Merge "Update BUFFER_CACHE_MAX_SIZE" into udc-qpr-dev
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 5c324b2..207fa4f 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -303,13 +303,8 @@
// frame numbers that were in a sync. We remove the frame from mSyncedFrameNumbers
// set and then check if it's empty. If there are no more pending syncs, we can
// proceed with flushing the shadow queue.
- // We also want to check if mSyncTransaction is null because it's possible another
- // sync request came in while waiting, but it hasn't started processing yet. In that
- // case, we don't actually want to flush the frames in between since they will get
- // processed and merged with the sync transaction and released earlier than if they
- // were sent to SF
mSyncedFrameNumbers.erase(currFrameNumber);
- if (mSyncedFrameNumbers.empty() && mSyncTransaction == nullptr) {
+ if (mSyncedFrameNumbers.empty()) {
flushShadowQueue();
}
} else {
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index d2228f1..b77174a 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -510,7 +510,8 @@
auto effect =
shaders::LinearEffect{.inputDataspace = parameters.layer.sourceDataspace,
.outputDataspace = parameters.outputDataSpace,
- .undoPremultipliedAlpha = parameters.undoPremultipliedAlpha};
+ .undoPremultipliedAlpha = parameters.undoPremultipliedAlpha,
+ .fakeOutputDataspace = parameters.fakeOutputDataspace};
auto effectIter = mRuntimeEffects.find(effect);
sk_sp<SkRuntimeEffect> runtimeEffect = nullptr;
@@ -906,12 +907,14 @@
(display.outputDataspace & ui::Dataspace::TRANSFER_MASK) ==
static_cast<int32_t>(ui::Dataspace::TRANSFER_SRGB);
- const ui::Dataspace runtimeEffectDataspace = !dimInLinearSpace && isExtendedHdr
+ const bool useFakeOutputDataspaceForRuntimeEffect = !dimInLinearSpace && isExtendedHdr;
+
+ const ui::Dataspace fakeDataspace = useFakeOutputDataspaceForRuntimeEffect
? static_cast<ui::Dataspace>(
(display.outputDataspace & ui::Dataspace::STANDARD_MASK) |
ui::Dataspace::TRANSFER_GAMMA2_2 |
(display.outputDataspace & ui::Dataspace::RANGE_MASK))
- : display.outputDataspace;
+ : ui::Dataspace::UNKNOWN;
// If the input dataspace is range extended, the output dataspace transfer is sRGB
// and dimmingStage is GAMMA_OETF, dim in linear space instead, and
@@ -1018,7 +1021,8 @@
.layerDimmingRatio = dimInLinearSpace
? layerDimmingRatio
: 1.f,
- .outputDataSpace = runtimeEffectDataspace}));
+ .outputDataSpace = display.outputDataspace,
+ .fakeOutputDataspace = fakeDataspace}));
// Turn on dithering when dimming beyond this (arbitrary) threshold...
static constexpr float kDimmingThreshold = 0.2f;
@@ -1082,7 +1086,8 @@
.undoPremultipliedAlpha = false,
.requiresLinearEffect = requiresLinearEffect,
.layerDimmingRatio = layerDimmingRatio,
- .outputDataSpace = runtimeEffectDataspace}));
+ .outputDataSpace = display.outputDataspace,
+ .fakeOutputDataspace = fakeDataspace}));
}
if (layer.disableBlending) {
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index 6457bfa..723e73c 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -157,6 +157,7 @@
bool requiresLinearEffect;
float layerDimmingRatio;
const ui::Dataspace outputDataSpace;
+ const ui::Dataspace fakeOutputDataspace;
};
sk_sp<SkShader> createRuntimeEffectShader(const RuntimeEffectShaderParameters&);
diff --git a/libs/shaders/shaders.cpp b/libs/shaders/shaders.cpp
index c85517a..ef039e5 100644
--- a/libs/shaders/shaders.cpp
+++ b/libs/shaders/shaders.cpp
@@ -168,8 +168,8 @@
void generateOETF(std::string& shader) {
// Only support gamma 2.2 for now
shader.append(R"(
- float OETF(float3 linear) {
- return sign(linear) * pow(abs(linear), (1.0 / 2.2));
+ float3 OETF(float3 linear) {
+ return sign(linear) * pow(abs(linear), float3(1.0 / 2.2));
}
)");
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index df3f7d9..c906c3e 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -668,7 +668,15 @@
} else {
// This pointer was already sent to the window. Use ACTION_HOVER_MOVE.
if (CC_UNLIKELY(maskedAction != AMOTION_EVENT_ACTION_HOVER_MOVE)) {
- LOG(FATAL) << "Expected ACTION_HOVER_MOVE instead of " << entry.getDescription();
+ android::base::LogSeverity severity = android::base::LogSeverity::FATAL;
+ if (entry.flags & AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT) {
+ // The Accessibility injected touch exploration event stream
+ // has known inconsistencies, so log ERROR instead of
+ // crashing the device with FATAL.
+ // TODO(b/286037469): Move a11y severity back to FATAL.
+ severity = android::base::LogSeverity::ERROR;
+ }
+ LOG(severity) << "Expected ACTION_HOVER_MOVE instead of " << entry.getDescription();
}
touchedWindow.targetFlags = InputTarget::Flags::DISPATCH_AS_IS;
}
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 51202b7..f4f0bab 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -3408,6 +3408,29 @@
}
/**
+ * Test that invalid HOVER events sent by accessibility do not cause a fatal crash.
+ */
+TEST_F(InputDispatcherTest, InvalidA11yHoverStreamDoesNotCrash) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 1200, 800));
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+ MotionEventBuilder hoverEnterBuilder =
+ MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(400))
+ .addFlag(AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT);
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, hoverEnterBuilder.build()));
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, hoverEnterBuilder.build()));
+ window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
+ window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
+}
+
+/**
* If mouse is hovering when the touch goes down, the hovering should be stopped via HOVER_EXIT.
*/
TEST_F(InputDispatcherTest, TouchDownAfterMouseHover) {
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h
index d607c75..9f6141a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h
@@ -66,7 +66,7 @@
TexturePool(renderengine::RenderEngine& renderEngine)
: mRenderEngine(renderEngine), mEnabled(false) {}
- virtual ~TexturePool() = default;
+ virtual ~TexturePool();
// Sets the display size for the texture pool.
// This will trigger a reallocation for all remaining textures in the pool.
@@ -83,11 +83,10 @@
// be held by the pool. This is useful when the active display changes.
void setEnabled(bool enable);
- void dump(std::string& out) const;
+ void dump(std::string& out) const EXCLUDES(mMutex);
protected:
// Proteted visibility so that they can be used for testing
- const static constexpr size_t kMinPoolSize = 3;
const static constexpr size_t kMaxPoolSize = 4;
struct Entry {
@@ -96,16 +95,20 @@
};
std::deque<Entry> mPool;
+ std::future<std::shared_ptr<renderengine::ExternalTexture>> mGenTextureFuture;
private:
- std::shared_ptr<renderengine::ExternalTexture> genTexture();
+ std::shared_ptr<renderengine::ExternalTexture> genTexture(ui::Size size);
// Returns a previously borrowed texture to the pool.
void returnTexture(std::shared_ptr<renderengine::ExternalTexture>&& texture,
const sp<Fence>& fence);
- void allocatePool();
- renderengine::RenderEngine& mRenderEngine;
- ui::Size mSize;
+ void genTextureAsyncIfNeeded() REQUIRES(mMutex);
+ void resetPool() REQUIRES(mMutex);
+ renderengine::RenderEngine& mRenderEngine GUARDED_BY(mRenderEngineMutex);
+ ui::Size mSize GUARDED_BY(mMutex);
bool mEnabled;
+ mutable std::mutex mMutex;
+ mutable std::mutex mRenderEngineMutex;
};
} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp b/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp
index 54ecb56..10f58ce 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp
@@ -25,31 +25,61 @@
namespace android::compositionengine::impl::planner {
-void TexturePool::allocatePool() {
+TexturePool::~TexturePool() {
+ if (mGenTextureFuture.valid()) {
+ mGenTextureFuture.get();
+ }
+}
+
+void TexturePool::resetPool() {
+ if (mGenTextureFuture.valid()) {
+ mGenTextureFuture.get();
+ }
mPool.clear();
- if (mEnabled && mSize.isValid()) {
- mPool.resize(kMinPoolSize);
- std::generate_n(mPool.begin(), kMinPoolSize, [&]() {
- return Entry{genTexture(), nullptr};
- });
+ genTextureAsyncIfNeeded();
+}
+
+// Generate a new texture asynchronously so it will not require allocation on the main
+// thread.
+void TexturePool::genTextureAsyncIfNeeded() {
+ if (mEnabled && mSize.isValid() && !mGenTextureFuture.valid()) {
+ mGenTextureFuture = std::async(
+ std::launch::async, [&](ui::Size size) { return genTexture(size); }, mSize);
}
}
void TexturePool::setDisplaySize(ui::Size size) {
+ std::lock_guard lock(mMutex);
if (mSize == size) {
return;
}
mSize = size;
- allocatePool();
+ resetPool();
}
std::shared_ptr<TexturePool::AutoTexture> TexturePool::borrowTexture() {
if (mPool.empty()) {
- return std::make_shared<AutoTexture>(*this, genTexture(), nullptr);
+ std::lock_guard lock(mMutex);
+ std::shared_ptr<TexturePool::AutoTexture> tex;
+ if (mGenTextureFuture.valid()) {
+ tex = std::make_shared<AutoTexture>(*this, mGenTextureFuture.get(), nullptr);
+ } else {
+ tex = std::make_shared<AutoTexture>(*this, genTexture(mSize), nullptr);
+ }
+ // Speculatively generate a new texture, so that the next call does not need
+ // to wait for allocation.
+ genTextureAsyncIfNeeded();
+ return tex;
}
const auto entry = mPool.front();
mPool.pop_front();
+ if (mPool.empty()) {
+ std::lock_guard lock(mMutex);
+ // Similiarly generate a new texture when lending out the last entry, so that
+ // the next call does not need to wait for allocation.
+ genTextureAsyncIfNeeded();
+ }
return std::make_shared<AutoTexture>(*this, entry.texture, entry.fence);
}
@@ -60,6 +90,8 @@
return;
}
+ std::lock_guard lock(mMutex);
+
// Or the texture on the floor if the pool is no longer tracking textures of the same size.
if (static_cast<int32_t>(texture->getBuffer()->getWidth()) != mSize.getWidth() ||
static_cast<int32_t>(texture->getBuffer()->getHeight()) != mSize.getHeight()) {
@@ -80,13 +112,14 @@
mPool.push_back({std::move(texture), fence});
}
-std::shared_ptr<renderengine::ExternalTexture> TexturePool::genTexture() {
- LOG_ALWAYS_FATAL_IF(!mSize.isValid(), "Attempted to generate texture with invalid size");
+std::shared_ptr<renderengine::ExternalTexture> TexturePool::genTexture(ui::Size size) {
+ std::lock_guard lock(mRenderEngineMutex);
+ LOG_ALWAYS_FATAL_IF(!size.isValid(), "Attempted to generate texture with invalid size");
return std::make_shared<
renderengine::impl::
ExternalTexture>(sp<GraphicBuffer>::
- make(static_cast<uint32_t>(mSize.getWidth()),
- static_cast<uint32_t>(mSize.getHeight()),
+ make(static_cast<uint32_t>(size.getWidth()),
+ static_cast<uint32_t>(size.getHeight()),
HAL_PIXEL_FORMAT_RGBA_8888, 1U,
static_cast<uint64_t>(
GraphicBuffer::USAGE_HW_RENDER |
@@ -100,13 +133,16 @@
void TexturePool::setEnabled(bool enabled) {
mEnabled = enabled;
- allocatePool();
+
+ std::lock_guard lock(mMutex);
+ resetPool();
}
void TexturePool::dump(std::string& out) const {
+ std::lock_guard lock(mMutex);
base::StringAppendF(&out,
"TexturePool (%s) has %zu buffers of size [%" PRId32 ", %" PRId32 "]\n",
mEnabled ? "enabled" : "disabled", mPool.size(), mSize.width, mSize.height);
}
-} // namespace android::compositionengine::impl::planner
\ No newline at end of file
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp
index 6fc90fe..494a9f4 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp
@@ -32,9 +32,9 @@
public:
TestableTexturePool(renderengine::RenderEngine& renderEngine) : TexturePool(renderEngine) {}
- size_t getMinPoolSize() const { return kMinPoolSize; }
size_t getMaxPoolSize() const { return kMaxPoolSize; }
size_t getPoolSize() const { return mPool.size(); }
+ size_t isGenTextureFutureValid() const { return mGenTextureFuture.valid(); }
};
struct TexturePoolTest : public testing::Test {
@@ -56,16 +56,8 @@
TestableTexturePool mTexturePool = TestableTexturePool(mRenderEngine);
};
-TEST_F(TexturePoolTest, preallocatesMinPool) {
- EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize());
-}
-
-TEST_F(TexturePoolTest, doesNotAllocateBeyondMinPool) {
- for (size_t i = 0; i < mTexturePool.getMinPoolSize() + 1; i++) {
- auto texture = mTexturePool.borrowTexture();
- }
-
- EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize());
+TEST_F(TexturePoolTest, preallocatesZeroSizePool) {
+ EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
}
TEST_F(TexturePoolTest, cyclesUpToMaxPoolSize) {
@@ -119,10 +111,10 @@
static_cast<int32_t>(texture->get()->getBuffer()->getHeight()));
mTexturePool.setDisplaySize(kDisplaySizeTwo);
- EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize());
+ EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
texture.reset();
// When the texture is returned to the pool, the pool now destroys it.
- EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize());
+ EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
texture = mTexturePool.borrowTexture();
EXPECT_EQ(kDisplaySizeTwo.getWidth(),
@@ -132,14 +124,11 @@
}
TEST_F(TexturePoolTest, freesBuffersWhenDisabled) {
- EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize());
-
std::deque<std::shared_ptr<TexturePool::AutoTexture>> textures;
- for (size_t i = 0; i < mTexturePool.getMinPoolSize() - 1; i++) {
+ for (size_t i = 0; i < 2; i++) {
textures.emplace_back(mTexturePool.borrowTexture());
}
- EXPECT_EQ(mTexturePool.getPoolSize(), 1u);
mTexturePool.setEnabled(false);
EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
@@ -148,12 +137,11 @@
}
TEST_F(TexturePoolTest, doesNotHoldBuffersWhenDisabled) {
- EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize());
mTexturePool.setEnabled(false);
EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
std::deque<std::shared_ptr<TexturePool::AutoTexture>> textures;
- for (size_t i = 0; i < mTexturePool.getMinPoolSize() - 1; i++) {
+ for (size_t i = 0; i < 2; i++) {
textures.emplace_back(mTexturePool.borrowTexture());
}
@@ -162,12 +150,13 @@
EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
}
-TEST_F(TexturePoolTest, reallocatesWhenReEnabled) {
- EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize());
+TEST_F(TexturePoolTest, genFutureWhenReEnabled) {
mTexturePool.setEnabled(false);
EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
+ EXPECT_FALSE(mTexturePool.isGenTextureFutureValid());
mTexturePool.setEnabled(true);
- EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize());
+ EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
+ EXPECT_TRUE(mTexturePool.isGenTextureFutureValid());
}
} // namespace
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index f6ca9e2..195d90c 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -39,7 +39,6 @@
#include <system/window.h>
#include <ui/GraphicTypes.h>
-#include "Display/DisplaySnapshot.h"
#include "DisplayDevice.h"
#include "FrontEnd/DisplayInfo.h"
#include "Layer.h"
@@ -214,10 +213,7 @@
ATRACE_INT(mRenderFrameRateFPSTrace.c_str(), renderFps.getIntValue());
mRefreshRateSelector->setActiveMode(modeId, renderFps);
-
- if (mRefreshRateOverlay) {
- mRefreshRateOverlay->changeRefreshRate(displayFps, renderFps);
- }
+ updateRefreshRateOverlayRate(displayFps, renderFps);
}
status_t DisplayDevice::initiateModeChange(const ActiveModeInfo& info,
@@ -231,10 +227,18 @@
return BAD_VALUE;
}
mUpcomingActiveMode = info;
- ATRACE_INT(mActiveModeFPSHwcTrace.c_str(), info.modeOpt->modePtr->getFps().getIntValue());
- return mHwComposer.setActiveModeWithConstraints(getPhysicalId(),
- info.modeOpt->modePtr->getHwcId(), constraints,
- outTimeline);
+ mIsModeSetPending = true;
+
+ const auto& pendingMode = *info.modeOpt->modePtr;
+ ATRACE_INT(mActiveModeFPSHwcTrace.c_str(), pendingMode.getFps().getIntValue());
+
+ return mHwComposer.setActiveModeWithConstraints(getPhysicalId(), pendingMode.getHwcId(),
+ constraints, outTimeline);
+}
+
+void DisplayDevice::finalizeModeChange(DisplayModeId modeId, Fps displayFps, Fps renderFps) {
+ setActiveMode(modeId, displayFps, renderFps);
+ mIsModeSetPending = false;
}
nsecs_t DisplayDevice::getVsyncPeriodFromHWC() const {
@@ -439,7 +443,7 @@
mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(fpsRange, features);
mRefreshRateOverlay->setLayerStack(getLayerStack());
mRefreshRateOverlay->setViewport(getSize());
- updateRefreshRateOverlayRate(getActiveMode().modePtr->getFps(), getActiveMode().fps);
+ updateRefreshRateOverlayRate(getActiveMode().modePtr->getFps(), getActiveMode().fps, setByHwc);
}
void DisplayDevice::updateRefreshRateOverlayRate(Fps displayFps, Fps renderFps, bool setByHwc) {
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index dc5f8a8..6d2fe54 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -217,6 +217,8 @@
return mUpcomingActiveMode;
}
+ bool isModeSetPending() const REQUIRES(kMainThreadContext) { return mIsModeSetPending; }
+
scheduler::FrameRateMode getActiveMode() const REQUIRES(kMainThreadContext) {
return mRefreshRateSelector->getActiveMode();
}
@@ -228,6 +230,9 @@
hal::VsyncPeriodChangeTimeline* outTimeline)
REQUIRES(kMainThreadContext);
+ void finalizeModeChange(DisplayModeId, Fps displayFps, Fps renderFps)
+ REQUIRES(kMainThreadContext);
+
scheduler::RefreshRateSelector& refreshRateSelector() const { return *mRefreshRateSelector; }
// Extends the lifetime of the RefreshRateSelector, so it can outlive this DisplayDevice.
@@ -302,7 +307,9 @@
ActiveModeInfo mDesiredActiveMode GUARDED_BY(mActiveModeLock);
TracedOrdinal<bool> mDesiredActiveModeChanged GUARDED_BY(mActiveModeLock) =
{ftl::Concat("DesiredActiveModeChanged-", getId().value).c_str(), false};
+
ActiveModeInfo mUpcomingActiveMode GUARDED_BY(kMainThreadContext);
+ bool mIsModeSetPending GUARDED_BY(kMainThreadContext) = false;
};
struct DisplayDeviceState {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 0dca21e..0b6ce80 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -186,7 +186,17 @@
FrameTargeter& pacesetterTargeter = *pacesetterOpt->get().targeterPtr;
pacesetterTargeter.beginFrame(beginFrameArgs, *pacesetterOpt->get().schedulePtr);
- if (!compositor.commit(pacesetterTargeter.target())) return;
+ FrameTargets targets;
+ targets.try_emplace(pacesetterId, &pacesetterTargeter.target());
+
+ for (const auto& [id, display] : mDisplays) {
+ if (id == pacesetterId) continue;
+
+ const FrameTargeter& targeter = *display.targeterPtr;
+ targets.try_emplace(id, &targeter.target());
+ }
+
+ if (!compositor.commit(pacesetterId, targets)) return;
// TODO(b/256196556): Choose the frontrunner display.
FrameTargeters targeters;
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h b/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h
index 6fe813a..12ee36e 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h
@@ -29,6 +29,7 @@
class FrameTarget;
class FrameTargeter;
+using FrameTargets = ui::PhysicalDisplayMap<PhysicalDisplayId, const scheduler::FrameTarget*>;
using FrameTargeters = ui::PhysicalDisplayMap<PhysicalDisplayId, scheduler::FrameTargeter*>;
} // namespace scheduler
@@ -39,7 +40,7 @@
// Commits transactions for layers and displays. Returns whether any state has been invalidated,
// i.e. whether a frame should be composited for each display.
- virtual bool commit(const scheduler::FrameTarget&) = 0;
+ virtual bool commit(PhysicalDisplayId pacesetterId, const scheduler::FrameTargets&) = 0;
// Composites a frame for each display. CompositionEngine performs GPU and/or HAL composition
// via RenderEngine and the Composer HAL, respectively.
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 9f8af90..c0d6d72 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1197,9 +1197,9 @@
}
void SurfaceFlinger::setDesiredActiveMode(display::DisplayModeRequest&& request, bool force) {
- ATRACE_CALL();
-
const auto displayId = request.mode.modePtr->getPhysicalDisplayId();
+ ATRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str());
+
const auto display = getDisplayDeviceLocked(displayId);
if (!display) {
ALOGW("%s: display is no longer valid", __func__);
@@ -1227,17 +1227,24 @@
// As we called to set period, we will call to onRefreshRateChangeCompleted once
// VsyncController model is locked.
mScheduler->modulateVsync(displayId, &VsyncModulator::onRefreshRateChangeInitiated);
- updatePhaseConfiguration(mode.fps);
+
+ if (displayId == mActiveDisplayId) {
+ updatePhaseConfiguration(mode.fps);
+ }
+
mScheduler->setModeChangePending(true);
break;
case DisplayDevice::DesiredActiveModeAction::InitiateRenderRateSwitch:
mScheduler->setRenderRate(displayId, mode.fps);
- updatePhaseConfiguration(mode.fps);
- mRefreshRateStats->setRefreshRate(mode.fps);
- if (display->getPhysicalId() == mActiveDisplayId && emitEvent) {
- mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, mode);
+
+ if (displayId == mActiveDisplayId) {
+ updatePhaseConfiguration(mode.fps);
+ mRefreshRateStats->setRefreshRate(mode.fps);
}
+ if (emitEvent) {
+ dispatchDisplayModeChangeEvent(displayId, mode);
+ }
break;
case DisplayDevice::DesiredActiveModeAction::None:
break;
@@ -1293,24 +1300,20 @@
return future.get();
}
-void SurfaceFlinger::updateInternalStateWithChangedMode() {
- ATRACE_CALL();
+void SurfaceFlinger::finalizeDisplayModeChange(DisplayDevice& display) {
+ const auto displayId = display.getPhysicalId();
+ ATRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str());
- const auto display = getDefaultDisplayDeviceLocked();
- if (!display) {
- return;
- }
-
- const auto upcomingModeInfo = display->getUpcomingActiveMode();
+ const auto upcomingModeInfo = display.getUpcomingActiveMode();
if (!upcomingModeInfo.modeOpt) {
// There is no pending mode change. This can happen if the active
// display changed and the mode change happened on a different display.
return;
}
- if (display->getActiveMode().modePtr->getResolution() !=
+ if (display.getActiveMode().modePtr->getResolution() !=
upcomingModeInfo.modeOpt->modePtr->getResolution()) {
- auto& state = mCurrentState.displays.editValueFor(display->getDisplayToken());
+ auto& state = mCurrentState.displays.editValueFor(display.getDisplayToken());
// We need to generate new sequenceId in order to recreate the display (and this
// way the framebuffer).
state.sequenceId = DisplayDeviceState{}.sequenceId;
@@ -1321,27 +1324,24 @@
return;
}
- mPhysicalDisplays.get(display->getPhysicalId())
- .transform(&PhysicalDisplay::snapshotRef)
- .transform(ftl::unit_fn([&](const display::DisplaySnapshot& snapshot) {
- FTL_FAKE_GUARD(kMainThreadContext,
- display->setActiveMode(upcomingModeInfo.modeOpt->modePtr->getId(),
- upcomingModeInfo.modeOpt->modePtr->getFps(),
- upcomingModeInfo.modeOpt->fps));
- }));
+ const auto& activeMode = *upcomingModeInfo.modeOpt;
+ display.finalizeModeChange(activeMode.modePtr->getId(), activeMode.modePtr->getFps(),
+ activeMode.fps);
- const Fps refreshRate = upcomingModeInfo.modeOpt->fps;
- mRefreshRateStats->setRefreshRate(refreshRate);
- updatePhaseConfiguration(refreshRate);
+ if (displayId == mActiveDisplayId) {
+ mRefreshRateStats->setRefreshRate(activeMode.fps);
+ updatePhaseConfiguration(activeMode.fps);
+ }
if (upcomingModeInfo.event != scheduler::DisplayModeEvent::None) {
- mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, *upcomingModeInfo.modeOpt);
+ dispatchDisplayModeChangeEvent(displayId, activeMode);
}
}
void SurfaceFlinger::clearDesiredActiveModeState(const sp<DisplayDevice>& display) {
display->clearDesiredActiveModeState();
if (display->getPhysicalId() == mActiveDisplayId) {
+ // TODO(b/255635711): Check for pending mode changes on other displays.
mScheduler->setModeChangePending(false);
}
}
@@ -1355,21 +1355,18 @@
clearDesiredActiveModeState(display);
mScheduler->resyncToHardwareVsync(displayId, true /* allowToEnable */, displayFps);
mScheduler->setRenderRate(displayId, renderFps);
- updatePhaseConfiguration(renderFps);
+
+ if (displayId == mActiveDisplayId) {
+ updatePhaseConfiguration(renderFps);
+ }
}
-void SurfaceFlinger::setActiveModeInHwcIfNeeded() {
+void SurfaceFlinger::initiateDisplayModeChanges() {
ATRACE_CALL();
std::optional<PhysicalDisplayId> displayToUpdateImmediately;
for (const auto& [id, physical] : mPhysicalDisplays) {
- const auto& snapshot = physical.snapshot();
-
- if (snapshot.connectionType() != ui::DisplayConnectionType::Internal) {
- continue;
- }
-
const auto display = getDisplayDeviceLocked(id);
if (!display) continue;
@@ -1380,14 +1377,14 @@
continue;
}
- if (id != mActiveDisplayId) {
- // Display is no longer the active display, so abort the mode change.
+ if (!display->isPoweredOn()) {
+ // Display is no longer powered on, so abort the mode change.
clearDesiredActiveModeState(display);
continue;
}
const auto desiredModeId = desiredActiveMode->modeOpt->modePtr->getId();
- const auto displayModePtrOpt = snapshot.displayModes().get(desiredModeId);
+ const auto displayModePtrOpt = physical.snapshot().displayModes().get(desiredModeId);
if (!displayModePtrOpt) {
ALOGW("Desired display mode is no longer supported. Mode ID = %d",
@@ -1437,19 +1434,18 @@
if (outTimeline.refreshRequired) {
scheduleComposite(FrameHint::kNone);
- mSetActiveModePending = true;
} else {
- // Updating the internal state should be done outside the loop,
- // because it can recreate a DisplayDevice and modify mDisplays
- // which will invalidate the iterator.
+ // TODO(b/255635711): Remove `displayToUpdateImmediately` to `finalizeDisplayModeChange`
+ // for all displays. This was only needed when the loop iterated over `mDisplays` rather
+ // than `mPhysicalDisplays`.
displayToUpdateImmediately = display->getPhysicalId();
}
}
if (displayToUpdateImmediately) {
- updateInternalStateWithChangedMode();
-
const auto display = getDisplayDeviceLocked(*displayToUpdateImmediately);
+ finalizeDisplayModeChange(*display);
+
const auto desiredActiveMode = display->getDesiredActiveMode();
if (desiredActiveMode && display->getActiveMode() == desiredActiveMode->modeOpt) {
desiredActiveModeChangeDone(display);
@@ -2336,7 +2332,10 @@
return mustComposite;
}
-bool SurfaceFlinger::commit(const scheduler::FrameTarget& pacesetterFrameTarget) {
+bool SurfaceFlinger::commit(PhysicalDisplayId pacesetterId,
+ const scheduler::FrameTargets& frameTargets) {
+ const scheduler::FrameTarget& pacesetterFrameTarget = *frameTargets.get(pacesetterId)->get();
+
const VsyncId vsyncId = pacesetterFrameTarget.vsyncId();
ATRACE_NAME(ftl::Concat(__func__, ' ', ftl::to_underlying(vsyncId)).c_str());
@@ -2349,20 +2348,35 @@
mTracingEnabledChanged = false;
}
- // If we are in the middle of a mode change and the fence hasn't
- // fired yet just wait for the next commit.
- if (mSetActiveModePending) {
- if (pacesetterFrameTarget.isFramePending()) {
- mScheduler->scheduleFrame();
- return false;
- }
+ // If a mode set is pending and the fence hasn't fired yet, wait for the next commit.
+ if (std::any_of(frameTargets.begin(), frameTargets.end(),
+ [this](const auto& pair) FTL_FAKE_GUARD(mStateLock)
+ FTL_FAKE_GUARD(kMainThreadContext) {
+ if (!pair.second->isFramePending()) return false;
- // We received the present fence from the HWC, so we assume it successfully updated
- // the mode, hence we update SF.
- mSetActiveModePending = false;
- {
- Mutex::Autolock lock(mStateLock);
- updateInternalStateWithChangedMode();
+ if (const auto display = getDisplayDeviceLocked(pair.first)) {
+ return display->isModeSetPending();
+ }
+
+ return false;
+ })) {
+ mScheduler->scheduleFrame();
+ return false;
+ }
+
+ {
+ Mutex::Autolock lock(mStateLock);
+
+ for (const auto [id, target] : frameTargets) {
+ // TODO(b/241285876): This is `nullptr` when the DisplayDevice is about to be removed in
+ // this commit, since the PhysicalDisplay has already been removed. Rather than checking
+ // for `nullptr` below, change Scheduler::onFrameSignal to filter out the FrameTarget of
+ // the removed display.
+ const auto display = getDisplayDeviceLocked(id);
+
+ if (display && display->isModeSetPending()) {
+ finalizeDisplayModeChange(*display);
+ }
}
}
@@ -2456,7 +2470,7 @@
{
Mutex::Autolock lock(mStateLock);
mScheduler->chooseRefreshRateForContent();
- setActiveModeInHwcIfNeeded();
+ initiateDisplayModeChanges();
}
updateCursorAsync();
@@ -3238,6 +3252,16 @@
mScheduler->onHotplugReceived(mSfConnectionHandle, displayId, connected);
}
+void SurfaceFlinger::dispatchDisplayModeChangeEvent(PhysicalDisplayId displayId,
+ const scheduler::FrameRateMode& mode) {
+ // TODO(b/255635821): Merge code paths and move to Scheduler.
+ const auto onDisplayModeChanged = displayId == mActiveDisplayId
+ ? &scheduler::Scheduler::onPrimaryDisplayModeChanged
+ : &scheduler::Scheduler::onNonPrimaryDisplayModeChanged;
+
+ ((*mScheduler).*onDisplayModeChanged)(mAppConnectionHandle, mode);
+}
+
sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal(
const wp<IBinder>& displayToken,
std::shared_ptr<compositionengine::Display> compositionDisplay,
@@ -3337,14 +3361,8 @@
Dataspace::UNKNOWN});
if (const auto& physical = state.physical) {
- mPhysicalDisplays.get(physical->id)
- .transform(&PhysicalDisplay::snapshotRef)
- .transform(ftl::unit_fn([&](const display::DisplaySnapshot& snapshot) {
- FTL_FAKE_GUARD(kMainThreadContext,
- display->setActiveMode(physical->activeMode->getId(),
- physical->activeMode->getFps(),
- physical->activeMode->getFps()));
- }));
+ const auto& mode = *physical->activeMode;
+ display->setActiveMode(mode.getId(), mode.getFps(), mode.getFps());
}
display->setLayerFilter(makeLayerFilterForDisplay(display->getId(), state.layerStack));
@@ -3863,12 +3881,8 @@
if (!display) continue;
- const bool isInternalDisplay = mPhysicalDisplays.get(displayId)
- .transform(&PhysicalDisplay::isInternal)
- .value_or(false);
-
- if (isInternalDisplay && displayId != mActiveDisplayId) {
- ALOGV("%s(%s): Inactive display", __func__, to_string(displayId).c_str());
+ if (!display->isPoweredOn()) {
+ ALOGV("%s(%s): Display is powered off", __func__, to_string(displayId).c_str());
continue;
}
@@ -3876,7 +3890,7 @@
setDesiredActiveMode(std::move(request));
} else {
ALOGV("%s: Mode %d is disallowed for display %s", __func__, modePtr->getId().value(),
- to_string(display->getId()).c_str());
+ to_string(displayId).c_str());
}
}
}
@@ -7581,6 +7595,7 @@
const sp<DisplayDevice>& display,
const scheduler::RefreshRateSelector::PolicyVariant& policy) {
const auto displayId = display->getPhysicalId();
+ ATRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str());
Mutex::Autolock lock(mStateLock);
@@ -7601,13 +7616,11 @@
break;
}
- const bool isInternalDisplay = mPhysicalDisplays.get(displayId)
- .transform(&PhysicalDisplay::isInternal)
- .value_or(false);
-
- if (isInternalDisplay && displayId != mActiveDisplayId) {
- // The policy will be be applied when the display becomes active.
- ALOGV("%s(%s): Inactive display", __func__, to_string(displayId).c_str());
+ // TODO(b/255635711): Apply the policy once the display is powered on, which is currently only
+ // done for the internal display that becomes active on fold/unfold. For now, assume that DM
+ // always powers on the secondary (internal or external) display before setting its policy.
+ if (!display->isPoweredOn()) {
+ ALOGV("%s(%s): Display is powered off", __func__, to_string(displayId).c_str());
return NO_ERROR;
}
@@ -7813,19 +7826,22 @@
bool setByHwc = getHwComposer().hasCapability(Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG);
for (const auto& [id, display] : mPhysicalDisplays) {
if (display.snapshot().connectionType() == ui::DisplayConnectionType::Internal) {
- if (setByHwc) {
- const auto status =
- getHwComposer().setRefreshRateChangedCallbackDebugEnabled(id, enable);
- if (status != NO_ERROR) {
- ALOGE("Error updating the refresh rate changed callback debug enabled");
- return;
- }
- }
-
if (const auto device = getDisplayDeviceLocked(id)) {
- device->enableRefreshRateOverlay(enable, setByHwc, mRefreshRateOverlaySpinner,
- mRefreshRateOverlayRenderRate,
- mRefreshRateOverlayShowInMiddle);
+ const auto enableOverlay = [&](const bool setByHwc) FTL_FAKE_GUARD(
+ kMainThreadContext) {
+ device->enableRefreshRateOverlay(enable, setByHwc, mRefreshRateOverlaySpinner,
+ mRefreshRateOverlayRenderRate,
+ mRefreshRateOverlayShowInMiddle);
+ };
+ enableOverlay(setByHwc);
+ if (setByHwc) {
+ const auto status =
+ getHwComposer().setRefreshRateChangedCallbackDebugEnabled(id, enable);
+ if (status != NO_ERROR) {
+ ALOGE("Error updating the refresh rate changed callback debug enabled");
+ enableOverlay(/*setByHwc*/ false);
+ }
+ }
}
}
}
@@ -7953,7 +7969,9 @@
resetPhaseConfiguration(activeDisplay.getActiveMode().fps);
+ // TODO(b/255635711): Check for pending mode changes on other displays.
mScheduler->setModeChangePending(false);
+
mScheduler->setPacesetterDisplay(mActiveDisplayId);
onActiveDisplaySizeChanged(activeDisplay);
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index f1759a5..db1c342 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -634,7 +634,8 @@
// ICompositor overrides:
void configure() override REQUIRES(kMainThreadContext);
- bool commit(const scheduler::FrameTarget&) override REQUIRES(kMainThreadContext);
+ bool commit(PhysicalDisplayId pacesetterId, const scheduler::FrameTargets&) override
+ REQUIRES(kMainThreadContext);
CompositeResultsPerDisplay composite(PhysicalDisplayId pacesetterId,
const scheduler::FrameTargeters&) override
REQUIRES(kMainThreadContext);
@@ -677,11 +678,10 @@
REQUIRES(mStateLock);
status_t setActiveModeFromBackdoor(const sp<display::DisplayToken>&, DisplayModeId);
- // Sets the active mode and a new refresh rate in SF.
- void updateInternalStateWithChangedMode() REQUIRES(mStateLock, kMainThreadContext);
- // Calls to setActiveMode on the main thread if there is a pending mode change
- // that needs to be applied.
- void setActiveModeInHwcIfNeeded() REQUIRES(mStateLock, kMainThreadContext);
+
+ void initiateDisplayModeChanges() REQUIRES(mStateLock, kMainThreadContext);
+ void finalizeDisplayModeChange(DisplayDevice&) REQUIRES(mStateLock, kMainThreadContext);
+
void clearDesiredActiveModeState(const sp<DisplayDevice>&) REQUIRES(mStateLock);
// Called when active mode is no longer is progress
void desiredActiveModeChangeDone(const sp<DisplayDevice>&) REQUIRES(mStateLock);
@@ -1003,7 +1003,9 @@
const DisplayDeviceState& drawingState)
REQUIRES(mStateLock, kMainThreadContext);
- void dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected);
+ void dispatchDisplayHotplugEvent(PhysicalDisplayId, bool connected);
+ void dispatchDisplayModeChangeEvent(PhysicalDisplayId, const scheduler::FrameRateMode&)
+ REQUIRES(mStateLock);
/*
* VSYNC
@@ -1322,9 +1324,6 @@
std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
scheduler::PresentLatencyTracker mPresentLatencyTracker GUARDED_BY(kMainThreadContext);
- // below flags are set by main thread only
- bool mSetActiveModePending = false;
-
bool mLumaSampling = true;
sp<RegionSamplingThread> mRegionSamplingThread;
sp<FpsReporter> mFpsReporter;
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index 36095b4..649df56 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -286,7 +286,7 @@
private:
// ICompositor overrides:
void configure() override {}
- bool commit(const scheduler::FrameTarget&) override { return false; }
+ bool commit(PhysicalDisplayId, const scheduler::FrameTargets&) override { return false; }
CompositeResultsPerDisplay composite(PhysicalDisplayId,
const scheduler::FrameTargeters&) override {
return {};
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index 1dcf222..9aa089f 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -35,7 +35,7 @@
struct NoOpCompositor final : ICompositor {
void configure() override {}
- bool commit(const scheduler::FrameTarget&) override { return false; }
+ bool commit(PhysicalDisplayId, const scheduler::FrameTargets&) override { return false; }
CompositeResultsPerDisplay composite(PhysicalDisplayId,
const scheduler::FrameTargeters&) override {
return {};
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index e176546..3738381 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -59,10 +59,36 @@
.WillByDefault(Return(true));
}
+ static constexpr HWDisplayId kInnerDisplayHwcId = PrimaryDisplayVariant::HWC_DISPLAY_ID;
+ static constexpr HWDisplayId kOuterDisplayHwcId = kInnerDisplayHwcId + 1;
+
+ auto injectOuterDisplay() {
+ constexpr PhysicalDisplayId kOuterDisplayId = PhysicalDisplayId::fromPort(254u);
+
+ constexpr bool kIsPrimary = false;
+ TestableSurfaceFlinger::FakeHwcDisplayInjector(kOuterDisplayId, hal::DisplayType::PHYSICAL,
+ kIsPrimary)
+ .setHwcDisplayId(kOuterDisplayHwcId)
+ .setPowerMode(hal::PowerMode::OFF)
+ .inject(&mFlinger, mComposer);
+
+ mOuterDisplay = mFakeDisplayInjector.injectInternalDisplay(
+ [&](FakeDisplayDeviceInjector& injector) {
+ injector.setPowerMode(hal::PowerMode::OFF);
+ injector.setDisplayModes(mock::cloneForDisplay(kOuterDisplayId, kModes),
+ kModeId120);
+ },
+ {.displayId = kOuterDisplayId,
+ .hwcDisplayId = kOuterDisplayHwcId,
+ .isPrimary = kIsPrimary});
+
+ return std::forward_as_tuple(mDisplay, mOuterDisplay);
+ }
+
protected:
void setupScheduler(std::shared_ptr<scheduler::RefreshRateSelector>);
- sp<DisplayDevice> mDisplay;
+ sp<DisplayDevice> mDisplay, mOuterDisplay;
mock::EventThread* mAppEventThread;
static constexpr DisplayModeId kModeId60{0};
@@ -320,32 +346,16 @@
return true;
}
-TEST_F(DisplayModeSwitchingTest, multiDisplay) {
- constexpr HWDisplayId kInnerDisplayHwcId = PrimaryDisplayVariant::HWC_DISPLAY_ID;
- constexpr HWDisplayId kOuterDisplayHwcId = kInnerDisplayHwcId + 1;
+TEST_F(DisplayModeSwitchingTest, innerXorOuterDisplay) {
+ const auto [innerDisplay, outerDisplay] = injectOuterDisplay();
- constexpr PhysicalDisplayId kOuterDisplayId = PhysicalDisplayId::fromPort(254u);
-
- constexpr bool kIsPrimary = false;
- TestableSurfaceFlinger::FakeHwcDisplayInjector(kOuterDisplayId, hal::DisplayType::PHYSICAL,
- kIsPrimary)
- .setHwcDisplayId(kOuterDisplayHwcId)
- .inject(&mFlinger, mComposer);
-
- const auto outerDisplay = mFakeDisplayInjector.injectInternalDisplay(
- [&](FakeDisplayDeviceInjector& injector) {
- injector.setDisplayModes(mock::cloneForDisplay(kOuterDisplayId, kModes),
- kModeId120);
- },
- {.displayId = kOuterDisplayId,
- .hwcDisplayId = kOuterDisplayHwcId,
- .isPrimary = kIsPrimary});
-
- const auto& innerDisplay = mDisplay;
+ EXPECT_TRUE(innerDisplay->isPoweredOn());
+ EXPECT_FALSE(outerDisplay->isPoweredOn());
EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+ // Only the inner display is powered on.
mFlinger.onActiveDisplayChanged(nullptr, *innerDisplay);
EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
@@ -380,6 +390,10 @@
EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+ innerDisplay->setPowerMode(hal::PowerMode::OFF);
+ outerDisplay->setPowerMode(hal::PowerMode::ON);
+
+ // Only the outer display is powered on.
mFlinger.onActiveDisplayChanged(innerDisplay.get(), *outerDisplay);
EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
@@ -401,5 +415,107 @@
EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId60));
}
+TEST_F(DisplayModeSwitchingTest, innerAndOuterDisplay) {
+ const auto [innerDisplay, outerDisplay] = injectOuterDisplay();
+
+ EXPECT_TRUE(innerDisplay->isPoweredOn());
+ EXPECT_FALSE(outerDisplay->isPoweredOn());
+
+ EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+
+ outerDisplay->setPowerMode(hal::PowerMode::ON);
+
+ // Both displays are powered on.
+ mFlinger.onActiveDisplayChanged(nullptr, *innerDisplay);
+
+ EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+
+ EXPECT_EQ(NO_ERROR,
+ mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(),
+ mock::createDisplayModeSpecs(kModeId90.value(),
+ false, 0.f, 120.f)));
+
+ EXPECT_EQ(NO_ERROR,
+ mFlinger.setDesiredDisplayModeSpecs(outerDisplay->getDisplayToken().promote(),
+ mock::createDisplayModeSpecs(kModeId60.value(),
+ false, 0.f, 120.f)));
+
+ EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
+
+ const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
+ EXPECT_CALL(*mComposer,
+ setActiveConfigWithConstraints(kInnerDisplayHwcId,
+ hal::HWConfigId(kModeId90.value()), _, _))
+ .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+
+ EXPECT_CALL(*mComposer,
+ setActiveConfigWithConstraints(kOuterDisplayHwcId,
+ hal::HWConfigId(kModeId60.value()), _, _))
+ .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+
+ mFlinger.commit();
+
+ EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
+
+ mFlinger.commit();
+
+ EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId60));
+}
+
+TEST_F(DisplayModeSwitchingTest, powerOffDuringModeSet) {
+ const auto [innerDisplay, outerDisplay] = injectOuterDisplay();
+
+ EXPECT_TRUE(innerDisplay->isPoweredOn());
+ EXPECT_FALSE(outerDisplay->isPoweredOn());
+
+ EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+
+ outerDisplay->setPowerMode(hal::PowerMode::ON);
+
+ // Both displays are powered on.
+ mFlinger.onActiveDisplayChanged(nullptr, *innerDisplay);
+
+ EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+
+ EXPECT_EQ(NO_ERROR,
+ mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(),
+ mock::createDisplayModeSpecs(kModeId90.value(),
+ false, 0.f, 120.f)));
+
+ EXPECT_EQ(NO_ERROR,
+ mFlinger.setDesiredDisplayModeSpecs(outerDisplay->getDisplayToken().promote(),
+ mock::createDisplayModeSpecs(kModeId60.value(),
+ false, 0.f, 120.f)));
+
+ EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
+
+ // Power off the outer display before the mode has been set.
+ outerDisplay->setPowerMode(hal::PowerMode::OFF);
+
+ const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
+ EXPECT_CALL(*mComposer,
+ setActiveConfigWithConstraints(kInnerDisplayHwcId,
+ hal::HWConfigId(kModeId90.value()), _, _))
+ .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+
+ mFlinger.commit();
+
+ EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+
+ mFlinger.commit();
+
+ EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+}
+
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index a978984..ffe8c10 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -173,7 +173,7 @@
private:
// ICompositor overrides:
void configure() override {}
- bool commit(const scheduler::FrameTarget&) override { return false; }
+ bool commit(PhysicalDisplayId, const scheduler::FrameTargets&) override { return false; }
CompositeResultsPerDisplay composite(PhysicalDisplayId,
const scheduler::FrameTargeters&) override {
return {};
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index da482d5..6b13c0d 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -380,10 +380,19 @@
.sfWorkDuration = 10ms},
*mScheduler->getVsyncSchedule());
- mFlinger->commit(frameTargeter.target());
+ scheduler::FrameTargets targets;
+ scheduler::FrameTargeters targeters;
+
+ for (const auto& [id, display] :
+ FTL_FAKE_GUARD(mFlinger->mStateLock, mFlinger->mPhysicalDisplays)) {
+ targets.try_emplace(id, &frameTargeter.target());
+ targeters.try_emplace(id, &frameTargeter);
+ }
+
+ mFlinger->commit(displayId, targets);
if (composite) {
- mFlinger->composite(displayId, ftl::init::map(displayId, &frameTargeter));
+ mFlinger->composite(displayId, targeters);
}
}
diff --git a/vulkan/include/vulkan/vk_android_native_buffer.h b/vulkan/include/vulkan/vk_android_native_buffer.h
index 40cf9fb..e78f470 100644
--- a/vulkan/include/vulkan/vk_android_native_buffer.h
+++ b/vulkan/include/vulkan/vk_android_native_buffer.h
@@ -55,7 +55,12 @@
* This version of the extension is largely designed to clean up the mix of
* GrallocUsage and GrallocUsage2
*/
-#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 9
+/*
+ * NOTE ON VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 10
+ *
+ * This version of the extension cleans up a bug introduced in version 9
+ */
+#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 10
#define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME "VK_ANDROID_native_buffer"
#define VK_ANDROID_NATIVE_BUFFER_ENUM(type, id) \
@@ -69,6 +74,8 @@
VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 2)
#define VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_ANDROID \
VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 3)
+#define VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_2_ANDROID \
+ VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 4)
/* clang-format off */
typedef enum VkSwapchainImageUsageFlagBitsANDROID {
@@ -152,6 +159,23 @@
VkImageUsageFlags imageUsage;
} VkGrallocUsageInfoANDROID;
+/*
+ * struct VkGrallocUsageInfo2ANDROID
+ *
+ * sType: VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_2_ANDROID
+ * pNext: NULL or a pointer to a structure extending this structure
+ * format: value specifying the format the image will be created with
+ * imageUsage: bitmask of VkImageUsageFlagBits describing intended usage
+ * swapchainImageUsage: is a bitmask of VkSwapchainImageUsageFlagsANDROID
+ */
+typedef struct {
+ VkStructureType sType;
+ const void* pNext;
+ VkFormat format;
+ VkImageUsageFlags imageUsage;
+ VkSwapchainImageUsageFlagsANDROID swapchainImageUsage;
+} VkGrallocUsageInfo2ANDROID;
+
/* DEPRECATED in SPEC_VERSION 6 */
typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsageANDROID)(
VkDevice device,
@@ -168,12 +192,18 @@
uint64_t* grallocConsumerUsage,
uint64_t* grallocProducerUsage);
-/* ADDED in SPEC_VERSION 9 */
+/* DEPRECATED in SPEC_VERSION 10 */
typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage3ANDROID)(
VkDevice device,
const VkGrallocUsageInfoANDROID* grallocUsageInfo,
uint64_t* grallocUsage);
+/* ADDED in SPEC_VERSION 10 */
+typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage4ANDROID)(
+ VkDevice device,
+ const VkGrallocUsageInfo2ANDROID* grallocUsageInfo,
+ uint64_t* grallocUsage);
+
typedef VkResult (VKAPI_PTR *PFN_vkAcquireImageANDROID)(
VkDevice device,
VkImage image,
@@ -208,13 +238,20 @@
uint64_t* grallocProducerUsage
);
-/* ADDED in SPEC_VERSION 9 */
+/* DEPRECATED in SPEC_VERSION 10 */
VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage3ANDROID(
VkDevice device,
const VkGrallocUsageInfoANDROID* grallocUsageInfo,
uint64_t* grallocUsage
);
+/* ADDED in SPEC_VERSION 10 */
+VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage4ANDROID(
+ VkDevice device,
+ const VkGrallocUsageInfo2ANDROID* grallocUsageInfo,
+ uint64_t* grallocUsage
+);
+
VKAPI_ATTR VkResult VKAPI_CALL vkAcquireImageANDROID(
VkDevice device,
VkImage image,
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 273cdd5..d21deef 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -1422,13 +1422,15 @@
if ((wrapper.GetHalExtensions()[ProcHook::ANDROID_native_buffer]) &&
!data->driver.GetSwapchainGrallocUsageANDROID &&
!data->driver.GetSwapchainGrallocUsage2ANDROID &&
- !data->driver.GetSwapchainGrallocUsage3ANDROID) {
+ !data->driver.GetSwapchainGrallocUsage3ANDROID &&
+ !data->driver.GetSwapchainGrallocUsage4ANDROID) {
ALOGE(
"Driver's implementation of ANDROID_native_buffer is broken;"
" must expose at least one of "
"vkGetSwapchainGrallocUsageANDROID or "
"vkGetSwapchainGrallocUsage2ANDROID or "
- "vkGetSwapchainGrallocUsage3ANDROID");
+ "vkGetSwapchainGrallocUsage3ANDROID or "
+ "vkGetSwapchainGrallocUsage4ANDROID");
data->driver.DestroyDevice(dev, pAllocator);
FreeDeviceData(data, data_allocator);
diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp
index 798af5c..8f09008 100644
--- a/vulkan/libvulkan/driver_gen.cpp
+++ b/vulkan/libvulkan/driver_gen.cpp
@@ -512,6 +512,13 @@
nullptr,
},
{
+ "vkGetSwapchainGrallocUsage4ANDROID",
+ ProcHook::DEVICE,
+ ProcHook::ANDROID_native_buffer,
+ nullptr,
+ nullptr,
+ },
+ {
"vkGetSwapchainGrallocUsageANDROID",
ProcHook::DEVICE,
ProcHook::ANDROID_native_buffer,
@@ -692,6 +699,7 @@
INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsageANDROID);
INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsage2ANDROID);
INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsage3ANDROID);
+ INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsage4ANDROID);
INIT_PROC_EXT(ANDROID_native_buffer, true, dev, AcquireImageANDROID);
INIT_PROC_EXT(ANDROID_native_buffer, true, dev, QueueSignalReleaseImageANDROID);
// clang-format on
diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h
index 31ba04b..4527214 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -128,6 +128,7 @@
PFN_vkGetSwapchainGrallocUsageANDROID GetSwapchainGrallocUsageANDROID;
PFN_vkGetSwapchainGrallocUsage2ANDROID GetSwapchainGrallocUsage2ANDROID;
PFN_vkGetSwapchainGrallocUsage3ANDROID GetSwapchainGrallocUsage3ANDROID;
+ PFN_vkGetSwapchainGrallocUsage4ANDROID GetSwapchainGrallocUsage4ANDROID;
PFN_vkAcquireImageANDROID AcquireImageANDROID;
PFN_vkQueueSignalReleaseImageANDROID QueueSignalReleaseImageANDROID;
// clang-format on
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index b73d2cb..c28390f 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -1569,7 +1569,47 @@
void* usage_info_pNext = nullptr;
VkImageCompressionControlEXT image_compression = {};
uint64_t native_usage = 0;
- if (dispatch.GetSwapchainGrallocUsage3ANDROID) {
+ if (dispatch.GetSwapchainGrallocUsage4ANDROID) {
+ ATRACE_BEGIN("GetSwapchainGrallocUsage4ANDROID");
+ VkGrallocUsageInfo2ANDROID gralloc_usage_info = {};
+ gralloc_usage_info.sType =
+ VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_2_ANDROID;
+ gralloc_usage_info.format = create_info->imageFormat;
+ gralloc_usage_info.imageUsage = create_info->imageUsage;
+ gralloc_usage_info.swapchainImageUsage = swapchain_image_usage;
+
+ // Look through the pNext chain for an image compression control struct
+ // if one is found AND the appropriate extensions are enabled,
+ // append it to be the gralloc usage pNext chain
+ const VkSwapchainCreateInfoKHR* create_infos = create_info;
+ while (create_infos->pNext) {
+ create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>(
+ create_infos->pNext);
+ switch (create_infos->sType) {
+ case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: {
+ const VkImageCompressionControlEXT* compression_infos =
+ reinterpret_cast<const VkImageCompressionControlEXT*>(
+ create_infos);
+ image_compression = *compression_infos;
+ image_compression.pNext = nullptr;
+ usage_info_pNext = &image_compression;
+ } break;
+
+ default:
+ // Ignore all other info structs
+ break;
+ }
+ }
+ gralloc_usage_info.pNext = usage_info_pNext;
+
+ result = dispatch.GetSwapchainGrallocUsage4ANDROID(
+ device, &gralloc_usage_info, &native_usage);
+ ATRACE_END();
+ if (result != VK_SUCCESS) {
+ ALOGE("vkGetSwapchainGrallocUsage4ANDROID failed: %d", result);
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
+ } else if (dispatch.GetSwapchainGrallocUsage3ANDROID) {
ATRACE_BEGIN("GetSwapchainGrallocUsage3ANDROID");
VkGrallocUsageInfoANDROID gralloc_usage_info = {};
gralloc_usage_info.sType = VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_ANDROID;
diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp
index f998b1a..2e87f17 100644
--- a/vulkan/nulldrv/null_driver.cpp
+++ b/vulkan/nulldrv/null_driver.cpp
@@ -959,6 +959,17 @@
return VK_SUCCESS;
}
+VkResult GetSwapchainGrallocUsage4ANDROID(
+ VkDevice,
+ const VkGrallocUsageInfo2ANDROID* grallocUsageInfo,
+ uint64_t* grallocUsage) {
+ // The null driver never reads or writes the gralloc buffer
+ ALOGV("TODO: vk%s - grallocUsageInfo->format:%i", __FUNCTION__,
+ grallocUsageInfo->format);
+ *grallocUsage = 0;
+ return VK_SUCCESS;
+}
+
VkResult AcquireImageANDROID(VkDevice,
VkImage,
int fence,
diff --git a/vulkan/nulldrv/null_driver_gen.cpp b/vulkan/nulldrv/null_driver_gen.cpp
index 0cb7bd3..935535f 100644
--- a/vulkan/nulldrv/null_driver_gen.cpp
+++ b/vulkan/nulldrv/null_driver_gen.cpp
@@ -262,6 +262,7 @@
{"vkGetSemaphoreCounterValue", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSemaphoreCounterValue>(GetSemaphoreCounterValue))},
{"vkGetSwapchainGrallocUsage2ANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsage2ANDROID>(GetSwapchainGrallocUsage2ANDROID))},
{"vkGetSwapchainGrallocUsage3ANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsage3ANDROID>(GetSwapchainGrallocUsage3ANDROID))},
+ {"vkGetSwapchainGrallocUsage4ANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsage4ANDROID>(GetSwapchainGrallocUsage4ANDROID))},
{"vkGetSwapchainGrallocUsageANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsageANDROID>(GetSwapchainGrallocUsageANDROID))},
{"vkInvalidateMappedMemoryRanges", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkInvalidateMappedMemoryRanges>(InvalidateMappedMemoryRanges))},
{"vkMapMemory", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkMapMemory>(MapMemory))},
diff --git a/vulkan/nulldrv/null_driver_gen.h b/vulkan/nulldrv/null_driver_gen.h
index 5c7fea0..fb3bd05 100644
--- a/vulkan/nulldrv/null_driver_gen.h
+++ b/vulkan/nulldrv/null_driver_gen.h
@@ -210,6 +210,7 @@
VKAPI_ATTR VkResult GetSwapchainGrallocUsageANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int* grallocUsage);
VKAPI_ATTR VkResult GetSwapchainGrallocUsage2ANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, uint64_t* grallocConsumerUsage, uint64_t* grallocProducerUsage);
VKAPI_ATTR VkResult GetSwapchainGrallocUsage3ANDROID(VkDevice device, const VkGrallocUsageInfoANDROID* grallocUsageInfo, uint64_t* grallocUsage);
+VKAPI_ATTR VkResult GetSwapchainGrallocUsage4ANDROID(VkDevice device, const VkGrallocUsageInfo2ANDROID* grallocUsageInfo, uint64_t* grallocUsage);
VKAPI_ATTR VkResult AcquireImageANDROID(VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence);
VKAPI_ATTR VkResult QueueSignalReleaseImageANDROID(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd);
VKAPI_ATTR VkResult CreateRenderPass2(VkDevice device, const VkRenderPassCreateInfo2* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass);
diff --git a/vulkan/scripts/generator_common.py b/vulkan/scripts/generator_common.py
index c25c6cb..866c1b7 100644
--- a/vulkan/scripts/generator_common.py
+++ b/vulkan/scripts/generator_common.py
@@ -70,6 +70,7 @@
'vkGetSwapchainGrallocUsageANDROID',
'vkGetSwapchainGrallocUsage2ANDROID',
'vkGetSwapchainGrallocUsage3ANDROID',
+ 'vkGetSwapchainGrallocUsage4ANDROID',
]
# Dict for mapping dispatch table to a type.