2014-01-03 18:09:17 -08:00
|
|
|
/*
|
|
|
|
* 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 "RenderProxy.h"
|
|
|
|
|
2015-02-19 14:36:50 -08:00
|
|
|
#include "DeferredLayerUpdater.h"
|
|
|
|
#include "DisplayList.h"
|
2016-03-31 16:36:16 -07:00
|
|
|
#include "Readback.h"
|
2015-02-19 14:36:50 -08:00
|
|
|
#include "Rect.h"
|
|
|
|
#include "renderthread/CanvasContext.h"
|
2016-08-01 14:39:24 -07:00
|
|
|
#include "renderthread/EglManager.h"
|
2015-02-19 14:36:50 -08:00
|
|
|
#include "renderthread/RenderTask.h"
|
|
|
|
#include "renderthread/RenderThread.h"
|
|
|
|
#include "utils/Macros.h"
|
2016-08-01 14:39:24 -07:00
|
|
|
#include "utils/TimeUtils.h"
|
2014-01-03 18:09:17 -08:00
|
|
|
|
2016-11-17 17:54:57 -08:00
|
|
|
#include <ui/GraphicBuffer.h>
|
|
|
|
|
2014-01-03 18:09:17 -08:00
|
|
|
namespace android {
|
|
|
|
namespace uirenderer {
|
|
|
|
namespace renderthread {
|
|
|
|
|
|
|
|
#define ARGS(method) method ## Args
|
|
|
|
|
2014-02-14 20:03:38 -08:00
|
|
|
#define CREATE_BRIDGE0(name) CREATE_BRIDGE(name,,,,,,,,)
|
2014-01-03 18:09:17 -08:00
|
|
|
#define CREATE_BRIDGE1(name, a1) CREATE_BRIDGE(name, a1,,,,,,,)
|
|
|
|
#define CREATE_BRIDGE2(name, a1, a2) CREATE_BRIDGE(name, a1,a2,,,,,,)
|
|
|
|
#define CREATE_BRIDGE3(name, a1, a2, a3) CREATE_BRIDGE(name, a1,a2,a3,,,,,)
|
|
|
|
#define CREATE_BRIDGE4(name, a1, a2, a3, a4) CREATE_BRIDGE(name, a1,a2,a3,a4,,,,)
|
2014-05-20 18:10:25 -07:00
|
|
|
#define CREATE_BRIDGE5(name, a1, a2, a3, a4, a5) CREATE_BRIDGE(name, a1,a2,a3,a4,a5,,,)
|
2014-07-23 18:19:28 -07:00
|
|
|
#define CREATE_BRIDGE6(name, a1, a2, a3, a4, a5, a6) CREATE_BRIDGE(name, a1,a2,a3,a4,a5,a6,,)
|
|
|
|
#define CREATE_BRIDGE7(name, a1, a2, a3, a4, a5, a6, a7) CREATE_BRIDGE(name, a1,a2,a3,a4,a5,a6,a7,)
|
2014-01-03 18:09:17 -08:00
|
|
|
#define CREATE_BRIDGE(name, a1, a2, a3, a4, a5, a6, a7, a8) \
|
|
|
|
typedef struct { \
|
|
|
|
a1; a2; a3; a4; a5; a6; a7; a8; \
|
|
|
|
} ARGS(name); \
|
2016-08-01 14:39:24 -07:00
|
|
|
static_assert(std::is_trivially_destructible<ARGS(name)>::value, \
|
|
|
|
"Error, ARGS must be trivially destructible!"); \
|
2014-01-03 18:09:17 -08:00
|
|
|
static void* Bridge_ ## name(ARGS(name)* args)
|
|
|
|
|
|
|
|
#define SETUP_TASK(method) \
|
|
|
|
LOG_ALWAYS_FATAL_IF( METHOD_INVOKE_PAYLOAD_SIZE < sizeof(ARGS(method)), \
|
2014-06-10 12:29:14 -07:00
|
|
|
"METHOD_INVOKE_PAYLOAD_SIZE %zu is smaller than sizeof(" #method "Args) %zu", \
|
2014-01-03 18:09:17 -08:00
|
|
|
METHOD_INVOKE_PAYLOAD_SIZE, sizeof(ARGS(method))); \
|
2014-04-07 17:31:44 -07:00
|
|
|
MethodInvokeRenderTask* task = new MethodInvokeRenderTask((RunnableMethod) Bridge_ ## method); \
|
2014-01-03 18:09:17 -08:00
|
|
|
ARGS(method) *args = (ARGS(method) *) task->payload()
|
|
|
|
|
2014-08-14 09:02:01 -07:00
|
|
|
CREATE_BRIDGE4(createContext, RenderThread* thread, bool translucent,
|
|
|
|
RenderNode* rootRenderNode, IContextFactory* contextFactory) {
|
2016-07-07 12:35:54 -04:00
|
|
|
return CanvasContext::create(*args->thread, args->translucent,
|
2014-08-14 09:02:01 -07:00
|
|
|
args->rootRenderNode, args->contextFactory);
|
2014-01-03 18:09:17 -08:00
|
|
|
}
|
|
|
|
|
2014-08-14 09:02:01 -07:00
|
|
|
RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory)
|
2014-01-03 18:09:17 -08:00
|
|
|
: mRenderThread(RenderThread::getInstance())
|
2015-01-05 15:51:13 -08:00
|
|
|
, mContext(nullptr) {
|
2014-01-03 18:09:17 -08:00
|
|
|
SETUP_TASK(createContext);
|
|
|
|
args->translucent = translucent;
|
2014-04-15 09:50:16 -07:00
|
|
|
args->rootRenderNode = rootRenderNode;
|
2014-06-23 13:13:08 -07:00
|
|
|
args->thread = &mRenderThread;
|
2014-08-14 09:02:01 -07:00
|
|
|
args->contextFactory = contextFactory;
|
2014-01-03 18:09:17 -08:00
|
|
|
mContext = (CanvasContext*) postAndWait(task);
|
2015-08-28 07:10:31 -07:00
|
|
|
mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode);
|
2014-01-03 18:09:17 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
RenderProxy::~RenderProxy() {
|
|
|
|
destroyContext();
|
|
|
|
}
|
|
|
|
|
|
|
|
CREATE_BRIDGE1(destroyContext, CanvasContext* context) {
|
|
|
|
delete args->context;
|
2015-01-05 15:51:13 -08:00
|
|
|
return nullptr;
|
2014-01-03 18:09:17 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void RenderProxy::destroyContext() {
|
|
|
|
if (mContext) {
|
|
|
|
SETUP_TASK(destroyContext);
|
|
|
|
args->context = mContext;
|
2015-01-05 15:51:13 -08:00
|
|
|
mContext = nullptr;
|
2015-08-28 07:10:31 -07:00
|
|
|
mDrawFrameTask.setContext(nullptr, nullptr, nullptr);
|
2014-03-26 15:10:40 -07:00
|
|
|
// This is also a fence as we need to be certain that there are no
|
|
|
|
// outstanding mDrawFrame tasks posted before it is destroyed
|
|
|
|
postAndWait(task);
|
2014-01-03 18:09:17 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-23 11:02:19 -07:00
|
|
|
CREATE_BRIDGE2(setSwapBehavior, CanvasContext* context, SwapBehavior swapBehavior) {
|
|
|
|
args->context->setSwapBehavior(args->swapBehavior);
|
2015-01-05 15:51:13 -08:00
|
|
|
return nullptr;
|
2014-10-23 11:02:19 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void RenderProxy::setSwapBehavior(SwapBehavior swapBehavior) {
|
|
|
|
SETUP_TASK(setSwapBehavior);
|
|
|
|
args->context = mContext;
|
|
|
|
args->swapBehavior = swapBehavior;
|
|
|
|
post(task);
|
|
|
|
}
|
|
|
|
|
2014-05-23 17:42:28 -07:00
|
|
|
CREATE_BRIDGE1(loadSystemProperties, CanvasContext* context) {
|
2014-05-05 16:39:37 -07:00
|
|
|
bool needsRedraw = false;
|
|
|
|
if (Caches::hasInstance()) {
|
2015-05-04 14:36:49 -07:00
|
|
|
needsRedraw = Properties::load();
|
2014-05-05 16:39:37 -07:00
|
|
|
}
|
2015-05-04 14:36:49 -07:00
|
|
|
if (args->context->profiler().consumeProperties()) {
|
2014-05-23 17:42:28 -07:00
|
|
|
needsRedraw = true;
|
|
|
|
}
|
2014-05-05 16:39:37 -07:00
|
|
|
return (void*) needsRedraw;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool RenderProxy::loadSystemProperties() {
|
|
|
|
SETUP_TASK(loadSystemProperties);
|
2014-05-23 17:42:28 -07:00
|
|
|
args->context = mContext;
|
2014-05-05 16:39:37 -07:00
|
|
|
return (bool) postAndWait(task);
|
|
|
|
}
|
|
|
|
|
2015-03-11 08:50:53 -07:00
|
|
|
CREATE_BRIDGE2(setName, CanvasContext* context, const char* name) {
|
|
|
|
args->context->setName(std::string(args->name));
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void RenderProxy::setName(const char* name) {
|
|
|
|
SETUP_TASK(setName);
|
|
|
|
args->context = mContext;
|
|
|
|
args->name = name;
|
2015-05-04 14:36:49 -07:00
|
|
|
postAndWait(task); // block since name/value pointers owned by caller
|
2015-03-11 08:50:53 -07:00
|
|
|
}
|
|
|
|
|
2016-02-02 15:18:23 -08:00
|
|
|
CREATE_BRIDGE2(initialize, CanvasContext* context, Surface* surface) {
|
|
|
|
args->context->initialize(args->surface);
|
2015-12-04 12:18:03 +01:00
|
|
|
return nullptr;
|
2014-01-03 18:09:17 -08:00
|
|
|
}
|
|
|
|
|
2016-02-02 15:18:23 -08:00
|
|
|
void RenderProxy::initialize(const sp<Surface>& surface) {
|
2014-01-03 18:09:17 -08:00
|
|
|
SETUP_TASK(initialize);
|
|
|
|
args->context = mContext;
|
2016-02-02 15:18:23 -08:00
|
|
|
args->surface = surface.get();
|
2015-12-04 12:18:03 +01:00
|
|
|
post(task);
|
2014-01-03 18:09:17 -08:00
|
|
|
}
|
|
|
|
|
2016-02-02 15:18:23 -08:00
|
|
|
CREATE_BRIDGE2(updateSurface, CanvasContext* context, Surface* surface) {
|
|
|
|
args->context->updateSurface(args->surface);
|
2015-01-05 15:51:13 -08:00
|
|
|
return nullptr;
|
2014-01-03 18:09:17 -08:00
|
|
|
}
|
|
|
|
|
2016-02-02 15:18:23 -08:00
|
|
|
void RenderProxy::updateSurface(const sp<Surface>& surface) {
|
2014-01-03 18:09:17 -08:00
|
|
|
SETUP_TASK(updateSurface);
|
|
|
|
args->context = mContext;
|
2016-02-02 15:18:23 -08:00
|
|
|
args->surface = surface.get();
|
2016-08-09 12:09:03 -07:00
|
|
|
post(task);
|
2014-04-09 10:01:03 -07:00
|
|
|
}
|
|
|
|
|
2016-02-02 15:18:23 -08:00
|
|
|
CREATE_BRIDGE2(pauseSurface, CanvasContext* context, Surface* surface) {
|
|
|
|
return (void*) args->context->pauseSurface(args->surface);
|
2014-04-09 10:01:03 -07:00
|
|
|
}
|
|
|
|
|
2016-02-02 15:18:23 -08:00
|
|
|
bool RenderProxy::pauseSurface(const sp<Surface>& surface) {
|
2014-04-09 10:01:03 -07:00
|
|
|
SETUP_TASK(pauseSurface);
|
|
|
|
args->context = mContext;
|
2016-02-02 15:18:23 -08:00
|
|
|
args->surface = surface.get();
|
2014-12-03 13:01:07 -08:00
|
|
|
return (bool) postAndWait(task);
|
2014-01-03 18:09:17 -08:00
|
|
|
}
|
|
|
|
|
2016-04-13 10:24:06 -07:00
|
|
|
CREATE_BRIDGE2(setStopped, CanvasContext* context, bool stopped) {
|
|
|
|
args->context->setStopped(args->stopped);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void RenderProxy::setStopped(bool stopped) {
|
|
|
|
SETUP_TASK(setStopped);
|
|
|
|
args->context = mContext;
|
|
|
|
args->stopped = stopped;
|
|
|
|
postAndWait(task);
|
|
|
|
}
|
|
|
|
|
2016-06-21 16:24:20 -07:00
|
|
|
CREATE_BRIDGE4(setup, CanvasContext* context,
|
2015-05-14 18:05:36 -07:00
|
|
|
float lightRadius, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
|
2016-06-21 16:24:20 -07:00
|
|
|
args->context->setup(args->lightRadius,
|
2014-07-23 18:19:28 -07:00
|
|
|
args->ambientShadowAlpha, args->spotShadowAlpha);
|
2015-01-05 15:51:13 -08:00
|
|
|
return nullptr;
|
2014-01-03 18:09:17 -08:00
|
|
|
}
|
|
|
|
|
2016-06-21 16:24:20 -07:00
|
|
|
void RenderProxy::setup(float lightRadius,
|
2015-03-11 08:50:53 -07:00
|
|
|
uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
|
2014-01-03 18:09:17 -08:00
|
|
|
SETUP_TASK(setup);
|
|
|
|
args->context = mContext;
|
2014-05-20 18:10:25 -07:00
|
|
|
args->lightRadius = lightRadius;
|
2014-07-23 18:19:28 -07:00
|
|
|
args->ambientShadowAlpha = ambientShadowAlpha;
|
|
|
|
args->spotShadowAlpha = spotShadowAlpha;
|
2014-01-03 18:09:17 -08:00
|
|
|
post(task);
|
|
|
|
}
|
|
|
|
|
2015-05-14 18:05:36 -07:00
|
|
|
CREATE_BRIDGE2(setLightCenter, CanvasContext* context, Vector3 lightCenter) {
|
|
|
|
args->context->setLightCenter(args->lightCenter);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void RenderProxy::setLightCenter(const Vector3& lightCenter) {
|
|
|
|
SETUP_TASK(setLightCenter);
|
|
|
|
args->context = mContext;
|
|
|
|
args->lightCenter = lightCenter;
|
|
|
|
post(task);
|
|
|
|
}
|
|
|
|
|
2014-05-07 13:45:54 -07:00
|
|
|
CREATE_BRIDGE2(setOpaque, CanvasContext* context, bool opaque) {
|
|
|
|
args->context->setOpaque(args->opaque);
|
2015-01-05 15:51:13 -08:00
|
|
|
return nullptr;
|
2014-05-07 13:45:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void RenderProxy::setOpaque(bool opaque) {
|
|
|
|
SETUP_TASK(setOpaque);
|
|
|
|
args->context = mContext;
|
|
|
|
args->opaque = opaque;
|
|
|
|
post(task);
|
|
|
|
}
|
|
|
|
|
2015-02-19 14:36:50 -08:00
|
|
|
int64_t* RenderProxy::frameInfo() {
|
|
|
|
return mDrawFrameTask.frameInfo();
|
|
|
|
}
|
|
|
|
|
2017-01-25 10:58:30 -08:00
|
|
|
int RenderProxy::syncAndDrawFrame() {
|
|
|
|
return mDrawFrameTask.drawFrame();
|
2014-01-03 18:09:17 -08:00
|
|
|
}
|
|
|
|
|
2017-01-25 10:58:30 -08:00
|
|
|
CREATE_BRIDGE1(destroy, CanvasContext* context) {
|
|
|
|
args->context->destroy();
|
2015-01-05 15:51:13 -08:00
|
|
|
return nullptr;
|
2014-01-03 18:09:17 -08:00
|
|
|
}
|
|
|
|
|
2017-01-25 10:58:30 -08:00
|
|
|
void RenderProxy::destroy() {
|
2014-09-03 07:39:53 -07:00
|
|
|
SETUP_TASK(destroy);
|
2014-01-03 18:09:17 -08:00
|
|
|
args->context = mContext;
|
2014-04-14 11:01:57 -07:00
|
|
|
// destroyCanvasAndSurface() needs a fence as when it returns the
|
|
|
|
// underlying BufferQueue is going to be released from under
|
|
|
|
// the render thread.
|
|
|
|
postAndWait(task);
|
2014-01-03 18:09:17 -08:00
|
|
|
}
|
|
|
|
|
2014-06-23 13:13:08 -07:00
|
|
|
CREATE_BRIDGE2(invokeFunctor, RenderThread* thread, Functor* functor) {
|
|
|
|
CanvasContext::invokeFunctor(*args->thread, args->functor);
|
2015-01-05 15:51:13 -08:00
|
|
|
return nullptr;
|
2014-03-28 20:30:27 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void RenderProxy::invokeFunctor(Functor* functor, bool waitForCompletion) {
|
2014-04-10 15:00:13 -07:00
|
|
|
ATRACE_CALL();
|
2014-06-23 13:13:08 -07:00
|
|
|
RenderThread& thread = RenderThread::getInstance();
|
2014-03-28 20:30:27 -07:00
|
|
|
SETUP_TASK(invokeFunctor);
|
2014-06-23 13:13:08 -07:00
|
|
|
args->thread = &thread;
|
2014-03-28 20:30:27 -07:00
|
|
|
args->functor = functor;
|
|
|
|
if (waitForCompletion) {
|
2014-06-23 13:13:08 -07:00
|
|
|
// waitForCompletion = true is expected to be fairly rare and only
|
|
|
|
// happen in destruction. Thus it should be fine to temporarily
|
|
|
|
// create a Mutex
|
2014-10-31 14:49:06 -07:00
|
|
|
staticPostAndWait(task);
|
2014-03-28 20:30:27 -07:00
|
|
|
} else {
|
2014-06-23 13:13:08 -07:00
|
|
|
thread.queue(task);
|
2014-03-28 20:30:27 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-29 10:09:36 -07:00
|
|
|
CREATE_BRIDGE1(createTextureLayer, CanvasContext* context) {
|
2016-07-22 12:13:32 -04:00
|
|
|
return args->context->createTextureLayer();
|
2014-02-14 20:03:38 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
DeferredLayerUpdater* RenderProxy::createTextureLayer() {
|
|
|
|
SETUP_TASK(createTextureLayer);
|
2014-04-08 15:18:56 -07:00
|
|
|
args->context = mContext;
|
2014-02-14 20:03:38 -08:00
|
|
|
void* retval = postAndWait(task);
|
|
|
|
DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(retval);
|
|
|
|
return layer;
|
|
|
|
}
|
|
|
|
|
2017-01-25 10:58:30 -08:00
|
|
|
CREATE_BRIDGE2(buildLayer, CanvasContext* context, RenderNode* node) {
|
|
|
|
args->context->buildLayer(args->node);
|
2015-01-05 15:51:13 -08:00
|
|
|
return nullptr;
|
2014-08-20 10:08:39 -07:00
|
|
|
}
|
|
|
|
|
2017-01-25 10:58:30 -08:00
|
|
|
void RenderProxy::buildLayer(RenderNode* node) {
|
2014-08-20 10:08:39 -07:00
|
|
|
SETUP_TASK(buildLayer);
|
|
|
|
args->context = mContext;
|
|
|
|
args->node = node;
|
|
|
|
postAndWait(task);
|
|
|
|
}
|
|
|
|
|
2014-02-14 20:03:38 -08:00
|
|
|
CREATE_BRIDGE3(copyLayerInto, CanvasContext* context, DeferredLayerUpdater* layer,
|
|
|
|
SkBitmap* bitmap) {
|
|
|
|
bool success = args->context->copyLayerInto(args->layer, args->bitmap);
|
|
|
|
return (void*) success;
|
|
|
|
}
|
|
|
|
|
2015-04-13 15:20:29 -07:00
|
|
|
bool RenderProxy::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap& bitmap) {
|
2014-02-14 20:03:38 -08:00
|
|
|
SETUP_TASK(copyLayerInto);
|
|
|
|
args->context = mContext;
|
|
|
|
args->layer = layer;
|
2015-04-13 15:20:29 -07:00
|
|
|
args->bitmap = &bitmap;
|
2014-02-14 20:03:38 -08:00
|
|
|
return (bool) postAndWait(task);
|
|
|
|
}
|
|
|
|
|
2014-05-29 18:56:11 -07:00
|
|
|
void RenderProxy::pushLayerUpdate(DeferredLayerUpdater* layer) {
|
|
|
|
mDrawFrameTask.pushLayerUpdate(layer);
|
|
|
|
}
|
|
|
|
|
|
|
|
void RenderProxy::cancelLayerUpdate(DeferredLayerUpdater* layer) {
|
|
|
|
mDrawFrameTask.removeLayerUpdate(layer);
|
2014-06-27 14:45:25 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
CREATE_BRIDGE1(detachSurfaceTexture, DeferredLayerUpdater* layer) {
|
|
|
|
args->layer->detachSurfaceTexture();
|
2015-01-05 15:51:13 -08:00
|
|
|
return nullptr;
|
2014-06-27 14:45:25 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void RenderProxy::detachSurfaceTexture(DeferredLayerUpdater* layer) {
|
|
|
|
SETUP_TASK(detachSurfaceTexture);
|
|
|
|
args->layer = layer;
|
|
|
|
postAndWait(task);
|
2014-02-14 20:03:38 -08:00
|
|
|
}
|
|
|
|
|
2017-01-25 10:58:30 -08:00
|
|
|
CREATE_BRIDGE1(destroyHardwareResources, CanvasContext* context) {
|
|
|
|
args->context->destroyHardwareResources();
|
2015-01-05 15:51:13 -08:00
|
|
|
return nullptr;
|
2014-05-23 15:11:19 -07:00
|
|
|
}
|
|
|
|
|
2017-01-25 10:58:30 -08:00
|
|
|
void RenderProxy::destroyHardwareResources() {
|
2014-06-30 16:20:04 -07:00
|
|
|
SETUP_TASK(destroyHardwareResources);
|
2014-05-23 15:11:19 -07:00
|
|
|
args->context = mContext;
|
2016-04-06 07:50:47 -07:00
|
|
|
postAndWait(task);
|
2014-05-23 15:11:19 -07:00
|
|
|
}
|
|
|
|
|
2015-05-04 14:36:49 -07:00
|
|
|
CREATE_BRIDGE2(trimMemory, RenderThread* thread, int level) {
|
2014-06-30 16:20:04 -07:00
|
|
|
CanvasContext::trimMemory(*args->thread, args->level);
|
2015-01-05 15:51:13 -08:00
|
|
|
return nullptr;
|
2014-06-30 16:20:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void RenderProxy::trimMemory(int level) {
|
2014-08-06 13:33:59 -07:00
|
|
|
// Avoid creating a RenderThread to do a trimMemory.
|
|
|
|
if (RenderThread::hasInstance()) {
|
|
|
|
RenderThread& thread = RenderThread::getInstance();
|
2015-05-04 14:36:49 -07:00
|
|
|
SETUP_TASK(trimMemory);
|
2014-08-06 13:33:59 -07:00
|
|
|
args->thread = &thread;
|
|
|
|
args->level = level;
|
|
|
|
thread.queue(task);
|
|
|
|
}
|
2014-06-30 16:20:04 -07:00
|
|
|
}
|
|
|
|
|
2015-05-04 14:36:49 -07:00
|
|
|
CREATE_BRIDGE2(overrideProperty, const char* name, const char* value) {
|
|
|
|
Properties::overrideProperty(args->name, args->value);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void RenderProxy::overrideProperty(const char* name, const char* value) {
|
|
|
|
SETUP_TASK(overrideProperty);
|
|
|
|
args->name = name;
|
|
|
|
args->value = value;
|
|
|
|
staticPostAndWait(task); // expensive, but block here since name/value pointers owned by caller
|
|
|
|
}
|
|
|
|
|
2014-04-07 16:59:25 -07:00
|
|
|
CREATE_BRIDGE0(fence) {
|
|
|
|
// Intentionally empty
|
2015-01-05 15:51:13 -08:00
|
|
|
return nullptr;
|
2014-04-07 16:59:25 -07:00
|
|
|
}
|
|
|
|
|
2014-11-22 00:35:09 +00:00
|
|
|
template <typename T>
|
|
|
|
void UNUSED(T t) {}
|
|
|
|
|
2014-04-07 16:59:25 -07:00
|
|
|
void RenderProxy::fence() {
|
|
|
|
SETUP_TASK(fence);
|
2014-11-10 15:23:43 -08:00
|
|
|
UNUSED(args);
|
2014-04-07 16:59:25 -07:00
|
|
|
postAndWait(task);
|
|
|
|
}
|
|
|
|
|
2016-01-18 10:31:58 +01:00
|
|
|
void RenderProxy::staticFence() {
|
|
|
|
SETUP_TASK(fence);
|
|
|
|
UNUSED(args);
|
|
|
|
staticPostAndWait(task);
|
|
|
|
}
|
|
|
|
|
2014-06-30 16:20:04 -07:00
|
|
|
CREATE_BRIDGE1(stopDrawing, CanvasContext* context) {
|
|
|
|
args->context->stopDrawing();
|
2015-01-05 15:51:13 -08:00
|
|
|
return nullptr;
|
2014-06-30 16:20:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void RenderProxy::stopDrawing() {
|
|
|
|
SETUP_TASK(stopDrawing);
|
|
|
|
args->context = mContext;
|
|
|
|
postAndWait(task);
|
|
|
|
}
|
|
|
|
|
2014-05-22 15:43:54 -07:00
|
|
|
CREATE_BRIDGE1(notifyFramePending, CanvasContext* context) {
|
|
|
|
args->context->notifyFramePending();
|
2015-01-05 15:51:13 -08:00
|
|
|
return nullptr;
|
2014-05-22 15:43:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void RenderProxy::notifyFramePending() {
|
|
|
|
SETUP_TASK(notifyFramePending);
|
|
|
|
args->context = mContext;
|
|
|
|
mRenderThread.queueAtFront(task);
|
|
|
|
}
|
|
|
|
|
2015-05-05 11:00:53 -07:00
|
|
|
CREATE_BRIDGE4(dumpProfileInfo, CanvasContext* context, RenderThread* thread,
|
|
|
|
int fd, int dumpFlags) {
|
2014-05-23 17:42:28 -07:00
|
|
|
args->context->profiler().dumpData(args->fd);
|
2015-06-01 10:35:35 -07:00
|
|
|
if (args->dumpFlags & DumpFlags::FrameStats) {
|
2015-02-19 14:36:50 -08:00
|
|
|
args->context->dumpFrames(args->fd);
|
|
|
|
}
|
2015-06-01 10:35:35 -07:00
|
|
|
if (args->dumpFlags & DumpFlags::Reset) {
|
2015-02-19 14:36:50 -08:00
|
|
|
args->context->resetFrameStats();
|
|
|
|
}
|
2016-04-07 16:36:57 -07:00
|
|
|
if (args->dumpFlags & DumpFlags::JankStats) {
|
|
|
|
args->thread->jankTracker().dump(args->fd);
|
|
|
|
}
|
2015-01-05 15:51:13 -08:00
|
|
|
return nullptr;
|
2014-05-23 17:42:28 -07:00
|
|
|
}
|
|
|
|
|
2015-02-19 14:36:50 -08:00
|
|
|
void RenderProxy::dumpProfileInfo(int fd, int dumpFlags) {
|
2014-05-23 17:42:28 -07:00
|
|
|
SETUP_TASK(dumpProfileInfo);
|
|
|
|
args->context = mContext;
|
2015-05-05 11:00:53 -07:00
|
|
|
args->thread = &mRenderThread;
|
2014-05-23 17:42:28 -07:00
|
|
|
args->fd = fd;
|
2015-02-19 14:36:50 -08:00
|
|
|
args->dumpFlags = dumpFlags;
|
2014-05-23 17:42:28 -07:00
|
|
|
postAndWait(task);
|
|
|
|
}
|
|
|
|
|
2015-05-05 11:00:53 -07:00
|
|
|
CREATE_BRIDGE1(resetProfileInfo, CanvasContext* context) {
|
|
|
|
args->context->resetFrameStats();
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void RenderProxy::resetProfileInfo() {
|
|
|
|
SETUP_TASK(resetProfileInfo);
|
|
|
|
args->context = mContext;
|
|
|
|
postAndWait(task);
|
|
|
|
}
|
|
|
|
|
2016-07-03 18:28:25 -07:00
|
|
|
CREATE_BRIDGE2(frameTimePercentile, RenderThread* thread, int percentile) {
|
|
|
|
return reinterpret_cast<void*>(static_cast<uintptr_t>(
|
|
|
|
args->thread->jankTracker().findPercentile(args->percentile)));
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t RenderProxy::frameTimePercentile(int p) {
|
|
|
|
SETUP_TASK(frameTimePercentile);
|
|
|
|
args->thread = &mRenderThread;
|
|
|
|
args->percentile = p;
|
|
|
|
return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(
|
|
|
|
postAndWait(task)));
|
|
|
|
}
|
|
|
|
|
2015-02-19 14:36:50 -08:00
|
|
|
CREATE_BRIDGE2(dumpGraphicsMemory, int fd, RenderThread* thread) {
|
|
|
|
args->thread->jankTracker().dump(args->fd);
|
|
|
|
|
2015-01-21 14:22:39 -08:00
|
|
|
FILE *file = fdopen(args->fd, "a");
|
|
|
|
if (Caches::hasInstance()) {
|
|
|
|
String8 cachesLog;
|
|
|
|
Caches::getInstance().dumpMemoryUsage(cachesLog);
|
|
|
|
fprintf(file, "\nCaches:\n%s\n", cachesLog.string());
|
|
|
|
} else {
|
|
|
|
fprintf(file, "\nNo caches instance.\n");
|
|
|
|
}
|
2016-01-14 10:04:08 -08:00
|
|
|
fprintf(file, "\nPipeline=FrameBuilder\n");
|
2015-01-21 14:22:39 -08:00
|
|
|
fflush(file);
|
2015-01-05 15:51:13 -08:00
|
|
|
return nullptr;
|
2014-10-31 14:49:06 -07:00
|
|
|
}
|
|
|
|
|
2015-01-21 14:22:39 -08:00
|
|
|
void RenderProxy::dumpGraphicsMemory(int fd) {
|
Don't create unnecessary RenderThread's instance when executing 'dumpsys gfxinfo'
To obtain the gfxinfo for each process, the static method of RenderProxy is used, which is named outputLogBuffer().
In there,
1. RenderTask is created for getting DisplayList Commands in RenderNode.
2. staticPostAndWait() is called
3. RenderThread's instance is created by 'RenderThread::getInstance()' in staticPostAndWait()
In case of the service, they don't use HW Acceleration, so don't need RenderThread.
But, by the process of No.3, RenderThread is created for all process.
As we know, RenderThread never be destroyed while the process is alive.
This patch checks RenderThread instance before the creation of RenderTask.
And, there is no one, just return to prevent the unnecessay creation of it.
Change-Id: I4fe29d83c9ced3e8b67177c0874c5d8ee62e1870
2015-03-20 21:22:32 +09:00
|
|
|
if (!RenderThread::hasInstance()) return;
|
2015-01-21 14:22:39 -08:00
|
|
|
SETUP_TASK(dumpGraphicsMemory);
|
2014-10-31 14:49:06 -07:00
|
|
|
args->fd = fd;
|
2015-02-19 14:36:50 -08:00
|
|
|
args->thread = &RenderThread::getInstance();
|
2014-10-31 14:49:06 -07:00
|
|
|
staticPostAndWait(task);
|
|
|
|
}
|
|
|
|
|
2015-03-18 15:24:33 -07:00
|
|
|
CREATE_BRIDGE2(setProcessStatsBuffer, RenderThread* thread, int fd) {
|
|
|
|
args->thread->jankTracker().switchStorageToAshmem(args->fd);
|
|
|
|
close(args->fd);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void RenderProxy::setProcessStatsBuffer(int fd) {
|
|
|
|
SETUP_TASK(setProcessStatsBuffer);
|
2017-01-19 15:56:21 -08:00
|
|
|
auto& rt = RenderThread::getInstance();
|
|
|
|
args->thread = &rt;
|
2015-03-18 15:24:33 -07:00
|
|
|
args->fd = dup(fd);
|
2017-01-19 15:56:21 -08:00
|
|
|
rt.queue(task);
|
|
|
|
}
|
|
|
|
|
|
|
|
CREATE_BRIDGE1(rotateProcessStatsBuffer, RenderThread* thread) {
|
|
|
|
args->thread->jankTracker().rotateStorage();
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void RenderProxy::rotateProcessStatsBuffer() {
|
|
|
|
SETUP_TASK(rotateProcessStatsBuffer);
|
|
|
|
auto& rt = RenderThread::getInstance();
|
|
|
|
args->thread = &rt;
|
|
|
|
rt.queue(task);
|
2015-03-18 15:24:33 -07:00
|
|
|
}
|
|
|
|
|
2016-06-10 10:03:20 -07:00
|
|
|
int RenderProxy::getRenderThreadTid() {
|
|
|
|
return mRenderThread.getTid();
|
|
|
|
}
|
|
|
|
|
2015-08-28 07:10:31 -07:00
|
|
|
CREATE_BRIDGE3(addRenderNode, CanvasContext* context, RenderNode* node, bool placeFront) {
|
|
|
|
args->context->addRenderNode(args->node, args->placeFront);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void RenderProxy::addRenderNode(RenderNode* node, bool placeFront) {
|
|
|
|
SETUP_TASK(addRenderNode);
|
|
|
|
args->context = mContext;
|
|
|
|
args->node = node;
|
|
|
|
args->placeFront = placeFront;
|
|
|
|
post(task);
|
|
|
|
}
|
|
|
|
|
|
|
|
CREATE_BRIDGE2(removeRenderNode, CanvasContext* context, RenderNode* node) {
|
|
|
|
args->context->removeRenderNode(args->node);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void RenderProxy::removeRenderNode(RenderNode* node) {
|
|
|
|
SETUP_TASK(removeRenderNode);
|
|
|
|
args->context = mContext;
|
|
|
|
args->node = node;
|
|
|
|
post(task);
|
|
|
|
}
|
|
|
|
|
|
|
|
CREATE_BRIDGE2(drawRenderNode, CanvasContext* context, RenderNode* node) {
|
|
|
|
args->context->prepareAndDraw(args->node);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void RenderProxy::drawRenderNode(RenderNode* node) {
|
|
|
|
SETUP_TASK(drawRenderNode);
|
|
|
|
args->context = mContext;
|
|
|
|
args->node = node;
|
|
|
|
// Be pseudo-thread-safe and don't use any member variables
|
|
|
|
staticPostAndWait(task);
|
|
|
|
}
|
|
|
|
|
2015-09-22 09:51:39 -07:00
|
|
|
CREATE_BRIDGE5(setContentDrawBounds, CanvasContext* context, int left, int top,
|
2015-08-28 07:10:31 -07:00
|
|
|
int right, int bottom) {
|
2015-09-22 09:51:39 -07:00
|
|
|
args->context->setContentDrawBounds(args->left, args->top, args->right, args->bottom);
|
2015-08-28 07:10:31 -07:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2015-09-22 09:51:39 -07:00
|
|
|
void RenderProxy::setContentDrawBounds(int left, int top, int right, int bottom) {
|
|
|
|
SETUP_TASK(setContentDrawBounds);
|
2015-08-28 07:10:31 -07:00
|
|
|
args->context = mContext;
|
|
|
|
args->left = left;
|
|
|
|
args->top = top;
|
|
|
|
args->right = right;
|
|
|
|
args->bottom = bottom;
|
|
|
|
staticPostAndWait(task);
|
|
|
|
}
|
|
|
|
|
2015-08-05 13:53:53 -07:00
|
|
|
CREATE_BRIDGE1(serializeDisplayListTree, CanvasContext* context) {
|
|
|
|
args->context->serializeDisplayListTree();
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void RenderProxy::serializeDisplayListTree() {
|
|
|
|
SETUP_TASK(serializeDisplayListTree);
|
|
|
|
args->context = mContext;
|
|
|
|
post(task);
|
|
|
|
}
|
|
|
|
|
2016-02-02 16:19:40 -08:00
|
|
|
CREATE_BRIDGE2(addFrameMetricsObserver, CanvasContext* context,
|
|
|
|
FrameMetricsObserver* frameStatsObserver) {
|
|
|
|
args->context->addFrameMetricsObserver(args->frameStatsObserver);
|
2015-12-15 15:21:31 -08:00
|
|
|
if (args->frameStatsObserver != nullptr) {
|
|
|
|
args->frameStatsObserver->decStrong(args->context);
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2016-02-02 16:19:40 -08:00
|
|
|
void RenderProxy::addFrameMetricsObserver(FrameMetricsObserver* observer) {
|
|
|
|
SETUP_TASK(addFrameMetricsObserver);
|
2015-12-15 15:21:31 -08:00
|
|
|
args->context = mContext;
|
|
|
|
args->frameStatsObserver = observer;
|
|
|
|
if (observer != nullptr) {
|
|
|
|
observer->incStrong(mContext);
|
|
|
|
}
|
|
|
|
post(task);
|
|
|
|
}
|
|
|
|
|
2016-02-02 16:19:40 -08:00
|
|
|
CREATE_BRIDGE2(removeFrameMetricsObserver, CanvasContext* context,
|
|
|
|
FrameMetricsObserver* frameStatsObserver) {
|
|
|
|
args->context->removeFrameMetricsObserver(args->frameStatsObserver);
|
2015-12-15 15:21:31 -08:00
|
|
|
if (args->frameStatsObserver != nullptr) {
|
|
|
|
args->frameStatsObserver->decStrong(args->context);
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2016-02-02 16:19:40 -08:00
|
|
|
void RenderProxy::removeFrameMetricsObserver(FrameMetricsObserver* observer) {
|
|
|
|
SETUP_TASK(removeFrameMetricsObserver);
|
2015-12-15 15:21:31 -08:00
|
|
|
args->context = mContext;
|
|
|
|
args->frameStatsObserver = observer;
|
|
|
|
if (observer != nullptr) {
|
|
|
|
observer->incStrong(mContext);
|
|
|
|
}
|
|
|
|
post(task);
|
|
|
|
}
|
|
|
|
|
2016-09-01 09:44:09 -07:00
|
|
|
CREATE_BRIDGE4(copySurfaceInto, RenderThread* thread,
|
|
|
|
Surface* surface, Rect srcRect, SkBitmap* bitmap) {
|
2016-11-07 16:05:41 -05:00
|
|
|
return (void*)args->thread->readback().copySurfaceInto(*args->surface,
|
|
|
|
args->srcRect, args->bitmap);
|
2016-03-31 16:36:16 -07:00
|
|
|
}
|
|
|
|
|
2016-09-01 09:44:09 -07:00
|
|
|
int RenderProxy::copySurfaceInto(sp<Surface>& surface, int left, int top,
|
|
|
|
int right, int bottom, SkBitmap* bitmap) {
|
2016-03-31 16:36:16 -07:00
|
|
|
SETUP_TASK(copySurfaceInto);
|
|
|
|
args->bitmap = bitmap;
|
|
|
|
args->surface = surface.get();
|
|
|
|
args->thread = &RenderThread::getInstance();
|
2016-09-01 09:44:09 -07:00
|
|
|
args->srcRect.set(left, top, right, bottom);
|
2016-04-25 13:03:44 -07:00
|
|
|
return static_cast<int>(
|
|
|
|
reinterpret_cast<intptr_t>( staticPostAndWait(task) ));
|
2016-03-31 16:36:16 -07:00
|
|
|
}
|
|
|
|
|
2016-10-20 18:39:04 -07:00
|
|
|
CREATE_BRIDGE2(prepareToDraw, RenderThread* thread, Bitmap* bitmap) {
|
2016-10-25 12:09:18 -04:00
|
|
|
CanvasContext::prepareToDraw(*args->thread, args->bitmap);
|
2016-10-20 18:39:04 -07:00
|
|
|
args->bitmap->unref();
|
2016-08-01 14:39:24 -07:00
|
|
|
args->bitmap = nullptr;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2016-10-20 18:39:04 -07:00
|
|
|
void RenderProxy::prepareToDraw(Bitmap& bitmap) {
|
2016-08-01 14:39:24 -07:00
|
|
|
// If we haven't spun up a hardware accelerated window yet, there's no
|
|
|
|
// point in precaching these bitmaps as it can't impact jank.
|
|
|
|
// We also don't know if we even will spin up a hardware-accelerated
|
|
|
|
// window or not.
|
|
|
|
if (!RenderThread::hasInstance()) return;
|
|
|
|
RenderThread* renderThread = &RenderThread::getInstance();
|
|
|
|
SETUP_TASK(prepareToDraw);
|
|
|
|
args->thread = renderThread;
|
2016-10-20 18:39:04 -07:00
|
|
|
bitmap.ref();
|
|
|
|
args->bitmap = &bitmap;
|
2016-08-01 14:39:24 -07:00
|
|
|
nsecs_t lastVsync = renderThread->timeLord().latestVsync();
|
|
|
|
nsecs_t estimatedNextVsync = lastVsync + renderThread->timeLord().frameIntervalNanos();
|
|
|
|
nsecs_t timeToNextVsync = estimatedNextVsync - systemTime(CLOCK_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
|
|
|
|
// TODO: Make this concept a first-class supported thing? RT could use
|
|
|
|
// knowledge of pending draws to better schedule this task
|
|
|
|
if (timeToNextVsync > -6_ms && timeToNextVsync < 1_ms) {
|
|
|
|
renderThread->queueAt(task, estimatedNextVsync + 8_ms);
|
|
|
|
} else {
|
|
|
|
renderThread->queue(task);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-27 10:23:13 -07:00
|
|
|
CREATE_BRIDGE2(allocateHardwareBitmap, RenderThread* thread, SkBitmap* bitmap) {
|
|
|
|
sk_sp<Bitmap> hardwareBitmap = Bitmap::allocateHardwareBitmap(*args->thread, *args->bitmap);
|
|
|
|
return hardwareBitmap.release();
|
|
|
|
}
|
|
|
|
|
|
|
|
sk_sp<Bitmap> RenderProxy::allocateHardwareBitmap(SkBitmap& bitmap) {
|
|
|
|
SETUP_TASK(allocateHardwareBitmap);
|
|
|
|
args->bitmap = &bitmap;
|
|
|
|
args->thread = &RenderThread::getInstance();
|
|
|
|
sk_sp<Bitmap> hardwareBitmap(reinterpret_cast<Bitmap*>(staticPostAndWait(task)));
|
|
|
|
return hardwareBitmap;
|
|
|
|
}
|
|
|
|
|
2016-11-17 17:54:57 -08:00
|
|
|
CREATE_BRIDGE3(copyGraphicBufferInto, RenderThread* thread, GraphicBuffer* buffer, SkBitmap* bitmap) {
|
|
|
|
return (void*) args->thread->readback().copyGraphicBufferInto(args->buffer, args->bitmap);
|
|
|
|
}
|
|
|
|
|
|
|
|
int RenderProxy::copyGraphicBufferInto(GraphicBuffer* buffer, SkBitmap* bitmap) {
|
2017-02-02 14:11:53 -05:00
|
|
|
RenderThread& thread = RenderThread::getInstance();
|
|
|
|
if (Properties::isSkiaEnabled() && gettid() == thread.getTid()) {
|
|
|
|
//TODO: fix everything that hits this. We should never be triggering a readback ourselves.
|
|
|
|
return (int) thread.readback().copyGraphicBufferInto(buffer, bitmap);
|
|
|
|
} else {
|
|
|
|
SETUP_TASK(copyGraphicBufferInto);
|
|
|
|
args->thread = &thread;
|
|
|
|
args->bitmap = bitmap;
|
|
|
|
args->buffer = buffer;
|
|
|
|
return static_cast<int>(reinterpret_cast<intptr_t>(staticPostAndWait(task)));
|
|
|
|
}
|
2016-11-17 17:54:57 -08:00
|
|
|
}
|
|
|
|
|
2014-01-03 18:09:17 -08:00
|
|
|
void RenderProxy::post(RenderTask* task) {
|
|
|
|
mRenderThread.queue(task);
|
|
|
|
}
|
|
|
|
|
|
|
|
void* RenderProxy::postAndWait(MethodInvokeRenderTask* task) {
|
|
|
|
void* retval;
|
|
|
|
task->setReturnPtr(&retval);
|
2015-11-10 12:52:44 -08:00
|
|
|
SignalingRenderTask syncTask(task, &mSyncMutex, &mSyncCondition);
|
|
|
|
AutoMutex _lock(mSyncMutex);
|
|
|
|
mRenderThread.queue(&syncTask);
|
2017-02-28 14:07:09 -08:00
|
|
|
while (!syncTask.hasRun()) {
|
|
|
|
mSyncCondition.wait(mSyncMutex);
|
|
|
|
}
|
2014-01-03 18:09:17 -08:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2014-10-31 14:49:06 -07:00
|
|
|
void* RenderProxy::staticPostAndWait(MethodInvokeRenderTask* task) {
|
|
|
|
RenderThread& thread = RenderThread::getInstance();
|
2017-02-02 14:11:53 -05:00
|
|
|
LOG_ALWAYS_FATAL_IF(gettid() == thread.getTid());
|
2014-10-31 14:49:06 -07:00
|
|
|
void* retval;
|
|
|
|
task->setReturnPtr(&retval);
|
2015-10-19 17:10:19 -07:00
|
|
|
thread.queueAndWait(task);
|
2014-10-31 14:49:06 -07:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2014-01-03 18:09:17 -08:00
|
|
|
} /* namespace renderthread */
|
|
|
|
} /* namespace uirenderer */
|
|
|
|
} /* namespace android */
|