x11_software_bitmap_presenter.cc 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. // Copyright 2019 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/x11_software_bitmap_presenter.h"
  5. #include <stddef.h>
  6. #include <stdint.h>
  7. #include <string.h>
  8. #include <cstring>
  9. #include <memory>
  10. #include <utility>
  11. #include "base/bind.h"
  12. #include "base/logging.h"
  13. #include "base/memory/raw_ptr.h"
  14. #include "base/memory/ref_counted_memory.h"
  15. #include "skia/ext/legacy_display_globals.h"
  16. #include "third_party/skia/include/core/SkCanvas.h"
  17. #include "third_party/skia/include/core/SkImageInfo.h"
  18. #include "third_party/skia/include/core/SkSurface.h"
  19. #include "ui/base/x/x11_shm_image_pool.h"
  20. #include "ui/base/x/x11_util.h"
  21. #include "ui/gfx/native_widget_types.h"
  22. #include "ui/gfx/x/connection.h"
  23. #include "ui/gfx/x/x11_atom_cache.h"
  24. #include "ui/gfx/x/xproto.h"
  25. #include "ui/gfx/x/xproto_types.h"
  26. #include "ui/gfx/x/xproto_util.h"
  27. namespace ui {
  28. namespace {
  29. constexpr int kMaxFramesPending = 2;
  30. class ScopedPixmap {
  31. public:
  32. ScopedPixmap(x11::Connection* connection, x11::Pixmap pixmap)
  33. : connection_(connection), pixmap_(pixmap) {}
  34. ScopedPixmap(const ScopedPixmap&) = delete;
  35. ScopedPixmap& operator=(const ScopedPixmap&) = delete;
  36. ~ScopedPixmap() {
  37. if (pixmap_ != x11::Pixmap::None)
  38. connection_->FreePixmap({pixmap_});
  39. }
  40. private:
  41. const raw_ptr<x11::Connection> connection_;
  42. x11::Pixmap pixmap_;
  43. };
  44. } // namespace
  45. // static
  46. bool X11SoftwareBitmapPresenter::CompositeBitmap(x11::Connection* connection,
  47. x11::Drawable widget,
  48. int x,
  49. int y,
  50. int width,
  51. int height,
  52. int depth,
  53. x11::GraphicsContext gc,
  54. const void* data) {
  55. int16_t x_i16 = x;
  56. int16_t y_i16 = y;
  57. uint16_t w_u16 = width;
  58. uint16_t h_u16 = height;
  59. uint8_t d_u8 = depth;
  60. connection->ClearArea({false, widget, x_i16, y_i16, w_u16, h_u16});
  61. constexpr auto kAllPlanes =
  62. std::numeric_limits<decltype(x11::GetImageRequest::plane_mask)>::max();
  63. scoped_refptr<base::RefCountedMemory> bg;
  64. auto req = connection->GetImage({x11::ImageFormat::ZPixmap, widget, x_i16,
  65. y_i16, w_u16, h_u16, kAllPlanes});
  66. if (auto reply = req.Sync()) {
  67. bg = reply->data;
  68. } else {
  69. auto pixmap_id = connection->GenerateId<x11::Pixmap>();
  70. connection->CreatePixmap({d_u8, pixmap_id, widget, w_u16, h_u16});
  71. ScopedPixmap pixmap(connection, pixmap_id);
  72. connection->ChangeGC(x11::ChangeGCRequest{
  73. .gc = gc, .subwindow_mode = x11::SubwindowMode::IncludeInferiors});
  74. connection->CopyArea(
  75. {widget, pixmap_id, gc, x_i16, y_i16, 0, 0, w_u16, h_u16});
  76. connection->ChangeGC(x11::ChangeGCRequest{
  77. .gc = gc, .subwindow_mode = x11::SubwindowMode::ClipByChildren});
  78. auto pix_req = connection->GetImage(
  79. {x11::ImageFormat::ZPixmap, pixmap_id, 0, 0, w_u16, h_u16, kAllPlanes});
  80. auto pix_reply = pix_req.Sync();
  81. if (!pix_reply)
  82. return false;
  83. bg = pix_reply->data;
  84. }
  85. SkBitmap bg_bitmap;
  86. SkImageInfo image_info = SkImageInfo::Make(
  87. width, height, kBGRA_8888_SkColorType, kPremul_SkAlphaType);
  88. if (!bg_bitmap.installPixels(image_info, const_cast<uint8_t*>(bg->data()),
  89. image_info.minRowBytes())) {
  90. return false;
  91. }
  92. SkCanvas canvas(bg_bitmap);
  93. SkBitmap fg_bitmap;
  94. image_info = SkImageInfo::Make(width, height, kBGRA_8888_SkColorType,
  95. kPremul_SkAlphaType);
  96. if (!fg_bitmap.installPixels(image_info, const_cast<void*>(data), 4 * width))
  97. return false;
  98. canvas.drawImage(fg_bitmap.asImage(), 0, 0);
  99. canvas.flush();
  100. connection->PutImage({x11::ImageFormat::ZPixmap, widget, gc, w_u16, h_u16,
  101. x_i16, y_i16, 0, d_u8, bg});
  102. return true;
  103. }
  104. X11SoftwareBitmapPresenter::X11SoftwareBitmapPresenter(
  105. x11::Connection* connection,
  106. gfx::AcceleratedWidget widget,
  107. bool enable_multibuffering)
  108. : widget_(static_cast<x11::Window>(widget)),
  109. connection_(connection),
  110. enable_multibuffering_(enable_multibuffering) {
  111. DCHECK_NE(widget_, x11::Window::None);
  112. gc_ = connection_->GenerateId<x11::GraphicsContext>();
  113. connection_->CreateGC({gc_, widget_});
  114. if (auto response = connection_->GetWindowAttributes({widget_}).Sync()) {
  115. visual_ = response->visual;
  116. depth_ = connection_->GetVisualInfoFromId(visual_)->format->depth;
  117. } else {
  118. LOG(ERROR) << "XGetWindowAttributes failed for window "
  119. << static_cast<uint32_t>(widget_);
  120. return;
  121. }
  122. shm_pool_ = std::make_unique<ui::XShmImagePool>(connection_, widget_, visual_,
  123. depth_, MaxFramesPending(),
  124. enable_multibuffering_);
  125. // TODO(thomasanderson): Avoid going through the X11 server to plumb this
  126. // property in.
  127. GetProperty(widget_, x11::GetAtom("CHROMIUM_COMPOSITE_WINDOW"), &composite_);
  128. }
  129. X11SoftwareBitmapPresenter::~X11SoftwareBitmapPresenter() {
  130. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  131. if (gc_ != x11::GraphicsContext{})
  132. connection_->FreeGC({gc_});
  133. }
  134. bool X11SoftwareBitmapPresenter::ShmPoolReady() const {
  135. return shm_pool_ && shm_pool_->Ready();
  136. }
  137. void X11SoftwareBitmapPresenter::Resize(const gfx::Size& pixel_size) {
  138. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  139. if (pixel_size == viewport_pixel_size_)
  140. return;
  141. viewport_pixel_size_ = pixel_size;
  142. // Fallback to the non-shm codepath when |composite_| is true, which only
  143. // happens for status icon windows that are typically 16x16px. It's possible
  144. // to add a shm codepath, but it wouldn't be buying much since it would only
  145. // affect windows that are tiny and infrequently updated.
  146. if (!composite_ && shm_pool_ && shm_pool_->Resize(pixel_size)) {
  147. needs_swap_ = false;
  148. surface_ = nullptr;
  149. } else {
  150. SkColorType color_type = ColorTypeForVisual(visual_);
  151. if (color_type == kUnknown_SkColorType)
  152. return;
  153. SkImageInfo info = SkImageInfo::Make(viewport_pixel_size_.width(),
  154. viewport_pixel_size_.height(),
  155. color_type, kOpaque_SkAlphaType);
  156. SkSurfaceProps props = skia::LegacyDisplayGlobals::GetSkSurfaceProps();
  157. surface_ = SkSurface::MakeRaster(info, &props);
  158. }
  159. }
  160. SkCanvas* X11SoftwareBitmapPresenter::GetSkCanvas() {
  161. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  162. if (ShmPoolReady())
  163. return shm_pool_->CurrentCanvas();
  164. else if (surface_)
  165. return surface_->getCanvas();
  166. return nullptr;
  167. }
  168. void X11SoftwareBitmapPresenter::EndPaint(const gfx::Rect& damage_rect) {
  169. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  170. gfx::Rect rect = damage_rect;
  171. rect.Intersect(gfx::Rect(viewport_pixel_size_));
  172. if (rect.IsEmpty())
  173. return;
  174. SkPixmap skia_pixmap;
  175. if (ShmPoolReady()) {
  176. // TODO(thomasanderson): Investigate direct rendering with DRI3 to avoid any
  177. // unnecessary X11 IPC or buffer copying.
  178. x11::Shm::PutImageRequest put_image_request{
  179. .drawable = widget_,
  180. .gc = gc_,
  181. .total_width =
  182. static_cast<uint16_t>(shm_pool_->CurrentBitmap().width()),
  183. .total_height =
  184. static_cast<uint16_t>(shm_pool_->CurrentBitmap().height()),
  185. .src_x = static_cast<uint16_t>(rect.x()),
  186. .src_y = static_cast<uint16_t>(rect.y()),
  187. .src_width = static_cast<uint16_t>(rect.width()),
  188. .src_height = static_cast<uint16_t>(rect.height()),
  189. .dst_x = static_cast<int16_t>(rect.x()),
  190. .dst_y = static_cast<int16_t>(rect.y()),
  191. .depth = static_cast<uint8_t>(depth_),
  192. .format = x11::ImageFormat::ZPixmap,
  193. .send_event = enable_multibuffering_,
  194. .shmseg = shm_pool_->CurrentSegment(),
  195. .offset = 0,
  196. };
  197. connection_->shm().PutImage(put_image_request);
  198. needs_swap_ = true;
  199. // Flush now to ensure the X server gets the request as early as
  200. // possible to reduce frame-to-frame latency.
  201. connection_->Flush();
  202. return;
  203. }
  204. if (surface_)
  205. surface_->peekPixels(&skia_pixmap);
  206. if (!skia_pixmap.addr())
  207. return;
  208. if (composite_ &&
  209. CompositeBitmap(connection_, widget_, rect.x(), rect.y(), rect.width(),
  210. rect.height(), depth_, gc_, skia_pixmap.addr())) {
  211. // Flush now to ensure the X server gets the request as early as
  212. // possible to reduce frame-to-frame latency.
  213. connection_->Flush();
  214. return;
  215. }
  216. auto* connection = x11::Connection::Get();
  217. DrawPixmap(connection, visual_, widget_, gc_, skia_pixmap, rect.x(), rect.y(),
  218. rect.x(), rect.y(), rect.width(), rect.height());
  219. // Flush now to ensure the X server gets the request as early as
  220. // possible to reduce frame-to-frame latency.
  221. connection_->Flush();
  222. }
  223. void X11SoftwareBitmapPresenter::OnSwapBuffers(
  224. SwapBuffersCallback swap_ack_callback) {
  225. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  226. if (enable_multibuffering_ && ShmPoolReady() && needs_swap_)
  227. shm_pool_->SwapBuffers(std::move(swap_ack_callback));
  228. else
  229. std::move(swap_ack_callback).Run(viewport_pixel_size_);
  230. needs_swap_ = false;
  231. }
  232. int X11SoftwareBitmapPresenter::MaxFramesPending() const {
  233. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  234. return enable_multibuffering_ ? kMaxFramesPending : 1;
  235. }
  236. } // namespace ui