From 859f98d82c1ff38f34c0495d180561121f8a1a81 Mon Sep 17 00:00:00 2001 From: John Reck Date: Wed, 13 Mar 2019 16:25:20 -0700 Subject: Add back render-ahead support Currently only supported in the EGL path. Vulkan support Coming Soon Bug: 127822449 Test: trace of hwuimacro Change-Id: Iac2b039e11d964aab5b8ca1bdf2a5430b187e2ea --- libs/hwui/HWUIProperties.sysprop | 7 ++++++ libs/hwui/Properties.cpp | 8 +++++++ libs/hwui/Properties.h | 4 ++++ libs/hwui/renderthread/CanvasContext.cpp | 30 ++++++++++++++++++++++++++ libs/hwui/renderthread/CanvasContext.h | 4 ++++ libs/hwui/renderthread/RenderProxy.cpp | 6 ++++++ libs/hwui/renderthread/RenderProxy.h | 17 +++++++++++++++ libs/hwui/tests/common/TestScene.h | 1 + libs/hwui/tests/macrobench/TestSceneRunner.cpp | 5 +++++ libs/hwui/tests/macrobench/main.cpp | 13 +++++++++++ 10 files changed, 95 insertions(+) (limited to 'libs') diff --git a/libs/hwui/HWUIProperties.sysprop b/libs/hwui/HWUIProperties.sysprop index 42191ca6f514..34aeaae867f9 100644 --- a/libs/hwui/HWUIProperties.sysprop +++ b/libs/hwui/HWUIProperties.sysprop @@ -7,3 +7,10 @@ prop { scope: Public access: Readonly } +prop { + api_name: "render_ahead" + type: Integer + prop_name: "ro.hwui.render_ahead" + scope: Public + access: Readonly +} \ No newline at end of file diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index 046ffc4da5ea..9b1f25986d56 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -67,6 +67,7 @@ bool Properties::debuggingEnabled = false; bool Properties::isolatedProcess = false; int Properties::contextPriority = 0; +int Properties::defaultRenderAhead = 0; static int property_get_int(const char* key, int defaultValue) { char buf[PROPERTY_VALUE_MAX] = { @@ -129,6 +130,13 @@ bool Properties::load() { enableForceDarkSupport = property_get_bool(PROPERTY_ENABLE_FORCE_DARK, true); + defaultRenderAhead = std::max(0, std::min(2, property_get_int(PROPERTY_RENDERAHEAD, + render_ahead().value_or(0)))); + + if (defaultRenderAhead && sRenderPipelineType == RenderPipelineType::SkiaVulkan) { + ALOGW("hwui.render_ahead of %d ignored because pipeline is skiavk", defaultRenderAhead); + } + return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw); } diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index 0a7f4e7eb41c..3e91c63fcbde 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -167,6 +167,8 @@ enum DebugLevel { #define PROPERTY_ENABLE_FORCE_DARK "debug.hwui.force_dark_enabled" +#define PROPERTY_RENDERAHEAD "debug.hwui.render_ahead" + /////////////////////////////////////////////////////////////////////////////// // Misc /////////////////////////////////////////////////////////////////////////////// @@ -251,6 +253,8 @@ public: ANDROID_API static int contextPriority; + static int defaultRenderAhead; + private: static ProfileType sProfileType; static bool sDisableProfileBars; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index baa41c1ec698..4808d68b89ab 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -111,6 +111,7 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode->makeRoot(); mRenderNodes.emplace_back(rootRenderNode); mProfiler.setDensity(mRenderThread.mainDisplayInfo().density); + setRenderAheadDepth(Properties::defaultRenderAhead); } CanvasContext::~CanvasContext() { @@ -159,6 +160,7 @@ void CanvasContext::setSurface(sp&& surface) { if (hasSurface) { mHaveNewSurface = true; mSwapHistory.clear(); + applyRenderAheadSettings(); } else { mRenderThread.removeFrameCallback(this); mGenerationID++; @@ -423,6 +425,12 @@ void CanvasContext::draw() { waitOnFences(); + if (mRenderAheadDepth) { + auto presentTime = mCurrentFrameInfo->get(FrameInfoIndex::Vsync) + + (mRenderThread.timeLord().frameIntervalNanos() * (mRenderAheadDepth + 1)); + native_window_set_buffers_timestamp(mNativeSurface.get(), presentTime); + } + bool requireSwap = false; bool didSwap = mRenderPipeline->swapBuffers(frame, drew, windowDirty, mCurrentFrameInfo, &requireSwap); @@ -636,6 +644,28 @@ bool CanvasContext::surfaceRequiresRedraw() { return width == mLastFrameWidth && height == mLastFrameHeight; } +void CanvasContext::applyRenderAheadSettings() { + if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { + // TODO: Fix SkiaVulkan's assumptions on buffer counts. And SIGBUS crashes. + mRenderAheadDepth = 0; + return; + } + if (mNativeSurface) { + native_window_set_buffer_count(mNativeSurface.get(), 3 + mRenderAheadDepth); + if (!mRenderAheadDepth) { + native_window_set_buffers_timestamp(mNativeSurface.get(), NATIVE_WINDOW_TIMESTAMP_AUTO); + } + } +} + +void CanvasContext::setRenderAheadDepth(int renderAhead) { + if (renderAhead < 0 || renderAhead > 2 || renderAhead == mRenderAheadDepth) { + return; + } + mRenderAheadDepth = renderAhead; + applyRenderAheadSettings(); +} + SkRect CanvasContext::computeDirtyRect(const Frame& frame, SkRect* dirty) { if (frame.width() != mLastFrameWidth || frame.height() != mLastFrameHeight) { // can't rely on prior content of window if viewport size changes diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index abca34225f98..4a3119a55c77 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -204,6 +204,8 @@ public: return mUseForceDark; } + void setRenderAheadDepth(int renderAhead); + private: CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory, std::unique_ptr renderPipeline); @@ -217,6 +219,7 @@ private: bool isSwapChainStuffed(); bool surfaceRequiresRedraw(); + void applyRenderAheadSettings(); SkRect computeDirtyRect(const Frame& frame, SkRect* dirty); @@ -235,6 +238,7 @@ private: // painted onto its surface. bool mIsDirty = false; SwapBehavior mSwapBehavior = SwapBehavior::kSwap_default; + int mRenderAheadDepth = 0; struct SwapHistory { SkRect damage; nsecs_t vsyncTime; diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 16240b4e177b..b58bab1191ed 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -312,6 +312,12 @@ void RenderProxy::setForceDark(bool enable) { mRenderThread.queue().post([this, enable]() { mContext->setForceDark(enable); }); } +void RenderProxy::setRenderAheadDepth(int renderAhead) { + mRenderThread.queue().post([ context = mContext, renderAhead ] { + context->setRenderAheadDepth(renderAhead); + }); +} + int RenderProxy::copySurfaceInto(sp& surface, int left, int top, int right, int bottom, SkBitmap* bitmap) { auto& thread = RenderThread::getInstance(); diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index a1a5551722bc..a0f08cbd26f9 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -123,6 +123,23 @@ public: ANDROID_API void removeFrameMetricsObserver(FrameMetricsObserver* observer); ANDROID_API void setForceDark(bool enable); + /** + * Sets a render-ahead depth on the backing renderer. This will increase latency by + * * renderAhead and increase memory usage by (3 + renderAhead) * . + * In return the renderer will be less susceptible to jitter, resulting in a smoother animation. + * + * Not recommended to use in response to anything touch driven, but for canned animations + * where latency is not a concern careful use may be beneficial. + * + * Note that when increasing this there will be a frame gap of N frames where N is + * renderAhead - . When decreasing this if there are any pending + * frames they will retain their prior renderAhead value, so it will take a few frames + * for the decrease to flush through. + * + * @param renderAhead How far to render ahead, must be in the range [0..2] + */ + ANDROID_API void setRenderAheadDepth(int renderAhead); + ANDROID_API static int copySurfaceInto(sp& surface, int left, int top, int right, int bottom, SkBitmap* bitmap); ANDROID_API static void prepareToDraw(Bitmap& bitmap); diff --git a/libs/hwui/tests/common/TestScene.h b/libs/hwui/tests/common/TestScene.h index 91022cfe734b..74a039b3d090 100644 --- a/libs/hwui/tests/common/TestScene.h +++ b/libs/hwui/tests/common/TestScene.h @@ -38,6 +38,7 @@ public: int count = 0; int reportFrametimeWeight = 0; bool renderOffscreen = true; + int renderAhead = 0; }; template diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp index b45dbc8b832b..aa579adfb2ce 100644 --- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp +++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp @@ -154,6 +154,11 @@ void run(const TestScene::Info& info, const TestScene::Options& opts, proxy->resetProfileInfo(); proxy->fence(); + if (opts.renderAhead) { + usleep(33000); + } + proxy->setRenderAheadDepth(opts.renderAhead); + ModifiedMovingAverage avgMs(opts.reportFrametimeWeight); nsecs_t start = systemTime(CLOCK_MONOTONIC); diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp index 174a14080eff..88d33c315a09 100644 --- a/libs/hwui/tests/macrobench/main.cpp +++ b/libs/hwui/tests/macrobench/main.cpp @@ -69,6 +69,7 @@ OPTIONS: are offscreen rendered --benchmark_format Set output format. Possible values are tabular, json, csv --renderer=TYPE Sets the render pipeline to use. May be skiagl or skiavk + --render-ahead=NUM Sets how far to render-ahead. Must be 0 (default), 1, or 2. )"); } @@ -170,6 +171,7 @@ enum { Onscreen, Offscreen, Renderer, + RenderAhead, }; } @@ -185,6 +187,7 @@ static const struct option LONG_OPTIONS[] = { {"onscreen", no_argument, nullptr, LongOpts::Onscreen}, {"offscreen", no_argument, nullptr, LongOpts::Offscreen}, {"renderer", required_argument, nullptr, LongOpts::Renderer}, + {"render-ahead", required_argument, nullptr, LongOpts::RenderAhead}, {0, 0, 0, 0}}; static const char* SHORT_OPTIONS = "c:r:h"; @@ -283,6 +286,16 @@ void parseOptions(int argc, char* argv[]) { gOpts.renderOffscreen = true; break; + case LongOpts::RenderAhead: + if (!optarg) { + error = true; + } + gOpts.renderAhead = atoi(optarg); + if (gOpts.renderAhead < 0 || gOpts.renderAhead > 2) { + error = true; + } + break; + case 'h': printHelp(); exit(EXIT_SUCCESS); -- cgit v1.2.3 From 89bb24e4cf510ef743667ad3aa719d960d7fde3d Mon Sep 17 00:00:00 2001 From: Winson Date: Wed, 3 Apr 2019 15:50:36 -0700 Subject: Fix remaining pointer leaks in Asset.cpp Follow up from comment in ag/6761240 Test: none Change-Id: Ib6a52b3fe13b4eb00b363ee720196fe0bfdfbb94 --- libs/androidfw/Asset.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'libs') diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp index 9a95fdf80cb5..92125c9da8bb 100644 --- a/libs/androidfw/Asset.cpp +++ b/libs/androidfw/Asset.cpp @@ -253,8 +253,10 @@ Asset::Asset(void) pAsset = new _FileAsset; result = pAsset->openChunk(NULL, fd, offset, length); - if (result != NO_ERROR) + if (result != NO_ERROR) { + delete pAsset; return NULL; + } pAsset->mAccessMode = mode; return pAsset; @@ -273,8 +275,10 @@ Asset::Asset(void) pAsset = new _CompressedAsset; result = pAsset->openChunk(fd, offset, compressionMethod, uncompressedLen, compressedLen); - if (result != NO_ERROR) + if (result != NO_ERROR) { + delete pAsset; return NULL; + } pAsset->mAccessMode = mode; return pAsset; @@ -328,8 +332,10 @@ Asset::Asset(void) pAsset = new _CompressedAsset; result = pAsset->openChunk(dataMap, uncompressedLen); - if (result != NO_ERROR) + if (result != NO_ERROR) { + delete pAsset; return NULL; + } pAsset->mAccessMode = mode; return pAsset; -- cgit v1.2.3 From 8e874bb8657179f2336cb30d65d63ca33773ea11 Mon Sep 17 00:00:00 2001 From: Brian Osman Date: Mon, 8 Apr 2019 12:24:46 -0400 Subject: Make an Android-specific copy of Skia's Null GL interface This functionality has been removed from Skia, but is still being used by HWUI's NullGlesDriver for unit tests. Making a local copy gets tests passing (so that rolling can resume). Test: hwui_unit_tests still pass Change-Id: I16b65af58eaeca5a39df89d1542760b652d8b0d5 --- libs/hwui/Android.bp | 1 + libs/hwui/debug/NullGlesDriver.cpp | 4 +- libs/hwui/debug/NullSkiaInterface.cpp | 852 ++++++++++++++++++++++++++++++++++ 3 files changed, 856 insertions(+), 1 deletion(-) create mode 100644 libs/hwui/debug/NullSkiaInterface.cpp (limited to 'libs') diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index ebba4cb79dfb..9fe53d3b2649 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -101,6 +101,7 @@ cc_defaults { "debug/GlesDriver.cpp", "debug/FatalBaseDriver.cpp", "debug/NullGlesDriver.cpp", + "debug/NullSkiaInterface.cpp", ], include_dirs: ["frameworks/native/opengl/libs/GLES2"], } diff --git a/libs/hwui/debug/NullGlesDriver.cpp b/libs/hwui/debug/NullGlesDriver.cpp index 212b24290e22..f27adf0177d2 100644 --- a/libs/hwui/debug/NullGlesDriver.cpp +++ b/libs/hwui/debug/NullGlesDriver.cpp @@ -20,8 +20,10 @@ namespace android { namespace uirenderer { namespace debug { +extern const GrGLInterface* CreateNullSkiaInterface(); + sk_sp NullGlesDriver::getSkiaInterface() { - sk_sp skiaInterface(GrGLCreateNullInterface()); + sk_sp skiaInterface(CreateNullSkiaInterface()); return skiaInterface; } diff --git a/libs/hwui/debug/NullSkiaInterface.cpp b/libs/hwui/debug/NullSkiaInterface.cpp new file mode 100644 index 000000000000..b5438cb3c10e --- /dev/null +++ b/libs/hwui/debug/NullSkiaInterface.cpp @@ -0,0 +1,852 @@ +/* + * Copyright (C) 2019 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. + */ + +// TODO: Remove this file. This has been temporarily copied from Skia (where this class is +// deprecated). The NullGlesDriver should be constructing a GrGLInterface that calls *its* +// GL functions! + +#include "gl/GrGLTestInterface.h" +#include "GrNonAtomicRef.h" +#include "SkMutex.h" +#include "SkTDArray.h" +#include "SkTo.h" +#include "gl/GrGLInterface.h" + +#include + +// added to suppress 'no previous prototype' warning and because this code is duplicated in +// SkNullGLContext.cpp +namespace { + +class GLObject : public GrNonAtomicRef { +public: + GLObject(GrGLuint id) : fID(id) {} + virtual ~GLObject() {} + + GrGLuint id() const { return fID; } + +private: + GrGLuint fID; +}; + +// This class maintains a sparsely populated array of object pointers. +template class TGLObjectManager { + static_assert(std::is_convertible::value, "T must be a subclass of GLObject"); + +public: + TGLObjectManager() : fFreeListHead(kFreeListEnd) { + *fGLObjects.append() = nullptr; // 0 is not a valid GL object id. + } + + ~TGLObjectManager() { + // nullptr out the entries that are really free list links rather than ptrs before deleting. + intptr_t curr = fFreeListHead; + while (kFreeListEnd != curr) { + intptr_t next = reinterpret_cast(fGLObjects[SkToS32(curr)]); + fGLObjects[SkToS32(curr)] = nullptr; + curr = next; + } + + fGLObjects.safeUnrefAll(); + } + + T* lookUp(GrGLuint id) { + T* object = fGLObjects[id]; + SkASSERT(object && object->id() == id); + return object; + } + + T* create() { + GrGLuint id; + T* object; + + if (kFreeListEnd == fFreeListHead) { + // no free slots - create a new one + id = fGLObjects.count(); + object = new T(id); + *fGLObjects.append() = object; + } else { + // grab the head of the free list and advance the head to the next free slot. + id = static_cast(fFreeListHead); + fFreeListHead = reinterpret_cast(fGLObjects[id]); + + object = new T(id); + fGLObjects[id] = object; + } + + return object; + } + + void free(T* object) { + SkASSERT(object); + SkASSERT(fGLObjects.count() > 0); + + GrGLuint id = object->id(); + object->unref(); + + fGLObjects[id] = reinterpret_cast(fFreeListHead); + fFreeListHead = id; + } + +private: + static const intptr_t kFreeListEnd = -1; + // Index of the first entry of fGLObjects in the free list. Free slots in fGLObjects are indices + // to the next free slot. The last free slot has a value of kFreeListEnd. + intptr_t fFreeListHead; + SkTDArray fGLObjects; +}; + +class Buffer : public GLObject { +public: + Buffer(GrGLuint id) : INHERITED(id), fDataPtr(nullptr), fSize(0), fMapped(false) {} + ~Buffer() { delete[] fDataPtr; } + + void allocate(GrGLsizeiptr size, const GrGLchar* dataPtr) { + if (fDataPtr) { + SkASSERT(0 != fSize); + delete[] fDataPtr; + } + + fSize = size; + fDataPtr = new char[size]; + } + + GrGLchar* dataPtr() { return fDataPtr; } + GrGLsizeiptr size() const { return fSize; } + + void setMapped(bool mapped) { fMapped = mapped; } + bool mapped() const { return fMapped; } + +private: + GrGLchar* fDataPtr; + GrGLsizeiptr fSize; // size in bytes + bool fMapped; + + typedef GLObject INHERITED; +}; + +class FramebufferAttachment : public GLObject { +public: + int numSamples() const { return fNumSamples; } + +protected: + FramebufferAttachment(int id) : INHERITED(id), fNumSamples(1) {} + + int fNumSamples; + + typedef GLObject INHERITED; +}; + +class Renderbuffer : public FramebufferAttachment { +public: + Renderbuffer(int id) : INHERITED(id) {} + void setNumSamples(int numSamples) { fNumSamples = numSamples; } + +private: + typedef FramebufferAttachment INHERITED; +}; + +class Texture : public FramebufferAttachment { +public: + Texture() : INHERITED(1) {} + +private: + typedef FramebufferAttachment INHERITED; +}; + +class Framebuffer : public GLObject { +public: + Framebuffer(int id) : INHERITED(id) {} + + void setAttachment(GrGLenum attachmentPoint, const FramebufferAttachment* attachment) { + switch (attachmentPoint) { + default: + SK_ABORT("Invalid framebuffer attachment."); + break; + case GR_GL_STENCIL_ATTACHMENT: + fAttachments[(int)AttachmentPoint::kStencil].reset(SkRef(attachment)); + break; + case GR_GL_DEPTH_ATTACHMENT: + fAttachments[(int)AttachmentPoint::kDepth].reset(SkRef(attachment)); + break; + case GR_GL_COLOR_ATTACHMENT0: + fAttachments[(int)AttachmentPoint::kColor].reset(SkRef(attachment)); + break; + } + } + + void notifyAttachmentDeleteWhileBound(const FramebufferAttachment* deleted) { + for (auto& attachment : fAttachments) { + if (attachment.get() == deleted) { + attachment.reset(nullptr); + } + } + } + + int numSamples() const { + int numSamples = 0; + for (auto& attachment : fAttachments) { + if (!attachment) { + continue; + } + if (numSamples) { + GrAlwaysAssert(attachment->numSamples() == numSamples); + continue; + } + numSamples = attachment->numSamples(); + } + GrAlwaysAssert(numSamples); + return numSamples; + } + +private: + enum AttachmentPoint { + kStencil, + kDepth, + kColor + }; + constexpr int static kNumAttachmentPoints = 1 + (int)AttachmentPoint::kColor; + + sk_sp fAttachments[kNumAttachmentPoints]; + + typedef GLObject INHERITED; +}; + +/** Null interface implementation */ +class NullInterface : public GrGLTestInterface { +public: + NullInterface(bool enableNVPR) + : fCurrDrawFramebuffer(0) + , fCurrReadFramebuffer(0) + , fCurrRenderbuffer(0) + , fCurrProgramID(0) + , fCurrShaderID(0) + , fCurrGenericID(0) + , fCurrUniformLocation(0) + , fCurrPathID(0) { + memset(fBoundBuffers, 0, sizeof(fBoundBuffers)); + fAdvertisedExtensions.push_back("GL_ARB_framebuffer_object"); + fAdvertisedExtensions.push_back("GL_ARB_blend_func_extended"); + fAdvertisedExtensions.push_back("GL_ARB_timer_query"); + fAdvertisedExtensions.push_back("GL_ARB_draw_buffers"); + fAdvertisedExtensions.push_back("GL_ARB_occlusion_query"); + fAdvertisedExtensions.push_back("GL_EXT_stencil_wrap"); + if (enableNVPR) { + fAdvertisedExtensions.push_back("GL_NV_path_rendering"); + fAdvertisedExtensions.push_back("GL_ARB_program_interface_query"); + } + fAdvertisedExtensions.push_back(nullptr); + + this->init(kGL_GrGLStandard); + } + + GrGLenum checkFramebufferStatus(GrGLenum target) override { + return GR_GL_FRAMEBUFFER_COMPLETE; + } + + GrGLvoid genBuffers(GrGLsizei n, GrGLuint* ids) override { + for (int i = 0; i < n; ++i) { + Buffer* buffer = fBufferManager.create(); + ids[i] = buffer->id(); + } + } + + GrGLvoid bufferData(GrGLenum target, GrGLsizeiptr size, const GrGLvoid* data, + GrGLenum usage) override { + GrGLuint id = fBoundBuffers[GetBufferIndex(target)]; + if (id > 0) { + Buffer* buffer = fBufferManager.lookUp(id); + buffer->allocate(size, (const GrGLchar*) data); + } + } + + GrGLuint createProgram() override { + return ++fCurrProgramID; + } + + GrGLuint createShader(GrGLenum type) override { + return ++fCurrShaderID; + } + + GrGLvoid bindBuffer(GrGLenum target, GrGLuint buffer) override { + fBoundBuffers[GetBufferIndex(target)] = buffer; + } + + // deleting a bound buffer has the side effect of binding 0 + GrGLvoid deleteBuffers(GrGLsizei n, const GrGLuint* ids) override { + // First potentially unbind the buffers. + for (int buffIdx = 0; buffIdx < kNumBufferTargets; ++buffIdx) { + if (!fBoundBuffers[buffIdx]) { + continue; + } + for (int i = 0; i < n; ++i) { + if (ids[i] == fBoundBuffers[buffIdx]) { + fBoundBuffers[buffIdx] = 0; + break; + } + } + } + + // Then actually "delete" the buffers. + for (int i = 0; i < n; ++i) { + if (ids[i] > 0) { + Buffer* buffer = fBufferManager.lookUp(ids[i]); + fBufferManager.free(buffer); + } + } + } + + GrGLvoid genFramebuffers(GrGLsizei n, GrGLuint *framebuffers) override { + for (int i = 0; i < n; ++i) { + Framebuffer* framebuffer = fFramebufferManager.create(); + framebuffers[i] = framebuffer->id(); + } + } + + GrGLvoid bindFramebuffer(GrGLenum target, GrGLuint framebuffer) override { + SkASSERT(GR_GL_FRAMEBUFFER == target || GR_GL_DRAW_FRAMEBUFFER == target || + GR_GL_READ_FRAMEBUFFER == target); + if (GR_GL_READ_FRAMEBUFFER != target) { + fCurrDrawFramebuffer = framebuffer; + } + if (GR_GL_DRAW_FRAMEBUFFER != target) { + fCurrReadFramebuffer = framebuffer; + } + } + + GrGLvoid deleteFramebuffers(GrGLsizei n, const GrGLuint* ids) override { + for (int i = 0; i < n; ++i) { + if (ids[i] == fCurrDrawFramebuffer) { + fCurrDrawFramebuffer = 0; + } + if (ids[i] == fCurrReadFramebuffer) { + fCurrReadFramebuffer = 0; + } + + if (ids[i] > 0) { + Framebuffer* framebuffer = fFramebufferManager.lookUp(ids[i]); + fFramebufferManager.free(framebuffer); + } + } + } + + GrGLvoid genQueries(GrGLsizei n, GrGLuint *ids) override { this->genGenericIds(n, ids); } + + GrGLvoid genRenderbuffers(GrGLsizei n, GrGLuint *renderbuffers) override { + for (int i = 0; i < n; ++i) { + Renderbuffer* renderbuffer = fRenderbufferManager.create(); + renderbuffers[i] = renderbuffer->id(); + } + } + + GrGLvoid bindRenderbuffer(GrGLenum target, GrGLuint renderbuffer) override { + SkASSERT(GR_GL_RENDERBUFFER == target); + fCurrRenderbuffer = renderbuffer; + } + + GrGLvoid deleteRenderbuffers(GrGLsizei n, const GrGLuint* ids) override { + for (int i = 0; i < n; ++i) { + if (ids[i] <= 0) { + continue; + } + if (ids[i] == fCurrRenderbuffer) { + fCurrRenderbuffer = 0; + } + Renderbuffer* renderbuffer = fRenderbufferManager.lookUp(ids[i]); + + if (fCurrDrawFramebuffer) { + Framebuffer* drawFramebuffer = fFramebufferManager.lookUp(fCurrDrawFramebuffer); + drawFramebuffer->notifyAttachmentDeleteWhileBound(renderbuffer); + } + if (fCurrReadFramebuffer) { + Framebuffer* readFramebuffer = fFramebufferManager.lookUp(fCurrReadFramebuffer); + readFramebuffer->notifyAttachmentDeleteWhileBound(renderbuffer); + } + + fRenderbufferManager.free(renderbuffer); + } + } + + GrGLvoid renderbufferStorage(GrGLenum target, GrGLenum internalformat, GrGLsizei width, + GrGLsizei height) override { + GrAlwaysAssert(GR_GL_RENDERBUFFER == target); + GrAlwaysAssert(fCurrRenderbuffer); + Renderbuffer* renderbuffer = fRenderbufferManager.lookUp(fCurrRenderbuffer); + renderbuffer->setNumSamples(1); + } + + GrGLvoid renderbufferStorageMultisample(GrGLenum target, GrGLsizei samples, + GrGLenum internalformat, GrGLsizei width, + GrGLsizei height) override { + GrAlwaysAssert(GR_GL_RENDERBUFFER == target); + GrAlwaysAssert(samples > 0); + GrAlwaysAssert(fCurrRenderbuffer); + Renderbuffer* renderbuffer = fRenderbufferManager.lookUp(fCurrRenderbuffer); + renderbuffer->setNumSamples(samples); + } + + GrGLvoid namedRenderbufferStorage(GrGLuint renderbuffer, GrGLenum GrGLinternalformat, + GrGLsizei width, GrGLsizei height) override { + SK_ABORT("Not implemented"); + } + + GrGLvoid namedRenderbufferStorageMultisample(GrGLuint renderbuffer, GrGLsizei samples, + GrGLenum GrGLinternalformat, GrGLsizei width, + GrGLsizei height) override { + SK_ABORT("Not implemented"); + } + + GrGLvoid framebufferRenderbuffer(GrGLenum target, GrGLenum attachment, + GrGLenum renderbuffertarget, + GrGLuint renderBufferID) override { + GrGLuint id = this->getBoundFramebufferID(target); + GrAlwaysAssert(id); + Framebuffer* framebuffer = fFramebufferManager.lookUp(id); + + GrAlwaysAssert(GR_GL_RENDERBUFFER == renderbuffertarget); + if (!renderBufferID && !fCurrRenderbuffer) { + return; + } + GrAlwaysAssert(fCurrRenderbuffer); + Renderbuffer* renderbuffer = fRenderbufferManager.lookUp(fCurrRenderbuffer); + + framebuffer->setAttachment(attachment, renderbuffer); + } + + GrGLvoid namedFramebufferRenderbuffer(GrGLuint framebuffer, GrGLenum attachment, + GrGLenum renderbuffertarget, + GrGLuint renderbuffer) override { + SK_ABORT("Not implemented"); + } + + GrGLvoid genSamplers(GrGLsizei n, GrGLuint* samplers) override { + this->genGenericIds(n, samplers); + } + + GrGLvoid genTextures(GrGLsizei n, GrGLuint *textures) override { + this->genGenericIds(n, textures); + } + + GrGLvoid framebufferTexture2D(GrGLenum target, GrGLenum attachment, GrGLenum textarget, + GrGLuint textureID, GrGLint level) override { + GrGLuint id = this->getBoundFramebufferID(target); + GrAlwaysAssert(id); + Framebuffer* framebuffer = fFramebufferManager.lookUp(id); + framebuffer->setAttachment(attachment, this->getSingleTextureObject()); + } + + GrGLvoid framebufferTexture2DMultisample(GrGLenum target, GrGLenum attachment, + GrGLenum textarget, GrGLuint texture, GrGLint level, + GrGLsizei samples) override { + SK_ABORT("Not implemented"); + } + + GrGLvoid namedFramebufferTexture1D(GrGLuint framebuffer, GrGLenum attachment, + GrGLenum textarget, GrGLuint texture, + GrGLint level) override { + SK_ABORT("Not implemented"); + } + + GrGLvoid namedFramebufferTexture2D(GrGLuint framebuffer, GrGLenum attachment, + GrGLenum textarget, GrGLuint texture, + GrGLint level) override { + SK_ABORT("Not implemented"); + } + + GrGLvoid namedFramebufferTexture3D(GrGLuint framebuffer, GrGLenum attachment, + GrGLenum textarget, GrGLuint texture, GrGLint level, + GrGLint zoffset) override { + SK_ABORT("Not implemented"); + } + + GrGLvoid genVertexArrays(GrGLsizei n, GrGLuint *arrays) override { + this->genGenericIds(n, arrays); + } + + GrGLenum getError() override { return GR_GL_NO_ERROR; } + + GrGLvoid getIntegerv(GrGLenum pname, GrGLint* params) override { + // TODO: remove from Ganesh the #defines for gets we don't use. + // We would like to minimize gets overall due to performance issues + switch (pname) { + case GR_GL_CONTEXT_PROFILE_MASK: + *params = GR_GL_CONTEXT_COMPATIBILITY_PROFILE_BIT; + break; + case GR_GL_STENCIL_BITS: + *params = 8; + break; + case GR_GL_SAMPLES: { + GrAlwaysAssert(fCurrDrawFramebuffer); + Framebuffer* framebuffer = fFramebufferManager.lookUp(fCurrDrawFramebuffer); + *params = framebuffer->numSamples(); + break; + } + case GR_GL_FRAMEBUFFER_BINDING: + *params = 0; + break; + case GR_GL_VIEWPORT: + params[0] = 0; + params[1] = 0; + params[2] = 800; + params[3] = 600; + break; + case GR_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: + case GR_GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS: + case GR_GL_MAX_TEXTURE_IMAGE_UNITS: + case GR_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: + *params = 8; + break; + case GR_GL_MAX_TEXTURE_COORDS: + *params = 8; + break; + case GR_GL_MAX_VERTEX_UNIFORM_VECTORS: + *params = kDefaultMaxVertexUniformVectors; + break; + case GR_GL_MAX_FRAGMENT_UNIFORM_VECTORS: + *params = kDefaultMaxFragmentUniformVectors; + break; + case GR_GL_MAX_FRAGMENT_UNIFORM_COMPONENTS: + *params = 16 * 4; + break; + case GR_GL_NUM_COMPRESSED_TEXTURE_FORMATS: + *params = 0; + break; + case GR_GL_COMPRESSED_TEXTURE_FORMATS: + break; + case GR_GL_MAX_TEXTURE_SIZE: + *params = 8192; + break; + case GR_GL_MAX_RENDERBUFFER_SIZE: + *params = 8192; + break; + case GR_GL_MAX_SAMPLES: + *params = 32; + break; + case GR_GL_MAX_VERTEX_ATTRIBS: + *params = kDefaultMaxVertexAttribs; + break; + case GR_GL_MAX_VARYING_VECTORS: + *params = kDefaultMaxVaryingVectors; + break; + case GR_GL_NUM_EXTENSIONS: { + GrGLint i = 0; + while (fAdvertisedExtensions[i++]); + *params = i; + break; + } + default: + SK_ABORT("Unexpected pname to GetIntegerv"); + } + } + + GrGLvoid getProgramiv(GrGLuint program, GrGLenum pname, GrGLint* params) override { + this->getShaderOrProgramiv(program, pname, params); + } + + GrGLvoid getProgramInfoLog(GrGLuint program, GrGLsizei bufsize, GrGLsizei* length, + char* infolog) override { + this->getInfoLog(program, bufsize, length, infolog); + } + + GrGLvoid getMultisamplefv(GrGLenum pname, GrGLuint index, GrGLfloat* val) override { + val[0] = val[1] = 0.5f; + } + + GrGLvoid getQueryiv(GrGLenum GLtarget, GrGLenum pname, GrGLint *params) override { + switch (pname) { + case GR_GL_CURRENT_QUERY: + *params = 0; + break; + case GR_GL_QUERY_COUNTER_BITS: + *params = 32; + break; + default: + SK_ABORT("Unexpected pname passed GetQueryiv."); + } + } + + GrGLvoid getQueryObjecti64v(GrGLuint id, GrGLenum pname, GrGLint64 *params) override { + this->queryResult(id, pname, params); + } + + GrGLvoid getQueryObjectiv(GrGLuint id, GrGLenum pname, GrGLint *params) override { + this->queryResult(id, pname, params); + } + + GrGLvoid getQueryObjectui64v(GrGLuint id, GrGLenum pname, GrGLuint64 *params) override { + this->queryResult(id, pname, params); + } + + GrGLvoid getQueryObjectuiv(GrGLuint id, GrGLenum pname, GrGLuint *params) override { + this->queryResult(id, pname, params); + } + + GrGLvoid getShaderiv(GrGLuint shader, GrGLenum pname, GrGLint* params) override { + this->getShaderOrProgramiv(shader, pname, params); + } + + GrGLvoid getShaderInfoLog(GrGLuint shader, GrGLsizei bufsize, GrGLsizei* length, + char* infolog) override { + this->getInfoLog(shader, bufsize, length, infolog); + } + + const GrGLubyte* getString(GrGLenum name) override { + switch (name) { + case GR_GL_EXTENSIONS: + return CombinedExtensionString(); + case GR_GL_VERSION: + return (const GrGLubyte*)"4.0 Null GL"; + case GR_GL_SHADING_LANGUAGE_VERSION: + return (const GrGLubyte*)"4.20.8 Null GLSL"; + case GR_GL_VENDOR: + return (const GrGLubyte*)"Null Vendor"; + case GR_GL_RENDERER: + return (const GrGLubyte*)"The Null (Non-)Renderer"; + default: + SK_ABORT("Unexpected name passed to GetString"); + return nullptr; + } + } + + const GrGLubyte* getStringi(GrGLenum name, GrGLuint i) override { + switch (name) { + case GR_GL_EXTENSIONS: { + GrGLint count; + this->getIntegerv(GR_GL_NUM_EXTENSIONS, &count); + if ((GrGLint)i <= count) { + return (const GrGLubyte*) fAdvertisedExtensions[i]; + } else { + return nullptr; + } + } + default: + SK_ABORT("Unexpected name passed to GetStringi"); + return nullptr; + } + } + + GrGLint getUniformLocation(GrGLuint program, const char* name) override { + return ++fCurrUniformLocation; + } + + GrGLvoid* mapBufferRange(GrGLenum target, GrGLintptr offset, GrGLsizeiptr length, + GrGLbitfield access) override { + GrGLuint id = fBoundBuffers[GetBufferIndex(target)]; + if (id > 0) { + // We just ignore the offset and length here. + Buffer* buffer = fBufferManager.lookUp(id); + SkASSERT(!buffer->mapped()); + buffer->setMapped(true); + return buffer->dataPtr(); + } + return nullptr; + } + + GrGLvoid* mapBuffer(GrGLenum target, GrGLenum access) override { + GrGLuint id = fBoundBuffers[GetBufferIndex(target)]; + if (id > 0) { + Buffer* buffer = fBufferManager.lookUp(id); + SkASSERT(!buffer->mapped()); + buffer->setMapped(true); + return buffer->dataPtr(); + } + + SkASSERT(false); + return nullptr; // no buffer bound to target + } + + GrGLboolean unmapBuffer(GrGLenum target) override { + GrGLuint id = fBoundBuffers[GetBufferIndex(target)]; + if (id > 0) { + Buffer* buffer = fBufferManager.lookUp(id); + SkASSERT(buffer->mapped()); + buffer->setMapped(false); + return GR_GL_TRUE; + } + + GrAlwaysAssert(false); + return GR_GL_FALSE; // GR_GL_INVALID_OPERATION; + } + + GrGLvoid getBufferParameteriv(GrGLenum target, GrGLenum pname, GrGLint* params) override { + switch (pname) { + case GR_GL_BUFFER_MAPPED: { + *params = GR_GL_FALSE; + GrGLuint id = fBoundBuffers[GetBufferIndex(target)]; + if (id > 0) { + Buffer* buffer = fBufferManager.lookUp(id); + if (buffer->mapped()) { + *params = GR_GL_TRUE; + } + } + break; } + default: + SK_ABORT("Unexpected pname to GetBufferParamateriv"); + break; + } + } + + // NV_path_rendering + GrGLuint genPaths(GrGLsizei range) override { + return ++fCurrPathID; + } + + +private: + inline int static GetBufferIndex(GrGLenum glTarget) { + switch (glTarget) { + default: SK_ABORT("Unexpected GL target to GetBufferIndex"); + case GR_GL_ARRAY_BUFFER: return 0; + case GR_GL_ELEMENT_ARRAY_BUFFER: return 1; + case GR_GL_TEXTURE_BUFFER: return 2; + case GR_GL_DRAW_INDIRECT_BUFFER: return 3; + case GR_GL_PIXEL_PACK_BUFFER: return 4; + case GR_GL_PIXEL_UNPACK_BUFFER: return 5; + } + } + constexpr int static kNumBufferTargets = 6; + + TGLObjectManager fBufferManager; + GrGLuint fBoundBuffers[kNumBufferTargets]; + TGLObjectManager fFramebufferManager; + GrGLuint fCurrDrawFramebuffer; + GrGLuint fCurrReadFramebuffer; + TGLObjectManager fRenderbufferManager; + GrGLuint fCurrRenderbuffer; + GrGLuint fCurrProgramID; + GrGLuint fCurrShaderID; + GrGLuint fCurrGenericID; + GrGLuint fCurrUniformLocation; + GrGLuint fCurrPathID; + sk_sp fSingleTextureObject; + SkTArray fAdvertisedExtensions; + + // the OpenGLES 2.0 spec says this must be >= 128 + static const GrGLint kDefaultMaxVertexUniformVectors = 128; + + // the OpenGLES 2.0 spec says this must be >=16 + static const GrGLint kDefaultMaxFragmentUniformVectors = 16; + + // the OpenGLES 2.0 spec says this must be >= 8 + static const GrGLint kDefaultMaxVertexAttribs = 8; + + // the OpenGLES 2.0 spec says this must be >= 8 + static const GrGLint kDefaultMaxVaryingVectors = 8; + + GrGLuint getBoundFramebufferID(GrGLenum target) { + switch (target) { + case GR_GL_FRAMEBUFFER: + case GR_GL_DRAW_FRAMEBUFFER: + return fCurrDrawFramebuffer; + case GR_GL_READ_FRAMEBUFFER: + return fCurrReadFramebuffer; + default: + SK_ABORT("Invalid framebuffer target."); + return 0; + } + } + + const Texture* getSingleTextureObject() { + // We currently only use FramebufferAttachment objects for a sample count, and all textures + // in Skia have one sample, so there is no need as of yet to track individual textures. This + // also works around a bug in chromium's cc_unittests where they send us texture IDs that + // were generated by cc::TestGLES2Interface. + if (!fSingleTextureObject) { + fSingleTextureObject.reset(new Texture); + } + return fSingleTextureObject.get(); + } + + const GrGLubyte* CombinedExtensionString() { + static SkString gExtString; + static SkMutex gMutex; + gMutex.acquire(); + if (0 == gExtString.size()) { + int i = 0; + while (fAdvertisedExtensions[i]) { + if (i > 0) { + gExtString.append(" "); + } + gExtString.append(fAdvertisedExtensions[i]); + ++i; + } + } + gMutex.release(); + return (const GrGLubyte*) gExtString.c_str(); + } + + GrGLvoid genGenericIds(GrGLsizei n, GrGLuint* ids) { + for (int i = 0; i < n; ++i) { + ids[i] = ++fCurrGenericID; + } + } + + GrGLvoid getInfoLog(GrGLuint object, GrGLsizei bufsize, GrGLsizei* length, + char* infolog) { + if (length) { + *length = 0; + } + if (bufsize > 0) { + *infolog = 0; + } + } + + GrGLvoid getShaderOrProgramiv(GrGLuint object, GrGLenum pname, GrGLint* params) { + switch (pname) { + case GR_GL_LINK_STATUS: // fallthru + case GR_GL_COMPILE_STATUS: + *params = GR_GL_TRUE; + break; + case GR_GL_INFO_LOG_LENGTH: // fallthru + case GL_PROGRAM_BINARY_LENGTH: + *params = 0; + break; + // we don't expect any other pnames + default: + SK_ABORT("Unexpected pname to GetProgramiv"); + break; + } + } + + template + void queryResult(GrGLenum GLtarget, GrGLenum pname, T *params) { + switch (pname) { + case GR_GL_QUERY_RESULT_AVAILABLE: + *params = GR_GL_TRUE; + break; + case GR_GL_QUERY_RESULT: + *params = 0; + break; + default: + SK_ABORT("Unexpected pname passed to GetQueryObject."); + break; + } + } + + typedef GrGLTestInterface INHERITED; +}; + +} // anonymous namespace + +namespace android { +namespace uirenderer { +namespace debug { + +const GrGLInterface* CreateNullSkiaInterface() { return new NullInterface(false); } + +} // namespace debug +} // namespace uirenderer +} // namespace android -- cgit v1.2.3 From 7fb4c9e4bb10e17d28a09bd84d364171359a1941 Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Tue, 9 Apr 2019 08:55:28 -0400 Subject: Ensure that hardware Bitmap has a ColorSpace Bug: 129355537 Test: I9e004fbb7c966bb58ae374087fecd66a1bb72346 android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode in SkiaGL could create an android::Bitmap without an SkColorSpace. Treat HAL_DATASPACE_UNKNOWN as sRGB, as this is how we treat it internally. Change-Id: Iad7f7d7cafce0a8759a84a4296ae5c3ce86dff4c --- libs/hwui/utils/Color.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'libs') diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp index d14116f7b555..39740bd46f9f 100644 --- a/libs/hwui/utils/Color.cpp +++ b/libs/hwui/utils/Color.cpp @@ -60,6 +60,9 @@ SkColorType PixelFormatToColorType(android::PixelFormat format) { } sk_sp DataSpaceToColorSpace(android_dataspace dataspace) { + if (dataspace == HAL_DATASPACE_UNKNOWN) { + return SkColorSpace::MakeSRGB(); + } skcms_Matrix3x3 gamut; switch (dataspace & HAL_DATASPACE_STANDARD_MASK) { -- cgit v1.2.3 From eae47f42d339d5c9f05761121ee84fc9f77c8a92 Mon Sep 17 00:00:00 2001 From: Brian Osman Date: Tue, 9 Apr 2019 13:02:22 -0400 Subject: Copy Skia's GrGLTestInterface locally (it's deleted at HEAD) Test: Test still compiles & passes Change-Id: I7e8239bedee5034e2489acbb95756b88c801b1c5 --- libs/hwui/debug/NullSkiaInterface.cpp | 556 +++++++++++++++++++++++++++++++++- 1 file changed, 553 insertions(+), 3 deletions(-) (limited to 'libs') diff --git a/libs/hwui/debug/NullSkiaInterface.cpp b/libs/hwui/debug/NullSkiaInterface.cpp index b5438cb3c10e..d141b9c0591b 100644 --- a/libs/hwui/debug/NullSkiaInterface.cpp +++ b/libs/hwui/debug/NullSkiaInterface.cpp @@ -18,11 +18,11 @@ // deprecated). The NullGlesDriver should be constructing a GrGLInterface that calls *its* // GL functions! -#include "gl/GrGLTestInterface.h" #include "GrNonAtomicRef.h" #include "SkMutex.h" #include "SkTDArray.h" #include "SkTo.h" +#include "gl/GrGLDefines.h" #include "gl/GrGLInterface.h" #include @@ -225,8 +225,558 @@ private: typedef GLObject INHERITED; }; +class TestInterface : public GrGLInterface { +public: + virtual GrGLvoid activeTexture(GrGLenum texture) {} + virtual GrGLvoid attachShader(GrGLuint program, GrGLuint shader) {} + virtual GrGLvoid beginQuery(GrGLenum target, GrGLuint id) {} + virtual GrGLvoid bindAttribLocation(GrGLuint program, GrGLuint index, const char* name) {} + virtual GrGLvoid bindBuffer(GrGLenum target, GrGLuint buffer) {} + virtual GrGLvoid bindFramebuffer(GrGLenum target, GrGLuint framebuffer) {} + virtual GrGLvoid bindRenderbuffer(GrGLenum target, GrGLuint renderbuffer) {} + virtual GrGLvoid bindSampler(GrGLuint unit, GrGLuint sampler) {} + virtual GrGLvoid bindTexture(GrGLenum target, GrGLuint texture) {} + virtual GrGLvoid bindFragDataLocation(GrGLuint program, GrGLuint colorNumber, const GrGLchar* name) {} + virtual GrGLvoid bindFragDataLocationIndexed(GrGLuint program, GrGLuint colorNumber, GrGLuint index, const GrGLchar * name) {} + virtual GrGLvoid bindVertexArray(GrGLuint array) {} + virtual GrGLvoid blendBarrier() {} + virtual GrGLvoid blendColor(GrGLclampf red, GrGLclampf green, GrGLclampf blue, GrGLclampf alpha) {} + virtual GrGLvoid blendEquation(GrGLenum mode) {} + virtual GrGLvoid blendFunc(GrGLenum sfactor, GrGLenum dfactor) {} + virtual GrGLvoid blitFramebuffer(GrGLint srcX0, GrGLint srcY0, GrGLint srcX1, GrGLint srcY1, GrGLint dstX0, GrGLint dstY0, GrGLint dstX1, GrGLint dstY1, GrGLbitfield mask, GrGLenum filter) {} + virtual GrGLvoid bufferData(GrGLenum target, GrGLsizeiptr size, const GrGLvoid* data, GrGLenum usage) {} + virtual GrGLvoid bufferSubData(GrGLenum target, GrGLintptr offset, GrGLsizeiptr size, const GrGLvoid* data) {} + virtual GrGLenum checkFramebufferStatus(GrGLenum target) { return GR_GL_FRAMEBUFFER_COMPLETE; } + virtual GrGLvoid clear(GrGLbitfield mask) {} + virtual GrGLvoid clearColor(GrGLclampf red, GrGLclampf green, GrGLclampf blue, GrGLclampf alpha) {} + virtual GrGLvoid clearStencil(GrGLint s) {} + virtual GrGLvoid colorMask(GrGLboolean red, GrGLboolean green, GrGLboolean blue, GrGLboolean alpha) {} + virtual GrGLvoid compileShader(GrGLuint shader) {} + virtual GrGLvoid compressedTexImage2D(GrGLenum target, GrGLint level, GrGLenum internalformat, GrGLsizei width, GrGLsizei height, GrGLint border, GrGLsizei imageSize, const GrGLvoid* data) {} + virtual GrGLvoid compressedTexSubImage2D(GrGLenum target, GrGLint level, GrGLint xoffset, GrGLint yoffset, GrGLsizei width, GrGLsizei height, GrGLenum format, GrGLsizei imageSize, const GrGLvoid* data) {} + virtual GrGLvoid copyTexSubImage2D(GrGLenum target, GrGLint level, GrGLint xoffset, GrGLint yoffset, GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height) {} + virtual GrGLuint createProgram() { return 0; } + virtual GrGLuint createShader(GrGLenum type) { return 0; } + virtual GrGLvoid cullFace(GrGLenum mode) {} + virtual GrGLvoid deleteBuffers(GrGLsizei n, const GrGLuint* buffers) {} + virtual GrGLvoid deleteFramebuffers(GrGLsizei n, const GrGLuint *framebuffers) {} + virtual GrGLvoid deleteProgram(GrGLuint program) {} + virtual GrGLvoid deleteQueries(GrGLsizei n, const GrGLuint *ids) {} + virtual GrGLvoid deleteRenderbuffers(GrGLsizei n, const GrGLuint *renderbuffers) {} + virtual GrGLvoid deleteSamplers(GrGLsizei n, const GrGLuint* samplers) {} + virtual GrGLvoid deleteShader(GrGLuint shader) {} + virtual GrGLvoid deleteTextures(GrGLsizei n, const GrGLuint* textures) {} + virtual GrGLvoid deleteVertexArrays(GrGLsizei n, const GrGLuint *arrays) {} + virtual GrGLvoid depthMask(GrGLboolean flag) {} + virtual GrGLvoid disable(GrGLenum cap) {} + virtual GrGLvoid disableVertexAttribArray(GrGLuint index) {} + virtual GrGLvoid drawArrays(GrGLenum mode, GrGLint first, GrGLsizei count) {} + virtual GrGLvoid drawArraysInstanced(GrGLenum mode, GrGLint first, GrGLsizei count, GrGLsizei primcount) {} + virtual GrGLvoid drawArraysIndirect(GrGLenum mode, const GrGLvoid* indirect) {} + virtual GrGLvoid drawBuffer(GrGLenum mode) {} + virtual GrGLvoid drawBuffers(GrGLsizei n, const GrGLenum* bufs) {} + virtual GrGLvoid drawElements(GrGLenum mode, GrGLsizei count, GrGLenum type, const GrGLvoid* indices) {} + virtual GrGLvoid drawElementsInstanced(GrGLenum mode, GrGLsizei count, GrGLenum type, const GrGLvoid *indices, GrGLsizei primcount) {} + virtual GrGLvoid drawElementsIndirect(GrGLenum mode, GrGLenum type, const GrGLvoid* indirect) {} + virtual GrGLvoid drawRangeElements(GrGLenum mode, GrGLuint start, GrGLuint end, GrGLsizei count, GrGLenum type, const GrGLvoid* indices) {} + virtual GrGLvoid enable(GrGLenum cap) {} + virtual GrGLvoid enableVertexAttribArray(GrGLuint index) {} + virtual GrGLvoid endQuery(GrGLenum target) {} + virtual GrGLvoid finish() {} + virtual GrGLvoid flush() {} + virtual GrGLvoid flushMappedBufferRange(GrGLenum target, GrGLintptr offset, GrGLsizeiptr length) {} + virtual GrGLvoid framebufferRenderbuffer(GrGLenum target, GrGLenum attachment, GrGLenum renderbuffertarget, GrGLuint renderbuffer) {} + virtual GrGLvoid framebufferTexture2D(GrGLenum target, GrGLenum attachment, GrGLenum textarget, GrGLuint texture, GrGLint level) {} + virtual GrGLvoid framebufferTexture2DMultisample(GrGLenum target, GrGLenum attachment, GrGLenum textarget, GrGLuint texture, GrGLint level, GrGLsizei samples) {} + virtual GrGLvoid frontFace(GrGLenum mode) {} + virtual GrGLvoid genBuffers(GrGLsizei n, GrGLuint* buffers) {} + virtual GrGLvoid genFramebuffers(GrGLsizei n, GrGLuint *framebuffers) {} + virtual GrGLvoid generateMipmap(GrGLenum target) {} + virtual GrGLvoid genQueries(GrGLsizei n, GrGLuint *ids) {} + virtual GrGLvoid genRenderbuffers(GrGLsizei n, GrGLuint *renderbuffers) {} + virtual GrGLvoid genSamplers(GrGLsizei n, GrGLuint *samplers) {} + virtual GrGLvoid genTextures(GrGLsizei n, GrGLuint* textures) {} + virtual GrGLvoid genVertexArrays(GrGLsizei n, GrGLuint *arrays) {} + virtual GrGLvoid getBufferParameteriv(GrGLenum target, GrGLenum pname, GrGLint* params) {} + virtual GrGLenum getError() { return GR_GL_NO_ERROR; } + virtual GrGLvoid getFramebufferAttachmentParameteriv(GrGLenum target, GrGLenum attachment, GrGLenum pname, GrGLint* params) {} + virtual GrGLvoid getIntegerv(GrGLenum pname, GrGLint* params) {} + virtual GrGLvoid getMultisamplefv(GrGLenum pname, GrGLuint index, GrGLfloat* val) {} + virtual GrGLvoid getProgramInfoLog(GrGLuint program, GrGLsizei bufsize, GrGLsizei* length, char* infolog) {} + virtual GrGLvoid getProgramiv(GrGLuint program, GrGLenum pname, GrGLint* params) {} + virtual GrGLvoid getQueryiv(GrGLenum GLtarget, GrGLenum pname, GrGLint *params) {} + virtual GrGLvoid getQueryObjecti64v(GrGLuint id, GrGLenum pname, GrGLint64 *params) {} + virtual GrGLvoid getQueryObjectiv(GrGLuint id, GrGLenum pname, GrGLint *params) {} + virtual GrGLvoid getQueryObjectui64v(GrGLuint id, GrGLenum pname, GrGLuint64 *params) {} + virtual GrGLvoid getQueryObjectuiv(GrGLuint id, GrGLenum pname, GrGLuint *params) {} + virtual GrGLvoid getRenderbufferParameteriv(GrGLenum target, GrGLenum pname, GrGLint* params) {} + virtual GrGLvoid getShaderInfoLog(GrGLuint shader, GrGLsizei bufsize, GrGLsizei* length, char* infolog) {} + virtual GrGLvoid getShaderiv(GrGLuint shader, GrGLenum pname, GrGLint* params) {} + virtual GrGLvoid getShaderPrecisionFormat(GrGLenum shadertype, GrGLenum precisiontype, GrGLint *range, GrGLint *precision) {} + virtual const GrGLubyte* getString(GrGLenum name) { return nullptr; } + virtual const GrGLubyte* getStringi(GrGLenum name, GrGLuint index) { return nullptr; } + virtual GrGLvoid getTexLevelParameteriv(GrGLenum target, GrGLint level, GrGLenum pname, GrGLint* params) {} + virtual GrGLint getUniformLocation(GrGLuint program, const char* name) { return 0; } + virtual GrGLvoid insertEventMarker(GrGLsizei length, const char* marker) {} + virtual GrGLvoid invalidateBufferData(GrGLuint buffer) {} + virtual GrGLvoid invalidateBufferSubData(GrGLuint buffer, GrGLintptr offset, GrGLsizeiptr length) {} + virtual GrGLvoid invalidateFramebuffer(GrGLenum target, GrGLsizei numAttachments, const GrGLenum *attachments) {} + virtual GrGLvoid invalidateSubFramebuffer(GrGLenum target, GrGLsizei numAttachments, const GrGLenum *attachments, GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height) {} + virtual GrGLvoid invalidateTexImage(GrGLuint texture, GrGLint level) {} + virtual GrGLvoid invalidateTexSubImage(GrGLuint texture, GrGLint level, GrGLint xoffset, GrGLint yoffset, GrGLint zoffset, GrGLsizei width, GrGLsizei height, GrGLsizei depth) {} + virtual GrGLboolean isTexture(GrGLuint texture) { return GR_GL_FALSE; } + virtual GrGLvoid lineWidth(GrGLfloat width) {} + virtual GrGLvoid linkProgram(GrGLuint program) {} + virtual GrGLvoid* mapBuffer(GrGLenum target, GrGLenum access) { return nullptr; } + virtual GrGLvoid* mapBufferRange(GrGLenum target, GrGLintptr offset, GrGLsizeiptr length, GrGLbitfield access) { return nullptr; } + virtual GrGLvoid* mapBufferSubData(GrGLuint target, GrGLintptr offset, GrGLsizeiptr size, GrGLenum access) { return nullptr; } + virtual GrGLvoid* mapTexSubImage2D(GrGLenum target, GrGLint level, GrGLint xoffset, GrGLint yoffset, GrGLsizei width, GrGLsizei height, GrGLenum format, GrGLenum type, GrGLenum access) { return nullptr; } + virtual GrGLvoid pixelStorei(GrGLenum pname, GrGLint param) {} + virtual GrGLvoid polygonMode(GrGLenum face, GrGLenum mode) {} + virtual GrGLvoid popGroupMarker() {} + virtual GrGLvoid pushGroupMarker(GrGLsizei length, const char* marker) {} + virtual GrGLvoid queryCounter(GrGLuint id, GrGLenum target) {} + virtual GrGLvoid rasterSamples(GrGLuint samples, GrGLboolean fixedsamplelocations) {} + virtual GrGLvoid readBuffer(GrGLenum src) {} + virtual GrGLvoid readPixels(GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height, GrGLenum format, GrGLenum type, GrGLvoid* pixels) {} + virtual GrGLvoid renderbufferStorage(GrGLenum target, GrGLenum internalformat, GrGLsizei width, GrGLsizei height) {} + virtual GrGLvoid renderbufferStorageMultisample(GrGLenum target, GrGLsizei samples, GrGLenum internalformat, GrGLsizei width, GrGLsizei height) {} + virtual GrGLvoid resolveMultisampleFramebuffer() {} + virtual GrGLvoid samplerParameteri(GrGLuint sampler, GrGLenum pname, GrGLint param) {} + virtual GrGLvoid samplerParameteriv(GrGLuint sampler, GrGLenum pname, const GrGLint* param) {} + virtual GrGLvoid scissor(GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height) {} + virtual GrGLvoid bindUniformLocation(GrGLuint program, GrGLint location, const char* name) {} + virtual GrGLvoid shaderSource(GrGLuint shader, GrGLsizei count, const char* const * str, const GrGLint* length) {} + virtual GrGLvoid stencilFunc(GrGLenum func, GrGLint ref, GrGLuint mask) {} + virtual GrGLvoid stencilFuncSeparate(GrGLenum face, GrGLenum func, GrGLint ref, GrGLuint mask) {} + virtual GrGLvoid stencilMask(GrGLuint mask) {} + virtual GrGLvoid stencilMaskSeparate(GrGLenum face, GrGLuint mask) {} + virtual GrGLvoid stencilOp(GrGLenum fail, GrGLenum zfail, GrGLenum zpass) {} + virtual GrGLvoid stencilOpSeparate(GrGLenum face, GrGLenum fail, GrGLenum zfail, GrGLenum zpass) {} + virtual GrGLvoid texBuffer(GrGLenum target, GrGLenum internalformat, GrGLuint buffer) {} + virtual GrGLvoid texImage2D(GrGLenum target, GrGLint level, GrGLint internalformat, GrGLsizei width, GrGLsizei height, GrGLint border, GrGLenum format, GrGLenum type, const GrGLvoid* pixels) {} + virtual GrGLvoid texParameterf(GrGLenum target, GrGLenum pname, GrGLfloat param) {} + virtual GrGLvoid texParameterfv(GrGLenum target, GrGLenum pname, const GrGLfloat* params) {} + virtual GrGLvoid texParameteri(GrGLenum target, GrGLenum pname, GrGLint param) {} + virtual GrGLvoid texParameteriv(GrGLenum target, GrGLenum pname, const GrGLint* params) {} + virtual GrGLvoid texStorage2D(GrGLenum target, GrGLsizei levels, GrGLenum internalformat, GrGLsizei width, GrGLsizei height) {} + virtual GrGLvoid discardFramebuffer(GrGLenum target, GrGLsizei numAttachments, const GrGLenum* attachments) {} + virtual GrGLvoid texSubImage2D(GrGLenum target, GrGLint level, GrGLint xoffset, GrGLint yoffset, GrGLsizei width, GrGLsizei height, GrGLenum format, GrGLenum type, const GrGLvoid* pixels) {} + virtual GrGLvoid textureBarrier() {} + virtual GrGLvoid uniform1f(GrGLint location, GrGLfloat v0) {} + virtual GrGLvoid uniform1i(GrGLint location, GrGLint v0) {} + virtual GrGLvoid uniform1fv(GrGLint location, GrGLsizei count, const GrGLfloat* v) {} + virtual GrGLvoid uniform1iv(GrGLint location, GrGLsizei count, const GrGLint* v) {} + virtual GrGLvoid uniform2f(GrGLint location, GrGLfloat v0, GrGLfloat v1) {} + virtual GrGLvoid uniform2i(GrGLint location, GrGLint v0, GrGLint v1) {} + virtual GrGLvoid uniform2fv(GrGLint location, GrGLsizei count, const GrGLfloat* v) {} + virtual GrGLvoid uniform2iv(GrGLint location, GrGLsizei count, const GrGLint* v) {} + virtual GrGLvoid uniform3f(GrGLint location, GrGLfloat v0, GrGLfloat v1, GrGLfloat v2) {} + virtual GrGLvoid uniform3i(GrGLint location, GrGLint v0, GrGLint v1, GrGLint v2) {} + virtual GrGLvoid uniform3fv(GrGLint location, GrGLsizei count, const GrGLfloat* v) {} + virtual GrGLvoid uniform3iv(GrGLint location, GrGLsizei count, const GrGLint* v) {} + virtual GrGLvoid uniform4f(GrGLint location, GrGLfloat v0, GrGLfloat v1, GrGLfloat v2, GrGLfloat v3) {} + virtual GrGLvoid uniform4i(GrGLint location, GrGLint v0, GrGLint v1, GrGLint v2, GrGLint v3) {} + virtual GrGLvoid uniform4fv(GrGLint location, GrGLsizei count, const GrGLfloat* v) {} + virtual GrGLvoid uniform4iv(GrGLint location, GrGLsizei count, const GrGLint* v) {} + virtual GrGLvoid uniformMatrix2fv(GrGLint location, GrGLsizei count, GrGLboolean transpose, const GrGLfloat* value) {} + virtual GrGLvoid uniformMatrix3fv(GrGLint location, GrGLsizei count, GrGLboolean transpose, const GrGLfloat* value) {} + virtual GrGLvoid uniformMatrix4fv(GrGLint location, GrGLsizei count, GrGLboolean transpose, const GrGLfloat* value) {} + virtual GrGLboolean unmapBuffer(GrGLenum target) { return GR_GL_TRUE; } + virtual GrGLvoid unmapBufferSubData(const GrGLvoid* mem) {} + virtual GrGLvoid unmapTexSubImage2D(const GrGLvoid* mem) {} + virtual GrGLvoid useProgram(GrGLuint program) {} + virtual GrGLvoid vertexAttrib1f(GrGLuint indx, const GrGLfloat value) {} + virtual GrGLvoid vertexAttrib2fv(GrGLuint indx, const GrGLfloat* values) {} + virtual GrGLvoid vertexAttrib3fv(GrGLuint indx, const GrGLfloat* values) {} + virtual GrGLvoid vertexAttrib4fv(GrGLuint indx, const GrGLfloat* values) {} + virtual GrGLvoid vertexAttribDivisor(GrGLuint index, GrGLuint divisor) {} + virtual GrGLvoid vertexAttribIPointer(GrGLuint indx, GrGLint size, GrGLenum type, GrGLsizei stride, const GrGLvoid* ptr) {} + virtual GrGLvoid vertexAttribPointer(GrGLuint indx, GrGLint size, GrGLenum type, GrGLboolean normalized, GrGLsizei stride, const GrGLvoid* ptr) {} + virtual GrGLvoid viewport(GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height) {} + virtual GrGLvoid matrixLoadf(GrGLenum matrixMode, const GrGLfloat* m) {} + virtual GrGLvoid matrixLoadIdentity(GrGLenum) {} + virtual GrGLvoid pathCommands(GrGLuint path, GrGLsizei numCommands, const GrGLubyte *commands, GrGLsizei numCoords, GrGLenum coordType, const GrGLvoid *coords) {} + virtual GrGLvoid pathParameteri(GrGLuint path, GrGLenum pname, GrGLint value) {} + virtual GrGLvoid pathParameterf(GrGLuint path, GrGLenum pname, GrGLfloat value) {} + virtual GrGLuint genPaths(GrGLsizei range) { return 0; } + virtual GrGLvoid deletePaths(GrGLuint path, GrGLsizei range) {} + virtual GrGLboolean isPath(GrGLuint path) { return true; } + virtual GrGLvoid pathStencilFunc(GrGLenum func, GrGLint ref, GrGLuint mask) {} + virtual GrGLvoid stencilFillPath(GrGLuint path, GrGLenum fillMode, GrGLuint mask) {} + virtual GrGLvoid stencilStrokePath(GrGLuint path, GrGLint reference, GrGLuint mask) {} + virtual GrGLvoid stencilFillPathInstanced(GrGLsizei numPaths, GrGLenum pathNameType, const GrGLvoid *paths, GrGLuint pathBase, GrGLenum fillMode, GrGLuint mask, GrGLenum transformType, const GrGLfloat *transformValues) {} + virtual GrGLvoid stencilStrokePathInstanced(GrGLsizei numPaths, GrGLenum pathNameType, const GrGLvoid *paths, GrGLuint pathBase, GrGLint reference, GrGLuint mask, GrGLenum transformType, const GrGLfloat *transformValues) {} + virtual GrGLvoid coverFillPath(GrGLuint path, GrGLenum coverMode) {} + virtual GrGLvoid coverStrokePath(GrGLuint name, GrGLenum coverMode) {} + virtual GrGLvoid coverFillPathInstanced(GrGLsizei numPaths, GrGLenum pathNameType, const GrGLvoid *paths, GrGLuint pathBase, GrGLenum coverMode, GrGLenum transformType, const GrGLfloat *transformValues) {} + virtual GrGLvoid coverStrokePathInstanced(GrGLsizei numPaths, GrGLenum pathNameType, const GrGLvoid *paths, GrGLuint pathBase, GrGLenum coverMode, GrGLenum transformType, const GrGLfloat* transformValues) {} + virtual GrGLvoid stencilThenCoverFillPath(GrGLuint path, GrGLenum fillMode, GrGLuint mask, GrGLenum coverMode) {} + virtual GrGLvoid stencilThenCoverStrokePath(GrGLuint path, GrGLint reference, GrGLuint mask, GrGLenum coverMode) {} + virtual GrGLvoid stencilThenCoverFillPathInstanced(GrGLsizei numPaths, GrGLenum pathNameType, const GrGLvoid *paths, GrGLuint pathBase, GrGLenum fillMode, GrGLuint mask, GrGLenum coverMode, GrGLenum transformType, const GrGLfloat *transformValues) {} + virtual GrGLvoid stencilThenCoverStrokePathInstanced(GrGLsizei numPaths, GrGLenum pathNameType, const GrGLvoid *paths, GrGLuint pathBase, GrGLint reference, GrGLuint mask, GrGLenum coverMode, GrGLenum transformType, const GrGLfloat *transformValues) {} + virtual GrGLvoid programPathFragmentInputGen(GrGLuint program, GrGLint location, GrGLenum genMode, GrGLint components,const GrGLfloat *coeffs) {} + virtual GrGLvoid bindFragmentInputLocation(GrGLuint program, GrGLint location, const GrGLchar* name) {} + virtual GrGLint getProgramResourceLocation(GrGLuint program, GrGLenum programInterface, const GrGLchar *name) { return 0; } + virtual GrGLvoid coverageModulation(GrGLenum components) {} + virtual GrGLvoid multiDrawArraysIndirect(GrGLenum mode, const GrGLvoid *indirect, GrGLsizei drawcount, GrGLsizei stride) {} + virtual GrGLvoid multiDrawElementsIndirect(GrGLenum mode, GrGLenum type, const GrGLvoid *indirect, GrGLsizei drawcount, GrGLsizei stride) {} + virtual GrGLuint64 getTextureHandle(GrGLuint texture) { return 0; } + virtual GrGLuint64 getTextureSamplerHandle(GrGLuint texture, GrGLuint sampler) { return 0; } + virtual GrGLvoid makeTextureHandleResident(GrGLuint64 handle) {} + virtual GrGLvoid makeTextureHandleNonResident(GrGLuint64 handle) {} + virtual GrGLuint64 getImageHandle(GrGLuint texture, GrGLint level, GrGLboolean layered, GrGLint layer, GrGLint format) { return 0; } + virtual GrGLvoid makeImageHandleResident(GrGLuint64 handle, GrGLenum access) {} + virtual GrGLvoid makeImageHandleNonResident(GrGLuint64 handle) {} + virtual GrGLboolean isTextureHandleResident(GrGLuint64 handle) { return GR_GL_FALSE; } + virtual GrGLboolean isImageHandleResident(GrGLuint64 handle) { return GR_GL_FALSE; } + virtual GrGLvoid uniformHandleui64(GrGLint location, GrGLuint64 v0) {} + virtual GrGLvoid uniformHandleui64v(GrGLint location, GrGLsizei count, const GrGLuint64 *value) {} + virtual GrGLvoid programUniformHandleui64(GrGLuint program, GrGLint location, GrGLuint64 v0) {} + virtual GrGLvoid programUniformHandleui64v(GrGLuint program, GrGLint location, GrGLsizei count, const GrGLuint64 *value) {} + virtual GrGLvoid textureParameteri(GrGLuint texture, GrGLenum target, GrGLenum pname, GrGLint param) {} + virtual GrGLvoid textureParameteriv(GrGLuint texture, GrGLenum target, GrGLenum pname, const GrGLint *param) {} + virtual GrGLvoid textureParameterf(GrGLuint texture, GrGLenum target, GrGLenum pname, float param) {} + virtual GrGLvoid textureParameterfv(GrGLuint texture, GrGLenum target, GrGLenum pname, const float *param) {} + virtual GrGLvoid textureImage1D(GrGLuint texture, GrGLenum target, GrGLint level, GrGLint GrGLinternalformat, GrGLsizei width, GrGLint border, GrGLenum format, GrGLenum type, const GrGLvoid *pixels) {} + virtual GrGLvoid textureImage2D(GrGLuint texture, GrGLenum target, GrGLint level, GrGLint GrGLinternalformat, GrGLsizei width, GrGLsizei height, GrGLint border, GrGLenum format, GrGLenum type, const GrGLvoid *pixels) {} + virtual GrGLvoid textureSubImage1D(GrGLuint texture, GrGLenum target, GrGLint level, GrGLint xoffset, GrGLsizei width, GrGLenum format, GrGLenum type, const GrGLvoid *pixels) {} + virtual GrGLvoid textureSubImage2D(GrGLuint texture, GrGLenum target, GrGLint level, GrGLint xoffset, GrGLint yoffset, GrGLsizei width, GrGLsizei height, GrGLenum format, GrGLenum type, const GrGLvoid *pixels) {} + virtual GrGLvoid copyTextureImage1D(GrGLuint texture, GrGLenum target, GrGLint level, GrGLenum GrGLinternalformat, GrGLint x, GrGLint y, GrGLsizei width, GrGLint border) {} + virtual GrGLvoid copyTextureImage2D(GrGLuint texture, GrGLenum target, GrGLint level, GrGLenum GrGLinternalformat, GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height, GrGLint border) {} + virtual GrGLvoid copyTextureSubImage1D(GrGLuint texture, GrGLenum target, GrGLint level, GrGLint xoffset, GrGLint x, GrGLint y, GrGLsizei width) {} + virtual GrGLvoid copyTextureSubImage2D(GrGLuint texture, GrGLenum target, GrGLint level, GrGLint xoffset, GrGLint yoffset, GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height) {} + virtual GrGLvoid getTextureImage(GrGLuint texture, GrGLenum target, GrGLint level, GrGLenum format, GrGLenum type, GrGLvoid *pixels) {} + virtual GrGLvoid getTextureParameterfv(GrGLuint texture, GrGLenum target, GrGLenum pname, float *params) {} + virtual GrGLvoid getTextureParameteriv(GrGLuint texture, GrGLenum target, GrGLenum pname, GrGLint *params) {} + virtual GrGLvoid getTextureLevelParameterfv(GrGLuint texture, GrGLenum target, GrGLint level, GrGLenum pname, float *params) {} + virtual GrGLvoid getTextureLevelParameteriv(GrGLuint texture, GrGLenum target, GrGLint level, GrGLenum pname, GrGLint *params) {} + virtual GrGLvoid textureImage3D(GrGLuint texture, GrGLenum target, GrGLint level, GrGLint GrGLinternalformat, GrGLsizei width, GrGLsizei height, GrGLsizei depth, GrGLint border, GrGLenum format, GrGLenum type, const GrGLvoid *pixels) {} + virtual GrGLvoid textureSubImage3D(GrGLuint texture, GrGLenum target, GrGLint level, GrGLint xoffset, GrGLint yoffset, GrGLint zoffset, GrGLsizei width, GrGLsizei height, GrGLsizei depth, GrGLenum format, GrGLenum type, const GrGLvoid *pixels) {} + virtual GrGLvoid copyTextureSubImage3D(GrGLuint texture, GrGLenum target, GrGLint level, GrGLint xoffset, GrGLint yoffset, GrGLint zoffset, GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height) {} + virtual GrGLvoid compressedTextureImage3D(GrGLuint texture, GrGLenum target, GrGLint level, GrGLenum GrGLinternalformat, GrGLsizei width, GrGLsizei height, GrGLsizei depth, GrGLint border, GrGLsizei imageSize, const GrGLvoid *data) {} + virtual GrGLvoid compressedTextureImage2D(GrGLuint texture, GrGLenum target, GrGLint level, GrGLenum GrGLinternalformat, GrGLsizei width, GrGLsizei height, GrGLint border, GrGLsizei imageSize, const GrGLvoid *data) {} + virtual GrGLvoid compressedTextureImage1D(GrGLuint texture, GrGLenum target, GrGLint level, GrGLenum GrGLinternalformat, GrGLsizei width, GrGLint border, GrGLsizei imageSize, const GrGLvoid *data) {} + virtual GrGLvoid compressedTextureSubImage3D(GrGLuint texture, GrGLenum target, GrGLint level, GrGLint xoffset, GrGLint yoffset, GrGLint zoffset, GrGLsizei width, GrGLsizei height, GrGLsizei depth, GrGLenum format, GrGLsizei imageSize, const GrGLvoid *data) {} + virtual GrGLvoid compressedTextureSubImage2D(GrGLuint texture, GrGLenum target, GrGLint level, GrGLint xoffset, GrGLint yoffset, GrGLsizei width, GrGLsizei height, GrGLenum format, GrGLsizei imageSize, const GrGLvoid *data) {} + virtual GrGLvoid compressedTextureSubImage1D(GrGLuint texture, GrGLenum target, GrGLint level, GrGLint xoffset, GrGLsizei width, GrGLenum format, GrGLsizei imageSize, const GrGLvoid *data) {} + virtual GrGLvoid getCompressedTextureImage(GrGLuint texture, GrGLenum target, GrGLint level, GrGLvoid *img) {} + virtual GrGLvoid namedBufferData(GrGLuint buffer, GrGLsizeiptr size, const GrGLvoid *data, GrGLenum usage) {} + virtual GrGLvoid namedBufferSubData(GrGLuint buffer, GrGLintptr offset, GrGLsizeiptr size, const GrGLvoid *data) {} + virtual GrGLvoid* mapNamedBuffer(GrGLuint buffer, GrGLenum access) { return nullptr; } + virtual GrGLboolean unmapNamedBuffer(GrGLuint buffer) { return GR_GL_FALSE; } + virtual GrGLvoid getNamedBufferParameteriv(GrGLuint buffer, GrGLenum pname, GrGLint *params) {} + virtual GrGLvoid getNamedBufferPointerv(GrGLuint buffer, GrGLenum pname, GrGLvoid* *params) {} + virtual GrGLvoid getNamedBufferSubData(GrGLuint buffer, GrGLintptr offset, GrGLsizeiptr size, GrGLvoid *data) {} + virtual GrGLvoid programUniform1f(GrGLuint program, GrGLint location, float v0) {} + virtual GrGLvoid programUniform2f(GrGLuint program, GrGLint location, float v0, float v1) {} + virtual GrGLvoid programUniform3f(GrGLuint program, GrGLint location, float v0, float v1, float v2) {} + virtual GrGLvoid programUniform4f(GrGLuint program, GrGLint location, float v0, float v1, float v2, float v3) {} + virtual GrGLvoid programUniform1i(GrGLuint program, GrGLint location, GrGLint v0) {} + virtual GrGLvoid programUniform2i(GrGLuint program, GrGLint location, GrGLint v0, GrGLint v1) {} + virtual GrGLvoid programUniform3i(GrGLuint program, GrGLint location, GrGLint v0, GrGLint v1, GrGLint v2) {} + virtual GrGLvoid programUniform4i(GrGLuint program, GrGLint location, GrGLint v0, GrGLint v1, GrGLint v2, GrGLint v3) {} + virtual GrGLvoid programUniform1fv(GrGLuint program, GrGLint location, GrGLsizei count, const float *value) {} + virtual GrGLvoid programUniform2fv(GrGLuint program, GrGLint location, GrGLsizei count, const float *value) {} + virtual GrGLvoid programUniform3fv(GrGLuint program, GrGLint location, GrGLsizei count, const float *value) {} + virtual GrGLvoid programUniform4fv(GrGLuint program, GrGLint location, GrGLsizei count, const float *value) {} + virtual GrGLvoid programUniform1iv(GrGLuint program, GrGLint location, GrGLsizei count, const GrGLint *value) {} + virtual GrGLvoid programUniform2iv(GrGLuint program, GrGLint location, GrGLsizei count, const GrGLint *value) {} + virtual GrGLvoid programUniform3iv(GrGLuint program, GrGLint location, GrGLsizei count, const GrGLint *value) {} + virtual GrGLvoid programUniform4iv(GrGLuint program, GrGLint location, GrGLsizei count, const GrGLint *value) {} + virtual GrGLvoid programUniformMatrix2fv(GrGLuint program, GrGLint location, GrGLsizei count, GrGLboolean transpose, const float *value) {} + virtual GrGLvoid programUniformMatrix3fv(GrGLuint program, GrGLint location, GrGLsizei count, GrGLboolean transpose, const float *value) {} + virtual GrGLvoid programUniformMatrix4fv(GrGLuint program, GrGLint location, GrGLsizei count, GrGLboolean transpose, const float *value) {} + virtual GrGLvoid programUniformMatrix2x3fv(GrGLuint program, GrGLint location, GrGLsizei count, GrGLboolean transpose, const float *value) {} + virtual GrGLvoid programUniformMatrix3x2fv(GrGLuint program, GrGLint location, GrGLsizei count, GrGLboolean transpose, const float *value) {} + virtual GrGLvoid programUniformMatrix2x4fv(GrGLuint program, GrGLint location, GrGLsizei count, GrGLboolean transpose, const float *value) {} + virtual GrGLvoid programUniformMatrix4x2fv(GrGLuint program, GrGLint location, GrGLsizei count, GrGLboolean transpose, const float *value) {} + virtual GrGLvoid programUniformMatrix3x4fv(GrGLuint program, GrGLint location, GrGLsizei count, GrGLboolean transpose, const float *value) {} + virtual GrGLvoid programUniformMatrix4x3fv(GrGLuint program, GrGLint location, GrGLsizei count, GrGLboolean transpose, const float *value) {} + virtual GrGLvoid namedRenderbufferStorage(GrGLuint renderbuffer, GrGLenum GrGLinternalformat, GrGLsizei width, GrGLsizei height) {} + virtual GrGLvoid getNamedRenderbufferParameteriv(GrGLuint renderbuffer, GrGLenum pname, GrGLint *params) {} + virtual GrGLvoid namedRenderbufferStorageMultisample(GrGLuint renderbuffer, GrGLsizei samples, GrGLenum GrGLinternalformat, GrGLsizei width, GrGLsizei height) {} + virtual GrGLenum checkNamedFramebufferStatus(GrGLuint framebuffer, GrGLenum target) { return GR_GL_FRAMEBUFFER_COMPLETE; } + virtual GrGLvoid namedFramebufferTexture1D(GrGLuint framebuffer, GrGLenum attachment, GrGLenum textarget, GrGLuint texture, GrGLint level) {} + virtual GrGLvoid namedFramebufferTexture2D(GrGLuint framebuffer, GrGLenum attachment, GrGLenum textarget, GrGLuint texture, GrGLint level) {} + virtual GrGLvoid namedFramebufferTexture3D(GrGLuint framebuffer, GrGLenum attachment, GrGLenum textarget, GrGLuint texture, GrGLint level, GrGLint zoffset) {} + virtual GrGLvoid namedFramebufferRenderbuffer(GrGLuint framebuffer, GrGLenum attachment, GrGLenum renderbuffertarget, GrGLuint renderbuffer) {} + virtual GrGLvoid getNamedFramebufferAttachmentParameteriv(GrGLuint framebuffer, GrGLenum attachment, GrGLenum pname, GrGLint *params) {} + virtual GrGLvoid generateTextureMipmap(GrGLuint texture, GrGLenum target) {} + virtual GrGLvoid framebufferDrawBuffer(GrGLuint framebuffer, GrGLenum mode) {} + virtual GrGLvoid framebufferDrawBuffers(GrGLuint framebuffer, GrGLsizei n, const GrGLenum *bufs) {} + virtual GrGLvoid framebufferReadBuffer(GrGLuint framebuffer, GrGLenum mode) {} + virtual GrGLvoid getFramebufferParameteriv(GrGLuint framebuffer, GrGLenum pname, GrGLint *param) {} + virtual GrGLvoid namedCopyBufferSubData(GrGLuint readBuffer, GrGLuint writeBuffer, GrGLintptr readOffset, GrGLintptr writeOffset, GrGLsizeiptr size) {} + virtual GrGLvoid vertexArrayVertexOffset(GrGLuint vaobj, GrGLuint buffer, GrGLint size, GrGLenum type, GrGLsizei stride, GrGLintptr offset) {} + virtual GrGLvoid vertexArrayColorOffset(GrGLuint vaobj, GrGLuint buffer, GrGLint size, GrGLenum type, GrGLsizei stride, GrGLintptr offset) {} + virtual GrGLvoid vertexArrayEdgeFlagOffset(GrGLuint vaobj, GrGLuint buffer, GrGLsizei stride, GrGLintptr offset) {} + virtual GrGLvoid vertexArrayIndexOffset(GrGLuint vaobj, GrGLuint buffer, GrGLenum type, GrGLsizei stride, GrGLintptr offset) {} + virtual GrGLvoid vertexArrayNormalOffset(GrGLuint vaobj, GrGLuint buffer, GrGLenum type, GrGLsizei stride, GrGLintptr offset) {} + virtual GrGLvoid vertexArrayTexCoordOffset(GrGLuint vaobj, GrGLuint buffer, GrGLint size, GrGLenum type, GrGLsizei stride, GrGLintptr offset) {} + virtual GrGLvoid vertexArrayMultiTexCoordOffset(GrGLuint vaobj, GrGLuint buffer, GrGLenum texunit, GrGLint size, GrGLenum type, GrGLsizei stride, GrGLintptr offset) {} + virtual GrGLvoid vertexArrayFogCoordOffset(GrGLuint vaobj, GrGLuint buffer, GrGLenum type, GrGLsizei stride, GrGLintptr offset) {} + virtual GrGLvoid vertexArraySecondaryColorOffset(GrGLuint vaobj, GrGLuint buffer, GrGLint size, GrGLenum type, GrGLsizei stride, GrGLintptr offset) {} + virtual GrGLvoid vertexArrayVertexAttribOffset(GrGLuint vaobj, GrGLuint buffer, GrGLuint index, GrGLint size, GrGLenum type, GrGLboolean normalized, GrGLsizei stride, GrGLintptr offset) {} + virtual GrGLvoid vertexArrayVertexAttribIOffset(GrGLuint vaobj, GrGLuint buffer, GrGLuint index, GrGLint size, GrGLenum type, GrGLsizei stride, GrGLintptr offset) {} + virtual GrGLvoid enableVertexArray(GrGLuint vaobj, GrGLenum array) {} + virtual GrGLvoid disableVertexArray(GrGLuint vaobj, GrGLenum array) {} + virtual GrGLvoid enableVertexArrayAttrib(GrGLuint vaobj, GrGLuint index) {} + virtual GrGLvoid disableVertexArrayAttrib(GrGLuint vaobj, GrGLuint index) {} + virtual GrGLvoid getVertexArrayIntegerv(GrGLuint vaobj, GrGLenum pname, GrGLint *param) {} + virtual GrGLvoid getVertexArrayPointerv(GrGLuint vaobj, GrGLenum pname, GrGLvoid **param) {} + virtual GrGLvoid getVertexArrayIntegeri_v(GrGLuint vaobj, GrGLuint index, GrGLenum pname, GrGLint *param) {} + virtual GrGLvoid getVertexArrayPointeri_v(GrGLuint vaobj, GrGLuint index, GrGLenum pname, GrGLvoid **param) {} + virtual GrGLvoid* mapNamedBufferRange(GrGLuint buffer, GrGLintptr offset, GrGLsizeiptr length, GrGLbitfield access) { return nullptr; } + virtual GrGLvoid flushMappedNamedBufferRange(GrGLuint buffer, GrGLintptr offset, GrGLsizeiptr length) {} + virtual GrGLvoid textureBuffer(GrGLuint texture, GrGLenum target, GrGLenum internalformat, GrGLuint buffer) {} + virtual GrGLsync fenceSync(GrGLenum condition, GrGLbitfield flags) { return nullptr; } + virtual GrGLboolean isSync(GrGLsync) { return false; } + virtual GrGLenum clientWaitSync(GrGLsync sync, GrGLbitfield flags, GrGLuint64 timeout) { return GR_GL_WAIT_FAILED; } + virtual GrGLvoid waitSync(GrGLsync sync, GrGLbitfield flags, GrGLuint64 timeout) {} + virtual GrGLvoid deleteSync(GrGLsync sync) {} + virtual GrGLvoid debugMessageControl(GrGLenum source, GrGLenum type, GrGLenum severity, GrGLsizei count, const GrGLuint* ids, GrGLboolean enabled) {} + virtual GrGLvoid debugMessageInsert(GrGLenum source, GrGLenum type, GrGLuint id, GrGLenum severity, GrGLsizei length, const GrGLchar* buf) {} + virtual GrGLvoid debugMessageCallback(GRGLDEBUGPROC callback, const GrGLvoid* userParam) {} + virtual GrGLuint getDebugMessageLog(GrGLuint count, GrGLsizei bufSize, GrGLenum* sources, GrGLenum* types, GrGLuint* ids, GrGLenum* severities, GrGLsizei* lengths, GrGLchar* messageLog) { return 0; } + virtual GrGLvoid pushDebugGroup(GrGLenum source, GrGLuint id, GrGLsizei length, const GrGLchar * message) {} + virtual GrGLvoid popDebugGroup() {} + virtual GrGLvoid objectLabel(GrGLenum identifier, GrGLuint name, GrGLsizei length, const GrGLchar *label) {} + virtual GrGLvoid getInternalformativ(GrGLenum target, GrGLenum internalformat, GrGLenum pname, GrGLsizei bufSize, GrGLint *params) {} + virtual GrGLvoid programBinary(GrGLuint program, GrGLenum binaryFormat, void *binary, GrGLsizei length) {} + virtual GrGLvoid getProgramBinary(GrGLuint program, GrGLsizei bufsize, GrGLsizei* length, GrGLenum *binaryFormat, void *binary) {} + virtual GrGLvoid programParameteri(GrGLuint program, GrGLenum pname, GrGLint value) {} + +protected: + // This must be called by leaf class + void init(GrGLStandard standard) { + fStandard = standard; + fExtensions.init(standard, fFunctions.fGetString, fFunctions.fGetStringi, + fFunctions.fGetIntegerv, nullptr, GR_EGL_NO_DISPLAY); + } + TestInterface(); +}; + +template +GrGLFunction bind_to_member(TestInterface* interface, + R (TestInterface::*member)(A...)) { + return [interface, member](A... a) -> R { return (interface->*member)(a...); }; +} + +TestInterface::TestInterface() { + fFunctions.fActiveTexture = bind_to_member(this, &TestInterface::activeTexture); + fFunctions.fAttachShader = bind_to_member(this, &TestInterface::attachShader); + fFunctions.fBeginQuery = bind_to_member(this, &TestInterface::beginQuery); + fFunctions.fBindAttribLocation = bind_to_member(this, &TestInterface::bindAttribLocation); + fFunctions.fBindBuffer = bind_to_member(this, &TestInterface::bindBuffer); + fFunctions.fBindFramebuffer = bind_to_member(this, &TestInterface::bindFramebuffer); + fFunctions.fBindRenderbuffer = bind_to_member(this, &TestInterface::bindRenderbuffer); + fFunctions.fBindSampler = bind_to_member(this, &TestInterface::bindSampler); + fFunctions.fBindTexture = bind_to_member(this, &TestInterface::bindTexture); + fFunctions.fBindFragDataLocation = bind_to_member(this, &TestInterface::bindFragDataLocation); + fFunctions.fBindFragDataLocationIndexed = bind_to_member(this, &TestInterface::bindFragDataLocationIndexed); + fFunctions.fBindVertexArray = bind_to_member(this, &TestInterface::bindVertexArray); + fFunctions.fBlendBarrier = bind_to_member(this, &TestInterface::blendBarrier); + fFunctions.fBlendColor = bind_to_member(this, &TestInterface::blendColor); + fFunctions.fBlendEquation = bind_to_member(this, &TestInterface::blendEquation); + fFunctions.fBlendFunc = bind_to_member(this, &TestInterface::blendFunc); + fFunctions.fBlitFramebuffer = bind_to_member(this, &TestInterface::blitFramebuffer); + fFunctions.fBufferData = bind_to_member(this, &TestInterface::bufferData); + fFunctions.fBufferSubData = bind_to_member(this, &TestInterface::bufferSubData); + fFunctions.fCheckFramebufferStatus = bind_to_member(this, &TestInterface::checkFramebufferStatus); + fFunctions.fClear = bind_to_member(this, &TestInterface::clear); + fFunctions.fClearColor = bind_to_member(this, &TestInterface::clearColor); + fFunctions.fClearStencil = bind_to_member(this, &TestInterface::clearStencil); + fFunctions.fColorMask = bind_to_member(this, &TestInterface::colorMask); + fFunctions.fCompileShader = bind_to_member(this, &TestInterface::compileShader); + fFunctions.fCompressedTexImage2D = bind_to_member(this, &TestInterface::compressedTexImage2D); + fFunctions.fCompressedTexSubImage2D = bind_to_member(this, &TestInterface::compressedTexSubImage2D); + fFunctions.fCopyTexSubImage2D = bind_to_member(this, &TestInterface::copyTexSubImage2D); + fFunctions.fCreateProgram = bind_to_member(this, &TestInterface::createProgram); + fFunctions.fCreateShader = bind_to_member(this, &TestInterface::createShader); + fFunctions.fCullFace = bind_to_member(this, &TestInterface::cullFace); + fFunctions.fDeleteBuffers = bind_to_member(this, &TestInterface::deleteBuffers); + fFunctions.fDeleteFramebuffers = bind_to_member(this, &TestInterface::deleteFramebuffers); + fFunctions.fDeleteProgram = bind_to_member(this, &TestInterface::deleteProgram); + fFunctions.fDeleteQueries = bind_to_member(this, &TestInterface::deleteQueries); + fFunctions.fDeleteRenderbuffers = bind_to_member(this, &TestInterface::deleteRenderbuffers); + fFunctions.fDeleteSamplers = bind_to_member(this, &TestInterface::deleteSamplers); + fFunctions.fDeleteShader = bind_to_member(this, &TestInterface::deleteShader); + fFunctions.fDeleteTextures = bind_to_member(this, &TestInterface::deleteTextures); + fFunctions.fDeleteVertexArrays = bind_to_member(this, &TestInterface::deleteVertexArrays); + fFunctions.fDepthMask = bind_to_member(this, &TestInterface::depthMask); + fFunctions.fDisable = bind_to_member(this, &TestInterface::disable); + fFunctions.fDisableVertexAttribArray = bind_to_member(this, &TestInterface::disableVertexAttribArray); + fFunctions.fDrawArrays = bind_to_member(this, &TestInterface::drawArrays); + fFunctions.fDrawArraysInstanced = bind_to_member(this, &TestInterface::drawArraysInstanced); + fFunctions.fDrawArraysIndirect = bind_to_member(this, &TestInterface::drawArraysIndirect); + fFunctions.fDrawBuffer = bind_to_member(this, &TestInterface::drawBuffer); + fFunctions.fDrawBuffers = bind_to_member(this, &TestInterface::drawBuffers); + fFunctions.fDrawElements = bind_to_member(this, &TestInterface::drawElements); + fFunctions.fDrawElementsInstanced = bind_to_member(this, &TestInterface::drawElementsInstanced); + fFunctions.fDrawElementsIndirect = bind_to_member(this, &TestInterface::drawElementsIndirect); + fFunctions.fDrawRangeElements = bind_to_member(this, &TestInterface::drawRangeElements); + fFunctions.fEnable = bind_to_member(this, &TestInterface::enable); + fFunctions.fEnableVertexAttribArray = bind_to_member(this, &TestInterface::enableVertexAttribArray); + fFunctions.fEndQuery = bind_to_member(this, &TestInterface::endQuery); + fFunctions.fFinish = bind_to_member(this, &TestInterface::finish); + fFunctions.fFlush = bind_to_member(this, &TestInterface::flush); + fFunctions.fFlushMappedBufferRange = bind_to_member(this, &TestInterface::flushMappedBufferRange); + fFunctions.fFramebufferRenderbuffer = bind_to_member(this, &TestInterface::framebufferRenderbuffer); + fFunctions.fFramebufferTexture2D = bind_to_member(this, &TestInterface::framebufferTexture2D); + fFunctions.fFramebufferTexture2DMultisample = bind_to_member(this, &TestInterface::framebufferTexture2DMultisample); + fFunctions.fFrontFace = bind_to_member(this, &TestInterface::frontFace); + fFunctions.fGenBuffers = bind_to_member(this, &TestInterface::genBuffers); + fFunctions.fGenFramebuffers = bind_to_member(this, &TestInterface::genFramebuffers); + fFunctions.fGenerateMipmap = bind_to_member(this, &TestInterface::generateMipmap); + fFunctions.fGenQueries = bind_to_member(this, &TestInterface::genQueries); + fFunctions.fGenRenderbuffers = bind_to_member(this, &TestInterface::genRenderbuffers); + fFunctions.fGenSamplers = bind_to_member(this, &TestInterface::genSamplers); + fFunctions.fGenTextures = bind_to_member(this, &TestInterface::genTextures); + fFunctions.fGenVertexArrays = bind_to_member(this, &TestInterface::genVertexArrays); + fFunctions.fGetBufferParameteriv = bind_to_member(this, &TestInterface::getBufferParameteriv); + fFunctions.fGetError = bind_to_member(this, &TestInterface::getError); + fFunctions.fGetFramebufferAttachmentParameteriv = bind_to_member(this, &TestInterface::getFramebufferAttachmentParameteriv); + fFunctions.fGetIntegerv = bind_to_member(this, &TestInterface::getIntegerv); + fFunctions.fGetMultisamplefv = bind_to_member(this, &TestInterface::getMultisamplefv); + fFunctions.fGetProgramInfoLog = bind_to_member(this, &TestInterface::getProgramInfoLog); + fFunctions.fGetProgramiv = bind_to_member(this, &TestInterface::getProgramiv); + fFunctions.fGetQueryiv = bind_to_member(this, &TestInterface::getQueryiv); + fFunctions.fGetQueryObjecti64v = bind_to_member(this, &TestInterface::getQueryObjecti64v); + fFunctions.fGetQueryObjectiv = bind_to_member(this, &TestInterface::getQueryObjectiv); + fFunctions.fGetQueryObjectui64v = bind_to_member(this, &TestInterface::getQueryObjectui64v); + fFunctions.fGetQueryObjectuiv = bind_to_member(this, &TestInterface::getQueryObjectuiv); + fFunctions.fGetRenderbufferParameteriv = bind_to_member(this, &TestInterface::getRenderbufferParameteriv); + fFunctions.fGetShaderInfoLog = bind_to_member(this, &TestInterface::getShaderInfoLog); + fFunctions.fGetShaderiv = bind_to_member(this, &TestInterface::getShaderiv); + fFunctions.fGetShaderPrecisionFormat = bind_to_member(this, &TestInterface::getShaderPrecisionFormat); + fFunctions.fGetString = bind_to_member(this, &TestInterface::getString); + fFunctions.fGetStringi = bind_to_member(this, &TestInterface::getStringi); + fFunctions.fGetTexLevelParameteriv = bind_to_member(this, &TestInterface::getTexLevelParameteriv); + fFunctions.fGetUniformLocation = bind_to_member(this, &TestInterface::getUniformLocation); + fFunctions.fInsertEventMarker = bind_to_member(this, &TestInterface::insertEventMarker); + fFunctions.fInvalidateBufferData = bind_to_member(this, &TestInterface::invalidateBufferData); + fFunctions.fInvalidateBufferSubData = bind_to_member(this, &TestInterface::invalidateBufferSubData); + fFunctions.fInvalidateFramebuffer = bind_to_member(this, &TestInterface::invalidateFramebuffer); + fFunctions.fInvalidateSubFramebuffer = bind_to_member(this, &TestInterface::invalidateSubFramebuffer); + fFunctions.fInvalidateTexImage = bind_to_member(this, &TestInterface::invalidateTexImage); + fFunctions.fInvalidateTexSubImage = bind_to_member(this, &TestInterface::invalidateTexSubImage); + fFunctions.fIsTexture = bind_to_member(this, &TestInterface::isTexture); + fFunctions.fLineWidth = bind_to_member(this, &TestInterface::lineWidth); + fFunctions.fLinkProgram = bind_to_member(this, &TestInterface::linkProgram); + fFunctions.fMapBuffer = bind_to_member(this, &TestInterface::mapBuffer); + fFunctions.fMapBufferRange = bind_to_member(this, &TestInterface::mapBufferRange); + fFunctions.fMapBufferSubData = bind_to_member(this, &TestInterface::mapBufferSubData); + fFunctions.fMapTexSubImage2D = bind_to_member(this, &TestInterface::mapTexSubImage2D); + fFunctions.fPixelStorei = bind_to_member(this, &TestInterface::pixelStorei); + fFunctions.fPolygonMode = bind_to_member(this, &TestInterface::polygonMode); + fFunctions.fPopGroupMarker = bind_to_member(this, &TestInterface::popGroupMarker); + fFunctions.fPushGroupMarker = bind_to_member(this, &TestInterface::pushGroupMarker); + fFunctions.fQueryCounter = bind_to_member(this, &TestInterface::queryCounter); + fFunctions.fReadBuffer = bind_to_member(this, &TestInterface::readBuffer); + fFunctions.fReadPixels = bind_to_member(this, &TestInterface::readPixels); + fFunctions.fRenderbufferStorage = bind_to_member(this, &TestInterface::renderbufferStorage); + fFunctions.fRenderbufferStorageMultisample = bind_to_member(this, &TestInterface::renderbufferStorageMultisample); + fFunctions.fResolveMultisampleFramebuffer = bind_to_member(this, &TestInterface::resolveMultisampleFramebuffer); + fFunctions.fScissor = bind_to_member(this, &TestInterface::scissor); + fFunctions.fBindUniformLocation = bind_to_member(this, &TestInterface::bindUniformLocation); + fFunctions.fSamplerParameteri = bind_to_member(this, &TestInterface::samplerParameteri); + fFunctions.fSamplerParameteriv = bind_to_member(this, &TestInterface::samplerParameteriv); + fFunctions.fShaderSource = bind_to_member(this, &TestInterface::shaderSource); + fFunctions.fStencilFunc = bind_to_member(this, &TestInterface::stencilFunc); + fFunctions.fStencilFuncSeparate = bind_to_member(this, &TestInterface::stencilFuncSeparate); + fFunctions.fStencilMask = bind_to_member(this, &TestInterface::stencilMask); + fFunctions.fStencilMaskSeparate = bind_to_member(this, &TestInterface::stencilMaskSeparate); + fFunctions.fStencilOp = bind_to_member(this, &TestInterface::stencilOp); + fFunctions.fStencilOpSeparate = bind_to_member(this, &TestInterface::stencilOpSeparate); + fFunctions.fTexBuffer = bind_to_member(this, &TestInterface::texBuffer); + fFunctions.fTexImage2D = bind_to_member(this, &TestInterface::texImage2D); + fFunctions.fTexParameterf = bind_to_member(this, &TestInterface::texParameterf); + fFunctions.fTexParameterfv = bind_to_member(this, &TestInterface::texParameterfv); + fFunctions.fTexParameteri = bind_to_member(this, &TestInterface::texParameteri); + fFunctions.fTexParameteriv = bind_to_member(this, &TestInterface::texParameteriv); + fFunctions.fTexStorage2D = bind_to_member(this, &TestInterface::texStorage2D); + fFunctions.fDiscardFramebuffer = bind_to_member(this, &TestInterface::discardFramebuffer); + fFunctions.fTexSubImage2D = bind_to_member(this, &TestInterface::texSubImage2D); + fFunctions.fTextureBarrier = bind_to_member(this, &TestInterface::textureBarrier); + fFunctions.fUniform1f = bind_to_member(this, &TestInterface::uniform1f); + fFunctions.fUniform1i = bind_to_member(this, &TestInterface::uniform1i); + fFunctions.fUniform1fv = bind_to_member(this, &TestInterface::uniform1fv); + fFunctions.fUniform1iv = bind_to_member(this, &TestInterface::uniform1iv); + fFunctions.fUniform2f = bind_to_member(this, &TestInterface::uniform2f); + fFunctions.fUniform2i = bind_to_member(this, &TestInterface::uniform2i); + fFunctions.fUniform2fv = bind_to_member(this, &TestInterface::uniform2fv); + fFunctions.fUniform2iv = bind_to_member(this, &TestInterface::uniform2iv); + fFunctions.fUniform3f = bind_to_member(this, &TestInterface::uniform3f); + fFunctions.fUniform3i = bind_to_member(this, &TestInterface::uniform3i); + fFunctions.fUniform3fv = bind_to_member(this, &TestInterface::uniform3fv); + fFunctions.fUniform3iv = bind_to_member(this, &TestInterface::uniform3iv); + fFunctions.fUniform4f = bind_to_member(this, &TestInterface::uniform4f); + fFunctions.fUniform4i = bind_to_member(this, &TestInterface::uniform4i); + fFunctions.fUniform4fv = bind_to_member(this, &TestInterface::uniform4fv); + fFunctions.fUniform4iv = bind_to_member(this, &TestInterface::uniform4iv); + fFunctions.fUniformMatrix2fv = bind_to_member(this, &TestInterface::uniformMatrix2fv); + fFunctions.fUniformMatrix3fv = bind_to_member(this, &TestInterface::uniformMatrix3fv); + fFunctions.fUniformMatrix4fv = bind_to_member(this, &TestInterface::uniformMatrix4fv); + fFunctions.fUnmapBuffer = bind_to_member(this, &TestInterface::unmapBuffer); + fFunctions.fUnmapBufferSubData = bind_to_member(this, &TestInterface::unmapBufferSubData); + fFunctions.fUnmapTexSubImage2D = bind_to_member(this, &TestInterface::unmapTexSubImage2D); + fFunctions.fUseProgram = bind_to_member(this, &TestInterface::useProgram); + fFunctions.fVertexAttrib1f = bind_to_member(this, &TestInterface::vertexAttrib1f); + fFunctions.fVertexAttrib2fv = bind_to_member(this, &TestInterface::vertexAttrib2fv); + fFunctions.fVertexAttrib3fv = bind_to_member(this, &TestInterface::vertexAttrib3fv); + fFunctions.fVertexAttrib4fv = bind_to_member(this, &TestInterface::vertexAttrib4fv); + fFunctions.fVertexAttribDivisor = bind_to_member(this, &TestInterface::vertexAttribDivisor); + fFunctions.fVertexAttribIPointer = bind_to_member(this, &TestInterface::vertexAttribIPointer); + fFunctions.fVertexAttribPointer = bind_to_member(this, &TestInterface::vertexAttribPointer); + fFunctions.fViewport = bind_to_member(this, &TestInterface::viewport); + fFunctions.fMatrixLoadf = bind_to_member(this, &TestInterface::matrixLoadf); + fFunctions.fMatrixLoadIdentity = bind_to_member(this, &TestInterface::matrixLoadIdentity); + fFunctions.fPathCommands = bind_to_member(this, &TestInterface::pathCommands); + fFunctions.fPathParameteri = bind_to_member(this, &TestInterface::pathParameteri); + fFunctions.fPathParameterf = bind_to_member(this, &TestInterface::pathParameterf); + fFunctions.fGenPaths = bind_to_member(this, &TestInterface::genPaths); + fFunctions.fDeletePaths = bind_to_member(this, &TestInterface::deletePaths); + fFunctions.fIsPath = bind_to_member(this, &TestInterface::isPath); + fFunctions.fPathStencilFunc = bind_to_member(this, &TestInterface::pathStencilFunc); + fFunctions.fStencilFillPath = bind_to_member(this, &TestInterface::stencilFillPath); + fFunctions.fStencilStrokePath = bind_to_member(this, &TestInterface::stencilStrokePath); + fFunctions.fStencilFillPathInstanced = bind_to_member(this, &TestInterface::stencilFillPathInstanced); + fFunctions.fStencilStrokePathInstanced = bind_to_member(this, &TestInterface::stencilStrokePathInstanced); + fFunctions.fCoverFillPath = bind_to_member(this, &TestInterface::coverFillPath); + fFunctions.fCoverStrokePath = bind_to_member(this, &TestInterface::coverStrokePath); + fFunctions.fCoverFillPathInstanced = bind_to_member(this, &TestInterface::coverFillPathInstanced); + fFunctions.fCoverStrokePathInstanced = bind_to_member(this, &TestInterface::coverStrokePathInstanced); + fFunctions.fStencilThenCoverFillPath = bind_to_member(this, &TestInterface::stencilThenCoverFillPath); + fFunctions.fStencilThenCoverStrokePath = bind_to_member(this, &TestInterface::stencilThenCoverStrokePath); + fFunctions.fStencilThenCoverFillPathInstanced = bind_to_member(this, &TestInterface::stencilThenCoverFillPathInstanced); + fFunctions.fStencilThenCoverStrokePathInstanced = bind_to_member(this, &TestInterface::stencilThenCoverStrokePathInstanced); + fFunctions.fProgramPathFragmentInputGen = bind_to_member(this, &TestInterface::programPathFragmentInputGen); + fFunctions.fBindFragmentInputLocation = bind_to_member(this, &TestInterface::bindFragmentInputLocation); + fFunctions.fGetProgramResourceLocation = bind_to_member(this, &TestInterface::getProgramResourceLocation); + fFunctions.fCoverageModulation = bind_to_member(this, &TestInterface::coverageModulation); + fFunctions.fMultiDrawArraysIndirect = bind_to_member(this, &TestInterface::multiDrawArraysIndirect); + fFunctions.fMultiDrawElementsIndirect = bind_to_member(this, &TestInterface::multiDrawElementsIndirect); + fFunctions.fFenceSync = bind_to_member(this, &TestInterface::fenceSync); + fFunctions.fIsSync = bind_to_member(this, &TestInterface::isSync); + fFunctions.fClientWaitSync = bind_to_member(this, &TestInterface::clientWaitSync); + fFunctions.fWaitSync = bind_to_member(this, &TestInterface::waitSync); + fFunctions.fDeleteSync = bind_to_member(this, &TestInterface::deleteSync); + fFunctions.fDebugMessageControl = bind_to_member(this, &TestInterface::debugMessageControl); + fFunctions.fDebugMessageInsert = bind_to_member(this, &TestInterface::debugMessageInsert); + fFunctions.fDebugMessageCallback = bind_to_member(this, &TestInterface::debugMessageCallback); + fFunctions.fGetDebugMessageLog = bind_to_member(this, &TestInterface::getDebugMessageLog); + fFunctions.fPushDebugGroup = bind_to_member(this, &TestInterface::pushDebugGroup); + fFunctions.fPopDebugGroup = bind_to_member(this, &TestInterface::popDebugGroup); + fFunctions.fObjectLabel = bind_to_member(this, &TestInterface::objectLabel); + fFunctions.fGetInternalformativ = bind_to_member(this, &TestInterface::getInternalformativ); + fFunctions.fProgramBinary = bind_to_member(this, &TestInterface::programBinary); + fFunctions.fGetProgramBinary = bind_to_member(this, &TestInterface::getProgramBinary); + fFunctions.fProgramParameteri = bind_to_member(this, &TestInterface::programParameteri); +} + /** Null interface implementation */ -class NullInterface : public GrGLTestInterface { +class NullInterface : public TestInterface { public: NullInterface(bool enableNVPR) : fCurrDrawFramebuffer(0) @@ -836,7 +1386,7 @@ private: } } - typedef GrGLTestInterface INHERITED; + typedef TestInterface INHERITED; }; } // anonymous namespace -- cgit v1.2.3 From 712e60544e6a3e0e2e170257076ad3e044d73787 Mon Sep 17 00:00:00 2001 From: yangbill Date: Thu, 11 Apr 2019 12:13:50 +0800 Subject: Add file-exclusion-filter-regex for hwuimicro Using atest to test hwuimicro will get test error due to current logic of GoogleBenchmarkTest will running all executable files under input device path. But due to the test config also under that device path which will break GoogleBenchmarkTest. Filter *.config file to prevent breakage. Bug: 129996310 Test: atest hwuimicro Change-Id: I68b6c623ed30f75faf7801bc5a1d556107359573 --- libs/hwui/AndroidTest.xml | 2 ++ 1 file changed, 2 insertions(+) (limited to 'libs') diff --git a/libs/hwui/AndroidTest.xml b/libs/hwui/AndroidTest.xml index eab32c5a67ce..381fb9f6c7bf 100644 --- a/libs/hwui/AndroidTest.xml +++ b/libs/hwui/AndroidTest.xml @@ -28,9 +28,11 @@ -- cgit v1.2.3 From 1c2f5fcc7e35312877531069ece5cbee2d9ee040 Mon Sep 17 00:00:00 2001 From: Mike Reed Date: Wed, 10 Apr 2019 16:57:52 -0400 Subject: Update to new factories for effects Test: make Change-Id: Ib342878524b51d0eacf6c60ece3c7da8bf27d2c0 --- libs/hwui/CanvasTransform.cpp | 2 +- libs/hwui/SkiaCanvas.cpp | 3 +-- libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp | 2 +- libs/hwui/tests/common/scenes/BitmapShaders.cpp | 6 ++---- libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp | 7 +++---- libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp | 2 +- libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp | 4 ++-- libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp | 2 +- libs/hwui/tests/common/scenes/TvApp.cpp | 2 +- libs/hwui/tests/unit/VectorDrawableTests.cpp | 2 +- 10 files changed, 14 insertions(+), 18 deletions(-) (limited to 'libs') diff --git a/libs/hwui/CanvasTransform.cpp b/libs/hwui/CanvasTransform.cpp index 0cfaa8c61279..83e02b9e8c77 100644 --- a/libs/hwui/CanvasTransform.cpp +++ b/libs/hwui/CanvasTransform.cpp @@ -102,7 +102,7 @@ static void applyColorTransform(ColorTransform transform, SkPaint& paint) { // TODO: LRU this or something to avoid spamming new color mode filters if (paint.getColorFilter()->asColorMode(&color, &mode)) { color = transformColor(transform, color); - paint.setColorFilter(SkColorFilter::MakeModeFilter(color, mode)); + paint.setColorFilter(SkColorFilters::Blend(color, mode)); } } } diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index f328a5399cbb..834069988f42 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -631,8 +631,7 @@ void SkiaCanvas::drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight, SkPaint& tmpPaint = paintCoW.writeable(); sk_sp image = bitmap.makeImage(); - sk_sp shader = - image->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode); + sk_sp shader = image->makeShader(); tmpPaint.setShader(std::move(shader)); mCanvas->drawVertices(builder.detach(), SkBlendMode::kModulate, diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp index d9456355cb88..3f9e50ae9095 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -180,7 +180,7 @@ SkiaCanvas::PaintCoW&& SkiaRecordingCanvas::filterBitmap(PaintCoW&& paint, if (colorSpaceFilter) { if (tmpPaint.getColorFilter()) { - tmpPaint.setColorFilter(SkColorFilter::MakeComposeFilter( + tmpPaint.setColorFilter(SkColorFilters::Compose( tmpPaint.refColorFilter(), std::move(colorSpaceFilter))); } else { tmpPaint.setColorFilter(std::move(colorSpaceFilter)); diff --git a/libs/hwui/tests/common/scenes/BitmapShaders.cpp b/libs/hwui/tests/common/scenes/BitmapShaders.cpp index 510766073b08..400196b203b9 100644 --- a/libs/hwui/tests/common/scenes/BitmapShaders.cpp +++ b/libs/hwui/tests/common/scenes/BitmapShaders.cpp @@ -46,14 +46,12 @@ public: SkPaint paint; sk_sp image = hwuiBitmap->makeImage(); sk_sp repeatShader = - image->makeShader(SkShader::TileMode::kRepeat_TileMode, - SkShader::TileMode::kRepeat_TileMode, nullptr); + image->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat); paint.setShader(std::move(repeatShader)); canvas.drawRoundRect(0, 0, 500, 500, 50.0f, 50.0f, paint); sk_sp mirrorShader = - image->makeShader(SkShader::TileMode::kMirror_TileMode, - SkShader::TileMode::kMirror_TileMode, nullptr); + image->makeShader(SkTileMode::kMirror, SkTileMode::kMirror); paint.setShader(std::move(mirrorShader)); canvas.drawRoundRect(0, 600, 500, 1100, 50.0f, 50.0f, paint); } diff --git a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp index 2af955fbb711..659926bae06f 100644 --- a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp +++ b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp @@ -60,10 +60,10 @@ public: colors[0] = Color::Black; colors[1] = Color::White; sk_sp gradientShader = SkGradientShader::MakeRadial( - center, 50, colors, nullptr, 2, SkShader::TileMode::kRepeat_TileMode); + center, 50, colors, nullptr, 2, SkTileMode::kRepeat); sk_sp compositeShader( - SkShader::MakeComposeShader(hardwareShader, gradientShader, SkBlendMode::kDstATop)); + SkShaders::Blend(SkBlendMode::kDstATop, hardwareShader, gradientShader)); SkPaint paint; paint.setShader(std::move(compositeShader)); @@ -74,7 +74,6 @@ public: sk_sp createBitmapShader(Bitmap& bitmap) { sk_sp image = bitmap.makeImage(); - return image->makeShader(SkShader::TileMode::kClamp_TileMode, - SkShader::TileMode::kClamp_TileMode); + return image->makeShader(); } }; diff --git a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp index ecaaf487e4f8..c6b60aaec9c5 100644 --- a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp @@ -44,7 +44,7 @@ class ListOfFadedTextAnimation : public TestListViewSceneBase { SkColor colors[2] = {Color::Black, Color::Transparent}; sk_sp s( - SkGradientShader::MakeLinear(pts, colors, NULL, 2, SkShader::kClamp_TileMode)); + SkGradientShader::MakeLinear(pts, colors, NULL, 2, SkTileMode::kClamp)); SkMatrix matrix; matrix.setScale(1, length); diff --git a/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp b/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp index ff0cb3705cb8..2572d71947b2 100644 --- a/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp +++ b/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp @@ -66,7 +66,7 @@ private: matrix[SkColorMatrix::kB_Trans] = 5.0f; matrix[SkColorMatrix::kA_Trans] = 10.0f; - paint.setColorFilter(SkColorFilter::MakeMatrixFilterRowMajor255(matrix)); + paint.setColorFilter(SkColorFilters::MatrixRowMajor255(matrix)); // set a shader so it's not likely for the matrix to be optimized away (since a // clever @@ -75,7 +75,7 @@ private: SkPoint pts[] = {SkPoint::Make(0, 0), SkPoint::Make(width, height)}; SkColor colors[2] = {Color::DeepPurple_500, Color::DeepOrange_500}; paint.setShader(SkGradientShader::MakeLinear(pts, colors, pos, 2, - SkShader::kClamp_TileMode)); + SkTileMode::kClamp)); // overdraw several times to emphasize shader cost for (int i = 0; i < 10; i++) { diff --git a/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp b/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp index 016c65c17c4c..8bd804e75674 100644 --- a/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp +++ b/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp @@ -57,7 +57,7 @@ private: // use i%2 start position to pick 2 color combo with black in it SkColor colors[3] = {Color::Transparent, Color::Black, Color::Cyan_500}; paint.setShader(SkGradientShader::MakeLinear(pts, colors + (i % 2), pos, 2, - SkShader::kClamp_TileMode)); + SkTileMode::kClamp)); canvas.drawRect(i, i, width, height, paint); } }); diff --git a/libs/hwui/tests/common/scenes/TvApp.cpp b/libs/hwui/tests/common/scenes/TvApp.cpp index 229c7f392629..76f02888f994 100644 --- a/libs/hwui/tests/common/scenes/TvApp.cpp +++ b/libs/hwui/tests/common/scenes/TvApp.cpp @@ -219,7 +219,7 @@ private: image.get())); SkPaint paint; sk_sp filter( - SkColorFilter::MakeModeFilter((curFrame % 150) << 24, SkBlendMode::kSrcATop)); + SkColorFilters::Blend((curFrame % 150) << 24, SkBlendMode::kSrcATop)); paint.setColorFilter(filter); sk_sp bitmap = mCachedBitmaps[ci]; canvas->drawBitmap(*bitmap, 0, 0, &paint); diff --git a/libs/hwui/tests/unit/VectorDrawableTests.cpp b/libs/hwui/tests/unit/VectorDrawableTests.cpp index 5db002862fcd..60fd7a753f2d 100644 --- a/libs/hwui/tests/unit/VectorDrawableTests.cpp +++ b/libs/hwui/tests/unit/VectorDrawableTests.cpp @@ -395,7 +395,7 @@ TEST(VectorDrawable, drawPathWithoutIncrementingShaderRefCount) { bitmap.allocN32Pixels(5, 5, false); SkCanvas canvas(bitmap); - sk_sp shader = SkShader::MakeColorShader(SK_ColorBLACK); + sk_sp shader = SkShaders::Color(SK_ColorBLACK); // Initial ref count is 1 EXPECT_TRUE(shader->unique()); -- cgit v1.2.3 From 62b389411be2d9233eac58cd856bfcb643db0684 Mon Sep 17 00:00:00 2001 From: Ben Wagner Date: Mon, 15 Apr 2019 11:59:33 -0400 Subject: Move AlphaFilterCanvas to new API. The AlphaFilterCanvas is only used when it will make a copy of the paint anyway. Remove the need to keep track of copy on write semantics when they are never used. Test: refactoring CL. Existing unit tests still pass. Change-Id: I20c0d618db7d1a76756bd4c16fe686ce7fb44f38 --- libs/hwui/pipeline/skia/RenderNodeDrawable.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'libs') diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp index 1bd30eb5371b..e65fe3a3b811 100644 --- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp +++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp @@ -187,17 +187,13 @@ public: AlphaFilterCanvas(SkCanvas* canvas, float alpha) : SkPaintFilterCanvas(canvas), mAlpha(alpha) {} protected: - bool onFilter(SkTCopyOnFirstWrite* paint, Type t) const override { - std::optional defaultPaint; - if (!*paint) { - paint->init(defaultPaint.emplace()); - } - paint->writable()->setAlpha((uint8_t)(*paint)->getAlpha() * mAlpha); + bool onFilter(SkPaint& paint) const override { + paint.setAlpha((uint8_t)paint.getAlpha() * mAlpha); return true; } void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override { // We unroll the drawable using "this" canvas, so that draw calls contained inside will - // get their alpha applied. THe default SkPaintFilterCanvas::onDrawDrawable does not unroll. + // get their alpha applied. The default SkPaintFilterCanvas::onDrawDrawable does not unroll. drawable->draw(this, matrix); } -- cgit v1.2.3 From 3b814cbedd25195ad9366bc953b8707c6134a574 Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Wed, 17 Apr 2019 08:34:54 -0400 Subject: Use structured bindings for mVectorDrawables Test: Build Change-Id: I60df096bf3720968039f7e4c5eac86682b2fc684 --- libs/hwui/pipeline/skia/SkiaDisplayList.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'libs') diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp index 41bcfc25f5c1..780ac20c65aa 100644 --- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp +++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp @@ -131,13 +131,12 @@ bool SkiaDisplayList::prepareListAndChildren( } } - for (auto& vectorDrawablePair : mVectorDrawables) { + for (auto& [vectorDrawable, cachedMatrix] : mVectorDrawables) { // If any vector drawable in the display list needs update, damage the node. - auto& vectorDrawable = vectorDrawablePair.first; if (vectorDrawable->isDirty()) { Matrix4 totalMatrix; info.damageAccumulator->computeCurrentTransform(&totalMatrix); - Matrix4 canvasMatrix(vectorDrawablePair.second); + Matrix4 canvasMatrix(cachedMatrix); totalMatrix.multiply(canvasMatrix); const SkRect& bounds = vectorDrawable->properties().getBounds(); if (intersects(info.screenSize, totalMatrix, bounds)) { -- cgit v1.2.3 From 765d31240c1c8e8d3817b0e13497732f96e705d1 Mon Sep 17 00:00:00 2001 From: Mike Reed Date: Tue, 23 Apr 2019 13:57:35 -0400 Subject: use colormatrix w/o legacy apis Test: make Change-Id: I01e29bd36ed3788c4f996092c81310c17f5039cd --- .../tests/common/scenes/SimpleColorMatrixAnimation.cpp | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) (limited to 'libs') diff --git a/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp b/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp index 2572d71947b2..c899a3c8cfc6 100644 --- a/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp +++ b/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp @@ -53,20 +53,13 @@ private: x, y, x + width, y + height, [width, height](RenderProperties& props, Canvas& canvas) { SkPaint paint; - float matrix[20] = {0}; - + // Simple scale/translate case where R, G, and B are all treated equivalently - matrix[SkColorMatrix::kR_Scale] = 1.1f; - matrix[SkColorMatrix::kG_Scale] = 1.1f; - matrix[SkColorMatrix::kB_Scale] = 1.1f; - matrix[SkColorMatrix::kA_Scale] = 0.5f; - - matrix[SkColorMatrix::kR_Trans] = 5.0f; - matrix[SkColorMatrix::kG_Trans] = 5.0f; - matrix[SkColorMatrix::kB_Trans] = 5.0f; - matrix[SkColorMatrix::kA_Trans] = 10.0f; + SkColorMatrix cm; + cm.setScale(1.1f, 1.1f, 1.1f, 0.5f); + cm.postTranslate255(5.0f, 5.0f, 5.0f, 10.0f); - paint.setColorFilter(SkColorFilters::MatrixRowMajor255(matrix)); + paint.setColorFilter(SkColorFilters::Matrix(cm)); // set a shader so it's not likely for the matrix to be optimized away (since a // clever -- cgit v1.2.3 From 5a31997b0eb5c2440956155124e372fd710c6dec Mon Sep 17 00:00:00 2001 From: Ben Wagner Date: Fri, 26 Apr 2019 10:38:49 -0400 Subject: Remove SkCanvas::filterBitmap declaration. It doesn't look like this even has an implementation anymore. Test: This just removes dead code. Change-Id: I126ad7f653e80973e0d79966a5885f486e2aa9f2 --- libs/hwui/SkiaCanvas.h | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'libs') diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h index 3fe2bce06b41..bce1235a0f55 100644 --- a/libs/hwui/SkiaCanvas.h +++ b/libs/hwui/SkiaCanvas.h @@ -221,16 +221,6 @@ private: void drawPoints(const float* points, int count, const SkPaint& paint, SkCanvas::PointMode mode); - /** Filters the paint for bitmap drawing. - * - * After filtering the paint for bitmap drawing, - * also calls filterPaint on the paint. - * - * @param paint the paint to filter. Will be initialized with the default - * SkPaint before filtering if filtering is required. - */ - PaintCoW&& filterBitmap(PaintCoW&& paint, sk_sp colorSpaceFilter) const; - class Clip; std::unique_ptr mCanvasOwned; // might own a canvas we allocated -- cgit v1.2.3 From 2ee3743456f4f9c0d8eb28331be462fd0e06fe05 Mon Sep 17 00:00:00 2001 From: Mike Reed Date: Fri, 26 Apr 2019 09:22:13 -0400 Subject: use new non-255 colormatrix also use new naming convention for asAColorMode Test: make Change-Id: I32ddaf6d2d0ce9b0f29ee32e866879884fc926b4 --- libs/hwui/CanvasTransform.cpp | 2 +- libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp | 1 - libs/hwui/tests/unit/SkiaBehaviorTests.cpp | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) (limited to 'libs') diff --git a/libs/hwui/CanvasTransform.cpp b/libs/hwui/CanvasTransform.cpp index 83e02b9e8c77..8c37d73366c2 100644 --- a/libs/hwui/CanvasTransform.cpp +++ b/libs/hwui/CanvasTransform.cpp @@ -100,7 +100,7 @@ static void applyColorTransform(ColorTransform transform, SkPaint& paint) { SkBlendMode mode; SkColor color; // TODO: LRU this or something to avoid spamming new color mode filters - if (paint.getColorFilter()->asColorMode(&color, &mode)) { + if (paint.getColorFilter()->asAColorMode(&color, &mode)) { color = transformColor(transform, color); paint.setColorFilter(SkColorFilters::Blend(color, mode)); } diff --git a/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp b/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp index c899a3c8cfc6..06205f67f20a 100644 --- a/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp +++ b/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp @@ -53,7 +53,6 @@ private: x, y, x + width, y + height, [width, height](RenderProperties& props, Canvas& canvas) { SkPaint paint; - // Simple scale/translate case where R, G, and B are all treated equivalently SkColorMatrix cm; cm.setScale(1.1f, 1.1f, 1.1f, 0.5f); diff --git a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp index df5f45618070..7951537e1525 100644 --- a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp +++ b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp @@ -48,14 +48,14 @@ TEST(SkiaBehavior, lightingColorFilter_simplify) { SkColor observedColor; SkBlendMode observedMode; - ASSERT_TRUE(filter->asColorMode(&observedColor, &observedMode)); + ASSERT_TRUE(filter->asAColorMode(&observedColor, &observedMode)); EXPECT_EQ(0xFF223344, observedColor); EXPECT_EQ(SkBlendMode::kModulate, observedMode); } { sk_sp failFilter(SkColorMatrixFilter::MakeLightingFilter(0x11223344, 0x1)); - EXPECT_FALSE(failFilter->asColorMode(nullptr, nullptr)); + EXPECT_FALSE(failFilter->asAColorMode(nullptr, nullptr)); } } -- cgit v1.2.3 From ea3cbd0997074d3df69b154af6fbe8fbba17a967 Mon Sep 17 00:00:00 2001 From: Ben Wagner Date: Fri, 26 Apr 2019 11:12:06 -0400 Subject: Interpret linearMetrics flag to imply no-hinting. When drawing text and the linearMetrics flag is set, interpret it as a 'linear' flag which implies no-hinting. This will keep existing behavior and match how the flag is currently documented when Skia changes the behavior to be seperable from hinting. Test: Strict improvement, broken tests will be fixed before landing. Change-Id: Ie76f84e4fd23f4086a2f955b37d3be140452e77f --- libs/hwui/hwui/Canvas.cpp | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) (limited to 'libs') diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp index 68aa7374fec4..a48c86028262 100644 --- a/libs/hwui/hwui/Canvas.cpp +++ b/libs/hwui/hwui/Canvas.cpp @@ -154,6 +154,11 @@ void Canvas::drawText(const uint16_t* text, int textSize, int start, int count, // minikin may modify the original paint Paint paint(origPaint); + // interpret 'linear metrics' flag as 'linear', forcing no-hinting when drawing + if (paint.getSkFont().isLinearMetrics()) { + paint.getSkFont().setHinting(SkFontHinting::kNone); + } + minikin::Layout layout = MinikinUtils::doLayout(&paint, bidiFlags, typeface, text, textSize, start, count, contextStart, contextCount, mt); @@ -234,23 +239,30 @@ private: }; void Canvas::drawTextOnPath(const uint16_t* text, int count, minikin::Bidi bidiFlags, - const SkPath& path, float hOffset, float vOffset, const Paint& paint, - const Typeface* typeface) { - Paint paintCopy(paint); + const SkPath& path, float hOffset, float vOffset, + const Paint& origPaint, const Typeface* typeface) { + // minikin may modify the original paint + Paint paint(origPaint); + + // interpret 'linear metrics' flag as 'linear', forcing no-hinting when drawing + if (paint.getSkFont().isLinearMetrics()) { + paint.getSkFont().setHinting(SkFontHinting::kNone); + } + minikin::Layout layout = - MinikinUtils::doLayout(&paintCopy, bidiFlags, typeface, text, count, // text buffer - 0, count, // draw range - 0, count, // context range + MinikinUtils::doLayout(&paint, bidiFlags, typeface, text, count, // text buffer + 0, count, // draw range + 0, count, // context range nullptr); - hOffset += MinikinUtils::hOffsetForTextAlign(&paintCopy, layout, path); + hOffset += MinikinUtils::hOffsetForTextAlign(&paint, layout, path); // 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. - paintCopy.setTextAlign(Paint::kLeft_Align); + paint.setTextAlign(Paint::kLeft_Align); - DrawTextOnPathFunctor f(layout, this, hOffset, vOffset, paintCopy, path); - MinikinUtils::forFontRun(layout, &paintCopy, f); + DrawTextOnPathFunctor f(layout, this, hOffset, vOffset, paint, path); + MinikinUtils::forFontRun(layout, &paint, f); } int Canvas::sApiLevel = 1; -- cgit v1.2.3 From c9b5a972712b5879021c12ad6042ea40584893c7 Mon Sep 17 00:00:00 2001 From: Mike Reed Date: Wed, 1 May 2019 15:53:36 -0400 Subject: don't use legacy postTranslate255 Test: make Change-Id: I040dffb99d540e8c6989a2bb1b5bcb70cc1a257e --- libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp b/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp index 06205f67f20a..e60bd5fae198 100644 --- a/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp +++ b/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp @@ -56,7 +56,7 @@ private: // Simple scale/translate case where R, G, and B are all treated equivalently SkColorMatrix cm; cm.setScale(1.1f, 1.1f, 1.1f, 0.5f); - cm.postTranslate255(5.0f, 5.0f, 5.0f, 10.0f); + cm.postTranslate(5.0f/255, 5.0f/255, 5.0f/255, 10.0f/255); paint.setColorFilter(SkColorFilters::Matrix(cm)); -- cgit v1.2.3 From ac33a487516196e9f1cf830e78313806e6daf777 Mon Sep 17 00:00:00 2001 From: Derek Sollenberger Date: Mon, 22 Apr 2019 16:28:09 -0400 Subject: Fix fading edge effect from impacting neighboring pixels Bug: 129117085 Test: skia unit tests and test cases described in the bug Change-Id: Ieaa7c831dd6298ac0565e6f1837b1c1dbd4545da --- libs/hwui/DisplayListOps.in | 3 ++- libs/hwui/RecordingCanvas.cpp | 13 +++++++++++++ libs/hwui/RecordingCanvas.h | 2 ++ libs/hwui/SkiaCanvas.cpp | 13 +++++++++++++ libs/hwui/SkiaCanvas.h | 1 + libs/hwui/hwui/Canvas.h | 1 + 6 files changed, 32 insertions(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/hwui/DisplayListOps.in b/libs/hwui/DisplayListOps.in index 14e3a32817a0..2deb5657c877 100644 --- a/libs/hwui/DisplayListOps.in +++ b/libs/hwui/DisplayListOps.in @@ -26,7 +26,8 @@ X(ClipPath) X(ClipRect) X(ClipRRect) X(ClipRegion) -X(DrawPaint) +X(DrawPaint) +X(DrawBehind) X(DrawPath) X(DrawRect) X(DrawRegion) diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index 85947665839a..e58fbbe8e667 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -20,6 +20,7 @@ #include "SkAndroidFrameworkUtils.h" #include "SkCanvas.h" +#include "SkCanvasPriv.h" #include "SkData.h" #include "SkDrawShadowInfo.h" #include "SkImage.h" @@ -187,6 +188,12 @@ struct DrawPaint final : Op { SkPaint paint; void draw(SkCanvas* c, const SkMatrix&) const { c->drawPaint(paint); } }; +struct DrawBehind final : Op { + static const auto kType = Type::DrawBehind; + DrawBehind(const SkPaint& paint) : paint(paint) {} + SkPaint paint; + void draw(SkCanvas* c, const SkMatrix&) const { SkCanvasPriv::DrawBehind(c, paint); } +}; struct DrawPath final : Op { static const auto kType = Type::DrawPath; DrawPath(const SkPath& path, const SkPaint& paint) : path(path), paint(paint) {} @@ -565,6 +572,9 @@ void DisplayListData::clipRegion(const SkRegion& region, SkClipOp op) { void DisplayListData::drawPaint(const SkPaint& paint) { this->push(0, paint); } +void DisplayListData::drawBehind(const SkPaint& paint) { + this->push(0, paint); +} void DisplayListData::drawPath(const SkPath& path, const SkPaint& paint) { this->push(0, path, paint); } @@ -834,6 +844,9 @@ void RecordingCanvas::onClipRegion(const SkRegion& region, SkClipOp op) { void RecordingCanvas::onDrawPaint(const SkPaint& paint) { fDL->drawPaint(paint); } +void RecordingCanvas::onDrawBehind(const SkPaint& paint) { + fDL->drawBehind(paint); +} void RecordingCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) { fDL->drawPath(path, paint); } diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h index 3a76ca1137a5..7269bcad3d7a 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -89,6 +89,7 @@ private: void clipRegion(const SkRegion&, SkClipOp); void drawPaint(const SkPaint&); + void drawBehind(const SkPaint&); void drawPath(const SkPath&, const SkPaint&); void drawRect(const SkRect&, const SkPaint&); void drawRegion(const SkRegion&, const SkPaint&); @@ -157,6 +158,7 @@ public: void onClipRegion(const SkRegion&, SkClipOp) override; void onDrawPaint(const SkPaint&) override; + void onDrawBehind(const SkPaint&) override; void onDrawPath(const SkPath&, const SkPaint&) override; void onDrawRect(const SkRect&, const SkPaint&) override; void onDrawRegion(const SkRegion&, const SkPaint&) override; diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 834069988f42..6ea6af8f2935 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -191,6 +192,18 @@ int SkiaCanvas::saveUnclippedLayer(int left, int top, int right, int bottom) { return SkAndroidFrameworkUtils::SaveBehind(mCanvas, &bounds); } +void SkiaCanvas::restoreUnclippedLayer(int restoreCount, const SkPaint& paint) { + + while (mCanvas->getSaveCount() > restoreCount + 1) { + this->restore(); + } + + if (mCanvas->getSaveCount() == restoreCount + 1) { + SkCanvasPriv::DrawBehind(mCanvas, *filterPaint(paint)); + this->restore(); + } +} + class SkiaCanvas::Clip { public: Clip(const SkRect& rect, SkClipOp op, const SkMatrix& m) diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h index 3fe2bce06b41..bbe91eb2fbc4 100644 --- a/libs/hwui/SkiaCanvas.h +++ b/libs/hwui/SkiaCanvas.h @@ -69,6 +69,7 @@ public: virtual int save(SaveFlags::Flags flags) override; virtual void restore() override; virtual void restoreToCount(int saveCount) override; + virtual void restoreUnclippedLayer(int saveCount, const SkPaint& paint) override; virtual int saveLayer(float left, float top, float right, float bottom, const SkPaint* paint, SaveFlags::Flags flags) override; diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h index 11e8579a481f..ac8db216b059 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -191,6 +191,7 @@ public: virtual int save(SaveFlags::Flags flags) = 0; virtual void restore() = 0; virtual void restoreToCount(int saveCount) = 0; + virtual void restoreUnclippedLayer(int saveCount, const SkPaint& paint) = 0; virtual int saveLayer(float left, float top, float right, float bottom, const SkPaint* paint, SaveFlags::Flags flags) = 0; -- cgit v1.2.3 From 0ab4439614fb2056e57d6079a2ea8bb80ea86465 Mon Sep 17 00:00:00 2001 From: Ben Wagner Date: Thu, 9 May 2019 18:44:34 -0400 Subject: Replace Skia font macros with enums. This mechanically replaces kXXX_SkTextEncoding with SkTextEncoding::kXXX and kXXX_SkFontHinting with SkFontHinting::kXXX. This will allow Skia to remove these old macro constants and get everyone on the new enums. Test: refactoring CL. Existing unit tests still pass. Change-Id: I4c3b1834b1c4cdfc7f708613c49dfb18fc3194f5 --- libs/hwui/tests/common/scenes/ListViewAnimation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp index feb881f654f8..fb7e34dfa513 100644 --- a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp @@ -53,7 +53,7 @@ class ListViewAnimation : public TestListViewSceneBase { char charToShow = 'A' + (rand() % 26); const SkPoint pos = {SkIntToScalar(size / 2), /*approximate centering*/ SkFloatToScalar(size * 0.7f)}; - canvas.drawSimpleText(&charToShow, 1, kUTF8_SkTextEncoding, pos.fX, pos.fY, font, paint); + canvas.drawSimpleText(&charToShow, 1, SkTextEncoding::kUTF8, pos.fX, pos.fY, font, paint); return bitmap; } -- cgit v1.2.3 From fd4293962c243871d95fb6bb6a08d6a20a8e8854 Mon Sep 17 00:00:00 2001 From: Greg Daniel Date: Thu, 9 May 2019 15:44:56 -0400 Subject: Make sure we don't delete VkSemaphores before exporting in VulkanManager Test: Manual building and testing on device to confirm error messages are gone. Bug: b/132358913 Change-Id: I0e22df5eb3bc61a7dd84d87db9a4f67756ecd5ae --- libs/hwui/renderthread/VulkanManager.cpp | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) (limited to 'libs') diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index 62fd48940870..ce5be8a90d00 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -482,6 +482,13 @@ struct DestroySemaphoreInfo { PFN_vkDestroySemaphore mDestroyFunction; VkDevice mDevice; VkSemaphore mSemaphore; + // We need to make sure we don't delete the VkSemaphore until it is done being used by both Skia + // (including by the GPU) and inside the VulkanManager. So we always start with two refs, one + // owned by Skia and one owned by the VulkanManager. The refs are decremented each time + // destroy_semaphore is called with this object. Skia will call destroy_semaphore once it is + // done with the semaphore and the GPU has finished work on the semaphore. The VulkanManager + // calls destroy_semaphore after sending the semaphore to Skia and exporting it if need be. + int mRefs = 2; DestroySemaphoreInfo(PFN_vkDestroySemaphore destroyFunction, VkDevice device, VkSemaphore semaphore) @@ -490,8 +497,11 @@ struct DestroySemaphoreInfo { static void destroy_semaphore(void* context) { DestroySemaphoreInfo* info = reinterpret_cast(context); - info->mDestroyFunction(info->mDevice, info->mSemaphore, nullptr); - delete info; + --info->mRefs; + if (!info->mRefs) { + info->mDestroyFunction(info->mDevice, info->mSemaphore, nullptr); + delete info; + } } void VulkanManager::swapBuffers(VulkanSurface* surface, const SkRect& dirtyRect) { @@ -542,6 +552,7 @@ void VulkanManager::swapBuffers(VulkanSurface* surface, const SkRect& dirtyRect) ALOGE("VulkanManager::swapBuffers(): Semaphore submission failed"); mQueueWaitIdle(mGraphicsQueue); } + destroy_semaphore(destroyInfo); surface->presentCurrentBuffer(dirtyRect, fenceFd); } @@ -644,13 +655,16 @@ status_t VulkanManager::createReleaseFence(sp& nativeFence, GrContext* gr DestroySemaphoreInfo* destroyInfo = new DestroySemaphoreInfo(mDestroySemaphore, mDevice, semaphore); + // Even if Skia fails to submit the semaphore, it will still call the destroy_semaphore callback + // which will remove its ref to the semaphore. The VulkanManager must still release its ref, + // when it is done with the semaphore. GrSemaphoresSubmitted submitted = grContext->flush(kNone_GrFlushFlags, 1, &backendSemaphore, destroy_semaphore, destroyInfo); if (submitted == GrSemaphoresSubmitted::kNo) { ALOGE("VulkanManager::createReleaseFence: Failed to submit semaphore"); - mDestroySemaphore(mDevice, semaphore, nullptr); + destroy_semaphore(destroyInfo); return INVALID_OPERATION; } @@ -663,6 +677,7 @@ status_t VulkanManager::createReleaseFence(sp& nativeFence, GrContext* gr int fenceFd = 0; err = mGetSemaphoreFdKHR(mDevice, &getFdInfo, &fenceFd); + destroy_semaphore(destroyInfo); if (VK_SUCCESS != err) { ALOGE("VulkanManager::createReleaseFence: Failed to get semaphore Fd"); return INVALID_OPERATION; -- cgit v1.2.3 From 3509b624feecbcb8be850f9e1f397cdd7aeea5e2 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Mon, 20 May 2019 15:22:22 -0700 Subject: Remove obtainStyledAttribute and AssetManager2::GetBag tracing These traces are small and noisy, so they hurt performance more than they help. This reverts commit c37457799be3db0590a5d94832b2fef5f64ef439. Test: m Bug: 132721345 Change-Id: I9ef719f54f2bc8a54f23e88f46d74e35417a6519 --- libs/androidfw/AssetManager2.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'libs') diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 1b515ad41e68..d20aecaaf0f6 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -778,8 +778,6 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { } const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector& child_resids) { - ATRACE_NAME("AssetManager::GetBag"); - auto cached_iter = cached_bags_.find(resid); if (cached_iter != cached_bags_.end()) { return cached_iter->second.get(); -- cgit v1.2.3 From da7c19c6d681e20ef924fe30be329aa36a02dd86 Mon Sep 17 00:00:00 2001 From: Stan Iliev Date: Wed, 22 May 2019 14:43:44 -0400 Subject: Fix light center position for layers SkiaLayer::inverseTransformInWindow should be the inverse matrix of Layer transform. This CL fixes a bug, that matrix value was not inverted. This matrix is used in one place: to transform light center coordinates from device to RenderNode/layer coordinate space. Bug: 132758858 Test: Ran test app attached to bug. Manually compared coordinates. Change-Id: Id8e887276a5296091a8264d7a0b1bcc955620a6a (cherry picked from commit 36b75f79611b9ed6f49dc0ec8d93f1cc945368ac) --- libs/hwui/pipeline/skia/SkiaPipeline.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index ccc1701dcc0b..1f9ab5a242b4 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -176,7 +176,7 @@ bool SkiaPipeline::createOrUpdateLayer(RenderNode* node, const DamageAccumulator // position Matrix4 windowTransform; damageAccumulator.computeCurrentTransform(&windowTransform); - node->getSkiaLayer()->inverseTransformInWindow = windowTransform; + node->getSkiaLayer()->inverseTransformInWindow.loadInverse(windowTransform); } else { String8 cachesOutput; mRenderThread.cacheManager().dumpMemoryUsage(cachesOutput, -- cgit v1.2.3 From 4ca1cc96404607912f7323a524ec5d4bfdccd133 Mon Sep 17 00:00:00 2001 From: Daniel Santiago Rivera Date: Tue, 30 Apr 2019 08:57:46 -0700 Subject: Update LOG_WINDOW format params. Params of size_t use %zu formatting, while int64_t use PRId64. These params had not been updated in a while since LOG_WINDOW is a no-op macro when LOG_NDEBUG is off. Test: Enable LOG_NDEBUG and do make Change-Id: I59e9fa1aa343fd0a1da83c40fd24f3ef7bae5ed4 --- libs/androidfw/CursorWindow.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'libs') diff --git a/libs/androidfw/CursorWindow.cpp b/libs/androidfw/CursorWindow.cpp index e1067fcd4d3d..6f05cbd0ebb3 100644 --- a/libs/androidfw/CursorWindow.cpp +++ b/libs/androidfw/CursorWindow.cpp @@ -69,7 +69,7 @@ status_t CursorWindow::create(const String8& name, size_t size, CursorWindow** o result = window->clear(); if (!result) { LOG_WINDOW("Created new CursorWindow: freeOffset=%d, " - "numRows=%d, numColumns=%d, mSize=%d, mData=%p", + "numRows=%d, numColumns=%d, mSize=%zu, mData=%p", window->mHeader->freeOffset, window->mHeader->numRows, window->mHeader->numColumns, @@ -124,7 +124,7 @@ status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outCursor CursorWindow* window = new CursorWindow(name, dupAshmemFd, data, size, true /*readOnly*/); LOG_WINDOW("Created CursorWindow from parcel: freeOffset=%d, " - "numRows=%d, numColumns=%d, mSize=%d, mData=%p", + "numRows=%d, numColumns=%d, mSize=%zu, mData=%p", window->mHeader->freeOffset, window->mHeader->numRows, window->mHeader->numColumns, @@ -200,7 +200,7 @@ status_t CursorWindow::allocRow() { FieldSlot* fieldDir = static_cast(offsetToPtr(fieldDirOffset)); memset(fieldDir, 0, fieldDirSize); - LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %d bytes at offset %u\n", + LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %zu bytes at offset %u\n", mHeader->numRows - 1, offsetFromPtr(rowSlot), fieldDirSize, fieldDirOffset); rowSlot->offset = fieldDirOffset; return OK; -- cgit v1.2.3 From a02a12d765f69325a1d844548e41370bda9dcc6d Mon Sep 17 00:00:00 2001 From: Jerome Gaillard Date: Tue, 28 May 2019 18:07:56 +0100 Subject: Use libbase API to access system properties in libhwui Calls to licutils properties API are replaced by calls to libbase properties API. This will make it easier to make a host target for libhwui as libbase provides a host implementation for its properties API. Bug: 117921091 Test: N/A Change-Id: I15b4fe1a16d2bc7b271387d8cf3403940b8aae2d --- libs/hwui/Android.bp | 1 + libs/hwui/Properties.cpp | 57 +++++++++++++------------------- libs/hwui/Properties.h | 1 - libs/hwui/pipeline/skia/SkiaPipeline.cpp | 7 ++-- 4 files changed, 28 insertions(+), 38 deletions(-) (limited to 'libs') diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 9fe53d3b2649..43d1c952cc52 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -54,6 +54,7 @@ cc_defaults { shared_libs: [ "liblog", "libcutils", + "libbase", "libstatslog", "libutils", "libEGL", diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index af20c4f4f20e..2196b0fde90f 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -23,8 +23,8 @@ #include #include +#include #include -#include #include namespace android { @@ -67,64 +67,54 @@ bool Properties::isolatedProcess = false; int Properties::contextPriority = 0; int Properties::defaultRenderAhead = -1; -static int property_get_int(const char* key, int defaultValue) { - char buf[PROPERTY_VALUE_MAX] = { - '\0', - }; - - if (property_get(key, buf, "") > 0) { - return atoi(buf); - } - return defaultValue; -} - bool Properties::load() { - char property[PROPERTY_VALUE_MAX]; bool prevDebugLayersUpdates = debugLayersUpdates; bool prevDebugOverdraw = debugOverdraw; debugOverdraw = false; - if (property_get(PROPERTY_DEBUG_OVERDRAW, property, nullptr) > 0) { - INIT_LOGD(" Overdraw debug enabled: %s", property); - if (!strcmp(property, "show")) { + std::string debugOverdrawProperty = base::GetProperty(PROPERTY_DEBUG_OVERDRAW, ""); + if (debugOverdrawProperty != "") { + INIT_LOGD(" Overdraw debug enabled: %s", debugOverdrawProperty); + if (debugOverdrawProperty == "show") { debugOverdraw = true; overdrawColorSet = OverdrawColorSet::Default; - } else if (!strcmp(property, "show_deuteranomaly")) { + } else if (debugOverdrawProperty == "show_deuteranomaly") { debugOverdraw = true; overdrawColorSet = OverdrawColorSet::Deuteranomaly; } } sProfileType = ProfileType::None; - if (property_get(PROPERTY_PROFILE, property, "") > 0) { - if (!strcmp(property, PROPERTY_PROFILE_VISUALIZE_BARS)) { + std::string profileProperty = base::GetProperty(PROPERTY_PROFILE, ""); + if (profileProperty != "") { + if (profileProperty == PROPERTY_PROFILE_VISUALIZE_BARS) { sProfileType = ProfileType::Bars; - } else if (!strcmp(property, "true")) { + } else if (profileProperty == "true") { sProfileType = ProfileType::Console; } } - debugLayersUpdates = property_get_bool(PROPERTY_DEBUG_LAYERS_UPDATES, false); + debugLayersUpdates = base::GetBoolProperty(PROPERTY_DEBUG_LAYERS_UPDATES, false); INIT_LOGD(" Layers updates debug enabled: %d", debugLayersUpdates); - showDirtyRegions = property_get_bool(PROPERTY_DEBUG_SHOW_DIRTY_REGIONS, false); + showDirtyRegions = base::GetBoolProperty(PROPERTY_DEBUG_SHOW_DIRTY_REGIONS, false); - debugLevel = (DebugLevel)property_get_int(PROPERTY_DEBUG, kDebugDisabled); + debugLevel = (DebugLevel)base::GetIntProperty(PROPERTY_DEBUG, (int)kDebugDisabled); - skipEmptyFrames = property_get_bool(PROPERTY_SKIP_EMPTY_DAMAGE, true); - useBufferAge = property_get_bool(PROPERTY_USE_BUFFER_AGE, true); - enablePartialUpdates = property_get_bool(PROPERTY_ENABLE_PARTIAL_UPDATES, true); + skipEmptyFrames = base::GetBoolProperty(PROPERTY_SKIP_EMPTY_DAMAGE, true); + useBufferAge = base::GetBoolProperty(PROPERTY_USE_BUFFER_AGE, true); + enablePartialUpdates = base::GetBoolProperty(PROPERTY_ENABLE_PARTIAL_UPDATES, true); - filterOutTestOverhead = property_get_bool(PROPERTY_FILTER_TEST_OVERHEAD, false); + filterOutTestOverhead = base::GetBoolProperty(PROPERTY_FILTER_TEST_OVERHEAD, false); - skpCaptureEnabled = debuggingEnabled && property_get_bool(PROPERTY_CAPTURE_SKP_ENABLED, false); + skpCaptureEnabled = debuggingEnabled && base::GetBoolProperty(PROPERTY_CAPTURE_SKP_ENABLED, false); SkAndroidFrameworkTraceUtil::setEnableTracing( - property_get_bool(PROPERTY_SKIA_ATRACE_ENABLED, false)); + base::GetBoolProperty(PROPERTY_SKIA_ATRACE_ENABLED, false)); - runningInEmulator = property_get_bool(PROPERTY_QEMU_KERNEL, false); + runningInEmulator = base::GetBoolProperty(PROPERTY_QEMU_KERNEL, false); - defaultRenderAhead = std::max(-1, std::min(2, property_get_int(PROPERTY_RENDERAHEAD, + defaultRenderAhead = std::max(-1, std::min(2, base::GetIntProperty(PROPERTY_RENDERAHEAD, render_ahead().value_or(0)))); return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw); @@ -175,9 +165,8 @@ RenderPipelineType Properties::peekRenderPipelineType() { return sRenderPipelineType; } bool useVulkan = use_vulkan().value_or(false); - char prop[PROPERTY_VALUE_MAX]; - property_get(PROPERTY_RENDERER, prop, useVulkan ? "skiavk" : "skiagl"); - if (!strcmp(prop, "skiavk")) { + std::string rendererProperty = base::GetProperty(PROPERTY_RENDERER, useVulkan ? "skiavk" : "skiagl"); + if (rendererProperty == "skiavk") { return RenderPipelineType::SkiaVulkan; } return RenderPipelineType::SkiaGL; diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index 71b07c947716..d3ecb54d94f6 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -18,7 +18,6 @@ #define ANDROID_HWUI_PROPERTIES_H #include -#include /** * This file contains the list of system properties used to configure libhwui. diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 1f9ab5a242b4..066828190627 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -30,6 +30,8 @@ #include +#include + using namespace android::uirenderer::renderthread; namespace android { @@ -240,12 +242,11 @@ static void savePictureAsync(const sk_sp& data, const std::string& filen SkCanvas* SkiaPipeline::tryCapture(SkSurface* surface) { if (CC_UNLIKELY(Properties::skpCaptureEnabled)) { - char prop[PROPERTY_VALUE_MAX] = {'\0'}; if (mCaptureSequence <= 0) { - property_get(PROPERTY_CAPTURE_SKP_FILENAME, prop, "0"); + std::string prop = base::GetProperty(PROPERTY_CAPTURE_SKP_FILENAME, "0"); if (prop[0] != '0' && mCapturedFile != prop) { mCapturedFile = prop; - mCaptureSequence = property_get_int32(PROPERTY_CAPTURE_SKP_FRAMES, 1); + mCaptureSequence = base::GetIntProperty(PROPERTY_CAPTURE_SKP_FRAMES, 1); } } if (mCaptureSequence > 0 || mPictureCapturedCallback) { -- cgit v1.2.3 From 21e7e2d9b13ef1d381b7b0f41f1cac6a947e9bdb Mon Sep 17 00:00:00 2001 From: Jerome Gaillard Date: Tue, 14 May 2019 14:34:46 +0100 Subject: Build native android graphics library for desktop The purpose of this is to be able to use the native graphics code from the Android platform directly in Android Studio (running on desktop) to do layout rendering. This creates a host library that is a subset of libandroid_runtime including only the JNI files relevant to Android graphics. It also includes LayoutlibLoader.cpp which is used to load the JNI when using it as part of layoutlib (the graphics library for Android Studio). This also creates libhwui-host, a host library that is a subset of libhwui. Bug: 117921091 Test: lunch sdk && m libandroid_runtime Change-Id: I3850020d2d4c13c85e377476bc463d3eb6a01c6d --- libs/hwui/Android.bp | 56 ++++++++++++++++++++++++++++++++ libs/hwui/Animator.cpp | 2 ++ libs/hwui/HardwareBitmapUploader.h | 6 ++++ libs/hwui/Properties.cpp | 13 ++++++++ libs/hwui/SkiaCanvas.h | 2 ++ libs/hwui/VectorDrawable.cpp | 6 ++++ libs/hwui/WebViewFunctorManager.h | 10 +++++- libs/hwui/hwui/AnimatedImageDrawable.cpp | 6 ++++ libs/hwui/hwui/Bitmap.cpp | 32 ++++++++++++++++++ libs/hwui/hwui/Bitmap.h | 10 ++++++ libs/hwui/hwui/Canvas.cpp | 4 +++ libs/hwui/hwui/Typeface.cpp | 4 +++ libs/hwui/renderthread/CacheManager.h | 8 +++++ libs/hwui/utils/TraceUtils.h | 1 + 14 files changed, 159 insertions(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 43d1c952cc52..47f22d6eb51e 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -398,3 +398,59 @@ phony { "hwuimacro", ] } + +cc_library_host_shared { + name: "libhwui-host", + + defaults: [ + "skia_deps", + ], + whole_static_libs: ["libskia"], + + srcs: [ + "hwui/AnimatedImageDrawable.cpp", + "hwui/AnimatedImageThread.cpp", + "hwui/Bitmap.cpp", + "hwui/Canvas.cpp", + "hwui/Typeface.cpp", + "hwui/MinikinSkia.cpp", + "hwui/MinikinUtils.cpp", + "hwui/PaintImpl.cpp", + "utils/Blur.cpp", + "utils/LinearAllocator.cpp", + "utils/VectorDrawableUtils.cpp", + "Animator.cpp", + "Interpolator.cpp", + "Matrix.cpp", + "PathParser.cpp", + "Properties.cpp", + "PropertyValuesAnimatorSet.cpp", + "PropertyValuesHolder.cpp", + "SkiaCanvas.cpp", + "VectorDrawable.cpp", + ], + include_dirs: [ + "external/skia/include/private", + "external/skia/src/core", + "external/vulkan-headers/include", + "system/core/base/include", + ], + cpp_std: "experimental", + cflags: [ + "-Wno-unused-parameter", + "-Wno-unused-variable", + ], + shared_libs: [ + "libbase", + "libharfbuzz_ng", + "libminikin", + ], + static_libs: [ + "libandroidfw", + "libutils", + ], + export_include_dirs: ["."], + export_static_lib_headers: [ + "libarect", + ], +} diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp index 74cf1fda1b75..93b9decd9cbc 100644 --- a/libs/hwui/Animator.cpp +++ b/libs/hwui/Animator.cpp @@ -155,9 +155,11 @@ void BaseRenderNodeAnimator::pushStaging(AnimationContext& context) { RenderNode* oldTarget = mTarget; mTarget = mStagingTarget; mStagingTarget = nullptr; +#ifdef __ANDROID__ // Layoutlib does not support RenderNode if (oldTarget && oldTarget != mTarget) { oldTarget->onAnimatorTargetChanged(this); } +#endif } if (!mHasStartValue) { diff --git a/libs/hwui/HardwareBitmapUploader.h b/libs/hwui/HardwareBitmapUploader.h index c300593d47a1..72243d23dd35 100644 --- a/libs/hwui/HardwareBitmapUploader.h +++ b/libs/hwui/HardwareBitmapUploader.h @@ -27,7 +27,13 @@ public: static sk_sp allocateHardwareBitmap(const SkBitmap& sourceBitmap); +#ifdef __ANDROID__ static bool hasFP16Support(); +#else + static bool hasFP16Support() { + return true; + } +#endif }; } // namespace android::uirenderer diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index 2196b0fde90f..1253beb2d2b2 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -17,11 +17,14 @@ #include "Properties.h" #include "Debug.h" #include "DeviceInfo.h" +#ifdef __ANDROID__ #include "HWUIProperties.sysprop.h" +#endif #include "SkTraceEventCommon.h" #include #include +#include #include #include @@ -30,6 +33,16 @@ namespace android { namespace uirenderer { +#ifndef __ANDROID__ // Layoutlib does not compile HWUIProperties.sysprop as it depends on cutils properties +std::optional use_vulkan() { + return base::GetBoolProperty("ro.hwui.use_vulkan", false); +} + +std::optional render_ahead() { + return base::GetIntProperty("ro.hwui.render_ahead", 0); +} +#endif + bool Properties::debugLayersUpdates = false; bool Properties::debugOverdraw = false; bool Properties::showDirtyRegions = false; diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h index 12ba091ab117..05a6d0dda42d 100644 --- a/libs/hwui/SkiaCanvas.h +++ b/libs/hwui/SkiaCanvas.h @@ -16,7 +16,9 @@ #pragma once #include "CanvasProperty.h" +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration #include "DeferredLayerUpdater.h" +#endif #include "RenderNode.h" #include "VectorDrawable.h" #include "hwui/Canvas.h" diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp index 5418b337c371..89ad1b99c6b6 100644 --- a/libs/hwui/VectorDrawable.cpp +++ b/libs/hwui/VectorDrawable.cpp @@ -487,6 +487,7 @@ Bitmap& Tree::getBitmapUpdateIfDirty() { } void Tree::updateCache(sp& atlas, GrContext* context) { +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration SkRect dst; sk_sp surface = mCache.getSurface(&dst); bool canReuseSurface = surface && dst.width() >= mProperties.getScaledWidth() && @@ -514,6 +515,7 @@ void Tree::updateCache(sp& atlas, GrContext* } mCache.dirty = false; } +#endif } void Tree::Cache::setAtlas(sp newAtlas, @@ -526,6 +528,7 @@ void Tree::Cache::setAtlas(sp newAtlas, sk_sp Tree::Cache::getSurface(SkRect* bounds) { sk_sp surface; +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration sp atlas = mAtlas.promote(); if (atlas.get() && mAtlasKey != INVALID_ATLAS_KEY) { auto atlasEntry = atlas->getEntry(mAtlasKey); @@ -533,17 +536,20 @@ sk_sp Tree::Cache::getSurface(SkRect* bounds) { surface = atlasEntry.surface; mAtlasKey = atlasEntry.key; } +#endif return surface; } void Tree::Cache::clear() { +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration sp lockAtlas = mAtlas.promote(); if (lockAtlas.get()) { lockAtlas->releaseEntry(mAtlasKey); } mAtlas = nullptr; mAtlasKey = INVALID_ATLAS_KEY; +#endif } void Tree::draw(SkCanvas* canvas, const SkRect& bounds, const SkPaint& inPaint) { diff --git a/libs/hwui/WebViewFunctorManager.h b/libs/hwui/WebViewFunctorManager.h index 2846cb1f087b..675b738c6406 100644 --- a/libs/hwui/WebViewFunctorManager.h +++ b/libs/hwui/WebViewFunctorManager.h @@ -17,7 +17,11 @@ #pragma once #include +#ifdef __ANDROID__ // Layoutlib does not support render thread #include +#else +#include +#endif #include #include @@ -34,7 +38,11 @@ public: class Handle : public LightRefBase { public: - ~Handle() { renderthread::RenderProxy::destroyFunctor(id()); } + ~Handle() { +#ifdef __ANDROID__ // Layoutlib does not support render thread + renderthread::RenderProxy::destroyFunctor(id()); +#endif + } int id() const { return mReference.id(); } diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp index 8d4e7e09b458..7677f9c6628d 100644 --- a/libs/hwui/hwui/AnimatedImageDrawable.cpp +++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp @@ -24,6 +24,12 @@ #include +#ifdef __APPLE__ + // macOS SDK 10.10 does not support CLOCK_MONOTONIC, which is not an issue since + // the value of the argument is not used in the host definition of systemTime +#define CLOCK_MONOTONIC +#endif + namespace android { AnimatedImageDrawable::AnimatedImageDrawable(sk_sp animatedImage, size_t bytesUsed) diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index 219d04055eae..4c2f0ad102bd 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -17,17 +17,23 @@ #include "HardwareBitmapUploader.h" #include "Properties.h" +#ifdef __ANDROID__ // Layoutlib does not support render thread #include "renderthread/RenderProxy.h" +#endif #include "utils/Color.h" #include +#ifndef _WIN32 #include +#endif #include #include +#ifndef _WIN32 #include #include +#endif #include #include @@ -76,6 +82,7 @@ sk_sp Bitmap::allocateAshmemBitmap(SkBitmap* bitmap) { } sk_sp Bitmap::allocateAshmemBitmap(size_t size, const SkImageInfo& info, size_t rowBytes) { +#ifdef __ANDROID__ // Create new ashmem region with read/write priv int fd = ashmem_create_region("bitmap", size); if (fd < 0) { @@ -94,10 +101,17 @@ sk_sp Bitmap::allocateAshmemBitmap(size_t size, const SkImageInfo& info, return nullptr; } return sk_sp(new Bitmap(addr, fd, size, info, rowBytes)); +#else + return Bitmap::allocateHeapBitmap(size, info, rowBytes); +#endif } sk_sp Bitmap::allocateHardwareBitmap(const SkBitmap& bitmap) { +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration return uirenderer::HardwareBitmapUploader::allocateHardwareBitmap(bitmap); +#else + return Bitmap::allocateHeapBitmap(bitmap.info()); +#endif } sk_sp Bitmap::allocateHeapBitmap(SkBitmap* bitmap) { @@ -133,6 +147,7 @@ sk_sp Bitmap::createFrom(const SkImageInfo& info, SkPixelRef& pixelRef) } +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration sk_sp Bitmap::createFrom(sp graphicBuffer, SkColorType colorType, sk_sp colorSpace, SkAlphaType alphaType, BitmapPalette palette) { @@ -140,9 +155,13 @@ sk_sp Bitmap::createFrom(sp graphicBuffer, SkColorType co colorType, alphaType, colorSpace); return sk_sp(new Bitmap(graphicBuffer.get(), info, palette)); } +#endif sk_sp Bitmap::createFrom(const SkImageInfo& info, size_t rowBytes, int fd, void* addr, size_t size, bool readOnly) { +#ifdef _WIN32 // ashmem not implemented on Windows + return nullptr; +#else if (info.colorType() == kUnknown_SkColorType) { LOG_ALWAYS_FATAL("unknown bitmap configuration"); return nullptr; @@ -163,6 +182,7 @@ sk_sp Bitmap::createFrom(const SkImageInfo& info, size_t rowBytes, int f bitmap->setImmutable(); } return bitmap; +#endif } void Bitmap::setColorSpace(sk_sp colorSpace) { @@ -217,6 +237,7 @@ Bitmap::Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info mPixelStorage.ashmem.size = mappedSize; } +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration Bitmap::Bitmap(GraphicBuffer* buffer, const SkImageInfo& info, BitmapPalette palette) : SkPixelRef(info.width(), info.height(), nullptr, bytesPerPixel(buffer->getPixelFormat()) * buffer->getStride()) @@ -230,6 +251,7 @@ Bitmap::Bitmap(GraphicBuffer* buffer, const SkImageInfo& info, BitmapPalette pal mImage = SkImage::MakeFromAHardwareBuffer(reinterpret_cast(buffer), mInfo.alphaType(), mInfo.refColorSpace()); } +#endif Bitmap::~Bitmap() { switch (mPixelStorageType) { @@ -238,17 +260,23 @@ Bitmap::~Bitmap() { mPixelStorage.external.context); break; case PixelStorageType::Ashmem: +#ifndef _WIN32 // ashmem not implemented on Windows munmap(mPixelStorage.ashmem.address, mPixelStorage.ashmem.size); +#endif close(mPixelStorage.ashmem.fd); break; case PixelStorageType::Heap: free(mPixelStorage.heap.address); +#ifdef __ANDROID__ mallopt(M_PURGE, 0); +#endif break; case PixelStorageType::Hardware: +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration auto buffer = mPixelStorage.hardware.buffer; buffer->decStrong(buffer); mPixelStorage.hardware.buffer = nullptr; +#endif break; } } @@ -307,11 +335,13 @@ void Bitmap::setAlphaType(SkAlphaType alphaType) { } void Bitmap::getSkBitmap(SkBitmap* outBitmap) { +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration if (isHardware()) { outBitmap->allocPixels(mInfo); uirenderer::renderthread::RenderProxy::copyHWBitmapInto(this, outBitmap); return; } +#endif outBitmap->setInfo(mInfo, rowBytes()); outBitmap->setPixelRef(sk_ref_sp(this), 0, 0); } @@ -321,12 +351,14 @@ void Bitmap::getBounds(SkRect* bounds) const { bounds->set(0, 0, SkIntToScalar(width()), SkIntToScalar(height())); } +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration GraphicBuffer* Bitmap::graphicBuffer() { if (isHardware()) { return mPixelStorage.hardware.buffer; } return nullptr; } +#endif sk_sp Bitmap::makeImage() { sk_sp image = mImage; diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h index dd98b25ac7e8..c7e18d10de96 100644 --- a/libs/hwui/hwui/Bitmap.h +++ b/libs/hwui/hwui/Bitmap.h @@ -23,7 +23,9 @@ #include #include #include +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration #include +#endif namespace android { @@ -71,11 +73,13 @@ public: /* The createFrom factories construct a new Bitmap object by wrapping the already allocated * memory that is provided as an input param. */ +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration static sk_sp createFrom(sp graphicBuffer, SkColorType colorType, sk_sp colorSpace, SkAlphaType alphaType = kPremul_SkAlphaType, BitmapPalette palette = BitmapPalette::Unknown); +#endif static sk_sp createFrom(const SkImageInfo& info, size_t rowBytes, int fd, void* addr, size_t size, bool readOnly); static sk_sp createFrom(const SkImageInfo&, SkPixelRef&); @@ -105,7 +109,9 @@ public: PixelStorageType pixelStorageType() const { return mPixelStorageType; } +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration GraphicBuffer* graphicBuffer(); +#endif /** * Creates or returns a cached SkImage and is safe to be invoked from either @@ -136,7 +142,9 @@ private: Bitmap(void* address, void* context, FreeFunc freeFunc, const SkImageInfo& info, size_t rowBytes); Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info, size_t rowBytes); +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration Bitmap(GraphicBuffer* buffer, const SkImageInfo& info, BitmapPalette palette); +#endif virtual ~Bitmap(); void* getStorage() const; @@ -165,9 +173,11 @@ private: void* address; size_t size; } heap; +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration struct { GraphicBuffer* buffer; } hardware; +#endif } mPixelStorage; sk_sp mImage; // Cache is used only for HW Bitmaps with Skia pipeline. diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp index a48c86028262..e2ddb91f1565 100644 --- a/libs/hwui/hwui/Canvas.cpp +++ b/libs/hwui/hwui/Canvas.cpp @@ -30,7 +30,11 @@ namespace android { Canvas* Canvas::create_recording_canvas(int width, int height, uirenderer::RenderNode* renderNode) { +#ifdef __ANDROID__ // Layoutlib does not support recording canvas return new uirenderer::skiapipeline::SkiaRecordingCanvas(renderNode, width, height); +#else + return NULL; +#endif } static inline void drawStroke(SkScalar left, SkScalar right, SkScalar top, SkScalar thickness, diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp index c4d8aa6c8fad..ccc328c702db 100644 --- a/libs/hwui/hwui/Typeface.cpp +++ b/libs/hwui/hwui/Typeface.cpp @@ -18,7 +18,9 @@ #include // For tests. #include +#ifndef _WIN32 #include // For tests. +#endif #include // For tests. #include "MinikinSkia.h" @@ -171,6 +173,7 @@ void Typeface::setDefault(const Typeface* face) { } void Typeface::setRobotoTypefaceForTest() { +#ifndef _WIN32 const char* kRobotoFont = "/system/fonts/Roboto-Regular.ttf"; int fd = open(kRobotoFont, O_RDONLY); @@ -198,5 +201,6 @@ void Typeface::setRobotoTypefaceForTest() { hwTypeface->fStyle = minikin::FontStyle(); Typeface::setDefault(hwTypeface); +#endif } } // namespace android diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h index 9a5a00fcf762..ad251f2364fe 100644 --- a/libs/hwui/renderthread/CacheManager.h +++ b/libs/hwui/renderthread/CacheManager.h @@ -17,7 +17,9 @@ #ifndef CACHEMANAGER_H #define CACHEMANAGER_H +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration #include +#endif #include #include #include @@ -42,7 +44,9 @@ class CacheManager { public: enum class TrimMemoryMode { Complete, UiHidden }; +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration void configureContext(GrContextOptions* context, const void* identity, ssize_t size); +#endif void trimMemory(TrimMemoryMode mode); void trimStaleResources(); void dumpMemoryUsage(String8& log, const RenderState* renderState = nullptr); @@ -57,11 +61,15 @@ private: explicit CacheManager(const DisplayInfo& display); +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration void reset(sk_sp grContext); +#endif void destroy(); const size_t mMaxSurfaceArea; +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration sk_sp mGrContext; +#endif int mMaxResources = 0; const size_t mMaxResourceBytes; diff --git a/libs/hwui/utils/TraceUtils.h b/libs/hwui/utils/TraceUtils.h index 1869d00396c0..e61b4be1784e 100644 --- a/libs/hwui/utils/TraceUtils.h +++ b/libs/hwui/utils/TraceUtils.h @@ -16,6 +16,7 @@ #ifndef TRACE_UTILS_H #define TRACE_UTILS_H +#include #include #define ATRACE_FORMAT(fmt, ...) \ -- cgit v1.2.3 From 23462d88f5a3753111aba3835a5aef8e619468d6 Mon Sep 17 00:00:00 2001 From: John Reck Date: Wed, 29 May 2019 16:55:06 -0700 Subject: Delete wrap_gles capability * Error enforcement doesn't work with libhwui anymore * We can just do the real, proper thing for unit tests & microbenchmarks, which is arguably better anyway * hwui doesn't have much opengl code left that was wrapped Fixes: 128629988 Test: hwuiunit, hwuimacro, and hwuimicro all still run Change-Id: If2568ea0745a67f83e1290860d474c1a01870376 --- libs/hwui/Android.bp | 54 +- libs/hwui/Properties.cpp | 4 +- libs/hwui/debug/DefaultGlesDriver.cpp | 40 - libs/hwui/debug/DefaultGlesDriver.h | 34 - libs/hwui/debug/FatalBaseDriver.cpp | 40 - libs/hwui/debug/FatalBaseDriver.h | 37 - libs/hwui/debug/GlesDriver.cpp | 46 - libs/hwui/debug/GlesDriver.h | 55 - libs/hwui/debug/GlesErrorCheckWrapper.cpp | 75 -- libs/hwui/debug/GlesErrorCheckWrapper.h | 41 - libs/hwui/debug/MockGlesDriver.h | 37 - libs/hwui/debug/NullGlesDriver.cpp | 174 --- libs/hwui/debug/NullGlesDriver.h | 202 ---- libs/hwui/debug/NullSkiaInterface.cpp | 1402 ------------------------ libs/hwui/debug/ScopedReplaceDriver.h | 45 - libs/hwui/debug/gles_decls.in | 543 ---------- libs/hwui/debug/gles_redefine.h | 914 ---------------- libs/hwui/debug/gles_stubs.in | 1629 ---------------------------- libs/hwui/debug/gles_undefine.h | 913 ---------------- libs/hwui/debug/nullegl.cpp | 175 --- libs/hwui/debug/wrap_gles.cpp | 33 - libs/hwui/debug/wrap_gles.h | 40 - libs/hwui/renderthread/RenderThread.cpp | 9 - libs/hwui/tests/microbench/main.cpp | 5 - libs/hwui/tests/unit/SkiaPipelineTests.cpp | 13 +- libs/hwui/tests/unit/main.cpp | 3 - libs/hwui/utils/GLUtils.cpp | 10 - 27 files changed, 9 insertions(+), 6564 deletions(-) delete mode 100644 libs/hwui/debug/DefaultGlesDriver.cpp delete mode 100644 libs/hwui/debug/DefaultGlesDriver.h delete mode 100644 libs/hwui/debug/FatalBaseDriver.cpp delete mode 100644 libs/hwui/debug/FatalBaseDriver.h delete mode 100644 libs/hwui/debug/GlesDriver.cpp delete mode 100644 libs/hwui/debug/GlesDriver.h delete mode 100644 libs/hwui/debug/GlesErrorCheckWrapper.cpp delete mode 100644 libs/hwui/debug/GlesErrorCheckWrapper.h delete mode 100644 libs/hwui/debug/MockGlesDriver.h delete mode 100644 libs/hwui/debug/NullGlesDriver.cpp delete mode 100644 libs/hwui/debug/NullGlesDriver.h delete mode 100644 libs/hwui/debug/NullSkiaInterface.cpp delete mode 100644 libs/hwui/debug/ScopedReplaceDriver.h delete mode 100644 libs/hwui/debug/gles_decls.in delete mode 100644 libs/hwui/debug/gles_redefine.h delete mode 100644 libs/hwui/debug/gles_stubs.in delete mode 100644 libs/hwui/debug/gles_undefine.h delete mode 100644 libs/hwui/debug/nullegl.cpp delete mode 100644 libs/hwui/debug/wrap_gles.cpp delete mode 100644 libs/hwui/debug/wrap_gles.h (limited to 'libs') diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 43d1c952cc52..11d92721e1e0 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -92,28 +92,6 @@ cc_defaults { ], } -cc_defaults { - name: "hwui_debug", - cflags: ["-include debug/wrap_gles.h"], - srcs: [ - "debug/wrap_gles.cpp", - "debug/DefaultGlesDriver.cpp", - "debug/GlesErrorCheckWrapper.cpp", - "debug/GlesDriver.cpp", - "debug/FatalBaseDriver.cpp", - "debug/NullGlesDriver.cpp", - "debug/NullSkiaInterface.cpp", - ], - include_dirs: ["frameworks/native/opengl/libs/GLES2"], -} - -cc_defaults { - name: "hwui_enable_opengl_validation", - defaults: ["hwui_debug"], - cflags: ["-DDEBUG_OPENGL=3"], - include_dirs: ["frameworks/native/opengl/libs/GLES2"], -} - // Build libhwui with PGO by default. // Location of PGO profile data is defined in build/soong/cc/pgo.go // and is separate from hwui. @@ -248,27 +226,13 @@ cc_library { name: "libhwui", defaults: [ "libhwui_defaults", - - // Enables fine-grained GLES error checking - // If enabled, every GLES call is wrapped & error checked - // Has moderate overhead - //"hwui_enable_opengl_validation", ], } -// ------------------------ -// static library null gpu -// ------------------------ - cc_library_static { - name: "libhwui_static_debug", + name: "libhwui_static", defaults: [ "libhwui_defaults", - "hwui_debug", - ], - cflags: ["-DHWUI_NULL_GPU"], - srcs: [ - "debug/nullegl.cpp", ], } @@ -296,15 +260,11 @@ cc_test { static_libs: [ "libgmock", - "libhwui_static_debug", + "libhwui_static", ], shared_libs: [ "libmemunreachable", ], - cflags: [ - "-include debug/wrap_gles.h", - "-DHWUI_NULL_GPU", - ], srcs: [ "tests/unit/main.cpp", @@ -348,8 +308,7 @@ cc_benchmark { name: "hwuimacro", defaults: ["hwui_test_defaults"], - // set to libhwui_static_debug to skip actual GL commands - whole_static_libs: ["libhwui"], + static_libs: ["libhwui"], shared_libs: [ "libmemunreachable", ], @@ -368,12 +327,7 @@ cc_benchmark { name: "hwuimicro", defaults: ["hwui_test_defaults"], - cflags: [ - "-include debug/wrap_gles.h", - "-DHWUI_NULL_GPU", - ], - - whole_static_libs: ["libhwui_static_debug"], + static_libs: ["libhwui_static"], shared_libs: [ "libmemunreachable", ], diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index 2196b0fde90f..7c2132dc543c 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -178,14 +178,14 @@ RenderPipelineType Properties::getRenderPipelineType() { } void Properties::overrideRenderPipelineType(RenderPipelineType type) { -#if !defined(HWUI_GLES_WRAP_ENABLED) // If we're doing actual rendering then we can't change the renderer after it's been set. // Unit tests can freely change this as often as it wants, though, as there's no actual // GL rendering happening if (sRenderPipelineType != RenderPipelineType::NotInitialized) { + LOG_ALWAYS_FATAL_IF(sRenderPipelineType != type, + "Trying to change pipeline but it's already set"); return; } -#endif sRenderPipelineType = type; } diff --git a/libs/hwui/debug/DefaultGlesDriver.cpp b/libs/hwui/debug/DefaultGlesDriver.cpp deleted file mode 100644 index 46ab20081d17..000000000000 --- a/libs/hwui/debug/DefaultGlesDriver.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2016 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 "DefaultGlesDriver.h" - -#include "gles_undefine.h" - -#include - -namespace android { -namespace uirenderer { -namespace debug { - -// Generate the proxy -#define API_ENTRY(x) DefaultGlesDriver::x##_ -#define CALL_GL_API(x, ...) x(__VA_ARGS__); -#define CALL_GL_API_RETURN(x, ...) return x(__VA_ARGS__); - -#include "gles_stubs.in" - -#undef API_ENTRY -#undef CALL_GL_API -#undef CALL_GL_API_RETURN - -} // namespace debug -} // namespace uirenderer -} // namespace android diff --git a/libs/hwui/debug/DefaultGlesDriver.h b/libs/hwui/debug/DefaultGlesDriver.h deleted file mode 100644 index 8027ea284aaa..000000000000 --- a/libs/hwui/debug/DefaultGlesDriver.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2016 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. - */ - -#pragma once - -#include "GlesDriver.h" - -namespace android { -namespace uirenderer { -namespace debug { - -class DefaultGlesDriver : public GlesDriver { -public: -#define GL_ENTRY(ret, api, ...) virtual ret api##_(__VA_ARGS__) override; -#include "gles_decls.in" -#undef GL_ENTRY -}; - -} // namespace debug -} // namespace uirenderer -} // namespace android diff --git a/libs/hwui/debug/FatalBaseDriver.cpp b/libs/hwui/debug/FatalBaseDriver.cpp deleted file mode 100644 index ed0f8316d208..000000000000 --- a/libs/hwui/debug/FatalBaseDriver.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2016 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 "FatalBaseDriver.h" - -#include - -namespace android { -namespace uirenderer { -namespace debug { - -// Generate the proxy -#define API_ENTRY(x) FatalBaseDriver::x##_ -#define CALL_GL_API(x, ...) LOG_ALWAYS_FATAL("Not Implemented"); -#define CALL_GL_API_RETURN(x, ...) \ - LOG_ALWAYS_FATAL("Not Implemented"); \ - return static_cast(0); - -#include "gles_stubs.in" - -#undef API_ENTRY -#undef CALL_GL_API -#undef CALL_GL_API_RETURN - -} // namespace debug -} // namespace uirenderer -} // namespace android diff --git a/libs/hwui/debug/FatalBaseDriver.h b/libs/hwui/debug/FatalBaseDriver.h deleted file mode 100644 index 45353d0568aa..000000000000 --- a/libs/hwui/debug/FatalBaseDriver.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2016 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. - */ - -#pragma once - -#include "GlesDriver.h" - -namespace android { -namespace uirenderer { -namespace debug { - -// A base driver that implements all the pure virtuals in the form of -// LOG_ALWAYS_FATALS. Suitable for selective-override implementations -// where only a known subset of methods need to be overridden -class FatalBaseDriver : public GlesDriver { -public: -#define GL_ENTRY(ret, api, ...) virtual ret api##_(__VA_ARGS__) override; -#include "gles_decls.in" -#undef GL_ENTRY -}; - -} // namespace debug -} // namespace uirenderer -} // namespace android diff --git a/libs/hwui/debug/GlesDriver.cpp b/libs/hwui/debug/GlesDriver.cpp deleted file mode 100644 index 98f06b0cb4f0..000000000000 --- a/libs/hwui/debug/GlesDriver.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2016 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 "GlesDriver.h" -#include "DefaultGlesDriver.h" -#include "GlesErrorCheckWrapper.h" - -namespace android { -namespace uirenderer { -namespace debug { - -static DefaultGlesDriver sDefaultDriver; - -static std::unique_ptr sGlesDriver(new GlesErrorCheckWrapper(sDefaultDriver)); - -GlesDriver* GlesDriver::get() { - return sGlesDriver.get(); -} - -std::unique_ptr GlesDriver::replace(std::unique_ptr&& driver) { - std::unique_ptr ret = std::move(sGlesDriver); - sGlesDriver = std::move(driver); - return ret; -} - -sk_sp GlesDriver::getSkiaInterface() { - sk_sp skiaInterface(GrGLCreateNativeInterface()); - return skiaInterface; -} - -} // namespace debug -} // namespace uirenderer -} // namespace android diff --git a/libs/hwui/debug/GlesDriver.h b/libs/hwui/debug/GlesDriver.h deleted file mode 100644 index 1c77c1a45b82..000000000000 --- a/libs/hwui/debug/GlesDriver.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2016 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. - */ - -#pragma once - -#ifndef HWUI_GLES_WRAP_ENABLED -#error Wrapping wasn't enabled, can't use this! -#endif - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace android { -namespace uirenderer { -namespace debug { - -// All the gl methods on GlesDriver have a trailing underscore -// This is to avoid collision with gles_redefine/gles_undefine -class GlesDriver { -public: - virtual ~GlesDriver() {} - virtual sk_sp getSkiaInterface(); - -#define GL_ENTRY(ret, api, ...) virtual ret api##_(__VA_ARGS__) = 0; -#include "gles_decls.in" -#undef GL_ENTRY - - static GlesDriver* get(); - static std::unique_ptr replace(std::unique_ptr&& driver); -}; - -} // namespace debug -} // namespace uirenderer -} // namespace android diff --git a/libs/hwui/debug/GlesErrorCheckWrapper.cpp b/libs/hwui/debug/GlesErrorCheckWrapper.cpp deleted file mode 100644 index 8d11c1905da3..000000000000 --- a/libs/hwui/debug/GlesErrorCheckWrapper.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2016 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 "GlesErrorCheckWrapper.h" - -#include - -namespace android { -namespace uirenderer { -namespace debug { - -void GlesErrorCheckWrapper::assertNoErrors(const char* apicall) { - GLenum status = GL_NO_ERROR; - GLenum lastError = GL_NO_ERROR; - const char* lastErrorName = nullptr; - while ((status = mBase.glGetError_()) != GL_NO_ERROR) { - lastError = status; - switch (status) { - case GL_INVALID_ENUM: - ALOGE("GL error: GL_INVALID_ENUM"); - lastErrorName = "GL_INVALID_ENUM"; - break; - case GL_INVALID_VALUE: - ALOGE("GL error: GL_INVALID_VALUE"); - lastErrorName = "GL_INVALID_VALUE"; - break; - case GL_INVALID_OPERATION: - ALOGE("GL error: GL_INVALID_OPERATION"); - lastErrorName = "GL_INVALID_OPERATION"; - break; - case GL_OUT_OF_MEMORY: - ALOGE("GL error: Out of memory!"); - lastErrorName = "GL_OUT_OF_MEMORY"; - break; - default: - ALOGE("GL error: 0x%x", status); - lastErrorName = "UNKNOWN"; - } - } - LOG_ALWAYS_FATAL_IF(lastError != GL_NO_ERROR, "%s error! %s (0x%x)", apicall, lastErrorName, - lastError); -} - -#define API_ENTRY(x) GlesErrorCheckWrapper::x##_ -#define CALL_GL_API(x, ...) \ - mBase.x##_(__VA_ARGS__); \ - assertNoErrors(#x) - -#define CALL_GL_API_RETURN(x, ...) \ - auto ret = mBase.x##_(__VA_ARGS__); \ - assertNoErrors(#x); \ - return ret - -#include "gles_stubs.in" - -#undef API_ENTRY -#undef CALL_GL_API -#undef CALL_GL_API_RETURN - -} // namespace debug -} // namespace uirenderer -} // namespace android diff --git a/libs/hwui/debug/GlesErrorCheckWrapper.h b/libs/hwui/debug/GlesErrorCheckWrapper.h deleted file mode 100644 index 791400bf30ec..000000000000 --- a/libs/hwui/debug/GlesErrorCheckWrapper.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2016 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. - */ - -#pragma once - -#include "GlesDriver.h" - -namespace android { -namespace uirenderer { -namespace debug { - -class GlesErrorCheckWrapper : public GlesDriver { -public: - explicit GlesErrorCheckWrapper(GlesDriver& base) : mBase(base) {} - -#define GL_ENTRY(ret, api, ...) virtual ret api##_(__VA_ARGS__) override; -#include "gles_decls.in" -#undef GL_ENTRY - -private: - void assertNoErrors(const char* apicall); - - GlesDriver& mBase; -}; - -} // namespace debug -} // namespace uirenderer -} // namespace android diff --git a/libs/hwui/debug/MockGlesDriver.h b/libs/hwui/debug/MockGlesDriver.h deleted file mode 100644 index e48ca193d48f..000000000000 --- a/libs/hwui/debug/MockGlesDriver.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2016 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. - */ - -#pragma once - -#include "FatalBaseDriver.h" - -#include - -namespace android { -namespace uirenderer { -namespace debug { - -class MockGlesDriver : public FatalBaseDriver { -public: - MOCK_METHOD2(glBindBuffer_, void(GLenum target, GLuint buffer)); - MOCK_METHOD4(glBufferData_, - void(GLenum target, GLsizeiptr size, const void* data, GLenum usage)); - MOCK_METHOD2(glGenBuffers_, void(GLsizei n, GLuint* buffers)); -}; - -} // namespace debug -} // namespace uirenderer -} // namespace android diff --git a/libs/hwui/debug/NullGlesDriver.cpp b/libs/hwui/debug/NullGlesDriver.cpp deleted file mode 100644 index f27adf0177d2..000000000000 --- a/libs/hwui/debug/NullGlesDriver.cpp +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (C) 2016 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 - -namespace android { -namespace uirenderer { -namespace debug { - -extern const GrGLInterface* CreateNullSkiaInterface(); - -sk_sp NullGlesDriver::getSkiaInterface() { - sk_sp skiaInterface(CreateNullSkiaInterface()); - return skiaInterface; -} - -struct { - GLboolean scissorEnabled; -} gState; - -static void nullglGenCommon(GLsizei n, GLuint* buffers) { - static GLuint nextId = 0; - int i; - for (i = 0; i < n; i++) { - buffers[i] = ++nextId; - } -} - -void NullGlesDriver::glGenBuffers_(GLsizei n, GLuint* buffers) { - nullglGenCommon(n, buffers); -} - -void NullGlesDriver::glGenFramebuffers_(GLsizei n, GLuint* framebuffers) { - nullglGenCommon(n, framebuffers); -} - -void NullGlesDriver::glGenRenderbuffers_(GLsizei n, GLuint* renderbuffers) { - nullglGenCommon(n, renderbuffers); -} - -void NullGlesDriver::glGenTextures_(GLsizei n, GLuint* textures) { - nullglGenCommon(n, textures); -} - -GLuint NullGlesDriver::glCreateProgram_(void) { - static GLuint nextProgram = 0; - return ++nextProgram; -} - -GLuint NullGlesDriver::glCreateShader_(GLenum type) { - static GLuint nextShader = 0; - return ++nextShader; -} - -void NullGlesDriver::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 NullGlesDriver::glGetProgramInfoLog_(GLuint program, GLsizei bufSize, GLsizei* length, - GLchar* infoLog) { - *length = snprintf(infoLog, bufSize, "success"); - if (*length >= bufSize) { - *length = bufSize - 1; - } -} - -void NullGlesDriver::glGetShaderiv_(GLuint shader, GLenum pname, GLint* params) { - switch (pname) { - case GL_COMPILE_STATUS: - case GL_DELETE_STATUS: - *params = GL_TRUE; - } -} - -void NullGlesDriver::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 NullGlesDriver::glEnable_(GLenum cap) { - setBooleanState(cap, GL_TRUE); -} - -void NullGlesDriver::glDisable_(GLenum cap) { - setBooleanState(cap, GL_FALSE); -} - -GLboolean NullGlesDriver::glIsEnabled_(GLenum cap) { - switch (cap) { - case GL_SCISSOR_TEST: - return gState.scissorEnabled; - default: - return GL_FALSE; - } -} - -void NullGlesDriver::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; - } -} - -GLenum NullGlesDriver::glCheckFramebufferStatus_(GLenum target) { - switch (target) { - case GL_FRAMEBUFFER: - return GL_FRAMEBUFFER_COMPLETE; - default: - return 0; // error case - } -} - -static 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* NullGlesDriver::glGetString_(GLenum name) { - return (GLubyte*)getString(name); -} - -} // namespace debug -} // namespace uirenderer -} // namespace android diff --git a/libs/hwui/debug/NullGlesDriver.h b/libs/hwui/debug/NullGlesDriver.h deleted file mode 100644 index 1a27dbc35299..000000000000 --- a/libs/hwui/debug/NullGlesDriver.h +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (C) 2016 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. - */ - -#pragma once - -#include "FatalBaseDriver.h" - -namespace android { -namespace uirenderer { -namespace debug { - -class NullGlesDriver : public FatalBaseDriver { -public: - virtual sk_sp getSkiaInterface() override; - - virtual void glGenBuffers_(GLsizei n, GLuint* buffers) override; - virtual void glGenFramebuffers_(GLsizei n, GLuint* framebuffers) override; - virtual void glGenRenderbuffers_(GLsizei n, GLuint* renderbuffers) override; - virtual void glGenTextures_(GLsizei n, GLuint* textures) override; - virtual GLuint glCreateProgram_(void) override; - virtual GLuint glCreateShader_(GLenum type) override; - virtual void glGetProgramiv_(GLuint program, GLenum pname, GLint* params) override; - virtual void glGetProgramInfoLog_(GLuint program, GLsizei bufSize, GLsizei* length, - GLchar* infoLog) override; - virtual void glGetShaderiv_(GLuint shader, GLenum pname, GLint* params) override; - virtual void glGetShaderInfoLog_(GLuint shader, GLsizei bufSize, GLsizei* length, - GLchar* infoLog) override; - virtual void glEnable_(GLenum cap) override; - virtual void glDisable_(GLenum cap) override; - virtual GLboolean glIsEnabled_(GLenum cap) override; - virtual void glGetIntegerv_(GLenum pname, GLint* data) override; - virtual const GLubyte* glGetString_(GLenum name) override; - virtual GLenum glCheckFramebufferStatus_(GLenum target) override; - - virtual void glActiveTexture_(GLenum texture) override {} - virtual void glAttachShader_(GLuint program, GLuint shader) override {} - virtual void glBindAttribLocation_(GLuint program, GLuint index, const GLchar* name) override {} - virtual void glBindBuffer_(GLenum target, GLuint buffer) override {} - virtual void glBindFramebuffer_(GLenum target, GLuint framebuffer) override {} - virtual void glBindRenderbuffer_(GLenum target, GLuint renderbuffer) override {} - virtual void glBindTexture_(GLenum target, GLuint texture) override {} - virtual void glBlendColor_(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) override {} - virtual void glBlendEquation_(GLenum mode) override {} - virtual void glBlendEquationSeparate_(GLenum modeRGB, GLenum modeAlpha) override {} - virtual void glBlendFunc_(GLenum sfactor, GLenum dfactor) override {} - virtual void glBlendFuncSeparate_(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, - GLenum dfactorAlpha) override {} - virtual void glBufferData_(GLenum target, GLsizeiptr size, const void* data, - GLenum usage) override {} - virtual void glBufferSubData_(GLenum target, GLintptr offset, GLsizeiptr size, - const void* data) override {} - virtual void glClear_(GLbitfield mask) override {} - virtual void glClearColor_(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) override {} - virtual void glClearDepthf_(GLfloat d) override {} - virtual void glClearStencil_(GLint s) override {} - virtual void glColorMask_(GLboolean red, GLboolean green, GLboolean blue, - GLboolean alpha) override {} - virtual void glCompileShader_(GLuint shader) override {} - virtual void glCompressedTexImage2D_(GLenum target, GLint level, GLenum internalformat, - GLsizei width, GLsizei height, GLint border, - GLsizei imageSize, const void* data) override {} - virtual void glCompressedTexSubImage2D_(GLenum target, GLint level, GLint xoffset, - GLint yoffset, GLsizei width, GLsizei height, - GLenum format, GLsizei imageSize, - const void* data) override {} - virtual void glCopyTexImage2D_(GLenum target, GLint level, GLenum internalformat, GLint x, - GLint y, GLsizei width, GLsizei height, GLint border) override {} - virtual void glCopyTexSubImage2D_(GLenum target, GLint level, GLint xoffset, GLint yoffset, - GLint x, GLint y, GLsizei width, GLsizei height) override {} - virtual void glCullFace_(GLenum mode) override {} - virtual void glDeleteBuffers_(GLsizei n, const GLuint* buffers) override {} - virtual void glDeleteFramebuffers_(GLsizei n, const GLuint* framebuffers) override {} - virtual void glDeleteProgram_(GLuint program) override {} - virtual void glDeleteRenderbuffers_(GLsizei n, const GLuint* renderbuffers) override {} - virtual void glDeleteShader_(GLuint shader) override {} - virtual void glDeleteTextures_(GLsizei n, const GLuint* textures) override {} - virtual void glDepthFunc_(GLenum func) override {} - virtual void glDepthMask_(GLboolean flag) override {} - virtual void glDepthRangef_(GLfloat n, GLfloat f) override {} - virtual void glDetachShader_(GLuint program, GLuint shader) override {} - virtual void glDisableVertexAttribArray_(GLuint index) override {} - virtual void glDrawArrays_(GLenum mode, GLint first, GLsizei count) override {} - virtual void glDrawElements_(GLenum mode, GLsizei count, GLenum type, - const void* indices) override {} - virtual void glEnableVertexAttribArray_(GLuint index) override {} - virtual void glFinish_(void) override {} - virtual void glFlush_(void) override {} - virtual void glFramebufferRenderbuffer_(GLenum target, GLenum attachment, - GLenum renderbuffertarget, - GLuint renderbuffer) override {} - virtual void glFramebufferTexture2D_(GLenum target, GLenum attachment, GLenum textarget, - GLuint texture, GLint level) override {} - virtual void glFrontFace_(GLenum mode) override {} - virtual void glGenerateMipmap_(GLenum target) override {} - virtual GLint glGetAttribLocation_(GLuint program, const GLchar* name) override { return 1; } - virtual GLenum glGetError_(void) override { return GL_NO_ERROR; } - virtual GLint glGetUniformLocation_(GLuint program, const GLchar* name) override { return 2; } - virtual void glHint_(GLenum target, GLenum mode) override {} - virtual void glLineWidth_(GLfloat width) override {} - virtual void glLinkProgram_(GLuint program) override {} - virtual void glPixelStorei_(GLenum pname, GLint param) override {} - virtual void glPolygonOffset_(GLfloat factor, GLfloat units) override {} - virtual void glReadPixels_(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, - GLenum type, void* pixels) override {} - virtual void glReleaseShaderCompiler_(void) override {} - virtual void glRenderbufferStorage_(GLenum target, GLenum internalformat, GLsizei width, - GLsizei height) override {} - virtual void glSampleCoverage_(GLfloat value, GLboolean invert) override {} - virtual void glScissor_(GLint x, GLint y, GLsizei width, GLsizei height) override {} - virtual void glShaderBinary_(GLsizei count, const GLuint* shaders, GLenum binaryformat, - const void* binary, GLsizei length) override {} - virtual void glShaderSource_(GLuint shader, GLsizei count, const GLchar* const* string, - const GLint* length) override {} - virtual void glStencilFunc_(GLenum func, GLint ref, GLuint mask) override {} - virtual void glStencilFuncSeparate_(GLenum face, GLenum func, GLint ref, GLuint mask) override { - } - virtual void glStencilMask_(GLuint mask) override {} - virtual void glStencilMaskSeparate_(GLenum face, GLuint mask) override {} - virtual void glStencilOp_(GLenum fail, GLenum zfail, GLenum zpass) override {} - virtual void glStencilOpSeparate_(GLenum face, GLenum sfail, GLenum dpfail, - GLenum dppass) override {} - virtual void glTexImage2D_(GLenum target, GLint level, GLint internalformat, GLsizei width, - GLsizei height, GLint border, GLenum format, GLenum type, - const void* pixels) override {} - virtual void glTexParameterf_(GLenum target, GLenum pname, GLfloat param) override {} - virtual void glTexParameterfv_(GLenum target, GLenum pname, const GLfloat* params) override {} - virtual void glTexParameteri_(GLenum target, GLenum pname, GLint param) override {} - virtual void glTexParameteriv_(GLenum target, GLenum pname, const GLint* params) override {} - virtual void glTexSubImage2D_(GLenum target, GLint level, GLint xoffset, GLint yoffset, - GLsizei width, GLsizei height, GLenum format, GLenum type, - const void* pixels) override {} - virtual void glUniform1f_(GLint location, GLfloat v0) override {} - virtual void glUniform1fv_(GLint location, GLsizei count, const GLfloat* value) override {} - virtual void glUniform1i_(GLint location, GLint v0) override {} - virtual void glUniform1iv_(GLint location, GLsizei count, const GLint* value) override {} - virtual void glUniform2f_(GLint location, GLfloat v0, GLfloat v1) override {} - virtual void glUniform2fv_(GLint location, GLsizei count, const GLfloat* value) override {} - virtual void glUniform2i_(GLint location, GLint v0, GLint v1) override {} - virtual void glUniform2iv_(GLint location, GLsizei count, const GLint* value) override {} - virtual void glUniform3f_(GLint location, GLfloat v0, GLfloat v1, GLfloat v2) override {} - virtual void glUniform3fv_(GLint location, GLsizei count, const GLfloat* value) override {} - virtual void glUniform3i_(GLint location, GLint v0, GLint v1, GLint v2) override {} - virtual void glUniform3iv_(GLint location, GLsizei count, const GLint* value) override {} - virtual void glUniform4f_(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, - GLfloat v3) override {} - virtual void glUniform4fv_(GLint location, GLsizei count, const GLfloat* value) override {} - virtual void glUniform4i_(GLint location, GLint v0, GLint v1, GLint v2, GLint v3) override {} - virtual void glUniform4iv_(GLint location, GLsizei count, const GLint* value) override {} - virtual void glUniformMatrix2fv_(GLint location, GLsizei count, GLboolean transpose, - const GLfloat* value) override {} - virtual void glUniformMatrix3fv_(GLint location, GLsizei count, GLboolean transpose, - const GLfloat* value) override {} - virtual void glUniformMatrix4fv_(GLint location, GLsizei count, GLboolean transpose, - const GLfloat* value) override {} - virtual void glUseProgram_(GLuint program) override {} - virtual void glValidateProgram_(GLuint program) override {} - virtual void glVertexAttrib1f_(GLuint index, GLfloat x) override {} - virtual void glVertexAttrib1fv_(GLuint index, const GLfloat* v) override {} - virtual void glVertexAttrib2f_(GLuint index, GLfloat x, GLfloat y) override {} - virtual void glVertexAttrib2fv_(GLuint index, const GLfloat* v) override {} - virtual void glVertexAttrib3f_(GLuint index, GLfloat x, GLfloat y, GLfloat z) override {} - virtual void glVertexAttrib3fv_(GLuint index, const GLfloat* v) override {} - virtual void glVertexAttrib4f_(GLuint index, GLfloat x, GLfloat y, GLfloat z, - GLfloat w) override {} - virtual void glVertexAttrib4fv_(GLuint index, const GLfloat* v) override {} - virtual void glVertexAttribPointer_(GLuint index, GLint size, GLenum type, GLboolean normalized, - GLsizei stride, const void* pointer) override {} - virtual void glViewport_(GLint x, GLint y, GLsizei width, GLsizei height) override {} - - // gles2 ext - virtual void glInsertEventMarkerEXT_(GLsizei length, const GLchar* marker) override {} - virtual void glPushGroupMarkerEXT_(GLsizei length, const GLchar* marker) override {} - virtual void glPopGroupMarkerEXT_(void) override {} - virtual void glDiscardFramebufferEXT_(GLenum target, GLsizei numAttachments, - const GLenum* attachments) override {} - virtual void glEGLImageTargetTexture2DOES_(GLenum target, GLeglImageOES image) override {} - - // GLES3 - virtual void* glMapBufferRange_(GLenum target, GLintptr offset, GLsizeiptr length, - GLbitfield access) override { - return 0; - } - - virtual GLboolean glUnmapBuffer_(GLenum target) override { return GL_FALSE; } -}; - -} // namespace debug -} // namespace uirenderer -} // namespace android diff --git a/libs/hwui/debug/NullSkiaInterface.cpp b/libs/hwui/debug/NullSkiaInterface.cpp deleted file mode 100644 index d141b9c0591b..000000000000 --- a/libs/hwui/debug/NullSkiaInterface.cpp +++ /dev/null @@ -1,1402 +0,0 @@ -/* - * Copyright (C) 2019 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. - */ - -// TODO: Remove this file. This has been temporarily copied from Skia (where this class is -// deprecated). The NullGlesDriver should be constructing a GrGLInterface that calls *its* -// GL functions! - -#include "GrNonAtomicRef.h" -#include "SkMutex.h" -#include "SkTDArray.h" -#include "SkTo.h" -#include "gl/GrGLDefines.h" -#include "gl/GrGLInterface.h" - -#include - -// added to suppress 'no previous prototype' warning and because this code is duplicated in -// SkNullGLContext.cpp -namespace { - -class GLObject : public GrNonAtomicRef { -public: - GLObject(GrGLuint id) : fID(id) {} - virtual ~GLObject() {} - - GrGLuint id() const { return fID; } - -private: - GrGLuint fID; -}; - -// This class maintains a sparsely populated array of object pointers. -template class TGLObjectManager { - static_assert(std::is_convertible::value, "T must be a subclass of GLObject"); - -public: - TGLObjectManager() : fFreeListHead(kFreeListEnd) { - *fGLObjects.append() = nullptr; // 0 is not a valid GL object id. - } - - ~TGLObjectManager() { - // nullptr out the entries that are really free list links rather than ptrs before deleting. - intptr_t curr = fFreeListHead; - while (kFreeListEnd != curr) { - intptr_t next = reinterpret_cast(fGLObjects[SkToS32(curr)]); - fGLObjects[SkToS32(curr)] = nullptr; - curr = next; - } - - fGLObjects.safeUnrefAll(); - } - - T* lookUp(GrGLuint id) { - T* object = fGLObjects[id]; - SkASSERT(object && object->id() == id); - return object; - } - - T* create() { - GrGLuint id; - T* object; - - if (kFreeListEnd == fFreeListHead) { - // no free slots - create a new one - id = fGLObjects.count(); - object = new T(id); - *fGLObjects.append() = object; - } else { - // grab the head of the free list and advance the head to the next free slot. - id = static_cast(fFreeListHead); - fFreeListHead = reinterpret_cast(fGLObjects[id]); - - object = new T(id); - fGLObjects[id] = object; - } - - return object; - } - - void free(T* object) { - SkASSERT(object); - SkASSERT(fGLObjects.count() > 0); - - GrGLuint id = object->id(); - object->unref(); - - fGLObjects[id] = reinterpret_cast(fFreeListHead); - fFreeListHead = id; - } - -private: - static const intptr_t kFreeListEnd = -1; - // Index of the first entry of fGLObjects in the free list. Free slots in fGLObjects are indices - // to the next free slot. The last free slot has a value of kFreeListEnd. - intptr_t fFreeListHead; - SkTDArray fGLObjects; -}; - -class Buffer : public GLObject { -public: - Buffer(GrGLuint id) : INHERITED(id), fDataPtr(nullptr), fSize(0), fMapped(false) {} - ~Buffer() { delete[] fDataPtr; } - - void allocate(GrGLsizeiptr size, const GrGLchar* dataPtr) { - if (fDataPtr) { - SkASSERT(0 != fSize); - delete[] fDataPtr; - } - - fSize = size; - fDataPtr = new char[size]; - } - - GrGLchar* dataPtr() { return fDataPtr; } - GrGLsizeiptr size() const { return fSize; } - - void setMapped(bool mapped) { fMapped = mapped; } - bool mapped() const { return fMapped; } - -private: - GrGLchar* fDataPtr; - GrGLsizeiptr fSize; // size in bytes - bool fMapped; - - typedef GLObject INHERITED; -}; - -class FramebufferAttachment : public GLObject { -public: - int numSamples() const { return fNumSamples; } - -protected: - FramebufferAttachment(int id) : INHERITED(id), fNumSamples(1) {} - - int fNumSamples; - - typedef GLObject INHERITED; -}; - -class Renderbuffer : public FramebufferAttachment { -public: - Renderbuffer(int id) : INHERITED(id) {} - void setNumSamples(int numSamples) { fNumSamples = numSamples; } - -private: - typedef FramebufferAttachment INHERITED; -}; - -class Texture : public FramebufferAttachment { -public: - Texture() : INHERITED(1) {} - -private: - typedef FramebufferAttachment INHERITED; -}; - -class Framebuffer : public GLObject { -public: - Framebuffer(int id) : INHERITED(id) {} - - void setAttachment(GrGLenum attachmentPoint, const FramebufferAttachment* attachment) { - switch (attachmentPoint) { - default: - SK_ABORT("Invalid framebuffer attachment."); - break; - case GR_GL_STENCIL_ATTACHMENT: - fAttachments[(int)AttachmentPoint::kStencil].reset(SkRef(attachment)); - break; - case GR_GL_DEPTH_ATTACHMENT: - fAttachments[(int)AttachmentPoint::kDepth].reset(SkRef(attachment)); - break; - case GR_GL_COLOR_ATTACHMENT0: - fAttachments[(int)AttachmentPoint::kColor].reset(SkRef(attachment)); - break; - } - } - - void notifyAttachmentDeleteWhileBound(const FramebufferAttachment* deleted) { - for (auto& attachment : fAttachments) { - if (attachment.get() == deleted) { - attachment.reset(nullptr); - } - } - } - - int numSamples() const { - int numSamples = 0; - for (auto& attachment : fAttachments) { - if (!attachment) { - continue; - } - if (numSamples) { - GrAlwaysAssert(attachment->numSamples() == numSamples); - continue; - } - numSamples = attachment->numSamples(); - } - GrAlwaysAssert(numSamples); - return numSamples; - } - -private: - enum AttachmentPoint { - kStencil, - kDepth, - kColor - }; - constexpr int static kNumAttachmentPoints = 1 + (int)AttachmentPoint::kColor; - - sk_sp fAttachments[kNumAttachmentPoints]; - - typedef GLObject INHERITED; -}; - -class TestInterface : public GrGLInterface { -public: - virtual GrGLvoid activeTexture(GrGLenum texture) {} - virtual GrGLvoid attachShader(GrGLuint program, GrGLuint shader) {} - virtual GrGLvoid beginQuery(GrGLenum target, GrGLuint id) {} - virtual GrGLvoid bindAttribLocation(GrGLuint program, GrGLuint index, const char* name) {} - virtual GrGLvoid bindBuffer(GrGLenum target, GrGLuint buffer) {} - virtual GrGLvoid bindFramebuffer(GrGLenum target, GrGLuint framebuffer) {} - virtual GrGLvoid bindRenderbuffer(GrGLenum target, GrGLuint renderbuffer) {} - virtual GrGLvoid bindSampler(GrGLuint unit, GrGLuint sampler) {} - virtual GrGLvoid bindTexture(GrGLenum target, GrGLuint texture) {} - virtual GrGLvoid bindFragDataLocation(GrGLuint program, GrGLuint colorNumber, const GrGLchar* name) {} - virtual GrGLvoid bindFragDataLocationIndexed(GrGLuint program, GrGLuint colorNumber, GrGLuint index, const GrGLchar * name) {} - virtual GrGLvoid bindVertexArray(GrGLuint array) {} - virtual GrGLvoid blendBarrier() {} - virtual GrGLvoid blendColor(GrGLclampf red, GrGLclampf green, GrGLclampf blue, GrGLclampf alpha) {} - virtual GrGLvoid blendEquation(GrGLenum mode) {} - virtual GrGLvoid blendFunc(GrGLenum sfactor, GrGLenum dfactor) {} - virtual GrGLvoid blitFramebuffer(GrGLint srcX0, GrGLint srcY0, GrGLint srcX1, GrGLint srcY1, GrGLint dstX0, GrGLint dstY0, GrGLint dstX1, GrGLint dstY1, GrGLbitfield mask, GrGLenum filter) {} - virtual GrGLvoid bufferData(GrGLenum target, GrGLsizeiptr size, const GrGLvoid* data, GrGLenum usage) {} - virtual GrGLvoid bufferSubData(GrGLenum target, GrGLintptr offset, GrGLsizeiptr size, const GrGLvoid* data) {} - virtual GrGLenum checkFramebufferStatus(GrGLenum target) { return GR_GL_FRAMEBUFFER_COMPLETE; } - virtual GrGLvoid clear(GrGLbitfield mask) {} - virtual GrGLvoid clearColor(GrGLclampf red, GrGLclampf green, GrGLclampf blue, GrGLclampf alpha) {} - virtual GrGLvoid clearStencil(GrGLint s) {} - virtual GrGLvoid colorMask(GrGLboolean red, GrGLboolean green, GrGLboolean blue, GrGLboolean alpha) {} - virtual GrGLvoid compileShader(GrGLuint shader) {} - virtual GrGLvoid compressedTexImage2D(GrGLenum target, GrGLint level, GrGLenum internalformat, GrGLsizei width, GrGLsizei height, GrGLint border, GrGLsizei imageSize, const GrGLvoid* data) {} - virtual GrGLvoid compressedTexSubImage2D(GrGLenum target, GrGLint level, GrGLint xoffset, GrGLint yoffset, GrGLsizei width, GrGLsizei height, GrGLenum format, GrGLsizei imageSize, const GrGLvoid* data) {} - virtual GrGLvoid copyTexSubImage2D(GrGLenum target, GrGLint level, GrGLint xoffset, GrGLint yoffset, GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height) {} - virtual GrGLuint createProgram() { return 0; } - virtual GrGLuint createShader(GrGLenum type) { return 0; } - virtual GrGLvoid cullFace(GrGLenum mode) {} - virtual GrGLvoid deleteBuffers(GrGLsizei n, const GrGLuint* buffers) {} - virtual GrGLvoid deleteFramebuffers(GrGLsizei n, const GrGLuint *framebuffers) {} - virtual GrGLvoid deleteProgram(GrGLuint program) {} - virtual GrGLvoid deleteQueries(GrGLsizei n, const GrGLuint *ids) {} - virtual GrGLvoid deleteRenderbuffers(GrGLsizei n, const GrGLuint *renderbuffers) {} - virtual GrGLvoid deleteSamplers(GrGLsizei n, const GrGLuint* samplers) {} - virtual GrGLvoid deleteShader(GrGLuint shader) {} - virtual GrGLvoid deleteTextures(GrGLsizei n, const GrGLuint* textures) {} - virtual GrGLvoid deleteVertexArrays(GrGLsizei n, const GrGLuint *arrays) {} - virtual GrGLvoid depthMask(GrGLboolean flag) {} - virtual GrGLvoid disable(GrGLenum cap) {} - virtual GrGLvoid disableVertexAttribArray(GrGLuint index) {} - virtual GrGLvoid drawArrays(GrGLenum mode, GrGLint first, GrGLsizei count) {} - virtual GrGLvoid drawArraysInstanced(GrGLenum mode, GrGLint first, GrGLsizei count, GrGLsizei primcount) {} - virtual GrGLvoid drawArraysIndirect(GrGLenum mode, const GrGLvoid* indirect) {} - virtual GrGLvoid drawBuffer(GrGLenum mode) {} - virtual GrGLvoid drawBuffers(GrGLsizei n, const GrGLenum* bufs) {} - virtual GrGLvoid drawElements(GrGLenum mode, GrGLsizei count, GrGLenum type, const GrGLvoid* indices) {} - virtual GrGLvoid drawElementsInstanced(GrGLenum mode, GrGLsizei count, GrGLenum type, const GrGLvoid *indices, GrGLsizei primcount) {} - virtual GrGLvoid drawElementsIndirect(GrGLenum mode, GrGLenum type, const GrGLvoid* indirect) {} - virtual GrGLvoid drawRangeElements(GrGLenum mode, GrGLuint start, GrGLuint end, GrGLsizei count, GrGLenum type, const GrGLvoid* indices) {} - virtual GrGLvoid enable(GrGLenum cap) {} - virtual GrGLvoid enableVertexAttribArray(GrGLuint index) {} - virtual GrGLvoid endQuery(GrGLenum target) {} - virtual GrGLvoid finish() {} - virtual GrGLvoid flush() {} - virtual GrGLvoid flushMappedBufferRange(GrGLenum target, GrGLintptr offset, GrGLsizeiptr length) {} - virtual GrGLvoid framebufferRenderbuffer(GrGLenum target, GrGLenum attachment, GrGLenum renderbuffertarget, GrGLuint renderbuffer) {} - virtual GrGLvoid framebufferTexture2D(GrGLenum target, GrGLenum attachment, GrGLenum textarget, GrGLuint texture, GrGLint level) {} - virtual GrGLvoid framebufferTexture2DMultisample(GrGLenum target, GrGLenum attachment, GrGLenum textarget, GrGLuint texture, GrGLint level, GrGLsizei samples) {} - virtual GrGLvoid frontFace(GrGLenum mode) {} - virtual GrGLvoid genBuffers(GrGLsizei n, GrGLuint* buffers) {} - virtual GrGLvoid genFramebuffers(GrGLsizei n, GrGLuint *framebuffers) {} - virtual GrGLvoid generateMipmap(GrGLenum target) {} - virtual GrGLvoid genQueries(GrGLsizei n, GrGLuint *ids) {} - virtual GrGLvoid genRenderbuffers(GrGLsizei n, GrGLuint *renderbuffers) {} - virtual GrGLvoid genSamplers(GrGLsizei n, GrGLuint *samplers) {} - virtual GrGLvoid genTextures(GrGLsizei n, GrGLuint* textures) {} - virtual GrGLvoid genVertexArrays(GrGLsizei n, GrGLuint *arrays) {} - virtual GrGLvoid getBufferParameteriv(GrGLenum target, GrGLenum pname, GrGLint* params) {} - virtual GrGLenum getError() { return GR_GL_NO_ERROR; } - virtual GrGLvoid getFramebufferAttachmentParameteriv(GrGLenum target, GrGLenum attachment, GrGLenum pname, GrGLint* params) {} - virtual GrGLvoid getIntegerv(GrGLenum pname, GrGLint* params) {} - virtual GrGLvoid getMultisamplefv(GrGLenum pname, GrGLuint index, GrGLfloat* val) {} - virtual GrGLvoid getProgramInfoLog(GrGLuint program, GrGLsizei bufsize, GrGLsizei* length, char* infolog) {} - virtual GrGLvoid getProgramiv(GrGLuint program, GrGLenum pname, GrGLint* params) {} - virtual GrGLvoid getQueryiv(GrGLenum GLtarget, GrGLenum pname, GrGLint *params) {} - virtual GrGLvoid getQueryObjecti64v(GrGLuint id, GrGLenum pname, GrGLint64 *params) {} - virtual GrGLvoid getQueryObjectiv(GrGLuint id, GrGLenum pname, GrGLint *params) {} - virtual GrGLvoid getQueryObjectui64v(GrGLuint id, GrGLenum pname, GrGLuint64 *params) {} - virtual GrGLvoid getQueryObjectuiv(GrGLuint id, GrGLenum pname, GrGLuint *params) {} - virtual GrGLvoid getRenderbufferParameteriv(GrGLenum target, GrGLenum pname, GrGLint* params) {} - virtual GrGLvoid getShaderInfoLog(GrGLuint shader, GrGLsizei bufsize, GrGLsizei* length, char* infolog) {} - virtual GrGLvoid getShaderiv(GrGLuint shader, GrGLenum pname, GrGLint* params) {} - virtual GrGLvoid getShaderPrecisionFormat(GrGLenum shadertype, GrGLenum precisiontype, GrGLint *range, GrGLint *precision) {} - virtual const GrGLubyte* getString(GrGLenum name) { return nullptr; } - virtual const GrGLubyte* getStringi(GrGLenum name, GrGLuint index) { return nullptr; } - virtual GrGLvoid getTexLevelParameteriv(GrGLenum target, GrGLint level, GrGLenum pname, GrGLint* params) {} - virtual GrGLint getUniformLocation(GrGLuint program, const char* name) { return 0; } - virtual GrGLvoid insertEventMarker(GrGLsizei length, const char* marker) {} - virtual GrGLvoid invalidateBufferData(GrGLuint buffer) {} - virtual GrGLvoid invalidateBufferSubData(GrGLuint buffer, GrGLintptr offset, GrGLsizeiptr length) {} - virtual GrGLvoid invalidateFramebuffer(GrGLenum target, GrGLsizei numAttachments, const GrGLenum *attachments) {} - virtual GrGLvoid invalidateSubFramebuffer(GrGLenum target, GrGLsizei numAttachments, const GrGLenum *attachments, GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height) {} - virtual GrGLvoid invalidateTexImage(GrGLuint texture, GrGLint level) {} - virtual GrGLvoid invalidateTexSubImage(GrGLuint texture, GrGLint level, GrGLint xoffset, GrGLint yoffset, GrGLint zoffset, GrGLsizei width, GrGLsizei height, GrGLsizei depth) {} - virtual GrGLboolean isTexture(GrGLuint texture) { return GR_GL_FALSE; } - virtual GrGLvoid lineWidth(GrGLfloat width) {} - virtual GrGLvoid linkProgram(GrGLuint program) {} - virtual GrGLvoid* mapBuffer(GrGLenum target, GrGLenum access) { return nullptr; } - virtual GrGLvoid* mapBufferRange(GrGLenum target, GrGLintptr offset, GrGLsizeiptr length, GrGLbitfield access) { return nullptr; } - virtual GrGLvoid* mapBufferSubData(GrGLuint target, GrGLintptr offset, GrGLsizeiptr size, GrGLenum access) { return nullptr; } - virtual GrGLvoid* mapTexSubImage2D(GrGLenum target, GrGLint level, GrGLint xoffset, GrGLint yoffset, GrGLsizei width, GrGLsizei height, GrGLenum format, GrGLenum type, GrGLenum access) { return nullptr; } - virtual GrGLvoid pixelStorei(GrGLenum pname, GrGLint param) {} - virtual GrGLvoid polygonMode(GrGLenum face, GrGLenum mode) {} - virtual GrGLvoid popGroupMarker() {} - virtual GrGLvoid pushGroupMarker(GrGLsizei length, const char* marker) {} - virtual GrGLvoid queryCounter(GrGLuint id, GrGLenum target) {} - virtual GrGLvoid rasterSamples(GrGLuint samples, GrGLboolean fixedsamplelocations) {} - virtual GrGLvoid readBuffer(GrGLenum src) {} - virtual GrGLvoid readPixels(GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height, GrGLenum format, GrGLenum type, GrGLvoid* pixels) {} - virtual GrGLvoid renderbufferStorage(GrGLenum target, GrGLenum internalformat, GrGLsizei width, GrGLsizei height) {} - virtual GrGLvoid renderbufferStorageMultisample(GrGLenum target, GrGLsizei samples, GrGLenum internalformat, GrGLsizei width, GrGLsizei height) {} - virtual GrGLvoid resolveMultisampleFramebuffer() {} - virtual GrGLvoid samplerParameteri(GrGLuint sampler, GrGLenum pname, GrGLint param) {} - virtual GrGLvoid samplerParameteriv(GrGLuint sampler, GrGLenum pname, const GrGLint* param) {} - virtual GrGLvoid scissor(GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height) {} - virtual GrGLvoid bindUniformLocation(GrGLuint program, GrGLint location, const char* name) {} - virtual GrGLvoid shaderSource(GrGLuint shader, GrGLsizei count, const char* const * str, const GrGLint* length) {} - virtual GrGLvoid stencilFunc(GrGLenum func, GrGLint ref, GrGLuint mask) {} - virtual GrGLvoid stencilFuncSeparate(GrGLenum face, GrGLenum func, GrGLint ref, GrGLuint mask) {} - virtual GrGLvoid stencilMask(GrGLuint mask) {} - virtual GrGLvoid stencilMaskSeparate(GrGLenum face, GrGLuint mask) {} - virtual GrGLvoid stencilOp(GrGLenum fail, GrGLenum zfail, GrGLenum zpass) {} - virtual GrGLvoid stencilOpSeparate(GrGLenum face, GrGLenum fail, GrGLenum zfail, GrGLenum zpass) {} - virtual GrGLvoid texBuffer(GrGLenum target, GrGLenum internalformat, GrGLuint buffer) {} - virtual GrGLvoid texImage2D(GrGLenum target, GrGLint level, GrGLint internalformat, GrGLsizei width, GrGLsizei height, GrGLint border, GrGLenum format, GrGLenum type, const GrGLvoid* pixels) {} - virtual GrGLvoid texParameterf(GrGLenum target, GrGLenum pname, GrGLfloat param) {} - virtual GrGLvoid texParameterfv(GrGLenum target, GrGLenum pname, const GrGLfloat* params) {} - virtual GrGLvoid texParameteri(GrGLenum target, GrGLenum pname, GrGLint param) {} - virtual GrGLvoid texParameteriv(GrGLenum target, GrGLenum pname, const GrGLint* params) {} - virtual GrGLvoid texStorage2D(GrGLenum target, GrGLsizei levels, GrGLenum internalformat, GrGLsizei width, GrGLsizei height) {} - virtual GrGLvoid discardFramebuffer(GrGLenum target, GrGLsizei numAttachments, const GrGLenum* attachments) {} - virtual GrGLvoid texSubImage2D(GrGLenum target, GrGLint level, GrGLint xoffset, GrGLint yoffset, GrGLsizei width, GrGLsizei height, GrGLenum format, GrGLenum type, const GrGLvoid* pixels) {} - virtual GrGLvoid textureBarrier() {} - virtual GrGLvoid uniform1f(GrGLint location, GrGLfloat v0) {} - virtual GrGLvoid uniform1i(GrGLint location, GrGLint v0) {} - virtual GrGLvoid uniform1fv(GrGLint location, GrGLsizei count, const GrGLfloat* v) {} - virtual GrGLvoid uniform1iv(GrGLint location, GrGLsizei count, const GrGLint* v) {} - virtual GrGLvoid uniform2f(GrGLint location, GrGLfloat v0, GrGLfloat v1) {} - virtual GrGLvoid uniform2i(GrGLint location, GrGLint v0, GrGLint v1) {} - virtual GrGLvoid uniform2fv(GrGLint location, GrGLsizei count, const GrGLfloat* v) {} - virtual GrGLvoid uniform2iv(GrGLint location, GrGLsizei count, const GrGLint* v) {} - virtual GrGLvoid uniform3f(GrGLint location, GrGLfloat v0, GrGLfloat v1, GrGLfloat v2) {} - virtual GrGLvoid uniform3i(GrGLint location, GrGLint v0, GrGLint v1, GrGLint v2) {} - virtual GrGLvoid uniform3fv(GrGLint location, GrGLsizei count, const GrGLfloat* v) {} - virtual GrGLvoid uniform3iv(GrGLint location, GrGLsizei count, const GrGLint* v) {} - virtual GrGLvoid uniform4f(GrGLint location, GrGLfloat v0, GrGLfloat v1, GrGLfloat v2, GrGLfloat v3) {} - virtual GrGLvoid uniform4i(GrGLint location, GrGLint v0, GrGLint v1, GrGLint v2, GrGLint v3) {} - virtual GrGLvoid uniform4fv(GrGLint location, GrGLsizei count, const GrGLfloat* v) {} - virtual GrGLvoid uniform4iv(GrGLint location, GrGLsizei count, const GrGLint* v) {} - virtual GrGLvoid uniformMatrix2fv(GrGLint location, GrGLsizei count, GrGLboolean transpose, const GrGLfloat* value) {} - virtual GrGLvoid uniformMatrix3fv(GrGLint location, GrGLsizei count, GrGLboolean transpose, const GrGLfloat* value) {} - virtual GrGLvoid uniformMatrix4fv(GrGLint location, GrGLsizei count, GrGLboolean transpose, const GrGLfloat* value) {} - virtual GrGLboolean unmapBuffer(GrGLenum target) { return GR_GL_TRUE; } - virtual GrGLvoid unmapBufferSubData(const GrGLvoid* mem) {} - virtual GrGLvoid unmapTexSubImage2D(const GrGLvoid* mem) {} - virtual GrGLvoid useProgram(GrGLuint program) {} - virtual GrGLvoid vertexAttrib1f(GrGLuint indx, const GrGLfloat value) {} - virtual GrGLvoid vertexAttrib2fv(GrGLuint indx, const GrGLfloat* values) {} - virtual GrGLvoid vertexAttrib3fv(GrGLuint indx, const GrGLfloat* values) {} - virtual GrGLvoid vertexAttrib4fv(GrGLuint indx, const GrGLfloat* values) {} - virtual GrGLvoid vertexAttribDivisor(GrGLuint index, GrGLuint divisor) {} - virtual GrGLvoid vertexAttribIPointer(GrGLuint indx, GrGLint size, GrGLenum type, GrGLsizei stride, const GrGLvoid* ptr) {} - virtual GrGLvoid vertexAttribPointer(GrGLuint indx, GrGLint size, GrGLenum type, GrGLboolean normalized, GrGLsizei stride, const GrGLvoid* ptr) {} - virtual GrGLvoid viewport(GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height) {} - virtual GrGLvoid matrixLoadf(GrGLenum matrixMode, const GrGLfloat* m) {} - virtual GrGLvoid matrixLoadIdentity(GrGLenum) {} - virtual GrGLvoid pathCommands(GrGLuint path, GrGLsizei numCommands, const GrGLubyte *commands, GrGLsizei numCoords, GrGLenum coordType, const GrGLvoid *coords) {} - virtual GrGLvoid pathParameteri(GrGLuint path, GrGLenum pname, GrGLint value) {} - virtual GrGLvoid pathParameterf(GrGLuint path, GrGLenum pname, GrGLfloat value) {} - virtual GrGLuint genPaths(GrGLsizei range) { return 0; } - virtual GrGLvoid deletePaths(GrGLuint path, GrGLsizei range) {} - virtual GrGLboolean isPath(GrGLuint path) { return true; } - virtual GrGLvoid pathStencilFunc(GrGLenum func, GrGLint ref, GrGLuint mask) {} - virtual GrGLvoid stencilFillPath(GrGLuint path, GrGLenum fillMode, GrGLuint mask) {} - virtual GrGLvoid stencilStrokePath(GrGLuint path, GrGLint reference, GrGLuint mask) {} - virtual GrGLvoid stencilFillPathInstanced(GrGLsizei numPaths, GrGLenum pathNameType, const GrGLvoid *paths, GrGLuint pathBase, GrGLenum fillMode, GrGLuint mask, GrGLenum transformType, const GrGLfloat *transformValues) {} - virtual GrGLvoid stencilStrokePathInstanced(GrGLsizei numPaths, GrGLenum pathNameType, const GrGLvoid *paths, GrGLuint pathBase, GrGLint reference, GrGLuint mask, GrGLenum transformType, const GrGLfloat *transformValues) {} - virtual GrGLvoid coverFillPath(GrGLuint path, GrGLenum coverMode) {} - virtual GrGLvoid coverStrokePath(GrGLuint name, GrGLenum coverMode) {} - virtual GrGLvoid coverFillPathInstanced(GrGLsizei numPaths, GrGLenum pathNameType, const GrGLvoid *paths, GrGLuint pathBase, GrGLenum coverMode, GrGLenum transformType, const GrGLfloat *transformValues) {} - virtual GrGLvoid coverStrokePathInstanced(GrGLsizei numPaths, GrGLenum pathNameType, const GrGLvoid *paths, GrGLuint pathBase, GrGLenum coverMode, GrGLenum transformType, const GrGLfloat* transformValues) {} - virtual GrGLvoid stencilThenCoverFillPath(GrGLuint path, GrGLenum fillMode, GrGLuint mask, GrGLenum coverMode) {} - virtual GrGLvoid stencilThenCoverStrokePath(GrGLuint path, GrGLint reference, GrGLuint mask, GrGLenum coverMode) {} - virtual GrGLvoid stencilThenCoverFillPathInstanced(GrGLsizei numPaths, GrGLenum pathNameType, const GrGLvoid *paths, GrGLuint pathBase, GrGLenum fillMode, GrGLuint mask, GrGLenum coverMode, GrGLenum transformType, const GrGLfloat *transformValues) {} - virtual GrGLvoid stencilThenCoverStrokePathInstanced(GrGLsizei numPaths, GrGLenum pathNameType, const GrGLvoid *paths, GrGLuint pathBase, GrGLint reference, GrGLuint mask, GrGLenum coverMode, GrGLenum transformType, const GrGLfloat *transformValues) {} - virtual GrGLvoid programPathFragmentInputGen(GrGLuint program, GrGLint location, GrGLenum genMode, GrGLint components,const GrGLfloat *coeffs) {} - virtual GrGLvoid bindFragmentInputLocation(GrGLuint program, GrGLint location, const GrGLchar* name) {} - virtual GrGLint getProgramResourceLocation(GrGLuint program, GrGLenum programInterface, const GrGLchar *name) { return 0; } - virtual GrGLvoid coverageModulation(GrGLenum components) {} - virtual GrGLvoid multiDrawArraysIndirect(GrGLenum mode, const GrGLvoid *indirect, GrGLsizei drawcount, GrGLsizei stride) {} - virtual GrGLvoid multiDrawElementsIndirect(GrGLenum mode, GrGLenum type, const GrGLvoid *indirect, GrGLsizei drawcount, GrGLsizei stride) {} - virtual GrGLuint64 getTextureHandle(GrGLuint texture) { return 0; } - virtual GrGLuint64 getTextureSamplerHandle(GrGLuint texture, GrGLuint sampler) { return 0; } - virtual GrGLvoid makeTextureHandleResident(GrGLuint64 handle) {} - virtual GrGLvoid makeTextureHandleNonResident(GrGLuint64 handle) {} - virtual GrGLuint64 getImageHandle(GrGLuint texture, GrGLint level, GrGLboolean layered, GrGLint layer, GrGLint format) { return 0; } - virtual GrGLvoid makeImageHandleResident(GrGLuint64 handle, GrGLenum access) {} - virtual GrGLvoid makeImageHandleNonResident(GrGLuint64 handle) {} - virtual GrGLboolean isTextureHandleResident(GrGLuint64 handle) { return GR_GL_FALSE; } - virtual GrGLboolean isImageHandleResident(GrGLuint64 handle) { return GR_GL_FALSE; } - virtual GrGLvoid uniformHandleui64(GrGLint location, GrGLuint64 v0) {} - virtual GrGLvoid uniformHandleui64v(GrGLint location, GrGLsizei count, const GrGLuint64 *value) {} - virtual GrGLvoid programUniformHandleui64(GrGLuint program, GrGLint location, GrGLuint64 v0) {} - virtual GrGLvoid programUniformHandleui64v(GrGLuint program, GrGLint location, GrGLsizei count, const GrGLuint64 *value) {} - virtual GrGLvoid textureParameteri(GrGLuint texture, GrGLenum target, GrGLenum pname, GrGLint param) {} - virtual GrGLvoid textureParameteriv(GrGLuint texture, GrGLenum target, GrGLenum pname, const GrGLint *param) {} - virtual GrGLvoid textureParameterf(GrGLuint texture, GrGLenum target, GrGLenum pname, float param) {} - virtual GrGLvoid textureParameterfv(GrGLuint texture, GrGLenum target, GrGLenum pname, const float *param) {} - virtual GrGLvoid textureImage1D(GrGLuint texture, GrGLenum target, GrGLint level, GrGLint GrGLinternalformat, GrGLsizei width, GrGLint border, GrGLenum format, GrGLenum type, const GrGLvoid *pixels) {} - virtual GrGLvoid textureImage2D(GrGLuint texture, GrGLenum target, GrGLint level, GrGLint GrGLinternalformat, GrGLsizei width, GrGLsizei height, GrGLint border, GrGLenum format, GrGLenum type, const GrGLvoid *pixels) {} - virtual GrGLvoid textureSubImage1D(GrGLuint texture, GrGLenum target, GrGLint level, GrGLint xoffset, GrGLsizei width, GrGLenum format, GrGLenum type, const GrGLvoid *pixels) {} - virtual GrGLvoid textureSubImage2D(GrGLuint texture, GrGLenum target, GrGLint level, GrGLint xoffset, GrGLint yoffset, GrGLsizei width, GrGLsizei height, GrGLenum format, GrGLenum type, const GrGLvoid *pixels) {} - virtual GrGLvoid copyTextureImage1D(GrGLuint texture, GrGLenum target, GrGLint level, GrGLenum GrGLinternalformat, GrGLint x, GrGLint y, GrGLsizei width, GrGLint border) {} - virtual GrGLvoid copyTextureImage2D(GrGLuint texture, GrGLenum target, GrGLint level, GrGLenum GrGLinternalformat, GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height, GrGLint border) {} - virtual GrGLvoid copyTextureSubImage1D(GrGLuint texture, GrGLenum target, GrGLint level, GrGLint xoffset, GrGLint x, GrGLint y, GrGLsizei width) {} - virtual GrGLvoid copyTextureSubImage2D(GrGLuint texture, GrGLenum target, GrGLint level, GrGLint xoffset, GrGLint yoffset, GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height) {} - virtual GrGLvoid getTextureImage(GrGLuint texture, GrGLenum target, GrGLint level, GrGLenum format, GrGLenum type, GrGLvoid *pixels) {} - virtual GrGLvoid getTextureParameterfv(GrGLuint texture, GrGLenum target, GrGLenum pname, float *params) {} - virtual GrGLvoid getTextureParameteriv(GrGLuint texture, GrGLenum target, GrGLenum pname, GrGLint *params) {} - virtual GrGLvoid getTextureLevelParameterfv(GrGLuint texture, GrGLenum target, GrGLint level, GrGLenum pname, float *params) {} - virtual GrGLvoid getTextureLevelParameteriv(GrGLuint texture, GrGLenum target, GrGLint level, GrGLenum pname, GrGLint *params) {} - virtual GrGLvoid textureImage3D(GrGLuint texture, GrGLenum target, GrGLint level, GrGLint GrGLinternalformat, GrGLsizei width, GrGLsizei height, GrGLsizei depth, GrGLint border, GrGLenum format, GrGLenum type, const GrGLvoid *pixels) {} - virtual GrGLvoid textureSubImage3D(GrGLuint texture, GrGLenum target, GrGLint level, GrGLint xoffset, GrGLint yoffset, GrGLint zoffset, GrGLsizei width, GrGLsizei height, GrGLsizei depth, GrGLenum format, GrGLenum type, const GrGLvoid *pixels) {} - virtual GrGLvoid copyTextureSubImage3D(GrGLuint texture, GrGLenum target, GrGLint level, GrGLint xoffset, GrGLint yoffset, GrGLint zoffset, GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height) {} - virtual GrGLvoid compressedTextureImage3D(GrGLuint texture, GrGLenum target, GrGLint level, GrGLenum GrGLinternalformat, GrGLsizei width, GrGLsizei height, GrGLsizei depth, GrGLint border, GrGLsizei imageSize, const GrGLvoid *data) {} - virtual GrGLvoid compressedTextureImage2D(GrGLuint texture, GrGLenum target, GrGLint level, GrGLenum GrGLinternalformat, GrGLsizei width, GrGLsizei height, GrGLint border, GrGLsizei imageSize, const GrGLvoid *data) {} - virtual GrGLvoid compressedTextureImage1D(GrGLuint texture, GrGLenum target, GrGLint level, GrGLenum GrGLinternalformat, GrGLsizei width, GrGLint border, GrGLsizei imageSize, const GrGLvoid *data) {} - virtual GrGLvoid compressedTextureSubImage3D(GrGLuint texture, GrGLenum target, GrGLint level, GrGLint xoffset, GrGLint yoffset, GrGLint zoffset, GrGLsizei width, GrGLsizei height, GrGLsizei depth, GrGLenum format, GrGLsizei imageSize, const GrGLvoid *data) {} - virtual GrGLvoid compressedTextureSubImage2D(GrGLuint texture, GrGLenum target, GrGLint level, GrGLint xoffset, GrGLint yoffset, GrGLsizei width, GrGLsizei height, GrGLenum format, GrGLsizei imageSize, const GrGLvoid *data) {} - virtual GrGLvoid compressedTextureSubImage1D(GrGLuint texture, GrGLenum target, GrGLint level, GrGLint xoffset, GrGLsizei width, GrGLenum format, GrGLsizei imageSize, const GrGLvoid *data) {} - virtual GrGLvoid getCompressedTextureImage(GrGLuint texture, GrGLenum target, GrGLint level, GrGLvoid *img) {} - virtual GrGLvoid namedBufferData(GrGLuint buffer, GrGLsizeiptr size, const GrGLvoid *data, GrGLenum usage) {} - virtual GrGLvoid namedBufferSubData(GrGLuint buffer, GrGLintptr offset, GrGLsizeiptr size, const GrGLvoid *data) {} - virtual GrGLvoid* mapNamedBuffer(GrGLuint buffer, GrGLenum access) { return nullptr; } - virtual GrGLboolean unmapNamedBuffer(GrGLuint buffer) { return GR_GL_FALSE; } - virtual GrGLvoid getNamedBufferParameteriv(GrGLuint buffer, GrGLenum pname, GrGLint *params) {} - virtual GrGLvoid getNamedBufferPointerv(GrGLuint buffer, GrGLenum pname, GrGLvoid* *params) {} - virtual GrGLvoid getNamedBufferSubData(GrGLuint buffer, GrGLintptr offset, GrGLsizeiptr size, GrGLvoid *data) {} - virtual GrGLvoid programUniform1f(GrGLuint program, GrGLint location, float v0) {} - virtual GrGLvoid programUniform2f(GrGLuint program, GrGLint location, float v0, float v1) {} - virtual GrGLvoid programUniform3f(GrGLuint program, GrGLint location, float v0, float v1, float v2) {} - virtual GrGLvoid programUniform4f(GrGLuint program, GrGLint location, float v0, float v1, float v2, float v3) {} - virtual GrGLvoid programUniform1i(GrGLuint program, GrGLint location, GrGLint v0) {} - virtual GrGLvoid programUniform2i(GrGLuint program, GrGLint location, GrGLint v0, GrGLint v1) {} - virtual GrGLvoid programUniform3i(GrGLuint program, GrGLint location, GrGLint v0, GrGLint v1, GrGLint v2) {} - virtual GrGLvoid programUniform4i(GrGLuint program, GrGLint location, GrGLint v0, GrGLint v1, GrGLint v2, GrGLint v3) {} - virtual GrGLvoid programUniform1fv(GrGLuint program, GrGLint location, GrGLsizei count, const float *value) {} - virtual GrGLvoid programUniform2fv(GrGLuint program, GrGLint location, GrGLsizei count, const float *value) {} - virtual GrGLvoid programUniform3fv(GrGLuint program, GrGLint location, GrGLsizei count, const float *value) {} - virtual GrGLvoid programUniform4fv(GrGLuint program, GrGLint location, GrGLsizei count, const float *value) {} - virtual GrGLvoid programUniform1iv(GrGLuint program, GrGLint location, GrGLsizei count, const GrGLint *value) {} - virtual GrGLvoid programUniform2iv(GrGLuint program, GrGLint location, GrGLsizei count, const GrGLint *value) {} - virtual GrGLvoid programUniform3iv(GrGLuint program, GrGLint location, GrGLsizei count, const GrGLint *value) {} - virtual GrGLvoid programUniform4iv(GrGLuint program, GrGLint location, GrGLsizei count, const GrGLint *value) {} - virtual GrGLvoid programUniformMatrix2fv(GrGLuint program, GrGLint location, GrGLsizei count, GrGLboolean transpose, const float *value) {} - virtual GrGLvoid programUniformMatrix3fv(GrGLuint program, GrGLint location, GrGLsizei count, GrGLboolean transpose, const float *value) {} - virtual GrGLvoid programUniformMatrix4fv(GrGLuint program, GrGLint location, GrGLsizei count, GrGLboolean transpose, const float *value) {} - virtual GrGLvoid programUniformMatrix2x3fv(GrGLuint program, GrGLint location, GrGLsizei count, GrGLboolean transpose, const float *value) {} - virtual GrGLvoid programUniformMatrix3x2fv(GrGLuint program, GrGLint location, GrGLsizei count, GrGLboolean transpose, const float *value) {} - virtual GrGLvoid programUniformMatrix2x4fv(GrGLuint program, GrGLint location, GrGLsizei count, GrGLboolean transpose, const float *value) {} - virtual GrGLvoid programUniformMatrix4x2fv(GrGLuint program, GrGLint location, GrGLsizei count, GrGLboolean transpose, const float *value) {} - virtual GrGLvoid programUniformMatrix3x4fv(GrGLuint program, GrGLint location, GrGLsizei count, GrGLboolean transpose, const float *value) {} - virtual GrGLvoid programUniformMatrix4x3fv(GrGLuint program, GrGLint location, GrGLsizei count, GrGLboolean transpose, const float *value) {} - virtual GrGLvoid namedRenderbufferStorage(GrGLuint renderbuffer, GrGLenum GrGLinternalformat, GrGLsizei width, GrGLsizei height) {} - virtual GrGLvoid getNamedRenderbufferParameteriv(GrGLuint renderbuffer, GrGLenum pname, GrGLint *params) {} - virtual GrGLvoid namedRenderbufferStorageMultisample(GrGLuint renderbuffer, GrGLsizei samples, GrGLenum GrGLinternalformat, GrGLsizei width, GrGLsizei height) {} - virtual GrGLenum checkNamedFramebufferStatus(GrGLuint framebuffer, GrGLenum target) { return GR_GL_FRAMEBUFFER_COMPLETE; } - virtual GrGLvoid namedFramebufferTexture1D(GrGLuint framebuffer, GrGLenum attachment, GrGLenum textarget, GrGLuint texture, GrGLint level) {} - virtual GrGLvoid namedFramebufferTexture2D(GrGLuint framebuffer, GrGLenum attachment, GrGLenum textarget, GrGLuint texture, GrGLint level) {} - virtual GrGLvoid namedFramebufferTexture3D(GrGLuint framebuffer, GrGLenum attachment, GrGLenum textarget, GrGLuint texture, GrGLint level, GrGLint zoffset) {} - virtual GrGLvoid namedFramebufferRenderbuffer(GrGLuint framebuffer, GrGLenum attachment, GrGLenum renderbuffertarget, GrGLuint renderbuffer) {} - virtual GrGLvoid getNamedFramebufferAttachmentParameteriv(GrGLuint framebuffer, GrGLenum attachment, GrGLenum pname, GrGLint *params) {} - virtual GrGLvoid generateTextureMipmap(GrGLuint texture, GrGLenum target) {} - virtual GrGLvoid framebufferDrawBuffer(GrGLuint framebuffer, GrGLenum mode) {} - virtual GrGLvoid framebufferDrawBuffers(GrGLuint framebuffer, GrGLsizei n, const GrGLenum *bufs) {} - virtual GrGLvoid framebufferReadBuffer(GrGLuint framebuffer, GrGLenum mode) {} - virtual GrGLvoid getFramebufferParameteriv(GrGLuint framebuffer, GrGLenum pname, GrGLint *param) {} - virtual GrGLvoid namedCopyBufferSubData(GrGLuint readBuffer, GrGLuint writeBuffer, GrGLintptr readOffset, GrGLintptr writeOffset, GrGLsizeiptr size) {} - virtual GrGLvoid vertexArrayVertexOffset(GrGLuint vaobj, GrGLuint buffer, GrGLint size, GrGLenum type, GrGLsizei stride, GrGLintptr offset) {} - virtual GrGLvoid vertexArrayColorOffset(GrGLuint vaobj, GrGLuint buffer, GrGLint size, GrGLenum type, GrGLsizei stride, GrGLintptr offset) {} - virtual GrGLvoid vertexArrayEdgeFlagOffset(GrGLuint vaobj, GrGLuint buffer, GrGLsizei stride, GrGLintptr offset) {} - virtual GrGLvoid vertexArrayIndexOffset(GrGLuint vaobj, GrGLuint buffer, GrGLenum type, GrGLsizei stride, GrGLintptr offset) {} - virtual GrGLvoid vertexArrayNormalOffset(GrGLuint vaobj, GrGLuint buffer, GrGLenum type, GrGLsizei stride, GrGLintptr offset) {} - virtual GrGLvoid vertexArrayTexCoordOffset(GrGLuint vaobj, GrGLuint buffer, GrGLint size, GrGLenum type, GrGLsizei stride, GrGLintptr offset) {} - virtual GrGLvoid vertexArrayMultiTexCoordOffset(GrGLuint vaobj, GrGLuint buffer, GrGLenum texunit, GrGLint size, GrGLenum type, GrGLsizei stride, GrGLintptr offset) {} - virtual GrGLvoid vertexArrayFogCoordOffset(GrGLuint vaobj, GrGLuint buffer, GrGLenum type, GrGLsizei stride, GrGLintptr offset) {} - virtual GrGLvoid vertexArraySecondaryColorOffset(GrGLuint vaobj, GrGLuint buffer, GrGLint size, GrGLenum type, GrGLsizei stride, GrGLintptr offset) {} - virtual GrGLvoid vertexArrayVertexAttribOffset(GrGLuint vaobj, GrGLuint buffer, GrGLuint index, GrGLint size, GrGLenum type, GrGLboolean normalized, GrGLsizei stride, GrGLintptr offset) {} - virtual GrGLvoid vertexArrayVertexAttribIOffset(GrGLuint vaobj, GrGLuint buffer, GrGLuint index, GrGLint size, GrGLenum type, GrGLsizei stride, GrGLintptr offset) {} - virtual GrGLvoid enableVertexArray(GrGLuint vaobj, GrGLenum array) {} - virtual GrGLvoid disableVertexArray(GrGLuint vaobj, GrGLenum array) {} - virtual GrGLvoid enableVertexArrayAttrib(GrGLuint vaobj, GrGLuint index) {} - virtual GrGLvoid disableVertexArrayAttrib(GrGLuint vaobj, GrGLuint index) {} - virtual GrGLvoid getVertexArrayIntegerv(GrGLuint vaobj, GrGLenum pname, GrGLint *param) {} - virtual GrGLvoid getVertexArrayPointerv(GrGLuint vaobj, GrGLenum pname, GrGLvoid **param) {} - virtual GrGLvoid getVertexArrayIntegeri_v(GrGLuint vaobj, GrGLuint index, GrGLenum pname, GrGLint *param) {} - virtual GrGLvoid getVertexArrayPointeri_v(GrGLuint vaobj, GrGLuint index, GrGLenum pname, GrGLvoid **param) {} - virtual GrGLvoid* mapNamedBufferRange(GrGLuint buffer, GrGLintptr offset, GrGLsizeiptr length, GrGLbitfield access) { return nullptr; } - virtual GrGLvoid flushMappedNamedBufferRange(GrGLuint buffer, GrGLintptr offset, GrGLsizeiptr length) {} - virtual GrGLvoid textureBuffer(GrGLuint texture, GrGLenum target, GrGLenum internalformat, GrGLuint buffer) {} - virtual GrGLsync fenceSync(GrGLenum condition, GrGLbitfield flags) { return nullptr; } - virtual GrGLboolean isSync(GrGLsync) { return false; } - virtual GrGLenum clientWaitSync(GrGLsync sync, GrGLbitfield flags, GrGLuint64 timeout) { return GR_GL_WAIT_FAILED; } - virtual GrGLvoid waitSync(GrGLsync sync, GrGLbitfield flags, GrGLuint64 timeout) {} - virtual GrGLvoid deleteSync(GrGLsync sync) {} - virtual GrGLvoid debugMessageControl(GrGLenum source, GrGLenum type, GrGLenum severity, GrGLsizei count, const GrGLuint* ids, GrGLboolean enabled) {} - virtual GrGLvoid debugMessageInsert(GrGLenum source, GrGLenum type, GrGLuint id, GrGLenum severity, GrGLsizei length, const GrGLchar* buf) {} - virtual GrGLvoid debugMessageCallback(GRGLDEBUGPROC callback, const GrGLvoid* userParam) {} - virtual GrGLuint getDebugMessageLog(GrGLuint count, GrGLsizei bufSize, GrGLenum* sources, GrGLenum* types, GrGLuint* ids, GrGLenum* severities, GrGLsizei* lengths, GrGLchar* messageLog) { return 0; } - virtual GrGLvoid pushDebugGroup(GrGLenum source, GrGLuint id, GrGLsizei length, const GrGLchar * message) {} - virtual GrGLvoid popDebugGroup() {} - virtual GrGLvoid objectLabel(GrGLenum identifier, GrGLuint name, GrGLsizei length, const GrGLchar *label) {} - virtual GrGLvoid getInternalformativ(GrGLenum target, GrGLenum internalformat, GrGLenum pname, GrGLsizei bufSize, GrGLint *params) {} - virtual GrGLvoid programBinary(GrGLuint program, GrGLenum binaryFormat, void *binary, GrGLsizei length) {} - virtual GrGLvoid getProgramBinary(GrGLuint program, GrGLsizei bufsize, GrGLsizei* length, GrGLenum *binaryFormat, void *binary) {} - virtual GrGLvoid programParameteri(GrGLuint program, GrGLenum pname, GrGLint value) {} - -protected: - // This must be called by leaf class - void init(GrGLStandard standard) { - fStandard = standard; - fExtensions.init(standard, fFunctions.fGetString, fFunctions.fGetStringi, - fFunctions.fGetIntegerv, nullptr, GR_EGL_NO_DISPLAY); - } - TestInterface(); -}; - -template -GrGLFunction bind_to_member(TestInterface* interface, - R (TestInterface::*member)(A...)) { - return [interface, member](A... a) -> R { return (interface->*member)(a...); }; -} - -TestInterface::TestInterface() { - fFunctions.fActiveTexture = bind_to_member(this, &TestInterface::activeTexture); - fFunctions.fAttachShader = bind_to_member(this, &TestInterface::attachShader); - fFunctions.fBeginQuery = bind_to_member(this, &TestInterface::beginQuery); - fFunctions.fBindAttribLocation = bind_to_member(this, &TestInterface::bindAttribLocation); - fFunctions.fBindBuffer = bind_to_member(this, &TestInterface::bindBuffer); - fFunctions.fBindFramebuffer = bind_to_member(this, &TestInterface::bindFramebuffer); - fFunctions.fBindRenderbuffer = bind_to_member(this, &TestInterface::bindRenderbuffer); - fFunctions.fBindSampler = bind_to_member(this, &TestInterface::bindSampler); - fFunctions.fBindTexture = bind_to_member(this, &TestInterface::bindTexture); - fFunctions.fBindFragDataLocation = bind_to_member(this, &TestInterface::bindFragDataLocation); - fFunctions.fBindFragDataLocationIndexed = bind_to_member(this, &TestInterface::bindFragDataLocationIndexed); - fFunctions.fBindVertexArray = bind_to_member(this, &TestInterface::bindVertexArray); - fFunctions.fBlendBarrier = bind_to_member(this, &TestInterface::blendBarrier); - fFunctions.fBlendColor = bind_to_member(this, &TestInterface::blendColor); - fFunctions.fBlendEquation = bind_to_member(this, &TestInterface::blendEquation); - fFunctions.fBlendFunc = bind_to_member(this, &TestInterface::blendFunc); - fFunctions.fBlitFramebuffer = bind_to_member(this, &TestInterface::blitFramebuffer); - fFunctions.fBufferData = bind_to_member(this, &TestInterface::bufferData); - fFunctions.fBufferSubData = bind_to_member(this, &TestInterface::bufferSubData); - fFunctions.fCheckFramebufferStatus = bind_to_member(this, &TestInterface::checkFramebufferStatus); - fFunctions.fClear = bind_to_member(this, &TestInterface::clear); - fFunctions.fClearColor = bind_to_member(this, &TestInterface::clearColor); - fFunctions.fClearStencil = bind_to_member(this, &TestInterface::clearStencil); - fFunctions.fColorMask = bind_to_member(this, &TestInterface::colorMask); - fFunctions.fCompileShader = bind_to_member(this, &TestInterface::compileShader); - fFunctions.fCompressedTexImage2D = bind_to_member(this, &TestInterface::compressedTexImage2D); - fFunctions.fCompressedTexSubImage2D = bind_to_member(this, &TestInterface::compressedTexSubImage2D); - fFunctions.fCopyTexSubImage2D = bind_to_member(this, &TestInterface::copyTexSubImage2D); - fFunctions.fCreateProgram = bind_to_member(this, &TestInterface::createProgram); - fFunctions.fCreateShader = bind_to_member(this, &TestInterface::createShader); - fFunctions.fCullFace = bind_to_member(this, &TestInterface::cullFace); - fFunctions.fDeleteBuffers = bind_to_member(this, &TestInterface::deleteBuffers); - fFunctions.fDeleteFramebuffers = bind_to_member(this, &TestInterface::deleteFramebuffers); - fFunctions.fDeleteProgram = bind_to_member(this, &TestInterface::deleteProgram); - fFunctions.fDeleteQueries = bind_to_member(this, &TestInterface::deleteQueries); - fFunctions.fDeleteRenderbuffers = bind_to_member(this, &TestInterface::deleteRenderbuffers); - fFunctions.fDeleteSamplers = bind_to_member(this, &TestInterface::deleteSamplers); - fFunctions.fDeleteShader = bind_to_member(this, &TestInterface::deleteShader); - fFunctions.fDeleteTextures = bind_to_member(this, &TestInterface::deleteTextures); - fFunctions.fDeleteVertexArrays = bind_to_member(this, &TestInterface::deleteVertexArrays); - fFunctions.fDepthMask = bind_to_member(this, &TestInterface::depthMask); - fFunctions.fDisable = bind_to_member(this, &TestInterface::disable); - fFunctions.fDisableVertexAttribArray = bind_to_member(this, &TestInterface::disableVertexAttribArray); - fFunctions.fDrawArrays = bind_to_member(this, &TestInterface::drawArrays); - fFunctions.fDrawArraysInstanced = bind_to_member(this, &TestInterface::drawArraysInstanced); - fFunctions.fDrawArraysIndirect = bind_to_member(this, &TestInterface::drawArraysIndirect); - fFunctions.fDrawBuffer = bind_to_member(this, &TestInterface::drawBuffer); - fFunctions.fDrawBuffers = bind_to_member(this, &TestInterface::drawBuffers); - fFunctions.fDrawElements = bind_to_member(this, &TestInterface::drawElements); - fFunctions.fDrawElementsInstanced = bind_to_member(this, &TestInterface::drawElementsInstanced); - fFunctions.fDrawElementsIndirect = bind_to_member(this, &TestInterface::drawElementsIndirect); - fFunctions.fDrawRangeElements = bind_to_member(this, &TestInterface::drawRangeElements); - fFunctions.fEnable = bind_to_member(this, &TestInterface::enable); - fFunctions.fEnableVertexAttribArray = bind_to_member(this, &TestInterface::enableVertexAttribArray); - fFunctions.fEndQuery = bind_to_member(this, &TestInterface::endQuery); - fFunctions.fFinish = bind_to_member(this, &TestInterface::finish); - fFunctions.fFlush = bind_to_member(this, &TestInterface::flush); - fFunctions.fFlushMappedBufferRange = bind_to_member(this, &TestInterface::flushMappedBufferRange); - fFunctions.fFramebufferRenderbuffer = bind_to_member(this, &TestInterface::framebufferRenderbuffer); - fFunctions.fFramebufferTexture2D = bind_to_member(this, &TestInterface::framebufferTexture2D); - fFunctions.fFramebufferTexture2DMultisample = bind_to_member(this, &TestInterface::framebufferTexture2DMultisample); - fFunctions.fFrontFace = bind_to_member(this, &TestInterface::frontFace); - fFunctions.fGenBuffers = bind_to_member(this, &TestInterface::genBuffers); - fFunctions.fGenFramebuffers = bind_to_member(this, &TestInterface::genFramebuffers); - fFunctions.fGenerateMipmap = bind_to_member(this, &TestInterface::generateMipmap); - fFunctions.fGenQueries = bind_to_member(this, &TestInterface::genQueries); - fFunctions.fGenRenderbuffers = bind_to_member(this, &TestInterface::genRenderbuffers); - fFunctions.fGenSamplers = bind_to_member(this, &TestInterface::genSamplers); - fFunctions.fGenTextures = bind_to_member(this, &TestInterface::genTextures); - fFunctions.fGenVertexArrays = bind_to_member(this, &TestInterface::genVertexArrays); - fFunctions.fGetBufferParameteriv = bind_to_member(this, &TestInterface::getBufferParameteriv); - fFunctions.fGetError = bind_to_member(this, &TestInterface::getError); - fFunctions.fGetFramebufferAttachmentParameteriv = bind_to_member(this, &TestInterface::getFramebufferAttachmentParameteriv); - fFunctions.fGetIntegerv = bind_to_member(this, &TestInterface::getIntegerv); - fFunctions.fGetMultisamplefv = bind_to_member(this, &TestInterface::getMultisamplefv); - fFunctions.fGetProgramInfoLog = bind_to_member(this, &TestInterface::getProgramInfoLog); - fFunctions.fGetProgramiv = bind_to_member(this, &TestInterface::getProgramiv); - fFunctions.fGetQueryiv = bind_to_member(this, &TestInterface::getQueryiv); - fFunctions.fGetQueryObjecti64v = bind_to_member(this, &TestInterface::getQueryObjecti64v); - fFunctions.fGetQueryObjectiv = bind_to_member(this, &TestInterface::getQueryObjectiv); - fFunctions.fGetQueryObjectui64v = bind_to_member(this, &TestInterface::getQueryObjectui64v); - fFunctions.fGetQueryObjectuiv = bind_to_member(this, &TestInterface::getQueryObjectuiv); - fFunctions.fGetRenderbufferParameteriv = bind_to_member(this, &TestInterface::getRenderbufferParameteriv); - fFunctions.fGetShaderInfoLog = bind_to_member(this, &TestInterface::getShaderInfoLog); - fFunctions.fGetShaderiv = bind_to_member(this, &TestInterface::getShaderiv); - fFunctions.fGetShaderPrecisionFormat = bind_to_member(this, &TestInterface::getShaderPrecisionFormat); - fFunctions.fGetString = bind_to_member(this, &TestInterface::getString); - fFunctions.fGetStringi = bind_to_member(this, &TestInterface::getStringi); - fFunctions.fGetTexLevelParameteriv = bind_to_member(this, &TestInterface::getTexLevelParameteriv); - fFunctions.fGetUniformLocation = bind_to_member(this, &TestInterface::getUniformLocation); - fFunctions.fInsertEventMarker = bind_to_member(this, &TestInterface::insertEventMarker); - fFunctions.fInvalidateBufferData = bind_to_member(this, &TestInterface::invalidateBufferData); - fFunctions.fInvalidateBufferSubData = bind_to_member(this, &TestInterface::invalidateBufferSubData); - fFunctions.fInvalidateFramebuffer = bind_to_member(this, &TestInterface::invalidateFramebuffer); - fFunctions.fInvalidateSubFramebuffer = bind_to_member(this, &TestInterface::invalidateSubFramebuffer); - fFunctions.fInvalidateTexImage = bind_to_member(this, &TestInterface::invalidateTexImage); - fFunctions.fInvalidateTexSubImage = bind_to_member(this, &TestInterface::invalidateTexSubImage); - fFunctions.fIsTexture = bind_to_member(this, &TestInterface::isTexture); - fFunctions.fLineWidth = bind_to_member(this, &TestInterface::lineWidth); - fFunctions.fLinkProgram = bind_to_member(this, &TestInterface::linkProgram); - fFunctions.fMapBuffer = bind_to_member(this, &TestInterface::mapBuffer); - fFunctions.fMapBufferRange = bind_to_member(this, &TestInterface::mapBufferRange); - fFunctions.fMapBufferSubData = bind_to_member(this, &TestInterface::mapBufferSubData); - fFunctions.fMapTexSubImage2D = bind_to_member(this, &TestInterface::mapTexSubImage2D); - fFunctions.fPixelStorei = bind_to_member(this, &TestInterface::pixelStorei); - fFunctions.fPolygonMode = bind_to_member(this, &TestInterface::polygonMode); - fFunctions.fPopGroupMarker = bind_to_member(this, &TestInterface::popGroupMarker); - fFunctions.fPushGroupMarker = bind_to_member(this, &TestInterface::pushGroupMarker); - fFunctions.fQueryCounter = bind_to_member(this, &TestInterface::queryCounter); - fFunctions.fReadBuffer = bind_to_member(this, &TestInterface::readBuffer); - fFunctions.fReadPixels = bind_to_member(this, &TestInterface::readPixels); - fFunctions.fRenderbufferStorage = bind_to_member(this, &TestInterface::renderbufferStorage); - fFunctions.fRenderbufferStorageMultisample = bind_to_member(this, &TestInterface::renderbufferStorageMultisample); - fFunctions.fResolveMultisampleFramebuffer = bind_to_member(this, &TestInterface::resolveMultisampleFramebuffer); - fFunctions.fScissor = bind_to_member(this, &TestInterface::scissor); - fFunctions.fBindUniformLocation = bind_to_member(this, &TestInterface::bindUniformLocation); - fFunctions.fSamplerParameteri = bind_to_member(this, &TestInterface::samplerParameteri); - fFunctions.fSamplerParameteriv = bind_to_member(this, &TestInterface::samplerParameteriv); - fFunctions.fShaderSource = bind_to_member(this, &TestInterface::shaderSource); - fFunctions.fStencilFunc = bind_to_member(this, &TestInterface::stencilFunc); - fFunctions.fStencilFuncSeparate = bind_to_member(this, &TestInterface::stencilFuncSeparate); - fFunctions.fStencilMask = bind_to_member(this, &TestInterface::stencilMask); - fFunctions.fStencilMaskSeparate = bind_to_member(this, &TestInterface::stencilMaskSeparate); - fFunctions.fStencilOp = bind_to_member(this, &TestInterface::stencilOp); - fFunctions.fStencilOpSeparate = bind_to_member(this, &TestInterface::stencilOpSeparate); - fFunctions.fTexBuffer = bind_to_member(this, &TestInterface::texBuffer); - fFunctions.fTexImage2D = bind_to_member(this, &TestInterface::texImage2D); - fFunctions.fTexParameterf = bind_to_member(this, &TestInterface::texParameterf); - fFunctions.fTexParameterfv = bind_to_member(this, &TestInterface::texParameterfv); - fFunctions.fTexParameteri = bind_to_member(this, &TestInterface::texParameteri); - fFunctions.fTexParameteriv = bind_to_member(this, &TestInterface::texParameteriv); - fFunctions.fTexStorage2D = bind_to_member(this, &TestInterface::texStorage2D); - fFunctions.fDiscardFramebuffer = bind_to_member(this, &TestInterface::discardFramebuffer); - fFunctions.fTexSubImage2D = bind_to_member(this, &TestInterface::texSubImage2D); - fFunctions.fTextureBarrier = bind_to_member(this, &TestInterface::textureBarrier); - fFunctions.fUniform1f = bind_to_member(this, &TestInterface::uniform1f); - fFunctions.fUniform1i = bind_to_member(this, &TestInterface::uniform1i); - fFunctions.fUniform1fv = bind_to_member(this, &TestInterface::uniform1fv); - fFunctions.fUniform1iv = bind_to_member(this, &TestInterface::uniform1iv); - fFunctions.fUniform2f = bind_to_member(this, &TestInterface::uniform2f); - fFunctions.fUniform2i = bind_to_member(this, &TestInterface::uniform2i); - fFunctions.fUniform2fv = bind_to_member(this, &TestInterface::uniform2fv); - fFunctions.fUniform2iv = bind_to_member(this, &TestInterface::uniform2iv); - fFunctions.fUniform3f = bind_to_member(this, &TestInterface::uniform3f); - fFunctions.fUniform3i = bind_to_member(this, &TestInterface::uniform3i); - fFunctions.fUniform3fv = bind_to_member(this, &TestInterface::uniform3fv); - fFunctions.fUniform3iv = bind_to_member(this, &TestInterface::uniform3iv); - fFunctions.fUniform4f = bind_to_member(this, &TestInterface::uniform4f); - fFunctions.fUniform4i = bind_to_member(this, &TestInterface::uniform4i); - fFunctions.fUniform4fv = bind_to_member(this, &TestInterface::uniform4fv); - fFunctions.fUniform4iv = bind_to_member(this, &TestInterface::uniform4iv); - fFunctions.fUniformMatrix2fv = bind_to_member(this, &TestInterface::uniformMatrix2fv); - fFunctions.fUniformMatrix3fv = bind_to_member(this, &TestInterface::uniformMatrix3fv); - fFunctions.fUniformMatrix4fv = bind_to_member(this, &TestInterface::uniformMatrix4fv); - fFunctions.fUnmapBuffer = bind_to_member(this, &TestInterface::unmapBuffer); - fFunctions.fUnmapBufferSubData = bind_to_member(this, &TestInterface::unmapBufferSubData); - fFunctions.fUnmapTexSubImage2D = bind_to_member(this, &TestInterface::unmapTexSubImage2D); - fFunctions.fUseProgram = bind_to_member(this, &TestInterface::useProgram); - fFunctions.fVertexAttrib1f = bind_to_member(this, &TestInterface::vertexAttrib1f); - fFunctions.fVertexAttrib2fv = bind_to_member(this, &TestInterface::vertexAttrib2fv); - fFunctions.fVertexAttrib3fv = bind_to_member(this, &TestInterface::vertexAttrib3fv); - fFunctions.fVertexAttrib4fv = bind_to_member(this, &TestInterface::vertexAttrib4fv); - fFunctions.fVertexAttribDivisor = bind_to_member(this, &TestInterface::vertexAttribDivisor); - fFunctions.fVertexAttribIPointer = bind_to_member(this, &TestInterface::vertexAttribIPointer); - fFunctions.fVertexAttribPointer = bind_to_member(this, &TestInterface::vertexAttribPointer); - fFunctions.fViewport = bind_to_member(this, &TestInterface::viewport); - fFunctions.fMatrixLoadf = bind_to_member(this, &TestInterface::matrixLoadf); - fFunctions.fMatrixLoadIdentity = bind_to_member(this, &TestInterface::matrixLoadIdentity); - fFunctions.fPathCommands = bind_to_member(this, &TestInterface::pathCommands); - fFunctions.fPathParameteri = bind_to_member(this, &TestInterface::pathParameteri); - fFunctions.fPathParameterf = bind_to_member(this, &TestInterface::pathParameterf); - fFunctions.fGenPaths = bind_to_member(this, &TestInterface::genPaths); - fFunctions.fDeletePaths = bind_to_member(this, &TestInterface::deletePaths); - fFunctions.fIsPath = bind_to_member(this, &TestInterface::isPath); - fFunctions.fPathStencilFunc = bind_to_member(this, &TestInterface::pathStencilFunc); - fFunctions.fStencilFillPath = bind_to_member(this, &TestInterface::stencilFillPath); - fFunctions.fStencilStrokePath = bind_to_member(this, &TestInterface::stencilStrokePath); - fFunctions.fStencilFillPathInstanced = bind_to_member(this, &TestInterface::stencilFillPathInstanced); - fFunctions.fStencilStrokePathInstanced = bind_to_member(this, &TestInterface::stencilStrokePathInstanced); - fFunctions.fCoverFillPath = bind_to_member(this, &TestInterface::coverFillPath); - fFunctions.fCoverStrokePath = bind_to_member(this, &TestInterface::coverStrokePath); - fFunctions.fCoverFillPathInstanced = bind_to_member(this, &TestInterface::coverFillPathInstanced); - fFunctions.fCoverStrokePathInstanced = bind_to_member(this, &TestInterface::coverStrokePathInstanced); - fFunctions.fStencilThenCoverFillPath = bind_to_member(this, &TestInterface::stencilThenCoverFillPath); - fFunctions.fStencilThenCoverStrokePath = bind_to_member(this, &TestInterface::stencilThenCoverStrokePath); - fFunctions.fStencilThenCoverFillPathInstanced = bind_to_member(this, &TestInterface::stencilThenCoverFillPathInstanced); - fFunctions.fStencilThenCoverStrokePathInstanced = bind_to_member(this, &TestInterface::stencilThenCoverStrokePathInstanced); - fFunctions.fProgramPathFragmentInputGen = bind_to_member(this, &TestInterface::programPathFragmentInputGen); - fFunctions.fBindFragmentInputLocation = bind_to_member(this, &TestInterface::bindFragmentInputLocation); - fFunctions.fGetProgramResourceLocation = bind_to_member(this, &TestInterface::getProgramResourceLocation); - fFunctions.fCoverageModulation = bind_to_member(this, &TestInterface::coverageModulation); - fFunctions.fMultiDrawArraysIndirect = bind_to_member(this, &TestInterface::multiDrawArraysIndirect); - fFunctions.fMultiDrawElementsIndirect = bind_to_member(this, &TestInterface::multiDrawElementsIndirect); - fFunctions.fFenceSync = bind_to_member(this, &TestInterface::fenceSync); - fFunctions.fIsSync = bind_to_member(this, &TestInterface::isSync); - fFunctions.fClientWaitSync = bind_to_member(this, &TestInterface::clientWaitSync); - fFunctions.fWaitSync = bind_to_member(this, &TestInterface::waitSync); - fFunctions.fDeleteSync = bind_to_member(this, &TestInterface::deleteSync); - fFunctions.fDebugMessageControl = bind_to_member(this, &TestInterface::debugMessageControl); - fFunctions.fDebugMessageInsert = bind_to_member(this, &TestInterface::debugMessageInsert); - fFunctions.fDebugMessageCallback = bind_to_member(this, &TestInterface::debugMessageCallback); - fFunctions.fGetDebugMessageLog = bind_to_member(this, &TestInterface::getDebugMessageLog); - fFunctions.fPushDebugGroup = bind_to_member(this, &TestInterface::pushDebugGroup); - fFunctions.fPopDebugGroup = bind_to_member(this, &TestInterface::popDebugGroup); - fFunctions.fObjectLabel = bind_to_member(this, &TestInterface::objectLabel); - fFunctions.fGetInternalformativ = bind_to_member(this, &TestInterface::getInternalformativ); - fFunctions.fProgramBinary = bind_to_member(this, &TestInterface::programBinary); - fFunctions.fGetProgramBinary = bind_to_member(this, &TestInterface::getProgramBinary); - fFunctions.fProgramParameteri = bind_to_member(this, &TestInterface::programParameteri); -} - -/** Null interface implementation */ -class NullInterface : public TestInterface { -public: - NullInterface(bool enableNVPR) - : fCurrDrawFramebuffer(0) - , fCurrReadFramebuffer(0) - , fCurrRenderbuffer(0) - , fCurrProgramID(0) - , fCurrShaderID(0) - , fCurrGenericID(0) - , fCurrUniformLocation(0) - , fCurrPathID(0) { - memset(fBoundBuffers, 0, sizeof(fBoundBuffers)); - fAdvertisedExtensions.push_back("GL_ARB_framebuffer_object"); - fAdvertisedExtensions.push_back("GL_ARB_blend_func_extended"); - fAdvertisedExtensions.push_back("GL_ARB_timer_query"); - fAdvertisedExtensions.push_back("GL_ARB_draw_buffers"); - fAdvertisedExtensions.push_back("GL_ARB_occlusion_query"); - fAdvertisedExtensions.push_back("GL_EXT_stencil_wrap"); - if (enableNVPR) { - fAdvertisedExtensions.push_back("GL_NV_path_rendering"); - fAdvertisedExtensions.push_back("GL_ARB_program_interface_query"); - } - fAdvertisedExtensions.push_back(nullptr); - - this->init(kGL_GrGLStandard); - } - - GrGLenum checkFramebufferStatus(GrGLenum target) override { - return GR_GL_FRAMEBUFFER_COMPLETE; - } - - GrGLvoid genBuffers(GrGLsizei n, GrGLuint* ids) override { - for (int i = 0; i < n; ++i) { - Buffer* buffer = fBufferManager.create(); - ids[i] = buffer->id(); - } - } - - GrGLvoid bufferData(GrGLenum target, GrGLsizeiptr size, const GrGLvoid* data, - GrGLenum usage) override { - GrGLuint id = fBoundBuffers[GetBufferIndex(target)]; - if (id > 0) { - Buffer* buffer = fBufferManager.lookUp(id); - buffer->allocate(size, (const GrGLchar*) data); - } - } - - GrGLuint createProgram() override { - return ++fCurrProgramID; - } - - GrGLuint createShader(GrGLenum type) override { - return ++fCurrShaderID; - } - - GrGLvoid bindBuffer(GrGLenum target, GrGLuint buffer) override { - fBoundBuffers[GetBufferIndex(target)] = buffer; - } - - // deleting a bound buffer has the side effect of binding 0 - GrGLvoid deleteBuffers(GrGLsizei n, const GrGLuint* ids) override { - // First potentially unbind the buffers. - for (int buffIdx = 0; buffIdx < kNumBufferTargets; ++buffIdx) { - if (!fBoundBuffers[buffIdx]) { - continue; - } - for (int i = 0; i < n; ++i) { - if (ids[i] == fBoundBuffers[buffIdx]) { - fBoundBuffers[buffIdx] = 0; - break; - } - } - } - - // Then actually "delete" the buffers. - for (int i = 0; i < n; ++i) { - if (ids[i] > 0) { - Buffer* buffer = fBufferManager.lookUp(ids[i]); - fBufferManager.free(buffer); - } - } - } - - GrGLvoid genFramebuffers(GrGLsizei n, GrGLuint *framebuffers) override { - for (int i = 0; i < n; ++i) { - Framebuffer* framebuffer = fFramebufferManager.create(); - framebuffers[i] = framebuffer->id(); - } - } - - GrGLvoid bindFramebuffer(GrGLenum target, GrGLuint framebuffer) override { - SkASSERT(GR_GL_FRAMEBUFFER == target || GR_GL_DRAW_FRAMEBUFFER == target || - GR_GL_READ_FRAMEBUFFER == target); - if (GR_GL_READ_FRAMEBUFFER != target) { - fCurrDrawFramebuffer = framebuffer; - } - if (GR_GL_DRAW_FRAMEBUFFER != target) { - fCurrReadFramebuffer = framebuffer; - } - } - - GrGLvoid deleteFramebuffers(GrGLsizei n, const GrGLuint* ids) override { - for (int i = 0; i < n; ++i) { - if (ids[i] == fCurrDrawFramebuffer) { - fCurrDrawFramebuffer = 0; - } - if (ids[i] == fCurrReadFramebuffer) { - fCurrReadFramebuffer = 0; - } - - if (ids[i] > 0) { - Framebuffer* framebuffer = fFramebufferManager.lookUp(ids[i]); - fFramebufferManager.free(framebuffer); - } - } - } - - GrGLvoid genQueries(GrGLsizei n, GrGLuint *ids) override { this->genGenericIds(n, ids); } - - GrGLvoid genRenderbuffers(GrGLsizei n, GrGLuint *renderbuffers) override { - for (int i = 0; i < n; ++i) { - Renderbuffer* renderbuffer = fRenderbufferManager.create(); - renderbuffers[i] = renderbuffer->id(); - } - } - - GrGLvoid bindRenderbuffer(GrGLenum target, GrGLuint renderbuffer) override { - SkASSERT(GR_GL_RENDERBUFFER == target); - fCurrRenderbuffer = renderbuffer; - } - - GrGLvoid deleteRenderbuffers(GrGLsizei n, const GrGLuint* ids) override { - for (int i = 0; i < n; ++i) { - if (ids[i] <= 0) { - continue; - } - if (ids[i] == fCurrRenderbuffer) { - fCurrRenderbuffer = 0; - } - Renderbuffer* renderbuffer = fRenderbufferManager.lookUp(ids[i]); - - if (fCurrDrawFramebuffer) { - Framebuffer* drawFramebuffer = fFramebufferManager.lookUp(fCurrDrawFramebuffer); - drawFramebuffer->notifyAttachmentDeleteWhileBound(renderbuffer); - } - if (fCurrReadFramebuffer) { - Framebuffer* readFramebuffer = fFramebufferManager.lookUp(fCurrReadFramebuffer); - readFramebuffer->notifyAttachmentDeleteWhileBound(renderbuffer); - } - - fRenderbufferManager.free(renderbuffer); - } - } - - GrGLvoid renderbufferStorage(GrGLenum target, GrGLenum internalformat, GrGLsizei width, - GrGLsizei height) override { - GrAlwaysAssert(GR_GL_RENDERBUFFER == target); - GrAlwaysAssert(fCurrRenderbuffer); - Renderbuffer* renderbuffer = fRenderbufferManager.lookUp(fCurrRenderbuffer); - renderbuffer->setNumSamples(1); - } - - GrGLvoid renderbufferStorageMultisample(GrGLenum target, GrGLsizei samples, - GrGLenum internalformat, GrGLsizei width, - GrGLsizei height) override { - GrAlwaysAssert(GR_GL_RENDERBUFFER == target); - GrAlwaysAssert(samples > 0); - GrAlwaysAssert(fCurrRenderbuffer); - Renderbuffer* renderbuffer = fRenderbufferManager.lookUp(fCurrRenderbuffer); - renderbuffer->setNumSamples(samples); - } - - GrGLvoid namedRenderbufferStorage(GrGLuint renderbuffer, GrGLenum GrGLinternalformat, - GrGLsizei width, GrGLsizei height) override { - SK_ABORT("Not implemented"); - } - - GrGLvoid namedRenderbufferStorageMultisample(GrGLuint renderbuffer, GrGLsizei samples, - GrGLenum GrGLinternalformat, GrGLsizei width, - GrGLsizei height) override { - SK_ABORT("Not implemented"); - } - - GrGLvoid framebufferRenderbuffer(GrGLenum target, GrGLenum attachment, - GrGLenum renderbuffertarget, - GrGLuint renderBufferID) override { - GrGLuint id = this->getBoundFramebufferID(target); - GrAlwaysAssert(id); - Framebuffer* framebuffer = fFramebufferManager.lookUp(id); - - GrAlwaysAssert(GR_GL_RENDERBUFFER == renderbuffertarget); - if (!renderBufferID && !fCurrRenderbuffer) { - return; - } - GrAlwaysAssert(fCurrRenderbuffer); - Renderbuffer* renderbuffer = fRenderbufferManager.lookUp(fCurrRenderbuffer); - - framebuffer->setAttachment(attachment, renderbuffer); - } - - GrGLvoid namedFramebufferRenderbuffer(GrGLuint framebuffer, GrGLenum attachment, - GrGLenum renderbuffertarget, - GrGLuint renderbuffer) override { - SK_ABORT("Not implemented"); - } - - GrGLvoid genSamplers(GrGLsizei n, GrGLuint* samplers) override { - this->genGenericIds(n, samplers); - } - - GrGLvoid genTextures(GrGLsizei n, GrGLuint *textures) override { - this->genGenericIds(n, textures); - } - - GrGLvoid framebufferTexture2D(GrGLenum target, GrGLenum attachment, GrGLenum textarget, - GrGLuint textureID, GrGLint level) override { - GrGLuint id = this->getBoundFramebufferID(target); - GrAlwaysAssert(id); - Framebuffer* framebuffer = fFramebufferManager.lookUp(id); - framebuffer->setAttachment(attachment, this->getSingleTextureObject()); - } - - GrGLvoid framebufferTexture2DMultisample(GrGLenum target, GrGLenum attachment, - GrGLenum textarget, GrGLuint texture, GrGLint level, - GrGLsizei samples) override { - SK_ABORT("Not implemented"); - } - - GrGLvoid namedFramebufferTexture1D(GrGLuint framebuffer, GrGLenum attachment, - GrGLenum textarget, GrGLuint texture, - GrGLint level) override { - SK_ABORT("Not implemented"); - } - - GrGLvoid namedFramebufferTexture2D(GrGLuint framebuffer, GrGLenum attachment, - GrGLenum textarget, GrGLuint texture, - GrGLint level) override { - SK_ABORT("Not implemented"); - } - - GrGLvoid namedFramebufferTexture3D(GrGLuint framebuffer, GrGLenum attachment, - GrGLenum textarget, GrGLuint texture, GrGLint level, - GrGLint zoffset) override { - SK_ABORT("Not implemented"); - } - - GrGLvoid genVertexArrays(GrGLsizei n, GrGLuint *arrays) override { - this->genGenericIds(n, arrays); - } - - GrGLenum getError() override { return GR_GL_NO_ERROR; } - - GrGLvoid getIntegerv(GrGLenum pname, GrGLint* params) override { - // TODO: remove from Ganesh the #defines for gets we don't use. - // We would like to minimize gets overall due to performance issues - switch (pname) { - case GR_GL_CONTEXT_PROFILE_MASK: - *params = GR_GL_CONTEXT_COMPATIBILITY_PROFILE_BIT; - break; - case GR_GL_STENCIL_BITS: - *params = 8; - break; - case GR_GL_SAMPLES: { - GrAlwaysAssert(fCurrDrawFramebuffer); - Framebuffer* framebuffer = fFramebufferManager.lookUp(fCurrDrawFramebuffer); - *params = framebuffer->numSamples(); - break; - } - case GR_GL_FRAMEBUFFER_BINDING: - *params = 0; - break; - case GR_GL_VIEWPORT: - params[0] = 0; - params[1] = 0; - params[2] = 800; - params[3] = 600; - break; - case GR_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: - case GR_GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS: - case GR_GL_MAX_TEXTURE_IMAGE_UNITS: - case GR_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: - *params = 8; - break; - case GR_GL_MAX_TEXTURE_COORDS: - *params = 8; - break; - case GR_GL_MAX_VERTEX_UNIFORM_VECTORS: - *params = kDefaultMaxVertexUniformVectors; - break; - case GR_GL_MAX_FRAGMENT_UNIFORM_VECTORS: - *params = kDefaultMaxFragmentUniformVectors; - break; - case GR_GL_MAX_FRAGMENT_UNIFORM_COMPONENTS: - *params = 16 * 4; - break; - case GR_GL_NUM_COMPRESSED_TEXTURE_FORMATS: - *params = 0; - break; - case GR_GL_COMPRESSED_TEXTURE_FORMATS: - break; - case GR_GL_MAX_TEXTURE_SIZE: - *params = 8192; - break; - case GR_GL_MAX_RENDERBUFFER_SIZE: - *params = 8192; - break; - case GR_GL_MAX_SAMPLES: - *params = 32; - break; - case GR_GL_MAX_VERTEX_ATTRIBS: - *params = kDefaultMaxVertexAttribs; - break; - case GR_GL_MAX_VARYING_VECTORS: - *params = kDefaultMaxVaryingVectors; - break; - case GR_GL_NUM_EXTENSIONS: { - GrGLint i = 0; - while (fAdvertisedExtensions[i++]); - *params = i; - break; - } - default: - SK_ABORT("Unexpected pname to GetIntegerv"); - } - } - - GrGLvoid getProgramiv(GrGLuint program, GrGLenum pname, GrGLint* params) override { - this->getShaderOrProgramiv(program, pname, params); - } - - GrGLvoid getProgramInfoLog(GrGLuint program, GrGLsizei bufsize, GrGLsizei* length, - char* infolog) override { - this->getInfoLog(program, bufsize, length, infolog); - } - - GrGLvoid getMultisamplefv(GrGLenum pname, GrGLuint index, GrGLfloat* val) override { - val[0] = val[1] = 0.5f; - } - - GrGLvoid getQueryiv(GrGLenum GLtarget, GrGLenum pname, GrGLint *params) override { - switch (pname) { - case GR_GL_CURRENT_QUERY: - *params = 0; - break; - case GR_GL_QUERY_COUNTER_BITS: - *params = 32; - break; - default: - SK_ABORT("Unexpected pname passed GetQueryiv."); - } - } - - GrGLvoid getQueryObjecti64v(GrGLuint id, GrGLenum pname, GrGLint64 *params) override { - this->queryResult(id, pname, params); - } - - GrGLvoid getQueryObjectiv(GrGLuint id, GrGLenum pname, GrGLint *params) override { - this->queryResult(id, pname, params); - } - - GrGLvoid getQueryObjectui64v(GrGLuint id, GrGLenum pname, GrGLuint64 *params) override { - this->queryResult(id, pname, params); - } - - GrGLvoid getQueryObjectuiv(GrGLuint id, GrGLenum pname, GrGLuint *params) override { - this->queryResult(id, pname, params); - } - - GrGLvoid getShaderiv(GrGLuint shader, GrGLenum pname, GrGLint* params) override { - this->getShaderOrProgramiv(shader, pname, params); - } - - GrGLvoid getShaderInfoLog(GrGLuint shader, GrGLsizei bufsize, GrGLsizei* length, - char* infolog) override { - this->getInfoLog(shader, bufsize, length, infolog); - } - - const GrGLubyte* getString(GrGLenum name) override { - switch (name) { - case GR_GL_EXTENSIONS: - return CombinedExtensionString(); - case GR_GL_VERSION: - return (const GrGLubyte*)"4.0 Null GL"; - case GR_GL_SHADING_LANGUAGE_VERSION: - return (const GrGLubyte*)"4.20.8 Null GLSL"; - case GR_GL_VENDOR: - return (const GrGLubyte*)"Null Vendor"; - case GR_GL_RENDERER: - return (const GrGLubyte*)"The Null (Non-)Renderer"; - default: - SK_ABORT("Unexpected name passed to GetString"); - return nullptr; - } - } - - const GrGLubyte* getStringi(GrGLenum name, GrGLuint i) override { - switch (name) { - case GR_GL_EXTENSIONS: { - GrGLint count; - this->getIntegerv(GR_GL_NUM_EXTENSIONS, &count); - if ((GrGLint)i <= count) { - return (const GrGLubyte*) fAdvertisedExtensions[i]; - } else { - return nullptr; - } - } - default: - SK_ABORT("Unexpected name passed to GetStringi"); - return nullptr; - } - } - - GrGLint getUniformLocation(GrGLuint program, const char* name) override { - return ++fCurrUniformLocation; - } - - GrGLvoid* mapBufferRange(GrGLenum target, GrGLintptr offset, GrGLsizeiptr length, - GrGLbitfield access) override { - GrGLuint id = fBoundBuffers[GetBufferIndex(target)]; - if (id > 0) { - // We just ignore the offset and length here. - Buffer* buffer = fBufferManager.lookUp(id); - SkASSERT(!buffer->mapped()); - buffer->setMapped(true); - return buffer->dataPtr(); - } - return nullptr; - } - - GrGLvoid* mapBuffer(GrGLenum target, GrGLenum access) override { - GrGLuint id = fBoundBuffers[GetBufferIndex(target)]; - if (id > 0) { - Buffer* buffer = fBufferManager.lookUp(id); - SkASSERT(!buffer->mapped()); - buffer->setMapped(true); - return buffer->dataPtr(); - } - - SkASSERT(false); - return nullptr; // no buffer bound to target - } - - GrGLboolean unmapBuffer(GrGLenum target) override { - GrGLuint id = fBoundBuffers[GetBufferIndex(target)]; - if (id > 0) { - Buffer* buffer = fBufferManager.lookUp(id); - SkASSERT(buffer->mapped()); - buffer->setMapped(false); - return GR_GL_TRUE; - } - - GrAlwaysAssert(false); - return GR_GL_FALSE; // GR_GL_INVALID_OPERATION; - } - - GrGLvoid getBufferParameteriv(GrGLenum target, GrGLenum pname, GrGLint* params) override { - switch (pname) { - case GR_GL_BUFFER_MAPPED: { - *params = GR_GL_FALSE; - GrGLuint id = fBoundBuffers[GetBufferIndex(target)]; - if (id > 0) { - Buffer* buffer = fBufferManager.lookUp(id); - if (buffer->mapped()) { - *params = GR_GL_TRUE; - } - } - break; } - default: - SK_ABORT("Unexpected pname to GetBufferParamateriv"); - break; - } - } - - // NV_path_rendering - GrGLuint genPaths(GrGLsizei range) override { - return ++fCurrPathID; - } - - -private: - inline int static GetBufferIndex(GrGLenum glTarget) { - switch (glTarget) { - default: SK_ABORT("Unexpected GL target to GetBufferIndex"); - case GR_GL_ARRAY_BUFFER: return 0; - case GR_GL_ELEMENT_ARRAY_BUFFER: return 1; - case GR_GL_TEXTURE_BUFFER: return 2; - case GR_GL_DRAW_INDIRECT_BUFFER: return 3; - case GR_GL_PIXEL_PACK_BUFFER: return 4; - case GR_GL_PIXEL_UNPACK_BUFFER: return 5; - } - } - constexpr int static kNumBufferTargets = 6; - - TGLObjectManager fBufferManager; - GrGLuint fBoundBuffers[kNumBufferTargets]; - TGLObjectManager fFramebufferManager; - GrGLuint fCurrDrawFramebuffer; - GrGLuint fCurrReadFramebuffer; - TGLObjectManager fRenderbufferManager; - GrGLuint fCurrRenderbuffer; - GrGLuint fCurrProgramID; - GrGLuint fCurrShaderID; - GrGLuint fCurrGenericID; - GrGLuint fCurrUniformLocation; - GrGLuint fCurrPathID; - sk_sp fSingleTextureObject; - SkTArray fAdvertisedExtensions; - - // the OpenGLES 2.0 spec says this must be >= 128 - static const GrGLint kDefaultMaxVertexUniformVectors = 128; - - // the OpenGLES 2.0 spec says this must be >=16 - static const GrGLint kDefaultMaxFragmentUniformVectors = 16; - - // the OpenGLES 2.0 spec says this must be >= 8 - static const GrGLint kDefaultMaxVertexAttribs = 8; - - // the OpenGLES 2.0 spec says this must be >= 8 - static const GrGLint kDefaultMaxVaryingVectors = 8; - - GrGLuint getBoundFramebufferID(GrGLenum target) { - switch (target) { - case GR_GL_FRAMEBUFFER: - case GR_GL_DRAW_FRAMEBUFFER: - return fCurrDrawFramebuffer; - case GR_GL_READ_FRAMEBUFFER: - return fCurrReadFramebuffer; - default: - SK_ABORT("Invalid framebuffer target."); - return 0; - } - } - - const Texture* getSingleTextureObject() { - // We currently only use FramebufferAttachment objects for a sample count, and all textures - // in Skia have one sample, so there is no need as of yet to track individual textures. This - // also works around a bug in chromium's cc_unittests where they send us texture IDs that - // were generated by cc::TestGLES2Interface. - if (!fSingleTextureObject) { - fSingleTextureObject.reset(new Texture); - } - return fSingleTextureObject.get(); - } - - const GrGLubyte* CombinedExtensionString() { - static SkString gExtString; - static SkMutex gMutex; - gMutex.acquire(); - if (0 == gExtString.size()) { - int i = 0; - while (fAdvertisedExtensions[i]) { - if (i > 0) { - gExtString.append(" "); - } - gExtString.append(fAdvertisedExtensions[i]); - ++i; - } - } - gMutex.release(); - return (const GrGLubyte*) gExtString.c_str(); - } - - GrGLvoid genGenericIds(GrGLsizei n, GrGLuint* ids) { - for (int i = 0; i < n; ++i) { - ids[i] = ++fCurrGenericID; - } - } - - GrGLvoid getInfoLog(GrGLuint object, GrGLsizei bufsize, GrGLsizei* length, - char* infolog) { - if (length) { - *length = 0; - } - if (bufsize > 0) { - *infolog = 0; - } - } - - GrGLvoid getShaderOrProgramiv(GrGLuint object, GrGLenum pname, GrGLint* params) { - switch (pname) { - case GR_GL_LINK_STATUS: // fallthru - case GR_GL_COMPILE_STATUS: - *params = GR_GL_TRUE; - break; - case GR_GL_INFO_LOG_LENGTH: // fallthru - case GL_PROGRAM_BINARY_LENGTH: - *params = 0; - break; - // we don't expect any other pnames - default: - SK_ABORT("Unexpected pname to GetProgramiv"); - break; - } - } - - template - void queryResult(GrGLenum GLtarget, GrGLenum pname, T *params) { - switch (pname) { - case GR_GL_QUERY_RESULT_AVAILABLE: - *params = GR_GL_TRUE; - break; - case GR_GL_QUERY_RESULT: - *params = 0; - break; - default: - SK_ABORT("Unexpected pname passed to GetQueryObject."); - break; - } - } - - typedef TestInterface INHERITED; -}; - -} // anonymous namespace - -namespace android { -namespace uirenderer { -namespace debug { - -const GrGLInterface* CreateNullSkiaInterface() { return new NullInterface(false); } - -} // namespace debug -} // namespace uirenderer -} // namespace android diff --git a/libs/hwui/debug/ScopedReplaceDriver.h b/libs/hwui/debug/ScopedReplaceDriver.h deleted file mode 100644 index 110196fb2a01..000000000000 --- a/libs/hwui/debug/ScopedReplaceDriver.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2016 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. - */ - -#pragma once - -#include "GlesDriver.h" - -namespace android { -namespace uirenderer { -namespace debug { - -template -class ScopedReplaceDriver { -public: - ScopedReplaceDriver() { - std::unique_ptr glDriver = std::make_unique(); - mCurrentDriver = glDriver.get(); - mOldDriver = GlesDriver::replace(std::move(glDriver)); - } - - Driver& get() { return *mCurrentDriver; } - - ~ScopedReplaceDriver() { GlesDriver::replace(std::move(mOldDriver)); } - -private: - std::unique_ptr mOldDriver; - Driver* mCurrentDriver; -}; - -} // namespace debug -} // namespace uirenderer -} // namespace android diff --git a/libs/hwui/debug/gles_decls.in b/libs/hwui/debug/gles_decls.in deleted file mode 100644 index 3900959c29de..000000000000 --- a/libs/hwui/debug/gles_decls.in +++ /dev/null @@ -1,543 +0,0 @@ -GL_ENTRY(void, glActiveTexture, GLenum texture) -GL_ENTRY(void, glAttachShader, GLuint program, GLuint shader) -GL_ENTRY(void, glBindAttribLocation, GLuint program, GLuint index, const GLchar *name) -GL_ENTRY(void, glBindBuffer, GLenum target, GLuint buffer) -GL_ENTRY(void, glBindFramebuffer, GLenum target, GLuint framebuffer) -GL_ENTRY(void, glBindRenderbuffer, GLenum target, GLuint renderbuffer) -GL_ENTRY(void, glBindTexture, GLenum target, GLuint texture) -GL_ENTRY(void, glBlendColor, GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) -GL_ENTRY(void, glBlendEquation, GLenum mode) -GL_ENTRY(void, glBlendEquationSeparate, GLenum modeRGB, GLenum modeAlpha) -GL_ENTRY(void, glBlendFunc, GLenum sfactor, GLenum dfactor) -GL_ENTRY(void, glBlendFuncSeparate, GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha) -GL_ENTRY(void, glBufferData, GLenum target, GLsizeiptr size, const void *data, GLenum usage) -GL_ENTRY(void, glBufferSubData, GLenum target, GLintptr offset, GLsizeiptr size, const void *data) -GL_ENTRY(GLenum, glCheckFramebufferStatus, GLenum target) -GL_ENTRY(void, glClear, GLbitfield mask) -GL_ENTRY(void, glClearColor, GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) -GL_ENTRY(void, glClearDepthf, GLfloat d) -GL_ENTRY(void, glClearStencil, GLint s) -GL_ENTRY(void, glColorMask, GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) -GL_ENTRY(void, glCompileShader, GLuint shader) -GL_ENTRY(void, glCompressedTexImage2D, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data) -GL_ENTRY(void, glCompressedTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data) -GL_ENTRY(void, glCopyTexImage2D, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) -GL_ENTRY(void, glCopyTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) -GL_ENTRY(GLuint, glCreateProgram, void) -GL_ENTRY(GLuint, glCreateShader, GLenum type) -GL_ENTRY(void, glCullFace, GLenum mode) -GL_ENTRY(void, glDeleteBuffers, GLsizei n, const GLuint *buffers) -GL_ENTRY(void, glDeleteFramebuffers, GLsizei n, const GLuint *framebuffers) -GL_ENTRY(void, glDeleteProgram, GLuint program) -GL_ENTRY(void, glDeleteRenderbuffers, GLsizei n, const GLuint *renderbuffers) -GL_ENTRY(void, glDeleteShader, GLuint shader) -GL_ENTRY(void, glDeleteTextures, GLsizei n, const GLuint *textures) -GL_ENTRY(void, glDepthFunc, GLenum func) -GL_ENTRY(void, glDepthMask, GLboolean flag) -GL_ENTRY(void, glDepthRangef, GLfloat n, GLfloat f) -GL_ENTRY(void, glDetachShader, GLuint program, GLuint shader) -GL_ENTRY(void, glDisable, GLenum cap) -GL_ENTRY(void, glDisableVertexAttribArray, GLuint index) -GL_ENTRY(void, glDrawArrays, GLenum mode, GLint first, GLsizei count) -GL_ENTRY(void, glDrawElements, GLenum mode, GLsizei count, GLenum type, const void *indices) -GL_ENTRY(void, glEnable, GLenum cap) -GL_ENTRY(void, glEnableVertexAttribArray, GLuint index) -GL_ENTRY(void, glFinish, void) -GL_ENTRY(void, glFlush, void) -GL_ENTRY(void, glFramebufferRenderbuffer, GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) -GL_ENTRY(void, glFramebufferTexture2D, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) -GL_ENTRY(void, glFrontFace, GLenum mode) -GL_ENTRY(void, glGenBuffers, GLsizei n, GLuint *buffers) -GL_ENTRY(void, glGenerateMipmap, GLenum target) -GL_ENTRY(void, glGenFramebuffers, GLsizei n, GLuint *framebuffers) -GL_ENTRY(void, glGenRenderbuffers, GLsizei n, GLuint *renderbuffers) -GL_ENTRY(void, glGenTextures, GLsizei n, GLuint *textures) -GL_ENTRY(void, glGetActiveAttrib, GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name) -GL_ENTRY(void, glGetActiveUniform, GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name) -GL_ENTRY(void, glGetAttachedShaders, GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders) -GL_ENTRY(GLint, glGetAttribLocation, GLuint program, const GLchar *name) -GL_ENTRY(void, glGetBooleanv, GLenum pname, GLboolean *data) -GL_ENTRY(void, glGetBufferParameteriv, GLenum target, GLenum pname, GLint *params) -GL_ENTRY(GLenum, glGetError, void) -GL_ENTRY(void, glGetFloatv, GLenum pname, GLfloat *data) -GL_ENTRY(void, glGetFramebufferAttachmentParameteriv, GLenum target, GLenum attachment, GLenum pname, GLint *params) -GL_ENTRY(void, glGetIntegerv, GLenum pname, GLint *data) -GL_ENTRY(void, glGetProgramiv, GLuint program, GLenum pname, GLint *params) -GL_ENTRY(void, glGetProgramInfoLog, GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog) -GL_ENTRY(void, glGetRenderbufferParameteriv, GLenum target, GLenum pname, GLint *params) -GL_ENTRY(void, glGetShaderiv, GLuint shader, GLenum pname, GLint *params) -GL_ENTRY(void, glGetShaderInfoLog, GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog) -GL_ENTRY(void, glGetShaderPrecisionFormat, GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision) -GL_ENTRY(void, glGetShaderSource, GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source) -GL_ENTRY(const GLubyte *, glGetString, GLenum name) -GL_ENTRY(void, glGetTexParameterfv, GLenum target, GLenum pname, GLfloat *params) -GL_ENTRY(void, glGetTexParameteriv, GLenum target, GLenum pname, GLint *params) -GL_ENTRY(void, glGetUniformfv, GLuint program, GLint location, GLfloat *params) -GL_ENTRY(void, glGetUniformiv, GLuint program, GLint location, GLint *params) -GL_ENTRY(GLint, glGetUniformLocation, GLuint program, const GLchar *name) -GL_ENTRY(void, glGetVertexAttribfv, GLuint index, GLenum pname, GLfloat *params) -GL_ENTRY(void, glGetVertexAttribiv, GLuint index, GLenum pname, GLint *params) -GL_ENTRY(void, glGetVertexAttribPointerv, GLuint index, GLenum pname, void **pointer) -GL_ENTRY(void, glHint, GLenum target, GLenum mode) -GL_ENTRY(GLboolean, glIsBuffer, GLuint buffer) -GL_ENTRY(GLboolean, glIsEnabled, GLenum cap) -GL_ENTRY(GLboolean, glIsFramebuffer, GLuint framebuffer) -GL_ENTRY(GLboolean, glIsProgram, GLuint program) -GL_ENTRY(GLboolean, glIsRenderbuffer, GLuint renderbuffer) -GL_ENTRY(GLboolean, glIsShader, GLuint shader) -GL_ENTRY(GLboolean, glIsTexture, GLuint texture) -GL_ENTRY(void, glLineWidth, GLfloat width) -GL_ENTRY(void, glLinkProgram, GLuint program) -GL_ENTRY(void, glPixelStorei, GLenum pname, GLint param) -GL_ENTRY(void, glPolygonOffset, GLfloat factor, GLfloat units) -GL_ENTRY(void, glReadPixels, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels) -GL_ENTRY(void, glReleaseShaderCompiler, void) -GL_ENTRY(void, glRenderbufferStorage, GLenum target, GLenum internalformat, GLsizei width, GLsizei height) -GL_ENTRY(void, glSampleCoverage, GLfloat value, GLboolean invert) -GL_ENTRY(void, glScissor, GLint x, GLint y, GLsizei width, GLsizei height) -GL_ENTRY(void, glShaderBinary, GLsizei count, const GLuint *shaders, GLenum binaryformat, const void *binary, GLsizei length) -GL_ENTRY(void, glShaderSource, GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length) -GL_ENTRY(void, glStencilFunc, GLenum func, GLint ref, GLuint mask) -GL_ENTRY(void, glStencilFuncSeparate, GLenum face, GLenum func, GLint ref, GLuint mask) -GL_ENTRY(void, glStencilMask, GLuint mask) -GL_ENTRY(void, glStencilMaskSeparate, GLenum face, GLuint mask) -GL_ENTRY(void, glStencilOp, GLenum fail, GLenum zfail, GLenum zpass) -GL_ENTRY(void, glStencilOpSeparate, GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass) -GL_ENTRY(void, glTexImage2D, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels) -GL_ENTRY(void, glTexParameterf, GLenum target, GLenum pname, GLfloat param) -GL_ENTRY(void, glTexParameterfv, GLenum target, GLenum pname, const GLfloat *params) -GL_ENTRY(void, glTexParameteri, GLenum target, GLenum pname, GLint param) -GL_ENTRY(void, glTexParameteriv, GLenum target, GLenum pname, const GLint *params) -GL_ENTRY(void, glTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels) -GL_ENTRY(void, glUniform1f, GLint location, GLfloat v0) -GL_ENTRY(void, glUniform1fv, GLint location, GLsizei count, const GLfloat *value) -GL_ENTRY(void, glUniform1i, GLint location, GLint v0) -GL_ENTRY(void, glUniform1iv, GLint location, GLsizei count, const GLint *value) -GL_ENTRY(void, glUniform2f, GLint location, GLfloat v0, GLfloat v1) -GL_ENTRY(void, glUniform2fv, GLint location, GLsizei count, const GLfloat *value) -GL_ENTRY(void, glUniform2i, GLint location, GLint v0, GLint v1) -GL_ENTRY(void, glUniform2iv, GLint location, GLsizei count, const GLint *value) -GL_ENTRY(void, glUniform3f, GLint location, GLfloat v0, GLfloat v1, GLfloat v2) -GL_ENTRY(void, glUniform3fv, GLint location, GLsizei count, const GLfloat *value) -GL_ENTRY(void, glUniform3i, GLint location, GLint v0, GLint v1, GLint v2) -GL_ENTRY(void, glUniform3iv, GLint location, GLsizei count, const GLint *value) -GL_ENTRY(void, glUniform4f, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) -GL_ENTRY(void, glUniform4fv, GLint location, GLsizei count, const GLfloat *value) -GL_ENTRY(void, glUniform4i, GLint location, GLint v0, GLint v1, GLint v2, GLint v3) -GL_ENTRY(void, glUniform4iv, GLint location, GLsizei count, const GLint *value) -GL_ENTRY(void, glUniformMatrix2fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glUniformMatrix3fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glUniformMatrix4fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glUseProgram, GLuint program) -GL_ENTRY(void, glValidateProgram, GLuint program) -GL_ENTRY(void, glVertexAttrib1f, GLuint index, GLfloat x) -GL_ENTRY(void, glVertexAttrib1fv, GLuint index, const GLfloat *v) -GL_ENTRY(void, glVertexAttrib2f, GLuint index, GLfloat x, GLfloat y) -GL_ENTRY(void, glVertexAttrib2fv, GLuint index, const GLfloat *v) -GL_ENTRY(void, glVertexAttrib3f, GLuint index, GLfloat x, GLfloat y, GLfloat z) -GL_ENTRY(void, glVertexAttrib3fv, GLuint index, const GLfloat *v) -GL_ENTRY(void, glVertexAttrib4f, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w) -GL_ENTRY(void, glVertexAttrib4fv, GLuint index, const GLfloat *v) -GL_ENTRY(void, glVertexAttribPointer, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer) -GL_ENTRY(void, glViewport, GLint x, GLint y, GLsizei width, GLsizei height) -GL_ENTRY(void, glReadBuffer, GLenum src) -GL_ENTRY(void, glDrawRangeElements, GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices) -GL_ENTRY(void, glTexImage3D, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels) -GL_ENTRY(void, glTexSubImage3D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels) -GL_ENTRY(void, glCopyTexSubImage3D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height) -GL_ENTRY(void, glCompressedTexImage3D, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data) -GL_ENTRY(void, glCompressedTexSubImage3D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data) -GL_ENTRY(void, glGenQueries, GLsizei n, GLuint *ids) -GL_ENTRY(void, glDeleteQueries, GLsizei n, const GLuint *ids) -GL_ENTRY(GLboolean, glIsQuery, GLuint id) -GL_ENTRY(void, glBeginQuery, GLenum target, GLuint id) -GL_ENTRY(void, glEndQuery, GLenum target) -GL_ENTRY(void, glGetQueryiv, GLenum target, GLenum pname, GLint *params) -GL_ENTRY(void, glGetQueryObjectuiv, GLuint id, GLenum pname, GLuint *params) -GL_ENTRY(GLboolean, glUnmapBuffer, GLenum target) -GL_ENTRY(void, glGetBufferPointerv, GLenum target, GLenum pname, void **params) -GL_ENTRY(void, glDrawBuffers, GLsizei n, const GLenum *bufs) -GL_ENTRY(void, glUniformMatrix2x3fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glUniformMatrix3x2fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glUniformMatrix2x4fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glUniformMatrix4x2fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glUniformMatrix3x4fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glUniformMatrix4x3fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glBlitFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) -GL_ENTRY(void, glRenderbufferStorageMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) -GL_ENTRY(void, glFramebufferTextureLayer, GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer) -GL_ENTRY(void *, glMapBufferRange, GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) -GL_ENTRY(void, glFlushMappedBufferRange, GLenum target, GLintptr offset, GLsizeiptr length) -GL_ENTRY(void, glBindVertexArray, GLuint array) -GL_ENTRY(void, glDeleteVertexArrays, GLsizei n, const GLuint *arrays) -GL_ENTRY(void, glGenVertexArrays, GLsizei n, GLuint *arrays) -GL_ENTRY(GLboolean, glIsVertexArray, GLuint array) -GL_ENTRY(void, glGetIntegeri_v, GLenum target, GLuint index, GLint *data) -GL_ENTRY(void, glBeginTransformFeedback, GLenum primitiveMode) -GL_ENTRY(void, glEndTransformFeedback, void) -GL_ENTRY(void, glBindBufferRange, GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size) -GL_ENTRY(void, glBindBufferBase, GLenum target, GLuint index, GLuint buffer) -GL_ENTRY(void, glTransformFeedbackVaryings, GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode) -GL_ENTRY(void, glGetTransformFeedbackVarying, GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name) -GL_ENTRY(void, glVertexAttribIPointer, GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer) -GL_ENTRY(void, glGetVertexAttribIiv, GLuint index, GLenum pname, GLint *params) -GL_ENTRY(void, glGetVertexAttribIuiv, GLuint index, GLenum pname, GLuint *params) -GL_ENTRY(void, glVertexAttribI4i, GLuint index, GLint x, GLint y, GLint z, GLint w) -GL_ENTRY(void, glVertexAttribI4ui, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w) -GL_ENTRY(void, glVertexAttribI4iv, GLuint index, const GLint *v) -GL_ENTRY(void, glVertexAttribI4uiv, GLuint index, const GLuint *v) -GL_ENTRY(void, glGetUniformuiv, GLuint program, GLint location, GLuint *params) -GL_ENTRY(GLint, glGetFragDataLocation, GLuint program, const GLchar *name) -GL_ENTRY(void, glUniform1ui, GLint location, GLuint v0) -GL_ENTRY(void, glUniform2ui, GLint location, GLuint v0, GLuint v1) -GL_ENTRY(void, glUniform3ui, GLint location, GLuint v0, GLuint v1, GLuint v2) -GL_ENTRY(void, glUniform4ui, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) -GL_ENTRY(void, glUniform1uiv, GLint location, GLsizei count, const GLuint *value) -GL_ENTRY(void, glUniform2uiv, GLint location, GLsizei count, const GLuint *value) -GL_ENTRY(void, glUniform3uiv, GLint location, GLsizei count, const GLuint *value) -GL_ENTRY(void, glUniform4uiv, GLint location, GLsizei count, const GLuint *value) -GL_ENTRY(void, glClearBufferiv, GLenum buffer, GLint drawbuffer, const GLint *value) -GL_ENTRY(void, glClearBufferuiv, GLenum buffer, GLint drawbuffer, const GLuint *value) -GL_ENTRY(void, glClearBufferfv, GLenum buffer, GLint drawbuffer, const GLfloat *value) -GL_ENTRY(void, glClearBufferfi, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) -GL_ENTRY(const GLubyte *, glGetStringi, GLenum name, GLuint index) -GL_ENTRY(void, glCopyBufferSubData, GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size) -GL_ENTRY(void, glGetUniformIndices, GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices) -GL_ENTRY(void, glGetActiveUniformsiv, GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params) -GL_ENTRY(GLuint, glGetUniformBlockIndex, GLuint program, const GLchar *uniformBlockName) -GL_ENTRY(void, glGetActiveUniformBlockiv, GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params) -GL_ENTRY(void, glGetActiveUniformBlockName, GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName) -GL_ENTRY(void, glUniformBlockBinding, GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding) -GL_ENTRY(void, glDrawArraysInstanced, GLenum mode, GLint first, GLsizei count, GLsizei instancecount) -GL_ENTRY(void, glDrawElementsInstanced, GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount) -GL_ENTRY(GLsync, glFenceSync, GLenum condition, GLbitfield flags) -GL_ENTRY(GLboolean, glIsSync, GLsync sync) -GL_ENTRY(void, glDeleteSync, GLsync sync) -GL_ENTRY(GLenum, glClientWaitSync, GLsync sync, GLbitfield flags, GLuint64 timeout) -GL_ENTRY(void, glWaitSync, GLsync sync, GLbitfield flags, GLuint64 timeout) -GL_ENTRY(void, glGetInteger64v, GLenum pname, GLint64 *data) -GL_ENTRY(void, glGetSynciv, GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values) -GL_ENTRY(void, glGetInteger64i_v, GLenum target, GLuint index, GLint64 *data) -GL_ENTRY(void, glGetBufferParameteri64v, GLenum target, GLenum pname, GLint64 *params) -GL_ENTRY(void, glGenSamplers, GLsizei count, GLuint *samplers) -GL_ENTRY(void, glDeleteSamplers, GLsizei count, const GLuint *samplers) -GL_ENTRY(GLboolean, glIsSampler, GLuint sampler) -GL_ENTRY(void, glBindSampler, GLuint unit, GLuint sampler) -GL_ENTRY(void, glSamplerParameteri, GLuint sampler, GLenum pname, GLint param) -GL_ENTRY(void, glSamplerParameteriv, GLuint sampler, GLenum pname, const GLint *param) -GL_ENTRY(void, glSamplerParameterf, GLuint sampler, GLenum pname, GLfloat param) -GL_ENTRY(void, glSamplerParameterfv, GLuint sampler, GLenum pname, const GLfloat *param) -GL_ENTRY(void, glGetSamplerParameteriv, GLuint sampler, GLenum pname, GLint *params) -GL_ENTRY(void, glGetSamplerParameterfv, GLuint sampler, GLenum pname, GLfloat *params) -GL_ENTRY(void, glVertexAttribDivisor, GLuint index, GLuint divisor) -GL_ENTRY(void, glBindTransformFeedback, GLenum target, GLuint id) -GL_ENTRY(void, glDeleteTransformFeedbacks, GLsizei n, const GLuint *ids) -GL_ENTRY(void, glGenTransformFeedbacks, GLsizei n, GLuint *ids) -GL_ENTRY(GLboolean, glIsTransformFeedback, GLuint id) -GL_ENTRY(void, glPauseTransformFeedback, void) -GL_ENTRY(void, glResumeTransformFeedback, void) -GL_ENTRY(void, glGetProgramBinary, GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary) -GL_ENTRY(void, glProgramBinary, GLuint program, GLenum binaryFormat, const void *binary, GLsizei length) -GL_ENTRY(void, glProgramParameteri, GLuint program, GLenum pname, GLint value) -GL_ENTRY(void, glInvalidateFramebuffer, GLenum target, GLsizei numAttachments, const GLenum *attachments) -GL_ENTRY(void, glInvalidateSubFramebuffer, GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height) -GL_ENTRY(void, glTexStorage2D, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) -GL_ENTRY(void, glTexStorage3D, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) -GL_ENTRY(void, glGetInternalformativ, GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint *params) -GL_ENTRY(void, glDispatchCompute, GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z) -GL_ENTRY(void, glDispatchComputeIndirect, GLintptr indirect) -GL_ENTRY(void, glDrawArraysIndirect, GLenum mode, const void *indirect) -GL_ENTRY(void, glDrawElementsIndirect, GLenum mode, GLenum type, const void *indirect) -GL_ENTRY(void, glFramebufferParameteri, GLenum target, GLenum pname, GLint param) -GL_ENTRY(void, glGetFramebufferParameteriv, GLenum target, GLenum pname, GLint *params) -GL_ENTRY(void, glGetProgramInterfaceiv, GLuint program, GLenum programInterface, GLenum pname, GLint *params) -GL_ENTRY(GLuint, glGetProgramResourceIndex, GLuint program, GLenum programInterface, const GLchar *name) -GL_ENTRY(void, glGetProgramResourceName, GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name) -GL_ENTRY(void, glGetProgramResourceiv, GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLint *params) -GL_ENTRY(GLint, glGetProgramResourceLocation, GLuint program, GLenum programInterface, const GLchar *name) -GL_ENTRY(void, glUseProgramStages, GLuint pipeline, GLbitfield stages, GLuint program) -GL_ENTRY(void, glActiveShaderProgram, GLuint pipeline, GLuint program) -GL_ENTRY(GLuint, glCreateShaderProgramv, GLenum type, GLsizei count, const GLchar *const*strings) -GL_ENTRY(void, glBindProgramPipeline, GLuint pipeline) -GL_ENTRY(void, glDeleteProgramPipelines, GLsizei n, const GLuint *pipelines) -GL_ENTRY(void, glGenProgramPipelines, GLsizei n, GLuint *pipelines) -GL_ENTRY(GLboolean, glIsProgramPipeline, GLuint pipeline) -GL_ENTRY(void, glGetProgramPipelineiv, GLuint pipeline, GLenum pname, GLint *params) -GL_ENTRY(void, glProgramUniform1i, GLuint program, GLint location, GLint v0) -GL_ENTRY(void, glProgramUniform2i, GLuint program, GLint location, GLint v0, GLint v1) -GL_ENTRY(void, glProgramUniform3i, GLuint program, GLint location, GLint v0, GLint v1, GLint v2) -GL_ENTRY(void, glProgramUniform4i, GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3) -GL_ENTRY(void, glProgramUniform1ui, GLuint program, GLint location, GLuint v0) -GL_ENTRY(void, glProgramUniform2ui, GLuint program, GLint location, GLuint v0, GLuint v1) -GL_ENTRY(void, glProgramUniform3ui, GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2) -GL_ENTRY(void, glProgramUniform4ui, GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) -GL_ENTRY(void, glProgramUniform1f, GLuint program, GLint location, GLfloat v0) -GL_ENTRY(void, glProgramUniform2f, GLuint program, GLint location, GLfloat v0, GLfloat v1) -GL_ENTRY(void, glProgramUniform3f, GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2) -GL_ENTRY(void, glProgramUniform4f, GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) -GL_ENTRY(void, glProgramUniform1iv, GLuint program, GLint location, GLsizei count, const GLint *value) -GL_ENTRY(void, glProgramUniform2iv, GLuint program, GLint location, GLsizei count, const GLint *value) -GL_ENTRY(void, glProgramUniform3iv, GLuint program, GLint location, GLsizei count, const GLint *value) -GL_ENTRY(void, glProgramUniform4iv, GLuint program, GLint location, GLsizei count, const GLint *value) -GL_ENTRY(void, glProgramUniform1uiv, GLuint program, GLint location, GLsizei count, const GLuint *value) -GL_ENTRY(void, glProgramUniform2uiv, GLuint program, GLint location, GLsizei count, const GLuint *value) -GL_ENTRY(void, glProgramUniform3uiv, GLuint program, GLint location, GLsizei count, const GLuint *value) -GL_ENTRY(void, glProgramUniform4uiv, GLuint program, GLint location, GLsizei count, const GLuint *value) -GL_ENTRY(void, glProgramUniform1fv, GLuint program, GLint location, GLsizei count, const GLfloat *value) -GL_ENTRY(void, glProgramUniform2fv, GLuint program, GLint location, GLsizei count, const GLfloat *value) -GL_ENTRY(void, glProgramUniform3fv, GLuint program, GLint location, GLsizei count, const GLfloat *value) -GL_ENTRY(void, glProgramUniform4fv, GLuint program, GLint location, GLsizei count, const GLfloat *value) -GL_ENTRY(void, glProgramUniformMatrix2fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glProgramUniformMatrix3fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glProgramUniformMatrix4fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glProgramUniformMatrix2x3fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glProgramUniformMatrix3x2fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glProgramUniformMatrix2x4fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glProgramUniformMatrix4x2fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glProgramUniformMatrix3x4fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glProgramUniformMatrix4x3fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glValidateProgramPipeline, GLuint pipeline) -GL_ENTRY(void, glGetProgramPipelineInfoLog, GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog) -GL_ENTRY(void, glBindImageTexture, GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format) -GL_ENTRY(void, glGetBooleani_v, GLenum target, GLuint index, GLboolean *data) -GL_ENTRY(void, glMemoryBarrier, GLbitfield barriers) -GL_ENTRY(void, glMemoryBarrierByRegion, GLbitfield barriers) -GL_ENTRY(void, glTexStorage2DMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations) -GL_ENTRY(void, glGetMultisamplefv, GLenum pname, GLuint index, GLfloat *val) -GL_ENTRY(void, glSampleMaski, GLuint maskNumber, GLbitfield mask) -GL_ENTRY(void, glGetTexLevelParameteriv, GLenum target, GLint level, GLenum pname, GLint *params) -GL_ENTRY(void, glGetTexLevelParameterfv, GLenum target, GLint level, GLenum pname, GLfloat *params) -GL_ENTRY(void, glBindVertexBuffer, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride) -GL_ENTRY(void, glVertexAttribFormat, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset) -GL_ENTRY(void, glVertexAttribIFormat, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) -GL_ENTRY(void, glVertexAttribBinding, GLuint attribindex, GLuint bindingindex) -GL_ENTRY(void, glVertexBindingDivisor, GLuint bindingindex, GLuint divisor) -GL_ENTRY(void, glBlendBarrier, void) -GL_ENTRY(void, glCopyImageSubData, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth) -GL_ENTRY(void, glDebugMessageControl, GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled) -GL_ENTRY(void, glDebugMessageInsert, GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf) -GL_ENTRY(void, glDebugMessageCallback, GLDEBUGPROC callback, const void *userParam) -GL_ENTRY(GLuint, glGetDebugMessageLog, GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog) -GL_ENTRY(void, glPushDebugGroup, GLenum source, GLuint id, GLsizei length, const GLchar *message) -GL_ENTRY(void, glPopDebugGroup, void) -GL_ENTRY(void, glObjectLabel, GLenum identifier, GLuint name, GLsizei length, const GLchar *label) -GL_ENTRY(void, glGetObjectLabel, GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label) -GL_ENTRY(void, glObjectPtrLabel, const void *ptr, GLsizei length, const GLchar *label) -GL_ENTRY(void, glGetObjectPtrLabel, const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label) -GL_ENTRY(void, glGetPointerv, GLenum pname, void **params) -GL_ENTRY(void, glEnablei, GLenum target, GLuint index) -GL_ENTRY(void, glDisablei, GLenum target, GLuint index) -GL_ENTRY(void, glBlendEquationi, GLuint buf, GLenum mode) -GL_ENTRY(void, glBlendEquationSeparatei, GLuint buf, GLenum modeRGB, GLenum modeAlpha) -GL_ENTRY(void, glBlendFunci, GLuint buf, GLenum src, GLenum dst) -GL_ENTRY(void, glBlendFuncSeparatei, GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) -GL_ENTRY(void, glColorMaski, GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a) -GL_ENTRY(GLboolean, glIsEnabledi, GLenum target, GLuint index) -GL_ENTRY(void, glDrawElementsBaseVertex, GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex) -GL_ENTRY(void, glDrawRangeElementsBaseVertex, GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex) -GL_ENTRY(void, glDrawElementsInstancedBaseVertex, GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex) -GL_ENTRY(void, glFramebufferTexture, GLenum target, GLenum attachment, GLuint texture, GLint level) -GL_ENTRY(void, glPrimitiveBoundingBox, GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW) -GL_ENTRY(GLenum, glGetGraphicsResetStatus, void) -GL_ENTRY(void, glReadnPixels, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data) -GL_ENTRY(void, glGetnUniformfv, GLuint program, GLint location, GLsizei bufSize, GLfloat *params) -GL_ENTRY(void, glGetnUniformiv, GLuint program, GLint location, GLsizei bufSize, GLint *params) -GL_ENTRY(void, glGetnUniformuiv, GLuint program, GLint location, GLsizei bufSize, GLuint *params) -GL_ENTRY(void, glMinSampleShading, GLfloat value) -GL_ENTRY(void, glPatchParameteri, GLenum pname, GLint value) -GL_ENTRY(void, glTexParameterIiv, GLenum target, GLenum pname, const GLint *params) -GL_ENTRY(void, glTexParameterIuiv, GLenum target, GLenum pname, const GLuint *params) -GL_ENTRY(void, glGetTexParameterIiv, GLenum target, GLenum pname, GLint *params) -GL_ENTRY(void, glGetTexParameterIuiv, GLenum target, GLenum pname, GLuint *params) -GL_ENTRY(void, glSamplerParameterIiv, GLuint sampler, GLenum pname, const GLint *param) -GL_ENTRY(void, glSamplerParameterIuiv, GLuint sampler, GLenum pname, const GLuint *param) -GL_ENTRY(void, glGetSamplerParameterIiv, GLuint sampler, GLenum pname, GLint *params) -GL_ENTRY(void, glGetSamplerParameterIuiv, GLuint sampler, GLenum pname, GLuint *params) -GL_ENTRY(void, glTexBuffer, GLenum target, GLenum internalformat, GLuint buffer) -GL_ENTRY(void, glTexBufferRange, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size) -GL_ENTRY(void, glTexStorage3DMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations) -GL_ENTRY(void, glBlendBarrierKHR, void) -GL_ENTRY(void, glDebugMessageControlKHR, GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled) -GL_ENTRY(void, glDebugMessageInsertKHR, GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf) -GL_ENTRY(void, glDebugMessageCallbackKHR, GLDEBUGPROCKHR callback, const void *userParam) -GL_ENTRY(GLuint, glGetDebugMessageLogKHR, GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog) -GL_ENTRY(void, glPushDebugGroupKHR, GLenum source, GLuint id, GLsizei length, const GLchar *message) -GL_ENTRY(void, glPopDebugGroupKHR, void) -GL_ENTRY(void, glObjectLabelKHR, GLenum identifier, GLuint name, GLsizei length, const GLchar *label) -GL_ENTRY(void, glGetObjectLabelKHR, GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label) -GL_ENTRY(void, glObjectPtrLabelKHR, const void *ptr, GLsizei length, const GLchar *label) -GL_ENTRY(void, glGetObjectPtrLabelKHR, const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label) -GL_ENTRY(void, glGetPointervKHR, GLenum pname, void **params) -GL_ENTRY(GLenum, glGetGraphicsResetStatusKHR, void) -GL_ENTRY(void, glReadnPixelsKHR, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data) -GL_ENTRY(void, glGetnUniformfvKHR, GLuint program, GLint location, GLsizei bufSize, GLfloat *params) -GL_ENTRY(void, glGetnUniformivKHR, GLuint program, GLint location, GLsizei bufSize, GLint *params) -GL_ENTRY(void, glGetnUniformuivKHR, GLuint program, GLint location, GLsizei bufSize, GLuint *params) -GL_ENTRY(void, glEGLImageTargetTexture2DOES, GLenum target, GLeglImageOES image) -GL_ENTRY(void, glEGLImageTargetRenderbufferStorageOES, GLenum target, GLeglImageOES image) -GL_ENTRY(void, glCopyImageSubDataOES, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth) -GL_ENTRY(void, glEnableiOES, GLenum target, GLuint index) -GL_ENTRY(void, glDisableiOES, GLenum target, GLuint index) -GL_ENTRY(void, glBlendEquationiOES, GLuint buf, GLenum mode) -GL_ENTRY(void, glBlendEquationSeparateiOES, GLuint buf, GLenum modeRGB, GLenum modeAlpha) -GL_ENTRY(void, glBlendFunciOES, GLuint buf, GLenum src, GLenum dst) -GL_ENTRY(void, glBlendFuncSeparateiOES, GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) -GL_ENTRY(void, glColorMaskiOES, GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a) -GL_ENTRY(GLboolean, glIsEnablediOES, GLenum target, GLuint index) -GL_ENTRY(void, glDrawElementsBaseVertexOES, GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex) -GL_ENTRY(void, glDrawRangeElementsBaseVertexOES, GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex) -GL_ENTRY(void, glDrawElementsInstancedBaseVertexOES, GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex) -GL_ENTRY(void, glFramebufferTextureOES, GLenum target, GLenum attachment, GLuint texture, GLint level) -GL_ENTRY(void, glGetProgramBinaryOES, GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary) -GL_ENTRY(void, glProgramBinaryOES, GLuint program, GLenum binaryFormat, const void *binary, GLint length) -GL_ENTRY(void *, glMapBufferOES, GLenum target, GLenum access) -GL_ENTRY(GLboolean, glUnmapBufferOES, GLenum target) -GL_ENTRY(void, glGetBufferPointervOES, GLenum target, GLenum pname, void **params) -GL_ENTRY(void, glPrimitiveBoundingBoxOES, GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW) -GL_ENTRY(void, glMinSampleShadingOES, GLfloat value) -GL_ENTRY(void, glPatchParameteriOES, GLenum pname, GLint value) -GL_ENTRY(void, glTexImage3DOES, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels) -GL_ENTRY(void, glTexSubImage3DOES, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels) -GL_ENTRY(void, glCopyTexSubImage3DOES, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height) -GL_ENTRY(void, glCompressedTexImage3DOES, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data) -GL_ENTRY(void, glCompressedTexSubImage3DOES, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data) -GL_ENTRY(void, glFramebufferTexture3DOES, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset) -GL_ENTRY(void, glTexParameterIivOES, GLenum target, GLenum pname, const GLint *params) -GL_ENTRY(void, glTexParameterIuivOES, GLenum target, GLenum pname, const GLuint *params) -GL_ENTRY(void, glGetTexParameterIivOES, GLenum target, GLenum pname, GLint *params) -GL_ENTRY(void, glGetTexParameterIuivOES, GLenum target, GLenum pname, GLuint *params) -GL_ENTRY(void, glSamplerParameterIivOES, GLuint sampler, GLenum pname, const GLint *param) -GL_ENTRY(void, glSamplerParameterIuivOES, GLuint sampler, GLenum pname, const GLuint *param) -GL_ENTRY(void, glGetSamplerParameterIivOES, GLuint sampler, GLenum pname, GLint *params) -GL_ENTRY(void, glGetSamplerParameterIuivOES, GLuint sampler, GLenum pname, GLuint *params) -GL_ENTRY(void, glTexBufferOES, GLenum target, GLenum internalformat, GLuint buffer) -GL_ENTRY(void, glTexBufferRangeOES, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size) -GL_ENTRY(void, glTexStorage3DMultisampleOES, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations) -GL_ENTRY(void, glTextureViewOES, GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers) -GL_ENTRY(void, glBindVertexArrayOES, GLuint array) -GL_ENTRY(void, glDeleteVertexArraysOES, GLsizei n, const GLuint *arrays) -GL_ENTRY(void, glGenVertexArraysOES, GLsizei n, GLuint *arrays) -GL_ENTRY(GLboolean, glIsVertexArrayOES, GLuint array) -GL_ENTRY(void, glDrawArraysInstancedBaseInstanceEXT, GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance) -GL_ENTRY(void, glDrawElementsInstancedBaseInstanceEXT, GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLuint baseinstance) -GL_ENTRY(void, glDrawElementsInstancedBaseVertexBaseInstanceEXT, GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance) -GL_ENTRY(void, glBindFragDataLocationIndexedEXT, GLuint program, GLuint colorNumber, GLuint index, const GLchar *name) -GL_ENTRY(void, glBindFragDataLocationEXT, GLuint program, GLuint color, const GLchar *name) -GL_ENTRY(GLint, glGetProgramResourceLocationIndexEXT, GLuint program, GLenum programInterface, const GLchar *name) -GL_ENTRY(GLint, glGetFragDataIndexEXT, GLuint program, const GLchar *name) -GL_ENTRY(void, glBufferStorageEXT, GLenum target, GLsizeiptr size, const void *data, GLbitfield flags) -GL_ENTRY(void, glCopyImageSubDataEXT, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth) -GL_ENTRY(void, glLabelObjectEXT, GLenum type, GLuint object, GLsizei length, const GLchar *label) -GL_ENTRY(void, glGetObjectLabelEXT, GLenum type, GLuint object, GLsizei bufSize, GLsizei *length, GLchar *label) -GL_ENTRY(void, glInsertEventMarkerEXT, GLsizei length, const GLchar *marker) -GL_ENTRY(void, glPushGroupMarkerEXT, GLsizei length, const GLchar *marker) -GL_ENTRY(void, glPopGroupMarkerEXT, void) -GL_ENTRY(void, glDiscardFramebufferEXT, GLenum target, GLsizei numAttachments, const GLenum *attachments) -GL_ENTRY(void, glGenQueriesEXT, GLsizei n, GLuint *ids) -GL_ENTRY(void, glDeleteQueriesEXT, GLsizei n, const GLuint *ids) -GL_ENTRY(GLboolean, glIsQueryEXT, GLuint id) -GL_ENTRY(void, glBeginQueryEXT, GLenum target, GLuint id) -GL_ENTRY(void, glEndQueryEXT, GLenum target) -GL_ENTRY(void, glQueryCounterEXT, GLuint id, GLenum target) -GL_ENTRY(void, glGetQueryivEXT, GLenum target, GLenum pname, GLint *params) -GL_ENTRY(void, glGetQueryObjectivEXT, GLuint id, GLenum pname, GLint *params) -GL_ENTRY(void, glGetQueryObjectuivEXT, GLuint id, GLenum pname, GLuint *params) -GL_ENTRY(void, glGetQueryObjecti64vEXT, GLuint id, GLenum pname, GLint64 *params) -GL_ENTRY(void, glGetQueryObjectui64vEXT, GLuint id, GLenum pname, GLuint64 *params) -GL_ENTRY(void, glDrawBuffersEXT, GLsizei n, const GLenum *bufs) -GL_ENTRY(void, glEnableiEXT, GLenum target, GLuint index) -GL_ENTRY(void, glDisableiEXT, GLenum target, GLuint index) -GL_ENTRY(void, glBlendEquationiEXT, GLuint buf, GLenum mode) -GL_ENTRY(void, glBlendEquationSeparateiEXT, GLuint buf, GLenum modeRGB, GLenum modeAlpha) -GL_ENTRY(void, glBlendFunciEXT, GLuint buf, GLenum src, GLenum dst) -GL_ENTRY(void, glBlendFuncSeparateiEXT, GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) -GL_ENTRY(void, glColorMaskiEXT, GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a) -GL_ENTRY(GLboolean, glIsEnablediEXT, GLenum target, GLuint index) -GL_ENTRY(void, glDrawElementsBaseVertexEXT, GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex) -GL_ENTRY(void, glDrawRangeElementsBaseVertexEXT, GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex) -GL_ENTRY(void, glDrawElementsInstancedBaseVertexEXT, GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex) -GL_ENTRY(void, glMultiDrawElementsBaseVertexEXT, GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, const GLint *basevertex) -GL_ENTRY(void, glDrawArraysInstancedEXT, GLenum mode, GLint start, GLsizei count, GLsizei primcount) -GL_ENTRY(void, glDrawElementsInstancedEXT, GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount) -GL_ENTRY(void, glFramebufferTextureEXT, GLenum target, GLenum attachment, GLuint texture, GLint level) -GL_ENTRY(void, glVertexAttribDivisorEXT, GLuint index, GLuint divisor) -GL_ENTRY(void *, glMapBufferRangeEXT, GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) -GL_ENTRY(void, glFlushMappedBufferRangeEXT, GLenum target, GLintptr offset, GLsizeiptr length) -GL_ENTRY(void, glMultiDrawArraysEXT, GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount) -GL_ENTRY(void, glMultiDrawElementsEXT, GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount) -GL_ENTRY(void, glMultiDrawArraysIndirectEXT, GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride) -GL_ENTRY(void, glMultiDrawElementsIndirectEXT, GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride) -GL_ENTRY(void, glRenderbufferStorageMultisampleEXT, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) -GL_ENTRY(void, glFramebufferTexture2DMultisampleEXT, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples) -GL_ENTRY(void, glReadBufferIndexedEXT, GLenum src, GLint index) -GL_ENTRY(void, glDrawBuffersIndexedEXT, GLint n, const GLenum *location, const GLint *indices) -GL_ENTRY(void, glGetIntegeri_vEXT, GLenum target, GLuint index, GLint *data) -GL_ENTRY(void, glPrimitiveBoundingBoxEXT, GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW) -GL_ENTRY(void, glRasterSamplesEXT, GLuint samples, GLboolean fixedsamplelocations) -GL_ENTRY(GLenum, glGetGraphicsResetStatusEXT, void) -GL_ENTRY(void, glReadnPixelsEXT, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data) -GL_ENTRY(void, glGetnUniformfvEXT, GLuint program, GLint location, GLsizei bufSize, GLfloat *params) -GL_ENTRY(void, glGetnUniformivEXT, GLuint program, GLint location, GLsizei bufSize, GLint *params) -GL_ENTRY(void, glActiveShaderProgramEXT, GLuint pipeline, GLuint program) -GL_ENTRY(void, glBindProgramPipelineEXT, GLuint pipeline) -GL_ENTRY(GLuint, glCreateShaderProgramvEXT, GLenum type, GLsizei count, const GLchar **strings) -GL_ENTRY(void, glDeleteProgramPipelinesEXT, GLsizei n, const GLuint *pipelines) -GL_ENTRY(void, glGenProgramPipelinesEXT, GLsizei n, GLuint *pipelines) -GL_ENTRY(void, glGetProgramPipelineInfoLogEXT, GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog) -GL_ENTRY(void, glGetProgramPipelineivEXT, GLuint pipeline, GLenum pname, GLint *params) -GL_ENTRY(GLboolean, glIsProgramPipelineEXT, GLuint pipeline) -GL_ENTRY(void, glProgramParameteriEXT, GLuint program, GLenum pname, GLint value) -GL_ENTRY(void, glProgramUniform1fEXT, GLuint program, GLint location, GLfloat v0) -GL_ENTRY(void, glProgramUniform1fvEXT, GLuint program, GLint location, GLsizei count, const GLfloat *value) -GL_ENTRY(void, glProgramUniform1iEXT, GLuint program, GLint location, GLint v0) -GL_ENTRY(void, glProgramUniform1ivEXT, GLuint program, GLint location, GLsizei count, const GLint *value) -GL_ENTRY(void, glProgramUniform2fEXT, GLuint program, GLint location, GLfloat v0, GLfloat v1) -GL_ENTRY(void, glProgramUniform2fvEXT, GLuint program, GLint location, GLsizei count, const GLfloat *value) -GL_ENTRY(void, glProgramUniform2iEXT, GLuint program, GLint location, GLint v0, GLint v1) -GL_ENTRY(void, glProgramUniform2ivEXT, GLuint program, GLint location, GLsizei count, const GLint *value) -GL_ENTRY(void, glProgramUniform3fEXT, GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2) -GL_ENTRY(void, glProgramUniform3fvEXT, GLuint program, GLint location, GLsizei count, const GLfloat *value) -GL_ENTRY(void, glProgramUniform3iEXT, GLuint program, GLint location, GLint v0, GLint v1, GLint v2) -GL_ENTRY(void, glProgramUniform3ivEXT, GLuint program, GLint location, GLsizei count, const GLint *value) -GL_ENTRY(void, glProgramUniform4fEXT, GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) -GL_ENTRY(void, glProgramUniform4fvEXT, GLuint program, GLint location, GLsizei count, const GLfloat *value) -GL_ENTRY(void, glProgramUniform4iEXT, GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3) -GL_ENTRY(void, glProgramUniform4ivEXT, GLuint program, GLint location, GLsizei count, const GLint *value) -GL_ENTRY(void, glProgramUniformMatrix2fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glProgramUniformMatrix3fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glProgramUniformMatrix4fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glUseProgramStagesEXT, GLuint pipeline, GLbitfield stages, GLuint program) -GL_ENTRY(void, glValidateProgramPipelineEXT, GLuint pipeline) -GL_ENTRY(void, glProgramUniform1uiEXT, GLuint program, GLint location, GLuint v0) -GL_ENTRY(void, glProgramUniform2uiEXT, GLuint program, GLint location, GLuint v0, GLuint v1) -GL_ENTRY(void, glProgramUniform3uiEXT, GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2) -GL_ENTRY(void, glProgramUniform4uiEXT, GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) -GL_ENTRY(void, glProgramUniform1uivEXT, GLuint program, GLint location, GLsizei count, const GLuint *value) -GL_ENTRY(void, glProgramUniform2uivEXT, GLuint program, GLint location, GLsizei count, const GLuint *value) -GL_ENTRY(void, glProgramUniform3uivEXT, GLuint program, GLint location, GLsizei count, const GLuint *value) -GL_ENTRY(void, glProgramUniform4uivEXT, GLuint program, GLint location, GLsizei count, const GLuint *value) -GL_ENTRY(void, glProgramUniformMatrix2x3fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glProgramUniformMatrix3x2fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glProgramUniformMatrix2x4fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glProgramUniformMatrix4x2fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glProgramUniformMatrix3x4fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glProgramUniformMatrix4x3fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glTexPageCommitmentEXT, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit) -GL_ENTRY(void, glPatchParameteriEXT, GLenum pname, GLint value) -GL_ENTRY(void, glTexParameterIivEXT, GLenum target, GLenum pname, const GLint *params) -GL_ENTRY(void, glTexParameterIuivEXT, GLenum target, GLenum pname, const GLuint *params) -GL_ENTRY(void, glGetTexParameterIivEXT, GLenum target, GLenum pname, GLint *params) -GL_ENTRY(void, glGetTexParameterIuivEXT, GLenum target, GLenum pname, GLuint *params) -GL_ENTRY(void, glSamplerParameterIivEXT, GLuint sampler, GLenum pname, const GLint *param) -GL_ENTRY(void, glSamplerParameterIuivEXT, GLuint sampler, GLenum pname, const GLuint *param) -GL_ENTRY(void, glGetSamplerParameterIivEXT, GLuint sampler, GLenum pname, GLint *params) -GL_ENTRY(void, glGetSamplerParameterIuivEXT, GLuint sampler, GLenum pname, GLuint *params) -GL_ENTRY(void, glTexBufferEXT, GLenum target, GLenum internalformat, GLuint buffer) -GL_ENTRY(void, glTexBufferRangeEXT, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size) -GL_ENTRY(void, glTexStorage1DEXT, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width) -GL_ENTRY(void, glTexStorage2DEXT, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) -GL_ENTRY(void, glTexStorage3DEXT, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) -GL_ENTRY(void, glTextureStorage1DEXT, GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width) -GL_ENTRY(void, glTextureStorage2DEXT, GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) -GL_ENTRY(void, glTextureStorage3DEXT, GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) -GL_ENTRY(void, glTextureViewEXT, GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers) diff --git a/libs/hwui/debug/gles_redefine.h b/libs/hwui/debug/gles_redefine.h deleted file mode 100644 index 49b506918372..000000000000 --- a/libs/hwui/debug/gles_redefine.h +++ /dev/null @@ -1,914 +0,0 @@ -/* - * Copyright (C) 2016 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 glActiveShaderProgram wrap_glActiveShaderProgram -#define glActiveShaderProgramEXT wrap_glActiveShaderProgramEXT -#define glActiveTexture wrap_glActiveTexture -#define glAlphaFunc wrap_glAlphaFunc -#define glAlphaFuncQCOM wrap_glAlphaFuncQCOM -#define glAlphaFuncx wrap_glAlphaFuncx -#define glAlphaFuncxOES wrap_glAlphaFuncxOES -#define glApplyFramebufferAttachmentCMAAINTEL wrap_glApplyFramebufferAttachmentCMAAINTEL -#define glAttachShader wrap_glAttachShader -#define glBeginConditionalRenderNV wrap_glBeginConditionalRenderNV -#define glBeginPerfMonitorAMD wrap_glBeginPerfMonitorAMD -#define glBeginPerfQueryINTEL wrap_glBeginPerfQueryINTEL -#define glBeginQuery wrap_glBeginQuery -#define glBeginQueryEXT wrap_glBeginQueryEXT -#define glBeginTransformFeedback wrap_glBeginTransformFeedback -#define glBindAttribLocation wrap_glBindAttribLocation -#define glBindBuffer wrap_glBindBuffer -#define glBindBufferBase wrap_glBindBufferBase -#define glBindBufferRange wrap_glBindBufferRange -#define glBindFragDataLocationEXT wrap_glBindFragDataLocationEXT -#define glBindFragDataLocationIndexedEXT wrap_glBindFragDataLocationIndexedEXT -#define glBindFramebuffer wrap_glBindFramebuffer -#define glBindFramebufferOES wrap_glBindFramebufferOES -#define glBindImageTexture wrap_glBindImageTexture -#define glBindProgramPipeline wrap_glBindProgramPipeline -#define glBindProgramPipelineEXT wrap_glBindProgramPipelineEXT -#define glBindRenderbuffer wrap_glBindRenderbuffer -#define glBindRenderbufferOES wrap_glBindRenderbufferOES -#define glBindSampler wrap_glBindSampler -#define glBindTexture wrap_glBindTexture -#define glBindTransformFeedback wrap_glBindTransformFeedback -#define glBindVertexArray wrap_glBindVertexArray -#define glBindVertexArrayOES wrap_glBindVertexArrayOES -#define glBindVertexBuffer wrap_glBindVertexBuffer -#define glBlendBarrier wrap_glBlendBarrier -#define glBlendBarrierKHR wrap_glBlendBarrierKHR -#define glBlendBarrierNV wrap_glBlendBarrierNV -#define glBlendColor wrap_glBlendColor -#define glBlendEquation wrap_glBlendEquation -#define glBlendEquationOES wrap_glBlendEquationOES -#define glBlendEquationSeparate wrap_glBlendEquationSeparate -#define glBlendEquationSeparateOES wrap_glBlendEquationSeparateOES -#define glBlendEquationSeparatei wrap_glBlendEquationSeparatei -#define glBlendEquationSeparateiEXT wrap_glBlendEquationSeparateiEXT -#define glBlendEquationSeparateiOES wrap_glBlendEquationSeparateiOES -#define glBlendEquationi wrap_glBlendEquationi -#define glBlendEquationiEXT wrap_glBlendEquationiEXT -#define glBlendEquationiOES wrap_glBlendEquationiOES -#define glBlendFunc wrap_glBlendFunc -#define glBlendFuncSeparate wrap_glBlendFuncSeparate -#define glBlendFuncSeparateOES wrap_glBlendFuncSeparateOES -#define glBlendFuncSeparatei wrap_glBlendFuncSeparatei -#define glBlendFuncSeparateiEXT wrap_glBlendFuncSeparateiEXT -#define glBlendFuncSeparateiOES wrap_glBlendFuncSeparateiOES -#define glBlendFunci wrap_glBlendFunci -#define glBlendFunciEXT wrap_glBlendFunciEXT -#define glBlendFunciOES wrap_glBlendFunciOES -#define glBlendParameteriNV wrap_glBlendParameteriNV -#define glBlitFramebuffer wrap_glBlitFramebuffer -#define glBlitFramebufferANGLE wrap_glBlitFramebufferANGLE -#define glBlitFramebufferNV wrap_glBlitFramebufferNV -#define glBufferData wrap_glBufferData -#define glBufferStorageEXT wrap_glBufferStorageEXT -#define glBufferSubData wrap_glBufferSubData -#define glCheckFramebufferStatus wrap_glCheckFramebufferStatus -#define glCheckFramebufferStatusOES wrap_glCheckFramebufferStatusOES -#define glClear wrap_glClear -#define glClearBufferfi wrap_glClearBufferfi -#define glClearBufferfv wrap_glClearBufferfv -#define glClearBufferiv wrap_glClearBufferiv -#define glClearBufferuiv wrap_glClearBufferuiv -#define glClearColor wrap_glClearColor -#define glClearColorx wrap_glClearColorx -#define glClearColorxOES wrap_glClearColorxOES -#define glClearDepthf wrap_glClearDepthf -#define glClearDepthfOES wrap_glClearDepthfOES -#define glClearDepthx wrap_glClearDepthx -#define glClearDepthxOES wrap_glClearDepthxOES -#define glClearStencil wrap_glClearStencil -#define glClientActiveTexture wrap_glClientActiveTexture -#define glClientWaitSync wrap_glClientWaitSync -#define glClientWaitSyncAPPLE wrap_glClientWaitSyncAPPLE -#define glClipPlanef wrap_glClipPlanef -#define glClipPlanefIMG wrap_glClipPlanefIMG -#define glClipPlanefOES wrap_glClipPlanefOES -#define glClipPlanex wrap_glClipPlanex -#define glClipPlanexIMG wrap_glClipPlanexIMG -#define glClipPlanexOES wrap_glClipPlanexOES -#define glColor4f wrap_glColor4f -#define glColor4ub wrap_glColor4ub -#define glColor4x wrap_glColor4x -#define glColor4xOES wrap_glColor4xOES -#define glColorMask wrap_glColorMask -#define glColorMaski wrap_glColorMaski -#define glColorMaskiEXT wrap_glColorMaskiEXT -#define glColorMaskiOES wrap_glColorMaskiOES -#define glColorPointer wrap_glColorPointer -#define glCompileShader wrap_glCompileShader -#define glCompressedTexImage2D wrap_glCompressedTexImage2D -#define glCompressedTexImage3D wrap_glCompressedTexImage3D -#define glCompressedTexImage3DOES wrap_glCompressedTexImage3DOES -#define glCompressedTexSubImage2D wrap_glCompressedTexSubImage2D -#define glCompressedTexSubImage3D wrap_glCompressedTexSubImage3D -#define glCompressedTexSubImage3DOES wrap_glCompressedTexSubImage3DOES -#define glCopyBufferSubData wrap_glCopyBufferSubData -#define glCopyBufferSubDataNV wrap_glCopyBufferSubDataNV -#define glCopyImageSubData wrap_glCopyImageSubData -#define glCopyImageSubDataEXT wrap_glCopyImageSubDataEXT -#define glCopyImageSubDataOES wrap_glCopyImageSubDataOES -#define glCopyPathNV wrap_glCopyPathNV -#define glCopyTexImage2D wrap_glCopyTexImage2D -#define glCopyTexSubImage2D wrap_glCopyTexSubImage2D -#define glCopyTexSubImage3D wrap_glCopyTexSubImage3D -#define glCopyTexSubImage3DOES wrap_glCopyTexSubImage3DOES -#define glCopyTextureLevelsAPPLE wrap_glCopyTextureLevelsAPPLE -#define glCoverFillPathInstancedNV wrap_glCoverFillPathInstancedNV -#define glCoverFillPathNV wrap_glCoverFillPathNV -#define glCoverStrokePathInstancedNV wrap_glCoverStrokePathInstancedNV -#define glCoverStrokePathNV wrap_glCoverStrokePathNV -#define glCoverageMaskNV wrap_glCoverageMaskNV -#define glCoverageModulationNV wrap_glCoverageModulationNV -#define glCoverageModulationTableNV wrap_glCoverageModulationTableNV -#define glCoverageOperationNV wrap_glCoverageOperationNV -#define glCreatePerfQueryINTEL wrap_glCreatePerfQueryINTEL -#define glCreateProgram wrap_glCreateProgram -#define glCreateShader wrap_glCreateShader -#define glCreateShaderProgramv wrap_glCreateShaderProgramv -#define glCreateShaderProgramvEXT wrap_glCreateShaderProgramvEXT -#define glCullFace wrap_glCullFace -#define glCurrentPaletteMatrixOES wrap_glCurrentPaletteMatrixOES -#define glDebugMessageCallback wrap_glDebugMessageCallback -#define glDebugMessageCallbackKHR wrap_glDebugMessageCallbackKHR -#define glDebugMessageControl wrap_glDebugMessageControl -#define glDebugMessageControlKHR wrap_glDebugMessageControlKHR -#define glDebugMessageInsert wrap_glDebugMessageInsert -#define glDebugMessageInsertKHR wrap_glDebugMessageInsertKHR -#define glDeleteBuffers wrap_glDeleteBuffers -#define glDeleteFencesNV wrap_glDeleteFencesNV -#define glDeleteFramebuffers wrap_glDeleteFramebuffers -#define glDeleteFramebuffersOES wrap_glDeleteFramebuffersOES -#define glDeletePathsNV wrap_glDeletePathsNV -#define glDeletePerfMonitorsAMD wrap_glDeletePerfMonitorsAMD -#define glDeletePerfQueryINTEL wrap_glDeletePerfQueryINTEL -#define glDeleteProgram wrap_glDeleteProgram -#define glDeleteProgramPipelines wrap_glDeleteProgramPipelines -#define glDeleteProgramPipelinesEXT wrap_glDeleteProgramPipelinesEXT -#define glDeleteQueries wrap_glDeleteQueries -#define glDeleteQueriesEXT wrap_glDeleteQueriesEXT -#define glDeleteRenderbuffers wrap_glDeleteRenderbuffers -#define glDeleteRenderbuffersOES wrap_glDeleteRenderbuffersOES -#define glDeleteSamplers wrap_glDeleteSamplers -#define glDeleteShader wrap_glDeleteShader -#define glDeleteSync wrap_glDeleteSync -#define glDeleteSyncAPPLE wrap_glDeleteSyncAPPLE -#define glDeleteTextures wrap_glDeleteTextures -#define glDeleteTransformFeedbacks wrap_glDeleteTransformFeedbacks -#define glDeleteVertexArrays wrap_glDeleteVertexArrays -#define glDeleteVertexArraysOES wrap_glDeleteVertexArraysOES -#define glDepthFunc wrap_glDepthFunc -#define glDepthMask wrap_glDepthMask -#define glDepthRangeArrayfvNV wrap_glDepthRangeArrayfvNV -#define glDepthRangeIndexedfNV wrap_glDepthRangeIndexedfNV -#define glDepthRangef wrap_glDepthRangef -#define glDepthRangefOES wrap_glDepthRangefOES -#define glDepthRangex wrap_glDepthRangex -#define glDepthRangexOES wrap_glDepthRangexOES -#define glDetachShader wrap_glDetachShader -#define glDisable wrap_glDisable -#define glDisableClientState wrap_glDisableClientState -#define glDisableDriverControlQCOM wrap_glDisableDriverControlQCOM -#define glDisableVertexAttribArray wrap_glDisableVertexAttribArray -#define glDisablei wrap_glDisablei -#define glDisableiEXT wrap_glDisableiEXT -#define glDisableiNV wrap_glDisableiNV -#define glDisableiOES wrap_glDisableiOES -#define glDiscardFramebufferEXT wrap_glDiscardFramebufferEXT -#define glDispatchCompute wrap_glDispatchCompute -#define glDispatchComputeIndirect wrap_glDispatchComputeIndirect -#define glDrawArrays wrap_glDrawArrays -#define glDrawArraysIndirect wrap_glDrawArraysIndirect -#define glDrawArraysInstanced wrap_glDrawArraysInstanced -#define glDrawArraysInstancedANGLE wrap_glDrawArraysInstancedANGLE -#define glDrawArraysInstancedBaseInstanceEXT wrap_glDrawArraysInstancedBaseInstanceEXT -#define glDrawArraysInstancedEXT wrap_glDrawArraysInstancedEXT -#define glDrawArraysInstancedNV wrap_glDrawArraysInstancedNV -#define glDrawBuffers wrap_glDrawBuffers -#define glDrawBuffersEXT wrap_glDrawBuffersEXT -#define glDrawBuffersIndexedEXT wrap_glDrawBuffersIndexedEXT -#define glDrawBuffersNV wrap_glDrawBuffersNV -#define glDrawElements wrap_glDrawElements -#define glDrawElementsBaseVertex wrap_glDrawElementsBaseVertex -#define glDrawElementsBaseVertexEXT wrap_glDrawElementsBaseVertexEXT -#define glDrawElementsBaseVertexOES wrap_glDrawElementsBaseVertexOES -#define glDrawElementsIndirect wrap_glDrawElementsIndirect -#define glDrawElementsInstanced wrap_glDrawElementsInstanced -#define glDrawElementsInstancedANGLE wrap_glDrawElementsInstancedANGLE -#define glDrawElementsInstancedBaseInstanceEXT wrap_glDrawElementsInstancedBaseInstanceEXT -#define glDrawElementsInstancedBaseVertex wrap_glDrawElementsInstancedBaseVertex -#define glDrawElementsInstancedBaseVertexBaseInstanceEXT \ - wrap_glDrawElementsInstancedBaseVertexBaseInstanceEXT -#define glDrawElementsInstancedBaseVertexEXT wrap_glDrawElementsInstancedBaseVertexEXT -#define glDrawElementsInstancedBaseVertexOES wrap_glDrawElementsInstancedBaseVertexOES -#define glDrawElementsInstancedEXT wrap_glDrawElementsInstancedEXT -#define glDrawElementsInstancedNV wrap_glDrawElementsInstancedNV -#define glDrawRangeElements wrap_glDrawRangeElements -#define glDrawRangeElementsBaseVertex wrap_glDrawRangeElementsBaseVertex -#define glDrawRangeElementsBaseVertexEXT wrap_glDrawRangeElementsBaseVertexEXT -#define glDrawRangeElementsBaseVertexOES wrap_glDrawRangeElementsBaseVertexOES -#define glDrawTexfOES wrap_glDrawTexfOES -#define glDrawTexfvOES wrap_glDrawTexfvOES -#define glDrawTexiOES wrap_glDrawTexiOES -#define glDrawTexivOES wrap_glDrawTexivOES -#define glDrawTexsOES wrap_glDrawTexsOES -#define glDrawTexsvOES wrap_glDrawTexsvOES -#define glDrawTexxOES wrap_glDrawTexxOES -#define glDrawTexxvOES wrap_glDrawTexxvOES -#define glEGLImageTargetRenderbufferStorageOES wrap_glEGLImageTargetRenderbufferStorageOES -#define glEGLImageTargetTexture2DOES wrap_glEGLImageTargetTexture2DOES -#define glEnable wrap_glEnable -#define glEnableClientState wrap_glEnableClientState -#define glEnableDriverControlQCOM wrap_glEnableDriverControlQCOM -#define glEnableVertexAttribArray wrap_glEnableVertexAttribArray -#define glEnablei wrap_glEnablei -#define glEnableiEXT wrap_glEnableiEXT -#define glEnableiNV wrap_glEnableiNV -#define glEnableiOES wrap_glEnableiOES -#define glEndConditionalRenderNV wrap_glEndConditionalRenderNV -#define glEndPerfMonitorAMD wrap_glEndPerfMonitorAMD -#define glEndPerfQueryINTEL wrap_glEndPerfQueryINTEL -#define glEndQuery wrap_glEndQuery -#define glEndQueryEXT wrap_glEndQueryEXT -#define glEndTilingQCOM wrap_glEndTilingQCOM -#define glEndTransformFeedback wrap_glEndTransformFeedback -#define glExtGetBufferPointervQCOM wrap_glExtGetBufferPointervQCOM -#define glExtGetBuffersQCOM wrap_glExtGetBuffersQCOM -#define glExtGetFramebuffersQCOM wrap_glExtGetFramebuffersQCOM -#define glExtGetProgramBinarySourceQCOM wrap_glExtGetProgramBinarySourceQCOM -#define glExtGetProgramsQCOM wrap_glExtGetProgramsQCOM -#define glExtGetRenderbuffersQCOM wrap_glExtGetRenderbuffersQCOM -#define glExtGetShadersQCOM wrap_glExtGetShadersQCOM -#define glExtGetTexLevelParameterivQCOM wrap_glExtGetTexLevelParameterivQCOM -#define glExtGetTexSubImageQCOM wrap_glExtGetTexSubImageQCOM -#define glExtGetTexturesQCOM wrap_glExtGetTexturesQCOM -#define glExtIsProgramBinaryQCOM wrap_glExtIsProgramBinaryQCOM -#define glExtTexObjectStateOverrideiQCOM wrap_glExtTexObjectStateOverrideiQCOM -#define glFenceSync wrap_glFenceSync -#define glFenceSyncAPPLE wrap_glFenceSyncAPPLE -#define glFinish wrap_glFinish -#define glFinishFenceNV wrap_glFinishFenceNV -#define glFlush wrap_glFlush -#define glFlushMappedBufferRange wrap_glFlushMappedBufferRange -#define glFlushMappedBufferRangeEXT wrap_glFlushMappedBufferRangeEXT -#define glFogf wrap_glFogf -#define glFogfv wrap_glFogfv -#define glFogx wrap_glFogx -#define glFogxOES wrap_glFogxOES -#define glFogxv wrap_glFogxv -#define glFogxvOES wrap_glFogxvOES -#define glFragmentCoverageColorNV wrap_glFragmentCoverageColorNV -#define glFramebufferParameteri wrap_glFramebufferParameteri -#define glFramebufferRenderbuffer wrap_glFramebufferRenderbuffer -#define glFramebufferRenderbufferOES wrap_glFramebufferRenderbufferOES -#define glFramebufferSampleLocationsfvNV wrap_glFramebufferSampleLocationsfvNV -#define glFramebufferTexture wrap_glFramebufferTexture -#define glFramebufferTexture2D wrap_glFramebufferTexture2D -#define glFramebufferTexture2DMultisampleEXT wrap_glFramebufferTexture2DMultisampleEXT -#define glFramebufferTexture2DMultisampleIMG wrap_glFramebufferTexture2DMultisampleIMG -#define glFramebufferTexture2DOES wrap_glFramebufferTexture2DOES -#define glFramebufferTexture3DOES wrap_glFramebufferTexture3DOES -#define glFramebufferTextureEXT wrap_glFramebufferTextureEXT -#define glFramebufferTextureLayer wrap_glFramebufferTextureLayer -#define glFramebufferTextureMultisampleMultiviewOVR wrap_glFramebufferTextureMultisampleMultiviewOVR -#define glFramebufferTextureMultiviewOVR wrap_glFramebufferTextureMultiviewOVR -#define glFramebufferTextureOES wrap_glFramebufferTextureOES -#define glFrontFace wrap_glFrontFace -#define glFrustumf wrap_glFrustumf -#define glFrustumfOES wrap_glFrustumfOES -#define glFrustumx wrap_glFrustumx -#define glFrustumxOES wrap_glFrustumxOES -#define glGenBuffers wrap_glGenBuffers -#define glGenFencesNV wrap_glGenFencesNV -#define glGenFramebuffers wrap_glGenFramebuffers -#define glGenFramebuffersOES wrap_glGenFramebuffersOES -#define glGenPathsNV wrap_glGenPathsNV -#define glGenPerfMonitorsAMD wrap_glGenPerfMonitorsAMD -#define glGenProgramPipelines wrap_glGenProgramPipelines -#define glGenProgramPipelinesEXT wrap_glGenProgramPipelinesEXT -#define glGenQueries wrap_glGenQueries -#define glGenQueriesEXT wrap_glGenQueriesEXT -#define glGenRenderbuffers wrap_glGenRenderbuffers -#define glGenRenderbuffersOES wrap_glGenRenderbuffersOES -#define glGenSamplers wrap_glGenSamplers -#define glGenTextures wrap_glGenTextures -#define glGenTransformFeedbacks wrap_glGenTransformFeedbacks -#define glGenVertexArrays wrap_glGenVertexArrays -#define glGenVertexArraysOES wrap_glGenVertexArraysOES -#define glGenerateMipmap wrap_glGenerateMipmap -#define glGenerateMipmapOES wrap_glGenerateMipmapOES -#define glGetActiveAttrib wrap_glGetActiveAttrib -#define glGetActiveUniform wrap_glGetActiveUniform -#define glGetActiveUniformBlockName wrap_glGetActiveUniformBlockName -#define glGetActiveUniformBlockiv wrap_glGetActiveUniformBlockiv -#define glGetActiveUniformsiv wrap_glGetActiveUniformsiv -#define glGetAttachedShaders wrap_glGetAttachedShaders -#define glGetAttribLocation wrap_glGetAttribLocation -#define glGetBooleani_v wrap_glGetBooleani_v -#define glGetBooleanv wrap_glGetBooleanv -#define glGetBufferParameteri64v wrap_glGetBufferParameteri64v -#define glGetBufferParameteriv wrap_glGetBufferParameteriv -#define glGetBufferPointerv wrap_glGetBufferPointerv -#define glGetBufferPointervOES wrap_glGetBufferPointervOES -#define glGetClipPlanef wrap_glGetClipPlanef -#define glGetClipPlanefOES wrap_glGetClipPlanefOES -#define glGetClipPlanex wrap_glGetClipPlanex -#define glGetClipPlanexOES wrap_glGetClipPlanexOES -#define glGetCoverageModulationTableNV wrap_glGetCoverageModulationTableNV -#define glGetDebugMessageLog wrap_glGetDebugMessageLog -#define glGetDebugMessageLogKHR wrap_glGetDebugMessageLogKHR -#define glGetDriverControlStringQCOM wrap_glGetDriverControlStringQCOM -#define glGetDriverControlsQCOM wrap_glGetDriverControlsQCOM -#define glGetError wrap_glGetError -#define glGetFenceivNV wrap_glGetFenceivNV -#define glGetFirstPerfQueryIdINTEL wrap_glGetFirstPerfQueryIdINTEL -#define glGetFixedv wrap_glGetFixedv -#define glGetFixedvOES wrap_glGetFixedvOES -#define glGetFloati_vNV wrap_glGetFloati_vNV -#define glGetFloatv wrap_glGetFloatv -#define glGetFragDataIndexEXT wrap_glGetFragDataIndexEXT -#define glGetFragDataLocation wrap_glGetFragDataLocation -#define glGetFramebufferAttachmentParameteriv wrap_glGetFramebufferAttachmentParameteriv -#define glGetFramebufferAttachmentParameterivOES wrap_glGetFramebufferAttachmentParameterivOES -#define glGetFramebufferParameteriv wrap_glGetFramebufferParameteriv -#define glGetGraphicsResetStatus wrap_glGetGraphicsResetStatus -#define glGetGraphicsResetStatusEXT wrap_glGetGraphicsResetStatusEXT -#define glGetGraphicsResetStatusKHR wrap_glGetGraphicsResetStatusKHR -#define glGetImageHandleNV wrap_glGetImageHandleNV -#define glGetInteger64i_v wrap_glGetInteger64i_v -#define glGetInteger64v wrap_glGetInteger64v -#define glGetInteger64vAPPLE wrap_glGetInteger64vAPPLE -#define glGetIntegeri_v wrap_glGetIntegeri_v -#define glGetIntegeri_vEXT wrap_glGetIntegeri_vEXT -#define glGetIntegerv wrap_glGetIntegerv -#define glGetInternalformatSampleivNV wrap_glGetInternalformatSampleivNV -#define glGetInternalformativ wrap_glGetInternalformativ -#define glGetLightfv wrap_glGetLightfv -#define glGetLightxv wrap_glGetLightxv -#define glGetLightxvOES wrap_glGetLightxvOES -#define glGetMaterialfv wrap_glGetMaterialfv -#define glGetMaterialxv wrap_glGetMaterialxv -#define glGetMaterialxvOES wrap_glGetMaterialxvOES -#define glGetMultisamplefv wrap_glGetMultisamplefv -#define glGetNextPerfQueryIdINTEL wrap_glGetNextPerfQueryIdINTEL -#define glGetObjectLabel wrap_glGetObjectLabel -#define glGetObjectLabelEXT wrap_glGetObjectLabelEXT -#define glGetObjectLabelKHR wrap_glGetObjectLabelKHR -#define glGetObjectPtrLabel wrap_glGetObjectPtrLabel -#define glGetObjectPtrLabelKHR wrap_glGetObjectPtrLabelKHR -#define glGetPathCommandsNV wrap_glGetPathCommandsNV -#define glGetPathCoordsNV wrap_glGetPathCoordsNV -#define glGetPathDashArrayNV wrap_glGetPathDashArrayNV -#define glGetPathLengthNV wrap_glGetPathLengthNV -#define glGetPathMetricRangeNV wrap_glGetPathMetricRangeNV -#define glGetPathMetricsNV wrap_glGetPathMetricsNV -#define glGetPathParameterfvNV wrap_glGetPathParameterfvNV -#define glGetPathParameterivNV wrap_glGetPathParameterivNV -#define glGetPathSpacingNV wrap_glGetPathSpacingNV -#define glGetPerfCounterInfoINTEL wrap_glGetPerfCounterInfoINTEL -#define glGetPerfMonitorCounterDataAMD wrap_glGetPerfMonitorCounterDataAMD -#define glGetPerfMonitorCounterInfoAMD wrap_glGetPerfMonitorCounterInfoAMD -#define glGetPerfMonitorCounterStringAMD wrap_glGetPerfMonitorCounterStringAMD -#define glGetPerfMonitorCountersAMD wrap_glGetPerfMonitorCountersAMD -#define glGetPerfMonitorGroupStringAMD wrap_glGetPerfMonitorGroupStringAMD -#define glGetPerfMonitorGroupsAMD wrap_glGetPerfMonitorGroupsAMD -#define glGetPerfQueryDataINTEL wrap_glGetPerfQueryDataINTEL -#define glGetPerfQueryIdByNameINTEL wrap_glGetPerfQueryIdByNameINTEL -#define glGetPerfQueryInfoINTEL wrap_glGetPerfQueryInfoINTEL -#define glGetPointerv wrap_glGetPointerv -#define glGetPointervKHR wrap_glGetPointervKHR -#define glGetProgramBinary wrap_glGetProgramBinary -#define glGetProgramBinaryOES wrap_glGetProgramBinaryOES -#define glGetProgramInfoLog wrap_glGetProgramInfoLog -#define glGetProgramInterfaceiv wrap_glGetProgramInterfaceiv -#define glGetProgramPipelineInfoLog wrap_glGetProgramPipelineInfoLog -#define glGetProgramPipelineInfoLogEXT wrap_glGetProgramPipelineInfoLogEXT -#define glGetProgramPipelineiv wrap_glGetProgramPipelineiv -#define glGetProgramPipelineivEXT wrap_glGetProgramPipelineivEXT -#define glGetProgramResourceIndex wrap_glGetProgramResourceIndex -#define glGetProgramResourceLocation wrap_glGetProgramResourceLocation -#define glGetProgramResourceLocationIndexEXT wrap_glGetProgramResourceLocationIndexEXT -#define glGetProgramResourceName wrap_glGetProgramResourceName -#define glGetProgramResourcefvNV wrap_glGetProgramResourcefvNV -#define glGetProgramResourceiv wrap_glGetProgramResourceiv -#define glGetProgramiv wrap_glGetProgramiv -#define glGetQueryObjecti64vEXT wrap_glGetQueryObjecti64vEXT -#define glGetQueryObjectivEXT wrap_glGetQueryObjectivEXT -#define glGetQueryObjectui64vEXT wrap_glGetQueryObjectui64vEXT -#define glGetQueryObjectuiv wrap_glGetQueryObjectuiv -#define glGetQueryObjectuivEXT wrap_glGetQueryObjectuivEXT -#define glGetQueryiv wrap_glGetQueryiv -#define glGetQueryivEXT wrap_glGetQueryivEXT -#define glGetRenderbufferParameteriv wrap_glGetRenderbufferParameteriv -#define glGetRenderbufferParameterivOES wrap_glGetRenderbufferParameterivOES -#define glGetSamplerParameterIiv wrap_glGetSamplerParameterIiv -#define glGetSamplerParameterIivEXT wrap_glGetSamplerParameterIivEXT -#define glGetSamplerParameterIivOES wrap_glGetSamplerParameterIivOES -#define glGetSamplerParameterIuiv wrap_glGetSamplerParameterIuiv -#define glGetSamplerParameterIuivEXT wrap_glGetSamplerParameterIuivEXT -#define glGetSamplerParameterIuivOES wrap_glGetSamplerParameterIuivOES -#define glGetSamplerParameterfv wrap_glGetSamplerParameterfv -#define glGetSamplerParameteriv wrap_glGetSamplerParameteriv -#define glGetShaderInfoLog wrap_glGetShaderInfoLog -#define glGetShaderPrecisionFormat wrap_glGetShaderPrecisionFormat -#define glGetShaderSource wrap_glGetShaderSource -#define glGetShaderiv wrap_glGetShaderiv -#define glGetString wrap_glGetString -#define glGetStringi wrap_glGetStringi -#define glGetSynciv wrap_glGetSynciv -#define glGetSyncivAPPLE wrap_glGetSyncivAPPLE -#define glGetTexEnvfv wrap_glGetTexEnvfv -#define glGetTexEnviv wrap_glGetTexEnviv -#define glGetTexEnvxv wrap_glGetTexEnvxv -#define glGetTexEnvxvOES wrap_glGetTexEnvxvOES -#define glGetTexGenfvOES wrap_glGetTexGenfvOES -#define glGetTexGenivOES wrap_glGetTexGenivOES -#define glGetTexGenxvOES wrap_glGetTexGenxvOES -#define glGetTexLevelParameterfv wrap_glGetTexLevelParameterfv -#define glGetTexLevelParameteriv wrap_glGetTexLevelParameteriv -#define glGetTexParameterIiv wrap_glGetTexParameterIiv -#define glGetTexParameterIivEXT wrap_glGetTexParameterIivEXT -#define glGetTexParameterIivOES wrap_glGetTexParameterIivOES -#define glGetTexParameterIuiv wrap_glGetTexParameterIuiv -#define glGetTexParameterIuivEXT wrap_glGetTexParameterIuivEXT -#define glGetTexParameterIuivOES wrap_glGetTexParameterIuivOES -#define glGetTexParameterfv wrap_glGetTexParameterfv -#define glGetTexParameteriv wrap_glGetTexParameteriv -#define glGetTexParameterxv wrap_glGetTexParameterxv -#define glGetTexParameterxvOES wrap_glGetTexParameterxvOES -#define glGetTextureHandleNV wrap_glGetTextureHandleNV -#define glGetTextureSamplerHandleNV wrap_glGetTextureSamplerHandleNV -#define glGetTransformFeedbackVarying wrap_glGetTransformFeedbackVarying -#define glGetTranslatedShaderSourceANGLE wrap_glGetTranslatedShaderSourceANGLE -#define glGetUniformBlockIndex wrap_glGetUniformBlockIndex -#define glGetUniformIndices wrap_glGetUniformIndices -#define glGetUniformLocation wrap_glGetUniformLocation -#define glGetUniformfv wrap_glGetUniformfv -#define glGetUniformiv wrap_glGetUniformiv -#define glGetUniformuiv wrap_glGetUniformuiv -#define glGetVertexAttribIiv wrap_glGetVertexAttribIiv -#define glGetVertexAttribIuiv wrap_glGetVertexAttribIuiv -#define glGetVertexAttribPointerv wrap_glGetVertexAttribPointerv -#define glGetVertexAttribfv wrap_glGetVertexAttribfv -#define glGetVertexAttribiv wrap_glGetVertexAttribiv -#define glGetnUniformfv wrap_glGetnUniformfv -#define glGetnUniformfvEXT wrap_glGetnUniformfvEXT -#define glGetnUniformfvKHR wrap_glGetnUniformfvKHR -#define glGetnUniformiv wrap_glGetnUniformiv -#define glGetnUniformivEXT wrap_glGetnUniformivEXT -#define glGetnUniformivKHR wrap_glGetnUniformivKHR -#define glGetnUniformuiv wrap_glGetnUniformuiv -#define glGetnUniformuivKHR wrap_glGetnUniformuivKHR -#define glHint wrap_glHint -#define glInsertEventMarkerEXT wrap_glInsertEventMarkerEXT -#define glInterpolatePathsNV wrap_glInterpolatePathsNV -#define glInvalidateFramebuffer wrap_glInvalidateFramebuffer -#define glInvalidateSubFramebuffer wrap_glInvalidateSubFramebuffer -#define glIsBuffer wrap_glIsBuffer -#define glIsEnabled wrap_glIsEnabled -#define glIsEnabledi wrap_glIsEnabledi -#define glIsEnablediEXT wrap_glIsEnablediEXT -#define glIsEnablediNV wrap_glIsEnablediNV -#define glIsEnablediOES wrap_glIsEnablediOES -#define glIsFenceNV wrap_glIsFenceNV -#define glIsFramebuffer wrap_glIsFramebuffer -#define glIsFramebufferOES wrap_glIsFramebufferOES -#define glIsImageHandleResidentNV wrap_glIsImageHandleResidentNV -#define glIsPathNV wrap_glIsPathNV -#define glIsPointInFillPathNV wrap_glIsPointInFillPathNV -#define glIsPointInStrokePathNV wrap_glIsPointInStrokePathNV -#define glIsProgram wrap_glIsProgram -#define glIsProgramPipeline wrap_glIsProgramPipeline -#define glIsProgramPipelineEXT wrap_glIsProgramPipelineEXT -#define glIsQuery wrap_glIsQuery -#define glIsQueryEXT wrap_glIsQueryEXT -#define glIsRenderbuffer wrap_glIsRenderbuffer -#define glIsRenderbufferOES wrap_glIsRenderbufferOES -#define glIsSampler wrap_glIsSampler -#define glIsShader wrap_glIsShader -#define glIsSync wrap_glIsSync -#define glIsSyncAPPLE wrap_glIsSyncAPPLE -#define glIsTexture wrap_glIsTexture -#define glIsTextureHandleResidentNV wrap_glIsTextureHandleResidentNV -#define glIsTransformFeedback wrap_glIsTransformFeedback -#define glIsVertexArray wrap_glIsVertexArray -#define glIsVertexArrayOES wrap_glIsVertexArrayOES -#define glLabelObjectEXT wrap_glLabelObjectEXT -#define glLightModelf wrap_glLightModelf -#define glLightModelfv wrap_glLightModelfv -#define glLightModelx wrap_glLightModelx -#define glLightModelxOES wrap_glLightModelxOES -#define glLightModelxv wrap_glLightModelxv -#define glLightModelxvOES wrap_glLightModelxvOES -#define glLightf wrap_glLightf -#define glLightfv wrap_glLightfv -#define glLightx wrap_glLightx -#define glLightxOES wrap_glLightxOES -#define glLightxv wrap_glLightxv -#define glLightxvOES wrap_glLightxvOES -#define glLineWidth wrap_glLineWidth -#define glLineWidthx wrap_glLineWidthx -#define glLineWidthxOES wrap_glLineWidthxOES -#define glLinkProgram wrap_glLinkProgram -#define glLoadIdentity wrap_glLoadIdentity -#define glLoadMatrixf wrap_glLoadMatrixf -#define glLoadMatrixx wrap_glLoadMatrixx -#define glLoadMatrixxOES wrap_glLoadMatrixxOES -#define glLoadPaletteFromModelViewMatrixOES wrap_glLoadPaletteFromModelViewMatrixOES -#define glLogicOp wrap_glLogicOp -#define glMakeImageHandleNonResidentNV wrap_glMakeImageHandleNonResidentNV -#define glMakeImageHandleResidentNV wrap_glMakeImageHandleResidentNV -#define glMakeTextureHandleNonResidentNV wrap_glMakeTextureHandleNonResidentNV -#define glMakeTextureHandleResidentNV wrap_glMakeTextureHandleResidentNV -#define glMapBufferOES wrap_glMapBufferOES -#define glMapBufferRange wrap_glMapBufferRange -#define glMapBufferRangeEXT wrap_glMapBufferRangeEXT -#define glMaterialf wrap_glMaterialf -#define glMaterialfv wrap_glMaterialfv -#define glMaterialx wrap_glMaterialx -#define glMaterialxOES wrap_glMaterialxOES -#define glMaterialxv wrap_glMaterialxv -#define glMaterialxvOES wrap_glMaterialxvOES -#define glMatrixIndexPointerOES wrap_glMatrixIndexPointerOES -#define glMatrixLoad3x2fNV wrap_glMatrixLoad3x2fNV -#define glMatrixLoad3x3fNV wrap_glMatrixLoad3x3fNV -#define glMatrixLoadTranspose3x3fNV wrap_glMatrixLoadTranspose3x3fNV -#define glMatrixMode wrap_glMatrixMode -#define glMatrixMult3x2fNV wrap_glMatrixMult3x2fNV -#define glMatrixMult3x3fNV wrap_glMatrixMult3x3fNV -#define glMatrixMultTranspose3x3fNV wrap_glMatrixMultTranspose3x3fNV -#define glMemoryBarrier wrap_glMemoryBarrier -#define glMemoryBarrierByRegion wrap_glMemoryBarrierByRegion -#define glMinSampleShading wrap_glMinSampleShading -#define glMinSampleShadingOES wrap_glMinSampleShadingOES -#define glMultMatrixf wrap_glMultMatrixf -#define glMultMatrixx wrap_glMultMatrixx -#define glMultMatrixxOES wrap_glMultMatrixxOES -#define glMultiDrawArraysEXT wrap_glMultiDrawArraysEXT -#define glMultiDrawArraysIndirectEXT wrap_glMultiDrawArraysIndirectEXT -#define glMultiDrawElementsBaseVertexEXT wrap_glMultiDrawElementsBaseVertexEXT -#define glMultiDrawElementsBaseVertexOES wrap_glMultiDrawElementsBaseVertexOES -#define glMultiDrawElementsEXT wrap_glMultiDrawElementsEXT -#define glMultiDrawElementsIndirectEXT wrap_glMultiDrawElementsIndirectEXT -#define glMultiTexCoord4f wrap_glMultiTexCoord4f -#define glMultiTexCoord4x wrap_glMultiTexCoord4x -#define glMultiTexCoord4xOES wrap_glMultiTexCoord4xOES -#define glNamedFramebufferSampleLocationsfvNV wrap_glNamedFramebufferSampleLocationsfvNV -#define glNormal3f wrap_glNormal3f -#define glNormal3x wrap_glNormal3x -#define glNormal3xOES wrap_glNormal3xOES -#define glNormalPointer wrap_glNormalPointer -#define glObjectLabel wrap_glObjectLabel -#define glObjectLabelKHR wrap_glObjectLabelKHR -#define glObjectPtrLabel wrap_glObjectPtrLabel -#define glObjectPtrLabelKHR wrap_glObjectPtrLabelKHR -#define glOrthof wrap_glOrthof -#define glOrthofOES wrap_glOrthofOES -#define glOrthox wrap_glOrthox -#define glOrthoxOES wrap_glOrthoxOES -#define glPatchParameteri wrap_glPatchParameteri -#define glPatchParameteriEXT wrap_glPatchParameteriEXT -#define glPatchParameteriOES wrap_glPatchParameteriOES -#define glPathCommandsNV wrap_glPathCommandsNV -#define glPathCoordsNV wrap_glPathCoordsNV -#define glPathCoverDepthFuncNV wrap_glPathCoverDepthFuncNV -#define glPathDashArrayNV wrap_glPathDashArrayNV -#define glPathGlyphIndexArrayNV wrap_glPathGlyphIndexArrayNV -#define glPathGlyphIndexRangeNV wrap_glPathGlyphIndexRangeNV -#define glPathGlyphRangeNV wrap_glPathGlyphRangeNV -#define glPathGlyphsNV wrap_glPathGlyphsNV -#define glPathMemoryGlyphIndexArrayNV wrap_glPathMemoryGlyphIndexArrayNV -#define glPathParameterfNV wrap_glPathParameterfNV -#define glPathParameterfvNV wrap_glPathParameterfvNV -#define glPathParameteriNV wrap_glPathParameteriNV -#define glPathParameterivNV wrap_glPathParameterivNV -#define glPathStencilDepthOffsetNV wrap_glPathStencilDepthOffsetNV -#define glPathStencilFuncNV wrap_glPathStencilFuncNV -#define glPathStringNV wrap_glPathStringNV -#define glPathSubCommandsNV wrap_glPathSubCommandsNV -#define glPathSubCoordsNV wrap_glPathSubCoordsNV -#define glPauseTransformFeedback wrap_glPauseTransformFeedback -#define glPixelStorei wrap_glPixelStorei -#define glPointAlongPathNV wrap_glPointAlongPathNV -#define glPointParameterf wrap_glPointParameterf -#define glPointParameterfv wrap_glPointParameterfv -#define glPointParameterx wrap_glPointParameterx -#define glPointParameterxOES wrap_glPointParameterxOES -#define glPointParameterxv wrap_glPointParameterxv -#define glPointParameterxvOES wrap_glPointParameterxvOES -#define glPointSize wrap_glPointSize -#define glPointSizePointerOES wrap_glPointSizePointerOES -#define glPointSizex wrap_glPointSizex -#define glPointSizexOES wrap_glPointSizexOES -#define glPolygonModeNV wrap_glPolygonModeNV -#define glPolygonOffset wrap_glPolygonOffset -#define glPolygonOffsetx wrap_glPolygonOffsetx -#define glPolygonOffsetxOES wrap_glPolygonOffsetxOES -#define glPopDebugGroup wrap_glPopDebugGroup -#define glPopDebugGroupKHR wrap_glPopDebugGroupKHR -#define glPopGroupMarkerEXT wrap_glPopGroupMarkerEXT -#define glPopMatrix wrap_glPopMatrix -#define glPrimitiveBoundingBox wrap_glPrimitiveBoundingBox -#define glPrimitiveBoundingBoxEXT wrap_glPrimitiveBoundingBoxEXT -#define glPrimitiveBoundingBoxOES wrap_glPrimitiveBoundingBoxOES -#define glProgramBinary wrap_glProgramBinary -#define glProgramBinaryOES wrap_glProgramBinaryOES -#define glProgramParameteri wrap_glProgramParameteri -#define glProgramParameteriEXT wrap_glProgramParameteriEXT -#define glProgramPathFragmentInputGenNV wrap_glProgramPathFragmentInputGenNV -#define glProgramUniform1f wrap_glProgramUniform1f -#define glProgramUniform1fEXT wrap_glProgramUniform1fEXT -#define glProgramUniform1fv wrap_glProgramUniform1fv -#define glProgramUniform1fvEXT wrap_glProgramUniform1fvEXT -#define glProgramUniform1i wrap_glProgramUniform1i -#define glProgramUniform1iEXT wrap_glProgramUniform1iEXT -#define glProgramUniform1iv wrap_glProgramUniform1iv -#define glProgramUniform1ivEXT wrap_glProgramUniform1ivEXT -#define glProgramUniform1ui wrap_glProgramUniform1ui -#define glProgramUniform1uiEXT wrap_glProgramUniform1uiEXT -#define glProgramUniform1uiv wrap_glProgramUniform1uiv -#define glProgramUniform1uivEXT wrap_glProgramUniform1uivEXT -#define glProgramUniform2f wrap_glProgramUniform2f -#define glProgramUniform2fEXT wrap_glProgramUniform2fEXT -#define glProgramUniform2fv wrap_glProgramUniform2fv -#define glProgramUniform2fvEXT wrap_glProgramUniform2fvEXT -#define glProgramUniform2i wrap_glProgramUniform2i -#define glProgramUniform2iEXT wrap_glProgramUniform2iEXT -#define glProgramUniform2iv wrap_glProgramUniform2iv -#define glProgramUniform2ivEXT wrap_glProgramUniform2ivEXT -#define glProgramUniform2ui wrap_glProgramUniform2ui -#define glProgramUniform2uiEXT wrap_glProgramUniform2uiEXT -#define glProgramUniform2uiv wrap_glProgramUniform2uiv -#define glProgramUniform2uivEXT wrap_glProgramUniform2uivEXT -#define glProgramUniform3f wrap_glProgramUniform3f -#define glProgramUniform3fEXT wrap_glProgramUniform3fEXT -#define glProgramUniform3fv wrap_glProgramUniform3fv -#define glProgramUniform3fvEXT wrap_glProgramUniform3fvEXT -#define glProgramUniform3i wrap_glProgramUniform3i -#define glProgramUniform3iEXT wrap_glProgramUniform3iEXT -#define glProgramUniform3iv wrap_glProgramUniform3iv -#define glProgramUniform3ivEXT wrap_glProgramUniform3ivEXT -#define glProgramUniform3ui wrap_glProgramUniform3ui -#define glProgramUniform3uiEXT wrap_glProgramUniform3uiEXT -#define glProgramUniform3uiv wrap_glProgramUniform3uiv -#define glProgramUniform3uivEXT wrap_glProgramUniform3uivEXT -#define glProgramUniform4f wrap_glProgramUniform4f -#define glProgramUniform4fEXT wrap_glProgramUniform4fEXT -#define glProgramUniform4fv wrap_glProgramUniform4fv -#define glProgramUniform4fvEXT wrap_glProgramUniform4fvEXT -#define glProgramUniform4i wrap_glProgramUniform4i -#define glProgramUniform4iEXT wrap_glProgramUniform4iEXT -#define glProgramUniform4iv wrap_glProgramUniform4iv -#define glProgramUniform4ivEXT wrap_glProgramUniform4ivEXT -#define glProgramUniform4ui wrap_glProgramUniform4ui -#define glProgramUniform4uiEXT wrap_glProgramUniform4uiEXT -#define glProgramUniform4uiv wrap_glProgramUniform4uiv -#define glProgramUniform4uivEXT wrap_glProgramUniform4uivEXT -#define glProgramUniformHandleui64NV wrap_glProgramUniformHandleui64NV -#define glProgramUniformHandleui64vNV wrap_glProgramUniformHandleui64vNV -#define glProgramUniformMatrix2fv wrap_glProgramUniformMatrix2fv -#define glProgramUniformMatrix2fvEXT wrap_glProgramUniformMatrix2fvEXT -#define glProgramUniformMatrix2x3fv wrap_glProgramUniformMatrix2x3fv -#define glProgramUniformMatrix2x3fvEXT wrap_glProgramUniformMatrix2x3fvEXT -#define glProgramUniformMatrix2x4fv wrap_glProgramUniformMatrix2x4fv -#define glProgramUniformMatrix2x4fvEXT wrap_glProgramUniformMatrix2x4fvEXT -#define glProgramUniformMatrix3fv wrap_glProgramUniformMatrix3fv -#define glProgramUniformMatrix3fvEXT wrap_glProgramUniformMatrix3fvEXT -#define glProgramUniformMatrix3x2fv wrap_glProgramUniformMatrix3x2fv -#define glProgramUniformMatrix3x2fvEXT wrap_glProgramUniformMatrix3x2fvEXT -#define glProgramUniformMatrix3x4fv wrap_glProgramUniformMatrix3x4fv -#define glProgramUniformMatrix3x4fvEXT wrap_glProgramUniformMatrix3x4fvEXT -#define glProgramUniformMatrix4fv wrap_glProgramUniformMatrix4fv -#define glProgramUniformMatrix4fvEXT wrap_glProgramUniformMatrix4fvEXT -#define glProgramUniformMatrix4x2fv wrap_glProgramUniformMatrix4x2fv -#define glProgramUniformMatrix4x2fvEXT wrap_glProgramUniformMatrix4x2fvEXT -#define glProgramUniformMatrix4x3fv wrap_glProgramUniformMatrix4x3fv -#define glProgramUniformMatrix4x3fvEXT wrap_glProgramUniformMatrix4x3fvEXT -#define glPushDebugGroup wrap_glPushDebugGroup -#define glPushDebugGroupKHR wrap_glPushDebugGroupKHR -#define glPushGroupMarkerEXT wrap_glPushGroupMarkerEXT -#define glPushMatrix wrap_glPushMatrix -#define glQueryCounterEXT wrap_glQueryCounterEXT -#define glQueryMatrixxOES wrap_glQueryMatrixxOES -#define glRasterSamplesEXT wrap_glRasterSamplesEXT -#define glReadBuffer wrap_glReadBuffer -#define glReadBufferIndexedEXT wrap_glReadBufferIndexedEXT -#define glReadBufferNV wrap_glReadBufferNV -#define glReadPixels wrap_glReadPixels -#define glReadnPixels wrap_glReadnPixels -#define glReadnPixelsEXT wrap_glReadnPixelsEXT -#define glReadnPixelsKHR wrap_glReadnPixelsKHR -#define glReleaseShaderCompiler wrap_glReleaseShaderCompiler -#define glRenderbufferStorage wrap_glRenderbufferStorage -#define glRenderbufferStorageMultisample wrap_glRenderbufferStorageMultisample -#define glRenderbufferStorageMultisampleANGLE wrap_glRenderbufferStorageMultisampleANGLE -#define glRenderbufferStorageMultisampleAPPLE wrap_glRenderbufferStorageMultisampleAPPLE -#define glRenderbufferStorageMultisampleEXT wrap_glRenderbufferStorageMultisampleEXT -#define glRenderbufferStorageMultisampleIMG wrap_glRenderbufferStorageMultisampleIMG -#define glRenderbufferStorageMultisampleNV wrap_glRenderbufferStorageMultisampleNV -#define glRenderbufferStorageOES wrap_glRenderbufferStorageOES -#define glResolveDepthValuesNV wrap_glResolveDepthValuesNV -#define glResolveMultisampleFramebufferAPPLE wrap_glResolveMultisampleFramebufferAPPLE -#define glResumeTransformFeedback wrap_glResumeTransformFeedback -#define glRotatef wrap_glRotatef -#define glRotatex wrap_glRotatex -#define glRotatexOES wrap_glRotatexOES -#define glSampleCoverage wrap_glSampleCoverage -#define glSampleCoveragex wrap_glSampleCoveragex -#define glSampleCoveragexOES wrap_glSampleCoveragexOES -#define glSampleMaski wrap_glSampleMaski -#define glSamplerParameterIiv wrap_glSamplerParameterIiv -#define glSamplerParameterIivEXT wrap_glSamplerParameterIivEXT -#define glSamplerParameterIivOES wrap_glSamplerParameterIivOES -#define glSamplerParameterIuiv wrap_glSamplerParameterIuiv -#define glSamplerParameterIuivEXT wrap_glSamplerParameterIuivEXT -#define glSamplerParameterIuivOES wrap_glSamplerParameterIuivOES -#define glSamplerParameterf wrap_glSamplerParameterf -#define glSamplerParameterfv wrap_glSamplerParameterfv -#define glSamplerParameteri wrap_glSamplerParameteri -#define glSamplerParameteriv wrap_glSamplerParameteriv -#define glScalef wrap_glScalef -#define glScalex wrap_glScalex -#define glScalexOES wrap_glScalexOES -#define glScissor wrap_glScissor -#define glScissorArrayvNV wrap_glScissorArrayvNV -#define glScissorIndexedNV wrap_glScissorIndexedNV -#define glScissorIndexedvNV wrap_glScissorIndexedvNV -#define glSelectPerfMonitorCountersAMD wrap_glSelectPerfMonitorCountersAMD -#define glSetFenceNV wrap_glSetFenceNV -#define glShadeModel wrap_glShadeModel -#define glShaderBinary wrap_glShaderBinary -#define glShaderSource wrap_glShaderSource -#define glStartTilingQCOM wrap_glStartTilingQCOM -#define glStencilFillPathInstancedNV wrap_glStencilFillPathInstancedNV -#define glStencilFillPathNV wrap_glStencilFillPathNV -#define glStencilFunc wrap_glStencilFunc -#define glStencilFuncSeparate wrap_glStencilFuncSeparate -#define glStencilMask wrap_glStencilMask -#define glStencilMaskSeparate wrap_glStencilMaskSeparate -#define glStencilOp wrap_glStencilOp -#define glStencilOpSeparate wrap_glStencilOpSeparate -#define glStencilStrokePathInstancedNV wrap_glStencilStrokePathInstancedNV -#define glStencilStrokePathNV wrap_glStencilStrokePathNV -#define glStencilThenCoverFillPathInstancedNV wrap_glStencilThenCoverFillPathInstancedNV -#define glStencilThenCoverFillPathNV wrap_glStencilThenCoverFillPathNV -#define glStencilThenCoverStrokePathInstancedNV wrap_glStencilThenCoverStrokePathInstancedNV -#define glStencilThenCoverStrokePathNV wrap_glStencilThenCoverStrokePathNV -#define glSubpixelPrecisionBiasNV wrap_glSubpixelPrecisionBiasNV -#define glTestFenceNV wrap_glTestFenceNV -#define glTexBuffer wrap_glTexBuffer -#define glTexBufferEXT wrap_glTexBufferEXT -#define glTexBufferOES wrap_glTexBufferOES -#define glTexBufferRange wrap_glTexBufferRange -#define glTexBufferRangeEXT wrap_glTexBufferRangeEXT -#define glTexBufferRangeOES wrap_glTexBufferRangeOES -#define glTexCoordPointer wrap_glTexCoordPointer -#define glTexEnvf wrap_glTexEnvf -#define glTexEnvfv wrap_glTexEnvfv -#define glTexEnvi wrap_glTexEnvi -#define glTexEnviv wrap_glTexEnviv -#define glTexEnvx wrap_glTexEnvx -#define glTexEnvxOES wrap_glTexEnvxOES -#define glTexEnvxv wrap_glTexEnvxv -#define glTexEnvxvOES wrap_glTexEnvxvOES -#define glTexGenfOES wrap_glTexGenfOES -#define glTexGenfvOES wrap_glTexGenfvOES -#define glTexGeniOES wrap_glTexGeniOES -#define glTexGenivOES wrap_glTexGenivOES -#define glTexGenxOES wrap_glTexGenxOES -#define glTexGenxvOES wrap_glTexGenxvOES -#define glTexImage2D wrap_glTexImage2D -#define glTexImage3D wrap_glTexImage3D -#define glTexImage3DOES wrap_glTexImage3DOES -#define glTexPageCommitmentEXT wrap_glTexPageCommitmentEXT -#define glTexParameterIiv wrap_glTexParameterIiv -#define glTexParameterIivEXT wrap_glTexParameterIivEXT -#define glTexParameterIivOES wrap_glTexParameterIivOES -#define glTexParameterIuiv wrap_glTexParameterIuiv -#define glTexParameterIuivEXT wrap_glTexParameterIuivEXT -#define glTexParameterIuivOES wrap_glTexParameterIuivOES -#define glTexParameterf wrap_glTexParameterf -#define glTexParameterfv wrap_glTexParameterfv -#define glTexParameteri wrap_glTexParameteri -#define glTexParameteriv wrap_glTexParameteriv -#define glTexParameterx wrap_glTexParameterx -#define glTexParameterxOES wrap_glTexParameterxOES -#define glTexParameterxv wrap_glTexParameterxv -#define glTexParameterxvOES wrap_glTexParameterxvOES -#define glTexStorage1DEXT wrap_glTexStorage1DEXT -#define glTexStorage2D wrap_glTexStorage2D -#define glTexStorage2DEXT wrap_glTexStorage2DEXT -#define glTexStorage2DMultisample wrap_glTexStorage2DMultisample -#define glTexStorage3D wrap_glTexStorage3D -#define glTexStorage3DEXT wrap_glTexStorage3DEXT -#define glTexStorage3DMultisample wrap_glTexStorage3DMultisample -#define glTexStorage3DMultisampleOES wrap_glTexStorage3DMultisampleOES -#define glTexSubImage2D wrap_glTexSubImage2D -#define glTexSubImage3D wrap_glTexSubImage3D -#define glTexSubImage3DOES wrap_glTexSubImage3DOES -#define glTextureStorage1DEXT wrap_glTextureStorage1DEXT -#define glTextureStorage2DEXT wrap_glTextureStorage2DEXT -#define glTextureStorage3DEXT wrap_glTextureStorage3DEXT -#define glTextureViewEXT wrap_glTextureViewEXT -#define glTextureViewOES wrap_glTextureViewOES -#define glTransformFeedbackVaryings wrap_glTransformFeedbackVaryings -#define glTransformPathNV wrap_glTransformPathNV -#define glTranslatef wrap_glTranslatef -#define glTranslatex wrap_glTranslatex -#define glTranslatexOES wrap_glTranslatexOES -#define glUniform1f wrap_glUniform1f -#define glUniform1fv wrap_glUniform1fv -#define glUniform1i wrap_glUniform1i -#define glUniform1iv wrap_glUniform1iv -#define glUniform1ui wrap_glUniform1ui -#define glUniform1uiv wrap_glUniform1uiv -#define glUniform2f wrap_glUniform2f -#define glUniform2fv wrap_glUniform2fv -#define glUniform2i wrap_glUniform2i -#define glUniform2iv wrap_glUniform2iv -#define glUniform2ui wrap_glUniform2ui -#define glUniform2uiv wrap_glUniform2uiv -#define glUniform3f wrap_glUniform3f -#define glUniform3fv wrap_glUniform3fv -#define glUniform3i wrap_glUniform3i -#define glUniform3iv wrap_glUniform3iv -#define glUniform3ui wrap_glUniform3ui -#define glUniform3uiv wrap_glUniform3uiv -#define glUniform4f wrap_glUniform4f -#define glUniform4fv wrap_glUniform4fv -#define glUniform4i wrap_glUniform4i -#define glUniform4iv wrap_glUniform4iv -#define glUniform4ui wrap_glUniform4ui -#define glUniform4uiv wrap_glUniform4uiv -#define glUniformBlockBinding wrap_glUniformBlockBinding -#define glUniformHandleui64NV wrap_glUniformHandleui64NV -#define glUniformHandleui64vNV wrap_glUniformHandleui64vNV -#define glUniformMatrix2fv wrap_glUniformMatrix2fv -#define glUniformMatrix2x3fv wrap_glUniformMatrix2x3fv -#define glUniformMatrix2x3fvNV wrap_glUniformMatrix2x3fvNV -#define glUniformMatrix2x4fv wrap_glUniformMatrix2x4fv -#define glUniformMatrix2x4fvNV wrap_glUniformMatrix2x4fvNV -#define glUniformMatrix3fv wrap_glUniformMatrix3fv -#define glUniformMatrix3x2fv wrap_glUniformMatrix3x2fv -#define glUniformMatrix3x2fvNV wrap_glUniformMatrix3x2fvNV -#define glUniformMatrix3x4fv wrap_glUniformMatrix3x4fv -#define glUniformMatrix3x4fvNV wrap_glUniformMatrix3x4fvNV -#define glUniformMatrix4fv wrap_glUniformMatrix4fv -#define glUniformMatrix4x2fv wrap_glUniformMatrix4x2fv -#define glUniformMatrix4x2fvNV wrap_glUniformMatrix4x2fvNV -#define glUniformMatrix4x3fv wrap_glUniformMatrix4x3fv -#define glUniformMatrix4x3fvNV wrap_glUniformMatrix4x3fvNV -#define glUnmapBuffer wrap_glUnmapBuffer -#define glUnmapBufferOES wrap_glUnmapBufferOES -#define glUseProgram wrap_glUseProgram -#define glUseProgramStages wrap_glUseProgramStages -#define glUseProgramStagesEXT wrap_glUseProgramStagesEXT -#define glValidateProgram wrap_glValidateProgram -#define glValidateProgramPipeline wrap_glValidateProgramPipeline -#define glValidateProgramPipelineEXT wrap_glValidateProgramPipelineEXT -#define glVertexAttrib1f wrap_glVertexAttrib1f -#define glVertexAttrib1fv wrap_glVertexAttrib1fv -#define glVertexAttrib2f wrap_glVertexAttrib2f -#define glVertexAttrib2fv wrap_glVertexAttrib2fv -#define glVertexAttrib3f wrap_glVertexAttrib3f -#define glVertexAttrib3fv wrap_glVertexAttrib3fv -#define glVertexAttrib4f wrap_glVertexAttrib4f -#define glVertexAttrib4fv wrap_glVertexAttrib4fv -#define glVertexAttribBinding wrap_glVertexAttribBinding -#define glVertexAttribDivisor wrap_glVertexAttribDivisor -#define glVertexAttribDivisorANGLE wrap_glVertexAttribDivisorANGLE -#define glVertexAttribDivisorEXT wrap_glVertexAttribDivisorEXT -#define glVertexAttribDivisorNV wrap_glVertexAttribDivisorNV -#define glVertexAttribFormat wrap_glVertexAttribFormat -#define glVertexAttribI4i wrap_glVertexAttribI4i -#define glVertexAttribI4iv wrap_glVertexAttribI4iv -#define glVertexAttribI4ui wrap_glVertexAttribI4ui -#define glVertexAttribI4uiv wrap_glVertexAttribI4uiv -#define glVertexAttribIFormat wrap_glVertexAttribIFormat -#define glVertexAttribIPointer wrap_glVertexAttribIPointer -#define glVertexAttribPointer wrap_glVertexAttribPointer -#define glVertexBindingDivisor wrap_glVertexBindingDivisor -#define glVertexPointer wrap_glVertexPointer -#define glViewport wrap_glViewport -#define glViewportArrayvNV wrap_glViewportArrayvNV -#define glViewportIndexedfNV wrap_glViewportIndexedfNV -#define glViewportIndexedfvNV wrap_glViewportIndexedfvNV -#define glWaitSync wrap_glWaitSync -#define glWaitSyncAPPLE wrap_glWaitSyncAPPLE -#define glWeightPathsNV wrap_glWeightPathsNV -#define glWeightPointerOES wrap_glWeightPointerOES \ No newline at end of file diff --git a/libs/hwui/debug/gles_stubs.in b/libs/hwui/debug/gles_stubs.in deleted file mode 100644 index 7cba0c115814..000000000000 --- a/libs/hwui/debug/gles_stubs.in +++ /dev/null @@ -1,1629 +0,0 @@ -void API_ENTRY(glActiveTexture)(GLenum texture) { - CALL_GL_API(glActiveTexture, texture); -} -void API_ENTRY(glAttachShader)(GLuint program, GLuint shader) { - CALL_GL_API(glAttachShader, program, shader); -} -void API_ENTRY(glBindAttribLocation)(GLuint program, GLuint index, const GLchar *name) { - CALL_GL_API(glBindAttribLocation, program, index, name); -} -void API_ENTRY(glBindBuffer)(GLenum target, GLuint buffer) { - CALL_GL_API(glBindBuffer, target, buffer); -} -void API_ENTRY(glBindFramebuffer)(GLenum target, GLuint framebuffer) { - CALL_GL_API(glBindFramebuffer, target, framebuffer); -} -void API_ENTRY(glBindRenderbuffer)(GLenum target, GLuint renderbuffer) { - CALL_GL_API(glBindRenderbuffer, target, renderbuffer); -} -void API_ENTRY(glBindTexture)(GLenum target, GLuint texture) { - CALL_GL_API(glBindTexture, target, texture); -} -void API_ENTRY(glBlendColor)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) { - CALL_GL_API(glBlendColor, red, green, blue, alpha); -} -void API_ENTRY(glBlendEquation)(GLenum mode) { - CALL_GL_API(glBlendEquation, mode); -} -void API_ENTRY(glBlendEquationSeparate)(GLenum modeRGB, GLenum modeAlpha) { - CALL_GL_API(glBlendEquationSeparate, modeRGB, modeAlpha); -} -void API_ENTRY(glBlendFunc)(GLenum sfactor, GLenum dfactor) { - CALL_GL_API(glBlendFunc, sfactor, dfactor); -} -void API_ENTRY(glBlendFuncSeparate)(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha) { - CALL_GL_API(glBlendFuncSeparate, sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha); -} -void API_ENTRY(glBufferData)(GLenum target, GLsizeiptr size, const void *data, GLenum usage) { - CALL_GL_API(glBufferData, target, size, data, usage); -} -void API_ENTRY(glBufferSubData)(GLenum target, GLintptr offset, GLsizeiptr size, const void *data) { - CALL_GL_API(glBufferSubData, target, offset, size, data); -} -GLenum API_ENTRY(glCheckFramebufferStatus)(GLenum target) { - CALL_GL_API_RETURN(glCheckFramebufferStatus, target); -} -void API_ENTRY(glClear)(GLbitfield mask) { - CALL_GL_API(glClear, mask); -} -void API_ENTRY(glClearColor)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) { - CALL_GL_API(glClearColor, red, green, blue, alpha); -} -void API_ENTRY(glClearDepthf)(GLfloat d) { - CALL_GL_API(glClearDepthf, d); -} -void API_ENTRY(glClearStencil)(GLint s) { - CALL_GL_API(glClearStencil, s); -} -void API_ENTRY(glColorMask)(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) { - CALL_GL_API(glColorMask, red, green, blue, alpha); -} -void API_ENTRY(glCompileShader)(GLuint shader) { - CALL_GL_API(glCompileShader, shader); -} -void API_ENTRY(glCompressedTexImage2D)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data) { - CALL_GL_API(glCompressedTexImage2D, target, level, internalformat, width, height, border, imageSize, data); -} -void API_ENTRY(glCompressedTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data) { - CALL_GL_API(glCompressedTexSubImage2D, target, level, xoffset, yoffset, width, height, format, imageSize, data); -} -void API_ENTRY(glCopyTexImage2D)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) { - CALL_GL_API(glCopyTexImage2D, target, level, internalformat, x, y, width, height, border); -} -void API_ENTRY(glCopyTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) { - CALL_GL_API(glCopyTexSubImage2D, target, level, xoffset, yoffset, x, y, width, height); -} -GLuint API_ENTRY(glCreateProgram)(void) { - CALL_GL_API_RETURN(glCreateProgram); -} -GLuint API_ENTRY(glCreateShader)(GLenum type) { - CALL_GL_API_RETURN(glCreateShader, type); -} -void API_ENTRY(glCullFace)(GLenum mode) { - CALL_GL_API(glCullFace, mode); -} -void API_ENTRY(glDeleteBuffers)(GLsizei n, const GLuint *buffers) { - CALL_GL_API(glDeleteBuffers, n, buffers); -} -void API_ENTRY(glDeleteFramebuffers)(GLsizei n, const GLuint *framebuffers) { - CALL_GL_API(glDeleteFramebuffers, n, framebuffers); -} -void API_ENTRY(glDeleteProgram)(GLuint program) { - CALL_GL_API(glDeleteProgram, program); -} -void API_ENTRY(glDeleteRenderbuffers)(GLsizei n, const GLuint *renderbuffers) { - CALL_GL_API(glDeleteRenderbuffers, n, renderbuffers); -} -void API_ENTRY(glDeleteShader)(GLuint shader) { - CALL_GL_API(glDeleteShader, shader); -} -void API_ENTRY(glDeleteTextures)(GLsizei n, const GLuint *textures) { - CALL_GL_API(glDeleteTextures, n, textures); -} -void API_ENTRY(glDepthFunc)(GLenum func) { - CALL_GL_API(glDepthFunc, func); -} -void API_ENTRY(glDepthMask)(GLboolean flag) { - CALL_GL_API(glDepthMask, flag); -} -void API_ENTRY(glDepthRangef)(GLfloat n, GLfloat f) { - CALL_GL_API(glDepthRangef, n, f); -} -void API_ENTRY(glDetachShader)(GLuint program, GLuint shader) { - CALL_GL_API(glDetachShader, program, shader); -} -void API_ENTRY(glDisable)(GLenum cap) { - CALL_GL_API(glDisable, cap); -} -void API_ENTRY(glDisableVertexAttribArray)(GLuint index) { - CALL_GL_API(glDisableVertexAttribArray, index); -} -void API_ENTRY(glDrawArrays)(GLenum mode, GLint first, GLsizei count) { - CALL_GL_API(glDrawArrays, mode, first, count); -} -void API_ENTRY(glDrawElements)(GLenum mode, GLsizei count, GLenum type, const void *indices) { - CALL_GL_API(glDrawElements, mode, count, type, indices); -} -void API_ENTRY(glEnable)(GLenum cap) { - CALL_GL_API(glEnable, cap); -} -void API_ENTRY(glEnableVertexAttribArray)(GLuint index) { - CALL_GL_API(glEnableVertexAttribArray, index); -} -void API_ENTRY(glFinish)(void) { - CALL_GL_API(glFinish); -} -void API_ENTRY(glFlush)(void) { - CALL_GL_API(glFlush); -} -void API_ENTRY(glFramebufferRenderbuffer)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) { - CALL_GL_API(glFramebufferRenderbuffer, target, attachment, renderbuffertarget, renderbuffer); -} -void API_ENTRY(glFramebufferTexture2D)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) { - CALL_GL_API(glFramebufferTexture2D, target, attachment, textarget, texture, level); -} -void API_ENTRY(glFrontFace)(GLenum mode) { - CALL_GL_API(glFrontFace, mode); -} -void API_ENTRY(glGenBuffers)(GLsizei n, GLuint *buffers) { - CALL_GL_API(glGenBuffers, n, buffers); -} -void API_ENTRY(glGenerateMipmap)(GLenum target) { - CALL_GL_API(glGenerateMipmap, target); -} -void API_ENTRY(glGenFramebuffers)(GLsizei n, GLuint *framebuffers) { - CALL_GL_API(glGenFramebuffers, n, framebuffers); -} -void API_ENTRY(glGenRenderbuffers)(GLsizei n, GLuint *renderbuffers) { - CALL_GL_API(glGenRenderbuffers, n, renderbuffers); -} -void API_ENTRY(glGenTextures)(GLsizei n, GLuint *textures) { - CALL_GL_API(glGenTextures, n, textures); -} -void API_ENTRY(glGetActiveAttrib)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name) { - CALL_GL_API(glGetActiveAttrib, program, index, bufSize, length, size, type, name); -} -void API_ENTRY(glGetActiveUniform)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name) { - CALL_GL_API(glGetActiveUniform, program, index, bufSize, length, size, type, name); -} -void API_ENTRY(glGetAttachedShaders)(GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders) { - CALL_GL_API(glGetAttachedShaders, program, maxCount, count, shaders); -} -GLint API_ENTRY(glGetAttribLocation)(GLuint program, const GLchar *name) { - CALL_GL_API_RETURN(glGetAttribLocation, program, name); -} -void API_ENTRY(glGetBooleanv)(GLenum pname, GLboolean *data) { - CALL_GL_API(glGetBooleanv, pname, data); -} -void API_ENTRY(glGetBufferParameteriv)(GLenum target, GLenum pname, GLint *params) { - CALL_GL_API(glGetBufferParameteriv, target, pname, params); -} -GLenum API_ENTRY(glGetError)(void) { - CALL_GL_API_RETURN(glGetError); -} -void API_ENTRY(glGetFloatv)(GLenum pname, GLfloat *data) { - CALL_GL_API(glGetFloatv, pname, data); -} -void API_ENTRY(glGetFramebufferAttachmentParameteriv)(GLenum target, GLenum attachment, GLenum pname, GLint *params) { - CALL_GL_API(glGetFramebufferAttachmentParameteriv, target, attachment, pname, params); -} -void API_ENTRY(glGetIntegerv)(GLenum pname, GLint *data) { - CALL_GL_API(glGetIntegerv, pname, data); -} -void API_ENTRY(glGetProgramiv)(GLuint program, GLenum pname, GLint *params) { - CALL_GL_API(glGetProgramiv, program, pname, params); -} -void API_ENTRY(glGetProgramInfoLog)(GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog) { - CALL_GL_API(glGetProgramInfoLog, program, bufSize, length, infoLog); -} -void API_ENTRY(glGetRenderbufferParameteriv)(GLenum target, GLenum pname, GLint *params) { - CALL_GL_API(glGetRenderbufferParameteriv, target, pname, params); -} -void API_ENTRY(glGetShaderiv)(GLuint shader, GLenum pname, GLint *params) { - CALL_GL_API(glGetShaderiv, shader, pname, params); -} -void API_ENTRY(glGetShaderInfoLog)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog) { - CALL_GL_API(glGetShaderInfoLog, shader, bufSize, length, infoLog); -} -void API_ENTRY(glGetShaderPrecisionFormat)(GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision) { - CALL_GL_API(glGetShaderPrecisionFormat, shadertype, precisiontype, range, precision); -} -void API_ENTRY(glGetShaderSource)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source) { - CALL_GL_API(glGetShaderSource, shader, bufSize, length, source); -} -const GLubyte * API_ENTRY(glGetString)(GLenum name) { - CALL_GL_API_RETURN(glGetString, name); -} -void API_ENTRY(glGetTexParameterfv)(GLenum target, GLenum pname, GLfloat *params) { - CALL_GL_API(glGetTexParameterfv, target, pname, params); -} -void API_ENTRY(glGetTexParameteriv)(GLenum target, GLenum pname, GLint *params) { - CALL_GL_API(glGetTexParameteriv, target, pname, params); -} -void API_ENTRY(glGetUniformfv)(GLuint program, GLint location, GLfloat *params) { - CALL_GL_API(glGetUniformfv, program, location, params); -} -void API_ENTRY(glGetUniformiv)(GLuint program, GLint location, GLint *params) { - CALL_GL_API(glGetUniformiv, program, location, params); -} -GLint API_ENTRY(glGetUniformLocation)(GLuint program, const GLchar *name) { - CALL_GL_API_RETURN(glGetUniformLocation, program, name); -} -void API_ENTRY(glGetVertexAttribfv)(GLuint index, GLenum pname, GLfloat *params) { - CALL_GL_API(glGetVertexAttribfv, index, pname, params); -} -void API_ENTRY(glGetVertexAttribiv)(GLuint index, GLenum pname, GLint *params) { - CALL_GL_API(glGetVertexAttribiv, index, pname, params); -} -void API_ENTRY(glGetVertexAttribPointerv)(GLuint index, GLenum pname, void **pointer) { - CALL_GL_API(glGetVertexAttribPointerv, index, pname, pointer); -} -void API_ENTRY(glHint)(GLenum target, GLenum mode) { - CALL_GL_API(glHint, target, mode); -} -GLboolean API_ENTRY(glIsBuffer)(GLuint buffer) { - CALL_GL_API_RETURN(glIsBuffer, buffer); -} -GLboolean API_ENTRY(glIsEnabled)(GLenum cap) { - CALL_GL_API_RETURN(glIsEnabled, cap); -} -GLboolean API_ENTRY(glIsFramebuffer)(GLuint framebuffer) { - CALL_GL_API_RETURN(glIsFramebuffer, framebuffer); -} -GLboolean API_ENTRY(glIsProgram)(GLuint program) { - CALL_GL_API_RETURN(glIsProgram, program); -} -GLboolean API_ENTRY(glIsRenderbuffer)(GLuint renderbuffer) { - CALL_GL_API_RETURN(glIsRenderbuffer, renderbuffer); -} -GLboolean API_ENTRY(glIsShader)(GLuint shader) { - CALL_GL_API_RETURN(glIsShader, shader); -} -GLboolean API_ENTRY(glIsTexture)(GLuint texture) { - CALL_GL_API_RETURN(glIsTexture, texture); -} -void API_ENTRY(glLineWidth)(GLfloat width) { - CALL_GL_API(glLineWidth, width); -} -void API_ENTRY(glLinkProgram)(GLuint program) { - CALL_GL_API(glLinkProgram, program); -} -void API_ENTRY(glPixelStorei)(GLenum pname, GLint param) { - CALL_GL_API(glPixelStorei, pname, param); -} -void API_ENTRY(glPolygonOffset)(GLfloat factor, GLfloat units) { - CALL_GL_API(glPolygonOffset, factor, units); -} -void API_ENTRY(glReadPixels)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels) { - CALL_GL_API(glReadPixels, x, y, width, height, format, type, pixels); -} -void API_ENTRY(glReleaseShaderCompiler)(void) { - CALL_GL_API(glReleaseShaderCompiler); -} -void API_ENTRY(glRenderbufferStorage)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) { - CALL_GL_API(glRenderbufferStorage, target, internalformat, width, height); -} -void API_ENTRY(glSampleCoverage)(GLfloat value, GLboolean invert) { - CALL_GL_API(glSampleCoverage, value, invert); -} -void API_ENTRY(glScissor)(GLint x, GLint y, GLsizei width, GLsizei height) { - CALL_GL_API(glScissor, x, y, width, height); -} -void API_ENTRY(glShaderBinary)(GLsizei count, const GLuint *shaders, GLenum binaryformat, const void *binary, GLsizei length) { - CALL_GL_API(glShaderBinary, count, shaders, binaryformat, binary, length); -} -void API_ENTRY(glShaderSource)(GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length) { - CALL_GL_API(glShaderSource, shader, count, string, length); -} -void API_ENTRY(glStencilFunc)(GLenum func, GLint ref, GLuint mask) { - CALL_GL_API(glStencilFunc, func, ref, mask); -} -void API_ENTRY(glStencilFuncSeparate)(GLenum face, GLenum func, GLint ref, GLuint mask) { - CALL_GL_API(glStencilFuncSeparate, face, func, ref, mask); -} -void API_ENTRY(glStencilMask)(GLuint mask) { - CALL_GL_API(glStencilMask, mask); -} -void API_ENTRY(glStencilMaskSeparate)(GLenum face, GLuint mask) { - CALL_GL_API(glStencilMaskSeparate, face, mask); -} -void API_ENTRY(glStencilOp)(GLenum fail, GLenum zfail, GLenum zpass) { - CALL_GL_API(glStencilOp, fail, zfail, zpass); -} -void API_ENTRY(glStencilOpSeparate)(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass) { - CALL_GL_API(glStencilOpSeparate, face, sfail, dpfail, dppass); -} -void API_ENTRY(glTexImage2D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels) { - CALL_GL_API(glTexImage2D, target, level, internalformat, width, height, border, format, type, pixels); -} -void API_ENTRY(glTexParameterf)(GLenum target, GLenum pname, GLfloat param) { - CALL_GL_API(glTexParameterf, target, pname, param); -} -void API_ENTRY(glTexParameterfv)(GLenum target, GLenum pname, const GLfloat *params) { - CALL_GL_API(glTexParameterfv, target, pname, params); -} -void API_ENTRY(glTexParameteri)(GLenum target, GLenum pname, GLint param) { - CALL_GL_API(glTexParameteri, target, pname, param); -} -void API_ENTRY(glTexParameteriv)(GLenum target, GLenum pname, const GLint *params) { - CALL_GL_API(glTexParameteriv, target, pname, params); -} -void API_ENTRY(glTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels) { - CALL_GL_API(glTexSubImage2D, target, level, xoffset, yoffset, width, height, format, type, pixels); -} -void API_ENTRY(glUniform1f)(GLint location, GLfloat v0) { - CALL_GL_API(glUniform1f, location, v0); -} -void API_ENTRY(glUniform1fv)(GLint location, GLsizei count, const GLfloat *value) { - CALL_GL_API(glUniform1fv, location, count, value); -} -void API_ENTRY(glUniform1i)(GLint location, GLint v0) { - CALL_GL_API(glUniform1i, location, v0); -} -void API_ENTRY(glUniform1iv)(GLint location, GLsizei count, const GLint *value) { - CALL_GL_API(glUniform1iv, location, count, value); -} -void API_ENTRY(glUniform2f)(GLint location, GLfloat v0, GLfloat v1) { - CALL_GL_API(glUniform2f, location, v0, v1); -} -void API_ENTRY(glUniform2fv)(GLint location, GLsizei count, const GLfloat *value) { - CALL_GL_API(glUniform2fv, location, count, value); -} -void API_ENTRY(glUniform2i)(GLint location, GLint v0, GLint v1) { - CALL_GL_API(glUniform2i, location, v0, v1); -} -void API_ENTRY(glUniform2iv)(GLint location, GLsizei count, const GLint *value) { - CALL_GL_API(glUniform2iv, location, count, value); -} -void API_ENTRY(glUniform3f)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2) { - CALL_GL_API(glUniform3f, location, v0, v1, v2); -} -void API_ENTRY(glUniform3fv)(GLint location, GLsizei count, const GLfloat *value) { - CALL_GL_API(glUniform3fv, location, count, value); -} -void API_ENTRY(glUniform3i)(GLint location, GLint v0, GLint v1, GLint v2) { - CALL_GL_API(glUniform3i, location, v0, v1, v2); -} -void API_ENTRY(glUniform3iv)(GLint location, GLsizei count, const GLint *value) { - CALL_GL_API(glUniform3iv, location, count, value); -} -void API_ENTRY(glUniform4f)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) { - CALL_GL_API(glUniform4f, location, v0, v1, v2, v3); -} -void API_ENTRY(glUniform4fv)(GLint location, GLsizei count, const GLfloat *value) { - CALL_GL_API(glUniform4fv, location, count, value); -} -void API_ENTRY(glUniform4i)(GLint location, GLint v0, GLint v1, GLint v2, GLint v3) { - CALL_GL_API(glUniform4i, location, v0, v1, v2, v3); -} -void API_ENTRY(glUniform4iv)(GLint location, GLsizei count, const GLint *value) { - CALL_GL_API(glUniform4iv, location, count, value); -} -void API_ENTRY(glUniformMatrix2fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glUniformMatrix2fv, location, count, transpose, value); -} -void API_ENTRY(glUniformMatrix3fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glUniformMatrix3fv, location, count, transpose, value); -} -void API_ENTRY(glUniformMatrix4fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glUniformMatrix4fv, location, count, transpose, value); -} -void API_ENTRY(glUseProgram)(GLuint program) { - CALL_GL_API(glUseProgram, program); -} -void API_ENTRY(glValidateProgram)(GLuint program) { - CALL_GL_API(glValidateProgram, program); -} -void API_ENTRY(glVertexAttrib1f)(GLuint index, GLfloat x) { - CALL_GL_API(glVertexAttrib1f, index, x); -} -void API_ENTRY(glVertexAttrib1fv)(GLuint index, const GLfloat *v) { - CALL_GL_API(glVertexAttrib1fv, index, v); -} -void API_ENTRY(glVertexAttrib2f)(GLuint index, GLfloat x, GLfloat y) { - CALL_GL_API(glVertexAttrib2f, index, x, y); -} -void API_ENTRY(glVertexAttrib2fv)(GLuint index, const GLfloat *v) { - CALL_GL_API(glVertexAttrib2fv, index, v); -} -void API_ENTRY(glVertexAttrib3f)(GLuint index, GLfloat x, GLfloat y, GLfloat z) { - CALL_GL_API(glVertexAttrib3f, index, x, y, z); -} -void API_ENTRY(glVertexAttrib3fv)(GLuint index, const GLfloat *v) { - CALL_GL_API(glVertexAttrib3fv, index, v); -} -void API_ENTRY(glVertexAttrib4f)(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w) { - CALL_GL_API(glVertexAttrib4f, index, x, y, z, w); -} -void API_ENTRY(glVertexAttrib4fv)(GLuint index, const GLfloat *v) { - CALL_GL_API(glVertexAttrib4fv, index, v); -} -void API_ENTRY(glVertexAttribPointer)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer) { - CALL_GL_API(glVertexAttribPointer, index, size, type, normalized, stride, pointer); -} -void API_ENTRY(glViewport)(GLint x, GLint y, GLsizei width, GLsizei height) { - CALL_GL_API(glViewport, x, y, width, height); -} -void API_ENTRY(glReadBuffer)(GLenum src) { - CALL_GL_API(glReadBuffer, src); -} -void API_ENTRY(glDrawRangeElements)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices) { - CALL_GL_API(glDrawRangeElements, mode, start, end, count, type, indices); -} -void API_ENTRY(glTexImage3D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels) { - CALL_GL_API(glTexImage3D, target, level, internalformat, width, height, depth, border, format, type, pixels); -} -void API_ENTRY(glTexSubImage3D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels) { - CALL_GL_API(glTexSubImage3D, target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels); -} -void API_ENTRY(glCopyTexSubImage3D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height) { - CALL_GL_API(glCopyTexSubImage3D, target, level, xoffset, yoffset, zoffset, x, y, width, height); -} -void API_ENTRY(glCompressedTexImage3D)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data) { - CALL_GL_API(glCompressedTexImage3D, target, level, internalformat, width, height, depth, border, imageSize, data); -} -void API_ENTRY(glCompressedTexSubImage3D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data) { - CALL_GL_API(glCompressedTexSubImage3D, target, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, data); -} -void API_ENTRY(glGenQueries)(GLsizei n, GLuint *ids) { - CALL_GL_API(glGenQueries, n, ids); -} -void API_ENTRY(glDeleteQueries)(GLsizei n, const GLuint *ids) { - CALL_GL_API(glDeleteQueries, n, ids); -} -GLboolean API_ENTRY(glIsQuery)(GLuint id) { - CALL_GL_API_RETURN(glIsQuery, id); -} -void API_ENTRY(glBeginQuery)(GLenum target, GLuint id) { - CALL_GL_API(glBeginQuery, target, id); -} -void API_ENTRY(glEndQuery)(GLenum target) { - CALL_GL_API(glEndQuery, target); -} -void API_ENTRY(glGetQueryiv)(GLenum target, GLenum pname, GLint *params) { - CALL_GL_API(glGetQueryiv, target, pname, params); -} -void API_ENTRY(glGetQueryObjectuiv)(GLuint id, GLenum pname, GLuint *params) { - CALL_GL_API(glGetQueryObjectuiv, id, pname, params); -} -GLboolean API_ENTRY(glUnmapBuffer)(GLenum target) { - CALL_GL_API_RETURN(glUnmapBuffer, target); -} -void API_ENTRY(glGetBufferPointerv)(GLenum target, GLenum pname, void **params) { - CALL_GL_API(glGetBufferPointerv, target, pname, params); -} -void API_ENTRY(glDrawBuffers)(GLsizei n, const GLenum *bufs) { - CALL_GL_API(glDrawBuffers, n, bufs); -} -void API_ENTRY(glUniformMatrix2x3fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glUniformMatrix2x3fv, location, count, transpose, value); -} -void API_ENTRY(glUniformMatrix3x2fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glUniformMatrix3x2fv, location, count, transpose, value); -} -void API_ENTRY(glUniformMatrix2x4fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glUniformMatrix2x4fv, location, count, transpose, value); -} -void API_ENTRY(glUniformMatrix4x2fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glUniformMatrix4x2fv, location, count, transpose, value); -} -void API_ENTRY(glUniformMatrix3x4fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glUniformMatrix3x4fv, location, count, transpose, value); -} -void API_ENTRY(glUniformMatrix4x3fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glUniformMatrix4x3fv, location, count, transpose, value); -} -void API_ENTRY(glBlitFramebuffer)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) { - CALL_GL_API(glBlitFramebuffer, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); -} -void API_ENTRY(glRenderbufferStorageMultisample)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) { - CALL_GL_API(glRenderbufferStorageMultisample, target, samples, internalformat, width, height); -} -void API_ENTRY(glFramebufferTextureLayer)(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer) { - CALL_GL_API(glFramebufferTextureLayer, target, attachment, texture, level, layer); -} -void * API_ENTRY(glMapBufferRange)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) { - CALL_GL_API_RETURN(glMapBufferRange, target, offset, length, access); -} -void API_ENTRY(glFlushMappedBufferRange)(GLenum target, GLintptr offset, GLsizeiptr length) { - CALL_GL_API(glFlushMappedBufferRange, target, offset, length); -} -void API_ENTRY(glBindVertexArray)(GLuint array) { - CALL_GL_API(glBindVertexArray, array); -} -void API_ENTRY(glDeleteVertexArrays)(GLsizei n, const GLuint *arrays) { - CALL_GL_API(glDeleteVertexArrays, n, arrays); -} -void API_ENTRY(glGenVertexArrays)(GLsizei n, GLuint *arrays) { - CALL_GL_API(glGenVertexArrays, n, arrays); -} -GLboolean API_ENTRY(glIsVertexArray)(GLuint array) { - CALL_GL_API_RETURN(glIsVertexArray, array); -} -void API_ENTRY(glGetIntegeri_v)(GLenum target, GLuint index, GLint *data) { - CALL_GL_API(glGetIntegeri_v, target, index, data); -} -void API_ENTRY(glBeginTransformFeedback)(GLenum primitiveMode) { - CALL_GL_API(glBeginTransformFeedback, primitiveMode); -} -void API_ENTRY(glEndTransformFeedback)(void) { - CALL_GL_API(glEndTransformFeedback); -} -void API_ENTRY(glBindBufferRange)(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size) { - CALL_GL_API(glBindBufferRange, target, index, buffer, offset, size); -} -void API_ENTRY(glBindBufferBase)(GLenum target, GLuint index, GLuint buffer) { - CALL_GL_API(glBindBufferBase, target, index, buffer); -} -void API_ENTRY(glTransformFeedbackVaryings)(GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode) { - CALL_GL_API(glTransformFeedbackVaryings, program, count, varyings, bufferMode); -} -void API_ENTRY(glGetTransformFeedbackVarying)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name) { - CALL_GL_API(glGetTransformFeedbackVarying, program, index, bufSize, length, size, type, name); -} -void API_ENTRY(glVertexAttribIPointer)(GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer) { - CALL_GL_API(glVertexAttribIPointer, index, size, type, stride, pointer); -} -void API_ENTRY(glGetVertexAttribIiv)(GLuint index, GLenum pname, GLint *params) { - CALL_GL_API(glGetVertexAttribIiv, index, pname, params); -} -void API_ENTRY(glGetVertexAttribIuiv)(GLuint index, GLenum pname, GLuint *params) { - CALL_GL_API(glGetVertexAttribIuiv, index, pname, params); -} -void API_ENTRY(glVertexAttribI4i)(GLuint index, GLint x, GLint y, GLint z, GLint w) { - CALL_GL_API(glVertexAttribI4i, index, x, y, z, w); -} -void API_ENTRY(glVertexAttribI4ui)(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w) { - CALL_GL_API(glVertexAttribI4ui, index, x, y, z, w); -} -void API_ENTRY(glVertexAttribI4iv)(GLuint index, const GLint *v) { - CALL_GL_API(glVertexAttribI4iv, index, v); -} -void API_ENTRY(glVertexAttribI4uiv)(GLuint index, const GLuint *v) { - CALL_GL_API(glVertexAttribI4uiv, index, v); -} -void API_ENTRY(glGetUniformuiv)(GLuint program, GLint location, GLuint *params) { - CALL_GL_API(glGetUniformuiv, program, location, params); -} -GLint API_ENTRY(glGetFragDataLocation)(GLuint program, const GLchar *name) { - CALL_GL_API_RETURN(glGetFragDataLocation, program, name); -} -void API_ENTRY(glUniform1ui)(GLint location, GLuint v0) { - CALL_GL_API(glUniform1ui, location, v0); -} -void API_ENTRY(glUniform2ui)(GLint location, GLuint v0, GLuint v1) { - CALL_GL_API(glUniform2ui, location, v0, v1); -} -void API_ENTRY(glUniform3ui)(GLint location, GLuint v0, GLuint v1, GLuint v2) { - CALL_GL_API(glUniform3ui, location, v0, v1, v2); -} -void API_ENTRY(glUniform4ui)(GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) { - CALL_GL_API(glUniform4ui, location, v0, v1, v2, v3); -} -void API_ENTRY(glUniform1uiv)(GLint location, GLsizei count, const GLuint *value) { - CALL_GL_API(glUniform1uiv, location, count, value); -} -void API_ENTRY(glUniform2uiv)(GLint location, GLsizei count, const GLuint *value) { - CALL_GL_API(glUniform2uiv, location, count, value); -} -void API_ENTRY(glUniform3uiv)(GLint location, GLsizei count, const GLuint *value) { - CALL_GL_API(glUniform3uiv, location, count, value); -} -void API_ENTRY(glUniform4uiv)(GLint location, GLsizei count, const GLuint *value) { - CALL_GL_API(glUniform4uiv, location, count, value); -} -void API_ENTRY(glClearBufferiv)(GLenum buffer, GLint drawbuffer, const GLint *value) { - CALL_GL_API(glClearBufferiv, buffer, drawbuffer, value); -} -void API_ENTRY(glClearBufferuiv)(GLenum buffer, GLint drawbuffer, const GLuint *value) { - CALL_GL_API(glClearBufferuiv, buffer, drawbuffer, value); -} -void API_ENTRY(glClearBufferfv)(GLenum buffer, GLint drawbuffer, const GLfloat *value) { - CALL_GL_API(glClearBufferfv, buffer, drawbuffer, value); -} -void API_ENTRY(glClearBufferfi)(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) { - CALL_GL_API(glClearBufferfi, buffer, drawbuffer, depth, stencil); -} -const GLubyte * API_ENTRY(glGetStringi)(GLenum name, GLuint index) { - CALL_GL_API_RETURN(glGetStringi, name, index); -} -void API_ENTRY(glCopyBufferSubData)(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size) { - CALL_GL_API(glCopyBufferSubData, readTarget, writeTarget, readOffset, writeOffset, size); -} -void API_ENTRY(glGetUniformIndices)(GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices) { - CALL_GL_API(glGetUniformIndices, program, uniformCount, uniformNames, uniformIndices); -} -void API_ENTRY(glGetActiveUniformsiv)(GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params) { - CALL_GL_API(glGetActiveUniformsiv, program, uniformCount, uniformIndices, pname, params); -} -GLuint API_ENTRY(glGetUniformBlockIndex)(GLuint program, const GLchar *uniformBlockName) { - CALL_GL_API_RETURN(glGetUniformBlockIndex, program, uniformBlockName); -} -void API_ENTRY(glGetActiveUniformBlockiv)(GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params) { - CALL_GL_API(glGetActiveUniformBlockiv, program, uniformBlockIndex, pname, params); -} -void API_ENTRY(glGetActiveUniformBlockName)(GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName) { - CALL_GL_API(glGetActiveUniformBlockName, program, uniformBlockIndex, bufSize, length, uniformBlockName); -} -void API_ENTRY(glUniformBlockBinding)(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding) { - CALL_GL_API(glUniformBlockBinding, program, uniformBlockIndex, uniformBlockBinding); -} -void API_ENTRY(glDrawArraysInstanced)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount) { - CALL_GL_API(glDrawArraysInstanced, mode, first, count, instancecount); -} -void API_ENTRY(glDrawElementsInstanced)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount) { - CALL_GL_API(glDrawElementsInstanced, mode, count, type, indices, instancecount); -} -GLsync API_ENTRY(glFenceSync)(GLenum condition, GLbitfield flags) { - CALL_GL_API_RETURN(glFenceSync, condition, flags); -} -GLboolean API_ENTRY(glIsSync)(GLsync sync) { - CALL_GL_API_RETURN(glIsSync, sync); -} -void API_ENTRY(glDeleteSync)(GLsync sync) { - CALL_GL_API(glDeleteSync, sync); -} -GLenum API_ENTRY(glClientWaitSync)(GLsync sync, GLbitfield flags, GLuint64 timeout) { - CALL_GL_API_RETURN(glClientWaitSync, sync, flags, timeout); -} -void API_ENTRY(glWaitSync)(GLsync sync, GLbitfield flags, GLuint64 timeout) { - CALL_GL_API(glWaitSync, sync, flags, timeout); -} -void API_ENTRY(glGetInteger64v)(GLenum pname, GLint64 *data) { - CALL_GL_API(glGetInteger64v, pname, data); -} -void API_ENTRY(glGetSynciv)(GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values) { - CALL_GL_API(glGetSynciv, sync, pname, bufSize, length, values); -} -void API_ENTRY(glGetInteger64i_v)(GLenum target, GLuint index, GLint64 *data) { - CALL_GL_API(glGetInteger64i_v, target, index, data); -} -void API_ENTRY(glGetBufferParameteri64v)(GLenum target, GLenum pname, GLint64 *params) { - CALL_GL_API(glGetBufferParameteri64v, target, pname, params); -} -void API_ENTRY(glGenSamplers)(GLsizei count, GLuint *samplers) { - CALL_GL_API(glGenSamplers, count, samplers); -} -void API_ENTRY(glDeleteSamplers)(GLsizei count, const GLuint *samplers) { - CALL_GL_API(glDeleteSamplers, count, samplers); -} -GLboolean API_ENTRY(glIsSampler)(GLuint sampler) { - CALL_GL_API_RETURN(glIsSampler, sampler); -} -void API_ENTRY(glBindSampler)(GLuint unit, GLuint sampler) { - CALL_GL_API(glBindSampler, unit, sampler); -} -void API_ENTRY(glSamplerParameteri)(GLuint sampler, GLenum pname, GLint param) { - CALL_GL_API(glSamplerParameteri, sampler, pname, param); -} -void API_ENTRY(glSamplerParameteriv)(GLuint sampler, GLenum pname, const GLint *param) { - CALL_GL_API(glSamplerParameteriv, sampler, pname, param); -} -void API_ENTRY(glSamplerParameterf)(GLuint sampler, GLenum pname, GLfloat param) { - CALL_GL_API(glSamplerParameterf, sampler, pname, param); -} -void API_ENTRY(glSamplerParameterfv)(GLuint sampler, GLenum pname, const GLfloat *param) { - CALL_GL_API(glSamplerParameterfv, sampler, pname, param); -} -void API_ENTRY(glGetSamplerParameteriv)(GLuint sampler, GLenum pname, GLint *params) { - CALL_GL_API(glGetSamplerParameteriv, sampler, pname, params); -} -void API_ENTRY(glGetSamplerParameterfv)(GLuint sampler, GLenum pname, GLfloat *params) { - CALL_GL_API(glGetSamplerParameterfv, sampler, pname, params); -} -void API_ENTRY(glVertexAttribDivisor)(GLuint index, GLuint divisor) { - CALL_GL_API(glVertexAttribDivisor, index, divisor); -} -void API_ENTRY(glBindTransformFeedback)(GLenum target, GLuint id) { - CALL_GL_API(glBindTransformFeedback, target, id); -} -void API_ENTRY(glDeleteTransformFeedbacks)(GLsizei n, const GLuint *ids) { - CALL_GL_API(glDeleteTransformFeedbacks, n, ids); -} -void API_ENTRY(glGenTransformFeedbacks)(GLsizei n, GLuint *ids) { - CALL_GL_API(glGenTransformFeedbacks, n, ids); -} -GLboolean API_ENTRY(glIsTransformFeedback)(GLuint id) { - CALL_GL_API_RETURN(glIsTransformFeedback, id); -} -void API_ENTRY(glPauseTransformFeedback)(void) { - CALL_GL_API(glPauseTransformFeedback); -} -void API_ENTRY(glResumeTransformFeedback)(void) { - CALL_GL_API(glResumeTransformFeedback); -} -void API_ENTRY(glGetProgramBinary)(GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary) { - CALL_GL_API(glGetProgramBinary, program, bufSize, length, binaryFormat, binary); -} -void API_ENTRY(glProgramBinary)(GLuint program, GLenum binaryFormat, const void *binary, GLsizei length) { - CALL_GL_API(glProgramBinary, program, binaryFormat, binary, length); -} -void API_ENTRY(glProgramParameteri)(GLuint program, GLenum pname, GLint value) { - CALL_GL_API(glProgramParameteri, program, pname, value); -} -void API_ENTRY(glInvalidateFramebuffer)(GLenum target, GLsizei numAttachments, const GLenum *attachments) { - CALL_GL_API(glInvalidateFramebuffer, target, numAttachments, attachments); -} -void API_ENTRY(glInvalidateSubFramebuffer)(GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height) { - CALL_GL_API(glInvalidateSubFramebuffer, target, numAttachments, attachments, x, y, width, height); -} -void API_ENTRY(glTexStorage2D)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) { - CALL_GL_API(glTexStorage2D, target, levels, internalformat, width, height); -} -void API_ENTRY(glTexStorage3D)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) { - CALL_GL_API(glTexStorage3D, target, levels, internalformat, width, height, depth); -} -void API_ENTRY(glGetInternalformativ)(GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint *params) { - CALL_GL_API(glGetInternalformativ, target, internalformat, pname, bufSize, params); -} -void API_ENTRY(glDispatchCompute)(GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z) { - CALL_GL_API(glDispatchCompute, num_groups_x, num_groups_y, num_groups_z); -} -void API_ENTRY(glDispatchComputeIndirect)(GLintptr indirect) { - CALL_GL_API(glDispatchComputeIndirect, indirect); -} -void API_ENTRY(glDrawArraysIndirect)(GLenum mode, const void *indirect) { - CALL_GL_API(glDrawArraysIndirect, mode, indirect); -} -void API_ENTRY(glDrawElementsIndirect)(GLenum mode, GLenum type, const void *indirect) { - CALL_GL_API(glDrawElementsIndirect, mode, type, indirect); -} -void API_ENTRY(glFramebufferParameteri)(GLenum target, GLenum pname, GLint param) { - CALL_GL_API(glFramebufferParameteri, target, pname, param); -} -void API_ENTRY(glGetFramebufferParameteriv)(GLenum target, GLenum pname, GLint *params) { - CALL_GL_API(glGetFramebufferParameteriv, target, pname, params); -} -void API_ENTRY(glGetProgramInterfaceiv)(GLuint program, GLenum programInterface, GLenum pname, GLint *params) { - CALL_GL_API(glGetProgramInterfaceiv, program, programInterface, pname, params); -} -GLuint API_ENTRY(glGetProgramResourceIndex)(GLuint program, GLenum programInterface, const GLchar *name) { - CALL_GL_API_RETURN(glGetProgramResourceIndex, program, programInterface, name); -} -void API_ENTRY(glGetProgramResourceName)(GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name) { - CALL_GL_API(glGetProgramResourceName, program, programInterface, index, bufSize, length, name); -} -void API_ENTRY(glGetProgramResourceiv)(GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLint *params) { - CALL_GL_API(glGetProgramResourceiv, program, programInterface, index, propCount, props, bufSize, length, params); -} -GLint API_ENTRY(glGetProgramResourceLocation)(GLuint program, GLenum programInterface, const GLchar *name) { - CALL_GL_API_RETURN(glGetProgramResourceLocation, program, programInterface, name); -} -void API_ENTRY(glUseProgramStages)(GLuint pipeline, GLbitfield stages, GLuint program) { - CALL_GL_API(glUseProgramStages, pipeline, stages, program); -} -void API_ENTRY(glActiveShaderProgram)(GLuint pipeline, GLuint program) { - CALL_GL_API(glActiveShaderProgram, pipeline, program); -} -GLuint API_ENTRY(glCreateShaderProgramv)(GLenum type, GLsizei count, const GLchar *const*strings) { - CALL_GL_API_RETURN(glCreateShaderProgramv, type, count, strings); -} -void API_ENTRY(glBindProgramPipeline)(GLuint pipeline) { - CALL_GL_API(glBindProgramPipeline, pipeline); -} -void API_ENTRY(glDeleteProgramPipelines)(GLsizei n, const GLuint *pipelines) { - CALL_GL_API(glDeleteProgramPipelines, n, pipelines); -} -void API_ENTRY(glGenProgramPipelines)(GLsizei n, GLuint *pipelines) { - CALL_GL_API(glGenProgramPipelines, n, pipelines); -} -GLboolean API_ENTRY(glIsProgramPipeline)(GLuint pipeline) { - CALL_GL_API_RETURN(glIsProgramPipeline, pipeline); -} -void API_ENTRY(glGetProgramPipelineiv)(GLuint pipeline, GLenum pname, GLint *params) { - CALL_GL_API(glGetProgramPipelineiv, pipeline, pname, params); -} -void API_ENTRY(glProgramUniform1i)(GLuint program, GLint location, GLint v0) { - CALL_GL_API(glProgramUniform1i, program, location, v0); -} -void API_ENTRY(glProgramUniform2i)(GLuint program, GLint location, GLint v0, GLint v1) { - CALL_GL_API(glProgramUniform2i, program, location, v0, v1); -} -void API_ENTRY(glProgramUniform3i)(GLuint program, GLint location, GLint v0, GLint v1, GLint v2) { - CALL_GL_API(glProgramUniform3i, program, location, v0, v1, v2); -} -void API_ENTRY(glProgramUniform4i)(GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3) { - CALL_GL_API(glProgramUniform4i, program, location, v0, v1, v2, v3); -} -void API_ENTRY(glProgramUniform1ui)(GLuint program, GLint location, GLuint v0) { - CALL_GL_API(glProgramUniform1ui, program, location, v0); -} -void API_ENTRY(glProgramUniform2ui)(GLuint program, GLint location, GLuint v0, GLuint v1) { - CALL_GL_API(glProgramUniform2ui, program, location, v0, v1); -} -void API_ENTRY(glProgramUniform3ui)(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2) { - CALL_GL_API(glProgramUniform3ui, program, location, v0, v1, v2); -} -void API_ENTRY(glProgramUniform4ui)(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) { - CALL_GL_API(glProgramUniform4ui, program, location, v0, v1, v2, v3); -} -void API_ENTRY(glProgramUniform1f)(GLuint program, GLint location, GLfloat v0) { - CALL_GL_API(glProgramUniform1f, program, location, v0); -} -void API_ENTRY(glProgramUniform2f)(GLuint program, GLint location, GLfloat v0, GLfloat v1) { - CALL_GL_API(glProgramUniform2f, program, location, v0, v1); -} -void API_ENTRY(glProgramUniform3f)(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2) { - CALL_GL_API(glProgramUniform3f, program, location, v0, v1, v2); -} -void API_ENTRY(glProgramUniform4f)(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) { - CALL_GL_API(glProgramUniform4f, program, location, v0, v1, v2, v3); -} -void API_ENTRY(glProgramUniform1iv)(GLuint program, GLint location, GLsizei count, const GLint *value) { - CALL_GL_API(glProgramUniform1iv, program, location, count, value); -} -void API_ENTRY(glProgramUniform2iv)(GLuint program, GLint location, GLsizei count, const GLint *value) { - CALL_GL_API(glProgramUniform2iv, program, location, count, value); -} -void API_ENTRY(glProgramUniform3iv)(GLuint program, GLint location, GLsizei count, const GLint *value) { - CALL_GL_API(glProgramUniform3iv, program, location, count, value); -} -void API_ENTRY(glProgramUniform4iv)(GLuint program, GLint location, GLsizei count, const GLint *value) { - CALL_GL_API(glProgramUniform4iv, program, location, count, value); -} -void API_ENTRY(glProgramUniform1uiv)(GLuint program, GLint location, GLsizei count, const GLuint *value) { - CALL_GL_API(glProgramUniform1uiv, program, location, count, value); -} -void API_ENTRY(glProgramUniform2uiv)(GLuint program, GLint location, GLsizei count, const GLuint *value) { - CALL_GL_API(glProgramUniform2uiv, program, location, count, value); -} -void API_ENTRY(glProgramUniform3uiv)(GLuint program, GLint location, GLsizei count, const GLuint *value) { - CALL_GL_API(glProgramUniform3uiv, program, location, count, value); -} -void API_ENTRY(glProgramUniform4uiv)(GLuint program, GLint location, GLsizei count, const GLuint *value) { - CALL_GL_API(glProgramUniform4uiv, program, location, count, value); -} -void API_ENTRY(glProgramUniform1fv)(GLuint program, GLint location, GLsizei count, const GLfloat *value) { - CALL_GL_API(glProgramUniform1fv, program, location, count, value); -} -void API_ENTRY(glProgramUniform2fv)(GLuint program, GLint location, GLsizei count, const GLfloat *value) { - CALL_GL_API(glProgramUniform2fv, program, location, count, value); -} -void API_ENTRY(glProgramUniform3fv)(GLuint program, GLint location, GLsizei count, const GLfloat *value) { - CALL_GL_API(glProgramUniform3fv, program, location, count, value); -} -void API_ENTRY(glProgramUniform4fv)(GLuint program, GLint location, GLsizei count, const GLfloat *value) { - CALL_GL_API(glProgramUniform4fv, program, location, count, value); -} -void API_ENTRY(glProgramUniformMatrix2fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glProgramUniformMatrix2fv, program, location, count, transpose, value); -} -void API_ENTRY(glProgramUniformMatrix3fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glProgramUniformMatrix3fv, program, location, count, transpose, value); -} -void API_ENTRY(glProgramUniformMatrix4fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glProgramUniformMatrix4fv, program, location, count, transpose, value); -} -void API_ENTRY(glProgramUniformMatrix2x3fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glProgramUniformMatrix2x3fv, program, location, count, transpose, value); -} -void API_ENTRY(glProgramUniformMatrix3x2fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glProgramUniformMatrix3x2fv, program, location, count, transpose, value); -} -void API_ENTRY(glProgramUniformMatrix2x4fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glProgramUniformMatrix2x4fv, program, location, count, transpose, value); -} -void API_ENTRY(glProgramUniformMatrix4x2fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glProgramUniformMatrix4x2fv, program, location, count, transpose, value); -} -void API_ENTRY(glProgramUniformMatrix3x4fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glProgramUniformMatrix3x4fv, program, location, count, transpose, value); -} -void API_ENTRY(glProgramUniformMatrix4x3fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glProgramUniformMatrix4x3fv, program, location, count, transpose, value); -} -void API_ENTRY(glValidateProgramPipeline)(GLuint pipeline) { - CALL_GL_API(glValidateProgramPipeline, pipeline); -} -void API_ENTRY(glGetProgramPipelineInfoLog)(GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog) { - CALL_GL_API(glGetProgramPipelineInfoLog, pipeline, bufSize, length, infoLog); -} -void API_ENTRY(glBindImageTexture)(GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format) { - CALL_GL_API(glBindImageTexture, unit, texture, level, layered, layer, access, format); -} -void API_ENTRY(glGetBooleani_v)(GLenum target, GLuint index, GLboolean *data) { - CALL_GL_API(glGetBooleani_v, target, index, data); -} -void API_ENTRY(glMemoryBarrier)(GLbitfield barriers) { - CALL_GL_API(glMemoryBarrier, barriers); -} -void API_ENTRY(glMemoryBarrierByRegion)(GLbitfield barriers) { - CALL_GL_API(glMemoryBarrierByRegion, barriers); -} -void API_ENTRY(glTexStorage2DMultisample)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations) { - CALL_GL_API(glTexStorage2DMultisample, target, samples, internalformat, width, height, fixedsamplelocations); -} -void API_ENTRY(glGetMultisamplefv)(GLenum pname, GLuint index, GLfloat *val) { - CALL_GL_API(glGetMultisamplefv, pname, index, val); -} -void API_ENTRY(glSampleMaski)(GLuint maskNumber, GLbitfield mask) { - CALL_GL_API(glSampleMaski, maskNumber, mask); -} -void API_ENTRY(glGetTexLevelParameteriv)(GLenum target, GLint level, GLenum pname, GLint *params) { - CALL_GL_API(glGetTexLevelParameteriv, target, level, pname, params); -} -void API_ENTRY(glGetTexLevelParameterfv)(GLenum target, GLint level, GLenum pname, GLfloat *params) { - CALL_GL_API(glGetTexLevelParameterfv, target, level, pname, params); -} -void API_ENTRY(glBindVertexBuffer)(GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride) { - CALL_GL_API(glBindVertexBuffer, bindingindex, buffer, offset, stride); -} -void API_ENTRY(glVertexAttribFormat)(GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset) { - CALL_GL_API(glVertexAttribFormat, attribindex, size, type, normalized, relativeoffset); -} -void API_ENTRY(glVertexAttribIFormat)(GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) { - CALL_GL_API(glVertexAttribIFormat, attribindex, size, type, relativeoffset); -} -void API_ENTRY(glVertexAttribBinding)(GLuint attribindex, GLuint bindingindex) { - CALL_GL_API(glVertexAttribBinding, attribindex, bindingindex); -} -void API_ENTRY(glVertexBindingDivisor)(GLuint bindingindex, GLuint divisor) { - CALL_GL_API(glVertexBindingDivisor, bindingindex, divisor); -} -void API_ENTRY(glBlendBarrier)(void) { - CALL_GL_API(glBlendBarrier); -} -void API_ENTRY(glCopyImageSubData)(GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth) { - CALL_GL_API(glCopyImageSubData, srcName, srcTarget, srcLevel, srcX, srcY, srcZ, dstName, dstTarget, dstLevel, dstX, dstY, dstZ, srcWidth, srcHeight, srcDepth); -} -void API_ENTRY(glDebugMessageControl)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled) { - CALL_GL_API(glDebugMessageControl, source, type, severity, count, ids, enabled); -} -void API_ENTRY(glDebugMessageInsert)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf) { - CALL_GL_API(glDebugMessageInsert, source, type, id, severity, length, buf); -} -void API_ENTRY(glDebugMessageCallback)(GLDEBUGPROC callback, const void *userParam) { - CALL_GL_API(glDebugMessageCallback, callback, userParam); -} -GLuint API_ENTRY(glGetDebugMessageLog)(GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog) { - CALL_GL_API_RETURN(glGetDebugMessageLog, count, bufSize, sources, types, ids, severities, lengths, messageLog); -} -void API_ENTRY(glPushDebugGroup)(GLenum source, GLuint id, GLsizei length, const GLchar *message) { - CALL_GL_API(glPushDebugGroup, source, id, length, message); -} -void API_ENTRY(glPopDebugGroup)(void) { - CALL_GL_API(glPopDebugGroup); -} -void API_ENTRY(glObjectLabel)(GLenum identifier, GLuint name, GLsizei length, const GLchar *label) { - CALL_GL_API(glObjectLabel, identifier, name, length, label); -} -void API_ENTRY(glGetObjectLabel)(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label) { - CALL_GL_API(glGetObjectLabel, identifier, name, bufSize, length, label); -} -void API_ENTRY(glObjectPtrLabel)(const void *ptr, GLsizei length, const GLchar *label) { - CALL_GL_API(glObjectPtrLabel, ptr, length, label); -} -void API_ENTRY(glGetObjectPtrLabel)(const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label) { - CALL_GL_API(glGetObjectPtrLabel, ptr, bufSize, length, label); -} -void API_ENTRY(glGetPointerv)(GLenum pname, void **params) { - CALL_GL_API(glGetPointerv, pname, params); -} -void API_ENTRY(glEnablei)(GLenum target, GLuint index) { - CALL_GL_API(glEnablei, target, index); -} -void API_ENTRY(glDisablei)(GLenum target, GLuint index) { - CALL_GL_API(glDisablei, target, index); -} -void API_ENTRY(glBlendEquationi)(GLuint buf, GLenum mode) { - CALL_GL_API(glBlendEquationi, buf, mode); -} -void API_ENTRY(glBlendEquationSeparatei)(GLuint buf, GLenum modeRGB, GLenum modeAlpha) { - CALL_GL_API(glBlendEquationSeparatei, buf, modeRGB, modeAlpha); -} -void API_ENTRY(glBlendFunci)(GLuint buf, GLenum src, GLenum dst) { - CALL_GL_API(glBlendFunci, buf, src, dst); -} -void API_ENTRY(glBlendFuncSeparatei)(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) { - CALL_GL_API(glBlendFuncSeparatei, buf, srcRGB, dstRGB, srcAlpha, dstAlpha); -} -void API_ENTRY(glColorMaski)(GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a) { - CALL_GL_API(glColorMaski, index, r, g, b, a); -} -GLboolean API_ENTRY(glIsEnabledi)(GLenum target, GLuint index) { - CALL_GL_API_RETURN(glIsEnabledi, target, index); -} -void API_ENTRY(glDrawElementsBaseVertex)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex) { - CALL_GL_API(glDrawElementsBaseVertex, mode, count, type, indices, basevertex); -} -void API_ENTRY(glDrawRangeElementsBaseVertex)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex) { - CALL_GL_API(glDrawRangeElementsBaseVertex, mode, start, end, count, type, indices, basevertex); -} -void API_ENTRY(glDrawElementsInstancedBaseVertex)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex) { - CALL_GL_API(glDrawElementsInstancedBaseVertex, mode, count, type, indices, instancecount, basevertex); -} -void API_ENTRY(glFramebufferTexture)(GLenum target, GLenum attachment, GLuint texture, GLint level) { - CALL_GL_API(glFramebufferTexture, target, attachment, texture, level); -} -void API_ENTRY(glPrimitiveBoundingBox)(GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW) { - CALL_GL_API(glPrimitiveBoundingBox, minX, minY, minZ, minW, maxX, maxY, maxZ, maxW); -} -GLenum API_ENTRY(glGetGraphicsResetStatus)(void) { - CALL_GL_API_RETURN(glGetGraphicsResetStatus); -} -void API_ENTRY(glReadnPixels)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data) { - CALL_GL_API(glReadnPixels, x, y, width, height, format, type, bufSize, data); -} -void API_ENTRY(glGetnUniformfv)(GLuint program, GLint location, GLsizei bufSize, GLfloat *params) { - CALL_GL_API(glGetnUniformfv, program, location, bufSize, params); -} -void API_ENTRY(glGetnUniformiv)(GLuint program, GLint location, GLsizei bufSize, GLint *params) { - CALL_GL_API(glGetnUniformiv, program, location, bufSize, params); -} -void API_ENTRY(glGetnUniformuiv)(GLuint program, GLint location, GLsizei bufSize, GLuint *params) { - CALL_GL_API(glGetnUniformuiv, program, location, bufSize, params); -} -void API_ENTRY(glMinSampleShading)(GLfloat value) { - CALL_GL_API(glMinSampleShading, value); -} -void API_ENTRY(glPatchParameteri)(GLenum pname, GLint value) { - CALL_GL_API(glPatchParameteri, pname, value); -} -void API_ENTRY(glTexParameterIiv)(GLenum target, GLenum pname, const GLint *params) { - CALL_GL_API(glTexParameterIiv, target, pname, params); -} -void API_ENTRY(glTexParameterIuiv)(GLenum target, GLenum pname, const GLuint *params) { - CALL_GL_API(glTexParameterIuiv, target, pname, params); -} -void API_ENTRY(glGetTexParameterIiv)(GLenum target, GLenum pname, GLint *params) { - CALL_GL_API(glGetTexParameterIiv, target, pname, params); -} -void API_ENTRY(glGetTexParameterIuiv)(GLenum target, GLenum pname, GLuint *params) { - CALL_GL_API(glGetTexParameterIuiv, target, pname, params); -} -void API_ENTRY(glSamplerParameterIiv)(GLuint sampler, GLenum pname, const GLint *param) { - CALL_GL_API(glSamplerParameterIiv, sampler, pname, param); -} -void API_ENTRY(glSamplerParameterIuiv)(GLuint sampler, GLenum pname, const GLuint *param) { - CALL_GL_API(glSamplerParameterIuiv, sampler, pname, param); -} -void API_ENTRY(glGetSamplerParameterIiv)(GLuint sampler, GLenum pname, GLint *params) { - CALL_GL_API(glGetSamplerParameterIiv, sampler, pname, params); -} -void API_ENTRY(glGetSamplerParameterIuiv)(GLuint sampler, GLenum pname, GLuint *params) { - CALL_GL_API(glGetSamplerParameterIuiv, sampler, pname, params); -} -void API_ENTRY(glTexBuffer)(GLenum target, GLenum internalformat, GLuint buffer) { - CALL_GL_API(glTexBuffer, target, internalformat, buffer); -} -void API_ENTRY(glTexBufferRange)(GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size) { - CALL_GL_API(glTexBufferRange, target, internalformat, buffer, offset, size); -} -void API_ENTRY(glTexStorage3DMultisample)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations) { - CALL_GL_API(glTexStorage3DMultisample, target, samples, internalformat, width, height, depth, fixedsamplelocations); -} -void API_ENTRY(glBlendBarrierKHR)(void) { - CALL_GL_API(glBlendBarrierKHR); -} -void API_ENTRY(glDebugMessageControlKHR)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled) { - CALL_GL_API(glDebugMessageControlKHR, source, type, severity, count, ids, enabled); -} -void API_ENTRY(glDebugMessageInsertKHR)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf) { - CALL_GL_API(glDebugMessageInsertKHR, source, type, id, severity, length, buf); -} -void API_ENTRY(glDebugMessageCallbackKHR)(GLDEBUGPROCKHR callback, const void *userParam) { - CALL_GL_API(glDebugMessageCallbackKHR, callback, userParam); -} -GLuint API_ENTRY(glGetDebugMessageLogKHR)(GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog) { - CALL_GL_API_RETURN(glGetDebugMessageLogKHR, count, bufSize, sources, types, ids, severities, lengths, messageLog); -} -void API_ENTRY(glPushDebugGroupKHR)(GLenum source, GLuint id, GLsizei length, const GLchar *message) { - CALL_GL_API(glPushDebugGroupKHR, source, id, length, message); -} -void API_ENTRY(glPopDebugGroupKHR)(void) { - CALL_GL_API(glPopDebugGroupKHR); -} -void API_ENTRY(glObjectLabelKHR)(GLenum identifier, GLuint name, GLsizei length, const GLchar *label) { - CALL_GL_API(glObjectLabelKHR, identifier, name, length, label); -} -void API_ENTRY(glGetObjectLabelKHR)(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label) { - CALL_GL_API(glGetObjectLabelKHR, identifier, name, bufSize, length, label); -} -void API_ENTRY(glObjectPtrLabelKHR)(const void *ptr, GLsizei length, const GLchar *label) { - CALL_GL_API(glObjectPtrLabelKHR, ptr, length, label); -} -void API_ENTRY(glGetObjectPtrLabelKHR)(const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label) { - CALL_GL_API(glGetObjectPtrLabelKHR, ptr, bufSize, length, label); -} -void API_ENTRY(glGetPointervKHR)(GLenum pname, void **params) { - CALL_GL_API(glGetPointervKHR, pname, params); -} -GLenum API_ENTRY(glGetGraphicsResetStatusKHR)(void) { - CALL_GL_API_RETURN(glGetGraphicsResetStatusKHR); -} -void API_ENTRY(glReadnPixelsKHR)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data) { - CALL_GL_API(glReadnPixelsKHR, x, y, width, height, format, type, bufSize, data); -} -void API_ENTRY(glGetnUniformfvKHR)(GLuint program, GLint location, GLsizei bufSize, GLfloat *params) { - CALL_GL_API(glGetnUniformfvKHR, program, location, bufSize, params); -} -void API_ENTRY(glGetnUniformivKHR)(GLuint program, GLint location, GLsizei bufSize, GLint *params) { - CALL_GL_API(glGetnUniformivKHR, program, location, bufSize, params); -} -void API_ENTRY(glGetnUniformuivKHR)(GLuint program, GLint location, GLsizei bufSize, GLuint *params) { - CALL_GL_API(glGetnUniformuivKHR, program, location, bufSize, params); -} -void API_ENTRY(glEGLImageTargetTexture2DOES)(GLenum target, GLeglImageOES image) { - CALL_GL_API(glEGLImageTargetTexture2DOES, target, image); -} -void API_ENTRY(glEGLImageTargetRenderbufferStorageOES)(GLenum target, GLeglImageOES image) { - CALL_GL_API(glEGLImageTargetRenderbufferStorageOES, target, image); -} -void API_ENTRY(glCopyImageSubDataOES)(GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth) { - CALL_GL_API(glCopyImageSubDataOES, srcName, srcTarget, srcLevel, srcX, srcY, srcZ, dstName, dstTarget, dstLevel, dstX, dstY, dstZ, srcWidth, srcHeight, srcDepth); -} -void API_ENTRY(glEnableiOES)(GLenum target, GLuint index) { - CALL_GL_API(glEnableiOES, target, index); -} -void API_ENTRY(glDisableiOES)(GLenum target, GLuint index) { - CALL_GL_API(glDisableiOES, target, index); -} -void API_ENTRY(glBlendEquationiOES)(GLuint buf, GLenum mode) { - CALL_GL_API(glBlendEquationiOES, buf, mode); -} -void API_ENTRY(glBlendEquationSeparateiOES)(GLuint buf, GLenum modeRGB, GLenum modeAlpha) { - CALL_GL_API(glBlendEquationSeparateiOES, buf, modeRGB, modeAlpha); -} -void API_ENTRY(glBlendFunciOES)(GLuint buf, GLenum src, GLenum dst) { - CALL_GL_API(glBlendFunciOES, buf, src, dst); -} -void API_ENTRY(glBlendFuncSeparateiOES)(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) { - CALL_GL_API(glBlendFuncSeparateiOES, buf, srcRGB, dstRGB, srcAlpha, dstAlpha); -} -void API_ENTRY(glColorMaskiOES)(GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a) { - CALL_GL_API(glColorMaskiOES, index, r, g, b, a); -} -GLboolean API_ENTRY(glIsEnablediOES)(GLenum target, GLuint index) { - CALL_GL_API_RETURN(glIsEnablediOES, target, index); -} -void API_ENTRY(glDrawElementsBaseVertexOES)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex) { - CALL_GL_API(glDrawElementsBaseVertexOES, mode, count, type, indices, basevertex); -} -void API_ENTRY(glDrawRangeElementsBaseVertexOES)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex) { - CALL_GL_API(glDrawRangeElementsBaseVertexOES, mode, start, end, count, type, indices, basevertex); -} -void API_ENTRY(glDrawElementsInstancedBaseVertexOES)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex) { - CALL_GL_API(glDrawElementsInstancedBaseVertexOES, mode, count, type, indices, instancecount, basevertex); -} -void API_ENTRY(glFramebufferTextureOES)(GLenum target, GLenum attachment, GLuint texture, GLint level) { - CALL_GL_API(glFramebufferTextureOES, target, attachment, texture, level); -} -void API_ENTRY(glGetProgramBinaryOES)(GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary) { - CALL_GL_API(glGetProgramBinaryOES, program, bufSize, length, binaryFormat, binary); -} -void API_ENTRY(glProgramBinaryOES)(GLuint program, GLenum binaryFormat, const void *binary, GLint length) { - CALL_GL_API(glProgramBinaryOES, program, binaryFormat, binary, length); -} -void * API_ENTRY(glMapBufferOES)(GLenum target, GLenum access) { - CALL_GL_API_RETURN(glMapBufferOES, target, access); -} -GLboolean API_ENTRY(glUnmapBufferOES)(GLenum target) { - CALL_GL_API_RETURN(glUnmapBufferOES, target); -} -void API_ENTRY(glGetBufferPointervOES)(GLenum target, GLenum pname, void **params) { - CALL_GL_API(glGetBufferPointervOES, target, pname, params); -} -void API_ENTRY(glPrimitiveBoundingBoxOES)(GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW) { - CALL_GL_API(glPrimitiveBoundingBoxOES, minX, minY, minZ, minW, maxX, maxY, maxZ, maxW); -} -void API_ENTRY(glMinSampleShadingOES)(GLfloat value) { - CALL_GL_API(glMinSampleShadingOES, value); -} -void API_ENTRY(glPatchParameteriOES)(GLenum pname, GLint value) { - CALL_GL_API(glPatchParameteriOES, pname, value); -} -void API_ENTRY(glTexImage3DOES)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels) { - CALL_GL_API(glTexImage3DOES, target, level, internalformat, width, height, depth, border, format, type, pixels); -} -void API_ENTRY(glTexSubImage3DOES)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels) { - CALL_GL_API(glTexSubImage3DOES, target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels); -} -void API_ENTRY(glCopyTexSubImage3DOES)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height) { - CALL_GL_API(glCopyTexSubImage3DOES, target, level, xoffset, yoffset, zoffset, x, y, width, height); -} -void API_ENTRY(glCompressedTexImage3DOES)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data) { - CALL_GL_API(glCompressedTexImage3DOES, target, level, internalformat, width, height, depth, border, imageSize, data); -} -void API_ENTRY(glCompressedTexSubImage3DOES)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data) { - CALL_GL_API(glCompressedTexSubImage3DOES, target, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, data); -} -void API_ENTRY(glFramebufferTexture3DOES)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset) { - CALL_GL_API(glFramebufferTexture3DOES, target, attachment, textarget, texture, level, zoffset); -} -void API_ENTRY(glTexParameterIivOES)(GLenum target, GLenum pname, const GLint *params) { - CALL_GL_API(glTexParameterIivOES, target, pname, params); -} -void API_ENTRY(glTexParameterIuivOES)(GLenum target, GLenum pname, const GLuint *params) { - CALL_GL_API(glTexParameterIuivOES, target, pname, params); -} -void API_ENTRY(glGetTexParameterIivOES)(GLenum target, GLenum pname, GLint *params) { - CALL_GL_API(glGetTexParameterIivOES, target, pname, params); -} -void API_ENTRY(glGetTexParameterIuivOES)(GLenum target, GLenum pname, GLuint *params) { - CALL_GL_API(glGetTexParameterIuivOES, target, pname, params); -} -void API_ENTRY(glSamplerParameterIivOES)(GLuint sampler, GLenum pname, const GLint *param) { - CALL_GL_API(glSamplerParameterIivOES, sampler, pname, param); -} -void API_ENTRY(glSamplerParameterIuivOES)(GLuint sampler, GLenum pname, const GLuint *param) { - CALL_GL_API(glSamplerParameterIuivOES, sampler, pname, param); -} -void API_ENTRY(glGetSamplerParameterIivOES)(GLuint sampler, GLenum pname, GLint *params) { - CALL_GL_API(glGetSamplerParameterIivOES, sampler, pname, params); -} -void API_ENTRY(glGetSamplerParameterIuivOES)(GLuint sampler, GLenum pname, GLuint *params) { - CALL_GL_API(glGetSamplerParameterIuivOES, sampler, pname, params); -} -void API_ENTRY(glTexBufferOES)(GLenum target, GLenum internalformat, GLuint buffer) { - CALL_GL_API(glTexBufferOES, target, internalformat, buffer); -} -void API_ENTRY(glTexBufferRangeOES)(GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size) { - CALL_GL_API(glTexBufferRangeOES, target, internalformat, buffer, offset, size); -} -void API_ENTRY(glTexStorage3DMultisampleOES)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations) { - CALL_GL_API(glTexStorage3DMultisampleOES, target, samples, internalformat, width, height, depth, fixedsamplelocations); -} -void API_ENTRY(glTextureViewOES)(GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers) { - CALL_GL_API(glTextureViewOES, texture, target, origtexture, internalformat, minlevel, numlevels, minlayer, numlayers); -} -void API_ENTRY(glBindVertexArrayOES)(GLuint array) { - CALL_GL_API(glBindVertexArrayOES, array); -} -void API_ENTRY(glDeleteVertexArraysOES)(GLsizei n, const GLuint *arrays) { - CALL_GL_API(glDeleteVertexArraysOES, n, arrays); -} -void API_ENTRY(glGenVertexArraysOES)(GLsizei n, GLuint *arrays) { - CALL_GL_API(glGenVertexArraysOES, n, arrays); -} -GLboolean API_ENTRY(glIsVertexArrayOES)(GLuint array) { - CALL_GL_API_RETURN(glIsVertexArrayOES, array); -} -void API_ENTRY(glDrawArraysInstancedBaseInstanceEXT)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance) { - CALL_GL_API(glDrawArraysInstancedBaseInstanceEXT, mode, first, count, instancecount, baseinstance); -} -void API_ENTRY(glDrawElementsInstancedBaseInstanceEXT)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLuint baseinstance) { - CALL_GL_API(glDrawElementsInstancedBaseInstanceEXT, mode, count, type, indices, instancecount, baseinstance); -} -void API_ENTRY(glDrawElementsInstancedBaseVertexBaseInstanceEXT)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance) { - CALL_GL_API(glDrawElementsInstancedBaseVertexBaseInstanceEXT, mode, count, type, indices, instancecount, basevertex, baseinstance); -} -void API_ENTRY(glBindFragDataLocationIndexedEXT)(GLuint program, GLuint colorNumber, GLuint index, const GLchar *name) { - CALL_GL_API(glBindFragDataLocationIndexedEXT, program, colorNumber, index, name); -} -void API_ENTRY(glBindFragDataLocationEXT)(GLuint program, GLuint color, const GLchar *name) { - CALL_GL_API(glBindFragDataLocationEXT, program, color, name); -} -GLint API_ENTRY(glGetProgramResourceLocationIndexEXT)(GLuint program, GLenum programInterface, const GLchar *name) { - CALL_GL_API_RETURN(glGetProgramResourceLocationIndexEXT, program, programInterface, name); -} -GLint API_ENTRY(glGetFragDataIndexEXT)(GLuint program, const GLchar *name) { - CALL_GL_API_RETURN(glGetFragDataIndexEXT, program, name); -} -void API_ENTRY(glBufferStorageEXT)(GLenum target, GLsizeiptr size, const void *data, GLbitfield flags) { - CALL_GL_API(glBufferStorageEXT, target, size, data, flags); -} -void API_ENTRY(glCopyImageSubDataEXT)(GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth) { - CALL_GL_API(glCopyImageSubDataEXT, srcName, srcTarget, srcLevel, srcX, srcY, srcZ, dstName, dstTarget, dstLevel, dstX, dstY, dstZ, srcWidth, srcHeight, srcDepth); -} -void API_ENTRY(glLabelObjectEXT)(GLenum type, GLuint object, GLsizei length, const GLchar *label) { - CALL_GL_API(glLabelObjectEXT, type, object, length, label); -} -void API_ENTRY(glGetObjectLabelEXT)(GLenum type, GLuint object, GLsizei bufSize, GLsizei *length, GLchar *label) { - CALL_GL_API(glGetObjectLabelEXT, type, object, bufSize, length, label); -} -void API_ENTRY(glInsertEventMarkerEXT)(GLsizei length, const GLchar *marker) { - CALL_GL_API(glInsertEventMarkerEXT, length, marker); -} -void API_ENTRY(glPushGroupMarkerEXT)(GLsizei length, const GLchar *marker) { - CALL_GL_API(glPushGroupMarkerEXT, length, marker); -} -void API_ENTRY(glPopGroupMarkerEXT)(void) { - CALL_GL_API(glPopGroupMarkerEXT); -} -void API_ENTRY(glDiscardFramebufferEXT)(GLenum target, GLsizei numAttachments, const GLenum *attachments) { - CALL_GL_API(glDiscardFramebufferEXT, target, numAttachments, attachments); -} -void API_ENTRY(glGenQueriesEXT)(GLsizei n, GLuint *ids) { - CALL_GL_API(glGenQueriesEXT, n, ids); -} -void API_ENTRY(glDeleteQueriesEXT)(GLsizei n, const GLuint *ids) { - CALL_GL_API(glDeleteQueriesEXT, n, ids); -} -GLboolean API_ENTRY(glIsQueryEXT)(GLuint id) { - CALL_GL_API_RETURN(glIsQueryEXT, id); -} -void API_ENTRY(glBeginQueryEXT)(GLenum target, GLuint id) { - CALL_GL_API(glBeginQueryEXT, target, id); -} -void API_ENTRY(glEndQueryEXT)(GLenum target) { - CALL_GL_API(glEndQueryEXT, target); -} -void API_ENTRY(glQueryCounterEXT)(GLuint id, GLenum target) { - CALL_GL_API(glQueryCounterEXT, id, target); -} -void API_ENTRY(glGetQueryivEXT)(GLenum target, GLenum pname, GLint *params) { - CALL_GL_API(glGetQueryivEXT, target, pname, params); -} -void API_ENTRY(glGetQueryObjectivEXT)(GLuint id, GLenum pname, GLint *params) { - CALL_GL_API(glGetQueryObjectivEXT, id, pname, params); -} -void API_ENTRY(glGetQueryObjectuivEXT)(GLuint id, GLenum pname, GLuint *params) { - CALL_GL_API(glGetQueryObjectuivEXT, id, pname, params); -} -void API_ENTRY(glGetQueryObjecti64vEXT)(GLuint id, GLenum pname, GLint64 *params) { - CALL_GL_API(glGetQueryObjecti64vEXT, id, pname, params); -} -void API_ENTRY(glGetQueryObjectui64vEXT)(GLuint id, GLenum pname, GLuint64 *params) { - CALL_GL_API(glGetQueryObjectui64vEXT, id, pname, params); -} -void API_ENTRY(glDrawBuffersEXT)(GLsizei n, const GLenum *bufs) { - CALL_GL_API(glDrawBuffersEXT, n, bufs); -} -void API_ENTRY(glEnableiEXT)(GLenum target, GLuint index) { - CALL_GL_API(glEnableiEXT, target, index); -} -void API_ENTRY(glDisableiEXT)(GLenum target, GLuint index) { - CALL_GL_API(glDisableiEXT, target, index); -} -void API_ENTRY(glBlendEquationiEXT)(GLuint buf, GLenum mode) { - CALL_GL_API(glBlendEquationiEXT, buf, mode); -} -void API_ENTRY(glBlendEquationSeparateiEXT)(GLuint buf, GLenum modeRGB, GLenum modeAlpha) { - CALL_GL_API(glBlendEquationSeparateiEXT, buf, modeRGB, modeAlpha); -} -void API_ENTRY(glBlendFunciEXT)(GLuint buf, GLenum src, GLenum dst) { - CALL_GL_API(glBlendFunciEXT, buf, src, dst); -} -void API_ENTRY(glBlendFuncSeparateiEXT)(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) { - CALL_GL_API(glBlendFuncSeparateiEXT, buf, srcRGB, dstRGB, srcAlpha, dstAlpha); -} -void API_ENTRY(glColorMaskiEXT)(GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a) { - CALL_GL_API(glColorMaskiEXT, index, r, g, b, a); -} -GLboolean API_ENTRY(glIsEnablediEXT)(GLenum target, GLuint index) { - CALL_GL_API_RETURN(glIsEnablediEXT, target, index); -} -void API_ENTRY(glDrawElementsBaseVertexEXT)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex) { - CALL_GL_API(glDrawElementsBaseVertexEXT, mode, count, type, indices, basevertex); -} -void API_ENTRY(glDrawRangeElementsBaseVertexEXT)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex) { - CALL_GL_API(glDrawRangeElementsBaseVertexEXT, mode, start, end, count, type, indices, basevertex); -} -void API_ENTRY(glDrawElementsInstancedBaseVertexEXT)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex) { - CALL_GL_API(glDrawElementsInstancedBaseVertexEXT, mode, count, type, indices, instancecount, basevertex); -} -void API_ENTRY(glMultiDrawElementsBaseVertexEXT)(GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, const GLint *basevertex) { - CALL_GL_API(glMultiDrawElementsBaseVertexEXT, mode, count, type, indices, primcount, basevertex); -} -void API_ENTRY(glDrawArraysInstancedEXT)(GLenum mode, GLint start, GLsizei count, GLsizei primcount) { - CALL_GL_API(glDrawArraysInstancedEXT, mode, start, count, primcount); -} -void API_ENTRY(glDrawElementsInstancedEXT)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount) { - CALL_GL_API(glDrawElementsInstancedEXT, mode, count, type, indices, primcount); -} -void API_ENTRY(glFramebufferTextureEXT)(GLenum target, GLenum attachment, GLuint texture, GLint level) { - CALL_GL_API(glFramebufferTextureEXT, target, attachment, texture, level); -} -void API_ENTRY(glVertexAttribDivisorEXT)(GLuint index, GLuint divisor) { - CALL_GL_API(glVertexAttribDivisorEXT, index, divisor); -} -void * API_ENTRY(glMapBufferRangeEXT)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) { - CALL_GL_API_RETURN(glMapBufferRangeEXT, target, offset, length, access); -} -void API_ENTRY(glFlushMappedBufferRangeEXT)(GLenum target, GLintptr offset, GLsizeiptr length) { - CALL_GL_API(glFlushMappedBufferRangeEXT, target, offset, length); -} -void API_ENTRY(glMultiDrawArraysEXT)(GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount) { - CALL_GL_API(glMultiDrawArraysEXT, mode, first, count, primcount); -} -void API_ENTRY(glMultiDrawElementsEXT)(GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount) { - CALL_GL_API(glMultiDrawElementsEXT, mode, count, type, indices, primcount); -} -void API_ENTRY(glMultiDrawArraysIndirectEXT)(GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride) { - CALL_GL_API(glMultiDrawArraysIndirectEXT, mode, indirect, drawcount, stride); -} -void API_ENTRY(glMultiDrawElementsIndirectEXT)(GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride) { - CALL_GL_API(glMultiDrawElementsIndirectEXT, mode, type, indirect, drawcount, stride); -} -void API_ENTRY(glRenderbufferStorageMultisampleEXT)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) { - CALL_GL_API(glRenderbufferStorageMultisampleEXT, target, samples, internalformat, width, height); -} -void API_ENTRY(glFramebufferTexture2DMultisampleEXT)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples) { - CALL_GL_API(glFramebufferTexture2DMultisampleEXT, target, attachment, textarget, texture, level, samples); -} -void API_ENTRY(glReadBufferIndexedEXT)(GLenum src, GLint index) { - CALL_GL_API(glReadBufferIndexedEXT, src, index); -} -void API_ENTRY(glDrawBuffersIndexedEXT)(GLint n, const GLenum *location, const GLint *indices) { - CALL_GL_API(glDrawBuffersIndexedEXT, n, location, indices); -} -void API_ENTRY(glGetIntegeri_vEXT)(GLenum target, GLuint index, GLint *data) { - CALL_GL_API(glGetIntegeri_vEXT, target, index, data); -} -void API_ENTRY(glPrimitiveBoundingBoxEXT)(GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW) { - CALL_GL_API(glPrimitiveBoundingBoxEXT, minX, minY, minZ, minW, maxX, maxY, maxZ, maxW); -} -void API_ENTRY(glRasterSamplesEXT)(GLuint samples, GLboolean fixedsamplelocations) { - CALL_GL_API(glRasterSamplesEXT, samples, fixedsamplelocations); -} -GLenum API_ENTRY(glGetGraphicsResetStatusEXT)(void) { - CALL_GL_API_RETURN(glGetGraphicsResetStatusEXT); -} -void API_ENTRY(glReadnPixelsEXT)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data) { - CALL_GL_API(glReadnPixelsEXT, x, y, width, height, format, type, bufSize, data); -} -void API_ENTRY(glGetnUniformfvEXT)(GLuint program, GLint location, GLsizei bufSize, GLfloat *params) { - CALL_GL_API(glGetnUniformfvEXT, program, location, bufSize, params); -} -void API_ENTRY(glGetnUniformivEXT)(GLuint program, GLint location, GLsizei bufSize, GLint *params) { - CALL_GL_API(glGetnUniformivEXT, program, location, bufSize, params); -} -void API_ENTRY(glActiveShaderProgramEXT)(GLuint pipeline, GLuint program) { - CALL_GL_API(glActiveShaderProgramEXT, pipeline, program); -} -void API_ENTRY(glBindProgramPipelineEXT)(GLuint pipeline) { - CALL_GL_API(glBindProgramPipelineEXT, pipeline); -} -GLuint API_ENTRY(glCreateShaderProgramvEXT)(GLenum type, GLsizei count, const GLchar **strings) { - CALL_GL_API_RETURN(glCreateShaderProgramvEXT, type, count, strings); -} -void API_ENTRY(glDeleteProgramPipelinesEXT)(GLsizei n, const GLuint *pipelines) { - CALL_GL_API(glDeleteProgramPipelinesEXT, n, pipelines); -} -void API_ENTRY(glGenProgramPipelinesEXT)(GLsizei n, GLuint *pipelines) { - CALL_GL_API(glGenProgramPipelinesEXT, n, pipelines); -} -void API_ENTRY(glGetProgramPipelineInfoLogEXT)(GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog) { - CALL_GL_API(glGetProgramPipelineInfoLogEXT, pipeline, bufSize, length, infoLog); -} -void API_ENTRY(glGetProgramPipelineivEXT)(GLuint pipeline, GLenum pname, GLint *params) { - CALL_GL_API(glGetProgramPipelineivEXT, pipeline, pname, params); -} -GLboolean API_ENTRY(glIsProgramPipelineEXT)(GLuint pipeline) { - CALL_GL_API_RETURN(glIsProgramPipelineEXT, pipeline); -} -void API_ENTRY(glProgramParameteriEXT)(GLuint program, GLenum pname, GLint value) { - CALL_GL_API(glProgramParameteriEXT, program, pname, value); -} -void API_ENTRY(glProgramUniform1fEXT)(GLuint program, GLint location, GLfloat v0) { - CALL_GL_API(glProgramUniform1fEXT, program, location, v0); -} -void API_ENTRY(glProgramUniform1fvEXT)(GLuint program, GLint location, GLsizei count, const GLfloat *value) { - CALL_GL_API(glProgramUniform1fvEXT, program, location, count, value); -} -void API_ENTRY(glProgramUniform1iEXT)(GLuint program, GLint location, GLint v0) { - CALL_GL_API(glProgramUniform1iEXT, program, location, v0); -} -void API_ENTRY(glProgramUniform1ivEXT)(GLuint program, GLint location, GLsizei count, const GLint *value) { - CALL_GL_API(glProgramUniform1ivEXT, program, location, count, value); -} -void API_ENTRY(glProgramUniform2fEXT)(GLuint program, GLint location, GLfloat v0, GLfloat v1) { - CALL_GL_API(glProgramUniform2fEXT, program, location, v0, v1); -} -void API_ENTRY(glProgramUniform2fvEXT)(GLuint program, GLint location, GLsizei count, const GLfloat *value) { - CALL_GL_API(glProgramUniform2fvEXT, program, location, count, value); -} -void API_ENTRY(glProgramUniform2iEXT)(GLuint program, GLint location, GLint v0, GLint v1) { - CALL_GL_API(glProgramUniform2iEXT, program, location, v0, v1); -} -void API_ENTRY(glProgramUniform2ivEXT)(GLuint program, GLint location, GLsizei count, const GLint *value) { - CALL_GL_API(glProgramUniform2ivEXT, program, location, count, value); -} -void API_ENTRY(glProgramUniform3fEXT)(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2) { - CALL_GL_API(glProgramUniform3fEXT, program, location, v0, v1, v2); -} -void API_ENTRY(glProgramUniform3fvEXT)(GLuint program, GLint location, GLsizei count, const GLfloat *value) { - CALL_GL_API(glProgramUniform3fvEXT, program, location, count, value); -} -void API_ENTRY(glProgramUniform3iEXT)(GLuint program, GLint location, GLint v0, GLint v1, GLint v2) { - CALL_GL_API(glProgramUniform3iEXT, program, location, v0, v1, v2); -} -void API_ENTRY(glProgramUniform3ivEXT)(GLuint program, GLint location, GLsizei count, const GLint *value) { - CALL_GL_API(glProgramUniform3ivEXT, program, location, count, value); -} -void API_ENTRY(glProgramUniform4fEXT)(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) { - CALL_GL_API(glProgramUniform4fEXT, program, location, v0, v1, v2, v3); -} -void API_ENTRY(glProgramUniform4fvEXT)(GLuint program, GLint location, GLsizei count, const GLfloat *value) { - CALL_GL_API(glProgramUniform4fvEXT, program, location, count, value); -} -void API_ENTRY(glProgramUniform4iEXT)(GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3) { - CALL_GL_API(glProgramUniform4iEXT, program, location, v0, v1, v2, v3); -} -void API_ENTRY(glProgramUniform4ivEXT)(GLuint program, GLint location, GLsizei count, const GLint *value) { - CALL_GL_API(glProgramUniform4ivEXT, program, location, count, value); -} -void API_ENTRY(glProgramUniformMatrix2fvEXT)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glProgramUniformMatrix2fvEXT, program, location, count, transpose, value); -} -void API_ENTRY(glProgramUniformMatrix3fvEXT)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glProgramUniformMatrix3fvEXT, program, location, count, transpose, value); -} -void API_ENTRY(glProgramUniformMatrix4fvEXT)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glProgramUniformMatrix4fvEXT, program, location, count, transpose, value); -} -void API_ENTRY(glUseProgramStagesEXT)(GLuint pipeline, GLbitfield stages, GLuint program) { - CALL_GL_API(glUseProgramStagesEXT, pipeline, stages, program); -} -void API_ENTRY(glValidateProgramPipelineEXT)(GLuint pipeline) { - CALL_GL_API(glValidateProgramPipelineEXT, pipeline); -} -void API_ENTRY(glProgramUniform1uiEXT)(GLuint program, GLint location, GLuint v0) { - CALL_GL_API(glProgramUniform1uiEXT, program, location, v0); -} -void API_ENTRY(glProgramUniform2uiEXT)(GLuint program, GLint location, GLuint v0, GLuint v1) { - CALL_GL_API(glProgramUniform2uiEXT, program, location, v0, v1); -} -void API_ENTRY(glProgramUniform3uiEXT)(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2) { - CALL_GL_API(glProgramUniform3uiEXT, program, location, v0, v1, v2); -} -void API_ENTRY(glProgramUniform4uiEXT)(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) { - CALL_GL_API(glProgramUniform4uiEXT, program, location, v0, v1, v2, v3); -} -void API_ENTRY(glProgramUniform1uivEXT)(GLuint program, GLint location, GLsizei count, const GLuint *value) { - CALL_GL_API(glProgramUniform1uivEXT, program, location, count, value); -} -void API_ENTRY(glProgramUniform2uivEXT)(GLuint program, GLint location, GLsizei count, const GLuint *value) { - CALL_GL_API(glProgramUniform2uivEXT, program, location, count, value); -} -void API_ENTRY(glProgramUniform3uivEXT)(GLuint program, GLint location, GLsizei count, const GLuint *value) { - CALL_GL_API(glProgramUniform3uivEXT, program, location, count, value); -} -void API_ENTRY(glProgramUniform4uivEXT)(GLuint program, GLint location, GLsizei count, const GLuint *value) { - CALL_GL_API(glProgramUniform4uivEXT, program, location, count, value); -} -void API_ENTRY(glProgramUniformMatrix2x3fvEXT)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glProgramUniformMatrix2x3fvEXT, program, location, count, transpose, value); -} -void API_ENTRY(glProgramUniformMatrix3x2fvEXT)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glProgramUniformMatrix3x2fvEXT, program, location, count, transpose, value); -} -void API_ENTRY(glProgramUniformMatrix2x4fvEXT)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glProgramUniformMatrix2x4fvEXT, program, location, count, transpose, value); -} -void API_ENTRY(glProgramUniformMatrix4x2fvEXT)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glProgramUniformMatrix4x2fvEXT, program, location, count, transpose, value); -} -void API_ENTRY(glProgramUniformMatrix3x4fvEXT)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glProgramUniformMatrix3x4fvEXT, program, location, count, transpose, value); -} -void API_ENTRY(glProgramUniformMatrix4x3fvEXT)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glProgramUniformMatrix4x3fvEXT, program, location, count, transpose, value); -} -void API_ENTRY(glTexPageCommitmentEXT)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit) { - CALL_GL_API(glTexPageCommitmentEXT, target, level, xoffset, yoffset, zoffset, width, height, depth, commit); -} -void API_ENTRY(glPatchParameteriEXT)(GLenum pname, GLint value) { - CALL_GL_API(glPatchParameteriEXT, pname, value); -} -void API_ENTRY(glTexParameterIivEXT)(GLenum target, GLenum pname, const GLint *params) { - CALL_GL_API(glTexParameterIivEXT, target, pname, params); -} -void API_ENTRY(glTexParameterIuivEXT)(GLenum target, GLenum pname, const GLuint *params) { - CALL_GL_API(glTexParameterIuivEXT, target, pname, params); -} -void API_ENTRY(glGetTexParameterIivEXT)(GLenum target, GLenum pname, GLint *params) { - CALL_GL_API(glGetTexParameterIivEXT, target, pname, params); -} -void API_ENTRY(glGetTexParameterIuivEXT)(GLenum target, GLenum pname, GLuint *params) { - CALL_GL_API(glGetTexParameterIuivEXT, target, pname, params); -} -void API_ENTRY(glSamplerParameterIivEXT)(GLuint sampler, GLenum pname, const GLint *param) { - CALL_GL_API(glSamplerParameterIivEXT, sampler, pname, param); -} -void API_ENTRY(glSamplerParameterIuivEXT)(GLuint sampler, GLenum pname, const GLuint *param) { - CALL_GL_API(glSamplerParameterIuivEXT, sampler, pname, param); -} -void API_ENTRY(glGetSamplerParameterIivEXT)(GLuint sampler, GLenum pname, GLint *params) { - CALL_GL_API(glGetSamplerParameterIivEXT, sampler, pname, params); -} -void API_ENTRY(glGetSamplerParameterIuivEXT)(GLuint sampler, GLenum pname, GLuint *params) { - CALL_GL_API(glGetSamplerParameterIuivEXT, sampler, pname, params); -} -void API_ENTRY(glTexBufferEXT)(GLenum target, GLenum internalformat, GLuint buffer) { - CALL_GL_API(glTexBufferEXT, target, internalformat, buffer); -} -void API_ENTRY(glTexBufferRangeEXT)(GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size) { - CALL_GL_API(glTexBufferRangeEXT, target, internalformat, buffer, offset, size); -} -void API_ENTRY(glTexStorage1DEXT)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width) { - CALL_GL_API(glTexStorage1DEXT, target, levels, internalformat, width); -} -void API_ENTRY(glTexStorage2DEXT)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) { - CALL_GL_API(glTexStorage2DEXT, target, levels, internalformat, width, height); -} -void API_ENTRY(glTexStorage3DEXT)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) { - CALL_GL_API(glTexStorage3DEXT, target, levels, internalformat, width, height, depth); -} -void API_ENTRY(glTextureStorage1DEXT)(GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width) { - CALL_GL_API(glTextureStorage1DEXT, texture, target, levels, internalformat, width); -} -void API_ENTRY(glTextureStorage2DEXT)(GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) { - CALL_GL_API(glTextureStorage2DEXT, texture, target, levels, internalformat, width, height); -} -void API_ENTRY(glTextureStorage3DEXT)(GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) { - CALL_GL_API(glTextureStorage3DEXT, texture, target, levels, internalformat, width, height, depth); -} -void API_ENTRY(glTextureViewEXT)(GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers) { - CALL_GL_API(glTextureViewEXT, texture, target, origtexture, internalformat, minlevel, numlevels, minlayer, numlayers); -} diff --git a/libs/hwui/debug/gles_undefine.h b/libs/hwui/debug/gles_undefine.h deleted file mode 100644 index e43829d98e38..000000000000 --- a/libs/hwui/debug/gles_undefine.h +++ /dev/null @@ -1,913 +0,0 @@ -/* - * Copyright (C) 2016 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. - */ - -#undef glActiveShaderProgram -#undef glActiveShaderProgramEXT -#undef glActiveTexture -#undef glAlphaFunc -#undef glAlphaFuncQCOM -#undef glAlphaFuncx -#undef glAlphaFuncxOES -#undef glApplyFramebufferAttachmentCMAAINTEL -#undef glAttachShader -#undef glBeginConditionalRenderNV -#undef glBeginPerfMonitorAMD -#undef glBeginPerfQueryINTEL -#undef glBeginQuery -#undef glBeginQueryEXT -#undef glBeginTransformFeedback -#undef glBindAttribLocation -#undef glBindBuffer -#undef glBindBufferBase -#undef glBindBufferRange -#undef glBindFragDataLocationEXT -#undef glBindFragDataLocationIndexedEXT -#undef glBindFramebuffer -#undef glBindFramebufferOES -#undef glBindImageTexture -#undef glBindProgramPipeline -#undef glBindProgramPipelineEXT -#undef glBindRenderbuffer -#undef glBindRenderbufferOES -#undef glBindSampler -#undef glBindTexture -#undef glBindTransformFeedback -#undef glBindVertexArray -#undef glBindVertexArrayOES -#undef glBindVertexBuffer -#undef glBlendBarrier -#undef glBlendBarrierKHR -#undef glBlendBarrierNV -#undef glBlendColor -#undef glBlendEquation -#undef glBlendEquationOES -#undef glBlendEquationSeparate -#undef glBlendEquationSeparateOES -#undef glBlendEquationSeparatei -#undef glBlendEquationSeparateiEXT -#undef glBlendEquationSeparateiOES -#undef glBlendEquationi -#undef glBlendEquationiEXT -#undef glBlendEquationiOES -#undef glBlendFunc -#undef glBlendFuncSeparate -#undef glBlendFuncSeparateOES -#undef glBlendFuncSeparatei -#undef glBlendFuncSeparateiEXT -#undef glBlendFuncSeparateiOES -#undef glBlendFunci -#undef glBlendFunciEXT -#undef glBlendFunciOES -#undef glBlendParameteriNV -#undef glBlitFramebuffer -#undef glBlitFramebufferANGLE -#undef glBlitFramebufferNV -#undef glBufferData -#undef glBufferStorageEXT -#undef glBufferSubData -#undef glCheckFramebufferStatus -#undef glCheckFramebufferStatusOES -#undef glClear -#undef glClearBufferfi -#undef glClearBufferfv -#undef glClearBufferiv -#undef glClearBufferuiv -#undef glClearColor -#undef glClearColorx -#undef glClearColorxOES -#undef glClearDepthf -#undef glClearDepthfOES -#undef glClearDepthx -#undef glClearDepthxOES -#undef glClearStencil -#undef glClientActiveTexture -#undef glClientWaitSync -#undef glClientWaitSyncAPPLE -#undef glClipPlanef -#undef glClipPlanefIMG -#undef glClipPlanefOES -#undef glClipPlanex -#undef glClipPlanexIMG -#undef glClipPlanexOES -#undef glColor4f -#undef glColor4ub -#undef glColor4x -#undef glColor4xOES -#undef glColorMask -#undef glColorMaski -#undef glColorMaskiEXT -#undef glColorMaskiOES -#undef glColorPointer -#undef glCompileShader -#undef glCompressedTexImage2D -#undef glCompressedTexImage3D -#undef glCompressedTexImage3DOES -#undef glCompressedTexSubImage2D -#undef glCompressedTexSubImage3D -#undef glCompressedTexSubImage3DOES -#undef glCopyBufferSubData -#undef glCopyBufferSubDataNV -#undef glCopyImageSubData -#undef glCopyImageSubDataEXT -#undef glCopyImageSubDataOES -#undef glCopyPathNV -#undef glCopyTexImage2D -#undef glCopyTexSubImage2D -#undef glCopyTexSubImage3D -#undef glCopyTexSubImage3DOES -#undef glCopyTextureLevelsAPPLE -#undef glCoverFillPathInstancedNV -#undef glCoverFillPathNV -#undef glCoverStrokePathInstancedNV -#undef glCoverStrokePathNV -#undef glCoverageMaskNV -#undef glCoverageModulationNV -#undef glCoverageModulationTableNV -#undef glCoverageOperationNV -#undef glCreatePerfQueryINTEL -#undef glCreateProgram -#undef glCreateShader -#undef glCreateShaderProgramv -#undef glCreateShaderProgramvEXT -#undef glCullFace -#undef glCurrentPaletteMatrixOES -#undef glDebugMessageCallback -#undef glDebugMessageCallbackKHR -#undef glDebugMessageControl -#undef glDebugMessageControlKHR -#undef glDebugMessageInsert -#undef glDebugMessageInsertKHR -#undef glDeleteBuffers -#undef glDeleteFencesNV -#undef glDeleteFramebuffers -#undef glDeleteFramebuffersOES -#undef glDeletePathsNV -#undef glDeletePerfMonitorsAMD -#undef glDeletePerfQueryINTEL -#undef glDeleteProgram -#undef glDeleteProgramPipelines -#undef glDeleteProgramPipelinesEXT -#undef glDeleteQueries -#undef glDeleteQueriesEXT -#undef glDeleteRenderbuffers -#undef glDeleteRenderbuffersOES -#undef glDeleteSamplers -#undef glDeleteShader -#undef glDeleteSync -#undef glDeleteSyncAPPLE -#undef glDeleteTextures -#undef glDeleteTransformFeedbacks -#undef glDeleteVertexArrays -#undef glDeleteVertexArraysOES -#undef glDepthFunc -#undef glDepthMask -#undef glDepthRangeArrayfvNV -#undef glDepthRangeIndexedfNV -#undef glDepthRangef -#undef glDepthRangefOES -#undef glDepthRangex -#undef glDepthRangexOES -#undef glDetachShader -#undef glDisable -#undef glDisableClientState -#undef glDisableDriverControlQCOM -#undef glDisableVertexAttribArray -#undef glDisablei -#undef glDisableiEXT -#undef glDisableiNV -#undef glDisableiOES -#undef glDiscardFramebufferEXT -#undef glDispatchCompute -#undef glDispatchComputeIndirect -#undef glDrawArrays -#undef glDrawArraysIndirect -#undef glDrawArraysInstanced -#undef glDrawArraysInstancedANGLE -#undef glDrawArraysInstancedBaseInstanceEXT -#undef glDrawArraysInstancedEXT -#undef glDrawArraysInstancedNV -#undef glDrawBuffers -#undef glDrawBuffersEXT -#undef glDrawBuffersIndexedEXT -#undef glDrawBuffersNV -#undef glDrawElements -#undef glDrawElementsBaseVertex -#undef glDrawElementsBaseVertexEXT -#undef glDrawElementsBaseVertexOES -#undef glDrawElementsIndirect -#undef glDrawElementsInstanced -#undef glDrawElementsInstancedANGLE -#undef glDrawElementsInstancedBaseInstanceEXT -#undef glDrawElementsInstancedBaseVertex -#undef glDrawElementsInstancedBaseVertexBaseInstanceEXT -#undef glDrawElementsInstancedBaseVertexEXT -#undef glDrawElementsInstancedBaseVertexOES -#undef glDrawElementsInstancedEXT -#undef glDrawElementsInstancedNV -#undef glDrawRangeElements -#undef glDrawRangeElementsBaseVertex -#undef glDrawRangeElementsBaseVertexEXT -#undef glDrawRangeElementsBaseVertexOES -#undef glDrawTexfOES -#undef glDrawTexfvOES -#undef glDrawTexiOES -#undef glDrawTexivOES -#undef glDrawTexsOES -#undef glDrawTexsvOES -#undef glDrawTexxOES -#undef glDrawTexxvOES -#undef glEGLImageTargetRenderbufferStorageOES -#undef glEGLImageTargetTexture2DOES -#undef glEnable -#undef glEnableClientState -#undef glEnableDriverControlQCOM -#undef glEnableVertexAttribArray -#undef glEnablei -#undef glEnableiEXT -#undef glEnableiNV -#undef glEnableiOES -#undef glEndConditionalRenderNV -#undef glEndPerfMonitorAMD -#undef glEndPerfQueryINTEL -#undef glEndQuery -#undef glEndQueryEXT -#undef glEndTilingQCOM -#undef glEndTransformFeedback -#undef glExtGetBufferPointervQCOM -#undef glExtGetBuffersQCOM -#undef glExtGetFramebuffersQCOM -#undef glExtGetProgramBinarySourceQCOM -#undef glExtGetProgramsQCOM -#undef glExtGetRenderbuffersQCOM -#undef glExtGetShadersQCOM -#undef glExtGetTexLevelParameterivQCOM -#undef glExtGetTexSubImageQCOM -#undef glExtGetTexturesQCOM -#undef glExtIsProgramBinaryQCOM -#undef glExtTexObjectStateOverrideiQCOM -#undef glFenceSync -#undef glFenceSyncAPPLE -#undef glFinish -#undef glFinishFenceNV -#undef glFlush -#undef glFlushMappedBufferRange -#undef glFlushMappedBufferRangeEXT -#undef glFogf -#undef glFogfv -#undef glFogx -#undef glFogxOES -#undef glFogxv -#undef glFogxvOES -#undef glFragmentCoverageColorNV -#undef glFramebufferParameteri -#undef glFramebufferRenderbuffer -#undef glFramebufferRenderbufferOES -#undef glFramebufferSampleLocationsfvNV -#undef glFramebufferTexture -#undef glFramebufferTexture2D -#undef glFramebufferTexture2DMultisampleEXT -#undef glFramebufferTexture2DMultisampleIMG -#undef glFramebufferTexture2DOES -#undef glFramebufferTexture3DOES -#undef glFramebufferTextureEXT -#undef glFramebufferTextureLayer -#undef glFramebufferTextureMultisampleMultiviewOVR -#undef glFramebufferTextureMultiviewOVR -#undef glFramebufferTextureOES -#undef glFrontFace -#undef glFrustumf -#undef glFrustumfOES -#undef glFrustumx -#undef glFrustumxOES -#undef glGenBuffers -#undef glGenFencesNV -#undef glGenFramebuffers -#undef glGenFramebuffersOES -#undef glGenPathsNV -#undef glGenPerfMonitorsAMD -#undef glGenProgramPipelines -#undef glGenProgramPipelinesEXT -#undef glGenQueries -#undef glGenQueriesEXT -#undef glGenRenderbuffers -#undef glGenRenderbuffersOES -#undef glGenSamplers -#undef glGenTextures -#undef glGenTransformFeedbacks -#undef glGenVertexArrays -#undef glGenVertexArraysOES -#undef glGenerateMipmap -#undef glGenerateMipmapOES -#undef glGetActiveAttrib -#undef glGetActiveUniform -#undef glGetActiveUniformBlockName -#undef glGetActiveUniformBlockiv -#undef glGetActiveUniformsiv -#undef glGetAttachedShaders -#undef glGetAttribLocation -#undef glGetBooleani_v -#undef glGetBooleanv -#undef glGetBufferParameteri64v -#undef glGetBufferParameteriv -#undef glGetBufferPointerv -#undef glGetBufferPointervOES -#undef glGetClipPlanef -#undef glGetClipPlanefOES -#undef glGetClipPlanex -#undef glGetClipPlanexOES -#undef glGetCoverageModulationTableNV -#undef glGetDebugMessageLog -#undef glGetDebugMessageLogKHR -#undef glGetDriverControlStringQCOM -#undef glGetDriverControlsQCOM -#undef glGetError -#undef glGetFenceivNV -#undef glGetFirstPerfQueryIdINTEL -#undef glGetFixedv -#undef glGetFixedvOES -#undef glGetFloati_vNV -#undef glGetFloatv -#undef glGetFragDataIndexEXT -#undef glGetFragDataLocation -#undef glGetFramebufferAttachmentParameteriv -#undef glGetFramebufferAttachmentParameterivOES -#undef glGetFramebufferParameteriv -#undef glGetGraphicsResetStatus -#undef glGetGraphicsResetStatusEXT -#undef glGetGraphicsResetStatusKHR -#undef glGetImageHandleNV -#undef glGetInteger64i_v -#undef glGetInteger64v -#undef glGetInteger64vAPPLE -#undef glGetIntegeri_v -#undef glGetIntegeri_vEXT -#undef glGetIntegerv -#undef glGetInternalformatSampleivNV -#undef glGetInternalformativ -#undef glGetLightfv -#undef glGetLightxv -#undef glGetLightxvOES -#undef glGetMaterialfv -#undef glGetMaterialxv -#undef glGetMaterialxvOES -#undef glGetMultisamplefv -#undef glGetNextPerfQueryIdINTEL -#undef glGetObjectLabel -#undef glGetObjectLabelEXT -#undef glGetObjectLabelKHR -#undef glGetObjectPtrLabel -#undef glGetObjectPtrLabelKHR -#undef glGetPathCommandsNV -#undef glGetPathCoordsNV -#undef glGetPathDashArrayNV -#undef glGetPathLengthNV -#undef glGetPathMetricRangeNV -#undef glGetPathMetricsNV -#undef glGetPathParameterfvNV -#undef glGetPathParameterivNV -#undef glGetPathSpacingNV -#undef glGetPerfCounterInfoINTEL -#undef glGetPerfMonitorCounterDataAMD -#undef glGetPerfMonitorCounterInfoAMD -#undef glGetPerfMonitorCounterStringAMD -#undef glGetPerfMonitorCountersAMD -#undef glGetPerfMonitorGroupStringAMD -#undef glGetPerfMonitorGroupsAMD -#undef glGetPerfQueryDataINTEL -#undef glGetPerfQueryIdByNameINTEL -#undef glGetPerfQueryInfoINTEL -#undef glGetPointerv -#undef glGetPointervKHR -#undef glGetProgramBinary -#undef glGetProgramBinaryOES -#undef glGetProgramInfoLog -#undef glGetProgramInterfaceiv -#undef glGetProgramPipelineInfoLog -#undef glGetProgramPipelineInfoLogEXT -#undef glGetProgramPipelineiv -#undef glGetProgramPipelineivEXT -#undef glGetProgramResourceIndex -#undef glGetProgramResourceLocation -#undef glGetProgramResourceLocationIndexEXT -#undef glGetProgramResourceName -#undef glGetProgramResourcefvNV -#undef glGetProgramResourceiv -#undef glGetProgramiv -#undef glGetQueryObjecti64vEXT -#undef glGetQueryObjectivEXT -#undef glGetQueryObjectui64vEXT -#undef glGetQueryObjectuiv -#undef glGetQueryObjectuivEXT -#undef glGetQueryiv -#undef glGetQueryivEXT -#undef glGetRenderbufferParameteriv -#undef glGetRenderbufferParameterivOES -#undef glGetSamplerParameterIiv -#undef glGetSamplerParameterIivEXT -#undef glGetSamplerParameterIivOES -#undef glGetSamplerParameterIuiv -#undef glGetSamplerParameterIuivEXT -#undef glGetSamplerParameterIuivOES -#undef glGetSamplerParameterfv -#undef glGetSamplerParameteriv -#undef glGetShaderInfoLog -#undef glGetShaderPrecisionFormat -#undef glGetShaderSource -#undef glGetShaderiv -#undef glGetString -#undef glGetStringi -#undef glGetSynciv -#undef glGetSyncivAPPLE -#undef glGetTexEnvfv -#undef glGetTexEnviv -#undef glGetTexEnvxv -#undef glGetTexEnvxvOES -#undef glGetTexGenfvOES -#undef glGetTexGenivOES -#undef glGetTexGenxvOES -#undef glGetTexLevelParameterfv -#undef glGetTexLevelParameteriv -#undef glGetTexParameterIiv -#undef glGetTexParameterIivEXT -#undef glGetTexParameterIivOES -#undef glGetTexParameterIuiv -#undef glGetTexParameterIuivEXT -#undef glGetTexParameterIuivOES -#undef glGetTexParameterfv -#undef glGetTexParameteriv -#undef glGetTexParameterxv -#undef glGetTexParameterxvOES -#undef glGetTextureHandleNV -#undef glGetTextureSamplerHandleNV -#undef glGetTransformFeedbackVarying -#undef glGetTranslatedShaderSourceANGLE -#undef glGetUniformBlockIndex -#undef glGetUniformIndices -#undef glGetUniformLocation -#undef glGetUniformfv -#undef glGetUniformiv -#undef glGetUniformuiv -#undef glGetVertexAttribIiv -#undef glGetVertexAttribIuiv -#undef glGetVertexAttribPointerv -#undef glGetVertexAttribfv -#undef glGetVertexAttribiv -#undef glGetnUniformfv -#undef glGetnUniformfvEXT -#undef glGetnUniformfvKHR -#undef glGetnUniformiv -#undef glGetnUniformivEXT -#undef glGetnUniformivKHR -#undef glGetnUniformuiv -#undef glGetnUniformuivKHR -#undef glHint -#undef glInsertEventMarkerEXT -#undef glInterpolatePathsNV -#undef glInvalidateFramebuffer -#undef glInvalidateSubFramebuffer -#undef glIsBuffer -#undef glIsEnabled -#undef glIsEnabledi -#undef glIsEnablediEXT -#undef glIsEnablediNV -#undef glIsEnablediOES -#undef glIsFenceNV -#undef glIsFramebuffer -#undef glIsFramebufferOES -#undef glIsImageHandleResidentNV -#undef glIsPathNV -#undef glIsPointInFillPathNV -#undef glIsPointInStrokePathNV -#undef glIsProgram -#undef glIsProgramPipeline -#undef glIsProgramPipelineEXT -#undef glIsQuery -#undef glIsQueryEXT -#undef glIsRenderbuffer -#undef glIsRenderbufferOES -#undef glIsSampler -#undef glIsShader -#undef glIsSync -#undef glIsSyncAPPLE -#undef glIsTexture -#undef glIsTextureHandleResidentNV -#undef glIsTransformFeedback -#undef glIsVertexArray -#undef glIsVertexArrayOES -#undef glLabelObjectEXT -#undef glLightModelf -#undef glLightModelfv -#undef glLightModelx -#undef glLightModelxOES -#undef glLightModelxv -#undef glLightModelxvOES -#undef glLightf -#undef glLightfv -#undef glLightx -#undef glLightxOES -#undef glLightxv -#undef glLightxvOES -#undef glLineWidth -#undef glLineWidthx -#undef glLineWidthxOES -#undef glLinkProgram -#undef glLoadIdentity -#undef glLoadMatrixf -#undef glLoadMatrixx -#undef glLoadMatrixxOES -#undef glLoadPaletteFromModelViewMatrixOES -#undef glLogicOp -#undef glMakeImageHandleNonResidentNV -#undef glMakeImageHandleResidentNV -#undef glMakeTextureHandleNonResidentNV -#undef glMakeTextureHandleResidentNV -#undef glMapBufferOES -#undef glMapBufferRange -#undef glMapBufferRangeEXT -#undef glMaterialf -#undef glMaterialfv -#undef glMaterialx -#undef glMaterialxOES -#undef glMaterialxv -#undef glMaterialxvOES -#undef glMatrixIndexPointerOES -#undef glMatrixLoad3x2fNV -#undef glMatrixLoad3x3fNV -#undef glMatrixLoadTranspose3x3fNV -#undef glMatrixMode -#undef glMatrixMult3x2fNV -#undef glMatrixMult3x3fNV -#undef glMatrixMultTranspose3x3fNV -#undef glMemoryBarrier -#undef glMemoryBarrierByRegion -#undef glMinSampleShading -#undef glMinSampleShadingOES -#undef glMultMatrixf -#undef glMultMatrixx -#undef glMultMatrixxOES -#undef glMultiDrawArraysEXT -#undef glMultiDrawArraysIndirectEXT -#undef glMultiDrawElementsBaseVertexEXT -#undef glMultiDrawElementsBaseVertexOES -#undef glMultiDrawElementsEXT -#undef glMultiDrawElementsIndirectEXT -#undef glMultiTexCoord4f -#undef glMultiTexCoord4x -#undef glMultiTexCoord4xOES -#undef glNamedFramebufferSampleLocationsfvNV -#undef glNormal3f -#undef glNormal3x -#undef glNormal3xOES -#undef glNormalPointer -#undef glObjectLabel -#undef glObjectLabelKHR -#undef glObjectPtrLabel -#undef glObjectPtrLabelKHR -#undef glOrthof -#undef glOrthofOES -#undef glOrthox -#undef glOrthoxOES -#undef glPatchParameteri -#undef glPatchParameteriEXT -#undef glPatchParameteriOES -#undef glPathCommandsNV -#undef glPathCoordsNV -#undef glPathCoverDepthFuncNV -#undef glPathDashArrayNV -#undef glPathGlyphIndexArrayNV -#undef glPathGlyphIndexRangeNV -#undef glPathGlyphRangeNV -#undef glPathGlyphsNV -#undef glPathMemoryGlyphIndexArrayNV -#undef glPathParameterfNV -#undef glPathParameterfvNV -#undef glPathParameteriNV -#undef glPathParameterivNV -#undef glPathStencilDepthOffsetNV -#undef glPathStencilFuncNV -#undef glPathStringNV -#undef glPathSubCommandsNV -#undef glPathSubCoordsNV -#undef glPauseTransformFeedback -#undef glPixelStorei -#undef glPointAlongPathNV -#undef glPointParameterf -#undef glPointParameterfv -#undef glPointParameterx -#undef glPointParameterxOES -#undef glPointParameterxv -#undef glPointParameterxvOES -#undef glPointSize -#undef glPointSizePointerOES -#undef glPointSizex -#undef glPointSizexOES -#undef glPolygonModeNV -#undef glPolygonOffset -#undef glPolygonOffsetx -#undef glPolygonOffsetxOES -#undef glPopDebugGroup -#undef glPopDebugGroupKHR -#undef glPopGroupMarkerEXT -#undef glPopMatrix -#undef glPrimitiveBoundingBox -#undef glPrimitiveBoundingBoxEXT -#undef glPrimitiveBoundingBoxOES -#undef glProgramBinary -#undef glProgramBinaryOES -#undef glProgramParameteri -#undef glProgramParameteriEXT -#undef glProgramPathFragmentInputGenNV -#undef glProgramUniform1f -#undef glProgramUniform1fEXT -#undef glProgramUniform1fv -#undef glProgramUniform1fvEXT -#undef glProgramUniform1i -#undef glProgramUniform1iEXT -#undef glProgramUniform1iv -#undef glProgramUniform1ivEXT -#undef glProgramUniform1ui -#undef glProgramUniform1uiEXT -#undef glProgramUniform1uiv -#undef glProgramUniform1uivEXT -#undef glProgramUniform2f -#undef glProgramUniform2fEXT -#undef glProgramUniform2fv -#undef glProgramUniform2fvEXT -#undef glProgramUniform2i -#undef glProgramUniform2iEXT -#undef glProgramUniform2iv -#undef glProgramUniform2ivEXT -#undef glProgramUniform2ui -#undef glProgramUniform2uiEXT -#undef glProgramUniform2uiv -#undef glProgramUniform2uivEXT -#undef glProgramUniform3f -#undef glProgramUniform3fEXT -#undef glProgramUniform3fv -#undef glProgramUniform3fvEXT -#undef glProgramUniform3i -#undef glProgramUniform3iEXT -#undef glProgramUniform3iv -#undef glProgramUniform3ivEXT -#undef glProgramUniform3ui -#undef glProgramUniform3uiEXT -#undef glProgramUniform3uiv -#undef glProgramUniform3uivEXT -#undef glProgramUniform4f -#undef glProgramUniform4fEXT -#undef glProgramUniform4fv -#undef glProgramUniform4fvEXT -#undef glProgramUniform4i -#undef glProgramUniform4iEXT -#undef glProgramUniform4iv -#undef glProgramUniform4ivEXT -#undef glProgramUniform4ui -#undef glProgramUniform4uiEXT -#undef glProgramUniform4uiv -#undef glProgramUniform4uivEXT -#undef glProgramUniformHandleui64NV -#undef glProgramUniformHandleui64vNV -#undef glProgramUniformMatrix2fv -#undef glProgramUniformMatrix2fvEXT -#undef glProgramUniformMatrix2x3fv -#undef glProgramUniformMatrix2x3fvEXT -#undef glProgramUniformMatrix2x4fv -#undef glProgramUniformMatrix2x4fvEXT -#undef glProgramUniformMatrix3fv -#undef glProgramUniformMatrix3fvEXT -#undef glProgramUniformMatrix3x2fv -#undef glProgramUniformMatrix3x2fvEXT -#undef glProgramUniformMatrix3x4fv -#undef glProgramUniformMatrix3x4fvEXT -#undef glProgramUniformMatrix4fv -#undef glProgramUniformMatrix4fvEXT -#undef glProgramUniformMatrix4x2fv -#undef glProgramUniformMatrix4x2fvEXT -#undef glProgramUniformMatrix4x3fv -#undef glProgramUniformMatrix4x3fvEXT -#undef glPushDebugGroup -#undef glPushDebugGroupKHR -#undef glPushGroupMarkerEXT -#undef glPushMatrix -#undef glQueryCounterEXT -#undef glQueryMatrixxOES -#undef glRasterSamplesEXT -#undef glReadBuffer -#undef glReadBufferIndexedEXT -#undef glReadBufferNV -#undef glReadPixels -#undef glReadnPixels -#undef glReadnPixelsEXT -#undef glReadnPixelsKHR -#undef glReleaseShaderCompiler -#undef glRenderbufferStorage -#undef glRenderbufferStorageMultisample -#undef glRenderbufferStorageMultisampleANGLE -#undef glRenderbufferStorageMultisampleAPPLE -#undef glRenderbufferStorageMultisampleEXT -#undef glRenderbufferStorageMultisampleIMG -#undef glRenderbufferStorageMultisampleNV -#undef glRenderbufferStorageOES -#undef glResolveDepthValuesNV -#undef glResolveMultisampleFramebufferAPPLE -#undef glResumeTransformFeedback -#undef glRotatef -#undef glRotatex -#undef glRotatexOES -#undef glSampleCoverage -#undef glSampleCoveragex -#undef glSampleCoveragexOES -#undef glSampleMaski -#undef glSamplerParameterIiv -#undef glSamplerParameterIivEXT -#undef glSamplerParameterIivOES -#undef glSamplerParameterIuiv -#undef glSamplerParameterIuivEXT -#undef glSamplerParameterIuivOES -#undef glSamplerParameterf -#undef glSamplerParameterfv -#undef glSamplerParameteri -#undef glSamplerParameteriv -#undef glScalef -#undef glScalex -#undef glScalexOES -#undef glScissor -#undef glScissorArrayvNV -#undef glScissorIndexedNV -#undef glScissorIndexedvNV -#undef glSelectPerfMonitorCountersAMD -#undef glSetFenceNV -#undef glShadeModel -#undef glShaderBinary -#undef glShaderSource -#undef glStartTilingQCOM -#undef glStencilFillPathInstancedNV -#undef glStencilFillPathNV -#undef glStencilFunc -#undef glStencilFuncSeparate -#undef glStencilMask -#undef glStencilMaskSeparate -#undef glStencilOp -#undef glStencilOpSeparate -#undef glStencilStrokePathInstancedNV -#undef glStencilStrokePathNV -#undef glStencilThenCoverFillPathInstancedNV -#undef glStencilThenCoverFillPathNV -#undef glStencilThenCoverStrokePathInstancedNV -#undef glStencilThenCoverStrokePathNV -#undef glSubpixelPrecisionBiasNV -#undef glTestFenceNV -#undef glTexBuffer -#undef glTexBufferEXT -#undef glTexBufferOES -#undef glTexBufferRange -#undef glTexBufferRangeEXT -#undef glTexBufferRangeOES -#undef glTexCoordPointer -#undef glTexEnvf -#undef glTexEnvfv -#undef glTexEnvi -#undef glTexEnviv -#undef glTexEnvx -#undef glTexEnvxOES -#undef glTexEnvxv -#undef glTexEnvxvOES -#undef glTexGenfOES -#undef glTexGenfvOES -#undef glTexGeniOES -#undef glTexGenivOES -#undef glTexGenxOES -#undef glTexGenxvOES -#undef glTexImage2D -#undef glTexImage3D -#undef glTexImage3DOES -#undef glTexPageCommitmentEXT -#undef glTexParameterIiv -#undef glTexParameterIivEXT -#undef glTexParameterIivOES -#undef glTexParameterIuiv -#undef glTexParameterIuivEXT -#undef glTexParameterIuivOES -#undef glTexParameterf -#undef glTexParameterfv -#undef glTexParameteri -#undef glTexParameteriv -#undef glTexParameterx -#undef glTexParameterxOES -#undef glTexParameterxv -#undef glTexParameterxvOES -#undef glTexStorage1DEXT -#undef glTexStorage2D -#undef glTexStorage2DEXT -#undef glTexStorage2DMultisample -#undef glTexStorage3D -#undef glTexStorage3DEXT -#undef glTexStorage3DMultisample -#undef glTexStorage3DMultisampleOES -#undef glTexSubImage2D -#undef glTexSubImage3D -#undef glTexSubImage3DOES -#undef glTextureStorage1DEXT -#undef glTextureStorage2DEXT -#undef glTextureStorage3DEXT -#undef glTextureViewEXT -#undef glTextureViewOES -#undef glTransformFeedbackVaryings -#undef glTransformPathNV -#undef glTranslatef -#undef glTranslatex -#undef glTranslatexOES -#undef glUniform1f -#undef glUniform1fv -#undef glUniform1i -#undef glUniform1iv -#undef glUniform1ui -#undef glUniform1uiv -#undef glUniform2f -#undef glUniform2fv -#undef glUniform2i -#undef glUniform2iv -#undef glUniform2ui -#undef glUniform2uiv -#undef glUniform3f -#undef glUniform3fv -#undef glUniform3i -#undef glUniform3iv -#undef glUniform3ui -#undef glUniform3uiv -#undef glUniform4f -#undef glUniform4fv -#undef glUniform4i -#undef glUniform4iv -#undef glUniform4ui -#undef glUniform4uiv -#undef glUniformBlockBinding -#undef glUniformHandleui64NV -#undef glUniformHandleui64vNV -#undef glUniformMatrix2fv -#undef glUniformMatrix2x3fv -#undef glUniformMatrix2x3fvNV -#undef glUniformMatrix2x4fv -#undef glUniformMatrix2x4fvNV -#undef glUniformMatrix3fv -#undef glUniformMatrix3x2fv -#undef glUniformMatrix3x2fvNV -#undef glUniformMatrix3x4fv -#undef glUniformMatrix3x4fvNV -#undef glUniformMatrix4fv -#undef glUniformMatrix4x2fv -#undef glUniformMatrix4x2fvNV -#undef glUniformMatrix4x3fv -#undef glUniformMatrix4x3fvNV -#undef glUnmapBuffer -#undef glUnmapBufferOES -#undef glUseProgram -#undef glUseProgramStages -#undef glUseProgramStagesEXT -#undef glValidateProgram -#undef glValidateProgramPipeline -#undef glValidateProgramPipelineEXT -#undef glVertexAttrib1f -#undef glVertexAttrib1fv -#undef glVertexAttrib2f -#undef glVertexAttrib2fv -#undef glVertexAttrib3f -#undef glVertexAttrib3fv -#undef glVertexAttrib4f -#undef glVertexAttrib4fv -#undef glVertexAttribBinding -#undef glVertexAttribDivisor -#undef glVertexAttribDivisorANGLE -#undef glVertexAttribDivisorEXT -#undef glVertexAttribDivisorNV -#undef glVertexAttribFormat -#undef glVertexAttribI4i -#undef glVertexAttribI4iv -#undef glVertexAttribI4ui -#undef glVertexAttribI4uiv -#undef glVertexAttribIFormat -#undef glVertexAttribIPointer -#undef glVertexAttribPointer -#undef glVertexBindingDivisor -#undef glVertexPointer -#undef glViewport -#undef glViewportArrayvNV -#undef glViewportIndexedfNV -#undef glViewportIndexedfvNV -#undef glWaitSync -#undef glWaitSyncAPPLE -#undef glWeightPathsNV -#undef glWeightPointerOES diff --git a/libs/hwui/debug/nullegl.cpp b/libs/hwui/debug/nullegl.cpp deleted file mode 100644 index ca47f8fd22ef..000000000000 --- a/libs/hwui/debug/nullegl.cpp +++ /dev/null @@ -1,175 +0,0 @@ -/* - * 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 -#include - -#include -#include -#include - -static EGLDisplay gDisplay = (EGLDisplay)1; -static EGLSyncKHR gFence = (EGLSyncKHR)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) { - if (name == EGL_EXTENSIONS) { - return "EGL_KHR_swap_buffers_with_damage"; - } - 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; -} - -EGLBoolean eglSwapBuffersWithDamageKHR(EGLDisplay dpy, EGLSurface surface, EGLint* rects, - EGLint rectCount) { - return EGL_TRUE; -} - -EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target, - EGLClientBuffer buffer, const EGLint* attrib_list) { - return (EGLImageKHR)malloc(sizeof(EGLImageKHR)); -} - -EGLSyncKHR eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint* attrib_list) { - return gFence; -} - -EGLBoolean eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync) { - return EGL_TRUE; -} - -EGLint eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout) { - return EGL_CONDITION_SATISFIED_KHR; -} - -EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR image) { - free(image); - return EGL_TRUE; -} - -void eglBeginFrame(EGLDisplay dpy, EGLSurface surface) {} diff --git a/libs/hwui/debug/wrap_gles.cpp b/libs/hwui/debug/wrap_gles.cpp deleted file mode 100644 index 8dc946e5667b..000000000000 --- a/libs/hwui/debug/wrap_gles.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2016 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 "GlesDriver.h" - -using namespace android::uirenderer::debug; - -#undef API_ENTRY -#undef CALL_GL_API -#undef CALL_GL_API_RETURN - -#define API_ENTRY(x) x -#define CALL_GL_API(api, ...) GlesDriver::get()->api##_(__VA_ARGS__) -#define CALL_GL_API_RETURN(api, ...) return GlesDriver::get()->api##_(__VA_ARGS__) - -#include "gles_stubs.in" - -#undef API_ENTRY -#undef CALL_GL_API -#undef CALL_GL_API_RETURN diff --git a/libs/hwui/debug/wrap_gles.h b/libs/hwui/debug/wrap_gles.h deleted file mode 100644 index 3da6e802d178..000000000000 --- a/libs/hwui/debug/wrap_gles.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2016 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'ing this file is bad, bad things should be compile errors -#ifdef HWUI_GLES_WRAP_ENABLED -#error wrap_gles.h should only be used as an auto-included header, don't directly #include it -#endif -#define HWUI_GLES_WRAP_ENABLED - -#include -#include -#include -#include -#include -#include -#include - -// constant used by the NULL GPU implementation as well as HWUI's unit tests -constexpr int NULL_GPU_MAX_TEXTURE_SIZE = 2048; - -// Generate stubs that route all the calls to our function table -#include "gles_redefine.h" - -#define GL_ENTRY(ret, api, ...) ret api(__VA_ARGS__); - -#include "gles_decls.in" -#undef GL_ENTRY diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 41cb8fdc66bd..0c5599ed2d79 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -31,10 +31,6 @@ #include "utils/TimeUtils.h" #include "utils/TraceUtils.h" -#ifdef HWUI_GLES_WRAP_ENABLED -#include "debug/GlesDriver.h" -#endif - #include #include @@ -197,12 +193,7 @@ void RenderThread::requireGlContext() { } mEglManager->initialize(); -#ifdef HWUI_GLES_WRAP_ENABLED - debug::GlesDriver* driver = debug::GlesDriver::get(); - sk_sp glInterface(driver->getSkiaInterface()); -#else sk_sp glInterface(GrGLCreateNativeInterface()); -#endif LOG_ALWAYS_FATAL_IF(!glInterface.get()); GrContextOptions options; diff --git a/libs/hwui/tests/microbench/main.cpp b/libs/hwui/tests/microbench/main.cpp index b5abf5bc5efa..9c4d25968d60 100644 --- a/libs/hwui/tests/microbench/main.cpp +++ b/libs/hwui/tests/microbench/main.cpp @@ -14,9 +14,6 @@ * limitations under the License. */ -#include "debug/GlesDriver.h" -#include "debug/NullGlesDriver.h" - #include "hwui/Typeface.h" #include @@ -24,10 +21,8 @@ #include using namespace android; -using namespace android::uirenderer; int main(int argc, char** argv) { - debug::GlesDriver::replace(std::make_unique()); benchmark::Initialize(&argc, argv); Typeface::setRobotoTypefaceForTest(); benchmark::RunSpecifiedBenchmarks(); diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp index a671bdada09a..7b76bd80de4d 100644 --- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp +++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp @@ -29,6 +29,7 @@ #include "pipeline/skia/SkiaRecordingCanvas.h" #include "pipeline/skia/SkiaUtils.h" #include "renderthread/CanvasContext.h" +#include "tests/common/TestContext.h" #include "tests/common/TestUtils.h" #include @@ -424,17 +425,9 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clip_replace) { EXPECT_EQ(1, surface->canvas()->mDrawCounter); } -static sp createDummySurface() { - sp producer; - sp consumer; - BufferQueue::createBufferQueue(&producer, &consumer); - producer->setMaxDequeuedBufferCount(1); - producer->setAsyncMode(true); - return new Surface(producer); -} - RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, context_lost) { - auto surface = createDummySurface(); + test::TestContext context; + auto surface = context.surface(); auto pipeline = std::make_unique(renderThread); EXPECT_FALSE(pipeline->isSurfaceReady()); EXPECT_TRUE(pipeline->setSurface(surface.get(), SwapBehavior::kSwap_default, ColorMode::SRGB, 0)); diff --git a/libs/hwui/tests/unit/main.cpp b/libs/hwui/tests/unit/main.cpp index 83d888c310f0..402cb5814366 100644 --- a/libs/hwui/tests/unit/main.cpp +++ b/libs/hwui/tests/unit/main.cpp @@ -18,8 +18,6 @@ #include "gtest/gtest.h" #include "Properties.h" -#include "debug/GlesDriver.h" -#include "debug/NullGlesDriver.h" #include "hwui/Typeface.h" #include "tests/common/LeakChecker.h" @@ -65,7 +63,6 @@ int main(int argc, char* argv[]) { } // Replace the default GLES driver - debug::GlesDriver::replace(std::make_unique()); Properties::isolatedProcess = true; // Run the tests diff --git a/libs/hwui/utils/GLUtils.cpp b/libs/hwui/utils/GLUtils.cpp index c694e93f7e21..61897627d842 100644 --- a/libs/hwui/utils/GLUtils.cpp +++ b/libs/hwui/utils/GLUtils.cpp @@ -21,19 +21,10 @@ #include "GLUtils.h" -#if DEBUG_OPENGL >= DEBUG_LEVEL_HIGH && !defined(HWUI_GLES_WRAP_ENABLED) -#error Setting DEBUG_OPENGL to HIGH requires setting HWUI_ENABLE_OPENGL_VALIDATION to true in the Android.mk! -#endif - namespace android { namespace uirenderer { bool GLUtils::dumpGLErrors() { -#if DEBUG_OPENGL >= DEBUG_LEVEL_HIGH - // If DEBUG_LEVEL_HIGH is set then every GLES call is already wrapped - // and asserts that there was no error. So this can just return success. - return false; -#else bool errorObserved = false; GLenum status = GL_NO_ERROR; while ((status = glGetError()) != GL_NO_ERROR) { @@ -56,7 +47,6 @@ bool GLUtils::dumpGLErrors() { } } return errorObserved; -#endif } const char* GLUtils::getGLFramebufferError() { -- cgit v1.2.3 From f9f964d8d282a67a56d72b7206252ad447465fae Mon Sep 17 00:00:00 2001 From: Nader Jawad Date: Fri, 24 May 2019 10:33:55 -0700 Subject: Brought back compatibility check for falling back on dest_out whenever clear is used as a blend mode to draw bitmaps Modified SkiaRecordingCanvas to call filterBitmap instead of filterPaint in order to conduct the necessary compatibility checks to use dest_out instead of clear whenever a bitmap is drawn Test: In progress.. Bug: 131689368 Change-Id: I56d015e773b312cac948d850ca2997d8df0cc66d --- libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp | 23 ++++++----------------- libs/hwui/pipeline/skia/SkiaRecordingCanvas.h | 2 +- 2 files changed, 7 insertions(+), 18 deletions(-) (limited to 'libs') diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp index a161bfdf2826..16c8b8923074 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -161,8 +161,7 @@ void SkiaRecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) { // Recording Canvas draw operations: Bitmaps // ---------------------------------------------------------------------------- -SkiaCanvas::PaintCoW&& SkiaRecordingCanvas::filterBitmap(PaintCoW&& paint, - sk_sp colorSpaceFilter) { +SkiaCanvas::PaintCoW&& SkiaRecordingCanvas::filterBitmap(PaintCoW&& paint) { bool fixBlending = false; bool fixAA = false; if (paint) { @@ -172,23 +171,13 @@ SkiaCanvas::PaintCoW&& SkiaRecordingCanvas::filterBitmap(PaintCoW&& paint, fixAA = paint->isAntiAlias(); } - if (fixBlending || fixAA || colorSpaceFilter) { + if (fixBlending || fixAA) { SkPaint& tmpPaint = paint.writeable(); if (fixBlending) { tmpPaint.setBlendMode(SkBlendMode::kDstOut); } - if (colorSpaceFilter) { - if (tmpPaint.getColorFilter()) { - tmpPaint.setColorFilter(SkColorFilters::Compose( - tmpPaint.refColorFilter(), std::move(colorSpaceFilter))); - } else { - tmpPaint.setColorFilter(std::move(colorSpaceFilter)); - } - LOG_ALWAYS_FATAL_IF(!tmpPaint.getColorFilter()); - } - // disabling AA on bitmap draws matches legacy HWUI behavior tmpPaint.setAntiAlias(false); } @@ -198,7 +187,7 @@ SkiaCanvas::PaintCoW&& SkiaRecordingCanvas::filterBitmap(PaintCoW&& paint, void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) { sk_sp image = bitmap.makeImage(); - mRecorder.drawImage(image, left, top, filterPaint(paint), bitmap.palette()); + mRecorder.drawImage(image, left, top, filterBitmap(paint), bitmap.palette()); // if image->unique() is true, then mRecorder.drawImage failed for some reason. It also means // it is not safe to store a raw SkImage pointer, because the image object will be destroyed // when this function ends. @@ -212,7 +201,7 @@ void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, con concat(matrix); sk_sp image = bitmap.makeImage(); - mRecorder.drawImage(image, 0, 0, filterPaint(paint), bitmap.palette()); + mRecorder.drawImage(image, 0, 0, filterBitmap(paint), bitmap.palette()); if (!bitmap.isImmutable() && image.get() && !image->unique()) { mDisplayList->mMutableImages.push_back(image.get()); } @@ -225,7 +214,7 @@ void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom); sk_sp image = bitmap.makeImage(); - mRecorder.drawImageRect(image, srcRect, dstRect, filterPaint(paint), + mRecorder.drawImageRect(image, srcRect, dstRect, filterBitmap(paint), SkCanvas::kFast_SrcRectConstraint, bitmap.palette()); if (!bitmap.isImmutable() && image.get() && !image->unique() && !srcRect.isEmpty() && !dstRect.isEmpty()) { @@ -263,7 +252,7 @@ void SkiaRecordingCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& ch filteredPaint.writeable().setFilterQuality(kLow_SkFilterQuality); } sk_sp image = bitmap.makeImage(); - mRecorder.drawImageLattice(image, lattice, dst, filterPaint(std::move(filteredPaint)), + mRecorder.drawImageLattice(image, lattice, dst, filterBitmap(std::move(filteredPaint)), bitmap.palette()); if (!bitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) { mDisplayList->mMutableImages.push_back(image.get()); diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h index afeccea3fb70..c42cea33211e 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h @@ -90,7 +90,7 @@ private: */ void initDisplayList(uirenderer::RenderNode* renderNode, int width, int height); - PaintCoW&& filterBitmap(PaintCoW&& paint, sk_sp colorSpaceFilter); + PaintCoW&& filterBitmap(PaintCoW&& paint); }; } // namespace skiapipeline -- cgit v1.2.3 From 182cbf9d509ede104789a50b334669712442b3ff Mon Sep 17 00:00:00 2001 From: Fedor Kudasov Date: Wed, 5 Jun 2019 10:30:20 +0100 Subject: Merge libhwui and libhwui-host targets Bug: 117921091 Test: all tests should pass Change-Id: I6d5c3b6af2a2837e5f3e729b165116e2860859ab --- libs/hwui/Android.bp | 270 ++++++++++++++++++++++++--------------------------- 1 file changed, 128 insertions(+), 142 deletions(-) (limited to 'libs') diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index e8a8f27522dc..82bcf9e9afa4 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -33,11 +33,6 @@ cc_defaults { include_dirs: [ "external/skia/include/private", "external/skia/src/core", - "external/skia/src/effects", - "external/skia/src/image", - "external/skia/src/utils", - "external/skia/src/gpu", - "external/skia/src/shaders", ], product_variables: { @@ -47,34 +42,67 @@ cc_defaults { }, }, }, + + target: { + android: { + include_dirs: [ + "external/skia/src/effects", + "external/skia/src/image", + "external/skia/src/utils", + "external/skia/src/gpu", + "external/skia/src/shaders", + ], + }, + host: { + include_dirs: [ + "external/vulkan-headers/include", + ], + cflags: [ + "-Wno-unused-variable", + ], + } + } } cc_defaults { name: "hwui_static_deps", shared_libs: [ - "liblog", - "libcutils", "libbase", - "libstatslog", - "libutils", - "libEGL", - "libGLESv1_CM", - "libGLESv2", - "libGLESv3", - "libvulkan", - "libui", - "libgui", - "libprotobuf-cpp-lite", "libharfbuzz_ng", - "libft2", "libminikin", - "libandroidfw", - "libcrypto", - "libsync", - ], - static_libs: [ - "libEGL_blobCache", ], + + target: { + android: { + shared_libs: [ + "liblog", + "libcutils", + "libstatslog", + "libutils", + "libEGL", + "libGLESv1_CM", + "libGLESv2", + "libGLESv3", + "libvulkan", + "libui", + "libgui", + "libprotobuf-cpp-lite", + "libft2", + "libandroidfw", + "libcrypto", + "libsync", + ], + static_libs: [ + "libEGL_blobCache", + ], + }, + host: { + static_libs: [ + "libandroidfw", + "libutils", + ], + } + } } cc_defaults { @@ -139,91 +167,105 @@ cc_defaults { "hwui/MinikinUtils.cpp", "hwui/PaintImpl.cpp", "hwui/Typeface.cpp", - "pipeline/skia/GLFunctorDrawable.cpp", - "pipeline/skia/LayerDrawable.cpp", - "pipeline/skia/RenderNodeDrawable.cpp", - "pipeline/skia/ReorderBarrierDrawables.cpp", - "pipeline/skia/ShaderCache.cpp", - "pipeline/skia/SkiaDisplayList.cpp", - "pipeline/skia/SkiaMemoryTracer.cpp", - "pipeline/skia/SkiaOpenGLPipeline.cpp", - "pipeline/skia/SkiaPipeline.cpp", - "pipeline/skia/SkiaProfileRenderer.cpp", - "pipeline/skia/SkiaRecordingCanvas.cpp", - "pipeline/skia/SkiaVulkanPipeline.cpp", - "pipeline/skia/VectorDrawableAtlas.cpp", - "pipeline/skia/VkFunctorDrawable.cpp", - "pipeline/skia/VkInteropFunctorDrawable.cpp", - "renderstate/RenderState.cpp", - "renderthread/CacheManager.cpp", - "renderthread/CanvasContext.cpp", - "renderthread/DrawFrameTask.cpp", - "renderthread/EglManager.cpp", - "renderthread/ReliableSurface.cpp", - "renderthread/VulkanManager.cpp", - "renderthread/VulkanSurface.cpp", - "renderthread/RenderProxy.cpp", - "renderthread/RenderTask.cpp", - "renderthread/RenderThread.cpp", - "renderthread/TimeLord.cpp", - "renderthread/Frame.cpp", - "service/GraphicsStatsService.cpp", - "surfacetexture/EGLConsumer.cpp", - "surfacetexture/ImageConsumer.cpp", - "surfacetexture/SurfaceTexture.cpp", - "thread/CommonPool.cpp", "utils/Blur.cpp", - "utils/Color.cpp", - "utils/GLUtils.cpp", "utils/LinearAllocator.cpp", - "utils/StringUtils.cpp", "utils/VectorDrawableUtils.cpp", - "AnimationContext.cpp", "Animator.cpp", - "AnimatorManager.cpp", - "CanvasTransform.cpp", - "DamageAccumulator.cpp", - "DeferredLayerUpdater.cpp", - "DeviceInfo.cpp", - "FrameInfo.cpp", - "FrameInfoVisualizer.cpp", - "GpuMemoryTracker.cpp", - "HardwareBitmapUploader.cpp", - "HWUIProperties.sysprop", "Interpolator.cpp", - "JankTracker.cpp", - "Layer.cpp", - "LayerUpdateQueue.cpp", "Matrix.cpp", "PathParser.cpp", - "ProfileData.cpp", - "ProfileDataContainer.cpp", "Properties.cpp", "PropertyValuesAnimatorSet.cpp", "PropertyValuesHolder.cpp", - "Readback.cpp", - "RecordingCanvas.cpp", - "RenderNode.cpp", - "RenderProperties.cpp", "SkiaCanvas.cpp", - "TreeInfo.cpp", - "WebViewFunctorManager.cpp", "VectorDrawable.cpp", - "protos/graphicsstats.proto", ], - // Allow implicit fallthroughs in HardwareBitmapUploader.cpp until they are fixed. - cflags: ["-Wno-implicit-fallthrough"], - proto: { export_proto_headers: true, }, export_include_dirs: ["."], + + target: { + android: { + + srcs: [ + "pipeline/skia/GLFunctorDrawable.cpp", + "pipeline/skia/LayerDrawable.cpp", + "pipeline/skia/RenderNodeDrawable.cpp", + "pipeline/skia/ReorderBarrierDrawables.cpp", + "pipeline/skia/ShaderCache.cpp", + "pipeline/skia/SkiaDisplayList.cpp", + "pipeline/skia/SkiaMemoryTracer.cpp", + "pipeline/skia/SkiaOpenGLPipeline.cpp", + "pipeline/skia/SkiaPipeline.cpp", + "pipeline/skia/SkiaProfileRenderer.cpp", + "pipeline/skia/SkiaRecordingCanvas.cpp", + "pipeline/skia/SkiaVulkanPipeline.cpp", + "pipeline/skia/VectorDrawableAtlas.cpp", + "pipeline/skia/VkFunctorDrawable.cpp", + "pipeline/skia/VkInteropFunctorDrawable.cpp", + "renderstate/RenderState.cpp", + "renderthread/CacheManager.cpp", + "renderthread/CanvasContext.cpp", + "renderthread/DrawFrameTask.cpp", + "renderthread/EglManager.cpp", + "renderthread/ReliableSurface.cpp", + "renderthread/VulkanManager.cpp", + "renderthread/VulkanSurface.cpp", + "renderthread/RenderProxy.cpp", + "renderthread/RenderTask.cpp", + "renderthread/RenderThread.cpp", + "renderthread/TimeLord.cpp", + "renderthread/Frame.cpp", + "service/GraphicsStatsService.cpp", + "surfacetexture/EGLConsumer.cpp", + "surfacetexture/ImageConsumer.cpp", + "surfacetexture/SurfaceTexture.cpp", + "thread/CommonPool.cpp", + "utils/Color.cpp", + "utils/GLUtils.cpp", + "utils/StringUtils.cpp", + "AnimationContext.cpp", + "AnimatorManager.cpp", + "CanvasTransform.cpp", + "DamageAccumulator.cpp", + "DeferredLayerUpdater.cpp", + "DeviceInfo.cpp", + "FrameInfo.cpp", + "FrameInfoVisualizer.cpp", + "GpuMemoryTracker.cpp", + "HardwareBitmapUploader.cpp", + "HWUIProperties.sysprop", + "JankTracker.cpp", + "Layer.cpp", + "LayerUpdateQueue.cpp", + "ProfileData.cpp", + "ProfileDataContainer.cpp", + "Readback.cpp", + "RecordingCanvas.cpp", + "RenderNode.cpp", + "RenderProperties.cpp", + "TreeInfo.cpp", + "WebViewFunctorManager.cpp", + "protos/graphicsstats.proto", + ], + + // Allow implicit fallthroughs in HardwareBitmapUploader.cpp until they are fixed. + cflags: ["-Wno-implicit-fallthrough"], + }, + host: { + export_static_lib_headers: [ + "libarect", + ], + } + } } cc_library { name: "libhwui", + host_supported: true, defaults: [ "libhwui_defaults", ], @@ -352,59 +394,3 @@ phony { "hwuimacro", ] } - -cc_library_host_shared { - name: "libhwui-host", - - defaults: [ - "skia_deps", - ], - whole_static_libs: ["libskia"], - - srcs: [ - "hwui/AnimatedImageDrawable.cpp", - "hwui/AnimatedImageThread.cpp", - "hwui/Bitmap.cpp", - "hwui/Canvas.cpp", - "hwui/Typeface.cpp", - "hwui/MinikinSkia.cpp", - "hwui/MinikinUtils.cpp", - "hwui/PaintImpl.cpp", - "utils/Blur.cpp", - "utils/LinearAllocator.cpp", - "utils/VectorDrawableUtils.cpp", - "Animator.cpp", - "Interpolator.cpp", - "Matrix.cpp", - "PathParser.cpp", - "Properties.cpp", - "PropertyValuesAnimatorSet.cpp", - "PropertyValuesHolder.cpp", - "SkiaCanvas.cpp", - "VectorDrawable.cpp", - ], - include_dirs: [ - "external/skia/include/private", - "external/skia/src/core", - "external/vulkan-headers/include", - "system/core/base/include", - ], - cpp_std: "experimental", - cflags: [ - "-Wno-unused-parameter", - "-Wno-unused-variable", - ], - shared_libs: [ - "libbase", - "libharfbuzz_ng", - "libminikin", - ], - static_libs: [ - "libandroidfw", - "libutils", - ], - export_include_dirs: ["."], - export_static_lib_headers: [ - "libarect", - ], -} -- cgit v1.2.3 From 31b110576254bf96f46b6ade26d74276981a8b74 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Thu, 13 Jun 2019 13:47:26 -0700 Subject: Suppress compressed rersources.arsc on host Do not print warnings when an APK loaded into aapt2's symbol table has a compressed resources.arsc. This log is helpful at runtime since compressed arscs take longer uncompress. Bug: 130617130 Test: manual Change-Id: I52847663ad8f46ba00d3dd1ebb2292ab54737680 --- libs/androidfw/ApkAssets.cpp | 2 +- libs/androidfw/AssetManager2.cpp | 8 +------- libs/androidfw/include/androidfw/Util.h | 7 +++++++ 3 files changed, 9 insertions(+), 8 deletions(-) (limited to 'libs') diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index 61e32301dc85..85b2d6f531aa 100644 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -134,7 +134,7 @@ std::unique_ptr ApkAssets::LoadImpl( } if (entry.method == kCompressDeflated) { - LOG(WARNING) << kResourcesArsc << " in APK '" << path << "' is compressed."; + ANDROID_LOG(WARNING) << kResourcesArsc << " in APK '" << path << "' is compressed."; } // Open the resource table via mmap unless it is compressed. This logic is taken care of by Open. diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index d20aecaaf0f6..01caf011f644 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -22,10 +22,10 @@ #include #include #include -#include #include "android-base/logging.h" #include "android-base/stringprintf.h" +#include "androidfw/Util.h" #include "utils/ByteOrder.h" #include "utils/Trace.h" @@ -35,12 +35,6 @@ #endif #endif -#ifdef __ANDROID__ -#define ANDROID_LOG(x) LOG(x) -#else -#define ANDROID_LOG(x) std::stringstream() -#endif - #include "androidfw/ResourceUtils.h" namespace android { diff --git a/libs/androidfw/include/androidfw/Util.h b/libs/androidfw/include/androidfw/Util.h index aa1466fde778..9a3646b49db8 100644 --- a/libs/androidfw/include/androidfw/Util.h +++ b/libs/androidfw/include/androidfw/Util.h @@ -19,12 +19,19 @@ #include #include +#include #include #include "android-base/macros.h" #include "androidfw/StringPiece.h" +#ifdef __ANDROID__ +#define ANDROID_LOG(x) LOG(x) +#else +#define ANDROID_LOG(x) std::stringstream() +#endif + namespace android { namespace util { -- cgit v1.2.3 From e218c6928508ef72de2cc5c8b89de0f250a3e68c Mon Sep 17 00:00:00 2001 From: Jerome Gaillard Date: Fri, 14 Jun 2019 12:58:57 +0100 Subject: Replace CLOCK_MONOTONIC with SYSTEM_TIME_MONOTONIC Using SYSTEM_TIME_MONOTONIC works for Android (where it translates to CLOCK_MONOTONIC) and host targets, while CLOCK_MONOTONIC is not defined on macOS. Bug: 117921091 Test: existing tests should pass Change-Id: I1fad472881830fb0701a320cf37319e083932ad4 --- libs/hwui/FrameInfo.h | 8 ++++---- libs/hwui/ProfileData.cpp | 2 +- libs/hwui/hwui/AnimatedImageDrawable.cpp | 10 ++-------- libs/hwui/renderthread/CanvasContext.cpp | 4 ++-- libs/hwui/renderthread/DrawFrameTask.cpp | 2 +- libs/hwui/renderthread/RenderProxy.cpp | 2 +- libs/hwui/renderthread/RenderThread.cpp | 2 +- libs/hwui/renderthread/TimeLord.cpp | 2 +- libs/hwui/tests/macrobench/TestSceneRunner.cpp | 10 +++++----- libs/hwui/thread/WorkQueue.h | 2 +- 10 files changed, 19 insertions(+), 25 deletions(-) (limited to 'libs') diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h index 0aab58c38ba0..b75192ff8476 100644 --- a/libs/hwui/FrameInfo.h +++ b/libs/hwui/FrameInfo.h @@ -100,15 +100,15 @@ class FrameInfo { public: void importUiThreadInfo(int64_t* info); - void markSyncStart() { set(FrameInfoIndex::SyncStart) = systemTime(CLOCK_MONOTONIC); } + void markSyncStart() { set(FrameInfoIndex::SyncStart) = systemTime(SYSTEM_TIME_MONOTONIC); } void markIssueDrawCommandsStart() { - set(FrameInfoIndex::IssueDrawCommandsStart) = systemTime(CLOCK_MONOTONIC); + set(FrameInfoIndex::IssueDrawCommandsStart) = systemTime(SYSTEM_TIME_MONOTONIC); } - void markSwapBuffers() { set(FrameInfoIndex::SwapBuffers) = systemTime(CLOCK_MONOTONIC); } + void markSwapBuffers() { set(FrameInfoIndex::SwapBuffers) = systemTime(SYSTEM_TIME_MONOTONIC); } - void markFrameCompleted() { set(FrameInfoIndex::FrameCompleted) = systemTime(CLOCK_MONOTONIC); } + void markFrameCompleted() { set(FrameInfoIndex::FrameCompleted) = systemTime(SYSTEM_TIME_MONOTONIC); } void addFlag(int frameInfoFlag) { set(FrameInfoIndex::Flags) |= static_cast(frameInfoFlag); diff --git a/libs/hwui/ProfileData.cpp b/libs/hwui/ProfileData.cpp index 70ca4e3e8074..c7f92321b090 100644 --- a/libs/hwui/ProfileData.cpp +++ b/libs/hwui/ProfileData.cpp @@ -143,7 +143,7 @@ void ProfileData::reset() { mSlowFrameCounts.fill(0); mTotalFrameCount = 0; mJankFrameCount = 0; - mStatStartTime = systemTime(CLOCK_MONOTONIC); + mStatStartTime = systemTime(SYSTEM_TIME_MONOTONIC); } void ProfileData::reportFrame(int64_t duration) { diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp index 7677f9c6628d..4544beae5df8 100644 --- a/libs/hwui/hwui/AnimatedImageDrawable.cpp +++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp @@ -24,12 +24,6 @@ #include -#ifdef __APPLE__ - // macOS SDK 10.10 does not support CLOCK_MONOTONIC, which is not an issue since - // the value of the argument is not used in the host definition of systemTime -#define CLOCK_MONOTONIC -#endif - namespace android { AnimatedImageDrawable::AnimatedImageDrawable(sk_sp animatedImage, size_t bytesUsed) @@ -70,7 +64,7 @@ bool AnimatedImageDrawable::nextSnapshotReady() const { // Only called on the RenderThread while UI thread is locked. bool AnimatedImageDrawable::isDirty(nsecs_t* outDelay) { *outDelay = 0; - const nsecs_t currentTime = systemTime(CLOCK_MONOTONIC); + const nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); const nsecs_t lastWallTime = mLastWallTime; mLastWallTime = currentTime; @@ -250,7 +244,7 @@ int AnimatedImageDrawable::drawStaging(SkCanvas* canvas) { bool update = false; { - const nsecs_t currentTime = systemTime(CLOCK_MONOTONIC); + const nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); std::unique_lock lock{mSwapLock}; // mLastWallTime starts off at 0. If it is still 0, just update it to // the current time and avoid updating diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index f326ce8d23e9..c9203d9bb530 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -463,7 +463,7 @@ void CanvasContext::draw() { } SwapHistory& swap = mSwapHistory.next(); swap.damage = windowDirty; - swap.swapCompletedTime = systemTime(CLOCK_MONOTONIC); + swap.swapCompletedTime = systemTime(SYSTEM_TIME_MONOTONIC); swap.vsyncTime = mRenderThread.timeLord().latestVsync(); if (mNativeSurface.get()) { int durationUs; @@ -549,7 +549,7 @@ void CanvasContext::prepareAndDraw(RenderNode* node) { UiFrameInfoBuilder(frameInfo).addFlag(FrameInfoFlags::RTAnimation).setVsync(vsync, vsync); TreeInfo info(TreeInfo::MODE_RT_ONLY, *this); - prepareTree(info, frameInfo, systemTime(CLOCK_MONOTONIC), node); + prepareTree(info, frameInfo, systemTime(SYSTEM_TIME_MONOTONIC), node); if (info.out.canDrawThisFrame) { draw(); } else { diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp index 91dc3bc6e603..1e593388d063 100644 --- a/libs/hwui/renderthread/DrawFrameTask.cpp +++ b/libs/hwui/renderthread/DrawFrameTask.cpp @@ -69,7 +69,7 @@ int DrawFrameTask::drawFrame() { LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!"); mSyncResult = SyncResult::OK; - mSyncQueued = systemTime(CLOCK_MONOTONIC); + mSyncQueued = systemTime(SYSTEM_TIME_MONOTONIC); postAndWait(); return mSyncResult; diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 1a1b9dac37f6..20247dc7c861 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -339,7 +339,7 @@ void RenderProxy::prepareToDraw(Bitmap& bitmap) { }; nsecs_t lastVsync = renderThread->timeLord().latestVsync(); nsecs_t estimatedNextVsync = lastVsync + renderThread->timeLord().frameIntervalNanos(); - nsecs_t timeToNextVsync = estimatedNextVsync - systemTime(CLOCK_MONOTONIC); + nsecs_t timeToNextVsync = estimatedNextVsync - systemTime(SYSTEM_TIME_MONOTONIC); // We expect the UI thread to take 4ms and for RT to be active from VSYNC+4ms to // VSYNC+12ms or so, so aim for the gap during which RT is expected to // be idle diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 863814263610..ee1a7ce19e82 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -103,7 +103,7 @@ public: [this]() { mRenderThread->drainDisplayEventQueue(); }); } - virtual nsecs_t latestVsyncEvent() override { return systemTime(CLOCK_MONOTONIC); } + virtual nsecs_t latestVsyncEvent() override { return systemTime(SYSTEM_TIME_MONOTONIC); } private: RenderThread* mRenderThread; diff --git a/libs/hwui/renderthread/TimeLord.cpp b/libs/hwui/renderthread/TimeLord.cpp index b82c5d159756..784068f1d877 100644 --- a/libs/hwui/renderthread/TimeLord.cpp +++ b/libs/hwui/renderthread/TimeLord.cpp @@ -31,7 +31,7 @@ bool TimeLord::vsyncReceived(nsecs_t vsync) { nsecs_t TimeLord::computeFrameTimeNanos() { // Logic copied from Choreographer.java - nsecs_t now = systemTime(CLOCK_MONOTONIC); + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); nsecs_t jitterNanos = now - mFrameTimeNanos; if (jitterNanos >= mFrameIntervalNanos) { nsecs_t lastFrameOffset = jitterNanos % mFrameIntervalNanos; diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp index 9c845f04e820..22d5abbd3dbc 100644 --- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp +++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp @@ -146,7 +146,7 @@ void run(const TestScene::Info& info, const TestScene::Options& opts, } for (int i = 0; i < warmupFrameCount; i++) { testContext.waitForVsync(); - nsecs_t vsync = systemTime(CLOCK_MONOTONIC); + nsecs_t vsync = systemTime(SYSTEM_TIME_MONOTONIC); UiFrameInfoBuilder(proxy->frameInfo()).setVsync(vsync, vsync); proxy->syncAndDrawFrame(); } @@ -161,10 +161,10 @@ void run(const TestScene::Info& info, const TestScene::Options& opts, ModifiedMovingAverage avgMs(opts.reportFrametimeWeight); - nsecs_t start = systemTime(CLOCK_MONOTONIC); + nsecs_t start = systemTime(SYSTEM_TIME_MONOTONIC); for (int i = 0; i < opts.count; i++) { testContext.waitForVsync(); - nsecs_t vsync = systemTime(CLOCK_MONOTONIC); + nsecs_t vsync = systemTime(SYSTEM_TIME_MONOTONIC); { ATRACE_NAME("UI-Draw Frame"); UiFrameInfoBuilder(proxy->frameInfo()).setVsync(vsync, vsync); @@ -173,7 +173,7 @@ void run(const TestScene::Info& info, const TestScene::Options& opts, } if (opts.reportFrametimeWeight) { proxy->fence(); - nsecs_t done = systemTime(CLOCK_MONOTONIC); + nsecs_t done = systemTime(SYSTEM_TIME_MONOTONIC); avgMs.add((done - vsync) / 1000000.0); if (i % 10 == 9) { printf("Average frametime %.3fms\n", avgMs.average()); @@ -181,7 +181,7 @@ void run(const TestScene::Info& info, const TestScene::Options& opts, } } proxy->fence(); - nsecs_t end = systemTime(CLOCK_MONOTONIC); + nsecs_t end = systemTime(SYSTEM_TIME_MONOTONIC); if (reporter) { outputBenchmarkReport(info, opts, reporter, proxy.get(), (end - start) / (double)s2ns(1)); diff --git a/libs/hwui/thread/WorkQueue.h b/libs/hwui/thread/WorkQueue.h index 42f8503fd000..46b8bc07b432 100644 --- a/libs/hwui/thread/WorkQueue.h +++ b/libs/hwui/thread/WorkQueue.h @@ -31,7 +31,7 @@ namespace android::uirenderer { struct MonotonicClock { - static nsecs_t now() { return systemTime(CLOCK_MONOTONIC); } + static nsecs_t now() { return systemTime(SYSTEM_TIME_MONOTONIC); } }; class WorkQueue { -- cgit v1.2.3 From 90df056f4d3d841b2af3e338bad5634cfbd4ec5c Mon Sep 17 00:00:00 2001 From: Fedor Kudasov Date: Wed, 19 Jun 2019 11:41:34 +0100 Subject: Split lighting out of SkiaPipeline ReorderBarierDrawables includes SkiaPipeline just for the sake of lighting, however SkiaPipeline brings much more unnecessary dependencies. Splitting lighting our of SkiaPipeline should make dependency resolution simpler. Bug: 117921091 Test: all tests should pass Change-Id: I6adf7c43555cfa3ff7090a1197fc11160d3a85ec --- libs/hwui/Android.bp | 1 + libs/hwui/LightingInfo.cpp | 31 ++++++++ libs/hwui/LightingInfo.h | 86 ++++++++++++++++++++++ .../hwui/pipeline/skia/ReorderBarrierDrawables.cpp | 10 +-- libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp | 3 +- libs/hwui/pipeline/skia/SkiaPipeline.cpp | 19 +++-- libs/hwui/pipeline/skia/SkiaPipeline.h | 48 ------------ libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp | 3 +- 8 files changed, 136 insertions(+), 65 deletions(-) create mode 100644 libs/hwui/LightingInfo.cpp create mode 100644 libs/hwui/LightingInfo.h (limited to 'libs') diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 82bcf9e9afa4..354a4b323f41 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -241,6 +241,7 @@ cc_defaults { "JankTracker.cpp", "Layer.cpp", "LayerUpdateQueue.cpp", + "LightingInfo.cpp", "ProfileData.cpp", "ProfileDataContainer.cpp", "Readback.cpp", diff --git a/libs/hwui/LightingInfo.cpp b/libs/hwui/LightingInfo.cpp new file mode 100644 index 000000000000..83bb255f3c3b --- /dev/null +++ b/libs/hwui/LightingInfo.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2019 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 "LightingInfo.h" + +#include + +namespace android { +namespace uirenderer { + +float LightingInfo::mLightRadius = 0; +uint8_t LightingInfo::mAmbientShadowAlpha = 0; +uint8_t LightingInfo::mSpotShadowAlpha = 0; + +Vector3 LightingInfo::mLightCenter = {FLT_MIN, FLT_MIN, FLT_MIN}; + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/LightingInfo.h b/libs/hwui/LightingInfo.h new file mode 100644 index 000000000000..3112eb3e0d73 --- /dev/null +++ b/libs/hwui/LightingInfo.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2019 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. + */ + +#pragma once + +#include "Lighting.h" +#include "Properties.h" + +namespace android { +namespace uirenderer { + +class LightingInfo { +public: + + static float getLightRadius() { + if (CC_UNLIKELY(Properties::overrideLightRadius > 0)) { + return Properties::overrideLightRadius; + } + return mLightRadius; + } + + static uint8_t getAmbientShadowAlpha() { + if (CC_UNLIKELY(Properties::overrideAmbientShadowStrength >= 0)) { + return Properties::overrideAmbientShadowStrength; + } + return mAmbientShadowAlpha; + } + + static uint8_t getSpotShadowAlpha() { + if (CC_UNLIKELY(Properties::overrideSpotShadowStrength >= 0)) { + return Properties::overrideSpotShadowStrength; + } + return mSpotShadowAlpha; + } + + static Vector3 getLightCenter() { + if (CC_UNLIKELY(Properties::overrideLightPosY > 0 || Properties::overrideLightPosZ > 0)) { + Vector3 adjustedLightCenter = mLightCenter; + if (CC_UNLIKELY(Properties::overrideLightPosY > 0)) { + // negated since this shifts up + adjustedLightCenter.y = -Properties::overrideLightPosY; + } + if (CC_UNLIKELY(Properties::overrideLightPosZ > 0)) { + adjustedLightCenter.z = Properties::overrideLightPosZ; + } + return adjustedLightCenter; + } + return mLightCenter; + } + + static Vector3 getLightCenterRaw() { + return mLightCenter; + } + + static void setLightCenterRaw(const Vector3& lightCenter) { + mLightCenter = lightCenter; + } + + static void updateLighting(const LightGeometry& lightGeometry, const LightInfo& lightInfo) { + mLightRadius = lightGeometry.radius; + mAmbientShadowAlpha = lightInfo.ambientShadowAlpha; + mSpotShadowAlpha = lightInfo.spotShadowAlpha; + mLightCenter = lightGeometry.center; + } +private: + static float mLightRadius; + static uint8_t mAmbientShadowAlpha; + static uint8_t mSpotShadowAlpha; + static Vector3 mLightCenter; +}; + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp index 0a3c8f4347eb..5133baebae18 100644 --- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp +++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp @@ -17,7 +17,7 @@ #include "ReorderBarrierDrawables.h" #include "RenderNode.h" #include "SkiaDisplayList.h" -#include "SkiaPipeline.h" +#include "LightingInfo.h" #include #include @@ -139,8 +139,8 @@ void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable* return; } - float ambientAlpha = (SkiaPipeline::getAmbientShadowAlpha() / 255.f) * casterAlpha; - float spotAlpha = (SkiaPipeline::getSpotShadowAlpha() / 255.f) * casterAlpha; + float ambientAlpha = (LightingInfo::getAmbientShadowAlpha() / 255.f) * casterAlpha; + float spotAlpha = (LightingInfo::getSpotShadowAlpha() / 255.f) * casterAlpha; const RevealClip& revealClip = casterProperties.getRevealClip(); const SkPath* revealClipPath = revealClip.getPath(); @@ -192,7 +192,7 @@ void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable* casterPath = &tmpPath; } - const Vector3 lightPos = SkiaPipeline::getLightCenter(); + const Vector3 lightPos = LightingInfo::getLightCenter(); SkPoint3 skiaLightPos = SkPoint3::Make(lightPos.x, lightPos.y, lightPos.z); SkPoint3 zParams; if (shadowMatrix.hasPerspective()) { @@ -206,7 +206,7 @@ void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable* SkColor ambientColor = multiplyAlpha(casterProperties.getAmbientShadowColor(), ambientAlpha); SkColor spotColor = multiplyAlpha(casterProperties.getSpotShadowColor(), spotAlpha); SkShadowUtils::DrawShadow( - canvas, *casterPath, zParams, skiaLightPos, SkiaPipeline::getLightRadius(), + canvas, *casterPath, zParams, skiaLightPos, LightingInfo::getLightRadius(), ambientColor, spotColor, casterAlpha < 1.0f ? SkShadowFlags::kTransparentOccluder_ShadowFlag : 0); } diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index 8092b1d10659..e7efe2ff798b 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -18,6 +18,7 @@ #include "DeferredLayerUpdater.h" #include "LayerDrawable.h" +#include "LightingInfo.h" #include "SkiaPipeline.h" #include "SkiaProfileRenderer.h" #include "hwui/Bitmap.h" @@ -99,7 +100,7 @@ bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, con mRenderThread.getGrContext(), backendRT, this->getSurfaceOrigin(), colorType, mSurfaceColorSpace, &props)); - SkiaPipeline::updateLighting(lightGeometry, lightInfo); + LightingInfo::updateLighting(lightGeometry, lightInfo); renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface, SkMatrix::I()); layerUpdateQueue->clear(); diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 066828190627..ff29a5b9e352 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -23,6 +23,7 @@ #include #include #include +#include "LightingInfo.h" #include "TreeInfo.h" #include "VectorDrawable.h" #include "thread/CommonPool.h" @@ -38,12 +39,6 @@ namespace android { namespace uirenderer { namespace skiapipeline { -float SkiaPipeline::mLightRadius = 0; -uint8_t SkiaPipeline::mAmbientShadowAlpha = 0; -uint8_t SkiaPipeline::mSpotShadowAlpha = 0; - -Vector3 SkiaPipeline::mLightCenter = {FLT_MIN, FLT_MIN, FLT_MIN}; - SkiaPipeline::SkiaPipeline(RenderThread& thread) : mRenderThread(thread) { mVectorDrawables.reserve(30); } @@ -84,7 +79,7 @@ void SkiaPipeline::onPrepareTree() { void SkiaPipeline::renderLayers(const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, bool opaque, const LightInfo& lightInfo) { - updateLighting(lightGeometry, lightInfo); + LightingInfo::updateLighting(lightGeometry, lightInfo); ATRACE_NAME("draw layers"); renderVectorDrawableCache(); renderLayersImpl(*layerUpdateQueue, opaque); @@ -117,9 +112,13 @@ void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque) layerCanvas->androidFramework_setDeviceClipRestriction(layerDamage.toSkIRect()); - auto savedLightCenter = mLightCenter; + // TODO: put localized light center calculation and storage to a drawable related code. + // It does not seem right to store something localized in a global state + const Vector3 savedLightCenter(LightingInfo::getLightCenterRaw()); + Vector3 transformedLightCenter(savedLightCenter); // map current light center into RenderNode's coordinate space - layerNode->getSkiaLayer()->inverseTransformInWindow.mapPoint3d(mLightCenter); + layerNode->getSkiaLayer()->inverseTransformInWindow.mapPoint3d(transformedLightCenter); + LightingInfo::setLightCenterRaw(transformedLightCenter); const RenderProperties& properties = layerNode->properties(); const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight()); @@ -136,7 +135,7 @@ void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque) RenderNodeDrawable root(layerNode, layerCanvas, false); root.forceDraw(layerCanvas); layerCanvas->restoreToCount(saveCount); - mLightCenter = savedLightCenter; + LightingInfo::setLightCenterRaw(savedLightCenter); // cache the current context so that we can defer flushing it until // either all the layers have been rendered or the context changes diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h index 41d864653b67..5fc1d6169d4a 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaPipeline.h @@ -60,49 +60,6 @@ public: void renderLayersImpl(const LayerUpdateQueue& layers, bool opaque); - static float getLightRadius() { - if (CC_UNLIKELY(Properties::overrideLightRadius > 0)) { - return Properties::overrideLightRadius; - } - return mLightRadius; - } - - static uint8_t getAmbientShadowAlpha() { - if (CC_UNLIKELY(Properties::overrideAmbientShadowStrength >= 0)) { - return Properties::overrideAmbientShadowStrength; - } - return mAmbientShadowAlpha; - } - - static uint8_t getSpotShadowAlpha() { - if (CC_UNLIKELY(Properties::overrideSpotShadowStrength >= 0)) { - return Properties::overrideSpotShadowStrength; - } - return mSpotShadowAlpha; - } - - static Vector3 getLightCenter() { - if (CC_UNLIKELY(Properties::overrideLightPosY > 0 || Properties::overrideLightPosZ > 0)) { - Vector3 adjustedLightCenter = mLightCenter; - if (CC_UNLIKELY(Properties::overrideLightPosY > 0)) { - // negated since this shifts up - adjustedLightCenter.y = -Properties::overrideLightPosY; - } - if (CC_UNLIKELY(Properties::overrideLightPosZ > 0)) { - adjustedLightCenter.z = Properties::overrideLightPosZ; - } - return adjustedLightCenter; - } - return mLightCenter; - } - - static void updateLighting(const LightGeometry& lightGeometry, const LightInfo& lightInfo) { - mLightRadius = lightGeometry.radius; - mAmbientShadowAlpha = lightInfo.ambientShadowAlpha; - mSpotShadowAlpha = lightInfo.spotShadowAlpha; - mLightCenter = lightGeometry.center; - } - void setPictureCapturedCallback( const std::function&&)>& callback) override { mPictureCapturedCallback = callback; @@ -163,11 +120,6 @@ private: std::unique_ptr mRecorder; std::unique_ptr mNwayCanvas; std::function&&)> mPictureCapturedCallback; - - static float mLightRadius; - static uint8_t mAmbientShadowAlpha; - static uint8_t mSpotShadowAlpha; - static Vector3 mLightCenter; }; } /* namespace skiapipeline */ diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index e8cb219db320..ad7c70614239 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -19,6 +19,7 @@ #include "DeferredLayerUpdater.h" #include "Readback.h" #include "ShaderCache.h" +#include "LightingInfo.h" #include "SkiaPipeline.h" #include "SkiaProfileRenderer.h" #include "VkInteropFunctorDrawable.h" @@ -69,7 +70,7 @@ bool SkiaVulkanPipeline::draw(const Frame& frame, const SkRect& screenDirty, con if (backBuffer.get() == nullptr) { return false; } - SkiaPipeline::updateLighting(lightGeometry, lightInfo); + LightingInfo::updateLighting(lightGeometry, lightInfo); renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, backBuffer, mVkSurface->getCurrentPreTransform()); ShaderCache::get().onVkFrameFlushed(mRenderThread.getGrContext()); -- cgit v1.2.3 From d501e10b0bcdad6591c39298f2735d190f257727 Mon Sep 17 00:00:00 2001 From: Fedor Kudasov Date: Fri, 21 Jun 2019 10:22:53 +0100 Subject: Move GL specific code away from header Bug: 117921091 Test: all tests should pass Change-Id: I124a17ac6a378029fc6836ad234bf45095675aae --- libs/hwui/DeferredLayerUpdater.cpp | 13 +++++++++++++ libs/hwui/DeferredLayerUpdater.h | 13 +------------ 2 files changed, 14 insertions(+), 12 deletions(-) (limited to 'libs') diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp index 3bee3018d36e..f300703e7e00 100644 --- a/libs/hwui/DeferredLayerUpdater.cpp +++ b/libs/hwui/DeferredLayerUpdater.cpp @@ -15,6 +15,9 @@ */ #include "DeferredLayerUpdater.h" +#include +#include + #include "renderstate/RenderState.h" #include "utils/PaintUtils.h" @@ -38,6 +41,16 @@ DeferredLayerUpdater::~DeferredLayerUpdater() { destroyLayer(); } +void DeferredLayerUpdater::setSurfaceTexture(const sp& consumer) { + if (consumer.get() != mSurfaceTexture.get()) { + mSurfaceTexture = consumer; + + GLenum target = consumer->getCurrentTextureTarget(); + LOG_ALWAYS_FATAL_IF(target != GL_TEXTURE_2D && target != GL_TEXTURE_EXTERNAL_OES, + "set unsupported SurfaceTexture with target %x", target); + } +} + void DeferredLayerUpdater::onContextDestroyed() { destroyLayer(); } diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h index a91c111933c4..1491f99402ba 100644 --- a/libs/hwui/DeferredLayerUpdater.h +++ b/libs/hwui/DeferredLayerUpdater.h @@ -24,9 +24,6 @@ #include #include -#include -#include - #include "renderstate/RenderState.h" #include "surfacetexture/SurfaceTexture.h" #include "Layer.h" @@ -67,15 +64,7 @@ public: return false; } - ANDROID_API void setSurfaceTexture(const sp& consumer) { - if (consumer.get() != mSurfaceTexture.get()) { - mSurfaceTexture = consumer; - - GLenum target = consumer->getCurrentTextureTarget(); - LOG_ALWAYS_FATAL_IF(target != GL_TEXTURE_2D && target != GL_TEXTURE_EXTERNAL_OES, - "set unsupported SurfaceTexture with target %x", target); - } - } + ANDROID_API void setSurfaceTexture(const sp& consumer); ANDROID_API void updateTexImage() { mUpdateTexImage = true; } -- cgit v1.2.3 From fe2dcf1877b0666e0395619a7de0353babbb1aee Mon Sep 17 00:00:00 2001 From: Fedor Kudasov Date: Fri, 21 Jun 2019 11:01:41 +0100 Subject: Remove redundant include from RenderNodeDrawable Bug: 117921091 Test: all tests should pass Change-Id: Iaac1a9fe2f04dedb5f04d7ae6288e69332824b00 --- libs/hwui/pipeline/skia/RenderNodeDrawable.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'libs') diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp index e65fe3a3b811..9c8463417e0a 100644 --- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp +++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp @@ -18,7 +18,6 @@ #include #include "RenderNode.h" #include "SkiaDisplayList.h" -#include "SkiaPipeline.h" #include "utils/TraceUtils.h" #include -- cgit v1.2.3 From c53174301ca771f8a198bb818cda3874e1f72135 Mon Sep 17 00:00:00 2001 From: Fedor Kudasov Date: Mon, 24 Jun 2019 09:07:03 +0100 Subject: Fix potential off-by-one Being consistent with the notion of mEndChildIndex, it should be -1 by default. Otherwise it assumes the display list has a single element. This is consistent with EndReorderBarrierDrawable constructor that would set mEndChildIndex to -1 if display list is empty. Bug: 117921091 Test: all tests should pass Change-Id: I64b84c789ad8126ac8f0b8c2ebfc128635e5b30d --- libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp index 5133baebae18..3b8caeb3aab1 100644 --- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp +++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp @@ -27,7 +27,7 @@ namespace uirenderer { namespace skiapipeline { StartReorderBarrierDrawable::StartReorderBarrierDrawable(SkiaDisplayList* data) - : mEndChildIndex(0), mBeginChildIndex(data->mChildNodes.size()), mDisplayList(data) {} + : mEndChildIndex(-1), mBeginChildIndex(data->mChildNodes.size()), mDisplayList(data) {} void StartReorderBarrierDrawable::onDraw(SkCanvas* canvas) { if (mChildren.empty()) { -- cgit v1.2.3 From b7c2eb98fbc75e616abc8c101ab1dcebdc8e2091 Mon Sep 17 00:00:00 2001 From: Fedor Kudasov Date: Mon, 24 Jun 2019 15:24:57 +0100 Subject: Remove redundant header Bug: 117921091 Test: all tests should pass Change-Id: I6a3ddd9b5f24bc3ed4d7862493c0c72e79b4410d --- libs/hwui/RenderNode.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'libs') diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index b73347b233d7..a833f7ef98c9 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -20,7 +20,6 @@ #include "Debug.h" #include "TreeInfo.h" #include "VectorDrawable.h" -#include "renderstate/RenderState.h" #include "renderthread/CanvasContext.h" #include "utils/FatVector.h" #include "utils/MathUtils.h" -- cgit v1.2.3 From 52abc909dadf632df1076781c6c14c250e761b05 Mon Sep 17 00:00:00 2001 From: Fedor Kudasov Date: Wed, 26 Jun 2019 16:59:16 +0100 Subject: Remove EGL references from CanvasContext header Bug: 117921091 Test: all tests should pass Change-Id: Ib8ff068c8dcbc4f9855cc91c4af1d704d66dde7b --- libs/hwui/renderthread/CanvasContext.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'libs') diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 982c087b031a..0910828da4f8 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -29,7 +29,6 @@ #include "renderthread/RenderTask.h" #include "renderthread/RenderThread.h" -#include #include #include #include @@ -55,7 +54,6 @@ class RenderState; namespace renderthread { -class EglManager; class Frame; // This per-renderer class manages the bridge between the global EGL context @@ -216,8 +214,9 @@ private: SkRect computeDirtyRect(const Frame& frame, SkRect* dirty); - EGLint mLastFrameWidth = 0; - EGLint mLastFrameHeight = 0; + // The same type as Frame.mWidth and Frame.mHeight + int32_t mLastFrameWidth = 0; + int32_t mLastFrameHeight = 0; RenderThread& mRenderThread; sp mNativeSurface; -- cgit v1.2.3 From 86bd214059cd6150304888a285941bf74af5b687 Mon Sep 17 00:00:00 2001 From: Fedor Kudasov Date: Tue, 18 Jun 2019 15:51:57 +0100 Subject: Enable RenderNode and RecordingCanvas for layoutlib Bug: 117921091 Test: all tests should pass Change-Id: I574b12a5f7a6a54cbbcb17c35a3884368fd404e6 --- libs/hwui/Android.bp | 31 +- libs/hwui/RenderNode.cpp | 7 + libs/hwui/RenderProperties.h | 4 + libs/hwui/hwui/Canvas.cpp | 4 - libs/hwui/pipeline/skia/SkiaDisplayList.cpp | 10 + libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp | 12 + libs/hwui/utils/HostColorSpace.cpp | 417 ++++++++++++++++++++++++ 7 files changed, 468 insertions(+), 17 deletions(-) create mode 100644 libs/hwui/utils/HostColorSpace.cpp (limited to 'libs') diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 354a4b323f41..5814bb70475c 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -56,6 +56,8 @@ cc_defaults { host: { include_dirs: [ "external/vulkan-headers/include", + "frameworks/native/libs/math/include", + "frameworks/native/libs/ui/include", ], cflags: [ "-Wno-unused-variable", @@ -159,6 +161,10 @@ cc_defaults { whole_static_libs: ["libskia"], srcs: [ + "pipeline/skia/SkiaDisplayList.cpp", + "pipeline/skia/SkiaRecordingCanvas.cpp", + "pipeline/skia/RenderNodeDrawable.cpp", + "pipeline/skia/ReorderBarrierDrawables.cpp", "hwui/AnimatedImageDrawable.cpp", "hwui/AnimatedImageThread.cpp", "hwui/Bitmap.cpp", @@ -168,15 +174,24 @@ cc_defaults { "hwui/PaintImpl.cpp", "hwui/Typeface.cpp", "utils/Blur.cpp", + "utils/Color.cpp", "utils/LinearAllocator.cpp", "utils/VectorDrawableUtils.cpp", + "AnimationContext.cpp", "Animator.cpp", + "AnimatorManager.cpp", + "CanvasTransform.cpp", + "DamageAccumulator.cpp", "Interpolator.cpp", + "LightingInfo.cpp", "Matrix.cpp", "PathParser.cpp", "Properties.cpp", "PropertyValuesAnimatorSet.cpp", "PropertyValuesHolder.cpp", + "RecordingCanvas.cpp", + "RenderNode.cpp", + "RenderProperties.cpp", "SkiaCanvas.cpp", "VectorDrawable.cpp", ], @@ -193,15 +208,11 @@ cc_defaults { srcs: [ "pipeline/skia/GLFunctorDrawable.cpp", "pipeline/skia/LayerDrawable.cpp", - "pipeline/skia/RenderNodeDrawable.cpp", - "pipeline/skia/ReorderBarrierDrawables.cpp", "pipeline/skia/ShaderCache.cpp", - "pipeline/skia/SkiaDisplayList.cpp", "pipeline/skia/SkiaMemoryTracer.cpp", "pipeline/skia/SkiaOpenGLPipeline.cpp", "pipeline/skia/SkiaPipeline.cpp", "pipeline/skia/SkiaProfileRenderer.cpp", - "pipeline/skia/SkiaRecordingCanvas.cpp", "pipeline/skia/SkiaVulkanPipeline.cpp", "pipeline/skia/VectorDrawableAtlas.cpp", "pipeline/skia/VkFunctorDrawable.cpp", @@ -224,13 +235,8 @@ cc_defaults { "surfacetexture/ImageConsumer.cpp", "surfacetexture/SurfaceTexture.cpp", "thread/CommonPool.cpp", - "utils/Color.cpp", "utils/GLUtils.cpp", "utils/StringUtils.cpp", - "AnimationContext.cpp", - "AnimatorManager.cpp", - "CanvasTransform.cpp", - "DamageAccumulator.cpp", "DeferredLayerUpdater.cpp", "DeviceInfo.cpp", "FrameInfo.cpp", @@ -241,13 +247,9 @@ cc_defaults { "JankTracker.cpp", "Layer.cpp", "LayerUpdateQueue.cpp", - "LightingInfo.cpp", "ProfileData.cpp", "ProfileDataContainer.cpp", "Readback.cpp", - "RecordingCanvas.cpp", - "RenderNode.cpp", - "RenderProperties.cpp", "TreeInfo.cpp", "WebViewFunctorManager.cpp", "protos/graphicsstats.proto", @@ -257,6 +259,9 @@ cc_defaults { cflags: ["-Wno-implicit-fallthrough"], }, host: { + srcs: [ + "utils/HostColorSpace.cpp", + ], export_static_lib_headers: [ "libarect", ], diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index a833f7ef98c9..8aee8f5b1848 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -20,7 +20,12 @@ #include "Debug.h" #include "TreeInfo.h" #include "VectorDrawable.h" +#ifdef __ANDROID__ #include "renderthread/CanvasContext.h" +#else +#include "DamageAccumulator.h" +#include "pipeline/skia/SkiaDisplayList.h" +#endif #include "utils/FatVector.h" #include "utils/MathUtils.h" #include "utils/StringUtils.h" @@ -160,6 +165,7 @@ void RenderNode::prepareLayer(TreeInfo& info, uint32_t dirtyMask) { } void RenderNode::pushLayerUpdate(TreeInfo& info) { +#ifdef __ANDROID__ // Layoutlib does not support CanvasContext and Layers LayerType layerType = properties().effectiveLayerType(); // 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 @@ -188,6 +194,7 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) { // That might be us, so tell CanvasContext that this layer is in the // tree and should not be destroyed. info.canvasContext.markLayerInUse(this); +#endif } /** diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h index e6710cc8f950..e9794489f171 100644 --- a/libs/hwui/RenderProperties.h +++ b/libs/hwui/RenderProperties.h @@ -526,9 +526,13 @@ public: } bool fitsOnLayer() const { +#ifdef __ANDROID__ // Layoutlib does not support device info const DeviceInfo* deviceInfo = DeviceInfo::get(); return mPrimitiveFields.mWidth <= deviceInfo->maxTextureSize() && mPrimitiveFields.mHeight <= deviceInfo->maxTextureSize(); +#else + return mPrimitiveFields.mWidth <= 4096 && mPrimitiveFields.mHeight <= 4096; +#endif } bool promotedToLayer() const { diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp index e2ddb91f1565..a48c86028262 100644 --- a/libs/hwui/hwui/Canvas.cpp +++ b/libs/hwui/hwui/Canvas.cpp @@ -30,11 +30,7 @@ namespace android { Canvas* Canvas::create_recording_canvas(int width, int height, uirenderer::RenderNode* renderNode) { -#ifdef __ANDROID__ // Layoutlib does not support recording canvas return new uirenderer::skiapipeline::SkiaRecordingCanvas(renderNode, width, height); -#else - return NULL; -#endif } static inline void drawStroke(SkScalar left, SkScalar right, SkScalar top, SkScalar thickness, diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp index 780ac20c65aa..c7d5f3193f45 100644 --- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp +++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp @@ -17,9 +17,15 @@ #include "SkiaDisplayList.h" #include "DumpOpsCanvas.h" +#ifdef __ANDROID__ // Layoutlib does not support SkiaPipeline #include "SkiaPipeline.h" +#else +#include "DamageAccumulator.h" +#endif #include "VectorDrawable.h" +#ifdef __ANDROID__ #include "renderthread/CanvasContext.h" +#endif #include #include @@ -81,6 +87,7 @@ bool SkiaDisplayList::prepareListAndChildren( // If the prepare tree is triggered by the UI thread and no previous call to // pinImages has failed then we must pin all mutable images in the GPU cache // until the next UI thread draw. +#ifdef __ANDROID__ // Layoutlib does not support CanvasContext if (info.prepareTextures && !info.canvasContext.pinImages(mMutableImages)) { // In the event that pinning failed we prevent future pinImage calls for the // remainder of this tree traversal and also unpin any currently pinned images @@ -88,6 +95,7 @@ bool SkiaDisplayList::prepareListAndChildren( info.prepareTextures = false; info.canvasContext.unpinImages(); } +#endif bool hasBackwardProjectedNodesHere = false; bool hasBackwardProjectedNodesSubtree = false; @@ -141,10 +149,12 @@ bool SkiaDisplayList::prepareListAndChildren( const SkRect& bounds = vectorDrawable->properties().getBounds(); if (intersects(info.screenSize, totalMatrix, bounds)) { isDirty = true; +#ifdef __ANDROID__ // Layoutlib does not support CanvasContext static_cast(info.canvasContext.getRenderPipeline()) ->getVectorDrawables() ->push_back(vectorDrawable); vectorDrawable->setPropertyChangeWillBeConsumed(true); +#endif } } } diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp index 16c8b8923074..38ef131bedef 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -18,14 +18,18 @@ #include #include "CanvasTransform.h" +#ifdef __ANDROID__ // Layoutlib does not support Layers #include "Layer.h" #include "LayerDrawable.h" +#endif #include "NinePatchUtils.h" #include "RenderNode.h" #include "pipeline/skia/AnimatedDrawables.h" +#ifdef __ANDROID__ // Layoutlib does not support GL, Vulcan etc. #include "pipeline/skia/GLFunctorDrawable.h" #include "pipeline/skia/VkFunctorDrawable.h" #include "pipeline/skia/VkInteropFunctorDrawable.h" +#endif namespace android { namespace uirenderer { @@ -102,13 +106,16 @@ void SkiaRecordingCanvas::insertReorderBarrier(bool enableReorder) { } void SkiaRecordingCanvas::drawLayer(uirenderer::DeferredLayerUpdater* layerUpdater) { +#ifdef __ANDROID__ // Layoutlib does not support Layers if (layerUpdater != nullptr) { // Create a ref-counted drawable, which is kept alive by sk_sp in SkLiteDL. sk_sp drawable(new LayerDrawable(layerUpdater)); drawDrawable(drawable.get()); } +#endif } + void SkiaRecordingCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) { // Record the child node. Drawable dtor will be invoked when mChildNodes deque is cleared. mDisplayList->mChildNodes.emplace_back(renderNode, asSkCanvas(), true, mCurrentBarrier); @@ -125,8 +132,10 @@ void SkiaRecordingCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) { } } + void SkiaRecordingCanvas::callDrawGLFunction(Functor* functor, uirenderer::GlFunctorLifecycleListener* listener) { +#ifdef __ANDROID__ // Layoutlib does not support GL, Vulcan etc. FunctorDrawable* functorDrawable; if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { functorDrawable = mDisplayList->allocateDrawable( @@ -137,9 +146,11 @@ void SkiaRecordingCanvas::callDrawGLFunction(Functor* functor, } mDisplayList->mChildFunctors.push_back(functorDrawable); drawDrawable(functorDrawable); +#endif } void SkiaRecordingCanvas::drawWebViewFunctor(int functor) { +#ifdef __ANDROID__ // Layoutlib does not support GL, Vulcan etc. FunctorDrawable* functorDrawable; if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { functorDrawable = mDisplayList->allocateDrawable(functor, asSkCanvas()); @@ -148,6 +159,7 @@ void SkiaRecordingCanvas::drawWebViewFunctor(int functor) { } mDisplayList->mChildFunctors.push_back(functorDrawable); drawDrawable(functorDrawable); +#endif } void SkiaRecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) { diff --git a/libs/hwui/utils/HostColorSpace.cpp b/libs/hwui/utils/HostColorSpace.cpp new file mode 100644 index 000000000000..77a6820c6999 --- /dev/null +++ b/libs/hwui/utils/HostColorSpace.cpp @@ -0,0 +1,417 @@ +/* + * Copyright (C) 2019 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. + */ +// This is copied from framework/native/libs/ui in order not to include libui in host build + +#include + +using namespace std::placeholders; + +namespace android { + +static constexpr float linearResponse(float v) { + return v; +} + +static constexpr float rcpResponse(float x, const ColorSpace::TransferParameters& p) { + return x >= p.d * p.c ? (std::pow(x, 1.0f / p.g) - p.b) / p.a : x / p.c; +} + +static constexpr float response(float x, const ColorSpace::TransferParameters& p) { + return x >= p.d ? std::pow(p.a * x + p.b, p.g) : p.c * x; +} + +static constexpr float rcpFullResponse(float x, const ColorSpace::TransferParameters& p) { + return x >= p.d * p.c ? (std::pow(x - p.e, 1.0f / p.g) - p.b) / p.a : (x - p.f) / p.c; +} + +static constexpr float fullResponse(float x, const ColorSpace::TransferParameters& p) { + return x >= p.d ? std::pow(p.a * x + p.b, p.g) + p.e : p.c * x + p.f; +} + +static float absRcpResponse(float x, float g,float a, float b, float c, float d) { + float xx = std::abs(x); + return std::copysign(xx >= d * c ? (std::pow(xx, 1.0f / g) - b) / a : xx / c, x); +} + +static float absResponse(float x, float g, float a, float b, float c, float d) { + float xx = std::abs(x); + return std::copysign(xx >= d ? std::pow(a * xx + b, g) : c * xx, x); +} + +static float safePow(float x, float e) { + return powf(x < 0.0f ? 0.0f : x, e); +} + +static ColorSpace::transfer_function toOETF(const ColorSpace::TransferParameters& parameters) { + if (parameters.e == 0.0f && parameters.f == 0.0f) { + return std::bind(rcpResponse, _1, parameters); + } + return std::bind(rcpFullResponse, _1, parameters); +} + +static ColorSpace::transfer_function toEOTF( const ColorSpace::TransferParameters& parameters) { + if (parameters.e == 0.0f && parameters.f == 0.0f) { + return std::bind(response, _1, parameters); + } + return std::bind(fullResponse, _1, parameters); +} + +static ColorSpace::transfer_function toOETF(float gamma) { + if (gamma == 1.0f) { + return linearResponse; + } + return std::bind(safePow, _1, 1.0f / gamma); +} + +static ColorSpace::transfer_function toEOTF(float gamma) { + if (gamma == 1.0f) { + return linearResponse; + } + return std::bind(safePow, _1, gamma); +} + +static constexpr std::array computePrimaries(const mat3& rgbToXYZ) { + float3 r(rgbToXYZ * float3{1, 0, 0}); + float3 g(rgbToXYZ * float3{0, 1, 0}); + float3 b(rgbToXYZ * float3{0, 0, 1}); + + return {{r.xy / dot(r, float3{1}), + g.xy / dot(g, float3{1}), + b.xy / dot(b, float3{1})}}; +} + +static constexpr float2 computeWhitePoint(const mat3& rgbToXYZ) { + float3 w(rgbToXYZ * float3{1}); + return w.xy / dot(w, float3{1}); +} + +ColorSpace::ColorSpace( + const std::string& name, + const mat3& rgbToXYZ, + transfer_function OETF, + transfer_function EOTF, + clamping_function clamper) noexcept + : mName(name) + , mRGBtoXYZ(rgbToXYZ) + , mXYZtoRGB(inverse(rgbToXYZ)) + , mOETF(std::move(OETF)) + , mEOTF(std::move(EOTF)) + , mClamper(std::move(clamper)) + , mPrimaries(computePrimaries(rgbToXYZ)) + , mWhitePoint(computeWhitePoint(rgbToXYZ)) { +} + +ColorSpace::ColorSpace( + const std::string& name, + const mat3& rgbToXYZ, + const TransferParameters parameters, + clamping_function clamper) noexcept + : mName(name) + , mRGBtoXYZ(rgbToXYZ) + , mXYZtoRGB(inverse(rgbToXYZ)) + , mParameters(parameters) + , mOETF(toOETF(mParameters)) + , mEOTF(toEOTF(mParameters)) + , mClamper(std::move(clamper)) + , mPrimaries(computePrimaries(rgbToXYZ)) + , mWhitePoint(computeWhitePoint(rgbToXYZ)) { +} + +ColorSpace::ColorSpace( + const std::string& name, + const mat3& rgbToXYZ, + float gamma, + clamping_function clamper) noexcept + : mName(name) + , mRGBtoXYZ(rgbToXYZ) + , mXYZtoRGB(inverse(rgbToXYZ)) + , mParameters({gamma, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}) + , mOETF(toOETF(gamma)) + , mEOTF(toEOTF(gamma)) + , mClamper(std::move(clamper)) + , mPrimaries(computePrimaries(rgbToXYZ)) + , mWhitePoint(computeWhitePoint(rgbToXYZ)) { +} + +ColorSpace::ColorSpace( + const std::string& name, + const std::array& primaries, + const float2& whitePoint, + transfer_function OETF, + transfer_function EOTF, + clamping_function clamper) noexcept + : mName(name) + , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint)) + , mXYZtoRGB(inverse(mRGBtoXYZ)) + , mOETF(std::move(OETF)) + , mEOTF(std::move(EOTF)) + , mClamper(std::move(clamper)) + , mPrimaries(primaries) + , mWhitePoint(whitePoint) { +} + +ColorSpace::ColorSpace( + const std::string& name, + const std::array& primaries, + const float2& whitePoint, + const TransferParameters parameters, + clamping_function clamper) noexcept + : mName(name) + , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint)) + , mXYZtoRGB(inverse(mRGBtoXYZ)) + , mParameters(parameters) + , mOETF(toOETF(mParameters)) + , mEOTF(toEOTF(mParameters)) + , mClamper(std::move(clamper)) + , mPrimaries(primaries) + , mWhitePoint(whitePoint) { +} + +ColorSpace::ColorSpace( + const std::string& name, + const std::array& primaries, + const float2& whitePoint, + float gamma, + clamping_function clamper) noexcept + : mName(name) + , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint)) + , mXYZtoRGB(inverse(mRGBtoXYZ)) + , mParameters({gamma, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}) + , mOETF(toOETF(gamma)) + , mEOTF(toEOTF(gamma)) + , mClamper(std::move(clamper)) + , mPrimaries(primaries) + , mWhitePoint(whitePoint) { +} + +constexpr mat3 ColorSpace::computeXYZMatrix( + const std::array& primaries, const float2& whitePoint) { + const float2& R = primaries[0]; + const float2& G = primaries[1]; + const float2& B = primaries[2]; + const float2& W = whitePoint; + + float oneRxRy = (1 - R.x) / R.y; + float oneGxGy = (1 - G.x) / G.y; + float oneBxBy = (1 - B.x) / B.y; + float oneWxWy = (1 - W.x) / W.y; + + float RxRy = R.x / R.y; + float GxGy = G.x / G.y; + float BxBy = B.x / B.y; + float WxWy = W.x / W.y; + + float BY = + ((oneWxWy - oneRxRy) * (GxGy - RxRy) - (WxWy - RxRy) * (oneGxGy - oneRxRy)) / + ((oneBxBy - oneRxRy) * (GxGy - RxRy) - (BxBy - RxRy) * (oneGxGy - oneRxRy)); + float GY = (WxWy - RxRy - BY * (BxBy - RxRy)) / (GxGy - RxRy); + float RY = 1 - GY - BY; + + float RYRy = RY / R.y; + float GYGy = GY / G.y; + float BYBy = BY / B.y; + + return { + float3{RYRy * R.x, RY, RYRy * (1 - R.x - R.y)}, + float3{GYGy * G.x, GY, GYGy * (1 - G.x - G.y)}, + float3{BYBy * B.x, BY, BYBy * (1 - B.x - B.y)} + }; +} + +const ColorSpace ColorSpace::sRGB() { + return { + "sRGB IEC61966-2.1", + {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, + {0.3127f, 0.3290f}, + {2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f, 0.0f, 0.0f} + }; +} + +const ColorSpace ColorSpace::linearSRGB() { + return { + "sRGB IEC61966-2.1 (Linear)", + {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, + {0.3127f, 0.3290f} + }; +} + +const ColorSpace ColorSpace::extendedSRGB() { + return { + "scRGB-nl IEC 61966-2-2:2003", + {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, + {0.3127f, 0.3290f}, + std::bind(absRcpResponse, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f), + std::bind(absResponse, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f), + std::bind(clamp, _1, -0.799f, 2.399f) + }; +} + +const ColorSpace ColorSpace::linearExtendedSRGB() { + return { + "scRGB IEC 61966-2-2:2003", + {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, + {0.3127f, 0.3290f}, + 1.0f, + std::bind(clamp, _1, -0.5f, 7.499f) + }; +} + +const ColorSpace ColorSpace::NTSC() { + return { + "NTSC (1953)", + {{float2{0.67f, 0.33f}, {0.21f, 0.71f}, {0.14f, 0.08f}}}, + {0.310f, 0.316f}, + {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f} + }; +} + +const ColorSpace ColorSpace::BT709() { + return { + "Rec. ITU-R BT.709-5", + {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, + {0.3127f, 0.3290f}, + {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f} + }; +} + +const ColorSpace ColorSpace::BT2020() { + return { + "Rec. ITU-R BT.2020-1", + {{float2{0.708f, 0.292f}, {0.170f, 0.797f}, {0.131f, 0.046f}}}, + {0.3127f, 0.3290f}, + {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f} + }; +} + +const ColorSpace ColorSpace::AdobeRGB() { + return { + "Adobe RGB (1998)", + {{float2{0.64f, 0.33f}, {0.21f, 0.71f}, {0.15f, 0.06f}}}, + {0.3127f, 0.3290f}, + 2.2f + }; +} + +const ColorSpace ColorSpace::ProPhotoRGB() { + return { + "ROMM RGB ISO 22028-2:2013", + {{float2{0.7347f, 0.2653f}, {0.1596f, 0.8404f}, {0.0366f, 0.0001f}}}, + {0.34567f, 0.35850f}, + {1.8f, 1.0f, 0.0f, 1 / 16.0f, 0.031248f, 0.0f, 0.0f} + }; +} + +const ColorSpace ColorSpace::DisplayP3() { + return { + "Display P3", + {{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}}, + {0.3127f, 0.3290f}, + {2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.039f, 0.0f, 0.0f} + }; +} + +const ColorSpace ColorSpace::DCIP3() { + return { + "SMPTE RP 431-2-2007 DCI (P3)", + {{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}}, + {0.314f, 0.351f}, + 2.6f + }; +} + +const ColorSpace ColorSpace::ACES() { + return { + "SMPTE ST 2065-1:2012 ACES", + {{float2{0.73470f, 0.26530f}, {0.0f, 1.0f}, {0.00010f, -0.0770f}}}, + {0.32168f, 0.33767f}, + 1.0f, + std::bind(clamp, _1, -65504.0f, 65504.0f) + }; +} + +const ColorSpace ColorSpace::ACEScg() { + return { + "Academy S-2014-004 ACEScg", + {{float2{0.713f, 0.293f}, {0.165f, 0.830f}, {0.128f, 0.044f}}}, + {0.32168f, 0.33767f}, + 1.0f, + std::bind(clamp, _1, -65504.0f, 65504.0f) + }; +} + +std::unique_ptr ColorSpace::createLUT(uint32_t size, const ColorSpace& src, + const ColorSpace& dst) { + size = clamp(size, 2u, 256u); + float m = 1.0f / float(size - 1); + + std::unique_ptr lut(new float3[size * size * size]); + float3* data = lut.get(); + + ColorSpaceConnector connector(src, dst); + + for (uint32_t z = 0; z < size; z++) { + for (int32_t y = int32_t(size - 1); y >= 0; y--) { + for (uint32_t x = 0; x < size; x++) { + *data++ = connector.transform({x * m, y * m, z * m}); + } + } + } + + return lut; +} + +static const float2 ILLUMINANT_D50_XY = {0.34567f, 0.35850f}; +static const float3 ILLUMINANT_D50_XYZ = {0.964212f, 1.0f, 0.825188f}; +static const mat3 BRADFORD = mat3{ + float3{ 0.8951f, -0.7502f, 0.0389f}, + float3{ 0.2664f, 1.7135f, -0.0685f}, + float3{-0.1614f, 0.0367f, 1.0296f} +}; + +static mat3 adaptation(const mat3& matrix, const float3& srcWhitePoint, const float3& dstWhitePoint) { + float3 srcLMS = matrix * srcWhitePoint; + float3 dstLMS = matrix * dstWhitePoint; + return inverse(matrix) * mat3{dstLMS / srcLMS} * matrix; +} + +ColorSpaceConnector::ColorSpaceConnector( + const ColorSpace& src, + const ColorSpace& dst) noexcept + : mSource(src) + , mDestination(dst) { + + if (all(lessThan(abs(src.getWhitePoint() - dst.getWhitePoint()), float2{1e-3f}))) { + mTransform = dst.getXYZtoRGB() * src.getRGBtoXYZ(); + } else { + mat3 rgbToXYZ(src.getRGBtoXYZ()); + mat3 xyzToRGB(dst.getXYZtoRGB()); + + float3 srcXYZ = ColorSpace::XYZ(float3{src.getWhitePoint(), 1}); + float3 dstXYZ = ColorSpace::XYZ(float3{dst.getWhitePoint(), 1}); + + if (any(greaterThan(abs(src.getWhitePoint() - ILLUMINANT_D50_XY), float2{1e-3f}))) { + rgbToXYZ = adaptation(BRADFORD, srcXYZ, ILLUMINANT_D50_XYZ) * src.getRGBtoXYZ(); + } + + if (any(greaterThan(abs(dst.getWhitePoint() - ILLUMINANT_D50_XY), float2{1e-3f}))) { + xyzToRGB = inverse(adaptation(BRADFORD, dstXYZ, ILLUMINANT_D50_XYZ) * dst.getRGBtoXYZ()); + } + + mTransform = xyzToRGB * rgbToXYZ; + } +} + +}; // namespace android -- cgit v1.2.3 From e275052118e686328560fee6bd28a0a21842a248 Mon Sep 17 00:00:00 2001 From: John Reck Date: Wed, 26 Jun 2019 13:46:09 -0700 Subject: Fix damage calculation for animation matrix Fixes: 111094270 Test: demo app from bug Change-Id: If434ade19fa103fabbe64f483193d61cb6f1b204 --- libs/hwui/DamageAccumulator.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'libs') diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp index cca0032b230e..2d2df52073f4 100644 --- a/libs/hwui/DamageAccumulator.cpp +++ b/libs/hwui/DamageAccumulator.cpp @@ -137,20 +137,28 @@ void DamageAccumulator::applyMatrix4Transform(DirtyStack* frame) { mapRect(frame->matrix4, frame->pendingDirty, &mHead->pendingDirty); } -static inline void mapRect(const RenderProperties& props, const SkRect& in, SkRect* out) { - if (in.isEmpty()) return; - const SkMatrix* transform = props.getTransformMatrix(); - SkRect temp(in); +static inline void applyMatrix(const SkMatrix* transform, SkRect* rect) { if (transform && !transform->isIdentity()) { if (CC_LIKELY(!transform->hasPerspective())) { - transform->mapRect(&temp); + transform->mapRect(rect); } else { // Don't attempt to calculate damage for a perspective transform // as the numbers this works with can break the perspective // calculations. Just give up and expand to DIRTY_MIN/DIRTY_MAX - temp.set(DIRTY_MIN, DIRTY_MIN, DIRTY_MAX, DIRTY_MAX); + rect->set(DIRTY_MIN, DIRTY_MIN, DIRTY_MAX, DIRTY_MAX); } } +} + +static inline void mapRect(const RenderProperties& props, const SkRect& in, SkRect* out) { + if (in.isEmpty()) return; + SkRect temp(in); + applyMatrix(props.getTransformMatrix(), &temp); + if (props.getStaticMatrix()) { + applyMatrix(props.getStaticMatrix(), &temp); + } else if (props.getAnimationMatrix()) { + applyMatrix(props.getAnimationMatrix(), &temp); + } temp.offset(props.getLeft(), props.getTop()); out->join(temp); } -- cgit v1.2.3 From 6bbcedacf50e95ce987eb35441a3f82d7e394167 Mon Sep 17 00:00:00 2001 From: Yiwei Zhang Date: Wed, 26 Jun 2019 13:20:59 -0700 Subject: HWUI-VK: Fix initial window transform Test: CtsUiRenderingTestCases and CtsGraphicsTestCases Change-Id: I5783be3cfe47597b7931b5605579edb6e189a87e --- libs/hwui/renderthread/VulkanSurface.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'libs') diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp index b2cc23e76b8a..1d75e55ae87a 100644 --- a/libs/hwui/renderthread/VulkanSurface.cpp +++ b/libs/hwui/renderthread/VulkanSurface.cpp @@ -52,11 +52,11 @@ static int InvertTransform(int transform) { static int ConvertVkTransformToNative(VkSurfaceTransformFlagsKHR transform) { switch (transform) { case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR: - return NATIVE_WINDOW_TRANSFORM_ROT_270; + return NATIVE_WINDOW_TRANSFORM_ROT_90; case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR: return NATIVE_WINDOW_TRANSFORM_ROT_180; case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR: - return NATIVE_WINDOW_TRANSFORM_ROT_90; + return NATIVE_WINDOW_TRANSFORM_ROT_270; case VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR: case VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR: default: @@ -211,7 +211,7 @@ VulkanSurface* VulkanSurface::Create(ANativeWindow* window, ColorMode colorMode, */ WindowInfo windowInfo; - windowInfo.transform = ConvertVkTransformToNative(caps.supportedTransforms); + windowInfo.transform = ConvertVkTransformToNative(caps.currentTransform); windowInfo.size = SkISize::Make(caps.currentExtent.width, caps.currentExtent.height); const SkISize minSize = SkISize::Make(caps.minImageExtent.width, caps.minImageExtent.height); -- cgit v1.2.3 From bd363c94ff63387c15184865826ba3700013cb1b Mon Sep 17 00:00:00 2001 From: Yiwei Zhang Date: Wed, 26 Jun 2019 14:22:32 -0700 Subject: HWUI-VK: Decouple hwui Vulkan backend from Vulkan WSI APIs 1. Android support all transforms as exposed by client composition. 2. Properly exposing min/max image size is tracked in http://b/134182502. 3. Current image size and max buffer count are cached in Surface already. 4. Remove surface support check since hwui does not have YUV + CPU consumer. 5. Since HWUI must require those image usage flags, just use them directly, and those are always supported by Vulkan driver and enforced by cts. Bug: 136110987 Test: CtsUiRenderingTestCases and CtsGraphicsTestCases Change-Id: I89bd9afb69a46e3deca09890ce6348c6b61edddf --- libs/hwui/renderthread/VulkanManager.cpp | 6 -- libs/hwui/renderthread/VulkanManager.h | 8 -- libs/hwui/renderthread/VulkanSurface.cpp | 136 +++++++++---------------------- 3 files changed, 39 insertions(+), 111 deletions(-) (limited to 'libs') diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index 46c3f2fc62d2..280f7d3489d2 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -145,12 +145,6 @@ void VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe GET_INST_PROC(GetPhysicalDeviceImageFormatProperties2); GET_INST_PROC(CreateDevice); GET_INST_PROC(EnumerateDeviceExtensionProperties); - GET_INST_PROC(CreateAndroidSurfaceKHR); - GET_INST_PROC(DestroySurfaceKHR); - GET_INST_PROC(GetPhysicalDeviceSurfaceSupportKHR); - GET_INST_PROC(GetPhysicalDeviceSurfaceCapabilitiesKHR); - GET_INST_PROC(GetPhysicalDeviceSurfaceFormatsKHR); - GET_INST_PROC(GetPhysicalDeviceSurfacePresentModesKHR); uint32_t gpuCount; LOG_ALWAYS_FATAL_IF(mEnumeratePhysicalDevices(mInstance, &gpuCount, nullptr)); diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h index dd3c6d0dba81..ec217c0fcbf4 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -107,14 +107,6 @@ private: FNPTR_TYPE fPtr; }; - // WSI interface functions - VkPtr mCreateAndroidSurfaceKHR; - VkPtr mDestroySurfaceKHR; - VkPtr mGetPhysicalDeviceSurfaceSupportKHR; - VkPtr mGetPhysicalDeviceSurfaceCapabilitiesKHR; - VkPtr mGetPhysicalDeviceSurfaceFormatsKHR; - VkPtr mGetPhysicalDeviceSurfacePresentModesKHR; - // Instance Functions VkPtr mEnumerateInstanceVersion; VkPtr mEnumerateInstanceExtensionProperties; diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp index 1d75e55ae87a..a6676608ec44 100644 --- a/libs/hwui/renderthread/VulkanSurface.cpp +++ b/libs/hwui/renderthread/VulkanSurface.cpp @@ -49,21 +49,6 @@ static int InvertTransform(int transform) { } } -static int ConvertVkTransformToNative(VkSurfaceTransformFlagsKHR transform) { - switch (transform) { - case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR: - return NATIVE_WINDOW_TRANSFORM_ROT_90; - case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR: - return NATIVE_WINDOW_TRANSFORM_ROT_180; - case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR: - return NATIVE_WINDOW_TRANSFORM_ROT_270; - case VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR: - case VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR: - default: - return 0; - } -} - static SkMatrix GetPreTransformMatrix(SkISize windowSize, int transform) { const int width = windowSize.width(); const int height = windowSize.height(); @@ -140,106 +125,60 @@ static bool ResetNativeWindow(ANativeWindow* window) { return true; } -class VkSurfaceAutoDeleter { -public: - VkSurfaceAutoDeleter(VkInstance instance, VkSurfaceKHR surface, - PFN_vkDestroySurfaceKHR destroySurfaceKHR) - : mInstance(instance), mSurface(surface), mDestroySurfaceKHR(destroySurfaceKHR) {} - ~VkSurfaceAutoDeleter() { destroy(); } - - void destroy() { - if (mSurface != VK_NULL_HANDLE) { - mDestroySurfaceKHR(mInstance, mSurface, nullptr); - mSurface = VK_NULL_HANDLE; - } - } - -private: - VkInstance mInstance; - VkSurfaceKHR mSurface; - PFN_vkDestroySurfaceKHR mDestroySurfaceKHR; -}; - VulkanSurface* VulkanSurface::Create(ANativeWindow* window, ColorMode colorMode, SkColorType colorType, sk_sp colorSpace, GrContext* grContext, const VulkanManager& vkManager, uint32_t extraBuffers) { - VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo; - memset(&surfaceCreateInfo, 0, sizeof(VkAndroidSurfaceCreateInfoKHR)); - surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR; - surfaceCreateInfo.pNext = nullptr; - surfaceCreateInfo.flags = 0; - surfaceCreateInfo.window = window; - - VkSurfaceKHR vkSurface = VK_NULL_HANDLE; - VkResult res = vkManager.mCreateAndroidSurfaceKHR(vkManager.mInstance, &surfaceCreateInfo, - nullptr, &vkSurface); - if (VK_SUCCESS != res) { - ALOGE("VulkanSurface::Create() vkCreateAndroidSurfaceKHR failed (%d)", res); - return nullptr; - } - - VkSurfaceAutoDeleter vkSurfaceDeleter(vkManager.mInstance, vkSurface, - vkManager.mDestroySurfaceKHR); - - SkDEBUGCODE(VkBool32 supported; res = vkManager.mGetPhysicalDeviceSurfaceSupportKHR( - vkManager.mPhysicalDevice, vkManager.mPresentQueueIndex, - vkSurface, &supported); - // All physical devices and queue families on Android must be capable of - // presentation with any native window. - SkASSERT(VK_SUCCESS == res && supported);); - - // check for capabilities - VkSurfaceCapabilitiesKHR caps; - res = vkManager.mGetPhysicalDeviceSurfaceCapabilitiesKHR(vkManager.mPhysicalDevice, vkSurface, - &caps); - if (VK_SUCCESS != res) { - ALOGE("VulkanSurface::Create() vkGetPhysicalDeviceSurfaceCapabilitiesKHR failed (%d)", res); - return nullptr; - } - - LOG_ALWAYS_FATAL_IF(0 == (caps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)); - - /* - * We must destroy the VK Surface before attempting to update the window as doing so after - * will cause the native window to be modified in unexpected ways. - */ - vkSurfaceDeleter.destroy(); + // TODO(http://b/134182502) + const SkISize minSize = SkISize::Make(1, 1); + const SkISize maxSize = SkISize::Make(4096, 4096); /* * Populate Window Info struct */ WindowInfo windowInfo; - windowInfo.transform = ConvertVkTransformToNative(caps.currentTransform); - windowInfo.size = SkISize::Make(caps.currentExtent.width, caps.currentExtent.height); + int err; + int width, height; + err = window->query(window, NATIVE_WINDOW_DEFAULT_WIDTH, &width); + if (err != 0 || width < 0) { + ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, width); + return nullptr; + } + err = window->query(window, NATIVE_WINDOW_DEFAULT_HEIGHT, &height); + if (err != 0 || height < 0) { + ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, height); + return nullptr; + } + windowInfo.size = SkISize::Make(width, height); + + int query_value; + err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT, &query_value); + if (err != 0 || query_value < 0) { + ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, query_value); + return nullptr; + } + windowInfo.transform = query_value; - const SkISize minSize = SkISize::Make(caps.minImageExtent.width, caps.minImageExtent.height); - const SkISize maxSize = SkISize::Make(caps.maxImageExtent.width, caps.maxImageExtent.height); ComputeWindowSizeAndTransform(&windowInfo, minSize, maxSize); - int query_value; - int err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value); + err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value); if (err != 0 || query_value < 0) { ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, query_value); return nullptr; } - auto min_undequeued_buffers = static_cast(query_value); + windowInfo.bufferCount = static_cast(query_value) + sTargetBufferCount + extraBuffers; - windowInfo.bufferCount = min_undequeued_buffers + - std::max(sTargetBufferCount + extraBuffers, caps.minImageCount); - if (caps.maxImageCount > 0 && windowInfo.bufferCount > caps.maxImageCount) { + err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &query_value); + if (err != 0 || query_value < 0) { + ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, query_value); + return nullptr; + } + if (windowInfo.bufferCount > static_cast(query_value)) { // Application must settle for fewer images than desired: - windowInfo.bufferCount = caps.maxImageCount; + windowInfo.bufferCount = static_cast(query_value); } - // Currently Skia requires the images to be color attachments and support all transfer - // operations. - VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | - VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | - VK_IMAGE_USAGE_TRANSFER_DST_BIT; - LOG_ALWAYS_FATAL_IF((caps.supportedUsageFlags & usageFlags) != usageFlags); - windowInfo.dataspace = HAL_DATASPACE_V0_SRGB; if (colorMode == ColorMode::WideColorGamut) { skcms_Matrix3x3 surfaceGamut; @@ -275,7 +214,10 @@ VulkanSurface* VulkanSurface::Create(ANativeWindow* window, ColorMode colorMode, imageFormatInfo.format = vkPixelFormat; imageFormatInfo.type = VK_IMAGE_TYPE_2D; imageFormatInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - imageFormatInfo.usage = usageFlags; + // Currently Skia requires the images to be color attachments and support all transfer + // operations. + imageFormatInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; imageFormatInfo.flags = 0; VkAndroidHardwareBufferUsageANDROID hwbUsage; @@ -286,8 +228,8 @@ VulkanSurface* VulkanSurface::Create(ANativeWindow* window, ColorMode colorMode, imgFormProps.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2; imgFormProps.pNext = &hwbUsage; - res = vkManager.mGetPhysicalDeviceImageFormatProperties2(vkManager.mPhysicalDevice, - &imageFormatInfo, &imgFormProps); + VkResult res = vkManager.mGetPhysicalDeviceImageFormatProperties2( + vkManager.mPhysicalDevice, &imageFormatInfo, &imgFormProps); if (VK_SUCCESS != res) { ALOGE("Failed to query GetPhysicalDeviceImageFormatProperties2"); return nullptr; -- cgit v1.2.3 From 68daf67e66baee9fd6b3adf7b5a94b72dd05aacf Mon Sep 17 00:00:00 2001 From: Yiwei Zhang Date: Wed, 26 Jun 2019 22:02:41 -0700 Subject: HWUI-VK: connect and set native window before any queries Since Vulkan WSI has been removed from HWUI Vulkan backend, upon VulkanSurface creation, we should connect to native window first. Bug: 136110987 Test: CtsUiRenderingTestCases and CtsGraphicsTestCases Change-Id: I005a8bf22cce0364f240e09fceeb275a6f36aa77 --- libs/hwui/renderthread/VulkanSurface.cpp | 124 ++++++++++++++++--------------- libs/hwui/renderthread/VulkanSurface.h | 5 ++ 2 files changed, 71 insertions(+), 58 deletions(-) (limited to 'libs') diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp index a6676608ec44..4f64da79c3fb 100644 --- a/libs/hwui/renderthread/VulkanSurface.cpp +++ b/libs/hwui/renderthread/VulkanSurface.cpp @@ -83,42 +83,45 @@ void VulkanSurface::ComputeWindowSizeAndTransform(WindowInfo* windowInfo, const } windowInfo->actualSize = windowSize; - if (windowInfo->transform & HAL_TRANSFORM_ROT_90) { + if (windowInfo->transform & NATIVE_WINDOW_TRANSFORM_ROT_90) { windowInfo->actualSize.set(windowSize.height(), windowSize.width()); } windowInfo->preTransform = GetPreTransformMatrix(windowInfo->size, windowInfo->transform); } -static bool ResetNativeWindow(ANativeWindow* window) { - // -- Reset the native window -- - // The native window might have been used previously, and had its properties - // changed from defaults. That will affect the answer we get for queries - // like MIN_UNDEQUEUED_BUFFERS. Reset to a known/default state before we - // attempt such queries. +static bool ConnectAndSetWindowDefaults(ANativeWindow* window) { + ATRACE_CALL(); int err = native_window_api_connect(window, NATIVE_WINDOW_API_EGL); if (err != 0) { - ALOGW("native_window_api_connect failed: %s (%d)", strerror(-err), err); + ALOGE("native_window_api_connect failed: %s (%d)", strerror(-err), err); return false; } // this will match what we do on GL so pick that here. err = window->setSwapInterval(window, 1); if (err != 0) { - ALOGW("native_window->setSwapInterval(1) failed: %s (%d)", strerror(-err), err); + ALOGE("native_window->setSwapInterval(1) failed: %s (%d)", strerror(-err), err); return false; } err = native_window_set_shared_buffer_mode(window, false); if (err != 0) { - ALOGW("native_window_set_shared_buffer_mode(false) failed: %s (%d)", strerror(-err), err); + ALOGE("native_window_set_shared_buffer_mode(false) failed: %s (%d)", strerror(-err), err); return false; } err = native_window_set_auto_refresh(window, false); if (err != 0) { - ALOGW("native_window_set_auto_refresh(false) failed: %s (%d)", strerror(-err), err); + ALOGE("native_window_set_auto_refresh(false) failed: %s (%d)", strerror(-err), err); + return false; + } + + err = native_window_set_scaling_mode(window, NATIVE_WINDOW_SCALING_MODE_FREEZE); + if (err != 0) { + ALOGE("native_window_set_scaling_mode(NATIVE_WINDOW_SCALING_MODE_FREEZE) failed: %s (%d)", + strerror(-err), err); return false; } @@ -133,69 +136,92 @@ VulkanSurface* VulkanSurface::Create(ANativeWindow* window, ColorMode colorMode, const SkISize minSize = SkISize::Make(1, 1); const SkISize maxSize = SkISize::Make(4096, 4096); - /* - * Populate Window Info struct - */ + // Connect and set native window to default configurations. + if (!ConnectAndSetWindowDefaults(window)) { + return nullptr; + } + + // Initialize WindowInfo struct. WindowInfo windowInfo; + if (!InitializeWindowInfoStruct(window, colorMode, colorType, colorSpace, vkManager, + extraBuffers, minSize, maxSize, &windowInfo)) { + return nullptr; + } + + // Now we attempt to modify the window. + if (!UpdateWindow(window, windowInfo)) { + return nullptr; + } + + return new VulkanSurface(window, windowInfo, minSize, maxSize, grContext); +} + +bool VulkanSurface::InitializeWindowInfoStruct(ANativeWindow* window, ColorMode colorMode, + SkColorType colorType, + sk_sp colorSpace, + const VulkanManager& vkManager, + uint32_t extraBuffers, const SkISize& minSize, + const SkISize& maxSize, WindowInfo* outWindowInfo) { + ATRACE_CALL(); - int err; int width, height; - err = window->query(window, NATIVE_WINDOW_DEFAULT_WIDTH, &width); + int err = window->query(window, NATIVE_WINDOW_DEFAULT_WIDTH, &width); if (err != 0 || width < 0) { ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, width); - return nullptr; + return false; } err = window->query(window, NATIVE_WINDOW_DEFAULT_HEIGHT, &height); if (err != 0 || height < 0) { ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, height); - return nullptr; + return false; } - windowInfo.size = SkISize::Make(width, height); + outWindowInfo->size = SkISize::Make(width, height); int query_value; err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT, &query_value); if (err != 0 || query_value < 0) { ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, query_value); - return nullptr; + return false; } - windowInfo.transform = query_value; + outWindowInfo->transform = query_value; - ComputeWindowSizeAndTransform(&windowInfo, minSize, maxSize); + ComputeWindowSizeAndTransform(outWindowInfo, minSize, maxSize); err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value); if (err != 0 || query_value < 0) { ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, query_value); - return nullptr; + return false; } - windowInfo.bufferCount = static_cast(query_value) + sTargetBufferCount + extraBuffers; + outWindowInfo->bufferCount = + static_cast(query_value) + sTargetBufferCount + extraBuffers; err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &query_value); if (err != 0 || query_value < 0) { ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, query_value); - return nullptr; + return false; } - if (windowInfo.bufferCount > static_cast(query_value)) { + if (outWindowInfo->bufferCount > static_cast(query_value)) { // Application must settle for fewer images than desired: - windowInfo.bufferCount = static_cast(query_value); + outWindowInfo->bufferCount = static_cast(query_value); } - windowInfo.dataspace = HAL_DATASPACE_V0_SRGB; + outWindowInfo->dataspace = HAL_DATASPACE_V0_SRGB; if (colorMode == ColorMode::WideColorGamut) { skcms_Matrix3x3 surfaceGamut; LOG_ALWAYS_FATAL_IF(!colorSpace->toXYZD50(&surfaceGamut), "Could not get gamut matrix from color space"); if (memcmp(&surfaceGamut, &SkNamedGamut::kSRGB, sizeof(surfaceGamut)) == 0) { - windowInfo.dataspace = HAL_DATASPACE_V0_SCRGB; + outWindowInfo->dataspace = HAL_DATASPACE_V0_SCRGB; } else if (memcmp(&surfaceGamut, &SkNamedGamut::kDCIP3, sizeof(surfaceGamut)) == 0) { - windowInfo.dataspace = HAL_DATASPACE_DISPLAY_P3; + outWindowInfo->dataspace = HAL_DATASPACE_DISPLAY_P3; } else { LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space."); } } - windowInfo.pixelFormat = ColorTypeToPixelFormat(colorType); + outWindowInfo->pixelFormat = ColorTypeToPixelFormat(colorType); VkFormat vkPixelFormat = VK_FORMAT_R8G8B8A8_UNORM; - if (windowInfo.pixelFormat == PIXEL_FORMAT_RGBA_FP16) { + if (outWindowInfo->pixelFormat == PIXEL_FORMAT_RGBA_FP16) { vkPixelFormat = VK_FORMAT_R16G16B16A16_SFLOAT; } @@ -232,31 +258,23 @@ VulkanSurface* VulkanSurface::Create(ANativeWindow* window, ColorMode colorMode, vkManager.mPhysicalDevice, &imageFormatInfo, &imgFormProps); if (VK_SUCCESS != res) { ALOGE("Failed to query GetPhysicalDeviceImageFormatProperties2"); - return nullptr; + return false; } uint64_t consumerUsage; - native_window_get_consumer_usage(window, &consumerUsage); - windowInfo.windowUsageFlags = consumerUsage | hwbUsage.androidHardwareBufferUsage; - - /* - * Now we attempt to modify the window! - */ - if (!UpdateWindow(window, windowInfo)) { - return nullptr; + err = native_window_get_consumer_usage(window, &consumerUsage); + if (err != 0) { + ALOGE("native_window_get_consumer_usage failed: %s (%d)", strerror(-err), err); + return false; } + outWindowInfo->windowUsageFlags = consumerUsage | hwbUsage.androidHardwareBufferUsage; - return new VulkanSurface(window, windowInfo, minSize, maxSize, grContext); + return true; } bool VulkanSurface::UpdateWindow(ANativeWindow* window, const WindowInfo& windowInfo) { ATRACE_CALL(); - if (!ResetNativeWindow(window)) { - return false; - } - - // -- Configure the native window -- int err = native_window_set_buffers_format(window, windowInfo.pixelFormat); if (err != 0) { ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_format(%d) failed: %s (%d)", @@ -295,16 +313,6 @@ bool VulkanSurface::UpdateWindow(ANativeWindow* window, const WindowInfo& window return false; } - // Vulkan defaults to NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW, but this is different than - // HWUI's expectation - err = native_window_set_scaling_mode(window, NATIVE_WINDOW_SCALING_MODE_FREEZE); - if (err != 0) { - ALOGE("VulkanSurface::UpdateWindow() native_window_set_scaling_mode(SCALE_TO_WINDOW) " - "failed: %s (%d)", - strerror(-err), err); - return false; - } - err = native_window_set_buffer_count(window, windowInfo.bufferCount); if (err != 0) { ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffer_count(%zu) failed: %s (%d)", @@ -319,7 +327,7 @@ bool VulkanSurface::UpdateWindow(ANativeWindow* window, const WindowInfo& window return false; } - return err == 0; + return true; } VulkanSurface::VulkanSurface(ANativeWindow* window, const WindowInfo& windowInfo, diff --git a/libs/hwui/renderthread/VulkanSurface.h b/libs/hwui/renderthread/VulkanSurface.h index b7af596ae762..54007e771999 100644 --- a/libs/hwui/renderthread/VulkanSurface.h +++ b/libs/hwui/renderthread/VulkanSurface.h @@ -103,6 +103,11 @@ private: VulkanSurface(ANativeWindow* window, const WindowInfo& windowInfo, SkISize minWindowSize, SkISize maxWindowSize, GrContext* grContext); + static bool InitializeWindowInfoStruct(ANativeWindow* window, ColorMode colorMode, + SkColorType colorType, sk_sp colorSpace, + const VulkanManager& vkManager, uint32_t extraBuffers, + const SkISize& minSize, const SkISize& maxSize, + WindowInfo* outWindowInfo); static bool UpdateWindow(ANativeWindow* window, const WindowInfo& windowInfo); static void ComputeWindowSizeAndTransform(WindowInfo* windowInfo, const SkISize& minSize, const SkISize& maxSize); -- cgit v1.2.3 From 6bdd90f70dbc848ec2a1db55ce5a0360ba20b56c Mon Sep 17 00:00:00 2001 From: Fedor Kudasov Date: Thu, 27 Jun 2019 18:09:30 +0100 Subject: Remove unused (mostly GL-related) headers Bug: 117921091 Test: all tests should pass Change-Id: I419aafd30bfc19f2180eedf924bbd55d5cd36170 --- libs/hwui/renderthread/CanvasContext.cpp | 3 --- libs/hwui/renderthread/RenderProxy.cpp | 3 --- 2 files changed, 6 deletions(-) (limited to 'libs') diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index c9203d9bb530..550c27d7a051 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -19,7 +19,6 @@ #include "../Properties.h" #include "AnimationContext.h" -#include "EglManager.h" #include "Frame.h" #include "LayerUpdateQueue.h" #include "Properties.h" @@ -33,8 +32,6 @@ #include "utils/TimeUtils.h" #include "utils/TraceUtils.h" -#include -#include #include #include diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 20247dc7c861..3dd1d4462d02 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -22,11 +22,8 @@ #include "Readback.h" #include "Rect.h" #include "WebViewFunctorManager.h" -#include "pipeline/skia/SkiaOpenGLPipeline.h" #include "pipeline/skia/VectorDrawableAtlas.h" -#include "renderstate/RenderState.h" #include "renderthread/CanvasContext.h" -#include "renderthread/EglManager.h" #include "renderthread/RenderTask.h" #include "renderthread/RenderThread.h" #include "utils/Macros.h" -- cgit v1.2.3 From db7d1df4fc5aa3641f5c3642fa8ed41629bb13ae Mon Sep 17 00:00:00 2001 From: John Reck Date: Thu, 27 Jun 2019 10:21:47 -0700 Subject: Switch to import for CtsUiRenderingTestCases Centralizes option management Test: this Change-Id: I80bcaec765e1ed2bf0bdbe370548157690367ec6 --- libs/hwui/TEST_MAPPING | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'libs') diff --git a/libs/hwui/TEST_MAPPING b/libs/hwui/TEST_MAPPING index d9f2acbb49d2..b1719a979ce5 100644 --- a/libs/hwui/TEST_MAPPING +++ b/libs/hwui/TEST_MAPPING @@ -1,13 +1,15 @@ { "presubmit": [ - { - "name": "CtsUiRenderingTestCases" - }, { "name": "CtsGraphicsTestCases" }, { "name": "CtsAccelerationTestCases" } + ], + "imports": [ + { + "path": "cts/tests/tests/uirendering" + } ] } \ No newline at end of file -- cgit v1.2.3 From dab6ecd6cec4a23c26928e6fca81fb8401fa4ad4 Mon Sep 17 00:00:00 2001 From: Yiwei Zhang Date: Thu, 27 Jun 2019 12:22:33 -0700 Subject: HWUI-VK: Use auto prerotation for Vulkan backend The consumer driven resizing now works as below: 1. Query the cached transform hint for this draw cycle 2. dequeuBuffer first to get the actual size 3. Cancel the pre-rotation done on the buffer 4. Update logical window size according to the transform Bug: 136110987 Test: manually test split screen with resizing and rotation Test: CtsUiRenderingTestCases and CtsGraphicsTestCases Change-Id: Ia7f1a53fd761b90cb3ff429086a060d48e40919c --- libs/hwui/renderthread/VulkanSurface.cpp | 154 ++++++++++++------------------- libs/hwui/renderthread/VulkanSurface.h | 11 +-- 2 files changed, 63 insertions(+), 102 deletions(-) (limited to 'libs') diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp index 4f64da79c3fb..bbffb359aebe 100644 --- a/libs/hwui/renderthread/VulkanSurface.cpp +++ b/libs/hwui/renderthread/VulkanSurface.cpp @@ -27,15 +27,6 @@ namespace android { namespace uirenderer { namespace renderthread { -static bool IsTransformSupported(int transform) { - // For now, only support pure rotations, not flip or flip-and-rotate, until we have - // more time to test them and build sample code. As far as I know we never actually - // use anything besides pure rotations anyway. - return transform == 0 || transform == NATIVE_WINDOW_TRANSFORM_ROT_90 || - transform == NATIVE_WINDOW_TRANSFORM_ROT_180 || - transform == NATIVE_WINDOW_TRANSFORM_ROT_270; -} - static int InvertTransform(int transform) { switch (transform) { case NATIVE_WINDOW_TRANSFORM_ROT_90: @@ -68,28 +59,6 @@ static SkMatrix GetPreTransformMatrix(SkISize windowSize, int transform) { return SkMatrix::I(); } -void VulkanSurface::ComputeWindowSizeAndTransform(WindowInfo* windowInfo, const SkISize& minSize, - const SkISize& maxSize) { - SkISize& windowSize = windowInfo->size; - - // clamp width & height to handle currentExtent of -1 and protect us from broken hints - if (windowSize.width() < minSize.width() || windowSize.width() > maxSize.width() || - windowSize.height() < minSize.height() || windowSize.height() > maxSize.height()) { - int width = std::min(maxSize.width(), std::max(minSize.width(), windowSize.width())); - int height = std::min(maxSize.height(), std::max(minSize.height(), windowSize.height())); - ALOGE("Invalid Window Dimensions [%d, %d]; clamping to [%d, %d]", windowSize.width(), - windowSize.height(), width, height); - windowSize.set(width, height); - } - - windowInfo->actualSize = windowSize; - if (windowInfo->transform & NATIVE_WINDOW_TRANSFORM_ROT_90) { - windowInfo->actualSize.set(windowSize.height(), windowSize.width()); - } - - windowInfo->preTransform = GetPreTransformMatrix(windowInfo->size, windowInfo->transform); -} - static bool ConnectAndSetWindowDefaults(ANativeWindow* window) { ATRACE_CALL(); @@ -125,6 +94,24 @@ static bool ConnectAndSetWindowDefaults(ANativeWindow* window) { return false; } + // Let consumer drive the size of the buffers. + err = native_window_set_buffers_dimensions(window, 0, 0); + if (err != 0) { + ALOGE("native_window_set_buffers_dimensions(0,0) failed: %s (%d)", strerror(-err), err); + return false; + } + + // Enable auto prerotation, so when buffer size is driven by the consumer + // and the transform hint specifies a 90 or 270 degree rotation, the width + // and height used for buffer pre-allocation and dequeueBuffer will be + // additionally swapped. + err = native_window_set_auto_prerotation(window, true); + if (err != 0) { + ALOGE("VulkanSurface::UpdateWindow() native_window_set_auto_prerotation failed: %s (%d)", + strerror(-err), err); + return false; + } + return true; } @@ -132,10 +119,6 @@ VulkanSurface* VulkanSurface::Create(ANativeWindow* window, ColorMode colorMode, SkColorType colorType, sk_sp colorSpace, GrContext* grContext, const VulkanManager& vkManager, uint32_t extraBuffers) { - // TODO(http://b/134182502) - const SkISize minSize = SkISize::Make(1, 1); - const SkISize maxSize = SkISize::Make(4096, 4096); - // Connect and set native window to default configurations. if (!ConnectAndSetWindowDefaults(window)) { return nullptr; @@ -144,7 +127,7 @@ VulkanSurface* VulkanSurface::Create(ANativeWindow* window, ColorMode colorMode, // Initialize WindowInfo struct. WindowInfo windowInfo; if (!InitializeWindowInfoStruct(window, colorMode, colorType, colorSpace, vkManager, - extraBuffers, minSize, maxSize, &windowInfo)) { + extraBuffers, &windowInfo)) { return nullptr; } @@ -153,15 +136,14 @@ VulkanSurface* VulkanSurface::Create(ANativeWindow* window, ColorMode colorMode, return nullptr; } - return new VulkanSurface(window, windowInfo, minSize, maxSize, grContext); + return new VulkanSurface(window, windowInfo, grContext); } bool VulkanSurface::InitializeWindowInfoStruct(ANativeWindow* window, ColorMode colorMode, SkColorType colorType, sk_sp colorSpace, const VulkanManager& vkManager, - uint32_t extraBuffers, const SkISize& minSize, - const SkISize& maxSize, WindowInfo* outWindowInfo) { + uint32_t extraBuffers, WindowInfo* outWindowInfo) { ATRACE_CALL(); int width, height; @@ -185,7 +167,13 @@ bool VulkanSurface::InitializeWindowInfoStruct(ANativeWindow* window, ColorMode } outWindowInfo->transform = query_value; - ComputeWindowSizeAndTransform(outWindowInfo, minSize, maxSize); + outWindowInfo->actualSize = outWindowInfo->size; + if (outWindowInfo->transform & NATIVE_WINDOW_TRANSFORM_ROT_90) { + outWindowInfo->actualSize.set(outWindowInfo->size.height(), outWindowInfo->size.width()); + } + + outWindowInfo->preTransform = + GetPreTransformMatrix(outWindowInfo->size, outWindowInfo->transform); err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value); if (err != 0 || query_value < 0) { @@ -290,15 +278,6 @@ bool VulkanSurface::UpdateWindow(ANativeWindow* window, const WindowInfo& window return false; } - const SkISize& size = windowInfo.actualSize; - err = native_window_set_buffers_dimensions(window, size.width(), size.height()); - if (err != 0) { - ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_dimensions(%d,%d) " - "failed: %s (%d)", - size.width(), size.height(), strerror(-err), err); - return false; - } - // native_window_set_buffers_transform() expects the transform the app is requesting that // the compositor perform during composition. With native windows, pre-transform works by // rendering with the same transform the compositor is applying (as in Vulkan), but @@ -331,12 +310,8 @@ bool VulkanSurface::UpdateWindow(ANativeWindow* window, const WindowInfo& window } VulkanSurface::VulkanSurface(ANativeWindow* window, const WindowInfo& windowInfo, - SkISize minWindowSize, SkISize maxWindowSize, GrContext* grContext) - : mNativeWindow(window) - , mWindowInfo(windowInfo) - , mGrContext(grContext) - , mMinWindowSize(minWindowSize) - , mMaxWindowSize(maxWindowSize) {} + GrContext* grContext) + : mNativeWindow(window), mWindowInfo(windowInfo), mGrContext(grContext) {} VulkanSurface::~VulkanSurface() { releaseBuffers(); @@ -379,56 +354,49 @@ VulkanSurface::NativeBufferInfo* VulkanSurface::dequeueNativeBuffer() { // value at the end of the function if everything dequeued correctly. mCurrentBufferInfo = nullptr; - // check if the native window has been resized or rotated and update accordingly - SkISize newSize = SkISize::MakeEmpty(); + // Query the transform hint synced from the initial Surface connect or last queueBuffer. The + // auto prerotation on the buffer is based on the same transform hint in use by the producer. int transformHint = 0; - mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_WIDTH, &newSize.fWidth); - mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_HEIGHT, &newSize.fHeight); - mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_TRANSFORM_HINT, &transformHint); - if (newSize != mWindowInfo.actualSize || transformHint != mWindowInfo.transform) { - WindowInfo newWindowInfo = mWindowInfo; - newWindowInfo.size = newSize; - newWindowInfo.transform = IsTransformSupported(transformHint) ? transformHint : 0; - ComputeWindowSizeAndTransform(&newWindowInfo, mMinWindowSize, mMaxWindowSize); - - int err = 0; - if (newWindowInfo.actualSize != mWindowInfo.actualSize) { - // reset the native buffers and update the window - err = native_window_set_buffers_dimensions(mNativeWindow.get(), - newWindowInfo.actualSize.width(), - newWindowInfo.actualSize.height()); - if (err != 0) { - ALOGE("native_window_set_buffers_dimensions(%d,%d) failed: %s (%d)", - newWindowInfo.actualSize.width(), newWindowInfo.actualSize.height(), - strerror(-err), err); - return nullptr; - } + int err = + mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_TRANSFORM_HINT, &transformHint); + + // Since auto pre-rotation is enabled, dequeueBuffer to get the consumer driven buffer size + // from ANativeWindowBuffer. + ANativeWindowBuffer* buffer; + int fence_fd; + err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buffer, &fence_fd); + if (err != 0) { + ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err); + return nullptr; + } + + SkISize actualSize = SkISize::Make(buffer->width, buffer->height); + if (actualSize != mWindowInfo.actualSize || transformHint != mWindowInfo.transform) { + if (actualSize != mWindowInfo.actualSize) { // reset the NativeBufferInfo (including SkSurface) associated with the old buffers. The // new NativeBufferInfo storage will be populated lazily as we dequeue each new buffer. + mWindowInfo.actualSize = actualSize; releaseBuffers(); - // TODO should we ask the nativewindow to allocate buffers? } - if (newWindowInfo.transform != mWindowInfo.transform) { + if (transformHint != mWindowInfo.transform) { err = native_window_set_buffers_transform(mNativeWindow.get(), - InvertTransform(newWindowInfo.transform)); + InvertTransform(transformHint)); if (err != 0) { - ALOGE("native_window_set_buffers_transform(%d) failed: %s (%d)", - newWindowInfo.transform, strerror(-err), err); - newWindowInfo.transform = mWindowInfo.transform; - ComputeWindowSizeAndTransform(&newWindowInfo, mMinWindowSize, mMaxWindowSize); + ALOGE("native_window_set_buffers_transform(%d) failed: %s (%d)", transformHint, + strerror(-err), err); + mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer, fence_fd); + return nullptr; } + mWindowInfo.transform = transformHint; } - mWindowInfo = newWindowInfo; - } + mWindowInfo.size = actualSize; + if (mWindowInfo.transform & NATIVE_WINDOW_TRANSFORM_ROT_90) { + mWindowInfo.size.set(actualSize.height(), actualSize.width()); + } - ANativeWindowBuffer* buffer; - int fence_fd; - int err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buffer, &fence_fd); - if (err != 0) { - ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err); - return nullptr; + mWindowInfo.preTransform = GetPreTransformMatrix(mWindowInfo.size, mWindowInfo.transform); } uint32_t idx; diff --git a/libs/hwui/renderthread/VulkanSurface.h b/libs/hwui/renderthread/VulkanSurface.h index 54007e771999..5fa860a20916 100644 --- a/libs/hwui/renderthread/VulkanSurface.h +++ b/libs/hwui/renderthread/VulkanSurface.h @@ -101,16 +101,12 @@ private: SkMatrix preTransform; }; - VulkanSurface(ANativeWindow* window, const WindowInfo& windowInfo, SkISize minWindowSize, - SkISize maxWindowSize, GrContext* grContext); + VulkanSurface(ANativeWindow* window, const WindowInfo& windowInfo, GrContext* grContext); static bool InitializeWindowInfoStruct(ANativeWindow* window, ColorMode colorMode, SkColorType colorType, sk_sp colorSpace, const VulkanManager& vkManager, uint32_t extraBuffers, - const SkISize& minSize, const SkISize& maxSize, WindowInfo* outWindowInfo); static bool UpdateWindow(ANativeWindow* window, const WindowInfo& windowInfo); - static void ComputeWindowSizeAndTransform(WindowInfo* windowInfo, const SkISize& minSize, - const SkISize& maxSize); void releaseBuffers(); // TODO: Just use a vector? @@ -122,11 +118,8 @@ private: uint32_t mPresentCount = 0; NativeBufferInfo* mCurrentBufferInfo = nullptr; - - const SkISize mMinWindowSize; - const SkISize mMaxWindowSize; }; } /* namespace renderthread */ } /* namespace uirenderer */ -} /* namespace android */ \ No newline at end of file +} /* namespace android */ -- cgit v1.2.3 From 34a2576340edeae79d63d78647260de85ba3398e Mon Sep 17 00:00:00 2001 From: Fedor Kudasov Date: Fri, 28 Jun 2019 21:53:56 +0100 Subject: Split out RootRenderNode - Deleted unused OnFinishedEvent, InvokeAnimationListeners - Hid FinishAndInvokeListener and AnimationContextBridge in the implmentation Bug: 117921091 Test: all tests should pass Change-Id: I98af836b27d06907a24696fcae03f24808c1993a --- libs/hwui/Android.bp | 1 + libs/hwui/RootRenderNode.cpp | 284 +++++++++++++++++++++++++++++++++++++++++++ libs/hwui/RootRenderNode.h | 89 ++++++++++++++ libs/hwui/TreeInfo.h | 1 - 4 files changed, 374 insertions(+), 1 deletion(-) create mode 100644 libs/hwui/RootRenderNode.cpp create mode 100644 libs/hwui/RootRenderNode.h (limited to 'libs') diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 5814bb70475c..bfead1a70fab 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -250,6 +250,7 @@ cc_defaults { "ProfileData.cpp", "ProfileDataContainer.cpp", "Readback.cpp", + "RootRenderNode.cpp", "TreeInfo.cpp", "WebViewFunctorManager.cpp", "protos/graphicsstats.proto", diff --git a/libs/hwui/RootRenderNode.cpp b/libs/hwui/RootRenderNode.cpp new file mode 100644 index 000000000000..d8c1b57e2ef4 --- /dev/null +++ b/libs/hwui/RootRenderNode.cpp @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2019 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 "RootRenderNode.h" + +namespace android::uirenderer { + +class FinishAndInvokeListener : public MessageHandler { +public: + explicit FinishAndInvokeListener(PropertyValuesAnimatorSet* anim) : mAnimator(anim) { + mListener = anim->getOneShotListener(); + mRequestId = anim->getRequestId(); + } + + virtual void handleMessage(const Message& message) { + if (mAnimator->getRequestId() == mRequestId) { + // Request Id has not changed, meaning there's no animation lifecyle change since the + // message is posted, so go ahead and call finish to make sure the PlayState is properly + // updated. This is needed because before the next frame comes in from UI thread to + // trigger an animation update, there could be reverse/cancel etc. So we need to update + // the playstate in time to ensure all the subsequent events get chained properly. + mAnimator->end(); + } + mListener->onAnimationFinished(nullptr); + } + +private: + sp mAnimator; + sp mListener; + uint32_t mRequestId; +}; + +void RootRenderNode::prepareTree(TreeInfo& info) { + info.errorHandler = mErrorHandler.get(); + + for (auto& anim : mRunningVDAnimators) { + // Assume that the property change in VD from the animators will not be consumed. Mark + // otherwise if the VDs are found in the display list tree. For VDs that are not in + // the display list tree, we stop providing animation pulses by 1) removing them from + // the animation list, 2) post a delayed message to end them at end time so their + // listeners can receive the corresponding callbacks. + anim->getVectorDrawable()->setPropertyChangeWillBeConsumed(false); + // Mark the VD dirty so it will damage itself during prepareTree. + anim->getVectorDrawable()->markDirty(); + } + if (info.mode == TreeInfo::MODE_FULL) { + for (auto& anim : mPausedVDAnimators) { + anim->getVectorDrawable()->setPropertyChangeWillBeConsumed(false); + anim->getVectorDrawable()->markDirty(); + } + } + // TODO: This is hacky + info.updateWindowPositions = true; + RenderNode::prepareTree(info); + info.updateWindowPositions = false; + info.errorHandler = nullptr; +} + +void RootRenderNode::attachAnimatingNode(RenderNode* animatingNode) { + mPendingAnimatingRenderNodes.push_back(animatingNode); +} + +void RootRenderNode::attachPendingVectorDrawableAnimators() { + mRunningVDAnimators.insert(mPendingVectorDrawableAnimators.begin(), + mPendingVectorDrawableAnimators.end()); + mPendingVectorDrawableAnimators.clear(); +} + +void RootRenderNode::detachAnimators() { + // Remove animators from the list and post a delayed message in future to end the animator + // For infinite animators, remove the listener so we no longer hold a global ref to the AVD + // java object, and therefore the AVD objects in both native and Java can be properly + // released. + for (auto& anim : mRunningVDAnimators) { + detachVectorDrawableAnimator(anim.get()); + anim->clearOneShotListener(); + } + for (auto& anim : mPausedVDAnimators) { + anim->clearOneShotListener(); + } + mRunningVDAnimators.clear(); + mPausedVDAnimators.clear(); +} + +// Move all the animators to the paused list, and send a delayed message to notify the finished +// listener. +void RootRenderNode::pauseAnimators() { + mPausedVDAnimators.insert(mRunningVDAnimators.begin(), mRunningVDAnimators.end()); + for (auto& anim : mRunningVDAnimators) { + detachVectorDrawableAnimator(anim.get()); + } + mRunningVDAnimators.clear(); +} + +void RootRenderNode::doAttachAnimatingNodes(AnimationContext* context) { + for (size_t i = 0; i < mPendingAnimatingRenderNodes.size(); i++) { + RenderNode* node = mPendingAnimatingRenderNodes[i].get(); + context->addAnimatingRenderNode(*node); + } + mPendingAnimatingRenderNodes.clear(); +} + +// Run VectorDrawable animators after prepareTree. +void RootRenderNode::runVectorDrawableAnimators(AnimationContext* context, TreeInfo& info) { + // Push staging. + if (info.mode == TreeInfo::MODE_FULL) { + pushStagingVectorDrawableAnimators(context); + } + + // Run the animators in the running list. + for (auto it = mRunningVDAnimators.begin(); it != mRunningVDAnimators.end();) { + if ((*it)->animate(*context)) { + it = mRunningVDAnimators.erase(it); + } else { + it++; + } + } + + // Run the animators in paused list during full sync. + if (info.mode == TreeInfo::MODE_FULL) { + // During full sync we also need to pulse paused animators, in case their targets + // have been added back to the display list. All the animators that passed the + // scheduled finish time will be removed from the paused list. + for (auto it = mPausedVDAnimators.begin(); it != mPausedVDAnimators.end();) { + if ((*it)->animate(*context)) { + // Animator has finished, remove from the list. + it = mPausedVDAnimators.erase(it); + } else { + it++; + } + } + } + + // Move the animators with a target not in DisplayList to paused list. + for (auto it = mRunningVDAnimators.begin(); it != mRunningVDAnimators.end();) { + if (!(*it)->getVectorDrawable()->getPropertyChangeWillBeConsumed()) { + // Vector Drawable is not in the display list, we should remove this animator from + // the list, put it in the paused list, and post a delayed message to end the + // animator. + detachVectorDrawableAnimator(it->get()); + mPausedVDAnimators.insert(*it); + it = mRunningVDAnimators.erase(it); + } else { + it++; + } + } + + // Move the animators with a target in DisplayList from paused list to running list, and + // trim paused list. + if (info.mode == TreeInfo::MODE_FULL) { + // Check whether any paused animator's target is back in Display List. If so, put the + // animator back in the running list. + for (auto it = mPausedVDAnimators.begin(); it != mPausedVDAnimators.end();) { + if ((*it)->getVectorDrawable()->getPropertyChangeWillBeConsumed()) { + mRunningVDAnimators.insert(*it); + it = mPausedVDAnimators.erase(it); + } else { + it++; + } + } + // Trim paused VD animators at full sync, so that when Java loses reference to an + // animator, we know we won't be requested to animate it any more, then we remove such + // animators from the paused list so they can be properly freed. We also remove the + // animators from paused list when the time elapsed since start has exceeded duration. + trimPausedVDAnimators(context); + } + + info.out.hasAnimations |= !mRunningVDAnimators.empty(); +} + +void RootRenderNode::trimPausedVDAnimators(AnimationContext* context) { + // Trim paused vector drawable animator list. + for (auto it = mPausedVDAnimators.begin(); it != mPausedVDAnimators.end();) { + // Remove paused VD animator if no one else is referencing it. Note that animators that + // have passed scheduled finish time are removed from list when they are being pulsed + // before prepare tree. + // TODO: this is a bit hacky, need to figure out a better way to track when the paused + // animators should be freed. + if ((*it)->getStrongCount() == 1) { + it = mPausedVDAnimators.erase(it); + } else { + it++; + } + } +} + +void RootRenderNode::pushStagingVectorDrawableAnimators(AnimationContext* context) { + for (auto& anim : mRunningVDAnimators) { + anim->pushStaging(*context); + } +} + +void RootRenderNode::destroy() { + for (auto& renderNode : mPendingAnimatingRenderNodes) { + renderNode->animators().endAllStagingAnimators(); + } + mPendingAnimatingRenderNodes.clear(); + mPendingVectorDrawableAnimators.clear(); +} + +void RootRenderNode::addVectorDrawableAnimator(PropertyValuesAnimatorSet* anim) { + mPendingVectorDrawableAnimators.insert(anim); +} + +void RootRenderNode::detachVectorDrawableAnimator(PropertyValuesAnimatorSet* anim) { + if (anim->isInfinite() || !anim->isRunning()) { + // Do not need to post anything if the animation is infinite (i.e. no meaningful + // end listener action), or if the animation has already ended. + return; + } + nsecs_t remainingTimeInMs = anim->getRemainingPlayTime(); + // Post a delayed onFinished event that is scheduled to be handled when the animator ends. + if (anim->getOneShotListener()) { + // VectorDrawable's oneshot listener is updated when there are user triggered animation + // lifecycle changes, such as start(), end(), etc. By using checking and clearing + // one shot listener, we ensure the same end listener event gets posted only once. + // Therefore no duplicates. Another benefit of using one shot listener is that no + // removal is necessary: the end time of animation will not change unless triggered by + // user events, in which case the already posted listener's id will become stale, and + // the onFinished callback will then be ignored. + sp message = new FinishAndInvokeListener(anim); + auto looper = Looper::getForThread(); + LOG_ALWAYS_FATAL_IF(looper == nullptr, "Not on a looper thread?"); + looper->sendMessageDelayed(ms2ns(remainingTimeInMs), message, 0); + anim->clearOneShotListener(); + } +} + +class AnimationContextBridge : public AnimationContext { +public: + AnimationContextBridge(renderthread::TimeLord& clock, RootRenderNode* rootNode) + : AnimationContext(clock), mRootNode(rootNode) {} + + virtual ~AnimationContextBridge() {} + + // Marks the start of a frame, which will update the frame time and move all + // next frame animations into the current frame + virtual void startFrame(TreeInfo::TraversalMode mode) { + if (mode == TreeInfo::MODE_FULL) { + mRootNode->doAttachAnimatingNodes(this); + mRootNode->attachPendingVectorDrawableAnimators(); + } + AnimationContext::startFrame(mode); + } + + // Runs any animations still left in mCurrentFrameAnimations + virtual void runRemainingAnimations(TreeInfo& info) { + AnimationContext::runRemainingAnimations(info); + mRootNode->runVectorDrawableAnimators(this, info); + } + + virtual void pauseAnimators() override { mRootNode->pauseAnimators(); } + + virtual void callOnFinished(BaseRenderNodeAnimator* animator, AnimationListener* listener) { + listener->onAnimationFinished(animator); + } + + virtual void destroy() { + AnimationContext::destroy(); + mRootNode->detachAnimators(); + } + +private: + sp mRootNode; +}; + +AnimationContext* ContextFactoryImpl::createAnimationContext(renderthread::TimeLord& clock) { + return new AnimationContextBridge(clock, mRootNode); +} + +} // namespace android::uirenderer diff --git a/libs/hwui/RootRenderNode.h b/libs/hwui/RootRenderNode.h new file mode 100644 index 000000000000..ea10921be20b --- /dev/null +++ b/libs/hwui/RootRenderNode.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2019 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. + */ + +#pragma once + +#include + +#include +#include + +#include "AnimationContext.h" +#include "Animator.h" +#include "PropertyValuesAnimatorSet.h" +#include "RenderNode.h" + +namespace android::uirenderer { + +class ANDROID_API RootRenderNode : public RenderNode { +public: + ANDROID_API explicit RootRenderNode(std::unique_ptr errorHandler) + : RenderNode(), mErrorHandler(std::move(errorHandler)) {} + + ANDROID_API virtual ~RootRenderNode() {} + + virtual void prepareTree(TreeInfo& info) override; + + ANDROID_API void attachAnimatingNode(RenderNode* animatingNode); + + void attachPendingVectorDrawableAnimators(); + + void detachAnimators(); + + void pauseAnimators(); + + void doAttachAnimatingNodes(AnimationContext* context); + + // Run VectorDrawable animators after prepareTree. + void runVectorDrawableAnimators(AnimationContext* context, TreeInfo& info); + + void trimPausedVDAnimators(AnimationContext* context); + + void pushStagingVectorDrawableAnimators(AnimationContext* context); + + ANDROID_API void destroy(); + + ANDROID_API void addVectorDrawableAnimator(PropertyValuesAnimatorSet* anim); + +private: + const std::unique_ptr mErrorHandler; + std::vector > mPendingAnimatingRenderNodes; + std::set > mPendingVectorDrawableAnimators; + std::set > mRunningVDAnimators; + // mPausedVDAnimators stores a list of animators that have not yet passed the finish time, but + // their VectorDrawable targets are no longer in the DisplayList. We skip these animators when + // render thread runs animators independent of UI thread (i.e. RT_ONLY mode). These animators + // need to be re-activated once their VD target is added back into DisplayList. Since that could + // only happen when we do a full sync, we need to make sure to pulse these paused animators at + // full sync. If any animator's VD target is found in DisplayList during a full sync, we move + // the animator back to the running list. + std::set > mPausedVDAnimators; + + void detachVectorDrawableAnimator(PropertyValuesAnimatorSet* anim); +}; + +class ANDROID_API ContextFactoryImpl : public IContextFactory { +public: + ANDROID_API explicit ContextFactoryImpl(RootRenderNode* rootNode) : mRootNode(rootNode) {} + + ANDROID_API virtual AnimationContext* createAnimationContext( + renderthread::TimeLord& clock) override; + +private: + RootRenderNode* mRootNode; +}; + +} // namespace android::uirenderer diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h index 7e8d12fd4597..f2481f83767d 100644 --- a/libs/hwui/TreeInfo.h +++ b/libs/hwui/TreeInfo.h @@ -40,7 +40,6 @@ class ErrorHandler { public: virtual void onError(const std::string& message) = 0; -protected: virtual ~ErrorHandler() = default; }; -- cgit v1.2.3 From bc409cf19001182301d81f7725988992393832e9 Mon Sep 17 00:00:00 2001 From: Fedor Kudasov Date: Wed, 3 Jul 2019 13:56:56 +0100 Subject: Rearrange some headers Bug: 117921091 Test: all tests should pass Change-Id: I6ee108df6ec1de8deabfb5ec6b0e8b6e76a940b1 --- libs/hwui/RootRenderNode.cpp | 2 ++ libs/hwui/RootRenderNode.h | 3 +-- libs/hwui/renderthread/RenderProxy.cpp | 2 -- 3 files changed, 3 insertions(+), 4 deletions(-) (limited to 'libs') diff --git a/libs/hwui/RootRenderNode.cpp b/libs/hwui/RootRenderNode.cpp index d8c1b57e2ef4..24801928121b 100644 --- a/libs/hwui/RootRenderNode.cpp +++ b/libs/hwui/RootRenderNode.cpp @@ -16,6 +16,8 @@ #include "RootRenderNode.h" +#include + namespace android::uirenderer { class FinishAndInvokeListener : public MessageHandler { diff --git a/libs/hwui/RootRenderNode.h b/libs/hwui/RootRenderNode.h index ea10921be20b..5c830e08ef68 100644 --- a/libs/hwui/RootRenderNode.h +++ b/libs/hwui/RootRenderNode.h @@ -16,13 +16,12 @@ #pragma once -#include - #include #include #include "AnimationContext.h" #include "Animator.h" +#include #include "PropertyValuesAnimatorSet.h" #include "RenderNode.h" diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 3dd1d4462d02..e50428b2ee62 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -30,8 +30,6 @@ #include "utils/TimeUtils.h" #include "utils/TraceUtils.h" -#include - namespace android { namespace uirenderer { namespace renderthread { -- cgit v1.2.3 From 666f8a5101475bdec7cc3d204e245879a2a15c0a Mon Sep 17 00:00:00 2001 From: Fedor Kudasov Date: Wed, 3 Jul 2019 15:21:14 +0100 Subject: Add more useful source files Bug: 117921091 Test: all tests should pass Change-Id: I2a7f53f985c060967e9eb3364b7f73b073eab1f9 --- libs/hwui/Android.bp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'libs') diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index bfead1a70fab..bf948b135728 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -165,6 +165,9 @@ cc_defaults { "pipeline/skia/SkiaRecordingCanvas.cpp", "pipeline/skia/RenderNodeDrawable.cpp", "pipeline/skia/ReorderBarrierDrawables.cpp", + "renderthread/Frame.cpp", + "renderthread/RenderTask.cpp", + "renderthread/TimeLord.cpp", "hwui/AnimatedImageDrawable.cpp", "hwui/AnimatedImageThread.cpp", "hwui/Bitmap.cpp", @@ -226,10 +229,7 @@ cc_defaults { "renderthread/VulkanManager.cpp", "renderthread/VulkanSurface.cpp", "renderthread/RenderProxy.cpp", - "renderthread/RenderTask.cpp", "renderthread/RenderThread.cpp", - "renderthread/TimeLord.cpp", - "renderthread/Frame.cpp", "service/GraphicsStatsService.cpp", "surfacetexture/EGLConsumer.cpp", "surfacetexture/ImageConsumer.cpp", -- cgit v1.2.3 From d2e49a29bbdec2ed4d7c09124e5fcc5f0acfbc96 Mon Sep 17 00:00:00 2001 From: Nathaniel Nifong Date: Mon, 24 Jun 2019 15:07:34 -0400 Subject: Introduce multi-frame SKP capturing in SkiaPipeline Capture script usage is the same as before, but all frames are combined into a single file which shares the images between frames, reducing the file size and serialization time somewhat. This brings us closer to the objective of realistic performance measurements, but to do that correctly, a second format is needed, (skbug.com/9174) Single frame captures still produce the same format. Test: The method used for serialization is tested in skia's DM unit tests, in MultiSkpTest.cpp Test: Tested manually with the libs/hwui/tests/scripts/skp-capture.sh script Bug: skbug.com/9210 Change-Id: I69da8d191640ebb444991f107d60389f1519a9db --- libs/hwui/pipeline/skia/SkiaPipeline.cpp | 152 ++++++++++++++++++++++++------- libs/hwui/pipeline/skia/SkiaPipeline.h | 50 +++++++++- libs/hwui/tests/scripts/skp-capture.sh | 70 ++++++++------ 3 files changed, 204 insertions(+), 68 deletions(-) (limited to 'libs') diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index ff29a5b9e352..84c0d1369e83 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -19,14 +19,17 @@ #include #include #include +#include #include #include #include #include +#include #include "LightingInfo.h" #include "TreeInfo.h" #include "VectorDrawable.h" #include "thread/CommonPool.h" +#include "tools/SkSharingProc.h" #include "utils/TraceUtils.h" #include @@ -99,7 +102,7 @@ void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque) SkASSERT(layerNode->getLayerSurface()); SkiaDisplayList* displayList = (SkiaDisplayList*)layerNode->getDisplayList(); if (!displayList || displayList->isEmpty()) { - SkDEBUGF(("%p drawLayers(%s) : missing drawable", layerNode, layerNode->getName())); + ALOGE("%p drawLayers(%s) : missing drawable", layerNode, layerNode->getName()); return; } @@ -233,58 +236,138 @@ static void savePictureAsync(const sk_sp& data, const std::string& filen if (stream.isValid()) { stream.write(data->data(), data->size()); stream.flush(); - SkDebugf("SKP Captured Drawing Output (%d bytes) for frame. %s", stream.bytesWritten(), + ALOGD("SKP Captured Drawing Output (%zu bytes) for frame. %s", stream.bytesWritten(), filename.c_str()); } }); } -SkCanvas* SkiaPipeline::tryCapture(SkSurface* surface) { - if (CC_UNLIKELY(Properties::skpCaptureEnabled)) { +// Note multiple SkiaPipeline instances may be loaded if more than one app is visible. +// Each instance may observe the filename changing and try to record to a file of the same name. +// Only the first one will succeed. There is no scope available here where we could coordinate +// to cause this function to return true for only one of the instances. +bool SkiaPipeline::shouldStartNewFileCapture() { + // Don't start a new file based capture if one is currently ongoing. + if (mCaptureMode != CaptureMode::None) { return false; } + + // A new capture is started when the filename property changes. + // Read the filename property. + std::string prop = base::GetProperty(PROPERTY_CAPTURE_SKP_FILENAME, "0"); + // if the filename property changed to a valid value + if (prop[0] != '0' && mCapturedFile != prop) { + // remember this new filename + mCapturedFile = prop; + // and get a property indicating how many frames to capture. + mCaptureSequence = base::GetIntProperty(PROPERTY_CAPTURE_SKP_FRAMES, 1); if (mCaptureSequence <= 0) { - std::string prop = base::GetProperty(PROPERTY_CAPTURE_SKP_FILENAME, "0"); - if (prop[0] != '0' && mCapturedFile != prop) { - mCapturedFile = prop; - mCaptureSequence = base::GetIntProperty(PROPERTY_CAPTURE_SKP_FRAMES, 1); - } + return false; + } else if (mCaptureSequence == 1) { + mCaptureMode = CaptureMode::SingleFrameSKP; + } else { + mCaptureMode = CaptureMode::MultiFrameSKP; } - if (mCaptureSequence > 0 || mPictureCapturedCallback) { - mRecorder.reset(new SkPictureRecorder()); - SkCanvas* pictureCanvas = - mRecorder->beginRecording(surface->width(), surface->height(), nullptr, - SkPictureRecorder::kPlaybackDrawPicture_RecordFlag); - mNwayCanvas = std::make_unique(surface->width(), surface->height()); - mNwayCanvas->addCanvas(surface->getCanvas()); - mNwayCanvas->addCanvas(pictureCanvas); - return mNwayCanvas.get(); + return true; + } + return false; +} + +// performs the first-frame work of a multi frame SKP capture. Returns true if successful. +bool SkiaPipeline::setupMultiFrameCapture() { + ALOGD("Set up multi-frame capture, frames = %d", mCaptureSequence); + // We own this stream and need to hold it until close() finishes. + auto stream = std::make_unique(mCapturedFile.c_str()); + if (stream->isValid()) { + mOpenMultiPicStream = std::move(stream); + mSerialContext.reset(new SkSharingSerialContext()); + SkSerialProcs procs; + procs.fImageProc = SkSharingSerialContext::serializeImage; + procs.fImageCtx = mSerialContext.get(); + // SkDocuments don't take owership of the streams they write. + // we need to keep it until after mMultiPic.close() + // procs is passed as a pointer, but just as a method of having an optional default. + // procs doesn't need to outlive this Make call. + mMultiPic = SkMakeMultiPictureDocument(mOpenMultiPicStream.get(), &procs); + return true; + } else { + ALOGE("Could not open \"%s\" for writing.", mCapturedFile.c_str()); + mCaptureSequence = 0; + mCaptureMode = CaptureMode::None; + return false; + } +} + +SkCanvas* SkiaPipeline::tryCapture(SkSurface* surface) { + if (CC_LIKELY(!Properties::skpCaptureEnabled)) { + return surface->getCanvas(); // Bail out early when capture is not turned on. + } + // Note that shouldStartNewFileCapture tells us if this is the *first* frame of a capture. + if (shouldStartNewFileCapture() && mCaptureMode == CaptureMode::MultiFrameSKP) { + if (!setupMultiFrameCapture()) { + return surface->getCanvas(); } } - return surface->getCanvas(); + + // Create a canvas pointer, fill it depending on what kind of capture is requested (if any) + SkCanvas* pictureCanvas = nullptr; + switch (mCaptureMode) { + case CaptureMode::CallbackAPI: + case CaptureMode::SingleFrameSKP: + mRecorder.reset(new SkPictureRecorder()); + pictureCanvas = mRecorder->beginRecording(surface->width(), surface->height(), + nullptr, SkPictureRecorder::kPlaybackDrawPicture_RecordFlag); + break; + case CaptureMode::MultiFrameSKP: + // If a multi frame recording is active, initialize recording for a single frame of a + // multi frame file. + pictureCanvas = mMultiPic->beginPage(surface->width(), surface->height()); + break; + case CaptureMode::None: + // Returning here in the non-capture case means we can count on pictureCanvas being + // non-null below. + return surface->getCanvas(); + } + + // Setting up an nway canvas is common to any kind of capture. + mNwayCanvas = std::make_unique(surface->width(), surface->height()); + mNwayCanvas->addCanvas(surface->getCanvas()); + mNwayCanvas->addCanvas(pictureCanvas); + return mNwayCanvas.get(); } void SkiaPipeline::endCapture(SkSurface* surface) { + if (CC_LIKELY(mCaptureMode == CaptureMode::None)) { return; } mNwayCanvas.reset(); - if (CC_UNLIKELY(mRecorder.get())) { - ATRACE_CALL(); + ATRACE_CALL(); + if (mCaptureSequence > 0 && mCaptureMode == CaptureMode::MultiFrameSKP) { + mMultiPic->endPage(); + mCaptureSequence--; + if (mCaptureSequence == 0) { + mCaptureMode = CaptureMode::None; + // Pass mMultiPic and mOpenMultiPicStream to a background thread, which will handle + // the heavyweight serialization work and destroy them. mOpenMultiPicStream is released + // to a bare pointer because keeping it in a smart pointer makes the lambda + // non-copyable. The lambda is only called once, so this is safe. + SkFILEWStream* stream = mOpenMultiPicStream.release(); + CommonPool::post([doc = std::move(mMultiPic), stream]{ + ALOGD("Finalizing multi frame SKP"); + doc->close(); + delete stream; + ALOGD("Multi frame SKP complete."); + }); + } + } else { sk_sp picture = mRecorder->finishRecordingAsPicture(); if (picture->approximateOpCount() > 0) { - if (mCaptureSequence > 0) { - ATRACE_BEGIN("picture->serialize"); - auto data = picture->serialize(); - ATRACE_END(); - - // offload saving to file in a different thread - if (1 == mCaptureSequence) { - savePictureAsync(data, mCapturedFile); - } else { - savePictureAsync(data, mCapturedFile + "_" + std::to_string(mCaptureSequence)); - } - mCaptureSequence--; - } if (mPictureCapturedCallback) { std::invoke(mPictureCapturedCallback, std::move(picture)); + } else { + // single frame skp to file + auto data = picture->serialize(); + savePictureAsync(data, mCapturedFile); + mCaptureSequence = 0; } } + mCaptureMode = CaptureMode::None; mRecorder.reset(); } } @@ -305,7 +388,6 @@ void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& cli // initialize the canvas for the current frame, that might be a recording canvas if SKP // capture is enabled. - std::unique_ptr recorder; SkCanvas* canvas = tryCapture(surface.get()); renderFrameImpl(layers, clip, nodes, opaque, contentDrawBounds, canvas, preTransform); diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h index 5fc1d6169d4a..37b559f92f05 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaPipeline.h @@ -17,12 +17,15 @@ #pragma once #include +#include +#include #include "Lighting.h" #include "hwui/AnimatedImageDrawable.h" #include "renderthread/CanvasContext.h" #include "renderthread/IRenderPipeline.h" class SkPictureRecorder; +struct SkSharingSerialContext; namespace android { namespace uirenderer { @@ -60,9 +63,12 @@ public: void renderLayersImpl(const LayerUpdateQueue& layers, bool opaque); + // Sets the recording callback to the provided function and the recording mode + // to CallbackAPI void setPictureCapturedCallback( const std::function&&)>& callback) override { mPictureCapturedCallback = callback; + mCaptureMode = callback ? CaptureMode::CallbackAPI : CaptureMode::None; } protected: @@ -92,8 +98,18 @@ private: */ void renderVectorDrawableCache(); + // Called every frame. Normally returns early with screen canvas. + // But when capture is enabled, returns an nwaycanvas where commands are also recorded. SkCanvas* tryCapture(SkSurface* surface); + // Called at the end of every frame, closes the recording if necessary. void endCapture(SkSurface* surface); + // Determine if a new file-based capture should be started. + // If so, sets mCapturedFile and mCaptureSequence and returns true. + // Should be called every frame when capture is enabled. + // sets mCaptureMode. + bool shouldStartNewFileCapture(); + // Set up a multi frame capture. + bool setupMultiFrameCapture(); std::vector> mPinnedImages; @@ -103,22 +119,46 @@ private: std::vector mVectorDrawables; // Block of properties used only for debugging to record a SkPicture and save it in a file. + // There are three possible ways of recording drawing commands. + enum class CaptureMode { + // return to this mode when capture stops. + None, + // A mode where every frame is recorded into an SkPicture and sent to a provided callback, + // until that callback is cleared + CallbackAPI, + // A mode where a finite number of frames are recorded to a file with + // SkMultiPictureDocument + MultiFrameSKP, + // A mode which records a single frame to a normal SKP file. + SingleFrameSKP, + }; + CaptureMode mCaptureMode = CaptureMode::None; + /** - * mCapturedFile is used to enforce we don't capture more than once for a given name (cause - * permissions don't allow to reset a property from render thread). + * mCapturedFile - the filename to write a recorded SKP to in either MultiFrameSKP or + * SingleFrameSKP mode. */ std::string mCapturedFile; /** - * mCaptureSequence counts how many frames are left to take in the sequence. + * mCaptureSequence counts down how many frames are left to take in the sequence. Applicable + * only to MultiFrameSKP or SingleFrameSKP mode. */ int mCaptureSequence = 0; + // Multi frame serialization stream and writer used when serializing more than one frame. + std::unique_ptr mOpenMultiPicStream; + sk_sp mMultiPic; + std::unique_ptr mSerialContext; + /** - * mRecorder holds the current picture recorder. We could store it on the stack to support - * parallel tryCapture calls (not really needed). + * mRecorder holds the current picture recorder when serializing in either SingleFrameSKP or + * CallbackAPI modes. */ std::unique_ptr mRecorder; std::unique_ptr mNwayCanvas; + + // Set by setPictureCapturedCallback and when set, CallbackAPI mode recording is ongoing. + // Not used in other recording modes. std::function&&)> mPictureCapturedCallback; }; diff --git a/libs/hwui/tests/scripts/skp-capture.sh b/libs/hwui/tests/scripts/skp-capture.sh index 54fa22929586..aad31fcc8eb9 100755 --- a/libs/hwui/tests/scripts/skp-capture.sh +++ b/libs/hwui/tests/scripts/skp-capture.sh @@ -4,6 +4,12 @@ # # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +# +# Before this can be used, the device must be rooted and the filesystem must be writable by Skia +# - These steps are necessary once after flashing to enable capture - +# adb root +# adb remount +# adb reboot if [ -z "$1" ]; then printf 'Usage:\n skp-capture.sh PACKAGE_NAME OPTIONAL_FRAME_COUNT\n\n' @@ -20,8 +26,8 @@ if ! command -v adb > /dev/null 2>&1; then exit 2 fi fi -phase1_timeout_seconds=15 -phase2_timeout_seconds=60 +phase1_timeout_seconds=60 +phase2_timeout_seconds=300 package="$1" filename="$(date '+%H%M%S').skp" remote_path="/data/data/${package}/cache/${filename}" @@ -29,11 +35,14 @@ local_path_prefix="$(date '+%Y-%m-%d_%H%M%S')_${package}" local_path="${local_path_prefix}.skp" enable_capture_key='debug.hwui.capture_skp_enabled' enable_capture_value=$(adb shell "getprop '${enable_capture_key}'") -#printf 'captureflag=' "$enable_capture_value" '\n' + +# TODO(nifong): check if filesystem is writable here with "avbctl get-verity" +# result will either start with "verity is disabled" or "verity is enabled" + if [ -z "$enable_capture_value" ]; then - printf 'Capture SKP property need to be enabled first. Please use\n' - printf "\"adb shell setprop debug.hwui.capture_skp_enabled true\" and then restart\n" - printf "the process.\n\n" + printf 'debug.hwui.capture_skp_enabled was found to be disabled, enabling it now.\n' + printf " restart the process you want to capture on the device, then retry this script.\n\n" + adb shell "setprop '${enable_capture_key}' true" exit 1 fi if [ ! -z "$2" ]; then @@ -57,12 +66,18 @@ banner() { printf ' %s' "$*" printf '\n=====================\n' } -banner '...WAITING...' -adb_test_exist() { - test '0' = "$(adb shell "test -e \"$1\"; echo \$?")"; +banner '...WAITING FOR APP INTERACTION...' +# Waiting for nonzero file is an indication that the pipeline has both opened the file and written +# the header. With multiple frames this does not occur until the last frame has been recorded, +# so we continue to show the "waiting for app interaction" message as long as the app still requires +# interaction to draw more frames. +adb_test_file_nonzero() { + # grab first byte of `du` output + X="$(adb shell "du \"$1\" 2> /dev/null | dd bs=1 count=1 2> /dev/null")" + test "$X" && test "$X" -ne 0 } timeout=$(( $(date +%s) + $phase1_timeout_seconds)) -while ! adb_test_exist "$remote_path"; do +while ! adb_test_file_nonzero "$remote_path"; do spin 0.05 if [ $(date +%s) -gt $timeout ] ; then printf '\bTimed out.\n' @@ -72,20 +87,27 @@ while ! adb_test_exist "$remote_path"; do done printf '\b' -#read -n1 -r -p "Press any key to continue..." key +# Disable further capturing +adb shell "setprop '${filename_key}' ''" banner '...SAVING...' -adb_test_file_nonzero() { - # grab first byte of `du` output - X="$(adb shell "du \"$1\" 2> /dev/null | dd bs=1 count=1 2> /dev/null")" - test "$X" && test "$X" -ne 0 +# return the size of a file in bytes +adb_filesize() { + adb shell "wc -c \"$1\"" 2> /dev/null | awk '{print $1}' } -#adb_filesize() { -# adb shell "wc -c \"$1\"" 2> /dev/null | awk '{print $1}' -#} timeout=$(( $(date +%s) + $phase2_timeout_seconds)) -while ! adb_test_file_nonzero "$remote_path"; do +last_size='0' # output of last size check command +unstable=true # false once the file size stops changing +counter=0 # used to perform size check only 1/sec though we update spinner 20/sec +# loop until the file size is unchanged for 1 second. +while [ $unstable != 0 ] ; do spin 0.05 + counter=$(( $counter+1 )) + if ! (( $counter % 20)) ; then + new_size=$(adb_filesize "$remote_path") + unstable=$(($(adb_filesize "$remote_path") != last_size)) + last_size=$new_size + fi if [ $(date +%s) -gt $timeout ] ; then printf '\bTimed out.\n' adb shell "setprop '${filename_key}' ''" @@ -94,7 +116,7 @@ while ! adb_test_file_nonzero "$remote_path"; do done printf '\b' -adb shell "setprop '${filename_key}' ''" +printf "SKP file serialized: %s\n" $(echo $last_size | numfmt --to=iec) i=0; while [ $i -lt 10 ]; do spin 0.10; i=$(($i + 1)); done; echo @@ -105,12 +127,4 @@ if ! [ -f "$local_path" ] ; then fi adb shell rm "$remote_path" printf '\nSKP saved to file:\n %s\n\n' "$local_path" -if [ ! -z "$2" ]; then - bridge="_" - adb shell "setprop 'debug.hwui.capture_skp_frames' ''" - for i in $(seq 2 $2); do - adb pull "${remote_path}_${i}" "${local_path_prefix}_${i}.skp" - adb shell rm "${remote_path}_${i}" - done -fi -- cgit v1.2.3 From 09cfce089d7539c2d727d2cd563471ef39eb163a Mon Sep 17 00:00:00 2001 From: Fedor Kudasov Date: Thu, 4 Jul 2019 09:41:13 +0100 Subject: Enable simplified version of RootRenderNode for host Bug: 117921091 Test: all tests should pass Change-Id: Iae6e5f37eb11cdcffe29336ce2eda6ab6897385c --- libs/hwui/Android.bp | 2 +- libs/hwui/Animator.cpp | 2 -- libs/hwui/RootRenderNode.cpp | 20 ++++++++++++++++++++ libs/hwui/RootRenderNode.h | 2 ++ 4 files changed, 23 insertions(+), 3 deletions(-) (limited to 'libs') diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index bf948b135728..0d837f2c7fed 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -195,6 +195,7 @@ cc_defaults { "RecordingCanvas.cpp", "RenderNode.cpp", "RenderProperties.cpp", + "RootRenderNode.cpp", "SkiaCanvas.cpp", "VectorDrawable.cpp", ], @@ -250,7 +251,6 @@ cc_defaults { "ProfileData.cpp", "ProfileDataContainer.cpp", "Readback.cpp", - "RootRenderNode.cpp", "TreeInfo.cpp", "WebViewFunctorManager.cpp", "protos/graphicsstats.proto", diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp index 93b9decd9cbc..74cf1fda1b75 100644 --- a/libs/hwui/Animator.cpp +++ b/libs/hwui/Animator.cpp @@ -155,11 +155,9 @@ void BaseRenderNodeAnimator::pushStaging(AnimationContext& context) { RenderNode* oldTarget = mTarget; mTarget = mStagingTarget; mStagingTarget = nullptr; -#ifdef __ANDROID__ // Layoutlib does not support RenderNode if (oldTarget && oldTarget != mTarget) { oldTarget->onAnimatorTargetChanged(this); } -#endif } if (!mHasStartValue) { diff --git a/libs/hwui/RootRenderNode.cpp b/libs/hwui/RootRenderNode.cpp index 24801928121b..ddbbf58b3071 100644 --- a/libs/hwui/RootRenderNode.cpp +++ b/libs/hwui/RootRenderNode.cpp @@ -16,10 +16,13 @@ #include "RootRenderNode.h" +#ifdef __ANDROID__ // Layoutlib does not support Looper (windows) #include +#endif namespace android::uirenderer { +#ifdef __ANDROID__ // Layoutlib does not support Looper class FinishAndInvokeListener : public MessageHandler { public: explicit FinishAndInvokeListener(PropertyValuesAnimatorSet* anim) : mAnimator(anim) { @@ -282,5 +285,22 @@ private: AnimationContext* ContextFactoryImpl::createAnimationContext(renderthread::TimeLord& clock) { return new AnimationContextBridge(clock, mRootNode); } +#else + +void RootRenderNode::prepareTree(TreeInfo& info) { + info.errorHandler = mErrorHandler.get(); + info.updateWindowPositions = true; + RenderNode::prepareTree(info); + info.updateWindowPositions = false; + info.errorHandler = nullptr; +} + +void RootRenderNode::attachAnimatingNode(RenderNode* animatingNode) { } + +void RootRenderNode::destroy() { } + +void RootRenderNode::addVectorDrawableAnimator(PropertyValuesAnimatorSet* anim) { } + +#endif } // namespace android::uirenderer diff --git a/libs/hwui/RootRenderNode.h b/libs/hwui/RootRenderNode.h index 5c830e08ef68..12de4ecac94b 100644 --- a/libs/hwui/RootRenderNode.h +++ b/libs/hwui/RootRenderNode.h @@ -74,6 +74,7 @@ private: void detachVectorDrawableAnimator(PropertyValuesAnimatorSet* anim); }; +#ifdef __ANDROID__ // Layoutlib does not support Animations class ANDROID_API ContextFactoryImpl : public IContextFactory { public: ANDROID_API explicit ContextFactoryImpl(RootRenderNode* rootNode) : mRootNode(rootNode) {} @@ -84,5 +85,6 @@ public: private: RootRenderNode* mRootNode; }; +#endif } // namespace android::uirenderer -- cgit v1.2.3 From cba9579158cc70bf8eadb6e8a239e0ab2dc073d2 Mon Sep 17 00:00:00 2001 From: Jeongik Cha Date: Tue, 9 Jul 2019 23:58:01 +0900 Subject: Consider overlay in system_ext as system's When there is overlay package in system_ext, turn on POLICY_SYSTEM_PARTITION. In other words, overlay pacakge in /system_ext is considered as system's one Test: mv vendor/overlay/framework-res__auto_generated_rro.apk system_ext/overlay and then check if it works properly. Bug: 136715327 Change-Id: Ib225368eae41203a8630f4310d26e9cf1afa706a --- libs/androidfw/AssetManager.cpp | 4 ++-- libs/androidfw/include/androidfw/AssetManager.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'libs') diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp index 4755cb866310..f7c83371f79c 100644 --- a/libs/androidfw/AssetManager.cpp +++ b/libs/androidfw/AssetManager.cpp @@ -74,7 +74,7 @@ const char* AssetManager::RESOURCES_FILENAME = "resources.arsc"; const char* AssetManager::IDMAP_BIN = "/system/bin/idmap"; const char* AssetManager::VENDOR_OVERLAY_DIR = "/vendor/overlay"; const char* AssetManager::PRODUCT_OVERLAY_DIR = "/product/overlay"; -const char* AssetManager::PRODUCT_SERVICES_OVERLAY_DIR = "/product_services/overlay"; +const char* AssetManager::SYSTEM_EXT_OVERLAY_DIR = "/system_ext/overlay"; const char* AssetManager::ODM_OVERLAY_DIR = "/odm/overlay"; const char* AssetManager::OEM_OVERLAY_DIR = "/oem/overlay"; const char* AssetManager::OVERLAY_THEME_DIR_PROPERTY = "ro.boot.vendor.overlay.theme"; @@ -575,7 +575,7 @@ bool AssetManager::appendPathToResTable(asset_path& ap, bool appAsLib) const { mZipSet.setZipResourceTableAsset(ap.path, ass); } } - + if (nextEntryIdx == 0 && ass != NULL) { // If this is the first resource table in the asset // manager, then we are going to cache it so that we diff --git a/libs/androidfw/include/androidfw/AssetManager.h b/libs/androidfw/include/androidfw/AssetManager.h index 66fba26b7289..ce0985b38986 100644 --- a/libs/androidfw/include/androidfw/AssetManager.h +++ b/libs/androidfw/include/androidfw/AssetManager.h @@ -61,7 +61,7 @@ public: static const char* IDMAP_BIN; static const char* VENDOR_OVERLAY_DIR; static const char* PRODUCT_OVERLAY_DIR; - static const char* PRODUCT_SERVICES_OVERLAY_DIR; + static const char* SYSTEM_EXT_OVERLAY_DIR; static const char* ODM_OVERLAY_DIR; static const char* OEM_OVERLAY_DIR; /* -- cgit v1.2.3 From 134372dba9cbe44684c50560b741c7e7b5a29fef Mon Sep 17 00:00:00 2001 From: Stan Iliev Date: Wed, 10 Jul 2019 16:46:09 -0400 Subject: Improve logic when to use filtering in readback and TextureView Detect scaling in screen coordinates, which allows to turn off filtering for some rect-to-rect matrices (90/270 rotation, scaling). Test: CTS test coverage expanded in testSamplingWithTransform Bug: 135895166 Change-Id: Icf5c45fa62bb7a96c5f5464d312bf98a653bc78d --- libs/hwui/pipeline/skia/LayerDrawable.cpp | 73 ++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 25 deletions(-) (limited to 'libs') diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp index eed19420a78a..96b17e1d7975 100644 --- a/libs/hwui/pipeline/skia/LayerDrawable.cpp +++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp @@ -33,21 +33,47 @@ void LayerDrawable::onDraw(SkCanvas* canvas) { } } -// This is a less-strict matrix.isTranslate() that will still report being translate-only -// on imperceptibly small scaleX & scaleY values. -static bool isBasicallyTranslate(const SkMatrix& matrix) { - if (!matrix.isScaleTranslate()) return false; - return MathUtils::isOne(matrix.getScaleX()) && MathUtils::isOne(matrix.getScaleY()); -} - -static bool shouldFilter(const SkMatrix& matrix) { - if (!matrix.isScaleTranslate()) return true; - - // We only care about meaningful scale here - bool noScale = MathUtils::isOne(matrix.getScaleX()) && MathUtils::isOne(matrix.getScaleY()); - bool pixelAligned = - SkScalarIsInt(matrix.getTranslateX()) && SkScalarIsInt(matrix.getTranslateY()); - return !(noScale && pixelAligned); +// Disable filtering when there is no scaling in screen coordinates and the corners have the same +// fraction (for translate) or zero fraction (for any other rect-to-rect transform). +static bool shouldFilterRect(const SkMatrix& matrix, const SkRect& srcRect, const SkRect& dstRect) { + if (!matrix.rectStaysRect()) return true; + SkRect dstDevRect = matrix.mapRect(dstRect); + float dstW, dstH; + bool requiresIntegerTranslate = false; + if (MathUtils::isZero(matrix.getScaleX()) && MathUtils::isZero(matrix.getScaleY())) { + // Has a 90 or 270 degree rotation, although total matrix may also have scale factors + // in m10 and m01. Those scalings are automatically handled by mapRect so comparing + // dimensions is sufficient, but swap width and height comparison. + dstW = dstDevRect.height(); + dstH = dstDevRect.width(); + requiresIntegerTranslate = true; + } else { + // Handle H/V flips or 180 rotation matrices. Axes may have been mirrored, but + // dimensions are still safe to compare directly. + dstW = dstDevRect.width(); + dstH = dstDevRect.height(); + requiresIntegerTranslate = + matrix.getScaleX() < -NON_ZERO_EPSILON || matrix.getScaleY() < -NON_ZERO_EPSILON; + } + if (!(MathUtils::areEqual(dstW, srcRect.width()) && + MathUtils::areEqual(dstH, srcRect.height()))) { + return true; + } + if (requiresIntegerTranslate) { + // Device rect and source rect should be integer aligned to ensure there's no difference + // in how nearest-neighbor sampling is resolved. + return !(MathUtils::isZero(SkScalarFraction(srcRect.x())) && + MathUtils::isZero(SkScalarFraction(srcRect.y())) && + MathUtils::isZero(SkScalarFraction(dstDevRect.x())) && + MathUtils::isZero(SkScalarFraction(dstDevRect.y()))); + } else { + // As long as src and device rects are translated by the same fractional amount, + // filtering won't be needed + return !(MathUtils::areEqual(SkScalarFraction(srcRect.x()), + SkScalarFraction(dstDevRect.x())) && + MathUtils::areEqual(SkScalarFraction(srcRect.y()), + SkScalarFraction(dstDevRect.y()))); + } } bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer, @@ -114,24 +140,21 @@ bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer skiaDestRect = SkRect::MakeIWH(layerWidth, layerHeight); } matrixInv.mapRect(&skiaDestRect); - // If (matrix is identity or an integer translation) and (src/dst buffers size match), + // If (matrix is a rect-to-rect transform) + // and (src/dst buffers size match in screen coordinates) + // and (src/dst corners align fractionally), // then use nearest neighbor, otherwise use bilerp sampling. - // Integer translation is defined as when src rect and dst rect align fractionally. // Skia TextureOp has the above logic build-in, but not NonAAFillRectOp. TextureOp works // only for SrcOver blending and without color filter (readback uses Src blending). - bool isIntegerTranslate = - isBasicallyTranslate(totalMatrix) && - SkScalarFraction(skiaDestRect.fLeft + totalMatrix[SkMatrix::kMTransX]) == - SkScalarFraction(skiaSrcRect.fLeft) && - SkScalarFraction(skiaDestRect.fTop + totalMatrix[SkMatrix::kMTransY]) == - SkScalarFraction(skiaSrcRect.fTop); - if (layer->getForceFilter() || !isIntegerTranslate) { + if (layer->getForceFilter() || + shouldFilterRect(totalMatrix, skiaSrcRect, skiaDestRect)) { paint.setFilterQuality(kLow_SkFilterQuality); } canvas->drawImageRect(layerImage.get(), skiaSrcRect, skiaDestRect, &paint, SkCanvas::kFast_SrcRectConstraint); } else { - if (layer->getForceFilter() || shouldFilter(totalMatrix)) { + SkRect imageRect = SkRect::MakeIWH(layerImage->width(), layerImage->height()); + if (layer->getForceFilter() || shouldFilterRect(totalMatrix, imageRect, imageRect)) { paint.setFilterQuality(kLow_SkFilterQuality); } canvas->drawImage(layerImage.get(), 0, 0, &paint); -- cgit v1.2.3 From 31fee3a24442e42c09f90893702e1e210495166d Mon Sep 17 00:00:00 2001 From: Nathaniel Nifong Date: Thu, 11 Jul 2019 16:27:14 -0400 Subject: Resolve drawable when playing back DisplayListData in the RenderThread. Drawables are mutable, and not expected to produce the same content if retained for longer than the duration of the frame. This becomes relevant when recording multi-frame SKPs because the serialization step forces drawables to be played back long after the frame completed. By resolving the drawable when we are drawing the frame we avoid ref'ing the SkDrawable and do not extend its lifetime beyond the frame. Bug: skia:9234 Test: Captured SKPs on a Pixel 3 Change-Id: I5d317dd14e20be9d5e18675df8667327bd05aff0 --- libs/hwui/RecordingCanvas.cpp | 7 ++++++- libs/hwui/pipeline/skia/SkiaPipeline.cpp | 3 +-- libs/hwui/tests/unit/RenderNodeDrawableTests.cpp | 10 ++++------ 3 files changed, 11 insertions(+), 9 deletions(-) (limited to 'libs') diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index e58fbbe8e667..c8eb1ca55910 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -274,7 +274,12 @@ struct DrawDrawable final : Op { } sk_sp drawable; SkMatrix matrix = SkMatrix::I(); - void draw(SkCanvas* c, const SkMatrix&) const { c->drawDrawable(drawable.get(), &matrix); } + // It is important that we call drawable->draw(c) here instead of c->drawDrawable(drawable). + // Drawables are mutable and in cases, like RenderNodeDrawable, are not expected to produce the + // same content if retained outside the duration of the frame. Therefore we resolve + // them now and do not allow the canvas to take a reference to the drawable and potentially + // keep it alive for longer than the frames duration (e.g. SKP serialization). + void draw(SkCanvas* c, const SkMatrix&) const { drawable->draw(c, &matrix); } }; struct DrawPicture final : Op { static const auto kType = Type::DrawPicture; diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 84c0d1369e83..530926bf8dd0 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -313,8 +313,7 @@ SkCanvas* SkiaPipeline::tryCapture(SkSurface* surface) { case CaptureMode::CallbackAPI: case CaptureMode::SingleFrameSKP: mRecorder.reset(new SkPictureRecorder()); - pictureCanvas = mRecorder->beginRecording(surface->width(), surface->height(), - nullptr, SkPictureRecorder::kPlaybackDrawPicture_RecordFlag); + pictureCanvas = mRecorder->beginRecording(surface->width(), surface->height()); break; case CaptureMode::MultiFrameSKP: // If a multi frame recording is active, initialize recording for a single frame of a diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp index c813cd945905..e70378bd15a5 100644 --- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp +++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp @@ -1096,10 +1096,8 @@ TEST(ReorderBarrierDrawable, testShadowMatrix) { int getDrawCounter() { return mDrawCounter; } virtual void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override { - // expect to draw 2 RenderNodeDrawable, 1 StartReorderBarrierDrawable, - // 1 EndReorderBarrierDrawable - mDrawCounter++; - SkCanvas::onDrawDrawable(drawable, matrix); + // Do not expect this to be called. See RecordingCanvas.cpp DrawDrawable for context. + EXPECT_TRUE(false); } virtual void didTranslate(SkScalar dx, SkScalar dy) override { @@ -1159,8 +1157,8 @@ TEST(ReorderBarrierDrawable, testShadowMatrix) { // create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection ShadowTestCanvas canvas(CANVAS_WIDTH, CANVAS_HEIGHT); RenderNodeDrawable drawable(parent.get(), &canvas, false); - canvas.drawDrawable(&drawable); - EXPECT_EQ(9, canvas.getDrawCounter()); + drawable.draw(&canvas); + EXPECT_EQ(5, canvas.getDrawCounter()); } // Draw a vector drawable twice but with different bounds and verify correct bounds are used. -- cgit v1.2.3 From 894e85a34d8e682cae72ec97d05dc19100ee9636 Mon Sep 17 00:00:00 2001 From: John Reck Date: Mon, 15 Jul 2019 16:16:44 -0700 Subject: Remove final usage of asSkCanvas Bug: 137581257 Test: builds Change-Id: Ib38f85c11be332e6cd784f8bbf55f58cde72ac09 --- libs/hwui/SkiaCanvas.cpp | 7 +++++++ libs/hwui/SkiaCanvas.h | 4 ++-- libs/hwui/hwui/Canvas.h | 15 +-------------- libs/hwui/tests/unit/SkiaCanvasTests.cpp | 2 +- 4 files changed, 11 insertions(+), 17 deletions(-) (limited to 'libs') diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 6ea6af8f2935..f01b1bfa780d 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -778,6 +778,13 @@ void SkiaCanvas::drawCircle(uirenderer::CanvasPropertyPrimitive* x, mCanvas->drawDrawable(drawable.get()); } +void SkiaCanvas::drawPicture(const SkPicture& picture) { + // TODO: Change to mCanvas->drawPicture()? SkCanvas::drawPicture seems to be + // where the logic is for playback vs. ref picture. Using picture.playback here + // to stay behavior-identical for now, but should revisit this at some point. + picture.playback(mCanvas); +} + // ---------------------------------------------------------------------------- // Canvas draw operations: View System // ---------------------------------------------------------------------------- diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h index 05a6d0dda42d..799a89158298 100644 --- a/libs/hwui/SkiaCanvas.h +++ b/libs/hwui/SkiaCanvas.h @@ -46,8 +46,6 @@ public: virtual ~SkiaCanvas(); - virtual SkCanvas* asSkCanvas() override { return mCanvas; } - virtual void resetRecording(int width, int height, uirenderer::RenderNode* renderNode) override { LOG_ALWAYS_FATAL("SkiaCanvas cannot be reset as a recording canvas"); @@ -155,9 +153,11 @@ public: virtual void drawRenderNode(uirenderer::RenderNode* renderNode) override; virtual void callDrawGLFunction(Functor* functor, uirenderer::GlFunctorLifecycleListener* listener) override; + virtual void drawPicture(const SkPicture& picture) override; protected: SkiaCanvas(); + SkCanvas* asSkCanvas() { return mCanvas; } void reset(SkCanvas* skiaCanvas); void drawDrawable(SkDrawable* drawable) { mCanvas->drawDrawable(drawable); } diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h index ac8db216b059..ee4fa1d689ef 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -131,20 +131,6 @@ public: */ static void setCompatibilityVersion(int apiLevel); - /** - * 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. - * - * The SkCanvas returned is *only* valid until another Canvas call is made - * that would change state (e.g. matrix or clip). Clients of asSkCanvas() - * are responsible for *not* persisting this pointer. - * - * 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; @@ -264,6 +250,7 @@ public: const SkPaint* paint) = 0; virtual double drawAnimatedImage(AnimatedImageDrawable* imgDrawable) = 0; + virtual void drawPicture(const SkPicture& picture) = 0; /** * Specifies if the positions passed to ::drawText are absolute or relative diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp index f6178aff0c2e..2ed1b25efc71 100644 --- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp +++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp @@ -78,7 +78,7 @@ TEST(SkiaCanvas, colorSpaceXform) { sk_sp picture = recorder.finishRecordingAsPicture(); // Playback to a software sRGB canvas. The result should be fully red. - canvas.asSkCanvas()->drawPicture(picture); + canvas.drawPicture(*picture); ASSERT_EQ(0xFF0000FF, *skBitmap.getAddr32(0, 0)); } -- cgit v1.2.3 From 8b994191924564624ef831b4fd4c67a1adbd624f Mon Sep 17 00:00:00 2001 From: Derek Sollenberger Date: Tue, 16 Jul 2019 13:23:29 -0400 Subject: Update includes to begin isolating graphics classes. GraphicsJNI.h will only be visible to files in the graphics component and this CL begins to remove all usages of that header that are known to be outside the graphic component. Test: compile Bug: 137655431 Change-Id: If310782e5d95d05ab7cbd54dafa5771d315707a7 --- libs/hwui/RecordingCanvas.h | 1 - libs/hwui/renderthread/RenderProxy.cpp | 2 ++ libs/hwui/renderthread/RenderProxy.h | 2 +- libs/hwui/renderthread/VulkanSurface.h | 1 + 4 files changed, 4 insertions(+), 2 deletions(-) (limited to 'libs') diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h index 7269bcad3d7a..16ec877002f7 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -29,7 +29,6 @@ #include "SkPaint.h" #include "SkPath.h" #include "SkRect.h" -#include "SkTDArray.h" #include "SkTemplates.h" #include diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index e50428b2ee62..40fbdffaf90a 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -16,6 +16,8 @@ #include "RenderProxy.h" +#include + #include "DeferredLayerUpdater.h" #include "DisplayList.h" #include "Properties.h" diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index a0f08cbd26f9..c3eb6ede9bf2 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -19,7 +19,6 @@ #include #include -#include #include #include "../FrameMetricsObserver.h" @@ -30,6 +29,7 @@ namespace android { class GraphicBuffer; +class Surface; namespace uirenderer { diff --git a/libs/hwui/renderthread/VulkanSurface.h b/libs/hwui/renderthread/VulkanSurface.h index 5fa860a20916..5717bb3afe5b 100644 --- a/libs/hwui/renderthread/VulkanSurface.h +++ b/libs/hwui/renderthread/VulkanSurface.h @@ -17,6 +17,7 @@ #include #include +#include #include #include -- cgit v1.2.3 From a8871166a39528ef1cd90632c5c1f5af2c3c7afa Mon Sep 17 00:00:00 2001 From: Fedor Kudasov Date: Thu, 4 Jul 2019 12:54:28 +0100 Subject: Enable surface for host Bug: 117921091 Test: all tests should pass Change-Id: I85ca106bb9d7bec36543abf6174ea6958207e56b --- libs/hostgraphics/Android.bp | 24 ++++++++++ libs/hostgraphics/gui/IGraphicBufferProducer.h | 36 ++++++++++++++ libs/hostgraphics/gui/Surface.h | 66 ++++++++++++++++++++++++++ 3 files changed, 126 insertions(+) create mode 100644 libs/hostgraphics/Android.bp create mode 100644 libs/hostgraphics/gui/IGraphicBufferProducer.h create mode 100644 libs/hostgraphics/gui/Surface.h (limited to 'libs') diff --git a/libs/hostgraphics/Android.bp b/libs/hostgraphics/Android.bp new file mode 100644 index 000000000000..aedb7522ab08 --- /dev/null +++ b/libs/hostgraphics/Android.bp @@ -0,0 +1,24 @@ +cc_library_host_static { + name: "libhostgraphics", + + srcs: [ + ":libui_host_common", + ], + + include_dirs: [ + // Here we override all the headers automatically included with frameworks/native/include. + // When frameworks/native/include will be removed from the list of automatic includes. + // We will have to copy necessary headers with a pre-build step (generated headers). + ".", + "frameworks/native/libs/nativebase/include", + "frameworks/native/libs/nativewindow/include", + "frameworks/native/libs/arect/include", + ], + export_include_dirs: ["."], + + target: { + windows: { + enabled: true, + } + }, +} \ No newline at end of file diff --git a/libs/hostgraphics/gui/IGraphicBufferProducer.h b/libs/hostgraphics/gui/IGraphicBufferProducer.h new file mode 100644 index 000000000000..00422136ff76 --- /dev/null +++ b/libs/hostgraphics/gui/IGraphicBufferProducer.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2019 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_GUI_IGRAPHICBUFFERPRODUCER_H +#define ANDROID_GUI_IGRAPHICBUFFERPRODUCER_H + +#include + +namespace android { + +class IGraphicBufferProducer : virtual public RefBase { +public: + enum class DisconnectMode { + // Disconnect only the specified API. + Api, + // Disconnect any API originally connected from the process calling disconnect. + AllLocal + }; +}; + +} // namespace android + +#endif // ANDROID_GUI_IGRAPHICBUFFERPRODUCER_H diff --git a/libs/hostgraphics/gui/Surface.h b/libs/hostgraphics/gui/Surface.h new file mode 100644 index 000000000000..de1ba00211d3 --- /dev/null +++ b/libs/hostgraphics/gui/Surface.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2019 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_GUI_SURFACE_H +#define ANDROID_GUI_SURFACE_H + +#include +#include +#include +#include + +namespace android { + +class Surface : public ANativeObjectBase { +public: + explicit Surface(const sp& bufferProducer, + bool controlledByApp = false) { + ANativeWindow::perform = hook_perform; + } + static bool isValid(const sp& surface) { return surface != nullptr; } + void allocateBuffers() {} + + uint64_t getNextFrameNumber() const { return 0; } + + int setScalingMode(int mode) { return 0; } + + virtual int disconnect(int api, + IGraphicBufferProducer::DisconnectMode mode = + IGraphicBufferProducer::DisconnectMode::Api) { + return 0; + } + + virtual int lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds) { + // TODO: implement this + return 0; + } + virtual int unlockAndPost() { return 0; } + virtual int query(int what, int* value) const { return 0; } + +protected: + virtual ~Surface() {} + + static int hook_perform(ANativeWindow* window, int operation, ...) { return 0; } + +private: + // can't be copied + Surface& operator=(const Surface& rhs); + Surface(const Surface& rhs); +}; + +} // namespace android + +#endif // ANDROID_GUI_SURFACE_H -- cgit v1.2.3 From 59dd2ea974a7f2abfcf6aced764b1e1dc695760b Mon Sep 17 00:00:00 2001 From: John Reck Date: Fri, 26 Jul 2019 16:51:08 -0700 Subject: Fix ReliableSurface to be more reliable Handle TIMED_OUT better by rescheduling (TODO: give up after N attempts?) Fix SYNC_SURFACE_LOST_REWARD_IF_FOUND path to actually go fetch a new surface. Bug: 137509524 Test: Injected errors randomly, verified nothing got permanently dead. Change-Id: Id30f8ad1dd7196041ee84c16c8cf5c814002a6ce --- libs/hwui/renderthread/CanvasContext.cpp | 24 +++++++++++++++++++++--- libs/hwui/renderthread/ReliableSurface.cpp | 15 ++++++++------- libs/hwui/renderthread/ReliableSurface.h | 10 ++++++++-- 3 files changed, 37 insertions(+), 12 deletions(-) (limited to 'libs') diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index d97c5ed4c7f5..d19351bf1db9 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -450,20 +450,38 @@ void CanvasContext::draw() { waitOnFences(); bool requireSwap = false; + int error = OK; bool didSwap = mRenderPipeline->swapBuffers(frame, drew, windowDirty, mCurrentFrameInfo, &requireSwap); mIsDirty = false; if (requireSwap) { - if (!didSwap) { // some error happened + bool didDraw = true; + // Handle any swapchain errors + error = mNativeSurface->getAndClearError(); + if (error == TIMED_OUT) { + // Try again + mRenderThread.postFrameCallback(this); + // But since this frame didn't happen, we need to mark full damage in the swap + // history + didDraw = false; + + } else if (error != OK || !didSwap) { + // Unknown error, abandon the surface setSurface(nullptr); + didDraw = false; } + SwapHistory& swap = mSwapHistory.next(); - swap.damage = windowDirty; + if (didDraw) { + swap.damage = windowDirty; + } else { + swap.damage = SkRect::MakeWH(INT_MAX, INT_MAX); + } swap.swapCompletedTime = systemTime(SYSTEM_TIME_MONOTONIC); swap.vsyncTime = mRenderThread.timeLord().latestVsync(); - if (mNativeSurface.get()) { + if (didDraw) { int durationUs; nsecs_t dequeueStart = mNativeSurface->getLastDequeueStartTime(); if (dequeueStart < mCurrentFrameInfo->get(FrameInfoIndex::SyncStart)) { diff --git a/libs/hwui/renderthread/ReliableSurface.cpp b/libs/hwui/renderthread/ReliableSurface.cpp index ad1fc4921781..a44b80457218 100644 --- a/libs/hwui/renderthread/ReliableSurface.cpp +++ b/libs/hwui/renderthread/ReliableSurface.cpp @@ -87,21 +87,21 @@ void ReliableSurface::perform(int operation, va_list args) { } int ReliableSurface::reserveNext() { + if constexpr (DISABLE_BUFFER_PREFETCH) { + return OK; + } { std::lock_guard _lock{mMutex}; if (mReservedBuffer) { ALOGW("reserveNext called but there was already a buffer reserved?"); return OK; } - if (mInErrorState) { + if (mBufferQueueState != OK) { return UNKNOWN_ERROR; } if (mHasDequeuedBuffer) { return OK; } - if constexpr (DISABLE_BUFFER_PREFETCH) { - return OK; - } } // TODO: Update this to better handle when requested dimensions have changed @@ -165,10 +165,11 @@ int ReliableSurface::dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd) { } } + int result = callProtected(mSurface, dequeueBuffer, buffer, fenceFd); if (result != OK) { ALOGW("dequeueBuffer failed, error = %d; switching to fallback", result); - *buffer = acquireFallbackBuffer(); + *buffer = acquireFallbackBuffer(result); *fenceFd = -1; return *buffer ? OK : INVALID_OPERATION; } else { @@ -201,9 +202,9 @@ bool ReliableSurface::isFallbackBuffer(const ANativeWindowBuffer* windowBuffer) return windowBuffer == scratchBuffer; } -ANativeWindowBuffer* ReliableSurface::acquireFallbackBuffer() { +ANativeWindowBuffer* ReliableSurface::acquireFallbackBuffer(int error) { std::lock_guard _lock{mMutex}; - mInErrorState = true; + mBufferQueueState = error; if (mScratchBuffer) { return AHardwareBuffer_to_ANativeWindowBuffer(mScratchBuffer.get()); diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h index 0bfc72ef61cb..41fc35eca9f7 100644 --- a/libs/hwui/renderthread/ReliableSurface.h +++ b/libs/hwui/renderthread/ReliableSurface.h @@ -43,6 +43,12 @@ public: uint64_t getNextFrameNumber() const { return mSurface->getNextFrameNumber(); } + int getAndClearError() { + int ret = mBufferQueueState; + mBufferQueueState = OK; + return ret; + } + private: const sp mSurface; @@ -55,10 +61,10 @@ private: ANativeWindowBuffer* mReservedBuffer = nullptr; base::unique_fd mReservedFenceFd; bool mHasDequeuedBuffer = false; - bool mInErrorState = false; + int mBufferQueueState = OK; bool isFallbackBuffer(const ANativeWindowBuffer* windowBuffer) const; - ANativeWindowBuffer* acquireFallbackBuffer(); + ANativeWindowBuffer* acquireFallbackBuffer(int error); void clearReservedBuffer(); void perform(int operation, va_list args); -- cgit v1.2.3 From c2dbc03acc61e3d4303afbbc63c8e7144f617846 Mon Sep 17 00:00:00 2001 From: Mike Reed Date: Thu, 25 Jul 2019 12:28:29 -0400 Subject: pass Paint not SkPaint to Canvas Test: cts Change-Id: I9a3314bc3f221b6e884c8c84d7b0241f7c5be600 --- libs/hwui/SkiaCanvas.cpp | 139 +++++++++++++-------- libs/hwui/SkiaCanvas.h | 69 +++++++--- libs/hwui/VectorDrawable.cpp | 9 +- libs/hwui/hwui/Canvas.cpp | 6 +- libs/hwui/hwui/Canvas.h | 40 +++--- libs/hwui/hwui/Paint.h | 7 ++ libs/hwui/hwui/PaintImpl.cpp | 4 + libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp | 61 +++++++-- libs/hwui/pipeline/skia/SkiaRecordingCanvas.h | 8 +- libs/hwui/tests/common/scenes/BitmapShaders.cpp | 3 +- .../common/scenes/HwBitmapInCompositeShader.cpp | 2 +- .../common/scenes/ListOfFadedTextAnimation.cpp | 2 +- .../hwui/tests/common/scenes/ListViewAnimation.cpp | 2 +- libs/hwui/tests/common/scenes/OvalAnimation.cpp | 2 +- .../hwui/tests/common/scenes/RectGridAnimation.cpp | 2 +- .../tests/common/scenes/SaveLayerAnimation.cpp | 2 +- libs/hwui/tests/common/scenes/ShapeAnimation.cpp | 18 +-- .../common/scenes/SimpleColorMatrixAnimation.cpp | 2 +- .../common/scenes/SimpleGradientAnimation.cpp | 2 +- libs/hwui/tests/common/scenes/TestSceneBase.h | 1 + libs/hwui/tests/common/scenes/TvApp.cpp | 2 +- .../tests/microbench/DisplayListCanvasBench.cpp | 3 +- libs/hwui/tests/unit/RenderNodeDrawableTests.cpp | 33 ++--- libs/hwui/tests/unit/SkiaCanvasTests.cpp | 3 +- libs/hwui/tests/unit/SkiaPipelineTests.cpp | 5 +- libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp | 3 +- libs/hwui/utils/PaintUtils.h | 23 ---- 27 files changed, 279 insertions(+), 174 deletions(-) (limited to 'libs') diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index f01b1bfa780d..54ac0bd9ff75 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -454,7 +454,7 @@ void SkiaCanvas::drawPaint(const SkPaint& paint) { // Canvas draw operations: Geometry // ---------------------------------------------------------------------------- -void SkiaCanvas::drawPoints(const float* points, int count, const SkPaint& paint, +void SkiaCanvas::drawPoints(const float* points, int count, const Paint& paint, SkCanvas::PointMode mode) { if (CC_UNLIKELY(count < 2 || paint.nothingToDraw())) return; // convert the floats into SkPoints @@ -464,109 +464,142 @@ void SkiaCanvas::drawPoints(const float* points, int count, const SkPaint& paint pts[i].set(points[0], points[1]); points += 2; } - mCanvas->drawPoints(mode, count, pts.get(), *filterPaint(paint)); + + apply_looper(&paint, [&](const SkPaint& p) { + mCanvas->drawPoints(mode, count, pts.get(), p); + }); } -void SkiaCanvas::drawPoint(float x, float y, const SkPaint& paint) { - mCanvas->drawPoint(x, y, *filterPaint(paint)); +void SkiaCanvas::drawPoint(float x, float y, const Paint& paint) { + apply_looper(&paint, [&](const SkPaint& p) { + mCanvas->drawPoint(x, y, p); + }); } -void SkiaCanvas::drawPoints(const float* points, int count, const SkPaint& paint) { - this->drawPoints(points, count, *filterPaint(paint), SkCanvas::kPoints_PointMode); +void SkiaCanvas::drawPoints(const float* points, int count, const Paint& 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, *filterPaint(paint)); + const Paint& paint) { + apply_looper(&paint, [&](const SkPaint& p) { + mCanvas->drawLine(startX, startY, stopX, stopY, p); + }); } -void SkiaCanvas::drawLines(const float* points, int count, const SkPaint& paint) { +void SkiaCanvas::drawLines(const float* points, int count, const Paint& paint) { if (CC_UNLIKELY(count < 4 || paint.nothingToDraw())) return; - this->drawPoints(points, count, *filterPaint(paint), SkCanvas::kLines_PointMode); + this->drawPoints(points, count, paint, SkCanvas::kLines_PointMode); } -void SkiaCanvas::drawRect(float left, float top, float right, float bottom, const SkPaint& paint) { +void SkiaCanvas::drawRect(float left, float top, float right, float bottom, const Paint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; - mCanvas->drawRect({left, top, right, bottom}, *filterPaint(paint)); + apply_looper(&paint, [&](const SkPaint& p) { + mCanvas->drawRect({left, top, right, bottom}, p); + }); } -void SkiaCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) { +void SkiaCanvas::drawRegion(const SkRegion& region, const Paint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; - mCanvas->drawRegion(region, *filterPaint(paint)); + apply_looper(&paint, [&](const SkPaint& p) { + mCanvas->drawRegion(region, p); + }); } void SkiaCanvas::drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, - const SkPaint& paint) { + const Paint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; SkRect rect = SkRect::MakeLTRB(left, top, right, bottom); - mCanvas->drawRoundRect(rect, rx, ry, *filterPaint(paint)); + apply_looper(&paint, [&](const SkPaint& p) { + mCanvas->drawRoundRect(rect, rx, ry, p); + }); } void SkiaCanvas::drawDoubleRoundRect(const SkRRect& outer, const SkRRect& inner, - const SkPaint& paint) { - mCanvas->drawDRRect(outer, inner, *filterPaint(paint)); + const Paint& paint) { + apply_looper(&paint, [&](const SkPaint& p) { + mCanvas->drawDRRect(outer, inner, p); + }); } -void SkiaCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) { +void SkiaCanvas::drawCircle(float x, float y, float radius, const Paint& paint) { if (CC_UNLIKELY(radius <= 0 || paint.nothingToDraw())) return; - mCanvas->drawCircle(x, y, radius, *filterPaint(paint)); + apply_looper(&paint, [&](const SkPaint& p) { + mCanvas->drawCircle(x, y, radius, p); + }); } -void SkiaCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) { +void SkiaCanvas::drawOval(float left, float top, float right, float bottom, const Paint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; SkRect oval = SkRect::MakeLTRB(left, top, right, bottom); - mCanvas->drawOval(oval, *filterPaint(paint)); + apply_looper(&paint, [&](const SkPaint& p) { + mCanvas->drawOval(oval, p); + }); } void SkiaCanvas::drawArc(float left, float top, float right, float bottom, float startAngle, - float sweepAngle, bool useCenter, const SkPaint& paint) { + float sweepAngle, bool useCenter, const Paint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; SkRect arc = SkRect::MakeLTRB(left, top, right, bottom); - if (fabs(sweepAngle) >= 360.0f) { - mCanvas->drawOval(arc, *filterPaint(paint)); - } else { - mCanvas->drawArc(arc, startAngle, sweepAngle, useCenter, *filterPaint(paint)); - } + apply_looper(&paint, [&](const SkPaint& p) { + if (fabs(sweepAngle) >= 360.0f) { + mCanvas->drawOval(arc, p); + } else { + mCanvas->drawArc(arc, startAngle, sweepAngle, useCenter, p); + } + }); } -void SkiaCanvas::drawPath(const SkPath& path, const SkPaint& paint) { +void SkiaCanvas::drawPath(const SkPath& path, const Paint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; if (CC_UNLIKELY(path.isEmpty() && (!path.isInverseFillType()))) { return; } - mCanvas->drawPath(path, *filterPaint(paint)); + apply_looper(&paint, [&](const SkPaint& p) { + mCanvas->drawPath(path, p); + }); } -void SkiaCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) { - mCanvas->drawVertices(vertices, mode, *filterPaint(paint)); +void SkiaCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const Paint& paint) { + apply_looper(&paint, [&](const SkPaint& p) { + mCanvas->drawVertices(vertices, mode, p); + }); } // ---------------------------------------------------------------------------- // Canvas draw operations: Bitmaps // ---------------------------------------------------------------------------- -void SkiaCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) { - mCanvas->drawImage(bitmap.makeImage(), left, top, filterPaint(paint)); +void SkiaCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) { + auto image = bitmap.makeImage(); + apply_looper(paint, [&](const SkPaint& p) { + mCanvas->drawImage(image, left, top, &p); + }); } -void SkiaCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) { +void SkiaCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const Paint* paint) { + auto image = bitmap.makeImage(); SkAutoCanvasRestore acr(mCanvas, true); mCanvas->concat(matrix); - mCanvas->drawImage(bitmap.makeImage(), 0, 0, filterPaint(paint)); + apply_looper(paint, [&](const SkPaint& p) { + mCanvas->drawImage(image, 0, 0, &p); + }); } void SkiaCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, - float dstBottom, const SkPaint* paint) { + float dstBottom, const Paint* paint) { + auto image = bitmap.makeImage(); SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom); SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom); - mCanvas->drawImageRect(bitmap.makeImage(), srcRect, dstRect, filterPaint(paint), - SkCanvas::kFast_SrcRectConstraint); + apply_looper(paint, [&](const SkPaint& p) { + mCanvas->drawImageRect(image, srcRect, dstRect, &p, SkCanvas::kFast_SrcRectConstraint); + }); } void SkiaCanvas::drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight, - const float* vertices, const int* colors, const SkPaint* paint) { + const float* vertices, const int* colors, const Paint* paint) { const int ptCount = (meshWidth + 1) * (meshHeight + 1); const int indexCount = meshWidth * meshHeight * 6; uint32_t flags = SkVertices::kHasTexCoords_BuilderFlag; @@ -640,20 +673,20 @@ void SkiaCanvas::drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight, #endif // cons-up a shader for the bitmap - PaintCoW paintCoW(paint); - SkPaint& tmpPaint = paintCoW.writeable(); - - sk_sp image = bitmap.makeImage(); - sk_sp shader = image->makeShader(); - tmpPaint.setShader(std::move(shader)); - - mCanvas->drawVertices(builder.detach(), SkBlendMode::kModulate, - *filterPaint(std::move(paintCoW))); + Paint pnt; + if (paint) { + pnt = *paint; + } + pnt.setShader(bitmap.makeImage()->makeShader()); + auto v = builder.detach(); + apply_looper(&pnt, [&](const SkPaint& p) { + mCanvas->drawVertices(v, SkBlendMode::kModulate, p); + }); } void SkiaCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& chunk, float dstLeft, float dstTop, float dstRight, float dstBottom, - const SkPaint* paint) { + const Paint* paint) { SkCanvas::Lattice lattice; NinePatchUtils::SetLatticeDivs(&lattice, chunk, bitmap.width(), bitmap.height()); @@ -674,8 +707,10 @@ void SkiaCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& chunk, floa lattice.fBounds = nullptr; SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom); - - mCanvas->drawImageLattice(bitmap.makeImage().get(), lattice, dst, filterPaint(paint)); + auto image = bitmap.makeImage(); + apply_looper(paint, [&](const SkPaint& p) { + mCanvas->drawImageLattice(image.get(), lattice, dst, &p); + }); } double SkiaCanvas::drawAnimatedImage(AnimatedImageDrawable* imgDrawable) { diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h index 799a89158298..ec83a1961eff 100644 --- a/libs/hwui/SkiaCanvas.h +++ b/libs/hwui/SkiaCanvas.h @@ -22,8 +22,10 @@ #include "RenderNode.h" #include "VectorDrawable.h" #include "hwui/Canvas.h" +#include "hwui/Paint.h" #include +#include "src/core/SkArenaAlloc.h" #include #include @@ -99,39 +101,39 @@ public: virtual void drawColor(int color, SkBlendMode 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 drawPoint(float x, float y, const Paint& paint) override; + virtual void drawPoints(const float* points, int count, const Paint& 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; + const Paint& paint) override; + virtual void drawLines(const float* points, int count, const Paint& paint) override; virtual void drawRect(float left, float top, float right, float bottom, - const SkPaint& paint) override; - virtual void drawRegion(const SkRegion& region, const SkPaint& paint) override; + const Paint& paint) override; + virtual void drawRegion(const SkRegion& region, const Paint& paint) override; virtual void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, - const SkPaint& paint) override; + const Paint& paint) override; virtual void drawDoubleRoundRect(const SkRRect& outer, const SkRRect& inner, - const SkPaint& paint) override; + const Paint& paint) override; - virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) override; + virtual void drawCircle(float x, float y, float radius, const Paint& paint) override; virtual void drawOval(float left, float top, float right, float bottom, - const SkPaint& paint) override; + const Paint& 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(const SkVertices*, SkBlendMode, const SkPaint& paint) override; + float sweepAngle, bool useCenter, const Paint& paint) override; + virtual void drawPath(const SkPath& path, const Paint& paint) override; + virtual void drawVertices(const SkVertices*, SkBlendMode, const Paint& paint) override; - virtual void drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) override; - virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) override; + virtual void drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) override; + virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const Paint* paint) override; virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, - float dstBottom, const SkPaint* paint) override; + float dstBottom, const Paint* paint) override; virtual void drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight, const float* vertices, const int* colors, - const SkPaint* paint) override; + const Paint* paint) override; virtual void drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& chunk, float dstLeft, float dstTop, float dstRight, float dstBottom, - const SkPaint* paint) override; + const Paint* paint) override; virtual double drawAnimatedImage(AnimatedImageDrawable* imgDrawable) override; virtual bool drawTextAbsolutePos() const override { return true; } @@ -208,6 +210,35 @@ protected: */ PaintCoW&& filterPaint(PaintCoW&& paint) const; + template void apply_looper(const Paint* paint, Proc proc) { + SkPaint skp; + SkDrawLooper* looper = nullptr; + if (paint) { + skp = *filterPaint(paint); + looper = paint->getLooper(); + } + if (looper) { + SkSTArenaAlloc<256> alloc; + SkDrawLooper::Context* ctx = looper->makeContext(&alloc); + if (ctx) { + SkDrawLooper::Context::Info info; + for (;;) { + SkPaint p = skp; + if (!ctx->next(&info, &p)) { + break; + } + mCanvas->save(); + mCanvas->translate(info.fTranslate.fX, info.fTranslate.fY); + proc(p); + mCanvas->restore(); + } + } + } else { + proc(skp); + } + } + + private: struct SaveRec { int saveCount; @@ -222,7 +253,7 @@ private: void recordClip(const T&, SkClipOp); void applyPersistentClips(size_t clipStartIndex); - void drawPoints(const float* points, int count, const SkPaint& paint, SkCanvas::PointMode mode); + void drawPoints(const float* points, int count, const Paint& paint, SkCanvas::PointMode mode); class Clip; diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp index 89ad1b99c6b6..f91d178f01f3 100644 --- a/libs/hwui/VectorDrawable.cpp +++ b/libs/hwui/VectorDrawable.cpp @@ -17,6 +17,7 @@ #include "VectorDrawable.h" #include +#include "hwui/Paint.h" #include "PathParser.h" #include "SkColorFilter.h" #include "SkImageInfo.h" @@ -458,8 +459,12 @@ void Tree::drawStaging(Canvas* outCanvas) { mStagingCache.dirty = false; } - SkPaint paint; - getPaintFor(&paint, mStagingProperties); + SkPaint skp; + getPaintFor(&skp, mStagingProperties); + Paint paint; + paint.setFilterQuality(skp.getFilterQuality()); + paint.setColorFilter(skp.refColorFilter()); + paint.setAlpha(skp.getAlpha()); outCanvas->drawBitmap(*mStagingCache.bitmap, 0, 0, mStagingCache.bitmap->width(), mStagingCache.bitmap->height(), mStagingProperties.getBounds().left(), mStagingProperties.getBounds().top(), diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp index a48c86028262..b98ffca84dfc 100644 --- a/libs/hwui/hwui/Canvas.cpp +++ b/libs/hwui/hwui/Canvas.cpp @@ -34,7 +34,7 @@ Canvas* Canvas::create_recording_canvas(int width, int height, uirenderer::Rende } static inline void drawStroke(SkScalar left, SkScalar right, SkScalar top, SkScalar thickness, - const SkPaint& paint, Canvas* canvas) { + const Paint& paint, Canvas* canvas) { const SkScalar strokeWidth = fmax(thickness, 1.0f); const SkScalar bottom = top + strokeWidth; canvas->drawRect(left, top, right, bottom, paint); @@ -182,7 +182,7 @@ void Canvas::drawText(const uint16_t* text, int textSize, int start, int count, void Canvas::drawDoubleRoundRectXY(float outerLeft, float outerTop, float outerRight, float outerBottom, float outerRx, float outerRy, float innerLeft, float innerTop, float innerRight, float innerBottom, float innerRx, - float innerRy, const SkPaint& paint) { + float innerRy, const Paint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; SkRect outer = SkRect::MakeLTRB(outerLeft, outerTop, outerRight, outerBottom); SkRect inner = SkRect::MakeLTRB(innerLeft, innerTop, innerRight, innerBottom); @@ -198,7 +198,7 @@ void Canvas::drawDoubleRoundRectXY(float outerLeft, float outerTop, float outerR void Canvas::drawDoubleRoundRectRadii(float outerLeft, float outerTop, float outerRight, float outerBottom, const float* outerRadii, float innerLeft, float innerTop, float innerRight, float innerBottom, - const float* innerRadii, const SkPaint& paint) { + const float* innerRadii, const Paint& paint) { static_assert(sizeof(SkVector) == sizeof(float) * 2); if (CC_UNLIKELY(paint.nothingToDraw())) return; SkRect outer = SkRect::MakeLTRB(outerLeft, outerTop, outerRight, outerBottom); diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h index ee4fa1d689ef..b90a4a3d68e6 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -217,37 +217,37 @@ public: 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 floatCount, const SkPaint& paint) = 0; + virtual void drawPoint(float x, float y, const Paint& paint) = 0; + virtual void drawPoints(const float* points, int floatCount, const Paint& paint) = 0; virtual void drawLine(float startX, float startY, float stopX, float stopY, - const SkPaint& paint) = 0; - virtual void drawLines(const float* points, int floatCount, const SkPaint& paint) = 0; + const Paint& paint) = 0; + virtual void drawLines(const float* points, int floatCount, const Paint& paint) = 0; virtual void drawRect(float left, float top, float right, float bottom, - const SkPaint& paint) = 0; - virtual void drawRegion(const SkRegion& region, const SkPaint& paint) = 0; + const Paint& paint) = 0; + virtual void drawRegion(const SkRegion& region, const Paint& paint) = 0; virtual void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, - const SkPaint& paint) = 0; + const Paint& paint) = 0; virtual void drawDoubleRoundRect(const SkRRect& outer, const SkRRect& inner, - const SkPaint& paint) = 0; - virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) = 0; + const Paint& paint) = 0; + virtual void drawCircle(float x, float y, float radius, const Paint& paint) = 0; virtual void drawOval(float left, float top, float right, float bottom, - const SkPaint& paint) = 0; + const Paint& 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(const SkVertices*, SkBlendMode, const SkPaint& paint) = 0; + float sweepAngle, bool useCenter, const Paint& paint) = 0; + virtual void drawPath(const SkPath& path, const Paint& paint) = 0; + virtual void drawVertices(const SkVertices*, SkBlendMode, const Paint& paint) = 0; // Bitmap-based - virtual void drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) = 0; - virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) = 0; + virtual void drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) = 0; + virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const Paint* paint) = 0; virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, - float dstBottom, const SkPaint* paint) = 0; + float dstBottom, const Paint* paint) = 0; virtual void drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight, - const float* vertices, const int* colors, const SkPaint* paint) = 0; + const float* vertices, const int* colors, const Paint* paint) = 0; virtual void drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& chunk, float dstLeft, float dstTop, float dstRight, float dstBottom, - const SkPaint* paint) = 0; + const Paint* paint) = 0; virtual double drawAnimatedImage(AnimatedImageDrawable* imgDrawable) = 0; virtual void drawPicture(const SkPicture& picture) = 0; @@ -281,12 +281,12 @@ public: void drawDoubleRoundRectXY(float outerLeft, float outerTop, float outerRight, float outerBottom, float outerRx, float outerRy, float innerLeft, float innerTop, float innerRight, float innerBottom, float innerRx, - float innerRy, const SkPaint& paint); + float innerRy, const Paint& paint); void drawDoubleRoundRectRadii(float outerLeft, float outerTop, float outerRight, float outerBottom, const float* outerRadii, float innerLeft, float innerTop, float innerRight, float innerBottom, - const float* innerRadii, const SkPaint& paint); + const float* innerRadii, const Paint& paint); static int GetApiLevel() { return sApiLevel; } diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h index 9b2fa9df1e8f..281ecd27d780 100644 --- a/libs/hwui/hwui/Paint.h +++ b/libs/hwui/hwui/Paint.h @@ -21,6 +21,7 @@ #include +#include #include #include #include @@ -58,12 +59,17 @@ public: SkFont& getSkFont() { return mFont; } const SkFont& getSkFont() const { return mFont; } + SkDrawLooper* getLooper() const { return mLooper.get(); } + void setLooper(sk_sp looper) { mLooper = std::move(looper); } + // These shadow the methods on SkPaint, but we need to so we can keep related // attributes in-sync. void reset(); void setAntiAlias(bool); + bool nothingToDraw() const { return !mLooper && SkPaint::nothingToDraw(); } + // End method shadowing void setLetterSpacing(float letterSpacing) { mLetterSpacing = letterSpacing; } @@ -146,6 +152,7 @@ public: private: SkFont mFont; + sk_sp mLooper; float mLetterSpacing = 0; float mWordSpacing = 0; diff --git a/libs/hwui/hwui/PaintImpl.cpp b/libs/hwui/hwui/PaintImpl.cpp index 2f2d575bca29..fa2674fc2f5e 100644 --- a/libs/hwui/hwui/PaintImpl.cpp +++ b/libs/hwui/hwui/PaintImpl.cpp @@ -34,6 +34,7 @@ Paint::Paint() Paint::Paint(const Paint& paint) : SkPaint(paint) , mFont(paint.mFont) + , mLooper(paint.mLooper) , mLetterSpacing(paint.mLetterSpacing) , mWordSpacing(paint.mWordSpacing) , mFontFeatureSettings(paint.mFontFeatureSettings) @@ -52,6 +53,7 @@ Paint::~Paint() {} Paint& Paint::operator=(const Paint& other) { SkPaint::operator=(other); mFont = other.mFont; + mLooper = other.mLooper; mLetterSpacing = other.mLetterSpacing; mWordSpacing = other.mWordSpacing; mFontFeatureSettings = other.mFontFeatureSettings; @@ -69,6 +71,7 @@ Paint& Paint::operator=(const Paint& other) { bool operator==(const Paint& a, const Paint& b) { return static_cast(a) == static_cast(b) && a.mFont == b.mFont && + a.mLooper == b.mLooper && a.mLetterSpacing == b.mLetterSpacing && a.mWordSpacing == b.mWordSpacing && a.mFontFeatureSettings == b.mFontFeatureSettings && a.mMinikinLocaleListId == b.mMinikinLocaleListId && @@ -83,6 +86,7 @@ void Paint::reset() { mFont = SkFont(); mFont.setEdging(SkFont::Edging::kAlias); + mLooper.reset(); mStrikeThru = false; mUnderline = false; diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp index 38ef131bedef..0db5133037b0 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -15,7 +15,7 @@ */ #include "SkiaRecordingCanvas.h" - +#include "hwui/Paint.h" #include #include "CanvasTransform.h" #ifdef __ANDROID__ // Layoutlib does not support Layers @@ -197,9 +197,37 @@ SkiaCanvas::PaintCoW&& SkiaRecordingCanvas::filterBitmap(PaintCoW&& paint) { return filterPaint(std::move(paint)); } -void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) { +static SkDrawLooper* get_looper(const Paint* paint) { + return paint ? paint->getLooper() : nullptr; +} + +template +void applyLooper(SkDrawLooper* looper, const SkPaint& paint, Proc proc) { + if (looper) { + SkSTArenaAlloc<256> alloc; + SkDrawLooper::Context* ctx = looper->makeContext(&alloc); + if (ctx) { + SkDrawLooper::Context::Info info; + for (;;) { + SkPaint p = paint; + if (!ctx->next(&info, &p)) { + break; + } + proc(info.fTranslate.fX, info.fTranslate.fY, p); + } + } + } else { + proc(0, 0, paint); + } +} + +void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) { sk_sp image = bitmap.makeImage(); - mRecorder.drawImage(image, left, top, filterBitmap(paint), bitmap.palette()); + + applyLooper(get_looper(paint), *filterBitmap(paint), [&](SkScalar x, SkScalar y, const SkPaint& p) { + mRecorder.drawImage(image, left + x, top + y, &p, bitmap.palette()); + }); + // if image->unique() is true, then mRecorder.drawImage failed for some reason. It also means // it is not safe to store a raw SkImage pointer, because the image object will be destroyed // when this function ends. @@ -208,12 +236,16 @@ void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, cons } } -void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) { +void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const Paint* paint) { SkAutoCanvasRestore acr(&mRecorder, true); concat(matrix); sk_sp image = bitmap.makeImage(); - mRecorder.drawImage(image, 0, 0, filterBitmap(paint), bitmap.palette()); + + applyLooper(get_looper(paint), *filterBitmap(paint), [&](SkScalar x, SkScalar y, const SkPaint& p) { + mRecorder.drawImage(image, x, y, &p, bitmap.palette()); + }); + if (!bitmap.isImmutable() && image.get() && !image->unique()) { mDisplayList->mMutableImages.push_back(image.get()); } @@ -221,13 +253,17 @@ void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, con void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, - float dstBottom, const SkPaint* paint) { + float dstBottom, const Paint* paint) { SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom); SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom); sk_sp image = bitmap.makeImage(); - mRecorder.drawImageRect(image, srcRect, dstRect, filterBitmap(paint), - SkCanvas::kFast_SrcRectConstraint, bitmap.palette()); + + applyLooper(get_looper(paint), *filterBitmap(paint), [&](SkScalar x, SkScalar y, const SkPaint& p) { + mRecorder.drawImageRect(image, srcRect, dstRect.makeOffset(x, y), &p, + SkCanvas::kFast_SrcRectConstraint, bitmap.palette()); + }); + if (!bitmap.isImmutable() && image.get() && !image->unique() && !srcRect.isEmpty() && !dstRect.isEmpty()) { mDisplayList->mMutableImages.push_back(image.get()); @@ -236,7 +272,7 @@ void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop void SkiaRecordingCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& chunk, float dstLeft, float dstTop, float dstRight, float dstBottom, - const SkPaint* paint) { + const Paint* paint) { SkCanvas::Lattice lattice; NinePatchUtils::SetLatticeDivs(&lattice, chunk, bitmap.width(), bitmap.height()); @@ -264,8 +300,11 @@ void SkiaRecordingCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& ch filteredPaint.writeable().setFilterQuality(kLow_SkFilterQuality); } sk_sp image = bitmap.makeImage(); - mRecorder.drawImageLattice(image, lattice, dst, filterBitmap(std::move(filteredPaint)), - bitmap.palette()); + + applyLooper(get_looper(paint), *filterBitmap(paint), [&](SkScalar x, SkScalar y, const SkPaint& p) { + mRecorder.drawImageLattice(image, lattice, dst.makeOffset(x, y), &p, bitmap.palette()); + }); + if (!bitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) { mDisplayList->mMutableImages.push_back(image.get()); } diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h index c42cea33211e..bd5274c94e75 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h @@ -45,14 +45,14 @@ public: virtual uirenderer::DisplayList* finishRecording() override; - virtual void drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) override; - virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) override; + virtual void drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) override; + virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const Paint* paint) override; virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, - float dstBottom, const SkPaint* paint) override; + float dstBottom, const Paint* paint) override; virtual void drawNinePatch(Bitmap& hwuiBitmap, const android::Res_png_9patch& chunk, float dstLeft, float dstTop, float dstRight, float dstBottom, - const SkPaint* paint) override; + const Paint* paint) override; virtual double drawAnimatedImage(AnimatedImageDrawable* animatedImage) override; virtual void drawRoundRect(uirenderer::CanvasPropertyPrimitive* left, diff --git a/libs/hwui/tests/common/scenes/BitmapShaders.cpp b/libs/hwui/tests/common/scenes/BitmapShaders.cpp index 400196b203b9..c4067af388e3 100644 --- a/libs/hwui/tests/common/scenes/BitmapShaders.cpp +++ b/libs/hwui/tests/common/scenes/BitmapShaders.cpp @@ -15,6 +15,7 @@ */ #include +#include "hwui/Paint.h" #include "TestSceneBase.h" #include "tests/common/BitmapAllocationTestUtils.h" #include "utils/Color.h" @@ -43,7 +44,7 @@ public: skCanvas.drawRect(SkRect::MakeXYWH(100, 100, 100, 100), skPaint); }); - SkPaint paint; + Paint paint; sk_sp image = hwuiBitmap->makeImage(); sk_sp repeatShader = image->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat); diff --git a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp index 659926bae06f..3d0a2b2474bf 100644 --- a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp +++ b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp @@ -65,7 +65,7 @@ public: sk_sp compositeShader( SkShaders::Blend(SkBlendMode::kDstATop, hardwareShader, gradientShader)); - SkPaint paint; + Paint paint; paint.setShader(std::move(compositeShader)); canvas.drawRoundRect(0, 0, 400, 200, 10.0f, 10.0f, paint); } diff --git a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp index c6b60aaec9c5..a9449b62a1f8 100644 --- a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp @@ -49,7 +49,7 @@ class ListOfFadedTextAnimation : public TestListViewSceneBase { SkMatrix matrix; matrix.setScale(1, length); matrix.postRotate(-90); - SkPaint fadingPaint; + Paint fadingPaint; fadingPaint.setShader(s->makeWithLocalMatrix(matrix)); fadingPaint.setBlendMode(SkBlendMode::kDstOut); canvas.drawRect(0, 0, length, itemHeight, fadingPaint); diff --git a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp index fb7e34dfa513..d031923a112b 100644 --- a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp @@ -79,7 +79,7 @@ class ListViewAnimation : public TestListViewSceneBase { static sk_sp filledBox(createBoxBitmap(true)); static sk_sp strokedBox(createBoxBitmap(false)); // TODO: switch to using round rect clipping, once merging correctly handles that - SkPaint roundRectPaint; + Paint roundRectPaint; roundRectPaint.setAntiAlias(true); roundRectPaint.setColor(Color::White); canvas.drawRoundRect(0, 0, itemWidth, itemHeight, dp(6), dp(6), roundRectPaint); diff --git a/libs/hwui/tests/common/scenes/OvalAnimation.cpp b/libs/hwui/tests/common/scenes/OvalAnimation.cpp index 4ff868b9d068..402c1ece2146 100644 --- a/libs/hwui/tests/common/scenes/OvalAnimation.cpp +++ b/libs/hwui/tests/common/scenes/OvalAnimation.cpp @@ -28,7 +28,7 @@ public: void createContent(int width, int height, Canvas& canvas) override { canvas.drawColor(Color::White, SkBlendMode::kSrcOver); card = TestUtils::createNode(0, 0, 200, 200, [](RenderProperties& props, Canvas& canvas) { - SkPaint paint; + Paint paint; paint.setAntiAlias(true); paint.setColor(Color::Black); canvas.drawOval(0, 0, 200, 200, paint); diff --git a/libs/hwui/tests/common/scenes/RectGridAnimation.cpp b/libs/hwui/tests/common/scenes/RectGridAnimation.cpp index 6a3b6a57b28a..d5ecfaff4f0c 100644 --- a/libs/hwui/tests/common/scenes/RectGridAnimation.cpp +++ b/libs/hwui/tests/common/scenes/RectGridAnimation.cpp @@ -41,7 +41,7 @@ public: } } - SkPaint paint; + Paint paint; paint.setColor(0xff00ffff); canvas.drawRegion(region, paint); }); diff --git a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp index 02dd42ff2ae8..97bfba34c790 100644 --- a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp +++ b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp @@ -45,7 +45,7 @@ public: canvas.save(SaveFlags::MatrixClip); canvas.translate(0, 400); canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::Flags(0)); // unclipped - SkPaint paint; + Paint paint; paint.setAntiAlias(true); paint.setColor(Color::Green_700); canvas.drawCircle(200, 200, 200, paint); diff --git a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp index d189a9379c33..70a1557dcf6a 100644 --- a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp @@ -30,14 +30,14 @@ public: void createContent(int width, int height, Canvas& canvas) override { card = TestUtils::createNode( 0, 0, width, height, [width](RenderProperties& props, Canvas& canvas) { - std::function ops[] = { - [](Canvas& canvas, float size, const SkPaint& paint) { + std::function ops[] = { + [](Canvas& canvas, float size, const Paint& paint) { canvas.drawArc(0, 0, size, size, 50, 189, true, paint); }, - [](Canvas& canvas, float size, const SkPaint& paint) { + [](Canvas& canvas, float size, const Paint& paint) { canvas.drawOval(0, 0, size, size, paint); }, - [](Canvas& canvas, float size, const SkPaint& paint) { + [](Canvas& canvas, float size, const Paint& paint) { SkPath diamondPath; diamondPath.moveTo(size / 2, 0); diamondPath.lineTo(size, size / 2); @@ -46,18 +46,18 @@ public: diamondPath.close(); canvas.drawPath(diamondPath, paint); }, - [](Canvas& canvas, float size, const SkPaint& paint) { + [](Canvas& canvas, float size, const Paint& paint) { float data[] = {0, 0, size, size, 0, size, size, 0}; canvas.drawLines(data, sizeof(data) / sizeof(float), paint); }, - [](Canvas& canvas, float size, const SkPaint& paint) { + [](Canvas& canvas, float size, const Paint& paint) { float data[] = {0, 0, size, size, 0, size, size, 0}; canvas.drawPoints(data, sizeof(data) / sizeof(float), paint); }, - [](Canvas& canvas, float size, const SkPaint& paint) { + [](Canvas& canvas, float size, const Paint& paint) { canvas.drawRect(0, 0, size, size, paint); }, - [](Canvas& canvas, float size, const SkPaint& paint) { + [](Canvas& canvas, float size, const Paint& paint) { float rad = size / 4; canvas.drawRoundRect(0, 0, size, size, rad, rad, paint); }}; @@ -66,7 +66,7 @@ public: // each combination of strokeWidth + style gets a column int outerCount = canvas.save(SaveFlags::MatrixClip); - SkPaint paint; + Paint paint; paint.setAntiAlias(true); SkPaint::Style styles[] = {SkPaint::kStroke_Style, SkPaint::kFill_Style, SkPaint::kStrokeAndFill_Style}; diff --git a/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp b/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp index e60bd5fae198..a0bc5aa245d5 100644 --- a/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp +++ b/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp @@ -52,7 +52,7 @@ private: return TestUtils::createNode( x, y, x + width, y + height, [width, height](RenderProperties& props, Canvas& canvas) { - SkPaint paint; + Paint paint; // Simple scale/translate case where R, G, and B are all treated equivalently SkColorMatrix cm; cm.setScale(1.1f, 1.1f, 1.1f, 0.5f); diff --git a/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp b/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp index 8bd804e75674..57a260c8d234 100644 --- a/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp +++ b/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp @@ -51,7 +51,7 @@ private: [width, height](RenderProperties& props, Canvas& canvas) { float pos[] = {0, 1}; SkPoint pts[] = {SkPoint::Make(0, 0), SkPoint::Make(width, height)}; - SkPaint paint; + Paint paint; // overdraw several times to emphasize shader cost for (int i = 0; i < 10; i++) { // use i%2 start position to pick 2 color combo with black in it diff --git a/libs/hwui/tests/common/scenes/TestSceneBase.h b/libs/hwui/tests/common/scenes/TestSceneBase.h index 6f76a502ae3e..24d35857c60d 100644 --- a/libs/hwui/tests/common/scenes/TestSceneBase.h +++ b/libs/hwui/tests/common/scenes/TestSceneBase.h @@ -17,6 +17,7 @@ #pragma once #include "hwui/Canvas.h" +#include "hwui/Paint.h" #include "RenderNode.h" #include "tests/common/TestContext.h" #include "tests/common/TestScene.h" diff --git a/libs/hwui/tests/common/scenes/TvApp.cpp b/libs/hwui/tests/common/scenes/TvApp.cpp index 76f02888f994..bac887053d2f 100644 --- a/libs/hwui/tests/common/scenes/TvApp.cpp +++ b/libs/hwui/tests/common/scenes/TvApp.cpp @@ -217,7 +217,7 @@ private: std::unique_ptr canvas(Canvas::create_recording_canvas( image->stagingProperties().getWidth(), image->stagingProperties().getHeight(), image.get())); - SkPaint paint; + Paint paint; sk_sp filter( SkColorFilters::Blend((curFrame % 150) << 24, SkBlendMode::kSrcATop)); paint.setColorFilter(filter); diff --git a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp index 70423a70157b..4ce6c32470ea 100644 --- a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp +++ b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp @@ -18,6 +18,7 @@ #include "DisplayList.h" #include "hwui/Canvas.h" +#include "hwui/Paint.h" #include "pipeline/skia/SkiaDisplayList.h" #include "tests/common/TestUtils.h" @@ -93,7 +94,7 @@ void BM_DisplayListCanvas_record_simpleBitmapView(benchmark::State& benchState) std::unique_ptr canvas(Canvas::create_recording_canvas(100, 100)); delete canvas->finishRecording(); - SkPaint rectPaint; + Paint rectPaint; sk_sp iconBitmap(TestUtils::createBitmap(80, 80)); while (benchState.KeepRunning()) { diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp index e70378bd15a5..3632be06c45f 100644 --- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp +++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp @@ -24,6 +24,7 @@ #include "DamageAccumulator.h" #include "FatalTestCanvas.h" #include "IContextFactory.h" +#include "hwui/Paint.h" #include "RecordingCanvas.h" #include "SkiaCanvas.h" #include "pipeline/skia/SkiaDisplayList.h" @@ -59,7 +60,7 @@ TEST(RenderNodeDrawable, create) { namespace { static void drawOrderedRect(Canvas* canvas, uint8_t expectedDrawOrder) { - SkPaint paint; + Paint paint; // order put in blue channel, transparent so overlapped content doesn't get rejected paint.setColor(SkColorSetARGB(1, 0, 0, expectedDrawOrder)); canvas->drawRect(0, 0, 100, 100, paint); @@ -211,7 +212,7 @@ TEST(RenderNodeDrawable, saveLayerClipAndMatrixRestore) { ASSERT_EQ(SkRect::MakeLTRB(0, 0, 400, 800), getRecorderClipBounds(recorder)); EXPECT_TRUE(getRecorderMatrix(recorder).isIdentity()); - SkPaint paint; + Paint paint; paint.setAntiAlias(true); paint.setColor(SK_ColorGREEN); recorder.drawRect(0.0f, 400.0f, 400.0f, 800.0f, paint); @@ -291,7 +292,7 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorder) { properties.setTranslationX(SCROLL_X); properties.setTranslationY(SCROLL_Y); - SkPaint paint; + Paint paint; paint.setColor(SK_ColorWHITE); canvas.drawRect(0, 0, 100, 100, paint); }, @@ -302,7 +303,7 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorder) { [](RenderProperties& properties, SkiaRecordingCanvas& canvas) { properties.setProjectBackwards(true); properties.setClipToBounds(false); - SkPaint paint; + Paint paint; paint.setColor(SK_ColorDKGRAY); canvas.drawRect(-10, -10, 60, 60, paint); }, @@ -310,7 +311,7 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorder) { auto child = TestUtils::createSkiaNode( 0, 50, 100, 100, [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) { - SkPaint paint; + Paint paint; paint.setColor(SK_ColorBLUE); canvas.drawRect(0, 0, 100, 50, paint); canvas.drawRenderNode(projectingRipple.get()); @@ -375,14 +376,14 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, emptyReceiver) { [](RenderProperties& properties, SkiaRecordingCanvas& canvas) { properties.setProjectBackwards(true); properties.setClipToBounds(false); - SkPaint paint; + Paint paint; canvas.drawRect(0, 0, 100, 100, paint); }, "P"); auto child = TestUtils::createSkiaNode( 0, 0, 100, 100, [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) { - SkPaint paint; + Paint paint; canvas.drawRect(0, 0, 100, 100, paint); canvas.drawRenderNode(projectingRipple.get()); }, @@ -483,7 +484,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, projectionHwLayer) { properties.setTranslationX(SCROLL_X); properties.setTranslationY(SCROLL_Y); - canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint()); + canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, Paint()); }, "B"); // B auto projectingRipple = TestUtils::createSkiaNode( @@ -491,14 +492,14 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, projectionHwLayer) { [](RenderProperties& properties, SkiaRecordingCanvas& canvas) { properties.setProjectBackwards(true); properties.setClipToBounds(false); - canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds + canvas.drawOval(100, 100, 300, 300, Paint()); // drawn mostly out of layer bounds }, "R"); // R auto child = TestUtils::createSkiaNode( 100, 100, 300, 300, [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) { canvas.drawRenderNode(projectingRipple.get()); - canvas.drawArc(0, 0, LAYER_WIDTH, LAYER_HEIGHT, 0.0f, 280.0f, true, SkPaint()); + canvas.drawArc(0, 0, LAYER_WIDTH, LAYER_HEIGHT, 0.0f, 280.0f, true, Paint()); }, "C"); // C auto parent = TestUtils::createSkiaNode( @@ -578,7 +579,7 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionChildScroll) { 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, [](RenderProperties& properties, SkiaRecordingCanvas& canvas) { properties.setProjectionReceiver(true); - canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint()); + canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, Paint()); }, "B"); // B auto projectingRipple = TestUtils::createSkiaNode( @@ -591,7 +592,7 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionChildScroll) { properties.setTranslationY(SCROLL_Y); properties.setProjectBackwards(true); properties.setClipToBounds(false); - canvas.drawOval(0, 0, 200, 200, SkPaint()); + canvas.drawOval(0, 0, 200, 200, Paint()); }, "R"); // R auto child = TestUtils::createSkiaNode( @@ -946,7 +947,7 @@ RENDERTHREAD_TEST(RenderNodeDrawable, simple) { [](RenderProperties& props, SkiaRecordingCanvas& canvas) { sk_sp bitmap(TestUtils::createBitmap(25, 25)); canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, - SkPaint()); + Paint()); canvas.drawBitmap(*bitmap, 10, 10, nullptr); }); @@ -1022,7 +1023,7 @@ TEST(RenderNodeDrawable, renderNode) { auto child = TestUtils::createSkiaNode( 10, 10, 110, 110, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { - SkPaint paint; + Paint paint; paint.setColor(SK_ColorWHITE); canvas.drawRect(0, 0, 100, 100, paint); }); @@ -1030,7 +1031,7 @@ TEST(RenderNodeDrawable, renderNode) { auto parent = TestUtils::createSkiaNode( 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, [&child](RenderProperties& props, SkiaRecordingCanvas& canvas) { - SkPaint paint; + Paint paint; paint.setColor(SK_ColorDKGRAY); canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, paint); @@ -1065,7 +1066,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, layerComposeQuality) { auto layerNode = TestUtils::createSkiaNode( 0, 0, LAYER_WIDTH, LAYER_HEIGHT, [](RenderProperties& properties, SkiaRecordingCanvas& canvas) { - canvas.drawPaint(SkPaint()); + canvas.drawPaint(Paint()); }); layerNode->animatorProperties().mutateLayerProperties().setType(LayerType::RenderLayer); diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp index 2ed1b25efc71..fcc64fdd0be6 100644 --- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp +++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp @@ -16,6 +16,7 @@ #include "tests/common/TestUtils.h" +#include #include #include #include @@ -32,7 +33,7 @@ TEST(SkiaCanvas, drawShadowLayer) { // clear to white canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrc); - SkPaint paint; + Paint paint; // it is transparent to ensure that we still draw the rect since it has a looper paint.setColor(SK_ColorTRANSPARENT); // this is how view's shadow layers are implemented diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp index 7b76bd80de4d..958baa78deab 100644 --- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp +++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp @@ -23,6 +23,7 @@ #include "AnimationContext.h" #include "DamageAccumulator.h" #include "IContextFactory.h" +#include "hwui/Paint.h" #include "SkiaCanvas.h" #include "pipeline/skia/SkiaDisplayList.h" #include "pipeline/skia/SkiaOpenGLPipeline.h" @@ -96,7 +97,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, testOnPrepareTree) { RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckOpaque) { auto halfGreenNode = TestUtils::createSkiaNode( 0, 0, 2, 2, [](RenderProperties& props, SkiaRecordingCanvas& bottomHalfGreenCanvas) { - SkPaint greenPaint; + Paint greenPaint; greenPaint.setColor(SK_ColorGREEN); greenPaint.setStyle(SkPaint::kFill_Style); bottomHalfGreenCanvas.drawRect(0, 1, 2, 2, greenPaint); @@ -294,7 +295,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, deferRenderNodeScene) { }; std::vector> nodes; - SkPaint transparentPaint; + Paint transparentPaint; transparentPaint.setAlpha(128); // backdrop diff --git a/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp index 635429dea359..eec25c6bd40d 100644 --- a/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp +++ b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp @@ -24,6 +24,7 @@ #include "DamageAccumulator.h" #include "FatalTestCanvas.h" #include "IContextFactory.h" +#include "hwui/Paint.h" #include "SkiaCanvas.h" #include "pipeline/skia/SkiaDisplayList.h" #include "pipeline/skia/SkiaPipeline.h" @@ -60,7 +61,7 @@ static void testProperty(std::function propSetupCallbac 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, [propSetupCallback](RenderProperties& props, SkiaRecordingCanvas& canvas) { propSetupCallback(props); - SkPaint paint; + Paint paint; paint.setColor(SK_ColorWHITE); canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, paint); }); diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h index ebf2343c5518..e2fdf2fcb5a5 100644 --- a/libs/hwui/utils/PaintUtils.h +++ b/libs/hwui/utils/PaintUtils.h @@ -67,29 +67,6 @@ public: return (filter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag) == 0; } - struct TextShadow { - SkScalar radius; - float dx; - float dy; - SkColor color; - }; - - static inline bool getTextShadow(const SkPaint* paint, TextShadow* textShadow) { - SkDrawLooper::BlurShadowRec blur; - if (paint && paint->getLooper() && paint->getLooper()->asABlurShadow(&blur)) { - if (textShadow) { - textShadow->radius = Blur::convertSigmaToRadius(blur.fSigma); - textShadow->dx = blur.fOffset.fX; - textShadow->dy = blur.fOffset.fY; - textShadow->color = blur.fColor; - } - return true; - } - return false; - } - - static inline bool hasTextShadow(const SkPaint* paint) { return getTextShadow(paint, nullptr); } - static inline SkBlendMode getBlendModeDirect(const SkPaint* paint) { return paint ? paint->getBlendMode() : SkBlendMode::kSrcOver; } -- cgit v1.2.3 From 5d0ca63d9c2b15b7a61b5e523790e490d23b7500 Mon Sep 17 00:00:00 2001 From: Derek Sollenberger Date: Fri, 19 Jul 2019 16:17:12 -0400 Subject: Isolate the graphics classes into their own static lib Update additional headers to include-what-you-use to resolve compiler errors related to moving include paths. Test: CtsUiRenderingTestCases Change-Id: I230408a53610ad1926f9e46d5aff66f52fec4a3a --- libs/hwui/RenderNode.cpp | 1 + libs/hwui/pipeline/skia/SkiaDisplayList.cpp | 1 + libs/hwui/pipeline/skia/SkiaDisplayList.h | 4 +++- libs/hwui/renderthread/VulkanManager.h | 1 + 4 files changed, 6 insertions(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index 8aee8f5b1848..8eb5e3d3dfbc 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -20,6 +20,7 @@ #include "Debug.h" #include "TreeInfo.h" #include "VectorDrawable.h" +#include "private/hwui/WebViewFunctor.h" #ifdef __ANDROID__ #include "renderthread/CanvasContext.h" #else diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp index c7d5f3193f45..d7076d4cf424 100644 --- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp +++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp @@ -15,6 +15,7 @@ */ #include "SkiaDisplayList.h" +#include "FunctorDrawable.h" #include "DumpOpsCanvas.h" #ifdef __ANDROID__ // Layoutlib does not support SkiaPipeline diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h index b79103787023..e3c3273a726a 100644 --- a/libs/hwui/pipeline/skia/SkiaDisplayList.h +++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h @@ -16,7 +16,6 @@ #pragma once -#include "FunctorDrawable.h" #include "RecordingCanvas.h" #include "RenderNodeDrawable.h" #include "TreeInfo.h" @@ -34,6 +33,7 @@ class CanvasContext; } class Outline; +struct WebViewSyncData; namespace VectorDrawable { class Tree; @@ -42,6 +42,8 @@ typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot; namespace skiapipeline { +class FunctorDrawable; + class SkiaDisplayList { public: size_t getUsedSize() { return allocator.usedSize() + mDisplayList.usedSize(); } diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h index ec217c0fcbf4..4c6a75504cd0 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -31,6 +31,7 @@ #include "Frame.h" #include "IRenderPipeline.h" #include "VulkanSurface.h" +#include "private/hwui/DrawVkInfo.h" class GrVkExtensions; -- cgit v1.2.3 From 694f3e4a5518e8e3fe025a5a04e9088fe41df97d Mon Sep 17 00:00:00 2001 From: Stan Iliev Date: Mon, 29 Jul 2019 17:00:49 -0400 Subject: Improve integer aligned coordinate detection 65.9998 should be detected as integer aligned. Before this CL only 65.0001 was detected as an integer. Test: Pass TextureViewTest#testSamplingWithTransform Bug: 137682518 Change-Id: I65e650f50040ab56d2237c14d5d8ee191349b93e --- libs/hwui/pipeline/skia/LayerDrawable.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'libs') diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp index 96b17e1d7975..b0173846e582 100644 --- a/libs/hwui/pipeline/skia/LayerDrawable.cpp +++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp @@ -33,6 +33,10 @@ void LayerDrawable::onDraw(SkCanvas* canvas) { } } +static inline SkScalar isIntegerAligned(SkScalar x) { + return fabsf(roundf(x) - x) <= NON_ZERO_EPSILON; +} + // Disable filtering when there is no scaling in screen coordinates and the corners have the same // fraction (for translate) or zero fraction (for any other rect-to-rect transform). static bool shouldFilterRect(const SkMatrix& matrix, const SkRect& srcRect, const SkRect& dstRect) { @@ -62,10 +66,10 @@ static bool shouldFilterRect(const SkMatrix& matrix, const SkRect& srcRect, cons if (requiresIntegerTranslate) { // Device rect and source rect should be integer aligned to ensure there's no difference // in how nearest-neighbor sampling is resolved. - return !(MathUtils::isZero(SkScalarFraction(srcRect.x())) && - MathUtils::isZero(SkScalarFraction(srcRect.y())) && - MathUtils::isZero(SkScalarFraction(dstDevRect.x())) && - MathUtils::isZero(SkScalarFraction(dstDevRect.y()))); + return !(isIntegerAligned(srcRect.x()) && + isIntegerAligned(srcRect.y()) && + isIntegerAligned(dstDevRect.x()) && + isIntegerAligned(dstDevRect.y())); } else { // As long as src and device rects are translated by the same fractional amount, // filtering won't be needed -- cgit v1.2.3 From 85d6f10a0f4f4b563ce9bea9133b5662c35c94eb Mon Sep 17 00:00:00 2001 From: Fedor Kudasov Date: Wed, 31 Jul 2019 15:58:16 +0100 Subject: Include more precise dependencies Bug: 117921091 Test: all tests should pass Change-Id: I3be0eeef413aaf69a3a9e27e0bc7f2841a1f5f95 --- libs/hwui/renderthread/CanvasContext.h | 1 + libs/hwui/renderthread/RenderThread.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 0910828da4f8..369b84e39aa0 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -22,6 +22,7 @@ #include "FrameMetricsReporter.h" #include "IContextFactory.h" #include "IRenderPipeline.h" +#include "JankTracker.h" #include "LayerUpdateQueue.h" #include "Lighting.h" #include "ReliableSurface.h" diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index c96e284df6b4..df7eeb398847 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -19,8 +19,8 @@ #include "RenderTask.h" -#include "../JankTracker.h" #include "CacheManager.h" +#include "ProfileDataContainer.h" #include "TimeLord.h" #include "WebViewFunctorManager.h" #include "thread/ThreadBase.h" -- cgit v1.2.3 From e690983f839c81c3dfd7439e780631c45de9d1ee Mon Sep 17 00:00:00 2001 From: Fedor Kudasov Date: Wed, 31 Jul 2019 16:06:29 +0100 Subject: Remove unused method Bug: 117921091 Test: all tests should pass Change-Id: I8830e061e30a73e1ea2accda8c9a4268818f5b80 --- libs/hwui/renderthread/CanvasContext.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'libs') diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 0910828da4f8..a44edaaccb9f 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -150,8 +150,6 @@ public: void setContentDrawBounds(const Rect& bounds) { mContentDrawBounds = bounds; } - RenderState& getRenderState() { return mRenderThread.renderState(); } - void addFrameMetricsObserver(FrameMetricsObserver* observer) { if (mFrameMetricsReporter.get() == nullptr) { mFrameMetricsReporter.reset(new FrameMetricsReporter()); -- cgit v1.2.3 From e2617db752119a4aed7feaafb9c910499c458e93 Mon Sep 17 00:00:00 2001 From: Nathaniel Nifong Date: Thu, 25 Jul 2019 10:44:08 -0400 Subject: use mskp extension to differentiate multi frame files. Test: Captured a 1-frame, and multi-frame file Change-Id: Id8583c163af0ee0a0ac8fe12aee92d2cfce272d1 --- libs/hwui/tests/scripts/skp-capture.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'libs') diff --git a/libs/hwui/tests/scripts/skp-capture.sh b/libs/hwui/tests/scripts/skp-capture.sh index aad31fcc8eb9..4b46fbf86818 100755 --- a/libs/hwui/tests/scripts/skp-capture.sh +++ b/libs/hwui/tests/scripts/skp-capture.sh @@ -29,10 +29,14 @@ fi phase1_timeout_seconds=60 phase2_timeout_seconds=300 package="$1" -filename="$(date '+%H%M%S').skp" +extension="skp" +if (( "$2" > 1 )); then # 2nd arg is number of frames + extension="mskp" # use different extension for multi frame files. +fi +filename="$(date '+%H%M%S').${extension}" remote_path="/data/data/${package}/cache/${filename}" local_path_prefix="$(date '+%Y-%m-%d_%H%M%S')_${package}" -local_path="${local_path_prefix}.skp" +local_path="${local_path_prefix}.${extension}" enable_capture_key='debug.hwui.capture_skp_enabled' enable_capture_value=$(adb shell "getprop '${enable_capture_key}'") -- cgit v1.2.3 From 7203e1f55a57bf809c3d07a391b82c364fb60b7e Mon Sep 17 00:00:00 2001 From: Stan Iliev Date: Thu, 25 Jul 2019 13:12:02 -0400 Subject: Add GPU draw stats to gfxinfo and GraphicsStatsService Test: ran dumpsys with gfxinfo and graphicsstats Change-Id: Id9950de87dc4343c6878baa6a6dd42fbc8aeddef --- libs/hwui/FrameInfo.cpp | 3 +- libs/hwui/FrameInfo.h | 9 +++++ libs/hwui/JankTracker.cpp | 8 +++++ libs/hwui/JankTracker.h | 1 + libs/hwui/ProfileData.cpp | 48 +++++++++++++++++++++++++ libs/hwui/ProfileData.h | 11 ++++++ libs/hwui/protos/graphicsstats.proto | 3 ++ libs/hwui/renderthread/CanvasContext.cpp | 19 +++++++++- libs/hwui/renderthread/CanvasContext.h | 3 ++ libs/hwui/renderthread/ReliableSurface.h | 15 ++++++++ libs/hwui/service/GraphicsStatsService.cpp | 56 ++++++++++++++++++++++++++++++ 11 files changed, 174 insertions(+), 2 deletions(-) (limited to 'libs') diff --git a/libs/hwui/FrameInfo.cpp b/libs/hwui/FrameInfo.cpp index 71cc9a81a09f..0698775b0021 100644 --- a/libs/hwui/FrameInfo.cpp +++ b/libs/hwui/FrameInfo.cpp @@ -37,13 +37,14 @@ const std::string FrameInfoNames[] = { "FrameCompleted", "DequeueBufferDuration", "QueueBufferDuration", + "GpuCompleted", }; static_assert((sizeof(FrameInfoNames) / sizeof(FrameInfoNames[0])) == static_cast(FrameInfoIndex::NumIndexes), "size mismatch: FrameInfoNames doesn't match the enum!"); -static_assert(static_cast(FrameInfoIndex::NumIndexes) == 16, +static_assert(static_cast(FrameInfoIndex::NumIndexes) == 17, "Must update value in FrameMetrics.java#FRAME_STATS_COUNT (and here)"); void FrameInfo::importUiThreadInfo(int64_t* info) { diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h index b75192ff8476..51674fbd557e 100644 --- a/libs/hwui/FrameInfo.h +++ b/libs/hwui/FrameInfo.h @@ -51,6 +51,8 @@ enum class FrameInfoIndex { DequeueBufferDuration, QueueBufferDuration, + GpuCompleted, + // Must be the last value! // Also must be kept in sync with FrameMetrics.java#FRAME_STATS_COUNT NumIndexes @@ -143,6 +145,13 @@ public: return duration(FrameInfoIndex::IntendedVsync, FrameInfoIndex::FrameCompleted); } + inline int64_t gpuDrawTime() const { + // GPU start time is approximated to the moment before swapBuffer is invoked. + // We could add an EGLSyncKHR fence at the beginning of the frame, but that is an overhead. + int64_t endTime = get(FrameInfoIndex::GpuCompleted); + return endTime > 0 ? endTime - get(FrameInfoIndex::SwapBuffers) : -1; + } + inline int64_t& set(FrameInfoIndex index) { return mFrameInfo[static_cast(index)]; } inline int64_t get(FrameInfoIndex index) const { diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp index 53c5ad8eff3c..eae3584465e4 100644 --- a/libs/hwui/JankTracker.cpp +++ b/libs/hwui/JankTracker.cpp @@ -232,5 +232,13 @@ void JankTracker::reset() { : FrameInfoIndex::IntendedVsync; } +void JankTracker::finishGpuDraw(const FrameInfo& frame) { + int64_t totalGPUDrawTime = frame.gpuDrawTime(); + if (totalGPUDrawTime >= 0) { + mData->reportGPUFrame(totalGPUDrawTime); + (*mGlobalData)->reportGPUFrame(totalGPUDrawTime); + } +} + } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/JankTracker.h b/libs/hwui/JankTracker.h index 110211eda23a..08059268f03e 100644 --- a/libs/hwui/JankTracker.h +++ b/libs/hwui/JankTracker.h @@ -58,6 +58,7 @@ public: FrameInfo* startFrame() { return &mFrames.next(); } void finishFrame(const FrameInfo& frame); + void finishGpuDraw(const FrameInfo& frame); void dumpStats(int fd) { dumpData(fd, &mDescription, mData.get()); } void dumpFrames(int fd); diff --git a/libs/hwui/ProfileData.cpp b/libs/hwui/ProfileData.cpp index c7f92321b090..7921662b213c 100644 --- a/libs/hwui/ProfileData.cpp +++ b/libs/hwui/ProfileData.cpp @@ -98,6 +98,10 @@ void ProfileData::mergeWith(const ProfileData& other) { if (mStatStartTime > other.mStatStartTime || mStatStartTime == 0) { mStatStartTime = other.mStatStartTime; } + for (size_t i = 0; i < other.mGPUFrameCounts.size(); i++) { + mGPUFrameCounts[i] >>= divider; + mGPUFrameCounts[i] += other.mGPUFrameCounts[i]; + } } void ProfileData::dump(int fd) const { @@ -117,6 +121,14 @@ void ProfileData::dump(int fd) const { histogramForEach([fd](HistogramEntry entry) { dprintf(fd, " %ums=%u", entry.renderTimeMs, entry.frameCount); }); + dprintf(fd, "\n50th gpu percentile: %ums", findGPUPercentile(50)); + dprintf(fd, "\n90th gpu percentile: %ums", findGPUPercentile(90)); + dprintf(fd, "\n95th gpu percentile: %ums", findGPUPercentile(95)); + dprintf(fd, "\n99th gpu percentile: %ums", findGPUPercentile(99)); + dprintf(fd, "\nGPU HISTOGRAM:"); + histogramGPUForEach([fd](HistogramEntry entry) { + dprintf(fd, " %ums=%u", entry.renderTimeMs, entry.frameCount); + }); } uint32_t ProfileData::findPercentile(int percentile) const { @@ -140,6 +152,7 @@ uint32_t ProfileData::findPercentile(int percentile) const { void ProfileData::reset() { mJankTypeCounts.fill(0); mFrameCounts.fill(0); + mGPUFrameCounts.fill(0); mSlowFrameCounts.fill(0); mTotalFrameCount = 0; mJankFrameCount = 0; @@ -167,5 +180,40 @@ void ProfileData::histogramForEach(const std::function& ca } } +uint32_t ProfileData::findGPUPercentile(int percentile) const { + uint32_t totalGPUFrameCount = 0; // this is usually mTotalFrameCount - 3. + for (int i = mGPUFrameCounts.size() - 1; i >= 0; i--) { + totalGPUFrameCount += mGPUFrameCounts[i]; + } + int pos = percentile * totalGPUFrameCount / 100; + int remaining = totalGPUFrameCount - pos; + for (int i = mGPUFrameCounts.size() - 1; i >= 0; i--) { + remaining -= mGPUFrameCounts[i]; + if (remaining <= 0) { + return GPUFrameTimeForFrameCountIndex(i); + } + } + return 0; +} + +uint32_t ProfileData::GPUFrameTimeForFrameCountIndex(uint32_t index) { + return index != 25 ? index + 1 : 4950; +} + +void ProfileData::reportGPUFrame(int64_t duration) { + uint32_t index = static_cast(ns2ms(duration)); + if (index > 25) { + index = 25; + } + + mGPUFrameCounts[index]++; +} + +void ProfileData::histogramGPUForEach(const std::function& callback) const { + for (size_t i = 0; i < mGPUFrameCounts.size(); i++) { + callback(HistogramEntry{GPUFrameTimeForFrameCountIndex(i), mGPUFrameCounts[i]}); + } +} + } /* namespace uirenderer */ } /* namespace android */ \ No newline at end of file diff --git a/libs/hwui/ProfileData.h b/libs/hwui/ProfileData.h index 564920b60328..ccbffc6f136e 100644 --- a/libs/hwui/ProfileData.h +++ b/libs/hwui/ProfileData.h @@ -54,8 +54,10 @@ public: void mergeWith(const ProfileData& other); void dump(int fd) const; uint32_t findPercentile(int percentile) const; + uint32_t findGPUPercentile(int percentile) const; void reportFrame(int64_t duration); + void reportGPUFrame(int64_t duration); void reportJank() { mJankFrameCount++; } void reportJankType(JankType type) { mJankTypeCounts[static_cast(type)]++; } @@ -69,15 +71,21 @@ public: uint32_t frameCount; }; void histogramForEach(const std::function& callback) const; + void histogramGPUForEach(const std::function& callback) const; constexpr static int HistogramSize() { return std::tuple_size::value + std::tuple_size::value; } + constexpr static int GPUHistogramSize() { + return std::tuple_size::value; + } + // Visible for testing static uint32_t frameTimeForFrameCountIndex(uint32_t index); static uint32_t frameTimeForSlowFrameCountIndex(uint32_t index); + static uint32_t GPUFrameTimeForFrameCountIndex(uint32_t index); private: // Open our guts up to unit tests @@ -88,6 +96,9 @@ private: std::array mFrameCounts; // Holds a histogram of frame times in 50ms increments from 150ms to 5s std::array mSlowFrameCounts; + // Holds a histogram of GPU draw times in 1ms increments. Frames longer than 25ms are placed in + // last bucket. + std::array mGPUFrameCounts; uint32_t mTotalFrameCount; uint32_t mJankFrameCount; diff --git a/libs/hwui/protos/graphicsstats.proto b/libs/hwui/protos/graphicsstats.proto index 1226d44ceb85..0cd5c6228504 100644 --- a/libs/hwui/protos/graphicsstats.proto +++ b/libs/hwui/protos/graphicsstats.proto @@ -46,6 +46,9 @@ message GraphicsStatsProto { // The frame time histogram for the package repeated GraphicsStatsHistogramBucketProto histogram = 6; + + // The gpu frame time histogram for the package + repeated GraphicsStatsHistogramBucketProto gpu_histogram = 7; } message GraphicsStatsJankSummaryProto { diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index d19351bf1db9..88a0c6ea3085 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -147,6 +147,7 @@ void CanvasContext::setSurface(sp&& surface) { mNativeSurface = new ReliableSurface{std::move(surface)}; // TODO: Fix error handling & re-shorten timeout mNativeSurface->setDequeueTimeout(4000_ms); + mNativeSurface->enableFrameTimestamps(true); } else { mNativeSurface = nullptr; } @@ -294,6 +295,7 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy // just keep using the previous frame's structure instead if (!wasSkipped(mCurrentFrameInfo)) { mCurrentFrameInfo = mJankTracker.startFrame(); + mLast4FrameInfos.next().first = mCurrentFrameInfo; } mCurrentFrameInfo->importUiThreadInfo(uiFrameInfo); mCurrentFrameInfo->set(FrameInfoIndex::SyncQueued) = syncQueued; @@ -445,7 +447,7 @@ void CanvasContext::draw() { mContentDrawBounds, mOpaque, mLightInfo, mRenderNodes, &(profiler())); - int64_t frameCompleteNr = mFrameCompleteCallbacks.size() ? getFrameNumber() : -1; + int64_t frameCompleteNr = getFrameNumber(); waitOnFences(); @@ -500,11 +502,13 @@ void CanvasContext::draw() { } mCurrentFrameInfo->set(FrameInfoIndex::DequeueBufferDuration) = swap.dequeueDuration; mCurrentFrameInfo->set(FrameInfoIndex::QueueBufferDuration) = swap.queueDuration; + mLast4FrameInfos[-1].second = frameCompleteNr; mHaveNewSurface = false; mFrameNumber = -1; } else { mCurrentFrameInfo->set(FrameInfoIndex::DequeueBufferDuration) = 0; mCurrentFrameInfo->set(FrameInfoIndex::QueueBufferDuration) = 0; + mLast4FrameInfos[-1].second = -1; } // TODO: Use a fence for real completion? @@ -537,6 +541,19 @@ void CanvasContext::draw() { mFrameMetricsReporter->reportFrameMetrics(mCurrentFrameInfo->data()); } + if (mLast4FrameInfos.size() == mLast4FrameInfos.capacity()) { + // By looking 4 frames back, we guarantee all SF stats are available. There are at + // most 3 buffers in BufferQueue. Surface object keeps stats for the last 8 frames. + FrameInfo* forthBehind = mLast4FrameInfos.front().first; + int64_t composedFrameId = mLast4FrameInfos.front().second; + nsecs_t acquireTime = -1; + mNativeSurface->getFrameTimestamps(composedFrameId, nullptr, &acquireTime, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr); + // Ignore default -1, NATIVE_WINDOW_TIMESTAMP_INVALID and NATIVE_WINDOW_TIMESTAMP_PENDING + forthBehind->set(FrameInfoIndex::GpuCompleted) = acquireTime > 0 ? acquireTime : -1; + mJankTracker.finishGpuDraw(*forthBehind); + } + GpuMemoryTracker::onFrameCompleted(); } diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index f9b93107de7b..8a76d6b3fc7a 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -29,6 +29,7 @@ #include "RenderNode.h" #include "renderthread/RenderTask.h" #include "renderthread/RenderThread.h" +#include "utils/RingBuffer.h" #include #include @@ -41,6 +42,7 @@ #include #include #include +#include #include namespace android { @@ -260,6 +262,7 @@ private: std::vector> mRenderNodes; FrameInfo* mCurrentFrameInfo = nullptr; + RingBuffer, 4> mLast4FrameInfos; std::string mName; JankTracker mJankTracker; FrameInfoVisualizer mProfiler; diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h index 41fc35eca9f7..7f1a0781dd87 100644 --- a/libs/hwui/renderthread/ReliableSurface.h +++ b/libs/hwui/renderthread/ReliableSurface.h @@ -49,6 +49,21 @@ public: return ret; } + status_t getFrameTimestamps(uint64_t frameNumber, + nsecs_t* outRequestedPresentTime, nsecs_t* outAcquireTime, + nsecs_t* outLatchTime, nsecs_t* outFirstRefreshStartTime, + nsecs_t* outLastRefreshStartTime, nsecs_t* outGlCompositionDoneTime, + nsecs_t* outDisplayPresentTime, nsecs_t* outDequeueReadyTime, + nsecs_t* outReleaseTime) { + return mSurface->getFrameTimestamps(frameNumber, outRequestedPresentTime, outAcquireTime, + outLatchTime, outFirstRefreshStartTime, outLastRefreshStartTime, + outGlCompositionDoneTime, outDisplayPresentTime, outDequeueReadyTime, outReleaseTime); + } + + void enableFrameTimestamps(bool enable) { + return mSurface->enableFrameTimestamps(enable); + } + private: const sp mSurface; diff --git a/libs/hwui/service/GraphicsStatsService.cpp b/libs/hwui/service/GraphicsStatsService.cpp index 8a16b2077f6f..8b5912b2081a 100644 --- a/libs/hwui/service/GraphicsStatsService.cpp +++ b/libs/hwui/service/GraphicsStatsService.cpp @@ -40,6 +40,7 @@ constexpr int32_t sHeaderSize = 4; static_assert(sizeof(sCurrentFileVersion) == sHeaderSize, "Header size is wrong"); constexpr int sHistogramSize = ProfileData::HistogramSize(); +constexpr int sGPUHistogramSize = ProfileData::GPUHistogramSize(); static bool mergeProfileDataIntoProto(protos::GraphicsStatsProto* proto, const std::string& package, int64_t versionCode, @@ -211,6 +212,37 @@ bool mergeProfileDataIntoProto(protos::GraphicsStatsProto* proto, const std::str bucket->set_frame_count(bucket->frame_count() + entry.frameCount); index++; }); + if (hitMergeError) return false; + // fill in GPU frame time histogram + creatingHistogram = false; + if (proto->gpu_histogram_size() == 0) { + proto->mutable_gpu_histogram()->Reserve(sGPUHistogramSize); + creatingHistogram = true; + } else if (proto->gpu_histogram_size() != sGPUHistogramSize) { + ALOGE("GPU histogram size mismatch, proto is %d expected %d", proto->gpu_histogram_size(), + sGPUHistogramSize); + return false; + } + index = 0; + data->histogramGPUForEach([&](ProfileData::HistogramEntry entry) { + if (hitMergeError) return; + + protos::GraphicsStatsHistogramBucketProto* bucket; + if (creatingHistogram) { + bucket = proto->add_gpu_histogram(); + bucket->set_render_millis(entry.renderTimeMs); + } else { + bucket = proto->mutable_gpu_histogram(index); + if (bucket->render_millis() != static_cast(entry.renderTimeMs)) { + ALOGW("GPU frame time mistmatch %d vs. %u", bucket->render_millis(), + entry.renderTimeMs); + hitMergeError = true; + return; + } + } + bucket->set_frame_count(bucket->frame_count() + entry.frameCount); + index++; + }); return !hitMergeError; } @@ -226,6 +258,22 @@ static int32_t findPercentile(protos::GraphicsStatsProto* proto, int percentile) return 0; } +static int32_t findGPUPercentile(protos::GraphicsStatsProto* proto, int percentile) { + uint32_t totalGPUFrameCount = 0; // this is usually proto->summary().total_frames() - 3. + for (auto it = proto->gpu_histogram().rbegin(); it != proto->gpu_histogram().rend(); ++it) { + totalGPUFrameCount += it->frame_count(); + } + int32_t pos = percentile * totalGPUFrameCount / 100; + int32_t remaining = totalGPUFrameCount - pos; + for (auto it = proto->gpu_histogram().rbegin(); it != proto->gpu_histogram().rend(); ++it) { + remaining -= it->frame_count(); + if (remaining <= 0) { + return it->render_millis(); + } + } + return 0; +} + void dumpAsTextToFd(protos::GraphicsStatsProto* proto, int fd) { // This isn't a full validation, just enough that we can deref at will if (proto->package_name().empty() || !proto->has_summary()) { @@ -255,6 +303,14 @@ void dumpAsTextToFd(protos::GraphicsStatsProto* proto, int fd) { for (const auto& it : proto->histogram()) { dprintf(fd, " %dms=%d", it.render_millis(), it.frame_count()); } + dprintf(fd, "\n50th gpu percentile: %dms", findGPUPercentile(proto, 50)); + dprintf(fd, "\n90th gpu percentile: %dms", findGPUPercentile(proto, 90)); + dprintf(fd, "\n95th gpu percentile: %dms", findGPUPercentile(proto, 95)); + dprintf(fd, "\n99th gpu percentile: %dms", findGPUPercentile(proto, 99)); + dprintf(fd, "\nGPU HISTOGRAM:"); + for (const auto& it : proto->gpu_histogram()) { + dprintf(fd, " %dms=%d", it.render_millis(), it.frame_count()); + } dprintf(fd, "\n"); } -- cgit v1.2.3 From 883c9d9dee640dd8f73852fddc7da65c1ba0d579 Mon Sep 17 00:00:00 2001 From: Stan Iliev Date: Thu, 1 Aug 2019 14:41:52 -0400 Subject: TextureVuew rebind GL texture, if AHB content has changed Bug: 138674291 Test: Ran apps with TextureView and CTS Change-Id: Ieecf7daf160761de719356644ddaeda8f9c068e1 --- libs/hwui/surfacetexture/ImageConsumer.cpp | 18 +++++++++++++++--- libs/hwui/surfacetexture/ImageConsumer.h | 5 ----- 2 files changed, 15 insertions(+), 8 deletions(-) (limited to 'libs') diff --git a/libs/hwui/surfacetexture/ImageConsumer.cpp b/libs/hwui/surfacetexture/ImageConsumer.cpp index bae616bbc636..17ee17d5cd1d 100644 --- a/libs/hwui/surfacetexture/ImageConsumer.cpp +++ b/libs/hwui/surfacetexture/ImageConsumer.cpp @@ -71,13 +71,16 @@ public: void makeImage(sp& graphicBuffer, android_dataspace dataspace, GrContext* context); + void newBufferContent(GrContext* context); + private: // The only way to invoke dtor is with unref, when mUsageCount is 0. ~AutoBackendTextureRelease() {} GrBackendTexture mBackendTexture; GrAHardwareBufferUtils::DeleteImageProc mDeleteProc; - GrAHardwareBufferUtils::DeleteImageCtx mDeleteCtx; + GrAHardwareBufferUtils::UpdateImageProc mUpdateProc; + GrAHardwareBufferUtils::TexImageCtx mImageCtx; // Starting with refcount 1, because the first ref is held by SurfaceTexture. Additional refs // are held by SkImages. @@ -101,7 +104,8 @@ AutoBackendTextureRelease::AutoBackendTextureRelease(GrContext* context, Graphic buffer->getWidth(), buffer->getHeight(), &mDeleteProc, - &mDeleteCtx, + &mUpdateProc, + &mImageCtx, createProtectedImage, backendFormat, false); @@ -123,7 +127,7 @@ void AutoBackendTextureRelease::unref(bool releaseImage) { mUsageCount--; if (mUsageCount <= 0) { if (mBackendTexture.isValid()) { - mDeleteProc(mDeleteCtx); + mDeleteProc(mImageCtx); mBackendTexture = {}; } delete this; @@ -154,6 +158,12 @@ void AutoBackendTextureRelease::makeImage(sp& graphicBuffer, } } +void AutoBackendTextureRelease::newBufferContent(GrContext* context) { + if (mBackendTexture.isValid()) { + mUpdateProc(mImageCtx, context); + } +} + void ImageConsumer::ImageSlot::createIfNeeded(sp graphicBuffer, android_dataspace dataspace, bool forceCreate, GrContext* context) { @@ -166,6 +176,8 @@ void ImageConsumer::ImageSlot::createIfNeeded(sp graphicBuffer, if (!mTextureRelease) { mTextureRelease = new AutoBackendTextureRelease(context, graphicBuffer.get()); + } else { + mTextureRelease->newBufferContent(context); } mDataspace = dataspace; diff --git a/libs/hwui/surfacetexture/ImageConsumer.h b/libs/hwui/surfacetexture/ImageConsumer.h index 2fdece989876..3e2a91a251f7 100644 --- a/libs/hwui/surfacetexture/ImageConsumer.h +++ b/libs/hwui/surfacetexture/ImageConsumer.h @@ -26,11 +26,6 @@ #include #include -namespace GrAHardwareBufferUtils { -typedef void* DeleteImageCtx; -typedef void (*DeleteImageProc)(DeleteImageCtx); -} - namespace android { namespace uirenderer { -- cgit v1.2.3 From 15e58b4023dda977836dfcc89bdc657a8b2f4a64 Mon Sep 17 00:00:00 2001 From: Fedor Kudasov Date: Thu, 4 Jul 2019 17:52:39 +0100 Subject: Add hostgraphics classes required for ImageReader Bug: 117921091 Test: all tests should pass Change-Id: I5d60a1177d43eb8dec08403cf34936007b8a267f (cherry picked from commit c1409f44f82313902a690fb257ee271dc16385a0) --- libs/hostgraphics/Android.bp | 7 +++ libs/hostgraphics/Fence.cpp | 23 ++++++++ libs/hostgraphics/HostBufferQueue.cpp | 69 ++++++++++++++++++++++++ libs/hostgraphics/PublicFormat.cpp | 33 ++++++++++++ libs/hostgraphics/gui/BufferItem.h | 65 ++++++++++++++++++++++ libs/hostgraphics/gui/BufferItemConsumer.h | 75 ++++++++++++++++++++++++++ libs/hostgraphics/gui/BufferQueue.h | 37 +++++++++++++ libs/hostgraphics/gui/ConsumerBase.h | 37 +++++++++++++ libs/hostgraphics/gui/IGraphicBufferConsumer.h | 65 ++++++++++++++++++++++ libs/hostgraphics/gui/IGraphicBufferProducer.h | 2 + libs/hostgraphics/ui/Fence.h | 72 +++++++++++++++++++++++++ libs/hostgraphics/ui/GraphicBuffer.h | 64 ++++++++++++++++++++++ 12 files changed, 549 insertions(+) create mode 100644 libs/hostgraphics/Fence.cpp create mode 100644 libs/hostgraphics/HostBufferQueue.cpp create mode 100644 libs/hostgraphics/PublicFormat.cpp create mode 100644 libs/hostgraphics/gui/BufferItem.h create mode 100644 libs/hostgraphics/gui/BufferItemConsumer.h create mode 100644 libs/hostgraphics/gui/BufferQueue.h create mode 100644 libs/hostgraphics/gui/ConsumerBase.h create mode 100644 libs/hostgraphics/gui/IGraphicBufferConsumer.h create mode 100644 libs/hostgraphics/ui/Fence.h create mode 100644 libs/hostgraphics/ui/GraphicBuffer.h (limited to 'libs') diff --git a/libs/hostgraphics/Android.bp b/libs/hostgraphics/Android.bp index aedb7522ab08..e713b98b867e 100644 --- a/libs/hostgraphics/Android.bp +++ b/libs/hostgraphics/Android.bp @@ -1,8 +1,15 @@ cc_library_host_static { name: "libhostgraphics", + cflags: [ + "-Wno-unused-parameter", + ], + srcs: [ ":libui_host_common", + "Fence.cpp", + "HostBufferQueue.cpp", + "PublicFormat.cpp", ], include_dirs: [ diff --git a/libs/hostgraphics/Fence.cpp b/libs/hostgraphics/Fence.cpp new file mode 100644 index 000000000000..9e54816651c4 --- /dev/null +++ b/libs/hostgraphics/Fence.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2019 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 + +namespace android { + +const sp Fence::NO_FENCE = sp(new Fence); + +} // namespace android \ No newline at end of file diff --git a/libs/hostgraphics/HostBufferQueue.cpp b/libs/hostgraphics/HostBufferQueue.cpp new file mode 100644 index 000000000000..ec304378c6c4 --- /dev/null +++ b/libs/hostgraphics/HostBufferQueue.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2019 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 + +namespace android { + +class HostBufferQueue : public IGraphicBufferProducer, public IGraphicBufferConsumer { +public: + HostBufferQueue() : mWidth(0), mHeight(0) { } + + virtual status_t setConsumerIsProtected(bool isProtected) { return OK; } + + virtual status_t detachBuffer(int slot) { return OK; } + + virtual status_t getReleasedBuffers(uint64_t* slotMask) { return OK; } + + virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h) { + mWidth = w; + mHeight = h; + mBuffer = sp(new GraphicBuffer(mWidth, mHeight)); + return OK; + } + + virtual status_t setDefaultBufferFormat(PixelFormat defaultFormat) { return OK; } + + virtual status_t setDefaultBufferDataSpace(android_dataspace defaultDataSpace) { return OK; } + + virtual status_t discardFreeBuffers() { return OK; } + + virtual status_t acquireBuffer(BufferItem* buffer, nsecs_t presentWhen, + uint64_t maxFrameNumber = 0) { + buffer->mGraphicBuffer = mBuffer; + buffer->mSlot = 0; + return OK; + } + + virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) { return OK; } + + virtual status_t setConsumerUsageBits(uint64_t usage) { return OK; } +private: + sp mBuffer; + uint32_t mWidth; + uint32_t mHeight; +}; + +void BufferQueue::createBufferQueue(sp* outProducer, + sp* outConsumer) { + + sp obj(new HostBufferQueue()); + + *outProducer = obj; + *outConsumer = obj; +} + +} // namespace android diff --git a/libs/hostgraphics/PublicFormat.cpp b/libs/hostgraphics/PublicFormat.cpp new file mode 100644 index 000000000000..af6d2738c801 --- /dev/null +++ b/libs/hostgraphics/PublicFormat.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2019 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 + +namespace android { + +android_dataspace mapPublicFormatToHalDataspace(PublicFormat f) { + return static_cast(0); +} + +int mapPublicFormatToHalFormat(PublicFormat f) { + return static_cast(f); +} + +PublicFormat mapHalFormatDataspaceToPublicFormat(int format, android_dataspace dataSpace) { + return static_cast(format); +} + +} // namespace android \ No newline at end of file diff --git a/libs/hostgraphics/gui/BufferItem.h b/libs/hostgraphics/gui/BufferItem.h new file mode 100644 index 000000000000..01409e19c715 --- /dev/null +++ b/libs/hostgraphics/gui/BufferItem.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2019 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_GUI_BUFFERITEM_H +#define ANDROID_GUI_BUFFERITEM_H + +#include +#include + +#include + +#include + +namespace android { + +class Fence; +class GraphicBuffer; + +// The only thing we need here for layoutlib is mGraphicBuffer. The rest of the fields are added +// just to satisfy the calls from the android_media_ImageReader.h + +class BufferItem { +public: + enum { INVALID_BUFFER_SLOT = -1 }; + + BufferItem() : mGraphicBuffer(nullptr), mFence(Fence::NO_FENCE) {} + ~BufferItem() {} + + sp mGraphicBuffer; + + sp mFence; + + Rect mCrop; + + uint32_t mTransform; + + uint32_t mScalingMode; + + int64_t mTimestamp; + + android_dataspace mDataSpace; + + uint64_t mFrameNumber; + + int mSlot; + + bool mTransformToDisplayInverse; +}; + +} + +#endif // ANDROID_GUI_BUFFERITEM_H diff --git a/libs/hostgraphics/gui/BufferItemConsumer.h b/libs/hostgraphics/gui/BufferItemConsumer.h new file mode 100644 index 000000000000..707b313eb102 --- /dev/null +++ b/libs/hostgraphics/gui/BufferItemConsumer.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2019 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_GUI_BUFFERITEMCONSUMER_H +#define ANDROID_GUI_BUFFERITEMCONSUMER_H + +#include + +#include +#include + +namespace android { + +class BufferItemConsumer : public ConsumerBase { +public: + BufferItemConsumer( + const sp& consumer, + uint64_t consumerUsage, + int bufferCount, + bool controlledByApp) : mConsumer(consumer) { + } + + status_t acquireBuffer(BufferItem *item, nsecs_t presentWhen, bool waitForFence = true) { + return mConsumer->acquireBuffer(item, presentWhen, 0); + } + + status_t releaseBuffer( + const BufferItem &item, const sp& releaseFence = Fence::NO_FENCE) { return OK; } + + void setName(const String8& name) { } + + void setFrameAvailableListener(const wp& listener) { } + + status_t setDefaultBufferSize(uint32_t width, uint32_t height) { + return mConsumer->setDefaultBufferSize(width, height); + } + + status_t setDefaultBufferFormat(PixelFormat defaultFormat) { + return mConsumer->setDefaultBufferFormat(defaultFormat); + } + + status_t setDefaultBufferDataSpace(android_dataspace defaultDataSpace) { + return mConsumer->setDefaultBufferDataSpace(defaultDataSpace); + } + + void abandon() { } + + status_t detachBuffer(int slot) { return OK; } + + status_t discardFreeBuffers() { return OK; } + + void freeBufferLocked(int slotIndex) { } + + status_t addReleaseFenceLocked( + int slot, const sp graphicBuffer, const sp& fence) { return OK; } +private: + sp mConsumer; +}; + +} // namespace android + +#endif // ANDROID_GUI_BUFFERITEMCONSUMER_H diff --git a/libs/hostgraphics/gui/BufferQueue.h b/libs/hostgraphics/gui/BufferQueue.h new file mode 100644 index 000000000000..aa3e7268e11c --- /dev/null +++ b/libs/hostgraphics/gui/BufferQueue.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2019 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_GUI_BUFFERQUEUE_H +#define ANDROID_GUI_BUFFERQUEUE_H + +#include +#include +#include + +namespace android { + +class BufferQueue { +public: + enum { INVALID_BUFFER_SLOT = BufferItem::INVALID_BUFFER_SLOT }; + enum { NO_BUFFER_AVAILABLE = IGraphicBufferConsumer::NO_BUFFER_AVAILABLE }; + + static void createBufferQueue(sp* outProducer, + sp* outConsumer); +}; + +} // namespace android + +#endif // ANDROID_GUI_BUFFERQUEUE_H diff --git a/libs/hostgraphics/gui/ConsumerBase.h b/libs/hostgraphics/gui/ConsumerBase.h new file mode 100644 index 000000000000..9002953c0848 --- /dev/null +++ b/libs/hostgraphics/gui/ConsumerBase.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2019 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_GUI_CONSUMERBASE_H +#define ANDROID_GUI_CONSUMERBASE_H + +#include + +#include + +namespace android { + +class ConsumerBase : public virtual RefBase { +public: + struct FrameAvailableListener : public virtual RefBase { + // See IConsumerListener::onFrame{Available,Replaced} + virtual void onFrameAvailable(const BufferItem& item) = 0; + virtual void onFrameReplaced(const BufferItem& /* item */) {} + }; +}; + +} // namespace android + +#endif // ANDROID_GUI_CONSUMERBASE_H \ No newline at end of file diff --git a/libs/hostgraphics/gui/IGraphicBufferConsumer.h b/libs/hostgraphics/gui/IGraphicBufferConsumer.h new file mode 100644 index 000000000000..9eb67b218800 --- /dev/null +++ b/libs/hostgraphics/gui/IGraphicBufferConsumer.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2019 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. + */ + +#pragma once + +#include + +#include + +#include + +namespace android { + +class BufferItem; +class Fence; +class GraphicBuffer; + +class IGraphicBufferConsumer : virtual public RefBase { +public: + enum { + // Returned by releaseBuffer, after which the consumer must free any references to the + // just-released buffer that it might have. + STALE_BUFFER_SLOT = 1, + // Returned by dequeueBuffer if there are no pending buffers available. + NO_BUFFER_AVAILABLE, + // Returned by dequeueBuffer if it's too early for the buffer to be acquired. + PRESENT_LATER, + }; + + virtual status_t acquireBuffer(BufferItem* buffer, nsecs_t presentWhen, + uint64_t maxFrameNumber = 0) = 0; + + virtual status_t detachBuffer(int slot) = 0; + + virtual status_t getReleasedBuffers(uint64_t* slotMask) = 0; + + virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h) = 0; + + virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) = 0; + + virtual status_t setDefaultBufferFormat(PixelFormat defaultFormat) = 0; + + virtual status_t setDefaultBufferDataSpace(android_dataspace defaultDataSpace) = 0; + + virtual status_t setConsumerUsageBits(uint64_t usage) = 0; + + virtual status_t setConsumerIsProtected(bool isProtected) = 0; + + virtual status_t discardFreeBuffers() = 0; +}; + +} // namespace android \ No newline at end of file diff --git a/libs/hostgraphics/gui/IGraphicBufferProducer.h b/libs/hostgraphics/gui/IGraphicBufferProducer.h index 00422136ff76..a1efd0bcfa4c 100644 --- a/libs/hostgraphics/gui/IGraphicBufferProducer.h +++ b/libs/hostgraphics/gui/IGraphicBufferProducer.h @@ -19,6 +19,8 @@ #include +#include + namespace android { class IGraphicBufferProducer : virtual public RefBase { diff --git a/libs/hostgraphics/ui/Fence.h b/libs/hostgraphics/ui/Fence.h new file mode 100644 index 000000000000..04d535c3a211 --- /dev/null +++ b/libs/hostgraphics/ui/Fence.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2019 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_FENCE_H +#define ANDROID_FENCE_H + +#include +#include + +typedef int64_t nsecs_t; + +namespace android { + +class Fence : public LightRefBase { +public: + Fence() { } + Fence(int) { } + static const sp NO_FENCE; + static constexpr nsecs_t SIGNAL_TIME_PENDING = INT64_MAX; + static constexpr nsecs_t SIGNAL_TIME_INVALID = -1; + static sp merge(const char* name, const sp& f1, const sp& f2) { + return NO_FENCE; + } + + static sp merge(const String8& name, const sp& f1, const sp& f2) { + return NO_FENCE; + } + + enum class Status { + Invalid, // Fence is invalid + Unsignaled, // Fence is valid but has not yet signaled + Signaled, // Fence is valid and has signaled + }; + + status_t wait(int timeout) { return OK; } + + status_t waitForever(const char* logname) { return OK; } + + int dup() const { return 0; } + + inline Status getStatus() { + // The sync_wait call underlying wait() has been measured to be + // significantly faster than the sync_fence_info call underlying + // getSignalTime(), which might otherwise appear to be the more obvious + // way to check whether a fence has signaled. + switch (wait(0)) { + case NO_ERROR: + return Status::Signaled; + case -ETIME: + return Status::Unsignaled; + default: + return Status::Invalid; + } + } +}; + +} // namespace android + +#endif // ANDROID_FENCE_H diff --git a/libs/hostgraphics/ui/GraphicBuffer.h b/libs/hostgraphics/ui/GraphicBuffer.h new file mode 100644 index 000000000000..ac88e44dbc65 --- /dev/null +++ b/libs/hostgraphics/ui/GraphicBuffer.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2019 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_GRAPHIC_BUFFER_H +#define ANDROID_GRAPHIC_BUFFER_H + +#include +#include + +#include + +#include +#include + +#include + +namespace android { + +class GraphicBuffer : virtual public RefBase { +public: + GraphicBuffer(uint32_t w, uint32_t h):width(w),height(h) { + data.resize(w*h); + } + uint32_t getWidth() const { return static_cast(width); } + uint32_t getHeight() const { return static_cast(height); } + uint32_t getStride() const { return static_cast(width); } + uint64_t getUsage() const { return 0; } + PixelFormat getPixelFormat() const { return PIXEL_FORMAT_RGBA_8888; } + //uint32_t getLayerCount() const { return static_cast(layerCount); } + Rect getBounds() const { return Rect(width, height); } + + status_t lockAsyncYCbCr(uint32_t inUsage, const Rect& rect, + android_ycbcr *ycbcr, int fenceFd) { return OK; } + + status_t lockAsync(uint32_t inUsage, const Rect& rect, void** vaddr, int fenceFd, + int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr) { + *vaddr = data.data(); + return OK; + } + + status_t unlockAsync(int *fenceFd) { return OK; } + +private: + uint32_t width; + uint32_t height; + std::vector data; +}; + +}; // namespace android + +#endif // ANDROID_GRAPHIC_BUFFER_H -- cgit v1.2.3 From 6073613967b1b0ba070549291b33fc05de79114b Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Fri, 2 Aug 2019 14:37:53 -0700 Subject: Crash in createPBufferSurface if eglCreatePbufferSurface fails EglManager::makeCurrent assumes mPBufferSurface has a valid surface. This assumtion is not satisfied if eglCreatePbufferSurface fails. Bug: 138733217 Test: build Change-Id: I580dca24f440accda458d53f988bb24cbabede36 Signed-off-by: Roman Kiryanov --- libs/hwui/renderthread/EglManager.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'libs') diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index 159cf497384a..da27c1ff9044 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -289,6 +289,10 @@ void EglManager::createPBufferSurface() { if (mPBufferSurface == EGL_NO_SURFACE && !EglExtensions.surfacelessContext) { EGLint attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE}; mPBufferSurface = eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs); + LOG_ALWAYS_FATAL_IF(mPBufferSurface == EGL_NO_SURFACE, + "Failed to create a pixel buffer display=%p, " + "mEglConfig=%p, error=%s", + mEglDisplay, mEglConfig, eglErrorString()); } } -- cgit v1.2.3 From 6db59a66bdcdad7e4c565e47c31437860455e832 Mon Sep 17 00:00:00 2001 From: Alec Mouri Date: Fri, 2 Aug 2019 17:05:26 -0700 Subject: [HWUI] clean up libgui include directives As a first pass removing some stale includes. Bug: 138819035 Change-Id: I7aef319aa8f1b13b6482ef61fda151560e570558 Test: builds --- libs/hwui/DeviceInfo.cpp | 1 - libs/hwui/renderthread/CacheManager.cpp | 1 - libs/hwui/renderthread/EglManager.cpp | 1 - libs/hwui/renderthread/VulkanManager.cpp | 3 ++- 4 files changed, 2 insertions(+), 4 deletions(-) (limited to 'libs') diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp index 0a9d965d0444..a0d3ff995e78 100644 --- a/libs/hwui/DeviceInfo.cpp +++ b/libs/hwui/DeviceInfo.cpp @@ -18,7 +18,6 @@ #include "Properties.h" -#include #include #include diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index 1b638c12ac7b..5469a6810c87 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -28,7 +28,6 @@ #include #include #include -#include #include #include diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index 159cf497384a..4f5f180643f3 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -29,7 +29,6 @@ #include #include -#include #include #include #include diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index 280f7d3489d2..510016585afc 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -17,7 +17,8 @@ #include "VulkanManager.h" #include -#include +#include +#include #include "Properties.h" #include "RenderThread.h" -- cgit v1.2.3 From e78f7c9f48a4c8be118f250c397e5e034ef2e4de Mon Sep 17 00:00:00 2001 From: Derek Sollenberger Date: Wed, 31 Jul 2019 15:18:47 -0400 Subject: Move GraphicBuffer back into libandroid_runtime. Update Bitmap to store AHardwareBuffer instead of GraphicBuffer and begin removing references to GraphicBuffer from the graphics JNI code Test: CtsUiRenderingTestCases Bug: 137655431 Change-Id: If533b6d87a87ae7e94a9b6f16fc52043714087df --- libs/hwui/Android.bp | 1 + libs/hwui/HardwareBitmapUploader.cpp | 5 +-- libs/hwui/hwui/Bitmap.cpp | 35 +++++++++++++------- libs/hwui/hwui/Bitmap.h | 19 +++++++---- libs/hwui/renderthread/VulkanSurface.h | 1 + .../common/scenes/HwBitmapInCompositeShader.cpp | 2 +- libs/hwui/utils/Color.cpp | 38 ++++++++++++++++++++++ libs/hwui/utils/Color.h | 5 +++ 8 files changed, 85 insertions(+), 21 deletions(-) (limited to 'libs') diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 0d837f2c7fed..ae90f117d448 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -88,6 +88,7 @@ cc_defaults { "libvulkan", "libui", "libgui", + "libnativewindow", "libprotobuf-cpp-lite", "libft2", "libandroidfw", diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp index 9bb6031b76ac..40bff88b7512 100644 --- a/libs/hwui/HardwareBitmapUploader.cpp +++ b/libs/hwui/HardwareBitmapUploader.cpp @@ -403,8 +403,9 @@ sk_sp HardwareBitmapUploader::allocateHardwareBitmap(const SkBitmap& sou if (!sUploader->uploadHardwareBitmap(bitmap, format, buffer)) { return nullptr; } - return Bitmap::createFrom(buffer, bitmap.colorType(), bitmap.refColorSpace(), - bitmap.alphaType(), Bitmap::computePalette(bitmap)); + return Bitmap::createFrom(buffer->toAHardwareBuffer(), bitmap.colorType(), + bitmap.refColorSpace(), bitmap.alphaType(), + Bitmap::computePalette(bitmap)); } void HardwareBitmapUploader::initialize() { diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index 4c2f0ad102bd..a1be5b72a5c5 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -148,12 +148,26 @@ sk_sp Bitmap::createFrom(const SkImageInfo& info, SkPixelRef& pixelRef) #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration -sk_sp Bitmap::createFrom(sp graphicBuffer, SkColorType colorType, +sk_sp Bitmap::createFrom(AHardwareBuffer* hardwareBuffer, sk_sp colorSpace, + BitmapPalette palette) { + AHardwareBuffer_Desc bufferDesc; + AHardwareBuffer_describe(hardwareBuffer, &bufferDesc); + SkImageInfo info = uirenderer::BufferDescriptionToImageInfo(bufferDesc, colorSpace); + + const size_t rowBytes = info.bytesPerPixel() * bufferDesc.stride; + return sk_sp(new Bitmap(hardwareBuffer, info, rowBytes, palette)); +} + +sk_sp Bitmap::createFrom(AHardwareBuffer* hardwareBuffer, SkColorType colorType, sk_sp colorSpace, SkAlphaType alphaType, BitmapPalette palette) { - SkImageInfo info = SkImageInfo::Make(graphicBuffer->getWidth(), graphicBuffer->getHeight(), + AHardwareBuffer_Desc bufferDesc; + AHardwareBuffer_describe(hardwareBuffer, &bufferDesc); + SkImageInfo info = SkImageInfo::Make(bufferDesc.width, bufferDesc.height, colorType, alphaType, colorSpace); - return sk_sp(new Bitmap(graphicBuffer.get(), info, palette)); + + const size_t rowBytes = info.bytesPerPixel() * bufferDesc.stride; + return sk_sp(new Bitmap(hardwareBuffer, info, rowBytes, palette)); } #endif @@ -238,18 +252,17 @@ Bitmap::Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info } #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration -Bitmap::Bitmap(GraphicBuffer* buffer, const SkImageInfo& info, BitmapPalette palette) - : SkPixelRef(info.width(), info.height(), nullptr, - bytesPerPixel(buffer->getPixelFormat()) * buffer->getStride()) +Bitmap::Bitmap(AHardwareBuffer* buffer, const SkImageInfo& info, size_t rowBytes, + BitmapPalette palette) + : SkPixelRef(info.width(), info.height(), nullptr, rowBytes) , mInfo(validateAlpha(info)) , mPixelStorageType(PixelStorageType::Hardware) , mPalette(palette) , mPaletteGenerationId(getGenerationID()) { mPixelStorage.hardware.buffer = buffer; - buffer->incStrong(buffer); + AHardwareBuffer_acquire(buffer); setImmutable(); // HW bitmaps are always immutable - mImage = SkImage::MakeFromAHardwareBuffer(reinterpret_cast(buffer), - mInfo.alphaType(), mInfo.refColorSpace()); + mImage = SkImage::MakeFromAHardwareBuffer(buffer, mInfo.alphaType(), mInfo.refColorSpace()); } #endif @@ -274,7 +287,7 @@ Bitmap::~Bitmap() { case PixelStorageType::Hardware: #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration auto buffer = mPixelStorage.hardware.buffer; - buffer->decStrong(buffer); + AHardwareBuffer_release(buffer); mPixelStorage.hardware.buffer = nullptr; #endif break; @@ -352,7 +365,7 @@ void Bitmap::getBounds(SkRect* bounds) const { } #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration -GraphicBuffer* Bitmap::graphicBuffer() { +AHardwareBuffer* Bitmap::hardwareBuffer() { if (isHardware()) { return mPixelStorage.hardware.buffer; } diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h index c7e18d10de96..00733c6b245b 100644 --- a/libs/hwui/hwui/Bitmap.h +++ b/libs/hwui/hwui/Bitmap.h @@ -24,7 +24,7 @@ #include #include #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration -#include +#include #endif namespace android { @@ -74,11 +74,15 @@ public: * memory that is provided as an input param. */ #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration - static sk_sp createFrom(sp graphicBuffer, - SkColorType colorType, + static sk_sp createFrom(AHardwareBuffer* hardwareBuffer, sk_sp colorSpace, - SkAlphaType alphaType = kPremul_SkAlphaType, BitmapPalette palette = BitmapPalette::Unknown); + + static sk_sp createFrom(AHardwareBuffer* hardwareBuffer, + SkColorType colorType, + sk_sp colorSpace, + SkAlphaType alphaType, + BitmapPalette palette); #endif static sk_sp createFrom(const SkImageInfo& info, size_t rowBytes, int fd, void* addr, size_t size, bool readOnly); @@ -110,7 +114,7 @@ public: PixelStorageType pixelStorageType() const { return mPixelStorageType; } #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration - GraphicBuffer* graphicBuffer(); + AHardwareBuffer* hardwareBuffer(); #endif /** @@ -143,7 +147,8 @@ private: size_t rowBytes); Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info, size_t rowBytes); #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration - Bitmap(GraphicBuffer* buffer, const SkImageInfo& info, BitmapPalette palette); + Bitmap(AHardwareBuffer* buffer, const SkImageInfo& info, size_t rowBytes, + BitmapPalette palette); #endif virtual ~Bitmap(); @@ -175,7 +180,7 @@ private: } heap; #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration struct { - GraphicBuffer* buffer; + AHardwareBuffer* buffer; } hardware; #endif } mPixelStorage; diff --git a/libs/hwui/renderthread/VulkanSurface.h b/libs/hwui/renderthread/VulkanSurface.h index 5717bb3afe5b..bd2362612a13 100644 --- a/libs/hwui/renderthread/VulkanSurface.h +++ b/libs/hwui/renderthread/VulkanSurface.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include diff --git a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp index 3d0a2b2474bf..5886ea39acce 100644 --- a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp +++ b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp @@ -50,7 +50,7 @@ public: pixels[4000 + 4 * i + 3] = 255; } buffer->unlock(); - sk_sp hardwareBitmap(Bitmap::createFrom(buffer, kRGBA_8888_SkColorType, + sk_sp hardwareBitmap(Bitmap::createFrom(buffer->toAHardwareBuffer(), SkColorSpace::MakeSRGB())); sk_sp hardwareShader(createBitmapShader(*hardwareBitmap)); diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp index cc7725b7b9de..9a27f28154e0 100644 --- a/libs/hwui/utils/Color.cpp +++ b/libs/hwui/utils/Color.cpp @@ -19,12 +19,50 @@ #include #include +#ifdef __ANDROID__ // Layoutlib does not support hardware buffers or native windows +#include +#endif + #include #include namespace android { namespace uirenderer { +#ifdef __ANDROID__ // Layoutlib does not support hardware buffers or native windows +SkImageInfo BufferDescriptionToImageInfo(const AHardwareBuffer_Desc& bufferDesc, + sk_sp colorSpace) { + SkColorType colorType = kUnknown_SkColorType; + SkAlphaType alphaType = kOpaque_SkAlphaType; + switch (bufferDesc.format) { + case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM: + colorType = kN32_SkColorType; + alphaType = kPremul_SkAlphaType; + break; + case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM: + colorType = kN32_SkColorType; + alphaType = kOpaque_SkAlphaType; + break; + case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM: + colorType = kRGB_565_SkColorType; + alphaType = kOpaque_SkAlphaType; + break; + case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM: + colorType = kRGBA_1010102_SkColorType; + alphaType = kPremul_SkAlphaType; + break; + case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT: + colorType = kRGBA_F16_SkColorType; + alphaType = kPremul_SkAlphaType; + break; + default: + ALOGV("Unsupported format: %d, return unknown by default", bufferDesc.format); + break; + } + return SkImageInfo::Make(bufferDesc.width, bufferDesc.height, colorType, alphaType, colorSpace); +} +#endif + android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType) { switch (colorType) { case kRGBA_8888_SkColorType: diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h index 79400de08ee0..7c2378a5aeb3 100644 --- a/libs/hwui/utils/Color.h +++ b/libs/hwui/utils/Color.h @@ -25,6 +25,8 @@ #include #include +struct AHardwareBuffer_Desc; + namespace android { namespace uirenderer { namespace Color { @@ -89,6 +91,9 @@ static constexpr float EOCF_sRGB(float srgb) { return srgb <= 0.04045f ? srgb / 12.92f : powf((srgb + 0.055f) / 1.055f, 2.4f); } +SkImageInfo BufferDescriptionToImageInfo(const AHardwareBuffer_Desc& bufferDesc, + sk_sp colorSpace); + android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType); ANDROID_API SkColorType PixelFormatToColorType(android::PixelFormat format); -- cgit v1.2.3 From 52e4392ed0111a24ac83f0393907e092ceb844f3 Mon Sep 17 00:00:00 2001 From: Stan Iliev Date: Tue, 6 Aug 2019 15:59:44 -0400 Subject: Access VectorDrawableAtlas only on RenderThread VectorDrawableAtlas dtor can run only on RenderThread, because it releases GPU resources. It is usually CacheManager, which destroys the atlas on RenderThread in response to trimMemory. This CL fixes a rare situation, when a Java finalizer thread deletes the atlas. Bug: 137722646 Test: Manual testing on a device Change-Id: I9d900809c23efaead9aaf16ee3306bc8c7c6b379 --- libs/hwui/VectorDrawable.cpp | 44 +++++++++++++++++++++++++---------- libs/hwui/renderthread/RenderThread.h | 5 ++++ 2 files changed, 37 insertions(+), 12 deletions(-) (limited to 'libs') diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp index f91d178f01f3..61403aa93c97 100644 --- a/libs/hwui/VectorDrawable.cpp +++ b/libs/hwui/VectorDrawable.cpp @@ -16,19 +16,24 @@ #include "VectorDrawable.h" +#include +#include #include -#include "hwui/Paint.h" + #include "PathParser.h" #include "SkColorFilter.h" #include "SkImageInfo.h" #include "SkShader.h" +#include "hwui/Paint.h" + +#ifdef __ANDROID__ +#include "renderthread/RenderThread.h" +#endif + #include "utils/Macros.h" #include "utils/TraceUtils.h" #include "utils/VectorDrawableUtils.h" -#include -#include - namespace android { namespace uirenderer { namespace VectorDrawable { @@ -472,7 +477,7 @@ void Tree::drawStaging(Canvas* outCanvas) { mStagingProperties.getBounds().bottom(), &paint); } -void Tree::getPaintFor(SkPaint* outPaint, const TreeProperties &prop) const { +void Tree::getPaintFor(SkPaint* outPaint, const TreeProperties& prop) const { // HWUI always draws VD with bilinear filtering. outPaint->setFilterQuality(kLow_SkFilterQuality); if (prop.getColorFilter() != nullptr) { @@ -492,7 +497,7 @@ Bitmap& Tree::getBitmapUpdateIfDirty() { } void Tree::updateCache(sp& atlas, GrContext* context) { -#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration SkRect dst; sk_sp surface = mCache.getSurface(&dst); bool canReuseSurface = surface && dst.width() >= mProperties.getScaledWidth() && @@ -533,7 +538,7 @@ void Tree::Cache::setAtlas(sp newAtlas, sk_sp Tree::Cache::getSurface(SkRect* bounds) { sk_sp surface; -#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration sp atlas = mAtlas.promote(); if (atlas.get() && mAtlasKey != INVALID_ATLAS_KEY) { auto atlasEntry = atlas->getEntry(mAtlasKey); @@ -547,13 +552,28 @@ sk_sp Tree::Cache::getSurface(SkRect* bounds) { } void Tree::Cache::clear() { -#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration - sp lockAtlas = mAtlas.promote(); - if (lockAtlas.get()) { - lockAtlas->releaseEntry(mAtlasKey); +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration + if (mAtlasKey != INVALID_ATLAS_KEY) { + if (renderthread::RenderThread::isCurrent()) { + sp lockAtlas = mAtlas.promote(); + if (lockAtlas.get()) { + lockAtlas->releaseEntry(mAtlasKey); + } + } else { + // VectorDrawableAtlas can be accessed only on RenderThread. + // Use by-copy capture of the current Cache variables, because "this" may not be valid + // by the time the lambda is evaluated on RenderThread. + renderthread::RenderThread::getInstance().queue().post( + [atlas = mAtlas, atlasKey = mAtlasKey]() { + sp lockAtlas = atlas.promote(); + if (lockAtlas.get()) { + lockAtlas->releaseEntry(atlasKey); + } + }); + } + mAtlasKey = INVALID_ATLAS_KEY; } mAtlas = nullptr; - mAtlasKey = INVALID_ATLAS_KEY; #endif } diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index df7eeb398847..5aa1af32094f 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -53,6 +53,10 @@ namespace skiapipeline { class VkFunctorDrawHandler; } +namespace VectorDrawable { +class Tree; +} + namespace renderthread { class CanvasContext; @@ -138,6 +142,7 @@ private: friend class android::uirenderer::TestUtils; friend class android::uirenderer::WebViewFunctor; friend class android::uirenderer::skiapipeline::VkFunctorDrawHandler; + friend class android::uirenderer::VectorDrawable::Tree; RenderThread(); virtual ~RenderThread(); -- cgit v1.2.3 From 0b9f0b81bce2ebb0d511f2c8dd348366746697ce Mon Sep 17 00:00:00 2001 From: Yiwei Zhang Date: Thu, 15 Aug 2019 11:33:59 -0700 Subject: HWUI-VK: remove duplicate GET_DEV_PROC Test: build, flash and boot Change-Id: Ia3b62c5d31952f85664f1926584fc9438d48dec3 --- libs/hwui/renderthread/VulkanManager.cpp | 38 +++++++++++++++----------------- 1 file changed, 18 insertions(+), 20 deletions(-) (limited to 'libs') diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index 510016585afc..35abc57fbe57 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -138,14 +138,14 @@ void VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe err = mCreateInstance(&instance_create, nullptr, &mInstance); LOG_ALWAYS_FATAL_IF(err < 0); + GET_INST_PROC(CreateDevice); GET_INST_PROC(DestroyInstance); + GET_INST_PROC(EnumerateDeviceExtensionProperties); GET_INST_PROC(EnumeratePhysicalDevices); - GET_INST_PROC(GetPhysicalDeviceProperties); - GET_INST_PROC(GetPhysicalDeviceQueueFamilyProperties); GET_INST_PROC(GetPhysicalDeviceFeatures2); GET_INST_PROC(GetPhysicalDeviceImageFormatProperties2); - GET_INST_PROC(CreateDevice); - GET_INST_PROC(EnumerateDeviceExtensionProperties); + GET_INST_PROC(GetPhysicalDeviceProperties); + GET_INST_PROC(GetPhysicalDeviceQueueFamilyProperties); uint32_t gpuCount; LOG_ALWAYS_FATAL_IF(mEnumeratePhysicalDevices(mInstance, &gpuCount, nullptr)); @@ -312,29 +312,27 @@ void VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe LOG_ALWAYS_FATAL_IF(mCreateDevice(mPhysicalDevice, &deviceInfo, nullptr, &mDevice)); - GET_DEV_PROC(GetDeviceQueue); - GET_DEV_PROC(DeviceWaitIdle); - GET_DEV_PROC(DestroyDevice); - GET_DEV_PROC(CreateCommandPool); - GET_DEV_PROC(DestroyCommandPool); GET_DEV_PROC(AllocateCommandBuffers); - GET_DEV_PROC(FreeCommandBuffers); - GET_DEV_PROC(ResetCommandBuffer); GET_DEV_PROC(BeginCommandBuffer); - GET_DEV_PROC(EndCommandBuffer); GET_DEV_PROC(CmdPipelineBarrier); - GET_DEV_PROC(GetDeviceQueue); - GET_DEV_PROC(QueueSubmit); - GET_DEV_PROC(QueueWaitIdle); - GET_DEV_PROC(DeviceWaitIdle); + GET_DEV_PROC(CreateCommandPool); + GET_DEV_PROC(CreateFence); GET_DEV_PROC(CreateSemaphore); + GET_DEV_PROC(DestroyCommandPool); + GET_DEV_PROC(DestroyDevice); + GET_DEV_PROC(DestroyFence); GET_DEV_PROC(DestroySemaphore); - GET_DEV_PROC(ImportSemaphoreFdKHR); + GET_DEV_PROC(DeviceWaitIdle); + GET_DEV_PROC(EndCommandBuffer); + GET_DEV_PROC(FreeCommandBuffers); + GET_DEV_PROC(GetDeviceQueue); GET_DEV_PROC(GetSemaphoreFdKHR); - GET_DEV_PROC(CreateFence); - GET_DEV_PROC(DestroyFence); - GET_DEV_PROC(WaitForFences); + GET_DEV_PROC(ImportSemaphoreFdKHR); + GET_DEV_PROC(QueueSubmit); + GET_DEV_PROC(QueueWaitIdle); + GET_DEV_PROC(ResetCommandBuffer); GET_DEV_PROC(ResetFences); + GET_DEV_PROC(WaitForFences); } void VulkanManager::initialize() { -- cgit v1.2.3 From 67e479a4e8a66057f4ebe6f03727cebc480716ec Mon Sep 17 00:00:00 2001 From: Garfield Tan Date: Mon, 5 Aug 2019 16:47:40 -0700 Subject: Add cursor type and hotspot to surface metadata. Also bootstrap unit tests for PointerController. Need to mark 3 functions of SpriteController virtual so their behaviors can be overridden. Bug: 130822623 Test: SurfaceFlinger can get cursor type and hotspot. Change-Id: I739cd03214364144bb4e22a166ecc7abfd3492fe --- libs/input/Android.bp | 1 + libs/input/SpriteController.cpp | 25 ++- libs/input/SpriteController.h | 17 +- libs/input/TEST_MAPPING | 7 + libs/input/tests/Android.bp | 45 ++++++ libs/input/tests/PointerController_test.cpp | 215 ++++++++++++++++++++++++++ libs/input/tests/mocks/MockSprite.h | 41 +++++ libs/input/tests/mocks/MockSpriteController.h | 39 +++++ 8 files changed, 381 insertions(+), 9 deletions(-) create mode 100644 libs/input/TEST_MAPPING create mode 100644 libs/input/tests/Android.bp create mode 100644 libs/input/tests/PointerController_test.cpp create mode 100644 libs/input/tests/mocks/MockSprite.h create mode 100644 libs/input/tests/mocks/MockSpriteController.h (limited to 'libs') diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 89d3cc4f5083..16f2917f8df8 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -20,6 +20,7 @@ cc_library_shared { ], shared_libs: [ + "libbinder", "libcutils", "liblog", "libutils", diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp index c1868d3a94d6..fd386e9f7a8a 100644 --- a/libs/input/SpriteController.cpp +++ b/libs/input/SpriteController.cpp @@ -245,7 +245,8 @@ void SpriteController::doUpdateSprites() { if (update.state.surfaceControl != NULL && (becomingVisible || becomingHidden || (wantSurfaceVisibleAndDrawn && (update.state.dirty & (DIRTY_ALPHA | DIRTY_POSITION | DIRTY_TRANSFORMATION_MATRIX | DIRTY_LAYER - | DIRTY_VISIBILITY | DIRTY_HOTSPOT | DIRTY_DISPLAY_ID))))) { + | DIRTY_VISIBILITY | DIRTY_HOTSPOT | DIRTY_DISPLAY_ID + | DIRTY_ICON_STYLE))))) { needApplyTransaction = true; if (wantSurfaceVisibleAndDrawn @@ -274,6 +275,21 @@ void SpriteController::doUpdateSprites() { update.state.transformationMatrix.dtdy); } + if (wantSurfaceVisibleAndDrawn + && (becomingVisible + || (update.state.dirty & (DIRTY_HOTSPOT | DIRTY_ICON_STYLE)))) { + Parcel p; + p.writeInt32(update.state.icon.style); + p.writeFloat(update.state.icon.hotSpotX); + p.writeFloat(update.state.icon.hotSpotY); + + // Pass cursor metadata in the sprite surface so that when Android is running as a + // client OS (e.g. ARC++) the host OS can get the requested cursor metadata and + // update mouse cursor in the host OS. + t.setMetadata( + update.state.surfaceControl, METADATA_MOUSE_CURSOR, p); + } + int32_t surfaceLayer = mOverlayLayer + update.state.layer; if (wantSurfaceVisibleAndDrawn && (becomingVisible || (update.state.dirty & DIRTY_LAYER))) { @@ -397,9 +413,14 @@ void SpriteController::SpriteImpl::setIcon(const SpriteIcon& icon) { } else { dirty = DIRTY_BITMAP; } + + if (mLocked.state.icon.style != icon.style) { + mLocked.state.icon.style = icon.style; + dirty |= DIRTY_ICON_STYLE; + } } else if (mLocked.state.icon.isValid()) { mLocked.state.icon.bitmap.reset(); - dirty = DIRTY_BITMAP | DIRTY_HOTSPOT; + dirty = DIRTY_BITMAP | DIRTY_HOTSPOT | DIRTY_ICON_STYLE; } else { return; // setting to invalid icon and already invalid so nothing to do } diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h index 5b216f50d113..79a904f5fe65 100644 --- a/libs/input/SpriteController.h +++ b/libs/input/SpriteController.h @@ -55,11 +55,12 @@ struct SpriteTransformationMatrix { * Icon that a sprite displays, including its hotspot. */ struct SpriteIcon { - inline SpriteIcon() : hotSpotX(0), hotSpotY(0) { } - inline SpriteIcon(const SkBitmap& bitmap, float hotSpotX, float hotSpotY) : - bitmap(bitmap), hotSpotX(hotSpotX), hotSpotY(hotSpotY) { } + inline SpriteIcon() : style(0), hotSpotX(0), hotSpotY(0) { } + inline SpriteIcon(const SkBitmap& bitmap, int32_t style, float hotSpotX, float hotSpotY) : + bitmap(bitmap), style(style), hotSpotX(hotSpotX), hotSpotY(hotSpotY) { } SkBitmap bitmap; + int32_t style; float hotSpotX; float hotSpotY; @@ -69,11 +70,12 @@ struct SpriteIcon { bitmap.readPixels(bitmapCopy.info(), bitmapCopy.getPixels(), bitmapCopy.rowBytes(), 0, 0); } - return SpriteIcon(bitmapCopy, hotSpotX, hotSpotY); + return SpriteIcon(bitmapCopy, style, hotSpotX, hotSpotY); } inline void reset() { bitmap.reset(); + style = 0; hotSpotX = 0; hotSpotY = 0; } @@ -149,15 +151,15 @@ public: SpriteController(const sp& looper, int32_t overlayLayer); /* Creates a new sprite, initially invisible. */ - sp createSprite(); + virtual sp createSprite(); /* Opens or closes a transaction to perform a batch of sprite updates as part of * a single operation such as setPosition and setAlpha. It is not necessary to * open a transaction when updating a single property. * Calls to openTransaction() nest and must be matched by an equal number * of calls to closeTransaction(). */ - void openTransaction(); - void closeTransaction(); + virtual void openTransaction(); + virtual void closeTransaction(); private: enum { @@ -174,6 +176,7 @@ private: DIRTY_VISIBILITY = 1 << 5, DIRTY_HOTSPOT = 1 << 6, DIRTY_DISPLAY_ID = 1 << 7, + DIRTY_ICON_STYLE = 1 << 8, }; /* Describes the state of a sprite. diff --git a/libs/input/TEST_MAPPING b/libs/input/TEST_MAPPING new file mode 100644 index 000000000000..fe74c62d4ec1 --- /dev/null +++ b/libs/input/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "libinputservice_test" + } + ] +} diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp new file mode 100644 index 000000000000..e83b2a78d180 --- /dev/null +++ b/libs/input/tests/Android.bp @@ -0,0 +1,45 @@ +// Copyright (C) 2019 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. + +cc_test { + name: "libinputservice_test", + srcs: [ + "PointerController_test.cpp", + ], + shared_libs: [ + "libinputservice", + "libgui", + "libhwui", + "libutils", + ], + static_libs: [ + "libgmock", + "libgtest", + ], + header_libs: [ + "libbase_headers", + "libinputflinger_headers", + ], + include_dirs: [ + "frameworks/base/libs", + ], + cflags: [ + "-Wall", + "-Werror", + "-Wextra", + ], + test_suites: [ + "general-tests", + ], +} diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp new file mode 100644 index 000000000000..92efb4ea86ff --- /dev/null +++ b/libs/input/tests/PointerController_test.cpp @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2019 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 "mocks/MockSprite.h" +#include "mocks/MockSpriteController.h" + +#include +#include + +#include +#include +#include +#include + +namespace android { + +enum TestCursorType { + CURSOR_TYPE_DEFAULT = 0, + CURSOR_TYPE_HOVER, + CURSOR_TYPE_TOUCH, + CURSOR_TYPE_ANCHOR, + CURSOR_TYPE_ADDITIONAL_1, + CURSOR_TYPE_ADDITIONAL_2, + CURSOR_TYPE_CUSTOM = -1, +}; + +using ::testing::AllOf; +using ::testing::Field; +using ::testing::NiceMock; +using ::testing::Mock; +using ::testing::Return; +using ::testing::Test; + +std::pair getHotSpotCoordinatesForType(int32_t type) { + return std::make_pair(type * 10, type * 10 + 5); +} + +class MockPointerControllerPolicyInterface : public PointerControllerPolicyInterface { +public: + virtual void loadPointerIcon(SpriteIcon* icon, int32_t displayId) override; + virtual void loadPointerResources(PointerResources* outResources, int32_t displayId) override; + virtual void loadAdditionalMouseResources(std::map* outResources, + std::map* outAnimationResources, int32_t displayId) override; + virtual int32_t getDefaultPointerIconId() override; + virtual int32_t getCustomPointerIconId() override; + +private: + void loadPointerIconForType(SpriteIcon* icon, int32_t cursorType); +}; + +void MockPointerControllerPolicyInterface::loadPointerIcon(SpriteIcon* icon, int32_t) { + loadPointerIconForType(icon, CURSOR_TYPE_DEFAULT); +} + +void MockPointerControllerPolicyInterface::loadPointerResources(PointerResources* outResources, + int32_t) { + loadPointerIconForType(&outResources->spotHover, CURSOR_TYPE_HOVER); + loadPointerIconForType(&outResources->spotTouch, CURSOR_TYPE_TOUCH); + loadPointerIconForType(&outResources->spotAnchor, CURSOR_TYPE_ANCHOR); +} + +void MockPointerControllerPolicyInterface::loadAdditionalMouseResources( + std::map* outResources, + std::map* outAnimationResources, + int32_t) { + SpriteIcon icon; + PointerAnimation anim; + + for (int32_t cursorType : {CURSOR_TYPE_ADDITIONAL_1, CURSOR_TYPE_ADDITIONAL_2}) { + loadPointerIconForType(&icon, cursorType); + anim.animationFrames.push_back(icon); + anim.durationPerFrame = 10; + (*outResources)[cursorType] = icon; + (*outAnimationResources)[cursorType] = anim; + } +} + +int32_t MockPointerControllerPolicyInterface::getDefaultPointerIconId() { + return CURSOR_TYPE_DEFAULT; +} + +int32_t MockPointerControllerPolicyInterface::getCustomPointerIconId() { + return CURSOR_TYPE_CUSTOM; +} + +void MockPointerControllerPolicyInterface::loadPointerIconForType(SpriteIcon* icon, int32_t type) { + icon->style = type; + std::pair hotSpot = getHotSpotCoordinatesForType(type); + icon->hotSpotX = hotSpot.first; + icon->hotSpotY = hotSpot.second; +} + +class PointerControllerTest : public Test { +protected: + PointerControllerTest(); + ~PointerControllerTest(); + + sp mPointerSprite; + sp mPolicy; + sp mSpriteController; + sp mPointerController; + +private: + void loopThread(); + + std::atomic mRunning = true; + class MyLooper : public Looper { + public: + MyLooper() : Looper(false) {} + ~MyLooper() = default; + }; + sp mLooper; + std::thread mThread; +}; + +PointerControllerTest::PointerControllerTest() : mPointerSprite(new NiceMock), + mLooper(new MyLooper), mThread(&PointerControllerTest::loopThread, this) { + + mSpriteController = new NiceMock(mLooper); + mPolicy = new MockPointerControllerPolicyInterface(); + + EXPECT_CALL(*mSpriteController, createSprite()) + .WillOnce(Return(mPointerSprite)); + + mPointerController = new PointerController(mPolicy, mLooper, mSpriteController); + + DisplayViewport viewport; + viewport.displayId = ADISPLAY_ID_DEFAULT; + viewport.logicalRight = 1600; + viewport.logicalBottom = 1200; + viewport.physicalRight = 800; + viewport.physicalBottom = 600; + viewport.deviceWidth = 400; + viewport.deviceHeight = 300; + mPointerController->setDisplayViewport(viewport); +} + +PointerControllerTest::~PointerControllerTest() { + mRunning.store(false, std::memory_order_relaxed); + mThread.join(); +} + +void PointerControllerTest::loopThread() { + Looper::setForThread(mLooper); + + while (mRunning.load(std::memory_order_relaxed)) { + mLooper->pollOnce(100); + } +} + +TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) { + mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE); + + std::pair hotspot = getHotSpotCoordinatesForType(CURSOR_TYPE_DEFAULT); + EXPECT_CALL(*mPointerSprite, setVisible(true)); + EXPECT_CALL(*mPointerSprite, setAlpha(1.0f)); + EXPECT_CALL(*mPointerSprite, setIcon( + AllOf( + Field(&SpriteIcon::style, CURSOR_TYPE_DEFAULT), + Field(&SpriteIcon::hotSpotX, hotspot.first), + Field(&SpriteIcon::hotSpotY, hotspot.second)))); + mPointerController->reloadPointerResources(); +} + +TEST_F(PointerControllerTest, updatePointerIcon) { + mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE); + + int32_t type = CURSOR_TYPE_ADDITIONAL_1; + std::pair hotspot = getHotSpotCoordinatesForType(type); + EXPECT_CALL(*mPointerSprite, setVisible(true)); + EXPECT_CALL(*mPointerSprite, setAlpha(1.0f)); + EXPECT_CALL(*mPointerSprite, setIcon( + AllOf( + Field(&SpriteIcon::style, type), + Field(&SpriteIcon::hotSpotX, hotspot.first), + Field(&SpriteIcon::hotSpotY, hotspot.second)))); + mPointerController->updatePointerIcon(type); +} + +TEST_F(PointerControllerTest, setCustomPointerIcon) { + mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE); + + int32_t style = CURSOR_TYPE_CUSTOM; + float hotSpotX = 15; + float hotSpotY = 20; + + SpriteIcon icon; + icon.style = style; + icon.hotSpotX = hotSpotX; + icon.hotSpotY = hotSpotY; + + EXPECT_CALL(*mPointerSprite, setVisible(true)); + EXPECT_CALL(*mPointerSprite, setAlpha(1.0f)); + EXPECT_CALL(*mPointerSprite, setIcon( + AllOf( + Field(&SpriteIcon::style, style), + Field(&SpriteIcon::hotSpotX, hotSpotX), + Field(&SpriteIcon::hotSpotY, hotSpotY)))); + mPointerController->setCustomPointerIcon(icon); +} + +} // namespace android diff --git a/libs/input/tests/mocks/MockSprite.h b/libs/input/tests/mocks/MockSprite.h new file mode 100644 index 000000000000..013b79c3a3bf --- /dev/null +++ b/libs/input/tests/mocks/MockSprite.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2019 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 _MOCK_SPRITE_H +#define _MOCK_SPRITE_H + +#include + +#include + +namespace android { + +class MockSprite : public Sprite { +public: + virtual ~MockSprite() = default; + + MOCK_METHOD(void, setIcon, (const SpriteIcon& icon), (override)); + MOCK_METHOD(void, setVisible, (bool), (override)); + MOCK_METHOD(void, setPosition, (float, float), (override)); + MOCK_METHOD(void, setLayer, (int32_t), (override)); + MOCK_METHOD(void, setAlpha, (float), (override)); + MOCK_METHOD(void, setTransformationMatrix, (const SpriteTransformationMatrix&), (override)); + MOCK_METHOD(void, setDisplayId, (int32_t), (override)); +}; + +} // namespace android + +#endif // _MOCK_SPRITE_H diff --git a/libs/input/tests/mocks/MockSpriteController.h b/libs/input/tests/mocks/MockSpriteController.h new file mode 100644 index 000000000000..a034f66c9abf --- /dev/null +++ b/libs/input/tests/mocks/MockSpriteController.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2019 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 _MOCK_SPRITE_CONTROLLER_H +#define _MOCK_SPRITE_CONTROLLER_H + +#include "MockSprite.h" + +#include + +namespace android { + +class MockSpriteController : public SpriteController { + +public: + MockSpriteController(sp looper) : SpriteController(looper, 0) {} + ~MockSpriteController() {} + + MOCK_METHOD(sp, createSprite, (), (override)); + MOCK_METHOD(void, openTransaction, (), (override)); + MOCK_METHOD(void, closeTransaction, (), (override)); +}; + +} // namespace android + +#endif // _MOCK_SPRITE_CONTROLLER_H -- cgit v1.2.3 From 8d45174361a39fece141367cad3d507201f07941 Mon Sep 17 00:00:00 2001 From: Alec Mouri Date: Thu, 1 Aug 2019 19:19:16 -0700 Subject: [HWUI] use ANativeWindow_getLastDequeueDuration api Replaces the query() call for getting the duration of the internal dequeue() call a call that will be abi stable. Bug: 137012798 Test: builds Change-Id: I6d2118a7c093dd2ecc4ebfb4b4ee8556f7e709a7 --- libs/hwui/renderthread/CanvasContext.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'libs') diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 88a0c6ea3085..e009134f9dbc 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -15,7 +15,17 @@ */ #include "CanvasContext.h" + #include +#include +#include +#include +#include + +#include +#include +#include +#include #include "../Properties.h" #include "AnimationContext.h" @@ -32,16 +42,6 @@ #include "utils/TimeUtils.h" #include "utils/TraceUtils.h" -#include - -#include -#include -#include - -#include -#include -#include - #define TRIM_MEMORY_COMPLETE 80 #define TRIM_MEMORY_UI_HIDDEN 20 @@ -484,16 +484,16 @@ void CanvasContext::draw() { swap.swapCompletedTime = systemTime(SYSTEM_TIME_MONOTONIC); swap.vsyncTime = mRenderThread.timeLord().latestVsync(); if (didDraw) { - int durationUs; nsecs_t dequeueStart = mNativeSurface->getLastDequeueStartTime(); if (dequeueStart < mCurrentFrameInfo->get(FrameInfoIndex::SyncStart)) { // Ignoring dequeue duration as it happened prior to frame render start // and thus is not part of the frame. swap.dequeueDuration = 0; } else { - mNativeSurface->query(NATIVE_WINDOW_LAST_DEQUEUE_DURATION, &durationUs); - swap.dequeueDuration = us2ns(durationUs); + swap.dequeueDuration = + us2ns(ANativeWindow_getLastDequeueDuration(mNativeSurface.get())); } + int durationUs; mNativeSurface->query(NATIVE_WINDOW_LAST_QUEUE_DURATION, &durationUs); swap.queueDuration = us2ns(durationUs); } else { -- cgit v1.2.3 From c287a77d51ca5adcf82724ad6474d127abdce40a Mon Sep 17 00:00:00 2001 From: Derek Sollenberger Date: Fri, 2 Aug 2019 13:44:31 -0400 Subject: Continue to isolate graphics classes by introducing C API This CL adds an initial C API to show how we should proceed with limiting the symbols the graphics library will expose to the framework. Refactor Surface and TextureView to have no dependencies on the graphics classes outside of the exposed C API. Test: CtsUiRenderingTestCases Bug: 137655431 Change-Id: Ic10aaba00388a47ca97f156fcaebc1ea7676dfb2 --- libs/hwui/utils/Color.cpp | 21 ++++++++++++++++----- libs/hwui/utils/Color.h | 6 ++++++ 2 files changed, 22 insertions(+), 5 deletions(-) (limited to 'libs') diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp index 9a27f28154e0..b93759f87da2 100644 --- a/libs/hwui/utils/Color.cpp +++ b/libs/hwui/utils/Color.cpp @@ -21,6 +21,7 @@ #ifdef __ANDROID__ // Layoutlib does not support hardware buffers or native windows #include +#include #endif #include @@ -30,11 +31,11 @@ namespace android { namespace uirenderer { #ifdef __ANDROID__ // Layoutlib does not support hardware buffers or native windows -SkImageInfo BufferDescriptionToImageInfo(const AHardwareBuffer_Desc& bufferDesc, - sk_sp colorSpace) { +static inline SkImageInfo createImageInfo(int32_t width, int32_t height, int32_t format, + sk_sp colorSpace) { SkColorType colorType = kUnknown_SkColorType; SkAlphaType alphaType = kOpaque_SkAlphaType; - switch (bufferDesc.format) { + switch (format) { case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM: colorType = kN32_SkColorType; alphaType = kPremul_SkAlphaType; @@ -56,10 +57,20 @@ SkImageInfo BufferDescriptionToImageInfo(const AHardwareBuffer_Desc& bufferDesc, alphaType = kPremul_SkAlphaType; break; default: - ALOGV("Unsupported format: %d, return unknown by default", bufferDesc.format); + ALOGV("Unsupported format: %d, return unknown by default", format); break; } - return SkImageInfo::Make(bufferDesc.width, bufferDesc.height, colorType, alphaType, colorSpace); + return SkImageInfo::Make(width, height, colorType, alphaType, colorSpace); +} + +SkImageInfo ANativeWindowToImageInfo(const ANativeWindow_Buffer& buffer, + sk_sp colorSpace) { + return createImageInfo(buffer.width, buffer.height, buffer.format, colorSpace); +} + +SkImageInfo BufferDescriptionToImageInfo(const AHardwareBuffer_Desc& bufferDesc, + sk_sp colorSpace) { + return createImageInfo(bufferDesc.width, bufferDesc.height, bufferDesc.format, colorSpace); } #endif diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h index 7c2378a5aeb3..07b5ec8fe0f0 100644 --- a/libs/hwui/utils/Color.h +++ b/libs/hwui/utils/Color.h @@ -25,6 +25,7 @@ #include #include +struct ANativeWindow_Buffer; struct AHardwareBuffer_Desc; namespace android { @@ -91,8 +92,13 @@ static constexpr float EOCF_sRGB(float srgb) { return srgb <= 0.04045f ? srgb / 12.92f : powf((srgb + 0.055f) / 1.055f, 2.4f); } +#ifdef __ANDROID__ // Layoutlib does not support hardware buffers or native windows +ANDROID_API SkImageInfo ANativeWindowToImageInfo(const ANativeWindow_Buffer& buffer, + sk_sp colorSpace); + SkImageInfo BufferDescriptionToImageInfo(const AHardwareBuffer_Desc& bufferDesc, sk_sp colorSpace); +#endif android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType); ANDROID_API SkColorType PixelFormatToColorType(android::PixelFormat format); -- cgit v1.2.3 From 8da1c38b69e947885fcec50cda46c5472ddb6746 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Fri, 23 Aug 2019 11:45:04 -0700 Subject: Fix security issue in DynamicRefTable::load. A crafted resources arsc could cause libandroidfw to read data out of bounds of the resources arsc. This change updates the logic to calculate whether the ref table chunk is large enough to hold the number of entries specified in the header. Bug: 129475100 Test: adb shell push ResTableTest data Test: adb shell push poc.arsc data Test: ./ResTableTest poc.arsc Change-Id: Ifbaad87bdbcb7eecf554ef362e0118f53532a22a --- libs/androidfw/ResourceTypes.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'libs') diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 2ad2e76cc696..8a035dbbc0f5 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -6902,9 +6902,8 @@ std::unique_ptr DynamicRefTable::clone() const { status_t DynamicRefTable::load(const ResTable_lib_header* const header) { const uint32_t entryCount = dtohl(header->count); - const uint32_t sizeOfEntries = sizeof(ResTable_lib_entry) * entryCount; const uint32_t expectedSize = dtohl(header->header.size) - dtohl(header->header.headerSize); - if (sizeOfEntries > expectedSize) { + if (entryCount > (expectedSize / sizeof(ResTable_lib_entry))) { ALOGE("ResTable_lib_header size %u is too small to fit %u entries (x %u).", expectedSize, entryCount, (uint32_t)sizeof(ResTable_lib_entry)); return UNKNOWN_ERROR; -- cgit v1.2.3 From f7689d657fd5050b96925b2142cb7eaf2f28157e Mon Sep 17 00:00:00 2001 From: Ravi Mistry Date: Mon, 26 Aug 2019 13:57:56 -0400 Subject: Stop MinikinSkia.h relying on SkRefCnt.h for and Test: Ran locally Change-Id: If70fdf419504b89e3a3501cf7f2133277064d78c --- libs/hwui/hwui/MinikinSkia.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'libs') diff --git a/libs/hwui/hwui/MinikinSkia.h b/libs/hwui/hwui/MinikinSkia.h index 90f7d48a47ee..298967689cd9 100644 --- a/libs/hwui/hwui/MinikinSkia.h +++ b/libs/hwui/hwui/MinikinSkia.h @@ -20,6 +20,8 @@ #include #include #include +#include +#include class SkFont; class SkTypeface; -- cgit v1.2.3 From ff2c36b7dec33755636c8c26654458aedda9ac27 Mon Sep 17 00:00:00 2001 From: Stan Iliev Date: Mon, 19 Aug 2019 10:48:31 -0400 Subject: Fix VkFunctorDrawable crash Fix WebView crash, which is specific for Vulkan pipeline introduced by ag/8550137. VkFunctorDrawable expects SkDrawable::onSnapGpuDrawHandler callback instead of SkDrawable::onDraw. This CL invokes SkCanvas::drawDrawable/SkGpuDevice::drawDrawable, which has the logic to invoke onSnapGpuDrawHandler. Test: Don't crash WebView with Vulkan pipeline, pass CTS Change-Id: Ia98f159511f4ad2dbdbe0d53f0aec2f8c6db263b --- libs/hwui/DisplayListOps.in | 1 + libs/hwui/RecordingCanvas.cpp | 18 ++++++++++++++++++ libs/hwui/RecordingCanvas.h | 6 ++++++ libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp | 2 +- 4 files changed, 26 insertions(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/hwui/DisplayListOps.in b/libs/hwui/DisplayListOps.in index 2deb5657c877..4a252afc1df3 100644 --- a/libs/hwui/DisplayListOps.in +++ b/libs/hwui/DisplayListOps.in @@ -49,3 +49,4 @@ X(DrawVertices) X(DrawAtlas) X(DrawShadowRec) X(DrawVectorDrawable) +X(DrawWebView) diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index c8eb1ca55910..c0df2faf120a 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -16,6 +16,7 @@ #include "RecordingCanvas.h" +#include "pipeline/skia/FunctorDrawable.h" #include "VectorDrawable.h" #include "SkAndroidFrameworkUtils.h" @@ -496,6 +497,16 @@ struct DrawVectorDrawable final : Op { SkPaint paint; BitmapPalette palette; }; +struct DrawWebView final : Op { + static const auto kType = Type::DrawWebView; + DrawWebView(skiapipeline::FunctorDrawable* drawable) : drawable(sk_ref_sp(drawable)) {} + sk_sp drawable; + // We can't invoke SkDrawable::draw directly, because VkFunctorDrawable expects + // SkDrawable::onSnapGpuDrawHandler callback instead of SkDrawable::onDraw. + // SkCanvas::drawDrawable/SkGpuDevice::drawDrawable has the logic to invoke + // onSnapGpuDrawHandler. + void draw(SkCanvas* c, const SkMatrix&) const { c->drawDrawable(drawable.get()); } +}; } template @@ -680,6 +691,9 @@ void DisplayListData::drawShadowRec(const SkPath& path, const SkDrawShadowRec& r void DisplayListData::drawVectorDrawable(VectorDrawableRoot* tree) { this->push(0, tree); } +void DisplayListData::drawWebView(skiapipeline::FunctorDrawable* drawable) { + this->push(0, drawable); +} typedef void (*draw_fn)(const void*, SkCanvas*, const SkMatrix&); typedef void (*void_fn)(const void*); @@ -986,5 +1000,9 @@ void RecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) { fDL->drawVectorDrawable(tree); } +void RecordingCanvas::drawWebView(skiapipeline::FunctorDrawable* drawable) { + fDL->drawWebView(drawable); +} + } // namespace uirenderer } // namespace android diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h index 16ec877002f7..a79b7c00ba04 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -36,6 +36,10 @@ namespace android { namespace uirenderer { +namespace skiapipeline { +class FunctorDrawable; +} + enum class DisplayListOpType : uint8_t { #define X(T) T, #include "DisplayListOps.in" @@ -119,6 +123,7 @@ private: SkBlendMode, const SkRect*, const SkPaint*); void drawShadowRec(const SkPath&, const SkDrawShadowRec&); void drawVectorDrawable(VectorDrawableRoot* tree); + void drawWebView(skiapipeline::FunctorDrawable*); template void* push(size_t, Args&&...); @@ -203,6 +208,7 @@ public: void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override; void drawVectorDrawable(VectorDrawableRoot* tree); + void drawWebView(skiapipeline::FunctorDrawable*); /** * If "isClipMayBeComplex" returns false, it is guaranteed the current clip is a rectangle. diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp index 0db5133037b0..d67cf8c9c73f 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -158,7 +158,7 @@ void SkiaRecordingCanvas::drawWebViewFunctor(int functor) { functorDrawable = mDisplayList->allocateDrawable(functor, asSkCanvas()); } mDisplayList->mChildFunctors.push_back(functorDrawable); - drawDrawable(functorDrawable); + mRecorder.drawWebView(functorDrawable); #endif } -- cgit v1.2.3 From 898b31640274863e14ba0c18a0f6f04706e17737 Mon Sep 17 00:00:00 2001 From: Songchun Fan Date: Mon, 8 Jul 2019 09:00:34 -0700 Subject: [base] close zip archive when parsing fails Test: manual Change-Id: I54c054da6000df15dba81ad211eb1cf61e88c5a8 --- libs/androidfw/ApkAssets.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'libs') diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index 9f4a6193b434..cf2ef3070385 100644 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -116,6 +116,7 @@ std::unique_ptr ApkAssets::LoadImpl( if (result != 0) { LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result); + ::CloseArchive(unmanaged_handle); return {}; } -- cgit v1.2.3 From 39adc88fef47133e08039d7f3c2e377dfccda7bd Mon Sep 17 00:00:00 2001 From: Mike Reed Date: Thu, 22 Aug 2019 11:53:05 -0400 Subject: use rect version of join, the other is deprecated Test: make Change-Id: I29f4ff6cea209ac5c053a525ff06cd1bfea6c9c0 --- libs/hwui/DamageAccumulator.cpp | 8 ++++---- libs/hwui/hwui/Bitmap.cpp | 2 +- libs/hwui/renderthread/CanvasContext.cpp | 6 +++--- libs/hwui/tests/common/scenes/RectGridAnimation.cpp | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) (limited to 'libs') diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp index 2d2df52073f4..b39f4f20dc0d 100644 --- a/libs/hwui/DamageAccumulator.cpp +++ b/libs/hwui/DamageAccumulator.cpp @@ -130,7 +130,7 @@ static inline void mapRect(const Matrix4* matrix, const SkRect& in, SkRect* out) // calculations. Just give up and expand to DIRTY_MIN/DIRTY_MAX temp.set(DIRTY_MIN, DIRTY_MIN, DIRTY_MAX, DIRTY_MAX); } - out->join(RECT_ARGS(temp)); + out->join({RECT_ARGS(temp)}); } void DamageAccumulator::applyMatrix4Transform(DirtyStack* frame) { @@ -145,7 +145,7 @@ static inline void applyMatrix(const SkMatrix* transform, SkRect* rect) { // Don't attempt to calculate damage for a perspective transform // as the numbers this works with can break the perspective // calculations. Just give up and expand to DIRTY_MIN/DIRTY_MAX - rect->set(DIRTY_MIN, DIRTY_MIN, DIRTY_MAX, DIRTY_MAX); + rect->setLTRB(DIRTY_MIN, DIRTY_MIN, DIRTY_MAX, DIRTY_MAX); } } } @@ -209,7 +209,7 @@ void DamageAccumulator::applyRenderNodeTransform(DirtyStack* frame) { // Perform clipping if (props.getClipDamageToBounds() && !frame->pendingDirty.isEmpty()) { - if (!frame->pendingDirty.intersect(0, 0, props.getWidth(), props.getHeight())) { + if (!frame->pendingDirty.intersect(SkRect::MakeIWH(props.getWidth(), props.getHeight()))) { frame->pendingDirty.setEmpty(); } } @@ -233,7 +233,7 @@ void DamageAccumulator::applyRenderNodeTransform(DirtyStack* frame) { } void DamageAccumulator::dirty(float left, float top, float right, float bottom) { - mHead->pendingDirty.join(left, top, right, bottom); + mHead->pendingDirty.join({left, top, right, bottom}); } void DamageAccumulator::peekAtDirty(SkRect* dest) const { diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index a1be5b72a5c5..2ba6fbe76de1 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -361,7 +361,7 @@ void Bitmap::getSkBitmap(SkBitmap* outBitmap) { void Bitmap::getBounds(SkRect* bounds) const { SkASSERT(bounds); - bounds->set(0, 0, SkIntToScalar(width()), SkIntToScalar(height())); + bounds->setIWH(width(), height()); } #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 88a0c6ea3085..aad15ab9b0df 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -726,7 +726,7 @@ SkRect CanvasContext::computeDirtyRect(const Frame& frame, SkRect* dirty) { // New surface needs a full draw dirty->setEmpty(); } else { - if (!dirty->isEmpty() && !dirty->intersect(0, 0, frame.width(), frame.height())) { + if (!dirty->isEmpty() && !dirty->intersect(SkRect::MakeIWH(frame.width(), frame.height()))) { ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?", SK_RECT_ARGS(*dirty), frame.width(), frame.height()); dirty->setEmpty(); @@ -735,7 +735,7 @@ SkRect CanvasContext::computeDirtyRect(const Frame& frame, SkRect* dirty) { } if (dirty->isEmpty()) { - dirty->set(0, 0, frame.width(), frame.height()); + dirty->setIWH(frame.width(), frame.height()); } // At this point dirty is the area of the window to update. However, @@ -751,7 +751,7 @@ SkRect CanvasContext::computeDirtyRect(const Frame& frame, SkRect* dirty) { if (frame.bufferAge() > (int)mSwapHistory.size()) { // We don't have enough history to handle this old of a buffer // Just do a full-draw - dirty->set(0, 0, frame.width(), frame.height()); + dirty->setIWH(frame.width(), frame.height()); } else { // At this point we haven't yet added the latest frame // to the damage history (happens below) diff --git a/libs/hwui/tests/common/scenes/RectGridAnimation.cpp b/libs/hwui/tests/common/scenes/RectGridAnimation.cpp index d5ecfaff4f0c..80b5cc191089 100644 --- a/libs/hwui/tests/common/scenes/RectGridAnimation.cpp +++ b/libs/hwui/tests/common/scenes/RectGridAnimation.cpp @@ -37,7 +37,7 @@ public: SkRegion region; for (int xOffset = 0; xOffset < 200; xOffset += 2) { for (int yOffset = 0; yOffset < 200; yOffset += 2) { - region.op(xOffset, yOffset, xOffset + 1, yOffset + 1, SkRegion::kUnion_Op); + region.op({xOffset, yOffset, xOffset + 1, yOffset + 1}, SkRegion::kUnion_Op); } } -- cgit v1.2.3 From 93b712056af78cc3cd36cc36e133344ab2066426 Mon Sep 17 00:00:00 2001 From: Mike Reed Date: Tue, 3 Sep 2019 16:45:12 -0400 Subject: use canvaspriv for legacy flag. This allows us to remove the legacy enum from SkCanvas.h Test: make Change-Id: I892f18169d9eaeead8332a63d1cebf28bba4159c --- libs/hwui/SkiaCanvas.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 54ac0bd9ff75..23140724ef64 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -163,7 +163,7 @@ static inline SkCanvas::SaveLayerFlags layerFlags(SaveFlags::Flags flags) { SkCanvas::SaveLayerFlags layerFlags = 0; if (!(flags & SaveFlags::ClipToLayer)) { - layerFlags |= SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag; + layerFlags |= SkCanvasPriv::kDontClipToLayer_SaveLayerFlag; } return layerFlags; -- cgit v1.2.3 From d013a88277e7cd1e0325d1be7143dce24eb3c95f Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Fri, 26 Oct 2018 13:04:41 -0700 Subject: Adapt to google::protobuf::uint64 type change Protobuf 3.9.1 redefines google::protobuf::uint64 from unsigned long long to uint64_t, which is sometimes unsigned long and sometimes unsigned long long. Use PRIu64 to print it, and add an implementation of ProtoOutputStream::write for long. Bug: 117607748 Test: m checkbuild Exempt-From-Owner-Approval: approved at https://android-review.googlesource.com/q/Ib2d3f4e17857f8ccbbe342ce6678e76b591df510 Change-Id: Ib2d3f4e17857f8ccbbe342ce6678e76b591df510 --- libs/hwui/service/GraphicsStatsService.cpp | 6 ++--- .../include/android/util/ProtoOutputStream.h | 1 + libs/protoutil/src/ProtoOutputStream.cpp | 28 ++++++++++++++++++++++ 3 files changed, 32 insertions(+), 3 deletions(-) (limited to 'libs') diff --git a/libs/hwui/service/GraphicsStatsService.cpp b/libs/hwui/service/GraphicsStatsService.cpp index 8b5912b2081a..12c5b836f711 100644 --- a/libs/hwui/service/GraphicsStatsService.cpp +++ b/libs/hwui/service/GraphicsStatsService.cpp @@ -282,9 +282,9 @@ void dumpAsTextToFd(protos::GraphicsStatsProto* proto, int fd) { return; } dprintf(fd, "\nPackage: %s", proto->package_name().c_str()); - dprintf(fd, "\nVersion: %lld", proto->version_code()); - dprintf(fd, "\nStats since: %lldns", proto->stats_start()); - dprintf(fd, "\nStats end: %lldns", proto->stats_end()); + dprintf(fd, "\nVersion: %" PRId64, proto->version_code()); + dprintf(fd, "\nStats since: %" PRId64 "ns", proto->stats_start()); + dprintf(fd, "\nStats end: %" PRId64 "ns", proto->stats_end()); auto summary = proto->summary(); dprintf(fd, "\nTotal frames rendered: %d", summary.total_frames()); dprintf(fd, "\nJanky frames: %d (%.2f%%)", summary.janky_frames(), diff --git a/libs/protoutil/include/android/util/ProtoOutputStream.h b/libs/protoutil/include/android/util/ProtoOutputStream.h index a6af4757a140..42bf03e6de05 100644 --- a/libs/protoutil/include/android/util/ProtoOutputStream.h +++ b/libs/protoutil/include/android/util/ProtoOutputStream.h @@ -98,6 +98,7 @@ public: bool write(uint64_t fieldId, double val); bool write(uint64_t fieldId, float val); bool write(uint64_t fieldId, int val); + bool write(uint64_t fieldId, long val); bool write(uint64_t fieldId, long long val); bool write(uint64_t fieldId, bool val); bool write(uint64_t fieldId, std::string val); diff --git a/libs/protoutil/src/ProtoOutputStream.cpp b/libs/protoutil/src/ProtoOutputStream.cpp index 6cfa357b580b..ea9b79a0353f 100644 --- a/libs/protoutil/src/ProtoOutputStream.cpp +++ b/libs/protoutil/src/ProtoOutputStream.cpp @@ -115,6 +115,34 @@ ProtoOutputStream::write(uint64_t fieldId, int val) return internalWrite(fieldId, val, "int"); } +bool +ProtoOutputStream::write(uint64_t fieldId, long val) +{ + if (mCompact) return false; + const uint32_t id = (uint32_t)fieldId; + switch (fieldId & FIELD_TYPE_MASK) { + case FIELD_TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break; + case FIELD_TYPE_FLOAT: writeFloatImpl(id, (float)val); break; + case FIELD_TYPE_INT64: writeInt64Impl(id, (long long)val); break; + case FIELD_TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break; + case FIELD_TYPE_INT32: writeInt32Impl(id, (int)val); break; + case FIELD_TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break; + case FIELD_TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break; + case FIELD_TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break; + case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break; + case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break; + case FIELD_TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break; + case FIELD_TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break; + case FIELD_TYPE_ENUM: writeEnumImpl(id, (int)val); break; + case FIELD_TYPE_BOOL: writeBoolImpl(id, val != 0); break; + default: + ALOGW("Field type %d is not supported when writing long val.", + (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT)); + return false; + } + return true; +} + bool ProtoOutputStream::write(uint64_t fieldId, long long val) { -- cgit v1.2.3 From 7ea41b6975b0471ed31d7b634381d93c9aed44ef Mon Sep 17 00:00:00 2001 From: Alec Mouri Date: Wed, 14 Aug 2019 11:44:51 -0700 Subject: [HWUI] use ANativeWindow_getLastQueueDuration api Replaces the query call with an abi-stable one. Bug: 137012798 Test: builds Change-Id: Ia8f01c3be0b79037cef88782913af55f6d00a6a2 --- libs/hwui/renderthread/CanvasContext.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'libs') diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 0ad050d7625e..a0c5e6315cab 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -493,9 +493,7 @@ void CanvasContext::draw() { swap.dequeueDuration = us2ns(ANativeWindow_getLastDequeueDuration(mNativeSurface.get())); } - int durationUs; - mNativeSurface->query(NATIVE_WINDOW_LAST_QUEUE_DURATION, &durationUs); - swap.queueDuration = us2ns(durationUs); + swap.queueDuration = us2ns(ANativeWindow_getLastQueueDuration(mNativeSurface.get())); } else { swap.dequeueDuration = 0; swap.queueDuration = 0; -- cgit v1.2.3 From 57bb0bfb4435bd214dffc9c38501781bdbd16f7d Mon Sep 17 00:00:00 2001 From: Robert Phillips Date: Fri, 6 Sep 2019 13:18:17 -0400 Subject: Switch to using GrContext::setResourceCacheLimit and getResourceCacheLimit The old version that took a maxResourceCount are now deprecated Test: does it compile Change-Id: Ib4d69c8907169329c7765c648f46fa5e4a10bf7a --- libs/hwui/pipeline/skia/SkiaPipeline.cpp | 8 ++++---- libs/hwui/renderthread/CacheManager.cpp | 7 +++---- libs/hwui/renderthread/CacheManager.h | 1 - 3 files changed, 7 insertions(+), 9 deletions(-) (limited to 'libs') diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 530926bf8dd0..67c181b452bb 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -511,13 +511,13 @@ void SkiaPipeline::renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& } void SkiaPipeline::dumpResourceCacheUsage() const { - int resources, maxResources; - size_t bytes, maxBytes; + int resources; + size_t bytes; mRenderThread.getGrContext()->getResourceCacheUsage(&resources, &bytes); - mRenderThread.getGrContext()->getResourceCacheLimits(&maxResources, &maxBytes); + size_t maxBytes = mRenderThread.getGrContext()->getResourceCacheLimit(); SkString log("Resource Cache Usage:\n"); - log.appendf("%8d items out of %d maximum items\n", resources, maxResources); + log.appendf("%8d items\n", resources); log.appendf("%8zu bytes (%.2f MB) out of %.2f MB maximum\n", bytes, bytes * (1.0f / (1024.0f * 1024.0f)), maxBytes * (1.0f / (1024.0f * 1024.0f))); diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index 5469a6810c87..fc268138e071 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -69,8 +69,7 @@ void CacheManager::reset(sk_sp context) { if (context) { mGrContext = std::move(context); - mGrContext->getResourceCacheLimits(&mMaxResources, nullptr); - mGrContext->setResourceCacheLimits(mMaxResources, mMaxResourceBytes); + mGrContext->setResourceCacheLimit(mMaxResourceBytes); } } @@ -119,8 +118,8 @@ void CacheManager::trimMemory(TrimMemoryMode mode) { // limits between the background and max amounts. This causes the unlocked resources // that have persistent data to be purged in LRU order. mGrContext->purgeUnlockedResources(true); - mGrContext->setResourceCacheLimits(mMaxResources, mBackgroundResourceBytes); - mGrContext->setResourceCacheLimits(mMaxResources, mMaxResourceBytes); + mGrContext->setResourceCacheLimit(mBackgroundResourceBytes); + mGrContext->setResourceCacheLimit(mMaxResourceBytes); SkGraphics::SetFontCacheLimit(mBackgroundCpuFontCacheBytes); SkGraphics::SetFontCacheLimit(mMaxCpuFontCacheBytes); break; diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h index ad251f2364fe..d7977cc4566d 100644 --- a/libs/hwui/renderthread/CacheManager.h +++ b/libs/hwui/renderthread/CacheManager.h @@ -71,7 +71,6 @@ private: sk_sp mGrContext; #endif - int mMaxResources = 0; const size_t mMaxResourceBytes; const size_t mBackgroundResourceBytes; -- cgit v1.2.3 From 8d0c5bd2006118af9d27813b608f35ce901695c9 Mon Sep 17 00:00:00 2001 From: Alec Mouri Date: Thu, 22 Aug 2019 19:20:41 -0700 Subject: [HWUI] hook in ANativeWindow_getLastDequeueStartTime Bug: 137012798 Test: builds Change-Id: I37fd2a7c40398053082f606f0a085db0a239e2e0 --- libs/hwui/renderthread/CanvasContext.cpp | 2 +- libs/hwui/renderthread/ReliableSurface.cpp | 2 +- libs/hwui/renderthread/ReliableSurface.h | 4 +--- 3 files changed, 3 insertions(+), 5 deletions(-) (limited to 'libs') diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index a0c5e6315cab..b5ef8f43dfb6 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -484,7 +484,7 @@ void CanvasContext::draw() { swap.swapCompletedTime = systemTime(SYSTEM_TIME_MONOTONIC); swap.vsyncTime = mRenderThread.timeLord().latestVsync(); if (didDraw) { - nsecs_t dequeueStart = mNativeSurface->getLastDequeueStartTime(); + nsecs_t dequeueStart = ANativeWindow_getLastDequeueStartTime(mNativeSurface.get()); if (dequeueStart < mCurrentFrameInfo->get(FrameInfoIndex::SyncStart)) { // Ignoring dequeue duration as it happened prior to frame render start // and thus is not part of the frame. diff --git a/libs/hwui/renderthread/ReliableSurface.cpp b/libs/hwui/renderthread/ReliableSurface.cpp index a44b80457218..864780fb6ae8 100644 --- a/libs/hwui/renderthread/ReliableSurface.cpp +++ b/libs/hwui/renderthread/ReliableSurface.cpp @@ -308,4 +308,4 @@ int ReliableSurface::hook_perform(ANativeWindow* window, int operation, ...) { return result; } -}; // namespace android::uirenderer::renderthread \ No newline at end of file +}; // namespace android::uirenderer::renderthread diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h index 7f1a0781dd87..0d251b1f10e7 100644 --- a/libs/hwui/renderthread/ReliableSurface.h +++ b/libs/hwui/renderthread/ReliableSurface.h @@ -39,8 +39,6 @@ public: int query(int what, int* value) const { return mSurface->query(what, value); } - nsecs_t getLastDequeueStartTime() const { return mSurface->getLastDequeueStartTime(); } - uint64_t getNextFrameNumber() const { return mSurface->getNextFrameNumber(); } int getAndClearError() { @@ -105,4 +103,4 @@ private: static int hook_queueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer); }; -}; // namespace android::uirenderer::renderthread \ No newline at end of file +}; // namespace android::uirenderer::renderthread -- cgit v1.2.3 From 672e9b0ce6f604ad3162f1c83524da97f9a0ed33 Mon Sep 17 00:00:00 2001 From: Alec Mouri Date: Fri, 23 Aug 2019 19:43:37 -0700 Subject: [HWUI] Use ANativeWindow_setDequeueTimeout Don't use the ReliableSurface version now that we have an APEX stub. Bug: 137012798 Test: builds Change-Id: I63d1d9d2ff60b54a75d5b4865a63eb22ac347208 --- libs/hwui/renderthread/CanvasContext.cpp | 2 +- libs/hwui/renderthread/ReliableSurface.h | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'libs') diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index b5ef8f43dfb6..684dc22ee4d1 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -146,7 +146,7 @@ void CanvasContext::setSurface(sp&& surface) { if (surface) { mNativeSurface = new ReliableSurface{std::move(surface)}; // TODO: Fix error handling & re-shorten timeout - mNativeSurface->setDequeueTimeout(4000_ms); + ANativeWindow_setDequeueTimeout(mNativeSurface.get(), 4000_ms); mNativeSurface->enableFrameTimestamps(true); } else { mNativeSurface = nullptr; diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h index 0d251b1f10e7..f768df37ba7d 100644 --- a/libs/hwui/renderthread/ReliableSurface.h +++ b/libs/hwui/renderthread/ReliableSurface.h @@ -31,8 +31,6 @@ public: ReliableSurface(sp&& surface); ~ReliableSurface(); - void setDequeueTimeout(nsecs_t timeout) { mSurface->setDequeueTimeout(timeout); } - int reserveNext(); void allocateBuffers() { mSurface->allocateBuffers(); } -- cgit v1.2.3 From 6e1271be3f7fa01715626a398975fd385a298e21 Mon Sep 17 00:00:00 2001 From: Alec Mouri Date: Sat, 31 Aug 2019 01:52:50 -0700 Subject: [HWUI] Remove nanosecond conversion from duration queries. Now that they return nanoseconds, there's no reason to lose precision. Bug: 137012798 Test: builds Change-Id: I52ca20cb010b4f1829596e0a765b7013ae3665ca --- libs/hwui/renderthread/CanvasContext.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'libs') diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 684dc22ee4d1..30cc007d454b 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -491,9 +491,9 @@ void CanvasContext::draw() { swap.dequeueDuration = 0; } else { swap.dequeueDuration = - us2ns(ANativeWindow_getLastDequeueDuration(mNativeSurface.get())); + ANativeWindow_getLastDequeueDuration(mNativeSurface.get()); } - swap.queueDuration = us2ns(ANativeWindow_getLastQueueDuration(mNativeSurface.get())); + swap.queueDuration = ANativeWindow_getLastQueueDuration(mNativeSurface.get()); } else { swap.dequeueDuration = 0; swap.queueDuration = 0; -- cgit v1.2.3 From 9ca5bbe39f54547dbe585c39459208719a9728b4 Mon Sep 17 00:00:00 2001 From: Derek Sollenberger Date: Wed, 14 Aug 2019 15:50:59 -0400 Subject: Create C API for accessing android.graphics.Bitmap in native code. Restrict access to SkBitmap for libandroid_runtime.so to be only within the boundaries of the graphics module. Test: CtsUiRenderingTestCases Bug: 137655431 Change-Id: I4d0ea227e91d22068966513c4e3a55021b9e924f --- libs/input/Android.bp | 2 +- libs/input/PointerController.cpp | 6 ----- libs/input/SpriteController.cpp | 51 ++++++++++++++++------------------------ libs/input/SpriteController.h | 18 +++++--------- libs/input/tests/Android.bp | 2 +- 5 files changed, 28 insertions(+), 51 deletions(-) (limited to 'libs') diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 16f2917f8df8..6bb896fd7b29 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -20,11 +20,11 @@ cc_library_shared { ], shared_libs: [ + "libandroid_runtime", "libbinder", "libcutils", "liblog", "libutils", - "libhwui", "libgui", "libui", "libinput", diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp index abf083789c23..e4348f2a9b21 100644 --- a/libs/input/PointerController.cpp +++ b/libs/input/PointerController.cpp @@ -24,12 +24,6 @@ #include -#include -#include -#include -#include -#include - namespace android { // --- WeakLooperCallback --- diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp index fd386e9f7a8a..804644c230b9 100644 --- a/libs/input/SpriteController.cpp +++ b/libs/input/SpriteController.cpp @@ -23,11 +23,9 @@ #include #include -#include -#include -#include -#include - +#include +#include +#include #include namespace android { @@ -132,8 +130,8 @@ void SpriteController::doUpdateSprites() { SpriteUpdate& update = updates.editItemAt(i); if (update.state.surfaceControl == NULL && update.state.wantSurfaceVisible()) { - update.state.surfaceWidth = update.state.icon.bitmap.width(); - update.state.surfaceHeight = update.state.icon.bitmap.height(); + update.state.surfaceWidth = update.state.icon.bitmap.getInfo().width; + update.state.surfaceHeight = update.state.icon.bitmap.getInfo().height; update.state.surfaceDrawn = false; update.state.surfaceVisible = false; update.state.surfaceControl = obtainSurface( @@ -154,8 +152,8 @@ void SpriteController::doUpdateSprites() { } if (update.state.wantSurfaceVisible()) { - int32_t desiredWidth = update.state.icon.bitmap.width(); - int32_t desiredHeight = update.state.icon.bitmap.height(); + int32_t desiredWidth = update.state.icon.bitmap.getInfo().width; + int32_t desiredHeight = update.state.icon.bitmap.getInfo().height; if (update.state.surfaceWidth < desiredWidth || update.state.surfaceHeight < desiredHeight) { needApplyTransaction = true; @@ -201,26 +199,22 @@ void SpriteController::doUpdateSprites() { if (status) { ALOGE("Error %d locking sprite surface before drawing.", status); } else { - SkBitmap surfaceBitmap; - ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format); - surfaceBitmap.installPixels(SkImageInfo::MakeN32Premul(outBuffer.width, outBuffer.height), - outBuffer.bits, bpr); + graphics::Paint paint; + paint.setBlendMode(ABLEND_MODE_SRC); - SkCanvas surfaceCanvas(surfaceBitmap); + graphics::Canvas canvas(outBuffer, (int32_t) surface->getBuffersDataSpace()); + canvas.drawBitmap(update.state.icon.bitmap, 0, 0, &paint); - SkPaint paint; - paint.setBlendMode(SkBlendMode::kSrc); - surfaceCanvas.drawBitmap(update.state.icon.bitmap, 0, 0, &paint); + const int iconWidth = update.state.icon.bitmap.getInfo().width; + const int iconHeight = update.state.icon.bitmap.getInfo().height; - if (outBuffer.width > update.state.icon.bitmap.width()) { - paint.setColor(0); // transparent fill color - surfaceCanvas.drawRect(SkRect::MakeLTRB(update.state.icon.bitmap.width(), 0, - outBuffer.width, update.state.icon.bitmap.height()), paint); + if (outBuffer.width > iconWidth) { + paint.setBlendMode(ABLEND_MODE_CLEAR); // clear to transparent + canvas.drawRect({iconWidth, 0, outBuffer.width, iconHeight}, paint); } - if (outBuffer.height > update.state.icon.bitmap.height()) { - paint.setColor(0); // transparent fill color - surfaceCanvas.drawRect(SkRect::MakeLTRB(0, update.state.icon.bitmap.height(), - outBuffer.width, outBuffer.height), paint); + if (outBuffer.height > iconHeight) { + paint.setBlendMode(ABLEND_MODE_CLEAR); // clear to transparent + canvas.drawRect({0, iconHeight, outBuffer.width, outBuffer.height}, paint); } status = surface->unlockAndPost(); @@ -398,12 +392,7 @@ void SpriteController::SpriteImpl::setIcon(const SpriteIcon& icon) { uint32_t dirty; if (icon.isValid()) { - SkBitmap* bitmapCopy = &mLocked.state.icon.bitmap; - if (bitmapCopy->tryAllocPixels(icon.bitmap.info().makeColorType(kN32_SkColorType))) { - icon.bitmap.readPixels(bitmapCopy->info(), bitmapCopy->getPixels(), - bitmapCopy->rowBytes(), 0, 0); - } - + mLocked.state.icon.bitmap = icon.bitmap.copy(ANDROID_BITMAP_FORMAT_RGBA_8888); if (!mLocked.state.icon.isValid() || mLocked.state.icon.hotSpotX != icon.hotSpotX || mLocked.state.icon.hotSpotY != icon.hotSpotY) { diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h index 79a904f5fe65..2513544d4bdf 100644 --- a/libs/input/SpriteController.h +++ b/libs/input/SpriteController.h @@ -20,10 +20,9 @@ #include #include +#include #include -#include - namespace android { /* @@ -56,21 +55,16 @@ struct SpriteTransformationMatrix { */ struct SpriteIcon { inline SpriteIcon() : style(0), hotSpotX(0), hotSpotY(0) { } - inline SpriteIcon(const SkBitmap& bitmap, int32_t style, float hotSpotX, float hotSpotY) : + inline SpriteIcon(const graphics::Bitmap& bitmap, int32_t style, float hotSpotX, float hotSpotY) : bitmap(bitmap), style(style), hotSpotX(hotSpotX), hotSpotY(hotSpotY) { } - SkBitmap bitmap; + graphics::Bitmap bitmap; int32_t style; float hotSpotX; float hotSpotY; inline SpriteIcon copy() const { - SkBitmap bitmapCopy; - if (bitmapCopy.tryAllocPixels(bitmap.info().makeColorType(kN32_SkColorType))) { - bitmap.readPixels(bitmapCopy.info(), bitmapCopy.getPixels(), bitmapCopy.rowBytes(), - 0, 0); - } - return SpriteIcon(bitmapCopy, style, hotSpotX, hotSpotY); + return SpriteIcon(bitmap.copy(ANDROID_BITMAP_FORMAT_RGBA_8888), style, hotSpotX, hotSpotY); } inline void reset() { @@ -81,7 +75,7 @@ struct SpriteIcon { } inline bool isValid() const { - return !bitmap.isNull() && !bitmap.empty(); + return bitmap.isValid() && !bitmap.isEmpty(); } }; @@ -183,7 +177,7 @@ private: * This structure is designed so that it can be copied during updates so that * surfaces can be resized and redrawn without blocking the client by holding a lock * on the sprites for a long time. - * Note that the SkBitmap holds a reference to a shared (and immutable) pixel ref. */ + * Note that the SpriteIcon holds a reference to a shared (and immutable) bitmap. */ struct SpriteState { inline SpriteState() : dirty(0), visible(false), diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index e83b2a78d180..b1e3d6fe845a 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -18,9 +18,9 @@ cc_test { "PointerController_test.cpp", ], shared_libs: [ + "libandroid_runtime", "libinputservice", "libgui", - "libhwui", "libutils", ], static_libs: [ -- cgit v1.2.3 From fe05b7c7ca0739a8abeb05843aa113e31c2af357 Mon Sep 17 00:00:00 2001 From: Nathaniel Nifong Date: Tue, 17 Sep 2019 12:52:52 -0400 Subject: Check for null surface pointer in renderOverdraw Bug: 141160420 Test: built Change-Id: I5d6d4fc11f95f27ab6aa31377d3b94e71f14dca4 --- libs/hwui/pipeline/skia/SkiaPipeline.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'libs') diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 67c181b452bb..3010206cdc5b 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -569,6 +569,7 @@ void SkiaPipeline::renderOverdraw(const LayerUpdateQueue& layers, const SkRect& // Set up the overdraw canvas. SkImageInfo offscreenInfo = SkImageInfo::MakeA8(surface->width(), surface->height()); sk_sp offscreen = surface->makeSurface(offscreenInfo); + LOG_ALWAYS_FATAL_IF(!offscreen, "Failed to create offscreen SkSurface for overdraw viz."); SkOverdrawCanvas overdrawCanvas(offscreen->getCanvas()); // Fake a redraw to replay the draw commands. This will increment the alpha channel -- cgit v1.2.3 From 2e3942246d14a52e4ca2d421ae81b91416a90643 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Wed, 28 Aug 2019 12:10:51 -0700 Subject: Enable CTS verification of overlayable API Allows retrieval of a string representation of overlayable resources that can be compared during CTS testing to verify that the overlayable resources on device match the expected overlayable API. Bug: 135052616 Test: libandroidfw_tests Test: atest OverlayHostTest Change-Id: I613f28c202a0904a917577f932d072111c1aa7bd --- libs/androidfw/AssetManager2.cpp | 58 +++++++++++++++++++++++- libs/androidfw/include/androidfw/AssetManager2.h | 6 ++- libs/androidfw/tests/AssetManager2_test.cpp | 8 +++- 3 files changed, 69 insertions(+), 3 deletions(-) (limited to 'libs') diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 01caf011f644..eec49df79630 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -224,6 +224,62 @@ const std::unordered_map* return &loaded_package->GetOverlayableMap(); } +bool AssetManager2::GetOverlayablesToString(const android::StringPiece& package_name, + std::string* out) const { + uint8_t package_id = 0U; + for (const auto& apk_assets : apk_assets_) { + const LoadedArsc* loaded_arsc = apk_assets->GetLoadedArsc(); + if (loaded_arsc == nullptr) { + continue; + } + + const auto& loaded_packages = loaded_arsc->GetPackages(); + if (loaded_packages.empty()) { + continue; + } + + const auto& loaded_package = loaded_packages[0]; + if (loaded_package->GetPackageName() == package_name) { + package_id = GetAssignedPackageId(loaded_package.get()); + break; + } + } + + if (package_id == 0U) { + ANDROID_LOG(ERROR) << base::StringPrintf("No package with name '%s", package_name.data()); + return false; + } + + const size_t idx = package_ids_[package_id]; + if (idx == 0xff) { + return false; + } + + std::string output; + for (const ConfiguredPackage& package : package_groups_[idx].packages_) { + const LoadedPackage* loaded_package = package.loaded_package_; + for (auto it = loaded_package->begin(); it != loaded_package->end(); it++) { + const OverlayableInfo* info = loaded_package->GetOverlayableInfo(*it); + if (info != nullptr) { + ResourceName res_name; + if (!GetResourceName(*it, &res_name)) { + ANDROID_LOG(ERROR) << base::StringPrintf( + "Unable to retrieve name of overlayable resource 0x%08x", *it); + return false; + } + + const std::string name = ToFormattedResourceString(&res_name); + output.append(base::StringPrintf( + "resource='%s' overlayable='%s' actor='%s' policy='0x%08x'\n", + name.c_str(), info->name.c_str(), info->actor.c_str(), info->policy_flags)); + } + } + } + + *out = std::move(output); + return true; +} + void AssetManager2::SetConfiguration(const ResTable_config& configuration) { const int diff = configuration_.diff(configuration); configuration_ = configuration; @@ -1073,7 +1129,7 @@ void AssetManager2::InvalidateCaches(uint32_t diff) { } } -uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) { +uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) const { for (auto& package_group : package_groups_) { for (auto& package2 : package_group.packages_) { if (package2.loaded_package_ == package) { diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index 1e2b36cb1703..de46081a6aa3 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -124,6 +124,10 @@ class AssetManager2 { // This may be nullptr if the APK represented by `cookie` has no resource table. const DynamicRefTable* GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const; + // Returns a string representation of the overlayable API of a package. + bool GetOverlayablesToString(const android::StringPiece& package_name, + std::string* out) const; + const std::unordered_map* GetOverlayableMapForPackage(uint32_t package_id) const; @@ -308,7 +312,7 @@ class AssetManager2 { const ResolvedBag* GetBag(uint32_t resid, std::vector& child_resids); // Retrieve the assigned package id of the package if loaded into this AssetManager - uint8_t GetAssignedPackageId(const LoadedPackage* package); + uint8_t GetAssignedPackageId(const LoadedPackage* package) const; // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must // have a longer lifetime. diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index 40c8e46e4d84..15910241518d 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -707,7 +707,7 @@ TEST_F(AssetManager2Test, GetLastPathAfterDisablingReturnsEmpty) { EXPECT_EQ("", resultDisabled); } -TEST_F(AssetManager2Test, GetOverlayableMap) { +TEST_F(AssetManager2Test, GetOverlayablesToString) { ResTable_config desired_config; memset(&desired_config, 0, sizeof(desired_config)); @@ -721,6 +721,12 @@ TEST_F(AssetManager2Test, GetOverlayableMap) { ASSERT_EQ(2, map->size()); ASSERT_EQ(map->at("OverlayableResources1"), "overlay://theme"); ASSERT_EQ(map->at("OverlayableResources2"), "overlay://com.android.overlayable"); + + std::string api; + ASSERT_TRUE(assetmanager.GetOverlayablesToString("com.android.overlayable", &api)); + ASSERT_EQ(api.find("not_overlayable"), std::string::npos); + ASSERT_NE(api.find("resource='com.android.overlayable:string/overlayable2' overlayable='OverlayableResources1' actor='overlay://theme' policy='0x0000000a'\n"), + std::string::npos); } } // namespace android -- cgit v1.2.3 From 019adb02932a889ebd200a1de8b8c4780b1477dc Mon Sep 17 00:00:00 2001 From: Stan Iliev Date: Thu, 26 Sep 2019 14:29:48 -0400 Subject: Improve comparison of fractions Fraction comparison does not work for 143.999985 and 2.000001. This CL resolves this corner case. Bug: 140778647 Test: pass CTS Change-Id: I7e39ba822167a3c36c628255a4c79b1ade976929 --- libs/hwui/pipeline/skia/LayerDrawable.cpp | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) (limited to 'libs') diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp index b0173846e582..f839213e9007 100644 --- a/libs/hwui/pipeline/skia/LayerDrawable.cpp +++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp @@ -43,41 +43,28 @@ static bool shouldFilterRect(const SkMatrix& matrix, const SkRect& srcRect, cons if (!matrix.rectStaysRect()) return true; SkRect dstDevRect = matrix.mapRect(dstRect); float dstW, dstH; - bool requiresIntegerTranslate = false; if (MathUtils::isZero(matrix.getScaleX()) && MathUtils::isZero(matrix.getScaleY())) { // Has a 90 or 270 degree rotation, although total matrix may also have scale factors // in m10 and m01. Those scalings are automatically handled by mapRect so comparing // dimensions is sufficient, but swap width and height comparison. dstW = dstDevRect.height(); dstH = dstDevRect.width(); - requiresIntegerTranslate = true; } else { // Handle H/V flips or 180 rotation matrices. Axes may have been mirrored, but // dimensions are still safe to compare directly. dstW = dstDevRect.width(); dstH = dstDevRect.height(); - requiresIntegerTranslate = - matrix.getScaleX() < -NON_ZERO_EPSILON || matrix.getScaleY() < -NON_ZERO_EPSILON; } if (!(MathUtils::areEqual(dstW, srcRect.width()) && MathUtils::areEqual(dstH, srcRect.height()))) { return true; } - if (requiresIntegerTranslate) { - // Device rect and source rect should be integer aligned to ensure there's no difference - // in how nearest-neighbor sampling is resolved. - return !(isIntegerAligned(srcRect.x()) && - isIntegerAligned(srcRect.y()) && - isIntegerAligned(dstDevRect.x()) && - isIntegerAligned(dstDevRect.y())); - } else { - // As long as src and device rects are translated by the same fractional amount, - // filtering won't be needed - return !(MathUtils::areEqual(SkScalarFraction(srcRect.x()), - SkScalarFraction(dstDevRect.x())) && - MathUtils::areEqual(SkScalarFraction(srcRect.y()), - SkScalarFraction(dstDevRect.y()))); - } + // Device rect and source rect should be integer aligned to ensure there's no difference + // in how nearest-neighbor sampling is resolved. + return !(isIntegerAligned(srcRect.x()) && + isIntegerAligned(srcRect.y()) && + isIntegerAligned(dstDevRect.x()) && + isIntegerAligned(dstDevRect.y())); } bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer, -- cgit v1.2.3 From 8a207962ec84904a0fb8c7b5ce547ed2d1b59794 Mon Sep 17 00:00:00 2001 From: John Reck Date: Fri, 4 Oct 2019 13:30:51 -0700 Subject: Drop all caches in UI_HIDDEN Bug: 137853925 Test: none Change-Id: Idf7002d9b07cc6b71b38ce76e7b6382100279a99 --- libs/hwui/renderthread/CacheManager.cpp | 2 +- libs/hwui/renderthread/CanvasContext.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'libs') diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index fc268138e071..20e607dfe6c0 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -135,7 +135,7 @@ void CacheManager::trimStaleResources() { return; } mGrContext->flush(); - mGrContext->purgeResourcesNotUsedInMs(std::chrono::seconds(30)); + mGrContext->performDeferredCleanup(std::chrono::seconds(30)); } sp CacheManager::acquireVectorDrawableAtlas() { diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 30cc007d454b..93fd0c87a361 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -646,11 +646,11 @@ void CanvasContext::trimMemory(RenderThread& thread, int level) { ATRACE_CALL(); if (!thread.getGrContext()) return; ATRACE_CALL(); - if (level >= TRIM_MEMORY_COMPLETE) { + if (level >= TRIM_MEMORY_UI_HIDDEN) { thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete); + } + if (level >= TRIM_MEMORY_COMPLETE) { thread.destroyRenderingContext(); - } else if (level >= TRIM_MEMORY_UI_HIDDEN) { - thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::UiHidden); } } -- cgit v1.2.3 From 05ac51641297ae2cbe9b186b8c369f3a23a156b9 Mon Sep 17 00:00:00 2001 From: John Reck Date: Fri, 4 Oct 2019 13:47:55 -0700 Subject: Drop max texture cache size from 12x to 5x Bug: 137853925 Test: none Change-Id: I8d05871a5f54c8e5d6528a6a8ed48f73464a1221 --- libs/hwui/renderthread/CacheManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index 20e607dfe6c0..dc07f0d84d18 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -39,7 +39,7 @@ namespace renderthread { // to the screen resolution. This is meant to be a conservative default based on // that analysis. The 4.0f is used because the default pixel format is assumed to // be ARGB_8888. -#define SURFACE_SIZE_MULTIPLIER (12.0f * 4.0f) +#define SURFACE_SIZE_MULTIPLIER (5.0f * 4.0f) #define BACKGROUND_RETENTION_PERCENTAGE (0.5f) CacheManager::CacheManager(const DisplayInfo& display) -- cgit v1.2.3 From 83161dcd6aa15c7da161b4ae561b06d20edd2510 Mon Sep 17 00:00:00 2001 From: John Reck Date: Fri, 4 Oct 2019 14:48:27 -0700 Subject: Delete VectorDrawableAtlas Poking around in a few apps it doesn't appear that the VectorDrawableAtlas is achieving sufficient utilization to justify its existence. The potential for draw call merging doesn't seem warranted for the RAM cost of the atlas. Bug: 137853925 Test: builds Change-Id: Id2419bc6dccb6316636d50c568f8fac75a2d563f --- libs/hwui/Android.bp | 2 - libs/hwui/VectorDrawable.cpp | 120 +-------- libs/hwui/VectorDrawable.h | 33 --- libs/hwui/pipeline/skia/SkiaDisplayList.cpp | 5 - libs/hwui/pipeline/skia/SkiaPipeline.cpp | 23 -- libs/hwui/pipeline/skia/SkiaPipeline.h | 13 - libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp | 283 ---------------------- libs/hwui/pipeline/skia/VectorDrawableAtlas.h | 212 ---------------- libs/hwui/renderthread/CacheManager.cpp | 20 -- libs/hwui/renderthread/CacheManager.h | 11 - libs/hwui/renderthread/CanvasContext.cpp | 1 - libs/hwui/renderthread/IRenderPipeline.h | 1 - libs/hwui/renderthread/RenderProxy.cpp | 22 -- libs/hwui/renderthread/RenderProxy.h | 4 - libs/hwui/tests/unit/SkiaPipelineTests.cpp | 33 --- libs/hwui/tests/unit/VectorDrawableAtlasTests.cpp | 163 ------------- 16 files changed, 7 insertions(+), 939 deletions(-) delete mode 100644 libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp delete mode 100644 libs/hwui/pipeline/skia/VectorDrawableAtlas.h delete mode 100644 libs/hwui/tests/unit/VectorDrawableAtlasTests.cpp (limited to 'libs') diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index ae90f117d448..1bcef7314bbf 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -219,7 +219,6 @@ cc_defaults { "pipeline/skia/SkiaPipeline.cpp", "pipeline/skia/SkiaProfileRenderer.cpp", "pipeline/skia/SkiaVulkanPipeline.cpp", - "pipeline/skia/VectorDrawableAtlas.cpp", "pipeline/skia/VkFunctorDrawable.cpp", "pipeline/skia/VkInteropFunctorDrawable.cpp", "renderstate/RenderState.cpp", @@ -345,7 +344,6 @@ cc_test { "tests/unit/ThreadBaseTests.cpp", "tests/unit/TypefaceTests.cpp", "tests/unit/VectorDrawableTests.cpp", - "tests/unit/VectorDrawableAtlasTests.cpp", "tests/unit/WebViewFunctorManagerTests.cpp", ], } diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp index 61403aa93c97..217b0c4a8b98 100644 --- a/libs/hwui/VectorDrawable.cpp +++ b/libs/hwui/VectorDrawable.cpp @@ -496,87 +496,6 @@ Bitmap& Tree::getBitmapUpdateIfDirty() { return *mCache.bitmap; } -void Tree::updateCache(sp& atlas, GrContext* context) { -#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration - SkRect dst; - sk_sp surface = mCache.getSurface(&dst); - bool canReuseSurface = surface && dst.width() >= mProperties.getScaledWidth() && - dst.height() >= mProperties.getScaledHeight(); - if (!canReuseSurface) { - int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth()); - int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight()); - auto atlasEntry = atlas->requestNewEntry(scaledWidth, scaledHeight, context); - if (INVALID_ATLAS_KEY != atlasEntry.key) { - dst = atlasEntry.rect; - surface = atlasEntry.surface; - mCache.setAtlas(atlas, atlasEntry.key); - } else { - // don't draw, if we failed to allocate an offscreen buffer - mCache.clear(); - surface.reset(); - } - } - if (!canReuseSurface || mCache.dirty) { - if (surface) { - Bitmap& bitmap = getBitmapUpdateIfDirty(); - SkBitmap skiaBitmap; - bitmap.getSkBitmap(&skiaBitmap); - surface->writePixels(skiaBitmap, dst.fLeft, dst.fTop); - } - mCache.dirty = false; - } -#endif -} - -void Tree::Cache::setAtlas(sp newAtlas, - skiapipeline::AtlasKey newAtlasKey) { - LOG_ALWAYS_FATAL_IF(newAtlasKey == INVALID_ATLAS_KEY); - clear(); - mAtlas = newAtlas; - mAtlasKey = newAtlasKey; -} - -sk_sp Tree::Cache::getSurface(SkRect* bounds) { - sk_sp surface; -#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration - sp atlas = mAtlas.promote(); - if (atlas.get() && mAtlasKey != INVALID_ATLAS_KEY) { - auto atlasEntry = atlas->getEntry(mAtlasKey); - *bounds = atlasEntry.rect; - surface = atlasEntry.surface; - mAtlasKey = atlasEntry.key; - } -#endif - - return surface; -} - -void Tree::Cache::clear() { -#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration - if (mAtlasKey != INVALID_ATLAS_KEY) { - if (renderthread::RenderThread::isCurrent()) { - sp lockAtlas = mAtlas.promote(); - if (lockAtlas.get()) { - lockAtlas->releaseEntry(mAtlasKey); - } - } else { - // VectorDrawableAtlas can be accessed only on RenderThread. - // Use by-copy capture of the current Cache variables, because "this" may not be valid - // by the time the lambda is evaluated on RenderThread. - renderthread::RenderThread::getInstance().queue().post( - [atlas = mAtlas, atlasKey = mAtlasKey]() { - sp lockAtlas = atlas.promote(); - if (lockAtlas.get()) { - lockAtlas->releaseEntry(atlasKey); - } - }); - } - mAtlasKey = INVALID_ATLAS_KEY; - } - mAtlas = nullptr; -#endif -} - void Tree::draw(SkCanvas* canvas, const SkRect& bounds, const SkPaint& inPaint) { if (canvas->quickReject(bounds)) { // The RenderNode is on screen, but the AVD is not. @@ -587,39 +506,14 @@ void Tree::draw(SkCanvas* canvas, const SkRect& bounds, const SkPaint& inPaint) SkPaint paint = inPaint; paint.setAlpha(mProperties.getRootAlpha() * 255); - if (canvas->getGrContext() == nullptr) { - // Recording to picture, don't use the SkSurface which won't work off of renderthread. - Bitmap& bitmap = getBitmapUpdateIfDirty(); - SkBitmap skiaBitmap; - bitmap.getSkBitmap(&skiaBitmap); - - int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth()); - int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight()); - canvas->drawBitmapRect(skiaBitmap, SkRect::MakeWH(scaledWidth, scaledHeight), bounds, - &paint, SkCanvas::kFast_SrcRectConstraint); - return; - } + Bitmap& bitmap = getBitmapUpdateIfDirty(); + SkBitmap skiaBitmap; + bitmap.getSkBitmap(&skiaBitmap); - SkRect src; - sk_sp vdSurface = mCache.getSurface(&src); - if (vdSurface) { - canvas->drawImageRect(vdSurface->makeImageSnapshot().get(), src, bounds, &paint, - SkCanvas::kFast_SrcRectConstraint); - } else { - // Handle the case when VectorDrawableAtlas has been destroyed, because of memory pressure. - // We render the VD into a temporary standalone buffer and mark the frame as dirty. Next - // frame will be cached into the atlas. - Bitmap& bitmap = getBitmapUpdateIfDirty(); - SkBitmap skiaBitmap; - bitmap.getSkBitmap(&skiaBitmap); - - int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth()); - int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight()); - canvas->drawBitmapRect(skiaBitmap, SkRect::MakeWH(scaledWidth, scaledHeight), bounds, - &paint, SkCanvas::kFast_SrcRectConstraint); - mCache.clear(); - markDirty(); - } + int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth()); + int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight()); + canvas->drawBitmapRect(skiaBitmap, SkRect::MakeWH(scaledWidth, scaledHeight), bounds, + &paint, SkCanvas::kFast_SrcRectConstraint); } void Tree::updateBitmapCache(Bitmap& bitmap, bool useStagingData) { diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h index 9c0bb161380c..e1b6f2adde74 100644 --- a/libs/hwui/VectorDrawable.h +++ b/libs/hwui/VectorDrawable.h @@ -651,46 +651,13 @@ public: void getPaintFor(SkPaint* outPaint, const TreeProperties &props) const; BitmapPalette computePalette(); - /** - * Draws VD into a GPU backed surface. - * This should always be called from RT and it works with Skia pipeline only. - */ - void updateCache(sp& atlas, GrContext* context); - void setAntiAlias(bool aa) { mRootNode->setAntiAlias(aa); } private: class Cache { public: sk_sp bitmap; // used by HWUI pipeline and software - // TODO: use surface instead of bitmap when drawing in software canvas bool dirty = true; - - // the rest of the code in Cache is used by Skia pipelines only - - ~Cache() { clear(); } - - /** - * Stores a weak pointer to the atlas and a key. - */ - void setAtlas(sp atlas, - skiapipeline::AtlasKey newAtlasKey); - - /** - * Gets a surface and bounds from the atlas. - * - * @return nullptr if the altas has been deleted. - */ - sk_sp getSurface(SkRect* bounds); - - /** - * Releases atlas key from the atlas, which makes it available for reuse. - */ - void clear(); - - private: - wp mAtlas; - skiapipeline::AtlasKey mAtlasKey = INVALID_ATLAS_KEY; }; bool allocateBitmapIfNeeded(Cache& cache, int width, int height); diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp index d7076d4cf424..158c3493a90c 100644 --- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp +++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp @@ -150,12 +150,7 @@ bool SkiaDisplayList::prepareListAndChildren( const SkRect& bounds = vectorDrawable->properties().getBounds(); if (intersects(info.screenSize, totalMatrix, bounds)) { isDirty = true; -#ifdef __ANDROID__ // Layoutlib does not support CanvasContext - static_cast(info.canvasContext.getRenderPipeline()) - ->getVectorDrawables() - ->push_back(vectorDrawable); vectorDrawable->setPropertyChangeWillBeConsumed(true); -#endif } } } diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 3010206cdc5b..87ef7fc9a6e1 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -43,7 +43,6 @@ namespace uirenderer { namespace skiapipeline { SkiaPipeline::SkiaPipeline(RenderThread& thread) : mRenderThread(thread) { - mVectorDrawables.reserve(30); } SkiaPipeline::~SkiaPipeline() { @@ -73,18 +72,11 @@ void SkiaPipeline::unpinImages() { mPinnedImages.clear(); } -void SkiaPipeline::onPrepareTree() { - // The only time mVectorDrawables is not empty is if prepare tree was called 2 times without - // a renderFrame in the middle. - mVectorDrawables.clear(); -} - void SkiaPipeline::renderLayers(const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, bool opaque, const LightInfo& lightInfo) { LightingInfo::updateLighting(lightGeometry, lightInfo); ATRACE_NAME("draw layers"); - renderVectorDrawableCache(); renderLayersImpl(*layerUpdateQueue, opaque); layerUpdateQueue->clear(); } @@ -213,19 +205,6 @@ void SkiaPipeline::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) { } } -void SkiaPipeline::renderVectorDrawableCache() { - if (!mVectorDrawables.empty()) { - sp atlas = mRenderThread.cacheManager().acquireVectorDrawableAtlas(); - auto grContext = mRenderThread.getGrContext(); - atlas->prepareForDraw(grContext); - ATRACE_NAME("Update VectorDrawables"); - for (auto vd : mVectorDrawables) { - vd->updateCache(atlas, grContext); - } - mVectorDrawables.clear(); - } -} - static void savePictureAsync(const sk_sp& data, const std::string& filename) { CommonPool::post([data, filename] { if (0 == access(filename.c_str(), F_OK)) { @@ -380,8 +359,6 @@ void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& cli Properties::skpCaptureEnabled = true; } - renderVectorDrawableCache(); - // draw all layers up front renderLayersImpl(layers, opaque); diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h index 37b559f92f05..215ff36ebb2b 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaPipeline.h @@ -41,7 +41,6 @@ public: bool pinImages(std::vector& mutableImages) override; bool pinImages(LsaVector>& images) override { return false; } void unpinImages() override; - void onPrepareTree() override; void renderLayers(const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, bool opaque, const LightInfo& lightInfo) override; @@ -57,8 +56,6 @@ public: const Rect& contentDrawBounds, sk_sp surface, const SkMatrix& preTransform); - std::vector* getVectorDrawables() { return &mVectorDrawables; } - static void prepareToDraw(const renderthread::RenderThread& thread, Bitmap* bitmap); void renderLayersImpl(const LayerUpdateQueue& layers, bool opaque); @@ -93,11 +90,6 @@ private: const std::vector>& nodes, const Rect& contentDrawBounds, sk_sp surface, const SkMatrix& preTransform); - /** - * Render mVectorDrawables into offscreen buffers. - */ - void renderVectorDrawableCache(); - // Called every frame. Normally returns early with screen canvas. // But when capture is enabled, returns an nwaycanvas where commands are also recorded. SkCanvas* tryCapture(SkSurface* surface); @@ -113,11 +105,6 @@ private: std::vector> mPinnedImages; - /** - * populated by prepareTree with dirty VDs - */ - std::vector mVectorDrawables; - // Block of properties used only for debugging to record a SkPicture and save it in a file. // There are three possible ways of recording drawing commands. enum class CaptureMode { diff --git a/libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp b/libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp deleted file mode 100644 index e783f389feb8..000000000000 --- a/libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Copyright (C) 2017 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 "VectorDrawableAtlas.h" - -#include -#include -#include -#include "renderthread/RenderProxy.h" -#include "renderthread/RenderThread.h" -#include "utils/TraceUtils.h" - -namespace android { -namespace uirenderer { -namespace skiapipeline { - -VectorDrawableAtlas::VectorDrawableAtlas(size_t surfaceArea, StorageMode storageMode) - : mWidth((int)std::sqrt(surfaceArea)) - , mHeight((int)std::sqrt(surfaceArea)) - , mStorageMode(storageMode) {} - -void VectorDrawableAtlas::prepareForDraw(GrContext* context) { - if (StorageMode::allowSharedSurface == mStorageMode) { - if (!mSurface) { - mSurface = createSurface(mWidth, mHeight, context); - mRectanizer = std::make_unique(mWidth, mHeight); - mPixelUsedByVDs = 0; - mPixelAllocated = 0; - mConsecutiveFailures = 0; - mFreeRects.clear(); - } else { - if (isFragmented()) { - // Invoke repack outside renderFrame to avoid jank. - renderthread::RenderProxy::repackVectorDrawableAtlas(); - } - } - } -} - -#define MAX_CONSECUTIVE_FAILURES 5 -#define MAX_UNUSED_RATIO 2.0f - -bool VectorDrawableAtlas::isFragmented() { - return mConsecutiveFailures > MAX_CONSECUTIVE_FAILURES && - mPixelUsedByVDs * MAX_UNUSED_RATIO < mPixelAllocated; -} - -void VectorDrawableAtlas::repackIfNeeded(GrContext* context) { - // We repackage when atlas failed to allocate space MAX_CONSECUTIVE_FAILURES consecutive - // times and the atlas allocated pixels are at least MAX_UNUSED_RATIO times higher than pixels - // used by atlas VDs. - if (isFragmented() && mSurface) { - repack(context); - } -} - -// compare to CacheEntry objects based on VD area. -bool VectorDrawableAtlas::compareCacheEntry(const CacheEntry& first, const CacheEntry& second) { - return first.VDrect.width() * first.VDrect.height() < - second.VDrect.width() * second.VDrect.height(); -} - -void VectorDrawableAtlas::repack(GrContext* context) { - ATRACE_CALL(); - sk_sp newSurface; - SkCanvas* canvas = nullptr; - if (StorageMode::allowSharedSurface == mStorageMode) { - newSurface = createSurface(mWidth, mHeight, context); - if (!newSurface) { - return; - } - canvas = newSurface->getCanvas(); - canvas->clear(SK_ColorTRANSPARENT); - mRectanizer = std::make_unique(mWidth, mHeight); - } else { - if (!mSurface) { - return; // nothing to repack - } - mRectanizer.reset(); - } - mFreeRects.clear(); - SkImage* sourceImageAtlas = nullptr; - if (mSurface) { - sourceImageAtlas = mSurface->makeImageSnapshot().get(); - } - - // Sort the list by VD size, which allows for the smallest VDs to get first in the atlas. - // Sorting is safe, because it does not affect iterator validity. - if (mRects.size() <= 100) { - mRects.sort(compareCacheEntry); - } - - for (CacheEntry& entry : mRects) { - SkRect currentVDRect = entry.VDrect; - SkImage* sourceImage; // copy either from the atlas or from a standalone surface - if (entry.surface) { - if (!fitInAtlas(currentVDRect.width(), currentVDRect.height())) { - continue; // don't even try to repack huge VD - } - sourceImage = entry.surface->makeImageSnapshot().get(); - } else { - sourceImage = sourceImageAtlas; - } - size_t VDRectArea = currentVDRect.width() * currentVDRect.height(); - SkIPoint16 pos; - if (canvas && mRectanizer->addRect(currentVDRect.width(), currentVDRect.height(), &pos)) { - SkRect newRect = - SkRect::MakeXYWH(pos.fX, pos.fY, currentVDRect.width(), currentVDRect.height()); - canvas->drawImageRect(sourceImage, currentVDRect, newRect, nullptr); - entry.VDrect = newRect; - entry.rect = newRect; - if (entry.surface) { - // A rectangle moved from a standalone surface to the atlas. - entry.surface = nullptr; - mPixelUsedByVDs += VDRectArea; - } - } else { - // Repack failed for this item. If it is not already, store it in a standalone - // surface. - if (!entry.surface) { - // A rectangle moved from an atlas to a standalone surface. - mPixelUsedByVDs -= VDRectArea; - SkRect newRect = SkRect::MakeWH(currentVDRect.width(), currentVDRect.height()); - entry.surface = createSurface(newRect.width(), newRect.height(), context); - auto tempCanvas = entry.surface->getCanvas(); - tempCanvas->clear(SK_ColorTRANSPARENT); - tempCanvas->drawImageRect(sourceImageAtlas, currentVDRect, newRect, nullptr); - entry.VDrect = newRect; - entry.rect = newRect; - } - } - } - mPixelAllocated = mPixelUsedByVDs; - context->flush(); - mSurface = newSurface; - mConsecutiveFailures = 0; -} - -AtlasEntry VectorDrawableAtlas::requestNewEntry(int width, int height, GrContext* context) { - AtlasEntry result; - if (width <= 0 || height <= 0) { - return result; - } - - if (mSurface) { - const size_t area = width * height; - - // Use a rectanizer to allocate unused space from the atlas surface. - bool notTooBig = fitInAtlas(width, height); - SkIPoint16 pos; - if (notTooBig && mRectanizer->addRect(width, height, &pos)) { - mPixelUsedByVDs += area; - mPixelAllocated += area; - result.rect = SkRect::MakeXYWH(pos.fX, pos.fY, width, height); - result.surface = mSurface; - auto eraseIt = mRects.emplace(mRects.end(), result.rect, result.rect, nullptr); - CacheEntry* entry = &(*eraseIt); - entry->eraseIt = eraseIt; - result.key = reinterpret_cast(entry); - mConsecutiveFailures = 0; - return result; - } - - // Try to reuse atlas memory from rectangles freed by "releaseEntry". - auto freeRectIt = mFreeRects.lower_bound(area); - while (freeRectIt != mFreeRects.end()) { - SkRect& freeRect = freeRectIt->second; - if (freeRect.width() >= width && freeRect.height() >= height) { - result.rect = SkRect::MakeXYWH(freeRect.fLeft, freeRect.fTop, width, height); - result.surface = mSurface; - auto eraseIt = mRects.emplace(mRects.end(), result.rect, freeRect, nullptr); - CacheEntry* entry = &(*eraseIt); - entry->eraseIt = eraseIt; - result.key = reinterpret_cast(entry); - mPixelUsedByVDs += area; - mFreeRects.erase(freeRectIt); - mConsecutiveFailures = 0; - return result; - } - freeRectIt++; - } - - if (notTooBig && mConsecutiveFailures <= MAX_CONSECUTIVE_FAILURES) { - mConsecutiveFailures++; - } - } - - // Allocate a surface for a rectangle that is too big or if atlas is full. - if (nullptr != context) { - result.rect = SkRect::MakeWH(width, height); - result.surface = createSurface(width, height, context); - auto eraseIt = mRects.emplace(mRects.end(), result.rect, result.rect, result.surface); - CacheEntry* entry = &(*eraseIt); - entry->eraseIt = eraseIt; - result.key = reinterpret_cast(entry); - } - - return result; -} - -AtlasEntry VectorDrawableAtlas::getEntry(AtlasKey atlasKey) { - AtlasEntry result; - if (INVALID_ATLAS_KEY != atlasKey) { - CacheEntry* entry = reinterpret_cast(atlasKey); - result.rect = entry->VDrect; - result.surface = entry->surface; - if (!result.surface) { - result.surface = mSurface; - } - result.key = atlasKey; - } - return result; -} - -void VectorDrawableAtlas::releaseEntry(AtlasKey atlasKey) { - if (INVALID_ATLAS_KEY != atlasKey) { - if (!renderthread::RenderThread::isCurrent()) { - { - AutoMutex _lock(mReleaseKeyLock); - mKeysForRelease.push_back(atlasKey); - } - // invoke releaseEntry on the renderthread - renderthread::RenderProxy::releaseVDAtlasEntries(); - return; - } - CacheEntry* entry = reinterpret_cast(atlasKey); - if (!entry->surface) { - // Store freed atlas rectangles in "mFreeRects" and try to reuse them later, when atlas - // is full. - SkRect& removedRect = entry->rect; - size_t rectArea = removedRect.width() * removedRect.height(); - mFreeRects.emplace(rectArea, removedRect); - SkRect& removedVDRect = entry->VDrect; - size_t VDRectArea = removedVDRect.width() * removedVDRect.height(); - mPixelUsedByVDs -= VDRectArea; - mConsecutiveFailures = 0; - } - auto eraseIt = entry->eraseIt; - mRects.erase(eraseIt); - } -} - -void VectorDrawableAtlas::delayedReleaseEntries() { - AutoMutex _lock(mReleaseKeyLock); - for (auto key : mKeysForRelease) { - releaseEntry(key); - } - mKeysForRelease.clear(); -} - -sk_sp VectorDrawableAtlas::createSurface(int width, int height, GrContext* context) { - SkImageInfo info = SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType); - // This must have a top-left origin so that calls to surface->canvas->writePixels - // performs a basic texture upload instead of a more complex drawing operation - return SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, info, 0, kTopLeft_GrSurfaceOrigin, - nullptr); -} - -void VectorDrawableAtlas::setStorageMode(StorageMode mode) { - mStorageMode = mode; - if (StorageMode::disallowSharedSurface == mStorageMode && mSurface) { - mSurface.reset(); - mRectanizer.reset(); - mFreeRects.clear(); - } -} - -} /* namespace skiapipeline */ -} /* namespace uirenderer */ -} /* namespace android */ diff --git a/libs/hwui/pipeline/skia/VectorDrawableAtlas.h b/libs/hwui/pipeline/skia/VectorDrawableAtlas.h deleted file mode 100644 index 5e892aa7e92c..000000000000 --- a/libs/hwui/pipeline/skia/VectorDrawableAtlas.h +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright (C) 2017 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. - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -class GrRectanizer; - -namespace android { -namespace uirenderer { -namespace skiapipeline { - -typedef uintptr_t AtlasKey; - -#define INVALID_ATLAS_KEY 0 - -struct AtlasEntry { - sk_sp surface; - SkRect rect; - AtlasKey key = INVALID_ATLAS_KEY; -}; - -/** - * VectorDrawableAtlas provides offscreen buffers used to draw VD and AnimatedVD. - * VectorDrawableAtlas can allocate a standalone surface or provide a subrect from a shared surface. - * VectorDrawableAtlas is owned by the CacheManager and weak pointers are kept by each - * VectorDrawable that is using it. VectorDrawableAtlas and its surface can be deleted at any time, - * except during a renderFrame call. VectorDrawable does not contain a pointer to atlas SkSurface - * nor any coordinates into the atlas, but instead holds a rectangle "id", which is resolved only - * when drawing. This design makes VectorDrawableAtlas free to move the data internally. - * At draw time a VectorDrawable may find, that its atlas has been deleted, which will make it - * draw in a standalone cache surface not part of an atlas. In this case VD won't use - * VectorDrawableAtlas until the next frame. - * VectorDrawableAtlas tries to fit VDs in the atlas SkSurface. If there is not enough space in - * the atlas, VectorDrawableAtlas creates a standalone surface for each VD. - * When a VectorDrawable is deleted, it invokes VectorDrawableAtlas::releaseEntry, which is keeping - * track of free spaces and allow to reuse the surface for another VD. - */ -// TODO: Check if not using atlas for AnimatedVD is more efficient. -// TODO: For low memory situations, when there are no paint effects in VD, we may render without an -// TODO: offscreen surface. -class VectorDrawableAtlas : public virtual RefBase { -public: - enum class StorageMode { allowSharedSurface, disallowSharedSurface }; - - explicit VectorDrawableAtlas(size_t surfaceArea, - StorageMode storageMode = StorageMode::allowSharedSurface); - - /** - * "prepareForDraw" may allocate a new surface if needed. It may schedule to repack the - * atlas at a later time. - */ - void prepareForDraw(GrContext* context); - - /** - * Repack the atlas if needed, by moving used rectangles into a new atlas surface. - * The goal of repacking is to fix a fragmented atlas. - */ - void repackIfNeeded(GrContext* context); - - /** - * Returns true if atlas is fragmented and repack is needed. - */ - bool isFragmented(); - - /** - * "requestNewEntry" is called by VectorDrawable to allocate a new rectangle area from the atlas - * or create a standalone surface if atlas is full. - * On success it returns a non-negative unique id, which can be used later with "getEntry" and - * "releaseEntry". - */ - AtlasEntry requestNewEntry(int width, int height, GrContext* context); - - /** - * "getEntry" extracts coordinates and surface of a previously created rectangle. - * "atlasKey" is an unique id created by "requestNewEntry". Passing a non-existing "atlasKey" is - * causing an undefined behaviour. - * On success it returns a rectangle Id -> may be same or different from "atlasKey" if - * implementation decides to move the record internally. - */ - AtlasEntry getEntry(AtlasKey atlasKey); - - /** - * "releaseEntry" is invoked when a VectorDrawable is deleted. Passing a non-existing "atlasKey" - * is causing an undefined behaviour. This is the only function in the class that can be - * invoked from any thread. It will marshal internally to render thread if needed. - */ - void releaseEntry(AtlasKey atlasKey); - - void setStorageMode(StorageMode mode); - - /** - * "delayedReleaseEntries" is indirectly invoked by "releaseEntry", when "releaseEntry" is - * invoked from a non render thread. - */ - void delayedReleaseEntries(); - -private: - struct CacheEntry { - CacheEntry(const SkRect& newVDrect, const SkRect& newRect, - const sk_sp& newSurface) - : VDrect(newVDrect), rect(newRect), surface(newSurface) {} - - /** - * size and position of VectorDrawable into the atlas or in "this.surface" - */ - SkRect VDrect; - - /** - * rect allocated in atlas surface or "this.surface". It may be bigger than "VDrect" - */ - SkRect rect; - - /** - * this surface is used if atlas is full or VD is too big - */ - sk_sp surface; - - /** - * iterator is used to delete self with a constant complexity (without traversing the list) - */ - std::list::iterator eraseIt; - }; - - /** - * atlas surface shared by all VDs - */ - sk_sp mSurface; - - std::unique_ptr mRectanizer; - const int mWidth; - const int mHeight; - - /** - * "mRects" keeps records only for rectangles used by VDs. List has nice properties: constant - * complexity to insert and erase and references are not invalidated by insert/erase. - */ - std::list mRects; - - /** - * Rectangles freed by "releaseEntry" are removed from "mRects" and added to "mFreeRects". - * "mFreeRects" is using for an index the rectangle area. There could be more than one free - * rectangle with the same area, which is the reason to use "multimap" instead of "map". - */ - std::multimap mFreeRects; - - /** - * area in atlas used by VectorDrawables (area in standalone surface not counted) - */ - int mPixelUsedByVDs = 0; - - /** - * area allocated in mRectanizer - */ - int mPixelAllocated = 0; - - /** - * Consecutive times we had to allocate standalone surfaces, because atlas was full. - */ - int mConsecutiveFailures = 0; - - /** - * mStorageMode allows using a shared surface to store small vector drawables. - * Using a shared surface can boost the performance by allowing GL ops to be batched, but may - * consume more memory. - */ - StorageMode mStorageMode; - - /** - * mKeysForRelease is used by releaseEntry implementation to pass atlas keys from an arbitrary - * calling thread to the render thread. - */ - std::vector mKeysForRelease; - - /** - * A lock used to protect access to mKeysForRelease. - */ - Mutex mReleaseKeyLock; - - sk_sp createSurface(int width, int height, GrContext* context); - - inline bool fitInAtlas(int width, int height) { - return 2 * width < mWidth && 2 * height < mHeight; - } - - void repack(GrContext* context); - - static bool compareCacheEntry(const CacheEntry& first, const CacheEntry& second); -}; - -} /* namespace skiapipeline */ -} /* namespace uirenderer */ -} /* namespace android */ diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index fc268138e071..ab9b1e20a80d 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -56,10 +56,6 @@ CacheManager::CacheManager(const DisplayInfo& display) , mBackgroundCpuFontCacheBytes(mMaxCpuFontCacheBytes * BACKGROUND_RETENTION_PERCENTAGE) { SkGraphics::SetFontCacheLimit(mMaxCpuFontCacheBytes); - - mVectorDrawableAtlas = new skiapipeline::VectorDrawableAtlas( - mMaxSurfaceArea / 2, - skiapipeline::VectorDrawableAtlas::StorageMode::disallowSharedSurface); } void CacheManager::reset(sk_sp context) { @@ -76,9 +72,6 @@ void CacheManager::reset(sk_sp context) { void CacheManager::destroy() { // cleanup any caches here as the GrContext is about to go away... mGrContext.reset(nullptr); - mVectorDrawableAtlas = new skiapipeline::VectorDrawableAtlas( - mMaxSurfaceArea / 2, - skiapipeline::VectorDrawableAtlas::StorageMode::disallowSharedSurface); } class CommonPoolExecutor : public SkExecutor { @@ -109,7 +102,6 @@ void CacheManager::trimMemory(TrimMemoryMode mode) { switch (mode) { case TrimMemoryMode::Complete: - mVectorDrawableAtlas = new skiapipeline::VectorDrawableAtlas(mMaxSurfaceArea / 2); mGrContext->freeGpuResources(); SkGraphics::PurgeAllCaches(); break; @@ -138,16 +130,6 @@ void CacheManager::trimStaleResources() { mGrContext->purgeResourcesNotUsedInMs(std::chrono::seconds(30)); } -sp CacheManager::acquireVectorDrawableAtlas() { - LOG_ALWAYS_FATAL_IF(mVectorDrawableAtlas.get() == nullptr); - LOG_ALWAYS_FATAL_IF(mGrContext == nullptr); - - /** - * TODO: define memory conditions where we clear the cache (e.g. surface->reset()) - */ - return mVectorDrawableAtlas; -} - void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) { if (!mGrContext) { log.appendFormat("No valid cache instance.\n"); @@ -176,8 +158,6 @@ void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) log.appendFormat("Other Caches:\n"); log.appendFormat(" Current / Maximum\n"); - log.appendFormat(" VectorDrawableAtlas %6.2f kB / %6.2f KB (entries = %zu)\n", 0.0f, 0.0f, - (size_t)0); if (renderState) { if (renderState->mActiveLayers.size() > 0) { diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h index d7977cc4566d..857710babf0c 100644 --- a/libs/hwui/renderthread/CacheManager.h +++ b/libs/hwui/renderthread/CacheManager.h @@ -25,8 +25,6 @@ #include #include -#include "pipeline/skia/VectorDrawableAtlas.h" - namespace android { class Surface; @@ -51,8 +49,6 @@ public: void trimStaleResources(); void dumpMemoryUsage(String8& log, const RenderState* renderState = nullptr); - sp acquireVectorDrawableAtlas(); - size_t getCacheSize() const { return mMaxResourceBytes; } size_t getBackgroundCacheSize() const { return mBackgroundResourceBytes; } @@ -77,13 +73,6 @@ private: const size_t mMaxGpuFontAtlasBytes; const size_t mMaxCpuFontCacheBytes; const size_t mBackgroundCpuFontCacheBytes; - - struct PipelineProps { - const void* pipelineKey = nullptr; - size_t surfaceArea = 0; - }; - - sp mVectorDrawableAtlas; }; } /* namespace renderthread */ diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 30cc007d454b..b41bdf6a174f 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -306,7 +306,6 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy info.out.canDrawThisFrame = true; mAnimationContext->startFrame(info.mode); - mRenderPipeline->onPrepareTree(); for (const sp& node : mRenderNodes) { // Only the primary target node will be drawn full - all other nodes would get drawn in // real time mode. In case of a window, the primary node is the window content and the other diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h index 3b81014c05e2..ef0aa98d4220 100644 --- a/libs/hwui/renderthread/IRenderPipeline.h +++ b/libs/hwui/renderthread/IRenderPipeline.h @@ -80,7 +80,6 @@ public: virtual bool pinImages(std::vector& mutableImages) = 0; virtual bool pinImages(LsaVector>& images) = 0; virtual void unpinImages() = 0; - virtual void onPrepareTree() = 0; virtual SkColorType getSurfaceColorType() const = 0; virtual sk_sp getSurfaceColorSpace() = 0; virtual GrSurfaceOrigin getSurfaceOrigin() = 0; diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 40fbdffaf90a..4f7ad7b69f57 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -24,7 +24,6 @@ #include "Readback.h" #include "Rect.h" #include "WebViewFunctorManager.h" -#include "pipeline/skia/VectorDrawableAtlas.h" #include "renderthread/CanvasContext.h" #include "renderthread/RenderTask.h" #include "renderthread/RenderThread.h" @@ -365,27 +364,6 @@ void RenderProxy::disableVsync() { Properties::disableVsync = true; } -void RenderProxy::repackVectorDrawableAtlas() { - RenderThread& thread = RenderThread::getInstance(); - thread.queue().post([&thread]() { - // The context may be null if trimMemory executed, but then the atlas was deleted too. - if (thread.getGrContext() != nullptr) { - thread.cacheManager().acquireVectorDrawableAtlas()->repackIfNeeded( - thread.getGrContext()); - } - }); -} - -void RenderProxy::releaseVDAtlasEntries() { - RenderThread& thread = RenderThread::getInstance(); - thread.queue().post([&thread]() { - // The context may be null if trimMemory executed, but then the atlas was deleted too. - if (thread.getGrContext() != nullptr) { - thread.cacheManager().acquireVectorDrawableAtlas()->delayedReleaseEntries(); - } - }); -} - void RenderProxy::preload() { // Create RenderThread object and start the thread. Then preload Vulkan/EGL driver. auto& thread = RenderThread::getInstance(); diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index c3eb6ede9bf2..e6fe1d4864da 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -150,10 +150,6 @@ public: ANDROID_API static void preload(); - static void repackVectorDrawableAtlas(); - - static void releaseVDAtlasEntries(); - private: RenderThread& mRenderThread; CanvasContext* mContext; diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp index 958baa78deab..307d13606cb8 100644 --- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp +++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp @@ -61,39 +61,6 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrame) { ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED); } -RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, testOnPrepareTree) { - auto redNode = TestUtils::createSkiaNode( - 0, 0, 1, 1, [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) { - redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver); - }); - - LayerUpdateQueue layerUpdateQueue; - SkRect dirty = SkRectMakeLargest(); - std::vector> renderNodes; - renderNodes.push_back(redNode); - bool opaque = true; - android::uirenderer::Rect contentDrawBounds(0, 0, 1, 1); - auto pipeline = std::make_unique(renderThread); - { - // add a pointer to a deleted vector drawable object in the pipeline - sp dirtyVD(new VectorDrawableRoot(new VectorDrawable::Group())); - dirtyVD->mutateProperties()->setScaledSize(5, 5); - pipeline->getVectorDrawables()->push_back(dirtyVD.get()); - } - - // pipeline should clean list of dirty vector drawables before prepare tree - pipeline->onPrepareTree(); - - auto surface = SkSurface::MakeRasterN32Premul(1, 1); - surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver); - ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); - - // drawFrame will crash if "SkiaPipeline::onPrepareTree" did not clean invalid VD pointer - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface, - SkMatrix::I()); - ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED); -} - RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckOpaque) { auto halfGreenNode = TestUtils::createSkiaNode( 0, 0, 2, 2, [](RenderProperties& props, SkiaRecordingCanvas& bottomHalfGreenCanvas) { diff --git a/libs/hwui/tests/unit/VectorDrawableAtlasTests.cpp b/libs/hwui/tests/unit/VectorDrawableAtlasTests.cpp deleted file mode 100644 index 0c95fdd42851..000000000000 --- a/libs/hwui/tests/unit/VectorDrawableAtlasTests.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (C) 2017 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 - -#include -#include "pipeline/skia/VectorDrawableAtlas.h" -#include "tests/common/TestUtils.h" - -using namespace android; -using namespace android::uirenderer; -using namespace android::uirenderer::renderthread; -using namespace android::uirenderer::skiapipeline; - -RENDERTHREAD_SKIA_PIPELINE_TEST(VectorDrawableAtlas, addGetRemove) { - VectorDrawableAtlas atlas(100 * 100); - atlas.prepareForDraw(renderThread.getGrContext()); - // create 150 rects 10x10, which won't fit in the atlas (atlas can fit no more than 100 rects) - const int MAX_RECTS = 150; - AtlasEntry VDRects[MAX_RECTS]; - - sk_sp atlasSurface; - - // check we are able to allocate new rects - // check that rects in the atlas do not intersect - for (uint32_t i = 0; i < MAX_RECTS; i++) { - VDRects[i] = atlas.requestNewEntry(10, 10, renderThread.getGrContext()); - if (0 == i) { - atlasSurface = VDRects[0].surface; - } - ASSERT_TRUE(VDRects[i].key != INVALID_ATLAS_KEY); - ASSERT_TRUE(VDRects[i].surface.get() != nullptr); - ASSERT_TRUE(VDRects[i].rect.width() == 10 && VDRects[i].rect.height() == 10); - - // nothing in the atlas should intersect - if (atlasSurface.get() == VDRects[i].surface.get()) { - for (uint32_t j = 0; j < i; j++) { - if (atlasSurface.get() == VDRects[j].surface.get()) { - ASSERT_FALSE(VDRects[i].rect.intersect(VDRects[j].rect)); - } - } - } - } - - // first 1/3 rects should all be in the same surface - for (uint32_t i = 1; i < MAX_RECTS / 3; i++) { - ASSERT_NE(VDRects[i].key, VDRects[0].key); - ASSERT_EQ(VDRects[i].surface.get(), atlasSurface.get()); - } - - // first rect is using atlas and last is a standalone surface - ASSERT_NE(VDRects[0].surface.get(), VDRects[MAX_RECTS - 1].surface.get()); - - // check getEntry returns the same surfaces that we had created - for (uint32_t i = 0; i < MAX_RECTS; i++) { - auto VDRect = atlas.getEntry(VDRects[i].key); - ASSERT_TRUE(VDRect.key != INVALID_ATLAS_KEY); - ASSERT_EQ(VDRects[i].key, VDRect.key); - ASSERT_EQ(VDRects[i].surface.get(), VDRect.surface.get()); - ASSERT_EQ(VDRects[i].rect, VDRect.rect); - atlas.releaseEntry(VDRect.key); - } - - // check that any new rects will be allocated in the atlas, even that rectanizer is full. - // rects in the atlas should not intersect. - for (uint32_t i = 0; i < MAX_RECTS / 3; i++) { - VDRects[i] = atlas.requestNewEntry(10, 10, renderThread.getGrContext()); - ASSERT_TRUE(VDRects[i].key != INVALID_ATLAS_KEY); - ASSERT_EQ(VDRects[i].surface.get(), atlasSurface.get()); - ASSERT_TRUE(VDRects[i].rect.width() == 10 && VDRects[i].rect.height() == 10); - for (uint32_t j = 0; j < i; j++) { - ASSERT_FALSE(VDRects[i].rect.intersect(VDRects[j].rect)); - } - } -} - -RENDERTHREAD_SKIA_PIPELINE_TEST(VectorDrawableAtlas, disallowSharedSurface) { - VectorDrawableAtlas atlas(100 * 100); - // don't allow to use a shared surface - atlas.setStorageMode(VectorDrawableAtlas::StorageMode::disallowSharedSurface); - atlas.prepareForDraw(renderThread.getGrContext()); - // create 150 rects 10x10, which won't fit in the atlas (atlas can fit no more than 100 rects) - const int MAX_RECTS = 150; - AtlasEntry VDRects[MAX_RECTS]; - - // check we are able to allocate new rects - // check that rects in the atlas use unique surfaces - for (uint32_t i = 0; i < MAX_RECTS; i++) { - VDRects[i] = atlas.requestNewEntry(10, 10, renderThread.getGrContext()); - ASSERT_TRUE(VDRects[i].key != INVALID_ATLAS_KEY); - ASSERT_TRUE(VDRects[i].surface.get() != nullptr); - ASSERT_TRUE(VDRects[i].rect.width() == 10 && VDRects[i].rect.height() == 10); - - // nothing in the atlas should use the same surface - for (uint32_t j = 0; j < i; j++) { - ASSERT_NE(VDRects[i].surface.get(), VDRects[j].surface.get()); - } - } -} - -RENDERTHREAD_SKIA_PIPELINE_TEST(VectorDrawableAtlas, repack) { - VectorDrawableAtlas atlas(100 * 100); - ASSERT_FALSE(atlas.isFragmented()); - atlas.prepareForDraw(renderThread.getGrContext()); - ASSERT_FALSE(atlas.isFragmented()); - // create 150 rects 10x10, which won't fit in the atlas (atlas can fit no more than 100 rects) - const int MAX_RECTS = 150; - AtlasEntry VDRects[MAX_RECTS]; - - sk_sp atlasSurface; - - // fill the atlas with check we are able to allocate new rects - for (uint32_t i = 0; i < MAX_RECTS; i++) { - VDRects[i] = atlas.requestNewEntry(10, 10, renderThread.getGrContext()); - if (0 == i) { - atlasSurface = VDRects[0].surface; - } - ASSERT_TRUE(VDRects[i].key != INVALID_ATLAS_KEY); - } - - ASSERT_FALSE(atlas.isFragmented()); - - // first 1/3 rects should all be in the same surface - for (uint32_t i = 1; i < MAX_RECTS / 3; i++) { - ASSERT_NE(VDRects[i].key, VDRects[0].key); - ASSERT_EQ(VDRects[i].surface.get(), atlasSurface.get()); - } - - // release all entries - for (uint32_t i = 0; i < MAX_RECTS; i++) { - auto VDRect = atlas.getEntry(VDRects[i].key); - ASSERT_TRUE(VDRect.key != INVALID_ATLAS_KEY); - atlas.releaseEntry(VDRect.key); - } - - ASSERT_FALSE(atlas.isFragmented()); - - // allocate 4x4 rects, which will fragment the atlas badly, because each entry occupies a 10x10 - // area - for (uint32_t i = 0; i < 4 * MAX_RECTS; i++) { - AtlasEntry entry = atlas.requestNewEntry(4, 4, renderThread.getGrContext()); - ASSERT_TRUE(entry.key != INVALID_ATLAS_KEY); - } - - ASSERT_TRUE(atlas.isFragmented()); - - atlas.repackIfNeeded(renderThread.getGrContext()); - - ASSERT_FALSE(atlas.isFragmented()); -} \ No newline at end of file -- cgit v1.2.3 From 9e4f52b2e5c76791edc885397389771692c0de8a Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Thu, 19 Sep 2019 12:15:52 -0700 Subject: Parse and abstract resource mapping This change introduces idmap parsing of tags. The tag allows one to explicitly map resources in the target to either a resource in the overlay or an inline attribute value. Use the android:resourcesMap atttribute on the tag in the android manifest to specify a file to provide the resource mapping. Bug: 135943783 Bug: 135051420 Test: idmap2_tests Change-Id: I1740dcdc01849c43b1f2cb8c6645d666dbb05dba --- libs/androidfw/ResourceTypes.cpp | 5 +++++ libs/androidfw/include/androidfw/ResourceTypes.h | 2 ++ 2 files changed, 7 insertions(+) (limited to 'libs') diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 535386920265..3fe2c5b0e21a 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -1063,6 +1063,11 @@ size_t ResStringPool::bytes() const return (mError == NO_ERROR) ? mHeader->header.size : 0; } +const void* ResStringPool::data() const +{ + return mHeader; +} + bool ResStringPool::isSorted() const { return (mHeader->flags&ResStringPool_header::SORTED_FLAG)!=0; diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index fc635aaeb0d8..c8ace90e6515 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -523,6 +523,8 @@ public: size_t size() const; size_t styleCount() const; size_t bytes() const; + const void* data() const; + bool isSorted() const; bool isUTF8() const; -- cgit v1.2.3 From f846aeec9d87cb0cef58061d400efe3af7008903 Mon Sep 17 00:00:00 2001 From: John Reck Date: Tue, 8 Oct 2019 23:28:41 +0000 Subject: Revert "Drop all caches in UI_HIDDEN" This reverts commit 8a207962ec84904a0fb8c7b5ce547ed2d1b59794. Bug: 142301356 Test: none, speculative Reason for revert: Seems to break alarm? Change-Id: Ia1680d1a937b596297c2eab3e54476daf9589347 --- libs/hwui/renderthread/CacheManager.cpp | 2 +- libs/hwui/renderthread/CanvasContext.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'libs') diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index 20e607dfe6c0..fc268138e071 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -135,7 +135,7 @@ void CacheManager::trimStaleResources() { return; } mGrContext->flush(); - mGrContext->performDeferredCleanup(std::chrono::seconds(30)); + mGrContext->purgeResourcesNotUsedInMs(std::chrono::seconds(30)); } sp CacheManager::acquireVectorDrawableAtlas() { diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 93fd0c87a361..30cc007d454b 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -646,11 +646,11 @@ void CanvasContext::trimMemory(RenderThread& thread, int level) { ATRACE_CALL(); if (!thread.getGrContext()) return; ATRACE_CALL(); - if (level >= TRIM_MEMORY_UI_HIDDEN) { - thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete); - } if (level >= TRIM_MEMORY_COMPLETE) { + thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete); thread.destroyRenderingContext(); + } else if (level >= TRIM_MEMORY_UI_HIDDEN) { + thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::UiHidden); } } -- cgit v1.2.3 From 183e1380cef0f63610497abed01e16036a2ea2d4 Mon Sep 17 00:00:00 2001 From: John Reck Date: Wed, 9 Oct 2019 13:41:18 -0700 Subject: Improve dumping of display list memory usage The first step of improving is measuring. So measure better. Bug: 138856108 Test: dump Change-Id: I076b904a1f0dfb209622c76bcb8778a10cd2b7db --- libs/hwui/RecordingCanvas.h | 1 + libs/hwui/RenderNode.cpp | 14 +++++++++++++- libs/hwui/RenderNode.h | 3 ++- libs/hwui/pipeline/skia/SkiaDisplayList.h | 1 + libs/hwui/utils/LinearAllocator.h | 1 + 5 files changed, 18 insertions(+), 2 deletions(-) (limited to 'libs') diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h index a79b7c00ba04..322eff24dd34 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -69,6 +69,7 @@ public: bool hasText() const { return mHasText; } size_t usedSize() const { return fUsed; } + size_t allocatedSize() const { return fReserved; } private: friend class RecordingCanvas; diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index 8eb5e3d3dfbc..6761435a8171 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -108,7 +108,7 @@ void RenderNode::output(std::ostream& output, uint32_t level) { output << std::endl; } -int RenderNode::getDebugSize() { +int RenderNode::getUsageSize() { int size = sizeof(RenderNode); if (mStagingDisplayList) { size += mStagingDisplayList->getUsedSize(); @@ -119,6 +119,18 @@ int RenderNode::getDebugSize() { return size; } +int RenderNode::getAllocatedSize() { + int size = sizeof(RenderNode); + if (mStagingDisplayList) { + size += mStagingDisplayList->getAllocatedSize(); + } + if (mDisplayList && mDisplayList != mStagingDisplayList) { + size += mDisplayList->getAllocatedSize(); + } + return size; +} + + void RenderNode::prepareTree(TreeInfo& info) { ATRACE_CALL(); LOG_ALWAYS_FATAL_IF(!info.damageAccumulator, "DamageAccumulator missing"); diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index c6db7f1ba60d..d55e5b0ce836 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -102,7 +102,8 @@ public: ANDROID_API void setStagingDisplayList(DisplayList* newData); ANDROID_API void output(); - ANDROID_API int getDebugSize(); + ANDROID_API int getUsageSize(); + ANDROID_API int getAllocatedSize(); bool isRenderable() const { return mDisplayList && !mDisplayList->isEmpty(); } diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h index e3c3273a726a..cdd00db9afdc 100644 --- a/libs/hwui/pipeline/skia/SkiaDisplayList.h +++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h @@ -47,6 +47,7 @@ class FunctorDrawable; class SkiaDisplayList { public: size_t getUsedSize() { return allocator.usedSize() + mDisplayList.usedSize(); } + size_t getAllocatedSize() { return allocator.allocatedSize() + mDisplayList.allocatedSize(); } ~SkiaDisplayList() { /* Given that we are using a LinearStdAllocator to store some of the diff --git a/libs/hwui/utils/LinearAllocator.h b/libs/hwui/utils/LinearAllocator.h index 9c4a1be4b269..539e6544ef02 100644 --- a/libs/hwui/utils/LinearAllocator.h +++ b/libs/hwui/utils/LinearAllocator.h @@ -115,6 +115,7 @@ public: * wasted) */ size_t usedSize() const { return mTotalAllocated - mWastedSpace; } + size_t allocatedSize() const { return mTotalAllocated; } private: LinearAllocator(const LinearAllocator& other); -- cgit v1.2.3 From 9947f1e4fa6b1e58ca38bfe0ed440a8ab4f66465 Mon Sep 17 00:00:00 2001 From: Winson Date: Fri, 16 Aug 2019 10:20:39 -0700 Subject: Add ResourceLoader API with .apk and .arsc support ResourceLoaders allow inserting another .apk/.arsc into AssetManager's resource resolution search. The effect is similar to overlays, where a entry of >= config later in the path list will return that ApkAsset's resource value instead. Because loading from an .arsc is supported, which doesn't contain any actual files, ResourceLoader exposes loadDrawable and loadXmlResourceParser to allow an application load those files from anywhere or create them in code. The data being loaded is either pushed into an .apk or .arsc that mocks itself as the package being "overlaid" and is passed in through ResourcesProvider, an interface with static methods that supports loading from a readable path on disk or a FileDescriptor. The APIs are accessed through a Context's getResources(), which has been changed to be unique per "Context-scope", which is usually the lifetime of the Java object. The exception is that Activities who get their Resources object persisted across recreations maintain that logic for persisting ResourceLoaders. Bug: 135270223 Test: atest FrameworksResourceLoaderTests Change-Id: I6929f0828629ad39a21fa155e7fec73bd75eec7d --- libs/androidfw/Android.bp | 5 +- libs/androidfw/ApkAssets.cpp | 93 ++++++++++++++++++++--- libs/androidfw/Asset.cpp | 20 +++-- libs/androidfw/AssetManager2.cpp | 72 ++++++++++++++++-- libs/androidfw/LoadedArsc.cpp | 29 +++++-- libs/androidfw/include/androidfw/ApkAssets.h | 38 +++++++-- libs/androidfw/include/androidfw/Asset.h | 5 ++ libs/androidfw/include/androidfw/AssetManager2.h | 8 +- libs/androidfw/include/androidfw/LoadedArsc.h | 26 ++++++- libs/androidfw/tests/LoadedArsc_test.cpp | 34 +++++++++ libs/androidfw/tests/data/loader/resources.arsc | Bin 0 -> 620 bytes libs/androidfw/tests/data/system/R.h | 6 ++ 12 files changed, 294 insertions(+), 42 deletions(-) create mode 100644 libs/androidfw/tests/data/loader/resources.arsc (limited to 'libs') diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index eeaefc5b157c..a34a6c0b3724 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -166,7 +166,10 @@ cc_test { static_libs: common_test_libs + ["liblog", "libz"], }, }, - data: ["tests/data/**/*.apk"], + data: [ + "tests/data/**/*.apk", + "tests/data/**/*.arsc", + ], test_suites: ["device-tests"], } diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index cf2ef3070385..b309621435b5 100644 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -42,12 +42,16 @@ static const std::string kResourcesArsc("resources.arsc"); ApkAssets::ApkAssets(ZipArchiveHandle unmanaged_handle, const std::string& path, - time_t last_mod_time) - : zip_handle_(unmanaged_handle, ::CloseArchive), path_(path), last_mod_time_(last_mod_time) { + time_t last_mod_time, + bool for_loader) + : zip_handle_(unmanaged_handle, ::CloseArchive), path_(path), last_mod_time_(last_mod_time), + for_loader(for_loader) { } -std::unique_ptr ApkAssets::Load(const std::string& path, bool system) { - return LoadImpl({} /*fd*/, path, nullptr, nullptr, system, false /*load_as_shared_library*/); +std::unique_ptr ApkAssets::Load(const std::string& path, bool system, + bool for_loader) { + return LoadImpl({} /*fd*/, path, nullptr, nullptr, system, false /*load_as_shared_library*/, + for_loader); } std::unique_ptr ApkAssets::LoadAsSharedLibrary(const std::string& path, @@ -76,9 +80,21 @@ std::unique_ptr ApkAssets::LoadOverlay(const std::string& idmap std::unique_ptr ApkAssets::LoadFromFd(unique_fd fd, const std::string& friendly_name, - bool system, bool force_shared_lib) { + bool system, bool force_shared_lib, + bool for_loader) { return LoadImpl(std::move(fd), friendly_name, nullptr /*idmap_asset*/, nullptr /*loaded_idmap*/, - system, force_shared_lib); + system, force_shared_lib, for_loader); +} + +std::unique_ptr ApkAssets::LoadArsc(const std::string& path, + bool for_loader) { + return LoadArscImpl({} /*fd*/, path, for_loader); +} + +std::unique_ptr ApkAssets::LoadArsc(unique_fd fd, + const std::string& friendly_name, + bool for_loader) { + return LoadArscImpl(std::move(fd), friendly_name, for_loader); } std::unique_ptr ApkAssets::CreateAssetFromFile(const std::string& path) { @@ -104,7 +120,8 @@ std::unique_ptr ApkAssets::CreateAssetFromFile(const std::string& path) { std::unique_ptr ApkAssets::LoadImpl( unique_fd fd, const std::string& path, std::unique_ptr idmap_asset, - std::unique_ptr loaded_idmap, bool system, bool load_as_shared_library) { + std::unique_ptr loaded_idmap, bool system, bool load_as_shared_library, + bool for_loader) { ::ZipArchiveHandle unmanaged_handle; int32_t result; if (fd >= 0) { @@ -123,7 +140,8 @@ std::unique_ptr ApkAssets::LoadImpl( time_t last_mod_time = getFileModDate(path.c_str()); // Wrap the handle in a unique_ptr so it gets automatically closed. - std::unique_ptr loaded_apk(new ApkAssets(unmanaged_handle, path, last_mod_time)); + std::unique_ptr + loaded_apk(new ApkAssets(unmanaged_handle, path, last_mod_time, for_loader)); // Find the resource table. ::ZipEntry entry; @@ -152,7 +170,7 @@ std::unique_ptr ApkAssets::LoadImpl( reinterpret_cast(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)), loaded_apk->resources_asset_->getLength()); loaded_apk->loaded_arsc_ = - LoadedArsc::Load(data, loaded_idmap.get(), system, load_as_shared_library); + LoadedArsc::Load(data, loaded_idmap.get(), system, load_as_shared_library, for_loader); if (loaded_apk->loaded_arsc_ == nullptr) { LOG(ERROR) << "Failed to load '" << kResourcesArsc << "' in APK '" << path << "'."; return {}; @@ -162,8 +180,53 @@ std::unique_ptr ApkAssets::LoadImpl( return std::move(loaded_apk); } +std::unique_ptr ApkAssets::LoadArscImpl(unique_fd fd, + const std::string& path, + bool for_loader) { + std::unique_ptr resources_asset; + + if (fd >= 0) { + resources_asset = std::unique_ptr(Asset::createFromFd(fd.release(), nullptr, + Asset::AccessMode::ACCESS_BUFFER)); + } else { + resources_asset = CreateAssetFromFile(path); + } + + if (resources_asset == nullptr) { + LOG(ERROR) << "Failed to open ARSC '" << path; + return {}; + } + + time_t last_mod_time = getFileModDate(path.c_str()); + + std::unique_ptr loaded_apk(new ApkAssets(nullptr, path, last_mod_time, for_loader)); + loaded_apk->resources_asset_ = std::move(resources_asset); + + const StringPiece data( + reinterpret_cast(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)), + loaded_apk->resources_asset_->getLength()); + loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, nullptr, false, false, for_loader); + if (loaded_apk->loaded_arsc_ == nullptr) { + LOG(ERROR) << "Failed to load '" << kResourcesArsc << path; + return {}; + } + + // Need to force a move for mingw32. + return std::move(loaded_apk); +} + +std::unique_ptr ApkAssets::LoadEmpty(bool for_loader) { + std::unique_ptr loaded_apk(new ApkAssets(nullptr, "", -1, for_loader)); + loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty(); + // Need to force a move for mingw32. + return std::move(loaded_apk); +} + std::unique_ptr ApkAssets::Open(const std::string& path, Asset::AccessMode mode) const { - CHECK(zip_handle_ != nullptr); + // If this is a resource loader from an .arsc, there will be no zip handle + if (zip_handle_ == nullptr) { + return {}; + } ::ZipEntry entry; int32_t result = ::FindEntry(zip_handle_.get(), path, &entry); @@ -205,7 +268,10 @@ std::unique_ptr ApkAssets::Open(const std::string& path, Asset::AccessMod bool ApkAssets::ForEachFile(const std::string& root_path, const std::function& f) const { - CHECK(zip_handle_ != nullptr); + // If this is a resource loader from an .arsc, there will be no zip handle + if (zip_handle_ == nullptr) { + return false; + } std::string root_path_full = root_path; if (root_path_full.back() != '/') { @@ -252,6 +318,11 @@ bool ApkAssets::ForEachFile(const std::string& root_path, } bool ApkAssets::IsUpToDate() const { + // Loaders are invalidated by the app, not the system, so assume up to date + if (for_loader) { + return true; + } + return last_mod_time_ == getFileModDate(path_.c_str()); } diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp index 92125c9da8bb..c132f343713f 100644 --- a/libs/androidfw/Asset.cpp +++ b/libs/androidfw/Asset.cpp @@ -133,14 +133,24 @@ Asset::Asset(void) */ /*static*/ Asset* Asset::createFromFile(const char* fileName, AccessMode mode) { + return createFromFd(open(fileName, O_RDONLY | O_BINARY), fileName, mode); +} + +/* + * Create a new Asset from a file on disk. There is a fair chance that + * the file doesn't actually exist. + * + * We can use "mode" to decide how we want to go about it. + */ +/*static*/ Asset* Asset::createFromFd(const int fd, const char* fileName, AccessMode mode) +{ + if (fd < 0) { + return NULL; + } + _FileAsset* pAsset; status_t result; off64_t length; - int fd; - - fd = open(fileName, O_RDONLY | O_BINARY); - if (fd < 0) - return NULL; /* * Under Linux, the lseek fails if we actually opened a directory. To diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index eec49df79630..e914f37bcac4 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -493,8 +493,12 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri type_flags |= type_spec->GetFlagsForEntryIndex(local_entry_idx); - // If the package is an overlay, then even configurations that are the same MUST be chosen. + + // If the package is an overlay or custom loader, + // then even configurations that are the same MUST be chosen. const bool package_is_overlay = loaded_package->IsOverlay(); + const bool package_is_loader = loaded_package->IsCustomLoader(); + const bool should_overlay = package_is_overlay || package_is_loader; if (use_fast_path) { const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx]; @@ -508,10 +512,28 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri if (best_config == nullptr) { resolution_type = Resolution::Step::Type::INITIAL; } else if (this_config.isBetterThan(*best_config, desired_config)) { - resolution_type = Resolution::Step::Type::BETTER_MATCH; - } else if (package_is_overlay && this_config.compare(*best_config) == 0) { - resolution_type = Resolution::Step::Type::OVERLAID; + if (package_is_loader) { + resolution_type = Resolution::Step::Type::BETTER_MATCH_LOADER; + } else { + resolution_type = Resolution::Step::Type::BETTER_MATCH; + } + } else if (should_overlay && this_config.compare(*best_config) == 0) { + if (package_is_loader) { + resolution_type = Resolution::Step::Type::OVERLAID_LOADER; + } else if (package_is_overlay) { + resolution_type = Resolution::Step::Type::OVERLAID; + } } else { + if (resource_resolution_logging_enabled_) { + if (package_is_loader) { + resolution_type = Resolution::Step::Type::SKIPPED_LOADER; + } else { + resolution_type = Resolution::Step::Type::SKIPPED; + } + resolution_steps.push_back(Resolution::Step{resolution_type, + this_config.toString(), + &loaded_package->GetPackageName()}); + } continue; } @@ -520,6 +542,16 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri const ResTable_type* type = filtered_group.types[i]; const uint32_t offset = LoadedPackage::GetEntryOffset(type, local_entry_idx); if (offset == ResTable_type::NO_ENTRY) { + if (resource_resolution_logging_enabled_) { + if (package_is_loader) { + resolution_type = Resolution::Step::Type::NO_ENTRY_LOADER; + } else { + resolution_type = Resolution::Step::Type::NO_ENTRY; + } + resolution_steps.push_back(Resolution::Step{Resolution::Step::Type::NO_ENTRY, + this_config.toString(), + &loaded_package->GetPackageName()}); + } continue; } @@ -554,9 +586,17 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri if (best_config == nullptr) { resolution_type = Resolution::Step::Type::INITIAL; } else if (this_config.isBetterThan(*best_config, desired_config)) { - resolution_type = Resolution::Step::Type::BETTER_MATCH; - } else if (package_is_overlay && this_config.compare(*best_config) == 0) { - resolution_type = Resolution::Step::Type::OVERLAID; + if (package_is_loader) { + resolution_type = Resolution::Step::Type::BETTER_MATCH_LOADER; + } else { + resolution_type = Resolution::Step::Type::BETTER_MATCH; + } + } else if (should_overlay && this_config.compare(*best_config) == 0) { + if (package_is_overlay) { + resolution_type = Resolution::Step::Type::OVERLAID; + } else if (package_is_loader) { + resolution_type = Resolution::Step::Type::OVERLAID_LOADER; + } } else { continue; } @@ -678,9 +718,27 @@ std::string AssetManager2::GetLastResourceResolution() const { case Resolution::Step::Type::BETTER_MATCH: prefix = "Found better"; break; + case Resolution::Step::Type::BETTER_MATCH_LOADER: + prefix = "Found better in loader"; + break; case Resolution::Step::Type::OVERLAID: prefix = "Overlaid"; break; + case Resolution::Step::Type::OVERLAID_LOADER: + prefix = "Overlaid by loader"; + break; + case Resolution::Step::Type::SKIPPED: + prefix = "Skipped"; + break; + case Resolution::Step::Type::SKIPPED_LOADER: + prefix = "Skipped loader"; + break; + case Resolution::Step::Type::NO_ENTRY: + prefix = "No entry"; + break; + case Resolution::Step::Type::NO_ENTRY_LOADER: + prefix = "No entry for loader"; + break; } if (!prefix.empty()) { diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index 72873abc6a42..882dc0d71759 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -401,7 +401,9 @@ const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const { std::unique_ptr LoadedPackage::Load(const Chunk& chunk, const LoadedIdmap* loaded_idmap, - bool system, bool load_as_shared_library) { + bool system, + bool load_as_shared_library, + bool for_loader) { ATRACE_NAME("LoadedPackage::Load"); std::unique_ptr loaded_package(new LoadedPackage()); @@ -430,6 +432,10 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, loaded_package->overlay_ = true; } + if (for_loader) { + loaded_package->custom_loader_ = true; + } + if (header->header.headerSize >= sizeof(ResTable_package)) { uint32_t type_id_offset = dtohl(header->typeIdOffset); if (type_id_offset > std::numeric_limits::max()) { @@ -696,7 +702,7 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, } bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, - bool load_as_shared_library) { + bool load_as_shared_library, bool for_loader) { const ResTable_header* header = chunk.header(); if (header == nullptr) { LOG(ERROR) << "RES_TABLE_TYPE too small."; @@ -735,7 +741,11 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, packages_seen++; std::unique_ptr loaded_package = - LoadedPackage::Load(child_chunk, loaded_idmap, system_, load_as_shared_library); + LoadedPackage::Load(child_chunk, + loaded_idmap, + system_, + load_as_shared_library, + for_loader); if (!loaded_package) { return false; } @@ -758,9 +768,11 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, } std::unique_ptr LoadedArsc::Load(const StringPiece& data, - const LoadedIdmap* loaded_idmap, bool system, - bool load_as_shared_library) { - ATRACE_NAME("LoadedArsc::LoadTable"); + const LoadedIdmap* loaded_idmap, + bool system, + bool load_as_shared_library, + bool for_loader) { + ATRACE_NAME("LoadedArsc::Load"); // Not using make_unique because the constructor is private. std::unique_ptr loaded_arsc(new LoadedArsc()); @@ -771,7 +783,10 @@ std::unique_ptr LoadedArsc::Load(const StringPiece& data, const Chunk chunk = iter.Next(); switch (chunk.type()) { case RES_TABLE_TYPE: - if (!loaded_arsc->LoadTable(chunk, loaded_idmap, load_as_shared_library)) { + if (!loaded_arsc->LoadTable(chunk, + loaded_idmap, + load_as_shared_library, + for_loader)) { return {}; } break; diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h index 49fc82bff11e..625b68207d83 100644 --- a/libs/androidfw/include/androidfw/ApkAssets.h +++ b/libs/androidfw/include/androidfw/ApkAssets.h @@ -40,7 +40,8 @@ class ApkAssets { // Creates an ApkAssets. // If `system` is true, the package is marked as a system package, and allows some functions to // filter out this package when computing what configurations/resources are available. - static std::unique_ptr Load(const std::string& path, bool system = false); + static std::unique_ptr Load(const std::string& path, bool system = false, + bool for_loader = false); // Creates an ApkAssets, but forces any package with ID 0x7f to be loaded as a shared library. // If `system` is true, the package is marked as a system package, and allows some functions to @@ -63,7 +64,21 @@ class ApkAssets { // If `force_shared_lib` is true, any package with ID 0x7f is loaded as a shared library. static std::unique_ptr LoadFromFd(base::unique_fd fd, const std::string& friendly_name, bool system, - bool force_shared_lib); + bool force_shared_lib, + bool for_loader = false); + + // Creates an empty wrapper ApkAssets from the given path which points to an .arsc. + static std::unique_ptr LoadArsc(const std::string& path, + bool for_loader = false); + + // Creates an empty wrapper ApkAssets from the given file descriptor which points to an .arsc, + // Takes ownership of the file descriptor. + static std::unique_ptr LoadArsc(base::unique_fd fd, + const std::string& friendly_name, + bool resource_loader = false); + + // Creates a totally empty ApkAssets with no resources table and no file entries. + static std::unique_ptr LoadEmpty(bool resource_loader = false); std::unique_ptr Open(const std::string& path, Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM) const; @@ -86,24 +101,33 @@ class ApkAssets { bool IsUpToDate() const; + // Creates an Asset from any file on the file system. + static std::unique_ptr CreateAssetFromFile(const std::string& path); + private: DISALLOW_COPY_AND_ASSIGN(ApkAssets); static std::unique_ptr LoadImpl(base::unique_fd fd, const std::string& path, std::unique_ptr idmap_asset, std::unique_ptr loaded_idmap, - bool system, bool load_as_shared_library); + bool system, bool load_as_shared_library, + bool resource_loader = false); - // Creates an Asset from any file on the file system. - static std::unique_ptr CreateAssetFromFile(const std::string& path); + static std::unique_ptr LoadArscImpl(base::unique_fd fd, + const std::string& path, + bool resource_loader = false); - ApkAssets(ZipArchiveHandle unmanaged_handle, const std::string& path, time_t last_mod_time); + ApkAssets(ZipArchiveHandle unmanaged_handle, + const std::string& path, + time_t last_mod_time, + bool for_loader = false); - using ZipArchivePtr = std::unique_ptr; + using ZipArchivePtr = std::unique_ptr; ZipArchivePtr zip_handle_; const std::string path_; time_t last_mod_time_; + bool for_loader; std::unique_ptr resources_asset_; std::unique_ptr idmap_asset_; std::unique_ptr loaded_arsc_; diff --git a/libs/androidfw/include/androidfw/Asset.h b/libs/androidfw/include/androidfw/Asset.h index 9d12a35395c9..053dbb7864c6 100644 --- a/libs/androidfw/include/androidfw/Asset.h +++ b/libs/androidfw/include/androidfw/Asset.h @@ -121,6 +121,11 @@ public: */ const char* getAssetSource(void) const { return mAssetSource.string(); } + /* + * Create the asset from a file descriptor. + */ + static Asset* createFromFd(const int fd, const char* fileName, AccessMode mode); + protected: /* * Adds this Asset to the global Asset list for debugging and diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index de46081a6aa3..c7348b180648 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -382,7 +382,13 @@ class AssetManager2 { enum class Type { INITIAL, BETTER_MATCH, - OVERLAID + BETTER_MATCH_LOADER, + OVERLAID, + OVERLAID_LOADER, + SKIPPED, + SKIPPED_LOADER, + NO_ENTRY, + NO_ENTRY_LOADER, }; // Marks what kind of override this step was. diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 950f5413f550..1a56876b9686 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -137,7 +137,8 @@ class LoadedPackage { static std::unique_ptr Load(const Chunk& chunk, const LoadedIdmap* loaded_idmap, bool system, - bool load_as_shared_library); + bool load_as_shared_library, + bool load_as_custom_loader); ~LoadedPackage(); @@ -187,6 +188,11 @@ class LoadedPackage { return overlay_; } + // Returns true if this package is a custom loader and should behave like an overlay + inline bool IsCustomLoader() const { + return custom_loader_; + } + // Returns the map of package name to package ID used in this LoadedPackage. At runtime, a // package could have been assigned a different package ID than what this LoadedPackage was // compiled with. AssetManager rewrites the package IDs so that they are compatible at runtime. @@ -260,6 +266,7 @@ class LoadedPackage { bool dynamic_ = false; bool system_ = false; bool overlay_ = false; + bool custom_loader_ = false; bool defines_overlayable_ = false; ByteBucketArray type_specs_; @@ -282,7 +289,8 @@ class LoadedArsc { static std::unique_ptr Load(const StringPiece& data, const LoadedIdmap* loaded_idmap = nullptr, bool system = false, - bool load_as_shared_library = false); + bool load_as_shared_library = false, + bool for_loader = false); // Create an empty LoadedArsc. This is used when an APK has no resources.arsc. static std::unique_ptr CreateEmpty(); @@ -311,7 +319,19 @@ class LoadedArsc { DISALLOW_COPY_AND_ASSIGN(LoadedArsc); LoadedArsc() = default; - bool LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, bool load_as_shared_library); + bool LoadTable( + const Chunk& chunk, + const LoadedIdmap* loaded_idmap, + bool load_as_shared_library, + bool for_loader + ); + + static std::unique_ptr LoadData(std::unique_ptr& loaded_arsc, + const char* data, + size_t length, + const LoadedIdmap* loaded_idmap = nullptr, + bool load_as_shared_library = false, + bool for_loader = false); ResStringPool global_string_pool_; std::vector> packages_; diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp index d58e8d20c8aa..fd57a92c216b 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -25,6 +25,7 @@ #include "data/overlayable/R.h" #include "data/sparse/R.h" #include "data/styles/R.h" +#include "data/system/R.h" namespace app = com::android::app; namespace basic = com::android::basic; @@ -387,6 +388,39 @@ TEST(LoadedArscTest, GetOverlayableMap) { ASSERT_EQ(map.at("OverlayableResources2"), "overlay://com.android.overlayable"); } +TEST(LoadedArscTest, LoadCustomLoader) { + std::string contents; + + std::unique_ptr + asset = ApkAssets::CreateAssetFromFile(GetTestDataPath() + "/loader/resources.arsc"); + + MockLoadedIdmap loaded_idmap; + const StringPiece data( + reinterpret_cast(asset->getBuffer(true /*wordAligned*/)), + asset->getLength()); + + std::unique_ptr loaded_arsc = + LoadedArsc::Load(data, nullptr, false, false, true); + ASSERT_THAT(loaded_arsc, NotNull()); + + const LoadedPackage* package = + loaded_arsc->GetPackageById(get_package_id(android::R::string::cancel)); + ASSERT_THAT(package, NotNull()); + EXPECT_THAT(package->GetPackageName(), StrEq("android")); + EXPECT_THAT(package->GetPackageId(), Eq(0x01)); + + const uint8_t type_index = get_type_id(android::R::string::cancel) - 1; + const uint16_t entry_index = get_entry_id(android::R::string::cancel); + + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); + + const ResTable_type* type = type_spec->types[0]; + ASSERT_THAT(type, NotNull()); + ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull()); +} + // structs with size fields (like Res_value, ResTable_entry) should be // backwards and forwards compatible (aka checking the size field against // sizeof(Res_value) might not be backwards compatible. diff --git a/libs/androidfw/tests/data/loader/resources.arsc b/libs/androidfw/tests/data/loader/resources.arsc new file mode 100644 index 000000000000..2c881f2cdfe5 Binary files /dev/null and b/libs/androidfw/tests/data/loader/resources.arsc differ diff --git a/libs/androidfw/tests/data/system/R.h b/libs/androidfw/tests/data/system/R.h index becb38830fb3..374107484784 100644 --- a/libs/androidfw/tests/data/system/R.h +++ b/libs/androidfw/tests/data/system/R.h @@ -40,6 +40,12 @@ struct R { number = 0x01030000, // sv }; }; + + struct string { + enum : uint32_t { + cancel = 0x01040000, + }; + }; }; } // namespace android -- cgit v1.2.3 From e753ffef5499bc0150827a06d44b50abe78b36dd Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Mon, 23 Sep 2019 09:47:02 -0700 Subject: Idmap format changes for bidirectional lookup This change modifies the idmap file format to allow for target resources to map to arbitrary type/value combinations and to allow overlay resources to be mapped back to target resource ids so references to overlay resources can appear as references to target resources at runtime. The mappings of target resources to overlay resources and vice-versa are both encoded as sparse arrays. Instead of looking up a resource by indexing into an array that maps to the overlay resource id, the runtime will binary search over the sparse array to find the type and value that overlays the target resource. Bug: 135943783 Test: idmap2_tests Change-Id: I5d5344cdb7fe35f4f2e8d6781016299dea5d1e20 --- libs/androidfw/include/androidfw/ResourceTypes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index c8ace90e6515..2efa65a009e1 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -39,7 +39,7 @@ namespace android { constexpr const static uint32_t kIdmapMagic = 0x504D4449u; -constexpr const static uint32_t kIdmapCurrentVersion = 0x00000001u; +constexpr const static uint32_t kIdmapCurrentVersion = 0x00000002u; /** * In C++11, char16_t is defined as *at least* 16 bits. We do a lot of -- cgit v1.2.3 From 8a891d86abdf35b6703785fb3368b39510e91357 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Mon, 1 Jul 2019 09:48:23 -0700 Subject: Allow for RRO internal referencing This change allows RROs to reference their own internal resources as expected. Overlays are loaded as shared libraries so they can have their own resource id space that does not conflict with the resource id space of the target or other overlays. References to overlay resources that override target resources now appear as references to the target resources. Overlay values that are inlined into the xml file specified using android:overlayResources are now able to be used at runtime. See go/rro-references for more information. Bug: 135943783 Test: idmap2_tests Test: libandroidfw_tests Change-Id: Ie349c56d7fd3f7d94b7d595ed6d01dc6b59b6178 --- libs/androidfw/Android.bp | 5 +- libs/androidfw/ApkAssets.cpp | 10 +- libs/androidfw/AssetManager2.cpp | 357 +++++++++++++-------- libs/androidfw/Idmap.cpp | 257 ++++++++++----- libs/androidfw/LoadedArsc.cpp | 49 +-- libs/androidfw/ResourceTypes.cpp | 45 ++- libs/androidfw/TEST_MAPPING | 3 + libs/androidfw/include/androidfw/ApkAssets.h | 12 +- libs/androidfw/include/androidfw/AssetManager2.h | 100 +++--- libs/androidfw/include/androidfw/Idmap.h | 158 +++++++-- libs/androidfw/include/androidfw/LoadedArsc.h | 28 +- libs/androidfw/include/androidfw/ResourceTypes.h | 94 ++++-- libs/androidfw/tests/ApkAssets_test.cpp | 33 -- libs/androidfw/tests/AssetManager2_test.cpp | 4 +- libs/androidfw/tests/Idmap_test.cpp | 269 +++++++++++----- libs/androidfw/tests/LoadedArsc_test.cpp | 72 +---- .../tests/data/loader/AndroidManifest.xml | 20 ++ libs/androidfw/tests/data/loader/build | 27 ++ .../tests/data/loader/res/values/public.xml | 19 ++ .../tests/data/loader/res/values/values.xml | 19 ++ libs/androidfw/tests/data/loader/resources.arsc | Bin 620 -> 652 bytes .../tests/data/overlay/AndroidManifest.xml | 8 +- libs/androidfw/tests/data/overlay/R.h | 38 +++ libs/androidfw/tests/data/overlay/build | 11 +- libs/androidfw/tests/data/overlay/overlay.apk | Bin 5211 -> 2988 bytes libs/androidfw/tests/data/overlay/overlay.idmap | Bin 0 -> 1077 bytes .../tests/data/overlay/res/layout/hello_view.xml | 20 ++ .../tests/data/overlay/res/values/values.xml | 14 +- .../tests/data/overlay/res/xml/overlays.xml | 47 +++ libs/androidfw/tests/data/overlayable/R.h | 37 +++ libs/androidfw/tests/data/overlayable/build | 5 +- .../tests/data/overlayable/overlayable.apk | Bin 3443 -> 6306 bytes .../data/overlayable/res/layout/hello_view.xml | 20 ++ .../data/overlayable/res/values/overlayable.xml | 54 ++-- .../tests/data/overlayable/res/values/public.xml | 17 + .../tests/data/overlayable/res/values/values.xml | 11 + libs/androidfw/tests/data/system/R.h | 3 +- .../tests/data/system/res/values/public.xml | 20 ++ .../tests/data/system/res/values/values.xml | 20 ++ libs/androidfw/tests/data/system/system.apk | Bin 1624 -> 1976 bytes 40 files changed, 1308 insertions(+), 598 deletions(-) create mode 100644 libs/androidfw/tests/data/loader/AndroidManifest.xml create mode 100755 libs/androidfw/tests/data/loader/build create mode 100644 libs/androidfw/tests/data/loader/res/values/public.xml create mode 100644 libs/androidfw/tests/data/loader/res/values/values.xml create mode 100644 libs/androidfw/tests/data/overlay/R.h create mode 100644 libs/androidfw/tests/data/overlay/overlay.idmap create mode 100644 libs/androidfw/tests/data/overlay/res/layout/hello_view.xml create mode 100644 libs/androidfw/tests/data/overlay/res/xml/overlays.xml create mode 100644 libs/androidfw/tests/data/overlayable/res/layout/hello_view.xml create mode 100644 libs/androidfw/tests/data/system/res/values/public.xml create mode 100644 libs/androidfw/tests/data/system/res/values/values.xml (limited to 'libs') diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index a34a6c0b3724..4f52a8800a74 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -167,8 +167,9 @@ cc_test { }, }, data: [ - "tests/data/**/*.apk", - "tests/data/**/*.arsc", + "tests/data/**/*.apk", + "tests/data/**/*.arsc", + "tests/data/**/*.idmap", ], test_suites: ["device-tests"], } diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index b309621435b5..16dbbf61351a 100644 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -45,7 +45,7 @@ ApkAssets::ApkAssets(ZipArchiveHandle unmanaged_handle, time_t last_mod_time, bool for_loader) : zip_handle_(unmanaged_handle, ::CloseArchive), path_(path), last_mod_time_(last_mod_time), - for_loader(for_loader) { + for_loader_(for_loader) { } std::unique_ptr ApkAssets::Load(const std::string& path, bool system, @@ -75,7 +75,7 @@ std::unique_ptr ApkAssets::LoadOverlay(const std::string& idmap return {}; } return LoadImpl({} /*fd*/, loaded_idmap->OverlayApkPath(), std::move(idmap_asset), - std::move(loaded_idmap), system, false /*load_as_shared_library*/); + std::move(loaded_idmap), system, true /*load_as_shared_library*/); } std::unique_ptr ApkAssets::LoadFromFd(unique_fd fd, @@ -165,12 +165,14 @@ std::unique_ptr ApkAssets::LoadImpl( // Must retain ownership of the IDMAP Asset so that all pointers to its mmapped data remain valid. loaded_apk->idmap_asset_ = std::move(idmap_asset); + loaded_apk->loaded_idmap_ = std::move(loaded_idmap); const StringPiece data( reinterpret_cast(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)), loaded_apk->resources_asset_->getLength()); loaded_apk->loaded_arsc_ = - LoadedArsc::Load(data, loaded_idmap.get(), system, load_as_shared_library, for_loader); + LoadedArsc::Load(data, loaded_apk->loaded_idmap_.get(), system, load_as_shared_library, + for_loader); if (loaded_apk->loaded_arsc_ == nullptr) { LOG(ERROR) << "Failed to load '" << kResourcesArsc << "' in APK '" << path << "'."; return {}; @@ -319,7 +321,7 @@ bool ApkAssets::ForEachFile(const std::string& root_path, bool ApkAssets::IsUpToDate() const { // Loaders are invalidated by the app, not the system, so assume up to date - if (for_loader) { + if (for_loader_) { return true; } diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index e914f37bcac4..ca4143f3e215 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -25,6 +25,7 @@ #include "android-base/logging.h" #include "android-base/stringprintf.h" +#include "androidfw/ResourceUtils.h" #include "androidfw/Util.h" #include "utils/ByteOrder.h" #include "utils/Trace.h" @@ -35,15 +36,13 @@ #endif #endif -#include "androidfw/ResourceUtils.h" - namespace android { struct FindEntryResult { // A pointer to the resource table entry for this resource. // If the size of the entry is > sizeof(ResTable_entry), it can be cast to // a ResTable_map_entry and processed as a bag/map. - const ResTable_entry* entry; + ResTable_entry_handle entry; // The configuration for which the resulting entry was defined. This is already swapped to host // endianness. @@ -55,6 +54,9 @@ struct FindEntryResult { // The dynamic package ID map for the package from which this resource came from. const DynamicRefTable* dynamic_ref_table; + // The package name of the resource. + const std::string* package_name; + // The string pool reference to the type's name. This uses a different string pool than // the global string pool, but this is hidden from the caller. StringPoolRef type_string_ref; @@ -83,11 +85,15 @@ void AssetManager2::BuildDynamicRefTable() { package_groups_.clear(); package_ids_.fill(0xff); + // A mapping from apk assets path to the runtime package id of its first loaded package. + std::unordered_map apk_assets_package_ids; + // 0x01 is reserved for the android package. int next_package_id = 0x02; const size_t apk_assets_count = apk_assets_.size(); for (size_t i = 0; i < apk_assets_count; i++) { - const LoadedArsc* loaded_arsc = apk_assets_[i]->GetLoadedArsc(); + const ApkAssets* apk_assets = apk_assets_[i]; + const LoadedArsc* loaded_arsc = apk_assets->GetLoadedArsc(); for (const std::unique_ptr& package : loaded_arsc->GetPackages()) { // Get the package ID or assign one if a shared library. @@ -103,9 +109,37 @@ void AssetManager2::BuildDynamicRefTable() { if (idx == 0xff) { package_ids_[package_id] = idx = static_cast(package_groups_.size()); package_groups_.push_back({}); - DynamicRefTable& ref_table = package_groups_.back().dynamic_ref_table; - ref_table.mAssignedPackageId = package_id; - ref_table.mAppAsLib = package->IsDynamic() && package->GetPackageId() == 0x7f; + + if (apk_assets->IsOverlay()) { + // The target package must precede the overlay package in the apk assets paths in order + // to take effect. + const auto& loaded_idmap = apk_assets->GetLoadedIdmap(); + auto target_package_iter = apk_assets_package_ids.find(loaded_idmap->TargetApkPath()); + if (target_package_iter != apk_assets_package_ids.end()) { + const uint8_t target_package_id = target_package_iter->second; + const uint8_t target_idx = package_ids_[target_package_id]; + CHECK(target_idx != 0xff) << "overlay added to apk_assets_package_ids but does not" + << " have an assigned package group"; + + PackageGroup& target_package_group = package_groups_[target_idx]; + + // Create a special dynamic reference table for the overlay to rewite references to + // overlay resources as references to the target resources they overlay. + auto overlay_table = std::make_shared( + loaded_idmap->GetOverlayDynamicRefTable(target_package_id)); + package_groups_.back().dynamic_ref_table = overlay_table; + + // Add the overlay resource map to the target package's set of overlays. + target_package_group.overlays_.push_back( + ConfiguredOverlay{loaded_idmap->GetTargetResourcesMap(target_package_id, + overlay_table.get()), + static_cast(i)}); + } + } + + DynamicRefTable* ref_table = package_groups_.back().dynamic_ref_table.get(); + ref_table->mAssignedPackageId = package_id; + ref_table->mAppAsLib = package->IsDynamic() && package->GetPackageId() == 0x7f; } PackageGroup* package_group = &package_groups_[idx]; @@ -116,9 +150,11 @@ void AssetManager2::BuildDynamicRefTable() { // Add the package name -> build time ID mappings. for (const DynamicPackageEntry& entry : package->GetDynamicPackageMap()) { String16 package_name(entry.package_name.c_str(), entry.package_name.size()); - package_group->dynamic_ref_table.mEntries.replaceValueFor( + package_group->dynamic_ref_table->mEntries.replaceValueFor( package_name, static_cast(entry.package_id)); } + + apk_assets_package_ids.insert(std::make_pair(apk_assets->GetPath(), package_id)); } } @@ -127,8 +163,8 @@ void AssetManager2::BuildDynamicRefTable() { for (auto iter = package_groups_.begin(); iter != package_groups_end; ++iter) { const std::string& package_name = iter->packages_[0].loaded_package_->GetPackageName(); for (auto iter2 = package_groups_.begin(); iter2 != package_groups_end; ++iter2) { - iter2->dynamic_ref_table.addMapping(String16(package_name.c_str(), package_name.size()), - iter->dynamic_ref_table.mAssignedPackageId); + iter2->dynamic_ref_table->addMapping(String16(package_name.c_str(), package_name.size()), + iter->dynamic_ref_table->mAssignedPackageId); } } } @@ -161,13 +197,13 @@ void AssetManager2::DumpToLog() const { (loaded_package->IsDynamic() ? " dynamic" : "")); } LOG(INFO) << base::StringPrintf("PG (%02x): ", - package_group.dynamic_ref_table.mAssignedPackageId) + package_group.dynamic_ref_table->mAssignedPackageId) << list; for (size_t i = 0; i < 256; i++) { - if (package_group.dynamic_ref_table.mLookupTable[i] != 0) { + if (package_group.dynamic_ref_table->mLookupTable[i] != 0) { LOG(INFO) << base::StringPrintf(" e[0x%02x] -> 0x%02x", (uint8_t) i, - package_group.dynamic_ref_table.mLookupTable[i]); + package_group.dynamic_ref_table->mLookupTable[i]); } } } @@ -189,14 +225,15 @@ const DynamicRefTable* AssetManager2::GetDynamicRefTableForPackage(uint32_t pack if (idx == 0xff) { return nullptr; } - return &package_groups_[idx].dynamic_ref_table; + return package_groups_[idx].dynamic_ref_table.get(); } -const DynamicRefTable* AssetManager2::GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const { +std::shared_ptr AssetManager2::GetDynamicRefTableForCookie( + ApkAssetsCookie cookie) const { for (const PackageGroup& package_group : package_groups_) { for (const ApkAssetsCookie& package_cookie : package_group.cookies_) { if (package_cookie == cookie) { - return &package_group.dynamic_ref_table; + return package_group.dynamic_ref_table; } } } @@ -290,21 +327,45 @@ void AssetManager2::SetConfiguration(const ResTable_config& configuration) { } } +std::set AssetManager2::GetNonSystemOverlayPaths() const { + std::set non_system_overlays; + for (const PackageGroup& package_group : package_groups_) { + bool found_system_package = false; + for (const ConfiguredPackage& package : package_group.packages_) { + if (package.loaded_package_->IsSystem()) { + found_system_package = true; + break; + } + } + + if (!found_system_package) { + for (const ConfiguredOverlay& overlay : package_group.overlays_) { + non_system_overlays.insert(apk_assets_[overlay.cookie]->GetPath()); + } + } + } + + return non_system_overlays; +} + std::set AssetManager2::GetResourceConfigurations(bool exclude_system, bool exclude_mipmap) const { ATRACE_NAME("AssetManager::GetResourceConfigurations"); + const auto non_system_overlays = + (exclude_system) ? GetNonSystemOverlayPaths() : std::set(); + std::set configurations; for (const PackageGroup& package_group : package_groups_) { - bool found_system_package = false; - for (const ConfiguredPackage& package : package_group.packages_) { + for (size_t i = 0; i < package_group.packages_.size(); i++) { + const ConfiguredPackage& package = package_group.packages_[i]; if (exclude_system && package.loaded_package_->IsSystem()) { - found_system_package = true; continue; } - if (exclude_system && package.loaded_package_->IsOverlay() && found_system_package) { - // Overlays must appear after the target package to take effect. Any overlay found in the - // same package as a system package is able to overlay system resources. + auto apk_assets = apk_assets_[package_group.cookies_[i]]; + if (exclude_system && apk_assets->IsOverlay() + && non_system_overlays.find(apk_assets->GetPath()) == non_system_overlays.end()) { + // Exclude overlays that target system resources. continue; } @@ -318,17 +379,20 @@ std::set AssetManager2::GetResourceLocales(bool exclude_system, bool merge_equivalent_languages) const { ATRACE_NAME("AssetManager::GetResourceLocales"); std::set locales; + const auto non_system_overlays = + (exclude_system) ? GetNonSystemOverlayPaths() : std::set(); + for (const PackageGroup& package_group : package_groups_) { - bool found_system_package = false; - for (const ConfiguredPackage& package : package_group.packages_) { + for (size_t i = 0; i < package_group.packages_.size(); i++) { + const ConfiguredPackage& package = package_group.packages_[i]; if (exclude_system && package.loaded_package_->IsSystem()) { - found_system_package = true; continue; } - if (exclude_system && package.loaded_package_->IsOverlay() && found_system_package) { - // Overlays must appear after the target package to take effect. Any overlay found in the - // same package as a system package is able to overlay system resources. + auto apk_assets = apk_assets_[package_group.cookies_[i]]; + if (exclude_system && apk_assets->IsOverlay() + && non_system_overlays.find(apk_assets->GetPath()) == non_system_overlays.end()) { + // Exclude overlays that target system resources. continue; } @@ -424,6 +488,12 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri bool /*stop_at_first_match*/, bool ignore_configuration, FindEntryResult* out_entry) const { + if (resource_resolution_logging_enabled_) { + // Clear the last logged resource resolution. + ResetResourceResolution(); + last_resolution_.resid = resid; + } + // Might use this if density_override != 0. ResTable_config density_override_config; @@ -435,6 +505,7 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri desired_config = &density_override_config; } + // Retrieve the package group from the package id of the resource id. if (!is_valid_resid(resid)) { LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid); return kInvalidCookie; @@ -443,8 +514,7 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri const uint32_t package_id = get_package_id(resid); const uint8_t type_idx = get_type_id(resid) - 1; const uint16_t entry_idx = get_entry_id(resid); - - const uint8_t package_idx = package_ids_[package_id]; + uint8_t package_idx = package_ids_[package_id]; if (package_idx == 0xff) { ANDROID_LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.", package_id, resid); @@ -452,8 +522,71 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri } const PackageGroup& package_group = package_groups_[package_idx]; - const size_t package_count = package_group.packages_.size(); + ApkAssetsCookie cookie = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config, + false /* stop_at_first_match */, + ignore_configuration, out_entry); + if (UNLIKELY(cookie == kInvalidCookie)) { + return kInvalidCookie; + } + + if (!apk_assets_[cookie]->IsLoader()) { + for (const auto& id_map : package_group.overlays_) { + auto overlay_entry = id_map.overlay_res_maps_.Lookup(resid); + if (!overlay_entry) { + // No id map entry exists for this target resource. + continue; + } + + if (overlay_entry.IsTableEntry()) { + // The target resource is overlaid by an inline value not represented by a resource. + out_entry->entry = overlay_entry.GetTableEntry(); + out_entry->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable(); + cookie = id_map.cookie; + continue; + } + + FindEntryResult overlay_result; + ApkAssetsCookie overlay_cookie = FindEntry(overlay_entry.GetResourceId(), density_override, + false /* stop_at_first_match */, + ignore_configuration, &overlay_result); + if (UNLIKELY(overlay_cookie == kInvalidCookie)) { + continue; + } + if (!overlay_result.config.isBetterThan(out_entry->config, desired_config) + && overlay_result.config.compare(out_entry->config) != 0) { + // The configuration of the entry for the overlay must be equal to or better than the target + // configuration to be chosen as the better value. + continue; + } + + cookie = overlay_cookie; + out_entry->entry = std::move(overlay_result.entry); + out_entry->config = overlay_result.config; + out_entry->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable(); + if (resource_resolution_logging_enabled_) { + last_resolution_.steps.push_back( + Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result.config.toString(), + &package_group.packages_[0].loaded_package_->GetPackageName()}); + } + } + } + + if (resource_resolution_logging_enabled_) { + last_resolution_.cookie = cookie; + last_resolution_.type_string_ref = out_entry->type_string_ref; + last_resolution_.entry_string_ref = out_entry->entry_string_ref; + } + + return cookie; +} + +ApkAssetsCookie AssetManager2::FindEntryInternal(const PackageGroup& package_group, + uint8_t type_idx, uint16_t entry_idx, + const ResTable_config& desired_config, + bool /*stop_at_first_match*/, + bool ignore_configuration, + FindEntryResult* out_entry) const { ApkAssetsCookie best_cookie = kInvalidCookie; const LoadedPackage* best_package = nullptr; const ResTable_type* best_type = nullptr; @@ -462,13 +595,14 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri uint32_t best_offset = 0u; uint32_t type_flags = 0u; - Resolution::Step::Type resolution_type; + Resolution::Step::Type resolution_type = Resolution::Step::Type::NO_ENTRY; std::vector resolution_steps; // If desired_config is the same as the set configuration, then we can use our filtered list // and we don't need to match the configurations, since they already matched. - const bool use_fast_path = !ignore_configuration && desired_config == &configuration_; + const bool use_fast_path = !ignore_configuration && &desired_config == &configuration_; + const size_t package_count = package_group.packages_.size(); for (size_t pi = 0; pi < package_count; pi++) { const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi]; const LoadedPackage* loaded_package = loaded_package_impl.loaded_package_; @@ -481,24 +615,10 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri continue; } - uint16_t local_entry_idx = entry_idx; - - // If there is an IDMAP supplied with this package, translate the entry ID. - if (type_spec->idmap_entries != nullptr) { - if (!LoadedIdmap::Lookup(type_spec->idmap_entries, local_entry_idx, &local_entry_idx)) { - // There is no mapping, so the resource is not meant to be in this overlay package. - continue; - } - } - - type_flags |= type_spec->GetFlagsForEntryIndex(local_entry_idx); - - // If the package is an overlay or custom loader, // then even configurations that are the same MUST be chosen. - const bool package_is_overlay = loaded_package->IsOverlay(); const bool package_is_loader = loaded_package->IsCustomLoader(); - const bool should_overlay = package_is_overlay || package_is_loader; + type_flags |= type_spec->GetFlagsForEntryIndex(entry_idx); if (use_fast_path) { const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx]; @@ -511,25 +631,15 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri // configurations that do NOT match have been filtered-out. if (best_config == nullptr) { resolution_type = Resolution::Step::Type::INITIAL; - } else if (this_config.isBetterThan(*best_config, desired_config)) { - if (package_is_loader) { - resolution_type = Resolution::Step::Type::BETTER_MATCH_LOADER; - } else { - resolution_type = Resolution::Step::Type::BETTER_MATCH; - } - } else if (should_overlay && this_config.compare(*best_config) == 0) { - if (package_is_loader) { - resolution_type = Resolution::Step::Type::OVERLAID_LOADER; - } else if (package_is_overlay) { - resolution_type = Resolution::Step::Type::OVERLAID; - } + } else if (this_config.isBetterThan(*best_config, &desired_config)) { + resolution_type = (package_is_loader) ? Resolution::Step::Type::BETTER_MATCH_LOADER + : Resolution::Step::Type::BETTER_MATCH; + } else if (package_is_loader && this_config.compare(*best_config) == 0) { + resolution_type = Resolution::Step::Type::OVERLAID_LOADER; } else { if (resource_resolution_logging_enabled_) { - if (package_is_loader) { - resolution_type = Resolution::Step::Type::SKIPPED_LOADER; - } else { - resolution_type = Resolution::Step::Type::SKIPPED; - } + resolution_type = (package_is_loader) ? Resolution::Step::Type::SKIPPED_LOADER + : Resolution::Step::Type::SKIPPED; resolution_steps.push_back(Resolution::Step{resolution_type, this_config.toString(), &loaded_package->GetPackageName()}); @@ -540,7 +650,7 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri // The configuration matches and is better than the previous selection. // Find the entry value if it exists for this configuration. const ResTable_type* type = filtered_group.types[i]; - const uint32_t offset = LoadedPackage::GetEntryOffset(type, local_entry_idx); + const uint32_t offset = LoadedPackage::GetEntryOffset(type, entry_idx); if (offset == ResTable_type::NO_ENTRY) { if (resource_resolution_logging_enabled_) { if (package_is_loader) { @@ -548,7 +658,7 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri } else { resolution_type = Resolution::Step::Type::NO_ENTRY; } - resolution_steps.push_back(Resolution::Step{Resolution::Step::Type::NO_ENTRY, + resolution_steps.push_back(Resolution::Step{resolution_type, this_config.toString(), &loaded_package->GetPackageName()}); } @@ -562,9 +672,9 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri best_offset = offset; if (resource_resolution_logging_enabled_) { - resolution_steps.push_back(Resolution::Step{resolution_type, - this_config.toString(), - &loaded_package->GetPackageName()}); + last_resolution_.steps.push_back(Resolution::Step{resolution_type, + this_config.toString(), + &loaded_package->GetPackageName()}); } } } else { @@ -579,24 +689,17 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri if (!ignore_configuration) { this_config.copyFromDtoH((*iter)->config); - if (!this_config.match(*desired_config)) { + if (!this_config.match(desired_config)) { continue; } if (best_config == nullptr) { resolution_type = Resolution::Step::Type::INITIAL; - } else if (this_config.isBetterThan(*best_config, desired_config)) { - if (package_is_loader) { - resolution_type = Resolution::Step::Type::BETTER_MATCH_LOADER; - } else { - resolution_type = Resolution::Step::Type::BETTER_MATCH; - } - } else if (should_overlay && this_config.compare(*best_config) == 0) { - if (package_is_overlay) { - resolution_type = Resolution::Step::Type::OVERLAID; - } else if (package_is_loader) { - resolution_type = Resolution::Step::Type::OVERLAID_LOADER; - } + } else if (this_config.isBetterThan(*best_config, &desired_config)) { + resolution_type = (package_is_loader) ? Resolution::Step::Type::BETTER_MATCH_LOADER + : Resolution::Step::Type::BETTER_MATCH; + } else if (package_is_loader && this_config.compare(*best_config) == 0) { + resolution_type = Resolution::Step::Type::OVERLAID_LOADER; } else { continue; } @@ -604,7 +707,7 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri // The configuration matches and is better than the previous selection. // Find the entry value if it exists for this configuration. - const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, local_entry_idx); + const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, entry_idx); if (offset == ResTable_type::NO_ENTRY) { continue; } @@ -622,9 +725,9 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri } if (resource_resolution_logging_enabled_) { - resolution_steps.push_back(Resolution::Step{resolution_type, - this_config.toString(), - &loaded_package->GetPackageName()}); + last_resolution_.steps.push_back(Resolution::Step{resolution_type, + this_config.toString(), + &loaded_package->GetPackageName()}); } } } @@ -639,38 +742,30 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri return kInvalidCookie; } - out_entry->entry = best_entry; + out_entry->entry = ResTable_entry_handle::unmanaged(best_entry); out_entry->config = *best_config; out_entry->type_flags = type_flags; + out_entry->package_name = &best_package->GetPackageName(); out_entry->type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1); out_entry->entry_string_ref = - StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index); - out_entry->dynamic_ref_table = &package_group.dynamic_ref_table; - - if (resource_resolution_logging_enabled_) { - last_resolution.resid = resid; - last_resolution.cookie = best_cookie; - last_resolution.steps = resolution_steps; - - // Cache only the type/entry refs since that's all that's needed to build name - last_resolution.type_string_ref = - StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1); - last_resolution.entry_string_ref = - StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index); - } + StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index); + out_entry->dynamic_ref_table = package_group.dynamic_ref_table.get(); return best_cookie; } +void AssetManager2::ResetResourceResolution() const { + last_resolution_.cookie = kInvalidCookie; + last_resolution_.resid = 0; + last_resolution_.steps.clear(); + last_resolution_.type_string_ref = StringPoolRef(); + last_resolution_.entry_string_ref = StringPoolRef(); +} + void AssetManager2::SetResourceResolutionLoggingEnabled(bool enabled) { resource_resolution_logging_enabled_ = enabled; - if (!enabled) { - last_resolution.cookie = kInvalidCookie; - last_resolution.resid = 0; - last_resolution.steps.clear(); - last_resolution.type_string_ref = StringPoolRef(); - last_resolution.entry_string_ref = StringPoolRef(); + ResetResourceResolution(); } } @@ -680,24 +775,24 @@ std::string AssetManager2::GetLastResourceResolution() const { return std::string(); } - auto cookie = last_resolution.cookie; + auto cookie = last_resolution_.cookie; if (cookie == kInvalidCookie) { LOG(ERROR) << "AssetManager hasn't resolved a resource to read resolution path."; return std::string(); } - uint32_t resid = last_resolution.resid; - std::vector& steps = last_resolution.steps; + uint32_t resid = last_resolution_.resid; + std::vector& steps = last_resolution_.steps; ResourceName resource_name; std::string resource_name_string; const LoadedPackage* package = - apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid)); + apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid)); if (package != nullptr) { - ToResourceName(last_resolution.type_string_ref, - last_resolution.entry_string_ref, + ToResourceName(last_resolution_.type_string_ref, + last_resolution_.entry_string_ref, package->GetPackageName(), &resource_name); resource_name_string = ToFormattedResourceString(&resource_name); @@ -762,25 +857,9 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) cons return false; } - const uint8_t package_idx = package_ids_[get_package_id(resid)]; - if (package_idx == 0xff) { - LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.", - get_package_id(resid), resid); - return false; - } - - const PackageGroup& package_group = package_groups_[package_idx]; - auto cookie_iter = std::find(package_group.cookies_.begin(), - package_group.cookies_.end(), cookie); - if (cookie_iter == package_group.cookies_.end()) { - return false; - } - - long package_pos = std::distance(package_group.cookies_.begin(), cookie_iter); - const LoadedPackage* package = package_group.packages_[package_pos].loaded_package_; return ToResourceName(entry.type_string_ref, entry.entry_string_ref, - package->GetPackageName(), + *entry.package_name, out_name); } @@ -807,7 +886,8 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, return kInvalidCookie; } - if (dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) { + const ResTable_entry* table_entry = *entry.entry; + if (dtohs(table_entry->flags) & ResTable_entry::FLAG_COMPLEX) { if (!may_be_bag) { LOG(ERROR) << base::StringPrintf("Resource %08x is a complex map type.", resid); return kInvalidCookie; @@ -822,7 +902,7 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, } const Res_value* device_value = reinterpret_cast( - reinterpret_cast(entry.entry) + dtohs(entry.entry->size)); + reinterpret_cast(table_entry) + dtohs(table_entry->size)); out_value->copyFrom_dtoh(*device_value); // Convert the package ID to the runtime assigned package ID. @@ -903,13 +983,14 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector& // Check that the size of the entry header is at least as big as // the desired ResTable_map_entry. Also verify that the entry // was intended to be a map. - if (dtohs(entry.entry->size) < sizeof(ResTable_map_entry) || - (dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) == 0) { + const ResTable_entry* table_entry = *entry.entry; + if (dtohs(table_entry->size) < sizeof(ResTable_map_entry) || + (dtohs(table_entry->flags) & ResTable_entry::FLAG_COMPLEX) == 0) { // Not a bag, nothing to do. return nullptr; } - const ResTable_map_entry* map = reinterpret_cast(entry.entry); + const ResTable_map_entry* map = reinterpret_cast(table_entry); const ResTable_map* map_entry = reinterpret_cast(reinterpret_cast(map) + map->size); const ResTable_map* const map_entry_end = map_entry + dtohl(map->count); @@ -1134,7 +1215,7 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name, } if (resid != 0u) { - return fix_package_id(resid, package_group.dynamic_ref_table.mAssignedPackageId); + return fix_package_id(resid, package_group.dynamic_ref_table->mAssignedPackageId); } } } @@ -1191,7 +1272,7 @@ uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) const for (auto& package_group : package_groups_) { for (auto& package2 : package_group.packages_) { if (package2.loaded_package_ == package) { - return package_group.dynamic_ref_table.mAssignedPackageId; + return package_group.dynamic_ref_table->mAssignedPackageId; } } } diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp index 7c1ee5cd7cfa..2b69c923597f 100644 --- a/libs/androidfw/Idmap.cpp +++ b/libs/androidfw/Idmap.cpp @@ -20,6 +20,8 @@ #include "android-base/logging.h" #include "android-base/stringprintf.h" +#include "androidfw/ResourceTypes.h" +#include "androidfw/Util.h" #include "utils/ByteOrder.h" #include "utils/Trace.h" @@ -29,40 +31,124 @@ #endif #endif -#include "androidfw/ResourceTypes.h" - using ::android::base::StringPrintf; namespace android { -constexpr static inline bool is_valid_package_id(uint16_t id) { - return id != 0 && id <= 255; +static bool compare_target_entries(const Idmap_target_entry &e1, const uint32_t target_id) { + return dtohl(e1.target_id) < target_id; } -constexpr static inline bool is_valid_type_id(uint16_t id) { - // Type IDs and package IDs have the same constraints in the IDMAP. - return is_valid_package_id(id); +static bool compare_overlay_entries(const Idmap_overlay_entry& e1, const uint32_t overlay_id) { + return dtohl(e1.overlay_id) < overlay_id; } -bool LoadedIdmap::Lookup(const IdmapEntry_header* header, uint16_t input_entry_id, - uint16_t* output_entry_id) { - if (input_entry_id < dtohs(header->entry_id_offset)) { - // After applying the offset, the entry is not present. - return false; +OverlayStringPool::OverlayStringPool(const LoadedIdmap* loaded_idmap) + : data_header_(loaded_idmap->data_header_), + idmap_string_pool_(loaded_idmap->string_pool_.get()) { }; + +OverlayStringPool::~OverlayStringPool() { + uninit(); +} + +const char16_t* OverlayStringPool::stringAt(size_t idx, size_t* outLen) const { + const size_t offset = dtohl(data_header_->string_pool_index_offset); + if (idmap_string_pool_ != nullptr && idx >= size() && idx >= offset) { + return idmap_string_pool_->stringAt(idx - offset, outLen); } - input_entry_id -= dtohs(header->entry_id_offset); - if (input_entry_id >= dtohs(header->entry_count)) { - // The entry is not present. - return false; + return ResStringPool::stringAt(idx, outLen); +} + +const char* OverlayStringPool::string8At(size_t idx, size_t* outLen) const { + const size_t offset = dtohl(data_header_->string_pool_index_offset); + if (idmap_string_pool_ != nullptr && idx >= size() && idx >= offset) { + return idmap_string_pool_->string8At(idx - offset, outLen); } - uint32_t result = dtohl(header->entries[input_entry_id]); - if (result == 0xffffffffu) { - return false; + return ResStringPool::string8At(idx, outLen); +} + +OverlayDynamicRefTable::OverlayDynamicRefTable(const Idmap_data_header* data_header, + const Idmap_overlay_entry* entries, + uint8_t target_assigned_package_id) + : data_header_(data_header), + entries_(entries), + target_assigned_package_id_(target_assigned_package_id) { }; + +status_t OverlayDynamicRefTable::lookupResourceId(uint32_t* resId) const { + const Idmap_overlay_entry* first_entry = entries_; + const Idmap_overlay_entry* end_entry = entries_ + dtohl(data_header_->overlay_entry_count); + auto entry = std::lower_bound(first_entry, end_entry, *resId, compare_overlay_entries); + + if (entry == end_entry || dtohl(entry->overlay_id) != *resId) { + // A mapping for the target resource id could not be found. + return DynamicRefTable::lookupResourceId(resId); } - *output_entry_id = static_cast(result); - return true; + + *resId = (0x00FFFFFFU & dtohl(entry->target_id)) + | (((uint32_t) target_assigned_package_id_) << 24); + return NO_ERROR; +} + +status_t OverlayDynamicRefTable::lookupResourceIdNoRewrite(uint32_t* resId) const { + return DynamicRefTable::lookupResourceId(resId); +} + +IdmapResMap::IdmapResMap(const Idmap_data_header* data_header, + const Idmap_target_entry* entries, + uint8_t target_assigned_package_id, + const OverlayDynamicRefTable* overlay_ref_table) + : data_header_(data_header), + entries_(entries), + target_assigned_package_id_(target_assigned_package_id), + overlay_ref_table_(overlay_ref_table) { }; + +IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const { + if ((target_res_id >> 24) != target_assigned_package_id_) { + // The resource id must have the same package id as the target package. + return {}; + } + + // The resource ids encoded within the idmap are build-time resource ids. + target_res_id = (0x00FFFFFFU & target_res_id) + | (((uint32_t) data_header_->target_package_id) << 24); + + const Idmap_target_entry* first_entry = entries_; + const Idmap_target_entry* end_entry = entries_ + dtohl(data_header_->target_entry_count); + auto entry = std::lower_bound(first_entry, end_entry, target_res_id, compare_target_entries); + + if (entry == end_entry || dtohl(entry->target_id) != target_res_id) { + // A mapping for the target resource id could not be found. + return {}; + } + + // A reference should be treated as an alias of the resource. Instead of returning the table + // entry, return the alias resource id to look up. The alias resource might not reside within the + // overlay package, so the resource id must be fixed with the dynamic reference table of the + // overlay before returning. + if (entry->type == Res_value::TYPE_REFERENCE + || entry->type == Res_value::TYPE_DYNAMIC_REFERENCE) { + uint32_t overlay_resource_id = dtohl(entry->value); + + // Lookup the resource without rewriting the overlay resource id back to the target resource id + // being looked up. + overlay_ref_table_->lookupResourceIdNoRewrite(&overlay_resource_id); + return Result(overlay_resource_id); + } + + // Copy the type and value into the ResTable_entry structure needed by asset manager. + uint16_t malloc_size = sizeof(ResTable_entry) + sizeof(Res_value); + auto table_entry = reinterpret_cast(malloc(malloc_size)); + memset(table_entry, 0, malloc_size); + table_entry->size = htods(sizeof(ResTable_entry)); + + auto table_value = reinterpret_cast(reinterpret_cast(table_entry) + + sizeof(ResTable_entry)); + table_value->dataType = entry->type; + table_value->data = entry->value; + + return Result(ResTable_entry_handle::managed(table_entry)); } static bool is_word_aligned(const void* data) { @@ -95,24 +181,26 @@ static bool IsValidIdmapHeader(const StringPiece& data) { return false; } - if (!is_valid_package_id(dtohs(header->target_package_id))) { - LOG(ERROR) << StringPrintf("Target package ID in Idmap is invalid: 0x%02x", - dtohs(header->target_package_id)); - return false; - } - - if (dtohs(header->type_count) > 255) { - LOG(ERROR) << StringPrintf("Idmap has too many type mappings (was %d, max 255)", - (int)dtohs(header->type_count)); - return false; - } return true; } -LoadedIdmap::LoadedIdmap(const Idmap_header* header) : header_(header) { +LoadedIdmap::LoadedIdmap(const Idmap_header* header, + const Idmap_data_header* data_header, + const Idmap_target_entry* target_entries, + const Idmap_overlay_entry* overlay_entries, + ResStringPool* string_pool) : header_(header), + data_header_(data_header), + target_entries_(target_entries), + overlay_entries_(overlay_entries), + string_pool_(string_pool) { + size_t length = strnlen(reinterpret_cast(header_->overlay_path), arraysize(header_->overlay_path)); overlay_apk_path_.assign(reinterpret_cast(header_->overlay_path), length); + + length = strnlen(reinterpret_cast(header_->target_path), + arraysize(header_->target_path)); + target_apk_path_.assign(reinterpret_cast(header_->target_path), length); } std::unique_ptr LoadedIdmap::Load(const StringPiece& idmap_data) { @@ -121,70 +209,67 @@ std::unique_ptr LoadedIdmap::Load(const StringPiece& idmap_da return {}; } - const Idmap_header* header = reinterpret_cast(idmap_data.data()); - - // Can't use make_unique because LoadedImpl constructor is private. - std::unique_ptr loaded_idmap = std::unique_ptr(new LoadedIdmap(header)); - + auto header = reinterpret_cast(idmap_data.data()); const uint8_t* data_ptr = reinterpret_cast(idmap_data.data()) + sizeof(*header); size_t data_size = idmap_data.size() - sizeof(*header); - size_t type_maps_encountered = 0u; - while (data_size >= sizeof(IdmapEntry_header)) { - if (!is_word_aligned(data_ptr)) { - LOG(ERROR) << "Type mapping in Idmap is not word aligned"; - return {}; - } - - // Validate the type IDs. - const IdmapEntry_header* entry_header = reinterpret_cast(data_ptr); - if (!is_valid_type_id(dtohs(entry_header->target_type_id)) || !is_valid_type_id(dtohs(entry_header->overlay_type_id))) { - LOG(ERROR) << StringPrintf("Invalid type map (0x%02x -> 0x%02x)", - dtohs(entry_header->target_type_id), - dtohs(entry_header->overlay_type_id)); - return {}; - } + // Currently idmap2 can only generate one data block. + auto data_header = reinterpret_cast(data_ptr); + data_ptr += sizeof(*data_header); + data_size -= sizeof(*data_header); + + // Make sure there is enough space for the target entries declared in the header. + const auto target_entries = reinterpret_cast(data_ptr); + if (data_size / sizeof(Idmap_target_entry) < + static_cast(dtohl(data_header->target_entry_count))) { + LOG(ERROR) << StringPrintf("Idmap too small for the number of target entries (%d)", + (int)dtohl(data_header->target_entry_count)); + return {}; + } - // Make sure there is enough space for the entries declared in the header. - if ((data_size - sizeof(*entry_header)) / sizeof(uint32_t) < - static_cast(dtohs(entry_header->entry_count))) { - LOG(ERROR) << StringPrintf("Idmap too small for the number of entries (%d)", - (int)dtohs(entry_header->entry_count)); - return {}; - } + // Advance the data pointer past the target entries. + const size_t target_entry_size_bytes = + (dtohl(data_header->target_entry_count) * sizeof(Idmap_target_entry)); + data_ptr += target_entry_size_bytes; + data_size -= target_entry_size_bytes; + + // Make sure there is enough space for the overlay entries declared in the header. + const auto overlay_entries = reinterpret_cast(data_ptr); + if (data_size / sizeof(Idmap_overlay_entry) < + static_cast(dtohl(data_header->overlay_entry_count))) { + LOG(ERROR) << StringPrintf("Idmap too small for the number of overlay entries (%d)", + (int)dtohl(data_header->overlay_entry_count)); + return {}; + } - // Only add a non-empty overlay. - if (dtohs(entry_header->entry_count != 0)) { - loaded_idmap->type_map_[static_cast(dtohs(entry_header->overlay_type_id))] = - entry_header; - } + // Advance the data pointer past the target entries. + const size_t overlay_entry_size_bytes = + (dtohl(data_header->overlay_entry_count) * sizeof(Idmap_overlay_entry)); + data_ptr += overlay_entry_size_bytes; + data_size -= overlay_entry_size_bytes; - const size_t entry_size_bytes = - sizeof(*entry_header) + (dtohs(entry_header->entry_count) * sizeof(uint32_t)); - data_ptr += entry_size_bytes; - data_size -= entry_size_bytes; - type_maps_encountered++; + // Read the idmap string pool that holds the value of inline string entries. + if (data_size < dtohl(data_header->string_pool_length)) { + LOG(ERROR) << StringPrintf("Idmap too small for string pool (length %d)", + (int)dtohl(data_header->string_pool_length)); + return {}; } - // Verify that we parsed all the type maps. - if (type_maps_encountered != static_cast(dtohs(header->type_count))) { - LOG(ERROR) << "Parsed " << type_maps_encountered << " type maps but expected " - << (int)dtohs(header->type_count); - return {}; + auto idmap_string_pool = util::make_unique(); + if (dtohl(data_header->string_pool_length) > 0) { + status_t err = idmap_string_pool->setTo(data_ptr, dtohl(data_header->string_pool_length)); + if (err != NO_ERROR) { + LOG(ERROR) << "idmap string pool corrupt."; + return {}; + } } - return std::move(loaded_idmap); -} -uint8_t LoadedIdmap::TargetPackageId() const { - return static_cast(dtohs(header_->target_package_id)); -} + // Can't use make_unique because LoadedImpl constructor is private. + std::unique_ptr loaded_idmap = std::unique_ptr( + new LoadedIdmap(header, data_header, target_entries, overlay_entries, + idmap_string_pool.release())); -const IdmapEntry_header* LoadedIdmap::GetEntryMapForType(uint8_t type_id) const { - auto iter = type_map_.find(type_id); - if (iter != type_map_.end()) { - return iter->second; - } - return nullptr; + return std::move(loaded_idmap); } } // namespace android diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index 882dc0d71759..c8962416d082 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -51,9 +51,8 @@ namespace { // the Type structs. class TypeSpecPtrBuilder { public: - explicit TypeSpecPtrBuilder(const ResTable_typeSpec* header, - const IdmapEntry_header* idmap_header) - : header_(header), idmap_header_(idmap_header) { + explicit TypeSpecPtrBuilder(const ResTable_typeSpec* header) + : header_(header) { } void AddType(const ResTable_type* type) { @@ -70,7 +69,6 @@ class TypeSpecPtrBuilder { TypeSpec* type_spec = (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(ElementType))); type_spec->type_spec = header_; - type_spec->idmap_entries = idmap_header_; type_spec->type_count = types_.size(); memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(ElementType)); return TypeSpecPtr(type_spec); @@ -80,7 +78,6 @@ class TypeSpecPtrBuilder { DISALLOW_COPY_AND_ASSIGN(TypeSpecPtrBuilder); const ResTable_typeSpec* header_; - const IdmapEntry_header* idmap_header_; std::vector types_; }; @@ -400,7 +397,6 @@ const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const { } std::unique_ptr LoadedPackage::Load(const Chunk& chunk, - const LoadedIdmap* loaded_idmap, bool system, bool load_as_shared_library, bool for_loader) { @@ -426,12 +422,6 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, loaded_package->dynamic_ = true; } - if (loaded_idmap != nullptr) { - // This is an overlay and so it needs to pretend to be the target package. - loaded_package->package_id_ = loaded_idmap->TargetPackageId(); - loaded_package->overlay_ = true; - } - if (for_loader) { loaded_package->custom_loader_ = true; } @@ -517,16 +507,9 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, return {}; } - // If this is an overlay, associate the mapping of this type to the target type - // from the IDMAP. - const IdmapEntry_header* idmap_entry_header = nullptr; - if (loaded_idmap != nullptr) { - idmap_entry_header = loaded_idmap->GetEntryMapForType(type_spec->id); - } - std::unique_ptr& builder_ptr = type_builder_map[type_spec->id - 1]; if (builder_ptr == nullptr) { - builder_ptr = util::make_unique(type_spec, idmap_entry_header); + builder_ptr = util::make_unique(type_spec); loaded_package->resource_ids_.set(type_spec->id, entry_count); } else { LOG(WARNING) << StringPrintf("RES_TABLE_TYPE_SPEC_TYPE already defined for ID %02x", @@ -687,15 +670,7 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, return {}; } - // We only add the type to the package if there is no IDMAP, or if the type is - // overlaying something. - if (loaded_idmap == nullptr || type_spec_ptr->idmap_entries != nullptr) { - // If this is an overlay, insert it at the target type ID. - if (type_spec_ptr->idmap_entries != nullptr) { - type_idx = dtohs(type_spec_ptr->idmap_entries->target_type_id) - 1; - } - loaded_package->type_specs_.editItemAt(type_idx) = std::move(type_spec_ptr); - } + loaded_package->type_specs_.editItemAt(type_idx) = std::move(type_spec_ptr); } return std::move(loaded_package); @@ -709,6 +684,10 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, return false; } + if (loaded_idmap != nullptr) { + global_string_pool_ = util::make_unique(loaded_idmap); + } + const size_t package_count = dtohl(header->packageCount); size_t packages_seen = 0; @@ -720,9 +699,9 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, switch (child_chunk.type()) { case RES_STRING_POOL_TYPE: // Only use the first string pool. Ignore others. - if (global_string_pool_.getError() == NO_INIT) { - status_t err = global_string_pool_.setTo(child_chunk.header(), - child_chunk.size()); + if (global_string_pool_->getError() == NO_INIT) { + status_t err = global_string_pool_->setTo(child_chunk.header(), + child_chunk.size()); if (err != NO_ERROR) { LOG(ERROR) << "RES_STRING_POOL_TYPE corrupt."; return false; @@ -741,11 +720,7 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, packages_seen++; std::unique_ptr loaded_package = - LoadedPackage::Load(child_chunk, - loaded_idmap, - system_, - load_as_shared_library, - for_loader); + LoadedPackage::Load(child_chunk, system_, load_as_shared_library, for_loader); if (!loaded_package) { return false; } diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 3fe2c5b0e21a..4d7e5dfea4f7 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -1363,11 +1363,10 @@ int32_t ResXMLParser::getAttributeData(size_t idx) const (((const uint8_t*)tag) + dtohs(tag->attributeStart) + (dtohs(tag->attributeSize)*idx)); - if (attr->typedValue.dataType != Res_value::TYPE_DYNAMIC_REFERENCE || - mTree.mDynamicRefTable == NULL) { + if (mTree.mDynamicRefTable == NULL || + !mTree.mDynamicRefTable->requiresLookup(&attr->typedValue)) { return dtohl(attr->typedValue.data); } - uint32_t data = dtohl(attr->typedValue.data); if (mTree.mDynamicRefTable->lookupResourceId(&data) == NO_ERROR) { return data; @@ -1613,10 +1612,9 @@ uint32_t ResXMLParser::getSourceResourceId() const static volatile int32_t gCount = 0; -ResXMLTree::ResXMLTree(const DynamicRefTable* dynamicRefTable) +ResXMLTree::ResXMLTree(std::shared_ptr dynamicRefTable) : ResXMLParser(*this) - , mDynamicRefTable((dynamicRefTable != nullptr) ? dynamicRefTable->clone() - : std::unique_ptr(nullptr)) + , mDynamicRefTable(std::move(dynamicRefTable)) , mError(NO_INIT), mOwnedData(NULL) { if (kDebugResXMLTree) { @@ -1627,7 +1625,7 @@ ResXMLTree::ResXMLTree(const DynamicRefTable* dynamicRefTable) ResXMLTree::ResXMLTree() : ResXMLParser(*this) - , mDynamicRefTable(std::unique_ptr(nullptr)) + , mDynamicRefTable(nullptr) , mError(NO_INIT), mOwnedData(NULL) { if (kDebugResXMLTree) { @@ -4789,7 +4787,7 @@ void ResTable::setParameters(const ResTable_config* params) packageGroup->clearBagCache(); // Find which configurations match the set of parameters. This allows for a much - // faster lookup in getEntry() if the set of values is narrowed down. + // faster lookup in Lookup() if the set of values is narrowed down. for (size_t t = 0; t < packageGroup->types.size(); t++) { if (packageGroup->types[t].isEmpty()) { continue; @@ -6897,13 +6895,6 @@ DynamicRefTable::DynamicRefTable(uint8_t packageId, bool appAsLib) mLookupTable[SYS_PACKAGE_ID] = SYS_PACKAGE_ID; } -std::unique_ptr DynamicRefTable::clone() const { - std::unique_ptr clone = std::unique_ptr( - new DynamicRefTable(mAssignedPackageId, mAppAsLib)); - clone->addMappings(*this); - return clone; -} - status_t DynamicRefTable::load(const ResTable_lib_header* const header) { const uint32_t entryCount = dtohl(header->count); @@ -7020,21 +7011,29 @@ status_t DynamicRefTable::lookupResourceId(uint32_t* resId) const { return NO_ERROR; } +bool DynamicRefTable::requiresLookup(const Res_value* value) const { + // Only resolve non-dynamic references and attributes if the package is loaded as a + // library or if a shared library is attempting to retrieve its own resource + if ((value->dataType == Res_value::TYPE_REFERENCE || + value->dataType == Res_value::TYPE_ATTRIBUTE) && + (mAppAsLib || (Res_GETPACKAGE(value->data) + 1) == 0)) { + return true; + } + return value->dataType == Res_value::TYPE_DYNAMIC_ATTRIBUTE || + value->dataType == Res_value::TYPE_DYNAMIC_REFERENCE; +} + status_t DynamicRefTable::lookupResourceValue(Res_value* value) const { + if (!requiresLookup(value)) { + return NO_ERROR; + } + uint8_t resolvedType = Res_value::TYPE_REFERENCE; switch (value->dataType) { case Res_value::TYPE_ATTRIBUTE: resolvedType = Res_value::TYPE_ATTRIBUTE; FALLTHROUGH_INTENDED; case Res_value::TYPE_REFERENCE: - // Only resolve non-dynamic references and attributes if the package is loaded as a - // library or if a shared library is attempting to retrieve its own resource - if (!(mAppAsLib || (Res_GETPACKAGE(value->data) + 1) == 0)) { - return NO_ERROR; - } - - // If the package is loaded as shared library, the resource reference - // also need to be fixed. break; case Res_value::TYPE_DYNAMIC_ATTRIBUTE: resolvedType = Res_value::TYPE_ATTRIBUTE; diff --git a/libs/androidfw/TEST_MAPPING b/libs/androidfw/TEST_MAPPING index a58b47fcff9d..d1a6a5c18299 100644 --- a/libs/androidfw/TEST_MAPPING +++ b/libs/androidfw/TEST_MAPPING @@ -3,6 +3,9 @@ { "name": "libandroidfw_tests", "host": true + }, + { + "name": "FrameworksResourceLoaderTests" } ] } \ No newline at end of file diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h index 625b68207d83..20472872263e 100644 --- a/libs/androidfw/include/androidfw/ApkAssets.h +++ b/libs/androidfw/include/androidfw/ApkAssets.h @@ -24,6 +24,7 @@ #include "android-base/unique_fd.h" #include "androidfw/Asset.h" +#include "androidfw/Idmap.h" #include "androidfw/LoadedArsc.h" #include "androidfw/misc.h" @@ -95,10 +96,18 @@ class ApkAssets { return loaded_arsc_.get(); } + inline const LoadedIdmap* GetLoadedIdmap() const { + return loaded_idmap_.get(); + } + inline bool IsOverlay() const { return idmap_asset_.get() != nullptr; } + inline bool IsLoader() const { + return for_loader_; + } + bool IsUpToDate() const; // Creates an Asset from any file on the file system. @@ -127,10 +136,11 @@ class ApkAssets { ZipArchivePtr zip_handle_; const std::string path_; time_t last_mod_time_; - bool for_loader; + bool for_loader_; std::unique_ptr resources_asset_; std::unique_ptr idmap_asset_; std::unique_ptr loaded_arsc_; + std::unique_ptr loaded_idmap_; }; } // namespace android diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index c7348b180648..20e40234b418 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -122,7 +122,7 @@ class AssetManager2 { // Returns the DynamicRefTable for the ApkAssets represented by the cookie. // This may be nullptr if the APK represented by `cookie` has no resource table. - const DynamicRefTable* GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const; + std::shared_ptr GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const; // Returns a string representation of the overlayable API of a package. bool GetOverlayablesToString(const android::StringPiece& package_name, @@ -236,12 +236,14 @@ class AssetManager2 { ResTable_config* in_out_selected_config, uint32_t* in_out_flags, uint32_t* out_last_reference) const; - // Enables or disables resource resolution logging. Clears stored steps when - // disabled. + // Resets the resource resolution structures in preparation for the next resource retrieval. + void ResetResourceResolution() const; + + // Enables or disables resource resolution logging. Clears stored steps when disabled. void SetResourceResolutionLoggingEnabled(bool enabled); - // Returns formatted log of last resource resolution path, or empty if no - // resource has been resolved yet. + // Returns formatted log of last resource resolution path, or empty if no resource has been + // resolved yet. std::string GetLastResourceResolution() const; const std::vector GetBagResIdStack(uint32_t resid); @@ -264,7 +266,7 @@ class AssetManager2 { void ForEachPackage(const std::function func) const { for (const PackageGroup& package_group : package_groups_) { if (!func(package_group.packages_.front().loaded_package_->GetPackageName(), - package_group.dynamic_ref_table.mAssignedPackageId)) { + package_group.dynamic_ref_table->mAssignedPackageId)) { return; } } @@ -275,6 +277,50 @@ class AssetManager2 { private: DISALLOW_COPY_AND_ASSIGN(AssetManager2); + // A collection of configurations and their associated ResTable_type that match the current + // AssetManager configuration. + struct FilteredConfigGroup { + std::vector configurations; + std::vector types; + }; + + // Represents an single package. + struct ConfiguredPackage { + // A pointer to the immutable, loaded package info. + const LoadedPackage* loaded_package_; + + // A mutable AssetManager-specific list of configurations that match the AssetManager's + // current configuration. This is used as an optimization to avoid checking every single + // candidate configuration when looking up resources. + ByteBucketArray filtered_configs_; + }; + + // Represents a Runtime Resource Overlay that overlays resources in the logical package. + struct ConfiguredOverlay { + // The set of package groups that overlay this package group. + IdmapResMap overlay_res_maps_; + + // The cookie of the overlay assets. + ApkAssetsCookie cookie; + }; + + // Represents a logical package, which can be made up of many individual packages. Each package + // in a PackageGroup shares the same package name and package ID. + struct PackageGroup { + // The set of packages that make-up this group. + std::vector packages_; + + // The cookies associated with each package in the group. They share the same order as + // packages_. + std::vector cookies_; + + // Runtime Resource Overlays that overlay resources in this package group. + std::vector overlays_; + + // A library reference table that contains build-package ID to runtime-package ID mappings. + std::shared_ptr dynamic_ref_table = std::make_shared(); + }; + // Finds the best entry for `resid` from the set of ApkAssets. The entry can be a simple // Res_value, or a complex map/bag type. If successful, it is available in `out_entry`. // Returns kInvalidCookie on failure. Otherwise, the return value is the cookie associated with @@ -295,6 +341,11 @@ class AssetManager2 { ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match, bool ignore_configuration, FindEntryResult* out_entry) const; + ApkAssetsCookie FindEntryInternal(const PackageGroup& package_group, uint8_t type_idx, + uint16_t entry_idx, const ResTable_config& desired_config, + bool /*stop_at_first_match*/, + bool ignore_configuration, FindEntryResult* out_entry) const; + // Assigns package IDs to all shared library ApkAssets. // Should be called whenever the ApkAssets are changed. void BuildDynamicRefTable(); @@ -307,6 +358,9 @@ class AssetManager2 { // This should always be called when mutating the AssetManager's configuration or ApkAssets set. void RebuildFilterList(bool filter_incompatible_configs = true); + // Retrieves the APK paths of overlays that overlay non-system packages. + std::set GetNonSystemOverlayPaths() const; + // AssetManager2::GetBag(resid) wraps this function to track which resource ids have already // been seen while traversing bag parents. const ResolvedBag* GetBag(uint32_t resid, std::vector& child_resids); @@ -318,38 +372,6 @@ class AssetManager2 { // have a longer lifetime. std::vector apk_assets_; - // A collection of configurations and their associated ResTable_type that match the current - // AssetManager configuration. - struct FilteredConfigGroup { - std::vector configurations; - std::vector types; - }; - - // Represents an single package. - struct ConfiguredPackage { - // A pointer to the immutable, loaded package info. - const LoadedPackage* loaded_package_; - - // A mutable AssetManager-specific list of configurations that match the AssetManager's - // current configuration. This is used as an optimization to avoid checking every single - // candidate configuration when looking up resources. - ByteBucketArray filtered_configs_; - }; - - // Represents a logical package, which can be made up of many individual packages. Each package - // in a PackageGroup shares the same package name and package ID. - struct PackageGroup { - // The set of packages that make-up this group. - std::vector packages_; - - // The cookies associated with each package in the group. They share the same order as - // packages_. - std::vector cookies_; - - // A library reference table that contains build-package ID to runtime-package ID mappings. - DynamicRefTable dynamic_ref_table; - }; - // DynamicRefTables for shared library package resolution. // These are ordered according to apk_assets_. The mappings may change depending on what is // in apk_assets_, therefore they must be stored in the AssetManager and not in the @@ -418,7 +440,7 @@ class AssetManager2 { }; // Record of the last resolved resource's resolution path. - mutable Resolution last_resolution; + mutable Resolution last_resolution_; }; class Theme { diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h index fd02e6f63b74..ab4c9c204842 100644 --- a/libs/androidfw/include/androidfw/Idmap.h +++ b/libs/androidfw/include/androidfw/Idmap.h @@ -20,20 +20,122 @@ #include #include #include +#include #include "android-base/macros.h" - #include "androidfw/StringPiece.h" +#include "androidfw/ResourceTypes.h" +#include "utils/ByteOrder.h" namespace android { -struct Idmap_header; -struct IdmapEntry_header; +class LoadedIdmap; +class IdmapResMap; + +// A string pool for overlay apk assets. The string pool holds the strings of the overlay resources +// table and additionally allows for loading strings from the idmap string pool. The idmap string +// pool strings are offset after the end of the overlay resource table string pool entries so +// queries for strings defined inline in the idmap do not conflict with queries for overlay +// resource table strings. +class OverlayStringPool : public ResStringPool { + public: + virtual ~OverlayStringPool(); + virtual const char16_t* stringAt(size_t idx, size_t* outLen) const; + virtual const char* string8At(size_t idx, size_t* outLen) const; + + explicit OverlayStringPool(const LoadedIdmap* loaded_idmap); + private: + const Idmap_data_header* data_header_; + const ResStringPool* idmap_string_pool_; +}; + +// A dynamic reference table for loaded overlay packages that rewrites the resource id of overlay +// resources to the resource id of corresponding target resources. +class OverlayDynamicRefTable : public DynamicRefTable { + public: + virtual ~OverlayDynamicRefTable() = default; + virtual status_t lookupResourceId(uint32_t* resId) const; + + private: + explicit OverlayDynamicRefTable(const Idmap_data_header* data_header, + const Idmap_overlay_entry* entries, + uint8_t target_assigned_package_id); + + // Rewrites a compile-time overlay resource id to the runtime resource id of corresponding target + // resource. + virtual status_t lookupResourceIdNoRewrite(uint32_t* resId) const; + + const Idmap_data_header* data_header_; + const Idmap_overlay_entry* entries_; + const int8_t target_assigned_package_id_; + + friend LoadedIdmap; + friend IdmapResMap; +}; + +// A mapping of target resource ids to a values or resource ids that should overlay the target. +class IdmapResMap { + public: + // Represents the result of a idmap lookup. The result can be one of three possibillities: + // 1) The result is a resource id which represents the overlay resource that should act as an + // alias of the target resource. + // 2) The result is a table entry which overlays the type and value of the target resource. + // 3) The result is neither and the target resource is not overlaid. + class Result { + public: + Result() : data_(nullptr) {}; + explicit Result(uint32_t value) : data_(value) {}; + explicit Result(ResTable_entry_handle&& value) : data_(value) { }; + + // Returns `true` if the resource is overlaid. + inline explicit operator bool() const { + return !std::get_if(&data_); + } + + inline bool IsResourceId() const { + return std::get_if(&data_); + } + + inline uint32_t GetResourceId() const { + return *std::get_if(&data_); + } + + inline bool IsTableEntry() const { + return std::get_if(&data_); + } + + inline const ResTable_entry_handle& GetTableEntry() const { + return *std::get_if(&data_); + } + + private: + std::variant data_; + }; + + // Looks up the value that overlays the target resource id. + Result Lookup(uint32_t target_res_id) const; + + inline const OverlayDynamicRefTable* GetOverlayDynamicRefTable() const { + return overlay_ref_table_; + } + + private: + explicit IdmapResMap(const Idmap_data_header* data_header, + const Idmap_target_entry* entries, + uint8_t target_assigned_package_id, + const OverlayDynamicRefTable* overlay_ref_table); + + const Idmap_data_header* data_header_; + const Idmap_target_entry* entries_; + const uint8_t target_assigned_package_id_; + const OverlayDynamicRefTable* overlay_ref_table_; + + friend LoadedIdmap; +}; // Represents a loaded/parsed IDMAP for a Runtime Resource Overlay (RRO). -// An RRO and its target APK have different resource IDs assigned to their resources. Overlaying -// a resource is done by resource name. An IDMAP is a generated mapping between the resource IDs -// of the RRO and the target APK for each resource with the same name. +// An RRO and its target APK have different resource IDs assigned to their resources. +// An IDMAP is a generated mapping between the resource IDs of the RRO and the target APK. // A LoadedIdmap can be set alongside the overlay's LoadedArsc to allow the overlay ApkAssets to // masquerade as the target ApkAssets resources. class LoadedIdmap { @@ -41,34 +143,52 @@ class LoadedIdmap { // Loads an IDMAP from a chunk of memory. Returns nullptr if the IDMAP data was malformed. static std::unique_ptr Load(const StringPiece& idmap_data); - // Performs a lookup of the expected entry ID for the given IDMAP entry header. - // Returns true if the mapping exists and fills `output_entry_id` with the result. - static bool Lookup(const IdmapEntry_header* header, uint16_t input_entry_id, - uint16_t* output_entry_id); - - // Returns the package ID for which this overlay should apply. - uint8_t TargetPackageId() const; - // Returns the path to the RRO (Runtime Resource Overlay) APK for which this IDMAP was generated. inline const std::string& OverlayApkPath() const { return overlay_apk_path_; } - // Returns the mapping of target entry ID to overlay entry ID for the given target type. - const IdmapEntry_header* GetEntryMapForType(uint8_t type_id) const; + // Returns the path to the RRO (Runtime Resource Overlay) APK for which this IDMAP was generated. + inline const std::string& TargetApkPath() const { + return target_apk_path_; + } + + // Returns a mapping from target resource ids to overlay values. + inline const IdmapResMap GetTargetResourcesMap( + uint8_t target_assigned_package_id, const OverlayDynamicRefTable* overlay_ref_table) const { + return IdmapResMap(data_header_, target_entries_, target_assigned_package_id, + overlay_ref_table); + } + + // Returns a dynamic reference table for a loaded overlay package. + inline const OverlayDynamicRefTable GetOverlayDynamicRefTable( + uint8_t target_assigned_package_id) const { + return OverlayDynamicRefTable(data_header_, overlay_entries_, target_assigned_package_id); + } protected: // Exposed as protected so that tests can subclass and mock this class out. LoadedIdmap() = default; - const Idmap_header* header_ = nullptr; + const Idmap_header* header_; + const Idmap_data_header* data_header_; + const Idmap_target_entry* target_entries_; + const Idmap_overlay_entry* overlay_entries_; + const std::unique_ptr string_pool_; + std::string overlay_apk_path_; - std::unordered_map type_map_; + std::string target_apk_path_; private: DISALLOW_COPY_AND_ASSIGN(LoadedIdmap); - explicit LoadedIdmap(const Idmap_header* header); + explicit LoadedIdmap(const Idmap_header* header, + const Idmap_data_header* data_header, + const Idmap_target_entry* target_entries, + const Idmap_overlay_entry* overlay_entries, + ResStringPool* string_pool); + + friend OverlayStringPool; }; } // namespace android diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 1a56876b9686..ba1beaa7827c 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -51,10 +51,6 @@ struct TypeSpec { // and under which configurations it varies. const ResTable_typeSpec* type_spec; - // Pointer to the mmapped data where the IDMAP mappings for this type - // exist. May be nullptr if no IDMAP exists. - const IdmapEntry_header* idmap_entries; - // The number of types that follow this struct. // There is a type for each configuration that entries are defined for. size_t type_count; @@ -135,8 +131,7 @@ class LoadedPackage { return iterator(this, resource_ids_.size() + 1, 0); } - static std::unique_ptr Load(const Chunk& chunk, - const LoadedIdmap* loaded_idmap, bool system, + static std::unique_ptr Load(const Chunk& chunk, bool system, bool load_as_shared_library, bool load_as_custom_loader); @@ -183,11 +178,6 @@ class LoadedPackage { return system_; } - // Returns true if this package is from an overlay ApkAssets. - inline bool IsOverlay() const { - return overlay_; - } - // Returns true if this package is a custom loader and should behave like an overlay inline bool IsCustomLoader() const { return custom_loader_; @@ -222,9 +212,6 @@ class LoadedPackage { const TypeSpecPtr& ptr = type_specs_[i]; if (ptr != nullptr) { uint8_t type_id = ptr->type_spec->id; - if (ptr->idmap_entries != nullptr) { - type_id = ptr->idmap_entries->target_type_id; - } f(ptr.get(), type_id - 1); } } @@ -265,7 +252,6 @@ class LoadedPackage { int type_id_offset_ = 0; bool dynamic_ = false; bool system_ = false; - bool overlay_ = false; bool custom_loader_ = false; bool defines_overlayable_ = false; @@ -298,7 +284,7 @@ class LoadedArsc { // Returns the string pool where all string resource values // (Res_value::dataType == Res_value::TYPE_STRING) are indexed. inline const ResStringPool* GetStringPool() const { - return &global_string_pool_; + return global_string_pool_.get(); } // Gets a pointer to the package with the specified package ID, or nullptr if no such package @@ -319,12 +305,8 @@ class LoadedArsc { DISALLOW_COPY_AND_ASSIGN(LoadedArsc); LoadedArsc() = default; - bool LoadTable( - const Chunk& chunk, - const LoadedIdmap* loaded_idmap, - bool load_as_shared_library, - bool for_loader - ); + bool LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, bool load_as_shared_library, + bool for_loader); static std::unique_ptr LoadData(std::unique_ptr& loaded_arsc, const char* data, @@ -333,7 +315,7 @@ class LoadedArsc { bool load_as_shared_library = false, bool for_loader = false); - ResStringPool global_string_pool_; + std::unique_ptr global_string_pool_ = util::make_unique(); std::vector> packages_; bool system_ = false; }; diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index 2efa65a009e1..b20e6579fda7 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -492,7 +492,7 @@ class ResStringPool public: ResStringPool(); ResStringPool(const void* data, size_t size, bool copyData=false); - ~ResStringPool(); + virtual ~ResStringPool(); void setToEmpty(); status_t setTo(const void* data, size_t size, bool copyData=false); @@ -506,10 +506,10 @@ public: inline const char16_t* stringAt(const ResStringPool_ref& ref, size_t* outLen) const { return stringAt(ref.index, outLen); } - const char16_t* stringAt(size_t idx, size_t* outLen) const; + virtual const char16_t* stringAt(size_t idx, size_t* outLen) const; // Note: returns null if the string pool is not UTF8. - const char* string8At(size_t idx, size_t* outLen) const; + virtual const char* string8At(size_t idx, size_t* outLen) const; // Return string whether the pool is UTF8 or UTF16. Does not allow you // to distinguish null. @@ -812,7 +812,7 @@ public: * The tree stores a clone of the specified DynamicRefTable, so any changes to the original * DynamicRefTable will not affect this tree after instantiation. **/ - explicit ResXMLTree(const DynamicRefTable* dynamicRefTable); + explicit ResXMLTree(std::shared_ptr dynamicRefTable); ResXMLTree(); ~ResXMLTree(); @@ -827,7 +827,7 @@ private: status_t validateNode(const ResXMLTree_node* node) const; - std::unique_ptr mDynamicRefTable; + std::shared_ptr mDynamicRefTable; status_t mError; void* mOwnedData; @@ -1584,6 +1584,50 @@ struct ResTable_map Res_value value; }; + +// A ResTable_entry variant that either holds an unmanaged pointer to a constant ResTable_entry or +// holds a ResTable_entry which is tied to the lifetime of the handle. +class ResTable_entry_handle { + public: + ResTable_entry_handle() = default; + + ResTable_entry_handle(const ResTable_entry_handle& handle) { + entry_ = handle.entry_; + } + + ResTable_entry_handle(ResTable_entry_handle&& handle) noexcept { + entry_ = handle.entry_; + } + + inline static ResTable_entry_handle managed(ResTable_entry* entry) { + return ResTable_entry_handle(std::shared_ptr(entry)); + } + + inline static ResTable_entry_handle unmanaged(const ResTable_entry* entry) { + return ResTable_entry_handle(std::shared_ptr(entry, [](auto /*p */){})); + } + + inline ResTable_entry_handle& operator=(const ResTable_entry_handle& handle) noexcept { + entry_ = handle.entry_; + return *this; + } + + inline ResTable_entry_handle& operator=(ResTable_entry_handle&& handle) noexcept { + entry_ = handle.entry_; + return *this; + } + + inline const ResTable_entry* operator*() & { + return entry_.get(); + } + + private: + explicit ResTable_entry_handle(std::shared_ptr entry) + : entry_(std::move(entry)) { } + + std::shared_ptr entry_; +}; + /** * A package-id to package name mapping for any shared libraries used * in this resource table. The package-id's encoded in this resource @@ -1668,7 +1712,8 @@ struct ResTable_overlayable_policy_header uint32_t entry_count; }; -struct alignas(uint32_t) Idmap_header { +#pragma pack(push, 1) +struct Idmap_header { // Always 0x504D4449 ('IDMP') uint32_t magic; @@ -1679,18 +1724,28 @@ struct alignas(uint32_t) Idmap_header { uint8_t target_path[256]; uint8_t overlay_path[256]; +}; - uint16_t target_package_id; - uint16_t type_count; -} __attribute__((packed)); +struct Idmap_data_header { + uint8_t target_package_id; + uint8_t overlay_package_id; + uint32_t target_entry_count; + uint32_t overlay_entry_count; + uint32_t string_pool_index_offset; + uint32_t string_pool_length; +}; -struct alignas(uint32_t) IdmapEntry_header { - uint16_t target_type_id; - uint16_t overlay_type_id; - uint16_t entry_count; - uint16_t entry_id_offset; - uint32_t entries[0]; -} __attribute__((packed)); +struct Idmap_target_entry { + uint32_t target_id; + uint8_t type; + uint32_t value; +}; + +struct Idmap_overlay_entry { + uint32_t overlay_id; + uint32_t target_id; +}; +#pragma pack(pop) class AssetManager2; @@ -1708,6 +1763,7 @@ class DynamicRefTable public: DynamicRefTable(); DynamicRefTable(uint8_t packageId, bool appAsLib); + virtual ~DynamicRefTable() = default; // Loads an unmapped reference table from the package. status_t load(const ResTable_lib_header* const header); @@ -1721,12 +1777,12 @@ public: void addMapping(uint8_t buildPackageId, uint8_t runtimePackageId); - // Creates a new clone of the reference table - std::unique_ptr clone() const; + // Returns whether or not the value must be looked up. + bool requiresLookup(const Res_value* value) const; // Performs the actual conversion of build-time resource ID to run-time // resource ID. - status_t lookupResourceId(uint32_t* resId) const; + virtual status_t lookupResourceId(uint32_t* resId) const; status_t lookupResourceValue(Res_value* value) const; inline const KeyedVector& entries() const { diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp index e2b9f0040989..0f2ee6fb968e 100644 --- a/libs/androidfw/tests/ApkAssets_test.cpp +++ b/libs/androidfw/tests/ApkAssets_test.cpp @@ -79,39 +79,6 @@ TEST(ApkAssetsTest, LoadApkAsSharedLibrary) { EXPECT_TRUE(loaded_arsc->GetPackages()[0]->IsDynamic()); } -TEST(ApkAssetsTest, LoadApkWithIdmap) { - std::string contents; - ResTable target_table; - const std::string target_path = GetTestDataPath() + "/basic/basic.apk"; - ASSERT_TRUE(ReadFileFromZipToString(target_path, "resources.arsc", &contents)); - ASSERT_THAT(target_table.add(contents.data(), contents.size(), 0, true /*copyData*/), - Eq(NO_ERROR)); - - ResTable overlay_table; - const std::string overlay_path = GetTestDataPath() + "/overlay/overlay.apk"; - ASSERT_TRUE(ReadFileFromZipToString(overlay_path, "resources.arsc", &contents)); - ASSERT_THAT(overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/), - Eq(NO_ERROR)); - - util::unique_cptr idmap_data; - void* temp_data; - size_t idmap_len; - - ASSERT_THAT(target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(), - overlay_path.c_str(), &temp_data, &idmap_len), - Eq(NO_ERROR)); - idmap_data.reset(temp_data); - - TemporaryFile tf; - ASSERT_TRUE(base::WriteFully(tf.fd, idmap_data.get(), idmap_len)); - close(tf.fd); - - // Open something so that the destructor of TemporaryFile closes a valid fd. - tf.fd = open("/dev/null", O_WRONLY); - - ASSERT_THAT(ApkAssets::LoadOverlay(tf.path), NotNull()); -} - TEST(ApkAssetsTest, CreateAndDestroyAssetKeepsApkAssetsOpen) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index 15910241518d..b3190be8caf1 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -718,15 +718,17 @@ TEST_F(AssetManager2Test, GetOverlayablesToString) { const auto map = assetmanager.GetOverlayableMapForPackage(0x7f); ASSERT_NE(nullptr, map); - ASSERT_EQ(2, map->size()); + ASSERT_EQ(3, map->size()); ASSERT_EQ(map->at("OverlayableResources1"), "overlay://theme"); ASSERT_EQ(map->at("OverlayableResources2"), "overlay://com.android.overlayable"); + ASSERT_EQ(map->at("OverlayableResources3"), ""); std::string api; ASSERT_TRUE(assetmanager.GetOverlayablesToString("com.android.overlayable", &api)); ASSERT_EQ(api.find("not_overlayable"), std::string::npos); ASSERT_NE(api.find("resource='com.android.overlayable:string/overlayable2' overlayable='OverlayableResources1' actor='overlay://theme' policy='0x0000000a'\n"), std::string::npos); + } } // namespace android diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp index 10b83a75304d..b679672ab34e 100644 --- a/libs/androidfw/tests/Idmap_test.cpp +++ b/libs/androidfw/tests/Idmap_test.cpp @@ -14,114 +14,231 @@ * limitations under the License. */ +#include "android-base/file.h" +#include "androidfw/ApkAssets.h" +#include "androidfw/AssetManager2.h" #include "androidfw/ResourceTypes.h" #include "utils/String16.h" #include "utils/String8.h" #include "TestHelpers.h" -#include "data/basic/R.h" +#include "data/overlay/R.h" +#include "data/overlayable/R.h" +#include "data/system/R.h" -using ::com::android::basic::R; +namespace overlay = com::android::overlay; +namespace overlayable = com::android::overlayable; namespace android { +namespace { + class IdmapTest : public ::testing::Test { protected: void SetUp() override { - std::string contents; - ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", - &contents)); - ASSERT_EQ(NO_ERROR, target_table_.add(contents.data(), contents.size(), 0, true)); - - ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlay/overlay.apk", - "resources.arsc", &overlay_data_)); - ResTable overlay_table; - ASSERT_EQ(NO_ERROR, overlay_table.add(overlay_data_.data(), overlay_data_.size())); - - char target_name[256] = "com.android.basic"; - ASSERT_EQ(NO_ERROR, overlay_table.createIdmap(target_table_, 0, 0, target_name, target_name, - &data_, &data_size_)); - } + // Move to the test data directory so the idmap can locate the overlay APK. + std::string original_path = base::GetExecutableDirectory(); + chdir(GetTestDataPath().c_str()); + + system_assets_ = ApkAssets::Load("system/system.apk"); + ASSERT_NE(nullptr, system_assets_); - void TearDown() override { - ::free(data_); + overlay_assets_ = ApkAssets::LoadOverlay("overlay/overlay.idmap"); + ASSERT_NE(nullptr, overlay_assets_); + + overlayable_assets_ = ApkAssets::Load("overlayable/overlayable.apk"); + ASSERT_NE(nullptr, overlayable_assets_); + chdir(original_path.c_str()); } - ResTable target_table_; - std::string overlay_data_; - void* data_ = nullptr; - size_t data_size_ = 0; + protected: + std::unique_ptr system_assets_; + std::unique_ptr overlay_assets_; + std::unique_ptr overlayable_assets_; }; -TEST_F(IdmapTest, CanLoadIdmap) { - ASSERT_EQ(NO_ERROR, - target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, data_size_)); +std::string GetStringFromApkAssets(const AssetManager2& asset_manager, const Res_value& value, + ApkAssetsCookie cookie) { + auto assets = asset_manager.GetApkAssets(); + const ResStringPool* string_pool = assets[cookie]->GetLoadedArsc()->GetStringPool(); + return GetStringFromPool(string_pool, value.data); +} + } TEST_F(IdmapTest, OverlayOverridesResourceValue) { + AssetManager2 asset_manager; + asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(), + overlay_assets_.get()}); Res_value val; - ssize_t block = target_table_.getResource(R::string::test2, &val, false); - ASSERT_GE(block, 0); - ASSERT_EQ(Res_value::TYPE_STRING, val.dataType); - const ResStringPool* pool = target_table_.getTableStringBlock(block); - ASSERT_TRUE(pool != NULL); - ASSERT_LT(val.data, pool->size()); - - size_t str_len; - const char16_t* target_str16 = pool->stringAt(val.data, &str_len); - ASSERT_TRUE(target_str16 != NULL); - ASSERT_EQ(String16("test2"), String16(target_str16, str_len)); - - ASSERT_EQ(NO_ERROR, - target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, data_size_)); - - ssize_t new_block = target_table_.getResource(R::string::test2, &val, false); - ASSERT_GE(new_block, 0); - ASSERT_NE(block, new_block); - ASSERT_EQ(Res_value::TYPE_STRING, val.dataType); - pool = target_table_.getTableStringBlock(new_block); - ASSERT_TRUE(pool != NULL); - ASSERT_LT(val.data, pool->size()); - - target_str16 = pool->stringAt(val.data, &str_len); - ASSERT_TRUE(target_str16 != NULL); - ASSERT_EQ(String16("test2-overlay"), String16(target_str16, str_len)); + ResTable_config config; + uint32_t flags; + ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable5, + false /* may_be_bag */, + 0 /* density_override */, &val, &config, + &flags); + ASSERT_EQ(cookie, 2U); + ASSERT_EQ(val.dataType, Res_value::TYPE_STRING); + ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "Overlay One"); } -TEST_F(IdmapTest, OverlaidResourceHasSameName) { - ASSERT_EQ(NO_ERROR, - target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, data_size_)); +TEST_F(IdmapTest, OverlayOverridesResourceValueUsingDifferentPackage) { + AssetManager2 asset_manager; + asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(), + overlay_assets_.get()}); + Res_value val; + ResTable_config config; + uint32_t flags; + ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable10, + false /* may_be_bag */, + 0 /* density_override */, &val, &config, + &flags); + ASSERT_EQ(cookie, 0U); + ASSERT_EQ(val.dataType, Res_value::TYPE_STRING); + ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "yes"); +} + +TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInternalResource) { + AssetManager2 asset_manager; + asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(), + overlay_assets_.get()}); + Res_value val; + ResTable_config config; + uint32_t flags; + ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable8, + false /* may_be_bag */, + 0 /* density_override */, &val, &config, + &flags); + ASSERT_EQ(cookie, 2U); + ASSERT_EQ(val.dataType, Res_value::TYPE_REFERENCE); + ASSERT_EQ(val.data, (overlay::R::string::internal & 0x00ffffff) | (0x02 << 24)); +} + +TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInlineInteger) { + AssetManager2 asset_manager; + asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(), + overlay_assets_.get()}); + Res_value val; + ResTable_config config; + uint32_t flags; + ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::integer::config_integer, + false /* may_be_bag */, + 0 /* density_override */, &val, &config, + &flags); + ASSERT_EQ(cookie, 2U); + ASSERT_EQ(val.dataType, Res_value::TYPE_INT_DEC); + ASSERT_EQ(val.data, 42); +} + +TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInlineString) { + AssetManager2 asset_manager; + asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(), + overlay_assets_.get()}); + Res_value val; + ResTable_config config; + uint32_t flags; + + ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable11, + false /* may_be_bag */, + 0 /* density_override */, &val, &config, + &flags); + ASSERT_EQ(cookie, 2U); + ASSERT_EQ(val.dataType, Res_value::TYPE_STRING); + ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "Hardcoded string"); +} - ResTable::resource_name res_name; - ASSERT_TRUE(target_table_.getResourceName(R::array::integerArray1, true, &res_name)); +TEST_F(IdmapTest, OverlayOverridesResourceValueUsingOverlayingResource) { + AssetManager2 asset_manager; + asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(), + overlay_assets_.get()}); + Res_value val; + ResTable_config config; + uint32_t flags; + ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable9, + false /* may_be_bag */, + 0 /* density_override */, &val, &config, + &flags); + ASSERT_EQ(cookie, 2U); + ASSERT_EQ(val.dataType, Res_value::TYPE_REFERENCE); + ASSERT_EQ(val.data, overlayable::R::string::overlayable7); +} - ASSERT_TRUE(res_name.package != NULL); - ASSERT_TRUE(res_name.type != NULL); - ASSERT_TRUE(res_name.name8 != NULL); +TEST_F(IdmapTest, OverlayOverridesXmlParser) { + AssetManager2 asset_manager; + asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(), + overlay_assets_.get()}); + Res_value val; + ResTable_config config; + uint32_t flags; + ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::layout::hello_view, + false /* may_be_bag */, + 0 /* density_override */, &val, &config, + &flags); + ASSERT_EQ(cookie, 2U); + ASSERT_EQ(val.dataType, Res_value::TYPE_STRING); + ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "res/layout/hello_view.xml"); + + auto asset = asset_manager.OpenNonAsset("res/layout/hello_view.xml", cookie, + Asset::ACCESS_RANDOM); + auto dynamic_ref_table = asset_manager.GetDynamicRefTableForCookie(cookie); + auto xml_tree = util::make_unique(std::move(dynamic_ref_table)); + status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), false); + ASSERT_EQ(err, NO_ERROR); + + while (xml_tree->next() != ResXMLParser::START_TAG) { } + + // The resource id of @id/hello_view should be rewritten to the resource id/hello_view within the + // target. + ASSERT_EQ(xml_tree->getAttributeNameResID(0), 0x010100d0 /* android:attr/id */); + ASSERT_EQ(xml_tree->getAttributeDataType(0), Res_value::TYPE_REFERENCE); + ASSERT_EQ(xml_tree->getAttributeData(0), overlayable::R::id::hello_view); + + // The resource id of @android:string/yes should not be rewritten even though it overlays + // string/overlayable10 in the target. + ASSERT_EQ(xml_tree->getAttributeNameResID(1), 0x0101014f /* android:attr/text */); + ASSERT_EQ(xml_tree->getAttributeDataType(1), Res_value::TYPE_REFERENCE); + ASSERT_EQ(xml_tree->getAttributeData(1), 0x01040013 /* android:string/yes */); + + // The resource id of the attribute within the overlay should be rewritten to the resource id of + // the attribute in the target. + ASSERT_EQ(xml_tree->getAttributeNameResID(2), overlayable::R::attr::max_lines); + ASSERT_EQ(xml_tree->getAttributeDataType(2), Res_value::TYPE_INT_DEC); + ASSERT_EQ(xml_tree->getAttributeData(2), 4); +} - EXPECT_EQ(String16("com.android.basic"), String16(res_name.package, res_name.packageLen)); - EXPECT_EQ(String16("array"), String16(res_name.type, res_name.typeLen)); - EXPECT_EQ(String8("integerArray1"), String8(res_name.name8, res_name.nameLen)); +TEST_F(IdmapTest, OverlaidResourceHasSameName) { + AssetManager2 asset_manager; + asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(), + overlay_assets_.get()}); + + AssetManager2::ResourceName name; + ASSERT_TRUE(asset_manager.GetResourceName(overlayable::R::string::overlayable9, &name)); + ASSERT_EQ(std::string(name.package), "com.android.overlayable"); + ASSERT_EQ(String16(name.type16), u"string"); + ASSERT_EQ(std::string(name.entry), "overlayable9"); } -constexpr const uint32_t kNonOverlaidResourceId = 0x7fff0000u; +TEST_F(IdmapTest, OverlayLoaderInterop) { + std::string contents; + auto loader_assets = ApkAssets::LoadArsc(GetTestDataPath() + "/loader/resources.arsc", + /* for_loader */ true); -TEST_F(IdmapTest, OverlayDoesNotIncludeNonOverlaidResources) { - // First check that the resource we're trying to not include when overlaid is present when - // the overlay is loaded as a standalone APK. - ResTable table; - ASSERT_EQ(NO_ERROR, table.add(overlay_data_.data(), overlay_data_.size(), 0, true)); + AssetManager2 asset_manager; + asset_manager.SetApkAssets({overlayable_assets_.get(), loader_assets.get(), + overlay_assets_.get()}); Res_value val; - ssize_t block = table.getResource(kNonOverlaidResourceId, &val, false /*mayBeBag*/); - ASSERT_GE(block, 0); - - // Now add the overlay and verify that the unoverlaid resource is gone. - ASSERT_EQ(NO_ERROR, - target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, data_size_)); - block = target_table_.getResource(kNonOverlaidResourceId, &val, false /*mayBeBag*/); - ASSERT_LT(block, 0); + ResTable_config config; + uint32_t flags; + ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable11, + false /* may_be_bag */, + 0 /* density_override */, &val, &config, + &flags); + std::cout << asset_manager.GetLastResourceResolution(); + ASSERT_EQ(cookie, 1U); + ASSERT_EQ(val.dataType, Res_value::TYPE_STRING); + ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "loader"); } } // namespace diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp index fd57a92c216b..82dd33523c75 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -144,7 +144,7 @@ TEST(LoadedArscTest, LoadAppAsSharedLibrary) { "resources.arsc", &contents)); std::unique_ptr loaded_arsc = - LoadedArsc::Load(StringPiece(contents), nullptr /*loaded_idmap*/, false /*system*/, + LoadedArsc::Load(StringPiece(contents), nullptr /* loaded_idmap */, false /*system*/, true /*load_as_shared_library*/); ASSERT_THAT(loaded_arsc, NotNull()); @@ -222,67 +222,13 @@ TEST(LoadedArscTest, LoadOutOfOrderTypeSpecs) { ASSERT_THAT(type_spec->types[0], NotNull()); } -class MockLoadedIdmap : public LoadedIdmap { - public: - MockLoadedIdmap() : LoadedIdmap() { - local_header_.magic = kIdmapMagic; - local_header_.version = kIdmapCurrentVersion; - local_header_.target_package_id = 0x08; - local_header_.type_count = 1; - header_ = &local_header_; - - entry_header = util::unique_cptr( - (IdmapEntry_header*)::malloc(sizeof(IdmapEntry_header) + sizeof(uint32_t))); - entry_header->target_type_id = 0x03; - entry_header->overlay_type_id = 0x02; - entry_header->entry_id_offset = 1; - entry_header->entry_count = 1; - entry_header->entries[0] = 0x00000000u; - type_map_[entry_header->overlay_type_id] = entry_header.get(); - } - - private: - Idmap_header local_header_; - util::unique_cptr entry_header; -}; - -TEST(LoadedArscTest, LoadOverlay) { - std::string contents; - ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlay/overlay.apk", "resources.arsc", - &contents)); - - MockLoadedIdmap loaded_idmap; - - std::unique_ptr loaded_arsc = - LoadedArsc::Load(StringPiece(contents), &loaded_idmap); - ASSERT_THAT(loaded_arsc, NotNull()); - - const LoadedPackage* package = loaded_arsc->GetPackageById(0x08u); - ASSERT_THAT(package, NotNull()); - - const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(0x03u - 1); - ASSERT_THAT(type_spec, NotNull()); - ASSERT_THAT(type_spec->type_count, Ge(1u)); - ASSERT_THAT(type_spec->types[0], NotNull()); - - // The entry being overlaid doesn't exist at the original entry index. - ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0001u), IsNull()); - - // Since this is an overlay, the actual entry ID must be mapped. - ASSERT_THAT(type_spec->idmap_entries, NotNull()); - uint16_t target_entry_id = 0u; - ASSERT_TRUE(LoadedIdmap::Lookup(type_spec->idmap_entries, 0x0001u, &target_entry_id)); - ASSERT_THAT(target_entry_id, Eq(0x0u)); - ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0000), NotNull()); -} - TEST(LoadedArscTest, LoadOverlayable) { std::string contents; ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlayable/overlayable.apk", "resources.arsc", &contents)); std::unique_ptr loaded_arsc = - LoadedArsc::Load(StringPiece(contents), nullptr /*loaded_idmap*/, false /*system*/, + LoadedArsc::Load(StringPiece(contents), nullptr /* loaded_idmap */, false /*system*/, false /*load_as_shared_library*/); ASSERT_THAT(loaded_arsc, NotNull()); @@ -383,9 +329,10 @@ TEST(LoadedArscTest, GetOverlayableMap) { ASSERT_EQ(std::string("com.android.overlayable"), packages[0]->GetPackageName()); const auto map = packages[0]->GetOverlayableMap(); - ASSERT_EQ(2, map.size()); + ASSERT_EQ(3, map.size()); ASSERT_EQ(map.at("OverlayableResources1"), "overlay://theme"); ASSERT_EQ(map.at("OverlayableResources2"), "overlay://com.android.overlayable"); + ASSERT_EQ(map.at("OverlayableResources3"), ""); } TEST(LoadedArscTest, LoadCustomLoader) { @@ -394,7 +341,6 @@ TEST(LoadedArscTest, LoadCustomLoader) { std::unique_ptr asset = ApkAssets::CreateAssetFromFile(GetTestDataPath() + "/loader/resources.arsc"); - MockLoadedIdmap loaded_idmap; const StringPiece data( reinterpret_cast(asset->getBuffer(true /*wordAligned*/)), asset->getLength()); @@ -404,13 +350,13 @@ TEST(LoadedArscTest, LoadCustomLoader) { ASSERT_THAT(loaded_arsc, NotNull()); const LoadedPackage* package = - loaded_arsc->GetPackageById(get_package_id(android::R::string::cancel)); + loaded_arsc->GetPackageById(get_package_id(overlayable::R::string::overlayable11)); ASSERT_THAT(package, NotNull()); - EXPECT_THAT(package->GetPackageName(), StrEq("android")); - EXPECT_THAT(package->GetPackageId(), Eq(0x01)); + EXPECT_THAT(package->GetPackageName(), StrEq("com.android.loader")); + EXPECT_THAT(package->GetPackageId(), Eq(0x7f)); - const uint8_t type_index = get_type_id(android::R::string::cancel) - 1; - const uint16_t entry_index = get_entry_id(android::R::string::cancel); + const uint8_t type_index = get_type_id(overlayable::R::string::overlayable11) - 1; + const uint16_t entry_index = get_entry_id(overlayable::R::string::overlayable11); const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); ASSERT_THAT(type_spec, NotNull()); diff --git a/libs/androidfw/tests/data/loader/AndroidManifest.xml b/libs/androidfw/tests/data/loader/AndroidManifest.xml new file mode 100644 index 000000000000..4c0bb47f59bf --- /dev/null +++ b/libs/androidfw/tests/data/loader/AndroidManifest.xml @@ -0,0 +1,20 @@ + + + + + + + diff --git a/libs/androidfw/tests/data/loader/build b/libs/androidfw/tests/data/loader/build new file mode 100755 index 000000000000..457ec51ffe69 --- /dev/null +++ b/libs/androidfw/tests/data/loader/build @@ -0,0 +1,27 @@ +#!/bin/bash +# +# Copyright (C) 2018 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. +# + +set -e + +FRAMEWORK_RES_APK=${ANDROID_PRODUCT_OUT}/system/framework/framework-res.apk + +rm resources.arsc +aapt2 compile --dir res -o compiled.flata +aapt2 link -I $FRAMEWORK_RES_APK --manifest AndroidManifest.xml -o loader.apk compiled.flata +unzip loader.apk resources.arsc +rm loader.apk +rm compiled.flata diff --git a/libs/androidfw/tests/data/loader/res/values/public.xml b/libs/androidfw/tests/data/loader/res/values/public.xml new file mode 100644 index 000000000000..3293229104ce --- /dev/null +++ b/libs/androidfw/tests/data/loader/res/values/public.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/libs/androidfw/tests/data/loader/res/values/values.xml b/libs/androidfw/tests/data/loader/res/values/values.xml new file mode 100644 index 000000000000..0653536508f8 --- /dev/null +++ b/libs/androidfw/tests/data/loader/res/values/values.xml @@ -0,0 +1,19 @@ + + + + + loader + diff --git a/libs/androidfw/tests/data/loader/resources.arsc b/libs/androidfw/tests/data/loader/resources.arsc index 2c881f2cdfe5..2bdb288dbcab 100644 Binary files a/libs/androidfw/tests/data/loader/resources.arsc and b/libs/androidfw/tests/data/loader/resources.arsc differ diff --git a/libs/androidfw/tests/data/overlay/AndroidManifest.xml b/libs/androidfw/tests/data/overlay/AndroidManifest.xml index a56ac18e900b..28a11489fae0 100644 --- a/libs/androidfw/tests/data/overlay/AndroidManifest.xml +++ b/libs/androidfw/tests/data/overlay/AndroidManifest.xml @@ -15,7 +15,9 @@ --> - - + package="com.android.test.overlay"> + diff --git a/libs/androidfw/tests/data/overlay/R.h b/libs/androidfw/tests/data/overlay/R.h new file mode 100644 index 000000000000..f3dbed29d7ee --- /dev/null +++ b/libs/androidfw/tests/data/overlay/R.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2019 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 TESTS_DATA_OVERLAY_R_H_ +#define TESTS_DATA_OVERLAY_R_H_ + +#include + +namespace com { +namespace android { +namespace overlay { + +struct R { + struct string { + enum : uint32_t { + internal = 0x7f040000, + }; + }; +}; + +} // namespace overlay +} // namespace android +} // namespace com + +#endif /* TESTS_DATA_OVERLAY_R_H_ */ diff --git a/libs/androidfw/tests/data/overlay/build b/libs/androidfw/tests/data/overlay/build index 716b1bd9db64..99dfd63051a7 100755 --- a/libs/androidfw/tests/data/overlay/build +++ b/libs/androidfw/tests/data/overlay/build @@ -17,6 +17,15 @@ set -e +FRAMEWORK_RES_APK=${ANDROID_PRODUCT_OUT}/system/framework/framework-res.apk + aapt2 compile --dir res -o compiled.flata -aapt2 link --manifest AndroidManifest.xml -o overlay.apk compiled.flata +aapt2 link -I $FRAMEWORK_RES_APK --manifest AndroidManifest.xml -o overlay.apk compiled.flata \ + --no-auto-version rm compiled.flata + +# Navigate back a directory so the idmap can find the overlays in the test data directory when being +# loaded during testing. +cd ../ +idmap2 create --target-apk-path overlayable/overlayable.apk \ + --overlay-apk-path overlay/overlay.apk --idmap-path overlay/overlay.idmap diff --git a/libs/androidfw/tests/data/overlay/overlay.apk b/libs/androidfw/tests/data/overlay/overlay.apk index d37874dcbb40..c594b8e67f28 100644 Binary files a/libs/androidfw/tests/data/overlay/overlay.apk and b/libs/androidfw/tests/data/overlay/overlay.apk differ diff --git a/libs/androidfw/tests/data/overlay/overlay.idmap b/libs/androidfw/tests/data/overlay/overlay.idmap new file mode 100644 index 000000000000..27cf792ff7e2 Binary files /dev/null and b/libs/androidfw/tests/data/overlay/overlay.idmap differ diff --git a/libs/androidfw/tests/data/overlay/res/layout/hello_view.xml b/libs/androidfw/tests/data/overlay/res/layout/hello_view.xml new file mode 100644 index 000000000000..54dc6c09acf1 --- /dev/null +++ b/libs/androidfw/tests/data/overlay/res/layout/hello_view.xml @@ -0,0 +1,20 @@ + + + \ No newline at end of file diff --git a/libs/androidfw/tests/data/overlay/res/values/values.xml b/libs/androidfw/tests/data/overlay/res/values/values.xml index 8e4417e457d1..ba018ec19e9f 100644 --- a/libs/androidfw/tests/data/overlay/res/values/values.xml +++ b/libs/androidfw/tests/data/overlay/res/values/values.xml @@ -13,13 +13,11 @@ See the License for the specific language governing permissions and limitations under the License. --> - - test2-overlay - - 10 - 11 - - - @null + Overlay One + Overlay Two + @string/internal + @string/overlay2 + Internal + diff --git a/libs/androidfw/tests/data/overlay/res/xml/overlays.xml b/libs/androidfw/tests/data/overlay/res/xml/overlays.xml new file mode 100644 index 000000000000..9eca2faa76f4 --- /dev/null +++ b/libs/androidfw/tests/data/overlay/res/xml/overlays.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/androidfw/tests/data/overlayable/R.h b/libs/androidfw/tests/data/overlayable/R.h index e46e264da318..35125a62ff4b 100644 --- a/libs/androidfw/tests/data/overlayable/R.h +++ b/libs/androidfw/tests/data/overlayable/R.h @@ -31,6 +31,43 @@ struct R { overlayable2 = 0x7f010002, overlayable3 = 0x7f010003, overlayable4 = 0x7f010004, + overlayable5 = 0x7f010005, + overlayable6 = 0x7f010006, + overlayable7 = 0x7f010007, + overlayable8 = 0x7f010008, + overlayable9 = 0x7f010009, + overlayable10 = 0x7f01000a, + overlayable11 = 0x7f01000b, + }; + }; + + struct attr { + enum : uint32_t { + max_lines = 0x7f020000, + }; + }; + + struct boolean { + enum : uint32_t { + config_bool = 0x7f030000, + }; + }; + + struct id { + enum : uint32_t { + hello_view = 0x7f040000, + }; + }; + + struct integer { + enum : uint32_t { + config_integer = 0x7f050000, + }; + }; + + struct layout { + enum : uint32_t { + hello_view = 0x7f060000, }; }; }; diff --git a/libs/androidfw/tests/data/overlayable/build b/libs/androidfw/tests/data/overlayable/build index 98fdc5101160..0aa97d639c30 100755 --- a/libs/androidfw/tests/data/overlayable/build +++ b/libs/androidfw/tests/data/overlayable/build @@ -17,6 +17,9 @@ set -e +FRAMEWORK_RES_APK=${ANDROID_PRODUCT_OUT}/system/framework/framework-res.apk + aapt2 compile --dir res -o compiled.flata -aapt2 link --manifest AndroidManifest.xml -o overlayable.apk compiled.flata +aapt2 link -I $FRAMEWORK_RES_APK --manifest AndroidManifest.xml -o overlayable.apk compiled.flata \ + --no-auto-version rm compiled.flata diff --git a/libs/androidfw/tests/data/overlayable/overlayable.apk b/libs/androidfw/tests/data/overlayable/overlayable.apk index 047e6afde86b..9dc9c15f68a9 100644 Binary files a/libs/androidfw/tests/data/overlayable/overlayable.apk and b/libs/androidfw/tests/data/overlayable/overlayable.apk differ diff --git a/libs/androidfw/tests/data/overlayable/res/layout/hello_view.xml b/libs/androidfw/tests/data/overlayable/res/layout/hello_view.xml new file mode 100644 index 000000000000..268118a91bff --- /dev/null +++ b/libs/androidfw/tests/data/overlayable/res/layout/hello_view.xml @@ -0,0 +1,20 @@ + + + \ No newline at end of file diff --git a/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml b/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml index fcdbe94466c1..b3e8f7d8e84b 100644 --- a/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml +++ b/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml @@ -15,27 +15,41 @@ --> - - - + + + + + - - - - + + + + + + - - - - - + + + + + + - - - - - - + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libs/androidfw/tests/data/overlayable/res/values/public.xml b/libs/androidfw/tests/data/overlayable/res/values/public.xml index 5676d7cc64c9..042a311b2b88 100644 --- a/libs/androidfw/tests/data/overlayable/res/values/public.xml +++ b/libs/androidfw/tests/data/overlayable/res/values/public.xml @@ -20,4 +20,21 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libs/androidfw/tests/data/overlayable/res/values/values.xml b/libs/androidfw/tests/data/overlayable/res/values/values.xml index a86b31282bc9..235772d35fd0 100644 --- a/libs/androidfw/tests/data/overlayable/res/values/values.xml +++ b/libs/androidfw/tests/data/overlayable/res/values/values.xml @@ -20,4 +20,15 @@ Overlayable Two Overlayable Three Overlayable Four + Overlayable Five + Overlayable Six + Overlayable Seven + Overlayable Eight + Overlayable Nine + Overlayable Ten + Overlayable Eleven + + 0 + false + diff --git a/libs/androidfw/tests/data/system/R.h b/libs/androidfw/tests/data/system/R.h index 374107484784..c0160c0f78a9 100644 --- a/libs/androidfw/tests/data/system/R.h +++ b/libs/androidfw/tests/data/system/R.h @@ -43,7 +43,8 @@ struct R { struct string { enum : uint32_t { - cancel = 0x01040000, + no = 0x01040009, + yes = 0x01040013, }; }; }; diff --git a/libs/androidfw/tests/data/system/res/values/public.xml b/libs/androidfw/tests/data/system/res/values/public.xml new file mode 100644 index 000000000000..077874d0b0fe --- /dev/null +++ b/libs/androidfw/tests/data/system/res/values/public.xml @@ -0,0 +1,20 @@ + + + + + + + \ No newline at end of file diff --git a/libs/androidfw/tests/data/system/res/values/values.xml b/libs/androidfw/tests/data/system/res/values/values.xml new file mode 100644 index 000000000000..0629c1d13795 --- /dev/null +++ b/libs/androidfw/tests/data/system/res/values/values.xml @@ -0,0 +1,20 @@ + + + + + yes + no + \ No newline at end of file diff --git a/libs/androidfw/tests/data/system/system.apk b/libs/androidfw/tests/data/system/system.apk index 9045d6c4de21..1f7e00733366 100644 Binary files a/libs/androidfw/tests/data/system/system.apk and b/libs/androidfw/tests/data/system/system.apk differ -- cgit v1.2.3 From ca718a06d57ca6250e85b3d7c67ed42f5c6b289f Mon Sep 17 00:00:00 2001 From: John Reck Date: Mon, 21 Oct 2019 10:14:58 -0700 Subject: Fix buffer damage for quad-buffer Test: none Change-Id: I4789d84945e381d67e96ef37554a161dbd0ab16c --- libs/hwui/renderthread/CanvasContext.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 8a76d6b3fc7a..6e3e43af8c6f 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -242,7 +242,8 @@ private: nsecs_t queueDuration; }; - RingBuffer mSwapHistory; + // Need at least 4 because we do quad buffer. Add a 5th for good measure. + RingBuffer mSwapHistory; int64_t mFrameNumber = -1; // last vsync for a dropped frame due to stuffed queue -- cgit v1.2.3 From 22d753f74d83bec10fc05a04156adaf76430d540 Mon Sep 17 00:00:00 2001 From: Alec Mouri Date: Thu, 5 Sep 2019 17:11:45 -0700 Subject: [HWUI] Get DeviceInfo through stable ABI This also removes the dependency on ui/DeviceInfo other than in test code. Bug: 136263392 Bug: 136263238 Test: builds, boots Change-Id: I6a4687e981359f0e6beb83be8a5501ed7fd16f15 --- libs/hwui/Android.bp | 1 + libs/hwui/DeviceInfo.cpp | 101 ++++++++++++----------------- libs/hwui/DeviceInfo.h | 26 ++++++-- libs/hwui/JankTracker.cpp | 14 ++-- libs/hwui/JankTracker.h | 3 +- libs/hwui/Properties.cpp | 1 - libs/hwui/RenderProperties.h | 3 + libs/hwui/renderthread/CacheManager.cpp | 9 +-- libs/hwui/renderthread/CacheManager.h | 3 +- libs/hwui/renderthread/CanvasContext.cpp | 4 +- libs/hwui/renderthread/RenderThread.cpp | 5 +- libs/hwui/renderthread/RenderThread.h | 1 - libs/hwui/tests/unit/CacheManagerTests.cpp | 8 +-- 13 files changed, 87 insertions(+), 92 deletions(-) (limited to 'libs') diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index ae90f117d448..b19aa2ff5862 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -88,6 +88,7 @@ cc_defaults { "libvulkan", "libui", "libgui", + "libnativedisplay", "libnativewindow", "libprotobuf-cpp-lite", "libft2", diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp index a0d3ff995e78..41e9b4be6649 100644 --- a/libs/hwui/DeviceInfo.cpp +++ b/libs/hwui/DeviceInfo.cpp @@ -15,74 +15,21 @@ */ #include - -#include "Properties.h" - #include +#include #include #include #include -#include +#include "Properties.h" namespace android { namespace uirenderer { -static constexpr android::DisplayInfo sDummyDisplay{ - 1080, // w - 1920, // h - 320.0, // xdpi - 320.0, // ydpi - 60.0, // fps - 2.0, // density - 0, // orientation - false, // secure? - 0, // appVsyncOffset - 0, // presentationDeadline - 1080, // viewportW - 1920, // viewportH -}; - DeviceInfo* DeviceInfo::get() { - static DeviceInfo sDeviceInfo; - return &sDeviceInfo; -} - -static DisplayInfo QueryDisplayInfo() { - if (Properties::isolatedProcess) { - return sDummyDisplay; - } - - const sp token = SurfaceComposerClient::getInternalDisplayToken(); - LOG_ALWAYS_FATAL_IF(token == nullptr, - "Failed to get display info because internal display is disconnected"); - - DisplayInfo displayInfo; - status_t status = SurfaceComposerClient::getDisplayInfo(token, &displayInfo); - LOG_ALWAYS_FATAL_IF(status, "Failed to get display info, error %d", status); - return displayInfo; -} - -static float QueryMaxRefreshRate() { - if (Properties::isolatedProcess) { - return sDummyDisplay.fps; - } - - const sp token = SurfaceComposerClient::getInternalDisplayToken(); - LOG_ALWAYS_FATAL_IF(token == nullptr, - "Failed to get display info because internal display is disconnected"); - - Vector configs; - configs.reserve(10); - status_t status = SurfaceComposerClient::getDisplayConfigs(token, &configs); - LOG_ALWAYS_FATAL_IF(status, "Failed to getDisplayConfigs, error %d", status); - LOG_ALWAYS_FATAL_IF(configs.size() == 0, "getDisplayConfigs returned 0 configs?"); - float max = 0.0f; - for (auto& info : configs) { - max = std::max(max, info.fps); - } - return max; + static DeviceInfo sDeviceInfo; + return &sDeviceInfo; } static void queryWideColorGamutPreference(sk_sp* colorSpace, SkColorType* colorType) { @@ -123,14 +70,17 @@ static void queryWideColorGamutPreference(sk_sp* colorSpace, SkCol } } -DeviceInfo::DeviceInfo() : mMaxRefreshRate(QueryMaxRefreshRate()) { +DeviceInfo::DeviceInfo() { #if HWUI_NULL_GPU mMaxTextureSize = NULL_GPU_MAX_TEXTURE_SIZE; #else mMaxTextureSize = -1; #endif - mDisplayInfo = QueryDisplayInfo(); - queryWideColorGamutPreference(&mWideColorSpace, &mWideColorType); + updateDisplayInfo(); + queryWideColorGamutPreference(&mWideColorSpace, &mWideColorType); +} +DeviceInfo::~DeviceInfo() { + ADisplay_release(mDisplays); } int DeviceInfo::maxTextureSize() const { @@ -143,7 +93,36 @@ void DeviceInfo::setMaxTextureSize(int maxTextureSize) { } void DeviceInfo::onDisplayConfigChanged() { - mDisplayInfo = QueryDisplayInfo(); + updateDisplayInfo(); +} + +void DeviceInfo::updateDisplayInfo() { + if (Properties::isolatedProcess) { + return; + } + + if (mCurrentConfig == nullptr) { + mDisplaysSize = ADisplay_acquirePhysicalDisplays(&mDisplays); + LOG_ALWAYS_FATAL_IF(mDisplays == nullptr || mDisplaysSize <= 0, + "Failed to get physical displays: no connected display: %d!", mDisplaysSize); + for (size_t i = 0; i < mDisplaysSize; i++) { + ADisplayType type = ADisplay_getDisplayType(mDisplays[i]); + if (type == ADisplayType::DISPLAY_TYPE_INTERNAL) { + mPhysicalDisplayIndex = i; + break; + } + } + LOG_ALWAYS_FATAL_IF(mPhysicalDisplayIndex < 0, "Failed to find a connected physical display!"); + mMaxRefreshRate = ADisplay_getMaxSupportedFps(mDisplays[mPhysicalDisplayIndex]); + } + status_t status = ADisplay_getCurrentConfig(mDisplays[mPhysicalDisplayIndex], &mCurrentConfig); + LOG_ALWAYS_FATAL_IF(status, "Failed to get display config, error %d", status); + mWidth = ADisplayConfig_getWidth(mCurrentConfig); + mHeight = ADisplayConfig_getHeight(mCurrentConfig); + mDensity = ADisplayConfig_getDensity(mCurrentConfig); + mRefreshRate = ADisplayConfig_getFps(mCurrentConfig); + mCompositorOffset = ADisplayConfig_getCompositorOffsetNanos(mCurrentConfig); + mAppOffset = ADisplayConfig_getAppVsyncOffsetNanos(mCurrentConfig); } } /* namespace uirenderer */ diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h index 0e3f11960ddc..34315830ed97 100644 --- a/libs/hwui/DeviceInfo.h +++ b/libs/hwui/DeviceInfo.h @@ -16,8 +16,8 @@ #ifndef DEVICEINFO_H #define DEVICEINFO_H +#include #include -#include #include "utils/Macros.h" @@ -33,28 +33,44 @@ class DeviceInfo { public: static DeviceInfo* get(); + static float getMaxRefreshRate() { return get()->mMaxRefreshRate; } + static int32_t getWidth() { return get()->mWidth; } + static int32_t getHeight() { return get()->mHeight; } + static float getDensity() { return get()->mDensity; } + static float getRefreshRate() { return get()->mRefreshRate; } + static int64_t getCompositorOffset() { return get()->mCompositorOffset; } + static int64_t getAppOffset() { return get()->mAppOffset; } // this value is only valid after the GPU has been initialized and there is a valid graphics // context or if you are using the HWUI_NULL_GPU int maxTextureSize() const; - const DisplayInfo& displayInfo() const { return mDisplayInfo; } sk_sp getWideColorSpace() const { return mWideColorSpace; } SkColorType getWideColorType() const { return mWideColorType; } - float getMaxRefreshRate() const { return mMaxRefreshRate; } void onDisplayConfigChanged(); private: friend class renderthread::RenderThread; static void setMaxTextureSize(int maxTextureSize); + void updateDisplayInfo(); DeviceInfo(); + ~DeviceInfo(); int mMaxTextureSize; - DisplayInfo mDisplayInfo; sk_sp mWideColorSpace; SkColorType mWideColorType; - const float mMaxRefreshRate; + ADisplayConfig* mCurrentConfig = nullptr; + ADisplay** mDisplays = nullptr; + int mDisplaysSize = 0; + int mPhysicalDisplayIndex = -1; + float mMaxRefreshRate = 60.0; + int32_t mWidth = 1080; + int32_t mHeight = 1920; + float mDensity = 2.0; + float mRefreshRate = 60.0; + int64_t mCompositorOffset = 0; + int64_t mAppOffset = 0; }; } /* namespace uirenderer */ diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp index eae3584465e4..10e7160e7069 100644 --- a/libs/hwui/JankTracker.cpp +++ b/libs/hwui/JankTracker.cpp @@ -16,8 +16,10 @@ #include "JankTracker.h" +#include #include #include +#include #include #include @@ -25,11 +27,9 @@ #include #include #include - -#include -#include #include +#include "DeviceInfo.h" #include "Properties.h" #include "utils/TimeUtils.h" #include "utils/Trace.h" @@ -79,11 +79,11 @@ static const int64_t EXEMPT_FRAMES_FLAGS = FrameInfoFlags::SurfaceCanvas; // and filter it out of the frame profile data static FrameInfoIndex sFrameStart = FrameInfoIndex::IntendedVsync; -JankTracker::JankTracker(ProfileDataContainer* globalData, const DisplayInfo& displayInfo) { +JankTracker::JankTracker(ProfileDataContainer* globalData) { mGlobalData = globalData; - nsecs_t frameIntervalNanos = static_cast(1_s / displayInfo.fps); - nsecs_t sfOffset = frameIntervalNanos - (displayInfo.presentationDeadline - 1_ms); - nsecs_t offsetDelta = sfOffset - displayInfo.appVsyncOffset; + nsecs_t frameIntervalNanos = static_cast(1_s / DeviceInfo::getRefreshRate()); + nsecs_t sfOffset = DeviceInfo::getCompositorOffset(); + nsecs_t offsetDelta = sfOffset - DeviceInfo::getAppOffset(); // There are two different offset cases. If the offsetDelta is positive // and small, then the intention is to give apps extra time by leveraging // pipelining between the UI & RT threads. If the offsetDelta is large or diff --git a/libs/hwui/JankTracker.h b/libs/hwui/JankTracker.h index 08059268f03e..4460266276f9 100644 --- a/libs/hwui/JankTracker.h +++ b/libs/hwui/JankTracker.h @@ -23,7 +23,6 @@ #include "utils/RingBuffer.h" #include -#include #include #include @@ -49,7 +48,7 @@ struct ProfileDataDescription { // TODO: Replace DrawProfiler with this class JankTracker { public: - explicit JankTracker(ProfileDataContainer* globalData, const DisplayInfo& displayInfo); + explicit JankTracker(ProfileDataContainer* globalData); void setDescription(JankTrackerType type, const std::string&& name) { mDescription.type = type; diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index 4e7df88b8095..446e81e65bb8 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -16,7 +16,6 @@ #include "Properties.h" #include "Debug.h" -#include "DeviceInfo.h" #ifdef __ANDROID__ #include "HWUIProperties.sysprop.h" #endif diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h index e9794489f171..24f6035b6708 100644 --- a/libs/hwui/RenderProperties.h +++ b/libs/hwui/RenderProperties.h @@ -16,7 +16,10 @@ #pragma once +#ifdef __ANDROID__ // Layoutlib does not support device info #include "DeviceInfo.h" +#endif // __ANDROID__ + #include "Outline.h" #include "Rect.h" #include "RevealClip.h" diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index dc07f0d84d18..b78c50a4b66d 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -16,6 +16,7 @@ #include "CacheManager.h" +#include "DeviceInfo.h" #include "Layer.h" #include "Properties.h" #include "RenderThread.h" @@ -42,8 +43,8 @@ namespace renderthread { #define SURFACE_SIZE_MULTIPLIER (5.0f * 4.0f) #define BACKGROUND_RETENTION_PERCENTAGE (0.5f) -CacheManager::CacheManager(const DisplayInfo& display) - : mMaxSurfaceArea(display.w * display.h) +CacheManager::CacheManager() + : mMaxSurfaceArea(DeviceInfo::getWidth() * DeviceInfo::getHeight()) , mMaxResourceBytes(mMaxSurfaceArea * SURFACE_SIZE_MULTIPLIER) , mBackgroundResourceBytes(mMaxResourceBytes * BACKGROUND_RETENTION_PERCENTAGE) // This sets the maximum size for a single texture atlas in the GPU font cache. If @@ -52,9 +53,9 @@ CacheManager::CacheManager(const DisplayInfo& display) , mMaxGpuFontAtlasBytes(GrNextSizePow2(mMaxSurfaceArea)) // This sets the maximum size of the CPU font cache to be at least the same size as the // total number of GPU font caches (i.e. 4 separate GPU atlases). - , mMaxCpuFontCacheBytes(std::max(mMaxGpuFontAtlasBytes*4, SkGraphics::GetFontCacheLimit())) + , mMaxCpuFontCacheBytes( + std::max(mMaxGpuFontAtlasBytes * 4, SkGraphics::GetFontCacheLimit())) , mBackgroundCpuFontCacheBytes(mMaxCpuFontCacheBytes * BACKGROUND_RETENTION_PERCENTAGE) { - SkGraphics::SetFontCacheLimit(mMaxCpuFontCacheBytes); mVectorDrawableAtlas = new skiapipeline::VectorDrawableAtlas( diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h index d7977cc4566d..dacb7bdb9207 100644 --- a/libs/hwui/renderthread/CacheManager.h +++ b/libs/hwui/renderthread/CacheManager.h @@ -21,7 +21,6 @@ #include #endif #include -#include #include #include @@ -59,7 +58,7 @@ public: private: friend class RenderThread; - explicit CacheManager(const DisplayInfo& display); + explicit CacheManager(); #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration void reset(sk_sp grContext); diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 93fd0c87a361..323f622cdb0a 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -102,13 +102,13 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* , mGenerationID(0) , mOpaque(!translucent) , mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord())) - , mJankTracker(&thread.globalProfileData(), DeviceInfo::get()->displayInfo()) + , mJankTracker(&thread.globalProfileData()) , mProfiler(mJankTracker.frames(), thread.timeLord().frameIntervalNanos()) , mContentDrawBounds(0, 0, 0, 0) , mRenderPipeline(std::move(renderPipeline)) { rootRenderNode->makeRoot(); mRenderNodes.emplace_back(rootRenderNode); - mProfiler.setDensity(DeviceInfo::get()->displayInfo().density); + mProfiler.setDensity(DeviceInfo::getDensity()); setRenderAheadDepth(Properties::defaultRenderAhead); } diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index ee1a7ce19e82..a446858ca565 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -179,12 +179,11 @@ void RenderThread::initThreadLocals() { mEglManager = new EglManager(); mRenderState = new RenderState(*this); mVkManager = new VulkanManager(); - mCacheManager = new CacheManager(DeviceInfo::get()->displayInfo()); + mCacheManager = new CacheManager(); } void RenderThread::setupFrameInterval() { - auto& displayInfo = DeviceInfo::get()->displayInfo(); - nsecs_t frameIntervalNanos = static_cast(1000000000 / displayInfo.fps); + nsecs_t frameIntervalNanos = static_cast(1000000000 / DeviceInfo::getRefreshRate()); mTimeLord.setFrameInterval(frameIntervalNanos); mDispatchFrameDelay = static_cast(frameIntervalNanos * .25f); } diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index 5aa1af32094f..bdd80721c4f3 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -29,7 +29,6 @@ #include #include #include -#include #include #include diff --git a/libs/hwui/tests/unit/CacheManagerTests.cpp b/libs/hwui/tests/unit/CacheManagerTests.cpp index 3f1ef93c878c..c83a3c88cbdd 100644 --- a/libs/hwui/tests/unit/CacheManagerTests.cpp +++ b/libs/hwui/tests/unit/CacheManagerTests.cpp @@ -33,7 +33,8 @@ static size_t getCacheUsage(GrContext* grContext) { } RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, trimMemory) { - DisplayInfo displayInfo = DeviceInfo::get()->displayInfo(); + int32_t width = DeviceInfo::get()->getWidth(); + int32_t height = DeviceInfo::get()->getHeight(); GrContext* grContext = renderThread.getGrContext(); ASSERT_TRUE(grContext != nullptr); @@ -42,7 +43,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, trimMemory) { std::vector> surfaces; while (getCacheUsage(grContext) <= renderThread.cacheManager().getBackgroundCacheSize()) { - SkImageInfo info = SkImageInfo::MakeA8(displayInfo.w, displayInfo.h); + SkImageInfo info = SkImageInfo::MakeA8(width, height); sk_sp surface = SkSurface::MakeRenderTarget(grContext, SkBudgeted::kYes, info); surface->getCanvas()->drawColor(SK_AlphaTRANSPARENT); @@ -52,8 +53,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, trimMemory) { } // create an image and pin it so that we have something with a unique key in the cache - sk_sp bitmap = - Bitmap::allocateHeapBitmap(SkImageInfo::MakeA8(displayInfo.w, displayInfo.h)); + sk_sp bitmap = Bitmap::allocateHeapBitmap(SkImageInfo::MakeA8(width, height)); sk_sp image = bitmap->makeImage(); ASSERT_TRUE(SkImage_pinAsTexture(image.get(), grContext)); -- cgit v1.2.3 From da819e47f14657c6eddf4905da99b92743aaa2c0 Mon Sep 17 00:00:00 2001 From: Alec Mouri Date: Fri, 25 Oct 2019 16:18:16 -0700 Subject: [HWUI] Use ANativeWindow_get{Height, Width} gui/Surface will eventually be removed since HWUI needs to depend on a stable ABI, so use the NDK methods for querying width and height that operates on ANativeWindow instead. Bug: 137012798 Test: builds Change-Id: I1309e31992190e7b44c6ea83f962f372b6b0afcf --- libs/hwui/renderthread/CanvasContext.cpp | 4 ++-- libs/hwui/tests/unit/SkiaDisplayListTests.cpp | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'libs') diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 93fd0c87a361..294a6b8baceb 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -565,8 +565,8 @@ SkISize CanvasContext::getNextFrameSize() const { ReliableSurface* surface = mNativeSurface.get(); if (surface) { SkISize size; - surface->query(NATIVE_WINDOW_WIDTH, &size.fWidth); - surface->query(NATIVE_WINDOW_HEIGHT, &size.fHeight); + size.fWidth = ANativeWindow_getWidth(surface); + size.fHeight = ANativeWindow_getHeight(surface); return size; } return {INT32_MAX, INT32_MAX}; diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp index 6fb164a99ae4..7d999c43b560 100644 --- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp +++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp @@ -208,9 +208,8 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren_vdOffscr test::TestContext testContext; testContext.setRenderOffscreen(true); auto surface = testContext.surface(); - int width, height; - surface->query(NATIVE_WINDOW_WIDTH, &width); - surface->query(NATIVE_WINDOW_HEIGHT, &height); + int width = ANativeWindow_getWidth(surface.get()); + int height = ANativeWindow_getHeight(surface.get()); canvasContext->setSurface(std::move(surface)); TreeInfo info(TreeInfo::MODE_FULL, *canvasContext.get()); -- cgit v1.2.3 From 2afc3b0a0052ce39a66fab3e4dfbd105a867804b Mon Sep 17 00:00:00 2001 From: John Reck Date: Mon, 28 Oct 2019 15:51:25 -0700 Subject: Remove a dead option Test: builds & text still works Change-Id: Ia9c678f258834d7cc5e65027db8be4c37fb6b4db --- libs/hwui/SkiaCanvas.h | 1 - libs/hwui/hwui/Canvas.cpp | 19 ++++--------------- libs/hwui/hwui/Canvas.h | 9 --------- 3 files changed, 4 insertions(+), 25 deletions(-) (limited to 'libs') diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h index ec83a1961eff..1eb089d8764c 100644 --- a/libs/hwui/SkiaCanvas.h +++ b/libs/hwui/SkiaCanvas.h @@ -136,7 +136,6 @@ public: const Paint* paint) override; virtual double drawAnimatedImage(AnimatedImageDrawable* imgDrawable) override; - virtual bool drawTextAbsolutePos() const override { return true; } virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override; virtual void drawRoundRect(uirenderer::CanvasPropertyPrimitive* left, diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp index b98ffca84dfc..c138a32eacc2 100644 --- a/libs/hwui/hwui/Canvas.cpp +++ b/libs/hwui/hwui/Canvas.cpp @@ -95,18 +95,10 @@ public: void operator()(size_t start, size_t end) { auto glyphFunc = [&](uint16_t* text, float* positions) { - if (canvas->drawTextAbsolutePos()) { - for (size_t i = start, textIndex = 0, posIndex = 0; i < end; i++) { - text[textIndex++] = layout.getGlyphId(i); - positions[posIndex++] = x + layout.getX(i); - positions[posIndex++] = y + layout.getY(i); - } - } else { - for (size_t i = start, textIndex = 0, posIndex = 0; i < end; i++) { - text[textIndex++] = layout.getGlyphId(i); - positions[posIndex++] = layout.getX(i); - positions[posIndex++] = layout.getY(i); - } + for (size_t i = start, textIndex = 0, posIndex = 0; i < end; i++) { + text[textIndex++] = layout.getGlyphId(i); + positions[posIndex++] = x + layout.getX(i); + positions[posIndex++] = y + layout.getY(i); } }; @@ -166,9 +158,6 @@ void Canvas::drawText(const uint16_t* text, int textSize, int start, int count, minikin::MinikinRect bounds; layout.getBounds(&bounds); - if (!drawTextAbsolutePos()) { - bounds.offset(x, y); - } // Set align to left for drawing, as we don't want individual // glyphs centered or right-aligned; the offset above takes diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h index b90a4a3d68e6..27dfed305a94 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -252,15 +252,6 @@ public: virtual double drawAnimatedImage(AnimatedImageDrawable* imgDrawable) = 0; virtual void drawPicture(const SkPicture& picture) = 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; - /** * Draws a VectorDrawable onto the canvas. */ -- cgit v1.2.3 From 4145919ef9853145532430c1e2e39ea53baee189 Mon Sep 17 00:00:00 2001 From: John Reck Date: Thu, 31 Oct 2019 15:04:58 -0700 Subject: Fix wrong surfaceRequiresRedraw check We only need to redraw if the size /changed/ not if it was the same. Also fix damageId to not use frameNumber as repeated redraws of the same frame would toggle. Bug: 143711430 Test: systrace Change-Id: I8ac4629c9ff4fd51de33d1be7aa46ccc995ba342 --- libs/hwui/TreeInfo.cpp | 1 - libs/hwui/renderthread/CanvasContext.cpp | 3 ++- libs/hwui/renderthread/CanvasContext.h | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) (limited to 'libs') diff --git a/libs/hwui/TreeInfo.cpp b/libs/hwui/TreeInfo.cpp index dc53dd6c27c3..750f869e2551 100644 --- a/libs/hwui/TreeInfo.cpp +++ b/libs/hwui/TreeInfo.cpp @@ -24,7 +24,6 @@ TreeInfo::TreeInfo(TraversalMode mode, renderthread::CanvasContext& canvasContex : mode(mode) , prepareTextures(mode == MODE_FULL) , canvasContext(canvasContext) - , damageGenerationId(canvasContext.getFrameNumber()) , disableForceDark(canvasContext.useForceDark() ? 0 : 1) , screenSize(canvasContext.getNextFrameSize()) {} diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index e5c502cca09a..4ca26c273c95 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -303,6 +303,7 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy info.damageAccumulator = &mDamageAccumulator; info.layerUpdateQueue = &mLayerUpdateQueue; + info.damageGenerationId = mDamageId++; info.out.canDrawThisFrame = true; mAnimationContext->startFrame(info.mode); @@ -702,7 +703,7 @@ bool CanvasContext::surfaceRequiresRedraw() { surface->query(NATIVE_WINDOW_WIDTH, &width); surface->query(NATIVE_WINDOW_HEIGHT, &height); - return width == mLastFrameWidth && height == mLastFrameHeight; + return width != mLastFrameWidth || height != mLastFrameHeight; } void CanvasContext::setRenderAheadDepth(int renderAhead) { diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 6e3e43af8c6f..b192d461da9b 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -245,6 +245,7 @@ private: // Need at least 4 because we do quad buffer. Add a 5th for good measure. RingBuffer mSwapHistory; int64_t mFrameNumber = -1; + int64_t mDamageId = 0; // last vsync for a dropped frame due to stuffed queue nsecs_t mLastDropVsync = 0; -- cgit v1.2.3 From b451d8746869a05b97b939ee380daee8b28a2436 Mon Sep 17 00:00:00 2001 From: Nick Desaulniers Date: Mon, 4 Nov 2019 17:18:51 -0800 Subject: [frameworks][base][hwui] re-fix -Wimplicit-int-float-conversion A new instance snuck in since I haven't completed the platform toolchain upgrade yet. There was imprecision in this call; accept it by making the implicit cast explicit. Bug: 139945549 Test: mm Change-Id: Id704d9741b480eba4cf3955e70119d935776faf6 Signed-off-by: Nick Desaulniers --- libs/hwui/renderthread/CanvasContext.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 4ca26c273c95..15e0c8d0a266 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -479,7 +479,8 @@ void CanvasContext::draw() { if (didDraw) { swap.damage = windowDirty; } else { - swap.damage = SkRect::MakeWH(INT_MAX, INT_MAX); + float max = static_cast(INT_MAX); + swap.damage = SkRect::MakeWH(max, max); } swap.swapCompletedTime = systemTime(SYSTEM_TIME_MONOTONIC); swap.vsyncTime = mRenderThread.timeLord().latestVsync(); -- cgit v1.2.3 From aaa9e834d443a56671eccbe97c755c253fa94afe Mon Sep 17 00:00:00 2001 From: Stan Iliev Date: Tue, 17 Sep 2019 14:07:23 -0400 Subject: Decouple SurfaceTexture from HWUI Remove all Skia and HWUI types from SurfaceTexture implementation. Move SurfaceTexture to libgui (ag/9578265). Define private C++ API for SurfaceTexture, which is consumed by DeferredLayerUpdater. Move AutoBackendTextureRelease/Skia code from SurfaceTexture to HWUI. Test: pass CtsUiRenderingTestCases and CtsViewTestCases Bug: 136263580 Change-Id: I3f971bb490f64a3ac0b2a66a89ba935bf7f08213 --- libs/hwui/Android.bp | 4 +- libs/hwui/AutoBackendTextureRelease.cpp | 91 ++++ libs/hwui/AutoBackendTextureRelease.h | 67 +++ libs/hwui/DeferredLayerUpdater.cpp | 133 ++++-- libs/hwui/DeferredLayerUpdater.h | 50 ++- libs/hwui/renderthread/EglManager.cpp | 44 +- libs/hwui/renderthread/EglManager.h | 6 +- libs/hwui/renderthread/RenderThread.h | 23 +- libs/hwui/renderthread/VulkanManager.cpp | 46 +- libs/hwui/renderthread/VulkanManager.h | 9 +- libs/hwui/surfacetexture/EGLConsumer.cpp | 675 ---------------------------- libs/hwui/surfacetexture/EGLConsumer.h | 311 ------------- libs/hwui/surfacetexture/ImageConsumer.cpp | 299 ------------ libs/hwui/surfacetexture/ImageConsumer.h | 115 ----- libs/hwui/surfacetexture/SurfaceTexture.cpp | 499 -------------------- libs/hwui/surfacetexture/SurfaceTexture.h | 452 ------------------- libs/hwui/tests/common/TestUtils.h | 1 + 17 files changed, 380 insertions(+), 2445 deletions(-) create mode 100644 libs/hwui/AutoBackendTextureRelease.cpp create mode 100644 libs/hwui/AutoBackendTextureRelease.h delete mode 100644 libs/hwui/surfacetexture/EGLConsumer.cpp delete mode 100644 libs/hwui/surfacetexture/EGLConsumer.h delete mode 100644 libs/hwui/surfacetexture/ImageConsumer.cpp delete mode 100644 libs/hwui/surfacetexture/ImageConsumer.h delete mode 100644 libs/hwui/surfacetexture/SurfaceTexture.cpp delete mode 100644 libs/hwui/surfacetexture/SurfaceTexture.h (limited to 'libs') diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index ecfaec22de59..f670cf97e0c2 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -235,12 +235,10 @@ cc_defaults { "renderthread/RenderProxy.cpp", "renderthread/RenderThread.cpp", "service/GraphicsStatsService.cpp", - "surfacetexture/EGLConsumer.cpp", - "surfacetexture/ImageConsumer.cpp", - "surfacetexture/SurfaceTexture.cpp", "thread/CommonPool.cpp", "utils/GLUtils.cpp", "utils/StringUtils.cpp", + "AutoBackendTextureRelease.cpp", "DeferredLayerUpdater.cpp", "DeviceInfo.cpp", "FrameInfo.cpp", diff --git a/libs/hwui/AutoBackendTextureRelease.cpp b/libs/hwui/AutoBackendTextureRelease.cpp new file mode 100644 index 000000000000..72747e8fa543 --- /dev/null +++ b/libs/hwui/AutoBackendTextureRelease.cpp @@ -0,0 +1,91 @@ +/* + * Copyright 2019 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 "AutoBackendTextureRelease.h" + +#include "renderthread/RenderThread.h" +#include "utils/Color.h" +#include "utils/PaintUtils.h" + +using namespace android::uirenderer::renderthread; + +namespace android { +namespace uirenderer { + +AutoBackendTextureRelease::AutoBackendTextureRelease(GrContext* context, AHardwareBuffer* buffer) { + AHardwareBuffer_Desc desc; + AHardwareBuffer_describe(buffer, &desc); + bool createProtectedImage = 0 != (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT); + GrBackendFormat backendFormat = + GrAHardwareBufferUtils::GetBackendFormat(context, buffer, desc.format, false); + mBackendTexture = GrAHardwareBufferUtils::MakeBackendTexture( + context, buffer, desc.width, desc.height, &mDeleteProc, &mUpdateProc, &mImageCtx, + createProtectedImage, backendFormat, false); +} + +void AutoBackendTextureRelease::unref(bool releaseImage) { + if (!RenderThread::isCurrent()) { + // EGLImage needs to be destroyed on RenderThread to prevent memory leak. + // ~SkImage dtor for both pipelines needs to be invoked on RenderThread, because it is not + // thread safe. + RenderThread::getInstance().queue().post([this, releaseImage]() { unref(releaseImage); }); + return; + } + + if (releaseImage) { + mImage.reset(); + } + + mUsageCount--; + if (mUsageCount <= 0) { + if (mBackendTexture.isValid()) { + mDeleteProc(mImageCtx); + mBackendTexture = {}; + } + delete this; + } +} + +// releaseProc is invoked by SkImage, when texture is no longer in use. +// "releaseContext" contains an "AutoBackendTextureRelease*". +static void releaseProc(SkImage::ReleaseContext releaseContext) { + AutoBackendTextureRelease* textureRelease = + reinterpret_cast(releaseContext); + textureRelease->unref(false); +} + +void AutoBackendTextureRelease::makeImage(AHardwareBuffer* buffer, android_dataspace dataspace, + GrContext* context) { + AHardwareBuffer_Desc desc; + AHardwareBuffer_describe(buffer, &desc); + SkColorType colorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(desc.format); + mImage = SkImage::MakeFromTexture( + context, mBackendTexture, kTopLeft_GrSurfaceOrigin, colorType, kPremul_SkAlphaType, + uirenderer::DataSpaceToColorSpace(dataspace), releaseProc, this); + if (mImage.get()) { + // The following ref will be counteracted by releaseProc, when SkImage is discarded. + ref(); + } +} + +void AutoBackendTextureRelease::newBufferContent(GrContext* context) { + if (mBackendTexture.isValid()) { + mUpdateProc(mImageCtx, context); + } +} + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/AutoBackendTextureRelease.h b/libs/hwui/AutoBackendTextureRelease.h new file mode 100644 index 000000000000..acdd63cb7921 --- /dev/null +++ b/libs/hwui/AutoBackendTextureRelease.h @@ -0,0 +1,67 @@ +/* + * Copyright 2019 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. + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace android { +namespace uirenderer { + +/** + * AutoBackendTextureRelease manages EglImage/VkImage lifetime. It is a ref-counted object + * that keeps GPU resources alive until the last SkImage object using them is destroyed. + */ +class AutoBackendTextureRelease final { +public: + AutoBackendTextureRelease(GrContext* context, AHardwareBuffer* buffer); + + const GrBackendTexture& getTexture() const { return mBackendTexture; } + + // Only called on the RenderThread, so it need not be thread-safe. + void ref() { mUsageCount++; } + + void unref(bool releaseImage); + + inline sk_sp getImage() const { return mImage; } + + void makeImage(AHardwareBuffer* buffer, android_dataspace dataspace, GrContext* context); + + void newBufferContent(GrContext* context); + +private: + // The only way to invoke dtor is with unref, when mUsageCount is 0. + ~AutoBackendTextureRelease() {} + + GrBackendTexture mBackendTexture; + GrAHardwareBufferUtils::DeleteImageProc mDeleteProc; + GrAHardwareBufferUtils::UpdateImageProc mUpdateProc; + GrAHardwareBufferUtils::TexImageCtx mImageCtx; + + // Starting with refcount 1, because the first ref is held by SurfaceTexture. Additional refs + // are held by SkImages. + int mUsageCount = 1; + + // mImage is the SkImage created from mBackendTexture. + sk_sp mImage; +}; + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp index f300703e7e00..d6b516fd5cf4 100644 --- a/libs/hwui/DeferredLayerUpdater.cpp +++ b/libs/hwui/DeferredLayerUpdater.cpp @@ -18,8 +18,15 @@ #include #include +#include "AutoBackendTextureRelease.h" +#include "Matrix.h" +#include "Properties.h" #include "renderstate/RenderState.h" -#include "utils/PaintUtils.h" +#include "renderthread/EglManager.h" +#include "renderthread/RenderThread.h" +#include "renderthread/VulkanManager.h" + +using namespace android::uirenderer::renderthread; namespace android { namespace uirenderer { @@ -27,7 +34,6 @@ namespace uirenderer { DeferredLayerUpdater::DeferredLayerUpdater(RenderState& renderState) : mRenderState(renderState) , mBlend(false) - , mSurfaceTexture(nullptr) , mTransform(nullptr) , mGLContextAttached(false) , mUpdateTexImage(false) @@ -41,14 +47,12 @@ DeferredLayerUpdater::~DeferredLayerUpdater() { destroyLayer(); } -void DeferredLayerUpdater::setSurfaceTexture(const sp& consumer) { - if (consumer.get() != mSurfaceTexture.get()) { - mSurfaceTexture = consumer; +void DeferredLayerUpdater::setSurfaceTexture(AutoTextureRelease&& consumer) { + mSurfaceTexture = std::move(consumer); - GLenum target = consumer->getCurrentTextureTarget(); - LOG_ALWAYS_FATAL_IF(target != GL_TEXTURE_2D && target != GL_TEXTURE_EXTERNAL_OES, - "set unsupported SurfaceTexture with target %x", target); - } + GLenum target = ASurfaceTexture_getCurrentTextureTarget(mSurfaceTexture.get()); + LOG_ALWAYS_FATAL_IF(target != GL_TEXTURE_2D && target != GL_TEXTURE_EXTERNAL_OES, + "set unsupported SurfaceTexture with target %x", target); } void DeferredLayerUpdater::onContextDestroyed() { @@ -61,13 +65,15 @@ void DeferredLayerUpdater::destroyLayer() { } if (mSurfaceTexture.get() && mGLContextAttached) { - mSurfaceTexture->detachFromView(); + ASurfaceTexture_releaseConsumerOwnership(mSurfaceTexture.get()); mGLContextAttached = false; } mLayer->postDecStrong(); mLayer = nullptr; + + mImageSlots.clear(); } void DeferredLayerUpdater::setPaint(const SkPaint* paint) { @@ -80,6 +86,35 @@ void DeferredLayerUpdater::setPaint(const SkPaint* paint) { } } +static status_t createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, EGLDisplay* display, + int* releaseFence, void* handle) { + *display = EGL_NO_DISPLAY; + RenderState* renderState = (RenderState*)handle; + status_t err; + if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) { + EglManager& eglManager = renderState->getRenderThread().eglManager(); + *display = eglManager.eglDisplay(); + err = eglManager.createReleaseFence(useFenceSync, eglFence, releaseFence); + } else { + err = renderState->getRenderThread().vulkanManager().createReleaseFence( + releaseFence, renderState->getRenderThread().getGrContext()); + } + return err; +} + +static status_t fenceWait(int fence, void* handle) { + // Wait on the producer fence for the buffer to be ready. + status_t err; + RenderState* renderState = (RenderState*)handle; + if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) { + err = renderState->getRenderThread().eglManager().fenceWait(fence); + } else { + err = renderState->getRenderThread().vulkanManager().fenceWait( + fence, renderState->getRenderThread().getGrContext()); + } + return err; +} + void DeferredLayerUpdater::apply() { if (!mLayer) { mLayer = new Layer(mRenderState, mColorFilter, mAlpha, mMode); @@ -92,24 +127,33 @@ void DeferredLayerUpdater::apply() { if (!mGLContextAttached) { mGLContextAttached = true; mUpdateTexImage = true; - mSurfaceTexture->attachToView(); + ASurfaceTexture_takeConsumerOwnership(mSurfaceTexture.get()); } if (mUpdateTexImage) { mUpdateTexImage = false; - sk_sp layerImage; - SkMatrix textureTransform; - bool queueEmpty = true; - // If the SurfaceTexture queue is in synchronous mode, need to discard all - // but latest frame. Since we can't tell which mode it is in, - // do this unconditionally. - do { - layerImage = mSurfaceTexture->dequeueImage(textureTransform, &queueEmpty, - mRenderState); - } while (layerImage.get() && (!queueEmpty)); - if (layerImage.get()) { - // force filtration if buffer size != layer size - bool forceFilter = mWidth != layerImage->width() || mHeight != layerImage->height(); - updateLayer(forceFilter, textureTransform, layerImage); + float transformMatrix[16]; + android_dataspace dataspace; + int slot; + bool newContent = false; + // Note: ASurfaceTexture_dequeueBuffer discards all but the last frame. This + // is necessary if the SurfaceTexture queue is in synchronous mode, and we + // cannot tell which mode it is in. + AHardwareBuffer* hardwareBuffer = ASurfaceTexture_dequeueBuffer( + mSurfaceTexture.get(), &slot, &dataspace, transformMatrix, &newContent, + createReleaseFence, fenceWait, &mRenderState); + + if (hardwareBuffer) { + sk_sp layerImage = mImageSlots[slot].createIfNeeded( + hardwareBuffer, dataspace, newContent, + mRenderState.getRenderThread().getGrContext()); + if (layerImage.get()) { + SkMatrix textureTransform; + mat4(transformMatrix).copyTo(textureTransform); + // force filtration if buffer size != layer size + bool forceFilter = + mWidth != layerImage->width() || mHeight != layerImage->height(); + updateLayer(forceFilter, textureTransform, layerImage); + } } } @@ -121,7 +165,7 @@ void DeferredLayerUpdater::apply() { } void DeferredLayerUpdater::updateLayer(bool forceFilter, const SkMatrix& textureTransform, - const sk_sp& layerImage) { + const sk_sp& layerImage) { mLayer->setBlend(mBlend); mLayer->setForceFilter(forceFilter); mLayer->setSize(mWidth, mHeight); @@ -136,5 +180,42 @@ void DeferredLayerUpdater::detachSurfaceTexture() { } } +sk_sp DeferredLayerUpdater::ImageSlot::createIfNeeded(AHardwareBuffer* buffer, + android_dataspace dataspace, + bool forceCreate, + GrContext* context) { + if (!mTextureRelease || !mTextureRelease->getImage().get() || dataspace != mDataspace || + forceCreate || mBuffer != buffer) { + if (buffer != mBuffer) { + clear(); + } + + if (!buffer) { + return nullptr; + } + + if (!mTextureRelease) { + mTextureRelease = new AutoBackendTextureRelease(context, buffer); + } else { + mTextureRelease->newBufferContent(context); + } + + mDataspace = dataspace; + mBuffer = buffer; + mTextureRelease->makeImage(buffer, dataspace, context); + } + return mTextureRelease ? mTextureRelease->getImage() : nullptr; +} + +void DeferredLayerUpdater::ImageSlot::clear() { + if (mTextureRelease) { + // The following unref counteracts the initial mUsageCount of 1, set by default initializer. + mTextureRelease->unref(true); + mTextureRelease = nullptr; + } + + mBuffer = nullptr; +} + } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h index 1491f99402ba..289f65c22ad3 100644 --- a/libs/hwui/DeferredLayerUpdater.h +++ b/libs/hwui/DeferredLayerUpdater.h @@ -19,21 +19,26 @@ #include #include #include +#include #include +// TODO: Use public SurfaceTexture APIs once available and include public NDK header file instead. +#include + #include -#include -#include +#include -#include "renderstate/RenderState.h" -#include "surfacetexture/SurfaceTexture.h" #include "Layer.h" #include "Rect.h" +#include "renderstate/RenderState.h" namespace android { namespace uirenderer { +class AutoBackendTextureRelease; class RenderState; +typedef std::unique_ptr AutoTextureRelease; + // Container to hold the properties a layer should be set to at the start // of a render pass class DeferredLayerUpdater : public VirtualLightRefBase, public IGpuContextCallback { @@ -64,7 +69,7 @@ public: return false; } - ANDROID_API void setSurfaceTexture(const sp& consumer); + ANDROID_API void setSurfaceTexture(AutoTextureRelease&& consumer); ANDROID_API void updateTexImage() { mUpdateTexImage = true; } @@ -92,6 +97,39 @@ protected: void onContextDestroyed() override; private: + /** + * ImageSlot contains the information and object references that + * DeferredLayerUpdater maintains about a slot. Slot id comes from + * ASurfaceTexture_dequeueBuffer. Usually there are at most 3 slots active at a time. + */ + class ImageSlot { + public: + ~ImageSlot() { clear(); } + + sk_sp createIfNeeded(AHardwareBuffer* buffer, android_dataspace dataspace, + bool forceCreate, GrContext* context); + + private: + void clear(); + + // the dataspace associated with the current image + android_dataspace mDataspace = HAL_DATASPACE_UNKNOWN; + + AHardwareBuffer* mBuffer = nullptr; + + /** + * mTextureRelease may outlive DeferredLayerUpdater, if the last ref is held by an SkImage. + * DeferredLayerUpdater holds one ref to mTextureRelease, which is decremented by "clear". + */ + AutoBackendTextureRelease* mTextureRelease = nullptr; + }; + + /** + * DeferredLayerUpdater stores the SkImages that have been allocated by the BufferQueue + * for each buffer slot. + */ + std::map mImageSlots; + RenderState& mRenderState; // Generic properties @@ -101,7 +139,7 @@ private: sk_sp mColorFilter; int mAlpha = 255; SkBlendMode mMode = SkBlendMode::kSrcOver; - sp mSurfaceTexture; + AutoTextureRelease mSurfaceTexture; SkMatrix* mTransform; bool mGLContextAttached; bool mUpdateTexImage; diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index 12021641518c..eb469a810358 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -16,23 +16,23 @@ #include "EglManager.h" +#include +#include #include #include #include +#include +#include #include -#include "utils/Color.h" -#include "utils/StringUtils.h" - -#include "Frame.h" -#include "Properties.h" - -#include -#include -#include #include #include +#include "Frame.h" +#include "Properties.h" +#include "utils/Color.h" +#include "utils/StringUtils.h" + #define GLES_VERSION 2 // Android-specific addition that is used to show when frames began in systrace @@ -508,7 +508,21 @@ bool EglManager::setPreserveBuffer(EGLSurface surface, bool preserve) { return preserved; } -status_t EglManager::fenceWait(sp& fence) { +static status_t waitForeverOnFence(int fence, const char* logname) { + ATRACE_CALL(); + if (fence == -1) { + return NO_ERROR; + } + constexpr int warningTimeout = 3000; + int err = sync_wait(fence, warningTimeout); + if (err < 0 && errno == ETIME) { + ALOGE("%s: fence %d didn't signal in %d ms", logname, fence, warningTimeout); + err = sync_wait(fence, -1); + } + return err < 0 ? -errno : status_t(NO_ERROR); +} + +status_t EglManager::fenceWait(int fence) { if (!hasEglContext()) { ALOGE("EglManager::fenceWait: EGLDisplay not initialized"); return INVALID_OPERATION; @@ -518,7 +532,7 @@ status_t EglManager::fenceWait(sp& fence) { SyncFeatures::getInstance().useNativeFenceSync()) { // Block GPU on the fence. // Create an EGLSyncKHR from the current fence. - int fenceFd = fence->dup(); + int fenceFd = ::dup(fence); if (fenceFd == -1) { ALOGE("EglManager::fenceWait: error dup'ing fence fd: %d", errno); return -errno; @@ -543,7 +557,7 @@ status_t EglManager::fenceWait(sp& fence) { } } else { // Block CPU on the fence. - status_t err = fence->waitForever("EglManager::fenceWait"); + status_t err = waitForeverOnFence(fence, "EglManager::fenceWait"); if (err != NO_ERROR) { ALOGE("EglManager::fenceWait: error waiting for fence: %d", err); return err; @@ -552,8 +566,8 @@ status_t EglManager::fenceWait(sp& fence) { return OK; } -status_t EglManager::createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, - sp& nativeFence) { +status_t EglManager::createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, int* nativeFence) { + *nativeFence = -1; if (!hasEglContext()) { ALOGE("EglManager::createReleaseFence: EGLDisplay not initialized"); return INVALID_OPERATION; @@ -574,7 +588,7 @@ status_t EglManager::createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, eglGetError()); return UNKNOWN_ERROR; } - nativeFence = new Fence(fenceFd); + *nativeFence = fenceFd; *eglFence = EGL_NO_SYNC_KHR; } else if (useFenceSync && SyncFeatures::getInstance().useFenceSync()) { if (*eglFence != EGL_NO_SYNC_KHR) { diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h index 27d41d26a73a..a893e245b214 100644 --- a/libs/hwui/renderthread/EglManager.h +++ b/libs/hwui/renderthread/EglManager.h @@ -21,9 +21,9 @@ #include #include #include -#include #include #include + #include "IRenderPipeline.h" #include "utils/Result.h" @@ -74,11 +74,11 @@ public: // Inserts a wait on fence command into the OpenGL ES command stream. If EGL extension // support is missing, block the CPU on the fence. - status_t fenceWait(sp& fence); + status_t fenceWait(int fence); // Creates a fence that is signaled, when all the pending GL commands are flushed. // Depending on installed extensions, the result is either Android native fence or EGL fence. - status_t createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, sp& nativeFence); + status_t createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, int* nativeFence); private: enum class SwapBehavior { diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index bdd80721c4f3..da79e97a6ceb 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -17,33 +17,32 @@ #ifndef RENDERTHREAD_H_ #define RENDERTHREAD_H_ -#include "RenderTask.h" - -#include "CacheManager.h" -#include "ProfileDataContainer.h" -#include "TimeLord.h" -#include "WebViewFunctorManager.h" -#include "thread/ThreadBase.h" -#include "utils/TimeUtils.h" - #include #include #include +#include #include #include -#include #include #include #include +#include "CacheManager.h" +#include "ProfileDataContainer.h" +#include "RenderTask.h" +#include "TimeLord.h" +#include "WebViewFunctorManager.h" +#include "thread/ThreadBase.h" +#include "utils/TimeUtils.h" + namespace android { class Bitmap; -class AutoBackendTextureRelease; namespace uirenderer { +class AutoBackendTextureRelease; class Readback; class RenderState; class TestUtils; @@ -137,7 +136,7 @@ private: friend class DispatchFrameCallbacks; friend class RenderProxy; friend class DummyVsyncSource; - friend class android::AutoBackendTextureRelease; + friend class android::uirenderer::AutoBackendTextureRelease; friend class android::uirenderer::TestUtils; friend class android::uirenderer::WebViewFunctor; friend class android::uirenderer::skiapipeline::VkFunctorDrawHandler; diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index 35abc57fbe57..a5355fc3499d 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -16,23 +16,22 @@ #include "VulkanManager.h" -#include #include #include - -#include "Properties.h" -#include "RenderThread.h" -#include "renderstate/RenderState.h" -#include "utils/FatVector.h" -#include "utils/TraceUtils.h" - #include #include #include #include +#include #include #include +#include "Properties.h" +#include "RenderThread.h" +#include "renderstate/RenderState.h" +#include "utils/FatVector.h" +#include "utils/TraceUtils.h" + namespace android { namespace uirenderer { namespace renderthread { @@ -482,7 +481,7 @@ struct DestroySemaphoreInfo { int mRefs = 2; DestroySemaphoreInfo(PFN_vkDestroySemaphore destroyFunction, VkDevice device, - VkSemaphore semaphore) + VkSemaphore semaphore) : mDestroyFunction(destroyFunction), mDevice(device), mSemaphore(semaphore) {} }; @@ -524,12 +523,11 @@ void VulkanManager::swapBuffers(VulkanSurface* surface, const SkRect& dirtyRect) backendSemaphore.initVulkan(semaphore); int fenceFd = -1; - DestroySemaphoreInfo* destroyInfo = new DestroySemaphoreInfo(mDestroySemaphore, mDevice, - semaphore); - GrSemaphoresSubmitted submitted = - bufferInfo->skSurface->flush(SkSurface::BackendSurfaceAccess::kPresent, - kNone_GrFlushFlags, 1, &backendSemaphore, - destroy_semaphore, destroyInfo); + DestroySemaphoreInfo* destroyInfo = + new DestroySemaphoreInfo(mDestroySemaphore, mDevice, semaphore); + GrSemaphoresSubmitted submitted = bufferInfo->skSurface->flush( + SkSurface::BackendSurfaceAccess::kPresent, kNone_GrFlushFlags, 1, &backendSemaphore, + destroy_semaphore, destroyInfo); if (submitted == GrSemaphoresSubmitted::kYes) { VkSemaphoreGetFdInfoKHR getFdInfo; getFdInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR; @@ -571,14 +569,14 @@ VulkanSurface* VulkanManager::createSurface(ANativeWindow* window, ColorMode col *this, extraBuffers); } -status_t VulkanManager::fenceWait(sp& fence, GrContext* grContext) { +status_t VulkanManager::fenceWait(int fence, GrContext* grContext) { if (!hasVkContext()) { ALOGE("VulkanManager::fenceWait: VkDevice not initialized"); return INVALID_OPERATION; } // Block GPU on the fence. - int fenceFd = fence->dup(); + int fenceFd = ::dup(fence); if (fenceFd == -1) { ALOGE("VulkanManager::fenceWait: error dup'ing fence fd: %d", errno); return -errno; @@ -619,7 +617,8 @@ status_t VulkanManager::fenceWait(sp& fence, GrContext* grContext) { return OK; } -status_t VulkanManager::createReleaseFence(sp& nativeFence, GrContext* grContext) { +status_t VulkanManager::createReleaseFence(int* nativeFence, GrContext* grContext) { + *nativeFence = -1; if (!hasVkContext()) { ALOGE("VulkanManager::createReleaseFence: VkDevice not initialized"); return INVALID_OPERATION; @@ -644,14 +643,13 @@ status_t VulkanManager::createReleaseFence(sp& nativeFence, GrContext* gr GrBackendSemaphore backendSemaphore; backendSemaphore.initVulkan(semaphore); - DestroySemaphoreInfo* destroyInfo = new DestroySemaphoreInfo(mDestroySemaphore, mDevice, - semaphore); + DestroySemaphoreInfo* destroyInfo = + new DestroySemaphoreInfo(mDestroySemaphore, mDevice, semaphore); // Even if Skia fails to submit the semaphore, it will still call the destroy_semaphore callback // which will remove its ref to the semaphore. The VulkanManager must still release its ref, // when it is done with the semaphore. - GrSemaphoresSubmitted submitted = - grContext->flush(kNone_GrFlushFlags, 1, &backendSemaphore, - destroy_semaphore, destroyInfo); + GrSemaphoresSubmitted submitted = grContext->flush(kNone_GrFlushFlags, 1, &backendSemaphore, + destroy_semaphore, destroyInfo); if (submitted == GrSemaphoresSubmitted::kNo) { ALOGE("VulkanManager::createReleaseFence: Failed to submit semaphore"); @@ -673,7 +671,7 @@ status_t VulkanManager::createReleaseFence(sp& nativeFence, GrContext* gr ALOGE("VulkanManager::createReleaseFence: Failed to get semaphore Fd"); return INVALID_OPERATION; } - nativeFence = new Fence(fenceFd); + *nativeFence = fenceFd; return OK; } diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h index 4c6a75504cd0..8b19f13fdfb9 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -20,14 +20,13 @@ #if !defined(VK_USE_PLATFORM_ANDROID_KHR) #define VK_USE_PLATFORM_ANDROID_KHR #endif -#include - #include #include -#include #include #include #include +#include + #include "Frame.h" #include "IRenderPipeline.h" #include "VulkanSurface.h" @@ -71,11 +70,11 @@ public: void destroy(); // Inserts a wait on fence command into the Vulkan command buffer. - status_t fenceWait(sp& fence, GrContext* grContext); + status_t fenceWait(int fence, GrContext* grContext); // Creates a fence that is signaled when all the pending Vulkan commands are finished on the // GPU. - status_t createReleaseFence(sp& nativeFence, GrContext* grContext); + status_t createReleaseFence(int* nativeFence, GrContext* grContext); // Returned pointers are owned by VulkanManager. // An instance of VkFunctorInitParams returned from getVkFunctorInitParams refers to diff --git a/libs/hwui/surfacetexture/EGLConsumer.cpp b/libs/hwui/surfacetexture/EGLConsumer.cpp deleted file mode 100644 index 85b3917809fa..000000000000 --- a/libs/hwui/surfacetexture/EGLConsumer.cpp +++ /dev/null @@ -1,675 +0,0 @@ -/* - * Copyright (C) 2018 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 - -#include -#include -#include -#include -#include -#include -#include -#include -#include "EGLConsumer.h" -#include "SurfaceTexture.h" - -#include -#include -#include - -#define PROT_CONTENT_EXT_STR "EGL_EXT_protected_content" -#define EGL_PROTECTED_CONTENT_EXT 0x32C0 - -namespace android { - -// Macros for including the SurfaceTexture name in log messages -#define EGC_LOGV(x, ...) ALOGV("[%s] " x, st.mName.string(), ##__VA_ARGS__) -#define EGC_LOGD(x, ...) ALOGD("[%s] " x, st.mName.string(), ##__VA_ARGS__) -#define EGC_LOGW(x, ...) ALOGW("[%s] " x, st.mName.string(), ##__VA_ARGS__) -#define EGC_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__) - -static const struct { - uint32_t width, height; - char const* bits; -} kDebugData = {15, 12, - "_______________" - "_______________" - "_____XX_XX_____" - "__X_X_____X_X__" - "__X_XXXXXXX_X__" - "__XXXXXXXXXXX__" - "___XX_XXX_XX___" - "____XXXXXXX____" - "_____X___X_____" - "____X_____X____" - "_______________" - "_______________"}; - -Mutex EGLConsumer::sStaticInitLock; -sp EGLConsumer::sReleasedTexImageBuffer; - -static bool hasEglProtectedContentImpl() { - EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); - const char* exts = eglQueryString(dpy, EGL_EXTENSIONS); - size_t cropExtLen = strlen(PROT_CONTENT_EXT_STR); - size_t extsLen = strlen(exts); - bool equal = !strcmp(PROT_CONTENT_EXT_STR, exts); - bool atStart = !strncmp(PROT_CONTENT_EXT_STR " ", exts, cropExtLen + 1); - bool atEnd = (cropExtLen + 1) < extsLen && - !strcmp(" " PROT_CONTENT_EXT_STR, exts + extsLen - (cropExtLen + 1)); - bool inMiddle = strstr(exts, " " PROT_CONTENT_EXT_STR " "); - return equal || atStart || atEnd || inMiddle; -} - -static bool hasEglProtectedContent() { - // Only compute whether the extension is present once the first time this - // function is called. - static bool hasIt = hasEglProtectedContentImpl(); - return hasIt; -} - -EGLConsumer::EGLConsumer() : mEglDisplay(EGL_NO_DISPLAY), mEglContext(EGL_NO_CONTEXT) {} - -status_t EGLConsumer::updateTexImage(SurfaceTexture& st) { - // Make sure the EGL state is the same as in previous calls. - status_t err = checkAndUpdateEglStateLocked(st); - if (err != NO_ERROR) { - return err; - } - - BufferItem item; - - // Acquire the next buffer. - // In asynchronous mode the list is guaranteed to be one buffer - // deep, while in synchronous mode we use the oldest buffer. - err = st.acquireBufferLocked(&item, 0); - if (err != NO_ERROR) { - if (err == BufferQueue::NO_BUFFER_AVAILABLE) { - // We always bind the texture even if we don't update its contents. - EGC_LOGV("updateTexImage: no buffers were available"); - glBindTexture(st.mTexTarget, st.mTexName); - err = NO_ERROR; - } else { - EGC_LOGE("updateTexImage: acquire failed: %s (%d)", strerror(-err), err); - } - return err; - } - - // Release the previous buffer. - err = updateAndReleaseLocked(item, nullptr, st); - if (err != NO_ERROR) { - // We always bind the texture. - glBindTexture(st.mTexTarget, st.mTexName); - return err; - } - - // Bind the new buffer to the GL texture, and wait until it's ready. - return bindTextureImageLocked(st); -} - -status_t EGLConsumer::releaseTexImage(SurfaceTexture& st) { - // Make sure the EGL state is the same as in previous calls. - status_t err = NO_ERROR; - - // if we're detached, no need to validate EGL's state -- we won't use it. - if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) { - err = checkAndUpdateEglStateLocked(st, true); - if (err != NO_ERROR) { - return err; - } - } - - // Update the EGLConsumer state. - int buf = st.mCurrentTexture; - if (buf != BufferQueue::INVALID_BUFFER_SLOT) { - EGC_LOGV("releaseTexImage: (slot=%d, mOpMode=%d)", buf, (int)st.mOpMode); - - // if we're detached, we just use the fence that was created in detachFromContext() - // so... basically, nothing more to do here. - if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) { - // Do whatever sync ops we need to do before releasing the slot. - err = syncForReleaseLocked(mEglDisplay, st); - if (err != NO_ERROR) { - EGC_LOGE("syncForReleaseLocked failed (slot=%d), err=%d", buf, err); - return err; - } - } - - err = st.releaseBufferLocked(buf, st.mSlots[buf].mGraphicBuffer, mEglDisplay, - EGL_NO_SYNC_KHR); - if (err < NO_ERROR) { - EGC_LOGE("releaseTexImage: failed to release buffer: %s (%d)", strerror(-err), err); - return err; - } - - if (mReleasedTexImage == nullptr) { - mReleasedTexImage = new EglImage(getDebugTexImageBuffer()); - } - - st.mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT; - mCurrentTextureImage = mReleasedTexImage; - st.mCurrentCrop.makeInvalid(); - st.mCurrentTransform = 0; - st.mCurrentTimestamp = 0; - st.mCurrentDataSpace = HAL_DATASPACE_UNKNOWN; - st.mCurrentFence = Fence::NO_FENCE; - st.mCurrentFenceTime = FenceTime::NO_FENCE; - - // detached, don't touch the texture (and we may not even have an - // EGLDisplay here. - if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) { - // This binds a dummy buffer (mReleasedTexImage). - status_t result = bindTextureImageLocked(st); - if (result != NO_ERROR) { - return result; - } - } - } - - return NO_ERROR; -} - -sp EGLConsumer::getDebugTexImageBuffer() { - Mutex::Autolock _l(sStaticInitLock); - if (CC_UNLIKELY(sReleasedTexImageBuffer == nullptr)) { - // The first time, create the debug texture in case the application - // continues to use it. - sp buffer = new GraphicBuffer( - kDebugData.width, kDebugData.height, PIXEL_FORMAT_RGBA_8888, - GraphicBuffer::USAGE_SW_WRITE_RARELY, "[EGLConsumer debug texture]"); - uint32_t* bits; - buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, reinterpret_cast(&bits)); - uint32_t stride = buffer->getStride(); - uint32_t height = buffer->getHeight(); - memset(bits, 0, stride * height * 4); - for (uint32_t y = 0; y < kDebugData.height; y++) { - for (uint32_t x = 0; x < kDebugData.width; x++) { - bits[x] = (kDebugData.bits[y + kDebugData.width + x] == 'X') ? 0xFF000000 - : 0xFFFFFFFF; - } - bits += stride; - } - buffer->unlock(); - sReleasedTexImageBuffer = buffer; - } - return sReleasedTexImageBuffer; -} - -void EGLConsumer::onAcquireBufferLocked(BufferItem* item, SurfaceTexture& st) { - // If item->mGraphicBuffer is not null, this buffer has not been acquired - // before, so any prior EglImage created is using a stale buffer. This - // replaces any old EglImage with a new one (using the new buffer). - int slot = item->mSlot; - if (item->mGraphicBuffer != nullptr || mEglSlots[slot].mEglImage.get() == nullptr) { - mEglSlots[slot].mEglImage = new EglImage(st.mSlots[slot].mGraphicBuffer); - } -} - -void EGLConsumer::onReleaseBufferLocked(int buf) { - mEglSlots[buf].mEglFence = EGL_NO_SYNC_KHR; -} - -status_t EGLConsumer::updateAndReleaseLocked(const BufferItem& item, PendingRelease* pendingRelease, - SurfaceTexture& st) { - status_t err = NO_ERROR; - - int slot = item.mSlot; - - if (st.mOpMode != SurfaceTexture::OpMode::attachedToGL) { - EGC_LOGE( - "updateAndRelease: EGLConsumer is not attached to an OpenGL " - "ES context"); - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); - return INVALID_OPERATION; - } - - // Confirm state. - err = checkAndUpdateEglStateLocked(st); - if (err != NO_ERROR) { - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); - return err; - } - - // Ensure we have a valid EglImageKHR for the slot, creating an EglImage - // if nessessary, for the gralloc buffer currently in the slot in - // ConsumerBase. - // We may have to do this even when item.mGraphicBuffer == NULL (which - // means the buffer was previously acquired). - err = mEglSlots[slot].mEglImage->createIfNeeded(mEglDisplay); - if (err != NO_ERROR) { - EGC_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d", mEglDisplay, - slot); - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); - return UNKNOWN_ERROR; - } - - // Do whatever sync ops we need to do before releasing the old slot. - if (slot != st.mCurrentTexture) { - err = syncForReleaseLocked(mEglDisplay, st); - if (err != NO_ERROR) { - // Release the buffer we just acquired. It's not safe to - // release the old buffer, so instead we just drop the new frame. - // As we are still under lock since acquireBuffer, it is safe to - // release by slot. - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, - EGL_NO_SYNC_KHR); - return err; - } - } - - EGC_LOGV( - "updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", st.mCurrentTexture, - mCurrentTextureImage != nullptr ? mCurrentTextureImage->graphicBufferHandle() : nullptr, - slot, st.mSlots[slot].mGraphicBuffer->handle); - - // Hang onto the pointer so that it isn't freed in the call to - // releaseBufferLocked() if we're in shared buffer mode and both buffers are - // the same. - sp nextTextureImage = mEglSlots[slot].mEglImage; - - // release old buffer - if (st.mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { - if (pendingRelease == nullptr) { - status_t status = st.releaseBufferLocked( - st.mCurrentTexture, mCurrentTextureImage->graphicBuffer(), mEglDisplay, - mEglSlots[st.mCurrentTexture].mEglFence); - if (status < NO_ERROR) { - EGC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status), - status); - err = status; - // keep going, with error raised [?] - } - } else { - pendingRelease->currentTexture = st.mCurrentTexture; - pendingRelease->graphicBuffer = mCurrentTextureImage->graphicBuffer(); - pendingRelease->display = mEglDisplay; - pendingRelease->fence = mEglSlots[st.mCurrentTexture].mEglFence; - pendingRelease->isPending = true; - } - } - - // Update the EGLConsumer state. - st.mCurrentTexture = slot; - mCurrentTextureImage = nextTextureImage; - st.mCurrentCrop = item.mCrop; - st.mCurrentTransform = item.mTransform; - st.mCurrentScalingMode = item.mScalingMode; - st.mCurrentTimestamp = item.mTimestamp; - st.mCurrentDataSpace = item.mDataSpace; - st.mCurrentFence = item.mFence; - st.mCurrentFenceTime = item.mFenceTime; - st.mCurrentFrameNumber = item.mFrameNumber; - - st.computeCurrentTransformMatrixLocked(); - - return err; -} - -status_t EGLConsumer::bindTextureImageLocked(SurfaceTexture& st) { - if (mEglDisplay == EGL_NO_DISPLAY) { - ALOGE("bindTextureImage: invalid display"); - return INVALID_OPERATION; - } - - GLenum error; - while ((error = glGetError()) != GL_NO_ERROR) { - EGC_LOGW("bindTextureImage: clearing GL error: %#04x", error); - } - - glBindTexture(st.mTexTarget, st.mTexName); - if (st.mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT && mCurrentTextureImage == nullptr) { - EGC_LOGE("bindTextureImage: no currently-bound texture"); - return NO_INIT; - } - - status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay); - if (err != NO_ERROR) { - EGC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay, - st.mCurrentTexture); - return UNKNOWN_ERROR; - } - mCurrentTextureImage->bindToTextureTarget(st.mTexTarget); - - // In the rare case that the display is terminated and then initialized - // again, we can't detect that the display changed (it didn't), but the - // image is invalid. In this case, repeat the exact same steps while - // forcing the creation of a new image. - if ((error = glGetError()) != GL_NO_ERROR) { - glBindTexture(st.mTexTarget, st.mTexName); - status_t result = mCurrentTextureImage->createIfNeeded(mEglDisplay, true); - if (result != NO_ERROR) { - EGC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay, - st.mCurrentTexture); - return UNKNOWN_ERROR; - } - mCurrentTextureImage->bindToTextureTarget(st.mTexTarget); - if ((error = glGetError()) != GL_NO_ERROR) { - EGC_LOGE("bindTextureImage: error binding external image: %#04x", error); - return UNKNOWN_ERROR; - } - } - - // Wait for the new buffer to be ready. - return doGLFenceWaitLocked(st); -} - -status_t EGLConsumer::checkAndUpdateEglStateLocked(SurfaceTexture& st, bool contextCheck) { - EGLDisplay dpy = eglGetCurrentDisplay(); - EGLContext ctx = eglGetCurrentContext(); - - if (!contextCheck) { - // if this is the first time we're called, mEglDisplay/mEglContext have - // never been set, so don't error out (below). - if (mEglDisplay == EGL_NO_DISPLAY) { - mEglDisplay = dpy; - } - if (mEglContext == EGL_NO_CONTEXT) { - mEglContext = ctx; - } - } - - if (mEglDisplay != dpy || dpy == EGL_NO_DISPLAY) { - EGC_LOGE("checkAndUpdateEglState: invalid current EGLDisplay"); - return INVALID_OPERATION; - } - - if (mEglContext != ctx || ctx == EGL_NO_CONTEXT) { - EGC_LOGE("checkAndUpdateEglState: invalid current EGLContext"); - return INVALID_OPERATION; - } - - mEglDisplay = dpy; - mEglContext = ctx; - return NO_ERROR; -} - -status_t EGLConsumer::detachFromContext(SurfaceTexture& st) { - EGLDisplay dpy = eglGetCurrentDisplay(); - EGLContext ctx = eglGetCurrentContext(); - - if (mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) { - EGC_LOGE("detachFromContext: invalid current EGLDisplay"); - return INVALID_OPERATION; - } - - if (mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) { - EGC_LOGE("detachFromContext: invalid current EGLContext"); - return INVALID_OPERATION; - } - - if (dpy != EGL_NO_DISPLAY && ctx != EGL_NO_CONTEXT) { - status_t err = syncForReleaseLocked(dpy, st); - if (err != OK) { - return err; - } - - glDeleteTextures(1, &st.mTexName); - } - - mEglDisplay = EGL_NO_DISPLAY; - mEglContext = EGL_NO_CONTEXT; - - return OK; -} - -status_t EGLConsumer::attachToContext(uint32_t tex, SurfaceTexture& st) { - // Initialize mCurrentTextureImage if there is a current buffer from past attached state. - int slot = st.mCurrentTexture; - if (slot != BufferItem::INVALID_BUFFER_SLOT) { - if (!mEglSlots[slot].mEglImage.get()) { - mEglSlots[slot].mEglImage = new EglImage(st.mSlots[slot].mGraphicBuffer); - } - mCurrentTextureImage = mEglSlots[slot].mEglImage; - } - - EGLDisplay dpy = eglGetCurrentDisplay(); - EGLContext ctx = eglGetCurrentContext(); - - if (dpy == EGL_NO_DISPLAY) { - EGC_LOGE("attachToContext: invalid current EGLDisplay"); - return INVALID_OPERATION; - } - - if (ctx == EGL_NO_CONTEXT) { - EGC_LOGE("attachToContext: invalid current EGLContext"); - return INVALID_OPERATION; - } - - // We need to bind the texture regardless of whether there's a current - // buffer. - glBindTexture(st.mTexTarget, GLuint(tex)); - - mEglDisplay = dpy; - mEglContext = ctx; - st.mTexName = tex; - st.mOpMode = SurfaceTexture::OpMode::attachedToGL; - - if (mCurrentTextureImage != nullptr) { - // This may wait for a buffer a second time. This is likely required if - // this is a different context, since otherwise the wait could be skipped - // by bouncing through another context. For the same context the extra - // wait is redundant. - status_t err = bindTextureImageLocked(st); - if (err != NO_ERROR) { - return err; - } - } - - return OK; -} - -status_t EGLConsumer::syncForReleaseLocked(EGLDisplay dpy, SurfaceTexture& st) { - EGC_LOGV("syncForReleaseLocked"); - - if (st.mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { - if (SyncFeatures::getInstance().useNativeFenceSync()) { - EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); - if (sync == EGL_NO_SYNC_KHR) { - EGC_LOGE("syncForReleaseLocked: error creating EGL fence: %#x", eglGetError()); - return UNKNOWN_ERROR; - } - glFlush(); - int fenceFd = eglDupNativeFenceFDANDROID(dpy, sync); - eglDestroySyncKHR(dpy, sync); - if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { - EGC_LOGE( - "syncForReleaseLocked: error dup'ing native fence " - "fd: %#x", - eglGetError()); - return UNKNOWN_ERROR; - } - sp fence(new Fence(fenceFd)); - status_t err = st.addReleaseFenceLocked(st.mCurrentTexture, - mCurrentTextureImage->graphicBuffer(), fence); - if (err != OK) { - EGC_LOGE( - "syncForReleaseLocked: error adding release fence: " - "%s (%d)", - strerror(-err), err); - return err; - } - } else if (st.mUseFenceSync && SyncFeatures::getInstance().useFenceSync()) { - EGLSyncKHR fence = mEglSlots[st.mCurrentTexture].mEglFence; - if (fence != EGL_NO_SYNC_KHR) { - // There is already a fence for the current slot. We need to - // wait on that before replacing it with another fence to - // ensure that all outstanding buffer accesses have completed - // before the producer accesses it. - EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000); - if (result == EGL_FALSE) { - EGC_LOGE( - "syncForReleaseLocked: error waiting for previous " - "fence: %#x", - eglGetError()); - return UNKNOWN_ERROR; - } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { - EGC_LOGE( - "syncForReleaseLocked: timeout waiting for previous " - "fence"); - return TIMED_OUT; - } - eglDestroySyncKHR(dpy, fence); - } - - // Create a fence for the outstanding accesses in the current - // OpenGL ES context. - fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr); - if (fence == EGL_NO_SYNC_KHR) { - EGC_LOGE("syncForReleaseLocked: error creating fence: %#x", eglGetError()); - return UNKNOWN_ERROR; - } - glFlush(); - mEglSlots[st.mCurrentTexture].mEglFence = fence; - } - } - - return OK; -} - -status_t EGLConsumer::doGLFenceWaitLocked(SurfaceTexture& st) const { - EGLDisplay dpy = eglGetCurrentDisplay(); - EGLContext ctx = eglGetCurrentContext(); - - if (mEglDisplay != dpy || mEglDisplay == EGL_NO_DISPLAY) { - EGC_LOGE("doGLFenceWait: invalid current EGLDisplay"); - return INVALID_OPERATION; - } - - if (mEglContext != ctx || mEglContext == EGL_NO_CONTEXT) { - EGC_LOGE("doGLFenceWait: invalid current EGLContext"); - return INVALID_OPERATION; - } - - if (st.mCurrentFence->isValid()) { - if (SyncFeatures::getInstance().useWaitSync() && - SyncFeatures::getInstance().useNativeFenceSync()) { - // Create an EGLSyncKHR from the current fence. - int fenceFd = st.mCurrentFence->dup(); - if (fenceFd == -1) { - EGC_LOGE("doGLFenceWait: error dup'ing fence fd: %d", errno); - return -errno; - } - EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, EGL_NONE}; - EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); - if (sync == EGL_NO_SYNC_KHR) { - close(fenceFd); - EGC_LOGE("doGLFenceWait: error creating EGL fence: %#x", eglGetError()); - return UNKNOWN_ERROR; - } - - // XXX: The spec draft is inconsistent as to whether this should - // return an EGLint or void. Ignore the return value for now, as - // it's not strictly needed. - eglWaitSyncKHR(dpy, sync, 0); - EGLint eglErr = eglGetError(); - eglDestroySyncKHR(dpy, sync); - if (eglErr != EGL_SUCCESS) { - EGC_LOGE("doGLFenceWait: error waiting for EGL fence: %#x", eglErr); - return UNKNOWN_ERROR; - } - } else { - status_t err = st.mCurrentFence->waitForever("EGLConsumer::doGLFenceWaitLocked"); - if (err != NO_ERROR) { - EGC_LOGE("doGLFenceWait: error waiting for fence: %d", err); - return err; - } - } - } - - return NO_ERROR; -} - -void EGLConsumer::onFreeBufferLocked(int slotIndex) { - mEglSlots[slotIndex].mEglImage.clear(); -} - -void EGLConsumer::onAbandonLocked() { - mCurrentTextureImage.clear(); -} - -EGLConsumer::EglImage::EglImage(sp graphicBuffer) - : mGraphicBuffer(graphicBuffer), mEglImage(EGL_NO_IMAGE_KHR), mEglDisplay(EGL_NO_DISPLAY) {} - -EGLConsumer::EglImage::~EglImage() { - if (mEglImage != EGL_NO_IMAGE_KHR) { - if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) { - ALOGE("~EglImage: eglDestroyImageKHR failed"); - } - eglTerminate(mEglDisplay); - } -} - -status_t EGLConsumer::EglImage::createIfNeeded(EGLDisplay eglDisplay, bool forceCreation) { - // If there's an image and it's no longer valid, destroy it. - bool haveImage = mEglImage != EGL_NO_IMAGE_KHR; - bool displayInvalid = mEglDisplay != eglDisplay; - if (haveImage && (displayInvalid || forceCreation)) { - if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) { - ALOGE("createIfNeeded: eglDestroyImageKHR failed"); - } - eglTerminate(mEglDisplay); - mEglImage = EGL_NO_IMAGE_KHR; - mEglDisplay = EGL_NO_DISPLAY; - } - - // If there's no image, create one. - if (mEglImage == EGL_NO_IMAGE_KHR) { - mEglDisplay = eglDisplay; - mEglImage = createImage(mEglDisplay, mGraphicBuffer); - } - - // Fail if we can't create a valid image. - if (mEglImage == EGL_NO_IMAGE_KHR) { - mEglDisplay = EGL_NO_DISPLAY; - const sp& buffer = mGraphicBuffer; - ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d", - buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->getUsage(), - buffer->getPixelFormat()); - return UNKNOWN_ERROR; - } - - return OK; -} - -void EGLConsumer::EglImage::bindToTextureTarget(uint32_t texTarget) { - glEGLImageTargetTexture2DOES(texTarget, static_cast(mEglImage)); -} - -EGLImageKHR EGLConsumer::EglImage::createImage(EGLDisplay dpy, - const sp& graphicBuffer) { - EGLClientBuffer cbuf = static_cast(graphicBuffer->getNativeBuffer()); - const bool createProtectedImage = - (graphicBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) && hasEglProtectedContent(); - EGLint attrs[] = { - EGL_IMAGE_PRESERVED_KHR, - EGL_TRUE, - createProtectedImage ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE, - createProtectedImage ? EGL_TRUE : EGL_NONE, - EGL_NONE, - }; - eglInitialize(dpy, nullptr, nullptr); - EGLImageKHR image = - eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs); - if (image == EGL_NO_IMAGE_KHR) { - EGLint error = eglGetError(); - ALOGE("error creating EGLImage: %#x", error); - eglTerminate(dpy); - } - return image; -} - -} // namespace android diff --git a/libs/hwui/surfacetexture/EGLConsumer.h b/libs/hwui/surfacetexture/EGLConsumer.h deleted file mode 100644 index 7dac3ef0f44a..000000000000 --- a/libs/hwui/surfacetexture/EGLConsumer.h +++ /dev/null @@ -1,311 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -#pragma once - -#include -#include - -#include - -#include -#include -#include - -namespace android { - -class SurfaceTexture; - -/* - * EGLConsumer implements the parts of SurfaceTexture that deal with - * textures attached to an GL context. - */ -class EGLConsumer { -public: - EGLConsumer(); - - /** - * updateTexImage acquires the most recently queued buffer, and sets the - * image contents of the target texture to it. - * - * This call may only be made while the OpenGL ES context to which the - * target texture belongs is bound to the calling thread. - * - * This calls doGLFenceWait to ensure proper synchronization. - */ - status_t updateTexImage(SurfaceTexture& st); - - /* - * releaseTexImage releases the texture acquired in updateTexImage(). - * This is intended to be used in single buffer mode. - * - * This call may only be made while the OpenGL ES context to which the - * target texture belongs is bound to the calling thread. - */ - status_t releaseTexImage(SurfaceTexture& st); - - /** - * detachFromContext detaches the EGLConsumer from the calling thread's - * current OpenGL ES context. This context must be the same as the context - * that was current for previous calls to updateTexImage. - * - * Detaching a EGLConsumer from an OpenGL ES context will result in the - * deletion of the OpenGL ES texture object into which the images were being - * streamed. After a EGLConsumer has been detached from the OpenGL ES - * context calls to updateTexImage will fail returning INVALID_OPERATION - * until the EGLConsumer is attached to a new OpenGL ES context using the - * attachToContext method. - */ - status_t detachFromContext(SurfaceTexture& st); - - /** - * attachToContext attaches a EGLConsumer that is currently in the - * 'detached' state to the current OpenGL ES context. A EGLConsumer is - * in the 'detached' state iff detachFromContext has successfully been - * called and no calls to attachToContext have succeeded since the last - * detachFromContext call. Calls to attachToContext made on a - * EGLConsumer that is not in the 'detached' state will result in an - * INVALID_OPERATION error. - * - * The tex argument specifies the OpenGL ES texture object name in the - * new context into which the image contents will be streamed. A successful - * call to attachToContext will result in this texture object being bound to - * the texture target and populated with the image contents that were - * current at the time of the last call to detachFromContext. - */ - status_t attachToContext(uint32_t tex, SurfaceTexture& st); - - /** - * onAcquireBufferLocked amends the ConsumerBase method to update the - * mEglSlots array in addition to the ConsumerBase behavior. - */ - void onAcquireBufferLocked(BufferItem* item, SurfaceTexture& st); - - /** - * onReleaseBufferLocked amends the ConsumerBase method to update the - * mEglSlots array in addition to the ConsumerBase. - */ - void onReleaseBufferLocked(int slot); - - /** - * onFreeBufferLocked frees up the given buffer slot. If the slot has been - * initialized this will release the reference to the GraphicBuffer in that - * slot and destroy the EGLImage in that slot. Otherwise it has no effect. - */ - void onFreeBufferLocked(int slotIndex); - - /** - * onAbandonLocked amends the ConsumerBase method to clear - * mCurrentTextureImage in addition to the ConsumerBase behavior. - */ - void onAbandonLocked(); - -protected: - struct PendingRelease { - PendingRelease() - : isPending(false) - , currentTexture(-1) - , graphicBuffer() - , display(nullptr) - , fence(nullptr) {} - - bool isPending; - int currentTexture; - sp graphicBuffer; - EGLDisplay display; - EGLSyncKHR fence; - }; - - /** - * This releases the buffer in the slot referenced by mCurrentTexture, - * then updates state to refer to the BufferItem, which must be a - * newly-acquired buffer. If pendingRelease is not null, the parameters - * which would have been passed to releaseBufferLocked upon the successful - * completion of the method will instead be returned to the caller, so that - * it may call releaseBufferLocked itself later. - */ - status_t updateAndReleaseLocked(const BufferItem& item, PendingRelease* pendingRelease, - SurfaceTexture& st); - - /** - * Binds mTexName and the current buffer to mTexTarget. Uses - * mCurrentTexture if it's set, mCurrentTextureImage if not. If the - * bind succeeds, this calls doGLFenceWait. - */ - status_t bindTextureImageLocked(SurfaceTexture& st); - - /** - * Gets the current EGLDisplay and EGLContext values, and compares them - * to mEglDisplay and mEglContext. If the fields have been previously - * set, the values must match; if not, the fields are set to the current - * values. - * The contextCheck argument is used to ensure that a GL context is - * properly set; when set to false, the check is not performed. - */ - status_t checkAndUpdateEglStateLocked(SurfaceTexture& st, bool contextCheck = false); - - /** - * EglImage is a utility class for tracking and creating EGLImageKHRs. There - * is primarily just one image per slot, but there is also special cases: - * - For releaseTexImage, we use a debug image (mReleasedTexImage) - * - After freeBuffer, we must still keep the current image/buffer - * Reference counting EGLImages lets us handle all these cases easily while - * also only creating new EGLImages from buffers when required. - */ - class EglImage : public LightRefBase { - public: - EglImage(sp graphicBuffer); - - /** - * createIfNeeded creates an EGLImage if required (we haven't created - * one yet, or the EGLDisplay or crop-rect has changed). - */ - status_t createIfNeeded(EGLDisplay display, bool forceCreate = false); - - /** - * This calls glEGLImageTargetTexture2DOES to bind the image to the - * texture in the specified texture target. - */ - void bindToTextureTarget(uint32_t texTarget); - - const sp& graphicBuffer() { return mGraphicBuffer; } - const native_handle* graphicBufferHandle() { - return mGraphicBuffer == nullptr ? nullptr : mGraphicBuffer->handle; - } - - private: - // Only allow instantiation using ref counting. - friend class LightRefBase; - virtual ~EglImage(); - - // createImage creates a new EGLImage from a GraphicBuffer. - EGLImageKHR createImage(EGLDisplay dpy, const sp& graphicBuffer); - - // Disallow copying - EglImage(const EglImage& rhs); - void operator=(const EglImage& rhs); - - // mGraphicBuffer is the buffer that was used to create this image. - sp mGraphicBuffer; - - // mEglImage is the EGLImage created from mGraphicBuffer. - EGLImageKHR mEglImage; - - // mEGLDisplay is the EGLDisplay that was used to create mEglImage. - EGLDisplay mEglDisplay; - - // mCropRect is the crop rectangle passed to EGL when mEglImage - // was created. - Rect mCropRect; - }; - - /** - * doGLFenceWaitLocked inserts a wait command into the OpenGL ES command - * stream to ensure that it is safe for future OpenGL ES commands to - * access the current texture buffer. - */ - status_t doGLFenceWaitLocked(SurfaceTexture& st) const; - - /** - * syncForReleaseLocked performs the synchronization needed to release the - * current slot from an OpenGL ES context. If needed it will set the - * current slot's fence to guard against a producer accessing the buffer - * before the outstanding accesses have completed. - */ - status_t syncForReleaseLocked(EGLDisplay dpy, SurfaceTexture& st); - - /** - * returns a graphic buffer used when the texture image has been released - */ - static sp getDebugTexImageBuffer(); - - /** - * The default consumer usage flags that EGLConsumer always sets on its - * BufferQueue instance; these will be OR:d with any additional flags passed - * from the EGLConsumer user. In particular, EGLConsumer will always - * consume buffers as hardware textures. - */ - static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE; - - /** - * mCurrentTextureImage is the EglImage/buffer of the current texture. It's - * possible that this buffer is not associated with any buffer slot, so we - * must track it separately in order to support the getCurrentBuffer method. - */ - sp mCurrentTextureImage; - - /** - * EGLSlot contains the information and object references that - * EGLConsumer maintains about a BufferQueue buffer slot. - */ - struct EglSlot { - EglSlot() : mEglFence(EGL_NO_SYNC_KHR) {} - - /** - * mEglImage is the EGLImage created from mGraphicBuffer. - */ - sp mEglImage; - - /** - * mFence is the EGL sync object that must signal before the buffer - * associated with this buffer slot may be dequeued. It is initialized - * to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based - * on a compile-time option) set to a new sync object in updateTexImage. - */ - EGLSyncKHR mEglFence; - }; - - /** - * mEglDisplay is the EGLDisplay with which this EGLConsumer is currently - * associated. It is intialized to EGL_NO_DISPLAY and gets set to the - * current display when updateTexImage is called for the first time and when - * attachToContext is called. - */ - EGLDisplay mEglDisplay; - - /** - * mEglContext is the OpenGL ES context with which this EGLConsumer is - * currently associated. It is initialized to EGL_NO_CONTEXT and gets set - * to the current GL context when updateTexImage is called for the first - * time and when attachToContext is called. - */ - EGLContext mEglContext; - - /** - * mEGLSlots stores the buffers that have been allocated by the BufferQueue - * for each buffer slot. It is initialized to null pointers, and gets - * filled in with the result of BufferQueue::acquire when the - * client dequeues a buffer from a - * slot that has not yet been used. The buffer allocated to a slot will also - * be replaced if the requested buffer usage or geometry differs from that - * of the buffer allocated to a slot. - */ - EglSlot mEglSlots[BufferQueueDefs::NUM_BUFFER_SLOTS]; - - /** - * protects static initialization - */ - static Mutex sStaticInitLock; - - /** - * mReleasedTexImageBuffer is a dummy buffer used when in single buffer - * mode and releaseTexImage() has been called - */ - static sp sReleasedTexImageBuffer; - sp mReleasedTexImage; -}; - -} // namespace android diff --git a/libs/hwui/surfacetexture/ImageConsumer.cpp b/libs/hwui/surfacetexture/ImageConsumer.cpp deleted file mode 100644 index 17ee17d5cd1d..000000000000 --- a/libs/hwui/surfacetexture/ImageConsumer.cpp +++ /dev/null @@ -1,299 +0,0 @@ -/* - * Copyright (C) 2018 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 "ImageConsumer.h" -#include -#include "Properties.h" -#include "SurfaceTexture.h" -#include "renderstate/RenderState.h" -#include "renderthread/EglManager.h" -#include "renderthread/RenderThread.h" -#include "renderthread/VulkanManager.h" -#include "utils/Color.h" -#include -#include - -// Macro for including the SurfaceTexture name in log messages -#define IMG_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__) - -using namespace android::uirenderer::renderthread; - -namespace android { - -void ImageConsumer::onFreeBufferLocked(int slotIndex) { - // This callback may be invoked on any thread. - mImageSlots[slotIndex].clear(); -} - -void ImageConsumer::onAcquireBufferLocked(BufferItem* item) { - // If item->mGraphicBuffer is not null, this buffer has not been acquired - // before, so any prior SkImage is created with a stale buffer. This resets the stale SkImage. - if (item->mGraphicBuffer != nullptr) { - mImageSlots[item->mSlot].clear(); - } -} - -void ImageConsumer::onReleaseBufferLocked(int buf) { - mImageSlots[buf].eglFence() = EGL_NO_SYNC_KHR; -} - -/** - * AutoBackendTextureRelease manages EglImage/VkImage lifetime. It is a ref-counted object - * that keeps GPU resources alive until the last SKImage object using them is destroyed. - */ -class AutoBackendTextureRelease { -public: - static void releaseProc(SkImage::ReleaseContext releaseContext); - - AutoBackendTextureRelease(GrContext* context, GraphicBuffer* buffer); - - const GrBackendTexture& getTexture() const { return mBackendTexture; } - - void ref() { mUsageCount++; } - - void unref(bool releaseImage); - - inline sk_sp getImage() { return mImage; } - - void makeImage(sp& graphicBuffer, android_dataspace dataspace, - GrContext* context); - - void newBufferContent(GrContext* context); - -private: - // The only way to invoke dtor is with unref, when mUsageCount is 0. - ~AutoBackendTextureRelease() {} - - GrBackendTexture mBackendTexture; - GrAHardwareBufferUtils::DeleteImageProc mDeleteProc; - GrAHardwareBufferUtils::UpdateImageProc mUpdateProc; - GrAHardwareBufferUtils::TexImageCtx mImageCtx; - - // Starting with refcount 1, because the first ref is held by SurfaceTexture. Additional refs - // are held by SkImages. - int mUsageCount = 1; - - // mImage is the SkImage created from mBackendTexture. - sk_sp mImage; -}; - -AutoBackendTextureRelease::AutoBackendTextureRelease(GrContext* context, GraphicBuffer* buffer) { - bool createProtectedImage = - 0 != (buffer->getUsage() & GraphicBuffer::USAGE_PROTECTED); - GrBackendFormat backendFormat = GrAHardwareBufferUtils::GetBackendFormat( - context, - reinterpret_cast(buffer), - buffer->getPixelFormat(), - false); - mBackendTexture = GrAHardwareBufferUtils::MakeBackendTexture( - context, - reinterpret_cast(buffer), - buffer->getWidth(), - buffer->getHeight(), - &mDeleteProc, - &mUpdateProc, - &mImageCtx, - createProtectedImage, - backendFormat, - false); -} - -void AutoBackendTextureRelease::unref(bool releaseImage) { - if (!RenderThread::isCurrent()) { - // EGLImage needs to be destroyed on RenderThread to prevent memory leak. - // ~SkImage dtor for both pipelines needs to be invoked on RenderThread, because it is not - // thread safe. - RenderThread::getInstance().queue().post([this, releaseImage]() { unref(releaseImage); }); - return; - } - - if (releaseImage) { - mImage.reset(); - } - - mUsageCount--; - if (mUsageCount <= 0) { - if (mBackendTexture.isValid()) { - mDeleteProc(mImageCtx); - mBackendTexture = {}; - } - delete this; - } -} - -void AutoBackendTextureRelease::releaseProc(SkImage::ReleaseContext releaseContext) { - AutoBackendTextureRelease* textureRelease = - reinterpret_cast(releaseContext); - textureRelease->unref(false); -} - -void AutoBackendTextureRelease::makeImage(sp& graphicBuffer, - android_dataspace dataspace, GrContext* context) { - SkColorType colorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat( - graphicBuffer->getPixelFormat()); - mImage = SkImage::MakeFromTexture(context, - mBackendTexture, - kTopLeft_GrSurfaceOrigin, - colorType, - kPremul_SkAlphaType, - uirenderer::DataSpaceToColorSpace(dataspace), - releaseProc, - this); - if (mImage.get()) { - // The following ref will be counteracted by releaseProc, when SkImage is discarded. - ref(); - } -} - -void AutoBackendTextureRelease::newBufferContent(GrContext* context) { - if (mBackendTexture.isValid()) { - mUpdateProc(mImageCtx, context); - } -} - -void ImageConsumer::ImageSlot::createIfNeeded(sp graphicBuffer, - android_dataspace dataspace, bool forceCreate, - GrContext* context) { - if (!mTextureRelease || !mTextureRelease->getImage().get() || dataspace != mDataspace - || forceCreate) { - if (!graphicBuffer.get()) { - clear(); - return; - } - - if (!mTextureRelease) { - mTextureRelease = new AutoBackendTextureRelease(context, graphicBuffer.get()); - } else { - mTextureRelease->newBufferContent(context); - } - - mDataspace = dataspace; - mTextureRelease->makeImage(graphicBuffer, dataspace, context); - } -} - -void ImageConsumer::ImageSlot::clear() { - if (mTextureRelease) { - // The following unref counteracts the initial mUsageCount of 1, set by default initializer. - mTextureRelease->unref(true); - mTextureRelease = nullptr; - } -} - -sk_sp ImageConsumer::ImageSlot::getImage() { - return mTextureRelease ? mTextureRelease->getImage() : nullptr; -} - -sk_sp ImageConsumer::dequeueImage(bool* queueEmpty, SurfaceTexture& st, - uirenderer::RenderState& renderState) { - BufferItem item; - status_t err; - err = st.acquireBufferLocked(&item, 0); - if (err != OK) { - if (err != BufferQueue::NO_BUFFER_AVAILABLE) { - IMG_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err); - } else { - int slot = st.mCurrentTexture; - if (slot != BufferItem::INVALID_BUFFER_SLOT) { - *queueEmpty = true; - mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer, - st.mCurrentDataSpace, false, renderState.getRenderThread().getGrContext()); - return mImageSlots[slot].getImage(); - } - } - return nullptr; - } - - int slot = item.mSlot; - if (item.mFence->isValid()) { - // Wait on the producer fence for the buffer to be ready. - if (uirenderer::Properties::getRenderPipelineType() == - uirenderer::RenderPipelineType::SkiaGL) { - err = renderState.getRenderThread().eglManager().fenceWait(item.mFence); - } else { - err = renderState.getRenderThread().vulkanManager().fenceWait( - item.mFence, renderState.getRenderThread().getGrContext()); - } - if (err != OK) { - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY, - EGL_NO_SYNC_KHR); - return nullptr; - } - } - - // Release old buffer. - if (st.mCurrentTexture != BufferItem::INVALID_BUFFER_SLOT) { - // If needed, set the released slot's fence to guard against a producer accessing the - // buffer before the outstanding accesses have completed. - sp releaseFence; - EGLDisplay display = EGL_NO_DISPLAY; - if (uirenderer::Properties::getRenderPipelineType() == - uirenderer::RenderPipelineType::SkiaGL) { - auto& eglManager = renderState.getRenderThread().eglManager(); - display = eglManager.eglDisplay(); - err = eglManager.createReleaseFence(st.mUseFenceSync, &mImageSlots[slot].eglFence(), - releaseFence); - } else { - err = renderState.getRenderThread().vulkanManager().createReleaseFence( - releaseFence, renderState.getRenderThread().getGrContext()); - } - if (OK != err) { - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY, - EGL_NO_SYNC_KHR); - return nullptr; - } - - if (releaseFence.get()) { - status_t err = st.addReleaseFenceLocked( - st.mCurrentTexture, st.mSlots[st.mCurrentTexture].mGraphicBuffer, releaseFence); - if (err != OK) { - IMG_LOGE("dequeueImage: error adding release fence: %s (%d)", strerror(-err), err); - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY, - EGL_NO_SYNC_KHR); - return nullptr; - } - } - - // Finally release the old buffer. - status_t status = st.releaseBufferLocked( - st.mCurrentTexture, st.mSlots[st.mCurrentTexture].mGraphicBuffer, display, - mImageSlots[st.mCurrentTexture].eglFence()); - if (status < NO_ERROR) { - IMG_LOGE("dequeueImage: failed to release buffer: %s (%d)", strerror(-status), status); - err = status; - // Keep going, with error raised. - } - } - - // Update the state. - st.mCurrentTexture = slot; - st.mCurrentCrop = item.mCrop; - st.mCurrentTransform = item.mTransform; - st.mCurrentScalingMode = item.mScalingMode; - st.mCurrentTimestamp = item.mTimestamp; - st.mCurrentDataSpace = item.mDataSpace; - st.mCurrentFence = item.mFence; - st.mCurrentFenceTime = item.mFenceTime; - st.mCurrentFrameNumber = item.mFrameNumber; - st.computeCurrentTransformMatrixLocked(); - - *queueEmpty = false; - mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer, item.mDataSpace, true, - renderState.getRenderThread().getGrContext()); - return mImageSlots[slot].getImage(); -} - -} /* namespace android */ diff --git a/libs/hwui/surfacetexture/ImageConsumer.h b/libs/hwui/surfacetexture/ImageConsumer.h deleted file mode 100644 index 3e2a91a251f7..000000000000 --- a/libs/hwui/surfacetexture/ImageConsumer.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -#pragma once - -#include -#include - -#include - -#include -#include -#include -#include - -namespace android { - -namespace uirenderer { -class RenderState; -} - -class AutoBackendTextureRelease; -class SurfaceTexture; - -/* - * ImageConsumer implements the parts of SurfaceTexture that deal with - * images consumed by HWUI view system. - */ -class ImageConsumer { -public: - sk_sp dequeueImage(bool* queueEmpty, SurfaceTexture& cb, - uirenderer::RenderState& renderState); - - /** - * onAcquireBufferLocked amends the ConsumerBase method to update the - * mImageSlots array in addition to the ConsumerBase behavior. - */ - void onAcquireBufferLocked(BufferItem* item); - - /** - * onReleaseBufferLocked amends the ConsumerBase method to update the - * mImageSlots array in addition to the ConsumerBase. - */ - void onReleaseBufferLocked(int slot); - - /** - * onFreeBufferLocked frees up the given buffer slot. If the slot has been - * initialized this will release the reference to the GraphicBuffer in that - * slot and destroy the SkImage in that slot. Otherwise it has no effect. - */ - void onFreeBufferLocked(int slotIndex); - -private: - /** - * ImageSlot contains the information and object references that - * ImageConsumer maintains about a BufferQueue buffer slot. - */ - class ImageSlot { - public: - ImageSlot() : mDataspace(HAL_DATASPACE_UNKNOWN), mEglFence(EGL_NO_SYNC_KHR) {} - - ~ImageSlot() { clear(); } - - void createIfNeeded(sp graphicBuffer, android_dataspace dataspace, - bool forceCreate, GrContext* context); - - void clear(); - - inline EGLSyncKHR& eglFence() { return mEglFence; } - - sk_sp getImage(); - - private: - // the dataspace associated with the current image - android_dataspace mDataspace; - - /** - * mEglFence is the EGL sync object that must signal before the buffer - * associated with this buffer slot may be dequeued. - */ - EGLSyncKHR mEglFence; - - /** - * mTextureRelease may outlive ImageConsumer, if the last ref is held by an SkImage. - * ImageConsumer holds one ref to mTextureRelease, which is decremented by "clear". - */ - AutoBackendTextureRelease* mTextureRelease = nullptr; - }; - - /** - * ImageConsumer stores the SkImages that have been allocated by the BufferQueue - * for each buffer slot. It is initialized to null pointers, and gets - * filled in with the result of BufferQueue::acquire when the - * client dequeues a buffer from a - * slot that has not yet been used. The buffer allocated to a slot will also - * be replaced if the requested buffer usage or geometry differs from that - * of the buffer allocated to a slot. - */ - ImageSlot mImageSlots[BufferQueueDefs::NUM_BUFFER_SLOTS]; -}; - -} /* namespace android */ diff --git a/libs/hwui/surfacetexture/SurfaceTexture.cpp b/libs/hwui/surfacetexture/SurfaceTexture.cpp deleted file mode 100644 index a27db6591d6a..000000000000 --- a/libs/hwui/surfacetexture/SurfaceTexture.cpp +++ /dev/null @@ -1,499 +0,0 @@ -/* - * Copyright (C) 2018 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 -#include -#include -#include - -#include - -#include "Matrix.h" -#include "SurfaceTexture.h" -#include "ImageConsumer.h" - -namespace android { - -// Macros for including the SurfaceTexture name in log messages -#define SFT_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__) -#define SFT_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__) -#define SFT_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__) -#define SFT_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__) - -static const mat4 mtxIdentity; - -SurfaceTexture::SurfaceTexture(const sp& bq, uint32_t tex, - uint32_t texTarget, bool useFenceSync, bool isControlledByApp) - : ConsumerBase(bq, isControlledByApp) - , mCurrentCrop(Rect::EMPTY_RECT) - , mCurrentTransform(0) - , mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE) - , mCurrentFence(Fence::NO_FENCE) - , mCurrentTimestamp(0) - , mCurrentDataSpace(HAL_DATASPACE_UNKNOWN) - , mCurrentFrameNumber(0) - , mDefaultWidth(1) - , mDefaultHeight(1) - , mFilteringEnabled(true) - , mTexName(tex) - , mUseFenceSync(useFenceSync) - , mTexTarget(texTarget) - , mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT) - , mOpMode(OpMode::attachedToGL) { - SFT_LOGV("SurfaceTexture"); - - memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix)); - - mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); -} - -SurfaceTexture::SurfaceTexture(const sp& bq, uint32_t texTarget, - bool useFenceSync, bool isControlledByApp) - : ConsumerBase(bq, isControlledByApp) - , mCurrentCrop(Rect::EMPTY_RECT) - , mCurrentTransform(0) - , mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE) - , mCurrentFence(Fence::NO_FENCE) - , mCurrentTimestamp(0) - , mCurrentDataSpace(HAL_DATASPACE_UNKNOWN) - , mCurrentFrameNumber(0) - , mDefaultWidth(1) - , mDefaultHeight(1) - , mFilteringEnabled(true) - , mTexName(0) - , mUseFenceSync(useFenceSync) - , mTexTarget(texTarget) - , mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT) - , mOpMode(OpMode::detached) { - SFT_LOGV("SurfaceTexture"); - - memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix)); - - mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); -} - -status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h) { - Mutex::Autolock lock(mMutex); - if (mAbandoned) { - SFT_LOGE("setDefaultBufferSize: SurfaceTexture is abandoned!"); - return NO_INIT; - } - mDefaultWidth = w; - mDefaultHeight = h; - return mConsumer->setDefaultBufferSize(w, h); -} - -status_t SurfaceTexture::updateTexImage() { - ATRACE_CALL(); - SFT_LOGV("updateTexImage"); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - SFT_LOGE("updateTexImage: SurfaceTexture is abandoned!"); - return NO_INIT; - } - - return mEGLConsumer.updateTexImage(*this); -} - -status_t SurfaceTexture::releaseTexImage() { - // releaseTexImage can be invoked even when not attached to a GL context. - ATRACE_CALL(); - SFT_LOGV("releaseTexImage"); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - SFT_LOGE("releaseTexImage: SurfaceTexture is abandoned!"); - return NO_INIT; - } - - return mEGLConsumer.releaseTexImage(*this); -} - -status_t SurfaceTexture::acquireBufferLocked(BufferItem* item, nsecs_t presentWhen, - uint64_t maxFrameNumber) { - status_t err = ConsumerBase::acquireBufferLocked(item, presentWhen, maxFrameNumber); - if (err != NO_ERROR) { - return err; - } - - switch (mOpMode) { - case OpMode::attachedToView: - mImageConsumer.onAcquireBufferLocked(item); - break; - case OpMode::attachedToGL: - mEGLConsumer.onAcquireBufferLocked(item, *this); - break; - case OpMode::detached: - break; - } - - return NO_ERROR; -} - -status_t SurfaceTexture::releaseBufferLocked(int buf, sp graphicBuffer, - EGLDisplay display, EGLSyncKHR eglFence) { - // release the buffer if it hasn't already been discarded by the - // BufferQueue. This can happen, for example, when the producer of this - // buffer has reallocated the original buffer slot after this buffer - // was acquired. - status_t err = ConsumerBase::releaseBufferLocked(buf, graphicBuffer, display, eglFence); - // We could be releasing an EGL/Vulkan buffer, even if not currently attached to a GL context. - mImageConsumer.onReleaseBufferLocked(buf); - mEGLConsumer.onReleaseBufferLocked(buf); - return err; -} - -status_t SurfaceTexture::detachFromContext() { - ATRACE_CALL(); - SFT_LOGV("detachFromContext"); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - SFT_LOGE("detachFromContext: abandoned SurfaceTexture"); - return NO_INIT; - } - - if (mOpMode != OpMode::attachedToGL) { - SFT_LOGE("detachFromContext: SurfaceTexture is not attached to a GL context"); - return INVALID_OPERATION; - } - - status_t err = mEGLConsumer.detachFromContext(*this); - if (err == OK) { - mOpMode = OpMode::detached; - } - - return err; -} - -status_t SurfaceTexture::attachToContext(uint32_t tex) { - ATRACE_CALL(); - SFT_LOGV("attachToContext"); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - SFT_LOGE("attachToContext: abandoned SurfaceTexture"); - return NO_INIT; - } - - if (mOpMode != OpMode::detached) { - SFT_LOGE( - "attachToContext: SurfaceTexture is already attached to a " - "context"); - return INVALID_OPERATION; - } - - if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { - // release possible ImageConsumer cache - mImageConsumer.onFreeBufferLocked(mCurrentTexture); - } - - return mEGLConsumer.attachToContext(tex, *this); -} - -void SurfaceTexture::attachToView() { - ATRACE_CALL(); - Mutex::Autolock _l(mMutex); - if (mAbandoned) { - SFT_LOGE("attachToView: abandoned SurfaceTexture"); - return; - } - if (mOpMode == OpMode::detached) { - mOpMode = OpMode::attachedToView; - - if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { - // release possible EGLConsumer texture cache - mEGLConsumer.onFreeBufferLocked(mCurrentTexture); - mEGLConsumer.onAbandonLocked(); - } - } else { - SFT_LOGE("attachToView: already attached"); - } -} - -void SurfaceTexture::detachFromView() { - ATRACE_CALL(); - Mutex::Autolock _l(mMutex); - - if (mAbandoned) { - SFT_LOGE("detachFromView: abandoned SurfaceTexture"); - return; - } - - if (mOpMode == OpMode::attachedToView) { - mOpMode = OpMode::detached; - // Free all EglImage and VkImage before the context is destroyed. - for (int i=0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; i++) { - mImageConsumer.onFreeBufferLocked(i); - } - } else { - SFT_LOGE("detachFromView: not attached to View"); - } -} - -uint32_t SurfaceTexture::getCurrentTextureTarget() const { - return mTexTarget; -} - -void SurfaceTexture::getTransformMatrix(float mtx[16]) { - Mutex::Autolock lock(mMutex); - memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix)); -} - -void SurfaceTexture::setFilteringEnabled(bool enabled) { - Mutex::Autolock lock(mMutex); - if (mAbandoned) { - SFT_LOGE("setFilteringEnabled: SurfaceTexture is abandoned!"); - return; - } - bool needsRecompute = mFilteringEnabled != enabled; - mFilteringEnabled = enabled; - - if (needsRecompute && mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) { - SFT_LOGD("setFilteringEnabled called with no current item"); - } - - if (needsRecompute && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { - computeCurrentTransformMatrixLocked(); - } -} - -void SurfaceTexture::computeCurrentTransformMatrixLocked() { - SFT_LOGV("computeCurrentTransformMatrixLocked"); - sp buf = (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) - ? nullptr - : mSlots[mCurrentTexture].mGraphicBuffer; - if (buf == nullptr) { - SFT_LOGD("computeCurrentTransformMatrixLocked: no current item"); - } - computeTransformMatrix(mCurrentTransformMatrix, buf, mCurrentCrop, mCurrentTransform, - mFilteringEnabled); -} - -void SurfaceTexture::computeTransformMatrix(float outTransform[16], const sp& buf, - const Rect& cropRect, uint32_t transform, - bool filtering) { - // Transform matrices - static const mat4 mtxFlipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1); - static const mat4 mtxFlipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1); - static const mat4 mtxRot90(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1); - - mat4 xform; - if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) { - xform *= mtxFlipH; - } - if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) { - xform *= mtxFlipV; - } - if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) { - xform *= mtxRot90; - } - - if (!cropRect.isEmpty() && buf.get()) { - float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f; - float bufferWidth = buf->getWidth(); - float bufferHeight = buf->getHeight(); - float shrinkAmount = 0.0f; - if (filtering) { - // In order to prevent bilinear sampling beyond the edge of the - // crop rectangle we may need to shrink it by 2 texels in each - // dimension. Normally this would just need to take 1/2 a texel - // off each end, but because the chroma channels of YUV420 images - // are subsampled we may need to shrink the crop region by a whole - // texel on each side. - switch (buf->getPixelFormat()) { - case PIXEL_FORMAT_RGBA_8888: - case PIXEL_FORMAT_RGBX_8888: - case PIXEL_FORMAT_RGBA_FP16: - case PIXEL_FORMAT_RGBA_1010102: - case PIXEL_FORMAT_RGB_888: - case PIXEL_FORMAT_RGB_565: - case PIXEL_FORMAT_BGRA_8888: - // We know there's no subsampling of any channels, so we - // only need to shrink by a half a pixel. - shrinkAmount = 0.5; - break; - - default: - // If we don't recognize the format, we must assume the - // worst case (that we care about), which is YUV420. - shrinkAmount = 1.0; - break; - } - } - - // Only shrink the dimensions that are not the size of the buffer. - if (cropRect.width() < bufferWidth) { - tx = (float(cropRect.left) + shrinkAmount) / bufferWidth; - sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) / bufferWidth; - } - if (cropRect.height() < bufferHeight) { - ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) / bufferHeight; - sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) / bufferHeight; - } - - mat4 crop(sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, 1, 0, tx, ty, 0, 1); - xform = crop * xform; - } - - // SurfaceFlinger expects the top of its window textures to be at a Y - // coordinate of 0, so SurfaceTexture must behave the same way. We don't - // want to expose this to applications, however, so we must add an - // additional vertical flip to the transform after all the other transforms. - xform = mtxFlipV * xform; - - memcpy(outTransform, xform.asArray(), sizeof(xform)); -} - -Rect SurfaceTexture::scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight) { - Rect outCrop = crop; - - uint32_t newWidth = static_cast(crop.width()); - uint32_t newHeight = static_cast(crop.height()); - - if (newWidth * bufferHeight > newHeight * bufferWidth) { - newWidth = newHeight * bufferWidth / bufferHeight; - ALOGV("too wide: newWidth = %d", newWidth); - } else if (newWidth * bufferHeight < newHeight * bufferWidth) { - newHeight = newWidth * bufferHeight / bufferWidth; - ALOGV("too tall: newHeight = %d", newHeight); - } - - uint32_t currentWidth = static_cast(crop.width()); - uint32_t currentHeight = static_cast(crop.height()); - - // The crop is too wide - if (newWidth < currentWidth) { - uint32_t dw = currentWidth - newWidth; - auto halfdw = dw / 2; - outCrop.left += halfdw; - // Not halfdw because it would subtract 1 too few when dw is odd - outCrop.right -= (dw - halfdw); - // The crop is too tall - } else if (newHeight < currentHeight) { - uint32_t dh = currentHeight - newHeight; - auto halfdh = dh / 2; - outCrop.top += halfdh; - // Not halfdh because it would subtract 1 too few when dh is odd - outCrop.bottom -= (dh - halfdh); - } - - ALOGV("getCurrentCrop final crop [%d,%d,%d,%d]", outCrop.left, outCrop.top, outCrop.right, - outCrop.bottom); - - return outCrop; -} - -nsecs_t SurfaceTexture::getTimestamp() { - SFT_LOGV("getTimestamp"); - Mutex::Autolock lock(mMutex); - return mCurrentTimestamp; -} - -android_dataspace SurfaceTexture::getCurrentDataSpace() { - SFT_LOGV("getCurrentDataSpace"); - Mutex::Autolock lock(mMutex); - return mCurrentDataSpace; -} - -uint64_t SurfaceTexture::getFrameNumber() { - SFT_LOGV("getFrameNumber"); - Mutex::Autolock lock(mMutex); - return mCurrentFrameNumber; -} - -Rect SurfaceTexture::getCurrentCrop() const { - Mutex::Autolock lock(mMutex); - return (mCurrentScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) - ? scaleDownCrop(mCurrentCrop, mDefaultWidth, mDefaultHeight) - : mCurrentCrop; -} - -uint32_t SurfaceTexture::getCurrentTransform() const { - Mutex::Autolock lock(mMutex); - return mCurrentTransform; -} - -uint32_t SurfaceTexture::getCurrentScalingMode() const { - Mutex::Autolock lock(mMutex); - return mCurrentScalingMode; -} - -sp SurfaceTexture::getCurrentFence() const { - Mutex::Autolock lock(mMutex); - return mCurrentFence; -} - -std::shared_ptr SurfaceTexture::getCurrentFenceTime() const { - Mutex::Autolock lock(mMutex); - return mCurrentFenceTime; -} - -void SurfaceTexture::freeBufferLocked(int slotIndex) { - SFT_LOGV("freeBufferLocked: slotIndex=%d", slotIndex); - if (slotIndex == mCurrentTexture) { - mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT; - } - // The slotIndex buffer could have EGL or SkImage cache, but there is no way to tell for sure. - // Buffers can be freed after SurfaceTexture has detached from GL context or View. - mImageConsumer.onFreeBufferLocked(slotIndex); - mEGLConsumer.onFreeBufferLocked(slotIndex); - ConsumerBase::freeBufferLocked(slotIndex); -} - -void SurfaceTexture::abandonLocked() { - SFT_LOGV("abandonLocked"); - mEGLConsumer.onAbandonLocked(); - ConsumerBase::abandonLocked(); -} - -status_t SurfaceTexture::setConsumerUsageBits(uint64_t usage) { - return ConsumerBase::setConsumerUsageBits(usage | DEFAULT_USAGE_FLAGS); -} - -void SurfaceTexture::dumpLocked(String8& result, const char* prefix) const { - result.appendFormat( - "%smTexName=%d mCurrentTexture=%d\n" - "%smCurrentCrop=[%d,%d,%d,%d] mCurrentTransform=%#x\n", - prefix, mTexName, mCurrentTexture, prefix, mCurrentCrop.left, mCurrentCrop.top, - mCurrentCrop.right, mCurrentCrop.bottom, mCurrentTransform); - - ConsumerBase::dumpLocked(result, prefix); -} - -sk_sp SurfaceTexture::dequeueImage(SkMatrix& transformMatrix, bool* queueEmpty, - uirenderer::RenderState& renderState) { - Mutex::Autolock _l(mMutex); - - if (mAbandoned) { - SFT_LOGE("dequeueImage: SurfaceTexture is abandoned!"); - return nullptr; - } - - if (mOpMode != OpMode::attachedToView) { - SFT_LOGE("dequeueImage: SurfaceTexture is not attached to a View"); - return nullptr; - } - - auto image = mImageConsumer.dequeueImage(queueEmpty, *this, renderState); - if (image.get()) { - uirenderer::mat4(mCurrentTransformMatrix).copyTo(transformMatrix); - } - return image; -} - -} // namespace android diff --git a/libs/hwui/surfacetexture/SurfaceTexture.h b/libs/hwui/surfacetexture/SurfaceTexture.h deleted file mode 100644 index b5d136ff3058..000000000000 --- a/libs/hwui/surfacetexture/SurfaceTexture.h +++ /dev/null @@ -1,452 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -#pragma once - -#include -#include - -#include -#include - -#include -#include - -#include "EGLConsumer.h" -#include "ImageConsumer.h" - -namespace android { - -namespace uirenderer { -class RenderState; -} - -/* - * SurfaceTexture consumes buffers of graphics data from a BufferQueue, - * and makes them available to HWUI render thread as a SkImage and to - * an application GL render thread as an OpenGL texture. - * - * When attached to an application GL render thread, a typical usage - * pattern is to set up the SurfaceTexture with the - * desired options, and call updateTexImage() when a new frame is desired. - * If a new frame is available, the texture will be updated. If not, - * the previous contents are retained. - * - * When attached to a HWUI render thread, the TextureView implementation - * calls dequeueImage, which either pulls a new SkImage or returns the - * last cached SkImage if BufferQueue is empty. - * When attached to HWUI render thread, SurfaceTexture is compatible to - * both Vulkan and GL drawing pipelines. - */ -class ANDROID_API SurfaceTexture : public ConsumerBase { -public: - enum { TEXTURE_EXTERNAL = 0x8D65 }; // GL_TEXTURE_EXTERNAL_OES - typedef ConsumerBase::FrameAvailableListener FrameAvailableListener; - - /** - * SurfaceTexture constructs a new SurfaceTexture object. If the constructor with - * the tex parameter is used, tex indicates the name of the OpenGL ES - * texture to which images are to be streamed. texTarget specifies the - * OpenGL ES texture target to which the texture will be bound in - * updateTexImage. useFenceSync specifies whether fences should be used to - * synchronize access to buffers if that behavior is enabled at - * compile-time. - * - * A SurfaceTexture may be detached from one OpenGL ES context and then - * attached to a different context using the detachFromContext and - * attachToContext methods, respectively. The intention of these methods is - * purely to allow a SurfaceTexture to be transferred from one consumer - * context to another. If such a transfer is not needed there is no - * requirement that either of these methods be called. - * - * If the constructor with the tex parameter is used, the SurfaceTexture is - * created in a state where it is considered attached to an OpenGL ES - * context for the purposes of the attachToContext and detachFromContext - * methods. However, despite being considered "attached" to a context, the - * specific OpenGL ES context doesn't get latched until the first call to - * updateTexImage. After that point, all calls to updateTexImage must be - * made with the same OpenGL ES context current. - * - * If the constructor without the tex parameter is used, the SurfaceTexture is - * created in a detached state, and attachToContext must be called before - * calls to updateTexImage. - */ - SurfaceTexture(const sp& bq, uint32_t tex, uint32_t texureTarget, - bool useFenceSync, bool isControlledByApp); - - SurfaceTexture(const sp& bq, uint32_t texureTarget, bool useFenceSync, - bool isControlledByApp); - - /** - * updateTexImage acquires the most recently queued buffer, and sets the - * image contents of the target texture to it. - * - * This call may only be made while the OpenGL ES context to which the - * target texture belongs is bound to the calling thread. - * - * This calls doGLFenceWait to ensure proper synchronization. - */ - status_t updateTexImage(); - - /** - * releaseTexImage releases the texture acquired in updateTexImage(). - * This is intended to be used in single buffer mode. - * - * This call may only be made while the OpenGL ES context to which the - * target texture belongs is bound to the calling thread. - */ - status_t releaseTexImage(); - - /** - * getTransformMatrix retrieves the 4x4 texture coordinate transform matrix - * associated with the texture image set by the most recent call to - * updateTexImage. - * - * This transform matrix maps 2D homogeneous texture coordinates of the form - * (s, t, 0, 1) with s and t in the inclusive range [0, 1] to the texture - * coordinate that should be used to sample that location from the texture. - * Sampling the texture outside of the range of this transform is undefined. - * - * This transform is necessary to compensate for transforms that the stream - * content producer may implicitly apply to the content. By forcing users of - * a SurfaceTexture to apply this transform we avoid performing an extra - * copy of the data that would be needed to hide the transform from the - * user. - * - * The matrix is stored in column-major order so that it may be passed - * directly to OpenGL ES via the glLoadMatrixf or glUniformMatrix4fv - * functions. - */ - void getTransformMatrix(float mtx[16]); - - /** - * Computes the transform matrix documented by getTransformMatrix - * from the BufferItem sub parts. - */ - static void computeTransformMatrix(float outTransform[16], const sp& buf, - const Rect& cropRect, uint32_t transform, bool filtering); - - /** - * Scale the crop down horizontally or vertically such that it has the - * same aspect ratio as the buffer does. - */ - static Rect scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight); - - /** - * getTimestamp retrieves the timestamp associated with the texture image - * set by the most recent call to updateTexImage. - * - * The timestamp is in nanoseconds, and is monotonically increasing. Its - * other semantics (zero point, etc) are source-dependent and should be - * documented by the source. - */ - int64_t getTimestamp(); - - /** - * getDataSpace retrieves the DataSpace associated with the texture image - * set by the most recent call to updateTexImage. - */ - android_dataspace getCurrentDataSpace(); - - /** - * getFrameNumber retrieves the frame number associated with the texture - * image set by the most recent call to updateTexImage. - * - * The frame number is an incrementing counter set to 0 at the creation of - * the BufferQueue associated with this consumer. - */ - uint64_t getFrameNumber(); - - /** - * setDefaultBufferSize is used to set the size of buffers returned by - * requestBuffers when a with and height of zero is requested. - * A call to setDefaultBufferSize() may trigger requestBuffers() to - * be called from the client. - * The width and height parameters must be no greater than the minimum of - * GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv). - * An error due to invalid dimensions might not be reported until - * updateTexImage() is called. - */ - status_t setDefaultBufferSize(uint32_t width, uint32_t height); - - /** - * setFilteringEnabled sets whether the transform matrix should be computed - * for use with bilinear filtering. - */ - void setFilteringEnabled(bool enabled); - - /** - * getCurrentTextureTarget returns the texture target of the current - * texture as returned by updateTexImage(). - */ - uint32_t getCurrentTextureTarget() const; - - /** - * getCurrentCrop returns the cropping rectangle of the current buffer. - */ - Rect getCurrentCrop() const; - - /** - * getCurrentTransform returns the transform of the current buffer. - */ - uint32_t getCurrentTransform() const; - - /** - * getCurrentScalingMode returns the scaling mode of the current buffer. - */ - uint32_t getCurrentScalingMode() const; - - /** - * getCurrentFence returns the fence indicating when the current buffer is - * ready to be read from. - */ - sp getCurrentFence() const; - - /** - * getCurrentFence returns the FenceTime indicating when the current - * buffer is ready to be read from. - */ - std::shared_ptr getCurrentFenceTime() const; - - /** - * setConsumerUsageBits overrides the ConsumerBase method to OR - * DEFAULT_USAGE_FLAGS to usage. - */ - status_t setConsumerUsageBits(uint64_t usage); - - /** - * detachFromContext detaches the SurfaceTexture from the calling thread's - * current OpenGL ES context. This context must be the same as the context - * that was current for previous calls to updateTexImage. - * - * Detaching a SurfaceTexture from an OpenGL ES context will result in the - * deletion of the OpenGL ES texture object into which the images were being - * streamed. After a SurfaceTexture has been detached from the OpenGL ES - * context calls to updateTexImage will fail returning INVALID_OPERATION - * until the SurfaceTexture is attached to a new OpenGL ES context using the - * attachToContext method. - */ - status_t detachFromContext(); - - /** - * attachToContext attaches a SurfaceTexture that is currently in the - * 'detached' state to the current OpenGL ES context. A SurfaceTexture is - * in the 'detached' state iff detachFromContext has successfully been - * called and no calls to attachToContext have succeeded since the last - * detachFromContext call. Calls to attachToContext made on a - * SurfaceTexture that is not in the 'detached' state will result in an - * INVALID_OPERATION error. - * - * The tex argument specifies the OpenGL ES texture object name in the - * new context into which the image contents will be streamed. A successful - * call to attachToContext will result in this texture object being bound to - * the texture target and populated with the image contents that were - * current at the time of the last call to detachFromContext. - */ - status_t attachToContext(uint32_t tex); - - sk_sp dequeueImage(SkMatrix& transformMatrix, bool* queueEmpty, - uirenderer::RenderState& renderState); - - /** - * attachToView attaches a SurfaceTexture that is currently in the - * 'detached' state to HWUI View system. - */ - void attachToView(); - - /** - * detachFromView detaches a SurfaceTexture from HWUI View system. - */ - void detachFromView(); - -protected: - /** - * abandonLocked overrides the ConsumerBase method to clear - * mCurrentTextureImage in addition to the ConsumerBase behavior. - */ - virtual void abandonLocked(); - - /** - * dumpLocked overrides the ConsumerBase method to dump SurfaceTexture- - * specific info in addition to the ConsumerBase behavior. - */ - virtual void dumpLocked(String8& result, const char* prefix) const override; - - /** - * acquireBufferLocked overrides the ConsumerBase method to update the - * mEglSlots array in addition to the ConsumerBase behavior. - */ - virtual status_t acquireBufferLocked(BufferItem* item, nsecs_t presentWhen, - uint64_t maxFrameNumber = 0) override; - - /** - * releaseBufferLocked overrides the ConsumerBase method to update the - * mEglSlots array in addition to the ConsumerBase. - */ - virtual status_t releaseBufferLocked(int slot, const sp graphicBuffer, - EGLDisplay display, EGLSyncKHR eglFence) override; - - /** - * freeBufferLocked frees up the given buffer slot. If the slot has been - * initialized this will release the reference to the GraphicBuffer in that - * slot and destroy the EGLImage in that slot. Otherwise it has no effect. - * - * This method must be called with mMutex locked. - */ - virtual void freeBufferLocked(int slotIndex); - - /** - * computeCurrentTransformMatrixLocked computes the transform matrix for the - * current texture. It uses mCurrentTransform and the current GraphicBuffer - * to compute this matrix and stores it in mCurrentTransformMatrix. - * mCurrentTextureImage must not be NULL. - */ - void computeCurrentTransformMatrixLocked(); - - /** - * The default consumer usage flags that SurfaceTexture always sets on its - * BufferQueue instance; these will be OR:d with any additional flags passed - * from the SurfaceTexture user. In particular, SurfaceTexture will always - * consume buffers as hardware textures. - */ - static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE; - - /** - * mCurrentCrop is the crop rectangle that applies to the current texture. - * It gets set each time updateTexImage is called. - */ - Rect mCurrentCrop; - - /** - * mCurrentTransform is the transform identifier for the current texture. It - * gets set each time updateTexImage is called. - */ - uint32_t mCurrentTransform; - - /** - * mCurrentScalingMode is the scaling mode for the current texture. It gets - * set each time updateTexImage is called. - */ - uint32_t mCurrentScalingMode; - - /** - * mCurrentFence is the fence received from BufferQueue in updateTexImage. - */ - sp mCurrentFence; - - /** - * The FenceTime wrapper around mCurrentFence. - */ - std::shared_ptr mCurrentFenceTime{FenceTime::NO_FENCE}; - - /** - * mCurrentTransformMatrix is the transform matrix for the current texture. - * It gets computed by computeTransformMatrix each time updateTexImage is - * called. - */ - float mCurrentTransformMatrix[16]; - - /** - * mCurrentTimestamp is the timestamp for the current texture. It - * gets set each time updateTexImage is called. - */ - int64_t mCurrentTimestamp; - - /** - * mCurrentDataSpace is the dataspace for the current texture. It - * gets set each time updateTexImage is called. - */ - android_dataspace mCurrentDataSpace; - - /** - * mCurrentFrameNumber is the frame counter for the current texture. - * It gets set each time updateTexImage is called. - */ - uint64_t mCurrentFrameNumber; - - uint32_t mDefaultWidth, mDefaultHeight; - - /** - * mFilteringEnabled indicates whether the transform matrix is computed for - * use with bilinear filtering. It defaults to true and is changed by - * setFilteringEnabled(). - */ - bool mFilteringEnabled; - - /** - * mTexName is the name of the OpenGL texture to which streamed images will - * be bound when updateTexImage is called. It is set at construction time - * and can be changed with a call to attachToContext. - */ - uint32_t mTexName; - - /** - * mUseFenceSync indicates whether creation of the EGL_KHR_fence_sync - * extension should be used to prevent buffers from being dequeued before - * it's safe for them to be written. It gets set at construction time and - * never changes. - */ - const bool mUseFenceSync; - - /** - * mTexTarget is the GL texture target with which the GL texture object is - * associated. It is set in the constructor and never changed. It is - * almost always GL_TEXTURE_EXTERNAL_OES except for one use case in Android - * Browser. In that case it is set to GL_TEXTURE_2D to allow - * glCopyTexSubImage to read from the texture. This is a hack to work - * around a GL driver limitation on the number of FBO attachments, which the - * browser's tile cache exceeds. - */ - const uint32_t mTexTarget; - - /** - * mCurrentTexture is the buffer slot index of the buffer that is currently - * bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT, - * indicating that no buffer slot is currently bound to the texture. Note, - * however, that a value of INVALID_BUFFER_SLOT does not necessarily mean - * that no buffer is bound to the texture. A call to setBufferCount will - * reset mCurrentTexture to INVALID_BUFFER_SLOT. - */ - int mCurrentTexture; - - enum class OpMode { detached, attachedToView, attachedToGL }; - /** - * mOpMode indicates whether the SurfaceTexture is currently attached to - * an OpenGL ES context or the HWUI view system. For legacy reasons, this is initialized to, - * "attachedToGL" indicating that the SurfaceTexture is considered to be attached to - * whatever GL context is current at the time of the first updateTexImage call. - * It is set to "detached" by detachFromContext, and then set to "attachedToGL" again by - * attachToContext. - * attachToView/detachFromView are used to attach/detach from HWUI view system. - */ - OpMode mOpMode; - - /** - * mEGLConsumer has SurfaceTexture logic used when attached to GL context. - */ - EGLConsumer mEGLConsumer; - - /** - * mImageConsumer has SurfaceTexture logic used when attached to HWUI view system. - */ - ImageConsumer mImageConsumer; - - friend class ImageConsumer; - friend class EGLConsumer; -}; - -// ---------------------------------------------------------------------------- -} // namespace android diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index e7124df72beb..91a808df3657 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -29,6 +29,7 @@ #include #include +#include namespace android { namespace uirenderer { -- cgit v1.2.3 From dc19a652971a799545de7489c2fb712ab65dc0fb Mon Sep 17 00:00:00 2001 From: Nathaniel Nifong Date: Mon, 11 Nov 2019 11:47:50 -0500 Subject: Remove unused argument 'layers' from renderFrameImpl and renderOverdraw Test: Flashed Pixel 3 and viewed home screen. Change-Id: I2cd38e33cdc13c25c270c355702e8c4c842b105a --- libs/hwui/pipeline/skia/SkiaPipeline.cpp | 10 +++++----- libs/hwui/pipeline/skia/SkiaPipeline.h | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'libs') diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 87ef7fc9a6e1..06479776fc73 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -366,12 +366,12 @@ void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& cli // capture is enabled. SkCanvas* canvas = tryCapture(surface.get()); - renderFrameImpl(layers, clip, nodes, opaque, contentDrawBounds, canvas, preTransform); + renderFrameImpl(clip, nodes, opaque, contentDrawBounds, canvas, preTransform); endCapture(surface.get()); if (CC_UNLIKELY(Properties::debugOverdraw)) { - renderOverdraw(layers, clip, nodes, contentDrawBounds, surface, preTransform); + renderOverdraw(clip, nodes, contentDrawBounds, surface, preTransform); } ATRACE_NAME("flush commands"); @@ -387,7 +387,7 @@ static Rect nodeBounds(RenderNode& node) { } } // namespace -void SkiaPipeline::renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip, +void SkiaPipeline::renderFrameImpl(const SkRect& clip, const std::vector>& nodes, bool opaque, const Rect& contentDrawBounds, SkCanvas* canvas, const SkMatrix& preTransform) { @@ -539,7 +539,7 @@ static const uint32_t kOverdrawColors[2][6] = { }, }; -void SkiaPipeline::renderOverdraw(const LayerUpdateQueue& layers, const SkRect& clip, +void SkiaPipeline::renderOverdraw(const SkRect& clip, const std::vector>& nodes, const Rect& contentDrawBounds, sk_sp surface, const SkMatrix& preTransform) { @@ -553,7 +553,7 @@ void SkiaPipeline::renderOverdraw(const LayerUpdateQueue& layers, const SkRect& // each time a pixel would have been drawn. // Pass true for opaque so we skip the clear - the overdrawCanvas is already zero // initialized. - renderFrameImpl(layers, clip, nodes, true, contentDrawBounds, &overdrawCanvas, preTransform); + renderFrameImpl(clip, nodes, true, contentDrawBounds, &overdrawCanvas, preTransform); sk_sp counts = offscreen->makeImageSnapshot(); // Draw overdraw colors to the canvas. The color filter will convert counts to colors. diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h index 215ff36ebb2b..7d575ad01936 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaPipeline.h @@ -77,7 +77,7 @@ protected: sk_sp mSurfaceColorSpace; private: - void renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip, + void renderFrameImpl(const SkRect& clip, const std::vector>& nodes, bool opaque, const Rect& contentDrawBounds, SkCanvas* canvas, const SkMatrix& preTransform); @@ -86,7 +86,7 @@ private: * Debugging feature. Draws a semi-transparent overlay on each pixel, indicating * how many times it has been drawn. */ - void renderOverdraw(const LayerUpdateQueue& layers, const SkRect& clip, + void renderOverdraw(const SkRect& clip, const std::vector>& nodes, const Rect& contentDrawBounds, sk_sp surface, const SkMatrix& preTransform); -- cgit v1.2.3 From 10689992cf793bd21873cd2e57e3d3681650b952 Mon Sep 17 00:00:00 2001 From: Stan Iliev Date: Wed, 13 Nov 2019 10:25:22 -0500 Subject: Fix an issue with GPU stats not enabled Invoke Surface::enableFrameTimestamps after eglDestroySurface. eglDestroySurface internally disables time stats. Order is important, when CanvasContext::setSurface is invoked with a surface, that is already current (which happens all the time). Test: ran UiBench microbenchmark tests Change-Id: I3d023c3a87da6329c556426d553c744e541b9dff --- libs/hwui/renderthread/CanvasContext.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 826a8ea09d7e..f0867686c321 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -147,7 +147,6 @@ void CanvasContext::setSurface(sp&& surface) { mNativeSurface = new ReliableSurface{std::move(surface)}; // TODO: Fix error handling & re-shorten timeout ANativeWindow_setDequeueTimeout(mNativeSurface.get(), 4000_ms); - mNativeSurface->enableFrameTimestamps(true); } else { mNativeSurface = nullptr; } @@ -169,6 +168,10 @@ void CanvasContext::setSurface(sp&& surface) { if (hasSurface) { mHaveNewSurface = true; mSwapHistory.clear(); + // Enable frame stats after the surface has been bound to the appropriate graphics API. + // Order is important when new and old surfaces are the same, because old surface has + // its frame stats disabled automatically. + mNativeSurface->enableFrameTimestamps(true); } else { mRenderThread.removeFrameCallback(this); mGenerationID++; -- cgit v1.2.3 From 5918429fa2ee09f44fda76978abb1302061b1e2f Mon Sep 17 00:00:00 2001 From: Tej Singh Date: Fri, 11 Oct 2019 11:07:06 -0700 Subject: Java API for pulled atoms This creates a java API for registering pullers. Will implement the statsd side in a follow up CL. Test: builds, boots Change-Id: Ib6735984297ce3148839a6370a3c15b2a585baf5 --- libs/services/Android.bp | 1 + libs/services/include/android/util/StatsEvent.h | 43 ++++++++++++++++++ libs/services/src/util/StatsEvent.cpp | 58 +++++++++++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 libs/services/include/android/util/StatsEvent.h create mode 100644 libs/services/src/util/StatsEvent.cpp (limited to 'libs') diff --git a/libs/services/Android.bp b/libs/services/Android.bp index 1b9939d9a598..b0fad57dfd29 100644 --- a/libs/services/Android.bp +++ b/libs/services/Android.bp @@ -22,6 +22,7 @@ cc_library_shared { "src/os/DropBoxManager.cpp", "src/os/StatsDimensionsValue.cpp", "src/os/StatsLogEventWrapper.cpp", + "src/util/StatsEvent.cpp", ], shared_libs: [ diff --git a/libs/services/include/android/util/StatsEvent.h b/libs/services/include/android/util/StatsEvent.h new file mode 100644 index 000000000000..48631174f7dd --- /dev/null +++ b/libs/services/include/android/util/StatsEvent.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2019 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 STATS_EVENT_H +#define STATS_EVENT_H + +#include +#include +#include +#include + +namespace android { +namespace util { +class StatsEvent : public android::Parcelable { + public: + StatsEvent(); + + StatsEvent(StatsEvent&& in) = default; + + android::status_t writeToParcel(android::Parcel* out) const; + + android::status_t readFromParcel(const android::Parcel* in); + + private: + int mAtomTag; + std::vector mBuffer; +}; +} // Namespace util +} // Namespace android + +#endif // STATS_ EVENT_H \ No newline at end of file diff --git a/libs/services/src/util/StatsEvent.cpp b/libs/services/src/util/StatsEvent.cpp new file mode 100644 index 000000000000..8b8579167b00 --- /dev/null +++ b/libs/services/src/util/StatsEvent.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2019 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 + +#include +#include +#include +#include + +using android::Parcel; +using android::Parcelable; +using android::status_t; +using std::vector; + +namespace android { +namespace util { + +StatsEvent::StatsEvent(){}; + +status_t StatsEvent::writeToParcel(Parcel* out) const { + // Implement me if desired. We don't currently use this. + ALOGE("Cannot do c++ StatsEvent.writeToParcel(); it is not implemented."); + (void)out; // To prevent compile error of unused parameter 'out' + return UNKNOWN_ERROR; +}; + +status_t StatsEvent::readFromParcel(const Parcel* in) { + status_t res = OK; + if (in == NULL) { + ALOGE("statsd received parcel argument was NULL."); + return BAD_VALUE; + } + if ((res = in->readInt32(&mAtomTag)) != OK) { + ALOGE("statsd could not read atom tag from parcel"); + return res; + } + if ((res = in->readByteVector(&mBuffer)) != OK) { + ALOGE("statsd could not read buffer from parcel"); + return res; + } + return NO_ERROR; +}; + +} // Namespace util +} // Namespace android -- cgit v1.2.3 From 73bfe41bab9b06a3d0c4e73c61928982b37b35eb Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Tue, 12 Nov 2019 16:22:04 -0800 Subject: Do not look for R.class of overlays Now that RROs are loaded as shared libraries, LoadedApk#makeApplication is attempting to find the onResourcesLoaded method of the overlays. This is a performance hit and causes more memory than necessary to be allocated during application start up. Bug: 143314947 Test: com.android.performance.tests.HermeticMemoryTest Change-Id: I3b8cd22dae83e0164d6678c80279f9fffceb34e6 --- libs/androidfw/ApkAssets.cpp | 49 +++++++++++--------- libs/androidfw/Idmap.cpp | 17 +++---- libs/androidfw/LoadedArsc.cpp | 41 +++++++++-------- libs/androidfw/include/androidfw/ApkAssets.h | 21 +++++---- libs/androidfw/include/androidfw/AssetManager2.h | 9 ++-- libs/androidfw/include/androidfw/LoadedArsc.h | 57 ++++++++++++------------ libs/androidfw/tests/LoadedArsc_test.cpp | 9 ++-- 7 files changed, 104 insertions(+), 99 deletions(-) (limited to 'libs') diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index 16dbbf61351a..18934fd55bad 100644 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -43,20 +43,22 @@ static const std::string kResourcesArsc("resources.arsc"); ApkAssets::ApkAssets(ZipArchiveHandle unmanaged_handle, const std::string& path, time_t last_mod_time, - bool for_loader) + package_property_t property_flags) : zip_handle_(unmanaged_handle, ::CloseArchive), path_(path), last_mod_time_(last_mod_time), - for_loader_(for_loader) { + property_flags_(property_flags) { } std::unique_ptr ApkAssets::Load(const std::string& path, bool system, bool for_loader) { - return LoadImpl({} /*fd*/, path, nullptr, nullptr, system, false /*load_as_shared_library*/, - for_loader); + package_property_t flags = (system ? PROPERTY_SYSTEM : 0U) | + (for_loader ? PROPERTY_LOADER : 0U); + return LoadImpl({} /*fd*/, path, nullptr, nullptr, flags); } std::unique_ptr ApkAssets::LoadAsSharedLibrary(const std::string& path, bool system) { - return LoadImpl({} /*fd*/, path, nullptr, nullptr, system, true /*load_as_shared_library*/); + package_property_t flags = PROPERTY_DYNAMIC | (system ? PROPERTY_SYSTEM : 0U); + return LoadImpl({} /*fd*/, path, nullptr, nullptr, flags); } std::unique_ptr ApkAssets::LoadOverlay(const std::string& idmap_path, @@ -74,27 +76,33 @@ std::unique_ptr ApkAssets::LoadOverlay(const std::string& idmap LOG(ERROR) << "failed to load IDMAP " << idmap_path; return {}; } - return LoadImpl({} /*fd*/, loaded_idmap->OverlayApkPath(), std::move(idmap_asset), - std::move(loaded_idmap), system, true /*load_as_shared_library*/); + + return LoadImpl({} /*fd*/, loaded_idmap->OverlayApkPath(), + std::move(idmap_asset), + std::move(loaded_idmap), + PROPERTY_OVERLAY | (system ? PROPERTY_SYSTEM : 0U)); } std::unique_ptr ApkAssets::LoadFromFd(unique_fd fd, const std::string& friendly_name, bool system, bool force_shared_lib, bool for_loader) { + package_property_t flags = (system ? PROPERTY_SYSTEM : 0U) | + (force_shared_lib ? PROPERTY_DYNAMIC : 0U) | + (for_loader ? PROPERTY_LOADER : 0U); return LoadImpl(std::move(fd), friendly_name, nullptr /*idmap_asset*/, nullptr /*loaded_idmap*/, - system, force_shared_lib, for_loader); + flags); } std::unique_ptr ApkAssets::LoadArsc(const std::string& path, bool for_loader) { - return LoadArscImpl({} /*fd*/, path, for_loader); + return LoadArscImpl({} /*fd*/, path, for_loader ? PROPERTY_LOADER : 0U); } std::unique_ptr ApkAssets::LoadArsc(unique_fd fd, const std::string& friendly_name, bool for_loader) { - return LoadArscImpl(std::move(fd), friendly_name, for_loader); + return LoadArscImpl(std::move(fd), friendly_name, for_loader ? PROPERTY_LOADER : 0U); } std::unique_ptr ApkAssets::CreateAssetFromFile(const std::string& path) { @@ -120,8 +128,7 @@ std::unique_ptr ApkAssets::CreateAssetFromFile(const std::string& path) { std::unique_ptr ApkAssets::LoadImpl( unique_fd fd, const std::string& path, std::unique_ptr idmap_asset, - std::unique_ptr loaded_idmap, bool system, bool load_as_shared_library, - bool for_loader) { + std::unique_ptr loaded_idmap, package_property_t property_flags) { ::ZipArchiveHandle unmanaged_handle; int32_t result; if (fd >= 0) { @@ -141,7 +148,7 @@ std::unique_ptr ApkAssets::LoadImpl( // Wrap the handle in a unique_ptr so it gets automatically closed. std::unique_ptr - loaded_apk(new ApkAssets(unmanaged_handle, path, last_mod_time, for_loader)); + loaded_apk(new ApkAssets(unmanaged_handle, path, last_mod_time, property_flags)); // Find the resource table. ::ZipEntry entry; @@ -170,9 +177,8 @@ std::unique_ptr ApkAssets::LoadImpl( const StringPiece data( reinterpret_cast(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)), loaded_apk->resources_asset_->getLength()); - loaded_apk->loaded_arsc_ = - LoadedArsc::Load(data, loaded_apk->loaded_idmap_.get(), system, load_as_shared_library, - for_loader); + loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, loaded_apk->loaded_idmap_.get(), + property_flags); if (loaded_apk->loaded_arsc_ == nullptr) { LOG(ERROR) << "Failed to load '" << kResourcesArsc << "' in APK '" << path << "'."; return {}; @@ -184,7 +190,7 @@ std::unique_ptr ApkAssets::LoadImpl( std::unique_ptr ApkAssets::LoadArscImpl(unique_fd fd, const std::string& path, - bool for_loader) { + package_property_t property_flags) { std::unique_ptr resources_asset; if (fd >= 0) { @@ -201,13 +207,14 @@ std::unique_ptr ApkAssets::LoadArscImpl(unique_fd fd, time_t last_mod_time = getFileModDate(path.c_str()); - std::unique_ptr loaded_apk(new ApkAssets(nullptr, path, last_mod_time, for_loader)); + std::unique_ptr loaded_apk( + new ApkAssets(nullptr, path, last_mod_time, property_flags)); loaded_apk->resources_asset_ = std::move(resources_asset); const StringPiece data( reinterpret_cast(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)), loaded_apk->resources_asset_->getLength()); - loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, nullptr, false, false, for_loader); + loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, nullptr, property_flags); if (loaded_apk->loaded_arsc_ == nullptr) { LOG(ERROR) << "Failed to load '" << kResourcesArsc << path; return {}; @@ -320,8 +327,8 @@ bool ApkAssets::ForEachFile(const std::string& root_path, } bool ApkAssets::IsUpToDate() const { - // Loaders are invalidated by the app, not the system, so assume up to date - if (for_loader_) { + if (IsLoader()) { + // Loaders are invalidated by the app, not the system, so assume up to date. return true; } diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp index 2b69c923597f..773353d32d51 100644 --- a/libs/androidfw/Idmap.cpp +++ b/libs/androidfw/Idmap.cpp @@ -44,8 +44,8 @@ static bool compare_overlay_entries(const Idmap_overlay_entry& e1, const uint32_ } OverlayStringPool::OverlayStringPool(const LoadedIdmap* loaded_idmap) - : data_header_(loaded_idmap->data_header_), - idmap_string_pool_(loaded_idmap->string_pool_.get()) { }; + : data_header_(loaded_idmap->data_header_), + idmap_string_pool_(loaded_idmap->string_pool_.get()) { }; OverlayStringPool::~OverlayStringPool() { uninit(); @@ -188,11 +188,12 @@ LoadedIdmap::LoadedIdmap(const Idmap_header* header, const Idmap_data_header* data_header, const Idmap_target_entry* target_entries, const Idmap_overlay_entry* overlay_entries, - ResStringPool* string_pool) : header_(header), - data_header_(data_header), - target_entries_(target_entries), - overlay_entries_(overlay_entries), - string_pool_(string_pool) { + ResStringPool* string_pool) + : header_(header), + data_header_(data_header), + target_entries_(target_entries), + overlay_entries_(overlay_entries), + string_pool_(string_pool) { size_t length = strnlen(reinterpret_cast(header_->overlay_path), arraysize(header_->overlay_path)); @@ -264,7 +265,7 @@ std::unique_ptr LoadedIdmap::Load(const StringPiece& idmap_da } } - // Can't use make_unique because LoadedImpl constructor is private. + // Can't use make_unique because LoadedIdmap constructor is private. std::unique_ptr loaded_idmap = std::unique_ptr( new LoadedIdmap(header, data_header, target_entries, overlay_entries, idmap_string_pool.release())); diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index c8962416d082..e35c0249fbdf 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -397,9 +397,7 @@ const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const { } std::unique_ptr LoadedPackage::Load(const Chunk& chunk, - bool system, - bool load_as_shared_library, - bool for_loader) { + package_property_t property_flags) { ATRACE_NAME("LoadedPackage::Load"); std::unique_ptr loaded_package(new LoadedPackage()); @@ -413,17 +411,24 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, return {}; } - loaded_package->system_ = system; + if ((property_flags & PROPERTY_SYSTEM) != 0) { + loaded_package->property_flags_ |= PROPERTY_SYSTEM; + } - loaded_package->package_id_ = dtohl(header->id); - if (loaded_package->package_id_ == 0 || - (loaded_package->package_id_ == kAppPackageId && load_as_shared_library)) { - // Package ID of 0 means this is a shared library. - loaded_package->dynamic_ = true; + if ((property_flags & PROPERTY_LOADER) != 0) { + loaded_package->property_flags_ |= PROPERTY_LOADER; } - if (for_loader) { - loaded_package->custom_loader_ = true; + if ((property_flags & PROPERTY_OVERLAY) != 0) { + // Overlay resources must have an exclusive resource id space for referencing internal + // resources. + loaded_package->property_flags_ |= PROPERTY_OVERLAY | PROPERTY_DYNAMIC; + } + + loaded_package->package_id_ = dtohl(header->id); + if (loaded_package->package_id_ == 0 || + (loaded_package->package_id_ == kAppPackageId && (property_flags & PROPERTY_DYNAMIC) != 0)) { + loaded_package->property_flags_ |= PROPERTY_DYNAMIC; } if (header->header.headerSize >= sizeof(ResTable_package)) { @@ -677,7 +682,7 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, } bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, - bool load_as_shared_library, bool for_loader) { + package_property_t property_flags) { const ResTable_header* header = chunk.header(); if (header == nullptr) { LOG(ERROR) << "RES_TABLE_TYPE too small."; @@ -720,7 +725,7 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, packages_seen++; std::unique_ptr loaded_package = - LoadedPackage::Load(child_chunk, system_, load_as_shared_library, for_loader); + LoadedPackage::Load(child_chunk, property_flags); if (!loaded_package) { return false; } @@ -744,24 +749,18 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, std::unique_ptr LoadedArsc::Load(const StringPiece& data, const LoadedIdmap* loaded_idmap, - bool system, - bool load_as_shared_library, - bool for_loader) { + package_property_t property_flags) { ATRACE_NAME("LoadedArsc::Load"); // Not using make_unique because the constructor is private. std::unique_ptr loaded_arsc(new LoadedArsc()); - loaded_arsc->system_ = system; ChunkIterator iter(data.data(), data.size()); while (iter.HasNext()) { const Chunk chunk = iter.Next(); switch (chunk.type()) { case RES_TABLE_TYPE: - if (!loaded_arsc->LoadTable(chunk, - loaded_idmap, - load_as_shared_library, - for_loader)) { + if (!loaded_arsc->LoadTable(chunk, loaded_idmap, property_flags)) { return {}; } break; diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h index 20472872263e..af802b0e50b9 100644 --- a/libs/androidfw/include/androidfw/ApkAssets.h +++ b/libs/androidfw/include/androidfw/ApkAssets.h @@ -76,10 +76,10 @@ class ApkAssets { // Takes ownership of the file descriptor. static std::unique_ptr LoadArsc(base::unique_fd fd, const std::string& friendly_name, - bool resource_loader = false); + bool for_loader = false); // Creates a totally empty ApkAssets with no resources table and no file entries. - static std::unique_ptr LoadEmpty(bool resource_loader = false); + static std::unique_ptr LoadEmpty(bool for_loader = false); std::unique_ptr Open(const std::string& path, Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM) const; @@ -100,12 +100,12 @@ class ApkAssets { return loaded_idmap_.get(); } - inline bool IsOverlay() const { - return idmap_asset_.get() != nullptr; + inline bool IsLoader() const { + return (property_flags_ & PROPERTY_LOADER) != 0; } - inline bool IsLoader() const { - return for_loader_; + inline bool IsOverlay() const { + return (property_flags_ & PROPERTY_OVERLAY) != 0; } bool IsUpToDate() const; @@ -119,24 +119,23 @@ class ApkAssets { static std::unique_ptr LoadImpl(base::unique_fd fd, const std::string& path, std::unique_ptr idmap_asset, std::unique_ptr loaded_idmap, - bool system, bool load_as_shared_library, - bool resource_loader = false); + package_property_t property_flags); static std::unique_ptr LoadArscImpl(base::unique_fd fd, const std::string& path, - bool resource_loader = false); + package_property_t property_flags); ApkAssets(ZipArchiveHandle unmanaged_handle, const std::string& path, time_t last_mod_time, - bool for_loader = false); + package_property_t property_flags); using ZipArchivePtr = std::unique_ptr; ZipArchivePtr zip_handle_; const std::string path_; time_t last_mod_time_; - bool for_loader_; + package_property_t property_flags_ = 0U; std::unique_ptr resources_asset_; std::unique_ptr idmap_asset_; std::unique_ptr loaded_arsc_; diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index 20e40234b418..00cbbcad56e6 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -263,10 +263,13 @@ class AssetManager2 { // Creates a new Theme from this AssetManager. std::unique_ptr NewTheme(); - void ForEachPackage(const std::function func) const { + void ForEachPackage(const std::function func, + package_property_t excluded_property_flags = 0U) const { for (const PackageGroup& package_group : package_groups_) { - if (!func(package_group.packages_.front().loaded_package_->GetPackageName(), - package_group.dynamic_ref_table->mAssignedPackageId)) { + const auto loaded_package = package_group.packages_.front().loaded_package_; + if ((loaded_package->GetPropertyFlags() & excluded_property_flags) == 0U + && !func(loaded_package->GetPackageName(), + package_group.dynamic_ref_table->mAssignedPackageId)) { return; } } diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index ba1beaa7827c..6cbda07b6950 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -69,6 +69,14 @@ struct TypeSpec { } }; +using package_property_t = uint32_t; +enum : package_property_t { + PROPERTY_DYNAMIC = 1, + PROPERTY_LOADER = 2, + PROPERTY_OVERLAY = 4, + PROPERTY_SYSTEM = 8, +}; + // TypeSpecPtr points to a block of memory that holds a TypeSpec struct, followed by an array of // ResTable_type pointers. // TypeSpecPtr is a managed pointer that knows how to delete itself. @@ -131,9 +139,8 @@ class LoadedPackage { return iterator(this, resource_ids_.size() + 1, 0); } - static std::unique_ptr Load(const Chunk& chunk, bool system, - bool load_as_shared_library, - bool load_as_custom_loader); + static std::unique_ptr Load(const Chunk& chunk, + package_property_t property_flags); ~LoadedPackage(); @@ -170,17 +177,26 @@ class LoadedPackage { // Returns true if this package is dynamic (shared library) and needs to have an ID assigned. inline bool IsDynamic() const { - return dynamic_; + return (property_flags_ & PROPERTY_DYNAMIC) != 0; + } + + // Returns true if this package is a Runtime Resource Overlay. + inline bool IsOverlay() const { + return (property_flags_ & PROPERTY_OVERLAY) != 0; } // Returns true if this package originates from a system provided resource. inline bool IsSystem() const { - return system_; + return (property_flags_ & PROPERTY_SYSTEM) != 0; } - // Returns true if this package is a custom loader and should behave like an overlay + // Returns true if this package is a custom loader and should behave like an overlay. inline bool IsCustomLoader() const { - return custom_loader_; + return (property_flags_ & PROPERTY_LOADER) != 0; + } + + inline package_property_t GetPropertyFlags() const { + return property_flags_; } // Returns the map of package name to package ID used in this LoadedPackage. At runtime, a @@ -248,12 +264,10 @@ class LoadedPackage { ResStringPool type_string_pool_; ResStringPool key_string_pool_; std::string package_name_; + bool defines_overlayable_ = false; int package_id_ = -1; int type_id_offset_ = 0; - bool dynamic_ = false; - bool system_ = false; - bool custom_loader_ = false; - bool defines_overlayable_ = false; + package_property_t property_flags_ = 0U; ByteBucketArray type_specs_; ByteBucketArray resource_ids_; @@ -274,9 +288,7 @@ class LoadedArsc { // ID. static std::unique_ptr Load(const StringPiece& data, const LoadedIdmap* loaded_idmap = nullptr, - bool system = false, - bool load_as_shared_library = false, - bool for_loader = false); + package_property_t property_flags = 0U); // Create an empty LoadedArsc. This is used when an APK has no resources.arsc. static std::unique_ptr CreateEmpty(); @@ -296,28 +308,15 @@ class LoadedArsc { return packages_; } - // Returns true if this is a system provided resource. - inline bool IsSystem() const { - return system_; - } - private: DISALLOW_COPY_AND_ASSIGN(LoadedArsc); LoadedArsc() = default; - bool LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, bool load_as_shared_library, - bool for_loader); - - static std::unique_ptr LoadData(std::unique_ptr& loaded_arsc, - const char* data, - size_t length, - const LoadedIdmap* loaded_idmap = nullptr, - bool load_as_shared_library = false, - bool for_loader = false); + bool LoadTable( + const Chunk& chunk, const LoadedIdmap* loaded_idmap, package_property_t property_flags); std::unique_ptr global_string_pool_ = util::make_unique(); std::vector> packages_; - bool system_ = false; }; } // namespace android diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp index 82dd33523c75..8615069e98dd 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -144,8 +144,7 @@ TEST(LoadedArscTest, LoadAppAsSharedLibrary) { "resources.arsc", &contents)); std::unique_ptr loaded_arsc = - LoadedArsc::Load(StringPiece(contents), nullptr /* loaded_idmap */, false /*system*/, - true /*load_as_shared_library*/); + LoadedArsc::Load(StringPiece(contents), nullptr /* loaded_idmap */, PROPERTY_DYNAMIC); ASSERT_THAT(loaded_arsc, NotNull()); const auto& packages = loaded_arsc->GetPackages(); @@ -227,9 +226,7 @@ TEST(LoadedArscTest, LoadOverlayable) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlayable/overlayable.apk", "resources.arsc", &contents)); - std::unique_ptr loaded_arsc = - LoadedArsc::Load(StringPiece(contents), nullptr /* loaded_idmap */, false /*system*/, - false /*load_as_shared_library*/); + std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); ASSERT_THAT(loaded_arsc, NotNull()); const LoadedPackage* package = loaded_arsc->GetPackageById( @@ -346,7 +343,7 @@ TEST(LoadedArscTest, LoadCustomLoader) { asset->getLength()); std::unique_ptr loaded_arsc = - LoadedArsc::Load(data, nullptr, false, false, true); + LoadedArsc::Load(data, nullptr, PROPERTY_LOADER); ASSERT_THAT(loaded_arsc, NotNull()); const LoadedPackage* package = -- cgit v1.2.3 From 70f2a92e4fe79f5e527b06e3456ee3d09e550fb0 Mon Sep 17 00:00:00 2001 From: Alec Mouri Date: Wed, 20 Nov 2019 11:10:29 -0800 Subject: [HWUI] Grab preferred WCG format from ADisplay * Removes the SurfaceComposerClient dependency from DeviceInfo * Removes libui dependencies from DeviceInfo. Bug: 144505134 Test: builds, boots, launch settings Change-Id: I5eb8b296ff274ca2affafedf6f2fd13fd75469b9 --- libs/hwui/DeviceInfo.cpp | 82 +++++++++++++++++++++++------------------------- libs/hwui/DeviceInfo.h | 4 +-- 2 files changed, 41 insertions(+), 45 deletions(-) (limited to 'libs') diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp index 41e9b4be6649..e53f3db08538 100644 --- a/libs/hwui/DeviceInfo.cpp +++ b/libs/hwui/DeviceInfo.cpp @@ -15,9 +15,8 @@ */ #include -#include #include -#include +#include #include #include @@ -32,44 +31,6 @@ DeviceInfo* DeviceInfo::get() { return &sDeviceInfo; } -static void queryWideColorGamutPreference(sk_sp* colorSpace, SkColorType* colorType) { - if (Properties::isolatedProcess) { - *colorSpace = SkColorSpace::MakeSRGB(); - *colorType = SkColorType::kN32_SkColorType; - return; - } - ui::Dataspace defaultDataspace, wcgDataspace; - ui::PixelFormat defaultPixelFormat, wcgPixelFormat; - status_t status = - SurfaceComposerClient::getCompositionPreference(&defaultDataspace, &defaultPixelFormat, - &wcgDataspace, &wcgPixelFormat); - LOG_ALWAYS_FATAL_IF(status, "Failed to get composition preference, error %d", status); - switch (wcgDataspace) { - case ui::Dataspace::DISPLAY_P3: - *colorSpace = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3); - break; - case ui::Dataspace::V0_SCRGB: - *colorSpace = SkColorSpace::MakeSRGB(); - break; - case ui::Dataspace::V0_SRGB: - // when sRGB is returned, it means wide color gamut is not supported. - *colorSpace = SkColorSpace::MakeSRGB(); - break; - default: - LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space."); - } - switch (wcgPixelFormat) { - case ui::PixelFormat::RGBA_8888: - *colorType = SkColorType::kN32_SkColorType; - break; - case ui::PixelFormat::RGBA_FP16: - *colorType = SkColorType::kRGBA_F16_SkColorType; - break; - default: - LOG_ALWAYS_FATAL("Unreachable: unsupported pixel format."); - } -} - DeviceInfo::DeviceInfo() { #if HWUI_NULL_GPU mMaxTextureSize = NULL_GPU_MAX_TEXTURE_SIZE; @@ -77,7 +38,6 @@ DeviceInfo::DeviceInfo() { mMaxTextureSize = -1; #endif updateDisplayInfo(); - queryWideColorGamutPreference(&mWideColorSpace, &mWideColorType); } DeviceInfo::~DeviceInfo() { ADisplay_release(mDisplays); @@ -113,9 +73,45 @@ void DeviceInfo::updateDisplayInfo() { } } LOG_ALWAYS_FATAL_IF(mPhysicalDisplayIndex < 0, "Failed to find a connected physical display!"); - mMaxRefreshRate = ADisplay_getMaxSupportedFps(mDisplays[mPhysicalDisplayIndex]); + + + // Since we now just got the primary display for the first time, then + // store the primary display metadata here. + ADisplay* primaryDisplay = mDisplays[mPhysicalDisplayIndex]; + mMaxRefreshRate = ADisplay_getMaxSupportedFps(primaryDisplay); + ADataSpace dataspace; + AHardwareBuffer_Format format; + ADisplay_getPreferredWideColorFormat(primaryDisplay, &dataspace, &format); + switch (dataspace) { + case ADATASPACE_DISPLAY_P3: + mWideColorSpace = + SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3); + break; + case ADATASPACE_SCRGB: + mWideColorSpace = SkColorSpace::MakeSRGB(); + break; + case ADATASPACE_SRGB: + // when sRGB is returned, it means wide color gamut is not supported. + mWideColorSpace = SkColorSpace::MakeSRGB(); + break; + default: + LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space."); + } + switch (format) { + case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM: + mWideColorType = SkColorType::kN32_SkColorType; + break; + case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT: + mWideColorType = SkColorType::kRGBA_F16_SkColorType; + break; + default: + LOG_ALWAYS_FATAL("Unreachable: unsupported pixel format."); + } } - status_t status = ADisplay_getCurrentConfig(mDisplays[mPhysicalDisplayIndex], &mCurrentConfig); + // This method may have been called when the display config changed, so + // sync with the current configuration. + ADisplay* primaryDisplay = mDisplays[mPhysicalDisplayIndex]; + status_t status = ADisplay_getCurrentConfig(primaryDisplay, &mCurrentConfig); LOG_ALWAYS_FATAL_IF(status, "Failed to get display config, error %d", status); mWidth = ADisplayConfig_getWidth(mCurrentConfig); mHeight = ADisplayConfig_getHeight(mCurrentConfig); diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h index 34315830ed97..a4207460883e 100644 --- a/libs/hwui/DeviceInfo.h +++ b/libs/hwui/DeviceInfo.h @@ -58,8 +58,8 @@ private: ~DeviceInfo(); int mMaxTextureSize; - sk_sp mWideColorSpace; - SkColorType mWideColorType; + sk_sp mWideColorSpace = SkColorSpace::MakeSRGB(); + SkColorType mWideColorType = SkColorType::kN32_SkColorType; ADisplayConfig* mCurrentConfig = nullptr; ADisplay** mDisplays = nullptr; int mDisplaysSize = 0; -- cgit v1.2.3 From d9d17367670eb930d74d2e2ffeeb3c0e9bea0a23 Mon Sep 17 00:00:00 2001 From: Winson Date: Wed, 2 Oct 2019 12:41:29 -0700 Subject: Overlayable actor enforcement Validates that the caller of an OverlayManager API that mutates state is actually allowed to act on the target as defined in the target's overlayable tag. An actor is valid if any of the following is true: - is root/system - is the target overlay package - has the CHANGE_OVERLAY_PACKAGES permission and an actor is not defined - is the same package name as the sole resolved Activity for the actor specified in the overlayable definition, with only pre-installed, namespaced actors currently supported Bug: 119442583 Bug: 135052950 Test: atest SystemConfigNamedActorTest Test: atest com.android.server.om Change-Id: If56b9e8366852eaef84f6bb25c3e6871eaa3f219 --- libs/androidfw/include/androidfw/LoadedArsc.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'libs') diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 6cbda07b6950..b5d3a1fc6c1f 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -273,6 +273,8 @@ class LoadedPackage { ByteBucketArray resource_ids_; std::vector dynamic_package_map_; std::vector>> overlayable_infos_; + + // A map of overlayable name to actor std::unordered_map overlayable_map_; }; -- cgit v1.2.3 From cd18c2271d143fa842f0de40497a4685c52e2548 Mon Sep 17 00:00:00 2001 From: John Reck Date: Thu, 21 Nov 2019 14:40:53 -0800 Subject: Fix for Surface#lockHardwareCanvas lockups By avoiding setting a dequeue buffer timeout we avoid hitting a different path in BufferQueue that prevents async behavior from happening. This restores P's behavior in this path. Bug: 143860379 Test: repro app in bug Change-Id: Iffbd9f9e6689a40876ff3aa74c10020e3f09fc6a --- libs/hwui/renderthread/CanvasContext.cpp | 8 +++++--- libs/hwui/renderthread/CanvasContext.h | 2 +- libs/hwui/renderthread/RenderProxy.cpp | 7 ++++--- libs/hwui/renderthread/RenderProxy.h | 2 +- 4 files changed, 11 insertions(+), 8 deletions(-) (limited to 'libs') diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index f0867686c321..84902210a751 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -140,13 +140,15 @@ void CanvasContext::destroy() { mAnimationContext->destroy(); } -void CanvasContext::setSurface(sp&& surface) { +void CanvasContext::setSurface(sp&& surface, bool enableTimeout) { ATRACE_CALL(); if (surface) { mNativeSurface = new ReliableSurface{std::move(surface)}; - // TODO: Fix error handling & re-shorten timeout - ANativeWindow_setDequeueTimeout(mNativeSurface.get(), 4000_ms); + if (enableTimeout) { + // TODO: Fix error handling & re-shorten timeout + ANativeWindow_setDequeueTimeout(mNativeSurface.get(), 4000_ms); + } } else { mNativeSurface = nullptr; } diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index b192d461da9b..4490f80eb8af 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -111,7 +111,7 @@ public: // Won't take effect until next EGLSurface creation void setSwapBehavior(SwapBehavior swapBehavior); - void setSurface(sp&& surface); + void setSurface(sp&& surface, bool enableTimeout = true); bool pauseSurface(); void setStopped(bool stopped); bool hasSurface() const { return mNativeSurface.get(); } diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 4f7ad7b69f57..f9e401a2e93b 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -78,9 +78,10 @@ void RenderProxy::setName(const char* name) { mRenderThread.queue().runSync([this, name]() { mContext->setName(std::string(name)); }); } -void RenderProxy::setSurface(const sp& surface) { - mRenderThread.queue().post( - [this, surf = surface]() mutable { mContext->setSurface(std::move(surf)); }); +void RenderProxy::setSurface(const sp& surface, bool enableTimeout) { + mRenderThread.queue().post([this, surf = surface, enableTimeout]() mutable { + mContext->setSurface(std::move(surf), enableTimeout); + }); } void RenderProxy::allocateBuffers() { diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index e6fe1d4864da..4683e1d69019 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -69,7 +69,7 @@ public: ANDROID_API bool loadSystemProperties(); ANDROID_API void setName(const char* name); - ANDROID_API void setSurface(const sp& surface); + ANDROID_API void setSurface(const sp& surface, bool enableTimeout = true); ANDROID_API void allocateBuffers(); ANDROID_API bool pause(); ANDROID_API void setStopped(bool stopped); -- cgit v1.2.3 From 3906d1dd0f48d705c555e0c8a1c1444725e84b09 Mon Sep 17 00:00:00 2001 From: Garfield Tan Date: Thu, 21 Nov 2019 16:42:13 -0800 Subject: Stop loading animation for addtional cursor type. In the test below it only expects MockSprite#setIcon() being called once, but with animation it may be called multiple times. Bug: 144947344 Test: atest PoitnerControllerTest#updatePointerIcon Change-Id: I5c4908fb8301cae144fd637c831f2012a35862e3 --- libs/input/tests/PointerController_test.cpp | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) (limited to 'libs') diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp index 92efb4ea86ff..b36406d6a703 100644 --- a/libs/input/tests/PointerController_test.cpp +++ b/libs/input/tests/PointerController_test.cpp @@ -32,8 +32,8 @@ enum TestCursorType { CURSOR_TYPE_HOVER, CURSOR_TYPE_TOUCH, CURSOR_TYPE_ANCHOR, - CURSOR_TYPE_ADDITIONAL_1, - CURSOR_TYPE_ADDITIONAL_2, + CURSOR_TYPE_ADDITIONAL, + CURSOR_TYPE_ADDITIONAL_ANIM, CURSOR_TYPE_CUSTOM = -1, }; @@ -79,13 +79,18 @@ void MockPointerControllerPolicyInterface::loadAdditionalMouseResources( SpriteIcon icon; PointerAnimation anim; - for (int32_t cursorType : {CURSOR_TYPE_ADDITIONAL_1, CURSOR_TYPE_ADDITIONAL_2}) { - loadPointerIconForType(&icon, cursorType); - anim.animationFrames.push_back(icon); - anim.durationPerFrame = 10; - (*outResources)[cursorType] = icon; - (*outAnimationResources)[cursorType] = anim; - } + // CURSOR_TYPE_ADDITIONAL doesn't have animation resource. + int32_t cursorType = CURSOR_TYPE_ADDITIONAL; + loadPointerIconForType(&icon, cursorType); + (*outResources)[cursorType] = icon; + + // CURSOR_TYPE_ADDITIONAL_ANIM has animation resource. + cursorType = CURSOR_TYPE_ADDITIONAL_ANIM; + loadPointerIconForType(&icon, cursorType); + anim.animationFrames.push_back(icon); + anim.durationPerFrame = 10; + (*outResources)[cursorType] = icon; + (*outAnimationResources)[cursorType] = anim; } int32_t MockPointerControllerPolicyInterface::getDefaultPointerIconId() { @@ -178,7 +183,7 @@ TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) { TEST_F(PointerControllerTest, updatePointerIcon) { mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE); - int32_t type = CURSOR_TYPE_ADDITIONAL_1; + int32_t type = CURSOR_TYPE_ADDITIONAL; std::pair hotspot = getHotSpotCoordinatesForType(type); EXPECT_CALL(*mPointerSprite, setVisible(true)); EXPECT_CALL(*mPointerSprite, setAlpha(1.0f)); -- cgit v1.2.3 From 15760c9db722b207f83bd3e0b9b0ec1880087830 Mon Sep 17 00:00:00 2001 From: Mike Reed Date: Mon, 25 Nov 2019 11:43:48 -0500 Subject: switch to new SkPathDirection enum Test: make Change-Id: If88814c12d12280d362078c1d8bc7f66558f82e2 --- libs/hwui/tests/unit/VectorDrawableTests.cpp | 6 +++--- libs/hwui/utils/VectorDrawableUtils.cpp | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'libs') diff --git a/libs/hwui/tests/unit/VectorDrawableTests.cpp b/libs/hwui/tests/unit/VectorDrawableTests.cpp index 60fd7a753f2d..6d4c57413f00 100644 --- a/libs/hwui/tests/unit/VectorDrawableTests.cpp +++ b/libs/hwui/tests/unit/VectorDrawableTests.cpp @@ -85,9 +85,9 @@ const static TestData sTestDataSet[] = { outPath->rCubicTo(8.0, 8.0, 8.0, 8.0, 8.0, 8.0); outPath->cubicTo(16.0, 16.0, 9.0, 9.0, 9.0, 9.0); outPath->rCubicTo(0.0, 0.0, 9.0, 9.0, 9.0, 9.0); - outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 10.0, + outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPathDirection::kCW, 10.0, 10.0); - outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 20.0, + outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPathDirection::kCW, 20.0, 20.0); }}, @@ -159,7 +159,7 @@ const static TestData sTestDataSet[] = { }, [](SkPath* outPath) { outPath->moveTo(300.0, 70.0); - outPath->arcTo(230.0, 230.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCCW_Direction, + outPath->arcTo(230.0, 230.0, 0.0, SkPath::kLarge_ArcSize, SkPathDirection::kCCW, 301.0, 70.0); outPath->close(); outPath->moveTo(300.0, 70.0); diff --git a/libs/hwui/utils/VectorDrawableUtils.cpp b/libs/hwui/utils/VectorDrawableUtils.cpp index 6b8f3154dd36..bd963df6750e 100644 --- a/libs/hwui/utils/VectorDrawableUtils.cpp +++ b/libs/hwui/utils/VectorDrawableUtils.cpp @@ -300,7 +300,7 @@ void PathResolver::addCommand(SkPath* outPath, char previousCmd, char cmd, // (rx ry x-axis-rotation large-arc-flag sweep-flag x y) outPath->arcTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), (SkPath::ArcSize) (points->at(k + 3) != 0), - (SkPath::Direction) (points->at(k + 4) == 0), + (SkPathDirection) (points->at(k + 4) == 0), points->at(k + 5) + currentX, points->at(k + 6) + currentY); currentX += points->at(k + 5); currentY += points->at(k + 6); @@ -310,7 +310,7 @@ void PathResolver::addCommand(SkPath* outPath, char previousCmd, char cmd, case 'A': // Draws an elliptical arc outPath->arcTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), (SkPath::ArcSize) (points->at(k + 3) != 0), - (SkPath::Direction) (points->at(k + 4) == 0), + (SkPathDirection) (points->at(k + 4) == 0), points->at(k + 5), points->at(k + 6)); currentX = points->at(k + 5); currentY = points->at(k + 6); -- cgit v1.2.3 From 7d77ff827c890205711b05d83642d85aca5475e8 Mon Sep 17 00:00:00 2001 From: Alec Mouri Date: Mon, 28 Oct 2019 16:39:38 -0700 Subject: [DisplayEventDispatcher] Kick DisplayEventDispatcher out of libandroidfw. DisplayEventDispatcher will live in libgui for now, as a shim over DisplayEventReceiver. This is so that we can keep libgui out of the UI-renderer module while we can work on stabilizing the interface with libandroidfw for asset management required by graphics classes. It also doesn't make much sense to have display-event functionality in a separate grab-bag library, especially when there's two users, and there is potentially an opportunity to somehow merge with DisplayEventReceiver to simplify code a bit. Change-Id: Ife0f9c8ad2053437087fdbf0618b892928ce6864 Bug: 142760698 Test: builds --- libs/androidfw/Android.bp | 2 - libs/androidfw/DisplayEventDispatcher.cpp | 153 --------------------- .../include/androidfw/DisplayEventDispatcher.h | 51 ------- 3 files changed, 206 deletions(-) delete mode 100644 libs/androidfw/DisplayEventDispatcher.cpp delete mode 100644 libs/androidfw/include/androidfw/DisplayEventDispatcher.h (limited to 'libs') diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index 4f52a8800a74..87657191e901 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -67,7 +67,6 @@ cc_library { "BackupData.cpp", "BackupHelpers.cpp", "CursorWindow.cpp", - "DisplayEventDispatcher.cpp", ], shared_libs: [ "libziparchive", @@ -75,7 +74,6 @@ cc_library { "libbinder", "liblog", "libcutils", - "libgui", "libutils", "libz", ], diff --git a/libs/androidfw/DisplayEventDispatcher.cpp b/libs/androidfw/DisplayEventDispatcher.cpp deleted file mode 100644 index d8a3f42690f4..000000000000 --- a/libs/androidfw/DisplayEventDispatcher.cpp +++ /dev/null @@ -1,153 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "DisplayEventDispatcher" - -#include -#include - -#include -#include -#include -#include - -#include - -namespace android { - -// Number of events to read at a time from the DisplayEventDispatcher pipe. -// The value should be large enough that we can quickly drain the pipe -// using just a few large reads. -static const size_t EVENT_BUFFER_SIZE = 100; - -DisplayEventDispatcher::DisplayEventDispatcher(const sp& looper, - ISurfaceComposer::VsyncSource vsyncSource, - ISurfaceComposer::ConfigChanged configChanged) : - mLooper(looper), mReceiver(vsyncSource, configChanged), mWaitingForVsync(false) { - ALOGV("dispatcher %p ~ Initializing display event dispatcher.", this); -} - -status_t DisplayEventDispatcher::initialize() { - status_t result = mReceiver.initCheck(); - if (result) { - ALOGW("Failed to initialize display event receiver, status=%d", result); - return result; - } - - int rc = mLooper->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT, - this, NULL); - if (rc < 0) { - return UNKNOWN_ERROR; - } - return OK; -} - -void DisplayEventDispatcher::dispose() { - ALOGV("dispatcher %p ~ Disposing display event dispatcher.", this); - - if (!mReceiver.initCheck()) { - mLooper->removeFd(mReceiver.getFd()); - } -} - -status_t DisplayEventDispatcher::scheduleVsync() { - if (!mWaitingForVsync) { - ALOGV("dispatcher %p ~ Scheduling vsync.", this); - - // Drain all pending events. - nsecs_t vsyncTimestamp; - PhysicalDisplayId vsyncDisplayId; - uint32_t vsyncCount; - if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) { - ALOGE("dispatcher %p ~ last event processed while scheduling was for %" PRId64 "", - this, ns2ms(static_cast(vsyncTimestamp))); - } - - status_t status = mReceiver.requestNextVsync(); - if (status) { - ALOGW("Failed to request next vsync, status=%d", status); - return status; - } - - mWaitingForVsync = true; - } - return OK; -} - -int DisplayEventDispatcher::handleEvent(int, int events, void*) { - if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) { - ALOGE("Display event receiver pipe was closed or an error occurred. " - "events=0x%x", events); - return 0; // remove the callback - } - - if (!(events & Looper::EVENT_INPUT)) { - ALOGW("Received spurious callback for unhandled poll event. " - "events=0x%x", events); - return 1; // keep the callback - } - - // Drain all pending events, keep the last vsync. - nsecs_t vsyncTimestamp; - PhysicalDisplayId vsyncDisplayId; - uint32_t vsyncCount; - if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) { - ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64 ", displayId=%" - ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", count=%d", - this, ns2ms(vsyncTimestamp), vsyncDisplayId, vsyncCount); - mWaitingForVsync = false; - dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount); - } - - return 1; // keep the callback -} - -bool DisplayEventDispatcher::processPendingEvents( - nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId, uint32_t* outCount) { - bool gotVsync = false; - DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE]; - ssize_t n; - while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) { - ALOGV("dispatcher %p ~ Read %d events.", this, int(n)); - for (ssize_t i = 0; i < n; i++) { - const DisplayEventReceiver::Event& ev = buf[i]; - switch (ev.header.type) { - case DisplayEventReceiver::DISPLAY_EVENT_VSYNC: - // Later vsync events will just overwrite the info from earlier - // ones. That's fine, we only care about the most recent. - gotVsync = true; - *outTimestamp = ev.header.timestamp; - *outDisplayId = ev.header.displayId; - *outCount = ev.vsync.count; - break; - case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG: - dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected); - break; - case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED: - dispatchConfigChanged(ev.header.timestamp, ev.header.displayId, ev.config.configId); - break; - default: - ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type); - break; - } - } - } - if (n < 0) { - ALOGW("Failed to get events from display event dispatcher, status=%d", status_t(n)); - } - return gotVsync; -} -} diff --git a/libs/androidfw/include/androidfw/DisplayEventDispatcher.h b/libs/androidfw/include/androidfw/DisplayEventDispatcher.h deleted file mode 100644 index 8bc25202b3ab..000000000000 --- a/libs/androidfw/include/androidfw/DisplayEventDispatcher.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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 -#include -#include - -namespace android { - -class DisplayEventDispatcher : public LooperCallback { -public: - explicit DisplayEventDispatcher(const sp& looper, - ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp, - ISurfaceComposer::ConfigChanged configChanged = ISurfaceComposer::eConfigChangedSuppress); - - status_t initialize(); - void dispose(); - status_t scheduleVsync(); - -protected: - virtual ~DisplayEventDispatcher() = default; - -private: - sp mLooper; - DisplayEventReceiver mReceiver; - bool mWaitingForVsync; - - virtual void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) = 0; - virtual void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, - bool connected) = 0; - virtual void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId, - int32_t configId) = 0; - - virtual int handleEvent(int receiveFd, int events, void* data); - bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId, - uint32_t* outCount); -}; -} -- cgit v1.2.3 From 6a8bf8e9e2faaa1294e5cb274826407676642e63 Mon Sep 17 00:00:00 2001 From: Mike Reed Date: Tue, 3 Dec 2019 13:01:07 -0500 Subject: use new SkPathFillType enum Test: make Change-Id: I8f20b7da284483ea4f59e35d9c3d48202cba02d5 --- libs/hwui/VectorDrawable.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'libs') diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp index 217b0c4a8b98..cd908354aea5 100644 --- a/libs/hwui/VectorDrawable.cpp +++ b/libs/hwui/VectorDrawable.cpp @@ -135,8 +135,7 @@ const SkPath& FullPath::getUpdatedPath(bool useStagingData, SkPath* tempStagingP bool setFillPath = properties.getFillGradient() != nullptr || properties.getFillColor() != SK_ColorTRANSPARENT; if (setFillPath) { - SkPath::FillType ft = static_cast(properties.getFillType()); - outPath->setFillType(ft); + outPath->setFillType(static_cast(properties.getFillType())); } return *outPath; } -- cgit v1.2.3 From 2945bfff9e59d7014fc60161595161d7b8270f4d Mon Sep 17 00:00:00 2001 From: Nathaniel Nifong Date: Mon, 25 Nov 2019 09:34:21 -0500 Subject: When saving MSKP files, record layers as SkPictures with annotations. No change when saving single frame SKP files. Layer redraws (full or partial) that occur at the beginning of frames are recorded as SkPictures written as the first commands in the frame, each preceded by an annotation recording the node id of the layer drawn to, and the dirty area of the draw. When rendered layers are used, the drawImageRect command is preceded by an annotation that provides the node id of the relevant layer. the skia debugger or skpbench could then use this information to play back the animation's layers. Test: tested by capturing both multi and single frame files on apps that use and do not use layers. normal rendering isn't affected, and capturing works as intended. Change-Id: Ic8f6947ebcc168334b6b740b3d63fc1788509b54 --- libs/hwui/pipeline/skia/RenderNodeDrawable.cpp | 13 +- libs/hwui/pipeline/skia/RenderNodeDrawable.h | 4 +- libs/hwui/pipeline/skia/SkiaPipeline.cpp | 165 +++++++++++++++++-------- libs/hwui/pipeline/skia/SkiaPipeline.h | 4 +- 4 files changed, 130 insertions(+), 56 deletions(-) (limited to 'libs') diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp index 9c8463417e0a..00ceb2d84f9e 100644 --- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp +++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp @@ -41,7 +41,7 @@ RenderNodeDrawable::~RenderNodeDrawable() { void RenderNodeDrawable::drawBackwardsProjectedNodes(SkCanvas* canvas, const SkiaDisplayList& displayList, - int nestLevel) { + int nestLevel) const { LOG_ALWAYS_FATAL_IF(0 == nestLevel && !displayList.mProjectionReceiver); for (auto& child : displayList.mChildNodes) { const RenderProperties& childProperties = child.getNodeProperties(); @@ -132,7 +132,7 @@ private: RenderNode& mNode; }; -void RenderNodeDrawable::forceDraw(SkCanvas* canvas) { +void RenderNodeDrawable::forceDraw(SkCanvas* canvas) const { RenderNode* renderNode = mRenderNode.get(); MarkDraw _marker{*canvas, *renderNode}; @@ -230,7 +230,14 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const { // we need to restrict the portion of the surface drawn to the size of the renderNode. SkASSERT(renderNode->getLayerSurface()->width() >= bounds.width()); SkASSERT(renderNode->getLayerSurface()->height() >= bounds.height()); - canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot().get(), bounds, + + // If SKP recording is active save an annotation that indicates this drawImageRect + // could also be rendered with the commands saved at ID associated with this node. + if (CC_UNLIKELY(Properties::skpCaptureEnabled)) { + canvas->drawAnnotation(bounds, String8::format( + "SurfaceID|%" PRId64, renderNode->uniqueId()).c_str(), nullptr); + } + canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot(), bounds, bounds, &paint); if (!renderNode->getSkiaLayer()->hasRenderedSinceRepaint) { diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.h b/libs/hwui/pipeline/skia/RenderNodeDrawable.h index 6ba8e599818c..6c390c3fce24 100644 --- a/libs/hwui/pipeline/skia/RenderNodeDrawable.h +++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.h @@ -58,7 +58,7 @@ public: * projection receiver then all projected children (excluding direct children) will be drawn * last. Any projected node not matching those requirements will not be drawn by this function. */ - void forceDraw(SkCanvas* canvas); + void forceDraw(SkCanvas* canvas) const; /** * Returns readonly render properties for this render node. @@ -113,7 +113,7 @@ private: * @param nestLevel should be always 0. Used to track how far we are from the receiver. */ void drawBackwardsProjectedNodes(SkCanvas* canvas, const SkiaDisplayList& displayList, - int nestLevel = 0); + int nestLevel = 0) const; /** * Applies the rendering properties of a view onto a SkCanvas. diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 06479776fc73..11dc013af6bc 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -26,11 +26,11 @@ #include #include #include "LightingInfo.h" -#include "TreeInfo.h" #include "VectorDrawable.h" #include "thread/CommonPool.h" #include "tools/SkSharingProc.h" #include "utils/TraceUtils.h" +#include "utils/String8.h" #include @@ -90,58 +90,61 @@ void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque) // only schedule repaint if node still on layer - possible it may have been // removed during a dropped frame, but layers may still remain scheduled so // as not to lose info on what portion is damaged - if (CC_LIKELY(layerNode->getLayerSurface() != nullptr)) { - SkASSERT(layerNode->getLayerSurface()); - SkiaDisplayList* displayList = (SkiaDisplayList*)layerNode->getDisplayList(); - if (!displayList || displayList->isEmpty()) { - ALOGE("%p drawLayers(%s) : missing drawable", layerNode, layerNode->getName()); - return; - } + if (CC_UNLIKELY(layerNode->getLayerSurface() == nullptr)) { + continue; + } + SkASSERT(layerNode->getLayerSurface()); + SkiaDisplayList* displayList = (SkiaDisplayList*)layerNode->getDisplayList(); + if (!displayList || displayList->isEmpty()) { + ALOGE("%p drawLayers(%s) : missing drawable", layerNode, layerNode->getName()); + return; + } - const Rect& layerDamage = layers.entries()[i].damage; + const Rect& layerDamage = layers.entries()[i].damage; - SkCanvas* layerCanvas = layerNode->getLayerSurface()->getCanvas(); + SkCanvas* layerCanvas = layerNode->getLayerSurface()->getCanvas(); - int saveCount = layerCanvas->save(); - SkASSERT(saveCount == 1); + int saveCount = layerCanvas->save(); + SkASSERT(saveCount == 1); - layerCanvas->androidFramework_setDeviceClipRestriction(layerDamage.toSkIRect()); + layerCanvas->androidFramework_setDeviceClipRestriction(layerDamage.toSkIRect()); - // TODO: put localized light center calculation and storage to a drawable related code. - // It does not seem right to store something localized in a global state - const Vector3 savedLightCenter(LightingInfo::getLightCenterRaw()); - Vector3 transformedLightCenter(savedLightCenter); - // map current light center into RenderNode's coordinate space - layerNode->getSkiaLayer()->inverseTransformInWindow.mapPoint3d(transformedLightCenter); - LightingInfo::setLightCenterRaw(transformedLightCenter); + // TODO: put localized light center calculation and storage to a drawable related code. + // It does not seem right to store something localized in a global state + // fix here and in recordLayers + const Vector3 savedLightCenter(LightingInfo::getLightCenterRaw()); + Vector3 transformedLightCenter(savedLightCenter); + // map current light center into RenderNode's coordinate space + layerNode->getSkiaLayer()->inverseTransformInWindow.mapPoint3d(transformedLightCenter); + LightingInfo::setLightCenterRaw(transformedLightCenter); - const RenderProperties& properties = layerNode->properties(); - const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight()); - if (properties.getClipToBounds() && layerCanvas->quickReject(bounds)) { - return; - } + const RenderProperties& properties = layerNode->properties(); + const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight()); + if (properties.getClipToBounds() && layerCanvas->quickReject(bounds)) { + return; + } - ATRACE_FORMAT("drawLayer [%s] %.1f x %.1f", layerNode->getName(), bounds.width(), - bounds.height()); - - layerNode->getSkiaLayer()->hasRenderedSinceRepaint = false; - layerCanvas->clear(SK_ColorTRANSPARENT); - - RenderNodeDrawable root(layerNode, layerCanvas, false); - root.forceDraw(layerCanvas); - layerCanvas->restoreToCount(saveCount); - LightingInfo::setLightCenterRaw(savedLightCenter); - - // cache the current context so that we can defer flushing it until - // either all the layers have been rendered or the context changes - GrContext* currentContext = layerNode->getLayerSurface()->getCanvas()->getGrContext(); - if (cachedContext.get() != currentContext) { - if (cachedContext.get()) { - ATRACE_NAME("flush layers (context changed)"); - cachedContext->flush(); - } - cachedContext.reset(SkSafeRef(currentContext)); + ATRACE_FORMAT("drawLayer [%s] %.1f x %.1f", layerNode->getName(), bounds.width(), + bounds.height()); + + layerNode->getSkiaLayer()->hasRenderedSinceRepaint = false; + layerCanvas->clear(SK_ColorTRANSPARENT); + + RenderNodeDrawable root(layerNode, layerCanvas, false); + root.forceDraw(layerCanvas); + layerCanvas->restoreToCount(saveCount); + + LightingInfo::setLightCenterRaw(savedLightCenter); + + // cache the current context so that we can defer flushing it until + // either all the layers have been rendered or the context changes + GrContext* currentContext = layerNode->getLayerSurface()->getCanvas()->getGrContext(); + if (cachedContext.get() != currentContext) { + if (cachedContext.get()) { + ATRACE_NAME("flush layers (context changed)"); + cachedContext->flush(); } + cachedContext.reset(SkSafeRef(currentContext)); } } @@ -275,12 +278,60 @@ bool SkiaPipeline::setupMultiFrameCapture() { } } -SkCanvas* SkiaPipeline::tryCapture(SkSurface* surface) { +// recurse through the rendernode's children, add any nodes which are layers to the queue. +static void collectLayers(RenderNode* node, LayerUpdateQueue* layers) { + SkiaDisplayList* dl = (SkiaDisplayList*)node->getDisplayList(); + if (dl) { + const auto& prop = node->properties(); + if (node->hasLayer()) { + layers->enqueueLayerWithDamage(node, Rect(prop.getWidth(), prop.getHeight())); + } + // The way to recurse through rendernodes is to call this with a lambda. + dl->updateChildren([&](RenderNode* child) { collectLayers(child, layers); }); + } +} + +// record the provided layers to the provided canvas as self-contained skpictures. +static void recordLayers(const LayerUpdateQueue& layers, + SkCanvas* mskpCanvas) { + const Vector3 savedLightCenter(LightingInfo::getLightCenterRaw()); + // Record the commands to re-draw each dirty layer into an SkPicture + for (size_t i = 0; i < layers.entries().size(); i++) { + RenderNode* layerNode = layers.entries()[i].renderNode.get(); + const Rect& layerDamage = layers.entries()[i].damage; + const RenderProperties& properties = layerNode->properties(); + + // Temporarily map current light center into RenderNode's coordinate space + Vector3 transformedLightCenter(savedLightCenter); + layerNode->getSkiaLayer()->inverseTransformInWindow.mapPoint3d(transformedLightCenter); + LightingInfo::setLightCenterRaw(transformedLightCenter); + + SkPictureRecorder layerRec; + auto* recCanvas = layerRec.beginRecording(properties.getWidth(), + properties.getHeight()); + // This is not recorded but still causes clipping. + recCanvas->androidFramework_setDeviceClipRestriction(layerDamage.toSkIRect()); + RenderNodeDrawable root(layerNode, recCanvas, false); + root.forceDraw(recCanvas); + // Now write this picture into the SKP canvas with an annotation indicating what it is + mskpCanvas->drawAnnotation(layerDamage.toSkRect(), String8::format( + "OffscreenLayerDraw|%" PRId64, layerNode->uniqueId()).c_str(), nullptr); + mskpCanvas->drawPicture(layerRec.finishRecordingAsPicture()); + } + LightingInfo::setLightCenterRaw(savedLightCenter); +} + +SkCanvas* SkiaPipeline::tryCapture(SkSurface* surface, RenderNode* root, + const LayerUpdateQueue& dirtyLayers) { if (CC_LIKELY(!Properties::skpCaptureEnabled)) { return surface->getCanvas(); // Bail out early when capture is not turned on. } // Note that shouldStartNewFileCapture tells us if this is the *first* frame of a capture. + bool firstFrameOfAnim = false; if (shouldStartNewFileCapture() && mCaptureMode == CaptureMode::MultiFrameSKP) { + // set a reminder to record every layer near the end of this method, after we have set up + // the nway canvas. + firstFrameOfAnim = true; if (!setupMultiFrameCapture()) { return surface->getCanvas(); } @@ -309,6 +360,20 @@ SkCanvas* SkiaPipeline::tryCapture(SkSurface* surface) { mNwayCanvas = std::make_unique(surface->width(), surface->height()); mNwayCanvas->addCanvas(surface->getCanvas()); mNwayCanvas->addCanvas(pictureCanvas); + + if (firstFrameOfAnim) { + // On the first frame of any mskp capture we want to record any layers that are needed in + // frame but may have been rendered offscreen before recording began. + // We do not maintain a list of all layers, since it isn't needed outside this rare, + // recording use case. Traverse the tree to find them and put them in this LayerUpdateQueue. + LayerUpdateQueue luq; + collectLayers(root, &luq); + recordLayers(luq, mNwayCanvas.get()); + } else { + // on non-first frames, we record any normal layer draws (dirty regions) + recordLayers(dirtyLayers, mNwayCanvas.get()); + } + return mNwayCanvas.get(); } @@ -359,13 +424,13 @@ void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& cli Properties::skpCaptureEnabled = true; } + // Initialize the canvas for the current frame, that might be a recording canvas if SKP + // capture is enabled. + SkCanvas* canvas = tryCapture(surface.get(), nodes[0].get(), layers); + // draw all layers up front renderLayersImpl(layers, opaque); - // initialize the canvas for the current frame, that might be a recording canvas if SKP - // capture is enabled. - SkCanvas* canvas = tryCapture(surface.get()); - renderFrameImpl(clip, nodes, opaque, contentDrawBounds, canvas, preTransform); endCapture(surface.get()); diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h index 7d575ad01936..af8414de4bc1 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaPipeline.h @@ -45,6 +45,8 @@ public: void renderLayers(const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, bool opaque, const LightInfo& lightInfo) override; + // If the given node didn't have a layer surface, or had one of the wrong size, this method + // creates a new one and returns true. Otherwise does nothing and returns false. bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator, ErrorHandler* errorHandler) override; @@ -92,7 +94,7 @@ private: // Called every frame. Normally returns early with screen canvas. // But when capture is enabled, returns an nwaycanvas where commands are also recorded. - SkCanvas* tryCapture(SkSurface* surface); + SkCanvas* tryCapture(SkSurface* surface, RenderNode* root, const LayerUpdateQueue& dirtyLayers); // Called at the end of every frame, closes the recording if necessary. void endCapture(SkSurface* surface); // Determine if a new file-based capture should be started. -- cgit v1.2.3 From 25e2b42a6b5e318226cab4634434cd2c40c99dac Mon Sep 17 00:00:00 2001 From: Ruchir Rastogi Date: Wed, 11 Dec 2019 14:17:51 -0800 Subject: Remove StatsEvent.h/cpp and StatsEvent.aidl As of ag/9820253, Binder communication between pull atom clients and statsd uses the structured StatsEventParcel. The custom parcelable StatsEvent class is not needed anymore. Test: m Change-Id: I33aa975a2cd1b02ba232aadc11e2ac58814de4c3 --- libs/services/Android.bp | 5 +-- libs/services/include/android/util/StatsEvent.h | 43 ------------------ libs/services/src/util/StatsEvent.cpp | 58 ------------------------- 3 files changed, 2 insertions(+), 104 deletions(-) delete mode 100644 libs/services/include/android/util/StatsEvent.h delete mode 100644 libs/services/src/util/StatsEvent.cpp (limited to 'libs') diff --git a/libs/services/Android.bp b/libs/services/Android.bp index b0fad57dfd29..901ffaa59cd1 100644 --- a/libs/services/Android.bp +++ b/libs/services/Android.bp @@ -22,17 +22,16 @@ cc_library_shared { "src/os/DropBoxManager.cpp", "src/os/StatsDimensionsValue.cpp", "src/os/StatsLogEventWrapper.cpp", - "src/util/StatsEvent.cpp", ], shared_libs: [ "libbinder", - "liblog", "libcutils", + "liblog", "libutils", ], header_libs: [ - "libbase_headers", + "libbase_headers", ], aidl: { include_dirs: ["frameworks/base/core/java/"], diff --git a/libs/services/include/android/util/StatsEvent.h b/libs/services/include/android/util/StatsEvent.h deleted file mode 100644 index 48631174f7dd..000000000000 --- a/libs/services/include/android/util/StatsEvent.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2019 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 STATS_EVENT_H -#define STATS_EVENT_H - -#include -#include -#include -#include - -namespace android { -namespace util { -class StatsEvent : public android::Parcelable { - public: - StatsEvent(); - - StatsEvent(StatsEvent&& in) = default; - - android::status_t writeToParcel(android::Parcel* out) const; - - android::status_t readFromParcel(const android::Parcel* in); - - private: - int mAtomTag; - std::vector mBuffer; -}; -} // Namespace util -} // Namespace android - -#endif // STATS_ EVENT_H \ No newline at end of file diff --git a/libs/services/src/util/StatsEvent.cpp b/libs/services/src/util/StatsEvent.cpp deleted file mode 100644 index 8b8579167b00..000000000000 --- a/libs/services/src/util/StatsEvent.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2019 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 - -#include -#include -#include -#include - -using android::Parcel; -using android::Parcelable; -using android::status_t; -using std::vector; - -namespace android { -namespace util { - -StatsEvent::StatsEvent(){}; - -status_t StatsEvent::writeToParcel(Parcel* out) const { - // Implement me if desired. We don't currently use this. - ALOGE("Cannot do c++ StatsEvent.writeToParcel(); it is not implemented."); - (void)out; // To prevent compile error of unused parameter 'out' - return UNKNOWN_ERROR; -}; - -status_t StatsEvent::readFromParcel(const Parcel* in) { - status_t res = OK; - if (in == NULL) { - ALOGE("statsd received parcel argument was NULL."); - return BAD_VALUE; - } - if ((res = in->readInt32(&mAtomTag)) != OK) { - ALOGE("statsd could not read atom tag from parcel"); - return res; - } - if ((res = in->readByteVector(&mBuffer)) != OK) { - ALOGE("statsd could not read buffer from parcel"); - return res; - } - return NO_ERROR; -}; - -} // Namespace util -} // Namespace android -- cgit v1.2.3 From df9e732962f0c9e3b16113413f9143a4a7d426a9 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Thu, 12 Dec 2019 10:23:54 -0800 Subject: Add tests for layout overlaying Add tests to ensure that findViewbyId retrieves the correct view when an id is overlaid in a RRO. Also fixes a bug that made strings hardcoded in the overlays.xml file unable to be retrieved from the string pool. Bug: 135943783 Test: atest OverlayDeviceTests Change-Id: I2bf03f151cb696d28f6bb9018eb319af29ba48f4 --- libs/androidfw/Idmap.cpp | 8 ++++++-- libs/androidfw/include/androidfw/Idmap.h | 9 +++++---- libs/androidfw/include/androidfw/ResourceTypes.h | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) (limited to 'libs') diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp index 773353d32d51..6aac4479c3a1 100644 --- a/libs/androidfw/Idmap.cpp +++ b/libs/androidfw/Idmap.cpp @@ -53,7 +53,7 @@ OverlayStringPool::~OverlayStringPool() { const char16_t* OverlayStringPool::stringAt(size_t idx, size_t* outLen) const { const size_t offset = dtohl(data_header_->string_pool_index_offset); - if (idmap_string_pool_ != nullptr && idx >= size() && idx >= offset) { + if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) { return idmap_string_pool_->stringAt(idx - offset, outLen); } @@ -62,13 +62,17 @@ const char16_t* OverlayStringPool::stringAt(size_t idx, size_t* outLen) const { const char* OverlayStringPool::string8At(size_t idx, size_t* outLen) const { const size_t offset = dtohl(data_header_->string_pool_index_offset); - if (idmap_string_pool_ != nullptr && idx >= size() && idx >= offset) { + if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) { return idmap_string_pool_->string8At(idx - offset, outLen); } return ResStringPool::string8At(idx, outLen); } +size_t OverlayStringPool::size() const { + return ResStringPool::size() + (idmap_string_pool_ != nullptr ? idmap_string_pool_->size() : 0U); +} + OverlayDynamicRefTable::OverlayDynamicRefTable(const Idmap_data_header* data_header, const Idmap_overlay_entry* entries, uint8_t target_assigned_package_id) diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h index ab4c9c204842..ccb57f373473 100644 --- a/libs/androidfw/include/androidfw/Idmap.h +++ b/libs/androidfw/include/androidfw/Idmap.h @@ -40,8 +40,9 @@ class IdmapResMap; class OverlayStringPool : public ResStringPool { public: virtual ~OverlayStringPool(); - virtual const char16_t* stringAt(size_t idx, size_t* outLen) const; - virtual const char* string8At(size_t idx, size_t* outLen) const; + const char16_t* stringAt(size_t idx, size_t* outLen) const override; + const char* string8At(size_t idx, size_t* outLen) const override; + size_t size() const override; explicit OverlayStringPool(const LoadedIdmap* loaded_idmap); private: @@ -53,8 +54,8 @@ class OverlayStringPool : public ResStringPool { // resources to the resource id of corresponding target resources. class OverlayDynamicRefTable : public DynamicRefTable { public: - virtual ~OverlayDynamicRefTable() = default; - virtual status_t lookupResourceId(uint32_t* resId) const; + ~OverlayDynamicRefTable() override = default; + status_t lookupResourceId(uint32_t* resId) const override; private: explicit OverlayDynamicRefTable(const Idmap_data_header* data_header, diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index b20e6579fda7..6d82d88b0a53 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -520,7 +520,7 @@ public: ssize_t indexOfString(const char16_t* str, size_t strLen) const; - size_t size() const; + virtual size_t size() const; size_t styleCount() const; size_t bytes() const; const void* data() const; -- cgit v1.2.3 From 753a56fa563e08aea0928eacf5317842388f4e24 Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Wed, 11 Dec 2019 11:02:15 -0500 Subject: Separate ImageDecoder JNI from actual work Bug: 135133301 Test: CtsGraphicsTestCases ImageDecoderTest Make the native ImageDecoder a class, with methods that do the bulk of the work that was previously done in JNI. This will allow the forthcoming NDK API (AImageDecoder) to share the same code for decoding. Move enums into implementation file, as no other code needs them. Make computeAllocationSize a public static method on hwui/Bitmap, for use by ImageDecoder. Change-Id: I4e4dae338a951761614aed42ca2cc157e3d526dd --- libs/hwui/Android.bp | 1 + libs/hwui/hwui/Bitmap.cpp | 6 +- libs/hwui/hwui/Bitmap.h | 4 + libs/hwui/hwui/ImageDecoder.cpp | 213 ++++++++++++++++++++++++++++++++++++++++ libs/hwui/hwui/ImageDecoder.h | 71 ++++++++++++++ 5 files changed, 291 insertions(+), 4 deletions(-) create mode 100644 libs/hwui/hwui/ImageDecoder.cpp create mode 100644 libs/hwui/hwui/ImageDecoder.h (limited to 'libs') diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index f670cf97e0c2..d945fc49ec88 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -176,6 +176,7 @@ cc_defaults { "hwui/AnimatedImageThread.cpp", "hwui/Bitmap.cpp", "hwui/Canvas.cpp", + "hwui/ImageDecoder.cpp", "hwui/MinikinSkia.cpp", "hwui/MinikinUtils.cpp", "hwui/PaintImpl.cpp", diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index 2ba6fbe76de1..f4149b9b4d7d 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -44,9 +44,7 @@ namespace android { -// returns true if rowBytes * height can be represented by a positive int32_t value -// and places that value in size. -static bool computeAllocationSize(size_t rowBytes, int height, size_t* size) { +bool Bitmap::computeAllocationSize(size_t rowBytes, int height, size_t* size) { return 0 <= height && height <= std::numeric_limits::max() && !__builtin_mul_overflow(rowBytes, (size_t)height, size) && *size <= std::numeric_limits::max(); @@ -66,7 +64,7 @@ static sk_sp allocateBitmap(SkBitmap* bitmap, AllocPixelRef alloc) { // we must respect the rowBytes value already set on the bitmap instead of // attempting to compute our own. const size_t rowBytes = bitmap->rowBytes(); - if (!computeAllocationSize(rowBytes, bitmap->height(), &size)) { + if (!Bitmap::computeAllocationSize(rowBytes, bitmap->height(), &size)) { return nullptr; } diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h index 00733c6b245b..1cda0465ae64 100644 --- a/libs/hwui/hwui/Bitmap.h +++ b/libs/hwui/hwui/Bitmap.h @@ -138,6 +138,10 @@ public: return mPalette; } + // returns true if rowBytes * height can be represented by a positive int32_t value + // and places that value in size. + static bool computeAllocationSize(size_t rowBytes, int height, size_t* size); + private: static sk_sp allocateAshmemBitmap(size_t size, const SkImageInfo& i, size_t rowBytes); static sk_sp allocateHeapBitmap(size_t size, const SkImageInfo& i, size_t rowBytes); diff --git a/libs/hwui/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp new file mode 100644 index 000000000000..4f2027d978a1 --- /dev/null +++ b/libs/hwui/hwui/ImageDecoder.cpp @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2019 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 "ImageDecoder.h" + +#include + +#include +#include +#include + +using namespace android; + +ImageDecoder::ImageDecoder(std::unique_ptr codec, sk_sp peeker) + : mCodec(std::move(codec)) + , mPeeker(std::move(peeker)) + , mTargetSize(mCodec->getInfo().dimensions()) + , mDecodeSize(mTargetSize) + , mOutColorType(mCodec->computeOutputColorType(kN32_SkColorType)) + , mOutAlphaType(mCodec->getInfo().isOpaque() ? + kOpaque_SkAlphaType : kPremul_SkAlphaType) + , mOutColorSpace(mCodec->getInfo().refColorSpace()) + , mSampleSize(1) +{ +} + +bool ImageDecoder::setTargetSize(int width, int height) { + if (width <= 0 || height <= 0) { + return false; + } + + auto info = SkImageInfo::Make(width, height, mOutColorType, mOutAlphaType); + size_t rowBytes = info.minRowBytes(); + if (rowBytes == 0) { + // This would have overflowed. + return false; + } + + size_t pixelMemorySize; + if (!Bitmap::computeAllocationSize(rowBytes, height, &pixelMemorySize)) { + return false; + } + + if (mCropRect) { + if (mCropRect->right() > width || mCropRect->bottom() > height) { + return false; + } + } + + SkISize targetSize = { width, height }, decodeSize = targetSize; + int sampleSize = mCodec->computeSampleSize(&decodeSize); + + if (decodeSize != targetSize && mOutAlphaType == kUnpremul_SkAlphaType + && !mCodec->getInfo().isOpaque()) { + return false; + } + + mTargetSize = targetSize; + mDecodeSize = decodeSize; + mSampleSize = sampleSize; + return true; +} + +bool ImageDecoder::setCropRect(const SkIRect* crop) { + if (!crop) { + mCropRect.reset(); + return true; + } + + if (crop->left() >= crop->right() || crop->top() >= crop->bottom()) { + return false; + } + + const auto& size = mTargetSize; + if (crop->left() < 0 || crop->top() < 0 + || crop->right() > size.width() || crop->bottom() > size.height()) { + return false; + } + + mCropRect.emplace(*crop); + return true; +} + +bool ImageDecoder::setOutColorType(SkColorType colorType) { + switch (colorType) { + case kRGB_565_SkColorType: + if (!opaque()) { + return false; + } + break; + case kGray_8_SkColorType: + if (!gray()) { + return false; + } + mOutColorSpace = nullptr; + break; + case kN32_SkColorType: + break; + case kRGBA_F16_SkColorType: + break; + default: + return false; + } + + mOutColorType = colorType; + return true; +} + +bool ImageDecoder::setOutAlphaType(SkAlphaType alpha) { + switch (alpha) { + case kOpaque_SkAlphaType: + return opaque(); + case kPremul_SkAlphaType: + if (opaque()) { + // Opaque can be treated as premul. + return true; + } + break; + case kUnpremul_SkAlphaType: + if (opaque()) { + // Opaque can be treated as unpremul. + return true; + } + if (mDecodeSize != mTargetSize) { + return false; + } + break; + default: + return false; + } + mOutAlphaType = alpha; + return true; +} + +void ImageDecoder::setOutColorSpace(sk_sp colorSpace) { + mOutColorSpace = std::move(colorSpace); +} + +SkImageInfo ImageDecoder::getOutputInfo() const { + SkISize size = mCropRect ? mCropRect->size() : mTargetSize; + return SkImageInfo::Make(size, mOutColorType, mOutAlphaType, mOutColorSpace); +} + +bool ImageDecoder::opaque() const { + return mOutAlphaType == kOpaque_SkAlphaType; +} + +bool ImageDecoder::gray() const { + return mCodec->getInfo().colorType() == kGray_8_SkColorType; +} + +SkCodec::Result ImageDecoder::decode(void* pixels, size_t rowBytes) { + void* decodePixels = pixels; + size_t decodeRowBytes = rowBytes; + auto decodeInfo = SkImageInfo::Make(mDecodeSize, mOutColorType, mOutAlphaType, mOutColorSpace); + // Used if we need a temporary before scaling or subsetting. + // FIXME: Use scanline decoding on only a couple lines to save memory. b/70709380. + SkBitmap tmp; + const bool scale = mDecodeSize != mTargetSize; + if (scale || mCropRect) { + if (!tmp.setInfo(decodeInfo)) { + return SkCodec::kInternalError; + } + if (!Bitmap::allocateHeapBitmap(&tmp)) { + return SkCodec::kInternalError; + } + decodePixels = tmp.getPixels(); + decodeRowBytes = tmp.rowBytes(); + } + + SkAndroidCodec::AndroidOptions options; + options.fSampleSize = mSampleSize; + auto result = mCodec->getAndroidPixels(decodeInfo, decodePixels, decodeRowBytes, &options); + + if (scale || mCropRect) { + SkBitmap scaledBm; + if (!scaledBm.installPixels(getOutputInfo(), pixels, rowBytes)) { + return SkCodec::kInternalError; + } + + SkPaint paint; + paint.setBlendMode(SkBlendMode::kSrc); + paint.setFilterQuality(kLow_SkFilterQuality); // bilinear filtering + + SkCanvas canvas(scaledBm, SkCanvas::ColorBehavior::kLegacy); + if (mCropRect) { + canvas.translate(-mCropRect->fLeft, -mCropRect->fTop); + } + if (scale) { + float scaleX = (float) mTargetSize.width() / mDecodeSize.width(); + float scaleY = (float) mTargetSize.height() / mDecodeSize.height(); + canvas.scale(scaleX, scaleY); + } + + canvas.drawBitmap(tmp, 0.0f, 0.0f, &paint); + } + + return result; +} + diff --git a/libs/hwui/hwui/ImageDecoder.h b/libs/hwui/hwui/ImageDecoder.h new file mode 100644 index 000000000000..b956f4a77793 --- /dev/null +++ b/libs/hwui/hwui/ImageDecoder.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2019 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. + */ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +class SkAndroidCodec; + +namespace android { + +class ANDROID_API ImageDecoder { +public: + std::unique_ptr mCodec; + sk_sp mPeeker; + + ImageDecoder(std::unique_ptr codec, + sk_sp peeker = nullptr); + + bool setTargetSize(int width, int height); + bool setCropRect(const SkIRect*); + + bool setOutColorType(SkColorType outColorType); + + bool setOutAlphaType(SkAlphaType outAlphaType); + + void setOutColorSpace(sk_sp cs); + + // The size is the final size after scaling and cropping. + SkImageInfo getOutputInfo() const; + SkColorType getOutColorType() const { return mOutColorType; } + SkAlphaType getOutAlphaType() const { return mOutAlphaType; } + + bool opaque() const; + bool gray() const; + + SkCodec::Result decode(void* pixels, size_t rowBytes); + +private: + SkISize mTargetSize; + SkISize mDecodeSize; + SkColorType mOutColorType; + SkAlphaType mOutAlphaType; + sk_sp mOutColorSpace; + int mSampleSize; + std::optional mCropRect; + + ImageDecoder(const ImageDecoder&) = delete; + ImageDecoder& operator=(const ImageDecoder&) = delete; +}; + +} // namespace android -- cgit v1.2.3 From d7e8a534d0fbf0b0f8f27f59f90dbbeb0b5641db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Kongstad?= Date: Fri, 11 Oct 2019 08:32:04 +0200 Subject: idmap2: add debug information to idmap file format Add a new variable length string to the idmap file format. This string will hold debug information like fulfilled policies and any warnings triggered while generating the file. Bump the idmap version to 3. Adjust the idmap header definition in ResourceType.h to take the new string into account. Example debug info: $ idmap2 create \ --target-apk-path frameworks/base/cmds/idmap2/tests/data/target/target.apk \ --overlay-apk-path frameworks/base/cmds/idmap2/tests/data/overlay/overlay.apk \ --idmap-path /tmp/a.idmap \ --policy public \ --policy oem $ idmap2 dump --idmap-path /tmp/a.idmap target apk path : frameworks/base/cmds/idmap2/tests/data/target/target.apk overlay apk path : frameworks/base/cmds/idmap2/tests/data/overlay/overlay.apk I fulfilled_policies=oem|public enforce_overlayable=true W failed to find resource "integer/not_in_target" in target resources 0x7f010000 -> 0x7f010000 integer/int1 0x7f02000c -> 0x7f020000 string/str1 [...] $ idmap2 dump --idmap-path /tmp/a.idmap --verbose 00000000: 504d4449 magic 00000004: 00000003 version 00000008: 76a20829 target crc 0000000c: c054fb26 overlay crc 00000010: ........ target path: frameworks/base/cmds/idmap2/tests/data/target/target.apk 00000110: ........ overlay path: frameworks/base/cmds/idmap2/tests/data/overlay/overlay.apk 00000210: ........ debug info: ... 00000294: 7f target package id 00000295: 7f overlay package id [...] Also, tell cpplint to accept non-const references as function parameters: they make more sense as out-parameters than pointers that are assumed to be non-null. Also, switch to regular expressions in the RawPrintVisitorTests: no more manual fixups of the stream offsets! Tell cpplint that the header is OK to use. Bug: 140790707 Test: idmap2_tests Change-Id: Ib94684a3b4001240321801e21af8e132fbcf6609 --- libs/androidfw/Idmap.cpp | 8 ++++++-- libs/androidfw/include/androidfw/ResourceTypes.h | 7 ++++++- libs/androidfw/tests/data/overlay/overlay.apk | Bin 2988 -> 2988 bytes libs/androidfw/tests/data/overlay/overlay.idmap | Bin 1077 -> 1137 bytes 4 files changed, 12 insertions(+), 3 deletions(-) (limited to 'libs') diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp index 773353d32d51..ce3bffff0aa5 100644 --- a/libs/androidfw/Idmap.cpp +++ b/libs/androidfw/Idmap.cpp @@ -43,6 +43,10 @@ static bool compare_overlay_entries(const Idmap_overlay_entry& e1, const uint32_ return dtohl(e1.overlay_id) < overlay_id; } +size_t Idmap_header::Size() const { + return sizeof(Idmap_header) + sizeof(uint8_t) * dtohl(debug_info_size); +} + OverlayStringPool::OverlayStringPool(const LoadedIdmap* loaded_idmap) : data_header_(loaded_idmap->data_header_), idmap_string_pool_(loaded_idmap->string_pool_.get()) { }; @@ -211,8 +215,8 @@ std::unique_ptr LoadedIdmap::Load(const StringPiece& idmap_da } auto header = reinterpret_cast(idmap_data.data()); - const uint8_t* data_ptr = reinterpret_cast(idmap_data.data()) + sizeof(*header); - size_t data_size = idmap_data.size() - sizeof(*header); + const uint8_t* data_ptr = reinterpret_cast(idmap_data.data()) + header->Size(); + size_t data_size = idmap_data.size() - header->Size(); // Currently idmap2 can only generate one data block. auto data_header = reinterpret_cast(data_ptr); diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index b20e6579fda7..b603326cec34 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -39,7 +39,7 @@ namespace android { constexpr const static uint32_t kIdmapMagic = 0x504D4449u; -constexpr const static uint32_t kIdmapCurrentVersion = 0x00000002u; +constexpr const static uint32_t kIdmapCurrentVersion = 0x00000003u; /** * In C++11, char16_t is defined as *at least* 16 bits. We do a lot of @@ -1724,6 +1724,11 @@ struct Idmap_header { uint8_t target_path[256]; uint8_t overlay_path[256]; + + uint32_t debug_info_size; + uint8_t debug_info[0]; + + size_t Size() const; }; struct Idmap_data_header { diff --git a/libs/androidfw/tests/data/overlay/overlay.apk b/libs/androidfw/tests/data/overlay/overlay.apk index c594b8e67f28..62e98662e68d 100644 Binary files a/libs/androidfw/tests/data/overlay/overlay.apk and b/libs/androidfw/tests/data/overlay/overlay.apk differ diff --git a/libs/androidfw/tests/data/overlay/overlay.idmap b/libs/androidfw/tests/data/overlay/overlay.idmap index 27cf792ff7e2..3759ed650033 100644 Binary files a/libs/androidfw/tests/data/overlay/overlay.idmap and b/libs/androidfw/tests/data/overlay/overlay.idmap differ -- cgit v1.2.3 From a5a0c962b685d441173ea1ba929df9950b3471c6 Mon Sep 17 00:00:00 2001 From: Alec Mouri Date: Tue, 10 Dec 2019 15:10:14 -0800 Subject: [HWUI] Minor ANativeWindow usage cleanup * Remove unused header in EglManager * Rename usage of rotation flags to use the public names. Bug: 137012798 Test: builds Change-Id: Ia603e8d74c6ba51f77e352333d1e82816582d827 --- libs/hwui/renderthread/EglManager.cpp | 1 - libs/hwui/renderthread/VulkanSurface.cpp | 20 ++++++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) (limited to 'libs') diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index eb469a810358..c1fed269de5b 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -22,7 +22,6 @@ #include #include #include -#include #include #include diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp index bbffb359aebe..a7ea21d8c4de 100644 --- a/libs/hwui/renderthread/VulkanSurface.cpp +++ b/libs/hwui/renderthread/VulkanSurface.cpp @@ -29,12 +29,12 @@ namespace renderthread { static int InvertTransform(int transform) { switch (transform) { - case NATIVE_WINDOW_TRANSFORM_ROT_90: - return NATIVE_WINDOW_TRANSFORM_ROT_270; - case NATIVE_WINDOW_TRANSFORM_ROT_180: - return NATIVE_WINDOW_TRANSFORM_ROT_180; - case NATIVE_WINDOW_TRANSFORM_ROT_270: - return NATIVE_WINDOW_TRANSFORM_ROT_90; + case ANATIVEWINDOW_TRANSFORM_ROTATE_90: + return ANATIVEWINDOW_TRANSFORM_ROTATE_270; + case ANATIVEWINDOW_TRANSFORM_ROTATE_180: + return ANATIVEWINDOW_TRANSFORM_ROTATE_180; + case ANATIVEWINDOW_TRANSFORM_ROTATE_270: + return ANATIVEWINDOW_TRANSFORM_ROTATE_90; default: return 0; } @@ -47,11 +47,11 @@ static SkMatrix GetPreTransformMatrix(SkISize windowSize, int transform) { switch (transform) { case 0: return SkMatrix::I(); - case NATIVE_WINDOW_TRANSFORM_ROT_90: + case ANATIVEWINDOW_TRANSFORM_ROTATE_90: return SkMatrix::MakeAll(0, -1, height, 1, 0, 0, 0, 0, 1); - case NATIVE_WINDOW_TRANSFORM_ROT_180: + case ANATIVEWINDOW_TRANSFORM_ROTATE_180: return SkMatrix::MakeAll(-1, 0, width, 0, -1, height, 0, 0, 1); - case NATIVE_WINDOW_TRANSFORM_ROT_270: + case ANATIVEWINDOW_TRANSFORM_ROTATE_270: return SkMatrix::MakeAll(0, 1, 0, -1, 0, width, 0, 0, 1); default: LOG_ALWAYS_FATAL("Unsupported Window Transform (%d)", transform); @@ -168,7 +168,7 @@ bool VulkanSurface::InitializeWindowInfoStruct(ANativeWindow* window, ColorMode outWindowInfo->transform = query_value; outWindowInfo->actualSize = outWindowInfo->size; - if (outWindowInfo->transform & NATIVE_WINDOW_TRANSFORM_ROT_90) { + if (outWindowInfo->transform & ANATIVEWINDOW_TRANSFORM_ROTATE_90) { outWindowInfo->actualSize.set(outWindowInfo->size.height(), outWindowInfo->size.width()); } -- cgit v1.2.3 From 4a818f189c910810d6be72147a4567d97ad7232a Mon Sep 17 00:00:00 2001 From: Alec Mouri Date: Tue, 19 Nov 2019 16:17:53 -0800 Subject: [HWUI] use AChoreographer in place of DisplayEventReceiver. Bug: 136262896 Test: builds, boots Test: scroll through settings app Test: Toggle between 60/90hz and observe systrace Change-Id: I8eef306a968525c55f3863ae688545faa43b23be --- libs/hwui/DeviceInfo.cpp | 7 +- libs/hwui/DeviceInfo.h | 7 +- libs/hwui/JankTracker.cpp | 2 +- libs/hwui/renderthread/RenderThread.cpp | 125 +++++++++++++------------------- libs/hwui/renderthread/RenderThread.h | 17 ++++- 5 files changed, 72 insertions(+), 86 deletions(-) (limited to 'libs') diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp index e53f3db08538..6d4a0c6421d9 100644 --- a/libs/hwui/DeviceInfo.cpp +++ b/libs/hwui/DeviceInfo.cpp @@ -52,8 +52,8 @@ void DeviceInfo::setMaxTextureSize(int maxTextureSize) { DeviceInfo::get()->mMaxTextureSize = maxTextureSize; } -void DeviceInfo::onDisplayConfigChanged() { - updateDisplayInfo(); +void DeviceInfo::onRefreshRateChanged(int64_t vsyncPeriod) { + mVsyncPeriod = vsyncPeriod; } void DeviceInfo::updateDisplayInfo() { @@ -113,10 +113,11 @@ void DeviceInfo::updateDisplayInfo() { ADisplay* primaryDisplay = mDisplays[mPhysicalDisplayIndex]; status_t status = ADisplay_getCurrentConfig(primaryDisplay, &mCurrentConfig); LOG_ALWAYS_FATAL_IF(status, "Failed to get display config, error %d", status); + mWidth = ADisplayConfig_getWidth(mCurrentConfig); mHeight = ADisplayConfig_getHeight(mCurrentConfig); mDensity = ADisplayConfig_getDensity(mCurrentConfig); - mRefreshRate = ADisplayConfig_getFps(mCurrentConfig); + mVsyncPeriod = static_cast(1000000000 / ADisplayConfig_getFps(mCurrentConfig)); mCompositorOffset = ADisplayConfig_getCompositorOffsetNanos(mCurrentConfig); mAppOffset = ADisplayConfig_getAppVsyncOffsetNanos(mCurrentConfig); } diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h index a4207460883e..16a22f4706f5 100644 --- a/libs/hwui/DeviceInfo.h +++ b/libs/hwui/DeviceInfo.h @@ -37,7 +37,7 @@ public: static int32_t getWidth() { return get()->mWidth; } static int32_t getHeight() { return get()->mHeight; } static float getDensity() { return get()->mDensity; } - static float getRefreshRate() { return get()->mRefreshRate; } + static int64_t getVsyncPeriod() { return get()->mVsyncPeriod; } static int64_t getCompositorOffset() { return get()->mCompositorOffset; } static int64_t getAppOffset() { return get()->mAppOffset; } @@ -47,7 +47,8 @@ public: sk_sp getWideColorSpace() const { return mWideColorSpace; } SkColorType getWideColorType() const { return mWideColorType; } - void onDisplayConfigChanged(); + // This method should be called whenever the display refresh rate changes. + void onRefreshRateChanged(int64_t vsyncPeriod); private: friend class renderthread::RenderThread; @@ -68,7 +69,7 @@ private: int32_t mWidth = 1080; int32_t mHeight = 1920; float mDensity = 2.0; - float mRefreshRate = 60.0; + int64_t mVsyncPeriod = 16666666; int64_t mCompositorOffset = 0; int64_t mAppOffset = 0; }; diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp index 10e7160e7069..d25fc4b0b03e 100644 --- a/libs/hwui/JankTracker.cpp +++ b/libs/hwui/JankTracker.cpp @@ -81,7 +81,7 @@ static FrameInfoIndex sFrameStart = FrameInfoIndex::IntendedVsync; JankTracker::JankTracker(ProfileDataContainer* globalData) { mGlobalData = globalData; - nsecs_t frameIntervalNanos = static_cast(1_s / DeviceInfo::getRefreshRate()); + nsecs_t frameIntervalNanos = DeviceInfo::getVsyncPeriod(); nsecs_t sfOffset = DeviceInfo::getCompositorOffset(); nsecs_t offsetDelta = sfOffset - DeviceInfo::getAppOffset(); // There are two different offset cases. If the offsetDelta is positive diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index a446858ca565..d78f641d45b9 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -34,7 +34,6 @@ #include #include -#include #include #include #include @@ -45,53 +44,43 @@ namespace android { namespace uirenderer { namespace renderthread { -// Number of events to read at a time from the DisplayEventReceiver pipe. -// The value should be large enough that we can quickly drain the pipe -// using just a few large reads. -static const size_t EVENT_BUFFER_SIZE = 100; - static bool gHasRenderThreadInstance = false; static JVMAttachHook gOnStartHook = nullptr; -class DisplayEventReceiverWrapper : public VsyncSource { +void RenderThread::frameCallback(int64_t frameTimeNanos, void* data) { + RenderThread* rt = reinterpret_cast(data); + rt->mVsyncRequested = false; + if (rt->timeLord().vsyncReceived(frameTimeNanos) && !rt->mFrameCallbackTaskPending) { + ATRACE_NAME("queue mFrameCallbackTask"); + rt->mFrameCallbackTaskPending = true; + nsecs_t runAt = (frameTimeNanos + rt->mDispatchFrameDelay); + rt->queue().postAt(runAt, [=]() { rt->dispatchFrameCallbacks(); }); + } +} + +void RenderThread::refreshRateCallback(int64_t vsyncPeriod, void* data) { + ATRACE_NAME("refreshRateCallback"); + RenderThread* rt = reinterpret_cast(data); + DeviceInfo::get()->onRefreshRateChanged(vsyncPeriod); + rt->setupFrameInterval(); +} + +class ChoreographerSource : public VsyncSource { public: - DisplayEventReceiverWrapper(std::unique_ptr&& receiver, - const std::function& onDisplayConfigChanged) - : mDisplayEventReceiver(std::move(receiver)) - , mOnDisplayConfigChanged(onDisplayConfigChanged) {} + ChoreographerSource(RenderThread* renderThread) : mRenderThread(renderThread) {} virtual void requestNextVsync() override { - status_t status = mDisplayEventReceiver->requestNextVsync(); - LOG_ALWAYS_FATAL_IF(status != NO_ERROR, "requestNextVsync failed with status: %d", status); + AChoreographer_postFrameCallback64(mRenderThread->mChoreographer, + RenderThread::frameCallback, mRenderThread); } - virtual nsecs_t latestVsyncEvent() override { - DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE]; - nsecs_t latest = 0; - ssize_t n; - while ((n = mDisplayEventReceiver->getEvents(buf, EVENT_BUFFER_SIZE)) > 0) { - for (ssize_t i = 0; i < n; i++) { - const DisplayEventReceiver::Event& ev = buf[i]; - switch (ev.header.type) { - case DisplayEventReceiver::DISPLAY_EVENT_VSYNC: - latest = ev.header.timestamp; - break; - case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED: - mOnDisplayConfigChanged(); - break; - } - } - } - if (n < 0) { - ALOGW("Failed to get events from display event receiver, status=%d", status_t(n)); - } - return latest; + virtual void drainPendingEvents() override { + AChoreographer_handlePendingEvents(mRenderThread->mChoreographer, mRenderThread); } private: - std::unique_ptr mDisplayEventReceiver; - std::function mOnDisplayConfigChanged; + RenderThread* mRenderThread; }; class DummyVsyncSource : public VsyncSource { @@ -99,11 +88,14 @@ public: DummyVsyncSource(RenderThread* renderThread) : mRenderThread(renderThread) {} virtual void requestNextVsync() override { - mRenderThread->queue().postDelayed(16_ms, - [this]() { mRenderThread->drainDisplayEventQueue(); }); + mRenderThread->queue().postDelayed(16_ms, [this]() { + RenderThread::frameCallback(systemTime(SYSTEM_TIME_MONOTONIC), mRenderThread); + }); } - virtual nsecs_t latestVsyncEvent() override { return systemTime(SYSTEM_TIME_MONOTONIC); } + virtual void drainPendingEvents() override { + RenderThread::frameCallback(systemTime(SYSTEM_TIME_MONOTONIC), mRenderThread); + } private: RenderThread* mRenderThread; @@ -145,29 +137,24 @@ RenderThread::RenderThread() } RenderThread::~RenderThread() { + // Note that if this fatal assertion is removed then member variables must + // be properly destroyed. LOG_ALWAYS_FATAL("Can't destroy the render thread"); } -void RenderThread::initializeDisplayEventReceiver() { - LOG_ALWAYS_FATAL_IF(mVsyncSource, "Initializing a second DisplayEventReceiver?"); +void RenderThread::initializeChoreographer() { + LOG_ALWAYS_FATAL_IF(mVsyncSource, "Initializing a second Choreographer?"); if (!Properties::isolatedProcess) { - auto receiver = std::make_unique( - ISurfaceComposer::eVsyncSourceApp, - ISurfaceComposer::eConfigChangedDispatch); - status_t status = receiver->initCheck(); - LOG_ALWAYS_FATAL_IF(status != NO_ERROR, - "Initialization of DisplayEventReceiver " - "failed with status: %d", - status); + mChoreographer = AChoreographer_create(); + LOG_ALWAYS_FATAL_IF(mChoreographer == nullptr, "Initialization of Choreographer failed"); + AChoreographer_registerRefreshRateCallback(mChoreographer, + RenderThread::refreshRateCallback, this); // Register the FD - mLooper->addFd(receiver->getFd(), 0, Looper::EVENT_INPUT, - RenderThread::displayEventReceiverCallback, this); - mVsyncSource = new DisplayEventReceiverWrapper(std::move(receiver), [this] { - DeviceInfo::get()->onDisplayConfigChanged(); - setupFrameInterval(); - }); + mLooper->addFd(AChoreographer_getFd(mChoreographer), 0, Looper::EVENT_INPUT, + RenderThread::choreographerCallback, this); + mVsyncSource = new ChoreographerSource(this); } else { mVsyncSource = new DummyVsyncSource(this); } @@ -175,7 +162,7 @@ void RenderThread::initializeDisplayEventReceiver() { void RenderThread::initThreadLocals() { setupFrameInterval(); - initializeDisplayEventReceiver(); + initializeChoreographer(); mEglManager = new EglManager(); mRenderState = new RenderState(*this); mVkManager = new VulkanManager(); @@ -183,7 +170,7 @@ void RenderThread::initThreadLocals() { } void RenderThread::setupFrameInterval() { - nsecs_t frameIntervalNanos = static_cast(1000000000 / DeviceInfo::getRefreshRate()); + nsecs_t frameIntervalNanos = DeviceInfo::getVsyncPeriod(); mTimeLord.setFrameInterval(frameIntervalNanos); mDispatchFrameDelay = static_cast(frameIntervalNanos * .25f); } @@ -288,7 +275,7 @@ void RenderThread::setGrContext(sk_sp context) { } } -int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) { +int RenderThread::choreographerCallback(int fd, int events, void* data) { if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) { ALOGE("Display event receiver pipe was closed or an error occurred. " "events=0x%x", @@ -302,24 +289,10 @@ int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) { events); return 1; // keep the callback } + RenderThread* rt = reinterpret_cast(data); + AChoreographer_handlePendingEvents(rt->mChoreographer, data); - reinterpret_cast(data)->drainDisplayEventQueue(); - - return 1; // keep the callback -} - -void RenderThread::drainDisplayEventQueue() { - ATRACE_CALL(); - nsecs_t vsyncEvent = mVsyncSource->latestVsyncEvent(); - if (vsyncEvent > 0) { - mVsyncRequested = false; - if (mTimeLord.vsyncReceived(vsyncEvent) && !mFrameCallbackTaskPending) { - ATRACE_NAME("queue mFrameCallbackTask"); - mFrameCallbackTaskPending = true; - nsecs_t runAt = (vsyncEvent + mDispatchFrameDelay); - queue().postAt(runAt, [this]() { dispatchFrameCallbacks(); }); - } - } + return 1; } void RenderThread::dispatchFrameCallbacks() { @@ -360,7 +333,7 @@ bool RenderThread::threadLoop() { processQueue(); if (mPendingRegistrationFrameCallbacks.size() && !mFrameCallbackTaskPending) { - drainDisplayEventQueue(); + mVsyncSource->drainPendingEvents(); mFrameCallbacks.insert(mPendingRegistrationFrameCallbacks.begin(), mPendingRegistrationFrameCallbacks.end()); mPendingRegistrationFrameCallbacks.clear(); diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index da79e97a6ceb..8be46a6d16e1 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -73,10 +74,11 @@ protected: struct VsyncSource { virtual void requestNextVsync() = 0; - virtual nsecs_t latestVsyncEvent() = 0; + virtual void drainPendingEvents() = 0; virtual ~VsyncSource() {} }; +class ChoreographerSource; class DummyVsyncSource; typedef void (*JVMAttachHook)(const char* name); @@ -136,6 +138,7 @@ private: friend class DispatchFrameCallbacks; friend class RenderProxy; friend class DummyVsyncSource; + friend class ChoreographerSource; friend class android::uirenderer::AutoBackendTextureRelease; friend class android::uirenderer::TestUtils; friend class android::uirenderer::WebViewFunctor; @@ -149,13 +152,21 @@ private: static RenderThread& getInstance(); void initThreadLocals(); - void initializeDisplayEventReceiver(); + void initializeChoreographer(); void setupFrameInterval(); - static int displayEventReceiverCallback(int fd, int events, void* data); + // Callbacks for choreographer events: + // choreographerCallback will call AChoreograper_handleEvent to call the + // corresponding callbacks for each display event type + static int choreographerCallback(int fd, int events, void* data); + // Callback that will be run on vsync ticks. + static void frameCallback(int64_t frameTimeNanos, void* data); + // Callback that will be run whenver there is a refresh rate change. + static void refreshRateCallback(int64_t vsyncPeriod, void* data); void drainDisplayEventQueue(); void dispatchFrameCallbacks(); void requestVsync(); + AChoreographer* mChoreographer; VsyncSource* mVsyncSource; bool mVsyncRequested; std::set mFrameCallbacks; -- cgit v1.2.3 From 2a3d9aaebb57728c8e1d6496a157854f02f7bdd5 Mon Sep 17 00:00:00 2001 From: Dominik Laskowski Date: Mon, 18 Nov 2019 20:26:39 -0800 Subject: Use ui::Rotation in libgui calls Bug: 144601064 Test: Build Change-Id: I46d6974e67dcae2d42766f08c899c56dddd795e9 --- libs/hwui/tests/common/TestContext.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'libs') diff --git a/libs/hwui/tests/common/TestContext.cpp b/libs/hwui/tests/common/TestContext.cpp index 0a54aca4970d..e075d806126b 100644 --- a/libs/hwui/tests/common/TestContext.cpp +++ b/libs/hwui/tests/common/TestContext.cpp @@ -25,16 +25,16 @@ namespace test { static const int IDENT_DISPLAYEVENT = 1; static android::DisplayInfo DUMMY_DISPLAY{ - 1080, // w - 1920, // h - 320.0, // xdpi - 320.0, // ydpi - 60.0, // fps - 2.0, // density - 0, // orientation - false, // secure? - 0, // appVsyncOffset - 0, // presentationDeadline + 1080, // w + 1920, // h + 320.0, // xdpi + 320.0, // ydpi + 60.0, // fps + 2.0, // density + ui::ROTATION_0, // orientation + false, // secure? + 0, // appVsyncOffset + 0, // presentationDeadline }; DisplayInfo getInternalDisplay() { -- cgit v1.2.3 From ffd8cb89e06526a54f03725474d0a97e69d3ace9 Mon Sep 17 00:00:00 2001 From: Anders Frostad Pedersen Date: Wed, 8 Jan 2020 21:54:07 +0100 Subject: Allow zero stride For some pixel formats, stride has no meaning. Take this into account and use buffer width instead. Bug: 143470518 Change-Id: I728b40803e80c4e534504c5b9db55921bb5e7dbc Test: android.graphics.cts.ImageDecoderTest#testConserveMemoryPlusHardware --- libs/hwui/hwui/Bitmap.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index f4149b9b4d7d..84549e8ce6e4 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -152,7 +152,9 @@ sk_sp Bitmap::createFrom(AHardwareBuffer* hardwareBuffer, sk_sp 0 ? bufferDesc.stride : bufferDesc.width; + const size_t rowBytes = info.bytesPerPixel() * bufferStride; return sk_sp(new Bitmap(hardwareBuffer, info, rowBytes, palette)); } -- cgit v1.2.3 From 68ede0717560edbe5fde96c3e847511469c4c0e7 Mon Sep 17 00:00:00 2001 From: Garfield Tan Date: Thu, 9 Jan 2020 11:30:04 -0800 Subject: Move setDisplayViewport to InputReader. InputReader is responsible to associate device and display so it makes sense to allow it set display viewport for pointer controller. Bug: 146385350 Test: Cursor can be associated with external freeform displays as expected. Change-Id: I00d664dd180f1e693b1900582feea8f7ff02f93c --- libs/input/PointerController.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h index 52305b8244a6..ebc622bae302 100644 --- a/libs/input/PointerController.h +++ b/libs/input/PointerController.h @@ -100,6 +100,7 @@ public: virtual int32_t getDisplayId() const; virtual void fade(Transition transition); virtual void unfade(Transition transition); + virtual void setDisplayViewport(const DisplayViewport& viewport); virtual void setPresentation(Presentation presentation); virtual void setSpots(const PointerCoords* spotCoords, @@ -108,7 +109,6 @@ public: void updatePointerIcon(int32_t iconId); void setCustomPointerIcon(const SpriteIcon& icon); - void setDisplayViewport(const DisplayViewport& viewport); void setInactivityTimeout(InactivityTimeout inactivityTimeout); void reloadPointerResources(); -- cgit v1.2.3 From cff969ff51ca706e21099364190b6355044e6647 Mon Sep 17 00:00:00 2001 From: Nathaniel Nifong Date: Thu, 9 Jan 2020 14:03:49 -0500 Subject: [MSKP] Record android device clip restriction (dirty region) with each frame. Test: Confirm recording and normal rendering unaffected on pixel 3 Change-Id: I368a24371317aba26e234649194ae1b05ab0396d --- libs/hwui/pipeline/skia/SkiaPipeline.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 11dc013af6bc..35a885f46919 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -457,7 +457,10 @@ void SkiaPipeline::renderFrameImpl(const SkRect& clip, const Rect& contentDrawBounds, SkCanvas* canvas, const SkMatrix& preTransform) { SkAutoCanvasRestore saver(canvas, true); - canvas->androidFramework_setDeviceClipRestriction(preTransform.mapRect(clip).roundOut()); + auto clipRestriction = preTransform.mapRect(clip).roundOut(); + canvas->androidFramework_setDeviceClipRestriction(clipRestriction); + canvas->drawAnnotation(SkRect::Make(clipRestriction), "AndroidDeviceClipRestriction", + nullptr); canvas->concat(preTransform); // STOPSHIP: Revert, temporary workaround to clear always F16 frame buffer for b/74976293 -- cgit v1.2.3 From 637ba5e8da729fda86ba6c1785947e1476610f8e Mon Sep 17 00:00:00 2001 From: Stan Iliev Date: Fri, 16 Aug 2019 13:43:08 -0400 Subject: Expose HWUI metrics via statsd Add atom definition for HWUI stats. Implement a C++ statsd puller inside GraphicsStatsService service. GraphicsStatsService has new private API, which returns a serialized proto with HWUI stats grouped by application package and version. Test: Ran "adb shell cmd stats pull-source 10068" Test: Ran "statsd_testdrive 10068" and it looks OK Bug: 142665516 Change-Id: I400c0dbf9e25181d36f9018688b03d86839ac3de --- libs/hwui/ProfileData.cpp | 3 + libs/hwui/ProfileData.h | 5 + libs/hwui/protos/graphicsstats.proto | 8 ++ libs/hwui/service/GraphicsStatsService.cpp | 167 ++++++++++++++++++++++++++--- libs/hwui/service/GraphicsStatsService.h | 6 ++ 5 files changed, 173 insertions(+), 16 deletions(-) (limited to 'libs') diff --git a/libs/hwui/ProfileData.cpp b/libs/hwui/ProfileData.cpp index 7921662b213c..a8e36e37905d 100644 --- a/libs/hwui/ProfileData.cpp +++ b/libs/hwui/ProfileData.cpp @@ -15,6 +15,7 @@ */ #include "ProfileData.h" +#include "Properties.h" #include @@ -102,6 +103,7 @@ void ProfileData::mergeWith(const ProfileData& other) { mGPUFrameCounts[i] >>= divider; mGPUFrameCounts[i] += other.mGPUFrameCounts[i]; } + mPipelineType = other.mPipelineType; } void ProfileData::dump(int fd) const { @@ -157,6 +159,7 @@ void ProfileData::reset() { mTotalFrameCount = 0; mJankFrameCount = 0; mStatStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + mPipelineType = Properties::getRenderPipelineType(); } void ProfileData::reportFrame(int64_t duration) { diff --git a/libs/hwui/ProfileData.h b/libs/hwui/ProfileData.h index ccbffc6f136e..dd3ba661dd29 100644 --- a/libs/hwui/ProfileData.h +++ b/libs/hwui/ProfileData.h @@ -16,6 +16,7 @@ #pragma once +#include "Properties.h" #include "utils/Macros.h" #include @@ -65,6 +66,7 @@ public: uint32_t jankFrameCount() const { return mJankFrameCount; } nsecs_t statsStartTime() const { return mStatStartTime; } uint32_t jankTypeCount(JankType type) const { return mJankTypeCounts[static_cast(type)]; } + RenderPipelineType pipelineType() const { return mPipelineType; } struct HistogramEntry { uint32_t renderTimeMs; @@ -103,6 +105,9 @@ private: uint32_t mTotalFrameCount; uint32_t mJankFrameCount; nsecs_t mStatStartTime; + + // true if HWUI renders with Vulkan pipeline + RenderPipelineType mPipelineType; }; // For testing diff --git a/libs/hwui/protos/graphicsstats.proto b/libs/hwui/protos/graphicsstats.proto index 0cd5c6228504..dd5676c9c961 100644 --- a/libs/hwui/protos/graphicsstats.proto +++ b/libs/hwui/protos/graphicsstats.proto @@ -29,6 +29,11 @@ message GraphicsStatsServiceDumpProto { } message GraphicsStatsProto { + enum PipelineType { + GL = 0; + VULKAN = 1; + } + // The package name of the app optional string package_name = 1; @@ -49,6 +54,9 @@ message GraphicsStatsProto { // The gpu frame time histogram for the package repeated GraphicsStatsHistogramBucketProto gpu_histogram = 7; + + // HWUI renders pipeline type: GL or Vulkan + optional PipelineType pipeline = 8; } message GraphicsStatsJankSummaryProto { diff --git a/libs/hwui/service/GraphicsStatsService.cpp b/libs/hwui/service/GraphicsStatsService.cpp index 12c5b836f711..c4186174b637 100644 --- a/libs/hwui/service/GraphicsStatsService.cpp +++ b/libs/hwui/service/GraphicsStatsService.cpp @@ -16,24 +16,28 @@ #include "GraphicsStatsService.h" -#include "JankTracker.h" -#include "protos/graphicsstats.pb.h" - -#include -#include - #include #include +#include #include +#include #include #include #include #include +#include +#include +#include + +#include "JankTracker.h" +#include "protos/graphicsstats.pb.h" + namespace android { namespace uirenderer { using namespace google::protobuf; +using namespace uirenderer::protos; constexpr int32_t sCurrentFileVersion = 1; constexpr int32_t sHeaderSize = 4; @@ -42,9 +46,9 @@ static_assert(sizeof(sCurrentFileVersion) == sHeaderSize, "Header size is wrong" constexpr int sHistogramSize = ProfileData::HistogramSize(); constexpr int sGPUHistogramSize = ProfileData::GPUHistogramSize(); -static bool mergeProfileDataIntoProto(protos::GraphicsStatsProto* proto, - const std::string& package, int64_t versionCode, - int64_t startTime, int64_t endTime, const ProfileData* data); +static bool mergeProfileDataIntoProto(protos::GraphicsStatsProto* proto, const std::string& package, + int64_t versionCode, int64_t startTime, int64_t endTime, + const ProfileData* data); static void dumpAsTextToFd(protos::GraphicsStatsProto* proto, int outFd); class FileDescriptor { @@ -57,7 +61,7 @@ public: } } bool valid() { return mFd != -1; } - operator int() { return mFd; } // NOLINT(google-explicit-constructor) + operator int() { return mFd; } // NOLINT(google-explicit-constructor) private: int mFd; @@ -167,6 +171,8 @@ bool mergeProfileDataIntoProto(protos::GraphicsStatsProto* proto, const std::str } proto->set_package_name(package); proto->set_version_code(versionCode); + proto->set_pipeline(data->pipelineType() == RenderPipelineType::SkiaGL ? + GraphicsStatsProto_PipelineType_GL : GraphicsStatsProto_PipelineType_VULKAN); auto summary = proto->mutable_summary(); summary->set_total_frames(summary->total_frames() + data->totalFrameCount()); summary->set_janky_frames(summary->janky_frames() + data->jankFrameCount()); @@ -179,8 +185,8 @@ bool mergeProfileDataIntoProto(protos::GraphicsStatsProto* proto, const std::str summary->set_slow_bitmap_upload_count(summary->slow_bitmap_upload_count() + data->jankTypeCount(kSlowSync)); summary->set_slow_draw_count(summary->slow_draw_count() + data->jankTypeCount(kSlowRT)); - summary->set_missed_deadline_count(summary->missed_deadline_count() - + data->jankTypeCount(kMissedDeadline)); + summary->set_missed_deadline_count(summary->missed_deadline_count() + + data->jankTypeCount(kMissedDeadline)); bool creatingHistogram = false; if (proto->histogram_size() == 0) { @@ -365,17 +371,69 @@ void GraphicsStatsService::saveBuffer(const std::string& path, const std::string class GraphicsStatsService::Dump { public: - Dump(int outFd, DumpType type) : mFd(outFd), mType(type) {} + Dump(int outFd, DumpType type) : mFd(outFd), mType(type) { + if (mFd == -1 && mType == DumpType::Protobuf) { + mType = DumpType::ProtobufStatsd; + } + } int fd() { return mFd; } DumpType type() { return mType; } protos::GraphicsStatsServiceDumpProto& proto() { return mProto; } + void mergeStat(const protos::GraphicsStatsProto& stat); + void updateProto(); private: + // use package name and app version for a key + typedef std::pair DumpKey; + + std::map mStats; int mFd; DumpType mType; protos::GraphicsStatsServiceDumpProto mProto; }; +void GraphicsStatsService::Dump::mergeStat(const protos::GraphicsStatsProto& stat) { + auto dumpKey = std::make_pair(stat.package_name(), stat.version_code()); + auto findIt = mStats.find(dumpKey); + if (findIt == mStats.end()) { + mStats[dumpKey] = stat; + } else { + auto summary = findIt->second.mutable_summary(); + summary->set_total_frames(summary->total_frames() + stat.summary().total_frames()); + summary->set_janky_frames(summary->janky_frames() + stat.summary().janky_frames()); + summary->set_missed_vsync_count(summary->missed_vsync_count() + + stat.summary().missed_vsync_count()); + summary->set_high_input_latency_count(summary->high_input_latency_count() + + stat.summary().high_input_latency_count()); + summary->set_slow_ui_thread_count(summary->slow_ui_thread_count() + + stat.summary().slow_ui_thread_count()); + summary->set_slow_bitmap_upload_count(summary->slow_bitmap_upload_count() + + stat.summary().slow_bitmap_upload_count()); + summary->set_slow_draw_count(summary->slow_draw_count() + stat.summary().slow_draw_count()); + summary->set_missed_deadline_count(summary->missed_deadline_count() + + stat.summary().missed_deadline_count()); + for (int bucketIndex = 0; bucketIndex < findIt->second.histogram_size(); bucketIndex++) { + auto bucket = findIt->second.mutable_histogram(bucketIndex); + bucket->set_frame_count(bucket->frame_count() + + stat.histogram(bucketIndex).frame_count()); + } + for (int bucketIndex = 0; bucketIndex < findIt->second.gpu_histogram_size(); + bucketIndex++) { + auto bucket = findIt->second.mutable_gpu_histogram(bucketIndex); + bucket->set_frame_count(bucket->frame_count() + + stat.gpu_histogram(bucketIndex).frame_count()); + } + findIt->second.set_stats_start(std::min(findIt->second.stats_start(), stat.stats_start())); + findIt->second.set_stats_end(std::max(findIt->second.stats_end(), stat.stats_end())); + } +} + +void GraphicsStatsService::Dump::updateProto() { + for (auto& stat : mStats) { + mProto.add_stats()->CopyFrom(stat.second); + } +} + GraphicsStatsService::Dump* GraphicsStatsService::createDump(int outFd, DumpType type) { return new Dump(outFd, type); } @@ -396,8 +454,9 @@ void GraphicsStatsService::addToDump(Dump* dump, const std::string& path, path.empty() ? "" : path.c_str(), data); return; } - - if (dump->type() == DumpType::Protobuf) { + if (dump->type() == DumpType::ProtobufStatsd) { + dump->mergeStat(statsProto); + } else if (dump->type() == DumpType::Protobuf) { dump->proto().add_stats()->CopyFrom(statsProto); } else { dumpAsTextToFd(&statsProto, dump->fd()); @@ -409,7 +468,9 @@ void GraphicsStatsService::addToDump(Dump* dump, const std::string& path) { if (!parseFromFile(path, &statsProto)) { return; } - if (dump->type() == DumpType::Protobuf) { + if (dump->type() == DumpType::ProtobufStatsd) { + dump->mergeStat(statsProto); + } else if (dump->type() == DumpType::Protobuf) { dump->proto().add_stats()->CopyFrom(statsProto); } else { dumpAsTextToFd(&statsProto, dump->fd()); @@ -424,5 +485,79 @@ void GraphicsStatsService::finishDump(Dump* dump) { delete dump; } +class MemOutputStreamLite : public io::ZeroCopyOutputStream { +public: + explicit MemOutputStreamLite() : mCopyAdapter(), mImpl(&mCopyAdapter) {} + virtual ~MemOutputStreamLite() {} + + virtual bool Next(void** data, int* size) override { return mImpl.Next(data, size); } + + virtual void BackUp(int count) override { mImpl.BackUp(count); } + + virtual int64 ByteCount() const override { return mImpl.ByteCount(); } + + bool Flush() { return mImpl.Flush(); } + + void copyData(const DumpMemoryFn& reader, void* param1, void* param2) { + int bufferOffset = 0; + int totalSize = mCopyAdapter.mBuffersSize - mCopyAdapter.mCurrentBufferUnusedSize; + int totalDataLeft = totalSize; + for (auto& it : mCopyAdapter.mBuffers) { + int bufferSize = std::min(totalDataLeft, (int)it.size()); // last buffer is not full + reader(it.data(), bufferOffset, bufferSize, totalSize, param1, param2); + bufferOffset += bufferSize; + totalDataLeft -= bufferSize; + } + } + +private: + struct MemAdapter : public io::CopyingOutputStream { + // Data is stored in an array of buffers. + // JNI SetByteArrayRegion assembles data in one continuous Java byte[] buffer. + std::vector> mBuffers; + int mBuffersSize = 0; // total bytes allocated in mBuffers + int mCurrentBufferUnusedSize = 0; // unused bytes in the last buffer mBuffers.back() + unsigned char* mCurrentBuffer = nullptr; // pointer to next free byte in mBuffers.back() + + explicit MemAdapter() {} + virtual ~MemAdapter() {} + + virtual bool Write(const void* buffer, int size) override { + while (size > 0) { + if (0 == mCurrentBufferUnusedSize) { + mCurrentBufferUnusedSize = + std::max(size, mBuffersSize ? 2 * mBuffersSize : 10000); + mBuffers.emplace_back(); + mBuffers.back().resize(mCurrentBufferUnusedSize); + mCurrentBuffer = mBuffers.back().data(); + mBuffersSize += mCurrentBufferUnusedSize; + } + int dataMoved = std::min(mCurrentBufferUnusedSize, size); + memcpy(mCurrentBuffer, buffer, dataMoved); + mCurrentBufferUnusedSize -= dataMoved; + mCurrentBuffer += dataMoved; + buffer = reinterpret_cast(buffer) + dataMoved; + size -= dataMoved; + } + return true; + } + }; + + MemOutputStreamLite::MemAdapter mCopyAdapter; + io::CopyingOutputStreamAdaptor mImpl; +}; + +void GraphicsStatsService::finishDumpInMemory(Dump* dump, const DumpMemoryFn& reader, void* param1, + void* param2) { + MemOutputStreamLite stream; + dump->updateProto(); + bool success = dump->proto().SerializeToZeroCopyStream(&stream) && stream.Flush(); + delete dump; + if (!success) { + return; + } + stream.copyData(reader, param1, param2); +} + } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/service/GraphicsStatsService.h b/libs/hwui/service/GraphicsStatsService.h index 389f599f439f..4bed96330a52 100644 --- a/libs/hwui/service/GraphicsStatsService.h +++ b/libs/hwui/service/GraphicsStatsService.h @@ -27,6 +27,9 @@ namespace protos { class GraphicsStatsProto; } +typedef void (*DumpMemoryFn)(void* buffer, int bufferOffset, int bufferSize, int totalSize, + void* param1, void* param2); + /* * The exported entry points used by GraphicsStatsService.java in f/b/services/core * @@ -40,6 +43,7 @@ public: enum class DumpType { Text, Protobuf, + ProtobufStatsd, }; ANDROID_API static void saveBuffer(const std::string& path, const std::string& package, @@ -52,6 +56,8 @@ public: int64_t startTime, int64_t endTime, const ProfileData* data); ANDROID_API static void addToDump(Dump* dump, const std::string& path); ANDROID_API static void finishDump(Dump* dump); + ANDROID_API static void finishDumpInMemory(Dump* dump, const DumpMemoryFn& reader, void* param1, + void* param2); // Visible for testing static bool parseFromFile(const std::string& path, protos::GraphicsStatsProto* output); -- cgit v1.2.3 From e0fae2356b3ec531baec9f575c99a62ac4bfa004 Mon Sep 17 00:00:00 2001 From: Stan Iliev Date: Tue, 7 Jan 2020 17:21:49 -0500 Subject: Add memory tracing in HWUI Add ATRACE memory counters to track memory usage at the end of each frame in HWUI. There are 3 catagories: CPU, GPU and Texture memory. There are 3 more counters for memory that can be purged. This CL deletes GpuMemoryTracker class, which implemented similar function for Android O HWUI renderer. Test: Collected systrace with gmail and setting. Test: memory tracing adds ~0.1ms per frame when ATRACE is enabled Bug: 146580770 Change-Id: Icbcc0478bc426dff578e83726fe7c95df171ed93 --- libs/hwui/Android.bp | 3 +- libs/hwui/GpuMemoryTracker.cpp | 122 ----------------- libs/hwui/GpuMemoryTracker.h | 77 ----------- libs/hwui/pipeline/skia/ATraceMemoryDump.cpp | 175 +++++++++++++++++++++++++ libs/hwui/pipeline/skia/ATraceMemoryDump.h | 79 +++++++++++ libs/hwui/renderstate/RenderState.cpp | 6 - libs/hwui/renderstate/RenderState.h | 1 - libs/hwui/renderthread/CacheManager.cpp | 14 ++ libs/hwui/renderthread/CacheManager.h | 1 + libs/hwui/renderthread/CanvasContext.cpp | 3 +- libs/hwui/renderthread/RenderThread.cpp | 1 - libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp | 65 --------- 12 files changed, 271 insertions(+), 276 deletions(-) delete mode 100644 libs/hwui/GpuMemoryTracker.cpp delete mode 100644 libs/hwui/GpuMemoryTracker.h create mode 100644 libs/hwui/pipeline/skia/ATraceMemoryDump.cpp create mode 100644 libs/hwui/pipeline/skia/ATraceMemoryDump.h delete mode 100644 libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp (limited to 'libs') diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index d945fc49ec88..a7f8cc4688ef 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -215,6 +215,7 @@ cc_defaults { android: { srcs: [ + "pipeline/skia/ATraceMemoryDump.cpp", "pipeline/skia/GLFunctorDrawable.cpp", "pipeline/skia/LayerDrawable.cpp", "pipeline/skia/ShaderCache.cpp", @@ -244,7 +245,6 @@ cc_defaults { "DeviceInfo.cpp", "FrameInfo.cpp", "FrameInfoVisualizer.cpp", - "GpuMemoryTracker.cpp", "HardwareBitmapUploader.cpp", "HWUIProperties.sysprop", "JankTracker.cpp", @@ -325,7 +325,6 @@ cc_test { "tests/unit/DamageAccumulatorTests.cpp", "tests/unit/DeferredLayerUpdaterTests.cpp", "tests/unit/FatVectorTests.cpp", - "tests/unit/GpuMemoryTrackerTests.cpp", "tests/unit/GraphicsStatsServiceTests.cpp", "tests/unit/LayerUpdateQueueTests.cpp", "tests/unit/LinearAllocatorTests.cpp", diff --git a/libs/hwui/GpuMemoryTracker.cpp b/libs/hwui/GpuMemoryTracker.cpp deleted file mode 100644 index a9a7af8f22f3..000000000000 --- a/libs/hwui/GpuMemoryTracker.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2016 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 "utils/StringUtils.h" - -#include -#include -#include -#include -#include -#include -#include - -namespace android { -namespace uirenderer { - -pthread_t gGpuThread = 0; - -#define NUM_TYPES static_cast(GpuObjectType::TypeCount) - -const char* TYPE_NAMES[] = { - "Texture", "OffscreenBuffer", "Layer", -}; - -struct TypeStats { - int totalSize = 0; - int count = 0; -}; - -static std::array gObjectStats; -static std::unordered_set gObjectSet; - -void GpuMemoryTracker::notifySizeChanged(int newSize) { - int delta = newSize - mSize; - mSize = newSize; - gObjectStats[static_cast(mType)].totalSize += delta; -} - -void GpuMemoryTracker::startTrackingObject() { - auto result = gObjectSet.insert(this); - LOG_ALWAYS_FATAL_IF(!result.second, - "startTrackingObject() on %p failed, already being tracked!", this); - gObjectStats[static_cast(mType)].count++; -} - -void GpuMemoryTracker::stopTrackingObject() { - size_t removed = gObjectSet.erase(this); - LOG_ALWAYS_FATAL_IF(removed != 1, "stopTrackingObject removed %zd, is %p not being tracked?", - removed, this); - gObjectStats[static_cast(mType)].count--; -} - -void GpuMemoryTracker::onGpuContextCreated() { - LOG_ALWAYS_FATAL_IF(gGpuThread != 0, - "We already have a gpu thread? " - "current = %lu, gpu thread = %lu", - pthread_self(), gGpuThread); - gGpuThread = pthread_self(); -} - -void GpuMemoryTracker::onGpuContextDestroyed() { - gGpuThread = 0; - if (CC_UNLIKELY(gObjectSet.size() > 0)) { - std::stringstream os; - dump(os); - ALOGE("%s", os.str().c_str()); - LOG_ALWAYS_FATAL("Leaked %zd GPU objects!", gObjectSet.size()); - } -} - -void GpuMemoryTracker::dump() { - std::stringstream strout; - dump(strout); - ALOGD("%s", strout.str().c_str()); -} - -void GpuMemoryTracker::dump(std::ostream& stream) { - for (int type = 0; type < NUM_TYPES; type++) { - const TypeStats& stats = gObjectStats[type]; - stream << TYPE_NAMES[type]; - stream << " is using " << SizePrinter{stats.totalSize}; - stream << ", count = " << stats.count; - stream << std::endl; - } -} - -int GpuMemoryTracker::getInstanceCount(GpuObjectType type) { - return gObjectStats[static_cast(type)].count; -} - -int GpuMemoryTracker::getTotalSize(GpuObjectType type) { - return gObjectStats[static_cast(type)].totalSize; -} - -void GpuMemoryTracker::onFrameCompleted() { - if (ATRACE_ENABLED()) { - char buf[128]; - for (int type = 0; type < NUM_TYPES; type++) { - snprintf(buf, 128, "hwui_%s", TYPE_NAMES[type]); - const TypeStats& stats = gObjectStats[type]; - ATRACE_INT(buf, stats.totalSize); - snprintf(buf, 128, "hwui_%s_count", TYPE_NAMES[type]); - ATRACE_INT(buf, stats.count); - } - } -} - -} // namespace uirenderer -} // namespace android; diff --git a/libs/hwui/GpuMemoryTracker.h b/libs/hwui/GpuMemoryTracker.h deleted file mode 100644 index de3ca99ef14b..000000000000 --- a/libs/hwui/GpuMemoryTracker.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2016 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. - */ -#pragma once - -#include -#include - -#include - -namespace android { -namespace uirenderer { - -extern pthread_t gGpuThread; - -#define ASSERT_GPU_THREAD() \ - LOG_ALWAYS_FATAL_IF(!pthread_equal(gGpuThread, pthread_self()), \ - "Error, %p of type %d (size=%d) used on wrong thread! cur thread %lu " \ - "!= gpu thread %lu", \ - this, static_cast(mType), mSize, pthread_self(), gGpuThread) - -enum class GpuObjectType { - Texture = 0, - OffscreenBuffer, - Layer, - - TypeCount, -}; - -class GpuMemoryTracker { -public: - GpuObjectType objectType() { return mType; } - int objectSize() { return mSize; } - - static void onGpuContextCreated(); - static void onGpuContextDestroyed(); - static void dump(); - static void dump(std::ostream& stream); - static int getInstanceCount(GpuObjectType type); - static int getTotalSize(GpuObjectType type); - static void onFrameCompleted(); - -protected: - explicit GpuMemoryTracker(GpuObjectType type) : mType(type) { - ASSERT_GPU_THREAD(); - startTrackingObject(); - } - - ~GpuMemoryTracker() { - notifySizeChanged(0); - stopTrackingObject(); - } - - void notifySizeChanged(int newSize); - -private: - void startTrackingObject(); - void stopTrackingObject(); - - int mSize = 0; - GpuObjectType mType; -}; - -} // namespace uirenderer -} // namespace android; diff --git a/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp b/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp new file mode 100644 index 000000000000..2c78b024536b --- /dev/null +++ b/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2020 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 "ATraceMemoryDump.h" + +#include + +#include + +namespace android { +namespace uirenderer { +namespace skiapipeline { + +// When purgeable is INVALID_TIME it won't be logged at all. +#define INVALID_TIME -1 + +/** + * Skia invokes the following SkTraceMemoryDump functions: + * 1. dumpNumericValue (dumpName, units="bytes", valueName="size") + * 2. dumpStringValue (dumpName, valueName="type") [optional -> for example CPU memory does not + * invoke dumpStringValue] + * 3. dumpNumericValue (dumpName, units="bytes", valueName="purgeable_size") [optional] + * 4. setMemoryBacking(dumpName, backingType) [optional -> for example Vulkan GPU resources do not + * invoke setMemoryBacking] + * + * ATraceMemoryDump calculates memory category first by looking at the "type" string passed to + * dumpStringValue and then by looking at "backingType" passed to setMemoryBacking. + * Only GPU Texture memory is tracked separately and everything else is grouped as one + * "GPU Memory" category. + */ +static std::unordered_map sResourceMap = { + {"malloc", "Graphics CPU Memory"}, // taken from setMemoryBacking(backingType) + {"gl_texture", "Graphics Texture Memory"}, // taken from setMemoryBacking(backingType) + {"Texture", + "Graphics Texture Memory"}, // taken from dumpStringValue(value, valueName="type") + // Uncomment categories below to split "GPU Memory" into more brackets for debugging. + /*{"vk_buffer", "vk_buffer"}, + {"gl_renderbuffer", "gl_renderbuffer"}, + {"gl_buffer", "gl_buffer"}, + {"RenderTarget", "RenderTarget"}, + {"Stencil", "Stencil"}, + {"Path Data", "Path Data"}, + {"Buffer Object", "Buffer Object"}, + {"Surface", "Surface"},*/ +}; + +ATraceMemoryDump::ATraceMemoryDump() { + mLastDumpName.reserve(100); + mCategory.reserve(100); +} + +void ATraceMemoryDump::dumpNumericValue(const char* dumpName, const char* valueName, + const char* units, uint64_t value) { + if (!strcmp(units, "bytes")) { + recordAndResetCountersIfNeeded(dumpName); + if (!strcmp(valueName, "size")) { + mLastDumpValue = value; + } else if (!strcmp(valueName, "purgeable_size")) { + mLastPurgeableDumpValue = value; + } + } +} + +void ATraceMemoryDump::dumpStringValue(const char* dumpName, const char* valueName, + const char* value) { + if (!strcmp(valueName, "type")) { + recordAndResetCountersIfNeeded(dumpName); + auto categoryIt = sResourceMap.find(value); + if (categoryIt != sResourceMap.end()) { + mCategory = categoryIt->second; + } + } +} + +void ATraceMemoryDump::setMemoryBacking(const char* dumpName, const char* backingType, + const char* backingObjectId) { + recordAndResetCountersIfNeeded(dumpName); + auto categoryIt = sResourceMap.find(backingType); + if (categoryIt != sResourceMap.end()) { + mCategory = categoryIt->second; + } +} + +/** + * startFrame is invoked before dumping anything. It resets counters from the previous frame. + * This is important, because if there is no new data for a given category trace would assume + * usage has not changed (instead of reporting 0). + */ +void ATraceMemoryDump::startFrame() { + resetCurrentCounter(""); + for (auto& it : mCurrentValues) { + // Once a category is observed in at least one frame, it is always reported in subsequent + // frames (even if it is 0). Not logging a category to ATRACE would mean its value has not + // changed since the previous frame, which is not what we want. + it.second.time = 0; + // If purgeableTime is INVALID_TIME, then logTraces won't log it at all. + if (it.second.purgeableTime != INVALID_TIME) { + it.second.purgeableTime = 0; + } + } +} + +/** + * logTraces reads from mCurrentValues and logs the counters with ATRACE. + */ +void ATraceMemoryDump::logTraces() { + // Accumulate data from last dumpName + recordAndResetCountersIfNeeded(""); + for (auto& it : mCurrentValues) { + ATRACE_INT64(it.first.c_str(), it.second.time); + if (it.second.purgeableTime != INVALID_TIME) { + ATRACE_INT64((std::string("Purgeable ") + it.first).c_str(), it.second.purgeableTime); + } + } +} + +/** + * recordAndResetCountersIfNeeded reads memory usage from mLastDumpValue/mLastPurgeableDumpValue and + * accumulates in mCurrentValues[category]. It makes provision to create a new category and track + * purgeable memory only if there is at least one observation. + * recordAndResetCountersIfNeeded won't do anything until all the information for a given dumpName + * is received. + */ +void ATraceMemoryDump::recordAndResetCountersIfNeeded(const char* dumpName) { + if (!mLastDumpName.compare(dumpName)) { + // Still waiting for more data for current dumpName. + return; + } + + // First invocation will have an empty mLastDumpName. + if (!mLastDumpName.empty()) { + // A new dumpName observed -> store the data already collected. + auto memoryCounter = mCurrentValues.find(mCategory); + if (memoryCounter != mCurrentValues.end()) { + memoryCounter->second.time += mLastDumpValue; + if (mLastPurgeableDumpValue != INVALID_TIME) { + if (memoryCounter->second.purgeableTime == INVALID_TIME) { + memoryCounter->second.purgeableTime = mLastPurgeableDumpValue; + } else { + memoryCounter->second.purgeableTime += mLastPurgeableDumpValue; + } + } + } else { + mCurrentValues[mCategory] = {mLastDumpValue, mLastPurgeableDumpValue}; + } + } + + // Reset counters and default category for the newly observed "dumpName". + resetCurrentCounter(dumpName); +} + +void ATraceMemoryDump::resetCurrentCounter(const char* dumpName) { + mLastDumpValue = 0; + mLastPurgeableDumpValue = INVALID_TIME; + mLastDumpName = dumpName; + // Categories not listed in sResourceMap are reported as "GPU memory" + mCategory = "GPU Memory"; +} + +} /* namespace skiapipeline */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/pipeline/skia/ATraceMemoryDump.h b/libs/hwui/pipeline/skia/ATraceMemoryDump.h new file mode 100644 index 000000000000..aa5c401ad1ca --- /dev/null +++ b/libs/hwui/pipeline/skia/ATraceMemoryDump.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2020 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. + */ + +#pragma once + +#include +#include + +#include +#include +#include + +namespace android { +namespace uirenderer { +namespace skiapipeline { + +class ATraceMemoryDump : public SkTraceMemoryDump { +public: + ATraceMemoryDump(); + ~ATraceMemoryDump() override {} + + void dumpNumericValue(const char* dumpName, const char* valueName, const char* units, + uint64_t value) override; + + void dumpStringValue(const char* dumpName, const char* valueName, const char* value) override; + + LevelOfDetail getRequestedDetails() const override { + return SkTraceMemoryDump::kLight_LevelOfDetail; + } + + bool shouldDumpWrappedObjects() const override { return false; } + + void setMemoryBacking(const char* dumpName, const char* backingType, + const char* backingObjectId) override; + + void setDiscardableMemoryBacking(const char*, const SkDiscardableMemory&) override {} + + void startFrame(); + + void logTraces(); + +private: + std::string mLastDumpName; + + uint64_t mLastDumpValue; + + uint64_t mLastPurgeableDumpValue; + + std::string mCategory; + + struct TraceValue { + uint64_t time; + uint64_t purgeableTime; + }; + + // keys are define in sResourceMap + std::unordered_map mCurrentValues; + + void recordAndResetCountersIfNeeded(const char* dumpName); + + void resetCurrentCounter(const char* dumpName); +}; + +} /* namespace skiapipeline */ +} /* namespace uirenderer */ +} /* namespace android */ \ No newline at end of file diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp index fad9440be73f..7e8c96d96860 100644 --- a/libs/hwui/renderstate/RenderState.cpp +++ b/libs/hwui/renderstate/RenderState.cpp @@ -16,7 +16,6 @@ #include "renderstate/RenderState.h" #include "renderthread/RenderThread.h" -#include "GpuMemoryTracker.h" namespace android { namespace uirenderer { @@ -25,15 +24,10 @@ RenderState::RenderState(renderthread::RenderThread& thread) : mRenderThread(thr mThreadId = pthread_self(); } -void RenderState::onContextCreated() { - GpuMemoryTracker::onGpuContextCreated(); -} - void RenderState::onContextDestroyed() { for(auto callback : mContextCallbacks) { callback->onContextDestroyed(); } - GpuMemoryTracker::onGpuContextDestroyed(); } void RenderState::postDecStrong(VirtualLightRefBase* object) { diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h index ff5d02fe359a..e08d32a7735c 100644 --- a/libs/hwui/renderstate/RenderState.h +++ b/libs/hwui/renderstate/RenderState.h @@ -62,7 +62,6 @@ private: ~RenderState() {} // Context notifications are only to be triggered by renderthread::RenderThread - void onContextCreated(); void onContextDestroyed(); std::set mContextCallbacks; diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index eaed46c44e5d..d177855e5a7d 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -20,10 +20,12 @@ #include "Layer.h" #include "Properties.h" #include "RenderThread.h" +#include "pipeline/skia/ATraceMemoryDump.h" #include "pipeline/skia/ShaderCache.h" #include "pipeline/skia/SkiaMemoryTracer.h" #include "renderstate/RenderState.h" #include "thread/CommonPool.h" +#include #include #include @@ -184,6 +186,18 @@ void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) gpuTracer.logTotals(log); } +void CacheManager::onFrameCompleted() { + if (ATRACE_ENABLED()) { + static skiapipeline::ATraceMemoryDump tracer; + tracer.startFrame(); + SkGraphics::DumpMemoryStatistics(&tracer); + if (mGrContext) { + mGrContext->dumpMemoryStatistics(&tracer); + } + tracer.logTraces(); + } +} + } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h index 968251e9f467..b009cc4f48f2 100644 --- a/libs/hwui/renderthread/CacheManager.h +++ b/libs/hwui/renderthread/CacheManager.h @@ -50,6 +50,7 @@ public: size_t getCacheSize() const { return mMaxResourceBytes; } size_t getBackgroundCacheSize() const { return mBackgroundResourceBytes; } + void onFrameCompleted(); private: friend class RenderThread; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 84902210a751..5993e176f0b8 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -16,7 +16,6 @@ #include "CanvasContext.h" -#include #include #include #include @@ -558,7 +557,7 @@ void CanvasContext::draw() { mJankTracker.finishGpuDraw(*forthBehind); } - GpuMemoryTracker::onFrameCompleted(); + mRenderThread.cacheManager().onFrameCompleted(); } // Called by choreographer to do an RT-driven animation diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index d78f641d45b9..cae3e3b5188c 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -270,7 +270,6 @@ void RenderThread::setGrContext(sk_sp context) { } mGrContext = std::move(context); if (mGrContext) { - mRenderState->onContextCreated(); DeviceInfo::setMaxTextureSize(mGrContext->maxRenderTargetSize()); } } diff --git a/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp b/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp deleted file mode 100644 index dac888cd79ca..000000000000 --- a/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2016 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 -#include - -#include "renderthread/EglManager.h" -#include "renderthread/RenderThread.h" -#include "tests/common/TestUtils.h" - -#include - -using namespace android; -using namespace android::uirenderer; -using namespace android::uirenderer::renderthread; - -class TestGPUObject : public GpuMemoryTracker { -public: - TestGPUObject() : GpuMemoryTracker(GpuObjectType::Texture) {} - - void changeSize(int newSize) { notifySizeChanged(newSize); } -}; - -// Other tests may have created a renderthread and EGL context. -// This will destroy the EGLContext on RenderThread if it exists so that the -// current thread can spoof being a GPU thread -static void destroyEglContext() { - if (TestUtils::isRenderThreadRunning()) { - TestUtils::runOnRenderThread([](RenderThread& thread) { thread.destroyRenderingContext(); }); - } -} - -TEST(GpuMemoryTracker, sizeCheck) { - destroyEglContext(); - - GpuMemoryTracker::onGpuContextCreated(); - ASSERT_EQ(0, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture)); - ASSERT_EQ(0, GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture)); - { - TestGPUObject myObj; - ASSERT_EQ(1, GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture)); - myObj.changeSize(500); - ASSERT_EQ(500, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture)); - myObj.changeSize(1000); - ASSERT_EQ(1000, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture)); - myObj.changeSize(300); - ASSERT_EQ(300, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture)); - } - ASSERT_EQ(0, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture)); - ASSERT_EQ(0, GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture)); - GpuMemoryTracker::onGpuContextDestroyed(); -} -- cgit v1.2.3 From 52d3777a26d654ee4c0651e6894c97b5d4d78fdd Mon Sep 17 00:00:00 2001 From: Nathaniel Nifong Date: Fri, 10 Jan 2020 16:12:41 -0500 Subject: Include text draws in looper refactor Test: built for pixel3, confirm shadows on text of launcher icons visible on a background image that shows it well. Bug:144199311 Change-Id: Ia2e71a1f0982ef94a9b0c0573bf76ead0c0b9b81 --- libs/hwui/SkiaCanvas.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'libs') diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 23140724ef64..12681ae221d5 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -746,7 +746,10 @@ void SkiaCanvas::drawGlyphs(ReadGlyphFunc glyphFunc, int count, const Paint& pai glyphFunc(buffer.glyphs, buffer.pos); sk_sp textBlob(builder.make()); - mCanvas->drawTextBlob(textBlob, 0, 0, paintCopy); + + apply_looper(&paintCopy, [&](const SkPaint& p) { + mCanvas->drawTextBlob(textBlob, 0, 0, p); + }); drawTextDecorations(x, y, totalAdvance, paintCopy); } @@ -783,8 +786,10 @@ void SkiaCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, xform[i - start].fTx = pos.x() - tan.y() * y - halfWidth * tan.x(); xform[i - start].fTy = pos.y() + tan.x() * y - halfWidth * tan.y(); } - - this->asSkCanvas()->drawTextBlob(builder.make(), 0, 0, paintCopy); + auto* finalCanvas = this->asSkCanvas(); + apply_looper(&paintCopy, [&](const SkPaint& p) { + finalCanvas->drawTextBlob(builder.make(), 0, 0, paintCopy); + }); } // ---------------------------------------------------------------------------- -- cgit v1.2.3 From fe50d739f75e13ebf64c010bf6ef504bcc81d860 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Thu, 19 Dec 2019 16:12:35 -0800 Subject: Assign shared libraries stable package ids When a shared library package is loaded into an AssetManager, the shared library will be assigned a unique package id. Subsequent AssetManaagers that load a shared library of the same package name as the original shared library will use previously assigned package name. Shared libraries will have stable package ids throughout the lifetime of application. Bug: 140790224 Bug: 128496033 Test: libandroidfw_tests Test: third-party app no longer crashes on open Test: atest CtsHostsideWebViewTests Change-Id: Idc0315be21ea00b74d1a918b7083ad655104c008 --- libs/androidfw/Android.bp | 1 + libs/androidfw/AssetManager2.cpp | 72 ++++++++++++++++------ libs/androidfw/DynamicLibManager.cpp | 34 ++++++++++ libs/androidfw/include/androidfw/AssetManager2.h | 7 +++ .../include/androidfw/DynamicLibManager.h | 48 +++++++++++++++ libs/androidfw/include/androidfw/MutexGuard.h | 3 +- libs/androidfw/tests/AssetManager2_test.cpp | 19 ++++++ 7 files changed, 164 insertions(+), 20 deletions(-) create mode 100644 libs/androidfw/DynamicLibManager.cpp create mode 100644 libs/androidfw/include/androidfw/DynamicLibManager.h (limited to 'libs') diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index 87657191e901..3f2f3495ae8f 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -44,6 +44,7 @@ cc_library { "AttributeResolution.cpp", "ChunkIterator.cpp", "ConfigDescription.cpp", + "DynamicLibManager.cpp", "Idmap.cpp", "LoadedArsc.cpp", "Locale.cpp", diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index ca4143f3e215..2c6be41052e8 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -25,6 +25,7 @@ #include "android-base/logging.h" #include "android-base/stringprintf.h" +#include "androidfw/DynamicLibManager.h" #include "androidfw/ResourceUtils.h" #include "androidfw/Util.h" #include "utils/ByteOrder.h" @@ -66,7 +67,12 @@ struct FindEntryResult { StringPoolRef entry_string_ref; }; -AssetManager2::AssetManager2() { +AssetManager2::AssetManager2() : dynamic_lib_manager_(std::make_unique()) { + memset(&configuration_, 0, sizeof(configuration_)); +} + +AssetManager2::AssetManager2(DynamicLibManager* dynamic_lib_manager) + : dynamic_lib_manager_(dynamic_lib_manager) { memset(&configuration_, 0, sizeof(configuration_)); } @@ -85,24 +91,44 @@ void AssetManager2::BuildDynamicRefTable() { package_groups_.clear(); package_ids_.fill(0xff); - // A mapping from apk assets path to the runtime package id of its first loaded package. + // Overlay resources are not directly referenced by an application so their resource ids + // can change throughout the application's lifetime. Assign overlay package ids last. + std::vector sorted_apk_assets(apk_assets_); + std::stable_partition(sorted_apk_assets.begin(), sorted_apk_assets.end(), [](const ApkAssets* a) { + return !a->IsOverlay(); + }); + std::unordered_map apk_assets_package_ids; + std::unordered_map package_name_package_ids; + + // Assign stable package ids to application packages. + uint8_t next_available_package_id = 0U; + for (const auto& apk_assets : sorted_apk_assets) { + for (const auto& package : apk_assets->GetLoadedArsc()->GetPackages()) { + uint8_t package_id = package->GetPackageId(); + if (package->IsOverlay()) { + package_id = GetDynamicLibManager()->FindUnassignedId(next_available_package_id); + next_available_package_id = package_id + 1; + } else if (package->IsDynamic()) { + package_id = GetDynamicLibManager()->GetAssignedId(package->GetPackageName()); + } - // 0x01 is reserved for the android package. - int next_package_id = 0x02; - const size_t apk_assets_count = apk_assets_.size(); - for (size_t i = 0; i < apk_assets_count; i++) { - const ApkAssets* apk_assets = apk_assets_[i]; - const LoadedArsc* loaded_arsc = apk_assets->GetLoadedArsc(); + // Map the path of the apk assets to the package id of its first loaded package. + apk_assets_package_ids[apk_assets->GetPath()] = package_id; - for (const std::unique_ptr& package : loaded_arsc->GetPackages()) { - // Get the package ID or assign one if a shared library. - int package_id; - if (package->IsDynamic()) { - package_id = next_package_id++; - } else { - package_id = package->GetPackageId(); - } + // Map the package name of the package to the first loaded package with that package id. + package_name_package_ids[package->GetPackageName()] = package_id; + } + } + + const int apk_assets_count = apk_assets_.size(); + for (int i = 0; i < apk_assets_count; i++) { + const auto& apk_assets = apk_assets_[i]; + for (const auto& package : apk_assets->GetLoadedArsc()->GetPackages()) { + const auto package_id_entry = package_name_package_ids.find(package->GetPackageName()); + CHECK(package_id_entry != package_name_package_ids.end()) + << "no package id assgined to package " << package->GetPackageName(); + const uint8_t package_id = package_id_entry->second; // Add the mapping for package ID to index if not present. uint8_t idx = package_ids_[package_id]; @@ -123,7 +149,7 @@ void AssetManager2::BuildDynamicRefTable() { PackageGroup& target_package_group = package_groups_[target_idx]; - // Create a special dynamic reference table for the overlay to rewite references to + // Create a special dynamic reference table for the overlay to rewrite references to // overlay resources as references to the target resources they overlay. auto overlay_table = std::make_shared( loaded_idmap->GetOverlayDynamicRefTable(target_package_id)); @@ -153,8 +179,6 @@ void AssetManager2::BuildDynamicRefTable() { package_group->dynamic_ref_table->mEntries.replaceValueFor( package_name, static_cast(entry.package_id)); } - - apk_assets_package_ids.insert(std::make_pair(apk_assets->GetPath(), package_id)); } } @@ -1279,6 +1303,16 @@ uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) const return 0; } +DynamicLibManager* AssetManager2::GetDynamicLibManager() const { + auto dynamic_lib_manager = + std::get_if>(&dynamic_lib_manager_); + if (dynamic_lib_manager) { + return (*dynamic_lib_manager).get(); + } else { + return *std::get_if(&dynamic_lib_manager_); + } +} + std::unique_ptr AssetManager2::NewTheme() { return std::unique_ptr(new Theme(this)); } diff --git a/libs/androidfw/DynamicLibManager.cpp b/libs/androidfw/DynamicLibManager.cpp new file mode 100644 index 000000000000..895b7695bf26 --- /dev/null +++ b/libs/androidfw/DynamicLibManager.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2019 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 "androidfw/DynamicLibManager.h" + +namespace android { + +uint8_t DynamicLibManager::GetAssignedId(const std::string& library_package_name) { + auto lib_entry = shared_lib_package_ids_.find(library_package_name); + if (lib_entry != shared_lib_package_ids_.end()) { + return lib_entry->second; + } + + return shared_lib_package_ids_[library_package_name] = next_package_id_++; +} + +uint8_t DynamicLibManager::FindUnassignedId(uint8_t start_package_id) { + return (start_package_id < next_package_id_) ? next_package_id_ : start_package_id; +} + +} // namespace android diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index 00cbbcad56e6..b2cec2a42994 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -27,6 +27,7 @@ #include "androidfw/ApkAssets.h" #include "androidfw/Asset.h" #include "androidfw/AssetManager.h" +#include "androidfw/DynamicLibManager.h" #include "androidfw/ResourceTypes.h" #include "androidfw/Util.h" @@ -94,6 +95,7 @@ class AssetManager2 { }; AssetManager2(); + explicit AssetManager2(DynamicLibManager* dynamic_lib_manager); // Sets/resets the underlying ApkAssets for this AssetManager. The ApkAssets // are not owned by the AssetManager, and must have a longer lifetime. @@ -371,6 +373,8 @@ class AssetManager2 { // Retrieve the assigned package id of the package if loaded into this AssetManager uint8_t GetAssignedPackageId(const LoadedPackage* package) const; + DynamicLibManager* GetDynamicLibManager() const; + // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must // have a longer lifetime. std::vector apk_assets_; @@ -389,6 +393,9 @@ class AssetManager2 { // may need to be purged. ResTable_config configuration_; + // Component responsible for assigning package ids to shared libraries. + std::variant, DynamicLibManager*> dynamic_lib_manager_; + // Cached set of bags. These are cached because they can inherit keys from parent bags, // which involves some calculation. std::unordered_map> cached_bags_; diff --git a/libs/androidfw/include/androidfw/DynamicLibManager.h b/libs/androidfw/include/androidfw/DynamicLibManager.h new file mode 100644 index 000000000000..1ff7079573d2 --- /dev/null +++ b/libs/androidfw/include/androidfw/DynamicLibManager.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2019 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 ANDROIDFW_DYNAMICLIBMANAGER_H +#define ANDROIDFW_DYNAMICLIBMANAGER_H + +#include +#include + +#include "android-base/macros.h" + +namespace android { + +// Manages assigning resource ids for dynamic resources. +class DynamicLibManager { + public: + DynamicLibManager() = default; + + // Retrieves the assigned package id for the library. + uint8_t GetAssignedId(const std::string& library_package_name); + + // Queries in ascending order for the first available package id that is not currently assigned to + // a library. + uint8_t FindUnassignedId(uint8_t start_package_id); + + private: + DISALLOW_COPY_AND_ASSIGN(DynamicLibManager); + + uint8_t next_package_id_ = 0x02; + std::unordered_map shared_lib_package_ids_; +}; + +} // namespace android + +#endif //ANDROIDFW_DYNAMICLIBMANAGER_H diff --git a/libs/androidfw/include/androidfw/MutexGuard.h b/libs/androidfw/include/androidfw/MutexGuard.h index 64924f433245..8891512958f0 100644 --- a/libs/androidfw/include/androidfw/MutexGuard.h +++ b/libs/androidfw/include/androidfw/MutexGuard.h @@ -47,7 +47,8 @@ class Guarded { static_assert(!std::is_pointer::value, "T must not be a raw pointer"); public: - explicit Guarded() : guarded_() { + template + explicit Guarded(Args&& ...args) : guarded_(std::forward(args)...) { } template diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index b3190be8caf1..2f6f3dfcaf1c 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -214,6 +214,25 @@ TEST_F(AssetManager2Test, FindsResourceFromAppLoadedAsSharedLibrary) { EXPECT_EQ(fix_package_id(appaslib::R::array::integerArray1, 0x02), value.data); } +TEST_F(AssetManager2Test, AssignsUnchangingPackageIdToSharedLibrary) { + DynamicLibManager lib_manager; + AssetManager2 assetmanager(&lib_manager); + assetmanager.SetApkAssets( + {lib_one_assets_.get(), lib_two_assets_.get(), libclient_assets_.get()}); + + AssetManager2 assetmanager2(&lib_manager); + assetmanager2.SetApkAssets( + {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()}); + + uint32_t res_id = assetmanager.GetResourceId("com.android.lib_one:string/foo"); + ASSERT_NE(0U, res_id); + + uint32_t res_id_2 = assetmanager2.GetResourceId("com.android.lib_one:string/foo"); + ASSERT_NE(0U, res_id_2); + + ASSERT_EQ(res_id, res_id_2); +} + TEST_F(AssetManager2Test, GetSharedLibraryResourceName) { AssetManager2 assetmanager; assetmanager.SetApkAssets({lib_one_assets_.get()}); -- cgit v1.2.3 From ee4a564d4ff2d16b6cdb6972e3aa92cd72668229 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Wed, 16 Oct 2019 08:32:55 -0700 Subject: Allow for overlaying dynamic shared libraries Overlays targeting shared libraries should be loaded into the resources of every target that depends on the shared library. Static shared libraries are currently not supported because overlays should override all versions of static shared libraries and there is not currently support for an overlay targeting multiple APKs. Also created a test instrumentation and host test suite for testing overlays and packages on the system image. Bug: 140790224 Test: atest OverlayRemountedTest Change-Id: I20a217b6368d6cf92b2b9f46908fd58012933f72 --- libs/androidfw/AssetManager2.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'libs') diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 2c6be41052e8..8cfd2d8ca696 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -141,7 +141,10 @@ void AssetManager2::BuildDynamicRefTable() { // to take effect. const auto& loaded_idmap = apk_assets->GetLoadedIdmap(); auto target_package_iter = apk_assets_package_ids.find(loaded_idmap->TargetApkPath()); - if (target_package_iter != apk_assets_package_ids.end()) { + if (target_package_iter == apk_assets_package_ids.end()) { + LOG(INFO) << "failed to find target package for overlay " + << loaded_idmap->OverlayApkPath(); + } else { const uint8_t target_package_id = target_package_iter->second; const uint8_t target_idx = package_ids_[target_package_id]; CHECK(target_idx != 0xff) << "overlay added to apk_assets_package_ids but does not" @@ -591,7 +594,7 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri if (resource_resolution_logging_enabled_) { last_resolution_.steps.push_back( Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result.config.toString(), - &package_group.packages_[0].loaded_package_->GetPackageName()}); + overlay_result.package_name}); } } } -- cgit v1.2.3 From e431e316981e5021d496c19c90840f56fc702aba Mon Sep 17 00:00:00 2001 From: Nader Jawad Date: Wed, 4 Dec 2019 14:13:18 -0800 Subject: Added support for antialiased canvas clipping Updated native hwui implementation to pass in optional anti-alias flag to canvas clipping operations. Made the default behavior to enable canvas clipping Bug: 69115461 Test: Added test to CtsUiRenderingTestCases Change-Id: I996f4b56e161cdc9f1ec5eba9c30f94474520af5 --- libs/hwui/SkiaCanvas.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'libs') diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 23140724ef64..7c49a1d653b7 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -217,13 +217,16 @@ public: canvas->setMatrix(mMatrix); switch (mType) { case Type::Rect: - canvas->clipRect(mRRect.rect(), mOp); + // Don't anti-alias rectangular clips + canvas->clipRect(mRRect.rect(), mOp, false); break; case Type::RRect: - canvas->clipRRect(mRRect, mOp); + // Ensure rounded rectangular clips are anti-aliased + canvas->clipRRect(mRRect, mOp, true); break; case Type::Path: - canvas->clipPath(mPath.value(), mOp); + // Ensure path clips are anti-aliased + canvas->clipPath(mPath.value(), mOp, true); break; } } @@ -392,7 +395,7 @@ bool SkiaCanvas::clipRect(float left, float top, float right, float bottom, SkCl bool SkiaCanvas::clipPath(const SkPath* path, SkClipOp op) { this->recordClip(*path, op); - mCanvas->clipPath(*path, op); + mCanvas->clipPath(*path, op, true); return !mCanvas->isClipEmpty(); } -- cgit v1.2.3 From 1ade46d272f1279ce3d3dbe4867b7229ec170aff Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Wed, 15 Jan 2020 05:41:06 -0500 Subject: Replace setAlphaFlags with setUnpremultipliedRequired Bug: 135133301 Test: I48e49ee08ab1954eddf62ecae87942aeb128c10d As described in I3381582e27894e1072db9b8635f3762b801f5d69, this is a more sensible API. In addition, remove unused methods on ImageDecoder. Lastly, update AImageDecoder methods in the map to document which API level they were introduced in. Change-Id: I1aff544e8d6932b9ed0931a00da66a0aba6cd536 --- libs/hwui/hwui/ImageDecoder.cpp | 47 ++++++++++++++++------------------------- libs/hwui/hwui/ImageDecoder.h | 8 +++---- 2 files changed, 22 insertions(+), 33 deletions(-) (limited to 'libs') diff --git a/libs/hwui/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp index 4f2027d978a1..a6c4e9db7280 100644 --- a/libs/hwui/hwui/ImageDecoder.cpp +++ b/libs/hwui/hwui/ImageDecoder.cpp @@ -30,19 +30,25 @@ ImageDecoder::ImageDecoder(std::unique_ptr codec, sk_spgetInfo().dimensions()) , mDecodeSize(mTargetSize) , mOutColorType(mCodec->computeOutputColorType(kN32_SkColorType)) - , mOutAlphaType(mCodec->getInfo().isOpaque() ? - kOpaque_SkAlphaType : kPremul_SkAlphaType) + , mUnpremultipliedRequired(false) , mOutColorSpace(mCodec->getInfo().refColorSpace()) , mSampleSize(1) { } +SkAlphaType ImageDecoder::getOutAlphaType() const { + // While an SkBitmap may want to use kOpaque_SkAlphaType for a performance + // optimization, this class just outputs raw pixels. Using either + // premultiplication choice has no effect on decoding an opaque encoded image. + return mUnpremultipliedRequired ? kUnpremul_SkAlphaType : kPremul_SkAlphaType; +} + bool ImageDecoder::setTargetSize(int width, int height) { if (width <= 0 || height <= 0) { return false; } - auto info = SkImageInfo::Make(width, height, mOutColorType, mOutAlphaType); + auto info = SkImageInfo::Make(width, height, mOutColorType, getOutAlphaType()); size_t rowBytes = info.minRowBytes(); if (rowBytes == 0) { // This would have overflowed. @@ -63,7 +69,7 @@ bool ImageDecoder::setTargetSize(int width, int height) { SkISize targetSize = { width, height }, decodeSize = targetSize; int sampleSize = mCodec->computeSampleSize(&decodeSize); - if (decodeSize != targetSize && mOutAlphaType == kUnpremul_SkAlphaType + if (decodeSize != targetSize && mUnpremultipliedRequired && !mCodec->getInfo().isOpaque()) { return false; } @@ -119,29 +125,11 @@ bool ImageDecoder::setOutColorType(SkColorType colorType) { return true; } -bool ImageDecoder::setOutAlphaType(SkAlphaType alpha) { - switch (alpha) { - case kOpaque_SkAlphaType: - return opaque(); - case kPremul_SkAlphaType: - if (opaque()) { - // Opaque can be treated as premul. - return true; - } - break; - case kUnpremul_SkAlphaType: - if (opaque()) { - // Opaque can be treated as unpremul. - return true; - } - if (mDecodeSize != mTargetSize) { - return false; - } - break; - default: - return false; +bool ImageDecoder::setUnpremultipliedRequired(bool required) { + if (required && !opaque() && mDecodeSize != mTargetSize) { + return false; } - mOutAlphaType = alpha; + mUnpremultipliedRequired = required; return true; } @@ -151,11 +139,11 @@ void ImageDecoder::setOutColorSpace(sk_sp colorSpace) { SkImageInfo ImageDecoder::getOutputInfo() const { SkISize size = mCropRect ? mCropRect->size() : mTargetSize; - return SkImageInfo::Make(size, mOutColorType, mOutAlphaType, mOutColorSpace); + return SkImageInfo::Make(size, mOutColorType, getOutAlphaType(), mOutColorSpace); } bool ImageDecoder::opaque() const { - return mOutAlphaType == kOpaque_SkAlphaType; + return mCodec->getInfo().alphaType() == kOpaque_SkAlphaType; } bool ImageDecoder::gray() const { @@ -165,7 +153,8 @@ bool ImageDecoder::gray() const { SkCodec::Result ImageDecoder::decode(void* pixels, size_t rowBytes) { void* decodePixels = pixels; size_t decodeRowBytes = rowBytes; - auto decodeInfo = SkImageInfo::Make(mDecodeSize, mOutColorType, mOutAlphaType, mOutColorSpace); + auto decodeInfo = SkImageInfo::Make(mDecodeSize, mOutColorType, getOutAlphaType(), + mOutColorSpace); // Used if we need a temporary before scaling or subsetting. // FIXME: Use scanline decoding on only a couple lines to save memory. b/70709380. SkBitmap tmp; diff --git a/libs/hwui/hwui/ImageDecoder.h b/libs/hwui/hwui/ImageDecoder.h index b956f4a77793..96f97e5421f3 100644 --- a/libs/hwui/hwui/ImageDecoder.h +++ b/libs/hwui/hwui/ImageDecoder.h @@ -41,14 +41,12 @@ public: bool setOutColorType(SkColorType outColorType); - bool setOutAlphaType(SkAlphaType outAlphaType); + bool setUnpremultipliedRequired(bool unpremultipliedRequired); void setOutColorSpace(sk_sp cs); // The size is the final size after scaling and cropping. SkImageInfo getOutputInfo() const; - SkColorType getOutColorType() const { return mOutColorType; } - SkAlphaType getOutAlphaType() const { return mOutAlphaType; } bool opaque() const; bool gray() const; @@ -59,13 +57,15 @@ private: SkISize mTargetSize; SkISize mDecodeSize; SkColorType mOutColorType; - SkAlphaType mOutAlphaType; + bool mUnpremultipliedRequired; sk_sp mOutColorSpace; int mSampleSize; std::optional mCropRect; ImageDecoder(const ImageDecoder&) = delete; ImageDecoder& operator=(const ImageDecoder&) = delete; + + SkAlphaType getOutAlphaType() const; }; } // namespace android -- cgit v1.2.3 From 700629d8c08d9114091cc605f417514a25e79a9c Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Thu, 16 Jan 2020 14:13:23 -0500 Subject: Support more dataspaces in DataSpaceToColorSpace Bug: 135133301 Test: ? Create an SkColorSpace for HAL_DATASPACE_TRANSFER_ST2084 and HAL_DATASPACE_TRANSFER_SMPTE_170M. This allows converting ADATASPACE_BT709, ADATASPACE_BT2020, and ADATASPACE_BT709 into SkColorSpaces. In addition, treat HAL_DATASPACE_DCI_P3 (aka HAL_DATASPACE_DCI_P3) specially, because its gamut does not match SkNamedGamut::kSRGB. This will allow Ia8ba4c17b517a05b664c6e317e235836473fd7f6 to use DataSpaceToColorSpace, rather than its own, slightly different version. Change-Id: I24fffd79c2bf251c28c2d0b8c3d2889dbffa772d --- libs/hwui/utils/Color.cpp | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) (limited to 'libs') diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp index b93759f87da2..c445885c5c63 100644 --- a/libs/hwui/utils/Color.cpp +++ b/libs/hwui/utils/Color.cpp @@ -108,10 +108,26 @@ SkColorType PixelFormatToColorType(android::PixelFormat format) { } } +// FIXME: Share with the version in android_bitmap.cpp? +// Skia's SkNamedGamut::kDCIP3 is based on a white point of D65. This gamut +// matches the white point used by ColorSpace.Named.DCIP3. +static constexpr skcms_Matrix3x3 kDCIP3 = {{ + {0.486143, 0.323835, 0.154234}, + {0.226676, 0.710327, 0.0629966}, + {0.000800549, 0.0432385, 0.78275}, +}}; + sk_sp DataSpaceToColorSpace(android_dataspace dataspace) { if (dataspace == HAL_DATASPACE_UNKNOWN) { return SkColorSpace::MakeSRGB(); } + if (dataspace == HAL_DATASPACE_DCI_P3) { + // This cannot be handled by the switch statements below because it + // needs to use the locally-defined kDCIP3 gamut, rather than the one in + // Skia (SkNamedGamut), which is used for other data spaces with + // HAL_DATASPACE_STANDARD_DCI_P3 (e.g. HAL_DATASPACE_DISPLAY_P3). + return SkColorSpace::MakeRGB({2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, kDCIP3); + } skcms_Matrix3x3 gamut; switch (dataspace & HAL_DATASPACE_STANDARD_MASK) { @@ -152,10 +168,12 @@ sk_sp DataSpaceToColorSpace(android_dataspace dataspace) { return SkColorSpace::MakeRGB({2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut); case HAL_DATASPACE_TRANSFER_GAMMA2_8: return SkColorSpace::MakeRGB({2.8f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut); + case HAL_DATASPACE_TRANSFER_ST2084: + return SkColorSpace::MakeRGB(SkNamedTransferFn::kPQ, gamut); + case HAL_DATASPACE_TRANSFER_SMPTE_170M: + return SkColorSpace::MakeRGB(SkNamedTransferFn::kRec2020, gamut); case HAL_DATASPACE_TRANSFER_UNSPECIFIED: return nullptr; - case HAL_DATASPACE_TRANSFER_SMPTE_170M: - case HAL_DATASPACE_TRANSFER_ST2084: case HAL_DATASPACE_TRANSFER_HLG: default: ALOGV("Unsupported Gamma: %d", dataspace); -- cgit v1.2.3 From 9010e8ba9163a5c8670d3b1b701d97686db1175f Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Tue, 20 Aug 2019 11:27:17 -0400 Subject: Implement native compress API Bug: 135133301 Test: Ifbcb41388a48afc64bb22623bb7e981b288b2457 Refactor the bulk of Bitmap_compress into hwui/Bitmap::compress, so that it can be shared by the new API. Update its enum to match the proper style. Also make the enum a class so it does not need to have a special return value for a bad parameter, which is now handled by the caller. Add ABitmap_compress, which implements the new API by calling hwui/Bitmap::compress. Change-Id: Ia8ba4c17b517a05b664c6e317e235836473fd7f6 --- libs/hwui/hwui/Bitmap.cpp | 57 ++++++++++++++++++++++++++++++++++++++++++++++- libs/hwui/hwui/Bitmap.h | 22 ++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index 84549e8ce6e4..3c402e996218 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -38,7 +38,7 @@ #include #include - +#include #include #include @@ -471,4 +471,59 @@ BitmapPalette Bitmap::computePalette(const SkImageInfo& info, const void* addr, return BitmapPalette::Unknown; } +Bitmap::CompressResult Bitmap::compress(JavaCompressFormat format, int32_t quality, + SkWStream* stream) { + SkBitmap skbitmap; + getSkBitmap(&skbitmap); + return compress(skbitmap, format, quality, stream); +} + +Bitmap::CompressResult Bitmap::compress(const SkBitmap& bitmap, JavaCompressFormat format, + int32_t quality, SkWStream* stream) { + SkBitmap skbitmap = bitmap; + if (skbitmap.colorType() == kRGBA_F16_SkColorType) { + // Convert to P3 before encoding. This matches + // SkAndroidCodec::computeOutputColorSpace for wide gamuts. Now that F16 + // could already be P3, we still want to convert to 8888. + auto cs = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3); + auto info = skbitmap.info().makeColorType(kRGBA_8888_SkColorType) + .makeColorSpace(std::move(cs)); + SkBitmap p3; + if (!p3.tryAllocPixels(info)) { + return CompressResult::AllocationFailed; + } + + SkPixmap pm; + SkAssertResult(p3.peekPixels(&pm)); // should always work if tryAllocPixels() did. + if (!skbitmap.readPixels(pm)) { + return CompressResult::Error; + } + skbitmap = p3; + } + + SkEncodedImageFormat fm; + switch (format) { + case JavaCompressFormat::Jpeg: + fm = SkEncodedImageFormat::kJPEG; + break; + case JavaCompressFormat::Png: + fm = SkEncodedImageFormat::kPNG; + break; + case JavaCompressFormat::Webp: + fm = SkEncodedImageFormat::kWEBP; + break; + case JavaCompressFormat::WebpLossy: + case JavaCompressFormat::WebpLossless: { + SkWebpEncoder::Options options; + options.fQuality = quality; + options.fCompression = format == JavaCompressFormat::WebpLossy ? + SkWebpEncoder::Compression::kLossy : SkWebpEncoder::Compression::kLossless; + return SkWebpEncoder::Encode(stream, skbitmap.pixmap(), options) + ? CompressResult::Success : CompressResult::Error; + } + } + + return SkEncodeImage(stream, skbitmap, fm, quality) + ? CompressResult::Success : CompressResult::Error; +} } // namespace android diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h index 1cda0465ae64..ee365af2f7be 100644 --- a/libs/hwui/hwui/Bitmap.h +++ b/libs/hwui/hwui/Bitmap.h @@ -27,6 +27,8 @@ #include #endif +class SkWStream; + namespace android { enum class PixelStorageType { @@ -142,6 +144,26 @@ public: // and places that value in size. static bool computeAllocationSize(size_t rowBytes, int height, size_t* size); + // These must match the int values of CompressFormat in Bitmap.java, as well as + // AndroidBitmapCompressFormat. + enum class JavaCompressFormat { + Jpeg = 0, + Png = 1, + Webp = 2, + WebpLossy = 3, + WebpLossless = 4, + }; + + enum class CompressResult { + Success, + AllocationFailed, + Error, + }; + + CompressResult compress(JavaCompressFormat format, int32_t quality, SkWStream* stream); + + static CompressResult compress(const SkBitmap& bitmap, JavaCompressFormat format, + int32_t quality, SkWStream* stream); private: static sk_sp allocateAshmemBitmap(size_t size, const SkImageInfo& i, size_t rowBytes); static sk_sp allocateHeapBitmap(size_t size, const SkImageInfo& i, size_t rowBytes); -- cgit v1.2.3 From 283a9289e91b4f481868045e1de16230e8aa2ecd Mon Sep 17 00:00:00 2001 From: Stan Iliev Date: Fri, 17 Jan 2020 11:46:21 -0500 Subject: Rename HWUI ATRACE memory counters Rename HWUI ATRACE counters from "Graphics CPU Memory", "Graphics Texture Memory" and "GPU Memory" to "HWUI CPU Memory", "HWUI Texture Memory" and "HWUI GPU Memory". New names apply for purgeable memory, but with "Purgeable " prefix. Add a new counter "HWUI All Memory", which is a sum of the other 3. New counter simplifies perfetto query needed to detect a memory regression. Bug: 147833967 Test: Ran systrace Change-Id: I39e0c6e1dbd21b67f457cc58f3fa94eb24d64d56 --- libs/hwui/pipeline/skia/ATraceMemoryDump.cpp | 41 +++++++++++++++------------- libs/hwui/pipeline/skia/ATraceMemoryDump.h | 4 +-- 2 files changed, 24 insertions(+), 21 deletions(-) (limited to 'libs') diff --git a/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp b/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp index 2c78b024536b..551bdc63121d 100644 --- a/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp +++ b/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp @@ -24,8 +24,8 @@ namespace android { namespace uirenderer { namespace skiapipeline { -// When purgeable is INVALID_TIME it won't be logged at all. -#define INVALID_TIME -1 +// When purgeable is INVALID_MEMORY_SIZE it won't be logged at all. +#define INVALID_MEMORY_SIZE -1 /** * Skia invokes the following SkTraceMemoryDump functions: @@ -42,10 +42,10 @@ namespace skiapipeline { * "GPU Memory" category. */ static std::unordered_map sResourceMap = { - {"malloc", "Graphics CPU Memory"}, // taken from setMemoryBacking(backingType) - {"gl_texture", "Graphics Texture Memory"}, // taken from setMemoryBacking(backingType) + {"malloc", "HWUI CPU Memory"}, // taken from setMemoryBacking(backingType) + {"gl_texture", "HWUI Texture Memory"}, // taken from setMemoryBacking(backingType) {"Texture", - "Graphics Texture Memory"}, // taken from dumpStringValue(value, valueName="type") + "HWUI Texture Memory"}, // taken from dumpStringValue(value, valueName="type") // Uncomment categories below to split "GPU Memory" into more brackets for debugging. /*{"vk_buffer", "vk_buffer"}, {"gl_renderbuffer", "gl_renderbuffer"}, @@ -105,10 +105,10 @@ void ATraceMemoryDump::startFrame() { // Once a category is observed in at least one frame, it is always reported in subsequent // frames (even if it is 0). Not logging a category to ATRACE would mean its value has not // changed since the previous frame, which is not what we want. - it.second.time = 0; - // If purgeableTime is INVALID_TIME, then logTraces won't log it at all. - if (it.second.purgeableTime != INVALID_TIME) { - it.second.purgeableTime = 0; + it.second.memory = 0; + // If purgeableMemory is INVALID_MEMORY_SIZE, then logTraces won't log it at all. + if (it.second.purgeableMemory != INVALID_MEMORY_SIZE) { + it.second.purgeableMemory = 0; } } } @@ -119,12 +119,15 @@ void ATraceMemoryDump::startFrame() { void ATraceMemoryDump::logTraces() { // Accumulate data from last dumpName recordAndResetCountersIfNeeded(""); + uint64_t hwui_all_frame_memory = 0; for (auto& it : mCurrentValues) { - ATRACE_INT64(it.first.c_str(), it.second.time); - if (it.second.purgeableTime != INVALID_TIME) { - ATRACE_INT64((std::string("Purgeable ") + it.first).c_str(), it.second.purgeableTime); + hwui_all_frame_memory += it.second.memory; + ATRACE_INT64(it.first.c_str(), it.second.memory); + if (it.second.purgeableMemory != INVALID_MEMORY_SIZE) { + ATRACE_INT64((std::string("Purgeable ") + it.first).c_str(), it.second.purgeableMemory); } } + ATRACE_INT64("HWUI All Memory", hwui_all_frame_memory); } /** @@ -145,12 +148,12 @@ void ATraceMemoryDump::recordAndResetCountersIfNeeded(const char* dumpName) { // A new dumpName observed -> store the data already collected. auto memoryCounter = mCurrentValues.find(mCategory); if (memoryCounter != mCurrentValues.end()) { - memoryCounter->second.time += mLastDumpValue; - if (mLastPurgeableDumpValue != INVALID_TIME) { - if (memoryCounter->second.purgeableTime == INVALID_TIME) { - memoryCounter->second.purgeableTime = mLastPurgeableDumpValue; + memoryCounter->second.memory += mLastDumpValue; + if (mLastPurgeableDumpValue != INVALID_MEMORY_SIZE) { + if (memoryCounter->second.purgeableMemory == INVALID_MEMORY_SIZE) { + memoryCounter->second.purgeableMemory = mLastPurgeableDumpValue; } else { - memoryCounter->second.purgeableTime += mLastPurgeableDumpValue; + memoryCounter->second.purgeableMemory += mLastPurgeableDumpValue; } } } else { @@ -164,10 +167,10 @@ void ATraceMemoryDump::recordAndResetCountersIfNeeded(const char* dumpName) { void ATraceMemoryDump::resetCurrentCounter(const char* dumpName) { mLastDumpValue = 0; - mLastPurgeableDumpValue = INVALID_TIME; + mLastPurgeableDumpValue = INVALID_MEMORY_SIZE; mLastDumpName = dumpName; // Categories not listed in sResourceMap are reported as "GPU memory" - mCategory = "GPU Memory"; + mCategory = "HWUI GPU Memory"; } } /* namespace skiapipeline */ diff --git a/libs/hwui/pipeline/skia/ATraceMemoryDump.h b/libs/hwui/pipeline/skia/ATraceMemoryDump.h index aa5c401ad1ca..4592711dd5b5 100644 --- a/libs/hwui/pipeline/skia/ATraceMemoryDump.h +++ b/libs/hwui/pipeline/skia/ATraceMemoryDump.h @@ -62,8 +62,8 @@ private: std::string mCategory; struct TraceValue { - uint64_t time; - uint64_t purgeableTime; + uint64_t memory; + uint64_t purgeableMemory; }; // keys are define in sResourceMap -- cgit v1.2.3 From 5af5d3077f921994b90e9bad89d393421f3cc160 Mon Sep 17 00:00:00 2001 From: Stan Iliev Date: Mon, 13 Jan 2020 11:29:18 -0500 Subject: Refactor TextureLayer JNI to use only APEX APIs Move ASurfaceTexture JNI implementation from libandroid to libnativedisplay. TextureLayer uses ASurfaceTexture_fromSurfaceTexture from libnativedisplay instead of libandroid_runtime code. libgui is no longer leaked through surface_texture_platform.h. DeferredLayerUpdater uses ASurfaceTexture_release instead of private ASurfaceTexture dtor. Test: pass CtsUiRenderingTestCases and CtsViewTestCases Bug: 147060713 Exempt-From-Owner-Approval: Changed only a header path in android_hardware_camera2_legacy_LegacyCameraDevice.cpp Change-Id: I9720d9c383f8120f9db116fd2b74fc241af84396 --- libs/hwui/DeferredLayerUpdater.cpp | 3 +++ libs/hwui/DeferredLayerUpdater.h | 5 ++--- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'libs') diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp index d6b516fd5cf4..5a50245a3765 100644 --- a/libs/hwui/DeferredLayerUpdater.cpp +++ b/libs/hwui/DeferredLayerUpdater.cpp @@ -18,6 +18,8 @@ #include #include +// TODO: Use public SurfaceTexture APIs once available and include public NDK header file instead. +#include #include "AutoBackendTextureRelease.h" #include "Matrix.h" #include "Properties.h" @@ -34,6 +36,7 @@ namespace uirenderer { DeferredLayerUpdater::DeferredLayerUpdater(RenderState& renderState) : mRenderState(renderState) , mBlend(false) + , mSurfaceTexture(nullptr, [](ASurfaceTexture*) {}) , mTransform(nullptr) , mGLContextAttached(false) , mUpdateTexImage(false) diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h index 289f65c22ad3..c44c0d537fa7 100644 --- a/libs/hwui/DeferredLayerUpdater.h +++ b/libs/hwui/DeferredLayerUpdater.h @@ -21,8 +21,7 @@ #include #include #include -// TODO: Use public SurfaceTexture APIs once available and include public NDK header file instead. -#include +#include #include #include @@ -37,7 +36,7 @@ namespace uirenderer { class AutoBackendTextureRelease; class RenderState; -typedef std::unique_ptr AutoTextureRelease; +typedef std::unique_ptr AutoTextureRelease; // Container to hold the properties a layer should be set to at the start // of a render pass -- cgit v1.2.3 From 89a5dc514e69bc5db205910f0106418a0a558c85 Mon Sep 17 00:00:00 2001 From: Nathaniel Nifong Date: Fri, 17 Jan 2020 15:32:17 -0500 Subject: Do not clip draw commands during recording of SKP files Test: Confirmed drawing is unaffected on pixel 3, confirmed all commands are recorded. Change-Id: Ib543730c321f5082d9a2331241873f9b2e8289c8 Bug: skia:9758 --- libs/hwui/pipeline/skia/SkiaPipeline.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'libs') diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 35a885f46919..b940cff04713 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -458,9 +458,15 @@ void SkiaPipeline::renderFrameImpl(const SkRect& clip, const SkMatrix& preTransform) { SkAutoCanvasRestore saver(canvas, true); auto clipRestriction = preTransform.mapRect(clip).roundOut(); - canvas->androidFramework_setDeviceClipRestriction(clipRestriction); - canvas->drawAnnotation(SkRect::Make(clipRestriction), "AndroidDeviceClipRestriction", - nullptr); + if (CC_UNLIKELY(mCaptureMode == CaptureMode::SingleFrameSKP + || mCaptureMode == CaptureMode::MultiFrameSKP)) { + canvas->drawAnnotation(SkRect::Make(clipRestriction), "AndroidDeviceClipRestriction", + nullptr); + } else { + // clip drawing to dirty region only when not recording SKP files (which should contain all + // draw ops on every frame) + canvas->androidFramework_setDeviceClipRestriction(clipRestriction); + } canvas->concat(preTransform); // STOPSHIP: Revert, temporary workaround to clear always F16 frame buffer for b/74976293 -- cgit v1.2.3 From d256790d13c66f902db22d9661234be4bd160ed5 Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Thu, 16 Jan 2020 15:01:52 -0500 Subject: Move ABitmap_getDataSpace into hwui to share with AImageDecoder Bug: 135133301 Test: android.graphics.cts.BitmapTest#testNdkDataSpace Change-Id: Ib54523a16a3ac9149a6b072489d97a39f8cc1564 --- libs/hwui/utils/Color.cpp | 89 +++++++++++++++++++++++++++++++++++++++++++++-- libs/hwui/utils/Color.h | 16 +++++++++ 2 files changed, 102 insertions(+), 3 deletions(-) (limited to 'libs') diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp index c445885c5c63..71a27ced2e09 100644 --- a/libs/hwui/utils/Color.cpp +++ b/libs/hwui/utils/Color.cpp @@ -108,7 +108,9 @@ SkColorType PixelFormatToColorType(android::PixelFormat format) { } } -// FIXME: Share with the version in android_bitmap.cpp? +namespace { +static constexpr skcms_TransferFunction k2Dot6 = {2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}; + // Skia's SkNamedGamut::kDCIP3 is based on a white point of D65. This gamut // matches the white point used by ColorSpace.Named.DCIP3. static constexpr skcms_Matrix3x3 kDCIP3 = {{ @@ -117,6 +119,87 @@ static constexpr skcms_Matrix3x3 kDCIP3 = {{ {0.000800549, 0.0432385, 0.78275}, }}; +static bool nearlyEqual(float a, float b) { + // By trial and error, this is close enough to match for the ADataSpaces we + // compare for. + return ::fabs(a - b) < .002f; +} + +static bool nearlyEqual(const skcms_TransferFunction& x, const skcms_TransferFunction& y) { + return nearlyEqual(x.g, y.g) + && nearlyEqual(x.a, y.a) + && nearlyEqual(x.b, y.b) + && nearlyEqual(x.c, y.c) + && nearlyEqual(x.d, y.d) + && nearlyEqual(x.e, y.e) + && nearlyEqual(x.f, y.f); +} + +static bool nearlyEqual(const skcms_Matrix3x3& x, const skcms_Matrix3x3& y) { + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + if (!nearlyEqual(x.vals[i][j], y.vals[i][j])) return false; + } + } + return true; +} + +} // anonymous namespace + +android_dataspace ColorSpaceToADataSpace(SkColorSpace* colorSpace, SkColorType colorType) { + if (!colorSpace) { + return HAL_DATASPACE_UNKNOWN; + } + + if (colorSpace->isSRGB()) { + if (colorType == kRGBA_F16_SkColorType) { + return HAL_DATASPACE_V0_SCRGB; + } + return HAL_DATASPACE_V0_SRGB; + } + + skcms_TransferFunction fn; + LOG_ALWAYS_FATAL_IF(!colorSpace->isNumericalTransferFn(&fn)); + + skcms_Matrix3x3 gamut; + LOG_ALWAYS_FATAL_IF(!colorSpace->toXYZD50(&gamut)); + + if (nearlyEqual(gamut, SkNamedGamut::kSRGB)) { + if (nearlyEqual(fn, SkNamedTransferFn::kLinear)) { + // Skia doesn't differentiate amongst the RANGES. In Java, we associate + // LINEAR_EXTENDED_SRGB with F16, and LINEAR_SRGB with other Configs. + // Make the same association here. + if (colorType == kRGBA_F16_SkColorType) { + return HAL_DATASPACE_V0_SCRGB_LINEAR; + } + return HAL_DATASPACE_V0_SRGB_LINEAR; + } + + if (nearlyEqual(fn, SkNamedTransferFn::kRec2020)) { + return HAL_DATASPACE_V0_BT709; + } + } + + if (nearlyEqual(fn, SkNamedTransferFn::kSRGB) && nearlyEqual(gamut, SkNamedGamut::kDCIP3)) { + return HAL_DATASPACE_DISPLAY_P3; + } + + if (nearlyEqual(fn, SkNamedTransferFn::k2Dot2) && nearlyEqual(gamut, SkNamedGamut::kAdobeRGB)) { + return HAL_DATASPACE_ADOBE_RGB; + } + + if (nearlyEqual(fn, SkNamedTransferFn::kRec2020) && + nearlyEqual(gamut, SkNamedGamut::kRec2020)) { + return HAL_DATASPACE_BT2020; + } + + if (nearlyEqual(fn, k2Dot6) && nearlyEqual(gamut, kDCIP3)) { + return HAL_DATASPACE_DCI_P3; + } + + return HAL_DATASPACE_UNKNOWN; +} + sk_sp DataSpaceToColorSpace(android_dataspace dataspace) { if (dataspace == HAL_DATASPACE_UNKNOWN) { return SkColorSpace::MakeSRGB(); @@ -126,7 +209,7 @@ sk_sp DataSpaceToColorSpace(android_dataspace dataspace) { // needs to use the locally-defined kDCIP3 gamut, rather than the one in // Skia (SkNamedGamut), which is used for other data spaces with // HAL_DATASPACE_STANDARD_DCI_P3 (e.g. HAL_DATASPACE_DISPLAY_P3). - return SkColorSpace::MakeRGB({2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, kDCIP3); + return SkColorSpace::MakeRGB(k2Dot6, kDCIP3); } skcms_Matrix3x3 gamut; @@ -165,7 +248,7 @@ sk_sp DataSpaceToColorSpace(android_dataspace dataspace) { case HAL_DATASPACE_TRANSFER_GAMMA2_2: return SkColorSpace::MakeRGB({2.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut); case HAL_DATASPACE_TRANSFER_GAMMA2_6: - return SkColorSpace::MakeRGB({2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut); + return SkColorSpace::MakeRGB(k2Dot6, gamut); case HAL_DATASPACE_TRANSFER_GAMMA2_8: return SkColorSpace::MakeRGB({2.8f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut); case HAL_DATASPACE_TRANSFER_ST2084: diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h index 07b5ec8fe0f0..a76f7e499c37 100644 --- a/libs/hwui/utils/Color.h +++ b/libs/hwui/utils/Color.h @@ -105,6 +105,22 @@ ANDROID_API SkColorType PixelFormatToColorType(android::PixelFormat format); ANDROID_API sk_sp DataSpaceToColorSpace(android_dataspace dataspace); +/** + * Return the android_dataspace corresponding to colorSpace. + * + * Note: This currently only returns android_dataspaces with corresponding + * ADataSpaces. The NDK relies on this, so if you need to update it to return + * an android_dataspace *without* an ADataSpace, the NDK methods need to be + * updated. + * + * @param colorSpace May be null, in which case this will return + * HAL_DATASPACE_UNKNOWN. + * @param colorType Some SkColorSpaces are associated with more than one + * android_dataspace. In that case, the SkColorType is used to + * determine which one to return. + */ +ANDROID_API android_dataspace ColorSpaceToADataSpace(SkColorSpace*, SkColorType); + struct Lab { float L; float a; -- cgit v1.2.3 From e5ace3f9cb9fdf81d34fe5d37e6b3cc148ae2427 Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Wed, 15 Jan 2020 15:31:40 -0500 Subject: Implement AImageDecoder dataspace methods Bug: 135133301 Test: Iffe659e50078139188c3325545624640ae177cc2 Implement AImageDecoderHeaderInfo_getDataSpace, which reports the default ADataSpace to decode to. It may report ADATASPACE_UNKNOWN, which means that we've mostly left the colors in their original color profile. This matches android.graphics.ImageDecoder/BitmapFactory, which would use a ColorSpace named "Unknown". (It will standardize on DISPLAY_P3 for some profiles, which again matches the Java classes.) Implement AImageDecoder_setDataSpace, which allows specifying the ADataSpace to decode to. It only supports explicit ADataSpaces. Change-Id: Iba2f9e09531c23fae83ebe13cb9d18394ee3cd59 --- libs/hwui/hwui/ImageDecoder.cpp | 13 +++++++++---- libs/hwui/hwui/ImageDecoder.h | 1 + 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'libs') diff --git a/libs/hwui/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp index a6c4e9db7280..4b2857f6c290 100644 --- a/libs/hwui/hwui/ImageDecoder.cpp +++ b/libs/hwui/hwui/ImageDecoder.cpp @@ -31,7 +31,7 @@ ImageDecoder::ImageDecoder(std::unique_ptr codec, sk_spcomputeOutputColorType(kN32_SkColorType)) , mUnpremultipliedRequired(false) - , mOutColorSpace(mCodec->getInfo().refColorSpace()) + , mOutColorSpace(mCodec->computeOutputColorSpace(mOutColorType, nullptr)) , mSampleSize(1) { } @@ -111,7 +111,6 @@ bool ImageDecoder::setOutColorType(SkColorType colorType) { if (!gray()) { return false; } - mOutColorSpace = nullptr; break; case kN32_SkColorType: break; @@ -137,9 +136,15 @@ void ImageDecoder::setOutColorSpace(sk_sp colorSpace) { mOutColorSpace = std::move(colorSpace); } +sk_sp ImageDecoder::getOutputColorSpace() const { + // kGray_8 is used for ALPHA_8, which ignores the color space. + return mOutColorType == kGray_8_SkColorType ? nullptr : mOutColorSpace; +} + + SkImageInfo ImageDecoder::getOutputInfo() const { SkISize size = mCropRect ? mCropRect->size() : mTargetSize; - return SkImageInfo::Make(size, mOutColorType, getOutAlphaType(), mOutColorSpace); + return SkImageInfo::Make(size, mOutColorType, getOutAlphaType(), getOutputColorSpace()); } bool ImageDecoder::opaque() const { @@ -154,7 +159,7 @@ SkCodec::Result ImageDecoder::decode(void* pixels, size_t rowBytes) { void* decodePixels = pixels; size_t decodeRowBytes = rowBytes; auto decodeInfo = SkImageInfo::Make(mDecodeSize, mOutColorType, getOutAlphaType(), - mOutColorSpace); + getOutputColorSpace()); // Used if we need a temporary before scaling or subsetting. // FIXME: Use scanline decoding on only a couple lines to save memory. b/70709380. SkBitmap tmp; diff --git a/libs/hwui/hwui/ImageDecoder.h b/libs/hwui/hwui/ImageDecoder.h index 96f97e5421f3..0c99f84cbb72 100644 --- a/libs/hwui/hwui/ImageDecoder.h +++ b/libs/hwui/hwui/ImageDecoder.h @@ -66,6 +66,7 @@ private: ImageDecoder& operator=(const ImageDecoder&) = delete; SkAlphaType getOutAlphaType() const; + sk_sp getOutputColorSpace() const; }; } // namespace android -- cgit v1.2.3 From db4f5466b925a846b6299277b0bfacc1891f6af7 Mon Sep 17 00:00:00 2001 From: Andrii Kulian Date: Tue, 21 Jan 2020 11:47:40 -0800 Subject: Move WindowManager Shell from packages/ to libs/ Test: Build Change-Id: I03833b0aff5836cc00b8371907f9059aa1acc5b1 --- libs/WindowManager/OWNERS | 3 ++ libs/WindowManager/Shell/Android.bp | 29 ++++++++++++++ libs/WindowManager/Shell/AndroidManifest.xml | 20 ++++++++++ libs/WindowManager/Shell/OWNERS | 4 ++ libs/WindowManager/Shell/res/values/config.xml | 21 ++++++++++ .../com/android/wm/shell/WindowManagerShell.java | 23 +++++++++++ libs/WindowManager/Shell/tests/Android.bp | 45 ++++++++++++++++++++++ libs/WindowManager/Shell/tests/AndroidManifest.xml | 32 +++++++++++++++ libs/WindowManager/Shell/tests/AndroidTest.xml | 31 +++++++++++++++ .../Shell/tests/res/values/config.xml | 21 ++++++++++ .../wm/shell/tests/WindowManagerShellTest.java | 40 +++++++++++++++++++ 11 files changed, 269 insertions(+) create mode 100644 libs/WindowManager/OWNERS create mode 100644 libs/WindowManager/Shell/Android.bp create mode 100644 libs/WindowManager/Shell/AndroidManifest.xml create mode 100644 libs/WindowManager/Shell/OWNERS create mode 100644 libs/WindowManager/Shell/res/values/config.xml create mode 100644 libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShell.java create mode 100644 libs/WindowManager/Shell/tests/Android.bp create mode 100644 libs/WindowManager/Shell/tests/AndroidManifest.xml create mode 100644 libs/WindowManager/Shell/tests/AndroidTest.xml create mode 100644 libs/WindowManager/Shell/tests/res/values/config.xml create mode 100644 libs/WindowManager/Shell/tests/src/com/android/wm/shell/tests/WindowManagerShellTest.java (limited to 'libs') diff --git a/libs/WindowManager/OWNERS b/libs/WindowManager/OWNERS new file mode 100644 index 000000000000..063d4594f2fa --- /dev/null +++ b/libs/WindowManager/OWNERS @@ -0,0 +1,3 @@ +set noparent + +include ../../services/core/java/com/android/server/wm/OWNERS \ No newline at end of file diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp new file mode 100644 index 000000000000..b8934dc8c583 --- /dev/null +++ b/libs/WindowManager/Shell/Android.bp @@ -0,0 +1,29 @@ +// Copyright (C) 2019 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. + +android_library { + name: "WindowManager-Shell", + srcs: [ + "src/**/*.java", + "src/**/I*.aidl", + ], + resource_dirs: [ + "res", + ], + manifest: "AndroidManifest.xml", + + platform_apis: true, + sdk_version: "current", + min_sdk_version: "system_current", +} diff --git a/libs/WindowManager/Shell/AndroidManifest.xml b/libs/WindowManager/Shell/AndroidManifest.xml new file mode 100644 index 000000000000..ea8a5c305029 --- /dev/null +++ b/libs/WindowManager/Shell/AndroidManifest.xml @@ -0,0 +1,20 @@ + + + + + diff --git a/libs/WindowManager/Shell/OWNERS b/libs/WindowManager/Shell/OWNERS new file mode 100644 index 000000000000..4390004f5f93 --- /dev/null +++ b/libs/WindowManager/Shell/OWNERS @@ -0,0 +1,4 @@ +# sysui owners +hwwang@google.com +mrenouf@google.com +winsonc@google.com \ No newline at end of file diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml new file mode 100644 index 000000000000..c894eb0133b5 --- /dev/null +++ b/libs/WindowManager/Shell/res/values/config.xml @@ -0,0 +1,21 @@ + + + + + \ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShell.java b/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShell.java new file mode 100644 index 000000000000..273bd27a221b --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShell.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2019 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. + */ + +package com.android.wm.shell; + +/** + * Interface for the shell. + */ +public class WindowManagerShell { +} diff --git a/libs/WindowManager/Shell/tests/Android.bp b/libs/WindowManager/Shell/tests/Android.bp new file mode 100644 index 000000000000..78fa45ebdf94 --- /dev/null +++ b/libs/WindowManager/Shell/tests/Android.bp @@ -0,0 +1,45 @@ +// Copyright (C) 2019 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. + +android_test { + name: "WindowManagerShellTests", + + srcs: ["**/*.java"], + + static_libs: [ + "WindowManager-Shell", + "junit", + "androidx.test.runner", + "androidx.test.rules", + "androidx.test.ext.junit", + "mockito-target-extended-minus-junit4", + "truth-prebuilt", + ], + libs: [ + "android.test.mock", + "android.test.base", + "android.test.runner", + ], + jni_libs: [ + "libdexmakerjvmtiagent", + "libstaticjvmtiagent", + ], + + sdk_version: "current", + platform_apis: true, + + optimize: { + enabled: false, + }, +} diff --git a/libs/WindowManager/Shell/tests/AndroidManifest.xml b/libs/WindowManager/Shell/tests/AndroidManifest.xml new file mode 100644 index 000000000000..a8f795ec8a8d --- /dev/null +++ b/libs/WindowManager/Shell/tests/AndroidManifest.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + diff --git a/libs/WindowManager/Shell/tests/AndroidTest.xml b/libs/WindowManager/Shell/tests/AndroidTest.xml new file mode 100644 index 000000000000..4dce4db360e4 --- /dev/null +++ b/libs/WindowManager/Shell/tests/AndroidTest.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + diff --git a/libs/WindowManager/Shell/tests/res/values/config.xml b/libs/WindowManager/Shell/tests/res/values/config.xml new file mode 100644 index 000000000000..c894eb0133b5 --- /dev/null +++ b/libs/WindowManager/Shell/tests/res/values/config.xml @@ -0,0 +1,21 @@ + + + + + \ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/src/com/android/wm/shell/tests/WindowManagerShellTest.java b/libs/WindowManager/Shell/tests/src/com/android/wm/shell/tests/WindowManagerShellTest.java new file mode 100644 index 000000000000..376875b143a1 --- /dev/null +++ b/libs/WindowManager/Shell/tests/src/com/android/wm/shell/tests/WindowManagerShellTest.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2019 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. + */ + +package com.android.wm.shell.tests; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import com.android.wm.shell.WindowManagerShell; + +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Tests for the shell. + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class WindowManagerShellTest { + + WindowManagerShell mShell; + + @Test + public void testNothing() { + // Do nothing + } +} -- cgit v1.2.3 From a4973d84360f76eba71222916756c74ec3840072 Mon Sep 17 00:00:00 2001 From: Mike Reed Date: Wed, 22 Jan 2020 10:10:37 -0500 Subject: extend recordingcanvas overrides for mat4 Note: SkCanvas now tracks a real 4x4, so android could consider using it Test: make Change-Id: I6e201f2feec4fb4663f5d3e62fe0ffef5ef9a33f --- libs/hwui/DisplayListOps.in | 48 +++++++++++----------- libs/hwui/RecordingCanvas.cpp | 24 +++++++++++ libs/hwui/RecordingCanvas.h | 4 ++ libs/hwui/pipeline/skia/GLFunctorDrawable.cpp | 9 ++-- libs/hwui/pipeline/skia/VkFunctorDrawable.cpp | 5 ++- .../pipeline/skia/VkInteropFunctorDrawable.cpp | 4 +- 6 files changed, 63 insertions(+), 31 deletions(-) (limited to 'libs') diff --git a/libs/hwui/DisplayListOps.in b/libs/hwui/DisplayListOps.in index 4a252afc1df3..49817925d9b4 100644 --- a/libs/hwui/DisplayListOps.in +++ b/libs/hwui/DisplayListOps.in @@ -14,39 +14,41 @@ * limitations under the License. */ -X(Flush) -X(Save) -X(Restore) +X(Flush) +X(Save) +X(Restore) X(SaveLayer) X(SaveBehind) -X(Concat) -X(SetMatrix) +X(Concat44) +X(Concat) +X(SetMatrix) +X(Scale) X(Translate) -X(ClipPath) -X(ClipRect) -X(ClipRRect) +X(ClipPath) +X(ClipRect) +X(ClipRRect) X(ClipRegion) X(DrawPaint) X(DrawBehind) -X(DrawPath) -X(DrawRect) -X(DrawRegion) -X(DrawOval) +X(DrawPath) +X(DrawRect) +X(DrawRegion) +X(DrawOval) X(DrawArc) -X(DrawRRect) -X(DrawDRRect) -X(DrawAnnotation) -X(DrawDrawable) +X(DrawRRect) +X(DrawDRRect) +X(DrawAnnotation) +X(DrawDrawable) X(DrawPicture) -X(DrawImage) -X(DrawImageNine) -X(DrawImageRect) +X(DrawImage) +X(DrawImageNine) +X(DrawImageRect) X(DrawImageLattice) X(DrawTextBlob) -X(DrawPatch) -X(DrawPoints) -X(DrawVertices) -X(DrawAtlas) +X(DrawPatch) +X(DrawPoints) +X(DrawVertices) +X(DrawAtlas) X(DrawShadowRec) X(DrawVectorDrawable) X(DrawWebView) diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index c0df2faf120a..dc467c41baed 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -130,6 +130,12 @@ struct SaveBehind final : Op { } }; +struct Concat44 final : Op { + static const auto kType = Type::Concat44; + Concat44(const SkScalar m[16]) { memcpy(colMajor, m, sizeof(colMajor)); } + SkScalar colMajor[16]; + void draw(SkCanvas* c, const SkMatrix&) const { c->experimental_concat44(colMajor); } +}; struct Concat final : Op { static const auto kType = Type::Concat; Concat(const SkMatrix& matrix) : matrix(matrix) {} @@ -144,6 +150,12 @@ struct SetMatrix final : Op { c->setMatrix(SkMatrix::Concat(original, matrix)); } }; +struct Scale final : Op { + static const auto kType = Type::Scale; + Scale(SkScalar sx, SkScalar sy) : sx(sx), sy(sy) {} + SkScalar sx, sy; + void draw(SkCanvas* c, const SkMatrix&) const { c->scale(sx, sy); } +}; struct Translate final : Op { static const auto kType = Type::Translate; Translate(SkScalar dx, SkScalar dy) : dx(dx), dy(dy) {} @@ -562,12 +574,18 @@ void DisplayListData::saveBehind(const SkRect* subset) { this->push(0, subset); } +void DisplayListData::concat44(const SkScalar colMajor[16]) { + this->push(0, colMajor); +} void DisplayListData::concat(const SkMatrix& matrix) { this->push(0, matrix); } void DisplayListData::setMatrix(const SkMatrix& matrix) { this->push(0, matrix); } +void DisplayListData::scale(SkScalar sx, SkScalar sy) { + this->push(0, sx, sy); +} void DisplayListData::translate(SkScalar dx, SkScalar dy) { this->push(0, dx, dy); } @@ -823,12 +841,18 @@ bool RecordingCanvas::onDoSaveBehind(const SkRect* subset) { return false; } +void RecordingCanvas::didConcat44(const SkScalar colMajor[16]) { + fDL->concat44(colMajor); +} void RecordingCanvas::didConcat(const SkMatrix& matrix) { fDL->concat(matrix); } void RecordingCanvas::didSetMatrix(const SkMatrix& matrix) { fDL->setMatrix(matrix); } +void RecordingCanvas::didScale(SkScalar sx, SkScalar sy) { + fDL->scale(sx, sy); +} void RecordingCanvas::didTranslate(SkScalar dx, SkScalar dy) { fDL->translate(dx, dy); } diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h index 322eff24dd34..7eb1ce3eb18a 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -82,8 +82,10 @@ private: void saveBehind(const SkRect*); void restore(); + void concat44(const SkScalar colMajor[16]); void concat(const SkMatrix&); void setMatrix(const SkMatrix&); + void scale(SkScalar, SkScalar); void translate(SkScalar, SkScalar); void translateZ(SkScalar); @@ -153,8 +155,10 @@ public: void onFlush() override; + void didConcat44(const SkScalar[16]) override; void didConcat(const SkMatrix&) override; void didSetMatrix(const SkMatrix&) override; + void didScale(SkScalar, SkScalar) override; void didTranslate(SkScalar, SkScalar) override; void onClipRect(const SkRect&, SkClipOp, ClipEdgeStyle) override; diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp index a1b2b18195bc..aa8849b642b1 100644 --- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp +++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp @@ -26,6 +26,7 @@ #include "SkAndroidFrameworkUtils.h" #include "SkClipStack.h" #include "SkRect.h" +#include "include/private/SkM44.h" namespace android { namespace uirenderer { @@ -92,7 +93,7 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) { SkIRect surfaceBounds = canvas->internal_private_getTopLayerBounds(); SkIRect clipBounds = canvas->getDeviceClipBounds(); - SkMatrix44 mat4(canvas->getTotalMatrix()); + SkM44 mat4(canvas->experimental_getLocalToDevice()); SkRegion clipRegion; canvas->temporary_internal_getRgnClip(&clipRegion); @@ -118,7 +119,7 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) { // update the matrix and clip that we pass to the WebView to match the coordinates of // the offscreen layer - mat4.preTranslate(-clipBounds.fLeft, -clipBounds.fTop, 0); + mat4.preTranslate(-clipBounds.fLeft, -clipBounds.fTop); clipBounds.offsetTo(0, 0); clipRegion.translate(-surfaceBounds.fLeft, -surfaceBounds.fTop); @@ -126,7 +127,7 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) { // we are drawing into a (clipped) offscreen layer so we must update the clip and matrix // from device coordinates to the layer's coordinates clipBounds.offset(-surfaceBounds.fLeft, -surfaceBounds.fTop); - mat4.preTranslate(-surfaceBounds.fLeft, -surfaceBounds.fTop, 0); + mat4.preTranslate(-surfaceBounds.fLeft, -surfaceBounds.fTop); } DrawGlInfo info; @@ -137,7 +138,7 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) { info.isLayer = fboID != 0; info.width = fboSize.width(); info.height = fboSize.height(); - mat4.asColMajorf(&info.transform[0]); + mat4.getColMajor(&info.transform[0]); info.color_space_ptr = canvas->imageInfo().colorSpace(); // ensure that the framebuffer that the webview will render into is bound before we clear diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp index 112792611fc3..68f111752a4c 100644 --- a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp +++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp @@ -20,6 +20,7 @@ #include #include #include +#include "include/private/SkM44.h" #include #include #include @@ -62,7 +63,7 @@ void VkFunctorDrawHandler::draw(const GrBackendDrawableInfo& info) { renderthread::RenderThread::getInstance().vulkanManager(); mFunctorHandle->initVk(vk_manager.getVkFunctorInitParams()); - SkMatrix44 mat4(mMatrix); + SkM44 mat4(mMatrix); VkFunctorDrawParams params{ .width = mImageInfo.width(), .height = mImageInfo.height(), @@ -72,7 +73,7 @@ void VkFunctorDrawHandler::draw(const GrBackendDrawableInfo& info) { .clip_right = mClip.fRight, .clip_bottom = mClip.fBottom, }; - mat4.asColMajorf(¶ms.transform[0]); + mat4.getColMajor(¶ms.transform[0]); params.secondary_command_buffer = vulkan_info.fSecondaryCommandBuffer; params.color_attachment_index = vulkan_info.fColorAttachmentIndex; params.compatible_render_pass = vulkan_info.fCompatibleRenderPass; diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp index 706325f00bd2..241d3708def5 100644 --- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp +++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp @@ -121,7 +121,7 @@ void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) { glBindTexture(GL_TEXTURE_2D, 0); DrawGlInfo info; - SkMatrix44 mat4(canvas->getTotalMatrix()); + SkM44 mat4(canvas->experimental_getLocalToDevice()); SkIRect clipBounds = canvas->getDeviceClipBounds(); info.clipLeft = clipBounds.fLeft; @@ -131,7 +131,7 @@ void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) { info.isLayer = true; info.width = mFBInfo.width(); info.height = mFBInfo.height(); - mat4.asColMajorf(&info.transform[0]); + mat4.getColMajor(&info.transform[0]); info.color_space_ptr = canvas->imageInfo().colorSpace(); glViewport(0, 0, info.width, info.height); -- cgit v1.2.3 From 949c6006cbffee4afbe01a4e180299abafc7c4b9 Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Thu, 23 Jan 2020 16:20:39 -0500 Subject: Make bitmap compression retain the ColorSpace Bug: 147877556 Test: I72b5f28903a67445bc205cdad8848384ad675b31 In Bitmap#compress and AndroidBitmap_compress, remove the conversion from F16 to 8888 P3. This was originally done because F16 forced a particular ColorSpace, and we wanted to use a wider gamut than SRGB. Now that F16 can be any RGB ColorSpace, encode it with the ColorSpace that it already has. Skip the conversion to 8888, which is unnecessary. The encoders can already convert from F16. Remove Bitmap::CompressResult. Now that compress does not allocate a Bitmap, there is no reason to return ALLOCATION_FAILED. Change-Id: I8190664398e762daf57092d919f382a56267dd07 --- libs/hwui/hwui/Bitmap.cpp | 34 +++++----------------------------- libs/hwui/hwui/Bitmap.h | 12 +++--------- 2 files changed, 8 insertions(+), 38 deletions(-) (limited to 'libs') diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index 3c402e996218..7c0efe16838e 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -471,36 +471,14 @@ BitmapPalette Bitmap::computePalette(const SkImageInfo& info, const void* addr, return BitmapPalette::Unknown; } -Bitmap::CompressResult Bitmap::compress(JavaCompressFormat format, int32_t quality, - SkWStream* stream) { +bool Bitmap::compress(JavaCompressFormat format, int32_t quality, SkWStream* stream) { SkBitmap skbitmap; getSkBitmap(&skbitmap); return compress(skbitmap, format, quality, stream); } -Bitmap::CompressResult Bitmap::compress(const SkBitmap& bitmap, JavaCompressFormat format, - int32_t quality, SkWStream* stream) { - SkBitmap skbitmap = bitmap; - if (skbitmap.colorType() == kRGBA_F16_SkColorType) { - // Convert to P3 before encoding. This matches - // SkAndroidCodec::computeOutputColorSpace for wide gamuts. Now that F16 - // could already be P3, we still want to convert to 8888. - auto cs = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3); - auto info = skbitmap.info().makeColorType(kRGBA_8888_SkColorType) - .makeColorSpace(std::move(cs)); - SkBitmap p3; - if (!p3.tryAllocPixels(info)) { - return CompressResult::AllocationFailed; - } - - SkPixmap pm; - SkAssertResult(p3.peekPixels(&pm)); // should always work if tryAllocPixels() did. - if (!skbitmap.readPixels(pm)) { - return CompressResult::Error; - } - skbitmap = p3; - } - +bool Bitmap::compress(const SkBitmap& bitmap, JavaCompressFormat format, + int32_t quality, SkWStream* stream) { SkEncodedImageFormat fm; switch (format) { case JavaCompressFormat::Jpeg: @@ -518,12 +496,10 @@ Bitmap::CompressResult Bitmap::compress(const SkBitmap& bitmap, JavaCompressForm options.fQuality = quality; options.fCompression = format == JavaCompressFormat::WebpLossy ? SkWebpEncoder::Compression::kLossy : SkWebpEncoder::Compression::kLossless; - return SkWebpEncoder::Encode(stream, skbitmap.pixmap(), options) - ? CompressResult::Success : CompressResult::Error; + return SkWebpEncoder::Encode(stream, bitmap.pixmap(), options); } } - return SkEncodeImage(stream, skbitmap, fm, quality) - ? CompressResult::Success : CompressResult::Error; + return SkEncodeImage(stream, bitmap, fm, quality); } } // namespace android diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h index ee365af2f7be..3bfb7800f735 100644 --- a/libs/hwui/hwui/Bitmap.h +++ b/libs/hwui/hwui/Bitmap.h @@ -154,16 +154,10 @@ public: WebpLossless = 4, }; - enum class CompressResult { - Success, - AllocationFailed, - Error, - }; - - CompressResult compress(JavaCompressFormat format, int32_t quality, SkWStream* stream); + bool compress(JavaCompressFormat format, int32_t quality, SkWStream* stream); - static CompressResult compress(const SkBitmap& bitmap, JavaCompressFormat format, - int32_t quality, SkWStream* stream); + static bool compress(const SkBitmap& bitmap, JavaCompressFormat format, + int32_t quality, SkWStream* stream); private: static sk_sp allocateAshmemBitmap(size_t size, const SkImageInfo& i, size_t rowBytes); static sk_sp allocateHeapBitmap(size_t size, const SkImageInfo& i, size_t rowBytes); -- cgit v1.2.3 From 4e65e38795a55fe437a7c38d28a0558a422ce2f5 Mon Sep 17 00:00:00 2001 From: Ruchir Rastogi Date: Wed, 22 Jan 2020 17:29:14 -0800 Subject: Delete StatsLogEventWrapper Since all pullers now use StatsEvent instead of StatsLogEventWrapper, this class can be deleted. Test: m -j Test: m -j statsd_test Bug: 146578681 Change-Id: I63013768ee13af0235f8cd857ca3fc16d2fd877f --- libs/services/Android.bp | 1 - .../include/android/os/StatsLogEventWrapper.h | 127 --------------------- libs/services/src/os/StatsLogEventWrapper.cpp | 127 --------------------- 3 files changed, 255 deletions(-) delete mode 100644 libs/services/include/android/os/StatsLogEventWrapper.h delete mode 100644 libs/services/src/os/StatsLogEventWrapper.cpp (limited to 'libs') diff --git a/libs/services/Android.bp b/libs/services/Android.bp index 901ffaa59cd1..9b047ca22d19 100644 --- a/libs/services/Android.bp +++ b/libs/services/Android.bp @@ -21,7 +21,6 @@ cc_library_shared { "src/content/ComponentName.cpp", "src/os/DropBoxManager.cpp", "src/os/StatsDimensionsValue.cpp", - "src/os/StatsLogEventWrapper.cpp", ], shared_libs: [ diff --git a/libs/services/include/android/os/StatsLogEventWrapper.h b/libs/services/include/android/os/StatsLogEventWrapper.h deleted file mode 100644 index 8de2ab49f42b..000000000000 --- a/libs/services/include/android/os/StatsLogEventWrapper.h +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (C) 2017 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 STATS_LOG_EVENT_WRAPPER_H -#define STATS_LOG_EVENT_WRAPPER_H - -#include -#include -#include -#include -#include - -namespace android { -namespace os { - -/** - * A wrapper for a union type to contain multiple types of values. - * - */ -struct StatsLogValue { - // Keep in sync with FieldValue.h - enum STATS_LOG_VALUE_TYPE { - UNKNOWN = 0, - INT = 1, - LONG = 2, - FLOAT = 3, - DOUBLE = 4, - STRING = 5, - STORAGE = 6 - }; - - StatsLogValue() : type(UNKNOWN) {} - - StatsLogValue(int32_t v) { - int_value = v; - type = INT; - } - - StatsLogValue(int64_t v) { - long_value = v; - type = LONG; - } - - StatsLogValue(float v) { - float_value = v; - type = FLOAT; - } - - StatsLogValue(double v) { - double_value = v; - type = DOUBLE; - } - - StatsLogValue(const std::string& v) { - str_value = v; - type = STRING; - } - - void setType(STATS_LOG_VALUE_TYPE t) { type = t; } - - union { - int32_t int_value; - int64_t long_value; - float float_value; - double double_value; - }; - std::string str_value; - std::vector storage_value; - - STATS_LOG_VALUE_TYPE type; -}; - -struct WorkChain { - std::vector uids; - std::vector tags; -}; - -// Represents a parcelable object. Only used to send data from Android OS to statsd. -class StatsLogEventWrapper : public android::Parcelable { - public: - StatsLogEventWrapper(); - - StatsLogEventWrapper(StatsLogEventWrapper&& in) = default; - - android::status_t writeToParcel(android::Parcel* out) const; - - android::status_t readFromParcel(const android::Parcel* in); - - int getTagId() const { return mTagId; } - - int64_t getElapsedRealTimeNs() const { return mElapsedRealTimeNs; } - - int64_t getWallClockTimeNs() const { return mWallClockTimeNs; } - - const std::vector& getElements() const { return mElements; } - - const std::vector& getWorkChains() const { return mWorkChains; } - - private: - int mTagId; - - int64_t mElapsedRealTimeNs; - - int64_t mWallClockTimeNs; - - std::vector mElements; - - std::vector mWorkChains; -}; -} // Namespace os -} // Namespace android - - -#endif // STATS_LOG_EVENT_WRAPPER_H - diff --git a/libs/services/src/os/StatsLogEventWrapper.cpp b/libs/services/src/os/StatsLogEventWrapper.cpp deleted file mode 100644 index f6dfdef16e19..000000000000 --- a/libs/services/src/os/StatsLogEventWrapper.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (C) 2017 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 - -#include -#include -#include -#include -#include - -using android::Parcel; -using android::Parcelable; -using android::status_t; -using std::vector; - -namespace android { -namespace os { - -StatsLogEventWrapper::StatsLogEventWrapper(){}; - -status_t StatsLogEventWrapper::writeToParcel(Parcel* out) const { - // Implement me if desired. We don't currently use this. - ALOGE( - "Cannot do c++ StatsLogEventWrapper.writeToParcel(); it is not " - "implemented."); - (void)out; // To prevent compile error of unused parameter 'in' - return UNKNOWN_ERROR; -}; - -status_t StatsLogEventWrapper::readFromParcel(const Parcel* in) { - status_t res = OK; - if (in == NULL) { - ALOGE("statsd received parcel argument was NULL."); - return BAD_VALUE; - } - if ((res = in->readInt32(&mTagId)) != OK) { - ALOGE("statsd could not read tagId from parcel"); - return res; - } - if ((res = in->readInt64(&mElapsedRealTimeNs)) != OK) { - ALOGE("statsd could not read elapsed real time from parcel"); - return res; - } - if ((res = in->readInt64(&mWallClockTimeNs)) != OK) { - ALOGE("statsd could not read wall clock time from parcel"); - return res; - } - int numWorkChain = 0; - if ((res = in->readInt32(&numWorkChain)) != OK) { - ALOGE("statsd could not read number of work chains from parcel"); - return res; - } - if (numWorkChain > 0) { - for (int i = 0; i < numWorkChain; i++) { - int numNodes = 0; - if ((res = in->readInt32(&numNodes)) != OK) { - ALOGE( - "statsd could not read number of nodes in work chain from parcel"); - return res; - } - if (numNodes == 0) { - ALOGE("empty work chain"); - return BAD_VALUE; - } - WorkChain wc; - for (int j = 0; j < numNodes; j++) { - wc.uids.push_back(in->readInt32()); - wc.tags.push_back(std::string(String8(in->readString16()).string())); - } - mWorkChains.push_back(wc); - } - } - int dataSize = 0; - if ((res = in->readInt32(&dataSize)) != OK) { - ALOGE("statsd could not read data size from parcel"); - return res; - } - if (mTagId <= 0 || mElapsedRealTimeNs <= 0 || mWallClockTimeNs <= 0 || - dataSize <= 0) { - ALOGE("statsd received invalid parcel"); - return BAD_VALUE; - } - - for (int i = 0; i < dataSize; i++) { - int type = in->readInt32(); - switch (type) { - case StatsLogValue::INT: - mElements.push_back(StatsLogValue(in->readInt32())); - break; - case StatsLogValue::LONG: - mElements.push_back(StatsLogValue(in->readInt64())); - break; - case StatsLogValue::STRING: - mElements.push_back( - StatsLogValue(std::string(String8(in->readString16()).string()))); - break; - case StatsLogValue::FLOAT: - mElements.push_back(StatsLogValue(in->readFloat())); - break; - case StatsLogValue::STORAGE: - mElements.push_back(StatsLogValue()); - mElements.back().setType(StatsLogValue::STORAGE); - in->readByteVector(&(mElements.back().storage_value)); - break; - default: - ALOGE("unrecognized data type: %d", type); - return BAD_TYPE; - } - } - return NO_ERROR; -}; - -} // Namespace os -} // Namespace android -- cgit v1.2.3 From 9f49a7cd7432950d0695ba4450b49a71a88ddc8e Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Wed, 29 Jan 2020 14:51:26 -0500 Subject: Force compressing ALPHA_8 to fail Bug: 146637821 Test: Iaf4d038379f46aa7e55384351d8098a32c4cd9ea This failed up through O, but "succeeded" for Bitmap.CompressFormat.PNG starting in P. The P behavior, though, uses a non-standard encoding. When re-decoding it, Android would treat it as an all black 8888 Bitmap with transparency, which is likely not what they wanted. Restore the pre-P behavior. Change-Id: I0f221fe66b6e318d2c9ed8fc199c2253f9f52497 --- libs/hwui/hwui/Bitmap.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'libs') diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index 7c0efe16838e..58cc08bc2d73 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -479,6 +479,14 @@ bool Bitmap::compress(JavaCompressFormat format, int32_t quality, SkWStream* str bool Bitmap::compress(const SkBitmap& bitmap, JavaCompressFormat format, int32_t quality, SkWStream* stream) { + if (bitmap.colorType() == kAlpha_8_SkColorType) { + // None of the JavaCompressFormats have a sensible way to compress an + // ALPHA_8 Bitmap. SkPngEncoder will compress one, but it uses a non- + // standard format that most decoders do not understand, so this is + // likely not useful. + return false; + } + SkEncodedImageFormat fm; switch (format) { case JavaCompressFormat::Jpeg: -- cgit v1.2.3 From 69b281d5f3ad61490bd8795795cefaa1f4674401 Mon Sep 17 00:00:00 2001 From: Dominik Laskowski Date: Fri, 22 Nov 2019 14:13:12 -0800 Subject: Adapt SurfaceControl to libgui API for display info Bug: 144601064 Test: dumpsys display Change-Id: I477e0e3126cc15eb5f120610b4e6b6b1568df389 --- libs/hwui/tests/common/TestContext.cpp | 74 +++++++++++++++----------- libs/hwui/tests/common/TestContext.h | 11 ++-- libs/hwui/tests/macrobench/TestSceneRunner.cpp | 8 ++- 3 files changed, 53 insertions(+), 40 deletions(-) (limited to 'libs') diff --git a/libs/hwui/tests/common/TestContext.cpp b/libs/hwui/tests/common/TestContext.cpp index e075d806126b..06f158f25fc5 100644 --- a/libs/hwui/tests/common/TestContext.cpp +++ b/libs/hwui/tests/common/TestContext.cpp @@ -22,43 +22,50 @@ namespace android { namespace uirenderer { namespace test { -static const int IDENT_DISPLAYEVENT = 1; - -static android::DisplayInfo DUMMY_DISPLAY{ - 1080, // w - 1920, // h - 320.0, // xdpi - 320.0, // ydpi - 60.0, // fps - 2.0, // density - ui::ROTATION_0, // orientation - false, // secure? - 0, // appVsyncOffset - 0, // presentationDeadline -}; - -DisplayInfo getInternalDisplay() { -#if !HWUI_NULL_GPU - DisplayInfo display; - const sp token = SurfaceComposerClient::getInternalDisplayToken(); - LOG_ALWAYS_FATAL_IF(token == nullptr, - "Failed to get display info because internal display is disconnected\n"); - status_t status = SurfaceComposerClient::getDisplayInfo(token, &display); - LOG_ALWAYS_FATAL_IF(status, "Failed to get display info\n"); - return display; +const DisplayInfo& getDisplayInfo() { + static DisplayInfo info = [] { + DisplayInfo info; +#if HWUI_NULL_GPU + info.density = 2.f; #else - return DUMMY_DISPLAY; + const sp token = SurfaceComposerClient::getInternalDisplayToken(); + LOG_ALWAYS_FATAL_IF(!token, "%s: No internal display", __FUNCTION__); + + const status_t status = SurfaceComposerClient::getDisplayInfo(token, &info); + LOG_ALWAYS_FATAL_IF(status, "%s: Failed to get display info", __FUNCTION__); #endif + return info; + }(); + + return info; } -// Initialize to a dummy default -android::DisplayInfo gDisplay = DUMMY_DISPLAY; +const DisplayConfig& getActiveDisplayConfig() { + static DisplayConfig config = [] { + DisplayConfig config; +#if HWUI_NULL_GPU + config.resolution = ui::Size(1080, 1920); + config.xDpi = config.yDpi = 320.f; + config.refreshRate = 60.f; +#else + const sp token = SurfaceComposerClient::getInternalDisplayToken(); + LOG_ALWAYS_FATAL_IF(!token, "%s: No internal display", __FUNCTION__); + + const status_t status = SurfaceComposerClient::getActiveDisplayConfig(token, &config); + LOG_ALWAYS_FATAL_IF(status, "%s: Failed to get active display config", __FUNCTION__); +#endif + return config; + }(); + + return config; +} TestContext::TestContext() { mLooper = new Looper(true); mSurfaceComposerClient = new SurfaceComposerClient(); - mLooper->addFd(mDisplayEventReceiver.getFd(), IDENT_DISPLAYEVENT, Looper::EVENT_INPUT, nullptr, - nullptr); + + constexpr int EVENT_ID = 1; + mLooper->addFd(mDisplayEventReceiver.getFd(), EVENT_ID, Looper::EVENT_INPUT, nullptr, nullptr); } TestContext::~TestContext() {} @@ -79,8 +86,10 @@ void TestContext::createSurface() { } void TestContext::createWindowSurface() { - mSurfaceControl = mSurfaceComposerClient->createSurface(String8("HwuiTest"), gDisplay.w, - gDisplay.h, PIXEL_FORMAT_RGBX_8888); + const ui::Size& resolution = getActiveDisplayResolution(); + mSurfaceControl = + mSurfaceComposerClient->createSurface(String8("HwuiTest"), resolution.getWidth(), + resolution.getHeight(), PIXEL_FORMAT_RGBX_8888); SurfaceComposerClient::Transaction t; t.setLayer(mSurfaceControl, 0x7FFFFFF).show(mSurfaceControl).apply(); @@ -94,7 +103,8 @@ void TestContext::createOffscreenSurface() { producer->setMaxDequeuedBufferCount(3); producer->setAsyncMode(true); mConsumer = new BufferItemConsumer(consumer, GRALLOC_USAGE_HW_COMPOSER, 4); - mConsumer->setDefaultBufferSize(gDisplay.w, gDisplay.h); + const ui::Size& resolution = getActiveDisplayResolution(); + mConsumer->setDefaultBufferSize(resolution.getWidth(), resolution.getHeight()); mSurface = new Surface(producer); } diff --git a/libs/hwui/tests/common/TestContext.h b/libs/hwui/tests/common/TestContext.h index 116d4de8090a..a012ecb1a1d3 100644 --- a/libs/hwui/tests/common/TestContext.h +++ b/libs/hwui/tests/common/TestContext.h @@ -23,20 +23,25 @@ #include #include #include +#include #include #include #include #include +#define dp(x) ((x) * android::uirenderer::test::getDisplayInfo().density) + namespace android { namespace uirenderer { namespace test { -extern DisplayInfo gDisplay; -#define dp(x) ((x)*android::uirenderer::test::gDisplay.density) +const DisplayInfo& getDisplayInfo(); +const DisplayConfig& getActiveDisplayConfig(); -DisplayInfo getInternalDisplay(); +inline const ui::Size& getActiveDisplayResolution() { + return getActiveDisplayConfig().resolution; +} class TestContext { public: diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp index 22d5abbd3dbc..3b6baa70db9a 100644 --- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp +++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp @@ -109,16 +109,14 @@ void outputBenchmarkReport(const TestScene::Info& info, const TestScene::Options void run(const TestScene::Info& info, const TestScene::Options& opts, benchmark::BenchmarkReporter* reporter) { - // Switch to the real display - gDisplay = getInternalDisplay(); - Properties::forceDrawFrame = true; TestContext testContext; testContext.setRenderOffscreen(opts.renderOffscreen); // create the native surface - const int width = gDisplay.w; - const int height = gDisplay.h; + const ui::Size& resolution = getActiveDisplayResolution(); + const int width = resolution.getWidth(); + const int height = resolution.getHeight(); sp surface = testContext.surface(); std::unique_ptr scene(info.createScene(opts)); -- cgit v1.2.3 From 429fff4bcc73be620e9eb8ee1269963fa3fd2dc8 Mon Sep 17 00:00:00 2001 From: Nathaniel Nifong Date: Thu, 30 Jan 2020 14:38:21 -0500 Subject: Provide a typeface proc during SKP serialization that forces typefaces to be written. Test: flashed to Pixel 3, confirmed system typeface is written, normal drawing unaffected. Change-Id: I0016224f628b4e564670cc05b982460ff001b6ac --- libs/hwui/pipeline/skia/SkiaPipeline.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'libs') diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 35a885f46919..82835d4154bc 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "LightingInfo.h" #include "VectorDrawable.h" @@ -264,6 +265,9 @@ bool SkiaPipeline::setupMultiFrameCapture() { SkSerialProcs procs; procs.fImageProc = SkSharingSerialContext::serializeImage; procs.fImageCtx = mSerialContext.get(); + procs.fTypefaceProc = [](SkTypeface* tf, void* ctx){ + return tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData); + }; // SkDocuments don't take owership of the streams they write. // we need to keep it until after mMultiPic.close() // procs is passed as a pointer, but just as a method of having an optional default. @@ -405,6 +409,10 @@ void SkiaPipeline::endCapture(SkSurface* surface) { std::invoke(mPictureCapturedCallback, std::move(picture)); } else { // single frame skp to file + SkSerialProcs procs; + procs.fTypefaceProc = [](SkTypeface* tf, void* ctx){ + return tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData); + }; auto data = picture->serialize(); savePictureAsync(data, mCapturedFile); mCaptureSequence = 0; -- cgit v1.2.3 From 2843c44b36f1595deebde7205e35738c17c7cda7 Mon Sep 17 00:00:00 2001 From: Yiwei Zhang Date: Mon, 27 Jan 2020 17:05:50 -0800 Subject: Rename HWUI memory atrace counters "HWUI GPU Memory" feels like total GPU memory used by HWUI, but it's actually the total of the memory categories not explictly listed. So this change renames it to "HWUI Misc Memory". Bug: 147833967 Test: take a systrace Change-Id: Id6b5f09981e148373dbc4e7f482e3da35740a566 --- libs/hwui/pipeline/skia/ATraceMemoryDump.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'libs') diff --git a/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp b/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp index 551bdc63121d..234f42d79cb7 100644 --- a/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp +++ b/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp @@ -39,14 +39,13 @@ namespace skiapipeline { * ATraceMemoryDump calculates memory category first by looking at the "type" string passed to * dumpStringValue and then by looking at "backingType" passed to setMemoryBacking. * Only GPU Texture memory is tracked separately and everything else is grouped as one - * "GPU Memory" category. + * "Misc Memory" category. */ static std::unordered_map sResourceMap = { {"malloc", "HWUI CPU Memory"}, // taken from setMemoryBacking(backingType) {"gl_texture", "HWUI Texture Memory"}, // taken from setMemoryBacking(backingType) - {"Texture", - "HWUI Texture Memory"}, // taken from dumpStringValue(value, valueName="type") - // Uncomment categories below to split "GPU Memory" into more brackets for debugging. + {"Texture", "HWUI Texture Memory"}, // taken from dumpStringValue(value, valueName="type") + // Uncomment categories below to split "Misc Memory" into more brackets for debugging. /*{"vk_buffer", "vk_buffer"}, {"gl_renderbuffer", "gl_renderbuffer"}, {"gl_buffer", "gl_buffer"}, @@ -169,8 +168,8 @@ void ATraceMemoryDump::resetCurrentCounter(const char* dumpName) { mLastDumpValue = 0; mLastPurgeableDumpValue = INVALID_MEMORY_SIZE; mLastDumpName = dumpName; - // Categories not listed in sResourceMap are reported as "GPU memory" - mCategory = "HWUI GPU Memory"; + // Categories not listed in sResourceMap are reported as "Misc Memory" + mCategory = "HWUI Misc Memory"; } } /* namespace skiapipeline */ -- cgit v1.2.3 From f023a323de7629a9ccd50685a55c70494f907c75 Mon Sep 17 00:00:00 2001 From: Alec Mouri Date: Mon, 25 Nov 2019 10:02:21 -0800 Subject: [HWUI] Use ANativeWindow inteception methods in ReliableSurface Test: boots Test: manually test with opening and scrolling through settings app Change-Id: I8d7a44d3ead0b2350318e1514153e256f97ccca5 --- libs/hwui/renderthread/CanvasContext.cpp | 44 ++--- libs/hwui/renderthread/CanvasContext.h | 2 +- libs/hwui/renderthread/ReliableSurface.cpp | 265 +++++++++++------------------ libs/hwui/renderthread/ReliableSurface.h | 47 ++--- 4 files changed, 152 insertions(+), 206 deletions(-) (limited to 'libs') diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 5993e176f0b8..096e25344f47 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -143,10 +143,11 @@ void CanvasContext::setSurface(sp&& surface, bool enableTimeout) { ATRACE_CALL(); if (surface) { - mNativeSurface = new ReliableSurface{std::move(surface)}; + mNativeSurface = std::make_unique(std::move(surface)); + mNativeSurface->init(); if (enableTimeout) { // TODO: Fix error handling & re-shorten timeout - ANativeWindow_setDequeueTimeout(mNativeSurface.get(), 4000_ms); + ANativeWindow_setDequeueTimeout(mNativeSurface->getNativeWindow(), 4000_ms); } } else { mNativeSurface = nullptr; @@ -161,8 +162,9 @@ void CanvasContext::setSurface(sp&& surface, bool enableTimeout) { } ColorMode colorMode = mWideColorGamut ? ColorMode::WideColorGamut : ColorMode::SRGB; - bool hasSurface = mRenderPipeline->setSurface(mNativeSurface.get(), mSwapBehavior, colorMode, - mRenderAheadCapacity); + bool hasSurface = mRenderPipeline->setSurface( + mNativeSurface ? mNativeSurface->getNativeWindow() : nullptr, mSwapBehavior, colorMode, + mRenderAheadCapacity); mFrameNumber = -1; @@ -428,7 +430,7 @@ void CanvasContext::setPresentTime() { presentTime = mCurrentFrameInfo->get(FrameInfoIndex::Vsync) + (frameIntervalNanos * (renderAhead + 1)); } - native_window_set_buffers_timestamp(mNativeSurface.get(), presentTime); + native_window_set_buffers_timestamp(mNativeSurface->getNativeWindow(), presentTime); } void CanvasContext::draw() { @@ -489,16 +491,18 @@ void CanvasContext::draw() { swap.swapCompletedTime = systemTime(SYSTEM_TIME_MONOTONIC); swap.vsyncTime = mRenderThread.timeLord().latestVsync(); if (didDraw) { - nsecs_t dequeueStart = ANativeWindow_getLastDequeueStartTime(mNativeSurface.get()); + nsecs_t dequeueStart = + ANativeWindow_getLastDequeueStartTime(mNativeSurface->getNativeWindow()); if (dequeueStart < mCurrentFrameInfo->get(FrameInfoIndex::SyncStart)) { // Ignoring dequeue duration as it happened prior to frame render start // and thus is not part of the frame. swap.dequeueDuration = 0; } else { swap.dequeueDuration = - ANativeWindow_getLastDequeueDuration(mNativeSurface.get()); + ANativeWindow_getLastDequeueDuration(mNativeSurface->getNativeWindow()); } - swap.queueDuration = ANativeWindow_getLastQueueDuration(mNativeSurface.get()); + swap.queueDuration = + ANativeWindow_getLastQueueDuration(mNativeSurface->getNativeWindow()); } else { swap.dequeueDuration = 0; swap.queueDuration = 0; @@ -567,14 +571,16 @@ void CanvasContext::doFrame() { } SkISize CanvasContext::getNextFrameSize() const { - ReliableSurface* surface = mNativeSurface.get(); - if (surface) { - SkISize size; - size.fWidth = ANativeWindow_getWidth(surface); - size.fHeight = ANativeWindow_getHeight(surface); - return size; + static constexpr SkISize defaultFrameSize = {INT32_MAX, INT32_MAX}; + if (mNativeSurface == nullptr) { + return defaultFrameSize; } - return {INT32_MAX, INT32_MAX}; + ANativeWindow* anw = mNativeSurface->getNativeWindow(); + + SkISize size; + size.fWidth = ANativeWindow_getWidth(anw); + size.fHeight = ANativeWindow_getHeight(anw); + return size; } void CanvasContext::prepareAndDraw(RenderNode* node) { @@ -702,11 +708,9 @@ bool CanvasContext::surfaceRequiresRedraw() { if (!mNativeSurface) return false; if (mHaveNewSurface) return true; - int width = -1; - int height = -1; - ReliableSurface* surface = mNativeSurface.get(); - surface->query(NATIVE_WINDOW_WIDTH, &width); - surface->query(NATIVE_WINDOW_HEIGHT, &height); + ANativeWindow* anw = mNativeSurface->getNativeWindow(); + const int width = ANativeWindow_getWidth(anw); + const int height = ANativeWindow_getHeight(anw); return width != mLastFrameWidth || height != mLastFrameHeight; } diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 4490f80eb8af..0967b20e44ee 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -220,7 +220,7 @@ private: int32_t mLastFrameHeight = 0; RenderThread& mRenderThread; - sp mNativeSurface; + std::unique_ptr mNativeSurface; // stopped indicates the CanvasContext will reject actual redraw operations, // and defer repaint until it is un-stopped bool mStopped = false; diff --git a/libs/hwui/renderthread/ReliableSurface.cpp b/libs/hwui/renderthread/ReliableSurface.cpp index 864780fb6ae8..e92500f5be51 100644 --- a/libs/hwui/renderthread/ReliableSurface.cpp +++ b/libs/hwui/renderthread/ReliableSurface.cpp @@ -26,64 +26,38 @@ namespace android::uirenderer::renderthread { // to propagate this error back to the caller constexpr bool DISABLE_BUFFER_PREFETCH = true; -// TODO: Make surface less protected -// This exists because perform is a varargs, and ANativeWindow has no va_list perform. -// So wrapping/chaining that is hard. Telling the compiler to ignore protected is easy, so we do -// that instead -struct SurfaceExposer : Surface { - // Make warnings happy - SurfaceExposer() = delete; - - using Surface::cancelBuffer; - using Surface::dequeueBuffer; - using Surface::lockBuffer_DEPRECATED; - using Surface::perform; - using Surface::queueBuffer; - using Surface::setBufferCount; - using Surface::setSwapInterval; -}; - -#define callProtected(surface, func, ...) ((*surface).*&SurfaceExposer::func)(__VA_ARGS__) - ReliableSurface::ReliableSurface(sp&& surface) : mSurface(std::move(surface)) { LOG_ALWAYS_FATAL_IF(!mSurface, "Error, unable to wrap a nullptr"); - - ANativeWindow::setSwapInterval = hook_setSwapInterval; - ANativeWindow::dequeueBuffer = hook_dequeueBuffer; - ANativeWindow::cancelBuffer = hook_cancelBuffer; - ANativeWindow::queueBuffer = hook_queueBuffer; - ANativeWindow::query = hook_query; - ANativeWindow::perform = hook_perform; - - ANativeWindow::dequeueBuffer_DEPRECATED = hook_dequeueBuffer_DEPRECATED; - ANativeWindow::cancelBuffer_DEPRECATED = hook_cancelBuffer_DEPRECATED; - ANativeWindow::lockBuffer_DEPRECATED = hook_lockBuffer_DEPRECATED; - ANativeWindow::queueBuffer_DEPRECATED = hook_queueBuffer_DEPRECATED; } ReliableSurface::~ReliableSurface() { clearReservedBuffer(); + // Clear out the interceptors for proper hygiene. + // As a concrete example, if the underlying ANativeWindow is associated with + // an EGLSurface that is still in use, then if we don't clear out the + // interceptors then we walk into undefined behavior. + ANativeWindow_setCancelBufferInterceptor(mSurface.get(), nullptr, nullptr); + ANativeWindow_setDequeueBufferInterceptor(mSurface.get(), nullptr, nullptr); + ANativeWindow_setQueueBufferInterceptor(mSurface.get(), nullptr, nullptr); + ANativeWindow_setPerformInterceptor(mSurface.get(), nullptr, nullptr); } -void ReliableSurface::perform(int operation, va_list args) { - std::lock_guard _lock{mMutex}; +void ReliableSurface::init() { + int result = ANativeWindow_setCancelBufferInterceptor(mSurface.get(), hook_cancelBuffer, this); + LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set cancelBuffer interceptor: error = %d", + result); - switch (operation) { - case NATIVE_WINDOW_SET_USAGE: - mUsage = va_arg(args, uint32_t); - break; - case NATIVE_WINDOW_SET_USAGE64: - mUsage = va_arg(args, uint64_t); - break; - case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY: - /* width */ va_arg(args, uint32_t); - /* height */ va_arg(args, uint32_t); - mFormat = va_arg(args, PixelFormat); - break; - case NATIVE_WINDOW_SET_BUFFERS_FORMAT: - mFormat = va_arg(args, PixelFormat); - break; - } + result = ANativeWindow_setDequeueBufferInterceptor(mSurface.get(), hook_dequeueBuffer, this); + LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set dequeueBuffer interceptor: error = %d", + result); + + result = ANativeWindow_setQueueBufferInterceptor(mSurface.get(), hook_queueBuffer, this); + LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set queueBuffer interceptor: error = %d", + result); + + result = ANativeWindow_setPerformInterceptor(mSurface.get(), hook_perform, this); + LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set perform interceptor: error = %d", + result); } int ReliableSurface::reserveNext() { @@ -111,7 +85,9 @@ int ReliableSurface::reserveNext() { int fenceFd = -1; ANativeWindowBuffer* buffer = nullptr; - int result = callProtected(mSurface, dequeueBuffer, &buffer, &fenceFd); + + // Note that this calls back into our own hooked method. + int result = ANativeWindow_dequeueBuffer(mSurface.get(), &buffer, &fenceFd); { std::lock_guard _lock{mMutex}; @@ -138,59 +114,11 @@ void ReliableSurface::clearReservedBuffer() { mHasDequeuedBuffer = false; } if (buffer) { - callProtected(mSurface, cancelBuffer, buffer, releaseFd); - } -} - -int ReliableSurface::cancelBuffer(ANativeWindowBuffer* buffer, int fenceFd) { - clearReservedBuffer(); - if (isFallbackBuffer(buffer)) { - if (fenceFd > 0) { - close(fenceFd); - } - return OK; - } - int result = callProtected(mSurface, cancelBuffer, buffer, fenceFd); - return result; -} - -int ReliableSurface::dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd) { - { - std::lock_guard _lock{mMutex}; - if (mReservedBuffer) { - *buffer = mReservedBuffer; - *fenceFd = mReservedFenceFd.release(); - mReservedBuffer = nullptr; - return OK; - } - } - - - int result = callProtected(mSurface, dequeueBuffer, buffer, fenceFd); - if (result != OK) { - ALOGW("dequeueBuffer failed, error = %d; switching to fallback", result); - *buffer = acquireFallbackBuffer(result); - *fenceFd = -1; - return *buffer ? OK : INVALID_OPERATION; - } else { - std::lock_guard _lock{mMutex}; - mHasDequeuedBuffer = true; - } - return OK; -} - -int ReliableSurface::queueBuffer(ANativeWindowBuffer* buffer, int fenceFd) { - clearReservedBuffer(); - - if (isFallbackBuffer(buffer)) { - if (fenceFd > 0) { - close(fenceFd); - } - return OK; + // Note that clearReservedBuffer may be reentrant here, so + // mReservedBuffer must be cleared once we reach here to avoid recursing + // forever. + ANativeWindow_cancelBuffer(mSurface.get(), buffer, releaseFd); } - - int result = callProtected(mSurface, queueBuffer, buffer, fenceFd); - return result; } bool ReliableSurface::isFallbackBuffer(const ANativeWindowBuffer* windowBuffer) const { @@ -229,82 +157,95 @@ ANativeWindowBuffer* ReliableSurface::acquireFallbackBuffer(int error) { return AHardwareBuffer_to_ANativeWindowBuffer(newBuffer); } -Surface* ReliableSurface::getWrapped(const ANativeWindow* window) { - return getSelf(window)->mSurface.get(); -} - -int ReliableSurface::hook_setSwapInterval(ANativeWindow* window, int interval) { - return callProtected(getWrapped(window), setSwapInterval, interval); -} - -int ReliableSurface::hook_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer, - int* fenceFd) { - return getSelf(window)->dequeueBuffer(buffer, fenceFd); -} - -int ReliableSurface::hook_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, - int fenceFd) { - return getSelf(window)->cancelBuffer(buffer, fenceFd); -} - -int ReliableSurface::hook_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, - int fenceFd) { - return getSelf(window)->queueBuffer(buffer, fenceFd); -} +int ReliableSurface::hook_dequeueBuffer(ANativeWindow* window, + ANativeWindow_dequeueBufferFn dequeueBuffer, void* data, + ANativeWindowBuffer** buffer, int* fenceFd) { + ReliableSurface* rs = reinterpret_cast(data); + { + std::lock_guard _lock{rs->mMutex}; + if (rs->mReservedBuffer) { + *buffer = rs->mReservedBuffer; + *fenceFd = rs->mReservedFenceFd.release(); + rs->mReservedBuffer = nullptr; + return OK; + } + } -int ReliableSurface::hook_dequeueBuffer_DEPRECATED(ANativeWindow* window, - ANativeWindowBuffer** buffer) { - ANativeWindowBuffer* buf; - int fenceFd = -1; - int result = window->dequeueBuffer(window, &buf, &fenceFd); + int result = dequeueBuffer(window, buffer, fenceFd); if (result != OK) { - return result; - } - sp fence(new Fence(fenceFd)); - int waitResult = fence->waitForever("dequeueBuffer_DEPRECATED"); - if (waitResult != OK) { - ALOGE("dequeueBuffer_DEPRECATED: Fence::wait returned an error: %d", waitResult); - window->cancelBuffer(window, buf, -1); - return waitResult; + ALOGW("dequeueBuffer failed, error = %d; switching to fallback", result); + *buffer = rs->acquireFallbackBuffer(result); + *fenceFd = -1; + return *buffer ? OK : INVALID_OPERATION; + } else { + std::lock_guard _lock{rs->mMutex}; + rs->mHasDequeuedBuffer = true; } - *buffer = buf; - return result; + return OK; } -int ReliableSurface::hook_cancelBuffer_DEPRECATED(ANativeWindow* window, - ANativeWindowBuffer* buffer) { - return window->cancelBuffer(window, buffer, -1); +int ReliableSurface::hook_cancelBuffer(ANativeWindow* window, + ANativeWindow_cancelBufferFn cancelBuffer, void* data, + ANativeWindowBuffer* buffer, int fenceFd) { + ReliableSurface* rs = reinterpret_cast(data); + rs->clearReservedBuffer(); + if (rs->isFallbackBuffer(buffer)) { + if (fenceFd > 0) { + close(fenceFd); + } + return OK; + } + return cancelBuffer(window, buffer, fenceFd); } -int ReliableSurface::hook_lockBuffer_DEPRECATED(ANativeWindow* window, - ANativeWindowBuffer* buffer) { - // This method is a no-op in Surface as well - return OK; -} +int ReliableSurface::hook_queueBuffer(ANativeWindow* window, + ANativeWindow_queueBufferFn queueBuffer, void* data, + ANativeWindowBuffer* buffer, int fenceFd) { + ReliableSurface* rs = reinterpret_cast(data); + rs->clearReservedBuffer(); -int ReliableSurface::hook_queueBuffer_DEPRECATED(ANativeWindow* window, - ANativeWindowBuffer* buffer) { - return window->queueBuffer(window, buffer, -1); -} + if (rs->isFallbackBuffer(buffer)) { + if (fenceFd > 0) { + close(fenceFd); + } + return OK; + } -int ReliableSurface::hook_query(const ANativeWindow* window, int what, int* value) { - return getWrapped(window)->query(what, value); + return queueBuffer(window, buffer, fenceFd); } -int ReliableSurface::hook_perform(ANativeWindow* window, int operation, ...) { +int ReliableSurface::hook_perform(ANativeWindow* window, ANativeWindow_performFn perform, + void* data, int operation, va_list args) { // Drop the reserved buffer if there is one since this (probably) mutated buffer dimensions // TODO: Filter to things that only affect the reserved buffer // TODO: Can we mutate the reserved buffer in some cases? - getSelf(window)->clearReservedBuffer(); - va_list args; - va_start(args, operation); - int result = callProtected(getWrapped(window), perform, operation, args); - va_end(args); + ReliableSurface* rs = reinterpret_cast(data); + rs->clearReservedBuffer(); - va_start(args, operation); - getSelf(window)->perform(operation, args); - va_end(args); + va_list argsCopy; + va_copy(argsCopy, args); + int result = perform(window, operation, argsCopy); + { + std::lock_guard _lock{rs->mMutex}; + + switch (operation) { + case ANATIVEWINDOW_PERFORM_SET_USAGE: + rs->mUsage = va_arg(args, uint32_t); + break; + case ANATIVEWINDOW_PERFORM_SET_USAGE64: + rs->mUsage = va_arg(args, uint64_t); + break; + case ANATIVEWINDOW_PERFORM_SET_BUFFERS_GEOMETRY: + /* width */ va_arg(args, uint32_t); + /* height */ va_arg(args, uint32_t); + rs->mFormat = va_arg(args, PixelFormat); + break; + case ANATIVEWINDOW_PERFORM_SET_BUFFERS_FORMAT: + rs->mFormat = va_arg(args, PixelFormat); + break; + } + } return result; } diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h index f768df37ba7d..a823d9da0ab3 100644 --- a/libs/hwui/renderthread/ReliableSurface.h +++ b/libs/hwui/renderthread/ReliableSurface.h @@ -16,6 +16,7 @@ #pragma once +#include #include #include #include @@ -24,13 +25,20 @@ namespace android::uirenderer::renderthread { -class ReliableSurface : public ANativeObjectBase { +class ReliableSurface { PREVENT_COPY_AND_ASSIGN(ReliableSurface); public: ReliableSurface(sp&& surface); ~ReliableSurface(); + // Performs initialization that is not safe to do in the constructor. + // For instance, registering ANativeWindow interceptors with ReliableSurface + // passed as the data pointer is not safe. + void init(); + + ANativeWindow* getNativeWindow() { return mSurface.get(); } + int reserveNext(); void allocateBuffers() { mSurface->allocateBuffers(); } @@ -61,7 +69,7 @@ public: } private: - const sp mSurface; + sp mSurface; mutable std::mutex mMutex; @@ -78,27 +86,20 @@ private: ANativeWindowBuffer* acquireFallbackBuffer(int error); void clearReservedBuffer(); - void perform(int operation, va_list args); - int cancelBuffer(ANativeWindowBuffer* buffer, int fenceFd); - int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd); - int queueBuffer(ANativeWindowBuffer* buffer, int fenceFd); - - static Surface* getWrapped(const ANativeWindow*); - - // ANativeWindow hooks - static int hook_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd); - static int hook_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer, - int* fenceFd); - static int hook_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd); - - static int hook_perform(ANativeWindow* window, int operation, ...); - static int hook_query(const ANativeWindow* window, int what, int* value); - static int hook_setSwapInterval(ANativeWindow* window, int interval); - - static int hook_cancelBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer); - static int hook_dequeueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer** buffer); - static int hook_lockBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer); - static int hook_queueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer); + // ANativeWindow hooks. When an ANativeWindow_* method is called on the + // underlying ANativeWindow, these methods will intercept the original call. + // For example, an EGL driver would call into these hooks instead of the + // original methods. + static int hook_cancelBuffer(ANativeWindow* window, ANativeWindow_cancelBufferFn cancelBuffer, + void* data, ANativeWindowBuffer* buffer, int fenceFd); + static int hook_dequeueBuffer(ANativeWindow* window, + ANativeWindow_dequeueBufferFn dequeueBuffer, void* data, + ANativeWindowBuffer** buffer, int* fenceFd); + static int hook_queueBuffer(ANativeWindow* window, ANativeWindow_queueBufferFn queueBuffer, + void* data, ANativeWindowBuffer* buffer, int fenceFd); + + static int hook_perform(ANativeWindow* window, ANativeWindow_performFn perform, void* data, + int operation, va_list args); }; }; // namespace android::uirenderer::renderthread -- cgit v1.2.3 From c2a6537fb96b6af210167797f9037b0a39a3a11b Mon Sep 17 00:00:00 2001 From: Alec Mouri Date: Mon, 9 Dec 2019 17:08:47 -0800 Subject: [HWUI] Hook up ANativeWindow_allocateBuffers Bug: 137012798 Test: builds Change-Id: Iefe7b73470cb6229fc72d1a1aa33eceb827f527c --- libs/hwui/renderthread/CanvasContext.cpp | 3 ++- libs/hwui/renderthread/ReliableSurface.h | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'libs') diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 096e25344f47..699b96a685c9 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -205,7 +205,8 @@ void CanvasContext::setStopped(bool stopped) { void CanvasContext::allocateBuffers() { if (mNativeSurface) { - mNativeSurface->allocateBuffers(); + ANativeWindow* anw = mNativeSurface->getNativeWindow(); + ANativeWindow_allocateBuffers(anw); } } diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h index a823d9da0ab3..32472539f616 100644 --- a/libs/hwui/renderthread/ReliableSurface.h +++ b/libs/hwui/renderthread/ReliableSurface.h @@ -41,8 +41,6 @@ public: int reserveNext(); - void allocateBuffers() { mSurface->allocateBuffers(); } - int query(int what, int* value) const { return mSurface->query(what, value); } uint64_t getNextFrameNumber() const { return mSurface->getNextFrameNumber(); } -- cgit v1.2.3 From a49beaaa693d0900a65c8ebf2a1f44242e469f59 Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Thu, 30 Jan 2020 15:34:13 -0500 Subject: Replace Outline#setConvexPath with Outline#setPath Bug: 148544953 Test: No change in behavior, no new tests setConvexPath no longer requires that the Path be Convex, so deprecate the method and replace it with a name that does not imply it must be Convex. Update internal references to the Path being Convex as well. Change-Id: I8935dc8ac7f7fb7db0d667e35fda67afdf2e6ac8 --- libs/hwui/Outline.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'libs') diff --git a/libs/hwui/Outline.h b/libs/hwui/Outline.h index f1c38031980e..2eb2c7c7e299 100644 --- a/libs/hwui/Outline.h +++ b/libs/hwui/Outline.h @@ -26,7 +26,7 @@ namespace uirenderer { class Outline { public: - enum class Type { None = 0, Empty = 1, ConvexPath = 2, RoundRect = 3 }; + enum class Type { None = 0, Empty = 1, Path = 2, RoundRect = 3 }; Outline() : mShouldClip(false), mType(Type::None), mRadius(0), mAlpha(0.0f) {} @@ -57,12 +57,12 @@ public: } } - void setConvexPath(const SkPath* outline, float alpha) { + void setPath(const SkPath* outline, float alpha) { if (!outline) { setEmpty(); return; } - mType = Type::ConvexPath; + mType = Type::Path; mPath = *outline; mBounds.set(outline->getBounds()); mAlpha = alpha; -- cgit v1.2.3 From 80c7ef17dbc182d381715f37fe6566146611a1da Mon Sep 17 00:00:00 2001 From: Alec Mouri Date: Tue, 10 Dec 2019 15:09:01 -0800 Subject: [HWUI] Use ANativeWindow_getNextFrameId api. This is part of the work to move away from gui/Surface and onto ANativeWindow in HWUI. Bug: 137012798 Test: builds Change-Id: I5862d7833b583b8295d1677e725ed58d97808b27 --- libs/hwui/renderthread/CanvasContext.cpp | 2 +- libs/hwui/renderthread/ReliableSurface.h | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'libs') diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 699b96a685c9..c1435d1ea2d5 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -700,7 +700,7 @@ void CanvasContext::enqueueFrameWork(std::function&& func) { int64_t CanvasContext::getFrameNumber() { // mFrameNumber is reset to -1 when the surface changes or we swap buffers if (mFrameNumber == -1 && mNativeSurface.get()) { - mFrameNumber = static_cast(mNativeSurface->getNextFrameNumber()); + mFrameNumber = ANativeWindow_getNextFrameId(mNativeSurface->getNativeWindow()); } return mFrameNumber; } diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h index 32472539f616..da5097ce33f0 100644 --- a/libs/hwui/renderthread/ReliableSurface.h +++ b/libs/hwui/renderthread/ReliableSurface.h @@ -43,8 +43,6 @@ public: int query(int what, int* value) const { return mSurface->query(what, value); } - uint64_t getNextFrameNumber() const { return mSurface->getNextFrameNumber(); } - int getAndClearError() { int ret = mBufferQueueState; mBufferQueueState = OK; -- cgit v1.2.3 From 996773a5d36949a7af3571927efa613670bd73b6 Mon Sep 17 00:00:00 2001 From: John Reck Date: Mon, 3 Feb 2020 16:30:56 -0800 Subject: Convert fatal aborts to error returns Bug: 148805166 Test: none Change-Id: I7824ed77721d2fc9a414797d4637e9d9239f1a99 --- libs/hwui/HardwareBitmapUploader.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'libs') diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp index 3681c69e912b..a3d552faeb0a 100644 --- a/libs/hwui/HardwareBitmapUploader.cpp +++ b/libs/hwui/HardwareBitmapUploader.cpp @@ -187,7 +187,9 @@ private: EGLSyncKHR fence = mUploadThread->queue().runSync([&]() -> EGLSyncKHR { AutoSkiaGlTexture glTexture; glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, autoImage.image); - GL_CHECKPOINT(MODERATE); + if (GLUtils::dumpGLErrors()) { + return EGL_NO_SYNC_KHR; + } // glTexSubImage2D is synchronous in sense that it memcpy() from pointer that we // provide. @@ -195,19 +197,26 @@ private: // when we first use it in drawing glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap.width(), bitmap.height(), format.format, format.type, bitmap.getPixels()); - GL_CHECKPOINT(MODERATE); + if (GLUtils::dumpGLErrors()) { + return EGL_NO_SYNC_KHR; + } EGLSyncKHR uploadFence = eglCreateSyncKHR(eglGetCurrentDisplay(), EGL_SYNC_FENCE_KHR, NULL); - LOG_ALWAYS_FATAL_IF(uploadFence == EGL_NO_SYNC_KHR, - "Could not create sync fence %#x", eglGetError()); + if (uploadFence == EGL_NO_SYNC_KHR) { + ALOGW("Could not create sync fence %#x", eglGetError()); + }; glFlush(); + GLUtils::dumpGLErrors(); return uploadFence; }); + if (fence == EGL_NO_SYNC_KHR) { + return false; + } EGLint waitStatus = eglClientWaitSyncKHR(display, fence, 0, FENCE_TIMEOUT); - LOG_ALWAYS_FATAL_IF(waitStatus != EGL_CONDITION_SATISFIED_KHR, - "Failed to wait for the fence %#x", eglGetError()); + ALOGE_IF(waitStatus != EGL_CONDITION_SATISFIED_KHR, + "Failed to wait for the fence %#x", eglGetError()); eglDestroySyncKHR(display, fence); } -- cgit v1.2.3 From 56cffe5b6a0c4bca1b926d9d80c9298122e4a573 Mon Sep 17 00:00:00 2001 From: John Reck Date: Mon, 3 Feb 2020 16:47:12 -0800 Subject: Adjust present time calculation Adjust the target present time when using render_ahead to better fit in the gaps between vsync pulses. This prevents small clock drifts from causing jank. Bug: 143540628 Test: manual, looked at systrace Change-Id: I26ae6ebf16a3dea48dfd8e1c420d9e5e43e42bcb --- libs/hwui/renderthread/CanvasContext.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 699b96a685c9..2b48c3581933 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -429,7 +429,8 @@ void CanvasContext::setPresentTime() { if (renderAhead) { presentTime = mCurrentFrameInfo->get(FrameInfoIndex::Vsync) + - (frameIntervalNanos * (renderAhead + 1)); + (frameIntervalNanos * (renderAhead + 1)) - DeviceInfo::get()->getAppOffset() + + (frameIntervalNanos / 2); } native_window_set_buffers_timestamp(mNativeSurface->getNativeWindow(), presentTime); } -- cgit v1.2.3 From f9a4dc41b51484420fac84bcbb09f3caa469e004 Mon Sep 17 00:00:00 2001 From: Jerome Gaillard Date: Wed, 29 Jan 2020 15:23:26 +0000 Subject: Make HWUI build for macOs Only use AnimatedImageThread on Android, it is not currently supported on host. Bug: 148525019 Test: on macOs: lunch sdk & m libhwui Change-Id: I644bc82887fb7e36b2a5e4117ee328a3df99a0d4 --- libs/hwui/Android.bp | 2 +- libs/hwui/hwui/AnimatedImageDrawable.cpp | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 51270f5bcebd..2ec7ceb20f4a 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -168,7 +168,6 @@ cc_defaults { "renderthread/RenderTask.cpp", "renderthread/TimeLord.cpp", "hwui/AnimatedImageDrawable.cpp", - "hwui/AnimatedImageThread.cpp", "hwui/Bitmap.cpp", "hwui/Canvas.cpp", "hwui/ImageDecoder.cpp", @@ -210,6 +209,7 @@ cc_defaults { android: { srcs: [ + "hwui/AnimatedImageThread.cpp", "pipeline/skia/ATraceMemoryDump.cpp", "pipeline/skia/GLFunctorDrawable.cpp", "pipeline/skia/LayerDrawable.cpp", diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp index 4544beae5df8..638de850a6c5 100644 --- a/libs/hwui/hwui/AnimatedImageDrawable.cpp +++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp @@ -15,7 +15,9 @@ */ #include "AnimatedImageDrawable.h" +#ifdef __ANDROID__ // Layoutlib does not support AnimatedImageThread #include "AnimatedImageThread.h" +#endif #include "utils/TraceUtils.h" @@ -160,8 +162,10 @@ void AnimatedImageDrawable::onDraw(SkCanvas* canvas) { } else if (starting) { // The image has animated, and now is being reset. Queue up the first // frame, but keep showing the current frame until the first is ready. +#ifdef __ANDROID__ // Layoutlib does not support AnimatedImageThread auto& thread = uirenderer::AnimatedImageThread::getInstance(); mNextSnapshot = thread.reset(sk_ref_sp(this)); +#endif } bool finalFrame = false; @@ -187,8 +191,10 @@ void AnimatedImageDrawable::onDraw(SkCanvas* canvas) { } if (mRunning && !mNextSnapshot.valid()) { +#ifdef __ANDROID__ // Layoutlib does not support AnimatedImageThread auto& thread = uirenderer::AnimatedImageThread::getInstance(); mNextSnapshot = thread.decodeNextFrame(sk_ref_sp(this)); +#endif } if (!drawDirectly) { -- cgit v1.2.3 From 6eeca5c776d95067d5e796b02cf11b8a755b4efc Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Thu, 30 Jan 2020 13:59:50 -0500 Subject: AImageDecoder: allow no color conversion Bug: 135133301 Test: I5e8bdcdae6837db23c0f4ef08f931f3bebe0ce0d Previously the default SkColorSpace for AImageDecoder was set to the result of SkAndroidCodec::computeOutputColorSpace. If the image has a profile that does not map to an SkColorSpace, it will return either DISPLAY_P3 or SRGB. Using that at decode time will result in color conversion. Instead, default to a null SkColorSpace for such a profile, resulting in no color conversion. If the image has no profile, default to SRGB, as usual. A client that wants SRGB can still request and get that, but this allows getting the raw pixels for an advanced client that may want to do its own conversion. Change-Id: I489f31fef79dec11e97c8e8fb9207adb77a3d0c7 --- libs/hwui/hwui/ImageDecoder.cpp | 15 ++++++++++++++- libs/hwui/hwui/ImageDecoder.h | 1 + 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/hwui/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp index 4b2857f6c290..afd82aca07c5 100644 --- a/libs/hwui/hwui/ImageDecoder.cpp +++ b/libs/hwui/hwui/ImageDecoder.cpp @@ -24,6 +24,19 @@ using namespace android; +sk_sp ImageDecoder::getDefaultColorSpace() const { + const skcms_ICCProfile* encodedProfile = mCodec->getICCProfile(); + if (encodedProfile) { + // If the profile maps directly to an SkColorSpace, that SkColorSpace + // will be returned. Otherwise, nullptr will be returned. In either + // case, using this SkColorSpace results in doing no color correction. + return SkColorSpace::Make(*encodedProfile); + } + + // The image has no embedded color profile, and should be treated as SRGB. + return SkColorSpace::MakeSRGB(); +} + ImageDecoder::ImageDecoder(std::unique_ptr codec, sk_sp peeker) : mCodec(std::move(codec)) , mPeeker(std::move(peeker)) @@ -31,7 +44,7 @@ ImageDecoder::ImageDecoder(std::unique_ptr codec, sk_spcomputeOutputColorType(kN32_SkColorType)) , mUnpremultipliedRequired(false) - , mOutColorSpace(mCodec->computeOutputColorSpace(mOutColorType, nullptr)) + , mOutColorSpace(getDefaultColorSpace()) , mSampleSize(1) { } diff --git a/libs/hwui/hwui/ImageDecoder.h b/libs/hwui/hwui/ImageDecoder.h index 0c99f84cbb72..a1b51573db3f 100644 --- a/libs/hwui/hwui/ImageDecoder.h +++ b/libs/hwui/hwui/ImageDecoder.h @@ -43,6 +43,7 @@ public: bool setUnpremultipliedRequired(bool unpremultipliedRequired); + sk_sp getDefaultColorSpace() const; void setOutColorSpace(sk_sp cs); // The size is the final size after scaling and cropping. -- cgit v1.2.3 From 255ffff56d7c99fb756c20f9f5a6c4ea93504813 Mon Sep 17 00:00:00 2001 From: Joe Onorato Date: Fri, 17 Jan 2020 01:30:02 -0800 Subject: Make libincident into a stable C API. Test: atest GtsIncidentConfirmationTestCases GtsIncidentManagerTestCases libincident_test Bug: 144187174 Change-Id: I65b5a13cfb6a57aa56f738e25a76c5ecb8e7a1a8 --- libs/incident/Android.bp | 69 +++++++- libs/incident/TEST_MAPPING | 21 +++ .../include/android/os/IncidentReportArgs.h | 77 --------- libs/incident/include/incident/incident_report.h | 192 +++++++++++++++++++++ .../include_priv/android/os/IncidentReportArgs.h | 79 +++++++++ libs/incident/libincident.map.txt | 15 ++ libs/incident/src/incident_report.cpp | 83 +++++++++ libs/incident/tests/IncidentReportArgs_test.cpp | 74 ++++++++ libs/incident/tests/IncidentReportRequest_test.cpp | 65 +++++++ libs/incident/tests/c_api_compile_test.c | 11 ++ 10 files changed, 607 insertions(+), 79 deletions(-) create mode 100644 libs/incident/TEST_MAPPING delete mode 100644 libs/incident/include/android/os/IncidentReportArgs.h create mode 100644 libs/incident/include/incident/incident_report.h create mode 100644 libs/incident/include_priv/android/os/IncidentReportArgs.h create mode 100644 libs/incident/libincident.map.txt create mode 100644 libs/incident/src/incident_report.cpp create mode 100644 libs/incident/tests/IncidentReportArgs_test.cpp create mode 100644 libs/incident/tests/IncidentReportRequest_test.cpp create mode 100644 libs/incident/tests/c_api_compile_test.c (limited to 'libs') diff --git a/libs/incident/Android.bp b/libs/incident/Android.bp index 150f6dcde5d0..512b8c439dcf 100644 --- a/libs/incident/Android.bp +++ b/libs/incident/Android.bp @@ -12,8 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -cc_library_shared { - name: "libincident", + +cc_defaults { + name: "libincidentpriv_defaults", cflags: [ "-Wall", @@ -50,6 +51,70 @@ cc_library_shared { ":libincident_aidl", "src/IncidentReportArgs.cpp", ], +} + +cc_library_shared { + name: "libincidentpriv", + defaults: ["libincidentpriv_defaults"], + export_include_dirs: ["include_priv"], +} + +cc_library_shared { + name: "libincident", + + cflags: [ + "-Wall", + "-Werror", + "-Wno-missing-field-initializers", + "-Wno-unused-variable", + "-Wunused-parameter", + ], + + shared_libs: [ + "libbinder", + "liblog", + "libutils", + "libincidentpriv", + ], + + srcs: [ + "src/incident_report.cpp", + ], export_include_dirs: ["include"], + + stubs: { + symbol_file: "libincident.map.txt", + versions: [ + "30", + ], + }, } + +cc_test { + name: "libincident_test", + defaults: ["libincidentpriv_defaults"], + test_suites: ["device-tests"], + + include_dirs: [ + "frameworks/base/libs/incident/include", + "frameworks/base/libs/incident/include_priv", + ], + + srcs: [ + "tests/IncidentReportArgs_test.cpp", + "tests/IncidentReportRequest_test.cpp", + "tests/c_api_compile_test.c", + ], + + shared_libs: [ + "libincident", + ], + + static_libs: [ + "libgmock", + ], +} + + + diff --git a/libs/incident/TEST_MAPPING b/libs/incident/TEST_MAPPING new file mode 100644 index 000000000000..b49513543cb1 --- /dev/null +++ b/libs/incident/TEST_MAPPING @@ -0,0 +1,21 @@ +{ + "presubmit": [ + { + "name": "libincident_c_api_test" + }, + { + "name": "GtsIncidentConfirmationTestCases" + }, + { + "name": "GtsIncidentManagerTestCases" + } + ], + "postsubmit": [ + ], + "imports": [ + { + "path": "frameworks/base/cmds/incidentd" + } + ] +} + diff --git a/libs/incident/include/android/os/IncidentReportArgs.h b/libs/incident/include/android/os/IncidentReportArgs.h deleted file mode 100644 index 94b4ad6eae31..000000000000 --- a/libs/incident/include/android/os/IncidentReportArgs.h +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright (c) 2016, 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_OS_DUMPSTATE_ARGS_H_ -#define ANDROID_OS_DUMPSTATE_ARGS_H_ - -#include -#include -#include - -#include -#include - -namespace android { -namespace os { - -using namespace std; - -// DESTINATION enum value, sync with frameworks/base/core/proto/android/privacy.proto -const uint8_t PRIVACY_POLICY_LOCAL = 0; -const uint8_t PRIVACY_POLICY_EXPLICIT = 100; -const uint8_t PRIVACY_POLICY_AUTOMATIC = 200; -const uint8_t PRIVACY_POLICY_UNSET = 255; - - -class IncidentReportArgs : public Parcelable { -public: - IncidentReportArgs(); - IncidentReportArgs(const IncidentReportArgs& that); - virtual ~IncidentReportArgs(); - - virtual status_t writeToParcel(Parcel* out) const; - virtual status_t readFromParcel(const Parcel* in); - - void setAll(bool all); - void setPrivacyPolicy(int privacyPolicy); - void addSection(int section); - void setReceiverPkg(const string& pkg); - void setReceiverCls(const string& cls); - void addHeader(const vector& headerProto); - - inline bool all() const { return mAll; } - bool containsSection(int section, bool specific) const; - inline int getPrivacyPolicy() const { return mPrivacyPolicy; } - inline const set& sections() const { return mSections; } - inline const string& receiverPkg() const { return mReceiverPkg; } - inline const string& receiverCls() const { return mReceiverCls; } - inline const vector>& headers() const { return mHeaders; } - - void merge(const IncidentReportArgs& that); - -private: - set mSections; - vector> mHeaders; - bool mAll; - int mPrivacyPolicy; - string mReceiverPkg; - string mReceiverCls; -}; - -} -} - -#endif // ANDROID_OS_DUMPSTATE_ARGS_H_ diff --git a/libs/incident/include/incident/incident_report.h b/libs/incident/include/incident/incident_report.h new file mode 100644 index 000000000000..49fe5b9b73b4 --- /dev/null +++ b/libs/incident/include/incident/incident_report.h @@ -0,0 +1,192 @@ +/** + * Copyright (c) 2020, 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. + */ + +/** + * @file incident_report.h + */ + +#ifndef ANDROID_INCIDENT_INCIDENT_REPORT_H +#define ANDROID_INCIDENT_INCIDENT_REPORT_H + +#include + +#if __cplusplus +#include +#include +#include + +extern "C" { +#endif // __cplusplus + +struct AIncidentReportArgs; +/** + * Opaque class to represent the arguments to an incident report request. + * Incident reports contain debugging data about the device at runtime. + * For more information see the android.os.IncidentManager java class. + */ +typedef struct AIncidentReportArgs AIncidentReportArgs; + +// Privacy policy enum value, sync with frameworks/base/core/proto/android/privacy.proto, +// IncidentReportArgs.h and IncidentReportArgs.java. +enum { + /** + * Flag marking fields and incident reports than can be taken + * off the device only via adb. + */ + INCIDENT_REPORT_PRIVACY_POLICY_LOCAL = 0, + + /** + * Flag marking fields and incident reports than can be taken + * off the device with contemporary consent. + */ + INCIDENT_REPORT_PRIVACY_POLICY_EXPLICIT = 100, + + /** + * Flag marking fields and incident reports than can be taken + * off the device with prior consent. + */ + INCIDENT_REPORT_PRIVACY_POLICY_AUTOMATIC = 200, + + /** + * Flag to indicate that a given field has not been marked + * with a privacy policy. + */ + INCIDENT_REPORT_PRIVACY_POLICY_UNSET = 255 +}; + +/** + * Allocate and initialize an AIncidentReportArgs object. + */ +AIncidentReportArgs* AIncidentReportArgs_init(); + +/** + * Duplicate an existing AIncidentReportArgs object. + */ +AIncidentReportArgs* AIncidentReportArgs_clone(AIncidentReportArgs* that); + +/** + * Clean up and delete an AIncidentReportArgs object. + */ +void AIncidentReportArgs_delete(AIncidentReportArgs* args); + +/** + * Set this incident report to include all sections. + */ +void AIncidentReportArgs_setAll(AIncidentReportArgs* args, bool all); + +/** + * Set this incident report privacy policy spec. + */ +void AIncidentReportArgs_setPrivacyPolicy(AIncidentReportArgs* args, int privacyPolicy); + +/** + * Add this section to the incident report. The section IDs are the field numbers + * from the android.os.IncidentProto protobuf message. + */ +void AIncidentReportArgs_addSection(AIncidentReportArgs* args, int section); + +/** + * Set the apk package name that will be sent a broadcast when the incident + * report completes. Must be called in conjunction with AIncidentReportArgs_setReceiverClass. + */ +void AIncidentReportArgs_setReceiverPackage(AIncidentReportArgs* args, char const* pkg); + +/** + * Set the fully qualified class name of the java BroadcastReceiver class that will be + * sent a broadcast when the report completes. Must be called in conjunction with + * AIncidentReportArgs_setReceiverPackage. + */ +void AIncidentReportArgs_setReceiverClass(AIncidentReportArgs* args, char const* cls); + +/** + * Add protobuf data as a header to the incident report. The buffer should be a serialized + * android.os.IncidentHeaderProto object. + */ +void AIncidentReportArgs_addHeader(AIncidentReportArgs* args, uint8_t const* buf, size_t size); + +/** + * Initiate taking the report described in the args object. Returns 0 on success, + * and non-zero otherwise. + */ +int AIncidentReportArgs_takeReport(AIncidentReportArgs* args); + +#if __cplusplus +} // extern "C" + +namespace android { +namespace os { + +class IncidentReportRequest { +public: + inline IncidentReportRequest() { + mImpl = AIncidentReportArgs_init(); + } + + inline IncidentReportRequest(const IncidentReportRequest& that) { + mImpl = AIncidentReportArgs_clone(that.mImpl); + } + + inline ~IncidentReportRequest() { + AIncidentReportArgs_delete(mImpl); + } + + inline AIncidentReportArgs* getImpl() { + return mImpl; + } + + inline void setAll(bool all) { + AIncidentReportArgs_setAll(mImpl, all); + } + + inline void setPrivacyPolicy(int privacyPolicy) { + AIncidentReportArgs_setPrivacyPolicy(mImpl, privacyPolicy); + } + + inline void addSection(int section) { + AIncidentReportArgs_addSection(mImpl, section); + } + + inline void setReceiverPackage(const std::string& pkg) { + AIncidentReportArgs_setReceiverPackage(mImpl, pkg.c_str()); + }; + + inline void setReceiverClass(const std::string& cls) { + AIncidentReportArgs_setReceiverClass(mImpl, cls.c_str()); + }; + + inline void addHeader(const std::vector& headerProto) { + AIncidentReportArgs_addHeader(mImpl, headerProto.data(), headerProto.size()); + }; + + inline void addHeader(const uint8_t* buf, size_t size) { + AIncidentReportArgs_addHeader(mImpl, buf, size); + }; + + // returns a status_t + inline int takeReport() { + return AIncidentReportArgs_takeReport(mImpl); + } + +private: + AIncidentReportArgs* mImpl; +}; + +} // namespace os +} // namespace android + +#endif // __cplusplus + +#endif // ANDROID_INCIDENT_INCIDENT_REPORT_H diff --git a/libs/incident/include_priv/android/os/IncidentReportArgs.h b/libs/incident/include_priv/android/os/IncidentReportArgs.h new file mode 100644 index 000000000000..0e6159032e45 --- /dev/null +++ b/libs/incident/include_priv/android/os/IncidentReportArgs.h @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2016, 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_OS_INCIDENT_REPORT_ARGS_H +#define ANDROID_OS_INCIDENT_REPORT_ARGS_H + +#include +#include +#include +#include + +#include +#include + +namespace android { +namespace os { + +using namespace std; + +// DESTINATION enum value, sync with frameworks/base/core/proto/android/privacy.proto, +// incident/incident_report.h and IncidentReportArgs.java +const uint8_t PRIVACY_POLICY_LOCAL = 0; +const uint8_t PRIVACY_POLICY_EXPLICIT = 100; +const uint8_t PRIVACY_POLICY_AUTOMATIC = 200; +const uint8_t PRIVACY_POLICY_UNSET = 255; + + +class IncidentReportArgs : public Parcelable { +public: + IncidentReportArgs(); + IncidentReportArgs(const IncidentReportArgs& that); + virtual ~IncidentReportArgs(); + + virtual status_t writeToParcel(Parcel* out) const; + virtual status_t readFromParcel(const Parcel* in); + + void setAll(bool all); + void setPrivacyPolicy(int privacyPolicy); + void addSection(int section); + void setReceiverPkg(const string& pkg); + void setReceiverCls(const string& cls); + void addHeader(const vector& headerProto); + + inline bool all() const { return mAll; } + bool containsSection(int section, bool specific) const; + inline int getPrivacyPolicy() const { return mPrivacyPolicy; } + inline const set& sections() const { return mSections; } + inline const string& receiverPkg() const { return mReceiverPkg; } + inline const string& receiverCls() const { return mReceiverCls; } + inline const vector>& headers() const { return mHeaders; } + + void merge(const IncidentReportArgs& that); + +private: + set mSections; + vector> mHeaders; + bool mAll; + int mPrivacyPolicy; + string mReceiverPkg; + string mReceiverCls; +}; + +} +} + +#endif // ANDROID_OS_INCIDENT_REPORT_ARGS_H diff --git a/libs/incident/libincident.map.txt b/libs/incident/libincident.map.txt new file mode 100644 index 000000000000..f157763f1a03 --- /dev/null +++ b/libs/incident/libincident.map.txt @@ -0,0 +1,15 @@ +LIBINCIDENT { + global: + AIncidentReportArgs_init; # apex # introduced=30 + AIncidentReportArgs_clone; # apex # introduced=30 + AIncidentReportArgs_delete; # apex # introduced=30 + AIncidentReportArgs_setAll; # apex # introduced=30 + AIncidentReportArgs_setPrivacyPolicy; # apex # introduced=30 + AIncidentReportArgs_addSection; # apex # introduced=30 + AIncidentReportArgs_setReceiverPackage; # apex # introduced=30 + AIncidentReportArgs_setReceiverClass; # apex # introduced=30 + AIncidentReportArgs_addHeader; # apex # introduced=30 + AIncidentReportArgs_takeReport; # apex # introduced=30 + local: + *; +}; diff --git a/libs/incident/src/incident_report.cpp b/libs/incident/src/incident_report.cpp new file mode 100644 index 000000000000..7897ddf6d251 --- /dev/null +++ b/libs/incident/src/incident_report.cpp @@ -0,0 +1,83 @@ +/** + * Copyright (c) 2020, 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 "libincident" + +#include + +#include +#include +#include +#include +#include + +using android::sp; +using android::binder::Status; +using android::os::IncidentReportArgs; +using android::os::IIncidentManager; +using std::string; +using std::vector; + +AIncidentReportArgs* AIncidentReportArgs_init() { + return reinterpret_cast(new IncidentReportArgs()); +} + +AIncidentReportArgs* AIncidentReportArgs_clone(AIncidentReportArgs* that) { + return reinterpret_cast( + new IncidentReportArgs(*reinterpret_cast(that))); +} + +void AIncidentReportArgs_delete(AIncidentReportArgs* args) { + delete reinterpret_cast(args); +} + +void AIncidentReportArgs_setAll(AIncidentReportArgs* args, bool all) { + reinterpret_cast(args)->setAll(all); +} + +void AIncidentReportArgs_setPrivacyPolicy(AIncidentReportArgs* args, int privacyPolicy) { + reinterpret_cast(args)->setPrivacyPolicy(privacyPolicy); +} + +void AIncidentReportArgs_addSection(AIncidentReportArgs* args, int section) { + reinterpret_cast(args)->addSection(section); +} + +void AIncidentReportArgs_setReceiverPackage(AIncidentReportArgs* args, char const* pkg) { + reinterpret_cast(args)->setReceiverPkg(string(pkg)); +} + +void AIncidentReportArgs_setReceiverClass(AIncidentReportArgs* args, char const* cls) { + reinterpret_cast(args)->setReceiverCls(string(cls)); +} + +void AIncidentReportArgs_addHeader(AIncidentReportArgs* args, uint8_t const* buf, size_t size) { + vector vec(buf, buf+size); + reinterpret_cast(args)->addHeader(vec); +} + +int AIncidentReportArgs_takeReport(AIncidentReportArgs* argp) { + IncidentReportArgs* args = reinterpret_cast(argp); + + sp service = android::interface_cast( + android::defaultServiceManager()->getService(android::String16("incident"))); + if (service == nullptr) { + ALOGW("Failed to fetch incident service."); + return false; + } + Status s = service->reportIncident(*args); + return s.transactionError(); +} diff --git a/libs/incident/tests/IncidentReportArgs_test.cpp b/libs/incident/tests/IncidentReportArgs_test.cpp new file mode 100644 index 000000000000..224b343c554a --- /dev/null +++ b/libs/incident/tests/IncidentReportArgs_test.cpp @@ -0,0 +1,74 @@ +// Copyright (C) 2018 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 + +#include + +namespace android { +namespace os { +namespace statsd { + +// Checks that all of the inline methods on IncidentReportRequest and the real C functions +// result in a working IncidentReportArgs. +TEST(IncidentReportArgsTest, testSerialization) { + IncidentReportArgs args; + args.setAll(0); + args.addSection(1000); + args.addSection(1001); + + vector header1; + header1.push_back(0x1); + header1.push_back(0x2); + vector header2; + header1.push_back(0x22); + header1.push_back(0x33); + + args.addHeader(header1); + args.addHeader(header2); + + args.setPrivacyPolicy(1); + + args.setReceiverPkg("com.android.os"); + args.setReceiverCls("com.android.os.Receiver"); + + Parcel out; + status_t err = args.writeToParcel(&out); + EXPECT_EQ(NO_ERROR, err); + + out.setDataPosition(0); + + IncidentReportArgs args2; + err = args2.readFromParcel(&out); + EXPECT_EQ(NO_ERROR, err); + + EXPECT_EQ(0, args2.all()); + set sections; + sections.insert(1000); + sections.insert(1001); + EXPECT_EQ(sections, args2.sections()); + EXPECT_EQ(1, args2.getPrivacyPolicy()); + + EXPECT_EQ(string("com.android.os"), args2.receiverPkg()); + EXPECT_EQ(string("com.android.os.Receiver"), args2.receiverCls()); + + vector> headers; + headers.push_back(header1); + headers.push_back(header2); + EXPECT_EQ(headers, args2.headers()); +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/libs/incident/tests/IncidentReportRequest_test.cpp b/libs/incident/tests/IncidentReportRequest_test.cpp new file mode 100644 index 000000000000..6d218b6682a3 --- /dev/null +++ b/libs/incident/tests/IncidentReportRequest_test.cpp @@ -0,0 +1,65 @@ +// Copyright (C) 2018 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 +#include + +#include + +namespace android { +namespace os { +namespace statsd { + +TEST(IncidentReportRequestTest, testWrite) { + IncidentReportRequest request; + request.setAll(0); + request.addSection(1000); + request.addSection(1001); + + vector header1; + header1.push_back(0x1); + header1.push_back(0x2); + vector header2; + header1.push_back(0x22); + header1.push_back(0x33); + + request.addHeader(header1); + request.addHeader(header2); + + request.setPrivacyPolicy(1); + + request.setReceiverPackage("com.android.os"); + request.setReceiverClass("com.android.os.Receiver"); + + IncidentReportArgs* args = reinterpret_cast(request.getImpl()); + + EXPECT_EQ(0, args->all()); + set sections; + sections.insert(1000); + sections.insert(1001); + EXPECT_EQ(sections, args->sections()); + EXPECT_EQ(1, args->getPrivacyPolicy()); + + EXPECT_EQ(string("com.android.os"), args->receiverPkg()); + EXPECT_EQ(string("com.android.os.Receiver"), args->receiverCls()); + + vector> headers; + headers.push_back(header1); + headers.push_back(header2); + EXPECT_EQ(headers, args->headers()); +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/libs/incident/tests/c_api_compile_test.c b/libs/incident/tests/c_api_compile_test.c new file mode 100644 index 000000000000..e1620dfe3280 --- /dev/null +++ b/libs/incident/tests/c_api_compile_test.c @@ -0,0 +1,11 @@ +#include +#include + +/* + * This file ensures that incident/incident_report.h actually compiles with C, + * since there is no other place in the tree that actually uses it from C. + */ +int not_called() { + return 0; +} + -- cgit v1.2.3 From d1d1aeb46abed2350c9e998a0bb068e2cb7c1ffa Mon Sep 17 00:00:00 2001 From: Joe Onorato Date: Wed, 5 Feb 2020 12:22:11 -0800 Subject: Remove this TEST_MAPPING file until I can figure out what this error means Missing test_suite setting for test: GtsIncidentConfirmationTestCases, test group: presubmit, TEST_MAPPING file path: [frameworks/base/libs/incident] Missing test_suite setting for test: GtsIncidentManagerTestCases, test group: presubmit, TEST_MAPPING file path: [frameworks/base/libs/incident] Test: treehugger Change-Id: I8c3f5a2ff1e22be764ae4cc42223a29f10a3480e --- libs/incident/TEST_MAPPING | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 libs/incident/TEST_MAPPING (limited to 'libs') diff --git a/libs/incident/TEST_MAPPING b/libs/incident/TEST_MAPPING deleted file mode 100644 index b49513543cb1..000000000000 --- a/libs/incident/TEST_MAPPING +++ /dev/null @@ -1,21 +0,0 @@ -{ - "presubmit": [ - { - "name": "libincident_c_api_test" - }, - { - "name": "GtsIncidentConfirmationTestCases" - }, - { - "name": "GtsIncidentManagerTestCases" - } - ], - "postsubmit": [ - ], - "imports": [ - { - "path": "frameworks/base/cmds/incidentd" - } - ] -} - -- cgit v1.2.3 From 1863d94e9a32a210cfb50d7c22bbac30dc33e010 Mon Sep 17 00:00:00 2001 From: Derek Sollenberger Date: Wed, 5 Feb 2020 15:41:51 -0500 Subject: Ensure SkiaPipeline always has a valid colorspace. Previously we didn't assign a colorspace to the pipeline until it was provided a surface to render into. This resulted in undefined behavior if the application attempted to render an offscreen layer before the OS provided the main window with its surface. Now instead of deferring setting whether or not the application is wide gamut we do initialize it to a default setting when the pipeline is created. Bug: 148042673 Test: apct/device_boot_health_check_extra_postsubmit Change-Id: I84d743511e949ac977486470bb14eec936de7f88 --- libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp | 6 ++---- libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h | 2 +- libs/hwui/pipeline/skia/SkiaPipeline.cpp | 2 ++ libs/hwui/pipeline/skia/SkiaPipeline.h | 4 +++- libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp | 5 ++--- libs/hwui/pipeline/skia/SkiaVulkanPipeline.h | 2 +- libs/hwui/renderthread/CanvasContext.cpp | 6 +++--- libs/hwui/renderthread/CanvasContext.h | 1 - libs/hwui/renderthread/IRenderPipeline.h | 4 +++- libs/hwui/tests/unit/SkiaPipelineTests.cpp | 2 +- 10 files changed, 18 insertions(+), 16 deletions(-) (limited to 'libs') diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index e7efe2ff798b..8d5acc631274 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -171,17 +171,15 @@ static void setBufferCount(ANativeWindow* window, uint32_t extraBuffers) { } bool SkiaOpenGLPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior, - ColorMode colorMode, uint32_t extraBuffers) { + uint32_t extraBuffers) { if (mEglSurface != EGL_NO_SURFACE) { mEglManager.destroySurface(mEglSurface); mEglSurface = EGL_NO_SURFACE; } - setSurfaceColorProperties(colorMode); - if (surface) { mRenderThread.requireGlContext(); - auto newSurface = mEglManager.createSurface(surface, colorMode, mSurfaceColorSpace); + auto newSurface = mEglManager.createSurface(surface, mColorMode, mSurfaceColorSpace); if (!newSurface) { return false; } diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h index 3fe0f92b1924..e482cad6c953 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h @@ -44,7 +44,7 @@ public: FrameInfo* currentFrameInfo, bool* requireSwap) override; DeferredLayerUpdater* createTextureLayer() override; bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior, - renderthread::ColorMode colorMode, uint32_t extraBuffers) override; + uint32_t extraBuffers) override; void onStop() override; bool isSurfaceReady() override; bool isContextReady() override; diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 6f4af3d26b3a..29b4dd7f32e7 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -44,6 +44,7 @@ namespace uirenderer { namespace skiapipeline { SkiaPipeline::SkiaPipeline(RenderThread& thread) : mRenderThread(thread) { + setSurfaceColorProperties(mColorMode); } SkiaPipeline::~SkiaPipeline() { @@ -584,6 +585,7 @@ void SkiaPipeline::dumpResourceCacheUsage() const { } void SkiaPipeline::setSurfaceColorProperties(ColorMode colorMode) { + mColorMode = colorMode; if (colorMode == ColorMode::SRGB) { mSurfaceColorType = SkColorType::kN32_SkColorType; mSurfaceColorSpace = SkColorSpace::MakeSRGB(); diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h index af8414de4bc1..8341164edc19 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaPipeline.h @@ -50,6 +50,7 @@ public: bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator, ErrorHandler* errorHandler) override; + void setSurfaceColorProperties(renderthread::ColorMode colorMode) override; SkColorType getSurfaceColorType() const override { return mSurfaceColorType; } sk_sp getSurfaceColorSpace() override { return mSurfaceColorSpace; } @@ -72,9 +73,10 @@ public: protected: void dumpResourceCacheUsage() const; - void setSurfaceColorProperties(renderthread::ColorMode colorMode); renderthread::RenderThread& mRenderThread; + + renderthread::ColorMode mColorMode = renderthread::ColorMode::SRGB; SkColorType mSurfaceColorType; sk_sp mSurfaceColorSpace; diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index ad7c70614239..535a19956e03 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -117,17 +117,16 @@ DeferredLayerUpdater* SkiaVulkanPipeline::createTextureLayer() { void SkiaVulkanPipeline::onStop() {} bool SkiaVulkanPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior, - ColorMode colorMode, uint32_t extraBuffers) { + uint32_t extraBuffers) { if (mVkSurface) { mVkManager.destroySurface(mVkSurface); mVkSurface = nullptr; } - setSurfaceColorProperties(colorMode); if (surface) { mRenderThread.requireVkContext(); mVkSurface = - mVkManager.createSurface(surface, colorMode, mSurfaceColorSpace, mSurfaceColorType, + mVkManager.createSurface(surface, mColorMode, mSurfaceColorSpace, mSurfaceColorType, mRenderThread.getGrContext(), extraBuffers); } diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h index 31734783de7f..c8bf233d8e1c 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h @@ -43,7 +43,7 @@ public: FrameInfo* currentFrameInfo, bool* requireSwap) override; DeferredLayerUpdater* createTextureLayer() override; bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior, - renderthread::ColorMode colorMode, uint32_t extraBuffers) override; + uint32_t extraBuffers) override; void onStop() override; bool isSurfaceReady() override; bool isContextReady() override; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index c1435d1ea2d5..0f3901cb225b 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -161,9 +161,8 @@ void CanvasContext::setSurface(sp&& surface, bool enableTimeout) { mRenderAheadCapacity = mRenderAheadDepth; } - ColorMode colorMode = mWideColorGamut ? ColorMode::WideColorGamut : ColorMode::SRGB; bool hasSurface = mRenderPipeline->setSurface( - mNativeSurface ? mNativeSurface->getNativeWindow() : nullptr, mSwapBehavior, colorMode, + mNativeSurface ? mNativeSurface->getNativeWindow() : nullptr, mSwapBehavior, mRenderAheadCapacity); mFrameNumber = -1; @@ -225,7 +224,8 @@ void CanvasContext::setOpaque(bool opaque) { } void CanvasContext::setWideGamut(bool wideGamut) { - mWideColorGamut = wideGamut; + ColorMode colorMode = wideGamut ? ColorMode::WideColorGamut : ColorMode::SRGB; + mRenderPipeline->setSurfaceColorProperties(colorMode); } bool CanvasContext::makeCurrent() { diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 0967b20e44ee..629c741e8757 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -251,7 +251,6 @@ private: nsecs_t mLastDropVsync = 0; bool mOpaque; - bool mWideColorGamut = false; bool mUseForceDark = false; LightInfo mLightInfo; LightGeometry mLightGeometry = {{0, 0, 0}, 0}; diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h index ef0aa98d4220..ba0d64c5492d 100644 --- a/libs/hwui/renderthread/IRenderPipeline.h +++ b/libs/hwui/renderthread/IRenderPipeline.h @@ -66,7 +66,7 @@ public: virtual bool swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) = 0; virtual DeferredLayerUpdater* createTextureLayer() = 0; - virtual bool setSurface(ANativeWindow* window, SwapBehavior swapBehavior, ColorMode colorMode, + virtual bool setSurface(ANativeWindow* window, SwapBehavior swapBehavior, uint32_t extraBuffers) = 0; virtual void onStop() = 0; virtual bool isSurfaceReady() = 0; @@ -80,6 +80,8 @@ public: virtual bool pinImages(std::vector& mutableImages) = 0; virtual bool pinImages(LsaVector>& images) = 0; virtual void unpinImages() = 0; + + virtual void setSurfaceColorProperties(ColorMode colorMode) = 0; virtual SkColorType getSurfaceColorType() const = 0; virtual sk_sp getSurfaceColorSpace() = 0; virtual GrSurfaceOrigin getSurfaceOrigin() = 0; diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp index 307d13606cb8..90bcd1c0e370 100644 --- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp +++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp @@ -398,7 +398,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, context_lost) { auto surface = context.surface(); auto pipeline = std::make_unique(renderThread); EXPECT_FALSE(pipeline->isSurfaceReady()); - EXPECT_TRUE(pipeline->setSurface(surface.get(), SwapBehavior::kSwap_default, ColorMode::SRGB, 0)); + EXPECT_TRUE(pipeline->setSurface(surface.get(), SwapBehavior::kSwap_default, 0)); EXPECT_TRUE(pipeline->isSurfaceReady()); renderThread.destroyRenderingContext(); EXPECT_FALSE(pipeline->isSurfaceReady()); -- cgit v1.2.3 From ff07c8fd7667aa0b23bf08e30b3bb91538eb8fe0 Mon Sep 17 00:00:00 2001 From: Alec Mouri Date: Wed, 11 Dec 2019 16:32:22 -0800 Subject: [HWUI] use native_window timestamp apis These aren't finalized as stable yet until we sort out some BufferQueue challenges. Bug: 137012798 Test: builds Change-Id: Icd5eb02afc4cf16a2be485e6710f71818b300b9d --- libs/hwui/renderthread/CanvasContext.cpp | 7 ++++--- libs/hwui/renderthread/ReliableSurface.h | 15 --------------- 2 files changed, 4 insertions(+), 18 deletions(-) (limited to 'libs') diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 0b5e005ea19f..aa8bac9362ab 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -174,7 +174,7 @@ void CanvasContext::setSurface(sp&& surface, bool enableTimeout) { // Enable frame stats after the surface has been bound to the appropriate graphics API. // Order is important when new and old surfaces are the same, because old surface has // its frame stats disabled automatically. - mNativeSurface->enableFrameTimestamps(true); + native_window_enable_frame_timestamps(mNativeSurface->getNativeWindow(), true); } else { mRenderThread.removeFrameCallback(this); mGenerationID++; @@ -556,8 +556,9 @@ void CanvasContext::draw() { FrameInfo* forthBehind = mLast4FrameInfos.front().first; int64_t composedFrameId = mLast4FrameInfos.front().second; nsecs_t acquireTime = -1; - mNativeSurface->getFrameTimestamps(composedFrameId, nullptr, &acquireTime, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr); + native_window_get_frame_timestamps(mNativeSurface->getNativeWindow(), composedFrameId, + nullptr, &acquireTime, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr); // Ignore default -1, NATIVE_WINDOW_TIMESTAMP_INVALID and NATIVE_WINDOW_TIMESTAMP_PENDING forthBehind->set(FrameInfoIndex::GpuCompleted) = acquireTime > 0 ? acquireTime : -1; mJankTracker.finishGpuDraw(*forthBehind); diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h index da5097ce33f0..e3cd8c019a23 100644 --- a/libs/hwui/renderthread/ReliableSurface.h +++ b/libs/hwui/renderthread/ReliableSurface.h @@ -49,21 +49,6 @@ public: return ret; } - status_t getFrameTimestamps(uint64_t frameNumber, - nsecs_t* outRequestedPresentTime, nsecs_t* outAcquireTime, - nsecs_t* outLatchTime, nsecs_t* outFirstRefreshStartTime, - nsecs_t* outLastRefreshStartTime, nsecs_t* outGlCompositionDoneTime, - nsecs_t* outDisplayPresentTime, nsecs_t* outDequeueReadyTime, - nsecs_t* outReleaseTime) { - return mSurface->getFrameTimestamps(frameNumber, outRequestedPresentTime, outAcquireTime, - outLatchTime, outFirstRefreshStartTime, outLastRefreshStartTime, - outGlCompositionDoneTime, outDisplayPresentTime, outDequeueReadyTime, outReleaseTime); - } - - void enableFrameTimestamps(bool enable) { - return mSurface->enableFrameTimestamps(enable); - } - private: sp mSurface; -- cgit v1.2.3 From c90438175fcd83c8890c426c28a3ded006faee35 Mon Sep 17 00:00:00 2001 From: Stan Iliev Date: Mon, 3 Feb 2020 16:57:09 -0500 Subject: Refactor GraphicsStatsService for updateability Move GraphicsStatsService to android.graphics package. Move GraphicsStatsService JNI from libservices.core to libandroid_runtime. Declare GraphicsStatsService ctor as the only @SystemApi. Remove MemoryFile usage from GraphicsStatsService, but use SharedMemory and other SDK APIs instead. This is done to avoid using unstable API MemoryFile.getFileDescriptor. Propose new SharedMemory.getFdDup API for next release, which is hidden for now. Refactor statsd puller to avoid proto serialization by moving data directly into AStatsEventList. "libprotoutil" is added as a static dependancy to libhwui, which should be fine because its implementation does not link anything. Bug: 146353313 Test: Ran "adb shell cmd stats pull-source 10068" Test: Passed unit tests and GraphicsStatsValidationTest CTS Change-Id: If16c5addbd519cba33e03bd84ac312595032e0e1 --- libs/hwui/Android.bp | 3 + libs/hwui/service/GraphicsStatsService.cpp | 143 +++++++++++++++-------------- libs/hwui/service/GraphicsStatsService.h | 8 +- 3 files changed, 79 insertions(+), 75 deletions(-) (limited to 'libs') diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 51270f5bcebd..301d1afc6c13 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -92,9 +92,12 @@ cc_defaults { "libandroidfw", "libcrypto", "libsync", + "libstatspull", + "libstatssocket", ], static_libs: [ "libEGL_blobCache", + "libprotoutil", ], }, host: { diff --git a/libs/hwui/service/GraphicsStatsService.cpp b/libs/hwui/service/GraphicsStatsService.cpp index c4186174b637..644d5fbd5bf9 100644 --- a/libs/hwui/service/GraphicsStatsService.cpp +++ b/libs/hwui/service/GraphicsStatsService.cpp @@ -26,9 +26,9 @@ #include #include -#include -#include -#include +#include +#include +#include #include "JankTracker.h" #include "protos/graphicsstats.pb.h" @@ -61,7 +61,7 @@ public: } } bool valid() { return mFd != -1; } - operator int() { return mFd; } // NOLINT(google-explicit-constructor) + operator int() { return mFd; } // NOLINT(google-explicit-constructor) private: int mFd; @@ -485,79 +485,82 @@ void GraphicsStatsService::finishDump(Dump* dump) { delete dump; } -class MemOutputStreamLite : public io::ZeroCopyOutputStream { -public: - explicit MemOutputStreamLite() : mCopyAdapter(), mImpl(&mCopyAdapter) {} - virtual ~MemOutputStreamLite() {} - - virtual bool Next(void** data, int* size) override { return mImpl.Next(data, size); } - - virtual void BackUp(int count) override { mImpl.BackUp(count); } - - virtual int64 ByteCount() const override { return mImpl.ByteCount(); } - - bool Flush() { return mImpl.Flush(); } - - void copyData(const DumpMemoryFn& reader, void* param1, void* param2) { - int bufferOffset = 0; - int totalSize = mCopyAdapter.mBuffersSize - mCopyAdapter.mCurrentBufferUnusedSize; - int totalDataLeft = totalSize; - for (auto& it : mCopyAdapter.mBuffers) { - int bufferSize = std::min(totalDataLeft, (int)it.size()); // last buffer is not full - reader(it.data(), bufferOffset, bufferSize, totalSize, param1, param2); - bufferOffset += bufferSize; - totalDataLeft -= bufferSize; - } - } - -private: - struct MemAdapter : public io::CopyingOutputStream { - // Data is stored in an array of buffers. - // JNI SetByteArrayRegion assembles data in one continuous Java byte[] buffer. - std::vector> mBuffers; - int mBuffersSize = 0; // total bytes allocated in mBuffers - int mCurrentBufferUnusedSize = 0; // unused bytes in the last buffer mBuffers.back() - unsigned char* mCurrentBuffer = nullptr; // pointer to next free byte in mBuffers.back() +using namespace google::protobuf; - explicit MemAdapter() {} - virtual ~MemAdapter() {} +// Field ids taken from FrameTimingHistogram message in atoms.proto +#define TIME_MILLIS_BUCKETS_FIELD_NUMBER 1 +#define FRAME_COUNTS_FIELD_NUMBER 2 + +static void writeCpuHistogram(AStatsEvent* event, + const uirenderer::protos::GraphicsStatsProto& stat) { + util::ProtoOutputStream proto; + for (int bucketIndex = 0; bucketIndex < stat.histogram_size(); bucketIndex++) { + auto& bucket = stat.histogram(bucketIndex); + proto.write(android::util::FIELD_TYPE_INT32 | android::util::FIELD_COUNT_REPEATED | + TIME_MILLIS_BUCKETS_FIELD_NUMBER /* field id */, + (int)bucket.render_millis()); + } + for (int bucketIndex = 0; bucketIndex < stat.histogram_size(); bucketIndex++) { + auto& bucket = stat.histogram(bucketIndex); + proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED | + FRAME_COUNTS_FIELD_NUMBER /* field id */, + (long long)bucket.frame_count()); + } + std::vector outVector; + proto.serializeToVector(&outVector); + AStatsEvent_writeByteArray(event, outVector.data(), outVector.size()); +} - virtual bool Write(const void* buffer, int size) override { - while (size > 0) { - if (0 == mCurrentBufferUnusedSize) { - mCurrentBufferUnusedSize = - std::max(size, mBuffersSize ? 2 * mBuffersSize : 10000); - mBuffers.emplace_back(); - mBuffers.back().resize(mCurrentBufferUnusedSize); - mCurrentBuffer = mBuffers.back().data(); - mBuffersSize += mCurrentBufferUnusedSize; - } - int dataMoved = std::min(mCurrentBufferUnusedSize, size); - memcpy(mCurrentBuffer, buffer, dataMoved); - mCurrentBufferUnusedSize -= dataMoved; - mCurrentBuffer += dataMoved; - buffer = reinterpret_cast(buffer) + dataMoved; - size -= dataMoved; - } - return true; - } - }; +static void writeGpuHistogram(AStatsEvent* event, + const uirenderer::protos::GraphicsStatsProto& stat) { + util::ProtoOutputStream proto; + for (int bucketIndex = 0; bucketIndex < stat.gpu_histogram_size(); bucketIndex++) { + auto& bucket = stat.gpu_histogram(bucketIndex); + proto.write(android::util::FIELD_TYPE_INT32 | android::util::FIELD_COUNT_REPEATED | + TIME_MILLIS_BUCKETS_FIELD_NUMBER /* field id */, + (int)bucket.render_millis()); + } + for (int bucketIndex = 0; bucketIndex < stat.gpu_histogram_size(); bucketIndex++) { + auto& bucket = stat.gpu_histogram(bucketIndex); + proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED | + FRAME_COUNTS_FIELD_NUMBER /* field id */, + (long long)bucket.frame_count()); + } + std::vector outVector; + proto.serializeToVector(&outVector); + AStatsEvent_writeByteArray(event, outVector.data(), outVector.size()); +} - MemOutputStreamLite::MemAdapter mCopyAdapter; - io::CopyingOutputStreamAdaptor mImpl; -}; -void GraphicsStatsService::finishDumpInMemory(Dump* dump, const DumpMemoryFn& reader, void* param1, - void* param2) { - MemOutputStreamLite stream; +void GraphicsStatsService::finishDumpInMemory(Dump* dump, AStatsEventList* data, + bool lastFullDay) { dump->updateProto(); - bool success = dump->proto().SerializeToZeroCopyStream(&stream) && stream.Flush(); - delete dump; - if (!success) { - return; + auto& serviceDump = dump->proto(); + for (int stat_index = 0; stat_index < serviceDump.stats_size(); stat_index++) { + auto& stat = serviceDump.stats(stat_index); + AStatsEvent* event = AStatsEventList_addStatsEvent(data); + AStatsEvent_setAtomId(event, android::util::GRAPHICS_STATS); + AStatsEvent_writeString(event, stat.package_name().c_str()); + AStatsEvent_writeInt64(event, (int64_t)stat.version_code()); + AStatsEvent_writeInt64(event, (int64_t)stat.stats_start()); + AStatsEvent_writeInt64(event, (int64_t)stat.stats_end()); + AStatsEvent_writeInt32(event, (int32_t)stat.pipeline()); + AStatsEvent_writeInt32(event, (int32_t)stat.summary().total_frames()); + AStatsEvent_writeInt32(event, (int32_t)stat.summary().missed_vsync_count()); + AStatsEvent_writeInt32(event, (int32_t)stat.summary().high_input_latency_count()); + AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_ui_thread_count()); + AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_bitmap_upload_count()); + AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_draw_count()); + AStatsEvent_writeInt32(event, (int32_t)stat.summary().missed_deadline_count()); + writeCpuHistogram(event, stat); + writeGpuHistogram(event, stat); + // TODO: fill in UI mainline module version, when the feature is available. + AStatsEvent_writeInt64(event, (int64_t)0); + AStatsEvent_writeBool(event, !lastFullDay); + AStatsEvent_build(event); } - stream.copyData(reader, param1, param2); } + } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/service/GraphicsStatsService.h b/libs/hwui/service/GraphicsStatsService.h index 4bed96330a52..59e21d039c9d 100644 --- a/libs/hwui/service/GraphicsStatsService.h +++ b/libs/hwui/service/GraphicsStatsService.h @@ -20,6 +20,7 @@ #include "JankTracker.h" #include "utils/Macros.h" +#include namespace android { namespace uirenderer { @@ -27,9 +28,6 @@ namespace protos { class GraphicsStatsProto; } -typedef void (*DumpMemoryFn)(void* buffer, int bufferOffset, int bufferSize, int totalSize, - void* param1, void* param2); - /* * The exported entry points used by GraphicsStatsService.java in f/b/services/core * @@ -56,8 +54,8 @@ public: int64_t startTime, int64_t endTime, const ProfileData* data); ANDROID_API static void addToDump(Dump* dump, const std::string& path); ANDROID_API static void finishDump(Dump* dump); - ANDROID_API static void finishDumpInMemory(Dump* dump, const DumpMemoryFn& reader, void* param1, - void* param2); + ANDROID_API static void finishDumpInMemory(Dump* dump, AStatsEventList* data, + bool lastFullDay); // Visible for testing static bool parseFromFile(const std::string& path, protos::GraphicsStatsProto* output); -- cgit v1.2.3 From 0865f2e7b22ceab8d1cc28092b8e361990882c78 Mon Sep 17 00:00:00 2001 From: Tim Murray Date: Tue, 4 Feb 2020 22:23:16 -0800 Subject: hwui: remove FatVector FatVector now lives in fw/native/libs/ui. Test: builds, boots, works Bug: 149096186 Change-Id: I09dc2fddd2a238ef47d7853b64b0bfbf74d3f71f --- libs/hwui/RenderNode.cpp | 2 +- libs/hwui/RenderNode.h | 3 +- libs/hwui/pipeline/skia/ReorderBarrierDrawables.h | 2 +- libs/hwui/renderthread/RenderThread.cpp | 3 +- libs/hwui/renderthread/VulkanManager.cpp | 2 +- libs/hwui/tests/unit/FatVectorTests.cpp | 2 +- libs/hwui/utils/FatVector.h | 105 ---------------------- 7 files changed, 8 insertions(+), 111 deletions(-) delete mode 100644 libs/hwui/utils/FatVector.h (limited to 'libs') diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index 6761435a8171..31e45558139d 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -27,7 +27,6 @@ #include "DamageAccumulator.h" #include "pipeline/skia/SkiaDisplayList.h" #endif -#include "utils/FatVector.h" #include "utils/MathUtils.h" #include "utils/StringUtils.h" #include "utils/TraceUtils.h" @@ -37,6 +36,7 @@ #include #include #include +#include namespace android { namespace uirenderer { diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index d55e5b0ce836..c0ec2174bb35 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -27,6 +27,8 @@ #include +#include + #include "AnimatorManager.h" #include "CanvasTransform.h" #include "Debug.h" @@ -35,7 +37,6 @@ #include "RenderProperties.h" #include "pipeline/skia/SkiaDisplayList.h" #include "pipeline/skia/SkiaLayer.h" -#include "utils/FatVector.h" #include diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h index cfc0f9b258da..d669f84c5ee2 100644 --- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h +++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h @@ -21,7 +21,7 @@ #include #include -#include +#include namespace android { namespace uirenderer { diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index cae3e3b5188c..206b58f62ea7 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -27,7 +27,6 @@ #include "pipeline/skia/SkiaOpenGLPipeline.h" #include "pipeline/skia/SkiaVulkanPipeline.h" #include "renderstate/RenderState.h" -#include "utils/FatVector.h" #include "utils/TimeUtils.h" #include "utils/TraceUtils.h" @@ -40,6 +39,8 @@ #include #include +#include + namespace android { namespace uirenderer { namespace renderthread { diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index a5355fc3499d..ba70afc8b8d2 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -23,13 +23,13 @@ #include #include #include +#include #include #include #include "Properties.h" #include "RenderThread.h" #include "renderstate/RenderState.h" -#include "utils/FatVector.h" #include "utils/TraceUtils.h" namespace android { diff --git a/libs/hwui/tests/unit/FatVectorTests.cpp b/libs/hwui/tests/unit/FatVectorTests.cpp index 8523e6c9e973..6585a6249b44 100644 --- a/libs/hwui/tests/unit/FatVectorTests.cpp +++ b/libs/hwui/tests/unit/FatVectorTests.cpp @@ -15,7 +15,7 @@ */ #include -#include +#include #include diff --git a/libs/hwui/utils/FatVector.h b/libs/hwui/utils/FatVector.h deleted file mode 100644 index 8cc4d1010ab6..000000000000 --- a/libs/hwui/utils/FatVector.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2015, 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_FAT_VECTOR_H -#define ANDROID_FAT_VECTOR_H - -#include "utils/Macros.h" - -#include -#include -#include -#include - -#include - -namespace android { -namespace uirenderer { - -template -class InlineStdAllocator { -public: - struct Allocation { - PREVENT_COPY_AND_ASSIGN(Allocation); - - public: - Allocation(){}; - // char array instead of T array, so memory is uninitialized, with no destructors run - char array[sizeof(T) * SIZE]; - bool inUse = false; - }; - - typedef T value_type; // needed to implement std::allocator - typedef T* pointer; // needed to implement std::allocator - - explicit InlineStdAllocator(Allocation& allocation) : mAllocation(allocation) {} - InlineStdAllocator(const InlineStdAllocator& other) : mAllocation(other.mAllocation) {} - ~InlineStdAllocator() {} - - T* allocate(size_t num, const void* = 0) { - if (!mAllocation.inUse && num <= SIZE) { - mAllocation.inUse = true; - return (T*)mAllocation.array; - } else { - return (T*)malloc(num * sizeof(T)); - } - } - - void deallocate(pointer p, size_t num) { - if (p == (T*)mAllocation.array) { - mAllocation.inUse = false; - } else { - // 'free' instead of delete here - destruction handled separately - free(p); - } - } - Allocation& mAllocation; -}; - -/** - * std::vector with SIZE elements preallocated into an internal buffer. - * - * Useful for avoiding the cost of malloc in cases where only SIZE or - * fewer elements are needed in the common case. - */ -template -class FatVector : public std::vector> { -public: - FatVector() - : std::vector>( - InlineStdAllocator(mAllocation)) { - this->reserve(SIZE); - } - - explicit FatVector(size_t capacity) : FatVector() { this->resize(capacity); } - -private: - typename InlineStdAllocator::Allocation mAllocation; -}; - -} // namespace uirenderer -} // namespace android - -#endif // ANDROID_FAT_VECTOR_H -- cgit v1.2.3 From be845950a25620eca1d8a3a6cba48c38e5e5e8c4 Mon Sep 17 00:00:00 2001 From: Prabir Pradhan Date: Fri, 31 Jan 2020 17:42:34 -0800 Subject: PointerController: Add guards to ensure display is valid This change makes it so that PointerController does not ask its Policy to load any resources for any displays until a DisplayViewport is set, and verifies this with unit tests. Bug: 145699789 Bug: 146385350 Test: atest libinputservice_test Change-Id: I2e48e7ac4700e6f9fdf939a7bd0e6639b051ade6 --- libs/input/PointerController.cpp | 33 +++++++++++++----- libs/input/tests/PointerController_test.cpp | 54 +++++++++++++++++++++++++---- 2 files changed, 72 insertions(+), 15 deletions(-) (limited to 'libs') diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp index e4348f2a9b21..3b494e9129db 100644 --- a/libs/input/PointerController.cpp +++ b/libs/input/PointerController.cpp @@ -251,19 +251,24 @@ void PointerController::unfade(Transition transition) { void PointerController::setPresentation(Presentation presentation) { AutoMutex _l(mLock); - if (presentation == PRESENTATION_POINTER && mLocked.additionalMouseResources.empty()) { - mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources, - &mLocked.animationResources, mLocked.viewport.displayId); + if (mLocked.presentation == presentation) { + return; } - if (mLocked.presentation != presentation) { - mLocked.presentation = presentation; - mLocked.presentationChanged = true; + mLocked.presentation = presentation; + mLocked.presentationChanged = true; - if (presentation != PRESENTATION_SPOT) { - fadeOutAndReleaseAllSpotsLocked(); - } + if (!mLocked.viewport.isValid()) { + return; + } + if (presentation == PRESENTATION_POINTER) { + if (mLocked.additionalMouseResources.empty()) { + mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources, + &mLocked.animationResources, + mLocked.viewport.displayId); + } + fadeOutAndReleaseAllSpotsLocked(); updatePointerLocked(); } } @@ -285,6 +290,9 @@ void PointerController::setSpots(const PointerCoords* spotCoords, #endif AutoMutex _l(mLock); + if (!mLocked.viewport.isValid()) { + return; + } std::vector newSpots; std::map>::const_iterator iter = @@ -331,6 +339,9 @@ void PointerController::clearSpots() { #endif AutoMutex _l(mLock); + if (!mLocked.viewport.isValid()) { + return; + } fadeOutAndReleaseAllSpotsLocked(); } @@ -752,6 +763,10 @@ void PointerController::fadeOutAndReleaseAllSpotsLocked() { } void PointerController::loadResourcesLocked() REQUIRES(mLock) { + if (!mLocked.viewport.isValid()) { + return; + } + mPolicy->loadPointerResources(&mResources, mLocked.viewport.displayId); mPolicy->loadPointerIcon(&mLocked.pointerIcon, mLocked.viewport.displayId); diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp index b36406d6a703..a15742671dc7 100644 --- a/libs/input/tests/PointerController_test.cpp +++ b/libs/input/tests/PointerController_test.cpp @@ -39,8 +39,8 @@ enum TestCursorType { using ::testing::AllOf; using ::testing::Field; -using ::testing::NiceMock; using ::testing::Mock; +using ::testing::NiceMock; using ::testing::Return; using ::testing::Test; @@ -57,12 +57,20 @@ public: virtual int32_t getDefaultPointerIconId() override; virtual int32_t getCustomPointerIconId() override; + bool allResourcesAreLoaded(); + bool noResourcesAreLoaded(); + private: void loadPointerIconForType(SpriteIcon* icon, int32_t cursorType); + + bool pointerIconLoaded{false}; + bool pointerResourcesLoaded{false}; + bool additionalMouseResourcesLoaded{false}; }; void MockPointerControllerPolicyInterface::loadPointerIcon(SpriteIcon* icon, int32_t) { loadPointerIconForType(icon, CURSOR_TYPE_DEFAULT); + pointerIconLoaded = true; } void MockPointerControllerPolicyInterface::loadPointerResources(PointerResources* outResources, @@ -70,6 +78,7 @@ void MockPointerControllerPolicyInterface::loadPointerResources(PointerResources loadPointerIconForType(&outResources->spotHover, CURSOR_TYPE_HOVER); loadPointerIconForType(&outResources->spotTouch, CURSOR_TYPE_TOUCH); loadPointerIconForType(&outResources->spotAnchor, CURSOR_TYPE_ANCHOR); + pointerResourcesLoaded = true; } void MockPointerControllerPolicyInterface::loadAdditionalMouseResources( @@ -91,6 +100,8 @@ void MockPointerControllerPolicyInterface::loadAdditionalMouseResources( anim.durationPerFrame = 10; (*outResources)[cursorType] = icon; (*outAnimationResources)[cursorType] = anim; + + additionalMouseResourcesLoaded = true; } int32_t MockPointerControllerPolicyInterface::getDefaultPointerIconId() { @@ -101,18 +112,27 @@ int32_t MockPointerControllerPolicyInterface::getCustomPointerIconId() { return CURSOR_TYPE_CUSTOM; } +bool MockPointerControllerPolicyInterface::allResourcesAreLoaded() { + return pointerIconLoaded && pointerResourcesLoaded && additionalMouseResourcesLoaded; +} + +bool MockPointerControllerPolicyInterface::noResourcesAreLoaded() { + return !(pointerIconLoaded || pointerResourcesLoaded || additionalMouseResourcesLoaded); +} + void MockPointerControllerPolicyInterface::loadPointerIconForType(SpriteIcon* icon, int32_t type) { icon->style = type; std::pair hotSpot = getHotSpotCoordinatesForType(type); icon->hotSpotX = hotSpot.first; icon->hotSpotY = hotSpot.second; } - class PointerControllerTest : public Test { protected: PointerControllerTest(); ~PointerControllerTest(); + void ensureDisplayViewportIsSet(); + sp mPointerSprite; sp mPolicy; sp mSpriteController; @@ -141,7 +161,14 @@ PointerControllerTest::PointerControllerTest() : mPointerSprite(new NiceMocksetDisplayViewport(viewport); -} -PointerControllerTest::~PointerControllerTest() { - mRunning.store(false, std::memory_order_relaxed); - mThread.join(); + // The first call to setDisplayViewport should trigger the loading of the necessary resources. + EXPECT_TRUE(mPolicy->allResourcesAreLoaded()); } void PointerControllerTest::loopThread() { @@ -167,6 +192,7 @@ void PointerControllerTest::loopThread() { } TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) { + ensureDisplayViewportIsSet(); mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE); std::pair hotspot = getHotSpotCoordinatesForType(CURSOR_TYPE_DEFAULT); @@ -181,6 +207,7 @@ TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) { } TEST_F(PointerControllerTest, updatePointerIcon) { + ensureDisplayViewportIsSet(); mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE); int32_t type = CURSOR_TYPE_ADDITIONAL; @@ -196,6 +223,7 @@ TEST_F(PointerControllerTest, updatePointerIcon) { } TEST_F(PointerControllerTest, setCustomPointerIcon) { + ensureDisplayViewportIsSet(); mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE); int32_t style = CURSOR_TYPE_CUSTOM; @@ -217,4 +245,18 @@ TEST_F(PointerControllerTest, setCustomPointerIcon) { mPointerController->setCustomPointerIcon(icon); } +TEST_F(PointerControllerTest, doesNotGetResourcesBeforeSettingViewport) { + mPointerController->setPresentation(PointerController::PRESENTATION_POINTER); + mPointerController->setSpots(nullptr, nullptr, BitSet32(), -1); + mPointerController->clearSpots(); + mPointerController->setPosition(1.0f, 1.0f); + mPointerController->move(1.0f, 1.0f); + mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE); + mPointerController->fade(PointerController::TRANSITION_IMMEDIATE); + + EXPECT_TRUE(mPolicy->noResourcesAreLoaded()); + + ensureDisplayViewportIsSet(); +} + } // namespace android -- cgit v1.2.3 From 56da4c3862f5fdf08cbdd3c0f6ebf021f0281385 Mon Sep 17 00:00:00 2001 From: Ruchir Rastogi Date: Mon, 3 Feb 2020 18:53:24 -0800 Subject: Use StatsDimensionsValueParcel within statsd apex Because statsd now uses StatsDimensionsValueParcel instead of StatsDimensionsValue.h/c, statsd no longer has to depend on libservices. Test: m -j Test: atest StatsdHostTestCases#testBroadcastSubscriber Bug: 148604617 Change-Id: I6d65383ccec99f4672d6575232981c0f6cc40fcf --- libs/services/Android.bp | 1 - .../include/android/os/StatsDimensionsValue.h | 70 ------------ libs/services/src/os/StatsDimensionsValue.cpp | 126 --------------------- 3 files changed, 197 deletions(-) delete mode 100644 libs/services/include/android/os/StatsDimensionsValue.h delete mode 100644 libs/services/src/os/StatsDimensionsValue.cpp (limited to 'libs') diff --git a/libs/services/Android.bp b/libs/services/Android.bp index 9b047ca22d19..1e621079b532 100644 --- a/libs/services/Android.bp +++ b/libs/services/Android.bp @@ -20,7 +20,6 @@ cc_library_shared { ":IDropBoxManagerService.aidl", "src/content/ComponentName.cpp", "src/os/DropBoxManager.cpp", - "src/os/StatsDimensionsValue.cpp", ], shared_libs: [ diff --git a/libs/services/include/android/os/StatsDimensionsValue.h b/libs/services/include/android/os/StatsDimensionsValue.h deleted file mode 100644 index cc0b05644f2c..000000000000 --- a/libs/services/include/android/os/StatsDimensionsValue.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2018 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 STATS_DIMENSIONS_VALUE_H -#define STATS_DIMENSIONS_VALUE_H - -#include -#include -#include -#include -#include - -namespace android { -namespace os { - -// Represents a parcelable object. Used to send data from statsd to StatsCompanionService.java. -class StatsDimensionsValue : public android::Parcelable { -public: - StatsDimensionsValue(); - - StatsDimensionsValue(int32_t field, String16 value); - StatsDimensionsValue(int32_t field, int32_t value); - StatsDimensionsValue(int32_t field, int64_t value); - StatsDimensionsValue(int32_t field, bool value); - StatsDimensionsValue(int32_t field, float value); - StatsDimensionsValue(int32_t field, std::vector value); - - virtual ~StatsDimensionsValue(); - - virtual android::status_t writeToParcel(android::Parcel* out) const override; - virtual android::status_t readFromParcel(const android::Parcel* in) override; - -private: - // Keep constants in sync with android/os/StatsDimensionsValue.java - // and stats_log.proto's DimensionValue. - static const int kStrValueType = 2; - static const int kIntValueType = 3; - static const int kLongValueType = 4; - static const int kBoolValueType = 5; - static const int kFloatValueType = 6; - static const int kTupleValueType = 7; - - int32_t mField; - int32_t mValueType; - - // This isn't very clever, but it isn't used for long-term storage, so it'll do. - String16 mStrValue; - int32_t mIntValue; - int64_t mLongValue; - bool mBoolValue; - float mFloatValue; - std::vector mTupleValue; -}; - -} // namespace os -} // namespace android - -#endif // STATS_DIMENSIONS_VALUE_H diff --git a/libs/services/src/os/StatsDimensionsValue.cpp b/libs/services/src/os/StatsDimensionsValue.cpp deleted file mode 100644 index 0052e0baa905..000000000000 --- a/libs/services/src/os/StatsDimensionsValue.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) 2018 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 "StatsDimensionsValue" - -#include "android/os/StatsDimensionsValue.h" - -#include - -using android::Parcel; -using android::Parcelable; -using android::status_t; -using std::vector; - -namespace android { -namespace os { - -StatsDimensionsValue::StatsDimensionsValue() {}; - -StatsDimensionsValue::StatsDimensionsValue(int32_t field, String16 value) : - mField(field), - mValueType(kStrValueType), - mStrValue(value) { -} -StatsDimensionsValue::StatsDimensionsValue(int32_t field, int32_t value) : - mField(field), - mValueType(kIntValueType), - mIntValue(value) { -} -StatsDimensionsValue::StatsDimensionsValue(int32_t field, int64_t value) : - mField(field), - mValueType(kLongValueType), - mLongValue(value) { -} -StatsDimensionsValue::StatsDimensionsValue(int32_t field, bool value) : - mField(field), - mValueType(kBoolValueType), - mBoolValue(value) { -} -StatsDimensionsValue::StatsDimensionsValue(int32_t field, float value) : - mField(field), - mValueType(kFloatValueType), - mFloatValue(value) { -} -StatsDimensionsValue::StatsDimensionsValue(int32_t field, vector value) : - mField(field), - mValueType(kTupleValueType), - mTupleValue(value) { -} - -StatsDimensionsValue::~StatsDimensionsValue() {} - -status_t -StatsDimensionsValue::writeToParcel(Parcel* out) const { - status_t err ; - - err = out->writeInt32(mField); - if (err != NO_ERROR) { - return err; - } - err = out->writeInt32(mValueType); - if (err != NO_ERROR) { - return err; - } - switch (mValueType) { - case kStrValueType: - err = out->writeString16(mStrValue); - break; - case kIntValueType: - err = out->writeInt32(mIntValue); - break; - case kLongValueType: - err = out->writeInt64(mLongValue); - break; - case kBoolValueType: - err = out->writeBool(mBoolValue); - break; - case kFloatValueType: - err = out->writeFloat(mFloatValue); - break; - case kTupleValueType: - { - int sz = mTupleValue.size(); - err = out->writeInt32(sz); - if (err != NO_ERROR) { - return err; - } - for (int i = 0; i < sz; ++i) { - err = mTupleValue[i].writeToParcel(out); - if (err != NO_ERROR) { - return err; - } - } - } - break; - default: - err = UNKNOWN_ERROR; - break; - } - return err; -} - -status_t -StatsDimensionsValue::readFromParcel(const Parcel* in) -{ - // Implement me if desired. We don't currently use this. - ALOGE("Cannot do c++ StatsDimensionsValue.readFromParcel(); it is not implemented."); - (void)in; // To prevent compile error of unused parameter 'in' - return UNKNOWN_ERROR; -} - -} // namespace os -} // namespace android -- cgit v1.2.3 From 155d539634571bcd97f07884f28c61ef18b863c2 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Mon, 10 Feb 2020 13:35:24 -0800 Subject: Sort bag by attribute key when using libs When shared libraries are assigned package ids in a different order than compile order, bag resources that use attributes from both multiple libraries will not be sorted in ascending attribute id order. This change detects when the attribute ids are not in order and sorts the bag entries accordingly. The change is designed to be less invasive. Deduping the GetBag logic should probably be spun off in a separate bug. Bug: 147674078 Test: libandroidfw_tests Change-Id: Id8ce8e9c7ef294fcc312b77468136067d392dbd0 --- libs/androidfw/AssetManager2.cpp | 29 ++++++++++++++++++--- libs/androidfw/tests/AssetManager2_test.cpp | 21 +++++++++++++++ libs/androidfw/tests/data/lib_two/R.h | 12 ++++++--- libs/androidfw/tests/data/lib_two/lib_two.apk | Bin 1426 -> 1586 bytes .../tests/data/lib_two/res/values/values.xml | 11 +++++--- libs/androidfw/tests/data/libclient/R.h | 1 + libs/androidfw/tests/data/libclient/libclient.apk | Bin 1982 -> 2168 bytes .../tests/data/libclient/res/values/values.xml | 6 +++++ 8 files changed, 70 insertions(+), 10 deletions(-) (limited to 'libs') diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 8cfd2d8ca696..32086625a726 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -992,6 +992,11 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { return bag; } +static bool compare_bag_entries(const ResolvedBag::Entry& entry1, + const ResolvedBag::Entry& entry2) { + return entry1.key < entry2.key; +} + const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector& child_resids) { auto cached_iter = cached_bags_.find(resid); if (cached_iter != cached_bags_.end()) { @@ -1027,13 +1032,15 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector& child_resids.push_back(resid); uint32_t parent_resid = dtohl(map->parent.ident); - if (parent_resid == 0 || std::find(child_resids.begin(), child_resids.end(), parent_resid) + if (parent_resid == 0U || std::find(child_resids.begin(), child_resids.end(), parent_resid) != child_resids.end()) { - // There is no parent or that a circular dependency exist, meaning there is nothing to - // inherit and we can do a simple copy of the entries in the map. + // There is no parent or a circular dependency exist, meaning there is nothing to inherit and + // we can do a simple copy of the entries in the map. const size_t entry_count = map_entry_end - map_entry; util::unique_cptr new_bag{reinterpret_cast( malloc(sizeof(ResolvedBag) + (entry_count * sizeof(ResolvedBag::Entry))))}; + + bool sort_entries = false; ResolvedBag::Entry* new_entry = new_bag->entries; for (; map_entry != map_entry_end; ++map_entry) { uint32_t new_key = dtohl(map_entry->name.ident); @@ -1059,8 +1066,15 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector& new_entry->value.data, new_key); return nullptr; } + sort_entries = sort_entries || + (new_entry != new_bag->entries && (new_entry->key < (new_entry - 1U)->key)); ++new_entry; } + + if (sort_entries) { + std::sort(new_bag->entries, new_bag->entries + entry_count, compare_bag_entries); + } + new_bag->type_spec_flags = entry.type_flags; new_bag->entry_count = static_cast(entry_count); ResolvedBag* result = new_bag.get(); @@ -1091,6 +1105,7 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector& const ResolvedBag::Entry* const parent_entry_end = parent_entry + parent_bag->entry_count; // The keys are expected to be in sorted order. Merge the two bags. + bool sort_entries = false; while (map_entry != map_entry_end && parent_entry != parent_entry_end) { uint32_t child_key = dtohl(map_entry->name.ident); if (!is_internal_resid(child_key)) { @@ -1123,6 +1138,8 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector& memcpy(new_entry, parent_entry, sizeof(*new_entry)); } + sort_entries = sort_entries || + (new_entry != new_bag->entries && (new_entry->key < (new_entry - 1U)->key)); if (child_key >= parent_entry->key) { // Move to the next parent entry if we used it or it was overridden. ++parent_entry; @@ -1153,6 +1170,8 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector& new_entry->value.dataType, new_entry->value.data, new_key); return nullptr; } + sort_entries = sort_entries || + (new_entry != new_bag->entries && (new_entry->key < (new_entry - 1U)->key)); ++map_entry; ++new_entry; } @@ -1172,6 +1191,10 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector& new_bag.release(), sizeof(ResolvedBag) + (actual_count * sizeof(ResolvedBag::Entry))))); } + if (sort_entries) { + std::sort(new_bag->entries, new_bag->entries + actual_count, compare_bag_entries); + } + // Combine flags from the parent and our own bag. new_bag->type_spec_flags = entry.type_flags | parent_bag->type_spec_flags; new_bag->entry_count = static_cast(actual_count); diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index 2f6f3dfcaf1c..35fea7ab86cb 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -285,6 +285,27 @@ TEST_F(AssetManager2Test, FindsBagResourceFromSharedLibrary) { EXPECT_EQ(0x03, get_package_id(bag->entries[1].key)); } +TEST_F(AssetManager2Test, FindsBagResourceFromMultipleSharedLibraries) { + AssetManager2 assetmanager; + + // libclient is built with lib_one and then lib_two in order. + // Reverse the order to test that proper package ID re-assignment is happening. + assetmanager.SetApkAssets( + {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()}); + + const ResolvedBag* bag = assetmanager.GetBag(libclient::R::style::ThemeMultiLib); + ASSERT_NE(nullptr, bag); + ASSERT_EQ(bag->entry_count, 2u); + + // First attribute comes from lib_two. + EXPECT_EQ(2, bag->entries[0].cookie); + EXPECT_EQ(0x02, get_package_id(bag->entries[0].key)); + + // The next two attributes come from lib_one. + EXPECT_EQ(2, bag->entries[1].cookie); + EXPECT_EQ(0x03, get_package_id(bag->entries[1].key)); +} + TEST_F(AssetManager2Test, FindsStyleResourceWithParentFromSharedLibrary) { AssetManager2 assetmanager; diff --git a/libs/androidfw/tests/data/lib_two/R.h b/libs/androidfw/tests/data/lib_two/R.h index 92b9cc10e7a8..fd5a910961cb 100644 --- a/libs/androidfw/tests/data/lib_two/R.h +++ b/libs/androidfw/tests/data/lib_two/R.h @@ -30,16 +30,22 @@ struct R { }; }; + struct integer { + enum : uint32_t { + bar = 0x02020000, // default + }; + }; + struct string { enum : uint32_t { - LibraryString = 0x02020000, // default - foo = 0x02020001, // default + LibraryString = 0x02030000, // default + foo = 0x02030001, // default }; }; struct style { enum : uint32_t { - Theme = 0x02030000, // default + Theme = 0x02040000, // default }; }; }; diff --git a/libs/androidfw/tests/data/lib_two/lib_two.apk b/libs/androidfw/tests/data/lib_two/lib_two.apk index 486c23000276..8193db637eed 100644 Binary files a/libs/androidfw/tests/data/lib_two/lib_two.apk and b/libs/androidfw/tests/data/lib_two/lib_two.apk differ diff --git a/libs/androidfw/tests/data/lib_two/res/values/values.xml b/libs/androidfw/tests/data/lib_two/res/values/values.xml index 340d14c34c5d..4e1d69aa5a5a 100644 --- a/libs/androidfw/tests/data/lib_two/res/values/values.xml +++ b/libs/androidfw/tests/data/lib_two/res/values/values.xml @@ -18,14 +18,17 @@ - + + 1337 + + Hi from library two - + Foo from lib_two - + diff --git a/libs/androidfw/tests/data/libclient/R.h b/libs/androidfw/tests/data/libclient/R.h index 43d1f9bb68e7..e21b3ebae826 100644 --- a/libs/androidfw/tests/data/libclient/R.h +++ b/libs/androidfw/tests/data/libclient/R.h @@ -34,6 +34,7 @@ struct R { struct style { enum : uint32_t { Theme = 0x7f020000, // default + ThemeMultiLib = 0x7f020001, // default }; }; diff --git a/libs/androidfw/tests/data/libclient/libclient.apk b/libs/androidfw/tests/data/libclient/libclient.apk index 17990248e862..4b9a8833c64a 100644 Binary files a/libs/androidfw/tests/data/libclient/libclient.apk and b/libs/androidfw/tests/data/libclient/libclient.apk differ diff --git a/libs/androidfw/tests/data/libclient/res/values/values.xml b/libs/androidfw/tests/data/libclient/res/values/values.xml index fead7c323767..a29f473e3c17 100644 --- a/libs/androidfw/tests/data/libclient/res/values/values.xml +++ b/libs/androidfw/tests/data/libclient/res/values/values.xml @@ -27,6 +27,12 @@ @com.android.lib_one:string/foo + + + @com.android.lib_one:string/foo -- cgit v1.2.3 From 8a82b141d1fda246a9353f04a1af09144334e451 Mon Sep 17 00:00:00 2001 From: Alec Mouri Date: Tue, 17 Dec 2019 09:41:48 -0800 Subject: [HWUI] use ANativeWindow_getLastQueuedBuffer api Bug: 137012798 Test: builds Change-Id: Ic33a21a73b0579726f47c53cc102fb91b5ead0d6 --- libs/hwui/Readback.cpp | 47 ++++++++++++++++++---------------- libs/hwui/Readback.h | 2 +- libs/hwui/renderthread/RenderProxy.cpp | 3 ++- libs/hwui/renderthread/RenderProxy.h | 4 +++ 4 files changed, 32 insertions(+), 24 deletions(-) (limited to 'libs') diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp index 89a9b997af97..84c07d7d9dff 100644 --- a/libs/hwui/Readback.cpp +++ b/libs/hwui/Readback.cpp @@ -16,16 +16,16 @@ #include "Readback.h" -#include "pipeline/skia/LayerDrawable.h" -#include "renderthread/EglManager.h" -#include "renderthread/VulkanManager.h" - -#include -#include +#include +#include #include + #include "DeferredLayerUpdater.h" #include "Properties.h" #include "hwui/Bitmap.h" +#include "pipeline/skia/LayerDrawable.h" +#include "renderthread/EglManager.h" +#include "renderthread/VulkanManager.h" #include "utils/Color.h" #include "utils/MathUtils.h" #include "utils/TraceUtils.h" @@ -35,40 +35,43 @@ using namespace android::uirenderer::renderthread; namespace android { namespace uirenderer { -CopyResult Readback::copySurfaceInto(Surface& surface, const Rect& srcRect, SkBitmap* bitmap) { +CopyResult Readback::copySurfaceInto(ANativeWindow* window, const Rect& srcRect, SkBitmap* bitmap) { ATRACE_CALL(); // Setup the source - sp sourceBuffer; - sp sourceFence; + AHardwareBuffer* rawSourceBuffer; + int rawSourceFence; Matrix4 texTransform; - status_t err = surface.getLastQueuedBuffer(&sourceBuffer, &sourceFence, texTransform.data); + status_t err = ANativeWindow_getLastQueuedBuffer(window, &rawSourceBuffer, &rawSourceFence, + texTransform.data); + base::unique_fd sourceFence(rawSourceFence); texTransform.invalidateType(); if (err != NO_ERROR) { ALOGW("Failed to get last queued buffer, error = %d", err); return CopyResult::UnknownError; } - if (!sourceBuffer.get()) { + if (rawSourceBuffer == nullptr) { ALOGW("Surface doesn't have any previously queued frames, nothing to readback from"); return CopyResult::SourceEmpty; } - if (sourceBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) { + + std::unique_ptr sourceBuffer( + rawSourceBuffer, AHardwareBuffer_release); + AHardwareBuffer_Desc description; + AHardwareBuffer_describe(sourceBuffer.get(), &description); + if (description.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) { ALOGW("Surface is protected, unable to copy from it"); return CopyResult::SourceInvalid; } - err = sourceFence->wait(500 /* ms */); - if (err != NO_ERROR) { + + if (sourceFence != -1 && sync_wait(sourceFence.get(), 500 /* ms */) != NO_ERROR) { ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt"); return CopyResult::Timeout; } - if (!sourceBuffer.get()) { - return CopyResult::UnknownError; - } - sk_sp colorSpace = - DataSpaceToColorSpace(static_cast(surface.getBuffersDataSpace())); - sk_sp image = SkImage::MakeFromAHardwareBuffer( - reinterpret_cast(sourceBuffer.get()), - kPremul_SkAlphaType, colorSpace); + sk_sp colorSpace = DataSpaceToColorSpace( + static_cast(ANativeWindow_getBuffersDataSpace(window))); + sk_sp image = + SkImage::MakeFromAHardwareBuffer(sourceBuffer.get(), kPremul_SkAlphaType, colorSpace); return copyImageInto(image, texTransform, srcRect, bitmap); } diff --git a/libs/hwui/Readback.h b/libs/hwui/Readback.h index e86a8136cfa3..e36f1ff6a072 100644 --- a/libs/hwui/Readback.h +++ b/libs/hwui/Readback.h @@ -47,7 +47,7 @@ public: /** * Copies the surface's most recently queued buffer into the provided bitmap. */ - CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect, SkBitmap* bitmap); + CopyResult copySurfaceInto(ANativeWindow* window, const Rect& srcRect, SkBitmap* bitmap); CopyResult copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap); diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index f9e401a2e93b..1e7fc71a7f04 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -317,8 +317,9 @@ void RenderProxy::setRenderAheadDepth(int renderAhead) { int RenderProxy::copySurfaceInto(sp& surface, int left, int top, int right, int bottom, SkBitmap* bitmap) { auto& thread = RenderThread::getInstance(); + ANativeWindow* window = surface.get(); return static_cast(thread.queue().runSync([&]() -> auto { - return thread.readback().copySurfaceInto(*surface, Rect(left, top, right, bottom), bitmap); + return thread.readback().copySurfaceInto(window, Rect(left, top, right, bottom), bitmap); })); } diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index 4683e1d69019..ab0dd2bcc8f5 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -140,6 +140,10 @@ public: */ ANDROID_API void setRenderAheadDepth(int renderAhead); + // TODO: This api will need to take in an ANativeWindow instead, but the + // caller, ThreadedRenderer, doesn't have access to libandroid due to a + // circular dependency, so it can't use the JNI ANativeWindow methods. Once + // that is resolved then replace the surface type here. ANDROID_API static int copySurfaceInto(sp& surface, int left, int top, int right, int bottom, SkBitmap* bitmap); ANDROID_API static void prepareToDraw(Bitmap& bitmap); -- cgit v1.2.3 From c9d5f3d952b5151f7562fb52c5aa7b05eecf1955 Mon Sep 17 00:00:00 2001 From: Alec Mouri Date: Thu, 13 Feb 2020 13:58:25 -0800 Subject: [ANativeWindow] Rename allocateBuffers to tryAllocateBuffers Renaming was done in response to API feedback. Bug: 148962594 Test: builds Change-Id: I05b3e99800d967dda243b6e7af48f1b645909bdd --- libs/hwui/renderthread/CanvasContext.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'libs') diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 91f9447a3d59..1df3336bb5e5 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -204,8 +204,7 @@ void CanvasContext::setStopped(bool stopped) { void CanvasContext::allocateBuffers() { if (mNativeSurface) { - ANativeWindow* anw = mNativeSurface->getNativeWindow(); - ANativeWindow_allocateBuffers(anw); + ANativeWindow_tryAllocateBuffers(mNativeSurface->getNativeWindow()); } } -- cgit v1.2.3 From 260613f89fb29662555264df976474c1ff8d2de4 Mon Sep 17 00:00:00 2001 From: Jeffrey Huang Date: Fri, 14 Feb 2020 10:08:22 -0800 Subject: Allow statsd to link against libprotoutil Bug: 145923087 Test: m -j Change-Id: I8e21903d8a1c142499ee5b772da54b0fcbae3b67 --- libs/protoutil/Android.bp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'libs') diff --git a/libs/protoutil/Android.bp b/libs/protoutil/Android.bp index b0af99732ddb..d2b7d5c75d58 100644 --- a/libs/protoutil/Android.bp +++ b/libs/protoutil/Android.bp @@ -45,6 +45,12 @@ cc_library { defaults: ["libprotoutil_defaults"], export_include_dirs: ["include"], + + apex_available: [ + "//apex_available:platform", + "com.android.os.statsd", + "test_com.android.os.statsd", + ], } cc_test { -- cgit v1.2.3 From 331c4e1207a28c6bbd9a8def5c22a892f25fe2f4 Mon Sep 17 00:00:00 2001 From: Mike Reed Date: Thu, 13 Feb 2020 10:21:54 -0500 Subject: Use updated factory for SkOverdrawColorFilter Test: make Change-Id: Ibf67a37e90152326b3088d2f15f17a6ad261658c --- libs/hwui/pipeline/skia/SkiaPipeline.cpp | 43 +++++++++++++++----------------- 1 file changed, 20 insertions(+), 23 deletions(-) (limited to 'libs') diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 29b4dd7f32e7..06584027863a 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -600,27 +600,24 @@ void SkiaPipeline::setSurfaceColorProperties(ColorMode colorMode) { // Overdraw debugging // These colors should be kept in sync with Caches::getOverdrawColor() with a few differences. -// This implementation: -// (1) Requires transparent entries for "no overdraw" and "single draws". -// (2) Requires premul colors (instead of unpremul). -// (3) Requires RGBA colors (instead of BGRA). -static const uint32_t kOverdrawColors[2][6] = { - { - 0x00000000, - 0x00000000, - 0x2f2f0000, - 0x2f002f00, - 0x3f00003f, - 0x7f00007f, - }, - { - 0x00000000, - 0x00000000, - 0x2f2f0000, - 0x4f004f4f, - 0x5f50335f, - 0x7f00007f, - }, +// This implementation requires transparent entries for "no overdraw" and "single draws". +static const SkColor kOverdrawColors[2][6] = { + { + 0x00000000, + 0x00000000, + 0x2f0000ff, + 0x2f00ff00, + 0x3fff0000, + 0x7fff0000, + }, + { + 0x00000000, + 0x00000000, + 0x2f0000ff, + 0x4fffff00, + 0x5fff89d7, + 0x7fff0000, + }, }; void SkiaPipeline::renderOverdraw(const SkRect& clip, @@ -642,8 +639,8 @@ void SkiaPipeline::renderOverdraw(const SkRect& clip, // Draw overdraw colors to the canvas. The color filter will convert counts to colors. SkPaint paint; - const SkPMColor* colors = kOverdrawColors[static_cast(Properties::overdrawColorSet)]; - paint.setColorFilter(SkOverdrawColorFilter::Make(colors)); + const SkColor* colors = kOverdrawColors[static_cast(Properties::overdrawColorSet)]; + paint.setColorFilter(SkOverdrawColorFilter::MakeWithSkColors(colors)); surface->getCanvas()->drawImage(counts.get(), 0.0f, 0.0f, &paint); } -- cgit v1.2.3 From 43fe6fcde5cb2630a8d1ffa47d3e6e58e11999ae Mon Sep 17 00:00:00 2001 From: Alec Mouri Date: Mon, 23 Dec 2019 07:46:19 -0800 Subject: [HWUI] Remove references to gui/Surface. ANativeWindow usage now has enough feature parity so that we can use that instead. Bug: 137012798 Test: builds Test: Scroll through settings Change-Id: I0054315058b28bcb5e779a6f71a3cfb164625a5f --- libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h | 4 ++- libs/hwui/pipeline/skia/SkiaPipeline.cpp | 13 +++++---- libs/hwui/renderthread/CanvasContext.cpp | 10 +++---- libs/hwui/renderthread/CanvasContext.h | 3 +- libs/hwui/renderthread/ReliableSurface.cpp | 33 +++++++++++++--------- libs/hwui/renderthread/ReliableSurface.h | 14 ++++----- libs/hwui/renderthread/RenderProxy.cpp | 13 ++++----- libs/hwui/renderthread/RenderProxy.h | 9 ++---- .../tests/common/scenes/MagnifierAnimation.cpp | 2 +- libs/hwui/tests/macrobench/TestSceneRunner.cpp | 2 +- libs/hwui/tests/unit/SkiaDisplayListTests.cpp | 2 +- 11 files changed, 54 insertions(+), 51 deletions(-) (limited to 'libs') diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h index e482cad6c953..fc6e1142b4f2 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h @@ -16,8 +16,10 @@ #pragma once -#include "SkiaPipeline.h" +#include +#include +#include "SkiaPipeline.h" #include "renderstate/RenderState.h" namespace android { diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 29b4dd7f32e7..90e49a0b4dbc 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -24,18 +24,19 @@ #include #include #include -#include #include +#include +#include +#include + +#include + #include "LightingInfo.h" #include "VectorDrawable.h" #include "thread/CommonPool.h" #include "tools/SkSharingProc.h" -#include "utils/TraceUtils.h" #include "utils/String8.h" - -#include - -#include +#include "utils/TraceUtils.h" using namespace android::uirenderer::renderthread; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 1df3336bb5e5..4299dd3b46fe 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -139,15 +139,15 @@ void CanvasContext::destroy() { mAnimationContext->destroy(); } -void CanvasContext::setSurface(sp&& surface, bool enableTimeout) { +void CanvasContext::setSurface(ANativeWindow* window, bool enableTimeout) { ATRACE_CALL(); - if (surface) { - mNativeSurface = std::make_unique(std::move(surface)); + if (window) { + mNativeSurface = std::make_unique(window); mNativeSurface->init(); if (enableTimeout) { // TODO: Fix error handling & re-shorten timeout - ANativeWindow_setDequeueTimeout(mNativeSurface->getNativeWindow(), 4000_ms); + ANativeWindow_setDequeueTimeout(window, 4000_ms); } } else { mNativeSurface = nullptr; @@ -167,7 +167,7 @@ void CanvasContext::setSurface(sp&& surface, bool enableTimeout) { mFrameNumber = -1; - if (hasSurface) { + if (window != nullptr && hasSurface) { mHaveNewSurface = true; mSwapHistory.clear(); // Enable frame stats after the surface has been bound to the appropriate graphics API. diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 629c741e8757..0f1b8aebf56c 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -35,7 +35,6 @@ #include #include #include -#include #include #include @@ -111,7 +110,7 @@ public: // Won't take effect until next EGLSurface creation void setSwapBehavior(SwapBehavior swapBehavior); - void setSurface(sp&& surface, bool enableTimeout = true); + void setSurface(ANativeWindow* window, bool enableTimeout = true); bool pauseSurface(); void setStopped(bool stopped); bool hasSurface() const { return mNativeSurface.get(); } diff --git a/libs/hwui/renderthread/ReliableSurface.cpp b/libs/hwui/renderthread/ReliableSurface.cpp index e92500f5be51..8a0b4e8455bd 100644 --- a/libs/hwui/renderthread/ReliableSurface.cpp +++ b/libs/hwui/renderthread/ReliableSurface.cpp @@ -16,7 +16,10 @@ #include "ReliableSurface.h" +#include #include +// TODO: this should be including apex instead. +#include namespace android::uirenderer::renderthread { @@ -26,8 +29,9 @@ namespace android::uirenderer::renderthread { // to propagate this error back to the caller constexpr bool DISABLE_BUFFER_PREFETCH = true; -ReliableSurface::ReliableSurface(sp&& surface) : mSurface(std::move(surface)) { - LOG_ALWAYS_FATAL_IF(!mSurface, "Error, unable to wrap a nullptr"); +ReliableSurface::ReliableSurface(ANativeWindow* window) : mWindow(window) { + LOG_ALWAYS_FATAL_IF(!mWindow, "Error, unable to wrap a nullptr"); + ANativeWindow_acquire(mWindow); } ReliableSurface::~ReliableSurface() { @@ -36,26 +40,27 @@ ReliableSurface::~ReliableSurface() { // As a concrete example, if the underlying ANativeWindow is associated with // an EGLSurface that is still in use, then if we don't clear out the // interceptors then we walk into undefined behavior. - ANativeWindow_setCancelBufferInterceptor(mSurface.get(), nullptr, nullptr); - ANativeWindow_setDequeueBufferInterceptor(mSurface.get(), nullptr, nullptr); - ANativeWindow_setQueueBufferInterceptor(mSurface.get(), nullptr, nullptr); - ANativeWindow_setPerformInterceptor(mSurface.get(), nullptr, nullptr); + ANativeWindow_setCancelBufferInterceptor(mWindow, nullptr, nullptr); + ANativeWindow_setDequeueBufferInterceptor(mWindow, nullptr, nullptr); + ANativeWindow_setQueueBufferInterceptor(mWindow, nullptr, nullptr); + ANativeWindow_setPerformInterceptor(mWindow, nullptr, nullptr); + ANativeWindow_release(mWindow); } void ReliableSurface::init() { - int result = ANativeWindow_setCancelBufferInterceptor(mSurface.get(), hook_cancelBuffer, this); + int result = ANativeWindow_setCancelBufferInterceptor(mWindow, hook_cancelBuffer, this); LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set cancelBuffer interceptor: error = %d", result); - result = ANativeWindow_setDequeueBufferInterceptor(mSurface.get(), hook_dequeueBuffer, this); + result = ANativeWindow_setDequeueBufferInterceptor(mWindow, hook_dequeueBuffer, this); LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set dequeueBuffer interceptor: error = %d", result); - result = ANativeWindow_setQueueBufferInterceptor(mSurface.get(), hook_queueBuffer, this); + result = ANativeWindow_setQueueBufferInterceptor(mWindow, hook_queueBuffer, this); LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set queueBuffer interceptor: error = %d", result); - result = ANativeWindow_setPerformInterceptor(mSurface.get(), hook_perform, this); + result = ANativeWindow_setPerformInterceptor(mWindow, hook_perform, this); LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set perform interceptor: error = %d", result); } @@ -87,7 +92,7 @@ int ReliableSurface::reserveNext() { ANativeWindowBuffer* buffer = nullptr; // Note that this calls back into our own hooked method. - int result = ANativeWindow_dequeueBuffer(mSurface.get(), &buffer, &fenceFd); + int result = ANativeWindow_dequeueBuffer(mWindow, &buffer, &fenceFd); { std::lock_guard _lock{mMutex}; @@ -117,7 +122,7 @@ void ReliableSurface::clearReservedBuffer() { // Note that clearReservedBuffer may be reentrant here, so // mReservedBuffer must be cleared once we reach here to avoid recursing // forever. - ANativeWindow_cancelBuffer(mSurface.get(), buffer, releaseFd); + ANativeWindow_cancelBuffer(mWindow, buffer, releaseFd); } } @@ -239,10 +244,10 @@ int ReliableSurface::hook_perform(ANativeWindow* window, ANativeWindow_performFn case ANATIVEWINDOW_PERFORM_SET_BUFFERS_GEOMETRY: /* width */ va_arg(args, uint32_t); /* height */ va_arg(args, uint32_t); - rs->mFormat = va_arg(args, PixelFormat); + rs->mFormat = static_cast(va_arg(args, int32_t)); break; case ANATIVEWINDOW_PERFORM_SET_BUFFERS_FORMAT: - rs->mFormat = va_arg(args, PixelFormat); + rs->mFormat = static_cast(va_arg(args, int32_t)); break; } } diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h index e3cd8c019a23..58cd06730123 100644 --- a/libs/hwui/renderthread/ReliableSurface.h +++ b/libs/hwui/renderthread/ReliableSurface.h @@ -16,12 +16,14 @@ #pragma once +#include #include -#include +#include #include #include #include +#include namespace android::uirenderer::renderthread { @@ -29,7 +31,7 @@ class ReliableSurface { PREVENT_COPY_AND_ASSIGN(ReliableSurface); public: - ReliableSurface(sp&& surface); + ReliableSurface(ANativeWindow* window); ~ReliableSurface(); // Performs initialization that is not safe to do in the constructor. @@ -37,12 +39,10 @@ public: // passed as the data pointer is not safe. void init(); - ANativeWindow* getNativeWindow() { return mSurface.get(); } + ANativeWindow* getNativeWindow() { return mWindow; } int reserveNext(); - int query(int what, int* value) const { return mSurface->query(what, value); } - int getAndClearError() { int ret = mBufferQueueState; mBufferQueueState = OK; @@ -50,12 +50,12 @@ public: } private: - sp mSurface; + ANativeWindow* mWindow; mutable std::mutex mMutex; uint64_t mUsage = AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER; - PixelFormat mFormat = PIXEL_FORMAT_RGBA_8888; + AHardwareBuffer_Format mFormat = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM; std::unique_ptr mScratchBuffer{ nullptr, AHardwareBuffer_release}; ANativeWindowBuffer* mReservedBuffer = nullptr; diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 1e7fc71a7f04..b66a13d1efda 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -16,8 +16,6 @@ #include "RenderProxy.h" -#include - #include "DeferredLayerUpdater.h" #include "DisplayList.h" #include "Properties.h" @@ -78,9 +76,11 @@ void RenderProxy::setName(const char* name) { mRenderThread.queue().runSync([this, name]() { mContext->setName(std::string(name)); }); } -void RenderProxy::setSurface(const sp& surface, bool enableTimeout) { - mRenderThread.queue().post([this, surf = surface, enableTimeout]() mutable { - mContext->setSurface(std::move(surf), enableTimeout); +void RenderProxy::setSurface(ANativeWindow* window, bool enableTimeout) { + ANativeWindow_acquire(window); + mRenderThread.queue().post([this, win = window, enableTimeout]() mutable { + mContext->setSurface(win, enableTimeout); + ANativeWindow_release(win); }); } @@ -314,10 +314,9 @@ void RenderProxy::setRenderAheadDepth(int renderAhead) { [context = mContext, renderAhead] { context->setRenderAheadDepth(renderAhead); }); } -int RenderProxy::copySurfaceInto(sp& surface, int left, int top, int right, int bottom, +int RenderProxy::copySurfaceInto(ANativeWindow* window, int left, int top, int right, int bottom, SkBitmap* bitmap) { auto& thread = RenderThread::getInstance(); - ANativeWindow* window = surface.get(); return static_cast(thread.queue().runSync([&]() -> auto { return thread.readback().copySurfaceInto(window, Rect(left, top, right, bottom), bitmap); })); diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index ab0dd2bcc8f5..3baeb2f7a476 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -18,6 +18,7 @@ #define RENDERPROXY_H_ #include +#include #include #include @@ -69,7 +70,7 @@ public: ANDROID_API bool loadSystemProperties(); ANDROID_API void setName(const char* name); - ANDROID_API void setSurface(const sp& surface, bool enableTimeout = true); + ANDROID_API void setSurface(ANativeWindow* window, bool enableTimeout = true); ANDROID_API void allocateBuffers(); ANDROID_API bool pause(); ANDROID_API void setStopped(bool stopped); @@ -140,11 +141,7 @@ public: */ ANDROID_API void setRenderAheadDepth(int renderAhead); - // TODO: This api will need to take in an ANativeWindow instead, but the - // caller, ThreadedRenderer, doesn't have access to libandroid due to a - // circular dependency, so it can't use the JNI ANativeWindow methods. Once - // that is resolved then replace the surface type here. - ANDROID_API static int copySurfaceInto(sp& surface, int left, int top, int right, + ANDROID_API static int copySurfaceInto(ANativeWindow* window, int left, int top, int right, int bottom, SkBitmap* bitmap); ANDROID_API static void prepareToDraw(Bitmap& bitmap); diff --git a/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp b/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp index f6cff1c643a1..f4fce277454d 100644 --- a/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp +++ b/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp @@ -70,7 +70,7 @@ public: magnifier->getSkBitmap(&temp); constexpr int x = 90; constexpr int y = 325; - RenderProxy::copySurfaceInto(renderTarget, x, y, x + magnifier->width(), + RenderProxy::copySurfaceInto(renderTarget.get(), x, y, x + magnifier->width(), y + magnifier->height(), &temp); } } diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp index 3b6baa70db9a..801cb7d9e8c5 100644 --- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp +++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp @@ -131,7 +131,7 @@ void run(const TestScene::Info& info, const TestScene::Options& opts, ContextFactory factory; std::unique_ptr proxy(new RenderProxy(false, rootNode.get(), &factory)); proxy->loadSystemProperties(); - proxy->setSurface(surface); + proxy->setSurface(surface.get()); float lightX = width / 2.0; proxy->setLightAlpha(255 * 0.075, 255 * 0.15); proxy->setLightGeometry((Vector3){lightX, dp(-200.0f), dp(800.0f)}, dp(800.0f)); diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp index 7d999c43b560..d08aea668b2a 100644 --- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp +++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp @@ -210,7 +210,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren_vdOffscr auto surface = testContext.surface(); int width = ANativeWindow_getWidth(surface.get()); int height = ANativeWindow_getHeight(surface.get()); - canvasContext->setSurface(std::move(surface)); + canvasContext->setSurface(surface.get()); TreeInfo info(TreeInfo::MODE_FULL, *canvasContext.get()); DamageAccumulator damageAccumulator; -- cgit v1.2.3 From 680414e09ec900b54ad4c1757cf538714b92d9c6 Mon Sep 17 00:00:00 2001 From: Alec Mouri Date: Tue, 28 Jan 2020 09:22:33 -0800 Subject: [HWUI] Remove libgui from libhwui's dependency list * Add sync features extensions into EglExtensions * Header cleanup Bug: 136263382 Test: builds Change-Id: Ibd29cfe5201419210c9dc09e82af10524454b6cd --- libs/hwui/Android.bp | 8 +++++++- libs/hwui/hwui/Bitmap.cpp | 1 - libs/hwui/renderthread/EglManager.cpp | 14 +++++++++----- 3 files changed, 16 insertions(+), 7 deletions(-) (limited to 'libs') diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index debb38b2c1b0..81dedda5341d 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -84,7 +84,6 @@ cc_defaults { "libGLESv3", "libvulkan", "libui", - "libgui", "libnativedisplay", "libnativewindow", "libprotobuf-cpp-lite", @@ -289,6 +288,13 @@ cc_defaults { name: "hwui_test_defaults", defaults: ["hwui_defaults"], test_suites: ["device-tests"], + target: { + android: { + shared_libs: [ + "libgui", + ], + } + }, srcs: [ "tests/common/scenes/*.cpp", "tests/common/LeakChecker.cpp", diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index 58cc08bc2d73..914c04645289 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -32,7 +32,6 @@ #ifndef _WIN32 #include -#include #endif #include diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index c1fed269de5b..5e0471c08d67 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #include #include @@ -79,6 +78,9 @@ static struct { bool displayP3 = false; bool contextPriority = false; bool surfacelessContext = false; + bool nativeFenceSync = false; + bool fenceSync = false; + bool waitSync = false; } EglExtensions; EglManager::EglManager() @@ -226,6 +228,9 @@ void EglManager::initExtensions() { EglExtensions.displayP3 = extensions.has("EGL_EXT_gl_colorspace_display_p3_passthrough"); EglExtensions.contextPriority = extensions.has("EGL_IMG_context_priority"); EglExtensions.surfacelessContext = extensions.has("EGL_KHR_surfaceless_context"); + EglExtensions.nativeFenceSync = extensions.has("EGL_ANDROID_native_fence_sync"); + EglExtensions.fenceSync = extensions.has("EGL_KHR_fence_sync"); + EglExtensions.waitSync = extensions.has("EGL_KHR_wait_sync"); } bool EglManager::hasEglContext() { @@ -527,8 +532,7 @@ status_t EglManager::fenceWait(int fence) { return INVALID_OPERATION; } - if (SyncFeatures::getInstance().useWaitSync() && - SyncFeatures::getInstance().useNativeFenceSync()) { + if (EglExtensions.waitSync && EglExtensions.nativeFenceSync) { // Block GPU on the fence. // Create an EGLSyncKHR from the current fence. int fenceFd = ::dup(fence); @@ -572,7 +576,7 @@ status_t EglManager::createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, return INVALID_OPERATION; } - if (SyncFeatures::getInstance().useNativeFenceSync()) { + if (EglExtensions.nativeFenceSync) { EGLSyncKHR sync = eglCreateSyncKHR(mEglDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); if (sync == EGL_NO_SYNC_KHR) { ALOGE("EglManager::createReleaseFence: error creating EGL fence: %#x", eglGetError()); @@ -589,7 +593,7 @@ status_t EglManager::createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, } *nativeFence = fenceFd; *eglFence = EGL_NO_SYNC_KHR; - } else if (useFenceSync && SyncFeatures::getInstance().useFenceSync()) { + } else if (useFenceSync && EglExtensions.fenceSync) { if (*eglFence != EGL_NO_SYNC_KHR) { // There is already a fence for the current slot. We need to // wait on that before replacing it with another fence to -- cgit v1.2.3 From b65171956c846031517c709ec0726c99a2bc5962 Mon Sep 17 00:00:00 2001 From: Stan Iliev Date: Tue, 18 Feb 2020 10:12:42 -0500 Subject: Update GraphicsStats proto definition Follow ProtoBestPractices and comments suggested by cl/295218381. Changing proto definition, should be fine, because these are fields added after the last Android release. Move PipelineType enum inside GraphicsStats and add a default UNKNOWN state. Rename start_millis and end_millis in GraphicsStats atom. Fields in GraphicsStatsProto cannot be renamed as these were part of previous Android releases. Test: Ran "adb shell cmd stats pull-source 10068" Test: Ran "statsd_testdrive 10068" and it looks OK Bug: 149646555 Change-Id: Idc326eb2a7eb9ff0be39026e6fec544491b7d8a6 --- libs/hwui/protos/graphicsstats.proto | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'libs') diff --git a/libs/hwui/protos/graphicsstats.proto b/libs/hwui/protos/graphicsstats.proto index dd5676c9c961..745393ce1a3d 100644 --- a/libs/hwui/protos/graphicsstats.proto +++ b/libs/hwui/protos/graphicsstats.proto @@ -30,8 +30,9 @@ message GraphicsStatsServiceDumpProto { message GraphicsStatsProto { enum PipelineType { - GL = 0; - VULKAN = 1; + UNKNOWN = 0; + GL = 1; + VULKAN = 2; } // The package name of the app -- cgit v1.2.3 From 305fb2fc1ae92a9769ae6575f2dc3da02bd8a4a8 Mon Sep 17 00:00:00 2001 From: Anton Hansson Date: Wed, 19 Feb 2020 15:27:58 +0000 Subject: Remove obsolete subdirs and optional_subdirs I was looking for documentation of what these mean, but discovered that these attributes have been obsolete for years. Test: m Change-Id: Ifcf47e99d22b1546780968d1030f835173612a27 --- libs/usb/Android.bp | 2 -- libs/usb/tests/AccessoryChat/Android.bp | 1 - 2 files changed, 3 deletions(-) (limited to 'libs') diff --git a/libs/usb/Android.bp b/libs/usb/Android.bp index 027a7488f723..e752b55f5ef7 100644 --- a/libs/usb/Android.bp +++ b/libs/usb/Android.bp @@ -19,5 +19,3 @@ java_sdk_library { srcs: ["src/**/*.java"], api_packages: ["com.android.future.usb"], } - -subdirs = ["tests/*"] diff --git a/libs/usb/tests/AccessoryChat/Android.bp b/libs/usb/tests/AccessoryChat/Android.bp index 63a670c67bfc..19ed3d3ef52e 100644 --- a/libs/usb/tests/AccessoryChat/Android.bp +++ b/libs/usb/tests/AccessoryChat/Android.bp @@ -1,4 +1,3 @@ -subdirs = ["accessorychat"] // // Copyright (C) 2011 The Android Open Source Project // -- cgit v1.2.3 From 83ccff716f160d3f9665732d50a7974f5f8e890a Mon Sep 17 00:00:00 2001 From: Derek Sollenberger Date: Thu, 24 Oct 2019 11:49:54 -0400 Subject: Move android.graphics JNI & APEX files into HWUI The graphics JNI code is now separate from libandroid_runtime and it along with HWUI headers are no longer visible to targets outside the boundary of what is to become the UI mainline module The exposed headers to targets outside the module are now restriced to C APIs contained in the apex header directory. Bug: 137655431 Test: CtsUiRenderingTestCases Change-Id: I30d34055b6870dc1039f190a88f4a747cee17300 --- libs/hwui/Android.bp | 226 +++- libs/hwui/apex/TypeCast.h | 70 ++ libs/hwui/apex/android_bitmap.cpp | 304 +++++ libs/hwui/apex/android_canvas.cpp | 109 ++ libs/hwui/apex/android_matrix.cpp | 37 + libs/hwui/apex/android_paint.cpp | 47 + libs/hwui/apex/android_region.cpp | 60 + libs/hwui/apex/include/android/graphics/bitmap.h | 145 +++ libs/hwui/apex/include/android/graphics/canvas.h | 141 +++ .../apex/include/android/graphics/jni_runtime.h | 32 + libs/hwui/apex/include/android/graphics/matrix.h | 38 + libs/hwui/apex/include/android/graphics/paint.h | 66 ++ libs/hwui/apex/include/android/graphics/region.h | 76 ++ .../apex/include/android/graphics/renderthread.h | 33 + libs/hwui/apex/jni_runtime.cpp | 171 +++ libs/hwui/apex/renderthread.cpp | 25 + libs/hwui/jni/AnimatedImageDrawable.cpp | 272 +++++ libs/hwui/jni/Bitmap.cpp | 1178 ++++++++++++++++++++ libs/hwui/jni/Bitmap.h | 53 + libs/hwui/jni/BitmapFactory.cpp | 664 +++++++++++ libs/hwui/jni/BitmapFactory.h | 31 + libs/hwui/jni/BitmapRegionDecoder.cpp | 291 +++++ libs/hwui/jni/ByteBufferStreamAdaptor.cpp | 335 ++++++ libs/hwui/jni/ByteBufferStreamAdaptor.h | 37 + libs/hwui/jni/Camera.cpp | 146 +++ libs/hwui/jni/CanvasProperty.cpp | 52 + libs/hwui/jni/ColorFilter.cpp | 91 ++ libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp | 306 +++++ libs/hwui/jni/CreateJavaOutputStreamAdaptor.h | 43 + libs/hwui/jni/FontFamily.cpp | 240 ++++ libs/hwui/jni/FontUtils.cpp | 63 ++ libs/hwui/jni/FontUtils.h | 71 ++ libs/hwui/jni/GIFMovie.cpp | 447 ++++++++ libs/hwui/jni/Graphics.cpp | 735 ++++++++++++ libs/hwui/jni/GraphicsJNI.h | 314 ++++++ libs/hwui/jni/GraphicsStatsService.cpp | 195 ++++ libs/hwui/jni/ImageDecoder.cpp | 526 +++++++++ libs/hwui/jni/ImageDecoder.h | 25 + libs/hwui/jni/Interpolator.cpp | 87 ++ libs/hwui/jni/MaskFilter.cpp | 94 ++ libs/hwui/jni/Movie.cpp | 166 +++ libs/hwui/jni/Movie.h | 79 ++ libs/hwui/jni/MovieImpl.cpp | 94 ++ libs/hwui/jni/NinePatch.cpp | 170 +++ libs/hwui/jni/NinePatchPeeker.cpp | 93 ++ libs/hwui/jni/NinePatchPeeker.h | 59 + libs/hwui/jni/Paint.cpp | 1160 +++++++++++++++++++ libs/hwui/jni/PaintFilter.cpp | 84 ++ libs/hwui/jni/Path.cpp | 562 ++++++++++ libs/hwui/jni/PathEffect.cpp | 120 ++ libs/hwui/jni/PathMeasure.cpp | 162 +++ libs/hwui/jni/Picture.cpp | 119 ++ libs/hwui/jni/Picture.h | 68 ++ libs/hwui/jni/Region.cpp | 360 ++++++ libs/hwui/jni/RtlProperties.h | 49 + libs/hwui/jni/Shader.cpp | 308 +++++ libs/hwui/jni/TEST_MAPPING | 7 + libs/hwui/jni/Typeface.cpp | 162 +++ libs/hwui/jni/Utils.cpp | 161 +++ libs/hwui/jni/Utils.h | 83 ++ libs/hwui/jni/YuvToJpegEncoder.cpp | 270 +++++ libs/hwui/jni/YuvToJpegEncoder.h | 74 ++ libs/hwui/jni/android_graphics_Canvas.cpp | 741 ++++++++++++ libs/hwui/jni/android_graphics_ColorSpace.cpp | 110 ++ .../jni/android_graphics_DisplayListCanvas.cpp | 220 ++++ .../hwui/jni/android_graphics_HardwareRenderer.cpp | 746 +++++++++++++ .../android_graphics_HardwareRendererObserver.cpp | 130 +++ .../android_graphics_HardwareRendererObserver.h | 75 ++ libs/hwui/jni/android_graphics_Matrix.cpp | 399 +++++++ libs/hwui/jni/android_graphics_Matrix.h | 30 + libs/hwui/jni/android_graphics_Picture.cpp | 113 ++ libs/hwui/jni/android_graphics_RenderNode.cpp | 765 +++++++++++++ libs/hwui/jni/android_graphics_TextureLayer.cpp | 90 ++ ...raphics_animation_NativeInterpolatorFactory.cpp | 114 ++ ...droid_graphics_animation_RenderNodeAnimator.cpp | 225 ++++ ...id_graphics_drawable_AnimatedVectorDrawable.cpp | 221 ++++ .../android_graphics_drawable_VectorDrawable.cpp | 425 +++++++ libs/hwui/jni/android_nio_utils.cpp | 46 + libs/hwui/jni/android_nio_utils.h | 81 ++ libs/hwui/jni/android_util_PathParser.cpp | 120 ++ libs/hwui/jni/fonts/Font.cpp | 148 +++ libs/hwui/jni/fonts/FontFamily.cpp | 101 ++ libs/hwui/jni/pdf/PdfDocument.cpp | 165 +++ libs/hwui/jni/pdf/PdfEditor.cpp | 308 +++++ libs/hwui/jni/pdf/PdfRenderer.cpp | 137 +++ libs/hwui/jni/pdf/PdfUtils.cpp | 135 +++ libs/hwui/jni/pdf/PdfUtils.h | 35 + libs/hwui/jni/text/LineBreaker.cpp | 174 +++ libs/hwui/jni/text/MeasuredText.cpp | 168 +++ 89 files changed, 18305 insertions(+), 3 deletions(-) create mode 100644 libs/hwui/apex/TypeCast.h create mode 100644 libs/hwui/apex/android_bitmap.cpp create mode 100644 libs/hwui/apex/android_canvas.cpp create mode 100644 libs/hwui/apex/android_matrix.cpp create mode 100644 libs/hwui/apex/android_paint.cpp create mode 100644 libs/hwui/apex/android_region.cpp create mode 100644 libs/hwui/apex/include/android/graphics/bitmap.h create mode 100644 libs/hwui/apex/include/android/graphics/canvas.h create mode 100644 libs/hwui/apex/include/android/graphics/jni_runtime.h create mode 100644 libs/hwui/apex/include/android/graphics/matrix.h create mode 100644 libs/hwui/apex/include/android/graphics/paint.h create mode 100644 libs/hwui/apex/include/android/graphics/region.h create mode 100644 libs/hwui/apex/include/android/graphics/renderthread.h create mode 100644 libs/hwui/apex/jni_runtime.cpp create mode 100644 libs/hwui/apex/renderthread.cpp create mode 100644 libs/hwui/jni/AnimatedImageDrawable.cpp create mode 100755 libs/hwui/jni/Bitmap.cpp create mode 100644 libs/hwui/jni/Bitmap.h create mode 100644 libs/hwui/jni/BitmapFactory.cpp create mode 100644 libs/hwui/jni/BitmapFactory.h create mode 100644 libs/hwui/jni/BitmapRegionDecoder.cpp create mode 100644 libs/hwui/jni/ByteBufferStreamAdaptor.cpp create mode 100644 libs/hwui/jni/ByteBufferStreamAdaptor.h create mode 100644 libs/hwui/jni/Camera.cpp create mode 100644 libs/hwui/jni/CanvasProperty.cpp create mode 100644 libs/hwui/jni/ColorFilter.cpp create mode 100644 libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp create mode 100644 libs/hwui/jni/CreateJavaOutputStreamAdaptor.h create mode 100644 libs/hwui/jni/FontFamily.cpp create mode 100644 libs/hwui/jni/FontUtils.cpp create mode 100644 libs/hwui/jni/FontUtils.h create mode 100644 libs/hwui/jni/GIFMovie.cpp create mode 100644 libs/hwui/jni/Graphics.cpp create mode 100644 libs/hwui/jni/GraphicsJNI.h create mode 100644 libs/hwui/jni/GraphicsStatsService.cpp create mode 100644 libs/hwui/jni/ImageDecoder.cpp create mode 100644 libs/hwui/jni/ImageDecoder.h create mode 100644 libs/hwui/jni/Interpolator.cpp create mode 100644 libs/hwui/jni/MaskFilter.cpp create mode 100644 libs/hwui/jni/Movie.cpp create mode 100644 libs/hwui/jni/Movie.h create mode 100644 libs/hwui/jni/MovieImpl.cpp create mode 100644 libs/hwui/jni/NinePatch.cpp create mode 100644 libs/hwui/jni/NinePatchPeeker.cpp create mode 100644 libs/hwui/jni/NinePatchPeeker.h create mode 100644 libs/hwui/jni/Paint.cpp create mode 100644 libs/hwui/jni/PaintFilter.cpp create mode 100644 libs/hwui/jni/Path.cpp create mode 100644 libs/hwui/jni/PathEffect.cpp create mode 100644 libs/hwui/jni/PathMeasure.cpp create mode 100644 libs/hwui/jni/Picture.cpp create mode 100644 libs/hwui/jni/Picture.h create mode 100644 libs/hwui/jni/Region.cpp create mode 100644 libs/hwui/jni/RtlProperties.h create mode 100644 libs/hwui/jni/Shader.cpp create mode 100644 libs/hwui/jni/TEST_MAPPING create mode 100644 libs/hwui/jni/Typeface.cpp create mode 100644 libs/hwui/jni/Utils.cpp create mode 100644 libs/hwui/jni/Utils.h create mode 100644 libs/hwui/jni/YuvToJpegEncoder.cpp create mode 100644 libs/hwui/jni/YuvToJpegEncoder.h create mode 100644 libs/hwui/jni/android_graphics_Canvas.cpp create mode 100644 libs/hwui/jni/android_graphics_ColorSpace.cpp create mode 100644 libs/hwui/jni/android_graphics_DisplayListCanvas.cpp create mode 100644 libs/hwui/jni/android_graphics_HardwareRenderer.cpp create mode 100644 libs/hwui/jni/android_graphics_HardwareRendererObserver.cpp create mode 100644 libs/hwui/jni/android_graphics_HardwareRendererObserver.h create mode 100644 libs/hwui/jni/android_graphics_Matrix.cpp create mode 100644 libs/hwui/jni/android_graphics_Matrix.h create mode 100644 libs/hwui/jni/android_graphics_Picture.cpp create mode 100644 libs/hwui/jni/android_graphics_RenderNode.cpp create mode 100644 libs/hwui/jni/android_graphics_TextureLayer.cpp create mode 100644 libs/hwui/jni/android_graphics_animation_NativeInterpolatorFactory.cpp create mode 100644 libs/hwui/jni/android_graphics_animation_RenderNodeAnimator.cpp create mode 100644 libs/hwui/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp create mode 100644 libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp create mode 100644 libs/hwui/jni/android_nio_utils.cpp create mode 100644 libs/hwui/jni/android_nio_utils.h create mode 100644 libs/hwui/jni/android_util_PathParser.cpp create mode 100644 libs/hwui/jni/fonts/Font.cpp create mode 100644 libs/hwui/jni/fonts/FontFamily.cpp create mode 100644 libs/hwui/jni/pdf/PdfDocument.cpp create mode 100644 libs/hwui/jni/pdf/PdfEditor.cpp create mode 100644 libs/hwui/jni/pdf/PdfRenderer.cpp create mode 100644 libs/hwui/jni/pdf/PdfUtils.cpp create mode 100644 libs/hwui/jni/pdf/PdfUtils.h create mode 100644 libs/hwui/jni/text/LineBreaker.cpp create mode 100644 libs/hwui/jni/text/MeasuredText.cpp (limited to 'libs') diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 81dedda5341d..954e4daeffa7 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -151,10 +151,230 @@ cc_defaults { }, } +// ------------------------ +// APEX +// ------------------------ + +cc_library_headers { + name: "android_graphics_apex_headers", + + host_supported: true, + export_include_dirs: [ + "apex/include", + ], + target: { + windows: { + enabled: true, + }, + } +} + +cc_defaults { + name: "android_graphics_apex", + host_supported: true, + cflags: [ + "-Wno-unused-parameter", + "-Wno-non-virtual-dtor", + "-Wno-maybe-uninitialized", + "-Wno-parentheses", + "-Wall", + "-Werror", + "-Wno-error=deprecated-declarations", + "-Wunused", + "-Wunreachable-code", + ], + + cppflags: ["-Wno-conversion-null"], + + srcs: [ + "apex/android_matrix.cpp", + "apex/android_paint.cpp", + "apex/android_region.cpp", + ], + + header_libs: [ "android_graphics_apex_headers" ], + + target: { + android: { + srcs: [ // sources that depend on android only libraries + "apex/android_bitmap.cpp", + "apex/android_canvas.cpp", + "apex/jni_runtime.cpp", + "apex/renderthread.cpp", + ], + }, + }, +} + +// ------------------------ +// Android Graphics JNI +// ------------------------ + +cc_library_headers { + name: "android_graphics_jni_headers", + + host_supported: true, + export_include_dirs: [ + "jni", + ], + target: { + windows: { + enabled: true, + }, + } +} + +cc_defaults { + name: "android_graphics_jni", + host_supported: true, + cflags: [ + "-Wno-unused-parameter", + "-Wno-non-virtual-dtor", + "-Wno-maybe-uninitialized", + "-Wno-parentheses", + + "-DGL_GLEXT_PROTOTYPES", + "-DEGL_EGLEXT_PROTOTYPES", + + "-DU_USING_ICU_NAMESPACE=0", + + "-Wall", + "-Werror", + "-Wno-error=deprecated-declarations", + "-Wunused", + "-Wunreachable-code", + ], + + cppflags: ["-Wno-conversion-null"], + + srcs: [ + "jni/android_graphics_animation_NativeInterpolatorFactory.cpp", + "jni/android_graphics_animation_RenderNodeAnimator.cpp", + "jni/android_graphics_Canvas.cpp", + "jni/android_graphics_ColorSpace.cpp", + "jni/android_graphics_drawable_AnimatedVectorDrawable.cpp", + "jni/android_graphics_drawable_VectorDrawable.cpp", + "jni/android_graphics_HardwareRendererObserver.cpp", + "jni/android_graphics_Matrix.cpp", + "jni/android_graphics_Picture.cpp", + "jni/android_graphics_DisplayListCanvas.cpp", + "jni/android_graphics_RenderNode.cpp", + "jni/android_nio_utils.cpp", + "jni/android_util_PathParser.cpp", + + "jni/Bitmap.cpp", + "jni/BitmapFactory.cpp", + "jni/ByteBufferStreamAdaptor.cpp", + "jni/Camera.cpp", + "jni/CanvasProperty.cpp", + "jni/ColorFilter.cpp", + "jni/CreateJavaOutputStreamAdaptor.cpp", + "jni/FontFamily.cpp", + "jni/FontUtils.cpp", + "jni/Graphics.cpp", + "jni/ImageDecoder.cpp", + "jni/Interpolator.cpp", + "jni/MaskFilter.cpp", + "jni/NinePatch.cpp", + "jni/NinePatchPeeker.cpp", + "jni/Paint.cpp", + "jni/PaintFilter.cpp", + "jni/Path.cpp", + "jni/PathEffect.cpp", + "jni/PathMeasure.cpp", + "jni/Picture.cpp", + "jni/Region.cpp", + "jni/Shader.cpp", + "jni/Typeface.cpp", + "jni/Utils.cpp", + "jni/YuvToJpegEncoder.cpp", + "jni/fonts/Font.cpp", + "jni/fonts/FontFamily.cpp", + "jni/text/LineBreaker.cpp", + "jni/text/MeasuredText.cpp", + ], + + header_libs: [ "android_graphics_jni_headers" ], + + include_dirs: [ + "external/skia/include/private", + "external/skia/src/codec", + "external/skia/src/core", + "external/skia/src/effects", + "external/skia/src/image", + "external/skia/src/images", + ], + + shared_libs: [ + "libbase", + "libbinder", + "libcutils", + "libharfbuzz_ng", + "liblog", + "libminikin", + "libnativehelper", + "libz", + "libziparchive", + "libjpeg", + ], + + target: { + android: { + srcs: [ // sources that depend on android only libraries + "jni/AnimatedImageDrawable.cpp", + "jni/android_graphics_TextureLayer.cpp", + "jni/android_graphics_HardwareRenderer.cpp", + "jni/BitmapRegionDecoder.cpp", + "jni/GIFMovie.cpp", + "jni/GraphicsStatsService.cpp", + "jni/Movie.cpp", + "jni/MovieImpl.cpp", + "jni/pdf/PdfDocument.cpp", + "jni/pdf/PdfEditor.cpp", + "jni/pdf/PdfRenderer.cpp", + "jni/pdf/PdfUtils.cpp", + ], + shared_libs: [ + "libandroidfw", + "libmediandk", + "libnativedisplay", + "libnativewindow", + "libstatspull", + "libstatssocket", + "libpdfium", + ], + static_libs: [ + "libgif", + "libstatslog", + ], + }, + host: { + cflags: [ + "-Wno-unused-const-variable", + "-Wno-unused-function", + ], + static_libs: [ + "libandroidfw", + ], + } + }, +} + // ------------------------ // library // ------------------------ +cc_library_headers { + name: "libhwui_internal_headers", + + host_supported: true, + export_include_dirs: [ + ".", + ], + header_libs: [ "android_graphics_jni_headers" ], + export_header_lib_headers: [ "android_graphics_jni_headers" ], +} + cc_defaults { name: "libhwui_defaults", defaults: ["hwui_defaults"], @@ -205,11 +425,8 @@ cc_defaults { export_proto_headers: true, }, - export_include_dirs: ["."], - target: { android: { - srcs: [ "hwui/AnimatedImageThread.cpp", "pipeline/skia/ATraceMemoryDump.cpp", @@ -274,7 +491,10 @@ cc_library { host_supported: true, defaults: [ "libhwui_defaults", + "android_graphics_apex", + "android_graphics_jni", ], + export_header_lib_headers: ["android_graphics_apex_headers"], } cc_library_static { diff --git a/libs/hwui/apex/TypeCast.h b/libs/hwui/apex/TypeCast.h new file mode 100644 index 000000000000..96721d007951 --- /dev/null +++ b/libs/hwui/apex/TypeCast.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2019 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_TYPECAST_H +#define ANDROID_GRAPHICS_TYPECAST_H + +struct ABitmap; +struct ACanvas; +struct APaint; + +namespace android { + + class Bitmap; + class Canvas; + class Paint; + + class TypeCast { + public: + static inline Bitmap& toBitmapRef(const ABitmap* bitmap) { + return const_cast(reinterpret_cast(*bitmap)); + } + + static inline Bitmap* toBitmap(ABitmap* bitmap) { + return reinterpret_cast(bitmap); + } + + static inline ABitmap* toABitmap(Bitmap* bitmap) { + return reinterpret_cast(bitmap); + } + + static inline Canvas* toCanvas(ACanvas* canvas) { + return reinterpret_cast(canvas); + } + + static inline ACanvas* toACanvas(Canvas* canvas) { + return reinterpret_cast(canvas); + } + + static inline const Paint& toPaintRef(const APaint* paint) { + return reinterpret_cast(*paint); + } + + static inline const Paint* toPaint(const APaint* paint) { + return reinterpret_cast(paint); + } + + static inline Paint* toPaint(APaint* paint) { + return reinterpret_cast(paint); + } + + static inline APaint* toAPaint(Paint* paint) { + return reinterpret_cast(paint); + } + }; +}; // namespace android + +#endif // ANDROID_GRAPHICS_TYPECAST_H diff --git a/libs/hwui/apex/android_bitmap.cpp b/libs/hwui/apex/android_bitmap.cpp new file mode 100644 index 000000000000..decd19071944 --- /dev/null +++ b/libs/hwui/apex/android_bitmap.cpp @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2019 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 "Bitmap" +#include + +#include "android/graphics/bitmap.h" +#include "TypeCast.h" +#include "GraphicsJNI.h" + +#include +#include +#include + +using namespace android; + +ABitmap* ABitmap_acquireBitmapFromJava(JNIEnv* env, jobject bitmapObj) { + Bitmap* bitmap = GraphicsJNI::getNativeBitmap(env, bitmapObj); + if (bitmap) { + bitmap->ref(); + return TypeCast::toABitmap(bitmap); + } + return nullptr; +} + +void ABitmap_acquireRef(ABitmap* bitmap) { + SkSafeRef(TypeCast::toBitmap(bitmap)); +} + +void ABitmap_releaseRef(ABitmap* bitmap) { + SkSafeUnref(TypeCast::toBitmap(bitmap)); +} + +static AndroidBitmapFormat getFormat(const SkImageInfo& info) { + switch (info.colorType()) { + case kN32_SkColorType: + return ANDROID_BITMAP_FORMAT_RGBA_8888; + case kRGB_565_SkColorType: + return ANDROID_BITMAP_FORMAT_RGB_565; + case kARGB_4444_SkColorType: + return ANDROID_BITMAP_FORMAT_RGBA_4444; + case kAlpha_8_SkColorType: + return ANDROID_BITMAP_FORMAT_A_8; + case kRGBA_F16_SkColorType: + return ANDROID_BITMAP_FORMAT_RGBA_F16; + default: + return ANDROID_BITMAP_FORMAT_NONE; + } +} + +static SkColorType getColorType(AndroidBitmapFormat format) { + switch (format) { + case ANDROID_BITMAP_FORMAT_RGBA_8888: + return kN32_SkColorType; + case ANDROID_BITMAP_FORMAT_RGB_565: + return kRGB_565_SkColorType; + case ANDROID_BITMAP_FORMAT_RGBA_4444: + return kARGB_4444_SkColorType; + case ANDROID_BITMAP_FORMAT_A_8: + return kAlpha_8_SkColorType; + case ANDROID_BITMAP_FORMAT_RGBA_F16: + return kRGBA_F16_SkColorType; + default: + return kUnknown_SkColorType; + } +} + +static uint32_t getAlphaFlags(const SkImageInfo& info) { + switch (info.alphaType()) { + case kUnknown_SkAlphaType: + LOG_ALWAYS_FATAL("Bitmap has no alpha type"); + break; + case kOpaque_SkAlphaType: + return ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE; + case kPremul_SkAlphaType: + return ANDROID_BITMAP_FLAGS_ALPHA_PREMUL; + case kUnpremul_SkAlphaType: + return ANDROID_BITMAP_FLAGS_ALPHA_UNPREMUL; + } +} + +static uint32_t getInfoFlags(const SkImageInfo& info, bool isHardware) { + uint32_t flags = getAlphaFlags(info); + if (isHardware) { + flags |= ANDROID_BITMAP_FLAGS_IS_HARDWARE; + } + return flags; +} + +ABitmap* ABitmap_copy(ABitmap* srcBitmapHandle, AndroidBitmapFormat dstFormat) { + SkColorType dstColorType = getColorType(dstFormat); + if (srcBitmapHandle && dstColorType != kUnknown_SkColorType) { + SkBitmap srcBitmap; + TypeCast::toBitmap(srcBitmapHandle)->getSkBitmap(&srcBitmap); + + sk_sp dstBitmap = + Bitmap::allocateHeapBitmap(srcBitmap.info().makeColorType(dstColorType)); + if (dstBitmap && srcBitmap.readPixels(dstBitmap->info(), dstBitmap->pixels(), + dstBitmap->rowBytes(), 0, 0)) { + return TypeCast::toABitmap(dstBitmap.release()); + } + } + return nullptr; +} + +static AndroidBitmapInfo getInfo(const SkImageInfo& imageInfo, uint32_t rowBytes, bool isHardware) { + AndroidBitmapInfo info; + info.width = imageInfo.width(); + info.height = imageInfo.height(); + info.stride = rowBytes; + info.format = getFormat(imageInfo); + info.flags = getInfoFlags(imageInfo, isHardware); + return info; +} + +AndroidBitmapInfo ABitmap_getInfo(ABitmap* bitmapHandle) { + Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle); + return getInfo(bitmap->info(), bitmap->rowBytes(), bitmap->isHardware()); +} + +ADataSpace ABitmap_getDataSpace(ABitmap* bitmapHandle) { + Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle); + const SkImageInfo& info = bitmap->info(); + return (ADataSpace)uirenderer::ColorSpaceToADataSpace(info.colorSpace(), info.colorType()); +} + +AndroidBitmapInfo ABitmap_getInfoFromJava(JNIEnv* env, jobject bitmapObj) { + uint32_t rowBytes = 0; + bool isHardware = false; + SkImageInfo imageInfo = GraphicsJNI::getBitmapInfo(env, bitmapObj, &rowBytes, &isHardware); + return getInfo(imageInfo, rowBytes, isHardware); +} + +void* ABitmap_getPixels(ABitmap* bitmapHandle) { + Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle); + if (bitmap->isHardware()) { + return nullptr; + } + return bitmap->pixels(); +} + +AndroidBitmapFormat ABitmapConfig_getFormatFromConfig(JNIEnv* env, jobject bitmapConfigObj) { + return GraphicsJNI::getFormatFromConfig(env, bitmapConfigObj); +} + +jobject ABitmapConfig_getConfigFromFormat(JNIEnv* env, AndroidBitmapFormat format) { + return GraphicsJNI::getConfigFromFormat(env, format); +} + +void ABitmap_notifyPixelsChanged(ABitmap* bitmapHandle) { + Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle); + if (bitmap->isImmutable()) { + ALOGE("Attempting to modify an immutable Bitmap!"); + } + return bitmap->notifyPixelsChanged(); +} + +namespace { +SkAlphaType getAlphaType(const AndroidBitmapInfo* info) { + switch (info->flags & ANDROID_BITMAP_FLAGS_ALPHA_MASK) { + case ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE: + return kOpaque_SkAlphaType; + case ANDROID_BITMAP_FLAGS_ALPHA_PREMUL: + return kPremul_SkAlphaType; + case ANDROID_BITMAP_FLAGS_ALPHA_UNPREMUL: + return kUnpremul_SkAlphaType; + default: + return kUnknown_SkAlphaType; + } +} + +class CompressWriter : public SkWStream { +public: + CompressWriter(void* userContext, AndroidBitmap_CompressWriteFunc fn) + : mUserContext(userContext), mFn(fn), mBytesWritten(0) {} + + bool write(const void* buffer, size_t size) override { + if (mFn(mUserContext, buffer, size)) { + mBytesWritten += size; + return true; + } + return false; + } + + size_t bytesWritten() const override { return mBytesWritten; } + +private: + void* mUserContext; + AndroidBitmap_CompressWriteFunc mFn; + size_t mBytesWritten; +}; + +} // anonymous namespace + +int ABitmap_compress(const AndroidBitmapInfo* info, ADataSpace dataSpace, const void* pixels, + AndroidBitmapCompressFormat inFormat, int32_t quality, void* userContext, + AndroidBitmap_CompressWriteFunc fn) { + Bitmap::JavaCompressFormat format; + switch (inFormat) { + case ANDROID_BITMAP_COMPRESS_FORMAT_JPEG: + format = Bitmap::JavaCompressFormat::Jpeg; + break; + case ANDROID_BITMAP_COMPRESS_FORMAT_PNG: + format = Bitmap::JavaCompressFormat::Png; + break; + case ANDROID_BITMAP_COMPRESS_FORMAT_WEBP_LOSSY: + format = Bitmap::JavaCompressFormat::WebpLossy; + break; + case ANDROID_BITMAP_COMPRESS_FORMAT_WEBP_LOSSLESS: + format = Bitmap::JavaCompressFormat::WebpLossless; + break; + default: + // kWEBP_JavaEncodeFormat is a valid parameter for Bitmap::compress, + // for the deprecated Bitmap.CompressFormat.WEBP, but it should not + // be provided via the NDK. Other integers are likewise invalid. + return ANDROID_BITMAP_RESULT_BAD_PARAMETER; + } + + SkColorType colorType; + switch (info->format) { + case ANDROID_BITMAP_FORMAT_RGBA_8888: + colorType = kN32_SkColorType; + break; + case ANDROID_BITMAP_FORMAT_RGB_565: + colorType = kRGB_565_SkColorType; + break; + case ANDROID_BITMAP_FORMAT_A_8: + // FIXME b/146637821: Should this encode as grayscale? We should + // make the same decision as for encoding an android.graphics.Bitmap. + // Note that encoding kAlpha_8 as WebP or JPEG will fail. Encoding + // it to PNG encodes as GRAY+ALPHA with a secret handshake that we + // only care about the alpha. I'm not sure whether Android decoding + // APIs respect that handshake. + colorType = kAlpha_8_SkColorType; + break; + case ANDROID_BITMAP_FORMAT_RGBA_F16: + colorType = kRGBA_F16_SkColorType; + break; + default: + return ANDROID_BITMAP_RESULT_BAD_PARAMETER; + } + + auto alphaType = getAlphaType(info); + if (alphaType == kUnknown_SkAlphaType) { + return ANDROID_BITMAP_RESULT_BAD_PARAMETER; + } + + sk_sp cs; + if (info->format == ANDROID_BITMAP_FORMAT_A_8) { + // FIXME: A Java Bitmap with ALPHA_8 never has a ColorSpace. So should + // we force that here (as I'm doing now) or should we treat anything + // besides ADATASPACE_UNKNOWN as an error? + cs = nullptr; + } else { + cs = uirenderer::DataSpaceToColorSpace((android_dataspace) dataSpace); + // DataSpaceToColorSpace treats UNKNOWN as SRGB, but compress forces the + // client to specify SRGB if that is what they want. + if (!cs || dataSpace == ADATASPACE_UNKNOWN) { + return ANDROID_BITMAP_RESULT_BAD_PARAMETER; + } + } + + { + size_t size; + if (!Bitmap::computeAllocationSize(info->stride, info->height, &size)) { + return ANDROID_BITMAP_RESULT_BAD_PARAMETER; + } + } + + auto imageInfo = + SkImageInfo::Make(info->width, info->height, colorType, alphaType, std::move(cs)); + SkBitmap bitmap; + // We are not going to modify the pixels, but installPixels expects them to + // not be const, since for all it knows we might want to draw to the SkBitmap. + if (!bitmap.installPixels(imageInfo, const_cast(pixels), info->stride)) { + return ANDROID_BITMAP_RESULT_BAD_PARAMETER; + } + + CompressWriter stream(userContext, fn); + return Bitmap::compress(bitmap, format, quality, &stream) ? ANDROID_BITMAP_RESULT_SUCCESS + : ANDROID_BITMAP_RESULT_JNI_EXCEPTION; +} + +AHardwareBuffer* ABitmap_getHardwareBuffer(ABitmap* bitmapHandle) { + Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle); + AHardwareBuffer* buffer = bitmap->hardwareBuffer(); + if (buffer) { + AHardwareBuffer_acquire(buffer); + } + return buffer; +} diff --git a/libs/hwui/apex/android_canvas.cpp b/libs/hwui/apex/android_canvas.cpp new file mode 100644 index 000000000000..2a939efed9bb --- /dev/null +++ b/libs/hwui/apex/android_canvas.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2019 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 "android/graphics/canvas.h" + +#include "TypeCast.h" +#include "GraphicsJNI.h" + +#include +#include + +#include +#include + +using namespace android; + +/* + * Converts a buffer and dataspace into an SkBitmap only if the resulting bitmap can be treated as a + * rendering destination for a Canvas. If the buffer is null or the format is one that we cannot + * render into with a Canvas then false is returned and the outBitmap param is unmodified. + */ +static bool convert(const ANativeWindow_Buffer* buffer, + int32_t /*android_dataspace_t*/ dataspace, + SkBitmap* outBitmap) { + if (buffer == nullptr) { + return false; + } + + sk_sp cs(uirenderer::DataSpaceToColorSpace((android_dataspace)dataspace)); + SkImageInfo imageInfo = uirenderer::ANativeWindowToImageInfo(*buffer, cs); + size_t rowBytes = buffer->stride * imageInfo.bytesPerPixel(); + + // If SkSurface::MakeRasterDirect fails then we should as well as we will not be able to + // draw into the canvas. + sk_sp surface = SkSurface::MakeRasterDirect(imageInfo, buffer->bits, rowBytes); + if (surface.get() != nullptr) { + if (outBitmap) { + outBitmap->setInfo(imageInfo, rowBytes); + outBitmap->setPixels(buffer->bits); + } + return true; + } + return false; +} + +bool ACanvas_isSupportedPixelFormat(int32_t bufferFormat) { + char pixels[8]; + ANativeWindow_Buffer buffer { 1, 1, 1, bufferFormat, pixels, {0} }; + return convert(&buffer, HAL_DATASPACE_UNKNOWN, nullptr); +} + +ACanvas* ACanvas_getNativeHandleFromJava(JNIEnv* env, jobject canvasObj) { + return TypeCast::toACanvas(GraphicsJNI::getNativeCanvas(env, canvasObj)); +} + +ACanvas* ACanvas_createCanvas(const ANativeWindow_Buffer* buffer, + int32_t /*android_dataspace_t*/ dataspace) { + SkBitmap bitmap; + bool isValidBuffer = convert(buffer, dataspace, &bitmap); + return isValidBuffer ? TypeCast::toACanvas(Canvas::create_canvas(bitmap)) : nullptr; +} + +void ACanvas_destroyCanvas(ACanvas* canvas) { + delete TypeCast::toCanvas(canvas); +} + +bool ACanvas_setBuffer(ACanvas* canvas, const ANativeWindow_Buffer* buffer, + int32_t /*android_dataspace_t*/ dataspace) { + SkBitmap bitmap; + bool isValidBuffer = (buffer == nullptr) ? false : convert(buffer, dataspace, &bitmap); + TypeCast::toCanvas(canvas)->setBitmap(bitmap); + return isValidBuffer; +} + +void ACanvas_clipRect(ACanvas* canvas, const ARect* clipRect, bool /*doAA*/) { + //TODO update Canvas to take antialias param + TypeCast::toCanvas(canvas)->clipRect(clipRect->left, clipRect->top, clipRect->right, + clipRect->bottom, SkClipOp::kIntersect); +} + +void ACanvas_clipOutRect(ACanvas* canvas, const ARect* clipRect, bool /*doAA*/) { + //TODO update Canvas to take antialias param + TypeCast::toCanvas(canvas)->clipRect(clipRect->left, clipRect->top, clipRect->right, + clipRect->bottom, SkClipOp::kDifference); +} + +void ACanvas_drawRect(ACanvas* canvas, const ARect* rect, const APaint* paint) { + TypeCast::toCanvas(canvas)->drawRect(rect->left, rect->top, rect->right, rect->bottom, + TypeCast::toPaintRef(paint)); +} + +void ACanvas_drawBitmap(ACanvas* canvas, const ABitmap* bitmap, float left, float top, + const APaint* paint) { + TypeCast::toCanvas(canvas)->drawBitmap(TypeCast::toBitmapRef(bitmap), left, top, + TypeCast::toPaint(paint)); +} diff --git a/libs/hwui/apex/android_matrix.cpp b/libs/hwui/apex/android_matrix.cpp new file mode 100644 index 000000000000..693b22b62663 --- /dev/null +++ b/libs/hwui/apex/android_matrix.cpp @@ -0,0 +1,37 @@ +/* + * Copyright 2020 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 "android/graphics/matrix.h" +#include "android_graphics_Matrix.h" + +bool AMatrix_getContents(JNIEnv* env, jobject matrixObj, float values[9]) { + static_assert(SkMatrix::kMScaleX == 0, "SkMatrix unexpected index"); + static_assert(SkMatrix::kMSkewX == 1, "SkMatrix unexpected index"); + static_assert(SkMatrix::kMTransX == 2, "SkMatrix unexpected index"); + static_assert(SkMatrix::kMSkewY == 3, "SkMatrix unexpected index"); + static_assert(SkMatrix::kMScaleY == 4, "SkMatrix unexpected index"); + static_assert(SkMatrix::kMTransY == 5, "SkMatrix unexpected index"); + static_assert(SkMatrix::kMPersp0 == 6, "SkMatrix unexpected index"); + static_assert(SkMatrix::kMPersp1 == 7, "SkMatrix unexpected index"); + static_assert(SkMatrix::kMPersp2 == 8, "SkMatrix unexpected index"); + + SkMatrix* m = android::android_graphics_Matrix_getSkMatrix(env, matrixObj); + if (m != nullptr) { + m->get9(values); + return true; + } + return false; +} diff --git a/libs/hwui/apex/android_paint.cpp b/libs/hwui/apex/android_paint.cpp new file mode 100644 index 000000000000..70bd085343ce --- /dev/null +++ b/libs/hwui/apex/android_paint.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2019 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 "android/graphics/paint.h" + +#include "TypeCast.h" + +#include + +using namespace android; + + +APaint* APaint_createPaint() { + return TypeCast::toAPaint(new Paint()); +} + +void APaint_destroyPaint(APaint* paint) { + delete TypeCast::toPaint(paint); +} + +static SkBlendMode convertBlendMode(ABlendMode blendMode) { + switch (blendMode) { + case ABLEND_MODE_CLEAR: + return SkBlendMode::kClear; + case ABLEND_MODE_SRC_OVER: + return SkBlendMode::kSrcOver; + case ABLEND_MODE_SRC: + return SkBlendMode::kSrc; + } +} + +void APaint_setBlendMode(APaint* paint, ABlendMode blendMode) { + TypeCast::toPaint(paint)->setBlendMode(convertBlendMode(blendMode)); +} diff --git a/libs/hwui/apex/android_region.cpp b/libs/hwui/apex/android_region.cpp new file mode 100644 index 000000000000..2030e7e69861 --- /dev/null +++ b/libs/hwui/apex/android_region.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2019 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 "android/graphics/region.h" + +#include "GraphicsJNI.h" + +#include + +static inline SkRegion::Iterator* ARegionIter_to_SkRegionIter(ARegionIterator* iterator) { + return reinterpret_cast(iterator); +} + +static inline ARegionIterator* SkRegionIter_to_ARegionIter(SkRegion::Iterator* iterator) { + return reinterpret_cast(iterator); +} + +ARegionIterator* ARegionIterator_acquireIterator(JNIEnv* env, jobject regionObj) { + SkRegion* region = GraphicsJNI::getNativeRegion(env, regionObj); + return (!region) ? nullptr : SkRegionIter_to_ARegionIter(new SkRegion::Iterator(*region)); +} + +void ARegionIterator_releaseIterator(ARegionIterator* iterator) { + delete ARegionIter_to_SkRegionIter(iterator); +} + +bool ARegionIterator_isComplex(ARegionIterator* iterator) { + return ARegionIter_to_SkRegionIter(iterator)->rgn()->isComplex(); +} + +bool ARegionIterator_isDone(ARegionIterator* iterator) { + return ARegionIter_to_SkRegionIter(iterator)->done(); +} + +void ARegionIterator_next(ARegionIterator* iterator) { + ARegionIter_to_SkRegionIter(iterator)->next(); +} + +ARect ARegionIterator_getRect(ARegionIterator* iterator) { + const SkIRect& rect = ARegionIter_to_SkRegionIter(iterator)->rect(); + return { rect.fLeft, rect.fTop, rect.fRight, rect.fBottom }; +} + +ARect ARegionIterator_getTotalBounds(ARegionIterator* iterator) { + const SkIRect& bounds = ARegionIter_to_SkRegionIter(iterator)->rgn()->getBounds(); + return { bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom }; +} diff --git a/libs/hwui/apex/include/android/graphics/bitmap.h b/libs/hwui/apex/include/android/graphics/bitmap.h new file mode 100644 index 000000000000..45fec2ab7b43 --- /dev/null +++ b/libs/hwui/apex/include/android/graphics/bitmap.h @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2019 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_BITMAP_H +#define ANDROID_GRAPHICS_BITMAP_H + +#include +#include +#include +#include + +struct AHardwareBuffer; + +__BEGIN_DECLS + +/** + * Opaque handle for a native graphics bitmap. + */ +typedef struct ABitmap ABitmap; + +/** + * Retrieve bitmapInfo for the provided java bitmap even if it has been recycled. In the case of a + * recycled bitmap the values contained in the bitmap before it was recycled are returned. + * + * NOTE: This API does not need to remain as an APEX API if/when we pull libjnigraphics into the + * UI module. + */ +AndroidBitmapInfo ABitmap_getInfoFromJava(JNIEnv* env, jobject bitmapObj); + +/** + * + * @return ptr to an opaque handle to the native bitmap or null if the java bitmap has been recycled + * or does not exist. + */ +ABitmap* ABitmap_acquireBitmapFromJava(JNIEnv* env, jobject bitmapObj); + +ABitmap* ABitmap_copy(ABitmap* srcBitmap, AndroidBitmapFormat dstFormat); + +void ABitmap_acquireRef(ABitmap* bitmap); +void ABitmap_releaseRef(ABitmap* bitmap); + +AndroidBitmapInfo ABitmap_getInfo(ABitmap* bitmap); +ADataSpace ABitmap_getDataSpace(ABitmap* bitmap); + +void* ABitmap_getPixels(ABitmap* bitmap); +void ABitmap_notifyPixelsChanged(ABitmap* bitmap); + +AndroidBitmapFormat ABitmapConfig_getFormatFromConfig(JNIEnv* env, jobject bitmapConfigObj); +jobject ABitmapConfig_getConfigFromFormat(JNIEnv* env, AndroidBitmapFormat format); + +// NDK access +int ABitmap_compress(const AndroidBitmapInfo* info, ADataSpace dataSpace, const void* pixels, + AndroidBitmapCompressFormat format, int32_t quality, void* userContext, + AndroidBitmap_CompressWriteFunc); +/** + * Retrieve the native object associated with a HARDWARE Bitmap. + * + * Client must not modify it while a Bitmap is wrapping it. + * + * @param bitmap Handle to an android.graphics.Bitmap. + * @return on success, a pointer to the + * AHardwareBuffer associated with bitmap. This acquires + * a reference on the buffer, and the client must call + * AHardwareBuffer_release when finished with it. + */ +AHardwareBuffer* ABitmap_getHardwareBuffer(ABitmap* bitmap); + +__END_DECLS + +#ifdef __cplusplus +namespace android { +namespace graphics { + class Bitmap { + public: + Bitmap() : mBitmap(nullptr) {} + Bitmap(JNIEnv* env, jobject bitmapObj) : + mBitmap(ABitmap_acquireBitmapFromJava(env, bitmapObj)) {} + Bitmap(const Bitmap& src) : mBitmap(src.mBitmap) { ABitmap_acquireRef(src.mBitmap); } + ~Bitmap() { ABitmap_releaseRef(mBitmap); } + + // copy operator + Bitmap& operator=(const Bitmap& other) { + if (&other != this) { + ABitmap_releaseRef(mBitmap); + mBitmap = other.mBitmap; + ABitmap_acquireRef(mBitmap); + } + return *this; + } + + // move operator + Bitmap& operator=(Bitmap&& other) { + if (&other != this) { + ABitmap_releaseRef(mBitmap); + mBitmap = other.mBitmap; + other.mBitmap = nullptr; + } + return *this; + } + + Bitmap copy(AndroidBitmapFormat dstFormat) const { + return Bitmap(ABitmap_copy(mBitmap, dstFormat)); + } + + bool isValid() const { return mBitmap != nullptr; } + bool isEmpty() const { + AndroidBitmapInfo info = getInfo(); + return info.width <= 0 || info.height <= 0; + } + void reset() { + ABitmap_releaseRef(mBitmap); + mBitmap = nullptr; + } + + ABitmap* get() const { return mBitmap; } + + AndroidBitmapInfo getInfo() const { return ABitmap_getInfo(mBitmap); } + ADataSpace getDataSpace() const { return ABitmap_getDataSpace(mBitmap); } + void* getPixels() const { return ABitmap_getPixels(mBitmap); } + void notifyPixelsChanged() const { ABitmap_notifyPixelsChanged(mBitmap); } + AHardwareBuffer* getHardwareBuffer() const { return ABitmap_getHardwareBuffer(mBitmap); } + + private: + // takes ownership of the provided ABitmap + Bitmap(ABitmap* bitmap) : mBitmap(bitmap) {} + + ABitmap* mBitmap; + }; +}; // namespace graphics +}; // namespace android +#endif // __cplusplus + +#endif // ANDROID_GRAPHICS_BITMAP_H diff --git a/libs/hwui/apex/include/android/graphics/canvas.h b/libs/hwui/apex/include/android/graphics/canvas.h new file mode 100644 index 000000000000..6fd6b0693b37 --- /dev/null +++ b/libs/hwui/apex/include/android/graphics/canvas.h @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2019 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 +#include +#include +#include +#include + +__BEGIN_DECLS + +/** + * Opaque handle for a native graphics canvas. + */ +typedef struct ACanvas ACanvas; + +// One of AHardwareBuffer_Format. +bool ACanvas_isSupportedPixelFormat(int32_t bufferFormat); + +/** + * Returns a native handle to a Java android.graphics.Canvas + * + * @return ACanvas* that is only valid for the life of the jobject. + */ +ACanvas* ACanvas_getNativeHandleFromJava(JNIEnv* env, jobject canvas); + +/** + * Creates a canvas that wraps the buffer + * + * @param buffer is a required param. If no buffer is provided a nullptr will be returned. + */ +ACanvas* ACanvas_createCanvas(const ANativeWindow_Buffer* buffer, + int32_t /*android_dataspace_t*/ dataspace); + +void ACanvas_destroyCanvas(ACanvas* canvas); + +/** + * Updates the canvas to render into the pixels in the provided buffer + * + * @param buffer The buffer that will provide the backing store for this canvas. The buffer must + * remain valid until the this method is called again with either another active + * buffer or nullptr. If nullptr is given the canvas will release the previous buffer + * and set an empty backing store. + * @return A boolean value indicating whether or not the buffer was successfully set. If false the + * method will behave as if nullptr were passed as the input buffer and the previous buffer + * will still be released. + */ +bool ACanvas_setBuffer(ACanvas* canvas, const ANativeWindow_Buffer* buffer, + int32_t /*android_dataspace_t*/ dataspace); + +/** + * Clips operations on the canvas to the intersection of the current clip and the provided clipRect. + * + * @param clipRect required + */ +void ACanvas_clipRect(ACanvas* canvas, const ARect* clipRect, bool doAntiAlias = false); + +/** + * Clips operations on the canvas to the difference of the current clip and the provided clipRect. + * + * @param clipRect required + */ +void ACanvas_clipOutRect(ACanvas* canvas, const ARect* clipRect, bool doAntiAlias = false); + +/** + * + * @param rect required + * @param paint required + */ +void ACanvas_drawRect(ACanvas* canvas, const ARect* rect, const APaint* paint); + +/** + * + * @param bitmap required + * @param left + * @param top + * @param paint + */ +void ACanvas_drawBitmap(ACanvas* canvas, const ABitmap* bitmap, float left, float top, + const APaint* paint); + +__END_DECLS + +#ifdef __cplusplus +namespace android { +namespace graphics { + class Canvas { + public: + Canvas(JNIEnv* env, jobject canvasObj) : + mCanvas(ACanvas_getNativeHandleFromJava(env, canvasObj)), + mOwnedPtr(false) {} + Canvas(const ANativeWindow_Buffer& buffer, int32_t /*android_dataspace_t*/ dataspace) : + mCanvas(ACanvas_createCanvas(&buffer, dataspace)), + mOwnedPtr(true) {} + ~Canvas() { + if (mOwnedPtr) { + ACanvas_destroyCanvas(mCanvas); + } + } + + bool setBuffer(const ANativeWindow_Buffer* buffer, + int32_t /*android_dataspace_t*/ dataspace) { + return ACanvas_setBuffer(mCanvas, buffer, dataspace); + } + + void clipRect(const ARect& clipRect, bool doAntiAlias = false) { + ACanvas_clipRect(mCanvas, &clipRect, doAntiAlias); + } + + void drawRect(const ARect& rect, const Paint& paint) { + ACanvas_drawRect(mCanvas, &rect, &paint.get()); + } + void drawBitmap(const Bitmap& bitmap, float left, float top, const Paint* paint) { + const APaint* aPaint = (paint) ? &paint->get() : nullptr; + ACanvas_drawBitmap(mCanvas, bitmap.get(), left, top, aPaint); + } + + private: + ACanvas* mCanvas; + const bool mOwnedPtr; + }; +}; // namespace graphics +}; // namespace android +#endif // __cplusplus + +#endif // ANDROID_GRAPHICS_CANVAS_H \ No newline at end of file diff --git a/libs/hwui/apex/include/android/graphics/jni_runtime.h b/libs/hwui/apex/include/android/graphics/jni_runtime.h new file mode 100644 index 000000000000..872a9497ab90 --- /dev/null +++ b/libs/hwui/apex/include/android/graphics/jni_runtime.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2019 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_JNI_RUNTIME_H +#define ANDROID_GRAPHICS_JNI_RUNTIME_H + +#include + +__BEGIN_DECLS + +void init_android_graphics(); + +int register_android_graphics_classes(JNIEnv* env); + +void zygote_preload_graphics(); + +__END_DECLS + + +#endif // ANDROID_GRAPHICS_JNI_RUNTIME_H \ No newline at end of file diff --git a/libs/hwui/apex/include/android/graphics/matrix.h b/libs/hwui/apex/include/android/graphics/matrix.h new file mode 100644 index 000000000000..4039cd1b8f74 --- /dev/null +++ b/libs/hwui/apex/include/android/graphics/matrix.h @@ -0,0 +1,38 @@ +/* + * Copyright 2020 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_MATRIX_H +#define ANDROID_GRAPHICS_MATRIX_H + +#include +#include + +__BEGIN_DECLS + +/** + * Returns an array of floats that represents the 3x3 matrix of the java object. + * @param values The 9 values of the 3x3 matrix in the following order. + * values[0] = scaleX values[1] = skewX values[2] = transX + * values[3] = skewY values[4] = scaleY values[5] = transY + * values[6] = persp0 values[7] = persp1 values[8] = persp2 + * @return true if the values param was populated and false otherwise. + + */ +bool AMatrix_getContents(JNIEnv* env, jobject matrixObj, float values[9]); + +__END_DECLS + +#endif // ANDROID_GRAPHICS_MATRIX_H diff --git a/libs/hwui/apex/include/android/graphics/paint.h b/libs/hwui/apex/include/android/graphics/paint.h new file mode 100644 index 000000000000..5895e006bf93 --- /dev/null +++ b/libs/hwui/apex/include/android/graphics/paint.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2019 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_PAINT_H +#define ANDROID_GRAPHICS_PAINT_H + +#include + +__BEGIN_DECLS + +/** + * Opaque handle for a native graphics canvas. + */ +typedef struct APaint APaint; + +/** Bitmap pixel format. */ +enum ABlendMode { + /** replaces destination with zero: fully transparent */ + ABLEND_MODE_CLEAR = 0, + /** source over destination */ + ABLEND_MODE_SRC_OVER = 1, + /** replaces destination **/ + ABLEND_MODE_SRC = 2, +}; + +APaint* APaint_createPaint(); + +void APaint_destroyPaint(APaint* paint); + +void APaint_setBlendMode(APaint* paint, ABlendMode blendMode); + +__END_DECLS + +#ifdef __cplusplus +namespace android { +namespace graphics { + class Paint { + public: + Paint() : mPaint(APaint_createPaint()) {} + ~Paint() { APaint_destroyPaint(mPaint); } + + void setBlendMode(ABlendMode blendMode) { APaint_setBlendMode(mPaint, blendMode); } + + const APaint& get() const { return *mPaint; } + + private: + APaint* mPaint; + }; +}; // namespace graphics +}; // namespace android +#endif // __cplusplus + + +#endif // ANDROID_GRAPHICS_PAINT_H \ No newline at end of file diff --git a/libs/hwui/apex/include/android/graphics/region.h b/libs/hwui/apex/include/android/graphics/region.h new file mode 100644 index 000000000000..961067a8e2db --- /dev/null +++ b/libs/hwui/apex/include/android/graphics/region.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2019 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_REGION_H +#define ANDROID_GRAPHICS_REGION_H + +#include +#include +#include + +__BEGIN_DECLS + +/** +* Opaque handle for a native graphics region iterator. +*/ +typedef struct ARegionIterator ARegionIterator; + +/** + * Returns a iterator for a Java android.graphics.Region + * + * @param env + * @param region + * @return ARegionIterator that must be closed and must not live longer than the life + * of the jobject. It returns nullptr if the region is not a valid object. + */ +ARegionIterator* ARegionIterator_acquireIterator(JNIEnv* env, jobject region); + +void ARegionIterator_releaseIterator(ARegionIterator* iterator); + +bool ARegionIterator_isComplex(ARegionIterator* iterator); + +bool ARegionIterator_isDone(ARegionIterator* iterator); + +void ARegionIterator_next(ARegionIterator* iterator); + +ARect ARegionIterator_getRect(ARegionIterator* iterator); + +ARect ARegionIterator_getTotalBounds(ARegionIterator* iterator); + +__END_DECLS + +#ifdef __cplusplus +namespace android { +namespace graphics { + class RegionIterator { + public: + RegionIterator(JNIEnv* env, jobject region) + : mIterator(ARegionIterator_acquireIterator(env, region)) {} + ~RegionIterator() { ARegionIterator_releaseIterator(mIterator); } + + bool isValid() const { return mIterator != nullptr; } + bool isComplex() { return ARegionIterator_isComplex(mIterator); } + bool isDone() { return ARegionIterator_isDone(mIterator); } + void next() { ARegionIterator_next(mIterator); } + ARect getRect() { return ARegionIterator_getRect(mIterator); } + ARect getTotalBounds() const { return ARegionIterator_getTotalBounds(mIterator); } + private: + ARegionIterator* mIterator; + }; +}; // namespace graphics +}; // namespace android + +#endif // __cplusplus +#endif // ANDROID_GRAPHICS_REGION_H \ No newline at end of file diff --git a/libs/hwui/apex/include/android/graphics/renderthread.h b/libs/hwui/apex/include/android/graphics/renderthread.h new file mode 100644 index 000000000000..0a790af731a9 --- /dev/null +++ b/libs/hwui/apex/include/android/graphics/renderthread.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2019 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_RENDERTHREAD_H +#define ANDROID_GRAPHICS_RENDERTHREAD_H + +#include + +__BEGIN_DECLS + +/** + * Dumps a textual representation of the graphics stats for this process. + * @param fd The file descriptor that the available graphics stats will be appended to. The + * function requires a valid fd, but does not persist or assume ownership of the fd + * outside the scope of this function. + */ +void ARenderThread_dumpGraphicsMemory(int fd); + +__END_DECLS + +#endif // ANDROID_GRAPHICS_RENDERTHREAD_H diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp new file mode 100644 index 000000000000..506c57802e32 --- /dev/null +++ b/libs/hwui/apex/jni_runtime.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2019 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 "android/graphics/jni_runtime.h" + +#include +#include +#include + +#include +#include +#include + +#define LOG_TAG "AndroidGraphicsJNI" + +extern int register_android_graphics_Bitmap(JNIEnv*); +extern int register_android_graphics_BitmapFactory(JNIEnv*); +extern int register_android_graphics_BitmapRegionDecoder(JNIEnv*); +extern int register_android_graphics_ByteBufferStreamAdaptor(JNIEnv* env); +extern int register_android_graphics_Camera(JNIEnv* env); +extern int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env); +extern int register_android_graphics_Graphics(JNIEnv* env); +extern int register_android_graphics_ImageDecoder(JNIEnv*); +extern int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv*); +extern int register_android_graphics_Interpolator(JNIEnv* env); +extern int register_android_graphics_MaskFilter(JNIEnv* env); +extern int register_android_graphics_Movie(JNIEnv* env); +extern int register_android_graphics_NinePatch(JNIEnv*); +extern int register_android_graphics_PathEffect(JNIEnv* env); +extern int register_android_graphics_Shader(JNIEnv* env); +extern int register_android_graphics_Typeface(JNIEnv* env); +extern int register_android_graphics_YuvImage(JNIEnv* env); + +namespace android { + +extern int register_android_graphics_Canvas(JNIEnv* env); +extern int register_android_graphics_CanvasProperty(JNIEnv* env); +extern int register_android_graphics_ColorFilter(JNIEnv* env); +extern int register_android_graphics_ColorSpace(JNIEnv* env); +extern int register_android_graphics_DrawFilter(JNIEnv* env); +extern int register_android_graphics_FontFamily(JNIEnv* env); +extern int register_android_graphics_HardwareRendererObserver(JNIEnv* env); +extern int register_android_graphics_Matrix(JNIEnv* env); +extern int register_android_graphics_Paint(JNIEnv* env); +extern int register_android_graphics_Path(JNIEnv* env); +extern int register_android_graphics_PathMeasure(JNIEnv* env); +extern int register_android_graphics_Picture(JNIEnv*); +extern int register_android_graphics_Region(JNIEnv* env); +extern int register_android_graphics_animation_NativeInterpolatorFactory(JNIEnv* env); +extern int register_android_graphics_animation_RenderNodeAnimator(JNIEnv* env); +extern int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env); +extern int register_android_graphics_drawable_VectorDrawable(JNIEnv* env); +extern int register_android_graphics_fonts_Font(JNIEnv* env); +extern int register_android_graphics_fonts_FontFamily(JNIEnv* env); +extern int register_android_graphics_pdf_PdfDocument(JNIEnv* env); +extern int register_android_graphics_pdf_PdfEditor(JNIEnv* env); +extern int register_android_graphics_pdf_PdfRenderer(JNIEnv* env); +extern int register_android_graphics_text_MeasuredText(JNIEnv* env); +extern int register_android_graphics_text_LineBreaker(JNIEnv *env); + +extern int register_android_util_PathParser(JNIEnv* env); +extern int register_android_view_DisplayListCanvas(JNIEnv* env); +extern int register_android_view_RenderNode(JNIEnv* env); +extern int register_android_view_TextureLayer(JNIEnv* env); +extern int register_android_view_ThreadedRenderer(JNIEnv* env); + +#ifdef NDEBUG + #define REG_JNI(name) { name } + struct RegJNIRec { + int (*mProc)(JNIEnv*); + }; +#else + #define REG_JNI(name) { name, #name } + struct RegJNIRec { + int (*mProc)(JNIEnv*); + const char* mName; + }; +#endif + +static const RegJNIRec gRegJNI[] = { + REG_JNI(register_android_graphics_Canvas), + // This needs to be before register_android_graphics_Graphics, or the latter + // will not be able to find the jmethodID for ColorSpace.get(). + REG_JNI(register_android_graphics_ColorSpace), + REG_JNI(register_android_graphics_Graphics), + REG_JNI(register_android_graphics_Bitmap), + REG_JNI(register_android_graphics_BitmapFactory), + REG_JNI(register_android_graphics_BitmapRegionDecoder), + REG_JNI(register_android_graphics_ByteBufferStreamAdaptor), + REG_JNI(register_android_graphics_Camera), + REG_JNI(register_android_graphics_CreateJavaOutputStreamAdaptor), + REG_JNI(register_android_graphics_CanvasProperty), + REG_JNI(register_android_graphics_ColorFilter), + REG_JNI(register_android_graphics_DrawFilter), + REG_JNI(register_android_graphics_FontFamily), + REG_JNI(register_android_graphics_HardwareRendererObserver), + REG_JNI(register_android_graphics_ImageDecoder), + REG_JNI(register_android_graphics_drawable_AnimatedImageDrawable), + REG_JNI(register_android_graphics_Interpolator), + REG_JNI(register_android_graphics_MaskFilter), + REG_JNI(register_android_graphics_Matrix), + REG_JNI(register_android_graphics_Movie), + REG_JNI(register_android_graphics_NinePatch), + REG_JNI(register_android_graphics_Paint), + REG_JNI(register_android_graphics_Path), + REG_JNI(register_android_graphics_PathMeasure), + REG_JNI(register_android_graphics_PathEffect), + REG_JNI(register_android_graphics_Picture), + REG_JNI(register_android_graphics_Region), + REG_JNI(register_android_graphics_Shader), + REG_JNI(register_android_graphics_Typeface), + REG_JNI(register_android_graphics_YuvImage), + REG_JNI(register_android_graphics_animation_NativeInterpolatorFactory), + REG_JNI(register_android_graphics_animation_RenderNodeAnimator), + REG_JNI(register_android_graphics_drawable_AnimatedVectorDrawable), + REG_JNI(register_android_graphics_drawable_VectorDrawable), + REG_JNI(register_android_graphics_fonts_Font), + REG_JNI(register_android_graphics_fonts_FontFamily), + REG_JNI(register_android_graphics_pdf_PdfDocument), + REG_JNI(register_android_graphics_pdf_PdfEditor), + REG_JNI(register_android_graphics_pdf_PdfRenderer), + REG_JNI(register_android_graphics_text_MeasuredText), + REG_JNI(register_android_graphics_text_LineBreaker), + + REG_JNI(register_android_util_PathParser), + REG_JNI(register_android_view_RenderNode), + REG_JNI(register_android_view_DisplayListCanvas), + REG_JNI(register_android_view_TextureLayer), + REG_JNI(register_android_view_ThreadedRenderer), +}; + +} // namespace android + +void init_android_graphics() { + SkGraphics::Init(); +} + +int register_android_graphics_classes(JNIEnv *env) { + for (size_t i = 0; i < NELEM(android::gRegJNI); i++) { + if (android::gRegJNI[i].mProc(env) < 0) { +#ifndef NDEBUG + __android_log_print(ANDROID_LOG_FATAL, LOG_TAG, "JNI Error!!! %s failed to load\n", + android::gRegJNI[i].mName); +#endif + return -1; + } + } + return 0; +} + +using android::uirenderer::Properties; +using android::uirenderer::RenderPipelineType; + +void zygote_preload_graphics() { + if (Properties::peekRenderPipelineType() == RenderPipelineType::SkiaGL) { + eglGetDisplay(EGL_DEFAULT_DISPLAY); + } +} \ No newline at end of file diff --git a/libs/hwui/apex/renderthread.cpp b/libs/hwui/apex/renderthread.cpp new file mode 100644 index 000000000000..5d26afe7a2ab --- /dev/null +++ b/libs/hwui/apex/renderthread.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2019 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 "android/graphics/renderthread.h" + +#include + +using namespace android; + +void ARenderThread_dumpGraphicsMemory(int fd) { + uirenderer::renderthread::RenderProxy::dumpGraphicsMemory(fd); +} diff --git a/libs/hwui/jni/AnimatedImageDrawable.cpp b/libs/hwui/jni/AnimatedImageDrawable.cpp new file mode 100644 index 000000000000..6c2a5a3f3fcc --- /dev/null +++ b/libs/hwui/jni/AnimatedImageDrawable.cpp @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2018 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 "GraphicsJNI.h" +#include "ImageDecoder.h" +#include "Utils.h" +#include "core_jni_helpers.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace android; + +static jmethodID gAnimatedImageDrawable_onAnimationEndMethodID; + +// Note: jpostProcess holds a handle to the ImageDecoder. +static jlong AnimatedImageDrawable_nCreate(JNIEnv* env, jobject /*clazz*/, + jlong nativeImageDecoder, jobject jpostProcess, + jint width, jint height, jlong colorSpaceHandle, + jboolean extended, jobject jsubset) { + if (nativeImageDecoder == 0) { + doThrowIOE(env, "Cannot create AnimatedImageDrawable from null!"); + return 0; + } + + auto* imageDecoder = reinterpret_cast(nativeImageDecoder); + SkIRect subset; + if (jsubset) { + GraphicsJNI::jrect_to_irect(env, jsubset, &subset); + } else { + subset = SkIRect::MakeWH(width, height); + } + + bool hasRestoreFrame = false; + if (imageDecoder->mCodec->getEncodedFormat() != SkEncodedImageFormat::kWEBP) { + const int frameCount = imageDecoder->mCodec->codec()->getFrameCount(); + for (int i = 0; i < frameCount; ++i) { + SkCodec::FrameInfo frameInfo; + if (!imageDecoder->mCodec->codec()->getFrameInfo(i, &frameInfo)) { + doThrowIOE(env, "Failed to read frame info!"); + return 0; + } + if (frameInfo.fDisposalMethod == SkCodecAnimation::DisposalMethod::kRestorePrevious) { + hasRestoreFrame = true; + break; + } + } + } + + auto info = imageDecoder->mCodec->getInfo().makeWH(width, height) + .makeColorSpace(GraphicsJNI::getNativeColorSpace(colorSpaceHandle)); + if (extended) { + info = info.makeColorType(kRGBA_F16_SkColorType); + } + + size_t bytesUsed = info.computeMinByteSize(); + // SkAnimatedImage has one SkBitmap for decoding, plus an extra one if there is a + // kRestorePrevious frame. AnimatedImageDrawable has two SkPictures storing the current + // frame and the next frame. (The former assumes that the image is animated, and the + // latter assumes that it is drawn to a hardware canvas.) + bytesUsed *= hasRestoreFrame ? 4 : 3; + sk_sp picture; + if (jpostProcess) { + SkRect bounds = SkRect::MakeWH(subset.width(), subset.height()); + + SkPictureRecorder recorder; + SkCanvas* skcanvas = recorder.beginRecording(bounds); + std::unique_ptr canvas(Canvas::create_canvas(skcanvas)); + postProcessAndRelease(env, jpostProcess, std::move(canvas)); + if (env->ExceptionCheck()) { + return 0; + } + picture = recorder.finishRecordingAsPicture(); + bytesUsed += picture->approximateBytesUsed(); + } + + + sk_sp animatedImg = SkAnimatedImage::Make(std::move(imageDecoder->mCodec), + info, subset, + std::move(picture)); + if (!animatedImg) { + doThrowIOE(env, "Failed to create drawable"); + return 0; + } + + bytesUsed += sizeof(animatedImg.get()); + + sk_sp drawable(new AnimatedImageDrawable(std::move(animatedImg), + bytesUsed)); + return reinterpret_cast(drawable.release()); +} + +static void AnimatedImageDrawable_destruct(AnimatedImageDrawable* drawable) { + SkSafeUnref(drawable); +} + +static jlong AnimatedImageDrawable_nGetNativeFinalizer(JNIEnv* /*env*/, jobject /*clazz*/) { + return static_cast(reinterpret_cast(&AnimatedImageDrawable_destruct)); +} + +// Java's FINISHED relies on this being -1 +static_assert(SkAnimatedImage::kFinished == -1); + +static jlong AnimatedImageDrawable_nDraw(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, + jlong canvasPtr) { + auto* drawable = reinterpret_cast(nativePtr); + auto* canvas = reinterpret_cast(canvasPtr); + return (jlong) canvas->drawAnimatedImage(drawable); +} + +static void AnimatedImageDrawable_nSetAlpha(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, + jint alpha) { + auto* drawable = reinterpret_cast(nativePtr); + drawable->setStagingAlpha(alpha); +} + +static jlong AnimatedImageDrawable_nGetAlpha(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { + auto* drawable = reinterpret_cast(nativePtr); + return drawable->getStagingAlpha(); +} + +static void AnimatedImageDrawable_nSetColorFilter(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, + jlong nativeFilter) { + auto* drawable = reinterpret_cast(nativePtr); + auto* filter = reinterpret_cast(nativeFilter); + drawable->setStagingColorFilter(sk_ref_sp(filter)); +} + +static jboolean AnimatedImageDrawable_nIsRunning(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { + auto* drawable = reinterpret_cast(nativePtr); + return drawable->isRunning(); +} + +static jboolean AnimatedImageDrawable_nStart(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { + auto* drawable = reinterpret_cast(nativePtr); + return drawable->start(); +} + +static jboolean AnimatedImageDrawable_nStop(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { + auto* drawable = reinterpret_cast(nativePtr); + return drawable->stop(); +} + +// Java's LOOP_INFINITE relies on this being the same. +static_assert(SkCodec::kRepetitionCountInfinite == -1); + +static jint AnimatedImageDrawable_nGetRepeatCount(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { + auto* drawable = reinterpret_cast(nativePtr); + return drawable->getRepetitionCount(); +} + +static void AnimatedImageDrawable_nSetRepeatCount(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, + jint loopCount) { + auto* drawable = reinterpret_cast(nativePtr); + drawable->setRepetitionCount(loopCount); +} + +class InvokeListener : public MessageHandler { +public: + InvokeListener(JNIEnv* env, jobject javaObject) { + LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&mJvm) != JNI_OK); + // Hold a weak reference to break a cycle that would prevent GC. + mWeakRef = env->NewWeakGlobalRef(javaObject); + } + + ~InvokeListener() override { + auto* env = get_env_or_die(mJvm); + env->DeleteWeakGlobalRef(mWeakRef); + } + + virtual void handleMessage(const Message&) override { + auto* env = get_env_or_die(mJvm); + jobject localRef = env->NewLocalRef(mWeakRef); + if (localRef) { + env->CallVoidMethod(localRef, gAnimatedImageDrawable_onAnimationEndMethodID); + } + } + +private: + JavaVM* mJvm; + jweak mWeakRef; +}; + +class JniAnimationEndListener : public OnAnimationEndListener { +public: + JniAnimationEndListener(sp&& looper, JNIEnv* env, jobject javaObject) { + mListener = new InvokeListener(env, javaObject); + mLooper = std::move(looper); + } + + void onAnimationEnd() override { mLooper->sendMessage(mListener, 0); } + +private: + sp mListener; + sp mLooper; +}; + +static void AnimatedImageDrawable_nSetOnAnimationEndListener(JNIEnv* env, jobject /*clazz*/, + jlong nativePtr, jobject jdrawable) { + auto* drawable = reinterpret_cast(nativePtr); + if (!jdrawable) { + drawable->setOnAnimationEndListener(nullptr); + } else { + sp looper = Looper::getForThread(); + if (!looper.get()) { + doThrowISE(env, + "Must set AnimatedImageDrawable's AnimationCallback on a thread with a " + "looper!"); + return; + } + + drawable->setOnAnimationEndListener( + std::make_unique(std::move(looper), env, jdrawable)); + } +} + +static jlong AnimatedImageDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { + auto* drawable = reinterpret_cast(nativePtr); + return drawable->byteSize(); +} + +static void AnimatedImageDrawable_nSetMirrored(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, + jboolean mirrored) { + auto* drawable = reinterpret_cast(nativePtr); + drawable->setStagingMirrored(mirrored); +} + +static const JNINativeMethod gAnimatedImageDrawableMethods[] = { + { "nCreate", "(JLandroid/graphics/ImageDecoder;IIJZLandroid/graphics/Rect;)J",(void*) AnimatedImageDrawable_nCreate }, + { "nGetNativeFinalizer", "()J", (void*) AnimatedImageDrawable_nGetNativeFinalizer }, + { "nDraw", "(JJ)J", (void*) AnimatedImageDrawable_nDraw }, + { "nSetAlpha", "(JI)V", (void*) AnimatedImageDrawable_nSetAlpha }, + { "nGetAlpha", "(J)I", (void*) AnimatedImageDrawable_nGetAlpha }, + { "nSetColorFilter", "(JJ)V", (void*) AnimatedImageDrawable_nSetColorFilter }, + { "nIsRunning", "(J)Z", (void*) AnimatedImageDrawable_nIsRunning }, + { "nStart", "(J)Z", (void*) AnimatedImageDrawable_nStart }, + { "nStop", "(J)Z", (void*) AnimatedImageDrawable_nStop }, + { "nGetRepeatCount", "(J)I", (void*) AnimatedImageDrawable_nGetRepeatCount }, + { "nSetRepeatCount", "(JI)V", (void*) AnimatedImageDrawable_nSetRepeatCount }, + { "nSetOnAnimationEndListener", "(JLandroid/graphics/drawable/AnimatedImageDrawable;)V", (void*) AnimatedImageDrawable_nSetOnAnimationEndListener }, + { "nNativeByteSize", "(J)J", (void*) AnimatedImageDrawable_nNativeByteSize }, + { "nSetMirrored", "(JZ)V", (void*) AnimatedImageDrawable_nSetMirrored }, +}; + +int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv* env) { + jclass animatedImageDrawable_class = FindClassOrDie(env, "android/graphics/drawable/AnimatedImageDrawable"); + gAnimatedImageDrawable_onAnimationEndMethodID = GetMethodIDOrDie(env, animatedImageDrawable_class, "onAnimationEnd", "()V"); + + return android::RegisterMethodsOrDie(env, "android/graphics/drawable/AnimatedImageDrawable", + gAnimatedImageDrawableMethods, NELEM(gAnimatedImageDrawableMethods)); +} + diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp new file mode 100755 index 000000000000..130322aaaa45 --- /dev/null +++ b/libs/hwui/jni/Bitmap.cpp @@ -0,0 +1,1178 @@ +#define LOG_TAG "Bitmap" +#include "Bitmap.h" + +#include "SkBitmap.h" +#include "SkPixelRef.h" +#include "SkImageEncoder.h" +#include "SkImageInfo.h" +#include "SkColor.h" +#include "SkColorSpace.h" +#include "GraphicsJNI.h" +#include "SkStream.h" +#include "SkWebpEncoder.h" + +#include "android_os_Parcel.h" +#include "android_nio_utils.h" +#include "CreateJavaOutputStreamAdaptor.h" +#include +#include +#include + +#ifdef __ANDROID__ // Layoutlib does not support graphic buffer, parcel or render thread +#include +#include +#include +#include +#endif + +#include "core_jni_helpers.h" + +#include +#include +#include +#include + +#define DEBUG_PARCEL 0 + +static jclass gBitmap_class; +static jfieldID gBitmap_nativePtr; +static jmethodID gBitmap_constructorMethodID; +static jmethodID gBitmap_reinitMethodID; + +namespace android { + +class BitmapWrapper { +public: + explicit BitmapWrapper(Bitmap* bitmap) + : mBitmap(bitmap) { } + + void freePixels() { + mInfo = mBitmap->info(); + mHasHardwareMipMap = mBitmap->hasHardwareMipMap(); + mAllocationSize = mBitmap->getAllocationByteCount(); + mRowBytes = mBitmap->rowBytes(); + mGenerationId = mBitmap->getGenerationID(); + mIsHardware = mBitmap->isHardware(); + mBitmap.reset(); + } + + bool valid() { + return mBitmap != nullptr; + } + + Bitmap& bitmap() { + assertValid(); + return *mBitmap; + } + + void assertValid() { + LOG_ALWAYS_FATAL_IF(!valid(), "Error, cannot access an invalid/free'd bitmap here!"); + } + + void getSkBitmap(SkBitmap* outBitmap) { + assertValid(); + mBitmap->getSkBitmap(outBitmap); + } + + bool hasHardwareMipMap() { + if (mBitmap) { + return mBitmap->hasHardwareMipMap(); + } + return mHasHardwareMipMap; + } + + void setHasHardwareMipMap(bool hasMipMap) { + assertValid(); + mBitmap->setHasHardwareMipMap(hasMipMap); + } + + void setAlphaType(SkAlphaType alphaType) { + assertValid(); + mBitmap->setAlphaType(alphaType); + } + + void setColorSpace(sk_sp colorSpace) { + assertValid(); + mBitmap->setColorSpace(colorSpace); + } + + const SkImageInfo& info() { + if (mBitmap) { + return mBitmap->info(); + } + return mInfo; + } + + size_t getAllocationByteCount() const { + if (mBitmap) { + return mBitmap->getAllocationByteCount(); + } + return mAllocationSize; + } + + size_t rowBytes() const { + if (mBitmap) { + return mBitmap->rowBytes(); + } + return mRowBytes; + } + + uint32_t getGenerationID() const { + if (mBitmap) { + return mBitmap->getGenerationID(); + } + return mGenerationId; + } + + bool isHardware() { + if (mBitmap) { + return mBitmap->isHardware(); + } + return mIsHardware; + } + + ~BitmapWrapper() { } + +private: + sk_sp mBitmap; + SkImageInfo mInfo; + bool mHasHardwareMipMap; + size_t mAllocationSize; + size_t mRowBytes; + uint32_t mGenerationId; + bool mIsHardware; +}; + +// Convenience class that does not take a global ref on the pixels, relying +// on the caller already having a local JNI ref +class LocalScopedBitmap { +public: + explicit LocalScopedBitmap(jlong bitmapHandle) + : mBitmapWrapper(reinterpret_cast(bitmapHandle)) {} + + BitmapWrapper* operator->() { + return mBitmapWrapper; + } + + void* pixels() { + return mBitmapWrapper->bitmap().pixels(); + } + + bool valid() { + return mBitmapWrapper && mBitmapWrapper->valid(); + } + +private: + BitmapWrapper* mBitmapWrapper; +}; + +namespace bitmap { + +// Assert that bitmap's SkAlphaType is consistent with isPremultiplied. +static void assert_premultiplied(const SkImageInfo& info, bool isPremultiplied) { + // kOpaque_SkAlphaType and kIgnore_SkAlphaType mean that isPremultiplied is + // irrelevant. This just tests to ensure that the SkAlphaType is not + // opposite of isPremultiplied. + if (isPremultiplied) { + SkASSERT(info.alphaType() != kUnpremul_SkAlphaType); + } else { + SkASSERT(info.alphaType() != kPremul_SkAlphaType); + } +} + +void reinitBitmap(JNIEnv* env, jobject javaBitmap, const SkImageInfo& info, + bool isPremultiplied) +{ + // The caller needs to have already set the alpha type properly, so the + // native SkBitmap stays in sync with the Java Bitmap. + assert_premultiplied(info, isPremultiplied); + + env->CallVoidMethod(javaBitmap, gBitmap_reinitMethodID, + info.width(), info.height(), isPremultiplied); +} + +jobject createBitmap(JNIEnv* env, Bitmap* bitmap, + int bitmapCreateFlags, jbyteArray ninePatchChunk, jobject ninePatchInsets, + int density) { + bool isMutable = bitmapCreateFlags & kBitmapCreateFlag_Mutable; + bool isPremultiplied = bitmapCreateFlags & kBitmapCreateFlag_Premultiplied; + // The caller needs to have already set the alpha type properly, so the + // native SkBitmap stays in sync with the Java Bitmap. + assert_premultiplied(bitmap->info(), isPremultiplied); + bool fromMalloc = bitmap->pixelStorageType() == PixelStorageType::Heap; + BitmapWrapper* bitmapWrapper = new BitmapWrapper(bitmap); + if (!isMutable) { + bitmapWrapper->bitmap().setImmutable(); + } + jobject obj = env->NewObject(gBitmap_class, gBitmap_constructorMethodID, + reinterpret_cast(bitmapWrapper), bitmap->width(), bitmap->height(), density, + isPremultiplied, ninePatchChunk, ninePatchInsets, fromMalloc); + + if (env->ExceptionCheck() != 0) { + ALOGE("*** Uncaught exception returned from Java call!\n"); + env->ExceptionDescribe(); + } + return obj; +} + +void toSkBitmap(jlong bitmapHandle, SkBitmap* outBitmap) { + LocalScopedBitmap bitmap(bitmapHandle); + bitmap->getSkBitmap(outBitmap); +} + +Bitmap& toBitmap(jlong bitmapHandle) { + LocalScopedBitmap localBitmap(bitmapHandle); + return localBitmap->bitmap(); +} + +} // namespace bitmap + +} // namespace android + +using namespace android; +using namespace android::bitmap; + +Bitmap* GraphicsJNI::getNativeBitmap(JNIEnv* env, jobject bitmap) { + SkASSERT(env); + SkASSERT(bitmap); + SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class)); + jlong bitmapHandle = env->GetLongField(bitmap, gBitmap_nativePtr); + LocalScopedBitmap localBitmap(bitmapHandle); + return localBitmap.valid() ? &localBitmap->bitmap() : nullptr; +} + +SkImageInfo GraphicsJNI::getBitmapInfo(JNIEnv* env, jobject bitmap, uint32_t* outRowBytes, + bool* isHardware) { + SkASSERT(env); + SkASSERT(bitmap); + SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class)); + jlong bitmapHandle = env->GetLongField(bitmap, gBitmap_nativePtr); + LocalScopedBitmap localBitmap(bitmapHandle); + if (outRowBytes) { + *outRowBytes = localBitmap->rowBytes(); + } + if (isHardware) { + *isHardware = localBitmap->isHardware(); + } + return localBitmap->info(); +} + +bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors, int srcOffset, int srcStride, + int x, int y, int width, int height, SkBitmap* dstBitmap) { + const jint* array = env->GetIntArrayElements(srcColors, NULL); + const SkColor* src = (const SkColor*)array + srcOffset; + + auto sRGB = SkColorSpace::MakeSRGB(); + SkImageInfo srcInfo = SkImageInfo::Make( + width, height, kBGRA_8888_SkColorType, kUnpremul_SkAlphaType, sRGB); + SkPixmap srcPM(srcInfo, src, srcStride * 4); + + dstBitmap->writePixels(srcPM, x, y); + + env->ReleaseIntArrayElements(srcColors, const_cast(array), JNI_ABORT); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int getPremulBitmapCreateFlags(bool isMutable) { + int flags = android::bitmap::kBitmapCreateFlag_Premultiplied; + if (isMutable) flags |= android::bitmap::kBitmapCreateFlag_Mutable; + return flags; +} + +static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, + jint offset, jint stride, jint width, jint height, + jint configHandle, jboolean isMutable, + jlong colorSpacePtr) { + SkColorType colorType = GraphicsJNI::legacyBitmapConfigToColorType(configHandle); + if (NULL != jColors) { + size_t n = env->GetArrayLength(jColors); + if (n < SkAbs32(stride) * (size_t)height) { + doThrowAIOOBE(env); + return NULL; + } + } + + // ARGB_4444 is a deprecated format, convert automatically to 8888 + if (colorType == kARGB_4444_SkColorType) { + colorType = kN32_SkColorType; + } + + sk_sp colorSpace; + if (colorType == kAlpha_8_SkColorType) { + colorSpace = nullptr; + } else { + colorSpace = GraphicsJNI::getNativeColorSpace(colorSpacePtr); + } + + SkBitmap bitmap; + bitmap.setInfo(SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType, + colorSpace)); + + sk_sp nativeBitmap = Bitmap::allocateHeapBitmap(&bitmap); + if (!nativeBitmap) { + ALOGE("OOM allocating Bitmap with dimensions %i x %i", width, height); + doThrowOOME(env); + return NULL; + } + + if (jColors != NULL) { + GraphicsJNI::SetPixels(env, jColors, offset, stride, 0, 0, width, height, &bitmap); + } + + return createBitmap(env, nativeBitmap.release(), getPremulBitmapCreateFlags(isMutable)); +} + +static bool bitmapCopyTo(SkBitmap* dst, SkColorType dstCT, const SkBitmap& src, + SkBitmap::Allocator* alloc) { + SkPixmap srcPM; + if (!src.peekPixels(&srcPM)) { + return false; + } + + SkImageInfo dstInfo = srcPM.info().makeColorType(dstCT); + switch (dstCT) { + case kRGB_565_SkColorType: + dstInfo = dstInfo.makeAlphaType(kOpaque_SkAlphaType); + break; + case kAlpha_8_SkColorType: + dstInfo = dstInfo.makeColorSpace(nullptr); + break; + default: + break; + } + + if (!dstInfo.colorSpace() && dstCT != kAlpha_8_SkColorType) { + dstInfo = dstInfo.makeColorSpace(SkColorSpace::MakeSRGB()); + } + + if (!dst->setInfo(dstInfo)) { + return false; + } + if (!dst->tryAllocPixels(alloc)) { + return false; + } + + SkPixmap dstPM; + if (!dst->peekPixels(&dstPM)) { + return false; + } + + return srcPM.readPixels(dstPM); +} + +static jobject Bitmap_copy(JNIEnv* env, jobject, jlong srcHandle, + jint dstConfigHandle, jboolean isMutable) { + SkBitmap src; + reinterpret_cast(srcHandle)->getSkBitmap(&src); + if (dstConfigHandle == GraphicsJNI::hardwareLegacyBitmapConfig()) { + sk_sp bitmap(Bitmap::allocateHardwareBitmap(src)); + if (!bitmap.get()) { + return NULL; + } + return createBitmap(env, bitmap.release(), getPremulBitmapCreateFlags(isMutable)); + } + + SkColorType dstCT = GraphicsJNI::legacyBitmapConfigToColorType(dstConfigHandle); + SkBitmap result; + HeapAllocator allocator; + + if (!bitmapCopyTo(&result, dstCT, src, &allocator)) { + return NULL; + } + auto bitmap = allocator.getStorageObjAndReset(); + return createBitmap(env, bitmap, getPremulBitmapCreateFlags(isMutable)); +} + +static Bitmap* Bitmap_copyAshmemImpl(JNIEnv* env, SkBitmap& src, SkColorType& dstCT) { + SkBitmap result; + + AshmemPixelAllocator allocator(env); + if (!bitmapCopyTo(&result, dstCT, src, &allocator)) { + return NULL; + } + auto bitmap = allocator.getStorageObjAndReset(); + bitmap->setImmutable(); + return bitmap; +} + +static jobject Bitmap_copyAshmem(JNIEnv* env, jobject, jlong srcHandle) { + SkBitmap src; + reinterpret_cast(srcHandle)->getSkBitmap(&src); + SkColorType dstCT = src.colorType(); + auto bitmap = Bitmap_copyAshmemImpl(env, src, dstCT); + jobject ret = createBitmap(env, bitmap, getPremulBitmapCreateFlags(false)); + return ret; +} + +static jobject Bitmap_copyAshmemConfig(JNIEnv* env, jobject, jlong srcHandle, jint dstConfigHandle) { + SkBitmap src; + reinterpret_cast(srcHandle)->getSkBitmap(&src); + SkColorType dstCT = GraphicsJNI::legacyBitmapConfigToColorType(dstConfigHandle); + auto bitmap = Bitmap_copyAshmemImpl(env, src, dstCT); + jobject ret = createBitmap(env, bitmap, getPremulBitmapCreateFlags(false)); + return ret; +} + +static void Bitmap_destruct(BitmapWrapper* bitmap) { + delete bitmap; +} + +static jlong Bitmap_getNativeFinalizer(JNIEnv*, jobject) { + return static_cast(reinterpret_cast(&Bitmap_destruct)); +} + +static void Bitmap_recycle(JNIEnv* env, jobject, jlong bitmapHandle) { + LocalScopedBitmap bitmap(bitmapHandle); + bitmap->freePixels(); +} + +static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jlong bitmapHandle, + jint width, jint height, jint configHandle, jboolean requestPremul) { + LocalScopedBitmap bitmap(bitmapHandle); + bitmap->assertValid(); + SkColorType colorType = GraphicsJNI::legacyBitmapConfigToColorType(configHandle); + + // ARGB_4444 is a deprecated format, convert automatically to 8888 + if (colorType == kARGB_4444_SkColorType) { + colorType = kN32_SkColorType; + } + size_t requestedSize = width * height * SkColorTypeBytesPerPixel(colorType); + if (requestedSize > bitmap->getAllocationByteCount()) { + // done in native as there's no way to get BytesPerPixel in Java + doThrowIAE(env, "Bitmap not large enough to support new configuration"); + return; + } + SkAlphaType alphaType; + if (bitmap->info().colorType() != kRGB_565_SkColorType + && bitmap->info().alphaType() == kOpaque_SkAlphaType) { + // If the original bitmap was set to opaque, keep that setting, unless it + // was 565, which is required to be opaque. + alphaType = kOpaque_SkAlphaType; + } else { + // Otherwise respect the premultiplied request. + alphaType = requestPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType; + } + bitmap->bitmap().reconfigure(SkImageInfo::Make(width, height, colorType, alphaType, + sk_ref_sp(bitmap->info().colorSpace()))); +} + +static jboolean Bitmap_compress(JNIEnv* env, jobject clazz, jlong bitmapHandle, + jint format, jint quality, + jobject jstream, jbyteArray jstorage) { + LocalScopedBitmap bitmap(bitmapHandle); + if (!bitmap.valid()) { + return JNI_FALSE; + } + + std::unique_ptr strm(CreateJavaOutputStreamAdaptor(env, jstream, jstorage)); + if (!strm.get()) { + return JNI_FALSE; + } + + auto fm = static_cast(format); + return bitmap->bitmap().compress(fm, quality, strm.get()) ? JNI_TRUE : JNI_FALSE; +} + +static inline void bitmapErase(SkBitmap bitmap, const SkColor4f& color, + const sk_sp& colorSpace) { + SkPaint p; + p.setColor4f(color, colorSpace.get()); + p.setBlendMode(SkBlendMode::kSrc); + SkCanvas canvas(bitmap); + canvas.drawPaint(p); +} + +static void Bitmap_erase(JNIEnv* env, jobject, jlong bitmapHandle, jint color) { + LocalScopedBitmap bitmap(bitmapHandle); + SkBitmap skBitmap; + bitmap->getSkBitmap(&skBitmap); + bitmapErase(skBitmap, SkColor4f::FromColor(color), SkColorSpace::MakeSRGB()); +} + +static void Bitmap_eraseLong(JNIEnv* env, jobject, jlong bitmapHandle, + jlong colorSpaceHandle, jlong colorLong) { + LocalScopedBitmap bitmap(bitmapHandle); + SkBitmap skBitmap; + bitmap->getSkBitmap(&skBitmap); + + SkColor4f color = GraphicsJNI::convertColorLong(colorLong); + sk_sp cs = GraphicsJNI::getNativeColorSpace(colorSpaceHandle); + bitmapErase(skBitmap, color, cs); +} + +static jint Bitmap_rowBytes(JNIEnv* env, jobject, jlong bitmapHandle) { + LocalScopedBitmap bitmap(bitmapHandle); + return static_cast(bitmap->rowBytes()); +} + +static jint Bitmap_config(JNIEnv* env, jobject, jlong bitmapHandle) { + LocalScopedBitmap bitmap(bitmapHandle); + if (bitmap->isHardware()) { + return GraphicsJNI::hardwareLegacyBitmapConfig(); + } + return GraphicsJNI::colorTypeToLegacyBitmapConfig(bitmap->info().colorType()); +} + +static jint Bitmap_getGenerationId(JNIEnv* env, jobject, jlong bitmapHandle) { + LocalScopedBitmap bitmap(bitmapHandle); + return static_cast(bitmap->getGenerationID()); +} + +static jboolean Bitmap_isPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle) { + LocalScopedBitmap bitmap(bitmapHandle); + if (bitmap->info().alphaType() == kPremul_SkAlphaType) { + return JNI_TRUE; + } + return JNI_FALSE; +} + +static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, jlong bitmapHandle) { + LocalScopedBitmap bitmap(bitmapHandle); + return !bitmap->info().isOpaque() ? JNI_TRUE : JNI_FALSE; +} + +static void Bitmap_setHasAlpha(JNIEnv* env, jobject, jlong bitmapHandle, + jboolean hasAlpha, jboolean requestPremul) { + LocalScopedBitmap bitmap(bitmapHandle); + if (hasAlpha) { + bitmap->setAlphaType( + requestPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType); + } else { + bitmap->setAlphaType(kOpaque_SkAlphaType); + } +} + +static void Bitmap_setPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle, + jboolean isPremul) { + LocalScopedBitmap bitmap(bitmapHandle); + if (!bitmap->info().isOpaque()) { + if (isPremul) { + bitmap->setAlphaType(kPremul_SkAlphaType); + } else { + bitmap->setAlphaType(kUnpremul_SkAlphaType); + } + } +} + +static jboolean Bitmap_hasMipMap(JNIEnv* env, jobject, jlong bitmapHandle) { + LocalScopedBitmap bitmap(bitmapHandle); + return bitmap->hasHardwareMipMap() ? JNI_TRUE : JNI_FALSE; +} + +static void Bitmap_setHasMipMap(JNIEnv* env, jobject, jlong bitmapHandle, + jboolean hasMipMap) { + LocalScopedBitmap bitmap(bitmapHandle); + bitmap->setHasHardwareMipMap(hasMipMap); +} + +/////////////////////////////////////////////////////////////////////////////// + +// This is the maximum possible size because the SkColorSpace must be +// representable (and therefore serializable) using a matrix and numerical +// transfer function. If we allow more color space representations in the +// framework, we may need to update this maximum size. +static constexpr uint32_t kMaxColorSpaceSerializedBytes = 80; + +static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { +#ifdef __ANDROID__ // Layoutlib does not support parcel + if (parcel == NULL) { + SkDebugf("-------- unparcel parcel is NULL\n"); + return NULL; + } + + android::Parcel* p = android::parcelForJavaObject(env, parcel); + + const SkColorType colorType = (SkColorType)p->readInt32(); + const SkAlphaType alphaType = (SkAlphaType)p->readInt32(); + const uint32_t colorSpaceSize = p->readUint32(); + sk_sp colorSpace; + if (colorSpaceSize > 0) { + if (colorSpaceSize > kMaxColorSpaceSerializedBytes) { + ALOGD("Bitmap_createFromParcel: Serialized SkColorSpace is larger than expected: " + "%d bytes\n", colorSpaceSize); + } + + const void* data = p->readInplace(colorSpaceSize); + if (data) { + colorSpace = SkColorSpace::Deserialize(data, colorSpaceSize); + } else { + ALOGD("Bitmap_createFromParcel: Unable to read serialized SkColorSpace data\n"); + } + } + const int width = p->readInt32(); + const int height = p->readInt32(); + const int rowBytes = p->readInt32(); + const int density = p->readInt32(); + + if (kN32_SkColorType != colorType && + kRGBA_F16_SkColorType != colorType && + kRGB_565_SkColorType != colorType && + kARGB_4444_SkColorType != colorType && + kAlpha_8_SkColorType != colorType) { + SkDebugf("Bitmap_createFromParcel unknown colortype: %d\n", colorType); + return NULL; + } + + std::unique_ptr bitmap(new SkBitmap); + if (!bitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType, colorSpace), + rowBytes)) { + return NULL; + } + + // Read the bitmap blob. + size_t size = bitmap->computeByteSize(); + android::Parcel::ReadableBlob blob; + android::status_t status = p->readBlob(size, &blob); + if (status) { + doThrowRE(env, "Could not read bitmap blob."); + return NULL; + } + + // Map the bitmap in place from the ashmem region if possible otherwise copy. + sk_sp nativeBitmap; + if (blob.fd() >= 0 && !blob.isMutable()) { +#if DEBUG_PARCEL + ALOGD("Bitmap.createFromParcel: mapped contents of bitmap from %s blob " + "(fds %s)", + blob.isMutable() ? "mutable" : "immutable", + p->allowFds() ? "allowed" : "forbidden"); +#endif + // Dup the file descriptor so we can keep a reference to it after the Parcel + // is disposed. + int dupFd = fcntl(blob.fd(), F_DUPFD_CLOEXEC, 0); + if (dupFd < 0) { + ALOGE("Error allocating dup fd. Error:%d", errno); + blob.release(); + doThrowRE(env, "Could not allocate dup blob fd."); + return NULL; + } + + // Map the pixels in place and take ownership of the ashmem region. We must also respect the + // rowBytes value already set on the bitmap instead of attempting to compute our own. + nativeBitmap = Bitmap::createFrom(bitmap->info(), bitmap->rowBytes(), dupFd, + const_cast(blob.data()), size, true); + if (!nativeBitmap) { + close(dupFd); + blob.release(); + doThrowRE(env, "Could not allocate ashmem pixel ref."); + return NULL; + } + + // Clear the blob handle, don't release it. + blob.clear(); + } else { +#if DEBUG_PARCEL + if (blob.fd() >= 0) { + ALOGD("Bitmap.createFromParcel: copied contents of mutable bitmap " + "from immutable blob (fds %s)", + p->allowFds() ? "allowed" : "forbidden"); + } else { + ALOGD("Bitmap.createFromParcel: copied contents from %s blob " + "(fds %s)", + blob.isMutable() ? "mutable" : "immutable", + p->allowFds() ? "allowed" : "forbidden"); + } +#endif + + // Copy the pixels into a new buffer. + nativeBitmap = Bitmap::allocateHeapBitmap(bitmap.get()); + if (!nativeBitmap) { + blob.release(); + doThrowRE(env, "Could not allocate java pixel ref."); + return NULL; + } + memcpy(bitmap->getPixels(), blob.data(), size); + + // Release the blob handle. + blob.release(); + } + + return createBitmap(env, nativeBitmap.release(), + getPremulBitmapCreateFlags(false), NULL, NULL, density); +#else + doThrowRE(env, "Cannot use parcels outside of Android"); + return NULL; +#endif +} + +static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, + jlong bitmapHandle, jint density, jobject parcel) { +#ifdef __ANDROID__ // Layoutlib does not support parcel + if (parcel == NULL) { + SkDebugf("------- writeToParcel null parcel\n"); + return JNI_FALSE; + } + + android::Parcel* p = android::parcelForJavaObject(env, parcel); + SkBitmap bitmap; + + auto bitmapWrapper = reinterpret_cast(bitmapHandle); + bitmapWrapper->getSkBitmap(&bitmap); + + p->writeInt32(bitmap.colorType()); + p->writeInt32(bitmap.alphaType()); + SkColorSpace* colorSpace = bitmap.colorSpace(); + if (colorSpace != nullptr) { + sk_sp data = colorSpace->serialize(); + size_t size = data->size(); + p->writeUint32(size); + if (size > 0) { + if (size > kMaxColorSpaceSerializedBytes) { + ALOGD("Bitmap_writeToParcel: Serialized SkColorSpace is larger than expected: " + "%zu bytes\n", size); + } + + p->write(data->data(), size); + } + } else { + p->writeUint32(0); + } + p->writeInt32(bitmap.width()); + p->writeInt32(bitmap.height()); + p->writeInt32(bitmap.rowBytes()); + p->writeInt32(density); + + // Transfer the underlying ashmem region if we have one and it's immutable. + android::status_t status; + int fd = bitmapWrapper->bitmap().getAshmemFd(); + if (fd >= 0 && p->allowFds()) { +#if DEBUG_PARCEL + ALOGD("Bitmap.writeToParcel: transferring immutable bitmap's ashmem fd as " + "immutable blob (fds %s)", + p->allowFds() ? "allowed" : "forbidden"); +#endif + + status = p->writeDupImmutableBlobFileDescriptor(fd); + if (status) { + doThrowRE(env, "Could not write bitmap blob file descriptor."); + return JNI_FALSE; + } + return JNI_TRUE; + } + + // Copy the bitmap to a new blob. +#if DEBUG_PARCEL + ALOGD("Bitmap.writeToParcel: copying bitmap into new blob (fds %s)", + p->allowFds() ? "allowed" : "forbidden"); +#endif + + size_t size = bitmap.computeByteSize(); + android::Parcel::WritableBlob blob; + status = p->writeBlob(size, false, &blob); + if (status) { + doThrowRE(env, "Could not copy bitmap to parcel blob."); + return JNI_FALSE; + } + + const void* pSrc = bitmap.getPixels(); + if (pSrc == NULL) { + memset(blob.data(), 0, size); + } else { + memcpy(blob.data(), pSrc, size); + } + + blob.release(); + return JNI_TRUE; +#else + doThrowRE(env, "Cannot use parcels outside of Android"); + return JNI_FALSE; +#endif +} + +static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz, + jlong srcHandle, jlong paintHandle, + jintArray offsetXY) { + SkBitmap src; + reinterpret_cast(srcHandle)->getSkBitmap(&src); + const android::Paint* paint = reinterpret_cast(paintHandle); + SkIPoint offset; + SkBitmap dst; + HeapAllocator allocator; + + src.extractAlpha(&dst, paint, &allocator, &offset); + // If Skia can't allocate pixels for destination bitmap, it resets + // it, that is set its pixels buffer to NULL, and zero width and height. + if (dst.getPixels() == NULL && src.getPixels() != NULL) { + doThrowOOME(env, "failed to allocate pixels for alpha"); + return NULL; + } + if (offsetXY != 0 && env->GetArrayLength(offsetXY) >= 2) { + int* array = env->GetIntArrayElements(offsetXY, NULL); + array[0] = offset.fX; + array[1] = offset.fY; + env->ReleaseIntArrayElements(offsetXY, array, 0); + } + + return createBitmap(env, allocator.getStorageObjAndReset(), + getPremulBitmapCreateFlags(true)); +} + +/////////////////////////////////////////////////////////////////////////////// + +static jboolean Bitmap_isSRGB(JNIEnv* env, jobject, jlong bitmapHandle) { + LocalScopedBitmap bitmapHolder(bitmapHandle); + if (!bitmapHolder.valid()) return JNI_TRUE; + + SkColorSpace* colorSpace = bitmapHolder->info().colorSpace(); + return colorSpace == nullptr || colorSpace->isSRGB(); +} + +static jboolean Bitmap_isSRGBLinear(JNIEnv* env, jobject, jlong bitmapHandle) { + LocalScopedBitmap bitmapHolder(bitmapHandle); + if (!bitmapHolder.valid()) return JNI_FALSE; + + SkColorSpace* colorSpace = bitmapHolder->info().colorSpace(); + sk_sp srgbLinear = SkColorSpace::MakeSRGBLinear(); + return colorSpace == srgbLinear.get() ? JNI_TRUE : JNI_FALSE; +} + +static jobject Bitmap_computeColorSpace(JNIEnv* env, jobject, jlong bitmapHandle) { + LocalScopedBitmap bitmapHolder(bitmapHandle); + if (!bitmapHolder.valid()) return nullptr; + + SkColorSpace* colorSpace = bitmapHolder->info().colorSpace(); + if (colorSpace == nullptr) return nullptr; + + return GraphicsJNI::getColorSpace(env, colorSpace, bitmapHolder->info().colorType()); +} + +static void Bitmap_setColorSpace(JNIEnv* env, jobject, jlong bitmapHandle, jlong colorSpacePtr) { + LocalScopedBitmap bitmapHolder(bitmapHandle); + sk_sp cs = GraphicsJNI::getNativeColorSpace(colorSpacePtr); + bitmapHolder->setColorSpace(cs); +} + +/////////////////////////////////////////////////////////////////////////////// + +static jint Bitmap_getPixel(JNIEnv* env, jobject, jlong bitmapHandle, + jint x, jint y) { + SkBitmap bitmap; + reinterpret_cast(bitmapHandle)->getSkBitmap(&bitmap); + + auto sRGB = SkColorSpace::MakeSRGB(); + SkImageInfo dstInfo = SkImageInfo::Make( + 1, 1, kBGRA_8888_SkColorType, kUnpremul_SkAlphaType, sRGB); + + SkColor dst; + bitmap.readPixels(dstInfo, &dst, dstInfo.minRowBytes(), x, y); + return static_cast(dst); +} + +static jlong Bitmap_getColor(JNIEnv* env, jobject, jlong bitmapHandle, + jint x, jint y) { + SkBitmap bitmap; + reinterpret_cast(bitmapHandle)->getSkBitmap(&bitmap); + + SkImageInfo dstInfo = SkImageInfo::Make( + 1, 1, kRGBA_F16_SkColorType, kUnpremul_SkAlphaType, bitmap.refColorSpace()); + + uint64_t dst; + bitmap.readPixels(dstInfo, &dst, dstInfo.minRowBytes(), x, y); + return static_cast(dst); +} + +static void Bitmap_getPixels(JNIEnv* env, jobject, jlong bitmapHandle, + jintArray pixelArray, jint offset, jint stride, + jint x, jint y, jint width, jint height) { + SkBitmap bitmap; + reinterpret_cast(bitmapHandle)->getSkBitmap(&bitmap); + + auto sRGB = SkColorSpace::MakeSRGB(); + SkImageInfo dstInfo = SkImageInfo::Make( + width, height, kBGRA_8888_SkColorType, kUnpremul_SkAlphaType, sRGB); + + jint* dst = env->GetIntArrayElements(pixelArray, NULL); + bitmap.readPixels(dstInfo, dst + offset, stride * 4, x, y); + env->ReleaseIntArrayElements(pixelArray, dst, 0); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void Bitmap_setPixel(JNIEnv* env, jobject, jlong bitmapHandle, + jint x, jint y, jint colorHandle) { + SkBitmap bitmap; + reinterpret_cast(bitmapHandle)->getSkBitmap(&bitmap); + SkColor color = static_cast(colorHandle); + + auto sRGB = SkColorSpace::MakeSRGB(); + SkImageInfo srcInfo = SkImageInfo::Make( + 1, 1, kBGRA_8888_SkColorType, kUnpremul_SkAlphaType, sRGB); + SkPixmap srcPM(srcInfo, &color, srcInfo.minRowBytes()); + + bitmap.writePixels(srcPM, x, y); +} + +static void Bitmap_setPixels(JNIEnv* env, jobject, jlong bitmapHandle, + jintArray pixelArray, jint offset, jint stride, + jint x, jint y, jint width, jint height) { + SkBitmap bitmap; + reinterpret_cast(bitmapHandle)->getSkBitmap(&bitmap); + GraphicsJNI::SetPixels(env, pixelArray, offset, stride, + x, y, width, height, &bitmap); +} + +static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject, + jlong bitmapHandle, jobject jbuffer) { + SkBitmap bitmap; + reinterpret_cast(bitmapHandle)->getSkBitmap(&bitmap); + const void* src = bitmap.getPixels(); + + if (NULL != src) { + android::AutoBufferPointer abp(env, jbuffer, JNI_TRUE); + + // the java side has already checked that buffer is large enough + memcpy(abp.pointer(), src, bitmap.computeByteSize()); + } +} + +static void Bitmap_copyPixelsFromBuffer(JNIEnv* env, jobject, + jlong bitmapHandle, jobject jbuffer) { + SkBitmap bitmap; + reinterpret_cast(bitmapHandle)->getSkBitmap(&bitmap); + void* dst = bitmap.getPixels(); + + if (NULL != dst) { + android::AutoBufferPointer abp(env, jbuffer, JNI_FALSE); + // the java side has already checked that buffer is large enough + memcpy(dst, abp.pointer(), bitmap.computeByteSize()); + bitmap.notifyPixelsChanged(); + } +} + +static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle, jlong bm1Handle) { + SkBitmap bm0; + SkBitmap bm1; + + LocalScopedBitmap bitmap0(bm0Handle); + LocalScopedBitmap bitmap1(bm1Handle); + + // Paying the price for making Hardware Bitmap as Config: + // later check for colorType will pass successfully, + // because Hardware Config internally may be RGBA8888 or smth like that. + if (bitmap0->isHardware() != bitmap1->isHardware()) { + return JNI_FALSE; + } + + bitmap0->bitmap().getSkBitmap(&bm0); + bitmap1->bitmap().getSkBitmap(&bm1); + if (bm0.width() != bm1.width() + || bm0.height() != bm1.height() + || bm0.colorType() != bm1.colorType() + || bm0.alphaType() != bm1.alphaType() + || !SkColorSpace::Equals(bm0.colorSpace(), bm1.colorSpace())) { + return JNI_FALSE; + } + + // if we can't load the pixels, return false + if (NULL == bm0.getPixels() || NULL == bm1.getPixels()) { + return JNI_FALSE; + } + + // now compare each scanline. We can't do the entire buffer at once, + // since we don't care about the pixel values that might extend beyond + // the width (since the scanline might be larger than the logical width) + const int h = bm0.height(); + const size_t size = bm0.width() * bm0.bytesPerPixel(); + for (int y = 0; y < h; y++) { + // SkBitmap::getAddr(int, int) may return NULL due to unrecognized config + // (ex: kRLE_Index8_Config). This will cause memcmp method to crash. Since bm0 + // and bm1 both have pixel data() (have passed NULL == getPixels() check), + // those 2 bitmaps should be valid (only unrecognized), we return JNI_FALSE + // to warn user those 2 unrecognized config bitmaps may be different. + void *bm0Addr = bm0.getAddr(0, y); + void *bm1Addr = bm1.getAddr(0, y); + + if(bm0Addr == NULL || bm1Addr == NULL) { + return JNI_FALSE; + } + + if (memcmp(bm0Addr, bm1Addr, size) != 0) { + return JNI_FALSE; + } + } + return JNI_TRUE; +} + +static void Bitmap_prepareToDraw(JNIEnv* env, jobject, jlong bitmapPtr) { +#ifdef __ANDROID__ // Layoutlib does not support render thread + LocalScopedBitmap bitmapHandle(bitmapPtr); + if (!bitmapHandle.valid()) return; + android::uirenderer::renderthread::RenderProxy::prepareToDraw(bitmapHandle->bitmap()); +#endif +} + +static jint Bitmap_getAllocationByteCount(JNIEnv* env, jobject, jlong bitmapPtr) { + LocalScopedBitmap bitmapHandle(bitmapPtr); + return static_cast(bitmapHandle->getAllocationByteCount()); +} + +static jobject Bitmap_copyPreserveInternalConfig(JNIEnv* env, jobject, jlong bitmapPtr) { + LocalScopedBitmap bitmapHandle(bitmapPtr); + LOG_ALWAYS_FATAL_IF(!bitmapHandle->isHardware(), + "Hardware config is only supported config in Bitmap_nativeCopyPreserveInternalConfig"); + Bitmap& hwuiBitmap = bitmapHandle->bitmap(); + SkBitmap src; + hwuiBitmap.getSkBitmap(&src); + + if (src.pixelRef() == nullptr) { + doThrowRE(env, "Could not copy a hardware bitmap."); + return NULL; + } + + sk_sp bitmap = Bitmap::createFrom(src.info(), *src.pixelRef()); + return createBitmap(env, bitmap.release(), getPremulBitmapCreateFlags(false)); +} + +#ifdef __ANDROID__ // Layoutlib does not support graphic buffer +typedef AHardwareBuffer* (*AHB_from_HB)(JNIEnv*, jobject); +AHB_from_HB AHardwareBuffer_fromHardwareBuffer; + +typedef jobject (*AHB_to_HB)(JNIEnv*, AHardwareBuffer*); +AHB_to_HB AHardwareBuffer_toHardwareBuffer; +#endif + +static jobject Bitmap_wrapHardwareBufferBitmap(JNIEnv* env, jobject, jobject hardwareBuffer, + jlong colorSpacePtr) { +#ifdef __ANDROID__ // Layoutlib does not support graphic buffer + AHardwareBuffer* buffer = AHardwareBuffer_fromHardwareBuffer(env, hardwareBuffer); + sk_sp bitmap = Bitmap::createFrom(buffer, + GraphicsJNI::getNativeColorSpace(colorSpacePtr)); + if (!bitmap.get()) { + ALOGW("failed to create hardware bitmap from hardware buffer"); + return NULL; + } + return bitmap::createBitmap(env, bitmap.release(), getPremulBitmapCreateFlags(false)); +#else + return NULL; +#endif +} + +static jobject Bitmap_createGraphicBufferHandle(JNIEnv* env, jobject, jlong bitmapPtr) { +#ifdef __ANDROID__ // Layoutlib does not support graphic buffer + LocalScopedBitmap bitmapHandle(bitmapPtr); + LOG_ALWAYS_FATAL_IF(!bitmapHandle->isHardware(), + "Hardware config is only supported config in Bitmap_getGraphicBuffer"); + + Bitmap& bitmap = bitmapHandle->bitmap(); + return android_graphics_GraphicBuffer_createFromAHardwareBuffer(env, bitmap.hardwareBuffer()); +#else + return NULL; +#endif +} + +static jobject Bitmap_getHardwareBuffer(JNIEnv* env, jobject, jlong bitmapPtr) { +#ifdef __ANDROID__ // Layoutlib does not support graphic buffer + LocalScopedBitmap bitmapHandle(bitmapPtr); + LOG_ALWAYS_FATAL_IF(!bitmapHandle->isHardware(), + "Hardware config is only supported config in Bitmap_getHardwareBuffer"); + + Bitmap& bitmap = bitmapHandle->bitmap(); + return AHardwareBuffer_toHardwareBuffer(env, bitmap.hardwareBuffer()); +#else + return NULL; +#endif +} + +static jboolean Bitmap_isImmutable(CRITICAL_JNI_PARAMS_COMMA jlong bitmapHandle) { + LocalScopedBitmap bitmapHolder(bitmapHandle); + if (!bitmapHolder.valid()) return JNI_FALSE; + + return bitmapHolder->bitmap().isImmutable() ? JNI_TRUE : JNI_FALSE; +} + +static void Bitmap_setImmutable(JNIEnv* env, jobject, jlong bitmapHandle) { + LocalScopedBitmap bitmapHolder(bitmapHandle); + if (!bitmapHolder.valid()) return; + + return bitmapHolder->bitmap().setImmutable(); +} + +/////////////////////////////////////////////////////////////////////////////// + +static const JNINativeMethod gBitmapMethods[] = { + { "nativeCreate", "([IIIIIIZJ)Landroid/graphics/Bitmap;", + (void*)Bitmap_creator }, + { "nativeCopy", "(JIZ)Landroid/graphics/Bitmap;", + (void*)Bitmap_copy }, + { "nativeCopyAshmem", "(J)Landroid/graphics/Bitmap;", + (void*)Bitmap_copyAshmem }, + { "nativeCopyAshmemConfig", "(JI)Landroid/graphics/Bitmap;", + (void*)Bitmap_copyAshmemConfig }, + { "nativeGetNativeFinalizer", "()J", (void*)Bitmap_getNativeFinalizer }, + { "nativeRecycle", "(J)V", (void*)Bitmap_recycle }, + { "nativeReconfigure", "(JIIIZ)V", (void*)Bitmap_reconfigure }, + { "nativeCompress", "(JIILjava/io/OutputStream;[B)Z", + (void*)Bitmap_compress }, + { "nativeErase", "(JI)V", (void*)Bitmap_erase }, + { "nativeErase", "(JJJ)V", (void*)Bitmap_eraseLong }, + { "nativeRowBytes", "(J)I", (void*)Bitmap_rowBytes }, + { "nativeConfig", "(J)I", (void*)Bitmap_config }, + { "nativeHasAlpha", "(J)Z", (void*)Bitmap_hasAlpha }, + { "nativeIsPremultiplied", "(J)Z", (void*)Bitmap_isPremultiplied}, + { "nativeSetHasAlpha", "(JZZ)V", (void*)Bitmap_setHasAlpha}, + { "nativeSetPremultiplied", "(JZ)V", (void*)Bitmap_setPremultiplied}, + { "nativeHasMipMap", "(J)Z", (void*)Bitmap_hasMipMap }, + { "nativeSetHasMipMap", "(JZ)V", (void*)Bitmap_setHasMipMap }, + { "nativeCreateFromParcel", + "(Landroid/os/Parcel;)Landroid/graphics/Bitmap;", + (void*)Bitmap_createFromParcel }, + { "nativeWriteToParcel", "(JILandroid/os/Parcel;)Z", + (void*)Bitmap_writeToParcel }, + { "nativeExtractAlpha", "(JJ[I)Landroid/graphics/Bitmap;", + (void*)Bitmap_extractAlpha }, + { "nativeGenerationId", "(J)I", (void*)Bitmap_getGenerationId }, + { "nativeGetPixel", "(JII)I", (void*)Bitmap_getPixel }, + { "nativeGetColor", "(JII)J", (void*)Bitmap_getColor }, + { "nativeGetPixels", "(J[IIIIIII)V", (void*)Bitmap_getPixels }, + { "nativeSetPixel", "(JIII)V", (void*)Bitmap_setPixel }, + { "nativeSetPixels", "(J[IIIIIII)V", (void*)Bitmap_setPixels }, + { "nativeCopyPixelsToBuffer", "(JLjava/nio/Buffer;)V", + (void*)Bitmap_copyPixelsToBuffer }, + { "nativeCopyPixelsFromBuffer", "(JLjava/nio/Buffer;)V", + (void*)Bitmap_copyPixelsFromBuffer }, + { "nativeSameAs", "(JJ)Z", (void*)Bitmap_sameAs }, + { "nativePrepareToDraw", "(J)V", (void*)Bitmap_prepareToDraw }, + { "nativeGetAllocationByteCount", "(J)I", (void*)Bitmap_getAllocationByteCount }, + { "nativeCopyPreserveInternalConfig", "(J)Landroid/graphics/Bitmap;", + (void*)Bitmap_copyPreserveInternalConfig }, + { "nativeWrapHardwareBufferBitmap", "(Landroid/hardware/HardwareBuffer;J)Landroid/graphics/Bitmap;", + (void*) Bitmap_wrapHardwareBufferBitmap }, + { "nativeCreateGraphicBufferHandle", "(J)Landroid/graphics/GraphicBuffer;", + (void*) Bitmap_createGraphicBufferHandle }, + { "nativeGetHardwareBuffer", "(J)Landroid/hardware/HardwareBuffer;", + (void*) Bitmap_getHardwareBuffer }, + { "nativeComputeColorSpace", "(J)Landroid/graphics/ColorSpace;", (void*)Bitmap_computeColorSpace }, + { "nativeSetColorSpace", "(JJ)V", (void*)Bitmap_setColorSpace }, + { "nativeIsSRGB", "(J)Z", (void*)Bitmap_isSRGB }, + { "nativeIsSRGBLinear", "(J)Z", (void*)Bitmap_isSRGBLinear}, + { "nativeSetImmutable", "(J)V", (void*)Bitmap_setImmutable}, + + // ------------ @CriticalNative ---------------- + { "nativeIsImmutable", "(J)Z", (void*)Bitmap_isImmutable} + +}; + +int register_android_graphics_Bitmap(JNIEnv* env) +{ + gBitmap_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Bitmap")); + gBitmap_nativePtr = GetFieldIDOrDie(env, gBitmap_class, "mNativePtr", "J"); + gBitmap_constructorMethodID = GetMethodIDOrDie(env, gBitmap_class, "", "(JIIIZ[BLandroid/graphics/NinePatch$InsetStruct;Z)V"); + gBitmap_reinitMethodID = GetMethodIDOrDie(env, gBitmap_class, "reinit", "(IIZ)V"); + +#ifdef __ANDROID__ // Layoutlib does not support graphic buffer + void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE); + AHardwareBuffer_fromHardwareBuffer = + (AHB_from_HB)dlsym(handle_, "AHardwareBuffer_fromHardwareBuffer"); + LOG_ALWAYS_FATAL_IF(AHardwareBuffer_fromHardwareBuffer == nullptr, + "Failed to find required symbol AHardwareBuffer_fromHardwareBuffer!"); + + AHardwareBuffer_toHardwareBuffer = (AHB_to_HB)dlsym(handle_, "AHardwareBuffer_toHardwareBuffer"); + LOG_ALWAYS_FATAL_IF(AHardwareBuffer_toHardwareBuffer == nullptr, + " Failed to find required symbol AHardwareBuffer_toHardwareBuffer!"); +#endif + return android::RegisterMethodsOrDie(env, "android/graphics/Bitmap", gBitmapMethods, + NELEM(gBitmapMethods)); +} diff --git a/libs/hwui/jni/Bitmap.h b/libs/hwui/jni/Bitmap.h new file mode 100644 index 000000000000..73eca3aa8ef8 --- /dev/null +++ b/libs/hwui/jni/Bitmap.h @@ -0,0 +1,53 @@ +/* + * 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 BITMAP_H_ +#define BITMAP_H_ + +#include +#include + +class SkBitmap; +struct SkImageInfo; + +namespace android { + +class Bitmap; + +namespace bitmap { + +enum BitmapCreateFlags { + kBitmapCreateFlag_None = 0x0, + kBitmapCreateFlag_Mutable = 0x1, + kBitmapCreateFlag_Premultiplied = 0x2, +}; + +jobject createBitmap(JNIEnv* env, Bitmap* bitmap, + int bitmapCreateFlags, jbyteArray ninePatchChunk = nullptr, + jobject ninePatchInsets = nullptr, int density = -1); + +Bitmap& toBitmap(jlong bitmapHandle); + +/** Reinitialize a bitmap. bitmap must already have its SkAlphaType set in + sync with isPremultiplied +*/ +void reinitBitmap(JNIEnv* env, jobject javaBitmap, const SkImageInfo& info, + bool isPremultiplied); + +} // namespace bitmap + +} // namespace android + +#endif /* BITMAP_H_ */ diff --git a/libs/hwui/jni/BitmapFactory.cpp b/libs/hwui/jni/BitmapFactory.cpp new file mode 100644 index 000000000000..adedffdd731c --- /dev/null +++ b/libs/hwui/jni/BitmapFactory.cpp @@ -0,0 +1,664 @@ +#define LOG_TAG "BitmapFactory" + +#include "BitmapFactory.h" +#include "CreateJavaOutputStreamAdaptor.h" +#include "GraphicsJNI.h" +#include "NinePatchPeeker.h" +#include "SkAndroidCodec.h" +#include "SkBRDAllocator.h" +#include "SkFrontBufferedStream.h" +#include "SkMath.h" +#include "SkPixelRef.h" +#include "SkStream.h" +#include "SkUtils.h" +#include "Utils.h" +#include "core_jni_helpers.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +jfieldID gOptions_justBoundsFieldID; +jfieldID gOptions_sampleSizeFieldID; +jfieldID gOptions_configFieldID; +jfieldID gOptions_colorSpaceFieldID; +jfieldID gOptions_premultipliedFieldID; +jfieldID gOptions_mutableFieldID; +jfieldID gOptions_ditherFieldID; +jfieldID gOptions_preferQualityOverSpeedFieldID; +jfieldID gOptions_scaledFieldID; +jfieldID gOptions_densityFieldID; +jfieldID gOptions_screenDensityFieldID; +jfieldID gOptions_targetDensityFieldID; +jfieldID gOptions_widthFieldID; +jfieldID gOptions_heightFieldID; +jfieldID gOptions_mimeFieldID; +jfieldID gOptions_outConfigFieldID; +jfieldID gOptions_outColorSpaceFieldID; +jfieldID gOptions_mCancelID; +jfieldID gOptions_bitmapFieldID; + +jfieldID gBitmap_ninePatchInsetsFieldID; + +jclass gBitmapConfig_class; +jmethodID gBitmapConfig_nativeToConfigMethodID; + +using namespace android; + +const char* getMimeType(SkEncodedImageFormat format) { + switch (format) { + case SkEncodedImageFormat::kBMP: + return "image/bmp"; + case SkEncodedImageFormat::kGIF: + return "image/gif"; + case SkEncodedImageFormat::kICO: + return "image/x-ico"; + case SkEncodedImageFormat::kJPEG: + return "image/jpeg"; + case SkEncodedImageFormat::kPNG: + return "image/png"; + case SkEncodedImageFormat::kWEBP: + return "image/webp"; + case SkEncodedImageFormat::kHEIF: + return "image/heif"; + case SkEncodedImageFormat::kWBMP: + return "image/vnd.wap.wbmp"; + case SkEncodedImageFormat::kDNG: + return "image/x-adobe-dng"; + default: + return nullptr; + } +} + +jstring getMimeTypeAsJavaString(JNIEnv* env, SkEncodedImageFormat format) { + jstring jstr = nullptr; + const char* mimeType = getMimeType(format); + if (mimeType) { + // NOTE: Caller should env->ExceptionCheck() for OOM + // (can't check for nullptr as it's a valid return value) + jstr = env->NewStringUTF(mimeType); + } + return jstr; +} + +class ScaleCheckingAllocator : public SkBitmap::HeapAllocator { +public: + ScaleCheckingAllocator(float scale, int size) + : mScale(scale), mSize(size) { + } + + virtual bool allocPixelRef(SkBitmap* bitmap) { + // accounts for scale in final allocation, using eventual size and config + const int bytesPerPixel = SkColorTypeBytesPerPixel(bitmap->colorType()); + const int requestedSize = bytesPerPixel * + int(bitmap->width() * mScale + 0.5f) * + int(bitmap->height() * mScale + 0.5f); + if (requestedSize > mSize) { + ALOGW("bitmap for alloc reuse (%d bytes) can't fit scaled bitmap (%d bytes)", + mSize, requestedSize); + return false; + } + return SkBitmap::HeapAllocator::allocPixelRef(bitmap); + } +private: + const float mScale; + const int mSize; +}; + +class RecyclingPixelAllocator : public SkBitmap::Allocator { +public: + RecyclingPixelAllocator(android::Bitmap* bitmap, unsigned int size) + : mBitmap(bitmap), mSize(size) { + } + + ~RecyclingPixelAllocator() { + } + + virtual bool allocPixelRef(SkBitmap* bitmap) { + const SkImageInfo& info = bitmap->info(); + if (info.colorType() == kUnknown_SkColorType) { + ALOGW("unable to reuse a bitmap as the target has an unknown bitmap configuration"); + return false; + } + + const size_t size = info.computeByteSize(bitmap->rowBytes()); + if (size > SK_MaxS32) { + ALOGW("bitmap is too large"); + return false; + } + + if (size > mSize) { + ALOGW("bitmap marked for reuse (%u bytes) can't fit new bitmap " + "(%zu bytes)", mSize, size); + return false; + } + + mBitmap->reconfigure(info, bitmap->rowBytes()); + bitmap->setPixelRef(sk_ref_sp(mBitmap), 0, 0); + return true; + } + +private: + android::Bitmap* const mBitmap; + const unsigned int mSize; +}; + +// Necessary for decodes when the native decoder cannot scale to appropriately match the sampleSize +// (for example, RAW). If the sampleSize divides evenly into the dimension, we require that the +// scale matches exactly. If sampleSize does not divide evenly, we allow the decoder to choose how +// best to round. +static bool needsFineScale(const int fullSize, const int decodedSize, const int sampleSize) { + if (fullSize % sampleSize == 0 && fullSize / sampleSize != decodedSize) { + return true; + } else if ((fullSize / sampleSize + 1) != decodedSize && + (fullSize / sampleSize) != decodedSize) { + return true; + } + return false; +} + +static bool needsFineScale(const SkISize fullSize, const SkISize decodedSize, + const int sampleSize) { + return needsFineScale(fullSize.width(), decodedSize.width(), sampleSize) || + needsFineScale(fullSize.height(), decodedSize.height(), sampleSize); +} + +static jobject doDecode(JNIEnv* env, std::unique_ptr stream, + jobject padding, jobject options, jlong inBitmapHandle, + jlong colorSpaceHandle) { + // Set default values for the options parameters. + int sampleSize = 1; + bool onlyDecodeSize = false; + SkColorType prefColorType = kN32_SkColorType; + bool isHardware = false; + bool isMutable = false; + float scale = 1.0f; + bool requireUnpremultiplied = false; + jobject javaBitmap = NULL; + sk_sp prefColorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle); + + // Update with options supplied by the client. + if (options != NULL) { + sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); + // Correct a non-positive sampleSize. sampleSize defaults to zero within the + // options object, which is strange. + if (sampleSize <= 0) { + sampleSize = 1; + } + + if (env->GetBooleanField(options, gOptions_justBoundsFieldID)) { + onlyDecodeSize = true; + } + + // initialize these, in case we fail later on + env->SetIntField(options, gOptions_widthFieldID, -1); + env->SetIntField(options, gOptions_heightFieldID, -1); + env->SetObjectField(options, gOptions_mimeFieldID, 0); + env->SetObjectField(options, gOptions_outConfigFieldID, 0); + env->SetObjectField(options, gOptions_outColorSpaceFieldID, 0); + + jobject jconfig = env->GetObjectField(options, gOptions_configFieldID); + prefColorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig); + isHardware = GraphicsJNI::isHardwareConfig(env, jconfig); + isMutable = env->GetBooleanField(options, gOptions_mutableFieldID); + requireUnpremultiplied = !env->GetBooleanField(options, gOptions_premultipliedFieldID); + javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID); + + if (env->GetBooleanField(options, gOptions_scaledFieldID)) { + const int density = env->GetIntField(options, gOptions_densityFieldID); + const int targetDensity = env->GetIntField(options, gOptions_targetDensityFieldID); + const int screenDensity = env->GetIntField(options, gOptions_screenDensityFieldID); + if (density != 0 && targetDensity != 0 && density != screenDensity) { + scale = (float) targetDensity / density; + } + } + } + + if (isMutable && isHardware) { + doThrowIAE(env, "Bitmaps with Config.HARDWARE are always immutable"); + return nullObjectReturn("Cannot create mutable hardware bitmap"); + } + + // Create the codec. + NinePatchPeeker peeker; + std::unique_ptr codec; + { + SkCodec::Result result; + std::unique_ptr c = SkCodec::MakeFromStream(std::move(stream), &result, + &peeker); + if (!c) { + SkString msg; + msg.printf("Failed to create image decoder with message '%s'", + SkCodec::ResultToString(result)); + return nullObjectReturn(msg.c_str()); + } + + codec = SkAndroidCodec::MakeFromCodec(std::move(c)); + if (!codec) { + return nullObjectReturn("SkAndroidCodec::MakeFromCodec returned null"); + } + } + + // Do not allow ninepatch decodes to 565. In the past, decodes to 565 + // would dither, and we do not want to pre-dither ninepatches, since we + // know that they will be stretched. We no longer dither 565 decodes, + // but we continue to prevent ninepatches from decoding to 565, in order + // to maintain the old behavior. + if (peeker.mPatch && kRGB_565_SkColorType == prefColorType) { + prefColorType = kN32_SkColorType; + } + + // Determine the output size. + SkISize size = codec->getSampledDimensions(sampleSize); + + int scaledWidth = size.width(); + int scaledHeight = size.height(); + bool willScale = false; + + // Apply a fine scaling step if necessary. + if (needsFineScale(codec->getInfo().dimensions(), size, sampleSize)) { + willScale = true; + scaledWidth = codec->getInfo().width() / sampleSize; + scaledHeight = codec->getInfo().height() / sampleSize; + } + + // Set the decode colorType + SkColorType decodeColorType = codec->computeOutputColorType(prefColorType); + if (decodeColorType == kRGBA_F16_SkColorType && isHardware && + !uirenderer::HardwareBitmapUploader::hasFP16Support()) { + decodeColorType = kN32_SkColorType; + } + + sk_sp decodeColorSpace = codec->computeOutputColorSpace( + decodeColorType, prefColorSpace); + + // Set the options and return if the client only wants the size. + if (options != NULL) { + jstring mimeType = getMimeTypeAsJavaString(env, codec->getEncodedFormat()); + if (env->ExceptionCheck()) { + return nullObjectReturn("OOM in getMimeTypeAsJavaString()"); + } + env->SetIntField(options, gOptions_widthFieldID, scaledWidth); + env->SetIntField(options, gOptions_heightFieldID, scaledHeight); + env->SetObjectField(options, gOptions_mimeFieldID, mimeType); + + jint configID = GraphicsJNI::colorTypeToLegacyBitmapConfig(decodeColorType); + if (isHardware) { + configID = GraphicsJNI::kHardware_LegacyBitmapConfig; + } + jobject config = env->CallStaticObjectMethod(gBitmapConfig_class, + gBitmapConfig_nativeToConfigMethodID, configID); + env->SetObjectField(options, gOptions_outConfigFieldID, config); + + env->SetObjectField(options, gOptions_outColorSpaceFieldID, + GraphicsJNI::getColorSpace(env, decodeColorSpace.get(), decodeColorType)); + + if (onlyDecodeSize) { + return nullptr; + } + } + + // Scale is necessary due to density differences. + if (scale != 1.0f) { + willScale = true; + scaledWidth = static_cast(scaledWidth * scale + 0.5f); + scaledHeight = static_cast(scaledHeight * scale + 0.5f); + } + + android::Bitmap* reuseBitmap = nullptr; + unsigned int existingBufferSize = 0; + if (javaBitmap != nullptr) { + reuseBitmap = &bitmap::toBitmap(inBitmapHandle); + if (reuseBitmap->isImmutable()) { + ALOGW("Unable to reuse an immutable bitmap as an image decoder target."); + javaBitmap = nullptr; + reuseBitmap = nullptr; + } else { + existingBufferSize = reuseBitmap->getAllocationByteCount(); + } + } + + HeapAllocator defaultAllocator; + RecyclingPixelAllocator recyclingAllocator(reuseBitmap, existingBufferSize); + ScaleCheckingAllocator scaleCheckingAllocator(scale, existingBufferSize); + SkBitmap::HeapAllocator heapAllocator; + SkBitmap::Allocator* decodeAllocator; + if (javaBitmap != nullptr && willScale) { + // This will allocate pixels using a HeapAllocator, since there will be an extra + // scaling step that copies these pixels into Java memory. This allocator + // also checks that the recycled javaBitmap is large enough. + decodeAllocator = &scaleCheckingAllocator; + } else if (javaBitmap != nullptr) { + decodeAllocator = &recyclingAllocator; + } else if (willScale || isHardware) { + // This will allocate pixels using a HeapAllocator, + // for scale case: there will be an extra scaling step. + // for hardware case: there will be extra swizzling & upload to gralloc step. + decodeAllocator = &heapAllocator; + } else { + decodeAllocator = &defaultAllocator; + } + + SkAlphaType alphaType = codec->computeOutputAlphaType(requireUnpremultiplied); + + const SkImageInfo decodeInfo = SkImageInfo::Make(size.width(), size.height(), + decodeColorType, alphaType, decodeColorSpace); + + SkImageInfo bitmapInfo = decodeInfo; + if (decodeColorType == kGray_8_SkColorType) { + // The legacy implementation of BitmapFactory used kAlpha8 for + // grayscale images (before kGray8 existed). While the codec + // recognizes kGray8, we need to decode into a kAlpha8 bitmap + // in order to avoid a behavior change. + bitmapInfo = + bitmapInfo.makeColorType(kAlpha_8_SkColorType).makeAlphaType(kPremul_SkAlphaType); + } + SkBitmap decodingBitmap; + if (!decodingBitmap.setInfo(bitmapInfo) || + !decodingBitmap.tryAllocPixels(decodeAllocator)) { + // SkAndroidCodec should recommend a valid SkImageInfo, so setInfo() + // should only only fail if the calculated value for rowBytes is too + // large. + // tryAllocPixels() can fail due to OOM on the Java heap, OOM on the + // native heap, or the recycled javaBitmap being too small to reuse. + return nullptr; + } + + // Use SkAndroidCodec to perform the decode. + SkAndroidCodec::AndroidOptions codecOptions; + codecOptions.fZeroInitialized = decodeAllocator == &defaultAllocator ? + SkCodec::kYes_ZeroInitialized : SkCodec::kNo_ZeroInitialized; + codecOptions.fSampleSize = sampleSize; + SkCodec::Result result = codec->getAndroidPixels(decodeInfo, decodingBitmap.getPixels(), + decodingBitmap.rowBytes(), &codecOptions); + switch (result) { + case SkCodec::kSuccess: + case SkCodec::kIncompleteInput: + break; + default: + return nullObjectReturn("codec->getAndroidPixels() failed."); + } + + // This is weird so let me explain: we could use the scale parameter + // directly, but for historical reasons this is how the corresponding + // Dalvik code has always behaved. We simply recreate the behavior here. + // The result is slightly different from simply using scale because of + // the 0.5f rounding bias applied when computing the target image size + const float scaleX = scaledWidth / float(decodingBitmap.width()); + const float scaleY = scaledHeight / float(decodingBitmap.height()); + + jbyteArray ninePatchChunk = NULL; + if (peeker.mPatch != NULL) { + if (willScale) { + peeker.scale(scaleX, scaleY, scaledWidth, scaledHeight); + } + + size_t ninePatchArraySize = peeker.mPatch->serializedSize(); + ninePatchChunk = env->NewByteArray(ninePatchArraySize); + if (ninePatchChunk == NULL) { + return nullObjectReturn("ninePatchChunk == null"); + } + + jbyte* array = (jbyte*) env->GetPrimitiveArrayCritical(ninePatchChunk, NULL); + if (array == NULL) { + return nullObjectReturn("primitive array == null"); + } + + memcpy(array, peeker.mPatch, peeker.mPatchSize); + env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0); + } + + jobject ninePatchInsets = NULL; + if (peeker.mHasInsets) { + ninePatchInsets = peeker.createNinePatchInsets(env, scale); + if (ninePatchInsets == NULL) { + return nullObjectReturn("nine patch insets == null"); + } + if (javaBitmap != NULL) { + env->SetObjectField(javaBitmap, gBitmap_ninePatchInsetsFieldID, ninePatchInsets); + } + } + + SkBitmap outputBitmap; + if (willScale) { + // Set the allocator for the outputBitmap. + SkBitmap::Allocator* outputAllocator; + if (javaBitmap != nullptr) { + outputAllocator = &recyclingAllocator; + } else { + outputAllocator = &defaultAllocator; + } + + SkColorType scaledColorType = decodingBitmap.colorType(); + // FIXME: If the alphaType is kUnpremul and the image has alpha, the + // colors may not be correct, since Skia does not yet support drawing + // to/from unpremultiplied bitmaps. + outputBitmap.setInfo( + bitmapInfo.makeWH(scaledWidth, scaledHeight).makeColorType(scaledColorType)); + if (!outputBitmap.tryAllocPixels(outputAllocator)) { + // This should only fail on OOM. The recyclingAllocator should have + // enough memory since we check this before decoding using the + // scaleCheckingAllocator. + return nullObjectReturn("allocation failed for scaled bitmap"); + } + + SkPaint paint; + // kSrc_Mode instructs us to overwrite the uninitialized pixels in + // outputBitmap. Otherwise we would blend by default, which is not + // what we want. + paint.setBlendMode(SkBlendMode::kSrc); + paint.setFilterQuality(kLow_SkFilterQuality); // bilinear filtering + + SkCanvas canvas(outputBitmap, SkCanvas::ColorBehavior::kLegacy); + canvas.scale(scaleX, scaleY); + canvas.drawBitmap(decodingBitmap, 0.0f, 0.0f, &paint); + } else { + outputBitmap.swap(decodingBitmap); + } + + if (padding) { + peeker.getPadding(env, padding); + } + + // If we get here, the outputBitmap should have an installed pixelref. + if (outputBitmap.pixelRef() == NULL) { + return nullObjectReturn("Got null SkPixelRef"); + } + + if (!isMutable && javaBitmap == NULL) { + // promise we will never change our pixels (great for sharing and pictures) + outputBitmap.setImmutable(); + } + + bool isPremultiplied = !requireUnpremultiplied; + if (javaBitmap != nullptr) { + bitmap::reinitBitmap(env, javaBitmap, outputBitmap.info(), isPremultiplied); + outputBitmap.notifyPixelsChanged(); + // If a java bitmap was passed in for reuse, pass it back + return javaBitmap; + } + + int bitmapCreateFlags = 0x0; + if (isMutable) bitmapCreateFlags |= android::bitmap::kBitmapCreateFlag_Mutable; + if (isPremultiplied) bitmapCreateFlags |= android::bitmap::kBitmapCreateFlag_Premultiplied; + + if (isHardware) { + sk_sp hardwareBitmap = Bitmap::allocateHardwareBitmap(outputBitmap); + if (!hardwareBitmap.get()) { + return nullObjectReturn("Failed to allocate a hardware bitmap"); + } + return bitmap::createBitmap(env, hardwareBitmap.release(), bitmapCreateFlags, + ninePatchChunk, ninePatchInsets, -1); + } + + // now create the java bitmap + return bitmap::createBitmap(env, defaultAllocator.getStorageObjAndReset(), + bitmapCreateFlags, ninePatchChunk, ninePatchInsets, -1); +} + +static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage, + jobject padding, jobject options, jlong inBitmapHandle, jlong colorSpaceHandle) { + + jobject bitmap = NULL; + std::unique_ptr stream(CreateJavaInputStreamAdaptor(env, is, storage)); + + if (stream.get()) { + std::unique_ptr bufferedStream( + SkFrontBufferedStream::Make(std::move(stream), SkCodec::MinBufferedBytesNeeded())); + SkASSERT(bufferedStream.get() != NULL); + bitmap = doDecode(env, std::move(bufferedStream), padding, options, inBitmapHandle, + colorSpaceHandle); + } + return bitmap; +} + +static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fileDescriptor, + jobject padding, jobject bitmapFactoryOptions, jlong inBitmapHandle, jlong colorSpaceHandle) { + + NPE_CHECK_RETURN_ZERO(env, fileDescriptor); + + int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor); + + struct stat fdStat; + if (fstat(descriptor, &fdStat) == -1) { + doThrowIOE(env, "broken file descriptor"); + return nullObjectReturn("fstat return -1"); + } + + // Restore the descriptor's offset on exiting this function. Even though + // we dup the descriptor, both the original and dup refer to the same open + // file description and changes to the file offset in one impact the other. + AutoFDSeek autoRestore(descriptor); + + // Duplicate the descriptor here to prevent leaking memory. A leak occurs + // if we only close the file descriptor and not the file object it is used to + // create. If we don't explicitly clean up the file (which in turn closes the + // descriptor) the buffers allocated internally by fseek will be leaked. + int dupDescriptor = fcntl(descriptor, F_DUPFD_CLOEXEC, 0); + + FILE* file = fdopen(dupDescriptor, "r"); + if (file == NULL) { + // cleanup the duplicated descriptor since it will not be closed when the + // file is cleaned up (fclose). + close(dupDescriptor); + return nullObjectReturn("Could not open file"); + } + + std::unique_ptr fileStream(new SkFILEStream(file)); + + // If there is no offset for the file descriptor, we use SkFILEStream directly. + if (::lseek(descriptor, 0, SEEK_CUR) == 0) { + assert(isSeekable(dupDescriptor)); + return doDecode(env, std::move(fileStream), padding, bitmapFactoryOptions, + inBitmapHandle, colorSpaceHandle); + } + + // Use a buffered stream. Although an SkFILEStream can be rewound, this + // ensures that SkImageDecoder::Factory never rewinds beyond the + // current position of the file descriptor. + std::unique_ptr stream(SkFrontBufferedStream::Make(std::move(fileStream), + SkCodec::MinBufferedBytesNeeded())); + + return doDecode(env, std::move(stream), padding, bitmapFactoryOptions, inBitmapHandle, + colorSpaceHandle); +} + +static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, jlong native_asset, + jobject padding, jobject options, jlong inBitmapHandle, jlong colorSpaceHandle) { + + Asset* asset = reinterpret_cast(native_asset); + // since we know we'll be done with the asset when we return, we can + // just use a simple wrapper + return doDecode(env, std::make_unique(asset), padding, options, + inBitmapHandle, colorSpaceHandle); +} + +static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray, + jint offset, jint length, jobject options, jlong inBitmapHandle, jlong colorSpaceHandle) { + + AutoJavaByteArray ar(env, byteArray); + return doDecode(env, std::make_unique(ar.ptr() + offset, length, false), + nullptr, options, inBitmapHandle, colorSpaceHandle); +} + +static jboolean nativeIsSeekable(JNIEnv* env, jobject, jobject fileDescriptor) { + jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor); + return isSeekable(descriptor) ? JNI_TRUE : JNI_FALSE; +} + +/////////////////////////////////////////////////////////////////////////////// + +static const JNINativeMethod gMethods[] = { + { "nativeDecodeStream", + "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;JJ)Landroid/graphics/Bitmap;", + (void*)nativeDecodeStream + }, + + { "nativeDecodeFileDescriptor", + "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;JJ)Landroid/graphics/Bitmap;", + (void*)nativeDecodeFileDescriptor + }, + + { "nativeDecodeAsset", + "(JLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;JJ)Landroid/graphics/Bitmap;", + (void*)nativeDecodeAsset + }, + + { "nativeDecodeByteArray", + "([BIILandroid/graphics/BitmapFactory$Options;JJ)Landroid/graphics/Bitmap;", + (void*)nativeDecodeByteArray + }, + + { "nativeIsSeekable", + "(Ljava/io/FileDescriptor;)Z", + (void*)nativeIsSeekable + }, +}; + +int register_android_graphics_BitmapFactory(JNIEnv* env) { + jclass options_class = FindClassOrDie(env, "android/graphics/BitmapFactory$Options"); + gOptions_bitmapFieldID = GetFieldIDOrDie(env, options_class, "inBitmap", + "Landroid/graphics/Bitmap;"); + gOptions_justBoundsFieldID = GetFieldIDOrDie(env, options_class, "inJustDecodeBounds", "Z"); + gOptions_sampleSizeFieldID = GetFieldIDOrDie(env, options_class, "inSampleSize", "I"); + gOptions_configFieldID = GetFieldIDOrDie(env, options_class, "inPreferredConfig", + "Landroid/graphics/Bitmap$Config;"); + gOptions_colorSpaceFieldID = GetFieldIDOrDie(env, options_class, "inPreferredColorSpace", + "Landroid/graphics/ColorSpace;"); + gOptions_premultipliedFieldID = GetFieldIDOrDie(env, options_class, "inPremultiplied", "Z"); + gOptions_mutableFieldID = GetFieldIDOrDie(env, options_class, "inMutable", "Z"); + gOptions_ditherFieldID = GetFieldIDOrDie(env, options_class, "inDither", "Z"); + gOptions_preferQualityOverSpeedFieldID = GetFieldIDOrDie(env, options_class, + "inPreferQualityOverSpeed", "Z"); + gOptions_scaledFieldID = GetFieldIDOrDie(env, options_class, "inScaled", "Z"); + gOptions_densityFieldID = GetFieldIDOrDie(env, options_class, "inDensity", "I"); + gOptions_screenDensityFieldID = GetFieldIDOrDie(env, options_class, "inScreenDensity", "I"); + gOptions_targetDensityFieldID = GetFieldIDOrDie(env, options_class, "inTargetDensity", "I"); + gOptions_widthFieldID = GetFieldIDOrDie(env, options_class, "outWidth", "I"); + gOptions_heightFieldID = GetFieldIDOrDie(env, options_class, "outHeight", "I"); + gOptions_mimeFieldID = GetFieldIDOrDie(env, options_class, "outMimeType", "Ljava/lang/String;"); + gOptions_outConfigFieldID = GetFieldIDOrDie(env, options_class, "outConfig", + "Landroid/graphics/Bitmap$Config;"); + gOptions_outColorSpaceFieldID = GetFieldIDOrDie(env, options_class, "outColorSpace", + "Landroid/graphics/ColorSpace;"); + gOptions_mCancelID = GetFieldIDOrDie(env, options_class, "mCancel", "Z"); + + jclass bitmap_class = FindClassOrDie(env, "android/graphics/Bitmap"); + gBitmap_ninePatchInsetsFieldID = GetFieldIDOrDie(env, bitmap_class, "mNinePatchInsets", + "Landroid/graphics/NinePatch$InsetStruct;"); + + gBitmapConfig_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, + "android/graphics/Bitmap$Config")); + gBitmapConfig_nativeToConfigMethodID = GetStaticMethodIDOrDie(env, gBitmapConfig_class, + "nativeToConfig", "(I)Landroid/graphics/Bitmap$Config;"); + + return android::RegisterMethodsOrDie(env, "android/graphics/BitmapFactory", + gMethods, NELEM(gMethods)); +} diff --git a/libs/hwui/jni/BitmapFactory.h b/libs/hwui/jni/BitmapFactory.h new file mode 100644 index 000000000000..45bffc44967d --- /dev/null +++ b/libs/hwui/jni/BitmapFactory.h @@ -0,0 +1,31 @@ +#ifndef _ANDROID_GRAPHICS_BITMAP_FACTORY_H_ +#define _ANDROID_GRAPHICS_BITMAP_FACTORY_H_ + +#include "GraphicsJNI.h" +#include "SkEncodedImageFormat.h" + +extern jclass gOptions_class; +extern jfieldID gOptions_justBoundsFieldID; +extern jfieldID gOptions_sampleSizeFieldID; +extern jfieldID gOptions_configFieldID; +extern jfieldID gOptions_colorSpaceFieldID; +extern jfieldID gOptions_premultipliedFieldID; +extern jfieldID gOptions_ditherFieldID; +extern jfieldID gOptions_purgeableFieldID; +extern jfieldID gOptions_shareableFieldID; +extern jfieldID gOptions_nativeAllocFieldID; +extern jfieldID gOptions_preferQualityOverSpeedFieldID; +extern jfieldID gOptions_widthFieldID; +extern jfieldID gOptions_heightFieldID; +extern jfieldID gOptions_mimeFieldID; +extern jfieldID gOptions_outConfigFieldID; +extern jfieldID gOptions_outColorSpaceFieldID; +extern jfieldID gOptions_mCancelID; +extern jfieldID gOptions_bitmapFieldID; + +extern jclass gBitmapConfig_class; +extern jmethodID gBitmapConfig_nativeToConfigMethodID; + +jstring getMimeTypeAsJavaString(JNIEnv*, SkEncodedImageFormat); + +#endif // _ANDROID_GRAPHICS_BITMAP_FACTORY_H_ diff --git a/libs/hwui/jni/BitmapRegionDecoder.cpp b/libs/hwui/jni/BitmapRegionDecoder.cpp new file mode 100644 index 000000000000..06b4ff849097 --- /dev/null +++ b/libs/hwui/jni/BitmapRegionDecoder.cpp @@ -0,0 +1,291 @@ +/* + * 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 "BitmapRegionDecoder" + +#include "BitmapFactory.h" +#include "CreateJavaOutputStreamAdaptor.h" +#include "GraphicsJNI.h" +#include "Utils.h" + +#include "SkBitmap.h" +#include "SkBitmapRegionDecoder.h" +#include "SkCodec.h" +#include "SkData.h" +#include "SkStream.h" + +#include "core_jni_helpers.h" + +#include +#include +#include +#include +#include + +#include + +using namespace android; + +static jobject createBitmapRegionDecoder(JNIEnv* env, std::unique_ptr stream) { + std::unique_ptr brd( + SkBitmapRegionDecoder::Create(stream.release(), + SkBitmapRegionDecoder::kAndroidCodec_Strategy)); + if (!brd) { + doThrowIOE(env, "Image format not supported"); + return nullObjectReturn("CreateBitmapRegionDecoder returned null"); + } + + return GraphicsJNI::createBitmapRegionDecoder(env, brd.release()); +} + +static jobject nativeNewInstanceFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray, + jint offset, jint length, jboolean isShareable) { + /* If isShareable we could decide to just wrap the java array and + share it, but that means adding a globalref to the java array object + For now we just always copy the array's data if isShareable. + */ + AutoJavaByteArray ar(env, byteArray); + std::unique_ptr stream(new SkMemoryStream(ar.ptr() + offset, length, true)); + + // the decoder owns the stream. + jobject brd = createBitmapRegionDecoder(env, std::move(stream)); + return brd; +} + +static jobject nativeNewInstanceFromFileDescriptor(JNIEnv* env, jobject clazz, + jobject fileDescriptor, jboolean isShareable) { + NPE_CHECK_RETURN_ZERO(env, fileDescriptor); + + jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor); + + struct stat fdStat; + if (fstat(descriptor, &fdStat) == -1) { + doThrowIOE(env, "broken file descriptor"); + return nullObjectReturn("fstat return -1"); + } + + sk_sp data(SkData::MakeFromFD(descriptor)); + std::unique_ptr stream(new SkMemoryStream(std::move(data))); + + // the decoder owns the stream. + jobject brd = createBitmapRegionDecoder(env, std::move(stream)); + return brd; +} + +static jobject nativeNewInstanceFromStream(JNIEnv* env, jobject clazz, + jobject is, // InputStream + jbyteArray storage, // byte[] + jboolean isShareable) { + jobject brd = NULL; + // for now we don't allow shareable with java inputstreams + std::unique_ptr stream(CopyJavaInputStream(env, is, storage)); + + if (stream) { + // the decoder owns the stream. + brd = createBitmapRegionDecoder(env, std::move(stream)); + } + return brd; +} + +static jobject nativeNewInstanceFromAsset(JNIEnv* env, jobject clazz, + jlong native_asset, // Asset + jboolean isShareable) { + Asset* asset = reinterpret_cast(native_asset); + std::unique_ptr stream(CopyAssetToStream(asset)); + if (NULL == stream) { + return NULL; + } + + // the decoder owns the stream. + jobject brd = createBitmapRegionDecoder(env, std::move(stream)); + return brd; +} + +/* + * nine patch not supported + * purgeable not supported + * reportSizeToVM not supported + */ +static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint inputX, + jint inputY, jint inputWidth, jint inputHeight, jobject options, jlong inBitmapHandle, + jlong colorSpaceHandle) { + + // Set default options. + int sampleSize = 1; + SkColorType colorType = kN32_SkColorType; + bool requireUnpremul = false; + jobject javaBitmap = nullptr; + bool isHardware = false; + sk_sp colorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle); + // Update the default options with any options supplied by the client. + if (NULL != options) { + sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); + jobject jconfig = env->GetObjectField(options, gOptions_configFieldID); + colorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig); + isHardware = GraphicsJNI::isHardwareConfig(env, jconfig); + requireUnpremul = !env->GetBooleanField(options, gOptions_premultipliedFieldID); + javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID); + // The Java options of ditherMode and preferQualityOverSpeed are deprecated. We will + // ignore the values of these fields. + + // Initialize these fields to indicate a failure. If the decode succeeds, we + // will update them later on. + env->SetIntField(options, gOptions_widthFieldID, -1); + env->SetIntField(options, gOptions_heightFieldID, -1); + env->SetObjectField(options, gOptions_mimeFieldID, 0); + env->SetObjectField(options, gOptions_outConfigFieldID, 0); + env->SetObjectField(options, gOptions_outColorSpaceFieldID, 0); + } + + // Recycle a bitmap if possible. + android::Bitmap* recycledBitmap = nullptr; + size_t recycledBytes = 0; + if (javaBitmap) { + recycledBitmap = &bitmap::toBitmap(inBitmapHandle); + if (recycledBitmap->isImmutable()) { + ALOGW("Warning: Reusing an immutable bitmap as an image decoder target."); + } + recycledBytes = recycledBitmap->getAllocationByteCount(); + } + + SkBitmapRegionDecoder* brd = reinterpret_cast(brdHandle); + SkColorType decodeColorType = brd->computeOutputColorType(colorType); + if (decodeColorType == kRGBA_F16_SkColorType && isHardware && + !uirenderer::HardwareBitmapUploader::hasFP16Support()) { + decodeColorType = kN32_SkColorType; + } + + // Set up the pixel allocator + SkBRDAllocator* allocator = nullptr; + RecyclingClippingPixelAllocator recycleAlloc(recycledBitmap, recycledBytes); + HeapAllocator heapAlloc; + if (javaBitmap) { + allocator = &recycleAlloc; + // We are required to match the color type of the recycled bitmap. + decodeColorType = recycledBitmap->info().colorType(); + } else { + allocator = &heapAlloc; + } + + sk_sp decodeColorSpace = brd->computeOutputColorSpace( + decodeColorType, colorSpace); + + // Decode the region. + SkIRect subset = SkIRect::MakeXYWH(inputX, inputY, inputWidth, inputHeight); + SkBitmap bitmap; + if (!brd->decodeRegion(&bitmap, allocator, subset, sampleSize, + decodeColorType, requireUnpremul, decodeColorSpace)) { + return nullObjectReturn("Failed to decode region."); + } + + // If the client provided options, indicate that the decode was successful. + if (NULL != options) { + env->SetIntField(options, gOptions_widthFieldID, bitmap.width()); + env->SetIntField(options, gOptions_heightFieldID, bitmap.height()); + + env->SetObjectField(options, gOptions_mimeFieldID, + getMimeTypeAsJavaString(env, brd->getEncodedFormat())); + if (env->ExceptionCheck()) { + return nullObjectReturn("OOM in encodedFormatToString()"); + } + + jint configID = GraphicsJNI::colorTypeToLegacyBitmapConfig(decodeColorType); + if (isHardware) { + configID = GraphicsJNI::kHardware_LegacyBitmapConfig; + } + jobject config = env->CallStaticObjectMethod(gBitmapConfig_class, + gBitmapConfig_nativeToConfigMethodID, configID); + env->SetObjectField(options, gOptions_outConfigFieldID, config); + + env->SetObjectField(options, gOptions_outColorSpaceFieldID, + GraphicsJNI::getColorSpace(env, decodeColorSpace.get(), decodeColorType)); + } + + // If we may have reused a bitmap, we need to indicate that the pixels have changed. + if (javaBitmap) { + recycleAlloc.copyIfNecessary(); + bitmap::reinitBitmap(env, javaBitmap, recycledBitmap->info(), !requireUnpremul); + return javaBitmap; + } + + int bitmapCreateFlags = 0; + if (!requireUnpremul) { + bitmapCreateFlags |= android::bitmap::kBitmapCreateFlag_Premultiplied; + } + if (isHardware) { + sk_sp hardwareBitmap = Bitmap::allocateHardwareBitmap(bitmap); + return bitmap::createBitmap(env, hardwareBitmap.release(), bitmapCreateFlags); + } + return android::bitmap::createBitmap(env, heapAlloc.getStorageObjAndReset(), bitmapCreateFlags); +} + +static jint nativeGetHeight(JNIEnv* env, jobject, jlong brdHandle) { + SkBitmapRegionDecoder* brd = + reinterpret_cast(brdHandle); + return static_cast(brd->height()); +} + +static jint nativeGetWidth(JNIEnv* env, jobject, jlong brdHandle) { + SkBitmapRegionDecoder* brd = + reinterpret_cast(brdHandle); + return static_cast(brd->width()); +} + +static void nativeClean(JNIEnv* env, jobject, jlong brdHandle) { + SkBitmapRegionDecoder* brd = + reinterpret_cast(brdHandle); + delete brd; +} + +/////////////////////////////////////////////////////////////////////////////// + +static const JNINativeMethod gBitmapRegionDecoderMethods[] = { + { "nativeDecodeRegion", + "(JIIIILandroid/graphics/BitmapFactory$Options;JJ)Landroid/graphics/Bitmap;", + (void*)nativeDecodeRegion}, + + { "nativeGetHeight", "(J)I", (void*)nativeGetHeight}, + + { "nativeGetWidth", "(J)I", (void*)nativeGetWidth}, + + { "nativeClean", "(J)V", (void*)nativeClean}, + + { "nativeNewInstance", + "([BIIZ)Landroid/graphics/BitmapRegionDecoder;", + (void*)nativeNewInstanceFromByteArray + }, + + { "nativeNewInstance", + "(Ljava/io/InputStream;[BZ)Landroid/graphics/BitmapRegionDecoder;", + (void*)nativeNewInstanceFromStream + }, + + { "nativeNewInstance", + "(Ljava/io/FileDescriptor;Z)Landroid/graphics/BitmapRegionDecoder;", + (void*)nativeNewInstanceFromFileDescriptor + }, + + { "nativeNewInstance", + "(JZ)Landroid/graphics/BitmapRegionDecoder;", + (void*)nativeNewInstanceFromAsset + }, +}; + +int register_android_graphics_BitmapRegionDecoder(JNIEnv* env) +{ + return android::RegisterMethodsOrDie(env, "android/graphics/BitmapRegionDecoder", + gBitmapRegionDecoderMethods, NELEM(gBitmapRegionDecoderMethods)); +} diff --git a/libs/hwui/jni/ByteBufferStreamAdaptor.cpp b/libs/hwui/jni/ByteBufferStreamAdaptor.cpp new file mode 100644 index 000000000000..d443fd8cdf14 --- /dev/null +++ b/libs/hwui/jni/ByteBufferStreamAdaptor.cpp @@ -0,0 +1,335 @@ +#include "ByteBufferStreamAdaptor.h" +#include "core_jni_helpers.h" +#include "Utils.h" +#include + +#include + +using namespace android; + +static jmethodID gByteBuffer_getMethodID; +static jmethodID gByteBuffer_setPositionMethodID; + +/** + * Helper method for accessing the JNI interface pointer. + * + * Image decoding (which this supports) is started on a thread that is already + * attached to the Java VM. But an AnimatedImageDrawable continues decoding on + * the AnimatedImageThread, which is not attached. This will attach if + * necessary. + */ +static JNIEnv* requireEnv(JavaVM* jvm) { + JNIEnv* env; + if (jvm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { + if (jvm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) { + LOG_ALWAYS_FATAL("Failed to AttachCurrentThread!"); + } + } + return env; +} + +class ByteBufferStream : public SkStreamAsset { +private: + ByteBufferStream(JavaVM* jvm, jobject jbyteBuffer, size_t initialPosition, size_t length, + jbyteArray storage) + : mJvm(jvm) + , mByteBuffer(jbyteBuffer) + , mPosition(0) + , mInitialPosition(initialPosition) + , mLength(length) + , mStorage(storage) {} + +public: + static ByteBufferStream* Create(JavaVM* jvm, JNIEnv* env, jobject jbyteBuffer, + size_t position, size_t length) { + // This object outlives its native method call. + jbyteBuffer = env->NewGlobalRef(jbyteBuffer); + if (!jbyteBuffer) { + return nullptr; + } + + jbyteArray storage = env->NewByteArray(kStorageSize); + if (!storage) { + env->DeleteGlobalRef(jbyteBuffer); + return nullptr; + } + + // This object outlives its native method call. + storage = static_cast(env->NewGlobalRef(storage)); + if (!storage) { + env->DeleteGlobalRef(jbyteBuffer); + return nullptr; + } + + return new ByteBufferStream(jvm, jbyteBuffer, position, length, storage); + } + + ~ByteBufferStream() override { + auto* env = requireEnv(mJvm); + env->DeleteGlobalRef(mByteBuffer); + env->DeleteGlobalRef(mStorage); + } + + size_t read(void* buffer, size_t size) override { + if (size > mLength - mPosition) { + size = mLength - mPosition; + } + if (!size) { + return 0; + } + + if (!buffer) { + return this->setPosition(mPosition + size) ? size : 0; + } + + auto* env = requireEnv(mJvm); + size_t bytesRead = 0; + do { + const size_t requested = (size > kStorageSize) ? kStorageSize : size; + const jint jrequested = static_cast(requested); + env->CallObjectMethod(mByteBuffer, gByteBuffer_getMethodID, mStorage, 0, jrequested); + if (env->ExceptionCheck()) { + ALOGE("Error in ByteBufferStream::read - was the ByteBuffer modified externally?"); + env->ExceptionDescribe(); + env->ExceptionClear(); + mPosition = mLength; + return bytesRead; + } + + env->GetByteArrayRegion(mStorage, 0, requested, reinterpret_cast(buffer)); + if (env->ExceptionCheck()) { + ALOGE("Internal error in ByteBufferStream::read"); + env->ExceptionDescribe(); + env->ExceptionClear(); + mPosition = mLength; + return bytesRead; + } + + mPosition += requested; + buffer = reinterpret_cast(reinterpret_cast(buffer) + requested); + bytesRead += requested; + size -= requested; + } while (size); + return bytesRead; + } + + bool isAtEnd() const override { return mLength == mPosition; } + + // SkStreamRewindable overrides + bool rewind() override { return this->setPosition(0); } + + SkStreamAsset* onDuplicate() const override { + // SkStreamRewindable requires overriding this, but it is not called by + // decoders, so does not need a true implementation. A proper + // implementation would require duplicating the ByteBuffer, which has + // its own internal position state. + return nullptr; + } + + // SkStreamSeekable overrides + size_t getPosition() const override { return mPosition; } + + bool seek(size_t position) override { + return this->setPosition(position > mLength ? mLength : position); + } + + bool move(long offset) override { + long newPosition = mPosition + offset; + if (newPosition < 0) { + return this->setPosition(0); + } + return this->seek(static_cast(newPosition)); + } + + SkStreamAsset* onFork() const override { + // SkStreamSeekable requires overriding this, but it is not called by + // decoders, so does not need a true implementation. A proper + // implementation would require duplicating the ByteBuffer, which has + // its own internal position state. + return nullptr; + } + + // SkStreamAsset overrides + size_t getLength() const override { return mLength; } + +private: + JavaVM* mJvm; + jobject mByteBuffer; + // Logical position of the SkStream, between 0 and mLength. + size_t mPosition; + // Initial position of mByteBuffer, treated as mPosition 0. + const size_t mInitialPosition; + // Logical length of the SkStream, from mInitialPosition to + // mByteBuffer.limit(). + const size_t mLength; + + // Range has already been checked by the caller. + bool setPosition(size_t newPosition) { + auto* env = requireEnv(mJvm); + env->CallObjectMethod(mByteBuffer, gByteBuffer_setPositionMethodID, + newPosition + mInitialPosition); + if (env->ExceptionCheck()) { + ALOGE("Internal error in ByteBufferStream::setPosition"); + env->ExceptionDescribe(); + env->ExceptionClear(); + mPosition = mLength; + return false; + } + mPosition = newPosition; + return true; + } + + // FIXME: This is an arbitrary storage size, which should be plenty for + // some formats (png, gif, many bmps). But for jpeg, the more we can supply + // in one call the better, and webp really wants all of the data. How to + // best choose the amount of storage used? + static constexpr size_t kStorageSize = 4096; + jbyteArray mStorage; +}; + +class ByteArrayStream : public SkStreamAsset { +private: + ByteArrayStream(JavaVM* jvm, jbyteArray jarray, size_t offset, size_t length) + : mJvm(jvm), mByteArray(jarray), mOffset(offset), mPosition(0), mLength(length) {} + +public: + static ByteArrayStream* Create(JavaVM* jvm, JNIEnv* env, jbyteArray jarray, size_t offset, + size_t length) { + // This object outlives its native method call. + jarray = static_cast(env->NewGlobalRef(jarray)); + if (!jarray) { + return nullptr; + } + return new ByteArrayStream(jvm, jarray, offset, length); + } + + ~ByteArrayStream() override { + auto* env = requireEnv(mJvm); + env->DeleteGlobalRef(mByteArray); + } + + size_t read(void* buffer, size_t size) override { + if (size > mLength - mPosition) { + size = mLength - mPosition; + } + if (!size) { + return 0; + } + + auto* env = requireEnv(mJvm); + if (buffer) { + env->GetByteArrayRegion(mByteArray, mPosition + mOffset, size, + reinterpret_cast(buffer)); + if (env->ExceptionCheck()) { + ALOGE("Internal error in ByteArrayStream::read"); + env->ExceptionDescribe(); + env->ExceptionClear(); + mPosition = mLength; + return 0; + } + } + + mPosition += size; + return size; + } + + bool isAtEnd() const override { return mLength == mPosition; } + + // SkStreamRewindable overrides + bool rewind() override { + mPosition = 0; + return true; + } + SkStreamAsset* onDuplicate() const override { + // SkStreamRewindable requires overriding this, but it is not called by + // decoders, so does not need a true implementation. Note that a proper + // implementation is fairly straightforward + return nullptr; + } + + // SkStreamSeekable overrides + size_t getPosition() const override { return mPosition; } + + bool seek(size_t position) override { + mPosition = (position > mLength) ? mLength : position; + return true; + } + + bool move(long offset) override { + long newPosition = mPosition + offset; + if (newPosition < 0) { + return this->seek(0); + } + return this->seek(static_cast(newPosition)); + } + + SkStreamAsset* onFork() const override { + // SkStreamSeekable requires overriding this, but it is not called by + // decoders, so does not need a true implementation. Note that a proper + // implementation is fairly straightforward + return nullptr; + } + + // SkStreamAsset overrides + size_t getLength() const override { return mLength; } + +private: + JavaVM* mJvm; + jbyteArray mByteArray; + // Offset in mByteArray. Only used when communicating with Java. + const size_t mOffset; + // Logical position of the SkStream, between 0 and mLength. + size_t mPosition; + const size_t mLength; +}; + +struct release_proc_context { + JavaVM* jvm; + jobject jbyteBuffer; +}; + +std::unique_ptr CreateByteBufferStreamAdaptor(JNIEnv* env, jobject jbyteBuffer, + size_t position, size_t limit) { + JavaVM* jvm; + LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&jvm) != JNI_OK); + + const size_t length = limit - position; + void* addr = env->GetDirectBufferAddress(jbyteBuffer); + if (addr) { + addr = reinterpret_cast(reinterpret_cast(addr) + position); + jbyteBuffer = env->NewGlobalRef(jbyteBuffer); + if (!jbyteBuffer) { + return nullptr; + } + + auto* context = new release_proc_context{jvm, jbyteBuffer}; + auto releaseProc = [](const void*, void* context) { + auto* c = reinterpret_cast(context); + JNIEnv* env = get_env_or_die(c->jvm); + env->DeleteGlobalRef(c->jbyteBuffer); + delete c; + }; + auto data = SkData::MakeWithProc(addr, length, releaseProc, context); + // The new SkMemoryStream will read directly from addr. + return std::unique_ptr(new SkMemoryStream(std::move(data))); + } + + // Non-direct, or direct access is not supported. + return std::unique_ptr(ByteBufferStream::Create(jvm, env, jbyteBuffer, position, + length)); +} + +std::unique_ptr CreateByteArrayStreamAdaptor(JNIEnv* env, jbyteArray array, size_t offset, + size_t length) { + JavaVM* jvm; + LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&jvm) != JNI_OK); + + return std::unique_ptr(ByteArrayStream::Create(jvm, env, array, offset, length)); +} + +int register_android_graphics_ByteBufferStreamAdaptor(JNIEnv* env) { + jclass byteBuffer_class = FindClassOrDie(env, "java/nio/ByteBuffer"); + gByteBuffer_getMethodID = GetMethodIDOrDie(env, byteBuffer_class, "get", "([BII)Ljava/nio/ByteBuffer;"); + gByteBuffer_setPositionMethodID = GetMethodIDOrDie(env, byteBuffer_class, "position", "(I)Ljava/nio/Buffer;"); + return true; +} diff --git a/libs/hwui/jni/ByteBufferStreamAdaptor.h b/libs/hwui/jni/ByteBufferStreamAdaptor.h new file mode 100644 index 000000000000..367a48fad9b9 --- /dev/null +++ b/libs/hwui/jni/ByteBufferStreamAdaptor.h @@ -0,0 +1,37 @@ +#ifndef _ANDROID_GRAPHICS_BYTE_BUFFER_STREAM_ADAPTOR_H_ +#define _ANDROID_GRAPHICS_BYTE_BUFFER_STREAM_ADAPTOR_H_ + +#include +#include + +class SkStream; + +/** + * Create an adaptor for treating a java.nio.ByteBuffer as an SkStream. + * + * This will special case direct ByteBuffers, but not the case where a byte[] + * can be used directly. For that, use CreateByteArrayStreamAdaptor. + * + * @param jbyteBuffer corresponding to the java ByteBuffer. This method will + * add a global ref. + * @param initialPosition returned by ByteBuffer.position(). Decoding starts + * from here. + * @param limit returned by ByteBuffer.limit(). + * + * Returns null on failure. + */ +std::unique_ptr CreateByteBufferStreamAdaptor(JNIEnv*, jobject jbyteBuffer, + size_t initialPosition, size_t limit); + +/** + * Create an adaptor for treating a Java byte[] as an SkStream. + * + * @param offset into the byte[] of the beginning of the data to use. + * @param length of data to use, starting from offset. + * + * Returns null on failure. + */ +std::unique_ptr CreateByteArrayStreamAdaptor(JNIEnv*, jbyteArray array, size_t offset, + size_t length); + +#endif // _ANDROID_GRAPHICS_BYTE_BUFFER_STREAM_ADAPTOR_H_ diff --git a/libs/hwui/jni/Camera.cpp b/libs/hwui/jni/Camera.cpp new file mode 100644 index 000000000000..da954972ab57 --- /dev/null +++ b/libs/hwui/jni/Camera.cpp @@ -0,0 +1,146 @@ +#include "jni.h" +#include "core_jni_helpers.h" + +#include "SkCamera.h" + +#include "GraphicsJNI.h" +#include + +static jfieldID gNativeInstanceFieldID; + +static void Camera_constructor(JNIEnv* env, jobject obj) { + Sk3DView* view = new Sk3DView; + env->SetLongField(obj, gNativeInstanceFieldID, reinterpret_cast(view)); +} + +static void Camera_destructor(JNIEnv* env, jobject obj) { + jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID); + Sk3DView* view = reinterpret_cast(viewHandle); + delete view; +} + +static void Camera_save(JNIEnv* env, jobject obj) { + jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID); + Sk3DView* v = reinterpret_cast(viewHandle); + v->save(); +} + +static void Camera_restore(JNIEnv* env, jobject obj) { + jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID); + Sk3DView* v = reinterpret_cast(viewHandle); + v->restore(); +} + +static void Camera_translate(JNIEnv* env, jobject obj, + jfloat dx, jfloat dy, jfloat dz) { + jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID); + Sk3DView* v = reinterpret_cast(viewHandle); + v->translate(dx, dy, dz); +} + +static void Camera_rotateX(JNIEnv* env, jobject obj, jfloat degrees) { + jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID); + Sk3DView* v = reinterpret_cast(viewHandle); + v->rotateX(degrees); +} + +static void Camera_rotateY(JNIEnv* env, jobject obj, jfloat degrees) { + jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID); + Sk3DView* v = reinterpret_cast(viewHandle); + v->rotateY(degrees); +} + +static void Camera_rotateZ(JNIEnv* env, jobject obj, jfloat degrees) { + jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID); + Sk3DView* v = reinterpret_cast(viewHandle); + v->rotateZ(degrees); +} + +static void Camera_rotate(JNIEnv* env, jobject obj, jfloat x, jfloat y, jfloat z) { + jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID); + Sk3DView* v = reinterpret_cast(viewHandle); + v->rotateX(x); + v->rotateY(y); + v->rotateZ(z); +} + +static void Camera_setLocation(JNIEnv* env, jobject obj, jfloat x, jfloat y, jfloat z) { + jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID); + Sk3DView* v = reinterpret_cast(viewHandle); + v->setCameraLocation(x, y, z); +} + +static jfloat Camera_getLocationX(JNIEnv* env, jobject obj) { + jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID); + Sk3DView* v = reinterpret_cast(viewHandle); + return SkScalarToFloat(v->getCameraLocationX()); +} + +static jfloat Camera_getLocationY(JNIEnv* env, jobject obj) { + jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID); + Sk3DView* v = reinterpret_cast(viewHandle); + return SkScalarToFloat(v->getCameraLocationY()); +} + +static jfloat Camera_getLocationZ(JNIEnv* env, jobject obj) { + jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID); + Sk3DView* v = reinterpret_cast(viewHandle); + return SkScalarToFloat(v->getCameraLocationZ()); +} + +static void Camera_getMatrix(JNIEnv* env, jobject obj, jlong matrixHandle) { + SkMatrix* native_matrix = reinterpret_cast(matrixHandle); + jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID); + Sk3DView* v = reinterpret_cast(viewHandle); + v->getMatrix(native_matrix); +} + +static void Camera_applyToCanvas(JNIEnv* env, jobject obj, jlong canvasHandle) { + android::Canvas* canvas = reinterpret_cast(canvasHandle); + jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID); + Sk3DView* v = reinterpret_cast(viewHandle); + SkMatrix matrix; + v->getMatrix(&matrix); + canvas->concat(matrix); +} + +static jfloat Camera_dotWithNormal(JNIEnv* env, jobject obj, + jfloat x, jfloat y, jfloat z) { + jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID); + Sk3DView* v = reinterpret_cast(viewHandle); + SkScalar dot = v->dotWithNormal(x, y, z); + return SkScalarToFloat(dot); +} + +// ---------------------------------------------------------------------------- + +/* + * JNI registration. + */ +static const JNINativeMethod gCameraMethods[] = { + /* name, signature, funcPtr */ + + { "nativeConstructor", "()V", (void*)Camera_constructor }, + { "nativeDestructor", "()V", (void*)Camera_destructor }, + { "save", "()V", (void*)Camera_save }, + { "restore", "()V", (void*)Camera_restore }, + { "translate", "(FFF)V", (void*)Camera_translate }, + { "rotateX", "(F)V", (void*)Camera_rotateX }, + { "rotateY", "(F)V", (void*)Camera_rotateY }, + { "rotateZ", "(F)V", (void*)Camera_rotateZ }, + { "rotate", "(FFF)V", (void*)Camera_rotate }, + { "setLocation", "(FFF)V", (void*)Camera_setLocation }, + { "getLocationX", "()F", (void*)Camera_getLocationX }, + { "getLocationY", "()F", (void*)Camera_getLocationY }, + { "getLocationZ", "()F", (void*)Camera_getLocationZ }, + { "nativeGetMatrix", "(J)V", (void*)Camera_getMatrix }, + { "nativeApplyToCanvas", "(J)V", (void*)Camera_applyToCanvas }, + { "dotWithNormal", "(FFF)F", (void*)Camera_dotWithNormal } +}; + +int register_android_graphics_Camera(JNIEnv* env) { + jclass clazz = android::FindClassOrDie(env, "android/graphics/Camera"); + gNativeInstanceFieldID = android::GetFieldIDOrDie(env, clazz, "native_instance", "J"); + return android::RegisterMethodsOrDie(env, "android/graphics/Camera", gCameraMethods, + NELEM(gCameraMethods)); +} diff --git a/libs/hwui/jni/CanvasProperty.cpp b/libs/hwui/jni/CanvasProperty.cpp new file mode 100644 index 000000000000..c841d6a5125a --- /dev/null +++ b/libs/hwui/jni/CanvasProperty.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 20014 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 "jni.h" +#include "GraphicsJNI.h" +#include + +#include +#include +#include + +namespace android { + +using namespace uirenderer; + +static jlong createFloat(JNIEnv* env, jobject clazz, jfloat initialValue) { + return reinterpret_cast(new CanvasPropertyPrimitive(initialValue)); +} + +static jlong createPaint(JNIEnv* env, jobject clazz, jlong paintPtr) { + const Paint* paint = reinterpret_cast(paintPtr); + return reinterpret_cast(new CanvasPropertyPaint(*paint)); +} + +// ---------------------------------------------------------------------------- +// JNI Glue +// ---------------------------------------------------------------------------- + +static const JNINativeMethod gMethods[] = { + { "nCreateFloat", "(F)J", (void*) createFloat }, + { "nCreatePaint", "(J)J", (void*) createPaint }, +}; + +int register_android_graphics_CanvasProperty(JNIEnv* env) { + return RegisterMethodsOrDie(env, "android/graphics/CanvasProperty", gMethods, + NELEM(gMethods)); +} + +}; // namespace android diff --git a/libs/hwui/jni/ColorFilter.cpp b/libs/hwui/jni/ColorFilter.cpp new file mode 100644 index 000000000000..164d35f46a47 --- /dev/null +++ b/libs/hwui/jni/ColorFilter.cpp @@ -0,0 +1,91 @@ +/* libs/android_runtime/android/graphics/ColorFilter.cpp +** +** Copyright 2006, 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 "jni.h" +#include "GraphicsJNI.h" +#include "core_jni_helpers.h" + +#include "SkColorFilter.h" +#include "SkColorMatrixFilter.h" + +namespace android { + +using namespace uirenderer; + +class SkColorFilterGlue { +public: + static void SafeUnref(SkColorFilter* filter) { + SkSafeUnref(filter); + } + + static jlong GetNativeFinalizer(JNIEnv*, jobject) { + return static_cast(reinterpret_cast(&SafeUnref)); + } + + static jlong CreateBlendModeFilter(JNIEnv* env, jobject, jint srcColor, jint modeHandle) { + SkBlendMode mode = static_cast(modeHandle); + return reinterpret_cast(SkColorFilters::Blend(srcColor, mode).release()); + } + + static jlong CreateLightingFilter(JNIEnv* env, jobject, jint mul, jint add) { + return reinterpret_cast(SkColorMatrixFilter::MakeLightingFilter(mul, add).release()); + } + + static jlong CreateColorMatrixFilter(JNIEnv* env, jobject, jfloatArray jarray) { + float matrix[20]; + env->GetFloatArrayRegion(jarray, 0, 20, matrix); + // java biases the translates by 255, so undo that before calling skia + matrix[ 4] *= (1.0f/255); + matrix[ 9] *= (1.0f/255); + matrix[14] *= (1.0f/255); + matrix[19] *= (1.0f/255); + return reinterpret_cast(SkColorFilters::Matrix(matrix).release()); + } +}; + +static const JNINativeMethod colorfilter_methods[] = { + {"nativeGetFinalizer", "()J", (void*) SkColorFilterGlue::GetNativeFinalizer } +}; + +static const JNINativeMethod blendmode_methods[] = { + { "native_CreateBlendModeFilter", "(II)J", (void*) SkColorFilterGlue::CreateBlendModeFilter }, +}; + +static const JNINativeMethod lighting_methods[] = { + { "native_CreateLightingFilter", "(II)J", (void*) SkColorFilterGlue::CreateLightingFilter }, +}; + +static const JNINativeMethod colormatrix_methods[] = { + { "nativeColorMatrixFilter", "([F)J", (void*) SkColorFilterGlue::CreateColorMatrixFilter }, +}; + +int register_android_graphics_ColorFilter(JNIEnv* env) { + android::RegisterMethodsOrDie(env, "android/graphics/ColorFilter", colorfilter_methods, + NELEM(colorfilter_methods)); + android::RegisterMethodsOrDie(env, "android/graphics/PorterDuffColorFilter", blendmode_methods, + NELEM(blendmode_methods)); + android::RegisterMethodsOrDie(env, "android/graphics/BlendModeColorFilter", blendmode_methods, + NELEM(blendmode_methods)); + android::RegisterMethodsOrDie(env, "android/graphics/LightingColorFilter", lighting_methods, + NELEM(lighting_methods)); + android::RegisterMethodsOrDie(env, "android/graphics/ColorMatrixColorFilter", + colormatrix_methods, NELEM(colormatrix_methods)); + + return 0; +} + +} diff --git a/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp new file mode 100644 index 000000000000..39483b55992b --- /dev/null +++ b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp @@ -0,0 +1,306 @@ +#include "CreateJavaOutputStreamAdaptor.h" +#include "SkData.h" +#include "SkMalloc.h" +#include "SkRefCnt.h" +#include "SkStream.h" +#include "SkTypes.h" +#include "Utils.h" + +#include +#include +#include + +static jmethodID gInputStream_readMethodID; +static jmethodID gInputStream_skipMethodID; + +/** + * Wrapper for a Java InputStream. + */ +class JavaInputStreamAdaptor : public SkStream { + JavaInputStreamAdaptor(JavaVM* jvm, jobject js, jbyteArray ar, jint capacity, + bool swallowExceptions) + : fJvm(jvm) + , fJavaInputStream(js) + , fJavaByteArray(ar) + , fCapacity(capacity) + , fBytesRead(0) + , fIsAtEnd(false) + , fSwallowExceptions(swallowExceptions) {} + +public: + static JavaInputStreamAdaptor* Create(JNIEnv* env, jobject js, jbyteArray ar, + bool swallowExceptions) { + JavaVM* jvm; + LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&jvm) != JNI_OK); + + js = env->NewGlobalRef(js); + if (!js) { + return nullptr; + } + + ar = (jbyteArray) env->NewGlobalRef(ar); + if (!ar) { + env->DeleteGlobalRef(js); + return nullptr; + } + + jint capacity = env->GetArrayLength(ar); + return new JavaInputStreamAdaptor(jvm, js, ar, capacity, swallowExceptions); + } + + ~JavaInputStreamAdaptor() override { + auto* env = android::get_env_or_die(fJvm); + env->DeleteGlobalRef(fJavaInputStream); + env->DeleteGlobalRef(fJavaByteArray); + } + + size_t read(void* buffer, size_t size) override { + auto* env = android::get_env_or_die(fJvm); + if (!fSwallowExceptions && checkException(env)) { + // Just in case the caller did not clear from a previous exception. + return 0; + } + if (NULL == buffer) { + if (0 == size) { + return 0; + } else { + /* InputStream.skip(n) can return <=0 but still not be at EOF + If we see that value, we need to call read(), which will + block if waiting for more data, or return -1 at EOF + */ + size_t amountSkipped = 0; + do { + size_t amount = this->doSkip(size - amountSkipped, env); + if (0 == amount) { + char tmp; + amount = this->doRead(&tmp, 1, env); + if (0 == amount) { + // if read returned 0, we're at EOF + fIsAtEnd = true; + break; + } + } + amountSkipped += amount; + } while (amountSkipped < size); + return amountSkipped; + } + } + return this->doRead(buffer, size, env); + } + + bool isAtEnd() const override { return fIsAtEnd; } + +private: + size_t doRead(void* buffer, size_t size, JNIEnv* env) { + size_t bytesRead = 0; + // read the bytes + do { + jint requested = 0; + if (size > static_cast(fCapacity)) { + requested = fCapacity; + } else { + // This is safe because requested is clamped to (jint) + // fCapacity. + requested = static_cast(size); + } + + jint n = env->CallIntMethod(fJavaInputStream, + gInputStream_readMethodID, fJavaByteArray, 0, requested); + if (checkException(env)) { + SkDebugf("---- read threw an exception\n"); + return bytesRead; + } + + if (n < 0) { // n == 0 should not be possible, see InputStream read() specifications. + fIsAtEnd = true; + break; // eof + } + + env->GetByteArrayRegion(fJavaByteArray, 0, n, + reinterpret_cast(buffer)); + if (checkException(env)) { + SkDebugf("---- read:GetByteArrayRegion threw an exception\n"); + return bytesRead; + } + + buffer = (void*)((char*)buffer + n); + bytesRead += n; + size -= n; + fBytesRead += n; + } while (size != 0); + + return bytesRead; + } + + size_t doSkip(size_t size, JNIEnv* env) { + jlong skipped = env->CallLongMethod(fJavaInputStream, + gInputStream_skipMethodID, (jlong)size); + if (checkException(env)) { + SkDebugf("------- skip threw an exception\n"); + return 0; + } + if (skipped < 0) { + skipped = 0; + } + + return (size_t)skipped; + } + + bool checkException(JNIEnv* env) { + if (!env->ExceptionCheck()) { + return false; + } + + env->ExceptionDescribe(); + if (fSwallowExceptions) { + env->ExceptionClear(); + } + + // There is no way to recover from the error, so consider the stream + // to be at the end. + fIsAtEnd = true; + + return true; + } + + JavaVM* fJvm; + jobject fJavaInputStream; + jbyteArray fJavaByteArray; + const jint fCapacity; + size_t fBytesRead; + bool fIsAtEnd; + const bool fSwallowExceptions; +}; + +SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, jbyteArray storage, + bool swallowExceptions) { + return JavaInputStreamAdaptor::Create(env, stream, storage, swallowExceptions); +} + +static SkMemoryStream* adaptor_to_mem_stream(SkStream* stream) { + SkASSERT(stream != NULL); + size_t bufferSize = 4096; + size_t streamLen = 0; + size_t len; + char* data = (char*)sk_malloc_throw(bufferSize); + + while ((len = stream->read(data + streamLen, + bufferSize - streamLen)) != 0) { + streamLen += len; + if (streamLen == bufferSize) { + bufferSize *= 2; + data = (char*)sk_realloc_throw(data, bufferSize); + } + } + data = (char*)sk_realloc_throw(data, streamLen); + + SkMemoryStream* streamMem = new SkMemoryStream(); + streamMem->setMemoryOwned(data, streamLen); + return streamMem; +} + +SkStreamRewindable* CopyJavaInputStream(JNIEnv* env, jobject stream, + jbyteArray storage) { + std::unique_ptr adaptor(CreateJavaInputStreamAdaptor(env, stream, storage)); + if (NULL == adaptor.get()) { + return NULL; + } + return adaptor_to_mem_stream(adaptor.get()); +} + +/////////////////////////////////////////////////////////////////////////////// + +static jmethodID gOutputStream_writeMethodID; +static jmethodID gOutputStream_flushMethodID; + +class SkJavaOutputStream : public SkWStream { +public: + SkJavaOutputStream(JNIEnv* env, jobject stream, jbyteArray storage) + : fEnv(env), fJavaOutputStream(stream), fJavaByteArray(storage), fBytesWritten(0) { + fCapacity = env->GetArrayLength(storage); + } + + virtual size_t bytesWritten() const { + return fBytesWritten; + } + + virtual bool write(const void* buffer, size_t size) { + JNIEnv* env = fEnv; + jbyteArray storage = fJavaByteArray; + + while (size > 0) { + jint requested = 0; + if (size > static_cast(fCapacity)) { + requested = fCapacity; + } else { + // This is safe because requested is clamped to (jint) + // fCapacity. + requested = static_cast(size); + } + + env->SetByteArrayRegion(storage, 0, requested, + reinterpret_cast(buffer)); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + SkDebugf("--- write:SetByteArrayElements threw an exception\n"); + return false; + } + + fEnv->CallVoidMethod(fJavaOutputStream, gOutputStream_writeMethodID, + storage, 0, requested); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + SkDebugf("------- write threw an exception\n"); + return false; + } + + buffer = (void*)((char*)buffer + requested); + size -= requested; + fBytesWritten += requested; + } + return true; + } + + virtual void flush() { + fEnv->CallVoidMethod(fJavaOutputStream, gOutputStream_flushMethodID); + } + +private: + JNIEnv* fEnv; + jobject fJavaOutputStream; // the caller owns this object + jbyteArray fJavaByteArray; // the caller owns this object + jint fCapacity; + size_t fBytesWritten; +}; + +SkWStream* CreateJavaOutputStreamAdaptor(JNIEnv* env, jobject stream, + jbyteArray storage) { + return new SkJavaOutputStream(env, stream, storage); +} + +static jclass findClassCheck(JNIEnv* env, const char classname[]) { + jclass clazz = env->FindClass(classname); + SkASSERT(!env->ExceptionCheck()); + return clazz; +} + +static jmethodID getMethodIDCheck(JNIEnv* env, jclass clazz, + const char methodname[], const char type[]) { + jmethodID id = env->GetMethodID(clazz, methodname, type); + SkASSERT(!env->ExceptionCheck()); + return id; +} + +int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env) { + jclass inputStream_Clazz = findClassCheck(env, "java/io/InputStream"); + gInputStream_readMethodID = getMethodIDCheck(env, inputStream_Clazz, "read", "([BII)I"); + gInputStream_skipMethodID = getMethodIDCheck(env, inputStream_Clazz, "skip", "(J)J"); + + jclass outputStream_Clazz = findClassCheck(env, "java/io/OutputStream"); + gOutputStream_writeMethodID = getMethodIDCheck(env, outputStream_Clazz, "write", "([BII)V"); + gOutputStream_flushMethodID = getMethodIDCheck(env, outputStream_Clazz, "flush", "()V"); + + return 0; +} diff --git a/libs/hwui/jni/CreateJavaOutputStreamAdaptor.h b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.h new file mode 100644 index 000000000000..fccd4717c4b7 --- /dev/null +++ b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.h @@ -0,0 +1,43 @@ +#ifndef _ANDROID_GRAPHICS_CREATE_JAVA_OUTPUT_STREAM_ADAPTOR_H_ +#define _ANDROID_GRAPHICS_CREATE_JAVA_OUTPUT_STREAM_ADAPTOR_H_ + +//#include +#include "jni.h" + +class SkMemoryStream; +class SkStream; +class SkStreamRewindable; +class SkWStream; + +/** + * Return an adaptor from a Java InputStream to an SkStream. + * Does not support rewind. + * @param env JNIEnv object. + * @param stream Pointer to Java InputStream. + * @param storage Java byte array for retrieving data from the + * Java InputStream. + * @param swallowExceptions Whether to call ExceptionClear() after + * an Exception is thrown. If false, it is up to the client to + * clear or propagate the exception. + * @return SkStream Simple subclass of SkStream which supports its + * basic methods like reading. Only valid until the calling + * function returns, since the Java InputStream is not managed + * by the SkStream. + */ +SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, jbyteArray storage, + bool swallowExceptions = true); + +/** + * Copy a Java InputStream. The result will be rewindable. + * @param env JNIEnv object. + * @param stream Pointer to Java InputStream. + * @param storage Java byte array for retrieving data from the + * Java InputStream. + * @return SkStreamRewindable The data in stream will be copied + * to a new SkStreamRewindable. + */ +SkStreamRewindable* CopyJavaInputStream(JNIEnv* env, jobject stream, jbyteArray storage); + +SkWStream* CreateJavaOutputStreamAdaptor(JNIEnv* env, jobject stream, jbyteArray storage); + +#endif // _ANDROID_GRAPHICS_CREATE_JAVA_OUTPUT_STREAM_ADAPTOR_H_ diff --git a/libs/hwui/jni/FontFamily.cpp b/libs/hwui/jni/FontFamily.cpp new file mode 100644 index 000000000000..0fd9cc7e9989 --- /dev/null +++ b/libs/hwui/jni/FontFamily.cpp @@ -0,0 +1,240 @@ +/* + * 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. + */ + +#define LOG_TAG "Minikin" + +#include +#include + +#include "SkData.h" +#include "SkFontMgr.h" +#include "SkRefCnt.h" +#include "SkTypeface.h" +#include "GraphicsJNI.h" +#include +#include +#include +#include "Utils.h" +#include "FontUtils.h" + +#include +#include +#include +#include +#include + +#include + +namespace android { + +struct NativeFamilyBuilder { + NativeFamilyBuilder(uint32_t langId, int variant) + : langId(langId), variant(static_cast(variant)) {} + uint32_t langId; + minikin::FamilyVariant variant; + std::vector fonts; + std::vector axes; +}; + +static inline NativeFamilyBuilder* toNativeBuilder(jlong ptr) { + return reinterpret_cast(ptr); +} + +static inline FontFamilyWrapper* toFamily(jlong ptr) { + return reinterpret_cast(ptr); +} + +template static inline jlong toJLong(Ptr ptr) { + return reinterpret_cast(ptr); +} + +static jlong FontFamily_initBuilder(JNIEnv* env, jobject clazz, jstring langs, jint variant) { + NativeFamilyBuilder* builder; + if (langs != nullptr) { + ScopedUtfChars str(env, langs); + builder = new NativeFamilyBuilder(minikin::registerLocaleList(str.c_str()), variant); + } else { + builder = new NativeFamilyBuilder(minikin::registerLocaleList(""), variant); + } + return toJLong(builder); +} + +static jlong FontFamily_create(CRITICAL_JNI_PARAMS_COMMA jlong builderPtr) { + if (builderPtr == 0) { + return 0; + } + NativeFamilyBuilder* builder = toNativeBuilder(builderPtr); + if (builder->fonts.empty()) { + return 0; + } + std::shared_ptr family = std::make_shared( + builder->langId, builder->variant, std::move(builder->fonts), + true /* isCustomFallback */); + if (family->getCoverage().length() == 0) { + return 0; + } + return toJLong(new FontFamilyWrapper(std::move(family))); +} + +static void releaseBuilder(jlong builderPtr) { + delete toNativeBuilder(builderPtr); +} + +static jlong FontFamily_getBuilderReleaseFunc(CRITICAL_JNI_PARAMS) { + return toJLong(&releaseBuilder); +} + +static void releaseFamily(jlong familyPtr) { + delete toFamily(familyPtr); +} + +static jlong FontFamily_getFamilyReleaseFunc(CRITICAL_JNI_PARAMS) { + return toJLong(&releaseFamily); +} + +static bool addSkTypeface(NativeFamilyBuilder* builder, sk_sp&& data, int ttcIndex, + jint weight, jint italic) { + FatVector skiaAxes; + for (const auto& axis : builder->axes) { + skiaAxes.emplace_back(SkFontArguments::Axis{axis.axisTag, axis.value}); + } + + const size_t fontSize = data->size(); + const void* fontPtr = data->data(); + std::unique_ptr fontData(new SkMemoryStream(std::move(data))); + + SkFontArguments params; + params.setCollectionIndex(ttcIndex); + params.setAxes(skiaAxes.data(), skiaAxes.size()); + + sk_sp fm(SkFontMgr::RefDefault()); + sk_sp face(fm->makeFromStream(std::move(fontData), params)); + if (face == NULL) { + ALOGE("addFont failed to create font, invalid request"); + builder->axes.clear(); + return false; + } + std::shared_ptr minikinFont = + std::make_shared(std::move(face), fontPtr, fontSize, "", ttcIndex, + builder->axes); + minikin::Font::Builder fontBuilder(minikinFont); + + if (weight != RESOLVE_BY_FONT_TABLE) { + fontBuilder.setWeight(weight); + } + if (italic != RESOLVE_BY_FONT_TABLE) { + fontBuilder.setSlant(static_cast(italic != 0)); + } + builder->fonts.push_back(fontBuilder.build()); + builder->axes.clear(); + return true; +} + +static void release_global_ref(const void* /*data*/, void* context) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + bool needToAttach = (env == NULL); + if (needToAttach) { + JavaVMAttachArgs args; + args.version = JNI_VERSION_1_4; + args.name = "release_font_data"; + args.group = NULL; + jint result = AndroidRuntime::getJavaVM()->AttachCurrentThread(&env, &args); + if (result != JNI_OK) { + ALOGE("failed to attach to thread to release global ref."); + return; + } + } + + jobject obj = reinterpret_cast(context); + env->DeleteGlobalRef(obj); + + if (needToAttach) { + AndroidRuntime::getJavaVM()->DetachCurrentThread(); + } +} + +static jboolean FontFamily_addFont(JNIEnv* env, jobject clazz, jlong builderPtr, jobject bytebuf, + jint ttcIndex, jint weight, jint isItalic) { + NPE_CHECK_RETURN_ZERO(env, bytebuf); + NativeFamilyBuilder* builder = reinterpret_cast(builderPtr); + const void* fontPtr = env->GetDirectBufferAddress(bytebuf); + if (fontPtr == NULL) { + ALOGE("addFont failed to create font, buffer invalid"); + builder->axes.clear(); + return false; + } + jlong fontSize = env->GetDirectBufferCapacity(bytebuf); + if (fontSize < 0) { + ALOGE("addFont failed to create font, buffer size invalid"); + builder->axes.clear(); + return false; + } + jobject fontRef = MakeGlobalRefOrDie(env, bytebuf); + sk_sp data(SkData::MakeWithProc(fontPtr, fontSize, + release_global_ref, reinterpret_cast(fontRef))); + return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic); +} + +static jboolean FontFamily_addFontWeightStyle(JNIEnv* env, jobject clazz, jlong builderPtr, + jobject font, jint ttcIndex, jint weight, jint isItalic) { + NPE_CHECK_RETURN_ZERO(env, font); + NativeFamilyBuilder* builder = toNativeBuilder(builderPtr); + const void* fontPtr = env->GetDirectBufferAddress(font); + if (fontPtr == NULL) { + ALOGE("addFont failed to create font, buffer invalid"); + builder->axes.clear(); + return false; + } + jlong fontSize = env->GetDirectBufferCapacity(font); + if (fontSize < 0) { + ALOGE("addFont failed to create font, buffer size invalid"); + builder->axes.clear(); + return false; + } + jobject fontRef = MakeGlobalRefOrDie(env, font); + sk_sp data(SkData::MakeWithProc(fontPtr, fontSize, + release_global_ref, reinterpret_cast(fontRef))); + return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic); +} + +static void FontFamily_addAxisValue(CRITICAL_JNI_PARAMS_COMMA jlong builderPtr, jint tag, jfloat value) { + NativeFamilyBuilder* builder = toNativeBuilder(builderPtr); + builder->axes.push_back({static_cast(tag), value}); +} + +/////////////////////////////////////////////////////////////////////////////// + +static const JNINativeMethod gFontFamilyMethods[] = { + { "nInitBuilder", "(Ljava/lang/String;I)J", (void*)FontFamily_initBuilder }, + { "nCreateFamily", "(J)J", (void*)FontFamily_create }, + { "nGetBuilderReleaseFunc", "()J", (void*)FontFamily_getBuilderReleaseFunc }, + { "nGetFamilyReleaseFunc", "()J", (void*)FontFamily_getFamilyReleaseFunc }, + { "nAddFont", "(JLjava/nio/ByteBuffer;III)Z", (void*)FontFamily_addFont }, + { "nAddFontWeightStyle", "(JLjava/nio/ByteBuffer;III)Z", + (void*)FontFamily_addFontWeightStyle }, + { "nAddAxisValue", "(JIF)V", (void*)FontFamily_addAxisValue }, +}; + +int register_android_graphics_FontFamily(JNIEnv* env) +{ + int err = RegisterMethodsOrDie(env, "android/graphics/FontFamily", gFontFamilyMethods, + NELEM(gFontFamilyMethods)); + + init_FontUtils(env); + return err; +} + +} diff --git a/libs/hwui/jni/FontUtils.cpp b/libs/hwui/jni/FontUtils.cpp new file mode 100644 index 000000000000..0cf61b9ade89 --- /dev/null +++ b/libs/hwui/jni/FontUtils.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2016 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 "FontUtils.h" + +#include +#include + +namespace android { +namespace { + +static struct { + jmethodID mGet; + jmethodID mSize; +} gListClassInfo; + +static struct { + jfieldID mTag; + jfieldID mStyleValue; +} gAxisClassInfo; + +} // namespace + +jint ListHelper::size() const { + return mEnv->CallIntMethod(mList, gListClassInfo.mSize); +} + +jobject ListHelper::get(jint index) const { + return mEnv->CallObjectMethod(mList, gListClassInfo.mGet, index); +} + +jint AxisHelper::getTag() const { + return mEnv->GetIntField(mAxis, gAxisClassInfo.mTag); +} + +jfloat AxisHelper::getStyleValue() const { + return mEnv->GetFloatField(mAxis, gAxisClassInfo.mStyleValue); +} + +void init_FontUtils(JNIEnv* env) { + jclass listClass = FindClassOrDie(env, "java/util/List"); + gListClassInfo.mGet = GetMethodIDOrDie(env, listClass, "get", "(I)Ljava/lang/Object;"); + gListClassInfo.mSize = GetMethodIDOrDie(env, listClass, "size", "()I"); + + jclass axisClass = FindClassOrDie(env, "android/graphics/fonts/FontVariationAxis"); + gAxisClassInfo.mTag = GetFieldIDOrDie(env, axisClass, "mTag", "I"); + gAxisClassInfo.mStyleValue = GetFieldIDOrDie(env, axisClass, "mStyleValue", "F"); +} + +} // namespace android diff --git a/libs/hwui/jni/FontUtils.h b/libs/hwui/jni/FontUtils.h new file mode 100644 index 000000000000..b36b4e60e33a --- /dev/null +++ b/libs/hwui/jni/FontUtils.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2017 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_FONT_UTILS_H_ +#define _ANDROID_GRAPHICS_FONT_UTILS_H_ + +#include +#include + +#include + +namespace minikin { +class FontFamily; +} // namespace minikin + +namespace android { + +struct FontFamilyWrapper { + explicit FontFamilyWrapper(std::shared_ptr&& family) : family(family) {} + std::shared_ptr family; +}; + +struct FontWrapper { + FontWrapper(minikin::Font&& font) : font(std::move(font)) {} + minikin::Font font; +}; + +// Utility wrapper for java.util.List +class ListHelper { +public: + ListHelper(JNIEnv* env, jobject list) : mEnv(env), mList(list) {} + + jint size() const; + jobject get(jint index) const; + +private: + JNIEnv* mEnv; + jobject mList; +}; + +// Utility wrapper for android.graphics.FontConfig$Axis +class AxisHelper { +public: + AxisHelper(JNIEnv* env, jobject axis) : mEnv(env), mAxis(axis) {} + + jint getTag() const; + jfloat getStyleValue() const; + +private: + JNIEnv* mEnv; + jobject mAxis; +}; + +void init_FontUtils(JNIEnv* env); + +}; // namespace android + +#endif // _ANDROID_GRAPHICS_FONT_UTILS_H_ diff --git a/libs/hwui/jni/GIFMovie.cpp b/libs/hwui/jni/GIFMovie.cpp new file mode 100644 index 000000000000..f84a4bd09073 --- /dev/null +++ b/libs/hwui/jni/GIFMovie.cpp @@ -0,0 +1,447 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + + +#include "Movie.h" +#include "SkColor.h" +#include "SkColorPriv.h" +#include "SkStream.h" +#include "SkTemplates.h" +#include "SkUtils.h" + +#include "gif_lib.h" + +#if GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0) +#define DGifCloseFile(a, b) DGifCloseFile(a) +#endif + +class GIFMovie : public Movie { +public: + explicit GIFMovie(SkStream* stream); + virtual ~GIFMovie(); + +protected: + virtual bool onGetInfo(Info*); + virtual bool onSetTime(SkMSec); + virtual bool onGetBitmap(SkBitmap*); + +private: + GifFileType* fGIF; + int fCurrIndex; + int fLastDrawIndex; + SkBitmap fBackup; + SkColor fPaintingColor; +}; + +static int Decode(GifFileType* fileType, GifByteType* out, int size) { + SkStream* stream = (SkStream*) fileType->UserData; + return (int) stream->read(out, size); +} + +GIFMovie::GIFMovie(SkStream* stream) +{ +#if GIFLIB_MAJOR < 5 + fGIF = DGifOpen( stream, Decode ); +#else + fGIF = DGifOpen( stream, Decode, nullptr ); +#endif + if (nullptr == fGIF) + return; + + if (DGifSlurp(fGIF) != GIF_OK) + { + DGifCloseFile(fGIF, nullptr); + fGIF = nullptr; + } + fCurrIndex = -1; + fLastDrawIndex = -1; + fPaintingColor = SkPackARGB32(0, 0, 0, 0); +} + +GIFMovie::~GIFMovie() +{ + if (fGIF) + DGifCloseFile(fGIF, nullptr); +} + +static SkMSec savedimage_duration(const SavedImage* image) +{ + for (int j = 0; j < image->ExtensionBlockCount; j++) + { + if (image->ExtensionBlocks[j].Function == GRAPHICS_EXT_FUNC_CODE) + { + SkASSERT(image->ExtensionBlocks[j].ByteCount >= 4); + const uint8_t* b = (const uint8_t*)image->ExtensionBlocks[j].Bytes; + return ((b[2] << 8) | b[1]) * 10; + } + } + return 0; +} + +bool GIFMovie::onGetInfo(Info* info) +{ + if (nullptr == fGIF) + return false; + + SkMSec dur = 0; + for (int i = 0; i < fGIF->ImageCount; i++) + dur += savedimage_duration(&fGIF->SavedImages[i]); + + info->fDuration = dur; + info->fWidth = fGIF->SWidth; + info->fHeight = fGIF->SHeight; + info->fIsOpaque = false; // how to compute? + return true; +} + +bool GIFMovie::onSetTime(SkMSec time) +{ + if (nullptr == fGIF) + return false; + + SkMSec dur = 0; + for (int i = 0; i < fGIF->ImageCount; i++) + { + dur += savedimage_duration(&fGIF->SavedImages[i]); + if (dur >= time) + { + fCurrIndex = i; + return fLastDrawIndex != fCurrIndex; + } + } + fCurrIndex = fGIF->ImageCount - 1; + return true; +} + +static void copyLine(uint32_t* dst, const unsigned char* src, const ColorMapObject* cmap, + int transparent, int width) +{ + for (; width > 0; width--, src++, dst++) { + if (*src != transparent && *src < cmap->ColorCount) { + const GifColorType& col = cmap->Colors[*src]; + *dst = SkPackARGB32(0xFF, col.Red, col.Green, col.Blue); + } + } +} + +#if GIFLIB_MAJOR < 5 +static void copyInterlaceGroup(SkBitmap* bm, const unsigned char*& src, + const ColorMapObject* cmap, int transparent, int copyWidth, + int copyHeight, const GifImageDesc& imageDesc, int rowStep, + int startRow) +{ + int row; + // every 'rowStep'th row, starting with row 'startRow' + for (row = startRow; row < copyHeight; row += rowStep) { + uint32_t* dst = bm->getAddr32(imageDesc.Left, imageDesc.Top + row); + copyLine(dst, src, cmap, transparent, copyWidth); + src += imageDesc.Width; + } + + // pad for rest height + src += imageDesc.Width * ((imageDesc.Height - row + rowStep - 1) / rowStep); +} + +static void blitInterlace(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap, + int transparent) +{ + int width = bm->width(); + int height = bm->height(); + GifWord copyWidth = frame->ImageDesc.Width; + if (frame->ImageDesc.Left + copyWidth > width) { + copyWidth = width - frame->ImageDesc.Left; + } + + GifWord copyHeight = frame->ImageDesc.Height; + if (frame->ImageDesc.Top + copyHeight > height) { + copyHeight = height - frame->ImageDesc.Top; + } + + // deinterlace + const unsigned char* src = (unsigned char*)frame->RasterBits; + + // group 1 - every 8th row, starting with row 0 + copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 8, 0); + + // group 2 - every 8th row, starting with row 4 + copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 8, 4); + + // group 3 - every 4th row, starting with row 2 + copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 4, 2); + + copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 2, 1); +} +#endif + +static void blitNormal(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap, + int transparent) +{ + int width = bm->width(); + int height = bm->height(); + const unsigned char* src = (unsigned char*)frame->RasterBits; + uint32_t* dst = bm->getAddr32(frame->ImageDesc.Left, frame->ImageDesc.Top); + GifWord copyWidth = frame->ImageDesc.Width; + if (frame->ImageDesc.Left + copyWidth > width) { + copyWidth = width - frame->ImageDesc.Left; + } + + GifWord copyHeight = frame->ImageDesc.Height; + if (frame->ImageDesc.Top + copyHeight > height) { + copyHeight = height - frame->ImageDesc.Top; + } + + for (; copyHeight > 0; copyHeight--) { + copyLine(dst, src, cmap, transparent, copyWidth); + src += frame->ImageDesc.Width; + dst += width; + } +} + +static void fillRect(SkBitmap* bm, GifWord left, GifWord top, GifWord width, GifWord height, + uint32_t col) +{ + int bmWidth = bm->width(); + int bmHeight = bm->height(); + uint32_t* dst = bm->getAddr32(left, top); + GifWord copyWidth = width; + if (left + copyWidth > bmWidth) { + copyWidth = bmWidth - left; + } + + GifWord copyHeight = height; + if (top + copyHeight > bmHeight) { + copyHeight = bmHeight - top; + } + + for (; copyHeight > 0; copyHeight--) { + sk_memset32(dst, col, copyWidth); + dst += bmWidth; + } +} + +static void drawFrame(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap) +{ + int transparent = -1; + + for (int i = 0; i < frame->ExtensionBlockCount; ++i) { + ExtensionBlock* eb = frame->ExtensionBlocks + i; + if (eb->Function == GRAPHICS_EXT_FUNC_CODE && + eb->ByteCount == 4) { + bool has_transparency = ((eb->Bytes[0] & 1) == 1); + if (has_transparency) { + transparent = (unsigned char)eb->Bytes[3]; + } + } + } + + if (frame->ImageDesc.ColorMap != nullptr) { + // use local color table + cmap = frame->ImageDesc.ColorMap; + } + + if (cmap == nullptr || cmap->ColorCount != (1 << cmap->BitsPerPixel)) { + SkDEBUGFAIL("bad colortable setup"); + return; + } + +#if GIFLIB_MAJOR < 5 + // before GIFLIB 5, de-interlacing wasn't done by library at load time + if (frame->ImageDesc.Interlace) { + blitInterlace(bm, frame, cmap, transparent); + return; + } +#endif + + blitNormal(bm, frame, cmap, transparent); +} + +static bool checkIfWillBeCleared(const SavedImage* frame) +{ + for (int i = 0; i < frame->ExtensionBlockCount; ++i) { + ExtensionBlock* eb = frame->ExtensionBlocks + i; + if (eb->Function == GRAPHICS_EXT_FUNC_CODE && + eb->ByteCount == 4) { + // check disposal method + int disposal = ((eb->Bytes[0] >> 2) & 7); + if (disposal == 2 || disposal == 3) { + return true; + } + } + } + return false; +} + +static void getTransparencyAndDisposalMethod(const SavedImage* frame, bool* trans, int* disposal) +{ + *trans = false; + *disposal = 0; + for (int i = 0; i < frame->ExtensionBlockCount; ++i) { + ExtensionBlock* eb = frame->ExtensionBlocks + i; + if (eb->Function == GRAPHICS_EXT_FUNC_CODE && + eb->ByteCount == 4) { + *trans = ((eb->Bytes[0] & 1) == 1); + *disposal = ((eb->Bytes[0] >> 2) & 7); + } + } +} + +// return true if area of 'target' is completely covers area of 'covered' +static bool checkIfCover(const SavedImage* target, const SavedImage* covered) +{ + if (target->ImageDesc.Left <= covered->ImageDesc.Left + && covered->ImageDesc.Left + covered->ImageDesc.Width <= + target->ImageDesc.Left + target->ImageDesc.Width + && target->ImageDesc.Top <= covered->ImageDesc.Top + && covered->ImageDesc.Top + covered->ImageDesc.Height <= + target->ImageDesc.Top + target->ImageDesc.Height) { + return true; + } + return false; +} + +static void disposeFrameIfNeeded(SkBitmap* bm, const SavedImage* cur, const SavedImage* next, + SkBitmap* backup, SkColor color) +{ + // We can skip disposal process if next frame is not transparent + // and completely covers current area + bool curTrans; + int curDisposal; + getTransparencyAndDisposalMethod(cur, &curTrans, &curDisposal); + bool nextTrans; + int nextDisposal; + getTransparencyAndDisposalMethod(next, &nextTrans, &nextDisposal); + if ((curDisposal == 2 || curDisposal == 3) + && (nextTrans || !checkIfCover(next, cur))) { + switch (curDisposal) { + // restore to background color + // -> 'background' means background under this image. + case 2: + fillRect(bm, cur->ImageDesc.Left, cur->ImageDesc.Top, + cur->ImageDesc.Width, cur->ImageDesc.Height, + color); + break; + + // restore to previous + case 3: + bm->swap(*backup); + break; + } + } + + // Save current image if next frame's disposal method == 3 + if (nextDisposal == 3) { + const uint32_t* src = bm->getAddr32(0, 0); + uint32_t* dst = backup->getAddr32(0, 0); + int cnt = bm->width() * bm->height(); + memcpy(dst, src, cnt*sizeof(uint32_t)); + } +} + +bool GIFMovie::onGetBitmap(SkBitmap* bm) +{ + const GifFileType* gif = fGIF; + if (nullptr == gif) + return false; + + if (gif->ImageCount < 1) { + return false; + } + + const int width = gif->SWidth; + const int height = gif->SHeight; + if (width <= 0 || height <= 0) { + return false; + } + + // no need to draw + if (fLastDrawIndex >= 0 && fLastDrawIndex == fCurrIndex) { + return true; + } + + int startIndex = fLastDrawIndex + 1; + if (fLastDrawIndex < 0 || !bm->readyToDraw()) { + // first time + + startIndex = 0; + + // create bitmap + if (!bm->tryAllocN32Pixels(width, height)) { + return false; + } + // create bitmap for backup + if (!fBackup.tryAllocN32Pixels(width, height)) { + return false; + } + } else if (startIndex > fCurrIndex) { + // rewind to 1st frame for repeat + startIndex = 0; + } + + int lastIndex = fCurrIndex; + if (lastIndex < 0) { + // first time + lastIndex = 0; + } else if (lastIndex > fGIF->ImageCount - 1) { + // this block must not be reached. + lastIndex = fGIF->ImageCount - 1; + } + + SkColor bgColor = SkPackARGB32(0, 0, 0, 0); + if (gif->SColorMap != nullptr && gif->SBackGroundColor < gif->SColorMap->ColorCount) { + const GifColorType& col = gif->SColorMap->Colors[gif->SBackGroundColor]; + bgColor = SkColorSetARGB(0xFF, col.Red, col.Green, col.Blue); + } + + // draw each frames - not intelligent way + for (int i = startIndex; i <= lastIndex; i++) { + const SavedImage* cur = &fGIF->SavedImages[i]; + if (i == 0) { + bool trans; + int disposal; + getTransparencyAndDisposalMethod(cur, &trans, &disposal); + if (!trans && gif->SColorMap != nullptr) { + fPaintingColor = bgColor; + } else { + fPaintingColor = SkColorSetARGB(0, 0, 0, 0); + } + + bm->eraseColor(fPaintingColor); + fBackup.eraseColor(fPaintingColor); + } else { + // Dispose previous frame before move to next frame. + const SavedImage* prev = &fGIF->SavedImages[i-1]; + disposeFrameIfNeeded(bm, prev, cur, &fBackup, fPaintingColor); + } + + // Draw frame + // We can skip this process if this index is not last and disposal + // method == 2 or method == 3 + if (i == lastIndex || !checkIfWillBeCleared(cur)) { + drawFrame(bm, cur, gif->SColorMap); + } + } + + // save index + fLastDrawIndex = lastIndex; + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +Movie* Movie::DecodeStream(SkStreamRewindable* stream) { + char buf[GIF_STAMP_LEN]; + if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) { + if (memcmp(GIF_STAMP, buf, GIF_STAMP_LEN) == 0 || + memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 || + memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) { + // must rewind here, since our construct wants to re-read the data + stream->rewind(); + return new GIFMovie(stream); + } + } + return nullptr; +} diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp new file mode 100644 index 000000000000..38fb8bdc1f7a --- /dev/null +++ b/libs/hwui/jni/Graphics.cpp @@ -0,0 +1,735 @@ +#define LOG_TAG "GraphicsJNI" + +#include +#include + +#include "jni.h" +#include +#include "GraphicsJNI.h" +#include "core_jni_helpers.h" + +#include "SkCanvas.h" +#include "SkMath.h" +#include "SkRegion.h" +#include +#include +#include + +using namespace android; + +void doThrowNPE(JNIEnv* env) { + jniThrowNullPointerException(env, NULL); +} + +void doThrowAIOOBE(JNIEnv* env) { + jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL); +} + +void doThrowRE(JNIEnv* env, const char* msg) { + jniThrowRuntimeException(env, msg); +} + +void doThrowIAE(JNIEnv* env, const char* msg) { + jniThrowException(env, "java/lang/IllegalArgumentException", msg); +} + +void doThrowISE(JNIEnv* env, const char* msg) { + jniThrowException(env, "java/lang/IllegalStateException", msg); +} + +void doThrowOOME(JNIEnv* env, const char* msg) { + jniThrowException(env, "java/lang/OutOfMemoryError", msg); +} + +void doThrowIOE(JNIEnv* env, const char* msg) { + jniThrowException(env, "java/io/IOException", msg); +} + +bool GraphicsJNI::hasException(JNIEnv *env) { + if (env->ExceptionCheck() != 0) { + ALOGE("*** Uncaught exception returned from Java call!\n"); + env->ExceptionDescribe(); + return true; + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +AutoJavaFloatArray::AutoJavaFloatArray(JNIEnv* env, jfloatArray array, + int minLength, JNIAccess access) +: fEnv(env), fArray(array), fPtr(NULL), fLen(0) { + ALOG_ASSERT(env); + if (array) { + fLen = env->GetArrayLength(array); + if (fLen < minLength) { + LOG_ALWAYS_FATAL("bad length"); + } + fPtr = env->GetFloatArrayElements(array, NULL); + } + fReleaseMode = (access == kRO_JNIAccess) ? JNI_ABORT : 0; +} + +AutoJavaFloatArray::~AutoJavaFloatArray() { + if (fPtr) { + fEnv->ReleaseFloatArrayElements(fArray, fPtr, fReleaseMode); + } +} + +AutoJavaIntArray::AutoJavaIntArray(JNIEnv* env, jintArray array, + int minLength) +: fEnv(env), fArray(array), fPtr(NULL), fLen(0) { + ALOG_ASSERT(env); + if (array) { + fLen = env->GetArrayLength(array); + if (fLen < minLength) { + LOG_ALWAYS_FATAL("bad length"); + } + fPtr = env->GetIntArrayElements(array, NULL); + } +} + +AutoJavaIntArray::~AutoJavaIntArray() { + if (fPtr) { + fEnv->ReleaseIntArrayElements(fArray, fPtr, 0); + } +} + +AutoJavaShortArray::AutoJavaShortArray(JNIEnv* env, jshortArray array, + int minLength, JNIAccess access) +: fEnv(env), fArray(array), fPtr(NULL), fLen(0) { + ALOG_ASSERT(env); + if (array) { + fLen = env->GetArrayLength(array); + if (fLen < minLength) { + LOG_ALWAYS_FATAL("bad length"); + } + fPtr = env->GetShortArrayElements(array, NULL); + } + fReleaseMode = (access == kRO_JNIAccess) ? JNI_ABORT : 0; +} + +AutoJavaShortArray::~AutoJavaShortArray() { + if (fPtr) { + fEnv->ReleaseShortArrayElements(fArray, fPtr, fReleaseMode); + } +} + +AutoJavaByteArray::AutoJavaByteArray(JNIEnv* env, jbyteArray array, + int minLength) +: fEnv(env), fArray(array), fPtr(NULL), fLen(0) { + ALOG_ASSERT(env); + if (array) { + fLen = env->GetArrayLength(array); + if (fLen < minLength) { + LOG_ALWAYS_FATAL("bad length"); + } + fPtr = env->GetByteArrayElements(array, NULL); + } +} + +AutoJavaByteArray::~AutoJavaByteArray() { + if (fPtr) { + fEnv->ReleaseByteArrayElements(fArray, fPtr, 0); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static jclass gRect_class; +static jfieldID gRect_leftFieldID; +static jfieldID gRect_topFieldID; +static jfieldID gRect_rightFieldID; +static jfieldID gRect_bottomFieldID; + +static jclass gRectF_class; +static jfieldID gRectF_leftFieldID; +static jfieldID gRectF_topFieldID; +static jfieldID gRectF_rightFieldID; +static jfieldID gRectF_bottomFieldID; + +static jclass gPoint_class; +static jfieldID gPoint_xFieldID; +static jfieldID gPoint_yFieldID; + +static jclass gPointF_class; +static jfieldID gPointF_xFieldID; +static jfieldID gPointF_yFieldID; + +static jclass gBitmapConfig_class; +static jfieldID gBitmapConfig_nativeInstanceID; +static jmethodID gBitmapConfig_nativeToConfigMethodID; + +static jclass gBitmapRegionDecoder_class; +static jmethodID gBitmapRegionDecoder_constructorMethodID; + +static jclass gCanvas_class; +static jfieldID gCanvas_nativeInstanceID; + +static jclass gPicture_class; +static jfieldID gPicture_nativeInstanceID; + +static jclass gRegion_class; +static jfieldID gRegion_nativeInstanceID; +static jmethodID gRegion_constructorMethodID; + +static jclass gByte_class; +static jobject gVMRuntime; +static jclass gVMRuntime_class; +static jmethodID gVMRuntime_newNonMovableArray; +static jmethodID gVMRuntime_addressOf; + +static jclass gColorSpace_class; +static jmethodID gColorSpace_getMethodID; +static jmethodID gColorSpace_matchMethodID; + +static jclass gColorSpaceRGB_class; +static jmethodID gColorSpaceRGB_constructorMethodID; + +static jclass gColorSpace_Named_class; +static jfieldID gColorSpace_Named_sRGBFieldID; +static jfieldID gColorSpace_Named_ExtendedSRGBFieldID; +static jfieldID gColorSpace_Named_LinearSRGBFieldID; +static jfieldID gColorSpace_Named_LinearExtendedSRGBFieldID; + +static jclass gTransferParameters_class; +static jmethodID gTransferParameters_constructorMethodID; + +/////////////////////////////////////////////////////////////////////////////// + +void GraphicsJNI::get_jrect(JNIEnv* env, jobject obj, int* L, int* T, int* R, int* B) +{ + ALOG_ASSERT(env->IsInstanceOf(obj, gRect_class)); + + *L = env->GetIntField(obj, gRect_leftFieldID); + *T = env->GetIntField(obj, gRect_topFieldID); + *R = env->GetIntField(obj, gRect_rightFieldID); + *B = env->GetIntField(obj, gRect_bottomFieldID); +} + +void GraphicsJNI::set_jrect(JNIEnv* env, jobject obj, int L, int T, int R, int B) +{ + ALOG_ASSERT(env->IsInstanceOf(obj, gRect_class)); + + env->SetIntField(obj, gRect_leftFieldID, L); + env->SetIntField(obj, gRect_topFieldID, T); + env->SetIntField(obj, gRect_rightFieldID, R); + env->SetIntField(obj, gRect_bottomFieldID, B); +} + +SkIRect* GraphicsJNI::jrect_to_irect(JNIEnv* env, jobject obj, SkIRect* ir) +{ + ALOG_ASSERT(env->IsInstanceOf(obj, gRect_class)); + + ir->setLTRB(env->GetIntField(obj, gRect_leftFieldID), + env->GetIntField(obj, gRect_topFieldID), + env->GetIntField(obj, gRect_rightFieldID), + env->GetIntField(obj, gRect_bottomFieldID)); + return ir; +} + +void GraphicsJNI::irect_to_jrect(const SkIRect& ir, JNIEnv* env, jobject obj) +{ + ALOG_ASSERT(env->IsInstanceOf(obj, gRect_class)); + + env->SetIntField(obj, gRect_leftFieldID, ir.fLeft); + env->SetIntField(obj, gRect_topFieldID, ir.fTop); + env->SetIntField(obj, gRect_rightFieldID, ir.fRight); + env->SetIntField(obj, gRect_bottomFieldID, ir.fBottom); +} + +SkRect* GraphicsJNI::jrectf_to_rect(JNIEnv* env, jobject obj, SkRect* r) +{ + ALOG_ASSERT(env->IsInstanceOf(obj, gRectF_class)); + + r->setLTRB(env->GetFloatField(obj, gRectF_leftFieldID), + env->GetFloatField(obj, gRectF_topFieldID), + env->GetFloatField(obj, gRectF_rightFieldID), + env->GetFloatField(obj, gRectF_bottomFieldID)); + return r; +} + +SkRect* GraphicsJNI::jrect_to_rect(JNIEnv* env, jobject obj, SkRect* r) +{ + ALOG_ASSERT(env->IsInstanceOf(obj, gRect_class)); + + r->setLTRB(SkIntToScalar(env->GetIntField(obj, gRect_leftFieldID)), + SkIntToScalar(env->GetIntField(obj, gRect_topFieldID)), + SkIntToScalar(env->GetIntField(obj, gRect_rightFieldID)), + SkIntToScalar(env->GetIntField(obj, gRect_bottomFieldID))); + return r; +} + +void GraphicsJNI::rect_to_jrectf(const SkRect& r, JNIEnv* env, jobject obj) +{ + ALOG_ASSERT(env->IsInstanceOf(obj, gRectF_class)); + + env->SetFloatField(obj, gRectF_leftFieldID, SkScalarToFloat(r.fLeft)); + env->SetFloatField(obj, gRectF_topFieldID, SkScalarToFloat(r.fTop)); + env->SetFloatField(obj, gRectF_rightFieldID, SkScalarToFloat(r.fRight)); + env->SetFloatField(obj, gRectF_bottomFieldID, SkScalarToFloat(r.fBottom)); +} + +SkIPoint* GraphicsJNI::jpoint_to_ipoint(JNIEnv* env, jobject obj, SkIPoint* point) +{ + ALOG_ASSERT(env->IsInstanceOf(obj, gPoint_class)); + + point->set(env->GetIntField(obj, gPoint_xFieldID), + env->GetIntField(obj, gPoint_yFieldID)); + return point; +} + +void GraphicsJNI::ipoint_to_jpoint(const SkIPoint& ir, JNIEnv* env, jobject obj) +{ + ALOG_ASSERT(env->IsInstanceOf(obj, gPoint_class)); + + env->SetIntField(obj, gPoint_xFieldID, ir.fX); + env->SetIntField(obj, gPoint_yFieldID, ir.fY); +} + +SkPoint* GraphicsJNI::jpointf_to_point(JNIEnv* env, jobject obj, SkPoint* point) +{ + ALOG_ASSERT(env->IsInstanceOf(obj, gPointF_class)); + + point->set(env->GetIntField(obj, gPointF_xFieldID), + env->GetIntField(obj, gPointF_yFieldID)); + return point; +} + +void GraphicsJNI::point_to_jpointf(const SkPoint& r, JNIEnv* env, jobject obj) +{ + ALOG_ASSERT(env->IsInstanceOf(obj, gPointF_class)); + + env->SetFloatField(obj, gPointF_xFieldID, SkScalarToFloat(r.fX)); + env->SetFloatField(obj, gPointF_yFieldID, SkScalarToFloat(r.fY)); +} + +// See enum values in GraphicsJNI.h +jint GraphicsJNI::colorTypeToLegacyBitmapConfig(SkColorType colorType) { + switch (colorType) { + case kRGBA_F16_SkColorType: + return kRGBA_16F_LegacyBitmapConfig; + case kN32_SkColorType: + return kARGB_8888_LegacyBitmapConfig; + case kARGB_4444_SkColorType: + return kARGB_4444_LegacyBitmapConfig; + case kRGB_565_SkColorType: + return kRGB_565_LegacyBitmapConfig; + case kAlpha_8_SkColorType: + return kA8_LegacyBitmapConfig; + case kUnknown_SkColorType: + default: + break; + } + return kNo_LegacyBitmapConfig; +} + +SkColorType GraphicsJNI::legacyBitmapConfigToColorType(jint legacyConfig) { + const uint8_t gConfig2ColorType[] = { + kUnknown_SkColorType, + kAlpha_8_SkColorType, + kUnknown_SkColorType, // Previously kIndex_8_SkColorType, + kRGB_565_SkColorType, + kARGB_4444_SkColorType, + kN32_SkColorType, + kRGBA_F16_SkColorType, + kN32_SkColorType + }; + + if (legacyConfig < 0 || legacyConfig > kLastEnum_LegacyBitmapConfig) { + legacyConfig = kNo_LegacyBitmapConfig; + } + return static_cast(gConfig2ColorType[legacyConfig]); +} + +AndroidBitmapFormat GraphicsJNI::getFormatFromConfig(JNIEnv* env, jobject jconfig) { + ALOG_ASSERT(env); + if (NULL == jconfig) { + return ANDROID_BITMAP_FORMAT_NONE; + } + ALOG_ASSERT(env->IsInstanceOf(jconfig, gBitmapConfig_class)); + jint javaConfigId = env->GetIntField(jconfig, gBitmapConfig_nativeInstanceID); + + const AndroidBitmapFormat config2BitmapFormat[] = { + ANDROID_BITMAP_FORMAT_NONE, + ANDROID_BITMAP_FORMAT_A_8, + ANDROID_BITMAP_FORMAT_NONE, // Previously Config.Index_8 + ANDROID_BITMAP_FORMAT_RGB_565, + ANDROID_BITMAP_FORMAT_RGBA_4444, + ANDROID_BITMAP_FORMAT_RGBA_8888, + ANDROID_BITMAP_FORMAT_RGBA_F16, + ANDROID_BITMAP_FORMAT_NONE // Congfig.HARDWARE + }; + return config2BitmapFormat[javaConfigId]; +} + +jobject GraphicsJNI::getConfigFromFormat(JNIEnv* env, AndroidBitmapFormat format) { + ALOG_ASSERT(env); + jint configId = kNo_LegacyBitmapConfig; + switch (format) { + case ANDROID_BITMAP_FORMAT_A_8: + configId = kA8_LegacyBitmapConfig; + break; + case ANDROID_BITMAP_FORMAT_RGB_565: + configId = kRGB_565_LegacyBitmapConfig; + break; + case ANDROID_BITMAP_FORMAT_RGBA_4444: + configId = kARGB_4444_LegacyBitmapConfig; + break; + case ANDROID_BITMAP_FORMAT_RGBA_8888: + configId = kARGB_8888_LegacyBitmapConfig; + break; + case ANDROID_BITMAP_FORMAT_RGBA_F16: + configId = kRGBA_16F_LegacyBitmapConfig; + break; + default: + break; + } + + return env->CallStaticObjectMethod(gBitmapConfig_class, + gBitmapConfig_nativeToConfigMethodID, configId); +} + +SkColorType GraphicsJNI::getNativeBitmapColorType(JNIEnv* env, jobject jconfig) { + ALOG_ASSERT(env); + if (NULL == jconfig) { + return kUnknown_SkColorType; + } + ALOG_ASSERT(env->IsInstanceOf(jconfig, gBitmapConfig_class)); + int c = env->GetIntField(jconfig, gBitmapConfig_nativeInstanceID); + return legacyBitmapConfigToColorType(c); +} + +bool GraphicsJNI::isHardwareConfig(JNIEnv* env, jobject jconfig) { + ALOG_ASSERT(env); + if (NULL == jconfig) { + return false; + } + int c = env->GetIntField(jconfig, gBitmapConfig_nativeInstanceID); + return c == kHardware_LegacyBitmapConfig; +} + +jint GraphicsJNI::hardwareLegacyBitmapConfig() { + return kHardware_LegacyBitmapConfig; +} + +android::Canvas* GraphicsJNI::getNativeCanvas(JNIEnv* env, jobject canvas) { + ALOG_ASSERT(env); + ALOG_ASSERT(canvas); + ALOG_ASSERT(env->IsInstanceOf(canvas, gCanvas_class)); + jlong canvasHandle = env->GetLongField(canvas, gCanvas_nativeInstanceID); + if (!canvasHandle) { + return NULL; + } + return reinterpret_cast(canvasHandle); +} + +SkRegion* GraphicsJNI::getNativeRegion(JNIEnv* env, jobject region) +{ + ALOG_ASSERT(env); + ALOG_ASSERT(region); + ALOG_ASSERT(env->IsInstanceOf(region, gRegion_class)); + jlong regionHandle = env->GetLongField(region, gRegion_nativeInstanceID); + SkRegion* r = reinterpret_cast(regionHandle); + ALOG_ASSERT(r); + return r; +} + +/////////////////////////////////////////////////////////////////////////////////////////// + +jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap) +{ + ALOG_ASSERT(bitmap != NULL); + + jobject obj = env->NewObject(gBitmapRegionDecoder_class, + gBitmapRegionDecoder_constructorMethodID, + reinterpret_cast(bitmap)); + hasException(env); // For the side effect of logging. + return obj; +} + +jobject GraphicsJNI::createRegion(JNIEnv* env, SkRegion* region) +{ + ALOG_ASSERT(region != NULL); + jobject obj = env->NewObject(gRegion_class, gRegion_constructorMethodID, + reinterpret_cast(region), 0); + hasException(env); // For the side effect of logging. + return obj; +} + +/////////////////////////////////////////////////////////////////////////////// + +jobject GraphicsJNI::getColorSpace(JNIEnv* env, SkColorSpace* decodeColorSpace, + SkColorType decodeColorType) { + if (!decodeColorSpace || decodeColorType == kAlpha_8_SkColorType) { + return nullptr; + } + + // Special checks for the common sRGB cases and their extended variants. + jobject namedCS = nullptr; + sk_sp srgbLinear = SkColorSpace::MakeSRGBLinear(); + if (decodeColorType == kRGBA_F16_SkColorType) { + // An F16 Bitmap will always report that it is EXTENDED if + // it matches a ColorSpace that has an EXTENDED variant. + if (decodeColorSpace->isSRGB()) { + namedCS = env->GetStaticObjectField(gColorSpace_Named_class, + gColorSpace_Named_ExtendedSRGBFieldID); + } else if (decodeColorSpace == srgbLinear.get()) { + namedCS = env->GetStaticObjectField(gColorSpace_Named_class, + gColorSpace_Named_LinearExtendedSRGBFieldID); + } + } else if (decodeColorSpace->isSRGB()) { + namedCS = env->GetStaticObjectField(gColorSpace_Named_class, + gColorSpace_Named_sRGBFieldID); + } else if (decodeColorSpace == srgbLinear.get()) { + namedCS = env->GetStaticObjectField(gColorSpace_Named_class, + gColorSpace_Named_LinearSRGBFieldID); + } + + if (namedCS) { + return env->CallStaticObjectMethod(gColorSpace_class, gColorSpace_getMethodID, namedCS); + } + + // Try to match against known RGB color spaces using the CIE XYZ D50 + // conversion matrix and numerical transfer function parameters + skcms_Matrix3x3 xyzMatrix; + LOG_ALWAYS_FATAL_IF(!decodeColorSpace->toXYZD50(&xyzMatrix)); + + skcms_TransferFunction transferParams; + // We can only handle numerical transfer functions at the moment + LOG_ALWAYS_FATAL_IF(!decodeColorSpace->isNumericalTransferFn(&transferParams)); + + jobject params = env->NewObject(gTransferParameters_class, + gTransferParameters_constructorMethodID, + transferParams.a, transferParams.b, transferParams.c, + transferParams.d, transferParams.e, transferParams.f, + transferParams.g); + + jfloatArray xyzArray = env->NewFloatArray(9); + jfloat xyz[9] = { + xyzMatrix.vals[0][0], + xyzMatrix.vals[1][0], + xyzMatrix.vals[2][0], + xyzMatrix.vals[0][1], + xyzMatrix.vals[1][1], + xyzMatrix.vals[2][1], + xyzMatrix.vals[0][2], + xyzMatrix.vals[1][2], + xyzMatrix.vals[2][2] + }; + env->SetFloatArrayRegion(xyzArray, 0, 9, xyz); + + jobject colorSpace = env->CallStaticObjectMethod(gColorSpace_class, + gColorSpace_matchMethodID, xyzArray, params); + + if (colorSpace == nullptr) { + // We couldn't find an exact match, let's create a new color space + // instance with the 3x3 conversion matrix and transfer function + colorSpace = env->NewObject(gColorSpaceRGB_class, + gColorSpaceRGB_constructorMethodID, + env->NewStringUTF("Unknown"), xyzArray, params); + } + + env->DeleteLocalRef(xyzArray); + return colorSpace; +} + +/////////////////////////////////////////////////////////////////////////////// +bool HeapAllocator::allocPixelRef(SkBitmap* bitmap) { + mStorage = android::Bitmap::allocateHeapBitmap(bitmap); + return !!mStorage; +} + +//////////////////////////////////////////////////////////////////////////////// + +RecyclingClippingPixelAllocator::RecyclingClippingPixelAllocator( + android::Bitmap* recycledBitmap, size_t recycledBytes) + : mRecycledBitmap(recycledBitmap) + , mRecycledBytes(recycledBytes) + , mSkiaBitmap(nullptr) + , mNeedsCopy(false) +{} + +RecyclingClippingPixelAllocator::~RecyclingClippingPixelAllocator() {} + +bool RecyclingClippingPixelAllocator::allocPixelRef(SkBitmap* bitmap) { + // Ensure that the caller did not pass in a NULL bitmap to the constructor or this + // function. + LOG_ALWAYS_FATAL_IF(!mRecycledBitmap); + LOG_ALWAYS_FATAL_IF(!bitmap); + mSkiaBitmap = bitmap; + + // This behaves differently than the RecyclingPixelAllocator. For backwards + // compatibility, the original color type of the recycled bitmap must be maintained. + if (mRecycledBitmap->info().colorType() != bitmap->colorType()) { + return false; + } + + // The Skia bitmap specifies the width and height needed by the decoder. + // mRecycledBitmap specifies the width and height of the bitmap that we + // want to reuse. Neither can be changed. We will try to find a way + // to reuse the memory. + const int maxWidth = std::max(bitmap->width(), mRecycledBitmap->info().width()); + const int maxHeight = std::max(bitmap->height(), mRecycledBitmap->info().height()); + const SkImageInfo maxInfo = bitmap->info().makeWH(maxWidth, maxHeight); + const size_t rowBytes = maxInfo.minRowBytes(); + const size_t bytesNeeded = maxInfo.computeByteSize(rowBytes); + if (bytesNeeded <= mRecycledBytes) { + // Here we take advantage of reconfigure() to reset the rowBytes + // of mRecycledBitmap. It is very important that we pass in + // mRecycledBitmap->info() for the SkImageInfo. According to the + // specification for BitmapRegionDecoder, we are not allowed to change + // the SkImageInfo. + // We can (must) preserve the color space since it doesn't affect the + // storage needs + mRecycledBitmap->reconfigure( + mRecycledBitmap->info().makeColorSpace(bitmap->refColorSpace()), + rowBytes); + + // Give the bitmap the same pixelRef as mRecycledBitmap. + // skbug.com/4538: We also need to make sure that the rowBytes on the pixel ref + // match the rowBytes on the bitmap. + bitmap->setInfo(bitmap->info(), rowBytes); + bitmap->setPixelRef(sk_ref_sp(mRecycledBitmap), 0, 0); + + // Make sure that the recycled bitmap has the correct alpha type. + mRecycledBitmap->setAlphaType(bitmap->alphaType()); + + bitmap->notifyPixelsChanged(); + mNeedsCopy = false; + + // TODO: If the dimensions of the SkBitmap are smaller than those of + // mRecycledBitmap, should we zero the memory in mRecycledBitmap? + return true; + } + + // In the event that mRecycledBitmap is not large enough, allocate new memory + // on the heap. + SkBitmap::HeapAllocator heapAllocator; + + // We will need to copy from heap memory to mRecycledBitmap's memory after the + // decode is complete. + mNeedsCopy = true; + + return heapAllocator.allocPixelRef(bitmap); +} + +void RecyclingClippingPixelAllocator::copyIfNecessary() { + if (mNeedsCopy) { + mRecycledBitmap->ref(); + SkPixelRef* recycledPixels = mRecycledBitmap; + void* dst = recycledPixels->pixels(); + const size_t dstRowBytes = mRecycledBitmap->rowBytes(); + const size_t bytesToCopy = std::min(mRecycledBitmap->info().minRowBytes(), + mSkiaBitmap->info().minRowBytes()); + const int rowsToCopy = std::min(mRecycledBitmap->info().height(), + mSkiaBitmap->info().height()); + for (int y = 0; y < rowsToCopy; y++) { + memcpy(dst, mSkiaBitmap->getAddr(0, y), bytesToCopy); + dst = SkTAddOffset(dst, dstRowBytes); + } + recycledPixels->notifyPixelsChanged(); + recycledPixels->unref(); + } + mRecycledBitmap = nullptr; + mSkiaBitmap = nullptr; +} + +//////////////////////////////////////////////////////////////////////////////// + +AshmemPixelAllocator::AshmemPixelAllocator(JNIEnv *env) { + LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&mJavaVM) != JNI_OK, + "env->GetJavaVM failed"); +} + +bool AshmemPixelAllocator::allocPixelRef(SkBitmap* bitmap) { + mStorage = android::Bitmap::allocateAshmemBitmap(bitmap); + return !!mStorage; +} + +//////////////////////////////////////////////////////////////////////////////// + +int register_android_graphics_Graphics(JNIEnv* env) +{ + jmethodID m; + jclass c; + + gRect_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Rect")); + gRect_leftFieldID = GetFieldIDOrDie(env, gRect_class, "left", "I"); + gRect_topFieldID = GetFieldIDOrDie(env, gRect_class, "top", "I"); + gRect_rightFieldID = GetFieldIDOrDie(env, gRect_class, "right", "I"); + gRect_bottomFieldID = GetFieldIDOrDie(env, gRect_class, "bottom", "I"); + + gRectF_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/RectF")); + gRectF_leftFieldID = GetFieldIDOrDie(env, gRectF_class, "left", "F"); + gRectF_topFieldID = GetFieldIDOrDie(env, gRectF_class, "top", "F"); + gRectF_rightFieldID = GetFieldIDOrDie(env, gRectF_class, "right", "F"); + gRectF_bottomFieldID = GetFieldIDOrDie(env, gRectF_class, "bottom", "F"); + + gPoint_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Point")); + gPoint_xFieldID = GetFieldIDOrDie(env, gPoint_class, "x", "I"); + gPoint_yFieldID = GetFieldIDOrDie(env, gPoint_class, "y", "I"); + + gPointF_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/PointF")); + gPointF_xFieldID = GetFieldIDOrDie(env, gPointF_class, "x", "F"); + gPointF_yFieldID = GetFieldIDOrDie(env, gPointF_class, "y", "F"); + + gBitmapRegionDecoder_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/BitmapRegionDecoder")); + gBitmapRegionDecoder_constructorMethodID = GetMethodIDOrDie(env, gBitmapRegionDecoder_class, "", "(J)V"); + + gBitmapConfig_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Bitmap$Config")); + gBitmapConfig_nativeInstanceID = GetFieldIDOrDie(env, gBitmapConfig_class, "nativeInt", "I"); + gBitmapConfig_nativeToConfigMethodID = GetStaticMethodIDOrDie(env, gBitmapConfig_class, + "nativeToConfig", + "(I)Landroid/graphics/Bitmap$Config;"); + + gCanvas_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Canvas")); + gCanvas_nativeInstanceID = GetFieldIDOrDie(env, gCanvas_class, "mNativeCanvasWrapper", "J"); + + gPicture_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Picture")); + gPicture_nativeInstanceID = GetFieldIDOrDie(env, gPicture_class, "mNativePicture", "J"); + + gRegion_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Region")); + gRegion_nativeInstanceID = GetFieldIDOrDie(env, gRegion_class, "mNativeRegion", "J"); + gRegion_constructorMethodID = GetMethodIDOrDie(env, gRegion_class, "", "(JI)V"); + + c = env->FindClass("java/lang/Byte"); + gByte_class = (jclass) env->NewGlobalRef( + env->GetStaticObjectField(c, env->GetStaticFieldID(c, "TYPE", "Ljava/lang/Class;"))); + + gVMRuntime_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "dalvik/system/VMRuntime")); + m = env->GetStaticMethodID(gVMRuntime_class, "getRuntime", "()Ldalvik/system/VMRuntime;"); + gVMRuntime = env->NewGlobalRef(env->CallStaticObjectMethod(gVMRuntime_class, m)); + gVMRuntime_newNonMovableArray = GetMethodIDOrDie(env, gVMRuntime_class, "newNonMovableArray", + "(Ljava/lang/Class;I)Ljava/lang/Object;"); + gVMRuntime_addressOf = GetMethodIDOrDie(env, gVMRuntime_class, "addressOf", "(Ljava/lang/Object;)J"); + + gColorSpace_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ColorSpace")); + gColorSpace_getMethodID = GetStaticMethodIDOrDie(env, gColorSpace_class, + "get", "(Landroid/graphics/ColorSpace$Named;)Landroid/graphics/ColorSpace;"); + gColorSpace_matchMethodID = GetStaticMethodIDOrDie(env, gColorSpace_class, "match", + "([FLandroid/graphics/ColorSpace$Rgb$TransferParameters;)Landroid/graphics/ColorSpace;"); + + gColorSpaceRGB_class = MakeGlobalRefOrDie(env, + FindClassOrDie(env, "android/graphics/ColorSpace$Rgb")); + gColorSpaceRGB_constructorMethodID = GetMethodIDOrDie(env, gColorSpaceRGB_class, + "", "(Ljava/lang/String;[FLandroid/graphics/ColorSpace$Rgb$TransferParameters;)V"); + + gColorSpace_Named_class = MakeGlobalRefOrDie(env, + FindClassOrDie(env, "android/graphics/ColorSpace$Named")); + gColorSpace_Named_sRGBFieldID = GetStaticFieldIDOrDie(env, + gColorSpace_Named_class, "SRGB", "Landroid/graphics/ColorSpace$Named;"); + gColorSpace_Named_ExtendedSRGBFieldID = GetStaticFieldIDOrDie(env, + gColorSpace_Named_class, "EXTENDED_SRGB", "Landroid/graphics/ColorSpace$Named;"); + gColorSpace_Named_LinearSRGBFieldID = GetStaticFieldIDOrDie(env, + gColorSpace_Named_class, "LINEAR_SRGB", "Landroid/graphics/ColorSpace$Named;"); + gColorSpace_Named_LinearExtendedSRGBFieldID = GetStaticFieldIDOrDie(env, + gColorSpace_Named_class, "LINEAR_EXTENDED_SRGB", "Landroid/graphics/ColorSpace$Named;"); + + gTransferParameters_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, + "android/graphics/ColorSpace$Rgb$TransferParameters")); + gTransferParameters_constructorMethodID = GetMethodIDOrDie(env, gTransferParameters_class, + "", "(DDDDDDD)V"); + + return 0; +} diff --git a/libs/hwui/jni/GraphicsJNI.h b/libs/hwui/jni/GraphicsJNI.h new file mode 100644 index 000000000000..1e497654f18d --- /dev/null +++ b/libs/hwui/jni/GraphicsJNI.h @@ -0,0 +1,314 @@ +#ifndef _ANDROID_GRAPHICS_GRAPHICS_JNI_H_ +#define _ANDROID_GRAPHICS_GRAPHICS_JNI_H_ + +#include "Bitmap.h" +#include "SkBitmap.h" +#include "SkBRDAllocator.h" +#include "SkCodec.h" +#include "SkPixelRef.h" +#include "SkMallocPixelRef.h" +#include "SkPoint.h" +#include "SkRect.h" +#include "SkColorSpace.h" +#include +#include +#include + +class SkBitmapRegionDecoder; +class SkCanvas; + +namespace android { +class Paint; +struct Typeface; +} + +class GraphicsJNI { +public: + // This enum must keep these int values, to match the int values + // in the java Bitmap.Config enum. + enum LegacyBitmapConfig { + kNo_LegacyBitmapConfig = 0, + kA8_LegacyBitmapConfig = 1, + kIndex8_LegacyBitmapConfig = 2, + kRGB_565_LegacyBitmapConfig = 3, + kARGB_4444_LegacyBitmapConfig = 4, + kARGB_8888_LegacyBitmapConfig = 5, + kRGBA_16F_LegacyBitmapConfig = 6, + kHardware_LegacyBitmapConfig = 7, + + kLastEnum_LegacyBitmapConfig = kHardware_LegacyBitmapConfig + }; + + // returns true if an exception is set (and dumps it out to the Log) + static bool hasException(JNIEnv*); + + static void get_jrect(JNIEnv*, jobject jrect, int* L, int* T, int* R, int* B); + static void set_jrect(JNIEnv*, jobject jrect, int L, int T, int R, int B); + + static SkIRect* jrect_to_irect(JNIEnv*, jobject jrect, SkIRect*); + static void irect_to_jrect(const SkIRect&, JNIEnv*, jobject jrect); + + static SkRect* jrectf_to_rect(JNIEnv*, jobject jrectf, SkRect*); + static SkRect* jrect_to_rect(JNIEnv*, jobject jrect, SkRect*); + static void rect_to_jrectf(const SkRect&, JNIEnv*, jobject jrectf); + + static void set_jpoint(JNIEnv*, jobject jrect, int x, int y); + + static SkIPoint* jpoint_to_ipoint(JNIEnv*, jobject jpoint, SkIPoint* point); + static void ipoint_to_jpoint(const SkIPoint& point, JNIEnv*, jobject jpoint); + + static SkPoint* jpointf_to_point(JNIEnv*, jobject jpointf, SkPoint* point); + static void point_to_jpointf(const SkPoint& point, JNIEnv*, jobject jpointf); + + static android::Canvas* getNativeCanvas(JNIEnv*, jobject canvas); + static android::Bitmap* getNativeBitmap(JNIEnv*, jobject bitmap); + static SkImageInfo getBitmapInfo(JNIEnv*, jobject bitmap, uint32_t* outRowBytes, + bool* isHardware); + static SkRegion* getNativeRegion(JNIEnv*, jobject region); + + /* + * LegacyBitmapConfig is the old enum in Skia that matched the enum int values + * in Bitmap.Config. Skia no longer supports this config, but has replaced it + * with SkColorType. These routines convert between the two. + */ + static SkColorType legacyBitmapConfigToColorType(jint legacyConfig); + static jint colorTypeToLegacyBitmapConfig(SkColorType colorType); + + /** Return the corresponding native colorType from the java Config enum, + or kUnknown_SkColorType if the java object is null. + */ + static SkColorType getNativeBitmapColorType(JNIEnv*, jobject jconfig); + static AndroidBitmapFormat getFormatFromConfig(JNIEnv* env, jobject jconfig); + static jobject getConfigFromFormat(JNIEnv* env, AndroidBitmapFormat format); + + static bool isHardwareConfig(JNIEnv* env, jobject jconfig); + static jint hardwareLegacyBitmapConfig(); + + static jobject createRegion(JNIEnv* env, SkRegion* region); + + static jobject createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap); + + /** + * Given a bitmap we natively allocate a memory block to store the contents + * of that bitmap. The memory is then attached to the bitmap via an + * SkPixelRef, which ensures that upon deletion the appropriate caches + * are notified. + */ + static bool allocatePixels(JNIEnv* env, SkBitmap* bitmap); + + /** Copy the colors in colors[] to the bitmap, convert to the correct + format along the way. + Whether to use premultiplied pixels is determined by dstBitmap's alphaType. + */ + static bool SetPixels(JNIEnv* env, jintArray colors, int srcOffset, + int srcStride, int x, int y, int width, int height, + SkBitmap* dstBitmap); + + /** + * Convert the native SkColorSpace retrieved from ColorSpace.Rgb.getNativeInstance(). + * + * This will never throw an Exception. If the ColorSpace is one that Skia cannot + * use, ColorSpace.Rgb.getNativeInstance() would have thrown an Exception. It may, + * however, be nullptr, which may be acceptable. + */ + static sk_sp getNativeColorSpace(jlong colorSpaceHandle); + + /** + * Return the android.graphics.ColorSpace Java object that corresponds to decodeColorSpace + * and decodeColorType. + * + * This may create a new object if none of the Named ColorSpaces match. + */ + static jobject getColorSpace(JNIEnv* env, SkColorSpace* decodeColorSpace, + SkColorType decodeColorType); + + /** + * Convert from a Java @ColorLong to an SkColor4f that Skia can use directly. + * + * This ignores the encoded ColorSpace, besides checking to see if it is sRGB, + * which is encoded differently. The color space should be passed down separately + * via ColorSpace#getNativeInstance(), and converted with getNativeColorSpace(), + * above. + */ + static SkColor4f convertColorLong(jlong color); +}; + +class HeapAllocator : public SkBRDAllocator { +public: + HeapAllocator() { }; + ~HeapAllocator() { }; + + virtual bool allocPixelRef(SkBitmap* bitmap) override; + + /** + * Fetches the backing allocation object. Must be called! + */ + android::Bitmap* getStorageObjAndReset() { + return mStorage.release(); + }; + + SkCodec::ZeroInitialized zeroInit() const override { return SkCodec::kYes_ZeroInitialized; } +private: + sk_sp mStorage; +}; + +/** + * Allocator to handle reusing bitmaps for BitmapRegionDecoder. + * + * The BitmapRegionDecoder documentation states that, if it is + * provided, the recycled bitmap will always be reused, clipping + * the decoded output to fit in the recycled bitmap if necessary. + * This allocator implements that behavior. + * + * Skia's SkBitmapRegionDecoder expects the memory that + * is allocated to be large enough to decode the entire region + * that is requested. It will decode directly into the memory + * that is provided. + * + * FIXME: BUG:25465958 + * If the recycled bitmap is not large enough for the decode + * requested, meaning that a clip is required, we will allocate + * enough memory for Skia to perform the decode, and then copy + * from the decoded output into the recycled bitmap. + * + * If the recycled bitmap is large enough for the decode requested, + * we will provide that memory for Skia to decode directly into. + * + * This allocator should only be used for a single allocation. + * After we reuse the recycledBitmap once, it is dangerous to + * reuse it again, given that it still may be in use from our + * first allocation. + */ +class RecyclingClippingPixelAllocator : public SkBRDAllocator { +public: + + RecyclingClippingPixelAllocator(android::Bitmap* recycledBitmap, + size_t recycledBytes); + + ~RecyclingClippingPixelAllocator(); + + virtual bool allocPixelRef(SkBitmap* bitmap) override; + + /** + * Must be called! + * + * In the event that the recycled bitmap is not large enough for + * the allocation requested, we will allocate memory on the heap + * instead. As a final step, once we are done using this memory, + * we will copy the contents of the heap memory into the recycled + * bitmap's memory, clipping as necessary. + */ + void copyIfNecessary(); + + /** + * Indicates that this allocator does not allocate zero initialized + * memory. + */ + SkCodec::ZeroInitialized zeroInit() const override { return SkCodec::kNo_ZeroInitialized; } + +private: + android::Bitmap* mRecycledBitmap; + const size_t mRecycledBytes; + SkBitmap* mSkiaBitmap; + bool mNeedsCopy; +}; + +class AshmemPixelAllocator : public SkBitmap::Allocator { +public: + explicit AshmemPixelAllocator(JNIEnv* env); + ~AshmemPixelAllocator() { }; + virtual bool allocPixelRef(SkBitmap* bitmap); + android::Bitmap* getStorageObjAndReset() { + return mStorage.release(); + }; + +private: + JavaVM* mJavaVM; + sk_sp mStorage; +}; + + +enum JNIAccess { + kRO_JNIAccess, + kRW_JNIAccess +}; + +class AutoJavaFloatArray { +public: + AutoJavaFloatArray(JNIEnv* env, jfloatArray array, + int minLength = 0, JNIAccess = kRW_JNIAccess); + ~AutoJavaFloatArray(); + + float* ptr() const { return fPtr; } + int length() const { return fLen; } + +private: + JNIEnv* fEnv; + jfloatArray fArray; + float* fPtr; + int fLen; + int fReleaseMode; +}; + +class AutoJavaIntArray { +public: + AutoJavaIntArray(JNIEnv* env, jintArray array, int minLength = 0); + ~AutoJavaIntArray(); + + jint* ptr() const { return fPtr; } + int length() const { return fLen; } + +private: + JNIEnv* fEnv; + jintArray fArray; + jint* fPtr; + int fLen; +}; + +class AutoJavaShortArray { +public: + AutoJavaShortArray(JNIEnv* env, jshortArray array, + int minLength = 0, JNIAccess = kRW_JNIAccess); + ~AutoJavaShortArray(); + + jshort* ptr() const { return fPtr; } + int length() const { return fLen; } + +private: + JNIEnv* fEnv; + jshortArray fArray; + jshort* fPtr; + int fLen; + int fReleaseMode; +}; + +class AutoJavaByteArray { +public: + AutoJavaByteArray(JNIEnv* env, jbyteArray array, int minLength = 0); + ~AutoJavaByteArray(); + + jbyte* ptr() const { return fPtr; } + int length() const { return fLen; } + +private: + JNIEnv* fEnv; + jbyteArray fArray; + jbyte* fPtr; + int fLen; +}; + +void doThrowNPE(JNIEnv* env); +void doThrowAIOOBE(JNIEnv* env); // Array Index Out Of Bounds Exception +void doThrowIAE(JNIEnv* env, const char* msg = NULL); // Illegal Argument +void doThrowRE(JNIEnv* env, const char* msg = NULL); // Runtime +void doThrowISE(JNIEnv* env, const char* msg = NULL); // Illegal State +void doThrowOOME(JNIEnv* env, const char* msg = NULL); // Out of memory +void doThrowIOE(JNIEnv* env, const char* msg = NULL); // IO Exception + +#define NPE_CHECK_RETURN_ZERO(env, object) \ + do { if (NULL == (object)) { doThrowNPE(env); return 0; } } while (0) + +#define NPE_CHECK_RETURN_VOID(env, object) \ + do { if (NULL == (object)) { doThrowNPE(env); return; } } while (0) + +#endif // _ANDROID_GRAPHICS_GRAPHICS_JNI_H_ diff --git a/libs/hwui/jni/GraphicsStatsService.cpp b/libs/hwui/jni/GraphicsStatsService.cpp new file mode 100644 index 000000000000..ef0aacc4d9ec --- /dev/null +++ b/libs/hwui/jni/GraphicsStatsService.cpp @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2017 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 "GraphicsStatsService" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "core_jni_helpers.h" + +namespace android { + +using namespace android::uirenderer; + +static jint getAshmemSize(JNIEnv*, jobject) { + return sizeof(ProfileData); +} + +static jlong createDump(JNIEnv*, jobject, jint fd, jboolean isProto) { + GraphicsStatsService::Dump* dump = + GraphicsStatsService::createDump(fd, + isProto ? GraphicsStatsService::DumpType::Protobuf + : GraphicsStatsService::DumpType::Text); + return reinterpret_cast(dump); +} + +static void addToDump(JNIEnv* env, jobject, jlong dumpPtr, jstring jpath, jstring jpackage, + jlong versionCode, jlong startTime, jlong endTime, jbyteArray jdata) { + std::string path; + const ProfileData* data = nullptr; + LOG_ALWAYS_FATAL_IF(jdata == nullptr && jpath == nullptr, "Path and data can't both be null"); + ScopedByteArrayRO buffer{env}; + if (jdata != nullptr) { + buffer.reset(jdata); + LOG_ALWAYS_FATAL_IF(buffer.size() != sizeof(ProfileData), + "Buffer size %zu doesn't match expected %zu!", buffer.size(), + sizeof(ProfileData)); + data = reinterpret_cast(buffer.get()); + } + if (jpath != nullptr) { + ScopedUtfChars pathChars(env, jpath); + LOG_ALWAYS_FATAL_IF(pathChars.size() <= 0 || !pathChars.c_str(), + "Failed to get path chars"); + path.assign(pathChars.c_str(), pathChars.size()); + } + ScopedUtfChars packageChars(env, jpackage); + LOG_ALWAYS_FATAL_IF(packageChars.size() <= 0 || !packageChars.c_str(), + "Failed to get path chars"); + GraphicsStatsService::Dump* dump = reinterpret_cast(dumpPtr); + LOG_ALWAYS_FATAL_IF(!dump, "null passed for dump pointer"); + + const std::string package(packageChars.c_str(), packageChars.size()); + GraphicsStatsService::addToDump(dump, path, package, versionCode, startTime, endTime, data); +} + +static void addFileToDump(JNIEnv* env, jobject, jlong dumpPtr, jstring jpath) { + ScopedUtfChars pathChars(env, jpath); + LOG_ALWAYS_FATAL_IF(pathChars.size() <= 0 || !pathChars.c_str(), "Failed to get path chars"); + const std::string path(pathChars.c_str(), pathChars.size()); + GraphicsStatsService::Dump* dump = reinterpret_cast(dumpPtr); + GraphicsStatsService::addToDump(dump, path); +} + +static void finishDump(JNIEnv*, jobject, jlong dumpPtr) { + GraphicsStatsService::Dump* dump = reinterpret_cast(dumpPtr); + GraphicsStatsService::finishDump(dump); +} + +static void finishDumpInMemory(JNIEnv* env, jobject, jlong dumpPtr, jlong pulledData, + jboolean lastFullDay) { + GraphicsStatsService::Dump* dump = reinterpret_cast(dumpPtr); + AStatsEventList* data = reinterpret_cast(pulledData); + GraphicsStatsService::finishDumpInMemory(dump, data, lastFullDay == JNI_TRUE); +} + +static void saveBuffer(JNIEnv* env, jobject clazz, jstring jpath, jstring jpackage, + jlong versionCode, jlong startTime, jlong endTime, jbyteArray jdata) { + ScopedByteArrayRO buffer(env, jdata); + LOG_ALWAYS_FATAL_IF(buffer.size() != sizeof(ProfileData), + "Buffer size %zu doesn't match expected %zu!", buffer.size(), + sizeof(ProfileData)); + ScopedUtfChars pathChars(env, jpath); + LOG_ALWAYS_FATAL_IF(pathChars.size() <= 0 || !pathChars.c_str(), "Failed to get path chars"); + ScopedUtfChars packageChars(env, jpackage); + LOG_ALWAYS_FATAL_IF(packageChars.size() <= 0 || !packageChars.c_str(), + "Failed to get path chars"); + + const std::string path(pathChars.c_str(), pathChars.size()); + const std::string package(packageChars.c_str(), packageChars.size()); + const ProfileData* data = reinterpret_cast(buffer.get()); + GraphicsStatsService::saveBuffer(path, package, versionCode, startTime, endTime, data); +} + +static jobject gGraphicsStatsServiceObject = nullptr; +static jmethodID gGraphicsStatsService_pullGraphicsStatsMethodID; + +static JNIEnv* getJNIEnv() { + JavaVM* vm = AndroidRuntime::getJavaVM(); + JNIEnv* env = nullptr; + if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { + if (vm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) { + LOG_ALWAYS_FATAL("Failed to AttachCurrentThread!"); + } + } + return env; +} + +// graphicsStatsPullCallback is invoked by statsd service to pull GRAPHICS_STATS atom. +static AStatsManager_PullAtomCallbackReturn graphicsStatsPullCallback(int32_t atom_tag, + AStatsEventList* data, + void* cookie) { + JNIEnv* env = getJNIEnv(); + if (!env) { + return false; + } + if (gGraphicsStatsServiceObject == nullptr) { + ALOGE("Failed to get graphicsstats service"); + return AStatsManager_PULL_SKIP; + } + + for (bool lastFullDay : {true, false}) { + env->CallVoidMethod(gGraphicsStatsServiceObject, + gGraphicsStatsService_pullGraphicsStatsMethodID, + (jboolean)(lastFullDay ? JNI_TRUE : JNI_FALSE), + reinterpret_cast(data)); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + ALOGE("Failed to invoke graphicsstats service"); + return AStatsManager_PULL_SKIP; + } + } + return AStatsManager_PULL_SUCCESS; +} + +// Register a puller for GRAPHICS_STATS atom with the statsd service. +static void nativeInit(JNIEnv* env, jobject javaObject) { + gGraphicsStatsServiceObject = env->NewGlobalRef(javaObject); + AStatsManager_PullAtomMetadata* metadata = AStatsManager_PullAtomMetadata_obtain(); + AStatsManager_PullAtomMetadata_setCoolDownNs(metadata, 10 * 1000000); // 10 milliseconds + AStatsManager_PullAtomMetadata_setTimeoutNs(metadata, 2 * NS_PER_SEC); // 2 seconds + + AStatsManager_registerPullAtomCallback(android::util::GRAPHICS_STATS, + &graphicsStatsPullCallback, metadata, nullptr); + + AStatsManager_PullAtomMetadata_release(metadata); +} + +static void nativeDestructor(JNIEnv* env, jobject javaObject) { + AStatsManager_unregisterPullAtomCallback(android::util::GRAPHICS_STATS); + env->DeleteGlobalRef(gGraphicsStatsServiceObject); + gGraphicsStatsServiceObject = nullptr; +} + +static const JNINativeMethod sMethods[] = + {{"nGetAshmemSize", "()I", (void*)getAshmemSize}, + {"nCreateDump", "(IZ)J", (void*)createDump}, + {"nAddToDump", "(JLjava/lang/String;Ljava/lang/String;JJJ[B)V", (void*)addToDump}, + {"nAddToDump", "(JLjava/lang/String;)V", (void*)addFileToDump}, + {"nFinishDump", "(J)V", (void*)finishDump}, + {"nFinishDumpInMemory", "(JJZ)V", (void*)finishDumpInMemory}, + {"nSaveBuffer", "(Ljava/lang/String;Ljava/lang/String;JJJ[B)V", (void*)saveBuffer}, + {"nativeInit", "()V", (void*)nativeInit}, + {"nativeDestructor", "()V", (void*)nativeDestructor}}; + +int register_android_graphics_GraphicsStatsService(JNIEnv* env) { + jclass graphicsStatsService_class = + FindClassOrDie(env, "android/graphics/GraphicsStatsService"); + gGraphicsStatsService_pullGraphicsStatsMethodID = + GetMethodIDOrDie(env, graphicsStatsService_class, "pullGraphicsStats", "(ZJ)V"); + return jniRegisterNativeMethods(env, "android/graphics/GraphicsStatsService", sMethods, + NELEM(sMethods)); +} + +} // namespace android diff --git a/libs/hwui/jni/ImageDecoder.cpp b/libs/hwui/jni/ImageDecoder.cpp new file mode 100644 index 000000000000..e17e057d75c7 --- /dev/null +++ b/libs/hwui/jni/ImageDecoder.cpp @@ -0,0 +1,526 @@ +/* + * Copyright (C) 2017 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 "Bitmap.h" +#include "BitmapFactory.h" +#include "ByteBufferStreamAdaptor.h" +#include "CreateJavaOutputStreamAdaptor.h" +#include "GraphicsJNI.h" +#include "ImageDecoder.h" +#include "NinePatchPeeker.h" +#include "Utils.h" +#include "core_jni_helpers.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +using namespace android; + +static jclass gImageDecoder_class; +static jclass gSize_class; +static jclass gDecodeException_class; +static jclass gCanvas_class; +static jmethodID gImageDecoder_constructorMethodID; +static jmethodID gImageDecoder_postProcessMethodID; +static jmethodID gSize_constructorMethodID; +static jmethodID gDecodeException_constructorMethodID; +static jmethodID gCallback_onPartialImageMethodID; +static jmethodID gCanvas_constructorMethodID; +static jmethodID gCanvas_releaseMethodID; + +// These need to stay in sync with ImageDecoder.java's Allocator constants. +enum Allocator { + kDefault_Allocator = 0, + kSoftware_Allocator = 1, + kSharedMemory_Allocator = 2, + kHardware_Allocator = 3, +}; + +// These need to stay in sync with ImageDecoder.java's Error constants. +enum Error { + kSourceException = 1, + kSourceIncomplete = 2, + kSourceMalformedData = 3, +}; + +// These need to stay in sync with PixelFormat.java's Format constants. +enum PixelFormat { + kUnknown = 0, + kTranslucent = -3, + kOpaque = -1, +}; + +// Clear and return any pending exception for handling other than throwing directly. +static jthrowable get_and_clear_exception(JNIEnv* env) { + jthrowable jexception = env->ExceptionOccurred(); + if (jexception) { + env->ExceptionClear(); + } + return jexception; +} + +// Throw a new ImageDecoder.DecodeException. Returns null for convenience. +static jobject throw_exception(JNIEnv* env, Error error, const char* msg, + jthrowable cause, jobject source) { + jstring jstr = nullptr; + if (msg) { + jstr = env->NewStringUTF(msg); + if (!jstr) { + // Out of memory. + return nullptr; + } + } + jthrowable exception = (jthrowable) env->NewObject(gDecodeException_class, + gDecodeException_constructorMethodID, error, jstr, cause, source); + // Only throw if not out of memory. + if (exception) { + env->Throw(exception); + } + return nullptr; +} + +static jobject native_create(JNIEnv* env, std::unique_ptr stream, + jobject source, jboolean preferAnimation) { + if (!stream.get()) { + return throw_exception(env, kSourceMalformedData, "Failed to create a stream", + nullptr, source); + } + sk_sp peeker(new NinePatchPeeker); + SkCodec::Result result; + auto codec = SkCodec::MakeFromStream( + std::move(stream), &result, peeker.get(), + preferAnimation ? SkCodec::SelectionPolicy::kPreferAnimation + : SkCodec::SelectionPolicy::kPreferStillImage); + if (jthrowable jexception = get_and_clear_exception(env)) { + return throw_exception(env, kSourceException, "", jexception, source); + } + if (!codec) { + switch (result) { + case SkCodec::kIncompleteInput: + return throw_exception(env, kSourceIncomplete, "", nullptr, source); + default: + SkString msg; + msg.printf("Failed to create image decoder with message '%s'", + SkCodec::ResultToString(result)); + return throw_exception(env, kSourceMalformedData, msg.c_str(), + nullptr, source); + + } + } + + const bool animated = codec->getFrameCount() > 1; + if (jthrowable jexception = get_and_clear_exception(env)) { + return throw_exception(env, kSourceException, "", jexception, source); + } + + auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec), + SkAndroidCodec::ExifOrientationBehavior::kRespect); + if (!androidCodec.get()) { + return throw_exception(env, kSourceMalformedData, "", nullptr, source); + } + + const auto& info = androidCodec->getInfo(); + const int width = info.width(); + const int height = info.height(); + const bool isNinePatch = peeker->mPatch != nullptr; + ImageDecoder* decoder = new ImageDecoder(std::move(androidCodec), std::move(peeker)); + return env->NewObject(gImageDecoder_class, gImageDecoder_constructorMethodID, + reinterpret_cast(decoder), width, height, + animated, isNinePatch); +} + +static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/, + jobject fileDescriptor, jboolean preferAnimation, jobject source) { + int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor); + + struct stat fdStat; + if (fstat(descriptor, &fdStat) == -1) { + return throw_exception(env, kSourceMalformedData, + "broken file descriptor; fstat returned -1", nullptr, source); + } + + int dupDescriptor = fcntl(descriptor, F_DUPFD_CLOEXEC, 0); + FILE* file = fdopen(dupDescriptor, "r"); + if (file == NULL) { + close(dupDescriptor); + return throw_exception(env, kSourceMalformedData, "Could not open file", + nullptr, source); + } + + std::unique_ptr fileStream(new SkFILEStream(file)); + return native_create(env, std::move(fileStream), source, preferAnimation); +} + +static jobject ImageDecoder_nCreateInputStream(JNIEnv* env, jobject /*clazz*/, + jobject is, jbyteArray storage, jboolean preferAnimation, jobject source) { + std::unique_ptr stream(CreateJavaInputStreamAdaptor(env, is, storage, false)); + + if (!stream.get()) { + return throw_exception(env, kSourceMalformedData, "Failed to create a stream", + nullptr, source); + } + + std::unique_ptr bufferedStream( + SkFrontBufferedStream::Make(std::move(stream), + SkCodec::MinBufferedBytesNeeded())); + return native_create(env, std::move(bufferedStream), source, preferAnimation); +} + +static jobject ImageDecoder_nCreateAsset(JNIEnv* env, jobject /*clazz*/, + jlong assetPtr, jboolean preferAnimation, jobject source) { + Asset* asset = reinterpret_cast(assetPtr); + std::unique_ptr stream(new AssetStreamAdaptor(asset)); + return native_create(env, std::move(stream), source, preferAnimation); +} + +static jobject ImageDecoder_nCreateByteBuffer(JNIEnv* env, jobject /*clazz*/, + jobject jbyteBuffer, jint initialPosition, jint limit, + jboolean preferAnimation, jobject source) { + std::unique_ptr stream = CreateByteBufferStreamAdaptor(env, jbyteBuffer, + initialPosition, limit); + if (!stream) { + return throw_exception(env, kSourceMalformedData, "Failed to read ByteBuffer", + nullptr, source); + } + return native_create(env, std::move(stream), source, preferAnimation); +} + +static jobject ImageDecoder_nCreateByteArray(JNIEnv* env, jobject /*clazz*/, + jbyteArray byteArray, jint offset, jint length, + jboolean preferAnimation, jobject source) { + std::unique_ptr stream(CreateByteArrayStreamAdaptor(env, byteArray, offset, length)); + return native_create(env, std::move(stream), source, preferAnimation); +} + +jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, std::unique_ptr canvas) { + jobject jcanvas = env->NewObject(gCanvas_class, gCanvas_constructorMethodID, + reinterpret_cast(canvas.get())); + if (!jcanvas) { + doThrowOOME(env, "Failed to create Java Canvas for PostProcess!"); + return kUnknown; + } + + // jcanvas now owns canvas. + canvas.release(); + + return env->CallIntMethod(jimageDecoder, gImageDecoder_postProcessMethodID, jcanvas); +} + +static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, + jobject jdecoder, jboolean jpostProcess, + jint targetWidth, jint targetHeight, jobject jsubset, + jboolean requireMutable, jint allocator, + jboolean requireUnpremul, jboolean preferRamOverQuality, + jboolean asAlphaMask, jlong colorSpaceHandle, + jboolean extended) { + auto* decoder = reinterpret_cast(nativePtr); + if (!decoder->setTargetSize(targetWidth, targetHeight)) { + doThrowISE(env, "Could not scale to target size!"); + return nullptr; + } + if (requireUnpremul && !decoder->setUnpremultipliedRequired(true)) { + doThrowISE(env, "Cannot scale unpremultiplied pixels!"); + return nullptr; + } + + SkColorType colorType = kN32_SkColorType; + if (asAlphaMask && decoder->gray()) { + // We have to trick Skia to decode this to a single channel. + colorType = kGray_8_SkColorType; + } else if (preferRamOverQuality) { + // FIXME: The post-process might add alpha, which would make a 565 + // result incorrect. If we call the postProcess before now and record + // to a picture, we can know whether alpha was added, and if not, we + // can still use 565. + if (decoder->opaque() && !jpostProcess) { + // If the final result will be hardware, decoding to 565 and then + // uploading to the gpu as 8888 will not save memory. This still + // may save us from using F16, but do not go down to 565. + if (allocator != kHardware_Allocator && + (allocator != kDefault_Allocator || requireMutable)) { + colorType = kRGB_565_SkColorType; + } + } + // Otherwise, stick with N32 + } else if (extended) { + colorType = kRGBA_F16_SkColorType; + } else { + colorType = decoder->mCodec->computeOutputColorType(colorType); + } + + const bool isHardware = !requireMutable + && (allocator == kDefault_Allocator || + allocator == kHardware_Allocator) + && colorType != kGray_8_SkColorType; + + if (colorType == kRGBA_F16_SkColorType && isHardware && + !uirenderer::HardwareBitmapUploader::hasFP16Support()) { + colorType = kN32_SkColorType; + } + + if (!decoder->setOutColorType(colorType)) { + doThrowISE(env, "Failed to set out color type!"); + return nullptr; + } + + { + sk_sp colorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle); + colorSpace = decoder->mCodec->computeOutputColorSpace(colorType, colorSpace); + decoder->setOutColorSpace(std::move(colorSpace)); + } + + if (jsubset) { + SkIRect subset; + GraphicsJNI::jrect_to_irect(env, jsubset, &subset); + if (!decoder->setCropRect(&subset)) { + doThrowISE(env, "Invalid crop rect!"); + return nullptr; + } + } + + SkImageInfo bitmapInfo = decoder->getOutputInfo(); + if (decoder->opaque()) { + bitmapInfo = bitmapInfo.makeAlphaType(kOpaque_SkAlphaType); + } + if (asAlphaMask && colorType == kGray_8_SkColorType) { + bitmapInfo = bitmapInfo.makeColorType(kAlpha_8_SkColorType); + } + + SkBitmap bm; + if (!bm.setInfo(bitmapInfo)) { + doThrowIOE(env, "Failed to setInfo properly"); + return nullptr; + } + + sk_sp nativeBitmap; + if (allocator == kSharedMemory_Allocator) { + nativeBitmap = Bitmap::allocateAshmemBitmap(&bm); + } else { + nativeBitmap = Bitmap::allocateHeapBitmap(&bm); + } + if (!nativeBitmap) { + SkString msg; + msg.printf("OOM allocating Bitmap with dimensions %i x %i", + bitmapInfo.width(), bitmapInfo.height()); + doThrowOOME(env, msg.c_str()); + return nullptr; + } + + SkCodec::Result result = decoder->decode(bm.getPixels(), bm.rowBytes()); + jthrowable jexception = get_and_clear_exception(env); + int onPartialImageError = jexception ? kSourceException + : 0; // No error. + switch (result) { + case SkCodec::kSuccess: + // Ignore the exception, since the decode was successful anyway. + jexception = nullptr; + onPartialImageError = 0; + break; + case SkCodec::kIncompleteInput: + if (!jexception) { + onPartialImageError = kSourceIncomplete; + } + break; + case SkCodec::kErrorInInput: + if (!jexception) { + onPartialImageError = kSourceMalformedData; + } + break; + default: + SkString msg; + msg.printf("getPixels failed with error %s", SkCodec::ResultToString(result)); + doThrowIOE(env, msg.c_str()); + return nullptr; + } + + if (onPartialImageError) { + env->CallVoidMethod(jdecoder, gCallback_onPartialImageMethodID, onPartialImageError, + jexception); + if (env->ExceptionCheck()) { + return nullptr; + } + } + + jbyteArray ninePatchChunk = nullptr; + jobject ninePatchInsets = nullptr; + + // Ignore ninepatch when post-processing. + if (!jpostProcess) { + // FIXME: Share more code with BitmapFactory.cpp. + auto* peeker = reinterpret_cast(decoder->mPeeker.get()); + if (peeker->mPatch != nullptr) { + size_t ninePatchArraySize = peeker->mPatch->serializedSize(); + ninePatchChunk = env->NewByteArray(ninePatchArraySize); + if (ninePatchChunk == nullptr) { + doThrowOOME(env, "Failed to allocate nine patch chunk."); + return nullptr; + } + + env->SetByteArrayRegion(ninePatchChunk, 0, peeker->mPatchSize, + reinterpret_cast(peeker->mPatch)); + } + + if (peeker->mHasInsets) { + ninePatchInsets = peeker->createNinePatchInsets(env, 1.0f); + if (ninePatchInsets == nullptr) { + doThrowOOME(env, "Failed to allocate nine patch insets."); + return nullptr; + } + } + } + + if (jpostProcess) { + std::unique_ptr canvas(Canvas::create_canvas(bm)); + + jint pixelFormat = postProcessAndRelease(env, jdecoder, std::move(canvas)); + if (env->ExceptionCheck()) { + return nullptr; + } + + SkAlphaType newAlphaType = bm.alphaType(); + switch (pixelFormat) { + case kUnknown: + break; + case kTranslucent: + newAlphaType = kPremul_SkAlphaType; + break; + case kOpaque: + newAlphaType = kOpaque_SkAlphaType; + break; + default: + SkString msg; + msg.printf("invalid return from postProcess: %i", pixelFormat); + doThrowIAE(env, msg.c_str()); + return nullptr; + } + + if (newAlphaType != bm.alphaType()) { + if (!bm.setAlphaType(newAlphaType)) { + SkString msg; + msg.printf("incompatible return from postProcess: %i", pixelFormat); + doThrowIAE(env, msg.c_str()); + return nullptr; + } + nativeBitmap->setAlphaType(newAlphaType); + } + } + + int bitmapCreateFlags = 0x0; + if (!requireUnpremul) { + // Even if the image is opaque, setting this flag means that + // if alpha is added (e.g. by PostProcess), it will be marked as + // premultiplied. + bitmapCreateFlags |= bitmap::kBitmapCreateFlag_Premultiplied; + } + + if (requireMutable) { + bitmapCreateFlags |= bitmap::kBitmapCreateFlag_Mutable; + } else { + if (isHardware) { + sk_sp hwBitmap = Bitmap::allocateHardwareBitmap(bm); + if (hwBitmap) { + hwBitmap->setImmutable(); + return bitmap::createBitmap(env, hwBitmap.release(), bitmapCreateFlags, + ninePatchChunk, ninePatchInsets); + } + if (allocator == kHardware_Allocator) { + doThrowOOME(env, "failed to allocate hardware Bitmap!"); + return nullptr; + } + // If we failed to create a hardware bitmap, go ahead and create a + // software one. + } + + nativeBitmap->setImmutable(); + } + return bitmap::createBitmap(env, nativeBitmap.release(), bitmapCreateFlags, ninePatchChunk, + ninePatchInsets); +} + +static jobject ImageDecoder_nGetSampledSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, + jint sampleSize) { + auto* decoder = reinterpret_cast(nativePtr); + SkISize size = decoder->mCodec->getSampledDimensions(sampleSize); + return env->NewObject(gSize_class, gSize_constructorMethodID, size.width(), size.height()); +} + +static void ImageDecoder_nGetPadding(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, + jobject outPadding) { + auto* decoder = reinterpret_cast(nativePtr); + reinterpret_cast(decoder->mPeeker.get())->getPadding(env, outPadding); +} + +static void ImageDecoder_nClose(JNIEnv* /*env*/, jobject /*clazz*/, jlong nativePtr) { + delete reinterpret_cast(nativePtr); +} + +static jstring ImageDecoder_nGetMimeType(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { + auto* decoder = reinterpret_cast(nativePtr); + return getMimeTypeAsJavaString(env, decoder->mCodec->getEncodedFormat()); +} + +static jobject ImageDecoder_nGetColorSpace(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { + auto* codec = reinterpret_cast(nativePtr)->mCodec.get(); + auto colorType = codec->computeOutputColorType(kN32_SkColorType); + sk_sp colorSpace = codec->computeOutputColorSpace(colorType); + return GraphicsJNI::getColorSpace(env, colorSpace.get(), colorType); +} + +static const JNINativeMethod gImageDecoderMethods[] = { + { "nCreate", "(JZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateAsset }, + { "nCreate", "(Ljava/nio/ByteBuffer;IIZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteBuffer }, + { "nCreate", "([BIIZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray }, + { "nCreate", "(Ljava/io/InputStream;[BZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream }, + { "nCreate", "(Ljava/io/FileDescriptor;ZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd }, + { "nDecodeBitmap", "(JLandroid/graphics/ImageDecoder;ZIILandroid/graphics/Rect;ZIZZZJZ)Landroid/graphics/Bitmap;", + (void*) ImageDecoder_nDecodeBitmap }, + { "nGetSampledSize","(JI)Landroid/util/Size;", (void*) ImageDecoder_nGetSampledSize }, + { "nGetPadding", "(JLandroid/graphics/Rect;)V", (void*) ImageDecoder_nGetPadding }, + { "nClose", "(J)V", (void*) ImageDecoder_nClose}, + { "nGetMimeType", "(J)Ljava/lang/String;", (void*) ImageDecoder_nGetMimeType }, + { "nGetColorSpace", "(J)Landroid/graphics/ColorSpace;", (void*) ImageDecoder_nGetColorSpace }, +}; + +int register_android_graphics_ImageDecoder(JNIEnv* env) { + gImageDecoder_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ImageDecoder")); + gImageDecoder_constructorMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "", "(JIIZZ)V"); + gImageDecoder_postProcessMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "postProcessAndRelease", "(Landroid/graphics/Canvas;)I"); + + gSize_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/util/Size")); + gSize_constructorMethodID = GetMethodIDOrDie(env, gSize_class, "", "(II)V"); + + gDecodeException_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ImageDecoder$DecodeException")); + gDecodeException_constructorMethodID = GetMethodIDOrDie(env, gDecodeException_class, "", "(ILjava/lang/String;Ljava/lang/Throwable;Landroid/graphics/ImageDecoder$Source;)V"); + + gCallback_onPartialImageMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "onPartialImage", "(ILjava/lang/Throwable;)V"); + + gCanvas_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Canvas")); + gCanvas_constructorMethodID = GetMethodIDOrDie(env, gCanvas_class, "", "(J)V"); + gCanvas_releaseMethodID = GetMethodIDOrDie(env, gCanvas_class, "release", "()V"); + + return android::RegisterMethodsOrDie(env, "android/graphics/ImageDecoder", gImageDecoderMethods, + NELEM(gImageDecoderMethods)); +} diff --git a/libs/hwui/jni/ImageDecoder.h b/libs/hwui/jni/ImageDecoder.h new file mode 100644 index 000000000000..8a7fa79503ba --- /dev/null +++ b/libs/hwui/jni/ImageDecoder.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2018 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 + +#include + +// Creates a Java Canvas object from canvas, calls jimageDecoder's PostProcess on it, and then +// releases the Canvas. +// Caller needs to check for exceptions. +jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, + std::unique_ptr canvas); diff --git a/libs/hwui/jni/Interpolator.cpp b/libs/hwui/jni/Interpolator.cpp new file mode 100644 index 000000000000..fa28359281db --- /dev/null +++ b/libs/hwui/jni/Interpolator.cpp @@ -0,0 +1,87 @@ +#include "GraphicsJNI.h" +#include "SkInterpolator.h" +#include "core_jni_helpers.h" + +#include + +static jlong Interpolator_constructor(JNIEnv* env, jobject clazz, jint valueCount, jint frameCount) +{ + return reinterpret_cast(new SkInterpolator(valueCount, frameCount)); +} + +static void Interpolator_destructor(JNIEnv* env, jobject clazz, jlong interpHandle) +{ + SkInterpolator* interp = reinterpret_cast(interpHandle); + delete interp; +} + +static void Interpolator_reset(JNIEnv* env, jobject clazz, jlong interpHandle, jint valueCount, jint frameCount) +{ + SkInterpolator* interp = reinterpret_cast(interpHandle); + interp->reset(valueCount, frameCount); +} + +static void Interpolator_setKeyFrame(JNIEnv* env, jobject clazz, jlong interpHandle, jint index, jint msec, jfloatArray valueArray, jfloatArray blendArray) +{ + SkInterpolator* interp = reinterpret_cast(interpHandle); + + AutoJavaFloatArray autoValues(env, valueArray); + AutoJavaFloatArray autoBlend(env, blendArray, 4); +#ifdef SK_SCALAR_IS_FLOAT + SkScalar* scalars = autoValues.ptr(); + SkScalar* blend = autoBlend.ptr(); +#else + #error Need to convert float array to SkScalar array before calling the following function. +#endif + + interp->setKeyFrame(index, msec, scalars, blend); +} + +static void Interpolator_setRepeatMirror(JNIEnv* env, jobject clazz, jlong interpHandle, jfloat repeatCount, jboolean mirror) +{ + SkInterpolator* interp = reinterpret_cast(interpHandle); + if (repeatCount > 32000) + repeatCount = 32000; + + interp->setRepeatCount(repeatCount); + interp->setMirror(mirror != 0); +} + +static jint Interpolator_timeToValues(JNIEnv* env, jobject clazz, jlong interpHandle, jint msec, jfloatArray valueArray) +{ + SkInterpolator* interp = reinterpret_cast(interpHandle); + SkInterpolatorBase::Result result; + + float* values = valueArray ? env->GetFloatArrayElements(valueArray, NULL) : NULL; + result = interp->timeToValues(msec, (SkScalar*)values); + + if (valueArray) { + int n = env->GetArrayLength(valueArray); + for (int i = 0; i < n; i++) { + values[i] = SkScalarToFloat(*(SkScalar*)&values[i]); + } + env->ReleaseFloatArrayElements(valueArray, values, 0); + } + + return static_cast(result); +} + +// ---------------------------------------------------------------------------- + +/* + * JNI registration. + */ +static const JNINativeMethod gInterpolatorMethods[] = { + { "nativeConstructor", "(II)J", (void*)Interpolator_constructor }, + { "nativeDestructor", "(J)V", (void*)Interpolator_destructor }, + { "nativeReset", "(JII)V", (void*)Interpolator_reset }, + { "nativeSetKeyFrame", "(JII[F[F)V", (void*)Interpolator_setKeyFrame }, + { "nativeSetRepeatMirror", "(JFZ)V", (void*)Interpolator_setRepeatMirror }, + { "nativeTimeToValues", "(JI[F)I", (void*)Interpolator_timeToValues } +}; + +int register_android_graphics_Interpolator(JNIEnv* env) +{ + return android::RegisterMethodsOrDie(env, "android/graphics/Interpolator", + gInterpolatorMethods, NELEM(gInterpolatorMethods)); +} diff --git a/libs/hwui/jni/MaskFilter.cpp b/libs/hwui/jni/MaskFilter.cpp new file mode 100644 index 000000000000..33d346f5d379 --- /dev/null +++ b/libs/hwui/jni/MaskFilter.cpp @@ -0,0 +1,94 @@ +#include "GraphicsJNI.h" +#include "SkMaskFilter.h" +#include "SkBlurMask.h" +#include "SkBlurMaskFilter.h" +#include "SkTableMaskFilter.h" + +#include "core_jni_helpers.h" + +#include + +static void ThrowIAE_IfNull(JNIEnv* env, void* ptr) { + if (NULL == ptr) { + doThrowIAE(env); + } +} + +class SkMaskFilterGlue { +public: + static void destructor(JNIEnv* env, jobject, jlong filterHandle) { + SkMaskFilter* filter = reinterpret_cast(filterHandle); + SkSafeUnref(filter); + } + + static jlong createBlur(JNIEnv* env, jobject, jfloat radius, jint blurStyle) { + SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(radius); + SkMaskFilter* filter = SkMaskFilter::MakeBlur((SkBlurStyle)blurStyle, sigma).release(); + ThrowIAE_IfNull(env, filter); + return reinterpret_cast(filter); + } + + static jlong createEmboss(JNIEnv* env, jobject, jfloatArray dirArray, jfloat ambient, jfloat specular, jfloat radius) { + SkScalar direction[3]; + + AutoJavaFloatArray autoDir(env, dirArray, 3); + float* values = autoDir.ptr(); + for (int i = 0; i < 3; i++) { + direction[i] = values[i]; + } + + SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(radius); + SkMaskFilter* filter = SkBlurMaskFilter::MakeEmboss(sigma, + direction, ambient, specular).release(); + ThrowIAE_IfNull(env, filter); + return reinterpret_cast(filter); + } + + static jlong createTable(JNIEnv* env, jobject, jbyteArray jtable) { + AutoJavaByteArray autoTable(env, jtable, 256); + SkMaskFilter* filter = SkTableMaskFilter::Create((const uint8_t*)autoTable.ptr()); + return reinterpret_cast(filter); + } + + static jlong createClipTable(JNIEnv* env, jobject, jint min, jint max) { + SkMaskFilter* filter = SkTableMaskFilter::CreateClip(min, max); + return reinterpret_cast(filter); + } + + static jlong createGammaTable(JNIEnv* env, jobject, jfloat gamma) { + SkMaskFilter* filter = SkTableMaskFilter::CreateGamma(gamma); + return reinterpret_cast(filter); + } +}; + +static const JNINativeMethod gMaskFilterMethods[] = { + { "nativeDestructor", "(J)V", (void*)SkMaskFilterGlue::destructor } +}; + +static const JNINativeMethod gBlurMaskFilterMethods[] = { + { "nativeConstructor", "(FI)J", (void*)SkMaskFilterGlue::createBlur } +}; + +static const JNINativeMethod gEmbossMaskFilterMethods[] = { + { "nativeConstructor", "([FFFF)J", (void*)SkMaskFilterGlue::createEmboss } +}; + +static const JNINativeMethod gTableMaskFilterMethods[] = { + { "nativeNewTable", "([B)J", (void*)SkMaskFilterGlue::createTable }, + { "nativeNewClip", "(II)J", (void*)SkMaskFilterGlue::createClipTable }, + { "nativeNewGamma", "(F)J", (void*)SkMaskFilterGlue::createGammaTable } +}; + +int register_android_graphics_MaskFilter(JNIEnv* env) +{ + android::RegisterMethodsOrDie(env, "android/graphics/MaskFilter", gMaskFilterMethods, + NELEM(gMaskFilterMethods)); + android::RegisterMethodsOrDie(env, "android/graphics/BlurMaskFilter", gBlurMaskFilterMethods, + NELEM(gBlurMaskFilterMethods)); + android::RegisterMethodsOrDie(env, "android/graphics/EmbossMaskFilter", + gEmbossMaskFilterMethods, NELEM(gEmbossMaskFilterMethods)); + android::RegisterMethodsOrDie(env, "android/graphics/TableMaskFilter", gTableMaskFilterMethods, + NELEM(gTableMaskFilterMethods)); + + return 0; +} diff --git a/libs/hwui/jni/Movie.cpp b/libs/hwui/jni/Movie.cpp new file mode 100644 index 000000000000..4c10a85c8257 --- /dev/null +++ b/libs/hwui/jni/Movie.cpp @@ -0,0 +1,166 @@ +#include "CreateJavaOutputStreamAdaptor.h" +#include "GraphicsJNI.h" +#include +#include "SkFrontBufferedStream.h" +#include "Movie.h" +#include "SkStream.h" +#include "SkUtils.h" +#include "Utils.h" +#include "core_jni_helpers.h" + +#include +#include +#include +#include +#include +#include + +static jclass gMovie_class; +static jmethodID gMovie_constructorMethodID; +static jfieldID gMovie_nativeInstanceID; + +jobject create_jmovie(JNIEnv* env, Movie* moov) { + if (NULL == moov) { + return NULL; + } + return env->NewObject(gMovie_class, gMovie_constructorMethodID, + static_cast(reinterpret_cast(moov))); +} + +static Movie* J2Movie(JNIEnv* env, jobject movie) { + SkASSERT(env); + SkASSERT(movie); + SkASSERT(env->IsInstanceOf(movie, gMovie_class)); + Movie* m = (Movie*)env->GetLongField(movie, gMovie_nativeInstanceID); + SkASSERT(m); + return m; +} + +/////////////////////////////////////////////////////////////////////////////// + +static jint movie_width(JNIEnv* env, jobject movie) { + NPE_CHECK_RETURN_ZERO(env, movie); + return static_cast(J2Movie(env, movie)->width()); +} + +static jint movie_height(JNIEnv* env, jobject movie) { + NPE_CHECK_RETURN_ZERO(env, movie); + return static_cast(J2Movie(env, movie)->height()); +} + +static jboolean movie_isOpaque(JNIEnv* env, jobject movie) { + NPE_CHECK_RETURN_ZERO(env, movie); + return J2Movie(env, movie)->isOpaque() ? JNI_TRUE : JNI_FALSE; +} + +static jint movie_duration(JNIEnv* env, jobject movie) { + NPE_CHECK_RETURN_ZERO(env, movie); + return static_cast(J2Movie(env, movie)->duration()); +} + +static jboolean movie_setTime(JNIEnv* env, jobject movie, jint ms) { + NPE_CHECK_RETURN_ZERO(env, movie); + return J2Movie(env, movie)->setTime(ms) ? JNI_TRUE : JNI_FALSE; +} + +static void movie_draw(JNIEnv* env, jobject movie, jlong canvasHandle, + jfloat fx, jfloat fy, jlong paintHandle) { + NPE_CHECK_RETURN_VOID(env, movie); + + android::Canvas* c = reinterpret_cast(canvasHandle); + const android::Paint* p = reinterpret_cast(paintHandle); + + // Canvas should never be NULL. However paint is an optional parameter and + // therefore may be NULL. + SkASSERT(c != NULL); + + Movie* m = J2Movie(env, movie); + const SkBitmap& b = m->bitmap(); + sk_sp wrapper = android::Bitmap::createFrom(b.info(), *b.pixelRef()); + c->drawBitmap(*wrapper, fx, fy, p); +} + +static jobject movie_decodeAsset(JNIEnv* env, jobject clazz, jlong native_asset) { + android::Asset* asset = reinterpret_cast(native_asset); + if (asset == NULL) return NULL; + android::AssetStreamAdaptor stream(asset); + Movie* moov = Movie::DecodeStream(&stream); + return create_jmovie(env, moov); +} + +static jobject movie_decodeStream(JNIEnv* env, jobject clazz, jobject istream) { + + NPE_CHECK_RETURN_ZERO(env, istream); + + jbyteArray byteArray = env->NewByteArray(16*1024); + ScopedLocalRef scoper(env, byteArray); + SkStream* strm = CreateJavaInputStreamAdaptor(env, istream, byteArray); + if (NULL == strm) { + return 0; + } + + // Need to buffer enough input to be able to rewind as much as might be read by a decoder + // trying to determine the stream's format. The only decoder for movies is GIF, which + // will only read 6. + // FIXME: Get this number from SkImageDecoder + // bufferedStream takes ownership of strm + std::unique_ptr bufferedStream(SkFrontBufferedStream::Make( + std::unique_ptr(strm), 6)); + SkASSERT(bufferedStream.get() != NULL); + + Movie* moov = Movie::DecodeStream(bufferedStream.get()); + return create_jmovie(env, moov); +} + +static jobject movie_decodeByteArray(JNIEnv* env, jobject clazz, + jbyteArray byteArray, + jint offset, jint length) { + + NPE_CHECK_RETURN_ZERO(env, byteArray); + + int totalLength = env->GetArrayLength(byteArray); + if ((offset | length) < 0 || offset + length > totalLength) { + doThrowAIOOBE(env); + return 0; + } + + AutoJavaByteArray ar(env, byteArray); + Movie* moov = Movie::DecodeMemory(ar.ptr() + offset, length); + return create_jmovie(env, moov); +} + +static void movie_destructor(JNIEnv* env, jobject, jlong movieHandle) { + Movie* movie = (Movie*) movieHandle; + delete movie; +} + +////////////////////////////////////////////////////////////////////////////////////////////// + +static const JNINativeMethod gMethods[] = { + { "width", "()I", (void*)movie_width }, + { "height", "()I", (void*)movie_height }, + { "isOpaque", "()Z", (void*)movie_isOpaque }, + { "duration", "()I", (void*)movie_duration }, + { "setTime", "(I)Z", (void*)movie_setTime }, + { "nDraw", "(JFFJ)V", + (void*)movie_draw }, + { "nativeDecodeAsset", "(J)Landroid/graphics/Movie;", + (void*)movie_decodeAsset }, + { "nativeDecodeStream", "(Ljava/io/InputStream;)Landroid/graphics/Movie;", + (void*)movie_decodeStream }, + { "nativeDestructor","(J)V", (void*)movie_destructor }, + { "decodeByteArray", "([BII)Landroid/graphics/Movie;", + (void*)movie_decodeByteArray }, +}; + +int register_android_graphics_Movie(JNIEnv* env) +{ + gMovie_class = android::FindClassOrDie(env, "android/graphics/Movie"); + gMovie_class = android::MakeGlobalRefOrDie(env, gMovie_class); + + gMovie_constructorMethodID = android::GetMethodIDOrDie(env, gMovie_class, "", "(J)V"); + + gMovie_nativeInstanceID = android::GetFieldIDOrDie(env, gMovie_class, "mNativeMovie", "J"); + + return android::RegisterMethodsOrDie(env, "android/graphics/Movie", gMethods, NELEM(gMethods)); +} diff --git a/libs/hwui/jni/Movie.h b/libs/hwui/jni/Movie.h new file mode 100644 index 000000000000..736890d5215e --- /dev/null +++ b/libs/hwui/jni/Movie.h @@ -0,0 +1,79 @@ + +/* + * Copyright 2008 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + + +#ifndef Movie_DEFINED +#define Movie_DEFINED + +#include "SkBitmap.h" +#include "SkCanvas.h" +#include "SkRefCnt.h" + +class SkStreamRewindable; + +class Movie : public SkRefCnt { +public: + /** Try to create a movie from the stream. If the stream format is not + supported, return NULL. + */ + static Movie* DecodeStream(SkStreamRewindable*); + /** Try to create a movie from the specified file path. If the file is not + found, or the format is not supported, return NULL. If a movie is + returned, the stream may be retained by the movie (via ref()) until + the movie is finished with it (by calling unref()). + */ + static Movie* DecodeFile(const char path[]); + /** Try to create a movie from the specified memory. + If the format is not supported, return NULL. If a movie is returned, + the data will have been read or copied, and so the caller may free + it. + */ + static Movie* DecodeMemory(const void* data, size_t length); + + SkMSec duration(); + int width(); + int height(); + int isOpaque(); + + /** Specify the time code (between 0...duration) to sample a bitmap + from the movie. Returns true if this time code generated a different + bitmap/frame from the previous state (i.e. true means you need to + redraw). + */ + bool setTime(SkMSec); + + // return the right bitmap for the current time code + const SkBitmap& bitmap(); + +protected: + struct Info { + SkMSec fDuration; + int fWidth; + int fHeight; + bool fIsOpaque; + }; + + virtual bool onGetInfo(Info*) = 0; + virtual bool onSetTime(SkMSec) = 0; + virtual bool onGetBitmap(SkBitmap*) = 0; + + // visible for subclasses + Movie(); + +private: + Info fInfo; + SkMSec fCurrTime; + SkBitmap fBitmap; + bool fNeedBitmap; + + void ensureInfo(); + + typedef SkRefCnt INHERITED; +}; + +#endif diff --git a/libs/hwui/jni/MovieImpl.cpp b/libs/hwui/jni/MovieImpl.cpp new file mode 100644 index 000000000000..ae9e04e617b0 --- /dev/null +++ b/libs/hwui/jni/MovieImpl.cpp @@ -0,0 +1,94 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "Movie.h" +#include "SkCanvas.h" +#include "SkPaint.h" + +// We should never see this in normal operation since our time values are +// 0-based. So we use it as a sentinal. +#define UNINITIALIZED_MSEC ((SkMSec)-1) + +Movie::Movie() +{ + fInfo.fDuration = UNINITIALIZED_MSEC; // uninitialized + fCurrTime = UNINITIALIZED_MSEC; // uninitialized + fNeedBitmap = true; +} + +void Movie::ensureInfo() +{ + if (fInfo.fDuration == UNINITIALIZED_MSEC && !this->onGetInfo(&fInfo)) + memset(&fInfo, 0, sizeof(fInfo)); // failure +} + +SkMSec Movie::duration() +{ + this->ensureInfo(); + return fInfo.fDuration; +} + +int Movie::width() +{ + this->ensureInfo(); + return fInfo.fWidth; +} + +int Movie::height() +{ + this->ensureInfo(); + return fInfo.fHeight; +} + +int Movie::isOpaque() +{ + this->ensureInfo(); + return fInfo.fIsOpaque; +} + +bool Movie::setTime(SkMSec time) +{ + SkMSec dur = this->duration(); + if (time > dur) + time = dur; + + bool changed = false; + if (time != fCurrTime) + { + fCurrTime = time; + changed = this->onSetTime(time); + fNeedBitmap |= changed; + } + return changed; +} + +const SkBitmap& Movie::bitmap() +{ + if (fCurrTime == UNINITIALIZED_MSEC) // uninitialized + this->setTime(0); + + if (fNeedBitmap) + { + if (!this->onGetBitmap(&fBitmap)) // failure + fBitmap.reset(); + fNeedBitmap = false; + } + return fBitmap; +} + +//////////////////////////////////////////////////////////////////// + +#include "SkStream.h" + +Movie* Movie::DecodeMemory(const void* data, size_t length) { + SkMemoryStream stream(data, length, false); + return Movie::DecodeStream(&stream); +} + +Movie* Movie::DecodeFile(const char path[]) { + std::unique_ptr stream = SkStream::MakeFromFile(path); + return stream ? Movie::DecodeStream(stream.get()) : nullptr; +} diff --git a/libs/hwui/jni/NinePatch.cpp b/libs/hwui/jni/NinePatch.cpp new file mode 100644 index 000000000000..15f951688d43 --- /dev/null +++ b/libs/hwui/jni/NinePatch.cpp @@ -0,0 +1,170 @@ +/* +** +** Copyright 2006, 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 "9patch" +#define LOG_NDEBUG 1 + +#include +#include +#include +#include + +#include "SkCanvas.h" +#include "SkLatticeIter.h" +#include "SkRegion.h" +#include "GraphicsJNI.h" +#include "NinePatchPeeker.h" +#include "NinePatchUtils.h" + +#include +#include "core_jni_helpers.h" + +jclass gInsetStruct_class; +jmethodID gInsetStruct_constructorMethodID; + +using namespace android; + +/** + * IMPORTANT NOTE: 9patch chunks can be manipuated either as an array of bytes + * or as a Res_png_9patch instance. It is important to note that the size of the + * array required to hold a 9patch chunk is greater than sizeof(Res_png_9patch). + * The code below manipulates chunks as Res_png_9patch* types to draw and as + * int8_t* to allocate and free the backing storage. + */ + +class SkNinePatchGlue { +public: + static jboolean isNinePatchChunk(JNIEnv* env, jobject, jbyteArray obj) { + if (NULL == obj) { + return JNI_FALSE; + } + if (env->GetArrayLength(obj) < (int)sizeof(Res_png_9patch)) { + return JNI_FALSE; + } + const jbyte* array = env->GetByteArrayElements(obj, 0); + if (array != NULL) { + const Res_png_9patch* chunk = reinterpret_cast(array); + int8_t wasDeserialized = chunk->wasDeserialized; + env->ReleaseByteArrayElements(obj, const_cast(array), JNI_ABORT); + return (wasDeserialized != -1) ? JNI_TRUE : JNI_FALSE; + } + return JNI_FALSE; + } + + static jlong validateNinePatchChunk(JNIEnv* env, jobject, jbyteArray obj) { + size_t chunkSize = env->GetArrayLength(obj); + if (chunkSize < (int) (sizeof(Res_png_9patch))) { + jniThrowRuntimeException(env, "Array too small for chunk."); + return NULL; + } + + int8_t* storage = new int8_t[chunkSize]; + // This call copies the content of the jbyteArray + env->GetByteArrayRegion(obj, 0, chunkSize, reinterpret_cast(storage)); + // Deserialize in place, return the array we just allocated + return reinterpret_cast(Res_png_9patch::deserialize(storage)); + } + + static void finalize(JNIEnv* env, jobject, jlong patchHandle) { + int8_t* patch = reinterpret_cast(patchHandle); + delete[] patch; + } + + static jlong getTransparentRegion(JNIEnv* env, jobject, jlong bitmapPtr, + jlong chunkHandle, jobject dstRect) { + Res_png_9patch* chunk = reinterpret_cast(chunkHandle); + SkASSERT(chunk); + + SkBitmap bitmap; + bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap); + SkRect dst; + GraphicsJNI::jrect_to_rect(env, dstRect, &dst); + + SkCanvas::Lattice lattice; + SkIRect src = SkIRect::MakeWH(bitmap.width(), bitmap.height()); + lattice.fBounds = &src; + NinePatchUtils::SetLatticeDivs(&lattice, *chunk, bitmap.width(), bitmap.height()); + lattice.fRectTypes = nullptr; + lattice.fColors = nullptr; + + SkRegion* region = nullptr; + if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), lattice)) { + SkLatticeIter iter(lattice, dst); + if (iter.numRectsToDraw() == chunk->numColors) { + SkRect dummy; + SkRect iterDst; + int index = 0; + while (iter.next(&dummy, &iterDst)) { + if (0 == chunk->getColors()[index++] && !iterDst.isEmpty()) { + if (!region) { + region = new SkRegion(); + } + + region->op(iterDst.round(), SkRegion::kUnion_Op); + } + } + } + } + + return reinterpret_cast(region); + } + +}; + +jobject NinePatchPeeker::createNinePatchInsets(JNIEnv* env, float scale) const { + if (!mHasInsets) { + return nullptr; + } + + return env->NewObject(gInsetStruct_class, gInsetStruct_constructorMethodID, + mOpticalInsets[0], mOpticalInsets[1], + mOpticalInsets[2], mOpticalInsets[3], + mOutlineInsets[0], mOutlineInsets[1], + mOutlineInsets[2], mOutlineInsets[3], + mOutlineRadius, mOutlineAlpha, scale); +} + +void NinePatchPeeker::getPadding(JNIEnv* env, jobject outPadding) const { + if (mPatch) { + GraphicsJNI::set_jrect(env, outPadding, + mPatch->paddingLeft, mPatch->paddingTop, + mPatch->paddingRight, mPatch->paddingBottom); + + } else { + GraphicsJNI::set_jrect(env, outPadding, -1, -1, -1, -1); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static const JNINativeMethod gNinePatchMethods[] = { + { "isNinePatchChunk", "([B)Z", (void*) SkNinePatchGlue::isNinePatchChunk }, + { "validateNinePatchChunk", "([B)J", + (void*) SkNinePatchGlue::validateNinePatchChunk }, + { "nativeFinalize", "(J)V", (void*) SkNinePatchGlue::finalize }, + { "nativeGetTransparentRegion", "(JJLandroid/graphics/Rect;)J", + (void*) SkNinePatchGlue::getTransparentRegion } +}; + +int register_android_graphics_NinePatch(JNIEnv* env) { + gInsetStruct_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, + "android/graphics/NinePatch$InsetStruct")); + gInsetStruct_constructorMethodID = GetMethodIDOrDie(env, gInsetStruct_class, "", + "(IIIIIIIIFIF)V"); + return android::RegisterMethodsOrDie(env, + "android/graphics/NinePatch", gNinePatchMethods, NELEM(gNinePatchMethods)); +} diff --git a/libs/hwui/jni/NinePatchPeeker.cpp b/libs/hwui/jni/NinePatchPeeker.cpp new file mode 100644 index 000000000000..9171fc687276 --- /dev/null +++ b/libs/hwui/jni/NinePatchPeeker.cpp @@ -0,0 +1,93 @@ +/* + * 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 "NinePatchPeeker.h" + +#include +#include + +using namespace android; + +bool NinePatchPeeker::readChunk(const char tag[], const void* data, size_t length) { + if (!strcmp("npTc", tag) && length >= sizeof(Res_png_9patch)) { + Res_png_9patch* patch = (Res_png_9patch*) data; + size_t patchSize = patch->serializedSize(); + if (length != patchSize) { + return false; + } + // You have to copy the data because it is owned by the png reader + Res_png_9patch* patchNew = (Res_png_9patch*) malloc(patchSize); + memcpy(patchNew, patch, patchSize); + Res_png_9patch::deserialize(patchNew); + patchNew->fileToDevice(); + free(mPatch); + mPatch = patchNew; + mPatchSize = patchSize; + } else if (!strcmp("npLb", tag) && length == sizeof(int32_t) * 4) { + mHasInsets = true; + memcpy(&mOpticalInsets, data, sizeof(int32_t) * 4); + } else if (!strcmp("npOl", tag) && length == 24) { // 4 int32_ts, 1 float, 1 int32_t sized byte + mHasInsets = true; + memcpy(&mOutlineInsets, data, sizeof(int32_t) * 4); + mOutlineRadius = ((const float*)data)[4]; + mOutlineAlpha = ((const int32_t*)data)[5] & 0xff; + } + return true; // keep on decoding +} + +static void scaleDivRange(int32_t* divs, int count, float scale, int maxValue) { + for (int i = 0; i < count; i++) { + divs[i] = int32_t(divs[i] * scale + 0.5f); + if (i > 0 && divs[i] == divs[i - 1]) { + divs[i]++; // avoid collisions + } + } + + if (CC_UNLIKELY(divs[count - 1] > maxValue)) { + // if the collision avoidance above put some divs outside the bounds of the bitmap, + // slide outer stretchable divs inward to stay within bounds + int highestAvailable = maxValue; + for (int i = count - 1; i >= 0; i--) { + divs[i] = highestAvailable; + if (i > 0 && divs[i] <= divs[i-1]) { + // keep shifting + highestAvailable = divs[i] - 1; + } else { + break; + } + } + } +} + +void NinePatchPeeker::scale(float scaleX, float scaleY, int scaledWidth, int scaledHeight) { + if (!mPatch) { + return; + } + + // The max value for the divRange is one pixel less than the actual max to ensure that the size + // of the last div is not zero. A div of size 0 is considered invalid input and will not render. + if (!SkScalarNearlyEqual(scaleX, 1.0f)) { + mPatch->paddingLeft = int(mPatch->paddingLeft * scaleX + 0.5f); + mPatch->paddingRight = int(mPatch->paddingRight * scaleX + 0.5f); + scaleDivRange(mPatch->getXDivs(), mPatch->numXDivs, scaleX, scaledWidth - 1); + } + + if (!SkScalarNearlyEqual(scaleY, 1.0f)) { + mPatch->paddingTop = int(mPatch->paddingTop * scaleY + 0.5f); + mPatch->paddingBottom = int(mPatch->paddingBottom * scaleY + 0.5f); + scaleDivRange(mPatch->getYDivs(), mPatch->numYDivs, scaleY, scaledHeight - 1); + } +} diff --git a/libs/hwui/jni/NinePatchPeeker.h b/libs/hwui/jni/NinePatchPeeker.h new file mode 100644 index 000000000000..e4e58dda4783 --- /dev/null +++ b/libs/hwui/jni/NinePatchPeeker.h @@ -0,0 +1,59 @@ +/* + * 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_GRAPHICS_NINE_PATCH_PEEKER_H_ +#define _ANDROID_GRAPHICS_NINE_PATCH_PEEKER_H_ + +#include "SkPngChunkReader.h" +#include + +#include + +using namespace android; + +class NinePatchPeeker : public SkPngChunkReader { +public: + NinePatchPeeker() + : mPatch(NULL) + , mPatchSize(0) + , mHasInsets(false) + , mOutlineRadius(0) + , mOutlineAlpha(0) { + memset(mOpticalInsets, 0, 4 * sizeof(int32_t)); + memset(mOutlineInsets, 0, 4 * sizeof(int32_t)); + } + + ~NinePatchPeeker() { + free(mPatch); + } + + bool readChunk(const char tag[], const void* data, size_t length) override; + + jobject createNinePatchInsets(JNIEnv*, float scale) const; + void getPadding(JNIEnv*, jobject outPadding) const; + void scale(float scaleX, float scaleY, int scaledWidth, int scaledHeight); + + Res_png_9patch* mPatch; + size_t mPatchSize; + bool mHasInsets; +private: + int32_t mOpticalInsets[4]; + int32_t mOutlineInsets[4]; + float mOutlineRadius; + uint8_t mOutlineAlpha; +}; + +#endif // _ANDROID_GRAPHICS_NINE_PATCH_PEEKER_H_ diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp new file mode 100644 index 000000000000..8e1bc8489baa --- /dev/null +++ b/libs/hwui/jni/Paint.cpp @@ -0,0 +1,1160 @@ +/* libs/android_runtime/android/graphics/Paint.cpp +** +** Copyright 2006, 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 "Paint" + +#include + +#include "jni.h" +#include "GraphicsJNI.h" +#include "core_jni_helpers.h" +#include +#include +#include + +#include "SkBlurDrawLooper.h" +#include "SkColorFilter.h" +#include "SkFont.h" +#include "SkFontMetrics.h" +#include "SkFontTypes.h" +#include "SkMaskFilter.h" +#include "SkPath.h" +#include "SkPathEffect.h" +#include "SkShader.h" +#include "SkBlendMode.h" +#include "unicode/uloc.h" +#include "unicode/ushape.h" +#include "utils/Blur.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace android { + +struct JMetricsID { + jfieldID top; + jfieldID ascent; + jfieldID descent; + jfieldID bottom; + jfieldID leading; +}; + +static jclass gFontMetrics_class; +static JMetricsID gFontMetrics_fieldID; + +static jclass gFontMetricsInt_class; +static JMetricsID gFontMetricsInt_fieldID; + +static void getPosTextPath(const SkFont& font, const uint16_t glyphs[], int count, + const SkPoint pos[], SkPath* dst) { + dst->reset(); + struct Rec { + SkPath* fDst; + const SkPoint* fPos; + } rec = { dst, pos }; + font.getPaths(glyphs, count, [](const SkPath* src, const SkMatrix& mx, void* ctx) { + Rec* rec = (Rec*)ctx; + if (src) { + SkMatrix tmp(mx); + tmp.postTranslate(rec->fPos->fX, rec->fPos->fY); + rec->fDst->addPath(*src, tmp); + } + rec->fPos += 1; + }, &rec); +} + +namespace PaintGlue { + enum MoveOpt { + AFTER, AT_OR_AFTER, BEFORE, AT_OR_BEFORE, AT + }; + + static void deletePaint(Paint* paint) { + delete paint; + } + + static jlong getNativeFinalizer(JNIEnv*, jobject) { + return static_cast(reinterpret_cast(&deletePaint)); + } + + static jlong init(JNIEnv* env, jobject) { + return reinterpret_cast(new Paint); + } + + static jlong initWithPaint(JNIEnv* env, jobject clazz, jlong paintHandle) { + Paint* paint = reinterpret_cast(paintHandle); + Paint* obj = new Paint(*paint); + return reinterpret_cast(obj); + } + + static int breakText(JNIEnv* env, const Paint& paint, const Typeface* typeface, + const jchar text[], int count, float maxWidth, jint bidiFlags, jfloatArray jmeasured, + const bool forwardScan) { + size_t measuredCount = 0; + float measured = 0; + + std::unique_ptr advancesArray(new float[count]); + MinikinUtils::measureText(&paint, static_cast(bidiFlags), typeface, text, + 0, count, count, advancesArray.get()); + + for (int i = 0; i < count; i++) { + // traverse in the given direction + int index = forwardScan ? i : (count - i - 1); + float width = advancesArray[index]; + if (measured + width > maxWidth) { + break; + } + // properly handle clusters when scanning backwards + if (forwardScan || width != 0.0f) { + measuredCount = i + 1; + } + measured += width; + } + + if (jmeasured && env->GetArrayLength(jmeasured) > 0) { + AutoJavaFloatArray autoMeasured(env, jmeasured, 1); + jfloat* array = autoMeasured.ptr(); + array[0] = measured; + } + return measuredCount; + } + + static jint breakTextC(JNIEnv* env, jobject clazz, jlong paintHandle, jcharArray jtext, + jint index, jint count, jfloat maxWidth, jint bidiFlags, jfloatArray jmeasuredWidth) { + NPE_CHECK_RETURN_ZERO(env, jtext); + + Paint* paint = reinterpret_cast(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + + bool forwardTextDirection; + if (count < 0) { + forwardTextDirection = false; + count = -count; + } + else { + forwardTextDirection = true; + } + + if ((index < 0) || (index + count > env->GetArrayLength(jtext))) { + doThrowAIOOBE(env); + return 0; + } + + const jchar* text = env->GetCharArrayElements(jtext, nullptr); + count = breakText(env, *paint, typeface, text + index, count, maxWidth, + bidiFlags, jmeasuredWidth, forwardTextDirection); + env->ReleaseCharArrayElements(jtext, const_cast(text), + JNI_ABORT); + return count; + } + + static jint breakTextS(JNIEnv* env, jobject clazz, jlong paintHandle, jstring jtext, + jboolean forwards, jfloat maxWidth, jint bidiFlags, jfloatArray jmeasuredWidth) { + NPE_CHECK_RETURN_ZERO(env, jtext); + + Paint* paint = reinterpret_cast(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + + int count = env->GetStringLength(jtext); + const jchar* text = env->GetStringChars(jtext, nullptr); + count = breakText(env, *paint, typeface, text, count, maxWidth, bidiFlags, jmeasuredWidth, forwards); + env->ReleaseStringChars(jtext, text); + return count; + } + + static jfloat doTextAdvances(JNIEnv *env, Paint *paint, const Typeface* typeface, + const jchar *text, jint start, jint count, jint contextCount, jint bidiFlags, + jfloatArray advances, jint advancesIndex) { + NPE_CHECK_RETURN_ZERO(env, text); + + if ((start | count | contextCount | advancesIndex) < 0 || contextCount < count) { + doThrowAIOOBE(env); + return 0; + } + if (count == 0) { + return 0; + } + if (advances) { + size_t advancesLength = env->GetArrayLength(advances); + if ((size_t)(count + advancesIndex) > advancesLength) { + doThrowAIOOBE(env); + return 0; + } + } + std::unique_ptr advancesArray; + if (advances) { + advancesArray.reset(new jfloat[count]); + } + const float advance = MinikinUtils::measureText(paint, + static_cast(bidiFlags), typeface, text, start, count, contextCount, + advancesArray.get()); + if (advances) { + env->SetFloatArrayRegion(advances, advancesIndex, count, advancesArray.get()); + } + return advance; + } + + static jfloat getTextAdvances___CIIIII_FI(JNIEnv* env, jobject clazz, jlong paintHandle, + jcharArray text, jint index, jint count, jint contextIndex, jint contextCount, + jint bidiFlags, jfloatArray advances, jint advancesIndex) { + Paint* paint = reinterpret_cast(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + jchar* textArray = env->GetCharArrayElements(text, nullptr); + jfloat result = doTextAdvances(env, paint, typeface, textArray + contextIndex, + index - contextIndex, count, contextCount, bidiFlags, advances, advancesIndex); + env->ReleaseCharArrayElements(text, textArray, JNI_ABORT); + return result; + } + + static jfloat getTextAdvances__StringIIIII_FI(JNIEnv* env, jobject clazz, jlong paintHandle, + jstring text, jint start, jint end, jint contextStart, jint contextEnd, jint bidiFlags, + jfloatArray advances, jint advancesIndex) { + Paint* paint = reinterpret_cast(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + const jchar* textArray = env->GetStringChars(text, nullptr); + jfloat result = doTextAdvances(env, paint, typeface, textArray + contextStart, + start - contextStart, end - start, contextEnd - contextStart, bidiFlags, + advances, advancesIndex); + env->ReleaseStringChars(text, textArray); + return result; + } + + static jint doTextRunCursor(JNIEnv *env, Paint* paint, const Typeface* typeface, + const jchar *text, jint start, jint count, jint dir, jint offset, jint opt) { + minikin::GraphemeBreak::MoveOpt moveOpt = minikin::GraphemeBreak::MoveOpt(opt); + minikin::Bidi bidiFlags = dir == 1 ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR; + std::unique_ptr advancesArray(new float[count]); + MinikinUtils::measureText(paint, bidiFlags, typeface, text, start, count, start + count, + advancesArray.get()); + size_t result = minikin::GraphemeBreak::getTextRunCursor(advancesArray.get(), text, + start, count, offset, moveOpt); + return static_cast(result); + } + + static jint getTextRunCursor___C(JNIEnv* env, jobject clazz, jlong paintHandle, jcharArray text, + jint contextStart, jint contextCount, jint dir, jint offset, jint cursorOpt) { + Paint* paint = reinterpret_cast(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + jchar* textArray = env->GetCharArrayElements(text, nullptr); + jint result = doTextRunCursor(env, paint, typeface, textArray, + contextStart, contextCount, dir, offset, cursorOpt); + env->ReleaseCharArrayElements(text, textArray, JNI_ABORT); + return result; + } + + static jint getTextRunCursor__String(JNIEnv* env, jobject clazz, jlong paintHandle, + jstring text, jint contextStart, jint contextEnd, jint dir, jint offset, + jint cursorOpt) { + Paint* paint = reinterpret_cast(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + const jchar* textArray = env->GetStringChars(text, nullptr); + jint result = doTextRunCursor(env, paint, typeface, textArray, + contextStart, contextEnd - contextStart, dir, offset, cursorOpt); + env->ReleaseStringChars(text, textArray); + return result; + } + + class GetTextFunctor { + public: + GetTextFunctor(const minikin::Layout& layout, SkPath* path, jfloat x, jfloat y, + Paint* paint, uint16_t* glyphs, SkPoint* pos) + : layout(layout), path(path), x(x), y(y), paint(paint), glyphs(glyphs), pos(pos) { + } + + void operator()(size_t start, size_t end) { + for (size_t i = start; i < end; i++) { + glyphs[i] = layout.getGlyphId(i); + pos[i].fX = x + layout.getX(i); + pos[i].fY = y + layout.getY(i); + } + const SkFont& font = paint->getSkFont(); + if (start == 0) { + getPosTextPath(font, glyphs, end, pos, path); + } else { + getPosTextPath(font, glyphs + start, end - start, pos + start, &tmpPath); + path->addPath(tmpPath); + } + } + private: + const minikin::Layout& layout; + SkPath* path; + jfloat x; + jfloat y; + Paint* paint; + uint16_t* glyphs; + SkPoint* pos; + SkPath tmpPath; + }; + + static void getTextPath(JNIEnv* env, Paint* paint, const Typeface* typeface, const jchar* text, + jint count, jint bidiFlags, jfloat x, jfloat y, SkPath* path) { + minikin::Layout layout = MinikinUtils::doLayout( + paint, static_cast(bidiFlags), typeface, + text, count, // text buffer + 0, count, // draw range + 0, count, // context range + nullptr); + size_t nGlyphs = layout.nGlyphs(); + uint16_t* glyphs = new uint16_t[nGlyphs]; + SkPoint* pos = new SkPoint[nGlyphs]; + + x += MinikinUtils::xOffsetForTextAlign(paint, layout); + Paint::Align align = paint->getTextAlign(); + paint->setTextAlign(Paint::kLeft_Align); + GetTextFunctor f(layout, path, x, y, paint, glyphs, pos); + MinikinUtils::forFontRun(layout, paint, f); + paint->setTextAlign(align); + delete[] glyphs; + delete[] pos; + } + + static void getTextPath___C(JNIEnv* env, jobject clazz, jlong paintHandle, jint bidiFlags, + jcharArray text, jint index, jint count, jfloat x, jfloat y, jlong pathHandle) { + Paint* paint = reinterpret_cast(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + SkPath* path = reinterpret_cast(pathHandle); + const jchar* textArray = env->GetCharArrayElements(text, nullptr); + getTextPath(env, paint, typeface, textArray + index, count, bidiFlags, x, y, path); + env->ReleaseCharArrayElements(text, const_cast(textArray), JNI_ABORT); + } + + static void getTextPath__String(JNIEnv* env, jobject clazz, jlong paintHandle, jint bidiFlags, + jstring text, jint start, jint end, jfloat x, jfloat y, jlong pathHandle) { + Paint* paint = reinterpret_cast(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + SkPath* path = reinterpret_cast(pathHandle); + const jchar* textArray = env->GetStringChars(text, nullptr); + getTextPath(env, paint, typeface, textArray + start, end - start, bidiFlags, x, y, path); + env->ReleaseStringChars(text, textArray); + } + + static void doTextBounds(JNIEnv* env, const jchar* text, int count, jobject bounds, + const Paint& paint, const Typeface* typeface, jint bidiFlags) { + SkRect r; + SkIRect ir; + + minikin::Layout layout = MinikinUtils::doLayout(&paint, + static_cast(bidiFlags), typeface, + text, count, // text buffer + 0, count, // draw range + 0, count, // context range + nullptr); + minikin::MinikinRect rect; + layout.getBounds(&rect); + r.fLeft = rect.mLeft; + r.fTop = rect.mTop; + r.fRight = rect.mRight; + r.fBottom = rect.mBottom; + r.roundOut(&ir); + GraphicsJNI::irect_to_jrect(ir, env, bounds); + } + + static void getStringBounds(JNIEnv* env, jobject, jlong paintHandle, jstring text, jint start, + jint end, jint bidiFlags, jobject bounds) { + const Paint* paint = reinterpret_cast(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + const jchar* textArray = env->GetStringChars(text, nullptr); + doTextBounds(env, textArray + start, end - start, bounds, *paint, typeface, bidiFlags); + env->ReleaseStringChars(text, textArray); + } + + static void getCharArrayBounds(JNIEnv* env, jobject, jlong paintHandle, jcharArray text, + jint index, jint count, jint bidiFlags, jobject bounds) { + const Paint* paint = reinterpret_cast(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + const jchar* textArray = env->GetCharArrayElements(text, nullptr); + doTextBounds(env, textArray + index, count, bounds, *paint, typeface, bidiFlags); + env->ReleaseCharArrayElements(text, const_cast(textArray), + JNI_ABORT); + } + + // Returns true if the given string is exact one pair of regional indicators. + static bool isFlag(const jchar* str, size_t length) { + const jchar RI_LEAD_SURROGATE = 0xD83C; + const jchar RI_TRAIL_SURROGATE_MIN = 0xDDE6; + const jchar RI_TRAIL_SURROGATE_MAX = 0xDDFF; + + if (length != 4) { + return false; + } + if (str[0] != RI_LEAD_SURROGATE || str[2] != RI_LEAD_SURROGATE) { + return false; + } + return RI_TRAIL_SURROGATE_MIN <= str[1] && str[1] <= RI_TRAIL_SURROGATE_MAX && + RI_TRAIL_SURROGATE_MIN <= str[3] && str[3] <= RI_TRAIL_SURROGATE_MAX; + } + + static jboolean layoutContainsNotdef(const minikin::Layout& layout) { + for (size_t i = 0; i < layout.nGlyphs(); i++) { + if (layout.getGlyphId(i) == 0) { + return true; + } + } + return false; + } + + // Don't count glyphs that are the recommended "space" glyph and are zero width. + // This logic makes assumptions about HarfBuzz layout, but does correctly handle + // cases where ligatures form and zero width space glyphs are left in as + // placeholders. + static size_t countNonSpaceGlyphs(const minikin::Layout& layout) { + size_t count = 0; + static unsigned int kSpaceGlyphId = 3; + for (size_t i = 0; i < layout.nGlyphs(); i++) { + if (layout.getGlyphId(i) != kSpaceGlyphId || layout.getCharAdvance(i) != 0.0) { + count++; + } + } + return count; + } + + static jboolean hasGlyph(JNIEnv *env, jclass, jlong paintHandle, jint bidiFlags, + jstring string) { + const Paint* paint = reinterpret_cast(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + ScopedStringChars str(env, string); + + /* Start by rejecting unsupported base code point and variation selector pairs. */ + size_t nChars = 0; + const uint32_t kStartOfString = 0xFFFFFFFF; + uint32_t prevCp = kStartOfString; + for (size_t i = 0; i < str.size(); i++) { + jchar cu = str[i]; + uint32_t cp = cu; + if (U16_IS_TRAIL(cu)) { + // invalid UTF-16, unpaired trailing surrogate + return false; + } else if (U16_IS_LEAD(cu)) { + if (i + 1 == str.size()) { + // invalid UTF-16, unpaired leading surrogate at end of string + return false; + } + i++; + jchar cu2 = str[i]; + if (!U16_IS_TRAIL(cu2)) { + // invalid UTF-16, unpaired leading surrogate + return false; + } + cp = U16_GET_SUPPLEMENTARY(cu, cu2); + } + + if (prevCp != kStartOfString && + ((0xFE00 <= cp && cp <= 0xFE0F) || (0xE0100 <= cp && cp <= 0xE01EF))) { + bool hasVS = MinikinUtils::hasVariationSelector(typeface, prevCp, cp); + if (!hasVS) { + // No font has a glyph for the code point and variation selector pair. + return false; + } else if (nChars == 1 && i + 1 == str.size()) { + // The string is just a codepoint and a VS, we have an authoritative answer + return true; + } + } + nChars++; + prevCp = cp; + } + minikin::Layout layout = MinikinUtils::doLayout(paint, + static_cast(bidiFlags), typeface, + str.get(), str.size(), // text buffer + 0, str.size(), // draw range + 0, str.size(), // context range + nullptr); + size_t nGlyphs = countNonSpaceGlyphs(layout); + if (nGlyphs != 1 && nChars > 1) { + // multiple-character input, and was not a ligature + // TODO: handle ZWJ/ZWNJ characters specially so we can detect certain ligatures + // in joining scripts, such as Arabic and Mongolian. + return false; + } + + if (nGlyphs == 0 || layoutContainsNotdef(layout)) { + return false; // The collection doesn't have a glyph. + } + + if (nChars == 2 && isFlag(str.get(), str.size())) { + // Some font may have a special glyph for unsupported regional indicator pairs. + // To return false for this case, need to compare the glyph id with the one of ZZ + // since ZZ is reserved for unknown or invalid territory. + // U+1F1FF (REGIONAL INDICATOR SYMBOL LETTER Z) is \uD83C\uDDFF in UTF16. + static const jchar ZZ_FLAG_STR[] = { 0xD83C, 0xDDFF, 0xD83C, 0xDDFF }; + minikin::Layout zzLayout = MinikinUtils::doLayout(paint, + static_cast(bidiFlags), typeface, + ZZ_FLAG_STR, 4, // text buffer + 0, 4, // draw range + 0, 4, // context range + nullptr); + if (zzLayout.nGlyphs() != 1 || layoutContainsNotdef(zzLayout)) { + // The font collection doesn't have a glyph for unknown flag. Just return true. + return true; + } + return zzLayout.getGlyphId(0) != layout.getGlyphId(0); + } + return true; + } + + static jfloat doRunAdvance(const Paint* paint, const Typeface* typeface, const jchar buf[], + jint start, jint count, jint bufSize, jboolean isRtl, jint offset) { + minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR; + if (offset == start + count) { + return MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count, + bufSize, nullptr); + } + std::unique_ptr advancesArray(new float[count]); + MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count, bufSize, + advancesArray.get()); + return minikin::getRunAdvance(advancesArray.get(), buf, start, count, offset); + } + + static jfloat getRunAdvance___CIIIIZI_F(JNIEnv *env, jclass, jlong paintHandle, jcharArray text, + jint start, jint end, jint contextStart, jint contextEnd, jboolean isRtl, jint offset) { + const Paint* paint = reinterpret_cast(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + ScopedCharArrayRO textArray(env, text); + jfloat result = doRunAdvance(paint, typeface, textArray.get() + contextStart, + start - contextStart, end - start, contextEnd - contextStart, isRtl, + offset - contextStart); + return result; + } + + static jint doOffsetForAdvance(const Paint* paint, const Typeface* typeface, const jchar buf[], + jint start, jint count, jint bufSize, jboolean isRtl, jfloat advance) { + minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR; + std::unique_ptr advancesArray(new float[count]); + MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count, bufSize, + advancesArray.get()); + return minikin::getOffsetForAdvance(advancesArray.get(), buf, start, count, advance); + } + + static jint getOffsetForAdvance___CIIIIZF_I(JNIEnv *env, jclass, jlong paintHandle, + jcharArray text, jint start, jint end, jint contextStart, jint contextEnd, + jboolean isRtl, jfloat advance) { + const Paint* paint = reinterpret_cast(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + ScopedCharArrayRO textArray(env, text); + jint result = doOffsetForAdvance(paint, typeface, textArray.get() + contextStart, + start - contextStart, end - start, contextEnd - contextStart, isRtl, advance); + result += contextStart; + return result; + } + + // ------------------ @FastNative --------------------------- + + static jint setTextLocales(JNIEnv* env, jobject clazz, jlong objHandle, jstring locales) { + Paint* obj = reinterpret_cast(objHandle); + ScopedUtfChars localesChars(env, locales); + jint minikinLocaleListId = minikin::registerLocaleList(localesChars.c_str()); + obj->setMinikinLocaleListId(minikinLocaleListId); + return minikinLocaleListId; + } + + static void setFontFeatureSettings(JNIEnv* env, jobject clazz, jlong paintHandle, jstring settings) { + Paint* paint = reinterpret_cast(paintHandle); + if (!settings) { + paint->setFontFeatureSettings(std::string()); + } else { + ScopedUtfChars settingsChars(env, settings); + paint->setFontFeatureSettings(std::string(settingsChars.c_str(), settingsChars.size())); + } + } + + static SkScalar getMetricsInternal(jlong paintHandle, SkFontMetrics *metrics) { + const int kElegantTop = 2500; + const int kElegantBottom = -1000; + const int kElegantAscent = 1900; + const int kElegantDescent = -500; + const int kElegantLeading = 0; + Paint* paint = reinterpret_cast(paintHandle); + SkFont* font = &paint->getSkFont(); + const Typeface* typeface = paint->getAndroidTypeface(); + typeface = Typeface::resolveDefault(typeface); + minikin::FakedFont baseFont = typeface->fFontCollection->baseFontFaked(typeface->fStyle); + float saveSkewX = font->getSkewX(); + bool savefakeBold = font->isEmbolden(); + MinikinFontSkia::populateSkFont(font, baseFont.font->typeface().get(), baseFont.fakery); + SkScalar spacing = font->getMetrics(metrics); + // The populateSkPaint call may have changed fake bold / text skew + // because we want to measure with those effects applied, so now + // restore the original settings. + font->setSkewX(saveSkewX); + font->setEmbolden(savefakeBold); + if (paint->getFamilyVariant() == minikin::FamilyVariant::ELEGANT) { + SkScalar size = font->getSize(); + metrics->fTop = -size * kElegantTop / 2048; + metrics->fBottom = -size * kElegantBottom / 2048; + metrics->fAscent = -size * kElegantAscent / 2048; + metrics->fDescent = -size * kElegantDescent / 2048; + metrics->fLeading = size * kElegantLeading / 2048; + spacing = metrics->fDescent - metrics->fAscent + metrics->fLeading; + } + return spacing; + } + + static jfloat getFontMetrics(JNIEnv* env, jobject, jlong paintHandle, jobject metricsObj) { + SkFontMetrics metrics; + SkScalar spacing = getMetricsInternal(paintHandle, &metrics); + + if (metricsObj) { + SkASSERT(env->IsInstanceOf(metricsObj, gFontMetrics_class)); + env->SetFloatField(metricsObj, gFontMetrics_fieldID.top, SkScalarToFloat(metrics.fTop)); + env->SetFloatField(metricsObj, gFontMetrics_fieldID.ascent, SkScalarToFloat(metrics.fAscent)); + env->SetFloatField(metricsObj, gFontMetrics_fieldID.descent, SkScalarToFloat(metrics.fDescent)); + env->SetFloatField(metricsObj, gFontMetrics_fieldID.bottom, SkScalarToFloat(metrics.fBottom)); + env->SetFloatField(metricsObj, gFontMetrics_fieldID.leading, SkScalarToFloat(metrics.fLeading)); + } + return SkScalarToFloat(spacing); + } + + static jint getFontMetricsInt(JNIEnv* env, jobject, jlong paintHandle, jobject metricsObj) { + SkFontMetrics metrics; + + getMetricsInternal(paintHandle, &metrics); + int ascent = SkScalarRoundToInt(metrics.fAscent); + int descent = SkScalarRoundToInt(metrics.fDescent); + int leading = SkScalarRoundToInt(metrics.fLeading); + + if (metricsObj) { + SkASSERT(env->IsInstanceOf(metricsObj, gFontMetricsInt_class)); + env->SetIntField(metricsObj, gFontMetricsInt_fieldID.top, SkScalarFloorToInt(metrics.fTop)); + env->SetIntField(metricsObj, gFontMetricsInt_fieldID.ascent, ascent); + env->SetIntField(metricsObj, gFontMetricsInt_fieldID.descent, descent); + env->SetIntField(metricsObj, gFontMetricsInt_fieldID.bottom, SkScalarCeilToInt(metrics.fBottom)); + env->SetIntField(metricsObj, gFontMetricsInt_fieldID.leading, leading); + } + return descent - ascent + leading; + } + + + // ------------------ @CriticalNative --------------------------- + + static void reset(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) { + reinterpret_cast(objHandle)->reset(); + } + + static void assign(CRITICAL_JNI_PARAMS_COMMA jlong dstPaintHandle, jlong srcPaintHandle) { + Paint* dst = reinterpret_cast(dstPaintHandle); + const Paint* src = reinterpret_cast(srcPaintHandle); + *dst = *src; + } + + static jint getFlags(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { + uint32_t flags = reinterpret_cast(paintHandle)->getJavaFlags(); + return static_cast(flags); + } + + static void setFlags(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint flags) { + reinterpret_cast(paintHandle)->setJavaFlags(flags); + } + + static jint getHinting(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { + return (SkFontHinting)reinterpret_cast(paintHandle)->getSkFont().getHinting() + == SkFontHinting::kNone ? 0 : 1; + } + + static void setHinting(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint mode) { + reinterpret_cast(paintHandle)->getSkFont().setHinting( + mode == 0 ? SkFontHinting::kNone : SkFontHinting::kNormal); + } + + static void setAntiAlias(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean aa) { + reinterpret_cast(paintHandle)->setAntiAlias(aa); + } + + static void setLinearText(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean linearText) { + reinterpret_cast(paintHandle)->getSkFont().setLinearMetrics(linearText); + } + + static void setSubpixelText(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean subpixelText) { + reinterpret_cast(paintHandle)->getSkFont().setSubpixel(subpixelText); + } + + static void setUnderlineText(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean underlineText) { + reinterpret_cast(paintHandle)->setUnderline(underlineText); + } + + static void setStrikeThruText(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean strikeThruText) { + reinterpret_cast(paintHandle)->setStrikeThru(strikeThruText); + } + + static void setFakeBoldText(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean fakeBoldText) { + reinterpret_cast(paintHandle)->getSkFont().setEmbolden(fakeBoldText); + } + + static void setFilterBitmap(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean filterBitmap) { + reinterpret_cast(paintHandle)->setFilterQuality( + filterBitmap ? kLow_SkFilterQuality : kNone_SkFilterQuality); + } + + static void setDither(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean dither) { + reinterpret_cast(paintHandle)->setDither(dither); + } + + static jint getStyle(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) { + Paint* obj = reinterpret_cast(objHandle); + return static_cast(obj->getStyle()); + } + + static void setStyle(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jint styleHandle) { + Paint* obj = reinterpret_cast(objHandle); + Paint::Style style = static_cast(styleHandle); + obj->setStyle(style); + } + + static void setColorLong(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jlong colorSpaceHandle, + jlong colorLong) { + SkColor4f color = GraphicsJNI::convertColorLong(colorLong); + sk_sp cs = GraphicsJNI::getNativeColorSpace(colorSpaceHandle); + reinterpret_cast(paintHandle)->setColor4f(color, cs.get()); + } + + static void setColor(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint color) { + reinterpret_cast(paintHandle)->setColor(color); + } + + static void setAlpha(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint a) { + reinterpret_cast(paintHandle)->setAlpha(a); + } + + static jfloat getStrokeWidth(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { + return SkScalarToFloat(reinterpret_cast(paintHandle)->getStrokeWidth()); + } + + static void setStrokeWidth(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jfloat width) { + reinterpret_cast(paintHandle)->setStrokeWidth(width); + } + + static jfloat getStrokeMiter(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { + return SkScalarToFloat(reinterpret_cast(paintHandle)->getStrokeMiter()); + } + + static void setStrokeMiter(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jfloat miter) { + reinterpret_cast(paintHandle)->setStrokeMiter(miter); + } + + static jint getStrokeCap(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) { + Paint* obj = reinterpret_cast(objHandle); + return static_cast(obj->getStrokeCap()); + } + + static void setStrokeCap(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jint capHandle) { + Paint* obj = reinterpret_cast(objHandle); + Paint::Cap cap = static_cast(capHandle); + obj->setStrokeCap(cap); + } + + static jint getStrokeJoin(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) { + Paint* obj = reinterpret_cast(objHandle); + return static_cast(obj->getStrokeJoin()); + } + + static void setStrokeJoin(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jint joinHandle) { + Paint* obj = reinterpret_cast(objHandle); + Paint::Join join = (Paint::Join) joinHandle; + obj->setStrokeJoin(join); + } + + static jboolean getFillPath(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong srcHandle, jlong dstHandle) { + Paint* obj = reinterpret_cast(objHandle); + SkPath* src = reinterpret_cast(srcHandle); + SkPath* dst = reinterpret_cast(dstHandle); + return obj->getFillPath(*src, dst) ? JNI_TRUE : JNI_FALSE; + } + + static jlong setShader(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong shaderHandle) { + Paint* obj = reinterpret_cast(objHandle); + SkShader* shader = reinterpret_cast(shaderHandle); + obj->setShader(sk_ref_sp(shader)); + return reinterpret_cast(obj->getShader()); + } + + static jlong setColorFilter(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong filterHandle) { + Paint* obj = reinterpret_cast(objHandle); + SkColorFilter* filter = reinterpret_cast(filterHandle); + obj->setColorFilter(sk_ref_sp(filter)); + return reinterpret_cast(obj->getColorFilter()); + } + + static void setXfermode(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint xfermodeHandle) { + // validate that the Java enum values match our expectations + static_assert(0 == static_cast(SkBlendMode::kClear), "xfermode_mismatch"); + static_assert(1 == static_cast(SkBlendMode::kSrc), "xfermode_mismatch"); + static_assert(2 == static_cast(SkBlendMode::kDst), "xfermode_mismatch"); + static_assert(3 == static_cast(SkBlendMode::kSrcOver), "xfermode_mismatch"); + static_assert(4 == static_cast(SkBlendMode::kDstOver), "xfermode_mismatch"); + static_assert(5 == static_cast(SkBlendMode::kSrcIn), "xfermode_mismatch"); + static_assert(6 == static_cast(SkBlendMode::kDstIn), "xfermode_mismatch"); + static_assert(7 == static_cast(SkBlendMode::kSrcOut), "xfermode_mismatch"); + static_assert(8 == static_cast(SkBlendMode::kDstOut), "xfermode_mismatch"); + static_assert(9 == static_cast(SkBlendMode::kSrcATop), "xfermode_mismatch"); + static_assert(10 == static_cast(SkBlendMode::kDstATop), "xfermode_mismatch"); + static_assert(11 == static_cast(SkBlendMode::kXor), "xfermode_mismatch"); + static_assert(12 == static_cast(SkBlendMode::kPlus), "xfermode_mismatch"); + static_assert(13 == static_cast(SkBlendMode::kModulate), "xfermode_mismatch"); + static_assert(14 == static_cast(SkBlendMode::kScreen), "xfermode_mismatch"); + static_assert(15 == static_cast(SkBlendMode::kOverlay), "xfermode_mismatch"); + static_assert(16 == static_cast(SkBlendMode::kDarken), "xfermode_mismatch"); + static_assert(17 == static_cast(SkBlendMode::kLighten), "xfermode_mismatch"); + static_assert(18 == static_cast(SkBlendMode::kColorDodge), "xfermode mismatch"); + static_assert(19 == static_cast(SkBlendMode::kColorBurn), "xfermode mismatch"); + static_assert(20 == static_cast(SkBlendMode::kHardLight), "xfermode mismatch"); + static_assert(21 == static_cast(SkBlendMode::kSoftLight), "xfermode mismatch"); + static_assert(22 == static_cast(SkBlendMode::kDifference), "xfermode mismatch"); + static_assert(23 == static_cast(SkBlendMode::kExclusion), "xfermode mismatch"); + static_assert(24 == static_cast(SkBlendMode::kMultiply), "xfermode mismatch"); + static_assert(25 == static_cast(SkBlendMode::kHue), "xfermode mismatch"); + static_assert(26 == static_cast(SkBlendMode::kSaturation), "xfermode mismatch"); + static_assert(27 == static_cast(SkBlendMode::kColor), "xfermode mismatch"); + static_assert(28 == static_cast(SkBlendMode::kLuminosity), "xfermode mismatch"); + + SkBlendMode mode = static_cast(xfermodeHandle); + Paint* paint = reinterpret_cast(paintHandle); + paint->setBlendMode(mode); + } + + static jlong setPathEffect(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong effectHandle) { + Paint* obj = reinterpret_cast(objHandle); + SkPathEffect* effect = reinterpret_cast(effectHandle); + obj->setPathEffect(sk_ref_sp(effect)); + return reinterpret_cast(obj->getPathEffect()); + } + + static jlong setMaskFilter(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong maskfilterHandle) { + Paint* obj = reinterpret_cast(objHandle); + SkMaskFilter* maskfilter = reinterpret_cast(maskfilterHandle); + obj->setMaskFilter(sk_ref_sp(maskfilter)); + return reinterpret_cast(obj->getMaskFilter()); + } + + static void setTypeface(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong typefaceHandle) { + Paint* paint = reinterpret_cast(objHandle); + paint->setAndroidTypeface(reinterpret_cast(typefaceHandle)); + } + + static jint getTextAlign(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) { + Paint* obj = reinterpret_cast(objHandle); + return static_cast(obj->getTextAlign()); + } + + static void setTextAlign(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jint alignHandle) { + Paint* obj = reinterpret_cast(objHandle); + Paint::Align align = static_cast(alignHandle); + obj->setTextAlign(align); + } + + static void setTextLocalesByMinikinLocaleListId(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, + jint minikinLocaleListId) { + Paint* obj = reinterpret_cast(objHandle); + obj->setMinikinLocaleListId(minikinLocaleListId); + } + + static jboolean isElegantTextHeight(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { + Paint* obj = reinterpret_cast(paintHandle); + return obj->getFamilyVariant() == minikin::FamilyVariant::ELEGANT; + } + + static void setElegantTextHeight(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean aa) { + Paint* obj = reinterpret_cast(paintHandle); + obj->setFamilyVariant( + aa ? minikin::FamilyVariant::ELEGANT : minikin::FamilyVariant::DEFAULT); + } + + static jfloat getTextSize(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { + return SkScalarToFloat(reinterpret_cast(paintHandle)->getSkFont().getSize()); + } + + static void setTextSize(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jfloat textSize) { + if (textSize >= 0) { + reinterpret_cast(paintHandle)->getSkFont().setSize(textSize); + } + } + + static jfloat getTextScaleX(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { + return SkScalarToFloat(reinterpret_cast(paintHandle)->getSkFont().getScaleX()); + } + + static void setTextScaleX(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jfloat scaleX) { + reinterpret_cast(paintHandle)->getSkFont().setScaleX(scaleX); + } + + static jfloat getTextSkewX(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { + return SkScalarToFloat(reinterpret_cast(paintHandle)->getSkFont().getSkewX()); + } + + static void setTextSkewX(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jfloat skewX) { + reinterpret_cast(paintHandle)->getSkFont().setSkewX(skewX); + } + + static jfloat getLetterSpacing(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { + Paint* paint = reinterpret_cast(paintHandle); + return paint->getLetterSpacing(); + } + + static void setLetterSpacing(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jfloat letterSpacing) { + Paint* paint = reinterpret_cast(paintHandle); + paint->setLetterSpacing(letterSpacing); + } + + static jfloat getWordSpacing(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { + Paint* paint = reinterpret_cast(paintHandle); + return paint->getWordSpacing(); + } + + static void setWordSpacing(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jfloat wordSpacing) { + Paint* paint = reinterpret_cast(paintHandle); + paint->setWordSpacing(wordSpacing); + } + + static jint getStartHyphenEdit(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint hyphen) { + Paint* paint = reinterpret_cast(paintHandle); + return static_cast(paint->getStartHyphenEdit()); + } + + static jint getEndHyphenEdit(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint hyphen) { + Paint* paint = reinterpret_cast(paintHandle); + return static_cast(paint->getEndHyphenEdit()); + } + + static void setStartHyphenEdit(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint hyphen) { + Paint* paint = reinterpret_cast(paintHandle); + paint->setStartHyphenEdit((uint32_t)hyphen); + } + + static void setEndHyphenEdit(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint hyphen) { + Paint* paint = reinterpret_cast(paintHandle); + paint->setEndHyphenEdit((uint32_t)hyphen); + } + + static jfloat ascent(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { + SkFontMetrics metrics; + getMetricsInternal(paintHandle, &metrics); + return SkScalarToFloat(metrics.fAscent); + } + + static jfloat descent(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { + SkFontMetrics metrics; + getMetricsInternal(paintHandle, &metrics); + return SkScalarToFloat(metrics.fDescent); + } + + static jfloat getUnderlinePosition(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { + SkFontMetrics metrics; + getMetricsInternal(paintHandle, &metrics); + SkScalar position; + if (metrics.hasUnderlinePosition(&position)) { + return SkScalarToFloat(position); + } else { + const SkScalar textSize = reinterpret_cast(paintHandle)->getSkFont().getSize(); + return SkScalarToFloat(Paint::kStdUnderline_Top * textSize); + } + } + + static jfloat getUnderlineThickness(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { + SkFontMetrics metrics; + getMetricsInternal(paintHandle, &metrics); + SkScalar thickness; + if (metrics.hasUnderlineThickness(&thickness)) { + return SkScalarToFloat(thickness); + } else { + const SkScalar textSize = reinterpret_cast(paintHandle)->getSkFont().getSize(); + return SkScalarToFloat(Paint::kStdUnderline_Thickness * textSize); + } + } + + static jfloat getStrikeThruPosition(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { + const SkScalar textSize = reinterpret_cast(paintHandle)->getSkFont().getSize(); + return SkScalarToFloat(Paint::kStdStrikeThru_Top * textSize); + } + + static jfloat getStrikeThruThickness(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { + const SkScalar textSize = reinterpret_cast(paintHandle)->getSkFont().getSize(); + return SkScalarToFloat(Paint::kStdStrikeThru_Thickness * textSize); + } + + static void setShadowLayer(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jfloat radius, + jfloat dx, jfloat dy, jlong colorSpaceHandle, + jlong colorLong) { + SkColor4f color = GraphicsJNI::convertColorLong(colorLong); + sk_sp cs = GraphicsJNI::getNativeColorSpace(colorSpaceHandle); + + Paint* paint = reinterpret_cast(paintHandle); + if (radius <= 0) { + paint->setLooper(nullptr); + } + else { + SkScalar sigma = android::uirenderer::Blur::convertRadiusToSigma(radius); + paint->setLooper(SkBlurDrawLooper::Make(color, cs.get(), sigma, dx, dy)); + } + } + + static jboolean hasShadowLayer(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { + Paint* paint = reinterpret_cast(paintHandle); + return paint->getLooper() && paint->getLooper()->asABlurShadow(nullptr); + } + + static jboolean equalsForTextMeasurement(CRITICAL_JNI_PARAMS_COMMA jlong lPaint, jlong rPaint) { + if (lPaint == rPaint) { + return true; + } + Paint* leftPaint = reinterpret_cast(lPaint); + Paint* rightPaint = reinterpret_cast(rPaint); + + const Typeface* leftTypeface = Typeface::resolveDefault(leftPaint->getAndroidTypeface()); + const Typeface* rightTypeface = Typeface::resolveDefault(rightPaint->getAndroidTypeface()); + minikin::MinikinPaint leftMinikinPaint + = MinikinUtils::prepareMinikinPaint(leftPaint, leftTypeface); + minikin::MinikinPaint rightMinikinPaint + = MinikinUtils::prepareMinikinPaint(rightPaint, rightTypeface); + + return leftMinikinPaint == rightMinikinPaint; + } + +}; // namespace PaintGlue + +static const JNINativeMethod methods[] = { + {"nGetNativeFinalizer", "()J", (void*) PaintGlue::getNativeFinalizer}, + {"nInit","()J", (void*) PaintGlue::init}, + {"nInitWithPaint","(J)J", (void*) PaintGlue::initWithPaint}, + {"nBreakText","(J[CIIFI[F)I", (void*) PaintGlue::breakTextC}, + {"nBreakText","(JLjava/lang/String;ZFI[F)I", (void*) PaintGlue::breakTextS}, + {"nGetTextAdvances","(J[CIIIII[FI)F", + (void*) PaintGlue::getTextAdvances___CIIIII_FI}, + {"nGetTextAdvances","(JLjava/lang/String;IIIII[FI)F", + (void*) PaintGlue::getTextAdvances__StringIIIII_FI}, + + {"nGetTextRunCursor", "(J[CIIIII)I", (void*) PaintGlue::getTextRunCursor___C}, + {"nGetTextRunCursor", "(JLjava/lang/String;IIIII)I", + (void*) PaintGlue::getTextRunCursor__String}, + {"nGetTextPath", "(JI[CIIFFJ)V", (void*) PaintGlue::getTextPath___C}, + {"nGetTextPath", "(JILjava/lang/String;IIFFJ)V", (void*) PaintGlue::getTextPath__String}, + {"nGetStringBounds", "(JLjava/lang/String;IIILandroid/graphics/Rect;)V", + (void*) PaintGlue::getStringBounds }, + {"nGetCharArrayBounds", "(J[CIIILandroid/graphics/Rect;)V", + (void*) PaintGlue::getCharArrayBounds }, + {"nHasGlyph", "(JILjava/lang/String;)Z", (void*) PaintGlue::hasGlyph }, + {"nGetRunAdvance", "(J[CIIIIZI)F", (void*) PaintGlue::getRunAdvance___CIIIIZI_F}, + {"nGetOffsetForAdvance", "(J[CIIIIZF)I", + (void*) PaintGlue::getOffsetForAdvance___CIIIIZF_I}, + + // --------------- @FastNative ---------------------- + + {"nSetTextLocales","(JLjava/lang/String;)I", (void*) PaintGlue::setTextLocales}, + {"nSetFontFeatureSettings","(JLjava/lang/String;)V", + (void*) PaintGlue::setFontFeatureSettings}, + {"nGetFontMetrics", "(JLandroid/graphics/Paint$FontMetrics;)F", + (void*)PaintGlue::getFontMetrics}, + {"nGetFontMetricsInt", "(JLandroid/graphics/Paint$FontMetricsInt;)I", + (void*)PaintGlue::getFontMetricsInt}, + + // --------------- @CriticalNative ------------------ + + {"nReset","(J)V", (void*) PaintGlue::reset}, + {"nSet","(JJ)V", (void*) PaintGlue::assign}, + {"nGetFlags","(J)I", (void*) PaintGlue::getFlags}, + {"nSetFlags","(JI)V", (void*) PaintGlue::setFlags}, + {"nGetHinting","(J)I", (void*) PaintGlue::getHinting}, + {"nSetHinting","(JI)V", (void*) PaintGlue::setHinting}, + {"nSetAntiAlias","(JZ)V", (void*) PaintGlue::setAntiAlias}, + {"nSetSubpixelText","(JZ)V", (void*) PaintGlue::setSubpixelText}, + {"nSetLinearText","(JZ)V", (void*) PaintGlue::setLinearText}, + {"nSetUnderlineText","(JZ)V", (void*) PaintGlue::setUnderlineText}, + {"nSetStrikeThruText","(JZ)V", (void*) PaintGlue::setStrikeThruText}, + {"nSetFakeBoldText","(JZ)V", (void*) PaintGlue::setFakeBoldText}, + {"nSetFilterBitmap","(JZ)V", (void*) PaintGlue::setFilterBitmap}, + {"nSetDither","(JZ)V", (void*) PaintGlue::setDither}, + {"nGetStyle","(J)I", (void*) PaintGlue::getStyle}, + {"nSetStyle","(JI)V", (void*) PaintGlue::setStyle}, + {"nSetColor","(JI)V", (void*) PaintGlue::setColor}, + {"nSetColor","(JJJ)V", (void*) PaintGlue::setColorLong}, + {"nSetAlpha","(JI)V", (void*) PaintGlue::setAlpha}, + {"nGetStrokeWidth","(J)F", (void*) PaintGlue::getStrokeWidth}, + {"nSetStrokeWidth","(JF)V", (void*) PaintGlue::setStrokeWidth}, + {"nGetStrokeMiter","(J)F", (void*) PaintGlue::getStrokeMiter}, + {"nSetStrokeMiter","(JF)V", (void*) PaintGlue::setStrokeMiter}, + {"nGetStrokeCap","(J)I", (void*) PaintGlue::getStrokeCap}, + {"nSetStrokeCap","(JI)V", (void*) PaintGlue::setStrokeCap}, + {"nGetStrokeJoin","(J)I", (void*) PaintGlue::getStrokeJoin}, + {"nSetStrokeJoin","(JI)V", (void*) PaintGlue::setStrokeJoin}, + {"nGetFillPath","(JJJ)Z", (void*) PaintGlue::getFillPath}, + {"nSetShader","(JJ)J", (void*) PaintGlue::setShader}, + {"nSetColorFilter","(JJ)J", (void*) PaintGlue::setColorFilter}, + {"nSetXfermode","(JI)V", (void*) PaintGlue::setXfermode}, + {"nSetPathEffect","(JJ)J", (void*) PaintGlue::setPathEffect}, + {"nSetMaskFilter","(JJ)J", (void*) PaintGlue::setMaskFilter}, + {"nSetTypeface","(JJ)V", (void*) PaintGlue::setTypeface}, + {"nGetTextAlign","(J)I", (void*) PaintGlue::getTextAlign}, + {"nSetTextAlign","(JI)V", (void*) PaintGlue::setTextAlign}, + {"nSetTextLocalesByMinikinLocaleListId","(JI)V", + (void*) PaintGlue::setTextLocalesByMinikinLocaleListId}, + {"nIsElegantTextHeight","(J)Z", (void*) PaintGlue::isElegantTextHeight}, + {"nSetElegantTextHeight","(JZ)V", (void*) PaintGlue::setElegantTextHeight}, + {"nGetTextSize","(J)F", (void*) PaintGlue::getTextSize}, + {"nSetTextSize","(JF)V", (void*) PaintGlue::setTextSize}, + {"nGetTextScaleX","(J)F", (void*) PaintGlue::getTextScaleX}, + {"nSetTextScaleX","(JF)V", (void*) PaintGlue::setTextScaleX}, + {"nGetTextSkewX","(J)F", (void*) PaintGlue::getTextSkewX}, + {"nSetTextSkewX","(JF)V", (void*) PaintGlue::setTextSkewX}, + {"nGetLetterSpacing","(J)F", (void*) PaintGlue::getLetterSpacing}, + {"nSetLetterSpacing","(JF)V", (void*) PaintGlue::setLetterSpacing}, + {"nGetWordSpacing","(J)F", (void*) PaintGlue::getWordSpacing}, + {"nSetWordSpacing","(JF)V", (void*) PaintGlue::setWordSpacing}, + {"nGetStartHyphenEdit", "(J)I", (void*) PaintGlue::getStartHyphenEdit}, + {"nGetEndHyphenEdit", "(J)I", (void*) PaintGlue::getEndHyphenEdit}, + {"nSetStartHyphenEdit", "(JI)V", (void*) PaintGlue::setStartHyphenEdit}, + {"nSetEndHyphenEdit", "(JI)V", (void*) PaintGlue::setEndHyphenEdit}, + {"nAscent","(J)F", (void*) PaintGlue::ascent}, + {"nDescent","(J)F", (void*) PaintGlue::descent}, + {"nGetUnderlinePosition","(J)F", (void*) PaintGlue::getUnderlinePosition}, + {"nGetUnderlineThickness","(J)F", (void*) PaintGlue::getUnderlineThickness}, + {"nGetStrikeThruPosition","(J)F", (void*) PaintGlue::getStrikeThruPosition}, + {"nGetStrikeThruThickness","(J)F", (void*) PaintGlue::getStrikeThruThickness}, + {"nSetShadowLayer", "(JFFFJJ)V", (void*)PaintGlue::setShadowLayer}, + {"nHasShadowLayer", "(J)Z", (void*)PaintGlue::hasShadowLayer}, + {"nEqualsForTextMeasurement", "(JJ)Z", (void*)PaintGlue::equalsForTextMeasurement}, +}; + +int register_android_graphics_Paint(JNIEnv* env) { + gFontMetrics_class = FindClassOrDie(env, "android/graphics/Paint$FontMetrics"); + gFontMetrics_class = MakeGlobalRefOrDie(env, gFontMetrics_class); + + gFontMetrics_fieldID.top = GetFieldIDOrDie(env, gFontMetrics_class, "top", "F"); + gFontMetrics_fieldID.ascent = GetFieldIDOrDie(env, gFontMetrics_class, "ascent", "F"); + gFontMetrics_fieldID.descent = GetFieldIDOrDie(env, gFontMetrics_class, "descent", "F"); + gFontMetrics_fieldID.bottom = GetFieldIDOrDie(env, gFontMetrics_class, "bottom", "F"); + gFontMetrics_fieldID.leading = GetFieldIDOrDie(env, gFontMetrics_class, "leading", "F"); + + gFontMetricsInt_class = FindClassOrDie(env, "android/graphics/Paint$FontMetricsInt"); + gFontMetricsInt_class = MakeGlobalRefOrDie(env, gFontMetricsInt_class); + + gFontMetricsInt_fieldID.top = GetFieldIDOrDie(env, gFontMetricsInt_class, "top", "I"); + gFontMetricsInt_fieldID.ascent = GetFieldIDOrDie(env, gFontMetricsInt_class, "ascent", "I"); + gFontMetricsInt_fieldID.descent = GetFieldIDOrDie(env, gFontMetricsInt_class, "descent", "I"); + gFontMetricsInt_fieldID.bottom = GetFieldIDOrDie(env, gFontMetricsInt_class, "bottom", "I"); + gFontMetricsInt_fieldID.leading = GetFieldIDOrDie(env, gFontMetricsInt_class, "leading", "I"); + + return RegisterMethodsOrDie(env, "android/graphics/Paint", methods, NELEM(methods)); +} + +} diff --git a/libs/hwui/jni/PaintFilter.cpp b/libs/hwui/jni/PaintFilter.cpp new file mode 100644 index 000000000000..4fe9140572d3 --- /dev/null +++ b/libs/hwui/jni/PaintFilter.cpp @@ -0,0 +1,84 @@ +/* libs/android_runtime/android/graphics/ColorFilter.cpp +** +** Copyright 2006, 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 "jni.h" +#include "GraphicsJNI.h" +#include + +#include "core_jni_helpers.h" + +#include "hwui/Paint.h" +#include "hwui/PaintFilter.h" +#include "SkPaint.h" + +namespace android { + +class PaintFlagsFilter : public PaintFilter { +public: + PaintFlagsFilter(uint32_t clearFlags, uint32_t setFlags) { + fClearFlags = static_cast(clearFlags); + fSetFlags = static_cast(setFlags); + } + void filter(SkPaint* paint) override { + uint32_t flags = Paint::GetSkPaintJavaFlags(*paint); + Paint::SetSkPaintJavaFlags(paint, (flags & ~fClearFlags) | fSetFlags); + } + void filterFullPaint(Paint* paint) override { + paint->setJavaFlags((paint->getJavaFlags() & ~fClearFlags) | fSetFlags); + } + +private: + uint16_t fClearFlags; + uint16_t fSetFlags; +}; + +class PaintFilterGlue { +public: + + static void finalizer(JNIEnv* env, jobject clazz, jlong objHandle) { + PaintFilter* obj = reinterpret_cast(objHandle); + SkSafeUnref(obj); + } + + static jlong CreatePaintFlagsFilter(JNIEnv* env, jobject clazz, + jint clearFlags, jint setFlags) { + PaintFilter* filter = nullptr; + if (clearFlags | setFlags) { + filter = new PaintFlagsFilter(clearFlags, setFlags); + } + return reinterpret_cast(filter); + } +}; + +static const JNINativeMethod drawfilter_methods[] = { + {"nativeDestructor", "(J)V", (void*) PaintFilterGlue::finalizer} +}; + +static const JNINativeMethod paintflags_methods[] = { + {"nativeConstructor","(II)J", (void*) PaintFilterGlue::CreatePaintFlagsFilter} +}; + +int register_android_graphics_DrawFilter(JNIEnv* env) { + int result = RegisterMethodsOrDie(env, "android/graphics/DrawFilter", drawfilter_methods, + NELEM(drawfilter_methods)); + result |= RegisterMethodsOrDie(env, "android/graphics/PaintFlagsDrawFilter", paintflags_methods, + NELEM(paintflags_methods)); + + return 0; +} + +} diff --git a/libs/hwui/jni/Path.cpp b/libs/hwui/jni/Path.cpp new file mode 100644 index 000000000000..481445258e3c --- /dev/null +++ b/libs/hwui/jni/Path.cpp @@ -0,0 +1,562 @@ +/* libs/android_runtime/android/graphics/Path.cpp +** +** Copyright 2006, 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. +*/ + +// This file was generated from the C++ include file: SkPath.h +// Any changes made to this file will be discarded by the build. +// To change this file, either edit the include, or device/tools/gluemaker/main.cpp, +// or one of the auxilary file specifications in device/tools/gluemaker. + +#include "jni.h" +#include "GraphicsJNI.h" +#include "core_jni_helpers.h" + +#include "SkPath.h" +#include "SkPathOps.h" +#include "SkGeometry.h" // WARNING: Internal Skia Header + +#include +#include + +namespace android { + +class SkPathGlue { +public: + + static void finalizer(SkPath* obj) { + delete obj; + } + + // ---------------- Regular JNI ----------------------------- + + static jlong init(JNIEnv* env, jclass clazz) { + return reinterpret_cast(new SkPath()); + } + + static jlong init_Path(JNIEnv* env, jclass clazz, jlong valHandle) { + SkPath* val = reinterpret_cast(valHandle); + return reinterpret_cast(new SkPath(*val)); + } + + static jlong getFinalizer(JNIEnv* env, jclass clazz) { + return static_cast(reinterpret_cast(&finalizer)); + } + + static void set(JNIEnv* env, jclass clazz, jlong dstHandle, jlong srcHandle) { + SkPath* dst = reinterpret_cast(dstHandle); + const SkPath* src = reinterpret_cast(srcHandle); + *dst = *src; + } + + static void computeBounds(JNIEnv* env, jclass clazz, jlong objHandle, jobject jbounds) { + SkPath* obj = reinterpret_cast(objHandle); + const SkRect& bounds = obj->getBounds(); + GraphicsJNI::rect_to_jrectf(bounds, env, jbounds); + } + + static void incReserve(JNIEnv* env, jclass clazz, jlong objHandle, jint extraPtCount) { + SkPath* obj = reinterpret_cast(objHandle); + obj->incReserve(extraPtCount); + } + + static void moveTo__FF(JNIEnv* env, jclass clazz, jlong objHandle, jfloat x, jfloat y) { + SkPath* obj = reinterpret_cast(objHandle); + obj->moveTo(x, y); + } + + static void rMoveTo(JNIEnv* env, jclass clazz, jlong objHandle, jfloat dx, jfloat dy) { + SkPath* obj = reinterpret_cast(objHandle); + obj->rMoveTo(dx, dy); + } + + static void lineTo__FF(JNIEnv* env, jclass clazz, jlong objHandle, jfloat x, jfloat y) { + SkPath* obj = reinterpret_cast(objHandle); + obj->lineTo(x, y); + } + + static void rLineTo(JNIEnv* env, jclass clazz, jlong objHandle, jfloat dx, jfloat dy) { + SkPath* obj = reinterpret_cast(objHandle); + obj->rLineTo(dx, dy); + } + + static void quadTo__FFFF(JNIEnv* env, jclass clazz, jlong objHandle, jfloat x1, jfloat y1, + jfloat x2, jfloat y2) { + SkPath* obj = reinterpret_cast(objHandle); + obj->quadTo(x1, y1, x2, y2); + } + + static void rQuadTo(JNIEnv* env, jclass clazz, jlong objHandle, jfloat dx1, jfloat dy1, + jfloat dx2, jfloat dy2) { + SkPath* obj = reinterpret_cast(objHandle); + obj->rQuadTo(dx1, dy1, dx2, dy2); + } + + static void cubicTo__FFFFFF(JNIEnv* env, jclass clazz, jlong objHandle, jfloat x1, jfloat y1, + jfloat x2, jfloat y2, jfloat x3, jfloat y3) { + SkPath* obj = reinterpret_cast(objHandle); + obj->cubicTo(x1, y1, x2, y2, x3, y3); + } + + static void rCubicTo(JNIEnv* env, jclass clazz, jlong objHandle, jfloat x1, jfloat y1, + jfloat x2, jfloat y2, jfloat x3, jfloat y3) { + SkPath* obj = reinterpret_cast(objHandle); + obj->rCubicTo(x1, y1, x2, y2, x3, y3); + } + + static void arcTo(JNIEnv* env, jclass clazz, jlong objHandle, jfloat left, jfloat top, + jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle, + jboolean forceMoveTo) { + SkPath* obj = reinterpret_cast(objHandle); + SkRect oval = SkRect::MakeLTRB(left, top, right, bottom); + obj->arcTo(oval, startAngle, sweepAngle, forceMoveTo); + } + + static void close(JNIEnv* env, jclass clazz, jlong objHandle) { + SkPath* obj = reinterpret_cast(objHandle); + obj->close(); + } + + static void addRect(JNIEnv* env, jclass clazz, jlong objHandle, + jfloat left, jfloat top, jfloat right, jfloat bottom, jint dirHandle) { + SkPath* obj = reinterpret_cast(objHandle); + SkPathDirection dir = static_cast(dirHandle); + obj->addRect(left, top, right, bottom, dir); + } + + static void addOval(JNIEnv* env, jclass clazz, jlong objHandle, + jfloat left, jfloat top, jfloat right, jfloat bottom, jint dirHandle) { + SkPath* obj = reinterpret_cast(objHandle); + SkPathDirection dir = static_cast(dirHandle); + SkRect oval = SkRect::MakeLTRB(left, top, right, bottom); + obj->addOval(oval, dir); + } + + static void addCircle(JNIEnv* env, jclass clazz, jlong objHandle, jfloat x, jfloat y, + jfloat radius, jint dirHandle) { + SkPath* obj = reinterpret_cast(objHandle); + SkPathDirection dir = static_cast(dirHandle); + obj->addCircle(x, y, radius, dir); + } + + static void addArc(JNIEnv* env, jclass clazz, jlong objHandle, jfloat left, jfloat top, + jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle) { + SkRect oval = SkRect::MakeLTRB(left, top, right, bottom); + SkPath* obj = reinterpret_cast(objHandle); + obj->addArc(oval, startAngle, sweepAngle); + } + + static void addRoundRectXY(JNIEnv* env, jclass clazz, jlong objHandle, jfloat left, jfloat top, + jfloat right, jfloat bottom, jfloat rx, jfloat ry, jint dirHandle) { + SkRect rect = SkRect::MakeLTRB(left, top, right, bottom); + SkPath* obj = reinterpret_cast(objHandle); + SkPathDirection dir = static_cast(dirHandle); + obj->addRoundRect(rect, rx, ry, dir); + } + + static void addRoundRect8(JNIEnv* env, jclass clazz, jlong objHandle, jfloat left, jfloat top, + jfloat right, jfloat bottom, jfloatArray array, jint dirHandle) { + SkRect rect = SkRect::MakeLTRB(left, top, right, bottom); + SkPath* obj = reinterpret_cast(objHandle); + SkPathDirection dir = static_cast(dirHandle); + AutoJavaFloatArray afa(env, array, 8); +#ifdef SK_SCALAR_IS_FLOAT + const float* src = afa.ptr(); +#else + #error Need to convert float array to SkScalar array before calling the following function. +#endif + obj->addRoundRect(rect, src, dir); + } + + static void addPath__PathFF(JNIEnv* env, jclass clazz, jlong objHandle, jlong srcHandle, + jfloat dx, jfloat dy) { + SkPath* obj = reinterpret_cast(objHandle); + SkPath* src = reinterpret_cast(srcHandle); + obj->addPath(*src, dx, dy); + } + + static void addPath__Path(JNIEnv* env, jclass clazz, jlong objHandle, jlong srcHandle) { + SkPath* obj = reinterpret_cast(objHandle); + SkPath* src = reinterpret_cast(srcHandle); + obj->addPath(*src); + } + + static void addPath__PathMatrix(JNIEnv* env, jclass clazz, jlong objHandle, jlong srcHandle, + jlong matrixHandle) { + SkPath* obj = reinterpret_cast(objHandle); + SkPath* src = reinterpret_cast(srcHandle); + SkMatrix* matrix = reinterpret_cast(matrixHandle); + obj->addPath(*src, *matrix); + } + + static void offset__FF(JNIEnv* env, jclass clazz, jlong objHandle, jfloat dx, jfloat dy) { + SkPath* obj = reinterpret_cast(objHandle); + obj->offset(dx, dy); + } + + static void setLastPoint(JNIEnv* env, jclass clazz, jlong objHandle, jfloat dx, jfloat dy) { + SkPath* obj = reinterpret_cast(objHandle); + obj->setLastPt(dx, dy); + } + + static void transform__MatrixPath(JNIEnv* env, jclass clazz, jlong objHandle, jlong matrixHandle, + jlong dstHandle) { + SkPath* obj = reinterpret_cast(objHandle); + SkMatrix* matrix = reinterpret_cast(matrixHandle); + SkPath* dst = reinterpret_cast(dstHandle); + obj->transform(*matrix, dst); + } + + static void transform__Matrix(JNIEnv* env, jclass clazz, jlong objHandle, jlong matrixHandle) { + SkPath* obj = reinterpret_cast(objHandle); + SkMatrix* matrix = reinterpret_cast(matrixHandle); + obj->transform(*matrix); + } + + static jboolean op(JNIEnv* env, jclass clazz, jlong p1Handle, jlong p2Handle, jint opHandle, + jlong rHandle) { + SkPath* p1 = reinterpret_cast(p1Handle); + SkPath* p2 = reinterpret_cast(p2Handle); + SkPathOp op = static_cast(opHandle); + SkPath* r = reinterpret_cast(rHandle); + return Op(*p1, *p2, op, r); + } + + typedef SkPoint (*bezierCalculation)(float t, const SkPoint* points); + + static void addMove(std::vector& segmentPoints, std::vector& lengths, + const SkPoint& point) { + float length = 0; + if (!lengths.empty()) { + length = lengths.back(); + } + segmentPoints.push_back(point); + lengths.push_back(length); + } + + static void addLine(std::vector& segmentPoints, std::vector& lengths, + const SkPoint& toPoint) { + if (segmentPoints.empty()) { + segmentPoints.push_back(SkPoint::Make(0, 0)); + lengths.push_back(0); + } else if (segmentPoints.back() == toPoint) { + return; // Empty line + } + float length = lengths.back() + SkPoint::Distance(segmentPoints.back(), toPoint); + segmentPoints.push_back(toPoint); + lengths.push_back(length); + } + + static float cubicCoordinateCalculation(float t, float p0, float p1, float p2, float p3) { + float oneMinusT = 1 - t; + float oneMinusTSquared = oneMinusT * oneMinusT; + float oneMinusTCubed = oneMinusTSquared * oneMinusT; + float tSquared = t * t; + float tCubed = tSquared * t; + return (oneMinusTCubed * p0) + (3 * oneMinusTSquared * t * p1) + + (3 * oneMinusT * tSquared * p2) + (tCubed * p3); + } + + static SkPoint cubicBezierCalculation(float t, const SkPoint* points) { + float x = cubicCoordinateCalculation(t, points[0].x(), points[1].x(), + points[2].x(), points[3].x()); + float y = cubicCoordinateCalculation(t, points[0].y(), points[1].y(), + points[2].y(), points[3].y()); + return SkPoint::Make(x, y); + } + + static float quadraticCoordinateCalculation(float t, float p0, float p1, float p2) { + float oneMinusT = 1 - t; + return oneMinusT * ((oneMinusT * p0) + (t * p1)) + t * ((oneMinusT * p1) + (t * p2)); + } + + static SkPoint quadraticBezierCalculation(float t, const SkPoint* points) { + float x = quadraticCoordinateCalculation(t, points[0].x(), points[1].x(), points[2].x()); + float y = quadraticCoordinateCalculation(t, points[0].y(), points[1].y(), points[2].y()); + return SkPoint::Make(x, y); + } + + // Subdivide a section of the Bezier curve, set the mid-point and the mid-t value. + // Returns true if further subdivision is necessary as defined by errorSquared. + static bool subdividePoints(const SkPoint* points, bezierCalculation bezierFunction, + float t0, const SkPoint &p0, float t1, const SkPoint &p1, + float& midT, SkPoint &midPoint, float errorSquared) { + midT = (t1 + t0) / 2; + float midX = (p1.x() + p0.x()) / 2; + float midY = (p1.y() + p0.y()) / 2; + + midPoint = (*bezierFunction)(midT, points); + float xError = midPoint.x() - midX; + float yError = midPoint.y() - midY; + float midErrorSquared = (xError * xError) + (yError * yError); + return midErrorSquared > errorSquared; + } + + // Divides Bezier curves until linear interpolation is very close to accurate, using + // errorSquared as a metric. Cubic Bezier curves can have an inflection point that improperly + // short-circuit subdivision. If you imagine an S shape, the top and bottom points being the + // starting and end points, linear interpolation would mark the center where the curve places + // the point. It is clearly not the case that we can linearly interpolate at that point. + // doubleCheckDivision forces a second examination between subdivisions to ensure that linear + // interpolation works. + static void addBezier(const SkPoint* points, + bezierCalculation bezierFunction, std::vector& segmentPoints, + std::vector& lengths, float errorSquared, bool doubleCheckDivision) { + typedef std::map PointMap; + PointMap tToPoint; + + tToPoint[0] = (*bezierFunction)(0, points); + tToPoint[1] = (*bezierFunction)(1, points); + + PointMap::iterator iter = tToPoint.begin(); + PointMap::iterator next = iter; + ++next; + while (next != tToPoint.end()) { + bool needsSubdivision = true; + SkPoint midPoint; + do { + float midT; + needsSubdivision = subdividePoints(points, bezierFunction, iter->first, + iter->second, next->first, next->second, midT, midPoint, errorSquared); + if (!needsSubdivision && doubleCheckDivision) { + SkPoint quarterPoint; + float quarterT; + needsSubdivision = subdividePoints(points, bezierFunction, iter->first, + iter->second, midT, midPoint, quarterT, quarterPoint, errorSquared); + if (needsSubdivision) { + // Found an inflection point. No need to double-check. + doubleCheckDivision = false; + } + } + if (needsSubdivision) { + next = tToPoint.insert(iter, PointMap::value_type(midT, midPoint)); + } + } while (needsSubdivision); + iter = next; + next++; + } + + // Now that each division can use linear interpolation with less than the allowed error + for (iter = tToPoint.begin(); iter != tToPoint.end(); ++iter) { + addLine(segmentPoints, lengths, iter->second); + } + } + + static void createVerbSegments(const SkPath::Iter& pathIter, SkPath::Verb verb, + const SkPoint* points, std::vector& segmentPoints, + std::vector& lengths, float errorSquared, float errorConic) { + switch (verb) { + case SkPath::kMove_Verb: + addMove(segmentPoints, lengths, points[0]); + break; + case SkPath::kClose_Verb: + addLine(segmentPoints, lengths, points[0]); + break; + case SkPath::kLine_Verb: + addLine(segmentPoints, lengths, points[1]); + break; + case SkPath::kQuad_Verb: + addBezier(points, quadraticBezierCalculation, segmentPoints, lengths, + errorSquared, false); + break; + case SkPath::kCubic_Verb: + addBezier(points, cubicBezierCalculation, segmentPoints, lengths, + errorSquared, true); + break; + case SkPath::kConic_Verb: { + SkAutoConicToQuads converter; + const SkPoint* quads = converter.computeQuads( + points, pathIter.conicWeight(), errorConic); + for (int i = 0; i < converter.countQuads(); i++) { + // Note: offset each subsequent quad by 2, since end points are shared + const SkPoint* quad = quads + i * 2; + addBezier(quad, quadraticBezierCalculation, segmentPoints, lengths, + errorConic, false); + } + break; + } + default: + static_assert(SkPath::kMove_Verb == 0 + && SkPath::kLine_Verb == 1 + && SkPath::kQuad_Verb == 2 + && SkPath::kConic_Verb == 3 + && SkPath::kCubic_Verb == 4 + && SkPath::kClose_Verb == 5 + && SkPath::kDone_Verb == 6, + "Path enum changed, new types may have been added."); + break; + } + } + + // Returns a float[] with each point along the path represented by 3 floats + // * fractional length along the path that the point resides + // * x coordinate + // * y coordinate + // Note that more than one point may have the same length along the path in + // the case of a move. + // NULL can be returned if the Path is empty. + static jfloatArray approximate(JNIEnv* env, jclass clazz, jlong pathHandle, + float acceptableError) { + SkPath* path = reinterpret_cast(pathHandle); + SkASSERT(path); + SkPath::Iter pathIter(*path, false); + SkPath::Verb verb; + SkPoint points[4]; + std::vector segmentPoints; + std::vector lengths; + float errorSquared = acceptableError * acceptableError; + float errorConic = acceptableError / 2; // somewhat arbitrary + + while ((verb = pathIter.next(points)) != SkPath::kDone_Verb) { + createVerbSegments(pathIter, verb, points, segmentPoints, lengths, + errorSquared, errorConic); + } + + if (segmentPoints.empty()) { + int numVerbs = path->countVerbs(); + if (numVerbs == 1) { + addMove(segmentPoints, lengths, path->getPoint(0)); + } else { + // Invalid or empty path. Fall back to point(0,0) + addMove(segmentPoints, lengths, SkPoint()); + } + } + + float totalLength = lengths.back(); + if (totalLength == 0) { + // Lone Move instructions should still be able to animate at the same value. + segmentPoints.push_back(segmentPoints.back()); + lengths.push_back(1); + totalLength = 1; + } + + size_t numPoints = segmentPoints.size(); + size_t approximationArraySize = numPoints * 3; + + float* approximation = new float[approximationArraySize]; + + int approximationIndex = 0; + for (size_t i = 0; i < numPoints; i++) { + const SkPoint& point = segmentPoints[i]; + approximation[approximationIndex++] = lengths[i] / totalLength; + approximation[approximationIndex++] = point.x(); + approximation[approximationIndex++] = point.y(); + } + + jfloatArray result = env->NewFloatArray(approximationArraySize); + env->SetFloatArrayRegion(result, 0, approximationArraySize, approximation); + delete[] approximation; + return result; + } + + // ---------------- @FastNative ----------------------------- + + static jboolean isRect(JNIEnv* env, jclass clazz, jlong objHandle, jobject jrect) { + SkRect rect; + SkPath* obj = reinterpret_cast(objHandle); + jboolean result = obj->isRect(&rect); + if (jrect) { + GraphicsJNI::rect_to_jrectf(rect, env, jrect); + } + return result; + } + + // ---------------- @CriticalNative ------------------------- + + static void reset(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) { + SkPath* obj = reinterpret_cast(objHandle); + obj->reset(); + } + + static void rewind(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) { + SkPath* obj = reinterpret_cast(objHandle); + obj->rewind(); + } + + static jboolean isEmpty(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) { + SkPath* obj = reinterpret_cast(objHandle); + return obj->isEmpty(); + } + + static jboolean isConvex(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) { + SkPath* obj = reinterpret_cast(objHandle); + return obj->isConvex(); + } + + static jint getFillType(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) { + SkPath* obj = reinterpret_cast(objHandle); + return static_cast(obj->getFillType()); + } + + static void setFillType(CRITICAL_JNI_PARAMS_COMMA jlong pathHandle, jint ftHandle) {; + SkPath* path = reinterpret_cast(pathHandle); + SkPathFillType ft = static_cast(ftHandle); + path->setFillType(ft); + } +}; + +static const JNINativeMethod methods[] = { + {"nInit","()J", (void*) SkPathGlue::init}, + {"nInit","(J)J", (void*) SkPathGlue::init_Path}, + {"nGetFinalizer", "()J", (void*) SkPathGlue::getFinalizer}, + {"nSet","(JJ)V", (void*) SkPathGlue::set}, + {"nComputeBounds","(JLandroid/graphics/RectF;)V", (void*) SkPathGlue::computeBounds}, + {"nIncReserve","(JI)V", (void*) SkPathGlue::incReserve}, + {"nMoveTo","(JFF)V", (void*) SkPathGlue::moveTo__FF}, + {"nRMoveTo","(JFF)V", (void*) SkPathGlue::rMoveTo}, + {"nLineTo","(JFF)V", (void*) SkPathGlue::lineTo__FF}, + {"nRLineTo","(JFF)V", (void*) SkPathGlue::rLineTo}, + {"nQuadTo","(JFFFF)V", (void*) SkPathGlue::quadTo__FFFF}, + {"nRQuadTo","(JFFFF)V", (void*) SkPathGlue::rQuadTo}, + {"nCubicTo","(JFFFFFF)V", (void*) SkPathGlue::cubicTo__FFFFFF}, + {"nRCubicTo","(JFFFFFF)V", (void*) SkPathGlue::rCubicTo}, + {"nArcTo","(JFFFFFFZ)V", (void*) SkPathGlue::arcTo}, + {"nClose","(J)V", (void*) SkPathGlue::close}, + {"nAddRect","(JFFFFI)V", (void*) SkPathGlue::addRect}, + {"nAddOval","(JFFFFI)V", (void*) SkPathGlue::addOval}, + {"nAddCircle","(JFFFI)V", (void*) SkPathGlue::addCircle}, + {"nAddArc","(JFFFFFF)V", (void*) SkPathGlue::addArc}, + {"nAddRoundRect","(JFFFFFFI)V", (void*) SkPathGlue::addRoundRectXY}, + {"nAddRoundRect","(JFFFF[FI)V", (void*) SkPathGlue::addRoundRect8}, + {"nAddPath","(JJFF)V", (void*) SkPathGlue::addPath__PathFF}, + {"nAddPath","(JJ)V", (void*) SkPathGlue::addPath__Path}, + {"nAddPath","(JJJ)V", (void*) SkPathGlue::addPath__PathMatrix}, + {"nOffset","(JFF)V", (void*) SkPathGlue::offset__FF}, + {"nSetLastPoint","(JFF)V", (void*) SkPathGlue::setLastPoint}, + {"nTransform","(JJJ)V", (void*) SkPathGlue::transform__MatrixPath}, + {"nTransform","(JJ)V", (void*) SkPathGlue::transform__Matrix}, + {"nOp","(JJIJ)Z", (void*) SkPathGlue::op}, + {"nApproximate", "(JF)[F", (void*) SkPathGlue::approximate}, + + // ------- @FastNative below here ---------------------- + {"nIsRect","(JLandroid/graphics/RectF;)Z", (void*) SkPathGlue::isRect}, + + // ------- @CriticalNative below here ------------------ + {"nReset","(J)V", (void*) SkPathGlue::reset}, + {"nRewind","(J)V", (void*) SkPathGlue::rewind}, + {"nIsEmpty","(J)Z", (void*) SkPathGlue::isEmpty}, + {"nIsConvex","(J)Z", (void*) SkPathGlue::isConvex}, + {"nGetFillType","(J)I", (void*) SkPathGlue::getFillType}, + {"nSetFillType","(JI)V", (void*) SkPathGlue::setFillType}, +}; + +int register_android_graphics_Path(JNIEnv* env) { + return RegisterMethodsOrDie(env, "android/graphics/Path", methods, NELEM(methods)); + + static_assert(0 == (int)SkPathDirection::kCW, "direction_mismatch"); + static_assert(1 == (int)SkPathDirection::kCCW, "direction_mismatch"); +} + +} diff --git a/libs/hwui/jni/PathEffect.cpp b/libs/hwui/jni/PathEffect.cpp new file mode 100644 index 000000000000..a4992de72ff6 --- /dev/null +++ b/libs/hwui/jni/PathEffect.cpp @@ -0,0 +1,120 @@ +#include "GraphicsJNI.h" +#include "Sk1DPathEffect.h" +#include "SkCornerPathEffect.h" +#include "SkDashPathEffect.h" +#include "SkDiscretePathEffect.h" +#include "SkPathEffect.h" +#include "core_jni_helpers.h" + +#include + +class SkPathEffectGlue { +public: + + static void destructor(JNIEnv* env, jobject, jlong effectHandle) { + SkPathEffect* effect = reinterpret_cast(effectHandle); + SkSafeUnref(effect); + } + + static jlong Compose_constructor(JNIEnv* env, jobject, + jlong outerHandle, jlong innerHandle) { + SkPathEffect* outer = reinterpret_cast(outerHandle); + SkPathEffect* inner = reinterpret_cast(innerHandle); + SkPathEffect* effect = SkPathEffect::MakeCompose(sk_ref_sp(outer), + sk_ref_sp(inner)).release(); + return reinterpret_cast(effect); + } + + static jlong Sum_constructor(JNIEnv* env, jobject, + jlong firstHandle, jlong secondHandle) { + SkPathEffect* first = reinterpret_cast(firstHandle); + SkPathEffect* second = reinterpret_cast(secondHandle); + SkPathEffect* effect = SkPathEffect::MakeSum(sk_ref_sp(first), + sk_ref_sp(second)).release(); + return reinterpret_cast(effect); + } + + static jlong Dash_constructor(JNIEnv* env, jobject, + jfloatArray intervalArray, jfloat phase) { + AutoJavaFloatArray autoInterval(env, intervalArray); + int count = autoInterval.length() & ~1; // even number +#ifdef SK_SCALAR_IS_FLOAT + SkScalar* intervals = autoInterval.ptr(); +#else + #error Need to convert float array to SkScalar array before calling the following function. +#endif + SkPathEffect* effect = SkDashPathEffect::Make(intervals, count, phase).release(); + return reinterpret_cast(effect); + } + + static jlong OneD_constructor(JNIEnv* env, jobject, + jlong shapeHandle, jfloat advance, jfloat phase, jint style) { + const SkPath* shape = reinterpret_cast(shapeHandle); + SkASSERT(shape != NULL); + SkPathEffect* effect = SkPath1DPathEffect::Make(*shape, advance, phase, + (SkPath1DPathEffect::Style)style).release(); + return reinterpret_cast(effect); + } + + static jlong Corner_constructor(JNIEnv* env, jobject, jfloat radius){ + SkPathEffect* effect = SkCornerPathEffect::Make(radius).release(); + return reinterpret_cast(effect); + } + + static jlong Discrete_constructor(JNIEnv* env, jobject, + jfloat length, jfloat deviation) { + SkPathEffect* effect = SkDiscretePathEffect::Make(length, deviation).release(); + return reinterpret_cast(effect); + } + +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////// + +static const JNINativeMethod gPathEffectMethods[] = { + { "nativeDestructor", "(J)V", (void*)SkPathEffectGlue::destructor } +}; + +static const JNINativeMethod gComposePathEffectMethods[] = { + { "nativeCreate", "(JJ)J", (void*)SkPathEffectGlue::Compose_constructor } +}; + +static const JNINativeMethod gSumPathEffectMethods[] = { + { "nativeCreate", "(JJ)J", (void*)SkPathEffectGlue::Sum_constructor } +}; + +static const JNINativeMethod gDashPathEffectMethods[] = { + { "nativeCreate", "([FF)J", (void*)SkPathEffectGlue::Dash_constructor } +}; + +static const JNINativeMethod gPathDashPathEffectMethods[] = { + { "nativeCreate", "(JFFI)J", (void*)SkPathEffectGlue::OneD_constructor } +}; + +static const JNINativeMethod gCornerPathEffectMethods[] = { + { "nativeCreate", "(F)J", (void*)SkPathEffectGlue::Corner_constructor } +}; + +static const JNINativeMethod gDiscretePathEffectMethods[] = { + { "nativeCreate", "(FF)J", (void*)SkPathEffectGlue::Discrete_constructor } +}; + +int register_android_graphics_PathEffect(JNIEnv* env) +{ + android::RegisterMethodsOrDie(env, "android/graphics/PathEffect", gPathEffectMethods, + NELEM(gPathEffectMethods)); + android::RegisterMethodsOrDie(env, "android/graphics/ComposePathEffect", + gComposePathEffectMethods, NELEM(gComposePathEffectMethods)); + android::RegisterMethodsOrDie(env, "android/graphics/SumPathEffect", gSumPathEffectMethods, + NELEM(gSumPathEffectMethods)); + android::RegisterMethodsOrDie(env, "android/graphics/DashPathEffect", gDashPathEffectMethods, + NELEM(gDashPathEffectMethods)); + android::RegisterMethodsOrDie(env, "android/graphics/PathDashPathEffect", + gPathDashPathEffectMethods, NELEM(gPathDashPathEffectMethods)); + android::RegisterMethodsOrDie(env, "android/graphics/CornerPathEffect", + gCornerPathEffectMethods, NELEM(gCornerPathEffectMethods)); + android::RegisterMethodsOrDie(env, "android/graphics/DiscretePathEffect", + gDiscretePathEffectMethods, NELEM(gDiscretePathEffectMethods)); + + return 0; +} diff --git a/libs/hwui/jni/PathMeasure.cpp b/libs/hwui/jni/PathMeasure.cpp new file mode 100644 index 000000000000..70e528d4be6f --- /dev/null +++ b/libs/hwui/jni/PathMeasure.cpp @@ -0,0 +1,162 @@ +/* libs/android_runtime/android/graphics/PathMeasure.cpp +** +** Copyright 2007, 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 "jni.h" +#include "GraphicsJNI.h" +#include + +#include "SkPathMeasure.h" + +/* We declare an explicit pair, so that we don't have to rely on the java + client to be sure not to edit the path while we have an active measure + object associated with it. + + This costs us the copy of the path, for the sake of not allowing a bad + java client to randomly crash (since we can't detect the case where the + native path has been modified). + + The C side does have this risk, but it chooses for speed over safety. If it + later changes this, and is internally safe from changes to the path, then + we can remove this explicit copy from our JNI code. + + Note that we do not have a reference on the java side to the java path. + Were we to not need the native copy here, we would want to add a java + reference, so that the java path would not get GD'd while the measure object + was still alive. +*/ +struct PathMeasurePair { + PathMeasurePair() {} + PathMeasurePair(const SkPath& path, bool forceClosed) + : fPath(path), fMeasure(fPath, forceClosed) {} + + SkPath fPath; // copy of the user's path + SkPathMeasure fMeasure; // this guy points to fPath +}; + +namespace android { + +class SkPathMeasureGlue { +public: + + static jlong create(JNIEnv* env, jobject clazz, jlong pathHandle, + jboolean forceClosedHandle) { + const SkPath* path = reinterpret_cast(pathHandle); + bool forceClosed = (forceClosedHandle == JNI_TRUE); + PathMeasurePair* pair; + if(path) + pair = new PathMeasurePair(*path, forceClosed); + else + pair = new PathMeasurePair; + return reinterpret_cast(pair); + } + + static void setPath(JNIEnv* env, jobject clazz, jlong pairHandle, + jlong pathHandle, jboolean forceClosedHandle) { + PathMeasurePair* pair = reinterpret_cast(pairHandle); + const SkPath* path = reinterpret_cast(pathHandle); + bool forceClosed = (forceClosedHandle == JNI_TRUE); + + if (NULL == path) { + pair->fPath.reset(); + } else { + pair->fPath = *path; + } + pair->fMeasure.setPath(&pair->fPath, forceClosed); + } + + static jfloat getLength(JNIEnv* env, jobject clazz, jlong pairHandle) { + PathMeasurePair* pair = reinterpret_cast(pairHandle); + return static_cast(SkScalarToFloat(pair->fMeasure.getLength())); + } + + static void convertTwoElemFloatArray(JNIEnv* env, jfloatArray array, const SkScalar src[2]) { + AutoJavaFloatArray autoArray(env, array, 2); + jfloat* ptr = autoArray.ptr(); + ptr[0] = SkScalarToFloat(src[0]); + ptr[1] = SkScalarToFloat(src[1]); + } + + static jboolean getPosTan(JNIEnv* env, jobject clazz, jlong pairHandle, jfloat dist, jfloatArray pos, jfloatArray tan) { + PathMeasurePair* pair = reinterpret_cast(pairHandle); + SkScalar tmpPos[2], tmpTan[2]; + SkScalar* posPtr = pos ? tmpPos : NULL; + SkScalar* tanPtr = tan ? tmpTan : NULL; + + if (!pair->fMeasure.getPosTan(dist, (SkPoint*)posPtr, (SkVector*)tanPtr)) { + return JNI_FALSE; + } + + if (pos) { + convertTwoElemFloatArray(env, pos, tmpPos); + } + if (tan) { + convertTwoElemFloatArray(env, tan, tmpTan); + } + return JNI_TRUE; + } + + static jboolean getMatrix(JNIEnv* env, jobject clazz, jlong pairHandle, jfloat dist, + jlong matrixHandle, jint flags) { + PathMeasurePair* pair = reinterpret_cast(pairHandle); + SkMatrix* matrix = reinterpret_cast(matrixHandle); + bool result = pair->fMeasure.getMatrix(dist, matrix, (SkPathMeasure::MatrixFlags)flags); + return result ? JNI_TRUE : JNI_FALSE; + } + + static jboolean getSegment(JNIEnv* env, jobject clazz, jlong pairHandle, jfloat startF, + jfloat stopF, jlong dstHandle, jboolean startWithMoveTo) { + PathMeasurePair* pair = reinterpret_cast(pairHandle); + SkPath* dst = reinterpret_cast(dstHandle); + bool result = pair->fMeasure.getSegment(startF, stopF, dst, startWithMoveTo); + return result ? JNI_TRUE : JNI_FALSE; + } + + static jboolean isClosed(JNIEnv* env, jobject clazz, jlong pairHandle) { + PathMeasurePair* pair = reinterpret_cast(pairHandle); + bool result = pair->fMeasure.isClosed(); + return result ? JNI_TRUE : JNI_FALSE; + } + + static jboolean nextContour(JNIEnv* env, jobject clazz, jlong pairHandle) { + PathMeasurePair* pair = reinterpret_cast(pairHandle); + bool result = pair->fMeasure.nextContour(); + return result ? JNI_TRUE : JNI_FALSE; + } + + static void destroy(JNIEnv* env, jobject clazz, jlong pairHandle) { + PathMeasurePair* pair = reinterpret_cast(pairHandle); + delete pair; + } +}; + +static const JNINativeMethod methods[] = { + {"native_create", "(JZ)J", (void*) SkPathMeasureGlue::create }, + {"native_setPath", "(JJZ)V", (void*) SkPathMeasureGlue::setPath }, + {"native_getLength", "(J)F", (void*) SkPathMeasureGlue::getLength }, + {"native_getPosTan", "(JF[F[F)Z", (void*) SkPathMeasureGlue::getPosTan }, + {"native_getMatrix", "(JFJI)Z", (void*) SkPathMeasureGlue::getMatrix }, + {"native_getSegment", "(JFFJZ)Z", (void*) SkPathMeasureGlue::getSegment }, + {"native_isClosed", "(J)Z", (void*) SkPathMeasureGlue::isClosed }, + {"native_nextContour", "(J)Z", (void*) SkPathMeasureGlue::nextContour }, + {"native_destroy", "(J)V", (void*) SkPathMeasureGlue::destroy } +}; + +int register_android_graphics_PathMeasure(JNIEnv* env) { + return RegisterMethodsOrDie(env, "android/graphics/PathMeasure", methods, NELEM(methods)); +} + +} diff --git a/libs/hwui/jni/Picture.cpp b/libs/hwui/jni/Picture.cpp new file mode 100644 index 000000000000..d1b952130e88 --- /dev/null +++ b/libs/hwui/jni/Picture.cpp @@ -0,0 +1,119 @@ +/* + * 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 "Picture.h" +#include "SkStream.h" + +#include +#include + +namespace android { + +Picture::Picture(const Picture* src) { + if (NULL != src) { + mWidth = src->width(); + mHeight = src->height(); + if (NULL != src->mPicture.get()) { + mPicture = src->mPicture; + } else if (NULL != src->mRecorder.get()) { + mPicture = src->makePartialCopy(); + } + } else { + mWidth = 0; + mHeight = 0; + } +} + +Picture::Picture(sk_sp&& src) { + mPicture = std::move(src); + mWidth = 0; + mHeight = 0; +} + +Canvas* Picture::beginRecording(int width, int height) { + mPicture.reset(NULL); + mRecorder.reset(new SkPictureRecorder); + mWidth = width; + mHeight = height; + SkCanvas* canvas = mRecorder->beginRecording(SkIntToScalar(width), SkIntToScalar(height)); + return Canvas::create_canvas(canvas); +} + +void Picture::endRecording() { + if (NULL != mRecorder.get()) { + mPicture = mRecorder->finishRecordingAsPicture(); + mRecorder.reset(NULL); + } +} + +int Picture::width() const { + return mWidth; +} + +int Picture::height() const { + return mHeight; +} + +Picture* Picture::CreateFromStream(SkStream* stream) { + Picture* newPict = new Picture; + + sk_sp skPicture = SkPicture::MakeFromStream(stream); + if (NULL != skPicture) { + newPict->mPicture = skPicture; + + const SkIRect cullRect = skPicture->cullRect().roundOut(); + newPict->mWidth = cullRect.width(); + newPict->mHeight = cullRect.height(); + } + + return newPict; +} + +void Picture::serialize(SkWStream* stream) const { + if (NULL != mRecorder.get()) { + this->makePartialCopy()->serialize(stream); + } else if (NULL != mPicture.get()) { + mPicture->serialize(stream); + } else { + // serialize "empty" picture + SkPictureRecorder recorder; + recorder.beginRecording(0, 0); + recorder.finishRecordingAsPicture()->serialize(stream); + } +} + +void Picture::draw(Canvas* canvas) { + if (NULL != mRecorder.get()) { + this->endRecording(); + SkASSERT(NULL != mPicture.get()); + } + + if (mPicture) { + canvas->drawPicture(*mPicture); + } +} + +sk_sp Picture::makePartialCopy() const { + SkASSERT(NULL != mRecorder.get()); + + SkPictureRecorder reRecorder; + + SkCanvas* canvas = reRecorder.beginRecording(mWidth, mHeight, NULL, 0); + mRecorder->partialReplay(canvas); + return reRecorder.finishRecordingAsPicture(); +} + +}; // namespace android diff --git a/libs/hwui/jni/Picture.h b/libs/hwui/jni/Picture.h new file mode 100644 index 000000000000..536f651473a9 --- /dev/null +++ b/libs/hwui/jni/Picture.h @@ -0,0 +1,68 @@ +/* + * 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_PICTURE_H_ +#define ANDROID_GRAPHICS_PICTURE_H_ + +#include "SkPicture.h" +#include "SkPictureRecorder.h" +#include "SkRefCnt.h" + +#include + +class SkStream; +class SkWStream; + +namespace android { + +class Canvas; + +// Skia's SkPicture class has been split into an SkPictureRecorder +// and an SkPicture. AndroidPicture recreates the functionality +// of the old SkPicture interface by flip-flopping between the two +// new classes. +class Picture { +public: + explicit Picture(const Picture* src = NULL); + explicit Picture(sk_sp&& src); + + Canvas* beginRecording(int width, int height); + + void endRecording(); + + int width() const; + + int height() const; + + static Picture* CreateFromStream(SkStream* stream); + + void serialize(SkWStream* stream) const; + + void draw(Canvas* canvas); + +private: + int mWidth; + int mHeight; + sk_sp mPicture; + std::unique_ptr mRecorder; + + // Make a copy of a picture that is in the midst of being recorded. The + // resulting picture will have balanced saves and restores. + sk_sp makePartialCopy() const; +}; + +}; // namespace android +#endif // ANDROID_GRAPHICS_PICTURE_H_ diff --git a/libs/hwui/jni/Region.cpp b/libs/hwui/jni/Region.cpp new file mode 100644 index 000000000000..87662f713449 --- /dev/null +++ b/libs/hwui/jni/Region.cpp @@ -0,0 +1,360 @@ +/* + * 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 "SkRegion.h" +#include "SkPath.h" +#include "GraphicsJNI.h" + +#ifdef __ANDROID__ // Layoutlib does not support parcel +#include +#endif +#include "android_os_Parcel.h" +#include "android_util_Binder.h" + +#include +#include + +namespace android { + +static jfieldID gRegion_nativeInstanceFieldID; + +static inline jboolean boolTojboolean(bool value) { + return value ? JNI_TRUE : JNI_FALSE; +} + +static inline SkRegion* GetSkRegion(JNIEnv* env, jobject regionObject) { + jlong regionHandle = env->GetLongField(regionObject, gRegion_nativeInstanceFieldID); + SkRegion* region = reinterpret_cast(regionHandle); + SkASSERT(region != NULL); + return region; +} + +static jlong Region_constructor(JNIEnv* env, jobject) { + return reinterpret_cast(new SkRegion); +} + +static void Region_destructor(JNIEnv* env, jobject, jlong regionHandle) { + SkRegion* region = reinterpret_cast(regionHandle); + SkASSERT(region); + delete region; +} + +static void Region_setRegion(JNIEnv* env, jobject, jlong dstHandle, jlong srcHandle) { + SkRegion* dst = reinterpret_cast(dstHandle); + const SkRegion* src = reinterpret_cast(srcHandle); + SkASSERT(dst && src); + *dst = *src; +} + +static jboolean Region_setRect(JNIEnv* env, jobject, jlong dstHandle, jint left, jint top, jint right, jint bottom) { + SkRegion* dst = reinterpret_cast(dstHandle); + bool result = dst->setRect({left, top, right, bottom}); + return boolTojboolean(result); +} + +static jboolean Region_setPath(JNIEnv* env, jobject, jlong dstHandle, + jlong pathHandle, jlong clipHandle) { + SkRegion* dst = reinterpret_cast(dstHandle); + const SkPath* path = reinterpret_cast(pathHandle); + const SkRegion* clip = reinterpret_cast(clipHandle); + SkASSERT(dst && path && clip); + bool result = dst->setPath(*path, *clip); + return boolTojboolean(result); + +} + +static jboolean Region_getBounds(JNIEnv* env, jobject, jlong regionHandle, jobject rectBounds) { + SkRegion* region = reinterpret_cast(regionHandle); + GraphicsJNI::irect_to_jrect(region->getBounds(), env, rectBounds); + bool result = !region->isEmpty(); + return boolTojboolean(result); +} + +static jboolean Region_getBoundaryPath(JNIEnv* env, jobject, jlong regionHandle, jlong pathHandle) { + const SkRegion* region = reinterpret_cast(regionHandle); + SkPath* path = reinterpret_cast(pathHandle); + bool result = region->getBoundaryPath(path); + return boolTojboolean(result); +} + +static jboolean Region_op0(JNIEnv* env, jobject, jlong dstHandle, jint left, jint top, jint right, jint bottom, jint op) { + SkRegion* dst = reinterpret_cast(dstHandle); + bool result = dst->op({left, top, right, bottom}, (SkRegion::Op)op); + return boolTojboolean(result); +} + +static jboolean Region_op1(JNIEnv* env, jobject, jlong dstHandle, jobject rectObject, jlong regionHandle, jint op) { + SkRegion* dst = reinterpret_cast(dstHandle); + const SkRegion* region = reinterpret_cast(regionHandle); + SkIRect ir; + GraphicsJNI::jrect_to_irect(env, rectObject, &ir); + bool result = dst->op(ir, *region, (SkRegion::Op)op); + return boolTojboolean(result); +} + +static jboolean Region_op2(JNIEnv* env, jobject, jlong dstHandle, jlong region1Handle, jlong region2Handle, jint op) { + SkRegion* dst = reinterpret_cast(dstHandle); + const SkRegion* region1 = reinterpret_cast(region1Handle); + const SkRegion* region2 = reinterpret_cast(region2Handle); + bool result = dst->op(*region1, *region2, (SkRegion::Op)op); + return boolTojboolean(result); +} + +//////////////////////////////////// These are methods, not static + +static jboolean Region_isEmpty(JNIEnv* env, jobject region) { + bool result = GetSkRegion(env, region)->isEmpty(); + return boolTojboolean(result); +} + +static jboolean Region_isRect(JNIEnv* env, jobject region) { + bool result = GetSkRegion(env, region)->isRect(); + return boolTojboolean(result); +} + +static jboolean Region_isComplex(JNIEnv* env, jobject region) { + bool result = GetSkRegion(env, region)->isComplex(); + return boolTojboolean(result); +} + +static jboolean Region_contains(JNIEnv* env, jobject region, jint x, jint y) { + bool result = GetSkRegion(env, region)->contains(x, y); + return boolTojboolean(result); +} + +static jboolean Region_quickContains(JNIEnv* env, jobject region, jint left, jint top, jint right, jint bottom) { + bool result = GetSkRegion(env, region)->quickContains({left, top, right, bottom}); + return boolTojboolean(result); +} + +static jboolean Region_quickRejectIIII(JNIEnv* env, jobject region, jint left, jint top, jint right, jint bottom) { + SkIRect ir; + ir.setLTRB(left, top, right, bottom); + bool result = GetSkRegion(env, region)->quickReject(ir); + return boolTojboolean(result); +} + +static jboolean Region_quickRejectRgn(JNIEnv* env, jobject region, jobject other) { + bool result = GetSkRegion(env, region)->quickReject(*GetSkRegion(env, other)); + return boolTojboolean(result); +} + +static void Region_translate(JNIEnv* env, jobject region, jint x, jint y, jobject dst) { + SkRegion* rgn = GetSkRegion(env, region); + if (dst) + rgn->translate(x, y, GetSkRegion(env, dst)); + else + rgn->translate(x, y); +} + +// Scale the rectangle by given scale and set the reuslt to the dst. +static void scale_rect(SkIRect* dst, const SkIRect& src, float scale) { + dst->fLeft = (int)::roundf(src.fLeft * scale); + dst->fTop = (int)::roundf(src.fTop * scale); + dst->fRight = (int)::roundf(src.fRight * scale); + dst->fBottom = (int)::roundf(src.fBottom * scale); +} + +// Scale the region by given scale and set the reuslt to the dst. +// dest and src can be the same region instance. +static void scale_rgn(SkRegion* dst, const SkRegion& src, float scale) { + SkRegion tmp; + SkRegion::Iterator iter(src); + + for (; !iter.done(); iter.next()) { + SkIRect r; + scale_rect(&r, iter.rect(), scale); + tmp.op(r, SkRegion::kUnion_Op); + } + dst->swap(tmp); +} + +static void Region_scale(JNIEnv* env, jobject region, jfloat scale, jobject dst) { + SkRegion* rgn = GetSkRegion(env, region); + if (dst) + scale_rgn(GetSkRegion(env, dst), *rgn, scale); + else + scale_rgn(rgn, *rgn, scale); +} + +static jstring Region_toString(JNIEnv* env, jobject clazz, jlong regionHandle) { + SkRegion* region = reinterpret_cast(regionHandle); + char* str = region->toString(); + if (str == NULL) { + return NULL; + } + jstring result = env->NewStringUTF(str); + free(str); + return result; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static jlong Region_createFromParcel(JNIEnv* env, jobject clazz, jobject parcel) +{ +#ifdef __ANDROID__ // Layoutlib does not support parcel + if (parcel == nullptr) { + return 0; + } + + android::Parcel* p = android::parcelForJavaObject(env, parcel); + + std::vector rects; + p->readInt32Vector(&rects); + + if ((rects.size() % 4) != 0) { + return 0; + } + + SkRegion* region = new SkRegion; + for (size_t x = 0; x + 4 <= rects.size(); x += 4) { + region->op({rects[x], rects[x+1], rects[x+2], rects[x+3]}, SkRegion::kUnion_Op); + } + + return reinterpret_cast(region); +#else + return 0; +#endif +} + +static jboolean Region_writeToParcel(JNIEnv* env, jobject clazz, jlong regionHandle, jobject parcel) +{ +#ifdef __ANDROID__ // Layoutlib does not support parcel + const SkRegion* region = reinterpret_cast(regionHandle); + if (parcel == nullptr) { + return JNI_FALSE; + } + + android::Parcel* p = android::parcelForJavaObject(env, parcel); + + std::vector rects; + SkRegion::Iterator it(*region); + while (!it.done()) { + const SkIRect& r = it.rect(); + rects.push_back(r.fLeft); + rects.push_back(r.fTop); + rects.push_back(r.fRight); + rects.push_back(r.fBottom); + it.next(); + } + + p->writeInt32Vector(rects); + return JNI_TRUE; +#else + return JNI_FALSE; +#endif +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static jboolean Region_equals(JNIEnv* env, jobject clazz, jlong r1Handle, jlong r2Handle) +{ + const SkRegion *r1 = reinterpret_cast(r1Handle); + const SkRegion *r2 = reinterpret_cast(r2Handle); + return boolTojboolean(*r1 == *r2); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +struct RgnIterPair { + SkRegion fRgn; // a copy of the caller's region + SkRegion::Iterator fIter; // an iterator acting upon the copy (fRgn) + + explicit RgnIterPair(const SkRegion& rgn) : fRgn(rgn) { + // have our iterator reference our copy (fRgn), so we know it will be + // unchanged for the lifetime of the iterator + fIter.reset(fRgn); + } +}; + +static jlong RegionIter_constructor(JNIEnv* env, jobject, jlong regionHandle) +{ + const SkRegion* region = reinterpret_cast(regionHandle); + SkASSERT(region); + return reinterpret_cast(new RgnIterPair(*region)); +} + +static void RegionIter_destructor(JNIEnv* env, jobject, jlong pairHandle) +{ + RgnIterPair* pair = reinterpret_cast(pairHandle); + SkASSERT(pair); + delete pair; +} + +static jboolean RegionIter_next(JNIEnv* env, jobject, jlong pairHandle, jobject rectObject) +{ + RgnIterPair* pair = reinterpret_cast(pairHandle); + // the caller has checked that rectObject is not nul + SkASSERT(pair); + SkASSERT(rectObject); + + if (!pair->fIter.done()) { + GraphicsJNI::irect_to_jrect(pair->fIter.rect(), env, rectObject); + pair->fIter.next(); + return JNI_TRUE; + } + return JNI_FALSE; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static const JNINativeMethod gRegionIterMethods[] = { + { "nativeConstructor", "(J)J", (void*)RegionIter_constructor }, + { "nativeDestructor", "(J)V", (void*)RegionIter_destructor }, + { "nativeNext", "(JLandroid/graphics/Rect;)Z", (void*)RegionIter_next } +}; + +static const JNINativeMethod gRegionMethods[] = { + // these are static methods + { "nativeConstructor", "()J", (void*)Region_constructor }, + { "nativeDestructor", "(J)V", (void*)Region_destructor }, + { "nativeSetRegion", "(JJ)V", (void*)Region_setRegion }, + { "nativeSetRect", "(JIIII)Z", (void*)Region_setRect }, + { "nativeSetPath", "(JJJ)Z", (void*)Region_setPath }, + { "nativeGetBounds", "(JLandroid/graphics/Rect;)Z", (void*)Region_getBounds }, + { "nativeGetBoundaryPath", "(JJ)Z", (void*)Region_getBoundaryPath }, + { "nativeOp", "(JIIIII)Z", (void*)Region_op0 }, + { "nativeOp", "(JLandroid/graphics/Rect;JI)Z", (void*)Region_op1 }, + { "nativeOp", "(JJJI)Z", (void*)Region_op2 }, + // these are methods that take the java region object + { "isEmpty", "()Z", (void*)Region_isEmpty }, + { "isRect", "()Z", (void*)Region_isRect }, + { "isComplex", "()Z", (void*)Region_isComplex }, + { "contains", "(II)Z", (void*)Region_contains }, + { "quickContains", "(IIII)Z", (void*)Region_quickContains }, + { "quickReject", "(IIII)Z", (void*)Region_quickRejectIIII }, + { "quickReject", "(Landroid/graphics/Region;)Z", (void*)Region_quickRejectRgn }, + { "scale", "(FLandroid/graphics/Region;)V", (void*)Region_scale }, + { "translate", "(IILandroid/graphics/Region;)V", (void*)Region_translate }, + { "nativeToString", "(J)Ljava/lang/String;", (void*)Region_toString }, + // parceling methods + { "nativeCreateFromParcel", "(Landroid/os/Parcel;)J", (void*)Region_createFromParcel }, + { "nativeWriteToParcel", "(JLandroid/os/Parcel;)Z", (void*)Region_writeToParcel }, + { "nativeEquals", "(JJ)Z", (void*)Region_equals }, +}; + +int register_android_graphics_Region(JNIEnv* env) +{ + jclass clazz = FindClassOrDie(env, "android/graphics/Region"); + + gRegion_nativeInstanceFieldID = GetFieldIDOrDie(env, clazz, "mNativeRegion", "J"); + + RegisterMethodsOrDie(env, "android/graphics/Region", gRegionMethods, NELEM(gRegionMethods)); + return RegisterMethodsOrDie(env, "android/graphics/RegionIterator", gRegionIterMethods, + NELEM(gRegionIterMethods)); +} + +} // namespace android diff --git a/libs/hwui/jni/RtlProperties.h b/libs/hwui/jni/RtlProperties.h new file mode 100644 index 000000000000..907dd59b6e68 --- /dev/null +++ b/libs/hwui/jni/RtlProperties.h @@ -0,0 +1,49 @@ +/* + * 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_GRAPHICS_RTL_PROPERTIES_H_ +#define _ANDROID_GRAPHICS_RTL_PROPERTIES_H_ + +#include +#include + +namespace android { + +/** + * Debug level for app developers. + */ +#define RTL_PROPERTY_DEBUG "rtl.debug_level" + +/** + * Debug levels. Debug levels are used as flags. + */ +enum RtlDebugLevel { + kRtlDebugDisabled = 0, + kRtlDebugMemory = 1, + kRtlDebugCaches = 2, + kRtlDebugAllocations = 3 +}; + +static RtlDebugLevel readRtlDebugLevel() { + char property[PROPERTY_VALUE_MAX]; + if (property_get(RTL_PROPERTY_DEBUG, property, NULL) > 0) { + return (RtlDebugLevel) atoi(property); + } + return kRtlDebugDisabled; +} + +} // namespace android +#endif // _ANDROID_GRAPHICS_RTL_PROPERTIES_H_ diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp new file mode 100644 index 000000000000..f5e2a5244416 --- /dev/null +++ b/libs/hwui/jni/Shader.cpp @@ -0,0 +1,308 @@ +#include "GraphicsJNI.h" +#include "SkColorFilter.h" +#include "SkGradientShader.h" +#include "SkImagePriv.h" +#include "SkShader.h" +#include "SkBlendMode.h" +#include "core_jni_helpers.h" +#include "include/effects/SkRuntimeEffect.h" + +#include + +#include + +using namespace android::uirenderer; + +/** + * By default Skia gradients will interpolate their colors in unpremul space + * and then premultiply each of the results. We must set this flag to preserve + * backwards compatiblity by premultiplying the colors of the gradient first, + * and then interpolating between them. + */ +static const uint32_t sGradientShaderFlags = SkGradientShader::kInterpolateColorsInPremul_Flag; + +#define ThrowIAE_IfNull(env, ptr) \ + if (nullptr == ptr) { \ + doThrowIAE(env); \ + return 0; \ + } + +static void Color_RGBToHSV(JNIEnv* env, jobject, jint red, jint green, jint blue, jfloatArray hsvArray) +{ + SkScalar hsv[3]; + SkRGBToHSV(red, green, blue, hsv); + + AutoJavaFloatArray autoHSV(env, hsvArray, 3); + float* values = autoHSV.ptr(); + for (int i = 0; i < 3; i++) { + values[i] = SkScalarToFloat(hsv[i]); + } +} + +static jint Color_HSVToColor(JNIEnv* env, jobject, jint alpha, jfloatArray hsvArray) +{ + AutoJavaFloatArray autoHSV(env, hsvArray, 3); +#ifdef SK_SCALAR_IS_FLOAT + SkScalar* hsv = autoHSV.ptr(); +#else + #error Need to convert float array to SkScalar array before calling the following function. +#endif + + return static_cast(SkHSVToColor(alpha, hsv)); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +static void Shader_safeUnref(SkShader* shader) { + SkSafeUnref(shader); +} + +static jlong Shader_getNativeFinalizer(JNIEnv*, jobject) { + return static_cast(reinterpret_cast(&Shader_safeUnref)); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jlong matrixPtr, jlong bitmapHandle, + jint tileModeX, jint tileModeY) { + const SkMatrix* matrix = reinterpret_cast(matrixPtr); + sk_sp image; + if (bitmapHandle) { + // Only pass a valid SkBitmap object to the constructor if the Bitmap exists. Otherwise, + // we'll pass an empty SkBitmap to avoid crashing/excepting for compatibility. + image = android::bitmap::toBitmap(bitmapHandle).makeImage(); + } + + if (!image.get()) { + SkBitmap bitmap; + image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode); + } + sk_sp shader = image->makeShader( + (SkTileMode)tileModeX, (SkTileMode)tileModeY); + ThrowIAE_IfNull(env, shader.get()); + + if (matrix) { + shader = shader->makeWithLocalMatrix(*matrix); + } + + return reinterpret_cast(shader.release()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +static std::vector convertColorLongs(JNIEnv* env, jlongArray colorArray) { + const size_t count = env->GetArrayLength(colorArray); + const jlong* colorValues = env->GetLongArrayElements(colorArray, nullptr); + + std::vector colors(count); + for (size_t i = 0; i < count; ++i) { + colors[i] = GraphicsJNI::convertColorLong(colorValues[i]); + } + + env->ReleaseLongArrayElements(colorArray, const_cast(colorValues), JNI_ABORT); + return colors; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +static jlong LinearGradient_create(JNIEnv* env, jobject, jlong matrixPtr, + jfloat x0, jfloat y0, jfloat x1, jfloat y1, jlongArray colorArray, + jfloatArray posArray, jint tileMode, jlong colorSpaceHandle) { + SkPoint pts[2]; + pts[0].set(x0, y0); + pts[1].set(x1, y1); + + std::vector colors = convertColorLongs(env, colorArray); + + AutoJavaFloatArray autoPos(env, posArray, colors.size()); +#ifdef SK_SCALAR_IS_FLOAT + SkScalar* pos = autoPos.ptr(); +#else + #error Need to convert float array to SkScalar array before calling the following function. +#endif + + sk_sp shader(SkGradientShader::MakeLinear(pts, &colors[0], + GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(), + static_cast(tileMode), sGradientShaderFlags, nullptr)); + ThrowIAE_IfNull(env, shader); + + const SkMatrix* matrix = reinterpret_cast(matrixPtr); + if (matrix) { + shader = shader->makeWithLocalMatrix(*matrix); + } + + return reinterpret_cast(shader.release()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +static jlong RadialGradient_create(JNIEnv* env, jobject, jlong matrixPtr, jfloat x, jfloat y, + jfloat radius, jlongArray colorArray, jfloatArray posArray, jint tileMode, + jlong colorSpaceHandle) { + SkPoint center; + center.set(x, y); + + std::vector colors = convertColorLongs(env, colorArray); + + AutoJavaFloatArray autoPos(env, posArray, colors.size()); +#ifdef SK_SCALAR_IS_FLOAT + SkScalar* pos = autoPos.ptr(); +#else + #error Need to convert float array to SkScalar array before calling the following function. +#endif + + sk_sp shader = SkGradientShader::MakeRadial(center, radius, &colors[0], + GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(), + static_cast(tileMode), sGradientShaderFlags, nullptr); + ThrowIAE_IfNull(env, shader); + + const SkMatrix* matrix = reinterpret_cast(matrixPtr); + if (matrix) { + shader = shader->makeWithLocalMatrix(*matrix); + } + + return reinterpret_cast(shader.release()); +} + +/////////////////////////////////////////////////////////////////////////////// + +static jlong SweepGradient_create(JNIEnv* env, jobject, jlong matrixPtr, jfloat x, jfloat y, + jlongArray colorArray, jfloatArray jpositions, jlong colorSpaceHandle) { + std::vector colors = convertColorLongs(env, colorArray); + + AutoJavaFloatArray autoPos(env, jpositions, colors.size()); +#ifdef SK_SCALAR_IS_FLOAT + SkScalar* pos = autoPos.ptr(); +#else + #error Need to convert float array to SkScalar array before calling the following function. +#endif + + sk_sp shader = SkGradientShader::MakeSweep(x, y, &colors[0], + GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(), + sGradientShaderFlags, nullptr); + ThrowIAE_IfNull(env, shader); + + const SkMatrix* matrix = reinterpret_cast(matrixPtr); + if (matrix) { + shader = shader->makeWithLocalMatrix(*matrix); + } + + return reinterpret_cast(shader.release()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +static jlong ComposeShader_create(JNIEnv* env, jobject o, jlong matrixPtr, + jlong shaderAHandle, jlong shaderBHandle, jint xfermodeHandle) { + const SkMatrix* matrix = reinterpret_cast(matrixPtr); + SkShader* shaderA = reinterpret_cast(shaderAHandle); + SkShader* shaderB = reinterpret_cast(shaderBHandle); + SkBlendMode mode = static_cast(xfermodeHandle); + sk_sp baseShader(SkShaders::Blend(mode, + sk_ref_sp(shaderA), sk_ref_sp(shaderB))); + + SkShader* shader; + + if (matrix) { + shader = baseShader->makeWithLocalMatrix(*matrix).release(); + } else { + shader = baseShader.release(); + } + return reinterpret_cast(shader); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +static jlong RuntimeShader_create(JNIEnv* env, jobject, jlong shaderFactory, jlong matrixPtr, + jbyteArray inputs, jlong colorSpaceHandle, jboolean isOpaque) { + SkRuntimeEffect* effect = reinterpret_cast(shaderFactory); + AutoJavaByteArray arInputs(env, inputs); + + sk_sp fData; + fData = SkData::MakeWithCopy(arInputs.ptr(), arInputs.length()); + const SkMatrix* matrix = reinterpret_cast(matrixPtr); + sk_sp shader = effect->makeShader(fData, nullptr, 0, matrix, isOpaque == JNI_TRUE); + ThrowIAE_IfNull(env, shader); + + return reinterpret_cast(shader.release()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +static jlong RuntimeShader_createShaderFactory(JNIEnv* env, jobject, jstring sksl) { + ScopedUtfChars strSksl(env, sksl); + sk_sp effect = std::get<0>(SkRuntimeEffect::Make(SkString(strSksl.c_str()))); + ThrowIAE_IfNull(env, effect); + + return reinterpret_cast(effect.release()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +static void Effect_safeUnref(SkRuntimeEffect* effect) { + SkSafeUnref(effect); +} + +static jlong RuntimeShader_getNativeFinalizer(JNIEnv*, jobject) { + return static_cast(reinterpret_cast(&Effect_safeUnref)); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +static const JNINativeMethod gColorMethods[] = { + { "nativeRGBToHSV", "(III[F)V", (void*)Color_RGBToHSV }, + { "nativeHSVToColor", "(I[F)I", (void*)Color_HSVToColor } +}; + +static const JNINativeMethod gShaderMethods[] = { + { "nativeGetFinalizer", "()J", (void*)Shader_getNativeFinalizer }, +}; + +static const JNINativeMethod gBitmapShaderMethods[] = { + { "nativeCreate", "(JJII)J", (void*)BitmapShader_constructor }, +}; + +static const JNINativeMethod gLinearGradientMethods[] = { + { "nativeCreate", "(JFFFF[J[FIJ)J", (void*)LinearGradient_create }, +}; + +static const JNINativeMethod gRadialGradientMethods[] = { + { "nativeCreate", "(JFFF[J[FIJ)J", (void*)RadialGradient_create }, +}; + +static const JNINativeMethod gSweepGradientMethods[] = { + { "nativeCreate", "(JFF[J[FJ)J", (void*)SweepGradient_create }, +}; + +static const JNINativeMethod gComposeShaderMethods[] = { + { "nativeCreate", "(JJJI)J", (void*)ComposeShader_create }, +}; + +static const JNINativeMethod gRuntimeShaderMethods[] = { + { "nativeGetFinalizer", "()J", (void*)RuntimeShader_getNativeFinalizer }, + { "nativeCreate", "(JJ[BJZ)J", (void*)RuntimeShader_create }, + { "nativeCreateShaderFactory", "(Ljava/lang/String;)J", + (void*)RuntimeShader_createShaderFactory }, +}; + +int register_android_graphics_Shader(JNIEnv* env) +{ + android::RegisterMethodsOrDie(env, "android/graphics/Color", gColorMethods, + NELEM(gColorMethods)); + android::RegisterMethodsOrDie(env, "android/graphics/Shader", gShaderMethods, + NELEM(gShaderMethods)); + android::RegisterMethodsOrDie(env, "android/graphics/BitmapShader", gBitmapShaderMethods, + NELEM(gBitmapShaderMethods)); + android::RegisterMethodsOrDie(env, "android/graphics/LinearGradient", gLinearGradientMethods, + NELEM(gLinearGradientMethods)); + android::RegisterMethodsOrDie(env, "android/graphics/RadialGradient", gRadialGradientMethods, + NELEM(gRadialGradientMethods)); + android::RegisterMethodsOrDie(env, "android/graphics/SweepGradient", gSweepGradientMethods, + NELEM(gSweepGradientMethods)); + android::RegisterMethodsOrDie(env, "android/graphics/ComposeShader", gComposeShaderMethods, + NELEM(gComposeShaderMethods)); + android::RegisterMethodsOrDie(env, "android/graphics/RuntimeShader", gRuntimeShaderMethods, + NELEM(gRuntimeShaderMethods)); + + return 0; +} diff --git a/libs/hwui/jni/TEST_MAPPING b/libs/hwui/jni/TEST_MAPPING new file mode 100644 index 000000000000..10bd0ee906fd --- /dev/null +++ b/libs/hwui/jni/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "CtsGraphicsTestCases" + } + ] +} diff --git a/libs/hwui/jni/Typeface.cpp b/libs/hwui/jni/Typeface.cpp new file mode 100644 index 000000000000..4ce56ba7444f --- /dev/null +++ b/libs/hwui/jni/Typeface.cpp @@ -0,0 +1,162 @@ +/* + * 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. + */ + +#include "jni.h" +#include "core_jni_helpers.h" + +#include "FontUtils.h" +#include "GraphicsJNI.h" +#include +#include +#include "SkTypeface.h" +#include +#include +#include + +using namespace android; + +static inline Typeface* toTypeface(jlong ptr) { + return reinterpret_cast(ptr); +} + +template static inline jlong toJLong(Ptr ptr) { + return reinterpret_cast(ptr); +} + +static jlong Typeface_createFromTypeface(JNIEnv* env, jobject, jlong familyHandle, jint style) { + Typeface* family = toTypeface(familyHandle); + Typeface* face = Typeface::createRelative(family, (Typeface::Style)style); + // TODO: the following logic shouldn't be necessary, the above should always succeed. + // Try to find the closest matching font, using the standard heuristic + if (NULL == face) { + face = Typeface::createRelative(family, (Typeface::Style)(style ^ Typeface::kItalic)); + } + for (int i = 0; NULL == face && i < 4; i++) { + face = Typeface::createRelative(family, (Typeface::Style)i); + } + return toJLong(face); +} + +static jlong Typeface_createFromTypefaceWithExactStyle(JNIEnv* env, jobject, jlong nativeInstance, + jint weight, jboolean italic) { + return toJLong(Typeface::createAbsolute(toTypeface(nativeInstance), weight, italic)); +} + +static jlong Typeface_createFromTypefaceWithVariation(JNIEnv* env, jobject, jlong familyHandle, + jobject listOfAxis) { + std::vector variations; + ListHelper list(env, listOfAxis); + for (jint i = 0; i < list.size(); i++) { + jobject axisObject = list.get(i); + if (axisObject == nullptr) { + continue; + } + AxisHelper axis(env, axisObject); + variations.push_back(minikin::FontVariation(axis.getTag(), axis.getStyleValue())); + } + return toJLong(Typeface::createFromTypefaceWithVariation(toTypeface(familyHandle), variations)); +} + +static jlong Typeface_createWeightAlias(JNIEnv* env, jobject, jlong familyHandle, jint weight) { + return toJLong(Typeface::createWithDifferentBaseWeight(toTypeface(familyHandle), weight)); +} + +static void releaseFunc(jlong ptr) { + delete toTypeface(ptr); +} + +// CriticalNative +static jlong Typeface_getReleaseFunc(CRITICAL_JNI_PARAMS) { + return toJLong(&releaseFunc); +} + +// CriticalNative +static jint Typeface_getStyle(CRITICAL_JNI_PARAMS_COMMA jlong faceHandle) { + return toTypeface(faceHandle)->fAPIStyle; +} + +// CriticalNative +static jint Typeface_getWeight(CRITICAL_JNI_PARAMS_COMMA jlong faceHandle) { + return toTypeface(faceHandle)->fStyle.weight(); +} + +static jlong Typeface_createFromArray(JNIEnv *env, jobject, jlongArray familyArray, + int weight, int italic) { + ScopedLongArrayRO families(env, familyArray); + std::vector> familyVec; + familyVec.reserve(families.size()); + for (size_t i = 0; i < families.size(); i++) { + FontFamilyWrapper* family = reinterpret_cast(families[i]); + familyVec.emplace_back(family->family); + } + return toJLong(Typeface::createFromFamilies(std::move(familyVec), weight, italic)); +} + +// CriticalNative +static void Typeface_setDefault(CRITICAL_JNI_PARAMS_COMMA jlong faceHandle) { + Typeface::setDefault(toTypeface(faceHandle)); + minikin::SystemFonts::registerDefault(toTypeface(faceHandle)->fFontCollection); +} + +static jobject Typeface_getSupportedAxes(JNIEnv *env, jobject, jlong faceHandle) { + Typeface* face = toTypeface(faceHandle); + const std::unordered_set& tagSet = face->fFontCollection->getSupportedTags(); + const size_t length = tagSet.size(); + if (length == 0) { + return nullptr; + } + std::vector tagVec(length); + int index = 0; + for (const auto& tag : tagSet) { + tagVec[index++] = tag; + } + std::sort(tagVec.begin(), tagVec.end()); + const jintArray result = env->NewIntArray(length); + env->SetIntArrayRegion(result, 0, length, tagVec.data()); + return result; +} + +static void Typeface_registerGenericFamily(JNIEnv *env, jobject, jstring familyName, jlong ptr) { + ScopedUtfChars familyNameChars(env, familyName); + minikin::SystemFonts::registerFallback(familyNameChars.c_str(), + toTypeface(ptr)->fFontCollection); +} + +/////////////////////////////////////////////////////////////////////////////// + +static const JNINativeMethod gTypefaceMethods[] = { + { "nativeCreateFromTypeface", "(JI)J", (void*)Typeface_createFromTypeface }, + { "nativeCreateFromTypefaceWithExactStyle", "(JIZ)J", + (void*)Typeface_createFromTypefaceWithExactStyle }, + { "nativeCreateFromTypefaceWithVariation", "(JLjava/util/List;)J", + (void*)Typeface_createFromTypefaceWithVariation }, + { "nativeCreateWeightAlias", "(JI)J", (void*)Typeface_createWeightAlias }, + { "nativeGetReleaseFunc", "()J", (void*)Typeface_getReleaseFunc }, + { "nativeGetStyle", "(J)I", (void*)Typeface_getStyle }, + { "nativeGetWeight", "(J)I", (void*)Typeface_getWeight }, + { "nativeCreateFromArray", "([JII)J", + (void*)Typeface_createFromArray }, + { "nativeSetDefault", "(J)V", (void*)Typeface_setDefault }, + { "nativeGetSupportedAxes", "(J)[I", (void*)Typeface_getSupportedAxes }, + { "nativeRegisterGenericFamily", "(Ljava/lang/String;J)V", + (void*)Typeface_registerGenericFamily }, +}; + +int register_android_graphics_Typeface(JNIEnv* env) +{ + return RegisterMethodsOrDie(env, "android/graphics/Typeface", gTypefaceMethods, + NELEM(gTypefaceMethods)); +} diff --git a/libs/hwui/jni/Utils.cpp b/libs/hwui/jni/Utils.cpp new file mode 100644 index 000000000000..17c194d04f84 --- /dev/null +++ b/libs/hwui/jni/Utils.cpp @@ -0,0 +1,161 @@ +/* + * 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 "Utils.h" +#include "SkUtils.h" +#include "SkData.h" + +#include + +using namespace android; + +AssetStreamAdaptor::AssetStreamAdaptor(Asset* asset) + : fAsset(asset) +{ +} + +bool AssetStreamAdaptor::rewind() { + off64_t pos = fAsset->seek(0, SEEK_SET); + if (pos == (off64_t)-1) { + SkDebugf("----- fAsset->seek(rewind) failed\n"); + return false; + } + return true; +} + +size_t AssetStreamAdaptor::getLength() const { + return fAsset->getLength(); +} + +bool AssetStreamAdaptor::isAtEnd() const { + return fAsset->getRemainingLength() == 0; +} + +SkStreamRewindable* AssetStreamAdaptor::onDuplicate() const { + // Cannot create a duplicate, since each AssetStreamAdaptor + // would be modifying the Asset. + //return new AssetStreamAdaptor(fAsset); + return NULL; +} + +bool AssetStreamAdaptor::hasPosition() const { + return fAsset->seek(0, SEEK_CUR) != -1; +} + +size_t AssetStreamAdaptor::getPosition() const { + const off64_t offset = fAsset->seek(0, SEEK_CUR); + if (offset == -1) { + SkDebugf("---- fAsset->seek(0, SEEK_CUR) failed\n"); + return 0; + } + + return offset; +} + +bool AssetStreamAdaptor::seek(size_t position) { + if (fAsset->seek(position, SEEK_SET) == -1) { + SkDebugf("---- fAsset->seek(0, SEEK_SET) failed\n"); + return false; + } + + return true; +} + +bool AssetStreamAdaptor::move(long offset) { + if (fAsset->seek(offset, SEEK_CUR) == -1) { + SkDebugf("---- fAsset->seek(%i, SEEK_CUR) failed\n", offset); + return false; + } + + return true; +} + +size_t AssetStreamAdaptor::read(void* buffer, size_t size) { + ssize_t amount; + + if (NULL == buffer) { + if (0 == size) { + return 0; + } + // asset->seek returns new total offset + // we want to return amount that was skipped + + off64_t oldOffset = fAsset->seek(0, SEEK_CUR); + if (-1 == oldOffset) { + SkDebugf("---- fAsset->seek(oldOffset) failed\n"); + return 0; + } + off64_t newOffset = fAsset->seek(size, SEEK_CUR); + if (-1 == newOffset) { + SkDebugf("---- fAsset->seek(%d) failed\n", size); + return 0; + } + amount = newOffset - oldOffset; + } else { + amount = fAsset->read(buffer, size); + } + + if (amount < 0) { + amount = 0; + } + return amount; +} + +SkMemoryStream* android::CopyAssetToStream(Asset* asset) { + if (NULL == asset) { + return NULL; + } + + const off64_t seekReturnVal = asset->seek(0, SEEK_SET); + if ((off64_t)-1 == seekReturnVal) { + SkDebugf("---- copyAsset: asset rewind failed\n"); + return NULL; + } + + const off64_t size = asset->getLength(); + if (size <= 0) { + SkDebugf("---- copyAsset: asset->getLength() returned %d\n", size); + return NULL; + } + + sk_sp data(SkData::MakeUninitialized(size)); + const off64_t len = asset->read(data->writable_data(), size); + if (len != size) { + SkDebugf("---- copyAsset: asset->read(%d) returned %d\n", size, len); + return NULL; + } + + return new SkMemoryStream(std::move(data)); +} + +jobject android::nullObjectReturn(const char msg[]) { + if (msg) { + SkDebugf("--- %s\n", msg); + } + return NULL; +} + +bool android::isSeekable(int descriptor) { + return ::lseek64(descriptor, 0, SEEK_CUR) != -1; +} + +JNIEnv* android::get_env_or_die(JavaVM* jvm) { + JNIEnv* env; + if (jvm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { + LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", jvm); + } + return env; +} diff --git a/libs/hwui/jni/Utils.h b/libs/hwui/jni/Utils.h new file mode 100644 index 000000000000..89255177ba2e --- /dev/null +++ b/libs/hwui/jni/Utils.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2006 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_UTILS_H_ +#define _ANDROID_GRAPHICS_UTILS_H_ + +#include "SkStream.h" + +#include +#include + +namespace android { + +class AssetStreamAdaptor : public SkStreamRewindable { +public: + explicit AssetStreamAdaptor(Asset*); + + virtual bool rewind(); + virtual size_t read(void* buffer, size_t size); + virtual bool hasLength() const { return true; } + virtual size_t getLength() const; + virtual bool hasPosition() const; + virtual size_t getPosition() const; + virtual bool seek(size_t position); + virtual bool move(long offset); + virtual bool isAtEnd() const; + +protected: + SkStreamRewindable* onDuplicate() const override; + +private: + Asset* fAsset; +}; + +/** + * Make a deep copy of the asset, and return it as a stream, or NULL if there + * was an error. + * FIXME: If we could "ref/reopen" the asset, we may not need to copy it here. + */ + +SkMemoryStream* CopyAssetToStream(Asset*); + +/** Restore the file descriptor's offset in our destructor + */ +class AutoFDSeek { +public: + explicit AutoFDSeek(int fd) : fFD(fd) { + fCurr = ::lseek(fd, 0, SEEK_CUR); + } + ~AutoFDSeek() { + if (fCurr >= 0) { + ::lseek(fFD, fCurr, SEEK_SET); + } + } +private: + int fFD; + off64_t fCurr; +}; + +jobject nullObjectReturn(const char msg[]); + +/** Check if the file descriptor is seekable. + */ +bool isSeekable(int descriptor); + +JNIEnv* get_env_or_die(JavaVM* jvm); + +}; // namespace android + +#endif // _ANDROID_GRAPHICS_UTILS_H_ diff --git a/libs/hwui/jni/YuvToJpegEncoder.cpp b/libs/hwui/jni/YuvToJpegEncoder.cpp new file mode 100644 index 000000000000..09adc824e520 --- /dev/null +++ b/libs/hwui/jni/YuvToJpegEncoder.cpp @@ -0,0 +1,270 @@ +#include "CreateJavaOutputStreamAdaptor.h" +#include "SkJPEGWriteUtility.h" +#include "YuvToJpegEncoder.h" +#include +#include + +#include "core_jni_helpers.h" + +#include + +YuvToJpegEncoder* YuvToJpegEncoder::create(int format, int* strides) { + // Only ImageFormat.NV21 and ImageFormat.YUY2 are supported + // for now. + if (format == HAL_PIXEL_FORMAT_YCrCb_420_SP) { + return new Yuv420SpToJpegEncoder(strides); + } else if (format == HAL_PIXEL_FORMAT_YCbCr_422_I) { + return new Yuv422IToJpegEncoder(strides); + } else { + return NULL; + } +} + +YuvToJpegEncoder::YuvToJpegEncoder(int* strides) : fStrides(strides) { +} + +struct ErrorMgr { + struct jpeg_error_mgr pub; + jmp_buf jmp; +}; + +void error_exit(j_common_ptr cinfo) { + ErrorMgr* err = (ErrorMgr*) cinfo->err; + (*cinfo->err->output_message) (cinfo); + longjmp(err->jmp, 1); +} + +bool YuvToJpegEncoder::encode(SkWStream* stream, void* inYuv, int width, + int height, int* offsets, int jpegQuality) { + jpeg_compress_struct cinfo; + ErrorMgr err; + skjpeg_destination_mgr sk_wstream(stream); + + cinfo.err = jpeg_std_error(&err.pub); + err.pub.error_exit = error_exit; + + if (setjmp(err.jmp)) { + jpeg_destroy_compress(&cinfo); + return false; + } + jpeg_create_compress(&cinfo); + + cinfo.dest = &sk_wstream; + + setJpegCompressStruct(&cinfo, width, height, jpegQuality); + + jpeg_start_compress(&cinfo, TRUE); + + compress(&cinfo, (uint8_t*) inYuv, offsets); + + jpeg_finish_compress(&cinfo); + + jpeg_destroy_compress(&cinfo); + + return true; +} + +void YuvToJpegEncoder::setJpegCompressStruct(jpeg_compress_struct* cinfo, + int width, int height, int quality) { + cinfo->image_width = width; + cinfo->image_height = height; + cinfo->input_components = 3; + cinfo->in_color_space = JCS_YCbCr; + jpeg_set_defaults(cinfo); + + jpeg_set_quality(cinfo, quality, TRUE); + jpeg_set_colorspace(cinfo, JCS_YCbCr); + cinfo->raw_data_in = TRUE; + cinfo->dct_method = JDCT_IFAST; + configSamplingFactors(cinfo); +} + +/////////////////////////////////////////////////////////////////// +Yuv420SpToJpegEncoder::Yuv420SpToJpegEncoder(int* strides) : + YuvToJpegEncoder(strides) { + fNumPlanes = 2; +} + +void Yuv420SpToJpegEncoder::compress(jpeg_compress_struct* cinfo, + uint8_t* yuv, int* offsets) { + SkDebugf("onFlyCompress"); + JSAMPROW y[16]; + JSAMPROW cb[8]; + JSAMPROW cr[8]; + JSAMPARRAY planes[3]; + planes[0] = y; + planes[1] = cb; + planes[2] = cr; + + int width = cinfo->image_width; + int height = cinfo->image_height; + uint8_t* yPlanar = yuv + offsets[0]; + uint8_t* vuPlanar = yuv + offsets[1]; //width * height; + uint8_t* uRows = new uint8_t [8 * (width >> 1)]; + uint8_t* vRows = new uint8_t [8 * (width >> 1)]; + + + // process 16 lines of Y and 8 lines of U/V each time. + while (cinfo->next_scanline < cinfo->image_height) { + //deitnerleave u and v + deinterleave(vuPlanar, uRows, vRows, cinfo->next_scanline, width, height); + + // Jpeg library ignores the rows whose indices are greater than height. + for (int i = 0; i < 16; i++) { + // y row + y[i] = yPlanar + (cinfo->next_scanline + i) * fStrides[0]; + + // construct u row and v row + if ((i & 1) == 0) { + // height and width are both halved because of downsampling + int offset = (i >> 1) * (width >> 1); + cb[i/2] = uRows + offset; + cr[i/2] = vRows + offset; + } + } + jpeg_write_raw_data(cinfo, planes, 16); + } + delete [] uRows; + delete [] vRows; + +} + +void Yuv420SpToJpegEncoder::deinterleave(uint8_t* vuPlanar, uint8_t* uRows, + uint8_t* vRows, int rowIndex, int width, int height) { + int numRows = (height - rowIndex) / 2; + if (numRows > 8) numRows = 8; + for (int row = 0; row < numRows; ++row) { + int offset = ((rowIndex >> 1) + row) * fStrides[1]; + uint8_t* vu = vuPlanar + offset; + for (int i = 0; i < (width >> 1); ++i) { + int index = row * (width >> 1) + i; + uRows[index] = vu[1]; + vRows[index] = vu[0]; + vu += 2; + } + } +} + +void Yuv420SpToJpegEncoder::configSamplingFactors(jpeg_compress_struct* cinfo) { + // cb and cr are horizontally downsampled and vertically downsampled as well. + cinfo->comp_info[0].h_samp_factor = 2; + cinfo->comp_info[0].v_samp_factor = 2; + cinfo->comp_info[1].h_samp_factor = 1; + cinfo->comp_info[1].v_samp_factor = 1; + cinfo->comp_info[2].h_samp_factor = 1; + cinfo->comp_info[2].v_samp_factor = 1; +} + +/////////////////////////////////////////////////////////////////////////////// +Yuv422IToJpegEncoder::Yuv422IToJpegEncoder(int* strides) : + YuvToJpegEncoder(strides) { + fNumPlanes = 1; +} + +void Yuv422IToJpegEncoder::compress(jpeg_compress_struct* cinfo, + uint8_t* yuv, int* offsets) { + SkDebugf("onFlyCompress_422"); + JSAMPROW y[16]; + JSAMPROW cb[16]; + JSAMPROW cr[16]; + JSAMPARRAY planes[3]; + planes[0] = y; + planes[1] = cb; + planes[2] = cr; + + int width = cinfo->image_width; + int height = cinfo->image_height; + uint8_t* yRows = new uint8_t [16 * width]; + uint8_t* uRows = new uint8_t [16 * (width >> 1)]; + uint8_t* vRows = new uint8_t [16 * (width >> 1)]; + + uint8_t* yuvOffset = yuv + offsets[0]; + + // process 16 lines of Y and 16 lines of U/V each time. + while (cinfo->next_scanline < cinfo->image_height) { + deinterleave(yuvOffset, yRows, uRows, vRows, cinfo->next_scanline, width, height); + + // Jpeg library ignores the rows whose indices are greater than height. + for (int i = 0; i < 16; i++) { + // y row + y[i] = yRows + i * width; + + // construct u row and v row + // width is halved because of downsampling + int offset = i * (width >> 1); + cb[i] = uRows + offset; + cr[i] = vRows + offset; + } + + jpeg_write_raw_data(cinfo, planes, 16); + } + delete [] yRows; + delete [] uRows; + delete [] vRows; +} + + +void Yuv422IToJpegEncoder::deinterleave(uint8_t* yuv, uint8_t* yRows, uint8_t* uRows, + uint8_t* vRows, int rowIndex, int width, int height) { + int numRows = height - rowIndex; + if (numRows > 16) numRows = 16; + for (int row = 0; row < numRows; ++row) { + uint8_t* yuvSeg = yuv + (rowIndex + row) * fStrides[0]; + for (int i = 0; i < (width >> 1); ++i) { + int indexY = row * width + (i << 1); + int indexU = row * (width >> 1) + i; + yRows[indexY] = yuvSeg[0]; + yRows[indexY + 1] = yuvSeg[2]; + uRows[indexU] = yuvSeg[1]; + vRows[indexU] = yuvSeg[3]; + yuvSeg += 4; + } + } +} + +void Yuv422IToJpegEncoder::configSamplingFactors(jpeg_compress_struct* cinfo) { + // cb and cr are horizontally downsampled and vertically downsampled as well. + cinfo->comp_info[0].h_samp_factor = 2; + cinfo->comp_info[0].v_samp_factor = 2; + cinfo->comp_info[1].h_samp_factor = 1; + cinfo->comp_info[1].v_samp_factor = 2; + cinfo->comp_info[2].h_samp_factor = 1; + cinfo->comp_info[2].v_samp_factor = 2; +} +/////////////////////////////////////////////////////////////////////////////// + +static jboolean YuvImage_compressToJpeg(JNIEnv* env, jobject, jbyteArray inYuv, + jint format, jint width, jint height, jintArray offsets, + jintArray strides, jint jpegQuality, jobject jstream, + jbyteArray jstorage) { + jbyte* yuv = env->GetByteArrayElements(inYuv, NULL); + SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage); + + jint* imgOffsets = env->GetIntArrayElements(offsets, NULL); + jint* imgStrides = env->GetIntArrayElements(strides, NULL); + YuvToJpegEncoder* encoder = YuvToJpegEncoder::create(format, imgStrides); + jboolean result = JNI_FALSE; + if (encoder != NULL) { + encoder->encode(strm, yuv, width, height, imgOffsets, jpegQuality); + delete encoder; + result = JNI_TRUE; + } + + env->ReleaseByteArrayElements(inYuv, yuv, 0); + env->ReleaseIntArrayElements(offsets, imgOffsets, 0); + env->ReleaseIntArrayElements(strides, imgStrides, 0); + delete strm; + return result; +} +/////////////////////////////////////////////////////////////////////////////// + +static const JNINativeMethod gYuvImageMethods[] = { + { "nativeCompressToJpeg", "([BIII[I[IILjava/io/OutputStream;[B)Z", + (void*)YuvImage_compressToJpeg } +}; + +int register_android_graphics_YuvImage(JNIEnv* env) +{ + return android::RegisterMethodsOrDie(env, "android/graphics/YuvImage", gYuvImageMethods, + NELEM(gYuvImageMethods)); +} diff --git a/libs/hwui/jni/YuvToJpegEncoder.h b/libs/hwui/jni/YuvToJpegEncoder.h new file mode 100644 index 000000000000..7e7b935df276 --- /dev/null +++ b/libs/hwui/jni/YuvToJpegEncoder.h @@ -0,0 +1,74 @@ +#ifndef _ANDROID_GRAPHICS_YUV_TO_JPEG_ENCODER_H_ +#define _ANDROID_GRAPHICS_YUV_TO_JPEG_ENCODER_H_ + +#include "SkTypes.h" +#include "SkStream.h" +extern "C" { + #include "jpeglib.h" + #include "jerror.h" +} + +class YuvToJpegEncoder { +public: + /** Create an encoder based on the YUV format. + * + * @param pixelFormat The yuv pixel format as defined in ui/PixelFormat.h. + * @param strides The number of row bytes in each image plane. + * @return an encoder based on the pixelFormat. + */ + static YuvToJpegEncoder* create(int pixelFormat, int* strides); + + explicit YuvToJpegEncoder(int* strides); + + /** Encode YUV data to jpeg, which is output to a stream. + * + * @param stream The jpeg output stream. + * @param inYuv The input yuv data. + * @param width Width of the the Yuv data in terms of pixels. + * @param height Height of the Yuv data in terms of pixels. + * @param offsets The offsets in each image plane with respect to inYuv. + * @param jpegQuality Picture quality in [0, 100]. + * @return true if successfully compressed the stream. + */ + bool encode(SkWStream* stream, void* inYuv, int width, + int height, int* offsets, int jpegQuality); + + virtual ~YuvToJpegEncoder() {} + +protected: + int fNumPlanes; + int* fStrides; + void setJpegCompressStruct(jpeg_compress_struct* cinfo, int width, + int height, int quality); + virtual void configSamplingFactors(jpeg_compress_struct* cinfo) = 0; + virtual void compress(jpeg_compress_struct* cinfo, + uint8_t* yuv, int* offsets) = 0; +}; + +class Yuv420SpToJpegEncoder : public YuvToJpegEncoder { +public: + explicit Yuv420SpToJpegEncoder(int* strides); + virtual ~Yuv420SpToJpegEncoder() {} + +private: + void configSamplingFactors(jpeg_compress_struct* cinfo); + void deinterleaveYuv(uint8_t* yuv, int width, int height, + uint8_t*& yPlanar, uint8_t*& uPlanar, uint8_t*& vPlanar); + void deinterleave(uint8_t* vuPlanar, uint8_t* uRows, uint8_t* vRows, + int rowIndex, int width, int height); + void compress(jpeg_compress_struct* cinfo, uint8_t* yuv, int* offsets); +}; + +class Yuv422IToJpegEncoder : public YuvToJpegEncoder { +public: + explicit Yuv422IToJpegEncoder(int* strides); + virtual ~Yuv422IToJpegEncoder() {} + +private: + void configSamplingFactors(jpeg_compress_struct* cinfo); + void compress(jpeg_compress_struct* cinfo, uint8_t* yuv, int* offsets); + void deinterleave(uint8_t* yuv, uint8_t* yRows, uint8_t* uRows, + uint8_t* vRows, int rowIndex, int width, int height); +}; + +#endif // _ANDROID_GRAPHICS_YUV_TO_JPEG_ENCODER_H_ diff --git a/libs/hwui/jni/android_graphics_Canvas.cpp b/libs/hwui/jni/android_graphics_Canvas.cpp new file mode 100644 index 000000000000..0ad3339ee05f --- /dev/null +++ b/libs/hwui/jni/android_graphics_Canvas.cpp @@ -0,0 +1,741 @@ +/* + * 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 "jni.h" +#include "GraphicsJNI.h" +#include "core_jni_helpers.h" + +#ifdef __ANDROID_ +#include +#else +#define __ANDROID_API_P__ 28 +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Bitmap.h" +#include "SkGraphics.h" +#include "SkRegion.h" +#include "SkVertices.h" + +namespace minikin { +class MeasuredText; +} // namespace minikin + +namespace android { + +namespace CanvasJNI { + +static Canvas* get_canvas(jlong canvasHandle) { + return reinterpret_cast(canvasHandle); +} + +static void delete_canvas(Canvas* canvas) { + delete canvas; +} + +static jlong getNativeFinalizer(JNIEnv* env, jobject clazz) { + return static_cast(reinterpret_cast(&delete_canvas)); +} + +// Native wrapper constructor used by Canvas(Bitmap) +static jlong initRaster(JNIEnv* env, jobject, jlong bitmapHandle) { + SkBitmap bitmap; + if (bitmapHandle != 0) { + bitmap::toBitmap(bitmapHandle).getSkBitmap(&bitmap); + } + return reinterpret_cast(Canvas::create_canvas(bitmap)); +} + +// Set the given bitmap as the new draw target (wrapped in a new SkCanvas), +// optionally copying canvas matrix & clip state. +static void setBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle) { + SkBitmap bitmap; + if (bitmapHandle != 0) { + bitmap::toBitmap(bitmapHandle).getSkBitmap(&bitmap); + } + get_canvas(canvasHandle)->setBitmap(bitmap); +} + +static jboolean isOpaque(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) { + return get_canvas(canvasHandle)->isOpaque() ? JNI_TRUE : JNI_FALSE; +} + +static jint getWidth(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) { + return static_cast(get_canvas(canvasHandle)->width()); +} + +static jint getHeight(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) { + return static_cast(get_canvas(canvasHandle)->height()); +} + +static jint save(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jint flagsHandle) { + SaveFlags::Flags flags = static_cast(flagsHandle); + return static_cast(get_canvas(canvasHandle)->save(flags)); +} + +static jint saveLayer(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat l, jfloat t, + jfloat r, jfloat b, jlong paintHandle, jint flagsHandle) { + Paint* paint = reinterpret_cast(paintHandle); + SaveFlags::Flags flags = static_cast(flagsHandle); + return static_cast(get_canvas(canvasHandle)->saveLayer(l, t, r, b, paint, flags)); +} + +static jint saveLayerAlpha(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat l, jfloat t, + jfloat r, jfloat b, jint alpha, jint flagsHandle) { + SaveFlags::Flags flags = static_cast(flagsHandle); + return static_cast(get_canvas(canvasHandle)->saveLayerAlpha(l, t, r, b, alpha, flags)); +} + +static jint saveUnclippedLayer(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jint l, jint t, jint r, jint b) { + return reinterpret_cast(get_canvas(canvasHandle)->saveUnclippedLayer(l, t, r, b)); +} + +static void restoreUnclippedLayer(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jint saveCount, jlong paintHandle) { + Paint* paint = reinterpret_cast(paintHandle); + get_canvas(canvasHandle)->restoreUnclippedLayer(saveCount, *paint); +} + +static bool restore(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) { + Canvas* canvas = get_canvas(canvasHandle); + if (canvas->getSaveCount() <= 1) { + return false; // cannot restore anymore + } + canvas->restore(); + return true; // success +} + +static void restoreToCount(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jint saveCount) { + Canvas* canvas = get_canvas(canvasHandle); + canvas->restoreToCount(saveCount); +} + +static jint getSaveCount(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) { + return static_cast(get_canvas(canvasHandle)->getSaveCount()); +} + +static void getMatrix(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong matrixHandle) { + SkMatrix* matrix = reinterpret_cast(matrixHandle); + get_canvas(canvasHandle)->getMatrix(matrix); +} + +static void setMatrix(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong matrixHandle) { + const SkMatrix* matrix = reinterpret_cast(matrixHandle); + get_canvas(canvasHandle)->setMatrix(matrix ? *matrix : SkMatrix::I()); +} + +static void concat(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong matrixHandle) { + const SkMatrix* matrix = reinterpret_cast(matrixHandle); + get_canvas(canvasHandle)->concat(*matrix); +} + +static void rotate(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat degrees) { + get_canvas(canvasHandle)->rotate(degrees); +} + +static void scale(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat sx, jfloat sy) { + get_canvas(canvasHandle)->scale(sx, sy); +} + +static void skew(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat sx, jfloat sy) { + get_canvas(canvasHandle)->skew(sx, sy); +} + +static void translate(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat dx, jfloat dy) { + get_canvas(canvasHandle)->translate(dx, dy); +} + +static jboolean getClipBounds(JNIEnv* env, jobject, jlong canvasHandle, jobject bounds) { + SkRect r; + SkIRect ir; + bool result = get_canvas(canvasHandle)->getClipBounds(&r); + + if (!result) { + r.setEmpty(); + } + r.round(&ir); + + (void)GraphicsJNI::irect_to_jrect(ir, env, bounds); + return result ? JNI_TRUE : JNI_FALSE; +} + +static jboolean quickRejectRect(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, + jfloat left, jfloat top, jfloat right, jfloat bottom) { + bool result = get_canvas(canvasHandle)->quickRejectRect(left, top, right, bottom); + return result ? JNI_TRUE : JNI_FALSE; +} + +static jboolean quickRejectPath(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong pathHandle) { + SkPath* path = reinterpret_cast(pathHandle); + bool result = get_canvas(canvasHandle)->quickRejectPath(*path); + return result ? JNI_TRUE : JNI_FALSE; +} + +// SkRegion::Op and SkClipOp are numerically identical, so we can freely cast +// from one to the other (though SkClipOp is destined to become a strict subset) +static_assert(SkRegion::kDifference_Op == static_cast(SkClipOp::kDifference), ""); +static_assert(SkRegion::kIntersect_Op == static_cast(SkClipOp::kIntersect), ""); +static_assert(SkRegion::kUnion_Op == static_cast(SkClipOp::kUnion_deprecated), ""); +static_assert(SkRegion::kXOR_Op == static_cast(SkClipOp::kXOR_deprecated), ""); +static_assert(SkRegion::kReverseDifference_Op == static_cast(SkClipOp::kReverseDifference_deprecated), ""); +static_assert(SkRegion::kReplace_Op == static_cast(SkClipOp::kReplace_deprecated), ""); + +static SkClipOp opHandleToClipOp(jint opHandle) { + // The opHandle is defined in Canvas.java to be Region::Op + SkRegion::Op rgnOp = static_cast(opHandle); + + // In the future, when we no longer support the wide range of ops (e.g. Union, Xor) + // this function can perform a range check and throw an unsupported-exception. + // e.g. if (rgnOp != kIntersect && rgnOp != kDifference) throw... + + // Skia now takes a different type, SkClipOp, as the parameter to clipping calls + // This type is binary compatible with SkRegion::Op, so a static_cast<> is safe. + return static_cast(rgnOp); +} + +static jboolean clipRect(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat l, jfloat t, + jfloat r, jfloat b, jint opHandle) { + bool nonEmptyClip = get_canvas(canvasHandle)->clipRect(l, t, r, b, + opHandleToClipOp(opHandle)); + return nonEmptyClip ? JNI_TRUE : JNI_FALSE; +} + +static jboolean clipPath(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong pathHandle, + jint opHandle) { + SkPath* path = reinterpret_cast(pathHandle); + bool nonEmptyClip = get_canvas(canvasHandle)->clipPath(path, opHandleToClipOp(opHandle)); + return nonEmptyClip ? JNI_TRUE : JNI_FALSE; +} + +static void drawColor(JNIEnv* env, jobject, jlong canvasHandle, jint color, jint modeHandle) { + SkBlendMode mode = static_cast(modeHandle); + get_canvas(canvasHandle)->drawColor(color, mode); +} + +static void drawColorLong(JNIEnv* env, jobject, jlong canvasHandle, jlong colorSpaceHandle, + jlong colorLong, jint modeHandle) { + SkColor4f color = GraphicsJNI::convertColorLong(colorLong); + sk_sp cs = GraphicsJNI::getNativeColorSpace(colorSpaceHandle); + SkPaint p; + p.setColor4f(color, cs.get()); + + SkBlendMode mode = static_cast(modeHandle); + p.setBlendMode(mode); + get_canvas(canvasHandle)->drawPaint(p); +} + +static void drawPaint(JNIEnv* env, jobject, jlong canvasHandle, jlong paintHandle) { + Paint* paint = reinterpret_cast(paintHandle); + get_canvas(canvasHandle)->drawPaint(*paint); +} + +static void drawPoint(JNIEnv*, jobject, jlong canvasHandle, jfloat x, jfloat y, + jlong paintHandle) { + const Paint* paint = reinterpret_cast(paintHandle); + get_canvas(canvasHandle)->drawPoint(x, y, *paint); +} + +static void drawPoints(JNIEnv* env, jobject, jlong canvasHandle, jfloatArray jptsArray, + jint offset, jint count, jlong paintHandle) { + NPE_CHECK_RETURN_VOID(env, jptsArray); + AutoJavaFloatArray autoPts(env, jptsArray); + float* floats = autoPts.ptr(); + const int length = autoPts.length(); + + if ((offset | count) < 0 || offset + count > length) { + doThrowAIOOBE(env); + return; + } + + const Paint* paint = reinterpret_cast(paintHandle); + get_canvas(canvasHandle)->drawPoints(floats + offset, count, *paint); +} + +static void drawLine(JNIEnv* env, jobject, jlong canvasHandle, jfloat startX, jfloat startY, + jfloat stopX, jfloat stopY, jlong paintHandle) { + Paint* paint = reinterpret_cast(paintHandle); + get_canvas(canvasHandle)->drawLine(startX, startY, stopX, stopY, *paint); +} + +static void drawLines(JNIEnv* env, jobject, jlong canvasHandle, jfloatArray jptsArray, + jint offset, jint count, jlong paintHandle) { + NPE_CHECK_RETURN_VOID(env, jptsArray); + AutoJavaFloatArray autoPts(env, jptsArray); + float* floats = autoPts.ptr(); + const int length = autoPts.length(); + + if ((offset | count) < 0 || offset + count > length) { + doThrowAIOOBE(env); + return; + } + + const Paint* paint = reinterpret_cast(paintHandle); + get_canvas(canvasHandle)->drawLines(floats + offset, count, *paint); +} + +static void drawRect(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top, + jfloat right, jfloat bottom, jlong paintHandle) { + const Paint* paint = reinterpret_cast(paintHandle); + get_canvas(canvasHandle)->drawRect(left, top, right, bottom, *paint); +} + +static void drawDoubleRoundRectXY(JNIEnv* env, jobject, jlong canvasHandle, jfloat outerLeft, + jfloat outerTop, jfloat outerRight, jfloat outerBottom, jfloat outerRx, + jfloat outerRy, jfloat innerLeft, jfloat innerTop, jfloat innerRight, + jfloat innerBottom, jfloat innerRx, jfloat innerRy, jlong paintHandle) { + const Paint* paint = reinterpret_cast(paintHandle); + get_canvas(canvasHandle)->drawDoubleRoundRectXY( + outerLeft, outerTop, outerRight, outerBottom, outerRx, outerRy, + innerLeft, innerTop, innerRight, innerBottom, innerRx, innerRy, *paint); +} + +static void drawDoubleRoundRectRadii(JNIEnv* env, jobject, jlong canvasHandle, jfloat outerLeft, + jfloat outerTop, jfloat outerRight, jfloat outerBottom, jfloatArray jouterRadii, + jfloat innerLeft, jfloat innerTop, jfloat innerRight, + jfloat innerBottom, jfloatArray jinnerRadii, jlong paintHandle) { + const Paint* paint = reinterpret_cast(paintHandle); + + float outerRadii[8]; + float innerRadii[8]; + env->GetFloatArrayRegion(jouterRadii, 0, 8, outerRadii); + env->GetFloatArrayRegion(jinnerRadii, 0, 8, innerRadii); + get_canvas(canvasHandle)->drawDoubleRoundRectRadii( + outerLeft, outerTop, outerRight, outerBottom, outerRadii, + innerLeft, innerTop, innerRight, innerBottom, innerRadii, *paint); + +} + +static void drawRegion(JNIEnv* env, jobject, jlong canvasHandle, jlong regionHandle, + jlong paintHandle) { + const SkRegion* region = reinterpret_cast(regionHandle); + const Paint* paint = reinterpret_cast(paintHandle); + get_canvas(canvasHandle)->drawRegion(*region, *paint); +} + +static void drawRoundRect(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top, + jfloat right, jfloat bottom, jfloat rx, jfloat ry, jlong paintHandle) { + const Paint* paint = reinterpret_cast(paintHandle); + get_canvas(canvasHandle)->drawRoundRect(left, top, right, bottom, rx, ry, *paint); +} + +static void drawCircle(JNIEnv* env, jobject, jlong canvasHandle, jfloat cx, jfloat cy, + jfloat radius, jlong paintHandle) { + const Paint* paint = reinterpret_cast(paintHandle); + get_canvas(canvasHandle)->drawCircle(cx, cy, radius, *paint); +} + +static void drawOval(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top, + jfloat right, jfloat bottom, jlong paintHandle) { + const Paint* paint = reinterpret_cast(paintHandle); + get_canvas(canvasHandle)->drawOval(left, top, right, bottom, *paint); +} + +static void drawArc(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top, + jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle, + jboolean useCenter, jlong paintHandle) { + const Paint* paint = reinterpret_cast(paintHandle); + get_canvas(canvasHandle)->drawArc(left, top, right, bottom, startAngle, sweepAngle, + useCenter, *paint); +} + +static void drawPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle, + jlong paintHandle) { + const SkPath* path = reinterpret_cast(pathHandle); + const Paint* paint = reinterpret_cast(paintHandle); + get_canvas(canvasHandle)->drawPath(*path, *paint); +} + +static void drawVertices(JNIEnv* env, jobject, jlong canvasHandle, + jint modeHandle, jint floatCount, + jfloatArray jverts, jint vertIndex, + jfloatArray jtexs, jint texIndex, + jintArray jcolors, jint colorIndex, + jshortArray jindices, jint indexIndex, + jint indexCount, jlong paintHandle) { + + const int vertexCount = floatCount >> 1; // 2 floats per SkPoint + + AutoJavaFloatArray vertA(env, jverts, vertIndex + floatCount); + AutoJavaFloatArray texA(env, jtexs, texIndex + floatCount); + AutoJavaIntArray colorA(env, jcolors, colorIndex + vertexCount); + AutoJavaShortArray indexA(env, jindices, indexIndex + indexCount); + + const float* verts = vertA.ptr() + vertIndex; + const float* texs = texA.ptr() + vertIndex; + const int* colors = NULL; + const uint16_t* indices = NULL; + + if (jcolors != NULL) { + colors = colorA.ptr() + colorIndex; + } + if (jindices != NULL) { + indices = (const uint16_t*)(indexA.ptr() + indexIndex); + } + + SkVertices::VertexMode mode = static_cast(modeHandle); + const Paint* paint = reinterpret_cast(paintHandle); + get_canvas(canvasHandle)->drawVertices(SkVertices::MakeCopy(mode, vertexCount, + reinterpret_cast(verts), + reinterpret_cast(texs), + reinterpret_cast(colors), + indexCount, indices).get(), + SkBlendMode::kModulate, *paint); +} + +static void drawNinePatch(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle, + jlong chunkHandle, jfloat left, jfloat top, jfloat right, jfloat bottom, + jlong paintHandle, jint dstDensity, jint srcDensity) { + + Canvas* canvas = get_canvas(canvasHandle); + Bitmap& bitmap = android::bitmap::toBitmap(bitmapHandle); + const android::Res_png_9patch* chunk = reinterpret_cast(chunkHandle); + const Paint* paint = reinterpret_cast(paintHandle); + + if (CC_LIKELY(dstDensity == srcDensity || dstDensity == 0 || srcDensity == 0)) { + canvas->drawNinePatch(bitmap, *chunk, left, top, right, bottom, paint); + } else { + canvas->save(SaveFlags::MatrixClip); + + SkScalar scale = dstDensity / (float)srcDensity; + canvas->translate(left, top); + canvas->scale(scale, scale); + + Paint filteredPaint; + if (paint) { + filteredPaint = *paint; + } + filteredPaint.setFilterQuality(kLow_SkFilterQuality); + + canvas->drawNinePatch(bitmap, *chunk, 0, 0, (right-left)/scale, (bottom-top)/scale, + &filteredPaint); + + canvas->restore(); + } +} + +static void drawBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle, + jfloat left, jfloat top, jlong paintHandle, jint canvasDensity, + jint screenDensity, jint bitmapDensity) { + Canvas* canvas = get_canvas(canvasHandle); + Bitmap& bitmap = android::bitmap::toBitmap(bitmapHandle); + const Paint* paint = reinterpret_cast(paintHandle); + + if (canvasDensity == bitmapDensity || canvasDensity == 0 || bitmapDensity == 0) { + if (screenDensity != 0 && screenDensity != bitmapDensity) { + Paint filteredPaint; + if (paint) { + filteredPaint = *paint; + } + filteredPaint.setFilterQuality(kLow_SkFilterQuality); + canvas->drawBitmap(bitmap, left, top, &filteredPaint); + } else { + canvas->drawBitmap(bitmap, left, top, paint); + } + } else { + canvas->save(SaveFlags::MatrixClip); + SkScalar scale = canvasDensity / (float)bitmapDensity; + canvas->translate(left, top); + canvas->scale(scale, scale); + + Paint filteredPaint; + if (paint) { + filteredPaint = *paint; + } + filteredPaint.setFilterQuality(kLow_SkFilterQuality); + + canvas->drawBitmap(bitmap, 0, 0, &filteredPaint); + canvas->restore(); + } +} + +static void drawBitmapMatrix(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle, + jlong matrixHandle, jlong paintHandle) { + const SkMatrix* matrix = reinterpret_cast(matrixHandle); + const Paint* paint = reinterpret_cast(paintHandle); + Bitmap& bitmap = android::bitmap::toBitmap(bitmapHandle); + get_canvas(canvasHandle)->drawBitmap(bitmap, *matrix, paint); +} + +static void drawBitmapRect(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle, + float srcLeft, float srcTop, float srcRight, float srcBottom, + float dstLeft, float dstTop, float dstRight, float dstBottom, + jlong paintHandle, jint screenDensity, jint bitmapDensity) { + Canvas* canvas = get_canvas(canvasHandle); + const Paint* paint = reinterpret_cast(paintHandle); + + Bitmap& bitmap = android::bitmap::toBitmap(bitmapHandle); + if (screenDensity != 0 && screenDensity != bitmapDensity) { + Paint filteredPaint; + if (paint) { + filteredPaint = *paint; + } + filteredPaint.setFilterQuality(kLow_SkFilterQuality); + canvas->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom, + dstLeft, dstTop, dstRight, dstBottom, &filteredPaint); + } else { + canvas->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom, + dstLeft, dstTop, dstRight, dstBottom, paint); + } +} + +static void drawBitmapArray(JNIEnv* env, jobject, jlong canvasHandle, + jintArray jcolors, jint offset, jint stride, + jfloat x, jfloat y, jint width, jint height, + jboolean hasAlpha, jlong paintHandle) { + // Note: If hasAlpha is false, kRGB_565_SkColorType will be used, which will + // correct the alphaType to kOpaque_SkAlphaType. + SkImageInfo info = SkImageInfo::Make(width, height, + hasAlpha ? kN32_SkColorType : kRGB_565_SkColorType, + kPremul_SkAlphaType); + SkBitmap bitmap; + bitmap.setInfo(info); + sk_sp androidBitmap = Bitmap::allocateHeapBitmap(&bitmap); + if (!androidBitmap) { + return; + } + + if (!GraphicsJNI::SetPixels(env, jcolors, offset, stride, 0, 0, width, height, &bitmap)) { + return; + } + + const Paint* paint = reinterpret_cast(paintHandle); + get_canvas(canvasHandle)->drawBitmap(*androidBitmap, x, y, paint); +} + +static void drawBitmapMesh(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle, + jint meshWidth, jint meshHeight, jfloatArray jverts, + jint vertIndex, jintArray jcolors, jint colorIndex, jlong paintHandle) { + if (Canvas::GetApiLevel() < __ANDROID_API_P__) { + // Before P we forgot to respect these. Now that we do respect them, explicitly + // zero them for backward compatibility. + vertIndex = 0; + colorIndex = 0; + } + + const int ptCount = (meshWidth + 1) * (meshHeight + 1); + AutoJavaFloatArray vertA(env, jverts, vertIndex + (ptCount << 1)); + AutoJavaIntArray colorA(env, jcolors, colorIndex + ptCount); + + const Paint* paint = reinterpret_cast(paintHandle); + Bitmap& bitmap = android::bitmap::toBitmap(bitmapHandle); + get_canvas(canvasHandle)->drawBitmapMesh(bitmap, meshWidth, meshHeight, + vertA.ptr() + vertIndex*2, + colorA.ptr() + colorIndex, paint); +} + +static void drawTextChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray charArray, + jint index, jint count, jfloat x, jfloat y, jint bidiFlags, + jlong paintHandle) { + Paint* paint = reinterpret_cast(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + ScopedCharArrayRO text(env, charArray); + // drawTextString and drawTextChars doesn't use context info + get_canvas(canvasHandle)->drawText( + text.get() + index, count, // text buffer + 0, count, // draw range + 0, count, // context range + x, y, // draw position + static_cast(bidiFlags), *paint, typeface, nullptr /* measured text */); +} + +static void drawTextString(JNIEnv* env, jobject, jlong canvasHandle, jstring strObj, + jint start, jint end, jfloat x, jfloat y, jint bidiFlags, + jlong paintHandle) { + ScopedStringChars text(env, strObj); + Paint* paint = reinterpret_cast(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + const int count = end - start; + // drawTextString and drawTextChars doesn't use context info + get_canvas(canvasHandle)->drawText( + text.get() + start, count, // text buffer + 0, count, // draw range + 0, count, // context range + x, y, // draw position + static_cast(bidiFlags), *paint, typeface, nullptr /* measured text */); +} + +static void drawTextRunChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray charArray, + jint index, jint count, jint contextIndex, jint contextCount, + jfloat x, jfloat y, jboolean isRtl, jlong paintHandle, + jlong mtHandle) { + minikin::MeasuredText* mt = reinterpret_cast(mtHandle); + const minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR; + + ScopedCharArrayRO text(env, charArray); + Paint* paint = reinterpret_cast(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + get_canvas(canvasHandle)->drawText( + text.get(), text.size(), // text buffer + index, count, // draw range + contextIndex, contextCount, // context range, + x, y, // draw position + bidiFlags, *paint, typeface, mt); +} + +static void drawTextRunString(JNIEnv* env, jobject obj, jlong canvasHandle, jstring strObj, + jint start, jint end, jint contextStart, jint contextEnd, + jfloat x, jfloat y, jboolean isRtl, jlong paintHandle) { + const minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR; + + ScopedStringChars text(env, strObj); + Paint* paint = reinterpret_cast(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + get_canvas(canvasHandle)->drawText( + text.get(), text.size(), // text buffer + start, end - start, // draw range + contextStart, contextEnd - contextStart, // context range + x, y, // draw position + bidiFlags, *paint, typeface, nullptr /* measured text */); +} + +static void drawTextOnPathChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text, + jint index, jint count, jlong pathHandle, jfloat hOffset, + jfloat vOffset, jint bidiFlags, jlong paintHandle) { + SkPath* path = reinterpret_cast(pathHandle); + Paint* paint = reinterpret_cast(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + + jchar* jchars = env->GetCharArrayElements(text, NULL); + + get_canvas(canvasHandle)->drawTextOnPath(jchars + index, count, + static_cast(bidiFlags), *path, hOffset, vOffset, *paint, typeface); + + env->ReleaseCharArrayElements(text, jchars, 0); +} + +static void drawTextOnPathString(JNIEnv* env, jobject, jlong canvasHandle, jstring text, + jlong pathHandle, jfloat hOffset, jfloat vOffset, + jint bidiFlags, jlong paintHandle) { + SkPath* path = reinterpret_cast(pathHandle); + Paint* paint = reinterpret_cast(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + + const jchar* jchars = env->GetStringChars(text, NULL); + int count = env->GetStringLength(text); + + get_canvas(canvasHandle)->drawTextOnPath(jchars, count, static_cast(bidiFlags), + *path, hOffset, vOffset, *paint, typeface); + + env->ReleaseStringChars(text, jchars); +} + +static void setPaintFilter(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong filterHandle) { + PaintFilter* paintFilter = reinterpret_cast(filterHandle); + get_canvas(canvasHandle)->setPaintFilter(sk_ref_sp(paintFilter)); +} + +static void freeCaches(JNIEnv* env, jobject) { + SkGraphics::PurgeFontCache(); +} + +static void freeTextLayoutCaches(JNIEnv* env, jobject) { + minikin::Layout::purgeCaches(); +} + +static void setCompatibilityVersion(JNIEnv* env, jobject, jint apiLevel) { + Canvas::setCompatibilityVersion(apiLevel); +} + + +}; // namespace CanvasJNI + +static const JNINativeMethod gMethods[] = { + {"nGetNativeFinalizer", "()J", (void*) CanvasJNI::getNativeFinalizer}, + {"nFreeCaches", "()V", (void*) CanvasJNI::freeCaches}, + {"nFreeTextLayoutCaches", "()V", (void*) CanvasJNI::freeTextLayoutCaches}, + {"nSetCompatibilityVersion", "(I)V", (void*) CanvasJNI::setCompatibilityVersion}, + + // ------------ @FastNative ---------------- + {"nInitRaster", "(J)J", (void*) CanvasJNI::initRaster}, + {"nSetBitmap", "(JJ)V", (void*) CanvasJNI::setBitmap}, + {"nGetClipBounds","(JLandroid/graphics/Rect;)Z", (void*) CanvasJNI::getClipBounds}, + + // ------------ @CriticalNative ---------------- + {"nIsOpaque","(J)Z", (void*) CanvasJNI::isOpaque}, + {"nGetWidth","(J)I", (void*) CanvasJNI::getWidth}, + {"nGetHeight","(J)I", (void*) CanvasJNI::getHeight}, + {"nSave","(JI)I", (void*) CanvasJNI::save}, + {"nSaveLayer","(JFFFFJI)I", (void*) CanvasJNI::saveLayer}, + {"nSaveLayerAlpha","(JFFFFII)I", (void*) CanvasJNI::saveLayerAlpha}, + {"nSaveUnclippedLayer","(JIIII)I", (void*) CanvasJNI::saveUnclippedLayer}, + {"nRestoreUnclippedLayer","(JIJ)V", (void*) CanvasJNI::restoreUnclippedLayer}, + {"nGetSaveCount","(J)I", (void*) CanvasJNI::getSaveCount}, + {"nRestore","(J)Z", (void*) CanvasJNI::restore}, + {"nRestoreToCount","(JI)V", (void*) CanvasJNI::restoreToCount}, + {"nGetMatrix", "(JJ)V", (void*)CanvasJNI::getMatrix}, + {"nSetMatrix","(JJ)V", (void*) CanvasJNI::setMatrix}, + {"nConcat","(JJ)V", (void*) CanvasJNI::concat}, + {"nRotate","(JF)V", (void*) CanvasJNI::rotate}, + {"nScale","(JFF)V", (void*) CanvasJNI::scale}, + {"nSkew","(JFF)V", (void*) CanvasJNI::skew}, + {"nTranslate","(JFF)V", (void*) CanvasJNI::translate}, + {"nQuickReject","(JJ)Z", (void*) CanvasJNI::quickRejectPath}, + {"nQuickReject","(JFFFF)Z", (void*)CanvasJNI::quickRejectRect}, + {"nClipRect","(JFFFFI)Z", (void*) CanvasJNI::clipRect}, + {"nClipPath","(JJI)Z", (void*) CanvasJNI::clipPath}, + {"nSetDrawFilter", "(JJ)V", (void*) CanvasJNI::setPaintFilter}, +}; + +// If called from Canvas these are regular JNI +// If called from DisplayListCanvas they are @FastNative +static const JNINativeMethod gDrawMethods[] = { + {"nDrawColor","(JII)V", (void*) CanvasJNI::drawColor}, + {"nDrawColor","(JJJI)V", (void*) CanvasJNI::drawColorLong}, + {"nDrawPaint","(JJ)V", (void*) CanvasJNI::drawPaint}, + {"nDrawPoint", "(JFFJ)V", (void*) CanvasJNI::drawPoint}, + {"nDrawPoints", "(J[FIIJ)V", (void*) CanvasJNI::drawPoints}, + {"nDrawLine", "(JFFFFJ)V", (void*) CanvasJNI::drawLine}, + {"nDrawLines", "(J[FIIJ)V", (void*) CanvasJNI::drawLines}, + {"nDrawRect","(JFFFFJ)V", (void*) CanvasJNI::drawRect}, + {"nDrawRegion", "(JJJ)V", (void*) CanvasJNI::drawRegion }, + {"nDrawRoundRect","(JFFFFFFJ)V", (void*) CanvasJNI::drawRoundRect}, + {"nDrawDoubleRoundRect", "(JFFFFFFFFFFFFJ)V", (void*) CanvasJNI::drawDoubleRoundRectXY}, + {"nDrawDoubleRoundRect", "(JFFFF[FFFFF[FJ)V", (void*) CanvasJNI::drawDoubleRoundRectRadii}, + {"nDrawCircle","(JFFFJ)V", (void*) CanvasJNI::drawCircle}, + {"nDrawOval","(JFFFFJ)V", (void*) CanvasJNI::drawOval}, + {"nDrawArc","(JFFFFFFZJ)V", (void*) CanvasJNI::drawArc}, + {"nDrawPath","(JJJ)V", (void*) CanvasJNI::drawPath}, + {"nDrawVertices", "(JII[FI[FI[II[SIIJ)V", (void*)CanvasJNI::drawVertices}, + {"nDrawNinePatch", "(JJJFFFFJII)V", (void*)CanvasJNI::drawNinePatch}, + {"nDrawBitmapMatrix", "(JJJJ)V", (void*)CanvasJNI::drawBitmapMatrix}, + {"nDrawBitmapMesh", "(JJII[FI[IIJ)V", (void*)CanvasJNI::drawBitmapMesh}, + {"nDrawBitmap","(JJFFJIII)V", (void*) CanvasJNI::drawBitmap}, + {"nDrawBitmap","(JJFFFFFFFFJII)V", (void*) CanvasJNI::drawBitmapRect}, + {"nDrawBitmap", "(J[IIIFFIIZJ)V", (void*)CanvasJNI::drawBitmapArray}, + {"nDrawText","(J[CIIFFIJ)V", (void*) CanvasJNI::drawTextChars}, + {"nDrawText","(JLjava/lang/String;IIFFIJ)V", (void*) CanvasJNI::drawTextString}, + {"nDrawTextRun","(J[CIIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunChars}, + {"nDrawTextRun","(JLjava/lang/String;IIIIFFZJ)V", (void*) CanvasJNI::drawTextRunString}, + {"nDrawTextOnPath","(J[CIIJFFIJ)V", (void*) CanvasJNI::drawTextOnPathChars}, + {"nDrawTextOnPath","(JLjava/lang/String;JFFIJ)V", (void*) CanvasJNI::drawTextOnPathString}, +}; + +int register_android_graphics_Canvas(JNIEnv* env) { + int ret = 0; + ret |= RegisterMethodsOrDie(env, "android/graphics/Canvas", gMethods, NELEM(gMethods)); + ret |= RegisterMethodsOrDie(env, "android/graphics/BaseCanvas", gDrawMethods, NELEM(gDrawMethods)); + ret |= RegisterMethodsOrDie(env, "android/graphics/BaseRecordingCanvas", gDrawMethods, NELEM(gDrawMethods)); + return ret; + +} + +}; // namespace android diff --git a/libs/hwui/jni/android_graphics_ColorSpace.cpp b/libs/hwui/jni/android_graphics_ColorSpace.cpp new file mode 100644 index 000000000000..7648fd021d18 --- /dev/null +++ b/libs/hwui/jni/android_graphics_ColorSpace.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2019 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 "jni.h" +#include "GraphicsJNI.h" +#include "core_jni_helpers.h" + +#include "SkColor.h" +#include "SkColorSpace.h" + +using namespace android; + +static skcms_Matrix3x3 getNativeXYZMatrix(JNIEnv* env, jfloatArray xyzD50) { + skcms_Matrix3x3 xyzMatrix; + jfloat* array = env->GetFloatArrayElements(xyzD50, NULL); + xyzMatrix.vals[0][0] = array[0]; + xyzMatrix.vals[1][0] = array[1]; + xyzMatrix.vals[2][0] = array[2]; + xyzMatrix.vals[0][1] = array[3]; + xyzMatrix.vals[1][1] = array[4]; + xyzMatrix.vals[2][1] = array[5]; + xyzMatrix.vals[0][2] = array[6]; + xyzMatrix.vals[1][2] = array[7]; + xyzMatrix.vals[2][2] = array[8]; + env->ReleaseFloatArrayElements(xyzD50, array, 0); + return xyzMatrix; +} + +/////////////////////////////////////////////////////////////////////////////// + +static float halfToFloat(uint16_t bits) { + __fp16 h; + memcpy(&h, &bits, 2); + return (float)h; +} + +SkColor4f GraphicsJNI::convertColorLong(jlong color) { + if ((color & 0x3f) == 0) { + // This corresponds to sRGB, which is treated differently than the rest. + uint8_t a = color >> 56 & 0xff; + uint8_t r = color >> 48 & 0xff; + uint8_t g = color >> 40 & 0xff; + uint8_t b = color >> 32 & 0xff; + SkColor c = SkColorSetARGB(a, r, g, b); + return SkColor4f::FromColor(c); + } + + // These match the implementation of android.graphics.Color#red(long) etc. + float r = halfToFloat((uint16_t)(color >> 48 & 0xffff)); + float g = halfToFloat((uint16_t)(color >> 32 & 0xffff)); + float b = halfToFloat((uint16_t)(color >> 16 & 0xffff)); + float a = (color >> 6 & 0x3ff) / 1023.0f; + + return SkColor4f{r, g, b, a}; +} + +sk_sp GraphicsJNI::getNativeColorSpace(jlong colorSpaceHandle) { + if (colorSpaceHandle == 0) return nullptr; + return sk_ref_sp(reinterpret_cast(colorSpaceHandle)); +} + +static void unref_colorSpace(SkColorSpace* cs) { + SkSafeUnref(cs); +} + +static jlong ColorSpace_getNativeFinalizer(JNIEnv*, jobject) { + return static_cast(reinterpret_cast(&unref_colorSpace)); +} + +static jlong ColorSpace_creator(JNIEnv* env, jobject, jfloat a, jfloat b, jfloat c, + jfloat d, jfloat e, jfloat f, jfloat g, jfloatArray xyzD50) { + skcms_TransferFunction p; + p.a = a; + p.b = b; + p.c = c; + p.d = d; + p.e = e; + p.f = f; + p.g = g; + skcms_Matrix3x3 xyzMatrix = getNativeXYZMatrix(env, xyzD50); + + return reinterpret_cast(SkColorSpace::MakeRGB(p, xyzMatrix).release()); +} + +static const JNINativeMethod gColorSpaceRgbMethods[] = { + { "nativeGetNativeFinalizer", "()J", (void*)ColorSpace_getNativeFinalizer }, + { "nativeCreate", "(FFFFFFF[F)J", (void*)ColorSpace_creator } +}; + +namespace android { + +int register_android_graphics_ColorSpace(JNIEnv* env) { + return android::RegisterMethodsOrDie(env, "android/graphics/ColorSpace$Rgb", + gColorSpaceRgbMethods, NELEM(gColorSpaceRgbMethods)); +} + +}; // namespace android diff --git a/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp new file mode 100644 index 000000000000..9907da5a2e68 --- /dev/null +++ b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp @@ -0,0 +1,220 @@ +/* + * 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 "jni.h" +#include "GraphicsJNI.h" +#include + +#include +#ifdef __ANDROID__ // Layoutlib does not support Looper and device properties +#include +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#ifdef __ANDROID__ // Layoutlib does not support RenderThread +#include +#endif + +#include "core_jni_helpers.h" + +namespace android { + +using namespace uirenderer; + +jmethodID gRunnableMethodId; + +static JNIEnv* jnienv(JavaVM* vm) { + JNIEnv* env; + if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { + LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", vm); + } + return env; +} + +#ifdef __ANDROID__ // Layoutlib does not support GL, Looper +class InvokeRunnableMessage : public MessageHandler { +public: + InvokeRunnableMessage(JNIEnv* env, jobject runnable) { + mRunnable = env->NewGlobalRef(runnable); + env->GetJavaVM(&mVm); + } + + virtual ~InvokeRunnableMessage() { + jnienv(mVm)->DeleteGlobalRef(mRunnable); + } + + virtual void handleMessage(const Message&) { + jnienv(mVm)->CallVoidMethod(mRunnable, gRunnableMethodId); + } + +private: + JavaVM* mVm; + jobject mRunnable; +}; + +class GlFunctorReleasedCallbackBridge : public GlFunctorLifecycleListener { +public: + GlFunctorReleasedCallbackBridge(JNIEnv* env, jobject javaCallback) { + mLooper = Looper::getForThread(); + mMessage = new InvokeRunnableMessage(env, javaCallback); + } + + virtual void onGlFunctorReleased(Functor* functor) override { + mLooper->sendMessage(mMessage, 0); + } + +private: + sp mLooper; + sp mMessage; +}; +#endif + +// ---------------- @FastNative ----------------------------- + +static void android_view_DisplayListCanvas_callDrawGLFunction(JNIEnv* env, jobject clazz, + jlong canvasPtr, jlong functorPtr, jobject releasedCallback) { +#ifdef __ANDROID__ // Layoutlib does not support GL + Canvas* canvas = reinterpret_cast(canvasPtr); + Functor* functor = reinterpret_cast(functorPtr); + sp bridge; + if (releasedCallback) { + bridge = new GlFunctorReleasedCallbackBridge(env, releasedCallback); + } + canvas->callDrawGLFunction(functor, bridge.get()); +#endif +} + + +// ---------------- @CriticalNative ------------------------- + +static jlong android_view_DisplayListCanvas_createDisplayListCanvas(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, + jint width, jint height) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + return reinterpret_cast(Canvas::create_recording_canvas(width, height, renderNode)); +} + +static void android_view_DisplayListCanvas_resetDisplayListCanvas(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, + jlong renderNodePtr, jint width, jint height) { + Canvas* canvas = reinterpret_cast(canvasPtr); + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + canvas->resetRecording(width, height, renderNode); +} + +static jint android_view_DisplayListCanvas_getMaxTextureSize(CRITICAL_JNI_PARAMS) { +#ifdef __ANDROID__ // Layoutlib does not support RenderProxy (RenderThread) + return android::uirenderer::renderthread::RenderProxy::maxTextureSize(); +#else + return 4096; +#endif +} + +static void android_view_DisplayListCanvas_insertReorderBarrier(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, + jboolean reorderEnable) { + Canvas* canvas = reinterpret_cast(canvasPtr); + canvas->insertReorderBarrier(reorderEnable); +} + +static jlong android_view_DisplayListCanvas_finishRecording(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr) { + Canvas* canvas = reinterpret_cast(canvasPtr); + return reinterpret_cast(canvas->finishRecording()); +} + +static void android_view_DisplayListCanvas_drawRenderNode(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, jlong renderNodePtr) { + Canvas* canvas = reinterpret_cast(canvasPtr); + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + canvas->drawRenderNode(renderNode); +} + +static void android_view_DisplayListCanvas_drawTextureLayer(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, jlong layerPtr) { + Canvas* canvas = reinterpret_cast(canvasPtr); + DeferredLayerUpdater* layer = reinterpret_cast(layerPtr); + canvas->drawLayer(layer); +} + +static void android_view_DisplayListCanvas_drawRoundRectProps(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, + jlong leftPropPtr, jlong topPropPtr, jlong rightPropPtr, jlong bottomPropPtr, + jlong rxPropPtr, jlong ryPropPtr, jlong paintPropPtr) { + Canvas* canvas = reinterpret_cast(canvasPtr); + CanvasPropertyPrimitive* leftProp = reinterpret_cast(leftPropPtr); + CanvasPropertyPrimitive* topProp = reinterpret_cast(topPropPtr); + CanvasPropertyPrimitive* rightProp = reinterpret_cast(rightPropPtr); + CanvasPropertyPrimitive* bottomProp = reinterpret_cast(bottomPropPtr); + CanvasPropertyPrimitive* rxProp = reinterpret_cast(rxPropPtr); + CanvasPropertyPrimitive* ryProp = reinterpret_cast(ryPropPtr); + CanvasPropertyPaint* paintProp = reinterpret_cast(paintPropPtr); + canvas->drawRoundRect(leftProp, topProp, rightProp, bottomProp, rxProp, ryProp, paintProp); +} + +static void android_view_DisplayListCanvas_drawCircleProps(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, + jlong xPropPtr, jlong yPropPtr, jlong radiusPropPtr, jlong paintPropPtr) { + Canvas* canvas = reinterpret_cast(canvasPtr); + CanvasPropertyPrimitive* xProp = reinterpret_cast(xPropPtr); + CanvasPropertyPrimitive* yProp = reinterpret_cast(yPropPtr); + CanvasPropertyPrimitive* radiusProp = reinterpret_cast(radiusPropPtr); + CanvasPropertyPaint* paintProp = reinterpret_cast(paintPropPtr); + canvas->drawCircle(xProp, yProp, radiusProp, paintProp); +} + +static void android_view_DisplayListCanvas_drawWebViewFunctor(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, jint functor) { + Canvas* canvas = reinterpret_cast(canvasPtr); + canvas->drawWebViewFunctor(functor); +} + +// ---------------------------------------------------------------------------- +// JNI Glue +// ---------------------------------------------------------------------------- + +const char* const kClassPathName = "android/graphics/RecordingCanvas"; + +static JNINativeMethod gMethods[] = { + + // ------------ @FastNative ------------------ + + { "nCallDrawGLFunction", "(JJLjava/lang/Runnable;)V", + (void*) android_view_DisplayListCanvas_callDrawGLFunction }, + + // ------------ @CriticalNative -------------- + { "nCreateDisplayListCanvas", "(JII)J", (void*) android_view_DisplayListCanvas_createDisplayListCanvas }, + { "nResetDisplayListCanvas", "(JJII)V", (void*) android_view_DisplayListCanvas_resetDisplayListCanvas }, + { "nGetMaximumTextureWidth", "()I", (void*) android_view_DisplayListCanvas_getMaxTextureSize }, + { "nGetMaximumTextureHeight", "()I", (void*) android_view_DisplayListCanvas_getMaxTextureSize }, + { "nInsertReorderBarrier", "(JZ)V", (void*) android_view_DisplayListCanvas_insertReorderBarrier }, + { "nFinishRecording", "(J)J", (void*) android_view_DisplayListCanvas_finishRecording }, + { "nDrawRenderNode", "(JJ)V", (void*) android_view_DisplayListCanvas_drawRenderNode }, + { "nDrawTextureLayer", "(JJ)V", (void*) android_view_DisplayListCanvas_drawTextureLayer }, + { "nDrawCircle", "(JJJJJ)V", (void*) android_view_DisplayListCanvas_drawCircleProps }, + { "nDrawRoundRect", "(JJJJJJJJ)V",(void*) android_view_DisplayListCanvas_drawRoundRectProps }, + { "nDrawWebViewFunctor", "(JI)V", (void*) android_view_DisplayListCanvas_drawWebViewFunctor }, +}; + +int register_android_view_DisplayListCanvas(JNIEnv* env) { + jclass runnableClass = FindClassOrDie(env, "java/lang/Runnable"); + gRunnableMethodId = GetMethodIDOrDie(env, runnableClass, "run", "()V"); + + return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); +} + +}; diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp new file mode 100644 index 000000000000..93449ffeae1b --- /dev/null +++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp @@ -0,0 +1,746 @@ +/* + * 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 "ThreadedRenderer" +#define ATRACE_TAG ATRACE_TAG_VIEW + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "android_graphics_HardwareRendererObserver.h" +#include "core_jni_helpers.h" +#include "jni.h" + +namespace android { + +using namespace android::uirenderer; +using namespace android::uirenderer::renderthread; + +struct { + jclass clazz; + jmethodID invokePictureCapturedCallback; +} gHardwareRenderer; + +struct { + jmethodID onFrameDraw; +} gFrameDrawingCallback; + +struct { + jmethodID onFrameComplete; +} gFrameCompleteCallback; + +static JNIEnv* getenv(JavaVM* vm) { + JNIEnv* env; + if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { + LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", vm); + } + return env; +} + +typedef ANativeWindow* (*ANW_fromSurface)(JNIEnv* env, jobject surface); +ANW_fromSurface fromSurface; + +class JvmErrorReporter : public ErrorHandler { +public: + JvmErrorReporter(JNIEnv* env) { + env->GetJavaVM(&mVm); + } + + virtual void onError(const std::string& message) override { + JNIEnv* env = getenv(mVm); + jniThrowException(env, "java/lang/IllegalStateException", message.c_str()); + } +private: + JavaVM* mVm; +}; + +class FrameCompleteWrapper : public LightRefBase { +public: + explicit FrameCompleteWrapper(JNIEnv* env, jobject jobject) { + env->GetJavaVM(&mVm); + mObject = env->NewGlobalRef(jobject); + LOG_ALWAYS_FATAL_IF(!mObject, "Failed to make global ref"); + } + + ~FrameCompleteWrapper() { + releaseObject(); + } + + void onFrameComplete(int64_t frameNr) { + if (mObject) { + ATRACE_FORMAT("frameComplete %" PRId64, frameNr); + getenv(mVm)->CallVoidMethod(mObject, gFrameCompleteCallback.onFrameComplete, frameNr); + releaseObject(); + } + } + +private: + JavaVM* mVm; + jobject mObject; + + void releaseObject() { + if (mObject) { + getenv(mVm)->DeleteGlobalRef(mObject); + mObject = nullptr; + } + } +}; + +static void android_view_ThreadedRenderer_rotateProcessStatsBuffer(JNIEnv* env, jobject clazz) { + RenderProxy::rotateProcessStatsBuffer(); +} + +static void android_view_ThreadedRenderer_setProcessStatsBuffer(JNIEnv* env, jobject clazz, + jint fd) { + RenderProxy::setProcessStatsBuffer(fd); +} + +static jint android_view_ThreadedRenderer_getRenderThreadTid(JNIEnv* env, jobject clazz, + jlong proxyPtr) { + RenderProxy* proxy = reinterpret_cast(proxyPtr); + return proxy->getRenderThreadTid(); +} + +static jlong android_view_ThreadedRenderer_createRootRenderNode(JNIEnv* env, jobject clazz) { + RootRenderNode* node = new RootRenderNode(std::make_unique(env)); + node->incStrong(0); + node->setName("RootRenderNode"); + return reinterpret_cast(node); +} + +static jlong android_view_ThreadedRenderer_createProxy(JNIEnv* env, jobject clazz, + jboolean translucent, jboolean isWideGamut, jlong rootRenderNodePtr) { + RootRenderNode* rootRenderNode = reinterpret_cast(rootRenderNodePtr); + ContextFactoryImpl factory(rootRenderNode); + RenderProxy* proxy = new RenderProxy(translucent, rootRenderNode, &factory); + proxy->setWideGamut(isWideGamut); + return (jlong) proxy; +} + +static void android_view_ThreadedRenderer_deleteProxy(JNIEnv* env, jobject clazz, + jlong proxyPtr) { + RenderProxy* proxy = reinterpret_cast(proxyPtr); + delete proxy; +} + +static jboolean android_view_ThreadedRenderer_loadSystemProperties(JNIEnv* env, jobject clazz, + jlong proxyPtr) { + RenderProxy* proxy = reinterpret_cast(proxyPtr); + return proxy->loadSystemProperties(); +} + +static void android_view_ThreadedRenderer_setName(JNIEnv* env, jobject clazz, + jlong proxyPtr, jstring jname) { + RenderProxy* proxy = reinterpret_cast(proxyPtr); + const char* name = env->GetStringUTFChars(jname, NULL); + proxy->setName(name); + env->ReleaseStringUTFChars(jname, name); +} + +static void android_view_ThreadedRenderer_setSurface(JNIEnv* env, jobject clazz, + jlong proxyPtr, jobject jsurface, jboolean discardBuffer) { + RenderProxy* proxy = reinterpret_cast(proxyPtr); + ANativeWindow* window = nullptr; + if (jsurface) { + window = fromSurface(env, jsurface); + } + bool enableTimeout = true; + if (discardBuffer) { + // Currently only Surface#lockHardwareCanvas takes this path + enableTimeout = false; + proxy->setSwapBehavior(SwapBehavior::kSwap_discardBuffer); + } + proxy->setSurface(window, enableTimeout); + ANativeWindow_release(window); +} + +static jboolean android_view_ThreadedRenderer_pause(JNIEnv* env, jobject clazz, + jlong proxyPtr) { + RenderProxy* proxy = reinterpret_cast(proxyPtr); + return proxy->pause(); +} + +static void android_view_ThreadedRenderer_setStopped(JNIEnv* env, jobject clazz, + jlong proxyPtr, jboolean stopped) { + RenderProxy* proxy = reinterpret_cast(proxyPtr); + proxy->setStopped(stopped); +} + +static void android_view_ThreadedRenderer_setLightAlpha(JNIEnv* env, jobject clazz, jlong proxyPtr, + jfloat ambientShadowAlpha, jfloat spotShadowAlpha) { + RenderProxy* proxy = reinterpret_cast(proxyPtr); + proxy->setLightAlpha((uint8_t) (255 * ambientShadowAlpha), (uint8_t) (255 * spotShadowAlpha)); +} + +static void android_view_ThreadedRenderer_setLightGeometry(JNIEnv* env, jobject clazz, + jlong proxyPtr, jfloat lightX, jfloat lightY, jfloat lightZ, jfloat lightRadius) { + RenderProxy* proxy = reinterpret_cast(proxyPtr); + proxy->setLightGeometry((Vector3){lightX, lightY, lightZ}, lightRadius); +} + +static void android_view_ThreadedRenderer_setOpaque(JNIEnv* env, jobject clazz, + jlong proxyPtr, jboolean opaque) { + RenderProxy* proxy = reinterpret_cast(proxyPtr); + proxy->setOpaque(opaque); +} + +static void android_view_ThreadedRenderer_setWideGamut(JNIEnv* env, jobject clazz, + jlong proxyPtr, jboolean wideGamut) { + RenderProxy* proxy = reinterpret_cast(proxyPtr); + proxy->setWideGamut(wideGamut); +} + +static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz, + jlong proxyPtr, jlongArray frameInfo, jint frameInfoSize) { + LOG_ALWAYS_FATAL_IF(frameInfoSize != UI_THREAD_FRAME_INFO_SIZE, + "Mismatched size expectations, given %d expected %d", + frameInfoSize, UI_THREAD_FRAME_INFO_SIZE); + RenderProxy* proxy = reinterpret_cast(proxyPtr); + env->GetLongArrayRegion(frameInfo, 0, frameInfoSize, proxy->frameInfo()); + return proxy->syncAndDrawFrame(); +} + +static void android_view_ThreadedRenderer_destroy(JNIEnv* env, jobject clazz, + jlong proxyPtr, jlong rootNodePtr) { + RootRenderNode* rootRenderNode = reinterpret_cast(rootNodePtr); + rootRenderNode->destroy(); + RenderProxy* proxy = reinterpret_cast(proxyPtr); + proxy->destroy(); +} + +static void android_view_ThreadedRenderer_registerAnimatingRenderNode(JNIEnv* env, jobject clazz, + jlong rootNodePtr, jlong animatingNodePtr) { + RootRenderNode* rootRenderNode = reinterpret_cast(rootNodePtr); + RenderNode* animatingNode = reinterpret_cast(animatingNodePtr); + rootRenderNode->attachAnimatingNode(animatingNode); +} + +static void android_view_ThreadedRenderer_registerVectorDrawableAnimator(JNIEnv* env, jobject clazz, + jlong rootNodePtr, jlong animatorPtr) { + RootRenderNode* rootRenderNode = reinterpret_cast(rootNodePtr); + PropertyValuesAnimatorSet* animator = reinterpret_cast(animatorPtr); + rootRenderNode->addVectorDrawableAnimator(animator); +} + +static void android_view_ThreadedRenderer_invokeFunctor(JNIEnv* env, jobject clazz, + jlong functorPtr, jboolean waitForCompletion) { + Functor* functor = reinterpret_cast(functorPtr); + RenderProxy::invokeFunctor(functor, waitForCompletion); +} + +static jlong android_view_ThreadedRenderer_createTextureLayer(JNIEnv* env, jobject clazz, + jlong proxyPtr) { + RenderProxy* proxy = reinterpret_cast(proxyPtr); + DeferredLayerUpdater* layer = proxy->createTextureLayer(); + return reinterpret_cast(layer); +} + +static void android_view_ThreadedRenderer_buildLayer(JNIEnv* env, jobject clazz, + jlong proxyPtr, jlong nodePtr) { + RenderProxy* proxy = reinterpret_cast(proxyPtr); + RenderNode* node = reinterpret_cast(nodePtr); + proxy->buildLayer(node); +} + +static jboolean android_view_ThreadedRenderer_copyLayerInto(JNIEnv* env, jobject clazz, + jlong proxyPtr, jlong layerPtr, jlong bitmapPtr) { + RenderProxy* proxy = reinterpret_cast(proxyPtr); + DeferredLayerUpdater* layer = reinterpret_cast(layerPtr); + SkBitmap bitmap; + bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap); + return proxy->copyLayerInto(layer, bitmap); +} + +static void android_view_ThreadedRenderer_pushLayerUpdate(JNIEnv* env, jobject clazz, + jlong proxyPtr, jlong layerPtr) { + RenderProxy* proxy = reinterpret_cast(proxyPtr); + DeferredLayerUpdater* layer = reinterpret_cast(layerPtr); + proxy->pushLayerUpdate(layer); +} + +static void android_view_ThreadedRenderer_cancelLayerUpdate(JNIEnv* env, jobject clazz, + jlong proxyPtr, jlong layerPtr) { + RenderProxy* proxy = reinterpret_cast(proxyPtr); + DeferredLayerUpdater* layer = reinterpret_cast(layerPtr); + proxy->cancelLayerUpdate(layer); +} + +static void android_view_ThreadedRenderer_detachSurfaceTexture(JNIEnv* env, jobject clazz, + jlong proxyPtr, jlong layerPtr) { + RenderProxy* proxy = reinterpret_cast(proxyPtr); + DeferredLayerUpdater* layer = reinterpret_cast(layerPtr); + proxy->detachSurfaceTexture(layer); +} + +static void android_view_ThreadedRenderer_destroyHardwareResources(JNIEnv* env, jobject clazz, + jlong proxyPtr) { + RenderProxy* proxy = reinterpret_cast(proxyPtr); + proxy->destroyHardwareResources(); +} + +static void android_view_ThreadedRenderer_trimMemory(JNIEnv* env, jobject clazz, + jint level) { + RenderProxy::trimMemory(level); +} + +static void android_view_ThreadedRenderer_overrideProperty(JNIEnv* env, jobject clazz, + jstring name, jstring value) { + const char* nameCharArray = env->GetStringUTFChars(name, NULL); + const char* valueCharArray = env->GetStringUTFChars(value, NULL); + RenderProxy::overrideProperty(nameCharArray, valueCharArray); + env->ReleaseStringUTFChars(name, nameCharArray); + env->ReleaseStringUTFChars(name, valueCharArray); +} + +static void android_view_ThreadedRenderer_fence(JNIEnv* env, jobject clazz, + jlong proxyPtr) { + RenderProxy* proxy = reinterpret_cast(proxyPtr); + proxy->fence(); +} + +static void android_view_ThreadedRenderer_stopDrawing(JNIEnv* env, jobject clazz, + jlong proxyPtr) { + RenderProxy* proxy = reinterpret_cast(proxyPtr); + proxy->stopDrawing(); +} + +static void android_view_ThreadedRenderer_notifyFramePending(JNIEnv* env, jobject clazz, + jlong proxyPtr) { + RenderProxy* proxy = reinterpret_cast(proxyPtr); + proxy->notifyFramePending(); +} + +static void android_view_ThreadedRenderer_dumpProfileInfo(JNIEnv* env, jobject clazz, + jlong proxyPtr, jobject javaFileDescriptor, jint dumpFlags) { + RenderProxy* proxy = reinterpret_cast(proxyPtr); + int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor); + proxy->dumpProfileInfo(fd, dumpFlags); +} + +static void android_view_ThreadedRenderer_addRenderNode(JNIEnv* env, jobject clazz, + jlong proxyPtr, jlong renderNodePtr, jboolean placeFront) { + RenderProxy* proxy = reinterpret_cast(proxyPtr); + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + proxy->addRenderNode(renderNode, placeFront); +} + +static void android_view_ThreadedRenderer_removeRenderNode(JNIEnv* env, jobject clazz, + jlong proxyPtr, jlong renderNodePtr) { + RenderProxy* proxy = reinterpret_cast(proxyPtr); + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + proxy->removeRenderNode(renderNode); +} + +static void android_view_ThreadedRendererd_drawRenderNode(JNIEnv* env, jobject clazz, + jlong proxyPtr, jlong renderNodePtr) { + RenderProxy* proxy = reinterpret_cast(proxyPtr); + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + proxy->drawRenderNode(renderNode); +} + +static void android_view_ThreadedRenderer_setContentDrawBounds(JNIEnv* env, + jobject clazz, jlong proxyPtr, jint left, jint top, jint right, jint bottom) { + RenderProxy* proxy = reinterpret_cast(proxyPtr); + proxy->setContentDrawBounds(left, top, right, bottom); +} + +class JGlobalRefHolder { +public: + JGlobalRefHolder(JavaVM* vm, jobject object) : mVm(vm), mObject(object) {} + + virtual ~JGlobalRefHolder() { + getenv(mVm)->DeleteGlobalRef(mObject); + mObject = nullptr; + } + + jobject object() { return mObject; } + JavaVM* vm() { return mVm; } + +private: + JGlobalRefHolder(const JGlobalRefHolder&) = delete; + void operator=(const JGlobalRefHolder&) = delete; + + JavaVM* mVm; + jobject mObject; +}; + +static void android_view_ThreadedRenderer_setPictureCapturedCallbackJNI(JNIEnv* env, + jobject clazz, jlong proxyPtr, jobject pictureCallback) { + RenderProxy* proxy = reinterpret_cast(proxyPtr); + if (!pictureCallback) { + proxy->setPictureCapturedCallback(nullptr); + } else { + JavaVM* vm = nullptr; + LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM"); + auto globalCallbackRef = std::make_shared(vm, + env->NewGlobalRef(pictureCallback)); + proxy->setPictureCapturedCallback([globalCallbackRef](sk_sp&& picture) { + JNIEnv* env = getenv(globalCallbackRef->vm()); + Picture* wrapper = new Picture{std::move(picture)}; + env->CallStaticVoidMethod(gHardwareRenderer.clazz, + gHardwareRenderer.invokePictureCapturedCallback, + static_cast(reinterpret_cast(wrapper)), + globalCallbackRef->object()); + }); + } +} + +static void android_view_ThreadedRenderer_setFrameCallback(JNIEnv* env, + jobject clazz, jlong proxyPtr, jobject frameCallback) { + RenderProxy* proxy = reinterpret_cast(proxyPtr); + if (!frameCallback) { + proxy->setFrameCallback(nullptr); + } else { + JavaVM* vm = nullptr; + LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM"); + auto globalCallbackRef = std::make_shared(vm, + env->NewGlobalRef(frameCallback)); + proxy->setFrameCallback([globalCallbackRef](int64_t frameNr) { + JNIEnv* env = getenv(globalCallbackRef->vm()); + env->CallVoidMethod(globalCallbackRef->object(), gFrameDrawingCallback.onFrameDraw, + static_cast(frameNr)); + }); + } +} + +static void android_view_ThreadedRenderer_setFrameCompleteCallback(JNIEnv* env, + jobject clazz, jlong proxyPtr, jobject callback) { + RenderProxy* proxy = reinterpret_cast(proxyPtr); + if (!callback) { + proxy->setFrameCompleteCallback(nullptr); + } else { + sp wrapper = new FrameCompleteWrapper{env, callback}; + proxy->setFrameCompleteCallback([wrapper](int64_t frameNr) { + wrapper->onFrameComplete(frameNr); + }); + } +} + +static jint android_view_ThreadedRenderer_copySurfaceInto(JNIEnv* env, + jobject clazz, jobject jsurface, jint left, jint top, + jint right, jint bottom, jlong bitmapPtr) { + SkBitmap bitmap; + bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap); + ANativeWindow* window = fromSurface(env, jsurface); + jint result = RenderProxy::copySurfaceInto(window, left, top, right, bottom, &bitmap); + ANativeWindow_release(window); + return result; +} + +class ContextFactory : public IContextFactory { +public: + virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) { + return new AnimationContext(clock); + } +}; + +static jobject android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode(JNIEnv* env, + jobject clazz, jlong renderNodePtr, jint jwidth, jint jheight) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + if (jwidth <= 0 || jheight <= 0) { + ALOGW("Invalid width %d or height %d", jwidth, jheight); + return nullptr; + } + + uint32_t width = jwidth; + uint32_t height = jheight; + + // Create an ImageReader wired up to a BufferItemConsumer + AImageReader* rawReader; + media_status_t result = + AImageReader_newWithUsage(width, height, AIMAGE_FORMAT_RGBA_8888, + AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE, 2, &rawReader); + std::unique_ptr reader(rawReader, + AImageReader_delete); + + if (result != AMEDIA_OK) { + ALOGW("Error creating image reader!"); + return nullptr; + } + + // Note that ownership of this window is maintained by AImageReader, so we + // shouldn't need to wrap around a smart pointer. + ANativeWindow* window; + result = AImageReader_getWindow(rawReader, &window); + + if (result != AMEDIA_OK) { + ALOGW("Error retrieving the native window!"); + return nullptr; + } + + // Render into the surface + { + ContextFactory factory; + RenderProxy proxy{true, renderNode, &factory}; + proxy.setSwapBehavior(SwapBehavior::kSwap_discardBuffer); + proxy.setSurface(window); + // Shadows can't be used via this interface, so just set the light source + // to all 0s. + proxy.setLightAlpha(0, 0); + proxy.setLightGeometry((Vector3){0, 0, 0}, 0); + nsecs_t vsync = systemTime(SYSTEM_TIME_MONOTONIC); + UiFrameInfoBuilder(proxy.frameInfo()) + .setVsync(vsync, vsync) + .addFlag(FrameInfoFlags::SurfaceCanvas); + proxy.syncAndDrawFrame(); + } + + AImage* rawImage; + result = AImageReader_acquireNextImage(rawReader, &rawImage); + std::unique_ptr image(rawImage, AImage_delete); + if (result != AMEDIA_OK) { + ALOGW("Error reading image: %d!", result); + return nullptr; + } + + AHardwareBuffer* buffer; + result = AImage_getHardwareBuffer(rawImage, &buffer); + + AHardwareBuffer_Desc desc; + AHardwareBuffer_describe(buffer, &desc); + + if (desc.width != width || desc.height != height) { + ALOGW("AHardwareBuffer size mismatch, got %dx%d expected %dx%d", desc.width, desc.height, + width, height); + // Continue I guess? + } + + sk_sp cs = uirenderer::DataSpaceToColorSpace( + static_cast(ANativeWindow_getBuffersDataSpace(window))); + if (cs == nullptr) { + // nullptr is treated as SRGB in Skia, thus explicitly use SRGB in order to make sure + // the returned bitmap has a color space. + cs = SkColorSpace::MakeSRGB(); + } + sk_sp bitmap = Bitmap::createFrom(buffer, cs); + return bitmap::createBitmap(env, bitmap.release(), + android::bitmap::kBitmapCreateFlag_Premultiplied); +} + +static void android_view_ThreadedRenderer_disableVsync(JNIEnv*, jclass) { + RenderProxy::disableVsync(); +} + +static void android_view_ThreadedRenderer_setHighContrastText(JNIEnv*, jclass, jboolean enable) { + Properties::enableHighContrastText = enable; +} + +static void android_view_ThreadedRenderer_hackySetRTAnimationsEnabled(JNIEnv*, jclass, + jboolean enable) { + Properties::enableRTAnimations = enable; +} + +static void android_view_ThreadedRenderer_setDebuggingEnabled(JNIEnv*, jclass, jboolean enable) { + Properties::debuggingEnabled = enable; +} + +static void android_view_ThreadedRenderer_setIsolatedProcess(JNIEnv*, jclass, jboolean isolated) { + Properties::isolatedProcess = isolated; +} + +static void android_view_ThreadedRenderer_setContextPriority(JNIEnv*, jclass, + jint contextPriority) { + Properties::contextPriority = contextPriority; +} + +static void android_view_ThreadedRenderer_allocateBuffers(JNIEnv* env, jobject clazz, + jlong proxyPtr) { + RenderProxy* proxy = reinterpret_cast(proxyPtr); + proxy->allocateBuffers(); +} + +static void android_view_ThreadedRenderer_setForceDark(JNIEnv* env, jobject clazz, + jlong proxyPtr, jboolean enable) { + RenderProxy* proxy = reinterpret_cast(proxyPtr); + proxy->setForceDark(enable); +} + +static void android_view_ThreadedRenderer_preload(JNIEnv*, jclass) { + RenderProxy::preload(); +} + +// ---------------------------------------------------------------------------- +// HardwareRendererObserver +// ---------------------------------------------------------------------------- + +static void android_view_ThreadedRenderer_addObserver(JNIEnv* env, jclass clazz, + jlong proxyPtr, jlong observerPtr) { + HardwareRendererObserver* observer = reinterpret_cast(observerPtr); + renderthread::RenderProxy* renderProxy = + reinterpret_cast(proxyPtr); + + renderProxy->addFrameMetricsObserver(observer); +} + +static void android_view_ThreadedRenderer_removeObserver(JNIEnv* env, jclass clazz, + jlong proxyPtr, jlong observerPtr) { + HardwareRendererObserver* observer = reinterpret_cast(observerPtr); + renderthread::RenderProxy* renderProxy = + reinterpret_cast(proxyPtr); + + renderProxy->removeFrameMetricsObserver(observer); +} + +// ---------------------------------------------------------------------------- +// Shaders +// ---------------------------------------------------------------------------- + +static void android_view_ThreadedRenderer_setupShadersDiskCache(JNIEnv* env, jobject clazz, + jstring diskCachePath, jstring skiaDiskCachePath) { + const char* cacheArray = env->GetStringUTFChars(diskCachePath, NULL); + android::egl_set_cache_filename(cacheArray); + env->ReleaseStringUTFChars(diskCachePath, cacheArray); + + const char* skiaCacheArray = env->GetStringUTFChars(skiaDiskCachePath, NULL); + uirenderer::skiapipeline::ShaderCache::get().setFilename(skiaCacheArray); + env->ReleaseStringUTFChars(skiaDiskCachePath, skiaCacheArray); +} + +// ---------------------------------------------------------------------------- +// JNI Glue +// ---------------------------------------------------------------------------- + +const char* const kClassPathName = "android/graphics/HardwareRenderer"; + +static const JNINativeMethod gMethods[] = { + { "nRotateProcessStatsBuffer", "()V", (void*) android_view_ThreadedRenderer_rotateProcessStatsBuffer }, + { "nSetProcessStatsBuffer", "(I)V", (void*) android_view_ThreadedRenderer_setProcessStatsBuffer }, + { "nGetRenderThreadTid", "(J)I", (void*) android_view_ThreadedRenderer_getRenderThreadTid }, + { "nCreateRootRenderNode", "()J", (void*) android_view_ThreadedRenderer_createRootRenderNode }, + { "nCreateProxy", "(ZZJ)J", (void*) android_view_ThreadedRenderer_createProxy }, + { "nDeleteProxy", "(J)V", (void*) android_view_ThreadedRenderer_deleteProxy }, + { "nLoadSystemProperties", "(J)Z", (void*) android_view_ThreadedRenderer_loadSystemProperties }, + { "nSetName", "(JLjava/lang/String;)V", (void*) android_view_ThreadedRenderer_setName }, + { "nSetSurface", "(JLandroid/view/Surface;Z)V", (void*) android_view_ThreadedRenderer_setSurface }, + { "nPause", "(J)Z", (void*) android_view_ThreadedRenderer_pause }, + { "nSetStopped", "(JZ)V", (void*) android_view_ThreadedRenderer_setStopped }, + { "nSetLightAlpha", "(JFF)V", (void*) android_view_ThreadedRenderer_setLightAlpha }, + { "nSetLightGeometry", "(JFFFF)V", (void*) android_view_ThreadedRenderer_setLightGeometry }, + { "nSetOpaque", "(JZ)V", (void*) android_view_ThreadedRenderer_setOpaque }, + { "nSetWideGamut", "(JZ)V", (void*) android_view_ThreadedRenderer_setWideGamut }, + { "nSyncAndDrawFrame", "(J[JI)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame }, + { "nDestroy", "(JJ)V", (void*) android_view_ThreadedRenderer_destroy }, + { "nRegisterAnimatingRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_registerAnimatingRenderNode }, + { "nRegisterVectorDrawableAnimator", "(JJ)V", (void*) android_view_ThreadedRenderer_registerVectorDrawableAnimator }, + { "nInvokeFunctor", "(JZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor }, + { "nCreateTextureLayer", "(J)J", (void*) android_view_ThreadedRenderer_createTextureLayer }, + { "nBuildLayer", "(JJ)V", (void*) android_view_ThreadedRenderer_buildLayer }, + { "nCopyLayerInto", "(JJJ)Z", (void*) android_view_ThreadedRenderer_copyLayerInto }, + { "nPushLayerUpdate", "(JJ)V", (void*) android_view_ThreadedRenderer_pushLayerUpdate }, + { "nCancelLayerUpdate", "(JJ)V", (void*) android_view_ThreadedRenderer_cancelLayerUpdate }, + { "nDetachSurfaceTexture", "(JJ)V", (void*) android_view_ThreadedRenderer_detachSurfaceTexture }, + { "nDestroyHardwareResources", "(J)V", (void*) android_view_ThreadedRenderer_destroyHardwareResources }, + { "nTrimMemory", "(I)V", (void*) android_view_ThreadedRenderer_trimMemory }, + { "nOverrideProperty", "(Ljava/lang/String;Ljava/lang/String;)V", (void*) android_view_ThreadedRenderer_overrideProperty }, + { "nFence", "(J)V", (void*) android_view_ThreadedRenderer_fence }, + { "nStopDrawing", "(J)V", (void*) android_view_ThreadedRenderer_stopDrawing }, + { "nNotifyFramePending", "(J)V", (void*) android_view_ThreadedRenderer_notifyFramePending }, + { "nDumpProfileInfo", "(JLjava/io/FileDescriptor;I)V", (void*) android_view_ThreadedRenderer_dumpProfileInfo }, + { "setupShadersDiskCache", "(Ljava/lang/String;Ljava/lang/String;)V", + (void*) android_view_ThreadedRenderer_setupShadersDiskCache }, + { "nAddRenderNode", "(JJZ)V", (void*) android_view_ThreadedRenderer_addRenderNode}, + { "nRemoveRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_removeRenderNode}, + { "nDrawRenderNode", "(JJ)V", (void*) android_view_ThreadedRendererd_drawRenderNode}, + { "nSetContentDrawBounds", "(JIIII)V", (void*)android_view_ThreadedRenderer_setContentDrawBounds}, + { "nSetPictureCaptureCallback", "(JLandroid/graphics/HardwareRenderer$PictureCapturedCallback;)V", + (void*) android_view_ThreadedRenderer_setPictureCapturedCallbackJNI }, + { "nSetFrameCallback", "(JLandroid/graphics/HardwareRenderer$FrameDrawingCallback;)V", + (void*)android_view_ThreadedRenderer_setFrameCallback}, + { "nSetFrameCompleteCallback", "(JLandroid/graphics/HardwareRenderer$FrameCompleteCallback;)V", + (void*)android_view_ThreadedRenderer_setFrameCompleteCallback }, + { "nAddObserver", "(JJ)V", (void*)android_view_ThreadedRenderer_addObserver }, + { "nRemoveObserver", "(JJ)V", (void*)android_view_ThreadedRenderer_removeObserver }, + { "nCopySurfaceInto", "(Landroid/view/Surface;IIIIJ)I", + (void*)android_view_ThreadedRenderer_copySurfaceInto }, + { "nCreateHardwareBitmap", "(JII)Landroid/graphics/Bitmap;", + (void*)android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode }, + { "disableVsync", "()V", (void*)android_view_ThreadedRenderer_disableVsync }, + { "nSetHighContrastText", "(Z)V", (void*)android_view_ThreadedRenderer_setHighContrastText }, + { "nHackySetRTAnimationsEnabled", "(Z)V", + (void*)android_view_ThreadedRenderer_hackySetRTAnimationsEnabled }, + { "nSetDebuggingEnabled", "(Z)V", (void*)android_view_ThreadedRenderer_setDebuggingEnabled }, + { "nSetIsolatedProcess", "(Z)V", (void*)android_view_ThreadedRenderer_setIsolatedProcess }, + { "nSetContextPriority", "(I)V", (void*)android_view_ThreadedRenderer_setContextPriority }, + { "nAllocateBuffers", "(J)V", (void*)android_view_ThreadedRenderer_allocateBuffers }, + { "nSetForceDark", "(JZ)V", (void*)android_view_ThreadedRenderer_setForceDark }, + { "preload", "()V", (void*)android_view_ThreadedRenderer_preload }, +}; + +static JavaVM* mJvm = nullptr; + +static void attachRenderThreadToJvm(const char* name) { + LOG_ALWAYS_FATAL_IF(!mJvm, "No jvm but we set the hook??"); + + JavaVMAttachArgs args; + args.version = JNI_VERSION_1_4; + args.name = name; + args.group = NULL; + JNIEnv* env; + mJvm->AttachCurrentThreadAsDaemon(&env, (void*) &args); +} + +int register_android_view_ThreadedRenderer(JNIEnv* env) { + env->GetJavaVM(&mJvm); + RenderThread::setOnStartHook(&attachRenderThreadToJvm); + + jclass hardwareRenderer = FindClassOrDie(env, + "android/graphics/HardwareRenderer"); + gHardwareRenderer.clazz = reinterpret_cast(env->NewGlobalRef(hardwareRenderer)); + gHardwareRenderer.invokePictureCapturedCallback = GetStaticMethodIDOrDie(env, hardwareRenderer, + "invokePictureCapturedCallback", + "(JLandroid/graphics/HardwareRenderer$PictureCapturedCallback;)V"); + + jclass frameCallbackClass = FindClassOrDie(env, + "android/graphics/HardwareRenderer$FrameDrawingCallback"); + gFrameDrawingCallback.onFrameDraw = GetMethodIDOrDie(env, frameCallbackClass, + "onFrameDraw", "(J)V"); + + jclass frameCompleteClass = FindClassOrDie(env, + "android/graphics/HardwareRenderer$FrameCompleteCallback"); + gFrameCompleteCallback.onFrameComplete = GetMethodIDOrDie(env, frameCompleteClass, + "onFrameComplete", "(J)V"); + + void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE); + fromSurface = (ANW_fromSurface)dlsym(handle_, "ANativeWindow_fromSurface"); + LOG_ALWAYS_FATAL_IF(fromSurface == nullptr, + "Failed to find required symbol ANativeWindow_fromSurface!"); + + return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); +} + +}; // namespace android diff --git a/libs/hwui/jni/android_graphics_HardwareRendererObserver.cpp b/libs/hwui/jni/android_graphics_HardwareRendererObserver.cpp new file mode 100644 index 000000000000..89b77b0b069a --- /dev/null +++ b/libs/hwui/jni/android_graphics_HardwareRendererObserver.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2019 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 "android_graphics_HardwareRendererObserver.h" + +#include "core_jni_helpers.h" +#include "nativehelper/jni_macros.h" + +#include + +namespace android { + +struct { + jmethodID callback; +} gHardwareRendererObserverClassInfo; + +static JNIEnv* getenv(JavaVM* vm) { + JNIEnv* env; + if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { + LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", vm); + } + return env; +} + +HardwareRendererObserver::HardwareRendererObserver(JavaVM *vm, jobject observer) : mVm(vm) { + mObserverWeak = getenv(mVm)->NewWeakGlobalRef(observer); + LOG_ALWAYS_FATAL_IF(mObserverWeak == nullptr, + "unable to create frame stats observer reference"); +} + +HardwareRendererObserver::~HardwareRendererObserver() { + JNIEnv* env = getenv(mVm); + env->DeleteWeakGlobalRef(mObserverWeak); +} + +bool HardwareRendererObserver::getNextBuffer(JNIEnv* env, jlongArray metrics, int* dropCount) { + jsize bufferSize = env->GetArrayLength(reinterpret_cast(metrics)); + LOG_ALWAYS_FATAL_IF(bufferSize != HardwareRendererObserver::kBufferSize, + "Mismatched Java/Native FrameMetrics data format."); + + FrameMetricsNotification& elem = mRingBuffer[mNextInQueue]; + if (elem.hasData.load()) { + env->SetLongArrayRegion(metrics, 0, kBufferSize, elem.buffer); + *dropCount = elem.dropCount; + mNextInQueue = (mNextInQueue + 1) % kRingSize; + elem.hasData = false; + return true; + } + + return false; +} + +void HardwareRendererObserver::notify(const int64_t* stats) { + FrameMetricsNotification& elem = mRingBuffer[mNextFree]; + + if (!elem.hasData.load()) { + memcpy(elem.buffer, stats, kBufferSize * sizeof(stats[0])); + + elem.dropCount = mDroppedReports; + mDroppedReports = 0; + mNextFree = (mNextFree + 1) % kRingSize; + elem.hasData = true; + + JNIEnv* env = getenv(mVm); + jobject target = env->NewLocalRef(mObserverWeak); + if (target != nullptr) { + env->CallVoidMethod(target, gHardwareRendererObserverClassInfo.callback); + env->DeleteLocalRef(target); + } + } else { + mDroppedReports++; + } +} + +static jlong android_graphics_HardwareRendererObserver_createObserver(JNIEnv* env, + jobject observerObj) { + JavaVM* vm = nullptr; + if (env->GetJavaVM(&vm) != JNI_OK) { + LOG_ALWAYS_FATAL("Unable to get Java VM"); + return 0; + } + + HardwareRendererObserver* observer = new HardwareRendererObserver(vm, observerObj); + return reinterpret_cast(observer); +} + +static jint android_graphics_HardwareRendererObserver_getNextBuffer(JNIEnv* env, jobject, + jlong observerPtr, + jlongArray metrics) { + HardwareRendererObserver* observer = reinterpret_cast(observerPtr); + int dropCount = 0; + if (observer->getNextBuffer(env, metrics, &dropCount)) { + return dropCount; + } else { + return -1; + } +} + +static const std::array gMethods = { + MAKE_JNI_NATIVE_METHOD("nCreateObserver", "()J", + android_graphics_HardwareRendererObserver_createObserver), + MAKE_JNI_NATIVE_METHOD("nGetNextBuffer", "(J[J)I", + android_graphics_HardwareRendererObserver_getNextBuffer), +}; + +int register_android_graphics_HardwareRendererObserver(JNIEnv* env) { + + jclass observerClass = FindClassOrDie(env, "android/graphics/HardwareRendererObserver"); + gHardwareRendererObserverClassInfo.callback = GetMethodIDOrDie(env, observerClass, + "notifyDataAvailable", "()V"); + + return RegisterMethodsOrDie(env, "android/graphics/HardwareRendererObserver", + gMethods.data(), gMethods.size()); + +} + +} // namespace android \ No newline at end of file diff --git a/libs/hwui/jni/android_graphics_HardwareRendererObserver.h b/libs/hwui/jni/android_graphics_HardwareRendererObserver.h new file mode 100644 index 000000000000..62111fd7d7a1 --- /dev/null +++ b/libs/hwui/jni/android_graphics_HardwareRendererObserver.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2019 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 "jni.h" + +#include +#include + +namespace android { + +/* + * Implements JNI layer for hwui frame metrics reporting. + */ +class HardwareRendererObserver : public uirenderer::FrameMetricsObserver { +public: + HardwareRendererObserver(JavaVM *vm, jobject observer); + ~HardwareRendererObserver(); + + /** + * Retrieves frame metrics for the oldest frame that the renderer has retained. The renderer + * will retain a buffer until it has been retrieved, via this method, or its internal storage + * is exhausted at which point it informs the caller of how many frames it has failed to store + * since the last time this method was invoked. + * @param env java env required to populate the provided buffer array + * @param metrics output parameter that represents the buffer of metrics that is to be filled + * @param dropCount output parameter that is updated to reflect the number of buffers that were + discarded since the last successful invocation of this method. + * @return true if there was data to populate the array and false otherwise. If false then + * neither the metrics buffer or dropCount will be modified. + */ + bool getNextBuffer(JNIEnv* env, jlongArray metrics, int* dropCount); + + void notify(const int64_t* stats) override; + +private: + static constexpr int kBufferSize = static_cast(uirenderer::FrameInfoIndex::NumIndexes); + static constexpr int kRingSize = 3; + + class FrameMetricsNotification { + public: + FrameMetricsNotification() {} + + std::atomic_bool hasData = false; + int64_t buffer[kBufferSize]; + int dropCount = 0; + private: + // non-copyable + FrameMetricsNotification(const FrameMetricsNotification&) = delete; + FrameMetricsNotification& operator=(const FrameMetricsNotification& ) = delete; + }; + + JavaVM* const mVm; + jweak mObserverWeak; + + int mNextFree = 0; + int mNextInQueue = 0; + FrameMetricsNotification mRingBuffer[kRingSize]; + + int mDroppedReports = 0; +}; + +} // namespace android diff --git a/libs/hwui/jni/android_graphics_Matrix.cpp b/libs/hwui/jni/android_graphics_Matrix.cpp new file mode 100644 index 000000000000..13369763e0cf --- /dev/null +++ b/libs/hwui/jni/android_graphics_Matrix.cpp @@ -0,0 +1,399 @@ +/* libs/android_runtime/android/graphics/Matrix.cpp +** +** Copyright 2006, 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 "GraphicsJNI.h" +#include "Matrix.h" +#include "SkMatrix.h" +#include "core_jni_helpers.h" + +#include + +namespace android { + +static_assert(sizeof(SkMatrix) == 40, "Unexpected sizeof(SkMatrix), " + "update size in Matrix.java#NATIVE_ALLOCATION_SIZE and here"); +static_assert(SK_SCALAR_IS_FLOAT, "SK_SCALAR_IS_FLOAT is false, " + "only float scalar is supported"); + +class SkMatrixGlue { +public: + + // ---------------- Regular JNI ----------------------------- + + static void finalizer(jlong objHandle) { + SkMatrix* obj = reinterpret_cast(objHandle); + delete obj; + } + + static jlong getNativeFinalizer(JNIEnv* env, jobject clazz) { + return static_cast(reinterpret_cast(&finalizer)); + } + + static jlong create(JNIEnv* env, jobject clazz, jlong srcHandle) { + const SkMatrix* src = reinterpret_cast(srcHandle); + SkMatrix* obj = new SkMatrix(); + if (src) + *obj = *src; + else + obj->reset(); + return reinterpret_cast(obj); + } + + // ---------------- @FastNative ----------------------------- + + static void mapPoints(JNIEnv* env, jobject clazz, jlong matrixHandle, + jfloatArray dst, jint dstIndex, jfloatArray src, jint srcIndex, + jint ptCount, jboolean isPts) { + SkMatrix* matrix = reinterpret_cast(matrixHandle); + SkASSERT(ptCount >= 0); + AutoJavaFloatArray autoSrc(env, src, srcIndex + (ptCount << 1), + kRO_JNIAccess); + AutoJavaFloatArray autoDst(env, dst, dstIndex + (ptCount << 1), + kRW_JNIAccess); + float* srcArray = autoSrc.ptr() + srcIndex; + float* dstArray = autoDst.ptr() + dstIndex; + if (isPts) + matrix->mapPoints((SkPoint*) dstArray, (const SkPoint*) srcArray, + ptCount); + else + matrix->mapVectors((SkVector*) dstArray, (const SkVector*) srcArray, + ptCount); + } + + static jboolean mapRect__RectFRectF(JNIEnv* env, jobject clazz, + jlong matrixHandle, jobjectArray dst, jobject src) { + SkMatrix* matrix = reinterpret_cast(matrixHandle); + SkRect dst_, src_; + GraphicsJNI::jrectf_to_rect(env, src, &src_); + jboolean rectStaysRect = matrix->mapRect(&dst_, src_); + GraphicsJNI::rect_to_jrectf(dst_, env, dst); + return rectStaysRect ? JNI_TRUE : JNI_FALSE; + } + + static jboolean setRectToRect(JNIEnv* env, jobject clazz, + jlong matrixHandle, jobject src, jobject dst, jint stfHandle) { + SkMatrix* matrix = reinterpret_cast(matrixHandle); + SkMatrix::ScaleToFit stf = static_cast(stfHandle); + SkRect src_; + GraphicsJNI::jrectf_to_rect(env, src, &src_); + SkRect dst_; + GraphicsJNI::jrectf_to_rect(env, dst, &dst_); + return matrix->setRectToRect(src_, dst_, stf) ? JNI_TRUE : JNI_FALSE; + } + + static jboolean setPolyToPoly(JNIEnv* env, jobject clazz, + jlong matrixHandle, jfloatArray jsrc, jint srcIndex, + jfloatArray jdst, jint dstIndex, jint ptCount) { + SkMatrix* matrix = reinterpret_cast(matrixHandle); + SkASSERT(srcIndex >= 0); + SkASSERT(dstIndex >= 0); + SkASSERT((unsigned )ptCount <= 4); + + AutoJavaFloatArray autoSrc(env, jsrc, srcIndex + (ptCount << 1), + kRO_JNIAccess); + AutoJavaFloatArray autoDst(env, jdst, dstIndex + (ptCount << 1), + kRW_JNIAccess); + float* src = autoSrc.ptr() + srcIndex; + float* dst = autoDst.ptr() + dstIndex; + bool result; + + result = matrix->setPolyToPoly((const SkPoint*) src, + (const SkPoint*) dst, ptCount); + return result ? JNI_TRUE : JNI_FALSE; + } + + static void getValues(JNIEnv* env, jobject clazz, jlong matrixHandle, + jfloatArray values) { + SkMatrix* matrix = reinterpret_cast(matrixHandle); + AutoJavaFloatArray autoValues(env, values, 9, kRW_JNIAccess); + float* dst = autoValues.ptr(); + for (int i = 0; i < 9; i++) { + dst[i] = matrix->get(i); + } + } + + static void setValues(JNIEnv* env, jobject clazz, jlong matrixHandle, + jfloatArray values) { + SkMatrix* matrix = reinterpret_cast(matrixHandle); + AutoJavaFloatArray autoValues(env, values, 9, kRO_JNIAccess); + const float* src = autoValues.ptr(); + + for (int i = 0; i < 9; i++) { + matrix->set(i, src[i]); + } + } + + // ---------------- @CriticalNative ----------------------------- + + static jboolean isIdentity(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) { + SkMatrix* obj = reinterpret_cast(objHandle); + return obj->isIdentity() ? JNI_TRUE : JNI_FALSE; + } + + static jboolean isAffine(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) { + SkMatrix* obj = reinterpret_cast(objHandle); + return obj->asAffine(NULL) ? JNI_TRUE : JNI_FALSE; + } + + static jboolean rectStaysRect(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) { + SkMatrix* obj = reinterpret_cast(objHandle); + return obj->rectStaysRect() ? JNI_TRUE : JNI_FALSE; + } + + static void reset(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) { + SkMatrix* obj = reinterpret_cast(objHandle); + obj->reset(); + } + + static void set(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong otherHandle) { + SkMatrix* obj = reinterpret_cast(objHandle); + SkMatrix* other = reinterpret_cast(otherHandle); + *obj = *other; + } + + static void setTranslate(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat dx, jfloat dy) { + SkMatrix* obj = reinterpret_cast(objHandle); + obj->setTranslate(dx, dy); + } + + static void setScale__FFFF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat sx, jfloat sy, jfloat px, + jfloat py) { + SkMatrix* obj = reinterpret_cast(objHandle); + obj->setScale(sx, sy, px, py); + } + + static void setScale__FF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat sx, jfloat sy) { + SkMatrix* obj = reinterpret_cast(objHandle); + obj->setScale(sx, sy); + } + + static void setRotate__FFF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat degrees, jfloat px, + jfloat py) { + SkMatrix* obj = reinterpret_cast(objHandle); + obj->setRotate(degrees, px, py); + } + + static void setRotate__F(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat degrees) { + SkMatrix* obj = reinterpret_cast(objHandle); + obj->setRotate(degrees); + } + + static void setSinCos__FFFF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat sinValue, + jfloat cosValue, jfloat px, jfloat py) { + SkMatrix* obj = reinterpret_cast(objHandle); + obj->setSinCos(sinValue, cosValue, px, py); + } + + static void setSinCos__FF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat sinValue, + jfloat cosValue) { + SkMatrix* obj = reinterpret_cast(objHandle); + obj->setSinCos(sinValue, cosValue); + } + + static void setSkew__FFFF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat kx, jfloat ky, jfloat px, + jfloat py) { + SkMatrix* obj = reinterpret_cast(objHandle); + obj->setSkew(kx, ky, px, py); + } + + static void setSkew__FF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat kx, jfloat ky) { + SkMatrix* obj = reinterpret_cast(objHandle); + obj->setSkew(kx, ky); + } + + static void setConcat(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong aHandle, jlong bHandle) { + SkMatrix* obj = reinterpret_cast(objHandle); + SkMatrix* a = reinterpret_cast(aHandle); + SkMatrix* b = reinterpret_cast(bHandle); + obj->setConcat(*a, *b); + } + + static void preTranslate(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat dx, jfloat dy) { + SkMatrix* obj = reinterpret_cast(objHandle); + obj->preTranslate(dx, dy); + } + + static void preScale__FFFF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat sx, jfloat sy, jfloat px, + jfloat py) { + SkMatrix* obj = reinterpret_cast(objHandle); + obj->preScale(sx, sy, px, py); + } + + static void preScale__FF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat sx, jfloat sy) { + SkMatrix* obj = reinterpret_cast(objHandle); + obj->preScale(sx, sy); + } + + static void preRotate__FFF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat degrees, jfloat px, + jfloat py) { + SkMatrix* obj = reinterpret_cast(objHandle); + obj->preRotate(degrees, px, py); + } + + static void preRotate__F(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat degrees) { + SkMatrix* obj = reinterpret_cast(objHandle); + obj->preRotate(degrees); + } + + static void preSkew__FFFF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat kx, jfloat ky, jfloat px, + jfloat py) { + SkMatrix* obj = reinterpret_cast(objHandle); + obj->preSkew(kx, ky, px, py); + } + + static void preSkew__FF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat kx, jfloat ky) { + SkMatrix* obj = reinterpret_cast(objHandle); + obj->preSkew(kx, ky); + } + + static void preConcat(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong otherHandle) { + SkMatrix* obj = reinterpret_cast(objHandle); + SkMatrix* other = reinterpret_cast(otherHandle); + obj->preConcat(*other); + } + + static void postTranslate(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat dx, jfloat dy) { + SkMatrix* obj = reinterpret_cast(objHandle); + obj->postTranslate(dx, dy); + } + + static void postScale__FFFF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat sx, jfloat sy, + jfloat px, jfloat py) { + SkMatrix* obj = reinterpret_cast(objHandle); + obj->postScale(sx, sy, px, py); + } + + static void postScale__FF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat sx, jfloat sy) { + SkMatrix* obj = reinterpret_cast(objHandle); + obj->postScale(sx, sy); + } + + static void postRotate__FFF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat degrees, jfloat px, + jfloat py) { + SkMatrix* obj = reinterpret_cast(objHandle); + obj->postRotate(degrees, px, py); + } + + static void postRotate__F(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat degrees) { + SkMatrix* obj = reinterpret_cast(objHandle); + obj->postRotate(degrees); + } + + static void postSkew__FFFF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat kx, jfloat ky, jfloat px, + jfloat py) { + SkMatrix* obj = reinterpret_cast(objHandle); + obj->postSkew(kx, ky, px, py); + } + + static void postSkew__FF(CRITICAL_JNI_PARAMS_COMMA jlong matrixHandle, jfloat kx, jfloat ky) { + SkMatrix* matrix = reinterpret_cast(matrixHandle); + matrix->postSkew(kx, ky); + } + + static void postConcat(CRITICAL_JNI_PARAMS_COMMA jlong matrixHandle, jlong otherHandle) { + SkMatrix* matrix = reinterpret_cast(matrixHandle); + SkMatrix* other = reinterpret_cast(otherHandle); + matrix->postConcat(*other); + } + + static jboolean invert(CRITICAL_JNI_PARAMS_COMMA jlong matrixHandle, jlong inverseHandle) { + SkMatrix* matrix = reinterpret_cast(matrixHandle); + SkMatrix* inverse = reinterpret_cast(inverseHandle); + return matrix->invert(inverse); + } + + static jfloat mapRadius(CRITICAL_JNI_PARAMS_COMMA jlong matrixHandle, jfloat radius) { + SkMatrix* matrix = reinterpret_cast(matrixHandle); + float result; + result = SkScalarToFloat(matrix->mapRadius(radius)); + return static_cast(result); + } + + static jboolean equals(CRITICAL_JNI_PARAMS_COMMA jlong aHandle, jlong bHandle) { + const SkMatrix* a = reinterpret_cast(aHandle); + const SkMatrix* b = reinterpret_cast(bHandle); + return *a == *b; + } +}; + +static const JNINativeMethod methods[] = { + {"nGetNativeFinalizer", "()J", (void*) SkMatrixGlue::getNativeFinalizer}, + {"nCreate","(J)J", (void*) SkMatrixGlue::create}, + + // ------- @FastNative below here --------------- + {"nMapPoints","(J[FI[FIIZ)V", (void*) SkMatrixGlue::mapPoints}, + {"nMapRect","(JLandroid/graphics/RectF;Landroid/graphics/RectF;)Z", + (void*) SkMatrixGlue::mapRect__RectFRectF}, + {"nSetRectToRect","(JLandroid/graphics/RectF;Landroid/graphics/RectF;I)Z", + (void*) SkMatrixGlue::setRectToRect}, + {"nSetPolyToPoly","(J[FI[FII)Z", (void*) SkMatrixGlue::setPolyToPoly}, + {"nGetValues","(J[F)V", (void*) SkMatrixGlue::getValues}, + {"nSetValues","(J[F)V", (void*) SkMatrixGlue::setValues}, + + // ------- @CriticalNative below here --------------- + {"nIsIdentity","(J)Z", (void*) SkMatrixGlue::isIdentity}, + {"nIsAffine","(J)Z", (void*) SkMatrixGlue::isAffine}, + {"nRectStaysRect","(J)Z", (void*) SkMatrixGlue::rectStaysRect}, + {"nReset","(J)V", (void*) SkMatrixGlue::reset}, + {"nSet","(JJ)V", (void*) SkMatrixGlue::set}, + {"nSetTranslate","(JFF)V", (void*) SkMatrixGlue::setTranslate}, + {"nSetScale","(JFFFF)V", (void*) SkMatrixGlue::setScale__FFFF}, + {"nSetScale","(JFF)V", (void*) SkMatrixGlue::setScale__FF}, + {"nSetRotate","(JFFF)V", (void*) SkMatrixGlue::setRotate__FFF}, + {"nSetRotate","(JF)V", (void*) SkMatrixGlue::setRotate__F}, + {"nSetSinCos","(JFFFF)V", (void*) SkMatrixGlue::setSinCos__FFFF}, + {"nSetSinCos","(JFF)V", (void*) SkMatrixGlue::setSinCos__FF}, + {"nSetSkew","(JFFFF)V", (void*) SkMatrixGlue::setSkew__FFFF}, + {"nSetSkew","(JFF)V", (void*) SkMatrixGlue::setSkew__FF}, + {"nSetConcat","(JJJ)V", (void*) SkMatrixGlue::setConcat}, + {"nPreTranslate","(JFF)V", (void*) SkMatrixGlue::preTranslate}, + {"nPreScale","(JFFFF)V", (void*) SkMatrixGlue::preScale__FFFF}, + {"nPreScale","(JFF)V", (void*) SkMatrixGlue::preScale__FF}, + {"nPreRotate","(JFFF)V", (void*) SkMatrixGlue::preRotate__FFF}, + {"nPreRotate","(JF)V", (void*) SkMatrixGlue::preRotate__F}, + {"nPreSkew","(JFFFF)V", (void*) SkMatrixGlue::preSkew__FFFF}, + {"nPreSkew","(JFF)V", (void*) SkMatrixGlue::preSkew__FF}, + {"nPreConcat","(JJ)V", (void*) SkMatrixGlue::preConcat}, + {"nPostTranslate","(JFF)V", (void*) SkMatrixGlue::postTranslate}, + {"nPostScale","(JFFFF)V", (void*) SkMatrixGlue::postScale__FFFF}, + {"nPostScale","(JFF)V", (void*) SkMatrixGlue::postScale__FF}, + {"nPostRotate","(JFFF)V", (void*) SkMatrixGlue::postRotate__FFF}, + {"nPostRotate","(JF)V", (void*) SkMatrixGlue::postRotate__F}, + {"nPostSkew","(JFFFF)V", (void*) SkMatrixGlue::postSkew__FFFF}, + {"nPostSkew","(JFF)V", (void*) SkMatrixGlue::postSkew__FF}, + {"nPostConcat","(JJ)V", (void*) SkMatrixGlue::postConcat}, + {"nInvert","(JJ)Z", (void*) SkMatrixGlue::invert}, + {"nMapRadius","(JF)F", (void*) SkMatrixGlue::mapRadius}, + {"nEquals", "(JJ)Z", (void*) SkMatrixGlue::equals} +}; + +static jfieldID sNativeInstanceField; + +int register_android_graphics_Matrix(JNIEnv* env) { + int result = RegisterMethodsOrDie(env, "android/graphics/Matrix", methods, NELEM(methods)); + + jclass clazz = FindClassOrDie(env, "android/graphics/Matrix"); + sNativeInstanceField = GetFieldIDOrDie(env, clazz, "native_instance", "J"); + + return result; +} + +SkMatrix* android_graphics_Matrix_getSkMatrix(JNIEnv* env, jobject matrixObj) { + return reinterpret_cast(env->GetLongField(matrixObj, sNativeInstanceField)); +} + +} diff --git a/libs/hwui/jni/android_graphics_Matrix.h b/libs/hwui/jni/android_graphics_Matrix.h new file mode 100644 index 000000000000..fe90d2ef945d --- /dev/null +++ b/libs/hwui/jni/android_graphics_Matrix.h @@ -0,0 +1,30 @@ +/* + * 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_GRAPHICS_MATRIX_H_ +#define _ANDROID_GRAPHICS_MATRIX_H_ + +#include "jni.h" +#include "SkMatrix.h" + +namespace android { + +/* Gets the underlying SkMatrix from a Matrix object. */ +SkMatrix* android_graphics_Matrix_getSkMatrix(JNIEnv* env, jobject matrixObj); + +} // namespace android + +#endif // _ANDROID_GRAPHICS_MATRIX_H_ diff --git a/libs/hwui/jni/android_graphics_Picture.cpp b/libs/hwui/jni/android_graphics_Picture.cpp new file mode 100644 index 000000000000..1d085e5ccc49 --- /dev/null +++ b/libs/hwui/jni/android_graphics_Picture.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2008 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 "CreateJavaOutputStreamAdaptor.h" +#include "GraphicsJNI.h" +#include "Picture.h" +#include "SkCanvas.h" +#include "SkStream.h" +#include "core_jni_helpers.h" +#include "nativehelper/jni_macros.h" + +#include + +#include + +namespace android { + +static jlong android_graphics_Picture_newPicture(JNIEnv* env, jobject, jlong srcHandle) { + const Picture* src = reinterpret_cast(srcHandle); + return reinterpret_cast(new Picture(src)); +} + +static jlong android_graphics_Picture_deserialize(JNIEnv* env, jobject, jobject jstream, + jbyteArray jstorage) { + Picture* picture = NULL; + SkStream* strm = CreateJavaInputStreamAdaptor(env, jstream, jstorage); + if (strm) { + picture = Picture::CreateFromStream(strm); + delete strm; + } + return reinterpret_cast(picture); +} + +static void android_graphics_Picture_killPicture(JNIEnv* env, jobject, jlong pictureHandle) { + Picture* picture = reinterpret_cast(pictureHandle); + SkASSERT(picture); + delete picture; +} + +static void android_graphics_Picture_draw(JNIEnv* env, jobject, jlong canvasHandle, + jlong pictureHandle) { + Canvas* canvas = reinterpret_cast(canvasHandle); + Picture* picture = reinterpret_cast(pictureHandle); + SkASSERT(canvas); + SkASSERT(picture); + picture->draw(canvas); +} + +static jboolean android_graphics_Picture_serialize(JNIEnv* env, jobject, jlong pictureHandle, + jobject jstream, jbyteArray jstorage) { + Picture* picture = reinterpret_cast(pictureHandle); + SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage); + + if (NULL != strm) { + picture->serialize(strm); + delete strm; + return JNI_TRUE; + } + return JNI_FALSE; +} + +static jint android_graphics_Picture_getWidth(JNIEnv* env, jobject, jlong pictureHandle) { + Picture* pict = reinterpret_cast(pictureHandle); + return static_cast(pict->width()); +} + +static jint android_graphics_Picture_getHeight(JNIEnv* env, jobject, jlong pictureHandle) { + Picture* pict = reinterpret_cast(pictureHandle); + return static_cast(pict->height()); +} + +static jlong android_graphics_Picture_beginRecording(JNIEnv* env, jobject, jlong pictHandle, + jint w, jint h) { + Picture* pict = reinterpret_cast(pictHandle); + Canvas* canvas = pict->beginRecording(w, h); + return reinterpret_cast(canvas); +} + +static void android_graphics_Picture_endRecording(JNIEnv* env, jobject, jlong pictHandle) { + Picture* pict = reinterpret_cast(pictHandle); + pict->endRecording(); +} + +static const std::array gMethods = { + MAKE_JNI_NATIVE_METHOD("nativeGetWidth", "(J)I", android_graphics_Picture_getWidth), + MAKE_JNI_NATIVE_METHOD("nativeGetHeight", "(J)I", android_graphics_Picture_getHeight), + MAKE_JNI_NATIVE_METHOD("nativeConstructor", "(J)J", android_graphics_Picture_newPicture), + MAKE_JNI_NATIVE_METHOD("nativeCreateFromStream", "(Ljava/io/InputStream;[B)J", android_graphics_Picture_deserialize), + MAKE_JNI_NATIVE_METHOD("nativeBeginRecording", "(JII)J", android_graphics_Picture_beginRecording), + MAKE_JNI_NATIVE_METHOD("nativeEndRecording", "(J)V", android_graphics_Picture_endRecording), + MAKE_JNI_NATIVE_METHOD("nativeDraw", "(JJ)V", android_graphics_Picture_draw), + MAKE_JNI_NATIVE_METHOD("nativeWriteToStream", "(JLjava/io/OutputStream;[B)Z", android_graphics_Picture_serialize), + MAKE_JNI_NATIVE_METHOD("nativeDestructor","(J)V", android_graphics_Picture_killPicture) +}; + +int register_android_graphics_Picture(JNIEnv* env) { + return RegisterMethodsOrDie(env, "android/graphics/Picture", gMethods.data(), gMethods.size()); +} + +}; // namespace android diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp new file mode 100644 index 000000000000..a8246c7d84b7 --- /dev/null +++ b/libs/hwui/jni/android_graphics_RenderNode.cpp @@ -0,0 +1,765 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "OpenGLRenderer" +#define ATRACE_TAG ATRACE_TAG_VIEW +#include "jni.h" +#include "GraphicsJNI.h" +#include +#include + +#include +#include +#include +#include +#ifdef __ANDROID__ // Layoutlib does not support CanvasContext +#include +#endif +#include +#include +#include + +#include "core_jni_helpers.h" + +namespace android { + +using namespace uirenderer; + +#define SET_AND_DIRTY(prop, val, dirtyFlag) \ + (reinterpret_cast(renderNodePtr)->mutateStagingProperties().prop(val) \ + ? (reinterpret_cast(renderNodePtr)->setPropertyFieldsDirty(dirtyFlag), true) \ + : false) + +// ---------------------------------------------------------------------------- +// DisplayList view properties +// ---------------------------------------------------------------------------- + +static void android_view_RenderNode_output(JNIEnv* env, jobject clazz, jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + renderNode->output(); +} + +static jint android_view_RenderNode_getUsageSize(JNIEnv* env, jobject clazz, jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + return renderNode->getUsageSize(); +} + +static jint android_view_RenderNode_getAllocatedSize(JNIEnv* env, jobject clazz, jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + return renderNode->getAllocatedSize(); +} + +static jlong android_view_RenderNode_create(JNIEnv* env, jobject, jstring name) { + RenderNode* renderNode = new RenderNode(); + renderNode->incStrong(0); + if (name != NULL) { + const char* textArray = env->GetStringUTFChars(name, NULL); + renderNode->setName(textArray); + env->ReleaseStringUTFChars(name, textArray); + } + return reinterpret_cast(renderNode); +} + +static void releaseRenderNode(RenderNode* renderNode) { + renderNode->decStrong(0); +} + +static jlong android_view_RenderNode_getNativeFinalizer(JNIEnv* env, + jobject clazz) { + return static_cast(reinterpret_cast(&releaseRenderNode)); +} + +static void android_view_RenderNode_setDisplayList(JNIEnv* env, + jobject clazz, jlong renderNodePtr, jlong displayListPtr) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + DisplayList* newData = reinterpret_cast(displayListPtr); + renderNode->setStagingDisplayList(newData); +} + +static jboolean android_view_RenderNode_isValid(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + return reinterpret_cast(renderNodePtr)->isValid(); +} + +// ---------------------------------------------------------------------------- +// RenderProperties - setters +// ---------------------------------------------------------------------------- + +static jboolean android_view_RenderNode_setLayerType(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jint jlayerType) { + LayerType layerType = static_cast(jlayerType); + return SET_AND_DIRTY(mutateLayerProperties().setType, layerType, RenderNode::GENERIC); +} + +static jboolean android_view_RenderNode_setLayerPaint(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jlong paintPtr) { + Paint* paint = reinterpret_cast(paintPtr); + return SET_AND_DIRTY(mutateLayerProperties().setFromPaint, paint, RenderNode::GENERIC); +} + +static jboolean android_view_RenderNode_setStaticMatrix(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jlong matrixPtr) { + SkMatrix* matrix = reinterpret_cast(matrixPtr); + return SET_AND_DIRTY(setStaticMatrix, matrix, RenderNode::GENERIC); +} + +static jboolean android_view_RenderNode_setAnimationMatrix(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jlong matrixPtr) { + SkMatrix* matrix = reinterpret_cast(matrixPtr); + return SET_AND_DIRTY(setAnimationMatrix, matrix, RenderNode::GENERIC); +} + +static jboolean android_view_RenderNode_setClipToBounds(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, + jboolean clipToBounds) { + return SET_AND_DIRTY(setClipToBounds, clipToBounds, RenderNode::GENERIC); +} + +static jboolean android_view_RenderNode_setClipBounds(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, + jint left, jint top, jint right, jint bottom) { + android::uirenderer::Rect clipBounds(left, top, right, bottom); + return SET_AND_DIRTY(setClipBounds, clipBounds, RenderNode::GENERIC); +} + +static jboolean android_view_RenderNode_setClipBoundsEmpty(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + return SET_AND_DIRTY(setClipBoundsEmpty,, RenderNode::GENERIC); +} + +static jboolean android_view_RenderNode_setProjectBackwards(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, + jboolean shouldProject) { + return SET_AND_DIRTY(setProjectBackwards, shouldProject, RenderNode::GENERIC); +} + +static jboolean android_view_RenderNode_setProjectionReceiver(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, + jboolean shouldRecieve) { + return SET_AND_DIRTY(setProjectionReceiver, shouldRecieve, RenderNode::GENERIC); +} + +static jboolean android_view_RenderNode_setOutlineRoundRect(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, + jint left, jint top, jint right, jint bottom, jfloat radius, jfloat alpha) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + renderNode->mutateStagingProperties().mutableOutline().setRoundRect(left, top, right, bottom, + radius, alpha); + renderNode->setPropertyFieldsDirty(RenderNode::GENERIC); + return true; +} + +static jboolean android_view_RenderNode_setOutlinePath(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, + jlong outlinePathPtr, jfloat alpha) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + SkPath* outlinePath = reinterpret_cast(outlinePathPtr); + renderNode->mutateStagingProperties().mutableOutline().setPath(outlinePath, alpha); + renderNode->setPropertyFieldsDirty(RenderNode::GENERIC); + return true; +} + +static jboolean android_view_RenderNode_setOutlineEmpty(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + renderNode->mutateStagingProperties().mutableOutline().setEmpty(); + renderNode->setPropertyFieldsDirty(RenderNode::GENERIC); + return true; +} + +static jboolean android_view_RenderNode_setOutlineNone(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + renderNode->mutateStagingProperties().mutableOutline().setNone(); + renderNode->setPropertyFieldsDirty(RenderNode::GENERIC); + return true; +} + +static jboolean android_view_RenderNode_hasShadow(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + return renderNode->stagingProperties().hasShadow(); +} + +static jboolean android_view_RenderNode_setSpotShadowColor(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jint shadowColor) { + return SET_AND_DIRTY(setSpotShadowColor, + static_cast(shadowColor), RenderNode::GENERIC); +} + +static jint android_view_RenderNode_getSpotShadowColor(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + return renderNode->stagingProperties().getSpotShadowColor(); +} + +static jboolean android_view_RenderNode_setAmbientShadowColor(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, + jint shadowColor) { + return SET_AND_DIRTY(setAmbientShadowColor, + static_cast(shadowColor), RenderNode::GENERIC); +} + +static jint android_view_RenderNode_getAmbientShadowColor(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + return renderNode->stagingProperties().getAmbientShadowColor(); +} + +static jboolean android_view_RenderNode_setClipToOutline(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, + jboolean clipToOutline) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + renderNode->mutateStagingProperties().mutableOutline().setShouldClip(clipToOutline); + renderNode->setPropertyFieldsDirty(RenderNode::GENERIC); + return true; +} + +static jboolean android_view_RenderNode_setRevealClip(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jboolean shouldClip, + jfloat x, jfloat y, jfloat radius) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + renderNode->mutateStagingProperties().mutableRevealClip().set( + shouldClip, x, y, radius); + renderNode->setPropertyFieldsDirty(RenderNode::GENERIC); + return true; +} + +static jboolean android_view_RenderNode_setAlpha(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float alpha) { + return SET_AND_DIRTY(setAlpha, alpha, RenderNode::ALPHA); +} + +static jboolean android_view_RenderNode_setHasOverlappingRendering(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, + bool hasOverlappingRendering) { + return SET_AND_DIRTY(setHasOverlappingRendering, hasOverlappingRendering, + RenderNode::GENERIC); +} + +static void android_view_RenderNode_setUsageHint(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jint usageHint) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + renderNode->setUsageHint(static_cast(usageHint)); +} + +static jboolean android_view_RenderNode_setElevation(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float elevation) { + return SET_AND_DIRTY(setElevation, elevation, RenderNode::Z); +} + +static jboolean android_view_RenderNode_setTranslationX(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float tx) { + return SET_AND_DIRTY(setTranslationX, tx, RenderNode::TRANSLATION_X | RenderNode::X); +} + +static jboolean android_view_RenderNode_setTranslationY(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float ty) { + return SET_AND_DIRTY(setTranslationY, ty, RenderNode::TRANSLATION_Y | RenderNode::Y); +} + +static jboolean android_view_RenderNode_setTranslationZ(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float tz) { + return SET_AND_DIRTY(setTranslationZ, tz, RenderNode::TRANSLATION_Z | RenderNode::Z); +} + +static jboolean android_view_RenderNode_setRotation(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float rotation) { + return SET_AND_DIRTY(setRotation, rotation, RenderNode::ROTATION); +} + +static jboolean android_view_RenderNode_setRotationX(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float rx) { + return SET_AND_DIRTY(setRotationX, rx, RenderNode::ROTATION_X); +} + +static jboolean android_view_RenderNode_setRotationY(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float ry) { + return SET_AND_DIRTY(setRotationY, ry, RenderNode::ROTATION_Y); +} + +static jboolean android_view_RenderNode_setScaleX(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float sx) { + return SET_AND_DIRTY(setScaleX, sx, RenderNode::SCALE_X); +} + +static jboolean android_view_RenderNode_setScaleY(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float sy) { + return SET_AND_DIRTY(setScaleY, sy, RenderNode::SCALE_Y); +} + +static jboolean android_view_RenderNode_setPivotX(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float px) { + return SET_AND_DIRTY(setPivotX, px, RenderNode::GENERIC); +} + +static jboolean android_view_RenderNode_setPivotY(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float py) { + return SET_AND_DIRTY(setPivotY, py, RenderNode::GENERIC); +} + +static jboolean android_view_RenderNode_resetPivot(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + return SET_AND_DIRTY(resetPivot, /* void */, RenderNode::GENERIC); +} + +static jboolean android_view_RenderNode_setCameraDistance(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float distance) { + return SET_AND_DIRTY(setCameraDistance, distance, RenderNode::GENERIC); +} + +static jboolean android_view_RenderNode_setLeft(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, int left) { + return SET_AND_DIRTY(setLeft, left, RenderNode::X); +} + +static jboolean android_view_RenderNode_setTop(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, int top) { + return SET_AND_DIRTY(setTop, top, RenderNode::Y); +} + +static jboolean android_view_RenderNode_setRight(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, int right) { + return SET_AND_DIRTY(setRight, right, RenderNode::X); +} + +static jboolean android_view_RenderNode_setBottom(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, int bottom) { + return SET_AND_DIRTY(setBottom, bottom, RenderNode::Y); +} + +static jint android_view_RenderNode_getLeft(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + return reinterpret_cast(renderNodePtr)->stagingProperties().getLeft(); +} + +static jint android_view_RenderNode_getTop(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + return reinterpret_cast(renderNodePtr)->stagingProperties().getTop(); +} + +static jint android_view_RenderNode_getRight(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + return reinterpret_cast(renderNodePtr)->stagingProperties().getRight(); +} + +static jint android_view_RenderNode_getBottom(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + return reinterpret_cast(renderNodePtr)->stagingProperties().getBottom(); +} + +static jboolean android_view_RenderNode_setLeftTopRightBottom(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, + int left, int top, int right, int bottom) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + if (renderNode->mutateStagingProperties().setLeftTopRightBottom(left, top, right, bottom)) { + renderNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); + return true; + } + return false; +} + +static jboolean android_view_RenderNode_offsetLeftAndRight(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jint offset) { + return SET_AND_DIRTY(offsetLeftRight, offset, RenderNode::X); +} + +static jboolean android_view_RenderNode_offsetTopAndBottom(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jint offset) { + return SET_AND_DIRTY(offsetTopBottom, offset, RenderNode::Y); +} + +// ---------------------------------------------------------------------------- +// RenderProperties - getters +// ---------------------------------------------------------------------------- + +static jboolean android_view_RenderNode_hasOverlappingRendering(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + return renderNode->stagingProperties().hasOverlappingRendering(); +} + +static jboolean android_view_RenderNode_getAnimationMatrix(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jlong outMatrixPtr) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + SkMatrix* outMatrix = reinterpret_cast(outMatrixPtr); + + const SkMatrix* animationMatrix = renderNode->stagingProperties().getAnimationMatrix(); + + if (animationMatrix) { + *outMatrix = *animationMatrix; + return JNI_TRUE; + } + return JNI_FALSE; +} + +static jboolean android_view_RenderNode_getClipToBounds(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + return renderNode->stagingProperties().getClipToBounds(); +} + +static jboolean android_view_RenderNode_getClipToOutline(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + return renderNode->stagingProperties().getOutline().getShouldClip(); +} + +static jfloat android_view_RenderNode_getAlpha(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + return renderNode->stagingProperties().getAlpha(); +} + +static jfloat android_view_RenderNode_getCameraDistance(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + return renderNode->stagingProperties().getCameraDistance(); +} + +static jfloat android_view_RenderNode_getScaleX(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + return renderNode->stagingProperties().getScaleX(); +} + +static jfloat android_view_RenderNode_getScaleY(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + return renderNode->stagingProperties().getScaleY(); +} + +static jfloat android_view_RenderNode_getElevation(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + return renderNode->stagingProperties().getElevation(); +} + +static jfloat android_view_RenderNode_getTranslationX(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + return renderNode->stagingProperties().getTranslationX(); +} + +static jfloat android_view_RenderNode_getTranslationY(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + return renderNode->stagingProperties().getTranslationY(); +} + +static jfloat android_view_RenderNode_getTranslationZ(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + return renderNode->stagingProperties().getTranslationZ(); +} + +static jfloat android_view_RenderNode_getRotation(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + return renderNode->stagingProperties().getRotation(); +} + +static jfloat android_view_RenderNode_getRotationX(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + return renderNode->stagingProperties().getRotationX(); +} + +static jfloat android_view_RenderNode_getRotationY(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + return renderNode->stagingProperties().getRotationY(); +} + +static jboolean android_view_RenderNode_isPivotExplicitlySet(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + return renderNode->stagingProperties().isPivotExplicitlySet(); +} + +static jboolean android_view_RenderNode_hasIdentityMatrix(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + renderNode->mutateStagingProperties().updateMatrix(); + return !renderNode->stagingProperties().hasTransformMatrix(); +} + +static jint android_view_RenderNode_getLayerType(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + return static_cast(renderNode->stagingProperties().layerProperties().type()); +} + +// ---------------------------------------------------------------------------- +// RenderProperties - computed getters +// ---------------------------------------------------------------------------- + +static void getTransformMatrix(jlong renderNodePtr, jlong outMatrixPtr) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + SkMatrix* outMatrix = reinterpret_cast(outMatrixPtr); + + renderNode->mutateStagingProperties().updateMatrix(); + const SkMatrix* transformMatrix = renderNode->stagingProperties().getTransformMatrix(); + + if (transformMatrix) { + *outMatrix = *transformMatrix; + } else { + outMatrix->setIdentity(); + } +} + +static void android_view_RenderNode_getTransformMatrix(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jlong outMatrixPtr) { + getTransformMatrix(renderNodePtr, outMatrixPtr); +} + +static void android_view_RenderNode_getInverseTransformMatrix(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, + jlong outMatrixPtr) { + // load transform matrix + getTransformMatrix(renderNodePtr, outMatrixPtr); + SkMatrix* outMatrix = reinterpret_cast(outMatrixPtr); + + // return it inverted + if (!outMatrix->invert(outMatrix)) { + // failed to load inverse, pass back identity + outMatrix->setIdentity(); + } +} + +static jfloat android_view_RenderNode_getPivotX(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + renderNode->mutateStagingProperties().updateMatrix(); + return renderNode->stagingProperties().getPivotX(); +} + +static jfloat android_view_RenderNode_getPivotY(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + renderNode->mutateStagingProperties().updateMatrix(); + return renderNode->stagingProperties().getPivotY(); +} + +static jint android_view_RenderNode_getWidth(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + return reinterpret_cast(renderNodePtr)->stagingProperties().getWidth(); +} + +static jint android_view_RenderNode_getHeight(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + return reinterpret_cast(renderNodePtr)->stagingProperties().getHeight(); +} + +static jboolean android_view_RenderNode_setAllowForceDark(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jboolean allow) { + return SET_AND_DIRTY(setAllowForceDark, allow, RenderNode::GENERIC); +} + +static jboolean android_view_RenderNode_getAllowForceDark(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + return reinterpret_cast(renderNodePtr)->stagingProperties().getAllowForceDark(); +} + +static jlong android_view_RenderNode_getUniqueId(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + return reinterpret_cast(renderNodePtr)->uniqueId(); +} + +// ---------------------------------------------------------------------------- +// RenderProperties - Animations +// ---------------------------------------------------------------------------- + +static void android_view_RenderNode_addAnimator(JNIEnv* env, jobject clazz, jlong renderNodePtr, + jlong animatorPtr) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + RenderPropertyAnimator* animator = reinterpret_cast(animatorPtr); + renderNode->addAnimator(animator); +} + +static void android_view_RenderNode_endAllAnimators(JNIEnv* env, jobject clazz, + jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + renderNode->animators().endAllStagingAnimators(); +} + +// ---------------------------------------------------------------------------- +// SurfaceView position callback +// ---------------------------------------------------------------------------- + +jmethodID gPositionListener_PositionChangedMethod; +jmethodID gPositionListener_PositionLostMethod; + +static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject, + jlong renderNodePtr, jobject listener) { + class PositionListenerTrampoline : public RenderNode::PositionListener { + public: + PositionListenerTrampoline(JNIEnv* env, jobject listener) { + env->GetJavaVM(&mVm); + mWeakRef = env->NewWeakGlobalRef(listener); + } + + virtual ~PositionListenerTrampoline() { + jnienv()->DeleteWeakGlobalRef(mWeakRef); + mWeakRef = nullptr; + } + + virtual void onPositionUpdated(RenderNode& node, const TreeInfo& info) override { + if (CC_UNLIKELY(!mWeakRef || !info.updateWindowPositions)) return; + + Matrix4 transform; + info.damageAccumulator->computeCurrentTransform(&transform); + const RenderProperties& props = node.properties(); + uirenderer::Rect bounds(props.getWidth(), props.getHeight()); + transform.mapRect(bounds); + + if (CC_LIKELY(transform.isPureTranslate())) { + // snap/round the computed bounds, so they match the rounding behavior + // of the clear done in SurfaceView#draw(). + bounds.snapGeometryToPixelBoundaries(false); + } else { + // Conservatively round out so the punched hole (in the ZOrderOnTop = true case) + // doesn't extend beyond the other window + bounds.roundOut(); + } + + if (mPreviousPosition == bounds) { + return; + } + mPreviousPosition = bounds; + +#ifdef __ANDROID__ // Layoutlib does not support CanvasContext + incStrong(0); + auto functor = std::bind( + std::mem_fn(&PositionListenerTrampoline::doUpdatePositionAsync), this, + (jlong) info.canvasContext.getFrameNumber(), + (jint) bounds.left, (jint) bounds.top, + (jint) bounds.right, (jint) bounds.bottom); + + info.canvasContext.enqueueFrameWork(std::move(functor)); +#endif + } + + virtual void onPositionLost(RenderNode& node, const TreeInfo* info) override { + if (CC_UNLIKELY(!mWeakRef || (info && !info->updateWindowPositions))) return; + + if (mPreviousPosition.isEmpty()) { + return; + } + mPreviousPosition.setEmpty(); + + ATRACE_NAME("SurfaceView position lost"); + JNIEnv* env = jnienv(); + jobject localref = env->NewLocalRef(mWeakRef); + if (CC_UNLIKELY(!localref)) { + jnienv()->DeleteWeakGlobalRef(mWeakRef); + mWeakRef = nullptr; + return; + } +#ifdef __ANDROID__ // Layoutlib does not support CanvasContext + // TODO: Remember why this is synchronous and then make a comment + env->CallVoidMethod(localref, gPositionListener_PositionLostMethod, + info ? info->canvasContext.getFrameNumber() : 0); +#endif + env->DeleteLocalRef(localref); + } + + private: + JNIEnv* jnienv() { + JNIEnv* env; + if (mVm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { + LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", mVm); + } + return env; + } + + void doUpdatePositionAsync(jlong frameNumber, jint left, jint top, + jint right, jint bottom) { + ATRACE_NAME("Update SurfaceView position"); + + JNIEnv* env = jnienv(); + jobject localref = env->NewLocalRef(mWeakRef); + if (CC_UNLIKELY(!localref)) { + env->DeleteWeakGlobalRef(mWeakRef); + mWeakRef = nullptr; + } else { + env->CallVoidMethod(localref, gPositionListener_PositionChangedMethod, + frameNumber, left, top, right, bottom); + env->DeleteLocalRef(localref); + } + + // We need to release ourselves here + decStrong(0); + } + + JavaVM* mVm; + jobject mWeakRef; + uirenderer::Rect mPreviousPosition; + }; + + RenderNode* renderNode = reinterpret_cast(renderNodePtr); + renderNode->setPositionListener(new PositionListenerTrampoline(env, listener)); +} + +// ---------------------------------------------------------------------------- +// JNI Glue +// ---------------------------------------------------------------------------- + +const char* const kClassPathName = "android/graphics/RenderNode"; + +static const JNINativeMethod gMethods[] = { +// ---------------------------------------------------------------------------- +// Regular JNI +// ---------------------------------------------------------------------------- + { "nCreate", "(Ljava/lang/String;)J", (void*) android_view_RenderNode_create }, + { "nGetNativeFinalizer", "()J", (void*) android_view_RenderNode_getNativeFinalizer }, + { "nOutput", "(J)V", (void*) android_view_RenderNode_output }, + { "nGetUsageSize", "(J)I", (void*) android_view_RenderNode_getUsageSize }, + { "nGetAllocatedSize", "(J)I", (void*) android_view_RenderNode_getAllocatedSize }, + { "nAddAnimator", "(JJ)V", (void*) android_view_RenderNode_addAnimator }, + { "nEndAllAnimators", "(J)V", (void*) android_view_RenderNode_endAllAnimators }, + { "nRequestPositionUpdates", "(JLandroid/graphics/RenderNode$PositionUpdateListener;)V", (void*) android_view_RenderNode_requestPositionUpdates }, + { "nSetDisplayList", "(JJ)V", (void*) android_view_RenderNode_setDisplayList }, + + +// ---------------------------------------------------------------------------- +// Fast JNI via @CriticalNative annotation in RenderNode.java +// ---------------------------------------------------------------------------- + { "nSetDisplayList", "(JJ)V", (void*) android_view_RenderNode_setDisplayList }, + + +// ---------------------------------------------------------------------------- +// Critical JNI via @CriticalNative annotation in RenderNode.java +// ---------------------------------------------------------------------------- + { "nIsValid", "(J)Z", (void*) android_view_RenderNode_isValid }, + { "nSetLayerType", "(JI)Z", (void*) android_view_RenderNode_setLayerType }, + { "nGetLayerType", "(J)I", (void*) android_view_RenderNode_getLayerType }, + { "nSetLayerPaint", "(JJ)Z", (void*) android_view_RenderNode_setLayerPaint }, + { "nSetStaticMatrix", "(JJ)Z", (void*) android_view_RenderNode_setStaticMatrix }, + { "nSetAnimationMatrix", "(JJ)Z", (void*) android_view_RenderNode_setAnimationMatrix }, + { "nGetAnimationMatrix", "(JJ)Z", (void*) android_view_RenderNode_getAnimationMatrix }, + { "nSetClipToBounds", "(JZ)Z", (void*) android_view_RenderNode_setClipToBounds }, + { "nGetClipToBounds", "(J)Z", (void*) android_view_RenderNode_getClipToBounds }, + { "nSetClipBounds", "(JIIII)Z", (void*) android_view_RenderNode_setClipBounds }, + { "nSetClipBoundsEmpty", "(J)Z", (void*) android_view_RenderNode_setClipBoundsEmpty }, + { "nSetProjectBackwards", "(JZ)Z", (void*) android_view_RenderNode_setProjectBackwards }, + { "nSetProjectionReceiver","(JZ)Z", (void*) android_view_RenderNode_setProjectionReceiver }, + + { "nSetOutlineRoundRect", "(JIIIIFF)Z", (void*) android_view_RenderNode_setOutlineRoundRect }, + { "nSetOutlinePath", "(JJF)Z", (void*) android_view_RenderNode_setOutlinePath }, + { "nSetOutlineEmpty", "(J)Z", (void*) android_view_RenderNode_setOutlineEmpty }, + { "nSetOutlineNone", "(J)Z", (void*) android_view_RenderNode_setOutlineNone }, + { "nHasShadow", "(J)Z", (void*) android_view_RenderNode_hasShadow }, + { "nSetSpotShadowColor", "(JI)Z", (void*) android_view_RenderNode_setSpotShadowColor }, + { "nGetSpotShadowColor", "(J)I", (void*) android_view_RenderNode_getSpotShadowColor }, + { "nSetAmbientShadowColor","(JI)Z", (void*) android_view_RenderNode_setAmbientShadowColor }, + { "nGetAmbientShadowColor","(J)I", (void*) android_view_RenderNode_getAmbientShadowColor }, + { "nSetClipToOutline", "(JZ)Z", (void*) android_view_RenderNode_setClipToOutline }, + { "nSetRevealClip", "(JZFFF)Z", (void*) android_view_RenderNode_setRevealClip }, + + { "nSetAlpha", "(JF)Z", (void*) android_view_RenderNode_setAlpha }, + { "nSetHasOverlappingRendering", "(JZ)Z", + (void*) android_view_RenderNode_setHasOverlappingRendering }, + { "nSetUsageHint", "(JI)V", (void*) android_view_RenderNode_setUsageHint }, + { "nSetElevation", "(JF)Z", (void*) android_view_RenderNode_setElevation }, + { "nSetTranslationX", "(JF)Z", (void*) android_view_RenderNode_setTranslationX }, + { "nSetTranslationY", "(JF)Z", (void*) android_view_RenderNode_setTranslationY }, + { "nSetTranslationZ", "(JF)Z", (void*) android_view_RenderNode_setTranslationZ }, + { "nSetRotation", "(JF)Z", (void*) android_view_RenderNode_setRotation }, + { "nSetRotationX", "(JF)Z", (void*) android_view_RenderNode_setRotationX }, + { "nSetRotationY", "(JF)Z", (void*) android_view_RenderNode_setRotationY }, + { "nSetScaleX", "(JF)Z", (void*) android_view_RenderNode_setScaleX }, + { "nSetScaleY", "(JF)Z", (void*) android_view_RenderNode_setScaleY }, + { "nSetPivotX", "(JF)Z", (void*) android_view_RenderNode_setPivotX }, + { "nSetPivotY", "(JF)Z", (void*) android_view_RenderNode_setPivotY }, + { "nResetPivot", "(J)Z", (void*) android_view_RenderNode_resetPivot }, + { "nSetCameraDistance", "(JF)Z", (void*) android_view_RenderNode_setCameraDistance }, + { "nSetLeft", "(JI)Z", (void*) android_view_RenderNode_setLeft }, + { "nSetTop", "(JI)Z", (void*) android_view_RenderNode_setTop }, + { "nSetRight", "(JI)Z", (void*) android_view_RenderNode_setRight }, + { "nSetBottom", "(JI)Z", (void*) android_view_RenderNode_setBottom }, + { "nGetLeft", "(J)I", (void*) android_view_RenderNode_getLeft }, + { "nGetTop", "(J)I", (void*) android_view_RenderNode_getTop }, + { "nGetRight", "(J)I", (void*) android_view_RenderNode_getRight }, + { "nGetBottom", "(J)I", (void*) android_view_RenderNode_getBottom }, + { "nSetLeftTopRightBottom","(JIIII)Z", (void*) android_view_RenderNode_setLeftTopRightBottom }, + { "nOffsetLeftAndRight", "(JI)Z", (void*) android_view_RenderNode_offsetLeftAndRight }, + { "nOffsetTopAndBottom", "(JI)Z", (void*) android_view_RenderNode_offsetTopAndBottom }, + + { "nHasOverlappingRendering", "(J)Z", (void*) android_view_RenderNode_hasOverlappingRendering }, + { "nGetClipToOutline", "(J)Z", (void*) android_view_RenderNode_getClipToOutline }, + { "nGetAlpha", "(J)F", (void*) android_view_RenderNode_getAlpha }, + { "nGetCameraDistance", "(J)F", (void*) android_view_RenderNode_getCameraDistance }, + { "nGetScaleX", "(J)F", (void*) android_view_RenderNode_getScaleX }, + { "nGetScaleY", "(J)F", (void*) android_view_RenderNode_getScaleY }, + { "nGetElevation", "(J)F", (void*) android_view_RenderNode_getElevation }, + { "nGetTranslationX", "(J)F", (void*) android_view_RenderNode_getTranslationX }, + { "nGetTranslationY", "(J)F", (void*) android_view_RenderNode_getTranslationY }, + { "nGetTranslationZ", "(J)F", (void*) android_view_RenderNode_getTranslationZ }, + { "nGetRotation", "(J)F", (void*) android_view_RenderNode_getRotation }, + { "nGetRotationX", "(J)F", (void*) android_view_RenderNode_getRotationX }, + { "nGetRotationY", "(J)F", (void*) android_view_RenderNode_getRotationY }, + { "nIsPivotExplicitlySet", "(J)Z", (void*) android_view_RenderNode_isPivotExplicitlySet }, + { "nHasIdentityMatrix", "(J)Z", (void*) android_view_RenderNode_hasIdentityMatrix }, + + { "nGetTransformMatrix", "(JJ)V", (void*) android_view_RenderNode_getTransformMatrix }, + { "nGetInverseTransformMatrix","(JJ)V", (void*) android_view_RenderNode_getInverseTransformMatrix }, + + { "nGetPivotX", "(J)F", (void*) android_view_RenderNode_getPivotX }, + { "nGetPivotY", "(J)F", (void*) android_view_RenderNode_getPivotY }, + { "nGetWidth", "(J)I", (void*) android_view_RenderNode_getWidth }, + { "nGetHeight", "(J)I", (void*) android_view_RenderNode_getHeight }, + { "nSetAllowForceDark", "(JZ)Z", (void*) android_view_RenderNode_setAllowForceDark }, + { "nGetAllowForceDark", "(J)Z", (void*) android_view_RenderNode_getAllowForceDark }, + { "nGetUniqueId", "(J)J", (void*) android_view_RenderNode_getUniqueId }, +}; + +int register_android_view_RenderNode(JNIEnv* env) { + jclass clazz = FindClassOrDie(env, "android/graphics/RenderNode$PositionUpdateListener"); + gPositionListener_PositionChangedMethod = GetMethodIDOrDie(env, clazz, + "positionChanged", "(JIIII)V"); + gPositionListener_PositionLostMethod = GetMethodIDOrDie(env, clazz, + "positionLost", "(J)V"); + return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); +} + +}; + diff --git a/libs/hwui/jni/android_graphics_TextureLayer.cpp b/libs/hwui/jni/android_graphics_TextureLayer.cpp new file mode 100644 index 000000000000..40f618025f99 --- /dev/null +++ b/libs/hwui/jni/android_graphics_TextureLayer.cpp @@ -0,0 +1,90 @@ +/* + * 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. + */ + +#define LOG_TAG "OpenGLRenderer" + +#include "jni.h" +#include + +#include +#include "core_jni_helpers.h" + +#include +#include +#include + +namespace android { + +using namespace uirenderer; + +static jboolean TextureLayer_prepare(JNIEnv* env, jobject clazz, + jlong layerUpdaterPtr, jint width, jint height, jboolean isOpaque) { + DeferredLayerUpdater* layer = reinterpret_cast(layerUpdaterPtr); + bool changed = false; + changed |= layer->setSize(width, height); + changed |= layer->setBlend(!isOpaque); + return changed; +} + +static void TextureLayer_setLayerPaint(JNIEnv* env, jobject clazz, + jlong layerUpdaterPtr, jlong paintPtr) { + DeferredLayerUpdater* layer = reinterpret_cast(layerUpdaterPtr); + if (layer) { + Paint* paint = reinterpret_cast(paintPtr); + layer->setPaint(paint); + } +} + +static void TextureLayer_setTransform(JNIEnv* env, jobject clazz, + jlong layerUpdaterPtr, jlong matrixPtr) { + DeferredLayerUpdater* layer = reinterpret_cast(layerUpdaterPtr); + SkMatrix* matrix = reinterpret_cast(matrixPtr); + layer->setTransform(matrix); +} + +static void TextureLayer_setSurfaceTexture(JNIEnv* env, jobject clazz, + jlong layerUpdaterPtr, jobject surface) { + DeferredLayerUpdater* layer = reinterpret_cast(layerUpdaterPtr); + ASurfaceTexture* surfaceTexture = ASurfaceTexture_fromSurfaceTexture(env, surface); + layer->setSurfaceTexture(AutoTextureRelease(surfaceTexture, &ASurfaceTexture_release)); +} + +static void TextureLayer_updateSurfaceTexture(JNIEnv* env, jobject clazz, + jlong layerUpdaterPtr) { + DeferredLayerUpdater* layer = reinterpret_cast(layerUpdaterPtr); + layer->updateTexImage(); +} + +// ---------------------------------------------------------------------------- +// JNI Glue +// ---------------------------------------------------------------------------- + +const char* const kClassPathName = "android/view/TextureLayer"; + +static const JNINativeMethod gMethods[] = { + { "nPrepare", "(JIIZ)Z", (void*) TextureLayer_prepare }, + { "nSetLayerPaint", "(JJ)V", (void*) TextureLayer_setLayerPaint }, + { "nSetTransform", "(JJ)V", (void*) TextureLayer_setTransform }, + { "nSetSurfaceTexture", "(JLandroid/graphics/SurfaceTexture;)V", + (void*) TextureLayer_setSurfaceTexture }, + { "nUpdateSurfaceTexture", "(J)V", (void*) TextureLayer_updateSurfaceTexture }, +}; + +int register_android_view_TextureLayer(JNIEnv* env) { + return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); +} + +}; diff --git a/libs/hwui/jni/android_graphics_animation_NativeInterpolatorFactory.cpp b/libs/hwui/jni/android_graphics_animation_NativeInterpolatorFactory.cpp new file mode 100644 index 000000000000..2073ac2d24be --- /dev/null +++ b/libs/hwui/jni/android_graphics_animation_NativeInterpolatorFactory.cpp @@ -0,0 +1,114 @@ +/* + * 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. + */ + +#define LOG_TAG "OpenGLRenderer" + +#include "jni.h" +#include +#include +#include "core_jni_helpers.h" + +#include + +namespace android { + +using namespace uirenderer; + +static jlong createAccelerateDecelerateInterpolator(JNIEnv* env, jobject clazz) { + return reinterpret_cast(new AccelerateDecelerateInterpolator()); +} + +static jlong createAccelerateInterpolator(JNIEnv* env, jobject clazz, jfloat factor) { + return reinterpret_cast(new AccelerateInterpolator(factor)); +} + +static jlong createAnticipateInterpolator(JNIEnv* env, jobject clazz, jfloat tension) { + return reinterpret_cast(new AnticipateInterpolator(tension)); +} + +static jlong createAnticipateOvershootInterpolator(JNIEnv* env, jobject clazz, jfloat tension) { + return reinterpret_cast(new AnticipateOvershootInterpolator(tension)); +} + +static jlong createBounceInterpolator(JNIEnv* env, jobject clazz) { + return reinterpret_cast(new BounceInterpolator()); +} + +static jlong createCycleInterpolator(JNIEnv* env, jobject clazz, jfloat cycles) { + return reinterpret_cast(new CycleInterpolator(cycles)); +} + +static jlong createDecelerateInterpolator(JNIEnv* env, jobject clazz, jfloat factor) { + return reinterpret_cast(new DecelerateInterpolator(factor)); +} + +static jlong createLinearInterpolator(JNIEnv* env, jobject clazz) { + return reinterpret_cast(new LinearInterpolator()); +} + +static jlong createOvershootInterpolator(JNIEnv* env, jobject clazz, jfloat tension) { + return reinterpret_cast(new OvershootInterpolator(tension)); +} + +static jlong createPathInterpolator(JNIEnv* env, jobject clazz, jfloatArray jX, jfloatArray jY) { + jsize lenX = env->GetArrayLength(jX); + jsize lenY = env->GetArrayLength(jY); + LOG_ALWAYS_FATAL_IF(lenX != lenY || lenX <= 0, "Invalid path interpolator, x size: %d," + " y size: %d", lenX, lenY); + std::vector x(lenX); + std::vector y(lenY); + env->GetFloatArrayRegion(jX, 0, lenX, x.data()); + env->GetFloatArrayRegion(jY, 0, lenX, y.data()); + + return reinterpret_cast(new PathInterpolator(std::move(x), std::move(y))); +} + +static jlong createLutInterpolator(JNIEnv* env, jobject clazz, jfloatArray jlut) { + jsize len = env->GetArrayLength(jlut); + if (len <= 0) { + return 0; + } + float* lut = new float[len]; + env->GetFloatArrayRegion(jlut, 0, len, lut); + return reinterpret_cast(new LUTInterpolator(lut, len)); +} + +// ---------------------------------------------------------------------------- +// JNI Glue +// ---------------------------------------------------------------------------- + +const char* const kClassPathName = "android/graphics/animation/NativeInterpolatorFactory"; + +static const JNINativeMethod gMethods[] = { + { "createAccelerateDecelerateInterpolator", "()J", (void*) createAccelerateDecelerateInterpolator }, + { "createAccelerateInterpolator", "(F)J", (void*) createAccelerateInterpolator }, + { "createAnticipateInterpolator", "(F)J", (void*) createAnticipateInterpolator }, + { "createAnticipateOvershootInterpolator", "(F)J", (void*) createAnticipateOvershootInterpolator }, + { "createBounceInterpolator", "()J", (void*) createBounceInterpolator }, + { "createCycleInterpolator", "(F)J", (void*) createCycleInterpolator }, + { "createDecelerateInterpolator", "(F)J", (void*) createDecelerateInterpolator }, + { "createLinearInterpolator", "()J", (void*) createLinearInterpolator }, + { "createOvershootInterpolator", "(F)J", (void*) createOvershootInterpolator }, + { "createPathInterpolator", "([F[F)J", (void*) createPathInterpolator }, + { "createLutInterpolator", "([F)J", (void*) createLutInterpolator }, +}; + +int register_android_graphics_animation_NativeInterpolatorFactory(JNIEnv* env) { + return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); +} + + +} // namespace android diff --git a/libs/hwui/jni/android_graphics_animation_RenderNodeAnimator.cpp b/libs/hwui/jni/android_graphics_animation_RenderNodeAnimator.cpp new file mode 100644 index 000000000000..878d4fc13f6d --- /dev/null +++ b/libs/hwui/jni/android_graphics_animation_RenderNodeAnimator.cpp @@ -0,0 +1,225 @@ +/* + * 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. + */ + +#define LOG_TAG "OpenGLRenderer" + +#include "jni.h" +#include +#include + +#include +#include +#include + +#include "core_jni_helpers.h" + +namespace android { + +using namespace uirenderer; + +static struct { + jclass clazz; + + jmethodID callOnFinished; +} gRenderNodeAnimatorClassInfo; + +static JNIEnv* getEnv(JavaVM* vm) { + JNIEnv* env; + if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { + return 0; + } + return env; +} + +class AnimationListenerLifecycleChecker : public AnimationListener { +public: + virtual void onAnimationFinished(BaseRenderNodeAnimator* animator) { + LOG_ALWAYS_FATAL("Lifecycle failure, nStart(%p) wasn't called", animator); + } +}; + +static AnimationListenerLifecycleChecker sLifecycleChecker; + +class AnimationListenerBridge : public AnimationListener { +public: + // This holds a strong reference to a Java WeakReference object. This avoids + // cyclic-references-of-doom. If you think "I know, just use NewWeakGlobalRef!" + // then you end up with basically a PhantomReference, which is totally not + // what we want. + AnimationListenerBridge(JNIEnv* env, jobject finishListener) { + mFinishListener = env->NewGlobalRef(finishListener); + env->GetJavaVM(&mJvm); + } + + virtual ~AnimationListenerBridge() { + if (mFinishListener) { + onAnimationFinished(NULL); + } + } + + virtual void onAnimationFinished(BaseRenderNodeAnimator*) { + LOG_ALWAYS_FATAL_IF(!mFinishListener, "Finished listener twice?"); + JNIEnv* env = getEnv(mJvm); + env->CallStaticVoidMethod( + gRenderNodeAnimatorClassInfo.clazz, + gRenderNodeAnimatorClassInfo.callOnFinished, + mFinishListener); + releaseJavaObject(); + } + +private: + void releaseJavaObject() { + JNIEnv* env = getEnv(mJvm); + env->DeleteGlobalRef(mFinishListener); + mFinishListener = NULL; + } + + JavaVM* mJvm; + jobject mFinishListener; +}; + +static inline RenderPropertyAnimator::RenderProperty toRenderProperty(jint property) { + LOG_ALWAYS_FATAL_IF(property < 0 || property > RenderPropertyAnimator::ALPHA, + "Invalid property %d", property); + return static_cast(property); +} + +static inline CanvasPropertyPaintAnimator::PaintField toPaintField(jint field) { + LOG_ALWAYS_FATAL_IF(field < 0 + || field > CanvasPropertyPaintAnimator::ALPHA, + "Invalid paint field %d", field); + return static_cast(field); +} + +static jlong createAnimator(JNIEnv* env, jobject clazz, + jint propertyRaw, jfloat finalValue) { + RenderPropertyAnimator::RenderProperty property = toRenderProperty(propertyRaw); + BaseRenderNodeAnimator* animator = new RenderPropertyAnimator(property, finalValue); + animator->setListener(&sLifecycleChecker); + return reinterpret_cast( animator ); +} + +static jlong createCanvasPropertyFloatAnimator(JNIEnv* env, jobject clazz, + jlong canvasPropertyPtr, jfloat finalValue) { + CanvasPropertyPrimitive* canvasProperty = reinterpret_cast(canvasPropertyPtr); + BaseRenderNodeAnimator* animator = new CanvasPropertyPrimitiveAnimator(canvasProperty, finalValue); + animator->setListener(&sLifecycleChecker); + return reinterpret_cast( animator ); +} + +static jlong createCanvasPropertyPaintAnimator(JNIEnv* env, jobject clazz, + jlong canvasPropertyPtr, jint paintFieldRaw, + jfloat finalValue) { + CanvasPropertyPaint* canvasProperty = reinterpret_cast(canvasPropertyPtr); + CanvasPropertyPaintAnimator::PaintField paintField = toPaintField(paintFieldRaw); + BaseRenderNodeAnimator* animator = new CanvasPropertyPaintAnimator( + canvasProperty, paintField, finalValue); + animator->setListener(&sLifecycleChecker); + return reinterpret_cast( animator ); +} + +static jlong createRevealAnimator(JNIEnv* env, jobject clazz, + jint centerX, jint centerY, jfloat startRadius, jfloat endRadius) { + BaseRenderNodeAnimator* animator = new RevealAnimator(centerX, centerY, startRadius, endRadius); + animator->setListener(&sLifecycleChecker); + return reinterpret_cast( animator ); +} + +static void setStartValue(JNIEnv* env, jobject clazz, jlong animatorPtr, jfloat startValue) { + BaseRenderNodeAnimator* animator = reinterpret_cast(animatorPtr); + animator->setStartValue(startValue); +} + +static void setDuration(JNIEnv* env, jobject clazz, jlong animatorPtr, jlong duration) { + LOG_ALWAYS_FATAL_IF(duration < 0, "Duration cannot be negative"); + BaseRenderNodeAnimator* animator = reinterpret_cast(animatorPtr); + animator->setDuration(duration); +} + +static jlong getDuration(JNIEnv* env, jobject clazz, jlong animatorPtr) { + BaseRenderNodeAnimator* animator = reinterpret_cast(animatorPtr); + return static_cast(animator->duration()); +} + +static void setStartDelay(JNIEnv* env, jobject clazz, jlong animatorPtr, jlong startDelay) { + LOG_ALWAYS_FATAL_IF(startDelay < 0, "Start delay cannot be negative"); + BaseRenderNodeAnimator* animator = reinterpret_cast(animatorPtr); + animator->setStartDelay(startDelay); +} + +static void setInterpolator(JNIEnv* env, jobject clazz, jlong animatorPtr, jlong interpolatorPtr) { + BaseRenderNodeAnimator* animator = reinterpret_cast(animatorPtr); + Interpolator* interpolator = reinterpret_cast(interpolatorPtr); + animator->setInterpolator(interpolator); +} + +static void setAllowRunningAsync(JNIEnv* env, jobject clazz, jlong animatorPtr, jboolean mayRunAsync) { + BaseRenderNodeAnimator* animator = reinterpret_cast(animatorPtr); + animator->setAllowRunningAsync(mayRunAsync); +} + +static void setListener(JNIEnv* env, jobject clazz, jlong animatorPtr, jobject finishListener) { + BaseRenderNodeAnimator* animator = reinterpret_cast(animatorPtr); + animator->setListener(new AnimationListenerBridge(env, finishListener)); +} + +static void start(JNIEnv* env, jobject clazz, jlong animatorPtr) { + BaseRenderNodeAnimator* animator = reinterpret_cast(animatorPtr); + animator->start(); +} + +static void end(JNIEnv* env, jobject clazz, jlong animatorPtr) { + BaseRenderNodeAnimator* animator = reinterpret_cast(animatorPtr); + animator->cancel(); +} + +// ---------------------------------------------------------------------------- +// JNI Glue +// ---------------------------------------------------------------------------- + +const char* const kClassPathName = "android/graphics/animation/RenderNodeAnimator"; + +static const JNINativeMethod gMethods[] = { + { "nCreateAnimator", "(IF)J", (void*) createAnimator }, + { "nCreateCanvasPropertyFloatAnimator", "(JF)J", (void*) createCanvasPropertyFloatAnimator }, + { "nCreateCanvasPropertyPaintAnimator", "(JIF)J", (void*) createCanvasPropertyPaintAnimator }, + { "nCreateRevealAnimator", "(IIFF)J", (void*) createRevealAnimator }, + { "nSetStartValue", "(JF)V", (void*) setStartValue }, + { "nSetDuration", "(JJ)V", (void*) setDuration }, + { "nGetDuration", "(J)J", (void*) getDuration }, + { "nSetStartDelay", "(JJ)V", (void*) setStartDelay }, + { "nSetInterpolator", "(JJ)V", (void*) setInterpolator }, + { "nSetAllowRunningAsync", "(JZ)V", (void*) setAllowRunningAsync }, + { "nSetListener", "(JLandroid/graphics/animation/RenderNodeAnimator;)V", (void*) setListener}, + { "nStart", "(J)V", (void*) start}, + { "nEnd", "(J)V", (void*) end }, +}; + +int register_android_graphics_animation_RenderNodeAnimator(JNIEnv* env) { + sLifecycleChecker.incStrong(0); + gRenderNodeAnimatorClassInfo.clazz = FindClassOrDie(env, kClassPathName); + gRenderNodeAnimatorClassInfo.clazz = MakeGlobalRefOrDie(env, + gRenderNodeAnimatorClassInfo.clazz); + + gRenderNodeAnimatorClassInfo.callOnFinished = GetStaticMethodIDOrDie( + env, gRenderNodeAnimatorClassInfo.clazz, "callOnFinished", + "(Landroid/graphics/animation/RenderNodeAnimator;)V"); + + return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); +} + + +} // namespace android diff --git a/libs/hwui/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp b/libs/hwui/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp new file mode 100644 index 000000000000..b6b53666e26e --- /dev/null +++ b/libs/hwui/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2016 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 "android/log.h" + +#include "jni.h" +#include "GraphicsJNI.h" +#include "core_jni_helpers.h" + +#include "Animator.h" +#include "Interpolator.h" +#include "PropertyValuesAnimatorSet.h" +#include "PropertyValuesHolder.h" +#include "VectorDrawable.h" + +namespace android { +using namespace uirenderer; +using namespace VectorDrawable; + +static struct { + jclass clazz; + jmethodID callOnFinished; +} gVectorDrawableAnimatorClassInfo; + +static JNIEnv* getEnv(JavaVM* vm) { + JNIEnv* env; + if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { + return 0; + } + return env; +} + +static AnimationListener* createAnimationListener(JNIEnv* env, jobject finishListener, jint id) { + class AnimationListenerBridge : public AnimationListener { + public: + AnimationListenerBridge(JNIEnv* env, jobject finishListener, jint id) { + mFinishListener = env->NewGlobalRef(finishListener); + env->GetJavaVM(&mJvm); + mId = id; + } + + virtual ~AnimationListenerBridge() { + if (mFinishListener) { + onAnimationFinished(NULL); + } + } + + virtual void onAnimationFinished(BaseRenderNodeAnimator*) { + LOG_ALWAYS_FATAL_IF(!mFinishListener, "Finished listener twice?"); + JNIEnv* env = getEnv(mJvm); + env->CallStaticVoidMethod( + gVectorDrawableAnimatorClassInfo.clazz, + gVectorDrawableAnimatorClassInfo.callOnFinished, + mFinishListener, mId); + releaseJavaObject(); + } + + private: + void releaseJavaObject() { + JNIEnv* env = getEnv(mJvm); + env->DeleteGlobalRef(mFinishListener); + mFinishListener = NULL; + } + + JavaVM* mJvm; + jobject mFinishListener; + jint mId; + }; + return new AnimationListenerBridge(env, finishListener, id); +} + +static void addAnimator(JNIEnv*, jobject, jlong animatorSetPtr, jlong propertyHolderPtr, + jlong interpolatorPtr, jlong startDelay, jlong duration, jint repeatCount, + jint repeatMode) { + PropertyValuesAnimatorSet* set = reinterpret_cast(animatorSetPtr); + PropertyValuesHolder* holder = reinterpret_cast(propertyHolderPtr); + Interpolator* interpolator = reinterpret_cast(interpolatorPtr); + RepeatMode mode = static_cast(repeatMode); + set->addPropertyAnimator(holder, interpolator, startDelay, duration, repeatCount, mode); +} + +static jlong createAnimatorSet(JNIEnv*, jobject) { + PropertyValuesAnimatorSet* animatorSet = new PropertyValuesAnimatorSet(); + return reinterpret_cast(animatorSet); +} + +static void setVectorDrawableTarget(JNIEnv*, jobject,jlong animatorPtr, jlong vectorDrawablePtr) { + VectorDrawable::Tree* tree = reinterpret_cast(vectorDrawablePtr); + PropertyValuesAnimatorSet* set = reinterpret_cast(animatorPtr); + set->setVectorDrawable(tree); +} + +static jlong createGroupPropertyHolder(JNIEnv*, jobject, jlong nativePtr, jint propertyId, + jfloat startValue, jfloat endValue) { + VectorDrawable::Group* group = reinterpret_cast(nativePtr); + GroupPropertyValuesHolder* newHolder = new GroupPropertyValuesHolder(group, propertyId, + startValue, endValue); + return reinterpret_cast(newHolder); +} + +static jlong createPathDataPropertyHolder(JNIEnv*, jobject, jlong nativePtr, jlong startValuePtr, + jlong endValuePtr) { + VectorDrawable::Path* path = reinterpret_cast(nativePtr); + PathData* startData = reinterpret_cast(startValuePtr); + PathData* endData = reinterpret_cast(endValuePtr); + PathDataPropertyValuesHolder* newHolder = new PathDataPropertyValuesHolder(path, + startData, endData); + return reinterpret_cast(newHolder); +} + +static jlong createPathColorPropertyHolder(JNIEnv*, jobject, jlong nativePtr, jint propertyId, + int startValue, jint endValue) { + VectorDrawable::FullPath* fullPath = reinterpret_cast(nativePtr); + FullPathColorPropertyValuesHolder* newHolder = new FullPathColorPropertyValuesHolder(fullPath, + propertyId, startValue, endValue); + return reinterpret_cast(newHolder); +} + +static jlong createPathPropertyHolder(JNIEnv*, jobject, jlong nativePtr, jint propertyId, + float startValue, jfloat endValue) { + VectorDrawable::FullPath* fullPath = reinterpret_cast(nativePtr); + FullPathPropertyValuesHolder* newHolder = new FullPathPropertyValuesHolder(fullPath, + propertyId, startValue, endValue); + return reinterpret_cast(newHolder); +} + +static jlong createRootAlphaPropertyHolder(JNIEnv*, jobject, jlong nativePtr, jfloat startValue, + float endValue) { + VectorDrawable::Tree* tree = reinterpret_cast(nativePtr); + RootAlphaPropertyValuesHolder* newHolder = new RootAlphaPropertyValuesHolder(tree, + startValue, endValue); + return reinterpret_cast(newHolder); +} +static void setFloatPropertyHolderData(JNIEnv* env, jobject, jlong propertyHolderPtr, + jfloatArray srcData, jint length) { + jfloat* propertyData = env->GetFloatArrayElements(srcData, nullptr); + PropertyValuesHolderImpl* holder = + reinterpret_cast*>(propertyHolderPtr); + holder->setPropertyDataSource(propertyData, length); + env->ReleaseFloatArrayElements(srcData, propertyData, JNI_ABORT); +} + +static void setIntPropertyHolderData(JNIEnv* env, jobject, jlong propertyHolderPtr, + jintArray srcData, jint length) { + jint* propertyData = env->GetIntArrayElements(srcData, nullptr); + PropertyValuesHolderImpl* holder = + reinterpret_cast*>(propertyHolderPtr); + holder->setPropertyDataSource(propertyData, length); + env->ReleaseIntArrayElements(srcData, propertyData, JNI_ABORT); +} + +static void start(JNIEnv* env, jobject, jlong animatorSetPtr, jobject finishListener, jint id) { + PropertyValuesAnimatorSet* set = reinterpret_cast(animatorSetPtr); + AnimationListener* listener = createAnimationListener(env, finishListener, id); + set->start(listener); +} + +static void reverse(JNIEnv* env, jobject, jlong animatorSetPtr, jobject finishListener, jint id) { + PropertyValuesAnimatorSet* set = reinterpret_cast(animatorSetPtr); + AnimationListener* listener = createAnimationListener(env, finishListener, id); + set->reverse(listener); +} + +static void end(JNIEnv*, jobject, jlong animatorSetPtr) { + PropertyValuesAnimatorSet* set = reinterpret_cast(animatorSetPtr); + set->end(); +} + +static void reset(JNIEnv*, jobject, jlong animatorSetPtr) { + PropertyValuesAnimatorSet* set = reinterpret_cast(animatorSetPtr); + set->reset(); +} + +static const JNINativeMethod gMethods[] = { + {"nCreateAnimatorSet", "()J", (void*)createAnimatorSet}, + {"nSetVectorDrawableTarget", "(JJ)V", (void*)setVectorDrawableTarget}, + {"nAddAnimator", "(JJJJJII)V", (void*)addAnimator}, + {"nSetPropertyHolderData", "(J[FI)V", (void*)setFloatPropertyHolderData}, + {"nSetPropertyHolderData", "(J[II)V", (void*)setIntPropertyHolderData}, + {"nStart", "(JLandroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimatorRT;I)V", (void*)start}, + {"nReverse", "(JLandroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimatorRT;I)V", (void*)reverse}, + + // ------------- @FastNative ------------------- + + {"nCreateGroupPropertyHolder", "(JIFF)J", (void*)createGroupPropertyHolder}, + {"nCreatePathDataPropertyHolder", "(JJJ)J", (void*)createPathDataPropertyHolder}, + {"nCreatePathColorPropertyHolder", "(JIII)J", (void*)createPathColorPropertyHolder}, + {"nCreatePathPropertyHolder", "(JIFF)J", (void*)createPathPropertyHolder}, + {"nCreateRootAlphaPropertyHolder", "(JFF)J", (void*)createRootAlphaPropertyHolder}, + {"nEnd", "(J)V", (void*)end}, + {"nReset", "(J)V", (void*)reset}, +}; + +const char* const kClassPathName = "android/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimatorRT"; +int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env) { + gVectorDrawableAnimatorClassInfo.clazz = FindClassOrDie(env, kClassPathName); + gVectorDrawableAnimatorClassInfo.clazz = MakeGlobalRefOrDie(env, + gVectorDrawableAnimatorClassInfo.clazz); + + gVectorDrawableAnimatorClassInfo.callOnFinished = GetStaticMethodIDOrDie( + env, gVectorDrawableAnimatorClassInfo.clazz, "callOnFinished", + "(Landroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimatorRT;I)V"); + return RegisterMethodsOrDie(env, "android/graphics/drawable/AnimatedVectorDrawable", + gMethods, NELEM(gMethods)); +} + +}; // namespace android diff --git a/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp b/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp new file mode 100644 index 000000000000..58a2379a6999 --- /dev/null +++ b/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp @@ -0,0 +1,425 @@ +/* + * 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 "GraphicsJNI.h" +#include "jni.h" +#include "core_jni_helpers.h" + +#include "PathParser.h" +#include "VectorDrawable.h" + +#include + +namespace android { +using namespace uirenderer; +using namespace uirenderer::VectorDrawable; + +/** + * VectorDrawable's pre-draw construction. + */ +static jlong createTree(JNIEnv*, jobject, jlong groupPtr) { + VectorDrawable::Group* rootGroup = reinterpret_cast(groupPtr); + VectorDrawable::Tree* tree = new VectorDrawable::Tree(rootGroup); + return reinterpret_cast(tree); +} + +static jlong createTreeFromCopy(JNIEnv*, jobject, jlong treePtr, jlong groupPtr) { + VectorDrawable::Group* rootGroup = reinterpret_cast(groupPtr); + VectorDrawable::Tree* treeToCopy = reinterpret_cast(treePtr); + VectorDrawable::Tree* tree = new VectorDrawable::Tree(treeToCopy, rootGroup); + return reinterpret_cast(tree); +} + +static jlong createEmptyFullPath(JNIEnv*, jobject) { + VectorDrawable::FullPath* newPath = new VectorDrawable::FullPath(); + return reinterpret_cast(newPath); +} + +static jlong createFullPath(JNIEnv*, jobject, jlong srcFullPathPtr) { + VectorDrawable::FullPath* srcFullPath = + reinterpret_cast(srcFullPathPtr); + VectorDrawable::FullPath* newPath = new VectorDrawable::FullPath(*srcFullPath); + return reinterpret_cast(newPath); +} + +static jlong createEmptyClipPath(JNIEnv*, jobject) { + VectorDrawable::ClipPath* newPath = new VectorDrawable::ClipPath(); + return reinterpret_cast(newPath); +} + +static jlong createClipPath(JNIEnv*, jobject, jlong srcClipPathPtr) { + VectorDrawable::ClipPath* srcClipPath = + reinterpret_cast(srcClipPathPtr); + VectorDrawable::ClipPath* newPath = new VectorDrawable::ClipPath(*srcClipPath); + return reinterpret_cast(newPath); +} + +static jlong createEmptyGroup(JNIEnv*, jobject) { + VectorDrawable::Group* newGroup = new VectorDrawable::Group(); + return reinterpret_cast(newGroup); +} + +static jlong createGroup(JNIEnv*, jobject, jlong srcGroupPtr) { + VectorDrawable::Group* srcGroup = reinterpret_cast(srcGroupPtr); + VectorDrawable::Group* newGroup = new VectorDrawable::Group(*srcGroup); + return reinterpret_cast(newGroup); +} + +static void setNodeName(JNIEnv* env, jobject, jlong nodePtr, jstring nameStr) { + VectorDrawable::Node* node = reinterpret_cast(nodePtr); + const char* nodeName = env->GetStringUTFChars(nameStr, NULL); + node->setName(nodeName); + env->ReleaseStringUTFChars(nameStr, nodeName); +} + +static void addChild(JNIEnv*, jobject, jlong groupPtr, jlong childPtr) { + VectorDrawable::Group* group = reinterpret_cast(groupPtr); + VectorDrawable::Node* child = reinterpret_cast(childPtr); + group->addChild(child); +} + +static void setAllowCaching(JNIEnv*, jobject, jlong treePtr, jboolean allowCaching) { + VectorDrawable::Tree* tree = reinterpret_cast(treePtr); + tree->setAllowCaching(allowCaching); +} + +static void setAntiAlias(JNIEnv*, jobject, jlong treePtr, jboolean aa) { + VectorDrawable::Tree* tree = reinterpret_cast(treePtr); + tree->setAntiAlias(aa); +} + +/** + * Draw + */ +static int draw(JNIEnv* env, jobject, jlong treePtr, jlong canvasPtr, + jlong colorFilterPtr, jobject jrect, jboolean needsMirroring, jboolean canReuseCache) { + VectorDrawable::Tree* tree = reinterpret_cast(treePtr); + Canvas* canvas = reinterpret_cast(canvasPtr); + SkRect rect; + GraphicsJNI::jrect_to_rect(env, jrect, &rect); + SkColorFilter* colorFilter = reinterpret_cast(colorFilterPtr); + return tree->draw(canvas, colorFilter, rect, needsMirroring, canReuseCache); +} + +/** + * Setters and getters for updating staging properties that can happen both pre-draw and post draw. + */ +static void setTreeViewportSize(JNIEnv*, jobject, jlong treePtr, + jfloat viewportWidth, jfloat viewportHeight) { + VectorDrawable::Tree* tree = reinterpret_cast(treePtr); + tree->mutateStagingProperties()->setViewportSize(viewportWidth, viewportHeight); +} + +static jboolean setRootAlpha(JNIEnv*, jobject, jlong treePtr, jfloat alpha) { + VectorDrawable::Tree* tree = reinterpret_cast(treePtr); + return tree->mutateStagingProperties()->setRootAlpha(alpha); +} + +static jfloat getRootAlpha(JNIEnv*, jobject, jlong treePtr) { + VectorDrawable::Tree* tree = reinterpret_cast(treePtr); + return tree->stagingProperties().getRootAlpha(); +} + +static void updateFullPathPropertiesAndStrokeStyles(JNIEnv*, jobject, jlong fullPathPtr, + jfloat strokeWidth, jint strokeColor, jfloat strokeAlpha, jint fillColor, jfloat fillAlpha, + jfloat trimPathStart, jfloat trimPathEnd, jfloat trimPathOffset, jfloat strokeMiterLimit, + jint strokeLineCap, jint strokeLineJoin, jint fillType) { + VectorDrawable::FullPath* fullPath = reinterpret_cast(fullPathPtr); + fullPath->mutateStagingProperties()->updateProperties(strokeWidth, strokeColor, strokeAlpha, + fillColor, fillAlpha, trimPathStart, trimPathEnd, trimPathOffset, strokeMiterLimit, + strokeLineCap, strokeLineJoin, fillType); +} + +static void updateFullPathFillGradient(JNIEnv*, jobject, jlong pathPtr, jlong fillGradientPtr) { + VectorDrawable::FullPath* path = reinterpret_cast(pathPtr); + SkShader* fillShader = reinterpret_cast(fillGradientPtr); + path->mutateStagingProperties()->setFillGradient(fillShader); +} + +static void updateFullPathStrokeGradient(JNIEnv*, jobject, jlong pathPtr, jlong strokeGradientPtr) { + VectorDrawable::FullPath* path = reinterpret_cast(pathPtr); + SkShader* strokeShader = reinterpret_cast(strokeGradientPtr); + path->mutateStagingProperties()->setStrokeGradient(strokeShader); +} + +static jboolean getFullPathProperties(JNIEnv* env, jobject, jlong fullPathPtr, + jbyteArray outProperties, jint length) { + VectorDrawable::FullPath* fullPath = reinterpret_cast(fullPathPtr); + int8_t pathProperties[length]; + bool success = fullPath->stagingProperties()->copyProperties(pathProperties, length); + env->SetByteArrayRegion(outProperties, 0, length, reinterpret_cast(&pathProperties)); + return success; +} + +static jboolean getGroupProperties(JNIEnv* env, jobject, jlong groupPtr, + jfloatArray outProperties, jint length) { + VectorDrawable::Group* group = reinterpret_cast(groupPtr); + float groupProperties[length]; + bool success = group->stagingProperties()->copyProperties(groupProperties, length); + env->SetFloatArrayRegion(outProperties, 0, length, reinterpret_cast(&groupProperties)); + return success; +} + +static void updateGroupProperties(JNIEnv*, jobject, jlong groupPtr, jfloat rotate, jfloat pivotX, + jfloat pivotY, jfloat scaleX, jfloat scaleY, jfloat translateX, jfloat translateY) { + VectorDrawable::Group* group = reinterpret_cast(groupPtr); + group->mutateStagingProperties()->updateProperties(rotate, pivotX, pivotY, scaleX, scaleY, + translateX, translateY); +} + +static void setPathString(JNIEnv* env, jobject, jlong pathPtr, jstring inputStr, + jint stringLength) { + VectorDrawable::Path* path = reinterpret_cast(pathPtr); + const char* pathString = env->GetStringUTFChars(inputStr, NULL); + + PathParser::ParseResult result; + PathData data; + PathParser::getPathDataFromAsciiString(&data, &result, pathString, stringLength); + if (result.failureOccurred) { + doThrowIAE(env, result.failureMessage.c_str()); + } + path->mutateStagingProperties()->setData(data); + env->ReleaseStringUTFChars(inputStr, pathString); +} + +/** + * Setters and getters that should only be called from animation thread for animation purpose. + */ +static jfloat getRotation(JNIEnv*, jobject, jlong groupPtr) { + VectorDrawable::Group* group = reinterpret_cast(groupPtr); + return group->stagingProperties()->getRotation(); +} + +static void setRotation(JNIEnv*, jobject, jlong groupPtr, jfloat rotation) { + VectorDrawable::Group* group = reinterpret_cast(groupPtr); + group->mutateStagingProperties()->setRotation(rotation); +} + +static jfloat getPivotX(JNIEnv*, jobject, jlong groupPtr) { + VectorDrawable::Group* group = reinterpret_cast(groupPtr); + return group->stagingProperties()->getPivotX(); +} + +static void setPivotX(JNIEnv*, jobject, jlong groupPtr, jfloat pivotX) { + VectorDrawable::Group* group = reinterpret_cast(groupPtr); + group->mutateStagingProperties()->setPivotX(pivotX); +} + +static jfloat getPivotY(JNIEnv*, jobject, jlong groupPtr) { + VectorDrawable::Group* group = reinterpret_cast(groupPtr); + return group->stagingProperties()->getPivotY(); +} + +static void setPivotY(JNIEnv*, jobject, jlong groupPtr, jfloat pivotY) { + VectorDrawable::Group* group = reinterpret_cast(groupPtr); + group->mutateStagingProperties()->setPivotY(pivotY); +} + +static jfloat getScaleX(JNIEnv*, jobject, jlong groupPtr) { + VectorDrawable::Group* group = reinterpret_cast(groupPtr); + return group->stagingProperties()->getScaleX(); +} + +static void setScaleX(JNIEnv*, jobject, jlong groupPtr, jfloat scaleX) { + VectorDrawable::Group* group = reinterpret_cast(groupPtr); + group->mutateStagingProperties()->setScaleX(scaleX); +} + +static jfloat getScaleY(JNIEnv*, jobject, jlong groupPtr) { + VectorDrawable::Group* group = reinterpret_cast(groupPtr); + return group->stagingProperties()->getScaleY(); +} + +static void setScaleY(JNIEnv*, jobject, jlong groupPtr, jfloat scaleY) { + VectorDrawable::Group* group = reinterpret_cast(groupPtr); + group->mutateStagingProperties()->setScaleY(scaleY); +} + +static jfloat getTranslateX(JNIEnv*, jobject, jlong groupPtr) { + VectorDrawable::Group* group = reinterpret_cast(groupPtr); + return group->stagingProperties()->getTranslateX(); +} + +static void setTranslateX(JNIEnv*, jobject, jlong groupPtr, jfloat translateX) { + VectorDrawable::Group* group = reinterpret_cast(groupPtr); + group->mutateStagingProperties()->setTranslateX(translateX); +} + +static jfloat getTranslateY(JNIEnv*, jobject, jlong groupPtr) { + VectorDrawable::Group* group = reinterpret_cast(groupPtr); + return group->stagingProperties()->getTranslateY(); +} + +static void setTranslateY(JNIEnv*, jobject, jlong groupPtr, jfloat translateY) { + VectorDrawable::Group* group = reinterpret_cast(groupPtr); + group->mutateStagingProperties()->setTranslateY(translateY); +} + +static void setPathData(JNIEnv*, jobject, jlong pathPtr, jlong pathDataPtr) { + VectorDrawable::Path* path = reinterpret_cast(pathPtr); + PathData* pathData = reinterpret_cast(pathDataPtr); + path->mutateStagingProperties()->setData(*pathData); +} + +static jfloat getStrokeWidth(JNIEnv*, jobject, jlong fullPathPtr) { + VectorDrawable::FullPath* fullPath = reinterpret_cast(fullPathPtr); + return fullPath->stagingProperties()->getStrokeWidth(); +} + +static void setStrokeWidth(JNIEnv*, jobject, jlong fullPathPtr, jfloat strokeWidth) { + VectorDrawable::FullPath* fullPath = reinterpret_cast(fullPathPtr); + fullPath->mutateStagingProperties()->setStrokeWidth(strokeWidth); +} + +static jint getStrokeColor(JNIEnv*, jobject, jlong fullPathPtr) { + VectorDrawable::FullPath* fullPath = reinterpret_cast(fullPathPtr); + return fullPath->stagingProperties()->getStrokeColor(); +} + +static void setStrokeColor(JNIEnv*, jobject, jlong fullPathPtr, jint strokeColor) { + VectorDrawable::FullPath* fullPath = reinterpret_cast(fullPathPtr); + fullPath->mutateStagingProperties()->setStrokeColor(strokeColor); +} + +static jfloat getStrokeAlpha(JNIEnv*, jobject, jlong fullPathPtr) { + VectorDrawable::FullPath* fullPath = reinterpret_cast(fullPathPtr); + return fullPath->stagingProperties()->getStrokeAlpha(); +} + +static void setStrokeAlpha(JNIEnv*, jobject, jlong fullPathPtr, jfloat strokeAlpha) { + VectorDrawable::FullPath* fullPath = reinterpret_cast(fullPathPtr); + fullPath->mutateStagingProperties()->setStrokeAlpha(strokeAlpha); +} + +static jint getFillColor(JNIEnv*, jobject, jlong fullPathPtr) { + VectorDrawable::FullPath* fullPath = reinterpret_cast(fullPathPtr); + return fullPath->stagingProperties()->getFillColor(); +} + +static void setFillColor(JNIEnv*, jobject, jlong fullPathPtr, jint fillColor) { + VectorDrawable::FullPath* fullPath = reinterpret_cast(fullPathPtr); + fullPath->mutateStagingProperties()->setFillColor(fillColor); +} + +static jfloat getFillAlpha(JNIEnv*, jobject, jlong fullPathPtr) { + VectorDrawable::FullPath* fullPath = reinterpret_cast(fullPathPtr); + return fullPath->stagingProperties()->getFillAlpha(); +} + +static void setFillAlpha(JNIEnv*, jobject, jlong fullPathPtr, jfloat fillAlpha) { + VectorDrawable::FullPath* fullPath = reinterpret_cast(fullPathPtr); + fullPath->mutateStagingProperties()->setFillAlpha(fillAlpha); +} + +static jfloat getTrimPathStart(JNIEnv*, jobject, jlong fullPathPtr) { + VectorDrawable::FullPath* fullPath = reinterpret_cast(fullPathPtr); + return fullPath->stagingProperties()->getTrimPathStart(); +} + +static void setTrimPathStart(JNIEnv*, jobject, jlong fullPathPtr, jfloat trimPathStart) { + VectorDrawable::FullPath* fullPath = reinterpret_cast(fullPathPtr); + fullPath->mutateStagingProperties()->setTrimPathStart(trimPathStart); +} + +static jfloat getTrimPathEnd(JNIEnv*, jobject, jlong fullPathPtr) { + VectorDrawable::FullPath* fullPath = reinterpret_cast(fullPathPtr); + return fullPath->stagingProperties()->getTrimPathEnd(); +} + +static void setTrimPathEnd(JNIEnv*, jobject, jlong fullPathPtr, jfloat trimPathEnd) { + VectorDrawable::FullPath* fullPath = reinterpret_cast(fullPathPtr); + fullPath->mutateStagingProperties()->setTrimPathEnd(trimPathEnd); +} + +static jfloat getTrimPathOffset(JNIEnv*, jobject, jlong fullPathPtr) { + VectorDrawable::FullPath* fullPath = reinterpret_cast(fullPathPtr); + return fullPath->stagingProperties()->getTrimPathOffset(); +} + +static void setTrimPathOffset(JNIEnv*, jobject, jlong fullPathPtr, jfloat trimPathOffset) { + VectorDrawable::FullPath* fullPath = reinterpret_cast(fullPathPtr); + fullPath->mutateStagingProperties()->setTrimPathOffset(trimPathOffset); +} + +static const JNINativeMethod gMethods[] = { + {"nDraw", "(JJJLandroid/graphics/Rect;ZZ)I", (void*)draw}, + {"nGetFullPathProperties", "(J[BI)Z", (void*)getFullPathProperties}, + {"nGetGroupProperties", "(J[FI)Z", (void*)getGroupProperties}, + {"nSetPathString", "(JLjava/lang/String;I)V", (void*)setPathString}, + {"nSetName", "(JLjava/lang/String;)V", (void*)setNodeName}, + + // ------------- @FastNative ---------------- + + {"nCreateTree", "(J)J", (void*)createTree}, + {"nCreateTreeFromCopy", "(JJ)J", (void*)createTreeFromCopy}, + {"nSetRendererViewportSize", "(JFF)V", (void*)setTreeViewportSize}, + {"nSetRootAlpha", "(JF)Z", (void*)setRootAlpha}, + {"nGetRootAlpha", "(J)F", (void*)getRootAlpha}, + {"nSetAntiAlias", "(JZ)V", (void*)setAntiAlias}, + {"nSetAllowCaching", "(JZ)V", (void*)setAllowCaching}, + + {"nCreateFullPath", "()J", (void*)createEmptyFullPath}, + {"nCreateFullPath", "(J)J", (void*)createFullPath}, + {"nUpdateFullPathProperties", "(JFIFIFFFFFIII)V", (void*)updateFullPathPropertiesAndStrokeStyles}, + {"nUpdateFullPathFillGradient", "(JJ)V", (void*)updateFullPathFillGradient}, + {"nUpdateFullPathStrokeGradient", "(JJ)V", (void*)updateFullPathStrokeGradient}, + + {"nCreateClipPath", "()J", (void*)createEmptyClipPath}, + {"nCreateClipPath", "(J)J", (void*)createClipPath}, + {"nCreateGroup", "()J", (void*)createEmptyGroup}, + {"nCreateGroup", "(J)J", (void*)createGroup}, + {"nUpdateGroupProperties", "(JFFFFFFF)V", (void*)updateGroupProperties}, + + {"nAddChild", "(JJ)V", (void*)addChild}, + {"nGetRotation", "(J)F", (void*)getRotation}, + {"nSetRotation", "(JF)V", (void*)setRotation}, + {"nGetPivotX", "(J)F", (void*)getPivotX}, + {"nSetPivotX", "(JF)V", (void*)setPivotX}, + {"nGetPivotY", "(J)F", (void*)getPivotY}, + {"nSetPivotY", "(JF)V", (void*)setPivotY}, + {"nGetScaleX", "(J)F", (void*)getScaleX}, + {"nSetScaleX", "(JF)V", (void*)setScaleX}, + {"nGetScaleY", "(J)F", (void*)getScaleY}, + {"nSetScaleY", "(JF)V", (void*)setScaleY}, + {"nGetTranslateX", "(J)F", (void*)getTranslateX}, + {"nSetTranslateX", "(JF)V", (void*)setTranslateX}, + {"nGetTranslateY", "(J)F", (void*)getTranslateY}, + {"nSetTranslateY", "(JF)V", (void*)setTranslateY}, + + {"nSetPathData", "(JJ)V", (void*)setPathData}, + {"nGetStrokeWidth", "(J)F", (void*)getStrokeWidth}, + {"nSetStrokeWidth", "(JF)V", (void*)setStrokeWidth}, + {"nGetStrokeColor", "(J)I", (void*)getStrokeColor}, + {"nSetStrokeColor", "(JI)V", (void*)setStrokeColor}, + {"nGetStrokeAlpha", "(J)F", (void*)getStrokeAlpha}, + {"nSetStrokeAlpha", "(JF)V", (void*)setStrokeAlpha}, + {"nGetFillColor", "(J)I", (void*)getFillColor}, + {"nSetFillColor", "(JI)V", (void*)setFillColor}, + {"nGetFillAlpha", "(J)F", (void*)getFillAlpha}, + {"nSetFillAlpha", "(JF)V", (void*)setFillAlpha}, + {"nGetTrimPathStart", "(J)F", (void*)getTrimPathStart}, + {"nSetTrimPathStart", "(JF)V", (void*)setTrimPathStart}, + {"nGetTrimPathEnd", "(J)F", (void*)getTrimPathEnd}, + {"nSetTrimPathEnd", "(JF)V", (void*)setTrimPathEnd}, + {"nGetTrimPathOffset", "(J)F", (void*)getTrimPathOffset}, + {"nSetTrimPathOffset", "(JF)V", (void*)setTrimPathOffset}, +}; + +int register_android_graphics_drawable_VectorDrawable(JNIEnv* env) { + return RegisterMethodsOrDie(env, "android/graphics/drawable/VectorDrawable", gMethods, NELEM(gMethods)); +} + +}; // namespace android diff --git a/libs/hwui/jni/android_nio_utils.cpp b/libs/hwui/jni/android_nio_utils.cpp new file mode 100644 index 000000000000..1e6d49e49b72 --- /dev/null +++ b/libs/hwui/jni/android_nio_utils.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2008 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 "android_nio_utils.h" + +#include "core_jni_helpers.h" + +namespace android { + +AutoBufferPointer::AutoBufferPointer(JNIEnv* env, jobject nioBuffer, jboolean commit) + : fEnv(env), fCommit(commit) { + jlong pointer = jniGetNioBufferPointer(fEnv, nioBuffer); + if (pointer != 0L) { + // Buffer is backed by a direct buffer. + fArray = nullptr; + fElements = nullptr; + fPointer = reinterpret_cast(pointer); + } else { + // Buffer is backed by a managed array. + jint byteOffset = jniGetNioBufferBaseArrayOffset(fEnv, nioBuffer); + fArray = jniGetNioBufferBaseArray(fEnv, nioBuffer); + fElements = fEnv->GetPrimitiveArrayCritical(fArray, /* isCopy= */ nullptr); + fPointer = reinterpret_cast(reinterpret_cast(fElements) + byteOffset); + } +} + +AutoBufferPointer::~AutoBufferPointer() { + if (nullptr != fArray) { + fEnv->ReleasePrimitiveArrayCritical(fArray, fElements, fCommit ? 0 : JNI_ABORT); + } +} + +} // namespace android diff --git a/libs/hwui/jni/android_nio_utils.h b/libs/hwui/jni/android_nio_utils.h new file mode 100644 index 000000000000..4aaa0a78c276 --- /dev/null +++ b/libs/hwui/jni/android_nio_utils.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2008 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_NIO_UTILS_H_ +#define _ANDROID_NIO_UTILS_H_ + +#include + +namespace android { + +/** + * Class providing scoped access to the memory backing a java.nio.Buffer instance. + * + * Instances of this class should only be allocated on the stack as heap allocation is not + * supported. + * + * Instances of this class do not create any global references for performance reasons. + */ +class AutoBufferPointer final { +public: + /** Constructor for an AutoBufferPointer instance. + * + * @param env The current JNI env + * @param nioBuffer Instance of a java.nio.Buffer whose memory will be accessed. + * @param commit JNI_TRUE if the underlying memory will be updated and should be + * copied back to the managed heap. JNI_FALSE if the data will + * not be modified or the modifications may be discarded. + * + * The commit parameter is only applicable if the buffer is backed by a managed heap + * array and the runtime had to provide a copy of the data rather than the original data. + */ + AutoBufferPointer(JNIEnv* env, jobject nioBuffer, jboolean commit); + + /** Destructor for an AutoBufferPointer instance. + * + * Releases critical managed heap array pointer if acquired. + */ + ~AutoBufferPointer(); + + /** + * Returns a pointer to the current position of the buffer provided to the constructor. This + * pointer is only valid whilst the AutoBufferPointer instance remains in scope. + */ + void* pointer() const { return fPointer; } + +private: + JNIEnv* const fEnv; + void* fPointer; // Pointer to current buffer position when constructed. + void* fElements; // Pointer to array element 0 (null if buffer is direct, may be + // within fArray or point to a copy of the array). + jarray fArray; // Pointer to array on managed heap. + const jboolean fCommit; // Flag to commit data to source (when fElements is a copy of fArray). + + // Unsupported constructors and operators. + AutoBufferPointer() = delete; + AutoBufferPointer(AutoBufferPointer&) = delete; + AutoBufferPointer& operator=(AutoBufferPointer&) = delete; + static void* operator new(size_t); + static void* operator new[](size_t); + static void* operator new(size_t, void*); + static void* operator new[](size_t, void*); + static void operator delete(void*, size_t); + static void operator delete[](void*, size_t); +}; + +} /* namespace android */ + +#endif // _ANDROID_NIO_UTILS_H_ diff --git a/libs/hwui/jni/android_util_PathParser.cpp b/libs/hwui/jni/android_util_PathParser.cpp new file mode 100644 index 000000000000..10efb95100ac --- /dev/null +++ b/libs/hwui/jni/android_util_PathParser.cpp @@ -0,0 +1,120 @@ +/* + * 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 "jni.h" +#include "GraphicsJNI.h" + +#include +#include +#include + +#include +#include "core_jni_helpers.h" + +namespace android { + +using namespace uirenderer; + +static void parseStringForPath(JNIEnv* env, jobject, jlong skPathHandle, jstring inputPathStr, + jint strLength) { + const char* pathString = env->GetStringUTFChars(inputPathStr, NULL); + SkPath* skPath = reinterpret_cast(skPathHandle); + + PathParser::ParseResult result; + PathParser::parseAsciiStringForSkPath(skPath, &result, pathString, strLength); + env->ReleaseStringUTFChars(inputPathStr, pathString); + if (result.failureOccurred) { + doThrowIAE(env, result.failureMessage.c_str()); + } +} + +static long createEmptyPathData(JNIEnv*, jobject) { + PathData* pathData = new PathData(); + return reinterpret_cast(pathData); +} + +static long createPathData(JNIEnv*, jobject, jlong pathDataPtr) { + PathData* pathData = reinterpret_cast(pathDataPtr); + PathData* newPathData = new PathData(*pathData); + return reinterpret_cast(newPathData); +} + +static long createPathDataFromStringPath(JNIEnv* env, jobject, jstring inputStr, jint strLength) { + const char* pathString = env->GetStringUTFChars(inputStr, NULL); + PathData* pathData = new PathData(); + PathParser::ParseResult result; + PathParser::getPathDataFromAsciiString(pathData, &result, pathString, strLength); + env->ReleaseStringUTFChars(inputStr, pathString); + if (!result.failureOccurred) { + return reinterpret_cast(pathData); + } else { + delete pathData; + doThrowIAE(env, result.failureMessage.c_str()); + return NULL; + } +} + +static bool interpolatePathData(JNIEnv*, jobject, jlong outPathDataPtr, jlong fromPathDataPtr, + jlong toPathDataPtr, jfloat fraction) { + PathData* outPathData = reinterpret_cast(outPathDataPtr); + PathData* fromPathData = reinterpret_cast(fromPathDataPtr); + PathData* toPathData = reinterpret_cast(toPathDataPtr); + return VectorDrawableUtils::interpolatePathData(outPathData, *fromPathData, + *toPathData, fraction); +} + +static void deletePathData(JNIEnv*, jobject, jlong pathDataHandle) { + PathData* pathData = reinterpret_cast(pathDataHandle); + delete pathData; +} + +static bool canMorphPathData(JNIEnv*, jobject, jlong fromPathDataPtr, jlong toPathDataPtr) { + PathData* fromPathData = reinterpret_cast(fromPathDataPtr); + PathData* toPathData = reinterpret_cast(toPathDataPtr); + return VectorDrawableUtils::canMorph(*fromPathData, *toPathData); +} + +static void setPathData(JNIEnv*, jobject, jlong outPathDataPtr, jlong fromPathDataPtr) { + PathData* fromPathData = reinterpret_cast(fromPathDataPtr); + PathData* outPathData = reinterpret_cast(outPathDataPtr); + *outPathData = *fromPathData; +} + +static void setSkPathFromPathData(JNIEnv*, jobject, jlong outPathPtr, jlong pathDataPtr) { + PathData* pathData = reinterpret_cast(pathDataPtr); + SkPath* skPath = reinterpret_cast(outPathPtr); + VectorDrawableUtils::verbsToPath(skPath, *pathData); +} + +static const JNINativeMethod gMethods[] = { + {"nParseStringForPath", "(JLjava/lang/String;I)V", (void*)parseStringForPath}, + {"nCreatePathDataFromString", "(Ljava/lang/String;I)J", (void*)createPathDataFromStringPath}, + + // ---------------- @FastNative ----------------- + + {"nCreateEmptyPathData", "()J", (void*)createEmptyPathData}, + {"nCreatePathData", "(J)J", (void*)createPathData}, + {"nInterpolatePathData", "(JJJF)Z", (void*)interpolatePathData}, + {"nFinalize", "(J)V", (void*)deletePathData}, + {"nCanMorph", "(JJ)Z", (void*)canMorphPathData}, + {"nSetPathData", "(JJ)V", (void*)setPathData}, + {"nCreatePathFromPathData", "(JJ)V", (void*)setSkPathFromPathData}, +}; + +int register_android_util_PathParser(JNIEnv* env) { + return RegisterMethodsOrDie(env, "android/util/PathParser", gMethods, NELEM(gMethods)); +} +}; diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp new file mode 100644 index 000000000000..bfb9bae45f0c --- /dev/null +++ b/libs/hwui/jni/fonts/Font.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2018 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 "Minikin" + +#include +#include + +#include "SkData.h" +#include "SkFontMgr.h" +#include "SkRefCnt.h" +#include "SkTypeface.h" +#include "GraphicsJNI.h" +#include +#include +#include "Utils.h" +#include "FontUtils.h" + +#include +#include +#include +#include + +#include + +namespace android { + +struct NativeFontBuilder { + std::vector axes; +}; + +static inline NativeFontBuilder* toBuilder(jlong ptr) { + return reinterpret_cast(ptr); +} + +static void releaseFont(jlong font) { + delete reinterpret_cast(font); +} + +static void release_global_ref(const void* /*data*/, void* context) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + if (env == nullptr) { + JavaVMAttachArgs args; + args.version = JNI_VERSION_1_4; + args.name = "release_font_data"; + args.group = nullptr; + jint result = AndroidRuntime::getJavaVM()->AttachCurrentThread(&env, &args); + if (result != JNI_OK) { + ALOGE("failed to attach to thread to release global ref."); + return; + } + } + + jobject obj = reinterpret_cast(context); + env->DeleteGlobalRef(obj); +} + +// Regular JNI +static jlong Font_Builder_initBuilder(JNIEnv*, jobject) { + return reinterpret_cast(new NativeFontBuilder()); +} + +// Critical Native +static void Font_Builder_addAxis(CRITICAL_JNI_PARAMS_COMMA jlong builderPtr, jint tag, jfloat value) { + toBuilder(builderPtr)->axes.emplace_back(static_cast(tag), value); +} + +// Regular JNI +static jlong Font_Builder_build(JNIEnv* env, jobject clazz, jlong builderPtr, jobject buffer, + jstring filePath, jint weight, jboolean italic, jint ttcIndex) { + NPE_CHECK_RETURN_ZERO(env, buffer); + std::unique_ptr builder(toBuilder(builderPtr)); + const void* fontPtr = env->GetDirectBufferAddress(buffer); + if (fontPtr == nullptr) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Not a direct buffer"); + return 0; + } + jlong fontSize = env->GetDirectBufferCapacity(buffer); + if (fontSize <= 0) { + jniThrowException(env, "java/lang/IllegalArgumentException", + "buffer size must not be zero or negative"); + return 0; + } + ScopedUtfChars fontPath(env, filePath); + jobject fontRef = MakeGlobalRefOrDie(env, buffer); + sk_sp data(SkData::MakeWithProc(fontPtr, fontSize, + release_global_ref, reinterpret_cast(fontRef))); + + FatVector skiaAxes; + for (const auto& axis : builder->axes) { + skiaAxes.emplace_back(SkFontArguments::Axis{axis.axisTag, axis.value}); + } + + std::unique_ptr fontData(new SkMemoryStream(std::move(data))); + + SkFontArguments params; + params.setCollectionIndex(ttcIndex); + params.setAxes(skiaAxes.data(), skiaAxes.size()); + + sk_sp fm(SkFontMgr::RefDefault()); + sk_sp face(fm->makeFromStream(std::move(fontData), params)); + if (face == nullptr) { + jniThrowException(env, "java/lang/IllegalArgumentException", + "Failed to create internal object. maybe invalid font data."); + return 0; + } + std::shared_ptr minikinFont = + std::make_shared(std::move(face), fontPtr, fontSize, + std::string_view(fontPath.c_str(), fontPath.size()), + ttcIndex, builder->axes); + minikin::Font font = minikin::Font::Builder(minikinFont).setWeight(weight) + .setSlant(static_cast(italic)).build(); + return reinterpret_cast(new FontWrapper(std::move(font))); +} + +// Critical Native +static jlong Font_Builder_getReleaseNativeFont(CRITICAL_JNI_PARAMS) { + return reinterpret_cast(releaseFont); +} + +/////////////////////////////////////////////////////////////////////////////// + +static const JNINativeMethod gFontBuilderMethods[] = { + { "nInitBuilder", "()J", (void*) Font_Builder_initBuilder }, + { "nAddAxis", "(JIF)V", (void*) Font_Builder_addAxis }, + { "nBuild", "(JLjava/nio/ByteBuffer;Ljava/lang/String;IZI)J", (void*) Font_Builder_build }, + { "nGetReleaseNativeFont", "()J", (void*) Font_Builder_getReleaseNativeFont }, +}; + +int register_android_graphics_fonts_Font(JNIEnv* env) { + return RegisterMethodsOrDie(env, "android/graphics/fonts/Font$Builder", gFontBuilderMethods, + NELEM(gFontBuilderMethods)); +} + +} diff --git a/libs/hwui/jni/fonts/FontFamily.cpp b/libs/hwui/jni/fonts/FontFamily.cpp new file mode 100644 index 000000000000..b0d10c356a9b --- /dev/null +++ b/libs/hwui/jni/fonts/FontFamily.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2018 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 "Minikin" + +#include +#include +#include + +#include "FontUtils.h" + +#include +#include + +#include + +namespace android { + +struct NativeFamilyBuilder { + std::vector fonts; +}; + +static inline NativeFamilyBuilder* toBuilder(jlong ptr) { + return reinterpret_cast(ptr); +} + +static inline FontWrapper* toFontWrapper(jlong ptr) { + return reinterpret_cast(ptr); +} + +static void releaseFontFamily(jlong family) { + delete reinterpret_cast(family); +} + +// Regular JNI +static jlong FontFamily_Builder_initBuilder(JNIEnv*, jobject) { + return reinterpret_cast(new NativeFamilyBuilder()); +} + +// Critical Native +static void FontFamily_Builder_addFont(CRITICAL_JNI_PARAMS_COMMA jlong builderPtr, jlong fontPtr) { + toBuilder(builderPtr)->fonts.push_back(toFontWrapper(fontPtr)->font); +} + +// Regular JNI +static jlong FontFamily_Builder_build(JNIEnv* env, jobject clazz, jlong builderPtr, + jstring langTags, jint variant, jboolean isCustomFallback) { + std::unique_ptr builder(toBuilder(builderPtr)); + uint32_t localeId; + if (langTags == nullptr) { + localeId = minikin::registerLocaleList(""); + } else { + ScopedUtfChars str(env, langTags); + localeId = minikin::registerLocaleList(str.c_str()); + } + std::shared_ptr family = std::make_shared( + localeId, static_cast(variant), std::move(builder->fonts), + isCustomFallback); + if (family->getCoverage().length() == 0) { + // No coverage means minikin rejected given font for some reasons. + jniThrowException(env, "java/lang/IllegalArgumentException", + "Failed to create internal object. maybe invalid font data"); + return 0; + } + return reinterpret_cast(new FontFamilyWrapper(std::move(family))); +} + +// CriticalNative +static jlong FontFamily_Builder_GetReleaseFunc(CRITICAL_JNI_PARAMS) { + return reinterpret_cast(releaseFontFamily); +} + +/////////////////////////////////////////////////////////////////////////////// + +static const JNINativeMethod gFontFamilyBuilderMethods[] = { + { "nInitBuilder", "()J", (void*) FontFamily_Builder_initBuilder }, + { "nAddFont", "(JJ)V", (void*) FontFamily_Builder_addFont }, + { "nBuild", "(JLjava/lang/String;IZ)J", (void*) FontFamily_Builder_build }, + + { "nGetReleaseNativeFamily", "()J", (void*) FontFamily_Builder_GetReleaseFunc }, +}; + +int register_android_graphics_fonts_FontFamily(JNIEnv* env) { + return RegisterMethodsOrDie(env, "android/graphics/fonts/FontFamily$Builder", + gFontFamilyBuilderMethods, NELEM(gFontFamilyBuilderMethods)); +} + +} diff --git a/libs/hwui/jni/pdf/PdfDocument.cpp b/libs/hwui/jni/pdf/PdfDocument.cpp new file mode 100644 index 000000000000..5f67d3008f45 --- /dev/null +++ b/libs/hwui/jni/pdf/PdfDocument.cpp @@ -0,0 +1,165 @@ +/* + * 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. + */ + +#include "jni.h" +#include "GraphicsJNI.h" +#include "core_jni_helpers.h" +#include + +#include "CreateJavaOutputStreamAdaptor.h" + +#include "SkPDFDocument.h" +#include "SkPicture.h" +#include "SkPictureRecorder.h" +#include "SkRect.h" +#include "SkStream.h" + +#include + +namespace android { + +struct PageRecord { + + PageRecord(int width, int height, const SkRect& contentRect) + : mPictureRecorder(new SkPictureRecorder()) + , mPicture(NULL) + , mWidth(width) + , mHeight(height) { + mContentRect = contentRect; + } + + ~PageRecord() { + delete mPictureRecorder; + if (NULL != mPicture) { + mPicture->unref(); + } + } + + SkPictureRecorder* mPictureRecorder; + SkPicture* mPicture; + const int mWidth; + const int mHeight; + SkRect mContentRect; +}; + +class PdfDocument { +public: + PdfDocument() { + mCurrentPage = NULL; + } + + SkCanvas* startPage(int width, int height, + int contentLeft, int contentTop, int contentRight, int contentBottom) { + assert(mCurrentPage == NULL); + + SkRect contentRect = SkRect::MakeLTRB( + contentLeft, contentTop, contentRight, contentBottom); + PageRecord* page = new PageRecord(width, height, contentRect); + mPages.push_back(page); + mCurrentPage = page; + + SkCanvas* canvas = page->mPictureRecorder->beginRecording( + SkRect::MakeWH(contentRect.width(), contentRect.height())); + + return canvas; + } + + void finishPage() { + assert(mCurrentPage != NULL); + assert(mCurrentPage->mPictureRecorder != NULL); + assert(mCurrentPage->mPicture == NULL); + mCurrentPage->mPicture = mCurrentPage->mPictureRecorder->finishRecordingAsPicture().release(); + delete mCurrentPage->mPictureRecorder; + mCurrentPage->mPictureRecorder = NULL; + mCurrentPage = NULL; + } + + void write(SkWStream* stream) { + sk_sp document = SkPDF::MakeDocument(stream); + for (unsigned i = 0; i < mPages.size(); i++) { + PageRecord* page = mPages[i]; + + SkCanvas* canvas = document->beginPage(page->mWidth, page->mHeight, + &(page->mContentRect)); + canvas->drawPicture(page->mPicture); + + document->endPage(); + } + document->close(); + } + + void close() { + assert(NULL == mCurrentPage); + for (unsigned i = 0; i < mPages.size(); i++) { + delete mPages[i]; + } + } + +private: + ~PdfDocument() { + close(); + } + + std::vector mPages; + PageRecord* mCurrentPage; +}; + +static jlong nativeCreateDocument(JNIEnv* env, jobject thiz) { + return reinterpret_cast(new PdfDocument()); +} + +static jlong nativeStartPage(JNIEnv* env, jobject thiz, jlong documentPtr, + jint pageWidth, jint pageHeight, + jint contentLeft, jint contentTop, jint contentRight, jint contentBottom) { + PdfDocument* document = reinterpret_cast(documentPtr); + SkCanvas* canvas = document->startPage(pageWidth, pageHeight, + contentLeft, contentTop, contentRight, contentBottom); + return reinterpret_cast(Canvas::create_canvas(canvas)); +} + +static void nativeFinishPage(JNIEnv* env, jobject thiz, jlong documentPtr) { + PdfDocument* document = reinterpret_cast(documentPtr); + document->finishPage(); +} + +static void nativeWriteTo(JNIEnv* env, jobject thiz, jlong documentPtr, jobject out, + jbyteArray chunk) { + PdfDocument* document = reinterpret_cast(documentPtr); + SkWStream* skWStream = CreateJavaOutputStreamAdaptor(env, out, chunk); + document->write(skWStream); + delete skWStream; +} + +static void nativeClose(JNIEnv* env, jobject thiz, jlong documentPtr) { + PdfDocument* document = reinterpret_cast(documentPtr); + document->close(); +} + +static const JNINativeMethod gPdfDocument_Methods[] = { + {"nativeCreateDocument", "()J", (void*) nativeCreateDocument}, + {"nativeStartPage", "(JIIIIII)J", (void*) nativeStartPage}, + {"nativeFinishPage", "(J)V", (void*) nativeFinishPage}, + {"nativeWriteTo", "(JLjava/io/OutputStream;[B)V", (void*) nativeWriteTo}, + {"nativeClose", "(J)V", (void*) nativeClose} +}; + +int register_android_graphics_pdf_PdfDocument(JNIEnv* env) { + return RegisterMethodsOrDie( + env, "android/graphics/pdf/PdfDocument", gPdfDocument_Methods, + NELEM(gPdfDocument_Methods)); +} + +}; diff --git a/libs/hwui/jni/pdf/PdfEditor.cpp b/libs/hwui/jni/pdf/PdfEditor.cpp new file mode 100644 index 000000000000..10c30260d7e3 --- /dev/null +++ b/libs/hwui/jni/pdf/PdfEditor.cpp @@ -0,0 +1,308 @@ +/* + * 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. + */ +#define LOG_TAG "PdfEditor" + +#include +#include + +#include + +#include +#include + +#include "PdfUtils.h" + +#include "jni.h" +#include + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor" +#include "fpdfview.h" +#include "fpdf_edit.h" +#include "fpdf_save.h" +#include "fpdf_transformpage.h" +#pragma GCC diagnostic pop + +#include "SkMatrix.h" + +#include + +namespace android { + +enum PageBox {PAGE_BOX_MEDIA, PAGE_BOX_CROP}; + +static struct { + jfieldID x; + jfieldID y; +} gPointClassInfo; + +static struct { + jfieldID left; + jfieldID top; + jfieldID right; + jfieldID bottom; +} gRectClassInfo; + +static jint nativeRemovePage(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex) { + FPDF_DOCUMENT document = reinterpret_cast(documentPtr); + + FPDFPage_Delete(document, pageIndex); + return FPDF_GetPageCount(document); +} + +struct PdfToFdWriter : FPDF_FILEWRITE { + int dstFd; +}; + +static bool writeAllBytes(const int fd, const void* buffer, const size_t byteCount) { + char* writeBuffer = static_cast(const_cast(buffer)); + size_t remainingBytes = byteCount; + while (remainingBytes > 0) { + ssize_t writtenByteCount = write(fd, writeBuffer, remainingBytes); + if (writtenByteCount == -1) { + if (errno == EINTR) { + continue; + } + ALOGE("Error writing to buffer: %d", errno); + return false; + } + remainingBytes -= writtenByteCount; + writeBuffer += writtenByteCount; + } + return true; +} + +static int writeBlock(FPDF_FILEWRITE* owner, const void* buffer, unsigned long size) { + const PdfToFdWriter* writer = reinterpret_cast(owner); + const bool success = writeAllBytes(writer->dstFd, buffer, size); + if (!success) { + ALOGE("Cannot write to file descriptor. Error:%d", errno); + return 0; + } + return 1; +} + +static void nativeWrite(JNIEnv* env, jclass thiz, jlong documentPtr, jint fd) { + FPDF_DOCUMENT document = reinterpret_cast(documentPtr); + PdfToFdWriter writer; + writer.dstFd = fd; + writer.WriteBlock = &writeBlock; + const bool success = FPDF_SaveAsCopy(document, &writer, FPDF_NO_INCREMENTAL); + if (!success) { + jniThrowExceptionFmt(env, "java/io/IOException", + "cannot write to fd. Error: %d", errno); + } +} + +static void nativeSetTransformAndClip(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, + jlong transformPtr, jint clipLeft, jint clipTop, jint clipRight, jint clipBottom) { + FPDF_DOCUMENT document = reinterpret_cast(documentPtr); + + FPDF_PAGE* page = (FPDF_PAGE*) FPDF_LoadPage(document, pageIndex); + if (!page) { + jniThrowException(env, "java/lang/IllegalStateException", + "cannot open page"); + return; + } + + double width = 0; + double height = 0; + + const int result = FPDF_GetPageSizeByIndex(document, pageIndex, &width, &height); + if (!result) { + jniThrowException(env, "java/lang/IllegalStateException", + "cannot get page size"); + return; + } + + // PDF's coordinate system origin is left-bottom while in graphics it + // is the top-left. So, translate the PDF coordinates to ours. + SkMatrix reflectOnX = SkMatrix::MakeScale(1, -1); + SkMatrix moveUp = SkMatrix::MakeTrans(0, FPDF_GetPageHeight(page)); + SkMatrix coordinateChange = SkMatrix::Concat(moveUp, reflectOnX); + + // Apply the transformation what was created in our coordinates. + SkMatrix matrix = SkMatrix::Concat(*reinterpret_cast(transformPtr), + coordinateChange); + + // Translate the result back to PDF coordinates. + matrix.setConcat(coordinateChange, matrix); + + SkScalar transformValues[6]; + if (!matrix.asAffine(transformValues)) { + FPDF_ClosePage(page); + + jniThrowException(env, "java/lang/IllegalArgumentException", + "transform matrix has perspective. Only affine matrices are allowed."); + return; + } + + FS_MATRIX transform = {transformValues[SkMatrix::kAScaleX], transformValues[SkMatrix::kASkewY], + transformValues[SkMatrix::kASkewX], transformValues[SkMatrix::kAScaleY], + transformValues[SkMatrix::kATransX], + transformValues[SkMatrix::kATransY]}; + + FS_RECTF clip = {(float) clipLeft, (float) clipTop, (float) clipRight, (float) clipBottom}; + + FPDFPage_TransFormWithClip(page, &transform, &clip); + + FPDF_ClosePage(page); +} + +static void nativeGetPageSize(JNIEnv* env, jclass thiz, jlong documentPtr, + jint pageIndex, jobject outSize) { + FPDF_DOCUMENT document = reinterpret_cast(documentPtr); + + FPDF_PAGE page = FPDF_LoadPage(document, pageIndex); + if (!page) { + jniThrowException(env, "java/lang/IllegalStateException", + "cannot open page"); + return; + } + + double width = 0; + double height = 0; + + const int result = FPDF_GetPageSizeByIndex(document, pageIndex, &width, &height); + if (!result) { + jniThrowException(env, "java/lang/IllegalStateException", + "cannot get page size"); + return; + } + + env->SetIntField(outSize, gPointClassInfo.x, width); + env->SetIntField(outSize, gPointClassInfo.y, height); + + FPDF_ClosePage(page); +} + +static bool nativeGetPageBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, + PageBox pageBox, jobject outBox) { + FPDF_DOCUMENT document = reinterpret_cast(documentPtr); + + FPDF_PAGE page = FPDF_LoadPage(document, pageIndex); + if (!page) { + jniThrowException(env, "java/lang/IllegalStateException", + "cannot open page"); + return false; + } + + float left; + float top; + float right; + float bottom; + + const FPDF_BOOL success = (pageBox == PAGE_BOX_MEDIA) + ? FPDFPage_GetMediaBox(page, &left, &top, &right, &bottom) + : FPDFPage_GetCropBox(page, &left, &top, &right, &bottom); + + FPDF_ClosePage(page); + + if (!success) { + return false; + } + + env->SetIntField(outBox, gRectClassInfo.left, (int) left); + env->SetIntField(outBox, gRectClassInfo.top, (int) top); + env->SetIntField(outBox, gRectClassInfo.right, (int) right); + env->SetIntField(outBox, gRectClassInfo.bottom, (int) bottom); + + return true; +} + +static jboolean nativeGetPageMediaBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, + jobject outMediaBox) { + const bool success = nativeGetPageBox(env, thiz, documentPtr, pageIndex, PAGE_BOX_MEDIA, + outMediaBox); + return success ? JNI_TRUE : JNI_FALSE; +} + +static jboolean nativeGetPageCropBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, + jobject outMediaBox) { + const bool success = nativeGetPageBox(env, thiz, documentPtr, pageIndex, PAGE_BOX_CROP, + outMediaBox); + return success ? JNI_TRUE : JNI_FALSE; +} + +static void nativeSetPageBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, + PageBox pageBox, jobject box) { + FPDF_DOCUMENT document = reinterpret_cast(documentPtr); + + FPDF_PAGE page = FPDF_LoadPage(document, pageIndex); + if (!page) { + jniThrowException(env, "java/lang/IllegalStateException", + "cannot open page"); + return; + } + + const int left = env->GetIntField(box, gRectClassInfo.left); + const int top = env->GetIntField(box, gRectClassInfo.top); + const int right = env->GetIntField(box, gRectClassInfo.right); + const int bottom = env->GetIntField(box, gRectClassInfo.bottom); + + if (pageBox == PAGE_BOX_MEDIA) { + FPDFPage_SetMediaBox(page, left, top, right, bottom); + } else { + FPDFPage_SetCropBox(page, left, top, right, bottom); + } + + FPDF_ClosePage(page); +} + +static void nativeSetPageMediaBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, + jobject mediaBox) { + nativeSetPageBox(env, thiz, documentPtr, pageIndex, PAGE_BOX_MEDIA, mediaBox); +} + +static void nativeSetPageCropBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, + jobject mediaBox) { + nativeSetPageBox(env, thiz, documentPtr, pageIndex, PAGE_BOX_CROP, mediaBox); +} + +static const JNINativeMethod gPdfEditor_Methods[] = { + {"nativeOpen", "(IJ)J", (void*) nativeOpen}, + {"nativeClose", "(J)V", (void*) nativeClose}, + {"nativeGetPageCount", "(J)I", (void*) nativeGetPageCount}, + {"nativeRemovePage", "(JI)I", (void*) nativeRemovePage}, + {"nativeWrite", "(JI)V", (void*) nativeWrite}, + {"nativeSetTransformAndClip", "(JIJIIII)V", (void*) nativeSetTransformAndClip}, + {"nativeGetPageSize", "(JILandroid/graphics/Point;)V", (void*) nativeGetPageSize}, + {"nativeScaleForPrinting", "(J)Z", (void*) nativeScaleForPrinting}, + {"nativeGetPageMediaBox", "(JILandroid/graphics/Rect;)Z", (void*) nativeGetPageMediaBox}, + {"nativeSetPageMediaBox", "(JILandroid/graphics/Rect;)V", (void*) nativeSetPageMediaBox}, + {"nativeGetPageCropBox", "(JILandroid/graphics/Rect;)Z", (void*) nativeGetPageCropBox}, + {"nativeSetPageCropBox", "(JILandroid/graphics/Rect;)V", (void*) nativeSetPageCropBox} +}; + +int register_android_graphics_pdf_PdfEditor(JNIEnv* env) { + const int result = RegisterMethodsOrDie( + env, "android/graphics/pdf/PdfEditor", gPdfEditor_Methods, + NELEM(gPdfEditor_Methods)); + + jclass pointClass = FindClassOrDie(env, "android/graphics/Point"); + gPointClassInfo.x = GetFieldIDOrDie(env, pointClass, "x", "I"); + gPointClassInfo.y = GetFieldIDOrDie(env, pointClass, "y", "I"); + + jclass rectClass = FindClassOrDie(env, "android/graphics/Rect"); + gRectClassInfo.left = GetFieldIDOrDie(env, rectClass, "left", "I"); + gRectClassInfo.top = GetFieldIDOrDie(env, rectClass, "top", "I"); + gRectClassInfo.right = GetFieldIDOrDie(env, rectClass, "right", "I"); + gRectClassInfo.bottom = GetFieldIDOrDie(env, rectClass, "bottom", "I"); + + return result; +}; + +}; diff --git a/libs/hwui/jni/pdf/PdfRenderer.cpp b/libs/hwui/jni/pdf/PdfRenderer.cpp new file mode 100644 index 000000000000..761830b0e97c --- /dev/null +++ b/libs/hwui/jni/pdf/PdfRenderer.cpp @@ -0,0 +1,137 @@ +/* + * 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 "PdfUtils.h" + +#include "jni.h" +#include +#include "GraphicsJNI.h" +#include "SkBitmap.h" +#include "SkMatrix.h" +#include "fpdfview.h" + +#include "core_jni_helpers.h" +#include +#include +#include +#include +#include + +namespace android { + +static const int RENDER_MODE_FOR_DISPLAY = 1; +static const int RENDER_MODE_FOR_PRINT = 2; + +static struct { + jfieldID x; + jfieldID y; +} gPointClassInfo; + +static jlong nativeOpenPageAndGetSize(JNIEnv* env, jclass thiz, jlong documentPtr, + jint pageIndex, jobject outSize) { + FPDF_DOCUMENT document = reinterpret_cast(documentPtr); + + FPDF_PAGE page = FPDF_LoadPage(document, pageIndex); + if (!page) { + jniThrowException(env, "java/lang/IllegalStateException", + "cannot load page"); + return -1; + } + + double width = 0; + double height = 0; + + int result = FPDF_GetPageSizeByIndex(document, pageIndex, &width, &height); + if (!result) { + jniThrowException(env, "java/lang/IllegalStateException", + "cannot get page size"); + return -1; + } + + env->SetIntField(outSize, gPointClassInfo.x, width); + env->SetIntField(outSize, gPointClassInfo.y, height); + + return reinterpret_cast(page); +} + +static void nativeClosePage(JNIEnv* env, jclass thiz, jlong pagePtr) { + FPDF_PAGE page = reinterpret_cast(pagePtr); + FPDF_ClosePage(page); +} + +static void nativeRenderPage(JNIEnv* env, jclass thiz, jlong documentPtr, jlong pagePtr, + jlong bitmapPtr, jint clipLeft, jint clipTop, jint clipRight, jint clipBottom, + jlong transformPtr, jint renderMode) { + FPDF_PAGE page = reinterpret_cast(pagePtr); + + SkBitmap skBitmap; + bitmap::toBitmap(bitmapPtr).getSkBitmap(&skBitmap); + + const int stride = skBitmap.width() * 4; + + FPDF_BITMAP bitmap = FPDFBitmap_CreateEx(skBitmap.width(), skBitmap.height(), + FPDFBitmap_BGRA, skBitmap.getPixels(), stride); + + int renderFlags = FPDF_REVERSE_BYTE_ORDER; + if (renderMode == RENDER_MODE_FOR_DISPLAY) { + renderFlags |= FPDF_LCD_TEXT; + } else if (renderMode == RENDER_MODE_FOR_PRINT) { + renderFlags |= FPDF_PRINTING; + } + + SkMatrix matrix = *reinterpret_cast(transformPtr); + SkScalar transformValues[6]; + if (!matrix.asAffine(transformValues)) { + jniThrowException(env, "java/lang/IllegalArgumentException", + "transform matrix has perspective. Only affine matrices are allowed."); + return; + } + + FS_MATRIX transform = {transformValues[SkMatrix::kAScaleX], transformValues[SkMatrix::kASkewY], + transformValues[SkMatrix::kASkewX], transformValues[SkMatrix::kAScaleY], + transformValues[SkMatrix::kATransX], + transformValues[SkMatrix::kATransY]}; + + FS_RECTF clip = {(float) clipLeft, (float) clipTop, (float) clipRight, (float) clipBottom}; + + FPDF_RenderPageBitmapWithMatrix(bitmap, page, &transform, &clip, renderFlags); + + skBitmap.notifyPixelsChanged(); +} + +static const JNINativeMethod gPdfRenderer_Methods[] = { + {"nativeCreate", "(IJ)J", (void*) nativeOpen}, + {"nativeClose", "(J)V", (void*) nativeClose}, + {"nativeGetPageCount", "(J)I", (void*) nativeGetPageCount}, + {"nativeScaleForPrinting", "(J)Z", (void*) nativeScaleForPrinting}, + {"nativeRenderPage", "(JJJIIIIJI)V", (void*) nativeRenderPage}, + {"nativeOpenPageAndGetSize", "(JILandroid/graphics/Point;)J", (void*) nativeOpenPageAndGetSize}, + {"nativeClosePage", "(J)V", (void*) nativeClosePage} +}; + +int register_android_graphics_pdf_PdfRenderer(JNIEnv* env) { + int result = RegisterMethodsOrDie( + env, "android/graphics/pdf/PdfRenderer", gPdfRenderer_Methods, + NELEM(gPdfRenderer_Methods)); + + jclass clazz = FindClassOrDie(env, "android/graphics/Point"); + gPointClassInfo.x = GetFieldIDOrDie(env, clazz, "x", "I"); + gPointClassInfo.y = GetFieldIDOrDie(env, clazz, "y", "I"); + + return result; +}; + +}; diff --git a/libs/hwui/jni/pdf/PdfUtils.cpp b/libs/hwui/jni/pdf/PdfUtils.cpp new file mode 100644 index 000000000000..36355ebbefc4 --- /dev/null +++ b/libs/hwui/jni/pdf/PdfUtils.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2016 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 "PdfUtils.h" + +#include "jni.h" +#include + +#include "fpdfview.h" + +#define LOG_TAG "PdfUtils" +#include + +namespace android { + +static int sUnmatchedPdfiumInitRequestCount = 0; + +int getBlock(void* param, unsigned long position, unsigned char* outBuffer, + unsigned long size) { + const int fd = reinterpret_cast(param); + const int readCount = pread(fd, outBuffer, size, position); + if (readCount < 0) { + ALOGE("Cannot read from file descriptor. Error:%d", errno); + return 0; + } + return 1; +} + +// Check if the last pdfium command failed and if so, forward the error to java via an exception. If +// this function returns true an exception is pending. +bool forwardPdfiumError(JNIEnv* env) { + long error = FPDF_GetLastError(); + switch (error) { + case FPDF_ERR_SUCCESS: + return false; + case FPDF_ERR_FILE: + jniThrowException(env, "java/io/IOException", "file not found or cannot be opened"); + break; + case FPDF_ERR_FORMAT: + jniThrowException(env, "java/io/IOException", "file not in PDF format or corrupted"); + break; + case FPDF_ERR_PASSWORD: + jniThrowException(env, "java/lang/SecurityException", + "password required or incorrect password"); + break; + case FPDF_ERR_SECURITY: + jniThrowException(env, "java/lang/SecurityException", "unsupported security scheme"); + break; + case FPDF_ERR_PAGE: + jniThrowException(env, "java/io/IOException", "page not found or content error"); + break; +#ifdef PDF_ENABLE_XFA + case FPDF_ERR_XFALOAD: + jniThrowException(env, "java/lang/Exception", "load XFA error"); + break; + case FPDF_ERR_XFALAYOUT: + jniThrowException(env, "java/lang/Exception", "layout XFA error"); + break; +#endif // PDF_ENABLE_XFA + case FPDF_ERR_UNKNOWN: + default: + jniThrowExceptionFmt(env, "java/lang/Exception", "unknown error %d", error); + } + + return true; +} + +static void initializeLibraryIfNeeded(JNIEnv* env) { + if (sUnmatchedPdfiumInitRequestCount == 0) { + FPDF_InitLibrary(); + } + + sUnmatchedPdfiumInitRequestCount++; +} + +static void destroyLibraryIfNeeded(JNIEnv* env, bool handleError) { + if (sUnmatchedPdfiumInitRequestCount == 1) { + FPDF_DestroyLibrary(); + } + + sUnmatchedPdfiumInitRequestCount--; +} + +jlong nativeOpen(JNIEnv* env, jclass thiz, jint fd, jlong size) { + initializeLibraryIfNeeded(env); + + FPDF_FILEACCESS loader; + loader.m_FileLen = size; + loader.m_Param = reinterpret_cast(intptr_t(fd)); + loader.m_GetBlock = &getBlock; + + FPDF_DOCUMENT document = FPDF_LoadCustomDocument(&loader, NULL); + if (!document) { + forwardPdfiumError(env); + destroyLibraryIfNeeded(env, false); + return -1; + } + + return reinterpret_cast(document); +} + +void nativeClose(JNIEnv* env, jclass thiz, jlong documentPtr) { + FPDF_DOCUMENT document = reinterpret_cast(documentPtr); + FPDF_CloseDocument(document); + + destroyLibraryIfNeeded(env, true); +} + +jint nativeGetPageCount(JNIEnv* env, jclass thiz, jlong documentPtr) { + FPDF_DOCUMENT document = reinterpret_cast(documentPtr); + + return FPDF_GetPageCount(document); +} + +jboolean nativeScaleForPrinting(JNIEnv* env, jclass thiz, jlong documentPtr) { + FPDF_DOCUMENT document = reinterpret_cast(documentPtr); + FPDF_BOOL printScaling = FPDF_VIEWERREF_GetPrintScaling(document); + + return printScaling ? JNI_TRUE : JNI_FALSE; +} + +}; diff --git a/libs/hwui/jni/pdf/PdfUtils.h b/libs/hwui/jni/pdf/PdfUtils.h new file mode 100644 index 000000000000..65327382e899 --- /dev/null +++ b/libs/hwui/jni/pdf/PdfUtils.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2016 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 PDF_UTILS_H_ +#define PDF_UTILS_H_ + +#include "jni.h" + +namespace android { + +int getBlock(void* param, unsigned long position, unsigned char* outBuffer, + unsigned long size); + +jlong nativeOpen(JNIEnv* env, jclass thiz, jint fd, jlong size); +void nativeClose(JNIEnv* env, jclass thiz, jlong documentPtr); + +jint nativeGetPageCount(JNIEnv* env, jclass thiz, jlong documentPtr); +jboolean nativeScaleForPrinting(JNIEnv* env, jclass thiz, jlong documentPtr); + +}; + +#endif /* PDF_UTILS_H_ */ diff --git a/libs/hwui/jni/text/LineBreaker.cpp b/libs/hwui/jni/text/LineBreaker.cpp new file mode 100644 index 000000000000..8dae6558bb0d --- /dev/null +++ b/libs/hwui/jni/text/LineBreaker.cpp @@ -0,0 +1,174 @@ +/* + * 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. + */ + +#define LOG_TAG "LineBreaker" + +#include "utils/misc.h" +#include "utils/Log.h" +#include +#include +#include +#include "core_jni_helpers.h" +#include "scoped_nullable_primitive_array.h" +#include +#include +#include +#include + +#include "SkPaint.h" +#include "SkTypeface.h" +#include +#include +#include +#include +#include +#include + +namespace android { + +static inline std::vector jintArrayToFloatVector(JNIEnv* env, jintArray javaArray) { + if (javaArray == nullptr) { + return std::vector(); + } else { + ScopedIntArrayRO intArr(env, javaArray); + return std::vector(intArr.get(), intArr.get() + intArr.size()); + } +} + +static inline minikin::android::StaticLayoutNative* toNative(jlong ptr) { + return reinterpret_cast(ptr); +} + +// set text and set a number of parameters for creating a layout (width, tabstops, strategy, +// hyphenFrequency) +static jlong nInit(JNIEnv* env, jclass /* unused */, + jint breakStrategy, jint hyphenationFrequency, jboolean isJustified, jintArray indents) { + return reinterpret_cast(new minikin::android::StaticLayoutNative( + static_cast(breakStrategy), + static_cast(hyphenationFrequency), + isJustified, + jintArrayToFloatVector(env, indents))); +} + +static void nFinish(jlong nativePtr) { + delete toNative(nativePtr); +} + +// CriticalNative +static jlong nGetReleaseFunc(CRITICAL_JNI_PARAMS) { + return reinterpret_cast(nFinish); +} + +static jlong nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr, + // Inputs + jcharArray javaText, + jlong measuredTextPtr, + jint length, + jfloat firstWidth, + jint firstWidthLineCount, + jfloat restWidth, + jfloatArray variableTabStops, + jfloat defaultTabStop, + jint indentsOffset) { + minikin::android::StaticLayoutNative* builder = toNative(nativePtr); + + ScopedCharArrayRO text(env, javaText); + ScopedNullableFloatArrayRO tabStops(env, variableTabStops); + + minikin::U16StringPiece u16Text(text.get(), length); + minikin::MeasuredText* measuredText = reinterpret_cast(measuredTextPtr); + + std::unique_ptr result = + std::make_unique(builder->computeBreaks( + u16Text, *measuredText, firstWidth, firstWidthLineCount, restWidth, indentsOffset, + tabStops.get(), tabStops.size(), defaultTabStop)); + return reinterpret_cast(result.release()); +} + +static jint nGetLineCount(CRITICAL_JNI_PARAMS_COMMA jlong ptr) { + return reinterpret_cast(ptr)->breakPoints.size(); +} + +static jint nGetLineBreakOffset(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) { + return reinterpret_cast(ptr)->breakPoints[i]; +} + +static jfloat nGetLineWidth(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) { + return reinterpret_cast(ptr)->widths[i]; +} + +static jfloat nGetLineAscent(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) { + return reinterpret_cast(ptr)->ascents[i]; +} + +static jfloat nGetLineDescent(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) { + return reinterpret_cast(ptr)->descents[i]; +} + +static jint nGetLineFlag(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) { + return reinterpret_cast(ptr)->flags[i]; +} + +static void nReleaseResult(jlong ptr) { + delete reinterpret_cast(ptr); +} + +static jlong nGetReleaseResultFunc(CRITICAL_JNI_PARAMS) { + return reinterpret_cast(nReleaseResult); +} + +static const JNINativeMethod gMethods[] = { + // Fast Natives + {"nInit", "(" + "I" // breakStrategy + "I" // hyphenationFrequency + "Z" // isJustified + "[I" // indents + ")J", (void*) nInit}, + + // Critical Natives + {"nGetReleaseFunc", "()J", (void*) nGetReleaseFunc}, + + // Regular JNI + {"nComputeLineBreaks", "(" + "J" // nativePtr + "[C" // text + "J" // MeasuredParagraph ptr. + "I" // length + "F" // firstWidth + "I" // firstWidthLineCount + "F" // restWidth + "[F" // variableTabStops + "F" // defaultTabStop + "I" // indentsOffset + ")J", (void*) nComputeLineBreaks}, + + // Result accessors, CriticalNatives + {"nGetLineCount", "(J)I", (void*)nGetLineCount}, + {"nGetLineBreakOffset", "(JI)I", (void*)nGetLineBreakOffset}, + {"nGetLineWidth", "(JI)F", (void*)nGetLineWidth}, + {"nGetLineAscent", "(JI)F", (void*)nGetLineAscent}, + {"nGetLineDescent", "(JI)F", (void*)nGetLineDescent}, + {"nGetLineFlag", "(JI)I", (void*)nGetLineFlag}, + {"nGetReleaseResultFunc", "()J", (void*)nGetReleaseResultFunc}, +}; + +int register_android_graphics_text_LineBreaker(JNIEnv* env) { + return RegisterMethodsOrDie(env, "android/graphics/text/LineBreaker", gMethods, + NELEM(gMethods)); +} + +} diff --git a/libs/hwui/jni/text/MeasuredText.cpp b/libs/hwui/jni/text/MeasuredText.cpp new file mode 100644 index 000000000000..3b5ecbcd1d35 --- /dev/null +++ b/libs/hwui/jni/text/MeasuredText.cpp @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2017 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 "MeasuredText" + +#include "GraphicsJNI.h" +#include "utils/misc.h" +#include "utils/Log.h" +#include +#include +#include +#include "core_jni_helpers.h" +#include +#include +#include +#include + +#include "SkPaint.h" +#include "SkTypeface.h" +#include +#include +#include +#include +#include +#include + +namespace android { + +static inline minikin::MeasuredTextBuilder* toBuilder(jlong ptr) { + return reinterpret_cast(ptr); +} + +static inline Paint* toPaint(jlong ptr) { + return reinterpret_cast(ptr); +} + +static inline minikin::MeasuredText* toMeasuredParagraph(jlong ptr) { + return reinterpret_cast(ptr); +} + +template static inline jlong toJLong(Ptr ptr) { + return reinterpret_cast(ptr); +} + +static void releaseMeasuredParagraph(jlong measuredTextPtr) { + delete toMeasuredParagraph(measuredTextPtr); +} + +// Regular JNI +static jlong nInitBuilder(CRITICAL_JNI_PARAMS) { + return toJLong(new minikin::MeasuredTextBuilder()); +} + +// Regular JNI +static void nAddStyleRun(JNIEnv* /* unused */, jclass /* unused */, jlong builderPtr, + jlong paintPtr, jint start, jint end, jboolean isRtl) { + Paint* paint = toPaint(paintPtr); + const Typeface* typeface = Typeface::resolveDefault(paint->getAndroidTypeface()); + minikin::MinikinPaint minikinPaint = MinikinUtils::prepareMinikinPaint(paint, typeface); + toBuilder(builderPtr)->addStyleRun(start, end, std::move(minikinPaint), isRtl); +} + +// Regular JNI +static void nAddReplacementRun(JNIEnv* /* unused */, jclass /* unused */, jlong builderPtr, + jlong paintPtr, jint start, jint end, jfloat width) { + toBuilder(builderPtr)->addReplacementRun(start, end, width, + toPaint(paintPtr)->getMinikinLocaleListId()); +} + +// Regular JNI +static jlong nBuildMeasuredText(JNIEnv* env, jclass /* unused */, jlong builderPtr, + jlong hintPtr, jcharArray javaText, jboolean computeHyphenation, + jboolean computeLayout) { + ScopedCharArrayRO text(env, javaText); + const minikin::U16StringPiece textBuffer(text.get(), text.size()); + + // Pass the ownership to Java. + return toJLong(toBuilder(builderPtr)->build(textBuffer, computeHyphenation, computeLayout, + toMeasuredParagraph(hintPtr)).release()); +} + +// Regular JNI +static void nFreeBuilder(JNIEnv* env, jclass /* unused */, jlong builderPtr) { + delete toBuilder(builderPtr); +} + +// CriticalNative +static jfloat nGetWidth(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint start, jint end) { + minikin::MeasuredText* mt = toMeasuredParagraph(ptr); + float r = 0.0f; + for (int i = start; i < end; ++i) { + r += mt->widths[i]; + } + return r; +} + +static jfloat nGetCharWidthAt(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint offset) { + return toMeasuredParagraph(ptr)->widths[offset]; +} + +// Regular JNI +static void nGetBounds(JNIEnv* env, jobject, jlong ptr, jcharArray javaText, jint start, jint end, + jobject bounds) { + ScopedCharArrayRO text(env, javaText); + const minikin::U16StringPiece textBuffer(text.get(), text.size()); + const minikin::Range range(start, end); + + minikin::MinikinRect rect = toMeasuredParagraph(ptr)->getBounds(textBuffer, range); + + SkRect r; + r.fLeft = rect.mLeft; + r.fTop = rect.mTop; + r.fRight = rect.mRight; + r.fBottom = rect.mBottom; + + SkIRect ir; + r.roundOut(&ir); + GraphicsJNI::irect_to_jrect(ir, env, bounds); +} + +// CriticalNative +static jlong nGetReleaseFunc(CRITICAL_JNI_PARAMS) { + return toJLong(&releaseMeasuredParagraph); +} + +static jint nGetMemoryUsage(CRITICAL_JNI_PARAMS_COMMA jlong ptr) { + return static_cast(toMeasuredParagraph(ptr)->getMemoryUsage()); +} + +static const JNINativeMethod gMTBuilderMethods[] = { + // MeasuredParagraphBuilder native functions. + {"nInitBuilder", "()J", (void*) nInitBuilder}, + {"nAddStyleRun", "(JJIIZ)V", (void*) nAddStyleRun}, + {"nAddReplacementRun", "(JJIIF)V", (void*) nAddReplacementRun}, + {"nBuildMeasuredText", "(JJ[CZZ)J", (void*) nBuildMeasuredText}, + {"nFreeBuilder", "(J)V", (void*) nFreeBuilder}, +}; + +static const JNINativeMethod gMTMethods[] = { + // MeasuredParagraph native functions. + {"nGetWidth", "(JII)F", (void*) nGetWidth}, // Critical Natives + {"nGetBounds", "(J[CIILandroid/graphics/Rect;)V", (void*) nGetBounds}, // Regular JNI + {"nGetReleaseFunc", "()J", (void*) nGetReleaseFunc}, // Critical Natives + {"nGetMemoryUsage", "(J)I", (void*) nGetMemoryUsage}, // Critical Native + {"nGetCharWidthAt", "(JI)F", (void*) nGetCharWidthAt}, // Critical Native +}; + +int register_android_graphics_text_MeasuredText(JNIEnv* env) { + return RegisterMethodsOrDie(env, "android/graphics/text/MeasuredText", + gMTMethods, NELEM(gMTMethods)) + + RegisterMethodsOrDie(env, "android/graphics/text/MeasuredText$Builder", + gMTBuilderMethods, NELEM(gMTBuilderMethods)); +} + +} -- cgit v1.2.3 From 5368eda5127701dc84b9e4da61dfbfa685a00b2d Mon Sep 17 00:00:00 2001 From: Derek Sollenberger Date: Fri, 25 Oct 2019 11:20:03 -0400 Subject: Cleanup LOG_TAG when bundled in HWUI Bug: 137655431 Test: CtsUiRenderingTestCases Change-Id: If0f1377e0ab831f38d752c3bbe282397c061b02c --- libs/hwui/apex/android_bitmap.cpp | 1 + libs/hwui/apex/jni_runtime.cpp | 1 + libs/hwui/jni/Bitmap.cpp | 1 + libs/hwui/jni/BitmapFactory.cpp | 1 + libs/hwui/jni/BitmapRegionDecoder.cpp | 1 + libs/hwui/jni/FontFamily.cpp | 1 + libs/hwui/jni/Graphics.cpp | 1 + libs/hwui/jni/GraphicsStatsService.cpp | 1 + libs/hwui/jni/NinePatch.cpp | 1 + libs/hwui/jni/Paint.cpp | 1 + libs/hwui/jni/android_graphics_DisplayListCanvas.cpp | 2 -- libs/hwui/jni/android_graphics_HardwareRenderer.cpp | 1 + libs/hwui/jni/android_graphics_RenderNode.cpp | 1 - libs/hwui/jni/android_graphics_TextureLayer.cpp | 2 -- libs/hwui/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp | 1 - libs/hwui/jni/fonts/Font.cpp | 1 + libs/hwui/jni/fonts/FontFamily.cpp | 1 + libs/hwui/jni/pdf/PdfEditor.cpp | 2 ++ libs/hwui/jni/pdf/PdfUtils.cpp | 1 + libs/hwui/jni/text/LineBreaker.cpp | 1 + libs/hwui/jni/text/MeasuredText.cpp | 1 + 21 files changed, 18 insertions(+), 6 deletions(-) (limited to 'libs') diff --git a/libs/hwui/apex/android_bitmap.cpp b/libs/hwui/apex/android_bitmap.cpp index decd19071944..b56a619b00aa 100644 --- a/libs/hwui/apex/android_bitmap.cpp +++ b/libs/hwui/apex/android_bitmap.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#undef LOG_TAG #define LOG_TAG "Bitmap" #include diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp index 506c57802e32..c674f9363821 100644 --- a/libs/hwui/apex/jni_runtime.cpp +++ b/libs/hwui/apex/jni_runtime.cpp @@ -24,6 +24,7 @@ #include #include +#undef LOG_TAG #define LOG_TAG "AndroidGraphicsJNI" extern int register_android_graphics_Bitmap(JNIEnv*); diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp index 130322aaaa45..1f2ce8ea6c81 100755 --- a/libs/hwui/jni/Bitmap.cpp +++ b/libs/hwui/jni/Bitmap.cpp @@ -1,3 +1,4 @@ +#undef LOG_TAG #define LOG_TAG "Bitmap" #include "Bitmap.h" diff --git a/libs/hwui/jni/BitmapFactory.cpp b/libs/hwui/jni/BitmapFactory.cpp index adedffdd731c..48a5783e6464 100644 --- a/libs/hwui/jni/BitmapFactory.cpp +++ b/libs/hwui/jni/BitmapFactory.cpp @@ -1,3 +1,4 @@ +#undef LOG_TAG #define LOG_TAG "BitmapFactory" #include "BitmapFactory.h" diff --git a/libs/hwui/jni/BitmapRegionDecoder.cpp b/libs/hwui/jni/BitmapRegionDecoder.cpp index 06b4ff849097..3f80f03d7a45 100644 --- a/libs/hwui/jni/BitmapRegionDecoder.cpp +++ b/libs/hwui/jni/BitmapRegionDecoder.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#undef LOG_TAG #define LOG_TAG "BitmapRegionDecoder" #include "BitmapFactory.h" diff --git a/libs/hwui/jni/FontFamily.cpp b/libs/hwui/jni/FontFamily.cpp index 0fd9cc7e9989..f0cdb5e2b8c3 100644 --- a/libs/hwui/jni/FontFamily.cpp +++ b/libs/hwui/jni/FontFamily.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#undef LOG_TAG #define LOG_TAG "Minikin" #include diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp index 38fb8bdc1f7a..37958645f406 100644 --- a/libs/hwui/jni/Graphics.cpp +++ b/libs/hwui/jni/Graphics.cpp @@ -1,3 +1,4 @@ +#undef LOG_TAG #define LOG_TAG "GraphicsJNI" #include diff --git a/libs/hwui/jni/GraphicsStatsService.cpp b/libs/hwui/jni/GraphicsStatsService.cpp index ef0aacc4d9ec..2ce392daeb87 100644 --- a/libs/hwui/jni/GraphicsStatsService.cpp +++ b/libs/hwui/jni/GraphicsStatsService.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#undef LOG_TAG #define LOG_TAG "GraphicsStatsService" #include diff --git a/libs/hwui/jni/NinePatch.cpp b/libs/hwui/jni/NinePatch.cpp index 15f951688d43..988615524cca 100644 --- a/libs/hwui/jni/NinePatch.cpp +++ b/libs/hwui/jni/NinePatch.cpp @@ -15,6 +15,7 @@ ** limitations under the License. */ +#undef LOG_TAG #define LOG_TAG "9patch" #define LOG_NDEBUG 1 diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp index 8e1bc8489baa..7dda47b30578 100644 --- a/libs/hwui/jni/Paint.cpp +++ b/libs/hwui/jni/Paint.cpp @@ -15,6 +15,7 @@ ** limitations under the License. */ +#undef LOG_TAG #define LOG_TAG "Paint" #include diff --git a/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp index 9907da5a2e68..ca10c1e6561b 100644 --- a/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp +++ b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#define LOG_TAG "OpenGLRenderer" - #include "jni.h" #include "GraphicsJNI.h" #include diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp index 93449ffeae1b..76bfce8ab5d7 100644 --- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp +++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#undef LOG_TAG #define LOG_TAG "ThreadedRenderer" #define ATRACE_TAG ATRACE_TAG_VIEW diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp index a8246c7d84b7..a5b9e5e6a28e 100644 --- a/libs/hwui/jni/android_graphics_RenderNode.cpp +++ b/libs/hwui/jni/android_graphics_RenderNode.cpp @@ -14,7 +14,6 @@ * limitations under the License. */ -#define LOG_TAG "OpenGLRenderer" #define ATRACE_TAG ATRACE_TAG_VIEW #include "jni.h" #include "GraphicsJNI.h" diff --git a/libs/hwui/jni/android_graphics_TextureLayer.cpp b/libs/hwui/jni/android_graphics_TextureLayer.cpp index 40f618025f99..e8043d2438bd 100644 --- a/libs/hwui/jni/android_graphics_TextureLayer.cpp +++ b/libs/hwui/jni/android_graphics_TextureLayer.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#define LOG_TAG "OpenGLRenderer" - #include "jni.h" #include diff --git a/libs/hwui/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp b/libs/hwui/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp index b6b53666e26e..12465566eceb 100644 --- a/libs/hwui/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp +++ b/libs/hwui/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#define LOG_TAG "OpenGLRenderer" #include "android/log.h" diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp index bfb9bae45f0c..d53883298f5b 100644 --- a/libs/hwui/jni/fonts/Font.cpp +++ b/libs/hwui/jni/fonts/Font.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#undef LOG_TAG #define LOG_TAG "Minikin" #include diff --git a/libs/hwui/jni/fonts/FontFamily.cpp b/libs/hwui/jni/fonts/FontFamily.cpp index b0d10c356a9b..26d70a0a2c9c 100644 --- a/libs/hwui/jni/fonts/FontFamily.cpp +++ b/libs/hwui/jni/fonts/FontFamily.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#undef LOG_TAG #define LOG_TAG "Minikin" #include diff --git a/libs/hwui/jni/pdf/PdfEditor.cpp b/libs/hwui/jni/pdf/PdfEditor.cpp index 10c30260d7e3..545f4c521ae8 100644 --- a/libs/hwui/jni/pdf/PdfEditor.cpp +++ b/libs/hwui/jni/pdf/PdfEditor.cpp @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +#undef LOG_TAG #define LOG_TAG "PdfEditor" #include diff --git a/libs/hwui/jni/pdf/PdfUtils.cpp b/libs/hwui/jni/pdf/PdfUtils.cpp index 36355ebbefc4..06d202828b85 100644 --- a/libs/hwui/jni/pdf/PdfUtils.cpp +++ b/libs/hwui/jni/pdf/PdfUtils.cpp @@ -21,6 +21,7 @@ #include "fpdfview.h" +#undef LOG_TAG #define LOG_TAG "PdfUtils" #include diff --git a/libs/hwui/jni/text/LineBreaker.cpp b/libs/hwui/jni/text/LineBreaker.cpp index 8dae6558bb0d..02963d8d6072 100644 --- a/libs/hwui/jni/text/LineBreaker.cpp +++ b/libs/hwui/jni/text/LineBreaker.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#undef LOG_TAG #define LOG_TAG "LineBreaker" #include "utils/misc.h" diff --git a/libs/hwui/jni/text/MeasuredText.cpp b/libs/hwui/jni/text/MeasuredText.cpp index 3b5ecbcd1d35..e001bd44dc78 100644 --- a/libs/hwui/jni/text/MeasuredText.cpp +++ b/libs/hwui/jni/text/MeasuredText.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#undef LOG_TAG #define LOG_TAG "MeasuredText" #include "GraphicsJNI.h" -- cgit v1.2.3 From 3d334d816a639a5fca93b9ef6319849833073b2f Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Wed, 19 Feb 2020 16:38:24 -0500 Subject: Make Bitmap::createFrom() account for zero stride Bug: 143470518 Test: android.graphics.cts.ImageDecoderTest#testConserveMemoryPlusHardware Follow up to ag/10045682, which was resolving a merge conflict. The original fix (https://android-review.googlesource.com/1203783) was on the single Bitmap constructor which took a GraphicBuffer parameter. The conflict was with ag/9130111, which switched the input to an AHardwareBuffer and split this version of Bitmap::createFrom into two methods. The constructor no longer has access to the information regarding the buffer stride, so that got moved into Bitmap::createFrom. But both versions should have the fix. (In fact, it appears that the version that did *not* have the fix is the one being called in testConserveMemoryPlusHardware.) Move the rowBytes computation into a common method so that both will have the fix. Change-Id: I16f77528abdb331af556bbe5d0485fe342f2325e --- libs/hwui/hwui/Bitmap.cpp | 14 ++++++++------ libs/hwui/hwui/Bitmap.h | 6 ++++++ 2 files changed, 14 insertions(+), 6 deletions(-) (limited to 'libs') diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index 914c04645289..56d951cdb338 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -150,11 +150,7 @@ sk_sp Bitmap::createFrom(AHardwareBuffer* hardwareBuffer, sk_sp 0 ? bufferDesc.stride : bufferDesc.width; - const size_t rowBytes = info.bytesPerPixel() * bufferStride; - return sk_sp(new Bitmap(hardwareBuffer, info, rowBytes, palette)); + return createFrom(hardwareBuffer, info, bufferDesc, palette); } sk_sp Bitmap::createFrom(AHardwareBuffer* hardwareBuffer, SkColorType colorType, @@ -164,8 +160,14 @@ sk_sp Bitmap::createFrom(AHardwareBuffer* hardwareBuffer, SkColorType co AHardwareBuffer_describe(hardwareBuffer, &bufferDesc); SkImageInfo info = SkImageInfo::Make(bufferDesc.width, bufferDesc.height, colorType, alphaType, colorSpace); + return createFrom(hardwareBuffer, info, bufferDesc, palette); +} - const size_t rowBytes = info.bytesPerPixel() * bufferDesc.stride; +sk_sp Bitmap::createFrom(AHardwareBuffer* hardwareBuffer, const SkImageInfo& info, + const AHardwareBuffer_Desc& bufferDesc, BitmapPalette palette) { + // If the stride is 0 we have to use the width as an approximation (eg, compressed buffer) + const auto bufferStride = bufferDesc.stride > 0 ? bufferDesc.stride : bufferDesc.width; + const size_t rowBytes = info.bytesPerPixel() * bufferStride; return sk_sp(new Bitmap(hardwareBuffer, info, rowBytes, palette)); } #endif diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h index 3bfb7800f735..b8b59947a57b 100644 --- a/libs/hwui/hwui/Bitmap.h +++ b/libs/hwui/hwui/Bitmap.h @@ -169,6 +169,12 @@ private: #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration Bitmap(AHardwareBuffer* buffer, const SkImageInfo& info, size_t rowBytes, BitmapPalette palette); + + // Common code for the two public facing createFrom(AHardwareBuffer*, ...) + // methods. + // bufferDesc is only used to compute rowBytes. + static sk_sp createFrom(AHardwareBuffer* hardwareBuffer, const SkImageInfo& info, + const AHardwareBuffer_Desc& bufferDesc, BitmapPalette palette); #endif virtual ~Bitmap(); -- cgit v1.2.3 From c5882c4eb614179c8dad323cbbb115b62bb35f43 Mon Sep 17 00:00:00 2001 From: Derek Sollenberger Date: Fri, 25 Oct 2019 11:11:32 -0400 Subject: Remove dependencies on headers outside UI module This includes AndroidRuntime and core_jni_helper.h Bug: 137655431 Test: CtsUiRenderingTestCases Change-Id: If3d26f41eaf4981505ee47634097f3645fd563fd --- libs/hwui/Android.bp | 5 + libs/hwui/DeviceInfo.cpp | 3 - libs/hwui/apex/LayoutlibLoader.cpp | 191 +++++++++++++++++++++ libs/hwui/apex/jni_runtime.cpp | 5 + libs/hwui/jni/AnimatedImageDrawable.cpp | 1 - libs/hwui/jni/Bitmap.cpp | 3 - libs/hwui/jni/BitmapFactory.cpp | 9 +- libs/hwui/jni/BitmapRegionDecoder.cpp | 4 - libs/hwui/jni/ByteBufferStreamAdaptor.cpp | 3 +- libs/hwui/jni/Camera.cpp | 3 - libs/hwui/jni/CanvasProperty.cpp | 2 - libs/hwui/jni/ColorFilter.cpp | 2 - libs/hwui/jni/CreateJavaOutputStreamAdaptor.h | 1 - libs/hwui/jni/FontFamily.cpp | 16 +- libs/hwui/jni/FontUtils.cpp | 3 +- libs/hwui/jni/Graphics.cpp | 38 +++- libs/hwui/jni/GraphicsJNI.h | 21 ++- libs/hwui/jni/GraphicsStatsService.cpp | 6 +- libs/hwui/jni/ImageDecoder.cpp | 7 +- libs/hwui/jni/Interpolator.cpp | 3 - libs/hwui/jni/MaskFilter.cpp | 4 - libs/hwui/jni/Movie.cpp | 2 - libs/hwui/jni/NinePatch.cpp | 3 - libs/hwui/jni/Paint.cpp | 2 - libs/hwui/jni/PaintFilter.cpp | 4 - libs/hwui/jni/Path.cpp | 2 - libs/hwui/jni/PathEffect.cpp | 3 - libs/hwui/jni/PathMeasure.cpp | 2 - libs/hwui/jni/Region.cpp | 3 - libs/hwui/jni/Shader.cpp | 3 - libs/hwui/jni/Typeface.cpp | 3 - libs/hwui/jni/YuvToJpegEncoder.cpp | 4 +- libs/hwui/jni/android_graphics_Canvas.cpp | 2 - libs/hwui/jni/android_graphics_ColorSpace.cpp | 13 +- .../jni/android_graphics_DisplayListCanvas.cpp | 5 - .../hwui/jni/android_graphics_HardwareRenderer.cpp | 2 - .../android_graphics_HardwareRendererObserver.cpp | 2 +- libs/hwui/jni/android_graphics_Matrix.cpp | 3 - libs/hwui/jni/android_graphics_Picture.cpp | 5 +- libs/hwui/jni/android_graphics_RenderNode.cpp | 5 - libs/hwui/jni/android_graphics_TextureLayer.cpp | 5 +- ...raphics_animation_NativeInterpolatorFactory.cpp | 6 +- ...droid_graphics_animation_RenderNodeAnimator.cpp | 6 +- ...id_graphics_drawable_AnimatedVectorDrawable.cpp | 2 - .../android_graphics_drawable_VectorDrawable.cpp | 2 - libs/hwui/jni/android_nio_utils.cpp | 2 +- libs/hwui/jni/android_util_PathParser.cpp | 2 - libs/hwui/jni/fonts/Font.cpp | 17 +- libs/hwui/jni/fonts/FontFamily.cpp | 3 +- libs/hwui/jni/graphics_jni_helpers.h | 106 ++++++++++++ libs/hwui/jni/pdf/PdfDocument.cpp | 2 - libs/hwui/jni/pdf/PdfEditor.cpp | 5 +- libs/hwui/jni/pdf/PdfRenderer.cpp | 3 - libs/hwui/jni/scoped_nullable_primitive_array.h | 103 +++++++++++ libs/hwui/jni/text/LineBreaker.cpp | 3 +- libs/hwui/jni/text/MeasuredText.cpp | 2 - 56 files changed, 507 insertions(+), 160 deletions(-) create mode 100644 libs/hwui/apex/LayoutlibLoader.cpp create mode 100644 libs/hwui/jni/graphics_jni_helpers.h create mode 100644 libs/hwui/jni/scoped_nullable_primitive_array.h (limited to 'libs') diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 954e4daeffa7..1715a279130e 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -203,6 +203,11 @@ cc_defaults { "apex/renderthread.cpp", ], }, + host: { + srcs: [ + "apex/LayoutlibLoader.cpp", + ], + } }, } diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp index 6d4a0c6421d9..c24224cbbd67 100644 --- a/libs/hwui/DeviceInfo.cpp +++ b/libs/hwui/DeviceInfo.cpp @@ -18,9 +18,6 @@ #include #include -#include -#include - #include "Properties.h" namespace android { diff --git a/libs/hwui/apex/LayoutlibLoader.cpp b/libs/hwui/apex/LayoutlibLoader.cpp new file mode 100644 index 000000000000..4bbf1214bdcf --- /dev/null +++ b/libs/hwui/apex/LayoutlibLoader.cpp @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2020 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 "graphics_jni_helpers.h" + +#include +#include + +#include +#include +#include +#include +#include + +using namespace std; + +/* + * This is responsible for setting up the JNI environment for communication between + * the Java and native parts of layoutlib, including registering native methods. + * This is mostly achieved by copying the way it is done in the platform + * (see AndroidRuntime.cpp). + */ + +static JavaVM* javaVM; + +extern int register_android_graphics_Bitmap(JNIEnv*); +extern int register_android_graphics_BitmapFactory(JNIEnv*); +extern int register_android_graphics_ByteBufferStreamAdaptor(JNIEnv* env); +extern int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env); +extern int register_android_graphics_Graphics(JNIEnv* env); +extern int register_android_graphics_ImageDecoder(JNIEnv*); +extern int register_android_graphics_Interpolator(JNIEnv* env); +extern int register_android_graphics_MaskFilter(JNIEnv* env); +extern int register_android_graphics_NinePatch(JNIEnv*); +extern int register_android_graphics_PathEffect(JNIEnv* env); +extern int register_android_graphics_Shader(JNIEnv* env); +extern int register_android_graphics_Typeface(JNIEnv* env); + +namespace android { + +extern int register_android_graphics_Canvas(JNIEnv* env); +extern int register_android_graphics_ColorFilter(JNIEnv* env); +extern int register_android_graphics_ColorSpace(JNIEnv* env); +extern int register_android_graphics_DrawFilter(JNIEnv* env); +extern int register_android_graphics_FontFamily(JNIEnv* env); +extern int register_android_graphics_Matrix(JNIEnv* env); +extern int register_android_graphics_Paint(JNIEnv* env); +extern int register_android_graphics_Path(JNIEnv* env); +extern int register_android_graphics_PathMeasure(JNIEnv* env); +extern int register_android_graphics_Picture(JNIEnv* env); +//extern int register_android_graphics_Region(JNIEnv* env); +extern int register_android_graphics_animation_NativeInterpolatorFactory(JNIEnv* env); +extern int register_android_graphics_animation_RenderNodeAnimator(JNIEnv* env); +extern int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env); +extern int register_android_graphics_drawable_VectorDrawable(JNIEnv* env); +extern int register_android_graphics_fonts_Font(JNIEnv* env); +extern int register_android_graphics_fonts_FontFamily(JNIEnv* env); +extern int register_android_graphics_text_LineBreaker(JNIEnv* env); +extern int register_android_graphics_text_MeasuredText(JNIEnv* env); +extern int register_android_util_PathParser(JNIEnv* env); +extern int register_android_view_RenderNode(JNIEnv* env); +extern int register_android_view_DisplayListCanvas(JNIEnv* env); + +#define REG_JNI(name) { name } +struct RegJNIRec { + int (*mProc)(JNIEnv*); +}; + +// Map of all possible class names to register to their corresponding JNI registration function pointer +// The actual list of registered classes will be determined at runtime via the 'native_classes' System property +static const std::unordered_map gRegJNIMap = { + {"android.graphics.Bitmap", REG_JNI(register_android_graphics_Bitmap)}, + {"android.graphics.BitmapFactory", REG_JNI(register_android_graphics_BitmapFactory)}, + {"android.graphics.ByteBufferStreamAdaptor", + REG_JNI(register_android_graphics_ByteBufferStreamAdaptor)}, + {"android.graphics.Canvas", REG_JNI(register_android_graphics_Canvas)}, + {"android.graphics.RenderNode", REG_JNI(register_android_view_RenderNode)}, + {"android.graphics.ColorFilter", REG_JNI(register_android_graphics_ColorFilter)}, + {"android.graphics.ColorSpace", REG_JNI(register_android_graphics_ColorSpace)}, + {"android.graphics.CreateJavaOutputStreamAdaptor", + REG_JNI(register_android_graphics_CreateJavaOutputStreamAdaptor)}, + {"android.graphics.DrawFilter", REG_JNI(register_android_graphics_DrawFilter)}, + {"android.graphics.FontFamily", REG_JNI(register_android_graphics_FontFamily)}, + {"android.graphics.Graphics", REG_JNI(register_android_graphics_Graphics)}, + {"android.graphics.ImageDecoder", REG_JNI(register_android_graphics_ImageDecoder)}, + {"android.graphics.Interpolator", REG_JNI(register_android_graphics_Interpolator)}, + {"android.graphics.MaskFilter", REG_JNI(register_android_graphics_MaskFilter)}, + {"android.graphics.Matrix", REG_JNI(register_android_graphics_Matrix)}, + {"android.graphics.NinePatch", REG_JNI(register_android_graphics_NinePatch)}, + {"android.graphics.Paint", REG_JNI(register_android_graphics_Paint)}, + {"android.graphics.Path", REG_JNI(register_android_graphics_Path)}, + {"android.graphics.PathEffect", REG_JNI(register_android_graphics_PathEffect)}, + {"android.graphics.PathMeasure", REG_JNI(register_android_graphics_PathMeasure)}, + {"android.graphics.Picture", REG_JNI(register_android_graphics_Picture)}, + {"android.graphics.RecordingCanvas", REG_JNI(register_android_view_DisplayListCanvas)}, +// {"android.graphics.Region", REG_JNI(register_android_graphics_Region)}, + {"android.graphics.Shader", REG_JNI(register_android_graphics_Shader)}, + {"android.graphics.Typeface", REG_JNI(register_android_graphics_Typeface)}, + {"android.graphics.animation.NativeInterpolatorFactory", + REG_JNI(register_android_graphics_animation_NativeInterpolatorFactory)}, + {"android.graphics.animation.RenderNodeAnimator", + REG_JNI(register_android_graphics_animation_RenderNodeAnimator)}, + {"android.graphics.drawable.AnimatedVectorDrawable", + REG_JNI(register_android_graphics_drawable_AnimatedVectorDrawable)}, + {"android.graphics.drawable.VectorDrawable", + REG_JNI(register_android_graphics_drawable_VectorDrawable)}, + {"android.graphics.fonts.Font", REG_JNI(register_android_graphics_fonts_Font)}, + {"android.graphics.fonts.FontFamily", REG_JNI(register_android_graphics_fonts_FontFamily)}, + {"android.graphics.text.LineBreaker", REG_JNI(register_android_graphics_text_LineBreaker)}, + {"android.graphics.text.MeasuredText", + REG_JNI(register_android_graphics_text_MeasuredText)}, + {"android.util.PathParser", REG_JNI(register_android_util_PathParser)}, +}; + +static int register_jni_procs(const std::unordered_map& jniRegMap, + const vector& classesToRegister, JNIEnv* env) { + + for (const string& className : classesToRegister) { + if (jniRegMap.at(className).mProc(env) < 0) { + return -1; + } + } + return 0; +} + +static vector parseCsv(const string& csvString) { + vector result; + istringstream stream(csvString); + string segment; + while(getline(stream, segment, ',')) + { + result.push_back(segment); + } + return result; +} + +static vector parseCsv(JNIEnv* env, jstring csvJString) { + const char* charArray = env->GetStringUTFChars(csvJString, 0); + string csvString(charArray); + vector result = parseCsv(csvString); + env->ReleaseStringUTFChars(csvJString, charArray); + return result; +} + +} // namespace android + +using namespace android; + +void init_android_graphics() { + SkGraphics::Init(); +} + +int register_android_graphics_classes(JNIEnv *env) { + JavaVM* vm = nullptr; + env->GetJavaVM(&vm); + GraphicsJNI::setJavaVM(vm); + + // Configuration is stored as java System properties. + // Get a reference to System.getProperty + jclass system = FindClassOrDie(env, "java/lang/System"); + jmethodID getPropertyMethod = GetStaticMethodIDOrDie(env, system, "getProperty", + "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); + + // Get the names of classes that need to register their native methods + auto nativesClassesJString = + (jstring) env->CallStaticObjectMethod(system, + getPropertyMethod, env->NewStringUTF("native_classes"), + env->NewStringUTF("")); + vector classesToRegister = parseCsv(env, nativesClassesJString); + + if (register_jni_procs(gRegJNIMap, classesToRegister, env) < 0) { + return JNI_ERR; + } + + return 0; +} + +void zygote_preload_graphics() { } diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp index c674f9363821..a114e2f42157 100644 --- a/libs/hwui/apex/jni_runtime.cpp +++ b/libs/hwui/apex/jni_runtime.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -150,6 +151,10 @@ void init_android_graphics() { } int register_android_graphics_classes(JNIEnv *env) { + JavaVM* vm = nullptr; + env->GetJavaVM(&vm); + GraphicsJNI::setJavaVM(vm); + for (size_t i = 0; i < NELEM(android::gRegJNI); i++) { if (android::gRegJNI[i].mProc(env) < 0) { #ifndef NDEBUG diff --git a/libs/hwui/jni/AnimatedImageDrawable.cpp b/libs/hwui/jni/AnimatedImageDrawable.cpp index 6c2a5a3f3fcc..055075d0c42a 100644 --- a/libs/hwui/jni/AnimatedImageDrawable.cpp +++ b/libs/hwui/jni/AnimatedImageDrawable.cpp @@ -17,7 +17,6 @@ #include "GraphicsJNI.h" #include "ImageDecoder.h" #include "Utils.h" -#include "core_jni_helpers.h" #include #include diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp index 1f2ce8ea6c81..9981e89ab561 100755 --- a/libs/hwui/jni/Bitmap.cpp +++ b/libs/hwui/jni/Bitmap.cpp @@ -26,9 +26,6 @@ #include #endif -#include "core_jni_helpers.h" - -#include #include #include #include diff --git a/libs/hwui/jni/BitmapFactory.cpp b/libs/hwui/jni/BitmapFactory.cpp index 48a5783e6464..f3395b9e6877 100644 --- a/libs/hwui/jni/BitmapFactory.cpp +++ b/libs/hwui/jni/BitmapFactory.cpp @@ -13,17 +13,15 @@ #include "SkStream.h" #include "SkUtils.h" #include "Utils.h" -#include "core_jni_helpers.h" #include #include #include #include #include +#include #include -#include #include -#include #include jfieldID gOptions_justBoundsFieldID; @@ -522,7 +520,9 @@ static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteA static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fileDescriptor, jobject padding, jobject bitmapFactoryOptions, jlong inBitmapHandle, jlong colorSpaceHandle) { - +#ifndef __ANDROID__ // LayoutLib for Windows does not support F_DUPFD_CLOEXEC + return nullObjectReturn("Not supported on Windows"); +#else NPE_CHECK_RETURN_ZERO(env, fileDescriptor); int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor); @@ -569,6 +569,7 @@ static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fi return doDecode(env, std::move(stream), padding, bitmapFactoryOptions, inBitmapHandle, colorSpaceHandle); +#endif } static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, jlong native_asset, diff --git a/libs/hwui/jni/BitmapRegionDecoder.cpp b/libs/hwui/jni/BitmapRegionDecoder.cpp index 3f80f03d7a45..712351382d97 100644 --- a/libs/hwui/jni/BitmapRegionDecoder.cpp +++ b/libs/hwui/jni/BitmapRegionDecoder.cpp @@ -28,12 +28,8 @@ #include "SkData.h" #include "SkStream.h" -#include "core_jni_helpers.h" - #include -#include #include -#include #include #include diff --git a/libs/hwui/jni/ByteBufferStreamAdaptor.cpp b/libs/hwui/jni/ByteBufferStreamAdaptor.cpp index d443fd8cdf14..db5f6f6c684f 100644 --- a/libs/hwui/jni/ByteBufferStreamAdaptor.cpp +++ b/libs/hwui/jni/ByteBufferStreamAdaptor.cpp @@ -1,7 +1,6 @@ #include "ByteBufferStreamAdaptor.h" -#include "core_jni_helpers.h" +#include "GraphicsJNI.h" #include "Utils.h" -#include #include diff --git a/libs/hwui/jni/Camera.cpp b/libs/hwui/jni/Camera.cpp index da954972ab57..a5e1adf26861 100644 --- a/libs/hwui/jni/Camera.cpp +++ b/libs/hwui/jni/Camera.cpp @@ -1,6 +1,3 @@ -#include "jni.h" -#include "core_jni_helpers.h" - #include "SkCamera.h" #include "GraphicsJNI.h" diff --git a/libs/hwui/jni/CanvasProperty.cpp b/libs/hwui/jni/CanvasProperty.cpp index c841d6a5125a..684ee23b9fca 100644 --- a/libs/hwui/jni/CanvasProperty.cpp +++ b/libs/hwui/jni/CanvasProperty.cpp @@ -14,9 +14,7 @@ * limitations under the License. */ -#include "jni.h" #include "GraphicsJNI.h" -#include #include #include diff --git a/libs/hwui/jni/ColorFilter.cpp b/libs/hwui/jni/ColorFilter.cpp index 164d35f46a47..cef21f91f3c1 100644 --- a/libs/hwui/jni/ColorFilter.cpp +++ b/libs/hwui/jni/ColorFilter.cpp @@ -15,9 +15,7 @@ ** limitations under the License. */ -#include "jni.h" #include "GraphicsJNI.h" -#include "core_jni_helpers.h" #include "SkColorFilter.h" #include "SkColorMatrixFilter.h" diff --git a/libs/hwui/jni/CreateJavaOutputStreamAdaptor.h b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.h index fccd4717c4b7..849418da01a1 100644 --- a/libs/hwui/jni/CreateJavaOutputStreamAdaptor.h +++ b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.h @@ -1,7 +1,6 @@ #ifndef _ANDROID_GRAPHICS_CREATE_JAVA_OUTPUT_STREAM_ADAPTOR_H_ #define _ANDROID_GRAPHICS_CREATE_JAVA_OUTPUT_STREAM_ADAPTOR_H_ -//#include #include "jni.h" class SkMemoryStream; diff --git a/libs/hwui/jni/FontFamily.cpp b/libs/hwui/jni/FontFamily.cpp index f0cdb5e2b8c3..a2fef1e19328 100644 --- a/libs/hwui/jni/FontFamily.cpp +++ b/libs/hwui/jni/FontFamily.cpp @@ -17,9 +17,6 @@ #undef LOG_TAG #define LOG_TAG "Minikin" -#include -#include - #include "SkData.h" #include "SkFontMgr.h" #include "SkRefCnt.h" @@ -27,7 +24,6 @@ #include "GraphicsJNI.h" #include #include -#include #include "Utils.h" #include "FontUtils.h" @@ -145,15 +141,11 @@ static bool addSkTypeface(NativeFamilyBuilder* builder, sk_sp&& data, in } static void release_global_ref(const void* /*data*/, void* context) { - JNIEnv* env = AndroidRuntime::getJNIEnv(); + JNIEnv* env = GraphicsJNI::getJNIEnv(); bool needToAttach = (env == NULL); if (needToAttach) { - JavaVMAttachArgs args; - args.version = JNI_VERSION_1_4; - args.name = "release_font_data"; - args.group = NULL; - jint result = AndroidRuntime::getJavaVM()->AttachCurrentThread(&env, &args); - if (result != JNI_OK) { + env = GraphicsJNI::attachJNIEnv("release_font_data"); + if (env == nullptr) { ALOGE("failed to attach to thread to release global ref."); return; } @@ -163,7 +155,7 @@ static void release_global_ref(const void* /*data*/, void* context) { env->DeleteGlobalRef(obj); if (needToAttach) { - AndroidRuntime::getJavaVM()->DetachCurrentThread(); + GraphicsJNI::detachJNIEnv(); } } diff --git a/libs/hwui/jni/FontUtils.cpp b/libs/hwui/jni/FontUtils.cpp index 0cf61b9ade89..654c5fdf6528 100644 --- a/libs/hwui/jni/FontUtils.cpp +++ b/libs/hwui/jni/FontUtils.cpp @@ -16,8 +16,7 @@ #include "FontUtils.h" -#include -#include +#include "graphics_jni_helpers.h" namespace android { namespace { diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp index 37958645f406..f76ecb4c9c8a 100644 --- a/libs/hwui/jni/Graphics.cpp +++ b/libs/hwui/jni/Graphics.cpp @@ -1,23 +1,55 @@ #undef LOG_TAG #define LOG_TAG "GraphicsJNI" +#include #include -#include #include "jni.h" #include #include "GraphicsJNI.h" -#include "core_jni_helpers.h" #include "SkCanvas.h" #include "SkMath.h" #include "SkRegion.h" -#include #include #include using namespace android; +/*static*/ JavaVM* GraphicsJNI::mJavaVM = nullptr; + +void GraphicsJNI::setJavaVM(JavaVM* javaVM) { + mJavaVM = javaVM; +} + +/** return a pointer to the JNIEnv for this thread */ +JNIEnv* GraphicsJNI::getJNIEnv() { + assert(mJavaVM != nullptr); + JNIEnv* env; + if (mJavaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { + return nullptr; + } + return env; +} + +/** create a JNIEnv* for this thread or assert if one already exists */ +JNIEnv* GraphicsJNI::attachJNIEnv(const char* envName) { + assert(getJNIEnv() == nullptr); + JNIEnv* env = nullptr; + JavaVMAttachArgs args = { JNI_VERSION_1_4, envName, NULL }; + int result = mJavaVM->AttachCurrentThread(&env, (void*) &args); + if (result != JNI_OK) { + ALOGE("thread attach failed: %#x", result); + } + return env; +} + +/** detach the current thread from the JavaVM */ +void GraphicsJNI::detachJNIEnv() { + assert(mJavaVM != nullptr); + mJavaVM->DetachCurrentThread(); +} + void doThrowNPE(JNIEnv* env) { jniThrowNullPointerException(env, NULL); } diff --git a/libs/hwui/jni/GraphicsJNI.h b/libs/hwui/jni/GraphicsJNI.h index 1e497654f18d..4bf3ed1c6a3e 100644 --- a/libs/hwui/jni/GraphicsJNI.h +++ b/libs/hwui/jni/GraphicsJNI.h @@ -10,10 +10,11 @@ #include "SkPoint.h" #include "SkRect.h" #include "SkColorSpace.h" -#include #include #include +#include "graphics_jni_helpers.h" + class SkBitmapRegionDecoder; class SkCanvas; @@ -39,6 +40,20 @@ public: kLastEnum_LegacyBitmapConfig = kHardware_LegacyBitmapConfig }; + static void setJavaVM(JavaVM* javaVM); + + /** returns a pointer to the JavaVM provided when we initialized the module */ + static JavaVM* getJavaVM() { return mJavaVM; } + + /** return a pointer to the JNIEnv for this thread */ + static JNIEnv* getJNIEnv(); + + /** create a JNIEnv* for this thread or assert if one already exists */ + static JNIEnv* attachJNIEnv(const char* envName); + + /** detach the current thread from the JavaVM */ + static void detachJNIEnv(); + // returns true if an exception is set (and dumps it out to the Log) static bool hasException(JNIEnv*); @@ -131,6 +146,10 @@ public: * above. */ static SkColor4f convertColorLong(jlong color); + +private: + /* JNI JavaVM pointer */ + static JavaVM* mJavaVM; }; class HeapAllocator : public SkBRDAllocator { diff --git a/libs/hwui/jni/GraphicsStatsService.cpp b/libs/hwui/jni/GraphicsStatsService.cpp index 2ce392daeb87..e9259462bcac 100644 --- a/libs/hwui/jni/GraphicsStatsService.cpp +++ b/libs/hwui/jni/GraphicsStatsService.cpp @@ -18,16 +18,14 @@ #define LOG_TAG "GraphicsStatsService" #include -#include #include -#include #include #include #include #include #include #include -#include "core_jni_helpers.h" +#include "GraphicsJNI.h" namespace android { @@ -116,7 +114,7 @@ static jobject gGraphicsStatsServiceObject = nullptr; static jmethodID gGraphicsStatsService_pullGraphicsStatsMethodID; static JNIEnv* getJNIEnv() { - JavaVM* vm = AndroidRuntime::getJavaVM(); + JavaVM* vm = GraphicsJNI::getJavaVM(); JNIEnv* env = nullptr; if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { if (vm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) { diff --git a/libs/hwui/jni/ImageDecoder.cpp b/libs/hwui/jni/ImageDecoder.cpp index e17e057d75c7..b6b378539bd0 100644 --- a/libs/hwui/jni/ImageDecoder.cpp +++ b/libs/hwui/jni/ImageDecoder.cpp @@ -22,7 +22,6 @@ #include "ImageDecoder.h" #include "NinePatchPeeker.h" #include "Utils.h" -#include "core_jni_helpers.h" #include #include @@ -34,7 +33,7 @@ #include #include -#include +#include #include using namespace android; @@ -154,6 +153,9 @@ static jobject native_create(JNIEnv* env, std::unique_ptr stream, static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/, jobject fileDescriptor, jboolean preferAnimation, jobject source) { +#ifndef __ANDROID__ // LayoutLib for Windows does not support F_DUPFD_CLOEXEC + return throw_exception(env, kSourceException, "Only supported on Android", nullptr, source); +#else int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor); struct stat fdStat; @@ -172,6 +174,7 @@ static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/, std::unique_ptr fileStream(new SkFILEStream(file)); return native_create(env, std::move(fileStream), source, preferAnimation); +#endif } static jobject ImageDecoder_nCreateInputStream(JNIEnv* env, jobject /*clazz*/, diff --git a/libs/hwui/jni/Interpolator.cpp b/libs/hwui/jni/Interpolator.cpp index fa28359281db..146d634a297c 100644 --- a/libs/hwui/jni/Interpolator.cpp +++ b/libs/hwui/jni/Interpolator.cpp @@ -1,8 +1,5 @@ #include "GraphicsJNI.h" #include "SkInterpolator.h" -#include "core_jni_helpers.h" - -#include static jlong Interpolator_constructor(JNIEnv* env, jobject clazz, jint valueCount, jint frameCount) { diff --git a/libs/hwui/jni/MaskFilter.cpp b/libs/hwui/jni/MaskFilter.cpp index 33d346f5d379..5383032e0f77 100644 --- a/libs/hwui/jni/MaskFilter.cpp +++ b/libs/hwui/jni/MaskFilter.cpp @@ -4,10 +4,6 @@ #include "SkBlurMaskFilter.h" #include "SkTableMaskFilter.h" -#include "core_jni_helpers.h" - -#include - static void ThrowIAE_IfNull(JNIEnv* env, void* ptr) { if (NULL == ptr) { doThrowIAE(env); diff --git a/libs/hwui/jni/Movie.cpp b/libs/hwui/jni/Movie.cpp index 4c10a85c8257..ede0ca8cda5b 100644 --- a/libs/hwui/jni/Movie.cpp +++ b/libs/hwui/jni/Movie.cpp @@ -6,13 +6,11 @@ #include "SkStream.h" #include "SkUtils.h" #include "Utils.h" -#include "core_jni_helpers.h" #include #include #include #include -#include #include static jclass gMovie_class; diff --git a/libs/hwui/jni/NinePatch.cpp b/libs/hwui/jni/NinePatch.cpp index 988615524cca..6942017d5f27 100644 --- a/libs/hwui/jni/NinePatch.cpp +++ b/libs/hwui/jni/NinePatch.cpp @@ -31,9 +31,6 @@ #include "NinePatchPeeker.h" #include "NinePatchUtils.h" -#include -#include "core_jni_helpers.h" - jclass gInsetStruct_class; jmethodID gInsetStruct_constructorMethodID; diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp index 7dda47b30578..df8635a8fe5a 100644 --- a/libs/hwui/jni/Paint.cpp +++ b/libs/hwui/jni/Paint.cpp @@ -20,9 +20,7 @@ #include -#include "jni.h" #include "GraphicsJNI.h" -#include "core_jni_helpers.h" #include #include #include diff --git a/libs/hwui/jni/PaintFilter.cpp b/libs/hwui/jni/PaintFilter.cpp index 4fe9140572d3..ec115b4e141c 100644 --- a/libs/hwui/jni/PaintFilter.cpp +++ b/libs/hwui/jni/PaintFilter.cpp @@ -15,11 +15,7 @@ ** limitations under the License. */ -#include "jni.h" #include "GraphicsJNI.h" -#include - -#include "core_jni_helpers.h" #include "hwui/Paint.h" #include "hwui/PaintFilter.h" diff --git a/libs/hwui/jni/Path.cpp b/libs/hwui/jni/Path.cpp index 481445258e3c..d67bcf221681 100644 --- a/libs/hwui/jni/Path.cpp +++ b/libs/hwui/jni/Path.cpp @@ -20,9 +20,7 @@ // To change this file, either edit the include, or device/tools/gluemaker/main.cpp, // or one of the auxilary file specifications in device/tools/gluemaker. -#include "jni.h" #include "GraphicsJNI.h" -#include "core_jni_helpers.h" #include "SkPath.h" #include "SkPathOps.h" diff --git a/libs/hwui/jni/PathEffect.cpp b/libs/hwui/jni/PathEffect.cpp index a4992de72ff6..f99bef7b7d58 100644 --- a/libs/hwui/jni/PathEffect.cpp +++ b/libs/hwui/jni/PathEffect.cpp @@ -4,9 +4,6 @@ #include "SkDashPathEffect.h" #include "SkDiscretePathEffect.h" #include "SkPathEffect.h" -#include "core_jni_helpers.h" - -#include class SkPathEffectGlue { public: diff --git a/libs/hwui/jni/PathMeasure.cpp b/libs/hwui/jni/PathMeasure.cpp index 70e528d4be6f..acf893e9544c 100644 --- a/libs/hwui/jni/PathMeasure.cpp +++ b/libs/hwui/jni/PathMeasure.cpp @@ -15,9 +15,7 @@ ** limitations under the License. */ -#include "jni.h" #include "GraphicsJNI.h" -#include #include "SkPathMeasure.h" diff --git a/libs/hwui/jni/Region.cpp b/libs/hwui/jni/Region.cpp index 87662f713449..c95bcea57ef5 100644 --- a/libs/hwui/jni/Region.cpp +++ b/libs/hwui/jni/Region.cpp @@ -24,9 +24,6 @@ #include "android_os_Parcel.h" #include "android_util_Binder.h" -#include -#include - namespace android { static jfieldID gRegion_nativeInstanceFieldID; diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp index f5e2a5244416..0f6837640524 100644 --- a/libs/hwui/jni/Shader.cpp +++ b/libs/hwui/jni/Shader.cpp @@ -4,11 +4,8 @@ #include "SkImagePriv.h" #include "SkShader.h" #include "SkBlendMode.h" -#include "core_jni_helpers.h" #include "include/effects/SkRuntimeEffect.h" -#include - #include using namespace android::uirenderer; diff --git a/libs/hwui/jni/Typeface.cpp b/libs/hwui/jni/Typeface.cpp index 4ce56ba7444f..2a5f402a4fa6 100644 --- a/libs/hwui/jni/Typeface.cpp +++ b/libs/hwui/jni/Typeface.cpp @@ -14,9 +14,6 @@ * limitations under the License. */ -#include "jni.h" -#include "core_jni_helpers.h" - #include "FontUtils.h" #include "GraphicsJNI.h" #include diff --git a/libs/hwui/jni/YuvToJpegEncoder.cpp b/libs/hwui/jni/YuvToJpegEncoder.cpp index 09adc824e520..689cf0bea741 100644 --- a/libs/hwui/jni/YuvToJpegEncoder.cpp +++ b/libs/hwui/jni/YuvToJpegEncoder.cpp @@ -4,9 +4,7 @@ #include #include -#include "core_jni_helpers.h" - -#include +#include "graphics_jni_helpers.h" YuvToJpegEncoder* YuvToJpegEncoder::create(int format, int* strides) { // Only ImageFormat.NV21 and ImageFormat.YUY2 are supported diff --git a/libs/hwui/jni/android_graphics_Canvas.cpp b/libs/hwui/jni/android_graphics_Canvas.cpp index 0ad3339ee05f..4aff3e544efa 100644 --- a/libs/hwui/jni/android_graphics_Canvas.cpp +++ b/libs/hwui/jni/android_graphics_Canvas.cpp @@ -14,9 +14,7 @@ * limitations under the License. */ -#include "jni.h" #include "GraphicsJNI.h" -#include "core_jni_helpers.h" #ifdef __ANDROID_ #include diff --git a/libs/hwui/jni/android_graphics_ColorSpace.cpp b/libs/hwui/jni/android_graphics_ColorSpace.cpp index 7648fd021d18..232fd71a12b4 100644 --- a/libs/hwui/jni/android_graphics_ColorSpace.cpp +++ b/libs/hwui/jni/android_graphics_ColorSpace.cpp @@ -14,12 +14,11 @@ * limitations under the License. */ -#include "jni.h" #include "GraphicsJNI.h" -#include "core_jni_helpers.h" #include "SkColor.h" #include "SkColorSpace.h" +#include "SkHalf.h" using namespace android; @@ -42,9 +41,13 @@ static skcms_Matrix3x3 getNativeXYZMatrix(JNIEnv* env, jfloatArray xyzD50) { /////////////////////////////////////////////////////////////////////////////// static float halfToFloat(uint16_t bits) { - __fp16 h; - memcpy(&h, &bits, 2); - return (float)h; +#ifdef __ANDROID__ // __fp16 is not defined on non-Android builds + __fp16 h; + memcpy(&h, &bits, 2); + return (float)h; +#else + return SkHalfToFloat(bits); +#endif } SkColor4f GraphicsJNI::convertColorLong(jlong color) { diff --git a/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp index ca10c1e6561b..54822f1f07e2 100644 --- a/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp +++ b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp @@ -14,11 +14,8 @@ * limitations under the License. */ -#include "jni.h" #include "GraphicsJNI.h" -#include -#include #ifdef __ANDROID__ // Layoutlib does not support Looper and device properties #include #endif @@ -36,8 +33,6 @@ #include #endif -#include "core_jni_helpers.h" - namespace android { using namespace uirenderer; diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp index 76bfce8ab5d7..49c7fcd468e1 100644 --- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp +++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp @@ -44,8 +44,6 @@ #include #include "android_graphics_HardwareRendererObserver.h" -#include "core_jni_helpers.h" -#include "jni.h" namespace android { diff --git a/libs/hwui/jni/android_graphics_HardwareRendererObserver.cpp b/libs/hwui/jni/android_graphics_HardwareRendererObserver.cpp index 89b77b0b069a..5b3e65648981 100644 --- a/libs/hwui/jni/android_graphics_HardwareRendererObserver.cpp +++ b/libs/hwui/jni/android_graphics_HardwareRendererObserver.cpp @@ -16,7 +16,7 @@ #include "android_graphics_HardwareRendererObserver.h" -#include "core_jni_helpers.h" +#include "graphics_jni_helpers.h" #include "nativehelper/jni_macros.h" #include diff --git a/libs/hwui/jni/android_graphics_Matrix.cpp b/libs/hwui/jni/android_graphics_Matrix.cpp index 13369763e0cf..7338ef24cb58 100644 --- a/libs/hwui/jni/android_graphics_Matrix.cpp +++ b/libs/hwui/jni/android_graphics_Matrix.cpp @@ -18,9 +18,6 @@ #include "GraphicsJNI.h" #include "Matrix.h" #include "SkMatrix.h" -#include "core_jni_helpers.h" - -#include namespace android { diff --git a/libs/hwui/jni/android_graphics_Picture.cpp b/libs/hwui/jni/android_graphics_Picture.cpp index 1d085e5ccc49..403efb2ab9c9 100644 --- a/libs/hwui/jni/android_graphics_Picture.cpp +++ b/libs/hwui/jni/android_graphics_Picture.cpp @@ -19,12 +19,9 @@ #include "Picture.h" #include "SkCanvas.h" #include "SkStream.h" -#include "core_jni_helpers.h" -#include "nativehelper/jni_macros.h" - -#include #include +#include "nativehelper/jni_macros.h" namespace android { diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp index a5b9e5e6a28e..85c802b40459 100644 --- a/libs/hwui/jni/android_graphics_RenderNode.cpp +++ b/libs/hwui/jni/android_graphics_RenderNode.cpp @@ -15,10 +15,7 @@ */ #define ATRACE_TAG ATRACE_TAG_VIEW -#include "jni.h" #include "GraphicsJNI.h" -#include -#include #include #include @@ -31,8 +28,6 @@ #include #include -#include "core_jni_helpers.h" - namespace android { using namespace uirenderer; diff --git a/libs/hwui/jni/android_graphics_TextureLayer.cpp b/libs/hwui/jni/android_graphics_TextureLayer.cpp index e8043d2438bd..bd20269d3751 100644 --- a/libs/hwui/jni/android_graphics_TextureLayer.cpp +++ b/libs/hwui/jni/android_graphics_TextureLayer.cpp @@ -14,11 +14,8 @@ * limitations under the License. */ -#include "jni.h" -#include - #include -#include "core_jni_helpers.h" +#include "graphics_jni_helpers.h" #include #include diff --git a/libs/hwui/jni/android_graphics_animation_NativeInterpolatorFactory.cpp b/libs/hwui/jni/android_graphics_animation_NativeInterpolatorFactory.cpp index 2073ac2d24be..764eff9a04be 100644 --- a/libs/hwui/jni/android_graphics_animation_NativeInterpolatorFactory.cpp +++ b/libs/hwui/jni/android_graphics_animation_NativeInterpolatorFactory.cpp @@ -16,12 +16,10 @@ #define LOG_TAG "OpenGLRenderer" -#include "jni.h" -#include +#include #include -#include "core_jni_helpers.h" -#include +#include "graphics_jni_helpers.h" namespace android { diff --git a/libs/hwui/jni/android_graphics_animation_RenderNodeAnimator.cpp b/libs/hwui/jni/android_graphics_animation_RenderNodeAnimator.cpp index 878d4fc13f6d..c6d26f853c1d 100644 --- a/libs/hwui/jni/android_graphics_animation_RenderNodeAnimator.cpp +++ b/libs/hwui/jni/android_graphics_animation_RenderNodeAnimator.cpp @@ -16,15 +16,11 @@ #define LOG_TAG "OpenGLRenderer" -#include "jni.h" -#include -#include - #include #include #include -#include "core_jni_helpers.h" +#include "graphics_jni_helpers.h" namespace android { diff --git a/libs/hwui/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp b/libs/hwui/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp index 12465566eceb..b3121e7b0373 100644 --- a/libs/hwui/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp +++ b/libs/hwui/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp @@ -16,9 +16,7 @@ #include "android/log.h" -#include "jni.h" #include "GraphicsJNI.h" -#include "core_jni_helpers.h" #include "Animator.h" #include "Interpolator.h" diff --git a/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp b/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp index 58a2379a6999..8a262969614e 100644 --- a/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp +++ b/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp @@ -15,8 +15,6 @@ */ #include "GraphicsJNI.h" -#include "jni.h" -#include "core_jni_helpers.h" #include "PathParser.h" #include "VectorDrawable.h" diff --git a/libs/hwui/jni/android_nio_utils.cpp b/libs/hwui/jni/android_nio_utils.cpp index 1e6d49e49b72..c2b09c1d15d7 100644 --- a/libs/hwui/jni/android_nio_utils.cpp +++ b/libs/hwui/jni/android_nio_utils.cpp @@ -16,7 +16,7 @@ #include "android_nio_utils.h" -#include "core_jni_helpers.h" +#include namespace android { diff --git a/libs/hwui/jni/android_util_PathParser.cpp b/libs/hwui/jni/android_util_PathParser.cpp index 10efb95100ac..df5e9cd44ed0 100644 --- a/libs/hwui/jni/android_util_PathParser.cpp +++ b/libs/hwui/jni/android_util_PathParser.cpp @@ -14,7 +14,6 @@ * limitations under the License. */ -#include "jni.h" #include "GraphicsJNI.h" #include @@ -22,7 +21,6 @@ #include #include -#include "core_jni_helpers.h" namespace android { diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp index d53883298f5b..5714cd1d0390 100644 --- a/libs/hwui/jni/fonts/Font.cpp +++ b/libs/hwui/jni/fonts/Font.cpp @@ -17,16 +17,12 @@ #undef LOG_TAG #define LOG_TAG "Minikin" -#include -#include - #include "SkData.h" #include "SkFontMgr.h" #include "SkRefCnt.h" #include "SkTypeface.h" #include "GraphicsJNI.h" #include -#include #include "Utils.h" #include "FontUtils.h" @@ -52,14 +48,11 @@ static void releaseFont(jlong font) { } static void release_global_ref(const void* /*data*/, void* context) { - JNIEnv* env = AndroidRuntime::getJNIEnv(); - if (env == nullptr) { - JavaVMAttachArgs args; - args.version = JNI_VERSION_1_4; - args.name = "release_font_data"; - args.group = nullptr; - jint result = AndroidRuntime::getJavaVM()->AttachCurrentThread(&env, &args); - if (result != JNI_OK) { + JNIEnv* env = GraphicsJNI::getJNIEnv(); + bool needToAttach = (env == nullptr); + if (needToAttach) { + env = GraphicsJNI::attachJNIEnv("release_font_data"); + if (env == nullptr) { ALOGE("failed to attach to thread to release global ref."); return; } diff --git a/libs/hwui/jni/fonts/FontFamily.cpp b/libs/hwui/jni/fonts/FontFamily.cpp index 26d70a0a2c9c..df619d9f1406 100644 --- a/libs/hwui/jni/fonts/FontFamily.cpp +++ b/libs/hwui/jni/fonts/FontFamily.cpp @@ -17,9 +17,8 @@ #undef LOG_TAG #define LOG_TAG "Minikin" -#include +#include "graphics_jni_helpers.h" #include -#include #include "FontUtils.h" diff --git a/libs/hwui/jni/graphics_jni_helpers.h b/libs/hwui/jni/graphics_jni_helpers.h new file mode 100644 index 000000000000..b97cc6a10179 --- /dev/null +++ b/libs/hwui/jni/graphics_jni_helpers.h @@ -0,0 +1,106 @@ +/* + * 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 GRAPHICS_JNI_HELPERS +#define GRAPHICS_JNI_HELPERS + +#include +#include +#include +#include +#include + +// Host targets (layoutlib) do not differentiate between regular and critical native methods, +// and they need all the JNI methods to have JNIEnv* and jclass/jobject as their first two arguments. +// The following macro allows to have those arguments when compiling for host while omitting them when +// compiling for Android. +#ifdef __ANDROID__ +#define CRITICAL_JNI_PARAMS +#define CRITICAL_JNI_PARAMS_COMMA +#else +#define CRITICAL_JNI_PARAMS JNIEnv*, jclass +#define CRITICAL_JNI_PARAMS_COMMA JNIEnv*, jclass, +#endif + +namespace android { + +// Defines some helpful functions. + +static inline jclass FindClassOrDie(JNIEnv* env, const char* class_name) { + jclass clazz = env->FindClass(class_name); + LOG_ALWAYS_FATAL_IF(clazz == NULL, "Unable to find class %s", class_name); + return clazz; +} + +static inline jfieldID GetFieldIDOrDie(JNIEnv* env, jclass clazz, const char* field_name, + const char* field_signature) { + jfieldID res = env->GetFieldID(clazz, field_name, field_signature); + LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static field %s", field_name); + return res; +} + +static inline jmethodID GetMethodIDOrDie(JNIEnv* env, jclass clazz, const char* method_name, + const char* method_signature) { + jmethodID res = env->GetMethodID(clazz, method_name, method_signature); + LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find method %s", method_name); + return res; +} + +static inline jfieldID GetStaticFieldIDOrDie(JNIEnv* env, jclass clazz, const char* field_name, + const char* field_signature) { + jfieldID res = env->GetStaticFieldID(clazz, field_name, field_signature); + LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static field %s", field_name); + return res; +} + +static inline jmethodID GetStaticMethodIDOrDie(JNIEnv* env, jclass clazz, const char* method_name, + const char* method_signature) { + jmethodID res = env->GetStaticMethodID(clazz, method_name, method_signature); + LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static method %s", method_name); + return res; +} + +template +static inline T MakeGlobalRefOrDie(JNIEnv* env, T in) { + jobject res = env->NewGlobalRef(in); + LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to create global reference."); + return static_cast(res); +} + +static inline int RegisterMethodsOrDie(JNIEnv* env, const char* className, + const JNINativeMethod* gMethods, int numMethods) { + int res = jniRegisterNativeMethods(env, className, gMethods, numMethods); + LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods."); + return res; +} + +/** + * Read the specified field from jobject, and convert to std::string. + * If the field cannot be obtained, return defaultValue. + */ +static inline std::string getStringField(JNIEnv* env, jobject obj, jfieldID fieldId, + const char* defaultValue) { + ScopedLocalRef strObj(env, jstring(env->GetObjectField(obj, fieldId))); + if (strObj != nullptr) { + ScopedUtfChars chars(env, strObj.get()); + return std::string(chars.c_str()); + } + return std::string(defaultValue); +} + +} // namespace android + +#endif // GRAPHICS_JNI_HELPERS diff --git a/libs/hwui/jni/pdf/PdfDocument.cpp b/libs/hwui/jni/pdf/PdfDocument.cpp index 5f67d3008f45..d21eb3f6a208 100644 --- a/libs/hwui/jni/pdf/PdfDocument.cpp +++ b/libs/hwui/jni/pdf/PdfDocument.cpp @@ -14,9 +14,7 @@ * limitations under the License. */ -#include "jni.h" #include "GraphicsJNI.h" -#include "core_jni_helpers.h" #include #include "CreateJavaOutputStreamAdaptor.h" diff --git a/libs/hwui/jni/pdf/PdfEditor.cpp b/libs/hwui/jni/pdf/PdfEditor.cpp index 545f4c521ae8..828d6e3992b6 100644 --- a/libs/hwui/jni/pdf/PdfEditor.cpp +++ b/libs/hwui/jni/pdf/PdfEditor.cpp @@ -27,8 +27,7 @@ #include "PdfUtils.h" -#include "jni.h" -#include +#include "graphics_jni_helpers.h" #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor" @@ -40,8 +39,6 @@ #include "SkMatrix.h" -#include - namespace android { enum PageBox {PAGE_BOX_MEDIA, PAGE_BOX_CROP}; diff --git a/libs/hwui/jni/pdf/PdfRenderer.cpp b/libs/hwui/jni/pdf/PdfRenderer.cpp index 761830b0e97c..cc1f96197c74 100644 --- a/libs/hwui/jni/pdf/PdfRenderer.cpp +++ b/libs/hwui/jni/pdf/PdfRenderer.cpp @@ -16,14 +16,11 @@ #include "PdfUtils.h" -#include "jni.h" -#include #include "GraphicsJNI.h" #include "SkBitmap.h" #include "SkMatrix.h" #include "fpdfview.h" -#include "core_jni_helpers.h" #include #include #include diff --git a/libs/hwui/jni/scoped_nullable_primitive_array.h b/libs/hwui/jni/scoped_nullable_primitive_array.h new file mode 100644 index 000000000000..77f4c9d14f07 --- /dev/null +++ b/libs/hwui/jni/scoped_nullable_primitive_array.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2017 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 SCOPED_NULLABLE_PRIMITIVE_ARRAY_H +#define SCOPED_NULLABLE_PRIMITIVE_ARRAY_H + +#include + +namespace android { + +#define ARRAY_TRAITS(ARRAY_TYPE, POINTER_TYPE, NAME) \ +class NAME ## ArrayTraits { \ +public: \ + static constexpr void getArrayRegion(JNIEnv* env, ARRAY_TYPE array, size_t start, \ + size_t len, POINTER_TYPE out) { \ + env->Get ## NAME ## ArrayRegion(array, start, len, out); \ + } \ + \ + static constexpr POINTER_TYPE getArrayElements(JNIEnv* env, ARRAY_TYPE array) { \ + return env->Get ## NAME ## ArrayElements(array, nullptr); \ + } \ + \ + static constexpr void releaseArrayElements(JNIEnv* env, ARRAY_TYPE array, \ + POINTER_TYPE buffer, jint mode) { \ + env->Release ## NAME ## ArrayElements(array, buffer, mode); \ + } \ +}; \ + +ARRAY_TRAITS(jbooleanArray, jboolean*, Boolean) +ARRAY_TRAITS(jbyteArray, jbyte*, Byte) +ARRAY_TRAITS(jcharArray, jchar*, Char) +ARRAY_TRAITS(jdoubleArray, jdouble*, Double) +ARRAY_TRAITS(jfloatArray, jfloat*, Float) +ARRAY_TRAITS(jintArray, jint*, Int) +ARRAY_TRAITS(jlongArray, jlong*, Long) +ARRAY_TRAITS(jshortArray, jshort*, Short) + +#undef ARRAY_TRAITS + +template +class ScopedArrayRO { +public: + ScopedArrayRO(JNIEnv* env, JavaArrayType javaArray) : mEnv(env), mJavaArray(javaArray) { + if (mJavaArray == nullptr) { + mSize = 0; + mRawArray = nullptr; + } else { + mSize = mEnv->GetArrayLength(mJavaArray); + if (mSize <= preallocSize) { + Traits::getArrayRegion(mEnv, mJavaArray, 0, mSize, mBuffer); + mRawArray = mBuffer; + } else { + mRawArray = Traits::getArrayElements(mEnv, mJavaArray); + } + } + } + + ~ScopedArrayRO() { + if (mRawArray != nullptr && mRawArray != mBuffer) { + Traits::releaseArrayElements(mEnv, mJavaArray, mRawArray, JNI_ABORT); + } + } + + const PrimitiveType* get() const { return mRawArray; } + const PrimitiveType& operator[](size_t n) const { return mRawArray[n]; } + size_t size() const { return mSize; } + +private: + JNIEnv* const mEnv; + JavaArrayType mJavaArray; + PrimitiveType* mRawArray; + size_t mSize; + PrimitiveType mBuffer[preallocSize]; + DISALLOW_COPY_AND_ASSIGN(ScopedArrayRO); +}; + +// ScopedNullable***ArrayRO provide convenient read-only access to Java array from JNI code. +// These accept nullptr. In that case, get() returns nullptr and size() returns 0. +using ScopedNullableBooleanArrayRO = ScopedArrayRO; +using ScopedNullableByteArrayRO = ScopedArrayRO; +using ScopedNullableCharArrayRO = ScopedArrayRO; +using ScopedNullableDoubleArrayRO = ScopedArrayRO; +using ScopedNullableFloatArrayRO = ScopedArrayRO; +using ScopedNullableIntArrayRO = ScopedArrayRO; +using ScopedNullableLongArrayRO = ScopedArrayRO; +using ScopedNullableShortArrayRO = ScopedArrayRO; + +} // namespace android + +#endif // SCOPED_NULLABLE_PRIMITIVE_ARRAY_H diff --git a/libs/hwui/jni/text/LineBreaker.cpp b/libs/hwui/jni/text/LineBreaker.cpp index 02963d8d6072..69865171a09d 100644 --- a/libs/hwui/jni/text/LineBreaker.cpp +++ b/libs/hwui/jni/text/LineBreaker.cpp @@ -19,10 +19,9 @@ #include "utils/misc.h" #include "utils/Log.h" +#include "graphics_jni_helpers.h" #include #include -#include -#include "core_jni_helpers.h" #include "scoped_nullable_primitive_array.h" #include #include diff --git a/libs/hwui/jni/text/MeasuredText.cpp b/libs/hwui/jni/text/MeasuredText.cpp index e001bd44dc78..7793746ee285 100644 --- a/libs/hwui/jni/text/MeasuredText.cpp +++ b/libs/hwui/jni/text/MeasuredText.cpp @@ -22,8 +22,6 @@ #include "utils/Log.h" #include #include -#include -#include "core_jni_helpers.h" #include #include #include -- cgit v1.2.3 From ee538a3a8578e684b0d04743a3e5904d622960ba Mon Sep 17 00:00:00 2001 From: Derek Sollenberger Date: Wed, 19 Feb 2020 11:51:17 -0500 Subject: Cleanup header and build targets for libhwui clients. Targets that will also be contained within the UI module are allowed to access internal headers. All other targets that depend on libhwui are restricted to using the APEX headers. Bug: 137655431 Test: CtsUiRenderingTestCases Change-Id: Id92e9874dafb98bd79839d45ab8f22ab999689de --- libs/input/Android.bp | 1 + libs/input/tests/Android.bp | 1 + 2 files changed, 2 insertions(+) (limited to 'libs') diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 6bb896fd7b29..88d6033ed9fb 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -23,6 +23,7 @@ cc_library_shared { "libandroid_runtime", "libbinder", "libcutils", + "libhwui", "liblog", "libutils", "libgui", diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index b1e3d6fe845a..213b3adfb2a8 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -20,6 +20,7 @@ cc_test { shared_libs: [ "libandroid_runtime", "libinputservice", + "libhwui", "libgui", "libutils", ], -- cgit v1.2.3 From 15da7e24a5c73f600dc87d103d82f6396b2ef82e Mon Sep 17 00:00:00 2001 From: Derek Sollenberger Date: Fri, 14 Feb 2020 14:16:34 -0500 Subject: Update Region.cpp to use AParcel NDK APIs Test: CtsGraphicsTestCases Bug: 145227478 Change-Id: I36f5d3e760bce3c302277c7897c7f8df0ac3cb61 --- libs/hwui/Android.bp | 3 ++- libs/hwui/jni/Region.cpp | 20 +++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) (limited to 'libs') diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 1715a279130e..973f857a861c 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -288,7 +288,6 @@ cc_defaults { "jni/PathEffect.cpp", "jni/PathMeasure.cpp", "jni/Picture.cpp", - "jni/Region.cpp", "jni/Shader.cpp", "jni/Typeface.cpp", "jni/Utils.cpp", @@ -334,6 +333,7 @@ cc_defaults { "jni/GraphicsStatsService.cpp", "jni/Movie.cpp", "jni/MovieImpl.cpp", + "jni/Region.cpp", // requires libbinder_ndk "jni/pdf/PdfDocument.cpp", "jni/pdf/PdfEditor.cpp", "jni/pdf/PdfRenderer.cpp", @@ -341,6 +341,7 @@ cc_defaults { ], shared_libs: [ "libandroidfw", + "libbinder_ndk", "libmediandk", "libnativedisplay", "libnativewindow", diff --git a/libs/hwui/jni/Region.cpp b/libs/hwui/jni/Region.cpp index c95bcea57ef5..1e064b820591 100644 --- a/libs/hwui/jni/Region.cpp +++ b/libs/hwui/jni/Region.cpp @@ -19,10 +19,10 @@ #include "GraphicsJNI.h" #ifdef __ANDROID__ // Layoutlib does not support parcel -#include +#include +#include +#include #endif -#include "android_os_Parcel.h" -#include "android_util_Binder.h" namespace android { @@ -207,10 +207,11 @@ static jlong Region_createFromParcel(JNIEnv* env, jobject clazz, jobject parcel) return 0; } - android::Parcel* p = android::parcelForJavaObject(env, parcel); - std::vector rects; - p->readInt32Vector(&rects); + + AParcel* p = AParcel_fromJavaParcel(env, parcel); + ndk::AParcel_readVector(p, &rects); + AParcel_delete(p); if ((rects.size() % 4) != 0) { return 0; @@ -235,8 +236,6 @@ static jboolean Region_writeToParcel(JNIEnv* env, jobject clazz, jlong regionHan return JNI_FALSE; } - android::Parcel* p = android::parcelForJavaObject(env, parcel); - std::vector rects; SkRegion::Iterator it(*region); while (!it.done()) { @@ -248,7 +247,10 @@ static jboolean Region_writeToParcel(JNIEnv* env, jobject clazz, jlong regionHan it.next(); } - p->writeInt32Vector(rects); + AParcel* p = AParcel_fromJavaParcel(env, parcel); + ndk::AParcel_writeVector(p, rects); + AParcel_delete(p); + return JNI_TRUE; #else return JNI_FALSE; -- cgit v1.2.3 From 42c50042d1f05d92ecc57baebe3326a57aeecf77 Mon Sep 17 00:00:00 2001 From: Derek Sollenberger Date: Tue, 18 Feb 2020 14:51:17 -0500 Subject: Remove dependence on libandroid_runtime from Bitmap.cpp The end goal is to have Bitmap.cpp use AParcel, but until that API is extended to support this use case this is an alternative way to isolate the graphics files from the libandroid_runtime. Test: CtsGraphicsTestCases Bug: 145227478 Change-Id: Ie3854fe03dec4f7b1b485295bb9a5ebba52ddb7c --- libs/hwui/Android.bp | 2 +- libs/hwui/jni/Bitmap.cpp | 48 ++++++++++++++++++++++++++++-------------------- 2 files changed, 29 insertions(+), 21 deletions(-) (limited to 'libs') diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 973f857a861c..ac2fd98248d0 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -311,7 +311,6 @@ cc_defaults { shared_libs: [ "libbase", - "libbinder", "libcutils", "libharfbuzz_ng", "liblog", @@ -341,6 +340,7 @@ cc_defaults { ], shared_libs: [ "libandroidfw", + "libbinder", "libbinder_ndk", "libmediandk", "libnativedisplay", diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp index 9981e89ab561..ba669053ed63 100755 --- a/libs/hwui/jni/Bitmap.cpp +++ b/libs/hwui/jni/Bitmap.cpp @@ -12,7 +12,6 @@ #include "SkStream.h" #include "SkWebpEncoder.h" -#include "android_os_Parcel.h" #include "android_nio_utils.h" #include "CreateJavaOutputStreamAdaptor.h" #include @@ -20,7 +19,7 @@ #include #ifdef __ANDROID__ // Layoutlib does not support graphic buffer, parcel or render thread -#include +#include #include #include #include @@ -568,6 +567,25 @@ static void Bitmap_setHasMipMap(JNIEnv* env, jobject, jlong bitmapHandle, /////////////////////////////////////////////////////////////////////////////// +#ifdef __ANDROID__ // Layoutlib does not support parcel +static struct parcel_offsets_t +{ + jclass clazz; + jfieldID mNativePtr; +} gParcelOffsets; + +static Parcel* parcelForJavaObject(JNIEnv* env, jobject obj) { + if (obj) { + Parcel* p = (Parcel*)env->GetLongField(obj, gParcelOffsets.mNativePtr); + if (p != NULL) { + return p; + } + jniThrowException(env, "java/lang/IllegalStateException", "Parcel has been finalized!"); + } + return NULL; +} +#endif + // This is the maximum possible size because the SkColorSpace must be // representable (and therefore serializable) using a matrix and numerical // transfer function. If we allow more color space representations in the @@ -581,7 +599,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { return NULL; } - android::Parcel* p = android::parcelForJavaObject(env, parcel); + android::Parcel* p = parcelForJavaObject(env, parcel); const SkColorType colorType = (SkColorType)p->readInt32(); const SkAlphaType alphaType = (SkAlphaType)p->readInt32(); @@ -704,7 +722,7 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, return JNI_FALSE; } - android::Parcel* p = android::parcelForJavaObject(env, parcel); + android::Parcel* p = parcelForJavaObject(env, parcel); SkBitmap bitmap; auto bitmapWrapper = reinterpret_cast(bitmapHandle); @@ -1048,19 +1066,6 @@ static jobject Bitmap_wrapHardwareBufferBitmap(JNIEnv* env, jobject, jobject har #endif } -static jobject Bitmap_createGraphicBufferHandle(JNIEnv* env, jobject, jlong bitmapPtr) { -#ifdef __ANDROID__ // Layoutlib does not support graphic buffer - LocalScopedBitmap bitmapHandle(bitmapPtr); - LOG_ALWAYS_FATAL_IF(!bitmapHandle->isHardware(), - "Hardware config is only supported config in Bitmap_getGraphicBuffer"); - - Bitmap& bitmap = bitmapHandle->bitmap(); - return android_graphics_GraphicBuffer_createFromAHardwareBuffer(env, bitmap.hardwareBuffer()); -#else - return NULL; -#endif -} - static jobject Bitmap_getHardwareBuffer(JNIEnv* env, jobject, jlong bitmapPtr) { #ifdef __ANDROID__ // Layoutlib does not support graphic buffer LocalScopedBitmap bitmapHandle(bitmapPtr); @@ -1138,8 +1143,6 @@ static const JNINativeMethod gBitmapMethods[] = { (void*)Bitmap_copyPreserveInternalConfig }, { "nativeWrapHardwareBufferBitmap", "(Landroid/hardware/HardwareBuffer;J)Landroid/graphics/Bitmap;", (void*) Bitmap_wrapHardwareBufferBitmap }, - { "nativeCreateGraphicBufferHandle", "(J)Landroid/graphics/GraphicBuffer;", - (void*) Bitmap_createGraphicBufferHandle }, { "nativeGetHardwareBuffer", "(J)Landroid/hardware/HardwareBuffer;", (void*) Bitmap_getHardwareBuffer }, { "nativeComputeColorSpace", "(J)Landroid/graphics/ColorSpace;", (void*)Bitmap_computeColorSpace }, @@ -1153,6 +1156,8 @@ static const JNINativeMethod gBitmapMethods[] = { }; +const char* const kParcelPathName = "android/os/Parcel"; + int register_android_graphics_Bitmap(JNIEnv* env) { gBitmap_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Bitmap")); @@ -1160,7 +1165,7 @@ int register_android_graphics_Bitmap(JNIEnv* env) gBitmap_constructorMethodID = GetMethodIDOrDie(env, gBitmap_class, "", "(JIIIZ[BLandroid/graphics/NinePatch$InsetStruct;Z)V"); gBitmap_reinitMethodID = GetMethodIDOrDie(env, gBitmap_class, "reinit", "(IIZ)V"); -#ifdef __ANDROID__ // Layoutlib does not support graphic buffer +#ifdef __ANDROID__ // Layoutlib does not support graphic buffer or parcel void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE); AHardwareBuffer_fromHardwareBuffer = (AHB_from_HB)dlsym(handle_, "AHardwareBuffer_fromHardwareBuffer"); @@ -1170,6 +1175,9 @@ int register_android_graphics_Bitmap(JNIEnv* env) AHardwareBuffer_toHardwareBuffer = (AHB_to_HB)dlsym(handle_, "AHardwareBuffer_toHardwareBuffer"); LOG_ALWAYS_FATAL_IF(AHardwareBuffer_toHardwareBuffer == nullptr, " Failed to find required symbol AHardwareBuffer_toHardwareBuffer!"); + + gParcelOffsets.clazz = MakeGlobalRefOrDie(env, FindClassOrDie(env, kParcelPathName)); + gParcelOffsets.mNativePtr = GetFieldIDOrDie(env, gParcelOffsets.clazz, "mNativePtr", "J"); #endif return android::RegisterMethodsOrDie(env, "android/graphics/Bitmap", gBitmapMethods, NELEM(gBitmapMethods)); -- cgit v1.2.3 From 2173ea286afff6766043227de0bc2d82d9595f77 Mon Sep 17 00:00:00 2001 From: Derek Sollenberger Date: Wed, 19 Feb 2020 15:37:29 -0500 Subject: Export symbols for the newly exposed APEX/internal headers Bug: 137655431 Test: CtsUiRenderingTestCases Change-Id: I71c6d1a53ae080bf104848679ee62a77cf07c9fa --- libs/hwui/apex/include/android/graphics/bitmap.h | 27 +++++++++++----------- libs/hwui/apex/include/android/graphics/canvas.h | 19 +++++++-------- .../apex/include/android/graphics/jni_runtime.h | 9 +++++--- libs/hwui/apex/include/android/graphics/matrix.h | 3 ++- libs/hwui/apex/include/android/graphics/paint.h | 7 +++--- libs/hwui/apex/include/android/graphics/region.h | 15 ++++++------ .../apex/include/android/graphics/renderthread.h | 3 ++- libs/hwui/jni/BitmapFactory.cpp | 1 + libs/hwui/jni/GraphicsJNI.h | 4 +++- libs/hwui/jni/GraphicsStatsService.cpp | 7 ++++-- libs/hwui/jni/MimeType.h | 22 ++++++++++++++++++ 11 files changed, 77 insertions(+), 40 deletions(-) create mode 100644 libs/hwui/jni/MimeType.h (limited to 'libs') diff --git a/libs/hwui/apex/include/android/graphics/bitmap.h b/libs/hwui/apex/include/android/graphics/bitmap.h index 45fec2ab7b43..8c4b439d2a2b 100644 --- a/libs/hwui/apex/include/android/graphics/bitmap.h +++ b/libs/hwui/apex/include/android/graphics/bitmap.h @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -37,31 +38,31 @@ typedef struct ABitmap ABitmap; * NOTE: This API does not need to remain as an APEX API if/when we pull libjnigraphics into the * UI module. */ -AndroidBitmapInfo ABitmap_getInfoFromJava(JNIEnv* env, jobject bitmapObj); +ANDROID_API AndroidBitmapInfo ABitmap_getInfoFromJava(JNIEnv* env, jobject bitmapObj); /** * * @return ptr to an opaque handle to the native bitmap or null if the java bitmap has been recycled * or does not exist. */ -ABitmap* ABitmap_acquireBitmapFromJava(JNIEnv* env, jobject bitmapObj); +ANDROID_API ABitmap* ABitmap_acquireBitmapFromJava(JNIEnv* env, jobject bitmapObj); -ABitmap* ABitmap_copy(ABitmap* srcBitmap, AndroidBitmapFormat dstFormat); +ANDROID_API ABitmap* ABitmap_copy(ABitmap* srcBitmap, AndroidBitmapFormat dstFormat); -void ABitmap_acquireRef(ABitmap* bitmap); -void ABitmap_releaseRef(ABitmap* bitmap); +ANDROID_API void ABitmap_acquireRef(ABitmap* bitmap); +ANDROID_API void ABitmap_releaseRef(ABitmap* bitmap); -AndroidBitmapInfo ABitmap_getInfo(ABitmap* bitmap); -ADataSpace ABitmap_getDataSpace(ABitmap* bitmap); +ANDROID_API AndroidBitmapInfo ABitmap_getInfo(ABitmap* bitmap); +ANDROID_API ADataSpace ABitmap_getDataSpace(ABitmap* bitmap); -void* ABitmap_getPixels(ABitmap* bitmap); -void ABitmap_notifyPixelsChanged(ABitmap* bitmap); +ANDROID_API void* ABitmap_getPixels(ABitmap* bitmap); +ANDROID_API void ABitmap_notifyPixelsChanged(ABitmap* bitmap); -AndroidBitmapFormat ABitmapConfig_getFormatFromConfig(JNIEnv* env, jobject bitmapConfigObj); -jobject ABitmapConfig_getConfigFromFormat(JNIEnv* env, AndroidBitmapFormat format); +ANDROID_API AndroidBitmapFormat ABitmapConfig_getFormatFromConfig(JNIEnv* env, jobject bitmapConfigObj); +ANDROID_API jobject ABitmapConfig_getConfigFromFormat(JNIEnv* env, AndroidBitmapFormat format); // NDK access -int ABitmap_compress(const AndroidBitmapInfo* info, ADataSpace dataSpace, const void* pixels, +ANDROID_API int ABitmap_compress(const AndroidBitmapInfo* info, ADataSpace dataSpace, const void* pixels, AndroidBitmapCompressFormat format, int32_t quality, void* userContext, AndroidBitmap_CompressWriteFunc); /** @@ -75,7 +76,7 @@ int ABitmap_compress(const AndroidBitmapInfo* info, ADataSpace dataSpace, const * a reference on the buffer, and the client must call * AHardwareBuffer_release when finished with it. */ -AHardwareBuffer* ABitmap_getHardwareBuffer(ABitmap* bitmap); +ANDROID_API AHardwareBuffer* ABitmap_getHardwareBuffer(ABitmap* bitmap); __END_DECLS diff --git a/libs/hwui/apex/include/android/graphics/canvas.h b/libs/hwui/apex/include/android/graphics/canvas.h index 6fd6b0693b37..a0cecc04a7e5 100644 --- a/libs/hwui/apex/include/android/graphics/canvas.h +++ b/libs/hwui/apex/include/android/graphics/canvas.h @@ -20,6 +20,7 @@ #include #include #include +#include #include __BEGIN_DECLS @@ -30,24 +31,24 @@ __BEGIN_DECLS typedef struct ACanvas ACanvas; // One of AHardwareBuffer_Format. -bool ACanvas_isSupportedPixelFormat(int32_t bufferFormat); +ANDROID_API bool ACanvas_isSupportedPixelFormat(int32_t bufferFormat); /** * Returns a native handle to a Java android.graphics.Canvas * * @return ACanvas* that is only valid for the life of the jobject. */ -ACanvas* ACanvas_getNativeHandleFromJava(JNIEnv* env, jobject canvas); +ANDROID_API ACanvas* ACanvas_getNativeHandleFromJava(JNIEnv* env, jobject canvas); /** * Creates a canvas that wraps the buffer * * @param buffer is a required param. If no buffer is provided a nullptr will be returned. */ -ACanvas* ACanvas_createCanvas(const ANativeWindow_Buffer* buffer, +ANDROID_API ACanvas* ACanvas_createCanvas(const ANativeWindow_Buffer* buffer, int32_t /*android_dataspace_t*/ dataspace); -void ACanvas_destroyCanvas(ACanvas* canvas); +ANDROID_API void ACanvas_destroyCanvas(ACanvas* canvas); /** * Updates the canvas to render into the pixels in the provided buffer @@ -60,7 +61,7 @@ void ACanvas_destroyCanvas(ACanvas* canvas); * method will behave as if nullptr were passed as the input buffer and the previous buffer * will still be released. */ -bool ACanvas_setBuffer(ACanvas* canvas, const ANativeWindow_Buffer* buffer, +ANDROID_API bool ACanvas_setBuffer(ACanvas* canvas, const ANativeWindow_Buffer* buffer, int32_t /*android_dataspace_t*/ dataspace); /** @@ -68,21 +69,21 @@ bool ACanvas_setBuffer(ACanvas* canvas, const ANativeWindow_Buffer* buffer, * * @param clipRect required */ -void ACanvas_clipRect(ACanvas* canvas, const ARect* clipRect, bool doAntiAlias = false); +ANDROID_API void ACanvas_clipRect(ACanvas* canvas, const ARect* clipRect, bool doAntiAlias = false); /** * Clips operations on the canvas to the difference of the current clip and the provided clipRect. * * @param clipRect required */ -void ACanvas_clipOutRect(ACanvas* canvas, const ARect* clipRect, bool doAntiAlias = false); +ANDROID_API void ACanvas_clipOutRect(ACanvas* canvas, const ARect* clipRect, bool doAntiAlias = false); /** * * @param rect required * @param paint required */ -void ACanvas_drawRect(ACanvas* canvas, const ARect* rect, const APaint* paint); +ANDROID_API void ACanvas_drawRect(ACanvas* canvas, const ARect* rect, const APaint* paint); /** * @@ -91,7 +92,7 @@ void ACanvas_drawRect(ACanvas* canvas, const ARect* rect, const APaint* paint); * @param top * @param paint */ -void ACanvas_drawBitmap(ACanvas* canvas, const ABitmap* bitmap, float left, float top, +ANDROID_API void ACanvas_drawBitmap(ACanvas* canvas, const ABitmap* bitmap, float left, float top, const APaint* paint); __END_DECLS diff --git a/libs/hwui/apex/include/android/graphics/jni_runtime.h b/libs/hwui/apex/include/android/graphics/jni_runtime.h index 872a9497ab90..487383ed50d5 100644 --- a/libs/hwui/apex/include/android/graphics/jni_runtime.h +++ b/libs/hwui/apex/include/android/graphics/jni_runtime.h @@ -16,15 +16,18 @@ #ifndef ANDROID_GRAPHICS_JNI_RUNTIME_H #define ANDROID_GRAPHICS_JNI_RUNTIME_H +#include #include __BEGIN_DECLS -void init_android_graphics(); +ANDROID_API void init_android_graphics(); -int register_android_graphics_classes(JNIEnv* env); +ANDROID_API int register_android_graphics_classes(JNIEnv* env); -void zygote_preload_graphics(); +ANDROID_API int register_android_graphics_GraphicsStatsService(JNIEnv* env); + +ANDROID_API void zygote_preload_graphics(); __END_DECLS diff --git a/libs/hwui/apex/include/android/graphics/matrix.h b/libs/hwui/apex/include/android/graphics/matrix.h index 4039cd1b8f74..987ad13f7635 100644 --- a/libs/hwui/apex/include/android/graphics/matrix.h +++ b/libs/hwui/apex/include/android/graphics/matrix.h @@ -18,6 +18,7 @@ #define ANDROID_GRAPHICS_MATRIX_H #include +#include #include __BEGIN_DECLS @@ -31,7 +32,7 @@ __BEGIN_DECLS * @return true if the values param was populated and false otherwise. */ -bool AMatrix_getContents(JNIEnv* env, jobject matrixObj, float values[9]); +ANDROID_API bool AMatrix_getContents(JNIEnv* env, jobject matrixObj, float values[9]); __END_DECLS diff --git a/libs/hwui/apex/include/android/graphics/paint.h b/libs/hwui/apex/include/android/graphics/paint.h index 5895e006bf93..058db8d37619 100644 --- a/libs/hwui/apex/include/android/graphics/paint.h +++ b/libs/hwui/apex/include/android/graphics/paint.h @@ -16,6 +16,7 @@ #ifndef ANDROID_GRAPHICS_PAINT_H #define ANDROID_GRAPHICS_PAINT_H +#include #include __BEGIN_DECLS @@ -35,11 +36,11 @@ enum ABlendMode { ABLEND_MODE_SRC = 2, }; -APaint* APaint_createPaint(); +ANDROID_API APaint* APaint_createPaint(); -void APaint_destroyPaint(APaint* paint); +ANDROID_API void APaint_destroyPaint(APaint* paint); -void APaint_setBlendMode(APaint* paint, ABlendMode blendMode); +ANDROID_API void APaint_setBlendMode(APaint* paint, ABlendMode blendMode); __END_DECLS diff --git a/libs/hwui/apex/include/android/graphics/region.h b/libs/hwui/apex/include/android/graphics/region.h index 961067a8e2db..0756d6dd63f6 100644 --- a/libs/hwui/apex/include/android/graphics/region.h +++ b/libs/hwui/apex/include/android/graphics/region.h @@ -16,6 +16,7 @@ #ifndef ANDROID_GRAPHICS_REGION_H #define ANDROID_GRAPHICS_REGION_H +#include #include #include #include @@ -35,19 +36,19 @@ typedef struct ARegionIterator ARegionIterator; * @return ARegionIterator that must be closed and must not live longer than the life * of the jobject. It returns nullptr if the region is not a valid object. */ -ARegionIterator* ARegionIterator_acquireIterator(JNIEnv* env, jobject region); +ANDROID_API ARegionIterator* ARegionIterator_acquireIterator(JNIEnv* env, jobject region); -void ARegionIterator_releaseIterator(ARegionIterator* iterator); +ANDROID_API void ARegionIterator_releaseIterator(ARegionIterator* iterator); -bool ARegionIterator_isComplex(ARegionIterator* iterator); +ANDROID_API bool ARegionIterator_isComplex(ARegionIterator* iterator); -bool ARegionIterator_isDone(ARegionIterator* iterator); +ANDROID_API bool ARegionIterator_isDone(ARegionIterator* iterator); -void ARegionIterator_next(ARegionIterator* iterator); +ANDROID_API void ARegionIterator_next(ARegionIterator* iterator); -ARect ARegionIterator_getRect(ARegionIterator* iterator); +ANDROID_API ARect ARegionIterator_getRect(ARegionIterator* iterator); -ARect ARegionIterator_getTotalBounds(ARegionIterator* iterator); +ANDROID_API ARect ARegionIterator_getTotalBounds(ARegionIterator* iterator); __END_DECLS diff --git a/libs/hwui/apex/include/android/graphics/renderthread.h b/libs/hwui/apex/include/android/graphics/renderthread.h index 0a790af731a9..50280a6dd1fb 100644 --- a/libs/hwui/apex/include/android/graphics/renderthread.h +++ b/libs/hwui/apex/include/android/graphics/renderthread.h @@ -16,6 +16,7 @@ #ifndef ANDROID_GRAPHICS_RENDERTHREAD_H #define ANDROID_GRAPHICS_RENDERTHREAD_H +#include #include __BEGIN_DECLS @@ -26,7 +27,7 @@ __BEGIN_DECLS * function requires a valid fd, but does not persist or assume ownership of the fd * outside the scope of this function. */ -void ARenderThread_dumpGraphicsMemory(int fd); +ANDROID_API void ARenderThread_dumpGraphicsMemory(int fd); __END_DECLS diff --git a/libs/hwui/jni/BitmapFactory.cpp b/libs/hwui/jni/BitmapFactory.cpp index f3395b9e6877..d4e27d812500 100644 --- a/libs/hwui/jni/BitmapFactory.cpp +++ b/libs/hwui/jni/BitmapFactory.cpp @@ -4,6 +4,7 @@ #include "BitmapFactory.h" #include "CreateJavaOutputStreamAdaptor.h" #include "GraphicsJNI.h" +#include "MimeType.h" #include "NinePatchPeeker.h" #include "SkAndroidCodec.h" #include "SkBRDAllocator.h" diff --git a/libs/hwui/jni/GraphicsJNI.h b/libs/hwui/jni/GraphicsJNI.h index 4bf3ed1c6a3e..b58a740a4c27 100644 --- a/libs/hwui/jni/GraphicsJNI.h +++ b/libs/hwui/jni/GraphicsJNI.h @@ -1,6 +1,8 @@ #ifndef _ANDROID_GRAPHICS_GRAPHICS_JNI_H_ #define _ANDROID_GRAPHICS_GRAPHICS_JNI_H_ +#include + #include "Bitmap.h" #include "SkBitmap.h" #include "SkBRDAllocator.h" @@ -75,7 +77,7 @@ public: static SkPoint* jpointf_to_point(JNIEnv*, jobject jpointf, SkPoint* point); static void point_to_jpointf(const SkPoint& point, JNIEnv*, jobject jpointf); - static android::Canvas* getNativeCanvas(JNIEnv*, jobject canvas); + ANDROID_API static android::Canvas* getNativeCanvas(JNIEnv*, jobject canvas); static android::Bitmap* getNativeBitmap(JNIEnv*, jobject bitmap); static SkImageInfo getBitmapInfo(JNIEnv*, jobject bitmap, uint32_t* outRowBytes, bool* isHardware); diff --git a/libs/hwui/jni/GraphicsStatsService.cpp b/libs/hwui/jni/GraphicsStatsService.cpp index e9259462bcac..6076552fc094 100644 --- a/libs/hwui/jni/GraphicsStatsService.cpp +++ b/libs/hwui/jni/GraphicsStatsService.cpp @@ -25,6 +25,8 @@ #include #include #include + +#include "android/graphics/jni_runtime.h" #include "GraphicsJNI.h" namespace android { @@ -171,6 +173,9 @@ static void nativeDestructor(JNIEnv* env, jobject javaObject) { gGraphicsStatsServiceObject = nullptr; } +} // namespace android +using namespace android; + static const JNINativeMethod sMethods[] = {{"nGetAshmemSize", "()I", (void*)getAshmemSize}, {"nCreateDump", "(IZ)J", (void*)createDump}, @@ -190,5 +195,3 @@ int register_android_graphics_GraphicsStatsService(JNIEnv* env) { return jniRegisterNativeMethods(env, "android/graphics/GraphicsStatsService", sMethods, NELEM(sMethods)); } - -} // namespace android diff --git a/libs/hwui/jni/MimeType.h b/libs/hwui/jni/MimeType.h new file mode 100644 index 000000000000..fdd510cfeb79 --- /dev/null +++ b/libs/hwui/jni/MimeType.h @@ -0,0 +1,22 @@ +/* + * Copyright 2019 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. + */ + +#pragma once + +#include +#include "SkEncodedImageFormat.h" + +ANDROID_API const char* getMimeType(SkEncodedImageFormat); -- cgit v1.2.3 From 62ac8b56a9f7cf75f3f0677ec37d8acb8def475c Mon Sep 17 00:00:00 2001 From: Winson Date: Wed, 4 Dec 2019 08:36:48 -0800 Subject: Refactor overlayable policy To make it easier to add the actor policy in a follow up CL, move most of the policy handling to a central location. The strings and transformation between strings and flags is now handled in libidmap2policies, with libandroidfw containing the single source of policy flags. This also extracts all the test resource IDs into an R.h so they can be swapped without having to edit a dozen files each time. Bug: 130563563 Test: m aapt2_tests idmapt2_tests and run from host test output Test: atest libandroidfw_tests Change-Id: Ie533c9cebf938215df7586f00c38763ae467e606 --- libs/androidfw/Android.bp | 1 - libs/androidfw/include/androidfw/ResourceTypes.h | 41 ++++++++++++++++++------ libs/androidfw/tests/LoadedArsc_test.cpp | 14 ++++---- 3 files changed, 39 insertions(+), 17 deletions(-) (limited to 'libs') diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index 3f2f3495ae8f..f87f98a59a12 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -191,4 +191,3 @@ cc_benchmark { shared_libs: common_test_libs, data: ["tests/data/**/*.apk"], } - diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index 35cebd425dc7..d15a3a27cbb1 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -34,6 +35,7 @@ #include +#include #include namespace android { @@ -1676,42 +1678,61 @@ struct ResTable_overlayable_header */ struct ResTable_overlayable_policy_header { - struct ResChunk_header header; - + /** + * Flags for a bitmask for all possible overlayable policy options. + * + * Any changes to this set should also update aidl/android/os/OverlayablePolicy.aidl + * and proto/OverlayablePolicy.proto. + */ enum PolicyFlags : uint32_t { + // Base + NONE = 0x00000000, + // Any overlay can overlay these resources. - POLICY_PUBLIC = 0x00000001, + PUBLIC = 0x00000001, // The overlay must reside of the system partition or must have existed on the system partition // before an upgrade to overlay these resources. - POLICY_SYSTEM_PARTITION = 0x00000002, + SYSTEM_PARTITION = 0x00000002, // The overlay must reside of the vendor partition or must have existed on the vendor partition // before an upgrade to overlay these resources. - POLICY_VENDOR_PARTITION = 0x00000004, + VENDOR_PARTITION = 0x00000004, // The overlay must reside of the product partition or must have existed on the product // partition before an upgrade to overlay these resources. - POLICY_PRODUCT_PARTITION = 0x00000008, + PRODUCT_PARTITION = 0x00000008, // The overlay must be signed with the same signature as the actor of the target resource, // which can be separate or the same as the target package with the resource. - POLICY_SIGNATURE = 0x00000010, + SIGNATURE = 0x00000010, // The overlay must reside of the odm partition or must have existed on the odm // partition before an upgrade to overlay these resources. - POLICY_ODM_PARTITION = 0x00000020, + ODM_PARTITION = 0x00000020, // The overlay must reside of the oem partition or must have existed on the oem // partition before an upgrade to overlay these resources. - POLICY_OEM_PARTITION = 0x00000040, + OEM_PARTITION = 0x00000040, }; - uint32_t policy_flags; + + using PolicyBitmask = uint32_t; + + struct ResChunk_header header; + + PolicyFlags policy_flags; // The number of ResTable_ref that follow this header. uint32_t entry_count; }; +inline ResTable_overlayable_policy_header::PolicyFlags& operator |=( + ResTable_overlayable_policy_header::PolicyFlags& first, + ResTable_overlayable_policy_header::PolicyFlags second) { + first = static_cast(first | second); + return first; +} + #pragma pack(push, 1) struct Idmap_header { // Always 0x504D4449 ('IDMP') diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp index 8615069e98dd..2d69dfe4f429 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -41,6 +41,8 @@ using ::testing::NotNull; using ::testing::SizeIs; using ::testing::StrEq; +using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags; + namespace android { TEST(LoadedArscTest, LoadSinglePackageArsc) { @@ -240,29 +242,29 @@ TEST(LoadedArscTest, LoadOverlayable) { ASSERT_THAT(info, NotNull()); EXPECT_THAT(info->name, Eq("OverlayableResources1")); EXPECT_THAT(info->actor, Eq("overlay://theme")); - EXPECT_THAT(info->policy_flags, Eq(ResTable_overlayable_policy_header::POLICY_PUBLIC)); + EXPECT_THAT(info->policy_flags, Eq(PolicyFlags::PUBLIC)); info = package->GetOverlayableInfo(overlayable::R::string::overlayable2); ASSERT_THAT(info, NotNull()); EXPECT_THAT(info->name, Eq("OverlayableResources1")); EXPECT_THAT(info->actor, Eq("overlay://theme")); EXPECT_THAT(info->policy_flags, - Eq(ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION - | ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION)); + Eq(PolicyFlags::SYSTEM_PARTITION + | PolicyFlags::PRODUCT_PARTITION)); info = package->GetOverlayableInfo(overlayable::R::string::overlayable3); ASSERT_THAT(info, NotNull()); EXPECT_THAT(info->name, Eq("OverlayableResources2")); EXPECT_THAT(info->actor, Eq("overlay://com.android.overlayable")); EXPECT_THAT(info->policy_flags, - Eq(ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION - | ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION)); + Eq(PolicyFlags::VENDOR_PARTITION + | PolicyFlags::PRODUCT_PARTITION)); info = package->GetOverlayableInfo(overlayable::R::string::overlayable4); EXPECT_THAT(info->name, Eq("OverlayableResources1")); EXPECT_THAT(info->actor, Eq("overlay://theme")); ASSERT_THAT(info, NotNull()); - EXPECT_THAT(info->policy_flags, Eq(ResTable_overlayable_policy_header::POLICY_PUBLIC)); + EXPECT_THAT(info->policy_flags, Eq(PolicyFlags::PUBLIC)); } TEST(LoadedArscTest, ResourceIdentifierIterator) { -- cgit v1.2.3 From f56ade365aa22e55655f0149cf47181cb3da6e8d Mon Sep 17 00:00:00 2001 From: Winson Date: Wed, 4 Dec 2019 11:32:41 -0800 Subject: Actor signature overlayable policy There are cases where an app can ship overlays for itself, but the "signature" policy as described would open up a vulnerability by allowing the system actor to create and sign any arbitrary overlay that will apply to the target. To prevent this, redefine "signature" as target package only, and introduce "actor" for checking against the actor signature. Any app that wishes to use both can include both policies. Bug: 130563563 Test: m aapt2_tests idmapt2_tests and run from host test output Test: atest libandroidfw_tests Change-Id: I1c583a5b37f4abbeb18fc6a35c502377d8977a41 --- libs/androidfw/include/androidfw/ResourceTypes.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'libs') diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index d15a3a27cbb1..2bfc7fc38d1c 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -1682,7 +1682,6 @@ struct ResTable_overlayable_policy_header * Flags for a bitmask for all possible overlayable policy options. * * Any changes to this set should also update aidl/android/os/OverlayablePolicy.aidl - * and proto/OverlayablePolicy.proto. */ enum PolicyFlags : uint32_t { // Base @@ -1703,8 +1702,8 @@ struct ResTable_overlayable_policy_header // partition before an upgrade to overlay these resources. PRODUCT_PARTITION = 0x00000008, - // The overlay must be signed with the same signature as the actor of the target resource, - // which can be separate or the same as the target package with the resource. + // The overlay must be signed with the same signature as the package containing the target + // resource SIGNATURE = 0x00000010, // The overlay must reside of the odm partition or must have existed on the odm @@ -1714,6 +1713,10 @@ struct ResTable_overlayable_policy_header // The overlay must reside of the oem partition or must have existed on the oem // partition before an upgrade to overlay these resources. OEM_PARTITION = 0x00000040, + + // The overlay must be signed with the same signature as the actor declared for the target + // resource + ACTOR_SIGNATURE = 0x00000080, }; using PolicyBitmask = uint32_t; -- cgit v1.2.3 From 894f132ba056b9d85598068e77d9050ad8963624 Mon Sep 17 00:00:00 2001 From: Tim Murray Date: Mon, 24 Feb 2020 21:30:20 +0000 Subject: Revert "hwui: remove FatVector" Revert "libui: rewrite Region with FatVector" Revert submission 10248126-fatvector-region Reason for revert: b/149254345 Reverted Changes: I09dc2fddd:hwui: remove FatVector I265c6c831:libui: rewrite Region with FatVector also fix wrong license in FatVector.h Test: boots Bug: 149254345 Change-Id: I8ac66acb8b635324051edd41c5d4092c223157ff --- libs/hwui/RenderNode.cpp | 2 +- libs/hwui/RenderNode.h | 3 +- libs/hwui/jni/FontFamily.cpp | 4 +- libs/hwui/jni/fonts/Font.cpp | 4 +- libs/hwui/pipeline/skia/ReorderBarrierDrawables.h | 2 +- libs/hwui/renderthread/RenderThread.cpp | 3 +- libs/hwui/renderthread/VulkanManager.cpp | 2 +- libs/hwui/tests/unit/FatVectorTests.cpp | 2 +- libs/hwui/utils/FatVector.h | 96 +++++++++++++++++++++++ 9 files changed, 106 insertions(+), 12 deletions(-) create mode 100644 libs/hwui/utils/FatVector.h (limited to 'libs') diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index 31e45558139d..6761435a8171 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -27,6 +27,7 @@ #include "DamageAccumulator.h" #include "pipeline/skia/SkiaDisplayList.h" #endif +#include "utils/FatVector.h" #include "utils/MathUtils.h" #include "utils/StringUtils.h" #include "utils/TraceUtils.h" @@ -36,7 +37,6 @@ #include #include #include -#include namespace android { namespace uirenderer { diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index c0ec2174bb35..d55e5b0ce836 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -27,8 +27,6 @@ #include -#include - #include "AnimatorManager.h" #include "CanvasTransform.h" #include "Debug.h" @@ -37,6 +35,7 @@ #include "RenderProperties.h" #include "pipeline/skia/SkiaDisplayList.h" #include "pipeline/skia/SkiaLayer.h" +#include "utils/FatVector.h" #include diff --git a/libs/hwui/jni/FontFamily.cpp b/libs/hwui/jni/FontFamily.cpp index a2fef1e19328..0ce04a2437b9 100644 --- a/libs/hwui/jni/FontFamily.cpp +++ b/libs/hwui/jni/FontFamily.cpp @@ -29,9 +29,9 @@ #include #include +#include #include #include -#include #include @@ -104,7 +104,7 @@ static jlong FontFamily_getFamilyReleaseFunc(CRITICAL_JNI_PARAMS) { static bool addSkTypeface(NativeFamilyBuilder* builder, sk_sp&& data, int ttcIndex, jint weight, jint italic) { - FatVector skiaAxes; + uirenderer::FatVector skiaAxes; for (const auto& axis : builder->axes) { skiaAxes.emplace_back(SkFontArguments::Axis{axis.axisTag, axis.value}); } diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp index 5714cd1d0390..7e8f8d8d173c 100644 --- a/libs/hwui/jni/fonts/Font.cpp +++ b/libs/hwui/jni/fonts/Font.cpp @@ -28,8 +28,8 @@ #include #include +#include #include -#include #include @@ -93,7 +93,7 @@ static jlong Font_Builder_build(JNIEnv* env, jobject clazz, jlong builderPtr, jo sk_sp data(SkData::MakeWithProc(fontPtr, fontSize, release_global_ref, reinterpret_cast(fontRef))); - FatVector skiaAxes; + uirenderer::FatVector skiaAxes; for (const auto& axis : builder->axes) { skiaAxes.emplace_back(SkFontArguments::Axis{axis.axisTag, axis.value}); } diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h index d669f84c5ee2..cfc0f9b258da 100644 --- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h +++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h @@ -21,7 +21,7 @@ #include #include -#include +#include namespace android { namespace uirenderer { diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 206b58f62ea7..cae3e3b5188c 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -27,6 +27,7 @@ #include "pipeline/skia/SkiaOpenGLPipeline.h" #include "pipeline/skia/SkiaVulkanPipeline.h" #include "renderstate/RenderState.h" +#include "utils/FatVector.h" #include "utils/TimeUtils.h" #include "utils/TraceUtils.h" @@ -39,8 +40,6 @@ #include #include -#include - namespace android { namespace uirenderer { namespace renderthread { diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index ba70afc8b8d2..a5355fc3499d 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -23,13 +23,13 @@ #include #include #include -#include #include #include #include "Properties.h" #include "RenderThread.h" #include "renderstate/RenderState.h" +#include "utils/FatVector.h" #include "utils/TraceUtils.h" namespace android { diff --git a/libs/hwui/tests/unit/FatVectorTests.cpp b/libs/hwui/tests/unit/FatVectorTests.cpp index 6585a6249b44..8523e6c9e973 100644 --- a/libs/hwui/tests/unit/FatVectorTests.cpp +++ b/libs/hwui/tests/unit/FatVectorTests.cpp @@ -15,7 +15,7 @@ */ #include -#include +#include #include diff --git a/libs/hwui/utils/FatVector.h b/libs/hwui/utils/FatVector.h new file mode 100644 index 000000000000..49f1984b779f --- /dev/null +++ b/libs/hwui/utils/FatVector.h @@ -0,0 +1,96 @@ +/* + * Copyright 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_FAT_VECTOR_H +#define ANDROID_FAT_VECTOR_H + +#include "utils/Macros.h" + +#include +#include +#include +#include + +#include + +namespace android { +namespace uirenderer { + +template +class InlineStdAllocator { +public: + struct Allocation { + PREVENT_COPY_AND_ASSIGN(Allocation); + + public: + Allocation(){}; + // char array instead of T array, so memory is uninitialized, with no destructors run + char array[sizeof(T) * SIZE]; + bool inUse = false; + }; + + typedef T value_type; // needed to implement std::allocator + typedef T* pointer; // needed to implement std::allocator + + explicit InlineStdAllocator(Allocation& allocation) : mAllocation(allocation) {} + InlineStdAllocator(const InlineStdAllocator& other) : mAllocation(other.mAllocation) {} + ~InlineStdAllocator() {} + + T* allocate(size_t num, const void* = 0) { + if (!mAllocation.inUse && num <= SIZE) { + mAllocation.inUse = true; + return (T*)mAllocation.array; + } else { + return (T*)malloc(num * sizeof(T)); + } + } + + void deallocate(pointer p, size_t num) { + if (p == (T*)mAllocation.array) { + mAllocation.inUse = false; + } else { + // 'free' instead of delete here - destruction handled separately + free(p); + } + } + Allocation& mAllocation; +}; + +/** + * std::vector with SIZE elements preallocated into an internal buffer. + * + * Useful for avoiding the cost of malloc in cases where only SIZE or + * fewer elements are needed in the common case. + */ +template +class FatVector : public std::vector> { +public: + FatVector() + : std::vector>( + InlineStdAllocator(mAllocation)) { + this->reserve(SIZE); + } + + explicit FatVector(size_t capacity) : FatVector() { this->resize(capacity); } + +private: + typename InlineStdAllocator::Allocation mAllocation; +}; + +} // namespace uirenderer +} // namespace android + +#endif // ANDROID_FAT_VECTOR_H -- cgit v1.2.3 From b6f7c4725253de798cd7487f671c66663614a158 Mon Sep 17 00:00:00 2001 From: Mike Ma Date: Tue, 3 Mar 2020 17:58:35 -0800 Subject: Add an option to zip incident report Incident reports are getting bigger as we add more sections. Add an option (-z, default off) to zip incident report. Bug: 150160547 Test: atest incidentd_test Test: adb shell incident -z -p EXPLICIT | gunzip | ./out/soong/host/linux-x86/bin/aprotoc --decode=android.os.IncidentProto --proto_path=./ --proto_path=external/protobuf/src frameworks/base/core/proto/android/os/incident.proto Change-Id: I7c8ff1d91df842c200462ee29f15feae68e62739 --- .../include_priv/android/os/IncidentReportArgs.h | 3 +++ libs/incident/src/IncidentReportArgs.cpp | 26 ++++++++++++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) (limited to 'libs') diff --git a/libs/incident/include_priv/android/os/IncidentReportArgs.h b/libs/incident/include_priv/android/os/IncidentReportArgs.h index 0e6159032e45..ec3aabb3b49d 100644 --- a/libs/incident/include_priv/android/os/IncidentReportArgs.h +++ b/libs/incident/include_priv/android/os/IncidentReportArgs.h @@ -53,6 +53,7 @@ public: void setReceiverPkg(const string& pkg); void setReceiverCls(const string& cls); void addHeader(const vector& headerProto); + void setGzip(bool gzip); inline bool all() const { return mAll; } bool containsSection(int section, bool specific) const; @@ -61,6 +62,7 @@ public: inline const string& receiverPkg() const { return mReceiverPkg; } inline const string& receiverCls() const { return mReceiverCls; } inline const vector>& headers() const { return mHeaders; } + inline bool gzip() const {return mGzip; } void merge(const IncidentReportArgs& that); @@ -71,6 +73,7 @@ private: int mPrivacyPolicy; string mReceiverPkg; string mReceiverCls; + bool mGzip; }; } diff --git a/libs/incident/src/IncidentReportArgs.cpp b/libs/incident/src/IncidentReportArgs.cpp index 9d8a98338ef0..db495cfbf7e1 100644 --- a/libs/incident/src/IncidentReportArgs.cpp +++ b/libs/incident/src/IncidentReportArgs.cpp @@ -26,7 +26,8 @@ namespace os { IncidentReportArgs::IncidentReportArgs() :mSections(), mAll(false), - mPrivacyPolicy(-1) + mPrivacyPolicy(-1), + mGzip(false) { } @@ -36,7 +37,8 @@ IncidentReportArgs::IncidentReportArgs(const IncidentReportArgs& that) mAll(that.mAll), mPrivacyPolicy(that.mPrivacyPolicy), mReceiverPkg(that.mReceiverPkg), - mReceiverCls(that.mReceiverCls) + mReceiverCls(that.mReceiverCls), + mGzip(that.mGzip) { } @@ -93,6 +95,11 @@ IncidentReportArgs::writeToParcel(Parcel* out) const return err; } + err = out->writeInt32(mGzip); + if (err != NO_ERROR) { + return err; + } + return NO_ERROR; } @@ -149,6 +156,15 @@ IncidentReportArgs::readFromParcel(const Parcel* in) mReceiverPkg = String8(in->readString16()).string(); mReceiverCls = String8(in->readString16()).string(); + int32_t gzip; + err = in->readInt32(&gzip); + if (err != NO_ERROR) { + return err; + } + if (gzip != 0) { + mGzip = gzip; + } + return OK; } @@ -193,6 +209,12 @@ IncidentReportArgs::addHeader(const vector& headerProto) mHeaders.push_back(headerProto); } +void +IncidentReportArgs::setGzip(bool gzip) +{ + mGzip = gzip; +} + bool IncidentReportArgs::containsSection(int section, bool specific) const { -- cgit v1.2.3 From 73b23c9a3e6800b83ec85083cfd49d9448f99feb Mon Sep 17 00:00:00 2001 From: Andrii Kulian Date: Mon, 9 Dec 2019 11:50:09 -0800 Subject: DO NOT MERGE Test sidecar OEM implementation for WM support library This provides an example implementation of the Sidecar interface for the Window support library, which allows mocking a foldable device by reading its current configuration from settings. This implementation also makes sure that the reported display features are adjusted to the window position and coordinate space, as required by the Sidecar interface contract. The reported values can be updated by using the following shell commands: settings put global device_posture settings put global display_features -[,,,] Bug: 146188055 Test: Manual, see instructions doc Change-Id: I4764298d7633bc3ef574b5cbc2dee256e9cf3f03 (cherry picked from commit d66f77b0b762091464c850bb79744ea5d07e0053) --- libs/WindowManager/Jetpack/Android.bp | 38 ++++ .../Jetpack/androidx.window.extensions.xml | 21 ++ .../window/extensions/ExtensionHelper.java | 130 ++++++++++++ .../window/extensions/ExtensionProvider.java | 42 ++++ .../window/extensions/SettingsExtensionImpl.java | 217 +++++++++++++++++++++ .../Jetpack/window-extensions-release.aar | Bin 0 -> 6427 bytes 6 files changed, 448 insertions(+) create mode 100644 libs/WindowManager/Jetpack/Android.bp create mode 100644 libs/WindowManager/Jetpack/androidx.window.extensions.xml create mode 100644 libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionHelper.java create mode 100644 libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java create mode 100644 libs/WindowManager/Jetpack/src/androidx/window/extensions/SettingsExtensionImpl.java create mode 100644 libs/WindowManager/Jetpack/window-extensions-release.aar (limited to 'libs') diff --git a/libs/WindowManager/Jetpack/Android.bp b/libs/WindowManager/Jetpack/Android.bp new file mode 100644 index 000000000000..308c1a59a7aa --- /dev/null +++ b/libs/WindowManager/Jetpack/Android.bp @@ -0,0 +1,38 @@ +// Copyright (C) 2020 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. + +android_library_import { + name: "window-extensions", + aars: ["window-extensions-release.aar"], + sdk_version: "current", +} + +java_library { + name: "androidx.window.extensions", + srcs: ["src/**/*.java"], + static_libs: ["window-extensions"], + installable: true, + sdk_version: "core_platform", + vendor: true, + libs: ["framework", "androidx.annotation_annotation",], + required: ["androidx.window.extensions.xml",], +} + +prebuilt_etc { + name: "androidx.window.extensions.xml", + vendor: true, + sub_dir: "permissions", + src: "androidx.window.extensions.xml", + filename_from_src: true, +} diff --git a/libs/WindowManager/Jetpack/androidx.window.extensions.xml b/libs/WindowManager/Jetpack/androidx.window.extensions.xml new file mode 100644 index 000000000000..1f0ff6656de0 --- /dev/null +++ b/libs/WindowManager/Jetpack/androidx.window.extensions.xml @@ -0,0 +1,21 @@ + + + + + diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionHelper.java new file mode 100644 index 000000000000..c4f11a0a370c --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionHelper.java @@ -0,0 +1,130 @@ +/* + * Copyright 2020 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. + */ + +package androidx.window.extensions; + +import static android.view.Display.INVALID_DISPLAY; +import static android.view.Surface.ROTATION_0; +import static android.view.Surface.ROTATION_180; +import static android.view.Surface.ROTATION_270; +import static android.view.Surface.ROTATION_90; + +import android.app.Activity; +import android.app.ActivityThread; +import android.graphics.Rect; +import android.hardware.display.DisplayManagerGlobal; +import android.os.IBinder; +import android.view.DisplayInfo; +import android.view.Surface; + +/** + * Toolkit class for calculation of the display feature bounds within the window. + * NOTE: This sample implementation only works for Activity windows, because there is no public APIs + * to obtain layout params or bounds for arbitrary windows. + */ +class ExtensionHelper { + /** + * Rotate the input rectangle specified in default display orientation to the current display + * rotation. + */ + static void rotateRectToDisplayRotation(Rect inOutRect, int displayId) { + DisplayManagerGlobal dmGlobal = DisplayManagerGlobal.getInstance(); + DisplayInfo displayInfo = dmGlobal.getDisplayInfo(displayId); + int rotation = displayInfo.rotation; + + boolean isSideRotation = rotation == ROTATION_90 || rotation == ROTATION_270; + int displayWidth = isSideRotation ? displayInfo.logicalHeight : displayInfo.logicalWidth; + int displayHeight = isSideRotation ? displayInfo.logicalWidth : displayInfo.logicalHeight; + + inOutRect.intersect(0, 0, displayWidth, displayHeight); + + rotateBounds(inOutRect, displayWidth, displayHeight, rotation); + } + + /** + * Rotate the input rectangle within parent bounds for a given delta. + */ + private static void rotateBounds(Rect inOutRect, int parentWidth, int parentHeight, + @Surface.Rotation int delta) { + int origLeft = inOutRect.left; + switch (delta) { + case ROTATION_0: + return; + case ROTATION_90: + inOutRect.left = inOutRect.top; + inOutRect.top = parentWidth - inOutRect.right; + inOutRect.right = inOutRect.bottom; + inOutRect.bottom = parentWidth - origLeft; + return; + case ROTATION_180: + inOutRect.left = parentWidth - inOutRect.right; + inOutRect.right = parentWidth - origLeft; + return; + case ROTATION_270: + inOutRect.left = parentHeight - inOutRect.bottom; + inOutRect.bottom = inOutRect.right; + inOutRect.right = parentHeight - inOutRect.top; + inOutRect.top = origLeft; + return; + } + } + + /** Transform rectangle from absolute coordinate space to the window coordinate space. */ + static void transformToWindowSpaceRect(Rect inOutRect, IBinder windowToken) { + Rect windowRect = getWindowRect(windowToken); + if (windowRect == null) { + inOutRect.setEmpty(); + return; + } + if (!Rect.intersects(inOutRect, windowRect)) { + inOutRect.setEmpty(); + return; + } + inOutRect.intersect(windowRect); + inOutRect.offset(-windowRect.left, -windowRect.top); + } + + /** + * Get the current window bounds in absolute coordinates. + * NOTE: Only works with Activity windows. + */ + private static Rect getWindowRect(IBinder windowToken) { + Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken); + final Rect windowRect = new Rect(); + if (activity != null) { + activity.getWindow().getDecorView().getWindowDisplayFrame(windowRect); + } + return windowRect; + } + + /** + * Check if this window is an Activity window that is in multi-window mode. + */ + static boolean isInMultiWindow(IBinder windowToken) { + Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken); + return activity != null && activity.isInMultiWindowMode(); + } + + /** + * Get the id of the parent display for the window. + * NOTE: Only works with Activity windows. + */ + static int getWindowDisplay(IBinder windowToken) { + Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken); + return activity != null + ? activity.getWindowManager().getDefaultDisplay().getDisplayId() : INVALID_DISPLAY; + } +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java new file mode 100644 index 000000000000..47349f11fb93 --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java @@ -0,0 +1,42 @@ +/* + * Copyright 2020 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. + */ + +package androidx.window.extensions; + +import android.content.Context; + +/** + * Provider class that will instantiate the library implementation. It must be included in the + * vendor library, and the vendor implementation must match the signature of this class. + */ +public class ExtensionProvider { + + /** + * The support library will instantiate the vendor implementation using this interface. + * @return An implementation of {@link ExtensionInterface}. + */ + public static ExtensionInterface getExtensionImpl(Context context) { + return new SettingsExtensionImpl(context); + } + + /** + * The support library will use this method to check API version compatibility. + * @return API version string in MAJOR.MINOR.PATCH-description format. + */ + public static String getApiVersion() { + return "1.0.0-settings_sample"; + } +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/SettingsExtensionImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/SettingsExtensionImpl.java new file mode 100644 index 000000000000..7a3fbf3ad9b8 --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/SettingsExtensionImpl.java @@ -0,0 +1,217 @@ +/* + * Copyright 2020 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. + */ + +package androidx.window.extensions; + +import static android.view.Display.DEFAULT_DISPLAY; + +import static androidx.window.extensions.ExtensionHelper.getWindowDisplay; +import static androidx.window.extensions.ExtensionHelper.isInMultiWindow; +import static androidx.window.extensions.ExtensionHelper.rotateRectToDisplayRotation; +import static androidx.window.extensions.ExtensionHelper.transformToWindowSpaceRect; + +import android.content.ContentResolver; +import android.content.Context; +import android.database.ContentObserver; +import android.graphics.Rect; +import android.net.Uri; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.Log; + +import androidx.annotation.NonNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +class SettingsExtensionImpl extends StubExtension { + private static final String TAG = "SettingsExtension"; + + private static final String DEVICE_POSTURE = "device_posture"; + private static final String DISPLAY_FEATURES = "display_features"; + + private static final Pattern FEATURE_PATTERN = + Pattern.compile("([a-z]+)-\\[(\\d+),(\\d+),(\\d+),(\\d+)]"); + + private static final String FEATURE_TYPE_FOLD = "fold"; + private static final String FEATURE_TYPE_HINGE = "hinge"; + + private Context mContext; + private SettingsObserver mSettingsObserver; + + final class SettingsObserver extends ContentObserver { + private final Uri mDevicePostureUri = + Settings.Global.getUriFor(DEVICE_POSTURE); + private final Uri mDisplayFeaturesUri = + Settings.Global.getUriFor(DISPLAY_FEATURES); + private final ContentResolver mResolver = mContext.getContentResolver(); + private boolean mRegisteredObservers; + + + private SettingsObserver() { + super(new Handler(Looper.getMainLooper())); + } + + private void registerObserversIfNeeded() { + if (mRegisteredObservers) { + return; + } + mRegisteredObservers = true; + mResolver.registerContentObserver(mDevicePostureUri, false /* notifyForDescendents */, + this /* ContentObserver */); + mResolver.registerContentObserver(mDisplayFeaturesUri, false /* notifyForDescendents */, + this /* ContentObserver */); + } + + private void unregisterObserversIfNeeded() { + if (!mRegisteredObservers) { + return; + } + mRegisteredObservers = false; + mResolver.unregisterContentObserver(this); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + if (uri == null) { + return; + } + + if (mDevicePostureUri.equals(uri)) { + updateDevicePosture(); + return; + } + if (mDisplayFeaturesUri.equals(uri)) { + updateDisplayFeatures(); + return; + } + } + } + + SettingsExtensionImpl(Context context) { + mContext = context; + mSettingsObserver = new SettingsObserver(); + } + + private void updateDevicePosture() { + updateDeviceState(getDeviceState()); + } + + /** Update display features with values read from settings. */ + private void updateDisplayFeatures() { + for (IBinder windowToken : getWindowsListeningForLayoutChanges()) { + ExtensionWindowLayoutInfo newLayout = getWindowLayoutInfo(windowToken); + updateWindowLayout(windowToken, newLayout); + } + } + + @NonNull + @Override + public ExtensionDeviceState getDeviceState() { + ContentResolver resolver = mContext.getContentResolver(); + int posture = Settings.Global.getInt(resolver, DEVICE_POSTURE, + ExtensionDeviceState.POSTURE_UNKNOWN); + return new ExtensionDeviceState(posture); + } + + @NonNull + @Override + public ExtensionWindowLayoutInfo getWindowLayoutInfo(@NonNull IBinder windowToken) { + List displayFeatures = readDisplayFeatures(windowToken); + return new ExtensionWindowLayoutInfo(displayFeatures); + } + + private List readDisplayFeatures(IBinder windowToken) { + List features = new ArrayList(); + int displayId = getWindowDisplay(windowToken); + if (displayId != DEFAULT_DISPLAY) { + Log.w(TAG, "This sample doesn't support display features on secondary displays"); + return features; + } + + ContentResolver resolver = mContext.getContentResolver(); + final String displayFeaturesString = Settings.Global.getString(resolver, DISPLAY_FEATURES); + if (isInMultiWindow(windowToken)) { + // It is recommended not to report any display features in multi-window mode, since it + // won't be possible to synchronize the display feature positions with window movement. + return features; + } + if (TextUtils.isEmpty(displayFeaturesString)) { + return features; + } + + String[] featureStrings = displayFeaturesString.split(";"); + for (String featureString : featureStrings) { + Matcher featureMatcher = FEATURE_PATTERN.matcher(featureString); + if (!featureMatcher.matches()) { + Log.e(TAG, "Malformed feature description format: " + featureString); + continue; + } + try { + String featureType = featureMatcher.group(1); + int type; + switch (featureType) { + case FEATURE_TYPE_FOLD: + type = ExtensionDisplayFeature.TYPE_FOLD; + break; + case FEATURE_TYPE_HINGE: + type = ExtensionDisplayFeature.TYPE_HINGE; + break; + default: { + Log.e(TAG, "Malformed feature type: " + featureType); + continue; + } + } + + int left = Integer.parseInt(featureMatcher.group(2)); + int top = Integer.parseInt(featureMatcher.group(3)); + int right = Integer.parseInt(featureMatcher.group(4)); + int bottom = Integer.parseInt(featureMatcher.group(5)); + Rect featureRect = new Rect(left, top, right, bottom); + rotateRectToDisplayRotation(featureRect, displayId); + transformToWindowSpaceRect(featureRect, windowToken); + if (!featureRect.isEmpty()) { + ExtensionDisplayFeature feature = + new ExtensionDisplayFeature(featureRect, type); + features.add(feature); + } else { + Log.w(TAG, "Failed to adjust feature to window"); + } + } catch (NumberFormatException e) { + Log.e(TAG, "Malformed feature description: " + featureString); + } + } + return features; + } + + @Override + protected void onListenersChanged() { + if (mSettingsObserver == null) { + return; + } + + if (hasListeners()) { + mSettingsObserver.registerObserversIfNeeded(); + } else { + mSettingsObserver.unregisterObserversIfNeeded(); + } + } +} diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar new file mode 100644 index 000000000000..0ebbb86daf82 Binary files /dev/null and b/libs/WindowManager/Jetpack/window-extensions-release.aar differ -- cgit v1.2.3 From 933c7a612ed94756deb4d8a2a2ba0d2df0c414bc Mon Sep 17 00:00:00 2001 From: Chih-Hung Hsieh Date: Fri, 6 Mar 2020 10:16:11 -0800 Subject: Fix bugprone-use-after-move warnings Bug: 150783499 Test: WITH_TIDY=1 make Change-Id: I185cb21521676ddbc4f2b7f098611a2efc7275e6 Merged-In: I185cb21521676ddbc4f2b7f098611a2efc7275e6 (cherry picked from commit c658184d366ee08cedd9ec208ea327530b52bfad) --- libs/androidfw/ApkAssets.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index 18934fd55bad..b2b0ec2a54f8 100644 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -77,7 +77,8 @@ std::unique_ptr ApkAssets::LoadOverlay(const std::string& idmap return {}; } - return LoadImpl({} /*fd*/, loaded_idmap->OverlayApkPath(), + auto apkPath = loaded_idmap->OverlayApkPath(); + return LoadImpl({} /*fd*/, apkPath, std::move(idmap_asset), std::move(loaded_idmap), PROPERTY_OVERLAY | (system ? PROPERTY_SYSTEM : 0U)); -- cgit v1.2.3 From a74d637df14aed6b767828f0cdd19f139013db7f Mon Sep 17 00:00:00 2001 From: Kazuhiro Inaba Date: Wed, 11 Mar 2020 00:01:53 +0900 Subject: Let LayerDrawable choose the best scaling filter for readback. The check used in Readback.cpp did not take the transformation matrix into account for judging whether the copy was scaling. libs/hwui/pipeline/skia/LayerDrawable.cpp already incorporates the logic to detect non-scaling copy, so we can just let it check the condition. This version regards 90-degree rotation without size change as non-scaling and disables filters. Bug: 151126720 Bug: 150839078 Test: atest android.view.cts.PixelCopyTest Change-Id: I69e987e6a2e48299c5e579f8c218c42a724dc606 --- libs/hwui/Readback.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'libs') diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp index 84c07d7d9dff..39900e65cb8a 100644 --- a/libs/hwui/Readback.cpp +++ b/libs/hwui/Readback.cpp @@ -146,12 +146,11 @@ CopyResult Readback::copyImageInto(const sk_sp& image, Matrix4& texTran } Layer layer(mRenderThread.renderState(), nullptr, 255, SkBlendMode::kSrc); - bool disableFilter = MathUtils::areEqual(skiaSrcRect.width(), skiaDestRect.width()) && - MathUtils::areEqual(skiaSrcRect.height(), skiaDestRect.height()); - layer.setForceFilter(!disableFilter); layer.setSize(displayedWidth, displayedHeight); texTransform.copyTo(layer.getTexTransform()); layer.setImage(image); + // Scaling filter is not explicitly set here, because it is done inside copyLayerInfo + // after checking the necessity based on the src/dest rect size and the transformation. if (copyLayerInto(&layer, &skiaSrcRect, &skiaDestRect, bitmap)) { copyResult = CopyResult::Success; } -- cgit v1.2.3 From 93a9e785eaf305147ab34b31658c4758b7dfa20c Mon Sep 17 00:00:00 2001 From: Nikita Iashchenko Date: Fri, 6 Mar 2020 10:36:11 +0000 Subject: Regenerate LocaleDataTables.cpp wtih Unicode 13 update LocaleDataTables.cpp is regenerated with Unicode 13 updates from CLDR. Bug: 149845726 Test: ./tools/localedata/extract_icu_data.py $ANDROID_BUILD_TOP Test: m droid Merged-In: Id2d023723b50c1c2f73fe07f46fb9b3cae990752 Change-Id: Id2d023723b50c1c2f73fe07f46fb9b3cae990752 --- libs/androidfw/LocaleDataTables.cpp | 2717 ++++++++++++++++++----------------- 1 file changed, 1362 insertions(+), 1355 deletions(-) (limited to 'libs') diff --git a/libs/androidfw/LocaleDataTables.cpp b/libs/androidfw/LocaleDataTables.cpp index e748bd886ebc..6c6c5c9f4e22 100644 --- a/libs/androidfw/LocaleDataTables.cpp +++ b/libs/androidfw/LocaleDataTables.cpp @@ -15,1474 +15,1478 @@ const char SCRIPT_CODES[][4] = { /* 11 */ {'C', 'a', 'r', 'i'}, /* 12 */ {'C', 'h', 'a', 'm'}, /* 13 */ {'C', 'h', 'e', 'r'}, - /* 14 */ {'C', 'o', 'p', 't'}, - /* 15 */ {'C', 'p', 'r', 't'}, - /* 16 */ {'C', 'y', 'r', 'l'}, - /* 17 */ {'D', 'e', 'v', 'a'}, - /* 18 */ {'E', 'g', 'y', 'p'}, - /* 19 */ {'E', 't', 'h', 'i'}, - /* 20 */ {'G', 'e', 'o', 'r'}, - /* 21 */ {'G', 'o', 'n', 'g'}, - /* 22 */ {'G', 'o', 'n', 'm'}, - /* 23 */ {'G', 'o', 't', 'h'}, - /* 24 */ {'G', 'r', 'e', 'k'}, - /* 25 */ {'G', 'u', 'j', 'r'}, - /* 26 */ {'G', 'u', 'r', 'u'}, - /* 27 */ {'H', 'a', 'n', 's'}, - /* 28 */ {'H', 'a', 'n', 't'}, - /* 29 */ {'H', 'a', 't', 'r'}, - /* 30 */ {'H', 'e', 'b', 'r'}, - /* 31 */ {'H', 'l', 'u', 'w'}, - /* 32 */ {'H', 'm', 'n', 'g'}, - /* 33 */ {'H', 'm', 'n', 'p'}, - /* 34 */ {'I', 't', 'a', 'l'}, - /* 35 */ {'J', 'p', 'a', 'n'}, - /* 36 */ {'K', 'a', 'l', 'i'}, - /* 37 */ {'K', 'a', 'n', 'a'}, - /* 38 */ {'K', 'h', 'a', 'r'}, - /* 39 */ {'K', 'h', 'm', 'r'}, - /* 40 */ {'K', 'n', 'd', 'a'}, - /* 41 */ {'K', 'o', 'r', 'e'}, - /* 42 */ {'L', 'a', 'n', 'a'}, - /* 43 */ {'L', 'a', 'o', 'o'}, - /* 44 */ {'L', 'a', 't', 'n'}, - /* 45 */ {'L', 'e', 'p', 'c'}, - /* 46 */ {'L', 'i', 'n', 'a'}, - /* 47 */ {'L', 'i', 's', 'u'}, - /* 48 */ {'L', 'y', 'c', 'i'}, - /* 49 */ {'L', 'y', 'd', 'i'}, - /* 50 */ {'M', 'a', 'n', 'd'}, - /* 51 */ {'M', 'a', 'n', 'i'}, - /* 52 */ {'M', 'e', 'r', 'c'}, - /* 53 */ {'M', 'l', 'y', 'm'}, - /* 54 */ {'M', 'o', 'n', 'g'}, - /* 55 */ {'M', 'r', 'o', 'o'}, - /* 56 */ {'M', 'y', 'm', 'r'}, - /* 57 */ {'N', 'a', 'r', 'b'}, - /* 58 */ {'N', 'k', 'o', 'o'}, - /* 59 */ {'N', 's', 'h', 'u'}, - /* 60 */ {'O', 'g', 'a', 'm'}, - /* 61 */ {'O', 'r', 'k', 'h'}, - /* 62 */ {'O', 'r', 'y', 'a'}, - /* 63 */ {'O', 's', 'g', 'e'}, - /* 64 */ {'P', 'a', 'u', 'c'}, - /* 65 */ {'P', 'h', 'l', 'i'}, - /* 66 */ {'P', 'h', 'n', 'x'}, - /* 67 */ {'P', 'l', 'r', 'd'}, - /* 68 */ {'P', 'r', 't', 'i'}, - /* 69 */ {'R', 'u', 'n', 'r'}, - /* 70 */ {'S', 'a', 'm', 'r'}, - /* 71 */ {'S', 'a', 'r', 'b'}, - /* 72 */ {'S', 'a', 'u', 'r'}, - /* 73 */ {'S', 'g', 'n', 'w'}, - /* 74 */ {'S', 'i', 'n', 'h'}, - /* 75 */ {'S', 'o', 'g', 'd'}, - /* 76 */ {'S', 'o', 'r', 'a'}, - /* 77 */ {'S', 'o', 'y', 'o'}, - /* 78 */ {'S', 'y', 'r', 'c'}, - /* 79 */ {'T', 'a', 'l', 'e'}, - /* 80 */ {'T', 'a', 'l', 'u'}, - /* 81 */ {'T', 'a', 'm', 'l'}, - /* 82 */ {'T', 'a', 'n', 'g'}, - /* 83 */ {'T', 'a', 'v', 't'}, - /* 84 */ {'T', 'e', 'l', 'u'}, - /* 85 */ {'T', 'f', 'n', 'g'}, - /* 86 */ {'T', 'h', 'a', 'a'}, - /* 87 */ {'T', 'h', 'a', 'i'}, - /* 88 */ {'T', 'i', 'b', 't'}, - /* 89 */ {'U', 'g', 'a', 'r'}, - /* 90 */ {'V', 'a', 'i', 'i'}, - /* 91 */ {'W', 'c', 'h', 'o'}, - /* 92 */ {'X', 'p', 'e', 'o'}, - /* 93 */ {'X', 's', 'u', 'x'}, - /* 94 */ {'Y', 'i', 'i', 'i'}, - /* 95 */ {'~', '~', '~', 'A'}, - /* 96 */ {'~', '~', '~', 'B'}, + /* 14 */ {'C', 'h', 'r', 's'}, + /* 15 */ {'C', 'o', 'p', 't'}, + /* 16 */ {'C', 'p', 'r', 't'}, + /* 17 */ {'C', 'y', 'r', 'l'}, + /* 18 */ {'D', 'e', 'v', 'a'}, + /* 19 */ {'E', 'g', 'y', 'p'}, + /* 20 */ {'E', 't', 'h', 'i'}, + /* 21 */ {'G', 'e', 'o', 'r'}, + /* 22 */ {'G', 'o', 'n', 'g'}, + /* 23 */ {'G', 'o', 'n', 'm'}, + /* 24 */ {'G', 'o', 't', 'h'}, + /* 25 */ {'G', 'r', 'e', 'k'}, + /* 26 */ {'G', 'u', 'j', 'r'}, + /* 27 */ {'G', 'u', 'r', 'u'}, + /* 28 */ {'H', 'a', 'n', 's'}, + /* 29 */ {'H', 'a', 'n', 't'}, + /* 30 */ {'H', 'a', 't', 'r'}, + /* 31 */ {'H', 'e', 'b', 'r'}, + /* 32 */ {'H', 'l', 'u', 'w'}, + /* 33 */ {'H', 'm', 'n', 'g'}, + /* 34 */ {'H', 'm', 'n', 'p'}, + /* 35 */ {'I', 't', 'a', 'l'}, + /* 36 */ {'J', 'p', 'a', 'n'}, + /* 37 */ {'K', 'a', 'l', 'i'}, + /* 38 */ {'K', 'a', 'n', 'a'}, + /* 39 */ {'K', 'h', 'a', 'r'}, + /* 40 */ {'K', 'h', 'm', 'r'}, + /* 41 */ {'K', 'i', 't', 's'}, + /* 42 */ {'K', 'n', 'd', 'a'}, + /* 43 */ {'K', 'o', 'r', 'e'}, + /* 44 */ {'L', 'a', 'n', 'a'}, + /* 45 */ {'L', 'a', 'o', 'o'}, + /* 46 */ {'L', 'a', 't', 'n'}, + /* 47 */ {'L', 'e', 'p', 'c'}, + /* 48 */ {'L', 'i', 'n', 'a'}, + /* 49 */ {'L', 'i', 's', 'u'}, + /* 50 */ {'L', 'y', 'c', 'i'}, + /* 51 */ {'L', 'y', 'd', 'i'}, + /* 52 */ {'M', 'a', 'n', 'd'}, + /* 53 */ {'M', 'a', 'n', 'i'}, + /* 54 */ {'M', 'e', 'r', 'c'}, + /* 55 */ {'M', 'l', 'y', 'm'}, + /* 56 */ {'M', 'o', 'n', 'g'}, + /* 57 */ {'M', 'r', 'o', 'o'}, + /* 58 */ {'M', 'y', 'm', 'r'}, + /* 59 */ {'N', 'a', 'r', 'b'}, + /* 60 */ {'N', 'k', 'o', 'o'}, + /* 61 */ {'N', 's', 'h', 'u'}, + /* 62 */ {'O', 'g', 'a', 'm'}, + /* 63 */ {'O', 'r', 'k', 'h'}, + /* 64 */ {'O', 'r', 'y', 'a'}, + /* 65 */ {'O', 's', 'g', 'e'}, + /* 66 */ {'P', 'a', 'u', 'c'}, + /* 67 */ {'P', 'h', 'l', 'i'}, + /* 68 */ {'P', 'h', 'n', 'x'}, + /* 69 */ {'P', 'l', 'r', 'd'}, + /* 70 */ {'P', 'r', 't', 'i'}, + /* 71 */ {'R', 'u', 'n', 'r'}, + /* 72 */ {'S', 'a', 'm', 'r'}, + /* 73 */ {'S', 'a', 'r', 'b'}, + /* 74 */ {'S', 'a', 'u', 'r'}, + /* 75 */ {'S', 'g', 'n', 'w'}, + /* 76 */ {'S', 'i', 'n', 'h'}, + /* 77 */ {'S', 'o', 'g', 'd'}, + /* 78 */ {'S', 'o', 'r', 'a'}, + /* 79 */ {'S', 'o', 'y', 'o'}, + /* 80 */ {'S', 'y', 'r', 'c'}, + /* 81 */ {'T', 'a', 'l', 'e'}, + /* 82 */ {'T', 'a', 'l', 'u'}, + /* 83 */ {'T', 'a', 'm', 'l'}, + /* 84 */ {'T', 'a', 'n', 'g'}, + /* 85 */ {'T', 'a', 'v', 't'}, + /* 86 */ {'T', 'e', 'l', 'u'}, + /* 87 */ {'T', 'f', 'n', 'g'}, + /* 88 */ {'T', 'h', 'a', 'a'}, + /* 89 */ {'T', 'h', 'a', 'i'}, + /* 90 */ {'T', 'i', 'b', 't'}, + /* 91 */ {'U', 'g', 'a', 'r'}, + /* 92 */ {'V', 'a', 'i', 'i'}, + /* 93 */ {'W', 'c', 'h', 'o'}, + /* 94 */ {'X', 'p', 'e', 'o'}, + /* 95 */ {'X', 's', 'u', 'x'}, + /* 96 */ {'Y', 'i', 'i', 'i'}, + /* 97 */ {'~', '~', '~', 'A'}, + /* 98 */ {'~', '~', '~', 'B'}, }; const std::unordered_map LIKELY_SCRIPTS({ - {0x61610000u, 44u}, // aa -> Latn - {0xA0000000u, 44u}, // aai -> Latn - {0xA8000000u, 44u}, // aak -> Latn - {0xD0000000u, 44u}, // aau -> Latn - {0x61620000u, 16u}, // ab -> Cyrl - {0xA0200000u, 44u}, // abi -> Latn - {0xC0200000u, 16u}, // abq -> Cyrl - {0xC4200000u, 44u}, // abr -> Latn - {0xCC200000u, 44u}, // abt -> Latn - {0xE0200000u, 44u}, // aby -> Latn - {0x8C400000u, 44u}, // acd -> Latn - {0x90400000u, 44u}, // ace -> Latn - {0x9C400000u, 44u}, // ach -> Latn - {0x80600000u, 44u}, // ada -> Latn - {0x90600000u, 44u}, // ade -> Latn - {0xA4600000u, 44u}, // adj -> Latn - {0xBC600000u, 88u}, // adp -> Tibt - {0xE0600000u, 16u}, // ady -> Cyrl - {0xE4600000u, 44u}, // adz -> Latn + {0x61610000u, 46u}, // aa -> Latn + {0xA0000000u, 46u}, // aai -> Latn + {0xA8000000u, 46u}, // aak -> Latn + {0xD0000000u, 46u}, // aau -> Latn + {0x61620000u, 17u}, // ab -> Cyrl + {0xA0200000u, 46u}, // abi -> Latn + {0xC0200000u, 17u}, // abq -> Cyrl + {0xC4200000u, 46u}, // abr -> Latn + {0xCC200000u, 46u}, // abt -> Latn + {0xE0200000u, 46u}, // aby -> Latn + {0x8C400000u, 46u}, // acd -> Latn + {0x90400000u, 46u}, // ace -> Latn + {0x9C400000u, 46u}, // ach -> Latn + {0x80600000u, 46u}, // ada -> Latn + {0x90600000u, 46u}, // ade -> Latn + {0xA4600000u, 46u}, // adj -> Latn + {0xBC600000u, 90u}, // adp -> Tibt + {0xE0600000u, 17u}, // ady -> Cyrl + {0xE4600000u, 46u}, // adz -> Latn {0x61650000u, 4u}, // ae -> Avst {0x84800000u, 1u}, // aeb -> Arab - {0xE0800000u, 44u}, // aey -> Latn - {0x61660000u, 44u}, // af -> Latn - {0x88C00000u, 44u}, // agc -> Latn - {0x8CC00000u, 44u}, // agd -> Latn - {0x98C00000u, 44u}, // agg -> Latn - {0xB0C00000u, 44u}, // agm -> Latn - {0xB8C00000u, 44u}, // ago -> Latn - {0xC0C00000u, 44u}, // agq -> Latn - {0x80E00000u, 44u}, // aha -> Latn - {0xACE00000u, 44u}, // ahl -> Latn + {0xE0800000u, 46u}, // aey -> Latn + {0x61660000u, 46u}, // af -> Latn + {0x88C00000u, 46u}, // agc -> Latn + {0x8CC00000u, 46u}, // agd -> Latn + {0x98C00000u, 46u}, // agg -> Latn + {0xB0C00000u, 46u}, // agm -> Latn + {0xB8C00000u, 46u}, // ago -> Latn + {0xC0C00000u, 46u}, // agq -> Latn + {0x80E00000u, 46u}, // aha -> Latn + {0xACE00000u, 46u}, // ahl -> Latn {0xB8E00000u, 0u}, // aho -> Ahom - {0x99200000u, 44u}, // ajg -> Latn - {0x616B0000u, 44u}, // ak -> Latn - {0xA9400000u, 93u}, // akk -> Xsux - {0x81600000u, 44u}, // ala -> Latn - {0xA1600000u, 44u}, // ali -> Latn - {0xB5600000u, 44u}, // aln -> Latn - {0xCD600000u, 16u}, // alt -> Cyrl - {0x616D0000u, 19u}, // am -> Ethi - {0xB1800000u, 44u}, // amm -> Latn - {0xB5800000u, 44u}, // amn -> Latn - {0xB9800000u, 44u}, // amo -> Latn - {0xBD800000u, 44u}, // amp -> Latn - {0x616E0000u, 44u}, // an -> Latn - {0x89A00000u, 44u}, // anc -> Latn - {0xA9A00000u, 44u}, // ank -> Latn - {0xB5A00000u, 44u}, // ann -> Latn - {0xE1A00000u, 44u}, // any -> Latn - {0xA5C00000u, 44u}, // aoj -> Latn - {0xB1C00000u, 44u}, // aom -> Latn - {0xE5C00000u, 44u}, // aoz -> Latn + {0x99200000u, 46u}, // ajg -> Latn + {0x616B0000u, 46u}, // ak -> Latn + {0xA9400000u, 95u}, // akk -> Xsux + {0x81600000u, 46u}, // ala -> Latn + {0xA1600000u, 46u}, // ali -> Latn + {0xB5600000u, 46u}, // aln -> Latn + {0xCD600000u, 17u}, // alt -> Cyrl + {0x616D0000u, 20u}, // am -> Ethi + {0xB1800000u, 46u}, // amm -> Latn + {0xB5800000u, 46u}, // amn -> Latn + {0xB9800000u, 46u}, // amo -> Latn + {0xBD800000u, 46u}, // amp -> Latn + {0x616E0000u, 46u}, // an -> Latn + {0x89A00000u, 46u}, // anc -> Latn + {0xA9A00000u, 46u}, // ank -> Latn + {0xB5A00000u, 46u}, // ann -> Latn + {0xE1A00000u, 46u}, // any -> Latn + {0xA5C00000u, 46u}, // aoj -> Latn + {0xB1C00000u, 46u}, // aom -> Latn + {0xE5C00000u, 46u}, // aoz -> Latn {0x89E00000u, 1u}, // apc -> Arab {0x8DE00000u, 1u}, // apd -> Arab - {0x91E00000u, 44u}, // ape -> Latn - {0xC5E00000u, 44u}, // apr -> Latn - {0xC9E00000u, 44u}, // aps -> Latn - {0xE5E00000u, 44u}, // apz -> Latn + {0x91E00000u, 46u}, // ape -> Latn + {0xC5E00000u, 46u}, // apr -> Latn + {0xC9E00000u, 46u}, // aps -> Latn + {0xE5E00000u, 46u}, // apz -> Latn {0x61720000u, 1u}, // ar -> Arab - {0x61725842u, 96u}, // ar-XB -> ~~~B + {0x61725842u, 98u}, // ar-XB -> ~~~B {0x8A200000u, 2u}, // arc -> Armi - {0x9E200000u, 44u}, // arh -> Latn - {0xB6200000u, 44u}, // arn -> Latn - {0xBA200000u, 44u}, // aro -> Latn + {0x9E200000u, 46u}, // arh -> Latn + {0xB6200000u, 46u}, // arn -> Latn + {0xBA200000u, 46u}, // aro -> Latn {0xC2200000u, 1u}, // arq -> Arab {0xCA200000u, 1u}, // ars -> Arab {0xE2200000u, 1u}, // ary -> Arab {0xE6200000u, 1u}, // arz -> Arab {0x61730000u, 7u}, // as -> Beng - {0x82400000u, 44u}, // asa -> Latn - {0x92400000u, 73u}, // ase -> Sgnw - {0x9A400000u, 44u}, // asg -> Latn - {0xBA400000u, 44u}, // aso -> Latn - {0xCE400000u, 44u}, // ast -> Latn - {0x82600000u, 44u}, // ata -> Latn - {0x9A600000u, 44u}, // atg -> Latn - {0xA6600000u, 44u}, // atj -> Latn - {0xE2800000u, 44u}, // auy -> Latn - {0x61760000u, 16u}, // av -> Cyrl + {0x82400000u, 46u}, // asa -> Latn + {0x92400000u, 75u}, // ase -> Sgnw + {0x9A400000u, 46u}, // asg -> Latn + {0xBA400000u, 46u}, // aso -> Latn + {0xCE400000u, 46u}, // ast -> Latn + {0x82600000u, 46u}, // ata -> Latn + {0x9A600000u, 46u}, // atg -> Latn + {0xA6600000u, 46u}, // atj -> Latn + {0xE2800000u, 46u}, // auy -> Latn + {0x61760000u, 17u}, // av -> Cyrl {0xAEA00000u, 1u}, // avl -> Arab - {0xB6A00000u, 44u}, // avn -> Latn - {0xCEA00000u, 44u}, // avt -> Latn - {0xD2A00000u, 44u}, // avu -> Latn - {0x82C00000u, 17u}, // awa -> Deva - {0x86C00000u, 44u}, // awb -> Latn - {0xBAC00000u, 44u}, // awo -> Latn - {0xDEC00000u, 44u}, // awx -> Latn - {0x61790000u, 44u}, // ay -> Latn - {0x87000000u, 44u}, // ayb -> Latn - {0x617A0000u, 44u}, // az -> Latn + {0xB6A00000u, 46u}, // avn -> Latn + {0xCEA00000u, 46u}, // avt -> Latn + {0xD2A00000u, 46u}, // avu -> Latn + {0x82C00000u, 18u}, // awa -> Deva + {0x86C00000u, 46u}, // awb -> Latn + {0xBAC00000u, 46u}, // awo -> Latn + {0xDEC00000u, 46u}, // awx -> Latn + {0x61790000u, 46u}, // ay -> Latn + {0x87000000u, 46u}, // ayb -> Latn + {0x617A0000u, 46u}, // az -> Latn {0x617A4951u, 1u}, // az-IQ -> Arab {0x617A4952u, 1u}, // az-IR -> Arab - {0x617A5255u, 16u}, // az-RU -> Cyrl - {0x62610000u, 16u}, // ba -> Cyrl + {0x617A5255u, 17u}, // az-RU -> Cyrl + {0x62610000u, 17u}, // ba -> Cyrl {0xAC010000u, 1u}, // bal -> Arab - {0xB4010000u, 44u}, // ban -> Latn - {0xBC010000u, 17u}, // bap -> Deva - {0xC4010000u, 44u}, // bar -> Latn - {0xC8010000u, 44u}, // bas -> Latn - {0xD4010000u, 44u}, // bav -> Latn + {0xB4010000u, 46u}, // ban -> Latn + {0xBC010000u, 18u}, // bap -> Deva + {0xC4010000u, 46u}, // bar -> Latn + {0xC8010000u, 46u}, // bas -> Latn + {0xD4010000u, 46u}, // bav -> Latn {0xDC010000u, 5u}, // bax -> Bamu - {0x80210000u, 44u}, // bba -> Latn - {0x84210000u, 44u}, // bbb -> Latn - {0x88210000u, 44u}, // bbc -> Latn - {0x8C210000u, 44u}, // bbd -> Latn - {0xA4210000u, 44u}, // bbj -> Latn - {0xBC210000u, 44u}, // bbp -> Latn - {0xC4210000u, 44u}, // bbr -> Latn - {0x94410000u, 44u}, // bcf -> Latn - {0x9C410000u, 44u}, // bch -> Latn - {0xA0410000u, 44u}, // bci -> Latn - {0xB0410000u, 44u}, // bcm -> Latn - {0xB4410000u, 44u}, // bcn -> Latn - {0xB8410000u, 44u}, // bco -> Latn - {0xC0410000u, 19u}, // bcq -> Ethi - {0xD0410000u, 44u}, // bcu -> Latn - {0x8C610000u, 44u}, // bdd -> Latn - {0x62650000u, 16u}, // be -> Cyrl - {0x94810000u, 44u}, // bef -> Latn - {0x9C810000u, 44u}, // beh -> Latn + {0x80210000u, 46u}, // bba -> Latn + {0x84210000u, 46u}, // bbb -> Latn + {0x88210000u, 46u}, // bbc -> Latn + {0x8C210000u, 46u}, // bbd -> Latn + {0xA4210000u, 46u}, // bbj -> Latn + {0xBC210000u, 46u}, // bbp -> Latn + {0xC4210000u, 46u}, // bbr -> Latn + {0x94410000u, 46u}, // bcf -> Latn + {0x9C410000u, 46u}, // bch -> Latn + {0xA0410000u, 46u}, // bci -> Latn + {0xB0410000u, 46u}, // bcm -> Latn + {0xB4410000u, 46u}, // bcn -> Latn + {0xB8410000u, 46u}, // bco -> Latn + {0xC0410000u, 20u}, // bcq -> Ethi + {0xD0410000u, 46u}, // bcu -> Latn + {0x8C610000u, 46u}, // bdd -> Latn + {0x62650000u, 17u}, // be -> Cyrl + {0x94810000u, 46u}, // bef -> Latn + {0x9C810000u, 46u}, // beh -> Latn {0xA4810000u, 1u}, // bej -> Arab - {0xB0810000u, 44u}, // bem -> Latn - {0xCC810000u, 44u}, // bet -> Latn - {0xD8810000u, 44u}, // bew -> Latn - {0xDC810000u, 44u}, // bex -> Latn - {0xE4810000u, 44u}, // bez -> Latn - {0x8CA10000u, 44u}, // bfd -> Latn - {0xC0A10000u, 81u}, // bfq -> Taml + {0xB0810000u, 46u}, // bem -> Latn + {0xCC810000u, 46u}, // bet -> Latn + {0xD8810000u, 46u}, // bew -> Latn + {0xDC810000u, 46u}, // bex -> Latn + {0xE4810000u, 46u}, // bez -> Latn + {0x8CA10000u, 46u}, // bfd -> Latn + {0xC0A10000u, 83u}, // bfq -> Taml {0xCCA10000u, 1u}, // bft -> Arab - {0xE0A10000u, 17u}, // bfy -> Deva - {0x62670000u, 16u}, // bg -> Cyrl - {0x88C10000u, 17u}, // bgc -> Deva + {0xE0A10000u, 18u}, // bfy -> Deva + {0x62670000u, 17u}, // bg -> Cyrl + {0x88C10000u, 18u}, // bgc -> Deva {0xB4C10000u, 1u}, // bgn -> Arab - {0xDCC10000u, 24u}, // bgx -> Grek - {0x84E10000u, 17u}, // bhb -> Deva - {0x98E10000u, 44u}, // bhg -> Latn - {0xA0E10000u, 17u}, // bhi -> Deva - {0xACE10000u, 44u}, // bhl -> Latn - {0xB8E10000u, 17u}, // bho -> Deva - {0xE0E10000u, 44u}, // bhy -> Latn - {0x62690000u, 44u}, // bi -> Latn - {0x85010000u, 44u}, // bib -> Latn - {0x99010000u, 44u}, // big -> Latn - {0xA9010000u, 44u}, // bik -> Latn - {0xB1010000u, 44u}, // bim -> Latn - {0xB5010000u, 44u}, // bin -> Latn - {0xB9010000u, 44u}, // bio -> Latn - {0xC1010000u, 44u}, // biq -> Latn - {0x9D210000u, 44u}, // bjh -> Latn - {0xA1210000u, 19u}, // bji -> Ethi - {0xA5210000u, 17u}, // bjj -> Deva - {0xB5210000u, 44u}, // bjn -> Latn - {0xB9210000u, 44u}, // bjo -> Latn - {0xC5210000u, 44u}, // bjr -> Latn - {0xCD210000u, 44u}, // bjt -> Latn - {0xE5210000u, 44u}, // bjz -> Latn - {0x89410000u, 44u}, // bkc -> Latn - {0xB1410000u, 44u}, // bkm -> Latn - {0xC1410000u, 44u}, // bkq -> Latn - {0xD1410000u, 44u}, // bku -> Latn - {0xD5410000u, 44u}, // bkv -> Latn - {0xCD610000u, 83u}, // blt -> Tavt - {0x626D0000u, 44u}, // bm -> Latn - {0x9D810000u, 44u}, // bmh -> Latn - {0xA9810000u, 44u}, // bmk -> Latn - {0xC1810000u, 44u}, // bmq -> Latn - {0xD1810000u, 44u}, // bmu -> Latn + {0xDCC10000u, 25u}, // bgx -> Grek + {0x84E10000u, 18u}, // bhb -> Deva + {0x98E10000u, 46u}, // bhg -> Latn + {0xA0E10000u, 18u}, // bhi -> Deva + {0xACE10000u, 46u}, // bhl -> Latn + {0xB8E10000u, 18u}, // bho -> Deva + {0xE0E10000u, 46u}, // bhy -> Latn + {0x62690000u, 46u}, // bi -> Latn + {0x85010000u, 46u}, // bib -> Latn + {0x99010000u, 46u}, // big -> Latn + {0xA9010000u, 46u}, // bik -> Latn + {0xB1010000u, 46u}, // bim -> Latn + {0xB5010000u, 46u}, // bin -> Latn + {0xB9010000u, 46u}, // bio -> Latn + {0xC1010000u, 46u}, // biq -> Latn + {0x9D210000u, 46u}, // bjh -> Latn + {0xA1210000u, 20u}, // bji -> Ethi + {0xA5210000u, 18u}, // bjj -> Deva + {0xB5210000u, 46u}, // bjn -> Latn + {0xB9210000u, 46u}, // bjo -> Latn + {0xC5210000u, 46u}, // bjr -> Latn + {0xCD210000u, 46u}, // bjt -> Latn + {0xE5210000u, 46u}, // bjz -> Latn + {0x89410000u, 46u}, // bkc -> Latn + {0xB1410000u, 46u}, // bkm -> Latn + {0xC1410000u, 46u}, // bkq -> Latn + {0xD1410000u, 46u}, // bku -> Latn + {0xD5410000u, 46u}, // bkv -> Latn + {0xCD610000u, 85u}, // blt -> Tavt + {0x626D0000u, 46u}, // bm -> Latn + {0x9D810000u, 46u}, // bmh -> Latn + {0xA9810000u, 46u}, // bmk -> Latn + {0xC1810000u, 46u}, // bmq -> Latn + {0xD1810000u, 46u}, // bmu -> Latn {0x626E0000u, 7u}, // bn -> Beng - {0x99A10000u, 44u}, // bng -> Latn - {0xB1A10000u, 44u}, // bnm -> Latn - {0xBDA10000u, 44u}, // bnp -> Latn - {0x626F0000u, 88u}, // bo -> Tibt - {0xA5C10000u, 44u}, // boj -> Latn - {0xB1C10000u, 44u}, // bom -> Latn - {0xB5C10000u, 44u}, // bon -> Latn + {0x99A10000u, 46u}, // bng -> Latn + {0xB1A10000u, 46u}, // bnm -> Latn + {0xBDA10000u, 46u}, // bnp -> Latn + {0x626F0000u, 90u}, // bo -> Tibt + {0xA5C10000u, 46u}, // boj -> Latn + {0xB1C10000u, 46u}, // bom -> Latn + {0xB5C10000u, 46u}, // bon -> Latn {0xE1E10000u, 7u}, // bpy -> Beng - {0x8A010000u, 44u}, // bqc -> Latn + {0x8A010000u, 46u}, // bqc -> Latn {0xA2010000u, 1u}, // bqi -> Arab - {0xBE010000u, 44u}, // bqp -> Latn - {0xD6010000u, 44u}, // bqv -> Latn - {0x62720000u, 44u}, // br -> Latn - {0x82210000u, 17u}, // bra -> Deva + {0xBE010000u, 46u}, // bqp -> Latn + {0xD6010000u, 46u}, // bqv -> Latn + {0x62720000u, 46u}, // br -> Latn + {0x82210000u, 18u}, // bra -> Deva {0x9E210000u, 1u}, // brh -> Arab - {0xDE210000u, 17u}, // brx -> Deva - {0xE6210000u, 44u}, // brz -> Latn - {0x62730000u, 44u}, // bs -> Latn - {0xA6410000u, 44u}, // bsj -> Latn + {0xDE210000u, 18u}, // brx -> Deva + {0xE6210000u, 46u}, // brz -> Latn + {0x62730000u, 46u}, // bs -> Latn + {0xA6410000u, 46u}, // bsj -> Latn {0xC2410000u, 6u}, // bsq -> Bass - {0xCA410000u, 44u}, // bss -> Latn - {0xCE410000u, 19u}, // bst -> Ethi - {0xBA610000u, 44u}, // bto -> Latn - {0xCE610000u, 44u}, // btt -> Latn - {0xD6610000u, 17u}, // btv -> Deva - {0x82810000u, 16u}, // bua -> Cyrl - {0x8A810000u, 44u}, // buc -> Latn - {0x8E810000u, 44u}, // bud -> Latn - {0x9A810000u, 44u}, // bug -> Latn - {0xAA810000u, 44u}, // buk -> Latn - {0xB2810000u, 44u}, // bum -> Latn - {0xBA810000u, 44u}, // buo -> Latn - {0xCA810000u, 44u}, // bus -> Latn - {0xD2810000u, 44u}, // buu -> Latn - {0x86A10000u, 44u}, // bvb -> Latn - {0x8EC10000u, 44u}, // bwd -> Latn - {0xC6C10000u, 44u}, // bwr -> Latn - {0x9EE10000u, 44u}, // bxh -> Latn - {0x93010000u, 44u}, // bye -> Latn - {0xB7010000u, 19u}, // byn -> Ethi - {0xC7010000u, 44u}, // byr -> Latn - {0xCB010000u, 44u}, // bys -> Latn - {0xD7010000u, 44u}, // byv -> Latn - {0xDF010000u, 44u}, // byx -> Latn - {0x83210000u, 44u}, // bza -> Latn - {0x93210000u, 44u}, // bze -> Latn - {0x97210000u, 44u}, // bzf -> Latn - {0x9F210000u, 44u}, // bzh -> Latn - {0xDB210000u, 44u}, // bzw -> Latn - {0x63610000u, 44u}, // ca -> Latn - {0xB4020000u, 44u}, // can -> Latn - {0xA4220000u, 44u}, // cbj -> Latn - {0x9C420000u, 44u}, // cch -> Latn + {0xCA410000u, 46u}, // bss -> Latn + {0xCE410000u, 20u}, // bst -> Ethi + {0xBA610000u, 46u}, // bto -> Latn + {0xCE610000u, 46u}, // btt -> Latn + {0xD6610000u, 18u}, // btv -> Deva + {0x82810000u, 17u}, // bua -> Cyrl + {0x8A810000u, 46u}, // buc -> Latn + {0x8E810000u, 46u}, // bud -> Latn + {0x9A810000u, 46u}, // bug -> Latn + {0xAA810000u, 46u}, // buk -> Latn + {0xB2810000u, 46u}, // bum -> Latn + {0xBA810000u, 46u}, // buo -> Latn + {0xCA810000u, 46u}, // bus -> Latn + {0xD2810000u, 46u}, // buu -> Latn + {0x86A10000u, 46u}, // bvb -> Latn + {0x8EC10000u, 46u}, // bwd -> Latn + {0xC6C10000u, 46u}, // bwr -> Latn + {0x9EE10000u, 46u}, // bxh -> Latn + {0x93010000u, 46u}, // bye -> Latn + {0xB7010000u, 20u}, // byn -> Ethi + {0xC7010000u, 46u}, // byr -> Latn + {0xCB010000u, 46u}, // bys -> Latn + {0xD7010000u, 46u}, // byv -> Latn + {0xDF010000u, 46u}, // byx -> Latn + {0x83210000u, 46u}, // bza -> Latn + {0x93210000u, 46u}, // bze -> Latn + {0x97210000u, 46u}, // bzf -> Latn + {0x9F210000u, 46u}, // bzh -> Latn + {0xDB210000u, 46u}, // bzw -> Latn + {0x63610000u, 46u}, // ca -> Latn + {0xB4020000u, 46u}, // can -> Latn + {0xA4220000u, 46u}, // cbj -> Latn + {0x9C420000u, 46u}, // cch -> Latn {0xBC420000u, 9u}, // ccp -> Cakm - {0x63650000u, 16u}, // ce -> Cyrl - {0x84820000u, 44u}, // ceb -> Latn - {0x80A20000u, 44u}, // cfa -> Latn - {0x98C20000u, 44u}, // cgg -> Latn - {0x63680000u, 44u}, // ch -> Latn - {0xA8E20000u, 44u}, // chk -> Latn - {0xB0E20000u, 16u}, // chm -> Cyrl - {0xB8E20000u, 44u}, // cho -> Latn - {0xBCE20000u, 44u}, // chp -> Latn + {0x63650000u, 17u}, // ce -> Cyrl + {0x84820000u, 46u}, // ceb -> Latn + {0x80A20000u, 46u}, // cfa -> Latn + {0x98C20000u, 46u}, // cgg -> Latn + {0x63680000u, 46u}, // ch -> Latn + {0xA8E20000u, 46u}, // chk -> Latn + {0xB0E20000u, 17u}, // chm -> Cyrl + {0xB8E20000u, 46u}, // cho -> Latn + {0xBCE20000u, 46u}, // chp -> Latn {0xC4E20000u, 13u}, // chr -> Cher - {0x89020000u, 44u}, // cic -> Latn + {0x89020000u, 46u}, // cic -> Latn {0x81220000u, 1u}, // cja -> Arab {0xB1220000u, 12u}, // cjm -> Cham - {0xD5220000u, 44u}, // cjv -> Latn + {0xD5220000u, 46u}, // cjv -> Latn {0x85420000u, 1u}, // ckb -> Arab - {0xAD420000u, 44u}, // ckl -> Latn - {0xB9420000u, 44u}, // cko -> Latn - {0xE1420000u, 44u}, // cky -> Latn - {0x81620000u, 44u}, // cla -> Latn - {0x91820000u, 44u}, // cme -> Latn - {0x99820000u, 77u}, // cmg -> Soyo - {0x636F0000u, 44u}, // co -> Latn - {0xBDC20000u, 14u}, // cop -> Copt - {0xC9E20000u, 44u}, // cps -> Latn + {0xAD420000u, 46u}, // ckl -> Latn + {0xB9420000u, 46u}, // cko -> Latn + {0xE1420000u, 46u}, // cky -> Latn + {0x81620000u, 46u}, // cla -> Latn + {0x91820000u, 46u}, // cme -> Latn + {0x99820000u, 79u}, // cmg -> Soyo + {0x636F0000u, 46u}, // co -> Latn + {0xBDC20000u, 15u}, // cop -> Copt + {0xC9E20000u, 46u}, // cps -> Latn {0x63720000u, 10u}, // cr -> Cans - {0x9E220000u, 16u}, // crh -> Cyrl + {0x9E220000u, 17u}, // crh -> Cyrl {0xA6220000u, 10u}, // crj -> Cans {0xAA220000u, 10u}, // crk -> Cans {0xAE220000u, 10u}, // crl -> Cans {0xB2220000u, 10u}, // crm -> Cans - {0xCA220000u, 44u}, // crs -> Latn - {0x63730000u, 44u}, // cs -> Latn - {0x86420000u, 44u}, // csb -> Latn + {0xCA220000u, 46u}, // crs -> Latn + {0x63730000u, 46u}, // cs -> Latn + {0x86420000u, 46u}, // csb -> Latn {0xDA420000u, 10u}, // csw -> Cans - {0x8E620000u, 64u}, // ctd -> Pauc - {0x63750000u, 16u}, // cu -> Cyrl - {0x63760000u, 16u}, // cv -> Cyrl - {0x63790000u, 44u}, // cy -> Latn - {0x64610000u, 44u}, // da -> Latn - {0x8C030000u, 44u}, // dad -> Latn - {0x94030000u, 44u}, // daf -> Latn - {0x98030000u, 44u}, // dag -> Latn - {0x9C030000u, 44u}, // dah -> Latn - {0xA8030000u, 44u}, // dak -> Latn - {0xC4030000u, 16u}, // dar -> Cyrl - {0xD4030000u, 44u}, // dav -> Latn - {0x8C230000u, 44u}, // dbd -> Latn - {0xC0230000u, 44u}, // dbq -> Latn + {0x8E620000u, 66u}, // ctd -> Pauc + {0x63750000u, 17u}, // cu -> Cyrl + {0x63760000u, 17u}, // cv -> Cyrl + {0x63790000u, 46u}, // cy -> Latn + {0x64610000u, 46u}, // da -> Latn + {0x8C030000u, 46u}, // dad -> Latn + {0x94030000u, 46u}, // daf -> Latn + {0x98030000u, 46u}, // dag -> Latn + {0x9C030000u, 46u}, // dah -> Latn + {0xA8030000u, 46u}, // dak -> Latn + {0xC4030000u, 17u}, // dar -> Cyrl + {0xD4030000u, 46u}, // dav -> Latn + {0x8C230000u, 46u}, // dbd -> Latn + {0xC0230000u, 46u}, // dbq -> Latn {0x88430000u, 1u}, // dcc -> Arab - {0xB4630000u, 44u}, // ddn -> Latn - {0x64650000u, 44u}, // de -> Latn - {0x8C830000u, 44u}, // ded -> Latn - {0xB4830000u, 44u}, // den -> Latn - {0x80C30000u, 44u}, // dga -> Latn - {0x9CC30000u, 44u}, // dgh -> Latn - {0xA0C30000u, 44u}, // dgi -> Latn + {0xB4630000u, 46u}, // ddn -> Latn + {0x64650000u, 46u}, // de -> Latn + {0x8C830000u, 46u}, // ded -> Latn + {0xB4830000u, 46u}, // den -> Latn + {0x80C30000u, 46u}, // dga -> Latn + {0x9CC30000u, 46u}, // dgh -> Latn + {0xA0C30000u, 46u}, // dgi -> Latn {0xACC30000u, 1u}, // dgl -> Arab - {0xC4C30000u, 44u}, // dgr -> Latn - {0xE4C30000u, 44u}, // dgz -> Latn - {0x81030000u, 44u}, // dia -> Latn - {0x91230000u, 44u}, // dje -> Latn - {0xA5A30000u, 44u}, // dnj -> Latn - {0x85C30000u, 44u}, // dob -> Latn + {0xC4C30000u, 46u}, // dgr -> Latn + {0xE4C30000u, 46u}, // dgz -> Latn + {0x81030000u, 46u}, // dia -> Latn + {0x91230000u, 46u}, // dje -> Latn + {0xA5A30000u, 46u}, // dnj -> Latn + {0x85C30000u, 46u}, // dob -> Latn {0xA1C30000u, 1u}, // doi -> Arab - {0xBDC30000u, 44u}, // dop -> Latn - {0xD9C30000u, 44u}, // dow -> Latn - {0x9E230000u, 54u}, // drh -> Mong - {0xA2230000u, 44u}, // dri -> Latn - {0xCA230000u, 19u}, // drs -> Ethi - {0x86430000u, 44u}, // dsb -> Latn - {0xB2630000u, 44u}, // dtm -> Latn - {0xBE630000u, 44u}, // dtp -> Latn - {0xCA630000u, 44u}, // dts -> Latn - {0xE2630000u, 17u}, // dty -> Deva - {0x82830000u, 44u}, // dua -> Latn - {0x8A830000u, 44u}, // duc -> Latn - {0x8E830000u, 44u}, // dud -> Latn - {0x9A830000u, 44u}, // dug -> Latn - {0x64760000u, 86u}, // dv -> Thaa - {0x82A30000u, 44u}, // dva -> Latn - {0xDAC30000u, 44u}, // dww -> Latn - {0xBB030000u, 44u}, // dyo -> Latn - {0xD3030000u, 44u}, // dyu -> Latn - {0x647A0000u, 88u}, // dz -> Tibt - {0x9B230000u, 44u}, // dzg -> Latn - {0xD0240000u, 44u}, // ebu -> Latn - {0x65650000u, 44u}, // ee -> Latn - {0xA0A40000u, 44u}, // efi -> Latn - {0xACC40000u, 44u}, // egl -> Latn - {0xE0C40000u, 18u}, // egy -> Egyp - {0x81440000u, 44u}, // eka -> Latn - {0xE1440000u, 36u}, // eky -> Kali - {0x656C0000u, 24u}, // el -> Grek - {0x81840000u, 44u}, // ema -> Latn - {0xA1840000u, 44u}, // emi -> Latn - {0x656E0000u, 44u}, // en -> Latn - {0x656E5841u, 95u}, // en-XA -> ~~~A - {0xB5A40000u, 44u}, // enn -> Latn - {0xC1A40000u, 44u}, // enq -> Latn - {0x656F0000u, 44u}, // eo -> Latn - {0xA2240000u, 44u}, // eri -> Latn - {0x65730000u, 44u}, // es -> Latn - {0x9A440000u, 22u}, // esg -> Gonm - {0xD2440000u, 44u}, // esu -> Latn - {0x65740000u, 44u}, // et -> Latn - {0xC6640000u, 44u}, // etr -> Latn - {0xCE640000u, 34u}, // ett -> Ital - {0xD2640000u, 44u}, // etu -> Latn - {0xDE640000u, 44u}, // etx -> Latn - {0x65750000u, 44u}, // eu -> Latn - {0xBAC40000u, 44u}, // ewo -> Latn - {0xCEE40000u, 44u}, // ext -> Latn + {0xBDC30000u, 46u}, // dop -> Latn + {0xD9C30000u, 46u}, // dow -> Latn + {0x9E230000u, 56u}, // drh -> Mong + {0xA2230000u, 46u}, // dri -> Latn + {0xCA230000u, 20u}, // drs -> Ethi + {0x86430000u, 46u}, // dsb -> Latn + {0xB2630000u, 46u}, // dtm -> Latn + {0xBE630000u, 46u}, // dtp -> Latn + {0xCA630000u, 46u}, // dts -> Latn + {0xE2630000u, 18u}, // dty -> Deva + {0x82830000u, 46u}, // dua -> Latn + {0x8A830000u, 46u}, // duc -> Latn + {0x8E830000u, 46u}, // dud -> Latn + {0x9A830000u, 46u}, // dug -> Latn + {0x64760000u, 88u}, // dv -> Thaa + {0x82A30000u, 46u}, // dva -> Latn + {0xDAC30000u, 46u}, // dww -> Latn + {0xBB030000u, 46u}, // dyo -> Latn + {0xD3030000u, 46u}, // dyu -> Latn + {0x647A0000u, 90u}, // dz -> Tibt + {0x9B230000u, 46u}, // dzg -> Latn + {0xD0240000u, 46u}, // ebu -> Latn + {0x65650000u, 46u}, // ee -> Latn + {0xA0A40000u, 46u}, // efi -> Latn + {0xACC40000u, 46u}, // egl -> Latn + {0xE0C40000u, 19u}, // egy -> Egyp + {0x81440000u, 46u}, // eka -> Latn + {0xE1440000u, 37u}, // eky -> Kali + {0x656C0000u, 25u}, // el -> Grek + {0x81840000u, 46u}, // ema -> Latn + {0xA1840000u, 46u}, // emi -> Latn + {0x656E0000u, 46u}, // en -> Latn + {0x656E5841u, 97u}, // en-XA -> ~~~A + {0xB5A40000u, 46u}, // enn -> Latn + {0xC1A40000u, 46u}, // enq -> Latn + {0x656F0000u, 46u}, // eo -> Latn + {0xA2240000u, 46u}, // eri -> Latn + {0x65730000u, 46u}, // es -> Latn + {0x9A440000u, 23u}, // esg -> Gonm + {0xD2440000u, 46u}, // esu -> Latn + {0x65740000u, 46u}, // et -> Latn + {0xC6640000u, 46u}, // etr -> Latn + {0xCE640000u, 35u}, // ett -> Ital + {0xD2640000u, 46u}, // etu -> Latn + {0xDE640000u, 46u}, // etx -> Latn + {0x65750000u, 46u}, // eu -> Latn + {0xBAC40000u, 46u}, // ewo -> Latn + {0xCEE40000u, 46u}, // ext -> Latn {0x66610000u, 1u}, // fa -> Arab - {0x80050000u, 44u}, // faa -> Latn - {0x84050000u, 44u}, // fab -> Latn - {0x98050000u, 44u}, // fag -> Latn - {0xA0050000u, 44u}, // fai -> Latn - {0xB4050000u, 44u}, // fan -> Latn - {0x66660000u, 44u}, // ff -> Latn - {0xA0A50000u, 44u}, // ffi -> Latn - {0xB0A50000u, 44u}, // ffm -> Latn - {0x66690000u, 44u}, // fi -> Latn + {0x80050000u, 46u}, // faa -> Latn + {0x84050000u, 46u}, // fab -> Latn + {0x98050000u, 46u}, // fag -> Latn + {0xA0050000u, 46u}, // fai -> Latn + {0xB4050000u, 46u}, // fan -> Latn + {0x66660000u, 46u}, // ff -> Latn + {0xA0A50000u, 46u}, // ffi -> Latn + {0xB0A50000u, 46u}, // ffm -> Latn + {0x66690000u, 46u}, // fi -> Latn {0x81050000u, 1u}, // fia -> Arab - {0xAD050000u, 44u}, // fil -> Latn - {0xCD050000u, 44u}, // fit -> Latn - {0x666A0000u, 44u}, // fj -> Latn - {0xC5650000u, 44u}, // flr -> Latn - {0xBD850000u, 44u}, // fmp -> Latn - {0x666F0000u, 44u}, // fo -> Latn - {0x8DC50000u, 44u}, // fod -> Latn - {0xB5C50000u, 44u}, // fon -> Latn - {0xC5C50000u, 44u}, // for -> Latn - {0x91E50000u, 44u}, // fpe -> Latn - {0xCA050000u, 44u}, // fqs -> Latn - {0x66720000u, 44u}, // fr -> Latn - {0x8A250000u, 44u}, // frc -> Latn - {0xBE250000u, 44u}, // frp -> Latn - {0xC6250000u, 44u}, // frr -> Latn - {0xCA250000u, 44u}, // frs -> Latn + {0xAD050000u, 46u}, // fil -> Latn + {0xCD050000u, 46u}, // fit -> Latn + {0x666A0000u, 46u}, // fj -> Latn + {0xC5650000u, 46u}, // flr -> Latn + {0xBD850000u, 46u}, // fmp -> Latn + {0x666F0000u, 46u}, // fo -> Latn + {0x8DC50000u, 46u}, // fod -> Latn + {0xB5C50000u, 46u}, // fon -> Latn + {0xC5C50000u, 46u}, // for -> Latn + {0x91E50000u, 46u}, // fpe -> Latn + {0xCA050000u, 46u}, // fqs -> Latn + {0x66720000u, 46u}, // fr -> Latn + {0x8A250000u, 46u}, // frc -> Latn + {0xBE250000u, 46u}, // frp -> Latn + {0xC6250000u, 46u}, // frr -> Latn + {0xCA250000u, 46u}, // frs -> Latn {0x86850000u, 1u}, // fub -> Arab - {0x8E850000u, 44u}, // fud -> Latn - {0x92850000u, 44u}, // fue -> Latn - {0x96850000u, 44u}, // fuf -> Latn - {0x9E850000u, 44u}, // fuh -> Latn - {0xC2850000u, 44u}, // fuq -> Latn - {0xC6850000u, 44u}, // fur -> Latn - {0xD6850000u, 44u}, // fuv -> Latn - {0xE2850000u, 44u}, // fuy -> Latn - {0xC6A50000u, 44u}, // fvr -> Latn - {0x66790000u, 44u}, // fy -> Latn - {0x67610000u, 44u}, // ga -> Latn - {0x80060000u, 44u}, // gaa -> Latn - {0x94060000u, 44u}, // gaf -> Latn - {0x98060000u, 44u}, // gag -> Latn - {0x9C060000u, 44u}, // gah -> Latn - {0xA4060000u, 44u}, // gaj -> Latn - {0xB0060000u, 44u}, // gam -> Latn - {0xB4060000u, 27u}, // gan -> Hans - {0xD8060000u, 44u}, // gaw -> Latn - {0xE0060000u, 44u}, // gay -> Latn - {0x80260000u, 44u}, // gba -> Latn - {0x94260000u, 44u}, // gbf -> Latn - {0xB0260000u, 17u}, // gbm -> Deva - {0xE0260000u, 44u}, // gby -> Latn + {0x8E850000u, 46u}, // fud -> Latn + {0x92850000u, 46u}, // fue -> Latn + {0x96850000u, 46u}, // fuf -> Latn + {0x9E850000u, 46u}, // fuh -> Latn + {0xC2850000u, 46u}, // fuq -> Latn + {0xC6850000u, 46u}, // fur -> Latn + {0xD6850000u, 46u}, // fuv -> Latn + {0xE2850000u, 46u}, // fuy -> Latn + {0xC6A50000u, 46u}, // fvr -> Latn + {0x66790000u, 46u}, // fy -> Latn + {0x67610000u, 46u}, // ga -> Latn + {0x80060000u, 46u}, // gaa -> Latn + {0x94060000u, 46u}, // gaf -> Latn + {0x98060000u, 46u}, // gag -> Latn + {0x9C060000u, 46u}, // gah -> Latn + {0xA4060000u, 46u}, // gaj -> Latn + {0xB0060000u, 46u}, // gam -> Latn + {0xB4060000u, 28u}, // gan -> Hans + {0xD8060000u, 46u}, // gaw -> Latn + {0xE0060000u, 46u}, // gay -> Latn + {0x80260000u, 46u}, // gba -> Latn + {0x94260000u, 46u}, // gbf -> Latn + {0xB0260000u, 18u}, // gbm -> Deva + {0xE0260000u, 46u}, // gby -> Latn {0xE4260000u, 1u}, // gbz -> Arab - {0xC4460000u, 44u}, // gcr -> Latn - {0x67640000u, 44u}, // gd -> Latn - {0x90660000u, 44u}, // gde -> Latn - {0xB4660000u, 44u}, // gdn -> Latn - {0xC4660000u, 44u}, // gdr -> Latn - {0x84860000u, 44u}, // geb -> Latn - {0xA4860000u, 44u}, // gej -> Latn - {0xAC860000u, 44u}, // gel -> Latn - {0xE4860000u, 19u}, // gez -> Ethi - {0xA8A60000u, 44u}, // gfk -> Latn - {0xB4C60000u, 17u}, // ggn -> Deva - {0xC8E60000u, 44u}, // ghs -> Latn - {0xAD060000u, 44u}, // gil -> Latn - {0xB1060000u, 44u}, // gim -> Latn + {0xC4460000u, 46u}, // gcr -> Latn + {0x67640000u, 46u}, // gd -> Latn + {0x90660000u, 46u}, // gde -> Latn + {0xB4660000u, 46u}, // gdn -> Latn + {0xC4660000u, 46u}, // gdr -> Latn + {0x84860000u, 46u}, // geb -> Latn + {0xA4860000u, 46u}, // gej -> Latn + {0xAC860000u, 46u}, // gel -> Latn + {0xE4860000u, 20u}, // gez -> Ethi + {0xA8A60000u, 46u}, // gfk -> Latn + {0xB4C60000u, 18u}, // ggn -> Deva + {0xC8E60000u, 46u}, // ghs -> Latn + {0xAD060000u, 46u}, // gil -> Latn + {0xB1060000u, 46u}, // gim -> Latn {0xA9260000u, 1u}, // gjk -> Arab - {0xB5260000u, 44u}, // gjn -> Latn + {0xB5260000u, 46u}, // gjn -> Latn {0xD1260000u, 1u}, // gju -> Arab - {0xB5460000u, 44u}, // gkn -> Latn - {0xBD460000u, 44u}, // gkp -> Latn - {0x676C0000u, 44u}, // gl -> Latn + {0xB5460000u, 46u}, // gkn -> Latn + {0xBD460000u, 46u}, // gkp -> Latn + {0x676C0000u, 46u}, // gl -> Latn {0xA9660000u, 1u}, // glk -> Arab - {0xB1860000u, 44u}, // gmm -> Latn - {0xD5860000u, 19u}, // gmv -> Ethi - {0x676E0000u, 44u}, // gn -> Latn - {0x8DA60000u, 44u}, // gnd -> Latn - {0x99A60000u, 44u}, // gng -> Latn - {0x8DC60000u, 44u}, // god -> Latn - {0x95C60000u, 19u}, // gof -> Ethi - {0xA1C60000u, 44u}, // goi -> Latn - {0xB1C60000u, 17u}, // gom -> Deva - {0xB5C60000u, 84u}, // gon -> Telu - {0xC5C60000u, 44u}, // gor -> Latn - {0xC9C60000u, 44u}, // gos -> Latn - {0xCDC60000u, 23u}, // got -> Goth - {0x86260000u, 44u}, // grb -> Latn - {0x8A260000u, 15u}, // grc -> Cprt + {0xB1860000u, 46u}, // gmm -> Latn + {0xD5860000u, 20u}, // gmv -> Ethi + {0x676E0000u, 46u}, // gn -> Latn + {0x8DA60000u, 46u}, // gnd -> Latn + {0x99A60000u, 46u}, // gng -> Latn + {0x8DC60000u, 46u}, // god -> Latn + {0x95C60000u, 20u}, // gof -> Ethi + {0xA1C60000u, 46u}, // goi -> Latn + {0xB1C60000u, 18u}, // gom -> Deva + {0xB5C60000u, 86u}, // gon -> Telu + {0xC5C60000u, 46u}, // gor -> Latn + {0xC9C60000u, 46u}, // gos -> Latn + {0xCDC60000u, 24u}, // got -> Goth + {0x86260000u, 46u}, // grb -> Latn + {0x8A260000u, 16u}, // grc -> Cprt {0xCE260000u, 7u}, // grt -> Beng - {0xDA260000u, 44u}, // grw -> Latn - {0xDA460000u, 44u}, // gsw -> Latn - {0x67750000u, 25u}, // gu -> Gujr - {0x86860000u, 44u}, // gub -> Latn - {0x8A860000u, 44u}, // guc -> Latn - {0x8E860000u, 44u}, // gud -> Latn - {0xC6860000u, 44u}, // gur -> Latn - {0xDA860000u, 44u}, // guw -> Latn - {0xDE860000u, 44u}, // gux -> Latn - {0xE6860000u, 44u}, // guz -> Latn - {0x67760000u, 44u}, // gv -> Latn - {0x96A60000u, 44u}, // gvf -> Latn - {0xC6A60000u, 17u}, // gvr -> Deva - {0xCAA60000u, 44u}, // gvs -> Latn + {0xDA260000u, 46u}, // grw -> Latn + {0xDA460000u, 46u}, // gsw -> Latn + {0x67750000u, 26u}, // gu -> Gujr + {0x86860000u, 46u}, // gub -> Latn + {0x8A860000u, 46u}, // guc -> Latn + {0x8E860000u, 46u}, // gud -> Latn + {0xC6860000u, 46u}, // gur -> Latn + {0xDA860000u, 46u}, // guw -> Latn + {0xDE860000u, 46u}, // gux -> Latn + {0xE6860000u, 46u}, // guz -> Latn + {0x67760000u, 46u}, // gv -> Latn + {0x96A60000u, 46u}, // gvf -> Latn + {0xC6A60000u, 18u}, // gvr -> Deva + {0xCAA60000u, 46u}, // gvs -> Latn {0x8AC60000u, 1u}, // gwc -> Arab - {0xA2C60000u, 44u}, // gwi -> Latn + {0xA2C60000u, 46u}, // gwi -> Latn {0xCEC60000u, 1u}, // gwt -> Arab - {0xA3060000u, 44u}, // gyi -> Latn - {0x68610000u, 44u}, // ha -> Latn + {0xA3060000u, 46u}, // gyi -> Latn + {0x68610000u, 46u}, // ha -> Latn {0x6861434Du, 1u}, // ha-CM -> Arab {0x68615344u, 1u}, // ha-SD -> Arab - {0x98070000u, 44u}, // hag -> Latn - {0xA8070000u, 27u}, // hak -> Hans - {0xB0070000u, 44u}, // ham -> Latn - {0xD8070000u, 44u}, // haw -> Latn + {0x98070000u, 46u}, // hag -> Latn + {0xA8070000u, 28u}, // hak -> Hans + {0xB0070000u, 46u}, // ham -> Latn + {0xD8070000u, 46u}, // haw -> Latn {0xE4070000u, 1u}, // haz -> Arab - {0x84270000u, 44u}, // hbb -> Latn - {0xE0670000u, 19u}, // hdy -> Ethi - {0x68650000u, 30u}, // he -> Hebr - {0xE0E70000u, 44u}, // hhy -> Latn - {0x68690000u, 17u}, // hi -> Deva - {0x81070000u, 44u}, // hia -> Latn - {0x95070000u, 44u}, // hif -> Latn - {0x99070000u, 44u}, // hig -> Latn - {0x9D070000u, 44u}, // hih -> Latn - {0xAD070000u, 44u}, // hil -> Latn - {0x81670000u, 44u}, // hla -> Latn - {0xD1670000u, 31u}, // hlu -> Hluw - {0x8D870000u, 67u}, // hmd -> Plrd - {0xCD870000u, 44u}, // hmt -> Latn + {0x84270000u, 46u}, // hbb -> Latn + {0xE0670000u, 20u}, // hdy -> Ethi + {0x68650000u, 31u}, // he -> Hebr + {0xE0E70000u, 46u}, // hhy -> Latn + {0x68690000u, 18u}, // hi -> Deva + {0x81070000u, 46u}, // hia -> Latn + {0x95070000u, 46u}, // hif -> Latn + {0x99070000u, 46u}, // hig -> Latn + {0x9D070000u, 46u}, // hih -> Latn + {0xAD070000u, 46u}, // hil -> Latn + {0x81670000u, 46u}, // hla -> Latn + {0xD1670000u, 32u}, // hlu -> Hluw + {0x8D870000u, 69u}, // hmd -> Plrd + {0xCD870000u, 46u}, // hmt -> Latn {0x8DA70000u, 1u}, // hnd -> Arab - {0x91A70000u, 17u}, // hne -> Deva - {0xA5A70000u, 32u}, // hnj -> Hmng - {0xB5A70000u, 44u}, // hnn -> Latn + {0x91A70000u, 18u}, // hne -> Deva + {0xA5A70000u, 33u}, // hnj -> Hmng + {0xB5A70000u, 46u}, // hnn -> Latn {0xB9A70000u, 1u}, // hno -> Arab - {0x686F0000u, 44u}, // ho -> Latn - {0x89C70000u, 17u}, // hoc -> Deva - {0xA5C70000u, 17u}, // hoj -> Deva - {0xCDC70000u, 44u}, // hot -> Latn - {0x68720000u, 44u}, // hr -> Latn - {0x86470000u, 44u}, // hsb -> Latn - {0xB6470000u, 27u}, // hsn -> Hans - {0x68740000u, 44u}, // ht -> Latn - {0x68750000u, 44u}, // hu -> Latn - {0xA2870000u, 44u}, // hui -> Latn + {0x686F0000u, 46u}, // ho -> Latn + {0x89C70000u, 18u}, // hoc -> Deva + {0xA5C70000u, 18u}, // hoj -> Deva + {0xCDC70000u, 46u}, // hot -> Latn + {0x68720000u, 46u}, // hr -> Latn + {0x86470000u, 46u}, // hsb -> Latn + {0xB6470000u, 28u}, // hsn -> Hans + {0x68740000u, 46u}, // ht -> Latn + {0x68750000u, 46u}, // hu -> Latn + {0xA2870000u, 46u}, // hui -> Latn {0x68790000u, 3u}, // hy -> Armn - {0x687A0000u, 44u}, // hz -> Latn - {0x69610000u, 44u}, // ia -> Latn - {0xB4080000u, 44u}, // ian -> Latn - {0xC4080000u, 44u}, // iar -> Latn - {0x80280000u, 44u}, // iba -> Latn - {0x84280000u, 44u}, // ibb -> Latn - {0xE0280000u, 44u}, // iby -> Latn - {0x80480000u, 44u}, // ica -> Latn - {0x9C480000u, 44u}, // ich -> Latn - {0x69640000u, 44u}, // id -> Latn - {0x8C680000u, 44u}, // idd -> Latn - {0xA0680000u, 44u}, // idi -> Latn - {0xD0680000u, 44u}, // idu -> Latn - {0x90A80000u, 44u}, // ife -> Latn - {0x69670000u, 44u}, // ig -> Latn - {0x84C80000u, 44u}, // igb -> Latn - {0x90C80000u, 44u}, // ige -> Latn - {0x69690000u, 94u}, // ii -> Yiii - {0xA5280000u, 44u}, // ijj -> Latn - {0x696B0000u, 44u}, // ik -> Latn - {0xA9480000u, 44u}, // ikk -> Latn - {0xCD480000u, 44u}, // ikt -> Latn - {0xD9480000u, 44u}, // ikw -> Latn - {0xDD480000u, 44u}, // ikx -> Latn - {0xB9680000u, 44u}, // ilo -> Latn - {0xB9880000u, 44u}, // imo -> Latn - {0x696E0000u, 44u}, // in -> Latn - {0x9DA80000u, 16u}, // inh -> Cyrl - {0x696F0000u, 44u}, // io -> Latn - {0xD1C80000u, 44u}, // iou -> Latn - {0xA2280000u, 44u}, // iri -> Latn - {0x69730000u, 44u}, // is -> Latn - {0x69740000u, 44u}, // it -> Latn + {0x687A0000u, 46u}, // hz -> Latn + {0x69610000u, 46u}, // ia -> Latn + {0xB4080000u, 46u}, // ian -> Latn + {0xC4080000u, 46u}, // iar -> Latn + {0x80280000u, 46u}, // iba -> Latn + {0x84280000u, 46u}, // ibb -> Latn + {0xE0280000u, 46u}, // iby -> Latn + {0x80480000u, 46u}, // ica -> Latn + {0x9C480000u, 46u}, // ich -> Latn + {0x69640000u, 46u}, // id -> Latn + {0x8C680000u, 46u}, // idd -> Latn + {0xA0680000u, 46u}, // idi -> Latn + {0xD0680000u, 46u}, // idu -> Latn + {0x90A80000u, 46u}, // ife -> Latn + {0x69670000u, 46u}, // ig -> Latn + {0x84C80000u, 46u}, // igb -> Latn + {0x90C80000u, 46u}, // ige -> Latn + {0x69690000u, 96u}, // ii -> Yiii + {0xA5280000u, 46u}, // ijj -> Latn + {0x696B0000u, 46u}, // ik -> Latn + {0xA9480000u, 46u}, // ikk -> Latn + {0xCD480000u, 46u}, // ikt -> Latn + {0xD9480000u, 46u}, // ikw -> Latn + {0xDD480000u, 46u}, // ikx -> Latn + {0xB9680000u, 46u}, // ilo -> Latn + {0xB9880000u, 46u}, // imo -> Latn + {0x696E0000u, 46u}, // in -> Latn + {0x9DA80000u, 17u}, // inh -> Cyrl + {0x696F0000u, 46u}, // io -> Latn + {0xD1C80000u, 46u}, // iou -> Latn + {0xA2280000u, 46u}, // iri -> Latn + {0x69730000u, 46u}, // is -> Latn + {0x69740000u, 46u}, // it -> Latn {0x69750000u, 10u}, // iu -> Cans - {0x69770000u, 30u}, // iw -> Hebr - {0xB2C80000u, 44u}, // iwm -> Latn - {0xCAC80000u, 44u}, // iws -> Latn - {0x9F280000u, 44u}, // izh -> Latn - {0xA3280000u, 44u}, // izi -> Latn - {0x6A610000u, 35u}, // ja -> Jpan - {0x84090000u, 44u}, // jab -> Latn - {0xB0090000u, 44u}, // jam -> Latn - {0xB8290000u, 44u}, // jbo -> Latn - {0xD0290000u, 44u}, // jbu -> Latn - {0xB4890000u, 44u}, // jen -> Latn - {0xA8C90000u, 44u}, // jgk -> Latn - {0xB8C90000u, 44u}, // jgo -> Latn - {0x6A690000u, 30u}, // ji -> Hebr - {0x85090000u, 44u}, // jib -> Latn - {0x89890000u, 44u}, // jmc -> Latn - {0xAD890000u, 17u}, // jml -> Deva - {0x82290000u, 44u}, // jra -> Latn - {0xCE890000u, 44u}, // jut -> Latn - {0x6A760000u, 44u}, // jv -> Latn - {0x6A770000u, 44u}, // jw -> Latn - {0x6B610000u, 20u}, // ka -> Geor - {0x800A0000u, 16u}, // kaa -> Cyrl - {0x840A0000u, 44u}, // kab -> Latn - {0x880A0000u, 44u}, // kac -> Latn - {0x8C0A0000u, 44u}, // kad -> Latn - {0xA00A0000u, 44u}, // kai -> Latn - {0xA40A0000u, 44u}, // kaj -> Latn - {0xB00A0000u, 44u}, // kam -> Latn - {0xB80A0000u, 44u}, // kao -> Latn - {0x8C2A0000u, 16u}, // kbd -> Cyrl - {0xB02A0000u, 44u}, // kbm -> Latn - {0xBC2A0000u, 44u}, // kbp -> Latn - {0xC02A0000u, 44u}, // kbq -> Latn - {0xDC2A0000u, 44u}, // kbx -> Latn + {0x69770000u, 31u}, // iw -> Hebr + {0xB2C80000u, 46u}, // iwm -> Latn + {0xCAC80000u, 46u}, // iws -> Latn + {0x9F280000u, 46u}, // izh -> Latn + {0xA3280000u, 46u}, // izi -> Latn + {0x6A610000u, 36u}, // ja -> Jpan + {0x84090000u, 46u}, // jab -> Latn + {0xB0090000u, 46u}, // jam -> Latn + {0xB8290000u, 46u}, // jbo -> Latn + {0xD0290000u, 46u}, // jbu -> Latn + {0xB4890000u, 46u}, // jen -> Latn + {0xA8C90000u, 46u}, // jgk -> Latn + {0xB8C90000u, 46u}, // jgo -> Latn + {0x6A690000u, 31u}, // ji -> Hebr + {0x85090000u, 46u}, // jib -> Latn + {0x89890000u, 46u}, // jmc -> Latn + {0xAD890000u, 18u}, // jml -> Deva + {0x82290000u, 46u}, // jra -> Latn + {0xCE890000u, 46u}, // jut -> Latn + {0x6A760000u, 46u}, // jv -> Latn + {0x6A770000u, 46u}, // jw -> Latn + {0x6B610000u, 21u}, // ka -> Geor + {0x800A0000u, 17u}, // kaa -> Cyrl + {0x840A0000u, 46u}, // kab -> Latn + {0x880A0000u, 46u}, // kac -> Latn + {0x8C0A0000u, 46u}, // kad -> Latn + {0xA00A0000u, 46u}, // kai -> Latn + {0xA40A0000u, 46u}, // kaj -> Latn + {0xB00A0000u, 46u}, // kam -> Latn + {0xB80A0000u, 46u}, // kao -> Latn + {0x8C2A0000u, 17u}, // kbd -> Cyrl + {0xB02A0000u, 46u}, // kbm -> Latn + {0xBC2A0000u, 46u}, // kbp -> Latn + {0xC02A0000u, 46u}, // kbq -> Latn + {0xDC2A0000u, 46u}, // kbx -> Latn {0xE02A0000u, 1u}, // kby -> Arab - {0x984A0000u, 44u}, // kcg -> Latn - {0xA84A0000u, 44u}, // kck -> Latn - {0xAC4A0000u, 44u}, // kcl -> Latn - {0xCC4A0000u, 44u}, // kct -> Latn - {0x906A0000u, 44u}, // kde -> Latn + {0x984A0000u, 46u}, // kcg -> Latn + {0xA84A0000u, 46u}, // kck -> Latn + {0xAC4A0000u, 46u}, // kcl -> Latn + {0xCC4A0000u, 46u}, // kct -> Latn + {0x906A0000u, 46u}, // kde -> Latn {0x9C6A0000u, 1u}, // kdh -> Arab - {0xAC6A0000u, 44u}, // kdl -> Latn - {0xCC6A0000u, 87u}, // kdt -> Thai - {0x808A0000u, 44u}, // kea -> Latn - {0xB48A0000u, 44u}, // ken -> Latn - {0xE48A0000u, 44u}, // kez -> Latn - {0xB8AA0000u, 44u}, // kfo -> Latn - {0xC4AA0000u, 17u}, // kfr -> Deva - {0xE0AA0000u, 17u}, // kfy -> Deva - {0x6B670000u, 44u}, // kg -> Latn - {0x90CA0000u, 44u}, // kge -> Latn - {0x94CA0000u, 44u}, // kgf -> Latn - {0xBCCA0000u, 44u}, // kgp -> Latn - {0x80EA0000u, 44u}, // kha -> Latn - {0x84EA0000u, 80u}, // khb -> Talu - {0xB4EA0000u, 17u}, // khn -> Deva - {0xC0EA0000u, 44u}, // khq -> Latn - {0xC8EA0000u, 44u}, // khs -> Latn - {0xCCEA0000u, 56u}, // kht -> Mymr + {0xAC6A0000u, 46u}, // kdl -> Latn + {0xCC6A0000u, 89u}, // kdt -> Thai + {0x808A0000u, 46u}, // kea -> Latn + {0xB48A0000u, 46u}, // ken -> Latn + {0xE48A0000u, 46u}, // kez -> Latn + {0xB8AA0000u, 46u}, // kfo -> Latn + {0xC4AA0000u, 18u}, // kfr -> Deva + {0xE0AA0000u, 18u}, // kfy -> Deva + {0x6B670000u, 46u}, // kg -> Latn + {0x90CA0000u, 46u}, // kge -> Latn + {0x94CA0000u, 46u}, // kgf -> Latn + {0xBCCA0000u, 46u}, // kgp -> Latn + {0x80EA0000u, 46u}, // kha -> Latn + {0x84EA0000u, 82u}, // khb -> Talu + {0xB4EA0000u, 18u}, // khn -> Deva + {0xC0EA0000u, 46u}, // khq -> Latn + {0xC8EA0000u, 46u}, // khs -> Latn + {0xCCEA0000u, 58u}, // kht -> Mymr {0xD8EA0000u, 1u}, // khw -> Arab - {0xE4EA0000u, 44u}, // khz -> Latn - {0x6B690000u, 44u}, // ki -> Latn - {0xA50A0000u, 44u}, // kij -> Latn - {0xD10A0000u, 44u}, // kiu -> Latn - {0xD90A0000u, 44u}, // kiw -> Latn - {0x6B6A0000u, 44u}, // kj -> Latn - {0x8D2A0000u, 44u}, // kjd -> Latn - {0x992A0000u, 43u}, // kjg -> Laoo - {0xC92A0000u, 44u}, // kjs -> Latn - {0xE12A0000u, 44u}, // kjy -> Latn - {0x6B6B0000u, 16u}, // kk -> Cyrl + {0xE4EA0000u, 46u}, // khz -> Latn + {0x6B690000u, 46u}, // ki -> Latn + {0xA50A0000u, 46u}, // kij -> Latn + {0xD10A0000u, 46u}, // kiu -> Latn + {0xD90A0000u, 46u}, // kiw -> Latn + {0x6B6A0000u, 46u}, // kj -> Latn + {0x8D2A0000u, 46u}, // kjd -> Latn + {0x992A0000u, 45u}, // kjg -> Laoo + {0xC92A0000u, 46u}, // kjs -> Latn + {0xE12A0000u, 46u}, // kjy -> Latn + {0x6B6B0000u, 17u}, // kk -> Cyrl {0x6B6B4146u, 1u}, // kk-AF -> Arab {0x6B6B434Eu, 1u}, // kk-CN -> Arab {0x6B6B4952u, 1u}, // kk-IR -> Arab {0x6B6B4D4Eu, 1u}, // kk-MN -> Arab - {0x894A0000u, 44u}, // kkc -> Latn - {0xA54A0000u, 44u}, // kkj -> Latn - {0x6B6C0000u, 44u}, // kl -> Latn - {0xB56A0000u, 44u}, // kln -> Latn - {0xC16A0000u, 44u}, // klq -> Latn - {0xCD6A0000u, 44u}, // klt -> Latn - {0xDD6A0000u, 44u}, // klx -> Latn - {0x6B6D0000u, 39u}, // km -> Khmr - {0x858A0000u, 44u}, // kmb -> Latn - {0x9D8A0000u, 44u}, // kmh -> Latn - {0xB98A0000u, 44u}, // kmo -> Latn - {0xC98A0000u, 44u}, // kms -> Latn - {0xD18A0000u, 44u}, // kmu -> Latn - {0xD98A0000u, 44u}, // kmw -> Latn - {0x6B6E0000u, 40u}, // kn -> Knda - {0x95AA0000u, 44u}, // knf -> Latn - {0xBDAA0000u, 44u}, // knp -> Latn - {0x6B6F0000u, 41u}, // ko -> Kore - {0xA1CA0000u, 16u}, // koi -> Cyrl - {0xA9CA0000u, 17u}, // kok -> Deva - {0xADCA0000u, 44u}, // kol -> Latn - {0xC9CA0000u, 44u}, // kos -> Latn - {0xE5CA0000u, 44u}, // koz -> Latn - {0x91EA0000u, 44u}, // kpe -> Latn - {0x95EA0000u, 44u}, // kpf -> Latn - {0xB9EA0000u, 44u}, // kpo -> Latn - {0xC5EA0000u, 44u}, // kpr -> Latn - {0xDDEA0000u, 44u}, // kpx -> Latn - {0x860A0000u, 44u}, // kqb -> Latn - {0x960A0000u, 44u}, // kqf -> Latn - {0xCA0A0000u, 44u}, // kqs -> Latn - {0xE20A0000u, 19u}, // kqy -> Ethi - {0x6B720000u, 44u}, // kr -> Latn - {0x8A2A0000u, 16u}, // krc -> Cyrl - {0xA22A0000u, 44u}, // kri -> Latn - {0xA62A0000u, 44u}, // krj -> Latn - {0xAE2A0000u, 44u}, // krl -> Latn - {0xCA2A0000u, 44u}, // krs -> Latn - {0xD22A0000u, 17u}, // kru -> Deva + {0x894A0000u, 46u}, // kkc -> Latn + {0xA54A0000u, 46u}, // kkj -> Latn + {0x6B6C0000u, 46u}, // kl -> Latn + {0xB56A0000u, 46u}, // kln -> Latn + {0xC16A0000u, 46u}, // klq -> Latn + {0xCD6A0000u, 46u}, // klt -> Latn + {0xDD6A0000u, 46u}, // klx -> Latn + {0x6B6D0000u, 40u}, // km -> Khmr + {0x858A0000u, 46u}, // kmb -> Latn + {0x9D8A0000u, 46u}, // kmh -> Latn + {0xB98A0000u, 46u}, // kmo -> Latn + {0xC98A0000u, 46u}, // kms -> Latn + {0xD18A0000u, 46u}, // kmu -> Latn + {0xD98A0000u, 46u}, // kmw -> Latn + {0x6B6E0000u, 42u}, // kn -> Knda + {0x95AA0000u, 46u}, // knf -> Latn + {0xBDAA0000u, 46u}, // knp -> Latn + {0x6B6F0000u, 43u}, // ko -> Kore + {0xA1CA0000u, 17u}, // koi -> Cyrl + {0xA9CA0000u, 18u}, // kok -> Deva + {0xADCA0000u, 46u}, // kol -> Latn + {0xC9CA0000u, 46u}, // kos -> Latn + {0xE5CA0000u, 46u}, // koz -> Latn + {0x91EA0000u, 46u}, // kpe -> Latn + {0x95EA0000u, 46u}, // kpf -> Latn + {0xB9EA0000u, 46u}, // kpo -> Latn + {0xC5EA0000u, 46u}, // kpr -> Latn + {0xDDEA0000u, 46u}, // kpx -> Latn + {0x860A0000u, 46u}, // kqb -> Latn + {0x960A0000u, 46u}, // kqf -> Latn + {0xCA0A0000u, 46u}, // kqs -> Latn + {0xE20A0000u, 20u}, // kqy -> Ethi + {0x6B720000u, 46u}, // kr -> Latn + {0x8A2A0000u, 17u}, // krc -> Cyrl + {0xA22A0000u, 46u}, // kri -> Latn + {0xA62A0000u, 46u}, // krj -> Latn + {0xAE2A0000u, 46u}, // krl -> Latn + {0xCA2A0000u, 46u}, // krs -> Latn + {0xD22A0000u, 18u}, // kru -> Deva {0x6B730000u, 1u}, // ks -> Arab - {0x864A0000u, 44u}, // ksb -> Latn - {0x8E4A0000u, 44u}, // ksd -> Latn - {0x964A0000u, 44u}, // ksf -> Latn - {0x9E4A0000u, 44u}, // ksh -> Latn - {0xA64A0000u, 44u}, // ksj -> Latn - {0xC64A0000u, 44u}, // ksr -> Latn - {0x866A0000u, 19u}, // ktb -> Ethi - {0xB26A0000u, 44u}, // ktm -> Latn - {0xBA6A0000u, 44u}, // kto -> Latn - {0xC66A0000u, 44u}, // ktr -> Latn - {0x6B750000u, 44u}, // ku -> Latn + {0x864A0000u, 46u}, // ksb -> Latn + {0x8E4A0000u, 46u}, // ksd -> Latn + {0x964A0000u, 46u}, // ksf -> Latn + {0x9E4A0000u, 46u}, // ksh -> Latn + {0xA64A0000u, 46u}, // ksj -> Latn + {0xC64A0000u, 46u}, // ksr -> Latn + {0x866A0000u, 20u}, // ktb -> Ethi + {0xB26A0000u, 46u}, // ktm -> Latn + {0xBA6A0000u, 46u}, // kto -> Latn + {0xC66A0000u, 46u}, // ktr -> Latn + {0x6B750000u, 46u}, // ku -> Latn {0x6B754952u, 1u}, // ku-IR -> Arab {0x6B754C42u, 1u}, // ku-LB -> Arab - {0x868A0000u, 44u}, // kub -> Latn - {0x8E8A0000u, 44u}, // kud -> Latn - {0x928A0000u, 44u}, // kue -> Latn - {0xA68A0000u, 44u}, // kuj -> Latn - {0xB28A0000u, 16u}, // kum -> Cyrl - {0xB68A0000u, 44u}, // kun -> Latn - {0xBE8A0000u, 44u}, // kup -> Latn - {0xCA8A0000u, 44u}, // kus -> Latn - {0x6B760000u, 16u}, // kv -> Cyrl - {0x9AAA0000u, 44u}, // kvg -> Latn - {0xC6AA0000u, 44u}, // kvr -> Latn + {0x868A0000u, 46u}, // kub -> Latn + {0x8E8A0000u, 46u}, // kud -> Latn + {0x928A0000u, 46u}, // kue -> Latn + {0xA68A0000u, 46u}, // kuj -> Latn + {0xB28A0000u, 17u}, // kum -> Cyrl + {0xB68A0000u, 46u}, // kun -> Latn + {0xBE8A0000u, 46u}, // kup -> Latn + {0xCA8A0000u, 46u}, // kus -> Latn + {0x6B760000u, 17u}, // kv -> Cyrl + {0x9AAA0000u, 46u}, // kvg -> Latn + {0xC6AA0000u, 46u}, // kvr -> Latn {0xDEAA0000u, 1u}, // kvx -> Arab - {0x6B770000u, 44u}, // kw -> Latn - {0xA6CA0000u, 44u}, // kwj -> Latn - {0xBACA0000u, 44u}, // kwo -> Latn - {0xC2CA0000u, 44u}, // kwq -> Latn - {0x82EA0000u, 44u}, // kxa -> Latn - {0x8AEA0000u, 19u}, // kxc -> Ethi - {0x92EA0000u, 44u}, // kxe -> Latn - {0xB2EA0000u, 87u}, // kxm -> Thai + {0x6B770000u, 46u}, // kw -> Latn + {0xA6CA0000u, 46u}, // kwj -> Latn + {0xBACA0000u, 46u}, // kwo -> Latn + {0xC2CA0000u, 46u}, // kwq -> Latn + {0x82EA0000u, 46u}, // kxa -> Latn + {0x8AEA0000u, 20u}, // kxc -> Ethi + {0x92EA0000u, 46u}, // kxe -> Latn + {0xB2EA0000u, 89u}, // kxm -> Thai {0xBEEA0000u, 1u}, // kxp -> Arab - {0xDAEA0000u, 44u}, // kxw -> Latn - {0xE6EA0000u, 44u}, // kxz -> Latn - {0x6B790000u, 16u}, // ky -> Cyrl + {0xDAEA0000u, 46u}, // kxw -> Latn + {0xE6EA0000u, 46u}, // kxz -> Latn + {0x6B790000u, 17u}, // ky -> Cyrl {0x6B79434Eu, 1u}, // ky-CN -> Arab - {0x6B795452u, 44u}, // ky-TR -> Latn - {0x930A0000u, 44u}, // kye -> Latn - {0xDF0A0000u, 44u}, // kyx -> Latn - {0xA72A0000u, 44u}, // kzj -> Latn - {0xC72A0000u, 44u}, // kzr -> Latn - {0xCF2A0000u, 44u}, // kzt -> Latn - {0x6C610000u, 44u}, // la -> Latn - {0x840B0000u, 46u}, // lab -> Lina - {0x8C0B0000u, 30u}, // lad -> Hebr - {0x980B0000u, 44u}, // lag -> Latn + {0x6B795452u, 46u}, // ky-TR -> Latn + {0x930A0000u, 46u}, // kye -> Latn + {0xDF0A0000u, 46u}, // kyx -> Latn + {0xA72A0000u, 46u}, // kzj -> Latn + {0xC72A0000u, 46u}, // kzr -> Latn + {0xCF2A0000u, 46u}, // kzt -> Latn + {0x6C610000u, 46u}, // la -> Latn + {0x840B0000u, 48u}, // lab -> Lina + {0x8C0B0000u, 31u}, // lad -> Hebr + {0x980B0000u, 46u}, // lag -> Latn {0x9C0B0000u, 1u}, // lah -> Arab - {0xA40B0000u, 44u}, // laj -> Latn - {0xC80B0000u, 44u}, // las -> Latn - {0x6C620000u, 44u}, // lb -> Latn - {0x902B0000u, 16u}, // lbe -> Cyrl - {0xD02B0000u, 44u}, // lbu -> Latn - {0xD82B0000u, 44u}, // lbw -> Latn - {0xB04B0000u, 44u}, // lcm -> Latn - {0xBC4B0000u, 87u}, // lcp -> Thai - {0x846B0000u, 44u}, // ldb -> Latn - {0x8C8B0000u, 44u}, // led -> Latn - {0x908B0000u, 44u}, // lee -> Latn - {0xB08B0000u, 44u}, // lem -> Latn - {0xBC8B0000u, 45u}, // lep -> Lepc - {0xC08B0000u, 44u}, // leq -> Latn - {0xD08B0000u, 44u}, // leu -> Latn - {0xE48B0000u, 16u}, // lez -> Cyrl - {0x6C670000u, 44u}, // lg -> Latn - {0x98CB0000u, 44u}, // lgg -> Latn - {0x6C690000u, 44u}, // li -> Latn - {0x810B0000u, 44u}, // lia -> Latn - {0x8D0B0000u, 44u}, // lid -> Latn - {0x950B0000u, 17u}, // lif -> Deva - {0x990B0000u, 44u}, // lig -> Latn - {0x9D0B0000u, 44u}, // lih -> Latn - {0xA50B0000u, 44u}, // lij -> Latn - {0xC90B0000u, 47u}, // lis -> Lisu - {0xBD2B0000u, 44u}, // ljp -> Latn + {0xA40B0000u, 46u}, // laj -> Latn + {0xC80B0000u, 46u}, // las -> Latn + {0x6C620000u, 46u}, // lb -> Latn + {0x902B0000u, 17u}, // lbe -> Cyrl + {0xD02B0000u, 46u}, // lbu -> Latn + {0xD82B0000u, 46u}, // lbw -> Latn + {0xB04B0000u, 46u}, // lcm -> Latn + {0xBC4B0000u, 89u}, // lcp -> Thai + {0x846B0000u, 46u}, // ldb -> Latn + {0x8C8B0000u, 46u}, // led -> Latn + {0x908B0000u, 46u}, // lee -> Latn + {0xB08B0000u, 46u}, // lem -> Latn + {0xBC8B0000u, 47u}, // lep -> Lepc + {0xC08B0000u, 46u}, // leq -> Latn + {0xD08B0000u, 46u}, // leu -> Latn + {0xE48B0000u, 17u}, // lez -> Cyrl + {0x6C670000u, 46u}, // lg -> Latn + {0x98CB0000u, 46u}, // lgg -> Latn + {0x6C690000u, 46u}, // li -> Latn + {0x810B0000u, 46u}, // lia -> Latn + {0x8D0B0000u, 46u}, // lid -> Latn + {0x950B0000u, 18u}, // lif -> Deva + {0x990B0000u, 46u}, // lig -> Latn + {0x9D0B0000u, 46u}, // lih -> Latn + {0xA50B0000u, 46u}, // lij -> Latn + {0xC90B0000u, 49u}, // lis -> Lisu + {0xBD2B0000u, 46u}, // ljp -> Latn {0xA14B0000u, 1u}, // lki -> Arab - {0xCD4B0000u, 44u}, // lkt -> Latn - {0x916B0000u, 44u}, // lle -> Latn - {0xB56B0000u, 44u}, // lln -> Latn - {0xB58B0000u, 84u}, // lmn -> Telu - {0xB98B0000u, 44u}, // lmo -> Latn - {0xBD8B0000u, 44u}, // lmp -> Latn - {0x6C6E0000u, 44u}, // ln -> Latn - {0xC9AB0000u, 44u}, // lns -> Latn - {0xD1AB0000u, 44u}, // lnu -> Latn - {0x6C6F0000u, 43u}, // lo -> Laoo - {0xA5CB0000u, 44u}, // loj -> Latn - {0xA9CB0000u, 44u}, // lok -> Latn - {0xADCB0000u, 44u}, // lol -> Latn - {0xC5CB0000u, 44u}, // lor -> Latn - {0xC9CB0000u, 44u}, // los -> Latn - {0xE5CB0000u, 44u}, // loz -> Latn + {0xCD4B0000u, 46u}, // lkt -> Latn + {0x916B0000u, 46u}, // lle -> Latn + {0xB56B0000u, 46u}, // lln -> Latn + {0xB58B0000u, 86u}, // lmn -> Telu + {0xB98B0000u, 46u}, // lmo -> Latn + {0xBD8B0000u, 46u}, // lmp -> Latn + {0x6C6E0000u, 46u}, // ln -> Latn + {0xC9AB0000u, 46u}, // lns -> Latn + {0xD1AB0000u, 46u}, // lnu -> Latn + {0x6C6F0000u, 45u}, // lo -> Laoo + {0xA5CB0000u, 46u}, // loj -> Latn + {0xA9CB0000u, 46u}, // lok -> Latn + {0xADCB0000u, 46u}, // lol -> Latn + {0xC5CB0000u, 46u}, // lor -> Latn + {0xC9CB0000u, 46u}, // los -> Latn + {0xE5CB0000u, 46u}, // loz -> Latn {0x8A2B0000u, 1u}, // lrc -> Arab - {0x6C740000u, 44u}, // lt -> Latn - {0x9A6B0000u, 44u}, // ltg -> Latn - {0x6C750000u, 44u}, // lu -> Latn - {0x828B0000u, 44u}, // lua -> Latn - {0xBA8B0000u, 44u}, // luo -> Latn - {0xE28B0000u, 44u}, // luy -> Latn + {0x6C740000u, 46u}, // lt -> Latn + {0x9A6B0000u, 46u}, // ltg -> Latn + {0x6C750000u, 46u}, // lu -> Latn + {0x828B0000u, 46u}, // lua -> Latn + {0xBA8B0000u, 46u}, // luo -> Latn + {0xE28B0000u, 46u}, // luy -> Latn {0xE68B0000u, 1u}, // luz -> Arab - {0x6C760000u, 44u}, // lv -> Latn - {0xAECB0000u, 87u}, // lwl -> Thai - {0x9F2B0000u, 27u}, // lzh -> Hans - {0xE72B0000u, 44u}, // lzz -> Latn - {0x8C0C0000u, 44u}, // mad -> Latn - {0x940C0000u, 44u}, // maf -> Latn - {0x980C0000u, 17u}, // mag -> Deva - {0xA00C0000u, 17u}, // mai -> Deva - {0xA80C0000u, 44u}, // mak -> Latn - {0xB40C0000u, 44u}, // man -> Latn - {0xB40C474Eu, 58u}, // man-GN -> Nkoo - {0xC80C0000u, 44u}, // mas -> Latn - {0xD80C0000u, 44u}, // maw -> Latn - {0xE40C0000u, 44u}, // maz -> Latn - {0x9C2C0000u, 44u}, // mbh -> Latn - {0xB82C0000u, 44u}, // mbo -> Latn - {0xC02C0000u, 44u}, // mbq -> Latn - {0xD02C0000u, 44u}, // mbu -> Latn - {0xD82C0000u, 44u}, // mbw -> Latn - {0xA04C0000u, 44u}, // mci -> Latn - {0xBC4C0000u, 44u}, // mcp -> Latn - {0xC04C0000u, 44u}, // mcq -> Latn - {0xC44C0000u, 44u}, // mcr -> Latn - {0xD04C0000u, 44u}, // mcu -> Latn - {0x806C0000u, 44u}, // mda -> Latn + {0x6C760000u, 46u}, // lv -> Latn + {0xAECB0000u, 89u}, // lwl -> Thai + {0x9F2B0000u, 28u}, // lzh -> Hans + {0xE72B0000u, 46u}, // lzz -> Latn + {0x8C0C0000u, 46u}, // mad -> Latn + {0x940C0000u, 46u}, // maf -> Latn + {0x980C0000u, 18u}, // mag -> Deva + {0xA00C0000u, 18u}, // mai -> Deva + {0xA80C0000u, 46u}, // mak -> Latn + {0xB40C0000u, 46u}, // man -> Latn + {0xB40C474Eu, 60u}, // man-GN -> Nkoo + {0xC80C0000u, 46u}, // mas -> Latn + {0xD80C0000u, 46u}, // maw -> Latn + {0xE40C0000u, 46u}, // maz -> Latn + {0x9C2C0000u, 46u}, // mbh -> Latn + {0xB82C0000u, 46u}, // mbo -> Latn + {0xC02C0000u, 46u}, // mbq -> Latn + {0xD02C0000u, 46u}, // mbu -> Latn + {0xD82C0000u, 46u}, // mbw -> Latn + {0xA04C0000u, 46u}, // mci -> Latn + {0xBC4C0000u, 46u}, // mcp -> Latn + {0xC04C0000u, 46u}, // mcq -> Latn + {0xC44C0000u, 46u}, // mcr -> Latn + {0xD04C0000u, 46u}, // mcu -> Latn + {0x806C0000u, 46u}, // mda -> Latn {0x906C0000u, 1u}, // mde -> Arab - {0x946C0000u, 16u}, // mdf -> Cyrl - {0x9C6C0000u, 44u}, // mdh -> Latn - {0xA46C0000u, 44u}, // mdj -> Latn - {0xC46C0000u, 44u}, // mdr -> Latn - {0xDC6C0000u, 19u}, // mdx -> Ethi - {0x8C8C0000u, 44u}, // med -> Latn - {0x908C0000u, 44u}, // mee -> Latn - {0xA88C0000u, 44u}, // mek -> Latn - {0xB48C0000u, 44u}, // men -> Latn - {0xC48C0000u, 44u}, // mer -> Latn - {0xCC8C0000u, 44u}, // met -> Latn - {0xD08C0000u, 44u}, // meu -> Latn + {0x946C0000u, 17u}, // mdf -> Cyrl + {0x9C6C0000u, 46u}, // mdh -> Latn + {0xA46C0000u, 46u}, // mdj -> Latn + {0xC46C0000u, 46u}, // mdr -> Latn + {0xDC6C0000u, 20u}, // mdx -> Ethi + {0x8C8C0000u, 46u}, // med -> Latn + {0x908C0000u, 46u}, // mee -> Latn + {0xA88C0000u, 46u}, // mek -> Latn + {0xB48C0000u, 46u}, // men -> Latn + {0xC48C0000u, 46u}, // mer -> Latn + {0xCC8C0000u, 46u}, // met -> Latn + {0xD08C0000u, 46u}, // meu -> Latn {0x80AC0000u, 1u}, // mfa -> Arab - {0x90AC0000u, 44u}, // mfe -> Latn - {0xB4AC0000u, 44u}, // mfn -> Latn - {0xB8AC0000u, 44u}, // mfo -> Latn - {0xC0AC0000u, 44u}, // mfq -> Latn - {0x6D670000u, 44u}, // mg -> Latn - {0x9CCC0000u, 44u}, // mgh -> Latn - {0xACCC0000u, 44u}, // mgl -> Latn - {0xB8CC0000u, 44u}, // mgo -> Latn - {0xBCCC0000u, 17u}, // mgp -> Deva - {0xE0CC0000u, 44u}, // mgy -> Latn - {0x6D680000u, 44u}, // mh -> Latn - {0xA0EC0000u, 44u}, // mhi -> Latn - {0xACEC0000u, 44u}, // mhl -> Latn - {0x6D690000u, 44u}, // mi -> Latn - {0x950C0000u, 44u}, // mif -> Latn - {0xB50C0000u, 44u}, // min -> Latn - {0xC90C0000u, 29u}, // mis -> Hatr - {0xD90C0000u, 44u}, // miw -> Latn - {0x6D6B0000u, 16u}, // mk -> Cyrl + {0x90AC0000u, 46u}, // mfe -> Latn + {0xB4AC0000u, 46u}, // mfn -> Latn + {0xB8AC0000u, 46u}, // mfo -> Latn + {0xC0AC0000u, 46u}, // mfq -> Latn + {0x6D670000u, 46u}, // mg -> Latn + {0x9CCC0000u, 46u}, // mgh -> Latn + {0xACCC0000u, 46u}, // mgl -> Latn + {0xB8CC0000u, 46u}, // mgo -> Latn + {0xBCCC0000u, 18u}, // mgp -> Deva + {0xE0CC0000u, 46u}, // mgy -> Latn + {0x6D680000u, 46u}, // mh -> Latn + {0xA0EC0000u, 46u}, // mhi -> Latn + {0xACEC0000u, 46u}, // mhl -> Latn + {0x6D690000u, 46u}, // mi -> Latn + {0x950C0000u, 46u}, // mif -> Latn + {0xB50C0000u, 46u}, // min -> Latn + {0xC90C0000u, 30u}, // mis -> Hatr + {0xD90C0000u, 46u}, // miw -> Latn + {0x6D6B0000u, 17u}, // mk -> Cyrl {0xA14C0000u, 1u}, // mki -> Arab - {0xAD4C0000u, 44u}, // mkl -> Latn - {0xBD4C0000u, 44u}, // mkp -> Latn - {0xD94C0000u, 44u}, // mkw -> Latn - {0x6D6C0000u, 53u}, // ml -> Mlym - {0x916C0000u, 44u}, // mle -> Latn - {0xBD6C0000u, 44u}, // mlp -> Latn - {0xC96C0000u, 44u}, // mls -> Latn - {0xB98C0000u, 44u}, // mmo -> Latn - {0xD18C0000u, 44u}, // mmu -> Latn - {0xDD8C0000u, 44u}, // mmx -> Latn - {0x6D6E0000u, 16u}, // mn -> Cyrl - {0x6D6E434Eu, 54u}, // mn-CN -> Mong - {0x81AC0000u, 44u}, // mna -> Latn - {0x95AC0000u, 44u}, // mnf -> Latn + {0xAD4C0000u, 46u}, // mkl -> Latn + {0xBD4C0000u, 46u}, // mkp -> Latn + {0xD94C0000u, 46u}, // mkw -> Latn + {0x6D6C0000u, 55u}, // ml -> Mlym + {0x916C0000u, 46u}, // mle -> Latn + {0xBD6C0000u, 46u}, // mlp -> Latn + {0xC96C0000u, 46u}, // mls -> Latn + {0xB98C0000u, 46u}, // mmo -> Latn + {0xD18C0000u, 46u}, // mmu -> Latn + {0xDD8C0000u, 46u}, // mmx -> Latn + {0x6D6E0000u, 17u}, // mn -> Cyrl + {0x6D6E434Eu, 56u}, // mn-CN -> Mong + {0x81AC0000u, 46u}, // mna -> Latn + {0x95AC0000u, 46u}, // mnf -> Latn {0xA1AC0000u, 7u}, // mni -> Beng - {0xD9AC0000u, 56u}, // mnw -> Mymr - {0x6D6F0000u, 44u}, // mo -> Latn - {0x81CC0000u, 44u}, // moa -> Latn - {0x91CC0000u, 44u}, // moe -> Latn - {0x9DCC0000u, 44u}, // moh -> Latn - {0xC9CC0000u, 44u}, // mos -> Latn - {0xDDCC0000u, 44u}, // mox -> Latn - {0xBDEC0000u, 44u}, // mpp -> Latn - {0xC9EC0000u, 44u}, // mps -> Latn - {0xCDEC0000u, 44u}, // mpt -> Latn - {0xDDEC0000u, 44u}, // mpx -> Latn - {0xAE0C0000u, 44u}, // mql -> Latn - {0x6D720000u, 17u}, // mr -> Deva - {0x8E2C0000u, 17u}, // mrd -> Deva - {0xA62C0000u, 16u}, // mrj -> Cyrl - {0xBA2C0000u, 55u}, // mro -> Mroo - {0x6D730000u, 44u}, // ms -> Latn + {0xD9AC0000u, 58u}, // mnw -> Mymr + {0x6D6F0000u, 46u}, // mo -> Latn + {0x81CC0000u, 46u}, // moa -> Latn + {0x91CC0000u, 46u}, // moe -> Latn + {0x9DCC0000u, 46u}, // moh -> Latn + {0xC9CC0000u, 46u}, // mos -> Latn + {0xDDCC0000u, 46u}, // mox -> Latn + {0xBDEC0000u, 46u}, // mpp -> Latn + {0xC9EC0000u, 46u}, // mps -> Latn + {0xCDEC0000u, 46u}, // mpt -> Latn + {0xDDEC0000u, 46u}, // mpx -> Latn + {0xAE0C0000u, 46u}, // mql -> Latn + {0x6D720000u, 18u}, // mr -> Deva + {0x8E2C0000u, 18u}, // mrd -> Deva + {0xA62C0000u, 17u}, // mrj -> Cyrl + {0xBA2C0000u, 57u}, // mro -> Mroo + {0x6D730000u, 46u}, // ms -> Latn {0x6D734343u, 1u}, // ms-CC -> Arab {0x6D734944u, 1u}, // ms-ID -> Arab - {0x6D740000u, 44u}, // mt -> Latn - {0x8A6C0000u, 44u}, // mtc -> Latn - {0x966C0000u, 44u}, // mtf -> Latn - {0xA26C0000u, 44u}, // mti -> Latn - {0xC66C0000u, 17u}, // mtr -> Deva - {0x828C0000u, 44u}, // mua -> Latn - {0xC68C0000u, 44u}, // mur -> Latn - {0xCA8C0000u, 44u}, // mus -> Latn - {0x82AC0000u, 44u}, // mva -> Latn - {0xB6AC0000u, 44u}, // mvn -> Latn + {0x6D740000u, 46u}, // mt -> Latn + {0x8A6C0000u, 46u}, // mtc -> Latn + {0x966C0000u, 46u}, // mtf -> Latn + {0xA26C0000u, 46u}, // mti -> Latn + {0xC66C0000u, 18u}, // mtr -> Deva + {0x828C0000u, 46u}, // mua -> Latn + {0xC68C0000u, 46u}, // mur -> Latn + {0xCA8C0000u, 46u}, // mus -> Latn + {0x82AC0000u, 46u}, // mva -> Latn + {0xB6AC0000u, 46u}, // mvn -> Latn {0xE2AC0000u, 1u}, // mvy -> Arab - {0xAACC0000u, 44u}, // mwk -> Latn - {0xC6CC0000u, 17u}, // mwr -> Deva - {0xD6CC0000u, 44u}, // mwv -> Latn - {0xDACC0000u, 33u}, // mww -> Hmnp - {0x8AEC0000u, 44u}, // mxc -> Latn - {0xB2EC0000u, 44u}, // mxm -> Latn - {0x6D790000u, 56u}, // my -> Mymr - {0xAB0C0000u, 44u}, // myk -> Latn - {0xB30C0000u, 19u}, // mym -> Ethi - {0xD70C0000u, 16u}, // myv -> Cyrl - {0xDB0C0000u, 44u}, // myw -> Latn - {0xDF0C0000u, 44u}, // myx -> Latn - {0xE70C0000u, 50u}, // myz -> Mand - {0xAB2C0000u, 44u}, // mzk -> Latn - {0xB32C0000u, 44u}, // mzm -> Latn + {0xAACC0000u, 46u}, // mwk -> Latn + {0xC6CC0000u, 18u}, // mwr -> Deva + {0xD6CC0000u, 46u}, // mwv -> Latn + {0xDACC0000u, 34u}, // mww -> Hmnp + {0x8AEC0000u, 46u}, // mxc -> Latn + {0xB2EC0000u, 46u}, // mxm -> Latn + {0x6D790000u, 58u}, // my -> Mymr + {0xAB0C0000u, 46u}, // myk -> Latn + {0xB30C0000u, 20u}, // mym -> Ethi + {0xD70C0000u, 17u}, // myv -> Cyrl + {0xDB0C0000u, 46u}, // myw -> Latn + {0xDF0C0000u, 46u}, // myx -> Latn + {0xE70C0000u, 52u}, // myz -> Mand + {0xAB2C0000u, 46u}, // mzk -> Latn + {0xB32C0000u, 46u}, // mzm -> Latn {0xB72C0000u, 1u}, // mzn -> Arab - {0xBF2C0000u, 44u}, // mzp -> Latn - {0xDB2C0000u, 44u}, // mzw -> Latn - {0xE72C0000u, 44u}, // mzz -> Latn - {0x6E610000u, 44u}, // na -> Latn - {0x880D0000u, 44u}, // nac -> Latn - {0x940D0000u, 44u}, // naf -> Latn - {0xA80D0000u, 44u}, // nak -> Latn - {0xB40D0000u, 27u}, // nan -> Hans - {0xBC0D0000u, 44u}, // nap -> Latn - {0xC00D0000u, 44u}, // naq -> Latn - {0xC80D0000u, 44u}, // nas -> Latn - {0x6E620000u, 44u}, // nb -> Latn - {0x804D0000u, 44u}, // nca -> Latn - {0x904D0000u, 44u}, // nce -> Latn - {0x944D0000u, 44u}, // ncf -> Latn - {0x9C4D0000u, 44u}, // nch -> Latn - {0xB84D0000u, 44u}, // nco -> Latn - {0xD04D0000u, 44u}, // ncu -> Latn - {0x6E640000u, 44u}, // nd -> Latn - {0x886D0000u, 44u}, // ndc -> Latn - {0xC86D0000u, 44u}, // nds -> Latn - {0x6E650000u, 17u}, // ne -> Deva - {0x848D0000u, 44u}, // neb -> Latn - {0xD88D0000u, 17u}, // new -> Deva - {0xDC8D0000u, 44u}, // nex -> Latn - {0xC4AD0000u, 44u}, // nfr -> Latn - {0x6E670000u, 44u}, // ng -> Latn - {0x80CD0000u, 44u}, // nga -> Latn - {0x84CD0000u, 44u}, // ngb -> Latn - {0xACCD0000u, 44u}, // ngl -> Latn - {0x84ED0000u, 44u}, // nhb -> Latn - {0x90ED0000u, 44u}, // nhe -> Latn - {0xD8ED0000u, 44u}, // nhw -> Latn - {0x950D0000u, 44u}, // nif -> Latn - {0xA10D0000u, 44u}, // nii -> Latn - {0xA50D0000u, 44u}, // nij -> Latn - {0xB50D0000u, 44u}, // nin -> Latn - {0xD10D0000u, 44u}, // niu -> Latn - {0xE10D0000u, 44u}, // niy -> Latn - {0xE50D0000u, 44u}, // niz -> Latn - {0xB92D0000u, 44u}, // njo -> Latn - {0x994D0000u, 44u}, // nkg -> Latn - {0xB94D0000u, 44u}, // nko -> Latn - {0x6E6C0000u, 44u}, // nl -> Latn - {0x998D0000u, 44u}, // nmg -> Latn - {0xE58D0000u, 44u}, // nmz -> Latn - {0x6E6E0000u, 44u}, // nn -> Latn - {0x95AD0000u, 44u}, // nnf -> Latn - {0x9DAD0000u, 44u}, // nnh -> Latn - {0xA9AD0000u, 44u}, // nnk -> Latn - {0xB1AD0000u, 44u}, // nnm -> Latn - {0xBDAD0000u, 91u}, // nnp -> Wcho - {0x6E6F0000u, 44u}, // no -> Latn - {0x8DCD0000u, 42u}, // nod -> Lana - {0x91CD0000u, 17u}, // noe -> Deva - {0xB5CD0000u, 69u}, // non -> Runr - {0xBDCD0000u, 44u}, // nop -> Latn - {0xD1CD0000u, 44u}, // nou -> Latn - {0xBA0D0000u, 58u}, // nqo -> Nkoo - {0x6E720000u, 44u}, // nr -> Latn - {0x862D0000u, 44u}, // nrb -> Latn + {0xBF2C0000u, 46u}, // mzp -> Latn + {0xDB2C0000u, 46u}, // mzw -> Latn + {0xE72C0000u, 46u}, // mzz -> Latn + {0x6E610000u, 46u}, // na -> Latn + {0x880D0000u, 46u}, // nac -> Latn + {0x940D0000u, 46u}, // naf -> Latn + {0xA80D0000u, 46u}, // nak -> Latn + {0xB40D0000u, 28u}, // nan -> Hans + {0xBC0D0000u, 46u}, // nap -> Latn + {0xC00D0000u, 46u}, // naq -> Latn + {0xC80D0000u, 46u}, // nas -> Latn + {0x6E620000u, 46u}, // nb -> Latn + {0x804D0000u, 46u}, // nca -> Latn + {0x904D0000u, 46u}, // nce -> Latn + {0x944D0000u, 46u}, // ncf -> Latn + {0x9C4D0000u, 46u}, // nch -> Latn + {0xB84D0000u, 46u}, // nco -> Latn + {0xD04D0000u, 46u}, // ncu -> Latn + {0x6E640000u, 46u}, // nd -> Latn + {0x886D0000u, 46u}, // ndc -> Latn + {0xC86D0000u, 46u}, // nds -> Latn + {0x6E650000u, 18u}, // ne -> Deva + {0x848D0000u, 46u}, // neb -> Latn + {0xD88D0000u, 18u}, // new -> Deva + {0xDC8D0000u, 46u}, // nex -> Latn + {0xC4AD0000u, 46u}, // nfr -> Latn + {0x6E670000u, 46u}, // ng -> Latn + {0x80CD0000u, 46u}, // nga -> Latn + {0x84CD0000u, 46u}, // ngb -> Latn + {0xACCD0000u, 46u}, // ngl -> Latn + {0x84ED0000u, 46u}, // nhb -> Latn + {0x90ED0000u, 46u}, // nhe -> Latn + {0xD8ED0000u, 46u}, // nhw -> Latn + {0x950D0000u, 46u}, // nif -> Latn + {0xA10D0000u, 46u}, // nii -> Latn + {0xA50D0000u, 46u}, // nij -> Latn + {0xB50D0000u, 46u}, // nin -> Latn + {0xD10D0000u, 46u}, // niu -> Latn + {0xE10D0000u, 46u}, // niy -> Latn + {0xE50D0000u, 46u}, // niz -> Latn + {0xB92D0000u, 46u}, // njo -> Latn + {0x994D0000u, 46u}, // nkg -> Latn + {0xB94D0000u, 46u}, // nko -> Latn + {0x6E6C0000u, 46u}, // nl -> Latn + {0x998D0000u, 46u}, // nmg -> Latn + {0xE58D0000u, 46u}, // nmz -> Latn + {0x6E6E0000u, 46u}, // nn -> Latn + {0x95AD0000u, 46u}, // nnf -> Latn + {0x9DAD0000u, 46u}, // nnh -> Latn + {0xA9AD0000u, 46u}, // nnk -> Latn + {0xB1AD0000u, 46u}, // nnm -> Latn + {0xBDAD0000u, 93u}, // nnp -> Wcho + {0x6E6F0000u, 46u}, // no -> Latn + {0x8DCD0000u, 44u}, // nod -> Lana + {0x91CD0000u, 18u}, // noe -> Deva + {0xB5CD0000u, 71u}, // non -> Runr + {0xBDCD0000u, 46u}, // nop -> Latn + {0xD1CD0000u, 46u}, // nou -> Latn + {0xBA0D0000u, 60u}, // nqo -> Nkoo + {0x6E720000u, 46u}, // nr -> Latn + {0x862D0000u, 46u}, // nrb -> Latn {0xAA4D0000u, 10u}, // nsk -> Cans - {0xB64D0000u, 44u}, // nsn -> Latn - {0xBA4D0000u, 44u}, // nso -> Latn - {0xCA4D0000u, 44u}, // nss -> Latn - {0xB26D0000u, 44u}, // ntm -> Latn - {0xC66D0000u, 44u}, // ntr -> Latn - {0xA28D0000u, 44u}, // nui -> Latn - {0xBE8D0000u, 44u}, // nup -> Latn - {0xCA8D0000u, 44u}, // nus -> Latn - {0xD68D0000u, 44u}, // nuv -> Latn - {0xDE8D0000u, 44u}, // nux -> Latn - {0x6E760000u, 44u}, // nv -> Latn - {0x86CD0000u, 44u}, // nwb -> Latn - {0xC2ED0000u, 44u}, // nxq -> Latn - {0xC6ED0000u, 44u}, // nxr -> Latn - {0x6E790000u, 44u}, // ny -> Latn - {0xB30D0000u, 44u}, // nym -> Latn - {0xB70D0000u, 44u}, // nyn -> Latn - {0xA32D0000u, 44u}, // nzi -> Latn - {0x6F630000u, 44u}, // oc -> Latn - {0x88CE0000u, 44u}, // ogc -> Latn - {0xC54E0000u, 44u}, // okr -> Latn - {0xD54E0000u, 44u}, // okv -> Latn - {0x6F6D0000u, 44u}, // om -> Latn - {0x99AE0000u, 44u}, // ong -> Latn - {0xB5AE0000u, 44u}, // onn -> Latn - {0xC9AE0000u, 44u}, // ons -> Latn - {0xB1EE0000u, 44u}, // opm -> Latn - {0x6F720000u, 62u}, // or -> Orya - {0xBA2E0000u, 44u}, // oro -> Latn + {0xB64D0000u, 46u}, // nsn -> Latn + {0xBA4D0000u, 46u}, // nso -> Latn + {0xCA4D0000u, 46u}, // nss -> Latn + {0xB26D0000u, 46u}, // ntm -> Latn + {0xC66D0000u, 46u}, // ntr -> Latn + {0xA28D0000u, 46u}, // nui -> Latn + {0xBE8D0000u, 46u}, // nup -> Latn + {0xCA8D0000u, 46u}, // nus -> Latn + {0xD68D0000u, 46u}, // nuv -> Latn + {0xDE8D0000u, 46u}, // nux -> Latn + {0x6E760000u, 46u}, // nv -> Latn + {0x86CD0000u, 46u}, // nwb -> Latn + {0xC2ED0000u, 46u}, // nxq -> Latn + {0xC6ED0000u, 46u}, // nxr -> Latn + {0x6E790000u, 46u}, // ny -> Latn + {0xB30D0000u, 46u}, // nym -> Latn + {0xB70D0000u, 46u}, // nyn -> Latn + {0xA32D0000u, 46u}, // nzi -> Latn + {0x6F630000u, 46u}, // oc -> Latn + {0x88CE0000u, 46u}, // ogc -> Latn + {0xC54E0000u, 46u}, // okr -> Latn + {0xD54E0000u, 46u}, // okv -> Latn + {0x6F6D0000u, 46u}, // om -> Latn + {0x99AE0000u, 46u}, // ong -> Latn + {0xB5AE0000u, 46u}, // onn -> Latn + {0xC9AE0000u, 46u}, // ons -> Latn + {0xB1EE0000u, 46u}, // opm -> Latn + {0x6F720000u, 64u}, // or -> Orya + {0xBA2E0000u, 46u}, // oro -> Latn {0xD22E0000u, 1u}, // oru -> Arab - {0x6F730000u, 16u}, // os -> Cyrl - {0x824E0000u, 63u}, // osa -> Osge + {0x6F730000u, 17u}, // os -> Cyrl + {0x824E0000u, 65u}, // osa -> Osge {0x826E0000u, 1u}, // ota -> Arab - {0xAA6E0000u, 61u}, // otk -> Orkh - {0xB32E0000u, 44u}, // ozm -> Latn - {0x70610000u, 26u}, // pa -> Guru + {0xAA6E0000u, 63u}, // otk -> Orkh + {0xB32E0000u, 46u}, // ozm -> Latn + {0x70610000u, 27u}, // pa -> Guru {0x7061504Bu, 1u}, // pa-PK -> Arab - {0x980F0000u, 44u}, // pag -> Latn - {0xAC0F0000u, 65u}, // pal -> Phli - {0xB00F0000u, 44u}, // pam -> Latn - {0xBC0F0000u, 44u}, // pap -> Latn - {0xD00F0000u, 44u}, // pau -> Latn - {0xA02F0000u, 44u}, // pbi -> Latn - {0x8C4F0000u, 44u}, // pcd -> Latn - {0xB04F0000u, 44u}, // pcm -> Latn - {0x886F0000u, 44u}, // pdc -> Latn - {0xCC6F0000u, 44u}, // pdt -> Latn - {0x8C8F0000u, 44u}, // ped -> Latn - {0xB88F0000u, 92u}, // peo -> Xpeo - {0xDC8F0000u, 44u}, // pex -> Latn - {0xACAF0000u, 44u}, // pfl -> Latn + {0x980F0000u, 46u}, // pag -> Latn + {0xAC0F0000u, 67u}, // pal -> Phli + {0xB00F0000u, 46u}, // pam -> Latn + {0xBC0F0000u, 46u}, // pap -> Latn + {0xD00F0000u, 46u}, // pau -> Latn + {0xA02F0000u, 46u}, // pbi -> Latn + {0x8C4F0000u, 46u}, // pcd -> Latn + {0xB04F0000u, 46u}, // pcm -> Latn + {0x886F0000u, 46u}, // pdc -> Latn + {0xCC6F0000u, 46u}, // pdt -> Latn + {0x8C8F0000u, 46u}, // ped -> Latn + {0xB88F0000u, 94u}, // peo -> Xpeo + {0xDC8F0000u, 46u}, // pex -> Latn + {0xACAF0000u, 46u}, // pfl -> Latn {0xACEF0000u, 1u}, // phl -> Arab - {0xB4EF0000u, 66u}, // phn -> Phnx - {0xAD0F0000u, 44u}, // pil -> Latn - {0xBD0F0000u, 44u}, // pip -> Latn + {0xB4EF0000u, 68u}, // phn -> Phnx + {0xAD0F0000u, 46u}, // pil -> Latn + {0xBD0F0000u, 46u}, // pip -> Latn {0x814F0000u, 8u}, // pka -> Brah - {0xB94F0000u, 44u}, // pko -> Latn - {0x706C0000u, 44u}, // pl -> Latn - {0x816F0000u, 44u}, // pla -> Latn - {0xC98F0000u, 44u}, // pms -> Latn - {0x99AF0000u, 44u}, // png -> Latn - {0xB5AF0000u, 44u}, // pnn -> Latn - {0xCDAF0000u, 24u}, // pnt -> Grek - {0xB5CF0000u, 44u}, // pon -> Latn - {0x81EF0000u, 17u}, // ppa -> Deva - {0xB9EF0000u, 44u}, // ppo -> Latn - {0x822F0000u, 38u}, // pra -> Khar + {0xB94F0000u, 46u}, // pko -> Latn + {0x706C0000u, 46u}, // pl -> Latn + {0x816F0000u, 46u}, // pla -> Latn + {0xC98F0000u, 46u}, // pms -> Latn + {0x99AF0000u, 46u}, // png -> Latn + {0xB5AF0000u, 46u}, // pnn -> Latn + {0xCDAF0000u, 25u}, // pnt -> Grek + {0xB5CF0000u, 46u}, // pon -> Latn + {0x81EF0000u, 18u}, // ppa -> Deva + {0xB9EF0000u, 46u}, // ppo -> Latn + {0x822F0000u, 39u}, // pra -> Khar {0x8E2F0000u, 1u}, // prd -> Arab - {0x9A2F0000u, 44u}, // prg -> Latn + {0x9A2F0000u, 46u}, // prg -> Latn {0x70730000u, 1u}, // ps -> Arab - {0xCA4F0000u, 44u}, // pss -> Latn - {0x70740000u, 44u}, // pt -> Latn - {0xBE6F0000u, 44u}, // ptp -> Latn - {0xD28F0000u, 44u}, // puu -> Latn - {0x82CF0000u, 44u}, // pwa -> Latn - {0x71750000u, 44u}, // qu -> Latn - {0x8A900000u, 44u}, // quc -> Latn - {0x9A900000u, 44u}, // qug -> Latn - {0xA0110000u, 44u}, // rai -> Latn - {0xA4110000u, 17u}, // raj -> Deva - {0xB8110000u, 44u}, // rao -> Latn - {0x94510000u, 44u}, // rcf -> Latn - {0xA4910000u, 44u}, // rej -> Latn - {0xAC910000u, 44u}, // rel -> Latn - {0xC8910000u, 44u}, // res -> Latn - {0xB4D10000u, 44u}, // rgn -> Latn + {0xCA4F0000u, 46u}, // pss -> Latn + {0x70740000u, 46u}, // pt -> Latn + {0xBE6F0000u, 46u}, // ptp -> Latn + {0xD28F0000u, 46u}, // puu -> Latn + {0x82CF0000u, 46u}, // pwa -> Latn + {0x71750000u, 46u}, // qu -> Latn + {0x8A900000u, 46u}, // quc -> Latn + {0x9A900000u, 46u}, // qug -> Latn + {0xA0110000u, 46u}, // rai -> Latn + {0xA4110000u, 18u}, // raj -> Deva + {0xB8110000u, 46u}, // rao -> Latn + {0x94510000u, 46u}, // rcf -> Latn + {0xA4910000u, 46u}, // rej -> Latn + {0xAC910000u, 46u}, // rel -> Latn + {0xC8910000u, 46u}, // res -> Latn + {0xB4D10000u, 46u}, // rgn -> Latn {0x98F10000u, 1u}, // rhg -> Arab - {0x81110000u, 44u}, // ria -> Latn - {0x95110000u, 85u}, // rif -> Tfng - {0x95114E4Cu, 44u}, // rif-NL -> Latn - {0xC9310000u, 17u}, // rjs -> Deva + {0x81110000u, 46u}, // ria -> Latn + {0x95110000u, 87u}, // rif -> Tfng + {0x95114E4Cu, 46u}, // rif-NL -> Latn + {0xC9310000u, 18u}, // rjs -> Deva {0xCD510000u, 7u}, // rkt -> Beng - {0x726D0000u, 44u}, // rm -> Latn - {0x95910000u, 44u}, // rmf -> Latn - {0xB9910000u, 44u}, // rmo -> Latn + {0x726D0000u, 46u}, // rm -> Latn + {0x95910000u, 46u}, // rmf -> Latn + {0xB9910000u, 46u}, // rmo -> Latn {0xCD910000u, 1u}, // rmt -> Arab - {0xD1910000u, 44u}, // rmu -> Latn - {0x726E0000u, 44u}, // rn -> Latn - {0x81B10000u, 44u}, // rna -> Latn - {0x99B10000u, 44u}, // rng -> Latn - {0x726F0000u, 44u}, // ro -> Latn - {0x85D10000u, 44u}, // rob -> Latn - {0x95D10000u, 44u}, // rof -> Latn - {0xB9D10000u, 44u}, // roo -> Latn - {0xBA310000u, 44u}, // rro -> Latn - {0xB2710000u, 44u}, // rtm -> Latn - {0x72750000u, 16u}, // ru -> Cyrl - {0x92910000u, 16u}, // rue -> Cyrl - {0x9A910000u, 44u}, // rug -> Latn - {0x72770000u, 44u}, // rw -> Latn - {0xAAD10000u, 44u}, // rwk -> Latn - {0xBAD10000u, 44u}, // rwo -> Latn - {0xD3110000u, 37u}, // ryu -> Kana - {0x73610000u, 17u}, // sa -> Deva - {0x94120000u, 44u}, // saf -> Latn - {0x9C120000u, 16u}, // sah -> Cyrl - {0xC0120000u, 44u}, // saq -> Latn - {0xC8120000u, 44u}, // sas -> Latn - {0xCC120000u, 44u}, // sat -> Latn - {0xD4120000u, 44u}, // sav -> Latn - {0xE4120000u, 72u}, // saz -> Saur - {0x80320000u, 44u}, // sba -> Latn - {0x90320000u, 44u}, // sbe -> Latn - {0xBC320000u, 44u}, // sbp -> Latn - {0x73630000u, 44u}, // sc -> Latn - {0xA8520000u, 17u}, // sck -> Deva + {0xD1910000u, 46u}, // rmu -> Latn + {0x726E0000u, 46u}, // rn -> Latn + {0x81B10000u, 46u}, // rna -> Latn + {0x99B10000u, 46u}, // rng -> Latn + {0x726F0000u, 46u}, // ro -> Latn + {0x85D10000u, 46u}, // rob -> Latn + {0x95D10000u, 46u}, // rof -> Latn + {0xB9D10000u, 46u}, // roo -> Latn + {0xBA310000u, 46u}, // rro -> Latn + {0xB2710000u, 46u}, // rtm -> Latn + {0x72750000u, 17u}, // ru -> Cyrl + {0x92910000u, 17u}, // rue -> Cyrl + {0x9A910000u, 46u}, // rug -> Latn + {0x72770000u, 46u}, // rw -> Latn + {0xAAD10000u, 46u}, // rwk -> Latn + {0xBAD10000u, 46u}, // rwo -> Latn + {0xD3110000u, 38u}, // ryu -> Kana + {0x73610000u, 18u}, // sa -> Deva + {0x94120000u, 46u}, // saf -> Latn + {0x9C120000u, 17u}, // sah -> Cyrl + {0xC0120000u, 46u}, // saq -> Latn + {0xC8120000u, 46u}, // sas -> Latn + {0xCC120000u, 46u}, // sat -> Latn + {0xD4120000u, 46u}, // sav -> Latn + {0xE4120000u, 74u}, // saz -> Saur + {0x80320000u, 46u}, // sba -> Latn + {0x90320000u, 46u}, // sbe -> Latn + {0xBC320000u, 46u}, // sbp -> Latn + {0x73630000u, 46u}, // sc -> Latn + {0xA8520000u, 18u}, // sck -> Deva {0xAC520000u, 1u}, // scl -> Arab - {0xB4520000u, 44u}, // scn -> Latn - {0xB8520000u, 44u}, // sco -> Latn - {0xC8520000u, 44u}, // scs -> Latn + {0xB4520000u, 46u}, // scn -> Latn + {0xB8520000u, 46u}, // sco -> Latn + {0xC8520000u, 46u}, // scs -> Latn {0x73640000u, 1u}, // sd -> Arab - {0x88720000u, 44u}, // sdc -> Latn + {0x88720000u, 46u}, // sdc -> Latn {0x9C720000u, 1u}, // sdh -> Arab - {0x73650000u, 44u}, // se -> Latn - {0x94920000u, 44u}, // sef -> Latn - {0x9C920000u, 44u}, // seh -> Latn - {0xA0920000u, 44u}, // sei -> Latn - {0xC8920000u, 44u}, // ses -> Latn - {0x73670000u, 44u}, // sg -> Latn - {0x80D20000u, 60u}, // sga -> Ogam - {0xC8D20000u, 44u}, // sgs -> Latn - {0xD8D20000u, 19u}, // sgw -> Ethi - {0xE4D20000u, 44u}, // sgz -> Latn - {0x73680000u, 44u}, // sh -> Latn - {0xA0F20000u, 85u}, // shi -> Tfng - {0xA8F20000u, 44u}, // shk -> Latn - {0xB4F20000u, 56u}, // shn -> Mymr + {0x73650000u, 46u}, // se -> Latn + {0x94920000u, 46u}, // sef -> Latn + {0x9C920000u, 46u}, // seh -> Latn + {0xA0920000u, 46u}, // sei -> Latn + {0xC8920000u, 46u}, // ses -> Latn + {0x73670000u, 46u}, // sg -> Latn + {0x80D20000u, 62u}, // sga -> Ogam + {0xC8D20000u, 46u}, // sgs -> Latn + {0xD8D20000u, 20u}, // sgw -> Ethi + {0xE4D20000u, 46u}, // sgz -> Latn + {0x73680000u, 46u}, // sh -> Latn + {0xA0F20000u, 87u}, // shi -> Tfng + {0xA8F20000u, 46u}, // shk -> Latn + {0xB4F20000u, 58u}, // shn -> Mymr {0xD0F20000u, 1u}, // shu -> Arab - {0x73690000u, 74u}, // si -> Sinh - {0x8D120000u, 44u}, // sid -> Latn - {0x99120000u, 44u}, // sig -> Latn - {0xAD120000u, 44u}, // sil -> Latn - {0xB1120000u, 44u}, // sim -> Latn - {0xC5320000u, 44u}, // sjr -> Latn - {0x736B0000u, 44u}, // sk -> Latn - {0x89520000u, 44u}, // skc -> Latn + {0x73690000u, 76u}, // si -> Sinh + {0x8D120000u, 46u}, // sid -> Latn + {0x99120000u, 46u}, // sig -> Latn + {0xAD120000u, 46u}, // sil -> Latn + {0xB1120000u, 46u}, // sim -> Latn + {0xC5320000u, 46u}, // sjr -> Latn + {0x736B0000u, 46u}, // sk -> Latn + {0x89520000u, 46u}, // skc -> Latn {0xC5520000u, 1u}, // skr -> Arab - {0xC9520000u, 44u}, // sks -> Latn - {0x736C0000u, 44u}, // sl -> Latn - {0x8D720000u, 44u}, // sld -> Latn - {0xA1720000u, 44u}, // sli -> Latn - {0xAD720000u, 44u}, // sll -> Latn - {0xE1720000u, 44u}, // sly -> Latn - {0x736D0000u, 44u}, // sm -> Latn - {0x81920000u, 44u}, // sma -> Latn - {0xA5920000u, 44u}, // smj -> Latn - {0xB5920000u, 44u}, // smn -> Latn - {0xBD920000u, 70u}, // smp -> Samr - {0xC1920000u, 44u}, // smq -> Latn - {0xC9920000u, 44u}, // sms -> Latn - {0x736E0000u, 44u}, // sn -> Latn - {0x89B20000u, 44u}, // snc -> Latn - {0xA9B20000u, 44u}, // snk -> Latn - {0xBDB20000u, 44u}, // snp -> Latn - {0xDDB20000u, 44u}, // snx -> Latn - {0xE1B20000u, 44u}, // sny -> Latn - {0x736F0000u, 44u}, // so -> Latn - {0x99D20000u, 75u}, // sog -> Sogd - {0xA9D20000u, 44u}, // sok -> Latn - {0xC1D20000u, 44u}, // soq -> Latn - {0xD1D20000u, 87u}, // sou -> Thai - {0xE1D20000u, 44u}, // soy -> Latn - {0x8DF20000u, 44u}, // spd -> Latn - {0xADF20000u, 44u}, // spl -> Latn - {0xC9F20000u, 44u}, // sps -> Latn - {0x73710000u, 44u}, // sq -> Latn - {0x73720000u, 16u}, // sr -> Cyrl - {0x73724D45u, 44u}, // sr-ME -> Latn - {0x7372524Fu, 44u}, // sr-RO -> Latn - {0x73725255u, 44u}, // sr-RU -> Latn - {0x73725452u, 44u}, // sr-TR -> Latn - {0x86320000u, 76u}, // srb -> Sora - {0xB6320000u, 44u}, // srn -> Latn - {0xC6320000u, 44u}, // srr -> Latn - {0xDE320000u, 17u}, // srx -> Deva - {0x73730000u, 44u}, // ss -> Latn - {0x8E520000u, 44u}, // ssd -> Latn - {0x9A520000u, 44u}, // ssg -> Latn - {0xE2520000u, 44u}, // ssy -> Latn - {0x73740000u, 44u}, // st -> Latn - {0xAA720000u, 44u}, // stk -> Latn - {0xC2720000u, 44u}, // stq -> Latn - {0x73750000u, 44u}, // su -> Latn - {0x82920000u, 44u}, // sua -> Latn - {0x92920000u, 44u}, // sue -> Latn - {0xAA920000u, 44u}, // suk -> Latn - {0xC6920000u, 44u}, // sur -> Latn - {0xCA920000u, 44u}, // sus -> Latn - {0x73760000u, 44u}, // sv -> Latn - {0x73770000u, 44u}, // sw -> Latn + {0xC9520000u, 46u}, // sks -> Latn + {0x736C0000u, 46u}, // sl -> Latn + {0x8D720000u, 46u}, // sld -> Latn + {0xA1720000u, 46u}, // sli -> Latn + {0xAD720000u, 46u}, // sll -> Latn + {0xE1720000u, 46u}, // sly -> Latn + {0x736D0000u, 46u}, // sm -> Latn + {0x81920000u, 46u}, // sma -> Latn + {0xA5920000u, 46u}, // smj -> Latn + {0xB5920000u, 46u}, // smn -> Latn + {0xBD920000u, 72u}, // smp -> Samr + {0xC1920000u, 46u}, // smq -> Latn + {0xC9920000u, 46u}, // sms -> Latn + {0x736E0000u, 46u}, // sn -> Latn + {0x89B20000u, 46u}, // snc -> Latn + {0xA9B20000u, 46u}, // snk -> Latn + {0xBDB20000u, 46u}, // snp -> Latn + {0xDDB20000u, 46u}, // snx -> Latn + {0xE1B20000u, 46u}, // sny -> Latn + {0x736F0000u, 46u}, // so -> Latn + {0x99D20000u, 77u}, // sog -> Sogd + {0xA9D20000u, 46u}, // sok -> Latn + {0xC1D20000u, 46u}, // soq -> Latn + {0xD1D20000u, 89u}, // sou -> Thai + {0xE1D20000u, 46u}, // soy -> Latn + {0x8DF20000u, 46u}, // spd -> Latn + {0xADF20000u, 46u}, // spl -> Latn + {0xC9F20000u, 46u}, // sps -> Latn + {0x73710000u, 46u}, // sq -> Latn + {0x73720000u, 17u}, // sr -> Cyrl + {0x73724D45u, 46u}, // sr-ME -> Latn + {0x7372524Fu, 46u}, // sr-RO -> Latn + {0x73725255u, 46u}, // sr-RU -> Latn + {0x73725452u, 46u}, // sr-TR -> Latn + {0x86320000u, 78u}, // srb -> Sora + {0xB6320000u, 46u}, // srn -> Latn + {0xC6320000u, 46u}, // srr -> Latn + {0xDE320000u, 18u}, // srx -> Deva + {0x73730000u, 46u}, // ss -> Latn + {0x8E520000u, 46u}, // ssd -> Latn + {0x9A520000u, 46u}, // ssg -> Latn + {0xE2520000u, 46u}, // ssy -> Latn + {0x73740000u, 46u}, // st -> Latn + {0xAA720000u, 46u}, // stk -> Latn + {0xC2720000u, 46u}, // stq -> Latn + {0x73750000u, 46u}, // su -> Latn + {0x82920000u, 46u}, // sua -> Latn + {0x92920000u, 46u}, // sue -> Latn + {0xAA920000u, 46u}, // suk -> Latn + {0xC6920000u, 46u}, // sur -> Latn + {0xCA920000u, 46u}, // sus -> Latn + {0x73760000u, 46u}, // sv -> Latn + {0x73770000u, 46u}, // sw -> Latn {0x86D20000u, 1u}, // swb -> Arab - {0x8AD20000u, 44u}, // swc -> Latn - {0x9AD20000u, 44u}, // swg -> Latn - {0xBED20000u, 44u}, // swp -> Latn - {0xD6D20000u, 17u}, // swv -> Deva - {0xB6F20000u, 44u}, // sxn -> Latn - {0xDAF20000u, 44u}, // sxw -> Latn + {0x8AD20000u, 46u}, // swc -> Latn + {0x9AD20000u, 46u}, // swg -> Latn + {0xBED20000u, 46u}, // swp -> Latn + {0xD6D20000u, 18u}, // swv -> Deva + {0xB6F20000u, 46u}, // sxn -> Latn + {0xDAF20000u, 46u}, // sxw -> Latn {0xAF120000u, 7u}, // syl -> Beng - {0xC7120000u, 78u}, // syr -> Syrc - {0xAF320000u, 44u}, // szl -> Latn - {0x74610000u, 81u}, // ta -> Taml - {0xA4130000u, 17u}, // taj -> Deva - {0xAC130000u, 44u}, // tal -> Latn - {0xB4130000u, 44u}, // tan -> Latn - {0xC0130000u, 44u}, // taq -> Latn - {0x88330000u, 44u}, // tbc -> Latn - {0x8C330000u, 44u}, // tbd -> Latn - {0x94330000u, 44u}, // tbf -> Latn - {0x98330000u, 44u}, // tbg -> Latn - {0xB8330000u, 44u}, // tbo -> Latn - {0xD8330000u, 44u}, // tbw -> Latn - {0xE4330000u, 44u}, // tbz -> Latn - {0xA0530000u, 44u}, // tci -> Latn - {0xE0530000u, 40u}, // tcy -> Knda - {0x8C730000u, 79u}, // tdd -> Tale - {0x98730000u, 17u}, // tdg -> Deva - {0x9C730000u, 17u}, // tdh -> Deva - {0xD0730000u, 44u}, // tdu -> Latn - {0x74650000u, 84u}, // te -> Telu - {0x8C930000u, 44u}, // ted -> Latn - {0xB0930000u, 44u}, // tem -> Latn - {0xB8930000u, 44u}, // teo -> Latn - {0xCC930000u, 44u}, // tet -> Latn - {0xA0B30000u, 44u}, // tfi -> Latn - {0x74670000u, 16u}, // tg -> Cyrl + {0xC7120000u, 80u}, // syr -> Syrc + {0xAF320000u, 46u}, // szl -> Latn + {0x74610000u, 83u}, // ta -> Taml + {0xA4130000u, 18u}, // taj -> Deva + {0xAC130000u, 46u}, // tal -> Latn + {0xB4130000u, 46u}, // tan -> Latn + {0xC0130000u, 46u}, // taq -> Latn + {0x88330000u, 46u}, // tbc -> Latn + {0x8C330000u, 46u}, // tbd -> Latn + {0x94330000u, 46u}, // tbf -> Latn + {0x98330000u, 46u}, // tbg -> Latn + {0xB8330000u, 46u}, // tbo -> Latn + {0xD8330000u, 46u}, // tbw -> Latn + {0xE4330000u, 46u}, // tbz -> Latn + {0xA0530000u, 46u}, // tci -> Latn + {0xE0530000u, 42u}, // tcy -> Knda + {0x8C730000u, 81u}, // tdd -> Tale + {0x98730000u, 18u}, // tdg -> Deva + {0x9C730000u, 18u}, // tdh -> Deva + {0xD0730000u, 46u}, // tdu -> Latn + {0x74650000u, 86u}, // te -> Telu + {0x8C930000u, 46u}, // ted -> Latn + {0xB0930000u, 46u}, // tem -> Latn + {0xB8930000u, 46u}, // teo -> Latn + {0xCC930000u, 46u}, // tet -> Latn + {0xA0B30000u, 46u}, // tfi -> Latn + {0x74670000u, 17u}, // tg -> Cyrl {0x7467504Bu, 1u}, // tg-PK -> Arab - {0x88D30000u, 44u}, // tgc -> Latn - {0xB8D30000u, 44u}, // tgo -> Latn - {0xD0D30000u, 44u}, // tgu -> Latn - {0x74680000u, 87u}, // th -> Thai - {0xACF30000u, 17u}, // thl -> Deva - {0xC0F30000u, 17u}, // thq -> Deva - {0xC4F30000u, 17u}, // thr -> Deva - {0x74690000u, 19u}, // ti -> Ethi - {0x95130000u, 44u}, // tif -> Latn - {0x99130000u, 19u}, // tig -> Ethi - {0xA9130000u, 44u}, // tik -> Latn - {0xB1130000u, 44u}, // tim -> Latn - {0xB9130000u, 44u}, // tio -> Latn - {0xD5130000u, 44u}, // tiv -> Latn - {0x746B0000u, 44u}, // tk -> Latn - {0xAD530000u, 44u}, // tkl -> Latn - {0xC5530000u, 44u}, // tkr -> Latn - {0xCD530000u, 17u}, // tkt -> Deva - {0x746C0000u, 44u}, // tl -> Latn - {0x95730000u, 44u}, // tlf -> Latn - {0xDD730000u, 44u}, // tlx -> Latn - {0xE1730000u, 44u}, // tly -> Latn - {0x9D930000u, 44u}, // tmh -> Latn - {0xE1930000u, 44u}, // tmy -> Latn - {0x746E0000u, 44u}, // tn -> Latn - {0x9DB30000u, 44u}, // tnh -> Latn - {0x746F0000u, 44u}, // to -> Latn - {0x95D30000u, 44u}, // tof -> Latn - {0x99D30000u, 44u}, // tog -> Latn - {0xC1D30000u, 44u}, // toq -> Latn - {0xA1F30000u, 44u}, // tpi -> Latn - {0xB1F30000u, 44u}, // tpm -> Latn - {0xE5F30000u, 44u}, // tpz -> Latn - {0xBA130000u, 44u}, // tqo -> Latn - {0x74720000u, 44u}, // tr -> Latn - {0xD2330000u, 44u}, // tru -> Latn - {0xD6330000u, 44u}, // trv -> Latn + {0x88D30000u, 46u}, // tgc -> Latn + {0xB8D30000u, 46u}, // tgo -> Latn + {0xD0D30000u, 46u}, // tgu -> Latn + {0x74680000u, 89u}, // th -> Thai + {0xACF30000u, 18u}, // thl -> Deva + {0xC0F30000u, 18u}, // thq -> Deva + {0xC4F30000u, 18u}, // thr -> Deva + {0x74690000u, 20u}, // ti -> Ethi + {0x95130000u, 46u}, // tif -> Latn + {0x99130000u, 20u}, // tig -> Ethi + {0xA9130000u, 46u}, // tik -> Latn + {0xB1130000u, 46u}, // tim -> Latn + {0xB9130000u, 46u}, // tio -> Latn + {0xD5130000u, 46u}, // tiv -> Latn + {0x746B0000u, 46u}, // tk -> Latn + {0xAD530000u, 46u}, // tkl -> Latn + {0xC5530000u, 46u}, // tkr -> Latn + {0xCD530000u, 18u}, // tkt -> Deva + {0x746C0000u, 46u}, // tl -> Latn + {0x95730000u, 46u}, // tlf -> Latn + {0xDD730000u, 46u}, // tlx -> Latn + {0xE1730000u, 46u}, // tly -> Latn + {0x9D930000u, 46u}, // tmh -> Latn + {0xE1930000u, 46u}, // tmy -> Latn + {0x746E0000u, 46u}, // tn -> Latn + {0x9DB30000u, 46u}, // tnh -> Latn + {0x746F0000u, 46u}, // to -> Latn + {0x95D30000u, 46u}, // tof -> Latn + {0x99D30000u, 46u}, // tog -> Latn + {0xC1D30000u, 46u}, // toq -> Latn + {0xA1F30000u, 46u}, // tpi -> Latn + {0xB1F30000u, 46u}, // tpm -> Latn + {0xE5F30000u, 46u}, // tpz -> Latn + {0xBA130000u, 46u}, // tqo -> Latn + {0x74720000u, 46u}, // tr -> Latn + {0xD2330000u, 46u}, // tru -> Latn + {0xD6330000u, 46u}, // trv -> Latn {0xDA330000u, 1u}, // trw -> Arab - {0x74730000u, 44u}, // ts -> Latn - {0x8E530000u, 24u}, // tsd -> Grek - {0x96530000u, 17u}, // tsf -> Deva - {0x9A530000u, 44u}, // tsg -> Latn - {0xA6530000u, 88u}, // tsj -> Tibt - {0xDA530000u, 44u}, // tsw -> Latn - {0x74740000u, 16u}, // tt -> Cyrl - {0x8E730000u, 44u}, // ttd -> Latn - {0x92730000u, 44u}, // tte -> Latn - {0xA6730000u, 44u}, // ttj -> Latn - {0xC6730000u, 44u}, // ttr -> Latn - {0xCA730000u, 87u}, // tts -> Thai - {0xCE730000u, 44u}, // ttt -> Latn - {0x9E930000u, 44u}, // tuh -> Latn - {0xAE930000u, 44u}, // tul -> Latn - {0xB2930000u, 44u}, // tum -> Latn - {0xC2930000u, 44u}, // tuq -> Latn - {0x8EB30000u, 44u}, // tvd -> Latn - {0xAEB30000u, 44u}, // tvl -> Latn - {0xD2B30000u, 44u}, // tvu -> Latn - {0x9ED30000u, 44u}, // twh -> Latn - {0xC2D30000u, 44u}, // twq -> Latn - {0x9AF30000u, 82u}, // txg -> Tang - {0x74790000u, 44u}, // ty -> Latn - {0x83130000u, 44u}, // tya -> Latn - {0xD7130000u, 16u}, // tyv -> Cyrl - {0xB3330000u, 44u}, // tzm -> Latn - {0xD0340000u, 44u}, // ubu -> Latn - {0xB0740000u, 16u}, // udm -> Cyrl + {0x74730000u, 46u}, // ts -> Latn + {0x8E530000u, 25u}, // tsd -> Grek + {0x96530000u, 18u}, // tsf -> Deva + {0x9A530000u, 46u}, // tsg -> Latn + {0xA6530000u, 90u}, // tsj -> Tibt + {0xDA530000u, 46u}, // tsw -> Latn + {0x74740000u, 17u}, // tt -> Cyrl + {0x8E730000u, 46u}, // ttd -> Latn + {0x92730000u, 46u}, // tte -> Latn + {0xA6730000u, 46u}, // ttj -> Latn + {0xC6730000u, 46u}, // ttr -> Latn + {0xCA730000u, 89u}, // tts -> Thai + {0xCE730000u, 46u}, // ttt -> Latn + {0x9E930000u, 46u}, // tuh -> Latn + {0xAE930000u, 46u}, // tul -> Latn + {0xB2930000u, 46u}, // tum -> Latn + {0xC2930000u, 46u}, // tuq -> Latn + {0x8EB30000u, 46u}, // tvd -> Latn + {0xAEB30000u, 46u}, // tvl -> Latn + {0xD2B30000u, 46u}, // tvu -> Latn + {0x9ED30000u, 46u}, // twh -> Latn + {0xC2D30000u, 46u}, // twq -> Latn + {0x9AF30000u, 84u}, // txg -> Tang + {0x74790000u, 46u}, // ty -> Latn + {0x83130000u, 46u}, // tya -> Latn + {0xD7130000u, 17u}, // tyv -> Cyrl + {0xB3330000u, 46u}, // tzm -> Latn + {0xD0340000u, 46u}, // ubu -> Latn + {0xB0740000u, 17u}, // udm -> Cyrl {0x75670000u, 1u}, // ug -> Arab - {0x75674B5Au, 16u}, // ug-KZ -> Cyrl - {0x75674D4Eu, 16u}, // ug-MN -> Cyrl - {0x80D40000u, 89u}, // uga -> Ugar - {0x756B0000u, 16u}, // uk -> Cyrl - {0xA1740000u, 44u}, // uli -> Latn - {0x85940000u, 44u}, // umb -> Latn + {0x75674B5Au, 17u}, // ug-KZ -> Cyrl + {0x75674D4Eu, 17u}, // ug-MN -> Cyrl + {0x80D40000u, 91u}, // uga -> Ugar + {0x756B0000u, 17u}, // uk -> Cyrl + {0xA1740000u, 46u}, // uli -> Latn + {0x85940000u, 46u}, // umb -> Latn {0xC5B40000u, 7u}, // unr -> Beng - {0xC5B44E50u, 17u}, // unr-NP -> Deva + {0xC5B44E50u, 18u}, // unr-NP -> Deva {0xDDB40000u, 7u}, // unx -> Beng - {0xA9D40000u, 44u}, // uok -> Latn + {0xA9D40000u, 46u}, // uok -> Latn {0x75720000u, 1u}, // ur -> Arab - {0xA2340000u, 44u}, // uri -> Latn - {0xCE340000u, 44u}, // urt -> Latn - {0xDA340000u, 44u}, // urw -> Latn - {0x82540000u, 44u}, // usa -> Latn - {0xC6740000u, 44u}, // utr -> Latn - {0x9EB40000u, 44u}, // uvh -> Latn - {0xAEB40000u, 44u}, // uvl -> Latn - {0x757A0000u, 44u}, // uz -> Latn + {0xA2340000u, 46u}, // uri -> Latn + {0xCE340000u, 46u}, // urt -> Latn + {0xDA340000u, 46u}, // urw -> Latn + {0x82540000u, 46u}, // usa -> Latn + {0xC6740000u, 46u}, // utr -> Latn + {0x9EB40000u, 46u}, // uvh -> Latn + {0xAEB40000u, 46u}, // uvl -> Latn + {0x757A0000u, 46u}, // uz -> Latn {0x757A4146u, 1u}, // uz-AF -> Arab - {0x757A434Eu, 16u}, // uz-CN -> Cyrl - {0x98150000u, 44u}, // vag -> Latn - {0xA0150000u, 90u}, // vai -> Vaii - {0xB4150000u, 44u}, // van -> Latn - {0x76650000u, 44u}, // ve -> Latn - {0x88950000u, 44u}, // vec -> Latn - {0xBC950000u, 44u}, // vep -> Latn - {0x76690000u, 44u}, // vi -> Latn - {0x89150000u, 44u}, // vic -> Latn - {0xD5150000u, 44u}, // viv -> Latn - {0xC9750000u, 44u}, // vls -> Latn - {0x95950000u, 44u}, // vmf -> Latn - {0xD9950000u, 44u}, // vmw -> Latn - {0x766F0000u, 44u}, // vo -> Latn - {0xCDD50000u, 44u}, // vot -> Latn - {0xBA350000u, 44u}, // vro -> Latn - {0xB6950000u, 44u}, // vun -> Latn - {0xCE950000u, 44u}, // vut -> Latn - {0x77610000u, 44u}, // wa -> Latn - {0x90160000u, 44u}, // wae -> Latn - {0xA4160000u, 44u}, // waj -> Latn - {0xAC160000u, 19u}, // wal -> Ethi - {0xB4160000u, 44u}, // wan -> Latn - {0xC4160000u, 44u}, // war -> Latn - {0xBC360000u, 44u}, // wbp -> Latn - {0xC0360000u, 84u}, // wbq -> Telu - {0xC4360000u, 17u}, // wbr -> Deva - {0xA0560000u, 44u}, // wci -> Latn - {0xC4960000u, 44u}, // wer -> Latn - {0xA0D60000u, 44u}, // wgi -> Latn - {0x98F60000u, 44u}, // whg -> Latn - {0x85160000u, 44u}, // wib -> Latn - {0xD1160000u, 44u}, // wiu -> Latn - {0xD5160000u, 44u}, // wiv -> Latn - {0x81360000u, 44u}, // wja -> Latn - {0xA1360000u, 44u}, // wji -> Latn - {0xC9760000u, 44u}, // wls -> Latn - {0xB9960000u, 44u}, // wmo -> Latn - {0x89B60000u, 44u}, // wnc -> Latn + {0x757A434Eu, 17u}, // uz-CN -> Cyrl + {0x98150000u, 46u}, // vag -> Latn + {0xA0150000u, 92u}, // vai -> Vaii + {0xB4150000u, 46u}, // van -> Latn + {0x76650000u, 46u}, // ve -> Latn + {0x88950000u, 46u}, // vec -> Latn + {0xBC950000u, 46u}, // vep -> Latn + {0x76690000u, 46u}, // vi -> Latn + {0x89150000u, 46u}, // vic -> Latn + {0xD5150000u, 46u}, // viv -> Latn + {0xC9750000u, 46u}, // vls -> Latn + {0x95950000u, 46u}, // vmf -> Latn + {0xD9950000u, 46u}, // vmw -> Latn + {0x766F0000u, 46u}, // vo -> Latn + {0xCDD50000u, 46u}, // vot -> Latn + {0xBA350000u, 46u}, // vro -> Latn + {0xB6950000u, 46u}, // vun -> Latn + {0xCE950000u, 46u}, // vut -> Latn + {0x77610000u, 46u}, // wa -> Latn + {0x90160000u, 46u}, // wae -> Latn + {0xA4160000u, 46u}, // waj -> Latn + {0xAC160000u, 20u}, // wal -> Ethi + {0xB4160000u, 46u}, // wan -> Latn + {0xC4160000u, 46u}, // war -> Latn + {0xBC360000u, 46u}, // wbp -> Latn + {0xC0360000u, 86u}, // wbq -> Telu + {0xC4360000u, 18u}, // wbr -> Deva + {0xA0560000u, 46u}, // wci -> Latn + {0xC4960000u, 46u}, // wer -> Latn + {0xA0D60000u, 46u}, // wgi -> Latn + {0x98F60000u, 46u}, // whg -> Latn + {0x85160000u, 46u}, // wib -> Latn + {0xD1160000u, 46u}, // wiu -> Latn + {0xD5160000u, 46u}, // wiv -> Latn + {0x81360000u, 46u}, // wja -> Latn + {0xA1360000u, 46u}, // wji -> Latn + {0xC9760000u, 46u}, // wls -> Latn + {0xB9960000u, 46u}, // wmo -> Latn + {0x89B60000u, 46u}, // wnc -> Latn {0xA1B60000u, 1u}, // wni -> Arab - {0xD1B60000u, 44u}, // wnu -> Latn - {0x776F0000u, 44u}, // wo -> Latn - {0x85D60000u, 44u}, // wob -> Latn - {0xC9D60000u, 44u}, // wos -> Latn - {0xCA360000u, 44u}, // wrs -> Latn - {0x9A560000u, 21u}, // wsg -> Gong - {0xAA560000u, 44u}, // wsk -> Latn - {0xB2760000u, 17u}, // wtm -> Deva - {0xD2960000u, 27u}, // wuu -> Hans - {0xD6960000u, 44u}, // wuv -> Latn - {0x82D60000u, 44u}, // wwa -> Latn - {0xD4170000u, 44u}, // xav -> Latn - {0xA0370000u, 44u}, // xbi -> Latn + {0xD1B60000u, 46u}, // wnu -> Latn + {0x776F0000u, 46u}, // wo -> Latn + {0x85D60000u, 46u}, // wob -> Latn + {0xC9D60000u, 46u}, // wos -> Latn + {0xCA360000u, 46u}, // wrs -> Latn + {0x9A560000u, 22u}, // wsg -> Gong + {0xAA560000u, 46u}, // wsk -> Latn + {0xB2760000u, 18u}, // wtm -> Deva + {0xD2960000u, 28u}, // wuu -> Hans + {0xD6960000u, 46u}, // wuv -> Latn + {0x82D60000u, 46u}, // wwa -> Latn + {0xD4170000u, 46u}, // xav -> Latn + {0xA0370000u, 46u}, // xbi -> Latn + {0xB8570000u, 14u}, // xco -> Chrs {0xC4570000u, 11u}, // xcr -> Cari - {0xC8970000u, 44u}, // xes -> Latn - {0x78680000u, 44u}, // xh -> Latn - {0x81770000u, 44u}, // xla -> Latn - {0x89770000u, 48u}, // xlc -> Lyci - {0x8D770000u, 49u}, // xld -> Lydi - {0x95970000u, 20u}, // xmf -> Geor - {0xB5970000u, 51u}, // xmn -> Mani - {0xC5970000u, 52u}, // xmr -> Merc - {0x81B70000u, 57u}, // xna -> Narb - {0xC5B70000u, 17u}, // xnr -> Deva - {0x99D70000u, 44u}, // xog -> Latn - {0xB5D70000u, 44u}, // xon -> Latn - {0xC5F70000u, 68u}, // xpr -> Prti - {0x86370000u, 44u}, // xrb -> Latn - {0x82570000u, 71u}, // xsa -> Sarb - {0xA2570000u, 44u}, // xsi -> Latn - {0xB2570000u, 44u}, // xsm -> Latn - {0xC6570000u, 17u}, // xsr -> Deva - {0x92D70000u, 44u}, // xwe -> Latn - {0xB0180000u, 44u}, // yam -> Latn - {0xB8180000u, 44u}, // yao -> Latn - {0xBC180000u, 44u}, // yap -> Latn - {0xC8180000u, 44u}, // yas -> Latn - {0xCC180000u, 44u}, // yat -> Latn - {0xD4180000u, 44u}, // yav -> Latn - {0xE0180000u, 44u}, // yay -> Latn - {0xE4180000u, 44u}, // yaz -> Latn - {0x80380000u, 44u}, // yba -> Latn - {0x84380000u, 44u}, // ybb -> Latn - {0xE0380000u, 44u}, // yby -> Latn - {0xC4980000u, 44u}, // yer -> Latn - {0xC4D80000u, 44u}, // ygr -> Latn - {0xD8D80000u, 44u}, // ygw -> Latn - {0x79690000u, 30u}, // yi -> Hebr - {0xB9580000u, 44u}, // yko -> Latn - {0x91780000u, 44u}, // yle -> Latn - {0x99780000u, 44u}, // ylg -> Latn - {0xAD780000u, 44u}, // yll -> Latn - {0xAD980000u, 44u}, // yml -> Latn - {0x796F0000u, 44u}, // yo -> Latn - {0xB5D80000u, 44u}, // yon -> Latn - {0x86380000u, 44u}, // yrb -> Latn - {0x92380000u, 44u}, // yre -> Latn - {0xAE380000u, 44u}, // yrl -> Latn - {0xCA580000u, 44u}, // yss -> Latn - {0x82980000u, 44u}, // yua -> Latn - {0x92980000u, 28u}, // yue -> Hant - {0x9298434Eu, 27u}, // yue-CN -> Hans - {0xA6980000u, 44u}, // yuj -> Latn - {0xCE980000u, 44u}, // yut -> Latn - {0xDA980000u, 44u}, // yuw -> Latn - {0x7A610000u, 44u}, // za -> Latn - {0x98190000u, 44u}, // zag -> Latn + {0xC8970000u, 46u}, // xes -> Latn + {0x78680000u, 46u}, // xh -> Latn + {0x81770000u, 46u}, // xla -> Latn + {0x89770000u, 50u}, // xlc -> Lyci + {0x8D770000u, 51u}, // xld -> Lydi + {0x95970000u, 21u}, // xmf -> Geor + {0xB5970000u, 53u}, // xmn -> Mani + {0xC5970000u, 54u}, // xmr -> Merc + {0x81B70000u, 59u}, // xna -> Narb + {0xC5B70000u, 18u}, // xnr -> Deva + {0x99D70000u, 46u}, // xog -> Latn + {0xB5D70000u, 46u}, // xon -> Latn + {0xC5F70000u, 70u}, // xpr -> Prti + {0x86370000u, 46u}, // xrb -> Latn + {0x82570000u, 73u}, // xsa -> Sarb + {0xA2570000u, 46u}, // xsi -> Latn + {0xB2570000u, 46u}, // xsm -> Latn + {0xC6570000u, 18u}, // xsr -> Deva + {0x92D70000u, 46u}, // xwe -> Latn + {0xB0180000u, 46u}, // yam -> Latn + {0xB8180000u, 46u}, // yao -> Latn + {0xBC180000u, 46u}, // yap -> Latn + {0xC8180000u, 46u}, // yas -> Latn + {0xCC180000u, 46u}, // yat -> Latn + {0xD4180000u, 46u}, // yav -> Latn + {0xE0180000u, 46u}, // yay -> Latn + {0xE4180000u, 46u}, // yaz -> Latn + {0x80380000u, 46u}, // yba -> Latn + {0x84380000u, 46u}, // ybb -> Latn + {0xE0380000u, 46u}, // yby -> Latn + {0xC4980000u, 46u}, // yer -> Latn + {0xC4D80000u, 46u}, // ygr -> Latn + {0xD8D80000u, 46u}, // ygw -> Latn + {0x79690000u, 31u}, // yi -> Hebr + {0xB9580000u, 46u}, // yko -> Latn + {0x91780000u, 46u}, // yle -> Latn + {0x99780000u, 46u}, // ylg -> Latn + {0xAD780000u, 46u}, // yll -> Latn + {0xAD980000u, 46u}, // yml -> Latn + {0x796F0000u, 46u}, // yo -> Latn + {0xB5D80000u, 46u}, // yon -> Latn + {0x86380000u, 46u}, // yrb -> Latn + {0x92380000u, 46u}, // yre -> Latn + {0xAE380000u, 46u}, // yrl -> Latn + {0xCA580000u, 46u}, // yss -> Latn + {0x82980000u, 46u}, // yua -> Latn + {0x92980000u, 29u}, // yue -> Hant + {0x9298434Eu, 28u}, // yue-CN -> Hans + {0xA6980000u, 46u}, // yuj -> Latn + {0xCE980000u, 46u}, // yut -> Latn + {0xDA980000u, 46u}, // yuw -> Latn + {0x7A610000u, 46u}, // za -> Latn + {0x98190000u, 46u}, // zag -> Latn {0xA4790000u, 1u}, // zdj -> Arab - {0x80990000u, 44u}, // zea -> Latn - {0x9CD90000u, 85u}, // zgh -> Tfng - {0x7A680000u, 27u}, // zh -> Hans - {0x7A684155u, 28u}, // zh-AU -> Hant - {0x7A68424Eu, 28u}, // zh-BN -> Hant - {0x7A684742u, 28u}, // zh-GB -> Hant - {0x7A684746u, 28u}, // zh-GF -> Hant - {0x7A68484Bu, 28u}, // zh-HK -> Hant - {0x7A684944u, 28u}, // zh-ID -> Hant - {0x7A684D4Fu, 28u}, // zh-MO -> Hant - {0x7A684D59u, 28u}, // zh-MY -> Hant - {0x7A685041u, 28u}, // zh-PA -> Hant - {0x7A685046u, 28u}, // zh-PF -> Hant - {0x7A685048u, 28u}, // zh-PH -> Hant - {0x7A685352u, 28u}, // zh-SR -> Hant - {0x7A685448u, 28u}, // zh-TH -> Hant - {0x7A685457u, 28u}, // zh-TW -> Hant - {0x7A685553u, 28u}, // zh-US -> Hant - {0x7A68564Eu, 28u}, // zh-VN -> Hant - {0xDCF90000u, 59u}, // zhx -> Nshu - {0x81190000u, 44u}, // zia -> Latn - {0xB1790000u, 44u}, // zlm -> Latn - {0xA1990000u, 44u}, // zmi -> Latn - {0x91B90000u, 44u}, // zne -> Latn - {0x7A750000u, 44u}, // zu -> Latn - {0x83390000u, 44u}, // zza -> Latn + {0x80990000u, 46u}, // zea -> Latn + {0x9CD90000u, 87u}, // zgh -> Tfng + {0x7A680000u, 28u}, // zh -> Hans + {0x7A684155u, 29u}, // zh-AU -> Hant + {0x7A68424Eu, 29u}, // zh-BN -> Hant + {0x7A684742u, 29u}, // zh-GB -> Hant + {0x7A684746u, 29u}, // zh-GF -> Hant + {0x7A68484Bu, 29u}, // zh-HK -> Hant + {0x7A684944u, 29u}, // zh-ID -> Hant + {0x7A684D4Fu, 29u}, // zh-MO -> Hant + {0x7A684D59u, 29u}, // zh-MY -> Hant + {0x7A685041u, 29u}, // zh-PA -> Hant + {0x7A685046u, 29u}, // zh-PF -> Hant + {0x7A685048u, 29u}, // zh-PH -> Hant + {0x7A685352u, 29u}, // zh-SR -> Hant + {0x7A685448u, 29u}, // zh-TH -> Hant + {0x7A685457u, 29u}, // zh-TW -> Hant + {0x7A685553u, 29u}, // zh-US -> Hant + {0x7A68564Eu, 29u}, // zh-VN -> Hant + {0xDCF90000u, 61u}, // zhx -> Nshu + {0x81190000u, 46u}, // zia -> Latn + {0xCD590000u, 41u}, // zkt -> Kits + {0xB1790000u, 46u}, // zlm -> Latn + {0xA1990000u, 46u}, // zmi -> Latn + {0x91B90000u, 46u}, // zne -> Latn + {0x7A750000u, 46u}, // zu -> Latn + {0x83390000u, 46u}, // zza -> Latn }); std::unordered_set REPRESENTATIVE_LOCALES({ @@ -1829,6 +1833,7 @@ std::unordered_set REPRESENTATIVE_LOCALES({ 0xC66A4D594C61746ELLU, // ktr_Latn_MY 0x6B75495141726162LLU, // ku_Arab_IQ 0x6B7554524C61746ELLU, // ku_Latn_TR + 0x6B75474559657A69LLU, // ku_Yezi_GE 0xB28A52554379726CLLU, // kum_Cyrl_RU 0x6B7652554379726CLLU, // kv_Cyrl_RU 0xC6AA49444C61746ELLU, // kvr_Latn_ID @@ -2199,6 +2204,7 @@ std::unordered_set REPRESENTATIVE_LOCALES({ 0xB276494E44657661LLU, // wtm_Deva_IN 0xD296434E48616E73LLU, // wuu_Hans_CN 0xD41742524C61746ELLU, // xav_Latn_BR + 0xB857555A43687273LLU, // xco_Chrs_UZ 0xC457545243617269LLU, // xcr_Cari_TR 0x78685A414C61746ELLU, // xh_Latn_ZA 0x897754524C796369LLU, // xlc_Lyci_TR @@ -2231,6 +2237,7 @@ std::unordered_set REPRESENTATIVE_LOCALES({ 0x7A68434E48616E73LLU, // zh_Hans_CN 0x7A68545748616E74LLU, // zh_Hant_TW 0xDCF9434E4E736875LLU, // zhx_Nshu_CN + 0xCD59434E4B697473LLU, // zkt_Kits_CN 0xB17954474C61746ELLU, // zlm_Latn_TG 0xA1994D594C61746ELLU, // zmi_Latn_MY 0x7A755A414C61746ELLU, // zu_Latn_ZA -- cgit v1.2.3 From e35e87f51453849f78a3be41a25e1257820eda5a Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Thu, 19 Mar 2020 16:57:50 -0700 Subject: Fix bugprone-use-after-move warnings Bug: 150783499 Test: WITH_TIDY=1 make Change-Id: I185cb21521676ddbc4f2b7f098611a2efc7275e6 (cherry picked from commit c658184d366ee08cedd9ec208ea327530b52bfad) Merged-In: I185cb21521676ddbc4f2b7f098611a2efc7275e6 --- libs/androidfw/ApkAssets.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index 18934fd55bad..b2b0ec2a54f8 100644 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -77,7 +77,8 @@ std::unique_ptr ApkAssets::LoadOverlay(const std::string& idmap return {}; } - return LoadImpl({} /*fd*/, loaded_idmap->OverlayApkPath(), + auto apkPath = loaded_idmap->OverlayApkPath(); + return LoadImpl({} /*fd*/, apkPath, std::move(idmap_asset), std::move(loaded_idmap), PROPERTY_OVERLAY | (system ? PROPERTY_SYSTEM : 0U)); -- cgit v1.2.3 From ef40d2e832e900aba6dbcf8217903d7dbe689ca0 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Wed, 11 Mar 2020 10:26:08 -0700 Subject: Refactor ApkAsset loading APIs To add the partner requested ResourcesProvider#loadFromDir APIs, this change adds format type integer that allows us to reduce the number of ApkAssets loading overrides. This change also adds hidden offset and length based ResourcesProvider APIs that could not make R. Bug: 142716192 Test: atest FrameworksResourceLoaderTests Change-Id: I926fde257cae701901dcd4ca408024feae8c90a6 Merged-In: I926fde257cae701901dcd4ca408024feae8c90a6 --- libs/androidfw/ApkAssets.cpp | 230 +++++++++++++--------- libs/androidfw/Asset.cpp | 25 ++- libs/androidfw/LoadedArsc.cpp | 2 +- libs/androidfw/include/androidfw/ApkAssets.h | 91 +++++---- libs/androidfw/include/androidfw/Asset.h | 24 ++- libs/androidfw/include/androidfw/LoadedArsc.h | 20 +- libs/androidfw/tests/ApkAssets_test.cpp | 2 +- libs/androidfw/tests/AssetManager2_test.cpp | 6 +- libs/androidfw/tests/AttributeResolution_test.cpp | 2 +- libs/androidfw/tests/Idmap_test.cpp | 4 +- 10 files changed, 244 insertions(+), 162 deletions(-) (limited to 'libs') diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index b2b0ec2a54f8..946fcc03f57e 100644 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -41,28 +41,85 @@ using base::unique_fd; static const std::string kResourcesArsc("resources.arsc"); ApkAssets::ApkAssets(ZipArchiveHandle unmanaged_handle, - const std::string& path, + std::string path, time_t last_mod_time, package_property_t property_flags) - : zip_handle_(unmanaged_handle, ::CloseArchive), path_(path), last_mod_time_(last_mod_time), + : zip_handle_(unmanaged_handle, ::CloseArchive), + path_(std::move(path)), + last_mod_time_(last_mod_time), property_flags_(property_flags) { } -std::unique_ptr ApkAssets::Load(const std::string& path, bool system, - bool for_loader) { - package_property_t flags = (system ? PROPERTY_SYSTEM : 0U) | - (for_loader ? PROPERTY_LOADER : 0U); - return LoadImpl({} /*fd*/, path, nullptr, nullptr, flags); +std::unique_ptr ApkAssets::Load(const std::string& path, + const package_property_t flags) { + ::ZipArchiveHandle unmanaged_handle; + const int32_t result = ::OpenArchive(path.c_str(), &unmanaged_handle); + if (result != 0) { + LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result); + ::CloseArchive(unmanaged_handle); + return {}; + } + + return LoadImpl(unmanaged_handle, path, nullptr /*idmap_asset*/, nullptr /*loaded_idmap*/, + flags); } -std::unique_ptr ApkAssets::LoadAsSharedLibrary(const std::string& path, - bool system) { - package_property_t flags = PROPERTY_DYNAMIC | (system ? PROPERTY_SYSTEM : 0U); - return LoadImpl({} /*fd*/, path, nullptr, nullptr, flags); +std::unique_ptr ApkAssets::LoadFromFd(unique_fd fd, + const std::string& friendly_name, + const package_property_t flags, + const off64_t offset, + const off64_t length) { + CHECK(length >= kUnknownLength) << "length must be greater than or equal to " << kUnknownLength; + CHECK(length != kUnknownLength || offset == 0) << "offset must be 0 if length is " + << kUnknownLength; + + ::ZipArchiveHandle unmanaged_handle; + const int32_t result = (length == kUnknownLength) + ? ::OpenArchiveFd(fd.release(), friendly_name.c_str(), &unmanaged_handle) + : ::OpenArchiveFdRange(fd.release(), friendly_name.c_str(), &unmanaged_handle, length, + offset); + + if (result != 0) { + LOG(ERROR) << "Failed to open APK '" << friendly_name << "' through FD with offset " << offset + << " and length " << length << ": " << ::ErrorCodeString(result); + ::CloseArchive(unmanaged_handle); + return {}; + } + + return LoadImpl(unmanaged_handle, friendly_name, nullptr /*idmap_asset*/, + nullptr /*loaded_idmap*/, flags); +} + +std::unique_ptr ApkAssets::LoadTable(const std::string& path, + const package_property_t flags) { + auto resources_asset = CreateAssetFromFile(path); + if (!resources_asset) { + LOG(ERROR) << "Failed to open ARSC '" << path; + return {}; + } + + return LoadTableImpl(std::move(resources_asset), path, flags); +} + +std::unique_ptr ApkAssets::LoadTableFromFd(unique_fd fd, + const std::string& friendly_name, + const package_property_t flags, + const off64_t offset, + const off64_t length) { + auto resources_asset = CreateAssetFromFd(std::move(fd), nullptr /* path */, offset, length); + if (!resources_asset) { + LOG(ERROR) << "Failed to open ARSC '" << friendly_name << "' through FD with offset " << offset + << " and length " << length; + return {}; + } + + return LoadTableImpl(std::move(resources_asset), friendly_name, flags); } std::unique_ptr ApkAssets::LoadOverlay(const std::string& idmap_path, - bool system) { + const package_property_t flags) { + CHECK((flags & PROPERTY_LOADER) == 0U) << "Cannot load RROs through loaders"; + std::unique_ptr idmap_asset = CreateAssetFromFile(idmap_path); if (idmap_asset == nullptr) { return {}; @@ -77,75 +134,74 @@ std::unique_ptr ApkAssets::LoadOverlay(const std::string& idmap return {}; } - auto apkPath = loaded_idmap->OverlayApkPath(); - return LoadImpl({} /*fd*/, apkPath, - std::move(idmap_asset), - std::move(loaded_idmap), - PROPERTY_OVERLAY | (system ? PROPERTY_SYSTEM : 0U)); -} -std::unique_ptr ApkAssets::LoadFromFd(unique_fd fd, - const std::string& friendly_name, - bool system, bool force_shared_lib, - bool for_loader) { - package_property_t flags = (system ? PROPERTY_SYSTEM : 0U) | - (force_shared_lib ? PROPERTY_DYNAMIC : 0U) | - (for_loader ? PROPERTY_LOADER : 0U); - return LoadImpl(std::move(fd), friendly_name, nullptr /*idmap_asset*/, nullptr /*loaded_idmap*/, - flags); -} + ::ZipArchiveHandle unmanaged_handle; + auto overlay_path = loaded_idmap->OverlayApkPath(); + const int32_t result = ::OpenArchive(overlay_path.c_str(), &unmanaged_handle); + if (result != 0) { + LOG(ERROR) << "Failed to open overlay APK '" << overlay_path << "' " + << ::ErrorCodeString(result); + ::CloseArchive(unmanaged_handle); + return {}; + } -std::unique_ptr ApkAssets::LoadArsc(const std::string& path, - bool for_loader) { - return LoadArscImpl({} /*fd*/, path, for_loader ? PROPERTY_LOADER : 0U); + return LoadImpl(unmanaged_handle, overlay_path, std::move(idmap_asset), std::move(loaded_idmap), + flags | PROPERTY_OVERLAY); } -std::unique_ptr ApkAssets::LoadArsc(unique_fd fd, - const std::string& friendly_name, - bool for_loader) { - return LoadArscImpl(std::move(fd), friendly_name, for_loader ? PROPERTY_LOADER : 0U); +std::unique_ptr ApkAssets::LoadEmpty(const package_property_t flags) { + std::unique_ptr loaded_apk(new ApkAssets(nullptr, "empty", -1, flags)); + loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty(); + // Need to force a move for mingw32. + return std::move(loaded_apk); } std::unique_ptr ApkAssets::CreateAssetFromFile(const std::string& path) { unique_fd fd(base::utf8::open(path.c_str(), O_RDONLY | O_BINARY | O_CLOEXEC)); - if (fd == -1) { + if (!fd.ok()) { LOG(ERROR) << "Failed to open file '" << path << "': " << SystemErrorCodeToString(errno); return {}; } - const off64_t file_len = lseek64(fd, 0, SEEK_END); - if (file_len < 0) { - LOG(ERROR) << "Failed to get size of file '" << path << "': " << SystemErrorCodeToString(errno); - return {}; - } - - std::unique_ptr file_map = util::make_unique(); - if (!file_map->create(path.c_str(), fd, 0, static_cast(file_len), true /*readOnly*/)) { - LOG(ERROR) << "Failed to mmap file '" << path << "': " << SystemErrorCodeToString(errno); - return {}; - } - return Asset::createFromUncompressedMap(std::move(file_map), Asset::AccessMode::ACCESS_RANDOM); + return CreateAssetFromFd(std::move(fd), path.c_str()); } -std::unique_ptr ApkAssets::LoadImpl( - unique_fd fd, const std::string& path, std::unique_ptr idmap_asset, - std::unique_ptr loaded_idmap, package_property_t property_flags) { - ::ZipArchiveHandle unmanaged_handle; - int32_t result; - if (fd >= 0) { - result = - ::OpenArchiveFd(fd.release(), path.c_str(), &unmanaged_handle, true /*assume_ownership*/); - } else { - result = ::OpenArchive(path.c_str(), &unmanaged_handle); +std::unique_ptr ApkAssets::CreateAssetFromFd(base::unique_fd fd, + const char* path, + off64_t offset, + off64_t length) { + CHECK(length >= kUnknownLength) << "length must be greater than or equal to " << kUnknownLength; + CHECK(length != kUnknownLength || offset == 0) << "offset must be 0 if length is " + << kUnknownLength; + if (length == kUnknownLength) { + length = lseek64(fd, 0, SEEK_END); + if (length < 0) { + LOG(ERROR) << "Failed to get size of file '" << ((path) ? path : "anon") << "': " + << SystemErrorCodeToString(errno); + return {}; + } } - if (result != 0) { - LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result); - ::CloseArchive(unmanaged_handle); + std::unique_ptr file_map = util::make_unique(); + if (!file_map->create(path, fd, offset, static_cast(length), true /*readOnly*/)) { + LOG(ERROR) << "Failed to mmap file '" << ((path) ? path : "anon") << "': " + << SystemErrorCodeToString(errno); return {}; } - time_t last_mod_time = getFileModDate(path.c_str()); + // If `path` is set, do not pass ownership of the `fd` to the new Asset since + // Asset::openFileDescriptor can use `path` to create new file descriptors. + return Asset::createFromUncompressedMap(std::move(file_map), + (path) ? base::unique_fd(-1) : std::move(fd), + Asset::AccessMode::ACCESS_RANDOM); +} + +std::unique_ptr ApkAssets::LoadImpl(ZipArchiveHandle unmanaged_handle, + const std::string& path, + std::unique_ptr idmap_asset, + std::unique_ptr idmap, + package_property_t property_flags) { + const time_t last_mod_time = getFileModDate(path.c_str()); // Wrap the handle in a unique_ptr so it gets automatically closed. std::unique_ptr @@ -153,7 +209,7 @@ std::unique_ptr ApkAssets::LoadImpl( // Find the resource table. ::ZipEntry entry; - result = ::FindEntry(loaded_apk->zip_handle_.get(), kResourcesArsc, &entry); + int32_t result = ::FindEntry(loaded_apk->zip_handle_.get(), kResourcesArsc, &entry); if (result != 0) { // There is no resources.arsc, so create an empty LoadedArsc and return. loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty(); @@ -173,7 +229,7 @@ std::unique_ptr ApkAssets::LoadImpl( // Must retain ownership of the IDMAP Asset so that all pointers to its mmapped data remain valid. loaded_apk->idmap_asset_ = std::move(idmap_asset); - loaded_apk->loaded_idmap_ = std::move(loaded_idmap); + loaded_apk->loaded_idmap_ = std::move(idmap); const StringPiece data( reinterpret_cast(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)), @@ -189,24 +245,10 @@ std::unique_ptr ApkAssets::LoadImpl( return std::move(loaded_apk); } -std::unique_ptr ApkAssets::LoadArscImpl(unique_fd fd, - const std::string& path, - package_property_t property_flags) { - std::unique_ptr resources_asset; - - if (fd >= 0) { - resources_asset = std::unique_ptr(Asset::createFromFd(fd.release(), nullptr, - Asset::AccessMode::ACCESS_BUFFER)); - } else { - resources_asset = CreateAssetFromFile(path); - } - - if (resources_asset == nullptr) { - LOG(ERROR) << "Failed to open ARSC '" << path; - return {}; - } - - time_t last_mod_time = getFileModDate(path.c_str()); +std::unique_ptr ApkAssets::LoadTableImpl(std::unique_ptr resources_asset, + const std::string& path, + package_property_t property_flags) { + const time_t last_mod_time = getFileModDate(path.c_str()); std::unique_ptr loaded_apk( new ApkAssets(nullptr, path, last_mod_time, property_flags)); @@ -225,13 +267,6 @@ std::unique_ptr ApkAssets::LoadArscImpl(unique_fd fd, return std::move(loaded_apk); } -std::unique_ptr ApkAssets::LoadEmpty(bool for_loader) { - std::unique_ptr loaded_apk(new ApkAssets(nullptr, "", -1, for_loader)); - loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty(); - // Need to force a move for mingw32. - return std::move(loaded_apk); -} - std::unique_ptr ApkAssets::Open(const std::string& path, Asset::AccessMode mode) const { // If this is a resource loader from an .arsc, there will be no zip handle if (zip_handle_ == nullptr) { @@ -244,10 +279,12 @@ std::unique_ptr ApkAssets::Open(const std::string& path, Asset::AccessMod return {}; } + const int fd = ::GetFileDescriptor(zip_handle_.get()); + const off64_t fd_offset = ::GetFileDescriptorOffset(zip_handle_.get()); if (entry.method == kCompressDeflated) { std::unique_ptr map = util::make_unique(); - if (!map->create(path_.c_str(), ::GetFileDescriptor(zip_handle_.get()), entry.offset, - entry.compressed_length, true /*readOnly*/)) { + if (!map->create(path_.c_str(), fd, fd_offset + entry.offset, entry.compressed_length, + true /*readOnly*/)) { LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'"; return {}; } @@ -261,13 +298,18 @@ std::unique_ptr ApkAssets::Open(const std::string& path, Asset::AccessMod return asset; } else { std::unique_ptr map = util::make_unique(); - if (!map->create(path_.c_str(), ::GetFileDescriptor(zip_handle_.get()), entry.offset, - entry.uncompressed_length, true /*readOnly*/)) { + if (!map->create(path_.c_str(), fd, fd_offset + entry.offset, entry.uncompressed_length, + true /*readOnly*/)) { LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'"; return {}; } - std::unique_ptr asset = Asset::createFromUncompressedMap(std::move(map), mode); + // TODO: apks created from file descriptors residing in RAM currently cannot open file + // descriptors to the assets they contain. This is because the Asset::openFileDeescriptor uses + // the zip path on disk to create a new file descriptor. This is fixed in a future change + // in the change topic. + std::unique_ptr asset = Asset::createFromUncompressedMap(std::move(map), + unique_fd(-1) /* fd*/, mode); if (asset == nullptr) { LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'"; return {}; diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp index c132f343713f..cd30c184d5a4 100644 --- a/libs/androidfw/Asset.cpp +++ b/libs/androidfw/Asset.cpp @@ -298,14 +298,13 @@ Asset::Asset(void) /* * Create a new Asset from a memory mapping. */ -/*static*/ Asset* Asset::createFromUncompressedMap(FileMap* dataMap, - AccessMode mode) +/*static*/ Asset* Asset::createFromUncompressedMap(FileMap* dataMap, AccessMode mode) { _FileAsset* pAsset; status_t result; pAsset = new _FileAsset; - result = pAsset->openChunk(dataMap); + result = pAsset->openChunk(dataMap, base::unique_fd(-1)); if (result != NO_ERROR) { delete pAsset; return NULL; @@ -316,11 +315,11 @@ Asset::Asset(void) } /*static*/ std::unique_ptr Asset::createFromUncompressedMap(std::unique_ptr dataMap, - AccessMode mode) + base::unique_fd fd, AccessMode mode) { std::unique_ptr<_FileAsset> pAsset = util::make_unique<_FileAsset>(); - status_t result = pAsset->openChunk(dataMap.get()); + status_t result = pAsset->openChunk(dataMap.get(), std::move(fd)); if (result != NO_ERROR) { return NULL; } @@ -415,7 +414,7 @@ off64_t Asset::handleSeek(off64_t offset, int whence, off64_t curPosn, off64_t m * Constructor. */ _FileAsset::_FileAsset(void) - : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mMap(NULL), mBuf(NULL) + : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mFd(-1), mMap(NULL), mBuf(NULL) { // Register the Asset with the global list here after it is fully constructed and its // vtable pointer points to this concrete type. b/31113965 @@ -485,7 +484,7 @@ status_t _FileAsset::openChunk(const char* fileName, int fd, off64_t offset, siz /* * Create the chunk from the map. */ -status_t _FileAsset::openChunk(FileMap* dataMap) +status_t _FileAsset::openChunk(FileMap* dataMap, base::unique_fd fd) { assert(mFp == NULL); // no reopen assert(mMap == NULL); @@ -494,6 +493,7 @@ status_t _FileAsset::openChunk(FileMap* dataMap) mMap = dataMap; mStart = -1; // not used mLength = dataMap->getDataLength(); + mFd = std::move(fd); assert(mOffset == 0); return NO_ERROR; @@ -692,6 +692,17 @@ const void* _FileAsset::getBuffer(bool wordAligned) int _FileAsset::openFileDescriptor(off64_t* outStart, off64_t* outLength) const { if (mMap != NULL) { + if (mFd.ok()) { + *outStart = mMap->getDataOffset(); + *outLength = mMap->getDataLength(); + const int fd = dup(mFd); + if (fd < 0) { + ALOGE("Unable to dup fd (%d).", mFd.get()); + return -1; + } + lseek64(fd, 0, SEEK_SET); + return fd; + } const char* fname = mMap->getFileName(); if (fname == NULL) { fname = mFileName; diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index e35c0249fbdf..70bb441f94cb 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -749,7 +749,7 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, std::unique_ptr LoadedArsc::Load(const StringPiece& data, const LoadedIdmap* loaded_idmap, - package_property_t property_flags) { + const package_property_t property_flags) { ATRACE_NAME("LoadedArsc::Load"); // Not using make_unique because the constructor is private. diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h index af802b0e50b9..643dc5c861f7 100644 --- a/libs/androidfw/include/androidfw/ApkAssets.h +++ b/libs/androidfw/include/androidfw/ApkAssets.h @@ -37,49 +37,46 @@ class LoadedIdmap; // Holds an APK. class ApkAssets { - public: - // Creates an ApkAssets. - // If `system` is true, the package is marked as a system package, and allows some functions to - // filter out this package when computing what configurations/resources are available. - static std::unique_ptr Load(const std::string& path, bool system = false, - bool for_loader = false); - - // Creates an ApkAssets, but forces any package with ID 0x7f to be loaded as a shared library. - // If `system` is true, the package is marked as a system package, and allows some functions to - // filter out this package when computing what configurations/resources are available. - static std::unique_ptr LoadAsSharedLibrary(const std::string& path, - bool system = false); + // This means the data extends to the end of the file. + static constexpr off64_t kUnknownLength = -1; - // Creates an ApkAssets from an IDMAP, which contains the original APK path, and the overlay - // data. - // If `system` is true, the package is marked as a system package, and allows some functions to - // filter out this package when computing what configurations/resources are available. - static std::unique_ptr LoadOverlay(const std::string& idmap_path, - bool system = false); + public: + // Creates an ApkAssets from the zip path. + static std::unique_ptr Load(const std::string& path, + package_property_t flags = 0U); // Creates an ApkAssets from the given file descriptor, and takes ownership of the file // descriptor. The `friendly_name` is some name that will be used to identify the source of // this ApkAssets in log messages and other debug scenarios. - // If `system` is true, the package is marked as a system package, and allows some functions to - // filter out this package when computing what configurations/resources are available. - // If `force_shared_lib` is true, any package with ID 0x7f is loaded as a shared library. + // If `length` equals kUnknownLength, offset must equal 0; otherwise, the apk data will be read + // using the `offset` into the file descriptor and will be `length` bytes long. static std::unique_ptr LoadFromFd(base::unique_fd fd, - const std::string& friendly_name, bool system, - bool force_shared_lib, - bool for_loader = false); + const std::string& friendly_name, + package_property_t flags = 0U, + off64_t offset = 0, + off64_t length = kUnknownLength); + + // Creates an ApkAssets from the given path which points to a resources.arsc. + static std::unique_ptr LoadTable(const std::string& path, + package_property_t flags = 0U); + + // Creates an ApkAssets from the given file descriptor which points to an resources.arsc, and + // takes ownership of the file descriptor. + // If `length` equals kUnknownLength, offset must equal 0; otherwise, the .arsc data will be read + // using the `offset` into the file descriptor and will be `length` bytes long. + static std::unique_ptr LoadTableFromFd(base::unique_fd fd, + const std::string& friendly_name, + package_property_t flags = 0U, + off64_t offset = 0, + off64_t length = kUnknownLength); - // Creates an empty wrapper ApkAssets from the given path which points to an .arsc. - static std::unique_ptr LoadArsc(const std::string& path, - bool for_loader = false); - - // Creates an empty wrapper ApkAssets from the given file descriptor which points to an .arsc, - // Takes ownership of the file descriptor. - static std::unique_ptr LoadArsc(base::unique_fd fd, - const std::string& friendly_name, - bool for_loader = false); + // Creates an ApkAssets from an IDMAP, which contains the original APK path, and the overlay + // data. + static std::unique_ptr LoadOverlay(const std::string& idmap_path, + package_property_t flags = 0U); // Creates a totally empty ApkAssets with no resources table and no file entries. - static std::unique_ptr LoadEmpty(bool for_loader = false); + static std::unique_ptr LoadEmpty(package_property_t flags = 0U); std::unique_ptr Open(const std::string& path, Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM) const; @@ -105,28 +102,38 @@ class ApkAssets { } inline bool IsOverlay() const { - return (property_flags_ & PROPERTY_OVERLAY) != 0; + return loaded_idmap_ != nullptr; } bool IsUpToDate() const; - // Creates an Asset from any file on the file system. + // Creates an Asset from a file on disk. static std::unique_ptr CreateAssetFromFile(const std::string& path); + // Creates an Asset from a file descriptor. + // + // The asset takes ownership of the file descriptor. If `length` equals kUnknownLength, offset + // must equal 0; otherwise, the asset data will be read using the `offset` into the file + // descriptor and will be `length` bytes long. + static std::unique_ptr CreateAssetFromFd(base::unique_fd fd, + const char* path, + off64_t offset = 0, + off64_t length = kUnknownLength); private: DISALLOW_COPY_AND_ASSIGN(ApkAssets); - static std::unique_ptr LoadImpl(base::unique_fd fd, const std::string& path, + static std::unique_ptr LoadImpl(ZipArchiveHandle unmanaged_handle, + const std::string& path, std::unique_ptr idmap_asset, - std::unique_ptr loaded_idmap, + std::unique_ptr idmap, package_property_t property_flags); - static std::unique_ptr LoadArscImpl(base::unique_fd fd, - const std::string& path, - package_property_t property_flags); + static std::unique_ptr LoadTableImpl(std::unique_ptr resources_asset, + const std::string& path, + package_property_t property_flags); ApkAssets(ZipArchiveHandle unmanaged_handle, - const std::string& path, + std::string path, time_t last_mod_time, package_property_t property_flags); diff --git a/libs/androidfw/include/androidfw/Asset.h b/libs/androidfw/include/androidfw/Asset.h index 053dbb7864c6..75761747a5b4 100644 --- a/libs/androidfw/include/androidfw/Asset.h +++ b/libs/androidfw/include/androidfw/Asset.h @@ -26,6 +26,7 @@ #include +#include #include #include #include @@ -202,8 +203,14 @@ private: */ static Asset* createFromUncompressedMap(FileMap* dataMap, AccessMode mode); + /* + * Create the asset from a memory-mapped file segment. + * + * The asset takes ownership of the FileMap and the file descriptor "fd". The file descriptor is + * used to request new file descriptors using "openFileDescriptor". + */ static std::unique_ptr createFromUncompressedMap(std::unique_ptr dataMap, - AccessMode mode); + base::unique_fd fd, AccessMode mode); /* * Create the asset from a memory-mapped file segment with compressed @@ -256,9 +263,9 @@ public: /* * Use a memory-mapped region. * - * On success, the object takes ownership of "dataMap". + * On success, the object takes ownership of "dataMap" and "fd". */ - status_t openChunk(FileMap* dataMap); + status_t openChunk(FileMap* dataMap, base::unique_fd fd); /* * Standard Asset interfaces. @@ -273,11 +280,12 @@ public: virtual bool isAllocated(void) const { return mBuf != NULL; } private: - off64_t mStart; // absolute file offset of start of chunk - off64_t mLength; // length of the chunk - off64_t mOffset; // current local offset, 0 == mStart - FILE* mFp; // for read/seek - char* mFileName; // for opening + off64_t mStart; // absolute file offset of start of chunk + off64_t mLength; // length of the chunk + off64_t mOffset; // current local offset, 0 == mStart + FILE* mFp; // for read/seek + char* mFileName; // for opening + base::unique_fd mFd; // for opening file descriptors /* * To support getBuffer() we either need to read the entire thing into diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index b5d3a1fc6c1f..89ff9f52125d 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -69,12 +69,24 @@ struct TypeSpec { } }; +// Flags that change the behavior of loaded packages. +// Keep in sync with f/b/android/content/res/ApkAssets.java using package_property_t = uint32_t; enum : package_property_t { - PROPERTY_DYNAMIC = 1, - PROPERTY_LOADER = 2, - PROPERTY_OVERLAY = 4, - PROPERTY_SYSTEM = 8, + // The package contains framework resource values specified by the system. + // This allows some functions to filter out this package when computing + // what configurations/resources are available. + PROPERTY_SYSTEM = 1U << 0U, + + // The package is a shared library or has a package id of 7f and is loaded as a shared library by + // force. + PROPERTY_DYNAMIC = 1U << 1U, + + // The package has been loaded dynamically using a ResourcesProvider. + PROPERTY_LOADER = 1U << 2U, + + // The package is a RRO. + PROPERTY_OVERLAY = 1U << 3U, }; // TypeSpecPtr points to a block of memory that holds a TypeSpec struct, followed by an array of diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp index 0f2ee6fb968e..26bf5ffe5e91 100644 --- a/libs/androidfw/tests/ApkAssets_test.cpp +++ b/libs/androidfw/tests/ApkAssets_test.cpp @@ -70,7 +70,7 @@ TEST(ApkAssetsTest, LoadApkAsSharedLibrary) { ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u)); EXPECT_FALSE(loaded_arsc->GetPackages()[0]->IsDynamic()); - loaded_apk = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/appaslib/appaslib.apk"); + loaded_apk = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk", PROPERTY_DYNAMIC); ASSERT_THAT(loaded_apk, NotNull()); loaded_arsc = loaded_apk->GetLoadedArsc(); diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index 35fea7ab86cb..ac32699c6dfd 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -63,10 +63,12 @@ class AssetManager2Test : public ::testing::Test { libclient_assets_ = ApkAssets::Load(GetTestDataPath() + "/libclient/libclient.apk"); ASSERT_NE(nullptr, libclient_assets_); - appaslib_assets_ = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/appaslib/appaslib.apk"); + appaslib_assets_ = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk", + PROPERTY_DYNAMIC); ASSERT_NE(nullptr, appaslib_assets_); - system_assets_ = ApkAssets::Load(GetTestDataPath() + "/system/system.apk", true /*system*/); + system_assets_ = ApkAssets::Load(GetTestDataPath() + "/system/system.apk", + PROPERTY_SYSTEM); ASSERT_NE(nullptr, system_assets_); app_assets_ = ApkAssets::Load(GetTestDataPath() + "/app/app.apk"); diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp index c8dbe205fee2..24361b5817f4 100644 --- a/libs/androidfw/tests/AttributeResolution_test.cpp +++ b/libs/androidfw/tests/AttributeResolution_test.cpp @@ -67,7 +67,7 @@ class AttributeResolutionXmlTest : public AttributeResolutionTest { TEST(AttributeResolutionLibraryTest, ApplyStyleWithDefaultStyleResId) { AssetManager2 assetmanager; - auto apk_assets = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/styles/styles.apk"); + auto apk_assets = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk", PROPERTY_DYNAMIC); ASSERT_NE(nullptr, apk_assets); assetmanager.SetApkAssets({apk_assets.get()}); diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp index b679672ab34e..41ba637da5d7 100644 --- a/libs/androidfw/tests/Idmap_test.cpp +++ b/libs/androidfw/tests/Idmap_test.cpp @@ -221,8 +221,8 @@ TEST_F(IdmapTest, OverlaidResourceHasSameName) { TEST_F(IdmapTest, OverlayLoaderInterop) { std::string contents; - auto loader_assets = ApkAssets::LoadArsc(GetTestDataPath() + "/loader/resources.arsc", - /* for_loader */ true); + auto loader_assets = ApkAssets::LoadTable(GetTestDataPath() + "/loader/resources.arsc", + PROPERTY_LOADER); AssetManager2 asset_manager; asset_manager.SetApkAssets({overlayable_assets_.get(), loader_assets.get(), -- cgit v1.2.3 From c07aa702703388747bd6e9b1091127e2736ffcd8 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Tue, 10 Mar 2020 13:49:12 -0700 Subject: Add ResourcesProvider.loadFromDirectory This API allows a directory to be loaded as if it was a zipped APK. This is a substitute for the DirectoryAssetProvider API that currently does not work in the native layer. Bug: 142716192 Test: atest FrameworksResourceLoaderTests Change-Id: Ia13e15653e75b421423dd56f9fe89e183ab4cb9a --- libs/androidfw/ApkAssets.cpp | 457 +++++++++++++++++---------- libs/androidfw/AssetManager2.cpp | 6 +- libs/androidfw/include/androidfw/ApkAssets.h | 60 +++- libs/androidfw/include/androidfw/Asset.h | 1 + libs/androidfw/tests/ApkAssets_test.cpp | 13 +- 5 files changed, 343 insertions(+), 194 deletions(-) (limited to 'libs') diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index 946fcc03f57e..f5bf84f18a89 100644 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -21,6 +21,7 @@ #include "android-base/errors.h" #include "android-base/file.h" #include "android-base/logging.h" +#include "android-base/stringprintf.h" #include "android-base/unique_fd.h" #include "android-base/utf8.h" #include "utils/Compat.h" @@ -40,28 +41,263 @@ using base::unique_fd; static const std::string kResourcesArsc("resources.arsc"); -ApkAssets::ApkAssets(ZipArchiveHandle unmanaged_handle, +ApkAssets::ApkAssets(std::unique_ptr assets_provider, std::string path, time_t last_mod_time, package_property_t property_flags) - : zip_handle_(unmanaged_handle, ::CloseArchive), + : assets_provider_(std::move(assets_provider)), path_(std::move(path)), last_mod_time_(last_mod_time), property_flags_(property_flags) { } -std::unique_ptr ApkAssets::Load(const std::string& path, - const package_property_t flags) { - ::ZipArchiveHandle unmanaged_handle; - const int32_t result = ::OpenArchive(path.c_str(), &unmanaged_handle); - if (result != 0) { - LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result); - ::CloseArchive(unmanaged_handle); - return {}; +// Provides asset files from a zip file. +class ZipAssetsProvider : public AssetsProvider { + public: + ~ZipAssetsProvider() override = default; + + static std::unique_ptr Create(const std::string& path) { + ::ZipArchiveHandle unmanaged_handle; + const int32_t result = ::OpenArchive(path.c_str(), &unmanaged_handle); + if (result != 0) { + LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result); + ::CloseArchive(unmanaged_handle); + return {}; + } + + return std::unique_ptr(new ZipAssetsProvider(path, path, unmanaged_handle)); + } + + static std::unique_ptr Create( + unique_fd fd, const std::string& friendly_name, const off64_t offset = 0, + const off64_t length = ApkAssets::kUnknownLength) { + + ::ZipArchiveHandle unmanaged_handle; + const int32_t result = (length == ApkAssets::kUnknownLength) + ? ::OpenArchiveFd(fd.release(), friendly_name.c_str(), &unmanaged_handle) + : ::OpenArchiveFdRange(fd.release(), friendly_name.c_str(), &unmanaged_handle, length, + offset); + + if (result != 0) { + LOG(ERROR) << "Failed to open APK '" << friendly_name << "' through FD with offset " << offset + << " and length " << length << ": " << ::ErrorCodeString(result); + ::CloseArchive(unmanaged_handle); + return {}; + } + + return std::unique_ptr(new ZipAssetsProvider({}, friendly_name, + unmanaged_handle)); + } + + // Iterate over all files and directories within the zip. The order of iteration is not + // guaranteed to be the same as the order of elements in the central directory but is stable for a + // given zip file. + bool ForEachFile(const std::string& root_path, + const std::function& f) const override { + // If this is a resource loader from an .arsc, there will be no zip handle + if (zip_handle_ == nullptr) { + return false; + } + + std::string root_path_full = root_path; + if (root_path_full.back() != '/') { + root_path_full += '/'; + } + + void* cookie; + if (::StartIteration(zip_handle_.get(), &cookie, root_path_full, "") != 0) { + return false; + } + + std::string name; + ::ZipEntry entry{}; + + // We need to hold back directories because many paths will contain them and we want to only + // surface one. + std::set dirs{}; + + int32_t result; + while ((result = ::Next(cookie, &entry, &name)) == 0) { + StringPiece full_file_path(name); + StringPiece leaf_file_path = full_file_path.substr(root_path_full.size()); + + if (!leaf_file_path.empty()) { + auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/'); + if (iter != leaf_file_path.end()) { + std::string dir = + leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string(); + dirs.insert(std::move(dir)); + } else { + f(leaf_file_path, kFileTypeRegular); + } + } + } + ::EndIteration(cookie); + + // Now present the unique directories. + for (const std::string& dir : dirs) { + f(dir, kFileTypeDirectory); + } + + // -1 is end of iteration, anything else is an error. + return result == -1; + } + + protected: + std::unique_ptr OpenInternal( + const std::string& path, Asset::AccessMode mode, bool* file_exists) const override { + if (file_exists) { + *file_exists = false; + } + + ::ZipEntry entry; + int32_t result = ::FindEntry(zip_handle_.get(), path, &entry); + if (result != 0) { + return {}; + } + + if (file_exists) { + *file_exists = true; + } + + const int fd = ::GetFileDescriptor(zip_handle_.get()); + const off64_t fd_offset = ::GetFileDescriptorOffset(zip_handle_.get()); + if (entry.method == kCompressDeflated) { + std::unique_ptr map = util::make_unique(); + if (!map->create(GetPath(), fd, entry.offset + fd_offset, entry.compressed_length, + true /*readOnly*/)) { + LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'"; + return {}; + } + + std::unique_ptr asset = + Asset::createFromCompressedMap(std::move(map), entry.uncompressed_length, mode); + if (asset == nullptr) { + LOG(ERROR) << "Failed to decompress '" << path << "' in APK '" << friendly_name_ << "'"; + return {}; + } + return asset; + } else { + std::unique_ptr map = util::make_unique(); + if (!map->create(GetPath(), fd, entry.offset + fd_offset, entry.uncompressed_length, + true /*readOnly*/)) { + LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'"; + return {}; + } + + unique_fd ufd; + if (!GetPath()) { + // If the `path` is not set, create a new `fd` for the new Asset to own in order to create + // new file descriptors using Asset::openFileDescriptor. If the path is set, it will be used + // to create new file descriptors. + ufd = unique_fd(dup(fd)); + if (!ufd.ok()) { + LOG(ERROR) << "Unable to dup fd '" << path << "' in APK '" << friendly_name_ << "'"; + return {}; + } + } + + std::unique_ptr asset = Asset::createFromUncompressedMap(std::move(map), + std::move(ufd), mode); + if (asset == nullptr) { + LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'"; + return {}; + } + return asset; + } + } + + private: + DISALLOW_COPY_AND_ASSIGN(ZipAssetsProvider); + + explicit ZipAssetsProvider(std::string path, + std::string friendly_name, + ZipArchiveHandle unmanaged_handle) + : zip_handle_(unmanaged_handle, ::CloseArchive), + path_(std::move(path)), + friendly_name_(std::move(friendly_name)) { } + + const char* GetPath() const { + return path_.empty() ? nullptr : path_.c_str(); + } + + using ZipArchivePtr = std::unique_ptr; + ZipArchivePtr zip_handle_; + std::string path_; + std::string friendly_name_; +}; + +class DirectoryAssetsProvider : AssetsProvider { + public: + ~DirectoryAssetsProvider() override = default; + + static std::unique_ptr Create(const std::string& path) { + struct stat sb{}; + const int result = stat(path.c_str(), &sb); + if (result == -1) { + LOG(ERROR) << "Failed to find directory '" << path << "'."; + return nullptr; + } + + if (!S_ISDIR(sb.st_mode)) { + LOG(ERROR) << "Path '" << path << "' is not a directory."; + return nullptr; + } + + return std::unique_ptr(new DirectoryAssetsProvider(path)); + } + + protected: + std::unique_ptr OpenInternal( + const std::string& path, Asset::AccessMode /* mode */, bool* file_exists) const override { + const std::string resolved_path = ResolvePath(path); + if (file_exists) { + struct stat sb{}; + const int result = stat(resolved_path.c_str(), &sb); + *file_exists = result != -1 && S_ISREG(sb.st_mode); + } + + return ApkAssets::CreateAssetFromFile(resolved_path); + } + + private: + DISALLOW_COPY_AND_ASSIGN(DirectoryAssetsProvider); + + explicit DirectoryAssetsProvider(std::string path) : path_(std::move(path)) { } + + inline std::string ResolvePath(const std::string& path) const { + return base::StringPrintf("%s%c%s", path_.c_str(), OS_PATH_SEPARATOR, path.c_str()); } - return LoadImpl(unmanaged_handle, path, nullptr /*idmap_asset*/, nullptr /*loaded_idmap*/, - flags); + const std::string path_; +}; + +// AssetProvider implementation that does not provide any assets. Used for ApkAssets::LoadEmpty. +class EmptyAssetsProvider : public AssetsProvider { + public: + EmptyAssetsProvider() = default; + ~EmptyAssetsProvider() override = default; + + protected: + std::unique_ptr OpenInternal(const std::string& /*path */, + Asset::AccessMode /* mode */, + bool* file_exists) const override { + if (file_exists) { + *file_exists = false; + } + return nullptr; + } + + private: + DISALLOW_COPY_AND_ASSIGN(EmptyAssetsProvider); +}; + +std::unique_ptr ApkAssets::Load(const std::string& path, + const package_property_t flags) { + auto assets = ZipAssetsProvider::Create(path); + return (assets) ? LoadImpl(std::move(assets), path, nullptr /*idmap_asset*/, + nullptr /*loaded_idmap*/, flags) + : nullptr; } std::unique_ptr ApkAssets::LoadFromFd(unique_fd fd, @@ -72,33 +308,17 @@ std::unique_ptr ApkAssets::LoadFromFd(unique_fd fd, CHECK(length >= kUnknownLength) << "length must be greater than or equal to " << kUnknownLength; CHECK(length != kUnknownLength || offset == 0) << "offset must be 0 if length is " << kUnknownLength; - - ::ZipArchiveHandle unmanaged_handle; - const int32_t result = (length == kUnknownLength) - ? ::OpenArchiveFd(fd.release(), friendly_name.c_str(), &unmanaged_handle) - : ::OpenArchiveFdRange(fd.release(), friendly_name.c_str(), &unmanaged_handle, length, - offset); - - if (result != 0) { - LOG(ERROR) << "Failed to open APK '" << friendly_name << "' through FD with offset " << offset - << " and length " << length << ": " << ::ErrorCodeString(result); - ::CloseArchive(unmanaged_handle); - return {}; - } - - return LoadImpl(unmanaged_handle, friendly_name, nullptr /*idmap_asset*/, - nullptr /*loaded_idmap*/, flags); + auto assets = ZipAssetsProvider::Create(std::move(fd), friendly_name, offset, length); + return (assets) ? LoadImpl(std::move(assets), friendly_name, nullptr /*idmap_asset*/, + nullptr /*loaded_idmap*/, flags) + : nullptr; } std::unique_ptr ApkAssets::LoadTable(const std::string& path, const package_property_t flags) { auto resources_asset = CreateAssetFromFile(path); - if (!resources_asset) { - LOG(ERROR) << "Failed to open ARSC '" << path; - return {}; - } - - return LoadTableImpl(std::move(resources_asset), path, flags); + return (resources_asset) ? LoadTableImpl(std::move(resources_asset), path, flags) + : nullptr; } std::unique_ptr ApkAssets::LoadTableFromFd(unique_fd fd, @@ -107,13 +327,8 @@ std::unique_ptr ApkAssets::LoadTableFromFd(unique_fd fd, const off64_t offset, const off64_t length) { auto resources_asset = CreateAssetFromFd(std::move(fd), nullptr /* path */, offset, length); - if (!resources_asset) { - LOG(ERROR) << "Failed to open ARSC '" << friendly_name << "' through FD with offset " << offset - << " and length " << length; - return {}; - } - - return LoadTableImpl(std::move(resources_asset), friendly_name, flags); + return (resources_asset) ? LoadTableImpl(std::move(resources_asset), friendly_name, flags) + : nullptr; } std::unique_ptr ApkAssets::LoadOverlay(const std::string& idmap_path, @@ -133,24 +348,26 @@ std::unique_ptr ApkAssets::LoadOverlay(const std::string& idmap LOG(ERROR) << "failed to load IDMAP " << idmap_path; return {}; } - - - ::ZipArchiveHandle unmanaged_handle; + auto overlay_path = loaded_idmap->OverlayApkPath(); - const int32_t result = ::OpenArchive(overlay_path.c_str(), &unmanaged_handle); - if (result != 0) { - LOG(ERROR) << "Failed to open overlay APK '" << overlay_path << "' " - << ::ErrorCodeString(result); - ::CloseArchive(unmanaged_handle); - return {}; - } + auto assets = ZipAssetsProvider::Create(overlay_path); + return (assets) ? LoadImpl(std::move(assets), overlay_path, std::move(idmap_asset), + std::move(loaded_idmap), flags | PROPERTY_OVERLAY) + : nullptr; +} - return LoadImpl(unmanaged_handle, overlay_path, std::move(idmap_asset), std::move(loaded_idmap), - flags | PROPERTY_OVERLAY); +std::unique_ptr ApkAssets::LoadFromDir(const std::string& path, + const package_property_t flags) { + auto assets = DirectoryAssetsProvider::Create(path); + return (assets) ? LoadImpl(std::move(assets), path, nullptr /*idmap_asset*/, + nullptr /*loaded_idmap*/, flags) + : nullptr; } std::unique_ptr ApkAssets::LoadEmpty(const package_property_t flags) { - std::unique_ptr loaded_apk(new ApkAssets(nullptr, "empty", -1, flags)); + std::unique_ptr loaded_apk(new ApkAssets( + std::unique_ptr(new EmptyAssetsProvider()), "empty" /* path */, + -1 /* last_mod-time */, flags)); loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty(); // Need to force a move for mingw32. return std::move(loaded_apk); @@ -196,7 +413,7 @@ std::unique_ptr ApkAssets::CreateAssetFromFd(base::unique_fd fd, Asset::AccessMode::ACCESS_RANDOM); } -std::unique_ptr ApkAssets::LoadImpl(ZipArchiveHandle unmanaged_handle, +std::unique_ptr ApkAssets::LoadImpl(std::unique_ptr assets, const std::string& path, std::unique_ptr idmap_asset, std::unique_ptr idmap, @@ -205,24 +422,19 @@ std::unique_ptr ApkAssets::LoadImpl(ZipArchiveHandle unmanaged_ // Wrap the handle in a unique_ptr so it gets automatically closed. std::unique_ptr - loaded_apk(new ApkAssets(unmanaged_handle, path, last_mod_time, property_flags)); + loaded_apk(new ApkAssets(std::move(assets), path, last_mod_time, property_flags)); - // Find the resource table. - ::ZipEntry entry; - int32_t result = ::FindEntry(loaded_apk->zip_handle_.get(), kResourcesArsc, &entry); - if (result != 0) { - // There is no resources.arsc, so create an empty LoadedArsc and return. + // Open the resource table via mmap unless it is compressed. This logic is taken care of by Open. + bool resources_asset_exists = false; + loaded_apk->resources_asset_ = loaded_apk->assets_provider_->Open( + kResourcesArsc, Asset::AccessMode::ACCESS_BUFFER, &resources_asset_exists); + + if (!resources_asset_exists) { loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty(); return std::move(loaded_apk); } - if (entry.method == kCompressDeflated) { - ANDROID_LOG(WARNING) << kResourcesArsc << " in APK '" << path << "' is compressed."; - } - - // Open the resource table via mmap unless it is compressed. This logic is taken care of by Open. - loaded_apk->resources_asset_ = loaded_apk->Open(kResourcesArsc, Asset::AccessMode::ACCESS_BUFFER); - if (loaded_apk->resources_asset_ == nullptr) { + if (!loaded_apk->resources_asset_) { LOG(ERROR) << "Failed to open '" << kResourcesArsc << "' in APK '" << path << "'."; return {}; } @@ -236,7 +448,7 @@ std::unique_ptr ApkAssets::LoadImpl(ZipArchiveHandle unmanaged_ loaded_apk->resources_asset_->getLength()); loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, loaded_apk->loaded_idmap_.get(), property_flags); - if (loaded_apk->loaded_arsc_ == nullptr) { + if (!loaded_apk->loaded_arsc_) { LOG(ERROR) << "Failed to load '" << kResourcesArsc << "' in APK '" << path << "'."; return {}; } @@ -251,7 +463,8 @@ std::unique_ptr ApkAssets::LoadTableImpl(std::unique_ptr const time_t last_mod_time = getFileModDate(path.c_str()); std::unique_ptr loaded_apk( - new ApkAssets(nullptr, path, last_mod_time, property_flags)); + new ApkAssets(std::unique_ptr(new EmptyAssetsProvider()), path, last_mod_time, + property_flags)); loaded_apk->resources_asset_ = std::move(resources_asset); const StringPiece data( @@ -267,111 +480,9 @@ std::unique_ptr ApkAssets::LoadTableImpl(std::unique_ptr return std::move(loaded_apk); } -std::unique_ptr ApkAssets::Open(const std::string& path, Asset::AccessMode mode) const { - // If this is a resource loader from an .arsc, there will be no zip handle - if (zip_handle_ == nullptr) { - return {}; - } - - ::ZipEntry entry; - int32_t result = ::FindEntry(zip_handle_.get(), path, &entry); - if (result != 0) { - return {}; - } - - const int fd = ::GetFileDescriptor(zip_handle_.get()); - const off64_t fd_offset = ::GetFileDescriptorOffset(zip_handle_.get()); - if (entry.method == kCompressDeflated) { - std::unique_ptr map = util::make_unique(); - if (!map->create(path_.c_str(), fd, fd_offset + entry.offset, entry.compressed_length, - true /*readOnly*/)) { - LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'"; - return {}; - } - - std::unique_ptr asset = - Asset::createFromCompressedMap(std::move(map), entry.uncompressed_length, mode); - if (asset == nullptr) { - LOG(ERROR) << "Failed to decompress '" << path << "'."; - return {}; - } - return asset; - } else { - std::unique_ptr map = util::make_unique(); - if (!map->create(path_.c_str(), fd, fd_offset + entry.offset, entry.uncompressed_length, - true /*readOnly*/)) { - LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'"; - return {}; - } - - // TODO: apks created from file descriptors residing in RAM currently cannot open file - // descriptors to the assets they contain. This is because the Asset::openFileDeescriptor uses - // the zip path on disk to create a new file descriptor. This is fixed in a future change - // in the change topic. - std::unique_ptr asset = Asset::createFromUncompressedMap(std::move(map), - unique_fd(-1) /* fd*/, mode); - if (asset == nullptr) { - LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'"; - return {}; - } - return asset; - } -} - -bool ApkAssets::ForEachFile(const std::string& root_path, - const std::function& f) const { - // If this is a resource loader from an .arsc, there will be no zip handle - if (zip_handle_ == nullptr) { - return false; - } - - std::string root_path_full = root_path; - if (root_path_full.back() != '/') { - root_path_full += '/'; - } - - void* cookie; - if (::StartIteration(zip_handle_.get(), &cookie, root_path_full, "") != 0) { - return false; - } - - std::string name; - ::ZipEntry entry; - - // We need to hold back directories because many paths will contain them and we want to only - // surface one. - std::set dirs; - - int32_t result; - while ((result = ::Next(cookie, &entry, &name)) == 0) { - StringPiece full_file_path(name); - StringPiece leaf_file_path = full_file_path.substr(root_path_full.size()); - - if (!leaf_file_path.empty()) { - auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/'); - if (iter != leaf_file_path.end()) { - std::string dir = - leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string(); - dirs.insert(std::move(dir)); - } else { - f(leaf_file_path, kFileTypeRegular); - } - } - } - ::EndIteration(cookie); - - // Now present the unique directories. - for (const std::string& dir : dirs) { - f(dir, kFileTypeDirectory); - } - - // -1 is end of iteration, anything else is an error. - return result == -1; -} - bool ApkAssets::IsUpToDate() const { if (IsLoader()) { - // Loaders are invalidated by the app, not the system, so assume up to date. + // Loaders are invalidated by the app, not the system, so assume they are up to date. return true; } diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 32086625a726..f20e18453f8b 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -463,7 +463,7 @@ std::unique_ptr AssetManager2::OpenDir(const std::string& dirname) con files->add(info); }; - if (!apk_assets->ForEachFile(full_path, func)) { + if (!apk_assets->GetAssetsProvider()->ForEachFile(full_path, func)) { return {}; } } @@ -487,7 +487,7 @@ std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, continue; } - std::unique_ptr asset = apk_assets_[i]->Open(filename, mode); + std::unique_ptr asset = apk_assets_[i]->GetAssetsProvider()->Open(filename, mode); if (asset) { if (out_cookie != nullptr) { *out_cookie = i; @@ -508,7 +508,7 @@ std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, if (cookie < 0 || static_cast(cookie) >= apk_assets_.size()) { return {}; } - return apk_assets_[cookie]->Open(filename, mode); + return apk_assets_[cookie]->GetAssetsProvider()->Open(filename, mode); } ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override, diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h index 643dc5c861f7..944476890af0 100644 --- a/libs/androidfw/include/androidfw/ApkAssets.h +++ b/libs/androidfw/include/androidfw/ApkAssets.h @@ -35,13 +35,46 @@ namespace android { class LoadedIdmap; +// Interface for retrieving assets provided by an ApkAssets. +class AssetsProvider { + public: + virtual ~AssetsProvider() = default; + + // Opens a file for reading. + std::unique_ptr Open(const std::string& path, + Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM, + bool* file_exists = nullptr) const { + return OpenInternal(path, mode, file_exists); + } + + // Iterate over all files and directories provided by the zip. The order of iteration is stable. + virtual bool ForEachFile(const std::string& /* path */, + const std::function& /* f */) const { + return true; + } + + protected: + AssetsProvider() = default; + + virtual std::unique_ptr OpenInternal(const std::string& path, + Asset::AccessMode mode, + bool* file_exists) const = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(AssetsProvider); +}; + +class ZipAssetsProvider; + // Holds an APK. class ApkAssets { + public: // This means the data extends to the end of the file. static constexpr off64_t kUnknownLength = -1; - public: - // Creates an ApkAssets from the zip path. + // Creates an ApkAssets. + // If `system` is true, the package is marked as a system package, and allows some functions to + // filter out this package when computing what configurations/resources are available. static std::unique_ptr Load(const std::string& path, package_property_t flags = 0U); @@ -75,19 +108,22 @@ class ApkAssets { static std::unique_ptr LoadOverlay(const std::string& idmap_path, package_property_t flags = 0U); + // Creates an ApkAssets from the directory path. File-based resources are read within the + // directory as if the directory is an APK. + static std::unique_ptr LoadFromDir(const std::string& path, + package_property_t flags = 0U); + // Creates a totally empty ApkAssets with no resources table and no file entries. static std::unique_ptr LoadEmpty(package_property_t flags = 0U); - std::unique_ptr Open(const std::string& path, - Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM) const; - - bool ForEachFile(const std::string& path, - const std::function& f) const; - inline const std::string& GetPath() const { return path_; } + inline const AssetsProvider* GetAssetsProvider() const { + return assets_provider_.get(); + } + // This is never nullptr. inline const LoadedArsc* GetLoadedArsc() const { return loaded_arsc_.get(); @@ -122,7 +158,7 @@ class ApkAssets { private: DISALLOW_COPY_AND_ASSIGN(ApkAssets); - static std::unique_ptr LoadImpl(ZipArchiveHandle unmanaged_handle, + static std::unique_ptr LoadImpl(std::unique_ptr assets, const std::string& path, std::unique_ptr idmap_asset, std::unique_ptr idmap, @@ -132,14 +168,12 @@ class ApkAssets { const std::string& path, package_property_t property_flags); - ApkAssets(ZipArchiveHandle unmanaged_handle, + ApkAssets(std::unique_ptr assets_provider, std::string path, time_t last_mod_time, package_property_t property_flags); - using ZipArchivePtr = std::unique_ptr; - - ZipArchivePtr zip_handle_; + std::unique_ptr assets_provider_; const std::string path_; time_t last_mod_time_; package_property_t property_flags_ = 0U; diff --git a/libs/androidfw/include/androidfw/Asset.h b/libs/androidfw/include/androidfw/Asset.h index 75761747a5b4..298509eb37a1 100644 --- a/libs/androidfw/include/androidfw/Asset.h +++ b/libs/androidfw/include/androidfw/Asset.h @@ -159,6 +159,7 @@ private: /* AssetManager needs access to our "create" functions */ friend class AssetManager; friend class ApkAssets; + friend class ZipAssetsProvider; /* * Create the asset from a named file on disk. diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp index 26bf5ffe5e91..ce9e53244801 100644 --- a/libs/androidfw/tests/ApkAssets_test.cpp +++ b/libs/androidfw/tests/ApkAssets_test.cpp @@ -42,7 +42,7 @@ TEST(ApkAssetsTest, LoadApk) { const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); ASSERT_THAT(loaded_arsc, NotNull()); ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull()); - ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull()); + ASSERT_THAT(loaded_apk->GetAssetsProvider()->Open("res/layout/main.xml"), NotNull()); } TEST(ApkAssetsTest, LoadApkFromFd) { @@ -57,7 +57,7 @@ TEST(ApkAssetsTest, LoadApkFromFd) { const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); ASSERT_THAT(loaded_arsc, NotNull()); ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull()); - ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull()); + ASSERT_THAT(loaded_apk->GetAssetsProvider()->Open("res/layout/main.xml"), NotNull()); } TEST(ApkAssetsTest, LoadApkAsSharedLibrary) { @@ -84,9 +84,11 @@ TEST(ApkAssetsTest, CreateAndDestroyAssetKeepsApkAssetsOpen) { ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); ASSERT_THAT(loaded_apk, NotNull()); - { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); } + { ASSERT_THAT(loaded_apk->GetAssetsProvider()->Open("res/layout/main.xml", + Asset::ACCESS_BUFFER), NotNull()); } - { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); } + { ASSERT_THAT(loaded_apk->GetAssetsProvider()->Open("res/layout/main.xml", + Asset::ACCESS_BUFFER), NotNull()); } } TEST(ApkAssetsTest, OpenUncompressedAssetFd) { @@ -94,7 +96,8 @@ TEST(ApkAssetsTest, OpenUncompressedAssetFd) { ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); ASSERT_THAT(loaded_apk, NotNull()); - auto asset = loaded_apk->Open("assets/uncompressed.txt", Asset::ACCESS_UNKNOWN); + auto asset = loaded_apk->GetAssetsProvider()->Open("assets/uncompressed.txt", + Asset::ACCESS_UNKNOWN); ASSERT_THAT(asset, NotNull()); off64_t start, length; -- cgit v1.2.3 From 4ea1e4288985508e3e0f21febe4da242c86a7dd1 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Wed, 11 Mar 2020 13:15:28 -0700 Subject: Move AssetsProvider to native layer Querying in the native layer for assets provided through AssetsProviders does not currently work. This change refactors the AssetProvider API to return a file descriptor that is read in the native layer and can bubble up to the java layer. This change also removes the InputStream API to favor of developers using memfd_create. Bug: 142716192 Test: atest ResourceLoaderValuesTest Change-Id: I1a7eca0994c3b7cc32008d9a72bf91086ff0e816 --- libs/androidfw/ApkAssets.cpp | 157 ++++++++++++++++++--------- libs/androidfw/include/androidfw/ApkAssets.h | 57 +++++----- 2 files changed, 136 insertions(+), 78 deletions(-) (limited to 'libs') diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index f5bf84f18a89..202651dc86d5 100644 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -144,8 +144,8 @@ class ZipAssetsProvider : public AssetsProvider { } protected: - std::unique_ptr OpenInternal( - const std::string& path, Asset::AccessMode mode, bool* file_exists) const override { + std::unique_ptr OpenInternal( + const std::string& path, Asset::AccessMode mode, bool* file_exists) const override { if (file_exists) { *file_exists = false; } @@ -292,49 +292,91 @@ class EmptyAssetsProvider : public AssetsProvider { DISALLOW_COPY_AND_ASSIGN(EmptyAssetsProvider); }; -std::unique_ptr ApkAssets::Load(const std::string& path, - const package_property_t flags) { +// AssetProvider implementation +class MultiAssetsProvider : public AssetsProvider { + public: + ~MultiAssetsProvider() override = default; + + static std::unique_ptr Create( + std::unique_ptr child, std::unique_ptr parent) { + CHECK(parent != nullptr) << "parent provider must not be null"; + return (!child) ? std::move(parent) + : std::unique_ptr(new MultiAssetsProvider( + std::move(child), std::move(parent))); + } + + bool ForEachFile(const std::string& root_path, + const std::function& f) const override { + // TODO: Only call the function once for files defined in the parent and child + return child_->ForEachFile(root_path, f) && parent_->ForEachFile(root_path, f); + } + + protected: + std::unique_ptr OpenInternal( + const std::string& path, Asset::AccessMode mode, bool* file_exists) const override { + auto asset = child_->Open(path, mode, file_exists); + return (asset) ? std::move(asset) : parent_->Open(path, mode, file_exists); + } + + private: + DISALLOW_COPY_AND_ASSIGN(MultiAssetsProvider); + + MultiAssetsProvider(std::unique_ptr child, + std::unique_ptr parent) + : child_(std::move(child)), parent_(std::move(parent)) { } + + std::unique_ptr child_; + std::unique_ptr parent_; +}; + +// Opens the archive using the file path. Calling CloseArchive on the zip handle will close the +// file. +std::unique_ptr ApkAssets::Load( + const std::string& path, const package_property_t flags, + std::unique_ptr override_asset) { auto assets = ZipAssetsProvider::Create(path); - return (assets) ? LoadImpl(std::move(assets), path, nullptr /*idmap_asset*/, - nullptr /*loaded_idmap*/, flags) + return (assets) ? LoadImpl(std::move(assets), path, flags, std::move(override_asset)) : nullptr; } -std::unique_ptr ApkAssets::LoadFromFd(unique_fd fd, - const std::string& friendly_name, - const package_property_t flags, - const off64_t offset, - const off64_t length) { +// Opens the archive using the file file descriptor with the specified file offset and read length. +// If the `assume_ownership` parameter is 'true' calling CloseArchive will close the file. +std::unique_ptr ApkAssets::LoadFromFd( + unique_fd fd, const std::string& friendly_name, const package_property_t flags, + std::unique_ptr override_asset, const off64_t offset, + const off64_t length) { CHECK(length >= kUnknownLength) << "length must be greater than or equal to " << kUnknownLength; CHECK(length != kUnknownLength || offset == 0) << "offset must be 0 if length is " << kUnknownLength; + auto assets = ZipAssetsProvider::Create(std::move(fd), friendly_name, offset, length); - return (assets) ? LoadImpl(std::move(assets), friendly_name, nullptr /*idmap_asset*/, - nullptr /*loaded_idmap*/, flags) + return (assets) ? LoadImpl(std::move(assets), friendly_name, flags, std::move(override_asset)) : nullptr; } -std::unique_ptr ApkAssets::LoadTable(const std::string& path, - const package_property_t flags) { - auto resources_asset = CreateAssetFromFile(path); - return (resources_asset) ? LoadTableImpl(std::move(resources_asset), path, flags) - : nullptr; +std::unique_ptr ApkAssets::LoadTable( + const std::string& path, const package_property_t flags, + std::unique_ptr override_asset) { + + auto assets = CreateAssetFromFile(path); + return (assets) ? LoadTableImpl(std::move(assets), path, flags, std::move(override_asset)) + : nullptr; } -std::unique_ptr ApkAssets::LoadTableFromFd(unique_fd fd, - const std::string& friendly_name, - const package_property_t flags, - const off64_t offset, - const off64_t length) { - auto resources_asset = CreateAssetFromFd(std::move(fd), nullptr /* path */, offset, length); - return (resources_asset) ? LoadTableImpl(std::move(resources_asset), friendly_name, flags) - : nullptr; +std::unique_ptr ApkAssets::LoadTableFromFd( + unique_fd fd, const std::string& friendly_name, const package_property_t flags, + std::unique_ptr override_asset, const off64_t offset, + const off64_t length) { + + auto assets = CreateAssetFromFd(std::move(fd), nullptr /* path */, offset, length); + return (assets) ? LoadTableImpl(std::move(assets), friendly_name, flags, + std::move(override_asset)) + : nullptr; } std::unique_ptr ApkAssets::LoadOverlay(const std::string& idmap_path, const package_property_t flags) { CHECK((flags & PROPERTY_LOADER) == 0U) << "Cannot load RROs through loaders"; - std::unique_ptr idmap_asset = CreateAssetFromFile(idmap_path); if (idmap_asset == nullptr) { return {}; @@ -351,23 +393,28 @@ std::unique_ptr ApkAssets::LoadOverlay(const std::string& idmap auto overlay_path = loaded_idmap->OverlayApkPath(); auto assets = ZipAssetsProvider::Create(overlay_path); - return (assets) ? LoadImpl(std::move(assets), overlay_path, std::move(idmap_asset), - std::move(loaded_idmap), flags | PROPERTY_OVERLAY) + return (assets) ? LoadImpl(std::move(assets), overlay_path, flags | PROPERTY_OVERLAY, + nullptr /* override_asset */, std::move(idmap_asset), + std::move(loaded_idmap)) : nullptr; } -std::unique_ptr ApkAssets::LoadFromDir(const std::string& path, - const package_property_t flags) { +std::unique_ptr ApkAssets::LoadFromDir( + const std::string& path, const package_property_t flags, + std::unique_ptr override_asset) { + auto assets = DirectoryAssetsProvider::Create(path); - return (assets) ? LoadImpl(std::move(assets), path, nullptr /*idmap_asset*/, - nullptr /*loaded_idmap*/, flags) + return (assets) ? LoadImpl(std::move(assets), path, flags, std::move(override_asset)) : nullptr; } -std::unique_ptr ApkAssets::LoadEmpty(const package_property_t flags) { - std::unique_ptr loaded_apk(new ApkAssets( - std::unique_ptr(new EmptyAssetsProvider()), "empty" /* path */, - -1 /* last_mod-time */, flags)); +std::unique_ptr ApkAssets::LoadEmpty( + const package_property_t flags, std::unique_ptr override_asset) { + + auto assets = (override_asset) ? std::move(override_asset) + : std::unique_ptr(new EmptyAssetsProvider()); + std::unique_ptr loaded_apk(new ApkAssets(std::move(assets), "empty" /* path */, + -1 /* last_mod-time */, flags)); loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty(); // Need to force a move for mingw32. return std::move(loaded_apk); @@ -413,27 +460,30 @@ std::unique_ptr ApkAssets::CreateAssetFromFd(base::unique_fd fd, Asset::AccessMode::ACCESS_RANDOM); } -std::unique_ptr ApkAssets::LoadImpl(std::unique_ptr assets, - const std::string& path, - std::unique_ptr idmap_asset, - std::unique_ptr idmap, - package_property_t property_flags) { +std::unique_ptr ApkAssets::LoadImpl( + std::unique_ptr assets, const std::string& path, + package_property_t property_flags, std::unique_ptr override_assets, + std::unique_ptr idmap_asset, std::unique_ptr idmap) { + const time_t last_mod_time = getFileModDate(path.c_str()); + // Open the resource table via mmap unless it is compressed. This logic is taken care of by Open. + bool resources_asset_exists = false; + auto resources_asset_ = assets->Open(kResourcesArsc, Asset::AccessMode::ACCESS_BUFFER, + &resources_asset_exists); + + assets = MultiAssetsProvider::Create(std::move(override_assets), std::move(assets)); + // Wrap the handle in a unique_ptr so it gets automatically closed. std::unique_ptr loaded_apk(new ApkAssets(std::move(assets), path, last_mod_time, property_flags)); - // Open the resource table via mmap unless it is compressed. This logic is taken care of by Open. - bool resources_asset_exists = false; - loaded_apk->resources_asset_ = loaded_apk->assets_provider_->Open( - kResourcesArsc, Asset::AccessMode::ACCESS_BUFFER, &resources_asset_exists); - if (!resources_asset_exists) { loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty(); return std::move(loaded_apk); } + loaded_apk->resources_asset_ = std::move(resources_asset_); if (!loaded_apk->resources_asset_) { LOG(ERROR) << "Failed to open '" << kResourcesArsc << "' in APK '" << path << "'."; return {}; @@ -457,14 +507,17 @@ std::unique_ptr ApkAssets::LoadImpl(std::unique_ptr ApkAssets::LoadTableImpl(std::unique_ptr resources_asset, - const std::string& path, - package_property_t property_flags) { +std::unique_ptr ApkAssets::LoadTableImpl( + std::unique_ptr resources_asset, const std::string& path, + package_property_t property_flags, std::unique_ptr override_assets) { + const time_t last_mod_time = getFileModDate(path.c_str()); + auto assets = (override_assets) ? std::move(override_assets) + : std::unique_ptr(new EmptyAssetsProvider()); + std::unique_ptr loaded_apk( - new ApkAssets(std::unique_ptr(new EmptyAssetsProvider()), path, last_mod_time, - property_flags)); + new ApkAssets(std::move(assets), path, last_mod_time, property_flags)); loaded_apk->resources_asset_ = std::move(resources_asset); const StringPiece data( diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h index 944476890af0..879b050b65bd 100644 --- a/libs/androidfw/include/androidfw/ApkAssets.h +++ b/libs/androidfw/include/androidfw/ApkAssets.h @@ -75,33 +75,33 @@ class ApkAssets { // Creates an ApkAssets. // If `system` is true, the package is marked as a system package, and allows some functions to // filter out this package when computing what configurations/resources are available. - static std::unique_ptr Load(const std::string& path, - package_property_t flags = 0U); + static std::unique_ptr Load( + const std::string& path, package_property_t flags = 0U, + std::unique_ptr override_asset = nullptr); // Creates an ApkAssets from the given file descriptor, and takes ownership of the file // descriptor. The `friendly_name` is some name that will be used to identify the source of // this ApkAssets in log messages and other debug scenarios. // If `length` equals kUnknownLength, offset must equal 0; otherwise, the apk data will be read // using the `offset` into the file descriptor and will be `length` bytes long. - static std::unique_ptr LoadFromFd(base::unique_fd fd, - const std::string& friendly_name, - package_property_t flags = 0U, - off64_t offset = 0, - off64_t length = kUnknownLength); + static std::unique_ptr LoadFromFd( + base::unique_fd fd, const std::string& friendly_name, package_property_t flags = 0U, + std::unique_ptr override_asset = nullptr, off64_t offset = 0, + off64_t length = kUnknownLength); // Creates an ApkAssets from the given path which points to a resources.arsc. - static std::unique_ptr LoadTable(const std::string& path, - package_property_t flags = 0U); + static std::unique_ptr LoadTable( + const std::string& path, package_property_t flags = 0U, + std::unique_ptr override_asset = nullptr); // Creates an ApkAssets from the given file descriptor which points to an resources.arsc, and // takes ownership of the file descriptor. // If `length` equals kUnknownLength, offset must equal 0; otherwise, the .arsc data will be read // using the `offset` into the file descriptor and will be `length` bytes long. - static std::unique_ptr LoadTableFromFd(base::unique_fd fd, - const std::string& friendly_name, - package_property_t flags = 0U, - off64_t offset = 0, - off64_t length = kUnknownLength); + static std::unique_ptr LoadTableFromFd( + base::unique_fd fd, const std::string& friendly_name, package_property_t flags = 0U, + std::unique_ptr override_asset = nullptr, off64_t offset = 0, + off64_t length = kUnknownLength); // Creates an ApkAssets from an IDMAP, which contains the original APK path, and the overlay // data. @@ -110,11 +110,14 @@ class ApkAssets { // Creates an ApkAssets from the directory path. File-based resources are read within the // directory as if the directory is an APK. - static std::unique_ptr LoadFromDir(const std::string& path, - package_property_t flags = 0U); + static std::unique_ptr LoadFromDir( + const std::string& path, package_property_t flags = 0U, + std::unique_ptr override_asset = nullptr); // Creates a totally empty ApkAssets with no resources table and no file entries. - static std::unique_ptr LoadEmpty(package_property_t flags = 0U); + static std::unique_ptr LoadEmpty( + package_property_t flags = 0U, + std::unique_ptr override_asset = nullptr); inline const std::string& GetPath() const { return path_; @@ -158,15 +161,17 @@ class ApkAssets { private: DISALLOW_COPY_AND_ASSIGN(ApkAssets); - static std::unique_ptr LoadImpl(std::unique_ptr assets, - const std::string& path, - std::unique_ptr idmap_asset, - std::unique_ptr idmap, - package_property_t property_flags); - - static std::unique_ptr LoadTableImpl(std::unique_ptr resources_asset, - const std::string& path, - package_property_t property_flags); + static std::unique_ptr LoadImpl( + std::unique_ptr assets, const std::string& path, + package_property_t property_flags, + std::unique_ptr override_assets = nullptr, + std::unique_ptr idmap_asset = nullptr, + std::unique_ptr idmap = nullptr); + + static std::unique_ptr LoadTableImpl( + std::unique_ptr resources_asset, const std::string& path, + package_property_t property_flags, + std::unique_ptr override_assets = nullptr); ApkAssets(std::unique_ptr assets_provider, std::string path, -- cgit v1.2.3 From 39cacf2de79e201ac77e7e1100a7b0ba29abb8f5 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Mon, 16 Mar 2020 14:54:02 -0700 Subject: Allow using loaders on non-RM Resources instances Currently there is a limitation where ResourcesLoaders cannot be used on Resources object not created through ResourcesManager. This change creates an update handler for Resources objects that are not registered with ResourcesManager. The handler changes the loaders on the asset manager owned by the Resources instance. Bug: 151666644 Test: atest ResourceLoaderValuesTest Change-Id: I5a89f686386bdb088dc964014e7becc0c2b4770f --- libs/androidfw/ApkAssets.cpp | 2 +- libs/androidfw/tests/ApkAssets_test.cpp | 3 +-- libs/androidfw/tests/Theme_test.cpp | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) (limited to 'libs') diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index 202651dc86d5..918e7af12d31 100644 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -471,7 +471,7 @@ std::unique_ptr ApkAssets::LoadImpl( bool resources_asset_exists = false; auto resources_asset_ = assets->Open(kResourcesArsc, Asset::AccessMode::ACCESS_BUFFER, &resources_asset_exists); - + assets = MultiAssetsProvider::Create(std::move(override_assets), std::move(assets)); // Wrap the handle in a unique_ptr so it gets automatically closed. diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp index ce9e53244801..19db25ce8811 100644 --- a/libs/androidfw/tests/ApkAssets_test.cpp +++ b/libs/androidfw/tests/ApkAssets_test.cpp @@ -50,8 +50,7 @@ TEST(ApkAssetsTest, LoadApkFromFd) { unique_fd fd(::open(path.c_str(), O_RDONLY | O_BINARY)); ASSERT_THAT(fd.get(), Ge(0)); - std::unique_ptr loaded_apk = - ApkAssets::LoadFromFd(std::move(fd), path, false /*system*/, false /*force_shared_lib*/); + std::unique_ptr loaded_apk = ApkAssets::LoadFromFd(std::move(fd), path); ASSERT_THAT(loaded_apk, NotNull()); const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp index be5ecd94a588..16b9c75982fb 100644 --- a/libs/androidfw/tests/Theme_test.cpp +++ b/libs/androidfw/tests/Theme_test.cpp @@ -36,7 +36,7 @@ namespace android { class ThemeTest : public ::testing::Test { public: void SetUp() override { - system_assets_ = ApkAssets::Load(GetTestDataPath() + "/system/system.apk", true /*system*/); + system_assets_ = ApkAssets::Load(GetTestDataPath() + "/system/system.apk", PROPERTY_SYSTEM); ASSERT_NE(nullptr, system_assets_); style_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk"); -- cgit v1.2.3 From 73597dcb9a737d5e49b98d38bb6952457de491a2 Mon Sep 17 00:00:00 2001 From: Tej Singh Date: Fri, 13 Mar 2020 18:42:40 -0700 Subject: Statsd update for native puller api feedback Update statsd to take in times in milliseconds instead of nanoseconds. Also make appropriate updates for graphics stats, odpm, subsystem sleep state, and LibStatsPullTests Test: atest LibStatsPullTests Test: bit statsd_test:* Bug: 150788562 Change-Id: I593552d6c50bb4dcb89ca9cc1c737781653e7cc5 --- libs/hwui/jni/GraphicsStatsService.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'libs') diff --git a/libs/hwui/jni/GraphicsStatsService.cpp b/libs/hwui/jni/GraphicsStatsService.cpp index 6076552fc094..1591ffabd26a 100644 --- a/libs/hwui/jni/GraphicsStatsService.cpp +++ b/libs/hwui/jni/GraphicsStatsService.cpp @@ -158,17 +158,17 @@ static AStatsManager_PullAtomCallbackReturn graphicsStatsPullCallback(int32_t at static void nativeInit(JNIEnv* env, jobject javaObject) { gGraphicsStatsServiceObject = env->NewGlobalRef(javaObject); AStatsManager_PullAtomMetadata* metadata = AStatsManager_PullAtomMetadata_obtain(); - AStatsManager_PullAtomMetadata_setCoolDownNs(metadata, 10 * 1000000); // 10 milliseconds - AStatsManager_PullAtomMetadata_setTimeoutNs(metadata, 2 * NS_PER_SEC); // 2 seconds + AStatsManager_PullAtomMetadata_setCoolDownMillis(metadata, 10); // 10 milliseconds + AStatsManager_PullAtomMetadata_setTimeoutMillis(metadata, 2 * MS_PER_SEC); // 2 seconds - AStatsManager_registerPullAtomCallback(android::util::GRAPHICS_STATS, - &graphicsStatsPullCallback, metadata, nullptr); + AStatsManager_setPullAtomCallback(android::util::GRAPHICS_STATS, metadata, + &graphicsStatsPullCallback, nullptr); AStatsManager_PullAtomMetadata_release(metadata); } static void nativeDestructor(JNIEnv* env, jobject javaObject) { - AStatsManager_unregisterPullAtomCallback(android::util::GRAPHICS_STATS); + AStatsManager_clearPullAtomCallback(android::util::GRAPHICS_STATS); env->DeleteGlobalRef(gGraphicsStatsServiceObject); gGraphicsStatsServiceObject = nullptr; } -- cgit v1.2.3 From 084aa3cb1705d9f860068d8d080d12f33bdbaff6 Mon Sep 17 00:00:00 2001 From: Joe Onorato Date: Fri, 20 Mar 2020 16:25:14 -0700 Subject: Native API Council asked that we remove the C++ class from the public header. The API is simple enough, so just reimplement everything using the C API directly. Bug: 148940365 Test: treehugger Change-Id: I0a75744e975e8d3c2a557e533eacd03200388ddc --- libs/incident/Android.bp | 2 +- libs/incident/AndroidTest.xml | 28 +++++++++ libs/incident/TEST_MAPPING | 10 +++ libs/incident/include/incident/incident_report.h | 71 +--------------------- libs/incident/tests/IncidentReportRequest_test.cpp | 67 ++++++++++++++++++-- libs/incident/tests/c_api_compile_test.c | 11 ---- 6 files changed, 102 insertions(+), 87 deletions(-) create mode 100644 libs/incident/AndroidTest.xml create mode 100644 libs/incident/TEST_MAPPING delete mode 100644 libs/incident/tests/c_api_compile_test.c (limited to 'libs') diff --git a/libs/incident/Android.bp b/libs/incident/Android.bp index 512b8c439dcf..af6411011411 100644 --- a/libs/incident/Android.bp +++ b/libs/incident/Android.bp @@ -93,6 +93,7 @@ cc_library_shared { cc_test { name: "libincident_test", + test_config: "AndroidTest.xml", defaults: ["libincidentpriv_defaults"], test_suites: ["device-tests"], @@ -104,7 +105,6 @@ cc_test { srcs: [ "tests/IncidentReportArgs_test.cpp", "tests/IncidentReportRequest_test.cpp", - "tests/c_api_compile_test.c", ], shared_libs: [ diff --git a/libs/incident/AndroidTest.xml b/libs/incident/AndroidTest.xml new file mode 100644 index 000000000000..7c0b04471d13 --- /dev/null +++ b/libs/incident/AndroidTest.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + diff --git a/libs/incident/TEST_MAPPING b/libs/incident/TEST_MAPPING new file mode 100644 index 000000000000..59ebe7664b5f --- /dev/null +++ b/libs/incident/TEST_MAPPING @@ -0,0 +1,10 @@ +{ + "presubmit": [ + { + "name": "libincident_test" + }, + { + "name": "GtsLibIncidentTests" + } + ] +} diff --git a/libs/incident/include/incident/incident_report.h b/libs/incident/include/incident/incident_report.h index 49fe5b9b73b4..4fbac9681214 100644 --- a/libs/incident/include/incident/incident_report.h +++ b/libs/incident/include/incident/incident_report.h @@ -18,16 +18,12 @@ * @file incident_report.h */ -#ifndef ANDROID_INCIDENT_INCIDENT_REPORT_H -#define ANDROID_INCIDENT_INCIDENT_REPORT_H +#pragma once #include +#include #if __cplusplus -#include -#include -#include - extern "C" { #endif // __cplusplus @@ -125,68 +121,5 @@ int AIncidentReportArgs_takeReport(AIncidentReportArgs* args); #if __cplusplus } // extern "C" - -namespace android { -namespace os { - -class IncidentReportRequest { -public: - inline IncidentReportRequest() { - mImpl = AIncidentReportArgs_init(); - } - - inline IncidentReportRequest(const IncidentReportRequest& that) { - mImpl = AIncidentReportArgs_clone(that.mImpl); - } - - inline ~IncidentReportRequest() { - AIncidentReportArgs_delete(mImpl); - } - - inline AIncidentReportArgs* getImpl() { - return mImpl; - } - - inline void setAll(bool all) { - AIncidentReportArgs_setAll(mImpl, all); - } - - inline void setPrivacyPolicy(int privacyPolicy) { - AIncidentReportArgs_setPrivacyPolicy(mImpl, privacyPolicy); - } - - inline void addSection(int section) { - AIncidentReportArgs_addSection(mImpl, section); - } - - inline void setReceiverPackage(const std::string& pkg) { - AIncidentReportArgs_setReceiverPackage(mImpl, pkg.c_str()); - }; - - inline void setReceiverClass(const std::string& cls) { - AIncidentReportArgs_setReceiverClass(mImpl, cls.c_str()); - }; - - inline void addHeader(const std::vector& headerProto) { - AIncidentReportArgs_addHeader(mImpl, headerProto.data(), headerProto.size()); - }; - - inline void addHeader(const uint8_t* buf, size_t size) { - AIncidentReportArgs_addHeader(mImpl, buf, size); - }; - - // returns a status_t - inline int takeReport() { - return AIncidentReportArgs_takeReport(mImpl); - } - -private: - AIncidentReportArgs* mImpl; -}; - -} // namespace os -} // namespace android - #endif // __cplusplus -#endif // ANDROID_INCIDENT_INCIDENT_REPORT_H diff --git a/libs/incident/tests/IncidentReportRequest_test.cpp b/libs/incident/tests/IncidentReportRequest_test.cpp index 6d218b6682a3..5619bb6c2220 100644 --- a/libs/incident/tests/IncidentReportRequest_test.cpp +++ b/libs/incident/tests/IncidentReportRequest_test.cpp @@ -17,9 +17,67 @@ #include -namespace android { -namespace os { -namespace statsd { +#include +#include + +using namespace std; +using namespace android::os; + +class IncidentReportRequest { +public: + inline IncidentReportRequest() { + mImpl = AIncidentReportArgs_init(); + } + + inline IncidentReportRequest(const IncidentReportRequest& that) { + mImpl = AIncidentReportArgs_clone(that.mImpl); + } + + inline ~IncidentReportRequest() { + AIncidentReportArgs_delete(mImpl); + } + + inline AIncidentReportArgs* getImpl() { + return mImpl; + } + + inline void setAll(bool all) { + AIncidentReportArgs_setAll(mImpl, all); + } + + inline void setPrivacyPolicy(int privacyPolicy) { + AIncidentReportArgs_setPrivacyPolicy(mImpl, privacyPolicy); + } + + inline void addSection(int section) { + AIncidentReportArgs_addSection(mImpl, section); + } + + inline void setReceiverPackage(const string& pkg) { + AIncidentReportArgs_setReceiverPackage(mImpl, pkg.c_str()); + }; + + inline void setReceiverClass(const string& cls) { + AIncidentReportArgs_setReceiverClass(mImpl, cls.c_str()); + }; + + inline void addHeader(const vector& headerProto) { + AIncidentReportArgs_addHeader(mImpl, headerProto.data(), headerProto.size()); + }; + + inline void addHeader(const uint8_t* buf, size_t size) { + AIncidentReportArgs_addHeader(mImpl, buf, size); + }; + + // returns a status_t + inline int takeReport() { + return AIncidentReportArgs_takeReport(mImpl); + } + +private: + AIncidentReportArgs* mImpl; +}; + TEST(IncidentReportRequestTest, testWrite) { IncidentReportRequest request; @@ -60,6 +118,3 @@ TEST(IncidentReportRequestTest, testWrite) { EXPECT_EQ(headers, args->headers()); } -} // namespace statsd -} // namespace os -} // namespace android diff --git a/libs/incident/tests/c_api_compile_test.c b/libs/incident/tests/c_api_compile_test.c deleted file mode 100644 index e1620dfe3280..000000000000 --- a/libs/incident/tests/c_api_compile_test.c +++ /dev/null @@ -1,11 +0,0 @@ -#include -#include - -/* - * This file ensures that incident/incident_report.h actually compiles with C, - * since there is no other place in the tree that actually uses it from C. - */ -int not_called() { - return 0; -} - -- cgit v1.2.3 From b894c274d01f47911770a44151732a0139d81873 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Wed, 12 Feb 2020 10:31:44 -0800 Subject: Revert "Assign shared libraries stable package ids" This change must be reverted because it broke packages with the same package name but different package ids loaded at once. Bug: 146685730 Test: MultiSplitInstallTest This reverts commit fe50d739f75e13ebf64c010bf6ef504bcc81d860. Change-Id: If6364fd660c76284452f77e7d4f09a3df9dede1d --- libs/androidfw/Android.bp | 1 - libs/androidfw/AssetManager2.cpp | 72 ++++++---------------- libs/androidfw/DynamicLibManager.cpp | 34 ---------- libs/androidfw/include/androidfw/AssetManager2.h | 7 --- .../include/androidfw/DynamicLibManager.h | 48 --------------- libs/androidfw/include/androidfw/MutexGuard.h | 3 +- libs/androidfw/tests/AssetManager2_test.cpp | 19 ------ 7 files changed, 20 insertions(+), 164 deletions(-) delete mode 100644 libs/androidfw/DynamicLibManager.cpp delete mode 100644 libs/androidfw/include/androidfw/DynamicLibManager.h (limited to 'libs') diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index f87f98a59a12..02c85aa34f4b 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -44,7 +44,6 @@ cc_library { "AttributeResolution.cpp", "ChunkIterator.cpp", "ConfigDescription.cpp", - "DynamicLibManager.cpp", "Idmap.cpp", "LoadedArsc.cpp", "Locale.cpp", diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index f20e18453f8b..803828fc16c5 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -25,7 +25,6 @@ #include "android-base/logging.h" #include "android-base/stringprintf.h" -#include "androidfw/DynamicLibManager.h" #include "androidfw/ResourceUtils.h" #include "androidfw/Util.h" #include "utils/ByteOrder.h" @@ -67,12 +66,7 @@ struct FindEntryResult { StringPoolRef entry_string_ref; }; -AssetManager2::AssetManager2() : dynamic_lib_manager_(std::make_unique()) { - memset(&configuration_, 0, sizeof(configuration_)); -} - -AssetManager2::AssetManager2(DynamicLibManager* dynamic_lib_manager) - : dynamic_lib_manager_(dynamic_lib_manager) { +AssetManager2::AssetManager2() { memset(&configuration_, 0, sizeof(configuration_)); } @@ -91,44 +85,24 @@ void AssetManager2::BuildDynamicRefTable() { package_groups_.clear(); package_ids_.fill(0xff); - // Overlay resources are not directly referenced by an application so their resource ids - // can change throughout the application's lifetime. Assign overlay package ids last. - std::vector sorted_apk_assets(apk_assets_); - std::stable_partition(sorted_apk_assets.begin(), sorted_apk_assets.end(), [](const ApkAssets* a) { - return !a->IsOverlay(); - }); - + // A mapping from apk assets path to the runtime package id of its first loaded package. std::unordered_map apk_assets_package_ids; - std::unordered_map package_name_package_ids; - - // Assign stable package ids to application packages. - uint8_t next_available_package_id = 0U; - for (const auto& apk_assets : sorted_apk_assets) { - for (const auto& package : apk_assets->GetLoadedArsc()->GetPackages()) { - uint8_t package_id = package->GetPackageId(); - if (package->IsOverlay()) { - package_id = GetDynamicLibManager()->FindUnassignedId(next_available_package_id); - next_available_package_id = package_id + 1; - } else if (package->IsDynamic()) { - package_id = GetDynamicLibManager()->GetAssignedId(package->GetPackageName()); - } - - // Map the path of the apk assets to the package id of its first loaded package. - apk_assets_package_ids[apk_assets->GetPath()] = package_id; - // Map the package name of the package to the first loaded package with that package id. - package_name_package_ids[package->GetPackageName()] = package_id; - } - } + // 0x01 is reserved for the android package. + int next_package_id = 0x02; + const size_t apk_assets_count = apk_assets_.size(); + for (size_t i = 0; i < apk_assets_count; i++) { + const ApkAssets* apk_assets = apk_assets_[i]; + const LoadedArsc* loaded_arsc = apk_assets->GetLoadedArsc(); - const int apk_assets_count = apk_assets_.size(); - for (int i = 0; i < apk_assets_count; i++) { - const auto& apk_assets = apk_assets_[i]; - for (const auto& package : apk_assets->GetLoadedArsc()->GetPackages()) { - const auto package_id_entry = package_name_package_ids.find(package->GetPackageName()); - CHECK(package_id_entry != package_name_package_ids.end()) - << "no package id assgined to package " << package->GetPackageName(); - const uint8_t package_id = package_id_entry->second; + for (const std::unique_ptr& package : loaded_arsc->GetPackages()) { + // Get the package ID or assign one if a shared library. + int package_id; + if (package->IsDynamic()) { + package_id = next_package_id++; + } else { + package_id = package->GetPackageId(); + } // Add the mapping for package ID to index if not present. uint8_t idx = package_ids_[package_id]; @@ -152,7 +126,7 @@ void AssetManager2::BuildDynamicRefTable() { PackageGroup& target_package_group = package_groups_[target_idx]; - // Create a special dynamic reference table for the overlay to rewrite references to + // Create a special dynamic reference table for the overlay to rewite references to // overlay resources as references to the target resources they overlay. auto overlay_table = std::make_shared( loaded_idmap->GetOverlayDynamicRefTable(target_package_id)); @@ -182,6 +156,8 @@ void AssetManager2::BuildDynamicRefTable() { package_group->dynamic_ref_table->mEntries.replaceValueFor( package_name, static_cast(entry.package_id)); } + + apk_assets_package_ids.insert(std::make_pair(apk_assets->GetPath(), package_id)); } } @@ -1329,16 +1305,6 @@ uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) const return 0; } -DynamicLibManager* AssetManager2::GetDynamicLibManager() const { - auto dynamic_lib_manager = - std::get_if>(&dynamic_lib_manager_); - if (dynamic_lib_manager) { - return (*dynamic_lib_manager).get(); - } else { - return *std::get_if(&dynamic_lib_manager_); - } -} - std::unique_ptr AssetManager2::NewTheme() { return std::unique_ptr(new Theme(this)); } diff --git a/libs/androidfw/DynamicLibManager.cpp b/libs/androidfw/DynamicLibManager.cpp deleted file mode 100644 index 895b7695bf26..000000000000 --- a/libs/androidfw/DynamicLibManager.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2019 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 "androidfw/DynamicLibManager.h" - -namespace android { - -uint8_t DynamicLibManager::GetAssignedId(const std::string& library_package_name) { - auto lib_entry = shared_lib_package_ids_.find(library_package_name); - if (lib_entry != shared_lib_package_ids_.end()) { - return lib_entry->second; - } - - return shared_lib_package_ids_[library_package_name] = next_package_id_++; -} - -uint8_t DynamicLibManager::FindUnassignedId(uint8_t start_package_id) { - return (start_package_id < next_package_id_) ? next_package_id_ : start_package_id; -} - -} // namespace android diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index b2cec2a42994..00cbbcad56e6 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -27,7 +27,6 @@ #include "androidfw/ApkAssets.h" #include "androidfw/Asset.h" #include "androidfw/AssetManager.h" -#include "androidfw/DynamicLibManager.h" #include "androidfw/ResourceTypes.h" #include "androidfw/Util.h" @@ -95,7 +94,6 @@ class AssetManager2 { }; AssetManager2(); - explicit AssetManager2(DynamicLibManager* dynamic_lib_manager); // Sets/resets the underlying ApkAssets for this AssetManager. The ApkAssets // are not owned by the AssetManager, and must have a longer lifetime. @@ -373,8 +371,6 @@ class AssetManager2 { // Retrieve the assigned package id of the package if loaded into this AssetManager uint8_t GetAssignedPackageId(const LoadedPackage* package) const; - DynamicLibManager* GetDynamicLibManager() const; - // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must // have a longer lifetime. std::vector apk_assets_; @@ -393,9 +389,6 @@ class AssetManager2 { // may need to be purged. ResTable_config configuration_; - // Component responsible for assigning package ids to shared libraries. - std::variant, DynamicLibManager*> dynamic_lib_manager_; - // Cached set of bags. These are cached because they can inherit keys from parent bags, // which involves some calculation. std::unordered_map> cached_bags_; diff --git a/libs/androidfw/include/androidfw/DynamicLibManager.h b/libs/androidfw/include/androidfw/DynamicLibManager.h deleted file mode 100644 index 1ff7079573d2..000000000000 --- a/libs/androidfw/include/androidfw/DynamicLibManager.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2019 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 ANDROIDFW_DYNAMICLIBMANAGER_H -#define ANDROIDFW_DYNAMICLIBMANAGER_H - -#include -#include - -#include "android-base/macros.h" - -namespace android { - -// Manages assigning resource ids for dynamic resources. -class DynamicLibManager { - public: - DynamicLibManager() = default; - - // Retrieves the assigned package id for the library. - uint8_t GetAssignedId(const std::string& library_package_name); - - // Queries in ascending order for the first available package id that is not currently assigned to - // a library. - uint8_t FindUnassignedId(uint8_t start_package_id); - - private: - DISALLOW_COPY_AND_ASSIGN(DynamicLibManager); - - uint8_t next_package_id_ = 0x02; - std::unordered_map shared_lib_package_ids_; -}; - -} // namespace android - -#endif //ANDROIDFW_DYNAMICLIBMANAGER_H diff --git a/libs/androidfw/include/androidfw/MutexGuard.h b/libs/androidfw/include/androidfw/MutexGuard.h index 8891512958f0..64924f433245 100644 --- a/libs/androidfw/include/androidfw/MutexGuard.h +++ b/libs/androidfw/include/androidfw/MutexGuard.h @@ -47,8 +47,7 @@ class Guarded { static_assert(!std::is_pointer::value, "T must not be a raw pointer"); public: - template - explicit Guarded(Args&& ...args) : guarded_(std::forward(args)...) { + explicit Guarded() : guarded_() { } template diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index ac32699c6dfd..6054fa3338bd 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -216,25 +216,6 @@ TEST_F(AssetManager2Test, FindsResourceFromAppLoadedAsSharedLibrary) { EXPECT_EQ(fix_package_id(appaslib::R::array::integerArray1, 0x02), value.data); } -TEST_F(AssetManager2Test, AssignsUnchangingPackageIdToSharedLibrary) { - DynamicLibManager lib_manager; - AssetManager2 assetmanager(&lib_manager); - assetmanager.SetApkAssets( - {lib_one_assets_.get(), lib_two_assets_.get(), libclient_assets_.get()}); - - AssetManager2 assetmanager2(&lib_manager); - assetmanager2.SetApkAssets( - {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()}); - - uint32_t res_id = assetmanager.GetResourceId("com.android.lib_one:string/foo"); - ASSERT_NE(0U, res_id); - - uint32_t res_id_2 = assetmanager2.GetResourceId("com.android.lib_one:string/foo"); - ASSERT_NE(0U, res_id_2); - - ASSERT_EQ(res_id, res_id_2); -} - TEST_F(AssetManager2Test, GetSharedLibraryResourceName) { AssetManager2 assetmanager; assetmanager.SetApkAssets({lib_one_assets_.get()}); -- cgit v1.2.3 From 824cc490c54d9a488c561965770abec09a32c0a5 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Wed, 12 Feb 2020 10:48:14 -0800 Subject: Assigned package ids to overlays last Before overlays could reference internal resources, overlays were added to AssetManagers before shared libraries. Overlays are now loaded as shared libraries so they must be assigned package ids after non-overlay shared libraries so enabling and disabling overlays does not affect the package ids of the non-overlay shared libraries. Since overlays are added to the end of the AssetManager by ResourcesManager, enabling and disabling overlays will not change the assets cookie of shared libraries loaded through ResourcesManager, but it will change the apk assets cookie or overlays added through AssetManager#addAssetPathAsSharedLibrary. The package ids of shared libraries added through AssetManager#addAssetPathAsSharedLibrary will not be affected by overlays since overlay package ids are assigned last. Bug: 146685730 Test: CtsHostsideWebViewTests Change-Id: If7ea17d51b18769bf2465e29af3ae6a71004d06c --- libs/androidfw/AssetManager2.cpp | 25 ++++++++--- libs/androidfw/include/androidfw/AssetManager2.h | 6 +-- libs/androidfw/tests/AssetManager2_test.cpp | 56 +++++++++++++++++------- 3 files changed, 62 insertions(+), 25 deletions(-) (limited to 'libs') diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 803828fc16c5..eaf452b5fa71 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -88,13 +88,24 @@ void AssetManager2::BuildDynamicRefTable() { // A mapping from apk assets path to the runtime package id of its first loaded package. std::unordered_map apk_assets_package_ids; + // Overlay resources are not directly referenced by an application so their resource ids + // can change throughout the application's lifetime. Assign overlay package ids last. + std::vector sorted_apk_assets(apk_assets_); + std::stable_partition(sorted_apk_assets.begin(), sorted_apk_assets.end(), [](const ApkAssets* a) { + return !a->IsOverlay(); + }); + + // The assets cookie must map to the position of the apk assets in the unsorted apk assets list. + std::unordered_map apk_assets_cookies; + apk_assets_cookies.reserve(apk_assets_.size()); + for (size_t i = 0, n = apk_assets_.size(); i < n; i++) { + apk_assets_cookies[apk_assets_[i]] = static_cast(i); + } + // 0x01 is reserved for the android package. int next_package_id = 0x02; - const size_t apk_assets_count = apk_assets_.size(); - for (size_t i = 0; i < apk_assets_count; i++) { - const ApkAssets* apk_assets = apk_assets_[i]; + for (const ApkAssets* apk_assets : sorted_apk_assets) { const LoadedArsc* loaded_arsc = apk_assets->GetLoadedArsc(); - for (const std::unique_ptr& package : loaded_arsc->GetPackages()) { // Get the package ID or assign one if a shared library. int package_id; @@ -126,7 +137,7 @@ void AssetManager2::BuildDynamicRefTable() { PackageGroup& target_package_group = package_groups_[target_idx]; - // Create a special dynamic reference table for the overlay to rewite references to + // Create a special dynamic reference table for the overlay to rewrite references to // overlay resources as references to the target resources they overlay. auto overlay_table = std::make_shared( loaded_idmap->GetOverlayDynamicRefTable(target_package_id)); @@ -136,7 +147,7 @@ void AssetManager2::BuildDynamicRefTable() { target_package_group.overlays_.push_back( ConfiguredOverlay{loaded_idmap->GetTargetResourcesMap(target_package_id, overlay_table.get()), - static_cast(i)}); + apk_assets_cookies[apk_assets]}); } } @@ -148,7 +159,7 @@ void AssetManager2::BuildDynamicRefTable() { // Add the package and to the set of packages with the same ID. package_group->packages_.push_back(ConfiguredPackage{package.get(), {}}); - package_group->cookies_.push_back(static_cast(i)); + package_group->cookies_.push_back(apk_assets_cookies[apk_assets]); // Add the package name -> build time ID mappings. for (const DynamicPackageEntry& entry : package->GetDynamicPackageMap()) { diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index 00cbbcad56e6..e21abade99a4 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -124,6 +124,9 @@ class AssetManager2 { // This may be nullptr if the APK represented by `cookie` has no resource table. std::shared_ptr GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const; + // Retrieve the assigned package id of the package if loaded into this AssetManager + uint8_t GetAssignedPackageId(const LoadedPackage* package) const; + // Returns a string representation of the overlayable API of a package. bool GetOverlayablesToString(const android::StringPiece& package_name, std::string* out) const; @@ -368,9 +371,6 @@ class AssetManager2 { // been seen while traversing bag parents. const ResolvedBag* GetBag(uint32_t resid, std::vector& child_resids); - // Retrieve the assigned package id of the package if loaded into this AssetManager - uint8_t GetAssignedPackageId(const LoadedPackage* package) const; - // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must // have a longer lifetime. std::vector apk_assets_; diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index 6054fa3338bd..8c255d16fe1f 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -17,9 +17,9 @@ #include "androidfw/AssetManager2.h" #include "androidfw/AssetManager.h" -#include "android-base/logging.h" - #include "TestHelpers.h" +#include "android-base/file.h" +#include "android-base/logging.h" #include "androidfw/ResourceUtils.h" #include "data/appaslib/R.h" #include "data/basic/R.h" @@ -45,37 +45,43 @@ namespace android { class AssetManager2Test : public ::testing::Test { public: void SetUp() override { - basic_assets_ = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); + // Move to the test data directory so the idmap can locate the overlay APK. + std::string original_path = base::GetExecutableDirectory(); + chdir(GetTestDataPath().c_str()); + + basic_assets_ = ApkAssets::Load("basic/basic.apk"); ASSERT_NE(nullptr, basic_assets_); - basic_de_fr_assets_ = ApkAssets::Load(GetTestDataPath() + "/basic/basic_de_fr.apk"); + basic_de_fr_assets_ = ApkAssets::Load("basic/basic_de_fr.apk"); ASSERT_NE(nullptr, basic_de_fr_assets_); - style_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk"); + style_assets_ = ApkAssets::Load("styles/styles.apk"); ASSERT_NE(nullptr, style_assets_); - lib_one_assets_ = ApkAssets::Load(GetTestDataPath() + "/lib_one/lib_one.apk"); + lib_one_assets_ = ApkAssets::Load("lib_one/lib_one.apk"); ASSERT_NE(nullptr, lib_one_assets_); - lib_two_assets_ = ApkAssets::Load(GetTestDataPath() + "/lib_two/lib_two.apk"); + lib_two_assets_ = ApkAssets::Load("lib_two/lib_two.apk"); ASSERT_NE(nullptr, lib_two_assets_); - libclient_assets_ = ApkAssets::Load(GetTestDataPath() + "/libclient/libclient.apk"); + libclient_assets_ = ApkAssets::Load("libclient/libclient.apk"); ASSERT_NE(nullptr, libclient_assets_); - appaslib_assets_ = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk", - PROPERTY_DYNAMIC); + appaslib_assets_ = ApkAssets::Load("appaslib/appaslib.apk", PROPERTY_DYNAMIC); ASSERT_NE(nullptr, appaslib_assets_); - system_assets_ = ApkAssets::Load(GetTestDataPath() + "/system/system.apk", - PROPERTY_SYSTEM); + system_assets_ = ApkAssets::Load("system/system.apk", PROPERTY_SYSTEM); ASSERT_NE(nullptr, system_assets_); - app_assets_ = ApkAssets::Load(GetTestDataPath() + "/app/app.apk"); + app_assets_ = ApkAssets::Load("app/app.apk"); ASSERT_THAT(app_assets_, NotNull()); - overlayable_assets_ = ApkAssets::Load(GetTestDataPath() + "/overlayable/overlayable.apk"); + overlay_assets_ = ApkAssets::LoadOverlay("overlay/overlay.idmap"); + ASSERT_NE(nullptr, overlay_assets_); + + overlayable_assets_ = ApkAssets::Load("overlayable/overlayable.apk"); ASSERT_THAT(overlayable_assets_, NotNull()); + chdir(original_path.c_str()); } protected: @@ -88,6 +94,7 @@ class AssetManager2Test : public ::testing::Test { std::unique_ptr appaslib_assets_; std::unique_ptr system_assets_; std::unique_ptr app_assets_; + std::unique_ptr overlay_assets_; std::unique_ptr overlayable_assets_; }; @@ -216,6 +223,26 @@ TEST_F(AssetManager2Test, FindsResourceFromAppLoadedAsSharedLibrary) { EXPECT_EQ(fix_package_id(appaslib::R::array::integerArray1, 0x02), value.data); } +TEST_F(AssetManager2Test, AssignsOverlayPackageIdLast) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets( + {overlayable_assets_.get(), overlay_assets_.get(), lib_one_assets_.get()}); + + auto apk_assets = assetmanager.GetApkAssets(); + ASSERT_EQ(3, apk_assets.size()); + ASSERT_EQ(overlayable_assets_.get(), apk_assets[0]); + ASSERT_EQ(overlay_assets_.get(), apk_assets[1]); + ASSERT_EQ(lib_one_assets_.get(), apk_assets[2]); + + auto get_first_package_id = [&assetmanager](const ApkAssets* apkAssets) -> uint8_t { + return assetmanager.GetAssignedPackageId(apkAssets->GetLoadedArsc()->GetPackages()[0].get()); + }; + + ASSERT_EQ(get_first_package_id(overlayable_assets_.get()), 0x7f); + ASSERT_EQ(get_first_package_id(overlay_assets_.get()), 0x03); + ASSERT_EQ(get_first_package_id(lib_one_assets_.get()), 0x02); +} + TEST_F(AssetManager2Test, GetSharedLibraryResourceName) { AssetManager2 assetmanager; assetmanager.SetApkAssets({lib_one_assets_.get()}); @@ -751,7 +778,6 @@ TEST_F(AssetManager2Test, GetOverlayablesToString) { ASSERT_EQ(api.find("not_overlayable"), std::string::npos); ASSERT_NE(api.find("resource='com.android.overlayable:string/overlayable2' overlayable='OverlayableResources1' actor='overlay://theme' policy='0x0000000a'\n"), std::string::npos); - } } // namespace android -- cgit v1.2.3 From 892ccd9bd5184cb71123040ac84f1bdec21cecaa Mon Sep 17 00:00:00 2001 From: Mike Ma Date: Fri, 20 Mar 2020 16:30:37 -0700 Subject: Optimize memory usage in incidentd EncodedBuffer is used a lot in incidentd. EncodedBuffer uses malloc internally to acquire memory. Frequently creating and destroying EncodedBuffer creates memory fragmentation, leading to high memory usage after taking an incident report. Also fixes a few other places with lots of malloc/free operations. This change: * Creates a pool of EncodedBuffer in incidentd. The saving is significant. It reduces EncodedBuffer creation from 3 per section to 3 per report. * Replaces malloc with mmap inside EncodedBuffer. mmap is guaranteed to be mem page aligned, so there will be no mem fragmentation after destroying EncodedBuffer. * Replaces new with mmap inside TombstoneSection * Forks a process to execute LogSection, because liblog malloc & free significant amount of memory Result: PSS before taking a report: 1295 KB PSS after taking a report: 1336 KB Bug: 150311553 Test: heapprofd Change-Id: I83bd9c969b751c80b2f42747020799bd85d8aae6 --- libs/protoutil/include/android/util/ProtoOutputStream.h | 1 + libs/protoutil/src/EncodedBuffer.cpp | 14 ++++++++++---- libs/protoutil/src/ProtoOutputStream.cpp | 8 ++++++-- 3 files changed, 17 insertions(+), 6 deletions(-) (limited to 'libs') diff --git a/libs/protoutil/include/android/util/ProtoOutputStream.h b/libs/protoutil/include/android/util/ProtoOutputStream.h index 42bf03e6de05..f4a358de1c41 100644 --- a/libs/protoutil/include/android/util/ProtoOutputStream.h +++ b/libs/protoutil/include/android/util/ProtoOutputStream.h @@ -90,6 +90,7 @@ class ProtoOutputStream { public: ProtoOutputStream(); + ProtoOutputStream(sp buffer); ~ProtoOutputStream(); /** diff --git a/libs/protoutil/src/EncodedBuffer.cpp b/libs/protoutil/src/EncodedBuffer.cpp index 7ffd8874a8fb..96b54c63a836 100644 --- a/libs/protoutil/src/EncodedBuffer.cpp +++ b/libs/protoutil/src/EncodedBuffer.cpp @@ -16,6 +16,7 @@ #define LOG_TAG "libprotoutil" #include +#include #include #include @@ -82,14 +83,16 @@ EncodedBuffer::Pointer::copy() const } // =========================================================== -EncodedBuffer::EncodedBuffer() : EncodedBuffer(0) +EncodedBuffer::EncodedBuffer() : EncodedBuffer(BUFFER_SIZE) { } EncodedBuffer::EncodedBuffer(size_t chunkSize) :mBuffers() { - mChunkSize = chunkSize == 0 ? BUFFER_SIZE : chunkSize; + // Align chunkSize to memory page size + chunkSize = chunkSize == 0 ? BUFFER_SIZE : chunkSize; + mChunkSize = (chunkSize / PAGE_SIZE + ((chunkSize % PAGE_SIZE == 0) ? 0 : 1)) * PAGE_SIZE; mWp = Pointer(mChunkSize); mEp = Pointer(mChunkSize); } @@ -98,7 +101,7 @@ EncodedBuffer::~EncodedBuffer() { for (size_t i=0; i mBuffers.size()) return NULL; uint8_t* buf = NULL; if (mWp.index() == mBuffers.size()) { - buf = (uint8_t*)malloc(mChunkSize); + // Use mmap instead of malloc to ensure memory alignment i.e. no fragmentation so that + // the mem region can be immediately reused by the allocator after calling munmap() + buf = (uint8_t*)mmap(NULL, mChunkSize, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); if (buf == NULL) return NULL; // This indicates NO_MEMORY diff --git a/libs/protoutil/src/ProtoOutputStream.cpp b/libs/protoutil/src/ProtoOutputStream.cpp index ea9b79a0353f..fcf82eed4eb1 100644 --- a/libs/protoutil/src/ProtoOutputStream.cpp +++ b/libs/protoutil/src/ProtoOutputStream.cpp @@ -26,8 +26,12 @@ namespace android { namespace util { -ProtoOutputStream::ProtoOutputStream() - :mBuffer(new EncodedBuffer()), +ProtoOutputStream::ProtoOutputStream(): ProtoOutputStream(new EncodedBuffer()) +{ +} + +ProtoOutputStream::ProtoOutputStream(sp buffer) + :mBuffer(buffer), mCopyBegin(0), mCompact(false), mDepth(0), -- cgit v1.2.3 From 026106f6d68382988502b093e963bfb3d4153396 Mon Sep 17 00:00:00 2001 From: Alec Mouri Date: Thu, 26 Mar 2020 17:17:09 -0700 Subject: [HWUI] Add null check for CanvasContext If ReliableSurface is forced to acquire a fallback buffer then the Surface may be abandoned. When getting frame timestamps we need to check that the Surface still exists. Bug: 152262035 Test: builds, boots Test: dumpsys gfxinfo Change-Id: Ifdb198ebf74cc9dc681c4ab51c4901176a7f5fc9 --- libs/hwui/renderthread/CanvasContext.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'libs') diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 4299dd3b46fe..c19b1878ad45 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -555,9 +555,11 @@ void CanvasContext::draw() { FrameInfo* forthBehind = mLast4FrameInfos.front().first; int64_t composedFrameId = mLast4FrameInfos.front().second; nsecs_t acquireTime = -1; - native_window_get_frame_timestamps(mNativeSurface->getNativeWindow(), composedFrameId, - nullptr, &acquireTime, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr); + if (mNativeSurface) { + native_window_get_frame_timestamps(mNativeSurface->getNativeWindow(), composedFrameId, + nullptr, &acquireTime, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr); + } // Ignore default -1, NATIVE_WINDOW_TIMESTAMP_INVALID and NATIVE_WINDOW_TIMESTAMP_PENDING forthBehind->set(FrameInfoIndex::GpuCompleted) = acquireTime > 0 ? acquireTime : -1; mJankTracker.finishGpuDraw(*forthBehind); -- cgit v1.2.3 From 199b09a1a73f831dd984f9139c46ae2a462d8898 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Wed, 1 Apr 2020 11:25:18 -0700 Subject: Begin moving FrameworkResourceLoaderTest to cts This changes removes FrameworkResourceLoaderTest from frameworks/base before it is moved to CTS. Bug: 152979463 Test: atest CtsResourcesLoaderTest Change-Id: I4b899564ab93472cb6d2a5ed0917026753c2827f --- libs/androidfw/TEST_MAPPING | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/androidfw/TEST_MAPPING b/libs/androidfw/TEST_MAPPING index d1a6a5c18299..777aa0b429e5 100644 --- a/libs/androidfw/TEST_MAPPING +++ b/libs/androidfw/TEST_MAPPING @@ -5,7 +5,7 @@ "host": true }, { - "name": "FrameworksResourceLoaderTests" + "name": "CtsResourcesLoaderTests" } ] } \ No newline at end of file -- cgit v1.2.3 From f889444fa9d6b0ab505b3ebd3812c70605ca8c96 Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Thu, 2 Apr 2020 11:38:48 -0400 Subject: AImageDecoder: Suppress log messages Bug: 153071768 Test: AImageDecoderTest For simplicity, I1aff544e8d6932b9ed0931a00da66a0aba6cd536 made ImageDecoder skip specifying kOpaque when setting the SkImageInfo for decoding. For some formats (e.g. jpeg and heif), this results in a log message: "Warning: an opaque image should be decoded as opaque - it is being decoded as non-opaque, which will draw slower" This isn't relevant to AImageDecoder, which doesn't let you specify kOpaque or not (since the intent is not to create an SkBitmap for Skia). Now that ImageDecoder specifies kOpaque properly, the JNI code no longer needs to correct it. Replace another opacity check with ::opaque() for simplicity. Change-Id: I7c270d9b9db61a61a338f40b056ce5d23a56e14d --- libs/hwui/hwui/ImageDecoder.cpp | 9 +++------ libs/hwui/jni/ImageDecoder.cpp | 3 --- 2 files changed, 3 insertions(+), 9 deletions(-) (limited to 'libs') diff --git a/libs/hwui/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp index afd82aca07c5..43cc4f244f71 100644 --- a/libs/hwui/hwui/ImageDecoder.cpp +++ b/libs/hwui/hwui/ImageDecoder.cpp @@ -50,10 +50,8 @@ ImageDecoder::ImageDecoder(std::unique_ptr codec, sk_spcomputeSampleSize(&decodeSize); - if (decodeSize != targetSize && mUnpremultipliedRequired - && !mCodec->getInfo().isOpaque()) { + if (decodeSize != targetSize && mUnpremultipliedRequired && !opaque()) { return false; } diff --git a/libs/hwui/jni/ImageDecoder.cpp b/libs/hwui/jni/ImageDecoder.cpp index b6b378539bd0..41d939bd6373 100644 --- a/libs/hwui/jni/ImageDecoder.cpp +++ b/libs/hwui/jni/ImageDecoder.cpp @@ -305,9 +305,6 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong } SkImageInfo bitmapInfo = decoder->getOutputInfo(); - if (decoder->opaque()) { - bitmapInfo = bitmapInfo.makeAlphaType(kOpaque_SkAlphaType); - } if (asAlphaMask && colorType == kGray_8_SkColorType) { bitmapInfo = bitmapInfo.makeColorType(kAlpha_8_SkColorType); } -- cgit v1.2.3 From d3e9eecf5b2d70386a5557dc033b3d3abae72dc8 Mon Sep 17 00:00:00 2001 From: Derek Sollenberger Date: Mon, 6 Apr 2020 11:43:59 -0400 Subject: Don't update the genID for an immutable bitmap AndroidBitmap_unlockPixels is the only way to release the ref on pixels acquired using AndroidBitmap_lockPixels. Apps that lock an immutable bitmap therefore are forced to call unlock pixels. Prior to this CL the unlock would update the GenID of the bitmap and also print a warning that the app was modifying an immutable bitmap. After this CL the bitmap's GenID will not be updated and no warning will be printed. As a result, apps that were using the NDK to allow them to mutate immutable bitmaps will break without getting a warning message. Test: hwui_unit_tests Bug: 150823341 Change-Id: Ie5e5cf86a7a83d53c179c9b3cf9be1a0566cfd93 --- libs/hwui/Android.bp | 9 ++++--- libs/hwui/apex/android_bitmap.cpp | 5 ++-- libs/hwui/tests/unit/ABitmapTests.cpp | 46 +++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 6 deletions(-) create mode 100644 libs/hwui/tests/unit/ABitmapTests.cpp (limited to 'libs') diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index ac2fd98248d0..aa842ff6a7b7 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -171,7 +171,6 @@ cc_library_headers { cc_defaults { name: "android_graphics_apex", - host_supported: true, cflags: [ "-Wno-unused-parameter", "-Wno-non-virtual-dtor", @@ -231,7 +230,6 @@ cc_library_headers { cc_defaults { name: "android_graphics_jni", - host_supported: true, cflags: [ "-Wno-unused-parameter", "-Wno-non-virtual-dtor", @@ -537,7 +535,11 @@ cc_defaults { cc_test { name: "hwui_unit_tests", - defaults: ["hwui_test_defaults"], + defaults: [ + "hwui_test_defaults", + "android_graphics_apex", + "android_graphics_jni", + ], static_libs: [ "libgmock", @@ -549,6 +551,7 @@ cc_test { srcs: [ "tests/unit/main.cpp", + "tests/unit/ABitmapTests.cpp", "tests/unit/CacheManagerTests.cpp", "tests/unit/CanvasContextTests.cpp", "tests/unit/CommonPoolTests.cpp", diff --git a/libs/hwui/apex/android_bitmap.cpp b/libs/hwui/apex/android_bitmap.cpp index b56a619b00aa..3780ba072308 100644 --- a/libs/hwui/apex/android_bitmap.cpp +++ b/libs/hwui/apex/android_bitmap.cpp @@ -163,10 +163,9 @@ jobject ABitmapConfig_getConfigFromFormat(JNIEnv* env, AndroidBitmapFormat forma void ABitmap_notifyPixelsChanged(ABitmap* bitmapHandle) { Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle); - if (bitmap->isImmutable()) { - ALOGE("Attempting to modify an immutable Bitmap!"); + if (!bitmap->isImmutable()) { + bitmap->notifyPixelsChanged(); } - return bitmap->notifyPixelsChanged(); } namespace { diff --git a/libs/hwui/tests/unit/ABitmapTests.cpp b/libs/hwui/tests/unit/ABitmapTests.cpp new file mode 100644 index 000000000000..8e2f7e09d406 --- /dev/null +++ b/libs/hwui/tests/unit/ABitmapTests.cpp @@ -0,0 +1,46 @@ +/* + * Copyright 2020 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 + +#include "android/graphics/bitmap.h" +#include "apex/TypeCast.h" +#include "hwui/Bitmap.h" +#include "tests/common/TestUtils.h" + +using namespace android; +using namespace android::uirenderer; + +TEST(ABitmap, notifyPixelsChanged) { + // generate a bitmap and its public API handle + sk_sp bitmap(TestUtils::createBitmap(1, 1)); + ABitmap* abmp = android::TypeCast::toABitmap(bitmap.get()); + + // verify that notification changes the genID + uint32_t genID = bitmap->getGenerationID(); + ABitmap_notifyPixelsChanged(abmp); + ASSERT_TRUE(bitmap->getGenerationID() != genID); + + // mark the bitmap as immutable + ASSERT_FALSE(bitmap->isImmutable()); + bitmap->setImmutable(); + ASSERT_TRUE(bitmap->isImmutable()); + + // attempt to notify that the pixels have changed + genID = bitmap->getGenerationID(); + ABitmap_notifyPixelsChanged(abmp); + ASSERT_TRUE(bitmap->getGenerationID() == genID); +} -- cgit v1.2.3 From f97b29d2e3aefcb4bb31a7b7ead9f651a91e7ff4 Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Mon, 6 Apr 2020 12:01:01 -0400 Subject: Switch get_env_or_die to requireEnv Fixes: 153099745 Test: manual The heif decoder may need to continue reading the input from a JavaInputStreamAdaptor on the AnimatedImageThread. Attach the JVM like we do for ByteBufferStream and ByteArrayStream. Similarly, if the AnimatedImageThread is holding a reference to an AnimatedImageDrawable, it is possible to call its destructor, and ultimately InvokeListener's destructor or ByteBufferStreamAdaptor's release_proc on that thread without the JVM attached. Attach in that case, too. Change-Id: I84d236eed2fb5c8617533aed0cae4c762d1eb6dd --- libs/hwui/jni/AnimatedImageDrawable.cpp | 2 +- libs/hwui/jni/ByteBufferStreamAdaptor.cpp | 20 +------------------- libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp | 4 ++-- libs/hwui/jni/Utils.cpp | 10 ++++++++++ libs/hwui/jni/Utils.h | 10 ++++++++++ 5 files changed, 24 insertions(+), 22 deletions(-) (limited to 'libs') diff --git a/libs/hwui/jni/AnimatedImageDrawable.cpp b/libs/hwui/jni/AnimatedImageDrawable.cpp index 055075d0c42a..1ff156593c41 100644 --- a/libs/hwui/jni/AnimatedImageDrawable.cpp +++ b/libs/hwui/jni/AnimatedImageDrawable.cpp @@ -183,7 +183,7 @@ public: } ~InvokeListener() override { - auto* env = get_env_or_die(mJvm); + auto* env = requireEnv(mJvm); env->DeleteWeakGlobalRef(mWeakRef); } diff --git a/libs/hwui/jni/ByteBufferStreamAdaptor.cpp b/libs/hwui/jni/ByteBufferStreamAdaptor.cpp index db5f6f6c684f..b10540cb3fbd 100644 --- a/libs/hwui/jni/ByteBufferStreamAdaptor.cpp +++ b/libs/hwui/jni/ByteBufferStreamAdaptor.cpp @@ -9,24 +9,6 @@ using namespace android; static jmethodID gByteBuffer_getMethodID; static jmethodID gByteBuffer_setPositionMethodID; -/** - * Helper method for accessing the JNI interface pointer. - * - * Image decoding (which this supports) is started on a thread that is already - * attached to the Java VM. But an AnimatedImageDrawable continues decoding on - * the AnimatedImageThread, which is not attached. This will attach if - * necessary. - */ -static JNIEnv* requireEnv(JavaVM* jvm) { - JNIEnv* env; - if (jvm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { - if (jvm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) { - LOG_ALWAYS_FATAL("Failed to AttachCurrentThread!"); - } - } - return env; -} - class ByteBufferStream : public SkStreamAsset { private: ByteBufferStream(JavaVM* jvm, jobject jbyteBuffer, size_t initialPosition, size_t length, @@ -304,7 +286,7 @@ std::unique_ptr CreateByteBufferStreamAdaptor(JNIEnv* env, jobject jby auto* context = new release_proc_context{jvm, jbyteBuffer}; auto releaseProc = [](const void*, void* context) { auto* c = reinterpret_cast(context); - JNIEnv* env = get_env_or_die(c->jvm); + JNIEnv* env = requireEnv(c->jvm); env->DeleteGlobalRef(c->jbyteBuffer); delete c; }; diff --git a/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp index 39483b55992b..f1c6b29204b2 100644 --- a/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp +++ b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp @@ -49,13 +49,13 @@ public: } ~JavaInputStreamAdaptor() override { - auto* env = android::get_env_or_die(fJvm); + auto* env = android::requireEnv(fJvm); env->DeleteGlobalRef(fJavaInputStream); env->DeleteGlobalRef(fJavaByteArray); } size_t read(void* buffer, size_t size) override { - auto* env = android::get_env_or_die(fJvm); + auto* env = android::requireEnv(fJvm); if (!fSwallowExceptions && checkException(env)) { // Just in case the caller did not clear from a previous exception. return 0; diff --git a/libs/hwui/jni/Utils.cpp b/libs/hwui/jni/Utils.cpp index 17c194d04f84..34fd6687d52c 100644 --- a/libs/hwui/jni/Utils.cpp +++ b/libs/hwui/jni/Utils.cpp @@ -159,3 +159,13 @@ JNIEnv* android::get_env_or_die(JavaVM* jvm) { } return env; } + +JNIEnv* android::requireEnv(JavaVM* jvm) { + JNIEnv* env; + if (jvm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { + if (jvm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) { + LOG_ALWAYS_FATAL("Failed to AttachCurrentThread!"); + } + } + return env; +} diff --git a/libs/hwui/jni/Utils.h b/libs/hwui/jni/Utils.h index 89255177ba2e..f628cc3c85ed 100644 --- a/libs/hwui/jni/Utils.h +++ b/libs/hwui/jni/Utils.h @@ -78,6 +78,16 @@ bool isSeekable(int descriptor); JNIEnv* get_env_or_die(JavaVM* jvm); +/** + * Helper method for accessing the JNI interface pointer. + * + * Image decoding (which this supports) is started on a thread that is already + * attached to the Java VM. But an AnimatedImageDrawable continues decoding on + * the AnimatedImageThread, which is not attached. This will attach if + * necessary. + */ +JNIEnv* requireEnv(JavaVM* jvm); + }; // namespace android #endif // _ANDROID_GRAPHICS_UTILS_H_ -- cgit v1.2.3 From 5f9753d770549db9c65e4e13c1e297a7f5fd6791 Mon Sep 17 00:00:00 2001 From: Derek Sollenberger Date: Wed, 1 Apr 2020 15:59:02 -0400 Subject: Don't reset the capture mode after each frame when using the CallbackAPI When multiframe skp support was introduced it set the capture mode to none for everything but the multiframe use case. This was incorrect in the case of the CallbackAPI where we want to continue to capture frames. Bug: 152084866 Test: hwui_unit_tests Change-Id: I7e3caf41a3725d03252df1ab7bedfe1b69a238c2 --- libs/hwui/pipeline/skia/SkiaPipeline.cpp | 2 +- libs/hwui/tests/unit/SkiaPipelineTests.cpp | 37 ++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 41aa1ff80e3c..5088494d6a07 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -418,9 +418,9 @@ void SkiaPipeline::endCapture(SkSurface* surface) { auto data = picture->serialize(); savePictureAsync(data, mCapturedFile); mCaptureSequence = 0; + mCaptureMode = CaptureMode::None; } } - mCaptureMode = CaptureMode::None; mRecorder.reset(); } } diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp index 90bcd1c0e370..1208062d9da0 100644 --- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp +++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp @@ -403,3 +403,40 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, context_lost) { renderThread.destroyRenderingContext(); EXPECT_FALSE(pipeline->isSurfaceReady()); } + +RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, pictureCallback) { + // create a pipeline and add a picture callback + auto pipeline = std::make_unique(renderThread); + int callbackCount = 0; + pipeline->setPictureCapturedCallback( + [&callbackCount](sk_sp&& picture) { callbackCount += 1; }); + + // create basic red frame and render it + auto redNode = TestUtils::createSkiaNode( + 0, 0, 1, 1, [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) { + redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver); + }); + LayerUpdateQueue layerUpdateQueue; + SkRect dirty = SkRectMakeLargest(); + std::vector> renderNodes; + renderNodes.push_back(redNode); + bool opaque = true; + android::uirenderer::Rect contentDrawBounds(0, 0, 1, 1); + auto surface = SkSurface::MakeRasterN32Premul(1, 1); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface, + SkMatrix::I()); + + // verify the callback was called + EXPECT_EQ(1, callbackCount); + + // render a second frame and check the callback count + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface, + SkMatrix::I()); + EXPECT_EQ(2, callbackCount); + + // unset the callback, render another frame, check callback was not invoked + pipeline->setPictureCapturedCallback(nullptr); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface, + SkMatrix::I()); + EXPECT_EQ(2, callbackCount); +} -- cgit v1.2.3 From a90930528d4be87b3132bf758afeb6aa7a7524b9 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Thu, 26 Mar 2020 17:15:01 -0700 Subject: Invalidate idmap when target updates When the target package update, check if the idmap file must change. If so, propagate the idmap changes to the targets overlay paths, and invalidate cached overlay ApkAssets in ResourcesManager. Bug: 147794117 Bug: 150877400 Test: OverlayRemountedTest Test: libandroidfw_tests Change-Id: I6115c30bae3672b188a5ff270720a0eea15b43b5 --- libs/androidfw/ApkAssets.cpp | 5 +++-- libs/androidfw/Idmap.cpp | 20 +++++++++++++++----- libs/androidfw/include/androidfw/Idmap.h | 18 ++++++++++++++++-- libs/androidfw/tests/Idmap_test.cpp | 30 +++++++++++++++++++++++++++--- 4 files changed, 61 insertions(+), 12 deletions(-) (limited to 'libs') diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index 918e7af12d31..05f4d6b63a4c 100644 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -385,7 +385,7 @@ std::unique_ptr ApkAssets::LoadOverlay(const std::string& idmap const StringPiece idmap_data( reinterpret_cast(idmap_asset->getBuffer(true /*wordAligned*/)), static_cast(idmap_asset->getLength())); - std::unique_ptr loaded_idmap = LoadedIdmap::Load(idmap_data); + std::unique_ptr loaded_idmap = LoadedIdmap::Load(idmap_path, idmap_data); if (loaded_idmap == nullptr) { LOG(ERROR) << "failed to load IDMAP " << idmap_path; return {}; @@ -538,8 +538,9 @@ bool ApkAssets::IsUpToDate() const { // Loaders are invalidated by the app, not the system, so assume they are up to date. return true; } + return (!loaded_idmap_ || loaded_idmap_->IsUpToDate()) && + last_mod_time_ == getFileModDate(path_.c_str()); - return last_mod_time_ == getFileModDate(path_.c_str()); } } // namespace android diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp index 0b2fd9ec982d..eb6ee9525bb9 100644 --- a/libs/androidfw/Idmap.cpp +++ b/libs/androidfw/Idmap.cpp @@ -20,6 +20,7 @@ #include "android-base/logging.h" #include "android-base/stringprintf.h" +#include "androidfw/misc.h" #include "androidfw/ResourceTypes.h" #include "androidfw/Util.h" #include "utils/ByteOrder.h" @@ -192,7 +193,9 @@ static bool IsValidIdmapHeader(const StringPiece& data) { return true; } -LoadedIdmap::LoadedIdmap(const Idmap_header* header, +LoadedIdmap::LoadedIdmap(std::string&& idmap_path, + const time_t last_mod_time, + const Idmap_header* header, const Idmap_data_header* data_header, const Idmap_target_entry* target_entries, const Idmap_overlay_entry* overlay_entries, @@ -201,7 +204,9 @@ LoadedIdmap::LoadedIdmap(const Idmap_header* header, data_header_(data_header), target_entries_(target_entries), overlay_entries_(overlay_entries), - string_pool_(string_pool) { + string_pool_(string_pool), + idmap_path_(std::move(idmap_path)), + idmap_last_mod_time_(last_mod_time) { size_t length = strnlen(reinterpret_cast(header_->overlay_path), arraysize(header_->overlay_path)); @@ -212,7 +217,8 @@ LoadedIdmap::LoadedIdmap(const Idmap_header* header, target_apk_path_.assign(reinterpret_cast(header_->target_path), length); } -std::unique_ptr LoadedIdmap::Load(const StringPiece& idmap_data) { +std::unique_ptr LoadedIdmap::Load(const StringPiece& idmap_path, + const StringPiece& idmap_data) { ATRACE_CALL(); if (!IsValidIdmapHeader(idmap_data)) { return {}; @@ -275,10 +281,14 @@ std::unique_ptr LoadedIdmap::Load(const StringPiece& idmap_da // Can't use make_unique because LoadedIdmap constructor is private. std::unique_ptr loaded_idmap = std::unique_ptr( - new LoadedIdmap(header, data_header, target_entries, overlay_entries, - idmap_string_pool.release())); + new LoadedIdmap(idmap_path.to_string(), getFileModDate(idmap_path.data()), header, + data_header, target_entries, overlay_entries, idmap_string_pool.release())); return std::move(loaded_idmap); } +bool LoadedIdmap::IsUpToDate() const { + return idmap_last_mod_time_ == getFileModDate(idmap_path_.c_str()); +} + } // namespace android diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h index ccb57f373473..ecc1ce65d124 100644 --- a/libs/androidfw/include/androidfw/Idmap.h +++ b/libs/androidfw/include/androidfw/Idmap.h @@ -142,7 +142,13 @@ class IdmapResMap { class LoadedIdmap { public: // Loads an IDMAP from a chunk of memory. Returns nullptr if the IDMAP data was malformed. - static std::unique_ptr Load(const StringPiece& idmap_data); + static std::unique_ptr Load(const StringPiece& idmap_path, + const StringPiece& idmap_data); + + // Returns the path to the IDMAP. + inline const std::string& IdmapPath() const { + return idmap_path_; + } // Returns the path to the RRO (Runtime Resource Overlay) APK for which this IDMAP was generated. inline const std::string& OverlayApkPath() const { @@ -167,6 +173,10 @@ class LoadedIdmap { return OverlayDynamicRefTable(data_header_, overlay_entries_, target_assigned_package_id); } + // Returns whether the idmap file on disk has not been modified since the construction of this + // LoadedIdmap. + bool IsUpToDate() const; + protected: // Exposed as protected so that tests can subclass and mock this class out. LoadedIdmap() = default; @@ -177,13 +187,17 @@ class LoadedIdmap { const Idmap_overlay_entry* overlay_entries_; const std::unique_ptr string_pool_; + const std::string idmap_path_; std::string overlay_apk_path_; std::string target_apk_path_; + const time_t idmap_last_mod_time_; private: DISALLOW_COPY_AND_ASSIGN(LoadedIdmap); - explicit LoadedIdmap(const Idmap_header* header, + explicit LoadedIdmap(std::string&& idmap_path, + time_t last_mod_time, + const Idmap_header* header, const Idmap_data_header* data_header, const Idmap_target_entry* target_entries, const Idmap_overlay_entry* overlay_entries, diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp index 41ba637da5d7..7aa0dbbafab3 100644 --- a/libs/androidfw/tests/Idmap_test.cpp +++ b/libs/androidfw/tests/Idmap_test.cpp @@ -38,7 +38,7 @@ class IdmapTest : public ::testing::Test { protected: void SetUp() override { // Move to the test data directory so the idmap can locate the overlay APK. - std::string original_path = base::GetExecutableDirectory(); + original_path = base::GetExecutableDirectory(); chdir(GetTestDataPath().c_str()); system_assets_ = ApkAssets::Load("system/system.apk"); @@ -49,10 +49,14 @@ class IdmapTest : public ::testing::Test { overlayable_assets_ = ApkAssets::Load("overlayable/overlayable.apk"); ASSERT_NE(nullptr, overlayable_assets_); + } + + void TearDown() override { chdir(original_path.c_str()); } protected: + std::string original_path; std::unique_ptr system_assets_; std::unique_ptr overlay_assets_; std::unique_ptr overlayable_assets_; @@ -221,8 +225,7 @@ TEST_F(IdmapTest, OverlaidResourceHasSameName) { TEST_F(IdmapTest, OverlayLoaderInterop) { std::string contents; - auto loader_assets = ApkAssets::LoadTable(GetTestDataPath() + "/loader/resources.arsc", - PROPERTY_LOADER); + auto loader_assets = ApkAssets::LoadTable("loader/resources.arsc", PROPERTY_LOADER); AssetManager2 asset_manager; asset_manager.SetApkAssets({overlayable_assets_.get(), loader_assets.get(), @@ -241,4 +244,25 @@ TEST_F(IdmapTest, OverlayLoaderInterop) { ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "loader"); } +TEST_F(IdmapTest, OverlayAssetsIsUpToDate) { + std::string idmap_contents; + ASSERT_TRUE(base::ReadFileToString("overlay/overlay.idmap", &idmap_contents)); + + TemporaryFile temp_file; + ASSERT_TRUE(base::WriteStringToFile(idmap_contents, temp_file.path)); + + auto apk_assets = ApkAssets::LoadOverlay(temp_file.path); + ASSERT_NE(nullptr, apk_assets); + ASSERT_TRUE(apk_assets->IsUpToDate()); + + unlink(temp_file.path); + ASSERT_FALSE(apk_assets->IsUpToDate()); + sleep(2); + + base::WriteStringToFile("hello", temp_file.path); + sleep(2); + + ASSERT_FALSE(apk_assets->IsUpToDate()); +} + } // namespace -- cgit v1.2.3 From 192400cf334e0e8befe336861a6d214c6a88c993 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Thu, 2 Apr 2020 09:54:23 -0700 Subject: Fail install when resources.arsc is compressed If an application targets R+, prevent the application from being installed if the app has a compressed resources.arsc or if the resources.arsc is not aligned on a 4-byte boundary. Resources tables that cannot be memory mapped have to be read into a buffer in RAM and exert unnecessary memory pressure on the system. Bug: 132742131 Test: manual (adding CTS tests) Change-Id: Ieef764c87643863de24531fac12cc520fe6d90d0 --- libs/androidfw/AssetManager2.cpp | 5 +++++ libs/androidfw/include/androidfw/ApkAssets.h | 17 +++++++++++------ libs/androidfw/include/androidfw/AssetManager2.h | 3 +++ 3 files changed, 19 insertions(+), 6 deletions(-) (limited to 'libs') diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index eaf452b5fa71..b9765ea7212c 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -331,6 +331,11 @@ bool AssetManager2::GetOverlayablesToString(const android::StringPiece& package_ return true; } +bool AssetManager2::ContainsAllocatedTable() const { + return std::find_if(apk_assets_.begin(), apk_assets_.end(), + std::mem_fn(&ApkAssets::IsTableAllocated)) != apk_assets_.end(); +} + void AssetManager2::SetConfiguration(const ResTable_config& configuration) { const int diff = configuration_.diff(configuration); configuration_ = configuration; diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h index 879b050b65bd..e57490aab2d8 100644 --- a/libs/androidfw/include/androidfw/ApkAssets.h +++ b/libs/androidfw/include/androidfw/ApkAssets.h @@ -119,31 +119,36 @@ class ApkAssets { package_property_t flags = 0U, std::unique_ptr override_asset = nullptr); - inline const std::string& GetPath() const { + const std::string& GetPath() const { return path_; } - inline const AssetsProvider* GetAssetsProvider() const { + const AssetsProvider* GetAssetsProvider() const { return assets_provider_.get(); } // This is never nullptr. - inline const LoadedArsc* GetLoadedArsc() const { + const LoadedArsc* GetLoadedArsc() const { return loaded_arsc_.get(); } - inline const LoadedIdmap* GetLoadedIdmap() const { + const LoadedIdmap* GetLoadedIdmap() const { return loaded_idmap_.get(); } - inline bool IsLoader() const { + bool IsLoader() const { return (property_flags_ & PROPERTY_LOADER) != 0; } - inline bool IsOverlay() const { + bool IsOverlay() const { return loaded_idmap_ != nullptr; } + // Returns whether the resources.arsc is allocated in RAM (not mmapped). + bool IsTableAllocated() const { + return resources_asset_ && resources_asset_->isAllocated(); + } + bool IsUpToDate() const; // Creates an Asset from a file on disk. diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index e21abade99a4..30ef25c6a516 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -134,6 +134,9 @@ class AssetManager2 { const std::unordered_map* GetOverlayableMapForPackage(uint32_t package_id) const; + // Returns whether the resources.arsc of any loaded apk assets is allocated in RAM (not mmapped). + bool ContainsAllocatedTable() const; + // Sets/resets the configuration for this AssetManager. This will cause all // caches that are related to the configuration change to be invalidated. void SetConfiguration(const ResTable_config& configuration); -- cgit v1.2.3 From 9054ca2b342b2ea902839f629e820546d8a2458b Mon Sep 17 00:00:00 2001 From: John Reck Date: Fri, 10 Apr 2020 14:36:02 -0700 Subject: Restore preserving mutability over parceling Bug: 150836900 Test: CTS test BitmapTest#testWriteToParcelPreserveMutability Change-Id: I2caba900a3259e2fb7b4e83d7241cebd7441fa06 Merged-In: I133047acfccae7af81199098caaf20ea8d641a89 --- libs/hwui/jni/Bitmap.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'libs') diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp index ba669053ed63..c0663a9bc699 100755 --- a/libs/hwui/jni/Bitmap.cpp +++ b/libs/hwui/jni/Bitmap.cpp @@ -601,6 +601,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { android::Parcel* p = parcelForJavaObject(env, parcel); + const bool isMutable = p->readInt32() != 0; const SkColorType colorType = (SkColorType)p->readInt32(); const SkAlphaType alphaType = (SkAlphaType)p->readInt32(); const uint32_t colorSpaceSize = p->readUint32(); @@ -649,7 +650,9 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { // Map the bitmap in place from the ashmem region if possible otherwise copy. sk_sp nativeBitmap; - if (blob.fd() >= 0 && !blob.isMutable()) { + // If the blob is mutable we have ownership of the region and can always use it + // If the blob is immutable _and_ we're immutable, we can then still use it + if (blob.fd() >= 0 && (blob.isMutable() || !isMutable)) { #if DEBUG_PARCEL ALOGD("Bitmap.createFromParcel: mapped contents of bitmap from %s blob " "(fds %s)", @@ -669,7 +672,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { // Map the pixels in place and take ownership of the ashmem region. We must also respect the // rowBytes value already set on the bitmap instead of attempting to compute our own. nativeBitmap = Bitmap::createFrom(bitmap->info(), bitmap->rowBytes(), dupFd, - const_cast(blob.data()), size, true); + const_cast(blob.data()), size, !isMutable); if (!nativeBitmap) { close(dupFd); blob.release(); @@ -707,7 +710,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { } return createBitmap(env, nativeBitmap.release(), - getPremulBitmapCreateFlags(false), NULL, NULL, density); + getPremulBitmapCreateFlags(isMutable), NULL, NULL, density); #else doThrowRE(env, "Cannot use parcels outside of Android"); return NULL; @@ -728,6 +731,7 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, auto bitmapWrapper = reinterpret_cast(bitmapHandle); bitmapWrapper->getSkBitmap(&bitmap); + p->writeInt32(!bitmap.isImmutable()); p->writeInt32(bitmap.colorType()); p->writeInt32(bitmap.alphaType()); SkColorSpace* colorSpace = bitmap.colorSpace(); @@ -754,7 +758,7 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, // Transfer the underlying ashmem region if we have one and it's immutable. android::status_t status; int fd = bitmapWrapper->bitmap().getAshmemFd(); - if (fd >= 0 && p->allowFds()) { + if (fd >= 0 && bitmap.isImmutable() && p->allowFds()) { #if DEBUG_PARCEL ALOGD("Bitmap.writeToParcel: transferring immutable bitmap's ashmem fd as " "immutable blob (fds %s)", @@ -775,9 +779,10 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, p->allowFds() ? "allowed" : "forbidden"); #endif + const bool mutableCopy = !bitmap.isImmutable(); size_t size = bitmap.computeByteSize(); android::Parcel::WritableBlob blob; - status = p->writeBlob(size, false, &blob); + status = p->writeBlob(size, mutableCopy, &blob); if (status) { doThrowRE(env, "Could not copy bitmap to parcel blob."); return JNI_FALSE; -- cgit v1.2.3 From 24fca180a146ec6e94a5058280c593216edbae15 Mon Sep 17 00:00:00 2001 From: Haibo Huang Date: Tue, 28 Apr 2020 14:47:47 -0700 Subject: Fix the use of pdfium FPDF_LoadPage returns FPDF_PAGE and that's also what expected by other APIs. There's no need for conversion. This works now because FPDF_PAGE is defined as void*. But will fails with new pdfium. Bug: 155031873 Change-Id: I74381ec8ec36797a5901ea3de845c2b5d798ba0c --- libs/hwui/jni/pdf/PdfEditor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/hwui/jni/pdf/PdfEditor.cpp b/libs/hwui/jni/pdf/PdfEditor.cpp index 828d6e3992b6..e65921ac8e0a 100644 --- a/libs/hwui/jni/pdf/PdfEditor.cpp +++ b/libs/hwui/jni/pdf/PdfEditor.cpp @@ -110,7 +110,7 @@ static void nativeSetTransformAndClip(JNIEnv* env, jclass thiz, jlong documentPt jlong transformPtr, jint clipLeft, jint clipTop, jint clipRight, jint clipBottom) { FPDF_DOCUMENT document = reinterpret_cast(documentPtr); - FPDF_PAGE* page = (FPDF_PAGE*) FPDF_LoadPage(document, pageIndex); + FPDF_PAGE page = FPDF_LoadPage(document, pageIndex); if (!page) { jniThrowException(env, "java/lang/IllegalStateException", "cannot open page"); -- cgit v1.2.3 From 5fc80e7b32a00b4e43b4ce13d60bd82796130182 Mon Sep 17 00:00:00 2001 From: chaviw Date: Thu, 30 Apr 2020 12:14:35 -0700 Subject: Send drawFinish callback even if did not draw There are cases where ViewRootImpl requests to draw, but there was nothing new to draw. In that case, the callback will never be invoked and ViewRootImpl will wait forever. This change will invoke the callback even if there is nothing to draw. It will use the last frameNumber since nothing new has drawn Test: Request draw with nothing new. Callback is invoked Fixes: 155429223 Change-Id: I7c9ed7fd63a451b17133a11ffbcf8fb64be558e5 --- libs/hwui/renderthread/CanvasContext.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'libs') diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index c19b1878ad45..335bcdcfc1fb 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -440,6 +440,12 @@ void CanvasContext::draw() { if (dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw()) { mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); + // Notify the callbacks, even if there's nothing to draw so they aren't waiting + // indefinitely + for (auto& func : mFrameCompleteCallbacks) { + std::invoke(func, mFrameNumber); + } + mFrameCompleteCallbacks.clear(); return; } -- cgit v1.2.3 From 950f2aa8258e35a8d65e538c3c1da39005f3dd0e Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Wed, 29 Apr 2020 13:46:00 -0400 Subject: Fix setShadowLayer + drawTextOnPath Bug: 140255441 Test: I49c468b0f4cc142e5d6b03f0aee917b3d85153f3 Fix two issues that prevented this combination from working: - make() the SkTextBlob once and reuse it for each iteration of the loop. This is necessary because make() resets the SkTextBlobBuilder, so a second call does nothing. - use the altered SkPaint, passed as a parameter to the lambda, rather than the Paint passed to apply_looper. Incidentally, reference mCanvas directly like the other methods, rather than calling asSkCanvas(), which returns mCanvas. Change-Id: I48ea1232b12df4f5f8afffc20c7dc8e5dc2bb511 --- libs/hwui/SkiaCanvas.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'libs') diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 5790150a3425..941437998838 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -789,9 +789,11 @@ void SkiaCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, xform[i - start].fTx = pos.x() - tan.y() * y - halfWidth * tan.x(); xform[i - start].fTy = pos.y() + tan.x() * y - halfWidth * tan.y(); } - auto* finalCanvas = this->asSkCanvas(); + + sk_sp textBlob(builder.make()); + apply_looper(&paintCopy, [&](const SkPaint& p) { - finalCanvas->drawTextBlob(builder.make(), 0, 0, paintCopy); + mCanvas->drawTextBlob(textBlob, 0, 0, p); }); } -- cgit v1.2.3 From b624af3dca0f80a8b6e924920d6c6054bbffdd31 Mon Sep 17 00:00:00 2001 From: Jagadeesh Pakaravoor Date: Fri, 1 May 2020 00:01:40 +0000 Subject: Revert^2 "hwui: remove FatVector" Revert submission 10449863-revert-fatvector Reason for revert: b/149254345 Original change (of introducing FatVector) was reverted as a stop-gap solution to fix the aforementioned bug. The bug was caused by an ABI lock between Surface's definition (that changed with Region) and lib-imsvt prebuilt. Enabling this change now as we have re-compiled the prebuilt with the change enabled. Doing that via a revert of the revert. Reverted Changes: I8ac66acb8:Revert "hwui: remove FatVector" Ib60dbf3ef:Revert "libui: rewrite Region with FatVector" Original changes: I09dc2fddd:hwui: remove FatVector I265c6c831:libui: rewrite Region with FatVector bug: 149254345 Change-Id: I9fb5e8908434abb9a763992e922445a2dc37e865 --- libs/hwui/RenderNode.cpp | 2 +- libs/hwui/RenderNode.h | 3 +- libs/hwui/jni/FontFamily.cpp | 4 +- libs/hwui/jni/fonts/Font.cpp | 4 +- libs/hwui/pipeline/skia/ReorderBarrierDrawables.h | 2 +- libs/hwui/renderthread/RenderThread.cpp | 3 +- libs/hwui/renderthread/VulkanManager.cpp | 2 +- libs/hwui/tests/unit/FatVectorTests.cpp | 2 +- libs/hwui/utils/FatVector.h | 96 ----------------------- 9 files changed, 12 insertions(+), 106 deletions(-) delete mode 100644 libs/hwui/utils/FatVector.h (limited to 'libs') diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index 6761435a8171..31e45558139d 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -27,7 +27,6 @@ #include "DamageAccumulator.h" #include "pipeline/skia/SkiaDisplayList.h" #endif -#include "utils/FatVector.h" #include "utils/MathUtils.h" #include "utils/StringUtils.h" #include "utils/TraceUtils.h" @@ -37,6 +36,7 @@ #include #include #include +#include namespace android { namespace uirenderer { diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index d55e5b0ce836..c0ec2174bb35 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -27,6 +27,8 @@ #include +#include + #include "AnimatorManager.h" #include "CanvasTransform.h" #include "Debug.h" @@ -35,7 +37,6 @@ #include "RenderProperties.h" #include "pipeline/skia/SkiaDisplayList.h" #include "pipeline/skia/SkiaLayer.h" -#include "utils/FatVector.h" #include diff --git a/libs/hwui/jni/FontFamily.cpp b/libs/hwui/jni/FontFamily.cpp index 0ce04a2437b9..a2fef1e19328 100644 --- a/libs/hwui/jni/FontFamily.cpp +++ b/libs/hwui/jni/FontFamily.cpp @@ -29,9 +29,9 @@ #include #include -#include #include #include +#include #include @@ -104,7 +104,7 @@ static jlong FontFamily_getFamilyReleaseFunc(CRITICAL_JNI_PARAMS) { static bool addSkTypeface(NativeFamilyBuilder* builder, sk_sp&& data, int ttcIndex, jint weight, jint italic) { - uirenderer::FatVector skiaAxes; + FatVector skiaAxes; for (const auto& axis : builder->axes) { skiaAxes.emplace_back(SkFontArguments::Axis{axis.axisTag, axis.value}); } diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp index 7e8f8d8d173c..5714cd1d0390 100644 --- a/libs/hwui/jni/fonts/Font.cpp +++ b/libs/hwui/jni/fonts/Font.cpp @@ -28,8 +28,8 @@ #include #include -#include #include +#include #include @@ -93,7 +93,7 @@ static jlong Font_Builder_build(JNIEnv* env, jobject clazz, jlong builderPtr, jo sk_sp data(SkData::MakeWithProc(fontPtr, fontSize, release_global_ref, reinterpret_cast(fontRef))); - uirenderer::FatVector skiaAxes; + FatVector skiaAxes; for (const auto& axis : builder->axes) { skiaAxes.emplace_back(SkFontArguments::Axis{axis.axisTag, axis.value}); } diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h index cfc0f9b258da..d669f84c5ee2 100644 --- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h +++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h @@ -21,7 +21,7 @@ #include #include -#include +#include namespace android { namespace uirenderer { diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index cae3e3b5188c..206b58f62ea7 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -27,7 +27,6 @@ #include "pipeline/skia/SkiaOpenGLPipeline.h" #include "pipeline/skia/SkiaVulkanPipeline.h" #include "renderstate/RenderState.h" -#include "utils/FatVector.h" #include "utils/TimeUtils.h" #include "utils/TraceUtils.h" @@ -40,6 +39,8 @@ #include #include +#include + namespace android { namespace uirenderer { namespace renderthread { diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index a5355fc3499d..ba70afc8b8d2 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -23,13 +23,13 @@ #include #include #include +#include #include #include #include "Properties.h" #include "RenderThread.h" #include "renderstate/RenderState.h" -#include "utils/FatVector.h" #include "utils/TraceUtils.h" namespace android { diff --git a/libs/hwui/tests/unit/FatVectorTests.cpp b/libs/hwui/tests/unit/FatVectorTests.cpp index 8523e6c9e973..6585a6249b44 100644 --- a/libs/hwui/tests/unit/FatVectorTests.cpp +++ b/libs/hwui/tests/unit/FatVectorTests.cpp @@ -15,7 +15,7 @@ */ #include -#include +#include #include diff --git a/libs/hwui/utils/FatVector.h b/libs/hwui/utils/FatVector.h deleted file mode 100644 index 49f1984b779f..000000000000 --- a/libs/hwui/utils/FatVector.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 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_FAT_VECTOR_H -#define ANDROID_FAT_VECTOR_H - -#include "utils/Macros.h" - -#include -#include -#include -#include - -#include - -namespace android { -namespace uirenderer { - -template -class InlineStdAllocator { -public: - struct Allocation { - PREVENT_COPY_AND_ASSIGN(Allocation); - - public: - Allocation(){}; - // char array instead of T array, so memory is uninitialized, with no destructors run - char array[sizeof(T) * SIZE]; - bool inUse = false; - }; - - typedef T value_type; // needed to implement std::allocator - typedef T* pointer; // needed to implement std::allocator - - explicit InlineStdAllocator(Allocation& allocation) : mAllocation(allocation) {} - InlineStdAllocator(const InlineStdAllocator& other) : mAllocation(other.mAllocation) {} - ~InlineStdAllocator() {} - - T* allocate(size_t num, const void* = 0) { - if (!mAllocation.inUse && num <= SIZE) { - mAllocation.inUse = true; - return (T*)mAllocation.array; - } else { - return (T*)malloc(num * sizeof(T)); - } - } - - void deallocate(pointer p, size_t num) { - if (p == (T*)mAllocation.array) { - mAllocation.inUse = false; - } else { - // 'free' instead of delete here - destruction handled separately - free(p); - } - } - Allocation& mAllocation; -}; - -/** - * std::vector with SIZE elements preallocated into an internal buffer. - * - * Useful for avoiding the cost of malloc in cases where only SIZE or - * fewer elements are needed in the common case. - */ -template -class FatVector : public std::vector> { -public: - FatVector() - : std::vector>( - InlineStdAllocator(mAllocation)) { - this->reserve(SIZE); - } - - explicit FatVector(size_t capacity) : FatVector() { this->resize(capacity); } - -private: - typename InlineStdAllocator::Allocation mAllocation; -}; - -} // namespace uirenderer -} // namespace android - -#endif // ANDROID_FAT_VECTOR_H -- cgit v1.2.3 From a2588f766f59029cd26faa96a135e8b43a62cca8 Mon Sep 17 00:00:00 2001 From: Haibo Huang Date: Tue, 28 Apr 2020 14:47:47 -0700 Subject: Fix the use of pdfium FPDF_LoadPage returns FPDF_PAGE and that's also what expected by other APIs. There's no need for conversion. This works now because FPDF_PAGE is defined as void*. But will fails with new pdfium. Bug: 155031873 Change-Id: I74381ec8ec36797a5901ea3de845c2b5d798ba0c (cherry picked from commit 24fca180a146ec6e94a5058280c593216edbae15) --- libs/hwui/jni/pdf/PdfEditor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/hwui/jni/pdf/PdfEditor.cpp b/libs/hwui/jni/pdf/PdfEditor.cpp index 828d6e3992b6..e65921ac8e0a 100644 --- a/libs/hwui/jni/pdf/PdfEditor.cpp +++ b/libs/hwui/jni/pdf/PdfEditor.cpp @@ -110,7 +110,7 @@ static void nativeSetTransformAndClip(JNIEnv* env, jclass thiz, jlong documentPt jlong transformPtr, jint clipLeft, jint clipTop, jint clipRight, jint clipBottom) { FPDF_DOCUMENT document = reinterpret_cast(documentPtr); - FPDF_PAGE* page = (FPDF_PAGE*) FPDF_LoadPage(document, pageIndex); + FPDF_PAGE page = FPDF_LoadPage(document, pageIndex); if (!page) { jniThrowException(env, "java/lang/IllegalStateException", "cannot open page"); -- cgit v1.2.3 From 8ddbc59d55881b1595f48facd6f91a4bd2d2453a Mon Sep 17 00:00:00 2001 From: John Reck Date: Thu, 7 May 2020 16:11:18 -0700 Subject: Avoid excessive KGSL maps Hook MIN_UNDEQUEUED_BUFFERS if possible to avoid thrashing kgsl maps when render_ahead is being used Bug: 143555869 Test: verified kgsl maps only happened once per buffer Change-Id: I985fae0a9a7635be3a1cf6177186e5541a1169df --- libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp | 17 +----------- libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h | 3 +-- libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp | 5 ++-- libs/hwui/pipeline/skia/SkiaVulkanPipeline.h | 3 +-- libs/hwui/renderthread/CanvasContext.cpp | 37 +++++++++++++++++++------- libs/hwui/renderthread/IRenderPipeline.h | 3 +-- libs/hwui/renderthread/ReliableSurface.cpp | 26 ++++++++++++++++++ libs/hwui/renderthread/ReliableSurface.h | 16 +++++++++++ libs/hwui/tests/unit/SkiaPipelineTests.cpp | 2 +- 9 files changed, 76 insertions(+), 36 deletions(-) (limited to 'libs') diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index 8d5acc631274..24a6228242a5 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -157,21 +157,7 @@ void SkiaOpenGLPipeline::onStop() { } } -static void setBufferCount(ANativeWindow* window, uint32_t extraBuffers) { - int query_value; - int err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value); - if (err != 0 || query_value < 0) { - ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, query_value); - return; - } - auto min_undequeued_buffers = static_cast(query_value); - - int bufferCount = min_undequeued_buffers + 2 + extraBuffers; - native_window_set_buffer_count(window, bufferCount); -} - -bool SkiaOpenGLPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior, - uint32_t extraBuffers) { +bool SkiaOpenGLPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior) { if (mEglSurface != EGL_NO_SURFACE) { mEglManager.destroySurface(mEglSurface); mEglSurface = EGL_NO_SURFACE; @@ -189,7 +175,6 @@ bool SkiaOpenGLPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBeh if (mEglSurface != EGL_NO_SURFACE) { const bool preserveBuffer = (swapBehavior != SwapBehavior::kSwap_discardBuffer); mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer); - setBufferCount(surface, extraBuffers); return true; } diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h index fc6e1142b4f2..fddd97f1c5b3 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h @@ -45,8 +45,7 @@ public: bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) override; DeferredLayerUpdater* createTextureLayer() override; - bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior, - uint32_t extraBuffers) override; + bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior) override; void onStop() override; bool isSurfaceReady() override; bool isContextReady() override; diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index 535a19956e03..212a4284a824 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -116,8 +116,7 @@ DeferredLayerUpdater* SkiaVulkanPipeline::createTextureLayer() { void SkiaVulkanPipeline::onStop() {} -bool SkiaVulkanPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior, - uint32_t extraBuffers) { +bool SkiaVulkanPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior) { if (mVkSurface) { mVkManager.destroySurface(mVkSurface); mVkSurface = nullptr; @@ -127,7 +126,7 @@ bool SkiaVulkanPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBeh mRenderThread.requireVkContext(); mVkSurface = mVkManager.createSurface(surface, mColorMode, mSurfaceColorSpace, mSurfaceColorType, - mRenderThread.getGrContext(), extraBuffers); + mRenderThread.getGrContext(), 0); } return mVkSurface != nullptr; diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h index c8bf233d8e1c..6268daa6213f 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h @@ -42,8 +42,7 @@ public: bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) override; DeferredLayerUpdater* createTextureLayer() override; - bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior, - uint32_t extraBuffers) override; + bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior) override; void onStop() override; bool isSurfaceReady() override; bool isContextReady() override; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 335bcdcfc1fb..a362bd220936 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -139,9 +139,30 @@ void CanvasContext::destroy() { mAnimationContext->destroy(); } +static void setBufferCount(ANativeWindow* window, uint32_t extraBuffers) { + int query_value; + int err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value); + if (err != 0 || query_value < 0) { + ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, query_value); + return; + } + auto min_undequeued_buffers = static_cast(query_value); + + int bufferCount = min_undequeued_buffers + 2 + extraBuffers; + native_window_set_buffer_count(window, bufferCount); +} + void CanvasContext::setSurface(ANativeWindow* window, bool enableTimeout) { ATRACE_CALL(); + if (mRenderAheadDepth == 0 && DeviceInfo::get()->getMaxRefreshRate() > 66.6f) { + mFixedRenderAhead = false; + mRenderAheadCapacity = 1; + } else { + mFixedRenderAhead = true; + mRenderAheadCapacity = mRenderAheadDepth; + } + if (window) { mNativeSurface = std::make_unique(window); mNativeSurface->init(); @@ -149,21 +170,17 @@ void CanvasContext::setSurface(ANativeWindow* window, bool enableTimeout) { // TODO: Fix error handling & re-shorten timeout ANativeWindow_setDequeueTimeout(window, 4000_ms); } + mNativeSurface->setExtraBufferCount(mRenderAheadCapacity); } else { mNativeSurface = nullptr; } - if (mRenderAheadDepth == 0 && DeviceInfo::get()->getMaxRefreshRate() > 66.6f) { - mFixedRenderAhead = false; - mRenderAheadCapacity = 1; - } else { - mFixedRenderAhead = true; - mRenderAheadCapacity = mRenderAheadDepth; - } - bool hasSurface = mRenderPipeline->setSurface( - mNativeSurface ? mNativeSurface->getNativeWindow() : nullptr, mSwapBehavior, - mRenderAheadCapacity); + mNativeSurface ? mNativeSurface->getNativeWindow() : nullptr, mSwapBehavior); + + if (mNativeSurface && !mNativeSurface->didSetExtraBuffers()) { + setBufferCount(mNativeSurface->getNativeWindow(), mRenderAheadCapacity); + } mFrameNumber = -1; diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h index ba0d64c5492d..c3c22869a42f 100644 --- a/libs/hwui/renderthread/IRenderPipeline.h +++ b/libs/hwui/renderthread/IRenderPipeline.h @@ -66,8 +66,7 @@ public: virtual bool swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) = 0; virtual DeferredLayerUpdater* createTextureLayer() = 0; - virtual bool setSurface(ANativeWindow* window, SwapBehavior swapBehavior, - uint32_t extraBuffers) = 0; + virtual bool setSurface(ANativeWindow* window, SwapBehavior swapBehavior) = 0; virtual void onStop() = 0; virtual bool isSurfaceReady() = 0; virtual bool isContextReady() = 0; diff --git a/libs/hwui/renderthread/ReliableSurface.cpp b/libs/hwui/renderthread/ReliableSurface.cpp index 8a0b4e8455bd..dcf1fc189588 100644 --- a/libs/hwui/renderthread/ReliableSurface.cpp +++ b/libs/hwui/renderthread/ReliableSurface.cpp @@ -19,6 +19,7 @@ #include #include // TODO: this should be including apex instead. +#include #include namespace android::uirenderer::renderthread { @@ -44,6 +45,7 @@ ReliableSurface::~ReliableSurface() { ANativeWindow_setDequeueBufferInterceptor(mWindow, nullptr, nullptr); ANativeWindow_setQueueBufferInterceptor(mWindow, nullptr, nullptr); ANativeWindow_setPerformInterceptor(mWindow, nullptr, nullptr); + ANativeWindow_setQueryInterceptor(mWindow, nullptr, nullptr); ANativeWindow_release(mWindow); } @@ -63,6 +65,10 @@ void ReliableSurface::init() { result = ANativeWindow_setPerformInterceptor(mWindow, hook_perform, this); LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set perform interceptor: error = %d", result); + + result = ANativeWindow_setQueryInterceptor(mWindow, hook_query, this); + LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set query interceptor: error = %d", + result); } int ReliableSurface::reserveNext() { @@ -249,9 +255,29 @@ int ReliableSurface::hook_perform(ANativeWindow* window, ANativeWindow_performFn case ANATIVEWINDOW_PERFORM_SET_BUFFERS_FORMAT: rs->mFormat = static_cast(va_arg(args, int32_t)); break; + case NATIVE_WINDOW_SET_BUFFER_COUNT: + size_t bufferCount = va_arg(args, size_t); + if (bufferCount >= rs->mExpectedBufferCount) { + rs->mDidSetExtraBuffers = true; + } else { + ALOGD("HOOK FAILED! Expected %zd got = %zd", rs->mExpectedBufferCount, bufferCount); + } + break; } } return result; } +int ReliableSurface::hook_query(const ANativeWindow *window, ANativeWindow_queryFn query, + void *data, int what, int *value) { + ReliableSurface* rs = reinterpret_cast(data); + int result = query(window, what, value); + if (what == ANATIVEWINDOW_QUERY_MIN_UNDEQUEUED_BUFFERS && result == OK) { + std::lock_guard _lock{rs->mMutex}; + *value += rs->mExtraBuffers; + rs->mExpectedBufferCount = *value + 2; + } + return result; +} + }; // namespace android::uirenderer::renderthread diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h index 58cd06730123..f699eb1fe6b3 100644 --- a/libs/hwui/renderthread/ReliableSurface.h +++ b/libs/hwui/renderthread/ReliableSurface.h @@ -17,6 +17,7 @@ #pragma once #include +#include #include #include #include @@ -49,6 +50,16 @@ public: return ret; } + void setExtraBufferCount(size_t extraBuffers) { + std::lock_guard _lock{mMutex}; + mExtraBuffers = extraBuffers; + } + + bool didSetExtraBuffers() const { + std::lock_guard _lock{mMutex}; + return mDidSetExtraBuffers; + } + private: ANativeWindow* mWindow; @@ -62,6 +73,9 @@ private: base::unique_fd mReservedFenceFd; bool mHasDequeuedBuffer = false; int mBufferQueueState = OK; + size_t mExtraBuffers = 0; + size_t mExpectedBufferCount = 0; + bool mDidSetExtraBuffers = false; bool isFallbackBuffer(const ANativeWindowBuffer* windowBuffer) const; ANativeWindowBuffer* acquireFallbackBuffer(int error); @@ -81,6 +95,8 @@ private: static int hook_perform(ANativeWindow* window, ANativeWindow_performFn perform, void* data, int operation, va_list args); + static int hook_query(const ANativeWindow* window, ANativeWindow_queryFn query, void* data, + int what, int* value); }; }; // namespace android::uirenderer::renderthread diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp index 1208062d9da0..e7a889d08cfd 100644 --- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp +++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp @@ -398,7 +398,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, context_lost) { auto surface = context.surface(); auto pipeline = std::make_unique(renderThread); EXPECT_FALSE(pipeline->isSurfaceReady()); - EXPECT_TRUE(pipeline->setSurface(surface.get(), SwapBehavior::kSwap_default, 0)); + EXPECT_TRUE(pipeline->setSurface(surface.get(), SwapBehavior::kSwap_default)); EXPECT_TRUE(pipeline->isSurfaceReady()); renderThread.destroyRenderingContext(); EXPECT_FALSE(pipeline->isSurfaceReady()); -- cgit v1.2.3 From 50df43e3543c33e0411b50046b881c73a7122f88 Mon Sep 17 00:00:00 2001 From: Chris Li Date: Mon, 4 May 2020 17:24:14 -0700 Subject: Update WM Jetpack impl to use WindowMetrics Before, SettingsExtensionImpl#getWindowLayoutInfo returned empty window info when Activity was not attached. Now, it returns the correct info after Activity create. Test: atest CtsWindowManagerJetpackTestCases:ExtensionTest Fixes: 152534741 Fixes: 155121604 Fixes: 155692339 Change-Id: Ieded3b70ff14aba03581bf7501cc5e923b5eed33 --- .../src/androidx/window/extensions/ExtensionHelper.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'libs') diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionHelper.java index c4f11a0a370c..c61f1ed2d179 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionHelper.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionHelper.java @@ -30,6 +30,8 @@ import android.os.IBinder; import android.view.DisplayInfo; import android.view.Surface; +import androidx.annotation.Nullable; + /** * Toolkit class for calculation of the display feature bounds within the window. * NOTE: This sample implementation only works for Activity windows, because there is no public APIs @@ -84,7 +86,7 @@ class ExtensionHelper { /** Transform rectangle from absolute coordinate space to the window coordinate space. */ static void transformToWindowSpaceRect(Rect inOutRect, IBinder windowToken) { - Rect windowRect = getWindowRect(windowToken); + Rect windowRect = getWindowBounds(windowToken); if (windowRect == null) { inOutRect.setEmpty(); return; @@ -101,13 +103,12 @@ class ExtensionHelper { * Get the current window bounds in absolute coordinates. * NOTE: Only works with Activity windows. */ - private static Rect getWindowRect(IBinder windowToken) { + @Nullable + private static Rect getWindowBounds(IBinder windowToken) { Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken); - final Rect windowRect = new Rect(); - if (activity != null) { - activity.getWindow().getDecorView().getWindowDisplayFrame(windowRect); - } - return windowRect; + return activity != null + ? activity.getWindowManager().getCurrentWindowMetrics().getBounds() + : null; } /** -- cgit v1.2.3 From a818713c5efbca48bbcd53dbaf1e61bd4ad35fd2 Mon Sep 17 00:00:00 2001 From: Jerome Gaillard Date: Tue, 26 May 2020 17:35:44 +0100 Subject: Use JNI types for return types of JNI methods JNI methods should use JNI types for their return types so as to ensure type compatibility between Java and C++. Bug: 152250228 Test: N/A Change-Id: Iae05806b91ebe5f173607d243d8504ec9e938589 --- libs/hwui/jni/android_graphics_Canvas.cpp | 2 +- libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp | 2 +- libs/hwui/jni/android_util_PathParser.cpp | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) (limited to 'libs') diff --git a/libs/hwui/jni/android_graphics_Canvas.cpp b/libs/hwui/jni/android_graphics_Canvas.cpp index 4aff3e544efa..b6c6cd0b5c1c 100644 --- a/libs/hwui/jni/android_graphics_Canvas.cpp +++ b/libs/hwui/jni/android_graphics_Canvas.cpp @@ -113,7 +113,7 @@ static void restoreUnclippedLayer(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, get_canvas(canvasHandle)->restoreUnclippedLayer(saveCount, *paint); } -static bool restore(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) { +static jboolean restore(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) { Canvas* canvas = get_canvas(canvasHandle); if (canvas->getSaveCount() <= 1) { return false; // cannot restore anymore diff --git a/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp b/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp index 8a262969614e..9cffceb308c8 100644 --- a/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp +++ b/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp @@ -102,7 +102,7 @@ static void setAntiAlias(JNIEnv*, jobject, jlong treePtr, jboolean aa) { /** * Draw */ -static int draw(JNIEnv* env, jobject, jlong treePtr, jlong canvasPtr, +static jint draw(JNIEnv* env, jobject, jlong treePtr, jlong canvasPtr, jlong colorFilterPtr, jobject jrect, jboolean needsMirroring, jboolean canReuseCache) { VectorDrawable::Tree* tree = reinterpret_cast(treePtr); Canvas* canvas = reinterpret_cast(canvasPtr); diff --git a/libs/hwui/jni/android_util_PathParser.cpp b/libs/hwui/jni/android_util_PathParser.cpp index df5e9cd44ed0..72995efb1c21 100644 --- a/libs/hwui/jni/android_util_PathParser.cpp +++ b/libs/hwui/jni/android_util_PathParser.cpp @@ -39,18 +39,18 @@ static void parseStringForPath(JNIEnv* env, jobject, jlong skPathHandle, jstring } } -static long createEmptyPathData(JNIEnv*, jobject) { +static jlong createEmptyPathData(JNIEnv*, jobject) { PathData* pathData = new PathData(); return reinterpret_cast(pathData); } -static long createPathData(JNIEnv*, jobject, jlong pathDataPtr) { +static jlong createPathData(JNIEnv*, jobject, jlong pathDataPtr) { PathData* pathData = reinterpret_cast(pathDataPtr); PathData* newPathData = new PathData(*pathData); return reinterpret_cast(newPathData); } -static long createPathDataFromStringPath(JNIEnv* env, jobject, jstring inputStr, jint strLength) { +static jlong createPathDataFromStringPath(JNIEnv* env, jobject, jstring inputStr, jint strLength) { const char* pathString = env->GetStringUTFChars(inputStr, NULL); PathData* pathData = new PathData(); PathParser::ParseResult result; @@ -65,7 +65,7 @@ static long createPathDataFromStringPath(JNIEnv* env, jobject, jstring inputStr, } } -static bool interpolatePathData(JNIEnv*, jobject, jlong outPathDataPtr, jlong fromPathDataPtr, +static jboolean interpolatePathData(JNIEnv*, jobject, jlong outPathDataPtr, jlong fromPathDataPtr, jlong toPathDataPtr, jfloat fraction) { PathData* outPathData = reinterpret_cast(outPathDataPtr); PathData* fromPathData = reinterpret_cast(fromPathDataPtr); @@ -79,7 +79,7 @@ static void deletePathData(JNIEnv*, jobject, jlong pathDataHandle) { delete pathData; } -static bool canMorphPathData(JNIEnv*, jobject, jlong fromPathDataPtr, jlong toPathDataPtr) { +static jboolean canMorphPathData(JNIEnv*, jobject, jlong fromPathDataPtr, jlong toPathDataPtr) { PathData* fromPathData = reinterpret_cast(fromPathDataPtr); PathData* toPathData = reinterpret_cast(toPathDataPtr); return VectorDrawableUtils::canMorph(*fromPathData, *toPathData); -- cgit v1.2.3 From d338dfe5830df0960b7b16b7191cb42bd2115996 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Sat, 23 Feb 2019 15:33:08 +0800 Subject: Avoid Asset::LoadImpl crash when getBuffer is null If for some reason Asset::getBuffer returns a null pointer, error out instead of dereferencing the null pointer. Bug: 125943266 Bug: 154461471 Test: boots Change-Id: I957be4f9b8c49c2a6829e8b82fae0ae8d8d7639e --- libs/androidfw/ApkAssets.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) mode change 100644 => 100755 libs/androidfw/ApkAssets.cpp (limited to 'libs') diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp old mode 100644 new mode 100755 index 05f4d6b63a4c..e15b42d46f53 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -496,6 +496,11 @@ std::unique_ptr ApkAssets::LoadImpl( const StringPiece data( reinterpret_cast(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)), loaded_apk->resources_asset_->getLength()); + if (data.data() == nullptr || data.empty()) { + LOG(ERROR) << "Failed to read '" << kResourcesArsc << "' data in APK '" << path << "'."; + return {}; + } + loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, loaded_apk->loaded_idmap_.get(), property_flags); if (!loaded_apk->loaded_arsc_) { @@ -523,9 +528,14 @@ std::unique_ptr ApkAssets::LoadTableImpl( const StringPiece data( reinterpret_cast(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)), loaded_apk->resources_asset_->getLength()); + if (data.data() == nullptr || data.empty()) { + LOG(ERROR) << "Failed to read resources table data in '" << path << "'."; + return {}; + } + loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, nullptr, property_flags); if (loaded_apk->loaded_arsc_ == nullptr) { - LOG(ERROR) << "Failed to load '" << kResourcesArsc << path; + LOG(ERROR) << "Failed to read resources table in '" << path << "'."; return {}; } -- cgit v1.2.3 From 19a44bc5bc1b243d9f2e2cf7c769b10626157a94 Mon Sep 17 00:00:00 2001 From: Garfield Tan Date: Thu, 28 May 2020 14:31:29 -0700 Subject: Isolate SpriteIcon into its own file. This is to isolate differences between internal master and AOSP so that the open source intern can work on a relatively new project. It also isolates a divergent piece of code into SpriteIcon.cpp. Bug: None Test: Pointer icons in ApiDemos still work. Change-Id: I1bf8f23411ad7170de2497d6fee0f70b015e9b5f Merged-In: I1bf8f23411ad7170de2497d6fee0f70b015e9b5f (cherry picked from commit 7e3457e593f417eb7a5a55d301b3d5f5dd1debdf) --- libs/input/Android.bp | 1 + libs/input/SpriteController.cpp | 46 +++++-------------------------- libs/input/SpriteController.h | 32 ++------------------- libs/input/SpriteIcon.cpp | 60 ++++++++++++++++++++++++++++++++++++++++ libs/input/SpriteIcon.h | 61 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 131 insertions(+), 69 deletions(-) create mode 100644 libs/input/SpriteIcon.cpp create mode 100644 libs/input/SpriteIcon.h (limited to 'libs') diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 88d6033ed9fb..5252cd041199 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -17,6 +17,7 @@ cc_library_shared { srcs: [ "PointerController.cpp", "SpriteController.cpp", + "SpriteIcon.cpp", ], shared_libs: [ diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp index 804644c230b9..acd8bced0612 100644 --- a/libs/input/SpriteController.cpp +++ b/libs/input/SpriteController.cpp @@ -23,11 +23,6 @@ #include #include -#include -#include -#include -#include - namespace android { // --- SpriteController --- @@ -130,8 +125,8 @@ void SpriteController::doUpdateSprites() { SpriteUpdate& update = updates.editItemAt(i); if (update.state.surfaceControl == NULL && update.state.wantSurfaceVisible()) { - update.state.surfaceWidth = update.state.icon.bitmap.getInfo().width; - update.state.surfaceHeight = update.state.icon.bitmap.getInfo().height; + update.state.surfaceWidth = update.state.icon.width(); + update.state.surfaceHeight = update.state.icon.height(); update.state.surfaceDrawn = false; update.state.surfaceVisible = false; update.state.surfaceControl = obtainSurface( @@ -152,8 +147,8 @@ void SpriteController::doUpdateSprites() { } if (update.state.wantSurfaceVisible()) { - int32_t desiredWidth = update.state.icon.bitmap.getInfo().width; - int32_t desiredHeight = update.state.icon.bitmap.getInfo().height; + int32_t desiredWidth = update.state.icon.width(); + int32_t desiredHeight = update.state.icon.height(); if (update.state.surfaceWidth < desiredWidth || update.state.surfaceHeight < desiredHeight) { needApplyTransaction = true; @@ -194,36 +189,9 @@ void SpriteController::doUpdateSprites() { if (update.state.surfaceControl != NULL && !update.state.surfaceDrawn && update.state.wantSurfaceVisible()) { sp surface = update.state.surfaceControl->getSurface(); - ANativeWindow_Buffer outBuffer; - status_t status = surface->lock(&outBuffer, NULL); - if (status) { - ALOGE("Error %d locking sprite surface before drawing.", status); - } else { - graphics::Paint paint; - paint.setBlendMode(ABLEND_MODE_SRC); - - graphics::Canvas canvas(outBuffer, (int32_t) surface->getBuffersDataSpace()); - canvas.drawBitmap(update.state.icon.bitmap, 0, 0, &paint); - - const int iconWidth = update.state.icon.bitmap.getInfo().width; - const int iconHeight = update.state.icon.bitmap.getInfo().height; - - if (outBuffer.width > iconWidth) { - paint.setBlendMode(ABLEND_MODE_CLEAR); // clear to transparent - canvas.drawRect({iconWidth, 0, outBuffer.width, iconHeight}, paint); - } - if (outBuffer.height > iconHeight) { - paint.setBlendMode(ABLEND_MODE_CLEAR); // clear to transparent - canvas.drawRect({0, iconHeight, outBuffer.width, outBuffer.height}, paint); - } - - status = surface->unlockAndPost(); - if (status) { - ALOGE("Error %d unlocking and posting sprite surface after drawing.", status); - } else { - update.state.surfaceDrawn = true; - update.surfaceChanged = surfaceChanged = true; - } + if (update.state.icon.draw(surface)) { + update.state.surfaceDrawn = true; + update.surfaceChanged = surfaceChanged = true; } } } diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h index 2513544d4bdf..137b5646feae 100644 --- a/libs/input/SpriteController.h +++ b/libs/input/SpriteController.h @@ -20,9 +20,10 @@ #include #include -#include #include +#include "SpriteIcon.h" + namespace android { /* @@ -50,35 +51,6 @@ struct SpriteTransformationMatrix { } }; -/* - * Icon that a sprite displays, including its hotspot. - */ -struct SpriteIcon { - inline SpriteIcon() : style(0), hotSpotX(0), hotSpotY(0) { } - inline SpriteIcon(const graphics::Bitmap& bitmap, int32_t style, float hotSpotX, float hotSpotY) : - bitmap(bitmap), style(style), hotSpotX(hotSpotX), hotSpotY(hotSpotY) { } - - graphics::Bitmap bitmap; - int32_t style; - float hotSpotX; - float hotSpotY; - - inline SpriteIcon copy() const { - return SpriteIcon(bitmap.copy(ANDROID_BITMAP_FORMAT_RGBA_8888), style, hotSpotX, hotSpotY); - } - - inline void reset() { - bitmap.reset(); - style = 0; - hotSpotX = 0; - hotSpotY = 0; - } - - inline bool isValid() const { - return bitmap.isValid() && !bitmap.isEmpty(); - } -}; - /* * A sprite is a simple graphical object that is displayed on-screen above other layers. * The basic sprite class is an interface. diff --git a/libs/input/SpriteIcon.cpp b/libs/input/SpriteIcon.cpp new file mode 100644 index 000000000000..b7e51e22a214 --- /dev/null +++ b/libs/input/SpriteIcon.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2020 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 "SpriteIcon.h" + +#include +#include +#include +#include +#include + +namespace android { + +bool SpriteIcon::draw(sp surface) const { + ANativeWindow_Buffer outBuffer; + status_t status = surface->lock(&outBuffer, NULL); + if (status) { + ALOGE("Error %d locking sprite surface before drawing.", status); + return false; + } + + graphics::Paint paint; + paint.setBlendMode(ABLEND_MODE_SRC); + + graphics::Canvas canvas(outBuffer, (int32_t)surface->getBuffersDataSpace()); + canvas.drawBitmap(bitmap, 0, 0, &paint); + + const int iconWidth = width(); + const int iconHeight = height(); + + if (outBuffer.width > iconWidth) { + paint.setBlendMode(ABLEND_MODE_CLEAR); // clear to transparent + canvas.drawRect({iconWidth, 0, outBuffer.width, iconHeight}, paint); + } + if (outBuffer.height > iconHeight) { + paint.setBlendMode(ABLEND_MODE_CLEAR); // clear to transparent + canvas.drawRect({0, iconHeight, outBuffer.width, outBuffer.height}, paint); + } + + status = surface->unlockAndPost(); + if (status) { + ALOGE("Error %d unlocking and posting sprite surface after drawing.", status); + } + return !status; +} + +} // namespace android diff --git a/libs/input/SpriteIcon.h b/libs/input/SpriteIcon.h new file mode 100644 index 000000000000..a257d7e89ebc --- /dev/null +++ b/libs/input/SpriteIcon.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2020 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 _UI_SPRITE_ICON_H +#define _UI_SPRITE_ICON_H + +#include +#include + +namespace android { + +/* + * Icon that a sprite displays, including its hotspot. + */ +struct SpriteIcon { + inline SpriteIcon() : style(0), hotSpotX(0), hotSpotY(0) {} + inline SpriteIcon(const graphics::Bitmap& bitmap, int32_t style, float hotSpotX, float hotSpotY) + : bitmap(bitmap), style(style), hotSpotX(hotSpotX), hotSpotY(hotSpotY) {} + + graphics::Bitmap bitmap; + int32_t style; + float hotSpotX; + float hotSpotY; + + inline SpriteIcon copy() const { + return SpriteIcon(bitmap.copy(ANDROID_BITMAP_FORMAT_RGBA_8888), style, hotSpotX, hotSpotY); + } + + inline void reset() { + bitmap.reset(); + style = 0; + hotSpotX = 0; + hotSpotY = 0; + } + + inline bool isValid() const { return bitmap.isValid() && !bitmap.isEmpty(); } + + inline int32_t width() const { return bitmap.getInfo().width; } + inline int32_t height() const { return bitmap.getInfo().height; } + + // Draw the bitmap onto the given surface. Returns true if it's successful, or false otherwise. + // Note it doesn't set any metadata to the surface. + bool draw(const sp surface) const; +}; + +} // namespace android + +#endif // _UI_SPRITE_ICON_H -- cgit v1.2.3 From a707013b78cea3586fdadf9a2f04932e823d7504 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Wed, 13 May 2020 14:17:52 -0700 Subject: Add policies and enforce overlayable to header If the fulfilled policies change without the contents of the target and overlay APKs changing, the idmap for the overlay should be regenerated. This change adds fulfilled policies and enforce overlayable to the idmap header so that idmap2d can determine if the polices or enforce overlayable changed from what was used to generate the idmap. Bug: 119328308 Test: idmap2_tests Test: atest RegenerateIdmapTest Change-Id: I96f970e82b5243be01b205ac2cb6ab249c6100bc --- libs/androidfw/include/androidfw/ResourceTypes.h | 5 ++++- libs/androidfw/tests/data/overlay/overlay.apk | Bin 2988 -> 2992 bytes libs/androidfw/tests/data/overlay/overlay.idmap | Bin 1137 -> 1090 bytes 3 files changed, 4 insertions(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index 2bfc7fc38d1c..21be81cb85bd 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -41,7 +41,7 @@ namespace android { constexpr const static uint32_t kIdmapMagic = 0x504D4449u; -constexpr const static uint32_t kIdmapCurrentVersion = 0x00000003u; +constexpr const static uint32_t kIdmapCurrentVersion = 0x00000004u; /** * In C++11, char16_t is defined as *at least* 16 bits. We do a lot of @@ -1746,6 +1746,9 @@ struct Idmap_header { uint32_t target_crc32; uint32_t overlay_crc32; + uint32_t fulfilled_policies; + uint8_t enforce_overlayable; + uint8_t target_path[256]; uint8_t overlay_path[256]; diff --git a/libs/androidfw/tests/data/overlay/overlay.apk b/libs/androidfw/tests/data/overlay/overlay.apk index 62e98662e68d..f1ed59279fdb 100644 Binary files a/libs/androidfw/tests/data/overlay/overlay.apk and b/libs/androidfw/tests/data/overlay/overlay.apk differ diff --git a/libs/androidfw/tests/data/overlay/overlay.idmap b/libs/androidfw/tests/data/overlay/overlay.idmap index 3759ed650033..29c5eb6a9ccf 100644 Binary files a/libs/androidfw/tests/data/overlay/overlay.idmap and b/libs/androidfw/tests/data/overlay/overlay.idmap differ -- cgit v1.2.3 From b9437ef4f12c5b9307c50bf4a294cbc1b62f2c1a Mon Sep 17 00:00:00 2001 From: Diego Vela Date: Tue, 2 Jun 2020 10:28:16 -0700 Subject: Downgrade Window JetPack Add window sidecar aar. Add settings implementation of SidecarInterface. Bug: 157477145 Test: use a device with sidecar and not window extensions. atest CtsWindowManagerJetpackTestCases:ExtensionTest Change-Id: Iade7a4ba5a75ba7301cd151b54a95820a3d4bdde --- libs/WindowManager/Jetpack/Android.bp | 14 +- .../Jetpack/androidx.window.extensions.xml | 21 -- .../Jetpack/androidx.window.sidecar.xml | 21 ++ .../window/extensions/ExtensionHelper.java | 131 ------------ .../window/extensions/ExtensionProvider.java | 42 ---- .../window/extensions/SettingsExtensionImpl.java | 217 -------------------- .../window/sidecar/SettingsSidecarImpl.java | 222 +++++++++++++++++++++ .../src/androidx/window/sidecar/SidecarHelper.java | 126 ++++++++++++ .../androidx/window/sidecar/SidecarProvider.java | 41 ++++ .../src/androidx/window/sidecar/StubSidecar.java | 85 ++++++++ .../Jetpack/window-extensions-release.aar | Bin 6427 -> 0 bytes .../Jetpack/window-sidecar-release.aar | Bin 0 -> 4366 bytes 12 files changed, 502 insertions(+), 418 deletions(-) delete mode 100644 libs/WindowManager/Jetpack/androidx.window.extensions.xml create mode 100644 libs/WindowManager/Jetpack/androidx.window.sidecar.xml delete mode 100644 libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionHelper.java delete mode 100644 libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java delete mode 100644 libs/WindowManager/Jetpack/src/androidx/window/extensions/SettingsExtensionImpl.java create mode 100644 libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java create mode 100644 libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java create mode 100644 libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java create mode 100644 libs/WindowManager/Jetpack/src/androidx/window/sidecar/StubSidecar.java delete mode 100644 libs/WindowManager/Jetpack/window-extensions-release.aar create mode 100644 libs/WindowManager/Jetpack/window-sidecar-release.aar (limited to 'libs') diff --git a/libs/WindowManager/Jetpack/Android.bp b/libs/WindowManager/Jetpack/Android.bp index 308c1a59a7aa..4f4364f72fef 100644 --- a/libs/WindowManager/Jetpack/Android.bp +++ b/libs/WindowManager/Jetpack/Android.bp @@ -13,26 +13,26 @@ // limitations under the License. android_library_import { - name: "window-extensions", - aars: ["window-extensions-release.aar"], + name: "window-sidecar", + aars: ["window-sidecar-release.aar"], sdk_version: "current", } java_library { - name: "androidx.window.extensions", + name: "androidx.window.sidecar", srcs: ["src/**/*.java"], - static_libs: ["window-extensions"], + static_libs: ["window-sidecar"], installable: true, sdk_version: "core_platform", vendor: true, libs: ["framework", "androidx.annotation_annotation",], - required: ["androidx.window.extensions.xml",], + required: ["androidx.window.sidecar.xml",], } prebuilt_etc { - name: "androidx.window.extensions.xml", + name: "androidx.window.sidecar.xml", vendor: true, sub_dir: "permissions", - src: "androidx.window.extensions.xml", + src: "androidx.window.sidecar.xml", filename_from_src: true, } diff --git a/libs/WindowManager/Jetpack/androidx.window.extensions.xml b/libs/WindowManager/Jetpack/androidx.window.extensions.xml deleted file mode 100644 index 1f0ff6656de0..000000000000 --- a/libs/WindowManager/Jetpack/androidx.window.extensions.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - diff --git a/libs/WindowManager/Jetpack/androidx.window.sidecar.xml b/libs/WindowManager/Jetpack/androidx.window.sidecar.xml new file mode 100644 index 000000000000..f88a5f4ae039 --- /dev/null +++ b/libs/WindowManager/Jetpack/androidx.window.sidecar.xml @@ -0,0 +1,21 @@ + + + + + diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionHelper.java deleted file mode 100644 index c61f1ed2d179..000000000000 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionHelper.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2020 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. - */ - -package androidx.window.extensions; - -import static android.view.Display.INVALID_DISPLAY; -import static android.view.Surface.ROTATION_0; -import static android.view.Surface.ROTATION_180; -import static android.view.Surface.ROTATION_270; -import static android.view.Surface.ROTATION_90; - -import android.app.Activity; -import android.app.ActivityThread; -import android.graphics.Rect; -import android.hardware.display.DisplayManagerGlobal; -import android.os.IBinder; -import android.view.DisplayInfo; -import android.view.Surface; - -import androidx.annotation.Nullable; - -/** - * Toolkit class for calculation of the display feature bounds within the window. - * NOTE: This sample implementation only works for Activity windows, because there is no public APIs - * to obtain layout params or bounds for arbitrary windows. - */ -class ExtensionHelper { - /** - * Rotate the input rectangle specified in default display orientation to the current display - * rotation. - */ - static void rotateRectToDisplayRotation(Rect inOutRect, int displayId) { - DisplayManagerGlobal dmGlobal = DisplayManagerGlobal.getInstance(); - DisplayInfo displayInfo = dmGlobal.getDisplayInfo(displayId); - int rotation = displayInfo.rotation; - - boolean isSideRotation = rotation == ROTATION_90 || rotation == ROTATION_270; - int displayWidth = isSideRotation ? displayInfo.logicalHeight : displayInfo.logicalWidth; - int displayHeight = isSideRotation ? displayInfo.logicalWidth : displayInfo.logicalHeight; - - inOutRect.intersect(0, 0, displayWidth, displayHeight); - - rotateBounds(inOutRect, displayWidth, displayHeight, rotation); - } - - /** - * Rotate the input rectangle within parent bounds for a given delta. - */ - private static void rotateBounds(Rect inOutRect, int parentWidth, int parentHeight, - @Surface.Rotation int delta) { - int origLeft = inOutRect.left; - switch (delta) { - case ROTATION_0: - return; - case ROTATION_90: - inOutRect.left = inOutRect.top; - inOutRect.top = parentWidth - inOutRect.right; - inOutRect.right = inOutRect.bottom; - inOutRect.bottom = parentWidth - origLeft; - return; - case ROTATION_180: - inOutRect.left = parentWidth - inOutRect.right; - inOutRect.right = parentWidth - origLeft; - return; - case ROTATION_270: - inOutRect.left = parentHeight - inOutRect.bottom; - inOutRect.bottom = inOutRect.right; - inOutRect.right = parentHeight - inOutRect.top; - inOutRect.top = origLeft; - return; - } - } - - /** Transform rectangle from absolute coordinate space to the window coordinate space. */ - static void transformToWindowSpaceRect(Rect inOutRect, IBinder windowToken) { - Rect windowRect = getWindowBounds(windowToken); - if (windowRect == null) { - inOutRect.setEmpty(); - return; - } - if (!Rect.intersects(inOutRect, windowRect)) { - inOutRect.setEmpty(); - return; - } - inOutRect.intersect(windowRect); - inOutRect.offset(-windowRect.left, -windowRect.top); - } - - /** - * Get the current window bounds in absolute coordinates. - * NOTE: Only works with Activity windows. - */ - @Nullable - private static Rect getWindowBounds(IBinder windowToken) { - Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken); - return activity != null - ? activity.getWindowManager().getCurrentWindowMetrics().getBounds() - : null; - } - - /** - * Check if this window is an Activity window that is in multi-window mode. - */ - static boolean isInMultiWindow(IBinder windowToken) { - Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken); - return activity != null && activity.isInMultiWindowMode(); - } - - /** - * Get the id of the parent display for the window. - * NOTE: Only works with Activity windows. - */ - static int getWindowDisplay(IBinder windowToken) { - Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken); - return activity != null - ? activity.getWindowManager().getDefaultDisplay().getDisplayId() : INVALID_DISPLAY; - } -} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java deleted file mode 100644 index 47349f11fb93..000000000000 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2020 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. - */ - -package androidx.window.extensions; - -import android.content.Context; - -/** - * Provider class that will instantiate the library implementation. It must be included in the - * vendor library, and the vendor implementation must match the signature of this class. - */ -public class ExtensionProvider { - - /** - * The support library will instantiate the vendor implementation using this interface. - * @return An implementation of {@link ExtensionInterface}. - */ - public static ExtensionInterface getExtensionImpl(Context context) { - return new SettingsExtensionImpl(context); - } - - /** - * The support library will use this method to check API version compatibility. - * @return API version string in MAJOR.MINOR.PATCH-description format. - */ - public static String getApiVersion() { - return "1.0.0-settings_sample"; - } -} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/SettingsExtensionImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/SettingsExtensionImpl.java deleted file mode 100644 index 7a3fbf3ad9b8..000000000000 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/SettingsExtensionImpl.java +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright 2020 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. - */ - -package androidx.window.extensions; - -import static android.view.Display.DEFAULT_DISPLAY; - -import static androidx.window.extensions.ExtensionHelper.getWindowDisplay; -import static androidx.window.extensions.ExtensionHelper.isInMultiWindow; -import static androidx.window.extensions.ExtensionHelper.rotateRectToDisplayRotation; -import static androidx.window.extensions.ExtensionHelper.transformToWindowSpaceRect; - -import android.content.ContentResolver; -import android.content.Context; -import android.database.ContentObserver; -import android.graphics.Rect; -import android.net.Uri; -import android.os.Handler; -import android.os.IBinder; -import android.os.Looper; -import android.provider.Settings; -import android.text.TextUtils; -import android.util.Log; - -import androidx.annotation.NonNull; - -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -class SettingsExtensionImpl extends StubExtension { - private static final String TAG = "SettingsExtension"; - - private static final String DEVICE_POSTURE = "device_posture"; - private static final String DISPLAY_FEATURES = "display_features"; - - private static final Pattern FEATURE_PATTERN = - Pattern.compile("([a-z]+)-\\[(\\d+),(\\d+),(\\d+),(\\d+)]"); - - private static final String FEATURE_TYPE_FOLD = "fold"; - private static final String FEATURE_TYPE_HINGE = "hinge"; - - private Context mContext; - private SettingsObserver mSettingsObserver; - - final class SettingsObserver extends ContentObserver { - private final Uri mDevicePostureUri = - Settings.Global.getUriFor(DEVICE_POSTURE); - private final Uri mDisplayFeaturesUri = - Settings.Global.getUriFor(DISPLAY_FEATURES); - private final ContentResolver mResolver = mContext.getContentResolver(); - private boolean mRegisteredObservers; - - - private SettingsObserver() { - super(new Handler(Looper.getMainLooper())); - } - - private void registerObserversIfNeeded() { - if (mRegisteredObservers) { - return; - } - mRegisteredObservers = true; - mResolver.registerContentObserver(mDevicePostureUri, false /* notifyForDescendents */, - this /* ContentObserver */); - mResolver.registerContentObserver(mDisplayFeaturesUri, false /* notifyForDescendents */, - this /* ContentObserver */); - } - - private void unregisterObserversIfNeeded() { - if (!mRegisteredObservers) { - return; - } - mRegisteredObservers = false; - mResolver.unregisterContentObserver(this); - } - - @Override - public void onChange(boolean selfChange, Uri uri) { - if (uri == null) { - return; - } - - if (mDevicePostureUri.equals(uri)) { - updateDevicePosture(); - return; - } - if (mDisplayFeaturesUri.equals(uri)) { - updateDisplayFeatures(); - return; - } - } - } - - SettingsExtensionImpl(Context context) { - mContext = context; - mSettingsObserver = new SettingsObserver(); - } - - private void updateDevicePosture() { - updateDeviceState(getDeviceState()); - } - - /** Update display features with values read from settings. */ - private void updateDisplayFeatures() { - for (IBinder windowToken : getWindowsListeningForLayoutChanges()) { - ExtensionWindowLayoutInfo newLayout = getWindowLayoutInfo(windowToken); - updateWindowLayout(windowToken, newLayout); - } - } - - @NonNull - @Override - public ExtensionDeviceState getDeviceState() { - ContentResolver resolver = mContext.getContentResolver(); - int posture = Settings.Global.getInt(resolver, DEVICE_POSTURE, - ExtensionDeviceState.POSTURE_UNKNOWN); - return new ExtensionDeviceState(posture); - } - - @NonNull - @Override - public ExtensionWindowLayoutInfo getWindowLayoutInfo(@NonNull IBinder windowToken) { - List displayFeatures = readDisplayFeatures(windowToken); - return new ExtensionWindowLayoutInfo(displayFeatures); - } - - private List readDisplayFeatures(IBinder windowToken) { - List features = new ArrayList(); - int displayId = getWindowDisplay(windowToken); - if (displayId != DEFAULT_DISPLAY) { - Log.w(TAG, "This sample doesn't support display features on secondary displays"); - return features; - } - - ContentResolver resolver = mContext.getContentResolver(); - final String displayFeaturesString = Settings.Global.getString(resolver, DISPLAY_FEATURES); - if (isInMultiWindow(windowToken)) { - // It is recommended not to report any display features in multi-window mode, since it - // won't be possible to synchronize the display feature positions with window movement. - return features; - } - if (TextUtils.isEmpty(displayFeaturesString)) { - return features; - } - - String[] featureStrings = displayFeaturesString.split(";"); - for (String featureString : featureStrings) { - Matcher featureMatcher = FEATURE_PATTERN.matcher(featureString); - if (!featureMatcher.matches()) { - Log.e(TAG, "Malformed feature description format: " + featureString); - continue; - } - try { - String featureType = featureMatcher.group(1); - int type; - switch (featureType) { - case FEATURE_TYPE_FOLD: - type = ExtensionDisplayFeature.TYPE_FOLD; - break; - case FEATURE_TYPE_HINGE: - type = ExtensionDisplayFeature.TYPE_HINGE; - break; - default: { - Log.e(TAG, "Malformed feature type: " + featureType); - continue; - } - } - - int left = Integer.parseInt(featureMatcher.group(2)); - int top = Integer.parseInt(featureMatcher.group(3)); - int right = Integer.parseInt(featureMatcher.group(4)); - int bottom = Integer.parseInt(featureMatcher.group(5)); - Rect featureRect = new Rect(left, top, right, bottom); - rotateRectToDisplayRotation(featureRect, displayId); - transformToWindowSpaceRect(featureRect, windowToken); - if (!featureRect.isEmpty()) { - ExtensionDisplayFeature feature = - new ExtensionDisplayFeature(featureRect, type); - features.add(feature); - } else { - Log.w(TAG, "Failed to adjust feature to window"); - } - } catch (NumberFormatException e) { - Log.e(TAG, "Malformed feature description: " + featureString); - } - } - return features; - } - - @Override - protected void onListenersChanged() { - if (mSettingsObserver == null) { - return; - } - - if (hasListeners()) { - mSettingsObserver.registerObserversIfNeeded(); - } else { - mSettingsObserver.unregisterObserversIfNeeded(); - } - } -} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java new file mode 100644 index 000000000000..92e575804bbe --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2020 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. + */ + +package androidx.window.sidecar; + +import static android.view.Display.DEFAULT_DISPLAY; + +import static androidx.window.sidecar.SidecarHelper.getWindowDisplay; +import static androidx.window.sidecar.SidecarHelper.isInMultiWindow; +import static androidx.window.sidecar.SidecarHelper.rotateRectToDisplayRotation; +import static androidx.window.sidecar.SidecarHelper.transformToWindowSpaceRect; + +import android.content.ContentResolver; +import android.content.Context; +import android.database.ContentObserver; +import android.graphics.Rect; +import android.net.Uri; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.Log; + +import androidx.annotation.NonNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +class SettingsSidecarImpl extends StubSidecar { + private static final String TAG = "SettingsSidecar"; + + private static final String DEVICE_POSTURE = "device_posture"; + private static final String DISPLAY_FEATURES = "display_features"; + + private static final Pattern FEATURE_PATTERN = + Pattern.compile("([a-z]+)-\\[(\\d+),(\\d+),(\\d+),(\\d+)]"); + + private static final String FEATURE_TYPE_FOLD = "fold"; + private static final String FEATURE_TYPE_HINGE = "hinge"; + + private Context mContext; + private SettingsObserver mSettingsObserver; + + final class SettingsObserver extends ContentObserver { + private final Uri mDevicePostureUri = + Settings.Global.getUriFor(DEVICE_POSTURE); + private final Uri mDisplayFeaturesUri = + Settings.Global.getUriFor(DISPLAY_FEATURES); + private final ContentResolver mResolver = mContext.getContentResolver(); + private boolean mRegisteredObservers; + + + private SettingsObserver() { + super(new Handler(Looper.getMainLooper())); + } + + private void registerObserversIfNeeded() { + if (mRegisteredObservers) { + return; + } + mRegisteredObservers = true; + mResolver.registerContentObserver(mDevicePostureUri, false /* notifyForDescendents */, + this /* ContentObserver */); + mResolver.registerContentObserver(mDisplayFeaturesUri, false /* notifyForDescendents */, + this /* ContentObserver */); + } + + private void unregisterObserversIfNeeded() { + if (!mRegisteredObservers) { + return; + } + mRegisteredObservers = false; + mResolver.unregisterContentObserver(this); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + if (uri == null) { + return; + } + + if (mDevicePostureUri.equals(uri)) { + updateDevicePosture(); + return; + } + if (mDisplayFeaturesUri.equals(uri)) { + updateDisplayFeatures(); + return; + } + } + } + + SettingsSidecarImpl(Context context) { + mContext = context; + mSettingsObserver = new SettingsObserver(); + } + + private void updateDevicePosture() { + updateDeviceState(getDeviceState()); + } + + /** Update display features with values read from settings. */ + private void updateDisplayFeatures() { + for (IBinder windowToken : getWindowsListeningForLayoutChanges()) { + SidecarWindowLayoutInfo newLayout = getWindowLayoutInfo(windowToken); + updateWindowLayout(windowToken, newLayout); + } + } + + @NonNull + @Override + public SidecarDeviceState getDeviceState() { + ContentResolver resolver = mContext.getContentResolver(); + int posture = Settings.Global.getInt(resolver, DEVICE_POSTURE, + SidecarDeviceState.POSTURE_UNKNOWN); + SidecarDeviceState deviceState = new SidecarDeviceState(); + deviceState.posture = posture; + return deviceState; + } + + @NonNull + @Override + public SidecarWindowLayoutInfo getWindowLayoutInfo(@NonNull IBinder windowToken) { + List displayFeatures = readDisplayFeatures(windowToken); + SidecarWindowLayoutInfo windowLayoutInfo = new SidecarWindowLayoutInfo(); + windowLayoutInfo.displayFeatures = displayFeatures; + return windowLayoutInfo; + } + + private List readDisplayFeatures(IBinder windowToken) { + List features = new ArrayList(); + int displayId = getWindowDisplay(windowToken); + if (displayId != DEFAULT_DISPLAY) { + Log.w(TAG, "This sample doesn't support display features on secondary displays"); + return features; + } + + ContentResolver resolver = mContext.getContentResolver(); + final String displayFeaturesString = Settings.Global.getString(resolver, DISPLAY_FEATURES); + if (isInMultiWindow(windowToken)) { + // It is recommended not to report any display features in multi-window mode, since it + // won't be possible to synchronize the display feature positions with window movement. + return features; + } + if (TextUtils.isEmpty(displayFeaturesString)) { + return features; + } + + String[] featureStrings = displayFeaturesString.split(";"); + for (String featureString : featureStrings) { + Matcher featureMatcher = FEATURE_PATTERN.matcher(featureString); + if (!featureMatcher.matches()) { + Log.e(TAG, "Malformed feature description format: " + featureString); + continue; + } + try { + String featureType = featureMatcher.group(1); + int type; + switch (featureType) { + case FEATURE_TYPE_FOLD: + type = SidecarDisplayFeature.TYPE_FOLD; + break; + case FEATURE_TYPE_HINGE: + type = SidecarDisplayFeature.TYPE_HINGE; + break; + default: { + Log.e(TAG, "Malformed feature type: " + featureType); + continue; + } + } + + int left = Integer.parseInt(featureMatcher.group(2)); + int top = Integer.parseInt(featureMatcher.group(3)); + int right = Integer.parseInt(featureMatcher.group(4)); + int bottom = Integer.parseInt(featureMatcher.group(5)); + Rect featureRect = new Rect(left, top, right, bottom); + rotateRectToDisplayRotation(featureRect, displayId); + transformToWindowSpaceRect(featureRect, windowToken); + if (!featureRect.isEmpty()) { + SidecarDisplayFeature feature = new SidecarDisplayFeature(); + feature.setRect(featureRect); + feature.setType(type); + features.add(feature); + } else { + Log.w(TAG, "Failed to adjust feature to window"); + } + } catch (NumberFormatException e) { + Log.e(TAG, "Malformed feature description: " + featureString); + } + } + return features; + } + + @Override + protected void onListenersChanged() { + if (mSettingsObserver == null) { + return; + } + + if (hasListeners()) { + mSettingsObserver.registerObserversIfNeeded(); + } else { + mSettingsObserver.unregisterObserversIfNeeded(); + } + } +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java new file mode 100644 index 000000000000..e5b6cff17b26 --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2020 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. + */ + +package androidx.window.sidecar; + +import static android.view.Display.INVALID_DISPLAY; +import static android.view.Surface.ROTATION_0; +import static android.view.Surface.ROTATION_180; +import static android.view.Surface.ROTATION_270; +import static android.view.Surface.ROTATION_90; + +import android.app.Activity; +import android.app.ActivityThread; +import android.graphics.Rect; +import android.hardware.display.DisplayManagerGlobal; +import android.os.IBinder; +import android.view.DisplayInfo; +import android.view.Surface; + +import androidx.annotation.Nullable; + +class SidecarHelper { + /** + * Rotate the input rectangle specified in default display orientation to the current display + * rotation. + */ + static void rotateRectToDisplayRotation(Rect inOutRect, int displayId) { + DisplayManagerGlobal dmGlobal = DisplayManagerGlobal.getInstance(); + DisplayInfo displayInfo = dmGlobal.getDisplayInfo(displayId); + int rotation = displayInfo.rotation; + + boolean isSideRotation = rotation == ROTATION_90 || rotation == ROTATION_270; + int displayWidth = isSideRotation ? displayInfo.logicalHeight : displayInfo.logicalWidth; + int displayHeight = isSideRotation ? displayInfo.logicalWidth : displayInfo.logicalHeight; + + inOutRect.intersect(0, 0, displayWidth, displayHeight); + + rotateBounds(inOutRect, displayWidth, displayHeight, rotation); + } + + /** + * Rotate the input rectangle within parent bounds for a given delta. + */ + private static void rotateBounds(Rect inOutRect, int parentWidth, int parentHeight, + @Surface.Rotation int delta) { + int origLeft = inOutRect.left; + switch (delta) { + case ROTATION_0: + return; + case ROTATION_90: + inOutRect.left = inOutRect.top; + inOutRect.top = parentWidth - inOutRect.right; + inOutRect.right = inOutRect.bottom; + inOutRect.bottom = parentWidth - origLeft; + return; + case ROTATION_180: + inOutRect.left = parentWidth - inOutRect.right; + inOutRect.right = parentWidth - origLeft; + return; + case ROTATION_270: + inOutRect.left = parentHeight - inOutRect.bottom; + inOutRect.bottom = inOutRect.right; + inOutRect.right = parentHeight - inOutRect.top; + inOutRect.top = origLeft; + return; + } + } + + /** Transform rectangle from absolute coordinate space to the window coordinate space. */ + static void transformToWindowSpaceRect(Rect inOutRect, IBinder windowToken) { + Rect windowRect = getWindowBounds(windowToken); + if (windowRect == null) { + inOutRect.setEmpty(); + return; + } + if (!Rect.intersects(inOutRect, windowRect)) { + inOutRect.setEmpty(); + return; + } + inOutRect.intersect(windowRect); + inOutRect.offset(-windowRect.left, -windowRect.top); + } + + /** + * Get the current window bounds in absolute coordinates. + * NOTE: Only works with Activity windows. + */ + @Nullable + private static Rect getWindowBounds(IBinder windowToken) { + Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken); + return activity != null + ? activity.getWindowManager().getCurrentWindowMetrics().getBounds() + : null; + } + + /** + * Check if this window is an Activity window that is in multi-window mode. + */ + static boolean isInMultiWindow(IBinder windowToken) { + Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken); + return activity != null && activity.isInMultiWindowMode(); + } + + /** + * Get the id of the parent display for the window. + * NOTE: Only works with Activity windows. + */ + static int getWindowDisplay(IBinder windowToken) { + Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken); + return activity != null + ? activity.getWindowManager().getDefaultDisplay().getDisplayId() : INVALID_DISPLAY; + } +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java new file mode 100644 index 000000000000..0b4915ed5dac --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2020 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. + */ + +package androidx.window.sidecar; + +import android.content.Context; + +/** + * Provider class that will instantiate the library implementation. It must be included in the + * vendor library, and the vendor implementation must match the signature of this class. + */ +public class SidecarProvider { + /** + * Provide a simple implementation of {@link SidecarInterface} that can be replaced by + * an OEM by overriding this method. + */ + public static SidecarInterface getSidecarImpl(Context context) { + return new SettingsSidecarImpl(context); + } + + /** + * The support library will use this method to check API version compatibility. + * @return API version string in MAJOR.MINOR.PATCH-description format. + */ + public static String getApiVersion() { + return "0.1.0-settings_sample"; + } +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/StubSidecar.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/StubSidecar.java new file mode 100644 index 000000000000..199c37315c07 --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/StubSidecar.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2020 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. + */ + +package androidx.window.sidecar; + +import android.os.IBinder; + +import androidx.annotation.NonNull; + +import java.util.HashSet; +import java.util.Set; + +/** + * Basic implementation of the {@link SidecarInterface}. An OEM can choose to use it as the base + * class for their implementation. + */ +abstract class StubSidecar implements SidecarInterface { + + private SidecarCallback mSidecarCallback; + private final Set mWindowLayoutChangeListenerTokens = new HashSet<>(); + private boolean mDeviceStateChangeListenerRegistered; + + StubSidecar() { + } + + @Override + public void setSidecarCallback(@NonNull SidecarCallback sidecarCallback) { + this.mSidecarCallback = sidecarCallback; + } + + @Override + public void onWindowLayoutChangeListenerAdded(@NonNull IBinder iBinder) { + this.mWindowLayoutChangeListenerTokens.add(iBinder); + this.onListenersChanged(); + } + + @Override + public void onWindowLayoutChangeListenerRemoved(@NonNull IBinder iBinder) { + this.mWindowLayoutChangeListenerTokens.remove(iBinder); + this.onListenersChanged(); + } + + @Override + public void onDeviceStateListenersChanged(boolean isEmpty) { + this.mDeviceStateChangeListenerRegistered = !isEmpty; + this.onListenersChanged(); + } + + void updateDeviceState(SidecarDeviceState newState) { + if (this.mSidecarCallback != null) { + mSidecarCallback.onDeviceStateChanged(newState); + } + } + + void updateWindowLayout(@NonNull IBinder windowToken, + @NonNull SidecarWindowLayoutInfo newLayout) { + if (this.mSidecarCallback != null) { + mSidecarCallback.onWindowLayoutChanged(windowToken, newLayout); + } + } + + @NonNull + Set getWindowsListeningForLayoutChanges() { + return mWindowLayoutChangeListenerTokens; + } + + protected boolean hasListeners() { + return !mWindowLayoutChangeListenerTokens.isEmpty() || mDeviceStateChangeListenerRegistered; + } + + protected abstract void onListenersChanged(); +} diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar deleted file mode 100644 index 0ebbb86daf82..000000000000 Binary files a/libs/WindowManager/Jetpack/window-extensions-release.aar and /dev/null differ diff --git a/libs/WindowManager/Jetpack/window-sidecar-release.aar b/libs/WindowManager/Jetpack/window-sidecar-release.aar new file mode 100644 index 000000000000..50f101d7d181 Binary files /dev/null and b/libs/WindowManager/Jetpack/window-sidecar-release.aar differ -- cgit v1.2.3 From 30d367873477c19e144206d38873471bc51414a8 Mon Sep 17 00:00:00 2001 From: Leon Scroggins Date: Thu, 11 Jun 2020 15:22:36 +0000 Subject: Revert "Drop max texture cache size from 12x to 5x" Bug: 155002050 Test: Manual - scroll Pchome app This reverts commit 05ac51641297ae2cbe9b186b8c369f3a23a156b9. Reason for revert: When running Pchome, this change resulted in poor performance while scrolling. Every frame appears to be uploading several textures, potentially the same textures over and over again. Change-Id: I0fc7216e16f783051d1bd87af764c1a6f361c138 --- libs/hwui/renderthread/CacheManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index d177855e5a7d..1e5877356e8d 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -42,7 +42,7 @@ namespace renderthread { // to the screen resolution. This is meant to be a conservative default based on // that analysis. The 4.0f is used because the default pixel format is assumed to // be ARGB_8888. -#define SURFACE_SIZE_MULTIPLIER (5.0f * 4.0f) +#define SURFACE_SIZE_MULTIPLIER (12.0f * 4.0f) #define BACKGROUND_RETENTION_PERCENTAGE (0.5f) CacheManager::CacheManager() -- cgit v1.2.3 From dd5d287a738fe0f90eb965412eab68c6ccf61c6f Mon Sep 17 00:00:00 2001 From: Ruchir Rastogi Date: Tue, 9 Jun 2020 17:22:53 -0700 Subject: Add libincident_test to MTS + remove GtsLibIncidentTests (the native GTS tests) from presubmit Test: m libincident_test Test: atest libincident_test Test: m mts && mts-tradefed run mts -m libincident_test Bug: 157142853 Change-Id: I5c6ea2edfbd29165ec65108c4a71a56698c76a27 --- libs/incident/Android.bp | 12 +++++++++++- libs/incident/AndroidTest.xml | 6 +++++- libs/incident/TEST_MAPPING | 3 --- 3 files changed, 16 insertions(+), 5 deletions(-) (limited to 'libs') diff --git a/libs/incident/Android.bp b/libs/incident/Android.bp index af6411011411..d291ec001daf 100644 --- a/libs/incident/Android.bp +++ b/libs/incident/Android.bp @@ -95,7 +95,17 @@ cc_test { name: "libincident_test", test_config: "AndroidTest.xml", defaults: ["libincidentpriv_defaults"], - test_suites: ["device-tests"], + test_suites: ["device-tests", "mts"], + compile_multilib: "both", + multilib: { + lib64: { + suffix: "64", + }, + lib32: { + suffix: "32", + }, + }, + require_root: true, include_dirs: [ "frameworks/base/libs/incident/include", diff --git a/libs/incident/AndroidTest.xml b/libs/incident/AndroidTest.xml index 7c0b04471d13..b6b3f8596826 100644 --- a/libs/incident/AndroidTest.xml +++ b/libs/incident/AndroidTest.xml @@ -16,13 +16,17 @@ - + + + + + diff --git a/libs/incident/TEST_MAPPING b/libs/incident/TEST_MAPPING index 59ebe7664b5f..25e000092ecf 100644 --- a/libs/incident/TEST_MAPPING +++ b/libs/incident/TEST_MAPPING @@ -2,9 +2,6 @@ "presubmit": [ { "name": "libincident_test" - }, - { - "name": "GtsLibIncidentTests" } ] } -- cgit v1.2.3 From c4142d91f5c549f7f3661aeb90d344641deabf62 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Mon, 22 Jun 2020 13:00:58 -0700 Subject: libafw: Use std::free explicitly for overlays Currently we rely on libc to call std::free on the malloced pointer to the overlay table entry. Rather than relying on the libc implementation to call std::free instead of trying to delete the pointer as if it was created using "new", explicitly pass std::free as the deleter to the data held by the std::shared_ptr. Bug: 159562360 Test: Observed no heap growth while looping infinitely and retrieving a string resource overlaid using an inline xml value Change-Id: I9a2ebaf9a993ad9c44fab8f052430c8142d4347d --- libs/androidfw/Idmap.cpp | 2 +- libs/androidfw/include/androidfw/ResourceTypes.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'libs') diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp index eb6ee9525bb9..5f231ffe4786 100644 --- a/libs/androidfw/Idmap.cpp +++ b/libs/androidfw/Idmap.cpp @@ -157,7 +157,7 @@ IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const { table_value->dataType = entry->type; table_value->data = entry->value; - return Result(ResTable_entry_handle::managed(table_entry)); + return Result(ResTable_entry_handle::managed(table_entry, [](auto p) { free(p); })); } static bool is_word_aligned(const void* data) { diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index 21be81cb85bd..e351a46d633a 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -1601,8 +1601,8 @@ class ResTable_entry_handle { entry_ = handle.entry_; } - inline static ResTable_entry_handle managed(ResTable_entry* entry) { - return ResTable_entry_handle(std::shared_ptr(entry)); + inline static ResTable_entry_handle managed(ResTable_entry* entry, void (*deleter)(void *)) { + return ResTable_entry_handle(std::shared_ptr(entry, deleter)); } inline static ResTable_entry_handle unmanaged(const ResTable_entry* entry) { -- cgit v1.2.3 From 864d304156d1ef8985ee39c3c1858349b133b365 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Tue, 2 Jun 2020 13:22:06 +0100 Subject: Update to use nativehelper/JNIPlatformHelp.h Reflects refactoring of JNI helper code that depends on private methods within libnativehelper. Bug: 151443957 Test: Treehugger Change-Id: I7af128f42ae89a77a8e3fb113ea533331153c535 Merged-In: I7af128f42ae89a77a8e3fb113ea533331153c535 Exempt-From-Owner-Approval: cherry pick (cherry picked from commit 329c612e3d5ae440bf13e1f5fc9a4012263524e9) --- libs/hwui/jni/BitmapFactory.cpp | 2 +- libs/hwui/jni/android_graphics_HardwareRenderer.cpp | 2 +- libs/hwui/jni/android_nio_utils.cpp | 2 +- libs/hwui/jni/android_nio_utils.h | 4 +++- libs/hwui/jni/graphics_jni_helpers.h | 2 +- 5 files changed, 7 insertions(+), 5 deletions(-) (limited to 'libs') diff --git a/libs/hwui/jni/BitmapFactory.cpp b/libs/hwui/jni/BitmapFactory.cpp index d4e27d812500..e8e89d81bdb7 100644 --- a/libs/hwui/jni/BitmapFactory.cpp +++ b/libs/hwui/jni/BitmapFactory.cpp @@ -16,7 +16,7 @@ #include "Utils.h" #include -#include +#include #include #include #include diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp index 49c7fcd468e1..9815e85db880 100644 --- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp +++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/libs/hwui/jni/android_nio_utils.cpp b/libs/hwui/jni/android_nio_utils.cpp index c2b09c1d15d7..0663821a5d89 100644 --- a/libs/hwui/jni/android_nio_utils.cpp +++ b/libs/hwui/jni/android_nio_utils.cpp @@ -16,7 +16,7 @@ #include "android_nio_utils.h" -#include +#include namespace android { diff --git a/libs/hwui/jni/android_nio_utils.h b/libs/hwui/jni/android_nio_utils.h index 4aaa0a78c276..4760d9ca8107 100644 --- a/libs/hwui/jni/android_nio_utils.h +++ b/libs/hwui/jni/android_nio_utils.h @@ -17,7 +17,9 @@ #ifndef _ANDROID_NIO_UTILS_H_ #define _ANDROID_NIO_UTILS_H_ -#include +#include + +#include namespace android { diff --git a/libs/hwui/jni/graphics_jni_helpers.h b/libs/hwui/jni/graphics_jni_helpers.h index b97cc6a10179..78db54acc9e5 100644 --- a/libs/hwui/jni/graphics_jni_helpers.h +++ b/libs/hwui/jni/graphics_jni_helpers.h @@ -18,7 +18,7 @@ #define GRAPHICS_JNI_HELPERS #include -#include +#include #include #include #include -- cgit v1.2.3 From 3a09650f6839974af52b029fa41a3668c162e39d Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Fri, 26 Jun 2020 20:25:34 +0100 Subject: Move PointerController from sp to shared_ptr Bug: 160010896 Test: atest PointerController_test, manual usage Change-Id: I4e665d00c56b44c9c1a4ea8cb27ffd10ade3315b Merged-In: I4e665d00c56b44c9c1a4ea8cb27ffd10ade3315b (cherry picked from commit 0cde5911bccf265932739f2428e0c8aa4d3359c1) --- libs/input/PointerController.cpp | 99 ++++++++++++++++------------- libs/input/PointerController.h | 66 +++++++++++-------- libs/input/tests/PointerController_test.cpp | 4 +- 3 files changed, 98 insertions(+), 71 deletions(-) (limited to 'libs') diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp index 3b494e9129db..f74989488491 100644 --- a/libs/input/PointerController.cpp +++ b/libs/input/PointerController.cpp @@ -24,30 +24,9 @@ #include -namespace android { - -// --- WeakLooperCallback --- - -class WeakLooperCallback: public LooperCallback { -protected: - virtual ~WeakLooperCallback() { } - -public: - explicit WeakLooperCallback(const wp& callback) : - mCallback(callback) { - } +#include - virtual int handleEvent(int fd, int events, void* data) { - sp callback = mCallback.promote(); - if (callback != NULL) { - return callback->handleEvent(fd, events, data); - } - return 0; // the client is gone, remove the callback - } - -private: - wp mCallback; -}; +namespace android { // --- PointerController --- @@ -64,21 +43,42 @@ static const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms // The number of events to be read at once for DisplayEventReceiver. static const int EVENT_BUFFER_SIZE = 100; -// --- PointerController --- - -PointerController::PointerController(const sp& policy, - const sp& looper, const sp& spriteController) : - mPolicy(policy), mLooper(looper), mSpriteController(spriteController) { - mHandler = new WeakMessageHandler(this); - mCallback = new WeakLooperCallback(this); - - if (mDisplayEventReceiver.initCheck() == NO_ERROR) { - mLooper->addFd(mDisplayEventReceiver.getFd(), Looper::POLL_CALLBACK, - Looper::EVENT_INPUT, mCallback, nullptr); +std::shared_ptr PointerController::create( + const sp& policy, const sp& looper, + const sp& spriteController) { + std::shared_ptr controller = std::shared_ptr( + new PointerController(policy, looper, spriteController)); + + /* + * Now we need to hook up the constructed PointerController object to its callbacks. + * + * This must be executed after the constructor but before any other methods on PointerController + * in order to ensure that the fully constructed object is visible on the Looper thread, since + * that may be a different thread than where the PointerController is initially constructed. + * + * Unfortunately, this cannot be done as part of the constructor since we need to hand out + * weak_ptr's which themselves cannot be constructed until there's at least one shared_ptr. + */ + + controller->mHandler->pointerController = controller; + controller->mCallback->pointerController = controller; + if (controller->mDisplayEventReceiver.initCheck() == NO_ERROR) { + controller->mLooper->addFd(controller->mDisplayEventReceiver.getFd(), Looper::POLL_CALLBACK, + Looper::EVENT_INPUT, controller->mCallback, nullptr); } else { ALOGE("Failed to initialize DisplayEventReceiver."); } + return controller; +} +PointerController::PointerController(const sp& policy, + const sp& looper, + const sp& spriteController) + : mPolicy(policy), + mLooper(looper), + mSpriteController(spriteController), + mHandler(new MessageHandler()), + mCallback(new LooperCallback()) { AutoMutex _l(mLock); mLocked.animationPending = false; @@ -480,24 +480,35 @@ void PointerController::setCustomPointerIcon(const SpriteIcon& icon) { updatePointerLocked(); } -void PointerController::handleMessage(const Message& message) { +void PointerController::MessageHandler::handleMessage(const Message& message) { + std::shared_ptr controller = pointerController.lock(); + + if (controller == nullptr) { + ALOGE("PointerController instance was released before processing message: what=%d", + message.what); + return; + } switch (message.what) { case MSG_INACTIVITY_TIMEOUT: - doInactivityTimeout(); + controller->doInactivityTimeout(); break; } } -int PointerController::handleEvent(int /* fd */, int events, void* /* data */) { +int PointerController::LooperCallback::handleEvent(int /* fd */, int events, void* /* data */) { + std::shared_ptr controller = pointerController.lock(); + if (controller == nullptr) { + ALOGW("PointerController instance was released with pending callbacks. events=0x%x", + events); + return 0; // Remove the callback, the PointerController is gone anyways + } if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) { - ALOGE("Display event receiver pipe was closed or an error occurred. " - "events=0x%x", events); + ALOGE("Display event receiver pipe was closed or an error occurred. events=0x%x", events); return 0; // remove the callback } if (!(events & Looper::EVENT_INPUT)) { - ALOGW("Received spurious callback for unhandled poll event. " - "events=0x%x", events); + ALOGW("Received spurious callback for unhandled poll event. events=0x%x", events); return 1; // keep the callback } @@ -505,7 +516,7 @@ int PointerController::handleEvent(int /* fd */, int events, void* /* data */) { ssize_t n; nsecs_t timestamp; DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE]; - while ((n = mDisplayEventReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) { + while ((n = controller->mDisplayEventReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) { for (size_t i = 0; i < static_cast(n); ++i) { if (buf[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) { timestamp = buf[i].header.timestamp; @@ -514,7 +525,7 @@ int PointerController::handleEvent(int /* fd */, int events, void* /* data */) { } } if (gotVsync) { - doAnimate(timestamp); + controller->doAnimate(timestamp); } return 1; // keep the callback } @@ -731,7 +742,7 @@ PointerController::Spot* PointerController::removeFirstFadingSpotLocked(std::vec return spot; } } - return NULL; + return nullptr; } void PointerController::releaseSpotLocked(Spot* spot) { diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h index ebc622bae302..ff5631b4d207 100644 --- a/libs/input/PointerController.h +++ b/libs/input/PointerController.h @@ -17,19 +17,20 @@ #ifndef _UI_POINTER_CONTROLLER_H #define _UI_POINTER_CONTROLLER_H -#include "SpriteController.h" - -#include -#include - -#include +#include +#include #include