Merge "Implement Skia pipelines for OpenGL and Vulkan."
This commit is contained in:
committed by
Android (Google) Code Review
commit
40800725d2
@@ -23,7 +23,10 @@ hwui_src_files := \
|
||||
pipeline/skia/RenderNodeDrawable.cpp \
|
||||
pipeline/skia/ReorderBarrierDrawables.cpp \
|
||||
pipeline/skia/SkiaDisplayList.cpp \
|
||||
pipeline/skia/SkiaOpenGLPipeline.cpp \
|
||||
pipeline/skia/SkiaPipeline.cpp \
|
||||
pipeline/skia/SkiaRecordingCanvas.cpp \
|
||||
pipeline/skia/SkiaVulkanPipeline.cpp \
|
||||
renderstate/Blend.cpp \
|
||||
renderstate/MeshState.cpp \
|
||||
renderstate/OffscreenBufferPool.cpp \
|
||||
@@ -296,6 +299,7 @@ LOCAL_SRC_FILES += \
|
||||
tests/unit/RenderPropertiesTests.cpp \
|
||||
tests/unit/SkiaBehaviorTests.cpp \
|
||||
tests/unit/SkiaDisplayListTests.cpp \
|
||||
tests/unit/SkiaPipelineTests.cpp \
|
||||
tests/unit/SkiaCanvasTests.cpp \
|
||||
tests/unit/SnapshotTests.cpp \
|
||||
tests/unit/StringUtilsTests.cpp \
|
||||
|
||||
@@ -41,6 +41,13 @@ void DeviceInfo::initialize() {
|
||||
});
|
||||
}
|
||||
|
||||
void DeviceInfo::initialize(int maxTextureSize) {
|
||||
std::call_once(sInitializedFlag, [maxTextureSize]() {
|
||||
sDeviceInfo = new DeviceInfo();
|
||||
sDeviceInfo->mMaxTextureSize = maxTextureSize;
|
||||
});
|
||||
}
|
||||
|
||||
void DeviceInfo::load() {
|
||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ public:
|
||||
// only call this after GL has been initialized, or at any point if compiled
|
||||
// with HWUI_NULL_GPU
|
||||
static void initialize();
|
||||
static void initialize(int maxTextureSize);
|
||||
|
||||
int maxTextureSize() const { return mMaxTextureSize; }
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "Matrix.h"
|
||||
#include "RenderProperties.h"
|
||||
#include "pipeline/skia/SkiaDisplayList.h"
|
||||
#include "pipeline/skia/SkiaLayer.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
@@ -60,10 +61,6 @@ namespace proto {
|
||||
class RenderNode;
|
||||
}
|
||||
|
||||
namespace skiapipeline {
|
||||
class SkiaDisplayList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Primary class for storing recorded canvas commands, as well as per-View/ViewGroup display properties.
|
||||
*
|
||||
@@ -312,14 +309,23 @@ public:
|
||||
* Returns true if an offscreen layer from any renderPipeline is attached
|
||||
* to this node.
|
||||
*/
|
||||
bool hasLayer() const { return mLayer || mLayerSurface.get(); }
|
||||
bool hasLayer() const { return mLayer || mSkiaLayer.get(); }
|
||||
|
||||
/**
|
||||
* Used by the RenderPipeline to attach an offscreen surface to the RenderNode.
|
||||
* The surface is then will be used to store the contents of a layer.
|
||||
*/
|
||||
void setLayerSurface(sk_sp<SkSurface> layer) { mLayerSurface = layer; }
|
||||
|
||||
void setLayerSurface(sk_sp<SkSurface> layer) {
|
||||
if (layer.get()) {
|
||||
if (!mSkiaLayer.get()) {
|
||||
mSkiaLayer = std::make_unique<skiapipeline::SkiaLayer>();
|
||||
}
|
||||
mSkiaLayer->layerSurface = std::move(layer);
|
||||
mSkiaLayer->inverseTransformInWindow.loadIdentity();
|
||||
} else {
|
||||
mSkiaLayer.reset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the RenderNode is of type LayerType::RenderLayer then this method will
|
||||
@@ -330,7 +336,13 @@ public:
|
||||
* NOTE: this function is only guaranteed to return accurate results after
|
||||
* prepareTree has been run for this RenderNode
|
||||
*/
|
||||
SkSurface* getLayerSurface() const { return mLayerSurface.get(); }
|
||||
SkSurface* getLayerSurface() const {
|
||||
return mSkiaLayer.get() ? mSkiaLayer->layerSurface.get() : nullptr;
|
||||
}
|
||||
|
||||
skiapipeline::SkiaLayer* getSkiaLayer() const {
|
||||
return mSkiaLayer.get();
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
@@ -346,7 +358,7 @@ private:
|
||||
* An offscreen rendering target used to contain the contents this RenderNode
|
||||
* when it has been set to draw as a LayerType::RenderLayer.
|
||||
*/
|
||||
sk_sp<SkSurface> mLayerSurface;
|
||||
std::unique_ptr<skiapipeline::SkiaLayer> mSkiaLayer;
|
||||
}; // class RenderNode
|
||||
|
||||
} /* namespace uirenderer */
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
#include "RenderNode.h"
|
||||
#include "MinikinUtils.h"
|
||||
#include "Paint.h"
|
||||
#include "Properties.h"
|
||||
#include "pipeline/skia/SkiaRecordingCanvas.h"
|
||||
#include "Typeface.h"
|
||||
|
||||
#include <SkDrawFilter.h>
|
||||
@@ -27,6 +29,9 @@
|
||||
namespace android {
|
||||
|
||||
Canvas* Canvas::create_recording_canvas(int width, int height, uirenderer::RenderNode* renderNode) {
|
||||
if (uirenderer::Properties::isSkiaEnabled()) {
|
||||
return new uirenderer::skiapipeline::SkiaRecordingCanvas(renderNode, width, height);
|
||||
}
|
||||
return new uirenderer::RecordingCanvas(width, height);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#include "RenderNodeDrawable.h"
|
||||
#include "RenderNode.h"
|
||||
#include "SkiaDisplayList.h"
|
||||
#include "SkiaFrameRenderer.h"
|
||||
#include "SkiaPipeline.h"
|
||||
#include "utils/TraceUtils.h"
|
||||
|
||||
namespace android {
|
||||
@@ -57,7 +57,7 @@ void RenderNodeDrawable::onDraw(SkCanvas* canvas) {
|
||||
|
||||
void RenderNodeDrawable::forceDraw(SkCanvas* canvas) {
|
||||
RenderNode* renderNode = mRenderNode.get();
|
||||
if (SkiaFrameRenderer::skpCaptureEnabled()) {
|
||||
if (SkiaPipeline::skpCaptureEnabled()) {
|
||||
SkRect dimensions = SkRect::MakeWH(renderNode->getWidth(), renderNode->getHeight());
|
||||
canvas->drawAnnotation(dimensions, renderNode->getName(), nullptr);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#include "ReorderBarrierDrawables.h"
|
||||
#include "RenderNode.h"
|
||||
#include "SkiaDisplayList.h"
|
||||
#include "SkiaFrameRenderer.h"
|
||||
#include "SkiaPipeline.h"
|
||||
|
||||
#include <SkBlurMask.h>
|
||||
#include <SkBlurMaskFilter.h>
|
||||
@@ -161,7 +161,7 @@ static void DrawSpotShadowGeneral(SkCanvas* canvas, const Shape& shape, float ca
|
||||
return;
|
||||
}
|
||||
|
||||
const Vector3 lightPos = SkiaFrameRenderer::getLightCenter();
|
||||
const Vector3 lightPos = SkiaPipeline::getLightCenter();
|
||||
float zRatio = casterZValue / (lightPos.z - casterZValue);
|
||||
// clamp
|
||||
if (zRatio < 0.0f) {
|
||||
@@ -170,7 +170,7 @@ static void DrawSpotShadowGeneral(SkCanvas* canvas, const Shape& shape, float ca
|
||||
zRatio = 0.95f;
|
||||
}
|
||||
|
||||
float blurRadius = SkiaFrameRenderer::getLightRadius()*zRatio;
|
||||
float blurRadius = SkiaPipeline::getLightRadius()*zRatio;
|
||||
|
||||
SkAutoCanvasRestore acr(canvas, true);
|
||||
|
||||
@@ -276,7 +276,7 @@ static void DrawRRectShadows(const SkRect& casterRect, SkScalar casterCornerRadi
|
||||
}
|
||||
|
||||
if (spotAlpha > 0.0f) {
|
||||
const Vector3 lightPos = SkiaFrameRenderer::getLightCenter();
|
||||
const Vector3 lightPos = SkiaPipeline::getLightCenter();
|
||||
float zRatio = casterZValue / (lightPos.z - casterZValue);
|
||||
// clamp
|
||||
if (zRatio < 0.0f) {
|
||||
@@ -285,7 +285,7 @@ static void DrawRRectShadows(const SkRect& casterRect, SkScalar casterCornerRadi
|
||||
zRatio = 0.95f;
|
||||
}
|
||||
|
||||
const SkScalar lightWidth = SkiaFrameRenderer::getLightRadius();
|
||||
const SkScalar lightWidth = SkiaPipeline::getLightRadius();
|
||||
SkScalar srcSpaceSpotRadius = 2.0f * lightWidth * zRatio;
|
||||
// the device-space radius sent to the blur shader must fit in 14.2 fixed point
|
||||
if (srcSpaceSpotRadius*scaleFactor > MAX_BLUR_RADIUS) {
|
||||
@@ -439,7 +439,7 @@ static void DrawRRectShadowsWithClip(const SkRect& casterRect, SkScalar casterCo
|
||||
}
|
||||
|
||||
if (spotAlpha > 0.0f) {
|
||||
const Vector3 lightPos = SkiaFrameRenderer::getLightCenter();
|
||||
const Vector3 lightPos = SkiaPipeline::getLightCenter();
|
||||
float zRatio = casterZValue / (lightPos.z - casterZValue);
|
||||
// clamp
|
||||
if (zRatio < 0.0f) {
|
||||
@@ -448,7 +448,7 @@ static void DrawRRectShadowsWithClip(const SkRect& casterRect, SkScalar casterCo
|
||||
zRatio = 0.95f;
|
||||
}
|
||||
|
||||
const SkScalar lightWidth = SkiaFrameRenderer::getLightRadius();
|
||||
const SkScalar lightWidth = SkiaPipeline::getLightRadius();
|
||||
const SkScalar srcSpaceSpotRadius = 2.0f * lightWidth * zRatio;
|
||||
const SkScalar devSpaceSpotRadius = srcSpaceSpotRadius * scaleFactor;
|
||||
|
||||
@@ -626,8 +626,8 @@ void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable*
|
||||
return;
|
||||
}
|
||||
|
||||
float ambientAlpha = SkiaFrameRenderer::getAmbientShadowAlpha()*casterAlpha;
|
||||
float spotAlpha = SkiaFrameRenderer::getSpotShadowAlpha()*casterAlpha;
|
||||
float ambientAlpha = SkiaPipeline::getAmbientShadowAlpha()*casterAlpha;
|
||||
float spotAlpha = SkiaPipeline::getSpotShadowAlpha()*casterAlpha;
|
||||
const float casterZValue = casterProperties.getZ();
|
||||
|
||||
const RevealClip& revealClip = casterProperties.getRevealClip();
|
||||
|
||||
@@ -16,39 +16,23 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <SkSurface.h>
|
||||
#include "Matrix.h"
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
namespace skiapipeline {
|
||||
|
||||
/**
|
||||
* TODO: this is a stub that will be added in a subsquent CL
|
||||
* An offscreen rendering target used to contain the contents a RenderNode.
|
||||
*/
|
||||
class SkiaFrameRenderer {
|
||||
public:
|
||||
|
||||
static bool skpCaptureEnabled() { return false; }
|
||||
|
||||
// TODO avoids unused compile error but we need to pass this to the reorder drawables!
|
||||
static float getLightRadius() {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
static uint8_t getAmbientShadowAlpha() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static uint8_t getSpotShadowAlpha() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static Vector3 getLightCenter() {
|
||||
Vector3 result;
|
||||
result.x = result.y = result.z = 1.0f;
|
||||
return result;
|
||||
}
|
||||
|
||||
struct SkiaLayer
|
||||
{
|
||||
sk_sp<SkSurface> layerSurface;
|
||||
Matrix4 inverseTransformInWindow;
|
||||
};
|
||||
|
||||
}; // namespace skiapipeline
|
||||
}; // namespace uirenderer
|
||||
}; // namespace android
|
||||
|
||||
} /* namespace skiapipeline */
|
||||
} /* namespace uirenderer */
|
||||
} /* namespace android */
|
||||
170
libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
Normal file
170
libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
Normal file
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* 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 "SkiaOpenGLPipeline.h"
|
||||
|
||||
#include "DeferredLayerUpdater.h"
|
||||
#include "renderthread/EglManager.h"
|
||||
#include "renderstate/RenderState.h"
|
||||
#include "Readback.h"
|
||||
#include "utils/TraceUtils.h"
|
||||
|
||||
#include <android/native_window.h>
|
||||
#include <cutils/properties.h>
|
||||
#include <strings.h>
|
||||
|
||||
using namespace android::uirenderer::renderthread;
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
namespace skiapipeline {
|
||||
|
||||
SkiaOpenGLPipeline::SkiaOpenGLPipeline(RenderThread& thread)
|
||||
: SkiaPipeline(thread)
|
||||
, mEglManager(thread.eglManager()) {
|
||||
}
|
||||
|
||||
MakeCurrentResult SkiaOpenGLPipeline::makeCurrent() {
|
||||
// TODO: Figure out why this workaround is needed, see b/13913604
|
||||
// In the meantime this matches the behavior of GLRenderer, so it is not a regression
|
||||
EGLint error = 0;
|
||||
if (!mEglManager.makeCurrent(mEglSurface, &error)) {
|
||||
return MakeCurrentResult::AlreadyCurrent;
|
||||
}
|
||||
return error ? MakeCurrentResult::Failed : MakeCurrentResult::Succeeded;
|
||||
}
|
||||
|
||||
Frame SkiaOpenGLPipeline::getFrame() {
|
||||
LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
|
||||
"drawRenderNode called on a context with no surface!");
|
||||
return mEglManager.beginFrame(mEglSurface);
|
||||
}
|
||||
|
||||
bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty,
|
||||
const SkRect& dirty,
|
||||
const FrameBuilder::LightGeometry& lightGeometry,
|
||||
LayerUpdateQueue* layerUpdateQueue,
|
||||
const Rect& contentDrawBounds, bool opaque,
|
||||
const BakedOpRenderer::LightInfo& lightInfo,
|
||||
const std::vector<sp<RenderNode>>& renderNodes,
|
||||
FrameInfoVisualizer* profiler) {
|
||||
|
||||
mEglManager.damageFrame(frame, dirty);
|
||||
|
||||
// setup surface for fbo0
|
||||
GrBackendRenderTargetDesc renderTargetDesc;
|
||||
renderTargetDesc.fWidth = frame.width();
|
||||
renderTargetDesc.fHeight = frame.height();
|
||||
renderTargetDesc.fConfig = kRGBA_8888_GrPixelConfig;
|
||||
renderTargetDesc.fOrigin = kBottomLeft_GrSurfaceOrigin;
|
||||
renderTargetDesc.fSampleCnt = 0;
|
||||
renderTargetDesc.fStencilBits = STENCIL_BUFFER_SIZE;
|
||||
renderTargetDesc.fRenderTargetHandle = 0;
|
||||
|
||||
SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
|
||||
|
||||
SkASSERT(mRenderThread.getGrContext() != nullptr);
|
||||
sk_sp<SkSurface> surface(SkSurface::MakeFromBackendRenderTarget(
|
||||
mRenderThread.getGrContext(), renderTargetDesc, &props));
|
||||
|
||||
SkiaPipeline::updateLighting(lightGeometry, lightInfo);
|
||||
renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
|
||||
layerUpdateQueue->clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SkiaOpenGLPipeline::swapBuffers(const Frame& frame, bool drew,
|
||||
const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) {
|
||||
|
||||
GL_CHECKPOINT(LOW);
|
||||
|
||||
// Even if we decided to cancel the frame, from the perspective of jank
|
||||
// metrics the frame was swapped at this point
|
||||
currentFrameInfo->markSwapBuffers();
|
||||
|
||||
*requireSwap = drew || mEglManager.damageRequiresSwap();
|
||||
|
||||
if (*requireSwap && (CC_UNLIKELY(!mEglManager.swapBuffers(frame, screenDirty)))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return *requireSwap;
|
||||
}
|
||||
|
||||
bool SkiaOpenGLPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
|
||||
layer->apply();
|
||||
return Readback::copyTextureLayerInto(mRenderThread, *(layer->backingLayer()), bitmap)
|
||||
== CopyResult::Success;
|
||||
}
|
||||
|
||||
DeferredLayerUpdater* SkiaOpenGLPipeline::createTextureLayer() {
|
||||
mEglManager.initialize();
|
||||
Layer* layer = new Layer(mRenderThread.renderState(), 0, 0);
|
||||
layer->generateTexture();
|
||||
return new DeferredLayerUpdater(layer);
|
||||
}
|
||||
|
||||
void SkiaOpenGLPipeline::onStop() {
|
||||
if (mEglManager.isCurrent(mEglSurface)) {
|
||||
mEglManager.makeCurrent(EGL_NO_SURFACE);
|
||||
}
|
||||
}
|
||||
|
||||
bool SkiaOpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior) {
|
||||
|
||||
if (mEglSurface != EGL_NO_SURFACE) {
|
||||
mEglManager.destroySurface(mEglSurface);
|
||||
mEglSurface = EGL_NO_SURFACE;
|
||||
}
|
||||
|
||||
if (surface) {
|
||||
mEglSurface = mEglManager.createSurface(surface);
|
||||
}
|
||||
|
||||
if (mEglSurface != EGL_NO_SURFACE) {
|
||||
const bool preserveBuffer = (swapBehavior != SwapBehavior::kSwap_discardBuffer);
|
||||
mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SkiaOpenGLPipeline::isSurfaceReady() {
|
||||
return CC_UNLIKELY(mEglSurface != EGL_NO_SURFACE);
|
||||
}
|
||||
|
||||
bool SkiaOpenGLPipeline::isContextReady() {
|
||||
return CC_LIKELY(mEglManager.hasEglContext());
|
||||
}
|
||||
|
||||
void SkiaOpenGLPipeline::invokeFunctor(const RenderThread& thread, Functor* functor) {
|
||||
DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
|
||||
if (thread.eglManager().hasEglContext()) {
|
||||
mode = DrawGlInfo::kModeProcess;
|
||||
}
|
||||
|
||||
(*functor)(mode, nullptr);
|
||||
|
||||
// If there's no context we don't need to reset as there's no gl state to save/restore
|
||||
if (mode != DrawGlInfo::kModeProcessNoContext) {
|
||||
thread.getGrContext()->resetContext();
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace skiapipeline */
|
||||
} /* namespace uirenderer */
|
||||
} /* namespace android */
|
||||
58
libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
Normal file
58
libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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 "SkiaPipeline.h"
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
namespace skiapipeline {
|
||||
|
||||
class SkiaOpenGLPipeline : public SkiaPipeline {
|
||||
public:
|
||||
SkiaOpenGLPipeline(renderthread::RenderThread& thread);
|
||||
virtual ~SkiaOpenGLPipeline() {}
|
||||
|
||||
renderthread::MakeCurrentResult makeCurrent() override;
|
||||
renderthread::Frame getFrame() override;
|
||||
bool draw(const renderthread::Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
|
||||
const FrameBuilder::LightGeometry& lightGeometry,
|
||||
LayerUpdateQueue* layerUpdateQueue,
|
||||
const Rect& contentDrawBounds, bool opaque,
|
||||
const BakedOpRenderer::LightInfo& lightInfo,
|
||||
const std::vector< sp<RenderNode> >& renderNodes,
|
||||
FrameInfoVisualizer* profiler) override;
|
||||
bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
|
||||
FrameInfo* currentFrameInfo, bool* requireSwap) override;
|
||||
bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) override;
|
||||
DeferredLayerUpdater* createTextureLayer() override;
|
||||
bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior) override;
|
||||
void onStop() override;
|
||||
bool isSurfaceReady() override;
|
||||
bool isContextReady() override;
|
||||
|
||||
static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor);
|
||||
|
||||
private:
|
||||
renderthread::EglManager& mEglManager;
|
||||
EGLSurface mEglSurface = EGL_NO_SURFACE;
|
||||
bool mBufferPreserved = false;
|
||||
};
|
||||
|
||||
} /* namespace skiapipeline */
|
||||
} /* namespace uirenderer */
|
||||
} /* namespace android */
|
||||
258
libs/hwui/pipeline/skia/SkiaPipeline.cpp
Normal file
258
libs/hwui/pipeline/skia/SkiaPipeline.cpp
Normal file
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* 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 "SkiaPipeline.h"
|
||||
|
||||
#include "utils/TraceUtils.h"
|
||||
#include <SkOSFile.h>
|
||||
#include <SkPicture.h>
|
||||
#include <SkPictureRecorder.h>
|
||||
#include <SkPixelSerializer.h>
|
||||
#include <SkStream.h>
|
||||
|
||||
using namespace android::uirenderer::renderthread;
|
||||
|
||||
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) { }
|
||||
|
||||
TaskManager* SkiaPipeline::getTaskManager() {
|
||||
return &mTaskManager;
|
||||
}
|
||||
|
||||
void SkiaPipeline::onDestroyHardwareResources() {
|
||||
// No need to flush the caches here. There is a timer
|
||||
// which will flush temporary resources over time.
|
||||
}
|
||||
|
||||
void SkiaPipeline::renderLayers(const FrameBuilder::LightGeometry& lightGeometry,
|
||||
LayerUpdateQueue* layerUpdateQueue, bool opaque,
|
||||
const BakedOpRenderer::LightInfo& lightInfo) {
|
||||
updateLighting(lightGeometry, lightInfo);
|
||||
ATRACE_NAME("draw layers");
|
||||
renderLayersImpl(*layerUpdateQueue, opaque);
|
||||
layerUpdateQueue->clear();
|
||||
}
|
||||
|
||||
void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque) {
|
||||
// Render all layers that need to be updated, in order.
|
||||
for (size_t i = 0; i < layers.entries().size(); i++) {
|
||||
RenderNode* layerNode = layers.entries()[i].renderNode;
|
||||
// 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());
|
||||
SkASSERT(layerNode->getDisplayList()->isSkiaDL());
|
||||
SkiaDisplayList* displayList = (SkiaDisplayList*)layerNode->getDisplayList();
|
||||
if (!displayList || displayList->isEmpty()) {
|
||||
SkDEBUGF(("%p drawLayers(%s) : missing drawable", this, layerNode->getName()));
|
||||
return;
|
||||
}
|
||||
|
||||
const Rect& layerDamage = layers.entries()[i].damage;
|
||||
|
||||
SkCanvas* layerCanvas = layerNode->getLayerSurface()->getCanvas();
|
||||
|
||||
int saveCount = layerCanvas->save();
|
||||
SkASSERT(saveCount == 1);
|
||||
|
||||
layerCanvas->clipRect(layerDamage.toSkRect(), SkRegion::kReplace_Op);
|
||||
|
||||
auto savedLightCenter = mLightCenter;
|
||||
// map current light center into RenderNode's coordinate space
|
||||
layerNode->getSkiaLayer()->inverseTransformInWindow.mapPoint3d(mLightCenter);
|
||||
|
||||
const RenderProperties& properties = layerNode->properties();
|
||||
const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight());
|
||||
if (properties.getClipToBounds() && layerCanvas->quickReject(bounds)) {
|
||||
return;
|
||||
}
|
||||
|
||||
layerCanvas->clear(SK_ColorTRANSPARENT);
|
||||
|
||||
RenderNodeDrawable root(layerNode, layerCanvas, false);
|
||||
root.forceDraw(layerCanvas);
|
||||
layerCanvas->restoreToCount(saveCount);
|
||||
layerCanvas->flush();
|
||||
mLightCenter = savedLightCenter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SkiaPipeline::createOrUpdateLayer(RenderNode* node,
|
||||
const DamageAccumulator& damageAccumulator) {
|
||||
SkSurface* layer = node->getLayerSurface();
|
||||
if (!layer || layer->width() != node->getWidth() || layer->height() != node->getHeight()) {
|
||||
SkImageInfo info = SkImageInfo::MakeN32Premul(node->getWidth(), node->getHeight());
|
||||
SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
|
||||
SkASSERT(mRenderThread.getGrContext() != nullptr);
|
||||
node->setLayerSurface(
|
||||
SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), SkBudgeted::kYes,
|
||||
info, 0, &props));
|
||||
if (node->getLayerSurface()) {
|
||||
// update the transform in window of the layer to reset its origin wrt light source
|
||||
// position
|
||||
Matrix4 windowTransform;
|
||||
damageAccumulator.computeCurrentTransform(&windowTransform);
|
||||
node->getSkiaLayer()->inverseTransformInWindow = windowTransform;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SkiaPipeline::destroyLayer(RenderNode* node) {
|
||||
node->setLayerSurface(nullptr);
|
||||
}
|
||||
|
||||
void SkiaPipeline::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) {
|
||||
GrContext* context = thread.getGrContext();
|
||||
if (context) {
|
||||
ATRACE_FORMAT("Bitmap#prepareToDraw %dx%d", bitmap->width(), bitmap->height());
|
||||
SkBitmap skiaBitmap;
|
||||
bitmap->getSkBitmap(&skiaBitmap);
|
||||
sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(skiaBitmap, kNever_SkCopyPixelsMode);
|
||||
SkImage_pinAsTexture(image.get(), context);
|
||||
SkImage_unpinAsTexture(image.get(), context);
|
||||
}
|
||||
}
|
||||
|
||||
// Encodes to PNG, unless there is already encoded data, in which case that gets
|
||||
// used.
|
||||
class PngPixelSerializer : public SkPixelSerializer {
|
||||
public:
|
||||
bool onUseEncodedData(const void*, size_t) override { return true; }
|
||||
SkData* onEncode(const SkPixmap& pixmap) override {
|
||||
return SkImageEncoder::EncodeData(pixmap.info(), pixmap.addr(), pixmap.rowBytes(),
|
||||
SkImageEncoder::kPNG_Type, 100);
|
||||
}
|
||||
};
|
||||
|
||||
void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
|
||||
const std::vector<sp<RenderNode>>& nodes, bool opaque, const Rect &contentDrawBounds,
|
||||
sk_sp<SkSurface> surface) {
|
||||
|
||||
// unpin all mutable images that were attached to nodes deleted while on the UI thread
|
||||
SkiaDisplayList::cleanupImages(surface->getCanvas()->getGrContext());
|
||||
|
||||
// draw all layers up front
|
||||
renderLayersImpl(layers, opaque);
|
||||
|
||||
// initialize the canvas for the current frame
|
||||
SkCanvas* canvas = surface->getCanvas();
|
||||
|
||||
std::unique_ptr<SkPictureRecorder> recorder;
|
||||
bool recordingPicture = false;
|
||||
char prop[PROPERTY_VALUE_MAX];
|
||||
if (skpCaptureEnabled()) {
|
||||
property_get("debug.hwui.capture_frame_as_skp", prop, "0");
|
||||
recordingPicture = prop[0] != '0' && !sk_exists(prop);
|
||||
if (recordingPicture) {
|
||||
recorder.reset(new SkPictureRecorder());
|
||||
canvas = recorder->beginRecording(surface->width(), surface->height(),
|
||||
nullptr, SkPictureRecorder::kPlaybackDrawPicture_RecordFlag);
|
||||
}
|
||||
}
|
||||
|
||||
canvas->clipRect(clip, SkRegion::kReplace_Op);
|
||||
|
||||
if (!opaque) {
|
||||
canvas->clear(SK_ColorTRANSPARENT);
|
||||
}
|
||||
|
||||
// If there are multiple render nodes, they are laid out as follows:
|
||||
// #0 - backdrop (content + caption)
|
||||
// #1 - content (positioned at (0,0) and clipped to - its bounds mContentDrawBounds)
|
||||
// #2 - additional overlay nodes
|
||||
// Usually the backdrop cannot be seen since it will be entirely covered by the content. While
|
||||
// resizing however it might become partially visible. The following render loop will crop the
|
||||
// backdrop against the content and draw the remaining part of it. It will then draw the content
|
||||
// cropped to the backdrop (since that indicates a shrinking of the window).
|
||||
//
|
||||
// Additional nodes will be drawn on top with no particular clipping semantics.
|
||||
|
||||
// The bounds of the backdrop against which the content should be clipped.
|
||||
Rect backdropBounds = contentDrawBounds;
|
||||
// Usually the contents bounds should be mContentDrawBounds - however - we will
|
||||
// move it towards the fixed edge to give it a more stable appearance (for the moment).
|
||||
// If there is no content bounds we ignore the layering as stated above and start with 2.
|
||||
int layer = (contentDrawBounds.isEmpty() || nodes.size() == 1) ? 2 : 0;
|
||||
|
||||
for (const sp<RenderNode>& node : nodes) {
|
||||
if (node->nothingToDraw()) continue;
|
||||
|
||||
SkASSERT(node->getDisplayList()->isSkiaDL());
|
||||
|
||||
int count = canvas->save();
|
||||
|
||||
if (layer == 0) {
|
||||
const RenderProperties& properties = node->properties();
|
||||
Rect targetBounds(properties.getLeft(), properties.getTop(),
|
||||
properties.getRight(), properties.getBottom());
|
||||
// Move the content bounds towards the fixed corner of the backdrop.
|
||||
const int x = targetBounds.left;
|
||||
const int y = targetBounds.top;
|
||||
// Remember the intersection of the target bounds and the intersection bounds against
|
||||
// which we have to crop the content.
|
||||
backdropBounds.set(x, y, x + backdropBounds.getWidth(), y + backdropBounds.getHeight());
|
||||
backdropBounds.doIntersect(targetBounds);
|
||||
} else if (layer == 1) {
|
||||
// We shift and clip the content to match its final location in the window.
|
||||
const SkRect clip = SkRect::MakeXYWH(contentDrawBounds.left, contentDrawBounds.top,
|
||||
backdropBounds.getWidth(), backdropBounds.getHeight());
|
||||
const float dx = backdropBounds.left - contentDrawBounds.left;
|
||||
const float dy = backdropBounds.top - contentDrawBounds.top;
|
||||
canvas->translate(dx, dy);
|
||||
// It gets cropped against the bounds of the backdrop to stay inside.
|
||||
canvas->clipRect(clip, SkRegion::kIntersect_Op);
|
||||
}
|
||||
|
||||
RenderNodeDrawable root(node.get(), canvas);
|
||||
root.draw(canvas);
|
||||
canvas->restoreToCount(count);
|
||||
layer++;
|
||||
}
|
||||
|
||||
if (skpCaptureEnabled() && recordingPicture) {
|
||||
sk_sp<SkPicture> picture = recorder->finishRecordingAsPicture();
|
||||
if (picture->approximateOpCount() > 0) {
|
||||
SkFILEWStream stream(prop);
|
||||
if (stream.isValid()) {
|
||||
PngPixelSerializer serializer;
|
||||
picture->serialize(&stream, &serializer);
|
||||
stream.flush();
|
||||
SkDebugf("Captured Drawing Output (%d bytes) for frame. %s", stream.bytesWritten(), prop);
|
||||
}
|
||||
}
|
||||
surface->getCanvas()->drawPicture(picture);
|
||||
}
|
||||
|
||||
ATRACE_NAME("flush commands");
|
||||
canvas->flush();
|
||||
}
|
||||
|
||||
} /* namespace skiapipeline */
|
||||
} /* namespace uirenderer */
|
||||
} /* namespace android */
|
||||
112
libs/hwui/pipeline/skia/SkiaPipeline.h
Normal file
112
libs/hwui/pipeline/skia/SkiaPipeline.h
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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 "renderthread/CanvasContext.h"
|
||||
#include "FrameBuilder.h"
|
||||
#include "renderthread/IRenderPipeline.h"
|
||||
#include <SkSurface.h>
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
namespace skiapipeline {
|
||||
|
||||
class SkiaPipeline : public renderthread::IRenderPipeline {
|
||||
public:
|
||||
SkiaPipeline(renderthread::RenderThread& thread);
|
||||
virtual ~SkiaPipeline() {}
|
||||
|
||||
TaskManager* getTaskManager() override;
|
||||
|
||||
void onDestroyHardwareResources() override;
|
||||
|
||||
void renderLayers(const FrameBuilder::LightGeometry& lightGeometry,
|
||||
LayerUpdateQueue* layerUpdateQueue, bool opaque,
|
||||
const BakedOpRenderer::LightInfo& lightInfo) override;
|
||||
|
||||
bool createOrUpdateLayer(RenderNode* node,
|
||||
const DamageAccumulator& damageAccumulator) override;
|
||||
|
||||
void renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
|
||||
const std::vector< sp<RenderNode> >& nodes, bool opaque, const Rect &contentDrawBounds,
|
||||
sk_sp<SkSurface> surface);
|
||||
|
||||
static void destroyLayer(RenderNode* node);
|
||||
|
||||
static void prepareToDraw(const renderthread::RenderThread& thread, Bitmap* bitmap);
|
||||
|
||||
static void renderLayersImpl(const LayerUpdateQueue& layers, bool opaque);
|
||||
|
||||
static bool skpCaptureEnabled() { return false; }
|
||||
|
||||
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 FrameBuilder::LightGeometry& lightGeometry,
|
||||
const BakedOpRenderer::LightInfo& lightInfo) {
|
||||
mLightRadius = lightGeometry.radius;
|
||||
mAmbientShadowAlpha = lightInfo.ambientShadowAlpha;
|
||||
mSpotShadowAlpha = lightInfo.spotShadowAlpha;
|
||||
mLightCenter = lightGeometry.center;
|
||||
}
|
||||
protected:
|
||||
renderthread::RenderThread& mRenderThread;
|
||||
|
||||
private:
|
||||
TaskManager mTaskManager;
|
||||
static float mLightRadius;
|
||||
static uint8_t mAmbientShadowAlpha;
|
||||
static uint8_t mSpotShadowAlpha;
|
||||
static Vector3 mLightCenter;
|
||||
};
|
||||
|
||||
} /* namespace skiapipeline */
|
||||
} /* namespace uirenderer */
|
||||
} /* namespace android */
|
||||
144
libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
Normal file
144
libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* 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 "SkiaVulkanPipeline.h"
|
||||
|
||||
#include "DeferredLayerUpdater.h"
|
||||
#include "renderthread/EglManager.h" // needed for Frame
|
||||
#include "Readback.h"
|
||||
#include "renderstate/RenderState.h"
|
||||
|
||||
#include <SkTypes.h>
|
||||
#include <WindowContextFactory_android.h>
|
||||
#include <VulkanWindowContext.h>
|
||||
|
||||
#include <android/native_window.h>
|
||||
#include <cutils/properties.h>
|
||||
#include <strings.h>
|
||||
|
||||
using namespace android::uirenderer::renderthread;
|
||||
using namespace sk_app;
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
namespace skiapipeline {
|
||||
|
||||
MakeCurrentResult SkiaVulkanPipeline::makeCurrent() {
|
||||
return (mWindowContext != nullptr) ?
|
||||
MakeCurrentResult::AlreadyCurrent : MakeCurrentResult::Failed;
|
||||
}
|
||||
|
||||
Frame SkiaVulkanPipeline::getFrame() {
|
||||
LOG_ALWAYS_FATAL_IF(mWindowContext == nullptr, "Tried to draw into null vulkan context!");
|
||||
mBackbuffer = mWindowContext->getBackbufferSurface();
|
||||
if (mBackbuffer.get() == nullptr) {
|
||||
// try recreating the context?
|
||||
SkDebugf("failed to get backbuffer");
|
||||
return Frame(-1, -1, 0);
|
||||
}
|
||||
|
||||
// TODO: support buffer age if Vulkan API can do it
|
||||
Frame frame(mBackbuffer->width(), mBackbuffer->height(), 0);
|
||||
return frame;
|
||||
}
|
||||
|
||||
bool SkiaVulkanPipeline::draw(const Frame& frame, const SkRect& screenDirty,
|
||||
const SkRect& dirty,
|
||||
const FrameBuilder::LightGeometry& lightGeometry,
|
||||
LayerUpdateQueue* layerUpdateQueue,
|
||||
const Rect& contentDrawBounds, bool opaque,
|
||||
const BakedOpRenderer::LightInfo& lightInfo,
|
||||
const std::vector<sp<RenderNode>>& renderNodes,
|
||||
FrameInfoVisualizer* profiler) {
|
||||
|
||||
if (mBackbuffer.get() == nullptr) {
|
||||
return false;
|
||||
}
|
||||
renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, mBackbuffer);
|
||||
layerUpdateQueue->clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SkiaVulkanPipeline::swapBuffers(const Frame& frame, bool drew,
|
||||
const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) {
|
||||
|
||||
*requireSwap = drew;
|
||||
|
||||
// Even if we decided to cancel the frame, from the perspective of jank
|
||||
// metrics the frame was swapped at this point
|
||||
currentFrameInfo->markSwapBuffers();
|
||||
|
||||
if (*requireSwap) {
|
||||
mWindowContext->swapBuffers();
|
||||
}
|
||||
|
||||
mBackbuffer.reset();
|
||||
|
||||
return *requireSwap;
|
||||
}
|
||||
|
||||
bool SkiaVulkanPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
|
||||
// TODO: implement copyLayerInto for vulkan.
|
||||
return false;
|
||||
}
|
||||
|
||||
DeferredLayerUpdater* SkiaVulkanPipeline::createTextureLayer() {
|
||||
Layer* layer = new Layer(mRenderThread.renderState(), 0, 0);
|
||||
return new DeferredLayerUpdater(layer);
|
||||
}
|
||||
|
||||
void SkiaVulkanPipeline::onStop() {
|
||||
}
|
||||
|
||||
bool SkiaVulkanPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior) {
|
||||
|
||||
if (mWindowContext) {
|
||||
delete mWindowContext;
|
||||
mWindowContext = nullptr;
|
||||
}
|
||||
|
||||
if (surface) {
|
||||
DisplayParams displayParams;
|
||||
mWindowContext = window_context_factory::NewVulkanForAndroid(surface, displayParams);
|
||||
if (mWindowContext) {
|
||||
DeviceInfo::initialize(mWindowContext->getGrContext()->caps()->maxRenderTargetSize());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// this doesn't work for if there is more than one CanvasContext available at one time!
|
||||
mRenderThread.setGrContext(mWindowContext ? mWindowContext->getGrContext() : nullptr);
|
||||
|
||||
return mWindowContext != nullptr;
|
||||
}
|
||||
|
||||
bool SkiaVulkanPipeline::isSurfaceReady() {
|
||||
return CC_LIKELY(mWindowContext != nullptr) && mWindowContext->isValid();
|
||||
}
|
||||
|
||||
bool SkiaVulkanPipeline::isContextReady() {
|
||||
return CC_LIKELY(mWindowContext != nullptr);
|
||||
}
|
||||
|
||||
void SkiaVulkanPipeline::invokeFunctor(const RenderThread& thread, Functor* functor) {
|
||||
// TODO: we currently don't support OpenGL WebView's
|
||||
DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
|
||||
(*functor)(mode, nullptr);
|
||||
}
|
||||
|
||||
} /* namespace skiapipeline */
|
||||
} /* namespace uirenderer */
|
||||
} /* namespace android */
|
||||
62
libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
Normal file
62
libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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 "SkiaPipeline.h"
|
||||
#include <SkSurface.h>
|
||||
|
||||
namespace sk_app {
|
||||
class WindowContext;
|
||||
}
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
namespace skiapipeline {
|
||||
|
||||
class SkiaVulkanPipeline : public SkiaPipeline {
|
||||
public:
|
||||
SkiaVulkanPipeline(renderthread::RenderThread& thread) : SkiaPipeline(thread) {}
|
||||
virtual ~SkiaVulkanPipeline() {}
|
||||
|
||||
renderthread::MakeCurrentResult makeCurrent() override;
|
||||
renderthread::Frame getFrame() override;
|
||||
bool draw(const renderthread::Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
|
||||
const FrameBuilder::LightGeometry& lightGeometry,
|
||||
LayerUpdateQueue* layerUpdateQueue,
|
||||
const Rect& contentDrawBounds, bool opaque,
|
||||
const BakedOpRenderer::LightInfo& lightInfo,
|
||||
const std::vector< sp<RenderNode> >& renderNodes,
|
||||
FrameInfoVisualizer* profiler) override;
|
||||
bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
|
||||
FrameInfo* currentFrameInfo, bool* requireSwap) override;
|
||||
bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) override;
|
||||
DeferredLayerUpdater* createTextureLayer() override;
|
||||
bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior) override;
|
||||
void onStop() override;
|
||||
bool isSurfaceReady() override;
|
||||
bool isContextReady() override;
|
||||
|
||||
static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor);
|
||||
|
||||
private:
|
||||
sk_app::WindowContext* mWindowContext = nullptr;
|
||||
sk_sp<SkSurface> mBackbuffer;
|
||||
};
|
||||
|
||||
} /* namespace skiapipeline */
|
||||
} /* namespace uirenderer */
|
||||
} /* namespace android */
|
||||
@@ -28,6 +28,9 @@
|
||||
#include "renderstate/Stencil.h"
|
||||
#include "protos/hwui.pb.h"
|
||||
#include "OpenGLPipeline.h"
|
||||
#include "pipeline/skia/SkiaOpenGLPipeline.h"
|
||||
#include "pipeline/skia/SkiaPipeline.h"
|
||||
#include "pipeline/skia/SkiaVulkanPipeline.h"
|
||||
#include "utils/GLUtils.h"
|
||||
#include "utils/TimeUtils.h"
|
||||
|
||||
@@ -69,13 +72,11 @@ CanvasContext* CanvasContext::create(RenderThread& thread,
|
||||
return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
|
||||
std::make_unique<OpenGLPipeline>(thread));
|
||||
case RenderPipelineType::SkiaGL:
|
||||
//TODO: implement SKIA GL
|
||||
LOG_ALWAYS_FATAL("skiaGL canvas type not implemented.");
|
||||
break;
|
||||
return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
|
||||
std::make_unique<skiapipeline::SkiaOpenGLPipeline>(thread));
|
||||
case RenderPipelineType::SkiaVulkan:
|
||||
//TODO: implement Vulkan
|
||||
LOG_ALWAYS_FATAL("Vulkan canvas type not implemented.");
|
||||
break;
|
||||
return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
|
||||
std::make_unique<skiapipeline::SkiaVulkanPipeline>(thread));
|
||||
default:
|
||||
LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType);
|
||||
break;
|
||||
@@ -89,6 +90,10 @@ void CanvasContext::destroyLayer(RenderNode* node) {
|
||||
case RenderPipelineType::OpenGL:
|
||||
OpenGLPipeline::destroyLayer(node);
|
||||
break;
|
||||
case RenderPipelineType::SkiaGL:
|
||||
case RenderPipelineType::SkiaVulkan:
|
||||
skiapipeline::SkiaPipeline::destroyLayer(node);
|
||||
break;
|
||||
default:
|
||||
LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType);
|
||||
break;
|
||||
@@ -102,6 +107,12 @@ void CanvasContext::invokeFunctor(const RenderThread& thread, Functor* functor)
|
||||
case RenderPipelineType::OpenGL:
|
||||
OpenGLPipeline::invokeFunctor(thread, functor);
|
||||
break;
|
||||
case RenderPipelineType::SkiaGL:
|
||||
skiapipeline::SkiaOpenGLPipeline::invokeFunctor(thread, functor);
|
||||
break;
|
||||
case RenderPipelineType::SkiaVulkan:
|
||||
skiapipeline::SkiaVulkanPipeline::invokeFunctor(thread, functor);
|
||||
break;
|
||||
default:
|
||||
LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType);
|
||||
break;
|
||||
@@ -114,6 +125,10 @@ void CanvasContext::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) {
|
||||
case RenderPipelineType::OpenGL:
|
||||
OpenGLPipeline::prepareToDraw(thread, bitmap);
|
||||
break;
|
||||
case RenderPipelineType::SkiaGL:
|
||||
case RenderPipelineType::SkiaVulkan:
|
||||
skiapipeline::SkiaPipeline::prepareToDraw(thread, bitmap);
|
||||
break;
|
||||
default:
|
||||
LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType);
|
||||
break;
|
||||
|
||||
@@ -31,6 +31,11 @@ class EglManager;
|
||||
|
||||
class Frame {
|
||||
public:
|
||||
Frame(EGLint width, EGLint height, EGLint bufferAge)
|
||||
: mWidth(width)
|
||||
, mHeight(height)
|
||||
, mBufferAge(bufferAge) { }
|
||||
|
||||
EGLint width() const { return mWidth; }
|
||||
EGLint height() const { return mHeight; }
|
||||
|
||||
@@ -39,6 +44,7 @@ public:
|
||||
EGLint bufferAge() const { return mBufferAge; }
|
||||
|
||||
private:
|
||||
Frame() {}
|
||||
friend class EglManager;
|
||||
|
||||
EGLSurface mSurface;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <Rect.h>
|
||||
#include <RenderNode.h>
|
||||
#include <hwui/Bitmap.h>
|
||||
#include <pipeline/skia/SkiaRecordingCanvas.h>
|
||||
#include <renderstate/RenderState.h>
|
||||
#include <renderthread/RenderThread.h>
|
||||
#include <Snapshot.h>
|
||||
@@ -196,6 +197,35 @@ public:
|
||||
node.setStagingDisplayList(canvas->finishRecording(), nullptr);
|
||||
}
|
||||
|
||||
static sp<RenderNode> createSkiaNode(int left, int top, int right, int bottom,
|
||||
std::function<void(RenderProperties& props, skiapipeline::SkiaRecordingCanvas& canvas)> setup,
|
||||
const char* name = nullptr, skiapipeline::SkiaDisplayList* displayList = nullptr) {
|
||||
#if HWUI_NULL_GPU
|
||||
// if RenderNodes are being sync'd/used, device info will be needed, since
|
||||
// DeviceInfo::maxTextureSize() affects layer property
|
||||
DeviceInfo::initialize();
|
||||
#endif
|
||||
sp<RenderNode> node = new RenderNode();
|
||||
if (name) {
|
||||
node->setName(name);
|
||||
}
|
||||
RenderProperties& props = node->mutateStagingProperties();
|
||||
props.setLeftTopRightBottom(left, top, right, bottom);
|
||||
if (displayList) {
|
||||
node->setStagingDisplayList(displayList, nullptr);
|
||||
}
|
||||
if (setup) {
|
||||
std::unique_ptr<skiapipeline::SkiaRecordingCanvas> canvas(
|
||||
new skiapipeline::SkiaRecordingCanvas(nullptr,
|
||||
props.getWidth(), props.getHeight()));
|
||||
setup(props, *canvas.get());
|
||||
node->setStagingDisplayList(canvas->finishRecording(), nullptr);
|
||||
}
|
||||
node->setPropertyFieldsDirty(0xFFFFFFFF);
|
||||
TestUtils::syncHierarchyPropertiesAndDisplayList(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces a sync of a tree of RenderNode, such that every descendant will have its staging
|
||||
* properties and DisplayList moved to the render copies.
|
||||
|
||||
@@ -34,34 +34,6 @@ using namespace android::uirenderer;
|
||||
using namespace android::uirenderer::renderthread;
|
||||
using namespace android::uirenderer::skiapipeline;
|
||||
|
||||
static sp<RenderNode> createSkiaNode(int left, int top, int right, int bottom,
|
||||
std::function<void(RenderProperties& props, SkiaRecordingCanvas& canvas)> setup,
|
||||
const char* name = nullptr, SkiaDisplayList* displayList = nullptr) {
|
||||
#if HWUI_NULL_GPU
|
||||
// if RenderNodes are being sync'd/used, device info will be needed, since
|
||||
// DeviceInfo::maxTextureSize() affects layer property
|
||||
DeviceInfo::initialize();
|
||||
#endif
|
||||
sp<RenderNode> node = new RenderNode();
|
||||
if (name) {
|
||||
node->setName(name);
|
||||
}
|
||||
RenderProperties& props = node->mutateStagingProperties();
|
||||
props.setLeftTopRightBottom(left, top, right, bottom);
|
||||
if (displayList) {
|
||||
node->setStagingDisplayList(displayList, nullptr);
|
||||
}
|
||||
if (setup) {
|
||||
std::unique_ptr<SkiaRecordingCanvas> canvas(new SkiaRecordingCanvas(nullptr,
|
||||
props.getWidth(), props.getHeight()));
|
||||
setup(props, *canvas.get());
|
||||
node->setStagingDisplayList(canvas->finishRecording(), nullptr);
|
||||
}
|
||||
node->setPropertyFieldsDirty(0xFFFFFFFF);
|
||||
TestUtils::syncHierarchyPropertiesAndDisplayList(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
TEST(RenderNodeDrawable, create) {
|
||||
auto rootNode = TestUtils::createNode(0, 0, 200, 400,
|
||||
[](RenderProperties& props, Canvas& canvas) {
|
||||
@@ -86,7 +58,7 @@ TEST(RenderNodeDrawable, drawContent) {
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
|
||||
|
||||
//create a RenderNodeDrawable backed by a RenderNode backed by a SkLiteRecorder
|
||||
auto rootNode = createSkiaNode(0, 0, 1, 1,
|
||||
auto rootNode = TestUtils::createSkiaNode(0, 0, 1, 1,
|
||||
[](RenderProperties& props, SkiaRecordingCanvas& recorder) {
|
||||
recorder.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
|
||||
});
|
||||
@@ -118,14 +90,14 @@ TEST(RenderNodeDrawable, drawAndReorder) {
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE);
|
||||
|
||||
//-z draws to all 4 pixels (RED)
|
||||
auto redNode = createSkiaNode(0, 0, 4, 4,
|
||||
auto redNode = TestUtils::createSkiaNode(0, 0, 4, 4,
|
||||
[](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
|
||||
redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
|
||||
props.setElevation(-10.0f);
|
||||
}, "redNode");
|
||||
|
||||
//0z draws to bottom 2 pixels (GREEN)
|
||||
auto bottomHalfGreenNode = createSkiaNode(0, 0, 4, 4,
|
||||
auto bottomHalfGreenNode = TestUtils::createSkiaNode(0, 0, 4, 4,
|
||||
[](RenderProperties& props, SkiaRecordingCanvas& bottomHalfGreenCanvas) {
|
||||
SkPaint greenPaint;
|
||||
greenPaint.setColor(SK_ColorGREEN);
|
||||
@@ -135,7 +107,7 @@ TEST(RenderNodeDrawable, drawAndReorder) {
|
||||
}, "bottomHalfGreenNode");
|
||||
|
||||
//+z draws to right 2 pixels (BLUE)
|
||||
auto rightHalfBlueNode = createSkiaNode(0, 0, 4, 4,
|
||||
auto rightHalfBlueNode = TestUtils::createSkiaNode(0, 0, 4, 4,
|
||||
[](RenderProperties& props, SkiaRecordingCanvas& rightHalfBlueCanvas) {
|
||||
SkPaint bluePaint;
|
||||
bluePaint.setColor(SK_ColorBLUE);
|
||||
@@ -144,7 +116,7 @@ TEST(RenderNodeDrawable, drawAndReorder) {
|
||||
props.setElevation(10.0f);
|
||||
}, "rightHalfBlueNode");
|
||||
|
||||
auto rootNode = createSkiaNode(0, 0, 4, 4,
|
||||
auto rootNode = TestUtils::createSkiaNode(0, 0, 4, 4,
|
||||
[&](RenderProperties& props, SkiaRecordingCanvas& rootRecorder) {
|
||||
rootRecorder.insertReorderBarrier(true);
|
||||
//draw in reverse Z order, so Z alters draw order
|
||||
@@ -167,7 +139,7 @@ TEST(RenderNodeDrawable, composeOnLayer)
|
||||
canvas.drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
|
||||
|
||||
auto rootNode = createSkiaNode(0, 0, 1, 1,
|
||||
auto rootNode = TestUtils::createSkiaNode(0, 0, 1, 1,
|
||||
[](RenderProperties& props, SkiaRecordingCanvas& recorder) {
|
||||
recorder.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
|
||||
});
|
||||
@@ -176,7 +148,7 @@ TEST(RenderNodeDrawable, composeOnLayer)
|
||||
auto surfaceLayer = SkSurface::MakeRasterN32Premul(1, 1);
|
||||
auto canvas2 = surfaceLayer->getCanvas();
|
||||
canvas2->drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
|
||||
rootNode->setLayerSurface( surfaceLayer );
|
||||
rootNode->setLayerSurface(surfaceLayer);
|
||||
|
||||
RenderNodeDrawable drawable1(rootNode.get(), &canvas, false);
|
||||
canvas.drawDrawable(&drawable1);
|
||||
@@ -190,7 +162,7 @@ TEST(RenderNodeDrawable, composeOnLayer)
|
||||
canvas.drawDrawable(&drawable3);
|
||||
ASSERT_EQ(SK_ColorRED, TestUtils::getColor(surface, 0, 0));
|
||||
|
||||
rootNode->setLayerSurface( sk_sp<SkSurface>() );
|
||||
rootNode->setLayerSurface(sk_sp<SkSurface>());
|
||||
}
|
||||
|
||||
//TODO: refactor to cover test cases from FrameBuilderTests_projectionReorder
|
||||
@@ -203,18 +175,18 @@ TEST(RenderNodeDrawable, projectDraw) {
|
||||
canvas.drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
|
||||
|
||||
auto redNode = createSkiaNode(0, 0, 1, 1,
|
||||
auto redNode = TestUtils::createSkiaNode(0, 0, 1, 1,
|
||||
[](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
|
||||
redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
|
||||
}, "redNode");
|
||||
|
||||
auto greenNodeWithRedChild = createSkiaNode(0, 0, 1, 1,
|
||||
auto greenNodeWithRedChild = TestUtils::createSkiaNode(0, 0, 1, 1,
|
||||
[&](RenderProperties& props, SkiaRecordingCanvas& greenCanvasWithRedChild) {
|
||||
greenCanvasWithRedChild.drawRenderNode(redNode.get());
|
||||
greenCanvasWithRedChild.drawColor(SK_ColorGREEN, SkBlendMode::kSrcOver);
|
||||
}, "greenNodeWithRedChild");
|
||||
|
||||
auto rootNode = createSkiaNode(0, 0, 1, 1,
|
||||
auto rootNode = TestUtils::createSkiaNode(0, 0, 1, 1,
|
||||
[&](RenderProperties& props, SkiaRecordingCanvas& rootCanvas) {
|
||||
rootCanvas.drawRenderNode(greenNodeWithRedChild.get());
|
||||
}, "rootNode");
|
||||
|
||||
181
libs/hwui/tests/unit/SkiaPipelineTests.cpp
Normal file
181
libs/hwui/tests/unit/SkiaPipelineTests.cpp
Normal file
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* 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 <gtest/gtest.h>
|
||||
#include <VectorDrawable.h>
|
||||
|
||||
#include "AnimationContext.h"
|
||||
#include "DamageAccumulator.h"
|
||||
#include "IContextFactory.h"
|
||||
#include "pipeline/skia/SkiaDisplayList.h"
|
||||
#include "pipeline/skia/SkiaRecordingCanvas.h"
|
||||
#include "pipeline/skia/SkiaOpenGLPipeline.h"
|
||||
#include "renderthread/CanvasContext.h"
|
||||
#include "tests/common/TestUtils.h"
|
||||
#include "SkiaCanvas.h"
|
||||
#include <SkLiteRecorder.h>
|
||||
#include <string.h>
|
||||
|
||||
using namespace android;
|
||||
using namespace android::uirenderer;
|
||||
using namespace android::uirenderer::renderthread;
|
||||
using namespace android::uirenderer::skiapipeline;
|
||||
|
||||
RENDERTHREAD_TEST(SkiaPipeline, renderFrame) {
|
||||
auto redNode = TestUtils::createSkiaNode(0, 0, 1, 1,
|
||||
[](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
|
||||
redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
|
||||
});
|
||||
LayerUpdateQueue layerUpdateQueue;
|
||||
SkRect dirty = SkRect::MakeLargest();
|
||||
std::vector<sp<RenderNode>> renderNodes;
|
||||
renderNodes.push_back(redNode);
|
||||
bool opaque = true;
|
||||
android::uirenderer::Rect contentDrawBounds(0, 0, 1, 1);
|
||||
auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
|
||||
auto surface = SkSurface::MakeRasterN32Premul(1, 1);
|
||||
surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
|
||||
pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
|
||||
}
|
||||
|
||||
RENDERTHREAD_TEST(SkiaPipeline, renderFrameCheckBounds) {
|
||||
auto backdropRedNode = TestUtils::createSkiaNode(1, 1, 4, 4,
|
||||
[](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
|
||||
redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
|
||||
});
|
||||
auto contentGreenNode = TestUtils::createSkiaNode(2, 2, 5, 5,
|
||||
[](RenderProperties& props, SkiaRecordingCanvas& blueCanvas) {
|
||||
blueCanvas.drawColor(SK_ColorGREEN, SkBlendMode::kSrcOver);
|
||||
});
|
||||
LayerUpdateQueue layerUpdateQueue;
|
||||
SkRect dirty = SkRect::MakeLargest();
|
||||
std::vector<sp<RenderNode>> renderNodes;
|
||||
renderNodes.push_back(backdropRedNode); //first node is backdrop
|
||||
renderNodes.push_back(contentGreenNode); //second node is content drawn on top of the backdrop
|
||||
android::uirenderer::Rect contentDrawBounds(1, 1, 3, 3);
|
||||
auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
|
||||
auto surface = SkSurface::MakeRasterN32Premul(5, 5);
|
||||
surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
|
||||
//backdropBounds is (1, 1, 3, 3), content clip is (1, 1, 3, 3), content translate is (0, 0)
|
||||
pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 1, 1), SK_ColorRED);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 2, 2), SK_ColorGREEN);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 3, 3), SK_ColorRED);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 4, 4), SK_ColorBLUE);
|
||||
|
||||
surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
|
||||
contentDrawBounds.set(0, 0, 5, 5);
|
||||
//backdropBounds is (1, 1, 4, 4), content clip is (0, 0, 3, 3), content translate is (1, 1)
|
||||
pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 1, 1), SK_ColorRED);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 2, 2), SK_ColorRED);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 3, 3), SK_ColorGREEN);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 4, 4), SK_ColorBLUE);
|
||||
}
|
||||
|
||||
RENDERTHREAD_TEST(SkiaPipeline, renderFrameCheckOpaque) {
|
||||
auto halfGreenNode = TestUtils::createSkiaNode(0, 0, 2, 2,
|
||||
[](RenderProperties& props, SkiaRecordingCanvas& bottomHalfGreenCanvas) {
|
||||
SkPaint greenPaint;
|
||||
greenPaint.setColor(SK_ColorGREEN);
|
||||
greenPaint.setStyle(SkPaint::kFill_Style);
|
||||
bottomHalfGreenCanvas.drawRect(0, 1, 2, 2, greenPaint);
|
||||
});
|
||||
LayerUpdateQueue layerUpdateQueue;
|
||||
SkRect dirty = SkRect::MakeLargest();
|
||||
std::vector<sp<RenderNode>> renderNodes;
|
||||
renderNodes.push_back(halfGreenNode);
|
||||
android::uirenderer::Rect contentDrawBounds(0, 0, 2, 2);
|
||||
auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
|
||||
auto surface = SkSurface::MakeRasterN32Premul(2, 2);
|
||||
surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
|
||||
pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN);
|
||||
pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, false, contentDrawBounds, surface);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned int)SK_ColorTRANSPARENT);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN);
|
||||
}
|
||||
|
||||
RENDERTHREAD_TEST(SkiaPipeline, renderFrameCheckDirtyRect) {
|
||||
auto redNode = TestUtils::createSkiaNode(0, 0, 2, 2,
|
||||
[](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
|
||||
redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
|
||||
});
|
||||
LayerUpdateQueue layerUpdateQueue;
|
||||
SkRect dirty = SkRect::MakeXYWH(0, 1, 2, 1);
|
||||
std::vector<sp<RenderNode>> renderNodes;
|
||||
renderNodes.push_back(redNode);
|
||||
android::uirenderer::Rect contentDrawBounds(0, 0, 2, 2);
|
||||
auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
|
||||
auto surface = SkSurface::MakeRasterN32Premul(2, 2);
|
||||
surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
|
||||
pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 1, 0), SK_ColorBLUE);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorRED);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 1, 1), SK_ColorRED);
|
||||
}
|
||||
|
||||
RENDERTHREAD_TEST(SkiaPipeline, renderLayer) {
|
||||
auto redNode = TestUtils::createSkiaNode(0, 0, 1, 1,
|
||||
[](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
|
||||
redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
|
||||
});
|
||||
auto surfaceLayer1 = SkSurface::MakeRasterN32Premul(1, 1);
|
||||
surfaceLayer1->getCanvas()->drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
|
||||
ASSERT_EQ(TestUtils::getColor(surfaceLayer1, 0, 0), SK_ColorWHITE);
|
||||
redNode->setLayerSurface(surfaceLayer1);
|
||||
|
||||
//create a 2nd 2x2 layer and add it to the queue as well.
|
||||
//make the layer's dirty area one half of the layer and verify only the dirty half is updated.
|
||||
auto blueNode = TestUtils::createSkiaNode(0, 0, 2, 2,
|
||||
[](RenderProperties& props, SkiaRecordingCanvas& blueCanvas) {
|
||||
blueCanvas.drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
|
||||
});
|
||||
auto surfaceLayer2 = SkSurface::MakeRasterN32Premul(2, 2);
|
||||
surfaceLayer2->getCanvas()->drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
|
||||
ASSERT_EQ(TestUtils::getColor(surfaceLayer2, 0, 0), SK_ColorWHITE);
|
||||
blueNode->setLayerSurface(surfaceLayer2);
|
||||
|
||||
//attach both layers to the update queue
|
||||
LayerUpdateQueue layerUpdateQueue;
|
||||
SkRect dirty = SkRect::MakeLargest();
|
||||
layerUpdateQueue.enqueueLayerWithDamage(redNode.get(), dirty);
|
||||
layerUpdateQueue.enqueueLayerWithDamage(blueNode.get(), SkRect::MakeWH(2, 1));
|
||||
ASSERT_EQ(layerUpdateQueue.entries().size(), 2UL);
|
||||
|
||||
bool opaque = true;
|
||||
FrameBuilder::LightGeometry lightGeometry;
|
||||
lightGeometry.radius = 1.0f;
|
||||
lightGeometry.center = { 0.0f, 0.0f, 0.0f };
|
||||
BakedOpRenderer::LightInfo lightInfo;
|
||||
auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
|
||||
pipeline->renderLayers(lightGeometry, &layerUpdateQueue, opaque, lightInfo);
|
||||
ASSERT_EQ(TestUtils::getColor(surfaceLayer1, 0, 0), SK_ColorRED);
|
||||
ASSERT_EQ(TestUtils::getColor(surfaceLayer2, 0, 0), SK_ColorBLUE);
|
||||
ASSERT_EQ(TestUtils::getColor(surfaceLayer2, 0, 1), SK_ColorWHITE);
|
||||
ASSERT_TRUE(layerUpdateQueue.entries().empty());
|
||||
redNode->setLayerSurface(sk_sp<SkSurface>());
|
||||
blueNode->setLayerSurface(sk_sp<SkSurface>());
|
||||
}
|
||||
Reference in New Issue
Block a user