Skip creating external textures that exceeds size limit
Skia always accept an Android buffer and wraps it around with a texture
even if its size exceeds the limit GL exposes. Therefore let's skip
creating the texture in SurfaceFlinger and outputs an error log to
logcat.
I chose to do it in SurfaceFlinger rather than RenderEngine is because
the external texture mapping is designed to be asynchronous, so it'd be
better to keep that way. The limit is also exposed out of RenderEngine
so SurfaceFlinger can check it before creating external textures as
well.
Bug: 190399306
Test: The test mentioned in the bug fails instead of crashing
SurfaceFlinger.
Test: atest SurfaceFlinger_test
Test: atest libsurfaceflinger_unittest
Change-Id: I52d253ed5a10f0e4ade372048721913405ed668a
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index a5b28fe..07f8562 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -798,6 +798,8 @@
? renderengine::RenderEngine::ContextPriority::REALTIME
: renderengine::RenderEngine::ContextPriority::MEDIUM)
.build()));
+ mMaxRenderTargetSize =
+ std::min(getRenderEngine().getMaxTextureSize(), getRenderEngine().getMaxViewportDims());
// Set SF main policy after initializing RenderEngine which has its own policy.
if (!SetTaskProfiles(0, {"SFMainPolicy"})) {
@@ -885,14 +887,6 @@
}
}
-size_t SurfaceFlinger::getMaxTextureSize() const {
- return getRenderEngine().getMaxTextureSize();
-}
-
-size_t SurfaceFlinger::getMaxViewportDims() const {
- return getRenderEngine().getMaxViewportDims();
-}
-
// ----------------------------------------------------------------------------
bool SurfaceFlinger::authenticateSurfaceTexture(
@@ -4168,17 +4162,30 @@
}
bool bufferChanged = what & layer_state_t::eBufferChanged;
bool cacheIdChanged = what & layer_state_t::eCachedBufferChanged;
+ bool bufferSizeExceedsLimit = false;
std::shared_ptr<renderengine::ExternalTexture> buffer;
if (bufferChanged && cacheIdChanged && s.buffer != nullptr) {
- ClientCache::getInstance().add(s.cachedBuffer, s.buffer);
- buffer = ClientCache::getInstance().get(s.cachedBuffer);
+ bufferSizeExceedsLimit =
+ exceedsMaxRenderTargetSize(s.buffer->getWidth(), s.buffer->getHeight());
+ if (!bufferSizeExceedsLimit) {
+ ClientCache::getInstance().add(s.cachedBuffer, s.buffer);
+ buffer = ClientCache::getInstance().get(s.cachedBuffer);
+ }
} else if (cacheIdChanged) {
buffer = ClientCache::getInstance().get(s.cachedBuffer);
} else if (bufferChanged && s.buffer != nullptr) {
- buffer = std::make_shared<
- renderengine::ExternalTexture>(s.buffer, getRenderEngine(),
- renderengine::ExternalTexture::Usage::READABLE);
+ bufferSizeExceedsLimit =
+ exceedsMaxRenderTargetSize(s.buffer->getWidth(), s.buffer->getHeight());
+ if (!bufferSizeExceedsLimit) {
+ buffer = std::make_shared<
+ renderengine::ExternalTexture>(s.buffer, getRenderEngine(),
+ renderengine::ExternalTexture::Usage::READABLE);
+ }
}
+ ALOGE_IF(bufferSizeExceedsLimit,
+ "Attempted to create an ExternalTexture for layer %s that exceeds render target size "
+ "limit.",
+ layer->getDebugName());
if (buffer) {
const bool frameNumberChanged = what & layer_state_t::eFrameNumberChanged;
const uint64_t frameNumber = frameNumberChanged
@@ -6181,6 +6188,13 @@
const sp<IScreenCaptureListener>& captureListener) {
ATRACE_CALL();
+ if (exceedsMaxRenderTargetSize(bufferSize.getWidth(), bufferSize.getHeight())) {
+ ALOGE("Attempted to capture screen with size (%" PRId32 ", %" PRId32
+ ") that exceeds render target size limit.",
+ bufferSize.getWidth(), bufferSize.getHeight());
+ return BAD_VALUE;
+ }
+
// Loop over all visible layers to see whether there's any protected layer. A protected layer is
// typically a layer with DRM contents, or have the GRALLOC_USAGE_PROTECTED set on the buffer.
// A protected layer has no implication on whether it's secure, which is explicitly set by