summaryrefslogtreecommitdiffstats
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/androidfw/Android.mk15
-rw-r--r--libs/androidfw/Asset.cpp16
-rw-r--r--libs/androidfw/AssetManager.cpp30
-rw-r--r--libs/androidfw/BackupData.cpp36
-rw-r--r--libs/androidfw/BackupHelpers.cpp65
-rw-r--r--libs/androidfw/ObbFile.cpp8
-rw-r--r--libs/androidfw/ResourceTypes.cpp688
-rw-r--r--libs/androidfw/StreamingZipInflater.cpp6
-rw-r--r--libs/androidfw/ZipFileRO.cpp23
-rw-r--r--libs/androidfw/misc.cpp4
-rw-r--r--libs/androidfw/tests/Android.mk21
-rw-r--r--libs/androidfw/tests/ResTable_test.cpp81
-rw-r--r--libs/common_time/Android.mk2
-rw-r--r--libs/common_time/clock_recovery.cpp4
-rw-r--r--libs/common_time/clock_recovery.h1
-rw-r--r--libs/common_time/common_clock.cpp3
-rw-r--r--libs/common_time/common_clock_service.cpp10
-rw-r--r--libs/common_time/common_time_server.cpp6
-rw-r--r--libs/common_time/common_time_server_api.cpp22
-rw-r--r--libs/common_time/main.cpp2
-rw-r--r--libs/hwui/AmbientShadow.cpp22
-rw-r--r--libs/hwui/AmbientShadow.h4
-rw-r--r--libs/hwui/Android.common.mk119
-rw-r--r--libs/hwui/Android.mk110
-rw-r--r--libs/hwui/AnimationContext.cpp16
-rw-r--r--libs/hwui/Animator.cpp10
-rw-r--r--libs/hwui/Animator.h32
-rw-r--r--libs/hwui/AnimatorManager.cpp14
-rw-r--r--libs/hwui/AnimatorManager.h2
-rw-r--r--libs/hwui/AssetAtlas.cpp13
-rw-r--r--libs/hwui/AssetAtlas.h4
-rw-r--r--libs/hwui/Caches.cpp421
-rw-r--r--libs/hwui/Caches.h299
-rw-r--r--libs/hwui/Canvas.h164
-rw-r--r--libs/hwui/CanvasState.cpp (renamed from libs/hwui/StatefulBaseRenderer.cpp)132
-rw-r--r--libs/hwui/CanvasState.h193
-rw-r--r--libs/hwui/ClipArea.cpp373
-rw-r--r--libs/hwui/ClipArea.h175
-rw-r--r--libs/hwui/DamageAccumulator.cpp8
-rw-r--r--libs/hwui/DeferredDisplayList.cpp89
-rw-r--r--libs/hwui/DeferredDisplayList.h8
-rw-r--r--libs/hwui/DeferredLayerUpdater.cpp25
-rw-r--r--libs/hwui/DeferredLayerUpdater.h13
-rw-r--r--libs/hwui/DisplayList.cpp31
-rw-r--r--libs/hwui/DisplayList.h26
-rw-r--r--libs/hwui/DisplayListCanvas.cpp570
-rw-r--r--libs/hwui/DisplayListCanvas.h388
-rw-r--r--libs/hwui/DisplayListLogBuffer.cpp112
-rw-r--r--libs/hwui/DisplayListLogBuffer.h57
-rw-r--r--libs/hwui/DisplayListOp.h508
-rw-r--r--libs/hwui/DisplayListRenderer.cpp532
-rw-r--r--libs/hwui/DisplayListRenderer.h316
-rw-r--r--libs/hwui/Dither.cpp23
-rw-r--r--libs/hwui/Dither.h10
-rw-r--r--libs/hwui/DrawProfiler.cpp4
-rw-r--r--libs/hwui/Extensions.cpp20
-rw-r--r--libs/hwui/Extensions.h13
-rw-r--r--libs/hwui/FboCache.cpp2
-rw-r--r--libs/hwui/Fence.h113
-rw-r--r--libs/hwui/FloatColor.h52
-rw-r--r--libs/hwui/FontRenderer.cpp225
-rw-r--r--libs/hwui/FontRenderer.h63
-rw-r--r--libs/hwui/FrameInfo.cpp28
-rw-r--r--libs/hwui/FrameInfo.h125
-rw-r--r--libs/hwui/GammaFontRenderer.cpp51
-rw-r--r--libs/hwui/GammaFontRenderer.h63
-rw-r--r--libs/hwui/Glop.h153
-rw-r--r--libs/hwui/GlopBuilder.cpp615
-rw-r--r--libs/hwui/GlopBuilder.h122
-rw-r--r--libs/hwui/GradientCache.cpp29
-rw-r--r--libs/hwui/GradientCache.h39
-rw-r--r--libs/hwui/Image.cpp4
-rw-r--r--libs/hwui/Interpolator.cpp12
-rw-r--r--libs/hwui/Interpolator.h23
-rw-r--r--libs/hwui/JankTracker.cpp276
-rw-r--r--libs/hwui/JankTracker.h87
-rw-r--r--libs/hwui/Layer.cpp60
-rw-r--r--libs/hwui/Layer.h54
-rw-r--r--libs/hwui/LayerCache.cpp11
-rw-r--r--libs/hwui/LayerCache.h5
-rw-r--r--libs/hwui/LayerRenderer.cpp82
-rw-r--r--libs/hwui/LayerRenderer.h19
-rw-r--r--libs/hwui/Matrix.cpp36
-rw-r--r--libs/hwui/Matrix.h2
-rw-r--r--[-rwxr-xr-x]libs/hwui/OpenGLRenderer.cpp2360
-rwxr-xr-xlibs/hwui/OpenGLRenderer.h525
-rw-r--r--libs/hwui/Outline.h2
-rw-r--r--libs/hwui/Patch.cpp52
-rw-r--r--libs/hwui/Patch.h25
-rw-r--r--libs/hwui/PatchCache.cpp68
-rw-r--r--libs/hwui/PatchCache.h14
-rw-r--r--libs/hwui/PathCache.cpp139
-rw-r--r--libs/hwui/PathCache.h59
-rw-r--r--libs/hwui/PathTessellator.cpp30
-rw-r--r--libs/hwui/PixelBuffer.cpp67
-rw-r--r--libs/hwui/PixelBuffer.h1
-rw-r--r--libs/hwui/Program.cpp12
-rw-r--r--libs/hwui/Program.h14
-rw-r--r--libs/hwui/ProgramCache.cpp29
-rw-r--r--libs/hwui/ProgramCache.h6
-rw-r--r--libs/hwui/Properties.h4
-rw-r--r--libs/hwui/Query.h152
-rw-r--r--libs/hwui/Rect.h21
-rw-r--r--libs/hwui/RenderBufferCache.cpp9
-rw-r--r--libs/hwui/RenderBufferCache.h4
-rw-r--r--libs/hwui/RenderNode.cpp99
-rw-r--r--libs/hwui/RenderNode.h8
-rw-r--r--libs/hwui/RenderProperties.cpp18
-rw-r--r--libs/hwui/RenderProperties.h20
-rw-r--r--libs/hwui/RenderState.cpp188
-rw-r--r--libs/hwui/Renderer.h233
-rw-r--r--libs/hwui/ResourceCache.cpp217
-rw-r--r--libs/hwui/ResourceCache.h63
-rw-r--r--libs/hwui/RevealClip.h2
-rw-r--r--libs/hwui/ShadowTessellator.cpp35
-rw-r--r--libs/hwui/ShadowTessellator.h8
-rw-r--r--libs/hwui/SkiaCanvas.cpp703
-rw-r--r--libs/hwui/SkiaCanvasProxy.cpp361
-rw-r--r--libs/hwui/SkiaCanvasProxy.h108
-rw-r--r--libs/hwui/SkiaShader.cpp574
-rw-r--r--libs/hwui/SkiaShader.h130
-rw-r--r--libs/hwui/Snapshot.cpp119
-rw-r--r--libs/hwui/Snapshot.h52
-rw-r--r--libs/hwui/SpotShadow.cpp24
-rw-r--r--libs/hwui/SpotShadow.h3
-rw-r--r--libs/hwui/StatefulBaseRenderer.h170
-rw-r--r--libs/hwui/TessellationCache.cpp24
-rw-r--r--libs/hwui/TessellationCache.h8
-rw-r--r--libs/hwui/TextDropShadowCache.cpp10
-rw-r--r--libs/hwui/TextDropShadowCache.h11
-rw-r--r--libs/hwui/Texture.cpp20
-rw-r--r--libs/hwui/Texture.h39
-rw-r--r--libs/hwui/TextureCache.cpp63
-rw-r--r--libs/hwui/TextureCache.h14
-rw-r--r--libs/hwui/TreeInfo.h10
-rw-r--r--libs/hwui/Vector.h3
-rw-r--r--libs/hwui/Vertex.h43
-rw-r--r--libs/hwui/VertexBuffer.h29
-rw-r--r--libs/hwui/font/CacheTexture.cpp80
-rw-r--r--libs/hwui/font/CacheTexture.h52
-rw-r--r--libs/hwui/font/CachedGlyphInfo.h4
-rw-r--r--libs/hwui/font/Font.cpp29
-rw-r--r--libs/hwui/font/Font.h8
-rw-r--r--libs/hwui/font/FontUtil.h11
-rw-r--r--libs/hwui/renderstate/Blend.cpp136
-rw-r--r--libs/hwui/renderstate/Blend.h58
-rw-r--r--libs/hwui/renderstate/MeshState.cpp175
-rw-r--r--libs/hwui/renderstate/MeshState.h144
-rw-r--r--libs/hwui/renderstate/PixelBufferState.cpp45
-rw-r--r--libs/hwui/renderstate/PixelBufferState.h37
-rw-r--r--libs/hwui/renderstate/RenderState.cpp354
-rw-r--r--libs/hwui/renderstate/RenderState.h (renamed from libs/hwui/RenderState.h)28
-rw-r--r--libs/hwui/renderstate/Scissor.cpp91
-rw-r--r--libs/hwui/renderstate/Scissor.h46
-rw-r--r--libs/hwui/renderstate/Stencil.cpp (renamed from libs/hwui/Stencil.cpp)37
-rw-r--r--libs/hwui/renderstate/Stencil.h (renamed from libs/hwui/Stencil.h)20
-rw-r--r--libs/hwui/renderstate/TextureState.cpp103
-rw-r--r--libs/hwui/renderstate/TextureState.h88
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp93
-rw-r--r--libs/hwui/renderthread/CanvasContext.h46
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.cpp23
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.h13
-rw-r--r--libs/hwui/renderthread/EglManager.cpp34
-rw-r--r--libs/hwui/renderthread/EglManager.h2
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp152
-rw-r--r--libs/hwui/renderthread/RenderProxy.h11
-rw-r--r--libs/hwui/renderthread/RenderTask.cpp3
-rw-r--r--libs/hwui/renderthread/RenderTask.h8
-rw-r--r--libs/hwui/renderthread/RenderThread.cpp43
-rw-r--r--libs/hwui/renderthread/RenderThread.h17
-rw-r--r--libs/hwui/renderthread/TimeLord.cpp8
-rw-r--r--libs/hwui/renderthread/TimeLord.h4
-rw-r--r--libs/hwui/tests/Android.mk38
-rw-r--r--libs/hwui/tests/TestContext.cpp65
-rw-r--r--libs/hwui/tests/TestContext.h38
-rw-r--r--libs/hwui/tests/main.cpp264
-rw-r--r--libs/hwui/tests/nullegl.cpp160
-rw-r--r--libs/hwui/tests/nullgles.cpp275
-rw-r--r--libs/hwui/thread/Signal.h6
-rw-r--r--libs/hwui/thread/TaskManager.cpp19
-rw-r--r--libs/hwui/thread/TaskManager.h4
-rw-r--r--libs/hwui/thread/TaskProcessor.h16
-rw-r--r--libs/hwui/unit_tests/Android.mk34
-rw-r--r--libs/hwui/unit_tests/ClipAreaTests.cpp116
-rw-r--r--libs/hwui/unit_tests/LinearAllocatorTests.cpp108
-rwxr-xr-xlibs/hwui/unit_tests/how_to_run.txt4
-rw-r--r--libs/hwui/unit_tests/main.cpp22
-rw-r--r--libs/hwui/utils/LinearAllocator.cpp272
-rw-r--r--libs/hwui/utils/LinearAllocator.h142
-rw-r--r--libs/hwui/utils/Macros.h36
-rw-r--r--libs/hwui/utils/PaintUtils.h81
-rw-r--r--libs/hwui/utils/Pair.h2
-rw-r--r--libs/hwui/utils/RingBuffer.h71
-rw-r--r--libs/hwui/utils/SortedList.h14
-rw-r--r--libs/hwui/utils/SortedListImpl.h2
-rw-r--r--libs/hwui/utils/Timing.h4
-rw-r--r--libs/input/Android.mk6
-rw-r--r--libs/input/PointerController.cpp3
-rw-r--r--libs/input/SpriteController.cpp4
-rw-r--r--libs/storage/Android.mk2
-rw-r--r--libs/storage/IMountService.cpp27
-rw-r--r--libs/storage/IMountServiceListener.cpp4
-rw-r--r--libs/storage/IMountShutdownObserver.cpp4
-rw-r--r--libs/storage/IObbActionListener.cpp9
-rw-r--r--libs/usb/tests/AccessoryChat/accessorychat/Android.mk2
205 files changed, 11493 insertions, 7616 deletions
diff --git a/libs/androidfw/Android.mk b/libs/androidfw/Android.mk
index 20d5470..461160a 100644
--- a/libs/androidfw/Android.mk
+++ b/libs/androidfw/Android.mk
@@ -40,10 +40,12 @@ hostSources := $(commonSources)
# For the host
# =====================================================
include $(CLEAR_VARS)
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_MODULE:= libandroidfw
LOCAL_MODULE_TAGS := optional
LOCAL_CFLAGS += -DSTATIC_ANDROIDFW_FOR_TOOLS
+LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
LOCAL_SRC_FILES:= $(hostSources)
LOCAL_C_INCLUDES := external/zlib
@@ -54,6 +56,7 @@ include $(BUILD_HOST_STATIC_LIBRARY)
# =====================================================
include $(CLEAR_VARS)
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_MODULE:= libandroidfw
LOCAL_MODULE_TAGS := optional
@@ -63,11 +66,13 @@ LOCAL_C_INCLUDES := \
system/core/include
LOCAL_STATIC_LIBRARIES := libziparchive
LOCAL_SHARED_LIBRARIES := \
- libbinder \
- liblog \
- libcutils \
- libutils \
- libz
+ libbinder \
+ liblog \
+ libcutils \
+ libutils \
+ libz
+
+LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
include $(BUILD_SHARED_LIBRARY)
diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp
index 589211f..782806e 100644
--- a/libs/androidfw/Asset.cpp
+++ b/libs/androidfw/Asset.cpp
@@ -45,6 +45,8 @@ using namespace android;
# define O_BINARY 0
#endif
+static const bool kIsDebug = false;
+
static Mutex gAssetLock;
static int32_t gCount = 0;
static Asset* gHead = NULL;
@@ -89,7 +91,9 @@ Asset::Asset(void)
gTail->mNext = this;
gTail = this;
}
- //ALOGI("Creating Asset %p #%d\n", this, gCount);
+ if (kIsDebug) {
+ ALOGI("Creating Asset %p #%d\n", this, gCount);
+ }
}
Asset::~Asset(void)
@@ -109,7 +113,9 @@ Asset::~Asset(void)
mPrev->mNext = mNext;
}
mNext = mPrev = NULL;
- //ALOGI("Destroying Asset in %p #%d\n", this, gCount);
+ if (kIsDebug) {
+ ALOGI("Destroying Asset in %p #%d\n", this, gCount);
+ }
}
/*
@@ -526,7 +532,7 @@ off64_t _FileAsset::seek(off64_t offset, int whence)
void _FileAsset::close(void)
{
if (mMap != NULL) {
- mMap->release();
+ delete mMap;
mMap = NULL;
}
if (mBuf != NULL) {
@@ -606,7 +612,7 @@ const void* _FileAsset::getBuffer(bool wordAligned)
map = new FileMap;
if (!map->create(NULL, fileno(mFp), mStart, mLength, true)) {
- map->release();
+ delete map;
return NULL;
}
@@ -821,7 +827,7 @@ off64_t _CompressedAsset::seek(off64_t offset, int whence)
void _CompressedAsset::close(void)
{
if (mMap != NULL) {
- mMap->release();
+ delete mMap;
mMap = NULL;
}
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index de6a33c..25cd363 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -64,6 +64,8 @@
using namespace android;
+static const bool kIsDebug = false;
+
/*
* Names for default app, locale, and vendor. We might want to change
* these to be an actual locale, e.g. always use en-US as the default.
@@ -152,15 +154,19 @@ AssetManager::AssetManager(CacheMode cacheMode)
mResources(NULL), mConfig(new ResTable_config),
mCacheMode(cacheMode), mCacheValid(false)
{
- int count = android_atomic_inc(&gCount)+1;
- //ALOGI("Creating AssetManager %p #%d\n", this, count);
+ int count = android_atomic_inc(&gCount) + 1;
+ if (kIsDebug) {
+ ALOGI("Creating AssetManager %p #%d\n", this, count);
+ }
memset(mConfig, 0, sizeof(ResTable_config));
}
AssetManager::~AssetManager(void)
{
int count = android_atomic_dec(&gCount);
- //ALOGI("Destroying AssetManager in %p #%d\n", this, count);
+ if (kIsDebug) {
+ ALOGI("Destroying AssetManager in %p #%d\n", this, count);
+ }
delete mConfig;
delete mResources;
@@ -296,6 +302,10 @@ bool AssetManager::addOverlayPath(const String8& packagePath, int32_t* cookie)
mAssetPaths.add(oap);
*cookie = static_cast<int32_t>(mAssetPaths.size());
+ if (mResources != NULL) {
+ appendPathToResTable(oap);
+ }
+
return true;
}
@@ -601,6 +611,11 @@ FileType AssetManager::getFileType(const char* fileName)
}
bool AssetManager::appendPathToResTable(const asset_path& ap) const {
+ // skip those ap's that correspond to system overlays
+ if (ap.isSystemOverlay) {
+ return true;
+ }
+
Asset* ass = NULL;
ResTable* sharedRes = NULL;
bool shared = true;
@@ -786,6 +801,7 @@ void AssetManager::addSystemOverlays(const char* pathOverlaysList,
oap.path = String8(buf, space - buf);
oap.type = kFileTypeRegular;
oap.idmap = String8(space + 1, newline - space - 1);
+ oap.isSystemOverlay = true;
Asset* oass = const_cast<AssetManager*>(this)->
openNonAssetInPathLocked("resources.arsc",
@@ -1864,7 +1880,9 @@ AssetManager::SharedZip::SharedZip(const String8& path, time_t modWhen)
: mPath(path), mZipFile(NULL), mModWhen(modWhen),
mResourceTableAsset(NULL), mResourceTable(NULL)
{
- //ALOGI("Creating SharedZip %p %s\n", this, (const char*)mPath);
+ if (kIsDebug) {
+ ALOGI("Creating SharedZip %p %s\n", this, (const char*)mPath);
+ }
ALOGV("+++ opening zip '%s'\n", mPath.string());
mZipFile = ZipFileRO::open(mPath.string());
if (mZipFile == NULL) {
@@ -1958,7 +1976,9 @@ bool AssetManager::SharedZip::getOverlay(size_t idx, asset_path* out) const
AssetManager::SharedZip::~SharedZip()
{
- //ALOGI("Destroying SharedZip %p %s\n", this, (const char*)mPath);
+ if (kIsDebug) {
+ ALOGI("Destroying SharedZip %p %s\n", this, (const char*)mPath);
+ }
if (mResourceTable != NULL) {
delete mResourceTable;
}
diff --git a/libs/androidfw/BackupData.cpp b/libs/androidfw/BackupData.cpp
index d16d5498..ba4a4ff 100644
--- a/libs/androidfw/BackupData.cpp
+++ b/libs/androidfw/BackupData.cpp
@@ -27,7 +27,7 @@
namespace android {
-static const bool DEBUG = false;
+static const bool kIsDebug = false;
/*
* File Format (v1):
@@ -45,12 +45,6 @@ static const bool DEBUG = false;
const static int ROUND_UP[4] = { 0, 3, 2, 1 };
static inline size_t
-round_up(size_t n)
-{
- return n + ROUND_UP[n % 4];
-}
-
-static inline size_t
padding_extra(size_t n)
{
return ROUND_UP[n % 4];
@@ -62,7 +56,7 @@ BackupDataWriter::BackupDataWriter(int fd)
m_entityCount(0)
{
m_pos = (ssize_t) lseek(fd, 0, SEEK_CUR);
- if (DEBUG) ALOGI("BackupDataWriter(%d) @ %ld", fd, (long)m_pos);
+ if (kIsDebug) ALOGI("BackupDataWriter(%d) @ %ld", fd, (long)m_pos);
}
BackupDataWriter::~BackupDataWriter()
@@ -79,7 +73,7 @@ BackupDataWriter::write_padding_for(int n)
paddingSize = padding_extra(n);
if (paddingSize > 0) {
uint32_t padding = 0xbcbcbcbc;
- if (DEBUG) ALOGI("writing %zd padding bytes for %d", paddingSize, n);
+ if (kIsDebug) ALOGI("writing %zd padding bytes for %d", paddingSize, n);
amt = write(m_fd, &padding, paddingSize);
if (amt != paddingSize) {
m_status = errno;
@@ -112,7 +106,7 @@ BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize)
} else {
k = key;
}
- if (DEBUG) {
+ if (kIsDebug) {
ALOGD("Writing header: prefix='%s' key='%s' dataSize=%zu", m_keyPrefix.string(),
key.string(), dataSize);
}
@@ -126,7 +120,7 @@ BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize)
header.keyLen = tolel(keyLen);
header.dataSize = tolel(dataSize);
- if (DEBUG) ALOGI("writing entity header, %zu bytes", sizeof(entity_header_v1));
+ if (kIsDebug) ALOGI("writing entity header, %zu bytes", sizeof(entity_header_v1));
amt = write(m_fd, &header, sizeof(entity_header_v1));
if (amt != sizeof(entity_header_v1)) {
m_status = errno;
@@ -134,7 +128,7 @@ BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize)
}
m_pos += amt;
- if (DEBUG) ALOGI("writing entity header key, %zd bytes", keyLen+1);
+ if (kIsDebug) ALOGI("writing entity header key, %zd bytes", keyLen+1);
amt = write(m_fd, k.string(), keyLen+1);
if (amt != keyLen+1) {
m_status = errno;
@@ -152,10 +146,10 @@ BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize)
status_t
BackupDataWriter::WriteEntityData(const void* data, size_t size)
{
- if (DEBUG) ALOGD("Writing data: size=%lu", (unsigned long) size);
+ if (kIsDebug) ALOGD("Writing data: size=%lu", (unsigned long) size);
if (m_status != NO_ERROR) {
- if (DEBUG) {
+ if (kIsDebug) {
ALOGD("Not writing data - stream in error state %d (%s)", m_status, strerror(m_status));
}
return m_status;
@@ -167,7 +161,7 @@ BackupDataWriter::WriteEntityData(const void* data, size_t size)
ssize_t amt = write(m_fd, data, size);
if (amt != (ssize_t)size) {
m_status = errno;
- if (DEBUG) ALOGD("write returned error %d (%s)", m_status, strerror(m_status));
+ if (kIsDebug) ALOGD("write returned error %d (%s)", m_status, strerror(m_status));
return m_status;
}
m_pos += amt;
@@ -189,7 +183,7 @@ BackupDataReader::BackupDataReader(int fd)
{
memset(&m_header, 0, sizeof(m_header));
m_pos = (ssize_t) lseek(fd, 0, SEEK_CUR);
- if (DEBUG) ALOGI("BackupDataReader(%d) @ %ld", fd, (long)m_pos);
+ if (kIsDebug) ALOGI("BackupDataReader(%d) @ %ld", fd, (long)m_pos);
}
BackupDataReader::~BackupDataReader()
@@ -342,15 +336,19 @@ BackupDataReader::ReadEntityData(void* data, size_t size)
return -1;
}
int remaining = m_dataEndPos - m_pos;
- //ALOGD("ReadEntityData size=%d m_pos=0x%x m_dataEndPos=0x%x remaining=%d\n",
- // size, m_pos, m_dataEndPos, remaining);
+ if (kIsDebug) {
+ ALOGD("ReadEntityData size=%zu m_pos=0x%zx m_dataEndPos=0x%zx remaining=%d\n",
+ size, m_pos, m_dataEndPos, remaining);
+ }
if (remaining <= 0) {
return 0;
}
if (((int)size) > remaining) {
size = remaining;
}
- //ALOGD(" reading %d bytes", size);
+ if (kIsDebug) {
+ ALOGD(" reading %zu bytes", size);
+ }
int amt = read(m_fd, data, size);
if (amt < 0) {
m_status = errno;
diff --git a/libs/androidfw/BackupHelpers.cpp b/libs/androidfw/BackupHelpers.cpp
index 33cf8ef..9300794 100644
--- a/libs/androidfw/BackupHelpers.cpp
+++ b/libs/androidfw/BackupHelpers.cpp
@@ -68,14 +68,11 @@ struct file_metadata_v1 {
const static int CURRENT_METADATA_VERSION = 1;
-#if 1
-#define LOGP(f, x...)
-#else
+static const bool kIsDebug = false;
#if TEST_BACKUP_HELPERS
-#define LOGP(f, x...) printf(f "\n", x)
+#define LOGP(f, x...) if (kIsDebug) printf(f "\n", x)
#else
-#define LOGP(x...) ALOGD(x)
-#endif
+#define LOGP(x...) if (kIsDebug) ALOGD(x)
#endif
const static int ROUND_UP[4] = { 0, 3, 2, 1 };
@@ -445,18 +442,6 @@ back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD
return 0;
}
-// Utility function, equivalent to stpcpy(): perform a strcpy, but instead of
-// returning the initial dest, return a pointer to the trailing NUL.
-static char* strcpy_ptr(char* dest, const char* str) {
- if (dest && str) {
- while ((*dest = *str) != 0) {
- dest++;
- str++;
- }
- }
- return dest;
-}
-
static void calc_tar_checksum(char* buf) {
// [ 148 : 8 ] checksum -- to be calculated with this field as space chars
memset(buf + 148, ' ', 8);
@@ -493,7 +478,8 @@ void send_tarfile_chunk(BackupDataWriter* writer, const char* buffer, size_t siz
}
int write_tarfile(const String8& packageName, const String8& domain,
- const String8& rootpath, const String8& filepath, BackupDataWriter* writer)
+ const String8& rootpath, const String8& filepath, off_t* outSize,
+ BackupDataWriter* writer)
{
// In the output stream everything is stored relative to the root
const char* relstart = filepath.string() + rootpath.length();
@@ -503,6 +489,7 @@ int write_tarfile(const String8& packageName, const String8& domain,
// If relpath is empty, it means this is the top of one of the standard named
// domain directories, so we should just skip it
if (relpath.length() == 0) {
+ *outSize = 0;
return 0;
}
@@ -532,12 +519,25 @@ int write_tarfile(const String8& packageName, const String8& domain,
return err;
}
+ // very large files need a pax extended size header
+ if (s.st_size > 077777777777LL) {
+ needExtended = true;
+ }
+
String8 fullname; // for pax later on
String8 prefix;
const int isdir = S_ISDIR(s.st_mode);
if (isdir) s.st_size = 0; // directories get no actual data in the tar stream
+ // Report the size, including a rough tar overhead estimation: 512 bytes for the
+ // overall tar file-block header, plus 2 blocks if using the pax extended format,
+ // plus the raw content size rounded up to a multiple of 512.
+ *outSize = 512 + (needExtended ? 1024 : 0) + 512*((s.st_size + 511)/512);
+
+ // Measure case: we've returned the size; now return without moving data
+ if (!writer) return 0;
+
// !!! TODO: use mmap when possible to avoid churning the buffer cache
// !!! TODO: this will break with symlinks; need to use readlink(2)
int fd = open(filepath.string(), O_RDONLY);
@@ -575,14 +575,10 @@ int write_tarfile(const String8& packageName, const String8& domain,
snprintf(buf + 116, 8, "0%lo", (unsigned long)s.st_gid);
// [ 124 : 12 ] file size in bytes
- if (s.st_size > 077777777777LL) {
- // very large files need a pax extended size header
- needExtended = true;
- }
snprintf(buf + 124, 12, "%011llo", (isdir) ? 0LL : s.st_size);
// [ 136 : 12 ] last mod time as a UTC time_t
- snprintf(buf + 136, 12, "%0lo", s.st_mtime);
+ snprintf(buf + 136, 12, "%0lo", (unsigned long)s.st_mtime);
// [ 156 : 1 ] link/file type
uint8_t type;
@@ -638,7 +634,6 @@ int write_tarfile(const String8& packageName, const String8& domain,
// construct the pax extended header data block
memset(paxData, 0, BUFSIZE - (paxData - buf));
- int len;
// size header -- calc len in digits by actually rendering the number
// to a string - brute force but simple
@@ -1203,7 +1198,6 @@ test_read_header_and_entity(BackupDataReader& reader, const char* str)
size_t bufSize = strlen(str)+1;
char* buf = (char*)malloc(bufSize);
String8 string;
- int cookie = 0x11111111;
size_t actualSize;
bool done;
int type;
@@ -1336,23 +1330,12 @@ get_mod_time(const char* filename, struct timeval times[2])
fprintf(stderr, "stat '%s' failed: %s\n", filename, strerror(errno));
return errno;
}
- times[0].tv_sec = st.st_atime;
- times[1].tv_sec = st.st_mtime;
- // If st_atime is a macro then struct stat64 uses struct timespec
- // to store the access and modif time values and typically
- // st_*time_nsec is not defined. In glibc, this is controlled by
- // __USE_MISC.
-#ifdef __USE_MISC
-#if !defined(st_atime) || defined(st_atime_nsec)
-#error "Check if this __USE_MISC conditional is still needed."
-#endif
+ times[0].tv_sec = st.st_atim.tv_sec;
times[0].tv_usec = st.st_atim.tv_nsec / 1000;
+
+ times[1].tv_sec = st.st_mtim.tv_sec;
times[1].tv_usec = st.st_mtim.tv_nsec / 1000;
-#else
- times[0].tv_usec = st.st_atime_nsec / 1000;
- times[1].tv_usec = st.st_mtime_nsec / 1000;
-#endif
return 0;
}
@@ -1493,7 +1476,6 @@ int
backup_helper_test_null_base()
{
int err;
- int oldSnapshotFD;
int dataStreamFD;
int newSnapshotFD;
@@ -1542,7 +1524,6 @@ int
backup_helper_test_missing_file()
{
int err;
- int oldSnapshotFD;
int dataStreamFD;
int newSnapshotFD;
diff --git a/libs/androidfw/ObbFile.cpp b/libs/androidfw/ObbFile.cpp
index ec59f06..195fa9a 100644
--- a/libs/androidfw/ObbFile.cpp
+++ b/libs/androidfw/ObbFile.cpp
@@ -122,7 +122,7 @@ bool ObbFile::parseObbFile(int fd)
if (fileLength < 0) {
ALOGW("error seeking in ObbFile: %s\n", strerror(errno));
} else {
- ALOGW("file is only %lld (less than %d minimum)\n", fileLength, kFooterMinSize);
+ ALOGW("file is only %lld (less than %d minimum)\n", (long long int)fileLength, kFooterMinSize);
}
return false;
}
@@ -150,8 +150,8 @@ bool ObbFile::parseObbFile(int fd)
footerSize = get4LE((unsigned char*)footer);
if (footerSize > (size_t)fileLength - kFooterTagSize
|| footerSize > kMaxBufSize) {
- ALOGW("claimed footer size is too large (0x%08zx; file size is 0x%08llx)\n",
- footerSize, fileLength);
+ ALOGW("claimed footer size is too large (0x%08zx; file size is 0x%08lld)\n",
+ footerSize, (long long int)fileLength);
return false;
}
@@ -164,7 +164,7 @@ bool ObbFile::parseObbFile(int fd)
off64_t fileOffset = fileLength - footerSize - kFooterTagSize;
if (lseek64(fd, fileOffset, SEEK_SET) != fileOffset) {
- ALOGW("seek %lld failed: %s\n", fileOffset, strerror(errno));
+ ALOGW("seek %lld failed: %s\n", (long long int)fileOffset, strerror(errno));
return false;
}
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index bdb53c3..04ebe70 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -17,6 +17,16 @@
#define LOG_TAG "ResourceType"
//#define LOG_NDEBUG 0
+#include <ctype.h>
+#include <memory.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <limits>
+#include <type_traits>
+
#include <androidfw/ByteBucketArray.h>
#include <androidfw/ResourceTypes.h>
#include <androidfw/TypeWrappers.h>
@@ -27,43 +37,23 @@
#include <utils/String16.h>
#include <utils/String8.h>
-#include <stdlib.h>
-#include <string.h>
-#include <memory.h>
-#include <ctype.h>
-#include <stdint.h>
-#include <stddef.h>
+#ifdef __ANDROID__
+#include <binder/TextOutput.h>
+#endif
#ifndef INT32_MAX
#define INT32_MAX ((int32_t)(2147483647))
#endif
-#define STRING_POOL_NOISY(x) //x
-#define XML_NOISY(x) //x
-#define TABLE_NOISY(x) //x
-#define TABLE_GETENTRY(x) //x
-#define TABLE_SUPER_NOISY(x) //x
-#define LOAD_TABLE_NOISY(x) //x
-#define TABLE_THEME(x) //x
-#define LIB_NOISY(x) //x
-
namespace android {
#ifdef HAVE_WINSOCK
#undef nhtol
#undef htonl
-
-#ifdef HAVE_LITTLE_ENDIAN
#define ntohl(x) ( ((x) << 24) | (((x) >> 24) & 255) | (((x) << 8) & 0xff0000) | (((x) >> 8) & 0xff00) )
#define htonl(x) ntohl(x)
#define ntohs(x) ( (((x) << 8) & 0xff00) | (((x) >> 8) & 255) )
#define htons(x) ntohs(x)
-#else
-#define ntohl(x) (x)
-#define htonl(x) (x)
-#define ntohs(x) (x)
-#define htons(x) (x)
-#endif
#endif
#define IDMAP_MAGIC 0x504D4449
@@ -72,6 +62,19 @@ namespace android {
#define APP_PACKAGE_ID 0x7f
#define SYS_PACKAGE_ID 0x01
+static const bool kDebugStringPoolNoisy = false;
+static const bool kDebugXMLNoisy = false;
+static const bool kDebugTableNoisy = false;
+static const bool kDebugTableGetEntry = false;
+static const bool kDebugTableSuperNoisy = false;
+static const bool kDebugLoadTableNoisy = false;
+static const bool kDebugLoadTableSuperNoisy = false;
+static const bool kDebugTableTheme = false;
+static const bool kDebugResXMLTree = false;
+static const bool kDebugLibNoisy = false;
+
+// TODO: This code uses 0xFFFFFFFF converted to bag_set* as a sentinel value. This is bad practice.
+
// Standard C isspace() is only required to look at the low byte of its input, so
// produces incorrect results for UTF-16 characters. For safety's sake, assume that
// any high-byte UTF-16 code point is not whitespace.
@@ -701,6 +704,12 @@ const char16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const
*u16len = decodeLength(&str);
if ((uint32_t)(str+*u16len-strings) < mStringPoolSize) {
+ // Reject malformed (non null-terminated) strings
+ if (str[*u16len] != 0x0000) {
+ ALOGW("Bad string block: string #%d is not null-terminated",
+ (int)idx);
+ return NULL;
+ }
return reinterpret_cast<const char16_t*>(str);
} else {
ALOGW("Bad string block: string #%d extends to %d, past end at %d\n",
@@ -719,12 +728,14 @@ const char16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const
if (mCache == NULL) {
#ifndef HAVE_ANDROID_OS
- STRING_POOL_NOISY(ALOGI("CREATING STRING CACHE OF %d bytes",
- mHeader->stringCount*sizeof(char16_t**)));
+ if (kDebugStringPoolNoisy) {
+ ALOGI("CREATING STRING CACHE OF %zu bytes",
+ mHeader->stringCount*sizeof(char16_t**));
+ }
#else
// We do not want to be in this case when actually running Android.
- ALOGW("CREATING STRING CACHE OF %d bytes",
- mHeader->stringCount*sizeof(char16_t**));
+ ALOGW("CREATING STRING CACHE OF %zu bytes",
+ static_cast<size_t>(mHeader->stringCount*sizeof(char16_t**)));
#endif
mCache = (char16_t**)calloc(mHeader->stringCount, sizeof(char16_t**));
if (mCache == NULL) {
@@ -746,6 +757,13 @@ const char16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const
return NULL;
}
+ // Reject malformed (non null-terminated) strings
+ if (u8str[u8len] != 0x00) {
+ ALOGW("Bad string block: string #%d is not null-terminated",
+ (int)idx);
+ return NULL;
+ }
+
char16_t *u16str = (char16_t *)calloc(*u16len+1, sizeof(char16_t));
if (!u16str) {
ALOGW("No memory when trying to allocate decode cache for string #%d\n",
@@ -753,7 +771,9 @@ const char16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const
return NULL;
}
- STRING_POOL_NOISY(ALOGI("Caching UTF8 string: %s", u8str));
+ if (kDebugStringPoolNoisy) {
+ ALOGI("Caching UTF8 string: %s", u8str);
+ }
utf8_to_utf16(u8str, u8len, u16str);
mCache[idx] = u16str;
return u16str;
@@ -843,7 +863,9 @@ ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const
size_t len;
if ((mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0) {
- STRING_POOL_NOISY(ALOGI("indexOfString UTF-8: %s", String8(str, strLen).string()));
+ if (kDebugStringPoolNoisy) {
+ ALOGI("indexOfString UTF-8: %s", String8(str, strLen).string());
+ }
// The string pool contains UTF 8 strings; we don't want to cause
// temporary UTF-16 strings to be created as we search.
@@ -869,10 +891,14 @@ ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const
} else {
c = -1;
}
- STRING_POOL_NOISY(ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n",
- (const char*)s, c, (int)l, (int)mid, (int)h));
+ if (kDebugStringPoolNoisy) {
+ ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n",
+ (const char*)s, c, (int)l, (int)mid, (int)h);
+ }
if (c == 0) {
- STRING_POOL_NOISY(ALOGI("MATCH!"));
+ if (kDebugStringPoolNoisy) {
+ ALOGI("MATCH!");
+ }
free(convBuffer);
return mid;
} else if (c < 0) {
@@ -891,18 +917,22 @@ ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const
const size_t str8Len = str8.size();
for (int i=mHeader->stringCount-1; i>=0; i--) {
const char* s = string8At(i, &len);
- STRING_POOL_NOISY(ALOGI("Looking at %s, i=%d\n",
- String8(s).string(),
- i));
+ if (kDebugStringPoolNoisy) {
+ ALOGI("Looking at %s, i=%d\n", String8(s).string(), i);
+ }
if (s && str8Len == len && memcmp(s, str8.string(), str8Len) == 0) {
- STRING_POOL_NOISY(ALOGI("MATCH!"));
+ if (kDebugStringPoolNoisy) {
+ ALOGI("MATCH!");
+ }
return i;
}
}
}
} else {
- STRING_POOL_NOISY(ALOGI("indexOfString UTF-16: %s", String8(str, strLen).string()));
+ if (kDebugStringPoolNoisy) {
+ ALOGI("indexOfString UTF-16: %s", String8(str, strLen).string());
+ }
if (mHeader->flags&ResStringPool_header::SORTED_FLAG) {
// Do a binary search for the string...
@@ -914,11 +944,14 @@ ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const
mid = l + (h - l)/2;
const char16_t* s = stringAt(mid, &len);
int c = s ? strzcmp16(s, len, str, strLen) : -1;
- STRING_POOL_NOISY(ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n",
- String8(s).string(),
- c, (int)l, (int)mid, (int)h));
+ if (kDebugStringPoolNoisy) {
+ ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n",
+ String8(s).string(), c, (int)l, (int)mid, (int)h);
+ }
if (c == 0) {
- STRING_POOL_NOISY(ALOGI("MATCH!"));
+ if (kDebugStringPoolNoisy) {
+ ALOGI("MATCH!");
+ }
return mid;
} else if (c < 0) {
l = mid + 1;
@@ -933,11 +966,13 @@ ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const
// block, start searching at the back.
for (int i=mHeader->stringCount-1; i>=0; i--) {
const char16_t* s = stringAt(i, &len);
- STRING_POOL_NOISY(ALOGI("Looking at %s, i=%d\n",
- String8(s).string(),
- i));
+ if (kDebugStringPoolNoisy) {
+ ALOGI("Looking at %s, i=%d\n", String8(s).string(), i);
+ }
if (s && strLen == len && strzcmp16(s, len, str, strLen) == 0) {
- STRING_POOL_NOISY(ALOGI("MATCH!"));
+ if (kDebugStringPoolNoisy) {
+ ALOGI("MATCH!");
+ }
return i;
}
}
@@ -1138,7 +1173,9 @@ const char16_t* ResXMLParser::getAttributeNamespace(size_t idx, size_t* outLen)
{
int32_t id = getAttributeNamespaceID(idx);
//printf("attribute namespace=%d idx=%d event=%p\n", id, idx, mEventCode);
- //XML_NOISY(printf("getAttributeNamespace 0x%x=0x%x\n", idx, id));
+ if (kDebugXMLNoisy) {
+ printf("getAttributeNamespace 0x%zx=0x%x\n", idx, id);
+ }
return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
}
@@ -1146,7 +1183,9 @@ const char* ResXMLParser::getAttributeNamespace8(size_t idx, size_t* outLen) con
{
int32_t id = getAttributeNamespaceID(idx);
//printf("attribute namespace=%d idx=%d event=%p\n", id, idx, mEventCode);
- //XML_NOISY(printf("getAttributeNamespace 0x%x=0x%x\n", idx, id));
+ if (kDebugXMLNoisy) {
+ printf("getAttributeNamespace 0x%zx=0x%x\n", idx, id);
+ }
return id >= 0 ? mTree.mStrings.string8At(id, outLen) : NULL;
}
@@ -1169,7 +1208,9 @@ const char16_t* ResXMLParser::getAttributeName(size_t idx, size_t* outLen) const
{
int32_t id = getAttributeNameID(idx);
//printf("attribute name=%d idx=%d event=%p\n", id, idx, mEventCode);
- //XML_NOISY(printf("getAttributeName 0x%x=0x%x\n", idx, id));
+ if (kDebugXMLNoisy) {
+ printf("getAttributeName 0x%zx=0x%x\n", idx, id);
+ }
return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
}
@@ -1177,7 +1218,9 @@ const char* ResXMLParser::getAttributeName8(size_t idx, size_t* outLen) const
{
int32_t id = getAttributeNameID(idx);
//printf("attribute name=%d idx=%d event=%p\n", id, idx, mEventCode);
- //XML_NOISY(printf("getAttributeName 0x%x=0x%x\n", idx, id));
+ if (kDebugXMLNoisy) {
+ printf("getAttributeName 0x%zx=0x%x\n", idx, id);
+ }
return id >= 0 ? mTree.mStrings.string8At(id, outLen) : NULL;
}
@@ -1212,7 +1255,9 @@ int32_t ResXMLParser::getAttributeValueStringID(size_t idx) const
const char16_t* ResXMLParser::getAttributeStringValue(size_t idx, size_t* outLen) const
{
int32_t id = getAttributeValueStringID(idx);
- //XML_NOISY(printf("getAttributeValue 0x%x=0x%x\n", idx, id));
+ if (kDebugXMLNoisy) {
+ printf("getAttributeValue 0x%zx=0x%x\n", idx, id);
+ }
return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
}
@@ -1303,54 +1348,69 @@ ssize_t ResXMLParser::indexOfAttribute(const char16_t* ns, size_t nsLen,
ns8 = String8(ns, nsLen);
}
attr8 = String8(attr, attrLen);
- STRING_POOL_NOISY(ALOGI("indexOfAttribute UTF8 %s (%d) / %s (%d)", ns8.string(), nsLen,
- attr8.string(), attrLen));
+ if (kDebugStringPoolNoisy) {
+ ALOGI("indexOfAttribute UTF8 %s (%zu) / %s (%zu)", ns8.string(), nsLen,
+ attr8.string(), attrLen);
+ }
for (size_t i=0; i<N; i++) {
size_t curNsLen = 0, curAttrLen = 0;
const char* curNs = getAttributeNamespace8(i, &curNsLen);
const char* curAttr = getAttributeName8(i, &curAttrLen);
- STRING_POOL_NOISY(ALOGI(" curNs=%s (%d), curAttr=%s (%d)", curNs, curNsLen,
- curAttr, curAttrLen));
+ if (kDebugStringPoolNoisy) {
+ ALOGI(" curNs=%s (%zu), curAttr=%s (%zu)", curNs, curNsLen, curAttr, curAttrLen);
+ }
if (curAttr != NULL && curNsLen == nsLen && curAttrLen == attrLen
&& memcmp(attr8.string(), curAttr, attrLen) == 0) {
if (ns == NULL) {
if (curNs == NULL) {
- STRING_POOL_NOISY(ALOGI(" FOUND!"));
+ if (kDebugStringPoolNoisy) {
+ ALOGI(" FOUND!");
+ }
return i;
}
} else if (curNs != NULL) {
//printf(" --> ns=%s, curNs=%s\n",
// String8(ns).string(), String8(curNs).string());
if (memcmp(ns8.string(), curNs, nsLen) == 0) {
- STRING_POOL_NOISY(ALOGI(" FOUND!"));
+ if (kDebugStringPoolNoisy) {
+ ALOGI(" FOUND!");
+ }
return i;
}
}
}
}
} else {
- STRING_POOL_NOISY(ALOGI("indexOfAttribute UTF16 %s (%d) / %s (%d)",
- String8(ns, nsLen).string(), nsLen,
- String8(attr, attrLen).string(), attrLen));
+ if (kDebugStringPoolNoisy) {
+ ALOGI("indexOfAttribute UTF16 %s (%zu) / %s (%zu)",
+ String8(ns, nsLen).string(), nsLen,
+ String8(attr, attrLen).string(), attrLen);
+ }
for (size_t i=0; i<N; i++) {
size_t curNsLen = 0, curAttrLen = 0;
const char16_t* curNs = getAttributeNamespace(i, &curNsLen);
const char16_t* curAttr = getAttributeName(i, &curAttrLen);
- STRING_POOL_NOISY(ALOGI(" curNs=%s (%d), curAttr=%s (%d)",
- String8(curNs, curNsLen).string(), curNsLen,
- String8(curAttr, curAttrLen).string(), curAttrLen));
+ if (kDebugStringPoolNoisy) {
+ ALOGI(" curNs=%s (%zu), curAttr=%s (%zu)",
+ String8(curNs, curNsLen).string(), curNsLen,
+ String8(curAttr, curAttrLen).string(), curAttrLen);
+ }
if (curAttr != NULL && curNsLen == nsLen && curAttrLen == attrLen
&& (memcmp(attr, curAttr, attrLen*sizeof(char16_t)) == 0)) {
if (ns == NULL) {
if (curNs == NULL) {
- STRING_POOL_NOISY(ALOGI(" FOUND!"));
+ if (kDebugStringPoolNoisy) {
+ ALOGI(" FOUND!");
+ }
return i;
}
} else if (curNs != NULL) {
//printf(" --> ns=%s, curNs=%s\n",
// String8(ns).string(), String8(curNs).string());
if (memcmp(ns, curNs, nsLen*sizeof(char16_t)) == 0) {
- STRING_POOL_NOISY(ALOGI(" FOUND!"));
+ if (kDebugStringPoolNoisy) {
+ ALOGI(" FOUND!");
+ }
return i;
}
}
@@ -1398,7 +1458,9 @@ ResXMLParser::event_code_t ResXMLParser::nextNode()
do {
const ResXMLTree_node* next = (const ResXMLTree_node*)
(((const uint8_t*)mCurNode) + dtohl(mCurNode->header.size));
- //ALOGW("Next node: prev=%p, next=%p\n", mCurNode, next);
+ if (kDebugXMLNoisy) {
+ ALOGI("Next node: prev=%p, next=%p\n", mCurNode, next);
+ }
if (((const uint8_t*)next) >= mTree.mDataEnd) {
mCurNode = NULL;
@@ -1475,7 +1537,9 @@ ResXMLTree::ResXMLTree(const DynamicRefTable* dynamicRefTable)
, mDynamicRefTable(dynamicRefTable)
, mError(NO_INIT), mOwnedData(NULL)
{
- //ALOGI("Creating ResXMLTree %p #%d\n", this, android_atomic_inc(&gCount)+1);
+ if (kDebugResXMLTree) {
+ ALOGI("Creating ResXMLTree %p #%d\n", this, android_atomic_inc(&gCount)+1);
+ }
restart();
}
@@ -1484,13 +1548,17 @@ ResXMLTree::ResXMLTree()
, mDynamicRefTable(NULL)
, mError(NO_INIT), mOwnedData(NULL)
{
- //ALOGI("Creating ResXMLTree %p #%d\n", this, android_atomic_inc(&gCount)+1);
+ if (kDebugResXMLTree) {
+ ALOGI("Creating ResXMLTree %p #%d\n", this, android_atomic_inc(&gCount)+1);
+ }
restart();
}
ResXMLTree::~ResXMLTree()
{
- //ALOGI("Destroying ResXMLTree in %p #%d\n", this, android_atomic_dec(&gCount)-1);
+ if (kDebugResXMLTree) {
+ ALOGI("Destroying ResXMLTree in %p #%d\n", this, android_atomic_dec(&gCount)-1);
+ }
uninit();
}
@@ -1543,8 +1611,10 @@ status_t ResXMLTree::setTo(const void* data, size_t size, bool copyData)
}
const uint16_t type = dtohs(chunk->type);
const size_t size = dtohl(chunk->size);
- XML_NOISY(printf("Scanning @ %p: type=0x%x, size=0x%x\n",
- (void*)(((uint32_t)chunk)-((uint32_t)mHeader)), type, size));
+ if (kDebugXMLNoisy) {
+ printf("Scanning @ %p: type=0x%x, size=0x%zx\n",
+ (void*)(((uintptr_t)chunk)-((uintptr_t)mHeader)), type, size);
+ }
if (type == RES_STRING_POOL_TYPE) {
mStrings.setTo(chunk, size);
} else if (type == RES_XML_RESOURCE_MAP_TYPE) {
@@ -1567,7 +1637,9 @@ status_t ResXMLTree::setTo(const void* data, size_t size, bool copyData)
mRootCode = mEventCode;
break;
} else {
- XML_NOISY(printf("Skipping unknown chunk!\n"));
+ if (kDebugXMLNoisy) {
+ printf("Skipping unknown chunk!\n");
+ }
}
lastChunk = chunk;
chunk = (const ResChunk_header*)
@@ -2151,9 +2223,11 @@ bool ResTable_config::isBetterThan(const ResTable_config& o,
myDelta += requested->screenHeightDp - screenHeightDp;
otherDelta += requested->screenHeightDp - o.screenHeightDp;
}
- //ALOGI("Comparing this %dx%d to other %dx%d in %dx%d: myDelta=%d otherDelta=%d",
- // screenWidthDp, screenHeightDp, o.screenWidthDp, o.screenHeightDp,
- // requested->screenWidthDp, requested->screenHeightDp, myDelta, otherDelta);
+ if (kDebugTableSuperNoisy) {
+ ALOGI("Comparing this %dx%d to other %dx%d in %dx%d: myDelta=%d otherDelta=%d",
+ screenWidthDp, screenHeightDp, o.screenWidthDp, o.screenHeightDp,
+ requested->screenWidthDp, requested->screenHeightDp, myDelta, otherDelta);
+ }
if (myDelta != otherDelta) {
return myDelta < otherDelta;
}
@@ -2408,11 +2482,17 @@ bool ResTable_config::match(const ResTable_config& settings) const {
}
if (screenSizeDp != 0) {
if (screenWidthDp != 0 && screenWidthDp > settings.screenWidthDp) {
- //ALOGI("Filtering out width %d in requested %d", screenWidthDp, settings.screenWidthDp);
+ if (kDebugTableSuperNoisy) {
+ ALOGI("Filtering out width %d in requested %d", screenWidthDp,
+ settings.screenWidthDp);
+ }
return false;
}
if (screenHeightDp != 0 && screenHeightDp > settings.screenHeightDp) {
- //ALOGI("Filtering out height %d in requested %d", screenHeightDp, settings.screenHeightDp);
+ if (kDebugTableSuperNoisy) {
+ ALOGI("Filtering out height %d in requested %d", screenHeightDp,
+ settings.screenHeightDp);
+ }
return false;
}
}
@@ -2432,9 +2512,13 @@ bool ResTable_config::match(const ResTable_config& settings) const {
// For compatibility, we count a request for KEYSHIDDEN_NO as also
// matching the more recent KEYSHIDDEN_SOFT. Basically
// KEYSHIDDEN_NO means there is some kind of keyboard available.
- //ALOGI("Matching keysHidden: have=%d, config=%d\n", keysHidden, setKeysHidden);
+ if (kDebugTableSuperNoisy) {
+ ALOGI("Matching keysHidden: have=%d, config=%d\n", keysHidden, setKeysHidden);
+ }
if (keysHidden != KEYSHIDDEN_NO || setKeysHidden != KEYSHIDDEN_SOFT) {
- //ALOGI("No match!");
+ if (kDebugTableSuperNoisy) {
+ ALOGI("No match!");
+ }
return false;
}
}
@@ -2469,6 +2553,58 @@ bool ResTable_config::match(const ResTable_config& settings) const {
return true;
}
+void ResTable_config::appendDirLocale(String8& out) const {
+ if (!language[0]) {
+ return;
+ }
+
+ if (!localeScript[0] && !localeVariant[0]) {
+ // Legacy format.
+ if (out.size() > 0) {
+ out.append("-");
+ }
+
+ char buf[4];
+ size_t len = unpackLanguage(buf);
+ out.append(buf, len);
+
+ if (country[0]) {
+ out.append("-r");
+ len = unpackRegion(buf);
+ out.append(buf, len);
+ }
+ return;
+ }
+
+ // We are writing the modified bcp47 tag.
+ // It starts with 'b+' and uses '+' as a separator.
+
+ if (out.size() > 0) {
+ out.append("-");
+ }
+ out.append("b+");
+
+ char buf[4];
+ size_t len = unpackLanguage(buf);
+ out.append(buf, len);
+
+ if (localeScript[0]) {
+ out.append("+");
+ out.append(localeScript, sizeof(localeScript));
+ }
+
+ if (country[0]) {
+ out.append("+");
+ len = unpackRegion(buf);
+ out.append(buf, len);
+ }
+
+ if (localeVariant[0]) {
+ out.append("+");
+ out.append(localeVariant, sizeof(localeVariant));
+ }
+}
+
void ResTable_config::getBcp47Locale(char str[RESTABLE_MAX_LOCALE_LEN]) const {
memset(str, 0, RESTABLE_MAX_LOCALE_LEN);
@@ -2569,12 +2705,7 @@ String8 ResTable_config::toString() const {
res.appendFormat("mnc%d", dtohs(mnc));
}
- char localeStr[RESTABLE_MAX_LOCALE_LEN];
- getBcp47Locale(localeStr);
- if (strlen(localeStr) > 0) {
- if (res.size() > 0) res.append("-");
- res.append(localeStr);
- }
+ appendDirLocale(res);
if ((screenLayout&MASK_LAYOUTDIR) != 0) {
if (res.size() > 0) res.append("-");
@@ -2939,17 +3070,25 @@ struct ResTable::PackageGroup
void clearBagCache() {
if (bags) {
- TABLE_NOISY(printf("bags=%p\n", bags));
+ if (kDebugTableNoisy) {
+ printf("bags=%p\n", bags);
+ }
for (size_t i = 0; i < bags->size(); i++) {
- TABLE_NOISY(printf("type=%d\n", i));
+ if (kDebugTableNoisy) {
+ printf("type=%zu\n", i);
+ }
const TypeList& typeList = types[i];
if (!typeList.isEmpty()) {
bag_set** typeBags = bags->get(i);
- TABLE_NOISY(printf("typeBags=%p\n", typeBags));
+ if (kDebugTableNoisy) {
+ printf("typeBags=%p\n", typeBags);
+ }
if (typeBags) {
const size_t N = typeList[0]->entryCount;
- TABLE_NOISY(printf("type->entryCount=%x\n", N));
- for (size_t j=0; j<N; j++) {
+ if (kDebugTableNoisy) {
+ printf("type->entryCount=%zu\n", N);
+ }
+ for (size_t j = 0; j < N; j++) {
if (typeBags[j] && typeBags[j] != (bag_set*)0xFFFFFFFF)
free(typeBags[j]);
}
@@ -3040,7 +3179,8 @@ ResTable::Theme::package_info* ResTable::Theme::copy_package(package_info* pi)
size_t cnt = pi->types[j].numEntries;
newpi->types[j].numEntries = cnt;
theme_entry* te = pi->types[j].entries;
- if (te != NULL) {
+ size_t cnt_max = SIZE_MAX / sizeof(theme_entry);
+ if (te != NULL && (cnt < 0xFFFFFFFF-1) && (cnt < cnt_max)) {
theme_entry* newte = (theme_entry*)malloc(cnt*sizeof(theme_entry));
newpi->types[j].entries = newte;
memcpy(newte, te, cnt*sizeof(theme_entry));
@@ -3057,7 +3197,9 @@ status_t ResTable::Theme::applyStyle(uint32_t resID, bool force)
uint32_t bagTypeSpecFlags = 0;
mTable.lock();
const ssize_t N = mTable.getBagLocked(resID, &bag, &bagTypeSpecFlags);
- TABLE_NOISY(ALOGV("Applying style 0x%08x to theme %p, count=%d", resID, this, N));
+ if (kDebugTableNoisy) {
+ ALOGV("Applying style 0x%08x to theme %p, count=%zu", resID, this, N);
+ }
if (N < 0) {
mTable.unlock();
return N;
@@ -3088,7 +3230,6 @@ status_t ResTable::Theme::applyStyle(uint32_t resID, bool force)
curPackageIndex = pidx;
curPI = mPackages[pidx];
if (curPI == NULL) {
- PackageGroup* const grp = mTable.mPackageGroups[pidx];
curPI = (package_info*)malloc(sizeof(package_info));
memset(curPI, 0, sizeof(*curPI));
mPackages[pidx] = curPI;
@@ -3106,9 +3247,12 @@ status_t ResTable::Theme::applyStyle(uint32_t resID, bool force)
if (curEntries == NULL) {
PackageGroup* const grp = mTable.mPackageGroups[curPackageIndex];
const TypeList& typeList = grp->types[t];
- int cnt = typeList.isEmpty() ? 0 : typeList[0]->entryCount;
- curEntries = (theme_entry*)malloc(cnt*sizeof(theme_entry));
- memset(curEntries, Res_value::TYPE_NULL, cnt*sizeof(theme_entry));
+ size_t cnt = typeList.isEmpty() ? 0 : typeList[0]->entryCount;
+ size_t cnt_max = SIZE_MAX / sizeof(theme_entry);
+ size_t buff_size = (cnt < cnt_max && cnt < 0xFFFFFFFF-1) ?
+ cnt*sizeof(theme_entry) : 0;
+ curEntries = (theme_entry*)malloc(buff_size);
+ memset(curEntries, Res_value::TYPE_NULL, buff_size);
curPI->types[t].numEntries = cnt;
curPI->types[t].entries = curEntries;
}
@@ -3120,9 +3264,11 @@ status_t ResTable::Theme::applyStyle(uint32_t resID, bool force)
continue;
}
theme_entry* curEntry = curEntries + e;
- TABLE_NOISY(ALOGV("Attr 0x%08x: type=0x%x, data=0x%08x; curType=0x%x",
- attrRes, bag->map.value.dataType, bag->map.value.data,
- curEntry->value.dataType));
+ if (kDebugTableNoisy) {
+ ALOGV("Attr 0x%08x: type=0x%x, data=0x%08x; curType=0x%x",
+ attrRes, bag->map.value.dataType, bag->map.value.data,
+ curEntry->value.dataType);
+ }
if (force || curEntry->value.dataType == Res_value::TYPE_NULL) {
curEntry->stringBlock = bag->stringBlock;
curEntry->typeSpecFlags |= bagTypeSpecFlags;
@@ -3134,17 +3280,21 @@ status_t ResTable::Theme::applyStyle(uint32_t resID, bool force)
mTable.unlock();
- //ALOGI("Applying style 0x%08x (force=%d) theme %p...\n", resID, force, this);
- //dumpToLog();
+ if (kDebugTableTheme) {
+ ALOGI("Applying style 0x%08x (force=%d) theme %p...\n", resID, force, this);
+ dumpToLog();
+ }
return NO_ERROR;
}
status_t ResTable::Theme::setTo(const Theme& other)
{
- //ALOGI("Setting theme %p from theme %p...\n", this, &other);
- //dumpToLog();
- //other.dumpToLog();
+ if (kDebugTableTheme) {
+ ALOGI("Setting theme %p from theme %p...\n", this, &other);
+ dumpToLog();
+ other.dumpToLog();
+ }
if (&mTable == &other.mTable) {
for (size_t i=0; i<Res_MAXPACKAGE; i++) {
@@ -3173,8 +3323,10 @@ status_t ResTable::Theme::setTo(const Theme& other)
}
}
- //ALOGI("Final theme:");
- //dumpToLog();
+ if (kDebugTableTheme) {
+ ALOGI("Final theme:");
+ dumpToLog();
+ }
return NO_ERROR;
}
@@ -3191,23 +3343,33 @@ ssize_t ResTable::Theme::getAttribute(uint32_t resID, Res_value* outValue,
const uint32_t t = Res_GETTYPE(resID);
const uint32_t e = Res_GETENTRY(resID);
- TABLE_THEME(ALOGI("Looking up attr 0x%08x in theme %p", resID, this));
+ if (kDebugTableTheme) {
+ ALOGI("Looking up attr 0x%08x in theme %p", resID, this);
+ }
if (p >= 0) {
const package_info* const pi = mPackages[p];
- TABLE_THEME(ALOGI("Found package: %p", pi));
+ if (kDebugTableTheme) {
+ ALOGI("Found package: %p", pi);
+ }
if (pi != NULL) {
- TABLE_THEME(ALOGI("Desired type index is %ld in avail %d", t, Res_MAXTYPE + 1));
+ if (kDebugTableTheme) {
+ ALOGI("Desired type index is %zd in avail %zu", t, Res_MAXTYPE + 1);
+ }
if (t <= Res_MAXTYPE) {
const type_info& ti = pi->types[t];
- TABLE_THEME(ALOGI("Desired entry index is %ld in avail %d", e, ti.numEntries));
+ if (kDebugTableTheme) {
+ ALOGI("Desired entry index is %u in avail %zu", e, ti.numEntries);
+ }
if (e < ti.numEntries) {
const theme_entry& te = ti.entries[e];
if (outTypeSpecFlags != NULL) {
*outTypeSpecFlags |= te.typeSpecFlags;
}
- TABLE_THEME(ALOGI("Theme value: type=0x%x, data=0x%08x",
- te.value.dataType, te.value.data));
+ if (kDebugTableTheme) {
+ ALOGI("Theme value: type=0x%x, data=0x%08x",
+ te.value.dataType, te.value.data);
+ }
const uint8_t type = te.value.dataType;
if (type == Res_value::TYPE_ATTRIBUTE) {
if (cnt > 0) {
@@ -3241,8 +3403,10 @@ ssize_t ResTable::Theme::resolveAttributeReference(Res_value* inOutValue,
if (inOutValue->dataType == Res_value::TYPE_ATTRIBUTE) {
uint32_t newTypeSpecFlags;
blockIndex = getAttribute(inOutValue->data, inOutValue, &newTypeSpecFlags);
- TABLE_THEME(ALOGI("Resolving attr reference: blockIndex=%d, type=0x%x, data=%p\n",
- (int)blockIndex, (int)inOutValue->dataType, (void*)inOutValue->data));
+ if (kDebugTableTheme) {
+ ALOGI("Resolving attr reference: blockIndex=%d, type=0x%x, data=0x%x\n",
+ (int)blockIndex, (int)inOutValue->dataType, inOutValue->data);
+ }
if (inoutTypeSpecFlags != NULL) *inoutTypeSpecFlags |= newTypeSpecFlags;
//printf("Retrieved attribute new type=0x%x\n", inOutValue->dataType);
if (blockIndex < 0) {
@@ -3281,7 +3445,9 @@ ResTable::ResTable()
{
memset(&mParams, 0, sizeof(mParams));
memset(mPackageMap, 0, sizeof(mPackageMap));
- //ALOGI("Creating ResTable %p\n", this);
+ if (kDebugTableSuperNoisy) {
+ ALOGI("Creating ResTable %p\n", this);
+ }
}
ResTable::ResTable(const void* data, size_t size, const int32_t cookie, bool copyData)
@@ -3291,12 +3457,16 @@ ResTable::ResTable(const void* data, size_t size, const int32_t cookie, bool cop
memset(mPackageMap, 0, sizeof(mPackageMap));
addInternal(data, size, NULL, 0, cookie, copyData);
LOG_FATAL_IF(mError != NO_ERROR, "Error parsing resource table");
- //ALOGI("Creating ResTable %p\n", this);
+ if (kDebugTableSuperNoisy) {
+ ALOGI("Creating ResTable %p\n", this);
+ }
}
ResTable::~ResTable()
{
- //ALOGI("Destroying ResTable in %p\n", this);
+ if (kDebugTableSuperNoisy) {
+ ALOGI("Destroying ResTable in %p\n", this);
+ }
uninit();
}
@@ -3425,9 +3595,10 @@ status_t ResTable::addInternal(const void* data, size_t dataSize, const void* id
const bool notDeviceEndian = htods(0xf0) != 0xf0;
- LOAD_TABLE_NOISY(
- ALOGV("Adding resources to ResTable: data=%p, size=0x%x, cookie=%d, copy=%d "
- "idmap=%p\n", data, dataSize, cookie, copyData, idmap));
+ if (kDebugLoadTableNoisy) {
+ ALOGV("Adding resources to ResTable: data=%p, size=%zu, cookie=%d, copy=%d "
+ "idmap=%p\n", data, dataSize, cookie, copyData, idmapData);
+ }
if (copyData || notDeviceEndian) {
header->ownedData = malloc(dataSize);
@@ -3440,9 +3611,13 @@ status_t ResTable::addInternal(const void* data, size_t dataSize, const void* id
header->header = (const ResTable_header*)data;
header->size = dtohl(header->header->header.size);
- //ALOGI("Got size 0x%x, again size 0x%x, raw size 0x%x\n", header->size,
- // dtohl(header->header->header.size), header->header->header.size);
- LOAD_TABLE_NOISY(ALOGV("Loading ResTable @%p:\n", header->header));
+ if (kDebugLoadTableSuperNoisy) {
+ ALOGI("Got size %zu, again size 0x%x, raw size 0x%x\n", header->size,
+ dtohl(header->header->header.size), header->header->header.size);
+ }
+ if (kDebugLoadTableNoisy) {
+ ALOGV("Loading ResTable @%p:\n", header->header);
+ }
if (dtohs(header->header->header.headerSize) > header->size
|| header->size > dataSize) {
ALOGW("Bad resource table: header size 0x%x or total size 0x%x is larger than data size 0x%x\n",
@@ -3470,9 +3645,11 @@ status_t ResTable::addInternal(const void* data, size_t dataSize, const void* id
if (err != NO_ERROR) {
return (mError=err);
}
- TABLE_NOISY(ALOGV("Chunk: type=0x%x, headerSize=0x%x, size=0x%x, pos=%p\n",
- dtohs(chunk->type), dtohs(chunk->headerSize), dtohl(chunk->size),
- (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header))));
+ if (kDebugTableNoisy) {
+ ALOGV("Chunk: type=0x%x, headerSize=0x%x, size=0x%x, pos=%p\n",
+ dtohs(chunk->type), dtohs(chunk->headerSize), dtohl(chunk->size),
+ (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header)));
+ }
const size_t csize = dtohl(chunk->size);
const uint16_t ctype = dtohs(chunk->type);
if (ctype == RES_STRING_POOL_TYPE) {
@@ -3516,7 +3693,9 @@ status_t ResTable::addInternal(const void* data, size_t dataSize, const void* id
ALOGW("No string values found in resource table!");
}
- TABLE_NOISY(ALOGV("Returning from add with mError=%d\n", mError));
+ if (kDebugTableNoisy) {
+ ALOGV("Returning from add with mError=%d\n", mError);
+ }
return mError;
}
@@ -3682,15 +3861,16 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag
return BAD_VALUE;
}
- TABLE_NOISY(size_t len;
- printf("Found value: pkg=%d, type=%d, str=%s, int=%d\n",
- entry.package->header->index,
- outValue->dataType,
- outValue->dataType == Res_value::TYPE_STRING
- ? String8(entry.package->header->values.stringAt(
- outValue->data, &len)).string()
- : "",
- outValue->data));
+ if (kDebugTableNoisy) {
+ size_t len;
+ printf("Found value: pkg=%zu, type=%d, str=%s, int=%d\n",
+ entry.package->header->index,
+ outValue->dataType,
+ outValue->dataType == Res_value::TYPE_STRING ?
+ String8(entry.package->header->values.stringAt(outValue->data, &len)).string() :
+ "",
+ outValue->data);
+ }
if (outSpecFlags != NULL) {
*outSpecFlags = entry.specFlags;
@@ -3711,15 +3891,16 @@ ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex,
while (blockIndex >= 0 && value->dataType == Res_value::TYPE_REFERENCE
&& value->data != 0 && count < 20) {
if (outLastRef) *outLastRef = value->data;
- uint32_t lastRef = value->data;
uint32_t newFlags = 0;
const ssize_t newIndex = getResource(value->data, value, true, 0, &newFlags,
outConfig);
if (newIndex == BAD_INDEX) {
return BAD_INDEX;
}
- TABLE_THEME(ALOGI("Resolving reference %p: newIndex=%d, type=0x%x, data=%p\n",
- (void*)lastRef, (int)newIndex, (int)value->dataType, (void*)value->data));
+ if (kDebugTableTheme) {
+ ALOGI("Resolving reference 0x%x: newIndex=%d, type=0x%x, data=0x%x\n",
+ value->data, (int)newIndex, (int)value->dataType, value->data);
+ }
//printf("Getting reference 0x%08x: newIndex=%d\n", value->data, newIndex);
if (inoutTypeSpecFlags != NULL) *inoutTypeSpecFlags |= newFlags;
if (newIndex < 0) {
@@ -3826,7 +4007,9 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag,
*outTypeSpecFlags = set->typeSpecFlags;
}
*outBag = (bag_entry*)(set+1);
- //ALOGI("Found existing bag for: %p\n", (void*)resID);
+ if (kDebugTableSuperNoisy) {
+ ALOGI("Found existing bag for: 0x%x\n", resID);
+ }
return set->numAttrs;
}
ALOGW("Attempt to retrieve bag 0x%08x which is invalid or in a cycle.",
@@ -3852,7 +4035,9 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag,
// Mark that we are currently working on this one.
typeSet[e] = (bag_set*)0xFFFFFFFF;
- TABLE_NOISY(ALOGI("Building bag: %p\n", (void*)resID));
+ if (kDebugTableNoisy) {
+ ALOGI("Building bag: %x\n", resID);
+ }
// Now collect all bag attributes
Entry entry;
@@ -3869,13 +4054,13 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag,
size_t N = count;
- TABLE_NOISY(ALOGI("Found map: size=%p parent=%p count=%d\n",
- entrySize, parent, count));
+ if (kDebugTableNoisy) {
+ ALOGI("Found map: size=%x parent=%x count=%d\n", entrySize, parent, count);
// If this map inherits from another, we need to start
// with its parent's values. Otherwise start out empty.
- TABLE_NOISY(printf("Creating new bag, entrySize=0x%08x, parent=0x%08x\n",
- entrySize, parent));
+ ALOGI("Creating new bag, entrySize=0x%08x, parent=0x%08x\n", entrySize, parent);
+ }
// This is what we are building.
bag_set* set = NULL;
@@ -3904,9 +4089,13 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag,
if (NP > 0) {
memcpy(set+1, parentBag, NP*sizeof(bag_entry));
set->numAttrs = NP;
- TABLE_NOISY(ALOGI("Initialized new bag with %d inherited attributes.\n", NP));
+ if (kDebugTableNoisy) {
+ ALOGI("Initialized new bag with %zd inherited attributes.\n", NP);
+ }
} else {
- TABLE_NOISY(ALOGI("Initialized new bag with no inherited attributes.\n"));
+ if (kDebugTableNoisy) {
+ ALOGI("Initialized new bag with no inherited attributes.\n");
+ }
set->numAttrs = 0;
}
set->availAttrs = NT;
@@ -3930,10 +4119,13 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag,
bag_entry* entries = (bag_entry*)(set+1);
size_t curEntry = 0;
uint32_t pos = 0;
- TABLE_NOISY(ALOGI("Starting with set %p, entries=%p, avail=%d\n",
- set, entries, set->availAttrs));
+ if (kDebugTableNoisy) {
+ ALOGI("Starting with set %p, entries=%p, avail=%zu\n", set, entries, set->availAttrs);
+ }
while (pos < count) {
- TABLE_NOISY(printf("Now at %p\n", (void*)curOff));
+ if (kDebugTableNoisy) {
+ ALOGI("Now at %p\n", (void*)curOff);
+ }
if (curOff > (dtohl(entry.type->header.size)-sizeof(ResTable_map))) {
ALOGW("ResTable_map at %d is beyond type chunk data %d",
@@ -3958,8 +4150,10 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag,
uint32_t oldName = 0;
while ((isInside=(curEntry < set->numAttrs))
&& (oldName=entries[curEntry].map.name.ident) < newName) {
- TABLE_NOISY(printf("#%d: Keeping existing attribute: 0x%08x\n",
- curEntry, entries[curEntry].map.name.ident));
+ if (kDebugTableNoisy) {
+ ALOGI("#%zu: Keeping existing attribute: 0x%08x\n",
+ curEntry, entries[curEntry].map.name.ident);
+ }
curEntry++;
}
@@ -3976,8 +4170,10 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag,
}
set->availAttrs = newAvail;
entries = (bag_entry*)(set+1);
- TABLE_NOISY(printf("Reallocated set %p, entries=%p, avail=%d\n",
- set, entries, set->availAttrs));
+ if (kDebugTableNoisy) {
+ ALOGI("Reallocated set %p, entries=%p, avail=%zu\n",
+ set, entries, set->availAttrs);
+ }
}
if (isInside) {
// Going in the middle, need to make space.
@@ -3985,11 +4181,13 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag,
sizeof(bag_entry)*(set->numAttrs-curEntry));
set->numAttrs++;
}
- TABLE_NOISY(printf("#%d: Inserting new attribute: 0x%08x\n",
- curEntry, newName));
+ if (kDebugTableNoisy) {
+ ALOGI("#%zu: Inserting new attribute: 0x%08x\n", curEntry, newName);
+ }
} else {
- TABLE_NOISY(printf("#%d: Replacing existing attribute: 0x%08x\n",
- curEntry, oldName));
+ if (kDebugTableNoisy) {
+ ALOGI("#%zu: Replacing existing attribute: 0x%08x\n", curEntry, oldName);
+ }
}
bag_entry* cur = entries+curEntry;
@@ -4003,9 +4201,11 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag,
return UNKNOWN_ERROR;
}
- TABLE_NOISY(printf("Setting entry #%d %p: block=%d, name=0x%08x, type=%d, data=0x%08x\n",
- curEntry, cur, cur->stringBlock, cur->map.name.ident,
- cur->map.value.dataType, cur->map.value.data));
+ if (kDebugTableNoisy) {
+ ALOGI("Setting entry #%zu %p: block=%zd, name=0x%08d, type=%d, data=0x%08x\n",
+ curEntry, cur, cur->stringBlock, cur->map.name.ident,
+ cur->map.value.dataType, cur->map.value.data);
+ }
// On to the next!
curEntry++;
@@ -4025,7 +4225,9 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag,
*outTypeSpecFlags = set->typeSpecFlags;
}
*outBag = (bag_entry*)(set+1);
- TABLE_NOISY(ALOGI("Returning %d attrs\n", set->numAttrs));
+ if (kDebugTableNoisy) {
+ ALOGI("Returning %zu attrs\n", set->numAttrs);
+ }
return set->numAttrs;
}
return BAD_INDEX;
@@ -4034,10 +4236,14 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag,
void ResTable::setParameters(const ResTable_config* params)
{
mLock.lock();
- TABLE_GETENTRY(ALOGI("Setting parameters: %s\n", params->toString().string()));
+ if (kDebugTableGetEntry) {
+ ALOGI("Setting parameters: %s\n", params->toString().string());
+ }
mParams = *params;
for (size_t i=0; i<mPackageGroups.size(); i++) {
- TABLE_NOISY(ALOGI("CLEARING BAGS FOR GROUP %d!", i));
+ if (kDebugTableNoisy) {
+ ALOGI("CLEARING BAGS FOR GROUP %zu!", i);
+ }
mPackageGroups[i]->clearBagCache();
}
mLock.unlock();
@@ -4075,7 +4281,9 @@ uint32_t ResTable::identifierForName(const char16_t* name, size_t nameLen,
size_t packageLen,
uint32_t* outTypeSpecFlags) const
{
- TABLE_SUPER_NOISY(printf("Identifier for name: error=%d\n", mError));
+ if (kDebugTableSuperNoisy) {
+ printf("Identifier for name: error=%d\n", mError);
+ }
// Check for internal resource identifier as the very first thing, so
// that we will always find them even when there are no resources.
@@ -4168,10 +4376,12 @@ nope:
}
nameLen = nameEnd-name;
- TABLE_NOISY(printf("Looking for identifier: type=%s, name=%s, package=%s\n",
- String8(type, typeLen).string(),
- String8(name, nameLen).string(),
- String8(package, packageLen).string()));
+ if (kDebugTableNoisy) {
+ printf("Looking for identifier: type=%s, name=%s, package=%s\n",
+ String8(type, typeLen).string(),
+ String8(name, nameLen).string(),
+ String8(package, packageLen).string());
+ }
const String16 attr("attr");
const String16 attrPrivate("^attr-private");
@@ -4182,7 +4392,9 @@ nope:
if (strzcmp16(package, packageLen,
group->name.string(), group->name.size())) {
- TABLE_NOISY(printf("Skipping package group: %s\n", String8(group->name).string()));
+ if (kDebugTableNoisy) {
+ printf("Skipping package group: %s\n", String8(group->name).string());
+ }
continue;
}
@@ -4406,8 +4618,7 @@ static bool parse_unit(const char* str, Res_value* outValue,
return false;
}
-
-bool ResTable::stringToInt(const char16_t* s, size_t len, Res_value* outValue)
+bool U16StringToInt(const char16_t* s, size_t len, Res_value* outValue)
{
while (len > 0 && isspace16(*s)) {
s++;
@@ -4419,7 +4630,7 @@ bool ResTable::stringToInt(const char16_t* s, size_t len, Res_value* outValue)
}
size_t i = 0;
- int32_t val = 0;
+ int64_t val = 0;
bool neg = false;
if (*s == '-') {
@@ -4431,28 +4642,50 @@ bool ResTable::stringToInt(const char16_t* s, size_t len, Res_value* outValue)
return false;
}
+ static_assert(std::is_same<uint32_t, Res_value::data_type>::value,
+ "Res_value::data_type has changed. The range checks in this "
+ "function are no longer correct.");
+
// Decimal or hex?
- if (s[i] == '0' && s[i+1] == 'x') {
- if (outValue)
- outValue->dataType = outValue->TYPE_INT_HEX;
+ bool isHex;
+ if (len > 1 && s[i] == '0' && s[i+1] == 'x') {
+ isHex = true;
i += 2;
+
+ if (neg) {
+ return false;
+ }
+
+ if (i == len) {
+ // Just u"0x"
+ return false;
+ }
+
bool error = false;
while (i < len && !error) {
val = (val*16) + get_hex(s[i], &error);
i++;
+
+ if (val > std::numeric_limits<uint32_t>::max()) {
+ return false;
+ }
}
if (error) {
return false;
}
} else {
- if (outValue)
- outValue->dataType = outValue->TYPE_INT_DEC;
+ isHex = false;
while (i < len) {
if (s[i] < '0' || s[i] > '9') {
return false;
}
val = (val*10) + s[i]-'0';
i++;
+
+ if ((neg && -val < std::numeric_limits<int32_t>::min()) ||
+ (!neg && val > std::numeric_limits<int32_t>::max())) {
+ return false;
+ }
}
}
@@ -4462,13 +4695,21 @@ bool ResTable::stringToInt(const char16_t* s, size_t len, Res_value* outValue)
i++;
}
- if (i == len) {
- if (outValue)
- outValue->data = val;
- return true;
+ if (i != len) {
+ return false;
}
- return false;
+ if (outValue) {
+ outValue->dataType =
+ isHex ? outValue->TYPE_INT_HEX : outValue->TYPE_INT_DEC;
+ outValue->data = static_cast<Res_value::data_type>(val);
+ }
+ return true;
+}
+
+bool ResTable::stringToInt(const char16_t* s, size_t len, Res_value* outValue)
+{
+ return U16StringToInt(s, len, outValue);
}
bool ResTable::stringToFloat(const char16_t* s, size_t len, Res_value* outValue)
@@ -4495,7 +4736,7 @@ bool ResTable::stringToFloat(const char16_t* s, size_t len, Res_value* outValue)
if (len > 0) {
return false;
}
- if (buf[0] < '0' && buf[0] > '9' && buf[0] != '.') {
+ if ((buf[0] < '0' || buf[0] > '9') && buf[0] != '.' && buf[0] != '-' && buf[0] != '+') {
return false;
}
@@ -4712,9 +4953,11 @@ bool ResTable::stringToValue(Res_value* outValue, String16* outString,
rid = Res_MAKEID(
accessor->getRemappedPackage(Res_GETPACKAGE(rid)),
Res_GETTYPE(rid), Res_GETENTRY(rid));
- TABLE_NOISY(printf("Incl %s:%s/%s: 0x%08x\n",
- String8(package).string(), String8(type).string(),
- String8(name).string(), rid));
+ if (kDebugTableNoisy) {
+ ALOGI("Incl %s:%s/%s: 0x%08x\n",
+ String8(package).string(), String8(type).string(),
+ String8(name).string(), rid);
+ }
}
uint32_t packageId = Res_GETPACKAGE(rid) + 1;
@@ -4729,9 +4972,11 @@ bool ResTable::stringToValue(Res_value* outValue, String16* outString,
uint32_t rid = accessor->getCustomResourceWithCreation(package, type, name,
createIfNotFound);
if (rid != 0) {
- TABLE_NOISY(printf("Pckg %s:%s/%s: 0x%08x\n",
- String8(package).string(), String8(type).string(),
- String8(name).string(), rid));
+ if (kDebugTableNoisy) {
+ ALOGI("Pckg %s:%s/%s: 0x%08x\n",
+ String8(package).string(), String8(type).string(),
+ String8(name).string(), rid);
+ }
uint32_t packageId = Res_GETPACKAGE(rid) + 1;
if (packageId == 0x00) {
outValue->data = rid;
@@ -5540,8 +5785,6 @@ status_t ResTable::getEntry(
}
// Check if there is the desired entry in this type.
- const uint8_t* const end = reinterpret_cast<const uint8_t*>(thisType)
- + dtohl(thisType->header.size);
const uint32_t* const eindex = reinterpret_cast<const uint32_t*>(
reinterpret_cast<const uint8_t*>(thisType) + dtohs(thisType->header.headerSize));
@@ -5730,9 +5973,11 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
const uint8_t* endPos = ((const uint8_t*)pkg) + dtohs(pkg->header.size);
while (((const uint8_t*)chunk) <= (endPos-sizeof(ResChunk_header)) &&
((const uint8_t*)chunk) <= (endPos-dtohl(chunk->size))) {
- TABLE_NOISY(ALOGV("PackageChunk: type=0x%x, headerSize=0x%x, size=0x%x, pos=%p\n",
- dtohs(chunk->type), dtohs(chunk->headerSize), dtohl(chunk->size),
- (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header))));
+ if (kDebugTableNoisy) {
+ ALOGV("PackageChunk: type=0x%x, headerSize=0x%x, size=0x%x, pos=%p\n",
+ dtohs(chunk->type), dtohs(chunk->headerSize), dtohl(chunk->size),
+ (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header)));
+ }
const size_t csize = dtohl(chunk->size);
const uint16_t ctype = dtohs(chunk->type);
if (ctype == RES_TABLE_TYPE_SPEC_TYPE) {
@@ -5746,11 +5991,13 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
const size_t typeSpecSize = dtohl(typeSpec->header.size);
const size_t newEntryCount = dtohl(typeSpec->entryCount);
- LOAD_TABLE_NOISY(printf("TypeSpec off %p: type=0x%x, headerSize=0x%x, size=%p\n",
- (void*)(base-(const uint8_t*)chunk),
- dtohs(typeSpec->header.type),
- dtohs(typeSpec->header.headerSize),
- (void*)typeSpecSize));
+ if (kDebugLoadTableNoisy) {
+ ALOGI("TypeSpec off %p: type=0x%x, headerSize=0x%x, size=%p\n",
+ (void*)(base-(const uint8_t*)chunk),
+ dtohs(typeSpec->header.type),
+ dtohs(typeSpec->header.headerSize),
+ (void*)typeSpecSize);
+ }
// look for block overrun or int overflow when multiplying by 4
if ((dtohl(typeSpec->entryCount) > (INT32_MAX/sizeof(uint32_t))
|| dtohs(typeSpec->header.headerSize)+(sizeof(uint32_t)*newEntryCount)
@@ -5808,13 +6055,14 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
const uint32_t typeSize = dtohl(type->header.size);
const size_t newEntryCount = dtohl(type->entryCount);
- LOAD_TABLE_NOISY(printf("Type off %p: type=0x%x, headerSize=0x%x, size=%p\n",
- (void*)(base-(const uint8_t*)chunk),
- dtohs(type->header.type),
- dtohs(type->header.headerSize),
- (void*)typeSize));
- if (dtohs(type->header.headerSize)+(sizeof(uint32_t)*newEntryCount)
- > typeSize) {
+ if (kDebugLoadTableNoisy) {
+ printf("Type off %p: type=0x%x, headerSize=0x%x, size=%u\n",
+ (void*)(base-(const uint8_t*)chunk),
+ dtohs(type->header.type),
+ dtohs(type->header.headerSize),
+ typeSize);
+ }
+ if (dtohs(type->header.headerSize)+(sizeof(uint32_t)*newEntryCount) > typeSize) {
ALOGW("ResTable_type entry index to %p extends beyond chunk end 0x%x.",
(void*)(dtohs(type->header.headerSize) + (sizeof(uint32_t)*newEntryCount)),
typeSize);
@@ -5860,11 +6108,12 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
t->configs.add(type);
- TABLE_GETENTRY(
+ if (kDebugTableGetEntry) {
ResTable_config thisConfig;
thisConfig.copyFromDtoH(type->config);
- ALOGI("Adding config to type %d: %s\n",
- type->id, thisConfig.toString().string()));
+ ALOGI("Adding config to type %d: %s\n", type->id,
+ thisConfig.toString().string());
+ }
} else {
ALOGV("Skipping empty ResTable_type for type %d", type->id);
}
@@ -5925,8 +6174,10 @@ status_t DynamicRefTable::load(const ResTable_lib_header* const header)
uint32_t packageId = dtohl(entry->packageId);
char16_t tmpName[sizeof(entry->packageName) / sizeof(char16_t)];
strcpy16_dtoh(tmpName, entry->packageName, sizeof(entry->packageName) / sizeof(char16_t));
- LIB_NOISY(ALOGV("Found lib entry %s with id %d\n", String8(tmpName).string(),
- dtohl(entry->packageId)));
+ if (kDebugLibNoisy) {
+ ALOGV("Found lib entry %s with id %d\n", String8(tmpName).string(),
+ dtohl(entry->packageId));
+ }
if (packageId >= 256) {
ALOGE("Bad package id 0x%08x", packageId);
return UNKNOWN_ERROR;
@@ -6109,20 +6360,20 @@ status_t ResTable::createIdmap(const ResTable& overlay,
if (Res_GETTYPE(overlayResID) + 1 != static_cast<size_t>(typeMap.overlayTypeId)) {
ALOGE("idmap: can't mix type ids in entry map. Resource 0x%08x maps to 0x%08x"
- " but entries should map to resources of type %02x",
+ " but entries should map to resources of type %02zx",
resID, overlayResID, typeMap.overlayTypeId);
return BAD_TYPE;
}
if (typeMap.entryOffset + typeMap.entryMap.size() < entryIndex) {
- // Resize to accomodate this entry and the 0's in between.
- if (typeMap.entryMap.resize((entryIndex - typeMap.entryOffset) + 1) < 0) {
+ // pad with 0xffffffff's (indicating non-existing entries) before adding this entry
+ size_t index = typeMap.entryMap.size();
+ size_t numItems = entryIndex - (typeMap.entryOffset + index);
+ if (typeMap.entryMap.insertAt(0xffffffff, index, numItems) < 0) {
return NO_MEMORY;
}
- typeMap.entryMap.editTop() = Res_GETENTRY(overlayResID);
- } else {
- typeMap.entryMap.add(Res_GETENTRY(overlayResID));
}
+ typeMap.entryMap.add(Res_GETENTRY(overlayResID));
}
if (!typeMap.entryMap.isEmpty()) {
@@ -6445,9 +6696,6 @@ void ResTable::print(bool inclValues) const
continue;
}
for (size_t entryIndex=0; entryIndex<entryCount; entryIndex++) {
-
- const uint8_t* const end = ((const uint8_t*)type)
- + dtohl(type->header.size);
const uint32_t* const eindex = (const uint32_t*)
(((const uint8_t*)type) + dtohs(type->header.headerSize));
diff --git a/libs/androidfw/StreamingZipInflater.cpp b/libs/androidfw/StreamingZipInflater.cpp
index 1dfec23..b39b5f0 100644
--- a/libs/androidfw/StreamingZipInflater.cpp
+++ b/libs/androidfw/StreamingZipInflater.cpp
@@ -41,6 +41,8 @@
_rc; })
#endif
+static const bool kIsDebug = false;
+
static inline size_t min_of(size_t a, size_t b) { return (a < b) ? a : b; }
using namespace android;
@@ -209,7 +211,9 @@ int StreamingZipInflater::readNextChunk() {
size_t toRead = min_of(mInBufSize, mInTotalSize - mInNextChunkOffset);
if (toRead > 0) {
ssize_t didRead = TEMP_FAILURE_RETRY(::read(mFd, mInBuf, toRead));
- //ALOGV("Reading input chunk, size %08x didread %08x", toRead, didRead);
+ if (kIsDebug) {
+ ALOGV("Reading input chunk, size %08zx didread %08zx", toRead, didRead);
+ }
if (didRead < 0) {
ALOGE("Error reading asset data: %s", strerror(errno));
return didRead;
diff --git a/libs/androidfw/ZipFileRO.cpp b/libs/androidfw/ZipFileRO.cpp
index 1ab18ad..af3d9b3 100644
--- a/libs/androidfw/ZipFileRO.cpp
+++ b/libs/androidfw/ZipFileRO.cpp
@@ -34,14 +34,6 @@
#include <assert.h>
#include <unistd.h>
-/*
- * We must open binary files using open(path, ... | O_BINARY) under Windows.
- * Otherwise strange read errors will happen.
- */
-#ifndef O_BINARY
-# define O_BINARY 0
-#endif
-
using namespace android;
class _ZipEntryRO {
@@ -50,7 +42,10 @@ public:
ZipEntryName name;
void *cookie;
- _ZipEntryRO() : cookie(NULL) {
+ _ZipEntryRO() : cookie(NULL) {}
+
+ ~_ZipEntryRO() {
+ EndIteration(cookie);
}
private:
@@ -83,15 +78,15 @@ ZipFileRO::~ZipFileRO() {
ZipEntryRO ZipFileRO::findEntryByName(const char* entryName) const
{
_ZipEntryRO* data = new _ZipEntryRO;
- const int32_t error = FindEntry(mHandle, entryName, &(data->entry));
+
+ data->name = ZipEntryName(entryName);
+
+ const int32_t error = FindEntry(mHandle, data->name, &(data->entry));
if (error) {
delete data;
return NULL;
}
- data->name.name = entryName;
- data->name.name_length = strlen(entryName);
-
return (ZipEntryRO) data;
}
@@ -205,7 +200,7 @@ FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const
FileMap* newMap = new FileMap();
if (!newMap->create(mFileName, fd, ze.offset, actualLen, true)) {
- newMap->release();
+ delete newMap;
return NULL;
}
diff --git a/libs/androidfw/misc.cpp b/libs/androidfw/misc.cpp
index ea54cc5..5285420 100644
--- a/libs/androidfw/misc.cpp
+++ b/libs/androidfw/misc.cpp
@@ -56,9 +56,11 @@ FileType getFileType(const char* fileName)
return kFileTypeBlockDev;
else if (S_ISFIFO(sb.st_mode))
return kFileTypeFifo;
-#ifdef HAVE_SYMLINKS
+#if defined(S_ISLNK)
else if (S_ISLNK(sb.st_mode))
return kFileTypeSymlink;
+#endif
+#if defined(S_ISSOCK)
else if (S_ISSOCK(sb.st_mode))
return kFileTypeSocket;
#endif
diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk
index c1014be..a353575 100644
--- a/libs/androidfw/tests/Android.mk
+++ b/libs/androidfw/tests/Android.mk
@@ -19,6 +19,7 @@
# targets here.
# ==========================================================
LOCAL_PATH:= $(call my-dir)
+
testFiles := \
AttributeFinder_test.cpp \
ByteBucketArray_test.cpp \
@@ -32,22 +33,33 @@ testFiles := \
TypeWrappers_test.cpp \
ZipUtils_test.cpp
+androidfw_test_cflags := \
+ -Wall \
+ -Werror \
+ -Wunused \
+ -Wunreachable-code \
+ -Wno-missing-field-initializers \
+
+# gtest is broken.
+androidfw_test_cflags += -Wno-unnamed-type-template-args
+
# ==========================================================
# Build the host tests: libandroidfw_tests
# ==========================================================
include $(CLEAR_VARS)
LOCAL_MODULE := libandroidfw_tests
+LOCAL_CFLAGS := $(androidfw_test_cflags)
LOCAL_SRC_FILES := $(testFiles)
LOCAL_STATIC_LIBRARIES := \
libandroidfw \
libutils \
libcutils \
- liblog
+ liblog \
+ libz \
include $(BUILD_HOST_NATIVE_TEST)
-
# ==========================================================
# Build the device tests: libandroidfw_tests
# ==========================================================
@@ -55,15 +67,16 @@ ifneq ($(SDK_ONLY),true)
include $(CLEAR_VARS)
LOCAL_MODULE := libandroidfw_tests
+LOCAL_CFLAGS := $(androidfw_test_cflags)
LOCAL_SRC_FILES := $(testFiles) \
BackupData_test.cpp \
- ObbFile_test.cpp
+ ObbFile_test.cpp \
+
LOCAL_SHARED_LIBRARIES := \
libandroidfw \
libcutils \
libutils \
libui \
- libstlport
include $(BUILD_NATIVE_TEST)
endif # Not SDK_ONLY
diff --git a/libs/androidfw/tests/ResTable_test.cpp b/libs/androidfw/tests/ResTable_test.cpp
index 6a9314e..dcfe91e 100644
--- a/libs/androidfw/tests/ResTable_test.cpp
+++ b/libs/androidfw/tests/ResTable_test.cpp
@@ -16,6 +16,10 @@
#include <androidfw/ResourceTypes.h>
+#include <codecvt>
+#include <locale>
+#include <string>
+
#include <utils/String8.h>
#include <utils/String16.h>
#include "TestHelpers.h"
@@ -201,4 +205,81 @@ TEST(ResTableTest, emptyTableHasSensibleDefaults) {
ASSERT_LT(table.getResource(base::R::integer::number1, &val, MAY_NOT_BE_BAG), 0);
}
+void testU16StringToInt(const char16_t* str, uint32_t expectedValue,
+ bool expectSuccess, bool expectHex) {
+ size_t len = std::char_traits<char16_t>::length(str);
+
+ // Gtest can't print UTF-16 strings, so we have to convert to UTF-8 :(
+ std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
+ std::string s = convert.to_bytes(std::u16string(str, len));
+
+ Res_value out = {};
+ ASSERT_EQ(expectSuccess, U16StringToInt(str, len, &out))
+ << "Failed with " << s;
+
+ if (!expectSuccess) {
+ ASSERT_EQ(out.TYPE_NULL, out.dataType) << "Failed with " << s;
+ return;
+ }
+
+ if (expectHex) {
+ ASSERT_EQ(out.TYPE_INT_HEX, out.dataType) << "Failed with " << s;
+ } else {
+ ASSERT_EQ(out.TYPE_INT_DEC, out.dataType) << "Failed with " << s;
+ }
+
+ ASSERT_EQ(expectedValue, out.data) << "Failed with " << s;
+}
+
+TEST(ResTableTest, U16StringToInt) {
+ testU16StringToInt(u"", 0U, false, false);
+ testU16StringToInt(u" ", 0U, false, false);
+ testU16StringToInt(u"\t\n", 0U, false, false);
+
+ testU16StringToInt(u"abcd", 0U, false, false);
+ testU16StringToInt(u"10abcd", 0U, false, false);
+ testU16StringToInt(u"42 42", 0U, false, false);
+ testU16StringToInt(u"- 42", 0U, false, false);
+ testU16StringToInt(u"-", 0U, false, false);
+
+ testU16StringToInt(u"0x", 0U, false, true);
+ testU16StringToInt(u"0xnope", 0U, false, true);
+ testU16StringToInt(u"0X42", 0U, false, true);
+ testU16StringToInt(u"0x42 0x42", 0U, false, true);
+ testU16StringToInt(u"-0x0", 0U, false, true);
+ testU16StringToInt(u"-0x42", 0U, false, true);
+ testU16StringToInt(u"- 0x42", 0U, false, true);
+
+ // Note that u" 42" would pass. This preserves the old behavior, but it may
+ // not be desired.
+ testU16StringToInt(u"42 ", 0U, false, false);
+ testU16StringToInt(u"0x42 ", 0U, false, true);
+
+ // Decimal cases.
+ testU16StringToInt(u"0", 0U, true, false);
+ testU16StringToInt(u"-0", 0U, true, false);
+ testU16StringToInt(u"42", 42U, true, false);
+ testU16StringToInt(u" 42", 42U, true, false);
+ testU16StringToInt(u"-42", static_cast<uint32_t>(-42), true, false);
+ testU16StringToInt(u" -42", static_cast<uint32_t>(-42), true, false);
+ testU16StringToInt(u"042", 42U, true, false);
+ testU16StringToInt(u"-042", static_cast<uint32_t>(-42), true, false);
+
+ // Hex cases.
+ testU16StringToInt(u"0x0", 0x0, true, true);
+ testU16StringToInt(u"0x42", 0x42, true, true);
+ testU16StringToInt(u" 0x42", 0x42, true, true);
+
+ // Just before overflow cases:
+ testU16StringToInt(u"2147483647", INT_MAX, true, false);
+ testU16StringToInt(u"-2147483648", static_cast<uint32_t>(INT_MIN), true,
+ false);
+ testU16StringToInt(u"0xffffffff", UINT_MAX, true, true);
+
+ // Overflow cases:
+ testU16StringToInt(u"2147483648", 0U, false, false);
+ testU16StringToInt(u"-2147483649", 0U, false, false);
+ testU16StringToInt(u"0x1ffffffff", 0U, false, true);
+}
+
}
diff --git a/libs/common_time/Android.mk b/libs/common_time/Android.mk
index 75eb528..1fec504 100644
--- a/libs/common_time/Android.mk
+++ b/libs/common_time/Android.mk
@@ -33,4 +33,6 @@ LOCAL_SHARED_LIBRARIES := \
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := common_time
+LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
+
include $(BUILD_EXECUTABLE)
diff --git a/libs/common_time/clock_recovery.cpp b/libs/common_time/clock_recovery.cpp
index 3a7c70c..392caa0 100644
--- a/libs/common_time/clock_recovery.cpp
+++ b/libs/common_time/clock_recovery.cpp
@@ -22,6 +22,7 @@
#define __STDC_LIMIT_MACROS
#define LOG_TAG "common_time"
#include <utils/Log.h>
+#include <inttypes.h>
#include <stdint.h>
#include <common_time/local_clock.h>
@@ -280,7 +281,7 @@ bool ClockRecoveryLoop::pushDisciplineEvent(int64_t local_time,
// system.
setTargetCorrection_l(tgt_correction);
- LOG_TS("clock_loop %lld %f %f %f %d\n", raw_delta, delta_f, CO, CObias, tgt_correction);
+ LOG_TS("clock_loop %" PRId64 " %f %f %f %d\n", raw_delta, delta_f, CO, CObias, tgt_correction);
#ifdef TIME_SERVICE_DEBUG
diag_thread_->pushDisciplineEvent(
@@ -335,7 +336,6 @@ void ClockRecoveryLoop::setTargetCorrection_l(int32_t tgt) {
// 300mSec.
if (tgt_correction_ != tgt) {
int64_t now = local_clock_->getLocalTime();
- status_t res;
tgt_correction_ = tgt;
diff --git a/libs/common_time/clock_recovery.h b/libs/common_time/clock_recovery.h
index b6c87ff..278a75e 100644
--- a/libs/common_time/clock_recovery.h
+++ b/libs/common_time/clock_recovery.h
@@ -111,7 +111,6 @@ class ClockRecoveryLoop {
bool last_error_est_valid_;
int32_t last_error_est_usec_;
float last_delta_f_;
- int32_t integrated_error_;
int32_t tgt_correction_;
int32_t cur_correction_;
LinearTransform time_to_cur_slew_;
diff --git a/libs/common_time/common_clock.cpp b/libs/common_time/common_clock.cpp
index c9eb388..ee326e1 100644
--- a/libs/common_time/common_clock.cpp
+++ b/libs/common_time/common_clock.cpp
@@ -19,6 +19,7 @@
#define LOG_TAG "common_time"
#include <utils/Log.h>
+#include <inttypes.h>
#include <stdint.h>
#include <utils/Errors.h>
@@ -50,7 +51,7 @@ bool CommonClock::init(uint64_t local_freq) {
LinearTransform::reduce(&numer, &denom);
if ((numer > UINT32_MAX) || (denom > UINT32_MAX)) {
- ALOGE("Overflow in CommonClock::init while trying to reduce %lld/%lld",
+ ALOGE("Overflow in CommonClock::init while trying to reduce %" PRIu64 "/%" PRIu64,
kCommonFreq, local_freq);
return false;
}
diff --git a/libs/common_time/common_clock_service.cpp b/libs/common_time/common_clock_service.cpp
index 9ca6f35..592ab1d 100644
--- a/libs/common_time/common_clock_service.cpp
+++ b/libs/common_time/common_clock_service.cpp
@@ -99,14 +99,14 @@ status_t CommonClockService::registerListener(
Mutex::Autolock lock(mCallbackLock);
// check whether this is a duplicate
for (size_t i = 0; i < mListeners.size(); i++) {
- if (mListeners[i]->asBinder() == listener->asBinder())
+ if (IInterface::asBinder(mListeners[i]) == IInterface::asBinder(listener))
return ALREADY_EXISTS;
}
}
mListeners.add(listener);
mTimeServer.reevaluateAutoDisableState(0 != mListeners.size());
- return listener->asBinder()->linkToDeath(this);
+ return IInterface::asBinder(listener)->linkToDeath(this);
}
status_t CommonClockService::unregisterListener(
@@ -117,8 +117,8 @@ status_t CommonClockService::unregisterListener(
{ // scoping for autolock pattern
Mutex::Autolock lock(mCallbackLock);
for (size_t i = 0; i < mListeners.size(); i++) {
- if (mListeners[i]->asBinder() == listener->asBinder()) {
- mListeners[i]->asBinder()->unlinkToDeath(this);
+ if (IInterface::asBinder(mListeners[i]) == IInterface::asBinder(listener)) {
+ IInterface::asBinder(mListeners[i])->unlinkToDeath(this);
mListeners.removeAt(i);
ret_val = OK;
break;
@@ -136,7 +136,7 @@ void CommonClockService::binderDied(const wp<IBinder>& who) {
{ // scoping for autolock pattern
Mutex::Autolock lock(mCallbackLock);
for (size_t i = 0; i < mListeners.size(); i++) {
- if (mListeners[i]->asBinder() == who) {
+ if (IInterface::asBinder(mListeners[i]) == who) {
mListeners.removeAt(i);
break;
}
diff --git a/libs/common_time/common_time_server.cpp b/libs/common_time/common_time_server.cpp
index 3e11987..01372e0 100644
--- a/libs/common_time/common_time_server.cpp
+++ b/libs/common_time/common_time_server.cpp
@@ -25,6 +25,7 @@
#include <arpa/inet.h>
#include <assert.h>
#include <fcntl.h>
+#include <inttypes.h>
#include <linux/if_ether.h>
#include <net/if.h>
#include <net/if_arp.h>
@@ -969,13 +970,14 @@ bool CommonTimeServer::handleSyncResponse(
// if the RTT of the packet is significantly larger than the panic
// threshold, we should simply discard it. Its better to do nothing
// than to take cues from a packet like that.
- int rttCommon = mCommonClock.localDurationToCommonDuration(rtt);
+ int64_t rttCommon = mCommonClock.localDurationToCommonDuration(rtt);
if (rttCommon > (static_cast<int64_t>(mPanicThresholdUsec) *
kRTTDiscardPanicThreshMultiplier)) {
- ALOGV("Dropping sync response with RTT of %lld uSec", rttCommon);
+ ALOGV("Dropping sync response with RTT of %" PRId64 " uSec", rttCommon);
mClient_ExpiredSyncRespsRXedFromCurMaster++;
if (shouldPanicNotGettingGoodData())
return becomeInitial("RX panic, no good data");
+ return true;
} else {
result = mClockRecovery.pushDisciplineEvent(avgLocal, avgCommon, rttCommon);
mClient_LastGoodSyncRX = clientRxLocalTime;
diff --git a/libs/common_time/common_time_server_api.cpp b/libs/common_time/common_time_server_api.cpp
index e157071..e0f35a9 100644
--- a/libs/common_time/common_time_server_api.cpp
+++ b/libs/common_time/common_time_server_api.cpp
@@ -27,6 +27,8 @@
#include "common_time_server.h"
+#include <inttypes.h>
+
namespace android {
//
@@ -286,7 +288,7 @@ void CommonTimeServer::reevaluateAutoDisableState(bool commonClockHasClients) {
#define checked_percentage(a, b) ((0 == b) ? 0.0f : ((100.0f * a) / b))
status_t CommonTimeServer::dumpClockInterface(int fd,
- const Vector<String16>& args,
+ const Vector<String16>& /* args */,
size_t activeClients) {
AutoMutex _lock(&mLock);
const size_t SIZE = 256;
@@ -308,15 +310,15 @@ status_t CommonTimeServer::dumpClockInterface(int fd,
synced = (OK == mCommonClock.localToCommon(localTime, &commonTime));
sockaddrToString(mMasterEP, mMasterEPValid, maStr, sizeof(maStr));
- dump_printf("Common Clock Service Status\nLocal time : %lld\n",
+ dump_printf("Common Clock Service Status\nLocal time : %" PRId64 "\n",
localTime);
if (synced)
- dump_printf("Common time : %lld\n", commonTime);
+ dump_printf("Common time : %" PRId64 "\n", commonTime);
else
dump_printf("Common time : %s\n", "not synced");
- dump_printf("Timeline ID : %016llx\n", mTimelineID);
+ dump_printf("Timeline ID : %016" PRIu64 "\n", mTimelineID);
dump_printf("State : %s\n", stateToString(mState));
dump_printf("Master Addr : %s\n", maStr);
@@ -349,10 +351,10 @@ status_t CommonTimeServer::dumpClockInterface(int fd,
int64_t localDelta, usecDelta;
localDelta = localTime - mClient_LastGoodSyncRX;
usecDelta = mCommonClock.localDurationToCommonDuration(localDelta);
- dump_printf("Last Good RX : %lld uSec ago\n", usecDelta);
+ dump_printf("Last Good RX : %" PRId64 " uSec ago\n", usecDelta);
}
- dump_printf("Active Clients : %u\n", activeClients);
+ dump_printf("Active Clients : %zu\n", activeClients);
mClient_PacketRTTLog.dumpLog(fd, mCommonClock);
mStateChangeLog.dumpLog(fd);
mElectionLog.dumpLog(fd);
@@ -363,7 +365,7 @@ status_t CommonTimeServer::dumpClockInterface(int fd,
}
status_t CommonTimeServer::dumpConfigInterface(int fd,
- const Vector<String16>& args) {
+ const Vector<String16>& /* args */) {
AutoMutex _lock(&mLock);
const size_t SIZE = 256;
char buffer[SIZE];
@@ -383,7 +385,7 @@ status_t CommonTimeServer::dumpConfigInterface(int fd,
"Bound Interface : %s\n",
mBindIfaceValid ? mBindIface.string() : "<unbound>");
dump_printf("Master Election Endpoint : %s\n", meStr);
- dump_printf("Master Election Group ID : %016llx\n", mSyncGroupID);
+ dump_printf("Master Election Group ID : %016" PRIu64 "\n", mSyncGroupID);
dump_printf("Master Announce Interval : %d mSec\n",
mMasterAnnounceIntervalMs);
dump_printf("Client Sync Interval : %d mSec\n",
@@ -419,12 +421,12 @@ void CommonTimeServer::PacketRTTLog::dumpLog(int fd, const CommonClock& cclk) {
if (rxTimes[i]) {
int64_t delta = rxTimes[i] - txTimes[i];
int64_t deltaUsec = cclk.localDurationToCommonDuration(delta);
- dump_printf("pkt[%2d] : localTX %12lld localRX %12lld "
+ dump_printf("pkt[%2d] : localTX %12" PRId64 " localRX %12" PRId64 " "
"(%.3f msec RTT)\n",
ndx, txTimes[i], rxTimes[i],
static_cast<float>(deltaUsec) / 1000.0);
} else {
- dump_printf("pkt[%2d] : localTX %12lld localRX never\n",
+ dump_printf("pkt[%2d] : localTX %12" PRId64 " localRX never\n",
ndx, txTimes[i]);
}
i = (i + 1) % RTT_LOG_SIZE;
diff --git a/libs/common_time/main.cpp b/libs/common_time/main.cpp
index 49eb30a..ac52c85 100644
--- a/libs/common_time/main.cpp
+++ b/libs/common_time/main.cpp
@@ -27,7 +27,7 @@
#include "common_time_server.h"
-int main(int argc, char *argv[]) {
+int main() {
using namespace android;
sp<CommonTimeServer> service = new CommonTimeServer();
diff --git a/libs/hwui/AmbientShadow.cpp b/libs/hwui/AmbientShadow.cpp
index b2dba00..a4100a2 100644
--- a/libs/hwui/AmbientShadow.cpp
+++ b/libs/hwui/AmbientShadow.cpp
@@ -45,8 +45,9 @@
/**
* Other constants:
*/
-// For the edge of the penumbra, the opacity is 0.
-#define OUTER_OPACITY (0.0f)
+// For the edge of the penumbra, the opacity is 0. After transform (1 - alpha),
+// it is 1.
+#define TRANSFORMED_OUTER_OPACITY (1.0f)
// Once the alpha difference is greater than this threshold, we will allocate extra
// edge vertices.
@@ -60,6 +61,7 @@
#include "AmbientShadow.h"
#include "ShadowTessellator.h"
#include "Vertex.h"
+#include "VertexBuffer.h"
#include "utils/MathUtils.h"
namespace android {
@@ -82,11 +84,13 @@ inline float getAlphaFromFactoredZ(float factoredZ) {
return 1.0 / (1 + MathUtils::max(factoredZ, 0.0f));
}
+// The shader is using gaussian function e^-(1-x)*(1-x)*4, therefore, we transform
+// the alpha value to (1 - alpha)
inline float getTransformedAlphaFromAlpha(float alpha) {
- return acosf(1.0f - 2.0f * alpha);
+ return 1.0f - alpha;
}
-// The output is ranged from 0 to M_PI.
+// The output is ranged from 0 to 1.
inline float getTransformedAlphaFromFactoredZ(float factoredZ) {
return getTransformedAlphaFromAlpha(getAlphaFromFactoredZ(factoredZ));
}
@@ -121,14 +125,14 @@ inline void computeBufferSize(int* totalVertexCount, int* totalIndexCount,
*totalUmbraCount = 0;
if (!isCasterOpaque) {
// Add the centroid if occluder is translucent.
- *totalVertexCount++;
+ (*totalVertexCount)++;
*totalIndexCount += 2 * innerVertexCount + 1;
*totalUmbraCount = innerVertexCount;
}
}
inline bool needsExtraForEdge(float firstAlpha, float secondAlpha) {
- return abs(firstAlpha - secondAlpha) > ALPHA_THRESHOLD;
+ return fabsf(firstAlpha - secondAlpha) > ALPHA_THRESHOLD;
}
/**
@@ -178,7 +182,7 @@ inline bool needsExtraForEdge(float firstAlpha, float secondAlpha) {
void AmbientShadow::createAmbientShadow(bool isCasterOpaque,
const Vector3* casterVertices, int casterVertexCount, const Vector3& centroid3d,
float heightFactor, float geomFactor, VertexBuffer& shadowVertexBuffer) {
- shadowVertexBuffer.setMode(VertexBuffer::kIndices);
+ shadowVertexBuffer.setMeshFeatureFlags(VertexBuffer::kAlpha | VertexBuffer::kIndices);
// In order to computer the outer vertices in one loop, we need pre-compute
// the normal by the vertex (n - 1) to vertex 0, and the spike and alpha value
@@ -248,7 +252,7 @@ void AmbientShadow::createAmbientShadow(bool isCasterOpaque,
indexBuffer[indexBufferIndex++] = vertexBufferIndex;
indexBuffer[indexBufferIndex++] = currentInnerVertexIndex;
AlphaVertex::set(&shadowVertices[vertexBufferIndex++], outerVertex.x,
- outerVertex.y, OUTER_OPACITY);
+ outerVertex.y, TRANSFORMED_OUTER_OPACITY);
if (j == 0) {
outerStart = outerVertex;
@@ -284,7 +288,7 @@ void AmbientShadow::createAmbientShadow(bool isCasterOpaque,
(outerLast * startWeight + outerNext * k) / extraVerticesNumber;
indexBuffer[indexBufferIndex++] = vertexBufferIndex;
AlphaVertex::set(&shadowVertices[vertexBufferIndex++], currentOuter.x,
- currentOuter.y, OUTER_OPACITY);
+ currentOuter.y, TRANSFORMED_OUTER_OPACITY);
if (!isCasterOpaque) {
umbraVertices[umbraIndex++] = vertexBufferIndex;
diff --git a/libs/hwui/AmbientShadow.h b/libs/hwui/AmbientShadow.h
index 9660dc0..8eb1048 100644
--- a/libs/hwui/AmbientShadow.h
+++ b/libs/hwui/AmbientShadow.h
@@ -19,13 +19,13 @@
#define ANDROID_HWUI_AMBIENT_SHADOW_H
#include "Debug.h"
-#include "OpenGLRenderer.h"
#include "Vector.h"
-#include "VertexBuffer.h"
namespace android {
namespace uirenderer {
+class VertexBuffer;
+
/**
* AmbientShadow is used to calculate the ambient shadow value around a polygon.
*/
diff --git a/libs/hwui/Android.common.mk b/libs/hwui/Android.common.mk
new file mode 100644
index 0000000..836f868
--- /dev/null
+++ b/libs/hwui/Android.common.mk
@@ -0,0 +1,119 @@
+# getConfig in external/skia/include/core/SkBitmap.h is deprecated.
+# Allow Gnu extension: in-class initializer of static 'const float' member.
+LOCAL_CLANG_CFLAGS += \
+ -Wno-deprecated-declarations \
+ -Wno-gnu-static-float-init
+
+LOCAL_SRC_FILES := \
+ font/CacheTexture.cpp \
+ font/Font.cpp \
+ renderstate/Blend.cpp \
+ renderstate/MeshState.cpp \
+ renderstate/PixelBufferState.cpp \
+ renderstate/RenderState.cpp \
+ renderstate/Scissor.cpp \
+ renderstate/Stencil.cpp \
+ renderstate/TextureState.cpp \
+ renderthread/CanvasContext.cpp \
+ renderthread/DrawFrameTask.cpp \
+ renderthread/EglManager.cpp \
+ renderthread/RenderProxy.cpp \
+ renderthread/RenderTask.cpp \
+ renderthread/RenderThread.cpp \
+ renderthread/TimeLord.cpp \
+ thread/TaskManager.cpp \
+ utils/Blur.cpp \
+ utils/GLUtils.cpp \
+ utils/LinearAllocator.cpp \
+ utils/SortedListImpl.cpp \
+ AmbientShadow.cpp \
+ AnimationContext.cpp \
+ Animator.cpp \
+ AnimatorManager.cpp \
+ AssetAtlas.cpp \
+ Caches.cpp \
+ CanvasState.cpp \
+ ClipArea.cpp \
+ DamageAccumulator.cpp \
+ DeferredDisplayList.cpp \
+ DeferredLayerUpdater.cpp \
+ DisplayList.cpp \
+ DisplayListCanvas.cpp \
+ Dither.cpp \
+ DrawProfiler.cpp \
+ Extensions.cpp \
+ FboCache.cpp \
+ FontRenderer.cpp \
+ FrameInfo.cpp \
+ GammaFontRenderer.cpp \
+ GlopBuilder.cpp \
+ GradientCache.cpp \
+ Image.cpp \
+ Interpolator.cpp \
+ JankTracker.cpp \
+ Layer.cpp \
+ LayerCache.cpp \
+ LayerRenderer.cpp \
+ Matrix.cpp \
+ OpenGLRenderer.cpp \
+ Patch.cpp \
+ PatchCache.cpp \
+ PathCache.cpp \
+ PathTessellator.cpp \
+ PixelBuffer.cpp \
+ Program.cpp \
+ ProgramCache.cpp \
+ RenderBufferCache.cpp \
+ RenderNode.cpp \
+ RenderProperties.cpp \
+ ResourceCache.cpp \
+ ShadowTessellator.cpp \
+ SkiaCanvas.cpp \
+ SkiaCanvasProxy.cpp \
+ SkiaShader.cpp \
+ Snapshot.cpp \
+ SpotShadow.cpp \
+ TessellationCache.cpp \
+ TextDropShadowCache.cpp \
+ Texture.cpp \
+ TextureCache.cpp
+
+intermediates := $(call intermediates-dir-for,STATIC_LIBRARIES,libRS,TARGET,)
+
+LOCAL_C_INCLUDES += \
+ external/skia/src/core
+
+LOCAL_CFLAGS += -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES
+LOCAL_SHARED_LIBRARIES := liblog libcutils libutils libEGL libGLESv2 libskia libui libgui
+
+ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT))
+ LOCAL_CFLAGS += -DANDROID_ENABLE_RENDERSCRIPT
+ LOCAL_SHARED_LIBRARIES += libRS libRScpp
+ LOCAL_C_INCLUDES += \
+ $(intermediates) \
+ frameworks/rs/cpp \
+ frameworks/rs \
+
+endif
+
+ifndef HWUI_COMPILE_SYMBOLS
+ LOCAL_CFLAGS += -fvisibility=hidden
+endif
+
+ifdef HWUI_COMPILE_FOR_PERF
+ # TODO: Non-arm?
+ LOCAL_CFLAGS += -fno-omit-frame-pointer -marm -mapcs
+endif
+
+ifeq (true, $(HWUI_NULL_GPU))
+ LOCAL_SRC_FILES += \
+ tests/nullegl.cpp \
+ tests/nullgles.cpp
+
+ LOCAL_CFLAGS += -DHWUI_NULL_GPU
+endif
+
+# Defaults for ATRACE_TAG and LOG_TAG for libhwui
+LOCAL_CFLAGS += -DATRACE_TAG=ATRACE_TAG_VIEW -DLOG_TAG=\"OpenGLRenderer\"
+
+LOCAL_CFLAGS += -Wall -Werror -Wno-unused-parameter -Wunreachable-code
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 49560ff..91e289c 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -1,110 +1,12 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-# Only build libhwui when USE_OPENGL_RENDERER is
-# defined in the current device/board configuration
-ifeq ($(USE_OPENGL_RENDERER),true)
- LOCAL_SRC_FILES := \
- utils/Blur.cpp \
- utils/GLUtils.cpp \
- utils/SortedListImpl.cpp \
- thread/TaskManager.cpp \
- font/CacheTexture.cpp \
- font/Font.cpp \
- AmbientShadow.cpp \
- AnimationContext.cpp \
- Animator.cpp \
- AnimatorManager.cpp \
- AssetAtlas.cpp \
- DamageAccumulator.cpp \
- FontRenderer.cpp \
- GammaFontRenderer.cpp \
- Caches.cpp \
- DisplayList.cpp \
- DeferredDisplayList.cpp \
- DeferredLayerUpdater.cpp \
- DisplayListLogBuffer.cpp \
- DisplayListRenderer.cpp \
- Dither.cpp \
- DrawProfiler.cpp \
- Extensions.cpp \
- FboCache.cpp \
- GradientCache.cpp \
- Image.cpp \
- Interpolator.cpp \
- Layer.cpp \
- LayerCache.cpp \
- LayerRenderer.cpp \
- Matrix.cpp \
- OpenGLRenderer.cpp \
- Patch.cpp \
- PatchCache.cpp \
- PathCache.cpp \
- PathTessellator.cpp \
- PixelBuffer.cpp \
- Program.cpp \
- ProgramCache.cpp \
- RenderBufferCache.cpp \
- RenderNode.cpp \
- RenderProperties.cpp \
- RenderState.cpp \
- ResourceCache.cpp \
- ShadowTessellator.cpp \
- SkiaShader.cpp \
- Snapshot.cpp \
- SpotShadow.cpp \
- StatefulBaseRenderer.cpp \
- Stencil.cpp \
- TessellationCache.cpp \
- Texture.cpp \
- TextureCache.cpp \
- TextDropShadowCache.cpp
+LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+LOCAL_MODULE := libhwui
-# RenderThread stuff
- LOCAL_SRC_FILES += \
- renderthread/CanvasContext.cpp \
- renderthread/DrawFrameTask.cpp \
- renderthread/EglManager.cpp \
- renderthread/RenderProxy.cpp \
- renderthread/RenderTask.cpp \
- renderthread/RenderThread.cpp \
- renderthread/TimeLord.cpp
+include $(LOCAL_PATH)/Android.common.mk
- intermediates := $(call intermediates-dir-for,STATIC_LIBRARIES,libRS,TARGET,)
+include $(BUILD_SHARED_LIBRARY)
- LOCAL_C_INCLUDES += \
- external/skia/src/core
-
- LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES
- LOCAL_CFLAGS += -Wno-unused-parameter
- LOCAL_MODULE_CLASS := SHARED_LIBRARIES
- LOCAL_SHARED_LIBRARIES := liblog libcutils libutils libEGL libGLESv2 libskia libui libgui
- LOCAL_MODULE := libhwui
- LOCAL_MODULE_TAGS := optional
-
- include external/stlport/libstlport.mk
-
- ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT))
- LOCAL_CFLAGS += -DANDROID_ENABLE_RENDERSCRIPT
- LOCAL_SHARED_LIBRARIES += libRS libRScpp
- LOCAL_C_INCLUDES += \
- $(intermediates) \
- frameworks/rs/cpp \
- frameworks/rs
- endif
-
- ifndef HWUI_COMPILE_SYMBOLS
- LOCAL_CFLAGS += -fvisibility=hidden
- endif
-
- ifdef HWUI_COMPILE_FOR_PERF
- LOCAL_CFLAGS += -fno-omit-frame-pointer -marm -mapcs
- endif
-
- # Defaults for ATRACE_TAG and LOG_TAG for libhwui
- LOCAL_CFLAGS += -DATRACE_TAG=ATRACE_TAG_VIEW -DLOG_TAG=\"OpenGLRenderer\"
-
- include $(BUILD_SHARED_LIBRARY)
-
- include $(call all-makefiles-under,$(LOCAL_PATH))
-endif
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/libs/hwui/AnimationContext.cpp b/libs/hwui/AnimationContext.cpp
index a20bdae..097be08 100644
--- a/libs/hwui/AnimationContext.cpp
+++ b/libs/hwui/AnimationContext.cpp
@@ -59,7 +59,7 @@ void AnimationContext::startFrame(TreeInfo::TraversalMode mode) {
"Missed running animations last frame!");
AnimationHandle* head = mNextFrameAnimations.mNextHandle;
if (head) {
- mNextFrameAnimations.mNextHandle = NULL;
+ mNextFrameAnimations.mNextHandle = nullptr;
mCurrentFrameAnimations.mNextHandle = head;
head->mPreviousHandle = &mCurrentFrameAnimations;
}
@@ -84,15 +84,15 @@ void AnimationContext::callOnFinished(BaseRenderNodeAnimator* animator,
AnimationHandle::AnimationHandle(AnimationContext& context)
: mContext(context)
- , mPreviousHandle(NULL)
- , mNextHandle(NULL) {
+ , mPreviousHandle(nullptr)
+ , mNextHandle(nullptr) {
}
AnimationHandle::AnimationHandle(RenderNode& animatingNode, AnimationContext& context)
: mRenderNode(&animatingNode)
, mContext(context)
- , mPreviousHandle(NULL)
- , mNextHandle(NULL) {
+ , mPreviousHandle(nullptr)
+ , mNextHandle(nullptr) {
mRenderNode->animators().setAnimationHandle(this);
}
@@ -114,7 +114,7 @@ void AnimationHandle::release() {
LOG_ALWAYS_FATAL_IF(mRenderNode->animators().hasAnimators(),
"Releasing the handle for an RenderNode with outstanding animators!");
removeFromList();
- mRenderNode->animators().setAnimationHandle(NULL);
+ mRenderNode->animators().setAnimationHandle(nullptr);
delete this;
}
@@ -135,8 +135,8 @@ void AnimationHandle::removeFromList() {
if (mNextHandle) {
mNextHandle->mPreviousHandle = mPreviousHandle;
}
- mPreviousHandle = NULL;
- mNextHandle = NULL;
+ mPreviousHandle = nullptr;
+ mNextHandle = nullptr;
}
} /* namespace uirenderer */
diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp
index 8bf2107..512e0e2 100644
--- a/libs/hwui/Animator.cpp
+++ b/libs/hwui/Animator.cpp
@@ -20,6 +20,7 @@
#include <set>
#include "AnimationContext.h"
+#include "Interpolator.h"
#include "RenderNode.h"
#include "RenderProperties.h"
@@ -31,11 +32,10 @@ namespace uirenderer {
************************************************************/
BaseRenderNodeAnimator::BaseRenderNodeAnimator(float finalValue)
- : mTarget(NULL)
+ : mTarget(nullptr)
, mFinalValue(finalValue)
, mDeltaValue(0)
, mFromValue(0)
- , mInterpolator(0)
, mStagingPlayState(NOT_STARTED)
, mPlayState(NOT_STARTED)
, mHasStartValue(false)
@@ -46,7 +46,6 @@ BaseRenderNodeAnimator::BaseRenderNodeAnimator(float finalValue)
}
BaseRenderNodeAnimator::~BaseRenderNodeAnimator() {
- delete mInterpolator;
}
void BaseRenderNodeAnimator::checkMutable() {
@@ -57,8 +56,7 @@ void BaseRenderNodeAnimator::checkMutable() {
void BaseRenderNodeAnimator::setInterpolator(Interpolator* interpolator) {
checkMutable();
- delete mInterpolator;
- mInterpolator = interpolator;
+ mInterpolator.reset(interpolator);
}
void BaseRenderNodeAnimator::setStartValue(float value) {
@@ -118,7 +116,7 @@ void BaseRenderNodeAnimator::transitionToRunning(AnimationContext& context) {
}
// No interpolator was set, use the default
if (!mInterpolator) {
- mInterpolator = Interpolator::createDefaultInterpolator();
+ mInterpolator.reset(Interpolator::createDefaultInterpolator());
}
if (mDuration < 0 || mDuration > 50000) {
ALOGW("Your duration is strange and confusing: %" PRId64, mDuration);
diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h
index 35a4a09..1b3d8e7 100644
--- a/libs/hwui/Animator.h
+++ b/libs/hwui/Animator.h
@@ -16,13 +16,12 @@
#ifndef ANIMATOR_H
#define ANIMATOR_H
+#include <memory>
#include <cutils/compiler.h>
#include <utils/RefBase.h>
#include <utils/StrongPointer.h>
+#include <utils/Timers.h>
-#include "CanvasProperty.h"
-#include "Interpolator.h"
-#include "TreeInfo.h"
#include "utils/Macros.h"
namespace android {
@@ -30,6 +29,9 @@ namespace uirenderer {
class AnimationContext;
class BaseRenderNodeAnimator;
+class CanvasPropertyPrimitive;
+class CanvasPropertyPaint;
+class Interpolator;
class RenderNode;
class RenderProperties;
@@ -62,7 +64,7 @@ public:
void attach(RenderNode* target);
virtual void onAttached() {}
- void detach() { mTarget = 0; }
+ void detach() { mTarget = nullptr; }
void pushStaging(AnimationContext& context);
bool animate(AnimationContext& context);
@@ -98,7 +100,7 @@ protected:
float mDeltaValue;
float mFromValue;
- Interpolator* mInterpolator;
+ std::unique_ptr<Interpolator> mInterpolator;
PlayState mStagingPlayState;
PlayState mPlayState;
bool mHasStartValue;
@@ -137,10 +139,10 @@ public:
ANDROID_API virtual uint32_t dirtyMask();
protected:
- virtual float getValue(RenderNode* target) const;
- virtual void setValue(RenderNode* target, float value);
- virtual void onAttached();
- virtual void onStagingPlayStateChanged();
+ virtual float getValue(RenderNode* target) const override;
+ virtual void setValue(RenderNode* target, float value) override;
+ virtual void onAttached() override;
+ virtual void onStagingPlayStateChanged() override;
private:
typedef bool (RenderProperties::*SetFloatProperty)(float value);
@@ -160,8 +162,8 @@ public:
ANDROID_API virtual uint32_t dirtyMask();
protected:
- virtual float getValue(RenderNode* target) const;
- virtual void setValue(RenderNode* target, float value);
+ virtual float getValue(RenderNode* target) const override;
+ virtual void setValue(RenderNode* target, float value) override;
private:
sp<CanvasPropertyPrimitive> mProperty;
};
@@ -179,8 +181,8 @@ public:
ANDROID_API virtual uint32_t dirtyMask();
protected:
- virtual float getValue(RenderNode* target) const;
- virtual void setValue(RenderNode* target, float value);
+ virtual float getValue(RenderNode* target) const override;
+ virtual void setValue(RenderNode* target, float value) override;
private:
sp<CanvasPropertyPaint> mProperty;
PaintField mField;
@@ -194,8 +196,8 @@ public:
ANDROID_API virtual uint32_t dirtyMask();
protected:
- virtual float getValue(RenderNode* target) const;
- virtual void setValue(RenderNode* target, float value);
+ virtual float getValue(RenderNode* target) const override;
+ virtual void setValue(RenderNode* target, float value) override;
private:
int mCenterX, mCenterY;
diff --git a/libs/hwui/AnimatorManager.cpp b/libs/hwui/AnimatorManager.cpp
index c28fb88..966959a 100644
--- a/libs/hwui/AnimatorManager.cpp
+++ b/libs/hwui/AnimatorManager.cpp
@@ -17,7 +17,9 @@
#include <algorithm>
+#include "Animator.h"
#include "AnimationContext.h"
+#include "DamageAccumulator.h"
#include "RenderNode.h"
namespace android {
@@ -27,12 +29,12 @@ using namespace std;
static void unref(BaseRenderNodeAnimator* animator) {
animator->detach();
- animator->decStrong(0);
+ animator->decStrong(nullptr);
}
AnimatorManager::AnimatorManager(RenderNode& parent)
: mParent(parent)
- , mAnimationHandle(NULL) {
+ , mAnimationHandle(nullptr) {
}
AnimatorManager::~AnimatorManager() {
@@ -41,7 +43,7 @@ AnimatorManager::~AnimatorManager() {
}
void AnimatorManager::addAnimator(const sp<BaseRenderNodeAnimator>& animator) {
- animator->incStrong(0);
+ animator->incStrong(nullptr);
animator->attach(&mParent);
mNewAnimators.push_back(animator.get());
}
@@ -85,7 +87,7 @@ public:
dirtyMask |= animator->dirtyMask();
bool remove = animator->animate(mContext);
if (remove) {
- animator->decStrong(0);
+ animator->decStrong(nullptr);
} else {
if (animator->isRunning()) {
mInfo.out.hasAnimations = true;
@@ -142,7 +144,7 @@ static void endStagingAnimator(BaseRenderNodeAnimator* animator) {
if (animator->listener()) {
animator->listener()->onAnimationFinished(animator);
}
- animator->decStrong(0);
+ animator->decStrong(nullptr);
}
void AnimatorManager::endAllStagingAnimators() {
@@ -159,7 +161,7 @@ public:
void operator() (BaseRenderNodeAnimator* animator) {
animator->forceEndNow(mContext);
- animator->decStrong(0);
+ animator->decStrong(nullptr);
}
private:
diff --git a/libs/hwui/AnimatorManager.h b/libs/hwui/AnimatorManager.h
index d03d427..fb75eb8 100644
--- a/libs/hwui/AnimatorManager.h
+++ b/libs/hwui/AnimatorManager.h
@@ -21,7 +21,6 @@
#include <cutils/compiler.h>
#include <utils/StrongPointer.h>
-#include "TreeInfo.h"
#include "utils/Macros.h"
namespace android {
@@ -30,6 +29,7 @@ namespace uirenderer {
class AnimationHandle;
class BaseRenderNodeAnimator;
class RenderNode;
+class TreeInfo;
// Responsible for managing the animators for a single RenderNode
class AnimatorManager {
diff --git a/libs/hwui/AssetAtlas.cpp b/libs/hwui/AssetAtlas.cpp
index 52ca92d..4d2e3a0 100644
--- a/libs/hwui/AssetAtlas.cpp
+++ b/libs/hwui/AssetAtlas.cpp
@@ -18,6 +18,7 @@
#include "AssetAtlas.h"
#include "Caches.h"
+#include "Image.h"
#include <GLES2/gl2ext.h>
@@ -47,7 +48,7 @@ void AssetAtlas::init(sp<GraphicBuffer> buffer, int64_t* map, int count) {
} else {
ALOGW("Could not create atlas image");
delete mImage;
- mImage = NULL;
+ mImage = nullptr;
}
updateTextureId();
@@ -56,7 +57,7 @@ void AssetAtlas::init(sp<GraphicBuffer> buffer, int64_t* map, int count) {
void AssetAtlas::terminate() {
if (mImage) {
delete mImage;
- mImage = NULL;
+ mImage = nullptr;
updateTextureId();
}
}
@@ -82,12 +83,12 @@ void AssetAtlas::updateTextureId() {
AssetAtlas::Entry* AssetAtlas::getEntry(const SkBitmap* bitmap) const {
ssize_t index = mEntries.indexOfKey(bitmap);
- return index >= 0 ? mEntries.valueAt(index) : NULL;
+ return index >= 0 ? mEntries.valueAt(index) : nullptr;
}
Texture* AssetAtlas::getEntryTexture(const SkBitmap* bitmap) const {
ssize_t index = mEntries.indexOfKey(bitmap);
- return index >= 0 ? mEntries.valueAt(index)->texture : NULL;
+ return index >= 0 ? mEntries.valueAt(index)->texture : nullptr;
}
/**
@@ -98,12 +99,12 @@ struct DelegateTexture: public Texture {
DelegateTexture(Caches& caches, Texture* delegate): Texture(caches), mDelegate(delegate) { }
virtual void setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture = false,
- bool force = false, GLenum renderTarget = GL_TEXTURE_2D) {
+ bool force = false, GLenum renderTarget = GL_TEXTURE_2D) override {
mDelegate->setWrapST(wrapS, wrapT, bindTexture, force, renderTarget);
}
virtual void setFilterMinMag(GLenum min, GLenum mag, bool bindTexture = false,
- bool force = false, GLenum renderTarget = GL_TEXTURE_2D) {
+ bool force = false, GLenum renderTarget = GL_TEXTURE_2D) override {
mDelegate->setFilterMinMag(min, mag, bindTexture, force, renderTarget);
}
diff --git a/libs/hwui/AssetAtlas.h b/libs/hwui/AssetAtlas.h
index fffd740..1772eff 100644
--- a/libs/hwui/AssetAtlas.h
+++ b/libs/hwui/AssetAtlas.h
@@ -27,7 +27,6 @@
#include <SkBitmap.h>
-#include "Image.h"
#include "Texture.h"
#include "UvMapper.h"
@@ -35,6 +34,7 @@ namespace android {
namespace uirenderer {
class Caches;
+class Image;
/**
* An asset atlas holds a collection of framework bitmaps in a single OpenGL
@@ -106,7 +106,7 @@ public:
friend class AssetAtlas;
};
- AssetAtlas(): mTexture(NULL), mImage(NULL),
+ AssetAtlas(): mTexture(nullptr), mImage(nullptr),
mBlendKey(true), mOpaqueKey(false) { }
~AssetAtlas() { terminate(); }
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index cdf8150..fd5a2ce 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -16,25 +16,22 @@
#define LOG_TAG "OpenGLRenderer"
-#include <utils/Log.h>
-#include <utils/String8.h>
-
#include "Caches.h"
-#include "DisplayListRenderer.h"
-#include "Properties.h"
+
+#include "GammaFontRenderer.h"
#include "LayerRenderer.h"
+#include "Properties.h"
+#include "renderstate/RenderState.h"
#include "ShadowTessellator.h"
-#include "RenderState.h"
-
-namespace android {
-#ifdef USE_OPENGL_RENDERER
-using namespace uirenderer;
-ANDROID_SINGLETON_STATIC_INSTANCE(Caches);
-#endif
+#include <utils/Log.h>
+#include <utils/String8.h>
+namespace android {
namespace uirenderer {
+Caches* Caches::sInstance = nullptr;
+
///////////////////////////////////////////////////////////////////////////////
// Macros
///////////////////////////////////////////////////////////////////////////////
@@ -49,8 +46,14 @@ namespace uirenderer {
// Constructors/destructor
///////////////////////////////////////////////////////////////////////////////
-Caches::Caches(): Singleton<Caches>(),
- mExtensions(Extensions::getInstance()), mInitialized(false), mRenderState(NULL) {
+Caches::Caches(RenderState& renderState)
+ : gradientCache(mExtensions)
+ , patchCache(renderState)
+ , programCache(mExtensions)
+ , dither(*this)
+ , mRenderState(&renderState)
+ , mInitialized(false) {
+ INIT_LOGD("Creating OpenGL renderer caches");
init();
initFont();
initConstraints();
@@ -60,7 +63,8 @@ Caches::Caches(): Singleton<Caches>(),
initTempProperties();
mDebugLevel = readDebugLevel();
- ALOGD("Enabling debug mode %d", mDebugLevel);
+ ALOGD_IF(mDebugLevel != kDebugDisabled,
+ "Enabling debug mode %d", mDebugLevel);
}
bool Caches::init() {
@@ -68,33 +72,8 @@ bool Caches::init() {
ATRACE_NAME("Caches::init");
- glGenBuffers(1, &meshBuffer);
- glBindBuffer(GL_ARRAY_BUFFER, meshBuffer);
- glBufferData(GL_ARRAY_BUFFER, sizeof(gMeshVertices), gMeshVertices, GL_STATIC_DRAW);
-
- mCurrentBuffer = meshBuffer;
- mCurrentIndicesBuffer = 0;
- mCurrentPositionPointer = this;
- mCurrentPositionStride = 0;
- mCurrentTexCoordsPointer = this;
- mCurrentPixelBuffer = 0;
-
- mTexCoordsArrayEnabled = false;
-
- glDisable(GL_SCISSOR_TEST);
- scissorEnabled = false;
- mScissorX = mScissorY = mScissorWidth = mScissorHeight = 0;
-
- glActiveTexture(gTextureUnits[0]);
- mTextureUnit = 0;
-
- mRegionMesh = NULL;
- mMeshIndices = 0;
- mShadowStripsIndices = 0;
- blend = false;
- lastSrcMode = GL_ZERO;
- lastDstMode = GL_ZERO;
- currentProgram = NULL;
+ mRegionMesh = nullptr;
+ mProgram = nullptr;
mFunctorsCount = 0;
@@ -102,11 +81,12 @@ bool Caches::init() {
debugOverdraw = false;
debugStencilClip = kStencilHide;
- patchCache.init(*this);
+ patchCache.init();
mInitialized = true;
- resetBoundTextures();
+ mPixelBufferState = new PixelBufferState();
+ mTextureState = new TextureState();
return true;
}
@@ -126,23 +106,9 @@ void Caches::initExtensions() {
startMark = startMarkNull;
endMark = endMarkNull;
}
-
- if (mExtensions.hasDebugLabel() && (drawDeferDisabled || drawReorderDisabled)) {
- setLabel = glLabelObjectEXT;
- getLabel = glGetObjectLabelEXT;
- } else {
- setLabel = setLabelNull;
- getLabel = getLabelNull;
- }
}
void Caches::initConstraints() {
- GLint maxTextureUnits;
- glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
- if (maxTextureUnits < REQUIRED_TEXTURE_UNITS_COUNT) {
- ALOGW("At least %d texture units are required!", REQUIRED_TEXTURE_UNITS_COUNT);
- }
-
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
}
@@ -164,7 +130,7 @@ bool Caches::initProperties() {
StencilClipDebug prevDebugStencilClip = debugStencilClip;
char property[PROPERTY_VALUE_MAX];
- if (property_get(PROPERTY_DEBUG_LAYERS_UPDATES, property, NULL) > 0) {
+ if (property_get(PROPERTY_DEBUG_LAYERS_UPDATES, property, nullptr) > 0) {
INIT_LOGD(" Layers updates debug enabled: %s", property);
debugLayersUpdates = !strcmp(property, "true");
} else {
@@ -172,7 +138,7 @@ bool Caches::initProperties() {
}
debugOverdraw = false;
- if (property_get(PROPERTY_DEBUG_OVERDRAW, property, NULL) > 0) {
+ if (property_get(PROPERTY_DEBUG_OVERDRAW, property, nullptr) > 0) {
INIT_LOGD(" Overdraw debug enabled: %s", property);
if (!strcmp(property, "show")) {
debugOverdraw = true;
@@ -184,7 +150,7 @@ bool Caches::initProperties() {
}
// See Properties.h for valid values
- if (property_get(PROPERTY_DEBUG_STENCIL_CLIP, property, NULL) > 0) {
+ if (property_get(PROPERTY_DEBUG_STENCIL_CLIP, property, nullptr) > 0) {
INIT_LOGD(" Stencil clip debug enabled: %s", property);
if (!strcmp(property, "hide")) {
debugStencilClip = kStencilHide;
@@ -213,37 +179,47 @@ bool Caches::initProperties() {
INIT_LOGD(" Draw reorder enabled");
}
- return (prevDebugLayersUpdates != debugLayersUpdates) ||
- (prevDebugOverdraw != debugOverdraw) ||
- (prevDebugStencilClip != debugStencilClip);
+ return (prevDebugLayersUpdates != debugLayersUpdates)
+ || (prevDebugOverdraw != debugOverdraw)
+ || (prevDebugStencilClip != debugStencilClip);
}
void Caches::terminate() {
if (!mInitialized) return;
-
- glDeleteBuffers(1, &meshBuffer);
- mCurrentBuffer = 0;
-
- glDeleteBuffers(1, &mMeshIndices);
- delete[] mRegionMesh;
- mMeshIndices = 0;
- mRegionMesh = NULL;
-
- glDeleteBuffers(1, &mShadowStripsIndices);
- mShadowStripsIndices = 0;
+ mRegionMesh.release();
fboCache.clear();
programCache.clear();
- currentProgram = NULL;
+ mProgram = nullptr;
patchCache.clear();
clearGarbage();
+ delete mPixelBufferState;
+ mPixelBufferState = nullptr;
+ delete mTextureState;
+ mTextureState = nullptr;
mInitialized = false;
}
+void Caches::setProgram(const ProgramDescription& description) {
+ setProgram(programCache.get(description));
+}
+
+void Caches::setProgram(Program* program) {
+ if (!program || !program->isInUse()) {
+ if (mProgram) {
+ mProgram->remove();
+ }
+ if (program) {
+ program->use();
+ }
+ mProgram = program;
+ }
+}
+
///////////////////////////////////////////////////////////////////////////////
// Debug
///////////////////////////////////////////////////////////////////////////////
@@ -278,7 +254,7 @@ void Caches::dumpMemoryUsage(String8 &log) {
const Layer* layer = *it;
log.appendFormat(" Layer size %dx%d; isTextureLayer()=%d; texid=%u fbo=%u; refs=%d\n",
layer->getWidth(), layer->getHeight(),
- layer->isTextureLayer(), layer->getTexture(),
+ layer->isTextureLayer(), layer->getTextureId(),
layer->getFbo(), layer->getStrongCount());
memused += layer->getWidth() * layer->getHeight() * 4;
}
@@ -371,285 +347,6 @@ void Caches::flush(FlushMode mode) {
}
///////////////////////////////////////////////////////////////////////////////
-// VBO
-///////////////////////////////////////////////////////////////////////////////
-
-bool Caches::bindMeshBuffer() {
- return bindMeshBuffer(meshBuffer);
-}
-
-bool Caches::bindMeshBuffer(const GLuint buffer) {
- if (mCurrentBuffer != buffer) {
- glBindBuffer(GL_ARRAY_BUFFER, buffer);
- mCurrentBuffer = buffer;
- return true;
- }
- return false;
-}
-
-bool Caches::unbindMeshBuffer() {
- if (mCurrentBuffer) {
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- mCurrentBuffer = 0;
- return true;
- }
- return false;
-}
-
-bool Caches::bindIndicesBufferInternal(const GLuint buffer) {
- if (mCurrentIndicesBuffer != buffer) {
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
- mCurrentIndicesBuffer = buffer;
- return true;
- }
- return false;
-}
-
-bool Caches::bindQuadIndicesBuffer() {
- if (!mMeshIndices) {
- uint16_t* regionIndices = new uint16_t[gMaxNumberOfQuads * 6];
- for (uint32_t i = 0; i < gMaxNumberOfQuads; 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, &mMeshIndices);
- bool force = bindIndicesBufferInternal(mMeshIndices);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, gMaxNumberOfQuads * 6 * sizeof(uint16_t),
- regionIndices, GL_STATIC_DRAW);
-
- delete[] regionIndices;
- return force;
- }
-
- return bindIndicesBufferInternal(mMeshIndices);
-}
-
-bool Caches::bindShadowIndicesBuffer() {
- if (!mShadowStripsIndices) {
- uint16_t* shadowIndices = new uint16_t[MAX_SHADOW_INDEX_COUNT];
- ShadowTessellator::generateShadowIndices(shadowIndices);
- glGenBuffers(1, &mShadowStripsIndices);
- bool force = bindIndicesBufferInternal(mShadowStripsIndices);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_SHADOW_INDEX_COUNT * sizeof(uint16_t),
- shadowIndices, GL_STATIC_DRAW);
-
- delete[] shadowIndices;
- return force;
- }
-
- return bindIndicesBufferInternal(mShadowStripsIndices);
-}
-
-bool Caches::unbindIndicesBuffer() {
- if (mCurrentIndicesBuffer) {
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- mCurrentIndicesBuffer = 0;
- return true;
- }
- return false;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// PBO
-///////////////////////////////////////////////////////////////////////////////
-
-bool Caches::bindPixelBuffer(const GLuint buffer) {
- if (mCurrentPixelBuffer != buffer) {
- glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer);
- mCurrentPixelBuffer = buffer;
- return true;
- }
- return false;
-}
-
-bool Caches::unbindPixelBuffer() {
- if (mCurrentPixelBuffer) {
- glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
- mCurrentPixelBuffer = 0;
- return true;
- }
- return false;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Meshes and textures
-///////////////////////////////////////////////////////////////////////////////
-
-void Caches::bindPositionVertexPointer(bool force, const GLvoid* vertices, GLsizei stride) {
- if (force || vertices != mCurrentPositionPointer || stride != mCurrentPositionStride) {
- GLuint slot = currentProgram->position;
- glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, stride, vertices);
- mCurrentPositionPointer = vertices;
- mCurrentPositionStride = stride;
- }
-}
-
-void Caches::bindTexCoordsVertexPointer(bool force, const GLvoid* vertices, GLsizei stride) {
- if (force || vertices != mCurrentTexCoordsPointer || stride != mCurrentTexCoordsStride) {
- GLuint slot = currentProgram->texCoords;
- glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, stride, vertices);
- mCurrentTexCoordsPointer = vertices;
- mCurrentTexCoordsStride = stride;
- }
-}
-
-void Caches::resetVertexPointers() {
- mCurrentPositionPointer = this;
- mCurrentTexCoordsPointer = this;
-}
-
-void Caches::resetTexCoordsVertexPointer() {
- mCurrentTexCoordsPointer = this;
-}
-
-void Caches::enableTexCoordsVertexArray() {
- if (!mTexCoordsArrayEnabled) {
- glEnableVertexAttribArray(Program::kBindingTexCoords);
- mCurrentTexCoordsPointer = this;
- mTexCoordsArrayEnabled = true;
- }
-}
-
-void Caches::disableTexCoordsVertexArray() {
- if (mTexCoordsArrayEnabled) {
- glDisableVertexAttribArray(Program::kBindingTexCoords);
- mTexCoordsArrayEnabled = false;
- }
-}
-
-void Caches::activeTexture(GLuint textureUnit) {
- if (mTextureUnit != textureUnit) {
- glActiveTexture(gTextureUnits[textureUnit]);
- mTextureUnit = textureUnit;
- }
-}
-
-void Caches::resetActiveTexture() {
- mTextureUnit = -1;
-}
-
-void Caches::bindTexture(GLuint texture) {
- if (mBoundTextures[mTextureUnit] != texture) {
- glBindTexture(GL_TEXTURE_2D, texture);
- mBoundTextures[mTextureUnit] = texture;
- }
-}
-
-void Caches::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 Caches::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 Caches::resetBoundTextures() {
- memset(mBoundTextures, 0, REQUIRED_TEXTURE_UNITS_COUNT * sizeof(GLuint));
-}
-
-void Caches::unbindTexture(GLuint texture) {
- for (int i = 0; i < REQUIRED_TEXTURE_UNITS_COUNT; i++) {
- if (mBoundTextures[i] == texture) {
- mBoundTextures[i] = 0;
- }
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Scissor
-///////////////////////////////////////////////////////////////////////////////
-
-bool Caches::setScissor(GLint x, GLint y, GLint width, GLint height) {
- if (scissorEnabled && (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;
-}
-
-bool Caches::enableScissor() {
- if (!scissorEnabled) {
- glEnable(GL_SCISSOR_TEST);
- scissorEnabled = true;
- resetScissor();
- return true;
- }
- return false;
-}
-
-bool Caches::disableScissor() {
- if (scissorEnabled) {
- glDisable(GL_SCISSOR_TEST);
- scissorEnabled = false;
- return true;
- }
- return false;
-}
-
-void Caches::setScissorEnabled(bool enabled) {
- if (scissorEnabled != enabled) {
- if (enabled) glEnable(GL_SCISSOR_TEST);
- else glDisable(GL_SCISSOR_TEST);
- scissorEnabled = enabled;
- }
-}
-
-void Caches::resetScissor() {
- mScissorX = mScissorY = mScissorWidth = mScissorHeight = 0;
-}
-
-///////////////////////////////////////////////////////////////////////////////
// Tiling
///////////////////////////////////////////////////////////////////////////////
@@ -688,10 +385,10 @@ void Caches::unregisterFunctors(uint32_t functorCount) {
TextureVertex* Caches::getRegionMesh() {
// Create the mesh, 2 triangles and 4 vertices per rectangle in the region
if (!mRegionMesh) {
- mRegionMesh = new TextureVertex[gMaxNumberOfQuads * 4];
+ mRegionMesh.reset(new TextureVertex[kMaxNumberOfQuads * 4]);
}
- return mRegionMesh;
+ return mRegionMesh.get();
}
///////////////////////////////////////////////////////////////////////////////
@@ -699,7 +396,7 @@ TextureVertex* Caches::getRegionMesh() {
///////////////////////////////////////////////////////////////////////////////
void Caches::initTempProperties() {
- propertyLightDiameter = -1.0f;
+ propertyLightRadius = -1.0f;
propertyLightPosY = -1.0f;
propertyLightPosZ = -1.0f;
propertyAmbientRatio = -1.0f;
@@ -713,9 +410,9 @@ void Caches::setTempProperty(const char* name, const char* value) {
propertyAmbientRatio = fmin(fmax(atof(value), 0.0), 10.0);
ALOGD("ambientRatio = %.2f", propertyAmbientRatio);
return;
- } else if (!strcmp(name, "lightDiameter")) {
- propertyLightDiameter = fmin(fmax(atof(value), 0.0), 3000.0);
- ALOGD("lightDiameter = %.2f", propertyLightDiameter);
+ } else if (!strcmp(name, "lightRadius")) {
+ propertyLightRadius = fmin(fmax(atof(value), 0.0), 3000.0);
+ ALOGD("lightRadius = %.2f", propertyLightRadius);
return;
} else if (!strcmp(name, "lightPosY")) {
propertyLightPosY = fmin(fmax(atof(value), 0.0), 3000.0);
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 2e179af..8aea8ff 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -21,86 +21,43 @@
#define LOG_TAG "OpenGLRenderer"
#endif
-#include <vector>
-
-#include <GLES3/gl3.h>
-
-#include <utils/KeyedVector.h>
-#include <utils/Singleton.h>
-#include <utils/Vector.h>
-
-#include <cutils/compiler.h>
-
-#include "thread/TaskProcessor.h"
-#include "thread/TaskManager.h"
#include "AssetAtlas.h"
+#include "Dither.h"
#include "Extensions.h"
-#include "FontRenderer.h"
-#include "GammaFontRenderer.h"
-#include "TextureCache.h"
-#include "LayerCache.h"
-#include "RenderBufferCache.h"
+#include "FboCache.h"
#include "GradientCache.h"
+#include "LayerCache.h"
#include "PatchCache.h"
#include "ProgramCache.h"
#include "PathCache.h"
+#include "RenderBufferCache.h"
+#include "renderstate/PixelBufferState.h"
+#include "renderstate/TextureState.h"
+#include "ResourceCache.h"
#include "TessellationCache.h"
#include "TextDropShadowCache.h"
-#include "FboCache.h"
-#include "ResourceCache.h"
-#include "Stencil.h"
-#include "Dither.h"
+#include "TextureCache.h"
+#include "thread/TaskProcessor.h"
+#include "thread/TaskManager.h"
-namespace android {
-namespace uirenderer {
+#include <vector>
+#include <memory>
-///////////////////////////////////////////////////////////////////////////////
-// Globals
-///////////////////////////////////////////////////////////////////////////////
+#include <GLES3/gl3.h>
-// GL ES 2.0 defines that at least 16 texture units must be supported
-#define REQUIRED_TEXTURE_UNITS_COUNT 3
-
-// Maximum number of quads that pre-allocated meshes can draw
-static const uint32_t gMaxNumberOfQuads = 2048;
-
-// Generates simple and textured vertices
-#define FV(x, y, u, v) { x, y, u, v }
-
-// This array is never used directly but used as a memcpy source in the
-// OpenGLRenderer constructor
-static const TextureVertex gMeshVertices[] = {
- FV(0.0f, 0.0f, 0.0f, 0.0f),
- FV(1.0f, 0.0f, 1.0f, 0.0f),
- FV(0.0f, 1.0f, 0.0f, 1.0f),
- FV(1.0f, 1.0f, 1.0f, 1.0f)
-};
-static const GLsizei gMeshStride = sizeof(TextureVertex);
-static const GLsizei gVertexStride = sizeof(Vertex);
-static const GLsizei gAlphaVertexStride = sizeof(AlphaVertex);
-static const GLsizei gMeshTextureOffset = 2 * sizeof(float);
-static const GLsizei gVertexAlphaOffset = 2 * sizeof(float);
-static const GLsizei gVertexAAWidthOffset = 2 * sizeof(float);
-static const GLsizei gVertexAALengthOffset = 3 * sizeof(float);
-static const GLsizei gMeshCount = 4;
-
-// Must define as many texture units as specified by REQUIRED_TEXTURE_UNITS_COUNT
-static const GLenum gTextureUnits[] = {
- GL_TEXTURE0,
- GL_TEXTURE1,
- GL_TEXTURE2
-};
+#include <utils/KeyedVector.h>
+#include <utils/Singleton.h>
+#include <utils/Vector.h>
-///////////////////////////////////////////////////////////////////////////////
-// Debug
-///////////////////////////////////////////////////////////////////////////////
+#include <cutils/compiler.h>
-struct CacheLogger {
- CacheLogger() {
- INIT_LOGD("Creating OpenGL renderer caches");
- }
-}; // struct CacheLogger
+#include <SkPath.h>
+
+namespace android {
+namespace uirenderer {
+
+class GammaFontRenderer;
///////////////////////////////////////////////////////////////////////////////
// Caches
@@ -109,12 +66,25 @@ struct CacheLogger {
class RenderNode;
class RenderState;
-class ANDROID_API Caches: public Singleton<Caches> {
- Caches();
+class ANDROID_API Caches {
+public:
+ static Caches& createInstance(RenderState& renderState) {
+ LOG_ALWAYS_FATAL_IF(sInstance, "double create of Caches attempted");
+ sInstance = new Caches(renderState);
+ return *sInstance;
+ }
- friend class Singleton<Caches>;
+ static Caches& getInstance() {
+ LOG_ALWAYS_FATAL_IF(!sInstance, "instance not yet created");
+ return *sInstance;
+ }
- CacheLogger mLogger;
+ static bool hasInstance() {
+ return sInstance != 0;
+ }
+private:
+ Caches(RenderState& renderState);
+ static Caches* sInstance;
public:
enum FlushMode {
@@ -133,8 +103,6 @@ public:
*/
bool initProperties();
- void setRenderState(RenderState* renderState) { mRenderState = renderState; }
-
/**
* Flush the cache.
*
@@ -173,116 +141,6 @@ public:
*/
void deleteLayerDeferred(Layer* layer);
- /**
- * Binds the VBO used to render simple textured quads.
- */
- bool bindMeshBuffer();
-
- /**
- * Binds the specified VBO if needed.
- */
- bool bindMeshBuffer(const GLuint buffer);
-
- /**
- * Unbinds the VBO used to render simple textured quads.
- */
- bool unbindMeshBuffer();
-
- /**
- * Binds a global indices buffer that can draw up to
- * gMaxNumberOfQuads quads.
- */
- bool bindQuadIndicesBuffer();
- bool bindShadowIndicesBuffer();
- bool unbindIndicesBuffer();
-
- /**
- * Binds the specified buffer as the current GL unpack pixel buffer.
- */
- bool bindPixelBuffer(const GLuint buffer);
-
- /**
- * Resets the current unpack pixel buffer to 0 (default value.)
- */
- bool unbindPixelBuffer();
-
- /**
- * Binds an attrib to the specified float vertex pointer.
- * Assumes a stride of gMeshStride and a size of 2.
- */
- void bindPositionVertexPointer(bool force, const GLvoid* vertices, GLsizei stride = gMeshStride);
-
- /**
- * Binds an attrib to the specified float vertex pointer.
- * Assumes a stride of gMeshStride and a size of 2.
- */
- void bindTexCoordsVertexPointer(bool force, const GLvoid* vertices, GLsizei stride = gMeshStride);
-
- /**
- * Resets the vertex pointers.
- */
- void resetVertexPointers();
- void resetTexCoordsVertexPointer();
-
- void enableTexCoordsVertexArray();
- void disableTexCoordsVertexArray();
-
- /**
- * Activate the specified texture unit. The texture unit must
- * be specified using an integer number (0 for GL_TEXTURE0 etc.)
- */
- void activeTexture(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);
-
- /**
- * Sets the scissor for the current surface.
- */
- bool setScissor(GLint x, GLint y, GLint width, GLint height);
-
- /**
- * Resets the scissor state.
- */
- void resetScissor();
-
- bool enableScissor();
- bool disableScissor();
- void setScissorEnabled(bool enabled);
void startTiling(GLuint x, GLuint y, GLuint width, GLuint height, bool discard);
void endTiling();
@@ -304,18 +162,9 @@ public:
void registerFunctors(uint32_t functorCount);
void unregisterFunctors(uint32_t functorCount);
- bool blend;
- GLenum lastSrcMode;
- GLenum lastDstMode;
- Program* currentProgram;
- bool scissorEnabled;
-
bool drawDeferDisabled;
bool drawReorderDisabled;
- // VBO to draw with
- GLuint meshBuffer;
-
// Misc
GLint maxTextureSize;
@@ -329,14 +178,18 @@ public:
kStencilShowRegion
};
StencilClipDebug debugStencilClip;
-
+private:
+ // Declared before gradientCache and programCache which need this to initialize.
+ // TODO: cleanup / move elsewhere
+ Extensions mExtensions;
+public:
TextureCache textureCache;
LayerCache layerCache;
RenderBufferCache renderBufferCache;
GradientCache gradientCache;
- ProgramCache programCache;
- PathCache pathCache;
PatchCache patchCache;
+ PathCache pathCache;
+ ProgramCache programCache;
TessellationCache tessellationCache;
TextDropShadowCache dropShadowCache;
FboCache fboCache;
@@ -346,7 +199,6 @@ public:
TaskManager tasks;
Dither dither;
- Stencil stencil;
bool gpuPixelBuffersEnabled;
@@ -355,20 +207,25 @@ public:
PFNGLPUSHGROUPMARKEREXTPROC startMark;
PFNGLPOPGROUPMARKEREXTPROC endMark;
- PFNGLLABELOBJECTEXTPROC setLabel;
- PFNGLGETOBJECTLABELEXTPROC getLabel;
-
// TEMPORARY properties
void initTempProperties();
void setTempProperty(const char* name, const char* value);
- float propertyLightDiameter;
+ float propertyLightRadius;
float propertyLightPosY;
float propertyLightPosZ;
float propertyAmbientRatio;
int propertyAmbientShadowStrength;
int propertySpotShadowStrength;
+ void setProgram(const ProgramDescription& description);
+ void setProgram(Program* program);
+
+ Extensions& extensions() { return mExtensions; }
+ Program& program() { return *mProgram; }
+ PixelBufferState& pixelBufferState() { return *mPixelBufferState; }
+ TextureState& textureState() { return *mTextureState; }
+
private:
enum OverdrawColorSet {
kColorSet_Default = 0,
@@ -380,45 +237,14 @@ private:
void initConstraints();
void initStaticProperties();
- bool bindIndicesBufferInternal(const GLuint buffer);
-
static void eventMarkNull(GLsizei length, const GLchar* marker) { }
static void startMarkNull(GLsizei length, const GLchar* marker) { }
static void endMarkNull() { }
- static void setLabelNull(GLenum type, uint object, GLsizei length,
- const char* label) { }
- static void getLabelNull(GLenum type, uint object, GLsizei bufferSize,
- GLsizei* length, char* label) {
- if (length) *length = 0;
- if (label) *label = '\0';
- }
-
- GLuint mCurrentBuffer;
- GLuint mCurrentIndicesBuffer;
- GLuint mCurrentPixelBuffer;
- const void* mCurrentPositionPointer;
- GLsizei mCurrentPositionStride;
- const void* mCurrentTexCoordsPointer;
- GLsizei mCurrentTexCoordsStride;
-
- bool mTexCoordsArrayEnabled;
-
- GLuint mTextureUnit;
-
- GLint mScissorX;
- GLint mScissorY;
- GLint mScissorWidth;
- GLint mScissorHeight;
-
- Extensions& mExtensions;
+ RenderState* mRenderState;
// Used to render layers
- TextureVertex* mRegionMesh;
-
- // Global index buffer
- GLuint mMeshIndices;
- GLuint mShadowStripsIndices;
+ std::unique_ptr<TextureVertex[]> mRegionMesh;
mutable Mutex mGarbageLock;
Vector<Layer*> mLayerGarbage;
@@ -428,12 +254,13 @@ private:
uint32_t mFunctorsCount;
- // Caches texture bindings for the GL_TEXTURE_2D target
- GLuint mBoundTextures[REQUIRED_TEXTURE_UNITS_COUNT];
-
OverdrawColorSet mOverdrawDebugColorSet;
- RenderState* mRenderState;
+ // TODO: move below to RenderState
+ PixelBufferState* mPixelBufferState = nullptr;
+ TextureState* mTextureState = nullptr;
+ Program* mProgram = nullptr; // note: object owned by ProgramCache
+
}; // class Caches
}; // namespace uirenderer
diff --git a/libs/hwui/Canvas.h b/libs/hwui/Canvas.h
new file mode 100644
index 0000000..aa24673
--- /dev/null
+++ b/libs/hwui/Canvas.h
@@ -0,0 +1,164 @@
+/*
+ * 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 ANDROID_GRAPHICS_CANVAS_H
+#define ANDROID_GRAPHICS_CANVAS_H
+
+#include <cutils/compiler.h>
+
+#include <SkBitmap.h>
+#include <SkCanvas.h>
+#include <SkMatrix.h>
+
+namespace android {
+
+class ANDROID_API Canvas {
+public:
+ virtual ~Canvas() {};
+
+ static Canvas* create_canvas(const SkBitmap& bitmap);
+
+ /**
+ * Create a new Canvas object which delegates to an SkCanvas.
+ *
+ * @param skiaCanvas Must not be NULL. All drawing calls will be
+ * delegated to this object. This function will call ref() on the
+ * SkCanvas, and the returned Canvas will unref() it upon
+ * destruction.
+ * @return new Canvas object. Will not return NULL.
+ */
+ static Canvas* create_canvas(SkCanvas* skiaCanvas);
+
+ /**
+ * Provides a Skia SkCanvas interface that acts as a proxy to this Canvas.
+ * It is useful for testing and clients (e.g. Picture/Movie) that expect to
+ * draw their contents into an SkCanvas.
+ *
+ * Further, the returned SkCanvas should NOT be unref'd and is valid until
+ * this canvas is destroyed or a new bitmap is set.
+ */
+ virtual SkCanvas* asSkCanvas() = 0;
+
+ virtual void setBitmap(const SkBitmap& bitmap) = 0;
+
+ virtual bool isOpaque() = 0;
+ virtual int width() = 0;
+ virtual int height() = 0;
+
+// ----------------------------------------------------------------------------
+// Canvas state operations
+// ----------------------------------------------------------------------------
+ // Save (layer)
+ virtual int getSaveCount() const = 0;
+ virtual int save(SkCanvas::SaveFlags flags) = 0;
+ virtual void restore() = 0;
+ virtual void restoreToCount(int saveCount) = 0;
+
+ virtual int saveLayer(float left, float top, float right, float bottom,
+ const SkPaint* paint, SkCanvas::SaveFlags flags) = 0;
+ virtual int saveLayerAlpha(float left, float top, float right, float bottom,
+ int alpha, SkCanvas::SaveFlags flags) = 0;
+
+ // Matrix
+ virtual void getMatrix(SkMatrix* outMatrix) const = 0;
+ virtual void setMatrix(const SkMatrix& matrix) = 0;
+
+ virtual void concat(const SkMatrix& matrix) = 0;
+ virtual void rotate(float degrees) = 0;
+ virtual void scale(float sx, float sy) = 0;
+ virtual void skew(float sx, float sy) = 0;
+ virtual void translate(float dx, float dy) = 0;
+
+ // clip
+ virtual bool getClipBounds(SkRect* outRect) const = 0;
+ virtual bool quickRejectRect(float left, float top, float right, float bottom) const = 0;
+ virtual bool quickRejectPath(const SkPath& path) const = 0;
+
+ virtual bool clipRect(float left, float top, float right, float bottom,
+ SkRegion::Op op = SkRegion::kIntersect_Op) = 0;
+ virtual bool clipPath(const SkPath* path, SkRegion::Op op) = 0;
+ virtual bool clipRegion(const SkRegion* region, SkRegion::Op op) = 0;
+
+ // filters
+ virtual SkDrawFilter* getDrawFilter() = 0;
+ virtual void setDrawFilter(SkDrawFilter* drawFilter) = 0;
+
+// ----------------------------------------------------------------------------
+// Canvas draw operations
+// ----------------------------------------------------------------------------
+ virtual void drawColor(int color, SkXfermode::Mode mode) = 0;
+ virtual void drawPaint(const SkPaint& paint) = 0;
+
+ // Geometry
+ virtual void drawPoint(float x, float y, const SkPaint& paint) = 0;
+ virtual void drawPoints(const float* points, int count, const SkPaint& paint) = 0;
+ virtual void drawLine(float startX, float startY, float stopX, float stopY,
+ const SkPaint& paint) = 0;
+ virtual void drawLines(const float* points, int count, const SkPaint& paint) = 0;
+ virtual void drawRect(float left, float top, float right, float bottom,
+ const SkPaint& paint) = 0;
+ virtual void drawRoundRect(float left, float top, float right, float bottom,
+ float rx, float ry, const SkPaint& paint) = 0;
+ virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) = 0;
+ virtual void drawOval(float left, float top, float right, float bottom,
+ const SkPaint& paint) = 0;
+ virtual void drawArc(float left, float top, float right, float bottom,
+ float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) = 0;
+ virtual void drawPath(const SkPath& path, const SkPaint& paint) = 0;
+ virtual void drawVertices(SkCanvas::VertexMode vertexMode, int vertexCount,
+ const float* verts, const float* tex, const int* colors,
+ const uint16_t* indices, int indexCount, const SkPaint& paint) = 0;
+
+ // Bitmap-based
+ virtual void drawBitmap(const SkBitmap& bitmap, float left, float top,
+ const SkPaint* paint) = 0;
+ virtual void drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
+ const SkPaint* paint) = 0;
+ virtual void drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
+ float srcRight, float srcBottom, float dstLeft, float dstTop,
+ float dstRight, float dstBottom, const SkPaint* paint) = 0;
+ virtual void drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
+ const float* vertices, const int* colors, const SkPaint* paint) = 0;
+
+ // Text
+ /**
+ * drawText: count is of glyphs
+ * totalAdvance is ignored in software renderering, used by hardware renderer for
+ * text decorations (underlines, strikethroughs).
+ */
+ virtual void drawText(const uint16_t* glyphs, const float* positions, int count,
+ const SkPaint& paint, float x, float y,
+ float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
+ float totalAdvance) = 0;
+ /** drawPosText: count is of UTF16 characters, posCount is floats (2 * glyphs) */
+ virtual void drawPosText(const uint16_t* text, const float* positions, int count,
+ int posCount, const SkPaint& paint) = 0;
+ /** drawTextOnPath: count is of glyphs */
+ virtual void drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path,
+ float hOffset, float vOffset, const SkPaint& paint) = 0;
+
+ /**
+ * Specifies if the positions passed to ::drawText are absolute or relative
+ * to the (x,y) value provided.
+ *
+ * If true the (x,y) values are ignored. Otherwise, those (x,y) values need
+ * to be added to each glyph's position to get its absolute position.
+ */
+ virtual bool drawTextAbsolutePos() const = 0;
+};
+
+}; // namespace android
+#endif // ANDROID_GRAPHICS_CANVAS_H
diff --git a/libs/hwui/StatefulBaseRenderer.cpp b/libs/hwui/CanvasState.cpp
index 88d6f68..e88e9f6 100644
--- a/libs/hwui/StatefulBaseRenderer.cpp
+++ b/libs/hwui/CanvasState.cpp
@@ -14,41 +14,45 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
-
#include <SkCanvas.h>
-#include "StatefulBaseRenderer.h"
-
+#include "CanvasState.h"
#include "utils/MathUtils.h"
namespace android {
namespace uirenderer {
-StatefulBaseRenderer::StatefulBaseRenderer()
+
+CanvasState::CanvasState(CanvasStateClient& renderer)
: mDirtyClip(false)
, mWidth(-1)
, mHeight(-1)
, mSaveCount(1)
, mFirstSnapshot(new Snapshot)
+ , mCanvas(renderer)
, mSnapshot(mFirstSnapshot) {
+
}
-void StatefulBaseRenderer::initializeSaveStack(float clipLeft, float clipTop,
+CanvasState::~CanvasState() {
+
+}
+
+void CanvasState::initializeSaveStack(float clipLeft, float clipTop,
float clipRight, float clipBottom, const Vector3& lightCenter) {
mSnapshot = new Snapshot(mFirstSnapshot,
SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
mSnapshot->setClip(clipLeft, clipTop, clipRight, clipBottom);
- mSnapshot->fbo = getTargetFbo();
+ mSnapshot->fbo = mCanvas.getTargetFbo();
mSnapshot->setRelativeLightCenter(lightCenter);
mSaveCount = 1;
}
-void StatefulBaseRenderer::setViewport(int width, int height) {
+void CanvasState::setViewport(int width, int height) {
mWidth = width;
mHeight = height;
mFirstSnapshot->initializeViewport(width, height);
- onViewportInitialized();
+ mCanvas.onViewportInitialized();
// create a temporary 1st snapshot, so old snapshots are released,
// and viewport can be queried safely.
@@ -63,24 +67,24 @@ void StatefulBaseRenderer::setViewport(int width, int height) {
///////////////////////////////////////////////////////////////////////////////
/**
- * Non-virtual implementation of save, guaranteed to save without side-effects
+ * Guaranteed to save without side-effects
*
- * The approach here and in restoreSnapshot(), allows subclasses to directly manipulate the save
+ * This approach, here and in restoreSnapshot(), allows subclasses to directly manipulate the save
* stack, and ensures restoreToCount() doesn't call back into subclass overrides.
*/
-int StatefulBaseRenderer::saveSnapshot(int flags) {
+int CanvasState::saveSnapshot(int flags) {
mSnapshot = new Snapshot(mSnapshot, flags);
return mSaveCount++;
}
-int StatefulBaseRenderer::save(int flags) {
+int CanvasState::save(int flags) {
return saveSnapshot(flags);
}
/**
- * Non-virtual implementation of restore, guaranteed to restore without side-effects.
+ * Guaranteed to restore without side-effects.
*/
-void StatefulBaseRenderer::restoreSnapshot() {
+void CanvasState::restoreSnapshot() {
sp<Snapshot> toRemove = mSnapshot;
sp<Snapshot> toRestore = mSnapshot->previous;
@@ -88,16 +92,16 @@ void StatefulBaseRenderer::restoreSnapshot() {
mSnapshot = toRestore;
// subclass handles restore implementation
- onSnapshotRestored(*toRemove, *toRestore);
+ mCanvas.onSnapshotRestored(*toRemove, *toRestore);
}
-void StatefulBaseRenderer::restore() {
+void CanvasState::restore() {
if (mSaveCount > 1) {
restoreSnapshot();
}
}
-void StatefulBaseRenderer::restoreToCount(int saveCount) {
+void CanvasState::restoreToCount(int saveCount) {
if (saveCount < 1) saveCount = 1;
while (mSaveCount > saveCount) {
@@ -109,40 +113,40 @@ void StatefulBaseRenderer::restoreToCount(int saveCount) {
// Matrix
///////////////////////////////////////////////////////////////////////////////
-void StatefulBaseRenderer::getMatrix(SkMatrix* matrix) const {
+void CanvasState::getMatrix(SkMatrix* matrix) const {
mSnapshot->transform->copyTo(*matrix);
}
-void StatefulBaseRenderer::translate(float dx, float dy, float dz) {
+void CanvasState::translate(float dx, float dy, float dz) {
mSnapshot->transform->translate(dx, dy, dz);
}
-void StatefulBaseRenderer::rotate(float degrees) {
+void CanvasState::rotate(float degrees) {
mSnapshot->transform->rotate(degrees, 0.0f, 0.0f, 1.0f);
}
-void StatefulBaseRenderer::scale(float sx, float sy) {
+void CanvasState::scale(float sx, float sy) {
mSnapshot->transform->scale(sx, sy, 1.0f);
}
-void StatefulBaseRenderer::skew(float sx, float sy) {
+void CanvasState::skew(float sx, float sy) {
mSnapshot->transform->skew(sx, sy);
}
-void StatefulBaseRenderer::setMatrix(const SkMatrix& matrix) {
+void CanvasState::setMatrix(const SkMatrix& matrix) {
mSnapshot->transform->load(matrix);
}
-void StatefulBaseRenderer::setMatrix(const Matrix4& matrix) {
+void CanvasState::setMatrix(const Matrix4& matrix) {
mSnapshot->transform->load(matrix);
}
-void StatefulBaseRenderer::concatMatrix(const SkMatrix& matrix) {
+void CanvasState::concatMatrix(const SkMatrix& matrix) {
mat4 transform(matrix);
mSnapshot->transform->multiply(transform);
}
-void StatefulBaseRenderer::concatMatrix(const Matrix4& matrix) {
+void CanvasState::concatMatrix(const Matrix4& matrix) {
mSnapshot->transform->multiply(matrix);
}
@@ -150,51 +154,22 @@ void StatefulBaseRenderer::concatMatrix(const Matrix4& matrix) {
// Clip
///////////////////////////////////////////////////////////////////////////////
-bool StatefulBaseRenderer::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
- if (CC_LIKELY(currentTransform()->rectToRect())) {
- mDirtyClip |= mSnapshot->clip(left, top, right, bottom, op);
- return !mSnapshot->clipRect->isEmpty();
- }
-
- SkPath path;
- path.addRect(left, top, right, bottom);
-
- return StatefulBaseRenderer::clipPath(&path, op);
+bool CanvasState::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
+ mDirtyClip |= mSnapshot->clip(left, top, right, bottom, op);
+ return !mSnapshot->clipIsEmpty();
}
-bool StatefulBaseRenderer::clipPath(const SkPath* path, SkRegion::Op op) {
- SkMatrix transform;
- currentTransform()->copyTo(transform);
-
- SkPath transformed;
- path->transform(transform, &transformed);
-
- SkRegion clip;
- if (!mSnapshot->previous->clipRegion->isEmpty()) {
- clip.setRegion(*mSnapshot->previous->clipRegion);
- } else {
- if (mSnapshot->previous == firstSnapshot()) {
- clip.setRect(0, 0, getWidth(), getHeight());
- } else {
- Rect* bounds = mSnapshot->previous->clipRect;
- clip.setRect(bounds->left, bounds->top, bounds->right, bounds->bottom);
- }
- }
-
- SkRegion region;
- region.setPath(transformed, clip);
-
- // region is the transformed input path, masked by the previous clip
- mDirtyClip |= mSnapshot->clipRegionTransformed(region, op);
- return !mSnapshot->clipRect->isEmpty();
+bool CanvasState::clipPath(const SkPath* path, SkRegion::Op op) {
+ mDirtyClip |= mSnapshot->clipPath(*path, op);
+ return !mSnapshot->clipIsEmpty();
}
-bool StatefulBaseRenderer::clipRegion(const SkRegion* region, SkRegion::Op op) {
+bool CanvasState::clipRegion(const SkRegion* region, SkRegion::Op op) {
mDirtyClip |= mSnapshot->clipRegionTransformed(*region, op);
- return !mSnapshot->clipRect->isEmpty();
+ return !mSnapshot->clipIsEmpty();
}
-void StatefulBaseRenderer::setClippingOutline(LinearAllocator& allocator, const Outline* outline) {
+void CanvasState::setClippingOutline(LinearAllocator& allocator, const Outline* outline) {
Rect bounds;
float radius;
if (!outline->getAsRoundRect(&bounds, &radius)) return; // only RR supported
@@ -209,7 +184,7 @@ void StatefulBaseRenderer::setClippingOutline(LinearAllocator& allocator, const
}
}
-void StatefulBaseRenderer::setClippingRoundRect(LinearAllocator& allocator,
+void CanvasState::setClippingRoundRect(LinearAllocator& allocator,
const Rect& rect, float radius, bool highPriority) {
mSnapshot->setClippingRoundRect(allocator, rect, radius, highPriority);
}
@@ -229,7 +204,7 @@ void StatefulBaseRenderer::setClippingRoundRect(LinearAllocator& allocator,
* @param snapOut if set, the geometry will be treated as having an AA ramp.
* See Rect::snapGeometryToPixelBoundaries()
*/
-bool StatefulBaseRenderer::calculateQuickRejectForScissor(float left, float top,
+bool CanvasState::calculateQuickRejectForScissor(float left, float top,
float right, float bottom,
bool* clipRequired, bool* roundRectClipRequired,
bool snapOut) const {
@@ -241,7 +216,7 @@ bool StatefulBaseRenderer::calculateQuickRejectForScissor(float left, float top,
currentTransform()->mapRect(r);
r.snapGeometryToPixelBoundaries(snapOut);
- Rect clipRect(*currentClipRect());
+ Rect clipRect(currentClipRect());
clipRect.snapToPixelBoundaries();
if (!clipRect.intersects(r)) return true;
@@ -253,24 +228,13 @@ bool StatefulBaseRenderer::calculateQuickRejectForScissor(float left, float top,
// round rect clip is required if RR clip exists, and geometry intersects its corners
if (roundRectClipRequired) {
- *roundRectClipRequired = mSnapshot->roundRectClipState != NULL
+ *roundRectClipRequired = mSnapshot->roundRectClipState != nullptr
&& mSnapshot->roundRectClipState->areaRequiresRoundRectClip(r);
}
return false;
}
-/**
- * Returns false if drawing won't be clipped out.
- *
- * Makes the decision conservatively, by rounding out the mapped rect before comparing with the
- * clipRect. To be used when perfect, pixel accuracy is not possible (esp. with tessellation) but
- * rejection is still desired.
- *
- * This function, unlike quickRejectSetupScissor, should be used where precise geometry information
- * isn't known (esp. when geometry adjusts based on scale). Generally, this will be first pass
- * rejection where precise rejection isn't important, or precise information isn't available.
- */
-bool StatefulBaseRenderer::quickRejectConservative(float left, float top,
+bool CanvasState::quickRejectConservative(float left, float top,
float right, float bottom) const {
if (mSnapshot->isIgnored() || bottom <= top || right <= left) {
return true;
@@ -280,7 +244,7 @@ bool StatefulBaseRenderer::quickRejectConservative(float left, float top,
currentTransform()->mapRect(r);
r.roundOut(); // rounded out to be conservative
- Rect clipRect(*currentClipRect());
+ Rect clipRect(currentClipRect());
clipRect.snapToPixelBoundaries();
if (!clipRect.intersects(r)) return true;
@@ -288,5 +252,5 @@ bool StatefulBaseRenderer::quickRejectConservative(float left, float top,
return false;
}
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/CanvasState.h b/libs/hwui/CanvasState.h
new file mode 100644
index 0000000..8e4a4d3
--- /dev/null
+++ b/libs/hwui/CanvasState.h
@@ -0,0 +1,193 @@
+/*
+ * 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 ANDROID_HWUI_CANVAS_STATE_H
+#define ANDROID_HWUI_CANVAS_STATE_H
+
+#include <SkMatrix.h>
+#include <SkPath.h>
+#include <SkRegion.h>
+
+#include "Snapshot.h"
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * Abstract base class for any class containing CanvasState.
+ * Defines three mandatory callbacks.
+ */
+class CanvasStateClient {
+public:
+ CanvasStateClient() { }
+ virtual ~CanvasStateClient() { }
+
+ /**
+ * Callback allowing embedder to take actions in the middle of a
+ * setViewport() call.
+ */
+ virtual void onViewportInitialized() = 0;
+
+ /**
+ * Callback allowing embedder to take actions in the middle of a
+ * restore() call. May be called several times sequentially.
+ */
+ virtual void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) = 0;
+
+ /**
+ * Allows subclasses to control what value is stored in snapshot's
+ * fbo field in * initializeSaveStack.
+ */
+ virtual GLuint getTargetFbo() const = 0;
+
+}; // class CanvasStateClient
+
+/**
+ * Implements Canvas state methods on behalf of Renderers.
+ *
+ * Manages the Snapshot stack, implementing matrix, save/restore, and clipping methods in the
+ * Renderer interface. Drawing and recording classes that include a CanvasState will have
+ * different use cases:
+ *
+ * Drawing code maintaining canvas state (i.e. OpenGLRenderer) can query attributes (such as
+ * transform) or hook into changes (e.g. save/restore) with minimal surface area for manipulating
+ * the stack itself.
+ *
+ * Recording code maintaining canvas state (i.e. DisplayListCanvas) can both record and pass
+ * through state operations to CanvasState, so that not only will querying operations work
+ * (getClip/Matrix), but so that quickRejection can also be used.
+ */
+
+class ANDROID_API CanvasState {
+public:
+ CanvasState(CanvasStateClient& renderer);
+ ~CanvasState();
+
+ /**
+ * Initializes the first snapshot, computing the projection matrix,
+ * and stores the dimensions of the render target.
+ */
+ void initializeSaveStack(float clipLeft, float clipTop, float clipRight, float clipBottom,
+ const Vector3& lightCenter);
+
+ void setViewport(int width, int height);
+
+ bool hasRectToRectTransform() const {
+ return CC_LIKELY(currentTransform()->rectToRect());
+ }
+
+ // Save (layer)
+ int getSaveCount() const { return mSaveCount; }
+ int save(int flags);
+ void restore();
+ void restoreToCount(int saveCount);
+
+ // Save/Restore without side-effects
+ int saveSnapshot(int flags);
+ void restoreSnapshot();
+
+ // Matrix
+ void getMatrix(SkMatrix* outMatrix) const;
+ void translate(float dx, float dy, float dz = 0.0f);
+ void rotate(float degrees);
+ void scale(float sx, float sy);
+ void skew(float sx, float sy);
+
+ void setMatrix(const SkMatrix& matrix);
+ void setMatrix(const Matrix4& matrix); // internal only convenience method
+ void concatMatrix(const SkMatrix& matrix);
+ void concatMatrix(const Matrix4& matrix); // internal only convenience method
+
+ // Clip
+ const Rect& getLocalClipBounds() const { return mSnapshot->getLocalClip(); }
+ const Rect& getRenderTargetClipBounds() const { return mSnapshot->getRenderTargetClip(); }
+
+ bool quickRejectConservative(float left, float top, float right, float bottom) const;
+
+ bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
+ bool clipPath(const SkPath* path, SkRegion::Op op);
+ bool clipRegion(const SkRegion* region, SkRegion::Op op);
+
+ /**
+ * Sets a "clipping outline", which is independent from the regular clip.
+ * Currently only supports rectangles or rounded rectangles; passing in a
+ * more complicated outline fails silently. Replaces any previous clipping
+ * outline.
+ */
+ void setClippingOutline(LinearAllocator& allocator, const Outline* outline);
+ void setClippingRoundRect(LinearAllocator& allocator,
+ const Rect& rect, float radius, bool highPriority = true);
+
+ /**
+ * Returns true if drawing in the rectangle (left, top, right, bottom)
+ * will be clipped out. Is conservative: might return false when subpixel-
+ * perfect tests would return true.
+ */
+ bool calculateQuickRejectForScissor(float left, float top, float right, float bottom,
+ bool* clipRequired, bool* roundRectClipRequired, bool snapOut) const;
+
+ void setDirtyClip(bool opaque) { mDirtyClip = opaque; }
+ bool getDirtyClip() const { return mDirtyClip; }
+
+ void scaleAlpha(float alpha) { mSnapshot->alpha *= alpha; }
+ void setEmpty(bool value) { mSnapshot->empty = value; }
+ void setInvisible(bool value) { mSnapshot->invisible = value; }
+
+ inline const mat4* currentTransform() const { return currentSnapshot()->transform; }
+ inline const Rect& currentClipRect() const { return currentSnapshot()->getClipRect(); }
+ inline Region* currentRegion() const { return currentSnapshot()->region; }
+ inline int currentFlags() const { return currentSnapshot()->flags; }
+ const Vector3& currentLightCenter() const { return currentSnapshot()->getRelativeLightCenter(); }
+ inline bool currentlyIgnored() const { return currentSnapshot()->isIgnored(); }
+ int getViewportWidth() const { return currentSnapshot()->getViewportWidth(); }
+ int getViewportHeight() const { return currentSnapshot()->getViewportHeight(); }
+ int getWidth() { return mWidth; }
+ int getHeight() { return mHeight; }
+
+ inline const Snapshot* currentSnapshot() const {
+ return mSnapshot != nullptr ? mSnapshot.get() : mFirstSnapshot.get();
+ }
+ inline Snapshot* writableSnapshot() { return mSnapshot.get(); }
+ inline const Snapshot* firstSnapshot() const { return mFirstSnapshot.get(); }
+
+private:
+ /// No default constructor - must supply a CanvasStateClient (mCanvas).
+ CanvasState();
+
+ /// indicates that the clip has been changed since the last time it was consumed
+ bool mDirtyClip;
+
+ /// Dimensions of the drawing surface
+ int mWidth, mHeight;
+
+ /// Number of saved states
+ int mSaveCount;
+
+ /// Base state
+ sp<Snapshot> mFirstSnapshot;
+
+ /// Host providing callbacks
+ CanvasStateClient& mCanvas;
+
+ /// Current state
+ sp<Snapshot> mSnapshot;
+
+}; // class CanvasState
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_CANVAS_STATE_H
diff --git a/libs/hwui/ClipArea.cpp b/libs/hwui/ClipArea.cpp
new file mode 100644
index 0000000..eb520b4
--- /dev/null
+++ b/libs/hwui/ClipArea.cpp
@@ -0,0 +1,373 @@
+/*
+ * 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 "ClipArea.h"
+
+#include <SkPath.h>
+#include <limits>
+
+#include "Rect.h"
+
+namespace android {
+namespace uirenderer {
+
+static bool intersect(Rect& r, const Rect& r2) {
+ bool hasIntersection = r.intersect(r2);
+ if (!hasIntersection) {
+ r.setEmpty();
+ }
+ return hasIntersection;
+}
+
+static void handlePoint(Rect& transformedBounds, const Matrix4& transform, float x, float y) {
+ Vertex v;
+ v.x = x;
+ v.y = y;
+ transform.mapPoint(v.x, v.y);
+ transformedBounds.expandToCoverVertex(v.x, v.y);
+}
+
+Rect transformAndCalculateBounds(const Rect& r, const Matrix4& transform) {
+ const float kMinFloat = std::numeric_limits<float>::lowest();
+ const float kMaxFloat = std::numeric_limits<float>::max();
+ Rect transformedBounds = { kMaxFloat, kMaxFloat, kMinFloat, kMinFloat };
+ handlePoint(transformedBounds, transform, r.left, r.top);
+ handlePoint(transformedBounds, transform, r.right, r.top);
+ handlePoint(transformedBounds, transform, r.left, r.bottom);
+ handlePoint(transformedBounds, transform, r.right, r.bottom);
+ return transformedBounds;
+}
+
+/*
+ * TransformedRectangle
+ */
+
+TransformedRectangle::TransformedRectangle() {
+}
+
+TransformedRectangle::TransformedRectangle(const Rect& bounds,
+ const Matrix4& transform)
+ : mBounds(bounds)
+ , mTransform(transform) {
+}
+
+bool TransformedRectangle::canSimplyIntersectWith(
+ const TransformedRectangle& other) const {
+
+ return mTransform == other.mTransform;
+}
+
+bool TransformedRectangle::intersectWith(const TransformedRectangle& other) {
+ Rect translatedBounds(other.mBounds);
+ return intersect(mBounds, translatedBounds);
+}
+
+bool TransformedRectangle::isEmpty() const {
+ return mBounds.isEmpty();
+}
+
+/*
+ * RectangleList
+ */
+
+RectangleList::RectangleList()
+ : mTransformedRectanglesCount(0) {
+}
+
+bool RectangleList::isEmpty() const {
+ if (mTransformedRectanglesCount < 1) {
+ return true;
+ }
+
+ for (int i = 0; i < mTransformedRectanglesCount; i++) {
+ if (mTransformedRectangles[i].isEmpty()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+int RectangleList::getTransformedRectanglesCount() const {
+ return mTransformedRectanglesCount;
+}
+
+const TransformedRectangle& RectangleList::getTransformedRectangle(int i) const {
+ return mTransformedRectangles[i];
+}
+
+void RectangleList::setEmpty() {
+ mTransformedRectanglesCount = 0;
+}
+
+void RectangleList::set(const Rect& bounds, const Matrix4& transform) {
+ mTransformedRectanglesCount = 1;
+ mTransformedRectangles[0] = TransformedRectangle(bounds, transform);
+}
+
+bool RectangleList::intersectWith(const Rect& bounds,
+ const Matrix4& transform) {
+ TransformedRectangle newRectangle(bounds, transform);
+
+ // Try to find a rectangle with a compatible transformation
+ int index = 0;
+ for (; index < mTransformedRectanglesCount; index++) {
+ TransformedRectangle& tr(mTransformedRectangles[index]);
+ if (tr.canSimplyIntersectWith(newRectangle)) {
+ tr.intersectWith(newRectangle);
+ return true;
+ }
+ }
+
+ // Add it to the list if there is room
+ if (index < kMaxTransformedRectangles) {
+ mTransformedRectangles[index] = newRectangle;
+ mTransformedRectanglesCount += 1;
+ return true;
+ }
+
+ // This rectangle list is full
+ return false;
+}
+
+Rect RectangleList::calculateBounds() const {
+ Rect bounds;
+ for (int index = 0; index < mTransformedRectanglesCount; index++) {
+ const TransformedRectangle& tr(mTransformedRectangles[index]);
+ if (index == 0) {
+ bounds = tr.transformedBounds();
+ } else {
+ bounds.intersect(tr.transformedBounds());
+ }
+ }
+ return bounds;
+}
+
+static SkPath pathFromTransformedRectangle(const Rect& bounds,
+ const Matrix4& transform) {
+ SkPath rectPath;
+ SkPath rectPathTransformed;
+ rectPath.addRect(bounds.left, bounds.top, bounds.right, bounds.bottom);
+ SkMatrix skTransform;
+ transform.copyTo(skTransform);
+ rectPath.transform(skTransform, &rectPathTransformed);
+ return rectPathTransformed;
+}
+
+SkRegion RectangleList::convertToRegion(const SkRegion& clip) const {
+ SkRegion rectangleListAsRegion;
+ for (int index = 0; index < mTransformedRectanglesCount; index++) {
+ const TransformedRectangle& tr(mTransformedRectangles[index]);
+ SkPath rectPathTransformed = pathFromTransformedRectangle(
+ tr.getBounds(), tr.getTransform());
+ if (index == 0) {
+ rectangleListAsRegion.setPath(rectPathTransformed, clip);
+ } else {
+ SkRegion rectRegion;
+ rectRegion.setPath(rectPathTransformed, clip);
+ rectangleListAsRegion.op(rectRegion, SkRegion::kIntersect_Op);
+ }
+ }
+ return rectangleListAsRegion;
+}
+
+/*
+ * ClipArea
+ */
+
+ClipArea::ClipArea()
+ : mMode(kModeRectangle) {
+}
+
+/*
+ * Interface
+ */
+
+void ClipArea::setViewportDimensions(int width, int height) {
+ mViewportBounds.set(0, 0, width, height);
+ mClipRect = mViewportBounds;
+}
+
+void ClipArea::setEmpty() {
+ mMode = kModeRectangle;
+ mClipRect.setEmpty();
+ mClipRegion.setEmpty();
+ mRectangleList.setEmpty();
+}
+
+void ClipArea::setClip(float left, float top, float right, float bottom) {
+ mMode = kModeRectangle;
+ mClipRect.set(left, top, right, bottom);
+ mClipRegion.setEmpty();
+}
+
+bool ClipArea::clipRectWithTransform(float left, float top, float right,
+ float bottom, const mat4* transform, SkRegion::Op op) {
+ Rect r(left, top, right, bottom);
+ return clipRectWithTransform(r, transform, op);
+}
+
+bool ClipArea::clipRectWithTransform(const Rect& r, const mat4* transform,
+ SkRegion::Op op) {
+ switch (mMode) {
+ case kModeRectangle:
+ return rectangleModeClipRectWithTransform(r, transform, op);
+ case kModeRectangleList:
+ return rectangleListModeClipRectWithTransform(r, transform, op);
+ case kModeRegion:
+ return regionModeClipRectWithTransform(r, transform, op);
+ }
+ return false;
+}
+
+bool ClipArea::clipRegion(const SkRegion& region, SkRegion::Op op) {
+ enterRegionMode();
+ mClipRegion.op(region, op);
+ onClipRegionUpdated();
+ return true;
+}
+
+bool ClipArea::clipPathWithTransform(const SkPath& path, const mat4* transform,
+ SkRegion::Op op) {
+ SkMatrix skTransform;
+ transform->copyTo(skTransform);
+ SkPath transformed;
+ path.transform(skTransform, &transformed);
+ SkRegion region;
+ regionFromPath(transformed, region);
+ return clipRegion(region, op);
+}
+
+/*
+ * Rectangle mode
+ */
+
+void ClipArea::enterRectangleMode() {
+ // Entering rectangle mode discards any
+ // existing clipping information from the other modes.
+ // The only way this occurs is by a clip setting operation.
+ mMode = kModeRectangle;
+}
+
+bool ClipArea::rectangleModeClipRectWithTransform(const Rect& r,
+ const mat4* transform, SkRegion::Op op) {
+
+ // TODO: we should be able to handle kReplace_Op efficiently without
+ // going through RegionMode and later falling back into RectangleMode.
+
+ if (op != SkRegion::kIntersect_Op) {
+ enterRegionMode();
+ return regionModeClipRectWithTransform(r, transform, op);
+ }
+
+ if (transform->rectToRect()) {
+ Rect transformed(r);
+ transform->mapRect(transformed);
+ bool hasIntersection = mClipRect.intersect(transformed);
+ if (!hasIntersection) {
+ mClipRect.setEmpty();
+ }
+ return true;
+ }
+
+ enterRectangleListMode();
+ return rectangleListModeClipRectWithTransform(r, transform, op);
+}
+
+bool ClipArea::rectangleModeClipRectWithTransform(float left, float top,
+ float right, float bottom, const mat4* transform, SkRegion::Op op) {
+ Rect r(left, top, right, bottom);
+ bool result = rectangleModeClipRectWithTransform(r, transform, op);
+ mClipRect = mRectangleList.calculateBounds();
+ return result;
+}
+
+/*
+ * RectangleList mode implementation
+ */
+
+void ClipArea::enterRectangleListMode() {
+ // Is is only legal to enter rectangle list mode from
+ // rectangle mode, since rectangle list mode cannot represent
+ // all clip areas that can be represented by a region.
+ ALOG_ASSERT(mMode == kModeRectangle);
+ mMode = kModeRectangleList;
+ mRectangleList.set(mClipRect, Matrix4::identity());
+}
+
+bool ClipArea::rectangleListModeClipRectWithTransform(const Rect& r,
+ const mat4* transform, SkRegion::Op op) {
+ if (op != SkRegion::kIntersect_Op
+ || !mRectangleList.intersectWith(r, *transform)) {
+ enterRegionMode();
+ return regionModeClipRectWithTransform(r, transform, op);
+ }
+ return true;
+}
+
+bool ClipArea::rectangleListModeClipRectWithTransform(float left, float top,
+ float right, float bottom, const mat4* transform, SkRegion::Op op) {
+ Rect r(left, top, right, bottom);
+ return rectangleListModeClipRectWithTransform(r, transform, op);
+}
+
+/*
+ * Region mode implementation
+ */
+
+void ClipArea::enterRegionMode() {
+ Mode oldMode = mMode;
+ mMode = kModeRegion;
+ if (oldMode != kModeRegion) {
+ if (oldMode == kModeRectangle) {
+ mClipRegion.setRect(mClipRect.left, mClipRect.top,
+ mClipRect.right, mClipRect.bottom);
+ } else {
+ mClipRegion = mRectangleList.convertToRegion(createViewportRegion());
+ onClipRegionUpdated();
+ }
+ }
+}
+
+bool ClipArea::regionModeClipRectWithTransform(const Rect& r,
+ const mat4* transform, SkRegion::Op op) {
+ SkPath transformedRect = pathFromTransformedRectangle(r, *transform);
+ SkRegion transformedRectRegion;
+ regionFromPath(transformedRect, transformedRectRegion);
+ mClipRegion.op(transformedRectRegion, op);
+ onClipRegionUpdated();
+ return true;
+}
+
+bool ClipArea::regionModeClipRectWithTransform(float left, float top,
+ float right, float bottom, const mat4* transform, SkRegion::Op op) {
+ return regionModeClipRectWithTransform(Rect(left, top, right, bottom),
+ transform, op);
+}
+
+void ClipArea::onClipRegionUpdated() {
+ if (!mClipRegion.isEmpty()) {
+ mClipRect.set(mClipRegion.getBounds());
+
+ if (mClipRegion.isRect()) {
+ mClipRegion.setEmpty();
+ enterRectangleMode();
+ }
+ } else {
+ mClipRect.setEmpty();
+ }
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/ClipArea.h b/libs/hwui/ClipArea.h
new file mode 100644
index 0000000..e284af0
--- /dev/null
+++ b/libs/hwui/ClipArea.h
@@ -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.
+ */
+#ifndef CLIPAREA_H
+#define CLIPAREA_H
+
+#include <SkRegion.h>
+
+#include "Matrix.h"
+#include "Rect.h"
+#include "utils/Pair.h"
+
+namespace android {
+namespace uirenderer {
+
+Rect transformAndCalculateBounds(const Rect& r, const Matrix4& transform);
+
+class TransformedRectangle {
+public:
+ TransformedRectangle();
+ TransformedRectangle(const Rect& bounds, const Matrix4& transform);
+
+ bool canSimplyIntersectWith(const TransformedRectangle& other) const;
+ bool intersectWith(const TransformedRectangle& other);
+
+ bool isEmpty() const;
+
+ const Rect& getBounds() const {
+ return mBounds;
+ }
+
+ Rect transformedBounds() const {
+ Rect transformedBounds(transformAndCalculateBounds(mBounds, mTransform));
+ return transformedBounds;
+ }
+
+ const Matrix4& getTransform() const {
+ return mTransform;
+ }
+
+private:
+ Rect mBounds;
+ Matrix4 mTransform;
+};
+
+class RectangleList {
+public:
+ RectangleList();
+
+ bool isEmpty() const;
+ int getTransformedRectanglesCount() const;
+ const TransformedRectangle& getTransformedRectangle(int i) const;
+
+ void setEmpty();
+ void set(const Rect& bounds, const Matrix4& transform);
+ bool intersectWith(const Rect& bounds, const Matrix4& transform);
+
+ SkRegion convertToRegion(const SkRegion& clip) const;
+ Rect calculateBounds() const;
+
+private:
+ enum {
+ kMaxTransformedRectangles = 5
+ };
+
+ int mTransformedRectanglesCount;
+ TransformedRectangle mTransformedRectangles[kMaxTransformedRectangles];
+};
+
+class ClipArea {
+public:
+ ClipArea();
+
+ void setViewportDimensions(int width, int height);
+
+ bool isEmpty() const {
+ return mClipRect.isEmpty();
+ }
+
+ void setEmpty();
+ void setClip(float left, float top, float right, float bottom);
+ bool clipRectWithTransform(float left, float top, float right, float bottom,
+ const mat4* transform, SkRegion::Op op = SkRegion::kIntersect_Op);
+ bool clipRectWithTransform(const Rect& r, const mat4* transform,
+ SkRegion::Op op = SkRegion::kIntersect_Op);
+ bool clipRegion(const SkRegion& region, SkRegion::Op op = SkRegion::kIntersect_Op);
+ bool clipPathWithTransform(const SkPath& path, const mat4* transform,
+ SkRegion::Op op);
+
+ const Rect& getClipRect() const {
+ return mClipRect;
+ }
+
+ const SkRegion& getClipRegion() const {
+ return mClipRegion;
+ }
+
+ const RectangleList& getRectangleList() const {
+ return mRectangleList;
+ }
+
+ bool isRegion() const {
+ return kModeRegion == mMode;
+ }
+
+ bool isSimple() const {
+ return mMode == kModeRectangle;
+ }
+
+ bool isRectangleList() const {
+ return mMode == kModeRectangleList;
+ }
+
+private:
+ void enterRectangleMode();
+ bool rectangleModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op);
+ bool rectangleModeClipRectWithTransform(float left, float top, float right,
+ float bottom, const mat4* transform, SkRegion::Op op);
+
+ void enterRectangleListMode();
+ bool rectangleListModeClipRectWithTransform(float left, float top,
+ float right, float bottom, const mat4* transform, SkRegion::Op op);
+ bool rectangleListModeClipRectWithTransform(const Rect& r,
+ const mat4* transform, SkRegion::Op op);
+
+ void enterRegionModeFromRectangleMode();
+ void enterRegionModeFromRectangleListMode();
+ void enterRegionMode();
+ bool regionModeClipRectWithTransform(const Rect& r, const mat4* transform,
+ SkRegion::Op op);
+ bool regionModeClipRectWithTransform(float left, float top, float right,
+ float bottom, const mat4* transform, SkRegion::Op op);
+
+ void ensureClipRegion();
+ void onClipRegionUpdated();
+ bool clipRegionOp(float left, float top, float right, float bottom,
+ SkRegion::Op op);
+
+ SkRegion createViewportRegion() {
+ return SkRegion(mViewportBounds.toSkIRect());
+ }
+
+ void regionFromPath(const SkPath& path, SkRegion& pathAsRegion) {
+ pathAsRegion.setPath(path, createViewportRegion());
+ }
+
+ enum Mode {
+ kModeRectangle,
+ kModeRegion,
+ kModeRectangleList
+ };
+
+ Mode mMode;
+ Rect mViewportBounds;
+ Rect mClipRect;
+ SkRegion mClipRegion;
+ RectangleList mRectangleList;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* CLIPAREA_H_ */
diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp
index 420e331..9bd3bdc 100644
--- a/libs/hwui/DamageAccumulator.cpp
+++ b/libs/hwui/DamageAccumulator.cpp
@@ -79,7 +79,7 @@ void DamageAccumulator::computeCurrentTransform(Matrix4* outMatrix) const {
void DamageAccumulator::pushCommon() {
if (!mHead->next) {
DirtyStack* nextFrame = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack));
- nextFrame->next = 0;
+ nextFrame->next = nullptr;
nextFrame->prev = mHead;
mHead->next = nextFrame;
}
@@ -147,7 +147,7 @@ static DirtyStack* findParentRenderNode(DirtyStack* frame) {
return frame;
}
}
- return NULL;
+ return nullptr;
}
static DirtyStack* findProjectionReceiver(DirtyStack* frame) {
@@ -160,7 +160,7 @@ static DirtyStack* findProjectionReceiver(DirtyStack* frame) {
}
}
}
- return NULL;
+ return nullptr;
}
static void applyTransforms(DirtyStack* frame, DirtyStack* end) {
@@ -222,7 +222,7 @@ void DamageAccumulator::finish(SkRect* totalDirty) {
LOG_ALWAYS_FATAL_IF(mHead->prev != mHead, "Cannot finish, mismatched push/pop calls! %p vs. %p", mHead->prev, mHead);
// Root node never has a transform, so this is the fully mapped dirty rect
*totalDirty = mHead->pendingDirty;
- totalDirty->roundOut();
+ totalDirty->roundOut(totalDirty);
mHead->pendingDirty.setEmpty();
}
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index a998594..c78971a 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -52,7 +52,7 @@ namespace uirenderer {
class Batch {
public:
- virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) = 0;
+ virtual void replay(OpenGLRenderer& renderer, Rect& dirty, int index) = 0;
virtual ~Batch() {}
virtual bool purelyDrawBatch() { return false; }
virtual bool coversBounds(const Rect& bounds) { return false; }
@@ -91,12 +91,10 @@ public:
return false;
}
- virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) {
+ virtual void replay(OpenGLRenderer& renderer, Rect& dirty, int index) override {
DEFER_LOGD("%d replaying DrawBatch %p, with %d ops (batch id %x, merge id %p)",
index, this, mOps.size(), getBatchId(), getMergeId());
- status_t status = DrawGlInfo::kStatusDone;
- DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
for (unsigned int i = 0; i < mOps.size(); i++) {
DrawOp* op = mOps[i].op;
const DeferredDisplayState* state = mOps[i].state;
@@ -105,8 +103,7 @@ public:
#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
renderer.eventMark(op->name());
#endif
- logBuffer.writeCommand(0, op->name());
- status |= op->applyDraw(renderer, dirty);
+ op->applyDraw(renderer, dirty);
#if DEBUG_MERGE_BEHAVIOR
const Rect& bounds = state->mBounds;
@@ -118,12 +115,11 @@ public:
batchColor);
#endif
}
- return status;
}
- virtual bool purelyDrawBatch() { return true; }
+ virtual bool purelyDrawBatch() override { return true; }
- virtual bool coversBounds(const Rect& bounds) {
+ virtual bool coversBounds(const Rect& bounds) override {
if (CC_LIKELY(!mAllOpsOpaque || !mBounds.contains(bounds) || count() == 1)) return false;
Region uncovered(android::Rect(bounds.left, bounds.top, bounds.right, bounds.bottom));
@@ -235,30 +231,11 @@ public:
return false;
}
- /* Draw Modifiers compatibility check
- *
- * Shadows are ignored, as only text uses them, and in that case they are drawn
- * per-DrawTextOp, before the unified text draw. Because of this, it's always safe to merge
- * text UNLESS a later draw's shadow should overlays a previous draw's text. This is covered
- * above with the intersection check.
- *
- * OverrideLayerAlpha is also ignored, as it's only used for drawing layers, which are never
- * merged.
- *
- * These ignore cases prevent us from simply memcmp'ing the drawModifiers
- */
- const DrawModifiers& lhsMod = lhs->mDrawModifiers;
- const DrawModifiers& rhsMod = rhs->mDrawModifiers;
-
- // Draw filter testing expects bit fields to be clear if filter not set.
- if (lhsMod.mHasDrawFilter != rhsMod.mHasDrawFilter) return false;
- if (lhsMod.mPaintFilterClearBits != rhsMod.mPaintFilterClearBits) return false;
- if (lhsMod.mPaintFilterSetBits != rhsMod.mPaintFilterSetBits) return false;
-
return true;
}
- virtual void add(DrawOp* op, const DeferredDisplayState* state, bool opaqueOverBounds) {
+ virtual void add(DrawOp* op, const DeferredDisplayState* state,
+ bool opaqueOverBounds) override {
DrawBatch::add(op, state, opaqueOverBounds);
const int newClipSideFlags = state->mClipSideFlags;
@@ -269,33 +246,29 @@ public:
if (newClipSideFlags & kClipSide_Bottom) mClipRect.bottom = state->mClip.bottom;
}
- virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) {
+ virtual void replay(OpenGLRenderer& renderer, Rect& dirty, int index) override {
DEFER_LOGD("%d replaying MergingDrawBatch %p, with %d ops,"
" clip flags %x (batch id %x, merge id %p)",
index, this, mOps.size(), mClipSideFlags, getBatchId(), getMergeId());
if (mOps.size() == 1) {
- return DrawBatch::replay(renderer, dirty, -1);
+ DrawBatch::replay(renderer, dirty, -1);
+ return;
}
// clipping in the merged case is done ahead of time since all ops share the clip (if any)
- renderer.setupMergedMultiDraw(mClipSideFlags ? &mClipRect : NULL);
+ renderer.setupMergedMultiDraw(mClipSideFlags ? &mClipRect : nullptr);
DrawOp* op = mOps[0].op;
- DisplayListLogBuffer& buffer = DisplayListLogBuffer::getInstance();
- buffer.writeCommand(0, "multiDraw");
- buffer.writeCommand(1, op->name());
-
#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
renderer.eventMark("multiDraw");
renderer.eventMark(op->name());
#endif
- status_t status = op->multiDraw(renderer, dirty, mOps, mBounds);
+ op->multiDraw(renderer, dirty, mOps, mBounds);
#if DEBUG_MERGE_BEHAVIOR
renderer.drawScreenSpaceColorRect(mBounds.left, mBounds.top, mBounds.right, mBounds.bottom,
DEBUG_COLOR_MERGEDBATCH);
#endif
- return status;
}
private:
@@ -313,7 +286,7 @@ public:
// creates a single operation batch
StateOpBatch(const StateOp* op, const DeferredDisplayState* state) : mOp(op), mState(state) {}
- virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) {
+ virtual void replay(OpenGLRenderer& renderer, Rect& dirty, int index) override {
DEFER_LOGD("replaying state op batch %p", this);
renderer.restoreDisplayState(*mState);
@@ -322,7 +295,6 @@ public:
// renderer.restoreToCount directly
int saveCount = -1;
mOp->applyState(renderer, saveCount);
- return DrawGlInfo::kStatusDone;
}
private:
@@ -333,19 +305,17 @@ private:
class RestoreToCountBatch : public Batch {
public:
RestoreToCountBatch(const StateOp* op, const DeferredDisplayState* state, int restoreCount) :
- mOp(op), mState(state), mRestoreCount(restoreCount) {}
+ mState(state), mRestoreCount(restoreCount) {}
- virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) {
+ virtual void replay(OpenGLRenderer& renderer, Rect& dirty, int index) override {
DEFER_LOGD("batch %p restoring to count %d", this, mRestoreCount);
renderer.restoreDisplayState(*mState);
renderer.restoreToCount(mRestoreCount);
- return DrawGlInfo::kStatusDone;
}
private:
// we use the state storage for the RestoreToCountOp, but don't replay the op itself
- const StateOp* mOp;
const DeferredDisplayState* mState;
/*
@@ -359,9 +329,8 @@ private:
#if DEBUG_MERGE_BEHAVIOR
class BarrierDebugBatch : public Batch {
- virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) {
+ virtual void replay(OpenGLRenderer& renderer, Rect& dirty, int index) {
renderer.drawScreenSpaceColorRect(0, 0, 10000, 10000, DEBUG_COLOR_BARRIER);
- return DrawGlInfo::kStatusDrew;
}
};
#endif
@@ -372,7 +341,7 @@ class BarrierDebugBatch : public Batch {
void DeferredDisplayList::resetBatchingState() {
for (int i = 0; i < kOpBatch_Count; i++) {
- mBatchLookup[i] = NULL;
+ mBatchLookup[i] = nullptr;
mMergingBatches[i].clear();
}
#if DEBUG_MERGE_BEHAVIOR
@@ -542,7 +511,7 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
}
// find the latest batch of the new op's type, and try to merge the new op into it
- DrawBatch* targetBatch = NULL;
+ DrawBatch* targetBatch = nullptr;
// insertion point of a new batch, will hopefully be immediately after similar batch
// (eventually, should be similar shader)
@@ -565,7 +534,7 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
// Try to merge with any existing batch with same mergeId.
if (mMergingBatches[deferInfo.batchId].get(deferInfo.mergeId, targetBatch)) {
if (!((MergingDrawBatch*) targetBatch)->canMergeWith(op, state)) {
- targetBatch = NULL;
+ targetBatch = nullptr;
}
}
} else {
@@ -591,7 +560,7 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
// NOTE: it may be possible to optimize for special cases where two operations
// of the same batch/paint could swap order, such as with a non-mergeable
// (clipped) and a mergeable text operation
- targetBatch = NULL;
+ targetBatch = nullptr;
#if DEBUG_DEFER
DEFER_LOGD("op couldn't join batch %p, was intersected by batch %d",
targetBatch, i);
@@ -648,26 +617,22 @@ void DeferredDisplayList::storeRestoreToCountBarrier(OpenGLRenderer& renderer, S
// Replay / flush
/////////////////////////////////////////////////////////////////////////////////
-static status_t replayBatchList(const Vector<Batch*>& batchList,
+static void replayBatchList(const Vector<Batch*>& batchList,
OpenGLRenderer& renderer, Rect& dirty) {
- status_t status = DrawGlInfo::kStatusDone;
for (unsigned int i = 0; i < batchList.size(); i++) {
if (batchList[i]) {
- status |= batchList[i]->replay(renderer, dirty, i);
+ batchList[i]->replay(renderer, dirty, i);
}
}
DEFER_LOGD("--flushed, drew %d batches", batchList.size());
- return status;
}
-status_t DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty) {
+void DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty) {
ATRACE_NAME("flush drawing commands");
Caches::getInstance().fontRenderer->endPrecaching();
- status_t status = DrawGlInfo::kStatusDone;
-
- if (isEmpty()) return status; // nothing to flush
+ if (isEmpty()) return; // nothing to flush
renderer.restoreToCount(1);
DEFER_LOGD("--flushing");
@@ -686,23 +651,21 @@ status_t DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty) {
}
// NOTE: depth of the save stack at this point, before playback, should be reflected in
// FLUSH_SAVE_STACK_DEPTH, so that save/restores match up correctly
- status |= replayBatchList(mBatches, renderer, dirty);
+ replayBatchList(mBatches, renderer, dirty);
renderer.restoreToCount(1);
renderer.setDrawModifiers(restoreDrawModifiers);
DEFER_LOGD("--flush complete, returning %x", status);
clear();
- return status;
}
void DeferredDisplayList::discardDrawingBatches(const unsigned int maxIndex) {
for (unsigned int i = mEarliestUnclearedIndex; i <= maxIndex; i++) {
// leave deferred state ops alone for simplicity (empty save restore pairs may now exist)
if (mBatches[i] && mBatches[i]->purelyDrawBatch()) {
- DrawBatch* b = (DrawBatch*) mBatches[i];
delete mBatches[i];
- mBatches.replaceAt(NULL, i);
+ mBatches.replaceAt(nullptr, i);
}
}
mEarliestUnclearedIndex = maxIndex + 1;
diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h
index 8a015b2..f535afb 100644
--- a/libs/hwui/DeferredDisplayList.h
+++ b/libs/hwui/DeferredDisplayList.h
@@ -69,7 +69,7 @@ public:
class OpStatePair {
public:
OpStatePair()
- : op(NULL), state(NULL) {}
+ : op(nullptr), state(nullptr) {}
OpStatePair(DrawOp* newOp, const DeferredDisplayState* newState)
: op(newOp), state(newState) {}
OpStatePair(const OpStatePair& other)
@@ -79,7 +79,7 @@ public:
};
class DeferredDisplayList {
- friend class DeferStateStruct; // used to give access to allocator
+ friend struct DeferStateStruct; // used to give access to allocator
public:
DeferredDisplayList(const Rect& bounds, bool avoidOverdraw = true) :
mBounds(bounds), mAvoidOverdraw(avoidOverdraw) {
@@ -106,7 +106,7 @@ public:
* Plays back all of the draw ops recorded into batches to the renderer.
* Adjusts the state of the renderer as necessary, and restores it when complete
*/
- status_t flush(OpenGLRenderer& renderer, Rect& dirty);
+ void flush(OpenGLRenderer& renderer, Rect& dirty);
void addClip(OpenGLRenderer& renderer, ClipOp* op);
void addSaveLayer(OpenGLRenderer& renderer, SaveLayerOp* op, int newSaveCount);
@@ -127,7 +127,7 @@ private:
}
void tryRecycleState(DeferredDisplayState* state) {
- mAllocator.rewindIfLastAlloc(state, sizeof(DeferredDisplayState));
+ mAllocator.rewindIfLastAlloc(state);
}
/**
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index d02455c..6b8c780 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -25,8 +25,8 @@ namespace android {
namespace uirenderer {
DeferredLayerUpdater::DeferredLayerUpdater(renderthread::RenderThread& thread, Layer* layer)
- : mSurfaceTexture(0)
- , mTransform(0)
+ : mSurfaceTexture(nullptr)
+ , mTransform(nullptr)
, mNeedsGLContextAttach(false)
, mUpdateTexImage(false)
, mLayer(layer)
@@ -42,14 +42,14 @@ DeferredLayerUpdater::DeferredLayerUpdater(renderthread::RenderThread& thread, L
DeferredLayerUpdater::~DeferredLayerUpdater() {
SkSafeUnref(mColorFilter);
- setTransform(0);
+ setTransform(nullptr);
mLayer->postDecStrong();
- mLayer = 0;
+ mLayer = nullptr;
}
void DeferredLayerUpdater::setPaint(const SkPaint* paint) {
OpenGLRenderer::getAlphaAndModeDirect(paint, &mAlpha, &mMode);
- SkColorFilter* colorFilter = (paint) ? paint->getColorFilter() : NULL;
+ SkColorFilter* colorFilter = (paint) ? paint->getColorFilter() : nullptr;
SkRefCnt_SafeAssign(mColorFilter, colorFilter);
}
@@ -62,7 +62,7 @@ bool DeferredLayerUpdater::apply() {
if (mSurfaceTexture.get()) {
if (mNeedsGLContextAttach) {
mNeedsGLContextAttach = false;
- mSurfaceTexture->attachToContext(mLayer->getTexture());
+ mSurfaceTexture->attachToContext(mLayer->getTextureId());
}
if (mUpdateTexImage) {
mUpdateTexImage = false;
@@ -70,7 +70,7 @@ bool DeferredLayerUpdater::apply() {
}
if (mTransform) {
mLayer->getTransform().load(*mTransform);
- setTransform(0);
+ setTransform(nullptr);
}
}
return success;
@@ -95,10 +95,10 @@ void DeferredLayerUpdater::doUpdateTexImage() {
bool forceFilter = false;
sp<GraphicBuffer> buffer = mSurfaceTexture->getCurrentBuffer();
- if (buffer != NULL) {
+ if (buffer != nullptr) {
// force filtration if buffer size != layer size
- forceFilter = mWidth != buffer->getWidth()
- || mHeight != buffer->getHeight();
+ forceFilter = mWidth != static_cast<int>(buffer->getWidth())
+ || mHeight != static_cast<int>(buffer->getHeight());
}
#if DEBUG_RENDERER
@@ -109,6 +109,9 @@ void DeferredLayerUpdater::doUpdateTexImage() {
mSurfaceTexture->getTransformMatrix(transform);
GLenum renderTarget = mSurfaceTexture->getCurrentTextureTarget();
+ LOG_ALWAYS_FATAL_IF(renderTarget != GL_TEXTURE_2D && renderTarget != GL_TEXTURE_EXTERNAL_OES,
+ "doUpdateTexImage target %x, 2d %x, EXT %x",
+ renderTarget, GL_TEXTURE_2D, GL_TEXTURE_EXTERNAL_OES);
LayerRenderer::updateTextureLayer(mLayer, mWidth, mHeight,
!mBlend, forceFilter, renderTarget, transform);
}
@@ -122,7 +125,7 @@ void DeferredLayerUpdater::detachSurfaceTexture() {
// TODO: Elevate to fatal exception
ALOGE("Failed to detach SurfaceTexture from context %d", err);
}
- mSurfaceTexture = 0;
+ mSurfaceTexture = nullptr;
mLayer->clearTexture();
}
}
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index 84411ed..82f2741 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -24,7 +24,6 @@
#include "Layer.h"
#include "Rect.h"
-#include "RenderNode.h"
#include "renderthread/RenderThread.h"
namespace android {
@@ -39,7 +38,7 @@ public:
ANDROID_API DeferredLayerUpdater(renderthread::RenderThread& thread, Layer* layer);
ANDROID_API ~DeferredLayerUpdater();
- ANDROID_API bool setSize(uint32_t width, uint32_t height) {
+ ANDROID_API bool setSize(int width, int height) {
if (mWidth != width || mHeight != height) {
mWidth = width;
mHeight = height;
@@ -60,6 +59,10 @@ public:
if (texture.get() != mSurfaceTexture.get()) {
mNeedsGLContextAttach = needsAttach;
mSurfaceTexture = texture;
+
+ GLenum target = texture->getCurrentTextureTarget();
+ LOG_ALWAYS_FATAL_IF(target != GL_TEXTURE_2D && target != GL_TEXTURE_EXTERNAL_OES,
+ "set unsupported GLConsumer with target %x", target);
}
}
@@ -69,7 +72,7 @@ public:
ANDROID_API void setTransform(const SkMatrix* matrix) {
delete mTransform;
- mTransform = matrix ? new SkMatrix(*matrix) : 0;
+ mTransform = matrix ? new SkMatrix(*matrix) : nullptr;
}
ANDROID_API void setPaint(const SkPaint* paint);
@@ -84,8 +87,8 @@ public:
private:
// Generic properties
- uint32_t mWidth;
- uint32_t mHeight;
+ int mWidth;
+ int mHeight;
bool mBlend;
SkColorFilter* mColorFilter;
int mAlpha;
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index 8953166..4540bec 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -24,7 +24,6 @@
#include "Debug.h"
#include "DisplayList.h"
#include "DisplayListOp.h"
-#include "DisplayListLogBuffer.h"
namespace android {
namespace uirenderer {
@@ -46,41 +45,25 @@ void DisplayListData::cleanupResources() {
resourceCache.decrementRefcountLocked(bitmapResources.itemAt(i));
}
- for (size_t i = 0; i < ownedBitmapResources.size(); i++) {
- const SkBitmap* bitmap = ownedBitmapResources.itemAt(i);
- resourceCache.decrementRefcountLocked(bitmap);
- resourceCache.destructorLocked(bitmap);
- }
-
for (size_t i = 0; i < patchResources.size(); i++) {
resourceCache.decrementRefcountLocked(patchResources.itemAt(i));
}
- for (size_t i = 0; i < sourcePaths.size(); i++) {
- resourceCache.decrementRefcountLocked(sourcePaths.itemAt(i));
- }
-
resourceCache.unlock();
- for (size_t i = 0; i < paints.size(); i++) {
- delete paints.itemAt(i);
- }
-
- for (size_t i = 0; i < regions.size(); i++) {
- delete regions.itemAt(i);
- }
-
- for (size_t i = 0; i < paths.size(); i++) {
- delete paths.itemAt(i);
+ for (size_t i = 0; i < pathResources.size(); i++) {
+ const SkPath* path = pathResources.itemAt(i);
+ if (path->unique() && Caches::hasInstance()) {
+ Caches::getInstance().pathCache.removeDeferred(path);
+ }
+ delete path;
}
bitmapResources.clear();
- ownedBitmapResources.clear();
patchResources.clear();
- sourcePaths.clear();
+ pathResources.clear();
paints.clear();
regions.clear();
- paths.clear();
}
size_t DisplayListData::addChild(DrawRenderNodeOp* op) {
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index 7a43a2a..7fbda1f 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -38,8 +38,9 @@
#include <androidfw/ResourceTypes.h>
#include "Debug.h"
-#include "Matrix.h"
+#include "CanvasProperty.h"
#include "DeferredDisplayList.h"
+#include "Matrix.h"
#include "RenderProperties.h"
class SkBitmap;
@@ -52,7 +53,7 @@ namespace uirenderer {
class DeferredDisplayList;
class DisplayListOp;
-class DisplayListRenderer;
+class DisplayListCanvas;
class OpenGLRenderer;
class Rect;
class Layer;
@@ -66,7 +67,7 @@ class DrawRenderNodeOp;
/**
* Holds data used in the playback a tree of DisplayLists.
*/
-class PlaybackStateStruct {
+struct PlaybackStateStruct {
protected:
PlaybackStateStruct(OpenGLRenderer& renderer, int replayFlags, LinearAllocator* allocator)
: mRenderer(renderer)
@@ -87,8 +88,7 @@ public:
}
};
-class DeferStateStruct : public PlaybackStateStruct {
-public:
+struct DeferStateStruct : public PlaybackStateStruct {
DeferStateStruct(DeferredDisplayList& deferredList, OpenGLRenderer& renderer, int replayFlags)
: PlaybackStateStruct(renderer, replayFlags, &(deferredList.mAllocator)),
mDeferredList(deferredList) {}
@@ -96,14 +96,12 @@ public:
DeferredDisplayList& mDeferredList;
};
-class ReplayStateStruct : public PlaybackStateStruct {
-public:
+struct ReplayStateStruct : public PlaybackStateStruct {
ReplayStateStruct(OpenGLRenderer& renderer, Rect& dirty, int replayFlags)
: PlaybackStateStruct(renderer, replayFlags, &mReplayAllocator),
- mDirty(dirty), mDrawGlStatus(DrawGlInfo::kStatusDone) {}
+ mDirty(dirty) {}
Rect& mDirty;
- status_t mDrawGlStatus;
LinearAllocator mReplayAllocator;
};
@@ -111,7 +109,7 @@ public:
* Data structure that holds the list of commands used in display list stream
*/
class DisplayListData {
- friend class DisplayListRenderer;
+ friend class DisplayListCanvas;
public:
struct Chunk {
// range of included ops in DLD::displayListOps
@@ -136,13 +134,11 @@ public:
int projectionReceiveIndex;
Vector<const SkBitmap*> bitmapResources;
- Vector<const SkBitmap*> ownedBitmapResources;
+ Vector<const SkPath*> pathResources;
Vector<const Res_png_9patch*> patchResources;
- Vector<const SkPaint*> paints;
- Vector<const SkPath*> paths;
- SortedVector<const SkPath*> sourcePaths;
- Vector<const SkRegion*> regions;
+ std::vector<std::unique_ptr<const SkPaint>> paints;
+ std::vector<std::unique_ptr<const SkRegion>> regions;
Vector<Functor*> functors;
const Vector<Chunk>& getChunks() const {
diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp
new file mode 100644
index 0000000..6b86030
--- /dev/null
+++ b/libs/hwui/DisplayListCanvas.cpp
@@ -0,0 +1,570 @@
+/*
+ * Copyright (C) 2010 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 "DisplayListCanvas.h"
+
+#include "ResourceCache.h"
+#include "DeferredDisplayList.h"
+#include "DeferredLayerUpdater.h"
+#include "DisplayListOp.h"
+#include "RenderNode.h"
+#include "utils/PaintUtils.h"
+
+#include <SkCamera.h>
+#include <SkCanvas.h>
+
+#include <private/hwui/DrawGlInfo.h>
+
+namespace android {
+namespace uirenderer {
+
+DisplayListCanvas::DisplayListCanvas()
+ : mState(*this)
+ , mResourceCache(ResourceCache::getInstance())
+ , mDisplayListData(nullptr)
+ , mTranslateX(0.0f)
+ , mTranslateY(0.0f)
+ , mHasDeferredTranslate(false)
+ , mDeferredBarrierType(kBarrier_None)
+ , mHighContrastText(false)
+ , mRestoreSaveCount(-1) {
+}
+
+DisplayListCanvas::~DisplayListCanvas() {
+ LOG_ALWAYS_FATAL_IF(mDisplayListData,
+ "Destroyed a DisplayListCanvas during a record!");
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Operations
+///////////////////////////////////////////////////////////////////////////////
+
+DisplayListData* DisplayListCanvas::finishRecording() {
+ mPaintMap.clear();
+ mRegionMap.clear();
+ mPathMap.clear();
+ DisplayListData* data = mDisplayListData;
+ mDisplayListData = nullptr;
+ mSkiaCanvasProxy.reset(nullptr);
+ return data;
+}
+
+void DisplayListCanvas::prepareDirty(float left, float top,
+ float right, float bottom) {
+
+ LOG_ALWAYS_FATAL_IF(mDisplayListData,
+ "prepareDirty called a second time during a recording!");
+ mDisplayListData = new DisplayListData();
+
+ mState.initializeSaveStack(0, 0, mState.getWidth(), mState.getHeight(), Vector3());
+
+ mDeferredBarrierType = kBarrier_InOrder;
+ mState.setDirtyClip(false);
+ mRestoreSaveCount = -1;
+}
+
+bool DisplayListCanvas::finish() {
+ flushRestoreToCount();
+ flushTranslate();
+ return false;
+}
+
+void DisplayListCanvas::interrupt() {
+}
+
+void DisplayListCanvas::resume() {
+}
+
+void DisplayListCanvas::callDrawGLFunction(Functor *functor, Rect& dirty) {
+ // Ignore dirty during recording, it matters only when we replay
+ addDrawOp(new (alloc()) DrawFunctorOp(functor));
+ mDisplayListData->functors.add(functor);
+}
+
+SkCanvas* DisplayListCanvas::asSkCanvas() {
+ LOG_ALWAYS_FATAL_IF(!mDisplayListData,
+ "attempting to get an SkCanvas when we are not recording!");
+ if (!mSkiaCanvasProxy) {
+ mSkiaCanvasProxy.reset(new SkiaCanvasProxy(this));
+ }
+ return mSkiaCanvasProxy.get();
+}
+
+int DisplayListCanvas::save(SkCanvas::SaveFlags flags) {
+ addStateOp(new (alloc()) SaveOp((int) flags));
+ return mState.save((int) flags);
+}
+
+void DisplayListCanvas::restore() {
+ if (mRestoreSaveCount < 0) {
+ restoreToCount(getSaveCount() - 1);
+ return;
+ }
+
+ mRestoreSaveCount--;
+ flushTranslate();
+ mState.restore();
+}
+
+void DisplayListCanvas::restoreToCount(int saveCount) {
+ mRestoreSaveCount = saveCount;
+ flushTranslate();
+ mState.restoreToCount(saveCount);
+}
+
+int DisplayListCanvas::saveLayer(float left, float top, float right, float bottom,
+ const SkPaint* paint, SkCanvas::SaveFlags flags) {
+ // force matrix/clip isolation for layer
+ flags |= SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag;
+
+ paint = refPaint(paint);
+ addStateOp(new (alloc()) SaveLayerOp(left, top, right, bottom, paint, (int) flags));
+ return mState.save((int) flags);
+}
+
+void DisplayListCanvas::translate(float dx, float dy) {
+ mHasDeferredTranslate = true;
+ mTranslateX += dx;
+ mTranslateY += dy;
+ flushRestoreToCount();
+ mState.translate(dx, dy, 0.0f);
+}
+
+void DisplayListCanvas::rotate(float degrees) {
+ addStateOp(new (alloc()) RotateOp(degrees));
+ mState.rotate(degrees);
+}
+
+void DisplayListCanvas::scale(float sx, float sy) {
+ addStateOp(new (alloc()) ScaleOp(sx, sy));
+ mState.scale(sx, sy);
+}
+
+void DisplayListCanvas::skew(float sx, float sy) {
+ addStateOp(new (alloc()) SkewOp(sx, sy));
+ mState.skew(sx, sy);
+}
+
+void DisplayListCanvas::setMatrix(const SkMatrix& matrix) {
+ addStateOp(new (alloc()) SetMatrixOp(matrix));
+ mState.setMatrix(matrix);
+}
+
+void DisplayListCanvas::concat(const SkMatrix& matrix) {
+ addStateOp(new (alloc()) ConcatMatrixOp(matrix));
+ mState.concatMatrix(matrix);
+}
+
+bool DisplayListCanvas::getClipBounds(SkRect* outRect) const {
+ Rect bounds = mState.getLocalClipBounds();
+ *outRect = SkRect::MakeLTRB(bounds.left, bounds.top, bounds.right, bounds.bottom);
+ return !(outRect->isEmpty());
+}
+
+bool DisplayListCanvas::quickRejectRect(float left, float top, float right, float bottom) const {
+ return mState.quickRejectConservative(left, top, right, bottom);
+}
+
+bool DisplayListCanvas::quickRejectPath(const SkPath& path) const {
+ SkRect bounds = path.getBounds();
+ return mState.quickRejectConservative(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom);
+}
+
+
+bool DisplayListCanvas::clipRect(float left, float top, float right, float bottom,
+ SkRegion::Op op) {
+ addStateOp(new (alloc()) ClipRectOp(left, top, right, bottom, op));
+ return mState.clipRect(left, top, right, bottom, op);
+}
+
+bool DisplayListCanvas::clipPath(const SkPath* path, SkRegion::Op op) {
+ path = refPath(path);
+ addStateOp(new (alloc()) ClipPathOp(path, op));
+ return mState.clipPath(path, op);
+}
+
+bool DisplayListCanvas::clipRegion(const SkRegion* region, SkRegion::Op op) {
+ region = refRegion(region);
+ addStateOp(new (alloc()) ClipRegionOp(region, op));
+ return mState.clipRegion(region, op);
+}
+
+void DisplayListCanvas::drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t flags) {
+ LOG_ALWAYS_FATAL_IF(!renderNode, "missing rendernode");
+
+ // dirty is an out parameter and should not be recorded,
+ // it matters only when replaying the display list
+ DrawRenderNodeOp* op = new (alloc()) DrawRenderNodeOp(renderNode, flags, *mState.currentTransform());
+ addRenderNodeOp(op);
+}
+
+void DisplayListCanvas::drawLayer(DeferredLayerUpdater* layerHandle, float x, float y) {
+ // We ref the DeferredLayerUpdater due to its thread-safe ref-counting
+ // semantics.
+ mDisplayListData->ref(layerHandle);
+ addDrawOp(new (alloc()) DrawLayerOp(layerHandle->backingLayer(), x, y));
+}
+
+void DisplayListCanvas::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) {
+ bitmap = refBitmap(bitmap);
+ paint = refPaint(paint);
+
+ addDrawOp(new (alloc()) DrawBitmapOp(bitmap, paint));
+}
+
+void DisplayListCanvas::drawBitmap(const SkBitmap& bitmap, float left, float top,
+ const SkPaint* paint) {
+ save(SkCanvas::kMatrix_SaveFlag);
+ translate(left, top);
+ drawBitmap(&bitmap, paint);
+ restore();
+}
+
+void DisplayListCanvas::drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
+ const SkPaint* paint) {
+ if (matrix.isIdentity()) {
+ drawBitmap(&bitmap, paint);
+ } else if (!(matrix.getType() & ~(SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask))) {
+ // SkMatrix::isScaleTranslate() not available in L
+ SkRect src;
+ SkRect dst;
+ bitmap.getBounds(&src);
+ matrix.mapRect(&dst, src);
+ drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom,
+ dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint);
+ } else {
+ save(SkCanvas::kMatrix_SaveFlag);
+ concat(matrix);
+ drawBitmap(&bitmap, paint);
+ restore();
+ }
+}
+
+void DisplayListCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
+ float srcRight, float srcBottom, float dstLeft, float dstTop,
+ float dstRight, float dstBottom, const SkPaint* paint) {
+ if (srcLeft == 0 && srcTop == 0
+ && srcRight == bitmap.width()
+ && srcBottom == bitmap.height()
+ && (srcBottom - srcTop == dstBottom - dstTop)
+ && (srcRight - srcLeft == dstRight - dstLeft)) {
+ // transform simple rect to rect drawing case into position bitmap ops, since they merge
+ save(SkCanvas::kMatrix_SaveFlag);
+ translate(dstLeft, dstTop);
+ drawBitmap(&bitmap, paint);
+ restore();
+ } else {
+ paint = refPaint(paint);
+
+ if (paint && paint->getShader()) {
+ float scaleX = (dstRight - dstLeft) / (srcRight - srcLeft);
+ float scaleY = (dstBottom - dstTop) / (srcBottom - srcTop);
+ if (!MathUtils::areEqual(scaleX, 1.0f) || !MathUtils::areEqual(scaleY, 1.0f)) {
+ // Apply the scale transform on the canvas, so that the shader
+ // effectively calculates positions relative to src rect space
+
+ save(SkCanvas::kMatrix_SaveFlag);
+ translate(dstLeft, dstTop);
+ scale(scaleX, scaleY);
+
+ dstLeft = 0.0f;
+ dstTop = 0.0f;
+ dstRight = srcRight - srcLeft;
+ dstBottom = srcBottom - srcTop;
+
+ addDrawOp(new (alloc()) DrawBitmapRectOp(refBitmap(&bitmap),
+ srcLeft, srcTop, srcRight, srcBottom,
+ dstLeft, dstTop, dstRight, dstBottom, paint));
+ restore();
+ return;
+ }
+ }
+
+ addDrawOp(new (alloc()) DrawBitmapRectOp(refBitmap(&bitmap),
+ srcLeft, srcTop, srcRight, srcBottom,
+ dstLeft, dstTop, dstRight, dstBottom, paint));
+ }
+}
+
+void DisplayListCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
+ const float* vertices, const int* colors, const SkPaint* paint) {
+ int vertexCount = (meshWidth + 1) * (meshHeight + 1);
+ vertices = refBuffer<float>(vertices, vertexCount * 2); // 2 floats per vertex
+ paint = refPaint(paint);
+ colors = refBuffer<int>(colors, vertexCount); // 1 color per vertex
+
+ addDrawOp(new (alloc()) DrawBitmapMeshOp(refBitmap(&bitmap), meshWidth, meshHeight,
+ vertices, colors, paint));
+}
+
+void DisplayListCanvas::drawPatch(const SkBitmap* bitmap, const Res_png_9patch* patch,
+ float left, float top, float right, float bottom, const SkPaint* paint) {
+ bitmap = refBitmap(bitmap);
+ patch = refPatch(patch);
+ paint = refPaint(paint);
+
+ addDrawOp(new (alloc()) DrawPatchOp(bitmap, patch, left, top, right, bottom, paint));
+}
+
+void DisplayListCanvas::drawColor(int color, SkXfermode::Mode mode) {
+ addDrawOp(new (alloc()) DrawColorOp(color, mode));
+}
+
+void DisplayListCanvas::drawPaint(const SkPaint& paint) {
+ SkRect bounds;
+ if (getClipBounds(&bounds)) {
+ drawRect(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, paint);
+ }
+}
+
+
+void DisplayListCanvas::drawRect(float left, float top, float right, float bottom,
+ const SkPaint& paint) {
+ addDrawOp(new (alloc()) DrawRectOp(left, top, right, bottom, refPaint(&paint)));
+}
+
+void DisplayListCanvas::drawRoundRect(float left, float top, float right, float bottom,
+ float rx, float ry, const SkPaint& paint) {
+ addDrawOp(new (alloc()) DrawRoundRectOp(left, top, right, bottom, rx, ry, refPaint(&paint)));
+}
+
+void DisplayListCanvas::drawRoundRect(
+ CanvasPropertyPrimitive* left, CanvasPropertyPrimitive* top,
+ CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom,
+ CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry,
+ CanvasPropertyPaint* paint) {
+ mDisplayListData->ref(left);
+ mDisplayListData->ref(top);
+ mDisplayListData->ref(right);
+ mDisplayListData->ref(bottom);
+ mDisplayListData->ref(rx);
+ mDisplayListData->ref(ry);
+ mDisplayListData->ref(paint);
+ addDrawOp(new (alloc()) DrawRoundRectPropsOp(&left->value, &top->value,
+ &right->value, &bottom->value, &rx->value, &ry->value, &paint->value));
+}
+
+void DisplayListCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) {
+ addDrawOp(new (alloc()) DrawCircleOp(x, y, radius, refPaint(&paint)));
+}
+
+void DisplayListCanvas::drawCircle(CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y,
+ CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint) {
+ mDisplayListData->ref(x);
+ mDisplayListData->ref(y);
+ mDisplayListData->ref(radius);
+ mDisplayListData->ref(paint);
+ addDrawOp(new (alloc()) DrawCirclePropsOp(&x->value, &y->value,
+ &radius->value, &paint->value));
+}
+
+void DisplayListCanvas::drawOval(float left, float top, float right, float bottom,
+ const SkPaint& paint) {
+ addDrawOp(new (alloc()) DrawOvalOp(left, top, right, bottom, refPaint(&paint)));
+}
+
+void DisplayListCanvas::drawArc(float left, float top, float right, float bottom,
+ float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) {
+ if (fabs(sweepAngle) >= 360.0f) {
+ drawOval(left, top, right, bottom, paint);
+ } else {
+ addDrawOp(new (alloc()) DrawArcOp(left, top, right, bottom,
+ startAngle, sweepAngle, useCenter, refPaint(&paint)));
+ }
+}
+
+void DisplayListCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
+ addDrawOp(new (alloc()) DrawPathOp(refPath(&path), refPaint(&paint)));
+}
+
+void DisplayListCanvas::drawLines(const float* points, int count, const SkPaint& paint) {
+ points = refBuffer<float>(points, count);
+
+ addDrawOp(new (alloc()) DrawLinesOp(points, count, refPaint(&paint)));
+}
+
+void DisplayListCanvas::drawPoints(const float* points, int count, const SkPaint& paint) {
+ points = refBuffer<float>(points, count);
+
+ addDrawOp(new (alloc()) DrawPointsOp(points, count, refPaint(&paint)));
+}
+
+void DisplayListCanvas::drawTextOnPath(const uint16_t* glyphs, int count,
+ const SkPath& path, float hOffset, float vOffset, const SkPaint& paint) {
+ if (!glyphs || count <= 0) return;
+
+ int bytesCount = 2 * count;
+ DrawOp* op = new (alloc()) DrawTextOnPathOp(refText((const char*) glyphs, bytesCount),
+ bytesCount, count, refPath(&path),
+ hOffset, vOffset, refPaint(&paint));
+ addDrawOp(op);
+}
+
+void DisplayListCanvas::drawPosText(const uint16_t* text, const float* positions,
+ int count, int posCount, const SkPaint& paint) {
+ if (!text || count <= 0) return;
+
+ int bytesCount = 2 * count;
+ positions = refBuffer<float>(positions, count * 2);
+
+ DrawOp* op = new (alloc()) DrawPosTextOp(refText((const char*) text, bytesCount),
+ bytesCount, count, positions, refPaint(&paint));
+ addDrawOp(op);
+}
+
+static void simplifyPaint(int color, SkPaint* paint) {
+ paint->setColor(color);
+ paint->setShader(nullptr);
+ paint->setColorFilter(nullptr);
+ paint->setLooper(nullptr);
+ paint->setStrokeWidth(4 + 0.04 * paint->getTextSize());
+ paint->setStrokeJoin(SkPaint::kRound_Join);
+ paint->setLooper(nullptr);
+}
+
+void DisplayListCanvas::drawText(const uint16_t* glyphs, const float* positions,
+ int count, const SkPaint& paint, float x, float y,
+ float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
+ float totalAdvance) {
+
+ if (!glyphs || count <= 0 || PaintUtils::paintWillNotDrawText(paint)) return;
+
+ int bytesCount = count * 2;
+ const char* text = refText((const char*) glyphs, bytesCount);
+ positions = refBuffer<float>(positions, count * 2);
+ Rect bounds(boundsLeft, boundsTop, boundsRight, boundsBottom);
+
+ if (CC_UNLIKELY(mHighContrastText)) {
+ // high contrast draw path
+ int color = paint.getColor();
+ int channelSum = SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color);
+ bool darken = channelSum < (128 * 3);
+
+ // outline
+ SkPaint* outlinePaint = copyPaint(&paint);
+ simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, outlinePaint);
+ outlinePaint->setStyle(SkPaint::kStrokeAndFill_Style);
+ addDrawOp(new (alloc()) DrawTextOp(text, bytesCount, count,
+ x, y, positions, outlinePaint, totalAdvance, bounds)); // bounds?
+
+ // inner
+ SkPaint* innerPaint = copyPaint(&paint);
+ simplifyPaint(darken ? SK_ColorBLACK : SK_ColorWHITE, innerPaint);
+ innerPaint->setStyle(SkPaint::kFill_Style);
+ addDrawOp(new (alloc()) DrawTextOp(text, bytesCount, count,
+ x, y, positions, innerPaint, totalAdvance, bounds));
+ } else {
+ // standard draw path
+ DrawOp* op = new (alloc()) DrawTextOp(text, bytesCount, count,
+ x, y, positions, refPaint(&paint), totalAdvance, bounds);
+ addDrawOp(op);
+ }
+}
+
+void DisplayListCanvas::drawRects(const float* rects, int count, const SkPaint* paint) {
+ if (count <= 0) return;
+
+ rects = refBuffer<float>(rects, count);
+ paint = refPaint(paint);
+ addDrawOp(new (alloc()) DrawRectsOp(rects, count, paint));
+}
+
+void DisplayListCanvas::setDrawFilter(SkDrawFilter* filter) {
+ mDrawFilter.reset(SkSafeRef(filter));
+}
+
+void DisplayListCanvas::insertReorderBarrier(bool enableReorder) {
+ flushRestoreToCount();
+ flushTranslate();
+ mDeferredBarrierType = enableReorder ? kBarrier_OutOfOrder : kBarrier_InOrder;
+}
+
+void DisplayListCanvas::flushRestoreToCount() {
+ if (mRestoreSaveCount >= 0) {
+ addOpAndUpdateChunk(new (alloc()) RestoreToCountOp(mRestoreSaveCount));
+ mRestoreSaveCount = -1;
+ }
+}
+
+void DisplayListCanvas::flushTranslate() {
+ if (mHasDeferredTranslate) {
+ if (mTranslateX != 0.0f || mTranslateY != 0.0f) {
+ addOpAndUpdateChunk(new (alloc()) TranslateOp(mTranslateX, mTranslateY));
+ mTranslateX = mTranslateY = 0.0f;
+ }
+ mHasDeferredTranslate = false;
+ }
+}
+
+size_t DisplayListCanvas::addOpAndUpdateChunk(DisplayListOp* op) {
+ int insertIndex = mDisplayListData->displayListOps.add(op);
+ if (mDeferredBarrierType != kBarrier_None) {
+ // op is first in new chunk
+ mDisplayListData->chunks.push();
+ DisplayListData::Chunk& newChunk = mDisplayListData->chunks.editTop();
+ newChunk.beginOpIndex = insertIndex;
+ newChunk.endOpIndex = insertIndex + 1;
+ newChunk.reorderChildren = (mDeferredBarrierType == kBarrier_OutOfOrder);
+
+ int nextChildIndex = mDisplayListData->children().size();
+ newChunk.beginChildIndex = newChunk.endChildIndex = nextChildIndex;
+ mDeferredBarrierType = kBarrier_None;
+ } else {
+ // standard case - append to existing chunk
+ mDisplayListData->chunks.editTop().endOpIndex = insertIndex + 1;
+ }
+ return insertIndex;
+}
+
+size_t DisplayListCanvas::flushAndAddOp(DisplayListOp* op) {
+ flushRestoreToCount();
+ flushTranslate();
+ return addOpAndUpdateChunk(op);
+}
+
+size_t DisplayListCanvas::addStateOp(StateOp* op) {
+ return flushAndAddOp(op);
+}
+
+size_t DisplayListCanvas::addDrawOp(DrawOp* op) {
+ Rect localBounds;
+ if (op->getLocalBounds(localBounds)) {
+ bool rejected = quickRejectRect(localBounds.left, localBounds.top,
+ localBounds.right, localBounds.bottom);
+ op->setQuickRejected(rejected);
+ }
+
+ mDisplayListData->hasDrawOps = true;
+ return flushAndAddOp(op);
+}
+
+size_t DisplayListCanvas::addRenderNodeOp(DrawRenderNodeOp* op) {
+ int opIndex = addDrawOp(op);
+ int childIndex = mDisplayListData->addChild(op);
+
+ // update the chunk's child indices
+ DisplayListData::Chunk& chunk = mDisplayListData->chunks.editTop();
+ chunk.endChildIndex = childIndex + 1;
+
+ if (op->renderNode()->stagingProperties().isProjectionReceiver()) {
+ // use staging property, since recording on UI thread
+ mDisplayListData->projectionReceiveIndex = opIndex;
+ }
+ return opIndex;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/DisplayListCanvas.h b/libs/hwui/DisplayListCanvas.h
new file mode 100644
index 0000000..2b0b6b2
--- /dev/null
+++ b/libs/hwui/DisplayListCanvas.h
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2010 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_DISPLAY_LIST_RENDERER_H
+#define ANDROID_HWUI_DISPLAY_LIST_RENDERER_H
+
+#include <SkDrawFilter.h>
+#include <SkMatrix.h>
+#include <SkPaint.h>
+#include <SkPath.h>
+#include <SkRegion.h>
+#include <SkTLazy.h>
+#include <cutils/compiler.h>
+
+#include "Canvas.h"
+#include "CanvasState.h"
+#include "DisplayList.h"
+#include "SkiaCanvasProxy.h"
+#include "RenderNode.h"
+#include "ResourceCache.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+// Debug
+#if DEBUG_DISPLAY_LIST
+ #define DISPLAY_LIST_LOGD(...) ALOGD(__VA_ARGS__)
+#else
+ #define DISPLAY_LIST_LOGD(...)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// Display list
+///////////////////////////////////////////////////////////////////////////////
+
+class DeferredDisplayList;
+class DeferredLayerUpdater;
+class DisplayListOp;
+class DrawOp;
+class RenderNode;
+class StateOp;
+
+/**
+ * Records drawing commands in a display list for later playback into an OpenGLRenderer.
+ */
+class ANDROID_API DisplayListCanvas: public Canvas, public CanvasStateClient {
+public:
+ DisplayListCanvas();
+ virtual ~DisplayListCanvas();
+
+ void insertReorderBarrier(bool enableReorder);
+
+ DisplayListData* finishRecording();
+
+// ----------------------------------------------------------------------------
+// HWUI Frame state operations
+// ----------------------------------------------------------------------------
+
+ void prepareDirty(float left, float top, float right, float bottom);
+ void prepare() { prepareDirty(0.0f, 0.0f, width(), height()); }
+ bool finish();
+ void interrupt();
+ void resume();
+
+// ----------------------------------------------------------------------------
+// HWUI Canvas state operations
+// ----------------------------------------------------------------------------
+
+ void setViewport(int width, int height) { mState.setViewport(width, height); }
+
+ const Rect& getRenderTargetClipBounds() const { return mState.getRenderTargetClipBounds(); }
+
+ bool isCurrentTransformSimple() {
+ return mState.currentTransform()->isSimple();
+ }
+
+// ----------------------------------------------------------------------------
+// HWUI Canvas draw operations
+// ----------------------------------------------------------------------------
+
+ // Bitmap-based
+ void drawBitmap(const SkBitmap* bitmap, const SkPaint* paint);
+ // TODO: move drawPatch() to Canvas.h
+ void drawPatch(const SkBitmap* bitmap, const Res_png_9patch* patch,
+ float left, float top, float right, float bottom, const SkPaint* paint);
+
+ // Shapes
+ void drawRects(const float* rects, int count, const SkPaint* paint);
+ void drawRoundRect(CanvasPropertyPrimitive* left, CanvasPropertyPrimitive* top,
+ CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom,
+ CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry,
+ CanvasPropertyPaint* paint);
+ void drawCircle(CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y,
+ CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint);
+
+
+// ----------------------------------------------------------------------------
+// HWUI Canvas draw operations - special
+// ----------------------------------------------------------------------------
+ void drawLayer(DeferredLayerUpdater* layerHandle, float x, float y);
+ void drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t replayFlags);
+
+ // TODO: rename for consistency
+ void callDrawGLFunction(Functor* functor, Rect& dirty);
+
+ void setHighContrastText(bool highContrastText) {
+ mHighContrastText = highContrastText;
+ }
+
+// ----------------------------------------------------------------------------
+// CanvasStateClient interface
+// ----------------------------------------------------------------------------
+ virtual void onViewportInitialized() override { }
+ virtual void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) override { }
+ virtual GLuint getTargetFbo() const override { return -1; }
+
+// ----------------------------------------------------------------------------
+// android/graphics/Canvas interface
+// ----------------------------------------------------------------------------
+ virtual SkCanvas* asSkCanvas() override;
+
+ virtual void setBitmap(const SkBitmap& bitmap) override {
+ LOG_ALWAYS_FATAL("DisplayListCanvas is not backed by a bitmap.");
+ }
+
+ virtual bool isOpaque() override { return false; }
+ virtual int width() override { return mState.getWidth(); }
+ virtual int height() override { return mState.getHeight(); }
+
+// ----------------------------------------------------------------------------
+// android/graphics/Canvas state operations
+// ----------------------------------------------------------------------------
+ // Save (layer)
+ virtual int getSaveCount() const override { return mState.getSaveCount(); }
+ virtual int save(SkCanvas::SaveFlags flags) override;
+ virtual void restore() override;
+ virtual void restoreToCount(int saveCount) override;
+
+ virtual int saveLayer(float left, float top, float right, float bottom, const SkPaint* paint,
+ SkCanvas::SaveFlags flags) override;
+ virtual int saveLayerAlpha(float left, float top, float right, float bottom,
+ int alpha, SkCanvas::SaveFlags flags) override {
+ SkPaint paint;
+ paint.setAlpha(alpha);
+ return saveLayer(left, top, right, bottom, &paint, flags);
+ }
+
+ // Matrix
+ virtual void getMatrix(SkMatrix* outMatrix) const override { mState.getMatrix(outMatrix); }
+ virtual void setMatrix(const SkMatrix& matrix) override;
+
+ virtual void concat(const SkMatrix& matrix) override;
+ virtual void rotate(float degrees) override;
+ virtual void scale(float sx, float sy) override;
+ virtual void skew(float sx, float sy) override;
+ virtual void translate(float dx, float dy) override;
+
+ // Clip
+ virtual bool getClipBounds(SkRect* outRect) const override;
+ virtual bool quickRejectRect(float left, float top, float right, float bottom) const override;
+ virtual bool quickRejectPath(const SkPath& path) const override;
+
+ virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op) override;
+ virtual bool clipPath(const SkPath* path, SkRegion::Op op) override;
+ virtual bool clipRegion(const SkRegion* region, SkRegion::Op op) override;
+
+ // Misc
+ virtual SkDrawFilter* getDrawFilter() override { return mDrawFilter.get(); }
+ virtual void setDrawFilter(SkDrawFilter* filter) override;
+
+// ----------------------------------------------------------------------------
+// android/graphics/Canvas draw operations
+// ----------------------------------------------------------------------------
+ virtual void drawColor(int color, SkXfermode::Mode mode) override;
+ virtual void drawPaint(const SkPaint& paint) override;
+
+ // Geometry
+ virtual void drawPoint(float x, float y, const SkPaint& paint) override {
+ float points[2] = { x, y };
+ drawPoints(points, 2, paint);
+ }
+ virtual void drawPoints(const float* points, int count, const SkPaint& paint) override;
+ virtual void drawLine(float startX, float startY, float stopX, float stopY,
+ const SkPaint& paint) override {
+ float points[4] = { startX, startY, stopX, stopY };
+ drawLines(points, 4, paint);
+ }
+ virtual void drawLines(const float* points, int count, const SkPaint& paint) override;
+ virtual void drawRect(float left, float top, float right, float bottom, const SkPaint& paint) override;
+ virtual void drawRoundRect(float left, float top, float right, float bottom,
+ float rx, float ry, const SkPaint& paint) override;
+ virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) override;
+ virtual void drawOval(float left, float top, float right, float bottom, const SkPaint& paint) override;
+ virtual void drawArc(float left, float top, float right, float bottom,
+ float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) override;
+ virtual void drawPath(const SkPath& path, const SkPaint& paint) override;
+ virtual void drawVertices(SkCanvas::VertexMode vertexMode, int vertexCount,
+ const float* verts, const float* tex, const int* colors,
+ const uint16_t* indices, int indexCount, const SkPaint& paint) override
+ { /* DisplayListCanvas does not support drawVertices(); ignore */ }
+
+ // Bitmap-based
+ virtual void drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) override;
+ virtual void drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
+ const SkPaint* paint) override;
+ virtual void drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
+ float srcRight, float srcBottom, float dstLeft, float dstTop,
+ float dstRight, float dstBottom, const SkPaint* paint) override;
+ virtual void drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
+ const float* vertices, const int* colors, const SkPaint* paint) override;
+
+ // Text
+ virtual void drawText(const uint16_t* glyphs, const float* positions, int count,
+ const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop,
+ float boundsRight, float boundsBottom, float totalAdvance) override;
+ virtual void drawPosText(const uint16_t* text, const float* positions, int count,
+ int posCount, const SkPaint& paint) override;
+ virtual void drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path,
+ float hOffset, float vOffset, const SkPaint& paint) override;
+ virtual bool drawTextAbsolutePos() const override { return false; }
+
+
+private:
+
+ CanvasState mState;
+ std::unique_ptr<SkiaCanvasProxy> mSkiaCanvasProxy;
+
+ enum DeferredBarrierType {
+ kBarrier_None,
+ kBarrier_InOrder,
+ kBarrier_OutOfOrder,
+ };
+
+ void flushRestoreToCount();
+ void flushTranslate();
+ void flushReorderBarrier();
+
+ LinearAllocator& alloc() { return mDisplayListData->allocator; }
+
+ // Each method returns final index of op
+ size_t addOpAndUpdateChunk(DisplayListOp* op);
+ // flushes any deferred operations, and appends the op
+ size_t flushAndAddOp(DisplayListOp* op);
+
+ size_t addStateOp(StateOp* op);
+ size_t addDrawOp(DrawOp* op);
+ size_t addRenderNodeOp(DrawRenderNodeOp* op);
+
+
+ template<class T>
+ inline const T* refBuffer(const T* srcBuffer, int32_t count) {
+ if (!srcBuffer) return nullptr;
+
+ T* dstBuffer = (T*) mDisplayListData->allocator.alloc(count * sizeof(T));
+ memcpy(dstBuffer, srcBuffer, count * sizeof(T));
+ return dstBuffer;
+ }
+
+ inline char* refText(const char* text, size_t byteLength) {
+ return (char*) refBuffer<uint8_t>((uint8_t*)text, byteLength);
+ }
+
+ inline const SkPath* refPath(const SkPath* path) {
+ if (!path) return nullptr;
+
+ // The points/verbs within the path are refcounted so this copy operation
+ // is inexpensive and maintains the generationID of the original path.
+ const SkPath* cachedPath = new SkPath(*path);
+ mDisplayListData->pathResources.add(cachedPath);
+ return cachedPath;
+ }
+
+ inline const SkPaint* refPaint(const SkPaint* paint) {
+ if (!paint) return nullptr;
+
+ // If there is a draw filter apply it here and store the modified paint
+ // so that we don't need to modify the paint every time we access it.
+ SkTLazy<SkPaint> filteredPaint;
+ if (mDrawFilter.get()) {
+ filteredPaint.set(*paint);
+ mDrawFilter->filter(filteredPaint.get(), SkDrawFilter::kPaint_Type);
+ paint = filteredPaint.get();
+ }
+
+ // compute the hash key for the paint and check the cache.
+ const uint32_t key = paint->getHash();
+ const SkPaint* cachedPaint = mPaintMap.valueFor(key);
+ // In the unlikely event that 2 unique paints have the same hash we do a
+ // object equality check to ensure we don't erroneously dedup them.
+ if (cachedPaint == nullptr || *cachedPaint != *paint) {
+ cachedPaint = new SkPaint(*paint);
+ std::unique_ptr<const SkPaint> copy(cachedPaint);
+ mDisplayListData->paints.push_back(std::move(copy));
+
+ // replaceValueFor() performs an add if the entry doesn't exist
+ mPaintMap.replaceValueFor(key, cachedPaint);
+ }
+
+ return cachedPaint;
+ }
+
+ inline SkPaint* copyPaint(const SkPaint* paint) {
+ if (!paint) return nullptr;
+
+ SkPaint* returnPaint = new SkPaint(*paint);
+ std::unique_ptr<const SkPaint> copy(returnPaint);
+ mDisplayListData->paints.push_back(std::move(copy));
+
+ return returnPaint;
+ }
+
+ inline const SkRegion* refRegion(const SkRegion* region) {
+ if (!region) {
+ return region;
+ }
+
+ const SkRegion* cachedRegion = mRegionMap.valueFor(region);
+ // TODO: Add generation ID to SkRegion
+ if (cachedRegion == nullptr) {
+ std::unique_ptr<const SkRegion> copy(new SkRegion(*region));
+ cachedRegion = copy.get();
+ mDisplayListData->regions.push_back(std::move(copy));
+
+ // replaceValueFor() performs an add if the entry doesn't exist
+ mRegionMap.replaceValueFor(region, cachedRegion);
+ }
+
+ return cachedRegion;
+ }
+
+ inline const SkBitmap* refBitmap(const SkBitmap* bitmap) {
+ // Note that this assumes the bitmap is immutable. There are cases this won't handle
+ // correctly, such as creating the bitmap from scratch, drawing with it, changing its
+ // contents, and drawing again. The only fix would be to always copy it the first time,
+ // which doesn't seem worth the extra cycles for this unlikely case.
+ const SkBitmap* cachedBitmap = mResourceCache.insert(bitmap);
+ mDisplayListData->bitmapResources.add(cachedBitmap);
+ return cachedBitmap;
+ }
+
+ inline const Res_png_9patch* refPatch(const Res_png_9patch* patch) {
+ mDisplayListData->patchResources.add(patch);
+ mResourceCache.incrementRefcount(patch);
+ return patch;
+ }
+
+ DefaultKeyedVector<uint32_t, const SkPaint*> mPaintMap;
+ DefaultKeyedVector<const SkPath*, const SkPath*> mPathMap;
+ DefaultKeyedVector<const SkRegion*, const SkRegion*> mRegionMap;
+
+ ResourceCache& mResourceCache;
+ DisplayListData* mDisplayListData;
+
+ float mTranslateX;
+ float mTranslateY;
+ bool mHasDeferredTranslate;
+ DeferredBarrierType mDeferredBarrierType;
+ bool mHighContrastText;
+
+ int mRestoreSaveCount;
+
+ SkAutoTUnref<SkDrawFilter> mDrawFilter;
+
+ friend class RenderNode;
+
+}; // class DisplayListCanvas
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_DISPLAY_LIST_RENDERER_H
diff --git a/libs/hwui/DisplayListLogBuffer.cpp b/libs/hwui/DisplayListLogBuffer.cpp
deleted file mode 100644
index 45aacca..0000000
--- a/libs/hwui/DisplayListLogBuffer.cpp
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2011 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 "DisplayListLogBuffer.h"
-
-// BUFFER_SIZE size must be one more than a multiple of COMMAND_SIZE to ensure
-// that mStart always points at the next command, not just the next item
-#define NUM_COMMANDS 50
-#define BUFFER_SIZE ((NUM_COMMANDS) + 1)
-
-/**
- * DisplayListLogBuffer is a utility class which logs the most recent display
- * list operations in a circular buffer. The log is process-wide, because we
- * only care about the most recent operations, not the operations on a per-window
- * basis for a given activity. The purpose of the log is to provide more debugging
- * information in a bug report, by telling us not just where a process hung (which
- * generally is just reported as a stack trace at the Java level) or crashed, but
- * also what happened immediately before that hang or crash. This may help track down
- * problems in the native rendering code or driver interaction related to the display
- * list operations that led up to the hang or crash.
- *
- * The log is implemented as a circular buffer for both space and performance
- * reasons - we only care about the last several operations to give us context
- * leading up to the problem, and we don't want to constantly copy data around or do
- * additional mallocs to keep the most recent operations logged. Only numbers are
- * logged to make the operation fast. If and when the log is output, we process this
- * data into meaningful strings.
- *
- * There is an assumption about the format of the command (currently 2 ints: the
- * opcode and the nesting level). If the type of information logged changes (for example,
- * we may want to save a timestamp), then the size of the buffer and the way the
- * information is recorded in writeCommand() should change to suit.
- */
-
-namespace android {
-
-#ifdef USE_OPENGL_RENDERER
-using namespace uirenderer;
-ANDROID_SINGLETON_STATIC_INSTANCE(DisplayListLogBuffer);
-#endif
-
-namespace uirenderer {
-
-
-DisplayListLogBuffer::DisplayListLogBuffer() {
- mBufferFirst = (OpLog*) malloc(BUFFER_SIZE * sizeof(OpLog));
- mStart = mBufferFirst;
- mBufferLast = mBufferFirst + BUFFER_SIZE - 1;
- mEnd = mStart;
-}
-
-DisplayListLogBuffer::~DisplayListLogBuffer() {
- free(mBufferFirst);
-}
-
-/**
- * Called from DisplayListRenderer to output the current buffer into the
- * specified FILE. This only happens in a dumpsys/bugreport operation.
- */
-void DisplayListLogBuffer::outputCommands(FILE *file)
-{
- OpLog* tmpBufferPtr = mStart;
- while (true) {
- if (tmpBufferPtr == mEnd) {
- break;
- }
-
- fprintf(file, "%*s%s\n", 2 * tmpBufferPtr->level, "", tmpBufferPtr->label);
-
- OpLog* nextOp = tmpBufferPtr++;
- if (tmpBufferPtr > mBufferLast) {
- tmpBufferPtr = mBufferFirst;
- }
- }
-}
-
-/**
- * Store the given level and label in the buffer and increment/wrap the mEnd
- * and mStart values as appropriate. Label should point to static memory.
- */
-void DisplayListLogBuffer::writeCommand(int level, const char* label) {
- mEnd->level = level;
- mEnd->label = label;
-
- if (mEnd == mBufferLast) {
- mEnd = mBufferFirst;
- } else {
- mEnd++;
- }
- if (mEnd == mStart) {
- mStart++;
- if (mStart > mBufferLast) {
- mStart = mBufferFirst;
- }
- }
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/DisplayListLogBuffer.h b/libs/hwui/DisplayListLogBuffer.h
deleted file mode 100644
index c884789..0000000
--- a/libs/hwui/DisplayListLogBuffer.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2011 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_DISPLAY_LIST_LOG_BUFFER_H
-#define ANDROID_HWUI_DISPLAY_LIST_LOG_BUFFER_H
-
-#include <utils/Singleton.h>
-
-#include <stdio.h>
-
-namespace android {
-namespace uirenderer {
-
-class DisplayListLogBuffer: public Singleton<DisplayListLogBuffer> {
- DisplayListLogBuffer();
- ~DisplayListLogBuffer();
-
- friend class Singleton<DisplayListLogBuffer>;
-
-public:
- void writeCommand(int level, const char* label);
- void outputCommands(FILE *file);
-
- bool isEmpty() {
- return (mStart == mEnd);
- }
-
- struct OpLog {
- int level;
- const char* label;
- };
-
-private:
- OpLog* mBufferFirst; // where the memory starts
- OpLog* mStart; // where the current command stream starts
- OpLog* mEnd; // where the current commands end
- OpLog* mBufferLast; // where the buffer memory ends
-
-};
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_DISPLAY_LIST_LOG_BUFFER_H
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 8a5e21d..b5801fc 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -17,9 +17,17 @@
#ifndef ANDROID_HWUI_DISPLAY_OPERATION_H
#define ANDROID_HWUI_DISPLAY_OPERATION_H
-#ifndef LOG_TAG
- #define LOG_TAG "OpenGLRenderer"
-#endif
+#include "OpenGLRenderer.h"
+#include "AssetAtlas.h"
+#include "DeferredDisplayList.h"
+#include "DisplayListCanvas.h"
+#include "GammaFontRenderer.h"
+#include "Patch.h"
+#include "RenderNode.h"
+#include "renderstate/RenderState.h"
+#include "UvMapper.h"
+#include "utils/LinearAllocator.h"
+#include "utils/PaintUtils.h"
#include <SkColor.h>
#include <SkPath.h>
@@ -28,19 +36,6 @@
#include <private/hwui/DrawGlInfo.h>
-#include "OpenGLRenderer.h"
-#include "AssetAtlas.h"
-#include "DeferredDisplayList.h"
-#include "DisplayListRenderer.h"
-#include "RenderState.h"
-#include "UvMapper.h"
-#include "utils/LinearAllocator.h"
-
-#define CRASH() do { \
- *(int *)(uintptr_t) 0xbbadbeef = 0; \
- ((void(*)())0)(); /* More reliable, but doesn't say BBADBEEF */ \
-} while(false)
-
// Use OP_LOG for logging with arglist, OP_LOGS if just printing char*
#define OP_LOGS(s) OP_LOG("%s", (s))
#define OP_LOG(s, ...) ALOGD( "%*s" s, level * 2, "", __VA_ARGS__ )
@@ -63,9 +58,9 @@ class DisplayListOp {
public:
// These objects should always be allocated with a LinearAllocator, and never destroyed/deleted.
// standard new() intentionally not implemented, and delete/deconstructor should never be used.
- virtual ~DisplayListOp() { CRASH(); }
- static void operator delete(void* ptr) { CRASH(); }
- /** static void* operator new(size_t size); PURPOSELY OMITTED **/
+ virtual ~DisplayListOp() { LOG_ALWAYS_FATAL("Destructor not supported"); }
+ static void operator delete(void* ptr) { LOG_ALWAYS_FATAL("delete not supported"); }
+ static void* operator new(size_t size) = delete; /** PURPOSELY OMITTED **/
static void* operator new(size_t size, LinearAllocator& allocator) {
return allocator.alloc(size);
}
@@ -90,12 +85,8 @@ public:
class StateOp : public DisplayListOp {
public:
- StateOp() {};
-
- virtual ~StateOp() {}
-
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
- bool useQuickReject) {
+ bool useQuickReject) override {
// default behavior only affects immediate, deferrable state, issue directly to renderer
applyState(deferStruct.mRenderer, saveCount);
}
@@ -105,7 +96,7 @@ public:
* list to flush
*/
virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level,
- bool useQuickReject) {
+ bool useQuickReject) override {
applyState(replayStruct.mRenderer, saveCount);
}
@@ -119,7 +110,7 @@ public:
: mPaint(paint), mQuickRejected(false) {}
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
- bool useQuickReject) {
+ bool useQuickReject) override {
if (mQuickRejected && CC_LIKELY(useQuickReject)) {
return;
}
@@ -128,15 +119,15 @@ public:
}
virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level,
- bool useQuickReject) {
+ bool useQuickReject) override {
if (mQuickRejected && CC_LIKELY(useQuickReject)) {
return;
}
- replayStruct.mDrawGlStatus |= applyDraw(replayStruct.mRenderer, replayStruct.mDirty);
+ applyDraw(replayStruct.mRenderer, replayStruct.mDirty);
}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) = 0;
+ virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) = 0;
/**
* Draw multiple instances of an operation, must be overidden for operations that merge
@@ -145,14 +136,12 @@ public:
* and pure translation transformations. Other guarantees of similarity should be enforced by
* reducing which operations are tagged as mergeable.
*/
- virtual status_t multiDraw(OpenGLRenderer& renderer, Rect& dirty,
+ virtual void multiDraw(OpenGLRenderer& renderer, Rect& dirty,
const Vector<OpStatePair>& ops, const Rect& bounds) {
- status_t status = DrawGlInfo::kStatusDone;
for (unsigned int i = 0; i < ops.size(); i++) {
renderer.restoreDisplayState(*(ops[i].state), true);
- status |= ops[i].op->applyDraw(renderer, dirty);
+ ops[i].op->applyDraw(renderer, dirty);
}
- return status;
}
/**
@@ -199,10 +188,6 @@ public:
}
protected:
- const SkPaint* getPaint(OpenGLRenderer& renderer) {
- return renderer.filterPaint(mPaint);
- }
-
// Helper method for determining op opaqueness. Assumes op fills its bounds in local
// coordinates, and that paint's alpha is used
inline bool isOpaqueOverBounds(const DeferredDisplayState& state) {
@@ -219,7 +204,7 @@ protected:
if (mPaint->getShader() && !mPaint->getShader()->isOpaque()) {
return false;
}
- if (Renderer::isBlendedColorFilter(mPaint->getColorFilter())) {
+ if (PaintUtils::isBlendedColorFilter(mPaint->getColorFilter())) {
return false;
}
}
@@ -232,7 +217,7 @@ protected:
}
- const SkPaint* mPaint; // should be accessed via getPaint() when applying
+ const SkPaint* mPaint;
bool mQuickRejected;
};
@@ -260,7 +245,7 @@ public:
// default empty constructor for bounds, to be overridden in child constructor body
DrawBoundedOp(const SkPaint* paint): DrawOp(paint) { }
- virtual bool getLocalBounds(Rect& localBounds) {
+ virtual bool getLocalBounds(Rect& localBounds) override {
localBounds.set(mLocalBounds);
OpenGLRenderer::TextShadow textShadow;
if (OpenGLRenderer::getTextShadow(mPaint, &textShadow)) {
@@ -287,20 +272,20 @@ public:
: mFlags(flags) {}
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
- bool useQuickReject) {
+ bool useQuickReject) override {
int newSaveCount = deferStruct.mRenderer.save(mFlags);
deferStruct.mDeferredList.addSave(deferStruct.mRenderer, this, newSaveCount);
}
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
+ virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override {
renderer.save(mFlags);
}
- virtual void output(int level, uint32_t logFlags) const {
+ virtual void output(int level, uint32_t logFlags) const override {
OP_LOG("Save flags %x", mFlags);
}
- virtual const char* name() { return "Save"; }
+ virtual const char* name() override { return "Save"; }
int getFlags() const { return mFlags; }
private:
@@ -313,21 +298,21 @@ public:
: mCount(count) {}
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
- bool useQuickReject) {
+ bool useQuickReject) override {
deferStruct.mDeferredList.addRestoreToCount(deferStruct.mRenderer,
this, saveCount + mCount);
deferStruct.mRenderer.restoreToCount(saveCount + mCount);
}
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
+ virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override {
renderer.restoreToCount(saveCount + mCount);
}
- virtual void output(int level, uint32_t logFlags) const {
+ virtual void output(int level, uint32_t logFlags) const override {
OP_LOG("Restore to count %d", mCount);
}
- virtual const char* name() { return "RestoreToCount"; }
+ virtual const char* name() override { return "RestoreToCount"; }
private:
int mCount;
@@ -339,7 +324,7 @@ public:
: mArea(left, top, right, bottom)
, mPaint(&mCachedPaint)
, mFlags(flags)
- , mConvexMask(NULL) {
+ , mConvexMask(nullptr) {
mCachedPaint.setAlpha(alpha);
}
@@ -347,11 +332,11 @@ public:
: mArea(left, top, right, bottom)
, mPaint(paint)
, mFlags(flags)
- , mConvexMask(NULL)
+ , mConvexMask(nullptr)
{}
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
- bool useQuickReject) {
+ bool useQuickReject) override {
// NOTE: don't bother with actual saveLayer, instead issuing it at flush time
int newSaveCount = deferStruct.mRenderer.getSaveCount();
deferStruct.mDeferredList.addSaveLayer(deferStruct.mRenderer, this, newSaveCount);
@@ -362,17 +347,19 @@ public:
mPaint, mFlags);
}
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
+ virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override {
renderer.saveLayer(mArea.left, mArea.top, mArea.right, mArea.bottom,
mPaint, mFlags, mConvexMask);
}
- virtual void output(int level, uint32_t logFlags) const {
+ virtual void output(int level, uint32_t logFlags) const override {
OP_LOG("SaveLayer%s of area " RECT_STRING,
(isSaveLayerAlpha() ? "Alpha" : ""),RECT_ARGS(mArea));
}
- virtual const char* name() { return isSaveLayerAlpha() ? "SaveLayerAlpha" : "SaveLayer"; }
+ virtual const char* name() override {
+ return isSaveLayerAlpha() ? "SaveLayerAlpha" : "SaveLayer";
+ }
int getFlags() { return mFlags; }
@@ -403,15 +390,15 @@ public:
TranslateOp(float dx, float dy)
: mDx(dx), mDy(dy) {}
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
+ virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override {
renderer.translate(mDx, mDy);
}
- virtual void output(int level, uint32_t logFlags) const {
+ virtual void output(int level, uint32_t logFlags) const override {
OP_LOG("Translate by %f %f", mDx, mDy);
}
- virtual const char* name() { return "Translate"; }
+ virtual const char* name() override { return "Translate"; }
private:
float mDx;
@@ -423,15 +410,15 @@ public:
RotateOp(float degrees)
: mDegrees(degrees) {}
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
+ virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override {
renderer.rotate(mDegrees);
}
- virtual void output(int level, uint32_t logFlags) const {
+ virtual void output(int level, uint32_t logFlags) const override {
OP_LOG("Rotate by %f degrees", mDegrees);
}
- virtual const char* name() { return "Rotate"; }
+ virtual const char* name() override { return "Rotate"; }
private:
float mDegrees;
@@ -442,15 +429,15 @@ public:
ScaleOp(float sx, float sy)
: mSx(sx), mSy(sy) {}
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
+ virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override {
renderer.scale(mSx, mSy);
}
- virtual void output(int level, uint32_t logFlags) const {
+ virtual void output(int level, uint32_t logFlags) const override {
OP_LOG("Scale by %f %f", mSx, mSy);
}
- virtual const char* name() { return "Scale"; }
+ virtual const char* name() override { return "Scale"; }
private:
float mSx;
@@ -462,15 +449,15 @@ public:
SkewOp(float sx, float sy)
: mSx(sx), mSy(sy) {}
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
+ virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override {
renderer.skew(mSx, mSy);
}
- virtual void output(int level, uint32_t logFlags) const {
+ virtual void output(int level, uint32_t logFlags) const override {
OP_LOG("Skew by %f %f", mSx, mSy);
}
- virtual const char* name() { return "Skew"; }
+ virtual const char* name() override { return "Skew"; }
private:
float mSx;
@@ -482,11 +469,11 @@ public:
SetMatrixOp(const SkMatrix& matrix)
: mMatrix(matrix) {}
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
+ virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override {
renderer.setMatrix(mMatrix);
}
- virtual void output(int level, uint32_t logFlags) const {
+ virtual void output(int level, uint32_t logFlags) const override {
if (mMatrix.isIdentity()) {
OP_LOGS("SetMatrix (reset)");
} else {
@@ -494,7 +481,7 @@ public:
}
}
- virtual const char* name() { return "SetMatrix"; }
+ virtual const char* name() override { return "SetMatrix"; }
private:
const SkMatrix mMatrix;
@@ -505,15 +492,15 @@ public:
ConcatMatrixOp(const SkMatrix& matrix)
: mMatrix(matrix) {}
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
+ virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override {
renderer.concatMatrix(mMatrix);
}
- virtual void output(int level, uint32_t logFlags) const {
+ virtual void output(int level, uint32_t logFlags) const override {
OP_LOG("ConcatMatrix " SK_MATRIX_STRING, SK_MATRIX_ARGS(&mMatrix));
}
- virtual const char* name() { return "ConcatMatrix"; }
+ virtual const char* name() override { return "ConcatMatrix"; }
private:
const SkMatrix mMatrix;
@@ -524,7 +511,7 @@ public:
ClipOp(SkRegion::Op op) : mOp(op) {}
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
- bool useQuickReject) {
+ bool useQuickReject) override {
// NOTE: must defer op BEFORE applying state, since it may read clip
deferStruct.mDeferredList.addClip(deferStruct.mRenderer, this);
@@ -547,18 +534,18 @@ public:
ClipRectOp(float left, float top, float right, float bottom, SkRegion::Op op)
: ClipOp(op), mArea(left, top, right, bottom) {}
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
+ virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override {
renderer.clipRect(mArea.left, mArea.top, mArea.right, mArea.bottom, mOp);
}
- virtual void output(int level, uint32_t logFlags) const {
+ virtual void output(int level, uint32_t logFlags) const override {
OP_LOG("ClipRect " RECT_STRING, RECT_ARGS(mArea));
}
- virtual const char* name() { return "ClipRect"; }
+ virtual const char* name() override { return "ClipRect"; }
protected:
- virtual bool isRect() { return true; }
+ virtual bool isRect() override { return true; }
private:
Rect mArea;
@@ -569,17 +556,17 @@ public:
ClipPathOp(const SkPath* path, SkRegion::Op op)
: ClipOp(op), mPath(path) {}
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
+ virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override {
renderer.clipPath(mPath, mOp);
}
- virtual void output(int level, uint32_t logFlags) const {
+ virtual void output(int level, uint32_t logFlags) const override {
SkRect bounds = mPath->getBounds();
OP_LOG("ClipPath bounds " RECT_STRING,
bounds.left(), bounds.top(), bounds.right(), bounds.bottom());
}
- virtual const char* name() { return "ClipPath"; }
+ virtual const char* name() override { return "ClipPath"; }
private:
const SkPath* mPath;
@@ -590,55 +577,22 @@ public:
ClipRegionOp(const SkRegion* region, SkRegion::Op op)
: ClipOp(op), mRegion(region) {}
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
+ virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override {
renderer.clipRegion(mRegion, mOp);
}
- virtual void output(int level, uint32_t logFlags) const {
+ virtual void output(int level, uint32_t logFlags) const override {
SkIRect bounds = mRegion->getBounds();
OP_LOG("ClipRegion bounds %d %d %d %d",
bounds.left(), bounds.top(), bounds.right(), bounds.bottom());
}
- virtual const char* name() { return "ClipRegion"; }
+ virtual const char* name() override { return "ClipRegion"; }
private:
const SkRegion* mRegion;
};
-class ResetPaintFilterOp : public StateOp {
-public:
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
- renderer.resetPaintFilter();
- }
-
- virtual void output(int level, uint32_t logFlags) const {
- OP_LOGS("ResetPaintFilter");
- }
-
- virtual const char* name() { return "ResetPaintFilter"; }
-};
-
-class SetupPaintFilterOp : public StateOp {
-public:
- SetupPaintFilterOp(int clearBits, int setBits)
- : mClearBits(clearBits), mSetBits(setBits) {}
-
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
- renderer.setupPaintFilter(mClearBits, mSetBits);
- }
-
- virtual void output(int level, uint32_t logFlags) const {
- OP_LOG("SetupPaintFilter, clear %#x, set %#x", mClearBits, mSetBits);
- }
-
- virtual const char* name() { return "SetupPaintFilter"; }
-
-private:
- int mClearBits;
- int mSetBits;
-};
-
///////////////////////////////////////////////////////////////////////////////
// DRAW OPERATIONS - these are operations that can draw to the canvas's device
///////////////////////////////////////////////////////////////////////////////
@@ -648,11 +602,11 @@ public:
DrawBitmapOp(const SkBitmap* bitmap, const SkPaint* paint)
: DrawBoundedOp(0, 0, bitmap->width(), bitmap->height(), paint)
, mBitmap(bitmap)
- , mEntryValid(false), mEntry(NULL) {
+ , mEntryValid(false), mEntry(nullptr) {
}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
- return renderer.drawBitmap(mBitmap, getPaint(renderer));
+ virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
+ renderer.drawBitmap(mBitmap, mPaint);
}
AssetAtlas::Entry* getAtlasEntry(OpenGLRenderer& renderer) {
@@ -672,8 +626,8 @@ public:
* for each bitmap in the batch. This method is also responsible for dirtying
* the current layer, if any.
*/
- virtual status_t multiDraw(OpenGLRenderer& renderer, Rect& dirty,
- const Vector<OpStatePair>& ops, const Rect& bounds) {
+ virtual void multiDraw(OpenGLRenderer& renderer, Rect& dirty,
+ const Vector<OpStatePair>& ops, const Rect& bounds) override {
const DeferredDisplayState& firstState = *(ops[0].state);
renderer.restoreDisplayState(firstState, true); // restore all but the clip
@@ -709,19 +663,20 @@ public:
}
}
- return renderer.drawBitmaps(mBitmap, mEntry, ops.size(), &vertices[0],
+ renderer.drawBitmaps(mBitmap, mEntry, ops.size(), &vertices[0],
pureTranslate, bounds, mPaint);
}
- virtual void output(int level, uint32_t logFlags) const {
- OP_LOG("Draw bitmap %p at %f %f%s", mBitmap, mLocalBounds.left, mLocalBounds.top,
+ virtual void output(int level, uint32_t logFlags) const override {
+ OP_LOG("Draw bitmap %p of size %dx%d%s",
+ mBitmap, mBitmap->width(), mBitmap->height(),
mEntry ? " using AssetAtlas" : "");
}
- virtual const char* name() { return "DrawBitmap"; }
+ virtual const char* name() override { return "DrawBitmap"; }
virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
- const DeferredDisplayState& state) {
+ const DeferredDisplayState& state) override {
deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap;
deferInfo.mergeId = getAtlasEntry(renderer) ?
(mergeid_t) mEntry->getMergeId() : (mergeid_t) mBitmap;
@@ -757,21 +712,19 @@ public:
: DrawBoundedOp(dstLeft, dstTop, dstRight, dstBottom, paint),
mBitmap(bitmap), mSrc(srcLeft, srcTop, srcRight, srcBottom) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
- return renderer.drawBitmap(mBitmap, mSrc.left, mSrc.top, mSrc.right, mSrc.bottom,
- mLocalBounds.left, mLocalBounds.top, mLocalBounds.right, mLocalBounds.bottom,
- getPaint(renderer));
+ virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
+ renderer.drawBitmap(mBitmap, mSrc, mLocalBounds, mPaint);
}
- virtual void output(int level, uint32_t logFlags) const {
+ virtual void output(int level, uint32_t logFlags) const override {
OP_LOG("Draw bitmap %p src=" RECT_STRING ", dst=" RECT_STRING,
mBitmap, RECT_ARGS(mSrc), RECT_ARGS(mLocalBounds));
}
- virtual const char* name() { return "DrawBitmapRect"; }
+ virtual const char* name() override { return "DrawBitmapRect"; }
virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
- const DeferredDisplayState& state) {
+ const DeferredDisplayState& state) override {
deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap;
}
@@ -780,27 +733,6 @@ private:
Rect mSrc;
};
-class DrawBitmapDataOp : public DrawBitmapOp {
-public:
- DrawBitmapDataOp(const SkBitmap* bitmap, const SkPaint* paint)
- : DrawBitmapOp(bitmap, paint) {}
-
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
- return renderer.drawBitmapData(mBitmap, getPaint(renderer));
- }
-
- virtual void output(int level, uint32_t logFlags) const {
- OP_LOG("Draw bitmap %p", mBitmap);
- }
-
- virtual const char* name() { return "DrawBitmapData"; }
-
- virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
- const DeferredDisplayState& state) {
- deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap;
- }
-};
-
class DrawBitmapMeshOp : public DrawBoundedOp {
public:
DrawBitmapMeshOp(const SkBitmap* bitmap, int meshWidth, int meshHeight,
@@ -809,19 +741,19 @@ public:
mBitmap(bitmap), mMeshWidth(meshWidth), mMeshHeight(meshHeight),
mVertices(vertices), mColors(colors) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
- return renderer.drawBitmapMesh(mBitmap, mMeshWidth, mMeshHeight,
- mVertices, mColors, getPaint(renderer));
+ virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
+ renderer.drawBitmapMesh(mBitmap, mMeshWidth, mMeshHeight,
+ mVertices, mColors, mPaint);
}
- virtual void output(int level, uint32_t logFlags) const {
+ virtual void output(int level, uint32_t logFlags) const override {
OP_LOG("Draw bitmap %p mesh %d x %d", mBitmap, mMeshWidth, mMeshHeight);
}
- virtual const char* name() { return "DrawBitmapMesh"; }
+ virtual const char* name() override { return "DrawBitmapMesh"; }
virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
- const DeferredDisplayState& state) {
+ const DeferredDisplayState& state) override {
deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap;
}
@@ -838,8 +770,8 @@ public:
DrawPatchOp(const SkBitmap* bitmap, const Res_png_9patch* patch,
float left, float top, float right, float bottom, const SkPaint* paint)
: DrawBoundedOp(left, top, right, bottom, paint),
- mBitmap(bitmap), mPatch(patch), mGenerationId(0), mMesh(NULL),
- mEntryValid(false), mEntry(NULL) {
+ mBitmap(bitmap), mPatch(patch), mGenerationId(0), mMesh(nullptr),
+ mEntryValid(false), mEntry(nullptr) {
};
AssetAtlas::Entry* getAtlasEntry(OpenGLRenderer& renderer) {
@@ -865,8 +797,8 @@ public:
* and transforming the vertices of each 9-patch in the batch. This method
* is also responsible for dirtying the current layer, if any.
*/
- virtual status_t multiDraw(OpenGLRenderer& renderer, Rect& dirty,
- const Vector<OpStatePair>& ops, const Rect& bounds) {
+ virtual void multiDraw(OpenGLRenderer& renderer, Rect& dirty,
+ const Vector<OpStatePair>& ops, const Rect& bounds) override {
const DeferredDisplayState& firstState = *(ops[0].state);
renderer.restoreDisplayState(firstState, true); // restore all but the clip
@@ -905,7 +837,7 @@ public:
patchOp->mLocalBounds.top + 0.5f);
// Copy & transform all the vertices for the current operation
- TextureVertex* opVertices = opMesh->vertices;
+ TextureVertex* opVertices = opMesh->vertices.get();
for (uint32_t j = 0; j < vertexCount; j++, opVertices++) {
TextureVertex::set(vertex++,
opVertices->x + tx, opVertices->y + ty,
@@ -935,27 +867,27 @@ public:
indexCount += opMesh->indexCount;
}
- return renderer.drawPatches(mBitmap, getAtlasEntry(renderer),
- &vertices[0], indexCount, getPaint(renderer));
+ renderer.drawPatches(mBitmap, getAtlasEntry(renderer),
+ &vertices[0], indexCount, mPaint);
}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
+ virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
// We're not calling the public variant of drawPatch() here
// This method won't perform the quickReject() since we've already done it at this point
- return renderer.drawPatch(mBitmap, getMesh(renderer), getAtlasEntry(renderer),
+ renderer.drawPatch(mBitmap, getMesh(renderer), getAtlasEntry(renderer),
mLocalBounds.left, mLocalBounds.top, mLocalBounds.right, mLocalBounds.bottom,
- getPaint(renderer));
+ mPaint);
}
- virtual void output(int level, uint32_t logFlags) const {
+ virtual void output(int level, uint32_t logFlags) const override {
OP_LOG("Draw patch " RECT_STRING "%s", RECT_ARGS(mLocalBounds),
mEntry ? " with AssetAtlas" : "");
}
- virtual const char* name() { return "DrawPatch"; }
+ virtual const char* name() override { return "DrawPatch"; }
virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
- const DeferredDisplayState& state) {
+ const DeferredDisplayState& state) override {
deferInfo.batchId = DeferredDisplayList::kOpBatch_Patch;
deferInfo.mergeId = getAtlasEntry(renderer) ? (mergeid_t) mEntry->getMergeId() : (mergeid_t) mBitmap;
deferInfo.mergeable = state.mMatrix.isPureTranslate() &&
@@ -977,17 +909,17 @@ private:
class DrawColorOp : public DrawOp {
public:
DrawColorOp(int color, SkXfermode::Mode mode)
- : DrawOp(NULL), mColor(color), mMode(mode) {};
+ : DrawOp(nullptr), mColor(color), mMode(mode) {};
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
- return renderer.drawColor(mColor, mMode);
+ virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
+ renderer.drawColor(mColor, mMode);
}
- virtual void output(int level, uint32_t logFlags) const {
+ virtual void output(int level, uint32_t logFlags) const override {
OP_LOG("Draw color %#x, mode %d", mColor, mMode);
}
- virtual const char* name() { return "DrawColor"; }
+ virtual const char* name() override { return "DrawColor"; }
private:
int mColor;
@@ -1001,7 +933,7 @@ public:
DrawStrokableOp(const Rect& localBounds, const SkPaint* paint)
: DrawBoundedOp(localBounds, paint) {};
- virtual bool getLocalBounds(Rect& localBounds) {
+ virtual bool getLocalBounds(Rect& localBounds) override {
localBounds.set(mLocalBounds);
if (mPaint && mPaint->getStyle() != SkPaint::kFill_Style) {
localBounds.outset(strokeWidthOutset());
@@ -1010,7 +942,7 @@ public:
}
virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
- const DeferredDisplayState& state) {
+ const DeferredDisplayState& state) override {
if (mPaint->getPathEffect()) {
deferInfo.batchId = DeferredDisplayList::kOpBatch_AlphaMaskTexture;
} else {
@@ -1026,23 +958,23 @@ public:
DrawRectOp(float left, float top, float right, float bottom, const SkPaint* paint)
: DrawStrokableOp(left, top, right, bottom, paint) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
- return renderer.drawRect(mLocalBounds.left, mLocalBounds.top,
- mLocalBounds.right, mLocalBounds.bottom, getPaint(renderer));
+ virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
+ renderer.drawRect(mLocalBounds.left, mLocalBounds.top,
+ mLocalBounds.right, mLocalBounds.bottom, mPaint);
}
- virtual void output(int level, uint32_t logFlags) const {
+ virtual void output(int level, uint32_t logFlags) const override {
OP_LOG("Draw Rect " RECT_STRING, RECT_ARGS(mLocalBounds));
}
virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
- const DeferredDisplayState& state) {
+ const DeferredDisplayState& state) override {
DrawStrokableOp::onDefer(renderer, deferInfo, state);
deferInfo.opaqueOverBounds = isOpaqueOverBounds(state) &&
mPaint->getStyle() == SkPaint::kFill_Style;
}
- virtual const char* name() { return "DrawRect"; }
+ virtual const char* name() override { return "DrawRect"; }
};
class DrawRectsOp : public DrawBoundedOp {
@@ -1051,18 +983,18 @@ public:
: DrawBoundedOp(rects, count, paint),
mRects(rects), mCount(count) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
- return renderer.drawRects(mRects, mCount, getPaint(renderer));
+ virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
+ renderer.drawRects(mRects, mCount, mPaint);
}
- virtual void output(int level, uint32_t logFlags) const {
+ virtual void output(int level, uint32_t logFlags) const override {
OP_LOG("Draw Rects count %d", mCount);
}
- virtual const char* name() { return "DrawRects"; }
+ virtual const char* name() override { return "DrawRects"; }
virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
- const DeferredDisplayState& state) {
+ const DeferredDisplayState& state) override {
deferInfo.batchId = DeferredDisplayList::kOpBatch_Vertices;
}
@@ -1077,17 +1009,17 @@ public:
float rx, float ry, const SkPaint* paint)
: DrawStrokableOp(left, top, right, bottom, paint), mRx(rx), mRy(ry) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
- return renderer.drawRoundRect(mLocalBounds.left, mLocalBounds.top,
- mLocalBounds.right, mLocalBounds.bottom, mRx, mRy, getPaint(renderer));
+ virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
+ renderer.drawRoundRect(mLocalBounds.left, mLocalBounds.top,
+ mLocalBounds.right, mLocalBounds.bottom, mRx, mRy, mPaint);
}
- virtual void output(int level, uint32_t logFlags) const {
+ virtual void output(int level, uint32_t logFlags) const override {
OP_LOG("Draw RoundRect " RECT_STRING ", rx %f, ry %f", RECT_ARGS(mLocalBounds), mRx, mRy);
}
virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
- const DeferredDisplayState& state) {
+ const DeferredDisplayState& state) override {
DrawStrokableOp::onDefer(renderer, deferInfo, state);
if (!mPaint->getPathEffect()) {
renderer.getCaches().tessellationCache.precacheRoundRect(state.mMatrix, *mPaint,
@@ -1095,7 +1027,7 @@ public:
}
}
- virtual const char* name() { return "DrawRoundRect"; }
+ virtual const char* name() override { return "DrawRoundRect"; }
private:
float mRx;
@@ -1109,17 +1041,17 @@ public:
: DrawOp(paint), mLeft(left), mTop(top), mRight(right), mBottom(bottom),
mRx(rx), mRy(ry) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
- return renderer.drawRoundRect(*mLeft, *mTop, *mRight, *mBottom,
- *mRx, *mRy, getPaint(renderer));
+ virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
+ renderer.drawRoundRect(*mLeft, *mTop, *mRight, *mBottom,
+ *mRx, *mRy, mPaint);
}
- virtual void output(int level, uint32_t logFlags) const {
+ virtual void output(int level, uint32_t logFlags) const override {
OP_LOG("Draw RoundRect Props " RECT_STRING ", rx %f, ry %f",
*mLeft, *mTop, *mRight, *mBottom, *mRx, *mRy);
}
- virtual const char* name() { return "DrawRoundRectProps"; }
+ virtual const char* name() override { return "DrawRoundRectProps"; }
private:
float* mLeft;
@@ -1136,15 +1068,15 @@ public:
: DrawStrokableOp(x - radius, y - radius, x + radius, y + radius, paint),
mX(x), mY(y), mRadius(radius) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
- return renderer.drawCircle(mX, mY, mRadius, getPaint(renderer));
+ virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
+ renderer.drawCircle(mX, mY, mRadius, mPaint);
}
- virtual void output(int level, uint32_t logFlags) const {
+ virtual void output(int level, uint32_t logFlags) const override {
OP_LOG("Draw Circle x %f, y %f, r %f", mX, mY, mRadius);
}
- virtual const char* name() { return "DrawCircle"; }
+ virtual const char* name() override { return "DrawCircle"; }
private:
float mX;
@@ -1157,15 +1089,15 @@ public:
DrawCirclePropsOp(float* x, float* y, float* radius, const SkPaint* paint)
: DrawOp(paint), mX(x), mY(y), mRadius(radius) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
- return renderer.drawCircle(*mX, *mY, *mRadius, getPaint(renderer));
+ virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
+ renderer.drawCircle(*mX, *mY, *mRadius, mPaint);
}
- virtual void output(int level, uint32_t logFlags) const {
+ virtual void output(int level, uint32_t logFlags) const override {
OP_LOG("Draw Circle Props x %p, y %p, r %p", mX, mY, mRadius);
}
- virtual const char* name() { return "DrawCircleProps"; }
+ virtual const char* name() override { return "DrawCircleProps"; }
private:
float* mX;
@@ -1178,16 +1110,16 @@ public:
DrawOvalOp(float left, float top, float right, float bottom, const SkPaint* paint)
: DrawStrokableOp(left, top, right, bottom, paint) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
- return renderer.drawOval(mLocalBounds.left, mLocalBounds.top,
- mLocalBounds.right, mLocalBounds.bottom, getPaint(renderer));
+ virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
+ renderer.drawOval(mLocalBounds.left, mLocalBounds.top,
+ mLocalBounds.right, mLocalBounds.bottom, mPaint);
}
- virtual void output(int level, uint32_t logFlags) const {
+ virtual void output(int level, uint32_t logFlags) const override {
OP_LOG("Draw Oval " RECT_STRING, RECT_ARGS(mLocalBounds));
}
- virtual const char* name() { return "DrawOval"; }
+ virtual const char* name() override { return "DrawOval"; }
};
class DrawArcOp : public DrawStrokableOp {
@@ -1197,18 +1129,18 @@ public:
: DrawStrokableOp(left, top, right, bottom, paint),
mStartAngle(startAngle), mSweepAngle(sweepAngle), mUseCenter(useCenter) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
- return renderer.drawArc(mLocalBounds.left, mLocalBounds.top,
+ virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
+ renderer.drawArc(mLocalBounds.left, mLocalBounds.top,
mLocalBounds.right, mLocalBounds.bottom,
- mStartAngle, mSweepAngle, mUseCenter, getPaint(renderer));
+ mStartAngle, mSweepAngle, mUseCenter, mPaint);
}
- virtual void output(int level, uint32_t logFlags) const {
+ virtual void output(int level, uint32_t logFlags) const override {
OP_LOG("Draw Arc " RECT_STRING ", start %f, sweep %f, useCenter %d",
RECT_ARGS(mLocalBounds), mStartAngle, mSweepAngle, mUseCenter);
}
- virtual const char* name() { return "DrawArc"; }
+ virtual const char* name() override { return "DrawArc"; }
private:
float mStartAngle;
@@ -1228,23 +1160,22 @@ public:
mLocalBounds.set(left, top, left + width, top + height);
}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
- return renderer.drawPath(mPath, getPaint(renderer));
+ virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
+ renderer.drawPath(mPath, mPaint);
}
virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
- const DeferredDisplayState& state) {
- const SkPaint* paint = getPaint(renderer);
- renderer.getCaches().pathCache.precache(mPath, paint);
+ const DeferredDisplayState& state) override {
+ renderer.getCaches().pathCache.precache(mPath, mPaint);
deferInfo.batchId = DeferredDisplayList::kOpBatch_AlphaMaskTexture;
}
- virtual void output(int level, uint32_t logFlags) const {
+ virtual void output(int level, uint32_t logFlags) const override {
OP_LOG("Draw Path %p in " RECT_STRING, mPath, RECT_ARGS(mLocalBounds));
}
- virtual const char* name() { return "DrawPath"; }
+ virtual const char* name() override { return "DrawPath"; }
private:
const SkPath* mPath;
@@ -1258,18 +1189,18 @@ public:
mLocalBounds.outset(strokeWidthOutset());
}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
- return renderer.drawLines(mPoints, mCount, getPaint(renderer));
+ virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
+ renderer.drawLines(mPoints, mCount, mPaint);
}
- virtual void output(int level, uint32_t logFlags) const {
+ virtual void output(int level, uint32_t logFlags) const override {
OP_LOG("Draw Lines count %d", mCount);
}
- virtual const char* name() { return "DrawLines"; }
+ virtual const char* name() override { return "DrawLines"; }
virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
- const DeferredDisplayState& state) {
+ const DeferredDisplayState& state) override {
deferInfo.batchId = mPaint->isAntiAlias() ?
DeferredDisplayList::kOpBatch_AlphaVertices :
DeferredDisplayList::kOpBatch_Vertices;
@@ -1285,15 +1216,15 @@ public:
DrawPointsOp(const float* points, int count, const SkPaint* paint)
: DrawLinesOp(points, count, paint) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
- return renderer.drawPoints(mPoints, mCount, getPaint(renderer));
+ virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
+ renderer.drawPoints(mPoints, mCount, mPaint);
}
- virtual void output(int level, uint32_t logFlags) const {
+ virtual void output(int level, uint32_t logFlags) const override {
OP_LOG("Draw Points count %d", mCount);
}
- virtual const char* name() { return "DrawPoints"; }
+ virtual const char* name() override { return "DrawPoints"; }
};
class DrawSomeTextOp : public DrawOp {
@@ -1301,19 +1232,18 @@ public:
DrawSomeTextOp(const char* text, int bytesCount, int count, const SkPaint* paint)
: DrawOp(paint), mText(text), mBytesCount(bytesCount), mCount(count) {};
- virtual void output(int level, uint32_t logFlags) const {
+ virtual void output(int level, uint32_t logFlags) const override {
OP_LOG("Draw some text, %d bytes", mBytesCount);
}
- virtual bool hasTextShadow() const {
+ virtual bool hasTextShadow() const override {
return OpenGLRenderer::hasTextShadow(mPaint);
}
virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
- const DeferredDisplayState& state) {
- const SkPaint* paint = getPaint(renderer);
- FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(paint);
- fontRenderer.precache(paint, mText, mCount, SkMatrix::I());
+ const DeferredDisplayState& state) override {
+ FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(mPaint);
+ fontRenderer.precache(mPaint, mText, mCount, SkMatrix::I());
deferInfo.batchId = mPaint->getColor() == SK_ColorBLACK ?
DeferredDisplayList::kOpBatch_Text :
@@ -1335,12 +1265,12 @@ public:
/* TODO: inherit from DrawBounded and init mLocalBounds */
}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
- return renderer.drawTextOnPath(mText, mBytesCount, mCount, mPath,
- mHOffset, mVOffset, getPaint(renderer));
+ virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
+ renderer.drawTextOnPath(mText, mBytesCount, mCount, mPath,
+ mHOffset, mVOffset, mPaint);
}
- virtual const char* name() { return "DrawTextOnPath"; }
+ virtual const char* name() override { return "DrawTextOnPath"; }
private:
const SkPath* mPath;
@@ -1356,11 +1286,11 @@ public:
/* TODO: inherit from DrawBounded and init mLocalBounds */
}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
- return renderer.drawPosText(mText, mBytesCount, mCount, mPositions, getPaint(renderer));
+ virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
+ renderer.drawPosText(mText, mBytesCount, mCount, mPositions, mPaint);
}
- virtual const char* name() { return "DrawPosText"; }
+ virtual const char* name() override { return "DrawPosText"; }
private:
const float* mPositions;
@@ -1376,13 +1306,12 @@ public:
}
virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
- const DeferredDisplayState& state) {
- const SkPaint* paint = getPaint(renderer);
- FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(paint);
+ const DeferredDisplayState& state) override {
+ FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(mPaint);
SkMatrix transform;
renderer.findBestFontTransform(state.mMatrix, &transform);
if (mPrecacheTransform != transform) {
- fontRenderer.precache(paint, mText, mCount, transform);
+ fontRenderer.precache(mPaint, mText, mCount, transform);
mPrecacheTransform = transform;
}
deferInfo.batchId = mPaint->getColor() == SK_ColorBLACK ?
@@ -1400,36 +1329,34 @@ public:
&& OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
+ virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
Rect bounds;
getLocalBounds(bounds);
- return renderer.drawText(mText, mBytesCount, mCount, mX, mY,
- mPositions, getPaint(renderer), mTotalAdvance, bounds);
+ renderer.drawText(mText, mBytesCount, mCount, mX, mY,
+ mPositions, mPaint, mTotalAdvance, bounds);
}
- virtual status_t multiDraw(OpenGLRenderer& renderer, Rect& dirty,
- const Vector<OpStatePair>& ops, const Rect& bounds) {
- status_t status = DrawGlInfo::kStatusDone;
+ virtual void multiDraw(OpenGLRenderer& renderer, Rect& dirty,
+ const Vector<OpStatePair>& ops, const Rect& bounds) override {
for (unsigned int i = 0; i < ops.size(); i++) {
const DeferredDisplayState& state = *(ops[i].state);
- DrawOpMode drawOpMode = (i == ops.size() - 1) ? kDrawOpMode_Flush : kDrawOpMode_Defer;
+ DrawOpMode drawOpMode = (i == ops.size() - 1) ? DrawOpMode::kFlush : DrawOpMode::kDefer;
renderer.restoreDisplayState(state, true); // restore all but the clip
DrawTextOp& op = *((DrawTextOp*)ops[i].op);
// quickReject() will not occure in drawText() so we can use mLocalBounds
// directly, we do not need to account for shadow by calling getLocalBounds()
- status |= renderer.drawText(op.mText, op.mBytesCount, op.mCount, op.mX, op.mY,
- op.mPositions, op.getPaint(renderer), op.mTotalAdvance, op.mLocalBounds,
+ renderer.drawText(op.mText, op.mBytesCount, op.mCount, op.mX, op.mY,
+ op.mPositions, op.mPaint, op.mTotalAdvance, op.mLocalBounds,
drawOpMode);
}
- return status;
}
- virtual void output(int level, uint32_t logFlags) const {
+ virtual void output(int level, uint32_t logFlags) const override {
OP_LOG("Draw Text of count %d, bytes %d", mCount, mBytesCount);
}
- virtual const char* name() { return "DrawText"; }
+ virtual const char* name() override { return "DrawText"; }
private:
const char* mText;
@@ -1449,20 +1376,19 @@ private:
class DrawFunctorOp : public DrawOp {
public:
DrawFunctorOp(Functor* functor)
- : DrawOp(NULL), mFunctor(functor) {}
+ : DrawOp(nullptr), mFunctor(functor) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
+ virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
renderer.startMark("GL functor");
- status_t ret = renderer.callDrawGLFunction(mFunctor, dirty);
+ renderer.callDrawGLFunction(mFunctor, dirty);
renderer.endMark();
- return ret;
}
- virtual void output(int level, uint32_t logFlags) const {
+ virtual void output(int level, uint32_t logFlags) const override {
OP_LOG("Draw Functor %p", mFunctor);
}
- virtual const char* name() { return "DrawFunctor"; }
+ virtual const char* name() override { return "DrawFunctor"; }
private:
Functor* mFunctor;
@@ -1473,36 +1399,38 @@ class DrawRenderNodeOp : public DrawBoundedOp {
friend class DisplayListData; // grant DisplayListData access to info of child
public:
DrawRenderNodeOp(RenderNode* renderNode, int flags, const mat4& transformFromParent)
- : DrawBoundedOp(0, 0, renderNode->getWidth(), renderNode->getHeight(), 0),
- mRenderNode(renderNode), mFlags(flags), mTransformFromParent(transformFromParent) {}
+ : DrawBoundedOp(0, 0, renderNode->getWidth(), renderNode->getHeight(), nullptr)
+ , mRenderNode(renderNode)
+ , mFlags(flags)
+ , mTransformFromParent(transformFromParent)
+ , mSkipInOrderDraw(false) {}
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
- bool useQuickReject) {
+ bool useQuickReject) override {
if (mRenderNode->isRenderable() && !mSkipInOrderDraw) {
mRenderNode->defer(deferStruct, level + 1);
}
}
virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level,
- bool useQuickReject) {
+ bool useQuickReject) override {
if (mRenderNode->isRenderable() && !mSkipInOrderDraw) {
mRenderNode->replay(replayStruct, level + 1);
}
}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
+ virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
LOG_ALWAYS_FATAL("should not be called, because replay() is overridden");
- return 0;
}
- virtual void output(int level, uint32_t logFlags) const {
+ virtual void output(int level, uint32_t logFlags) const override {
OP_LOG("Draw RenderNode %p %s, flags %#x", mRenderNode, mRenderNode->getName(), mFlags);
if (mRenderNode && (logFlags & kOpLogFlag_Recurse)) {
mRenderNode->output(level + 1);
}
}
- virtual const char* name() { return "DrawRenderNode"; }
+ virtual const char* name() override { return "DrawRenderNode"; }
RenderNode* renderNode() { return mRenderNode; }
@@ -1537,7 +1465,7 @@ class DrawShadowOp : public DrawOp {
public:
DrawShadowOp(const mat4& transformXY, const mat4& transformZ,
float casterAlpha, const SkPath* casterOutline)
- : DrawOp(NULL)
+ : DrawOp(nullptr)
, mTransformXY(transformXY)
, mTransformZ(transformZ)
, mCasterAlpha(casterAlpha)
@@ -1545,13 +1473,13 @@ public:
}
virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
- const DeferredDisplayState& state) {
+ const DeferredDisplayState& state) override {
renderer.getCaches().tessellationCache.precacheShadows(&state.mMatrix,
renderer.getLocalClipBounds(), isCasterOpaque(), mCasterOutline,
&mTransformXY, &mTransformZ, renderer.getLightCenter(), renderer.getLightRadius());
}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
+ virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
TessellationCache::vertexBuffer_pair_t buffers;
Matrix4 drawTransform(*(renderer.currentTransform()));
renderer.getCaches().tessellationCache.getShadowBuffers(&drawTransform,
@@ -1559,14 +1487,14 @@ public:
&mTransformXY, &mTransformZ, renderer.getLightCenter(), renderer.getLightRadius(),
buffers);
- return renderer.drawShadow(mCasterAlpha, buffers.first, buffers.second);
+ renderer.drawShadow(mCasterAlpha, buffers.first, buffers.second);
}
- virtual void output(int level, uint32_t logFlags) const {
+ virtual void output(int level, uint32_t logFlags) const override {
OP_LOGS("DrawShadow");
}
- virtual const char* name() { return "DrawShadow"; }
+ virtual const char* name() override { return "DrawShadow"; }
private:
bool isCasterOpaque() { return mCasterAlpha >= 1.0f; }
@@ -1580,17 +1508,17 @@ private:
class DrawLayerOp : public DrawOp {
public:
DrawLayerOp(Layer* layer, float x, float y)
- : DrawOp(NULL), mLayer(layer), mX(x), mY(y) {}
+ : DrawOp(nullptr), mLayer(layer), mX(x), mY(y) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
- return renderer.drawLayer(mLayer, mX, mY);
+ virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
+ renderer.drawLayer(mLayer, mX, mY);
}
- virtual void output(int level, uint32_t logFlags) const {
+ virtual void output(int level, uint32_t logFlags) const override {
OP_LOG("Draw Layer %p at %f %f", mLayer, mX, mY);
}
- virtual const char* name() { return "DrawLayer"; }
+ virtual const char* name() override { return "DrawLayer"; }
private:
Layer* mLayer;
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
deleted file mode 100644
index c2cb76e..0000000
--- a/libs/hwui/DisplayListRenderer.cpp
+++ /dev/null
@@ -1,532 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-
-#define LOG_TAG "OpenGLRenderer"
-
-#include <SkCamera.h>
-#include <SkCanvas.h>
-
-#include <private/hwui/DrawGlInfo.h>
-
-#include "ResourceCache.h"
-#include "DeferredDisplayList.h"
-#include "DeferredLayerUpdater.h"
-#include "DisplayListLogBuffer.h"
-#include "DisplayListOp.h"
-#include "DisplayListRenderer.h"
-#include "RenderNode.h"
-
-namespace android {
-namespace uirenderer {
-
-DisplayListRenderer::DisplayListRenderer()
- : mResourceCache(ResourceCache::getInstance())
- , mDisplayListData(NULL)
- , mTranslateX(0.0f)
- , mTranslateY(0.0f)
- , mDeferredBarrierType(kBarrier_None)
- , mHighContrastText(false)
- , mRestoreSaveCount(-1) {
-}
-
-DisplayListRenderer::~DisplayListRenderer() {
- LOG_ALWAYS_FATAL_IF(mDisplayListData,
- "Destroyed a DisplayListRenderer during a record!");
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Operations
-///////////////////////////////////////////////////////////////////////////////
-
-DisplayListData* DisplayListRenderer::finishRecording() {
- mPaintMap.clear();
- mRegionMap.clear();
- mPathMap.clear();
- DisplayListData* data = mDisplayListData;
- mDisplayListData = 0;
- return data;
-}
-
-status_t DisplayListRenderer::prepareDirty(float left, float top,
- float right, float bottom, bool opaque) {
-
- LOG_ALWAYS_FATAL_IF(mDisplayListData,
- "prepareDirty called a second time during a recording!");
- mDisplayListData = new DisplayListData();
-
- initializeSaveStack(0, 0, getWidth(), getHeight(), Vector3());
-
- mDeferredBarrierType = kBarrier_InOrder;
- mDirtyClip = opaque;
- mRestoreSaveCount = -1;
-
- return DrawGlInfo::kStatusDone; // No invalidate needed at record-time
-}
-
-void DisplayListRenderer::finish() {
- flushRestoreToCount();
- flushTranslate();
-}
-
-void DisplayListRenderer::interrupt() {
-}
-
-void DisplayListRenderer::resume() {
-}
-
-status_t DisplayListRenderer::callDrawGLFunction(Functor *functor, Rect& dirty) {
- // Ignore dirty during recording, it matters only when we replay
- addDrawOp(new (alloc()) DrawFunctorOp(functor));
- mDisplayListData->functors.add(functor);
- return DrawGlInfo::kStatusDone; // No invalidate needed at record-time
-}
-
-int DisplayListRenderer::save(int flags) {
- addStateOp(new (alloc()) SaveOp(flags));
- return StatefulBaseRenderer::save(flags);
-}
-
-void DisplayListRenderer::restore() {
- if (mRestoreSaveCount < 0) {
- restoreToCount(getSaveCount() - 1);
- return;
- }
-
- mRestoreSaveCount--;
- flushTranslate();
- StatefulBaseRenderer::restore();
-}
-
-void DisplayListRenderer::restoreToCount(int saveCount) {
- mRestoreSaveCount = saveCount;
- flushTranslate();
- StatefulBaseRenderer::restoreToCount(saveCount);
-}
-
-int DisplayListRenderer::saveLayer(float left, float top, float right, float bottom,
- const SkPaint* paint, int flags) {
- // force matrix/clip isolation for layer
- flags |= SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag;
-
- paint = refPaint(paint);
- addStateOp(new (alloc()) SaveLayerOp(left, top, right, bottom, paint, flags));
- return StatefulBaseRenderer::save(flags);
-}
-
-void DisplayListRenderer::translate(float dx, float dy, float dz) {
- // ignore dz, not used at defer time
- mHasDeferredTranslate = true;
- mTranslateX += dx;
- mTranslateY += dy;
- flushRestoreToCount();
- StatefulBaseRenderer::translate(dx, dy, dz);
-}
-
-void DisplayListRenderer::rotate(float degrees) {
- addStateOp(new (alloc()) RotateOp(degrees));
- StatefulBaseRenderer::rotate(degrees);
-}
-
-void DisplayListRenderer::scale(float sx, float sy) {
- addStateOp(new (alloc()) ScaleOp(sx, sy));
- StatefulBaseRenderer::scale(sx, sy);
-}
-
-void DisplayListRenderer::skew(float sx, float sy) {
- addStateOp(new (alloc()) SkewOp(sx, sy));
- StatefulBaseRenderer::skew(sx, sy);
-}
-
-void DisplayListRenderer::setMatrix(const SkMatrix& matrix) {
- addStateOp(new (alloc()) SetMatrixOp(matrix));
- StatefulBaseRenderer::setMatrix(matrix);
-}
-
-void DisplayListRenderer::concatMatrix(const SkMatrix& matrix) {
- addStateOp(new (alloc()) ConcatMatrixOp(matrix));
- StatefulBaseRenderer::concatMatrix(matrix);
-}
-
-bool DisplayListRenderer::clipRect(float left, float top, float right, float bottom,
- SkRegion::Op op) {
- addStateOp(new (alloc()) ClipRectOp(left, top, right, bottom, op));
- return StatefulBaseRenderer::clipRect(left, top, right, bottom, op);
-}
-
-bool DisplayListRenderer::clipPath(const SkPath* path, SkRegion::Op op) {
- path = refPath(path);
- addStateOp(new (alloc()) ClipPathOp(path, op));
- return StatefulBaseRenderer::clipPath(path, op);
-}
-
-bool DisplayListRenderer::clipRegion(const SkRegion* region, SkRegion::Op op) {
- region = refRegion(region);
- addStateOp(new (alloc()) ClipRegionOp(region, op));
- return StatefulBaseRenderer::clipRegion(region, op);
-}
-
-status_t DisplayListRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t flags) {
- LOG_ALWAYS_FATAL_IF(!renderNode, "missing rendernode");
-
- // dirty is an out parameter and should not be recorded,
- // it matters only when replaying the display list
- DrawRenderNodeOp* op = new (alloc()) DrawRenderNodeOp(renderNode, flags, *currentTransform());
- addRenderNodeOp(op);
-
- return DrawGlInfo::kStatusDone;
-}
-
-status_t DisplayListRenderer::drawLayer(DeferredLayerUpdater* layerHandle, float x, float y) {
- // We ref the DeferredLayerUpdater due to its thread-safe ref-counting
- // semantics.
- mDisplayListData->ref(layerHandle);
- addDrawOp(new (alloc()) DrawLayerOp(layerHandle->backingLayer(), x, y));
- return DrawGlInfo::kStatusDone;
-}
-
-status_t DisplayListRenderer::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) {
- bitmap = refBitmap(bitmap);
- paint = refPaint(paint);
-
- addDrawOp(new (alloc()) DrawBitmapOp(bitmap, paint));
- return DrawGlInfo::kStatusDone;
-}
-
-status_t DisplayListRenderer::drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop,
- float srcRight, float srcBottom, float dstLeft, float dstTop,
- float dstRight, float dstBottom, const SkPaint* paint) {
- if (srcLeft == 0 && srcTop == 0
- && srcRight == bitmap->width() && srcBottom == bitmap->height()
- && (srcBottom - srcTop == dstBottom - dstTop)
- && (srcRight - srcLeft == dstRight - dstLeft)) {
- // transform simple rect to rect drawing case into position bitmap ops, since they merge
- save(SkCanvas::kMatrix_SaveFlag);
- translate(dstLeft, dstTop);
- drawBitmap(bitmap, paint);
- restore();
- } else {
- bitmap = refBitmap(bitmap);
- paint = refPaint(paint);
-
- addDrawOp(new (alloc()) DrawBitmapRectOp(bitmap,
- srcLeft, srcTop, srcRight, srcBottom,
- dstLeft, dstTop, dstRight, dstBottom, paint));
- }
- return DrawGlInfo::kStatusDone;
-}
-
-status_t DisplayListRenderer::drawBitmapData(const SkBitmap* bitmap, const SkPaint* paint) {
- bitmap = refBitmapData(bitmap);
- paint = refPaint(paint);
-
- addDrawOp(new (alloc()) DrawBitmapDataOp(bitmap, paint));
- return DrawGlInfo::kStatusDone;
-}
-
-status_t DisplayListRenderer::drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight,
- const float* vertices, const int* colors, const SkPaint* paint) {
- int vertexCount = (meshWidth + 1) * (meshHeight + 1);
- bitmap = refBitmap(bitmap);
- vertices = refBuffer<float>(vertices, vertexCount * 2); // 2 floats per vertex
- paint = refPaint(paint);
- colors = refBuffer<int>(colors, vertexCount); // 1 color per vertex
-
- addDrawOp(new (alloc()) DrawBitmapMeshOp(bitmap, meshWidth, meshHeight,
- vertices, colors, paint));
- return DrawGlInfo::kStatusDone;
-}
-
-status_t DisplayListRenderer::drawPatch(const SkBitmap* bitmap, const Res_png_9patch* patch,
- float left, float top, float right, float bottom, const SkPaint* paint) {
- bitmap = refBitmap(bitmap);
- patch = refPatch(patch);
- paint = refPaint(paint);
-
- addDrawOp(new (alloc()) DrawPatchOp(bitmap, patch, left, top, right, bottom, paint));
- return DrawGlInfo::kStatusDone;
-}
-
-status_t DisplayListRenderer::drawColor(int color, SkXfermode::Mode mode) {
- addDrawOp(new (alloc()) DrawColorOp(color, mode));
- return DrawGlInfo::kStatusDone;
-}
-
-status_t DisplayListRenderer::drawRect(float left, float top, float right, float bottom,
- const SkPaint* paint) {
- paint = refPaint(paint);
- addDrawOp(new (alloc()) DrawRectOp(left, top, right, bottom, paint));
- return DrawGlInfo::kStatusDone;
-}
-
-status_t DisplayListRenderer::drawRoundRect(float left, float top, float right, float bottom,
- float rx, float ry, const SkPaint* paint) {
- paint = refPaint(paint);
- addDrawOp(new (alloc()) DrawRoundRectOp(left, top, right, bottom, rx, ry, paint));
- return DrawGlInfo::kStatusDone;
-}
-
-status_t DisplayListRenderer::drawRoundRect(
- CanvasPropertyPrimitive* left, CanvasPropertyPrimitive* top,
- CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom,
- CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry,
- CanvasPropertyPaint* paint) {
- mDisplayListData->ref(left);
- mDisplayListData->ref(top);
- mDisplayListData->ref(right);
- mDisplayListData->ref(bottom);
- mDisplayListData->ref(rx);
- mDisplayListData->ref(ry);
- mDisplayListData->ref(paint);
- addDrawOp(new (alloc()) DrawRoundRectPropsOp(&left->value, &top->value,
- &right->value, &bottom->value, &rx->value, &ry->value, &paint->value));
- return DrawGlInfo::kStatusDone;
-}
-
-status_t DisplayListRenderer::drawCircle(float x, float y, float radius, const SkPaint* paint) {
- paint = refPaint(paint);
- addDrawOp(new (alloc()) DrawCircleOp(x, y, radius, paint));
- return DrawGlInfo::kStatusDone;
-}
-
-status_t DisplayListRenderer::drawCircle(CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y,
- CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint) {
- mDisplayListData->ref(x);
- mDisplayListData->ref(y);
- mDisplayListData->ref(radius);
- mDisplayListData->ref(paint);
- addDrawOp(new (alloc()) DrawCirclePropsOp(&x->value, &y->value,
- &radius->value, &paint->value));
- return DrawGlInfo::kStatusDone;
-}
-
-status_t DisplayListRenderer::drawOval(float left, float top, float right, float bottom,
- const SkPaint* paint) {
- paint = refPaint(paint);
- addDrawOp(new (alloc()) DrawOvalOp(left, top, right, bottom, paint));
- return DrawGlInfo::kStatusDone;
-}
-
-status_t DisplayListRenderer::drawArc(float left, float top, float right, float bottom,
- float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint) {
- if (fabs(sweepAngle) >= 360.0f) {
- return drawOval(left, top, right, bottom, paint);
- }
-
- paint = refPaint(paint);
- addDrawOp(new (alloc()) DrawArcOp(left, top, right, bottom,
- startAngle, sweepAngle, useCenter, paint));
- return DrawGlInfo::kStatusDone;
-}
-
-status_t DisplayListRenderer::drawPath(const SkPath* path, const SkPaint* paint) {
- path = refPath(path);
- paint = refPaint(paint);
-
- addDrawOp(new (alloc()) DrawPathOp(path, paint));
- return DrawGlInfo::kStatusDone;
-}
-
-status_t DisplayListRenderer::drawLines(const float* points, int count, const SkPaint* paint) {
- points = refBuffer<float>(points, count);
- paint = refPaint(paint);
-
- addDrawOp(new (alloc()) DrawLinesOp(points, count, paint));
- return DrawGlInfo::kStatusDone;
-}
-
-status_t DisplayListRenderer::drawPoints(const float* points, int count, const SkPaint* paint) {
- points = refBuffer<float>(points, count);
- paint = refPaint(paint);
-
- addDrawOp(new (alloc()) DrawPointsOp(points, count, paint));
- return DrawGlInfo::kStatusDone;
-}
-
-status_t DisplayListRenderer::drawTextOnPath(const char* text, int bytesCount, int count,
- const SkPath* path, float hOffset, float vOffset, const SkPaint* paint) {
- if (!text || count <= 0) return DrawGlInfo::kStatusDone;
-
- text = refText(text, bytesCount);
- path = refPath(path);
- paint = refPaint(paint);
-
- DrawOp* op = new (alloc()) DrawTextOnPathOp(text, bytesCount, count, path,
- hOffset, vOffset, paint);
- addDrawOp(op);
- return DrawGlInfo::kStatusDone;
-}
-
-status_t DisplayListRenderer::drawPosText(const char* text, int bytesCount, int count,
- const float* positions, const SkPaint* paint) {
- if (!text || count <= 0) return DrawGlInfo::kStatusDone;
-
- text = refText(text, bytesCount);
- positions = refBuffer<float>(positions, count * 2);
- paint = refPaint(paint);
-
- DrawOp* op = new (alloc()) DrawPosTextOp(text, bytesCount, count, positions, paint);
- addDrawOp(op);
- return DrawGlInfo::kStatusDone;
-}
-
-static void simplifyPaint(int color, SkPaint* paint) {
- paint->setColor(color);
- paint->setShader(NULL);
- paint->setColorFilter(NULL);
- paint->setLooper(NULL);
- paint->setStrokeWidth(4 + 0.04 * paint->getTextSize());
- paint->setStrokeJoin(SkPaint::kRound_Join);
- paint->setLooper(NULL);
-}
-
-status_t DisplayListRenderer::drawText(const char* text, int bytesCount, int count,
- float x, float y, const float* positions, const SkPaint* paint,
- float totalAdvance, const Rect& bounds, DrawOpMode drawOpMode) {
-
- if (!text || count <= 0 || paintWillNotDrawText(*paint)) return DrawGlInfo::kStatusDone;
-
- text = refText(text, bytesCount);
- positions = refBuffer<float>(positions, count * 2);
-
- if (CC_UNLIKELY(mHighContrastText)) {
- // high contrast draw path
- int color = paint->getColor();
- int channelSum = SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color);
- bool darken = channelSum < (128 * 3);
-
- // outline
- SkPaint* outlinePaint = copyPaint(paint);
- simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, outlinePaint);
- outlinePaint->setStyle(SkPaint::kStrokeAndFill_Style);
- addDrawOp(new (alloc()) DrawTextOp(text, bytesCount, count,
- x, y, positions, outlinePaint, totalAdvance, bounds)); // bounds?
-
- // inner
- SkPaint* innerPaint = copyPaint(paint);
- simplifyPaint(darken ? SK_ColorBLACK : SK_ColorWHITE, innerPaint);
- innerPaint->setStyle(SkPaint::kFill_Style);
- addDrawOp(new (alloc()) DrawTextOp(text, bytesCount, count,
- x, y, positions, innerPaint, totalAdvance, bounds));
- } else {
- // standard draw path
- paint = refPaint(paint);
-
- DrawOp* op = new (alloc()) DrawTextOp(text, bytesCount, count,
- x, y, positions, paint, totalAdvance, bounds);
- addDrawOp(op);
- }
- return DrawGlInfo::kStatusDone;
-}
-
-status_t DisplayListRenderer::drawRects(const float* rects, int count, const SkPaint* paint) {
- if (count <= 0) return DrawGlInfo::kStatusDone;
-
- rects = refBuffer<float>(rects, count);
- paint = refPaint(paint);
- addDrawOp(new (alloc()) DrawRectsOp(rects, count, paint));
- return DrawGlInfo::kStatusDone;
-}
-
-void DisplayListRenderer::resetPaintFilter() {
- addStateOp(new (alloc()) ResetPaintFilterOp());
-}
-
-void DisplayListRenderer::setupPaintFilter(int clearBits, int setBits) {
- addStateOp(new (alloc()) SetupPaintFilterOp(clearBits, setBits));
-}
-
-void DisplayListRenderer::insertReorderBarrier(bool enableReorder) {
- flushRestoreToCount();
- flushTranslate();
- mDeferredBarrierType = enableReorder ? kBarrier_OutOfOrder : kBarrier_InOrder;
-}
-
-void DisplayListRenderer::flushRestoreToCount() {
- if (mRestoreSaveCount >= 0) {
- addOpAndUpdateChunk(new (alloc()) RestoreToCountOp(mRestoreSaveCount));
- mRestoreSaveCount = -1;
- }
-}
-
-void DisplayListRenderer::flushTranslate() {
- if (mHasDeferredTranslate) {
- if (mTranslateX != 0.0f || mTranslateY != 0.0f) {
- addOpAndUpdateChunk(new (alloc()) TranslateOp(mTranslateX, mTranslateY));
- mTranslateX = mTranslateY = 0.0f;
- }
- mHasDeferredTranslate = false;
- }
-}
-
-size_t DisplayListRenderer::addOpAndUpdateChunk(DisplayListOp* op) {
- int insertIndex = mDisplayListData->displayListOps.add(op);
- if (mDeferredBarrierType != kBarrier_None) {
- // op is first in new chunk
- mDisplayListData->chunks.push();
- DisplayListData::Chunk& newChunk = mDisplayListData->chunks.editTop();
- newChunk.beginOpIndex = insertIndex;
- newChunk.endOpIndex = insertIndex + 1;
- newChunk.reorderChildren = (mDeferredBarrierType == kBarrier_OutOfOrder);
-
- int nextChildIndex = mDisplayListData->children().size();
- newChunk.beginChildIndex = newChunk.endChildIndex = nextChildIndex;
- mDeferredBarrierType = kBarrier_None;
- } else {
- // standard case - append to existing chunk
- mDisplayListData->chunks.editTop().endOpIndex = insertIndex + 1;
- }
- return insertIndex;
-}
-
-size_t DisplayListRenderer::flushAndAddOp(DisplayListOp* op) {
- flushRestoreToCount();
- flushTranslate();
- return addOpAndUpdateChunk(op);
-}
-
-size_t DisplayListRenderer::addStateOp(StateOp* op) {
- return flushAndAddOp(op);
-}
-
-size_t DisplayListRenderer::addDrawOp(DrawOp* op) {
- Rect localBounds;
- if (op->getLocalBounds(localBounds)) {
- bool rejected = quickRejectConservative(localBounds.left, localBounds.top,
- localBounds.right, localBounds.bottom);
- op->setQuickRejected(rejected);
- }
-
- mDisplayListData->hasDrawOps = true;
- return flushAndAddOp(op);
-}
-
-size_t DisplayListRenderer::addRenderNodeOp(DrawRenderNodeOp* op) {
- int opIndex = addDrawOp(op);
- int childIndex = mDisplayListData->addChild(op);
-
- // update the chunk's child indices
- DisplayListData::Chunk& chunk = mDisplayListData->chunks.editTop();
- chunk.endChildIndex = childIndex + 1;
-
- if (op->renderNode()->stagingProperties().isProjectionReceiver()) {
- // use staging property, since recording on UI thread
- mDisplayListData->projectionReceiveIndex = opIndex;
- }
- return opIndex;
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
deleted file mode 100644
index 2cc2be3..0000000
--- a/libs/hwui/DisplayListRenderer.h
+++ /dev/null
@@ -1,316 +0,0 @@
-/*
- * Copyright (C) 2010 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_DISPLAY_LIST_RENDERER_H
-#define ANDROID_HWUI_DISPLAY_LIST_RENDERER_H
-
-#include <SkMatrix.h>
-#include <SkPaint.h>
-#include <SkPath.h>
-#include <cutils/compiler.h>
-
-#include "DisplayListLogBuffer.h"
-#include "RenderNode.h"
-#include "ResourceCache.h"
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
-// Debug
-#if DEBUG_DISPLAY_LIST
- #define DISPLAY_LIST_LOGD(...) ALOGD(__VA_ARGS__)
-#else
- #define DISPLAY_LIST_LOGD(...)
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-// Display list
-///////////////////////////////////////////////////////////////////////////////
-
-class DeferredDisplayList;
-class DeferredLayerUpdater;
-class DisplayListRenderer;
-class DisplayListOp;
-class DrawOp;
-class StateOp;
-
-/**
- * Records drawing commands in a display list for later playback into an OpenGLRenderer.
- */
-class ANDROID_API DisplayListRenderer: public StatefulBaseRenderer {
-public:
- DisplayListRenderer();
- virtual ~DisplayListRenderer();
-
- void insertReorderBarrier(bool enableReorder);
-
- DisplayListData* finishRecording();
-
-// ----------------------------------------------------------------------------
-// Frame state operations
-// ----------------------------------------------------------------------------
- virtual status_t prepareDirty(float left, float top, float right, float bottom, bool opaque);
- virtual void finish();
- virtual void interrupt();
- virtual void resume();
-
-// ----------------------------------------------------------------------------
-// Canvas state operations
-// ----------------------------------------------------------------------------
- // Save (layer)
- virtual int save(int flags);
- virtual void restore();
- virtual void restoreToCount(int saveCount);
- virtual int saveLayer(float left, float top, float right, float bottom,
- const SkPaint* paint, int flags);
-
- // Matrix
- virtual void translate(float dx, float dy, float dz = 0.0f);
- virtual void rotate(float degrees);
- virtual void scale(float sx, float sy);
- virtual void skew(float sx, float sy);
-
- virtual void setMatrix(const SkMatrix& matrix);
- virtual void concatMatrix(const SkMatrix& matrix);
-
- // Clip
- virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
- virtual bool clipPath(const SkPath* path, SkRegion::Op op);
- virtual bool clipRegion(const SkRegion* region, SkRegion::Op op);
-
- // Misc - should be implemented with SkPaint inspection
- virtual void resetPaintFilter();
- virtual void setupPaintFilter(int clearBits, int setBits);
-
- bool isCurrentTransformSimple() {
- return currentTransform()->isSimple();
- }
-
-// ----------------------------------------------------------------------------
-// Canvas draw operations
-// ----------------------------------------------------------------------------
- virtual status_t drawColor(int color, SkXfermode::Mode mode);
-
- // Bitmap-based
- virtual status_t drawBitmap(const SkBitmap* bitmap, const SkPaint* paint);
- virtual status_t drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop,
- float srcRight, float srcBottom, float dstLeft, float dstTop,
- float dstRight, float dstBottom, const SkPaint* paint);
- virtual status_t drawBitmapData(const SkBitmap* bitmap, const SkPaint* paint);
- virtual status_t drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight,
- const float* vertices, const int* colors, const SkPaint* paint);
- virtual status_t drawPatch(const SkBitmap* bitmap, const Res_png_9patch* patch,
- float left, float top, float right, float bottom, const SkPaint* paint);
-
- // Shapes
- virtual status_t drawRect(float left, float top, float right, float bottom,
- const SkPaint* paint);
- virtual status_t drawRects(const float* rects, int count, const SkPaint* paint);
- virtual status_t drawRoundRect(float left, float top, float right, float bottom,
- float rx, float ry, const SkPaint* paint);
- virtual status_t drawRoundRect(CanvasPropertyPrimitive* left, CanvasPropertyPrimitive* top,
- CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom,
- CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry,
- CanvasPropertyPaint* paint);
- virtual status_t drawCircle(float x, float y, float radius, const SkPaint* paint);
- virtual status_t drawCircle(CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y,
- CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint);
- virtual status_t drawOval(float left, float top, float right, float bottom,
- const SkPaint* paint);
- virtual status_t drawArc(float left, float top, float right, float bottom,
- float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint);
- virtual status_t drawPath(const SkPath* path, const SkPaint* paint);
- virtual status_t drawLines(const float* points, int count, const SkPaint* paint);
- virtual status_t drawPoints(const float* points, int count, const SkPaint* paint);
-
- // Text
- virtual status_t drawText(const char* text, int bytesCount, int count, float x, float y,
- const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds,
- DrawOpMode drawOpMode = kDrawOpMode_Immediate);
- virtual status_t drawTextOnPath(const char* text, int bytesCount, int count, const SkPath* path,
- float hOffset, float vOffset, const SkPaint* paint);
- virtual status_t drawPosText(const char* text, int bytesCount, int count,
- const float* positions, const SkPaint* paint);
-
-// ----------------------------------------------------------------------------
-// Canvas draw operations - special
-// ----------------------------------------------------------------------------
- virtual status_t drawLayer(DeferredLayerUpdater* layerHandle, float x, float y);
- virtual status_t drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t replayFlags);
-
- // TODO: rename for consistency
- virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty);
-
- void setHighContrastText(bool highContrastText) {
- mHighContrastText = highContrastText;
- }
-private:
- enum DeferredBarrierType {
- kBarrier_None,
- kBarrier_InOrder,
- kBarrier_OutOfOrder,
- };
-
- void flushRestoreToCount();
- void flushTranslate();
- void flushReorderBarrier();
-
- LinearAllocator& alloc() { return mDisplayListData->allocator; }
-
- // Each method returns final index of op
- size_t addOpAndUpdateChunk(DisplayListOp* op);
- // flushes any deferred operations, and appends the op
- size_t flushAndAddOp(DisplayListOp* op);
-
- size_t addStateOp(StateOp* op);
- size_t addDrawOp(DrawOp* op);
- size_t addRenderNodeOp(DrawRenderNodeOp* op);
-
-
- template<class T>
- inline const T* refBuffer(const T* srcBuffer, int32_t count) {
- if (!srcBuffer) return NULL;
-
- T* dstBuffer = (T*) mDisplayListData->allocator.alloc(count * sizeof(T));
- memcpy(dstBuffer, srcBuffer, count * sizeof(T));
- return dstBuffer;
- }
-
- inline char* refText(const char* text, size_t byteLength) {
- return (char*) refBuffer<uint8_t>((uint8_t*)text, byteLength);
- }
-
- inline const SkPath* refPath(const SkPath* path) {
- if (!path) return NULL;
-
- const SkPath* pathCopy = mPathMap.valueFor(path);
- if (pathCopy == NULL || pathCopy->getGenerationID() != path->getGenerationID()) {
- SkPath* newPathCopy = new SkPath(*path);
- newPathCopy->setSourcePath(path);
-
- pathCopy = newPathCopy;
- // replaceValueFor() performs an add if the entry doesn't exist
- mPathMap.replaceValueFor(path, pathCopy);
- mDisplayListData->paths.add(pathCopy);
- }
- if (mDisplayListData->sourcePaths.indexOf(path) < 0) {
- mResourceCache.incrementRefcount(path);
- mDisplayListData->sourcePaths.add(path);
- }
- return pathCopy;
- }
-
- inline const SkPaint* refPaint(const SkPaint* paint) {
- if (!paint) return NULL;
-
- const SkPaint* paintCopy = mPaintMap.valueFor(paint);
- if (paintCopy == NULL
- || paintCopy->getGenerationID() != paint->getGenerationID()
- // We can't compare shader pointers because that will always
- // change as we do partial copying via wrapping. However, if the
- // shader changes the paint generationID will have changed and
- // so we don't hit this comparison anyway
- || !(paint->getShader() && paintCopy->getShader()
- && paint->getShader()->getGenerationID() == paintCopy->getShader()->getGenerationID())) {
- paintCopy = copyPaint(paint);
- // replaceValueFor() performs an add if the entry doesn't exist
- mPaintMap.replaceValueFor(paint, paintCopy);
- }
-
- return paintCopy;
- }
-
- inline SkPaint* copyPaint(const SkPaint* paint) {
- if (!paint) return NULL;
- SkPaint* paintCopy = new SkPaint(*paint);
- if (paint->getShader()) {
- SkShader* shaderCopy = SkShader::CreateLocalMatrixShader(
- paint->getShader(), paint->getShader()->getLocalMatrix());
- paintCopy->setShader(shaderCopy);
- paintCopy->setGenerationID(paint->getGenerationID());
- shaderCopy->setGenerationID(paint->getShader()->getGenerationID());
- shaderCopy->unref();
- }
- mDisplayListData->paints.add(paintCopy);
- return paintCopy;
- }
-
- inline const SkRegion* refRegion(const SkRegion* region) {
- if (!region) {
- return region;
- }
-
- const SkRegion* regionCopy = mRegionMap.valueFor(region);
- // TODO: Add generation ID to SkRegion
- if (regionCopy == NULL) {
- regionCopy = new SkRegion(*region);
- // replaceValueFor() performs an add if the entry doesn't exist
- mRegionMap.replaceValueFor(region, regionCopy);
- mDisplayListData->regions.add(regionCopy);
- }
-
- return regionCopy;
- }
-
- inline const SkBitmap* refBitmap(const SkBitmap* bitmap) {
- // Note that this assumes the bitmap is immutable. There are cases this won't handle
- // correctly, such as creating the bitmap from scratch, drawing with it, changing its
- // contents, and drawing again. The only fix would be to always copy it the first time,
- // which doesn't seem worth the extra cycles for this unlikely case.
- mDisplayListData->bitmapResources.add(bitmap);
- mResourceCache.incrementRefcount(bitmap);
- return bitmap;
- }
-
- inline const SkBitmap* refBitmapData(const SkBitmap* bitmap) {
- mDisplayListData->ownedBitmapResources.add(bitmap);
- mResourceCache.incrementRefcount(bitmap);
- return bitmap;
- }
-
- inline const Res_png_9patch* refPatch(const Res_png_9patch* patch) {
- mDisplayListData->patchResources.add(patch);
- mResourceCache.incrementRefcount(patch);
- return patch;
- }
-
- DefaultKeyedVector<const SkPaint*, const SkPaint*> mPaintMap;
- DefaultKeyedVector<const SkPath*, const SkPath*> mPathMap;
- DefaultKeyedVector<const SkRegion*, const SkRegion*> mRegionMap;
-
- ResourceCache& mResourceCache;
- DisplayListData* mDisplayListData;
-
- float mTranslateX;
- float mTranslateY;
- bool mHasDeferredTranslate;
- DeferredBarrierType mDeferredBarrierType;
- bool mHighContrastText;
-
- int mRestoreSaveCount;
-
- friend class RenderNode;
-
-}; // class DisplayListRenderer
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_DISPLAY_LIST_RENDERER_H
diff --git a/libs/hwui/Dither.cpp b/libs/hwui/Dither.cpp
index 77006a4..1ba6511 100644
--- a/libs/hwui/Dither.cpp
+++ b/libs/hwui/Dither.cpp
@@ -24,15 +24,16 @@ namespace uirenderer {
// Lifecycle
///////////////////////////////////////////////////////////////////////////////
-Dither::Dither(): mCaches(NULL), mInitialized(false), mDitherTexture(0) {
+Dither::Dither(Caches& caches)
+ : mCaches(caches)
+ , mInitialized(false)
+ , mDitherTexture(0) {
}
void Dither::bindDitherTexture() {
if (!mInitialized) {
- bool useFloatTexture = Extensions::getInstance().hasFloatTextures();
-
glGenTextures(1, &mDitherTexture);
- mCaches->bindTexture(mDitherTexture);
+ mCaches.textureState().bindTexture(mDitherTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
@@ -40,7 +41,7 @@ void Dither::bindDitherTexture() {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
- if (useFloatTexture) {
+ if (mCaches.extensions().hasFloatTextures()) {
// We use a R16F texture, let's remap the alpha channel to the
// red channel to avoid changing the shader sampling code on GL ES 3.0+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_RED);
@@ -71,13 +72,13 @@ void Dither::bindDitherTexture() {
mInitialized = true;
} else {
- mCaches->bindTexture(mDitherTexture);
+ mCaches.textureState().bindTexture(mDitherTexture);
}
}
void Dither::clear() {
if (mInitialized) {
- mCaches->deleteTexture(mDitherTexture);
+ mCaches.textureState().deleteTexture(mDitherTexture);
mInitialized = false;
}
}
@@ -86,15 +87,13 @@ void Dither::clear() {
// Program management
///////////////////////////////////////////////////////////////////////////////
-void Dither::setupProgram(Program* program, GLuint* textureUnit) {
- if (!mCaches) mCaches = &Caches::getInstance();
-
+void Dither::setupProgram(Program& program, GLuint* textureUnit) {
GLuint textureSlot = (*textureUnit)++;
- mCaches->activeTexture(textureSlot);
+ mCaches.textureState().activateTexture(textureSlot);
bindDitherTexture();
- glUniform1i(program->getUniform("ditherSampler"), textureSlot);
+ glUniform1i(program.getUniform("ditherSampler"), textureSlot);
}
}; // namespace uirenderer
diff --git a/libs/hwui/Dither.h b/libs/hwui/Dither.h
index 546236b..b589b80 100644
--- a/libs/hwui/Dither.h
+++ b/libs/hwui/Dither.h
@@ -19,12 +19,12 @@
#include <GLES3/gl3.h>
-#include "Program.h"
-
namespace android {
namespace uirenderer {
class Caches;
+class Extensions;
+class Program;
// Must be a power of two
#define DITHER_KERNEL_SIZE 4
@@ -37,15 +37,15 @@ class Caches;
*/
class Dither {
public:
- Dither();
+ Dither(Caches& caches);
void clear();
- void setupProgram(Program* program, GLuint* textureUnit);
+ void setupProgram(Program& program, GLuint* textureUnit);
private:
void bindDitherTexture();
- Caches* mCaches;
+ Caches& mCaches;
bool mInitialized;
GLuint mDitherTexture;
};
diff --git a/libs/hwui/DrawProfiler.cpp b/libs/hwui/DrawProfiler.cpp
index e590642..ecde5ff 100644
--- a/libs/hwui/DrawProfiler.cpp
+++ b/libs/hwui/DrawProfiler.cpp
@@ -59,7 +59,7 @@ static int dpToPx(int dp, float density) {
DrawProfiler::DrawProfiler()
: mType(kNone)
, mDensity(0)
- , mData(NULL)
+ , mData(nullptr)
, mDataSize(0)
, mCurrentFrame(-1)
, mPreviousTime(0)
@@ -160,7 +160,7 @@ void DrawProfiler::createData() {
void DrawProfiler::destroyData() {
delete mData;
- mData = NULL;
+ mData = nullptr;
}
void DrawProfiler::addRect(Rect& r, float data, float* shapeOutput) {
diff --git a/libs/hwui/Extensions.cpp b/libs/hwui/Extensions.cpp
index 84e7e65..2a82216 100644
--- a/libs/hwui/Extensions.cpp
+++ b/libs/hwui/Extensions.cpp
@@ -16,18 +16,16 @@
#define LOG_TAG "OpenGLRenderer"
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
+#include "Extensions.h"
+
+#include "Debug.h"
+#include "Properties.h"
#include <EGL/egl.h>
#include <EGL/eglext.h>
-
+#include <GLES2/gl2ext.h>
#include <utils/Log.h>
-#include "Debug.h"
-#include "Extensions.h"
-#include "Properties.h"
-
namespace android {
using namespace uirenderer;
@@ -50,14 +48,13 @@ namespace uirenderer {
// Constructors
///////////////////////////////////////////////////////////////////////////////
-Extensions::Extensions(): Singleton<Extensions>() {
+Extensions::Extensions() {
// Query GL extensions
findExtensions((const char*) glGetString(GL_EXTENSIONS), mGlExtensionList);
mHasNPot = hasGlExtension("GL_OES_texture_npot");
mHasFramebufferFetch = hasGlExtension("GL_NV_shader_framebuffer_fetch");
mHasDiscardFramebuffer = hasGlExtension("GL_EXT_discard_framebuffer");
mHasDebugMarker = hasGlExtension("GL_EXT_debug_marker");
- mHasDebugLabel = hasGlExtension("GL_EXT_debug_label");
mHasTiledRendering = hasGlExtension("GL_QCOM_tiled_rendering");
mHas1BitStencil = hasGlExtension("GL_OES_stencil1");
mHas4BitStencil = hasGlExtension("GL_OES_stencil4");
@@ -66,7 +63,7 @@ Extensions::Extensions(): Singleton<Extensions>() {
findExtensions(eglQueryString(eglGetCurrentDisplay(), EGL_EXTENSIONS), mEglExtensionList);
char property[PROPERTY_VALUE_MAX];
- if (property_get(PROPERTY_DEBUG_NV_PROFILING, property, NULL) > 0) {
+ if (property_get(PROPERTY_DEBUG_NV_PROFILING, property, nullptr) > 0) {
mHasNvSystemTime = !strcmp(property, "true") && hasEglExtension("EGL_NV_system_time");
} else {
mHasNvSystemTime = false;
@@ -93,9 +90,6 @@ Extensions::Extensions(): Singleton<Extensions>() {
}
}
-Extensions::~Extensions() {
-}
-
///////////////////////////////////////////////////////////////////////////////
// Methods
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h
index 25d4c5e..e7d317d 100644
--- a/libs/hwui/Extensions.h
+++ b/libs/hwui/Extensions.h
@@ -23,6 +23,8 @@
#include <utils/SortedVector.h>
#include <utils/String8.h>
+#include <GLES2/gl2.h>
+
namespace android {
namespace uirenderer {
@@ -30,13 +32,14 @@ namespace uirenderer {
// Classes
///////////////////////////////////////////////////////////////////////////////
-class ANDROID_API Extensions: public Singleton<Extensions> {
+class ANDROID_API Extensions {
public:
+ Extensions();
+
inline bool hasNPot() const { return mHasNPot; }
inline bool hasFramebufferFetch() const { return mHasFramebufferFetch; }
inline bool hasDiscardFramebuffer() const { return mHasDiscardFramebuffer; }
inline bool hasDebugMarker() const { return mHasDebugMarker; }
- inline bool hasDebugLabel() const { return mHasDebugLabel; }
inline bool hasTiledRendering() const { return mHasTiledRendering; }
inline bool has1BitStencil() const { return mHas1BitStencil; }
inline bool has4BitStencil() const { return mHas4BitStencil; }
@@ -55,13 +58,8 @@ public:
void dump() const;
private:
- Extensions();
- ~Extensions();
-
void findExtensions(const char* extensions, SortedVector<String8>& list) const;
- friend class Singleton<Extensions>;
-
SortedVector<String8> mGlExtensionList;
SortedVector<String8> mEglExtensionList;
@@ -69,7 +67,6 @@ private:
bool mHasFramebufferFetch;
bool mHasDiscardFramebuffer;
bool mHasDebugMarker;
- bool mHasDebugLabel;
bool mHasTiledRendering;
bool mHas1BitStencil;
bool mHas4BitStencil;
diff --git a/libs/hwui/FboCache.cpp b/libs/hwui/FboCache.cpp
index beef7be..b54d532 100644
--- a/libs/hwui/FboCache.cpp
+++ b/libs/hwui/FboCache.cpp
@@ -31,7 +31,7 @@ namespace uirenderer {
FboCache::FboCache(): mMaxSize(DEFAULT_FBO_CACHE_SIZE) {
char property[PROPERTY_VALUE_MAX];
- if (property_get(PROPERTY_FBO_CACHE_SIZE, property, NULL) > 0) {
+ if (property_get(PROPERTY_FBO_CACHE_SIZE, property, nullptr) > 0) {
INIT_LOGD(" Setting fbo cache size to %s", property);
mMaxSize = atoi(property);
} else {
diff --git a/libs/hwui/Fence.h b/libs/hwui/Fence.h
deleted file mode 100644
index f175e98..0000000
--- a/libs/hwui/Fence.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2013 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_FENCE_H
-#define ANDROID_HWUI_FENCE_H
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-
-namespace android {
-namespace uirenderer {
-
-/**
- * Creating a Fence instance inserts a new sync fence in the OpenGL
- * commands stream. The caller can then wait for the fence to be signaled
- * by calling the wait method.
- */
-class Fence {
-public:
- enum {
- /**
- * Default timeout in nano-seconds for wait()
- */
- kDefaultTimeout = 1000000000
- };
-
- /**
- * Inserts a new sync fence in the OpenGL commands stream.
- */
- Fence() {
- mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- if (mDisplay != EGL_NO_DISPLAY) {
- mFence = eglCreateSyncKHR(mDisplay, EGL_SYNC_FENCE_KHR, NULL);
- } else {
- mFence = EGL_NO_SYNC_KHR;
- }
- }
-
- /**
- * Destroys the fence. Any caller waiting on the fence will be
- * signaled immediately.
- */
- ~Fence() {
- if (mFence != EGL_NO_SYNC_KHR) {
- eglDestroySyncKHR(mDisplay, mFence);
- }
- }
-
- /**
- * Blocks the calling thread until this fence is signaled, or until
- * <timeout> nanoseconds have passed.
- *
- * Returns true if waiting for the fence was successful, false if
- * a timeout or an error occurred.
- */
- bool wait(EGLTimeKHR timeout = kDefaultTimeout) {
- EGLint waitStatus = eglClientWaitSyncKHR(mDisplay, mFence,
- EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, timeout);
- if (waitStatus == EGL_FALSE) {
- ALOGW("Failed to wait for the fence %#x", eglGetError());
- }
- return waitStatus == EGL_CONDITION_SATISFIED_KHR;
- }
-
-private:
- EGLDisplay mDisplay;
- EGLSyncKHR mFence;
-
-}; // class Fence
-
-/**
- * An AutoFence creates a Fence instance and waits for the fence
- * to be signaled when the AutoFence is destroyed. This is useful
- * to automatically wait for a series of OpenGL commands to be
- * executed. For example:
- *
- * void drawAndWait() {
- * glDrawElements();
- * AutoFence fence;
- * }
- */
-class AutoFence {
-public:
- AutoFence(EGLTimeKHR timeout = Fence::kDefaultTimeout): mTimeout(timeout) {
- }
-
- ~AutoFence() {
- mFence.wait(mTimeout);
- }
-
-private:
- EGLTimeKHR mTimeout;
- Fence mFence;
-
-}; // class AutoFence
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_FENCE_H
diff --git a/libs/hwui/FloatColor.h b/libs/hwui/FloatColor.h
new file mode 100644
index 0000000..97dec88
--- /dev/null
+++ b/libs/hwui/FloatColor.h
@@ -0,0 +1,52 @@
+/*
+ * 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 FLOATCOLOR_H
+#define FLOATCOLOR_H
+
+#include "utils/Macros.h"
+
+#include <stdint.h>
+
+namespace android {
+namespace uirenderer {
+
+struct FloatColor {
+ void set(uint32_t color) {
+ a = ((color >> 24) & 0xff) / 255.0f;
+ r = a * ((color >> 16) & 0xff) / 255.0f;
+ g = a * ((color >> 8) & 0xff) / 255.0f;
+ b = a * ((color ) & 0xff) / 255.0f;
+ }
+
+ bool isNotBlack() {
+ return a < 1.0f
+ || r > 0.0f
+ || g > 0.0f
+ || b > 0.0f;
+ }
+
+ float r;
+ float g;
+ float b;
+ float a;
+};
+
+REQUIRE_COMPATIBLE_LAYOUT(FloatColor);
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* FLOATCOLOR_H */
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 3910381..c79ae77 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -14,7 +14,20 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
+#include "FontRenderer.h"
+
+#include "Caches.h"
+#include "Debug.h"
+#include "Extensions.h"
+#include "Glop.h"
+#include "GlopBuilder.h"
+#include "OpenGLRenderer.h"
+#include "PixelBuffer.h"
+#include "Rect.h"
+#include "renderstate/RenderState.h"
+#include "utils/Blur.h"
+#include "utils/MathUtils.h"
+#include "utils/Timing.h"
#include <SkGlyph.h>
#include <SkUtils.h>
@@ -27,17 +40,6 @@
#include <RenderScript.h>
#endif
-#include "utils/Blur.h"
-#include "utils/Timing.h"
-
-#include "Caches.h"
-#include "Debug.h"
-#include "Extensions.h"
-#include "FontRenderer.h"
-#include "OpenGLRenderer.h"
-#include "PixelBuffer.h"
-#include "Rect.h"
-
namespace android {
namespace uirenderer {
@@ -47,48 +49,23 @@ namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
// TextSetupFunctor
///////////////////////////////////////////////////////////////////////////////
-status_t TextSetupFunctor::operator ()(int what, void* data) {
- Data* typedData = reinterpret_cast<Data*>(data);
- GLenum glyphFormat = typedData ? typedData->glyphFormat : GL_ALPHA;
-
- renderer->setupDraw();
- renderer->setupDrawTextGamma(paint);
- renderer->setupDrawDirtyRegionsDisabled();
- renderer->setupDrawWithTexture(glyphFormat == GL_ALPHA);
- switch (glyphFormat) {
- case GL_ALPHA: {
- renderer->setupDrawAlpha8Color(paint->getColor(), alpha);
- break;
- }
- case GL_RGBA: {
- float floatAlpha = alpha / 255.0f;
- renderer->setupDrawColor(floatAlpha, floatAlpha, floatAlpha, floatAlpha);
- break;
- }
- default: {
-#if DEBUG_FONT_RENDERER
- ALOGD("TextSetupFunctor: called with unknown glyph format %x", glyphFormat);
-#endif
- break;
- }
- }
- renderer->setupDrawColorFilter(paint->getColorFilter());
- renderer->setupDrawShader(paint->getShader());
- renderer->setupDrawBlending(paint);
- renderer->setupDrawProgram();
- renderer->setupDrawModelView(kModelViewMode_Translate, false,
- 0.0f, 0.0f, 0.0f, 0.0f, pureTranslate);
- // Calling setupDrawTexture with the name 0 will enable the
- // uv attributes and increase the texture unit count
- // texture binding will be performed by the font renderer as
- // needed
- renderer->setupDrawTexture(0);
- renderer->setupDrawPureColorUniforms();
- renderer->setupDrawColorFilterUniforms(paint->getColorFilter());
- renderer->setupDrawShaderUniforms(paint->getShader(), pureTranslate);
- renderer->setupDrawTextGammaUniforms();
-
- return NO_ERROR;
+
+void TextDrawFunctor::draw(CacheTexture& texture, bool linearFiltering) {
+ int textureFillFlags = static_cast<int>(texture.getFormat() == GL_ALPHA
+ ? TextureFillFlags::kIsAlphaMaskTexture : TextureFillFlags::kNone);
+ if (linearFiltering) {
+ textureFillFlags |= TextureFillFlags::kForceFilter;
+ }
+ const Matrix4& transform = pureTranslate ? Matrix4::identity() : *(renderer->currentTransform());
+ Glop glop;
+ GlopBuilder(renderer->mRenderState, renderer->mCaches, &glop)
+ .setMeshTexturedIndexedQuads(texture.mesh(), texture.meshElementCount())
+ .setFillTexturePaint(texture.getTexture(), textureFillFlags, paint, renderer->currentSnapshot()->alpha)
+ .setTransform(renderer->currentSnapshot()->getOrthoMatrix(), transform, false)
+ .setModelViewOffsetRect(0, 0, Rect(0, 0, 0, 0))
+ .setRoundRectClipState(renderer->currentSnapshot()->roundRectClipState)
+ .build();
+ renderer->renderGlop(glop);
}
///////////////////////////////////////////////////////////////////////////////
@@ -97,47 +74,51 @@ status_t TextSetupFunctor::operator ()(int what, void* data) {
static bool sLogFontRendererCreate = true;
-FontRenderer::FontRenderer() :
- mActiveFonts(LruCache<Font::FontDescription, Font*>::kUnlimitedCapacity) {
+FontRenderer::FontRenderer()
+ : mGammaTable(nullptr)
+ , mCurrentFont(nullptr)
+ , mActiveFonts(LruCache<Font::FontDescription, Font*>::kUnlimitedCapacity)
+ , mCurrentCacheTexture(nullptr)
+ , mUploadTexture(false)
+ , mFunctor(nullptr)
+ , mClip(nullptr)
+ , mBounds(nullptr)
+ , mDrawn(false)
+ , mInitialized(false)
+ , mLinearFiltering(false) {
if (sLogFontRendererCreate) {
INIT_LOGD("Creating FontRenderer");
}
- mGammaTable = NULL;
- mInitialized = false;
-
- mCurrentCacheTexture = NULL;
-
- mLinearFiltering = false;
-
mSmallCacheWidth = DEFAULT_TEXT_SMALL_CACHE_WIDTH;
mSmallCacheHeight = DEFAULT_TEXT_SMALL_CACHE_HEIGHT;
mLargeCacheWidth = DEFAULT_TEXT_LARGE_CACHE_WIDTH;
mLargeCacheHeight = DEFAULT_TEXT_LARGE_CACHE_HEIGHT;
char property[PROPERTY_VALUE_MAX];
- if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, NULL) > 0) {
+ if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, nullptr) > 0) {
mSmallCacheWidth = atoi(property);
}
- if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, NULL) > 0) {
+ if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, nullptr) > 0) {
mSmallCacheHeight = atoi(property);
}
- if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, NULL) > 0) {
+ if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, nullptr) > 0) {
mLargeCacheWidth = atoi(property);
}
- if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, NULL) > 0) {
+ if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, nullptr) > 0) {
mLargeCacheHeight = atoi(property);
}
uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize;
- mSmallCacheWidth = mSmallCacheWidth > maxTextureSize ? maxTextureSize : mSmallCacheWidth;
- mSmallCacheHeight = mSmallCacheHeight > maxTextureSize ? maxTextureSize : mSmallCacheHeight;
- mLargeCacheWidth = mLargeCacheWidth > maxTextureSize ? maxTextureSize : mLargeCacheWidth;
- mLargeCacheHeight = mLargeCacheHeight > maxTextureSize ? maxTextureSize : mLargeCacheHeight;
+
+ mSmallCacheWidth = MathUtils::min(mSmallCacheWidth, maxTextureSize);
+ mSmallCacheHeight = MathUtils::min(mSmallCacheHeight, maxTextureSize);
+ mLargeCacheWidth = MathUtils::min(mLargeCacheWidth, maxTextureSize);
+ mLargeCacheHeight = MathUtils::min(mLargeCacheHeight, maxTextureSize);
if (sLogFontRendererCreate) {
INIT_LOGD(" Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i",
@@ -183,6 +164,8 @@ void FontRenderer::flushAllAndInvalidate() {
for (uint32_t i = 0; i < mRGBACacheTextures.size(); i++) {
mRGBACacheTextures[i]->init();
}
+
+ mDrawn = false;
}
void FontRenderer::flushLargeCaches(Vector<CacheTexture*>& cacheTextures) {
@@ -195,7 +178,7 @@ void FontRenderer::flushLargeCaches(Vector<CacheTexture*>& cacheTextures) {
while (it.next()) {
it.value()->invalidateTextureCache(cacheTexture);
}
- cacheTexture->releaseTexture();
+ cacheTexture->releasePixelBuffer();
}
}
}
@@ -213,7 +196,7 @@ CacheTexture* FontRenderer::cacheBitmapInTexture(Vector<CacheTexture*>& cacheTex
}
}
// Could not fit glyph into current cache textures
- return NULL;
+ return nullptr;
}
void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
@@ -224,7 +207,7 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp
// so we can avoid doing extra work later on
if (glyph.fWidth == 0 || glyph.fHeight == 0) {
cachedGlyph->mIsValid = true;
- cachedGlyph->mCacheTexture = NULL;
+ cachedGlyph->mCacheTexture = nullptr;
return;
}
@@ -232,7 +215,7 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp
// choose an appropriate cache texture list for this glyph format
SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
- Vector<CacheTexture*>* cacheTextures = NULL;
+ Vector<CacheTexture*>* cacheTextures = nullptr;
switch (format) {
case SkMask::kA8_Format:
case SkMask::kBW_Format:
@@ -287,9 +270,9 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp
uint32_t cacheWidth = cacheTexture->getWidth();
if (!cacheTexture->getPixelBuffer()) {
- Caches::getInstance().activeTexture(0);
+ Caches::getInstance().textureState().activateTexture(0);
// Large-glyph texture memory is allocated only as needed
- cacheTexture->allocateTexture();
+ cacheTexture->allocatePixelBuffer();
}
if (!cacheTexture->mesh()) {
cacheTexture->allocateMesh();
@@ -358,7 +341,7 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp
break;
}
case SkMask::kBW_Format: {
- uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
+ uint32_t cacheX = 0, cacheY = 0;
uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX
- TEXTURE_BORDER_SIZE;
static const uint8_t COLORS[2] = { 0, 255 };
@@ -397,11 +380,11 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp
CacheTexture* FontRenderer::createCacheTexture(int width, int height, GLenum format,
bool allocate) {
- CacheTexture* cacheTexture = new CacheTexture(width, height, format, gMaxNumberOfQuads);
+ CacheTexture* cacheTexture = new CacheTexture(width, height, format, kMaxNumberOfQuads);
if (allocate) {
- Caches::getInstance().activeTexture(0);
- cacheTexture->allocateTexture();
+ Caches::getInstance().textureState().activateTexture(0);
+ cacheTexture->allocatePixelBuffer();
cacheTexture->allocateMesh();
}
@@ -446,8 +429,8 @@ void checkTextureUpdateForCache(Caches& caches, Vector<CacheTexture*>& cacheText
if (cacheTexture->isDirty() && cacheTexture->getPixelBuffer()) {
if (cacheTexture->getTextureId() != lastTextureId) {
lastTextureId = cacheTexture->getTextureId();
- caches.activeTexture(0);
- caches.bindTexture(lastTextureId);
+ caches.textureState().activateTexture(0);
+ caches.textureState().bindTexture(lastTextureId);
}
if (cacheTexture->upload()) {
@@ -473,7 +456,7 @@ void FontRenderer::checkTextureUpdate() {
checkTextureUpdateForCache(caches, mRGBACacheTextures, resetPixelStore, lastTextureId);
// Unbind any PBO we might have used to update textures
- caches.unbindPixelBuffer();
+ caches.pixelBufferState().unbind();
// Reset to default unpack row length to avoid affecting texture
// uploads in other parts of the renderer
@@ -485,44 +468,23 @@ void FontRenderer::checkTextureUpdate() {
}
void FontRenderer::issueDrawCommand(Vector<CacheTexture*>& cacheTextures) {
- Caches& caches = Caches::getInstance();
+ if (!mFunctor) return;
+
bool first = true;
- bool force = false;
+ bool forceRebind = false;
for (uint32_t i = 0; i < cacheTextures.size(); i++) {
CacheTexture* texture = cacheTextures[i];
if (texture->canDraw()) {
if (first) {
- if (mFunctor) {
- TextSetupFunctor::Data functorData(texture->getFormat());
- (*mFunctor)(0, &functorData);
- }
-
checkTextureUpdate();
- caches.bindQuadIndicesBuffer();
-
- if (!mDrawn) {
- // If returns true, a VBO was bound and we must
- // rebind our vertex attrib pointers even if
- // they have the same values as the current pointers
- force = caches.unbindMeshBuffer();
- }
-
- caches.activeTexture(0);
first = false;
+ mDrawn = true;
}
- caches.bindTexture(texture->getTextureId());
- texture->setLinearFiltering(mLinearFiltering, false);
-
- TextureVertex* mesh = texture->mesh();
- caches.bindPositionVertexPointer(force, &mesh[0].x);
- caches.bindTexCoordsVertexPointer(force, &mesh[0].u);
- force = false;
-
- glDrawElements(GL_TRIANGLES, texture->meshElementCount(),
- GL_UNSIGNED_SHORT, texture->indices());
+ mFunctor->draw(*texture, mLinearFiltering);
texture->resetMesh();
+ forceRebind = false;
}
}
}
@@ -530,8 +492,6 @@ void FontRenderer::issueDrawCommand(Vector<CacheTexture*>& cacheTextures) {
void FontRenderer::issueDrawCommand() {
issueDrawCommand(mACacheTextures);
issueDrawCommand(mRGBACacheTextures);
-
- mDrawn = true;
}
void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
@@ -598,7 +558,7 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, co
DropShadow image;
image.width = 0;
image.height = 0;
- image.image = NULL;
+ image.image = nullptr;
image.penX = 0;
image.penY = 0;
@@ -607,8 +567,8 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, co
}
mDrawn = false;
- mClip = NULL;
- mBounds = NULL;
+ mClip = nullptr;
+ mBounds = nullptr;
Rect bounds;
mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
@@ -644,10 +604,10 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, co
// NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted
// TODO: don't draw pure whitespace in the first place, and avoid needing this check
mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
- Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
+ Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, nullptr, positions);
// Unbind any PBO we might have used
- Caches::getInstance().unbindPixelBuffer();
+ Caches::getInstance().pixelBufferState().unbind();
blurImage(&dataBuffer, paddedWidth, paddedHeight, radius);
}
@@ -661,7 +621,7 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, co
return image;
}
-void FontRenderer::initRender(const Rect* clip, Rect* bounds, Functor* functor) {
+void FontRenderer::initRender(const Rect* clip, Rect* bounds, TextDrawFunctor* functor) {
checkInit();
mDrawn = false;
@@ -671,8 +631,8 @@ void FontRenderer::initRender(const Rect* clip, Rect* bounds, Functor* functor)
}
void FontRenderer::finishRender() {
- mBounds = NULL;
- mClip = NULL;
+ mBounds = nullptr;
+ mClip = nullptr;
issueDrawCommand();
}
@@ -689,7 +649,7 @@ void FontRenderer::endPrecaching() {
bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const char *text,
uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
- const float* positions, Rect* bounds, Functor* functor, bool forceFinish) {
+ const float* positions, Rect* bounds, TextDrawFunctor* functor, bool forceFinish) {
if (!mCurrentFont) {
ALOGE("No font set");
return false;
@@ -707,7 +667,7 @@ bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const c
bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, const char *text,
uint32_t startIndex, uint32_t len, int numGlyphs, const SkPath* path,
- float hOffset, float vOffset, Rect* bounds, Functor* functor) {
+ float hOffset, float vOffset, Rect* bounds, TextDrawFunctor* functor) {
if (!mCurrentFont) {
ALOGE("No font set");
return false;
@@ -724,7 +684,7 @@ void FontRenderer::removeFont(const Font* font) {
mActiveFonts.remove(font->getDescription());
if (mCurrentFont == font) {
- mCurrentFont = NULL;
+ mCurrentFont = nullptr;
}
}
@@ -734,7 +694,7 @@ void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, flo
if (width * height * intRadius >= RS_MIN_INPUT_CUTOFF) {
uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
- if (mRs == 0) {
+ if (mRs == nullptr) {
mRs = new RSC::RS();
// a null path is OK because there are no custom kernels used
// hence nothing gets cached by RS
@@ -746,7 +706,7 @@ void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, flo
mRsScript = RSC::ScriptIntrinsicBlur::create(mRs, mRsElement);
}
}
- if (mRs != 0) {
+ if (mRs != nullptr) {
RSC::sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0);
RSC::sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t,
RS_ALLOCATION_MIPMAP_NONE,
@@ -770,15 +730,12 @@ void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, flo
}
#endif
- float *gaussian = new float[2 * intRadius + 1];
- Blur::generateGaussianWeights(gaussian, intRadius);
-
- uint8_t* scratch = new uint8_t[width * height];
- Blur::horizontal(gaussian, intRadius, *image, scratch, width, height);
- Blur::vertical(gaussian, intRadius, scratch, *image, width, height);
+ std::unique_ptr<float[]> gaussian(new float[2 * intRadius + 1]);
+ Blur::generateGaussianWeights(gaussian.get(), intRadius);
- delete[] gaussian;
- delete[] scratch;
+ std::unique_ptr<uint8_t[]> scratch(new uint8_t[width * height]);
+ Blur::horizontal(gaussian.get(), intRadius, *image, scratch.get(), width, height);
+ Blur::vertical(gaussian.get(), intRadius, scratch.get(), *image, width, height);
}
static uint32_t calculateCacheSize(const Vector<CacheTexture*>& cacheTextures) {
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 5c96c6b..dfb107c 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -17,7 +17,12 @@
#ifndef ANDROID_HWUI_FONT_RENDERER_H
#define ANDROID_HWUI_FONT_RENDERER_H
-#include <utils/Functor.h>
+#include "font/FontUtil.h"
+#include "font/CacheTexture.h"
+#include "font/CachedGlyphInfo.h"
+#include "font/Font.h"
+#include "utils/SortedList.h"
+
#include <utils/LruCache.h>
#include <utils/Vector.h>
#include <utils/StrongPointer.h>
@@ -26,14 +31,6 @@
#include <GLES2/gl2.h>
-#include "font/FontUtil.h"
-#include "font/CacheTexture.h"
-#include "font/CachedGlyphInfo.h"
-#include "font/Font.h"
-#include "utils/SortedList.h"
-#include "Matrix.h"
-#include "Properties.h"
-
#ifdef ANDROID_ENABLE_RENDERSCRIPT
#include "RenderScript.h"
namespace RSC {
@@ -49,26 +46,20 @@ namespace uirenderer {
class OpenGLRenderer;
-///////////////////////////////////////////////////////////////////////////////
-// TextSetupFunctor
-///////////////////////////////////////////////////////////////////////////////
-class TextSetupFunctor: public Functor {
+class TextDrawFunctor {
public:
- struct Data {
- Data(GLenum glyphFormat) : glyphFormat(glyphFormat) {
- }
-
- GLenum glyphFormat;
- };
-
- TextSetupFunctor(OpenGLRenderer* renderer, float x, float y, bool pureTranslate,
- int alpha, SkXfermode::Mode mode, const SkPaint* paint): Functor(),
- renderer(renderer), x(x), y(y), pureTranslate(pureTranslate),
- alpha(alpha), mode(mode), paint(paint) {
+ TextDrawFunctor(OpenGLRenderer* renderer, float x, float y, bool pureTranslate,
+ int alpha, SkXfermode::Mode mode, const SkPaint* paint)
+ : renderer(renderer)
+ , x(x)
+ , y(y)
+ , pureTranslate(pureTranslate)
+ , alpha(alpha)
+ , mode(mode)
+ , paint(paint) {
}
- ~TextSetupFunctor() { }
- status_t operator ()(int what, void* data);
+ void draw(CacheTexture& texture, bool linearFiltering);
OpenGLRenderer* renderer;
float x;
@@ -79,10 +70,6 @@ public:
const SkPaint* paint;
};
-///////////////////////////////////////////////////////////////////////////////
-// FontRenderer
-///////////////////////////////////////////////////////////////////////////////
-
class FontRenderer {
public:
FontRenderer();
@@ -103,22 +90,14 @@ public:
// bounds is an out parameter
bool renderPosText(const SkPaint* paint, const Rect* clip, const char *text,
uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, const float* positions,
- Rect* bounds, Functor* functor, bool forceFinish = true);
+ Rect* bounds, TextDrawFunctor* functor, bool forceFinish = true);
// bounds is an out parameter
bool renderTextOnPath(const SkPaint* paint, const Rect* clip, const char *text,
uint32_t startIndex, uint32_t len, int numGlyphs, const SkPath* path,
- float hOffset, float vOffset, Rect* bounds, Functor* functor);
+ float hOffset, float vOffset, Rect* bounds, TextDrawFunctor* functor);
struct DropShadow {
- DropShadow() { };
-
- DropShadow(const DropShadow& dropShadow):
- width(dropShadow.width), height(dropShadow.height),
- image(dropShadow.image), penX(dropShadow.penX),
- penY(dropShadow.penY) {
- }
-
uint32_t width;
uint32_t height;
uint8_t* image;
@@ -154,7 +133,7 @@ private:
void flushAllAndInvalidate();
void checkInit();
- void initRender(const Rect* clip, Rect* bounds, Functor* functor);
+ void initRender(const Rect* clip, Rect* bounds, TextDrawFunctor* functor);
void finishRender();
void issueDrawCommand(Vector<CacheTexture*>& cacheTextures);
@@ -195,7 +174,7 @@ private:
bool mUploadTexture;
- Functor* mFunctor;
+ TextDrawFunctor* mFunctor;
const Rect* mClip;
Rect* mBounds;
bool mDrawn;
diff --git a/libs/hwui/FrameInfo.cpp b/libs/hwui/FrameInfo.cpp
new file mode 100644
index 0000000..6da1fa8
--- /dev/null
+++ b/libs/hwui/FrameInfo.cpp
@@ -0,0 +1,28 @@
+/*
+ * 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 "FrameInfo.h"
+
+#include <cstring>
+
+namespace android {
+namespace uirenderer {
+
+void FrameInfo::importUiThreadInfo(int64_t* info) {
+ memcpy(mFrameInfo, info, UI_THREAD_FRAME_INFO_SIZE * sizeof(int64_t));
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h
new file mode 100644
index 0000000..65daf03
--- /dev/null
+++ b/libs/hwui/FrameInfo.h
@@ -0,0 +1,125 @@
+/*
+ * 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 FRAMEINFO_H_
+#define FRAMEINFO_H_
+
+#include "utils/Macros.h"
+
+#include <cutils/compiler.h>
+#include <utils/Timers.h>
+
+#include <memory.h>
+
+namespace android {
+namespace uirenderer {
+
+#define UI_THREAD_FRAME_INFO_SIZE 9
+
+enum class FrameInfoIndex {
+ kFlags = 0,
+ kIntendedVsync,
+ kVsync,
+ kOldestInputEvent,
+ kNewestInputEvent,
+ kHandleInputStart,
+ kAnimationStart,
+ kPerformTraversalsStart,
+ kDrawStart,
+ // End of UI frame info
+
+ kSyncStart,
+ kIssueDrawCommandsStart,
+ kSwapBuffers,
+ kFrameCompleted,
+
+ // Must be the last value!
+ kNumIndexes
+};
+
+enum class FrameInfoFlags {
+ kWindowLayoutChanged = 1 << 0,
+ kRTAnimation = 1 << 1,
+ kSurfaceCanvas = 1 << 2,
+};
+MAKE_FLAGS_ENUM(FrameInfoFlags)
+
+class ANDROID_API UiFrameInfoBuilder {
+public:
+ UiFrameInfoBuilder(int64_t* buffer) : mBuffer(buffer) {
+ memset(mBuffer, 0, UI_THREAD_FRAME_INFO_SIZE * sizeof(int64_t));
+ }
+
+ UiFrameInfoBuilder& setVsync(nsecs_t vsyncTime, nsecs_t intendedVsync) {
+ set(FrameInfoIndex::kVsync) = vsyncTime;
+ set(FrameInfoIndex::kIntendedVsync) = intendedVsync;
+ return *this;
+ }
+
+ UiFrameInfoBuilder& addFlag(FrameInfoFlags flag) {
+ set(FrameInfoIndex::kFlags) |= static_cast<uint64_t>(flag);
+ return *this;
+ }
+
+private:
+ inline int64_t& set(FrameInfoIndex index) {
+ return mBuffer[static_cast<int>(index)];
+ }
+
+ int64_t* mBuffer;
+};
+
+class FrameInfo {
+public:
+ void importUiThreadInfo(int64_t* info);
+
+ void markSyncStart() {
+ set(FrameInfoIndex::kSyncStart) = systemTime(CLOCK_MONOTONIC);
+ }
+
+ void markIssueDrawCommandsStart() {
+ set(FrameInfoIndex::kIssueDrawCommandsStart) = systemTime(CLOCK_MONOTONIC);
+ }
+
+ void markSwapBuffers() {
+ set(FrameInfoIndex::kSwapBuffers) = systemTime(CLOCK_MONOTONIC);
+ }
+
+ void markFrameCompleted() {
+ set(FrameInfoIndex::kFrameCompleted) = systemTime(CLOCK_MONOTONIC);
+ }
+
+ int64_t operator[](FrameInfoIndex index) const {
+ if (index == FrameInfoIndex::kNumIndexes) return 0;
+ return mFrameInfo[static_cast<int>(index)];
+ }
+
+ int64_t operator[](int index) const {
+ if (index < 0 || index >= static_cast<int>(FrameInfoIndex::kNumIndexes)) return 0;
+ return mFrameInfo[index];
+ }
+
+private:
+ inline int64_t& set(FrameInfoIndex index) {
+ return mFrameInfo[static_cast<int>(index)];
+ }
+
+ int64_t mFrameInfo[static_cast<int>(FrameInfoIndex::kNumIndexes)];
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* FRAMEINFO_H_ */
diff --git a/libs/hwui/GammaFontRenderer.cpp b/libs/hwui/GammaFontRenderer.cpp
index 06d2aad..0bcd83a 100644
--- a/libs/hwui/GammaFontRenderer.cpp
+++ b/libs/hwui/GammaFontRenderer.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
-
#include "Debug.h"
#include "GammaFontRenderer.h"
#include "Properties.h"
@@ -61,7 +59,7 @@ GammaFontRenderer::GammaFontRenderer() {
// Get the gamma
mGamma = DEFAULT_TEXT_GAMMA;
- if (property_get(PROPERTY_TEXT_GAMMA, property, NULL) > 0) {
+ if (property_get(PROPERTY_TEXT_GAMMA, property, nullptr) > 0) {
INIT_LOGD(" Setting text gamma to %s", property);
mGamma = atof(property);
} else {
@@ -70,7 +68,7 @@ GammaFontRenderer::GammaFontRenderer() {
// Get the black gamma threshold
mBlackThreshold = DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD;
- if (property_get(PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD, property, NULL) > 0) {
+ if (property_get(PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD, property, nullptr) > 0) {
INIT_LOGD(" Setting text black gamma threshold to %s", property);
mBlackThreshold = atoi(property);
} else {
@@ -80,7 +78,7 @@ GammaFontRenderer::GammaFontRenderer() {
// Get the white gamma threshold
mWhiteThreshold = DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD;
- if (property_get(PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD, property, NULL) > 0) {
+ if (property_get(PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD, property, nullptr) > 0) {
INIT_LOGD(" Setting text white gamma threshold to %s", property);
mWhiteThreshold = atoi(property);
} else {
@@ -96,15 +94,16 @@ GammaFontRenderer::~GammaFontRenderer() {
// Shader-based renderer
///////////////////////////////////////////////////////////////////////////////
-ShaderGammaFontRenderer::ShaderGammaFontRenderer(bool multiGamma): GammaFontRenderer() {
+ShaderGammaFontRenderer::ShaderGammaFontRenderer(bool multiGamma)
+ : GammaFontRenderer() {
INIT_LOGD("Creating shader gamma font renderer");
- mRenderer = NULL;
+ mRenderer = nullptr;
mMultiGamma = multiGamma;
}
void ShaderGammaFontRenderer::describe(ProgramDescription& description,
const SkPaint* paint) const {
- if (paint->getShader() == NULL) {
+ if (paint->getShader() == nullptr) {
if (mMultiGamma) {
const int l = luminance(paint);
@@ -123,9 +122,9 @@ void ShaderGammaFontRenderer::describe(ProgramDescription& description,
}
void ShaderGammaFontRenderer::setupProgram(ProgramDescription& description,
- Program* program) const {
+ Program& program) const {
if (description.hasGammaCorrection) {
- glUniform1f(program->getUniform("gamma"), description.gamma);
+ glUniform1f(program.getUniform("gamma"), description.gamma);
}
}
@@ -139,7 +138,8 @@ void ShaderGammaFontRenderer::endPrecaching() {
// Lookup-based renderer
///////////////////////////////////////////////////////////////////////////////
-LookupGammaFontRenderer::LookupGammaFontRenderer(): GammaFontRenderer() {
+LookupGammaFontRenderer::LookupGammaFontRenderer()
+ : GammaFontRenderer() {
INIT_LOGD("Creating lookup gamma font renderer");
// Compute the gamma tables
@@ -149,7 +149,7 @@ LookupGammaFontRenderer::LookupGammaFontRenderer(): GammaFontRenderer() {
mGammaTable[i] = uint8_t((float)::floor(pow(i / 255.0f, gamma) * 255.0f + 0.5f));
}
- mRenderer = NULL;
+ mRenderer = nullptr;
}
void LookupGammaFontRenderer::endPrecaching() {
@@ -162,7 +162,8 @@ void LookupGammaFontRenderer::endPrecaching() {
// Lookup-based renderer, using 3 different correction tables
///////////////////////////////////////////////////////////////////////////////
-Lookup3GammaFontRenderer::Lookup3GammaFontRenderer(): GammaFontRenderer() {
+Lookup3GammaFontRenderer::Lookup3GammaFontRenderer()
+ : GammaFontRenderer() {
INIT_LOGD("Creating lookup3 gamma font renderer");
// Compute the gamma tables
@@ -183,12 +184,6 @@ Lookup3GammaFontRenderer::Lookup3GammaFontRenderer(): GammaFontRenderer() {
memset(mRenderersUsageCount, 0, sizeof(uint32_t) * kGammaCount);
}
-Lookup3GammaFontRenderer::~Lookup3GammaFontRenderer() {
- for (int i = 0; i < kGammaCount; i++) {
- delete mRenderers[i];
- }
-}
-
void Lookup3GammaFontRenderer::endPrecaching() {
for (int i = 0; i < kGammaCount; i++) {
if (mRenderers[i]) {
@@ -199,8 +194,7 @@ void Lookup3GammaFontRenderer::endPrecaching() {
void Lookup3GammaFontRenderer::clear() {
for (int i = 0; i < kGammaCount; i++) {
- delete mRenderers[i];
- mRenderers[i] = NULL;
+ mRenderers[i].release();
}
}
@@ -221,8 +215,7 @@ void Lookup3GammaFontRenderer::flush() {
if (count <= 1 || min < 0) return;
- delete mRenderers[min];
- mRenderers[min] = NULL;
+ mRenderers[min].release();
// Also eliminate the caches for large glyphs, as they consume significant memory
for (int i = 0; i < kGammaCount; ++i) {
@@ -233,18 +226,16 @@ void Lookup3GammaFontRenderer::flush() {
}
FontRenderer* Lookup3GammaFontRenderer::getRenderer(Gamma gamma) {
- FontRenderer* renderer = mRenderers[gamma];
- if (!renderer) {
- renderer = new FontRenderer();
- mRenderers[gamma] = renderer;
- renderer->setGammaTable(&mGammaTable[gamma * 256]);
+ if (!mRenderers[gamma]) {
+ mRenderers[gamma].reset(new FontRenderer());
+ mRenderers[gamma]->setGammaTable(&mGammaTable[gamma * 256]);
}
mRenderersUsageCount[gamma]++;
- return renderer;
+ return mRenderers[gamma].get();
}
FontRenderer& Lookup3GammaFontRenderer::getFontRenderer(const SkPaint* paint) {
- if (paint->getShader() == NULL) {
+ if (paint->getShader() == nullptr) {
const int l = luminance(paint);
if (l <= mBlackThreshold) {
diff --git a/libs/hwui/GammaFontRenderer.h b/libs/hwui/GammaFontRenderer.h
index 46cfd04..ca55bf1 100644
--- a/libs/hwui/GammaFontRenderer.h
+++ b/libs/hwui/GammaFontRenderer.h
@@ -38,7 +38,7 @@ public:
virtual uint32_t getFontRendererSize(uint32_t fontRenderer, GLenum format) const = 0;
virtual void describe(ProgramDescription& description, const SkPaint* paint) const = 0;
- virtual void setupProgram(ProgramDescription& description, Program* program) const = 0;
+ virtual void setupProgram(ProgramDescription& description, Program& program) const = 0;
virtual void endPrecaching() = 0;
@@ -59,36 +59,36 @@ public:
delete mRenderer;
}
- void clear() {
+ void clear() override {
delete mRenderer;
- mRenderer = NULL;
+ mRenderer = nullptr;
}
- void flush() {
+ void flush() override {
if (mRenderer) {
mRenderer->flushLargeCaches();
}
}
- FontRenderer& getFontRenderer(const SkPaint* paint) {
+ FontRenderer& getFontRenderer(const SkPaint* paint) override {
if (!mRenderer) {
mRenderer = new FontRenderer;
}
return *mRenderer;
}
- uint32_t getFontRendererCount() const {
+ uint32_t getFontRendererCount() const override {
return 1;
}
- uint32_t getFontRendererSize(uint32_t fontRenderer, GLenum format) const {
+ uint32_t getFontRendererSize(uint32_t fontRenderer, GLenum format) const override {
return mRenderer ? mRenderer->getCacheSize(format) : 0;
}
- void describe(ProgramDescription& description, const SkPaint* paint) const;
- void setupProgram(ProgramDescription& description, Program* program) const;
+ void describe(ProgramDescription& description, const SkPaint* paint) const override;
+ void setupProgram(ProgramDescription& description, Program& program) const override;
- void endPrecaching();
+ void endPrecaching() override;
private:
ShaderGammaFontRenderer(bool multiGamma);
@@ -105,18 +105,18 @@ public:
delete mRenderer;
}
- void clear() {
+ void clear() override {
delete mRenderer;
- mRenderer = NULL;
+ mRenderer = nullptr;
}
- void flush() {
+ void flush() override {
if (mRenderer) {
mRenderer->flushLargeCaches();
}
}
- FontRenderer& getFontRenderer(const SkPaint* paint) {
+ FontRenderer& getFontRenderer(const SkPaint* paint) override {
if (!mRenderer) {
mRenderer = new FontRenderer;
mRenderer->setGammaTable(&mGammaTable[0]);
@@ -124,21 +124,21 @@ public:
return *mRenderer;
}
- uint32_t getFontRendererCount() const {
+ uint32_t getFontRendererCount() const override {
return 1;
}
- uint32_t getFontRendererSize(uint32_t fontRenderer, GLenum format) const {
+ uint32_t getFontRendererSize(uint32_t fontRenderer, GLenum format) const override {
return mRenderer ? mRenderer->getCacheSize(format) : 0;
}
- void describe(ProgramDescription& description, const SkPaint* paint) const {
+ void describe(ProgramDescription& description, const SkPaint* paint) const override {
}
- void setupProgram(ProgramDescription& description, Program* program) const {
+ void setupProgram(ProgramDescription& description, Program& program) const override {
}
- void endPrecaching();
+ void endPrecaching() override;
private:
LookupGammaFontRenderer();
@@ -151,33 +151,30 @@ private:
class Lookup3GammaFontRenderer: public GammaFontRenderer {
public:
- ~Lookup3GammaFontRenderer();
+ void clear() override;
+ void flush() override;
- void clear();
- void flush();
+ FontRenderer& getFontRenderer(const SkPaint* paint) override;
- FontRenderer& getFontRenderer(const SkPaint* paint);
-
- uint32_t getFontRendererCount() const {
+ uint32_t getFontRendererCount() const override {
return kGammaCount;
}
- uint32_t getFontRendererSize(uint32_t fontRenderer, GLenum format) const {
+ uint32_t getFontRendererSize(uint32_t fontRenderer, GLenum format) const override {
if (fontRenderer >= kGammaCount) return 0;
- FontRenderer* renderer = mRenderers[fontRenderer];
- if (!renderer) return 0;
+ if (!mRenderers[fontRenderer]) return 0;
- return renderer->getCacheSize(format);
+ return mRenderers[fontRenderer]->getCacheSize(format);
}
- void describe(ProgramDescription& description, const SkPaint* paint) const {
+ void describe(ProgramDescription& description, const SkPaint* paint) const override {
}
- void setupProgram(ProgramDescription& description, Program* program) const {
+ void setupProgram(ProgramDescription& description, Program& program) const override {
}
- void endPrecaching();
+ void endPrecaching() override;
private:
Lookup3GammaFontRenderer();
@@ -192,7 +189,7 @@ private:
FontRenderer* getRenderer(Gamma gamma);
uint32_t mRenderersUsageCount[kGammaCount];
- FontRenderer* mRenderers[kGammaCount];
+ std::unique_ptr<FontRenderer> mRenderers[kGammaCount];
uint8_t mGammaTable[256 * kGammaCount];
diff --git a/libs/hwui/Glop.h b/libs/hwui/Glop.h
new file mode 100644
index 0000000..2c6f6c1
--- /dev/null
+++ b/libs/hwui/Glop.h
@@ -0,0 +1,153 @@
+/*
+ * 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 ANDROID_HWUI_GLOP_H
+#define ANDROID_HWUI_GLOP_H
+
+#include "FloatColor.h"
+#include "Matrix.h"
+#include "Program.h"
+#include "Rect.h"
+#include "SkiaShader.h"
+#include "utils/Macros.h"
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <SkXfermode.h>
+
+namespace android {
+namespace uirenderer {
+
+class Program;
+class RoundRectClipState;
+class Texture;
+
+/*
+ * Enumerates optional vertex attributes
+ *
+ * Position is always enabled by MeshState, these other attributes
+ * are enabled/disabled dynamically based on mesh content.
+ */
+enum class VertexAttribFlags {
+ kNone = 0,
+ kTextureCoord = 1 << 0,
+ kColor = 1 << 1,
+ kAlpha = 1 << 2,
+};
+MAKE_FLAGS_ENUM(VertexAttribFlags)
+
+/**
+ * Structure containing all data required to issue an OpenGL draw
+ *
+ * Includes all of the mesh, fill, and GL state required to perform
+ * the operation. Pieces of data are either directly copied into the
+ * structure, or stored as a pointer or GL object reference to data
+ * managed.
+ *
+ * Eventually, a Glop should be able to be drawn multiple times from
+ * a single construction, up until GL context destruction. Currently,
+ * vertex/index/Texture/RoundRectClipState pointers prevent this from
+ * being safe.
+ */
+// TODO: PREVENT_COPY_AND_ASSIGN(...) or similar
+struct Glop {
+ struct Mesh {
+ GLuint primitiveMode; // GL_TRIANGLES and GL_TRIANGLE_STRIP supported
+
+ // buffer object and void* are mutually exclusive.
+ // Only GL_UNSIGNED_SHORT supported.
+ struct Indices {
+ GLuint bufferObject;
+ const void* indices;
+ } indices;
+
+ // buffer object and void*s are mutually exclusive.
+ // TODO: enforce mutual exclusion with restricted setters and/or unions
+ struct Vertices {
+ GLuint bufferObject;
+ int attribFlags;
+ const void* position;
+ const void* texCoord;
+ const void* color;
+ GLsizei stride;
+ } vertices;
+
+ int elementCount;
+ TextureVertex mappedVertices[4];
+ } mesh;
+
+ struct Fill {
+ Program* program;
+
+ struct TextureData {
+ Texture* texture;
+ GLenum target;
+ GLenum filter;
+ GLenum clamp;
+ Matrix4* textureTransform;
+ } texture;
+
+ bool colorEnabled;
+ FloatColor color;
+
+ ProgramDescription::ColorFilterMode filterMode;
+ union Filter {
+ struct Matrix {
+ float matrix[16];
+ float vector[4];
+ } matrix;
+ FloatColor color;
+ } filter;
+
+ SkiaShaderData skiaShaderData;
+ } fill;
+
+ struct Transform {
+ Matrix4 ortho; // TODO: out of op, since this is static per FBO
+ Matrix4 modelView;
+ Matrix4 canvas;
+ bool fudgingOffset;
+ } transform;
+
+ const RoundRectClipState* roundRectClipState;
+
+ /**
+ * Blending to be used by this draw - both GL_NONE if blending is disabled.
+ *
+ * Defined by fill step, but can be force-enabled by presence of kAlpha_Attrib
+ */
+ struct Blend {
+ GLenum src;
+ GLenum dst;
+ } blend;
+
+ /**
+ * Bounds of the drawing command in layer space. Only mapped into layer
+ * space once GlopBuilder::build() is called.
+ */
+ Rect bounds;
+
+ /**
+ * Additional render state to enumerate:
+ * - scissor + (bits for whether each of LTRB needed?)
+ * - stencil mode (draw into, mask, count, etc)
+ */
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif // ANDROID_HWUI_GLOP_H
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
new file mode 100644
index 0000000..9ca6bc6
--- /dev/null
+++ b/libs/hwui/GlopBuilder.cpp
@@ -0,0 +1,615 @@
+/*
+ * 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 "GlopBuilder.h"
+
+#include "Caches.h"
+#include "Glop.h"
+#include "Matrix.h"
+#include "Patch.h"
+#include "renderstate/MeshState.h"
+#include "renderstate/RenderState.h"
+#include "SkiaShader.h"
+#include "Texture.h"
+#include "utils/PaintUtils.h"
+#include "VertexBuffer.h"
+
+#include <GLES2/gl2.h>
+#include <SkPaint.h>
+
+namespace android {
+namespace uirenderer {
+
+#define TRIGGER_STAGE(stageFlag) \
+ LOG_ALWAYS_FATAL_IF((stageFlag) & mStageFlags, "Stage %d cannot be run twice", (stageFlag)); \
+ mStageFlags = static_cast<StageFlags>(mStageFlags | (stageFlag))
+
+#define REQUIRE_STAGES(requiredFlags) \
+ LOG_ALWAYS_FATAL_IF((mStageFlags & (requiredFlags)) != (requiredFlags), \
+ "not prepared for current stage")
+
+static void setUnitQuadTextureCoords(Rect uvs, TextureVertex* quadVertex) {
+ quadVertex[0] = {0, 0, uvs.left, uvs.top};
+ quadVertex[1] = {1, 0, uvs.right, uvs.top};
+ quadVertex[2] = {0, 1, uvs.left, uvs.bottom};
+ quadVertex[3] = {1, 1, uvs.right, uvs.bottom};
+}
+
+GlopBuilder::GlopBuilder(RenderState& renderState, Caches& caches, Glop* outGlop)
+ : mRenderState(renderState)
+ , mCaches(caches)
+ , mShader(nullptr)
+ , mOutGlop(outGlop) {
+ mStageFlags = kInitialStage;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Mesh
+////////////////////////////////////////////////////////////////////////////////
+
+GlopBuilder& GlopBuilder::setMeshUnitQuad() {
+ TRIGGER_STAGE(kMeshStage);
+
+ mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP;
+ mOutGlop->mesh.indices = { 0, nullptr };
+ mOutGlop->mesh.vertices = {
+ mRenderState.meshState().getUnitQuadVBO(),
+ static_cast<int>(VertexAttribFlags::kNone),
+ nullptr, nullptr, nullptr,
+ kTextureVertexStride };
+ mOutGlop->mesh.elementCount = 4;
+ return *this;
+}
+
+GlopBuilder& GlopBuilder::setMeshTexturedUnitQuad(const UvMapper* uvMapper) {
+ if (uvMapper) {
+ // can't use unit quad VBO, so build UV vertices manually
+ return setMeshTexturedUvQuad(uvMapper, Rect(0, 0, 1, 1));
+ }
+
+ TRIGGER_STAGE(kMeshStage);
+
+ mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP;
+ mOutGlop->mesh.indices = { 0, nullptr };
+ mOutGlop->mesh.vertices = {
+ mRenderState.meshState().getUnitQuadVBO(),
+ static_cast<int>(VertexAttribFlags::kTextureCoord),
+ nullptr, (const void*) kMeshTextureOffset, nullptr,
+ kTextureVertexStride };
+ mOutGlop->mesh.elementCount = 4;
+ return *this;
+}
+
+GlopBuilder& GlopBuilder::setMeshTexturedUvQuad(const UvMapper* uvMapper, Rect uvs) {
+ TRIGGER_STAGE(kMeshStage);
+
+ if (CC_UNLIKELY(uvMapper)) {
+ uvMapper->map(uvs);
+ }
+ setUnitQuadTextureCoords(uvs, &mOutGlop->mesh.mappedVertices[0]);
+
+ const TextureVertex* textureVertex = mOutGlop->mesh.mappedVertices;
+ mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP;
+ mOutGlop->mesh.indices = { 0, nullptr };
+ mOutGlop->mesh.vertices = {
+ 0,
+ static_cast<int>(VertexAttribFlags::kTextureCoord),
+ &textureVertex[0].x, &textureVertex[0].u, nullptr,
+ kTextureVertexStride };
+ mOutGlop->mesh.elementCount = 4;
+ return *this;
+}
+
+GlopBuilder& GlopBuilder::setMeshIndexedQuads(Vertex* vertexData, int quadCount) {
+ TRIGGER_STAGE(kMeshStage);
+
+ mOutGlop->mesh.primitiveMode = GL_TRIANGLES;
+ mOutGlop->mesh.indices = { mRenderState.meshState().getQuadListIBO(), nullptr };
+ mOutGlop->mesh.vertices = {
+ 0,
+ static_cast<int>(VertexAttribFlags::kNone),
+ vertexData, nullptr, nullptr,
+ kVertexStride };
+ mOutGlop->mesh.elementCount = 6 * quadCount;
+ return *this;
+}
+
+GlopBuilder& GlopBuilder::setMeshTexturedIndexedQuads(TextureVertex* vertexData, int elementCount) {
+ TRIGGER_STAGE(kMeshStage);
+
+ mOutGlop->mesh.primitiveMode = GL_TRIANGLES;
+ mOutGlop->mesh.indices = { mRenderState.meshState().getQuadListIBO(), nullptr };
+ mOutGlop->mesh.vertices = {
+ 0,
+ static_cast<int>(VertexAttribFlags::kTextureCoord),
+ &vertexData[0].x, &vertexData[0].u, nullptr,
+ kTextureVertexStride };
+ mOutGlop->mesh.elementCount = elementCount;
+ return *this;
+}
+
+GlopBuilder& GlopBuilder::setMeshTexturedMesh(TextureVertex* vertexData, int elementCount) {
+ TRIGGER_STAGE(kMeshStage);
+
+ mOutGlop->mesh.primitiveMode = GL_TRIANGLES;
+ mOutGlop->mesh.indices = { 0, nullptr };
+ mOutGlop->mesh.vertices = {
+ 0,
+ static_cast<int>(VertexAttribFlags::kTextureCoord),
+ &vertexData[0].x, &vertexData[0].u, nullptr,
+ kTextureVertexStride };
+ mOutGlop->mesh.elementCount = elementCount;
+ return *this;
+}
+
+GlopBuilder& GlopBuilder::setMeshColoredTexturedMesh(ColorTextureVertex* vertexData, int elementCount) {
+ TRIGGER_STAGE(kMeshStage);
+
+ mOutGlop->mesh.primitiveMode = GL_TRIANGLES;
+ mOutGlop->mesh.indices = { 0, nullptr };
+ mOutGlop->mesh.vertices = {
+ 0,
+ VertexAttribFlags::kTextureCoord | VertexAttribFlags::kColor,
+ &vertexData[0].x, &vertexData[0].u, &vertexData[0].r,
+ kColorTextureVertexStride };
+ mOutGlop->mesh.elementCount = elementCount;
+ return *this;
+}
+
+GlopBuilder& GlopBuilder::setMeshVertexBuffer(const VertexBuffer& vertexBuffer, bool shadowInterp) {
+ TRIGGER_STAGE(kMeshStage);
+
+ const VertexBuffer::MeshFeatureFlags flags = vertexBuffer.getMeshFeatureFlags();
+
+ bool alphaVertex = flags & VertexBuffer::kAlpha;
+ bool indices = flags & VertexBuffer::kIndices;
+
+ mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP;
+ mOutGlop->mesh.indices = { 0, vertexBuffer.getIndices() };
+ mOutGlop->mesh.vertices = {
+ 0,
+ static_cast<int>(alphaVertex ? VertexAttribFlags::kAlpha : VertexAttribFlags::kNone),
+ vertexBuffer.getBuffer(), nullptr, nullptr,
+ alphaVertex ? kAlphaVertexStride : kVertexStride };
+ mOutGlop->mesh.elementCount = indices
+ ? vertexBuffer.getIndexCount() : vertexBuffer.getVertexCount();
+
+ mDescription.useShadowAlphaInterp = shadowInterp;
+ return *this;
+}
+
+GlopBuilder& GlopBuilder::setMeshPatchQuads(const Patch& patch) {
+ TRIGGER_STAGE(kMeshStage);
+
+ mOutGlop->mesh.primitiveMode = GL_TRIANGLES;
+ mOutGlop->mesh.indices = { mRenderState.meshState().getQuadListIBO(), nullptr };
+ mOutGlop->mesh.vertices = {
+ mCaches.patchCache.getMeshBuffer(),
+ static_cast<int>(VertexAttribFlags::kTextureCoord),
+ (void*)patch.positionOffset, (void*)patch.textureOffset, nullptr,
+ kTextureVertexStride };
+ mOutGlop->mesh.elementCount = patch.indexCount;
+ return *this;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Fill
+////////////////////////////////////////////////////////////////////////////////
+
+void GlopBuilder::setFill(int color, float alphaScale,
+ SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage,
+ const SkShader* shader, const SkColorFilter* colorFilter) {
+ if (mode != SkXfermode::kClear_Mode) {
+ float alpha = (SkColorGetA(color) / 255.0f) * alphaScale;
+ if (!shader) {
+ float colorScale = alpha / 255.0f;
+ mOutGlop->fill.color = {
+ colorScale * SkColorGetR(color),
+ colorScale * SkColorGetG(color),
+ colorScale * SkColorGetB(color),
+ alpha
+ };
+ } else {
+ mOutGlop->fill.color = { 1, 1, 1, alpha };
+ }
+ } else {
+ mOutGlop->fill.color = { 0, 0, 0, 1 };
+ }
+
+ mOutGlop->blend = { GL_ZERO, GL_ZERO };
+ if (mOutGlop->fill.color.a < 1.0f
+ || (mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::kAlpha)
+ || (mOutGlop->fill.texture.texture && mOutGlop->fill.texture.texture->blend)
+ || mOutGlop->roundRectClipState
+ || PaintUtils::isBlendedShader(shader)
+ || PaintUtils::isBlendedColorFilter(colorFilter)
+ || mode != SkXfermode::kSrcOver_Mode) {
+ if (CC_LIKELY(mode <= SkXfermode::kScreen_Mode)) {
+ Blend::getFactors(mode, modeUsage,
+ &mOutGlop->blend.src, &mOutGlop->blend.dst);
+ } else {
+ // These blend modes are not supported by OpenGL directly and have
+ // to be implemented using shaders. Since the shader will perform
+ // the blending, don't enable GL blending off here
+ // If the blend mode cannot be implemented using shaders, fall
+ // back to the default SrcOver blend mode instead
+ if (CC_UNLIKELY(mCaches.extensions().hasFramebufferFetch())) {
+ mDescription.framebufferMode = mode;
+ mDescription.swapSrcDst = (modeUsage == Blend::ModeOrderSwap::Swap);
+ // blending in shader, don't enable
+ } else {
+ // unsupported
+ Blend::getFactors(SkXfermode::kSrcOver_Mode, modeUsage,
+ &mOutGlop->blend.src, &mOutGlop->blend.dst);
+ }
+ }
+ }
+ mShader = shader; // shader resolved in ::build()
+
+ if (colorFilter) {
+ SkColor color;
+ SkXfermode::Mode mode;
+ SkScalar srcColorMatrix[20];
+ if (colorFilter->asColorMode(&color, &mode)) {
+ mOutGlop->fill.filterMode = mDescription.colorOp = ProgramDescription::kColorBlend;
+ mDescription.colorMode = mode;
+
+ const float alpha = SkColorGetA(color) / 255.0f;
+ float colorScale = alpha / 255.0f;
+ mOutGlop->fill.filter.color = {
+ colorScale * SkColorGetR(color),
+ colorScale * SkColorGetG(color),
+ colorScale * SkColorGetB(color),
+ alpha,
+ };
+ } else if (colorFilter->asColorMatrix(srcColorMatrix)) {
+ mOutGlop->fill.filterMode = mDescription.colorOp = ProgramDescription::kColorMatrix;
+
+ float* colorMatrix = mOutGlop->fill.filter.matrix.matrix;
+ memcpy(colorMatrix, srcColorMatrix, 4 * sizeof(float));
+ memcpy(&colorMatrix[4], &srcColorMatrix[5], 4 * sizeof(float));
+ memcpy(&colorMatrix[8], &srcColorMatrix[10], 4 * sizeof(float));
+ memcpy(&colorMatrix[12], &srcColorMatrix[15], 4 * sizeof(float));
+
+ // Skia uses the range [0..255] for the addition vector, but we need
+ // the [0..1] range to apply the vector in GLSL
+ float* colorVector = mOutGlop->fill.filter.matrix.vector;
+ colorVector[0] = srcColorMatrix[4] / 255.0f;
+ colorVector[1] = srcColorMatrix[9] / 255.0f;
+ colorVector[2] = srcColorMatrix[14] / 255.0f;
+ colorVector[3] = srcColorMatrix[19] / 255.0f;
+ } else {
+ LOG_ALWAYS_FATAL("unsupported ColorFilter");
+ }
+ } else {
+ mOutGlop->fill.filterMode = ProgramDescription::kColorNone;
+ }
+}
+
+GlopBuilder& GlopBuilder::setFillTexturePaint(Texture& texture, int textureFillFlags,
+ const SkPaint* paint, float alphaScale) {
+ TRIGGER_STAGE(kFillStage);
+ REQUIRE_STAGES(kMeshStage);
+
+ GLenum filter = (textureFillFlags & TextureFillFlags::kForceFilter)
+ ? GL_LINEAR : PaintUtils::getFilter(paint);
+ mOutGlop->fill.texture = { &texture,
+ GL_TEXTURE_2D, filter, GL_CLAMP_TO_EDGE, nullptr };
+
+ if (paint) {
+ int color = paint->getColor();
+ SkShader* shader = paint->getShader();
+
+ if (!(textureFillFlags & TextureFillFlags::kIsAlphaMaskTexture)) {
+ // Texture defines color, so disable shaders, and reset all non-alpha color channels
+ color |= 0x00FFFFFF;
+ shader = nullptr;
+ }
+ setFill(color, alphaScale,
+ PaintUtils::getXfermode(paint->getXfermode()), Blend::ModeOrderSwap::NoSwap,
+ shader, paint->getColorFilter());
+ } else {
+ mOutGlop->fill.color = { alphaScale, alphaScale, alphaScale, alphaScale };
+
+ if (alphaScale < 1.0f
+ || (mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::kAlpha)
+ || texture.blend
+ || mOutGlop->roundRectClipState) {
+ Blend::getFactors(SkXfermode::kSrcOver_Mode, Blend::ModeOrderSwap::NoSwap,
+ &mOutGlop->blend.src, &mOutGlop->blend.dst);
+ } else {
+ mOutGlop->blend = { GL_ZERO, GL_ZERO };
+ }
+ }
+
+ if (textureFillFlags & TextureFillFlags::kIsAlphaMaskTexture) {
+ mDescription.modulate = mOutGlop->fill.color.isNotBlack();
+ mDescription.hasAlpha8Texture = true;
+ } else {
+ mDescription.modulate = mOutGlop->fill.color.a < 1.0f;
+ }
+ return *this;
+}
+
+GlopBuilder& GlopBuilder::setFillPaint(const SkPaint& paint, float alphaScale) {
+ TRIGGER_STAGE(kFillStage);
+ REQUIRE_STAGES(kMeshStage);
+
+ mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
+
+ setFill(paint.getColor(), alphaScale,
+ PaintUtils::getXfermode(paint.getXfermode()), Blend::ModeOrderSwap::NoSwap,
+ paint.getShader(), paint.getColorFilter());
+ mDescription.modulate = mOutGlop->fill.color.a < 1.0f;
+ return *this;
+}
+
+GlopBuilder& GlopBuilder::setFillPathTexturePaint(PathTexture& texture,
+ const SkPaint& paint, float alphaScale) {
+ TRIGGER_STAGE(kFillStage);
+ REQUIRE_STAGES(kMeshStage);
+
+ //specify invalid filter/clamp, since these are always static for PathTextures
+ mOutGlop->fill.texture = { &texture, GL_TEXTURE_2D, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
+
+ setFill(paint.getColor(), alphaScale,
+ PaintUtils::getXfermode(paint.getXfermode()), Blend::ModeOrderSwap::NoSwap,
+ paint.getShader(), paint.getColorFilter());
+
+ mDescription.hasAlpha8Texture = true;
+ mDescription.modulate = mOutGlop->fill.color.isNotBlack();
+ return *this;
+}
+
+GlopBuilder& GlopBuilder::setFillShadowTexturePaint(ShadowTexture& texture, int shadowColor,
+ const SkPaint& paint, float alphaScale) {
+ TRIGGER_STAGE(kFillStage);
+ REQUIRE_STAGES(kMeshStage);
+
+ //specify invalid filter/clamp, since these are always static for ShadowTextures
+ mOutGlop->fill.texture = { &texture, GL_TEXTURE_2D, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
+
+ const int ALPHA_BITMASK = SK_ColorBLACK;
+ const int COLOR_BITMASK = ~ALPHA_BITMASK;
+ if ((shadowColor & ALPHA_BITMASK) == ALPHA_BITMASK) {
+ // shadow color is fully opaque: override its alpha with that of paint
+ shadowColor &= paint.getColor() | COLOR_BITMASK;
+ }
+
+ setFill(shadowColor, alphaScale,
+ PaintUtils::getXfermode(paint.getXfermode()), Blend::ModeOrderSwap::NoSwap,
+ paint.getShader(), paint.getColorFilter());
+
+ mDescription.hasAlpha8Texture = true;
+ mDescription.modulate = mOutGlop->fill.color.isNotBlack();
+ return *this;
+}
+
+GlopBuilder& GlopBuilder::setFillBlack() {
+ TRIGGER_STAGE(kFillStage);
+ REQUIRE_STAGES(kMeshStage);
+
+ mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
+ setFill(SK_ColorBLACK, 1.0f, SkXfermode::kSrcOver_Mode, Blend::ModeOrderSwap::NoSwap,
+ nullptr, nullptr);
+ return *this;
+}
+
+GlopBuilder& GlopBuilder::setFillClear() {
+ TRIGGER_STAGE(kFillStage);
+ REQUIRE_STAGES(kMeshStage);
+
+ mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
+ setFill(SK_ColorBLACK, 1.0f, SkXfermode::kClear_Mode, Blend::ModeOrderSwap::NoSwap,
+ nullptr, nullptr);
+ return *this;
+}
+
+GlopBuilder& GlopBuilder::setFillLayer(Texture& texture, const SkColorFilter* colorFilter,
+ float alpha, SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage) {
+ TRIGGER_STAGE(kFillStage);
+ REQUIRE_STAGES(kMeshStage);
+
+ mOutGlop->fill.texture = { &texture,
+ GL_TEXTURE_2D, GL_LINEAR, GL_CLAMP_TO_EDGE, nullptr };
+ mOutGlop->fill.color = { alpha, alpha, alpha, alpha };
+
+ setFill(SK_ColorWHITE, alpha, mode, modeUsage, nullptr, colorFilter);
+
+ mDescription.modulate = mOutGlop->fill.color.a < 1.0f;
+ return *this;
+}
+
+GlopBuilder& GlopBuilder::setFillTextureLayer(Layer& layer, float alpha) {
+ TRIGGER_STAGE(kFillStage);
+ REQUIRE_STAGES(kMeshStage);
+
+ mOutGlop->fill.texture = { &(layer.getTexture()),
+ layer.getRenderTarget(), GL_LINEAR, GL_CLAMP_TO_EDGE, &layer.getTexTransform() };
+ mOutGlop->fill.color = { alpha, alpha, alpha, alpha };
+
+ setFill(SK_ColorWHITE, alpha, layer.getMode(), Blend::ModeOrderSwap::NoSwap,
+ nullptr, layer.getColorFilter());
+
+ mDescription.modulate = mOutGlop->fill.color.a < 1.0f;
+ mDescription.hasTextureTransform = true;
+ return *this;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Transform
+////////////////////////////////////////////////////////////////////////////////
+
+GlopBuilder& GlopBuilder::setTransform(const Matrix4& ortho,
+ const Matrix4& transform, bool fudgingOffset) {
+ TRIGGER_STAGE(kTransformStage);
+
+ mOutGlop->transform.ortho.load(ortho);
+ mOutGlop->transform.canvas.load(transform);
+ mOutGlop->transform.fudgingOffset = fudgingOffset;
+ return *this;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ModelView
+////////////////////////////////////////////////////////////////////////////////
+
+GlopBuilder& GlopBuilder::setModelViewMapUnitToRect(const Rect destination) {
+ TRIGGER_STAGE(kModelViewStage);
+
+ mOutGlop->transform.modelView.loadTranslate(destination.left, destination.top, 0.0f);
+ mOutGlop->transform.modelView.scale(destination.getWidth(), destination.getHeight(), 1.0f);
+ mOutGlop->bounds = destination;
+ return *this;
+}
+
+GlopBuilder& GlopBuilder::setModelViewMapUnitToRectSnap(const Rect destination) {
+ TRIGGER_STAGE(kModelViewStage);
+ REQUIRE_STAGES(kTransformStage | kFillStage);
+
+ float left = destination.left;
+ float top = destination.top;
+
+ const Matrix4& canvasTransform = mOutGlop->transform.canvas;
+ if (CC_LIKELY(canvasTransform.isPureTranslate())) {
+ // snap by adjusting the model view matrix
+ const float translateX = canvasTransform.getTranslateX();
+ const float translateY = canvasTransform.getTranslateY();
+
+ left = (int) floorf(left + translateX + 0.5f) - translateX;
+ top = (int) floorf(top + translateY + 0.5f) - translateY;
+ mOutGlop->fill.texture.filter = GL_NEAREST;
+ }
+
+ mOutGlop->transform.modelView.loadTranslate(left, top, 0.0f);
+ mOutGlop->transform.modelView.scale(destination.getWidth(), destination.getHeight(), 1.0f);
+ mOutGlop->bounds = destination;
+ return *this;
+}
+
+GlopBuilder& GlopBuilder::setModelViewOffsetRect(float offsetX, float offsetY, const Rect source) {
+ TRIGGER_STAGE(kModelViewStage);
+
+ mOutGlop->transform.modelView.loadTranslate(offsetX, offsetY, 0.0f);
+ mOutGlop->bounds = source;
+ mOutGlop->bounds.translate(offsetX, offsetY);
+ return *this;
+}
+
+GlopBuilder& GlopBuilder::setModelViewOffsetRectSnap(float offsetX, float offsetY, const Rect source) {
+ TRIGGER_STAGE(kModelViewStage);
+ REQUIRE_STAGES(kTransformStage | kFillStage);
+
+ const Matrix4& canvasTransform = mOutGlop->transform.canvas;
+ if (CC_LIKELY(canvasTransform.isPureTranslate())) {
+ // snap by adjusting the model view matrix
+ const float translateX = canvasTransform.getTranslateX();
+ const float translateY = canvasTransform.getTranslateY();
+
+ offsetX = (int) floorf(offsetX + translateX + source.left + 0.5f) - translateX - source.left;
+ offsetY = (int) floorf(offsetY + translateY + source.top + 0.5f) - translateY - source.top;
+ mOutGlop->fill.texture.filter = GL_NEAREST;
+ }
+
+ mOutGlop->transform.modelView.loadTranslate(offsetX, offsetY, 0.0f);
+ mOutGlop->bounds = source;
+ mOutGlop->bounds.translate(offsetX, offsetY);
+ return *this;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// RoundRectClip
+////////////////////////////////////////////////////////////////////////////////
+
+GlopBuilder& GlopBuilder::setRoundRectClipState(const RoundRectClipState* roundRectClipState) {
+ TRIGGER_STAGE(kRoundRectClipStage);
+
+ mOutGlop->roundRectClipState = roundRectClipState;
+ mDescription.hasRoundRectClip = roundRectClipState != nullptr;
+ return *this;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Build
+////////////////////////////////////////////////////////////////////////////////
+
+void verify(const ProgramDescription& description, const Glop& glop) {
+ if (glop.fill.texture.texture != nullptr) {
+ LOG_ALWAYS_FATAL_IF(((description.hasTexture && description.hasExternalTexture)
+ || (!description.hasTexture && !description.hasExternalTexture)
+ || ((glop.mesh.vertices.attribFlags & VertexAttribFlags::kTextureCoord) == 0)),
+ "Texture %p, hT%d, hET %d, attribFlags %x",
+ glop.fill.texture.texture,
+ description.hasTexture, description.hasExternalTexture,
+ glop.mesh.vertices.attribFlags);
+ } else {
+ LOG_ALWAYS_FATAL_IF((description.hasTexture
+ || description.hasExternalTexture
+ || ((glop.mesh.vertices.attribFlags & VertexAttribFlags::kTextureCoord) != 0)),
+ "No texture, hT%d, hET %d, attribFlags %x",
+ description.hasTexture, description.hasExternalTexture,
+ glop.mesh.vertices.attribFlags);
+ }
+
+ if ((glop.mesh.vertices.attribFlags & VertexAttribFlags::kAlpha)
+ && glop.mesh.vertices.bufferObject) {
+ LOG_ALWAYS_FATAL("VBO and alpha attributes are not currently compatible");
+ }
+
+ if (description.hasTextureTransform != (glop.fill.texture.textureTransform != nullptr)) {
+ LOG_ALWAYS_FATAL("Texture transform incorrectly specified");
+ }
+}
+
+void GlopBuilder::build() {
+ REQUIRE_STAGES(kAllStages);
+ if (mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::kTextureCoord) {
+ if (mOutGlop->fill.texture.target == GL_TEXTURE_2D) {
+ mDescription.hasTexture = true;
+ } else {
+ mDescription.hasExternalTexture = true;
+ }
+
+ }
+ mDescription.hasColors = mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::kColor;
+ mDescription.hasVertexAlpha = mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::kAlpha;
+
+ // Enable debug highlight when what we're about to draw is tested against
+ // the stencil buffer and if stencil highlight debugging is on
+ mDescription.hasDebugHighlight = !mCaches.debugOverdraw
+ && mCaches.debugStencilClip == Caches::kStencilShowHighlight
+ && mRenderState.stencil().isTestEnabled();
+
+ // serialize shader info into ShaderData
+ GLuint textureUnit = mOutGlop->fill.texture.texture ? 1 : 0;
+ SkiaShader::store(mCaches, mShader, mOutGlop->transform.modelView,
+ &textureUnit, &mDescription, &(mOutGlop->fill.skiaShaderData));
+
+ // duplicates ProgramCache's definition of color uniform presence
+ const bool singleColor = !mDescription.hasTexture
+ && !mDescription.hasExternalTexture
+ && !mDescription.hasGradient
+ && !mDescription.hasBitmap;
+ mOutGlop->fill.colorEnabled = mDescription.modulate || singleColor;
+
+ verify(mDescription, *mOutGlop);
+
+ // Final step: populate program and map bounds into render target space
+ mOutGlop->fill.program = mCaches.programCache.get(mDescription);
+ mOutGlop->transform.canvas.mapRect(mOutGlop->bounds);
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/GlopBuilder.h b/libs/hwui/GlopBuilder.h
new file mode 100644
index 0000000..b335a2c
--- /dev/null
+++ b/libs/hwui/GlopBuilder.h
@@ -0,0 +1,122 @@
+/*
+ * 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_GLOPBUILDER_H
+#define RENDERSTATE_GLOPBUILDER_H
+
+#include "OpenGLRenderer.h"
+#include "Program.h"
+#include "renderstate/Blend.h"
+#include "utils/Macros.h"
+
+class SkPaint;
+class SkShader;
+
+namespace android {
+namespace uirenderer {
+
+class Caches;
+class Matrix4;
+class RenderState;
+class Texture;
+class VertexBuffer;
+struct Glop;
+
+enum class TextureFillFlags {
+ kNone = 0,
+ kIsAlphaMaskTexture = 1 << 0,
+ kForceFilter = 1 << 1,
+};
+MAKE_FLAGS_ENUM(TextureFillFlags);
+
+class GlopBuilder {
+ PREVENT_COPY_AND_ASSIGN(GlopBuilder);
+public:
+ GlopBuilder(RenderState& renderState, Caches& caches, Glop* outGlop);
+
+ GlopBuilder& setMeshUnitQuad();
+ GlopBuilder& setMeshTexturedUnitQuad(const UvMapper* uvMapper);
+ GlopBuilder& setMeshTexturedUvQuad(const UvMapper* uvMapper, const Rect uvs);
+ GlopBuilder& setMeshVertexBuffer(const VertexBuffer& vertexBuffer, bool shadowInterp);
+ GlopBuilder& setMeshIndexedQuads(Vertex* vertexData, int quadCount);
+ GlopBuilder& setMeshTexturedMesh(TextureVertex* vertexData, int elementCount); // TODO: use indexed quads
+ GlopBuilder& setMeshColoredTexturedMesh(ColorTextureVertex* vertexData, int elementCount); // TODO: use indexed quads
+ GlopBuilder& setMeshTexturedIndexedQuads(TextureVertex* vertexData, int elementCount); // TODO: take quadCount
+ GlopBuilder& setMeshPatchQuads(const Patch& patch);
+
+ GlopBuilder& setFillPaint(const SkPaint& paint, float alphaScale);
+ GlopBuilder& setFillTexturePaint(Texture& texture, int textureFillFlags,
+ const SkPaint* paint, float alphaScale);
+ GlopBuilder& setFillPathTexturePaint(PathTexture& texture,
+ const SkPaint& paint, float alphaScale);
+ GlopBuilder& setFillShadowTexturePaint(ShadowTexture& texture, int shadowColor,
+ const SkPaint& paint, float alphaScale);
+ GlopBuilder& setFillBlack();
+ GlopBuilder& setFillClear();
+ GlopBuilder& setFillLayer(Texture& texture, const SkColorFilter* colorFilter,
+ float alpha, SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage);
+ GlopBuilder& setFillTextureLayer(Layer& layer, float alpha);
+
+ GlopBuilder& setTransform(const Matrix4& ortho, const Matrix4& transform, bool fudgingOffset);
+
+ GlopBuilder& setModelViewMapUnitToRect(const Rect destination);
+ GlopBuilder& setModelViewMapUnitToRectSnap(const Rect destination);
+ GlopBuilder& setModelViewMapUnitToRectOptionalSnap(bool snap, const Rect& destination) {
+ if (snap) {
+ return setModelViewMapUnitToRectSnap(destination);
+ } else {
+ return setModelViewMapUnitToRect(destination);
+ }
+ }
+ GlopBuilder& setModelViewOffsetRect(float offsetX, float offsetY, const Rect source);
+ GlopBuilder& setModelViewOffsetRectSnap(float offsetX, float offsetY, const Rect source);
+ GlopBuilder& setModelViewOffsetRectOptionalSnap(bool snap,
+ float offsetX, float offsetY, const Rect& source) {
+ if (snap) {
+ return setModelViewOffsetRectSnap(offsetX, offsetY, source);
+ } else {
+ return setModelViewOffsetRect(offsetX, offsetY, source);
+ }
+ }
+
+ GlopBuilder& setRoundRectClipState(const RoundRectClipState* roundRectClipState);
+
+ void build();
+private:
+ void setFill(int color, float alphaScale,
+ SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage,
+ const SkShader* shader, const SkColorFilter* colorFilter);
+
+ enum StageFlags {
+ kInitialStage = 0,
+ kMeshStage = 1 << 0,
+ kTransformStage = 1 << 1,
+ kModelViewStage = 1 << 2,
+ kFillStage = 1 << 3,
+ kRoundRectClipStage = 1 << 4,
+ kAllStages = kMeshStage | kFillStage | kTransformStage | kModelViewStage | kRoundRectClipStage,
+ } mStageFlags;
+
+ ProgramDescription mDescription;
+ RenderState& mRenderState;
+ Caches& mCaches;
+ const SkShader* mShader;
+ Glop* mOutGlop;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif // RENDERSTATE_GLOPBUILDER_H
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index ffd1e8c..ea93e7f 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -52,21 +52,24 @@ int GradientCacheEntry::compare(const GradientCacheEntry& lhs, const GradientCac
int deltaInt = int(lhs.count) - int(rhs.count);
if (deltaInt != 0) return deltaInt;
- deltaInt = memcmp(lhs.colors, rhs.colors, lhs.count * sizeof(uint32_t));
+ deltaInt = memcmp(lhs.colors.get(), rhs.colors.get(), lhs.count * sizeof(uint32_t));
if (deltaInt != 0) return deltaInt;
- return memcmp(lhs.positions, rhs.positions, lhs.count * sizeof(float));
+ return memcmp(lhs.positions.get(), rhs.positions.get(), lhs.count * sizeof(float));
}
///////////////////////////////////////////////////////////////////////////////
// Constructors/destructor
///////////////////////////////////////////////////////////////////////////////
-GradientCache::GradientCache():
- mCache(LruCache<GradientCacheEntry, Texture*>::kUnlimitedCapacity),
- mSize(0), mMaxSize(MB(DEFAULT_GRADIENT_CACHE_SIZE)) {
+GradientCache::GradientCache(Extensions& extensions)
+ : mCache(LruCache<GradientCacheEntry, Texture*>::kUnlimitedCapacity)
+ , mSize(0)
+ , mMaxSize(MB(DEFAULT_GRADIENT_CACHE_SIZE))
+ , mUseFloatTexture(extensions.hasFloatTextures())
+ , mHasNpot(extensions.hasNPot()){
char property[PROPERTY_VALUE_MAX];
- if (property_get(PROPERTY_GRADIENT_CACHE_SIZE, property, NULL) > 0) {
+ if (property_get(PROPERTY_GRADIENT_CACHE_SIZE, property, nullptr) > 0) {
INIT_LOGD(" Setting gradient cache size to %sMB", property);
setMaxSize(MB(atof(property)));
} else {
@@ -76,16 +79,6 @@ GradientCache::GradientCache():
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
mCache.setOnEntryRemovedListener(this);
-
- const Extensions& extensions = Extensions::getInstance();
- mUseFloatTexture = extensions.hasFloatTextures();
- mHasNpot = extensions.hasNPot();
-}
-
-GradientCache::GradientCache(uint32_t maxByteSize):
- mCache(LruCache<GradientCacheEntry, Texture*>::kUnlimitedCapacity),
- mSize(0), mMaxSize(maxByteSize) {
- mCache.setOnEntryRemovedListener(this);
}
GradientCache::~GradientCache() {
@@ -173,7 +166,7 @@ Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient,
GradientInfo info;
getGradientInfo(colors, count, info);
- Texture* texture = new Texture();
+ Texture* texture = new Texture(Caches::getInstance());
texture->width = info.width;
texture->height = 2;
texture->blend = info.hasAlpha;
@@ -285,7 +278,7 @@ void GradientCache::generateTexture(uint32_t* colors, float* positions, Texture*
memcpy(pixels + rowBytes, pixels, rowBytes);
glGenTextures(1, &texture->id);
- Caches::getInstance().bindTexture(texture->id);
+ Caches::getInstance().textureState().bindTexture(texture->id);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
if (mUseFloatTexture) {
diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h
index 6a783b1..08319ea 100644
--- a/libs/hwui/GradientCache.h
+++ b/libs/hwui/GradientCache.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_HWUI_GRADIENT_CACHE_H
#define ANDROID_HWUI_GRADIENT_CACHE_H
+#include <memory>
+
#include <GLES3/gl3.h>
#include <SkShader.h>
@@ -25,16 +27,16 @@
#include <utils/Mutex.h>
#include <utils/Vector.h>
-#include "Texture.h"
-
namespace android {
namespace uirenderer {
+class Texture;
+
struct GradientCacheEntry {
GradientCacheEntry() {
count = 0;
- colors = NULL;
- positions = NULL;
+ colors = nullptr;
+ positions = nullptr;
}
GradientCacheEntry(uint32_t* colors, float* positions, uint32_t count) {
@@ -42,20 +44,12 @@ struct GradientCacheEntry {
}
GradientCacheEntry(const GradientCacheEntry& entry) {
- copy(entry.colors, entry.positions, entry.count);
- }
-
- ~GradientCacheEntry() {
- delete[] colors;
- delete[] positions;
+ copy(entry.colors.get(), entry.positions.get(), entry.count);
}
GradientCacheEntry& operator=(const GradientCacheEntry& entry) {
if (this != &entry) {
- delete[] colors;
- delete[] positions;
-
- copy(entry.colors, entry.positions, entry.count);
+ copy(entry.colors.get(), entry.positions.get(), entry.count);
}
return *this;
@@ -73,18 +67,18 @@ struct GradientCacheEntry {
return compare(*this, other) != 0;
}
- uint32_t* colors;
- float* positions;
+ std::unique_ptr<uint32_t[]> colors;
+ std::unique_ptr<float[]> positions;
uint32_t count;
private:
void copy(uint32_t* colors, float* positions, uint32_t count) {
this->count = count;
- this->colors = new uint32_t[count];
- this->positions = new float[count];
+ this->colors.reset(new uint32_t[count]);
+ this->positions.reset(new float[count]);
- memcpy(this->colors, colors, count * sizeof(uint32_t));
- memcpy(this->positions, positions, count * sizeof(float));
+ memcpy(this->colors.get(), colors, count * sizeof(uint32_t));
+ memcpy(this->positions.get(), positions, count * sizeof(float));
}
}; // GradientCacheEntry
@@ -110,15 +104,14 @@ inline hash_t hash_type(const GradientCacheEntry& entry) {
*/
class GradientCache: public OnEntryRemoved<GradientCacheEntry, Texture*> {
public:
- GradientCache();
- GradientCache(uint32_t maxByteSize);
+ GradientCache(Extensions& extensions);
~GradientCache();
/**
* Used as a callback when an entry is removed from the cache.
* Do not invoke directly.
*/
- void operator()(GradientCacheEntry& shader, Texture*& texture);
+ void operator()(GradientCacheEntry& shader, Texture*& texture) override;
/**
* Returns the texture associated with the specified shader.
diff --git a/libs/hwui/Image.cpp b/libs/hwui/Image.cpp
index edf3930..a31c546 100644
--- a/libs/hwui/Image.cpp
+++ b/libs/hwui/Image.cpp
@@ -39,7 +39,7 @@ Image::Image(sp<GraphicBuffer> buffer) {
} else {
// Create a 2D texture to sample from the EGLImage
glGenTextures(1, &mTexture);
- Caches::getInstance().bindTexture(mTexture);
+ Caches::getInstance().textureState().bindTexture(mTexture);
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, mImage);
GLenum status = GL_NO_ERROR;
@@ -54,7 +54,7 @@ Image::~Image() {
eglDestroyImageKHR(eglGetDisplay(EGL_DEFAULT_DISPLAY), mImage);
mImage = EGL_NO_IMAGE_KHR;
- Caches::getInstance().deleteTexture(mTexture);
+ Caches::getInstance().textureState().deleteTexture(mTexture);
mTexture = 0;
}
}
diff --git a/libs/hwui/Interpolator.cpp b/libs/hwui/Interpolator.cpp
index ff8ff73..e1b0fc3 100644
--- a/libs/hwui/Interpolator.cpp
+++ b/libs/hwui/Interpolator.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "Interpolator"
-
#include "Interpolator.h"
#include <cmath>
@@ -90,14 +88,12 @@ float OvershootInterpolator::interpolate(float t) {
return t * t * ((mTension + 1) * t + mTension) + 1.0f;
}
-LUTInterpolator::LUTInterpolator(float* values, size_t size) {
- mValues = values;
- mSize = size;
+LUTInterpolator::LUTInterpolator(float* values, size_t size)
+ : mValues(values)
+ , mSize(size) {
}
LUTInterpolator::~LUTInterpolator() {
- delete mValues;
- mValues = 0;
}
float LUTInterpolator::interpolate(float input) {
@@ -114,7 +110,7 @@ float LUTInterpolator::interpolate(float input) {
LOG_ALWAYS_FATAL_IF(i1 < 0 || i2 < 0, "negatives in interpolation!"
" i1=%d, i2=%d, input=%f, lutpos=%f, size=%zu, values=%p, ipart=%f, weight=%f",
- i1, i2, input, lutpos, mSize, mValues, ipart, weight);
+ i1, i2, input, lutpos, mSize, mValues.get(), ipart, weight);
float v1 = mValues[i1];
float v2 = mValues[i2];
diff --git a/libs/hwui/Interpolator.h b/libs/hwui/Interpolator.h
index dfa0a85..66ce119 100644
--- a/libs/hwui/Interpolator.h
+++ b/libs/hwui/Interpolator.h
@@ -17,6 +17,7 @@
#define INTERPOLATOR_H
#include <stddef.h>
+#include <memory>
#include <cutils/compiler.h>
@@ -37,13 +38,13 @@ protected:
class ANDROID_API AccelerateDecelerateInterpolator : public Interpolator {
public:
- virtual float interpolate(float input);
+ virtual float interpolate(float input) override;
};
class ANDROID_API AccelerateInterpolator : public Interpolator {
public:
AccelerateInterpolator(float factor) : mFactor(factor), mDoubleFactor(factor*2) {}
- virtual float interpolate(float input);
+ virtual float interpolate(float input) override;
private:
const float mFactor;
const float mDoubleFactor;
@@ -52,7 +53,7 @@ private:
class ANDROID_API AnticipateInterpolator : public Interpolator {
public:
AnticipateInterpolator(float tension) : mTension(tension) {}
- virtual float interpolate(float input);
+ virtual float interpolate(float input) override;
private:
const float mTension;
};
@@ -60,20 +61,20 @@ private:
class ANDROID_API AnticipateOvershootInterpolator : public Interpolator {
public:
AnticipateOvershootInterpolator(float tension) : mTension(tension) {}
- virtual float interpolate(float input);
+ virtual float interpolate(float input) override;
private:
const float mTension;
};
class ANDROID_API BounceInterpolator : public Interpolator {
public:
- virtual float interpolate(float input);
+ virtual float interpolate(float input) override;
};
class ANDROID_API CycleInterpolator : public Interpolator {
public:
CycleInterpolator(float cycles) : mCycles(cycles) {}
- virtual float interpolate(float input);
+ virtual float interpolate(float input) override;
private:
const float mCycles;
};
@@ -81,20 +82,20 @@ private:
class ANDROID_API DecelerateInterpolator : public Interpolator {
public:
DecelerateInterpolator(float factor) : mFactor(factor) {}
- virtual float interpolate(float input);
+ virtual float interpolate(float input) override;
private:
const float mFactor;
};
class ANDROID_API LinearInterpolator : public Interpolator {
public:
- virtual float interpolate(float input) { return input; }
+ virtual float interpolate(float input) override { return input; }
};
class ANDROID_API OvershootInterpolator : public Interpolator {
public:
OvershootInterpolator(float tension) : mTension(tension) {}
- virtual float interpolate(float input);
+ virtual float interpolate(float input) override;
private:
const float mTension;
};
@@ -104,10 +105,10 @@ public:
LUTInterpolator(float* values, size_t size);
~LUTInterpolator();
- virtual float interpolate(float input);
+ virtual float interpolate(float input) override;
private:
- float* mValues;
+ std::unique_ptr<float[]> mValues;
size_t mSize;
};
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
new file mode 100644
index 0000000..cc5c403
--- /dev/null
+++ b/libs/hwui/JankTracker.cpp
@@ -0,0 +1,276 @@
+/*
+ * 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 "JankTracker.h"
+
+#include <algorithm>
+#include <cutils/ashmem.h>
+#include <cutils/log.h>
+#include <cstdio>
+#include <errno.h>
+#include <inttypes.h>
+#include <sys/mman.h>
+
+namespace android {
+namespace uirenderer {
+
+static const char* JANK_TYPE_NAMES[] = {
+ "Missed Vsync",
+ "High input latency",
+ "Slow UI thread",
+ "Slow bitmap uploads",
+ "Slow draw",
+};
+
+struct Comparison {
+ FrameInfoIndex start;
+ FrameInfoIndex end;
+};
+
+static const Comparison COMPARISONS[] = {
+ {FrameInfoIndex::kIntendedVsync, FrameInfoIndex::kVsync},
+ {FrameInfoIndex::kOldestInputEvent, FrameInfoIndex::kVsync},
+ {FrameInfoIndex::kVsync, FrameInfoIndex::kSyncStart},
+ {FrameInfoIndex::kSyncStart, FrameInfoIndex::kIssueDrawCommandsStart},
+ {FrameInfoIndex::kIssueDrawCommandsStart, FrameInfoIndex::kFrameCompleted},
+};
+
+// If the event exceeds 10 seconds throw it away, this isn't a jank event
+// it's an ANR and will be handled as such
+static const int64_t IGNORE_EXCEEDING = seconds_to_nanoseconds(10);
+
+/*
+ * Frames that are exempt from jank metrics.
+ * First-draw frames, for example, are expected to
+ * be slow, this is hidden from the user with window animations and
+ * other tricks
+ *
+ * Similarly, we don't track direct-drawing via Surface:lockHardwareCanvas()
+ * for now
+ *
+ * TODO: kSurfaceCanvas can negatively impact other drawing by using up
+ * time on the RenderThread, figure out how to attribute that as a jank-causer
+ */
+static const int64_t EXEMPT_FRAMES_FLAGS
+ = FrameInfoFlags::kWindowLayoutChanged
+ | FrameInfoFlags::kSurfaceCanvas;
+
+// The bucketing algorithm controls so to speak
+// If a frame is <= to this it goes in bucket 0
+static const uint32_t kBucketMinThreshold = 7;
+// If a frame is > this, start counting in increments of 2ms
+static const uint32_t kBucket2msIntervals = 32;
+// If a frame is > this, start counting in increments of 4ms
+static const uint32_t kBucket4msIntervals = 48;
+
+// This will be called every frame, performance sensitive
+// Uses bit twiddling to avoid branching while achieving the packing desired
+static uint32_t frameCountIndexForFrameTime(nsecs_t frameTime, uint32_t max) {
+ uint32_t index = static_cast<uint32_t>(ns2ms(frameTime));
+ // If index > kBucketMinThreshold mask will be 0xFFFFFFFF as a result
+ // of negating 1 (twos compliment, yaay) else mask will be 0
+ uint32_t mask = -(index > kBucketMinThreshold);
+ // If index > threshold, this will essentially perform:
+ // amountAboveThreshold = index - threshold;
+ // index = threshold + (amountAboveThreshold / 2)
+ // However if index is <= this will do nothing. It will underflow, do
+ // a right shift by 0 (no-op), then overflow back to the original value
+ index = ((index - kBucket4msIntervals) >> (index > kBucket4msIntervals))
+ + kBucket4msIntervals;
+ index = ((index - kBucket2msIntervals) >> (index > kBucket2msIntervals))
+ + kBucket2msIntervals;
+ // If index was < minThreshold at the start of all this it's going to
+ // be a pretty garbage value right now. However, mask is 0 so we'll end
+ // up with the desired result of 0.
+ index = (index - kBucketMinThreshold) & mask;
+ return index < max ? index : max;
+}
+
+// Only called when dumping stats, less performance sensitive
+static uint32_t frameTimeForFrameCountIndex(uint32_t index) {
+ index = index + kBucketMinThreshold;
+ if (index > kBucket2msIntervals) {
+ index += (index - kBucket2msIntervals);
+ }
+ if (index > kBucket4msIntervals) {
+ // This works because it was already doubled by the above if
+ // 1 is added to shift slightly more towards the middle of the bucket
+ index += (index - kBucket4msIntervals) + 1;
+ }
+ return index;
+}
+
+JankTracker::JankTracker(nsecs_t frameIntervalNanos) {
+ // By default this will use malloc memory. It may be moved later to ashmem
+ // if there is shared space for it and a request comes in to do that.
+ mData = new ProfileData;
+ reset();
+ setFrameInterval(frameIntervalNanos);
+}
+
+JankTracker::~JankTracker() {
+ freeData();
+}
+
+void JankTracker::freeData() {
+ if (mIsMapped) {
+ munmap(mData, sizeof(ProfileData));
+ } else {
+ delete mData;
+ }
+ mIsMapped = false;
+ mData = nullptr;
+}
+
+void JankTracker::switchStorageToAshmem(int ashmemfd) {
+ int regionSize = ashmem_get_size_region(ashmemfd);
+ if (regionSize < static_cast<int>(sizeof(ProfileData))) {
+ ALOGW("Ashmem region is too small! Received %d, required %u",
+ regionSize, static_cast<unsigned int>(sizeof(ProfileData)));
+ return;
+ }
+ ProfileData* newData = reinterpret_cast<ProfileData*>(
+ mmap(NULL, sizeof(ProfileData), PROT_READ | PROT_WRITE,
+ MAP_SHARED, ashmemfd, 0));
+ if (newData == MAP_FAILED) {
+ int err = errno;
+ ALOGW("Failed to move profile data to ashmem fd %d, error = %d",
+ ashmemfd, err);
+ return;
+ }
+
+ // The new buffer may have historical data that we want to build on top of
+ // But let's make sure we don't overflow Just In Case
+ uint32_t divider = 0;
+ if (newData->totalFrameCount > (1 << 24)) {
+ divider = 4;
+ }
+ for (size_t i = 0; i < mData->jankTypeCounts.size(); i++) {
+ newData->jankTypeCounts[i] >>= divider;
+ newData->jankTypeCounts[i] += mData->jankTypeCounts[i];
+ }
+ for (size_t i = 0; i < mData->frameCounts.size(); i++) {
+ newData->frameCounts[i] >>= divider;
+ newData->frameCounts[i] += mData->frameCounts[i];
+ }
+ newData->jankFrameCount >>= divider;
+ newData->jankFrameCount += mData->jankFrameCount;
+ newData->totalFrameCount >>= divider;
+ newData->totalFrameCount += mData->totalFrameCount;
+ if (newData->statStartTime > mData->statStartTime
+ || newData->statStartTime == 0) {
+ newData->statStartTime = mData->statStartTime;
+ }
+
+ freeData();
+ mData = newData;
+ mIsMapped = true;
+}
+
+void JankTracker::setFrameInterval(nsecs_t frameInterval) {
+ mFrameInterval = frameInterval;
+ mThresholds[kMissedVsync] = 1;
+ /*
+ * Due to interpolation and sample rate differences between the touch
+ * panel and the display (example, 85hz touch panel driving a 60hz display)
+ * we call high latency 1.5 * frameinterval
+ *
+ * NOTE: Be careful when tuning this! A theoretical 1,000hz touch panel
+ * on a 60hz display will show kOldestInputEvent - kIntendedVsync of being 15ms
+ * Thus this must always be larger than frameInterval, or it will fail
+ */
+ mThresholds[kHighInputLatency] = static_cast<int64_t>(1.5 * frameInterval);
+
+ // Note that these do not add up to 1. This is intentional. It's to deal
+ // with variance in values, and should be sort of an upper-bound on what
+ // is reasonable to expect.
+ mThresholds[kSlowUI] = static_cast<int64_t>(.5 * frameInterval);
+ mThresholds[kSlowSync] = static_cast<int64_t>(.2 * frameInterval);
+ mThresholds[kSlowRT] = static_cast<int64_t>(.75 * frameInterval);
+
+}
+
+void JankTracker::addFrame(const FrameInfo& frame) {
+ mData->totalFrameCount++;
+ // Fast-path for jank-free frames
+ int64_t totalDuration =
+ frame[FrameInfoIndex::kFrameCompleted] - frame[FrameInfoIndex::kIntendedVsync];
+ uint32_t framebucket = frameCountIndexForFrameTime(
+ totalDuration, mData->frameCounts.size());
+ // Keep the fast path as fast as possible.
+ if (CC_LIKELY(totalDuration < mFrameInterval)) {
+ mData->frameCounts[framebucket]++;
+ return;
+ }
+
+ if (frame[FrameInfoIndex::kFlags] & EXEMPT_FRAMES_FLAGS) {
+ return;
+ }
+
+ mData->frameCounts[framebucket]++;
+ mData->jankFrameCount++;
+
+ for (int i = 0; i < NUM_BUCKETS; i++) {
+ int64_t delta = frame[COMPARISONS[i].end] - frame[COMPARISONS[i].start];
+ if (delta >= mThresholds[i] && delta < IGNORE_EXCEEDING) {
+ mData->jankTypeCounts[i]++;
+ }
+ }
+}
+
+void JankTracker::dumpBuffer(const void* buffer, size_t bufsize, int fd) {
+ if (bufsize < sizeof(ProfileData)) {
+ return;
+ }
+ const ProfileData* data = reinterpret_cast<const ProfileData*>(buffer);
+ dumpData(data, fd);
+}
+
+void JankTracker::dumpData(const ProfileData* data, int fd) {
+ dprintf(fd, "\nStats since: %" PRIu64 "ns", data->statStartTime);
+ dprintf(fd, "\nTotal frames rendered: %u", data->totalFrameCount);
+ dprintf(fd, "\nJanky frames: %u (%.2f%%)", data->jankFrameCount,
+ (float) data->jankFrameCount / (float) data->totalFrameCount * 100.0f);
+ dprintf(fd, "\n90th percentile: %ums", findPercentile(data, 90));
+ dprintf(fd, "\n95th percentile: %ums", findPercentile(data, 95));
+ dprintf(fd, "\n99th percentile: %ums", findPercentile(data, 99));
+ for (int i = 0; i < NUM_BUCKETS; i++) {
+ dprintf(fd, "\nNumber %s: %u", JANK_TYPE_NAMES[i], data->jankTypeCounts[i]);
+ }
+ dprintf(fd, "\n");
+}
+
+void JankTracker::reset() {
+ mData->jankTypeCounts.fill(0);
+ mData->frameCounts.fill(0);
+ mData->totalFrameCount = 0;
+ mData->jankFrameCount = 0;
+ mData->statStartTime = systemTime(CLOCK_MONOTONIC);
+}
+
+uint32_t JankTracker::findPercentile(const ProfileData* data, int percentile) {
+ int pos = percentile * data->totalFrameCount / 100;
+ int remaining = data->totalFrameCount - pos;
+ for (int i = data->frameCounts.size() - 1; i >= 0; i--) {
+ remaining -= data->frameCounts[i];
+ if (remaining <= 0) {
+ return frameTimeForFrameCountIndex(i);
+ }
+ }
+ return 0;
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/JankTracker.h b/libs/hwui/JankTracker.h
new file mode 100644
index 0000000..3887e5e
--- /dev/null
+++ b/libs/hwui/JankTracker.h
@@ -0,0 +1,87 @@
+/*
+ * 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 JANKTRACKER_H_
+#define JANKTRACKER_H_
+
+#include "FrameInfo.h"
+#include "renderthread/TimeLord.h"
+#include "utils/RingBuffer.h"
+
+#include <cutils/compiler.h>
+
+#include <array>
+#include <memory>
+
+namespace android {
+namespace uirenderer {
+
+enum JankType {
+ kMissedVsync = 0,
+ kHighInputLatency,
+ kSlowUI,
+ kSlowSync,
+ kSlowRT,
+
+ // must be last
+ NUM_BUCKETS,
+};
+
+// Try to keep as small as possible, should match ASHMEM_SIZE in
+// GraphicsStatsService.java
+struct ProfileData {
+ std::array<uint32_t, NUM_BUCKETS> jankTypeCounts;
+ // See comments on kBucket* constants for what this holds
+ std::array<uint32_t, 55> frameCounts;
+
+ uint32_t totalFrameCount;
+ uint32_t jankFrameCount;
+ nsecs_t statStartTime;
+};
+
+// TODO: Replace DrawProfiler with this
+class JankTracker {
+public:
+ JankTracker(nsecs_t frameIntervalNanos);
+ ~JankTracker();
+
+ void addFrame(const FrameInfo& frame);
+
+ void dump(int fd) { dumpData(mData, fd); }
+ void reset();
+
+ void switchStorageToAshmem(int ashmemfd);
+
+ uint32_t findPercentile(int p) { return findPercentile(mData, p); }
+
+ ANDROID_API static void dumpBuffer(const void* buffer, size_t bufsize, int fd);
+
+private:
+ void freeData();
+ void setFrameInterval(nsecs_t frameIntervalNanos);
+
+ static uint32_t findPercentile(const ProfileData* data, int p);
+ static void dumpData(const ProfileData* data, int fd);
+
+ std::array<int64_t, NUM_BUCKETS> mThresholds;
+ int64_t mFrameInterval;
+ ProfileData* mData;
+ bool mIsMapped = false;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* JANKTRACKER_H_ */
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 05259ff..458f35b 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -16,17 +16,18 @@
#define LOG_TAG "OpenGLRenderer"
-#include <utils/Log.h>
+#include "Layer.h"
#include "Caches.h"
#include "DeferredDisplayList.h"
-#include "Layer.h"
#include "LayerRenderer.h"
#include "OpenGLRenderer.h"
#include "RenderNode.h"
-#include "RenderState.h"
+#include "renderstate/RenderState.h"
#include "utils/TraceUtils.h"
+#include <utils/Log.h>
+
#define ATRACE_LAYER_WORK(label) \
ATRACE_FORMAT("%s HW Layer DisplayList %s %ux%u", \
label, \
@@ -36,7 +37,7 @@
namespace android {
namespace uirenderer {
-Layer::Layer(Type layerType, RenderState& renderState, const uint32_t layerWidth, const uint32_t layerHeight)
+Layer::Layer(Type layerType, RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight)
: state(kState_Uncached)
, caches(Caches::getInstance())
, renderState(renderState)
@@ -44,27 +45,10 @@ Layer::Layer(Type layerType, RenderState& renderState, const uint32_t layerWidth
, type(layerType) {
// TODO: This is a violation of Android's typical ref counting, but it
// preserves the old inc/dec ref locations. This should be changed...
- incStrong(0);
- mesh = NULL;
- meshElementCount = 0;
- cacheable = true;
- dirty = false;
+ incStrong(nullptr);
renderTarget = GL_TEXTURE_2D;
texture.width = layerWidth;
texture.height = layerHeight;
- colorFilter = NULL;
- deferredUpdateScheduled = false;
- renderer = NULL;
- renderNode = NULL;
- fbo = 0;
- stencil = NULL;
- debugDrawUpdate = false;
- hasDrawnSinceUpdate = false;
- forceFilter = false;
- deferredList = NULL;
- convexMask = NULL;
- rendererLightPosDirty = true;
- wasBuildLayered = false;
renderState.registerLayer(this);
}
@@ -79,8 +63,6 @@ Layer::~Layer() {
}
delete[] mesh;
- delete deferredList;
- delete renderer;
}
void Layer::onGlContextLost() {
@@ -98,7 +80,7 @@ uint32_t Layer::computeIdealHeight(uint32_t layerHeight) {
void Layer::requireRenderer() {
if (!renderer) {
- renderer = new LayerRenderer(renderState, this);
+ renderer.reset(new LayerRenderer(renderState, this));
renderer->initProperties();
}
}
@@ -137,7 +119,7 @@ bool Layer::resize(const uint32_t width, const uint32_t height) {
setSize(desiredWidth, desiredHeight);
if (fbo) {
- caches.activeTexture(0);
+ caches.textureState().activateTexture(0);
bindTexture();
allocateTexture();
@@ -168,7 +150,7 @@ void Layer::removeFbo(bool flush) {
renderState.bindFramebuffer(previousFbo);
caches.renderBufferCache.put(stencil);
- stencil = NULL;
+ stencil = nullptr;
}
if (fbo) {
@@ -189,7 +171,7 @@ void Layer::updateDeferred(RenderNode* renderNode, int left, int top, int right,
void Layer::setPaint(const SkPaint* paint) {
OpenGLRenderer::getAlphaAndModeDirect(paint, &alpha, &mode);
- setColorFilter((paint) ? paint->getColorFilter() : NULL);
+ setColorFilter((paint) ? paint->getColorFilter() : nullptr);
}
void Layer::setColorFilter(SkColorFilter* filter) {
@@ -198,7 +180,7 @@ void Layer::setColorFilter(SkColorFilter* filter) {
void Layer::bindTexture() const {
if (texture.id) {
- caches.bindTexture(renderTarget, texture.id);
+ caches.textureState().bindTexture(renderTarget, texture.id);
}
}
@@ -222,7 +204,7 @@ void Layer::deleteTexture() {
}
void Layer::clearTexture() {
- caches.unbindTexture(texture.id);
+ caches.textureState().unbindTexture(texture.id);
texture.id = 0;
}
@@ -233,7 +215,7 @@ void Layer::allocateTexture() {
if (texture.id) {
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glTexImage2D(renderTarget, 0, GL_RGBA, getWidth(), getHeight(), 0,
- GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+ GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
}
}
@@ -249,8 +231,7 @@ void Layer::defer(const OpenGLRenderer& rootRenderer) {
dirtyRect.set(0, 0, width, height);
}
- delete deferredList;
- deferredList = new DeferredDisplayList(dirtyRect);
+ deferredList.reset(new DeferredDisplayList(dirtyRect));
DeferStateStruct deferredState(*deferredList, *renderer,
RenderNode::kReplayFlag_ClipChildren);
@@ -266,19 +247,16 @@ void Layer::defer(const OpenGLRenderer& rootRenderer) {
}
void Layer::cancelDefer() {
- renderNode = NULL;
+ renderNode = nullptr;
deferredUpdateScheduled = false;
- if (deferredList) {
- delete deferredList;
- deferredList = NULL;
- }
+ deferredList.release();
}
void Layer::flush() {
// renderer is checked as layer may be destroyed/put in layer cache with flush scheduled
if (deferredList && renderer) {
ATRACE_LAYER_WORK("Issue");
- renderer->startMark((renderNode.get() != NULL) ? renderNode->getName() : "Layer");
+ renderer->startMark((renderNode.get() != nullptr) ? renderNode->getName() : "Layer");
renderer->setViewport(layer.getWidth(), layer.getHeight());
renderer->prepareDirty(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom,
@@ -289,7 +267,7 @@ void Layer::flush() {
renderer->finish();
dirtyRect.setEmpty();
- renderNode = NULL;
+ renderNode = nullptr;
renderer->endMark();
}
@@ -310,7 +288,7 @@ void Layer::render(const OpenGLRenderer& rootRenderer) {
dirtyRect.setEmpty();
deferredUpdateScheduled = false;
- renderNode = NULL;
+ renderNode = nullptr;
}
void Layer::postDecStrong() {
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index e196cb1..b670870 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -20,6 +20,8 @@
#include <cutils/compiler.h>
#include <sys/types.h>
#include <utils/StrongPointer.h>
+#include <utils/RefBase.h>
+#include <memory>
#include <GLES2/gl2.h>
@@ -43,11 +45,11 @@ namespace uirenderer {
// Forward declarations
class Caches;
+class RenderNode;
class RenderState;
class OpenGLRenderer;
-class RenderNode;
class DeferredDisplayList;
-class DeferStateStruct;
+struct DeferStateStruct;
/**
* A layer has dimensions and is backed by an OpenGL texture or FBO.
@@ -70,7 +72,7 @@ public:
};
State state; // public for logging/debugging purposes
- Layer(Type type, RenderState& renderState, const uint32_t layerWidth, const uint32_t layerHeight);
+ Layer(Type type, RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight);
~Layer();
static uint32_t computeIdealWidth(uint32_t layerWidth);
@@ -198,10 +200,14 @@ public:
return stencil;
}
- inline GLuint getTexture() const {
+ inline GLuint getTextureId() const {
return texture.id;
}
+ inline Texture& getTexture() {
+ return texture;
+ }
+
inline GLenum getRenderTarget() const {
return renderTarget;
}
@@ -318,19 +324,19 @@ public:
/**
* If the layer can be rendered as a mesh, this is non-null.
*/
- TextureVertex* mesh;
- GLsizei meshElementCount;
+ TextureVertex* mesh = nullptr;
+ GLsizei meshElementCount = 0;
/**
* Used for deferred updates.
*/
- bool deferredUpdateScheduled;
- OpenGLRenderer* renderer;
+ bool deferredUpdateScheduled = false;
+ std::unique_ptr<OpenGLRenderer> renderer;
sp<RenderNode> renderNode;
Rect dirtyRect;
- bool debugDrawUpdate;
- bool hasDrawnSinceUpdate;
- bool wasBuildLayered;
+ bool debugDrawUpdate = false;
+ bool hasDrawnSinceUpdate = false;
+ bool wasBuildLayered = false;
private:
void requireRenderer();
@@ -344,17 +350,17 @@ private:
* Name of the FBO used to render the layer. If the name is 0
* this layer is not backed by an FBO, but a simple texture.
*/
- GLuint fbo;
+ GLuint fbo = 0;
/**
* The render buffer used as the stencil buffer.
*/
- RenderBuffer* stencil;
+ RenderBuffer* stencil = nullptr;
/**
* Indicates whether this layer has been used already.
*/
- bool empty;
+ bool empty = true;
/**
* The texture backing this layer.
@@ -364,7 +370,7 @@ private:
/**
* If set to true (by default), the layer can be reused.
*/
- bool cacheable;
+ bool cacheable = true;
/**
* Denotes whether the layer is a DisplayList, or Texture layer.
@@ -375,32 +381,32 @@ private:
* When set to true, this layer is dirty and should be cleared
* before any rendering occurs.
*/
- bool dirty;
+ bool dirty = false;
/**
* Indicates the render target.
*/
- GLenum renderTarget;
+ GLenum renderTarget = GL_TEXTURE_2D;
/**
* Color filter used to draw this layer. Optional.
*/
- SkColorFilter* colorFilter;
+ SkColorFilter* colorFilter = nullptr;
/**
* Indicates raster data backing the layer is scaled, requiring filtration.
*/
- bool forceFilter;
+ bool forceFilter = false;
/**
* Opacity of the layer.
*/
- int alpha;
+ int alpha = 255;
/**
* Blending mode of the layer.
*/
- SkXfermode::Mode mode;
+ SkXfermode::Mode mode = SkXfermode::kSrcOver_Mode;
/**
* Optional texture coordinates transform.
@@ -416,20 +422,20 @@ private:
* Cached transform of layer in window, updated only on creation / resize
*/
mat4 cachedInvTransformInWindow;
- bool rendererLightPosDirty;
+ bool rendererLightPosDirty = true;
/**
* Used to defer display lists when the layer is updated with a
* display list.
*/
- DeferredDisplayList* deferredList;
+ std::unique_ptr<DeferredDisplayList> deferredList;
/**
* This convex path should be used to mask the layer's draw to the screen.
*
* Data not owned/managed by layer object.
*/
- const SkPath* convexMask;
+ const SkPath* convexMask = nullptr;
}; // struct Layer
diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
index 3033dc6..bcbd412 100644
--- a/libs/hwui/LayerCache.cpp
+++ b/libs/hwui/LayerCache.cpp
@@ -33,7 +33,7 @@ namespace uirenderer {
LayerCache::LayerCache(): mSize(0), mMaxSize(MB(DEFAULT_LAYER_CACHE_SIZE)) {
char property[PROPERTY_VALUE_MAX];
- if (property_get(PROPERTY_LAYER_CACHE_SIZE, property, NULL) > 0) {
+ if (property_get(PROPERTY_LAYER_CACHE_SIZE, property, nullptr) > 0) {
INIT_LOGD(" Setting layer cache size to %sMB", property);
setMaxSize(MB(atof(property)));
} else {
@@ -84,7 +84,7 @@ void LayerCache::deleteLayer(Layer* layer) {
layer->getFbo());
mSize -= layer->getWidth() * layer->getHeight() * 4;
layer->state = Layer::kState_DeletedFromCache;
- layer->decStrong(0);
+ layer->decStrong(nullptr);
}
}
@@ -97,7 +97,7 @@ void LayerCache::clear() {
}
Layer* LayerCache::get(RenderState& renderState, const uint32_t width, const uint32_t height) {
- Layer* layer = NULL;
+ Layer* layer = nullptr;
LayerEntry entry(width, height);
ssize_t index = mCache.indexOf(entry);
@@ -116,9 +116,6 @@ Layer* LayerCache::get(RenderState& renderState, const uint32_t width, const uin
layer = new Layer(Layer::kType_DisplayList, renderState, entry.mWidth, entry.mHeight);
layer->setBlend(true);
- layer->setEmpty(true);
- layer->setFbo(0);
-
layer->generateTexture();
layer->bindTexture();
layer->setFilter(GL_NEAREST);
@@ -137,7 +134,7 @@ void LayerCache::dump() {
size_t size = mCache.size();
for (size_t i = 0; i < size; i++) {
const LayerEntry& entry = mCache.itemAt(i);
- LAYER_LOGD(" Layer size %dx%d", entry.mWidth, entry.mHeight);
+ ALOGD(" Layer size %dx%d", entry.mWidth, entry.mHeight);
}
}
diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h
index 6b93e8f..7d17b9b 100644
--- a/libs/hwui/LayerCache.h
+++ b/libs/hwui/LayerCache.h
@@ -30,7 +30,6 @@ class RenderState;
// Defines
///////////////////////////////////////////////////////////////////////////////
-// Debug
#if DEBUG_LAYERS
#define LAYER_LOGD(...) ALOGD(__VA_ARGS__)
#else
@@ -97,10 +96,10 @@ public:
private:
struct LayerEntry {
LayerEntry():
- mLayer(NULL), mWidth(0), mHeight(0) {
+ mLayer(nullptr), mWidth(0), mHeight(0) {
}
- LayerEntry(const uint32_t layerWidth, const uint32_t layerHeight): mLayer(NULL) {
+ LayerEntry(const uint32_t layerWidth, const uint32_t layerHeight): mLayer(nullptr) {
mWidth = Layer::computeIdealWidth(layerWidth);
mHeight = Layer::computeIdealHeight(layerHeight);
}
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 83f9c6a..223bdf0 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -17,18 +17,19 @@
#define LOG_TAG "OpenGLRenderer"
#define ATRACE_TAG ATRACE_TAG_VIEW
-#include <ui/Rect.h>
-
-#include <private/hwui/DrawGlInfo.h>
-
-#include "RenderState.h"
#include "LayerCache.h"
#include "LayerRenderer.h"
#include "Matrix.h"
#include "Properties.h"
#include "Rect.h"
+#include "renderstate/RenderState.h"
#include "utils/TraceUtils.h"
+#include <ui/Rect.h>
+
+#include <private/hwui/DrawGlInfo.h>
+
+
namespace android {
namespace uirenderer {
@@ -44,11 +45,11 @@ LayerRenderer::LayerRenderer(RenderState& renderState, Layer* layer)
LayerRenderer::~LayerRenderer() {
}
-status_t LayerRenderer::prepareDirty(float left, float top, float right, float bottom,
+void LayerRenderer::prepareDirty(float left, float top, float right, float bottom,
bool opaque) {
LAYER_RENDERER_LOGD("Rendering into layer, fbo = %d", mLayer->getFbo());
- renderState().bindFramebuffer(mLayer->getFbo());
+ mRenderState.bindFramebuffer(mLayer->getFbo());
const float width = mLayer->layer.getWidth();
const float height = mLayer->layer.getHeight();
@@ -65,25 +66,23 @@ status_t LayerRenderer::prepareDirty(float left, float top, float right, float b
}
mLayer->clipRect.set(dirty);
- return OpenGLRenderer::prepareDirty(dirty.left, dirty.top, dirty.right, dirty.bottom, opaque);
+ OpenGLRenderer::prepareDirty(dirty.left, dirty.top, dirty.right, dirty.bottom, opaque);
}
-status_t LayerRenderer::clear(float left, float top, float right, float bottom, bool opaque) {
+void LayerRenderer::clear(float left, float top, float right, float bottom, bool opaque) {
if (mLayer->isDirty()) {
- getCaches().disableScissor();
+ mRenderState.scissor().setEnabled(false);
glClear(GL_COLOR_BUFFER_BIT);
- getCaches().resetScissor();
+ mRenderState.scissor().reset();
mLayer->setDirty(false);
-
- return DrawGlInfo::kStatusDone;
+ } else {
+ OpenGLRenderer::clear(left, top, right, bottom, opaque);
}
-
- return OpenGLRenderer::clear(left, top, right, bottom, opaque);
}
-void LayerRenderer::finish() {
- OpenGLRenderer::finish();
+bool LayerRenderer::finish() {
+ bool retval = OpenGLRenderer::finish();
generateMesh();
@@ -91,6 +90,7 @@ void LayerRenderer::finish() {
// No need to unbind our FBO, this will be taken care of by the caller
// who will invoke OpenGLRenderer::resume()
+ return retval;
}
GLuint LayerRenderer::getTargetFbo() const {
@@ -118,7 +118,7 @@ void LayerRenderer::ensureStencilBuffer() {
///////////////////////////////////////////////////////////////////////////////
Region* LayerRenderer::getRegion() const {
- if (currentSnapshot()->flags & Snapshot::kFlagFboTarget) {
+ if (mState.currentFlags() & Snapshot::kFlagFboTarget) {
return OpenGLRenderer::getRegion();
}
return &mLayer->region;
@@ -131,7 +131,7 @@ void LayerRenderer::generateMesh() {
if (mLayer->region.isRect() || mLayer->region.isEmpty()) {
if (mLayer->mesh) {
delete[] mLayer->mesh;
- mLayer->mesh = NULL;
+ mLayer->mesh = nullptr;
mLayer->meshElementCount = 0;
}
@@ -152,7 +152,7 @@ void LayerRenderer::generateMesh() {
if (mLayer->mesh && mLayer->meshElementCount < elementCount) {
delete[] mLayer->mesh;
- mLayer->mesh = NULL;
+ mLayer->mesh = nullptr;
}
if (!mLayer->mesh) {
@@ -193,14 +193,14 @@ Layer* LayerRenderer::createRenderLayer(RenderState& renderState, uint32_t width
GLuint fbo = caches.fboCache.get();
if (!fbo) {
ALOGW("Could not obtain an FBO");
- return NULL;
+ return nullptr;
}
- caches.activeTexture(0);
+ caches.textureState().activateTexture(0);
Layer* layer = caches.layerCache.get(renderState, width, height);
if (!layer) {
ALOGW("Could not obtain a layer");
- return NULL;
+ return nullptr;
}
// We first obtain a layer before comparing against the max texture size
@@ -213,9 +213,9 @@ Layer* LayerRenderer::createRenderLayer(RenderState& renderState, uint32_t width
// Creating a new layer always increment its refcount by 1, this allows
// us to destroy the layer object if one was created for us
- layer->decStrong(0);
+ layer->decStrong(nullptr);
- return NULL;
+ return nullptr;
}
layer->setFbo(fbo);
@@ -223,7 +223,7 @@ Layer* LayerRenderer::createRenderLayer(RenderState& renderState, uint32_t width
layer->texCoords.set(0.0f, height / float(layer->getHeight()),
width / float(layer->getWidth()), 0.0f);
layer->setAlpha(255, SkXfermode::kSrcOver_Mode);
- layer->setColorFilter(NULL);
+ layer->setColorFilter(nullptr);
layer->setDirty(true);
layer->region.clear();
@@ -241,13 +241,13 @@ Layer* LayerRenderer::createRenderLayer(RenderState& renderState, uint32_t width
if (glGetError() != GL_NO_ERROR) {
ALOGE("Could not allocate texture for layer (fbo=%d %dx%d)", fbo, width, height);
renderState.bindFramebuffer(previousFbo);
- layer->decStrong(0);
- return NULL;
+ layer->decStrong(nullptr);
+ return nullptr;
}
}
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
- layer->getTexture(), 0);
+ layer->getTextureId(), 0);
renderState.bindFramebuffer(previousFbo);
@@ -275,15 +275,12 @@ Layer* LayerRenderer::createTextureLayer(RenderState& renderState) {
Layer* layer = new Layer(Layer::kType_Texture, renderState, 0, 0);
layer->setCacheable(false);
- layer->setEmpty(true);
- layer->setFbo(0);
- layer->setAlpha(255, SkXfermode::kSrcOver_Mode);
layer->layer.set(0.0f, 0.0f, 0.0f, 0.0f);
layer->texCoords.set(0.0f, 1.0f, 1.0f, 0.0f);
layer->region.clear();
layer->setRenderTarget(GL_NONE); // see ::updateTextureLayer()
- Caches::getInstance().activeTexture(0);
+ Caches::getInstance().textureState().activateTexture(0);
layer->generateTexture();
return layer;
@@ -317,7 +314,7 @@ void LayerRenderer::destroyLayer(Layer* layer) {
if (!Caches::getInstance().layerCache.put(layer)) {
LAYER_RENDERER_LOGD(" Destroyed!");
- layer->decStrong(0);
+ layer->decStrong(nullptr);
} else {
LAYER_RENDERER_LOGD(" Cached!");
#if DEBUG_LAYER_RENDERER
@@ -337,7 +334,7 @@ void LayerRenderer::flushLayer(RenderState& renderState, Layer* layer) {
if (fbo) {
// If possible, discard any enqueud operations on deferred
// rendering architectures
- if (Extensions::getInstance().hasDiscardFramebuffer()) {
+ if (Caches::getInstance().extensions().hasDiscardFramebuffer()) {
GLuint previousFbo = renderState.getFramebuffer();
if (fbo != previousFbo) {
renderState.bindFramebuffer(fbo);
@@ -356,8 +353,9 @@ void LayerRenderer::flushLayer(RenderState& renderState, Layer* layer) {
bool LayerRenderer::copyLayer(RenderState& renderState, Layer* layer, SkBitmap* bitmap) {
Caches& caches = Caches::getInstance();
- if (layer && bitmap->width() <= caches.maxTextureSize &&
- bitmap->height() <= caches.maxTextureSize) {
+ if (layer
+ && bitmap->width() <= caches.maxTextureSize
+ && bitmap->height() <= caches.maxTextureSize) {
GLuint fbo = caches.fboCache.get();
if (!fbo) {
@@ -412,8 +410,8 @@ bool LayerRenderer::copyLayer(RenderState& renderState, Layer* layer, SkBitmap*
glGenTextures(1, &texture);
if ((error = glGetError()) != GL_NO_ERROR) goto error;
- caches.activeTexture(0);
- caches.bindTexture(texture);
+ caches.textureState().activateTexture(0);
+ caches.textureState().bindTexture(texture);
glPixelStorei(GL_PACK_ALIGNMENT, bitmap->bytesPerPixel());
@@ -424,7 +422,7 @@ bool LayerRenderer::copyLayer(RenderState& renderState, Layer* layer, SkBitmap*
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, format, bitmap->width(), bitmap->height(),
- 0, format, type, NULL);
+ 0, format, type, nullptr);
if ((error = glGetError()) != GL_NO_ERROR) goto error;
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
@@ -437,7 +435,7 @@ bool LayerRenderer::copyLayer(RenderState& renderState, Layer* layer, SkBitmap*
renderer.OpenGLRenderer::prepareDirty(0.0f, 0.0f,
bitmap->width(), bitmap->height(), !layer->isBlend());
- caches.disableScissor();
+ renderState.scissor().setEnabled(false);
renderer.translate(0.0f, bitmap->height());
renderer.scale(1.0f, -1.0f);
@@ -475,7 +473,7 @@ error:
renderState.bindFramebuffer(previousFbo);
layer->setAlpha(alpha, mode);
layer->setFbo(previousLayerFbo);
- caches.deleteTexture(texture);
+ caches.textureState().deleteTexture(texture);
caches.fboCache.put(fbo);
renderState.setViewport(previousViewportWidth, previousViewportHeight);
diff --git a/libs/hwui/LayerRenderer.h b/libs/hwui/LayerRenderer.h
index 4d8620b..47ded7e 100644
--- a/libs/hwui/LayerRenderer.h
+++ b/libs/hwui/LayerRenderer.h
@@ -49,10 +49,11 @@ public:
LayerRenderer(RenderState& renderState, Layer* layer);
virtual ~LayerRenderer();
- virtual void onViewportInitialized() { /* do nothing */ }
- virtual status_t prepareDirty(float left, float top, float right, float bottom, bool opaque);
- virtual status_t clear(float left, float top, float right, float bottom, bool opaque);
- virtual void finish();
+ virtual void onViewportInitialized() override { /* do nothing */ }
+ virtual void prepareDirty(float left, float top, float right, float bottom,
+ bool opaque) override;
+ virtual void clear(float left, float top, float right, float bottom, bool opaque) override;
+ virtual bool finish() override;
static Layer* createTextureLayer(RenderState& renderState);
static Layer* createRenderLayer(RenderState& renderState, uint32_t width, uint32_t height);
@@ -65,11 +66,11 @@ public:
static void flushLayer(RenderState& renderState, Layer* layer);
protected:
- virtual void ensureStencilBuffer();
- virtual bool hasLayer() const;
- virtual Region* getRegion() const;
- virtual GLuint getTargetFbo() const;
- virtual bool suppressErrorChecks() const;
+ virtual void ensureStencilBuffer() override;
+ virtual bool hasLayer() const override;
+ virtual Region* getRegion() const override;
+ virtual GLuint getTargetFbo() const override;
+ virtual bool suppressErrorChecks() const override;
private:
void generateMesh();
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index 9f2014f..061d26a 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -203,6 +203,34 @@ void Matrix4::copyTo(SkMatrix& v) const {
}
void Matrix4::loadInverse(const Matrix4& v) {
+ // Fast case for common translation matrices
+ if (v.isPureTranslate()) {
+ // Reset the matrix
+ // Unnamed fields are never written to except by
+ // loadIdentity(), they don't need to be reset
+ data[kScaleX] = 1.0f;
+ data[kSkewX] = 0.0f;
+
+ data[kScaleY] = 1.0f;
+ data[kSkewY] = 0.0f;
+
+ data[kScaleZ] = 1.0f;
+
+ data[kPerspective0] = 0.0f;
+ data[kPerspective1] = 0.0f;
+ data[kPerspective2] = 1.0f;
+
+ // No need to deal with kTranslateZ because isPureTranslate()
+ // only returns true when the kTranslateZ component is 0
+ data[kTranslateX] = -v.data[kTranslateX];
+ data[kTranslateY] = -v.data[kTranslateY];
+ data[kTranslateZ] = 0.0f;
+
+ // A "pure translate" matrix can be identity or translation
+ mType = v.getType();
+ return;
+ }
+
double scale = 1.0 /
(v.data[kScaleX] * ((double) v.data[kScaleY] * v.data[kPerspective2] -
(double) v.data[kTranslateY] * v.data[kPerspective1]) +
@@ -212,18 +240,18 @@ void Matrix4::loadInverse(const Matrix4& v) {
(double) v.data[kScaleY] * v.data[kPerspective0]));
data[kScaleX] = (v.data[kScaleY] * v.data[kPerspective2] -
- v.data[kTranslateY] * v.data[kPerspective1]) * scale;
+ v.data[kTranslateY] * v.data[kPerspective1]) * scale;
data[kSkewX] = (v.data[kTranslateX] * v.data[kPerspective1] -
v.data[kSkewX] * v.data[kPerspective2]) * scale;
data[kTranslateX] = (v.data[kSkewX] * v.data[kTranslateY] -
- v.data[kTranslateX] * v.data[kScaleY]) * scale;
+ v.data[kTranslateX] * v.data[kScaleY]) * scale;
data[kSkewY] = (v.data[kTranslateY] * v.data[kPerspective0] -
v.data[kSkewY] * v.data[kPerspective2]) * scale;
data[kScaleY] = (v.data[kScaleX] * v.data[kPerspective2] -
- v.data[kTranslateX] * v.data[kPerspective0]) * scale;
+ v.data[kTranslateX] * v.data[kPerspective0]) * scale;
data[kTranslateY] = (v.data[kTranslateX] * v.data[kSkewY] -
- v.data[kScaleX] * v.data[kTranslateY]) * scale;
+ v.data[kScaleX] * v.data[kTranslateY]) * scale;
data[kPerspective0] = (v.data[kSkewY] * v.data[kPerspective1] -
v.data[kScaleY] * v.data[kPerspective0]) * scale;
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index 1c5c578..a760135 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -210,7 +210,7 @@ public:
void decomposeScale(float& sx, float& sy) const;
- void dump(const char* label = NULL) const;
+ void dump(const char* label = nullptr) const;
static const Matrix4& identity();
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 6f1e8a2..30935d5 100755..100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -14,7 +14,25 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
+#include "OpenGLRenderer.h"
+
+#include "DeferredDisplayList.h"
+#include "GammaFontRenderer.h"
+#include "Glop.h"
+#include "GlopBuilder.h"
+#include "Patch.h"
+#include "PathTessellator.h"
+#include "Properties.h"
+#include "RenderNode.h"
+#include "renderstate/MeshState.h"
+#include "renderstate/RenderState.h"
+#include "ShadowTessellator.h"
+#include "SkiaShader.h"
+#include "Vector.h"
+#include "VertexBuffer.h"
+#include "utils/GLUtils.h"
+#include "utils/PaintUtils.h"
+#include "utils/TraceUtils.h"
#include <stdlib.h>
#include <stdint.h>
@@ -32,20 +50,6 @@
#include <ui/Rect.h>
-#include "OpenGLRenderer.h"
-#include "DeferredDisplayList.h"
-#include "DisplayListRenderer.h"
-#include "Fence.h"
-#include "RenderState.h"
-#include "PathTessellator.h"
-#include "Properties.h"
-#include "ShadowTessellator.h"
-#include "SkiaShader.h"
-#include "utils/GLUtils.h"
-#include "utils/TraceUtils.h"
-#include "Vector.h"
-#include "VertexBuffer.h"
-
#if DEBUG_DETAILED_EVENTS
#define EVENT_LOGD(...) eventMarkDEBUG(__VA_ARGS__)
#else
@@ -55,88 +59,19 @@
namespace android {
namespace uirenderer {
-static GLenum getFilter(const SkPaint* paint) {
- if (!paint || paint->getFilterLevel() != SkPaint::kNone_FilterLevel) {
- return GL_LINEAR;
- }
- return GL_NEAREST;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Globals
-///////////////////////////////////////////////////////////////////////////////
-
-/**
- * Structure mapping Skia xfermodes to OpenGL blending factors.
- */
-struct Blender {
- SkXfermode::Mode mode;
- GLenum src;
- GLenum dst;
-}; // struct Blender
-
-// In this array, the index of each Blender equals the value of the first
-// entry. For instance, gBlends[1] == gBlends[SkXfermode::kSrc_Mode]
-static const Blender gBlends[] = {
- { 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.
-static const Blender gBlendsSwap[] = {
- { 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 }
-};
-
-///////////////////////////////////////////////////////////////////////////////
-// Functions
-///////////////////////////////////////////////////////////////////////////////
-
-template<typename T>
-static inline T min(T a, T b) {
- return a < b ? a : b;
-}
-
///////////////////////////////////////////////////////////////////////////////
// Constructors/destructor
///////////////////////////////////////////////////////////////////////////////
OpenGLRenderer::OpenGLRenderer(RenderState& renderState)
- : mFrameStarted(false)
+ : mState(*this)
, mCaches(Caches::getInstance())
- , mExtensions(Extensions::getInstance())
, mRenderState(renderState)
+ , mFrameStarted(false)
, mScissorOptimizationDisabled(false)
, mSuppressTiling(false)
, mFirstFrameAfterResize(true)
+ , mDirty(false)
, mLightCenter((Vector3){FLT_MIN, FLT_MIN, FLT_MIN})
, mLightRadius(FLT_MIN)
, mAmbientShadowAlpha(0)
@@ -144,8 +79,6 @@ OpenGLRenderer::OpenGLRenderer(RenderState& renderState)
// *set* draw modifiers to be 0
memset(&mDrawModifiers, 0, sizeof(mDrawModifiers));
mDrawModifiers.mOverrideLayerAlpha = 1.0f;
-
- memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices));
}
OpenGLRenderer::~OpenGLRenderer() {
@@ -179,28 +112,26 @@ void OpenGLRenderer::initLight(const Vector3& lightCenter, float lightRadius,
void OpenGLRenderer::onViewportInitialized() {
glDisable(GL_DITHER);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
-
- glEnableVertexAttribArray(Program::kBindingPosition);
mFirstFrameAfterResize = true;
}
void OpenGLRenderer::setupFrameState(float left, float top,
float right, float bottom, bool opaque) {
mCaches.clearGarbage();
- initializeSaveStack(left, top, right, bottom, mLightCenter);
+ mState.initializeSaveStack(left, top, right, bottom, mLightCenter);
mOpaque = opaque;
mTilingClip.set(left, top, right, bottom);
}
-status_t OpenGLRenderer::startFrame() {
- if (mFrameStarted) return DrawGlInfo::kStatusDone;
+void OpenGLRenderer::startFrame() {
+ if (mFrameStarted) return;
mFrameStarted = true;
- mDirtyClip = true;
+ mState.setDirtyClip(true);
discardFramebuffer(mTilingClip.left, mTilingClip.top, mTilingClip.right, mTilingClip.bottom);
- mRenderState.setViewport(getWidth(), getHeight());
+ mRenderState.setViewport(mState.getWidth(), mState.getHeight());
// Functors break the tiling extension in pretty spectacular ways
// This ensures we don't use tiling when a functor is going to be
@@ -213,11 +144,11 @@ status_t OpenGLRenderer::startFrame() {
debugOverdraw(true, true);
- return clear(mTilingClip.left, mTilingClip.top,
+ clear(mTilingClip.left, mTilingClip.top,
mTilingClip.right, mTilingClip.bottom, mOpaque);
}
-status_t OpenGLRenderer::prepareDirty(float left, float top,
+void OpenGLRenderer::prepareDirty(float left, float top,
float right, float bottom, bool opaque) {
setupFrameState(left, top, right, bottom, opaque);
@@ -227,21 +158,19 @@ status_t OpenGLRenderer::prepareDirty(float left, float top,
// for each layer and wait until the first drawing command
// to start the frame
if (currentSnapshot()->fbo == 0) {
- syncState();
+ mRenderState.blend().syncEnabled();
updateLayers();
} else {
- return startFrame();
+ startFrame();
}
-
- return DrawGlInfo::kStatusDone;
}
void OpenGLRenderer::discardFramebuffer(float left, float top, float right, float bottom) {
// If we know that we are going to redraw the entire framebuffer,
// perform a discard to let the driver know we don't need to preserve
// the back buffer for this frame.
- if (mExtensions.hasDiscardFramebuffer() &&
- left <= 0.0f && top <= 0.0f && right >= getWidth() && bottom >= getHeight()) {
+ if (mCaches.extensions().hasDiscardFramebuffer() &&
+ left <= 0.0f && top <= 0.0f && right >= mState.getWidth() && bottom >= mState.getHeight()) {
const bool isFbo = getTargetFbo() == 0;
const GLenum attachments[] = {
isFbo ? (const GLenum) GL_COLOR_EXT : (const GLenum) GL_COLOR_ATTACHMENT0,
@@ -250,24 +179,16 @@ void OpenGLRenderer::discardFramebuffer(float left, float top, float right, floa
}
}
-status_t OpenGLRenderer::clear(float left, float top, float right, float bottom, bool opaque) {
+void OpenGLRenderer::clear(float left, float top, float right, float bottom, bool opaque) {
if (!opaque) {
- mCaches.enableScissor();
- mCaches.setScissor(left, getViewportHeight() - bottom, right - left, bottom - top);
+ mRenderState.scissor().setEnabled(true);
+ mRenderState.scissor().set(left, getViewportHeight() - bottom, right - left, bottom - top);
glClear(GL_COLOR_BUFFER_BIT);
- return DrawGlInfo::kStatusDrew;
+ mDirty = true;
+ return;
}
- mCaches.resetScissor();
- return DrawGlInfo::kStatusDone;
-}
-
-void OpenGLRenderer::syncState() {
- if (mCaches.blend) {
- glEnable(GL_BLEND);
- } else {
- glDisable(GL_BLEND);
- }
+ mRenderState.scissor().reset();
}
void OpenGLRenderer::startTilingCurrentClip(bool opaque, bool expand) {
@@ -307,13 +228,9 @@ void OpenGLRenderer::endTiling() {
if (!mSuppressTiling) mCaches.endTiling();
}
-void OpenGLRenderer::finish() {
+bool OpenGLRenderer::finish() {
renderOverdraw();
endTiling();
-
- for (size_t i = 0; i < mTempPaths.size(); i++) {
- delete mTempPaths[i];
- }
mTempPaths.clear();
// When finish() is invoked on FBO 0 we've reached the end
@@ -338,6 +255,8 @@ void OpenGLRenderer::finish() {
}
mFrameStarted = false;
+
+ return reportAndClearDirty();
}
void OpenGLRenderer::resumeAfterLayer() {
@@ -345,14 +264,14 @@ void OpenGLRenderer::resumeAfterLayer() {
mRenderState.bindFramebuffer(currentSnapshot()->fbo);
debugOverdraw(true, false);
- mCaches.resetScissor();
+ mRenderState.scissor().reset();
dirtyClip();
}
-status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) {
- if (currentSnapshot()->isIgnored()) return DrawGlInfo::kStatusDone;
+void OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) {
+ if (mState.currentlyIgnored()) return;
- Rect clip(*currentClipRect());
+ Rect clip(mState.currentClipRect());
clip.snapToPixelBoundaries();
// Since we don't know what the functor will draw, let's dirty
@@ -371,12 +290,12 @@ status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) {
info.height = getViewportHeight();
currentTransform()->copyTo(&info.transform[0]);
- bool prevDirtyClip = mDirtyClip;
+ bool prevDirtyClip = mState.getDirtyClip();
// setup GL state for functor
- if (mDirtyClip) {
+ if (mState.getDirtyClip()) {
setStencilFromClip(); // can issue draws, so must precede enableScissor()/interrupt()
}
- if (mCaches.enableScissor() || prevDirtyClip) {
+ if (mRenderState.scissor().setEnabled(true) || prevDirtyClip) {
setScissorFromClip();
}
@@ -384,7 +303,7 @@ status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) {
// Scissor may have been modified, reset dirty clip
dirtyClip();
- return DrawGlInfo::kStatusDrew;
+ mDirty = true;
}
///////////////////////////////////////////////////////////////////////////////
@@ -426,27 +345,29 @@ void OpenGLRenderer::renderOverdraw() {
if (mCaches.debugOverdraw && getTargetFbo() == 0) {
const Rect* clip = &mTilingClip;
- mCaches.enableScissor();
- mCaches.setScissor(clip->left, firstSnapshot()->getViewportHeight() - clip->bottom,
- clip->right - clip->left, clip->bottom - clip->top);
+ mRenderState.scissor().setEnabled(true);
+ mRenderState.scissor().set(clip->left,
+ mState.firstSnapshot()->getViewportHeight() - clip->bottom,
+ clip->right - clip->left,
+ clip->bottom - clip->top);
// 1x overdraw
- mCaches.stencil.enableDebugTest(2);
+ mRenderState.stencil().enableDebugTest(2);
drawColor(mCaches.getOverdrawColor(1), SkXfermode::kSrcOver_Mode);
// 2x overdraw
- mCaches.stencil.enableDebugTest(3);
+ mRenderState.stencil().enableDebugTest(3);
drawColor(mCaches.getOverdrawColor(2), SkXfermode::kSrcOver_Mode);
// 3x overdraw
- mCaches.stencil.enableDebugTest(4);
+ mRenderState.stencil().enableDebugTest(4);
drawColor(mCaches.getOverdrawColor(3), SkXfermode::kSrcOver_Mode);
// 4x overdraw and higher
- mCaches.stencil.enableDebugTest(4, true);
+ mRenderState.stencil().enableDebugTest(4, true);
drawColor(mCaches.getOverdrawColor(4), SkXfermode::kSrcOver_Mode);
- mCaches.stencil.disable();
+ mRenderState.stencil().disable();
}
}
@@ -457,7 +378,6 @@ void OpenGLRenderer::renderOverdraw() {
bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) {
if (layer->deferredUpdateScheduled && layer->renderer
&& layer->renderNode.get() && layer->renderNode->isRenderable()) {
- Rect& dirty = layer->dirtyRect;
if (inFrame) {
endTiling();
@@ -555,11 +475,11 @@ void OpenGLRenderer::cancelLayerUpdate(Layer* layer) {
void OpenGLRenderer::flushLayerUpdates() {
ATRACE_NAME("Update HW Layers");
- syncState();
+ mRenderState.blend().syncEnabled();
updateLayers();
flushLayers();
// Wait for all the layer updates to be executed
- AutoFence fence;
+ glFinish();
}
void OpenGLRenderer::markLayersAsBuildLayers() {
@@ -603,9 +523,9 @@ int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom,
// force matrix/clip isolation for layer
flags |= SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag;
- const int count = saveSnapshot(flags);
+ const int count = mState.saveSnapshot(flags);
- if (!currentSnapshot()->isIgnored()) {
+ if (!mState.currentlyIgnored()) {
createLayer(left, top, right, bottom, paint, flags, convexMask);
}
@@ -618,7 +538,7 @@ void OpenGLRenderer::calculateLayerBoundsAndClip(Rect& bounds, Rect& clip, bool
currentTransform()->mapRect(bounds);
// Layers only make sense if they are in the framebuffer's bounds
- if (bounds.intersect(*currentClipRect())) {
+ if (bounds.intersect(mState.currentClipRect())) {
// We cannot work with sub-pixels in this case
bounds.snapToPixelBoundaries();
@@ -652,17 +572,17 @@ void OpenGLRenderer::updateSnapshotIgnoreForLayer(const Rect& bounds, const Rect
if (bounds.isEmpty() || bounds.getWidth() > mCaches.maxTextureSize ||
bounds.getHeight() > mCaches.maxTextureSize ||
(fboLayer && clip.isEmpty())) {
- mSnapshot->empty = fboLayer;
+ writableSnapshot()->empty = fboLayer;
} else {
- mSnapshot->invisible = mSnapshot->invisible || (alpha <= 0 && fboLayer);
+ writableSnapshot()->invisible = writableSnapshot()->invisible || (alpha <= 0 && fboLayer);
}
}
int OpenGLRenderer::saveLayerDeferred(float left, float top, float right, float bottom,
const SkPaint* paint, int flags) {
- const int count = saveSnapshot(flags);
+ const int count = mState.saveSnapshot(flags);
- if (!currentSnapshot()->isIgnored() && (flags & SkCanvas::kClipToLayer_SaveFlag)) {
+ if (!mState.currentlyIgnored() && (flags & SkCanvas::kClipToLayer_SaveFlag)) {
// initialize the snapshot as though it almost represents an FBO layer so deferred draw
// operations will be able to store and restore the current clip and transform info, and
// quick rejection will be correct (for display lists)
@@ -672,11 +592,11 @@ int OpenGLRenderer::saveLayerDeferred(float left, float top, float right, float
calculateLayerBoundsAndClip(bounds, clip, true);
updateSnapshotIgnoreForLayer(bounds, clip, true, getAlphaDirect(paint));
- if (!currentSnapshot()->isIgnored()) {
- mSnapshot->resetTransform(-bounds.left, -bounds.top, 0.0f);
- mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom);
- mSnapshot->initializeViewport(bounds.getWidth(), bounds.getHeight());
- mSnapshot->roundRectClipState = NULL;
+ if (!mState.currentlyIgnored()) {
+ writableSnapshot()->resetTransform(-bounds.left, -bounds.top, 0.0f);
+ writableSnapshot()->resetClip(clip.left, clip.top, clip.right, clip.bottom);
+ writableSnapshot()->initializeViewport(bounds.getWidth(), bounds.getHeight());
+ writableSnapshot()->roundRectClipState = nullptr;
}
}
@@ -748,11 +668,11 @@ bool OpenGLRenderer::createLayer(float left, float top, float right, float botto
updateSnapshotIgnoreForLayer(bounds, clip, fboLayer, getAlphaDirect(paint));
// Bail out if we won't draw in this snapshot
- if (currentSnapshot()->isIgnored()) {
+ if (mState.currentlyIgnored()) {
return false;
}
- mCaches.activeTexture(0);
+ mCaches.textureState().activateTexture(0);
Layer* layer = mCaches.layerCache.get(mRenderState, bounds.getWidth(), bounds.getHeight());
if (!layer) {
return false;
@@ -768,8 +688,8 @@ bool OpenGLRenderer::createLayer(float left, float top, float right, float botto
layer->setConvexMask(convexMask); // note: the mask must be cleared before returning to the cache
// Save the layer in the snapshot
- mSnapshot->flags |= Snapshot::kFlagIsLayer;
- mSnapshot->layer = layer;
+ writableSnapshot()->flags |= Snapshot::kFlagIsLayer;
+ writableSnapshot()->layer = layer;
ATRACE_FORMAT_BEGIN("%ssaveLayer %ux%u",
fboLayer ? "" : "unclipped ",
@@ -787,7 +707,7 @@ bool OpenGLRenderer::createLayer(float left, float top, float right, float botto
// Unfortunately some drivers will turn the entire target texture black
// when reading outside of the window.
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, layer->getWidth(), layer->getHeight(),
- 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+ 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
layer->setEmpty(false);
}
@@ -796,7 +716,7 @@ bool OpenGLRenderer::createLayer(float left, float top, float right, float botto
bounds.getWidth(), bounds.getHeight());
// Enqueue the buffer coordinates to clear the corresponding region later
- mLayers.push(new Rect(bounds));
+ mLayers.push_back(Rect(bounds));
}
}
@@ -807,13 +727,13 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) {
layer->clipRect.set(clip);
layer->setFbo(mCaches.fboCache.get());
- mSnapshot->region = &mSnapshot->layer->region;
- mSnapshot->flags |= Snapshot::kFlagFboTarget | Snapshot::kFlagIsFboLayer;
- mSnapshot->fbo = layer->getFbo();
- mSnapshot->resetTransform(-bounds.left, -bounds.top, 0.0f);
- mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom);
- mSnapshot->initializeViewport(bounds.getWidth(), bounds.getHeight());
- mSnapshot->roundRectClipState = NULL;
+ writableSnapshot()->region = &writableSnapshot()->layer->region;
+ writableSnapshot()->flags |= Snapshot::kFlagFboTarget | Snapshot::kFlagIsFboLayer;
+ writableSnapshot()->fbo = layer->getFbo();
+ writableSnapshot()->resetTransform(-bounds.left, -bounds.top, 0.0f);
+ writableSnapshot()->resetClip(clip.left, clip.top, clip.right, clip.bottom);
+ writableSnapshot()->initializeViewport(bounds.getWidth(), bounds.getHeight());
+ writableSnapshot()->roundRectClipState = nullptr;
endTiling();
debugOverdraw(false, false);
@@ -828,14 +748,14 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) {
}
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
- layer->getTexture(), 0);
+ layer->getTextureId(), 0);
// Expand the startTiling region by 1
startTilingCurrentClip(true, true);
// Clear the FBO, expand the clear region by 1 to get nice bilinear filtering
- mCaches.enableScissor();
- mCaches.setScissor(clip.left - 1.0f, bounds.getHeight() - clip.bottom - 1.0f,
+ mRenderState.scissor().setEnabled(true);
+ mRenderState.scissor().set(clip.left - 1.0f, bounds.getHeight() - clip.bottom - 1.0f,
clip.getWidth() + 2.0f, clip.getHeight() + 2.0f);
glClear(GL_COLOR_BUFFER_BIT);
@@ -860,9 +780,9 @@ void OpenGLRenderer::composeLayer(const Snapshot& removed, const Snapshot& resto
const bool fboLayer = removed.flags & Snapshot::kFlagIsFboLayer;
bool clipRequired = false;
- calculateQuickRejectForScissor(rect.left, rect.top, rect.right, rect.bottom,
- &clipRequired, NULL, false); // safely ignore return, should never be rejected
- mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired);
+ mState.calculateQuickRejectForScissor(rect.left, rect.top, rect.right, rect.bottom,
+ &clipRequired, nullptr, false); // safely ignore return, should never be rejected
+ mRenderState.scissor().setEnabled(mScissorOptimizationDisabled || clipRequired);
if (fboLayer) {
endTiling();
@@ -890,9 +810,9 @@ void OpenGLRenderer::composeLayer(const Snapshot& removed, const Snapshot& resto
layer->setAlpha(255);
}
- mCaches.unbindMeshBuffer();
+ mRenderState.meshState().unbindMeshBuffer();
- mCaches.activeTexture(0);
+ mCaches.textureState().activateTexture(0);
// When the layer is stored in an FBO, we can save a bit of fillrate by
// drawing only the dirty region
@@ -905,7 +825,7 @@ void OpenGLRenderer::composeLayer(const Snapshot& removed, const Snapshot& resto
save(0);
// the layer contains screen buffer content that shouldn't be alpha modulated
// (and any necessary alpha modulation was handled drawing into the layer)
- mSnapshot->alpha = 1.0f;
+ writableSnapshot()->alpha = 1.0f;
composeLayerRect(layer, rect, true);
restore();
}
@@ -913,97 +833,50 @@ void OpenGLRenderer::composeLayer(const Snapshot& removed, const Snapshot& resto
dirtyClip();
// Failing to add the layer to the cache should happen only if the layer is too large
- layer->setConvexMask(NULL);
+ layer->setConvexMask(nullptr);
if (!mCaches.layerCache.put(layer)) {
LAYER_LOGD("Deleting layer");
- layer->decStrong(0);
+ layer->decStrong(nullptr);
}
}
void OpenGLRenderer::drawTextureLayer(Layer* layer, const Rect& rect) {
- float alpha = getLayerAlpha(layer);
-
- setupDraw();
- if (layer->getRenderTarget() == GL_TEXTURE_2D) {
- setupDrawWithTexture();
- } else {
- setupDrawWithExternalTexture();
- }
- setupDrawTextureTransform();
- setupDrawColor(alpha, alpha, alpha, alpha);
- setupDrawColorFilter(layer->getColorFilter());
- setupDrawBlending(layer);
- setupDrawProgram();
- setupDrawPureColorUniforms();
- setupDrawColorFilterUniforms(layer->getColorFilter());
- if (layer->getRenderTarget() == GL_TEXTURE_2D) {
- setupDrawTexture(layer->getTexture());
- } else {
- setupDrawExternalTexture(layer->getTexture());
- }
- if (currentTransform()->isPureTranslate() &&
- !layer->getForceFilter() &&
- layer->getWidth() == (uint32_t) rect.getWidth() &&
- layer->getHeight() == (uint32_t) rect.getHeight()) {
- const float x = (int) floorf(rect.left + currentTransform()->getTranslateX() + 0.5f);
- const float y = (int) floorf(rect.top + currentTransform()->getTranslateY() + 0.5f);
-
- layer->setFilter(GL_NEAREST);
- setupDrawModelView(kModelViewMode_TranslateAndScale, false,
- x, y, x + rect.getWidth(), y + rect.getHeight(), true);
- } else {
- layer->setFilter(GL_LINEAR);
- setupDrawModelView(kModelViewMode_TranslateAndScale, false,
- rect.left, rect.top, rect.right, rect.bottom);
- }
- setupDrawTextureTransformUniforms(layer->getTexTransform());
- setupDrawMesh(&mMeshVertices[0].x, &mMeshVertices[0].u);
-
- glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
+ bool snap = !layer->getForceFilter()
+ && layer->getWidth() == (uint32_t) rect.getWidth()
+ && layer->getHeight() == (uint32_t) rect.getHeight();
+ Glop glop;
+ GlopBuilder(mRenderState, mCaches, &glop)
+ .setMeshTexturedUvQuad(nullptr, Rect(0, 1, 1, 0)) // TODO: simplify with VBO
+ .setFillTextureLayer(*layer, getLayerAlpha(layer))
+ .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false)
+ .setModelViewMapUnitToRectOptionalSnap(snap, rect)
+ .setRoundRectClipState(currentSnapshot()->roundRectClipState)
+ .build();
+ renderGlop(glop);
}
void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) {
if (layer->isTextureLayer()) {
EVENT_LOGD("composeTextureLayerRect");
- resetDrawTextureTexCoords(0.0f, 1.0f, 1.0f, 0.0f);
drawTextureLayer(layer, rect);
- resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
} else {
EVENT_LOGD("composeHardwareLayerRect");
- const Rect& texCoords = layer->texCoords;
- resetDrawTextureTexCoords(texCoords.left, texCoords.top,
- texCoords.right, texCoords.bottom);
-
- float x = rect.left;
- float y = rect.top;
- bool simpleTransform = currentTransform()->isPureTranslate() &&
- layer->getWidth() == (uint32_t) rect.getWidth() &&
- layer->getHeight() == (uint32_t) rect.getHeight();
-
- if (simpleTransform) {
- // When we're swapping, the layer is already in screen coordinates
- if (!swap) {
- x = (int) floorf(rect.left + currentTransform()->getTranslateX() + 0.5f);
- y = (int) floorf(rect.top + currentTransform()->getTranslateY() + 0.5f);
- }
-
- layer->setFilter(GL_NEAREST, true);
- } else {
- layer->setFilter(GL_LINEAR, true);
- }
- SkPaint layerPaint;
- layerPaint.setAlpha(getLayerAlpha(layer) * 255);
- layerPaint.setXfermodeMode(layer->getMode());
- layerPaint.setColorFilter(layer->getColorFilter());
-
- bool blend = layer->isBlend() || getLayerAlpha(layer) < 1.0f;
- drawTextureMesh(x, y, x + rect.getWidth(), y + rect.getHeight(),
- layer->getTexture(), &layerPaint, blend,
- &mMeshVertices[0].x, &mMeshVertices[0].u,
- GL_TRIANGLE_STRIP, gMeshCount, swap, swap || simpleTransform);
-
- resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
+ Blend::ModeOrderSwap modeUsage = swap ?
+ Blend::ModeOrderSwap::Swap : Blend::ModeOrderSwap::NoSwap;
+ const Matrix4& transform = swap ? Matrix4::identity() : *currentTransform();
+ bool snap = !swap
+ && layer->getWidth() == static_cast<uint32_t>(rect.getWidth())
+ && layer->getHeight() == static_cast<uint32_t>(rect.getHeight());
+ Glop glop;
+ GlopBuilder(mRenderState, mCaches, &glop)
+ .setMeshTexturedUvQuad(nullptr, layer->texCoords)
+ .setFillLayer(layer->getTexture(), layer->getColorFilter(), getLayerAlpha(layer), layer->getMode(), modeUsage)
+ .setTransform(currentSnapshot()->getOrthoMatrix(), transform, false)
+ .setModelViewMapUnitToRectOptionalSnap(snap, rect)
+ .setRoundRectClipState(currentSnapshot()->roundRectClipState)
+ .build();
+ renderGlop(glop);
}
}
@@ -1033,14 +906,14 @@ public:
, mLayer(layer) {
}
- virtual bool asACustomShader(void** data) const {
+ virtual bool asACustomShader(void** data) const override {
if (data) {
*data = static_cast<void*>(mLayer);
}
return true;
}
- virtual bool isOpaque() const {
+ virtual bool isOpaque() const override {
return !mLayer->isBlend();
}
@@ -1049,13 +922,13 @@ protected:
LOG_ALWAYS_FATAL("LayerShader should never be drawn with raster backend.");
}
- virtual void flatten(SkWriteBuffer&) const {
+ virtual void flatten(SkWriteBuffer&) const override {
LOG_ALWAYS_FATAL("LayerShader should never be flattened.");
}
- virtual Factory getFactory() const {
+ virtual Factory getFactory() const override {
LOG_ALWAYS_FATAL("LayerShader should never be created from a stream.");
- return NULL;
+ return nullptr;
}
private:
// Unowned.
@@ -1088,7 +961,7 @@ void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) {
const SkPath* maskPath = layer->getConvexMask();
DRAW_DOUBLE_STENCIL(drawConvexPath(*maskPath, &paint));
- paint.setShader(NULL);
+ paint.setShader(nullptr);
restore();
return;
@@ -1115,42 +988,12 @@ void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) {
rects = safeRegion.getArray(&count);
}
- const float alpha = getLayerAlpha(layer);
const float texX = 1.0f / float(layer->getWidth());
const float texY = 1.0f / float(layer->getHeight());
const float height = rect.getHeight();
- setupDraw();
-
- // We must get (and therefore bind) the region mesh buffer
- // after we setup drawing in case we need to mess with the
- // stencil buffer in setupDraw()
- TextureVertex* mesh = mCaches.getRegionMesh();
- uint32_t numQuads = 0;
-
- setupDrawWithTexture();
- setupDrawColor(alpha, alpha, alpha, alpha);
- setupDrawColorFilter(layer->getColorFilter());
- setupDrawBlending(layer);
- setupDrawProgram();
- setupDrawDirtyRegionsDisabled();
- setupDrawPureColorUniforms();
- setupDrawColorFilterUniforms(layer->getColorFilter());
- setupDrawTexture(layer->getTexture());
- if (currentTransform()->isPureTranslate()) {
- const float x = (int) floorf(rect.left + currentTransform()->getTranslateX() + 0.5f);
- const float y = (int) floorf(rect.top + currentTransform()->getTranslateY() + 0.5f);
-
- layer->setFilter(GL_NEAREST);
- setupDrawModelView(kModelViewMode_Translate, false,
- x, y, x + rect.getWidth(), y + rect.getHeight(), true);
- } else {
- layer->setFilter(GL_LINEAR);
- setupDrawModelView(kModelViewMode_Translate, false,
- rect.left, rect.top, rect.right, rect.bottom);
- }
- setupDrawMeshIndices(&mesh[0].x, &mesh[0].u);
-
+ TextureVertex quadVertices[count * 4];
+ TextureVertex* mesh = &quadVertices[0];
for (size_t i = 0; i < count; i++) {
const android::Rect* r = &rects[i];
@@ -1164,21 +1007,17 @@ void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) {
TextureVertex::set(mesh++, r->right, r->top, u2, v1);
TextureVertex::set(mesh++, r->left, r->bottom, u1, v2);
TextureVertex::set(mesh++, r->right, r->bottom, u2, v2);
-
- numQuads++;
-
- if (numQuads >= gMaxNumberOfQuads) {
- DRAW_DOUBLE_STENCIL(glDrawElements(GL_TRIANGLES, numQuads * 6,
- GL_UNSIGNED_SHORT, NULL));
- numQuads = 0;
- mesh = mCaches.getRegionMesh();
- }
- }
-
- if (numQuads > 0) {
- DRAW_DOUBLE_STENCIL(glDrawElements(GL_TRIANGLES, numQuads * 6,
- GL_UNSIGNED_SHORT, NULL));
}
+ Rect modelRect = Rect(rect.getWidth(), rect.getHeight());
+ Glop glop;
+ GlopBuilder(mRenderState, mCaches, &glop)
+ .setMeshTexturedIndexedQuads(&quadVertices[0], count * 6)
+ .setFillLayer(layer->getTexture(), layer->getColorFilter(), getLayerAlpha(layer), layer->getMode(), Blend::ModeOrderSwap::NoSwap)
+ .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false)
+ .setModelViewOffsetRectSnap(rect.left, rect.top, modelRect)
+ .setRoundRectClipState(currentSnapshot()->roundRectClipState)
+ .build();
+ DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate, renderGlop(glop));
#if DEBUG_LAYERS_AS_REGIONS
drawRegionRectsDebug(layer->region);
@@ -1231,7 +1070,7 @@ void OpenGLRenderer::drawRegionRects(const SkRegion& region, const SkPaint& pain
}
void OpenGLRenderer::dirtyLayer(const float left, const float top,
- const float right, const float bottom, const mat4 transform) {
+ const float right, const float bottom, const Matrix4& transform) {
if (hasLayer()) {
Rect bounds(left, top, right, bottom);
transform.mapRect(bounds);
@@ -1248,7 +1087,7 @@ void OpenGLRenderer::dirtyLayer(const float left, const float top,
}
void OpenGLRenderer::dirtyLayerUnchecked(Rect& bounds, Region* region) {
- if (bounds.intersect(*currentClipRect())) {
+ if (bounds.intersect(mState.currentClipRect())) {
bounds.snapToPixelBoundaries();
android::Rect dirty(bounds.left, bounds.top, bounds.right, bounds.bottom);
if (!dirty.isEmpty()) {
@@ -1257,26 +1096,11 @@ void OpenGLRenderer::dirtyLayerUnchecked(Rect& bounds, Region* region) {
}
}
-void OpenGLRenderer::issueIndexedQuadDraw(Vertex* mesh, GLsizei quadsCount) {
- GLsizei elementsCount = quadsCount * 6;
- while (elementsCount > 0) {
- GLsizei drawCount = min(elementsCount, (GLsizei) gMaxNumberOfQuads * 6);
-
- setupDrawIndexedVertices(&mesh[0].x);
- glDrawElements(GL_TRIANGLES, drawCount, GL_UNSIGNED_SHORT, NULL);
-
- elementsCount -= drawCount;
- // Though there are 4 vertices in a quad, we use 6 indices per
- // quad to draw with GL_TRIANGLES
- mesh += (drawCount / 6) * 4;
- }
-}
-
void OpenGLRenderer::clearLayerRegions() {
- const size_t count = mLayers.size();
- if (count == 0) return;
+ const size_t quadCount = mLayers.size();
+ if (quadCount == 0) return;
- if (!currentSnapshot()->isIgnored()) {
+ if (!mState.currentlyIgnored()) {
EVENT_LOGD("clearLayerRegions");
// Doing several glScissor/glClear here can negatively impact
// GPUs with a tiler architecture, instead we draw quads with
@@ -1285,44 +1109,36 @@ void OpenGLRenderer::clearLayerRegions() {
// The list contains bounds that have already been clipped
// against their initial clip rect, and the current clip
// is likely different so we need to disable clipping here
- bool scissorChanged = mCaches.disableScissor();
+ bool scissorChanged = mRenderState.scissor().setEnabled(false);
- Vertex mesh[count * 4];
+ Vertex mesh[quadCount * 4];
Vertex* vertex = mesh;
- for (uint32_t i = 0; i < count; i++) {
- Rect* bounds = mLayers.itemAt(i);
-
- Vertex::set(vertex++, bounds->left, bounds->top);
- Vertex::set(vertex++, bounds->right, bounds->top);
- Vertex::set(vertex++, bounds->left, bounds->bottom);
- Vertex::set(vertex++, bounds->right, bounds->bottom);
+ for (uint32_t i = 0; i < quadCount; i++) {
+ const Rect& bounds = mLayers[i];
- delete bounds;
+ Vertex::set(vertex++, bounds.left, bounds.top);
+ Vertex::set(vertex++, bounds.right, bounds.top);
+ Vertex::set(vertex++, bounds.left, bounds.bottom);
+ Vertex::set(vertex++, bounds.right, bounds.bottom);
}
// We must clear the list of dirty rects before we
- // call setupDraw() to prevent stencil setup to do
- // the same thing again
+ // call clearLayerRegions() in renderGlop to prevent
+ // stencil setup from doing the same thing again
mLayers.clear();
- SkPaint clearPaint;
- clearPaint.setXfermodeMode(SkXfermode::kClear_Mode);
-
- setupDraw(false);
- setupDrawColor(0.0f, 0.0f, 0.0f, 1.0f);
- setupDrawBlending(&clearPaint, true);
- setupDrawProgram();
- setupDrawPureColorUniforms();
- setupDrawModelView(kModelViewMode_Translate, false,
- 0.0f, 0.0f, 0.0f, 0.0f, true);
-
- issueIndexedQuadDraw(&mesh[0], count);
-
- if (scissorChanged) mCaches.enableScissor();
+ Glop glop;
+ GlopBuilder(mRenderState, mCaches, &glop)
+ .setMeshIndexedQuads(&mesh[0], quadCount)
+ .setFillClear()
+ .setTransform(currentSnapshot()->getOrthoMatrix(), Matrix4::identity(), false)
+ .setModelViewOffsetRect(0, 0, Rect(currentSnapshot()->getClipRect()))
+ .setRoundRectClipState(currentSnapshot()->roundRectClipState)
+ .build();
+ renderGlop(glop, false);
+
+ if (scissorChanged) mRenderState.scissor().setEnabled(true);
} else {
- for (uint32_t i = 0; i < count; i++) {
- delete mLayers.itemAt(i);
- }
mLayers.clear();
}
}
@@ -1332,7 +1148,7 @@ void OpenGLRenderer::clearLayerRegions() {
///////////////////////////////////////////////////////////////////////////////
bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDeferFlags) {
- const Rect* currentClip = currentClipRect();
+ const Rect& currentClip = mState.currentClipRect();
const mat4* currentMatrix = currentTransform();
if (stateDeferFlags & kStateDeferFlag_Draw) {
@@ -1344,32 +1160,32 @@ bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDef
// is used, it should more closely duplicate the quickReject logic (in how it uses
// snapToPixelBoundaries)
- if(!clippedBounds.intersect(*currentClip)) {
+ if (!clippedBounds.intersect(currentClip)) {
// quick rejected
return true;
}
state.mClipSideFlags = kClipSide_None;
- if (!currentClip->contains(state.mBounds)) {
+ if (!currentClip.contains(state.mBounds)) {
int& flags = state.mClipSideFlags;
// op partially clipped, so record which sides are clipped for clip-aware merging
- if (currentClip->left > state.mBounds.left) flags |= kClipSide_Left;
- if (currentClip->top > state.mBounds.top) flags |= kClipSide_Top;
- if (currentClip->right < state.mBounds.right) flags |= kClipSide_Right;
- if (currentClip->bottom < state.mBounds.bottom) flags |= kClipSide_Bottom;
+ if (currentClip.left > state.mBounds.left) flags |= kClipSide_Left;
+ if (currentClip.top > state.mBounds.top) flags |= kClipSide_Top;
+ if (currentClip.right < state.mBounds.right) flags |= kClipSide_Right;
+ if (currentClip.bottom < state.mBounds.bottom) flags |= kClipSide_Bottom;
}
state.mBounds.set(clippedBounds);
} else {
// Empty bounds implies size unknown. Label op as conservatively clipped to disable
// overdraw avoidance (since we don't know what it overlaps)
state.mClipSideFlags = kClipSide_ConservativeFull;
- state.mBounds.set(*currentClip);
+ state.mBounds.set(currentClip);
}
}
state.mClipValid = (stateDeferFlags & kStateDeferFlag_Clip);
if (state.mClipValid) {
- state.mClip.set(*currentClip);
+ state.mClip.set(currentClip);
}
// Transform, drawModifiers, and alpha always deferred, since they are used by state operations
@@ -1385,12 +1201,12 @@ bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDef
void OpenGLRenderer::restoreDisplayState(const DeferredDisplayState& state, bool skipClipRestore) {
setMatrix(state.mMatrix);
- mSnapshot->alpha = state.mAlpha;
+ writableSnapshot()->alpha = state.mAlpha;
mDrawModifiers = state.mDrawModifiers;
- mSnapshot->roundRectClipState = state.mRoundRectClipState;
+ writableSnapshot()->roundRectClipState = state.mRoundRectClipState;
if (state.mClipValid && !skipClipRestore) {
- mSnapshot->setClip(state.mClip.left, state.mClip.top,
+ writableSnapshot()->setClip(state.mClip.left, state.mClip.top,
state.mClip.right, state.mClip.bottom);
dirtyClip();
}
@@ -1404,13 +1220,14 @@ void OpenGLRenderer::restoreDisplayState(const DeferredDisplayState& state, bool
* This method should be called when restoreDisplayState() won't be restoring the clip
*/
void OpenGLRenderer::setupMergedMultiDraw(const Rect* clipRect) {
- if (clipRect != NULL) {
- mSnapshot->setClip(clipRect->left, clipRect->top, clipRect->right, clipRect->bottom);
+ if (clipRect != nullptr) {
+ writableSnapshot()->setClip(clipRect->left, clipRect->top, clipRect->right, clipRect->bottom);
} else {
- mSnapshot->setClip(0, 0, getWidth(), getHeight());
+ writableSnapshot()->setClip(0, 0, mState.getWidth(), mState.getHeight());
}
dirtyClip();
- mCaches.setScissorEnabled(clipRect != NULL || mScissorOptimizationDisabled);
+ bool enableScissor = (clipRect != nullptr) || mScissorOptimizationDisabled;
+ mRenderState.scissor().setEnabled(enableScissor);
}
///////////////////////////////////////////////////////////////////////////////
@@ -1418,12 +1235,12 @@ void OpenGLRenderer::setupMergedMultiDraw(const Rect* clipRect) {
///////////////////////////////////////////////////////////////////////////////
void OpenGLRenderer::setScissorFromClip() {
- Rect clip(*currentClipRect());
+ Rect clip(mState.currentClipRect());
clip.snapToPixelBoundaries();
- if (mCaches.setScissor(clip.left, getViewportHeight() - clip.bottom,
+ if (mRenderState.scissor().set(clip.left, getViewportHeight() - clip.bottom,
clip.getWidth(), clip.getHeight())) {
- mDirtyClip = false;
+ mState.setDirtyClip(false);
}
}
@@ -1445,34 +1262,104 @@ void OpenGLRenderer::attachStencilBufferToLayer(Layer* layer) {
endTiling();
RenderBuffer* buffer = mCaches.renderBufferCache.get(
- Stencil::getSmallestStencilFormat(), layer->getWidth(), layer->getHeight());
+ Stencil::getSmallestStencilFormat(),
+ layer->getWidth(), layer->getHeight());
layer->setStencilRenderBuffer(buffer);
startTiling(layer->clipRect, layer->layer.getHeight());
}
}
+static void handlePoint(std::vector<Vertex>& rectangleVertices, const Matrix4& transform,
+ float x, float y) {
+ Vertex v;
+ v.x = x;
+ v.y = y;
+ transform.mapPoint(v.x, v.y);
+ rectangleVertices.push_back(v);
+}
+
+static void handlePointNoTransform(std::vector<Vertex>& rectangleVertices, float x, float y) {
+ Vertex v;
+ v.x = x;
+ v.y = y;
+ rectangleVertices.push_back(v);
+}
+
+void OpenGLRenderer::drawRectangleList(const RectangleList& rectangleList) {
+ int quadCount = rectangleList.getTransformedRectanglesCount();
+ std::vector<Vertex> rectangleVertices(quadCount * 4);
+ Rect scissorBox = rectangleList.calculateBounds();
+ scissorBox.snapToPixelBoundaries();
+ for (int i = 0; i < quadCount; ++i) {
+ const TransformedRectangle& tr(rectangleList.getTransformedRectangle(i));
+ const Matrix4& transform = tr.getTransform();
+ Rect bounds = tr.getBounds();
+ if (transform.rectToRect()) {
+ transform.mapRect(bounds);
+ if (!bounds.intersect(scissorBox)) {
+ bounds.setEmpty();
+ } else {
+ handlePointNoTransform(rectangleVertices, bounds.left, bounds.top);
+ handlePointNoTransform(rectangleVertices, bounds.right, bounds.top);
+ handlePointNoTransform(rectangleVertices, bounds.left, bounds.bottom);
+ handlePointNoTransform(rectangleVertices, bounds.right, bounds.bottom);
+ }
+ } else {
+ handlePoint(rectangleVertices, transform, bounds.left, bounds.top);
+ handlePoint(rectangleVertices, transform, bounds.right, bounds.top);
+ handlePoint(rectangleVertices, transform, bounds.left, bounds.bottom);
+ handlePoint(rectangleVertices, transform, bounds.right, bounds.bottom);
+ }
+ }
+
+ mRenderState.scissor().set(scissorBox.left, getViewportHeight() - scissorBox.bottom,
+ scissorBox.getWidth(), scissorBox.getHeight());
+
+ Glop glop;
+ Vertex* vertices = &rectangleVertices[0];
+ GlopBuilder(mRenderState, mCaches, &glop)
+ .setMeshIndexedQuads(vertices, rectangleVertices.size() / 4)
+ .setFillBlack()
+ .setTransform(currentSnapshot()->getOrthoMatrix(), Matrix4::identity(), false)
+ .setModelViewOffsetRect(0, 0, scissorBox)
+ .setRoundRectClipState(currentSnapshot()->roundRectClipState)
+ .build();
+ renderGlop(glop);
+}
+
void OpenGLRenderer::setStencilFromClip() {
if (!mCaches.debugOverdraw) {
- if (!currentSnapshot()->clipRegion->isEmpty()) {
+ if (!currentSnapshot()->clipIsSimple()) {
+ int incrementThreshold;
EVENT_LOGD("setStencilFromClip - enabling");
// NOTE: The order here is important, we must set dirtyClip to false
// before any draw call to avoid calling back into this method
- mDirtyClip = false;
+ mState.setDirtyClip(false);
ensureStencilBuffer();
- mCaches.stencil.enableWrite();
+ const ClipArea& clipArea = currentSnapshot()->getClipArea();
- // Clear and update the stencil, but first make sure we restrict drawing
+ bool isRectangleList = clipArea.isRectangleList();
+ if (isRectangleList) {
+ incrementThreshold = clipArea.getRectangleList().getTransformedRectanglesCount();
+ } else {
+ incrementThreshold = 0;
+ }
+
+ mRenderState.stencil().enableWrite(incrementThreshold);
+
+ // Clean and update the stencil, but first make sure we restrict drawing
// to the region's bounds
- bool resetScissor = mCaches.enableScissor();
+ bool resetScissor = mRenderState.scissor().setEnabled(true);
if (resetScissor) {
// The scissor was not set so we now need to update it
setScissorFromClip();
}
- mCaches.stencil.clear();
+
+ mRenderState.stencil().clear();
// stash and disable the outline clip state, since stencil doesn't account for outline
bool storedSkipOutlineClip = mSkipOutlineClip;
@@ -1482,28 +1369,34 @@ void OpenGLRenderer::setStencilFromClip() {
paint.setColor(SK_ColorBLACK);
paint.setXfermodeMode(SkXfermode::kSrc_Mode);
- // NOTE: We could use the region contour path to generate a smaller mesh
- // Since we are using the stencil we could use the red book path
- // drawing technique. It might increase bandwidth usage though.
+ if (isRectangleList) {
+ drawRectangleList(clipArea.getRectangleList());
+ } else {
+ // NOTE: We could use the region contour path to generate a smaller mesh
+ // Since we are using the stencil we could use the red book path
+ // drawing technique. It might increase bandwidth usage though.
- // The last parameter is important: we are not drawing in the color buffer
- // so we don't want to dirty the current layer, if any
- drawRegionRects(*(currentSnapshot()->clipRegion), paint, false);
- if (resetScissor) mCaches.disableScissor();
+ // The last parameter is important: we are not drawing in the color buffer
+ // so we don't want to dirty the current layer, if any
+ drawRegionRects(clipArea.getClipRegion(), paint, false);
+ }
+ if (resetScissor) mRenderState.scissor().setEnabled(false);
mSkipOutlineClip = storedSkipOutlineClip;
- mCaches.stencil.enableTest();
+ mRenderState.stencil().enableTest(incrementThreshold);
// Draw the region used to generate the stencil if the appropriate debug
// mode is enabled
- if (mCaches.debugStencilClip == Caches::kStencilShowRegion) {
+ // TODO: Implement for rectangle list clip areas
+ if (mCaches.debugStencilClip == Caches::kStencilShowRegion &&
+ !clipArea.isRectangleList()) {
paint.setColor(0x7f0000ff);
paint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
- drawRegionRects(*(currentSnapshot()->clipRegion), paint);
+ drawRegionRects(currentSnapshot()->getClipRegion(), paint);
}
} else {
EVENT_LOGD("setStencilFromClip - disabling");
- mCaches.stencil.disable();
+ mRenderState.stencil().disable();
}
}
}
@@ -1528,13 +1421,13 @@ bool OpenGLRenderer::quickRejectSetupScissor(float left, float top, float right,
bool clipRequired = false;
bool roundRectClipRequired = false;
- if (calculateQuickRejectForScissor(left, top, right, bottom,
+ if (mState.calculateQuickRejectForScissor(left, top, right, bottom,
&clipRequired, &roundRectClipRequired, snapOut)) {
return true;
}
// not quick rejected, so enable the scissor if clipRequired
- mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired);
+ mRenderState.scissor().setEnabled(mScissorOptimizationDisabled || clipRequired);
mSkipOutlineClip = !roundRectClipRequired;
return false;
}
@@ -1550,410 +1443,60 @@ void OpenGLRenderer::debugClip() {
#endif
}
-///////////////////////////////////////////////////////////////////////////////
-// Drawing commands
-///////////////////////////////////////////////////////////////////////////////
-
-void OpenGLRenderer::setupDraw(bool clearLayer) {
+void OpenGLRenderer::renderGlop(const Glop& glop, bool clearLayer) {
// TODO: It would be best if we could do this before quickRejectSetupScissor()
// changes the scissor test state
if (clearLayer) clearLayerRegions();
- // Make sure setScissor & setStencil happen at the beginning of
- // this method
- if (mDirtyClip) {
- if (mCaches.scissorEnabled) {
+
+ if (mState.getDirtyClip()) {
+ if (mRenderState.scissor().isEnabled()) {
setScissorFromClip();
}
setStencilFromClip();
}
-
- mDescription.reset();
-
- mSetShaderColor = false;
- mColorSet = false;
- mColorA = mColorR = mColorG = mColorB = 0.0f;
- mTextureUnit = 0;
- mTrackDirtyRegions = true;
-
- // Enable debug highlight when what we're about to draw is tested against
- // the stencil buffer and if stencil highlight debugging is on
- mDescription.hasDebugHighlight = !mCaches.debugOverdraw &&
- mCaches.debugStencilClip == Caches::kStencilShowHighlight &&
- mCaches.stencil.isTestEnabled();
-}
-
-void OpenGLRenderer::setupDrawWithTexture(bool isAlpha8) {
- mDescription.hasTexture = true;
- mDescription.hasAlpha8Texture = isAlpha8;
-}
-
-void OpenGLRenderer::setupDrawWithTextureAndColor(bool isAlpha8) {
- mDescription.hasTexture = true;
- mDescription.hasColors = true;
- mDescription.hasAlpha8Texture = isAlpha8;
-}
-
-void OpenGLRenderer::setupDrawWithExternalTexture() {
- mDescription.hasExternalTexture = true;
-}
-
-void OpenGLRenderer::setupDrawNoTexture() {
- mCaches.disableTexCoordsVertexArray();
-}
-
-void OpenGLRenderer::setupDrawVertexAlpha(bool useShadowAlphaInterp) {
- mDescription.hasVertexAlpha = true;
- mDescription.useShadowAlphaInterp = useShadowAlphaInterp;
-}
-
-void OpenGLRenderer::setupDrawColor(int color, int alpha) {
- mColorA = alpha / 255.0f;
- mColorR = mColorA * ((color >> 16) & 0xFF) / 255.0f;
- mColorG = mColorA * ((color >> 8) & 0xFF) / 255.0f;
- mColorB = mColorA * ((color ) & 0xFF) / 255.0f;
- mColorSet = true;
- mSetShaderColor = mDescription.setColorModulate(mColorA);
-}
-
-void OpenGLRenderer::setupDrawAlpha8Color(int color, int alpha) {
- mColorA = alpha / 255.0f;
- mColorR = mColorA * ((color >> 16) & 0xFF) / 255.0f;
- mColorG = mColorA * ((color >> 8) & 0xFF) / 255.0f;
- mColorB = mColorA * ((color ) & 0xFF) / 255.0f;
- mColorSet = true;
- mSetShaderColor = mDescription.setAlpha8ColorModulate(mColorR, mColorG, mColorB, mColorA);
-}
-
-void OpenGLRenderer::setupDrawTextGamma(const SkPaint* paint) {
- mCaches.fontRenderer->describe(mDescription, paint);
-}
-
-void OpenGLRenderer::setupDrawColor(float r, float g, float b, float a) {
- mColorA = a;
- mColorR = r;
- mColorG = g;
- mColorB = b;
- mColorSet = true;
- mSetShaderColor = mDescription.setColorModulate(a);
-}
-
-void OpenGLRenderer::setupDrawShader(const SkShader* shader) {
- if (shader != NULL) {
- SkiaShader::describe(&mCaches, mDescription, mExtensions, *shader);
- }
-}
-
-void OpenGLRenderer::setupDrawColorFilter(const SkColorFilter* filter) {
- if (filter == NULL) {
- return;
- }
-
- SkXfermode::Mode mode;
- if (filter->asColorMode(NULL, &mode)) {
- mDescription.colorOp = ProgramDescription::kColorBlend;
- mDescription.colorMode = mode;
- } else if (filter->asColorMatrix(NULL)) {
- mDescription.colorOp = ProgramDescription::kColorMatrix;
- }
-}
-
-void OpenGLRenderer::accountForClear(SkXfermode::Mode mode) {
- if (mColorSet && mode == SkXfermode::kClear_Mode) {
- mColorA = 1.0f;
- mColorR = mColorG = mColorB = 0.0f;
- mSetShaderColor = mDescription.modulate = true;
- }
-}
-
-void OpenGLRenderer::setupDrawBlending(const Layer* layer, bool swapSrcDst) {
- SkXfermode::Mode mode = layer->getMode();
- // When the blending mode is kClear_Mode, we need to use a modulate color
- // argb=1,0,0,0
- accountForClear(mode);
- // TODO: check shader blending, once we have shader drawing support for layers.
- bool blend = layer->isBlend() || getLayerAlpha(layer) < 1.0f ||
- (mColorSet && mColorA < 1.0f) || isBlendedColorFilter(layer->getColorFilter());
- chooseBlending(blend, mode, mDescription, swapSrcDst);
-}
-
-void OpenGLRenderer::setupDrawBlending(const SkPaint* paint, bool blend, bool swapSrcDst) {
- SkXfermode::Mode mode = getXfermodeDirect(paint);
- // When the blending mode is kClear_Mode, we need to use a modulate color
- // argb=1,0,0,0
- accountForClear(mode);
- blend |= (mColorSet && mColorA < 1.0f) ||
- (getShader(paint) && !getShader(paint)->isOpaque()) ||
- isBlendedColorFilter(getColorFilter(paint));
- chooseBlending(blend, mode, mDescription, swapSrcDst);
-}
-
-void OpenGLRenderer::setupDrawProgram() {
- useProgram(mCaches.programCache.get(mDescription));
- if (mDescription.hasRoundRectClip) {
- // TODO: avoid doing this repeatedly, stashing state pointer in program
- const RoundRectClipState* state = mSnapshot->roundRectClipState;
- const Rect& innerRect = state->innerRect;
- glUniform4f(mCaches.currentProgram->getUniform("roundRectInnerRectLTRB"),
- innerRect.left, innerRect.top,
- innerRect.right, innerRect.bottom);
- glUniformMatrix4fv(mCaches.currentProgram->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(mCaches.currentProgram->getUniform("roundRectRadius"),
- roundedOutRadius);
- }
-}
-
-void OpenGLRenderer::setupDrawDirtyRegionsDisabled() {
- mTrackDirtyRegions = false;
-}
-
-void OpenGLRenderer::setupDrawModelView(ModelViewMode mode, bool offset,
- float left, float top, float right, float bottom, bool ignoreTransform) {
- mModelViewMatrix.loadTranslate(left, top, 0.0f);
- if (mode == kModelViewMode_TranslateAndScale) {
- mModelViewMatrix.scale(right - left, bottom - top, 1.0f);
- }
-
- bool dirty = right - left > 0.0f && bottom - top > 0.0f;
- const Matrix4& transformMatrix = ignoreTransform ? Matrix4::identity() : *currentTransform();
- mCaches.currentProgram->set(mSnapshot->getOrthoMatrix(), mModelViewMatrix, transformMatrix, offset);
- if (dirty && mTrackDirtyRegions) {
- if (!ignoreTransform) {
- dirtyLayer(left, top, right, bottom, *currentTransform());
- } else {
- dirtyLayer(left, top, right, bottom);
- }
- }
-}
-
-void OpenGLRenderer::setupDrawColorUniforms(bool hasShader) {
- if ((mColorSet && !hasShader) || (hasShader && mSetShaderColor)) {
- mCaches.currentProgram->setColor(mColorR, mColorG, mColorB, mColorA);
- }
-}
-
-void OpenGLRenderer::setupDrawPureColorUniforms() {
- if (mSetShaderColor) {
- mCaches.currentProgram->setColor(mColorR, mColorG, mColorB, mColorA);
- }
-}
-
-void OpenGLRenderer::setupDrawShaderUniforms(const SkShader* shader, bool ignoreTransform) {
- if (shader == NULL) {
- return;
- }
-
- if (ignoreTransform) {
- // if ignoreTransform=true was passed to setupDrawModelView, undo currentTransform()
- // because it was built into modelView / the geometry, and the description needs to
- // compensate.
- mat4 modelViewWithoutTransform;
- modelViewWithoutTransform.loadInverse(*currentTransform());
- modelViewWithoutTransform.multiply(mModelViewMatrix);
- mModelViewMatrix.load(modelViewWithoutTransform);
- }
-
- SkiaShader::setupProgram(&mCaches, mModelViewMatrix, &mTextureUnit, mExtensions, *shader);
-}
-
-void OpenGLRenderer::setupDrawColorFilterUniforms(const SkColorFilter* filter) {
- if (NULL == filter) {
- return;
- }
-
- SkColor color;
- SkXfermode::Mode mode;
- if (filter->asColorMode(&color, &mode)) {
- const int alpha = SkColorGetA(color);
- const GLfloat a = alpha / 255.0f;
- const GLfloat r = a * SkColorGetR(color) / 255.0f;
- const GLfloat g = a * SkColorGetG(color) / 255.0f;
- const GLfloat b = a * SkColorGetB(color) / 255.0f;
- glUniform4f(mCaches.currentProgram->getUniform("colorBlend"), r, g, b, a);
- return;
- }
-
- SkScalar srcColorMatrix[20];
- if (filter->asColorMatrix(srcColorMatrix)) {
-
- float colorMatrix[16];
- memcpy(colorMatrix, srcColorMatrix, 4 * sizeof(float));
- memcpy(&colorMatrix[4], &srcColorMatrix[5], 4 * sizeof(float));
- memcpy(&colorMatrix[8], &srcColorMatrix[10], 4 * sizeof(float));
- memcpy(&colorMatrix[12], &srcColorMatrix[15], 4 * sizeof(float));
-
- // Skia uses the range [0..255] for the addition vector, but we need
- // the [0..1] range to apply the vector in GLSL
- float colorVector[4];
- colorVector[0] = srcColorMatrix[4] / 255.0f;
- colorVector[1] = srcColorMatrix[9] / 255.0f;
- colorVector[2] = srcColorMatrix[14] / 255.0f;
- colorVector[3] = srcColorMatrix[19] / 255.0f;
-
- glUniformMatrix4fv(mCaches.currentProgram->getUniform("colorMatrix"), 1,
- GL_FALSE, colorMatrix);
- glUniform4fv(mCaches.currentProgram->getUniform("colorMatrixVector"), 1, colorVector);
- return;
- }
-
- // it is an error if we ever get here
-}
-
-void OpenGLRenderer::setupDrawTextGammaUniforms() {
- mCaches.fontRenderer->setupProgram(mDescription, mCaches.currentProgram);
-}
-
-void OpenGLRenderer::setupDrawSimpleMesh() {
- bool force = mCaches.bindMeshBuffer();
- mCaches.bindPositionVertexPointer(force, 0);
- mCaches.unbindIndicesBuffer();
-}
-
-void OpenGLRenderer::setupDrawTexture(GLuint texture) {
- if (texture) bindTexture(texture);
- mTextureUnit++;
- mCaches.enableTexCoordsVertexArray();
-}
-
-void OpenGLRenderer::setupDrawExternalTexture(GLuint texture) {
- bindExternalTexture(texture);
- mTextureUnit++;
- mCaches.enableTexCoordsVertexArray();
-}
-
-void OpenGLRenderer::setupDrawTextureTransform() {
- mDescription.hasTextureTransform = true;
-}
-
-void OpenGLRenderer::setupDrawTextureTransformUniforms(mat4& transform) {
- glUniformMatrix4fv(mCaches.currentProgram->getUniform("mainTextureTransform"), 1,
- GL_FALSE, &transform.data[0]);
-}
-
-void OpenGLRenderer::setupDrawMesh(const GLvoid* vertices,
- const GLvoid* texCoords, GLuint vbo) {
- bool force = false;
- if (!vertices || vbo) {
- force = mCaches.bindMeshBuffer(vbo == 0 ? mCaches.meshBuffer : vbo);
- } else {
- force = mCaches.unbindMeshBuffer();
- }
-
- mCaches.bindPositionVertexPointer(force, vertices);
- if (mCaches.currentProgram->texCoords >= 0) {
- mCaches.bindTexCoordsVertexPointer(force, texCoords);
+ mRenderState.render(glop);
+ if (!mRenderState.stencil().isWriteEnabled()) {
+ // TODO: specify more clearly when a draw should dirty the layer.
+ // is writing to the stencil the only time we should ignore this?
+ dirtyLayer(glop.bounds.left, glop.bounds.top, glop.bounds.right, glop.bounds.bottom);
+ mDirty = true;
}
-
- mCaches.unbindIndicesBuffer();
-}
-
-void OpenGLRenderer::setupDrawMesh(const GLvoid* vertices,
- const GLvoid* texCoords, const GLvoid* colors) {
- bool force = mCaches.unbindMeshBuffer();
- GLsizei stride = sizeof(ColorTextureVertex);
-
- mCaches.bindPositionVertexPointer(force, vertices, stride);
- if (mCaches.currentProgram->texCoords >= 0) {
- mCaches.bindTexCoordsVertexPointer(force, texCoords, stride);
- }
- int slot = mCaches.currentProgram->getAttrib("colors");
- if (slot >= 0) {
- glEnableVertexAttribArray(slot);
- glVertexAttribPointer(slot, 4, GL_FLOAT, GL_FALSE, stride, colors);
- }
-
- mCaches.unbindIndicesBuffer();
-}
-
-void OpenGLRenderer::setupDrawMeshIndices(const GLvoid* vertices,
- const GLvoid* texCoords, GLuint vbo) {
- bool force = false;
- // If vbo is != 0 we want to treat the vertices parameter as an offset inside
- // a VBO. However, if vertices is set to NULL and vbo == 0 then we want to
- // use the default VBO found in Caches
- if (!vertices || vbo) {
- force = mCaches.bindMeshBuffer(vbo == 0 ? mCaches.meshBuffer : vbo);
- } else {
- force = mCaches.unbindMeshBuffer();
- }
- mCaches.bindQuadIndicesBuffer();
-
- mCaches.bindPositionVertexPointer(force, vertices);
- if (mCaches.currentProgram->texCoords >= 0) {
- mCaches.bindTexCoordsVertexPointer(force, texCoords);
- }
-}
-
-void OpenGLRenderer::setupDrawIndexedVertices(GLvoid* vertices) {
- bool force = mCaches.unbindMeshBuffer();
- mCaches.bindQuadIndicesBuffer();
- mCaches.bindPositionVertexPointer(force, vertices, gVertexStride);
}
///////////////////////////////////////////////////////////////////////////////
// Drawing
///////////////////////////////////////////////////////////////////////////////
-status_t OpenGLRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t replayFlags) {
- status_t status;
+void OpenGLRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t replayFlags) {
// All the usual checks and setup operations (quickReject, setupDraw, etc.)
// will be performed by the display list itself
if (renderNode && renderNode->isRenderable()) {
// compute 3d ordering
renderNode->computeOrdering();
if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
- status = startFrame();
+ startFrame();
ReplayStateStruct replayStruct(*this, dirty, replayFlags);
renderNode->replay(replayStruct, 0);
- return status | replayStruct.mDrawGlStatus;
+ return;
}
// Don't avoid overdraw when visualizing, since that makes it harder to
// debug where it's coming from, and when the problem occurs.
bool avoidOverdraw = !mCaches.debugOverdraw;
- DeferredDisplayList deferredList(*currentClipRect(), avoidOverdraw);
+ DeferredDisplayList deferredList(mState.currentClipRect(), avoidOverdraw);
DeferStateStruct deferStruct(deferredList, *this, replayFlags);
renderNode->defer(deferStruct, 0);
flushLayers();
- status = startFrame();
+ startFrame();
- return deferredList.flush(*this, dirty) | status;
- }
-
- // Even if there is no drawing command(Ex: invisible),
- // it still needs startFrame to clear buffer and start tiling.
- return startFrame();
-}
-
-void OpenGLRenderer::drawAlphaBitmap(Texture* texture, float left, float top, const SkPaint* paint) {
- int color = paint != NULL ? paint->getColor() : 0;
-
- float x = left;
- float y = top;
-
- texture->setWrap(GL_CLAMP_TO_EDGE, true);
-
- bool ignoreTransform = false;
- if (currentTransform()->isPureTranslate()) {
- x = (int) floorf(left + currentTransform()->getTranslateX() + 0.5f);
- y = (int) floorf(top + currentTransform()->getTranslateY() + 0.5f);
- ignoreTransform = true;
-
- texture->setFilter(GL_NEAREST, true);
+ deferredList.flush(*this, dirty);
} else {
- texture->setFilter(getFilter(paint), true);
+ // Even if there is no drawing command(Ex: invisible),
+ // it still needs startFrame to clear buffer and start tiling.
+ startFrame();
}
-
- // No need to check for a UV mapper on the texture object, only ARGB_8888
- // bitmaps get packed in the atlas
- drawAlpha8TextureMesh(x, y, x + texture->width, y + texture->height, texture->id,
- paint, (GLvoid*) NULL, (GLvoid*) gMeshTextureOffset,
- GL_TRIANGLE_STRIP, gMeshCount, ignoreTransform);
}
/**
@@ -1961,102 +1504,79 @@ void OpenGLRenderer::drawAlphaBitmap(Texture* texture, float left, float top, co
* will not set the scissor enable or dirty the current layer, if any.
* The caller is responsible for properly dirtying the current layer.
*/
-status_t OpenGLRenderer::drawBitmaps(const SkBitmap* bitmap, AssetAtlas::Entry* entry,
+void OpenGLRenderer::drawBitmaps(const SkBitmap* bitmap, AssetAtlas::Entry* entry,
int bitmapCount, TextureVertex* vertices, bool pureTranslate,
const Rect& bounds, const SkPaint* paint) {
- mCaches.activeTexture(0);
Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap);
- if (!texture) return DrawGlInfo::kStatusDone;
+ if (!texture) return;
const AutoTexture autoCleanup(texture);
- texture->setWrap(GL_CLAMP_TO_EDGE, true);
- texture->setFilter(pureTranslate ? GL_NEAREST : getFilter(paint), true);
-
- const float x = (int) floorf(bounds.left + 0.5f);
- const float y = (int) floorf(bounds.top + 0.5f);
- if (CC_UNLIKELY(bitmap->colorType() == kAlpha_8_SkColorType)) {
- drawAlpha8TextureMesh(x, y, x + bounds.getWidth(), y + bounds.getHeight(),
- texture->id, paint, &vertices[0].x, &vertices[0].u,
- GL_TRIANGLES, bitmapCount * 6, true,
- kModelViewMode_Translate, false);
- } else {
- drawTextureMesh(x, y, x + bounds.getWidth(), y + bounds.getHeight(),
- texture->id, paint, texture->blend, &vertices[0].x, &vertices[0].u,
- GL_TRIANGLES, bitmapCount * 6, false, true, 0,
- kModelViewMode_Translate, false);
- }
-
- return DrawGlInfo::kStatusDrew;
-}
-
-status_t OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) {
+ // TODO: remove layer dirty in multi-draw callers
+ // TODO: snap doesn't need to touch transform, only texture filter.
+ bool snap = pureTranslate;
+ const float x = floorf(bounds.left + 0.5f);
+ const float y = floorf(bounds.top + 0.5f);
+ int textureFillFlags = static_cast<int>((bitmap->colorType() == kAlpha_8_SkColorType)
+ ? TextureFillFlags::kIsAlphaMaskTexture : TextureFillFlags::kNone);
+ Glop glop;
+ GlopBuilder(mRenderState, mCaches, &glop)
+ .setMeshTexturedMesh(vertices, bitmapCount * 6)
+ .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
+ .setTransform(currentSnapshot()->getOrthoMatrix(), Matrix4::identity(), false)
+ .setModelViewOffsetRectOptionalSnap(snap, x, y, Rect(0, 0, bounds.getWidth(), bounds.getHeight()))
+ .setRoundRectClipState(currentSnapshot()->roundRectClipState)
+ .build();
+ renderGlop(glop);
+}
+
+void OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) {
if (quickRejectSetupScissor(0, 0, bitmap->width(), bitmap->height())) {
- return DrawGlInfo::kStatusDone;
+ return;
}
- mCaches.activeTexture(0);
+ mCaches.textureState().activateTexture(0);
Texture* texture = getTexture(bitmap);
- if (!texture) return DrawGlInfo::kStatusDone;
+ if (!texture) return;
const AutoTexture autoCleanup(texture);
- if (CC_UNLIKELY(bitmap->colorType() == kAlpha_8_SkColorType)) {
- drawAlphaBitmap(texture, 0, 0, paint);
- } else {
- drawTextureRect(0, 0, bitmap->width(), bitmap->height(), texture, paint);
- }
-
- return DrawGlInfo::kStatusDrew;
+ int textureFillFlags = static_cast<int>((bitmap->colorType() == kAlpha_8_SkColorType)
+ ? TextureFillFlags::kIsAlphaMaskTexture : TextureFillFlags::kNone);
+ Glop glop;
+ GlopBuilder(mRenderState, mCaches, &glop)
+ .setMeshTexturedUnitQuad(texture->uvMapper)
+ .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
+ .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false)
+ .setModelViewMapUnitToRectSnap(Rect(0, 0, texture->width, texture->height))
+ .setRoundRectClipState(currentSnapshot()->roundRectClipState)
+ .build();
+ renderGlop(glop);
}
-status_t OpenGLRenderer::drawBitmapData(const SkBitmap* bitmap, const SkPaint* paint) {
- if (quickRejectSetupScissor(0, 0, bitmap->width(), bitmap->height())) {
- return DrawGlInfo::kStatusDone;
- }
-
- mCaches.activeTexture(0);
- Texture* texture = mCaches.textureCache.getTransient(bitmap);
- const AutoTexture autoCleanup(texture);
-
- if (CC_UNLIKELY(bitmap->colorType() == kAlpha_8_SkColorType)) {
- drawAlphaBitmap(texture, 0, 0, paint);
- } else {
- drawTextureRect(0, 0, bitmap->width(), bitmap->height(), texture, paint);
- }
-
- return DrawGlInfo::kStatusDrew;
-}
-
-status_t OpenGLRenderer::drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight,
+void OpenGLRenderer::drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight,
const float* vertices, const int* colors, const SkPaint* paint) {
- if (!vertices || currentSnapshot()->isIgnored()) {
- return DrawGlInfo::kStatusDone;
+ if (!vertices || mState.currentlyIgnored()) {
+ return;
}
- // TODO: use quickReject on bounds from vertices
- mCaches.enableScissor();
-
float left = FLT_MAX;
float top = FLT_MAX;
float right = FLT_MIN;
float bottom = FLT_MIN;
- const uint32_t count = meshWidth * meshHeight * 6;
+ const uint32_t elementCount = meshWidth * meshHeight * 6;
- Vector<ColorTextureVertex> mesh; // TODO: use C++11 unique_ptr
- mesh.setCapacity(count);
- ColorTextureVertex* vertex = mesh.editArray();
+ std::unique_ptr<ColorTextureVertex[]> mesh(new ColorTextureVertex[elementCount]);
+ ColorTextureVertex* vertex = &mesh[0];
- bool cleanupColors = false;
+ std::unique_ptr<int[]> tempColors;
if (!colors) {
uint32_t colorsCount = (meshWidth + 1) * (meshHeight + 1);
- int* newColors = new int[colorsCount];
- memset(newColors, 0xff, colorsCount * sizeof(int));
- colors = newColors;
- cleanupColors = true;
+ tempColors.reset(new int[colorsCount]);
+ memset(tempColors.get(), 0xff, colorsCount * sizeof(int));
+ colors = tempColors.get();
}
- mCaches.activeTexture(0);
Texture* texture = mRenderState.assetAtlas().getEntryTexture(bitmap);
const UvMapper& mapper(getMapper(texture));
@@ -2096,210 +1616,83 @@ status_t OpenGLRenderer::drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, i
}
if (quickRejectSetupScissor(left, top, right, bottom)) {
- if (cleanupColors) delete[] colors;
- return DrawGlInfo::kStatusDone;
+ return;
}
if (!texture) {
texture = mCaches.textureCache.get(bitmap);
if (!texture) {
- if (cleanupColors) delete[] colors;
- return DrawGlInfo::kStatusDone;
+ return;
}
}
const AutoTexture autoCleanup(texture);
- texture->setWrap(GL_CLAMP_TO_EDGE, true);
- texture->setFilter(getFilter(paint), true);
-
- int alpha;
- SkXfermode::Mode mode;
- getAlphaAndMode(paint, &alpha, &mode);
-
- float a = alpha / 255.0f;
-
- if (hasLayer()) {
- dirtyLayer(left, top, right, bottom, *currentTransform());
- }
-
- setupDraw();
- setupDrawWithTextureAndColor();
- setupDrawColor(a, a, a, a);
- setupDrawColorFilter(getColorFilter(paint));
- setupDrawBlending(paint, true);
- setupDrawProgram();
- setupDrawDirtyRegionsDisabled();
- setupDrawModelView(kModelViewMode_TranslateAndScale, false, 0.0f, 0.0f, 1.0f, 1.0f);
- setupDrawTexture(texture->id);
- setupDrawPureColorUniforms();
- setupDrawColorFilterUniforms(getColorFilter(paint));
- setupDrawMesh(&mesh[0].x, &mesh[0].u, &mesh[0].r);
-
- glDrawArrays(GL_TRIANGLES, 0, count);
-
- int slot = mCaches.currentProgram->getAttrib("colors");
- if (slot >= 0) {
- glDisableVertexAttribArray(slot);
- }
-
- if (cleanupColors) delete[] colors;
-
- return DrawGlInfo::kStatusDrew;
-}
-
-status_t OpenGLRenderer::drawBitmap(const SkBitmap* bitmap,
- float srcLeft, float srcTop, float srcRight, float srcBottom,
- float dstLeft, float dstTop, float dstRight, float dstBottom,
- const SkPaint* paint) {
- if (quickRejectSetupScissor(dstLeft, dstTop, dstRight, dstBottom)) {
- return DrawGlInfo::kStatusDone;
+ /*
+ * TODO: handle alpha_8 textures correctly by applying paint color, but *not*
+ * shader in that case to mimic the behavior in SkiaCanvas::drawBitmapMesh.
+ */
+ Glop glop;
+ GlopBuilder(mRenderState, mCaches, &glop)
+ .setMeshColoredTexturedMesh(mesh.get(), elementCount)
+ .setFillTexturePaint(*texture, static_cast<int>(TextureFillFlags::kNone), paint, currentSnapshot()->alpha)
+ .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false)
+ .setModelViewOffsetRect(0, 0, Rect(left, top, right, bottom))
+ .setRoundRectClipState(currentSnapshot()->roundRectClipState)
+ .build();
+ renderGlop(glop);
+}
+
+void OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, Rect src, Rect dst, const SkPaint* paint) {
+ if (quickRejectSetupScissor(dst)) {
+ return;
}
- mCaches.activeTexture(0);
Texture* texture = getTexture(bitmap);
- if (!texture) return DrawGlInfo::kStatusDone;
+ if (!texture) return;
const AutoTexture autoCleanup(texture);
- const float width = texture->width;
- const float height = texture->height;
-
- float u1 = fmax(0.0f, srcLeft / width);
- float v1 = fmax(0.0f, srcTop / height);
- float u2 = fmin(1.0f, srcRight / width);
- float v2 = fmin(1.0f, srcBottom / height);
-
- getMapper(texture).map(u1, v1, u2, v2);
-
- mCaches.unbindMeshBuffer();
- resetDrawTextureTexCoords(u1, v1, u2, v2);
-
- texture->setWrap(GL_CLAMP_TO_EDGE, true);
-
- float scaleX = (dstRight - dstLeft) / (srcRight - srcLeft);
- float scaleY = (dstBottom - dstTop) / (srcBottom - srcTop);
-
- bool scaled = scaleX != 1.0f || scaleY != 1.0f;
- // Apply a scale transform on the canvas only when a shader is in use
- // Skia handles the ratio between the dst and src rects as a scale factor
- // when a shader is set
- bool useScaleTransform = getShader(paint) && scaled;
- bool ignoreTransform = false;
-
- if (CC_LIKELY(currentTransform()->isPureTranslate() && !useScaleTransform)) {
- float x = (int) floorf(dstLeft + currentTransform()->getTranslateX() + 0.5f);
- float y = (int) floorf(dstTop + currentTransform()->getTranslateY() + 0.5f);
-
- dstRight = x + (dstRight - dstLeft);
- dstBottom = y + (dstBottom - dstTop);
-
- dstLeft = x;
- dstTop = y;
-
- texture->setFilter(scaled ? getFilter(paint) : GL_NEAREST, true);
- ignoreTransform = true;
- } else {
- texture->setFilter(getFilter(paint), true);
- }
-
- if (CC_UNLIKELY(useScaleTransform)) {
- save(SkCanvas::kMatrix_SaveFlag);
- translate(dstLeft, dstTop);
- scale(scaleX, scaleY);
-
- dstLeft = 0.0f;
- dstTop = 0.0f;
-
- dstRight = srcRight - srcLeft;
- dstBottom = srcBottom - srcTop;
- }
-
- if (CC_UNLIKELY(bitmap->colorType() == kAlpha_8_SkColorType)) {
- drawAlpha8TextureMesh(dstLeft, dstTop, dstRight, dstBottom,
- texture->id, paint,
- &mMeshVertices[0].x, &mMeshVertices[0].u,
- GL_TRIANGLE_STRIP, gMeshCount, ignoreTransform);
- } else {
- drawTextureMesh(dstLeft, dstTop, dstRight, dstBottom,
- texture->id, paint, texture->blend,
- &mMeshVertices[0].x, &mMeshVertices[0].u,
- GL_TRIANGLE_STRIP, gMeshCount, false, ignoreTransform);
- }
-
- if (CC_UNLIKELY(useScaleTransform)) {
- restore();
- }
-
- resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
-
- return DrawGlInfo::kStatusDrew;
-}
-
-status_t OpenGLRenderer::drawPatch(const SkBitmap* bitmap, const Res_png_9patch* patch,
- float left, float top, float right, float bottom, const SkPaint* paint) {
- if (quickRejectSetupScissor(left, top, right, bottom)) {
- return DrawGlInfo::kStatusDone;
- }
-
- AssetAtlas::Entry* entry = mRenderState.assetAtlas().getEntry(bitmap);
- const Patch* mesh = mCaches.patchCache.get(entry, bitmap->width(), bitmap->height(),
- right - left, bottom - top, patch);
-
- return drawPatch(bitmap, mesh, entry, left, top, right, bottom, paint);
-}
-
-status_t OpenGLRenderer::drawPatch(const SkBitmap* bitmap, const Patch* mesh,
+ Rect uv(fmax(0.0f, src.left / texture->width),
+ fmax(0.0f, src.top / texture->height),
+ fmin(1.0f, src.right / texture->width),
+ fmin(1.0f, src.bottom / texture->height));
+
+ int textureFillFlags = static_cast<int>((bitmap->colorType() == kAlpha_8_SkColorType)
+ ? TextureFillFlags::kIsAlphaMaskTexture : TextureFillFlags::kNone);
+ Glop glop;
+ GlopBuilder(mRenderState, mCaches, &glop)
+ .setMeshTexturedUvQuad(texture->uvMapper, uv)
+ .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
+ .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false)
+ .setModelViewMapUnitToRectSnap(dst)
+ .setRoundRectClipState(currentSnapshot()->roundRectClipState)
+ .build();
+ renderGlop(glop);
+}
+
+void OpenGLRenderer::drawPatch(const SkBitmap* bitmap, const Patch* mesh,
AssetAtlas::Entry* entry, float left, float top, float right, float bottom,
const SkPaint* paint) {
- if (quickRejectSetupScissor(left, top, right, bottom)) {
- return DrawGlInfo::kStatusDone;
- }
-
- if (CC_LIKELY(mesh && mesh->verticesCount > 0)) {
- mCaches.activeTexture(0);
- Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap);
- if (!texture) return DrawGlInfo::kStatusDone;
- const AutoTexture autoCleanup(texture);
-
- texture->setWrap(GL_CLAMP_TO_EDGE, true);
- texture->setFilter(GL_LINEAR, true);
-
- const bool pureTranslate = currentTransform()->isPureTranslate();
- // Mark the current layer dirty where we are going to draw the patch
- if (hasLayer() && mesh->hasEmptyQuads) {
- const float offsetX = left + currentTransform()->getTranslateX();
- const float offsetY = top + currentTransform()->getTranslateY();
- const size_t count = mesh->quads.size();
- for (size_t i = 0; i < count; i++) {
- const Rect& bounds = mesh->quads.itemAt(i);
- if (CC_LIKELY(pureTranslate)) {
- const float x = (int) floorf(bounds.left + offsetX + 0.5f);
- const float y = (int) floorf(bounds.top + offsetY + 0.5f);
- dirtyLayer(x, y, x + bounds.getWidth(), y + bounds.getHeight());
- } else {
- dirtyLayer(left + bounds.left, top + bounds.top,
- left + bounds.right, top + bounds.bottom, *currentTransform());
- }
- }
- }
+ if (!mesh || !mesh->verticesCount || quickRejectSetupScissor(left, top, right, bottom)) {
+ return;
+ }
- bool ignoreTransform = false;
- if (CC_LIKELY(pureTranslate)) {
- const float x = (int) floorf(left + currentTransform()->getTranslateX() + 0.5f);
- const float y = (int) floorf(top + currentTransform()->getTranslateY() + 0.5f);
+ Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap);
+ if (!texture) return;
- right = x + right - left;
- bottom = y + bottom - top;
- left = x;
- top = y;
- ignoreTransform = true;
- }
- drawIndexedTextureMesh(left, top, right, bottom, texture->id, paint,
- texture->blend, (GLvoid*) mesh->offset, (GLvoid*) mesh->textureOffset,
- GL_TRIANGLES, mesh->indexCount, false, ignoreTransform,
- mCaches.patchCache.getMeshBuffer(), kModelViewMode_Translate, !mesh->hasEmptyQuads);
+ // 9 patches are built for stretching - always filter
+ int textureFillFlags = static_cast<int>(TextureFillFlags::kForceFilter);
+ if (bitmap->colorType() == kAlpha_8_SkColorType) {
+ textureFillFlags |= TextureFillFlags::kIsAlphaMaskTexture;
}
-
- return DrawGlInfo::kStatusDrew;
+ Glop glop;
+ GlopBuilder(mRenderState, mCaches, &glop)
+ .setMeshPatchQuads(*mesh)
+ .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
+ .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false)
+ .setModelViewOffsetRectSnap(left, top, Rect(0, 0, right - left, bottom - top)) // TODO: get minimal bounds from patch
+ .setRoundRectClipState(currentSnapshot()->roundRectClipState)
+ .build();
+ renderGlop(glop);
}
/**
@@ -2307,87 +1700,49 @@ status_t OpenGLRenderer::drawPatch(const SkBitmap* bitmap, const Patch* mesh,
* will not set the scissor enable or dirty the current layer, if any.
* The caller is responsible for properly dirtying the current layer.
*/
-status_t OpenGLRenderer::drawPatches(const SkBitmap* bitmap, AssetAtlas::Entry* entry,
- TextureVertex* vertices, uint32_t indexCount, const SkPaint* paint) {
- mCaches.activeTexture(0);
+void OpenGLRenderer::drawPatches(const SkBitmap* bitmap, AssetAtlas::Entry* entry,
+ TextureVertex* vertices, uint32_t elementCount, const SkPaint* paint) {
+ mCaches.textureState().activateTexture(0);
Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap);
- if (!texture) return DrawGlInfo::kStatusDone;
+ if (!texture) return;
const AutoTexture autoCleanup(texture);
- texture->setWrap(GL_CLAMP_TO_EDGE, true);
- texture->setFilter(GL_LINEAR, true);
-
- drawIndexedTextureMesh(0.0f, 0.0f, 1.0f, 1.0f, texture->id, paint,
- texture->blend, &vertices[0].x, &vertices[0].u,
- GL_TRIANGLES, indexCount, false, true, 0, kModelViewMode_Translate, false);
-
- return DrawGlInfo::kStatusDrew;
+ // TODO: get correct bounds from caller
+ // 9 patches are built for stretching - always filter
+ int textureFillFlags = static_cast<int>(TextureFillFlags::kForceFilter);
+ if (bitmap->colorType() == kAlpha_8_SkColorType) {
+ textureFillFlags |= TextureFillFlags::kIsAlphaMaskTexture;
+ }
+ Glop glop;
+ GlopBuilder(mRenderState, mCaches, &glop)
+ .setMeshTexturedIndexedQuads(vertices, elementCount)
+ .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
+ .setTransform(currentSnapshot()->getOrthoMatrix(), Matrix4::identity(), false)
+ .setModelViewOffsetRect(0, 0, Rect(0, 0, 0, 0))
+ .setRoundRectClipState(currentSnapshot()->roundRectClipState)
+ .build();
+ renderGlop(glop);
}
-status_t OpenGLRenderer::drawVertexBuffer(float translateX, float translateY,
+void OpenGLRenderer::drawVertexBuffer(float translateX, float translateY,
const VertexBuffer& vertexBuffer, const SkPaint* paint, int displayFlags) {
// not missing call to quickReject/dirtyLayer, always done at a higher level
if (!vertexBuffer.getVertexCount()) {
// no vertices to draw
- return DrawGlInfo::kStatusDone;
- }
-
- Rect bounds(vertexBuffer.getBounds());
- bounds.translate(translateX, translateY);
- dirtyLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, *currentTransform());
-
- int color = paint->getColor();
- bool isAA = paint->isAntiAlias();
-
- setupDraw();
- setupDrawNoTexture();
- if (isAA) setupDrawVertexAlpha((displayFlags & kVertexBuffer_ShadowInterp));
- setupDrawColor(color, ((color >> 24) & 0xFF) * mSnapshot->alpha);
- setupDrawColorFilter(getColorFilter(paint));
- setupDrawShader(getShader(paint));
- setupDrawBlending(paint, isAA);
- setupDrawProgram();
- setupDrawModelView(kModelViewMode_Translate, (displayFlags & kVertexBuffer_Offset),
- translateX, translateY, 0, 0);
- setupDrawColorUniforms(getShader(paint));
- setupDrawColorFilterUniforms(getColorFilter(paint));
- setupDrawShaderUniforms(getShader(paint));
-
- const void* vertices = vertexBuffer.getBuffer();
- bool force = mCaches.unbindMeshBuffer();
- mCaches.bindPositionVertexPointer(true, vertices, isAA ? gAlphaVertexStride : gVertexStride);
- mCaches.resetTexCoordsVertexPointer();
-
- int alphaSlot = -1;
- if (isAA) {
- void* alphaCoords = ((GLbyte*) vertices) + gVertexAlphaOffset;
- alphaSlot = mCaches.currentProgram->getAttrib("vtxAlpha");
- // TODO: avoid enable/disable in back to back uses of the alpha attribute
- glEnableVertexAttribArray(alphaSlot);
- glVertexAttribPointer(alphaSlot, 1, GL_FLOAT, GL_FALSE, gAlphaVertexStride, alphaCoords);
- }
-
- const VertexBuffer::Mode mode = vertexBuffer.getMode();
- if (mode == VertexBuffer::kStandard) {
- mCaches.unbindIndicesBuffer();
- glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexBuffer.getVertexCount());
- } else if (mode == VertexBuffer::kOnePolyRingShadow) {
- mCaches.bindShadowIndicesBuffer();
- glDrawElements(GL_TRIANGLE_STRIP, ONE_POLY_RING_SHADOW_INDEX_COUNT, GL_UNSIGNED_SHORT, 0);
- } else if (mode == VertexBuffer::kTwoPolyRingShadow) {
- mCaches.bindShadowIndicesBuffer();
- glDrawElements(GL_TRIANGLE_STRIP, TWO_POLY_RING_SHADOW_INDEX_COUNT, GL_UNSIGNED_SHORT, 0);
- } else if (mode == VertexBuffer::kIndices) {
- mCaches.unbindIndicesBuffer();
- glDrawElements(GL_TRIANGLE_STRIP, vertexBuffer.getIndexCount(), GL_UNSIGNED_SHORT,
- vertexBuffer.getIndices());
- }
-
- if (isAA) {
- glDisableVertexAttribArray(alphaSlot);
- }
-
- return DrawGlInfo::kStatusDrew;
+ return;
+ }
+
+ bool fudgeOffset = displayFlags & kVertexBuffer_Offset;
+ bool shadowInterp = displayFlags & kVertexBuffer_ShadowInterp;
+ Glop glop;
+ GlopBuilder(mRenderState, mCaches, &glop)
+ .setMeshVertexBuffer(vertexBuffer, shadowInterp)
+ .setFillPaint(*paint, currentSnapshot()->alpha)
+ .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), fudgeOffset)
+ .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds())
+ .setRoundRectClipState(currentSnapshot()->roundRectClipState)
+ .build();
+ renderGlop(glop);
}
/**
@@ -2399,11 +1754,11 @@ status_t OpenGLRenderer::drawVertexBuffer(float translateX, float translateY,
*
* Doesn't yet support joins, caps, or path effects.
*/
-status_t OpenGLRenderer::drawConvexPath(const SkPath& path, const SkPaint* paint) {
+void OpenGLRenderer::drawConvexPath(const SkPath& path, const SkPaint* paint) {
VertexBuffer vertexBuffer;
// TODO: try clipping large paths to viewport
PathTessellator::tessellatePath(path, paint, *currentTransform(), vertexBuffer);
- return drawVertexBuffer(vertexBuffer, paint);
+ drawVertexBuffer(vertexBuffer, paint);
}
/**
@@ -2417,8 +1772,8 @@ status_t OpenGLRenderer::drawConvexPath(const SkPath& path, const SkPaint* paint
* TODO: try using a fixed input buffer for non-capped lines as in text rendering. this may reduce
* memory transfer by removing need for degenerate vertices.
*/
-status_t OpenGLRenderer::drawLines(const float* points, int count, const SkPaint* paint) {
- if (currentSnapshot()->isIgnored() || count < 4) return DrawGlInfo::kStatusDone;
+void OpenGLRenderer::drawLines(const float* points, int count, const SkPaint* paint) {
+ if (mState.currentlyIgnored() || count < 4) return;
count &= ~0x3; // round down to nearest four
@@ -2427,15 +1782,15 @@ status_t OpenGLRenderer::drawLines(const float* points, int count, const SkPaint
const Rect& bounds = buffer.getBounds();
if (quickRejectSetupScissor(bounds.left, bounds.top, bounds.right, bounds.bottom)) {
- return DrawGlInfo::kStatusDone;
+ return;
}
int displayFlags = paint->isAntiAlias() ? 0 : kVertexBuffer_Offset;
- return drawVertexBuffer(buffer, paint, displayFlags);
+ drawVertexBuffer(buffer, paint, displayFlags);
}
-status_t OpenGLRenderer::drawPoints(const float* points, int count, const SkPaint* paint) {
- if (currentSnapshot()->isIgnored() || count < 2) return DrawGlInfo::kStatusDone;
+void OpenGLRenderer::drawPoints(const float* points, int count, const SkPaint* paint) {
+ if (mState.currentlyIgnored() || count < 2) return;
count &= ~0x1; // round down to nearest two
@@ -2444,18 +1799,20 @@ status_t OpenGLRenderer::drawPoints(const float* points, int count, const SkPain
const Rect& bounds = buffer.getBounds();
if (quickRejectSetupScissor(bounds.left, bounds.top, bounds.right, bounds.bottom)) {
- return DrawGlInfo::kStatusDone;
+ return;
}
int displayFlags = paint->isAntiAlias() ? 0 : kVertexBuffer_Offset;
- return drawVertexBuffer(buffer, paint, displayFlags);
+ drawVertexBuffer(buffer, paint, displayFlags);
+
+ mDirty = true;
}
-status_t OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) {
+void OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) {
// No need to check against the clip, we fill the clip region
- if (currentSnapshot()->isIgnored()) return DrawGlInfo::kStatusDone;
+ if (mState.currentlyIgnored()) return;
- Rect clip(*currentClipRect());
+ Rect clip(mState.currentClipRect());
clip.snapToPixelBoundaries();
SkPaint paint;
@@ -2464,12 +1821,12 @@ status_t OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) {
drawColorRect(clip.left, clip.top, clip.right, clip.bottom, &paint, true);
- return DrawGlInfo::kStatusDrew;
+ mDirty = true;
}
-status_t OpenGLRenderer::drawShape(float left, float top, const PathTexture* texture,
+void OpenGLRenderer::drawShape(float left, float top, PathTexture* texture,
const SkPaint* paint) {
- if (!texture) return DrawGlInfo::kStatusDone;
+ if (!texture) return;
const AutoTexture autoCleanup(texture);
const float x = left + texture->left - texture->offset;
@@ -2477,89 +1834,89 @@ status_t OpenGLRenderer::drawShape(float left, float top, const PathTexture* tex
drawPathTexture(texture, x, y, paint);
- return DrawGlInfo::kStatusDrew;
+ mDirty = true;
}
-status_t OpenGLRenderer::drawRoundRect(float left, float top, float right, float bottom,
+void OpenGLRenderer::drawRoundRect(float left, float top, float right, float bottom,
float rx, float ry, const SkPaint* p) {
- if (currentSnapshot()->isIgnored()
+ if (mState.currentlyIgnored()
|| quickRejectSetupScissor(left, top, right, bottom, p)
- || paintWillNotDraw(*p)) {
- return DrawGlInfo::kStatusDone;
+ || PaintUtils::paintWillNotDraw(*p)) {
+ return;
}
- if (p->getPathEffect() != 0) {
- mCaches.activeTexture(0);
- const PathTexture* texture = mCaches.pathCache.getRoundRect(
+ if (p->getPathEffect() != nullptr) {
+ mCaches.textureState().activateTexture(0);
+ PathTexture* texture = mCaches.pathCache.getRoundRect(
right - left, bottom - top, rx, ry, p);
- return drawShape(left, top, texture, p);
+ drawShape(left, top, texture, p);
+ } else {
+ const VertexBuffer* vertexBuffer = mCaches.tessellationCache.getRoundRect(
+ *currentTransform(), *p, right - left, bottom - top, rx, ry);
+ drawVertexBuffer(left, top, *vertexBuffer, p);
}
-
- const VertexBuffer* vertexBuffer = mCaches.tessellationCache.getRoundRect(
- *currentTransform(), *p, right - left, bottom - top, rx, ry);
- return drawVertexBuffer(left, top, *vertexBuffer, p);
}
-status_t OpenGLRenderer::drawCircle(float x, float y, float radius, const SkPaint* p) {
- if (currentSnapshot()->isIgnored()
+void OpenGLRenderer::drawCircle(float x, float y, float radius, const SkPaint* p) {
+ if (mState.currentlyIgnored()
|| quickRejectSetupScissor(x - radius, y - radius, x + radius, y + radius, p)
- || paintWillNotDraw(*p)) {
- return DrawGlInfo::kStatusDone;
- }
- if (p->getPathEffect() != 0) {
- mCaches.activeTexture(0);
- const PathTexture* texture = mCaches.pathCache.getCircle(radius, p);
- return drawShape(x - radius, y - radius, texture, p);
+ || PaintUtils::paintWillNotDraw(*p)) {
+ return;
}
-
- SkPath path;
- if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
- path.addCircle(x, y, radius + p->getStrokeWidth() / 2);
+ if (p->getPathEffect() != nullptr) {
+ mCaches.textureState().activateTexture(0);
+ PathTexture* texture = mCaches.pathCache.getCircle(radius, p);
+ drawShape(x - radius, y - radius, texture, p);
} else {
- path.addCircle(x, y, radius);
+ SkPath path;
+ if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
+ path.addCircle(x, y, radius + p->getStrokeWidth() / 2);
+ } else {
+ path.addCircle(x, y, radius);
+ }
+ drawConvexPath(path, p);
}
- return drawConvexPath(path, p);
}
-status_t OpenGLRenderer::drawOval(float left, float top, float right, float bottom,
+void OpenGLRenderer::drawOval(float left, float top, float right, float bottom,
const SkPaint* p) {
- if (currentSnapshot()->isIgnored()
+ if (mState.currentlyIgnored()
|| quickRejectSetupScissor(left, top, right, bottom, p)
- || paintWillNotDraw(*p)) {
- return DrawGlInfo::kStatusDone;
- }
-
- if (p->getPathEffect() != 0) {
- mCaches.activeTexture(0);
- const PathTexture* texture = mCaches.pathCache.getOval(right - left, bottom - top, p);
- return drawShape(left, top, texture, p);
+ || PaintUtils::paintWillNotDraw(*p)) {
+ return;
}
- SkPath path;
- SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
- if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
- rect.outset(p->getStrokeWidth() / 2, p->getStrokeWidth() / 2);
+ if (p->getPathEffect() != nullptr) {
+ mCaches.textureState().activateTexture(0);
+ PathTexture* texture = mCaches.pathCache.getOval(right - left, bottom - top, p);
+ drawShape(left, top, texture, p);
+ } else {
+ SkPath path;
+ SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
+ if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
+ rect.outset(p->getStrokeWidth() / 2, p->getStrokeWidth() / 2);
+ }
+ path.addOval(rect);
+ drawConvexPath(path, p);
}
- path.addOval(rect);
- return drawConvexPath(path, p);
}
-status_t OpenGLRenderer::drawArc(float left, float top, float right, float bottom,
+void OpenGLRenderer::drawArc(float left, float top, float right, float bottom,
float startAngle, float sweepAngle, bool useCenter, const SkPaint* p) {
- if (currentSnapshot()->isIgnored()
+ if (mState.currentlyIgnored()
|| quickRejectSetupScissor(left, top, right, bottom, p)
- || paintWillNotDraw(*p)) {
- return DrawGlInfo::kStatusDone;
+ || PaintUtils::paintWillNotDraw(*p)) {
+ return;
}
// TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180)
- if (p->getStyle() != SkPaint::kStroke_Style || p->getPathEffect() != 0 || useCenter) {
- mCaches.activeTexture(0);
- const PathTexture* texture = mCaches.pathCache.getArc(right - left, bottom - top,
+ if (p->getStyle() != SkPaint::kStroke_Style || p->getPathEffect() != nullptr || useCenter) {
+ mCaches.textureState().activateTexture(0);
+ PathTexture* texture = mCaches.pathCache.getArc(right - left, bottom - top,
startAngle, sweepAngle, useCenter, p);
- return drawShape(left, top, texture, p);
+ drawShape(left, top, texture, p);
+ return;
}
-
SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
rect.outset(p->getStrokeWidth() / 2, p->getStrokeWidth() / 2);
@@ -2573,53 +1930,54 @@ status_t OpenGLRenderer::drawArc(float left, float top, float right, float botto
if (useCenter) {
path.close();
}
- return drawConvexPath(path, p);
+ drawConvexPath(path, p);
}
// See SkPaintDefaults.h
#define SkPaintDefaults_MiterLimit SkIntToScalar(4)
-status_t OpenGLRenderer::drawRect(float left, float top, float right, float bottom,
+void OpenGLRenderer::drawRect(float left, float top, float right, float bottom,
const SkPaint* p) {
- if (currentSnapshot()->isIgnored()
+ if (mState.currentlyIgnored()
|| quickRejectSetupScissor(left, top, right, bottom, p)
- || paintWillNotDraw(*p)) {
- return DrawGlInfo::kStatusDone;
+ || PaintUtils::paintWillNotDraw(*p)) {
+ return;
}
if (p->getStyle() != SkPaint::kFill_Style) {
// only fill style is supported by drawConvexPath, since others have to handle joins
- if (p->getPathEffect() != 0 || p->getStrokeJoin() != SkPaint::kMiter_Join ||
+ if (p->getPathEffect() != nullptr || p->getStrokeJoin() != SkPaint::kMiter_Join ||
p->getStrokeMiter() != SkPaintDefaults_MiterLimit) {
- mCaches.activeTexture(0);
- const PathTexture* texture =
+ mCaches.textureState().activateTexture(0);
+ PathTexture* texture =
mCaches.pathCache.getRect(right - left, bottom - top, p);
- return drawShape(left, top, texture, p);
+ drawShape(left, top, texture, p);
+ } else {
+ SkPath path;
+ SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
+ if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
+ rect.outset(p->getStrokeWidth() / 2, p->getStrokeWidth() / 2);
+ }
+ path.addRect(rect);
+ drawConvexPath(path, p);
}
+ } else {
+ if (p->isAntiAlias() && !currentTransform()->isSimple()) {
+ SkPath path;
+ path.addRect(left, top, right, bottom);
+ drawConvexPath(path, p);
+ } else {
+ drawColorRect(left, top, right, bottom, p);
- SkPath path;
- SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
- if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
- rect.outset(p->getStrokeWidth() / 2, p->getStrokeWidth() / 2);
+ mDirty = true;
}
- path.addRect(rect);
- return drawConvexPath(path, p);
- }
-
- if (p->isAntiAlias() && !currentTransform()->isSimple()) {
- SkPath path;
- path.addRect(left, top, right, bottom);
- return drawConvexPath(path, p);
- } else {
- drawColorRect(left, top, right, bottom, p);
- return DrawGlInfo::kStatusDrew;
}
}
void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const char* text,
int bytesCount, int count, const float* positions,
FontRenderer& fontRenderer, int alpha, float x, float y) {
- mCaches.activeTexture(0);
+ mCaches.textureState().activateTexture(0);
TextShadow textShadow;
if (!getTextShadow(paint, &textShadow)) {
@@ -2629,63 +1987,52 @@ void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const char* text,
// NOTE: The drop shadow will not perform gamma correction
// if shader-based correction is enabled
mCaches.dropShadowCache.setFontRenderer(fontRenderer);
- const ShadowTexture* shadow = mCaches.dropShadowCache.get(
+ ShadowTexture* texture = mCaches.dropShadowCache.get(
paint, text, bytesCount, count, textShadow.radius, positions);
// If the drop shadow exceeds the max texture size or couldn't be
// allocated, skip drawing
- if (!shadow) return;
- const AutoTexture autoCleanup(shadow);
-
- const float sx = x - shadow->left + textShadow.dx;
- const float sy = y - shadow->top + textShadow.dy;
-
- const int shadowAlpha = ((textShadow.color >> 24) & 0xFF) * mSnapshot->alpha;
- if (getShader(paint)) {
- textShadow.color = SK_ColorWHITE;
- }
+ if (!texture) return;
+ const AutoTexture autoCleanup(texture);
- setupDraw();
- setupDrawWithTexture(true);
- setupDrawAlpha8Color(textShadow.color, shadowAlpha < 255 ? shadowAlpha : alpha);
- setupDrawColorFilter(getColorFilter(paint));
- setupDrawShader(getShader(paint));
- setupDrawBlending(paint, true);
- setupDrawProgram();
- setupDrawModelView(kModelViewMode_TranslateAndScale, false,
- sx, sy, sx + shadow->width, sy + shadow->height);
- setupDrawTexture(shadow->id);
- setupDrawPureColorUniforms();
- setupDrawColorFilterUniforms(getColorFilter(paint));
- setupDrawShaderUniforms(getShader(paint));
- setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset);
+ const float sx = x - texture->left + textShadow.dx;
+ const float sy = y - texture->top + textShadow.dy;
- glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
+ Glop glop;
+ GlopBuilder(mRenderState, mCaches, &glop)
+ .setMeshTexturedUnitQuad(nullptr)
+ .setFillShadowTexturePaint(*texture, textShadow.color, *paint, currentSnapshot()->alpha)
+ .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false)
+ .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width, sy + texture->height))
+ .setRoundRectClipState(currentSnapshot()->roundRectClipState)
+ .build();
+ renderGlop(glop);
}
bool OpenGLRenderer::canSkipText(const SkPaint* paint) const {
- float alpha = (hasTextShadow(paint) ? 1.0f : paint->getAlpha()) * mSnapshot->alpha;
- return alpha == 0.0f && getXfermode(paint->getXfermode()) == SkXfermode::kSrcOver_Mode;
+ float alpha = (hasTextShadow(paint) ? 1.0f : paint->getAlpha()) * currentSnapshot()->alpha;
+ return MathUtils::isZero(alpha)
+ && PaintUtils::getXfermode(paint->getXfermode()) == SkXfermode::kSrcOver_Mode;
}
-status_t OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count,
+void OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count,
const float* positions, const SkPaint* paint) {
- if (text == NULL || count == 0 || currentSnapshot()->isIgnored() || canSkipText(paint)) {
- return DrawGlInfo::kStatusDone;
+ if (text == nullptr || count == 0 || mState.currentlyIgnored() || canSkipText(paint)) {
+ return;
}
// NOTE: Skia does not support perspective transform on drawPosText yet
if (!currentTransform()->isSimple()) {
- return DrawGlInfo::kStatusDone;
+ return;
}
- mCaches.enableScissor();
+ mRenderState.scissor().setEnabled(true);
float x = 0.0f;
float y = 0.0f;
const bool pureTranslate = currentTransform()->isPureTranslate();
if (pureTranslate) {
- x = (int) floorf(x + currentTransform()->getTranslateX() + 0.5f);
- y = (int) floorf(y + currentTransform()->getTranslateY() + 0.5f);
+ x = floorf(x + currentTransform()->getTranslateX() + 0.5f);
+ y = floorf(y + currentTransform()->getTranslateY() + 0.5f);
}
FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
@@ -2707,23 +2054,16 @@ status_t OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count
}
fontRenderer.setTextureFiltering(linearFilter);
- const Rect* clip = pureTranslate ? mSnapshot->clipRect : &mSnapshot->getLocalClip();
+ const Rect& clip(pureTranslate ? writableSnapshot()->getClipRect() : writableSnapshot()->getLocalClip());
Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
- const bool hasActiveLayer = hasLayer();
-
- TextSetupFunctor functor(this, x, y, pureTranslate, alpha, mode, paint);
- if (fontRenderer.renderPosText(paint, clip, text, 0, bytesCount, count, x, y,
- positions, hasActiveLayer ? &bounds : NULL, &functor)) {
- if (hasActiveLayer) {
- if (!pureTranslate) {
- currentTransform()->mapRect(bounds);
- }
- dirtyLayerUnchecked(bounds, getRegion());
- }
+ TextDrawFunctor functor(this, x, y, pureTranslate, alpha, mode, paint);
+ if (fontRenderer.renderPosText(paint, &clip, text, 0, bytesCount, count, x, y,
+ positions, hasLayer() ? &bounds : nullptr, &functor)) {
+ dirtyLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, *currentTransform());
+ mDirty = true;
}
- return DrawGlInfo::kStatusDrew;
}
bool OpenGLRenderer::findBestFontTransform(const mat4& transform, SkMatrix* outMatrix) const {
@@ -2747,16 +2087,77 @@ bool OpenGLRenderer::findBestFontTransform(const mat4& transform, SkMatrix* outM
return true;
}
-status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float x, float y,
+int OpenGLRenderer::getSaveCount() const {
+ return mState.getSaveCount();
+}
+
+int OpenGLRenderer::save(int flags) {
+ return mState.save(flags);
+}
+
+void OpenGLRenderer::restore() {
+ mState.restore();
+}
+
+void OpenGLRenderer::restoreToCount(int saveCount) {
+ mState.restoreToCount(saveCount);
+}
+
+void OpenGLRenderer::translate(float dx, float dy, float dz) {
+ mState.translate(dx, dy, dz);
+}
+
+void OpenGLRenderer::rotate(float degrees) {
+ mState.rotate(degrees);
+}
+
+void OpenGLRenderer::scale(float sx, float sy) {
+ mState.scale(sx, sy);
+}
+
+void OpenGLRenderer::skew(float sx, float sy) {
+ mState.skew(sx, sy);
+}
+
+void OpenGLRenderer::setMatrix(const Matrix4& matrix) {
+ mState.setMatrix(matrix);
+}
+
+void OpenGLRenderer::concatMatrix(const Matrix4& matrix) {
+ mState.concatMatrix(matrix);
+}
+
+bool OpenGLRenderer::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
+ return mState.clipRect(left, top, right, bottom, op);
+}
+
+bool OpenGLRenderer::clipPath(const SkPath* path, SkRegion::Op op) {
+ return mState.clipPath(path, op);
+}
+
+bool OpenGLRenderer::clipRegion(const SkRegion* region, SkRegion::Op op) {
+ return mState.clipRegion(region, op);
+}
+
+void OpenGLRenderer::setClippingOutline(LinearAllocator& allocator, const Outline* outline) {
+ mState.setClippingOutline(allocator, outline);
+}
+
+void OpenGLRenderer::setClippingRoundRect(LinearAllocator& allocator,
+ const Rect& rect, float radius, bool highPriority) {
+ mState.setClippingRoundRect(allocator, rect, radius, highPriority);
+}
+
+void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float x, float y,
const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds,
DrawOpMode drawOpMode) {
- if (drawOpMode == kDrawOpMode_Immediate) {
+ if (drawOpMode == DrawOpMode::kImmediate) {
// The checks for corner-case ignorable text and quick rejection is only done for immediate
// drawing as ops from DeferredDisplayList are already filtered for these
- if (text == NULL || count == 0 || currentSnapshot()->isIgnored() || canSkipText(paint) ||
+ if (text == nullptr || count == 0 || mState.currentlyIgnored() || canSkipText(paint) ||
quickRejectSetupScissor(bounds)) {
- return DrawGlInfo::kStatusDone;
+ return;
}
}
@@ -2767,8 +2168,8 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count, f
const bool pureTranslate = transform.isPureTranslate();
if (CC_LIKELY(pureTranslate)) {
- x = (int) floorf(x + transform.getTranslateX() + 0.5f);
- y = (int) floorf(y + transform.getTranslateY() + 0.5f);
+ x = floorf(x + transform.getTranslateX() + 0.5f);
+ y = floorf(y + transform.getTranslateY() + 0.5f);
}
int alpha;
@@ -2804,25 +2205,25 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count, f
fontRenderer.setTextureFiltering(linearFilter);
// TODO: Implement better clipping for scaled/rotated text
- const Rect* clip = !pureTranslate ? NULL : currentClipRect();
+ const Rect* clip = !pureTranslate ? nullptr : &mState.currentClipRect();
Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
bool status;
- TextSetupFunctor functor(this, x, y, pureTranslate, alpha, mode, paint);
+ TextDrawFunctor functor(this, x, y, pureTranslate, alpha, mode, paint);
// don't call issuedrawcommand, do it at end of batch
- bool forceFinish = (drawOpMode != kDrawOpMode_Defer);
+ bool forceFinish = (drawOpMode != DrawOpMode::kDefer);
if (CC_UNLIKELY(paint->getTextAlign() != SkPaint::kLeft_Align)) {
SkPaint paintCopy(*paint);
paintCopy.setTextAlign(SkPaint::kLeft_Align);
status = fontRenderer.renderPosText(&paintCopy, clip, text, 0, bytesCount, count, x, y,
- positions, hasActiveLayer ? &layerBounds : NULL, &functor, forceFinish);
+ positions, hasActiveLayer ? &layerBounds : nullptr, &functor, forceFinish);
} else {
status = fontRenderer.renderPosText(paint, clip, text, 0, bytesCount, count, x, y,
- positions, hasActiveLayer ? &layerBounds : NULL, &functor, forceFinish);
+ positions, hasActiveLayer ? &layerBounds : nullptr, &functor, forceFinish);
}
- if ((status || drawOpMode != kDrawOpMode_Immediate) && hasActiveLayer) {
+ if ((status || drawOpMode != DrawOpMode::kImmediate) && hasActiveLayer) {
if (!pureTranslate) {
transform.mapRect(layerBounds);
}
@@ -2831,17 +2232,17 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count, f
drawTextDecorations(totalAdvance, oldX, oldY, paint);
- return DrawGlInfo::kStatusDrew;
+ mDirty = true;
}
-status_t OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int count,
+void OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int count,
const SkPath* path, float hOffset, float vOffset, const SkPaint* paint) {
- if (text == NULL || count == 0 || currentSnapshot()->isIgnored() || canSkipText(paint)) {
- return DrawGlInfo::kStatusDone;
+ if (text == nullptr || count == 0 || mState.currentlyIgnored() || canSkipText(paint)) {
+ return;
}
// TODO: avoid scissor by calculating maximum bounds using path bounds + font metrics
- mCaches.enableScissor();
+ mRenderState.scissor().setEnabled(true);
FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
fontRenderer.setFont(paint, SkMatrix::I());
@@ -2850,47 +2251,40 @@ status_t OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int co
int alpha;
SkXfermode::Mode mode;
getAlphaAndMode(paint, &alpha, &mode);
- TextSetupFunctor functor(this, 0.0f, 0.0f, false, alpha, mode, paint);
+ TextDrawFunctor functor(this, 0.0f, 0.0f, false, alpha, mode, paint);
- const Rect* clip = &mSnapshot->getLocalClip();
+ const Rect* clip = &writableSnapshot()->getLocalClip();
Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
- const bool hasActiveLayer = hasLayer();
-
if (fontRenderer.renderTextOnPath(paint, clip, text, 0, bytesCount, count, path,
- hOffset, vOffset, hasActiveLayer ? &bounds : NULL, &functor)) {
- if (hasActiveLayer) {
- currentTransform()->mapRect(bounds);
- dirtyLayerUnchecked(bounds, getRegion());
- }
+ hOffset, vOffset, hasLayer() ? &bounds : nullptr, &functor)) {
+ dirtyLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, *currentTransform());
+ mDirty = true;
}
-
- return DrawGlInfo::kStatusDrew;
}
-status_t OpenGLRenderer::drawPath(const SkPath* path, const SkPaint* paint) {
- if (currentSnapshot()->isIgnored()) return DrawGlInfo::kStatusDone;
+void OpenGLRenderer::drawPath(const SkPath* path, const SkPaint* paint) {
+ if (mState.currentlyIgnored()) return;
- mCaches.activeTexture(0);
+ mCaches.textureState().activateTexture(0);
- const PathTexture* texture = mCaches.pathCache.get(path, paint);
- if (!texture) return DrawGlInfo::kStatusDone;
+ PathTexture* texture = mCaches.pathCache.get(path, paint);
+ if (!texture) return;
const AutoTexture autoCleanup(texture);
const float x = texture->left - texture->offset;
const float y = texture->top - texture->offset;
drawPathTexture(texture, x, y, paint);
-
- return DrawGlInfo::kStatusDrew;
+ mDirty = true;
}
-status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y) {
+void OpenGLRenderer::drawLayer(Layer* layer, float x, float y) {
if (!layer) {
- return DrawGlInfo::kStatusDone;
+ return;
}
- mat4* transform = NULL;
+ mat4* transform = nullptr;
if (layer->isTextureLayer()) {
transform = &layer->getTransform();
if (!transform->isIdentity()) {
@@ -2900,14 +2294,15 @@ status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y) {
}
bool clipRequired = false;
- const bool rejected = calculateQuickRejectForScissor(x, y,
- x + layer->layer.getWidth(), y + layer->layer.getHeight(), &clipRequired, NULL, false);
+ const bool rejected = mState.calculateQuickRejectForScissor(
+ x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight(),
+ &clipRequired, nullptr, false);
if (rejected) {
if (transform && !transform->isIdentity()) {
restore();
}
- return DrawGlInfo::kStatusDone;
+ return;
}
EVENT_LOGD("drawLayer," RECT_STRING ", clipRequired %d", x, y,
@@ -2915,54 +2310,23 @@ status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y) {
updateLayer(layer, true);
- mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired);
- mCaches.activeTexture(0);
+ mRenderState.scissor().setEnabled(mScissorOptimizationDisabled || clipRequired);
+ mCaches.textureState().activateTexture(0);
if (CC_LIKELY(!layer->region.isEmpty())) {
if (layer->region.isRect()) {
DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate,
composeLayerRect(layer, layer->regionRect));
} else if (layer->mesh) {
-
- const float a = getLayerAlpha(layer);
- setupDraw();
- setupDrawWithTexture();
- setupDrawColor(a, a, a, a);
- setupDrawColorFilter(layer->getColorFilter());
- setupDrawBlending(layer);
- setupDrawProgram();
- setupDrawPureColorUniforms();
- setupDrawColorFilterUniforms(layer->getColorFilter());
- setupDrawTexture(layer->getTexture());
- if (CC_LIKELY(currentTransform()->isPureTranslate())) {
- int tx = (int) floorf(x + currentTransform()->getTranslateX() + 0.5f);
- int ty = (int) floorf(y + currentTransform()->getTranslateY() + 0.5f);
-
- layer->setFilter(GL_NEAREST);
- setupDrawModelView(kModelViewMode_Translate, false, tx, ty,
- tx + layer->layer.getWidth(), ty + layer->layer.getHeight(), true);
- } else {
- layer->setFilter(GL_LINEAR);
- setupDrawModelView(kModelViewMode_Translate, false, x, y,
- x + layer->layer.getWidth(), y + layer->layer.getHeight());
- }
-
- TextureVertex* mesh = &layer->mesh[0];
- GLsizei elementsCount = layer->meshElementCount;
-
- while (elementsCount > 0) {
- GLsizei drawCount = min(elementsCount, (GLsizei) gMaxNumberOfQuads * 6);
-
- setupDrawMeshIndices(&mesh[0].x, &mesh[0].u);
- DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate,
- glDrawElements(GL_TRIANGLES, drawCount, GL_UNSIGNED_SHORT, NULL));
-
- elementsCount -= drawCount;
- // Though there are 4 vertices in a quad, we use 6 indices per
- // quad to draw with GL_TRIANGLES
- mesh += (drawCount / 6) * 4;
- }
-
+ Glop glop;
+ GlopBuilder(mRenderState, mCaches, &glop)
+ .setMeshTexturedIndexedQuads(layer->mesh, layer->meshElementCount)
+ .setFillLayer(layer->getTexture(), layer->getColorFilter(), getLayerAlpha(layer), layer->getMode(), Blend::ModeOrderSwap::NoSwap)
+ .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false)
+ .setModelViewOffsetRectSnap(x, y, Rect(0, 0, layer->layer.getWidth(), layer->layer.getHeight()))
+ .setRoundRectClipState(currentSnapshot()->roundRectClipState)
+ .build();
+ DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate, renderGlop(glop));
#if DEBUG_LAYERS_AS_REGIONS
drawRegionRectsDebug(layer->region);
#endif
@@ -2982,53 +2346,16 @@ status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y) {
restore();
}
- return DrawGlInfo::kStatusDrew;
+ mDirty = true;
}
///////////////////////////////////////////////////////////////////////////////
// Draw filters
///////////////////////////////////////////////////////////////////////////////
-
-void OpenGLRenderer::resetPaintFilter() {
- // when clearing the PaintFilter, the masks should also be cleared for simple DrawModifier
- // comparison, see MergingDrawBatch::canMergeWith
- mDrawModifiers.mHasDrawFilter = false;
- mDrawModifiers.mPaintFilterClearBits = 0;
- mDrawModifiers.mPaintFilterSetBits = 0;
-}
-
-void OpenGLRenderer::setupPaintFilter(int clearBits, int setBits) {
- // TODO: don't bother with boolean, it's redundant with clear/set bits
- mDrawModifiers.mHasDrawFilter = true;
- mDrawModifiers.mPaintFilterClearBits = clearBits & SkPaint::kAllFlags;
- mDrawModifiers.mPaintFilterSetBits = setBits & SkPaint::kAllFlags;
-}
-
-const SkPaint* OpenGLRenderer::filterPaint(const SkPaint* paint) {
- // TODO: use CompatFlagsDrawFilter here, and combine logic with android/graphics/DrawFilter.cpp
- // to avoid clobbering 0x02 paint flag
-
- // Equivalent to the Java Paint's FILTER_BITMAP_FLAG.
- static const uint32_t sFilterBitmapFlag = 0x02;
-
- if (CC_LIKELY(!mDrawModifiers.mHasDrawFilter || !paint)) {
- return paint;
- }
-
- const uint32_t clearBits = mDrawModifiers.mPaintFilterClearBits;
- const uint32_t setBits = mDrawModifiers.mPaintFilterSetBits;
-
- const uint32_t flags = (paint->getFlags() & ~clearBits) | setBits;
- mFilteredPaint = *paint;
- mFilteredPaint.setFlags(flags);
-
- // check if paint filter trying to override bitmap filter
- if ((clearBits | setBits) & sFilterBitmapFlag) {
- mFilteredPaint.setFilterLevel(flags & sFilterBitmapFlag
- ? SkPaint::kLow_FilterLevel : SkPaint::kNone_FilterLevel);
- }
-
- return &mFilteredPaint;
+void OpenGLRenderer::setDrawFilter(SkDrawFilter* filter) {
+ // We should never get here since we apply the draw filter when stashing
+ // the paints in the DisplayList.
+ LOG_ALWAYS_FATAL("OpenGLRenderer does not directly support DrawFilters");
}
///////////////////////////////////////////////////////////////////////////////
@@ -3043,32 +2370,21 @@ Texture* OpenGLRenderer::getTexture(const SkBitmap* bitmap) {
return texture;
}
-void OpenGLRenderer::drawPathTexture(const PathTexture* texture,
- float x, float y, const SkPaint* paint) {
+void OpenGLRenderer::drawPathTexture(PathTexture* texture, float x, float y,
+ const SkPaint* paint) {
if (quickRejectSetupScissor(x, y, x + texture->width, y + texture->height)) {
return;
}
- int alpha;
- SkXfermode::Mode mode;
- getAlphaAndMode(paint, &alpha, &mode);
-
- setupDraw();
- setupDrawWithTexture(true);
- setupDrawAlpha8Color(paint->getColor(), alpha);
- setupDrawColorFilter(getColorFilter(paint));
- setupDrawShader(getShader(paint));
- setupDrawBlending(paint, true);
- setupDrawProgram();
- setupDrawModelView(kModelViewMode_TranslateAndScale, false,
- x, y, x + texture->width, y + texture->height);
- setupDrawTexture(texture->id);
- setupDrawPureColorUniforms();
- setupDrawColorFilterUniforms(getColorFilter(paint));
- setupDrawShaderUniforms(getShader(paint));
- setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset);
-
- glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
+ Glop glop;
+ GlopBuilder(mRenderState, mCaches, &glop)
+ .setMeshTexturedUnitQuad(nullptr)
+ .setFillPathTexturePaint(*texture, *paint, currentSnapshot()->alpha)
+ .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false)
+ .setModelViewMapUnitToRect(Rect(x, y, x + texture->width, y + texture->height))
+ .setRoundRectClipState(currentSnapshot()->roundRectClipState)
+ .build();
+ renderGlop(glop);
}
// Same values used by Skia
@@ -3121,28 +2437,20 @@ void OpenGLRenderer::drawTextDecorations(float underlineWidth, float x, float y,
}
}
-status_t OpenGLRenderer::drawRects(const float* rects, int count, const SkPaint* paint) {
- if (currentSnapshot()->isIgnored()) {
- return DrawGlInfo::kStatusDone;
+void OpenGLRenderer::drawRects(const float* rects, int count, const SkPaint* paint) {
+ if (mState.currentlyIgnored()) {
+ return;
}
- return drawColorRects(rects, count, paint, false, true, true);
+ drawColorRects(rects, count, paint, false, true, true);
}
-static void mapPointFakeZ(Vector3& point, const mat4& transformXY, const mat4& transformZ) {
- // map z coordinate with true 3d matrix
- point.z = transformZ.mapZ(point);
-
- // map x,y coordinates with draw/Skia matrix
- transformXY.mapPoint(point.x, point.y);
-}
-
-status_t OpenGLRenderer::drawShadow(float casterAlpha,
+void OpenGLRenderer::drawShadow(float casterAlpha,
const VertexBuffer* ambientShadowVertexBuffer, const VertexBuffer* spotShadowVertexBuffer) {
- if (currentSnapshot()->isIgnored()) return DrawGlInfo::kStatusDone;
+ if (mState.currentlyIgnored()) return;
// TODO: use quickRejectWithScissor. For now, always force enable scissor.
- mCaches.enableScissor();
+ mRenderState.scissor().setEnabled(true);
SkPaint paint;
paint.setAntiAlias(true); // want to use AlphaVertex
@@ -3166,19 +2474,13 @@ status_t OpenGLRenderer::drawShadow(float casterAlpha,
drawVertexBuffer(*spotShadowVertexBuffer, &paint, kVertexBuffer_ShadowInterp);
}
- return DrawGlInfo::kStatusDrew;
+ mDirty=true;
}
-status_t OpenGLRenderer::drawColorRects(const float* rects, int count, const SkPaint* paint,
+void OpenGLRenderer::drawColorRects(const float* rects, int count, const SkPaint* paint,
bool ignoreTransform, bool dirty, bool clip) {
if (count == 0) {
- return DrawGlInfo::kStatusDone;
- }
-
- int color = paint->getColor();
- // If a shader is set, preserve only the alpha
- if (getShader(paint)) {
- color |= 0x00ffffff;
+ return;
}
float left = FLT_MAX;
@@ -3207,247 +2509,37 @@ status_t OpenGLRenderer::drawColorRects(const float* rects, int count, const SkP
}
if (clip && quickRejectSetupScissor(left, top, right, bottom)) {
- return DrawGlInfo::kStatusDone;
- }
-
- setupDraw();
- setupDrawNoTexture();
- setupDrawColor(color, ((color >> 24) & 0xFF) * currentSnapshot()->alpha);
- setupDrawShader(getShader(paint));
- setupDrawColorFilter(getColorFilter(paint));
- setupDrawBlending(paint);
- setupDrawProgram();
- setupDrawDirtyRegionsDisabled();
- setupDrawModelView(kModelViewMode_Translate, false,
- 0.0f, 0.0f, 0.0f, 0.0f, ignoreTransform);
- setupDrawColorUniforms(getShader(paint));
- setupDrawShaderUniforms(getShader(paint));
- setupDrawColorFilterUniforms(getColorFilter(paint));
-
- if (dirty && hasLayer()) {
- dirtyLayer(left, top, right, bottom, *currentTransform());
+ return;
}
- issueIndexedQuadDraw(&mesh[0], count / 4);
-
- return DrawGlInfo::kStatusDrew;
+ const Matrix4& transform = ignoreTransform ? Matrix4::identity() : *currentTransform();
+ Glop glop;
+ GlopBuilder(mRenderState, mCaches, &glop)
+ .setMeshIndexedQuads(&mesh[0], count / 4)
+ .setFillPaint(*paint, currentSnapshot()->alpha)
+ .setTransform(currentSnapshot()->getOrthoMatrix(), transform, false)
+ .setModelViewOffsetRect(0, 0, Rect(left, top, right, bottom))
+ .setRoundRectClipState(currentSnapshot()->roundRectClipState)
+ .build();
+ renderGlop(glop);
}
void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom,
const SkPaint* paint, bool ignoreTransform) {
- int color = paint->getColor();
- // If a shader is set, preserve only the alpha
- if (getShader(paint)) {
- color |= 0x00ffffff;
- }
-
- setupDraw();
- setupDrawNoTexture();
- setupDrawColor(color, ((color >> 24) & 0xFF) * currentSnapshot()->alpha);
- setupDrawShader(getShader(paint));
- setupDrawColorFilter(getColorFilter(paint));
- setupDrawBlending(paint);
- setupDrawProgram();
- setupDrawModelView(kModelViewMode_TranslateAndScale, false,
- left, top, right, bottom, ignoreTransform);
- setupDrawColorUniforms(getShader(paint));
- setupDrawShaderUniforms(getShader(paint), ignoreTransform);
- setupDrawColorFilterUniforms(getColorFilter(paint));
- setupDrawSimpleMesh();
-
- glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
-}
-
-void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom,
- Texture* texture, const SkPaint* paint) {
- texture->setWrap(GL_CLAMP_TO_EDGE, true);
-
- GLvoid* vertices = (GLvoid*) NULL;
- GLvoid* texCoords = (GLvoid*) gMeshTextureOffset;
-
- if (texture->uvMapper) {
- vertices = &mMeshVertices[0].x;
- texCoords = &mMeshVertices[0].u;
-
- Rect uvs(0.0f, 0.0f, 1.0f, 1.0f);
- texture->uvMapper->map(uvs);
-
- resetDrawTextureTexCoords(uvs.left, uvs.top, uvs.right, uvs.bottom);
- }
-
- if (CC_LIKELY(currentTransform()->isPureTranslate())) {
- const float x = (int) floorf(left + currentTransform()->getTranslateX() + 0.5f);
- const float y = (int) floorf(top + currentTransform()->getTranslateY() + 0.5f);
-
- texture->setFilter(GL_NEAREST, true);
- drawTextureMesh(x, y, x + texture->width, y + texture->height, texture->id,
- paint, texture->blend, vertices, texCoords,
- GL_TRIANGLE_STRIP, gMeshCount, false, true);
- } else {
- texture->setFilter(getFilter(paint), true);
- drawTextureMesh(left, top, right, bottom, texture->id, paint,
- texture->blend, vertices, texCoords, GL_TRIANGLE_STRIP, gMeshCount);
- }
-
- if (texture->uvMapper) {
- resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
- }
-}
-
-void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float bottom,
- GLuint texture, const SkPaint* paint, bool blend,
- GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
- bool swapSrcDst, bool ignoreTransform, GLuint vbo,
- ModelViewMode modelViewMode, bool dirty) {
-
- int a;
- SkXfermode::Mode mode;
- getAlphaAndMode(paint, &a, &mode);
- const float alpha = a / 255.0f;
-
- setupDraw();
- setupDrawWithTexture();
- setupDrawColor(alpha, alpha, alpha, alpha);
- setupDrawColorFilter(getColorFilter(paint));
- setupDrawBlending(paint, blend, swapSrcDst);
- setupDrawProgram();
- if (!dirty) setupDrawDirtyRegionsDisabled();
- setupDrawModelView(modelViewMode, false, left, top, right, bottom, ignoreTransform);
- setupDrawTexture(texture);
- setupDrawPureColorUniforms();
- setupDrawColorFilterUniforms(getColorFilter(paint));
- setupDrawMesh(vertices, texCoords, vbo);
-
- glDrawArrays(drawMode, 0, elementsCount);
-}
-
-void OpenGLRenderer::drawIndexedTextureMesh(float left, float top, float right, float bottom,
- GLuint texture, const SkPaint* paint, bool blend,
- GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
- bool swapSrcDst, bool ignoreTransform, GLuint vbo,
- ModelViewMode modelViewMode, bool dirty) {
-
- int a;
- SkXfermode::Mode mode;
- getAlphaAndMode(paint, &a, &mode);
- const float alpha = a / 255.0f;
-
- setupDraw();
- setupDrawWithTexture();
- setupDrawColor(alpha, alpha, alpha, alpha);
- setupDrawColorFilter(getColorFilter(paint));
- setupDrawBlending(paint, blend, swapSrcDst);
- setupDrawProgram();
- if (!dirty) setupDrawDirtyRegionsDisabled();
- setupDrawModelView(modelViewMode, false, left, top, right, bottom, ignoreTransform);
- setupDrawTexture(texture);
- setupDrawPureColorUniforms();
- setupDrawColorFilterUniforms(getColorFilter(paint));
- setupDrawMeshIndices(vertices, texCoords, vbo);
-
- glDrawElements(drawMode, elementsCount, GL_UNSIGNED_SHORT, NULL);
-}
-
-void OpenGLRenderer::drawAlpha8TextureMesh(float left, float top, float right, float bottom,
- GLuint texture, const SkPaint* paint,
- GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
- bool ignoreTransform, ModelViewMode modelViewMode, bool dirty) {
-
- int color = paint != NULL ? paint->getColor() : 0;
- int alpha;
- SkXfermode::Mode mode;
- getAlphaAndMode(paint, &alpha, &mode);
-
- setupDraw();
- setupDrawWithTexture(true);
- if (paint != NULL) {
- setupDrawAlpha8Color(color, alpha);
- }
- setupDrawColorFilter(getColorFilter(paint));
- setupDrawShader(getShader(paint));
- setupDrawBlending(paint, true);
- setupDrawProgram();
- if (!dirty) setupDrawDirtyRegionsDisabled();
- setupDrawModelView(modelViewMode, false, left, top, right, bottom, ignoreTransform);
- setupDrawTexture(texture);
- setupDrawPureColorUniforms();
- setupDrawColorFilterUniforms(getColorFilter(paint));
- setupDrawShaderUniforms(getShader(paint), ignoreTransform);
- setupDrawMesh(vertices, texCoords);
-
- glDrawArrays(drawMode, 0, elementsCount);
-}
-
-void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode,
- ProgramDescription& description, bool swapSrcDst) {
-
- if (mSnapshot->roundRectClipState != NULL /*&& !mSkipOutlineClip*/) {
- blend = true;
- mDescription.hasRoundRectClip = true;
- }
- mSkipOutlineClip = true;
-
- blend = blend || mode != SkXfermode::kSrcOver_Mode;
-
- if (blend) {
- // These blend modes are not supported by OpenGL directly and have
- // to be implemented using shaders. Since the shader will perform
- // the blending, turn blending off here
- // If the blend mode cannot be implemented using shaders, fall
- // back to the default SrcOver blend mode instead
- if (CC_UNLIKELY(mode > SkXfermode::kScreen_Mode)) {
- if (CC_UNLIKELY(mExtensions.hasFramebufferFetch())) {
- description.framebufferMode = mode;
- description.swapSrcDst = swapSrcDst;
-
- if (mCaches.blend) {
- glDisable(GL_BLEND);
- mCaches.blend = false;
- }
-
- return;
- } else {
- mode = SkXfermode::kSrcOver_Mode;
- }
- }
-
- if (!mCaches.blend) {
- glEnable(GL_BLEND);
- }
-
- GLenum sourceMode = swapSrcDst ? gBlendsSwap[mode].src : gBlends[mode].src;
- GLenum destMode = swapSrcDst ? gBlendsSwap[mode].dst : gBlends[mode].dst;
-
- if (sourceMode != mCaches.lastSrcMode || destMode != mCaches.lastDstMode) {
- glBlendFunc(sourceMode, destMode);
- mCaches.lastSrcMode = sourceMode;
- mCaches.lastDstMode = destMode;
- }
- } else if (mCaches.blend) {
- glDisable(GL_BLEND);
- }
- mCaches.blend = blend;
-}
-
-bool OpenGLRenderer::useProgram(Program* program) {
- if (!program->isInUse()) {
- if (mCaches.currentProgram != NULL) mCaches.currentProgram->remove();
- program->use();
- mCaches.currentProgram = program;
- return false;
- }
- return true;
-}
-
-void OpenGLRenderer::resetDrawTextureTexCoords(float u1, float v1, float u2, float v2) {
- TextureVertex* v = &mMeshVertices[0];
- TextureVertex::setUV(v++, u1, v1);
- TextureVertex::setUV(v++, u2, v1);
- TextureVertex::setUV(v++, u1, v2);
- TextureVertex::setUV(v++, u2, v2);
-}
-
-void OpenGLRenderer::getAlphaAndMode(const SkPaint* paint, int* alpha, SkXfermode::Mode* mode) const {
+ const Matrix4& transform = ignoreTransform ? Matrix4::identity() : *currentTransform();
+ Glop glop;
+ GlopBuilder(mRenderState, mCaches, &glop)
+ .setMeshUnitQuad()
+ .setFillPaint(*paint, currentSnapshot()->alpha)
+ .setTransform(currentSnapshot()->getOrthoMatrix(), transform, false)
+ .setModelViewMapUnitToRect(Rect(left, top, right, bottom))
+ .setRoundRectClipState(currentSnapshot()->roundRectClipState)
+ .build();
+ renderGlop(glop);
+}
+
+void OpenGLRenderer::getAlphaAndMode(const SkPaint* paint, int* alpha,
+ SkXfermode::Mode* mode) const {
getAlphaAndModeDirect(paint, alpha, mode);
if (mDrawModifiers.mOverrideLayerAlpha < 1.0f) {
// if drawing a layer, ignore the paint's alpha
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 5eee2e2..5f8960a 100755
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -17,6 +17,18 @@
#ifndef ANDROID_HWUI_OPENGL_RENDERER_H
#define ANDROID_HWUI_OPENGL_RENDERER_H
+#include "CanvasState.h"
+#include "Debug.h"
+#include "Extensions.h"
+#include "Matrix.h"
+#include "Program.h"
+#include "Rect.h"
+#include "Snapshot.h"
+#include "UvMapper.h"
+#include "Vertex.h"
+#include "Caches.h"
+#include "utils/PaintUtils.h"
+
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
@@ -38,45 +50,33 @@
#include <androidfw/ResourceTypes.h>
-#include "Debug.h"
-#include "Extensions.h"
-#include "Matrix.h"
-#include "Program.h"
-#include "Rect.h"
-#include "Renderer.h"
-#include "Snapshot.h"
-#include "StatefulBaseRenderer.h"
-#include "UvMapper.h"
-#include "Vertex.h"
-#include "Caches.h"
-#include "CanvasProperty.h"
-
class SkShader;
namespace android {
namespace uirenderer {
+enum class DrawOpMode {
+ kImmediate,
+ kDefer,
+ kFlush
+};
+
class DeferredDisplayState;
+struct Glop;
class RenderState;
class RenderNode;
-class TextSetupFunctor;
+class TextDrawFunctor;
class VertexBuffer;
struct DrawModifiers {
- DrawModifiers() {
- reset();
- }
+ DrawModifiers()
+ : mOverrideLayerAlpha(0.0f) {}
void reset() {
- memset(this, 0, sizeof(DrawModifiers));
+ mOverrideLayerAlpha = 0.0f;
}
float mOverrideLayerAlpha;
-
- // Draw filters
- bool mHasDrawFilter;
- int mPaintFilterClearBits;
- int mPaintFilterSetBits;
};
enum StateDeferFlags {
@@ -123,20 +123,59 @@ enum ModelViewMode {
/**
* OpenGL Renderer implementation.
*/
-class OpenGLRenderer : public StatefulBaseRenderer {
+class OpenGLRenderer : public CanvasStateClient {
public:
OpenGLRenderer(RenderState& renderState);
virtual ~OpenGLRenderer();
+ /**
+ * Sets the dimension of the underlying drawing surface. This method must
+ * be called at least once every time the drawing surface changes size.
+ *
+ * @param width The width in pixels of the underlysing surface
+ * @param height The height in pixels of the underlysing surface
+ */
+ void setViewport(int width, int height) { mState.setViewport(width, height); }
+
void initProperties();
void initLight(const Vector3& lightCenter, float lightRadius,
uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
- virtual void onViewportInitialized();
- virtual status_t prepareDirty(float left, float top, float right, float bottom, bool opaque);
- virtual void finish();
+ /*
+ * Prepares the renderer to draw a frame. This method must be invoked
+ * at the beginning of each frame. Only the specified rectangle of the
+ * frame is assumed to be dirty. A clip will automatically be set to
+ * the specified rectangle.
+ *
+ * @param opaque If true, the target surface is considered opaque
+ * and will not be cleared. If false, the target surface
+ * will be cleared
+ */
+ virtual void prepareDirty(float left, float top, float right, float bottom,
+ bool opaque);
- virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty);
+ /**
+ * Prepares the renderer to draw a frame. This method must be invoked
+ * at the beginning of each frame. When this method is invoked, the
+ * entire drawing surface is assumed to be redrawn.
+ *
+ * @param opaque If true, the target surface is considered opaque
+ * and will not be cleared. If false, the target surface
+ * will be cleared
+ */
+ void prepare(bool opaque) {
+ prepareDirty(0.0f, 0.0f, mState.getWidth(), mState.getHeight(), opaque);
+ }
+
+ /**
+ * Indicates the end of a frame. This method must be invoked whenever
+ * the caller is done rendering a frame.
+ * Returns true if any drawing was done during the frame (the output
+ * has changed / is "dirty" and should be displayed to the user).
+ */
+ virtual bool finish();
+
+ void callDrawGLFunction(Functor* functor, Rect& dirty);
void pushLayerUpdate(Layer* layer);
void cancelLayerUpdate(Layer* layer);
@@ -145,7 +184,7 @@ public:
virtual int saveLayer(float left, float top, float right, float bottom,
const SkPaint* paint, int flags) {
- return saveLayer(left, top, right, bottom, paint, flags, NULL);
+ return saveLayer(left, top, right, bottom, paint, flags, nullptr);
}
// Specialized saveLayer implementation, which will pass the convexMask to an FBO layer, if
@@ -156,56 +195,50 @@ public:
int saveLayerDeferred(float left, float top, float right, float bottom,
const SkPaint* paint, int flags);
- virtual status_t drawRenderNode(RenderNode* displayList, Rect& dirty, int32_t replayFlags = 1);
- virtual status_t drawLayer(Layer* layer, float x, float y);
- virtual status_t drawBitmap(const SkBitmap* bitmap, const SkPaint* paint);
- status_t drawBitmaps(const SkBitmap* bitmap, AssetAtlas::Entry* entry, int bitmapCount,
+ void drawRenderNode(RenderNode* displayList, Rect& dirty, int32_t replayFlags = 1);
+ void drawLayer(Layer* layer, float x, float y);
+ void drawBitmap(const SkBitmap* bitmap, const SkPaint* paint);
+ void drawBitmaps(const SkBitmap* bitmap, AssetAtlas::Entry* entry, int bitmapCount,
TextureVertex* vertices, bool pureTranslate, const Rect& bounds, const SkPaint* paint);
- virtual status_t drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop,
- float srcRight, float srcBottom, float dstLeft, float dstTop,
- float dstRight, float dstBottom, const SkPaint* paint);
- virtual status_t drawBitmapData(const SkBitmap* bitmap, const SkPaint* paint);
- virtual status_t drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight,
+ void drawBitmap(const SkBitmap* bitmap, Rect src, Rect dst,
+ const SkPaint* paint);
+ void drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight,
const float* vertices, const int* colors, const SkPaint* paint);
- status_t drawPatches(const SkBitmap* bitmap, AssetAtlas::Entry* entry,
+ void drawPatches(const SkBitmap* bitmap, AssetAtlas::Entry* entry,
TextureVertex* vertices, uint32_t indexCount, const SkPaint* paint);
- virtual status_t drawPatch(const SkBitmap* bitmap, const Res_png_9patch* patch,
+ void drawPatch(const SkBitmap* bitmap, const Patch* mesh, AssetAtlas::Entry* entry,
float left, float top, float right, float bottom, const SkPaint* paint);
- status_t drawPatch(const SkBitmap* bitmap, const Patch* mesh, AssetAtlas::Entry* entry,
- float left, float top, float right, float bottom, const SkPaint* paint);
- virtual status_t drawColor(int color, SkXfermode::Mode mode);
- virtual status_t drawRect(float left, float top, float right, float bottom,
+ void drawColor(int color, SkXfermode::Mode mode);
+ void drawRect(float left, float top, float right, float bottom,
const SkPaint* paint);
- virtual status_t drawRoundRect(float left, float top, float right, float bottom,
+ void drawRoundRect(float left, float top, float right, float bottom,
float rx, float ry, const SkPaint* paint);
- virtual status_t drawCircle(float x, float y, float radius, const SkPaint* paint);
- virtual status_t drawOval(float left, float top, float right, float bottom,
+ void drawCircle(float x, float y, float radius, const SkPaint* paint);
+ void drawOval(float left, float top, float right, float bottom,
const SkPaint* paint);
- virtual status_t drawArc(float left, float top, float right, float bottom,
+ void drawArc(float left, float top, float right, float bottom,
float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint);
- virtual status_t drawPath(const SkPath* path, const SkPaint* paint);
- virtual status_t drawLines(const float* points, int count, const SkPaint* paint);
- virtual status_t drawPoints(const float* points, int count, const SkPaint* paint);
- virtual status_t drawTextOnPath(const char* text, int bytesCount, int count, const SkPath* path,
+ void drawPath(const SkPath* path, const SkPaint* paint);
+ void drawLines(const float* points, int count, const SkPaint* paint);
+ void drawPoints(const float* points, int count, const SkPaint* paint);
+ void drawTextOnPath(const char* text, int bytesCount, int count, const SkPath* path,
float hOffset, float vOffset, const SkPaint* paint);
- virtual status_t drawPosText(const char* text, int bytesCount, int count,
+ void drawPosText(const char* text, int bytesCount, int count,
const float* positions, const SkPaint* paint);
- virtual status_t drawText(const char* text, int bytesCount, int count, float x, float y,
+ void drawText(const char* text, int bytesCount, int count, float x, float y,
const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds,
- DrawOpMode drawOpMode = kDrawOpMode_Immediate);
- virtual status_t drawRects(const float* rects, int count, const SkPaint* paint);
+ DrawOpMode drawOpMode = DrawOpMode::kImmediate);
+ void drawRects(const float* rects, int count, const SkPaint* paint);
- status_t drawShadow(float casterAlpha,
- const VertexBuffer* ambientShadowVertexBuffer, const VertexBuffer* spotShadowVertexBuffer);
+ void drawShadow(float casterAlpha,
+ const VertexBuffer* ambientShadowVertexBuffer,
+ const VertexBuffer* spotShadowVertexBuffer);
- virtual void resetPaintFilter();
- virtual void setupPaintFilter(int clearBits, int setBits);
+ void setDrawFilter(SkDrawFilter* filter);
// If this value is set to < 1.0, it overrides alpha set on layer (see drawBitmap, drawLayer)
void setOverrideLayerAlpha(float alpha) { mDrawModifiers.mOverrideLayerAlpha = alpha; }
- const SkPaint* filterPaint(const SkPaint* paint);
-
/**
* Store the current display state (most importantly, the current clip and transform), and
* additionally map the state's bounds from local to window coordinates.
@@ -227,21 +260,18 @@ public:
return mCaches;
}
- // simple rect clip
- bool isCurrentClipSimple() {
- return mSnapshot->clipRegion->isEmpty();
+ RenderState& renderState() {
+ return mRenderState;
}
- int getViewportWidth() { return currentSnapshot()->getViewportWidth(); }
- int getViewportHeight() { return currentSnapshot()->getViewportHeight(); }
+ int getViewportWidth() { return mState.getViewportWidth(); }
+ int getViewportHeight() { return mState.getViewportHeight(); }
/**
* Scales the alpha on the current snapshot. This alpha value will be modulated
* with other alpha values when drawing primitives.
*/
- void scaleAlpha(float alpha) {
- mSnapshot->alpha *= alpha;
- }
+ void scaleAlpha(float alpha) { mState.scaleAlpha(alpha); }
/**
* Inserts a named event marker in the stream of GL commands.
@@ -275,14 +305,15 @@ public:
* @param alpha Where to store the resulting alpha
* @param mode Where to store the resulting xfermode
*/
- static inline void getAlphaAndModeDirect(const SkPaint* paint, int* alpha, SkXfermode::Mode* mode) {
+ static inline void getAlphaAndModeDirect(const SkPaint* paint, int* alpha,
+ SkXfermode::Mode* mode) {
*mode = getXfermodeDirect(paint);
*alpha = getAlphaDirect(paint);
}
static inline SkXfermode::Mode getXfermodeDirect(const SkPaint* paint) {
if (!paint) return SkXfermode::kSrcOver_Mode;
- return getXfermode(paint->getXfermode());
+ return PaintUtils::getXfermode(paint->getXfermode());
}
static inline int getAlphaDirect(const SkPaint* paint) {
@@ -312,7 +343,7 @@ public:
}
static inline bool hasTextShadow(const SkPaint* paint) {
- return getTextShadow(paint, NULL);
+ return getTextShadow(paint, nullptr);
}
/**
@@ -334,18 +365,72 @@ public:
drawColorRect(left, top, right, bottom, color, SkXfermode::kSrcOver_Mode, true);
if (stencilWasEnabled) mCaches.stencil.enableTest();
+ mDirty = true;
}
#endif
- const Vector3& getLightCenter() const { return currentSnapshot()->getRelativeLightCenter(); }
+ const Vector3& getLightCenter() const { return mState.currentLightCenter(); }
float getLightRadius() const { return mLightRadius; }
uint8_t getAmbientShadowAlpha() const { return mAmbientShadowAlpha; }
uint8_t getSpotShadowAlpha() const { return mSpotShadowAlpha; }
+ ///////////////////////////////////////////////////////////////////
+ /// State manipulation
+
+ int getSaveCount() const;
+ int save(int flags);
+ void restore();
+ void restoreToCount(int saveCount);
+
+ void getMatrix(SkMatrix* outMatrix) const { mState.getMatrix(outMatrix); }
+ void setMatrix(const SkMatrix& matrix) { mState.setMatrix(matrix); }
+ void concatMatrix(const SkMatrix& matrix) { mState.concatMatrix(matrix); }
+
+ void translate(float dx, float dy, float dz = 0.0f);
+ void rotate(float degrees);
+ void scale(float sx, float sy);
+ void skew(float sx, float sy);
+
+ void setMatrix(const Matrix4& matrix); // internal only convenience method
+ void concatMatrix(const Matrix4& matrix); // internal only convenience method
+
+ const Rect& getLocalClipBounds() const { return mState.getLocalClipBounds(); }
+ const Rect& getRenderTargetClipBounds() const { return mState.getRenderTargetClipBounds(); }
+ bool quickRejectConservative(float left, float top,
+ float right, float bottom) const {
+ return mState.quickRejectConservative(left, top, right, bottom);
+ }
+
+ bool clipRect(float left, float top,
+ float right, float bottom, SkRegion::Op op);
+ bool clipPath(const SkPath* path, SkRegion::Op op);
+ bool clipRegion(const SkRegion* region, SkRegion::Op op);
+
+ /**
+ * Does not support different clipping Ops (that is, every call to setClippingOutline is
+ * effectively using SkRegion::kReplaceOp)
+ *
+ * The clipping outline is independent from the regular clip.
+ */
+ void setClippingOutline(LinearAllocator& allocator, const Outline* outline);
+ void setClippingRoundRect(LinearAllocator& allocator,
+ const Rect& rect, float radius, bool highPriority = true);
+
+ inline bool hasRectToRectTransform() const { return mState.hasRectToRectTransform(); }
+ inline const mat4* currentTransform() const { return mState.currentTransform(); }
+
+ ///////////////////////////////////////////////////////////////////
+ /// CanvasStateClient interface
+
+ virtual void onViewportInitialized() override;
+ virtual void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) override;
+ virtual GLuint getTargetFbo() const override { return 0; }
+
SkPath* allocPathForFrame() {
- SkPath* path = new SkPath();
- mTempPaths.push_back(path);
- return path;
+ std::unique_ptr<SkPath> path(new SkPath());
+ SkPath* returnPath = path.get();
+ mTempPaths.push_back(std::move(path));
+ return returnPath;
}
protected:
@@ -359,12 +444,12 @@ protected:
* Indicates the start of rendering. This method will setup the
* initial OpenGL state (viewport, clearing the buffer, etc.)
*/
- status_t startFrame();
+ void startFrame();
/**
* Clears the underlying surface if needed.
*/
- virtual status_t clear(float left, float top, float right, float bottom, bool opaque);
+ virtual void clear(float left, float top, float right, float bottom, bool opaque);
/**
* Call this method after updating a layer during a drawing pass.
@@ -384,9 +469,16 @@ protected:
*/
void attachStencilBufferToLayer(Layer* layer);
+ /**
+ * Draw a rectangle list. Currently only used for the the stencil buffer so that the stencil
+ * will have a value of 'n' in every unclipped pixel, where 'n' is the number of rectangles
+ * in the list.
+ */
+ void drawRectangleList(const RectangleList& rectangleList);
+
bool quickRejectSetupScissor(float left, float top, float right, float bottom,
- const SkPaint* paint = NULL);
- bool quickRejectSetupScissor(const Rect& bounds, const SkPaint* paint = NULL) {
+ const SkPaint* paint = nullptr);
+ bool quickRejectSetupScissor(const Rect& bounds, const SkPaint* paint = nullptr) {
return quickRejectSetupScissor(bounds.left, bounds.top,
bounds.right, bounds.bottom, paint);
}
@@ -411,21 +503,14 @@ protected:
* Returns the region of the current layer.
*/
virtual Region* getRegion() const {
- return mSnapshot->region;
+ return mState.currentRegion();
}
/**
* Indicates whether rendering is currently targeted at a layer.
*/
virtual bool hasLayer() const {
- return (mSnapshot->flags & Snapshot::kFlagFboTarget) && mSnapshot->region;
- }
-
- /**
- * Returns the name of the FBO this renderer is rendering into.
- */
- virtual GLuint getTargetFbo() const {
- return 0;
+ return (mState.currentFlags() & Snapshot::kFlagFboTarget) && mState.currentRegion();
}
/**
@@ -459,7 +544,7 @@ protected:
* null then null is returned.
*/
static inline SkColorFilter* getColorFilter(const SkPaint* paint) {
- return paint ? paint->getColorFilter() : NULL;
+ return paint ? paint->getColorFilter() : nullptr;
}
/**
@@ -467,7 +552,7 @@ protected:
* null then null is returned.
*/
static inline const SkShader* getShader(const SkPaint* paint) {
- return paint ? paint->getShader() : NULL;
+ return paint ? paint->getShader() : nullptr;
}
/**
@@ -477,9 +562,13 @@ protected:
return false;
}
- inline RenderState& renderState() { return mRenderState; }
+ CanvasState mState;
+ Caches& mCaches;
+ RenderState& mRenderState;
private:
+ void renderGlop(const Glop& glop, bool clearLayer = true);
+
/**
* Discards the content of the framebuffer if supported by the driver.
* This method should be called at the beginning of a frame to optimize
@@ -488,12 +577,6 @@ private:
void discardFramebuffer(float left, float top, float right, float bottom);
/**
- * Ensures the state of the renderer is the same as the state of
- * the GL context.
- */
- void syncState();
-
- /**
* Tells the GPU what part of the screen is about to be redrawn.
* This method will use the current layer space clip rect.
* This method needs to be invoked every time getTargetFbo() is
@@ -514,8 +597,6 @@ private:
*/
void endTiling();
- void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored);
-
/**
* Sets the clipping rectangle using glScissor. The clip is defined by
* the current snapshot's clipRect member.
@@ -594,7 +675,7 @@ private:
* are transformed with the supplied matrix.
*/
void dirtyLayer(const float left, const float top,
- const float right, const float bottom, const mat4 transform);
+ const float right, const float bottom, const Matrix4& transform);
/**
* Mark the layer as dirty at the specified coordinates.
@@ -629,7 +710,7 @@ private:
* @param dirty True if calling this method should dirty the current layer
* @param clip True if the rects should be clipped, false otherwise
*/
- status_t drawColorRects(const float* rects, int count, const SkPaint* paint,
+ void drawColorRects(const float* rects, int count, const SkPaint* paint,
bool ignoreTransform = false, bool dirty = true, bool clip = true);
/**
@@ -643,18 +724,7 @@ private:
* @param texture The texture reprsenting the shape
* @param paint The paint to draw the shape with
*/
- status_t drawShape(float left, float top, const PathTexture* texture, const SkPaint* paint);
-
- /**
- * Draws the specified texture as an alpha bitmap. Alpha bitmaps obey
- * different compositing rules.
- *
- * @param texture The texture to draw with
- * @param left The x coordinate of the bitmap
- * @param top The y coordinate of the bitmap
- * @param paint The paint to render with
- */
- void drawAlphaBitmap(Texture* texture, float left, float top, const SkPaint* paint);
+ void drawShape(float left, float top, PathTexture* texture, const SkPaint* paint);
/**
* Renders a strip of polygons with the specified paint, used for tessellated geometry.
@@ -663,15 +733,15 @@ private:
* @param paint The paint to render with
* @param flags flags with which to draw
*/
- status_t drawVertexBuffer(float translateX, float translateY, const VertexBuffer& vertexBuffer,
+ void drawVertexBuffer(float translateX, float translateY, const VertexBuffer& vertexBuffer,
const SkPaint* paint, int flags = 0);
/**
* Convenience for translating method
*/
- status_t drawVertexBuffer(const VertexBuffer& vertexBuffer,
+ void drawVertexBuffer(const VertexBuffer& vertexBuffer,
const SkPaint* paint, int flags = 0) {
- return drawVertexBuffer(0.0f, 0.0f, vertexBuffer, paint, flags);
+ drawVertexBuffer(0.0f, 0.0f, vertexBuffer, paint, flags);
}
/**
@@ -680,67 +750,7 @@ private:
* @param path The hull of the path to draw
* @param paint The paint to render with
*/
- status_t drawConvexPath(const SkPath& path, const SkPaint* paint);
-
- /**
- * Draws a textured rectangle with the specified texture. The specified coordinates
- * are transformed by the current snapshot's transform matrix.
- *
- * @param left The left coordinate of the rectangle
- * @param top The top coordinate of the rectangle
- * @param right The right coordinate of the rectangle
- * @param bottom The bottom coordinate of the rectangle
- * @param texture The texture to use
- * @param paint The paint containing the alpha, blending mode, etc.
- */
- void drawTextureRect(float left, float top, float right, float bottom,
- Texture* texture, const SkPaint* paint);
-
- /**
- * Draws a textured mesh with the specified texture. If the indices are omitted,
- * the mesh is drawn as a simple quad. The mesh pointers become offsets when a
- * VBO is bound.
- *
- * @param left The left coordinate of the rectangle
- * @param top The top coordinate of the rectangle
- * @param right The right coordinate of the rectangle
- * @param bottom The bottom coordinate of the rectangle
- * @param texture The texture name to map onto the rectangle
- * @param paint The paint containing the alpha, blending mode, colorFilter, etc.
- * @param blend True if the texture contains an alpha channel
- * @param vertices The vertices that define the mesh
- * @param texCoords The texture coordinates of each vertex
- * @param elementsCount The number of elements in the mesh, required by indices
- * @param swapSrcDst Whether or not the src and dst blending operations should be swapped
- * @param ignoreTransform True if the current transform should be ignored
- * @param vbo The VBO used to draw the mesh
- * @param modelViewMode Defines whether the model view matrix should be scaled
- * @param dirty True if calling this method should dirty the current layer
- */
- void drawTextureMesh(float left, float top, float right, float bottom, GLuint texture,
- const SkPaint* paint, bool blend,
- GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
- bool swapSrcDst = false, bool ignoreTransform = false, GLuint vbo = 0,
- ModelViewMode modelViewMode = kModelViewMode_TranslateAndScale, bool dirty = true);
-
- void drawIndexedTextureMesh(float left, float top, float right, float bottom, GLuint texture,
- const SkPaint* paint, bool blend,
- GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
- bool swapSrcDst = false, bool ignoreTransform = false, GLuint vbo = 0,
- ModelViewMode modelViewMode = kModelViewMode_TranslateAndScale, bool dirty = true);
-
- void drawAlpha8TextureMesh(float left, float top, float right, float bottom,
- GLuint texture, const SkPaint* paint,
- GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
- bool ignoreTransform, ModelViewMode modelViewMode = kModelViewMode_TranslateAndScale,
- bool dirty = true);
-
- /**
- * Draws the specified list of vertices as quads using indexed GL_TRIANGLES.
- * If the number of vertices to draw exceeds the number of indices we have
- * pre-allocated, this method will generate several glDrawElements() calls.
- */
- void issueIndexedQuadDraw(Vertex* mesh, GLsizei quadsCount);
+ void drawConvexPath(const SkPath& path, const SkPaint* paint);
/**
* Draws text underline and strike-through if needed.
@@ -780,7 +790,7 @@ private:
* @param y The y coordinate where the texture will be drawn
* @param paint The paint to draw the texture with
*/
- void drawPathTexture(const PathTexture* texture, float x, float y, const SkPaint* paint);
+ void drawPathTexture(PathTexture* texture, float x, float y, const SkPaint* paint);
/**
* Resets the texture coordinates stored in mMeshVertices. Setting the values
@@ -800,106 +810,6 @@ private:
*/
bool canSkipText(const SkPaint* paint) const;
- /**
- * Binds the specified texture. The texture unit must have been selected
- * prior to calling this method.
- */
- inline void bindTexture(GLuint texture) {
- mCaches.bindTexture(texture);
- }
-
- /**
- * Binds the specified EGLImage texture. The texture unit must have been selected
- * prior to calling this method.
- */
- inline void bindExternalTexture(GLuint texture) {
- mCaches.bindTexture(GL_TEXTURE_EXTERNAL_OES, texture);
- }
-
- /**
- * Enable or disable blending as necessary. This function sets the appropriate
- * blend function based on the specified xfermode.
- */
- inline void chooseBlending(bool blend, SkXfermode::Mode mode, ProgramDescription& description,
- bool swapSrcDst = false);
-
- /**
- * Use the specified program with the current GL context. If the program is already
- * in use, it will not be bound again. If it is not in use, the current program is
- * marked unused and the specified program becomes used and becomes the new
- * current program.
- *
- * @param program The program to use
- *
- * @return true If the specified program was already in use, false otherwise.
- */
- inline bool useProgram(Program* program);
-
- /**
- * Invoked before any drawing operation. This sets required state.
- */
- void setupDraw(bool clear = true);
-
- /**
- * Various methods to setup OpenGL rendering.
- */
- void setupDrawWithTexture(bool isAlpha8 = false);
- void setupDrawWithTextureAndColor(bool isAlpha8 = false);
- void setupDrawWithExternalTexture();
- void setupDrawNoTexture();
- void setupDrawVertexAlpha(bool useShadowAlphaInterp);
- void setupDrawColor(int color, int alpha);
- void setupDrawColor(float r, float g, float b, float a);
- void setupDrawAlpha8Color(int color, int alpha);
- void setupDrawTextGamma(const SkPaint* paint);
- void setupDrawShader(const SkShader* shader);
- void setupDrawColorFilter(const SkColorFilter* filter);
- void setupDrawBlending(const Layer* layer, bool swapSrcDst = false);
- void setupDrawBlending(const SkPaint* paint, bool blend = true, bool swapSrcDst = false);
- void setupDrawProgram();
- void setupDrawDirtyRegionsDisabled();
-
- /**
- * Setup the current program matrices based upon the nature of the geometry.
- *
- * @param mode If kModelViewMode_Translate, the geometry must be translated by the left and top
- * parameters. If kModelViewMode_TranslateAndScale, the geometry that exists in the (0,0, 1,1)
- * space must be scaled up and translated to fill the quad provided in (l,t,r,b). These
- * transformations are stored in the modelView matrix and uploaded to the shader.
- *
- * @param offset Set to true if the the matrix should be fudged (translated) slightly to disambiguate
- * geometry pixel positioning. See Vertex::GeometryFudgeFactor().
- *
- * @param ignoreTransform Set to true if l,t,r,b coordinates already in layer space,
- * currentTransform() will be ignored. (e.g. when drawing clip in layer coordinates to stencil,
- * or when simple translation has been extracted)
- */
- void setupDrawModelView(ModelViewMode mode, bool offset,
- float left, float top, float right, float bottom, bool ignoreTransform = false);
- void setupDrawColorUniforms(bool hasShader);
- void setupDrawPureColorUniforms();
-
- /**
- * Setup uniforms for the current shader.
- *
- * @param shader SkShader on the current paint.
- *
- * @param ignoreTransform Set to true to ignore the transform in shader.
- */
- void setupDrawShaderUniforms(const SkShader* shader, bool ignoreTransform = false);
- void setupDrawColorFilterUniforms(const SkColorFilter* paint);
- void setupDrawSimpleMesh();
- void setupDrawTexture(GLuint texture);
- void setupDrawExternalTexture(GLuint texture);
- void setupDrawTextureTransform();
- void setupDrawTextureTransformUniforms(mat4& transform);
- void setupDrawTextGammaUniforms();
- void setupDrawMesh(const GLvoid* vertices, const GLvoid* texCoords = NULL, GLuint vbo = 0);
- void setupDrawMesh(const GLvoid* vertices, const GLvoid* texCoords, const GLvoid* colors);
- void setupDrawMeshIndices(const GLvoid* vertices, const GLvoid* texCoords, GLuint vbo = 0);
- void setupDrawIndexedVertices(GLvoid* vertices);
- void accountForClear(SkXfermode::Mode mode);
-
bool updateLayer(Layer* layer, bool inFrame);
void updateLayers();
void flushLayers();
@@ -931,9 +841,7 @@ private:
/**
* Should be invoked every time the glScissor is modified.
*/
- inline void dirtyClip() {
- mDirtyClip = true;
- }
+ inline void dirtyClip() { mState.setDirtyClip(true); }
inline const UvMapper& getMapper(const Texture* texture) {
return texture && texture->uvMapper ? *texture->uvMapper : mUvMapper;
@@ -946,21 +854,9 @@ private:
*/
Texture* getTexture(const SkBitmap* bitmap);
- /**
- * Model-view matrix used to position/size objects
- *
- * Stores operation-local modifications to the draw matrix that aren't incorporated into the
- * currentTransform().
- *
- * If generated with kModelViewMode_Translate, mModelViewMatrix will reflect an x/y offset,
- * e.g. the offset in drawLayer(). If generated with kModelViewMode_TranslateAndScale,
- * mModelViewMatrix will reflect a translation and scale, e.g. the translation and scale
- * required to make VBO 0 (a rect of (0,0,1,1)) scaled to match the x,y offset, and width/height
- * of a bitmap.
- *
- * Used as input to SkiaShader transformation.
- */
- mat4 mModelViewMatrix;
+ bool reportAndClearDirty() { bool ret = mDirty; mDirty = false; return ret; }
+ inline Snapshot* writableSnapshot() { return mState.writableSnapshot(); }
+ inline const Snapshot* currentSnapshot() const { return mState.currentSnapshot(); }
// State used to define the clipping region
Rect mTilingClip;
@@ -969,9 +865,6 @@ private:
// Is a frame currently being rendered
bool mFrameStarted;
- // Used to draw textured quads
- TextureVertex mMeshVertices[4];
-
// Default UV mapper
const UvMapper mUvMapper;
@@ -979,31 +872,11 @@ private:
DrawModifiers mDrawModifiers;
SkPaint mFilteredPaint;
- // Various caches
- Caches& mCaches;
- Extensions& mExtensions;
- RenderState& mRenderState;
-
// List of rectangles to clear after saveLayer() is invoked
- Vector<Rect*> mLayers;
+ std::vector<Rect> mLayers;
// List of layers to update at the beginning of a frame
Vector< sp<Layer> > mLayerUpdates;
- // The following fields are used to setup drawing
- // Used to describe the shaders to generate
- ProgramDescription mDescription;
- // Color description
- bool mColorSet;
- float mColorA, mColorR, mColorG, mColorB;
- // Indicates that the shader should get a color
- bool mSetShaderColor;
- // Current texture unit
- GLuint mTextureUnit;
- // Track dirty regions, true by default
- bool mTrackDirtyRegions;
- // Indicate whether we are drawing an opaque frame
- bool mOpaqueFrame;
-
// See PROPERTY_DISABLE_SCISSOR_OPTIMIZATION in
// Properties.h
bool mScissorOptimizationDisabled;
@@ -1014,6 +887,10 @@ private:
bool mSkipOutlineClip;
+ // True if anything has been drawn since the last call to
+ // reportAndClearDirty()
+ bool mDirty;
+
// Lighting + shadows
Vector3 mLightCenter;
float mLightRadius;
@@ -1021,10 +898,10 @@ private:
uint8_t mSpotShadowAlpha;
// Paths kept alive for the duration of the frame
- std::vector<SkPath*> mTempPaths;
+ std::vector<std::unique_ptr<SkPath>> mTempPaths;
friend class Layer;
- friend class TextSetupFunctor;
+ friend class TextDrawFunctor;
friend class DrawBitmapOp;
friend class DrawPatchOp;
diff --git a/libs/hwui/Outline.h b/libs/hwui/Outline.h
index 6dacd5e..5e9b213 100644
--- a/libs/hwui/Outline.h
+++ b/libs/hwui/Outline.h
@@ -95,7 +95,7 @@ public:
}
const SkPath* getPath() const {
- if (mType == kOutlineType_None || mType == kOutlineType_Empty) return NULL;
+ if (mType == kOutlineType_None || mType == kOutlineType_Empty) return nullptr;
return &mPath;
}
diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp
index 442e9ba..f673c6a 100644
--- a/libs/hwui/Patch.cpp
+++ b/libs/hwui/Patch.cpp
@@ -24,22 +24,12 @@
#include "Patch.h"
#include "Properties.h"
#include "UvMapper.h"
+#include "utils/MathUtils.h"
namespace android {
namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
-// Constructors/destructor
-///////////////////////////////////////////////////////////////////////////////
-
-Patch::Patch(): vertices(NULL), verticesCount(0), indexCount(0), hasEmptyQuads(false) {
-}
-
-Patch::~Patch() {
- delete[] vertices;
-}
-
-///////////////////////////////////////////////////////////////////////////////
// Vertices management
///////////////////////////////////////////////////////////////////////////////
@@ -47,19 +37,11 @@ uint32_t Patch::getSize() const {
return verticesCount * sizeof(TextureVertex);
}
-TextureVertex* Patch::createMesh(const float bitmapWidth, const float bitmapHeight,
- float width, float height, const Res_png_9patch* patch) {
- UvMapper mapper;
- return createMesh(bitmapWidth, bitmapHeight, width, height, mapper, patch);
-}
-
-TextureVertex* Patch::createMesh(const float bitmapWidth, const float bitmapHeight,
- float width, float height, const UvMapper& mapper, const Res_png_9patch* patch) {
- if (vertices) return vertices;
+Patch::Patch(const float bitmapWidth, const float bitmapHeight,
+ float width, float height, const UvMapper& mapper, const Res_png_9patch* patch)
+ : mColors(patch->getColors()) {
int8_t emptyQuads = 0;
- mColors = patch->getColors();
-
const int8_t numColors = patch->numColors;
if (uint8_t(numColors) < sizeof(uint32_t) * 4) {
for (int8_t i = 0; i < numColors; i++) {
@@ -75,10 +57,10 @@ TextureVertex* Patch::createMesh(const float bitmapWidth, const float bitmapHeig
uint32_t yCount = patch->numYDivs;
uint32_t maxVertices = ((xCount + 1) * (yCount + 1) - emptyQuads) * 4;
- if (maxVertices == 0) return NULL;
+ if (maxVertices == 0) return;
- TextureVertex* tempVertices = new TextureVertex[maxVertices];
- TextureVertex* vertex = tempVertices;
+ vertices.reset(new TextureVertex[maxVertices]);
+ TextureVertex* vertex = vertices.get();
const int32_t* xDivs = patch->getXDivs();
const int32_t* yDivs = patch->getYDivs();
@@ -157,15 +139,11 @@ TextureVertex* Patch::createMesh(const float bitmapWidth, const float bitmapHeig
width, bitmapWidth, quadCount);
}
- if (verticesCount == maxVertices) {
- vertices = tempVertices;
- } else {
- vertices = new TextureVertex[verticesCount];
- memcpy(vertices, tempVertices, verticesCount * sizeof(TextureVertex));
- delete[] tempVertices;
+ if (verticesCount != maxVertices) {
+ std::unique_ptr<TextureVertex[]> reducedVertices(new TextureVertex[verticesCount]);
+ memcpy(reducedVertices.get(), vertices.get(), verticesCount * sizeof(TextureVertex));
+ vertices = std::move(reducedVertices);
}
-
- return vertices;
}
void Patch::generateRow(const int32_t* xDivs, uint32_t xCount, TextureVertex*& vertex,
@@ -213,10 +191,10 @@ void Patch::generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, f
const uint32_t oldQuadCount = quadCount;
quadCount++;
- if (x1 < 0.0f) x1 = 0.0f;
- if (x2 < 0.0f) x2 = 0.0f;
- if (y1 < 0.0f) y1 = 0.0f;
- if (y2 < 0.0f) y2 = 0.0f;
+ x1 = MathUtils::max(x1, 0.0f);
+ x2 = MathUtils::max(x2, 0.0f);
+ y1 = MathUtils::max(y1, 0.0f);
+ y2 = MathUtils::max(y2, 0.0f);
// Skip degenerate and transparent (empty) quads
if ((mColors[oldQuadCount] == 0) || x1 >= x2 || y1 >= y2) {
diff --git a/libs/hwui/Patch.h b/libs/hwui/Patch.h
index 1ba045d..b63bd24 100644
--- a/libs/hwui/Patch.h
+++ b/libs/hwui/Patch.h
@@ -27,38 +27,35 @@
#include "Rect.h"
#include "UvMapper.h"
-#include "Vertex.h"
namespace android {
namespace uirenderer {
+struct TextureVertex;
+
///////////////////////////////////////////////////////////////////////////////
// 9-patch structures
///////////////////////////////////////////////////////////////////////////////
class Patch {
public:
- Patch();
- ~Patch();
+ Patch(const float bitmapWidth, const float bitmapHeight,
+ float width, float height,
+ const UvMapper& mapper, const Res_png_9patch* patch);
/**
* Returns the size of this patch's mesh in bytes.
*/
uint32_t getSize() const;
- TextureVertex* vertices;
- uint32_t verticesCount;
- uint32_t indexCount;
- bool hasEmptyQuads;
+ std::unique_ptr<TextureVertex[]> vertices;
+ uint32_t verticesCount = 0;
+ uint32_t indexCount = 0;
+ bool hasEmptyQuads = false;
Vector<Rect> quads;
- GLintptr offset;
- GLintptr textureOffset;
-
- TextureVertex* createMesh(const float bitmapWidth, const float bitmapHeight,
- float width, float height, const Res_png_9patch* patch);
- TextureVertex* createMesh(const float bitmapWidth, const float bitmapHeight,
- float width, float height, const UvMapper& mapper, const Res_png_9patch* patch);
+ GLintptr positionOffset = 0;
+ GLintptr textureOffset = 0;
private:
void generateRow(const int32_t* xDivs, uint32_t xCount, TextureVertex*& vertex,
diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp
index 2f2debc..2765262 100644
--- a/libs/hwui/PatchCache.cpp
+++ b/libs/hwui/PatchCache.cpp
@@ -20,8 +20,10 @@
#include <utils/Log.h>
#include "Caches.h"
+#include "Patch.h"
#include "PatchCache.h"
#include "Properties.h"
+#include "renderstate/RenderState.h"
namespace android {
namespace uirenderer {
@@ -30,11 +32,15 @@ namespace uirenderer {
// Constructors/destructor
///////////////////////////////////////////////////////////////////////////////
-PatchCache::PatchCache():
- mSize(0), mCache(LruCache<PatchDescription, Patch*>::kUnlimitedCapacity),
- mMeshBuffer(0), mFreeBlocks(NULL), mGenerationId(0) {
+PatchCache::PatchCache(RenderState& renderState)
+ : mRenderState(renderState)
+ , mSize(0)
+ , mCache(LruCache<PatchDescription, Patch*>::kUnlimitedCapacity)
+ , mMeshBuffer(0)
+ , mFreeBlocks(nullptr)
+ , mGenerationId(0) {
char property[PROPERTY_VALUE_MAX];
- if (property_get(PROPERTY_PATCH_CACHE_SIZE, property, NULL) > 0) {
+ if (property_get(PROPERTY_PATCH_CACHE_SIZE, property, nullptr) > 0) {
INIT_LOGD(" Setting patch cache size to %skB", property);
mMaxSize = KB(atoi(property));
} else {
@@ -47,15 +53,15 @@ PatchCache::~PatchCache() {
clear();
}
-void PatchCache::init(Caches& caches) {
+void PatchCache::init() {
bool created = false;
if (!mMeshBuffer) {
glGenBuffers(1, &mMeshBuffer);
created = true;
}
- caches.bindMeshBuffer(mMeshBuffer);
- caches.resetVertexPointers();
+ mRenderState.meshState().bindMeshBuffer(mMeshBuffer);
+ mRenderState.meshState().resetVertexPointers();
if (created) {
createVertexBuffer();
@@ -84,7 +90,7 @@ void PatchCache::clear() {
clearCache();
if (mMeshBuffer) {
- Caches::getInstance().unbindMeshBuffer();
+ mRenderState.meshState().unbindMeshBuffer();
glDeleteBuffers(1, &mMeshBuffer);
mMeshBuffer = 0;
mSize = 0;
@@ -104,7 +110,7 @@ void PatchCache::clearCache() {
delete block;
block = next;
}
- mFreeBlocks = NULL;
+ mFreeBlocks = nullptr;
}
void PatchCache::remove(Vector<patch_pair_t>& patchesToRemove, Res_png_9patch* patch) {
@@ -124,11 +130,11 @@ void PatchCache::removeDeferred(Res_png_9patch* patch) {
size_t count = mGarbage.size();
for (size_t i = 0; i < count; i++) {
if (patch == mGarbage[i]) {
- patch = NULL;
+ patch = nullptr;
break;
}
}
- LOG_ALWAYS_FATAL_IF(patch == NULL);
+ LOG_ALWAYS_FATAL_IF(patch == nullptr);
mGarbage.push(patch);
}
@@ -156,7 +162,7 @@ void PatchCache::clearGarbage() {
// Release the patch and mark the space in the free list
Patch* patch = pair.getSecond();
- BufferBlock* block = new BufferBlock(patch->offset, patch->getSize());
+ BufferBlock* block = new BufferBlock(patch->positionOffset, patch->getSize());
block->next = mFreeBlocks;
mFreeBlocks = block;
@@ -174,7 +180,7 @@ void PatchCache::clearGarbage() {
}
void PatchCache::createVertexBuffer() {
- glBufferData(GL_ARRAY_BUFFER, mMaxSize, NULL, GL_DYNAMIC_DRAW);
+ glBufferData(GL_ARRAY_BUFFER, mMaxSize, nullptr, GL_DYNAMIC_DRAW);
mSize = 0;
mFreeBlocks = new BufferBlock(0, mMaxSize);
mGenerationId++;
@@ -184,9 +190,9 @@ void PatchCache::createVertexBuffer() {
* Sets the mesh's offsets and copies its associated vertices into
* the mesh buffer (VBO).
*/
-void PatchCache::setupMesh(Patch* newMesh, TextureVertex* vertices) {
+void PatchCache::setupMesh(Patch* newMesh) {
// This call ensures the VBO exists and that it is bound
- init(Caches::getInstance());
+ init();
// If we're running out of space, let's clear the entire cache
uint32_t size = newMesh->getSize();
@@ -196,7 +202,7 @@ void PatchCache::setupMesh(Patch* newMesh, TextureVertex* vertices) {
}
// Find a block where we can fit the mesh
- BufferBlock* previous = NULL;
+ BufferBlock* previous = nullptr;
BufferBlock* block = mFreeBlocks;
while (block) {
// The mesh fits
@@ -212,14 +218,14 @@ void PatchCache::setupMesh(Patch* newMesh, TextureVertex* vertices) {
if (!block) {
clearCache();
createVertexBuffer();
- previous = NULL;
+ previous = nullptr;
block = mFreeBlocks;
}
// Copy the 9patch mesh in the VBO
- newMesh->offset = (GLintptr) (block->offset);
- newMesh->textureOffset = newMesh->offset + gMeshTextureOffset;
- glBufferSubData(GL_ARRAY_BUFFER, newMesh->offset, size, vertices);
+ newMesh->positionOffset = (GLintptr) (block->offset);
+ newMesh->textureOffset = newMesh->positionOffset + kMeshTextureOffset;
+ glBufferSubData(GL_ARRAY_BUFFER, newMesh->positionOffset, size, newMesh->vertices.get());
// Remove the block since we've used it entirely
if (block->size == size) {
@@ -238,6 +244,8 @@ void PatchCache::setupMesh(Patch* newMesh, TextureVertex* vertices) {
mSize += size;
}
+static const UvMapper sIdentity;
+
const Patch* PatchCache::get(const AssetAtlas::Entry* entry,
const uint32_t bitmapWidth, const uint32_t bitmapHeight,
const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch) {
@@ -246,20 +254,12 @@ const Patch* PatchCache::get(const AssetAtlas::Entry* entry,
const Patch* mesh = mCache.get(description);
if (!mesh) {
- Patch* newMesh = new Patch();
- TextureVertex* vertices;
-
- if (entry) {
- // An atlas entry has a UV mapper
- vertices = newMesh->createMesh(bitmapWidth, bitmapHeight,
- pixelWidth, pixelHeight, entry->uvMapper, patch);
- } else {
- vertices = newMesh->createMesh(bitmapWidth, bitmapHeight,
- pixelWidth, pixelHeight, patch);
- }
+ const UvMapper& mapper = entry ? entry->uvMapper : sIdentity;
+ Patch* newMesh = new Patch(bitmapWidth, bitmapHeight,
+ pixelWidth, pixelHeight, mapper, patch);
- if (vertices) {
- setupMesh(newMesh, vertices);
+ if (newMesh->vertices) {
+ setupMesh(newMesh);
}
#if DEBUG_PATCHES
@@ -278,7 +278,7 @@ void PatchCache::dumpFreeBlocks(const char* prefix) {
String8 dump;
BufferBlock* block = mFreeBlocks;
while (block) {
- dump.appendFormat("->(%d, %d)", block->offset, block->size);
+ dump.appendFormat("->(%d, %d)", block->positionOffset, block->size);
block = block->next;
}
ALOGD("%s: Free blocks%s", prefix, dump.string());
diff --git a/libs/hwui/PatchCache.h b/libs/hwui/PatchCache.h
index 9f2c9a5..387f79a 100644
--- a/libs/hwui/PatchCache.h
+++ b/libs/hwui/PatchCache.h
@@ -25,12 +25,13 @@
#include "AssetAtlas.h"
#include "Debug.h"
-#include "Patch.h"
#include "utils/Pair.h"
namespace android {
namespace uirenderer {
+class Patch;
+
///////////////////////////////////////////////////////////////////////////////
// Defines
///////////////////////////////////////////////////////////////////////////////
@@ -50,9 +51,9 @@ class Caches;
class PatchCache {
public:
- PatchCache();
+ PatchCache(RenderState& renderState);
~PatchCache();
- void init(Caches& caches);
+ void init();
const Patch* get(const AssetAtlas::Entry* entry,
const uint32_t bitmapWidth, const uint32_t bitmapHeight,
@@ -90,7 +91,7 @@ public:
private:
struct PatchDescription {
- PatchDescription(): mPatch(NULL), mBitmapWidth(0), mBitmapHeight(0),
+ PatchDescription(): mPatch(nullptr), mBitmapWidth(0), mBitmapHeight(0),
mPixelWidth(0), mPixelHeight(0) {
}
@@ -145,7 +146,7 @@ private:
* to track available regions of memory in the VBO.
*/
struct BufferBlock {
- BufferBlock(uint32_t offset, uint32_t size): offset(offset), size(size), next(NULL) {
+ BufferBlock(uint32_t offset, uint32_t size): offset(offset), size(size), next(nullptr) {
}
uint32_t offset;
@@ -159,7 +160,7 @@ private:
void clearCache();
void createVertexBuffer();
- void setupMesh(Patch* newMesh, TextureVertex* vertices);
+ void setupMesh(Patch* newMesh);
void remove(Vector<patch_pair_t>& patchesToRemove, Res_png_9patch* patch);
@@ -167,6 +168,7 @@ private:
void dumpFreeBlocks(const char* prefix);
#endif
+ RenderState& mRenderState;
uint32_t mMaxSize;
uint32_t mSize;
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index 6f48e4d..bdb44a6 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -31,7 +31,6 @@
#include "PathCache.h"
#include "thread/Signal.h"
-#include "thread/Task.h"
#include "thread/TaskProcessor.h"
namespace android {
@@ -41,25 +40,25 @@ namespace uirenderer {
// Cache entries
///////////////////////////////////////////////////////////////////////////////
-PathDescription::PathDescription():
- type(kShapeNone),
- join(SkPaint::kDefault_Join),
- cap(SkPaint::kDefault_Cap),
- style(SkPaint::kFill_Style),
- miter(4.0f),
- strokeWidth(1.0f),
- pathEffect(NULL) {
+PathDescription::PathDescription()
+ : type(kShapeNone)
+ , join(SkPaint::kDefault_Join)
+ , cap(SkPaint::kDefault_Cap)
+ , style(SkPaint::kFill_Style)
+ , miter(4.0f)
+ , strokeWidth(1.0f)
+ , pathEffect(nullptr) {
memset(&shape, 0, sizeof(Shape));
}
-PathDescription::PathDescription(ShapeType type, const SkPaint* paint):
- type(type),
- join(paint->getStrokeJoin()),
- cap(paint->getStrokeCap()),
- style(paint->getStyle()),
- miter(paint->getStrokeMiter()),
- strokeWidth(paint->getStrokeWidth()),
- pathEffect(paint->getPathEffect()) {
+PathDescription::PathDescription(ShapeType type, const SkPaint* paint)
+ : type(type)
+ , join(paint->getStrokeJoin())
+ , cap(paint->getStrokeCap())
+ , style(paint->getStyle())
+ , miter(paint->getStrokeMiter())
+ , strokeWidth(paint->getStrokeWidth())
+ , pathEffect(paint->getPathEffect()) {
memset(&shape, 0, sizeof(Shape));
}
@@ -81,7 +80,7 @@ hash_t PathDescription::hash() const {
bool PathCache::canDrawAsConvexPath(SkPath* path, const SkPaint* paint) {
// NOTE: This should only be used after PathTessellator handles joins properly
- return paint->getPathEffect() == NULL && path->getConvexity() == SkPath::kConvex_Convexity;
+ return paint->getPathEffect() == nullptr && path->getConvexity() == SkPath::kConvex_Convexity;
}
void PathCache::computePathBounds(const SkPath* path, const SkPaint* paint,
@@ -114,9 +113,9 @@ static void initPaint(SkPaint& paint) {
// will be applied later when compositing the alpha8 texture
paint.setColor(SK_ColorBLACK);
paint.setAlpha(255);
- paint.setColorFilter(NULL);
- paint.setMaskFilter(NULL);
- paint.setShader(NULL);
+ paint.setColorFilter(nullptr);
+ paint.setMaskFilter(nullptr);
+ paint.setShader(nullptr);
SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrc_Mode);
SkSafeUnref(paint.setXfermode(mode));
}
@@ -133,18 +132,6 @@ static void drawPath(const SkPath *path, const SkPaint* paint, SkBitmap& bitmap,
canvas.drawPath(*path, pathPaint);
}
-static PathTexture* createTexture(float left, float top, float offset,
- uint32_t width, uint32_t height, uint32_t id) {
- PathTexture* texture = new PathTexture(Caches::getInstance());
- texture->left = left;
- texture->top = top;
- texture->offset = offset;
- texture->width = width;
- texture->height = height;
- texture->generation = id;
- return texture;
-}
-
///////////////////////////////////////////////////////////////////////////////
// Cache constructor/destructor
///////////////////////////////////////////////////////////////////////////////
@@ -153,7 +140,7 @@ PathCache::PathCache():
mCache(LruCache<PathDescription, PathTexture*>::kUnlimitedCapacity),
mSize(0), mMaxSize(MB(DEFAULT_PATH_CACHE_SIZE)) {
char property[PROPERTY_VALUE_MAX];
- if (property_get(PROPERTY_PATH_CACHE_SIZE, property, NULL) > 0) {
+ if (property_get(PROPERTY_PATH_CACHE_SIZE, property, nullptr) > 0) {
INIT_LOGD(" Setting %s cache size to %sMB", name, property);
setMaxSize(MB(atof(property)));
} else {
@@ -211,8 +198,8 @@ void PathCache::removeTexture(PathTexture* texture) {
// If there is a pending task we must wait for it to return
// before attempting our cleanup
const sp<Task<SkBitmap*> >& task = texture->task();
- if (task != NULL) {
- SkBitmap* bitmap = task->getResult();
+ if (task != nullptr) {
+ task->getResult();
texture->clearTask();
} else {
// If there is a pending task, the path was not added
@@ -231,7 +218,7 @@ void PathCache::removeTexture(PathTexture* texture) {
}
if (texture->id) {
- Caches::getInstance().deleteTexture(texture->id);
+ Caches::getInstance().textureState().deleteTexture(texture->id);
}
delete texture;
}
@@ -261,14 +248,15 @@ PathTexture* PathCache::addTexture(const PathDescription& entry, const SkPath *p
uint32_t width, height;
computePathBounds(path, paint, left, top, offset, width, height);
- if (!checkTextureSize(width, height)) return NULL;
+ if (!checkTextureSize(width, height)) return nullptr;
purgeCache(width, height);
SkBitmap bitmap;
drawPath(path, paint, bitmap, left, top, offset, width, height);
- PathTexture* texture = createTexture(left, top, offset, width, height,
+ PathTexture* texture = new PathTexture(Caches::getInstance(),
+ left, top, offset, width, height,
path->getGenerationID());
generateTexture(entry, &bitmap, texture);
@@ -313,7 +301,7 @@ void PathCache::generateTexture(SkBitmap& bitmap, Texture* texture) {
glGenTextures(1, &texture->id);
- Caches::getInstance().bindTexture(texture->id);
+ Caches::getInstance().textureState().bindTexture(texture->id);
// Textures are Alpha8
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
@@ -355,7 +343,7 @@ void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) {
} else {
texture->width = 0;
texture->height = 0;
- t->setResult(NULL);
+ t->setResult(nullptr);
}
}
@@ -363,22 +351,9 @@ void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) {
// Paths
///////////////////////////////////////////////////////////////////////////////
-void PathCache::remove(Vector<PathDescription>& pathsToRemove, const path_pair_t& pair) {
- LruCache<PathDescription, PathTexture*>::Iterator i(mCache);
-
- while (i.next()) {
- const PathDescription& key = i.key();
- if (key.type == kShapePath &&
- (key.shape.path.mPath == pair.getFirst() ||
- key.shape.path.mPath == pair.getSecond())) {
- pathsToRemove.push(key);
- }
- }
-}
-
-void PathCache::removeDeferred(SkPath* path) {
+void PathCache::removeDeferred(const SkPath* path) {
Mutex::Autolock l(mLock);
- mGarbage.push(path_pair_t(path, const_cast<SkPath*>(path->getSourcePath())));
+ mGarbage.push(path->getGenerationID());
}
void PathCache::clearGarbage() {
@@ -388,9 +363,15 @@ void PathCache::clearGarbage() {
Mutex::Autolock l(mLock);
size_t count = mGarbage.size();
for (size_t i = 0; i < count; i++) {
- const path_pair_t& pair = mGarbage.itemAt(i);
- remove(pathsToRemove, pair);
- delete pair.getFirst();
+ const uint32_t generationID = mGarbage.itemAt(i);
+
+ LruCache<PathDescription, PathTexture*>::Iterator iter(mCache);
+ while (iter.next()) {
+ const PathDescription& key = iter.key();
+ if (key.type == kShapePath && key.shape.path.mGenerationID == generationID) {
+ pathsToRemove.push(key);
+ }
+ }
}
mGarbage.clear();
}
@@ -400,27 +381,9 @@ void PathCache::clearGarbage() {
}
}
-/**
- * To properly handle path mutations at draw time we always make a copy
- * of paths objects when recording display lists. The source path points
- * to the path we originally copied the path from. This ensures we use
- * the original path as a cache key the first time a path is inserted
- * in the cache. The source path is also used to reclaim garbage when a
- * Dalvik Path object is collected.
- */
-static const SkPath* getSourcePath(const SkPath* path) {
- const SkPath* sourcePath = path->getSourcePath();
- if (sourcePath && sourcePath->getGenerationID() == path->getGenerationID()) {
- return const_cast<SkPath*>(sourcePath);
- }
- return path;
-}
-
PathTexture* PathCache::get(const SkPath* path, const SkPaint* paint) {
- path = getSourcePath(path);
-
PathDescription entry(kShapePath, paint);
- entry.shape.path.mPath = path;
+ entry.shape.path.mGenerationID = path->getGenerationID();
PathTexture* texture = mCache.get(entry);
@@ -430,7 +393,7 @@ PathTexture* PathCache::get(const SkPath* path, const SkPaint* paint) {
// A bitmap is attached to the texture, this means we need to
// upload it as a GL texture
const sp<Task<SkBitmap*> >& task = texture->task();
- if (task != NULL) {
+ if (task != nullptr) {
// But we must first wait for the worker thread to be done
// producing the bitmap, so let's wait
SkBitmap* bitmap = task->getResult();
@@ -440,14 +403,9 @@ PathTexture* PathCache::get(const SkPath* path, const SkPaint* paint) {
} else {
ALOGW("Path too large to be rendered into a texture");
texture->clearTask();
- texture = NULL;
+ texture = nullptr;
mCache.remove(entry);
}
- } else if (path->getGenerationID() != texture->generation) {
- // The size of the path might have changed so we first
- // remove the entry from the cache
- mCache.remove(entry);
- texture = addTexture(entry, path, paint);
}
}
@@ -459,25 +417,20 @@ void PathCache::precache(const SkPath* path, const SkPaint* paint) {
return;
}
- path = getSourcePath(path);
-
PathDescription entry(kShapePath, paint);
- entry.shape.path.mPath = path;
+ entry.shape.path.mGenerationID = path->getGenerationID();
PathTexture* texture = mCache.get(entry);
bool generate = false;
if (!texture) {
generate = true;
- } else if (path->getGenerationID() != texture->generation) {
- mCache.remove(entry);
- generate = true;
}
if (generate) {
// It is important to specify the generation ID so we do not
// attempt to precache the same path several times
- texture = createTexture(0.0f, 0.0f, 0.0f, 0, 0, path->getGenerationID());
+ texture = new PathTexture(Caches::getInstance(), path->getGenerationID());
sp<PathTask> task = new PathTask(path, paint, texture);
texture->setTask(task);
@@ -490,7 +443,7 @@ void PathCache::precache(const SkPath* path, const SkPaint* paint) {
// be enforced.
mCache.put(entry, texture);
- if (mProcessor == NULL) {
+ if (mProcessor == nullptr) {
mProcessor = new PathProcessor(Caches::getInstance());
}
mProcessor->add(task);
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index bc34188..4297693 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -17,22 +17,22 @@
#ifndef ANDROID_HWUI_PATH_CACHE_H
#define ANDROID_HWUI_PATH_CACHE_H
-#include <GLES2/gl2.h>
-
-#include <utils/LruCache.h>
-#include <utils/Mutex.h>
-#include <utils/Vector.h>
-
#include "Debug.h"
-#include "Properties.h"
#include "Texture.h"
+#include "thread/Task.h"
+#include "thread/TaskProcessor.h"
#include "utils/Macros.h"
#include "utils/Pair.h"
+#include <GLES2/gl2.h>
+#include <SkPath.h>
+#include <utils/LruCache.h>
+#include <utils/Mutex.h>
+#include <utils/Vector.h>
+
class SkBitmap;
class SkCanvas;
class SkPaint;
-class SkPath;
struct SkRect;
namespace android {
@@ -59,7 +59,19 @@ class Caches;
* Alpha texture used to represent a path.
*/
struct PathTexture: public Texture {
- PathTexture(Caches& caches): Texture(caches) {
+ PathTexture(Caches& caches, float left, float top,
+ float offset, int width, int height, int generation)
+ : Texture(caches)
+ , left(left)
+ , top(top)
+ , offset(offset) {
+ this->width = width;
+ this->height = height;
+ this->generation = generation;
+ }
+ PathTexture(Caches& caches, int generation)
+ : Texture(caches) {
+ this->generation = generation;
}
~PathTexture() {
@@ -69,15 +81,15 @@ struct PathTexture: public Texture {
/**
* Left coordinate of the path bounds.
*/
- float left;
+ float left = 0;
/**
* Top coordinate of the path bounds.
*/
- float top;
+ float top = 0;
/**
* Offset to draw the path at the correct origin.
*/
- float offset;
+ float offset = 0;
sp<Task<SkBitmap*> > task() const {
return mTask;
@@ -88,7 +100,7 @@ struct PathTexture: public Texture {
}
void clearTask() {
- if (mTask != NULL) {
+ if (mTask != nullptr) {
mTask.clear();
}
}
@@ -118,7 +130,7 @@ struct PathDescription {
SkPathEffect* pathEffect;
union Shape {
struct Path {
- const SkPath* mPath;
+ uint32_t mGenerationID;
} path;
struct RoundRect {
float mWidth;
@@ -166,7 +178,7 @@ public:
* Used as a callback when an entry is removed from the cache.
* Do not invoke directly.
*/
- void operator()(PathDescription& path, PathTexture*& texture);
+ void operator()(PathDescription& path, PathTexture*& texture) override;
/**
* Clears the cache. This causes all textures to be deleted.
@@ -198,7 +210,7 @@ public:
* Removes the specified path. This is meant to be called from threads
* that are not the EGL context thread.
*/
- void removeDeferred(SkPath* path);
+ ANDROID_API void removeDeferred(const SkPath* path);
/**
* Process deferred removals.
*/
@@ -227,8 +239,6 @@ public:
float& left, float& top, float& offset, uint32_t& width, uint32_t& height);
private:
- typedef Pair<SkPath*, SkPath*> path_pair_t;
-
PathTexture* addTexture(const PathDescription& entry,
const SkPath *path, const SkPaint* paint);
PathTexture* addTexture(const PathDescription& entry, SkBitmap* bitmap);
@@ -245,12 +255,6 @@ private:
}
/**
- * Removes an entry.
- * The pair must define first=path, second=sourcePath
- */
- void remove(Vector<PathDescription>& pathsToRemove, const path_pair_t& pair);
-
- /**
* Ensures there is enough space in the cache for a texture of the specified
* dimensions.
*/
@@ -279,8 +283,7 @@ private:
delete future()->get();
}
- // copied, since input path not refcounted / guaranteed to survive for duration of task
- // TODO: avoid deep copy with refcounting
+ // copied, since input path not guaranteed to survive for duration of task
const SkPath path;
// copied, since input paint may not be immutable
@@ -293,7 +296,7 @@ private:
PathProcessor(Caches& caches);
~PathProcessor() { }
- virtual void onProcess(const sp<Task<SkBitmap*> >& task);
+ virtual void onProcess(const sp<Task<SkBitmap*> >& task) override;
private:
uint32_t mMaxTextureSize;
@@ -308,7 +311,7 @@ private:
sp<PathProcessor> mProcessor;
- Vector<path_pair_t> mGarbage;
+ Vector<uint32_t> mGarbage;
mutable Mutex mLock;
}; // class PathCache
diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp
index 27ef06f..c1f61d6 100644
--- a/libs/hwui/PathTessellator.cpp
+++ b/libs/hwui/PathTessellator.cpp
@@ -37,6 +37,7 @@
#include <SkPath.h>
#include <SkPaint.h>
+#include <SkGeometry.h> // WARNING: Internal Skia Header
#include <stdlib.h>
#include <stdint.h>
@@ -229,7 +230,6 @@ void getStrokeVerticesFromPerimeter(const PaintInfo& paintInfo, const Vector<Ver
current->x - totalOffset.x,
current->y - totalOffset.y);
- last = current;
current = next;
lastNormal = nextNormal;
}
@@ -279,7 +279,6 @@ void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo,
- (vertices[lastIndex].x - vertices[lastIndex - 1].x),
vertices[lastIndex].y - vertices[lastIndex - 1].y);
const float dTheta = PI / (extra + 1);
- const float radialScale = 2.0f / (1 + cos(dTheta));
int capOffset;
for (int i = 0; i < extra; i++) {
@@ -290,14 +289,14 @@ void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo,
}
beginTheta += dTheta;
- Vector2 beginRadialOffset = {cos(beginTheta), sin(beginTheta)};
+ Vector2 beginRadialOffset = {cosf(beginTheta), sinf(beginTheta)};
paintInfo.scaleOffsetForStrokeWidth(beginRadialOffset);
Vertex::set(&buffer[capOffset],
vertices[0].x + beginRadialOffset.x,
vertices[0].y + beginRadialOffset.y);
endTheta += dTheta;
- Vector2 endRadialOffset = {cos(endTheta), sin(endTheta)};
+ Vector2 endRadialOffset = {cosf(endTheta), sinf(endTheta)};
paintInfo.scaleOffsetForStrokeWidth(endRadialOffset);
Vertex::set(&buffer[allocSize - 1 - capOffset],
vertices[lastIndex].x + endRadialOffset.x,
@@ -373,7 +372,6 @@ void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Ver
current->y - totalOffset.y,
maxAlpha);
- last = current;
current = next;
lastNormal = nextNormal;
}
@@ -468,7 +466,7 @@ inline static void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>&
for (int i = 0; i < extra; i++) {
theta += dTheta;
- Vector2 radialOffset = {cos(theta), sin(theta)};
+ Vector2 radialOffset = {cosf(theta), sinf(theta)};
// scale to compensate for pinching at sharp angles, see totalOffsetFromNormals()
radialOffset *= radialScale;
@@ -701,7 +699,6 @@ void getStrokeVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<V
current->y - outerOffset.y,
0.0f);
- last = current;
current = next;
lastNormal = nextNormal;
}
@@ -787,6 +784,7 @@ void PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint,
Rect bounds(path.getBounds());
paintInfo.expandBoundsForStroke(&bounds);
vertexBuffer.setBounds(bounds);
+ vertexBuffer.setMeshFeatureFlags(paintInfo.isAA ? VertexBuffer::kAlpha : VertexBuffer::kNone);
}
template <class TYPE>
@@ -831,7 +829,6 @@ void PathTessellator::tessellatePoints(const float* points, int count, const SkP
Rect bounds;
// tessellate, then duplicate outline across points
- int numPoints = count / 2;
VertexBuffer tempBuffer;
if (!paintInfo.isAA) {
getFillVerticesFromPerimeter(outlineVertices, tempBuffer);
@@ -845,6 +842,7 @@ void PathTessellator::tessellatePoints(const float* points, int count, const SkP
// expand bounds from vertex coords to pixel data
paintInfo.expandBoundsForStroke(&bounds);
vertexBuffer.setBounds(bounds);
+ vertexBuffer.setMeshFeatureFlags(paintInfo.isAA ? VertexBuffer::kAlpha : VertexBuffer::kNone);
}
void PathTessellator::tessellateLines(const float* points, int count, const SkPaint* paint,
@@ -895,6 +893,7 @@ void PathTessellator::tessellateLines(const float* points, int count, const SkPa
// expand bounds from vertex coords to pixel data
paintInfo.expandBoundsForStroke(&bounds);
vertexBuffer.setBounds(bounds);
+ vertexBuffer.setMeshFeatureFlags(paintInfo.isAA ? VertexBuffer::kAlpha : VertexBuffer::kNone);
}
///////////////////////////////////////////////////////////////////////////////
@@ -953,6 +952,21 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo
pts[2].x(), pts[2].y(),
sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
break;
+ case SkPath::kConic_Verb: {
+ ALOGV("kConic_Verb");
+ SkAutoConicToQuads converter;
+ const SkPoint* quads = converter.computeQuads(pts, iter.conicWeight(),
+ thresholdSquared);
+ for (int i = 0; i < converter.countQuads(); ++i) {
+ const int offset = 2 * i;
+ recursiveQuadraticBezierVertices(
+ quads[offset].x(), quads[offset].y(),
+ quads[offset+2].x(), quads[offset+2].y(),
+ quads[offset+1].x(), quads[offset+1].y(),
+ sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
+ }
+ break;
+ }
default:
break;
}
diff --git a/libs/hwui/PixelBuffer.cpp b/libs/hwui/PixelBuffer.cpp
index 5b642b9..9665a68 100644
--- a/libs/hwui/PixelBuffer.cpp
+++ b/libs/hwui/PixelBuffer.cpp
@@ -16,13 +16,14 @@
#define LOG_TAG "OpenGLRenderer"
-#include <utils/Log.h>
+#include "PixelBuffer.h"
-#include "Caches.h"
#include "Debug.h"
#include "Extensions.h"
-#include "PixelBuffer.h"
#include "Properties.h"
+#include "renderstate/RenderState.h"
+
+#include <utils/Log.h>
namespace android {
namespace uirenderer {
@@ -34,33 +35,28 @@ namespace uirenderer {
class CpuPixelBuffer: public PixelBuffer {
public:
CpuPixelBuffer(GLenum format, uint32_t width, uint32_t height);
- ~CpuPixelBuffer();
- uint8_t* map(AccessMode mode = kAccessMode_ReadWrite);
- void unmap();
+ uint8_t* map(AccessMode mode = kAccessMode_ReadWrite) override;
+ void unmap() override;
- uint8_t* getMappedPointer() const;
+ uint8_t* getMappedPointer() const override;
- void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset);
+ void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) override;
private:
- uint8_t* mBuffer;
+ std::unique_ptr<uint8_t[]> mBuffer;
};
-CpuPixelBuffer::CpuPixelBuffer(GLenum format, uint32_t width, uint32_t height):
- PixelBuffer(format, width, height) {
- mBuffer = new uint8_t[width * height * formatSize(format)];
-}
-
-CpuPixelBuffer::~CpuPixelBuffer() {
- delete[] mBuffer;
+CpuPixelBuffer::CpuPixelBuffer(GLenum format, uint32_t width, uint32_t height)
+ : PixelBuffer(format, width, height)
+ , mBuffer(new uint8_t[width * height * formatSize(format)]) {
}
uint8_t* CpuPixelBuffer::map(AccessMode mode) {
if (mAccessMode == kAccessMode_None) {
mAccessMode = mode;
}
- return mBuffer;
+ return mBuffer.get();
}
void CpuPixelBuffer::unmap() {
@@ -68,12 +64,12 @@ void CpuPixelBuffer::unmap() {
}
uint8_t* CpuPixelBuffer::getMappedPointer() const {
- return mAccessMode == kAccessMode_None ? NULL : mBuffer;
+ return mAccessMode == kAccessMode_None ? nullptr : mBuffer.get();
}
void CpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) {
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height,
- mFormat, GL_UNSIGNED_BYTE, mBuffer + offset);
+ mFormat, GL_UNSIGNED_BYTE, &mBuffer[offset]);
}
///////////////////////////////////////////////////////////////////////////////
@@ -85,12 +81,12 @@ public:
GpuPixelBuffer(GLenum format, uint32_t width, uint32_t height);
~GpuPixelBuffer();
- uint8_t* map(AccessMode mode = kAccessMode_ReadWrite);
- void unmap();
+ uint8_t* map(AccessMode mode = kAccessMode_ReadWrite) override;
+ void unmap() override;
- uint8_t* getMappedPointer() const;
+ uint8_t* getMappedPointer() const override;
- void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset);
+ void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) override;
private:
GLuint mBuffer;
@@ -98,12 +94,16 @@ private:
Caches& mCaches;
};
-GpuPixelBuffer::GpuPixelBuffer(GLenum format, uint32_t width, uint32_t height):
- PixelBuffer(format, width, height), mMappedPointer(0), mCaches(Caches::getInstance()) {
+GpuPixelBuffer::GpuPixelBuffer(GLenum format,
+ uint32_t width, uint32_t height)
+ : PixelBuffer(format, width, height)
+ , mMappedPointer(nullptr)
+ , mCaches(Caches::getInstance()){
glGenBuffers(1, &mBuffer);
- mCaches.bindPixelBuffer(mBuffer);
- glBufferData(GL_PIXEL_UNPACK_BUFFER, getSize(), NULL, GL_DYNAMIC_DRAW);
- mCaches.unbindPixelBuffer();
+
+ mCaches.pixelBufferState().bind(mBuffer);
+ glBufferData(GL_PIXEL_UNPACK_BUFFER, getSize(), nullptr, GL_DYNAMIC_DRAW);
+ mCaches.pixelBufferState().unbind();
}
GpuPixelBuffer::~GpuPixelBuffer() {
@@ -112,7 +112,7 @@ GpuPixelBuffer::~GpuPixelBuffer() {
uint8_t* GpuPixelBuffer::map(AccessMode mode) {
if (mAccessMode == kAccessMode_None) {
- mCaches.bindPixelBuffer(mBuffer);
+ mCaches.pixelBufferState().bind(mBuffer);
mMappedPointer = (uint8_t*) glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, getSize(), mode);
#if DEBUG_OPENGL
if (!mMappedPointer) {
@@ -131,14 +131,14 @@ uint8_t* GpuPixelBuffer::map(AccessMode mode) {
void GpuPixelBuffer::unmap() {
if (mAccessMode != kAccessMode_None) {
if (mMappedPointer) {
- mCaches.bindPixelBuffer(mBuffer);
+ mCaches.pixelBufferState().bind(mBuffer);
GLboolean status = glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
if (status == GL_FALSE) {
ALOGE("Corrupted GPU pixel buffer");
}
}
mAccessMode = kAccessMode_None;
- mMappedPointer = NULL;
+ mMappedPointer = nullptr;
}
}
@@ -148,7 +148,7 @@ uint8_t* GpuPixelBuffer::getMappedPointer() const {
void GpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) {
// If the buffer is not mapped, unmap() will not bind it
- mCaches.bindPixelBuffer(mBuffer);
+ mCaches.pixelBufferState().bind(mBuffer);
unmap();
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, mFormat,
GL_UNSIGNED_BYTE, reinterpret_cast<void*>(offset));
@@ -158,7 +158,8 @@ void GpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t hei
// Factory
///////////////////////////////////////////////////////////////////////////////
-PixelBuffer* PixelBuffer::create(GLenum format, uint32_t width, uint32_t height, BufferType type) {
+PixelBuffer* PixelBuffer::create(GLenum format,
+ uint32_t width, uint32_t height, BufferType type) {
if (type == kBufferType_Auto && Caches::getInstance().gpuPixelBuffersEnabled) {
return new GpuPixelBuffer(format, width, height);
}
diff --git a/libs/hwui/PixelBuffer.h b/libs/hwui/PixelBuffer.h
index 04225a2..aac5ec4 100644
--- a/libs/hwui/PixelBuffer.h
+++ b/libs/hwui/PixelBuffer.h
@@ -18,6 +18,7 @@
#define ANDROID_HWUI_PIXEL_BUFFER_H
#include <GLES3/gl3.h>
+#include <cutils/log.h>
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp
index e6fd2dc..32713e9 100644
--- a/libs/hwui/Program.cpp
+++ b/libs/hwui/Program.cpp
@@ -46,7 +46,7 @@ Program::Program(const ProgramDescription& description, const char* vertex, cons
glAttachShader(mProgramId, mVertexShader);
glAttachShader(mProgramId, mFragmentShader);
- position = bindAttrib("position", kBindingPosition);
+ bindAttrib("position", kBindingPosition);
if (description.hasTexture || description.hasExternalTexture) {
texCoords = bindAttrib("texCoords", kBindingTexCoords);
} else {
@@ -64,7 +64,7 @@ Program::Program(const ProgramDescription& description, const char* vertex, cons
glGetProgramiv(mProgramId, GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen > 1) {
GLchar log[infoLen];
- glGetProgramInfoLog(mProgramId, infoLen, 0, &log[0]);
+ glGetProgramInfoLog(mProgramId, infoLen, nullptr, &log[0]);
ALOGE("%s", log);
}
LOG_ALWAYS_FATAL("Error while linking shaders");
@@ -135,7 +135,7 @@ GLuint Program::buildShader(const char* source, GLenum type) {
ATRACE_NAME("Build GL Shader");
GLuint shader = glCreateShader(type);
- glShaderSource(shader, 1, &source, 0);
+ glShaderSource(shader, 1, &source, nullptr);
glCompileShader(shader);
GLint status;
@@ -145,7 +145,7 @@ GLuint Program::buildShader(const char* source, GLenum type) {
// Some drivers return wrong values for GL_INFO_LOG_LENGTH
// use a fixed size instead
GLchar log[512];
- glGetShaderInfoLog(shader, sizeof(log), 0, &log[0]);
+ glGetShaderInfoLog(shader, sizeof(log), nullptr, &log[0]);
LOG_ALWAYS_FATAL("Shader info log: %s", log);
return 0;
}
@@ -177,12 +177,12 @@ void Program::set(const mat4& projectionMatrix, const mat4& modelViewMatrix,
glUniformMatrix4fv(transform, 1, GL_FALSE, &t.data[0]);
}
-void Program::setColor(const float r, const float g, const float b, const float a) {
+void Program::setColor(FloatColor color) {
if (!mHasColorUniform) {
mColorUniform = getUniform("color");
mHasColorUniform = true;
}
- glUniform4f(mColorUniform, r, g, b, a);
+ glUniform4f(mColorUniform, color.r, color.g, color.b, color.a);
}
void Program::use() {
diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h
index d05b331..af1e4a7 100644
--- a/libs/hwui/Program.h
+++ b/libs/hwui/Program.h
@@ -25,6 +25,7 @@
#include <SkXfermode.h>
#include "Debug.h"
+#include "FloatColor.h"
#include "Matrix.h"
#include "Properties.h"
@@ -102,7 +103,7 @@ typedef uint64_t programid;
* A ProgramDescription must be used in conjunction with a ProgramCache.
*/
struct ProgramDescription {
- enum ColorModifier {
+ enum ColorFilterMode {
kColorNone = 0,
kColorMatrix,
kColorBlend
@@ -148,7 +149,7 @@ struct ProgramDescription {
GLenum bitmapWrapT;
// Color operations
- ColorModifier colorOp;
+ ColorFilterMode colorOp;
SkXfermode::Mode colorMode;
// Framebuffer blending (requires Extensions.hasFramebufferFetch())
@@ -363,15 +364,10 @@ public:
/**
* Sets the color associated with this shader.
*/
- void setColor(const float r, const float g, const float b, const float a);
+ void setColor(FloatColor color);
/**
- * Name of the position attribute.
- */
- int position;
-
- /**
- * Name of the texCoords attribute if it exists, -1 otherwise.
+ * Name of the texCoords attribute if it exists (kBindingTexCoords), -1 otherwise.
*/
int texCoords;
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 62835e0..41adda1 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -240,8 +240,9 @@ const char* gFS_Main_ModulateColor =
const char* gFS_Main_ApplyVertexAlphaLinearInterp =
" fragColor *= alpha;\n";
const char* gFS_Main_ApplyVertexAlphaShadowInterp =
- " fragColor *= (1.0 - cos(alpha)) / 2.0;\n";
-
+ // Use a gaussian function for the shadow fall off. Note that alpha here
+ // is actually (1.0 - alpha) for saving computation.
+ " fragColor *= exp(- alpha * alpha * 4.0) - 0.018;\n";
const char* gFS_Main_FetchTexture[2] = {
// Don't modulate
" fragColor = texture2D(baseSampler, outTexCoords);\n",
@@ -380,9 +381,9 @@ const char* gBlendOps[18] = {
// Xor
"return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb, "
"src.a + dst.a - 2.0 * src.a * dst.a);\n",
- // Add
+ // Plus
"return min(src + dst, 1.0);\n",
- // Multiply
+ // Modulate
"return src * dst;\n",
// Screen
"return src + dst - src * dst;\n",
@@ -404,7 +405,8 @@ const char* gBlendOps[18] = {
// Constructors/destructors
///////////////////////////////////////////////////////////////////////////////
-ProgramCache::ProgramCache(): mHasES3(Extensions::getInstance().getMajorGlVersion() >= 3) {
+ProgramCache::ProgramCache(Extensions& extensions)
+ : mHasES3(extensions.getMajorGlVersion() >= 3) {
}
ProgramCache::~ProgramCache() {
@@ -417,11 +419,6 @@ ProgramCache::~ProgramCache() {
void ProgramCache::clear() {
PROGRAM_LOGD("Clearing program cache");
-
- size_t count = mCache.size();
- for (size_t i = 0; i < count; i++) {
- delete mCache.valueAt(i);
- }
mCache.clear();
}
@@ -433,14 +430,14 @@ Program* ProgramCache::get(const ProgramDescription& description) {
key = PROGRAM_KEY_TEXTURE;
}
- ssize_t index = mCache.indexOfKey(key);
- Program* program = NULL;
- if (index < 0) {
+ auto iter = mCache.find(key);
+ Program* program = nullptr;
+ if (iter == mCache.end()) {
description.log("Could not find program");
program = generateProgram(description, key);
- mCache.add(key, program);
+ mCache[key] = std::unique_ptr<Program>(program);
} else {
- program = mCache.valueAt(index);
+ program = iter->second.get();
}
return program;
}
@@ -834,7 +831,7 @@ void ProgramCache::printLongString(const String8& shader) const {
while ((index = shader.find("\n", index)) > -1) {
String8 line(str, index - lastIndex);
if (line.length() == 0) line.append("\n");
- PROGRAM_LOGD("%s", line.string());
+ ALOGD("%s", line.string());
index++;
str += (index - lastIndex);
lastIndex = index;
diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h
index 38f6f99..1dadda1 100644
--- a/libs/hwui/ProgramCache.h
+++ b/libs/hwui/ProgramCache.h
@@ -20,12 +20,12 @@
#include <utils/KeyedVector.h>
#include <utils/Log.h>
#include <utils/String8.h>
+#include <map>
#include <GLES2/gl2.h>
#include "Debug.h"
#include "Program.h"
-#include "Properties.h"
namespace android {
namespace uirenderer {
@@ -40,7 +40,7 @@ namespace uirenderer {
*/
class ProgramCache {
public:
- ProgramCache();
+ ProgramCache(Extensions& extensions);
~ProgramCache();
Program* get(const ProgramDescription& description);
@@ -56,7 +56,7 @@ private:
void printLongString(const String8& shader) const;
- KeyedVector<programid, Program*> mCache;
+ std::map<programid, std::unique_ptr<Program>> mCache;
const bool mHasES3;
}; // class ProgramCache
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index befed16..a0312e1 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -253,9 +253,9 @@ enum DebugLevel {
// Converts a number of kilo-bytes into bytes
#define KB(s) s * 1024
-static DebugLevel readDebugLevel() {
+static inline DebugLevel readDebugLevel() {
char property[PROPERTY_VALUE_MAX];
- if (property_get(PROPERTY_DEBUG, property, NULL) > 0) {
+ if (property_get(PROPERTY_DEBUG, property, nullptr) > 0) {
return (DebugLevel) atoi(property);
}
return kDebugDisabled;
diff --git a/libs/hwui/Query.h b/libs/hwui/Query.h
deleted file mode 100644
index e25b16b..0000000
--- a/libs/hwui/Query.h
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2013 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_QUERY_H
-#define ANDROID_HWUI_QUERY_H
-
-#include <GLES3/gl3.h>
-
-#include "Extensions.h"
-
-namespace android {
-namespace uirenderer {
-
-/**
- * A Query instance can be used to perform occlusion queries. If the device
- * does not support occlusion queries, the result of a query will always be
- * 0 and the result will always be marked available.
- *
- * To run an occlusion query successfully, you must start end end the query:
- *
- * Query query;
- * query.begin();
- * // execute OpenGL calls
- * query.end();
- * GLuint result = query.getResult();
- */
-class Query {
-public:
- /**
- * Possible query targets.
- */
- enum Target {
- /**
- * Indicates if any sample passed the depth & stencil tests.
- */
- kTargetSamples = GL_ANY_SAMPLES_PASSED,
- /**
- * Indicates if any sample passed the depth & stencil tests.
- * The implementation may choose to use a less precise version
- * of the test, potentially resulting in false positives.
- */
- kTargetConservativeSamples = GL_ANY_SAMPLES_PASSED_CONSERVATIVE,
- };
-
- /**
- * Creates a new query with the specified target. The default
- * target is kTargetSamples (of GL_ANY_SAMPLES_PASSED in OpenGL.)
- */
- Query(Target target = kTargetSamples): mActive(false), mTarget(target),
- mCanQuery(Extensions::getInstance().hasOcclusionQueries()),
- mQuery(0) {
- }
-
- ~Query() {
- if (mQuery) {
- glDeleteQueries(1, &mQuery);
- }
- }
-
- /**
- * Begins the query. If the query has already begun or if the device
- * does not support occlusion queries, calling this method as no effect.
- * After calling this method successfully, the query is marked active.
- */
- void begin() {
- if (!mActive && mCanQuery) {
- if (!mQuery) {
- glGenQueries(1, &mQuery);
- }
-
- glBeginQuery(mTarget, mQuery);
- mActive = true;
- }
- }
-
- /**
- * Ends the query. If the query has already begun or if the device
- * does not support occlusion queries, calling this method as no effect.
- * After calling this method successfully, the query is marked inactive.
- */
- void end() {
- if (mQuery && mActive) {
- glEndQuery(mTarget);
- mActive = false;
- }
- }
-
- /**
- * Returns true if the query is active, false otherwise.
- */
- bool isActive() {
- return mActive;
- }
-
- /**
- * Returns true if the result of the query is available,
- * false otherwise. Calling getResult() before the result
- * is available may result in the calling thread being blocked.
- * If the device does not support queries, this method always
- * returns true.
- */
- bool isResultAvailable() {
- if (!mQuery) return true;
-
- GLuint result;
- glGetQueryObjectuiv(mQuery, GL_QUERY_RESULT_AVAILABLE, &result);
- return result == GL_TRUE;
- }
-
- /**
- * Returns the result of the query. If the device does not
- * support queries this method will return 0.
- *
- * Calling this method implicitely calls end() if the query
- * is currently active.
- */
- GLuint getResult() {
- if (!mQuery) return 0;
-
- end();
-
- GLuint result;
- glGetQueryObjectuiv(mQuery, GL_QUERY_RESULT, &result);
- return result;
- }
-
-
-private:
- bool mActive;
- GLenum mTarget;
- bool mCanQuery;
- GLuint mQuery;
-
-}; // class Query
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_QUERY_H
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index 13265a9..c82082f 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -111,6 +111,10 @@ public:
set(r.left, r.top, r.right, r.bottom);
}
+ inline void set(const SkIRect& r) {
+ set(r.left(), r.top(), r.right(), r.bottom());
+ }
+
inline float getWidth() const {
return right - left;
}
@@ -248,7 +252,22 @@ public:
bottom = fmaxf(bottom, y);
}
- void dump(const char* label = NULL) const {
+ void expandToCoverRect(float otherLeft, float otherTop, float otherRight, float otherBottom) {
+ left = fminf(left, otherLeft);
+ top = fminf(top, otherTop);
+ right = fmaxf(right, otherRight);
+ bottom = fmaxf(bottom, otherBottom);
+ }
+
+ SkRect toSkRect() const {
+ return SkRect::MakeLTRB(left, top, right, bottom);
+ }
+
+ SkIRect toSkIRect() const {
+ return SkIRect::MakeLTRB(left, top, right, bottom);
+ }
+
+ void dump(const char* label = nullptr) const {
ALOGD("%s[l=%f t=%f r=%f b=%f]", label ? label : "Rect", left, top, right, bottom);
}
diff --git a/libs/hwui/RenderBufferCache.cpp b/libs/hwui/RenderBufferCache.cpp
index 830a13a..d0812c9 100644
--- a/libs/hwui/RenderBufferCache.cpp
+++ b/libs/hwui/RenderBufferCache.cpp
@@ -42,7 +42,7 @@ namespace uirenderer {
RenderBufferCache::RenderBufferCache(): mSize(0), mMaxSize(MB(DEFAULT_RENDER_BUFFER_CACHE_SIZE)) {
char property[PROPERTY_VALUE_MAX];
- if (property_get(PROPERTY_RENDER_BUFFER_CACHE_SIZE, property, NULL) > 0) {
+ if (property_get(PROPERTY_RENDER_BUFFER_CACHE_SIZE, property, nullptr) > 0) {
INIT_LOGD(" Setting render buffer cache size to %sMB", property);
setMaxSize(MB(atof(property)));
} else {
@@ -108,7 +108,7 @@ void RenderBufferCache::clear() {
}
RenderBuffer* RenderBufferCache::get(GLenum format, const uint32_t width, const uint32_t height) {
- RenderBuffer* buffer = NULL;
+ RenderBuffer* buffer = nullptr;
RenderBufferEntry entry(format, width, height);
ssize_t index = mCache.indexOf(entry);
@@ -158,6 +158,11 @@ bool RenderBufferCache::put(RenderBuffer* buffer) {
buffer->getWidth(), buffer->getHeight());
return true;
+ } else {
+ RENDER_BUFFER_LOGD("Deleted %s render buffer (%dx%d) Size=%d, MaxSize=%d",
+ RenderBuffer::formatName(buffer->getFormat()),
+ buffer->getWidth(), buffer->getHeight(), size, mMaxSize);
+ delete buffer;
}
return false;
}
diff --git a/libs/hwui/RenderBufferCache.h b/libs/hwui/RenderBufferCache.h
index af8060f..6c668b0 100644
--- a/libs/hwui/RenderBufferCache.h
+++ b/libs/hwui/RenderBufferCache.h
@@ -78,11 +78,11 @@ public:
private:
struct RenderBufferEntry {
RenderBufferEntry():
- mBuffer(NULL), mWidth(0), mHeight(0) {
+ mBuffer(nullptr), mWidth(0), mHeight(0) {
}
RenderBufferEntry(GLenum format, const uint32_t width, const uint32_t height):
- mBuffer(NULL), mFormat(format), mWidth(width), mHeight(height) {
+ mBuffer(nullptr), mFormat(format), mWidth(width), mHeight(height) {
}
RenderBufferEntry(RenderBuffer* buffer):
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 787ee62..e009451 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -29,9 +29,9 @@
#include "DamageAccumulator.h"
#include "Debug.h"
#include "DisplayListOp.h"
-#include "DisplayListLogBuffer.h"
#include "LayerRenderer.h"
#include "OpenGLRenderer.h"
+#include "TreeInfo.h"
#include "utils/MathUtils.h"
#include "utils/TraceUtils.h"
#include "renderthread/CanvasContext.h"
@@ -39,28 +39,6 @@
namespace android {
namespace uirenderer {
-void RenderNode::outputLogBuffer(int fd) {
- DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
- if (logBuffer.isEmpty()) {
- return;
- }
-
- FILE *file = fdopen(fd, "a");
-
- fprintf(file, "\nRecent DisplayList operations\n");
- logBuffer.outputCommands(file);
-
- if (Caches::hasInstance()) {
- String8 cachesLog;
- Caches::getInstance().dumpMemoryUsage(cachesLog);
- fprintf(file, "\nCaches:\n%s\n", cachesLog.string());
- } else {
- fprintf(file, "\nNo caches instance.\n");
- }
-
- fflush(file);
-}
-
void RenderNode::debugDumpLayers(const char* prefix) {
if (mLayer) {
ALOGD("%sNode %p (%s) has layer %p (fbo = %u, wasBuildLayered = %s)",
@@ -77,10 +55,10 @@ void RenderNode::debugDumpLayers(const char* prefix) {
RenderNode::RenderNode()
: mDirtyPropertyFields(0)
, mNeedsDisplayListDataSync(false)
- , mDisplayListData(0)
- , mStagingDisplayListData(0)
+ , mDisplayListData(nullptr)
+ , mStagingDisplayListData(nullptr)
, mAnimatorManager(*this)
- , mLayer(0)
+ , mLayer(nullptr)
, mParentCount(0) {
}
@@ -90,7 +68,7 @@ RenderNode::~RenderNode() {
if (mLayer) {
ALOGW("Memory Warning: Layer %p missed its detachment, held on to for far too long!", mLayer);
mLayer->postDecStrong();
- mLayer = 0;
+ mLayer = nullptr;
}
}
@@ -109,7 +87,7 @@ void RenderNode::output(uint32_t level) {
getName(),
(properties().hasShadow() ? ", casting shadow" : ""),
(isRenderable() ? "" : ", empty"),
- (mLayer != NULL ? ", on HW Layer" : ""));
+ (mLayer != nullptr ? ", on HW Layer" : ""));
ALOGD("%*s%s %d", level * 2, "", "Save",
SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
@@ -161,7 +139,7 @@ void RenderNode::damageSelf(TreeInfo& info) {
void RenderNode::prepareLayer(TreeInfo& info, uint32_t dirtyMask) {
LayerType layerType = properties().layerProperties().type();
- if (CC_UNLIKELY(layerType == kLayerTypeRenderLayer)) {
+ if (CC_UNLIKELY(layerType == LayerType::RenderLayer)) {
// Damage applied so far needs to affect our parent, but does not require
// the layer to be updated. So we pop/push here to clear out the current
// damage and get a clean state for display list or children updates to
@@ -178,10 +156,10 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) {
LayerType layerType = properties().layerProperties().type();
// If we are not a layer OR we cannot be rendered (eg, view was detached)
// we need to destroy any Layers we may have had previously
- if (CC_LIKELY(layerType != kLayerTypeRenderLayer) || CC_UNLIKELY(!isRenderable())) {
+ if (CC_LIKELY(layerType != LayerType::RenderLayer) || CC_UNLIKELY(!isRenderable())) {
if (CC_UNLIKELY(mLayer)) {
LayerRenderer::destroyLayer(mLayer);
- mLayer = NULL;
+ mLayer = nullptr;
}
return;
}
@@ -195,7 +173,7 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) {
} else if (mLayer->layer.getWidth() != getWidth() || mLayer->layer.getHeight() != getHeight()) {
if (!LayerRenderer::resizeLayer(mLayer, getWidth(), getHeight())) {
LayerRenderer::destroyLayer(mLayer);
- mLayer = 0;
+ mLayer = nullptr;
}
damageSelf(info);
transformUpdateNeeded = true;
@@ -222,7 +200,7 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) {
}
if (dirty.intersect(0, 0, getWidth(), getHeight())) {
- dirty.roundOut();
+ dirty.roundOut(&dirty);
mLayer->updateDeferred(this, dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom);
}
// This is not inside the above if because we may have called
@@ -310,10 +288,10 @@ void RenderNode::pushStagingDisplayListChanges(TreeInfo& info) {
Caches::getInstance().registerFunctors(mStagingDisplayListData->functors.size());
}
mDisplayListData = mStagingDisplayListData;
- mStagingDisplayListData = NULL;
+ mStagingDisplayListData = nullptr;
if (mDisplayListData) {
for (size_t i = 0; i < mDisplayListData->functors.size(); i++) {
- (*mDisplayListData->functors[i])(DrawGlInfo::kModeSync, NULL);
+ (*mDisplayListData->functors[i])(DrawGlInfo::kModeSync, nullptr);
}
}
damageSelf(info);
@@ -330,18 +308,13 @@ void RenderNode::deleteDisplayListData() {
}
}
delete mDisplayListData;
- mDisplayListData = NULL;
+ mDisplayListData = nullptr;
}
void RenderNode::prepareSubTree(TreeInfo& info, DisplayListData* subtree) {
if (subtree) {
TextureCache& cache = Caches::getInstance().textureCache;
info.out.hasFunctors |= subtree->functors.size();
- // TODO: Fix ownedBitmapResources to not require disabling prepareTextures
- // and thus falling out of async drawing path.
- if (subtree->ownedBitmapResources.size()) {
- info.prepareTextures = false;
- }
for (size_t i = 0; info.prepareTextures && i < subtree->bitmapResources.size(); i++) {
info.prepareTextures = cache.prefetchAndMarkInUse(subtree->bitmapResources[i]);
}
@@ -358,7 +331,7 @@ void RenderNode::prepareSubTree(TreeInfo& info, DisplayListData* subtree) {
void RenderNode::destroyHardwareResources() {
if (mLayer) {
LayerRenderer::destroyLayer(mLayer);
- mLayer = NULL;
+ mLayer = nullptr;
}
if (mDisplayListData) {
for (size_t i = 0; i < mDisplayListData->children().size(); i++) {
@@ -411,7 +384,7 @@ void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler) {
renderer.concatMatrix(*properties().getTransformMatrix());
}
}
- const bool isLayer = properties().layerProperties().type() != kLayerTypeNone;
+ const bool isLayer = properties().layerProperties().type() != LayerType::None;
int clipFlags = properties().getClippingFlags();
if (properties().getAlpha() < 1) {
if (isLayer) {
@@ -429,9 +402,10 @@ void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler) {
clipFlags = 0; // all clipping done by saveLayer
}
- ATRACE_FORMAT("%s alpha caused %ssaveLayer %dx%d",
- getName(), clipFlags ? "" : "unclipped ",
- (int)layerBounds.getWidth(), (int)layerBounds.getHeight());
+ ATRACE_FORMAT("%s alpha caused %ssaveLayer %dx%d", getName(),
+ (saveFlags & SkCanvas::kClipToLayer_SaveFlag) ? "" : "unclipped ",
+ static_cast<int>(layerBounds.getWidth()),
+ static_cast<int>(layerBounds.getHeight()));
SaveLayerOp* op = new (handler.allocator()) SaveLayerOp(
layerBounds.left, layerBounds.top, layerBounds.right, layerBounds.bottom,
@@ -516,7 +490,7 @@ void RenderNode::computeOrdering() {
// TODO: create temporary DDLOp and call computeOrderingImpl on top DisplayList so that
// transform properties are applied correctly to top level children
- if (mDisplayListData == NULL) return;
+ if (mDisplayListData == nullptr) return;
for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) {
DrawRenderNodeOp* childOp = mDisplayListData->children()[i];
childOp->mRenderNode->computeOrderingImpl(childOp,
@@ -530,7 +504,7 @@ void RenderNode::computeOrderingImpl(
Vector<DrawRenderNodeOp*>* compositedChildrenOfProjectionSurface,
const mat4* transformFromProjectionSurface) {
mProjectedNodes.clear();
- if (mDisplayListData == NULL || mDisplayListData->isEmpty()) return;
+ if (mDisplayListData == nullptr || mDisplayListData->isEmpty()) return;
// TODO: should avoid this calculation in most cases
// TODO: just calculate single matrix, down to all leaf composited elements
@@ -554,9 +528,9 @@ void RenderNode::computeOrderingImpl(
DrawRenderNodeOp* childOp = mDisplayListData->children()[i];
RenderNode* child = childOp->mRenderNode;
- const SkPath* projectionOutline = NULL;
- Vector<DrawRenderNodeOp*>* projectionChildren = NULL;
- const mat4* projectionTransform = NULL;
+ const SkPath* projectionOutline = nullptr;
+ Vector<DrawRenderNodeOp*>* projectionChildren = nullptr;
+ const mat4* projectionTransform = nullptr;
if (isProjectionReceiver && !child->properties().getProjectBackwards()) {
// if receiving projections, collect projecting descendent
@@ -682,7 +656,7 @@ void RenderNode::issueDrawShadowOperation(const Matrix4& transformFromParent, T&
// holds temporary SkPath to store the result of intersections
- SkPath* frameAllocatedPath = NULL;
+ SkPath* frameAllocatedPath = nullptr;
const SkPath* outlinePath = casterOutlinePath;
// intersect the outline with the reveal clip, if present
@@ -777,7 +751,6 @@ void RenderNode::issueOperationsOf3dChildren(ChildrenSelectMode mode,
int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
DrawRenderNodeOp* childOp = zTranslatedNodes[drawIndex].value;
- RenderNode* child = childOp->mRenderNode;
renderer.concatMatrix(childOp->mTransformFromParent);
childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
@@ -810,9 +783,9 @@ void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T&
// If the projection reciever has an outline, we mask each of the projected rendernodes to it
// Either with clipRect, or special saveLayer masking
- if (projectionReceiverOutline != NULL) {
+ if (projectionReceiverOutline != nullptr) {
const SkRect& outlineBounds = projectionReceiverOutline->getBounds();
- if (projectionReceiverOutline->isRect(NULL)) {
+ if (projectionReceiverOutline->isRect(nullptr)) {
// mask to the rect outline simply with clipRect
ClipRectOp* clipOp = new (alloc) ClipRectOp(
outlineBounds.left(), outlineBounds.top(),
@@ -847,7 +820,7 @@ void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T&
renderer.restoreToCount(restoreTo);
}
- if (projectionReceiverOutline != NULL) {
+ if (projectionReceiverOutline != nullptr) {
handler(new (alloc) RestoreToCountOp(restoreTo),
PROPERTY_SAVECOUNT, properties().getClipToBounds());
}
@@ -864,13 +837,12 @@ void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T&
*/
template <class T>
void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
- const int level = handler.level();
if (mDisplayListData->isEmpty()) {
DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, getName());
return;
}
- const bool drawLayer = (mLayer && (&renderer != mLayer->renderer));
+ const bool drawLayer = (mLayer && (&renderer != mLayer->renderer.get()));
// If we are updating the contents of mLayer, we don't want to apply any of
// the RenderNode's properties to this issueOperations pass. Those will all
// be applied when the layer is drawn, aka when this is true.
@@ -888,7 +860,7 @@ void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
#if DEBUG_DISPLAY_LIST
const Rect& clipRect = renderer.getLocalClipBounds();
DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), localClipBounds: %.0f, %.0f, %.0f, %.0f",
- level * 2, "", this, getName(),
+ handler.level() * 2, "", this, getName(),
clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
#endif
@@ -915,7 +887,6 @@ void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
} else {
const int saveCountOffset = renderer.getSaveCount() - 1;
const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex;
- DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
for (size_t chunkIndex = 0; chunkIndex < mDisplayListData->getChunks().size(); chunkIndex++) {
const DisplayListData::Chunk& chunk = mDisplayListData->getChunks()[chunkIndex];
@@ -926,15 +897,15 @@ void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
initialTransform, zTranslatedNodes, renderer, handler);
- for (int opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
+ for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
DisplayListOp *op = mDisplayListData->displayListOps[opIndex];
#if DEBUG_DISPLAY_LIST
- op->output(level + 1);
+ op->output(handler.level() + 1);
#endif
- logBuffer.writeCommand(level, op->name());
handler(op, saveCountOffset, properties().getClipToBounds());
- if (CC_UNLIKELY(!mProjectedNodes.isEmpty() && opIndex == projectionReceiveIndex)) {
+ if (CC_UNLIKELY(!mProjectedNodes.isEmpty() && projectionReceiveIndex >= 0 &&
+ opIndex == static_cast<size_t>(projectionReceiveIndex))) {
issueOperationsOfProjectedChildren(renderer, handler);
}
}
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 2ce7cb7..d0d81d9 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -33,13 +33,10 @@
#include <androidfw/ResourceTypes.h>
#include "AnimatorManager.h"
-#include "DamageAccumulator.h"
#include "Debug.h"
#include "Matrix.h"
-#include "DeferredDisplayList.h"
#include "DisplayList.h"
#include "RenderProperties.h"
-#include "TreeInfo.h"
class SkBitmap;
class SkPaint;
@@ -50,7 +47,7 @@ namespace android {
namespace uirenderer {
class DisplayListOp;
-class DisplayListRenderer;
+class DisplayListCanvas;
class OpenGLRenderer;
class Rect;
class Layer;
@@ -61,12 +58,13 @@ class SaveLayerOp;
class SaveOp;
class RestoreToCountOp;
class DrawRenderNodeOp;
+class TreeInfo;
/**
* Primary class for storing recorded canvas commands, as well as per-View/ViewGroup display properties.
*
* Recording of canvas commands is somewhat similar to SkPicture, except the canvas-recording
- * functionality is split between DisplayListRenderer (which manages the recording), DisplayListData
+ * functionality is split between DisplayListCanvas (which manages the recording), DisplayListData
* (which holds the actual data), and DisplayList (which holds properties and performs playback onto
* a renderer).
*
diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp
index 250cadc..9f1ceed 100644
--- a/libs/hwui/RenderProperties.cpp
+++ b/libs/hwui/RenderProperties.cpp
@@ -33,19 +33,17 @@
namespace android {
namespace uirenderer {
-LayerProperties::LayerProperties()
- : mType(kLayerTypeNone)
- , mColorFilter(NULL) {
+LayerProperties::LayerProperties() {
reset();
}
LayerProperties::~LayerProperties() {
- setType(kLayerTypeNone);
+ setType(LayerType::None);
}
void LayerProperties::reset() {
mOpaque = false;
- setFromPaint(NULL);
+ setFromPaint(nullptr);
}
bool LayerProperties::setColorFilter(SkColorFilter* filter) {
@@ -61,7 +59,7 @@ bool LayerProperties::setFromPaint(const SkPaint* paint) {
OpenGLRenderer::getAlphaAndModeDirect(paint, &alpha, &mode);
changed |= setAlpha(static_cast<uint8_t>(alpha));
changed |= setXferMode(mode);
- changed |= setColorFilter(paint ? paint->getColorFilter() : NULL);
+ changed |= setColorFilter(paint ? paint->getColorFilter() : nullptr);
return changed;
}
@@ -92,7 +90,7 @@ RenderProperties::PrimitiveFields::PrimitiveFields()
}
RenderProperties::ComputedFields::ComputedFields()
- : mTransformMatrix(NULL) {
+ : mTransformMatrix(nullptr) {
}
RenderProperties::ComputedFields::~ComputedFields() {
@@ -100,8 +98,8 @@ RenderProperties::ComputedFields::~ComputedFields() {
}
RenderProperties::RenderProperties()
- : mStaticMatrix(NULL)
- , mAnimationMatrix(NULL) {
+ : mStaticMatrix(nullptr)
+ , mAnimationMatrix(nullptr) {
}
RenderProperties::~RenderProperties() {
@@ -146,7 +144,7 @@ void RenderProperties::debugOutputProperties(const int level) const {
}
}
- const bool isLayer = layerProperties().type() != kLayerTypeNone;
+ const bool isLayer = layerProperties().type() != LayerType::None;
int clipFlags = getClippingFlags();
if (mPrimitiveFields.mAlpha < 1) {
if (isLayer) {
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index 31c4f22..61e98d2 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -26,8 +26,8 @@
#include <SkCamera.h>
#include <SkMatrix.h>
#include <SkRegion.h>
+#include <SkXfermode.h>
-#include "Animator.h"
#include "Rect.h"
#include "RevealClip.h"
#include "Outline.h"
@@ -49,12 +49,12 @@ class RenderProperties;
#define RP_SET_AND_DIRTY(a, b) RP_SET(a, b, mPrimitiveFields.mMatrixOrPivotDirty = true)
// Keep in sync with View.java:LAYER_TYPE_*
-enum LayerType {
- kLayerTypeNone = 0,
+enum class LayerType {
+ None = 0,
// Although we cannot build the software layer directly (must be done at
// record time), this information is used when applying alpha.
- kLayerTypeSoftware = 1,
- kLayerTypeRenderLayer = 2,
+ Software = 1,
+ RenderLayer = 2,
// TODO: LayerTypeSurfaceTexture? Maybe?
};
@@ -124,12 +124,12 @@ private:
friend class RenderProperties;
- LayerType mType;
+ LayerType mType = LayerType::None;
// Whether or not that Layer's content is opaque, doesn't include alpha
bool mOpaque;
uint8_t mAlpha;
SkXfermode::Mode mMode;
- SkColorFilter* mColorFilter;
+ SkColorFilter* mColorFilter = nullptr;
};
/*
@@ -188,7 +188,7 @@ public:
if (matrix) {
mStaticMatrix = new SkMatrix(*matrix);
} else {
- mStaticMatrix = NULL;
+ mStaticMatrix = nullptr;
}
return true;
}
@@ -203,7 +203,7 @@ public:
if (matrix) {
mAnimationMatrix = new SkMatrix(*matrix);
} else {
- mAnimationMatrix = NULL;
+ mAnimationMatrix = nullptr;
}
return true;
}
@@ -571,7 +571,7 @@ public:
bool hasShadow() const {
return getZ() > 0.0f
- && getOutline().getPath() != NULL
+ && getOutline().getPath() != nullptr
&& getOutline().getAlpha() != 0.0f;
}
diff --git a/libs/hwui/RenderState.cpp b/libs/hwui/RenderState.cpp
deleted file mode 100644
index ca640e5..0000000
--- a/libs/hwui/RenderState.cpp
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * 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.h"
-
-#include "renderthread/CanvasContext.h"
-#include "renderthread/EglManager.h"
-
-namespace android {
-namespace uirenderer {
-
-RenderState::RenderState(renderthread::RenderThread& thread)
- : mRenderThread(thread)
- , mCaches(NULL)
- , mViewportWidth(0)
- , mViewportHeight(0)
- , mFramebuffer(0) {
- mThreadId = pthread_self();
-}
-
-RenderState::~RenderState() {
-}
-
-void RenderState::onGLContextCreated() {
- // This is delayed because the first access of Caches makes GL calls
- mCaches = &Caches::getInstance();
- mCaches->init();
- mCaches->setRenderState(this);
- 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);
- }
-*/
- std::for_each(mActiveLayers.begin(), mActiveLayers.end(), layerLostGlContext);
- mAssetAtlas.terminate();
-}
-
-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() {
- if (mCaches->currentProgram) {
- if (mCaches->currentProgram->isInUse()) {
- mCaches->currentProgram->remove();
- mCaches->currentProgram = NULL;
- }
- }
- mCaches->resetActiveTexture();
- mCaches->unbindMeshBuffer();
- mCaches->unbindIndicesBuffer();
- mCaches->resetVertexPointers();
- mCaches->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);
-
- mCaches->scissorEnabled = glIsEnabled(GL_SCISSOR_TEST);
- mCaches->enableScissor();
- mCaches->resetScissor();
-
- mCaches->activeTexture(0);
- mCaches->resetBoundTextures();
-
- mCaches->blend = true;
- glEnable(GL_BLEND);
- glBlendFunc(mCaches->lastSrcMode, mCaches->lastDstMode);
- glBlendEquation(GL_FUNC_ADD);
-}
-
-void RenderState::debugOverdraw(bool enable, bool clear) {
- if (mCaches->debugOverdraw && mFramebuffer == 0) {
- if (clear) {
- mCaches->disableScissor();
- mCaches->stencil.clear();
- }
- if (enable) {
- mCaches->stencil.enableDebugWrite();
- } else {
- mCaches->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() {
- mObject->decStrong(0);
- mObject = 0;
- delete this;
- }
-
-private:
- VirtualLightRefBase* mObject;
-};
-
-void RenderState::postDecStrong(VirtualLightRefBase* object) {
- mRenderThread.queue(new DecStrongTask(object));
-}
-
-} /* namespace uirenderer */
-} /* namespace android */
diff --git a/libs/hwui/Renderer.h b/libs/hwui/Renderer.h
deleted file mode 100644
index 3159d1e..0000000
--- a/libs/hwui/Renderer.h
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Copyright (C) 2013 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_RENDERER_H
-#define ANDROID_HWUI_RENDERER_H
-
-#include <SkColorFilter.h>
-#include <SkPaint.h>
-#include <SkRegion.h>
-
-#include <utils/String8.h>
-
-#include "AssetAtlas.h"
-
-namespace android {
-
-class Functor;
-struct Res_png_9patch;
-
-namespace uirenderer {
-
-class RenderNode;
-class Layer;
-class Matrix4;
-class SkiaColorFilter;
-class Patch;
-
-enum DrawOpMode {
- kDrawOpMode_Immediate,
- kDrawOpMode_Defer,
- kDrawOpMode_Flush
-};
-
-/**
- * Hwui's abstract version of Canvas.
- *
- * Provides methods for frame state operations, as well as the SkCanvas style transform/clip state,
- * and varied drawing operations.
- *
- * Should at some point interact with native SkCanvas.
- */
-class ANDROID_API Renderer {
-public:
- virtual ~Renderer() {}
-
- /**
- * Safely retrieves the mode from the specified xfermode. If the specified
- * xfermode is null, the mode is assumed to be SkXfermode::kSrcOver_Mode.
- */
- static inline SkXfermode::Mode getXfermode(SkXfermode* mode) {
- SkXfermode::Mode resultMode;
- if (!SkXfermode::AsMode(mode, &resultMode)) {
- resultMode = SkXfermode::kSrcOver_Mode;
- }
- return resultMode;
- }
-
- // TODO: move to a method on android:Paint
- static inline bool paintWillNotDraw(const SkPaint& paint) {
- return paint.getAlpha() == 0
- && !paint.getColorFilter()
- && getXfermode(paint.getXfermode()) == SkXfermode::kSrcOver_Mode;
- }
-
- // TODO: move to a method on android:Paint
- static inline bool paintWillNotDrawText(const SkPaint& paint) {
- return paint.getAlpha() == 0
- && paint.getLooper() == NULL
- && !paint.getColorFilter()
- && getXfermode(paint.getXfermode()) == SkXfermode::kSrcOver_Mode;
- }
-
- static bool isBlendedColorFilter(const SkColorFilter* filter) {
- if (filter == NULL) {
- return false;
- }
- return (filter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag) == 0;
- }
-
-// ----------------------------------------------------------------------------
-// Frame state operations
-// ----------------------------------------------------------------------------
- /**
- * Sets the dimension of the underlying drawing surface. This method must
- * be called at least once every time the drawing surface changes size.
- *
- * @param width The width in pixels of the underlysing surface
- * @param height The height in pixels of the underlysing surface
- */
- virtual void setViewport(int width, int height) = 0;
-
- /**
- * Prepares the renderer to draw a frame. This method must be invoked
- * at the beginning of each frame. When this method is invoked, the
- * entire drawing surface is assumed to be redrawn.
- *
- * @param opaque If true, the target surface is considered opaque
- * and will not be cleared. If false, the target surface
- * will be cleared
- */
- virtual status_t prepare(bool opaque) = 0;
-
- /**
- * Prepares the renderer to draw a frame. This method must be invoked
- * at the beginning of each frame. Only the specified rectangle of the
- * frame is assumed to be dirty. A clip will automatically be set to
- * the specified rectangle.
- *
- * @param left The left coordinate of the dirty rectangle
- * @param top The top coordinate of the dirty rectangle
- * @param right The right coordinate of the dirty rectangle
- * @param bottom The bottom coordinate of the dirty rectangle
- * @param opaque If true, the target surface is considered opaque
- * and will not be cleared. If false, the target surface
- * will be cleared in the specified dirty rectangle
- */
- virtual status_t prepareDirty(float left, float top, float right, float bottom,
- bool opaque) = 0;
-
- /**
- * Indicates the end of a frame. This method must be invoked whenever
- * the caller is done rendering a frame.
- */
- virtual void finish() = 0;
-
-// ----------------------------------------------------------------------------
-// Canvas state operations
-// ----------------------------------------------------------------------------
- // Save (layer)
- virtual int getSaveCount() const = 0;
- virtual int save(int flags) = 0;
- virtual void restore() = 0;
- virtual void restoreToCount(int saveCount) = 0;
-
- virtual int saveLayer(float left, float top, float right, float bottom,
- const SkPaint* paint, int flags) = 0;
-
- int saveLayerAlpha(float left, float top, float right, float bottom,
- int alpha, int flags) {
- SkPaint paint;
- paint.setAlpha(alpha);
- return saveLayer(left, top, right, bottom, &paint, flags);
- }
-
- // Matrix
- virtual void getMatrix(SkMatrix* outMatrix) const = 0;
- virtual void translate(float dx, float dy, float dz = 0.0f) = 0;
- virtual void rotate(float degrees) = 0;
- virtual void scale(float sx, float sy) = 0;
- virtual void skew(float sx, float sy) = 0;
-
- virtual void setMatrix(const SkMatrix& matrix) = 0;
- virtual void concatMatrix(const SkMatrix& matrix) = 0;
-
- // clip
- virtual const Rect& getLocalClipBounds() const = 0;
- virtual bool quickRejectConservative(float left, float top,
- float right, float bottom) const = 0;
- virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op) = 0;
- virtual bool clipPath(const SkPath* path, SkRegion::Op op) = 0;
- virtual bool clipRegion(const SkRegion* region, SkRegion::Op op) = 0;
-
- // Misc - should be implemented with SkPaint inspection
- virtual void resetPaintFilter() = 0;
- virtual void setupPaintFilter(int clearBits, int setBits) = 0;
-
-// ----------------------------------------------------------------------------
-// Canvas draw operations
-// ----------------------------------------------------------------------------
- virtual status_t drawColor(int color, SkXfermode::Mode mode) = 0;
-
- // Bitmap-based
- virtual status_t drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) = 0;
- virtual status_t drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop,
- float srcRight, float srcBottom, float dstLeft, float dstTop,
- float dstRight, float dstBottom, const SkPaint* paint) = 0;
- virtual status_t drawBitmapData(const SkBitmap* bitmap, const SkPaint* paint) = 0;
- virtual status_t drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight,
- const float* vertices, const int* colors, const SkPaint* paint) = 0;
- virtual status_t drawPatch(const SkBitmap* bitmap, const Res_png_9patch* patch,
- float left, float top, float right, float bottom, const SkPaint* paint) = 0;
-
- // Shapes
- virtual status_t drawRect(float left, float top, float right, float bottom,
- const SkPaint* paint) = 0;
- virtual status_t drawRects(const float* rects, int count, const SkPaint* paint) = 0;
- virtual status_t drawRoundRect(float left, float top, float right, float bottom,
- float rx, float ry, const SkPaint* paint) = 0;
- virtual status_t drawCircle(float x, float y, float radius, const SkPaint* paint) = 0;
- virtual status_t drawOval(float left, float top, float right, float bottom,
- const SkPaint* paint) = 0;
- virtual status_t drawArc(float left, float top, float right, float bottom,
- float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint) = 0;
- virtual status_t drawPath(const SkPath* path, const SkPaint* paint) = 0;
- virtual status_t drawLines(const float* points, int count, const SkPaint* paint) = 0;
- virtual status_t drawPoints(const float* points, int count, const SkPaint* paint) = 0;
-
- // Text
- virtual status_t drawText(const char* text, int bytesCount, int count, float x, float y,
- const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds,
- DrawOpMode drawOpMode = kDrawOpMode_Immediate) = 0;
- virtual status_t drawTextOnPath(const char* text, int bytesCount, int count, const SkPath* path,
- float hOffset, float vOffset, const SkPaint* paint) = 0;
- virtual status_t drawPosText(const char* text, int bytesCount, int count,
- const float* positions, const SkPaint* paint) = 0;
-
-// ----------------------------------------------------------------------------
-// Canvas draw operations - special
-// ----------------------------------------------------------------------------
- virtual status_t drawRenderNode(RenderNode* renderNode, Rect& dirty,
- int32_t replayFlags) = 0;
-
- // TODO: rename for consistency
- virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty) = 0;
-}; // class Renderer
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_RENDERER_H
diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp
index 31bd637..d3b8d70 100644
--- a/libs/hwui/ResourceCache.cpp
+++ b/libs/hwui/ResourceCache.cpp
@@ -16,16 +16,13 @@
#define LOG_TAG "OpenGLRenderer"
-#include <SkPixelRef.h>
#include "ResourceCache.h"
#include "Caches.h"
namespace android {
-#ifdef USE_OPENGL_RENDERER
using namespace uirenderer;
ANDROID_SINGLETON_STATIC_INSTANCE(ResourceCache);
-#endif
namespace uirenderer {
@@ -39,8 +36,8 @@ void ResourceCache::logCache() {
ResourceReference* ref = mCache->valueAt(i);
ALOGD(" ResourceCache: mCache(%zu): resource, ref = 0x%p, 0x%p",
i, mCache->keyAt(i), mCache->valueAt(i));
- ALOGD(" ResourceCache: mCache(%zu): refCount, recycled, destroyed, type = %d, %d, %d, %d",
- i, ref->refCount, ref->recycled, ref->destroyed, ref->resourceType);
+ ALOGD(" ResourceCache: mCache(%zu): refCount, destroyed, type = %d, %d, %d",
+ i, ref->refCount, ref->destroyed, ref->resourceType);
}
}
@@ -62,17 +59,24 @@ void ResourceCache::unlock() {
mLock.unlock();
}
-void ResourceCache::incrementRefcount(void* resource, ResourceType resourceType) {
+const SkBitmap* ResourceCache::insert(const SkBitmap* bitmapResource) {
Mutex::Autolock _l(mLock);
- incrementRefcountLocked(resource, resourceType);
-}
-void ResourceCache::incrementRefcount(const SkBitmap* bitmapResource) {
- incrementRefcount((void*) bitmapResource, kBitmap);
+ BitmapKey bitmapKey(bitmapResource);
+ ssize_t index = mBitmapCache.indexOfKey(bitmapKey);
+ if (index == NAME_NOT_FOUND) {
+ SkBitmap* cachedBitmap = new SkBitmap(*bitmapResource);
+ index = mBitmapCache.add(bitmapKey, cachedBitmap);
+ return cachedBitmap;
+ }
+
+ mBitmapCache.keyAt(index).mRefCount++;
+ return mBitmapCache.valueAt(index);
}
-void ResourceCache::incrementRefcount(const SkPath* pathResource) {
- incrementRefcount((void*) pathResource, kPath);
+void ResourceCache::incrementRefcount(void* resource, ResourceType resourceType) {
+ Mutex::Autolock _l(mLock);
+ incrementRefcountLocked(resource, resourceType);
}
void ResourceCache::incrementRefcount(const Res_png_9patch* patchResource) {
@@ -81,37 +85,22 @@ void ResourceCache::incrementRefcount(const Res_png_9patch* patchResource) {
void ResourceCache::incrementRefcountLocked(void* resource, ResourceType resourceType) {
ssize_t index = mCache->indexOfKey(resource);
- ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL;
- if (ref == NULL || mCache->size() == 0) {
+ ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : nullptr;
+ if (ref == nullptr || mCache->size() == 0) {
ref = new ResourceReference(resourceType);
mCache->add(resource, ref);
}
ref->refCount++;
}
-void ResourceCache::incrementRefcountLocked(const SkBitmap* bitmapResource) {
- incrementRefcountLocked((void*) bitmapResource, kBitmap);
-}
-
-void ResourceCache::incrementRefcountLocked(const SkPath* pathResource) {
- incrementRefcountLocked((void*) pathResource, kPath);
-}
-
-void ResourceCache::incrementRefcountLocked(const Res_png_9patch* patchResource) {
- incrementRefcountLocked((void*) patchResource, kNinePatch);
-}
-
void ResourceCache::decrementRefcount(void* resource) {
Mutex::Autolock _l(mLock);
decrementRefcountLocked(resource);
}
void ResourceCache::decrementRefcount(const SkBitmap* bitmapResource) {
- decrementRefcount((void*) bitmapResource);
-}
-
-void ResourceCache::decrementRefcount(const SkPath* pathResource) {
- decrementRefcount((void*) pathResource);
+ Mutex::Autolock _l(mLock);
+ decrementRefcountLocked(bitmapResource);
}
void ResourceCache::decrementRefcount(const Res_png_9patch* patchResource) {
@@ -120,8 +109,8 @@ void ResourceCache::decrementRefcount(const Res_png_9patch* patchResource) {
void ResourceCache::decrementRefcountLocked(void* resource) {
ssize_t index = mCache->indexOfKey(resource);
- ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL;
- if (ref == NULL) {
+ ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : nullptr;
+ if (ref == nullptr) {
// Should not get here - shouldn't get a call to decrement if we're not yet tracking it
return;
}
@@ -132,62 +121,26 @@ void ResourceCache::decrementRefcountLocked(void* resource) {
}
void ResourceCache::decrementRefcountLocked(const SkBitmap* bitmapResource) {
- decrementRefcountLocked((void*) bitmapResource);
-}
-
-void ResourceCache::decrementRefcountLocked(const SkPath* pathResource) {
- decrementRefcountLocked((void*) pathResource);
+ BitmapKey bitmapKey(bitmapResource);
+ ssize_t index = mBitmapCache.indexOfKey(bitmapKey);
+
+ LOG_ALWAYS_FATAL_IF(index == NAME_NOT_FOUND,
+ "Decrementing the reference of an untracked Bitmap");
+
+ const BitmapKey& cacheEntry = mBitmapCache.keyAt(index);
+ if (cacheEntry.mRefCount == 1) {
+ // delete the bitmap and remove it from the cache
+ delete mBitmapCache.valueAt(index);
+ mBitmapCache.removeItemsAt(index);
+ } else {
+ cacheEntry.mRefCount--;
+ }
}
void ResourceCache::decrementRefcountLocked(const Res_png_9patch* patchResource) {
decrementRefcountLocked((void*) patchResource);
}
-void ResourceCache::destructor(SkPath* resource) {
- Mutex::Autolock _l(mLock);
- destructorLocked(resource);
-}
-
-void ResourceCache::destructorLocked(SkPath* resource) {
- ssize_t index = mCache->indexOfKey(resource);
- ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL;
- if (ref == NULL) {
- // If we're not tracking this resource, just delete it
- if (Caches::hasInstance()) {
- Caches::getInstance().pathCache.removeDeferred(resource);
- } else {
- delete resource;
- }
- return;
- }
- ref->destroyed = true;
- if (ref->refCount == 0) {
- deleteResourceReferenceLocked(resource, ref);
- }
-}
-
-void ResourceCache::destructor(const SkBitmap* resource) {
- Mutex::Autolock _l(mLock);
- destructorLocked(resource);
-}
-
-void ResourceCache::destructorLocked(const SkBitmap* resource) {
- ssize_t index = mCache->indexOfKey(resource);
- ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL;
- if (ref == NULL) {
- // If we're not tracking this resource, just delete it
- if (Caches::hasInstance()) {
- Caches::getInstance().textureCache.releaseTexture(resource);
- }
- delete resource;
- return;
- }
- ref->destroyed = true;
- if (ref->refCount == 0) {
- deleteResourceReferenceLocked(resource, ref);
- }
-}
-
void ResourceCache::destructor(Res_png_9patch* resource) {
Mutex::Autolock _l(mLock);
destructorLocked(resource);
@@ -195,8 +148,8 @@ void ResourceCache::destructor(Res_png_9patch* resource) {
void ResourceCache::destructorLocked(Res_png_9patch* resource) {
ssize_t index = mCache->indexOfKey(resource);
- ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL;
- if (ref == NULL) {
+ ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : nullptr;
+ if (ref == nullptr) {
// If we're not tracking this resource, just delete it
if (Caches::hasInstance()) {
Caches::getInstance().patchCache.removeDeferred(resource);
@@ -214,73 +167,12 @@ void ResourceCache::destructorLocked(Res_png_9patch* resource) {
}
/**
- * Return value indicates whether resource was actually recycled, which happens when RefCnt
- * reaches 0.
- */
-bool ResourceCache::recycle(SkBitmap* resource) {
- Mutex::Autolock _l(mLock);
- return recycleLocked(resource);
-}
-
-/**
- * Return value indicates whether resource was actually recycled, which happens when RefCnt
- * reaches 0.
- */
-bool ResourceCache::recycleLocked(SkBitmap* resource) {
- ssize_t index = mCache->indexOfKey(resource);
- if (index < 0) {
- if (Caches::hasInstance()) {
- Caches::getInstance().textureCache.releaseTexture(resource);
- }
- // not tracking this resource; just recycle the pixel data
- resource->setPixels(NULL, NULL);
- return true;
- }
- ResourceReference* ref = mCache->valueAt(index);
- if (ref == NULL) {
- // Should not get here - shouldn't get a call to recycle if we're not yet tracking it
- return true;
- }
- ref->recycled = true;
- if (ref->refCount == 0) {
- deleteResourceReferenceLocked(resource, ref);
- return true;
- }
- // Still referring to resource, don't recycle yet
- return false;
-}
-
-/**
* This method should only be called while the mLock mutex is held (that mutex is grabbed
* by the various destructor() and recycle() methods which call this method).
*/
void ResourceCache::deleteResourceReferenceLocked(const void* resource, ResourceReference* ref) {
- if (ref->recycled && ref->resourceType == kBitmap) {
- SkBitmap* bitmap = (SkBitmap*) resource;
- if (Caches::hasInstance()) {
- Caches::getInstance().textureCache.releaseTexture(bitmap);
- }
- bitmap->setPixels(NULL, NULL);
- }
if (ref->destroyed) {
switch (ref->resourceType) {
- case kBitmap: {
- SkBitmap* bitmap = (SkBitmap*) resource;
- if (Caches::hasInstance()) {
- Caches::getInstance().textureCache.releaseTexture(bitmap);
- }
- delete bitmap;
- }
- break;
- case kPath: {
- SkPath* path = (SkPath*) resource;
- if (Caches::hasInstance()) {
- Caches::getInstance().pathCache.removeDeferred(path);
- } else {
- delete path;
- }
- }
- break;
case kNinePatch: {
if (Caches::hasInstance()) {
Caches::getInstance().patchCache.removeDeferred((Res_png_9patch*) resource);
@@ -298,5 +190,38 @@ void ResourceCache::deleteResourceReferenceLocked(const void* resource, Resource
delete ref;
}
+///////////////////////////////////////////////////////////////////////////////
+// Bitmap Key
+///////////////////////////////////////////////////////////////////////////////
+
+void BitmapKey::operator=(const BitmapKey& other) {
+ this->mRefCount = other.mRefCount;
+ this->mBitmapDimensions = other.mBitmapDimensions;
+ this->mPixelRefOrigin = other.mPixelRefOrigin;
+ this->mPixelRefStableID = other.mPixelRefStableID;
+}
+
+bool BitmapKey::operator==(const BitmapKey& other) const {
+ return mPixelRefStableID == other.mPixelRefStableID &&
+ mPixelRefOrigin == other.mPixelRefOrigin &&
+ mBitmapDimensions == other.mBitmapDimensions;
+}
+
+bool BitmapKey::operator<(const BitmapKey& other) const {
+ if (mPixelRefStableID != other.mPixelRefStableID) {
+ return mPixelRefStableID < other.mPixelRefStableID;
+ }
+ if (mPixelRefOrigin.x() != other.mPixelRefOrigin.x()) {
+ return mPixelRefOrigin.x() < other.mPixelRefOrigin.x();
+ }
+ if (mPixelRefOrigin.y() != other.mPixelRefOrigin.y()) {
+ return mPixelRefOrigin.y() < other.mPixelRefOrigin.y();
+ }
+ if (mBitmapDimensions.width() != other.mBitmapDimensions.width()) {
+ return mBitmapDimensions.width() < other.mBitmapDimensions.width();
+ }
+ return mBitmapDimensions.height() < other.mBitmapDimensions.height();
+}
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/ResourceCache.h b/libs/hwui/ResourceCache.h
index f12f2a4..fae55d1 100644
--- a/libs/hwui/ResourceCache.h
+++ b/libs/hwui/ResourceCache.h
@@ -20,39 +20,68 @@
#include <cutils/compiler.h>
#include <SkBitmap.h>
+#include <SkPixelRef.h>
#include <utils/KeyedVector.h>
#include <utils/Singleton.h>
#include <androidfw/ResourceTypes.h>
-#include "Layer.h"
-
namespace android {
namespace uirenderer {
+class Layer;
+
/**
* Type of Resource being cached
*/
enum ResourceType {
- kBitmap,
kNinePatch,
- kPath
};
class ResourceReference {
public:
ResourceReference(ResourceType type) {
- refCount = 0; recycled = false; destroyed = false; resourceType = type;
+ refCount = 0; destroyed = false; resourceType = type;
}
int refCount;
- bool recycled;
bool destroyed;
ResourceType resourceType;
};
+class BitmapKey {
+public:
+ BitmapKey(const SkBitmap* bitmap)
+ : mRefCount(1)
+ , mBitmapDimensions(bitmap->dimensions())
+ , mPixelRefOrigin(bitmap->pixelRefOrigin())
+ , mPixelRefStableID(bitmap->pixelRef()->getStableID()) { }
+
+ void operator=(const BitmapKey& other);
+ bool operator==(const BitmapKey& other) const;
+ bool operator<(const BitmapKey& other) const;
+
+private:
+ // This constructor is only used by the KeyedVector implementation
+ BitmapKey()
+ : mRefCount(-1)
+ , mBitmapDimensions(SkISize::Make(0,0))
+ , mPixelRefOrigin(SkIPoint::Make(0,0))
+ , mPixelRefStableID(0) { }
+
+ // reference count of all HWUI object using this bitmap
+ mutable int mRefCount;
+
+ SkISize mBitmapDimensions;
+ SkIPoint mPixelRefOrigin;
+ uint32_t mPixelRefStableID;
+
+ friend class ResourceCache;
+ friend struct android::key_value_pair_t<BitmapKey, SkBitmap*>;
+};
+
class ANDROID_API ResourceCache: public Singleton<ResourceCache> {
ResourceCache();
~ResourceCache();
@@ -68,33 +97,24 @@ public:
void lock();
void unlock();
- void incrementRefcount(const SkPath* resource);
- void incrementRefcount(const SkBitmap* resource);
- void incrementRefcount(const Res_png_9patch* resource);
+ /**
+ * The cache stores a copy of the provided resource or refs an existing resource
+ * if the bitmap has previously been inserted and returns the cached copy.
+ */
+ const SkBitmap* insert(const SkBitmap* resource);
- void incrementRefcountLocked(const SkPath* resource);
- void incrementRefcountLocked(const SkBitmap* resource);
- void incrementRefcountLocked(const Res_png_9patch* resource);
+ void incrementRefcount(const Res_png_9patch* resource);
void decrementRefcount(const SkBitmap* resource);
- void decrementRefcount(const SkPath* resource);
void decrementRefcount(const Res_png_9patch* resource);
void decrementRefcountLocked(const SkBitmap* resource);
- void decrementRefcountLocked(const SkPath* resource);
void decrementRefcountLocked(const Res_png_9patch* resource);
- void destructor(SkPath* resource);
- void destructor(const SkBitmap* resource);
void destructor(Res_png_9patch* resource);
- void destructorLocked(SkPath* resource);
- void destructorLocked(const SkBitmap* resource);
void destructorLocked(Res_png_9patch* resource);
- bool recycle(SkBitmap* resource);
- bool recycleLocked(SkBitmap* resource);
-
private:
void deleteResourceReferenceLocked(const void* resource, ResourceReference* ref);
@@ -114,6 +134,7 @@ private:
mutable Mutex mLock;
KeyedVector<const void*, ResourceReference*>* mCache;
+ KeyedVector<BitmapKey, SkBitmap*> mBitmapCache;
};
}; // namespace uirenderer
diff --git a/libs/hwui/RevealClip.h b/libs/hwui/RevealClip.h
index a9600f1..0084a8e 100644
--- a/libs/hwui/RevealClip.h
+++ b/libs/hwui/RevealClip.h
@@ -54,7 +54,7 @@ public:
float getRadius() const { return mRadius; }
const SkPath* getPath() const {
- if (!mShouldClip) return NULL;
+ if (!mShouldClip) return nullptr;
return &mPath;
}
diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp
index c1ffa0a..30d3f41 100644
--- a/libs/hwui/ShadowTessellator.cpp
+++ b/libs/hwui/ShadowTessellator.cpp
@@ -87,9 +87,8 @@ void ShadowTessellator::tessellateSpotShadow(bool isCasterOpaque,
reverseReceiverTransform.loadInverse(receiverTransform);
reverseReceiverTransform.mapPoint3d(adjustedLightCenter);
- const int lightVertexCount = 8;
- if (CC_UNLIKELY(caches.propertyLightDiameter > 0)) {
- lightRadius = caches.propertyLightDiameter;
+ if (CC_UNLIKELY(caches.propertyLightRadius > 0)) {
+ lightRadius = caches.propertyLightRadius;
}
// Now light and caster are both in local space, we will check whether
@@ -114,33 +113,6 @@ void ShadowTessellator::tessellateSpotShadow(bool isCasterOpaque,
#endif
}
-void ShadowTessellator::generateShadowIndices(uint16_t* shadowIndices) {
- int currentIndex = 0;
- const int rays = SHADOW_RAY_COUNT;
- // For the penumbra area.
- for (int layer = 0; layer < 2; layer ++) {
- int baseIndex = layer * rays;
- for (int i = 0; i < rays; i++) {
- shadowIndices[currentIndex++] = i + baseIndex;
- shadowIndices[currentIndex++] = rays + i + baseIndex;
- }
- // To close the loop, back to the ray 0.
- shadowIndices[currentIndex++] = 0 + baseIndex;
- // Note this is the same as the first index of next layer loop.
- shadowIndices[currentIndex++] = rays + baseIndex;
- }
-
-#if DEBUG_SHADOW
- if (currentIndex != MAX_SHADOW_INDEX_COUNT) {
- ALOGW("vertex index count is wrong. current %d, expected %d",
- currentIndex, MAX_SHADOW_INDEX_COUNT);
- }
- for (int i = 0; i < MAX_SHADOW_INDEX_COUNT; i++) {
- ALOGD("vertex index is (%d, %d)", i, shadowIndices[i]);
- }
-#endif
-}
-
/**
* Calculate the centroid of a 2d polygon.
*
@@ -194,7 +166,7 @@ Vector2 ShadowTessellator::calculateNormal(const Vector2& p1, const Vector2& p2)
* @param len the number of points of the polygon
*/
bool ShadowTessellator::isClockwise(const Vector2* polygon, int len) {
- if (len < 2 || polygon == NULL) {
+ if (len < 2 || polygon == nullptr) {
return true;
}
double sum = 0;
@@ -225,6 +197,7 @@ bool ShadowTessellator::isClockwisePath(const SkPath& path) {
case SkPath::kLine_Verb:
arrayForDirection.add((Vector2){pts[1].x(), pts[1].y()});
break;
+ case SkPath::kConic_Verb:
case SkPath::kQuad_Verb:
arrayForDirection.add((Vector2){pts[1].x(), pts[1].y()});
arrayForDirection.add((Vector2){pts[2].x(), pts[2].y()});
diff --git a/libs/hwui/ShadowTessellator.h b/libs/hwui/ShadowTessellator.h
index 8f19b5c..c04d8ef 100644
--- a/libs/hwui/ShadowTessellator.h
+++ b/libs/hwui/ShadowTessellator.h
@@ -18,14 +18,16 @@
#ifndef ANDROID_HWUI_SHADOW_TESSELLATOR_H
#define ANDROID_HWUI_SHADOW_TESSELLATOR_H
+#include <SkPath.h>
+
#include "Debug.h"
#include "Matrix.h"
-#include "OpenGLRenderer.h"
-#include "VertexBuffer.h"
namespace android {
namespace uirenderer {
+class VertexBuffer;
+
// All SHADOW_* are used to define all the geometry property of shadows.
// Use a simplified example to illustrate the geometry setup here.
// Assuming we use 6 rays and only 1 layer, Then we will have 2 hexagons, which
@@ -76,8 +78,6 @@ public:
const mat4& receiverTransform, const Vector3& lightCenter, int lightRadius,
const Rect& casterBounds, const Rect& localClip, VertexBuffer& shadowVertexBuffer);
- static void generateShadowIndices(uint16_t* shadowIndices);
-
static Vector2 centroid2d(const Vector2* poly, int polyLength);
static bool isClockwise(const Vector2* polygon, int len);
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
new file mode 100644
index 0000000..a323065
--- /dev/null
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -0,0 +1,703 @@
+/*
+ * 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 "Canvas.h"
+
+#include <SkCanvas.h>
+#include <SkClipStack.h>
+#include <SkDevice.h>
+#include <SkDeque.h>
+#include <SkDrawFilter.h>
+#include <SkGraphics.h>
+#include <SkShader.h>
+#include <SkTArray.h>
+#include <SkTemplates.h>
+
+namespace android {
+
+// Holds an SkCanvas reference plus additional native data.
+class SkiaCanvas : public Canvas {
+public:
+ explicit SkiaCanvas(const SkBitmap& bitmap);
+
+ /**
+ * Create a new SkiaCanvas.
+ *
+ * @param canvas SkCanvas to handle calls made to this SkiaCanvas. Must
+ * not be NULL. This constructor will ref() the SkCanvas, and unref()
+ * it in its destructor.
+ */
+ explicit SkiaCanvas(SkCanvas* canvas) : mCanvas(canvas) {
+ SkASSERT(canvas);
+ canvas->ref();
+ }
+
+ virtual SkCanvas* asSkCanvas() override {
+ return mCanvas.get();
+ }
+
+ virtual void setBitmap(const SkBitmap& bitmap) override;
+
+ virtual bool isOpaque() override;
+ virtual int width() override;
+ virtual int height() override;
+
+ virtual int getSaveCount() const override;
+ virtual int save(SkCanvas::SaveFlags flags) override;
+ virtual void restore() override;
+ virtual void restoreToCount(int saveCount) override;
+
+ virtual int saveLayer(float left, float top, float right, float bottom,
+ const SkPaint* paint, SkCanvas::SaveFlags flags) override;
+ virtual int saveLayerAlpha(float left, float top, float right, float bottom,
+ int alpha, SkCanvas::SaveFlags flags) override;
+
+ virtual void getMatrix(SkMatrix* outMatrix) const override;
+ virtual void setMatrix(const SkMatrix& matrix) override;
+ virtual void concat(const SkMatrix& matrix) override;
+ virtual void rotate(float degrees) override;
+ virtual void scale(float sx, float sy) override;
+ virtual void skew(float sx, float sy) override;
+ virtual void translate(float dx, float dy) override;
+
+ virtual bool getClipBounds(SkRect* outRect) const override;
+ virtual bool quickRejectRect(float left, float top, float right, float bottom) const override;
+ virtual bool quickRejectPath(const SkPath& path) const override;
+ virtual bool clipRect(float left, float top, float right, float bottom,
+ SkRegion::Op op) override;
+ virtual bool clipPath(const SkPath* path, SkRegion::Op op) override;
+ virtual bool clipRegion(const SkRegion* region, SkRegion::Op op) override;
+
+ virtual SkDrawFilter* getDrawFilter() override;
+ virtual void setDrawFilter(SkDrawFilter* drawFilter) override;
+
+ virtual void drawColor(int color, SkXfermode::Mode mode) override;
+ virtual void drawPaint(const SkPaint& paint) override;
+
+ virtual void drawPoint(float x, float y, const SkPaint& paint) override;
+ virtual void drawPoints(const float* points, int count, const SkPaint& paint) override;
+ virtual void drawLine(float startX, float startY, float stopX, float stopY,
+ const SkPaint& paint) override;
+ virtual void drawLines(const float* points, int count, const SkPaint& paint) override;
+ virtual void drawRect(float left, float top, float right, float bottom,
+ const SkPaint& paint) override;
+ virtual void drawRoundRect(float left, float top, float right, float bottom,
+ float rx, float ry, const SkPaint& paint) override;
+ virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) override;
+ virtual void drawOval(float left, float top, float right, float bottom,
+ const SkPaint& paint) override;
+ virtual void drawArc(float left, float top, float right, float bottom,
+ float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) override;
+ virtual void drawPath(const SkPath& path, const SkPaint& paint) override;
+ virtual void drawVertices(SkCanvas::VertexMode vertexMode, int vertexCount,
+ const float* verts, const float* tex, const int* colors,
+ const uint16_t* indices, int indexCount, const SkPaint& paint) override;
+
+ virtual void drawBitmap(const SkBitmap& bitmap, float left, float top,
+ const SkPaint* paint) override;
+ virtual void drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
+ const SkPaint* paint) override;
+ virtual void drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
+ float srcRight, float srcBottom, float dstLeft, float dstTop,
+ float dstRight, float dstBottom, const SkPaint* paint) override;
+ virtual void drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
+ const float* vertices, const int* colors, const SkPaint* paint) override;
+
+ virtual void drawText(const uint16_t* text, const float* positions, int count,
+ const SkPaint& paint, float x, float y,
+ float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
+ float totalAdvance) override;
+ virtual void drawPosText(const uint16_t* text, const float* positions, int count,
+ int posCount, const SkPaint& paint) override;
+ virtual void drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path,
+ float hOffset, float vOffset, const SkPaint& paint) override;
+
+ virtual bool drawTextAbsolutePos() const override { return true; }
+
+private:
+ struct SaveRec {
+ int saveCount;
+ SkCanvas::SaveFlags saveFlags;
+ };
+
+ void recordPartialSave(SkCanvas::SaveFlags flags);
+ void saveClipsForFrame(SkTArray<SkClipStack::Element>& clips, int frameSaveCount);
+ void applyClips(const SkTArray<SkClipStack::Element>& clips);
+
+ void drawPoints(const float* points, int count, const SkPaint& paint,
+ SkCanvas::PointMode mode);
+ void drawTextDecorations(float x, float y, float length, const SkPaint& paint);
+
+ SkAutoTUnref<SkCanvas> mCanvas;
+ SkAutoTDelete<SkDeque> mSaveStack; // lazily allocated, tracks partial saves.
+};
+
+Canvas* Canvas::create_canvas(const SkBitmap& bitmap) {
+ return new SkiaCanvas(bitmap);
+}
+
+Canvas* Canvas::create_canvas(SkCanvas* skiaCanvas) {
+ return new SkiaCanvas(skiaCanvas);
+}
+
+SkiaCanvas::SkiaCanvas(const SkBitmap& bitmap) {
+ mCanvas.reset(new SkCanvas(bitmap));
+}
+
+// ----------------------------------------------------------------------------
+// Canvas state operations: Replace Bitmap
+// ----------------------------------------------------------------------------
+
+class ClipCopier : public SkCanvas::ClipVisitor {
+public:
+ ClipCopier(SkCanvas* dstCanvas) : m_dstCanvas(dstCanvas) {}
+
+ virtual void clipRect(const SkRect& rect, SkRegion::Op op, bool antialias) {
+ m_dstCanvas->clipRect(rect, op, antialias);
+ }
+ virtual void clipRRect(const SkRRect& rrect, SkRegion::Op op, bool antialias) {
+ m_dstCanvas->clipRRect(rrect, op, antialias);
+ }
+ virtual void clipPath(const SkPath& path, SkRegion::Op op, bool antialias) {
+ m_dstCanvas->clipPath(path, op, antialias);
+ }
+
+private:
+ SkCanvas* m_dstCanvas;
+};
+
+void SkiaCanvas::setBitmap(const SkBitmap& bitmap) {
+ SkCanvas* newCanvas = new SkCanvas(bitmap);
+ SkASSERT(newCanvas);
+
+ if (!bitmap.isNull()) {
+ // Copy the canvas matrix & clip state.
+ newCanvas->setMatrix(mCanvas->getTotalMatrix());
+ if (NULL != mCanvas->getDevice() && NULL != newCanvas->getDevice()) {
+ ClipCopier copier(newCanvas);
+ mCanvas->replayClips(&copier);
+ }
+ }
+
+ // unrefs the existing canvas
+ mCanvas.reset(newCanvas);
+
+ // clean up the old save stack
+ mSaveStack.reset(NULL);
+}
+
+// ----------------------------------------------------------------------------
+// Canvas state operations
+// ----------------------------------------------------------------------------
+
+bool SkiaCanvas::isOpaque() {
+ return mCanvas->getDevice()->accessBitmap(false).isOpaque();
+}
+
+int SkiaCanvas::width() {
+ return mCanvas->getBaseLayerSize().width();
+}
+
+int SkiaCanvas::height() {
+ return mCanvas->getBaseLayerSize().height();
+}
+
+// ----------------------------------------------------------------------------
+// Canvas state operations: Save (layer)
+// ----------------------------------------------------------------------------
+
+int SkiaCanvas::getSaveCount() const {
+ return mCanvas->getSaveCount();
+}
+
+int SkiaCanvas::save(SkCanvas::SaveFlags flags) {
+ int count = mCanvas->save();
+ recordPartialSave(flags);
+ return count;
+}
+
+void SkiaCanvas::restore() {
+ const SaveRec* rec = (NULL == mSaveStack.get())
+ ? NULL
+ : static_cast<SaveRec*>(mSaveStack->back());
+ int currentSaveCount = mCanvas->getSaveCount() - 1;
+ SkASSERT(NULL == rec || currentSaveCount >= rec->saveCount);
+
+ if (NULL == rec || rec->saveCount != currentSaveCount) {
+ // Fast path - no record for this frame.
+ mCanvas->restore();
+ return;
+ }
+
+ bool preserveMatrix = !(rec->saveFlags & SkCanvas::kMatrix_SaveFlag);
+ bool preserveClip = !(rec->saveFlags & SkCanvas::kClip_SaveFlag);
+
+ SkMatrix savedMatrix;
+ if (preserveMatrix) {
+ savedMatrix = mCanvas->getTotalMatrix();
+ }
+
+ SkTArray<SkClipStack::Element> savedClips;
+ if (preserveClip) {
+ saveClipsForFrame(savedClips, currentSaveCount);
+ }
+
+ mCanvas->restore();
+
+ if (preserveMatrix) {
+ mCanvas->setMatrix(savedMatrix);
+ }
+
+ if (preserveClip && !savedClips.empty()) {
+ applyClips(savedClips);
+ }
+
+ mSaveStack->pop_back();
+}
+
+void SkiaCanvas::restoreToCount(int restoreCount) {
+ while (mCanvas->getSaveCount() > restoreCount) {
+ this->restore();
+ }
+}
+
+int SkiaCanvas::saveLayer(float left, float top, float right, float bottom,
+ const SkPaint* paint, SkCanvas::SaveFlags flags) {
+ SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom);
+ int count = mCanvas->saveLayer(&bounds, paint, flags | SkCanvas::kMatrixClip_SaveFlag);
+ recordPartialSave(flags);
+ return count;
+}
+
+int SkiaCanvas::saveLayerAlpha(float left, float top, float right, float bottom,
+ int alpha, SkCanvas::SaveFlags flags) {
+ SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom);
+ int count = mCanvas->saveLayerAlpha(&bounds, alpha, flags | SkCanvas::kMatrixClip_SaveFlag);
+ recordPartialSave(flags);
+ return count;
+}
+
+// ----------------------------------------------------------------------------
+// functions to emulate legacy SaveFlags (i.e. independent matrix/clip flags)
+// ----------------------------------------------------------------------------
+
+void SkiaCanvas::recordPartialSave(SkCanvas::SaveFlags flags) {
+ // A partial save is a save operation which doesn't capture the full canvas state.
+ // (either kMatrix_SaveFlags or kClip_SaveFlag is missing).
+
+ // Mask-out non canvas state bits.
+ flags = static_cast<SkCanvas::SaveFlags>(flags & SkCanvas::kMatrixClip_SaveFlag);
+
+ if (SkCanvas::kMatrixClip_SaveFlag == flags) {
+ // not a partial save.
+ return;
+ }
+
+ if (NULL == mSaveStack.get()) {
+ mSaveStack.reset(SkNEW_ARGS(SkDeque, (sizeof(struct SaveRec), 8)));
+ }
+
+ SaveRec* rec = static_cast<SaveRec*>(mSaveStack->push_back());
+ // Store the save counter in the SkClipStack domain.
+ // (0-based, equal to the number of save ops on the stack).
+ rec->saveCount = mCanvas->getSaveCount() - 1;
+ rec->saveFlags = flags;
+}
+
+void SkiaCanvas::saveClipsForFrame(SkTArray<SkClipStack::Element>& clips, int frameSaveCount) {
+ SkClipStack::Iter clipIterator(*mCanvas->getClipStack(),
+ SkClipStack::Iter::kTop_IterStart);
+ while (const SkClipStack::Element* elem = clipIterator.next()) {
+ if (elem->getSaveCount() < frameSaveCount) {
+ // done with the current frame.
+ break;
+ }
+ SkASSERT(elem->getSaveCount() == frameSaveCount);
+ clips.push_back(*elem);
+ }
+}
+
+void SkiaCanvas::applyClips(const SkTArray<SkClipStack::Element>& clips) {
+ ClipCopier clipCopier(mCanvas);
+
+ // The clip stack stores clips in device space.
+ SkMatrix origMatrix = mCanvas->getTotalMatrix();
+ mCanvas->resetMatrix();
+
+ // We pushed the clips in reverse order.
+ for (int i = clips.count() - 1; i >= 0; --i) {
+ clips[i].replay(&clipCopier);
+ }
+
+ mCanvas->setMatrix(origMatrix);
+}
+
+// ----------------------------------------------------------------------------
+// Canvas state operations: Matrix
+// ----------------------------------------------------------------------------
+
+void SkiaCanvas::getMatrix(SkMatrix* outMatrix) const {
+ *outMatrix = mCanvas->getTotalMatrix();
+}
+
+void SkiaCanvas::setMatrix(const SkMatrix& matrix) {
+ mCanvas->setMatrix(matrix);
+}
+
+void SkiaCanvas::concat(const SkMatrix& matrix) {
+ mCanvas->concat(matrix);
+}
+
+void SkiaCanvas::rotate(float degrees) {
+ mCanvas->rotate(degrees);
+}
+
+void SkiaCanvas::scale(float sx, float sy) {
+ mCanvas->scale(sx, sy);
+}
+
+void SkiaCanvas::skew(float sx, float sy) {
+ mCanvas->skew(sx, sy);
+}
+
+void SkiaCanvas::translate(float dx, float dy) {
+ mCanvas->translate(dx, dy);
+}
+
+// ----------------------------------------------------------------------------
+// Canvas state operations: Clips
+// ----------------------------------------------------------------------------
+
+// This function is a mirror of SkCanvas::getClipBounds except that it does
+// not outset the edge of the clip to account for anti-aliasing. There is
+// a skia bug to investigate pushing this logic into back into skia.
+// (see https://code.google.com/p/skia/issues/detail?id=1303)
+bool SkiaCanvas::getClipBounds(SkRect* outRect) const {
+ SkIRect ibounds;
+ if (!mCanvas->getClipDeviceBounds(&ibounds)) {
+ return false;
+ }
+
+ SkMatrix inverse;
+ // if we can't invert the CTM, we can't return local clip bounds
+ if (!mCanvas->getTotalMatrix().invert(&inverse)) {
+ if (outRect) {
+ outRect->setEmpty();
+ }
+ return false;
+ }
+
+ if (NULL != outRect) {
+ SkRect r = SkRect::Make(ibounds);
+ inverse.mapRect(outRect, r);
+ }
+ return true;
+}
+
+bool SkiaCanvas::quickRejectRect(float left, float top, float right, float bottom) const {
+ SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom);
+ return mCanvas->quickReject(bounds);
+}
+
+bool SkiaCanvas::quickRejectPath(const SkPath& path) const {
+ return mCanvas->quickReject(path);
+}
+
+bool SkiaCanvas::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
+ SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
+ mCanvas->clipRect(rect, op);
+ return mCanvas->isClipEmpty();
+}
+
+bool SkiaCanvas::clipPath(const SkPath* path, SkRegion::Op op) {
+ mCanvas->clipPath(*path, op);
+ return mCanvas->isClipEmpty();
+}
+
+bool SkiaCanvas::clipRegion(const SkRegion* region, SkRegion::Op op) {
+ SkPath rgnPath;
+ if (region->getBoundaryPath(&rgnPath)) {
+ // The region is specified in device space.
+ SkMatrix savedMatrix = mCanvas->getTotalMatrix();
+ mCanvas->resetMatrix();
+ mCanvas->clipPath(rgnPath, op);
+ mCanvas->setMatrix(savedMatrix);
+ } else {
+ mCanvas->clipRect(SkRect::MakeEmpty(), op);
+ }
+ return mCanvas->isClipEmpty();
+}
+
+// ----------------------------------------------------------------------------
+// Canvas state operations: Filters
+// ----------------------------------------------------------------------------
+
+SkDrawFilter* SkiaCanvas::getDrawFilter() {
+ return mCanvas->getDrawFilter();
+}
+
+void SkiaCanvas::setDrawFilter(SkDrawFilter* drawFilter) {
+ mCanvas->setDrawFilter(drawFilter);
+}
+
+// ----------------------------------------------------------------------------
+// Canvas draw operations
+// ----------------------------------------------------------------------------
+
+void SkiaCanvas::drawColor(int color, SkXfermode::Mode mode) {
+ mCanvas->drawColor(color, mode);
+}
+
+void SkiaCanvas::drawPaint(const SkPaint& paint) {
+ mCanvas->drawPaint(paint);
+}
+
+// ----------------------------------------------------------------------------
+// Canvas draw operations: Geometry
+// ----------------------------------------------------------------------------
+
+void SkiaCanvas::drawPoints(const float* points, int count, const SkPaint& paint,
+ SkCanvas::PointMode mode) {
+ // convert the floats into SkPoints
+ count >>= 1; // now it is the number of points
+ SkAutoSTMalloc<32, SkPoint> storage(count);
+ SkPoint* pts = storage.get();
+ for (int i = 0; i < count; i++) {
+ pts[i].set(points[0], points[1]);
+ points += 2;
+ }
+ mCanvas->drawPoints(mode, count, pts, paint);
+}
+
+
+void SkiaCanvas::drawPoint(float x, float y, const SkPaint& paint) {
+ mCanvas->drawPoint(x, y, paint);
+}
+
+void SkiaCanvas::drawPoints(const float* points, int count, const SkPaint& paint) {
+ this->drawPoints(points, count, paint, SkCanvas::kPoints_PointMode);
+}
+
+void SkiaCanvas::drawLine(float startX, float startY, float stopX, float stopY,
+ const SkPaint& paint) {
+ mCanvas->drawLine(startX, startY, stopX, stopY, paint);
+}
+
+void SkiaCanvas::drawLines(const float* points, int count, const SkPaint& paint) {
+ this->drawPoints(points, count, paint, SkCanvas::kLines_PointMode);
+}
+
+void SkiaCanvas::drawRect(float left, float top, float right, float bottom,
+ const SkPaint& paint) {
+ mCanvas->drawRectCoords(left, top, right, bottom, paint);
+
+}
+
+void SkiaCanvas::drawRoundRect(float left, float top, float right, float bottom,
+ float rx, float ry, const SkPaint& paint) {
+ SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
+ mCanvas->drawRoundRect(rect, rx, ry, paint);
+}
+
+void SkiaCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) {
+ mCanvas->drawCircle(x, y, radius, paint);
+}
+
+void SkiaCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) {
+ SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
+ mCanvas->drawOval(oval, paint);
+}
+
+void SkiaCanvas::drawArc(float left, float top, float right, float bottom,
+ float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) {
+ SkRect arc = SkRect::MakeLTRB(left, top, right, bottom);
+ mCanvas->drawArc(arc, startAngle, sweepAngle, useCenter, paint);
+}
+
+void SkiaCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
+ mCanvas->drawPath(path, paint);
+}
+
+void SkiaCanvas::drawVertices(SkCanvas::VertexMode vertexMode, int vertexCount,
+ const float* verts, const float* texs, const int* colors,
+ const uint16_t* indices, int indexCount, const SkPaint& paint) {
+#ifndef SK_SCALAR_IS_FLOAT
+ SkDEBUGFAIL("SkScalar must be a float for these conversions to be valid");
+#endif
+ const int ptCount = vertexCount >> 1;
+ mCanvas->drawVertices(vertexMode, ptCount, (SkPoint*)verts, (SkPoint*)texs,
+ (SkColor*)colors, NULL, indices, indexCount, paint);
+}
+
+// ----------------------------------------------------------------------------
+// Canvas draw operations: Bitmaps
+// ----------------------------------------------------------------------------
+
+void SkiaCanvas::drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) {
+ mCanvas->drawBitmap(bitmap, left, top, paint);
+}
+
+void SkiaCanvas::drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) {
+ SkAutoCanvasRestore acr(mCanvas, true);
+ mCanvas->concat(matrix);
+ mCanvas->drawBitmap(bitmap, 0, 0, paint);
+}
+
+void SkiaCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
+ float srcRight, float srcBottom, float dstLeft, float dstTop,
+ float dstRight, float dstBottom, const SkPaint* paint) {
+ SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
+ SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
+ mCanvas->drawBitmapRectToRect(bitmap, &srcRect, dstRect, paint);
+}
+
+void SkiaCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
+ const float* vertices, const int* colors, const SkPaint* paint) {
+
+ const int ptCount = (meshWidth + 1) * (meshHeight + 1);
+ const int indexCount = meshWidth * meshHeight * 6;
+
+ /* Our temp storage holds 2 or 3 arrays.
+ texture points [ptCount * sizeof(SkPoint)]
+ optionally vertex points [ptCount * sizeof(SkPoint)] if we need a
+ copy to convert from float to fixed
+ indices [ptCount * sizeof(uint16_t)]
+ */
+ ssize_t storageSize = ptCount * sizeof(SkPoint); // texs[]
+ storageSize += indexCount * sizeof(uint16_t); // indices[]
+
+
+#ifndef SK_SCALAR_IS_FLOAT
+ SkDEBUGFAIL("SkScalar must be a float for these conversions to be valid");
+#endif
+ SkAutoMalloc storage(storageSize);
+ SkPoint* texs = (SkPoint*)storage.get();
+ uint16_t* indices = (uint16_t*)(texs + ptCount);
+
+ // cons up texture coordinates and indices
+ {
+ const SkScalar w = SkIntToScalar(bitmap.width());
+ const SkScalar h = SkIntToScalar(bitmap.height());
+ const SkScalar dx = w / meshWidth;
+ const SkScalar dy = h / meshHeight;
+
+ SkPoint* texsPtr = texs;
+ SkScalar y = 0;
+ for (int i = 0; i <= meshHeight; i++) {
+ if (i == meshHeight) {
+ y = h; // to ensure numerically we hit h exactly
+ }
+ SkScalar x = 0;
+ for (int j = 0; j < meshWidth; j++) {
+ texsPtr->set(x, y);
+ texsPtr += 1;
+ x += dx;
+ }
+ texsPtr->set(w, y);
+ texsPtr += 1;
+ y += dy;
+ }
+ SkASSERT(texsPtr - texs == ptCount);
+ }
+
+ // cons up indices
+ {
+ uint16_t* indexPtr = indices;
+ int index = 0;
+ for (int i = 0; i < meshHeight; i++) {
+ for (int j = 0; j < meshWidth; j++) {
+ // lower-left triangle
+ *indexPtr++ = index;
+ *indexPtr++ = index + meshWidth + 1;
+ *indexPtr++ = index + meshWidth + 2;
+ // upper-right triangle
+ *indexPtr++ = index;
+ *indexPtr++ = index + meshWidth + 2;
+ *indexPtr++ = index + 1;
+ // bump to the next cell
+ index += 1;
+ }
+ // bump to the next row
+ index += 1;
+ }
+ SkASSERT(indexPtr - indices == indexCount);
+ SkASSERT((char*)indexPtr - (char*)storage.get() == storageSize);
+ }
+
+ // double-check that we have legal indices
+#ifdef SK_DEBUG
+ {
+ for (int i = 0; i < indexCount; i++) {
+ SkASSERT((unsigned)indices[i] < (unsigned)ptCount);
+ }
+ }
+#endif
+
+ // cons-up a shader for the bitmap
+ SkPaint tmpPaint;
+ if (paint) {
+ tmpPaint = *paint;
+ }
+ SkShader* shader = SkShader::CreateBitmapShader(bitmap,
+ SkShader::kClamp_TileMode,
+ SkShader::kClamp_TileMode);
+ SkSafeUnref(tmpPaint.setShader(shader));
+
+ mCanvas->drawVertices(SkCanvas::kTriangles_VertexMode, ptCount, (SkPoint*)vertices,
+ texs, (const SkColor*)colors, NULL, indices,
+ indexCount, tmpPaint);
+}
+
+// ----------------------------------------------------------------------------
+// Canvas draw operations: Text
+// ----------------------------------------------------------------------------
+
+void SkiaCanvas::drawText(const uint16_t* text, const float* positions, int count,
+ const SkPaint& paint, float x, float y,
+ float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
+ float totalAdvance) {
+ // Set align to left for drawing, as we don't want individual
+ // glyphs centered or right-aligned; the offset above takes
+ // care of all alignment.
+ SkPaint paintCopy(paint);
+ paintCopy.setTextAlign(SkPaint::kLeft_Align);
+
+ SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats);
+ mCanvas->drawPosText(text, count << 1, reinterpret_cast<const SkPoint*>(positions), paintCopy);
+}
+
+void SkiaCanvas::drawPosText(const uint16_t* text, const float* positions, int count, int posCount,
+ const SkPaint& paint) {
+ SkPoint* posPtr = posCount > 0 ? new SkPoint[posCount] : NULL;
+ int indx;
+ for (indx = 0; indx < posCount; indx++) {
+ posPtr[indx].fX = positions[indx << 1];
+ posPtr[indx].fY = positions[(indx << 1) + 1];
+ }
+
+ SkPaint paintCopy(paint);
+ paintCopy.setTextEncoding(SkPaint::kUTF16_TextEncoding);
+ mCanvas->drawPosText(text, count, posPtr, paintCopy);
+
+ delete[] posPtr;
+}
+
+void SkiaCanvas::drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path,
+ float hOffset, float vOffset, const SkPaint& paint) {
+ mCanvas->drawTextOnPathHV(glyphs, count << 1, path, hOffset, vOffset, paint);
+}
+
+} // namespace android
diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp
new file mode 100644
index 0000000..8a6c8c5
--- /dev/null
+++ b/libs/hwui/SkiaCanvasProxy.cpp
@@ -0,0 +1,361 @@
+/*
+ * 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 "SkiaCanvasProxy.h"
+
+#include <cutils/log.h>
+#include <SkPatchUtils.h>
+
+namespace android {
+namespace uirenderer {
+
+SkiaCanvasProxy::SkiaCanvasProxy(Canvas* canvas, bool filterHwuiCalls)
+ : INHERITED(canvas->width(), canvas->height())
+ , mCanvas(canvas)
+ , mFilterHwuiCalls(filterHwuiCalls) {}
+
+void SkiaCanvasProxy::onDrawPaint(const SkPaint& paint) {
+ mCanvas->drawPaint(paint);
+}
+
+void SkiaCanvasProxy::onDrawPoints(PointMode pointMode, size_t count, const SkPoint pts[],
+ const SkPaint& paint) {
+ if (!pts || count == 0) {
+ return;
+ }
+
+ // convert the SkPoints into floats
+ SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats);
+ const size_t floatCount = count << 1;
+ const float* floatArray = &pts[0].fX;
+
+ switch (pointMode) {
+ case kPoints_PointMode: {
+ mCanvas->drawPoints(floatArray, floatCount, paint);
+ break;
+ }
+ case kLines_PointMode: {
+ mCanvas->drawLines(floatArray, floatCount, paint);
+ break;
+ }
+ case kPolygon_PointMode: {
+ SkPaint strokedPaint(paint);
+ strokedPaint.setStyle(SkPaint::kStroke_Style);
+
+ SkPath path;
+ for (size_t i = 0; i < count - 1; i++) {
+ path.moveTo(pts[i]);
+ path.lineTo(pts[i+1]);
+ this->drawPath(path, strokedPaint);
+ path.rewind();
+ }
+ break;
+ }
+ default:
+ LOG_ALWAYS_FATAL("Unknown point type");
+ }
+}
+
+void SkiaCanvasProxy::onDrawOval(const SkRect& rect, const SkPaint& paint) {
+ mCanvas->drawOval(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint);
+}
+
+void SkiaCanvasProxy::onDrawRect(const SkRect& rect, const SkPaint& paint) {
+ mCanvas->drawRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint);
+}
+
+void SkiaCanvasProxy::onDrawRRect(const SkRRect& roundRect, const SkPaint& paint) {
+ if (!roundRect.isComplex()) {
+ const SkRect& rect = roundRect.rect();
+ SkVector radii = roundRect.getSimpleRadii();
+ mCanvas->drawRoundRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom,
+ radii.fX, radii.fY, paint);
+ } else {
+ SkPath path;
+ path.addRRect(roundRect);
+ mCanvas->drawPath(path, paint);
+ }
+}
+
+void SkiaCanvasProxy::onDrawPath(const SkPath& path, const SkPaint& paint) {
+ mCanvas->drawPath(path, paint);
+}
+
+void SkiaCanvasProxy::onDrawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
+ const SkPaint* paint) {
+ mCanvas->drawBitmap(bitmap, left, top, paint);
+}
+
+void SkiaCanvasProxy::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* srcPtr,
+ const SkRect& dst, const SkPaint* paint, DrawBitmapRectFlags) {
+ SkRect src = (srcPtr) ? *srcPtr : SkRect::MakeWH(bitmap.width(), bitmap.height());
+ mCanvas->drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom,
+ dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint);
+}
+
+void SkiaCanvasProxy::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
+ const SkRect& dst, const SkPaint*) {
+ //TODO make nine-patch drawing a method on Canvas.h
+ SkDEBUGFAIL("SkiaCanvasProxy::onDrawBitmapNine is not yet supported");
+}
+
+void SkiaCanvasProxy::onDrawSprite(const SkBitmap& bitmap, int left, int top,
+ const SkPaint* paint) {
+ mCanvas->save(SkCanvas::kMatrixClip_SaveFlag);
+ mCanvas->setMatrix(SkMatrix::I());
+ mCanvas->drawBitmap(bitmap, left, top, paint);
+ mCanvas->restore();
+}
+
+void SkiaCanvasProxy::onDrawVertices(VertexMode mode, int vertexCount, const SkPoint vertices[],
+ const SkPoint texs[], const SkColor colors[], SkXfermode*, const uint16_t indices[],
+ int indexCount, const SkPaint& paint) {
+ if (mFilterHwuiCalls) {
+ return;
+ }
+ // convert the SkPoints into floats
+ SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats);
+ const int floatCount = vertexCount << 1;
+ const float* vArray = &vertices[0].fX;
+ const float* tArray = (texs) ? &texs[0].fX : NULL;
+ const int* cArray = (colors) ? (int*)colors : NULL;
+ mCanvas->drawVertices(mode, floatCount, vArray, tArray, cArray, indices, indexCount, paint);
+}
+
+SkSurface* SkiaCanvasProxy::onNewSurface(const SkImageInfo&, const SkSurfaceProps&) {
+ SkDEBUGFAIL("SkiaCanvasProxy::onNewSurface is not supported");
+ return NULL;
+}
+
+void SkiaCanvasProxy::willSave() {
+ mCanvas->save(SkCanvas::kMatrixClip_SaveFlag);
+}
+
+SkCanvas::SaveLayerStrategy SkiaCanvasProxy::willSaveLayer(const SkRect* rectPtr,
+ const SkPaint* paint, SaveFlags flags) {
+ SkRect rect;
+ if (rectPtr) {
+ rect = *rectPtr;
+ } else if(!mCanvas->getClipBounds(&rect)) {
+ rect = SkRect::MakeEmpty();
+ }
+ mCanvas->saveLayer(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint, flags);
+ return SkCanvas::kNoLayer_SaveLayerStrategy;
+}
+
+void SkiaCanvasProxy::willRestore() {
+ mCanvas->restore();
+}
+
+void SkiaCanvasProxy::didConcat(const SkMatrix& matrix) {
+ mCanvas->concat(matrix);
+}
+
+void SkiaCanvasProxy::didSetMatrix(const SkMatrix& matrix) {
+ mCanvas->setMatrix(matrix);
+}
+
+void SkiaCanvasProxy::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
+ const SkPaint& paint) {
+ SkPath path;
+ path.addRRect(outer);
+ path.addRRect(inner);
+ path.setFillType(SkPath::kEvenOdd_FillType);
+ this->drawPath(path, paint);
+}
+
+/**
+ * Utility class that converts the incoming text & paint from the given encoding
+ * into glyphIDs.
+ */
+class GlyphIDConverter {
+public:
+ GlyphIDConverter(const void* text, size_t byteLength, const SkPaint& origPaint) {
+ paint = origPaint;
+ if (paint.getTextEncoding() == SkPaint::kGlyphID_TextEncoding) {
+ glyphIDs = (uint16_t*)text;
+ count = byteLength >> 1;
+ } else {
+ storage.reset(byteLength); // ensures space for one glyph per ID given UTF8 encoding.
+ glyphIDs = storage.get();
+ count = paint.textToGlyphs(text, byteLength, storage.get());
+ paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ }
+ }
+
+ SkPaint paint;
+ uint16_t* glyphIDs;
+ int count;
+private:
+ SkAutoSTMalloc<32, uint16_t> storage;
+};
+
+void SkiaCanvasProxy::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
+ const SkPaint& origPaint) {
+ // convert to glyphIDs if necessary
+ GlyphIDConverter glyphs(text, byteLength, origPaint);
+
+ // compute the glyph positions
+ SkAutoSTMalloc<32, SkPoint> pointStorage(glyphs.count);
+ SkAutoSTMalloc<32, SkScalar> glyphWidths(glyphs.count);
+ glyphs.paint.getTextWidths(glyphs.glyphIDs, glyphs.count << 1, glyphWidths.get());
+
+ // compute conservative bounds
+ // NOTE: We could call the faster paint.getFontBounds for a less accurate,
+ // but even more conservative bounds if this is too slow.
+ SkRect bounds;
+ glyphs.paint.measureText(glyphs.glyphIDs, glyphs.count << 1, &bounds);
+
+ // adjust for non-left alignment
+ if (glyphs.paint.getTextAlign() != SkPaint::kLeft_Align) {
+ SkScalar stop = 0;
+ for (int i = 0; i < glyphs.count; i++) {
+ stop += glyphWidths[i];
+ }
+ if (glyphs.paint.getTextAlign() == SkPaint::kCenter_Align) {
+ stop = SkScalarHalf(stop);
+ }
+ if (glyphs.paint.isVerticalText()) {
+ y -= stop;
+ } else {
+ x -= stop;
+ }
+ }
+
+ // setup the first glyph position and adjust bounds if needed
+ int xBaseline = 0;
+ int yBaseline = 0;
+ if (mCanvas->drawTextAbsolutePos()) {
+ bounds.offset(x,y);
+ xBaseline = x;
+ yBaseline = y;
+ }
+ pointStorage[0].set(xBaseline, yBaseline);
+
+ // setup the remaining glyph positions
+ if (glyphs.paint.isVerticalText()) {
+ for (int i = 1; i < glyphs.count; i++) {
+ pointStorage[i].set(xBaseline, glyphWidths[i-1] + pointStorage[i-1].fY);
+ }
+ } else {
+ for (int i = 1; i < glyphs.count; i++) {
+ pointStorage[i].set(glyphWidths[i-1] + pointStorage[i-1].fX, yBaseline);
+ }
+ }
+
+ SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats);
+ mCanvas->drawText(glyphs.glyphIDs, &pointStorage[0].fX, glyphs.count, glyphs.paint,
+ x, y, bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, 0);
+}
+
+void SkiaCanvasProxy::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
+ const SkPaint& origPaint) {
+ // convert to glyphIDs if necessary
+ GlyphIDConverter glyphs(text, byteLength, origPaint);
+
+ // convert to relative positions if necessary
+ int x, y;
+ const SkPoint* posArray;
+ SkAutoSTMalloc<32, SkPoint> pointStorage;
+ if (mCanvas->drawTextAbsolutePos()) {
+ x = 0;
+ y = 0;
+ posArray = pos;
+ } else {
+ x = pos[0].fX;
+ y = pos[0].fY;
+ posArray = pointStorage.reset(glyphs.count);
+ for (int i = 0; i < glyphs.count; i++) {
+ pointStorage[i].fX = pos[i].fX- x;
+ pointStorage[i].fY = pos[i].fY- y;
+ }
+ }
+
+ // compute conservative bounds
+ // NOTE: We could call the faster paint.getFontBounds for a less accurate,
+ // but even more conservative bounds if this is too slow.
+ SkRect bounds;
+ glyphs.paint.measureText(glyphs.glyphIDs, glyphs.count << 1, &bounds);
+ bounds.offset(x, y);
+
+ SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats);
+ mCanvas->drawText(glyphs.glyphIDs, &posArray[0].fX, glyphs.count, glyphs.paint, x, y,
+ bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, 0);
+}
+
+void SkiaCanvasProxy::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
+ SkScalar constY, const SkPaint& paint) {
+ const size_t pointCount = byteLength >> 1;
+ SkAutoSTMalloc<32, SkPoint> storage(pointCount);
+ SkPoint* pts = storage.get();
+ for (size_t i = 0; i < pointCount; i++) {
+ pts[i].set(xpos[i], constY);
+ }
+ this->onDrawPosText(text, byteLength, pts, paint);
+}
+
+void SkiaCanvasProxy::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
+ const SkMatrix* matrix, const SkPaint& origPaint) {
+ // convert to glyphIDs if necessary
+ GlyphIDConverter glyphs(text, byteLength, origPaint);
+ mCanvas->drawTextOnPath(glyphs.glyphIDs, glyphs.count, path, 0, 0, glyphs.paint);
+}
+
+void SkiaCanvasProxy::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
+ const SkPaint& paint) {
+ SkDEBUGFAIL("SkiaCanvasProxy::onDrawTextBlob is not supported");
+}
+
+void SkiaCanvasProxy::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
+ const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
+ if (mFilterHwuiCalls) {
+ return;
+ }
+ SkPatchUtils::VertexData data;
+
+ SkMatrix matrix;
+ mCanvas->getMatrix(&matrix);
+ SkISize lod = SkPatchUtils::GetLevelOfDetail(cubics, &matrix);
+
+ // It automatically adjusts lodX and lodY in case it exceeds the number of indices.
+ // If it fails to generate the vertices, then we do not draw.
+ if (SkPatchUtils::getVertexData(&data, cubics, colors, texCoords, lod.width(), lod.height())) {
+ this->drawVertices(SkCanvas::kTriangles_VertexMode, data.fVertexCount, data.fPoints,
+ data.fTexCoords, data.fColors, xmode, data.fIndices, data.fIndexCount,
+ paint);
+ }
+}
+
+void SkiaCanvasProxy::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle) {
+ mCanvas->clipRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, op);
+}
+
+void SkiaCanvasProxy::onClipRRect(const SkRRect& roundRect, SkRegion::Op op, ClipEdgeStyle) {
+ SkPath path;
+ path.addRRect(roundRect);
+ mCanvas->clipPath(&path, op);
+}
+
+void SkiaCanvasProxy::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle) {
+ mCanvas->clipPath(&path, op);
+}
+
+void SkiaCanvasProxy::onClipRegion(const SkRegion& region, SkRegion::Op op) {
+ mCanvas->clipRegion(&region, op);
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/SkiaCanvasProxy.h b/libs/hwui/SkiaCanvasProxy.h
new file mode 100644
index 0000000..0de9650
--- /dev/null
+++ b/libs/hwui/SkiaCanvasProxy.h
@@ -0,0 +1,108 @@
+/*
+ * 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 SkiaCanvasProxy_DEFINED
+#define SkiaCanvasProxy_DEFINED
+
+#include <cutils/compiler.h>
+#include <SkCanvas.h>
+
+#include "Canvas.h"
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * This class serves as a proxy between Skia's SkCanvas and Android Framework's
+ * Canvas. The class does not maintain any draw-related state and will pass
+ * through most requests directly to the Canvas provided in the constructor.
+ *
+ * Upon construction it is expected that the provided Canvas has already been
+ * prepared for recording and will continue to be in the recording state while
+ * this proxy class is being used.
+ *
+ * If filterHwuiCalls is true, the proxy silently ignores away draw calls that
+ * aren't supported by HWUI.
+ */
+class ANDROID_API SkiaCanvasProxy : public SkCanvas {
+public:
+ SkiaCanvasProxy(Canvas* canvas, bool filterHwuiCalls = false);
+ virtual ~SkiaCanvasProxy() {}
+
+protected:
+
+ virtual SkSurface* onNewSurface(const SkImageInfo&, const SkSurfaceProps&) override;
+
+ virtual void willSave() override;
+ virtual SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) override;
+ virtual void willRestore() override;
+
+ virtual void didConcat(const SkMatrix&) override;
+ virtual void didSetMatrix(const SkMatrix&) override;
+
+ virtual void onDrawPaint(const SkPaint& paint) override;
+ virtual void onDrawPoints(PointMode, size_t count, const SkPoint pts[],
+ const SkPaint&) override;
+ virtual void onDrawOval(const SkRect&, const SkPaint&) override;
+ virtual void onDrawRect(const SkRect&, const SkPaint&) override;
+ virtual void onDrawRRect(const SkRRect&, const SkPaint&) override;
+ virtual void onDrawPath(const SkPath& path, const SkPaint&) override;
+ virtual void onDrawBitmap(const SkBitmap&, SkScalar left, SkScalar top,
+ const SkPaint*) override;
+ virtual void onDrawBitmapRect(const SkBitmap&, const SkRect* src, const SkRect& dst,
+ const SkPaint* paint, DrawBitmapRectFlags flags) override;
+ virtual void onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
+ const SkRect& dst, const SkPaint*) override;
+ virtual void onDrawSprite(const SkBitmap&, int left, int top,
+ const SkPaint*) override;
+ virtual void onDrawVertices(VertexMode, int vertexCount, const SkPoint vertices[],
+ const SkPoint texs[], const SkColor colors[], SkXfermode*,
+ const uint16_t indices[], int indexCount,
+ const SkPaint&) override;
+
+ virtual void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override;
+
+ virtual void onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
+ const SkPaint&) override;
+ virtual void onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
+ const SkPaint&) override;
+ virtual void onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
+ SkScalar constY, const SkPaint&) override;
+ virtual void onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
+ const SkMatrix* matrix, const SkPaint&) override;
+ virtual void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
+ const SkPaint& paint) override;
+
+ virtual void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
+ const SkPoint texCoords[4], SkXfermode* xmode,
+ const SkPaint& paint) override;
+
+ virtual void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) override;
+ virtual void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) override;
+ virtual void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) override;
+ virtual void onClipRegion(const SkRegion&, SkRegion::Op) override;
+
+private:
+ Canvas* mCanvas;
+ bool mFilterHwuiCalls;
+
+ typedef SkCanvas INHERITED;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // SkiaCanvasProxy_DEFINED
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index c672bc4..ecf8b6b 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -16,16 +16,17 @@
#define LOG_TAG "OpenGLRenderer"
-#include <utils/Log.h>
-
-#include <SkMatrix.h>
+#include "SkiaShader.h"
#include "Caches.h"
+#include "Extensions.h"
#include "Layer.h"
#include "Matrix.h"
-#include "SkiaShader.h"
#include "Texture.h"
+#include <SkMatrix.h>
+#include <utils/Log.h>
+
namespace android {
namespace uirenderer {
@@ -33,7 +34,7 @@ namespace uirenderer {
// Support
///////////////////////////////////////////////////////////////////////////////
-static const GLint gTileModes[] = {
+static const GLenum gTileModes[] = {
GL_CLAMP_TO_EDGE, // == SkShader::kClamp_TileMode
GL_REPEAT, // == SkShader::kRepeat_Mode
GL_MIRRORED_REPEAT // == SkShader::kMirror_TileMode
@@ -46,17 +47,12 @@ static inline bool isPowerOfTwo(unsigned int n) {
return !(n & (n - 1));
}
-static inline void bindUniformColor(int slot, uint32_t color) {
- const float a = ((color >> 24) & 0xff) / 255.0f;
- glUniform4f(slot,
- a * ((color >> 16) & 0xff) / 255.0f,
- a * ((color >> 8) & 0xff) / 255.0f,
- a * ((color ) & 0xff) / 255.0f,
- a);
+static inline void bindUniformColor(int slot, FloatColor color) {
+ glUniform4fv(slot, 1, reinterpret_cast<const float*>(&color));
}
static inline void bindTexture(Caches* caches, Texture* texture, GLenum wrapS, GLenum wrapT) {
- caches->bindTexture(texture->id);
+ caches->textureState().bindTexture(texture->id);
texture->setWrapST(wrapS, wrapT);
}
@@ -78,229 +74,11 @@ static void computeScreenSpaceMatrix(mat4& screenSpace, const SkMatrix& unitMatr
screenSpace.multiply(modelViewMatrix);
}
-// Returns true if one is a bitmap and the other is a gradient
-static bool bitmapAndGradient(SkiaShaderType type1, SkiaShaderType type2) {
- return (type1 == kBitmap_SkiaShaderType && type2 == kGradient_SkiaShaderType)
- || (type2 == kBitmap_SkiaShaderType && type1 == kGradient_SkiaShaderType);
-}
-
-SkiaShaderType SkiaShader::getType(const SkShader& shader) {
- // First check for a gradient shader.
- switch (shader.asAGradient(NULL)) {
- case SkShader::kNone_GradientType:
- // Not a gradient shader. Fall through to check for other types.
- break;
- case SkShader::kLinear_GradientType:
- case SkShader::kRadial_GradientType:
- case SkShader::kSweep_GradientType:
- return kGradient_SkiaShaderType;
- default:
- // This is a Skia gradient that has no SkiaShader equivalent. Return None to skip.
- return kNone_SkiaShaderType;
- }
-
- // The shader is not a gradient. Check for a bitmap shader.
- if (shader.asABitmap(NULL, NULL, NULL) == SkShader::kDefault_BitmapType) {
- return kBitmap_SkiaShaderType;
- }
-
- // Check for a ComposeShader.
- SkShader::ComposeRec rec;
- if (shader.asACompose(&rec)) {
- const SkiaShaderType shaderAType = getType(*rec.fShaderA);
- const SkiaShaderType shaderBType = getType(*rec.fShaderB);
-
- // Compose is only supported if one is a bitmap and the other is a
- // gradient. Otherwise, return None to skip.
- if (!bitmapAndGradient(shaderAType, shaderBType)) {
- return kNone_SkiaShaderType;
- }
- return kCompose_SkiaShaderType;
- }
-
- if (shader.asACustomShader(NULL)) {
- return kLayer_SkiaShaderType;
- }
-
- return kNone_SkiaShaderType;
-}
-
-typedef void (*describeProc)(Caches* caches, ProgramDescription& description,
- const Extensions& extensions, const SkShader& shader);
-
-describeProc gDescribeProc[] = {
- InvalidSkiaShader::describe,
- SkiaBitmapShader::describe,
- SkiaGradientShader::describe,
- SkiaComposeShader::describe,
- SkiaLayerShader::describe,
-};
-
-typedef void (*setupProgramProc)(Caches* caches, const mat4& modelViewMatrix,
- GLuint* textureUnit, const Extensions& extensions, const SkShader& shader);
-
-setupProgramProc gSetupProgramProc[] = {
- InvalidSkiaShader::setupProgram,
- SkiaBitmapShader::setupProgram,
- SkiaGradientShader::setupProgram,
- SkiaComposeShader::setupProgram,
- SkiaLayerShader::setupProgram,
-};
-
-void SkiaShader::describe(Caches* caches, ProgramDescription& description,
- const Extensions& extensions, const SkShader& shader) {
- gDescribeProc[getType(shader)](caches, description, extensions, shader);
-}
-
-void SkiaShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
- GLuint* textureUnit, const Extensions& extensions, const SkShader& shader) {
-
- gSetupProgramProc[getType(shader)](caches, modelViewMatrix, textureUnit, extensions, shader);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Layer shader
-///////////////////////////////////////////////////////////////////////////////
-
-void SkiaLayerShader::describe(Caches*, ProgramDescription& description,
- const Extensions&, const SkShader& shader) {
- description.hasBitmap = true;
-}
-
-void SkiaLayerShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
- GLuint* textureUnit, const Extensions&, const SkShader& shader) {
- Layer* layer;
- if (!shader.asACustomShader(reinterpret_cast<void**>(&layer))) {
- LOG_ALWAYS_FATAL("SkiaLayerShader::setupProgram called on the wrong type of shader!");
- }
-
- GLuint textureSlot = (*textureUnit)++;
- caches->activeTexture(textureSlot);
-
- const float width = layer->getWidth();
- const float height = layer->getHeight();
-
- mat4 textureTransform;
- computeScreenSpaceMatrix(textureTransform, SkMatrix::I(), shader.getLocalMatrix(),
- modelViewMatrix);
-
-
- // Uniforms
- layer->bindTexture();
- layer->setWrap(GL_CLAMP_TO_EDGE);
- layer->setFilter(GL_LINEAR);
-
- Program* program = caches->currentProgram;
- glUniform1i(program->getUniform("bitmapSampler"), textureSlot);
- glUniformMatrix4fv(program->getUniform("textureTransform"), 1,
- GL_FALSE, &textureTransform.data[0]);
- glUniform2f(program->getUniform("textureDimension"), 1.0f / width, 1.0f / height);
-}
-
///////////////////////////////////////////////////////////////////////////////
-// Bitmap shader
+// gradient shader matrix helpers
///////////////////////////////////////////////////////////////////////////////
-struct BitmapShaderInfo {
- float width;
- float height;
- GLenum wrapS;
- GLenum wrapT;
- Texture* texture;
-};
-
-static bool bitmapShaderHelper(Caches* caches, ProgramDescription* description,
- BitmapShaderInfo* shaderInfo,
- const Extensions& extensions,
- const SkBitmap& bitmap, SkShader::TileMode tileModes[2]) {
- Texture* texture = caches->textureCache.get(&bitmap);
- if (!texture) return false;
-
- const float width = texture->width;
- const float height = texture->height;
- GLenum wrapS, wrapT;
-
- if (description) {
- description->hasBitmap = true;
- }
- // The driver does not support non-power of two mirrored/repeated
- // textures, so do it ourselves
- if (!extensions.hasNPot() && (!isPowerOfTwo(width) || !isPowerOfTwo(height)) &&
- (tileModes[0] != SkShader::kClamp_TileMode ||
- tileModes[1] != SkShader::kClamp_TileMode)) {
- if (description) {
- description->isBitmapNpot = true;
- description->bitmapWrapS = gTileModes[tileModes[0]];
- description->bitmapWrapT = gTileModes[tileModes[1]];
- }
- wrapS = GL_CLAMP_TO_EDGE;
- wrapT = GL_CLAMP_TO_EDGE;
- } else {
- wrapS = gTileModes[tileModes[0]];
- wrapT = gTileModes[tileModes[1]];
- }
-
- if (shaderInfo) {
- shaderInfo->width = width;
- shaderInfo->height = height;
- shaderInfo->wrapS = wrapS;
- shaderInfo->wrapT = wrapT;
- shaderInfo->texture = texture;
- }
- return true;
-}
-
-void SkiaBitmapShader::describe(Caches* caches, ProgramDescription& description,
- const Extensions& extensions, const SkShader& shader) {
- SkBitmap bitmap;
- SkShader::TileMode xy[2];
- if (shader.asABitmap(&bitmap, NULL, xy) != SkShader::kDefault_BitmapType) {
- LOG_ALWAYS_FATAL("SkiaBitmapShader::describe called with a different kind of shader!");
- }
- bitmapShaderHelper(caches, &description, NULL, extensions, bitmap, xy);
-}
-
-void SkiaBitmapShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
- GLuint* textureUnit, const Extensions& extensions, const SkShader& shader) {
- SkBitmap bitmap;
- SkShader::TileMode xy[2];
- if (shader.asABitmap(&bitmap, NULL, xy) != SkShader::kDefault_BitmapType) {
- LOG_ALWAYS_FATAL("SkiaBitmapShader::setupProgram called with a different kind of shader!");
- }
-
- GLuint textureSlot = (*textureUnit)++;
- Caches::getInstance().activeTexture(textureSlot);
-
- BitmapShaderInfo shaderInfo;
- if (!bitmapShaderHelper(caches, NULL, &shaderInfo, extensions, bitmap, xy)) {
- return;
- }
-
- Program* program = caches->currentProgram;
- Texture* texture = shaderInfo.texture;
-
- const AutoTexture autoCleanup(texture);
-
- mat4 textureTransform;
- computeScreenSpaceMatrix(textureTransform, SkMatrix::I(), shader.getLocalMatrix(),
- modelViewMatrix);
-
- // Uniforms
- bindTexture(caches, texture, shaderInfo.wrapS, shaderInfo.wrapT);
- texture->setFilter(GL_LINEAR);
-
- glUniform1i(program->getUniform("bitmapSampler"), textureSlot);
- glUniformMatrix4fv(program->getUniform("textureTransform"), 1,
- GL_FALSE, &textureTransform.data[0]);
- glUniform2f(program->getUniform("textureDimension"), 1.0f / shaderInfo.width,
- 1.0f / shaderInfo.height);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Linear gradient shader
-///////////////////////////////////////////////////////////////////////////////
-
-static void toUnitMatrix(const SkPoint pts[2], SkMatrix* matrix) {
+static void toLinearUnitMatrix(const SkPoint pts[2], SkMatrix* matrix) {
SkVector vec = pts[1] - pts[0];
const float mag = vec.length();
const float inv = mag ? 1.0f / mag : 0;
@@ -311,10 +89,6 @@ static void toUnitMatrix(const SkPoint pts[2], SkMatrix* matrix) {
matrix->postScale(inv, inv);
}
-///////////////////////////////////////////////////////////////////////////////
-// Circular gradient shader
-///////////////////////////////////////////////////////////////////////////////
-
static void toCircularUnitMatrix(const float x, const float y, const float radius,
SkMatrix* matrix) {
const float inv = 1.0f / radius;
@@ -322,10 +96,6 @@ static void toCircularUnitMatrix(const float x, const float y, const float radiu
matrix->postScale(inv, inv);
}
-///////////////////////////////////////////////////////////////////////////////
-// Sweep gradient shader
-///////////////////////////////////////////////////////////////////////////////
-
static void toSweepUnitMatrix(const float x, const float y, SkMatrix* matrix) {
matrix->setTranslate(-x, -y);
}
@@ -338,135 +108,289 @@ static bool isSimpleGradient(const SkShader::GradientInfo& gradInfo) {
return gradInfo.fColorCount == 2 && gradInfo.fTileMode == SkShader::kClamp_TileMode;
}
-void SkiaGradientShader::describe(Caches*, ProgramDescription& description,
- const Extensions& extensions, const SkShader& shader) {
+///////////////////////////////////////////////////////////////////////////////
+// Store / apply
+///////////////////////////////////////////////////////////////////////////////
+
+bool tryStoreGradient(Caches& caches, const SkShader& shader, const Matrix4 modelViewMatrix,
+ GLuint* textureUnit, ProgramDescription* description,
+ SkiaShaderData::GradientShaderData* outData) {
SkShader::GradientInfo gradInfo;
gradInfo.fColorCount = 0;
- gradInfo.fColors = NULL;
- gradInfo.fColorOffsets = NULL;
+ gradInfo.fColors = nullptr;
+ gradInfo.fColorOffsets = nullptr;
+ SkMatrix unitMatrix;
switch (shader.asAGradient(&gradInfo)) {
case SkShader::kLinear_GradientType:
- description.gradientType = ProgramDescription::kGradientLinear;
+ description->gradientType = ProgramDescription::kGradientLinear;
+
+ toLinearUnitMatrix(gradInfo.fPoint, &unitMatrix);
break;
case SkShader::kRadial_GradientType:
- description.gradientType = ProgramDescription::kGradientCircular;
+ description->gradientType = ProgramDescription::kGradientCircular;
+
+ toCircularUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY,
+ gradInfo.fRadius[0], &unitMatrix);
break;
case SkShader::kSweep_GradientType:
- description.gradientType = ProgramDescription::kGradientSweep;
+ description->gradientType = ProgramDescription::kGradientSweep;
+
+ toSweepUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY, &unitMatrix);
break;
default:
// Do nothing. This shader is unsupported.
- return;
+ return false;
}
- description.hasGradient = true;
- description.isSimpleGradient = isSimpleGradient(gradInfo);
-}
+ description->hasGradient = true;
+ description->isSimpleGradient = isSimpleGradient(gradInfo);
-void SkiaGradientShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
- GLuint* textureUnit, const Extensions&, const SkShader& shader) {
- // SkShader::GradientInfo.fColorCount is an in/out parameter. As input, it tells asAGradient
- // how much space has been allocated for fColors and fColorOffsets. 10 was chosen
- // arbitrarily, but should be >= 2.
- // As output, it tells the number of actual colors/offsets in the gradient.
- const int COLOR_COUNT = 10;
- SkAutoSTMalloc<COLOR_COUNT, SkColor> colorStorage(COLOR_COUNT);
- SkAutoSTMalloc<COLOR_COUNT, SkScalar> positionStorage(COLOR_COUNT);
+ computeScreenSpaceMatrix(outData->screenSpace, unitMatrix,
+ shader.getLocalMatrix(), modelViewMatrix);
- SkShader::GradientInfo gradInfo;
- gradInfo.fColorCount = COLOR_COUNT;
- gradInfo.fColors = colorStorage.get();
- gradInfo.fColorOffsets = positionStorage.get();
+ // re-query shader to get full color / offset data
+ std::unique_ptr<SkColor[]> colorStorage(new SkColor[gradInfo.fColorCount]);
+ std::unique_ptr<SkScalar[]> colorOffsets(new SkScalar[gradInfo.fColorCount]);
+ gradInfo.fColors = &colorStorage[0];
+ gradInfo.fColorOffsets = &colorOffsets[0];
+ shader.asAGradient(&gradInfo);
- SkShader::GradientType gradType = shader.asAGradient(&gradInfo);
-
- Program* program = caches->currentProgram;
if (CC_UNLIKELY(!isSimpleGradient(gradInfo))) {
- if (gradInfo.fColorCount > COLOR_COUNT) {
- // There was not enough room in our arrays for all the colors and offsets. Try again,
- // now that we know the true number of colors.
- gradInfo.fColors = colorStorage.reset(gradInfo.fColorCount);
- gradInfo.fColorOffsets = positionStorage.reset(gradInfo.fColorCount);
-
- shader.asAGradient(&gradInfo);
- }
- GLuint textureSlot = (*textureUnit)++;
- caches->activeTexture(textureSlot);
+ outData->gradientSampler = (*textureUnit)++;
#ifndef SK_SCALAR_IS_FLOAT
#error Need to convert gradInfo.fColorOffsets to float!
#endif
- Texture* texture = caches->gradientCache.get(gradInfo.fColors, gradInfo.fColorOffsets,
- gradInfo.fColorCount);
+ outData->gradientTexture = caches.gradientCache.get(
+ gradInfo.fColors, gradInfo.fColorOffsets, gradInfo.fColorCount);
+ outData->wrapST = gTileModes[gradInfo.fTileMode];
+ } else {
+ outData->gradientSampler = 0;
+ outData->gradientTexture = nullptr;
- // Uniforms
- bindTexture(caches, texture, gTileModes[gradInfo.fTileMode], gTileModes[gradInfo.fTileMode]);
- glUniform1i(program->getUniform("gradientSampler"), textureSlot);
+ outData->startColor.set(gradInfo.fColors[0]);
+ outData->endColor.set(gradInfo.fColors[1]);
+ }
+
+ outData->ditherSampler = (*textureUnit)++;
+ return true;
+}
+
+void applyGradient(Caches& caches, const SkiaShaderData::GradientShaderData& data) {
+ if (CC_UNLIKELY(data.gradientTexture)) {
+ caches.textureState().activateTexture(data.gradientSampler);
+ bindTexture(&caches, data.gradientTexture, data.wrapST, data.wrapST);
+ glUniform1i(caches.program().getUniform("gradientSampler"), data.gradientSampler);
} else {
- bindUniformColor(program->getUniform("startColor"), gradInfo.fColors[0]);
- bindUniformColor(program->getUniform("endColor"), gradInfo.fColors[1]);
+ bindUniformColor(caches.program().getUniform("startColor"), data.startColor);
+ bindUniformColor(caches.program().getUniform("endColor"), data.endColor);
}
- caches->dither.setupProgram(program, textureUnit);
+ // TODO: remove sampler slot incrementing from dither.setupProgram,
+ // since this assignment of slots is done at store, not apply time
+ GLuint ditherSampler = data.ditherSampler;
+ caches.dither.setupProgram(caches.program(), &ditherSampler);
+ glUniformMatrix4fv(caches.program().getUniform("screenSpace"), 1,
+ GL_FALSE, &data.screenSpace.data[0]);
+}
- SkMatrix unitMatrix;
- switch (gradType) {
- case SkShader::kLinear_GradientType:
- toUnitMatrix(gradInfo.fPoint, &unitMatrix);
+bool tryStoreBitmap(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix,
+ GLuint* textureUnit, ProgramDescription* description,
+ SkiaShaderData::BitmapShaderData* outData) {
+ SkBitmap bitmap;
+ SkShader::TileMode xy[2];
+ if (shader.asABitmap(&bitmap, nullptr, xy) != SkShader::kDefault_BitmapType) {
+ return false;
+ }
+
+ outData->bitmapTexture = caches.textureCache.get(&bitmap);
+ if (!outData->bitmapTexture) return false;
+
+ outData->bitmapSampler = (*textureUnit)++;
+
+ const float width = outData->bitmapTexture->width;
+ const float height = outData->bitmapTexture->height;
+
+ description->hasBitmap = true;
+ if (!caches.extensions().hasNPot()
+ && (!isPowerOfTwo(width) || !isPowerOfTwo(height))
+ && (xy[0] != SkShader::kClamp_TileMode || xy[1] != SkShader::kClamp_TileMode)) {
+ description->isBitmapNpot = true;
+ description->bitmapWrapS = gTileModes[xy[0]];
+ description->bitmapWrapT = gTileModes[xy[1]];
+
+ outData->wrapS = GL_CLAMP_TO_EDGE;
+ outData->wrapT = GL_CLAMP_TO_EDGE;
+ } else {
+ outData->wrapS = gTileModes[xy[0]];
+ outData->wrapT = gTileModes[xy[1]];
+ }
+
+ computeScreenSpaceMatrix(outData->textureTransform, SkMatrix::I(), shader.getLocalMatrix(),
+ modelViewMatrix);
+ outData->textureDimension[0] = 1.0f / width;
+ outData->textureDimension[1] = 1.0f / height;
+
+ return true;
+}
+
+void applyBitmap(Caches& caches, const SkiaShaderData::BitmapShaderData& data) {
+ caches.textureState().activateTexture(data.bitmapSampler);
+ bindTexture(&caches, data.bitmapTexture, data.wrapS, data.wrapT);
+ data.bitmapTexture->setFilter(GL_LINEAR);
+
+ glUniform1i(caches.program().getUniform("bitmapSampler"), data.bitmapSampler);
+ glUniformMatrix4fv(caches.program().getUniform("textureTransform"), 1, GL_FALSE,
+ &data.textureTransform.data[0]);
+ glUniform2fv(caches.program().getUniform("textureDimension"), 1, &data.textureDimension[0]);
+}
+
+SkiaShaderType getComposeSubType(const SkShader& shader) {
+ // First check for a gradient shader.
+ switch (shader.asAGradient(nullptr)) {
+ case SkShader::kNone_GradientType:
+ // Not a gradient shader. Fall through to check for other types.
break;
+ case SkShader::kLinear_GradientType:
case SkShader::kRadial_GradientType:
- toCircularUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY,
- gradInfo.fRadius[0], &unitMatrix);
- break;
case SkShader::kSweep_GradientType:
- toSweepUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY, &unitMatrix);
- break;
+ return kGradient_SkiaShaderType;
default:
- LOG_ALWAYS_FATAL("Invalid SkShader gradient type %d", gradType);
+ // This is a Skia gradient that has no SkiaShader equivalent. Return None to skip.
+ return kNone_SkiaShaderType;
+ }
+
+ // The shader is not a gradient. Check for a bitmap shader.
+ if (shader.asABitmap(nullptr, nullptr, nullptr) == SkShader::kDefault_BitmapType) {
+ return kBitmap_SkiaShaderType;
}
+ return kNone_SkiaShaderType;
+}
- mat4 screenSpace;
- computeScreenSpaceMatrix(screenSpace, unitMatrix, shader.getLocalMatrix(), modelViewMatrix);
- glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
+void storeCompose(Caches& caches, const SkShader& bitmapShader, const SkShader& gradientShader,
+ const Matrix4& modelViewMatrix, GLuint* textureUnit,
+ ProgramDescription* description, SkiaShaderData* outData) {
+ LOG_ALWAYS_FATAL_IF(!tryStoreBitmap(caches, bitmapShader, modelViewMatrix,
+ textureUnit, description, &outData->bitmapData),
+ "failed storing bitmap shader data");
+ LOG_ALWAYS_FATAL_IF(!tryStoreGradient(caches, gradientShader, modelViewMatrix,
+ textureUnit, description, &outData->gradientData),
+ "failing storing gradient shader data");
}
-///////////////////////////////////////////////////////////////////////////////
-// Compose shader
-///////////////////////////////////////////////////////////////////////////////
+bool tryStoreCompose(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix,
+ GLuint* textureUnit, ProgramDescription* description,
+ SkiaShaderData* outData) {
-void SkiaComposeShader::describe(Caches* caches, ProgramDescription& description,
- const Extensions& extensions, const SkShader& shader) {
SkShader::ComposeRec rec;
- if (!shader.asACompose(&rec)) {
- LOG_ALWAYS_FATAL("SkiaComposeShader::describe called on the wrong shader type!");
- }
- SkiaShader::describe(caches, description, extensions, *rec.fShaderA);
- SkiaShader::describe(caches, description, extensions, *rec.fShaderB);
- if (SkiaShader::getType(*rec.fShaderA) == kBitmap_SkiaShaderType) {
- description.isBitmapFirst = true;
+ if (!shader.asACompose(&rec)) return false;
+
+ const SkiaShaderType shaderAType = getComposeSubType(*rec.fShaderA);
+ const SkiaShaderType shaderBType = getComposeSubType(*rec.fShaderB);
+
+ // check that type enum values are the 2 flags that compose the kCompose value
+ if ((shaderAType & shaderBType) != 0) return false;
+ if ((shaderAType | shaderBType) != kCompose_SkiaShaderType) return false;
+
+ mat4 transform;
+ computeScreenSpaceMatrix(transform, SkMatrix::I(), shader.getLocalMatrix(), modelViewMatrix);
+ if (shaderAType == kBitmap_SkiaShaderType) {
+ description->isBitmapFirst = true;
+ storeCompose(caches, *rec.fShaderA, *rec.fShaderB,
+ transform, textureUnit, description, outData);
+ } else {
+ description->isBitmapFirst = false;
+ storeCompose(caches, *rec.fShaderB, *rec.fShaderA,
+ transform, textureUnit, description, outData);
}
- if (!SkXfermode::AsMode(rec.fMode, &description.shadersMode)) {
+ if (!SkXfermode::AsMode(rec.fMode, &description->shadersMode)) {
// TODO: Support other modes.
- description.shadersMode = SkXfermode::kSrcOver_Mode;
+ description->shadersMode = SkXfermode::kSrcOver_Mode;
}
+ return true;
}
-void SkiaComposeShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
- GLuint* textureUnit, const Extensions& extensions, const SkShader& shader) {
- SkShader::ComposeRec rec;
- if (!shader.asACompose(&rec)) {
- LOG_ALWAYS_FATAL("SkiaComposeShader::setupProgram called on the wrong shader type!");
+bool tryStoreLayer(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix,
+ GLuint* textureUnit, ProgramDescription* description,
+ SkiaShaderData::LayerShaderData* outData) {
+ Layer* layer;
+ if (!shader.asACustomShader(reinterpret_cast<void**>(&layer))) {
+ return false;
}
- // Apply this compose shader's local transform and pass it down to
- // the child shaders. They will in turn apply their local transform
- // to this matrix.
- mat4 transform;
- computeScreenSpaceMatrix(transform, SkMatrix::I(), shader.getLocalMatrix(),
+ description->hasBitmap = true;
+ outData->layer = layer;
+ outData->bitmapSampler = (*textureUnit)++;
+
+ const float width = layer->getWidth();
+ const float height = layer->getHeight();
+
+ computeScreenSpaceMatrix(outData->textureTransform, SkMatrix::I(), shader.getLocalMatrix(),
modelViewMatrix);
- SkiaShader::setupProgram(caches, transform, textureUnit, extensions, *rec.fShaderA);
- SkiaShader::setupProgram(caches, transform, textureUnit, extensions, *rec.fShaderB);
+ outData->textureDimension[0] = 1.0f / width;
+ outData->textureDimension[1] = 1.0f / height;
+ return true;
+}
+
+void applyLayer(Caches& caches, const SkiaShaderData::LayerShaderData& data) {
+ caches.textureState().activateTexture(data.bitmapSampler);
+
+ data.layer->bindTexture();
+ data.layer->setWrap(GL_CLAMP_TO_EDGE);
+ data.layer->setFilter(GL_LINEAR);
+
+ glUniform1i(caches.program().getUniform("bitmapSampler"), data.bitmapSampler);
+ glUniformMatrix4fv(caches.program().getUniform("textureTransform"), 1,
+ GL_FALSE, &data.textureTransform.data[0]);
+ glUniform2fv(caches.program().getUniform("textureDimension"), 1, &data.textureDimension[0]);
+}
+
+void SkiaShader::store(Caches& caches, const SkShader* shader, const Matrix4& modelViewMatrix,
+ GLuint* textureUnit, ProgramDescription* description,
+ SkiaShaderData* outData) {
+ if (!shader) {
+ outData->skiaShaderType = kNone_SkiaShaderType;
+ return;
+ }
+
+ if (tryStoreGradient(caches, *shader, modelViewMatrix,
+ textureUnit, description, &outData->gradientData)) {
+ outData->skiaShaderType = kGradient_SkiaShaderType;
+ return;
+ }
+
+ if (tryStoreBitmap(caches, *shader, modelViewMatrix,
+ textureUnit, description, &outData->bitmapData)) {
+ outData->skiaShaderType = kBitmap_SkiaShaderType;
+ return;
+ }
+
+ if (tryStoreCompose(caches, *shader, modelViewMatrix,
+ textureUnit, description, outData)) {
+ outData->skiaShaderType = kCompose_SkiaShaderType;
+ return;
+ }
+
+ if (tryStoreLayer(caches, *shader, modelViewMatrix,
+ textureUnit, description, &outData->layerData)) {
+ outData->skiaShaderType = kLayer_SkiaShaderType;
+ }
+}
+
+void SkiaShader::apply(Caches& caches, const SkiaShaderData& data) {
+ if (!data.skiaShaderType) return;
+
+ if (data.skiaShaderType & kGradient_SkiaShaderType) {
+ applyGradient(caches, data.gradientData);
+ }
+ if (data.skiaShaderType & kBitmap_SkiaShaderType) {
+ applyBitmap(caches, data.bitmapData);
+ }
+
+ if (data.skiaShaderType == kLayer_SkiaShaderType) {
+ applyLayer(caches, data.layerData);
+ }
}
}; // namespace uirenderer
diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h
index 034c3f6..5b8aa86 100644
--- a/libs/hwui/SkiaShader.h
+++ b/libs/hwui/SkiaShader.h
@@ -17,102 +17,82 @@
#ifndef ANDROID_HWUI_SKIA_SHADER_H
#define ANDROID_HWUI_SKIA_SHADER_H
-#include <SkShader.h>
-#include <SkXfermode.h>
+#include "FloatColor.h"
+#include "Matrix.h"
#include <GLES2/gl2.h>
-
+#include <SkShader.h>
+#include <SkXfermode.h>
#include <cutils/compiler.h>
-#include "Extensions.h"
-#include "ProgramCache.h"
-#include "TextureCache.h"
-#include "GradientCache.h"
-
namespace android {
namespace uirenderer {
class Caches;
+class Extensions;
class Layer;
+class Texture;
+struct ProgramDescription;
/**
* Type of Skia shader in use.
+ *
+ * Note that kBitmap | kGradient = kCompose, since Compose implies
+ * both its component types are in use simultaneously. No other
+ * composition of multiple types is supported.
*/
enum SkiaShaderType {
- kNone_SkiaShaderType,
- kBitmap_SkiaShaderType,
- kGradient_SkiaShaderType,
- kCompose_SkiaShaderType,
- kLayer_SkiaShaderType
+ kNone_SkiaShaderType = 0,
+ kBitmap_SkiaShaderType = 1,
+ kGradient_SkiaShaderType = 2,
+ kCompose_SkiaShaderType = kBitmap_SkiaShaderType | kGradient_SkiaShaderType,
+ kLayer_SkiaShaderType = 4,
};
-class SkiaShader {
-public:
- static SkiaShaderType getType(const SkShader& shader);
- static void describe(Caches* caches, ProgramDescription& description,
- const Extensions& extensions, const SkShader& shader);
- static void setupProgram(Caches* caches, const mat4& modelViewMatrix,
- GLuint* textureUnit, const Extensions& extensions, const SkShader& shader);
+struct SkiaShaderData {
+ SkiaShaderType skiaShaderType;
+ struct BitmapShaderData {
+ Texture* bitmapTexture;
+ GLuint bitmapSampler;
+ GLenum wrapS;
+ GLenum wrapT;
+
+ Matrix4 textureTransform;
+ float textureDimension[2];
+ } bitmapData;
+ struct GradientShaderData {
+ Matrix4 screenSpace;
+ GLuint ditherSampler;
+
+ // simple gradient
+ FloatColor startColor;
+ FloatColor endColor;
+
+ // complex gradient
+ Texture* gradientTexture;
+ GLuint gradientSampler;
+ GLenum wrapST;
+
+ } gradientData;
+ struct LayerShaderData {
+ Layer* layer;
+ GLuint bitmapSampler;
+ GLenum wrapS;
+ GLenum wrapT;
+
+ Matrix4 textureTransform;
+ float textureDimension[2];
+ } layerData;
};
-class InvalidSkiaShader {
-public:
- static void describe(Caches* caches, ProgramDescription& description,
- const Extensions& extensions, const SkShader& shader) {
- // This shader is unsupported. Skip it.
- }
- static void setupProgram(Caches* caches, const mat4& modelViewMatrix,
- GLuint* textureUnit, const Extensions& extensions, const SkShader& shader) {
- // This shader is unsupported. Skip it.
- }
-
-};
-/**
- * A shader that draws a layer.
- */
-class SkiaLayerShader {
-public:
- static void describe(Caches* caches, ProgramDescription& description,
- const Extensions& extensions, const SkShader& shader);
- static void setupProgram(Caches* caches, const mat4& modelViewMatrix,
- GLuint* textureUnit, const Extensions& extensions, const SkShader& shader);
-}; // class SkiaLayerShader
-
-/**
- * A shader that draws a bitmap.
- */
-class SkiaBitmapShader {
-public:
- static void describe(Caches* caches, ProgramDescription& description,
- const Extensions& extensions, const SkShader& shader);
- static void setupProgram(Caches* caches, const mat4& modelViewMatrix,
- GLuint* textureUnit, const Extensions& extensions, const SkShader& shader);
-
-
-}; // class SkiaBitmapShader
-
-/**
- * A shader that draws one of three types of gradient, depending on shader param.
- */
-class SkiaGradientShader {
+class SkiaShader {
public:
- static void describe(Caches* caches, ProgramDescription& description,
- const Extensions& extensions, const SkShader& shader);
- static void setupProgram(Caches* caches, const mat4& modelViewMatrix,
- GLuint* textureUnit, const Extensions& extensions, const SkShader& shader);
+ static void store(Caches& caches, const SkShader* shader, const Matrix4& modelViewMatrix,
+ GLuint* textureUnit, ProgramDescription* description,
+ SkiaShaderData* outData);
+ static void apply(Caches& caches, const SkiaShaderData& data);
};
-/**
- * A shader that draws two shaders, composited with an xfermode.
- */
-class SkiaComposeShader {
-public:
- static void describe(Caches* caches, ProgramDescription& description,
- const Extensions& extensions, const SkShader& shader);
- static void setupProgram(Caches* caches, const mat4& modelViewMatrix,
- GLuint* textureUnit, const Extensions& extensions, const SkShader& shader);
-}; // class SkiaComposeShader
-
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index cf8229f..9e7faee 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -29,17 +29,16 @@ namespace uirenderer {
Snapshot::Snapshot()
: flags(0)
- , previous(NULL)
- , layer(NULL)
+ , previous(nullptr)
+ , layer(nullptr)
, fbo(0)
, invisible(false)
, empty(false)
, alpha(1.0f)
- , roundRectClipState(NULL) {
+ , roundRectClipState(nullptr)
+ , mClipArea(&mClipAreaRoot) {
transform = &mTransformRoot;
- clipRect = &mClipRectRoot;
- region = NULL;
- clipRegion = &mClipRegionRoot;
+ region = nullptr;
}
/**
@@ -55,6 +54,7 @@ Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags)
, empty(false)
, alpha(s->alpha)
, roundRectClipState(s->roundRectClipState)
+ , mClipArea(nullptr)
, mViewportData(s->mViewportData)
, mRelativeLightCenter(s->mRelativeLightCenter) {
if (saveFlags & SkCanvas::kMatrix_SaveFlag) {
@@ -65,22 +65,17 @@ Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags)
}
if (saveFlags & SkCanvas::kClip_SaveFlag) {
- mClipRectRoot.set(*s->clipRect);
- clipRect = &mClipRectRoot;
- if (!s->clipRegion->isEmpty()) {
- mClipRegionRoot.op(*s->clipRegion, SkRegion::kUnion_Op);
- }
- clipRegion = &mClipRegionRoot;
+ mClipAreaRoot = s->getClipArea();
+ mClipArea = &mClipAreaRoot;
} else {
- clipRect = s->clipRect;
- clipRegion = s->clipRegion;
+ mClipArea = s->mClipArea;
}
if (s->flags & Snapshot::kFlagFboTarget) {
flags |= Snapshot::kFlagFboTarget;
region = s->region;
} else {
- region = NULL;
+ region = nullptr;
}
}
@@ -88,88 +83,23 @@ Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags)
// Clipping
///////////////////////////////////////////////////////////////////////////////
-void Snapshot::ensureClipRegion() {
- if (clipRegion->isEmpty()) {
- clipRegion->setRect(clipRect->left, clipRect->top, clipRect->right, clipRect->bottom);
- }
-}
-
-void Snapshot::copyClipRectFromRegion() {
- if (!clipRegion->isEmpty()) {
- const SkIRect& bounds = clipRegion->getBounds();
- clipRect->set(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom);
-
- if (clipRegion->isRect()) {
- clipRegion->setEmpty();
- }
- } else {
- clipRect->setEmpty();
- }
-}
-
-bool Snapshot::clipRegionOp(float left, float top, float right, float bottom, SkRegion::Op op) {
- SkIRect tmp;
- tmp.set(left, top, right, bottom);
- clipRegion->op(tmp, op);
- copyClipRectFromRegion();
- return true;
-}
-
bool Snapshot::clipRegionTransformed(const SkRegion& region, SkRegion::Op op) {
- ensureClipRegion();
- clipRegion->op(region, op);
- copyClipRectFromRegion();
flags |= Snapshot::kFlagClipSet;
- return true;
+ return mClipArea->clipRegion(region, op);
}
bool Snapshot::clip(float left, float top, float right, float bottom, SkRegion::Op op) {
- Rect r(left, top, right, bottom);
- transform->mapRect(r);
- return clipTransformed(r, op);
+ flags |= Snapshot::kFlagClipSet;
+ return mClipArea->clipRectWithTransform(left, top, right, bottom, transform, op);
}
-bool Snapshot::clipTransformed(const Rect& r, SkRegion::Op op) {
- bool clipped = false;
-
- switch (op) {
- case SkRegion::kIntersect_Op: {
- if (CC_UNLIKELY(!clipRegion->isEmpty())) {
- ensureClipRegion();
- clipped = clipRegionOp(r.left, r.top, r.right, r.bottom, SkRegion::kIntersect_Op);
- } else {
- clipped = clipRect->intersect(r);
- if (!clipped) {
- clipRect->setEmpty();
- clipped = true;
- }
- }
- break;
- }
- case SkRegion::kReplace_Op: {
- setClip(r.left, r.top, r.right, r.bottom);
- clipped = true;
- break;
- }
- default: {
- ensureClipRegion();
- clipped = clipRegionOp(r.left, r.top, r.right, r.bottom, op);
- break;
- }
- }
-
- if (clipped) {
- flags |= Snapshot::kFlagClipSet;
- }
-
- return clipped;
+bool Snapshot::clipPath(const SkPath& path, SkRegion::Op op) {
+ flags |= Snapshot::kFlagClipSet;
+ return mClipArea->clipPathWithTransform(path, transform, op);
}
void Snapshot::setClip(float left, float top, float right, float bottom) {
- clipRect->set(left, top, right, bottom);
- if (!clipRegion->isEmpty()) {
- clipRegion->setEmpty();
- }
+ mClipArea->setClip(left, top, right, bottom);
flags |= Snapshot::kFlagClipSet;
}
@@ -181,7 +111,7 @@ const Rect& Snapshot::getLocalClip() {
mat4 inverse;
inverse.loadInverse(*transform);
- mLocalClip.set(*clipRect);
+ mLocalClip.set(mClipArea->getClipRect());
inverse.mapRect(mLocalClip);
return mLocalClip;
@@ -191,8 +121,7 @@ void Snapshot::resetClip(float left, float top, float right, float bottom) {
// TODO: This is incorrect, when we start rendering into a new layer,
// we may have to modify the previous snapshot's clip rect and clip
// region if the previous restore() call did not restore the clip
- clipRect = &mClipRectRoot;
- clipRegion = &mClipRegionRoot;
+ mClipArea = &mClipAreaRoot;
setClip(left, top, right, bottom);
}
@@ -219,7 +148,7 @@ void Snapshot::resetTransform(float x, float y, float z) {
void Snapshot::setClippingRoundRect(LinearAllocator& allocator, const Rect& bounds,
float radius, bool highPriority) {
if (bounds.isEmpty()) {
- clipRect->setEmpty();
+ mClipArea->setEmpty();
return;
}
@@ -272,9 +201,11 @@ bool Snapshot::isIgnored() const {
void Snapshot::dump() const {
ALOGD("Snapshot %p, flags %x, prev %p, height %d, ignored %d, hasComplexClip %d",
- this, flags, previous.get(), getViewportHeight(), isIgnored(), clipRegion && !clipRegion->isEmpty());
- ALOGD(" ClipRect (at %p) %.1f %.1f %.1f %.1f",
- clipRect, clipRect->left, clipRect->top, clipRect->right, clipRect->bottom);
+ this, flags, previous.get(), getViewportHeight(), isIgnored(), !mClipArea->isSimple());
+ const Rect& clipRect(mClipArea->getClipRect());
+ ALOGD(" ClipRect %.1f %.1f %.1f %.1f, clip simple %d",
+ clipRect.left, clipRect.top, clipRect.right, clipRect.bottom, mClipArea->isSimple());
+
ALOGD(" Transform (at %p):", transform);
transform->dump();
}
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index 549de9b..4d704ab 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -26,6 +26,7 @@
#include <SkRegion.h>
+#include "ClipArea.h"
#include "Layer.h"
#include "Matrix.h"
#include "Outline.h"
@@ -129,6 +130,11 @@ public:
bool clipRegionTransformed(const SkRegion& region, SkRegion::Op op);
/**
+ * Modifies the current clip with the specified path and operation.
+ */
+ bool clipPath(const SkPath& path, SkRegion::Op op);
+
+ /**
* Sets the current clip.
*/
void setClip(float left, float top, float right, float bottom);
@@ -142,7 +148,16 @@ public:
/**
* Returns the current clip in render target coordinates.
*/
- const Rect& getRenderTargetClip() { return *clipRect; }
+ const Rect& getRenderTargetClip() { return mClipArea->getClipRect(); }
+
+ /*
+ * Accessor functions so that the clip area can stay private
+ */
+ bool clipIsEmpty() const { return mClipArea->isEmpty(); }
+ const Rect& getClipRect() const { return mClipArea->getClipRect(); }
+ const SkRegion& getClipRegion() const { return mClipArea->getClipRegion(); }
+ bool clipIsSimple() const { return mClipArea->isSimple(); }
+ const ClipArea& getClipArea() const { return *mClipArea; }
/**
* Resets the clip to the specified rect.
@@ -156,6 +171,7 @@ public:
void initializeViewport(int width, int height) {
mViewportData.initialize(width, height);
+ mClipAreaRoot.setViewportDimensions(width, height);
}
int getViewportWidth() const { return mViewportData.mWidth; }
@@ -175,7 +191,7 @@ public:
/**
* Indicates whether this snapshot should be ignored. A snapshot
- * is typicalled ignored if its layer is invisible or empty.
+ * is typically ignored if its layer is invisible or empty.
*/
bool isIgnored() const;
@@ -230,24 +246,6 @@ public:
mat4* transform;
/**
- * Current clip rect. The clip is stored in canvas-space coordinates,
- * (screen-space coordinates in the regular case.)
- *
- * This is a reference to a rect owned by this snapshot or another
- * snapshot. This pointer must not be freed. See ::mClipRectRoot.
- */
- Rect* clipRect;
-
- /**
- * Current clip region. The clip is stored in canvas-space coordinates,
- * (screen-space coordinates in the regular case.)
- *
- * This is a reference to a region owned by this snapshot or another
- * snapshot. This pointer must not be freed. See ::mClipRegionRoot.
- */
- SkRegion* clipRegion;
-
- /**
* The ancestor layer's dirty region.
*
* This is a reference to a region owned by a layer. This pointer must
@@ -260,7 +258,7 @@ public:
* has translucent rendering in a non-overlapping View. This value will be used by
* the renderer to set the alpha in the current color being used for ensuing drawing
* operations. The value is inherited by child snapshots because the same value should
- * be applied to descendents of the current DisplayList (for example, a TextView contains
+ * be applied to descendants of the current DisplayList (for example, a TextView contains
* the base alpha value which should be applied to the child DisplayLists used for drawing
* the actual text).
*/
@@ -298,16 +296,12 @@ private:
mat4 mOrthoMatrix;
};
- void ensureClipRegion();
- void copyClipRectFromRegion();
-
- bool clipRegionOp(float left, float top, float right, float bottom, SkRegion::Op op);
-
mat4 mTransformRoot;
- Rect mClipRectRoot;
- Rect mLocalClip; // don't use directly, call getLocalClip() which initializes this
- SkRegion mClipRegionRoot;
+ ClipArea mClipAreaRoot;
+ ClipArea* mClipArea;
+ Rect mLocalClip;
+
ViewportData mViewportData;
Vector3 mRelativeLightCenter;
diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp
index b2dd899..db3c2d9 100644
--- a/libs/hwui/SpotShadow.cpp
+++ b/libs/hwui/SpotShadow.cpp
@@ -44,6 +44,9 @@
// For each RADIANS_DIVISOR, we would allocate one more vertex b/t the normals.
#define SPOT_CORNER_RADIANS_DIVISOR (M_PI / SPOT_EXTRA_CORNER_VERTEX_PER_PI)
+// For performance, we use (1 - alpha) value for the shader input.
+#define TRANSFORMED_PENUMBRA_ALPHA 1.0f
+#define TRANSFORMED_UMBRA_ALPHA 0.0f
#include <math.h>
#include <stdlib.h>
@@ -52,6 +55,7 @@
#include "ShadowTessellator.h"
#include "SpotShadow.h"
#include "Vertex.h"
+#include "VertexBuffer.h"
#include "utils/MathUtils.h"
// TODO: After we settle down the new algorithm, we can remove the old one and
@@ -331,7 +335,7 @@ bool SpotShadow::testPointInsidePolygon(const Vector2 testPoint,
* @param len the number of points of the polygon
*/
void SpotShadow::makeClockwise(Vector2* polygon, int len) {
- if (polygon == 0 || len == 0) {
+ if (polygon == nullptr || len == 0) {
return;
}
if (!ShadowTessellator::isClockwise(polygon, len)) {
@@ -797,11 +801,15 @@ inline void genNewPenumbraAndPairWithUmbra(const Vector2* penumbra, int penumbra
previousPenumbra * weightForPreviousPenumbra;
int skippedUmbraIndex = (previousClosestUmbraIndex + k + 1) % umbraLength;
- verticesPair[verticesPairIndex++] = {newPenumbraIndex, skippedUmbraIndex};
+ verticesPair[verticesPairIndex].outerIndex = newPenumbraIndex;
+ verticesPair[verticesPairIndex].innerIndex = skippedUmbraIndex;
+ verticesPairIndex++;
newPenumbra[newPenumbraIndex++] = interpolatedPenumbra;
}
}
- verticesPair[verticesPairIndex++] = {newPenumbraIndex, currentClosestUmbraIndex};
+ verticesPair[verticesPairIndex].outerIndex = newPenumbraIndex;
+ verticesPair[verticesPairIndex].innerIndex = currentClosestUmbraIndex;
+ verticesPairIndex++;
newPenumbra[newPenumbraIndex++] = currentPenumbraVertex;
previousClosestUmbraIndex = currentClosestUmbraIndex;
@@ -959,11 +967,11 @@ void SpotShadow::generateTriangleStrip(bool isCasterOpaque, float shadowStrength
// Fill the IB and VB for the penumbra area.
for (int i = 0; i < newPenumbraLength; i++) {
AlphaVertex::set(&shadowVertices[vertexBufferIndex++], newPenumbra[i].x,
- newPenumbra[i].y, 0.0f);
+ newPenumbra[i].y, TRANSFORMED_PENUMBRA_ALPHA);
}
for (int i = 0; i < umbraLength; i++) {
AlphaVertex::set(&shadowVertices[vertexBufferIndex++], umbra[i].x, umbra[i].y,
- M_PI);
+ TRANSFORMED_UMBRA_ALPHA);
}
for (int i = 0; i < verticesPairIndex; i++) {
@@ -1003,14 +1011,14 @@ void SpotShadow::generateTriangleStrip(bool isCasterOpaque, float shadowStrength
indexBuffer[indexBufferIndex++] = newPenumbraLength + i;
indexBuffer[indexBufferIndex++] = vertexBufferIndex;
AlphaVertex::set(&shadowVertices[vertexBufferIndex++],
- closerVertex.x, closerVertex.y, M_PI);
+ closerVertex.x, closerVertex.y, TRANSFORMED_UMBRA_ALPHA);
}
} else {
// If there is no occluded umbra at all, then draw the triangle fan
// starting from the centroid to all umbra vertices.
int lastCentroidIndex = vertexBufferIndex;
AlphaVertex::set(&shadowVertices[vertexBufferIndex++], centroid.x,
- centroid.y, M_PI);
+ centroid.y, TRANSFORMED_UMBRA_ALPHA);
for (int i = 0; i < umbraLength; i++) {
indexBuffer[indexBufferIndex++] = newPenumbraLength + i;
indexBuffer[indexBufferIndex++] = lastCentroidIndex;
@@ -1026,7 +1034,7 @@ void SpotShadow::generateTriangleStrip(bool isCasterOpaque, float shadowStrength
ShadowTessellator::checkOverflow(vertexBufferIndex, totalVertexCount, "Spot Vertex Buffer");
ShadowTessellator::checkOverflow(indexBufferIndex, totalIndexCount, "Spot Index Buffer");
- shadowTriangleStrip.setMode(VertexBuffer::kIndices);
+ shadowTriangleStrip.setMeshFeatureFlags(VertexBuffer::kAlpha | VertexBuffer::kIndices);
shadowTriangleStrip.computeBounds<AlphaVertex>();
}
diff --git a/libs/hwui/SpotShadow.h b/libs/hwui/SpotShadow.h
index e2d94f7..62a7e5d 100644
--- a/libs/hwui/SpotShadow.h
+++ b/libs/hwui/SpotShadow.h
@@ -19,11 +19,12 @@
#include "Debug.h"
#include "Vector.h"
-#include "VertexBuffer.h"
namespace android {
namespace uirenderer {
+class VertexBuffer;
+
class SpotShadow {
public:
static void createSpotShadow(bool isCasterOpaque, const Vector3& lightCenter,
diff --git a/libs/hwui/StatefulBaseRenderer.h b/libs/hwui/StatefulBaseRenderer.h
deleted file mode 100644
index 745e48a..0000000
--- a/libs/hwui/StatefulBaseRenderer.h
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * 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 ANDROID_HWUI_STATEFUL_BASE_RENDERER_H
-#define ANDROID_HWUI_STATEFUL_BASE_RENDERER_H
-
-#include <utils/RefBase.h>
-
-#include "Renderer.h"
-#include "Snapshot.h"
-
-namespace android {
-namespace uirenderer {
-
-/**
- * Abstract Renderer subclass, which implements Canvas state methods.
- *
- * Manages the Snapshot stack, implementing matrix, save/restore, and clipping methods in the
- * Renderer interface. Drawing and recording classes that extend StatefulBaseRenderer will have
- * different use cases:
- *
- * Drawing subclasses (i.e. OpenGLRenderer) can query attributes (such as transform) or hook into
- * changes (e.g. save/restore) with minimal surface area for manipulating the stack itself.
- *
- * Recording subclasses (i.e. DisplayListRenderer) can both record and pass through state operations
- * to StatefulBaseRenderer, so that not only will querying operations work (getClip/Matrix), but so
- * that quickRejection can also be used.
- */
-class StatefulBaseRenderer : public Renderer {
-public:
- StatefulBaseRenderer();
-
- virtual status_t prepare(bool opaque) {
- return prepareDirty(0.0f, 0.0f, mWidth, mHeight, opaque);
- }
-
- /**
- * Initialize the first snapshot, computing the projection matrix, and stores the dimensions of
- * the render target.
- */
- virtual void setViewport(int width, int height);
- void initializeSaveStack(float clipLeft, float clipTop, float clipRight, float clipBottom,
- const Vector3& lightCenter);
-
- // getters
- bool hasRectToRectTransform() const {
- return CC_LIKELY(currentTransform()->rectToRect());
- }
-
- // Save (layer)
- virtual int getSaveCount() const { return mSaveCount; }
- virtual int save(int flags);
- virtual void restore();
- virtual void restoreToCount(int saveCount);
- //virtual int saveLayer(float left, float top, float right, float bottom,
- // int alpha, SkXfermode::Mode mode, int flags);
-
- // Matrix
- virtual void getMatrix(SkMatrix* outMatrix) const;
- virtual void translate(float dx, float dy, float dz = 0.0f);
- virtual void rotate(float degrees);
- virtual void scale(float sx, float sy);
- virtual void skew(float sx, float sy);
-
- virtual void setMatrix(const SkMatrix& matrix);
- void setMatrix(const Matrix4& matrix); // internal only convenience method
- virtual void concatMatrix(const SkMatrix& matrix);
- void concatMatrix(const Matrix4& matrix); // internal only convenience method
-
- // Clip
- virtual const Rect& getLocalClipBounds() const { return mSnapshot->getLocalClip(); }
-
- virtual bool quickRejectConservative(float left, float top, float right, float bottom) const;
-
- virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
- virtual bool clipPath(const SkPath* path, SkRegion::Op op);
- virtual bool clipRegion(const SkRegion* region, SkRegion::Op op);
-
- /**
- * Does not support different clipping Ops (that is, every call to setClippingOutline is
- * effectively using SkRegion::kReplaceOp)
- *
- * The clipping outline is independent from the regular clip.
- */
- void setClippingOutline(LinearAllocator& allocator, const Outline* outline);
- void setClippingRoundRect(LinearAllocator& allocator,
- const Rect& rect, float radius, bool highPriority = true);
-
- inline const mat4* currentTransform() const {
- return mSnapshot->transform;
- }
-
-protected:
- const Rect& getRenderTargetClipBounds() const { return mSnapshot->getRenderTargetClip(); }
-
- int getWidth() { return mWidth; }
- int getHeight() { return mHeight; }
-
- // Save
- int saveSnapshot(int flags);
- void restoreSnapshot();
-
- // allows subclasses to control what value is stored in snapshot's fbo field in
- // initializeSaveStack
- virtual GLuint getTargetFbo() const {
- return -1;
- }
-
- // Clip
- bool calculateQuickRejectForScissor(float left, float top, float right, float bottom,
- bool* clipRequired, bool* roundRectClipRequired, bool snapOut) const;
-
- /**
- * Called just after a restore has occurred. The 'removed' snapshot popped from the stack,
- * 'restored' snapshot has become the top/current.
- *
- * Subclasses can override this method to handle layer restoration
- */
- virtual void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {};
-
- virtual void onViewportInitialized() {};
-
- inline const Rect* currentClipRect() const {
- return mSnapshot->clipRect;
- }
-
- inline const Snapshot* currentSnapshot() const {
- return mSnapshot != NULL ? mSnapshot.get() : mFirstSnapshot.get();
- }
-
- inline const Snapshot* firstSnapshot() const {
- return mFirstSnapshot.get();
- }
-
- // indicites that the clip has been changed since the last time it was consumed
- bool mDirtyClip;
-
-private:
- // Dimensions of the drawing surface
- int mWidth, mHeight;
-
- // Number of saved states
- int mSaveCount;
-
- // Base state
- sp<Snapshot> mFirstSnapshot;
-
-protected:
- // Current state
- // TODO: should become private, once hooks needed by OpenGLRenderer are added
- sp<Snapshot> mSnapshot;
-}; // class StatefulBaseRenderer
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_STATEFUL_BASE_RENDERER_H
diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp
index 1e38f9e..7edb9fb 100644
--- a/libs/hwui/TessellationCache.cpp
+++ b/libs/hwui/TessellationCache.cpp
@@ -76,7 +76,7 @@ void TessellationCache::Description::setupMatrixAndPaint(Matrix4* matrix, SkPain
}
TessellationCache::ShadowDescription::ShadowDescription()
- : nodeKey(NULL) {
+ : nodeKey(nullptr) {
memset(&matrixData, 0, 16 * sizeof(float));
}
@@ -114,7 +114,7 @@ public:
: TaskProcessor<VertexBuffer*>(&caches.tasks) {}
~TessellationProcessor() {}
- virtual void onProcess(const sp<Task<VertexBuffer*> >& task) {
+ virtual void onProcess(const sp<Task<VertexBuffer*> >& task) override {
TessellationTask* t = static_cast<TessellationTask*>(task.get());
ATRACE_NAME("shape tessellation");
VertexBuffer* buffer = t->tessellator(t->description);
@@ -126,7 +126,7 @@ class TessellationCache::Buffer {
public:
Buffer(const sp<Task<VertexBuffer*> >& task)
: mTask(task)
- , mBuffer(NULL) {
+ , mBuffer(nullptr) {
}
~Buffer() {
@@ -146,9 +146,9 @@ public:
private:
void blockOnPrecache() {
- if (mTask != NULL) {
+ if (mTask != nullptr) {
mBuffer = mTask->getResult();
- LOG_ALWAYS_FATAL_IF(mBuffer == NULL, "Failed to precache");
+ LOG_ALWAYS_FATAL_IF(mBuffer == nullptr, "Failed to precache");
mTask.clear();
}
}
@@ -278,7 +278,7 @@ public:
: TaskProcessor<TessellationCache::vertexBuffer_pair_t*>(&caches.tasks) {}
~ShadowProcessor() {}
- virtual void onProcess(const sp<Task<TessellationCache::vertexBuffer_pair_t*> >& task) {
+ virtual void onProcess(const sp<Task<TessellationCache::vertexBuffer_pair_t*> >& task) override {
ShadowTask* t = static_cast<ShadowTask*>(task.get());
ATRACE_NAME("shadow tessellation");
@@ -302,7 +302,7 @@ TessellationCache::TessellationCache()
, mCache(LruCache<Description, Buffer*>::kUnlimitedCapacity)
, mShadowCache(LruCache<ShadowDescription, Task<vertexBuffer_pair_t*>*>::kUnlimitedCapacity) {
char property[PROPERTY_VALUE_MAX];
- if (property_get(PROPERTY_VERTEX_CACHE_SIZE, property, NULL) > 0) {
+ if (property_get(PROPERTY_VERTEX_CACHE_SIZE, property, nullptr) > 0) {
INIT_LOGD(" Setting %s cache size to %sMB", name, property);
setMaxSize(MB(atof(property)));
} else {
@@ -380,14 +380,14 @@ void TessellationCache::precacheShadows(const Matrix4* drawTransform, const Rect
const Vector3& lightCenter, float lightRadius) {
ShadowDescription key(casterPerimeter, drawTransform);
+ if (mShadowCache.get(key)) return;
sp<ShadowTask> task = new ShadowTask(drawTransform, localClip, opaque,
casterPerimeter, transformXY, transformZ, lightCenter, lightRadius);
- if (mShadowProcessor == NULL) {
+ if (mShadowProcessor == nullptr) {
mShadowProcessor = new ShadowProcessor(Caches::getInstance());
}
mShadowProcessor->add(task);
-
- task->incStrong(NULL); // not using sp<>s, so manually ref while in the cache
+ task->incStrong(nullptr); // not using sp<>s, so manually ref while in the cache
mShadowCache.put(key, task.get());
}
@@ -402,7 +402,7 @@ void TessellationCache::getShadowBuffers(const Matrix4* drawTransform, const Rec
transformXY, transformZ, lightCenter, lightRadius);
task = static_cast<ShadowTask*>(mShadowCache.get(key));
}
- LOG_ALWAYS_FATAL_IF(task == NULL, "shadow not precached");
+ LOG_ALWAYS_FATAL_IF(task == nullptr, "shadow not precached");
outBuffers = *(task->getResult());
}
@@ -418,7 +418,7 @@ TessellationCache::Buffer* TessellationCache::getOrCreateBuffer(
sp<TessellationTask> task = new TessellationTask(tessellator, entry);
buffer = new Buffer(task);
- if (mProcessor == NULL) {
+ if (mProcessor == nullptr) {
mProcessor = new TessellationProcessor(Caches::getInstance());
}
mProcessor->add(task);
diff --git a/libs/hwui/TessellationCache.h b/libs/hwui/TessellationCache.h
index 688a699..3efeaf6 100644
--- a/libs/hwui/TessellationCache.h
+++ b/libs/hwui/TessellationCache.h
@@ -24,7 +24,6 @@
#include "Debug.h"
#include "utils/Macros.h"
#include "utils/Pair.h"
-#include "VertexBuffer.h"
class SkBitmap;
class SkCanvas;
@@ -36,6 +35,7 @@ namespace android {
namespace uirenderer {
class Caches;
+class VertexBuffer;
///////////////////////////////////////////////////////////////////////////////
// Classes
@@ -166,7 +166,7 @@ private:
sp<TaskProcessor<VertexBuffer*> > mProcessor;
LruCache<Description, Buffer*> mCache;
class BufferRemovedListener : public OnEntryRemoved<Description, Buffer*> {
- void operator()(Description& description, Buffer*& buffer);
+ void operator()(Description& description, Buffer*& buffer) override;
};
BufferRemovedListener mBufferRemovedListener;
@@ -178,8 +178,8 @@ private:
// holds a pointer, and implicit strong ref to each shadow task of the frame
LruCache<ShadowDescription, Task<vertexBuffer_pair_t*>*> mShadowCache;
class BufferPairRemovedListener : public OnEntryRemoved<ShadowDescription, Task<vertexBuffer_pair_t*>*> {
- void operator()(ShadowDescription& description, Task<vertexBuffer_pair_t*>*& bufferPairTask) {
- bufferPairTask->decStrong(NULL);
+ void operator()(ShadowDescription& description, Task<vertexBuffer_pair_t*>*& bufferPairTask) override {
+ bufferPairTask->decStrong(nullptr);
}
};
BufferPairRemovedListener mBufferPairRemovedListener;
diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp
index 4eec462..c2e88f3 100644
--- a/libs/hwui/TextDropShadowCache.cpp
+++ b/libs/hwui/TextDropShadowCache.cpp
@@ -20,6 +20,7 @@
#include "Caches.h"
#include "Debug.h"
+#include "FontRenderer.h"
#include "TextDropShadowCache.h"
#include "Properties.h"
@@ -40,7 +41,8 @@ hash_t ShadowText::hash() const {
hash = JenkinsHashMix(hash, android::hash_type(italicStyle));
hash = JenkinsHashMix(hash, android::hash_type(scaleX));
if (text) {
- hash = JenkinsHashMixShorts(hash, text, charCount);
+ hash = JenkinsHashMixShorts(
+ hash, reinterpret_cast<const uint16_t*>(text), charCount);
}
if (positions) {
for (uint32_t i = 0; i < charCount * 2; i++) {
@@ -98,7 +100,7 @@ TextDropShadowCache::TextDropShadowCache():
mCache(LruCache<ShadowText, ShadowTexture*>::kUnlimitedCapacity),
mSize(0), mMaxSize(MB(DEFAULT_DROP_SHADOW_CACHE_SIZE)) {
char property[PROPERTY_VALUE_MAX];
- if (property_get(PROPERTY_DROP_SHADOW_CACHE_SIZE, property, NULL) > 0) {
+ if (property_get(PROPERTY_DROP_SHADOW_CACHE_SIZE, property, nullptr) > 0) {
INIT_LOGD(" Setting drop shadow cache size to %sMB", property);
setMaxSize(MB(atof(property)));
} else {
@@ -180,7 +182,7 @@ ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const char* text,
len, numGlyphs, radius, positions);
if (!shadow.image) {
- return NULL;
+ return nullptr;
}
Caches& caches = Caches::getInstance();
@@ -205,7 +207,7 @@ ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const char* text,
glGenTextures(1, &texture->id);
- caches.bindTexture(texture->id);
+ caches.textureState().bindTexture(texture->id);
// Textures are Alpha8
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
diff --git a/libs/hwui/TextDropShadowCache.h b/libs/hwui/TextDropShadowCache.h
index 54b930b..caf089f 100644
--- a/libs/hwui/TextDropShadowCache.h
+++ b/libs/hwui/TextDropShadowCache.h
@@ -24,17 +24,18 @@
#include <utils/LruCache.h>
#include <utils/String16.h>
-#include "FontRenderer.h"
+#include "font/Font.h"
#include "Texture.h"
namespace android {
namespace uirenderer {
class Caches;
+class FontRenderer;
struct ShadowText {
- ShadowText(): len(0), radius(0.0f), textSize(0.0f), typeface(NULL),
- flags(0), italicStyle(0.0f), scaleX(0), text(NULL), positions(NULL) {
+ ShadowText(): len(0), radius(0.0f), textSize(0.0f), typeface(nullptr),
+ flags(0), italicStyle(0.0f), scaleX(0), text(nullptr), positions(nullptr) {
}
// len is the number of bytes in text
@@ -75,7 +76,7 @@ struct ShadowText {
uint32_t charCount = len / sizeof(char16_t);
str.setTo((const char16_t*) text, charCount);
text = str.string();
- if (positions != NULL) {
+ if (positions != nullptr) {
positionsCopy.clear();
positionsCopy.appendArray(positions, charCount * 2);
positions = positionsCopy.array();
@@ -133,7 +134,7 @@ public:
* Used as a callback when an entry is removed from the cache.
* Do not invoke directly.
*/
- void operator()(ShadowText& text, ShadowTexture*& texture);
+ void operator()(ShadowText& text, ShadowTexture*& texture) override;
ShadowTexture* get(const SkPaint* paint, const char* text, uint32_t len,
int numGlyphs, float radius, const float* positions);
diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp
index e783905..593e918 100644
--- a/libs/hwui/Texture.cpp
+++ b/libs/hwui/Texture.cpp
@@ -24,20 +24,6 @@
namespace android {
namespace uirenderer {
-Texture::Texture(): id(0), generation(0), blend(false), width(0), height(0),
- cleanup(false), bitmapSize(0), mipMap(false), uvMapper(NULL), isInUse(false),
- mWrapS(GL_CLAMP_TO_EDGE), mWrapT(GL_CLAMP_TO_EDGE),
- mMinFilter(GL_NEAREST), mMagFilter(GL_NEAREST),
- mFirstFilter(true), mFirstWrap(true), mCaches(Caches::getInstance()) {
-}
-
-Texture::Texture(Caches& caches): id(0), generation(0), blend(false), width(0), height(0),
- cleanup(false), bitmapSize(0), mipMap(false), uvMapper(NULL), isInUse(false),
- mWrapS(GL_CLAMP_TO_EDGE), mWrapT(GL_CLAMP_TO_EDGE),
- mMinFilter(GL_NEAREST), mMagFilter(GL_NEAREST),
- mFirstFilter(true), mFirstWrap(true), mCaches(caches) {
-}
-
void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force,
GLenum renderTarget) {
@@ -48,7 +34,7 @@ void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force
mWrapT = wrapT;
if (bindTexture) {
- mCaches.bindTexture(renderTarget, id);
+ mCaches.textureState().bindTexture(renderTarget, id);
}
glTexParameteri(renderTarget, GL_TEXTURE_WRAP_S, wrapS);
@@ -66,7 +52,7 @@ void Texture::setFilterMinMag(GLenum min, GLenum mag, bool bindTexture, bool for
mMagFilter = mag;
if (bindTexture) {
- mCaches.bindTexture(renderTarget, id);
+ mCaches.textureState().bindTexture(renderTarget, id);
}
if (mipMap && min == GL_LINEAR) min = GL_LINEAR_MIPMAP_LINEAR;
@@ -77,7 +63,7 @@ void Texture::setFilterMinMag(GLenum min, GLenum mag, bool bindTexture, bool for
}
void Texture::deleteTexture() const {
- mCaches.deleteTexture(id);
+ mCaches.textureState().deleteTexture(id);
}
}; // namespace uirenderer
diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h
index d5601f8..7227ce0 100644
--- a/libs/hwui/Texture.h
+++ b/libs/hwui/Texture.h
@@ -30,8 +30,7 @@ class UvMapper;
*/
class Texture {
public:
- Texture();
- Texture(Caches& caches);
+ Texture(Caches& caches) : mCaches(caches) { }
virtual ~Texture() { }
@@ -59,62 +58,62 @@ public:
/**
* Name of the texture.
*/
- GLuint id;
+ GLuint id = 0;
/**
* Generation of the backing bitmap,
*/
- uint32_t generation;
+ uint32_t generation = 0;
/**
* Indicates whether the texture requires blending.
*/
- bool blend;
+ bool blend = false;
/**
* Width of the backing bitmap.
*/
- uint32_t width;
+ uint32_t width = 0;
/**
* Height of the backing bitmap.
*/
- uint32_t height;
+ uint32_t height = 0;
/**
* Indicates whether this texture should be cleaned up after use.
*/
- bool cleanup;
+ bool cleanup = false;
/**
* Optional, size of the original bitmap.
*/
- uint32_t bitmapSize;
+ uint32_t bitmapSize = 0;
/**
* Indicates whether this texture will use trilinear filtering.
*/
- bool mipMap;
+ bool mipMap = false;
/**
* Optional, pointer to a texture coordinates mapper.
*/
- const UvMapper* uvMapper;
+ const UvMapper* uvMapper = nullptr;
/**
* Whether or not the Texture is marked in use and thus not evictable for
* the current frame. This is reset at the start of a new frame.
*/
- bool isInUse;
+ bool isInUse = false;
private:
/**
- * Last wrap modes set on this texture. Defaults to GL_CLAMP_TO_EDGE.
+ * Last wrap modes set on this texture.
*/
- GLenum mWrapS;
- GLenum mWrapT;
+ GLenum mWrapS = GL_CLAMP_TO_EDGE;
+ GLenum mWrapT = GL_CLAMP_TO_EDGE;
/**
- * Last filters set on this texture. Defaults to GL_NEAREST.
+ * Last filters set on this texture.
*/
- GLenum mMinFilter;
- GLenum mMagFilter;
+ GLenum mMinFilter = GL_NEAREST;
+ GLenum mMagFilter = GL_NEAREST;
- bool mFirstFilter;
- bool mFirstWrap;
+ bool mFirstFilter = true;
+ bool mFirstWrap = true;
Caches& mCaches;
}; // struct Texture
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 63454d8..b911a0f 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -26,6 +26,7 @@
#include "AssetAtlas.h"
#include "Caches.h"
+#include "Texture.h"
#include "TextureCache.h"
#include "Properties.h"
#include "utils/TraceUtils.h"
@@ -37,19 +38,21 @@ namespace uirenderer {
// Constructors/destructor
///////////////////////////////////////////////////////////////////////////////
-TextureCache::TextureCache():
- mCache(LruCache<uint32_t, Texture*>::kUnlimitedCapacity),
- mSize(0), mMaxSize(MB(DEFAULT_TEXTURE_CACHE_SIZE)),
- mFlushRate(DEFAULT_TEXTURE_CACHE_FLUSH_RATE), mAssetAtlas(0) {
+TextureCache::TextureCache()
+ : mCache(LruCache<uint32_t, Texture*>::kUnlimitedCapacity)
+ , mSize(0)
+ , mMaxSize(MB(DEFAULT_TEXTURE_CACHE_SIZE))
+ , mFlushRate(DEFAULT_TEXTURE_CACHE_FLUSH_RATE)
+ , mAssetAtlas(nullptr) {
char property[PROPERTY_VALUE_MAX];
- if (property_get(PROPERTY_TEXTURE_CACHE_SIZE, property, NULL) > 0) {
+ if (property_get(PROPERTY_TEXTURE_CACHE_SIZE, property, nullptr) > 0) {
INIT_LOGD(" Setting texture cache size to %sMB", property);
setMaxSize(MB(atof(property)));
} else {
INIT_LOGD(" Using default texture cache size of %.2fMB", DEFAULT_TEXTURE_CACHE_SIZE);
}
- if (property_get(PROPERTY_TEXTURE_CACHE_FLUSH_RATE, property, NULL) > 0) {
+ if (property_get(PROPERTY_TEXTURE_CACHE_FLUSH_RATE, property, nullptr) > 0) {
float flushRate = atof(property);
INIT_LOGD(" Setting texture cache flush rate to %.2f%%", flushRate * 100.0f);
setFlushRate(flushRate);
@@ -58,20 +61,6 @@ TextureCache::TextureCache():
DEFAULT_TEXTURE_CACHE_FLUSH_RATE * 100.0f);
}
- init();
-}
-
-TextureCache::TextureCache(uint32_t maxByteSize):
- mCache(LruCache<uint32_t, Texture*>::kUnlimitedCapacity),
- mSize(0), mMaxSize(maxByteSize), mAssetAtlas(0) {
- init();
-}
-
-TextureCache::~TextureCache() {
- mCache.clear();
-}
-
-void TextureCache::init() {
mCache.setOnEntryRemovedListener(this);
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
@@ -80,6 +69,10 @@ void TextureCache::init() {
mDebugEnabled = readDebugLevel() & kDebugCaches;
}
+TextureCache::~TextureCache() {
+ mCache.clear();
+}
+
///////////////////////////////////////////////////////////////////////////////
// Size management
///////////////////////////////////////////////////////////////////////////////
@@ -159,7 +152,7 @@ Texture* TextureCache::getCachedTexture(const SkBitmap* bitmap) {
if (!texture) {
if (!canMakeTextureFromBitmap(bitmap)) {
- return NULL;
+ return nullptr;
}
const uint32_t size = bitmap->rowBytes() * bitmap->height();
@@ -175,7 +168,7 @@ Texture* TextureCache::getCachedTexture(const SkBitmap* bitmap) {
}
if (canCache) {
- texture = new Texture();
+ texture = new Texture(Caches::getInstance());
texture->bitmapSize = size;
generateTexture(bitmap, texture, false);
@@ -209,11 +202,11 @@ Texture* TextureCache::get(const SkBitmap* bitmap) {
if (!texture) {
if (!canMakeTextureFromBitmap(bitmap)) {
- return NULL;
+ return nullptr;
}
const uint32_t size = bitmap->rowBytes() * bitmap->height();
- texture = new Texture();
+ texture = new Texture(Caches::getInstance());
texture->bitmapSize = size;
generateTexture(bitmap, texture, false);
texture->cleanup = true;
@@ -223,7 +216,7 @@ Texture* TextureCache::get(const SkBitmap* bitmap) {
}
Texture* TextureCache::getTransient(const SkBitmap* bitmap) {
- Texture* texture = new Texture();
+ Texture* texture = new Texture(Caches::getInstance());
texture->bitmapSize = bitmap->rowBytes() * bitmap->height();
texture->cleanup = true;
@@ -232,11 +225,9 @@ Texture* TextureCache::getTransient(const SkBitmap* bitmap) {
return texture;
}
-void TextureCache::releaseTexture(const SkBitmap* bitmap) {
- if (!bitmap || !bitmap->pixelRef()) return;
-
+void TextureCache::releaseTexture(uint32_t pixelRefStableID) {
Mutex::Autolock _l(mLock);
- mGarbage.push(bitmap->pixelRef()->getStableID());
+ mGarbage.push(pixelRefStableID);
}
void TextureCache::clearGarbage() {
@@ -281,7 +272,7 @@ void TextureCache::generateTexture(const SkBitmap* bitmap, Texture* texture, boo
// We could also enable mipmapping if both bitmap dimensions are powers
// of 2 but we'd have to deal with size changes. Let's keep this simple
- const bool canMipMap = Extensions::getInstance().hasNPot();
+ const bool canMipMap = Caches::getInstance().extensions().hasNPot();
// If the texture had mipmap enabled but not anymore,
// force a glTexImage2D to discard the mipmap levels
@@ -297,23 +288,20 @@ void TextureCache::generateTexture(const SkBitmap* bitmap, Texture* texture, boo
texture->width = bitmap->width();
texture->height = bitmap->height();
- Caches::getInstance().bindTexture(texture->id);
+ Caches::getInstance().textureState().bindTexture(texture->id);
switch (bitmap->colorType()) {
case kAlpha_8_SkColorType:
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(),
texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels());
texture->blend = true;
break;
case kRGB_565_SkColorType:
- glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(),
texture->width, texture->height, GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels());
texture->blend = false;
break;
case kN32_SkColorType:
- glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(),
texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels());
// Do this after calling getPixels() to make sure Skia's deferred
@@ -322,7 +310,6 @@ void TextureCache::generateTexture(const SkBitmap* bitmap, Texture* texture, boo
break;
case kARGB_4444_SkColorType:
case kIndex_8_SkColorType:
- glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
uploadLoFiTexture(resize, bitmap, texture->width, texture->height);
texture->blend = !bitmap->isOpaque();
break;
@@ -351,7 +338,7 @@ void TextureCache::uploadLoFiTexture(bool resize, const SkBitmap* bitmap,
rgbaBitmap.eraseColor(0);
SkCanvas canvas(rgbaBitmap);
- canvas.drawBitmap(*bitmap, 0.0f, 0.0f, NULL);
+ canvas.drawBitmap(*bitmap, 0.0f, 0.0f, nullptr);
uploadToTexture(resize, GL_RGBA, rgbaBitmap.rowBytesAsPixels(), rgbaBitmap.bytesPerPixel(),
width, height, GL_UNSIGNED_BYTE, rgbaBitmap.getPixels());
@@ -359,7 +346,9 @@ void TextureCache::uploadLoFiTexture(bool resize, const SkBitmap* bitmap,
void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei stride, GLsizei bpp,
GLsizei width, GLsizei height, GLenum type, const GLvoid * data) {
- const bool useStride = stride != width && Extensions::getInstance().hasUnpackRowLength();
+ glPixelStorei(GL_UNPACK_ALIGNMENT, bpp);
+ const bool useStride = stride != width
+ && Caches::getInstance().extensions().hasUnpackRowLength();
if ((stride == width) || useStride) {
if (useStride) {
glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
index cf8d134..a2c6380 100644
--- a/libs/hwui/TextureCache.h
+++ b/libs/hwui/TextureCache.h
@@ -24,11 +24,12 @@
#include <utils/Vector.h>
#include "Debug.h"
-#include "Texture.h"
namespace android {
namespace uirenderer {
+class Texture;
+
///////////////////////////////////////////////////////////////////////////////
// Defines
///////////////////////////////////////////////////////////////////////////////
@@ -51,17 +52,16 @@ class AssetAtlas;
* Any texture added to the cache causing the cache to grow beyond the maximum
* allowed size will also cause the oldest texture to be kicked out.
*/
-class TextureCache: public OnEntryRemoved<uint32_t, Texture*> {
+class TextureCache : public OnEntryRemoved<uint32_t, Texture*> {
public:
TextureCache();
- TextureCache(uint32_t maxByteSize);
~TextureCache();
/**
* Used as a callback when an entry is removed from the cache.
* Do not invoke directly.
*/
- void operator()(uint32_t&, Texture*& texture);
+ void operator()(uint32_t&, Texture*& texture) override;
/**
* Resets all Textures to not be marked as in use
@@ -87,10 +87,10 @@ public:
Texture* getTransient(const SkBitmap* bitmap);
/**
- * Removes the texture associated with the specified bitmap. This is meant
+ * Removes the texture associated with the specified pixelRef. This is meant
* to be called from threads that are not the EGL context thread.
*/
- void releaseTexture(const SkBitmap* bitmap);
+ ANDROID_API void releaseTexture(uint32_t pixelRefStableID);
/**
* Process deferred removals.
*/
@@ -145,8 +145,6 @@ private:
void uploadToTexture(bool resize, GLenum format, GLsizei stride, GLsizei bpp,
GLsizei width, GLsizei height, GLenum type, const GLvoid * data);
- void init();
-
LruCache<uint32_t, Texture*> mCache;
uint32_t mSize;
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index ae6ea94..0799c6c 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -20,7 +20,6 @@
#include <utils/Timers.h>
-#include "DamageAccumulator.h"
#include "utils/Macros.h"
namespace android {
@@ -30,6 +29,7 @@ namespace renderthread {
class CanvasContext;
}
+class DamageAccumulator;
class OpenGLRenderer;
class RenderState;
@@ -59,11 +59,11 @@ public:
: mode(mode)
, prepareTextures(mode == MODE_FULL)
, runAnimations(true)
- , damageAccumulator(NULL)
+ , damageAccumulator(nullptr)
, renderState(renderState)
- , renderer(NULL)
- , errorHandler(NULL)
- , canvasContext(NULL)
+ , renderer(nullptr)
+ , errorHandler(nullptr)
+ , canvasContext(nullptr)
{}
explicit TreeInfo(TraversalMode mode, const TreeInfo& clone)
diff --git a/libs/hwui/Vector.h b/libs/hwui/Vector.h
index aa6acc9..7c3f2fd 100644
--- a/libs/hwui/Vector.h
+++ b/libs/hwui/Vector.h
@@ -17,6 +17,9 @@
#ifndef ANDROID_HWUI_VECTOR_H
#define ANDROID_HWUI_VECTOR_H
+#include <math.h>
+#include <utils/Log.h>
+
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h
index 4ff0b18..11d0c4b 100644
--- a/libs/hwui/Vertex.h
+++ b/libs/hwui/Vertex.h
@@ -19,6 +19,8 @@
#include "Vector.h"
+#include "utils/Macros.h"
+
namespace android {
namespace uirenderer {
@@ -39,8 +41,8 @@ struct Vertex {
float x, y;
static inline void set(Vertex* vertex, float x, float y) {
- vertex[0].x = x;
- vertex[0].y = y;
+ vertex->x = x;
+ vertex->y = y;
}
static inline void set(Vertex* vertex, Vector2 val) {
@@ -53,6 +55,8 @@ struct Vertex {
}; // struct Vertex
+REQUIRE_COMPATIBLE_LAYOUT(Vertex);
+
/**
* Simple structure to describe a vertex with a position and texture UV.
*/
@@ -61,10 +65,7 @@ struct TextureVertex {
float u, v;
static inline void set(TextureVertex* vertex, float x, float y, float u, float v) {
- vertex[0].x = x;
- vertex[0].y = y;
- vertex[0].u = u;
- vertex[0].v = v;
+ *vertex = { x, y, u, v };
}
static inline void setUV(TextureVertex* vertex, float u, float v) {
@@ -73,39 +74,43 @@ struct TextureVertex {
}
}; // struct TextureVertex
+REQUIRE_COMPATIBLE_LAYOUT(TextureVertex);
+
/**
* Simple structure to describe a vertex with a position, texture UV and ARGB color.
*/
-struct ColorTextureVertex : TextureVertex {
+struct ColorTextureVertex {
+ float x, y;
+ float u, v;
float r, g, b, a;
static inline void set(ColorTextureVertex* vertex, float x, float y,
float u, float v, int color) {
- TextureVertex::set(vertex, x, y, u, v);
- const float a = ((color >> 24) & 0xff) / 255.0f;
- vertex[0].r = a * ((color >> 16) & 0xff) / 255.0f;
- vertex[0].g = a * ((color >> 8) & 0xff) / 255.0f;
- vertex[0].b = a * ((color ) & 0xff) / 255.0f;
- vertex[0].a = a;
+ float a = ((color >> 24) & 0xff) / 255.0f;
+ float r = a * ((color >> 16) & 0xff) / 255.0f;
+ float g = a * ((color >> 8) & 0xff) / 255.0f;
+ float b = a * ((color) & 0xff) / 255.0f;
+ *vertex = { x, y, u, v, r, g, b, a };
}
}; // struct ColorTextureVertex
+REQUIRE_COMPATIBLE_LAYOUT(ColorTextureVertex);
+
/**
* Simple structure to describe a vertex with a position and an alpha value.
*/
-struct AlphaVertex : Vertex {
+struct AlphaVertex {
+ float x, y;
float alpha;
static inline void set(AlphaVertex* vertex, float x, float y, float alpha) {
- Vertex::set(vertex, x, y);
- vertex[0].alpha = alpha;
+ *vertex = { x, y, alpha };
}
static inline void copyWithOffset(AlphaVertex* vertex, const AlphaVertex& src,
float x, float y) {
- Vertex::set(vertex, src.x + x, src.y + y);
- vertex[0].alpha = src.alpha;
+ AlphaVertex::set(vertex, src.x + x, src.y + y, src.alpha);
}
static inline void setColor(AlphaVertex* vertex, float alpha) {
@@ -113,6 +118,8 @@ struct AlphaVertex : Vertex {
}
}; // struct AlphaVertex
+REQUIRE_COMPATIBLE_LAYOUT(AlphaVertex);
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/VertexBuffer.h b/libs/hwui/VertexBuffer.h
index 8c3a272..9be4d84 100644
--- a/libs/hwui/VertexBuffer.h
+++ b/libs/hwui/VertexBuffer.h
@@ -24,25 +24,24 @@ namespace uirenderer {
class VertexBuffer {
public:
- enum Mode {
- kStandard = 0,
- kOnePolyRingShadow = 1,
- kTwoPolyRingShadow = 2,
- kIndices = 3
+ enum MeshFeatureFlags {
+ kNone = 0,
+ kAlpha = 1 << 0,
+ kIndices = 1 << 1,
};
VertexBuffer()
- : mBuffer(0)
- , mIndices(0)
+ : mBuffer(nullptr)
+ , mIndices(nullptr)
, mVertexCount(0)
, mIndexCount(0)
, mAllocatedVertexCount(0)
, mAllocatedIndexCount(0)
, mByteCount(0)
- , mMode(kStandard)
- , mReallocBuffer(0)
- , mCleanupMethod(NULL)
- , mCleanupIndexMethod(NULL)
+ , mMeshFeatureFlags(kNone)
+ , mReallocBuffer(nullptr)
+ , mCleanupMethod(nullptr)
+ , mCleanupIndexMethod(nullptr)
{}
~VertexBuffer() {
@@ -135,10 +134,12 @@ public:
void updateVertexCount(unsigned int newCount) {
mVertexCount = MathUtils::min(newCount, mAllocatedVertexCount);
}
- Mode getMode() const { return mMode; }
+ MeshFeatureFlags getMeshFeatureFlags() const { return mMeshFeatureFlags; }
+ void setMeshFeatureFlags(int flags) {
+ mMeshFeatureFlags = static_cast<MeshFeatureFlags>(flags);
+ }
void setBounds(Rect bounds) { mBounds = bounds; }
- void setMode(Mode mode) { mMode = mode; }
template <class TYPE>
void createDegenerateSeparators(int allocSize) {
@@ -166,7 +167,7 @@ private:
unsigned int mAllocatedIndexCount;
unsigned int mByteCount;
- Mode mMode;
+ MeshFeatureFlags mMeshFeatureFlags;
void* mReallocBuffer; // used for multi-allocation
diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp
index 24ffb80..845cf30 100644
--- a/libs/hwui/font/CacheTexture.cpp
+++ b/libs/hwui/font/CacheTexture.cpp
@@ -17,6 +17,7 @@
#include <SkGlyph.h>
#include "CacheTexture.h"
+#include "FontUtil.h"
#include "../Caches.h"
#include "../Debug.h"
#include "../Extensions.h"
@@ -42,7 +43,7 @@ CacheBlock* CacheBlock::insertBlock(CacheBlock* head, CacheBlock* newBlock) {
#endif
CacheBlock* currBlock = head;
- CacheBlock* prevBlock = NULL;
+ CacheBlock* prevBlock = nullptr;
while (currBlock && currBlock->mY != TEXTURE_BORDER_SIZE) {
if (newBlock->mWidth < currBlock->mWidth) {
@@ -108,29 +109,33 @@ CacheBlock* CacheBlock::removeBlock(CacheBlock* head, CacheBlock* blockToRemove)
// CacheTexture
///////////////////////////////////////////////////////////////////////////////
-CacheTexture::CacheTexture(uint16_t width, uint16_t height, GLenum format, uint32_t maxQuadCount) :
- mTexture(NULL), mTextureId(0), mWidth(width), mHeight(height), mFormat(format),
- mLinearFiltering(false), mDirty(false), mNumGlyphs(0),
- mMesh(NULL), mCurrentQuad(0), mMaxQuadCount(maxQuadCount),
- mCaches(Caches::getInstance()) {
+CacheTexture::CacheTexture(uint16_t width, uint16_t height, GLenum format, uint32_t maxQuadCount)
+ : mTexture(Caches::getInstance())
+ , mFormat(format)
+ , mMaxQuadCount(maxQuadCount)
+ , mCaches(Caches::getInstance()) {
+ mTexture.width = width;
+ mTexture.height = height;
+ mTexture.blend = true;
+
mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
- mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE);
+ getWidth() - TEXTURE_BORDER_SIZE, getHeight() - TEXTURE_BORDER_SIZE);
// OpenGL ES 3.0+ lets us specify the row length for unpack operations such
// as glTexSubImage2D(). This allows us to upload a sub-rectangle of a texture.
// With OpenGL ES 2.0 we have to upload entire stripes instead.
- mHasUnpackRowLength = Extensions::getInstance().hasUnpackRowLength();
+ mHasUnpackRowLength = mCaches.extensions().hasUnpackRowLength();
}
CacheTexture::~CacheTexture() {
releaseMesh();
- releaseTexture();
+ releasePixelBuffer();
reset();
}
void CacheTexture::reset() {
// Delete existing cache blocks
- while (mCacheBlocks != NULL) {
+ while (mCacheBlocks != nullptr) {
CacheBlock* tmpBlock = mCacheBlocks;
mCacheBlocks = mCacheBlocks->mNext;
delete tmpBlock;
@@ -143,35 +148,28 @@ void CacheTexture::init() {
// reset, then create a new remainder space to start again
reset();
mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
- mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE);
+ getWidth() - TEXTURE_BORDER_SIZE, getHeight() - TEXTURE_BORDER_SIZE);
}
void CacheTexture::releaseMesh() {
delete[] mMesh;
}
-void CacheTexture::releaseTexture() {
- if (mTexture) {
- delete mTexture;
- mTexture = NULL;
+void CacheTexture::releasePixelBuffer() {
+ if (mPixelBuffer) {
+ delete mPixelBuffer;
+ mPixelBuffer = nullptr;
}
- if (mTextureId) {
- mCaches.deleteTexture(mTextureId);
- mTextureId = 0;
+ if (mTexture.id) {
+ mCaches.textureState().deleteTexture(mTexture.id);
+ mTexture.id = 0;
}
mDirty = false;
mCurrentQuad = 0;
}
-void CacheTexture::setLinearFiltering(bool linearFiltering, bool bind) {
- if (linearFiltering != mLinearFiltering) {
- mLinearFiltering = linearFiltering;
-
- const GLenum filtering = linearFiltering ? GL_LINEAR : GL_NEAREST;
- if (bind) mCaches.bindTexture(getTextureId());
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
- }
+void CacheTexture::setLinearFiltering(bool linearFiltering) {
+ mTexture.setFilter(linearFiltering ? GL_LINEAR : GL_NEAREST);
}
void CacheTexture::allocateMesh() {
@@ -180,19 +178,19 @@ void CacheTexture::allocateMesh() {
}
}
-void CacheTexture::allocateTexture() {
- if (!mTexture) {
- mTexture = PixelBuffer::create(mFormat, mWidth, mHeight);
+void CacheTexture::allocatePixelBuffer() {
+ if (!mPixelBuffer) {
+ mPixelBuffer = PixelBuffer::create(mFormat, getWidth(), getHeight());
}
- if (!mTextureId) {
- glGenTextures(1, &mTextureId);
+ if (!mTexture.id) {
+ glGenTextures(1, &mTexture.id);
- mCaches.bindTexture(mTextureId);
+ mCaches.textureState().bindTexture(mTexture.id);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// Initialize texture dimensions
- glTexImage2D(GL_TEXTURE_2D, 0, mFormat, mWidth, mHeight, 0,
- mFormat, GL_UNSIGNED_BYTE, 0);
+ glTexImage2D(GL_TEXTURE_2D, 0, mFormat, getWidth(), getHeight(), 0,
+ mFormat, GL_UNSIGNED_BYTE, nullptr);
const GLenum filtering = getLinearFiltering() ? GL_LINEAR : GL_NEAREST;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
@@ -208,16 +206,16 @@ bool CacheTexture::upload() {
uint32_t x = mHasUnpackRowLength ? dirtyRect.left : 0;
uint32_t y = dirtyRect.top;
- uint32_t width = mHasUnpackRowLength ? dirtyRect.getWidth() : mWidth;
+ uint32_t width = mHasUnpackRowLength ? dirtyRect.getWidth() : getWidth();
uint32_t height = dirtyRect.getHeight();
// The unpack row length only needs to be specified when a new
// texture is bound
if (mHasUnpackRowLength) {
- glPixelStorei(GL_UNPACK_ROW_LENGTH, mWidth);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, getWidth());
}
- mTexture->upload(x, y, width, height);
+ mPixelBuffer->upload(x, y, width, height);
setDirty(false);
return mHasUnpackRowLength;
@@ -257,7 +255,7 @@ bool CacheTexture::fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_
return false;
}
- if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 > mHeight) {
+ if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 > getHeight()) {
return false;
}
@@ -294,10 +292,10 @@ bool CacheTexture::fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_
cacheBlock->mWidth -= roundedUpW;
cacheBlock->mX += roundedUpW;
- if (mHeight - glyphH >= glyphH) {
+ if (getHeight() - glyphH >= glyphH) {
// There's enough height left over to create a new CacheBlock
CacheBlock* newBlock = new CacheBlock(oldX, glyphH + TEXTURE_BORDER_SIZE,
- roundedUpW, mHeight - glyphH - TEXTURE_BORDER_SIZE);
+ roundedUpW, getHeight() - glyphH - TEXTURE_BORDER_SIZE);
#if DEBUG_FONT_RENDERER
ALOGD("fitBitmap: Created new block: this, x, y, w, h = %p, %d, %d, %d, %d",
newBlock, newBlock->mX, newBlock->mY,
diff --git a/libs/hwui/font/CacheTexture.h b/libs/hwui/font/CacheTexture.h
index 4cc4f22..6dabc76 100644
--- a/libs/hwui/font/CacheTexture.h
+++ b/libs/hwui/font/CacheTexture.h
@@ -17,16 +17,15 @@
#ifndef ANDROID_HWUI_CACHE_TEXTURE_H
#define ANDROID_HWUI_CACHE_TEXTURE_H
-#include <GLES3/gl3.h>
+#include "PixelBuffer.h"
+#include "Rect.h"
+#include "Texture.h"
+#include "Vertex.h"
+#include <GLES3/gl3.h>
#include <SkScalerContext.h>
-
#include <utils/Log.h>
-#include "FontUtil.h"
-#include "../PixelBuffer.h"
-#include "../Rect.h"
-#include "../Vertex.h"
namespace android {
namespace uirenderer {
@@ -55,7 +54,7 @@ struct CacheBlock {
CacheBlock* mPrev;
CacheBlock(uint16_t x, uint16_t y, uint16_t width, uint16_t height):
- mX(x), mY(y), mWidth(width), mHeight(height), mNext(NULL), mPrev(NULL) {
+ mX(x), mY(y), mWidth(width), mHeight(height), mNext(nullptr), mPrev(nullptr) {
}
static CacheBlock* insertBlock(CacheBlock* head, CacheBlock* newBlock);
@@ -81,9 +80,9 @@ public:
void init();
void releaseMesh();
- void releaseTexture();
+ void releasePixelBuffer();
- void allocateTexture();
+ void allocatePixelBuffer();
void allocateMesh();
// Returns true if glPixelStorei(GL_UNPACK_ROW_LENGTH) must be reset
@@ -93,11 +92,11 @@ public:
bool fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY);
inline uint16_t getWidth() const {
- return mWidth;
+ return mTexture.width;
}
inline uint16_t getHeight() const {
- return mHeight;
+ return mTexture.height;
}
inline GLenum getFormat() const {
@@ -105,7 +104,7 @@ public:
}
inline uint32_t getOffset(uint16_t x, uint16_t y) const {
- return (y * mWidth + x) * PixelBuffer::formatSize(mFormat);
+ return (y * getWidth() + x) * PixelBuffer::formatSize(mFormat);
}
inline const Rect* getDirtyRect() const {
@@ -113,12 +112,17 @@ public:
}
inline PixelBuffer* getPixelBuffer() const {
+ return mPixelBuffer;
+ }
+
+ Texture& getTexture() {
+ allocatePixelBuffer();
return mTexture;
}
GLuint getTextureId() {
- allocateTexture();
- return mTextureId;
+ allocatePixelBuffer();
+ return mTexture.id;
}
inline bool isDirty() const {
@@ -132,7 +136,7 @@ public:
/**
* This method assumes that the proper texture unit is active.
*/
- void setLinearFiltering(bool linearFiltering, bool bind = true);
+ void setLinearFiltering(bool linearFiltering);
inline uint16_t getGlyphCount() const {
return mNumGlyphs;
@@ -147,7 +151,7 @@ public:
}
uint16_t* indices() const {
- return (uint16_t*) 0;
+ return (uint16_t*) nullptr;
}
void resetMesh() {
@@ -177,16 +181,14 @@ public:
private:
void setDirty(bool dirty);
- PixelBuffer* mTexture;
- GLuint mTextureId;
- uint16_t mWidth;
- uint16_t mHeight;
+ PixelBuffer* mPixelBuffer = nullptr;
+ Texture mTexture;
GLenum mFormat;
- bool mLinearFiltering;
- bool mDirty;
- uint16_t mNumGlyphs;
- TextureVertex* mMesh;
- uint32_t mCurrentQuad;
+ bool mLinearFiltering = false;
+ bool mDirty = false;
+ uint16_t mNumGlyphs = 0;
+ TextureVertex* mMesh = nullptr;
+ uint32_t mCurrentQuad = 0;
uint32_t mMaxQuadCount;
Caches& mCaches;
CacheBlock* mCacheBlocks;
diff --git a/libs/hwui/font/CachedGlyphInfo.h b/libs/hwui/font/CachedGlyphInfo.h
index 6680a00..0642d59 100644
--- a/libs/hwui/font/CachedGlyphInfo.h
+++ b/libs/hwui/font/CachedGlyphInfo.h
@@ -19,11 +19,11 @@
#include <SkFixed.h>
-#include "CacheTexture.h"
-
namespace android {
namespace uirenderer {
+class CacheTexture;
+
struct CachedGlyphInfo {
// Has the cache been invalidated?
bool mIsValid;
diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp
index af39f16..b07a3c8 100644
--- a/libs/hwui/font/Font.cpp
+++ b/libs/hwui/font/Font.cpp
@@ -22,6 +22,7 @@
#include <utils/JenkinsHash.h>
#include <utils/Trace.h>
+#include <SkDeviceProperties.h>
#include <SkGlyph.h>
#include <SkGlyphCache.h>
#include <SkUtils.h>
@@ -41,9 +42,7 @@ namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
Font::Font(FontRenderer* state, const Font::FontDescription& desc) :
- mState(state), mDescription(desc) {
- mDeviceProperties = SkDeviceProperties::Make(SkDeviceProperties::Geometry::MakeDefault(), 1.0f);
-}
+ mState(state), mDescription(desc) { }
Font::FontDescription::FontDescription(const SkPaint* paint, const SkMatrix& rasterMatrix)
: mLookupTransform(rasterMatrix) {
@@ -285,7 +284,8 @@ CachedGlyphInfo* Font::getCachedGlyph(const SkPaint* paint, glyph_t textUnit, bo
if (cachedGlyph) {
// Is the glyph still in texture cache?
if (!cachedGlyph->mIsValid) {
- SkAutoGlyphCache autoCache(*paint, &mDeviceProperties, &mDescription.mLookupTransform);
+ SkDeviceProperties deviceProperties(kUnknown_SkPixelGeometry, 1.0f);
+ SkAutoGlyphCache autoCache(*paint, &deviceProperties, &mDescription.mLookupTransform);
const SkGlyph& skiaGlyph = GET_METRICS(autoCache.getCache(), textUnit);
updateGlyphCache(paint, skiaGlyph, autoCache.getCache(), cachedGlyph, precaching);
}
@@ -298,13 +298,13 @@ CachedGlyphInfo* Font::getCachedGlyph(const SkPaint* paint, glyph_t textUnit, bo
void Font::render(const SkPaint* paint, const char *text, uint32_t start, uint32_t len,
int numGlyphs, int x, int y, const float* positions) {
- render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
- 0, 0, NULL, positions);
+ render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, nullptr,
+ 0, 0, nullptr, positions);
}
void Font::render(const SkPaint* paint, const char *text, uint32_t start, uint32_t len,
int numGlyphs, const SkPath* path, float hOffset, float vOffset) {
- if (numGlyphs == 0 || text == NULL || len == 0) {
+ if (numGlyphs == 0 || text == nullptr || len == 0) {
return;
}
@@ -354,18 +354,18 @@ void Font::render(const SkPaint* paint, const char *text, uint32_t start, uint32
void Font::measure(const SkPaint* paint, const char* text, uint32_t start, uint32_t len,
int numGlyphs, Rect *bounds, const float* positions) {
- if (bounds == NULL) {
+ if (bounds == nullptr) {
ALOGE("No return rectangle provided to measure text");
return;
}
bounds->set(1e6, -1e6, -1e6, 1e6);
- render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, positions);
+ render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, nullptr, 0, 0, bounds, positions);
}
void Font::precache(const SkPaint* paint, const char* text, int numGlyphs) {
ATRACE_NAME("Precache Glyphs");
- if (numGlyphs == 0 || text == NULL) {
+ if (numGlyphs == 0 || text == nullptr) {
return;
}
@@ -378,7 +378,7 @@ void Font::precache(const SkPaint* paint, const char* text, int numGlyphs) {
break;
}
- CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph, true);
+ getCachedGlyph(paint, glyph, true);
glyphsCount++;
}
}
@@ -386,7 +386,7 @@ void Font::precache(const SkPaint* paint, const char* text, int numGlyphs) {
void Font::render(const SkPaint* paint, const char* text, uint32_t start, uint32_t len,
int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) {
- if (numGlyphs == 0 || text == NULL || len == 0) {
+ if (numGlyphs == 0 || text == nullptr || len == 0) {
return;
}
@@ -403,8 +403,6 @@ void Font::render(const SkPaint* paint, const char* text, uint32_t start, uint32
text += start;
int glyphsCount = 0;
- const SkPaint::Align align = paint->getTextAlign();
-
while (glyphsCount < numGlyphs) {
glyph_t glyph = GET_GLYPH(text);
@@ -477,7 +475,8 @@ CachedGlyphInfo* Font::cacheGlyph(const SkPaint* paint, glyph_t glyph, bool prec
CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
mCachedGlyphs.add(glyph, newGlyph);
- SkAutoGlyphCache autoCache(*paint, &mDeviceProperties, &mDescription.mLookupTransform);
+ SkDeviceProperties deviceProperties(kUnknown_SkPixelGeometry, 1.0f);
+ SkAutoGlyphCache autoCache(*paint, &deviceProperties, &mDescription.mLookupTransform);
const SkGlyph& skiaGlyph = GET_METRICS(autoCache.getCache(), glyph);
newGlyph->mIsValid = false;
newGlyph->mGlyphIndex = skiaGlyph.fID;
diff --git a/libs/hwui/font/Font.h b/libs/hwui/font/Font.h
index 0f10464..3119d73 100644
--- a/libs/hwui/font/Font.h
+++ b/libs/hwui/font/Font.h
@@ -22,13 +22,12 @@
#include <utils/KeyedVector.h>
#include <SkScalar.h>
-#include <SkDeviceProperties.h>
#include <SkGlyphCache.h>
#include <SkScalerContext.h>
#include <SkPaint.h>
#include <SkPathMeasure.h>
-#include "CachedGlyphInfo.h"
+#include "FontUtil.h"
#include "../Rect.h"
#include "../Matrix.h"
@@ -39,6 +38,8 @@ namespace uirenderer {
// Font
///////////////////////////////////////////////////////////////////////////////
+struct CachedGlyphInfo;
+class CacheTexture;
class FontRenderer;
/**
@@ -119,7 +120,7 @@ private:
void measure(const SkPaint* paint, const char* text, uint32_t start, uint32_t len,
int numGlyphs, Rect *bounds, const float* positions);
- void invalidateTextureCache(CacheTexture* cacheTexture = NULL);
+ void invalidateTextureCache(CacheTexture* cacheTexture = nullptr);
CachedGlyphInfo* cacheGlyph(const SkPaint* paint, glyph_t glyph, bool precaching);
void updateGlyphCache(const SkPaint* paint, const SkGlyph& skiaGlyph,
@@ -150,7 +151,6 @@ private:
DefaultKeyedVector<glyph_t, CachedGlyphInfo*> mCachedGlyphs;
bool mIdentityTransform;
- SkDeviceProperties mDeviceProperties;
};
inline int strictly_order_type(const Font::FontDescription& lhs,
diff --git a/libs/hwui/font/FontUtil.h b/libs/hwui/font/FontUtil.h
index c2fd5f5..4e5debe 100644
--- a/libs/hwui/font/FontUtil.h
+++ b/libs/hwui/font/FontUtil.h
@@ -30,9 +30,12 @@
#define DEFAULT_TEXT_LARGE_CACHE_WIDTH 2048
#define DEFAULT_TEXT_LARGE_CACHE_HEIGHT 512
-#define TEXTURE_BORDER_SIZE 1
-#if TEXTURE_BORDER_SIZE != 1
-# error TEXTURE_BORDER_SIZE other than 1 is not currently supported
+#ifdef TEXTURE_BORDER_SIZE
+ #if TEXTURE_BORDER_SIZE != 1
+ #error TEXTURE_BORDER_SIZE other than 1 is not currently supported
+ #endif
+#else
+ #define TEXTURE_BORDER_SIZE 1
#endif
#define CACHE_BLOCK_ROUNDING_SIZE 4
@@ -44,7 +47,7 @@
#define GET_GLYPH(text) nextGlyph((const uint16_t**) &text)
#define IS_END_OF_STRING(glyph) false
- static glyph_t nextGlyph(const uint16_t** srcPtr) {
+ static inline glyph_t nextGlyph(const uint16_t** srcPtr) {
const uint16_t* src = *srcPtr;
glyph_t g = *src++;
*srcPtr = src;
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.h b/libs/hwui/renderstate/RenderState.h
index 9ac9356..4fd792c 100644
--- a/libs/hwui/RenderState.h
+++ b/libs/hwui/renderstate/RenderState.h
@@ -20,16 +20,26 @@
#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;
@@ -74,8 +84,15 @@ public:
// 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;
@@ -87,8 +104,15 @@ private:
RenderState(renderthread::RenderThread& thread);
~RenderState();
+
renderthread::RenderThread& mRenderThread;
- Caches* mCaches;
+ 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;
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/Stencil.cpp b/libs/hwui/renderstate/Stencil.cpp
index 8ce57db..cedb233 100644
--- a/libs/hwui/Stencil.cpp
+++ b/libs/hwui/renderstate/Stencil.cpp
@@ -14,10 +14,12 @@
* limitations under the License.
*/
+#include "renderstate/Stencil.h"
+
+#include "Caches.h"
#include "Debug.h"
#include "Extensions.h"
#include "Properties.h"
-#include "Stencil.h"
#include <GLES2/gl2ext.h>
@@ -32,7 +34,8 @@ namespace uirenderer {
#define STENCIL_MASK_VALUE 0x1
#endif
-Stencil::Stencil(): mState(kDisabled) {
+Stencil::Stencil()
+ : mState(kDisabled) {
}
uint8_t Stencil::getStencilSize() {
@@ -41,7 +44,7 @@ uint8_t Stencil::getStencilSize() {
GLenum Stencil::getSmallestStencilFormat() {
#if !DEBUG_STENCIL
- const Extensions& extensions = Extensions::getInstance();
+ const Extensions& extensions = Caches::getInstance().extensions();
if (extensions.has1BitStencil()) {
return GL_STENCIL_INDEX1_OES;
} else if (extensions.has4BitStencil()) {
@@ -56,24 +59,36 @@ void Stencil::clear() {
glClear(GL_STENCIL_BUFFER_BIT);
}
-void Stencil::enableTest() {
+void Stencil::enableTest(int incrementThreshold) {
if (mState != kTest) {
enable();
- glStencilFunc(GL_EQUAL, STENCIL_WRITE_VALUE, STENCIL_MASK_VALUE);
+ 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() {
+void Stencil::enableWrite(int incrementThreshold) {
if (mState != kWrite) {
enable();
- 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);
+ 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;
}
}
@@ -110,5 +125,9 @@ void Stencil::disable() {
}
}
+void Stencil::dump() {
+ ALOGD("Stencil: state %d", mState);
+}
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/Stencil.h b/libs/hwui/renderstate/Stencil.h
index c5e5186..e4f0f3f 100644
--- a/libs/hwui/Stencil.h
+++ b/libs/hwui/renderstate/Stencil.h
@@ -53,17 +53,21 @@ public:
void clear();
/**
- * Enables stencil test. When the stencil test is enabled the stencil
- * buffer is not written into.
+ * 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();
+ 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.
+ * 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();
+ void enableWrite(int incrementThreshold);
/**
* The test passes only when equal to the specified value.
@@ -94,6 +98,12 @@ public:
return mState == kTest;
}
+ bool isWriteEnabled() {
+ return mState == kWrite;
+ }
+
+ void dump();
+
private:
void enable();
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
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 75bd067..9237151 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -16,19 +16,19 @@
#include "CanvasContext.h"
-#include <algorithm>
-#include <private/hwui/DrawGlInfo.h>
-#include <strings.h>
-
#include "EglManager.h"
#include "RenderThread.h"
#include "../AnimationContext.h"
#include "../Caches.h"
#include "../DeferredLayerUpdater.h"
-#include "../RenderState.h"
+#include "../renderstate/RenderState.h"
+#include "../renderstate/Stencil.h"
#include "../LayerRenderer.h"
#include "../OpenGLRenderer.h"
-#include "../Stencil.h"
+
+#include <algorithm>
+#include <private/hwui/DrawGlInfo.h>
+#include <strings.h>
#define TRIM_MEMORY_COMPLETE 80
#define TRIM_MEMORY_UI_HIDDEN 20
@@ -41,32 +41,28 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent,
RenderNode* rootRenderNode, IContextFactory* contextFactory)
: mRenderThread(thread)
, mEglManager(thread.eglManager())
- , mEglSurface(EGL_NO_SURFACE)
- , mBufferPreserved(false)
- , mSwapBehavior(kSwap_default)
, mOpaque(!translucent)
- , mCanvas(NULL)
- , mHaveNewSurface(false)
- , mRootRenderNode(rootRenderNode) {
- mAnimationContext = contextFactory->createAnimationContext(mRenderThread.timeLord());
+ , mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord()))
+ , mRootRenderNode(rootRenderNode)
+ , mJankTracker(thread.timeLord().frameIntervalNanos()) {
mRenderThread.renderState().registerCanvasContext(this);
+ mProfiler.setDensity(mRenderThread.mainDisplayInfo().density);
}
CanvasContext::~CanvasContext() {
destroy();
- delete mAnimationContext;
mRenderThread.renderState().unregisterCanvasContext(this);
}
void CanvasContext::destroy() {
stopDrawing();
- setSurface(NULL);
+ setSurface(nullptr);
freePrefetechedLayers();
destroyHardwareResources();
mAnimationContext->destroy();
if (mCanvas) {
delete mCanvas;
- mCanvas = 0;
+ mCanvas = nullptr;
}
}
@@ -96,7 +92,7 @@ void CanvasContext::setSurface(ANativeWindow* window) {
void CanvasContext::swapBuffers() {
if (CC_UNLIKELY(!mEglManager.swapBuffers(mEglSurface))) {
- setSurface(NULL);
+ setSurface(nullptr);
}
mHaveNewSurface = false;
}
@@ -152,9 +148,13 @@ void CanvasContext::processLayerUpdate(DeferredLayerUpdater* layerUpdater) {
}
}
-void CanvasContext::prepareTree(TreeInfo& info) {
+void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo) {
mRenderThread.removeFrameCallback(this);
+ mCurrentFrameInfo = &mFrames.next();
+ mCurrentFrameInfo->importUiThreadInfo(uiFrameInfo);
+ mCurrentFrameInfo->markSyncStart();
+
info.damageAccumulator = &mDamageAccumulator;
info.renderer = mCanvas;
if (mPrefetechedLayers.size() && info.mode == TreeInfo::MODE_FULL) {
@@ -204,6 +204,7 @@ void CanvasContext::draw() {
"drawRenderNode called on a context with no canvas or surface!");
profiler().markPlaybackStart();
+ mCurrentFrameInfo->markIssueDrawCommandsStart();
SkRect dirty;
mDamageAccumulator.finish(&dirty);
@@ -224,29 +225,36 @@ void CanvasContext::draw() {
profiler().unionDirty(&dirty);
}
- status_t status;
if (!dirty.isEmpty()) {
- status = mCanvas->prepareDirty(dirty.fLeft, dirty.fTop,
+ mCanvas->prepareDirty(dirty.fLeft, dirty.fTop,
dirty.fRight, dirty.fBottom, mOpaque);
} else {
- status = mCanvas->prepare(mOpaque);
+ mCanvas->prepare(mOpaque);
}
Rect outBounds;
- status |= mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds);
+ mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds);
profiler().draw(mCanvas);
- mCanvas->finish();
+ bool drew = mCanvas->finish();
profiler().markPlaybackEnd();
- if (status & DrawGlInfo::kStatusDrew) {
+ // Even if we decided to cancel the frame, from the perspective of jank
+ // metrics the frame was swapped at this point
+ mCurrentFrameInfo->markSwapBuffers();
+
+ if (drew) {
swapBuffers();
} else {
mEglManager.cancelFrame();
}
+ // TODO: Use a fence for real completion?
+ mCurrentFrameInfo->markFrameCompleted();
+ mJankTracker.addFrame(*mCurrentFrameInfo);
+ mRenderThread.jankTracker().addFrame(*mCurrentFrameInfo);
profiler().finishFrame();
}
@@ -259,9 +267,14 @@ void CanvasContext::doFrame() {
ATRACE_CALL();
profiler().startFrame();
+ int64_t frameInfo[UI_THREAD_FRAME_INFO_SIZE];
+ UiFrameInfoBuilder(frameInfo)
+ .addFlag(FrameInfoFlags::kRTAnimation)
+ .setVsync(mRenderThread.timeLord().computeFrameTimeNanos(),
+ mRenderThread.timeLord().latestVsync());
TreeInfo info(TreeInfo::MODE_RT_ONLY, mRenderThread.renderState());
- prepareTree(info);
+ prepareTree(info, frameInfo);
if (info.out.canDrawThisFrame) {
draw();
}
@@ -275,19 +288,19 @@ void CanvasContext::invokeFunctor(RenderThread& thread, Functor* functor) {
mode = DrawGlInfo::kModeProcess;
}
- thread.renderState().invokeFunctor(functor, mode, NULL);
+ thread.renderState().invokeFunctor(functor, mode, nullptr);
}
void CanvasContext::markLayerInUse(RenderNode* node) {
if (mPrefetechedLayers.erase(node)) {
- node->decStrong(0);
+ node->decStrong(nullptr);
}
}
static void destroyPrefetechedNode(RenderNode* node) {
ALOGW("Incorrectly called buildLayer on View: %s, destroying layer...", node->getName());
node->destroyHardwareResources();
- node->decStrong(0);
+ node->decStrong(nullptr);
}
void CanvasContext::freePrefetechedLayers() {
@@ -321,7 +334,7 @@ void CanvasContext::buildLayer(RenderNode* node) {
mCanvas->markLayersAsBuildLayers();
mCanvas->flushLayerUpdates();
- node->incStrong(0);
+ node->incStrong(nullptr);
mPrefetechedLayers.insert(node);
}
@@ -374,6 +387,28 @@ void CanvasContext::setTextureAtlas(RenderThread& thread,
thread.eglManager().setTextureAtlas(buffer, map, mapSize);
}
+void CanvasContext::dumpFrames(int fd) {
+ FILE* file = fdopen(fd, "a");
+ fprintf(file, "\n\n---PROFILEDATA---");
+ for (size_t i = 0; i < mFrames.size(); i++) {
+ FrameInfo& frame = mFrames[i];
+ if (frame[FrameInfoIndex::kSyncStart] == 0) {
+ continue;
+ }
+ fprintf(file, "\n");
+ for (int i = 0; i < static_cast<int>(FrameInfoIndex::kNumIndexes); i++) {
+ fprintf(file, "%" PRId64 ",", frame[i]);
+ }
+ }
+ fprintf(file, "\n---PROFILEDATA---\n\n");
+ fflush(file);
+}
+
+void CanvasContext::resetFrameStats() {
+ mFrames.clear();
+ mRenderThread.jankTracker().reset();
+}
+
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 0cc2c7c..f5f1f54 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -17,7 +17,14 @@
#ifndef CANVASCONTEXT_H_
#define CANVASCONTEXT_H_
-#include <set>
+#include "DamageAccumulator.h"
+#include "DrawProfiler.h"
+#include "IContextFactory.h"
+#include "FrameInfo.h"
+#include "RenderNode.h"
+#include "utils/RingBuffer.h"
+#include "renderthread/RenderTask.h"
+#include "renderthread/RenderThread.h"
#include <cutils/compiler.h>
#include <EGL/egl.h>
@@ -25,14 +32,8 @@
#include <utils/Functor.h>
#include <utils/Vector.h>
-#include "../DamageAccumulator.h"
-#include "../DrawProfiler.h"
-#include "../IContextFactory.h"
-#include "../RenderNode.h"
-#include "RenderTask.h"
-#include "RenderThread.h"
-
-#define FUNCTOR_PROCESS_DELAY 4
+#include <set>
+#include <string>
namespace android {
namespace uirenderer {
@@ -75,12 +76,12 @@ public:
void setOpaque(bool opaque);
void makeCurrent();
void processLayerUpdate(DeferredLayerUpdater* layerUpdater);
- void prepareTree(TreeInfo& info);
+ void prepareTree(TreeInfo& info, int64_t* uiFrameInfo);
void draw();
void destroy();
// IFrameCallback, Chroreographer-driven frame callback entry point
- virtual void doFrame();
+ virtual void doFrame() override;
void buildLayer(RenderNode* node);
bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
@@ -103,6 +104,12 @@ public:
DrawProfiler& profiler() { return mProfiler; }
+ void dumpFrames(int fd);
+ void resetFrameStats();
+
+ void setName(const std::string&& name) { mName = name; }
+ const std::string& name() { return mName; }
+
private:
friend class RegisterFrameCallbackTask;
// TODO: Replace with something better for layer & other GL object
@@ -120,19 +127,24 @@ private:
RenderThread& mRenderThread;
EglManager& mEglManager;
sp<ANativeWindow> mNativeWindow;
- EGLSurface mEglSurface;
- bool mBufferPreserved;
- SwapBehavior mSwapBehavior;
+ EGLSurface mEglSurface = EGL_NO_SURFACE;
+ bool mBufferPreserved = false;
+ SwapBehavior mSwapBehavior = kSwap_default;
bool mOpaque;
- OpenGLRenderer* mCanvas;
- bool mHaveNewSurface;
+ OpenGLRenderer* mCanvas = nullptr;
+ bool mHaveNewSurface = false;
DamageAccumulator mDamageAccumulator;
- AnimationContext* mAnimationContext;
+ std::unique_ptr<AnimationContext> mAnimationContext;
const sp<RenderNode> mRootRenderNode;
DrawProfiler mProfiler;
+ FrameInfo* mCurrentFrameInfo = nullptr;
+ // Ring buffer large enough for 1 second worth of frames
+ RingBuffer<FrameInfo, 60> mFrames;
+ std::string mName;
+ JankTracker mJankTracker;
std::set<RenderNode*> mPrefetechedLayers;
};
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 97b31a9..35391b2 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -32,11 +32,8 @@ namespace uirenderer {
namespace renderthread {
DrawFrameTask::DrawFrameTask()
- : mRenderThread(NULL)
- , mContext(NULL)
- , mFrameTimeNanos(0)
- , mRecordDurationNanos(0)
- , mDensity(1.0f) // safe enough default
+ : mRenderThread(nullptr)
+ , mContext(nullptr)
, mSyncResult(kSync_OK) {
}
@@ -68,18 +65,12 @@ void DrawFrameTask::removeLayerUpdate(DeferredLayerUpdater* layer) {
}
}
-int DrawFrameTask::drawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos) {
+int DrawFrameTask::drawFrame() {
LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!");
mSyncResult = kSync_OK;
- mFrameTimeNanos = frameTimeNanos;
- mRecordDurationNanos = recordDurationNanos;
postAndWait();
- // Reset the single-frame data
- mFrameTimeNanos = 0;
- mRecordDurationNanos = 0;
-
return mSyncResult;
}
@@ -92,8 +83,7 @@ void DrawFrameTask::postAndWait() {
void DrawFrameTask::run() {
ATRACE_NAME("DrawFrame");
- mContext->profiler().setDensity(mDensity);
- mContext->profiler().startFrame(mRecordDurationNanos);
+ mContext->profiler().startFrame();
bool canUnblockUiThread;
bool canDrawThisFrame;
@@ -122,7 +112,8 @@ void DrawFrameTask::run() {
bool DrawFrameTask::syncFrameState(TreeInfo& info) {
ATRACE_CALL();
- mRenderThread->timeLord().vsyncReceived(mFrameTimeNanos);
+ int64_t vsync = mFrameInfo[static_cast<int>(FrameInfoIndex::kVsync)];
+ mRenderThread->timeLord().vsyncReceived(vsync);
mContext->makeCurrent();
Caches::getInstance().textureCache.resetMarkInUse();
@@ -130,7 +121,7 @@ bool DrawFrameTask::syncFrameState(TreeInfo& info) {
mContext->processLayerUpdate(mLayers[i].get());
}
mLayers.clear();
- mContext->prepareTree(info);
+ mContext->prepareTree(info, mFrameInfo);
// This is after the prepareTree so that any pending operations
// (RenderNode tree state, prefetched layers, etc...) will be flushed.
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index 28f6cb2..8039643 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -25,6 +25,7 @@
#include "RenderTask.h"
#include "../Rect.h"
+#include "../FrameInfo.h"
#include "../TreeInfo.h"
namespace android {
@@ -61,10 +62,11 @@ public:
void pushLayerUpdate(DeferredLayerUpdater* layer);
void removeLayerUpdate(DeferredLayerUpdater* layer);
- void setDensity(float density) { mDensity = density; }
- int drawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos);
+ int drawFrame();
- virtual void run();
+ int64_t* frameInfo() { return mFrameInfo; }
+
+ virtual void run() override;
private:
void postAndWait();
@@ -80,12 +82,11 @@ private:
/*********************************************
* Single frame data
*********************************************/
- nsecs_t mFrameTimeNanos;
- nsecs_t mRecordDurationNanos;
- float mDensity;
std::vector< sp<DeferredLayerUpdater> > mLayers;
int mSyncResult;
+
+ int64_t mFrameInfo[UI_THREAD_FRAME_INFO_SIZE];
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 8fb1b10..3afca2f 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -16,15 +16,19 @@
#include "EglManager.h"
+#include "../Caches.h"
+#include "../renderstate/RenderState.h"
+#include "RenderThread.h"
+
#include <cutils/log.h>
#include <cutils/properties.h>
-
-#include "../RenderState.h"
-#include "RenderThread.h"
+#include <EGL/eglext.h>
#define PROPERTY_RENDER_DIRTY_REGIONS "debug.hwui.render_dirty_regions"
#define GLES_VERSION 2
+#define WAIT_FOR_GPU_COMPLETION 0
+
// Android-specific addition that is used to show when frames began in systrace
EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface);
@@ -67,12 +71,12 @@ static bool load_dirty_regions_property() {
EglManager::EglManager(RenderThread& thread)
: mRenderThread(thread)
, mEglDisplay(EGL_NO_DISPLAY)
- , mEglConfig(0)
+ , mEglConfig(nullptr)
, mEglContext(EGL_NO_CONTEXT)
, mPBufferSurface(EGL_NO_SURFACE)
, mAllowPreserveBuffer(load_dirty_regions_property())
, mCurrentSurface(EGL_NO_SURFACE)
- , mAtlasMap(NULL)
+ , mAtlasMap(nullptr)
, mAtlasMapSize(0)
, mInFrame(false) {
mCanSetPreserveBuffer = mAllowPreserveBuffer;
@@ -193,7 +197,7 @@ void EglManager::usePBufferSurface() {
EGLSurface EglManager::createSurface(EGLNativeWindowType window) {
initialize();
- EGLSurface surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, window, NULL);
+ EGLSurface surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, window, nullptr);
LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE,
"Failed to create EGLSurface for window %p, eglErr = %s",
(void*) window, egl_error_str());
@@ -213,9 +217,6 @@ void EglManager::destroy() {
if (mEglDisplay == EGL_NO_DISPLAY) return;
usePBufferSurface();
- if (Caches::hasInstance()) {
- Caches::getInstance().terminate();
- }
mRenderThread.renderState().onGLContextDestroyed();
eglDestroyContext(mEglDisplay, mEglContext);
@@ -262,6 +263,14 @@ void EglManager::beginFrame(EGLSurface surface, EGLint* width, EGLint* height) {
bool EglManager::swapBuffers(EGLSurface surface) {
mInFrame = false;
+
+#if WAIT_FOR_GPU_COMPLETION
+ {
+ ATRACE_NAME("Finishing GPU work");
+ fence();
+ }
+#endif
+
eglSwapBuffers(mEglDisplay, surface);
EGLint err = eglGetError();
if (CC_LIKELY(err == EGL_SUCCESS)) {
@@ -280,6 +289,13 @@ bool EglManager::swapBuffers(EGLSurface surface) {
return false;
}
+void EglManager::fence() {
+ EGLSyncKHR fence = eglCreateSyncKHR(mEglDisplay, EGL_SYNC_FENCE_KHR, NULL);
+ eglClientWaitSyncKHR(mEglDisplay, fence,
+ EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, EGL_FOREVER_KHR);
+ eglDestroySyncKHR(mEglDisplay, fence);
+}
+
void EglManager::cancelFrame() {
mInFrame = false;
}
diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h
index e12db3a..b1a18a9 100644
--- a/libs/hwui/renderthread/EglManager.h
+++ b/libs/hwui/renderthread/EglManager.h
@@ -55,6 +55,8 @@ public:
void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize);
+ void fence();
+
private:
friend class RenderThread;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 6d063a4..cc87241 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -16,14 +16,14 @@
#include "RenderProxy.h"
-#include "CanvasContext.h"
-#include "RenderTask.h"
-#include "RenderThread.h"
-
-#include "../DeferredLayerUpdater.h"
-#include "../DisplayList.h"
-#include "../LayerRenderer.h"
-#include "../Rect.h"
+#include "DeferredLayerUpdater.h"
+#include "DisplayList.h"
+#include "LayerRenderer.h"
+#include "Rect.h"
+#include "renderthread/CanvasContext.h"
+#include "renderthread/RenderTask.h"
+#include "renderthread/RenderThread.h"
+#include "utils/Macros.h"
namespace android {
namespace uirenderer {
@@ -52,6 +52,12 @@ namespace renderthread {
MethodInvokeRenderTask* task = new MethodInvokeRenderTask((RunnableMethod) Bridge_ ## method); \
ARGS(method) *args = (ARGS(method) *) task->payload()
+enum class DumpFlags {
+ kFrameStats = 1 << 0,
+ kReset = 1 << 1,
+};
+MAKE_FLAGS_ENUM(DumpFlags)
+
CREATE_BRIDGE4(createContext, RenderThread* thread, bool translucent,
RenderNode* rootRenderNode, IContextFactory* contextFactory) {
return new CanvasContext(*args->thread, args->translucent,
@@ -60,7 +66,7 @@ CREATE_BRIDGE4(createContext, RenderThread* thread, bool translucent,
RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory)
: mRenderThread(RenderThread::getInstance())
- , mContext(0) {
+ , mContext(nullptr) {
SETUP_TASK(createContext);
args->translucent = translucent;
args->rootRenderNode = rootRenderNode;
@@ -76,36 +82,24 @@ RenderProxy::~RenderProxy() {
CREATE_BRIDGE1(destroyContext, CanvasContext* context) {
delete args->context;
- return NULL;
+ return nullptr;
}
void RenderProxy::destroyContext() {
if (mContext) {
SETUP_TASK(destroyContext);
args->context = mContext;
- mContext = 0;
- mDrawFrameTask.setContext(NULL, NULL);
+ mContext = nullptr;
+ mDrawFrameTask.setContext(nullptr, nullptr);
// This is also a fence as we need to be certain that there are no
// outstanding mDrawFrame tasks posted before it is destroyed
postAndWait(task);
}
}
-CREATE_BRIDGE2(setFrameInterval, RenderThread* thread, nsecs_t frameIntervalNanos) {
- args->thread->timeLord().setFrameInterval(args->frameIntervalNanos);
- return NULL;
-}
-
-void RenderProxy::setFrameInterval(nsecs_t frameIntervalNanos) {
- SETUP_TASK(setFrameInterval);
- args->thread = &mRenderThread;
- args->frameIntervalNanos = frameIntervalNanos;
- post(task);
-}
-
CREATE_BRIDGE2(setSwapBehavior, CanvasContext* context, SwapBehavior swapBehavior) {
args->context->setSwapBehavior(args->swapBehavior);
- return NULL;
+ return nullptr;
}
void RenderProxy::setSwapBehavior(SwapBehavior swapBehavior) {
@@ -132,6 +126,18 @@ bool RenderProxy::loadSystemProperties() {
return (bool) postAndWait(task);
}
+CREATE_BRIDGE2(setName, CanvasContext* context, const char* name) {
+ args->context->setName(std::string(args->name));
+ return nullptr;
+}
+
+void RenderProxy::setName(const char* name) {
+ SETUP_TASK(setName);
+ args->context = mContext;
+ args->name = name;
+ post(task);
+}
+
CREATE_BRIDGE2(initialize, CanvasContext* context, ANativeWindow* window) {
return (void*) args->context->initialize(args->window);
}
@@ -145,7 +151,7 @@ bool RenderProxy::initialize(const sp<ANativeWindow>& window) {
CREATE_BRIDGE2(updateSurface, CanvasContext* context, ANativeWindow* window) {
args->context->updateSurface(args->window);
- return NULL;
+ return nullptr;
}
void RenderProxy::updateSurface(const sp<ANativeWindow>& window) {
@@ -171,7 +177,7 @@ CREATE_BRIDGE7(setup, CanvasContext* context, int width, int height,
uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
args->context->setup(args->width, args->height, args->lightCenter, args->lightRadius,
args->ambientShadowAlpha, args->spotShadowAlpha);
- return NULL;
+ return nullptr;
}
void RenderProxy::setup(int width, int height, const Vector3& lightCenter, float lightRadius,
@@ -189,7 +195,7 @@ void RenderProxy::setup(int width, int height, const Vector3& lightCenter, float
CREATE_BRIDGE2(setOpaque, CanvasContext* context, bool opaque) {
args->context->setOpaque(args->opaque);
- return NULL;
+ return nullptr;
}
void RenderProxy::setOpaque(bool opaque) {
@@ -199,15 +205,17 @@ void RenderProxy::setOpaque(bool opaque) {
post(task);
}
-int RenderProxy::syncAndDrawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos,
- float density) {
- mDrawFrameTask.setDensity(density);
- return mDrawFrameTask.drawFrame(frameTimeNanos, recordDurationNanos);
+int64_t* RenderProxy::frameInfo() {
+ return mDrawFrameTask.frameInfo();
+}
+
+int RenderProxy::syncAndDrawFrame() {
+ return mDrawFrameTask.drawFrame();
}
CREATE_BRIDGE1(destroy, CanvasContext* context) {
args->context->destroy();
- return NULL;
+ return nullptr;
}
void RenderProxy::destroy() {
@@ -221,7 +229,7 @@ void RenderProxy::destroy() {
CREATE_BRIDGE2(invokeFunctor, RenderThread* thread, Functor* functor) {
CanvasContext::invokeFunctor(*args->thread, args->functor);
- return NULL;
+ return nullptr;
}
void RenderProxy::invokeFunctor(Functor* functor, bool waitForCompletion) {
@@ -242,7 +250,7 @@ void RenderProxy::invokeFunctor(Functor* functor, bool waitForCompletion) {
CREATE_BRIDGE2(runWithGlContext, CanvasContext* context, RenderTask* task) {
args->context->runWithGlContext(args->task);
- return NULL;
+ return nullptr;
}
void RenderProxy::runWithGlContext(RenderTask* gltask) {
@@ -254,7 +262,7 @@ void RenderProxy::runWithGlContext(RenderTask* gltask) {
CREATE_BRIDGE2(createTextureLayer, RenderThread* thread, CanvasContext* context) {
Layer* layer = args->context->createTextureLayer();
- if (!layer) return 0;
+ if (!layer) return nullptr;
return new DeferredLayerUpdater(*args->thread, layer);
}
@@ -269,7 +277,7 @@ DeferredLayerUpdater* RenderProxy::createTextureLayer() {
CREATE_BRIDGE2(buildLayer, CanvasContext* context, RenderNode* node) {
args->context->buildLayer(args->node);
- return NULL;
+ return nullptr;
}
void RenderProxy::buildLayer(RenderNode* node) {
@@ -303,7 +311,7 @@ void RenderProxy::cancelLayerUpdate(DeferredLayerUpdater* layer) {
CREATE_BRIDGE1(detachSurfaceTexture, DeferredLayerUpdater* layer) {
args->layer->detachSurfaceTexture();
- return NULL;
+ return nullptr;
}
void RenderProxy::detachSurfaceTexture(DeferredLayerUpdater* layer) {
@@ -314,7 +322,7 @@ void RenderProxy::detachSurfaceTexture(DeferredLayerUpdater* layer) {
CREATE_BRIDGE1(destroyHardwareResources, CanvasContext* context) {
args->context->destroyHardwareResources();
- return NULL;
+ return nullptr;
}
void RenderProxy::destroyHardwareResources() {
@@ -325,7 +333,7 @@ void RenderProxy::destroyHardwareResources() {
CREATE_BRIDGE2(timMemory, RenderThread* thread, int level) {
CanvasContext::trimMemory(*args->thread, args->level);
- return NULL;
+ return nullptr;
}
void RenderProxy::trimMemory(int level) {
@@ -341,17 +349,21 @@ void RenderProxy::trimMemory(int level) {
CREATE_BRIDGE0(fence) {
// Intentionally empty
- return NULL;
+ return nullptr;
}
+template <typename T>
+void UNUSED(T t) {}
+
void RenderProxy::fence() {
SETUP_TASK(fence);
+ UNUSED(args);
postAndWait(task);
}
CREATE_BRIDGE1(stopDrawing, CanvasContext* context) {
args->context->stopDrawing();
- return NULL;
+ return nullptr;
}
void RenderProxy::stopDrawing() {
@@ -362,7 +374,7 @@ void RenderProxy::stopDrawing() {
CREATE_BRIDGE1(notifyFramePending, CanvasContext* context) {
args->context->notifyFramePending();
- return NULL;
+ return nullptr;
}
void RenderProxy::notifyFramePending() {
@@ -371,45 +383,77 @@ void RenderProxy::notifyFramePending() {
mRenderThread.queueAtFront(task);
}
-CREATE_BRIDGE2(dumpProfileInfo, CanvasContext* context, int fd) {
+CREATE_BRIDGE3(dumpProfileInfo, CanvasContext* context, int fd, int dumpFlags) {
args->context->profiler().dumpData(args->fd);
- return NULL;
+ if (args->dumpFlags & DumpFlags::kFrameStats) {
+ args->context->dumpFrames(args->fd);
+ }
+ if (args->dumpFlags & DumpFlags::kReset) {
+ args->context->resetFrameStats();
+ }
+ return nullptr;
}
-void RenderProxy::dumpProfileInfo(int fd) {
+void RenderProxy::dumpProfileInfo(int fd, int dumpFlags) {
SETUP_TASK(dumpProfileInfo);
args->context = mContext;
args->fd = fd;
+ args->dumpFlags = dumpFlags;
postAndWait(task);
}
-CREATE_BRIDGE1(outputLogBuffer, int fd) {
- RenderNode::outputLogBuffer(args->fd);
- return NULL;
+CREATE_BRIDGE2(dumpGraphicsMemory, int fd, RenderThread* thread) {
+ args->thread->jankTracker().dump(args->fd);
+
+ FILE *file = fdopen(args->fd, "a");
+ if (Caches::hasInstance()) {
+ String8 cachesLog;
+ Caches::getInstance().dumpMemoryUsage(cachesLog);
+ fprintf(file, "\nCaches:\n%s\n", cachesLog.string());
+ } else {
+ fprintf(file, "\nNo caches instance.\n");
+ }
+ fflush(file);
+ return nullptr;
}
-void RenderProxy::outputLogBuffer(int fd) {
- SETUP_TASK(outputLogBuffer);
+void RenderProxy::dumpGraphicsMemory(int fd) {
+ if (!RenderThread::hasInstance()) return;
+ SETUP_TASK(dumpGraphicsMemory);
args->fd = fd;
+ args->thread = &RenderThread::getInstance();
staticPostAndWait(task);
}
CREATE_BRIDGE4(setTextureAtlas, RenderThread* thread, GraphicBuffer* buffer, int64_t* map, size_t size) {
CanvasContext::setTextureAtlas(*args->thread, args->buffer, args->map, args->size);
- args->buffer->decStrong(0);
- return NULL;
+ args->buffer->decStrong(nullptr);
+ return nullptr;
}
void RenderProxy::setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size) {
SETUP_TASK(setTextureAtlas);
args->thread = &mRenderThread;
args->buffer = buffer.get();
- args->buffer->incStrong(0);
+ args->buffer->incStrong(nullptr);
args->map = map;
args->size = size;
post(task);
}
+CREATE_BRIDGE2(setProcessStatsBuffer, RenderThread* thread, int fd) {
+ args->thread->jankTracker().switchStorageToAshmem(args->fd);
+ close(args->fd);
+ return nullptr;
+}
+
+void RenderProxy::setProcessStatsBuffer(int fd) {
+ SETUP_TASK(setProcessStatsBuffer);
+ args->thread = &mRenderThread;
+ args->fd = dup(fd);
+ post(task);
+}
+
void RenderProxy::post(RenderTask* task) {
mRenderThread.queue(task);
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index fd1fe05..29c6f08 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -62,10 +62,10 @@ public:
ANDROID_API RenderProxy(bool translucent, RenderNode* rootNode, IContextFactory* contextFactory);
ANDROID_API virtual ~RenderProxy();
- ANDROID_API void setFrameInterval(nsecs_t frameIntervalNanos);
// Won't take effect until next EGLSurface creation
ANDROID_API void setSwapBehavior(SwapBehavior swapBehavior);
ANDROID_API bool loadSystemProperties();
+ ANDROID_API void setName(const char* name);
ANDROID_API bool initialize(const sp<ANativeWindow>& window);
ANDROID_API void updateSurface(const sp<ANativeWindow>& window);
@@ -73,8 +73,8 @@ public:
ANDROID_API void setup(int width, int height, const Vector3& lightCenter, float lightRadius,
uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
ANDROID_API void setOpaque(bool opaque);
- ANDROID_API int syncAndDrawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos,
- float density);
+ ANDROID_API int64_t* frameInfo();
+ ANDROID_API int syncAndDrawFrame();
ANDROID_API void destroy();
ANDROID_API static void invokeFunctor(Functor* functor, bool waitForCompletion);
@@ -95,10 +95,11 @@ public:
ANDROID_API void stopDrawing();
ANDROID_API void notifyFramePending();
- ANDROID_API void dumpProfileInfo(int fd);
- ANDROID_API static void outputLogBuffer(int fd);
+ ANDROID_API void dumpProfileInfo(int fd, int dumpFlags);
+ ANDROID_API static void dumpGraphicsMemory(int fd);
ANDROID_API void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size);
+ ANDROID_API void setProcessStatsBuffer(int fd);
private:
RenderThread& mRenderThread;
diff --git a/libs/hwui/renderthread/RenderTask.cpp b/libs/hwui/renderthread/RenderTask.cpp
index 7ca61e4..b14f580 100644
--- a/libs/hwui/renderthread/RenderTask.cpp
+++ b/libs/hwui/renderthread/RenderTask.cpp
@@ -14,11 +14,8 @@
* limitations under the License.
*/
-#define LOG_TAG "RenderTask"
-
#include "RenderTask.h"
-#include <utils/Log.h>
#include <utils/Condition.h>
#include <utils/Mutex.h>
diff --git a/libs/hwui/renderthread/RenderTask.h b/libs/hwui/renderthread/RenderTask.h
index 1554a16..89c3a7d 100644
--- a/libs/hwui/renderthread/RenderTask.h
+++ b/libs/hwui/renderthread/RenderTask.h
@@ -47,7 +47,7 @@ namespace renderthread {
class ANDROID_API RenderTask {
public:
- ANDROID_API RenderTask() : mNext(0), mRunAt(0) {}
+ ANDROID_API RenderTask() : mNext(nullptr), mRunAt(0) {}
ANDROID_API virtual ~RenderTask() {}
ANDROID_API virtual void run() = 0;
@@ -61,7 +61,7 @@ public:
// Takes ownership of task, caller owns lock and signal
SignalingRenderTask(RenderTask* task, Mutex* lock, Condition* signal)
: mTask(task), mLock(lock), mSignal(signal) {}
- virtual void run();
+ virtual void run() override;
private:
RenderTask* mTask;
@@ -74,12 +74,12 @@ typedef void* (*RunnableMethod)(void* data);
class MethodInvokeRenderTask : public RenderTask {
public:
MethodInvokeRenderTask(RunnableMethod method)
- : mMethod(method), mReturnPtr(0) {}
+ : mMethod(method), mReturnPtr(nullptr) {}
void* payload() { return mData; }
void setReturnPtr(void** retptr) { mReturnPtr = retptr; }
- virtual void run() {
+ virtual void run() override {
void* retval = mMethod(mData);
if (mReturnPtr) {
*mReturnPtr = retval;
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 84826b7..3ac2976 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -16,17 +16,17 @@
#include "RenderThread.h"
-#if defined(HAVE_PTHREADS)
-#include <sys/resource.h>
-#endif
-#include <gui/DisplayEventReceiver.h>
-#include <utils/Log.h>
-
-#include "../RenderState.h"
+#include "../renderstate/RenderState.h"
#include "CanvasContext.h"
#include "EglManager.h"
#include "RenderProxy.h"
+#include <gui/DisplayEventReceiver.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/SurfaceComposerClient.h>
+#include <sys/resource.h>
+#include <utils/Log.h>
+
namespace android {
using namespace uirenderer::renderthread;
ANDROID_SINGLETON_STATIC_INSTANCE(RenderThread);
@@ -42,16 +42,16 @@ static const size_t EVENT_BUFFER_SIZE = 100;
// Slight delay to give the UI time to push us a new frame before we replay
static const nsecs_t DISPATCH_FRAME_CALLBACKS_DELAY = milliseconds_to_nanoseconds(4);
-TaskQueue::TaskQueue() : mHead(0), mTail(0) {}
+TaskQueue::TaskQueue() : mHead(nullptr), mTail(nullptr) {}
RenderTask* TaskQueue::next() {
RenderTask* ret = mHead;
if (ret) {
mHead = ret->mNext;
if (!mHead) {
- mTail = 0;
+ mTail = nullptr;
}
- ret->mNext = 0;
+ ret->mNext = nullptr;
}
return ret;
}
@@ -71,7 +71,7 @@ void TaskQueue::queue(RenderTask* task) {
mTail = task;
} else {
// Need to find the proper insertion point
- RenderTask* previous = 0;
+ RenderTask* previous = nullptr;
RenderTask* next = mHead;
while (next && next->mRunAt <= task->mRunAt) {
previous = next;
@@ -131,19 +131,19 @@ private:
public:
DispatchFrameCallbacks(RenderThread* rt) : mRenderThread(rt) {}
- virtual void run() {
+ virtual void run() override {
mRenderThread->dispatchFrameCallbacks();
}
};
RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>()
, mNextWakeup(LLONG_MAX)
- , mDisplayEventReceiver(0)
+ , mDisplayEventReceiver(nullptr)
, mVsyncRequested(false)
, mFrameCallbackTaskPending(false)
- , mFrameCallbackTask(0)
- , mRenderState(NULL)
- , mEglManager(NULL) {
+ , mFrameCallbackTask(nullptr)
+ , mRenderState(nullptr)
+ , mEglManager(nullptr) {
mFrameCallbackTask = new DispatchFrameCallbacks(this);
mLooper = new Looper(false);
run("RenderThread");
@@ -166,9 +166,16 @@ void RenderThread::initializeDisplayEventReceiver() {
}
void RenderThread::initThreadLocals() {
+ sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(
+ ISurfaceComposer::eDisplayIdMain));
+ status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &mDisplayInfo);
+ LOG_ALWAYS_FATAL_IF(status, "Failed to get display info\n");
+ nsecs_t frameIntervalNanos = static_cast<nsecs_t>(1000000000 / mDisplayInfo.fps);
+ mTimeLord.setFrameInterval(frameIntervalNanos);
initializeDisplayEventReceiver();
mEglManager = new EglManager(*this);
mRenderState = new RenderState(*this);
+ mJankTracker = new JankTracker(frameIntervalNanos);
}
int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) {
@@ -250,9 +257,7 @@ void RenderThread::requestVsync() {
}
bool RenderThread::threadLoop() {
-#if defined(HAVE_PTHREADS)
setpriority(PRIO_PROCESS, 0, PRIORITY_DISPLAY);
-#endif
initThreadLocals();
int timeoutMillis = -1;
@@ -350,7 +355,7 @@ RenderTask* RenderThread::nextTask(nsecs_t* nextWakeup) {
if (next->mRunAt <= 0 || next->mRunAt <= systemTime(SYSTEM_TIME_MONOTONIC)) {
next = mQueue.next();
} else {
- next = 0;
+ next = nullptr;
}
}
if (nextWakeup) {
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 99c2e15..8096099 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -19,16 +19,18 @@
#include "RenderTask.h"
-#include <memory>
-#include <set>
+#include "../JankTracker.h"
+#include "TimeLord.h"
#include <cutils/compiler.h>
+#include <ui/DisplayInfo.h>
#include <utils/Looper.h>
#include <utils/Mutex.h>
#include <utils/Singleton.h>
#include <utils/Thread.h>
-#include "TimeLord.h"
+#include <memory>
+#include <set>
namespace android {
@@ -88,9 +90,12 @@ public:
TimeLord& timeLord() { return mTimeLord; }
RenderState& renderState() { return *mRenderState; }
EglManager& eglManager() { return *mEglManager; }
+ JankTracker& jankTracker() { return *mJankTracker; }
+
+ const DisplayInfo& mainDisplayInfo() { return mDisplayInfo; }
protected:
- virtual bool threadLoop();
+ virtual bool threadLoop() override;
private:
friend class Singleton<RenderThread>;
@@ -118,6 +123,8 @@ private:
nsecs_t mNextWakeup;
TaskQueue mQueue;
+ DisplayInfo mDisplayInfo;
+
DisplayEventReceiver* mDisplayEventReceiver;
bool mVsyncRequested;
std::set<IFrameCallback*> mFrameCallbacks;
@@ -132,6 +139,8 @@ private:
TimeLord mTimeLord;
RenderState* mRenderState;
EglManager* mEglManager;
+
+ JankTracker* mJankTracker = nullptr;
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/TimeLord.cpp b/libs/hwui/renderthread/TimeLord.cpp
index f187493..f846d6a 100644
--- a/libs/hwui/renderthread/TimeLord.cpp
+++ b/libs/hwui/renderthread/TimeLord.cpp
@@ -32,7 +32,7 @@ bool TimeLord::vsyncReceived(nsecs_t vsync) {
return false;
}
-nsecs_t TimeLord::computeFrameTimeMs() {
+nsecs_t TimeLord::computeFrameTimeNanos() {
// Logic copied from Choreographer.java
nsecs_t now = systemTime(CLOCK_MONOTONIC);
nsecs_t jitterNanos = now - mFrameTimeNanos;
@@ -40,7 +40,11 @@ nsecs_t TimeLord::computeFrameTimeMs() {
nsecs_t lastFrameOffset = jitterNanos % mFrameIntervalNanos;
mFrameTimeNanos = now - lastFrameOffset;
}
- return nanoseconds_to_milliseconds(mFrameTimeNanos);
+ return mFrameTimeNanos;
+}
+
+nsecs_t TimeLord::computeFrameTimeMs() {
+ return nanoseconds_to_milliseconds(computeFrameTimeNanos());
}
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/TimeLord.h b/libs/hwui/renderthread/TimeLord.h
index 7c155d2..5464399 100644
--- a/libs/hwui/renderthread/TimeLord.h
+++ b/libs/hwui/renderthread/TimeLord.h
@@ -29,9 +29,13 @@ class RenderThread;
class TimeLord {
public:
void setFrameInterval(nsecs_t intervalNanos) { mFrameIntervalNanos = intervalNanos; }
+ nsecs_t frameIntervalNanos() const { return mFrameIntervalNanos; }
+
// returns true if the vsync is newer, false if it was rejected for staleness
bool vsyncReceived(nsecs_t vsync);
+ nsecs_t latestVsync() { return mFrameTimeNanos; }
nsecs_t computeFrameTimeMs();
+ nsecs_t computeFrameTimeNanos();
private:
friend class RenderThread;
diff --git a/libs/hwui/tests/Android.mk b/libs/hwui/tests/Android.mk
index 7bdce7f..b6f0baf 100644
--- a/libs/hwui/tests/Android.mk
+++ b/libs/hwui/tests/Android.mk
@@ -15,34 +15,9 @@
#
local_target_dir := $(TARGET_OUT_DATA)/local/tmp
-LOCAL_PATH:= $(call my-dir)
+LOCAL_PATH:= $(call my-dir)/..
include $(CLEAR_VARS)
-LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES
-LOCAL_CFLAGS += -Wno-unused-parameter
-LOCAL_CFLAGS += -DATRACE_TAG=ATRACE_TAG_VIEW -DLOG_TAG=\"OpenGLRenderer\"
-
-LOCAL_SRC_FILES:= \
- TestContext.cpp \
- main.cpp
-
-LOCAL_C_INCLUDES += \
- $(LOCAL_PATH)/.. \
- external/skia/src/core
-
-LOCAL_SHARED_LIBRARIES := \
- liblog \
- libcutils \
- libutils \
- libskia \
- libgui \
- libui \
- libhwui
-
-ifeq ($(WITH_MALLOC_LEAK_CHECK),true)
- LOCAL_CFLAGS += -DMALLOC_LEAK_CHECK
-endif
-
LOCAL_MODULE_PATH := $(local_target_dir)
LOCAL_MODULE:= hwuitest
LOCAL_MODULE_TAGS := tests
@@ -50,7 +25,12 @@ LOCAL_MULTILIB := both
LOCAL_MODULE_STEM_32 := hwuitest
LOCAL_MODULE_STEM_64 := hwuitest64
-include external/stlport/libstlport.mk
-include $(BUILD_EXECUTABLE)
+HWUI_NULL_GPU := false
+
+include $(LOCAL_PATH)/Android.common.mk
-include $(call all-makefiles-under,$(LOCAL_PATH))
+LOCAL_SRC_FILES += \
+ tests/TestContext.cpp \
+ tests/main.cpp
+
+include $(BUILD_EXECUTABLE)
diff --git a/libs/hwui/tests/TestContext.cpp b/libs/hwui/tests/TestContext.cpp
index 35e402d..542bbae 100644
--- a/libs/hwui/tests/TestContext.cpp
+++ b/libs/hwui/tests/TestContext.cpp
@@ -16,30 +16,59 @@
#include "TestContext.h"
-#include <gui/ISurfaceComposer.h>
-#include <gui/SurfaceComposerClient.h>
+namespace android {
+namespace uirenderer {
+namespace test {
-using namespace android;
+static const int IDENT_DISPLAYEVENT = 1;
-DisplayInfo gDisplay;
-sp<SurfaceComposerClient> gSession;
-
-void createTestEnvironment() {
- gSession = new SurfaceComposerClient();
+static DisplayInfo getBuiltInDisplay() {
+ DisplayInfo display;
sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(
- ISurfaceComposer::eDisplayIdMain));
- status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &gDisplay);
+ ISurfaceComposer::eDisplayIdMain));
+ status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &display);
LOG_ALWAYS_FATAL_IF(status, "Failed to get display info\n");
+ return display;
+}
+
+android::DisplayInfo gDisplay = getBuiltInDisplay();
+
+TestContext::TestContext() {
+ mLooper = new Looper(true);
+ mSurfaceComposerClient = new SurfaceComposerClient();
+ mLooper->addFd(mDisplayEventReceiver.getFd(), IDENT_DISPLAYEVENT,
+ Looper::EVENT_INPUT, nullptr, nullptr);
}
-sp<SurfaceControl> createWindow(int width, int height) {
- sp<SurfaceControl> control = gSession->createSurface(String8("HwuiTest"),
- width, height, PIXEL_FORMAT_RGBX_8888);
+TestContext::~TestContext() {}
+
+sp<Surface> TestContext::surface() {
+ if (!mSurfaceControl.get()) {
+ mSurfaceControl = mSurfaceComposerClient->createSurface(String8("HwuiTest"),
+ gDisplay.w, gDisplay.h, PIXEL_FORMAT_RGBX_8888);
- SurfaceComposerClient::openGlobalTransaction();
- control->setLayer(0x7FFFFFF);
- control->show();
- SurfaceComposerClient::closeGlobalTransaction();
+ SurfaceComposerClient::openGlobalTransaction();
+ mSurfaceControl->setLayer(0x7FFFFFF);
+ mSurfaceControl->show();
+ SurfaceComposerClient::closeGlobalTransaction();
+ }
- return control;
+ return mSurfaceControl->getSurface();
}
+
+void TestContext::waitForVsync() {
+ // Request vsync
+ mDisplayEventReceiver.requestNextVsync();
+
+ // Wait
+ mLooper->pollOnce(-1);
+
+ // Drain it
+ DisplayEventReceiver::Event buf[100];
+ while (mDisplayEventReceiver.getEvents(buf, 100) > 0) { }
+}
+
+} // namespace test
+} // namespace uirenderer
+} // namespace android
+
diff --git a/libs/hwui/tests/TestContext.h b/libs/hwui/tests/TestContext.h
index 8a5d530..7b30fc1 100644
--- a/libs/hwui/tests/TestContext.h
+++ b/libs/hwui/tests/TestContext.h
@@ -17,17 +17,39 @@
#ifndef TESTCONTEXT_H
#define TESTCONTEXT_H
-#include <ui/DisplayInfo.h>
+#include <gui/DisplayEventReceiver.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/SurfaceComposerClient.h>
#include <gui/SurfaceControl.h>
+#include <gui/Surface.h>
+#include <ui/DisplayInfo.h>
+#include <utils/Looper.h>
+
+namespace android {
+namespace uirenderer {
+namespace test {
+
+extern DisplayInfo gDisplay;
+#define dp(x) ((x) * android::uirenderer::test::gDisplay.density)
+
+class TestContext {
+public:
+ TestContext();
+ ~TestContext();
+
+ sp<Surface> surface();
-extern android::DisplayInfo gDisplay;
-#define dp(x) ((x) * gDisplay.density)
+ void waitForVsync();
-// Initializes all the static globals that are shared across all contexts
-// such as display info
-void createTestEnvironment();
+private:
+ sp<SurfaceComposerClient> mSurfaceComposerClient;
+ sp<SurfaceControl> mSurfaceControl;
+ DisplayEventReceiver mDisplayEventReceiver;
+ sp<Looper> mLooper;
+};
-// Defaults to fullscreen
-android::sp<android::SurfaceControl> createWindow(int width = -1, int height = -1);
+} // namespace test
+} // namespace uirenderer
+} // namespace android
#endif
diff --git a/libs/hwui/tests/main.cpp b/libs/hwui/tests/main.cpp
index 2d99e9f..61ad082 100644
--- a/libs/hwui/tests/main.cpp
+++ b/libs/hwui/tests/main.cpp
@@ -21,111 +21,243 @@
#include <ui/PixelFormat.h>
#include <AnimationContext.h>
-#include <DisplayListRenderer.h>
+#include <DisplayListCanvas.h>
#include <RenderNode.h>
#include <renderthread/RenderProxy.h>
+#include <renderthread/RenderTask.h>
#include "TestContext.h"
using namespace android;
using namespace android::uirenderer;
using namespace android::uirenderer::renderthread;
+using namespace android::uirenderer::test;
class ContextFactory : public IContextFactory {
public:
- virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) {
+ virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
return new AnimationContext(clock);
}
};
-static DisplayListRenderer* startRecording(RenderNode* node) {
- DisplayListRenderer* renderer = new DisplayListRenderer();
- renderer->setViewport(node->getWidth(), node->getHeight());
- renderer->prepare(false);
+static DisplayListCanvas* startRecording(RenderNode* node) {
+ DisplayListCanvas* renderer = new DisplayListCanvas();
+ renderer->setViewport(node->stagingProperties().getWidth(),
+ node->stagingProperties().getHeight());
+ renderer->prepare();
return renderer;
}
-static void endRecording(DisplayListRenderer* renderer, RenderNode* node) {
+static void endRecording(DisplayListCanvas* renderer, RenderNode* node) {
renderer->finish();
node->setStagingDisplayList(renderer->finishRecording());
delete renderer;
}
-sp<RenderNode> createCard(int x, int y, int width, int height) {
- sp<RenderNode> node = new RenderNode();
- node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
- node->mutateStagingProperties().setElevation(dp(16));
- node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1);
- node->mutateStagingProperties().mutableOutline().setShouldClip(true);
- node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
+class TreeContentAnimation {
+public:
+ virtual ~TreeContentAnimation() {}
+ virtual int getFrameCount() { return 150; }
+ virtual void createContent(int width, int height, DisplayListCanvas* renderer) = 0;
+ virtual void doFrame(int frameNr) = 0;
- DisplayListRenderer* renderer = startRecording(node.get());
- renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
- endRecording(renderer, node.get());
+ template <class T>
+ static void run() {
+ T animation;
- return node;
-}
+ TestContext testContext;
-int main(int argc, char* argv[]) {
- createTestEnvironment();
-
- // create the native surface
- const int width = gDisplay.w;
- const int height = gDisplay.h;
- sp<SurfaceControl> control = createWindow(width, height);
- sp<Surface> surface = control->getSurface();
-
- RenderNode* rootNode = new RenderNode();
- rootNode->incStrong(0);
- rootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, width, height);
- rootNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- rootNode->mutateStagingProperties().setClipToBounds(false);
- rootNode->setPropertyFieldsDirty(RenderNode::GENERIC);
-
- ContextFactory factory;
- RenderProxy* proxy = new RenderProxy(false, rootNode, &factory);
- proxy->loadSystemProperties();
- proxy->initialize(surface);
- float lightX = width / 2.0;
- proxy->setup(width, height, (Vector3){lightX, dp(-200.0f), dp(800.0f)},
- dp(800.0f), 255 * 0.075, 255 * 0.15);
-
- android::uirenderer::Rect DUMMY;
+ // create the native surface
+ const int width = gDisplay.w;
+ const int height = gDisplay.h;
+ sp<Surface> surface = testContext.surface();
- std::vector< sp<RenderNode> > cards;
+ RenderNode* rootNode = new RenderNode();
+ rootNode->incStrong(nullptr);
+ rootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, width, height);
+ rootNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ rootNode->mutateStagingProperties().setClipToBounds(false);
+ rootNode->setPropertyFieldsDirty(RenderNode::GENERIC);
+
+ ContextFactory factory;
+ std::unique_ptr<RenderProxy> proxy(new RenderProxy(false, rootNode, &factory));
+ proxy->loadSystemProperties();
+ proxy->initialize(surface);
+ float lightX = width / 2.0;
+ proxy->setup(width, height, (Vector3){lightX, dp(-200.0f), dp(800.0f)},
+ dp(800.0f), 255 * 0.075, 255 * 0.15);
+
+ android::uirenderer::Rect DUMMY;
+
+ DisplayListCanvas* renderer = startRecording(rootNode);
+ animation.createContent(width, height, renderer);
+ endRecording(renderer, rootNode);
- DisplayListRenderer* renderer = startRecording(rootNode);
- renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
- renderer->insertReorderBarrier(true);
+ for (int i = 0; i < animation.getFrameCount(); i++) {
+#if !HWUI_NULL_GPU
+ testContext.waitForVsync();
+#endif
- for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
- for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
- sp<RenderNode> card = createCard(x, y, dp(100), dp(100));
- renderer->drawRenderNode(card.get(), DUMMY, 0);
- cards.push_back(card);
+ ATRACE_NAME("UI-Draw Frame");
+ animation.doFrame(i);
+ proxy->syncAndDrawFrame();
}
+
+ rootNode->decStrong(nullptr);
}
+};
+
+class ShadowGridAnimation : public TreeContentAnimation {
+public:
+ std::vector< sp<RenderNode> > cards;
+ void createContent(int width, int height, DisplayListCanvas* renderer) override {
+ android::uirenderer::Rect DUMMY;
+
+ renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ renderer->insertReorderBarrier(true);
- renderer->insertReorderBarrier(false);
- endRecording(renderer, rootNode);
+ for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
+ for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
+ sp<RenderNode> card = createCard(x, y, dp(100), dp(100));
+ renderer->drawRenderNode(card.get(), DUMMY, 0);
+ cards.push_back(card);
+ }
+ }
- for (int i = 0; i < 150; i++) {
- ATRACE_NAME("UI-Draw Frame");
- for (int ci = 0; ci < cards.size(); ci++) {
- cards[ci]->mutateStagingProperties().setTranslationX(i);
- cards[ci]->mutateStagingProperties().setTranslationY(i);
+ renderer->insertReorderBarrier(false);
+ }
+ void doFrame(int frameNr) override {
+ for (size_t ci = 0; ci < cards.size(); ci++) {
+ cards[ci]->mutateStagingProperties().setTranslationX(frameNr);
+ cards[ci]->mutateStagingProperties().setTranslationY(frameNr);
cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
}
- nsecs_t frameTimeNs = systemTime(CLOCK_MONOTONIC);
- proxy->syncAndDrawFrame(frameTimeNs, 0, gDisplay.density);
- usleep(12000);
}
+private:
+ sp<RenderNode> createCard(int x, int y, int width, int height) {
+ sp<RenderNode> node = new RenderNode();
+ node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+ node->mutateStagingProperties().setElevation(dp(16));
+ node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1);
+ node->mutateStagingProperties().mutableOutline().setShouldClip(true);
+ node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
+
+ DisplayListCanvas* renderer = startRecording(node.get());
+ renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
+ endRecording(renderer, node.get());
+ return node;
+ }
+};
+
+class RectGridAnimation : public TreeContentAnimation {
+public:
+ sp<RenderNode> card;
+ void createContent(int width, int height, DisplayListCanvas* renderer) override {
+ android::uirenderer::Rect DUMMY;
- sleep(5);
+ renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ renderer->insertReorderBarrier(true);
- delete proxy;
- rootNode->decStrong(0);
+ card = createCard(40, 40, 200, 200);
+ renderer->drawRenderNode(card.get(), DUMMY, 0);
+
+ renderer->insertReorderBarrier(false);
+ }
+ void doFrame(int frameNr) override {
+ card->mutateStagingProperties().setTranslationX(frameNr);
+ card->mutateStagingProperties().setTranslationY(frameNr);
+ card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+private:
+ sp<RenderNode> createCard(int x, int y, int width, int height) {
+ sp<RenderNode> node = new RenderNode();
+ node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+ node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+
+ DisplayListCanvas* renderer = startRecording(node.get());
+ renderer->drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode);
+
+ float rects[width * height];
+ int index = 0;
+ for (int xOffset = 0; xOffset < width; xOffset+=2) {
+ for (int yOffset = 0; yOffset < height; yOffset+=2) {
+ rects[index++] = xOffset;
+ rects[index++] = yOffset;
+ rects[index++] = xOffset + 1;
+ rects[index++] = yOffset + 1;
+ }
+ }
+ int count = width * height;
+ SkPaint paint;
+ paint.setColor(0xff00ffff);
+ renderer->drawRects(rects, count, &paint);
+
+ endRecording(renderer, node.get());
+ return node;
+ }
+};
+
+class OvalAnimation : public TreeContentAnimation {
+public:
+ sp<RenderNode> card;
+ void createContent(int width, int height, DisplayListCanvas* renderer) override {
+ android::uirenderer::Rect DUMMY;
+
+ renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ renderer->insertReorderBarrier(true);
+
+ card = createCard(40, 40, 400, 400);
+ renderer->drawRenderNode(card.get(), DUMMY, 0);
+
+ renderer->insertReorderBarrier(false);
+ }
+
+ void doFrame(int frameNr) override {
+ card->mutateStagingProperties().setTranslationX(frameNr);
+ card->mutateStagingProperties().setTranslationY(frameNr);
+ card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+private:
+ sp<RenderNode> createCard(int x, int y, int width, int height) {
+ sp<RenderNode> node = new RenderNode();
+ node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+ node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+
+ DisplayListCanvas* renderer = startRecording(node.get());
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setColor(0xFF000000);
+ renderer->drawOval(0, 0, width, height, paint);
+
+ endRecording(renderer, node.get());
+ return node;
+ }
+};
+
+struct cstr_cmp {
+ bool operator()(const char *a, const char *b) const {
+ return std::strcmp(a, b) < 0;
+ }
+};
+
+typedef void (*testProc)();
+
+std::map<const char*, testProc, cstr_cmp> gTestMap {
+ {"shadowgrid", TreeContentAnimation::run<ShadowGridAnimation>},
+ {"rectgrid", TreeContentAnimation::run<RectGridAnimation> },
+ {"oval", TreeContentAnimation::run<OvalAnimation> },
+};
+
+int main(int argc, char* argv[]) {
+ const char* testName = argc > 1 ? argv[1] : "shadowgrid";
+ testProc proc = gTestMap[testName];
+ if(!proc) {
+ printf("Error: couldn't find test %s\n", testName);
+ return 1;
+ }
+ proc();
printf("Success!\n");
return 0;
}
diff --git a/libs/hwui/tests/nullegl.cpp b/libs/hwui/tests/nullegl.cpp
new file mode 100644
index 0000000..b6cc2f2
--- /dev/null
+++ b/libs/hwui/tests/nullegl.cpp
@@ -0,0 +1,160 @@
+/*
+ * 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 <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+
+static EGLDisplay gDisplay = (EGLDisplay) 1;
+
+typedef struct {
+ EGLSurface surface;
+ EGLContext context;
+} ThreadState;
+
+static pthread_key_t ThreadStateKey;
+static pthread_once_t ThreadStateSetupOnce = PTHREAD_ONCE_INIT;
+
+static void destroyThreadState(void* state) {
+ free(state);
+}
+
+static void makeThreadState() {
+ pthread_key_create(&ThreadStateKey, destroyThreadState);
+}
+
+ThreadState* getThreadState() {
+ ThreadState* ptr;
+ pthread_once(&ThreadStateSetupOnce, makeThreadState);
+ if ((ptr = (ThreadState*) pthread_getspecific(ThreadStateKey)) == NULL) {
+ ptr = (ThreadState*) calloc(1, sizeof(ThreadState));
+ ptr->context = EGL_NO_CONTEXT;
+ ptr->surface = EGL_NO_SURFACE;
+ pthread_setspecific(ThreadStateKey, ptr);
+ }
+ return ptr;
+}
+
+EGLint eglGetError(void) {
+ return EGL_SUCCESS;
+}
+
+EGLDisplay eglGetDisplay(EGLNativeDisplayType display_id) {
+ return gDisplay;
+}
+
+EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor) {
+ return EGL_TRUE;
+}
+
+EGLBoolean eglTerminate(EGLDisplay dpy) {
+ return EGL_TRUE;
+}
+
+const char * eglQueryString(EGLDisplay dpy, EGLint name) {
+ return "";
+}
+
+EGLBoolean eglChooseConfig(EGLDisplay dpy, const EGLint *attrib_list,
+ EGLConfig *configs, EGLint config_size,
+ EGLint *num_config) {
+ memset(configs, 9, sizeof(EGLConfig) * config_size);
+ *num_config = config_size;
+ return EGL_TRUE;
+}
+
+EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config,
+ EGLNativeWindowType win,
+ const EGLint *attrib_list) {
+ return (EGLSurface) malloc(sizeof(void*));
+}
+
+EGLSurface eglCreatePbufferSurface(EGLDisplay dpy, EGLConfig config,
+ const EGLint *attrib_list) {
+ return (EGLSurface) malloc(sizeof(void*));
+}
+
+EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface) {
+ free(surface);
+ return EGL_TRUE;
+}
+
+EGLBoolean eglQuerySurface(EGLDisplay dpy, EGLSurface surface,
+ EGLint attribute, EGLint *value) {
+ *value = 1000;
+ return EGL_TRUE;
+}
+
+EGLBoolean eglReleaseThread(void) {
+ return EGL_TRUE;
+}
+
+EGLBoolean eglSurfaceAttrib(EGLDisplay dpy, EGLSurface surface,
+ EGLint attribute, EGLint value) {
+ return EGL_TRUE;
+}
+
+EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval) {
+ return EGL_TRUE;
+}
+
+EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config,
+ EGLContext share_context,
+ const EGLint *attrib_list) {
+ return (EGLContext) malloc(sizeof(void*));
+}
+EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx) {
+ free(ctx);
+ return EGL_TRUE;
+}
+
+EGLBoolean eglMakeCurrent(EGLDisplay dpy, EGLSurface draw,
+ EGLSurface read, EGLContext ctx) {
+ ThreadState* state = getThreadState();
+ state->surface = draw;
+ state->context = ctx;
+ return EGL_TRUE;
+}
+
+EGLContext eglGetCurrentContext(void) {
+ return getThreadState()->context;
+}
+
+EGLSurface eglGetCurrentSurface(EGLint readdraw) {
+ return getThreadState()->surface;
+}
+
+EGLDisplay eglGetCurrentDisplay(void) {
+ return gDisplay;
+}
+
+EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface) {
+ return EGL_TRUE;
+}
+
+EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list) {
+ return (EGLImageKHR) malloc(sizeof(EGLImageKHR));
+}
+
+EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR image) {
+ free(image);
+ return EGL_TRUE;
+}
+
+void eglBeginFrame(EGLDisplay dpy, EGLSurface surface) {}
diff --git a/libs/hwui/tests/nullgles.cpp b/libs/hwui/tests/nullgles.cpp
new file mode 100644
index 0000000..8ca7598
--- /dev/null
+++ b/libs/hwui/tests/nullgles.cpp
@@ -0,0 +1,275 @@
+/*
+ * 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 <GLES3/gl3.h>
+#include <GLES2/gl2ext.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+struct {
+ GLboolean scissorEnabled;
+} gState;
+
+void glGenCommon(GLsizei n, GLuint *buffers) {
+ static GLuint nextId = 0;
+ int i;
+ for(i = 0; i < n; i++) {
+ buffers[i] = ++nextId;
+ }
+}
+
+void glGenBuffers(GLsizei n, GLuint *buffers) {
+ glGenCommon(n, buffers);
+}
+
+void glGenFramebuffers(GLsizei n, GLuint *framebuffers) {
+ glGenCommon(n, framebuffers);
+}
+
+void glGenRenderbuffers(GLsizei n, GLuint *renderbuffers) {
+ glGenCommon(n, renderbuffers);
+}
+
+void glGenTextures(GLsizei n, GLuint *textures) {
+ glGenCommon(n, textures);
+}
+
+GLuint glCreateProgram(void) {
+ static GLuint nextProgram = 0;
+ return ++nextProgram;
+}
+
+GLuint glCreateShader(GLenum type) {
+ static GLuint nextShader = 0;
+ return ++nextShader;
+}
+
+void glGetProgramiv(GLuint program, GLenum pname, GLint *params) {
+ switch (pname) {
+ case GL_DELETE_STATUS:
+ case GL_LINK_STATUS:
+ case GL_VALIDATE_STATUS:
+ *params = GL_TRUE;
+ break;
+ case GL_INFO_LOG_LENGTH:
+ *params = 16;
+ break;
+ }
+}
+
+void glGetProgramInfoLog(GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog) {
+ *length = snprintf(infoLog, bufSize, "success");
+ if (*length >= bufSize) {
+ *length = bufSize - 1;
+ }
+}
+
+void glGetShaderiv(GLuint shader, GLenum pname, GLint *params) {
+ switch (pname) {
+ case GL_COMPILE_STATUS:
+ case GL_DELETE_STATUS:
+ *params = GL_TRUE;
+ }
+}
+
+void glGetShaderInfoLog(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog) {
+ *length = snprintf(infoLog, bufSize, "success");
+ if (*length >= bufSize) {
+ *length = bufSize - 1;
+ }
+}
+
+void setBooleanState(GLenum cap, GLboolean value) {
+ switch (cap) {
+ case GL_SCISSOR_TEST:
+ gState.scissorEnabled = value;
+ break;
+ }
+}
+
+void glEnable(GLenum cap) {
+ setBooleanState(cap, GL_TRUE);
+}
+
+void glDisable(GLenum cap) {
+ setBooleanState(cap, GL_FALSE);
+}
+
+GLboolean glIsEnabled(GLenum cap) {
+ switch (cap) {
+ case GL_SCISSOR_TEST:
+ return gState.scissorEnabled;
+ default:
+ return GL_FALSE;
+ }
+}
+
+void glGetIntegerv(GLenum pname, GLint *data) {
+ switch (pname) {
+ case GL_MAX_TEXTURE_SIZE:
+ *data = 2048;
+ break;
+ case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS:
+ *data = 4;
+ break;
+ default:
+ *data = 0;
+ }
+}
+
+const char* getString(GLenum name) {
+ switch (name) {
+ case GL_VENDOR:
+ return "android";
+ case GL_RENDERER:
+ return "null";
+ case GL_VERSION:
+ return "OpenGL ES 2.0 rev1";
+ case GL_SHADING_LANGUAGE_VERSION:
+ return "OpenGL ES GLSL ES 2.0 rev1";
+ case GL_EXTENSIONS:
+ default:
+ return "";
+ }
+}
+
+const GLubyte* glGetString(GLenum name) {
+ return (GLubyte*) getString(name);
+}
+
+void glActiveTexture(GLenum texture) {}
+void glAttachShader(GLuint program, GLuint shader) {}
+void glBindAttribLocation(GLuint program, GLuint index, const GLchar *name) {}
+void glBindBuffer(GLenum target, GLuint buffer) {}
+void glBindFramebuffer(GLenum target, GLuint framebuffer) {}
+void glBindRenderbuffer(GLenum target, GLuint renderbuffer) {}
+void glBindTexture(GLenum target, GLuint texture) {}
+void glBlendColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) {}
+void glBlendEquation(GLenum mode) {}
+void glBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha) {}
+void glBlendFunc(GLenum sfactor, GLenum dfactor) {}
+void glBlendFuncSeparate(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha) {}
+void glBufferData(GLenum target, GLsizeiptr size, const void *data, GLenum usage) {}
+void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const void *data) {}
+void glClear(GLbitfield mask) {}
+void glClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) {}
+void glClearDepthf(GLfloat d) {}
+void glClearStencil(GLint s) {}
+void glColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) {}
+void glCompileShader(GLuint shader) {}
+void glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data) {}
+void glCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data) {}
+void glCopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) {}
+void glCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) {}
+void glCullFace(GLenum mode) {}
+void glDeleteBuffers(GLsizei n, const GLuint *buffers) {}
+void glDeleteFramebuffers(GLsizei n, const GLuint *framebuffers) {}
+void glDeleteProgram(GLuint program) {}
+void glDeleteRenderbuffers(GLsizei n, const GLuint *renderbuffers) {}
+void glDeleteShader(GLuint shader) {}
+void glDeleteTextures(GLsizei n, const GLuint *textures) {}
+void glDepthFunc(GLenum func) {}
+void glDepthMask(GLboolean flag) {}
+void glDepthRangef(GLfloat n, GLfloat f) {}
+void glDetachShader(GLuint program, GLuint shader) {}
+void glDisableVertexAttribArray(GLuint index) {}
+void glDrawArrays(GLenum mode, GLint first, GLsizei count) {}
+void glDrawElements(GLenum mode, GLsizei count, GLenum type, const void *indices) {}
+void glEnableVertexAttribArray(GLuint index) {}
+void glFinish(void) {}
+void glFlush(void) {}
+void glFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) {}
+void glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) {}
+void glFrontFace(GLenum mode) {}
+void glGenerateMipmap(GLenum target) {}
+GLint glGetAttribLocation(GLuint program, const GLchar *name) { return 1; }
+GLenum glGetError(void) { return GL_NO_ERROR; }
+GLint glGetUniformLocation(GLuint program, const GLchar *name) { return 2; }
+void glHint(GLenum target, GLenum mode) {}
+void glLineWidth(GLfloat width) {}
+void glLinkProgram(GLuint program) {}
+void glPixelStorei(GLenum pname, GLint param) {}
+void glPolygonOffset(GLfloat factor, GLfloat units) {}
+void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels) {}
+void glReleaseShaderCompiler(void) {}
+void glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) {}
+void glSampleCoverage(GLfloat value, GLboolean invert) {}
+void glScissor(GLint x, GLint y, GLsizei width, GLsizei height) {}
+void glShaderBinary(GLsizei count, const GLuint *shaders, GLenum binaryformat, const void *binary, GLsizei length) {}
+void glShaderSource(GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length) {}
+void glStencilFunc(GLenum func, GLint ref, GLuint mask) {}
+void glStencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask) {}
+void glStencilMask(GLuint mask) {}
+void glStencilMaskSeparate(GLenum face, GLuint mask) {}
+void glStencilOp(GLenum fail, GLenum zfail, GLenum zpass) {}
+void glStencilOpSeparate(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass) {}
+void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels) {}
+void glTexParameterf(GLenum target, GLenum pname, GLfloat param) {}
+void glTexParameterfv(GLenum target, GLenum pname, const GLfloat *params) {}
+void glTexParameteri(GLenum target, GLenum pname, GLint param) {}
+void glTexParameteriv(GLenum target, GLenum pname, const GLint *params) {}
+void glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels) {}
+void glUniform1f(GLint location, GLfloat v0) {}
+void glUniform1fv(GLint location, GLsizei count, const GLfloat *value) {}
+void glUniform1i(GLint location, GLint v0) {}
+void glUniform1iv(GLint location, GLsizei count, const GLint *value) {}
+void glUniform2f(GLint location, GLfloat v0, GLfloat v1) {}
+void glUniform2fv(GLint location, GLsizei count, const GLfloat *value) {}
+void glUniform2i(GLint location, GLint v0, GLint v1) {}
+void glUniform2iv(GLint location, GLsizei count, const GLint *value) {}
+void glUniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2) {}
+void glUniform3fv(GLint location, GLsizei count, const GLfloat *value) {}
+void glUniform3i(GLint location, GLint v0, GLint v1, GLint v2) {}
+void glUniform3iv(GLint location, GLsizei count, const GLint *value) {}
+void glUniform4f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) {}
+void glUniform4fv(GLint location, GLsizei count, const GLfloat *value) {}
+void glUniform4i(GLint location, GLint v0, GLint v1, GLint v2, GLint v3) {}
+void glUniform4iv(GLint location, GLsizei count, const GLint *value) {}
+void glUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {}
+void glUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {}
+void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {}
+void glUseProgram(GLuint program) {}
+void glValidateProgram(GLuint program) {}
+void glVertexAttrib1f(GLuint index, GLfloat x) {}
+void glVertexAttrib1fv(GLuint index, const GLfloat *v) {}
+void glVertexAttrib2f(GLuint index, GLfloat x, GLfloat y) {}
+void glVertexAttrib2fv(GLuint index, const GLfloat *v) {}
+void glVertexAttrib3f(GLuint index, GLfloat x, GLfloat y, GLfloat z) {}
+void glVertexAttrib3fv(GLuint index, const GLfloat *v) {}
+void glVertexAttrib4f(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w) {}
+void glVertexAttrib4fv(GLuint index, const GLfloat *v) {}
+void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer) {}
+void glViewport(GLint x, GLint y, GLsizei width, GLsizei height) {}
+
+
+// gles2 ext
+void glInsertEventMarkerEXT(GLsizei length, const GLchar *marker) {}
+void glPushGroupMarkerEXT(GLsizei length, const GLchar *marker) {}
+void glPopGroupMarkerEXT(void) {}
+void glDiscardFramebufferEXT(GLenum target, GLsizei numAttachments, const GLenum *attachments) {}
+void glStartTilingQCOM(GLuint x, GLuint y, GLuint width, GLuint height, GLbitfield preserveMask) {}
+void glEndTilingQCOM(GLbitfield preserveMask) {}
+void glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image) {}
+
+// GLES3
+void* glMapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) {
+ return 0;
+}
+
+GLboolean glUnmapBuffer(GLenum target) {
+ return GL_FALSE;
+}
diff --git a/libs/hwui/thread/Signal.h b/libs/hwui/thread/Signal.h
index dcf5449..d4cfeeb 100644
--- a/libs/hwui/thread/Signal.h
+++ b/libs/hwui/thread/Signal.h
@@ -30,8 +30,10 @@ public:
~Signal() { }
void signal() {
- Mutex::Autolock l(mLock);
- mSignaled = true;
+ {
+ Mutex::Autolock l(mLock);
+ mSignaled = true;
+ }
mCondition.signal(mType);
}
diff --git a/libs/hwui/thread/TaskManager.cpp b/libs/hwui/thread/TaskManager.cpp
index cb5401c..f0ed0bb 100644
--- a/libs/hwui/thread/TaskManager.cpp
+++ b/libs/hwui/thread/TaskManager.cpp
@@ -14,10 +14,8 @@
* limitations under the License.
*/
-#include <sys/sysinfo.h>
-#if defined(HAVE_PTHREADS)
#include <sys/resource.h>
-#endif
+#include <sys/sysinfo.h>
#include "TaskManager.h"
#include "Task.h"
@@ -83,9 +81,7 @@ bool TaskManager::addTaskBase(const sp<TaskBase>& task, const sp<TaskProcessorBa
///////////////////////////////////////////////////////////////////////////////
status_t TaskManager::WorkerThread::readyToRun() {
-#if defined(HAVE_PTHREADS)
setpriority(PRIO_PROCESS, 0, PRIORITY_FOREGROUND);
-#endif
return NO_ERROR;
}
@@ -109,10 +105,15 @@ bool TaskManager::WorkerThread::threadLoop() {
bool TaskManager::WorkerThread::addTask(TaskWrapper task) {
if (!isRunning()) {
run(mName.string(), PRIORITY_DEFAULT);
+ } else if (exitPending()) {
+ return false;
}
- Mutex::Autolock l(mLock);
- ssize_t index = mTasks.add(task);
+ ssize_t index;
+ {
+ Mutex::Autolock l(mLock);
+ index = mTasks.add(task);
+ }
mSignal.signal();
return index >= 0;
@@ -124,10 +125,6 @@ size_t TaskManager::WorkerThread::getTaskCount() const {
}
void TaskManager::WorkerThread::exit() {
- {
- Mutex::Autolock l(mLock);
- mTasks.clear();
- }
requestExit();
mSignal.signal();
}
diff --git a/libs/hwui/thread/TaskManager.h b/libs/hwui/thread/TaskManager.h
index 5a933ab..10e8b9e 100644
--- a/libs/hwui/thread/TaskManager.h
+++ b/libs/hwui/thread/TaskManager.h
@@ -84,8 +84,8 @@ private:
void exit();
private:
- virtual status_t readyToRun();
- virtual bool threadLoop();
+ virtual status_t readyToRun() override;
+ virtual bool threadLoop() override;
// Lock for the list of tasks
mutable Mutex mLock;
diff --git a/libs/hwui/thread/TaskProcessor.h b/libs/hwui/thread/TaskProcessor.h
index d1269f0..82538e9 100644
--- a/libs/hwui/thread/TaskProcessor.h
+++ b/libs/hwui/thread/TaskProcessor.h
@@ -30,9 +30,6 @@ public:
TaskProcessorBase() { }
virtual ~TaskProcessorBase() { };
-private:
- friend class TaskManager;
-
virtual void process(const sp<TaskBase>& task) = 0;
};
@@ -42,12 +39,19 @@ public:
TaskProcessor(TaskManager* manager): mManager(manager) { }
virtual ~TaskProcessor() { }
- bool add(const sp<Task<T> >& task);
+ void add(const sp<Task<T> >& task) {
+ if (!addImpl(task)) {
+ // fall back to immediate execution
+ process(task);
+ }
+ }
virtual void onProcess(const sp<Task<T> >& task) = 0;
private:
- virtual void process(const sp<TaskBase>& task) {
+ bool addImpl(const sp<Task<T> >& task);
+
+ virtual void process(const sp<TaskBase>& task) override {
sp<Task<T> > realTask = static_cast<Task<T>* >(task.get());
// This is the right way to do it but sp<> doesn't play nice
// sp<Task<T> > realTask = static_cast<sp<Task<T> > >(task);
@@ -58,7 +62,7 @@ private:
};
template<typename T>
-bool TaskProcessor<T>::add(const sp<Task<T> >& task) {
+bool TaskProcessor<T>::addImpl(const sp<Task<T> >& task) {
if (mManager) {
sp<TaskProcessor<T> > self(this);
return mManager->addTask(task, self);
diff --git a/libs/hwui/unit_tests/Android.mk b/libs/hwui/unit_tests/Android.mk
new file mode 100644
index 0000000..51601b0
--- /dev/null
+++ b/libs/hwui/unit_tests/Android.mk
@@ -0,0 +1,34 @@
+#
+# 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.
+#
+
+local_target_dir := $(TARGET_OUT_DATA)/local/tmp
+LOCAL_PATH:= $(call my-dir)/..
+
+include $(CLEAR_VARS)
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.common.mk
+LOCAL_MODULE := hwui_unit_tests
+LOCAL_MODULE_TAGS := tests
+
+include $(LOCAL_PATH)/Android.common.mk
+
+LOCAL_SRC_FILES += \
+ unit_tests/ClipAreaTests.cpp \
+ unit_tests/LinearAllocatorTests.cpp \
+ unit_tests/main.cpp
+
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libs/hwui/unit_tests/ClipAreaTests.cpp b/libs/hwui/unit_tests/ClipAreaTests.cpp
new file mode 100644
index 0000000..166d5b6
--- /dev/null
+++ b/libs/hwui/unit_tests/ClipAreaTests.cpp
@@ -0,0 +1,116 @@
+/*
+ * 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 <gtest/gtest.h>
+#include <SkPath.h>
+#include <SkRegion.h>
+
+#include "ClipArea.h"
+
+#include "Matrix.h"
+#include "Rect.h"
+#include "utils/LinearAllocator.h"
+
+namespace android {
+namespace uirenderer {
+
+static Rect kViewportBounds(0, 0, 2048, 2048);
+
+static ClipArea createClipArea() {
+ ClipArea area;
+ area.setViewportDimensions(kViewportBounds.getWidth(), kViewportBounds.getHeight());
+ return area;
+}
+
+TEST(TransformedRectangle, basics) {
+ Rect r(0, 0, 100, 100);
+ Matrix4 minus90;
+ minus90.loadRotate(-90);
+ minus90.mapRect(r);
+ Rect r2(20, 40, 120, 60);
+
+ Matrix4 m90;
+ m90.loadRotate(90);
+ TransformedRectangle tr(r, m90);
+ EXPECT_TRUE(tr.canSimplyIntersectWith(tr));
+
+ Matrix4 m0;
+ TransformedRectangle tr0(r2, m0);
+ EXPECT_FALSE(tr.canSimplyIntersectWith(tr0));
+
+ Matrix4 m45;
+ m45.loadRotate(45);
+ TransformedRectangle tr2(r, m45);
+ EXPECT_FALSE(tr2.canSimplyIntersectWith(tr));
+}
+
+TEST(RectangleList, basics) {
+ RectangleList list;
+ EXPECT_TRUE(list.isEmpty());
+
+ Rect r(0, 0, 100, 100);
+ Matrix4 m45;
+ m45.loadRotate(45);
+ list.set(r, m45);
+ EXPECT_FALSE(list.isEmpty());
+
+ Rect r2(20, 20, 200, 200);
+ list.intersectWith(r2, m45);
+ EXPECT_FALSE(list.isEmpty());
+ EXPECT_EQ(1, list.getTransformedRectanglesCount());
+
+ Rect r3(20, 20, 200, 200);
+ Matrix4 m30;
+ m30.loadRotate(30);
+ list.intersectWith(r2, m30);
+ EXPECT_FALSE(list.isEmpty());
+ EXPECT_EQ(2, list.getTransformedRectanglesCount());
+
+ SkRegion clip;
+ clip.setRect(0, 0, 2000, 2000);
+ SkRegion rgn(list.convertToRegion(clip));
+ EXPECT_FALSE(rgn.isEmpty());
+}
+
+TEST(ClipArea, basics) {
+ ClipArea area(createClipArea());
+ EXPECT_FALSE(area.isEmpty());
+}
+
+TEST(ClipArea, paths) {
+ ClipArea area(createClipArea());
+ Matrix4 transform;
+ transform.loadIdentity();
+ SkPath path;
+ SkScalar r = 100;
+ path.addCircle(r, r, r);
+ area.clipPathWithTransform(path, &transform, SkRegion::kIntersect_Op);
+ EXPECT_FALSE(area.isEmpty());
+ EXPECT_FALSE(area.isSimple());
+ EXPECT_FALSE(area.isRectangleList());
+ Rect clipRect(area.getClipRect());
+ clipRect.dump("clipRect");
+ Rect expected(0, 0, r * 2, r * 2);
+ expected.dump("expected");
+ EXPECT_EQ(expected, clipRect);
+ SkRegion clipRegion(area.getClipRegion());
+ auto skRect(clipRegion.getBounds());
+ Rect regionBounds;
+ regionBounds.set(skRect);
+ EXPECT_EQ(expected, regionBounds);
+}
+}
+}
diff --git a/libs/hwui/unit_tests/LinearAllocatorTests.cpp b/libs/hwui/unit_tests/LinearAllocatorTests.cpp
new file mode 100644
index 0000000..b3959d1
--- /dev/null
+++ b/libs/hwui/unit_tests/LinearAllocatorTests.cpp
@@ -0,0 +1,108 @@
+/*
+ * 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 <gtest/gtest.h>
+#include <utils/LinearAllocator.h>
+
+using namespace android;
+using namespace android::uirenderer;
+
+struct SimplePair {
+ int one = 1;
+ int two = 2;
+};
+
+class SignalingDtor {
+public:
+ SignalingDtor() {
+ mDestroyed = nullptr;
+ }
+ SignalingDtor(bool* destroyedSignal) {
+ mDestroyed = destroyedSignal;
+ *mDestroyed = false;
+ }
+ virtual ~SignalingDtor() {
+ if (mDestroyed) {
+ *mDestroyed = true;
+ }
+ }
+ void setSignal(bool* destroyedSignal) {
+ mDestroyed = destroyedSignal;
+ }
+private:
+ bool* mDestroyed;
+};
+
+TEST(LinearAllocator, alloc) {
+ LinearAllocator la;
+ EXPECT_EQ(0u, la.usedSize());
+ la.alloc(64);
+ // There's some internal tracking as well as padding
+ // so the usedSize isn't strictly defined
+ EXPECT_LE(64u, la.usedSize());
+ EXPECT_GT(80u, la.usedSize());
+ auto pair = la.alloc<SimplePair>();
+ EXPECT_LE(64u + sizeof(SimplePair), la.usedSize());
+ EXPECT_GT(80u + sizeof(SimplePair), la.usedSize());
+ EXPECT_EQ(1, pair->one);
+ EXPECT_EQ(2, pair->two);
+}
+
+TEST(LinearAllocator, dtor) {
+ bool destroyed[10];
+ {
+ LinearAllocator la;
+ for (int i = 0; i < 5; i++) {
+ la.alloc<SignalingDtor>()->setSignal(destroyed + i);
+ la.alloc<SimplePair>();
+ }
+ la.alloc(100);
+ for (int i = 0; i < 5; i++) {
+ auto sd = new (la) SignalingDtor(destroyed + 5 + i);
+ la.autoDestroy(sd);
+ new (la) SimplePair();
+ }
+ la.alloc(100);
+ for (int i = 0; i < 10; i++) {
+ EXPECT_FALSE(destroyed[i]);
+ }
+ }
+ for (int i = 0; i < 10; i++) {
+ EXPECT_TRUE(destroyed[i]);
+ }
+}
+
+TEST(LinearAllocator, rewind) {
+ bool destroyed;
+ {
+ LinearAllocator la;
+ auto addr = la.alloc(100);
+ EXPECT_LE(100u, la.usedSize());
+ la.rewindIfLastAlloc(addr, 100);
+ EXPECT_GT(16u, la.usedSize());
+ size_t emptySize = la.usedSize();
+ auto sigdtor = la.alloc<SignalingDtor>();
+ sigdtor->setSignal(&destroyed);
+ EXPECT_FALSE(destroyed);
+ EXPECT_LE(emptySize, la.usedSize());
+ la.rewindIfLastAlloc(sigdtor);
+ EXPECT_TRUE(destroyed);
+ EXPECT_EQ(emptySize, la.usedSize());
+ destroyed = false;
+ }
+ // Checking for a double-destroy case
+ EXPECT_EQ(destroyed, false);
+}
diff --git a/libs/hwui/unit_tests/how_to_run.txt b/libs/hwui/unit_tests/how_to_run.txt
new file mode 100755
index 0000000..a2d6a34
--- /dev/null
+++ b/libs/hwui/unit_tests/how_to_run.txt
@@ -0,0 +1,4 @@
+mmm -j8 $ANDROID_BUILD_TOP/frameworks/base/libs/hwui/unit_tests &&
+adb push $ANDROID_PRODUCT_OUT/data/nativetest/hwui_unit_tests/hwui_unit_tests \
+ /data/nativetest/hwui_unit_tests/hwui_unit_tests &&
+adb shell /data/nativetest/hwui_unit_tests/hwui_unit_tests
diff --git a/libs/hwui/unit_tests/main.cpp b/libs/hwui/unit_tests/main.cpp
new file mode 100644
index 0000000..c9b9636
--- /dev/null
+++ b/libs/hwui/unit_tests/main.cpp
@@ -0,0 +1,22 @@
+/*
+ * 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 <gtest/gtest.h>
+
+int main(int argc, char **argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/libs/hwui/utils/LinearAllocator.cpp b/libs/hwui/utils/LinearAllocator.cpp
new file mode 100644
index 0000000..59b12cf
--- /dev/null
+++ b/libs/hwui/utils/LinearAllocator.cpp
@@ -0,0 +1,272 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define LOG_NDEBUG 1
+
+#include "utils/LinearAllocator.h"
+
+#include <stdlib.h>
+#include <utils/Log.h>
+
+
+// The ideal size of a page allocation (these need to be multiples of 8)
+#define INITIAL_PAGE_SIZE ((size_t)4096) // 4kb
+#define MAX_PAGE_SIZE ((size_t)131072) // 128kb
+
+// The maximum amount of wasted space we can have per page
+// Allocations exceeding this will have their own dedicated page
+// If this is too low, we will malloc too much
+// Too high, and we may waste too much space
+// Must be smaller than INITIAL_PAGE_SIZE
+#define MAX_WASTE_SIZE ((size_t)1024)
+
+#if ALIGN_DOUBLE
+#define ALIGN_SZ (sizeof(double))
+#else
+#define ALIGN_SZ (sizeof(int))
+#endif
+
+#define ALIGN(x) ((x + ALIGN_SZ - 1 ) & ~(ALIGN_SZ - 1))
+#define ALIGN_PTR(p) ((void*)(ALIGN((size_t)p)))
+
+#if LOG_NDEBUG
+#define ADD_ALLOCATION(size)
+#define RM_ALLOCATION(size)
+#else
+#include <utils/Thread.h>
+#include <utils/Timers.h>
+static size_t s_totalAllocations = 0;
+static nsecs_t s_nextLog = 0;
+static android::Mutex s_mutex;
+
+static void _logUsageLocked() {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ if (now > s_nextLog) {
+ s_nextLog = now + milliseconds_to_nanoseconds(10);
+ ALOGV("Total memory usage: %zu kb", s_totalAllocations / 1024);
+ }
+}
+
+static void _addAllocation(size_t size) {
+ android::AutoMutex lock(s_mutex);
+ s_totalAllocations += size;
+ _logUsageLocked();
+}
+
+#define ADD_ALLOCATION(size) _addAllocation(size);
+#define RM_ALLOCATION(size) _addAllocation(-size);
+#endif
+
+#define min(x,y) (((x) < (y)) ? (x) : (y))
+
+void* operator new(std::size_t size, android::uirenderer::LinearAllocator& la) {
+ return la.alloc(size);
+}
+
+namespace android {
+namespace uirenderer {
+
+class LinearAllocator::Page {
+public:
+ Page* next() { return mNextPage; }
+ void setNext(Page* next) { mNextPage = next; }
+
+ Page()
+ : mNextPage(0)
+ {}
+
+ void* operator new(size_t /*size*/, void* buf) { return buf; }
+
+ void* start() {
+ return (void*) (((size_t)this) + sizeof(Page));
+ }
+
+ void* end(int pageSize) {
+ return (void*) (((size_t)start()) + pageSize);
+ }
+
+private:
+ Page(const Page& /*other*/) {}
+ Page* mNextPage;
+};
+
+LinearAllocator::LinearAllocator()
+ : mPageSize(INITIAL_PAGE_SIZE)
+ , mMaxAllocSize(MAX_WASTE_SIZE)
+ , mNext(0)
+ , mCurrentPage(0)
+ , mPages(0)
+ , mTotalAllocated(0)
+ , mWastedSpace(0)
+ , mPageCount(0)
+ , mDedicatedPageCount(0) {}
+
+LinearAllocator::~LinearAllocator(void) {
+ while (mDtorList) {
+ auto node = mDtorList;
+ mDtorList = node->next;
+ node->dtor(node->addr);
+ }
+ Page* p = mPages;
+ while (p) {
+ Page* next = p->next();
+ p->~Page();
+ free(p);
+ RM_ALLOCATION(mPageSize);
+ p = next;
+ }
+}
+
+void* LinearAllocator::start(Page* p) {
+ return ALIGN_PTR(((size_t*)p) + sizeof(Page));
+}
+
+void* LinearAllocator::end(Page* p) {
+ return ((char*)p) + mPageSize;
+}
+
+bool LinearAllocator::fitsInCurrentPage(size_t size) {
+ return mNext && ((char*)mNext + size) <= end(mCurrentPage);
+}
+
+void LinearAllocator::ensureNext(size_t size) {
+ if (fitsInCurrentPage(size)) return;
+
+ if (mCurrentPage && mPageSize < MAX_PAGE_SIZE) {
+ mPageSize = min(MAX_PAGE_SIZE, mPageSize * 2);
+ mPageSize = ALIGN(mPageSize);
+ }
+ mWastedSpace += mPageSize;
+ Page* p = newPage(mPageSize);
+ if (mCurrentPage) {
+ mCurrentPage->setNext(p);
+ }
+ mCurrentPage = p;
+ if (!mPages) {
+ mPages = mCurrentPage;
+ }
+ mNext = start(mCurrentPage);
+}
+
+void* LinearAllocator::alloc(size_t size) {
+ size = ALIGN(size);
+ if (size > mMaxAllocSize && !fitsInCurrentPage(size)) {
+ ALOGV("Exceeded max size %zu > %zu", size, mMaxAllocSize);
+ // Allocation is too large, create a dedicated page for the allocation
+ Page* page = newPage(size);
+ mDedicatedPageCount++;
+ page->setNext(mPages);
+ mPages = page;
+ if (!mCurrentPage)
+ mCurrentPage = mPages;
+ return start(page);
+ }
+ ensureNext(size);
+ void* ptr = mNext;
+ mNext = ((char*)mNext) + size;
+ mWastedSpace -= size;
+ return ptr;
+}
+
+void LinearAllocator::addToDestructionList(Destructor dtor, void* addr) {
+ static_assert(std::is_standard_layout<DestructorNode>::value,
+ "DestructorNode must have standard layout");
+ static_assert(std::is_trivially_destructible<DestructorNode>::value,
+ "DestructorNode must be trivially destructable");
+ auto node = new (*this) DestructorNode();
+ node->dtor = dtor;
+ node->addr = addr;
+ node->next = mDtorList;
+ mDtorList = node;
+}
+
+void LinearAllocator::runDestructorFor(void* addr) {
+ auto node = mDtorList;
+ DestructorNode* previous = nullptr;
+ while (node) {
+ if (node->addr == addr) {
+ if (previous) {
+ previous->next = node->next;
+ } else {
+ mDtorList = node->next;
+ }
+ node->dtor(node->addr);
+ rewindIfLastAlloc(node, sizeof(DestructorNode));
+ break;
+ }
+ previous = node;
+ node = node->next;
+ }
+}
+
+void LinearAllocator::rewindIfLastAlloc(void* ptr, size_t allocSize) {
+ // First run the destructor as running the destructor will
+ // also rewind for the DestructorNode allocation which will
+ // have been allocated after this void* if it has a destructor
+ runDestructorFor(ptr);
+ // Don't bother rewinding across pages
+ allocSize = ALIGN(allocSize);
+ if (ptr >= start(mCurrentPage) && ptr < end(mCurrentPage)
+ && ptr == ((char*)mNext - allocSize)) {
+ mWastedSpace += allocSize;
+ mNext = ptr;
+ }
+}
+
+LinearAllocator::Page* LinearAllocator::newPage(size_t pageSize) {
+ pageSize = ALIGN(pageSize + sizeof(LinearAllocator::Page));
+ ADD_ALLOCATION(pageSize);
+ mTotalAllocated += pageSize;
+ mPageCount++;
+ void* buf = malloc(pageSize);
+ return new (buf) Page();
+}
+
+static const char* toSize(size_t value, float& result) {
+ if (value < 2000) {
+ result = value;
+ return "B";
+ }
+ if (value < 2000000) {
+ result = value / 1024.0f;
+ return "KB";
+ }
+ result = value / 1048576.0f;
+ return "MB";
+}
+
+void LinearAllocator::dumpMemoryStats(const char* prefix) {
+ float prettySize;
+ const char* prettySuffix;
+ prettySuffix = toSize(mTotalAllocated, prettySize);
+ ALOGD("%sTotal allocated: %.2f%s", prefix, prettySize, prettySuffix);
+ prettySuffix = toSize(mWastedSpace, prettySize);
+ ALOGD("%sWasted space: %.2f%s (%.1f%%)", prefix, prettySize, prettySuffix,
+ (float) mWastedSpace / (float) mTotalAllocated * 100.0f);
+ ALOGD("%sPages %zu (dedicated %zu)", prefix, mPageCount, mDedicatedPageCount);
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/utils/LinearAllocator.h b/libs/hwui/utils/LinearAllocator.h
new file mode 100644
index 0000000..d90dd82
--- /dev/null
+++ b/libs/hwui/utils/LinearAllocator.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ANDROID_LINEARALLOCATOR_H
+#define ANDROID_LINEARALLOCATOR_H
+
+#include <stddef.h>
+#include <type_traits>
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * A memory manager that internally allocates multi-kbyte buffers for placing objects in. It avoids
+ * the overhead of malloc when many objects are allocated. It is most useful when creating many
+ * small objects with a similar lifetime, and doesn't add significant overhead for large
+ * allocations.
+ */
+class LinearAllocator {
+public:
+ LinearAllocator();
+ ~LinearAllocator();
+
+ /**
+ * Reserves and returns a region of memory of at least size 'size', aligning as needed.
+ * Typically this is used in an object's overridden new() method or as a replacement for malloc.
+ *
+ * The lifetime of the returned buffers is tied to that of the LinearAllocator. If calling
+ * delete() on an object stored in a buffer is needed, it should be overridden to use
+ * rewindIfLastAlloc()
+ */
+ void* alloc(size_t size);
+
+ /**
+ * Allocates an instance of the template type with the default constructor
+ * and adds it to the automatic destruction list.
+ */
+ template<class T>
+ T* alloc() {
+ T* ret = new (*this) T;
+ autoDestroy(ret);
+ return ret;
+ }
+
+ /**
+ * Adds the pointer to the tracking list to have its destructor called
+ * when the LinearAllocator is destroyed.
+ */
+ template<class T>
+ void autoDestroy(T* addr) {
+ if (!std::is_trivially_destructible<T>::value) {
+ auto dtor = [](void* addr) { ((T*)addr)->~T(); };
+ addToDestructionList(dtor, addr);
+ }
+ }
+
+ /**
+ * Attempt to deallocate the given buffer, with the LinearAllocator attempting to rewind its
+ * state if possible.
+ */
+ void rewindIfLastAlloc(void* ptr, size_t allocSize);
+
+ /**
+ * Same as rewindIfLastAlloc(void*, size_t)
+ */
+ template<class T>
+ void rewindIfLastAlloc(T* ptr) {
+ rewindIfLastAlloc((void*)ptr, sizeof(T));
+ }
+
+ /**
+ * Dump memory usage statistics to the log (allocated and wasted space)
+ */
+ void dumpMemoryStats(const char* prefix = "");
+
+ /**
+ * The number of bytes used for buffers allocated in the LinearAllocator (does not count space
+ * wasted)
+ */
+ size_t usedSize() const { return mTotalAllocated - mWastedSpace; }
+
+private:
+ LinearAllocator(const LinearAllocator& other);
+
+ class Page;
+ typedef void (*Destructor)(void* addr);
+ struct DestructorNode {
+ Destructor dtor;
+ void* addr;
+ DestructorNode* next = nullptr;
+ };
+
+ void addToDestructionList(Destructor, void* addr);
+ void runDestructorFor(void* addr);
+ Page* newPage(size_t pageSize);
+ bool fitsInCurrentPage(size_t size);
+ void ensureNext(size_t size);
+ void* start(Page *p);
+ void* end(Page* p);
+
+ size_t mPageSize;
+ size_t mMaxAllocSize;
+ void* mNext;
+ Page* mCurrentPage;
+ Page* mPages;
+ DestructorNode* mDtorList = nullptr;
+
+ // Memory usage tracking
+ size_t mTotalAllocated;
+ size_t mWastedSpace;
+ size_t mPageCount;
+ size_t mDedicatedPageCount;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+void* operator new(std::size_t size, android::uirenderer::LinearAllocator& la);
+
+#endif // ANDROID_LINEARALLOCATOR_H
diff --git a/libs/hwui/utils/Macros.h b/libs/hwui/utils/Macros.h
index 5b7c87c..1b31059 100644
--- a/libs/hwui/utils/Macros.h
+++ b/libs/hwui/utils/Macros.h
@@ -16,10 +16,12 @@
#ifndef MACROS_H
#define MACROS_H
+#include <type_traits>
+
#define PREVENT_COPY_AND_ASSIGN(Type) \
private: \
- Type(const Type&); \
- void operator=(const Type&)
+ Type(const Type&) = delete; \
+ void operator=(const Type&) = delete
#define DESCRIPTION_TYPE(Type) \
int compare(const Type& rhs) const { return memcmp(this, &rhs, sizeof(Type));} \
@@ -29,4 +31,34 @@
friend inline int compare_type(const Type& lhs, const Type& rhs) { return lhs.compare(rhs); } \
friend inline hash_t hash_type(const Type& entry) { return entry.hash(); }
+#define REQUIRE_COMPATIBLE_LAYOUT(Type) \
+ static_assert(std::is_standard_layout<Type>::value, \
+ #Type " must have standard layout")
+
+#define MAKE_FLAGS_ENUM(enumType) \
+ inline void operator|=(int& lhs, enumType rhs) { \
+ lhs |= static_cast<int>(rhs); \
+ } \
+ inline int operator|(int lhs, enumType rhs) { \
+ return lhs | static_cast<int>(rhs); \
+ } \
+ inline int operator|(enumType lhs, int rhs) { \
+ return static_cast<int>(lhs) | rhs; \
+ } \
+ inline int operator|(enumType lhs, enumType rhs) { \
+ return static_cast<int>(lhs) | static_cast<int>(rhs); \
+ } \
+ inline void operator&=(int& lhs, enumType rhs) { \
+ lhs &= static_cast<int>(rhs); \
+ } \
+ inline int operator&(int lhs, enumType rhs) { \
+ return lhs & static_cast<int>(rhs); \
+ } \
+ inline int operator&(enumType lhs, int rhs) { \
+ return static_cast<int>(lhs) & rhs; \
+ } \
+ inline int operator&(enumType lhs, enumType rhs) { \
+ return static_cast<int>(lhs) & static_cast<int>(rhs); \
+ }
+
#endif /* MACROS_H */
diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h
new file mode 100644
index 0000000..ba02f5f
--- /dev/null
+++ b/libs/hwui/utils/PaintUtils.h
@@ -0,0 +1,81 @@
+/*
+ * 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 PAINT_UTILS_H
+#define PAINT_UTILS_H
+
+#include <SkColorFilter.h>
+#include <SkXfermode.h>
+
+namespace android {
+namespace uirenderer {
+
+class PaintUtils {
+public:
+
+ /**
+ * Safely retrieves the mode from the specified xfermode. If the specified
+ * xfermode is null, the mode is assumed to be SkXfermode::kSrcOver_Mode.
+ */
+ static inline SkXfermode::Mode getXfermode(SkXfermode* mode) {
+ SkXfermode::Mode resultMode;
+ if (!SkXfermode::AsMode(mode, &resultMode)) {
+ resultMode = SkXfermode::kSrcOver_Mode;
+ }
+ return resultMode;
+ }
+
+ static inline GLenum getFilter(const SkPaint* paint) {
+ if (!paint || paint->getFilterQuality() != kNone_SkFilterQuality) {
+ return GL_LINEAR;
+ }
+ return GL_NEAREST;
+ }
+
+ // TODO: move to a method on android:Paint? replace with SkPaint::nothingToDraw()?
+ static inline bool paintWillNotDraw(const SkPaint& paint) {
+ return paint.getAlpha() == 0
+ && !paint.getColorFilter()
+ && getXfermode(paint.getXfermode()) == SkXfermode::kSrcOver_Mode;
+ }
+
+ // TODO: move to a method on android:Paint? replace with SkPaint::nothingToDraw()?
+ static inline bool paintWillNotDrawText(const SkPaint& paint) {
+ return paint.getAlpha() == 0
+ && paint.getLooper() == nullptr
+ && !paint.getColorFilter()
+ && getXfermode(paint.getXfermode()) == SkXfermode::kSrcOver_Mode;
+ }
+
+ static bool isBlendedShader(const SkShader* shader) {
+ if (shader == nullptr) {
+ return false;
+ }
+ return !shader->isOpaque();
+ }
+
+ static bool isBlendedColorFilter(const SkColorFilter* filter) {
+ if (filter == nullptr) {
+ return false;
+ }
+ return (filter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag) == 0;
+ }
+
+}; // class PaintUtils
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* PAINT_UTILS_H */
diff --git a/libs/hwui/utils/Pair.h b/libs/hwui/utils/Pair.h
index 172606a..0db3aa3 100644
--- a/libs/hwui/utils/Pair.h
+++ b/libs/hwui/utils/Pair.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_HWUI_PAIR_H
#define ANDROID_HWUI_PAIR_H
+#include <utils/TypeHelpers.h>
+
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/utils/RingBuffer.h b/libs/hwui/utils/RingBuffer.h
new file mode 100644
index 0000000..fc9aec0
--- /dev/null
+++ b/libs/hwui/utils/RingBuffer.h
@@ -0,0 +1,71 @@
+/*
+ * 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 RINGBUFFER_H_
+#define RINGBUFFER_H_
+
+#include "utils/Macros.h"
+
+#include <stddef.h>
+
+namespace android {
+namespace uirenderer {
+
+template<class T, size_t SIZE>
+class RingBuffer {
+ PREVENT_COPY_AND_ASSIGN(RingBuffer);
+
+public:
+ RingBuffer() {}
+ ~RingBuffer() {}
+
+ size_t capacity() { return SIZE; }
+ size_t size() { return mCount; }
+
+ T& next() {
+ mHead = (mHead + 1) % SIZE;
+ if (mCount < SIZE) {
+ mCount++;
+ }
+ return mBuffer[mHead];
+ }
+
+ T& front() {
+ return this[0];
+ }
+
+ T& back() {
+ return this[size() - 1];
+ }
+
+ T& operator[](size_t index) {
+ return mBuffer[(mHead + index + 1) % mCount];
+ }
+
+ void clear() {
+ mCount = 0;
+ mHead = -1;
+ }
+
+private:
+ T mBuffer[SIZE];
+ int mHead = -1;
+ size_t mCount = 0;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif /* RINGBUFFER_H_ */
diff --git a/libs/hwui/utils/SortedList.h b/libs/hwui/utils/SortedList.h
index 2fa890a..a2c8c52 100644
--- a/libs/hwui/utils/SortedList.h
+++ b/libs/hwui/utils/SortedList.h
@@ -93,13 +93,13 @@ public:
}
protected:
- virtual void do_construct(void* storage, size_t num) const;
- virtual void do_destroy(void* storage, size_t num) const;
- virtual void do_copy(void* dest, const void* from, size_t num) const;
- virtual void do_splat(void* dest, const void* item, size_t num) const;
- virtual void do_move_forward(void* dest, const void* from, size_t num) const;
- virtual void do_move_backward(void* dest, const void* from, size_t num) const;
- virtual int do_compare(const void* lhs, const void* rhs) const;
+ virtual void do_construct(void* storage, size_t num) const override;
+ virtual void do_destroy(void* storage, size_t num) const override;
+ virtual void do_copy(void* dest, const void* from, size_t num) const override;
+ virtual void do_splat(void* dest, const void* item, size_t num) const override;
+ virtual void do_move_forward(void* dest, const void* from, size_t num) const override;
+ virtual void do_move_backward(void* dest, const void* from, size_t num) const override;
+ virtual int do_compare(const void* lhs, const void* rhs) const override;
}; // class SortedList
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/utils/SortedListImpl.h b/libs/hwui/utils/SortedListImpl.h
index dc385b5..b101826 100644
--- a/libs/hwui/utils/SortedListImpl.h
+++ b/libs/hwui/utils/SortedListImpl.h
@@ -41,7 +41,7 @@ protected:
virtual int do_compare(const void* lhs, const void* rhs) const = 0;
private:
- ssize_t _indexOrderOf(const void* item, size_t* order = 0) const;
+ ssize_t _indexOrderOf(const void* item, size_t* order = nullptr) const;
// these are made private, because they can't be used on a SortedVector
// (they don't have an implementation either)
diff --git a/libs/hwui/utils/Timing.h b/libs/hwui/utils/Timing.h
index eced987..dd8847a 100644
--- a/libs/hwui/utils/Timing.h
+++ b/libs/hwui/utils/Timing.h
@@ -24,12 +24,12 @@ class MethodTimer {
public:
MethodTimer(const char* name)
: mMethodName(name) {
- gettimeofday(&mStart, NULL);
+ gettimeofday(&mStart, nullptr);
}
~MethodTimer() {
struct timeval stop;
- gettimeofday(&stop, NULL);
+ gettimeofday(&stop, nullptr);
long long elapsed = (stop.tv_sec * 1000000) - (mStart.tv_sec * 1000000)
+ (stop.tv_usec - mStart.tv_usec);
ALOGD("%s took %.2fms", mMethodName, elapsed / 1000.0);
diff --git a/libs/input/Android.mk b/libs/input/Android.mk
index a7fb0e2..2bbfdcc 100644
--- a/libs/input/Android.mk
+++ b/libs/input/Android.mk
@@ -27,14 +27,14 @@ LOCAL_SHARED_LIBRARIES := \
libskia \
libgui \
libui \
- libinput \
- libinputflinger
+ libinput \
+ libinputflinger
LOCAL_C_INCLUDES := \
frameworks/native/services
-LOCAL_CFLAGS += -Wno-unused-parameter
+LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
LOCAL_MODULE:= libinputservice
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 9af521b..1152737 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -25,11 +25,14 @@
#include <cutils/log.h>
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
#include <SkBitmap.h>
#include <SkCanvas.h>
#include <SkColor.h>
#include <SkPaint.h>
#include <SkXfermode.h>
+#pragma GCC diagnostic pop
namespace android {
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index 5391393..0bc832a 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -24,11 +24,15 @@
#include <utils/String8.h>
#include <gui/Surface.h>
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
#include <SkBitmap.h>
#include <SkCanvas.h>
#include <SkColor.h>
#include <SkPaint.h>
#include <SkXfermode.h>
+#pragma GCC diagnostic pop
+
#include <android/native_window.h>
namespace android {
diff --git a/libs/storage/Android.mk b/libs/storage/Android.mk
index 7a9dd6c..fae2bf7 100644
--- a/libs/storage/Android.mk
+++ b/libs/storage/Android.mk
@@ -9,4 +9,6 @@ LOCAL_SRC_FILES:= \
LOCAL_MODULE:= libstorage
+LOCAL_CFLAGS += -Wall -Werror
+
include $(BUILD_STATIC_LIBRARY)
diff --git a/libs/storage/IMountService.cpp b/libs/storage/IMountService.cpp
index 621de18..c643ed0 100644
--- a/libs/storage/IMountService.cpp
+++ b/libs/storage/IMountService.cpp
@@ -64,7 +64,7 @@ public:
{
Parcel data, reply;
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
- data.writeStrongBinder(listener->asBinder());
+ data.writeStrongBinder(IInterface::asBinder(listener));
if (remote()->transact(TRANSACTION_registerListener, data, &reply) != NO_ERROR) {
ALOGD("registerListener could not contact remote\n");
return;
@@ -80,7 +80,7 @@ public:
{
Parcel data, reply;
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
- data.writeStrongBinder(listener->asBinder());
+ data.writeStrongBinder(IInterface::asBinder(listener));
if (remote()->transact(TRANSACTION_unregisterListener, data, &reply) != NO_ERROR) {
ALOGD("unregisterListener could not contact remote\n");
return;
@@ -207,12 +207,19 @@ public:
ALOGD("getStorageUsers caught exception %d\n", err);
return err;
}
- const int32_t numUsers = reply.readInt32();
+ int32_t numUsersI = reply.readInt32();
+ uint32_t numUsers;
+ if (numUsersI < 0) {
+ ALOGW("Number of users is negative: %d\n", numUsersI);
+ numUsers = 0;
+ } else {
+ numUsers = static_cast<uint32_t>(numUsersI);
+ }
*users = (int32_t*)malloc(sizeof(int32_t)*numUsers);
- for (int i = 0; i < numUsers; i++) {
+ for (size_t i = 0; i < numUsers; i++) {
**users++ = reply.readInt32();
}
- return numUsers;
+ return static_cast<int32_t>(numUsers);
}
int32_t getVolumeState(const String16& mountPoint)
@@ -406,7 +413,7 @@ public:
{
Parcel data, reply;
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
- data.writeStrongBinder(observer->asBinder());
+ data.writeStrongBinder(IInterface::asBinder(observer));
if (remote()->transact(TRANSACTION_shutdown, data, &reply) != NO_ERROR) {
ALOGD("shutdown could not contact remote\n");
return;
@@ -443,7 +450,7 @@ public:
data.writeString16(rawPath);
data.writeString16(canonicalPath);
data.writeString16(key);
- data.writeStrongBinder(token->asBinder());
+ data.writeStrongBinder(IInterface::asBinder(token));
data.writeInt32(nonce);
if (remote()->transact(TRANSACTION_mountObb, data, &reply) != NO_ERROR) {
ALOGD("mountObb could not contact remote\n");
@@ -463,7 +470,7 @@ public:
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
data.writeString16(filename);
data.writeInt32(force ? 1 : 0);
- data.writeStrongBinder(token->asBinder());
+ data.writeStrongBinder(IInterface::asBinder(token));
data.writeInt32(nonce);
if (remote()->transact(TRANSACTION_unmountObb, data, &reply) != NO_ERROR) {
ALOGD("unmountObb could not contact remote\n");
@@ -546,8 +553,8 @@ public:
}
};
-IMPLEMENT_META_INTERFACE(MountService, "IMountService");
+IMPLEMENT_META_INTERFACE(MountService, "IMountService")
// ----------------------------------------------------------------------
-};
+}
diff --git a/libs/storage/IMountServiceListener.cpp b/libs/storage/IMountServiceListener.cpp
index c98a424..11b53fd 100644
--- a/libs/storage/IMountServiceListener.cpp
+++ b/libs/storage/IMountServiceListener.cpp
@@ -34,7 +34,7 @@ status_t BnMountServiceListener::onTransact(
onUsbMassStorageConnectionChanged(connected);
reply->writeNoException();
return NO_ERROR;
- } break;
+ }
case TRANSACTION_onStorageStateChanged: {
CHECK_INTERFACE(IMountServiceListener, data, reply);
String16 path = data.readString16();
@@ -50,4 +50,4 @@ status_t BnMountServiceListener::onTransact(
}
// ----------------------------------------------------------------------
-};
+}
diff --git a/libs/storage/IMountShutdownObserver.cpp b/libs/storage/IMountShutdownObserver.cpp
index 1a6fdee..a74a768 100644
--- a/libs/storage/IMountShutdownObserver.cpp
+++ b/libs/storage/IMountShutdownObserver.cpp
@@ -33,11 +33,11 @@ status_t BnMountShutdownObserver::onTransact(
onShutDownComplete(statusCode);
reply->writeNoException();
return NO_ERROR;
- } break;
+ }
default:
return BBinder::onTransact(code, data, reply, flags);
}
}
// ----------------------------------------------------------------------
-};
+}
diff --git a/libs/storage/IObbActionListener.cpp b/libs/storage/IObbActionListener.cpp
index eaa211e..9656e65 100644
--- a/libs/storage/IObbActionListener.cpp
+++ b/libs/storage/IObbActionListener.cpp
@@ -30,10 +30,11 @@ public:
: BpInterface<IObbActionListener>(impl)
{ }
- virtual void onObbResult(const String16& filename, const int32_t nonce, const int32_t state) { }
+ virtual void onObbResult(const String16& /* filename */, const int32_t /* nonce */,
+ const int32_t /* state */) { }
};
-IMPLEMENT_META_INTERFACE(ObbActionListener, "IObbActionListener");
+IMPLEMENT_META_INTERFACE(ObbActionListener, "IObbActionListener")
// ----------------------------------------------------------------------
@@ -49,7 +50,7 @@ status_t BnObbActionListener::onTransact(
onObbResult(filename, nonce, state);
reply->writeNoException();
return NO_ERROR;
- } break;
+ }
default:
return BBinder::onTransact(code, data, reply, flags);
}
@@ -57,4 +58,4 @@ status_t BnObbActionListener::onTransact(
// ----------------------------------------------------------------------
-};
+}
diff --git a/libs/usb/tests/AccessoryChat/accessorychat/Android.mk b/libs/usb/tests/AccessoryChat/accessorychat/Android.mk
index 3e07155..51f2111 100644
--- a/libs/usb/tests/AccessoryChat/accessorychat/Android.mk
+++ b/libs/usb/tests/AccessoryChat/accessorychat/Android.mk
@@ -4,6 +4,7 @@ LOCAL_PATH:= $(call my-dir)
ifeq ($(HOST_OS),linux)
include $(CLEAR_VARS)
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_MODULE_TAGS := optional
@@ -21,6 +22,7 @@ endif
# Build for device
include $(CLEAR_VARS)
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_MODULE_TAGS := optional