blob: f36352102ff156e99a1aea9cbd61da35c9e12674 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_GL_GL_DISPLAY_MANAGER_H_
#define UI_GL_GL_DISPLAY_MANAGER_H_
#include <map>
#include <memory>
#include <vector>
#include "base/check.h"
#include "base/export_template.h"
#include "base/no_destructor.h"
#include "base/synchronization/lock.h"
#include "base/thread_annotations.h"
#include "ui/gl/gl_display.h"
#include "ui/gl/gl_export.h"
#include "ui/gl/gl_switches.h"
#include "ui/gl/gpu_preference.h"
namespace gl {
template <typename GLDisplayPlatform>
class GLDisplayManager {
public:
// Getter for the singleton. This will return nullptr on failure.
// This should only be called inside the ui/gl module. In component build,
// calling this outside ui/gl module returns a different instance.
static GLDisplayManager<GLDisplayPlatform>* GetInstance() {
static base::NoDestructor<GLDisplayManager<GLDisplayPlatform>> instance;
return instance.get();
}
// This should be called before calling GetDisplay(GpuPreference).
// Otherwise kDefault GPU will be mapped to 0 instead of a valid
// system_device_id.
void SetGpuPreference(GpuPreference preference, uint64_t system_device_id) {
#if DCHECK_IS_ON()
auto iter = gpu_preference_map_.find(preference);
DCHECK(gpu_preference_map_.end() == iter);
#endif
gpu_preference_map_[preference] = system_device_id;
}
// This should be called if display creation is failed on the specified
// system_device_id display. This way, we will no longer attempt to use this
// display, but instead use the default display.
void RemoveGpuPreference(GpuPreference preference) {
uint64_t system_device_id = GetSystemDeviceId(preference);
for (auto iter = gpu_preference_map_.begin();
iter != gpu_preference_map_.end();
/* no increment */) {
if (iter->second == system_device_id && gpu_preference_map_.size() > 1) {
iter = gpu_preference_map_.erase(iter);
} else {
iter++;
}
}
// Ensure that kDefault is always set if there is at least one other gpu
// preference.
if (!gpu_preference_map_.empty()) {
auto iter = gpu_preference_map_.find(GpuPreference::kDefault);
if (iter == gpu_preference_map_.end()) {
gpu_preference_map_[GpuPreference::kDefault] =
gpu_preference_map_.begin()->second;
}
}
base::AutoLock auto_lock(lock_);
for (size_t i = 0; i < displays_.size(); i++) {
if (displays_[i]->system_device_id() == system_device_id) {
displays_.erase(displays_.begin() + i);
i--;
}
}
}
uint64_t GetSystemDeviceId(GpuPreference preference) {
uint64_t system_device_id = 0;
auto iter = gpu_preference_map_.find(preference);
if (!SupportsEGLDualGPURendering() ||
(iter == gpu_preference_map_.end() &&
preference != GpuPreference::kDefault)) {
// If kLowPower or kHighPerformance is queried but they are not set in the
// map, default to the kDefault GPU.
// Also do this if EGLDualGPURendering is not enabled.
iter = gpu_preference_map_.find(GpuPreference::kDefault);
}
if (iter != gpu_preference_map_.end()) {
system_device_id = iter->second;
}
return system_device_id;
}
GLDisplayManager(const GLDisplayManager&) = delete;
GLDisplayManager& operator=(const GLDisplayManager&) = delete;
bool IsEmpty() {
base::AutoLock auto_lock(lock_);
return displays_.empty();
}
void OverrideEGLDualGPURenderingSupportForTests(bool value) {
override_egl_dual_gpu_rendering_support_for_tests_ = value;
}
bool SupportsEGLDualGPURendering() {
return features::SupportsEGLDualGPURendering() ||
override_egl_dual_gpu_rendering_support_for_tests_;
}
GLDisplayPlatform* GetDisplay(GpuPreference preference,
gl::DisplayKey display_key) {
return GetDisplay(GetSystemDeviceId(preference), display_key);
}
GLDisplayPlatform* GetDisplay(GpuPreference preference) {
return GetDisplay(GetSystemDeviceId(preference), gl::DisplayKey::kDefault);
}
private:
friend class base::NoDestructor<GLDisplayManager<GLDisplayPlatform>>;
#if defined(USE_EGL)
friend class GLDisplayManagerEGLTest;
#endif
// Don't delete these functions for testing purpose.
// Each test constructs a scoped GLDisplayManager directly.
GLDisplayManager() = default;
virtual ~GLDisplayManager() = default;
GLDisplayPlatform* GetDisplay(uint64_t system_device_id,
gl::DisplayKey display_key) {
base::AutoLock auto_lock(lock_);
for (const auto& display : displays_) {
if (display->system_device_id() == system_device_id &&
display->display_key() == display_key) {
return display.get();
}
}
std::unique_ptr<GLDisplayPlatform> display(
new GLDisplayPlatform(system_device_id, display_key));
displays_.push_back(std::move(display));
return displays_.back().get();
}
mutable base::Lock lock_;
std::vector<std::unique_ptr<GLDisplayPlatform>> displays_ GUARDED_BY(lock_);
std::map<GpuPreference, uint64_t> gpu_preference_map_;
bool override_egl_dual_gpu_rendering_support_for_tests_ = false;
};
#if defined(USE_EGL)
using GLDisplayManagerEGL = GLDisplayManager<GLDisplayEGL>;
extern template class EXPORT_TEMPLATE_DECLARE(GL_EXPORT)
GLDisplayManager<GLDisplayEGL>;
#endif
#if defined(USE_GLX)
using GLDisplayManagerX11 = GLDisplayManager<GLDisplayX11>;
extern template class EXPORT_TEMPLATE_DECLARE(GL_EXPORT)
GLDisplayManager<GLDisplayX11>;
#endif
} // namespace gl
#endif // UI_GL_GL_DISPLAY_MANAGER_H_