Merge "SF: Handle buffer scale transforms when calculating layer geometry"
diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h
index a065a4c..916af69 100644
--- a/include/input/InputWindow.h
+++ b/include/input/InputWindow.h
@@ -116,7 +116,7 @@
INPUT_FEATURE_NO_INPUT_CHANNEL = 0x00000002,
INPUT_FEATURE_DISABLE_USER_ACTIVITY = 0x00000004,
};
-
+
/* These values are filled in by the WM and passed through SurfaceFlinger
* unless specified otherwise.
*/
@@ -165,6 +165,8 @@
int32_t displayId;
int32_t portalToDisplayId = ADISPLAY_ID_NONE;
InputApplicationInfo applicationInfo;
+ bool replaceTouchableRegionWithCrop;
+ wp<IBinder> touchableRegionCropHandle;
void addTouchableRegion(const Rect& region);
diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp
index 5c5613d..5a60347 100644
--- a/libs/input/InputWindow.cpp
+++ b/libs/input/InputWindow.cpp
@@ -98,7 +98,8 @@
output.writeInt32(portalToDisplayId);
applicationInfo.write(output);
output.write(touchableRegion);
-
+ output.writeBool(replaceTouchableRegionWithCrop);
+ output.writeWeakBinder(touchableRegionCropHandle);
return OK;
}
@@ -140,6 +141,8 @@
ret.portalToDisplayId = from.readInt32();
ret.applicationInfo = InputApplicationInfo::read(from);
from.read(ret.touchableRegion);
+ ret.replaceTouchableRegionWithCrop = from.readBool();
+ ret.touchableRegionCropHandle = from.readWeakBinder();
return ret;
}
diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp
index 09dd72b..6db18ab 100644
--- a/libs/input/tests/InputWindow_test.cpp
+++ b/libs/input/tests/InputWindow_test.cpp
@@ -37,6 +37,7 @@
}
TEST(InputWindowInfo, Parcelling) {
+ sp<IBinder> touchableRegionCropHandle = new BBinder();
InputWindowInfo i;
i.token = new BBinder();
i.name = "Foobar";
@@ -62,6 +63,8 @@
i.inputFeatures = 29;
i.displayId = 34;
i.portalToDisplayId = 2;
+ i.replaceTouchableRegionWithCrop = true;
+ i.touchableRegionCropHandle = touchableRegionCropHandle;
Parcel p;
i.write(p);
@@ -92,6 +95,8 @@
ASSERT_EQ(i.inputFeatures, i2.inputFeatures);
ASSERT_EQ(i.displayId, i2.displayId);
ASSERT_EQ(i.portalToDisplayId, i2.portalToDisplayId);
+ ASSERT_EQ(i.replaceTouchableRegionWithCrop, i2.replaceTouchableRegionWithCrop);
+ ASSERT_EQ(i.touchableRegionCropHandle, i2.touchableRegionCropHandle);
}
} // namespace test
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 8069a1a..f651309 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -432,10 +432,15 @@
}
GLESRenderEngine::~GLESRenderEngine() {
- for (const auto& image : mFramebufferImageCache) {
- eglDestroyImageKHR(mEGLDisplay, image.second);
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ unbindFrameBuffer(mDrawingBuffer.get());
+ mDrawingBuffer = nullptr;
+ while (!mFramebufferImageCache.empty()) {
+ EGLImageKHR expired = mFramebufferImageCache.front().second;
+ mFramebufferImageCache.pop_front();
+ eglDestroyImageKHR(mEGLDisplay, expired);
}
- mFramebufferImageCache.clear();
+ mImageCache.clear();
eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglTerminate(mEGLDisplay);
}
@@ -627,6 +632,16 @@
status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer,
sp<Fence> bufferFence) {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ return bindExternalTextureBufferLocked(texName, buffer, bufferFence);
+}
+
+status_t GLESRenderEngine::bindExternalTextureBufferLocked(uint32_t texName,
+ sp<GraphicBuffer> buffer,
+ sp<Fence> bufferFence) {
+ if (buffer == nullptr) {
+ return BAD_VALUE;
+ }
ATRACE_CALL();
auto cachedImage = mImageCache.find(buffer->getId());
@@ -675,6 +690,7 @@
}
void GLESRenderEngine::unbindExternalTextureBuffer(uint64_t bufferId) {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
const auto& cachedImage = mImageCache.find(bufferId);
if (cachedImage != mImageCache.end()) {
ALOGV("Destroying image for buffer: %" PRIu64, bufferId);
@@ -771,7 +787,7 @@
EGLImageKHR GLESRenderEngine::createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer,
bool isProtected) {
sp<GraphicBuffer> graphicBuffer = GraphicBuffer::from(nativeBuffer);
- uint32_t bufferId = graphicBuffer->getId();
+ uint64_t bufferId = graphicBuffer->getId();
for (const auto& image : mFramebufferImageCache) {
if (image.first == bufferId) {
return image.second;
@@ -810,114 +826,125 @@
sync_wait(bufferFence.get(), -1);
}
- BindNativeBufferAsFramebuffer fbo(*this, buffer);
-
- if (fbo.getStatus() != NO_ERROR) {
- ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
- buffer->handle);
- checkErrors();
- return fbo.getStatus();
+ if (buffer == nullptr) {
+ ALOGE("No output buffer provided. Aborting GPU composition.");
+ return BAD_VALUE;
}
- // clear the entire buffer, sometimes when we reuse buffers we'd persist
- // ghost images otherwise.
- // we also require a full transparent framebuffer for overlays. This is
- // probably not quite efficient on all GPUs, since we could filter out
- // opaque layers.
- clearWithColor(0.0, 0.0, 0.0, 0.0);
+ {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
- setViewportAndProjection(display.physicalDisplay, display.clip);
+ BindNativeBufferAsFramebuffer fbo(*this, buffer);
- setOutputDataSpace(display.outputDataspace);
- setDisplayMaxLuminance(display.maxLuminance);
-
- mat4 projectionMatrix = mState.projectionMatrix * display.globalTransform;
- mState.projectionMatrix = projectionMatrix;
- if (!display.clearRegion.isEmpty()) {
- glDisable(GL_BLEND);
- fillRegionWithColor(display.clearRegion, 0.0, 0.0, 0.0, 1.0);
- }
-
- Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2, 2);
- for (auto layer : layers) {
- mState.projectionMatrix = projectionMatrix * layer.geometry.positionTransform;
-
- const FloatRect bounds = layer.geometry.boundaries;
- Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
- position[0] = vec2(bounds.left, bounds.top);
- position[1] = vec2(bounds.left, bounds.bottom);
- position[2] = vec2(bounds.right, bounds.bottom);
- position[3] = vec2(bounds.right, bounds.top);
-
- setupLayerCropping(layer, mesh);
- setColorTransform(display.colorTransform * layer.colorTransform);
-
- bool usePremultipliedAlpha = true;
- bool disableTexture = true;
- bool isOpaque = false;
-
- if (layer.source.buffer.buffer != nullptr) {
- disableTexture = false;
- isOpaque = layer.source.buffer.isOpaque;
-
- sp<GraphicBuffer> gBuf = layer.source.buffer.buffer;
- bindExternalTextureBuffer(layer.source.buffer.textureName, gBuf,
- layer.source.buffer.fence);
-
- usePremultipliedAlpha = layer.source.buffer.usePremultipliedAlpha;
- Texture texture(Texture::TEXTURE_EXTERNAL, layer.source.buffer.textureName);
- mat4 texMatrix = layer.source.buffer.textureTransform;
-
- texture.setMatrix(texMatrix.asArray());
- texture.setFiltering(layer.source.buffer.useTextureFiltering);
-
- texture.setDimensions(gBuf->getWidth(), gBuf->getHeight());
- setSourceY410BT2020(layer.source.buffer.isY410BT2020);
-
- renderengine::Mesh::VertexArray<vec2> texCoords(mesh.getTexCoordArray<vec2>());
- texCoords[0] = vec2(0.0, 0.0);
- texCoords[1] = vec2(0.0, 1.0);
- texCoords[2] = vec2(1.0, 1.0);
- texCoords[3] = vec2(1.0, 0.0);
- setupLayerTexturing(texture);
- }
-
- const half3 solidColor = layer.source.solidColor;
- const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha);
- // Buffer sources will have a black solid color ignored in the shader,
- // so in that scenario the solid color passed here is arbitrary.
- setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color,
- layer.geometry.roundedCornersRadius);
- if (layer.disableBlending) {
- glDisable(GL_BLEND);
- }
- setSourceDataSpace(layer.sourceDataspace);
-
- drawMesh(mesh);
-
- // Cleanup if there's a buffer source
- if (layer.source.buffer.buffer != nullptr) {
- disableBlending();
- setSourceY410BT2020(false);
- disableTexturing();
- }
- }
-
- *drawFence = flush();
- // If flush failed or we don't support native fences, we need to force the
- // gl command stream to be executed.
- if (drawFence->get() < 0) {
- bool success = finish();
- if (!success) {
- ALOGE("Failed to flush RenderEngine commands");
+ if (fbo.getStatus() != NO_ERROR) {
+ ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
+ buffer->handle);
checkErrors();
- // Chances are, something illegal happened (either the caller passed
- // us bad parameters, or we messed up our shader generation).
- return INVALID_OPERATION;
+ return fbo.getStatus();
}
- }
- checkErrors();
+ // clear the entire buffer, sometimes when we reuse buffers we'd persist
+ // ghost images otherwise.
+ // we also require a full transparent framebuffer for overlays. This is
+ // probably not quite efficient on all GPUs, since we could filter out
+ // opaque layers.
+ clearWithColor(0.0, 0.0, 0.0, 0.0);
+
+ setViewportAndProjection(display.physicalDisplay, display.clip);
+
+ setOutputDataSpace(display.outputDataspace);
+ setDisplayMaxLuminance(display.maxLuminance);
+
+ mat4 projectionMatrix = mState.projectionMatrix * display.globalTransform;
+ mState.projectionMatrix = projectionMatrix;
+ if (!display.clearRegion.isEmpty()) {
+ glDisable(GL_BLEND);
+ fillRegionWithColor(display.clearRegion, 0.0, 0.0, 0.0, 1.0);
+ }
+
+ Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2, 2);
+ for (auto layer : layers) {
+ mState.projectionMatrix = projectionMatrix * layer.geometry.positionTransform;
+
+ const FloatRect bounds = layer.geometry.boundaries;
+ Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
+ position[0] = vec2(bounds.left, bounds.top);
+ position[1] = vec2(bounds.left, bounds.bottom);
+ position[2] = vec2(bounds.right, bounds.bottom);
+ position[3] = vec2(bounds.right, bounds.top);
+
+ setupLayerCropping(layer, mesh);
+ setColorTransform(display.colorTransform * layer.colorTransform);
+
+ bool usePremultipliedAlpha = true;
+ bool disableTexture = true;
+ bool isOpaque = false;
+
+ if (layer.source.buffer.buffer != nullptr) {
+ disableTexture = false;
+ isOpaque = layer.source.buffer.isOpaque;
+
+ sp<GraphicBuffer> gBuf = layer.source.buffer.buffer;
+ bindExternalTextureBufferLocked(layer.source.buffer.textureName, gBuf,
+ layer.source.buffer.fence);
+
+ usePremultipliedAlpha = layer.source.buffer.usePremultipliedAlpha;
+ Texture texture(Texture::TEXTURE_EXTERNAL, layer.source.buffer.textureName);
+ mat4 texMatrix = layer.source.buffer.textureTransform;
+
+ texture.setMatrix(texMatrix.asArray());
+ texture.setFiltering(layer.source.buffer.useTextureFiltering);
+
+ texture.setDimensions(gBuf->getWidth(), gBuf->getHeight());
+ setSourceY410BT2020(layer.source.buffer.isY410BT2020);
+
+ renderengine::Mesh::VertexArray<vec2> texCoords(mesh.getTexCoordArray<vec2>());
+ texCoords[0] = vec2(0.0, 0.0);
+ texCoords[1] = vec2(0.0, 1.0);
+ texCoords[2] = vec2(1.0, 1.0);
+ texCoords[3] = vec2(1.0, 0.0);
+ setupLayerTexturing(texture);
+ }
+
+ const half3 solidColor = layer.source.solidColor;
+ const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha);
+ // Buffer sources will have a black solid color ignored in the shader,
+ // so in that scenario the solid color passed here is arbitrary.
+ setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color,
+ layer.geometry.roundedCornersRadius);
+ if (layer.disableBlending) {
+ glDisable(GL_BLEND);
+ }
+ setSourceDataSpace(layer.sourceDataspace);
+
+ drawMesh(mesh);
+
+ // Cleanup if there's a buffer source
+ if (layer.source.buffer.buffer != nullptr) {
+ disableBlending();
+ setSourceY410BT2020(false);
+ disableTexturing();
+ }
+ }
+
+ if (drawFence != nullptr) {
+ *drawFence = flush();
+ }
+ // If flush failed or we don't support native fences, we need to force the
+ // gl command stream to be executed.
+ if (drawFence == nullptr || drawFence->get() < 0) {
+ bool success = finish();
+ if (!success) {
+ ALOGE("Failed to flush RenderEngine commands");
+ checkErrors();
+ // Chances are, something illegal happened (either the caller passed
+ // us bad parameters, or we messed up our shader generation).
+ return INVALID_OPERATION;
+ }
+ }
+
+ checkErrors();
+ }
return NO_ERROR;
}
@@ -1331,6 +1358,20 @@
return (isInputHdrDataSpace || isOutputHdrDataSpace) && inputTransfer != outputTransfer;
}
+bool GLESRenderEngine::isImageCachedForTesting(uint64_t bufferId) {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ const auto& cachedImage = mImageCache.find(bufferId);
+ return cachedImage != mImageCache.end();
+}
+
+bool GLESRenderEngine::isFramebufferImageCachedForTesting(uint64_t bufferId) {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ return std::any_of(mFramebufferImageCache.cbegin(), mFramebufferImageCache.cend(),
+ [=](std::pair<uint64_t, EGLImageKHR> image) {
+ return image.first == bufferId;
+ });
+}
+
// FlushTracer implementation
GLESRenderEngine::FlushTracer::FlushTracer(GLESRenderEngine* engine) : mEngine(engine) {
mThread = std::thread(&GLESRenderEngine::FlushTracer::loop, this);
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 728882a..efb6ef0 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -56,7 +56,7 @@
EGLDisplay display, EGLConfig config, EGLContext ctxt, EGLSurface dummy,
EGLContext protectedContext, EGLSurface protectedDummy,
uint32_t imageCacheSize);
- ~GLESRenderEngine() override;
+ ~GLESRenderEngine() override EXCLUDES(mRenderingMutex);
std::unique_ptr<Framebuffer> createFramebuffer() override;
std::unique_ptr<Image> createImage() override;
@@ -74,8 +74,9 @@
void genTextures(size_t count, uint32_t* names) override;
void deleteTextures(size_t count, uint32_t const* names) override;
void bindExternalTextureImage(uint32_t texName, const Image& image) override;
- status_t bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer, sp<Fence> fence);
- void unbindExternalTextureBuffer(uint64_t bufferId);
+ status_t bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer, sp<Fence> fence)
+ EXCLUDES(mRenderingMutex);
+ void unbindExternalTextureBuffer(uint64_t bufferId) EXCLUDES(mRenderingMutex);
status_t bindFrameBuffer(Framebuffer* framebuffer) override;
void unbindFrameBuffer(Framebuffer* framebuffer) override;
void checkErrors() const override;
@@ -85,7 +86,7 @@
bool useProtectedContext(bool useProtectedContext) override;
status_t drawLayers(const DisplaySettings& display, const std::vector<LayerSettings>& layers,
ANativeWindowBuffer* buffer, base::unique_fd&& bufferFence,
- base::unique_fd* drawFence) override;
+ base::unique_fd* drawFence) EXCLUDES(mRenderingMutex) override;
// internal to RenderEngine
EGLDisplay getEGLDisplay() const { return mEGLDisplay; }
@@ -93,6 +94,12 @@
// Creates an output image for rendering to
EGLImageKHR createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer, bool isProtected);
+ // Test-only methods
+ // Returns true iff mImageCache contains an image keyed by bufferId
+ bool isImageCachedForTesting(uint64_t bufferId) EXCLUDES(mRenderingMutex);
+ // Returns true iff mFramebufferImageCache contains an image keyed by bufferId
+ bool isFramebufferImageCachedForTesting(uint64_t bufferId) EXCLUDES(mRenderingMutex);
+
protected:
Framebuffer* getFramebufferForDrawing() override;
void dump(std::string& result) override;
@@ -197,10 +204,17 @@
const bool mUseColorManagement = false;
// Cache of GL images that we'll store per GraphicBuffer ID
- // TODO: Layer should call back on destruction instead to clean this up,
- // as it has better system utilization at the potential expense of a
- // more complicated interface.
- std::unordered_map<uint64_t, std::unique_ptr<Image>> mImageCache;
+ std::unordered_map<uint64_t, std::unique_ptr<Image>> mImageCache GUARDED_BY(mRenderingMutex);
+ // Mutex guarding rendering operations, so that:
+ // 1. GL operations aren't interleaved, and
+ // 2. Internal state related to rendering that is potentially modified by
+ // multiple threads is guaranteed thread-safe.
+ std::mutex mRenderingMutex;
+
+ // See bindExternalTextureBuffer above, but requiring that mRenderingMutex
+ // is held.
+ status_t bindExternalTextureBufferLocked(uint32_t texName, sp<GraphicBuffer> buffer,
+ sp<Fence> fence) REQUIRES(mRenderingMutex);
std::unique_ptr<Framebuffer> mDrawingBuffer;
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index a2bbaff..8c93cf4 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -19,6 +19,7 @@
#include <renderengine/RenderEngine.h>
#include <sync/sync.h>
#include <ui/PixelFormat.h>
+#include "../gl/GLESRenderEngine.h"
constexpr int DEFAULT_DISPLAY_WIDTH = 128;
constexpr int DEFAULT_DISPLAY_HEIGHT = 256;
@@ -27,6 +28,19 @@
namespace android {
struct RenderEngineTest : public ::testing::Test {
+ static void SetUpTestSuite() {
+ sRE = renderengine::gl::GLESRenderEngine::create(static_cast<int32_t>(
+ ui::PixelFormat::RGBA_8888),
+ 0, 1);
+ }
+
+ static void TearDownTestSuite() {
+ // The ordering here is important - sCurrentBuffer must live longer
+ // than RenderEngine to avoid a null reference on tear-down.
+ sRE = nullptr;
+ sCurrentBuffer = nullptr;
+ }
+
static sp<GraphicBuffer> allocateDefaultBuffer() {
return new GraphicBuffer(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT,
HAL_PIXEL_FORMAT_RGBA_8888, 1,
@@ -101,12 +115,12 @@
DEFAULT_DISPLAY_HEIGHT - DEFAULT_DISPLAY_OFFSET);
}
- static void invokeDraw(renderengine::DisplaySettings settings,
- std::vector<renderengine::LayerSettings> layers,
- sp<GraphicBuffer> buffer) {
+ void invokeDraw(renderengine::DisplaySettings settings,
+ std::vector<renderengine::LayerSettings> layers, sp<GraphicBuffer> buffer) {
base::unique_fd fence;
status_t status = sRE->drawLayers(settings, layers, buffer->getNativeBuffer(),
base::unique_fd(), &fence);
+ sCurrentBuffer = buffer;
int fd = fence.release();
if (fd >= 0) {
@@ -115,9 +129,12 @@
}
ASSERT_EQ(NO_ERROR, status);
+ if (layers.size() > 0) {
+ ASSERT_TRUE(sRE->isFramebufferImageCachedForTesting(buffer->getId()));
+ }
}
- static void drawEmptyLayers() {
+ void drawEmptyLayers() {
renderengine::DisplaySettings settings;
std::vector<renderengine::LayerSettings> layers;
// Meaningless buffer since we don't do any drawing
@@ -200,17 +217,22 @@
void clearRegion();
- // Dumb hack to get aroud the fact that tear-down for renderengine isn't
- // well defined right now, so we can't create multiple instances
- static std::unique_ptr<renderengine::RenderEngine> sRE;
+ // Keep around the same renderengine object to save on initialization time.
+ // For now, exercise the GL backend directly so that some caching specifics
+ // can be tested without changing the interface.
+ static std::unique_ptr<renderengine::gl::GLESRenderEngine> sRE;
+ // Dumb hack to avoid NPE in the EGL driver: the GraphicBuffer needs to
+ // be freed *after* RenderEngine is destroyed, so that the EGL image is
+ // destroyed first.
+ static sp<GraphicBuffer> sCurrentBuffer;
sp<GraphicBuffer> mBuffer;
std::vector<uint32_t> mTexNames;
};
-std::unique_ptr<renderengine::RenderEngine> RenderEngineTest::sRE =
- renderengine::RenderEngine::create(static_cast<int32_t>(ui::PixelFormat::RGBA_8888), 0, 1);
+std::unique_ptr<renderengine::gl::GLESRenderEngine> RenderEngineTest::sRE = nullptr;
+sp<GraphicBuffer> RenderEngineTest::sCurrentBuffer = nullptr;
struct ColorSourceVariant {
static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b,
@@ -245,7 +267,7 @@
RenderEngineTest* fixture) {
sp<GraphicBuffer> buf = RenderEngineTest::allocateSourceBuffer(1, 1);
uint32_t texName;
- RenderEngineTest::sRE->genTextures(1, &texName);
+ fixture->sRE->genTextures(1, &texName);
fixture->mTexNames.push_back(texName);
uint8_t* pixels;
@@ -740,6 +762,38 @@
drawEmptyLayers();
}
+TEST_F(RenderEngineTest, drawLayers_nullOutputBuffer) {
+ renderengine::DisplaySettings settings;
+ std::vector<renderengine::LayerSettings> layers;
+ renderengine::LayerSettings layer;
+ layer.geometry.boundaries = fullscreenRect().toFloatRect();
+ BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
+ layers.push_back(layer);
+ base::unique_fd fence;
+ status_t status = sRE->drawLayers(settings, layers, nullptr, base::unique_fd(), &fence);
+
+ ASSERT_EQ(BAD_VALUE, status);
+}
+
+TEST_F(RenderEngineTest, drawLayers_nullOutputFence) {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+
+ std::vector<renderengine::LayerSettings> layers;
+ renderengine::LayerSettings layer;
+ layer.geometry.boundaries = fullscreenRect().toFloatRect();
+ BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
+ layer.alpha = 1.0;
+ layers.push_back(layer);
+
+ status_t status = sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(),
+ base::unique_fd(), nullptr);
+ sCurrentBuffer = mBuffer;
+ ASSERT_EQ(NO_ERROR, status);
+ expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
+}
+
TEST_F(RenderEngineTest, drawLayers_fillRedBuffer_colorSource) {
fillRedBuffer<ColorSourceVariant>();
}
@@ -912,4 +966,41 @@
clearRegion();
}
+TEST_F(RenderEngineTest, drawLayers_fillsBufferAndCachesImages) {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+
+ std::vector<renderengine::LayerSettings> layers;
+
+ renderengine::LayerSettings layer;
+ layer.geometry.boundaries = fullscreenRect().toFloatRect();
+ BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
+
+ layers.push_back(layer);
+ invokeDraw(settings, layers, mBuffer);
+ uint64_t bufferId = layer.source.buffer.buffer->getId();
+ EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId));
+ sRE->unbindExternalTextureBuffer(bufferId);
+ EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
+}
+
+TEST_F(RenderEngineTest, drawLayers_bindExternalBufferWithNullBuffer) {
+ status_t result = sRE->bindExternalTextureBuffer(0, nullptr, nullptr);
+ ASSERT_EQ(BAD_VALUE, result);
+}
+
+TEST_F(RenderEngineTest, drawLayers_bindExternalBufferCachesImages) {
+ sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
+ uint32_t texName;
+ sRE->genTextures(1, &texName);
+ mTexNames.push_back(texName);
+
+ sRE->bindExternalTextureBuffer(texName, buf, nullptr);
+ uint64_t bufferId = buf->getId();
+ EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId));
+ sRE->unbindExternalTextureBuffer(bufferId);
+ EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
+}
+
} // namespace android
diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp
index d13942d..28c3f7b 100644
--- a/libs/ui/Transform.cpp
+++ b/libs/ui/Transform.cpp
@@ -381,6 +381,37 @@
return (getOrientation() & ROT_INVALID) ? false : true;
}
+mat4 Transform::asMatrix4() const {
+ // Internally Transform uses a 3x3 matrix since the transform is meant for
+ // two-dimensional values. An equivalent 4x4 matrix means inserting an extra
+ // row and column which adds as an identity transform on the third
+ // dimension.
+
+ mat4 m = mat4{mat4::NO_INIT}; // NO_INIT since we explicitly set every element
+
+ m[0][0] = mMatrix[0][0];
+ m[0][1] = mMatrix[0][1];
+ m[0][2] = 0.f;
+ m[0][3] = mMatrix[0][2];
+
+ m[1][0] = mMatrix[1][0];
+ m[1][1] = mMatrix[1][1];
+ m[1][2] = 0.f;
+ m[1][3] = mMatrix[1][2];
+
+ m[2][0] = 0.f;
+ m[2][1] = 0.f;
+ m[2][2] = 1.f;
+ m[2][3] = 0.f;
+
+ m[3][0] = mMatrix[2][0];
+ m[3][1] = mMatrix[2][1];
+ m[3][2] = 0.f;
+ m[3][3] = mMatrix[2][2];
+
+ return m;
+}
+
void Transform::dump(std::string& out, const char* name) const {
using android::base::StringAppendF;
diff --git a/libs/ui/include/ui/Transform.h b/libs/ui/include/ui/Transform.h
index dcb26cf..f29a370 100644
--- a/libs/ui/include/ui/Transform.h
+++ b/libs/ui/include/ui/Transform.h
@@ -22,6 +22,7 @@
#include <string>
#include <hardware/hardware.h>
+#include <math/mat4.h>
#include <math/vec2.h>
#include <math/vec3.h>
#include <ui/Point.h>
@@ -88,6 +89,9 @@
vec2 transform(const vec2& v) const;
vec3 transform(const vec3& v) const;
+ // Expands from the internal 3x3 matrix to an equivalent 4x4 matrix
+ mat4 asMatrix4() const;
+
Transform inverse() const;
// for debugging
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index bb18aa1..4751e5f 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -105,6 +105,10 @@
return getEffectiveScalingMode() != NATIVE_WINDOW_SCALING_MODE_FREEZE;
}
+bool BufferLayer::usesSourceCrop() const {
+ return true;
+}
+
static constexpr mat4 inverseOrientation(uint32_t transform) {
const mat4 flipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
const mat4 flipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1);
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 4f3ad41..dc103cb 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -78,6 +78,8 @@
// isFixedSize - true if content has a fixed size
bool isFixedSize() const override;
+ bool usesSourceCrop() const override;
+
bool isHdrY410() const override;
void setPerFrameData(const sp<const DisplayDevice>& display, const ui::Transform& transform,
diff --git a/services/surfaceflinger/ColorLayer.h b/services/surfaceflinger/ColorLayer.h
index bd83d1a..53d5b5b 100644
--- a/services/surfaceflinger/ColorLayer.h
+++ b/services/surfaceflinger/ColorLayer.h
@@ -46,11 +46,10 @@
bool onPreComposition(nsecs_t /*refreshStartTime*/) override { return false; }
protected:
- FloatRect computeCrop(const sp<const DisplayDevice>& /*display*/) const override { return {}; }
- bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
- bool useIdentityTransform, Region& clearRegion,
- const bool supportProtectedContent,
- renderengine::LayerSettings& layer) override;
+ virtual bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
+ bool useIdentityTransform, Region& clearRegion,
+ const bool supportProtectedContent,
+ renderengine::LayerSettings& layer);
private:
std::shared_ptr<compositionengine::Layer> mCompositionLayer;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index 6cc87ba..9f635b9 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -24,12 +24,21 @@
namespace compositionengine {
+struct LayerFECompositionState;
+
// Defines the interface used by the CompositionEngine to make requests
// of the front-end layer
class LayerFE : public virtual RefBase {
public:
+ // Latches the output-independent state. If includeGeometry is false, the
+ // geometry state can be skipped.
+ virtual void latchCompositionState(LayerFECompositionState&, bool includeGeometry) const = 0;
+
// Called after the layer is displayed to update the presentation fence
virtual void onLayerDisplayed(const sp<Fence>&) = 0;
+
+ // Gets some kind of identifier for the layer for debug purposes.
+ virtual const char* getDebugName() const = 0;
};
} // namespace compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 785a6d7..e6ee078 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -41,6 +41,22 @@
Region geomVisibleRegion;
/*
+ * Geometry state
+ */
+
+ bool isSecure{false};
+ bool geomUsesSourceCrop{false};
+ bool geomBufferUsesDisplayInverseTransform{false};
+ uint32_t geomBufferTransform{0};
+ ui::Transform geomLayerTransform;
+ ui::Transform geomInverseLayerTransform;
+ Rect geomBufferSize;
+ Rect geomContentCrop;
+ Rect geomCrop;
+ Region geomActiveTransparentRegion;
+ FloatRect geomLayerBounds;
+
+ /*
* Presentation
*/
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index e7a17c4..cd63b57 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -64,6 +64,15 @@
// TODO(lpique): Make this protected once it is only internally called.
virtual CompositionState& editState() = 0;
+ // Recalculates the state of the output layer from the output-independent
+ // layer. If includeGeometry is false, the geometry state can be skipped.
+ virtual void updateCompositionState(bool includeGeometry) = 0;
+
+ // Writes the geometry state to the HWC, or does nothing if this layer does
+ // not use the HWC. If includeGeometry is false, the geometry state can be
+ // skipped.
+ virtual void writeStateToHWC(bool includeGeometry) const = 0;
+
// Debugging
virtual void dump(std::string& result) const = 0;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index d8f0cdd..6a4818f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -21,6 +21,8 @@
#include <compositionengine/OutputLayer.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <ui/FloatRect.h>
+#include <ui/Rect.h>
#include "DisplayHardware/DisplayIdentification.h"
@@ -41,9 +43,18 @@
const OutputLayerCompositionState& getState() const override;
OutputLayerCompositionState& editState() override;
+ void updateCompositionState(bool) override;
+ void writeStateToHWC(bool) const override;
+
void dump(std::string& result) const override;
+ virtual FloatRect calculateOutputSourceCrop() const;
+ virtual Rect calculateOutputDisplayFrame() const;
+ virtual uint32_t calculateOutputRelativeBufferTransform() const;
+
private:
+ Rect calculateInitialCrop() const;
+
const compositionengine::Output& mOutput;
std::shared_ptr<compositionengine::Layer> mLayer;
sp<compositionengine::LayerFE> mLayerFE;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index a0c2a63..aab18db 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -17,6 +17,7 @@
#pragma once
#include <compositionengine/LayerFE.h>
+#include <compositionengine/LayerFECompositionState.h>
#include <gmock/gmock.h>
#include <ui/Fence.h>
@@ -29,7 +30,10 @@
LayerFE();
virtual ~LayerFE();
+ MOCK_CONST_METHOD2(latchCompositionState, void(LayerFECompositionState&, bool));
MOCK_METHOD1(onLayerDisplayed, void(const sp<Fence>&));
+
+ MOCK_CONST_METHOD0(getDebugName, const char*());
};
} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
index 54c7407..29cd08a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -38,6 +38,9 @@
MOCK_CONST_METHOD0(getState, const impl::OutputLayerCompositionState&());
MOCK_METHOD0(editState, impl::OutputLayerCompositionState&());
+ MOCK_METHOD1(updateCompositionState, void(bool));
+ MOCK_CONST_METHOD1(writeStateToHWC, void(bool));
+
MOCK_CONST_METHOD1(dump, void(std::string&));
};
diff --git a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
index c497013..9598430 100644
--- a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
@@ -24,7 +24,7 @@
using android::base::StringAppendF;
void dumpVal(std::string& out, const char* name, bool value) {
- StringAppendF(&out, "%s=%c ", name, value ? 'T' : 'F');
+ StringAppendF(&out, "%s=%s ", name, value ? "true" : "false");
}
void dumpVal(std::string& out, const char* name, const void* value) {
@@ -56,7 +56,7 @@
}
void dumpVal(std::string& out, const char* name, const char* valueName, int value) {
- StringAppendF(&out, "%s=%s (%d)", name, valueName, value);
+ StringAppendF(&out, "%s=%s (%d) ", name, valueName, value);
}
void dumpVal(std::string& out, const char* name, const std::string& valueName, int value) {
diff --git a/services/surfaceflinger/CompositionEngine/src/Layer.cpp b/services/surfaceflinger/CompositionEngine/src/Layer.cpp
index 109e9f8..96e9731 100644
--- a/services/surfaceflinger/CompositionEngine/src/Layer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Layer.cpp
@@ -52,7 +52,9 @@
}
void Layer::dump(std::string& out) const {
- android::base::StringAppendF(&out, " Layer %p\n", this);
+ auto layerFE = getLayerFE();
+ android::base::StringAppendF(&out, "* compositionengine::Layer %p (%s)\n", this,
+ layerFE ? layerFE->getDebugName() : "<unknown>");
mState.dump(out);
}
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp
index 517b641..40c4da9 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp
@@ -31,6 +31,25 @@
void dumpFrontEnd(std::string& out, const LayerFECompositionState& state) {
out.append(" ");
+ dumpVal(out, "isSecure", state.isSecure);
+ dumpVal(out, "geomUsesSourceCrop", state.geomUsesSourceCrop);
+ dumpVal(out, "geomBufferUsesDisplayInverseTransform",
+ state.geomBufferUsesDisplayInverseTransform);
+ dumpVal(out, "geomLayerTransform", state.geomLayerTransform);
+
+ out.append("\n ");
+ dumpVal(out, "geomBufferSize", state.geomBufferSize);
+ dumpVal(out, "geomContentCrop", state.geomContentCrop);
+ dumpVal(out, "geomCrop", state.geomCrop);
+ dumpVal(out, "geomBufferTransform", state.geomBufferTransform);
+
+ out.append("\n ");
+ dumpVal(out, "geomActiveTransparentRegion", state.geomActiveTransparentRegion);
+
+ out.append(" ");
+ dumpVal(out, "geomLayerBounds", state.geomLayerBounds);
+
+ out.append("\n ");
dumpVal(out, "blend", toString(state.blendMode), state.blendMode);
dumpVal(out, "alpha", state.alpha);
@@ -61,7 +80,7 @@
} // namespace
void LayerCompositionState::dump(std::string& out) const {
- out.append(" frontend:\n");
+ out.append(" frontend:\n");
dumpFrontEnd(out, frontEnd);
}
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index ad4c7bf..d22bdaf 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -147,7 +147,7 @@
out.append(" No render surface!\n");
}
- out.append("\n %d Layers", mOutputLayersOrderedByZ.size());
+ android::base::StringAppendF(&out, "\n %zu Layers\b", mOutputLayersOrderedByZ.size());
for (const auto& outputLayer : mOutputLayersOrderedByZ) {
if (!outputLayer) {
continue;
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
index 78807ff..9549054 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -20,6 +20,7 @@
namespace android::compositionengine::impl {
void OutputCompositionState::dump(std::string& out) const {
+ out.append(" ");
dumpVal(out, "isEnabled", isEnabled);
dumpVal(out, "isSecure", isSecure);
@@ -37,7 +38,7 @@
dumpVal(out, "scissor", scissor);
dumpVal(out, "needsFiltering", needsFiltering);
- out.append("\n");
+ out.append("\n ");
dumpVal(out, "colorMode", toString(colorMode), colorMode);
dumpVal(out, "renderIntent", toString(renderIntent), renderIntent);
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 10da49d..379ad87 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -19,7 +19,10 @@
#include <compositionengine/Layer.h>
#include <compositionengine/LayerFE.h>
#include <compositionengine/Output.h>
+#include <compositionengine/impl/LayerCompositionState.h>
+#include <compositionengine/impl/OutputCompositionState.h>
#include <compositionengine/impl/OutputLayer.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
#include "DisplayHardware/HWComposer.h"
@@ -29,6 +32,18 @@
namespace impl {
+namespace {
+
+FloatRect reduce(const FloatRect& win, const Region& exclude) {
+ if (CC_LIKELY(exclude.isEmpty())) {
+ return win;
+ }
+ // Convert through Rect (by rounding) for lack of FloatRegion
+ return Region(Rect{win}).subtract(exclude).getBounds().toFloatRect();
+}
+
+} // namespace
+
std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(
const CompositionEngine& compositionEngine, std::optional<DisplayId> displayId,
const compositionengine::Output& output, std::shared_ptr<compositionengine::Layer> layer,
@@ -77,10 +92,296 @@
return mState;
}
+Rect OutputLayer::calculateInitialCrop() const {
+ const auto& layerState = mLayer->getState().frontEnd;
+
+ // apply the projection's clipping to the window crop in
+ // layerstack space, and convert-back to layer space.
+ // if there are no window scaling involved, this operation will map to full
+ // pixels in the buffer.
+
+ FloatRect activeCropFloat =
+ reduce(layerState.geomLayerBounds, layerState.geomActiveTransparentRegion);
+
+ const Rect& viewport = mOutput.getState().viewport;
+ const ui::Transform& layerTransform = layerState.geomLayerTransform;
+ const ui::Transform& inverseLayerTransform = layerState.geomInverseLayerTransform;
+ // Transform to screen space.
+ activeCropFloat = layerTransform.transform(activeCropFloat);
+ activeCropFloat = activeCropFloat.intersect(viewport.toFloatRect());
+ // Back to layer space to work with the content crop.
+ activeCropFloat = inverseLayerTransform.transform(activeCropFloat);
+
+ // This needs to be here as transform.transform(Rect) computes the
+ // transformed rect and then takes the bounding box of the result before
+ // returning. This means
+ // transform.inverse().transform(transform.transform(Rect)) != Rect
+ // in which case we need to make sure the final rect is clipped to the
+ // display bounds.
+ Rect activeCrop{activeCropFloat};
+ if (!activeCrop.intersect(layerState.geomBufferSize, &activeCrop)) {
+ activeCrop.clear();
+ }
+ return activeCrop;
+}
+
+FloatRect OutputLayer::calculateOutputSourceCrop() const {
+ const auto& layerState = mLayer->getState().frontEnd;
+ const auto& outputState = mOutput.getState();
+
+ if (!layerState.geomUsesSourceCrop) {
+ return {};
+ }
+
+ // the content crop is the area of the content that gets scaled to the
+ // layer's size. This is in buffer space.
+ FloatRect crop = layerState.geomContentCrop.toFloatRect();
+
+ // In addition there is a WM-specified crop we pull from our drawing state.
+ Rect activeCrop = calculateInitialCrop();
+ const Rect& bufferSize = layerState.geomBufferSize;
+
+ int winWidth = bufferSize.getWidth();
+ int winHeight = bufferSize.getHeight();
+
+ // The bufferSize for buffer state layers can be unbounded ([0, 0, -1, -1])
+ // if display frame hasn't been set and the parent is an unbounded layer.
+ if (winWidth < 0 && winHeight < 0) {
+ return crop;
+ }
+
+ // Transform the window crop to match the buffer coordinate system,
+ // which means using the inverse of the current transform set on the
+ // SurfaceFlingerConsumer.
+ uint32_t invTransform = layerState.geomBufferTransform;
+ if (layerState.geomBufferUsesDisplayInverseTransform) {
+ /*
+ * the code below applies the primary display's inverse transform to the
+ * buffer
+ */
+ uint32_t invTransformOrient = outputState.orientation;
+ // calculate the inverse transform
+ if (invTransformOrient & HAL_TRANSFORM_ROT_90) {
+ invTransformOrient ^= HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_FLIP_H;
+ }
+ // and apply to the current transform
+ invTransform =
+ (ui::Transform(invTransformOrient) * ui::Transform(invTransform)).getOrientation();
+ }
+
+ if (invTransform & HAL_TRANSFORM_ROT_90) {
+ // If the activeCrop has been rotate the ends are rotated but not
+ // the space itself so when transforming ends back we can't rely on
+ // a modification of the axes of rotation. To account for this we
+ // need to reorient the inverse rotation in terms of the current
+ // axes of rotation.
+ bool is_h_flipped = (invTransform & HAL_TRANSFORM_FLIP_H) != 0;
+ bool is_v_flipped = (invTransform & HAL_TRANSFORM_FLIP_V) != 0;
+ if (is_h_flipped == is_v_flipped) {
+ invTransform ^= HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_FLIP_H;
+ }
+ std::swap(winWidth, winHeight);
+ }
+ const Rect winCrop =
+ activeCrop.transform(invTransform, bufferSize.getWidth(), bufferSize.getHeight());
+
+ // below, crop is intersected with winCrop expressed in crop's coordinate space
+ float xScale = crop.getWidth() / float(winWidth);
+ float yScale = crop.getHeight() / float(winHeight);
+
+ float insetL = winCrop.left * xScale;
+ float insetT = winCrop.top * yScale;
+ float insetR = (winWidth - winCrop.right) * xScale;
+ float insetB = (winHeight - winCrop.bottom) * yScale;
+
+ crop.left += insetL;
+ crop.top += insetT;
+ crop.right -= insetR;
+ crop.bottom -= insetB;
+
+ return crop;
+}
+
+Rect OutputLayer::calculateOutputDisplayFrame() const {
+ const auto& layerState = mLayer->getState().frontEnd;
+ const auto& outputState = mOutput.getState();
+
+ // apply the layer's transform, followed by the display's global transform
+ // here we're guaranteed that the layer's transform preserves rects
+ Region activeTransparentRegion = layerState.geomActiveTransparentRegion;
+ const ui::Transform& layerTransform = layerState.geomLayerTransform;
+ const ui::Transform& inverseLayerTransform = layerState.geomInverseLayerTransform;
+ const Rect& bufferSize = layerState.geomBufferSize;
+ Rect activeCrop = layerState.geomCrop;
+ if (!activeCrop.isEmpty() && bufferSize.isValid()) {
+ activeCrop = layerTransform.transform(activeCrop);
+ if (!activeCrop.intersect(outputState.viewport, &activeCrop)) {
+ activeCrop.clear();
+ }
+ activeCrop = inverseLayerTransform.transform(activeCrop, true);
+ // This needs to be here as transform.transform(Rect) computes the
+ // transformed rect and then takes the bounding box of the result before
+ // returning. This means
+ // transform.inverse().transform(transform.transform(Rect)) != Rect
+ // in which case we need to make sure the final rect is clipped to the
+ // display bounds.
+ if (!activeCrop.intersect(bufferSize, &activeCrop)) {
+ activeCrop.clear();
+ }
+ // mark regions outside the crop as transparent
+ activeTransparentRegion.orSelf(Rect(0, 0, bufferSize.getWidth(), activeCrop.top));
+ activeTransparentRegion.orSelf(
+ Rect(0, activeCrop.bottom, bufferSize.getWidth(), bufferSize.getHeight()));
+ activeTransparentRegion.orSelf(Rect(0, activeCrop.top, activeCrop.left, activeCrop.bottom));
+ activeTransparentRegion.orSelf(
+ Rect(activeCrop.right, activeCrop.top, bufferSize.getWidth(), activeCrop.bottom));
+ }
+
+ // reduce uses a FloatRect to provide more accuracy during the
+ // transformation. We then round upon constructing 'frame'.
+ Rect frame{
+ layerTransform.transform(reduce(layerState.geomLayerBounds, activeTransparentRegion))};
+ if (!frame.intersect(outputState.viewport, &frame)) {
+ frame.clear();
+ }
+ const ui::Transform displayTransform{outputState.transform};
+
+ return displayTransform.transform(frame);
+}
+
+uint32_t OutputLayer::calculateOutputRelativeBufferTransform() const {
+ const auto& layerState = mLayer->getState().frontEnd;
+ const auto& outputState = mOutput.getState();
+
+ /*
+ * Transformations are applied in this order:
+ * 1) buffer orientation/flip/mirror
+ * 2) state transformation (window manager)
+ * 3) layer orientation (screen orientation)
+ * (NOTE: the matrices are multiplied in reverse order)
+ */
+ const ui::Transform& layerTransform = layerState.geomLayerTransform;
+ const ui::Transform displayTransform{outputState.orientation};
+ const ui::Transform bufferTransform{layerState.geomBufferTransform};
+ ui::Transform transform(displayTransform * layerTransform * bufferTransform);
+
+ if (layerState.geomBufferUsesDisplayInverseTransform) {
+ /*
+ * the code below applies the primary display's inverse transform to the
+ * buffer
+ */
+ uint32_t invTransform = outputState.orientation;
+ // calculate the inverse transform
+ if (invTransform & HAL_TRANSFORM_ROT_90) {
+ invTransform ^= HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_FLIP_H;
+ }
+
+ /*
+ * Here we cancel out the orientation component of the WM transform.
+ * The scaling and translate components are already included in our bounds
+ * computation so it's enough to just omit it in the composition.
+ * See comment in BufferLayer::prepareClientLayer with ref to b/36727915 for why.
+ */
+ transform =
+ ui::Transform(invTransform) * displayTransform * layerTransform * bufferTransform;
+ }
+
+ // this gives us only the "orientation" component of the transform
+ return transform.getOrientation();
+} // namespace impl
+
+void OutputLayer::updateCompositionState(bool includeGeometry) {
+ if (includeGeometry) {
+ mState.displayFrame = calculateOutputDisplayFrame();
+ mState.sourceCrop = calculateOutputSourceCrop();
+ mState.bufferTransform =
+ static_cast<Hwc2::Transform>(calculateOutputRelativeBufferTransform());
+
+ if ((mLayer->getState().frontEnd.isSecure && !mOutput.getState().isSecure) ||
+ (mState.bufferTransform & ui::Transform::ROT_INVALID)) {
+ mState.forceClientComposition = true;
+ }
+ }
+}
+
+void OutputLayer::writeStateToHWC(bool includeGeometry) const {
+ // Skip doing this if there is no HWC interface
+ if (!mState.hwc) {
+ return;
+ }
+
+ auto& hwcLayer = (*mState.hwc).hwcLayer;
+ if (!hwcLayer) {
+ ALOGE("[%s] failed to write composition state to HWC -- no hwcLayer for output %s",
+ mLayerFE->getDebugName(), mOutput.getName().c_str());
+ return;
+ }
+
+ if (includeGeometry) {
+ // Output dependent state
+
+ if (auto error = hwcLayer->setDisplayFrame(mState.displayFrame);
+ error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to set display frame [%d, %d, %d, %d]: %s (%d)",
+ mLayerFE->getDebugName(), mState.displayFrame.left, mState.displayFrame.top,
+ mState.displayFrame.right, mState.displayFrame.bottom, to_string(error).c_str(),
+ static_cast<int32_t>(error));
+ }
+
+ if (auto error = hwcLayer->setSourceCrop(mState.sourceCrop); error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to set source crop [%.3f, %.3f, %.3f, %.3f]: "
+ "%s (%d)",
+ mLayerFE->getDebugName(), mState.sourceCrop.left, mState.sourceCrop.top,
+ mState.sourceCrop.right, mState.sourceCrop.bottom, to_string(error).c_str(),
+ static_cast<int32_t>(error));
+ }
+
+ if (auto error = hwcLayer->setZOrder(mState.z); error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to set Z %u: %s (%d)", mLayerFE->getDebugName(), mState.z,
+ to_string(error).c_str(), static_cast<int32_t>(error));
+ }
+
+ if (auto error =
+ hwcLayer->setTransform(static_cast<HWC2::Transform>(mState.bufferTransform));
+ error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to set transform %s: %s (%d)", mLayerFE->getDebugName(),
+ toString(mState.bufferTransform).c_str(), to_string(error).c_str(),
+ static_cast<int32_t>(error));
+ }
+
+ // Output independent state
+
+ const auto& outputIndependentState = mLayer->getState().frontEnd;
+
+ if (auto error = hwcLayer->setBlendMode(
+ static_cast<HWC2::BlendMode>(outputIndependentState.blendMode));
+ error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to set blend mode %s: %s (%d)", mLayerFE->getDebugName(),
+ toString(outputIndependentState.blendMode).c_str(), to_string(error).c_str(),
+ static_cast<int32_t>(error));
+ }
+
+ if (auto error = hwcLayer->setPlaneAlpha(outputIndependentState.alpha);
+ error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to set plane alpha %.3f: %s (%d)", mLayerFE->getDebugName(),
+ outputIndependentState.alpha, to_string(error).c_str(),
+ static_cast<int32_t>(error));
+ }
+
+ if (auto error =
+ hwcLayer->setInfo(outputIndependentState.type, outputIndependentState.appId);
+ error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to set info %s (%d)", mLayerFE->getDebugName(),
+ to_string(error).c_str(), static_cast<int32_t>(error));
+ }
+ }
+}
+
void OutputLayer::dump(std::string& out) const {
using android::base::StringAppendF;
- StringAppendF(&out, " Output Layer %p\n", this);
+ StringAppendF(&out, " - Output Layer %p (Composition layer %p) (%s)\n", this, mLayer.get(),
+ mLayerFE->getDebugName());
mState.dump(out);
}
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
index 10f27b8..861ea57 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
@@ -46,7 +46,7 @@
dumpVal(out, "clearClientTarget", clearClientTarget);
dumpVal(out, "displayFrame", displayFrame);
dumpVal(out, "sourceCrop", sourceCrop);
- dumpVal(out, "bufferTransform%", toString(bufferTransform), bufferTransform);
+ dumpVal(out, "bufferTransform", toString(bufferTransform), bufferTransform);
dumpVal(out, "z-index", z);
if (hwc) {
diff --git a/services/surfaceflinger/CompositionEngine/tests/FloatRectMatcher.h b/services/surfaceflinger/CompositionEngine/tests/FloatRectMatcher.h
new file mode 100644
index 0000000..6741cc9
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/FloatRectMatcher.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <android-base/stringprintf.h>
+#include <gmock/gmock.h>
+
+namespace {
+
+using android::base::StringAppendF;
+using FloatRect = android::FloatRect;
+
+void dumpFloatRect(const FloatRect& rect, std::string& result, const char* name) {
+ StringAppendF(&result, "%s (%f %f %f %f) ", name, rect.left, rect.top, rect.right, rect.bottom);
+}
+
+// Checks for a region match
+MATCHER_P(FloatRectEq, expected, "") {
+ std::string buf;
+ buf.append("FloatRects are not equal\n");
+ dumpFloatRect(expected, buf, "expected rect");
+ dumpFloatRect(arg, buf, "actual rect");
+ *result_listener << buf;
+
+ const float TOLERANCE = 1e-3f;
+ return (std::fabs(expected.left - arg.left) < TOLERANCE) &&
+ (std::fabs(expected.top - arg.top) < TOLERANCE) &&
+ (std::fabs(expected.right - arg.right) < TOLERANCE) &&
+ (std::fabs(expected.bottom - arg.bottom) < TOLERANCE);
+}
+
+} // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index f7dcf6f..2504fa6 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -21,18 +21,40 @@
#include <compositionengine/mock/Output.h>
#include <gtest/gtest.h>
+#include "FloatRectMatcher.h"
#include "MockHWC2.h"
#include "MockHWComposer.h"
+#include "RectMatcher.h"
namespace android::compositionengine {
namespace {
+using testing::_;
+using testing::Return;
+using testing::ReturnRef;
using testing::StrictMock;
constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{42};
+constexpr auto TR_IDENT = 0u;
+constexpr auto TR_FLP_H = HAL_TRANSFORM_FLIP_H;
+constexpr auto TR_FLP_V = HAL_TRANSFORM_FLIP_V;
+constexpr auto TR_ROT_90 = HAL_TRANSFORM_ROT_90;
+constexpr auto TR_ROT_180 = TR_FLP_H | TR_FLP_V;
+constexpr auto TR_ROT_270 = TR_ROT_90 | TR_ROT_180;
+
+const std::string kOutputName{"Test Output"};
+
class OutputLayerTest : public testing::Test {
public:
+ OutputLayerTest() {
+ EXPECT_CALL(*mLayerFE, getDebugName()).WillRepeatedly(Return("Test LayerFE"));
+ EXPECT_CALL(mOutput, getName()).WillRepeatedly(ReturnRef(kOutputName));
+
+ EXPECT_CALL(*mLayer, getState()).WillRepeatedly(ReturnRef(mLayerState));
+ EXPECT_CALL(mOutput, getState()).WillRepeatedly(ReturnRef(mOutputState));
+ }
+
~OutputLayerTest() override = default;
compositionengine::mock::Output mOutput;
@@ -41,15 +63,18 @@
sp<compositionengine::mock::LayerFE> mLayerFE{
new StrictMock<compositionengine::mock::LayerFE>()};
impl::OutputLayer mOutputLayer{mOutput, mLayer, mLayerFE};
+
+ impl::LayerCompositionState mLayerState;
+ impl::OutputCompositionState mOutputState;
};
-/* ------------------------------------------------------------------------
+/*
* Basic construction
*/
TEST_F(OutputLayerTest, canInstantiateOutputLayer) {}
-/* ------------------------------------------------------------------------
+/*
* OutputLayer::initialize()
*/
@@ -71,15 +96,481 @@
mOutputLayer.initialize(compositionEngine, DEFAULT_DISPLAY_ID);
- const auto& state = mOutputLayer.getState();
- ASSERT_TRUE(state.hwc);
+ const auto& outputLayerState = mOutputLayer.getState();
+ ASSERT_TRUE(outputLayerState.hwc);
- const auto& hwcState = *state.hwc;
+ const auto& hwcState = *outputLayerState.hwc;
EXPECT_EQ(&hwcLayer, hwcState.hwcLayer.get());
EXPECT_CALL(hwc, destroyLayer(DEFAULT_DISPLAY_ID, &hwcLayer));
mOutputLayer.editState().hwc.reset();
}
+/*
+ * OutputLayer::calculateOutputSourceCrop()
+ */
+
+struct OutputLayerSourceCropTest : public OutputLayerTest {
+ OutputLayerSourceCropTest() {
+ // Set reasonable default values for a simple case. Each test will
+ // set one specific value to something different.
+ mLayerState.frontEnd.geomUsesSourceCrop = true;
+ mLayerState.frontEnd.geomContentCrop = Rect{0, 0, 1920, 1080};
+ mLayerState.frontEnd.geomActiveTransparentRegion = Region{};
+ mLayerState.frontEnd.geomLayerBounds = FloatRect{0.f, 0.f, 1920.f, 1080.f};
+ mLayerState.frontEnd.geomLayerTransform = ui::Transform{TR_IDENT};
+ mLayerState.frontEnd.geomBufferSize = Rect{0, 0, 1920, 1080};
+ mLayerState.frontEnd.geomBufferTransform = TR_IDENT;
+
+ mOutputState.viewport = Rect{0, 0, 1920, 1080};
+ }
+
+ FloatRect calculateOutputSourceCrop() {
+ mLayerState.frontEnd.geomInverseLayerTransform =
+ mLayerState.frontEnd.geomLayerTransform.inverse();
+
+ return mOutputLayer.calculateOutputSourceCrop();
+ }
+};
+
+TEST_F(OutputLayerSourceCropTest, computesEmptyIfSourceCropNotUsed) {
+ mLayerState.frontEnd.geomUsesSourceCrop = false;
+
+ const FloatRect expected{};
+ EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected));
+}
+
+TEST_F(OutputLayerSourceCropTest, correctForSimpleDefaultCase) {
+ const FloatRect expected{0.f, 0.f, 1920.f, 1080.f};
+ EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected));
+}
+
+TEST_F(OutputLayerSourceCropTest, handlesBoundsOutsideViewport) {
+ mLayerState.frontEnd.geomLayerBounds = FloatRect{-2000.f, -2000.f, 2000.f, 2000.f};
+
+ const FloatRect expected{0.f, 0.f, 1920.f, 1080.f};
+ EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected));
+}
+
+TEST_F(OutputLayerSourceCropTest, handlesBoundsOutsideViewportRotated) {
+ mLayerState.frontEnd.geomLayerBounds = FloatRect{-2000.f, -2000.f, 2000.f, 2000.f};
+ mLayerState.frontEnd.geomLayerTransform.set(HAL_TRANSFORM_ROT_90, 1920, 1080);
+
+ const FloatRect expected{0.f, 0.f, 1080.f, 1080.f};
+ EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected));
+}
+
+TEST_F(OutputLayerSourceCropTest, calculateOutputSourceCropWorksWithATransformedBuffer) {
+ struct Entry {
+ uint32_t bufferInvDisplay;
+ uint32_t buffer;
+ uint32_t display;
+ FloatRect expected;
+ };
+ // Not an exhaustive list of cases, but hopefully enough.
+ const std::array<Entry, 12> testData = {
+ // clang-format off
+ // inv buffer display expected
+ /* 0 */ Entry{false, TR_IDENT, TR_IDENT, FloatRect{0.f, 0.f, 1920.f, 1080.f}},
+ /* 1 */ Entry{false, TR_IDENT, TR_ROT_90, FloatRect{0.f, 0.f, 1920.f, 1080.f}},
+ /* 2 */ Entry{false, TR_IDENT, TR_ROT_180, FloatRect{0.f, 0.f, 1920.f, 1080.f}},
+ /* 3 */ Entry{false, TR_IDENT, TR_ROT_270, FloatRect{0.f, 0.f, 1920.f, 1080.f}},
+
+ /* 4 */ Entry{true, TR_IDENT, TR_IDENT, FloatRect{0.f, 0.f, 1920.f, 1080.f}},
+ /* 5 */ Entry{true, TR_IDENT, TR_ROT_90, FloatRect{0.f, 0.f, 1920.f, 1080.f}},
+ /* 6 */ Entry{true, TR_IDENT, TR_ROT_180, FloatRect{0.f, 0.f, 1920.f, 1080.f}},
+ /* 7 */ Entry{true, TR_IDENT, TR_ROT_270, FloatRect{0.f, 0.f, 1920.f, 1080.f}},
+
+ /* 8 */ Entry{false, TR_IDENT, TR_IDENT, FloatRect{0.f, 0.f, 1920.f, 1080.f}},
+ /* 9 */ Entry{false, TR_ROT_90, TR_ROT_90, FloatRect{0.f, 0.f, 1920.f, 1080.f}},
+ /* 10 */ Entry{false, TR_ROT_180, TR_ROT_180, FloatRect{0.f, 0.f, 1920.f, 1080.f}},
+ /* 11 */ Entry{false, TR_ROT_270, TR_ROT_270, FloatRect{0.f, 0.f, 1920.f, 1080.f}},
+
+ // clang-format on
+ };
+
+ for (size_t i = 0; i < testData.size(); i++) {
+ const auto& entry = testData[i];
+
+ mLayerState.frontEnd.geomBufferUsesDisplayInverseTransform = entry.bufferInvDisplay;
+ mLayerState.frontEnd.geomBufferTransform = entry.buffer;
+ mOutputState.orientation = entry.display;
+
+ EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(entry.expected)) << "entry " << i;
+ }
+}
+
+TEST_F(OutputLayerSourceCropTest, geomContentCropAffectsCrop) {
+ mLayerState.frontEnd.geomContentCrop = Rect{0, 0, 960, 540};
+
+ const FloatRect expected{0.f, 0.f, 960.f, 540.f};
+ EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected));
+}
+
+TEST_F(OutputLayerSourceCropTest, viewportAffectsCrop) {
+ mOutputState.viewport = Rect{0, 0, 960, 540};
+
+ const FloatRect expected{0.f, 0.f, 960.f, 540.f};
+ EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected));
+}
+
+/*
+ * OutputLayer::calculateOutputDisplayFrame()
+ */
+
+struct OutputLayerDisplayFrameTest : public OutputLayerTest {
+ OutputLayerDisplayFrameTest() {
+ // Set reasonable default values for a simple case. Each test will
+ // set one specific value to something different.
+
+ mLayerState.frontEnd.geomActiveTransparentRegion = Region{};
+ mLayerState.frontEnd.geomLayerTransform = ui::Transform{TR_IDENT};
+ mLayerState.frontEnd.geomBufferSize = Rect{0, 0, 1920, 1080};
+ mLayerState.frontEnd.geomBufferUsesDisplayInverseTransform = false;
+ mLayerState.frontEnd.geomCrop = Rect{0, 0, 1920, 1080};
+ mLayerState.frontEnd.geomLayerBounds = FloatRect{0.f, 0.f, 1920.f, 1080.f};
+
+ mOutputState.viewport = Rect{0, 0, 1920, 1080};
+ mOutputState.transform = ui::Transform{TR_IDENT};
+ }
+
+ Rect calculateOutputDisplayFrame() {
+ mLayerState.frontEnd.geomInverseLayerTransform =
+ mLayerState.frontEnd.geomLayerTransform.inverse();
+
+ return mOutputLayer.calculateOutputDisplayFrame();
+ }
+};
+
+TEST_F(OutputLayerDisplayFrameTest, correctForSimpleDefaultCase) {
+ const Rect expected{0, 0, 1920, 1080};
+ EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+}
+
+TEST_F(OutputLayerDisplayFrameTest, fullActiveTransparentRegionReturnsEmptyFrame) {
+ mLayerState.frontEnd.geomActiveTransparentRegion = Region{Rect{0, 0, 1920, 1080}};
+ const Rect expected{0, 0, 0, 0};
+ EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+}
+
+TEST_F(OutputLayerDisplayFrameTest, cropAffectsDisplayFrame) {
+ mLayerState.frontEnd.geomCrop = Rect{100, 200, 300, 500};
+ const Rect expected{100, 200, 300, 500};
+ EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+}
+
+TEST_F(OutputLayerDisplayFrameTest, cropAffectsDisplayFrameRotated) {
+ mLayerState.frontEnd.geomCrop = Rect{100, 200, 300, 500};
+ mLayerState.frontEnd.geomLayerTransform.set(HAL_TRANSFORM_ROT_90, 1920, 1080);
+ const Rect expected{1420, 100, 1720, 300};
+ EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+}
+
+TEST_F(OutputLayerDisplayFrameTest, emptyGeomCropIsNotUsedToComputeFrame) {
+ mLayerState.frontEnd.geomCrop = Rect{};
+ const Rect expected{0, 0, 1920, 1080};
+ EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+}
+
+TEST_F(OutputLayerDisplayFrameTest, geomLayerBoundsAffectsFrame) {
+ mLayerState.frontEnd.geomLayerBounds = FloatRect{0.f, 0.f, 960.f, 540.f};
+ const Rect expected{0, 0, 960, 540};
+ EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+}
+
+TEST_F(OutputLayerDisplayFrameTest, viewportAffectsFrame) {
+ mOutputState.viewport = Rect{0, 0, 960, 540};
+ const Rect expected{0, 0, 960, 540};
+ EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+}
+
+TEST_F(OutputLayerDisplayFrameTest, outputTransformAffectsDisplayFrame) {
+ mOutputState.transform = ui::Transform{HAL_TRANSFORM_ROT_90};
+ const Rect expected{-1080, 0, 0, 1920};
+ EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+}
+
+/*
+ * OutputLayer::calculateOutputRelativeBufferTransform()
+ */
+
+TEST_F(OutputLayerTest, calculateOutputRelativeBufferTransformTestsNeeded) {
+ mLayerState.frontEnd.geomBufferUsesDisplayInverseTransform = false;
+
+ struct Entry {
+ uint32_t layer;
+ uint32_t buffer;
+ uint32_t display;
+ uint32_t expected;
+ };
+ // Not an exhaustive list of cases, but hopefully enough.
+ const std::array<Entry, 24> testData = {
+ // clang-format off
+ // layer buffer display expected
+ /* 0 */ Entry{TR_IDENT, TR_IDENT, TR_IDENT, TR_IDENT},
+ /* 1 */ Entry{TR_IDENT, TR_IDENT, TR_ROT_90, TR_ROT_90},
+ /* 2 */ Entry{TR_IDENT, TR_IDENT, TR_ROT_180, TR_ROT_180},
+ /* 3 */ Entry{TR_IDENT, TR_IDENT, TR_ROT_270, TR_ROT_270},
+
+ /* 4 */ Entry{TR_IDENT, TR_FLP_H, TR_IDENT, TR_FLP_H ^ TR_IDENT},
+ /* 5 */ Entry{TR_IDENT, TR_FLP_H, TR_ROT_90, TR_FLP_H ^ TR_ROT_90},
+ /* 6 */ Entry{TR_IDENT, TR_FLP_H, TR_ROT_180, TR_FLP_H ^ TR_ROT_180},
+ /* 7 */ Entry{TR_IDENT, TR_FLP_H, TR_ROT_270, TR_FLP_H ^ TR_ROT_270},
+
+ /* 8 */ Entry{TR_IDENT, TR_FLP_V, TR_IDENT, TR_FLP_V},
+ /* 9 */ Entry{TR_IDENT, TR_ROT_90, TR_ROT_90, TR_ROT_180},
+ /* 10 */ Entry{TR_IDENT, TR_ROT_180, TR_ROT_180, TR_IDENT},
+ /* 11 */ Entry{TR_IDENT, TR_ROT_270, TR_ROT_270, TR_ROT_180},
+
+ /* 12 */ Entry{TR_ROT_90, TR_IDENT, TR_IDENT, TR_IDENT ^ TR_ROT_90},
+ /* 13 */ Entry{TR_ROT_90, TR_FLP_H, TR_ROT_90, TR_FLP_H ^ TR_ROT_180},
+ /* 14 */ Entry{TR_ROT_90, TR_IDENT, TR_ROT_180, TR_IDENT ^ TR_ROT_270},
+ /* 15 */ Entry{TR_ROT_90, TR_FLP_H, TR_ROT_270, TR_FLP_H ^ TR_IDENT},
+
+ /* 16 */ Entry{TR_ROT_180, TR_FLP_H, TR_IDENT, TR_FLP_H ^ TR_ROT_180},
+ /* 17 */ Entry{TR_ROT_180, TR_IDENT, TR_ROT_90, TR_IDENT ^ TR_ROT_270},
+ /* 18 */ Entry{TR_ROT_180, TR_FLP_H, TR_ROT_180, TR_FLP_H ^ TR_IDENT},
+ /* 19 */ Entry{TR_ROT_180, TR_IDENT, TR_ROT_270, TR_IDENT ^ TR_ROT_90},
+
+ /* 20 */ Entry{TR_ROT_270, TR_IDENT, TR_IDENT, TR_IDENT ^ TR_ROT_270},
+ /* 21 */ Entry{TR_ROT_270, TR_FLP_H, TR_ROT_90, TR_FLP_H ^ TR_IDENT},
+ /* 22 */ Entry{TR_ROT_270, TR_FLP_H, TR_ROT_180, TR_FLP_H ^ TR_ROT_90},
+ /* 23 */ Entry{TR_ROT_270, TR_IDENT, TR_ROT_270, TR_IDENT ^ TR_ROT_180},
+ // clang-format on
+ };
+
+ for (size_t i = 0; i < testData.size(); i++) {
+ const auto& entry = testData[i];
+
+ mLayerState.frontEnd.geomLayerTransform.set(entry.layer, 1920, 1080);
+ mLayerState.frontEnd.geomBufferTransform = entry.buffer;
+ mOutputState.orientation = entry.display;
+
+ auto actual = mOutputLayer.calculateOutputRelativeBufferTransform();
+ EXPECT_EQ(entry.expected, actual) << "entry " << i;
+ }
+}
+
+TEST_F(OutputLayerTest,
+ calculateOutputRelativeBufferTransformTestWithOfBufferUsesDisplayInverseTransform) {
+ mLayerState.frontEnd.geomBufferUsesDisplayInverseTransform = true;
+
+ struct Entry {
+ uint32_t layer;
+ uint32_t buffer;
+ uint32_t display;
+ uint32_t expected;
+ };
+ // Not an exhaustive list of cases, but hopefully enough.
+ const std::array<Entry, 24> testData = {
+ // clang-format off
+ // layer buffer display expected
+ /* 0 */ Entry{TR_IDENT, TR_IDENT, TR_IDENT, TR_IDENT},
+ /* 1 */ Entry{TR_IDENT, TR_IDENT, TR_ROT_90, TR_IDENT},
+ /* 2 */ Entry{TR_IDENT, TR_IDENT, TR_ROT_180, TR_IDENT},
+ /* 3 */ Entry{TR_IDENT, TR_IDENT, TR_ROT_270, TR_IDENT},
+
+ /* 4 */ Entry{TR_IDENT, TR_FLP_H, TR_IDENT, TR_FLP_H},
+ /* 5 */ Entry{TR_IDENT, TR_FLP_H, TR_ROT_90, TR_FLP_H},
+ /* 6 */ Entry{TR_IDENT, TR_FLP_H, TR_ROT_180, TR_FLP_H},
+ /* 7 */ Entry{TR_IDENT, TR_FLP_H, TR_ROT_270, TR_FLP_H},
+
+ /* 8 */ Entry{TR_IDENT, TR_FLP_V, TR_IDENT, TR_FLP_V},
+ /* 9 */ Entry{TR_IDENT, TR_ROT_90, TR_ROT_90, TR_ROT_90},
+ /* 10 */ Entry{TR_IDENT, TR_ROT_180, TR_ROT_180, TR_ROT_180},
+ /* 11 */ Entry{TR_IDENT, TR_ROT_270, TR_ROT_270, TR_ROT_270},
+
+ /* 12 */ Entry{TR_ROT_90, TR_IDENT, TR_IDENT, TR_IDENT ^ TR_ROT_90},
+ /* 13 */ Entry{TR_ROT_90, TR_FLP_H, TR_ROT_90, TR_FLP_H ^ TR_ROT_90},
+ /* 14 */ Entry{TR_ROT_90, TR_IDENT, TR_ROT_180, TR_IDENT ^ TR_ROT_90},
+ /* 15 */ Entry{TR_ROT_90, TR_FLP_H, TR_ROT_270, TR_FLP_H ^ TR_ROT_90},
+
+ /* 16 */ Entry{TR_ROT_180, TR_FLP_H, TR_IDENT, TR_FLP_H ^ TR_ROT_180},
+ /* 17 */ Entry{TR_ROT_180, TR_IDENT, TR_ROT_90, TR_IDENT ^ TR_ROT_180},
+ /* 18 */ Entry{TR_ROT_180, TR_FLP_H, TR_ROT_180, TR_FLP_H ^ TR_ROT_180},
+ /* 19 */ Entry{TR_ROT_180, TR_IDENT, TR_ROT_270, TR_IDENT ^ TR_ROT_180},
+
+ /* 20 */ Entry{TR_ROT_270, TR_IDENT, TR_IDENT, TR_IDENT ^ TR_ROT_270},
+ /* 21 */ Entry{TR_ROT_270, TR_FLP_H, TR_ROT_90, TR_FLP_H ^ TR_ROT_270},
+ /* 22 */ Entry{TR_ROT_270, TR_FLP_H, TR_ROT_180, TR_FLP_H ^ TR_ROT_270},
+ /* 23 */ Entry{TR_ROT_270, TR_IDENT, TR_ROT_270, TR_IDENT ^ TR_ROT_270},
+ // clang-format on
+ };
+
+ for (size_t i = 0; i < testData.size(); i++) {
+ const auto& entry = testData[i];
+
+ mLayerState.frontEnd.geomLayerTransform = ui::Transform{entry.layer};
+ mLayerState.frontEnd.geomBufferTransform = entry.buffer;
+ mOutputState.orientation = entry.display;
+
+ auto actual = mOutputLayer.calculateOutputRelativeBufferTransform();
+ EXPECT_EQ(entry.expected, actual) << "entry " << i;
+ }
+}
+
+/*
+ * OutputLayer::updateCompositionState()
+ */
+
+struct OutputLayerPartialMockForUpdateCompositionState : public impl::OutputLayer {
+ OutputLayerPartialMockForUpdateCompositionState(const compositionengine::Output& output,
+ std::shared_ptr<compositionengine::Layer> layer,
+ sp<compositionengine::LayerFE> layerFE)
+ : impl::OutputLayer(output, layer, layerFE) {}
+ // Mock everything called by updateCompositionState to simplify testing it.
+ MOCK_CONST_METHOD0(calculateOutputSourceCrop, FloatRect());
+ MOCK_CONST_METHOD0(calculateOutputDisplayFrame, Rect());
+ MOCK_CONST_METHOD0(calculateOutputRelativeBufferTransform, uint32_t());
+};
+
+struct OutputLayerUpdateCompositionStateTest : public OutputLayerTest {
+public:
+ OutputLayerUpdateCompositionStateTest() {
+ EXPECT_CALL(*mLayer, getState()).WillRepeatedly(ReturnRef(mLayerState));
+ EXPECT_CALL(mOutput, getState()).WillRepeatedly(ReturnRef(mOutputState));
+ }
+
+ ~OutputLayerUpdateCompositionStateTest() = default;
+
+ void setupGeometryChildCallValues() {
+ EXPECT_CALL(mOutputLayer, calculateOutputSourceCrop()).WillOnce(Return(kSourceCrop));
+ EXPECT_CALL(mOutputLayer, calculateOutputDisplayFrame()).WillOnce(Return(kDisplayFrame));
+ EXPECT_CALL(mOutputLayer, calculateOutputRelativeBufferTransform())
+ .WillOnce(Return(mBufferTransform));
+ }
+
+ void validateComputedGeometryState() {
+ const auto& state = mOutputLayer.getState();
+ EXPECT_EQ(kSourceCrop, state.sourceCrop);
+ EXPECT_EQ(kDisplayFrame, state.displayFrame);
+ EXPECT_EQ(static_cast<Hwc2::Transform>(mBufferTransform), state.bufferTransform);
+ }
+
+ const FloatRect kSourceCrop{1.f, 2.f, 3.f, 4.f};
+ const Rect kDisplayFrame{11, 12, 13, 14};
+ uint32_t mBufferTransform{21};
+
+ using OutputLayer = OutputLayerPartialMockForUpdateCompositionState;
+ StrictMock<OutputLayer> mOutputLayer{mOutput, mLayer, mLayerFE};
+};
+
+TEST_F(OutputLayerUpdateCompositionStateTest, setsStateNormally) {
+ mLayerState.frontEnd.isSecure = true;
+ mOutputState.isSecure = true;
+
+ setupGeometryChildCallValues();
+
+ mOutputLayer.updateCompositionState(true);
+
+ validateComputedGeometryState();
+
+ EXPECT_EQ(false, mOutputLayer.getState().forceClientComposition);
+}
+
+TEST_F(OutputLayerUpdateCompositionStateTest,
+ alsoSetsForceCompositionIfSecureLayerOnNonsecureOutput) {
+ mLayerState.frontEnd.isSecure = true;
+ mOutputState.isSecure = false;
+
+ setupGeometryChildCallValues();
+
+ mOutputLayer.updateCompositionState(true);
+
+ validateComputedGeometryState();
+
+ EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition);
+}
+
+TEST_F(OutputLayerUpdateCompositionStateTest,
+ alsoSetsForceCompositionIfUnsupportedBufferTransform) {
+ mLayerState.frontEnd.isSecure = true;
+ mOutputState.isSecure = true;
+
+ mBufferTransform = ui::Transform::ROT_INVALID;
+
+ setupGeometryChildCallValues();
+
+ mOutputLayer.updateCompositionState(true);
+
+ validateComputedGeometryState();
+
+ EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition);
+}
+
+TEST_F(OutputLayerUpdateCompositionStateTest, doesNotRecomputeGeometryIfNotRequested) {
+ mOutputLayer.updateCompositionState(false);
+
+ EXPECT_EQ(false, mOutputLayer.getState().forceClientComposition);
+}
+
+/*
+ * OutputLayer::writeStateToHWC()
+ */
+
+struct OutputLayerWriteStateToHWCTest : public OutputLayerTest {
+ static constexpr HWC2::Error kError = HWC2::Error::Unsupported;
+ static constexpr FloatRect kSourceCrop{11.f, 12.f, 13.f, 14.f};
+ static constexpr uint32_t kZOrder = 21u;
+ static constexpr Hwc2::Transform kBufferTransform = static_cast<Hwc2::Transform>(31);
+ static constexpr Hwc2::IComposerClient::BlendMode kBlendMode =
+ static_cast<Hwc2::IComposerClient::BlendMode>(41);
+ static constexpr float kAlpha = 51.f;
+ static constexpr uint32_t kType = 61u;
+ static constexpr uint32_t kAppId = 62u;
+
+ static const Rect kDisplayFrame;
+
+ OutputLayerWriteStateToHWCTest() {
+ auto& outputLayerState = mOutputLayer.editState();
+ outputLayerState.hwc = impl::OutputLayerCompositionState::Hwc(mHwcLayer);
+
+ outputLayerState.displayFrame = kDisplayFrame;
+ outputLayerState.sourceCrop = kSourceCrop;
+ outputLayerState.z = kZOrder;
+ outputLayerState.bufferTransform = static_cast<Hwc2::Transform>(kBufferTransform);
+
+ mLayerState.frontEnd.blendMode = kBlendMode;
+ mLayerState.frontEnd.alpha = kAlpha;
+ mLayerState.frontEnd.type = kType;
+ mLayerState.frontEnd.appId = kAppId;
+ }
+
+ void expectGeometryCommonCalls() {
+ EXPECT_CALL(*mHwcLayer, setDisplayFrame(kDisplayFrame)).WillOnce(Return(kError));
+ EXPECT_CALL(*mHwcLayer, setSourceCrop(kSourceCrop)).WillOnce(Return(kError));
+ EXPECT_CALL(*mHwcLayer, setZOrder(kZOrder)).WillOnce(Return(kError));
+ EXPECT_CALL(*mHwcLayer, setTransform(static_cast<HWC2::Transform>(kBufferTransform)))
+ .WillOnce(Return(kError));
+
+ EXPECT_CALL(*mHwcLayer, setBlendMode(static_cast<HWC2::BlendMode>(kBlendMode)))
+ .WillOnce(Return(kError));
+ EXPECT_CALL(*mHwcLayer, setPlaneAlpha(kAlpha)).WillOnce(Return(kError));
+ EXPECT_CALL(*mHwcLayer, setInfo(kType, kAppId)).WillOnce(Return(kError));
+ }
+
+ std::shared_ptr<HWC2::mock::Layer> mHwcLayer{std::make_shared<StrictMock<HWC2::mock::Layer>>()};
+};
+
+const Rect OutputLayerWriteStateToHWCTest::kDisplayFrame{1001, 1002, 1003, 10044};
+
+TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoHWCState) {
+ mOutputLayer.editState().hwc.reset();
+
+ mOutputLayer.writeStateToHWC(true);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoHWCLayer) {
+ mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc(nullptr);
+
+ mOutputLayer.writeStateToHWC(true);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, canSetsAllState) {
+ expectGeometryCommonCalls();
+
+ mOutputLayer.writeStateToHWC(true);
+}
+
} // namespace
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/RectMatcher.h b/services/surfaceflinger/CompositionEngine/tests/RectMatcher.h
new file mode 100644
index 0000000..d4c76bc
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/RectMatcher.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <android-base/stringprintf.h>
+#include <gmock/gmock.h>
+
+namespace {
+
+using android::base::StringAppendF;
+using Rect = android::Rect;
+
+void dumpRect(const Rect& rect, std::string& result, const char* name) {
+ StringAppendF(&result, "%s (%d %d %d %d) ", name, rect.left, rect.top, rect.right, rect.bottom);
+}
+
+// Checks for a region match
+MATCHER_P(RectEq, expected, "") {
+ std::string buf;
+ buf.append("Rects are not equal\n");
+ dumpRect(expected, buf, "expected rect");
+ dumpRect(arg, buf, "actual rect");
+ *result_listener << buf;
+
+ return (expected.left == arg.left) && (expected.top == arg.top) &&
+ (expected.right == arg.right) && (expected.bottom == arg.bottom);
+}
+
+} // namespace
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index d28a484..512a0b4 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -29,6 +29,7 @@
#include <android-base/stringprintf.h>
#include <compositionengine/Display.h>
#include <compositionengine/Layer.h>
+#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/OutputLayer.h>
#include <compositionengine/impl/LayerCompositionState.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
@@ -368,36 +369,6 @@
return size;
}
-Rect Layer::computeInitialCrop(const sp<const DisplayDevice>& display) const {
- // the crop is the area of the window that gets cropped, but not
- // scaled in any ways.
- const State& s(getDrawingState());
-
- // apply the projection's clipping to the window crop in
- // layerstack space, and convert-back to layer space.
- // if there are no window scaling involved, this operation will map to full
- // pixels in the buffer.
-
- FloatRect activeCropFloat = getBounds();
- ui::Transform t = getTransform();
- // Transform to screen space.
- activeCropFloat = t.transform(activeCropFloat);
- activeCropFloat = activeCropFloat.intersect(display->getViewport().toFloatRect());
- // Back to layer space to work with the content crop.
- activeCropFloat = t.inverse().transform(activeCropFloat);
- // This needs to be here as transform.transform(Rect) computes the
- // transformed rect and then takes the bounding box of the result before
- // returning. This means
- // transform.inverse().transform(transform.transform(Rect)) != Rect
- // in which case we need to make sure the final rect is clipped to the
- // display bounds.
- Rect activeCrop{activeCropFloat};
- if (!activeCrop.intersect(getBufferSize(s), &activeCrop)) {
- activeCrop.clear();
- }
- return activeCrop;
-}
-
void Layer::setupRoundedCornersCropCoordinates(Rect win,
const FloatRect& roundedCornersCrop) const {
// Translate win by the rounded corners rect coordinates, to have all values in
@@ -415,189 +386,17 @@
cropCoords[3] = vec2(win.right, win.top);
}
-FloatRect Layer::computeCrop(const sp<const DisplayDevice>& display) const {
- // the content crop is the area of the content that gets scaled to the
- // layer's size. This is in buffer space.
- FloatRect crop = getContentCrop().toFloatRect();
-
- // In addition there is a WM-specified crop we pull from our drawing state.
- const State& s(getDrawingState());
-
- Rect activeCrop = computeInitialCrop(display);
- Rect bufferSize = getBufferSize(s);
-
- int32_t winWidth = bufferSize.getWidth();
- int32_t winHeight = bufferSize.getHeight();
-
- // The bufferSize for buffer state layers can be unbounded ([0, 0, -1, -1]) if display frame
- // hasn't been set and the parent is an unbounded layer.
- if (winWidth < 0 && winHeight < 0) {
- return crop;
- }
-
- // Transform the window crop to match the buffer coordinate system,
- // which means using the inverse of the current transform set on the
- // SurfaceFlingerConsumer.
- uint32_t invTransform = mCurrentTransform;
- if (getTransformToDisplayInverse()) {
- /*
- * the code below applies the primary display's inverse transform to the
- * buffer
- */
- uint32_t invTransformOrient = DisplayDevice::getPrimaryDisplayOrientationTransform();
- // calculate the inverse transform
- if (invTransformOrient & NATIVE_WINDOW_TRANSFORM_ROT_90) {
- invTransformOrient ^= NATIVE_WINDOW_TRANSFORM_FLIP_V | NATIVE_WINDOW_TRANSFORM_FLIP_H;
- }
- // and apply to the current transform
- invTransform = (ui::Transform(invTransformOrient) *
- ui::Transform(invTransform)).getOrientation();
- }
-
- if (invTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
- // If the activeCrop has been rotate the ends are rotated but not
- // the space itself so when transforming ends back we can't rely on
- // a modification of the axes of rotation. To account for this we
- // need to reorient the inverse rotation in terms of the current
- // axes of rotation.
- bool is_h_flipped = (invTransform & NATIVE_WINDOW_TRANSFORM_FLIP_H) != 0;
- bool is_v_flipped = (invTransform & NATIVE_WINDOW_TRANSFORM_FLIP_V) != 0;
- if (is_h_flipped == is_v_flipped) {
- invTransform ^= NATIVE_WINDOW_TRANSFORM_FLIP_V | NATIVE_WINDOW_TRANSFORM_FLIP_H;
- }
- std::swap(winWidth, winHeight);
- }
- const Rect winCrop =
- activeCrop.transform(invTransform, bufferSize.getWidth(), bufferSize.getHeight());
-
- // below, crop is intersected with winCrop expressed in crop's coordinate space
- float xScale = crop.getWidth() / float(winWidth);
- float yScale = crop.getHeight() / float(winHeight);
-
- float insetL = winCrop.left * xScale;
- float insetT = winCrop.top * yScale;
- float insetR = (winWidth - winCrop.right) * xScale;
- float insetB = (winHeight - winCrop.bottom) * yScale;
-
- crop.left += insetL;
- crop.top += insetT;
- crop.right -= insetR;
- crop.bottom -= insetB;
-
- return crop;
-}
-
-void Layer::setGeometry(const sp<const DisplayDevice>& display, uint32_t z) {
- const auto outputLayer = findOutputLayerForDisplay(display);
- LOG_FATAL_IF(!outputLayer);
- LOG_FATAL_IF(!outputLayer->getState().hwc);
- auto& hwcLayer = (*outputLayer->getState().hwc).hwcLayer;
-
- if (!hasHwcLayer(display)) {
- ALOGE("[%s] failed to setGeometry: no HWC layer found (%s)", mName.string(),
- display->getDebugName().c_str());
- return;
- }
-
- LOG_FATAL_IF(!getCompositionLayer());
- auto& commonCompositionState = getCompositionLayer()->editState().frontEnd;
- auto& compositionState = outputLayer->editState();
-
- // enable this layer
- compositionState.forceClientComposition = false;
-
- if (isSecure() && !display->isSecure()) {
- compositionState.forceClientComposition = true;
- }
-
- // this gives us only the "orientation" component of the transform
- const State& s(getDrawingState());
- const Rect bufferSize = getBufferSize(s);
+void Layer::latchGeometry(compositionengine::LayerFECompositionState& compositionState) const {
+ const auto& drawingState{getDrawingState()};
+ auto alpha = static_cast<float>(getAlpha());
auto blendMode = HWC2::BlendMode::None;
- if (!isOpaque(s) || getAlpha() != 1.0f) {
+ if (!isOpaque(drawingState) || alpha != 1.0f) {
blendMode =
mPremultipliedAlpha ? HWC2::BlendMode::Premultiplied : HWC2::BlendMode::Coverage;
}
- auto error = hwcLayer->setBlendMode(blendMode);
- ALOGE_IF(error != HWC2::Error::None,
- "[%s] Failed to set blend mode %s:"
- " %s (%d)",
- mName.string(), to_string(blendMode).c_str(), to_string(error).c_str(),
- static_cast<int32_t>(error));
- commonCompositionState.blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode);
- // apply the layer's transform, followed by the display's global transform
- // here we're guaranteed that the layer's transform preserves rects
- Region activeTransparentRegion(getActiveTransparentRegion(s));
- ui::Transform t = getTransform();
- Rect activeCrop = getCrop(s);
- if (!activeCrop.isEmpty() && bufferSize.isValid()) {
- activeCrop = t.transform(activeCrop);
- if (!activeCrop.intersect(display->getViewport(), &activeCrop)) {
- activeCrop.clear();
- }
- activeCrop = t.inverse().transform(activeCrop, true);
- // This needs to be here as transform.transform(Rect) computes the
- // transformed rect and then takes the bounding box of the result before
- // returning. This means
- // transform.inverse().transform(transform.transform(Rect)) != Rect
- // in which case we need to make sure the final rect is clipped to the
- // display bounds.
- if (!activeCrop.intersect(bufferSize, &activeCrop)) {
- activeCrop.clear();
- }
- // mark regions outside the crop as transparent
- activeTransparentRegion.orSelf(Rect(0, 0, bufferSize.getWidth(), activeCrop.top));
- activeTransparentRegion.orSelf(
- Rect(0, activeCrop.bottom, bufferSize.getWidth(), bufferSize.getHeight()));
- activeTransparentRegion.orSelf(Rect(0, activeCrop.top, activeCrop.left, activeCrop.bottom));
- activeTransparentRegion.orSelf(
- Rect(activeCrop.right, activeCrop.top, bufferSize.getWidth(), activeCrop.bottom));
- }
-
- // getBounds returns a FloatRect to provide more accuracy during the
- // transformation. We then round upon constructing 'frame'.
- Rect frame{t.transform(getBounds(activeTransparentRegion))};
- if (!frame.intersect(display->getViewport(), &frame)) {
- frame.clear();
- }
- const ui::Transform& tr = display->getTransform();
- Rect transformedFrame = tr.transform(frame);
- error = hwcLayer->setDisplayFrame(transformedFrame);
- if (error != HWC2::Error::None) {
- ALOGE("[%s] Failed to set display frame [%d, %d, %d, %d]: %s (%d)", mName.string(),
- transformedFrame.left, transformedFrame.top, transformedFrame.right,
- transformedFrame.bottom, to_string(error).c_str(), static_cast<int32_t>(error));
- } else {
- compositionState.displayFrame = transformedFrame;
- }
-
- FloatRect sourceCrop = computeCrop(display);
- error = hwcLayer->setSourceCrop(sourceCrop);
- if (error != HWC2::Error::None) {
- ALOGE("[%s] Failed to set source crop [%.3f, %.3f, %.3f, %.3f]: "
- "%s (%d)",
- mName.string(), sourceCrop.left, sourceCrop.top, sourceCrop.right, sourceCrop.bottom,
- to_string(error).c_str(), static_cast<int32_t>(error));
- } else {
- compositionState.sourceCrop = sourceCrop;
- }
-
- float alpha = static_cast<float>(getAlpha());
- error = hwcLayer->setPlaneAlpha(alpha);
- ALOGE_IF(error != HWC2::Error::None,
- "[%s] Failed to set plane alpha %.3f: "
- "%s (%d)",
- mName.string(), alpha, to_string(error).c_str(), static_cast<int32_t>(error));
- commonCompositionState.alpha = alpha;
-
- error = hwcLayer->setZOrder(z);
- ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set Z %u: %s (%d)", mName.string(), z,
- to_string(error).c_str(), static_cast<int32_t>(error));
- compositionState.z = z;
-
- int type = s.metadata.getInt32(METADATA_WINDOW_TYPE, 0);
- int appId = s.metadata.getInt32(METADATA_OWNER_UID, 0);
+ int type = drawingState.metadata.getInt32(METADATA_WINDOW_TYPE, 0);
+ int appId = drawingState.metadata.getInt32(METADATA_OWNER_UID, 0);
sp<Layer> parent = mDrawingParent.promote();
if (parent.get()) {
auto& parentState = parent->getDrawingState();
@@ -609,60 +408,33 @@
}
}
- error = hwcLayer->setInfo(type, appId);
- ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set info (%d)", mName.string(),
- static_cast<int32_t>(error));
+ compositionState.geomLayerTransform = getTransform();
+ compositionState.geomInverseLayerTransform = compositionState.geomLayerTransform.inverse();
+ compositionState.geomBufferSize = getBufferSize(drawingState);
+ compositionState.geomContentCrop = getContentCrop();
+ compositionState.geomCrop = getCrop(drawingState);
+ compositionState.geomBufferTransform = mCurrentTransform;
+ compositionState.geomBufferUsesDisplayInverseTransform = getTransformToDisplayInverse();
+ compositionState.geomActiveTransparentRegion = getActiveTransparentRegion(drawingState);
+ compositionState.geomLayerBounds = mBounds;
+ compositionState.geomUsesSourceCrop = usesSourceCrop();
+ compositionState.isSecure = isSecure();
- commonCompositionState.type = type;
- commonCompositionState.appId = appId;
+ compositionState.blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode);
+ compositionState.alpha = alpha;
+ compositionState.type = type;
+ compositionState.appId = appId;
+}
- /*
- * Transformations are applied in this order:
- * 1) buffer orientation/flip/mirror
- * 2) state transformation (window manager)
- * 3) layer orientation (screen orientation)
- * (NOTE: the matrices are multiplied in reverse order)
- */
-
- const ui::Transform bufferOrientation(mCurrentTransform);
- ui::Transform transform(tr * t * bufferOrientation);
-
- if (getTransformToDisplayInverse()) {
- /*
- * the code below applies the primary display's inverse transform to the
- * buffer
- */
- uint32_t invTransform = DisplayDevice::getPrimaryDisplayOrientationTransform();
- // calculate the inverse transform
- if (invTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
- invTransform ^= NATIVE_WINDOW_TRANSFORM_FLIP_V | NATIVE_WINDOW_TRANSFORM_FLIP_H;
- }
-
- /*
- * Here we cancel out the orientation component of the WM transform.
- * The scaling and translate components are already included in our bounds
- * computation so it's enough to just omit it in the composition.
- * See comment in BufferLayer::prepareClientLayer with ref to b/36727915 for why.
- */
- transform = ui::Transform(invTransform) * tr * bufferOrientation;
+void Layer::latchCompositionState(compositionengine::LayerFECompositionState& compositionState,
+ bool includeGeometry) const {
+ if (includeGeometry) {
+ latchGeometry(compositionState);
}
+}
- // this gives us only the "orientation" component of the transform
- const uint32_t orientation = transform.getOrientation();
- if (orientation & ui::Transform::ROT_INVALID) {
- // we can only handle simple transformation
- compositionState.forceClientComposition = true;
- (*compositionState.hwc).hwcCompositionType = Hwc2::IComposerClient::Composition::CLIENT;
- } else {
- auto transform = static_cast<HWC2::Transform>(orientation);
- auto error = hwcLayer->setTransform(transform);
- ALOGE_IF(error != HWC2::Error::None,
- "[%s] Failed to set transform %s: "
- "%s (%d)",
- mName.string(), to_string(transform).c_str(), to_string(error).c_str(),
- static_cast<int32_t>(error));
- compositionState.bufferTransform = static_cast<Hwc2::Transform>(transform);
- }
+const char* Layer::getDebugName() const {
+ return mName.string();
}
void Layer::forceClientComposition(const sp<DisplayDevice>& display) {
@@ -2044,8 +1816,24 @@
mDrawingParent = mCurrentParent;
}
+static wp<Layer> extractLayerFromBinder(const wp<IBinder>& weakBinderHandle) {
+ if (weakBinderHandle == nullptr) {
+ return nullptr;
+ }
+ sp<IBinder> binderHandle = weakBinderHandle.promote();
+ if (binderHandle == nullptr) {
+ return nullptr;
+ }
+ sp<Layer::Handle> handle = static_cast<Layer::Handle*>(binderHandle.get());
+ if (handle == nullptr) {
+ return nullptr;
+ }
+ return handle->owner;
+}
+
void Layer::setInputInfo(const InputWindowInfo& info) {
mCurrentState.inputInfo = info;
+ mCurrentState.touchableRegionCrop = extractLayerFromBinder(info.touchableRegionCropHandle);
mCurrentState.modified = true;
mCurrentState.inputInfoChanged = true;
setTransactionFlags(eTransactionNeeded);
@@ -2234,6 +2022,18 @@
// bounds.
info.touchableRegion = info.touchableRegion.translate(info.frameLeft, info.frameTop);
info.visible = canReceiveInput();
+
+ auto cropLayer = mDrawingState.touchableRegionCrop.promote();
+ if (info.replaceTouchableRegionWithCrop) {
+ if (cropLayer == nullptr) {
+ info.touchableRegion = Region(Rect{mScreenBounds});
+ } else {
+ info.touchableRegion = Region(Rect{cropLayer->mScreenBounds});
+ }
+ } else if (cropLayer != nullptr) {
+ info.touchableRegion = info.touchableRegion.intersect(Rect{cropLayer->mScreenBounds});
+ }
+
return info;
}
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index f333d0a..89063da 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -70,6 +70,7 @@
namespace compositionengine {
class Layer;
class OutputLayer;
+struct LayerFECompositionState;
}
namespace impl {
@@ -183,6 +184,7 @@
bool inputInfoChanged;
InputWindowInfo inputInfo;
+ wp<Layer> touchableRegionCrop;
// dataspace is only used by BufferStateLayer and ColorLayer
ui::Dataspace dataspace;
@@ -419,6 +421,11 @@
*/
virtual bool isFixedSize() const { return true; }
+ /*
+ * usesSourceCrop - true if content should use a source crop
+ */
+ virtual bool usesSourceCrop() const { return false; }
+
// Most layers aren't created from the main thread, and therefore need to
// grab the SF state lock to access HWC, but ContainerLayer does, so we need
// to avoid grabbing the lock again to avoid deadlock
@@ -455,13 +462,19 @@
/*
* compositionengine::LayerFE overrides
*/
+ void latchCompositionState(compositionengine::LayerFECompositionState&,
+ bool includeGeometry) const override;
void onLayerDisplayed(const sp<Fence>& releaseFence) override;
+ const char* getDebugName() const override;
+protected:
+ void latchGeometry(compositionengine::LayerFECompositionState& outState) const;
+
+public:
virtual void setDefaultBufferSize(uint32_t /*w*/, uint32_t /*h*/) {}
virtual bool isHdrY410() const { return false; }
- void setGeometry(const sp<const DisplayDevice>& display, uint32_t z);
void forceClientComposition(const sp<DisplayDevice>& display);
bool getForceClientComposition(const sp<DisplayDevice>& display);
virtual void setPerFrameData(const sp<const DisplayDevice>& display,
@@ -708,12 +721,6 @@
uint32_t getEffectiveUsage(uint32_t usage) const;
- virtual FloatRect computeCrop(const sp<const DisplayDevice>& display) const;
- // Compute the initial crop as specified by parent layers and the
- // SurfaceControl for this layer. Does not include buffer crop from the
- // IGraphicBufferProducer client, as that should not affect child clipping.
- // Returns in screen space.
- Rect computeInitialCrop(const sp<const DisplayDevice>& display) const;
/**
* Setup rounded corners coordinates of this layer, taking into account the layer bounds and
* crop coordinates, transforming them into layer space.
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 37b9466..ca94e15 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1758,24 +1758,30 @@
mGeometryInvalid = false;
for (const auto& [token, displayDevice] : mDisplays) {
auto display = displayDevice->getCompositionDisplay();
- const auto displayId = display->getId();
- if (!displayId) {
- continue;
- }
- const Vector<sp<Layer>>& currentLayers = displayDevice->getVisibleLayersSortedByZ();
- for (size_t i = 0; i < currentLayers.size(); i++) {
- const auto& layer = currentLayers[i];
+ uint32_t zOrder = 0;
- if (!layer->hasHwcLayer(displayDevice)) {
- layer->forceClientComposition(displayDevice);
- continue;
+ for (auto& layer : display->getOutputLayersOrderedByZ()) {
+ auto& compositionState = layer->editState();
+ compositionState.forceClientComposition = false;
+ if (!compositionState.hwc || mDebugDisableHWC || mDebugRegion) {
+ compositionState.forceClientComposition = true;
}
- layer->setGeometry(displayDevice, i);
- if (mDebugDisableHWC || mDebugRegion) {
- layer->forceClientComposition(displayDevice);
- }
+ // The output Z order is set here based on a simple counter.
+ compositionState.z = zOrder++;
+
+ // Update the display independent composition state. This goes
+ // to the general composition layer state structure.
+ // TODO: Do this once per compositionengine::CompositionLayer.
+ layer->getLayerFE().latchCompositionState(layer->getLayer().editState().frontEnd,
+ true);
+
+ // Recalculate the geometry state of the output layer.
+ layer->updateCompositionState(true);
+
+ // Write the updated geometry state to the HWC
+ layer->writeStateToHWC(true);
}
}
}
@@ -3282,17 +3288,7 @@
clientCompositionDisplay.physicalDisplay = displayState.scissor;
clientCompositionDisplay.clip = displayState.scissor;
const ui::Transform& displayTransform = displayState.transform;
- mat4 m;
- m[0][0] = displayTransform[0][0];
- m[0][1] = displayTransform[0][1];
- m[0][3] = displayTransform[0][2];
- m[1][0] = displayTransform[1][0];
- m[1][1] = displayTransform[1][1];
- m[1][3] = displayTransform[1][2];
- m[3][0] = displayTransform[2][0];
- m[3][1] = displayTransform[2][1];
- m[3][3] = displayTransform[2][2];
- clientCompositionDisplay.globalTransform = m;
+ clientCompositionDisplay.globalTransform = displayTransform.asMatrix4();
const auto* profile = display->getDisplayColorProfile();
Dataspace outputDataspace = Dataspace::UNKNOWN;
@@ -4392,6 +4388,14 @@
return NO_ERROR;
}
+status_t SurfaceFlinger::dumpCritical(int fd, const DumpArgs&, bool asProto) {
+ if (asProto && mTracing.isEnabled()) {
+ mTracing.writeToFileAsync();
+ }
+
+ return doDump(fd, DumpArgs(), asProto);
+}
+
void SurfaceFlinger::listLayersLocked(std::string& result) const {
mCurrentState.traverseInZOrder(
[&](Layer* layer) { StringAppendF(&result, "%s\n", layer->getName().string()); });
@@ -4714,6 +4718,14 @@
result.append("\n");
}
+ {
+ StringAppendF(&result, "Composition layers\n");
+ mDrawingState.traverseInZOrder([&](Layer* layer) {
+ auto compositionLayer = layer->getCompositionLayer();
+ if (compositionLayer) compositionLayer->dump(result);
+ });
+ }
+
/*
* Dump Display state
*/
@@ -5569,18 +5581,7 @@
// buffer bounds.
clientCompositionDisplay.physicalDisplay = Rect(reqWidth, reqHeight);
ui::Transform transform = renderArea.getTransform();
- mat4 m;
- m[0][0] = transform[0][0];
- m[0][1] = transform[0][1];
- m[0][3] = transform[0][2];
- m[1][0] = transform[1][0];
- m[1][1] = transform[1][1];
- m[1][3] = transform[1][2];
- m[3][0] = transform[2][0];
- m[3][1] = transform[2][1];
- m[3][3] = transform[2][2];
-
- clientCompositionDisplay.globalTransform = m;
+ clientCompositionDisplay.globalTransform = transform.asMatrix4();
mat4 rotMatrix;
// Displacement for repositioning the clipping rectangle after rotating it
// with the rotation hint.
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 103672c..0d39cb5 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -905,9 +905,7 @@
status_t doDump(int fd, const DumpArgs& args, bool asProto);
- status_t dumpCritical(int fd, const DumpArgs&, bool asProto) override {
- return doDump(fd, DumpArgs(), asProto);
- }
+ status_t dumpCritical(int fd, const DumpArgs&, bool asProto);
status_t dumpAll(int fd, const DumpArgs& args, bool asProto) override {
return doDump(fd, args, asProto);