blob: 2b86b1eb754207568d23e9891b35268e0fce0c69 [file] [log] [blame]
/*
* 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 "DeferredLayerUpdater.h"
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
// TODO: Use public SurfaceTexture APIs once available and include public NDK header file instead.
#include <surfacetexture/surface_texture_platform.h>
#include "AutoBackendTextureRelease.h"
#include "Matrix.h"
#include "Properties.h"
#include "renderstate/RenderState.h"
#include "renderthread/EglManager.h"
#include "renderthread/RenderThread.h"
#include "renderthread/VulkanManager.h"
using namespace android::uirenderer::renderthread;
namespace android {
namespace uirenderer {
DeferredLayerUpdater::DeferredLayerUpdater(RenderState& renderState)
: mRenderState(renderState)
, mBlend(false)
, mSurfaceTexture(nullptr, [](ASurfaceTexture*) {})
, mTransform(nullptr)
, mGLContextAttached(false)
, mUpdateTexImage(false)
, mLayer(nullptr) {
renderState.registerContextCallback(this);
}
DeferredLayerUpdater::~DeferredLayerUpdater() {
setTransform(nullptr);
mRenderState.removeContextCallback(this);
destroyLayer();
}
void DeferredLayerUpdater::setSurfaceTexture(AutoTextureRelease&& consumer) {
mSurfaceTexture = std::move(consumer);
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() {
destroyLayer();
}
void DeferredLayerUpdater::destroyLayer() {
if (!mLayer) {
return;
}
if (mSurfaceTexture.get() && mGLContextAttached) {
ASurfaceTexture_releaseConsumerOwnership(mSurfaceTexture.get());
mGLContextAttached = false;
}
mLayer->postDecStrong();
mLayer = nullptr;
for (auto& [index, slot] : mImageSlots) {
slot.clear(mRenderState.getRenderThread().getGrContext());
}
mImageSlots.clear();
}
void DeferredLayerUpdater::setPaint(const SkPaint* paint) {
mAlpha = PaintUtils::getAlphaDirect(paint);
mMode = PaintUtils::getBlendModeDirect(paint);
if (paint) {
mColorFilter = paint->refColorFilter();
} else {
mColorFilter.reset();
}
}
status_t DeferredLayerUpdater::createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence,
EGLDisplay* display, int* releaseFence,
void* handle) {
*display = EGL_NO_DISPLAY;
DeferredLayerUpdater* dlu = (DeferredLayerUpdater*)handle;
RenderState& renderState = dlu->mRenderState;
status_t err;
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
EglManager& eglManager = renderState.getRenderThread().eglManager();
*display = eglManager.eglDisplay();
err = eglManager.createReleaseFence(useFenceSync, eglFence, releaseFence);
} else {
int previousSlot = dlu->mCurrentSlot;
if (previousSlot != -1) {
dlu->mImageSlots[previousSlot].releaseQueueOwnership(
renderState.getRenderThread().getGrContext());
}
err = renderState.getRenderThread().vulkanManager().createReleaseFence(
releaseFence, renderState.getRenderThread().getGrContext());
}
return err;
}
status_t DeferredLayerUpdater::fenceWait(int fence, void* handle) {
// Wait on the producer fence for the buffer to be ready.
status_t err;
DeferredLayerUpdater* dlu = (DeferredLayerUpdater*)handle;
RenderState& renderState = dlu->mRenderState;
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);
}
mLayer->setColorFilter(mColorFilter);
mLayer->setAlpha(mAlpha, mMode);
if (mSurfaceTexture.get()) {
if (!mGLContextAttached) {
mGLContextAttached = true;
mUpdateTexImage = true;
ASurfaceTexture_takeConsumerOwnership(mSurfaceTexture.get());
}
if (mUpdateTexImage) {
mUpdateTexImage = false;
float transformMatrix[16];
android_dataspace dataspace;
int slot;
bool newContent = false;
ARect currentCrop;
uint32_t outTransform;
// 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, &outTransform,
&newContent, createReleaseFence, fenceWait, this, &currentCrop);
if (hardwareBuffer) {
mCurrentSlot = slot;
sk_sp<SkImage> layerImage = mImageSlots[slot].createIfNeeded(
hardwareBuffer, dataspace, newContent,
mRenderState.getRenderThread().getGrContext());
// unref to match the ref added by ASurfaceTexture_dequeueBuffer. eglCreateImageKHR
// (invoked by createIfNeeded) will add a ref to the AHardwareBuffer.
AHardwareBuffer_release(hardwareBuffer);
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);
}
}
}
if (mTransform) {
mLayer->getTransform() = *mTransform;
setTransform(nullptr);
}
}
}
void DeferredLayerUpdater::updateLayer(bool forceFilter, const SkMatrix& textureTransform,
const sk_sp<SkImage>& layerImage) {
mLayer->setBlend(mBlend);
mLayer->setForceFilter(forceFilter);
mLayer->setSize(mWidth, mHeight);
mLayer->getTexTransform() = textureTransform;
mLayer->setImage(layerImage);
}
void DeferredLayerUpdater::detachSurfaceTexture() {
if (mSurfaceTexture.get()) {
destroyLayer();
mSurfaceTexture = nullptr;
}
}
sk_sp<SkImage> DeferredLayerUpdater::ImageSlot::createIfNeeded(AHardwareBuffer* buffer,
android_dataspace dataspace,
bool forceCreate,
GrDirectContext* context) {
if (!mTextureRelease || !mTextureRelease->getImage().get() || dataspace != mDataspace ||
forceCreate || mBuffer != buffer) {
if (buffer != mBuffer) {
clear(context);
}
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(GrDirectContext* context) {
if (mTextureRelease) {
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
this->releaseQueueOwnership(context);
}
// The following unref counteracts the initial mUsageCount of 1, set by default initializer.
mTextureRelease->unref(true);
mTextureRelease = nullptr;
}
mBuffer = nullptr;
}
void DeferredLayerUpdater::ImageSlot::releaseQueueOwnership(GrDirectContext* context) {
LOG_ALWAYS_FATAL_IF(Properties::getRenderPipelineType() != RenderPipelineType::SkiaVulkan);
if (mTextureRelease) {
mTextureRelease->releaseQueueOwnership(context);
}
}
} /* namespace uirenderer */
} /* namespace android */