Merge "Lock display refresh rate when primary range is a single rate." into rvc-dev
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index deaa15f..a9f2d73 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -95,6 +95,7 @@
while (sm == nullptr) {
sm = interface_cast<AidlServiceManager>(ProcessState::self()->getContextObject(nullptr));
if (sm == nullptr) {
+ ALOGE("Waiting 1s on context object on %s.", ProcessState::self()->getDriverName().c_str());
sleep(1);
}
}
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index 3a401ad..7116154 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -20,6 +20,8 @@
#include <binder/Binder.h>
+#include <assert.h>
+
namespace android {
// ----------------------------------------------------------------------
@@ -155,7 +157,11 @@
std::unique_ptr<I##INTERFACE> I##INTERFACE::default_impl; \
bool I##INTERFACE::setDefaultImpl(std::unique_ptr<I##INTERFACE> impl)\
{ \
- if (!I##INTERFACE::default_impl && impl) { \
+ /* Only one user of this interface can use this function */ \
+ /* at a time. This is a heuristic to detect if two different */ \
+ /* users in the same process use this function. */ \
+ assert(!I##INTERFACE::default_impl); \
+ if (impl) { \
I##INTERFACE::default_impl = std::move(impl); \
return true; \
} \
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
index ca5be48..4835aef 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -38,6 +38,7 @@
out.append("\n ");
+ dumpVal(out, "bounds", bounds);
dumpVal(out, "frame", frame);
dumpVal(out, "viewport", viewport);
dumpVal(out, "sourceClip", sourceClip);
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 0c8aafc..730f297 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -227,6 +227,8 @@
if (destinationClip.isEmpty()) {
destinationClip = displayBounds;
}
+ // Make sure the destination clip is contained in the display bounds
+ destinationClip.intersect(displayBounds, &destinationClip);
uint32_t transformOrientation;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 17458e3..5b9dbf2 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1353,8 +1353,15 @@
// First traverse the tree and count how many layers has votes
int layersWithVote = 0;
traverseTree([&layersWithVote](Layer* layer) {
- if (layer->mCurrentState.frameRate.rate > 0 ||
- layer->mCurrentState.frameRate.type == FrameRateCompatibility::NoVote) {
+ const auto layerVotedWithDefaultCompatibility = layer->mCurrentState.frameRate.rate > 0 &&
+ layer->mCurrentState.frameRate.type == FrameRateCompatibility::Default;
+ const auto layerVotedWithNoVote =
+ layer->mCurrentState.frameRate.type == FrameRateCompatibility::NoVote;
+
+ // We do not count layers that are ExactOrMultiple for the same reason
+ // we are allowing touch boost for those layers. See
+ // RefreshRateConfigs::getBestRefreshRate for more details.
+ if (layerVotedWithDefaultCompatibility || layerVotedWithNoVote) {
layersWithVote++;
}
});
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index a1ae35c..d5bebf6 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -52,6 +52,8 @@
// Sets the display size. Client is responsible for synchronization.
virtual void setDisplayArea(uint32_t displayArea) = 0;
+ virtual void setConfigChangePending(bool pending) = 0;
+
// Marks the layer as active, and records the given state to its history.
virtual void record(Layer*, nsecs_t presentTime, nsecs_t now) = 0;
@@ -78,6 +80,8 @@
void setDisplayArea(uint32_t /*displayArea*/) override {}
+ void setConfigChangePending(bool /*pending*/) override {}
+
// Marks the layer as active, and records the given state to its history.
void record(Layer*, nsecs_t presentTime, nsecs_t now) override;
@@ -134,6 +138,8 @@
// Sets the display size. Client is responsible for synchronization.
void setDisplayArea(uint32_t displayArea) override { mDisplayArea = displayArea; }
+ void setConfigChangePending(bool pending) override { mConfigChangePending = pending; }
+
// Marks the layer as active, and records the given state to its history.
void record(Layer*, nsecs_t presentTime, nsecs_t now) override;
@@ -178,6 +184,9 @@
// Whether to use priority sent from WindowManager to determine the relevancy of the layer.
const bool mUseFrameRatePriority;
+
+ // Whether a config change is in progress or not
+ std::atomic<bool> mConfigChangePending = false;
};
} // namespace impl
diff --git a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
index 120a60f..afc8c4f 100644
--- a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
@@ -103,7 +103,7 @@
LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
const auto& info = it->second;
- info->setLastPresentTime(presentTime, now);
+ info->setLastPresentTime(presentTime, now, mConfigChangePending);
// Activate layer if inactive.
if (const auto end = activeLayers().end(); it >= end) {
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
index 255eac6..78f4433 100644
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
@@ -34,12 +34,15 @@
mDefaultVote(defaultVote),
mLayerVote({defaultVote, 0.0f}) {}
-void LayerInfoV2::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now) {
+void LayerInfoV2::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now,
+ bool pendingConfigChange) {
lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
mLastUpdatedTime = std::max(lastPresentTime, now);
- FrameTimeData frameTime = {.presetTime = lastPresentTime, .queueTime = mLastUpdatedTime};
+ FrameTimeData frameTime = {.presetTime = lastPresentTime,
+ .queueTime = mLastUpdatedTime,
+ .pendingConfigChange = pendingConfigChange};
mFrameTimes.push_back(frameTime);
if (mFrameTimes.size() > HISTORY_SIZE) {
@@ -47,23 +50,28 @@
}
}
-bool LayerInfoV2::isFrequent(nsecs_t now) const {
- for (auto it = mFrameTimes.crbegin(); it != mFrameTimes.crend(); ++it) {
- if (now - it->queueTime >= MAX_FREQUENT_LAYER_PERIOD_NS.count()) {
- ALOGV("%s infrequent (last frame is %.2fms ago", mName.c_str(),
- (now - mFrameTimes.back().queueTime) / 1e6f);
- return false;
+bool LayerInfoV2::isFrequent(nsecs_t now) {
+ mLastReportedIsFrequent = [&] {
+ for (auto it = mFrameTimes.crbegin(); it != mFrameTimes.crend(); ++it) {
+ if (now - it->queueTime >= MAX_FREQUENT_LAYER_PERIOD_NS.count()) {
+ ALOGV("%s infrequent (last frame is %.2fms ago)", mName.c_str(),
+ (now - mFrameTimes.back().queueTime) / 1e6f);
+ return false;
+ }
+
+ const auto numFrames = std::distance(mFrameTimes.crbegin(), it + 1);
+ if (numFrames >= FREQUENT_LAYER_WINDOW_SIZE) {
+ ALOGV("%s frequent (burst of %zu frames)", mName.c_str(), numFrames);
+ return true;
+ }
}
- const auto numFrames = std::distance(mFrameTimes.crbegin(), it + 1);
- if (numFrames >= FREQUENT_LAYER_WINDOW_SIZE) {
- ALOGV("%s frequent (burst of %zu frames", mName.c_str(), numFrames);
- return true;
- }
- }
+ ALOGV("%s %sfrequent (not enough frames %zu)", mName.c_str(),
+ mLastReportedIsFrequent ? "" : "in", mFrameTimes.size());
+ return mLastReportedIsFrequent;
+ }();
- ALOGV("%s infrequent (not enough frames %zu)", mName.c_str(), mFrameTimes.size());
- return false;
+ return mLastReportedIsFrequent;
}
bool LayerInfoV2::hasEnoughDataForHeuristic() const {
@@ -80,21 +88,20 @@
return true;
}
-std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible() {
- static constexpr float MARGIN = 1.0f; // 1Hz
-
- if (!hasEnoughDataForHeuristic()) {
- ALOGV("Not enough data");
- return std::nullopt;
- }
-
- // Calculate the refresh rate by finding the average delta between frames
+std::pair<nsecs_t, bool> LayerInfoV2::calculateAverageFrameTime() const {
nsecs_t totalPresentTimeDeltas = 0;
nsecs_t totalQueueTimeDeltas = 0;
- auto missingPresentTime = false;
+ bool missingPresentTime = false;
+ int numFrames = 0;
for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
+ // Ignore frames captured during a config change
+ if (it->pendingConfigChange || (it + 1)->pendingConfigChange) {
+ continue;
+ }
+
totalQueueTimeDeltas +=
std::max(((it + 1)->queueTime - it->queueTime), mHighRefreshRatePeriod);
+ numFrames++;
if (it->presetTime == 0 || (it + 1)->presetTime == 0) {
missingPresentTime = true;
@@ -105,11 +112,6 @@
std::max(((it + 1)->presetTime - it->presetTime), mHighRefreshRatePeriod);
}
- // If there are no presentation timestamps provided we can't calculate the refresh rate
- if (missingPresentTime && mLastReportedRefreshRate == 0) {
- return std::nullopt;
- }
-
// Calculate the average frame time based on presentation timestamps. If those
// doesn't exist, we look at the time the buffer was queued only. We can do that only if
// we calculated a refresh rate based on presentation timestamps in the past. The reason
@@ -117,13 +119,18 @@
// when implementing render ahead for specific refresh rates. When hwui no longer provides
// presentation timestamps we look at the queue time to see if the current refresh rate still
// matches the content.
- const float averageFrameTime =
+ const auto averageFrameTime =
static_cast<float>(missingPresentTime ? totalQueueTimeDeltas : totalPresentTimeDeltas) /
- (mFrameTimes.size() - 1);
+ numFrames;
+ return {static_cast<nsecs_t>(averageFrameTime), missingPresentTime};
+}
- // Now once we calculated the refresh rate we need to make sure that all the frames we captured
- // are evenly distributed and we don't calculate the average across some burst of frames.
+bool LayerInfoV2::isRefreshRateStable(nsecs_t averageFrameTime, bool missingPresentTime) const {
for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
+ // Ignore frames captured during a config change
+ if (it->pendingConfigChange || (it + 1)->pendingConfigChange) {
+ continue;
+ }
const auto presentTimeDeltas = [&] {
const auto delta = missingPresentTime ? (it + 1)->queueTime - it->queueTime
: (it + 1)->presetTime - it->presetTime;
@@ -131,10 +138,32 @@
}();
if (std::abs(presentTimeDeltas - averageFrameTime) > 2 * averageFrameTime) {
- return std::nullopt;
+ return false;
}
}
+ return true;
+}
+
+std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible() {
+ static constexpr float MARGIN = 1.0f; // 1Hz
+
+ if (!hasEnoughDataForHeuristic()) {
+ ALOGV("Not enough data");
+ return std::nullopt;
+ }
+
+ const auto [averageFrameTime, missingPresentTime] = calculateAverageFrameTime();
+
+ // If there are no presentation timestamps provided we can't calculate the refresh rate
+ if (missingPresentTime && mLastReportedRefreshRate == 0) {
+ return std::nullopt;
+ }
+
+ if (!isRefreshRateStable(averageFrameTime, missingPresentTime)) {
+ return std::nullopt;
+ }
+
const auto refreshRate = 1e9f / averageFrameTime;
if (std::abs(refreshRate - mLastReportedRefreshRate) > MARGIN) {
mLastReportedRefreshRate = refreshRate;
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.h b/services/surfaceflinger/Scheduler/LayerInfoV2.h
index 97c7017..82da7e3 100644
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.h
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.h
@@ -47,9 +47,7 @@
// is within a threshold. If a layer is infrequent, its average refresh rate is disregarded in
// favor of a low refresh rate.
static constexpr size_t FREQUENT_LAYER_WINDOW_SIZE = 3;
- static constexpr float MIN_FPS_FOR_FREQUENT_LAYER = 10.0f;
- static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS =
- std::chrono::nanoseconds(static_cast<nsecs_t>(1e9f / MIN_FPS_FOR_FREQUENT_LAYER)) + 1ms;
+ static constexpr std::chrono::nanoseconds MAX_FREQUENT_LAYER_PERIOD_NS = 150ms;
friend class LayerHistoryTestV2;
@@ -63,7 +61,7 @@
// Records the last requested present time. It also stores information about when
// the layer was last updated. If the present time is farther in the future than the
// updated time, the updated time is the present time.
- void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now);
+ void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, bool pendingConfigChange);
// Sets an explicit layer vote. This usually comes directly from the application via
// ANativeWindow_setFrameRate API
@@ -93,11 +91,14 @@
struct FrameTimeData {
nsecs_t presetTime; // desiredPresentTime, if provided
nsecs_t queueTime; // buffer queue time
+ bool pendingConfigChange;
};
- bool isFrequent(nsecs_t now) const;
+ bool isFrequent(nsecs_t now);
bool hasEnoughDataForHeuristic() const;
std::optional<float> calculateRefreshRateIfPossible();
+ std::pair<nsecs_t, bool> calculateAverageFrameTime() const;
+ bool isRefreshRateStable(nsecs_t averageFrameTime, bool missingPresentTime) const;
const std::string mName;
@@ -109,6 +110,13 @@
float mLastReportedRefreshRate = 0.0f;
+ // Used to determine whether a layer should be considered frequent or
+ // not when we don't have enough frames. This member will not be cleared
+ // as part of clearHistory() to remember whether this layer was frequent
+ // or not before we processed touch boost (or anything else that would
+ // clear layer history).
+ bool mLastReportedIsFrequent = true;
+
// Holds information about the layer vote
struct {
LayerHistory::LayerVoteType type;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 4eef81d..7c2af23 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -423,6 +423,12 @@
}
}
+void Scheduler::setConfigChangePending(bool pending) {
+ if (mLayerHistory) {
+ mLayerHistory->setConfigChangePending(pending);
+ }
+}
+
void Scheduler::chooseRefreshRateForContent() {
if (!mLayerHistory) return;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 6eabfd2..066e9ca 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -119,6 +119,7 @@
// Layers are registered on creation, and unregistered when the weak reference expires.
void registerLayer(Layer*);
void recordLayerHistory(Layer*, nsecs_t presentTime);
+ void setConfigChangePending(bool pending);
// Detects content using layer history, and selects a matching refresh rate.
void chooseRefreshRateForContent();
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 18f789e..2d25319 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1010,6 +1010,7 @@
mPhaseConfiguration->setRefreshRateFps(refreshRate.getFps());
mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
+ mScheduler->setConfigChangePending(true);
}
if (mRefreshRateOverlay) {
@@ -1090,6 +1091,7 @@
mScheduler->resyncToHardwareVsync(true, refreshRate.getVsyncPeriod());
mPhaseConfiguration->setRefreshRateFps(refreshRate.getFps());
mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
+ mScheduler->setConfigChangePending(false);
}
void SurfaceFlinger::performSetActiveConfig() {
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
index d55648a..09ef06a 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
@@ -103,17 +103,8 @@
EXPECT_TRUE(history().summarize(time).empty());
EXPECT_EQ(0, activeLayerCount());
- // The first few updates are considered infrequent
- for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
- history().record(layer.get(), 0, time);
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- }
-
- // Max returned if active layers have insufficient history.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
+ // The first few updates are considered frequent
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
history().record(layer.get(), 0, time);
ASSERT_EQ(1, history().summarize(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
@@ -144,8 +135,8 @@
history().record(layer.get(), 0, time);
auto summary = history().summarize(time);
ASSERT_EQ(1, history().summarize(time).size());
- // Layer is still considered inactive so we expect to get Min
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+ // Layer is still considered active so we expect to get Max
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
@@ -475,13 +466,13 @@
nsecs_t time = systemTime();
- // The first few updates are considered infrequent
- for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
+ // The first few updates are considered frequent
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
history().record(layer.get(), 0, time);
ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(1, frequentLayerCount(time));
}
// advance the time for the previous frame to be inactive
@@ -508,6 +499,36 @@
EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
+
+ // advance the time for the previous frame to be inactive
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+
+ // Now event if we post a quick few frame we should stay infrequent
+ for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
+ history().record(layer.get(), time, time);
+ time += HI_FPS_PERIOD;
+
+ EXPECT_EQ(1, layerCount());
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ }
+
+ // clear the history
+ history().clear();
+
+ // Now event if we post a quick few frame we should stay infrequent
+ for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
+ history().record(layer.get(), time, time);
+ time += HI_FPS_PERIOD;
+
+ EXPECT_EQ(1, layerCount());
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ }
}
TEST_F(LayerHistoryTestV2, invisibleExplicitLayer) {
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index 37b5368..d9a9427 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -621,6 +621,7 @@
// global functions
if (instance == VK_NULL_HANDLE) {
if (strcmp(pName, "vkCreateInstance") == 0) return reinterpret_cast<PFN_vkVoidFunction>(CreateInstance);
+ if (strcmp(pName, "vkGetInstanceProcAddr") == 0) return reinterpret_cast<PFN_vkVoidFunction>(GetInstanceProcAddr);
if (strcmp(pName, "vkEnumerateInstanceVersion") == 0) return reinterpret_cast<PFN_vkVoidFunction>(EnumerateInstanceVersion);
if (strcmp(pName, "vkEnumerateInstanceLayerProperties") == 0) return reinterpret_cast<PFN_vkVoidFunction>(EnumerateInstanceLayerProperties);
if (strcmp(pName, "vkEnumerateInstanceExtensionProperties") == 0) return reinterpret_cast<PFN_vkVoidFunction>(EnumerateInstanceExtensionProperties);
diff --git a/vulkan/scripts/api_generator.py b/vulkan/scripts/api_generator.py
index 7c39075..be24172 100644
--- a/vulkan/scripts/api_generator.py
+++ b/vulkan/scripts/api_generator.py
@@ -152,7 +152,9 @@
if (instance == VK_NULL_HANDLE) {\n""")
for cmd in gencom.command_list:
- if gencom.is_globally_dispatched(cmd):
+ # vkGetInstanceProcAddr(nullptr, "vkGetInstanceProcAddr") is effectively
+ # globally dispatched
+ if gencom.is_globally_dispatched(cmd) or cmd == 'vkGetInstanceProcAddr':
f.write(gencom.indent(2) +
'if (strcmp(pName, \"' + cmd +
'\") == 0) return reinterpret_cast<PFN_vkVoidFunction>(' +