This reuses the HDR tonemapping curve that was used in RenderEngine, however display-level metadata may not be aligned. But because there are no composition changes that can cause flicker, e.g., switching rapidly between using a TextureView and a SurfaceView, then that should be okay. That means that the HDR tonemapping is not as high quality as it could be, but it is much better than before. Bug: 200309590 Test: builds, boots Test: Instagram video preview Change-Id: I4dd042333f383f383d568b6f2326ee14facd08ed
273 lines
10 KiB
C++
273 lines
10 KiB
C++
/*
|
|
* 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 "android/hdr_metadata.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;
|
|
AHdrMetadataType hdrMetadataType;
|
|
android_cta861_3_metadata cta861_3;
|
|
android_smpte2086_metadata smpte2086;
|
|
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, &hdrMetadataType, &cta861_3,
|
|
&smpte2086, transformMatrix, &outTransform, &newContent, createReleaseFence,
|
|
fenceWait, this, ¤tCrop);
|
|
|
|
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()) {
|
|
// force filtration if buffer size != layer size
|
|
bool forceFilter =
|
|
mWidth != layerImage->width() || mHeight != layerImage->height();
|
|
SkRect currentCropRect =
|
|
SkRect::MakeLTRB(currentCrop.left, currentCrop.top, currentCrop.right,
|
|
currentCrop.bottom);
|
|
|
|
float maxLuminanceNits = -1.f;
|
|
if (hdrMetadataType & HDR10_SMPTE2086) {
|
|
maxLuminanceNits = std::max(smpte2086.maxLuminance, maxLuminanceNits);
|
|
}
|
|
|
|
if (hdrMetadataType & HDR10_CTA861_3) {
|
|
maxLuminanceNits =
|
|
std::max(cta861_3.maxContentLightLevel, maxLuminanceNits);
|
|
}
|
|
updateLayer(forceFilter, layerImage, outTransform, currentCropRect,
|
|
maxLuminanceNits);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mTransform) {
|
|
mLayer->getTransform() = *mTransform;
|
|
setTransform(nullptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DeferredLayerUpdater::updateLayer(bool forceFilter, const sk_sp<SkImage>& layerImage,
|
|
const uint32_t transform, SkRect currentCrop,
|
|
float maxLuminanceNits) {
|
|
mLayer->setBlend(mBlend);
|
|
mLayer->setForceFilter(forceFilter);
|
|
mLayer->setSize(mWidth, mHeight);
|
|
mLayer->setCurrentCropRect(currentCrop);
|
|
mLayer->setWindowTransform(transform);
|
|
mLayer->setImage(layerImage);
|
|
mLayer->setMaxLuminanceNits(maxLuminanceNits);
|
|
}
|
|
|
|
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 */
|