visual_picker_glx.cc 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. // Copyright 2016 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ui/base/x/visual_picker_glx.h"
  5. #include <algorithm>
  6. #include <bitset>
  7. #include <cstring>
  8. #include <map>
  9. #include <numeric>
  10. #include <vector>
  11. #include "base/memory/singleton.h"
  12. #include "ui/gfx/x/future.h"
  13. // These constants are obtained from GL/glx.h and GL/glxext.h.
  14. constexpr uint32_t GLX_LEVEL = 3;
  15. constexpr uint32_t GLX_DOUBLEBUFFER = 5;
  16. constexpr uint32_t GLX_STEREO = 6;
  17. constexpr uint32_t GLX_BUFFER_SIZE = 2;
  18. constexpr uint32_t GLX_AUX_BUFFERS = 7;
  19. constexpr uint32_t GLX_RED_SIZE = 8;
  20. constexpr uint32_t GLX_GREEN_SIZE = 9;
  21. constexpr uint32_t GLX_BLUE_SIZE = 10;
  22. constexpr uint32_t GLX_ALPHA_SIZE = 11;
  23. constexpr uint32_t GLX_DEPTH_SIZE = 12;
  24. constexpr uint32_t GLX_STENCIL_SIZE = 13;
  25. constexpr uint32_t GLX_ACCUM_RED_SIZE = 14;
  26. constexpr uint32_t GLX_ACCUM_GREEN_SIZE = 15;
  27. constexpr uint32_t GLX_ACCUM_BLUE_SIZE = 16;
  28. constexpr uint32_t GLX_ACCUM_ALPHA_SIZE = 17;
  29. constexpr uint32_t GLX_CONFIG_CAVEAT = 0x20;
  30. constexpr uint32_t GLX_VISUAL_CAVEAT_EXT = 0x20;
  31. constexpr uint32_t GLX_X_VISUAL_TYPE = 0x22;
  32. constexpr uint32_t GLX_BIND_TO_TEXTURE_TARGETS_EXT = 0x20D3;
  33. constexpr uint32_t GLX_NONE = 0x8000;
  34. constexpr uint32_t GLX_NONE_EXT = 0x8000;
  35. constexpr uint32_t GLX_TRUE_COLOR = 0x8002;
  36. constexpr uint32_t GLX_VISUAL_ID = 0x800B;
  37. constexpr uint32_t GLX_DRAWABLE_TYPE = 0x8010;
  38. constexpr uint32_t GLX_RENDER_TYPE = 0x8011;
  39. constexpr uint32_t GLX_FBCONFIG_ID = 0x8013;
  40. constexpr uint32_t GLX_PIXMAP_BIT = 0x00000002;
  41. constexpr uint32_t GLX_TEXTURE_2D_BIT_EXT = 0x00000002;
  42. constexpr uint32_t GLX_SAMPLE_BUFFERS_ARB = 100000;
  43. constexpr uint32_t GLX_SAMPLES = 100001;
  44. constexpr uint32_t GL_FALSE = 0;
  45. namespace ui {
  46. namespace {
  47. bool IsArgbVisual(const x11::Connection::VisualInfo& visual) {
  48. auto bits = [](auto x) {
  49. return std::bitset<8 * sizeof(decltype(x))>(x).count();
  50. };
  51. auto bits_rgb = bits(visual.visual_type->red_mask) +
  52. bits(visual.visual_type->green_mask) +
  53. bits(visual.visual_type->blue_mask);
  54. return static_cast<std::size_t>(visual.format->depth) > bits_rgb;
  55. }
  56. // Used to filter visuals by the best class.
  57. int VisualScore(x11::VisualClass c_class) {
  58. // A higher score is more preferable.
  59. switch (c_class) {
  60. case x11::VisualClass::TrueColor:
  61. return 1;
  62. case x11::VisualClass::DirectColor:
  63. return 0;
  64. default:
  65. return -1;
  66. }
  67. }
  68. } // anonymous namespace
  69. // static
  70. VisualPickerGlx* VisualPickerGlx::GetInstance() {
  71. return base::Singleton<VisualPickerGlx>::get();
  72. }
  73. x11::Glx::FbConfig VisualPickerGlx::GetFbConfigForFormat(
  74. gfx::BufferFormat format) {
  75. if (!config_map_)
  76. FillConfigMap();
  77. auto it = config_map_->find(format);
  78. return it == config_map_->end() ? x11::Glx::FbConfig{} : it->second;
  79. }
  80. x11::VisualId VisualPickerGlx::PickBestGlVisual(
  81. const x11::Glx::GetVisualConfigsReply& configs,
  82. base::RepeatingCallback<bool(const x11::Connection::VisualInfo&)> pred,
  83. bool want_alpha) const {
  84. int highest_score = -1;
  85. x11::VisualId best_visual{};
  86. for (size_t cfg = 0; cfg < configs.num_visuals; cfg++) {
  87. size_t i = cfg * configs.num_properties;
  88. std::map<uint32_t, uint32_t> props;
  89. static constexpr uint32_t static_props[] = {
  90. GLX_VISUAL_ID, GLX_X_VISUAL_TYPE, GLX_RENDER_TYPE,
  91. GLX_RED_SIZE, GLX_GREEN_SIZE, GLX_BLUE_SIZE,
  92. GLX_ALPHA_SIZE, GLX_ACCUM_RED_SIZE, GLX_ACCUM_GREEN_SIZE,
  93. GLX_ACCUM_BLUE_SIZE, GLX_ACCUM_ALPHA_SIZE, GLX_DOUBLEBUFFER,
  94. GLX_STEREO, GLX_BUFFER_SIZE, GLX_DEPTH_SIZE,
  95. GLX_STENCIL_SIZE, GLX_AUX_BUFFERS, GLX_LEVEL,
  96. };
  97. for (const uint32_t prop : static_props)
  98. props[prop] = configs.property_list[i++];
  99. const size_t extra_props =
  100. (configs.num_properties - std::size(static_props)) / 2;
  101. for (size_t j = 0; j < extra_props; j++) {
  102. const auto key = configs.property_list[i++];
  103. const auto value = configs.property_list[i++];
  104. // Mesa adds a (0, 0) key-value pair at the end of each property list.
  105. if (!key)
  106. continue;
  107. props[key] = value;
  108. }
  109. auto get = [&](uint32_t key) {
  110. auto it = props.find(key);
  111. return it == props.end() ? 0 : it->second;
  112. };
  113. const auto visual_id = static_cast<x11::VisualId>(get(GLX_VISUAL_ID));
  114. const auto* info = connection_->GetVisualInfoFromId(visual_id);
  115. if (!pred.Run(*info))
  116. continue;
  117. if (!get(GLX_DOUBLEBUFFER) || get(GLX_STEREO))
  118. continue;
  119. auto caveat = get(GLX_VISUAL_CAVEAT_EXT);
  120. if (caveat && caveat != GLX_NONE_EXT)
  121. continue;
  122. // Give precedence to the root visual if it satisfies the basic
  123. // requirements above. This can avoid an expensive copy-on-present.
  124. if (visual_id == connection_->default_root_visual().visual_id)
  125. return visual_id;
  126. int score = 0;
  127. if (get(GLX_SAMPLE_BUFFERS_ARB) == 0) {
  128. score++;
  129. if (get(GLX_DEPTH_SIZE) == 0 && get(GLX_STENCIL_SIZE) == 0) {
  130. score++;
  131. const bool has_alpha = get(GLX_ALPHA_SIZE) > 0;
  132. if (has_alpha == want_alpha)
  133. score++;
  134. }
  135. }
  136. if (score > highest_score) {
  137. highest_score = score;
  138. best_visual = visual_id;
  139. }
  140. }
  141. return best_visual;
  142. }
  143. x11::VisualId VisualPickerGlx::PickBestSystemVisual(
  144. const x11::Glx::GetVisualConfigsReply& configs) const {
  145. x11::Connection::VisualInfo default_visual_info =
  146. *connection_->GetVisualInfoFromId(
  147. connection_->default_root_visual().visual_id);
  148. auto is_compatible_with_root_visual =
  149. [](const x11::Connection::VisualInfo& default_visual_info,
  150. const x11::Connection::VisualInfo& visual_info) {
  151. const auto& dvt = *default_visual_info.visual_type;
  152. const auto& vt = *visual_info.visual_type;
  153. return vt.c_class == dvt.c_class &&
  154. visual_info.format->depth == default_visual_info.format->depth &&
  155. vt.red_mask == dvt.red_mask && vt.green_mask == dvt.green_mask &&
  156. vt.blue_mask == dvt.blue_mask &&
  157. vt.colormap_entries == dvt.colormap_entries &&
  158. vt.bits_per_rgb_value == dvt.bits_per_rgb_value;
  159. };
  160. return PickBestGlVisual(
  161. configs,
  162. base::BindRepeating(is_compatible_with_root_visual, default_visual_info),
  163. IsArgbVisual(default_visual_info));
  164. }
  165. x11::VisualId VisualPickerGlx::PickBestRgbaVisual(
  166. const x11::Glx::GetVisualConfigsReply& configs) const {
  167. int best_class_score = -1;
  168. for (const auto& depth : connection_->default_screen().allowed_depths) {
  169. for (const auto& vis : depth.visuals)
  170. best_class_score = std::max(best_class_score, VisualScore(vis.c_class));
  171. }
  172. auto pred = [](int best_class_score,
  173. const x11::Connection::VisualInfo& visual_info) {
  174. if (!IsArgbVisual(visual_info))
  175. return false;
  176. return VisualScore(visual_info.visual_type->c_class) == best_class_score;
  177. };
  178. return PickBestGlVisual(configs, base::BindRepeating(pred, best_class_score),
  179. true);
  180. }
  181. void VisualPickerGlx::FillConfigMap() {
  182. DCHECK(!config_map_);
  183. config_map_ =
  184. std::make_unique<base::flat_map<gfx::BufferFormat, x11::Glx::FbConfig>>();
  185. if (auto configs = connection_->glx()
  186. .GetFBConfigs({static_cast<uint32_t>(
  187. connection_->DefaultScreenId())})
  188. .Sync()) {
  189. const auto n_cfgs = configs->num_FB_configs;
  190. const auto n_props = configs->num_properties;
  191. // Iterate from back to front since "preferred" FB configs appear earlier.
  192. for (size_t cfg = n_cfgs; cfg-- > 0;) {
  193. std::map<uint32_t, uint32_t> props;
  194. for (size_t prop = 0; prop < n_props; prop++) {
  195. size_t i = 2 * cfg * n_props + 2 * prop;
  196. const auto key = configs->property_list[i];
  197. const auto value = configs->property_list[i + 1];
  198. props[key] = value;
  199. }
  200. auto get = [&](uint32_t key) {
  201. auto it = props.find(key);
  202. return it == props.end() ? 0 : it->second;
  203. };
  204. // Each config must have an ID.
  205. auto id = get(GLX_FBCONFIG_ID);
  206. DCHECK(id);
  207. auto fbconfig = static_cast<x11::Glx::FbConfig>(id);
  208. // Ensure the config is compatible with pixmap drawing.
  209. if (!(get(GLX_DRAWABLE_TYPE) & GLX_PIXMAP_BIT))
  210. continue;
  211. // Ensure we can bind to GL_TEXTURE_2D.
  212. if (!(get(GLX_BIND_TO_TEXTURE_TARGETS_EXT) & GLX_TEXTURE_2D_BIT_EXT))
  213. continue;
  214. // No double-buffering.
  215. if (get(GLX_DOUBLEBUFFER) != GL_FALSE)
  216. continue;
  217. // Prefer true-color over direct-color.
  218. if (get(GLX_X_VISUAL_TYPE) != GLX_TRUE_COLOR)
  219. continue;
  220. // No caveats.
  221. auto caveat = get(GLX_CONFIG_CAVEAT);
  222. if (caveat && caveat != GLX_NONE)
  223. continue;
  224. // No antialiasing needed.
  225. if (get(GLX_SAMPLES))
  226. continue;
  227. // No depth buffer needed.
  228. if (get(GLX_DEPTH_SIZE))
  229. continue;
  230. auto r = get(GLX_RED_SIZE);
  231. auto g = get(GLX_GREEN_SIZE);
  232. auto b = get(GLX_BLUE_SIZE);
  233. auto a = get(GLX_ALPHA_SIZE);
  234. if (r == 5 && g == 6 && b == 5 && a == 0)
  235. (*config_map_)[gfx::BufferFormat::BGR_565] = fbconfig;
  236. else if (r == 8 && g == 8 && b == 8 && a == 0)
  237. (*config_map_)[gfx::BufferFormat::BGRX_8888] = fbconfig;
  238. else if (r == 10 && g == 10 && b == 10 && a == 0)
  239. (*config_map_)[gfx::BufferFormat::BGRA_1010102] = fbconfig;
  240. else if (r == 8 && g == 8 && b == 8 && a == 8)
  241. (*config_map_)[gfx::BufferFormat::BGRA_8888] = fbconfig;
  242. }
  243. }
  244. }
  245. VisualPickerGlx::VisualPickerGlx() : connection_(x11::Connection::Get()) {
  246. auto configs =
  247. connection_->glx()
  248. .GetVisualConfigs(
  249. {static_cast<uint32_t>(connection_->DefaultScreenId())})
  250. .Sync();
  251. if (configs) {
  252. system_visual_ = PickBestSystemVisual(*configs.reply);
  253. rgba_visual_ = PickBestRgbaVisual(*configs.reply);
  254. }
  255. }
  256. VisualPickerGlx::~VisualPickerGlx() = default;
  257. } // namespace ui