direct_composition_child_surface_win.cc 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782
  1. // Copyright 2017 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/gl/direct_composition_child_surface_win.h"
  5. #include <d3d11_1.h>
  6. #include <dcomptypes.h>
  7. #include "base/debug/alias.h"
  8. #include "base/debug/dump_without_crashing.h"
  9. #include "base/metrics/histogram_functions.h"
  10. #include "base/metrics/histogram_macros.h"
  11. #include "base/process/process.h"
  12. #include "base/synchronization/waitable_event.h"
  13. #include "base/threading/thread_task_runner_handle.h"
  14. #include "base/trace_event/trace_event.h"
  15. #include "base/trace_event/traced_value.h"
  16. #include "base/win/windows_version.h"
  17. #include "ui/gfx/color_space_win.h"
  18. #include "ui/gfx/native_widget_types.h"
  19. #include "ui/gl/direct_composition_support.h"
  20. #include "ui/gl/egl_util.h"
  21. #include "ui/gl/gl_angle_util_win.h"
  22. #include "ui/gl/gl_bindings.h"
  23. #include "ui/gl/gl_context.h"
  24. #include "ui/gl/gl_surface_egl.h"
  25. #include "ui/gl/gl_switches.h"
  26. #include "ui/gl/gl_utils.h"
  27. #include "ui/gl/scoped_make_current.h"
  28. #include "ui/gl/vsync_thread_win.h"
  29. #ifndef EGL_ANGLE_d3d_texture_client_buffer
  30. #define EGL_ANGLE_d3d_texture_client_buffer 1
  31. #define EGL_D3D_TEXTURE_ANGLE 0x33A3
  32. #define EGL_TEXTURE_OFFSET_X_ANGLE 0x3490
  33. #define EGL_TEXTURE_OFFSET_Y_ANGLE 0x3491
  34. #endif /* EGL_ANGLE_d3d_texture_client_buffer */
  35. namespace gl {
  36. namespace {
  37. // Only one DirectComposition surface can be rendered into at a time. Track
  38. // here which IDCompositionSurface is being rendered into. If another context
  39. // is made current, then this surface will be suspended.
  40. IDCompositionSurface* g_current_surface = nullptr;
  41. // If damage_rect / full_chrome_rect >= kForceFullDamageThreshold, present
  42. // the swap chain with full damage.
  43. float kForceFullDamageThreshold = 0.6f;
  44. const char* kDirectCompositionChildSurfaceLabel =
  45. "DirectCompositionChildSurface";
  46. bool SupportsLowLatencyPresentation() {
  47. return base::FeatureList::IsEnabled(
  48. features::kDirectCompositionLowLatencyPresentation);
  49. }
  50. bool IsVerifyDrawOffsetEnabled() {
  51. return base::FeatureList::IsEnabled(
  52. features::kDirectCompositionVerifyDrawOffset);
  53. }
  54. bool IsWaitableSwapChainEnabled() {
  55. // Waitable swap chains were first enabled in Win 8.1/DXGI 1.3
  56. return (base::win::GetVersion() >= base::win::Version::WIN8_1) &&
  57. base::FeatureList::IsEnabled(features::kDXGIWaitableSwapChain);
  58. }
  59. UINT GetMaxWaitableQueuedFrames() {
  60. return static_cast<UINT>(
  61. features::kDXGIWaitableSwapChainMaxQueuedFrames.Get());
  62. }
  63. } // namespace
  64. DirectCompositionChildSurfaceWin::PendingFrame::PendingFrame(
  65. Microsoft::WRL::ComPtr<ID3D11Query> query,
  66. PresentationCallback callback)
  67. : query(std::move(query)), callback(std::move(callback)) {}
  68. DirectCompositionChildSurfaceWin::PendingFrame::PendingFrame(
  69. PendingFrame&& other) = default;
  70. DirectCompositionChildSurfaceWin::PendingFrame::~PendingFrame() = default;
  71. DirectCompositionChildSurfaceWin::PendingFrame&
  72. DirectCompositionChildSurfaceWin::PendingFrame::operator=(
  73. PendingFrame&& other) = default;
  74. DirectCompositionChildSurfaceWin::DirectCompositionChildSurfaceWin(
  75. GLDisplayEGL* display,
  76. VSyncCallback vsync_callback,
  77. bool use_angle_texture_offset,
  78. size_t max_pending_frames,
  79. bool force_full_damage,
  80. bool force_full_damage_always)
  81. : GLSurfaceEGL(display),
  82. vsync_callback_(std::move(vsync_callback)),
  83. use_angle_texture_offset_(use_angle_texture_offset),
  84. max_pending_frames_(max_pending_frames),
  85. force_full_damage_(force_full_damage),
  86. force_full_damage_always_(force_full_damage_always),
  87. vsync_thread_(VSyncThreadWin::GetInstance()),
  88. task_runner_(base::ThreadTaskRunnerHandle::Get()) {}
  89. DirectCompositionChildSurfaceWin::~DirectCompositionChildSurfaceWin() {
  90. Destroy();
  91. }
  92. bool DirectCompositionChildSurfaceWin::Initialize(GLSurfaceFormat format) {
  93. d3d11_device_ = QueryD3D11DeviceObjectFromANGLE();
  94. dcomp_device_ = GetDirectCompositionDevice();
  95. if (!dcomp_device_)
  96. return false;
  97. EGLint pbuffer_attribs[] = {
  98. EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE,
  99. };
  100. default_surface_ = eglCreatePbufferSurface(display_->GetDisplay(),
  101. GetConfig(), pbuffer_attribs);
  102. if (!default_surface_) {
  103. DLOG(ERROR) << "eglCreatePbufferSurface failed with error "
  104. << ui::GetLastEGLErrorString();
  105. return false;
  106. }
  107. return true;
  108. }
  109. bool DirectCompositionChildSurfaceWin::ReleaseDrawTexture(bool will_discard) {
  110. EGLSurface egl_surface = real_surface_;
  111. real_surface_ = nullptr;
  112. // We make current with the same surface (could be the parent), but its
  113. // handle has changed to |default_surface_|.
  114. gl::GLContext* context = gl::GLContext::GetCurrent();
  115. DCHECK(context);
  116. gl::GLSurface* surface = gl::GLSurface::GetCurrent();
  117. DCHECK(surface);
  118. bool result = context->MakeCurrent(surface);
  119. // If MakeCurrent fails (probably lost device), we'll want to return failure,
  120. // but we still want to reset the rest of the state for consistency.
  121. DLOG_IF(ERROR, !result) << "Failed to make current in ReleaseDrawTexture";
  122. if (egl_surface)
  123. eglDestroySurface(display_->GetDisplay(), egl_surface);
  124. if (dcomp_surface_.Get() == g_current_surface)
  125. g_current_surface = nullptr;
  126. HRESULT hr, device_removed_reason;
  127. if (draw_texture_) {
  128. CopyOffscreenTextureToDrawTexture();
  129. draw_texture_.Reset();
  130. if (dcomp_surface_) {
  131. TRACE_EVENT0("gpu", "DirectCompositionChildSurfaceWin::EndDraw");
  132. hr = dcomp_surface_->EndDraw();
  133. if (FAILED(hr)) {
  134. DLOG(ERROR) << "EndDraw failed with error " << std::hex << hr;
  135. return false;
  136. }
  137. dcomp_surface_serial_++;
  138. } else if (!will_discard) {
  139. const bool use_swap_chain_tearing =
  140. DirectCompositionSwapChainTearingEnabled();
  141. UINT interval =
  142. first_swap_ || !vsync_enabled_ || use_swap_chain_tearing ? 0 : 1;
  143. UINT flags = use_swap_chain_tearing ? DXGI_PRESENT_ALLOW_TEARING : 0;
  144. bool actually_force_full_damage = false;
  145. if (force_full_damage_) {
  146. if (force_full_damage_always_) {
  147. actually_force_full_damage = true;
  148. } else {
  149. float percentage = swap_rect_.size().GetArea();
  150. percentage /= size_.GetArea();
  151. if (percentage >= kForceFullDamageThreshold)
  152. actually_force_full_damage = true;
  153. }
  154. }
  155. TRACE_EVENT2(
  156. "gpu", "DirectCompositionChildSurfaceWin::PresentSwapChain",
  157. "has_alpha", has_alpha_, "dirty_rect",
  158. actually_force_full_damage ? "full_damage" : swap_rect_.ToString());
  159. if (actually_force_full_damage) {
  160. hr = swap_chain_->Present(interval, flags);
  161. } else {
  162. DXGI_PRESENT_PARAMETERS params = {};
  163. RECT dirty_rect = swap_rect_.ToRECT();
  164. params.DirtyRectsCount = 1;
  165. params.pDirtyRects = &dirty_rect;
  166. hr = swap_chain_->Present1(interval, flags, &params);
  167. }
  168. // Ignore DXGI_STATUS_OCCLUDED since that's not an error but only
  169. // indicates that the window is occluded and we can stop rendering.
  170. if (FAILED(hr) && hr != DXGI_STATUS_OCCLUDED) {
  171. DLOG(ERROR) << "Present1 failed with error " << std::hex << hr;
  172. return false;
  173. }
  174. Microsoft::WRL::ComPtr<IDXGISwapChainMedia> swap_chain_media;
  175. if (force_full_damage_ && SUCCEEDED(swap_chain_.As(&swap_chain_media))) {
  176. DXGI_FRAME_STATISTICS_MEDIA stats = {};
  177. // GetFrameStatisticsMedia fails with
  178. // DXGI_ERROR_FRAME_STATISTICS_DISJOINT sometimes, which means an
  179. // event (such as power cycle) interrupted the gathering of
  180. // presentation statistics. In this situation, calling the function
  181. // again succeeds but returns with CompositionMode = NONE.
  182. // Waiting for the DXGI adapter to finish presenting before calling
  183. // the function doesn't get rid of the failure.
  184. if (SUCCEEDED(swap_chain_media->GetFrameStatisticsMedia(&stats))) {
  185. if (actually_force_full_damage) {
  186. base::UmaHistogramSparse(
  187. "GPU.DirectComposition.CompositionMode2.MainBuffer.FullDamage",
  188. stats.CompositionMode);
  189. } else {
  190. base::UmaHistogramSparse(
  191. "GPU.DirectComposition.CompositionMode2.MainBuffer."
  192. "PartialDamage",
  193. stats.CompositionMode);
  194. }
  195. }
  196. }
  197. if (first_swap_) {
  198. // Wait for the GPU to finish executing its commands before
  199. // committing the DirectComposition tree, or else the swapchain
  200. // may flicker black when it's first presented.
  201. first_swap_ = false;
  202. Microsoft::WRL::ComPtr<IDXGIDevice2> dxgi_device2;
  203. d3d11_device_.As(&dxgi_device2);
  204. DCHECK(dxgi_device2);
  205. base::WaitableEvent event(
  206. base::WaitableEvent::ResetPolicy::AUTOMATIC,
  207. base::WaitableEvent::InitialState::NOT_SIGNALED);
  208. hr = dxgi_device2->EnqueueSetEvent(event.handle());
  209. if (SUCCEEDED(hr)) {
  210. event.Wait();
  211. } else {
  212. device_removed_reason = d3d11_device_->GetDeviceRemovedReason();
  213. base::debug::Alias(&hr);
  214. base::debug::Alias(&device_removed_reason);
  215. base::debug::DumpWithoutCrashing();
  216. }
  217. }
  218. }
  219. }
  220. return result;
  221. }
  222. void DirectCompositionChildSurfaceWin::Destroy() {
  223. for (auto& frame : pending_frames_)
  224. std::move(frame.callback).Run(gfx::PresentationFeedback::Failure());
  225. pending_frames_.clear();
  226. if (vsync_thread_started_)
  227. vsync_thread_->RemoveObserver(this);
  228. if (default_surface_) {
  229. if (!eglDestroySurface(display_->GetDisplay(), default_surface_)) {
  230. DLOG(ERROR) << "eglDestroySurface failed with error "
  231. << ui::GetLastEGLErrorString();
  232. }
  233. default_surface_ = nullptr;
  234. }
  235. if (real_surface_) {
  236. if (!eglDestroySurface(display_->GetDisplay(), real_surface_)) {
  237. DLOG(ERROR) << "eglDestroySurface failed with error "
  238. << ui::GetLastEGLErrorString();
  239. }
  240. real_surface_ = nullptr;
  241. }
  242. if (dcomp_surface_ && (dcomp_surface_.Get() == g_current_surface)) {
  243. CopyOffscreenTextureToDrawTexture();
  244. HRESULT hr = dcomp_surface_->EndDraw();
  245. if (FAILED(hr))
  246. DLOG(ERROR) << "EndDraw failed with error " << std::hex << hr;
  247. g_current_surface = nullptr;
  248. }
  249. draw_texture_.Reset();
  250. offscreen_texture_.Reset();
  251. dcomp_surface_.Reset();
  252. }
  253. gfx::Size DirectCompositionChildSurfaceWin::GetSize() {
  254. return size_;
  255. }
  256. bool DirectCompositionChildSurfaceWin::IsOffscreen() {
  257. return false;
  258. }
  259. void* DirectCompositionChildSurfaceWin::GetHandle() {
  260. return real_surface_ ? real_surface_ : default_surface_;
  261. }
  262. gfx::SwapResult DirectCompositionChildSurfaceWin::SwapBuffers(
  263. PresentationCallback callback) {
  264. TRACE_EVENT1("gpu", "DirectCompositionChildSurfaceWin::SwapBuffers", "size",
  265. size_.ToString());
  266. gfx::SwapResult swap_result = ReleaseDrawTexture(false /* will_discard */)
  267. ? gfx::SwapResult::SWAP_ACK
  268. : gfx::SwapResult::SWAP_FAILED;
  269. EnqueuePendingFrame(std::move(callback));
  270. // Reset swap_rect_ since SetDrawRectangle may not be called when the root
  271. // damage rect is empty.
  272. swap_rect_ = gfx::Rect();
  273. return swap_result;
  274. }
  275. gfx::SurfaceOrigin DirectCompositionChildSurfaceWin::GetOrigin() const {
  276. return gfx::SurfaceOrigin::kTopLeft;
  277. }
  278. bool DirectCompositionChildSurfaceWin::SupportsPostSubBuffer() {
  279. return true;
  280. }
  281. bool DirectCompositionChildSurfaceWin::OnMakeCurrent(GLContext* context) {
  282. if (g_current_surface != dcomp_surface_.Get()) {
  283. if (g_current_surface) {
  284. HRESULT hr = g_current_surface->SuspendDraw();
  285. if (FAILED(hr)) {
  286. DLOG(ERROR) << "SuspendDraw failed with error " << std::hex << hr;
  287. return false;
  288. }
  289. g_current_surface = nullptr;
  290. }
  291. // We're in the middle of |dcomp_surface_| draw only if |draw_texture_| is
  292. // not null.
  293. if (dcomp_surface_ && draw_texture_) {
  294. HRESULT hr = dcomp_surface_->ResumeDraw();
  295. if (FAILED(hr)) {
  296. DLOG(ERROR) << "ResumeDraw failed with error " << std::hex << hr;
  297. return false;
  298. }
  299. g_current_surface = dcomp_surface_.Get();
  300. }
  301. }
  302. return true;
  303. }
  304. bool DirectCompositionChildSurfaceWin::SupportsDCLayers() const {
  305. return true;
  306. }
  307. bool DirectCompositionChildSurfaceWin::SetDrawRectangle(
  308. const gfx::Rect& rectangle) {
  309. if (!gfx::Rect(size_).Contains(rectangle)) {
  310. DLOG(ERROR) << "Draw rectangle must be contained within size of surface";
  311. return false;
  312. }
  313. if (draw_texture_) {
  314. DLOG(ERROR) << "SetDrawRectangle must be called only once per swap buffers";
  315. return false;
  316. }
  317. DCHECK(!real_surface_);
  318. DCHECK(!g_current_surface);
  319. if (gfx::Rect(size_) != rectangle && !swap_chain_ && !dcomp_surface_) {
  320. DLOG(ERROR) << "First draw to surface must draw to everything";
  321. return false;
  322. }
  323. gl::GLContext* context = gl::GLContext::GetCurrent();
  324. if (!context) {
  325. DLOG(ERROR) << "gl::GLContext::GetCurrent() returned nullptr";
  326. return false;
  327. }
  328. gl::GLSurface* surface = gl::GLSurface::GetCurrent();
  329. if (!surface) {
  330. DLOG(ERROR) << "gl::GLSurface::GetCurrent() returned nullptr";
  331. return false;
  332. }
  333. DXGI_FORMAT dxgi_format = gfx::ColorSpaceWin::GetDXGIFormat(color_space_);
  334. // IDCompositionDevice2::CreateSurface does not support rgb10. In cases where
  335. // dc overlays are to be used for rgb10, use swap chains instead.
  336. if (!dcomp_surface_ && enable_dc_layers_ &&
  337. dxgi_format != DXGI_FORMAT::DXGI_FORMAT_R10G10B10A2_UNORM) {
  338. TRACE_EVENT2("gpu", "DirectCompositionChildSurfaceWin::CreateSurface",
  339. "width", size_.width(), "height", size_.height());
  340. swap_chain_.Reset();
  341. // Always treat as premultiplied, because an underlay could cause it to
  342. // become transparent.
  343. HRESULT hr = dcomp_device_->CreateSurface(
  344. size_.width(), size_.height(), dxgi_format,
  345. DXGI_ALPHA_MODE_PREMULTIPLIED, &dcomp_surface_);
  346. base::UmaHistogramSparse("GPU.DirectComposition.DcompDeviceCreateSurface",
  347. hr);
  348. if (FAILED(hr)) {
  349. DLOG(ERROR) << "CreateSurface failed with error " << std::hex << hr;
  350. // Disable direct composition because CreateSurface might fail again next
  351. // time.
  352. SetDirectCompositionSwapChainFailed();
  353. return false;
  354. }
  355. // Use swap chains for rgb10 because dcomp surfaces cannot be created.
  356. } else if (!swap_chain_ &&
  357. (!enable_dc_layers_ ||
  358. dxgi_format == DXGI_FORMAT::DXGI_FORMAT_R10G10B10A2_UNORM)) {
  359. TRACE_EVENT2("gpu", "DirectCompositionChildSurfaceWin::CreateSwapChain",
  360. "width", size_.width(), "height", size_.height());
  361. offscreen_texture_.Reset();
  362. dcomp_surface_.Reset();
  363. Microsoft::WRL::ComPtr<IDXGIDevice> dxgi_device;
  364. d3d11_device_.As(&dxgi_device);
  365. DCHECK(dxgi_device);
  366. Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter;
  367. dxgi_device->GetAdapter(&dxgi_adapter);
  368. DCHECK(dxgi_adapter);
  369. Microsoft::WRL::ComPtr<IDXGIFactory2> dxgi_factory;
  370. dxgi_adapter->GetParent(IID_PPV_ARGS(&dxgi_factory));
  371. DCHECK(dxgi_factory);
  372. DXGI_SWAP_CHAIN_DESC1 desc = {};
  373. desc.Width = size_.width();
  374. desc.Height = size_.height();
  375. desc.Format = dxgi_format;
  376. desc.Stereo = FALSE;
  377. desc.SampleDesc.Count = 1;
  378. desc.BufferCount = gl::DirectCompositionRootSurfaceBufferCount();
  379. desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
  380. desc.Scaling = DXGI_SCALING_STRETCH;
  381. desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
  382. desc.AlphaMode =
  383. has_alpha_ ? DXGI_ALPHA_MODE_PREMULTIPLIED : DXGI_ALPHA_MODE_IGNORE;
  384. desc.Flags = 0;
  385. if (DirectCompositionSwapChainTearingEnabled())
  386. desc.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
  387. if (IsWaitableSwapChainEnabled())
  388. desc.Flags |= DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
  389. HRESULT hr = dxgi_factory->CreateSwapChainForComposition(
  390. d3d11_device_.Get(), &desc, nullptr, &swap_chain_);
  391. first_swap_ = true;
  392. base::UmaHistogramSparse(
  393. "GPU.DirectComposition.CreateSwapChainForComposition", hr);
  394. // If CreateSwapChainForComposition fails, we cannot draw to the
  395. // browser window. Return false after disabling Direct Composition support
  396. // and let the Renderer handle it. Either the GPU command buffer or the GPU
  397. // process will be restarted.
  398. if (FAILED(hr)) {
  399. DLOG(ERROR) << "CreateSwapChainForComposition failed with error "
  400. << std::hex << hr;
  401. // Disable direct composition because SwapChain creation might fail again
  402. // next time.
  403. SetDirectCompositionSwapChainFailed();
  404. return false;
  405. }
  406. gl::LabelSwapChainAndBuffers(swap_chain_.Get(),
  407. kDirectCompositionChildSurfaceLabel);
  408. Microsoft::WRL::ComPtr<IDXGISwapChain3> swap_chain;
  409. if (SUCCEEDED(swap_chain_.As(&swap_chain))) {
  410. hr = swap_chain->SetColorSpace1(
  411. gfx::ColorSpaceWin::GetDXGIColorSpace(color_space_));
  412. DCHECK(SUCCEEDED(hr))
  413. << "SetColorSpace1 failed with error " << std::hex << hr;
  414. if (IsWaitableSwapChainEnabled()) {
  415. hr = swap_chain->SetMaximumFrameLatency(GetMaxWaitableQueuedFrames());
  416. DCHECK(SUCCEEDED(hr))
  417. << "SetMaximumFrameLatency failed with error " << std::hex << hr;
  418. }
  419. }
  420. }
  421. swap_rect_ = rectangle;
  422. draw_offset_ = gfx::Vector2d();
  423. const bool verify_draw_offset = dcomp_surface_ && IsVerifyDrawOffsetEnabled();
  424. if (dcomp_surface_) {
  425. TRACE_EVENT0("gpu", "DirectCompositionChildSurfaceWin::BeginDraw");
  426. const RECT rect = rectangle.ToRECT();
  427. dcomp_update_offset_ = {};
  428. HRESULT hr = dcomp_surface_->BeginDraw(&rect, IID_PPV_ARGS(&draw_texture_),
  429. &dcomp_update_offset_);
  430. if (FAILED(hr)) {
  431. DLOG(ERROR) << "BeginDraw failed with error " << std::hex << hr;
  432. return false;
  433. }
  434. if (verify_draw_offset) {
  435. draw_offset_ = {features::kVerifyDrawOffsetX.Get(),
  436. features::kVerifyDrawOffsetY.Get()};
  437. } else {
  438. draw_offset_ = gfx::Point(dcomp_update_offset_) - rectangle.origin();
  439. }
  440. } else {
  441. TRACE_EVENT0("gpu", "DirectCompositionChildSurfaceWin::GetBuffer");
  442. swap_chain_->GetBuffer(0, IID_PPV_ARGS(&draw_texture_));
  443. }
  444. DCHECK(draw_texture_);
  445. g_current_surface = dcomp_surface_.Get();
  446. std::vector<EGLint> pbuffer_attribs;
  447. pbuffer_attribs.push_back(EGL_WIDTH);
  448. pbuffer_attribs.push_back(size_.width());
  449. pbuffer_attribs.push_back(EGL_HEIGHT);
  450. pbuffer_attribs.push_back(size_.height());
  451. if (use_angle_texture_offset_) {
  452. pbuffer_attribs.push_back(EGL_TEXTURE_OFFSET_X_ANGLE);
  453. pbuffer_attribs.push_back(draw_offset_.x());
  454. pbuffer_attribs.push_back(EGL_TEXTURE_OFFSET_Y_ANGLE);
  455. pbuffer_attribs.push_back(draw_offset_.y());
  456. }
  457. pbuffer_attribs.push_back(EGL_NONE);
  458. EGLClientBuffer buffer =
  459. reinterpret_cast<EGLClientBuffer>(draw_texture_.Get());
  460. if (verify_draw_offset) {
  461. buffer = reinterpret_cast<EGLClientBuffer>(GetOffscreenTexture().Get());
  462. }
  463. real_surface_ = eglCreatePbufferFromClientBuffer(
  464. display_->GetDisplay(), EGL_D3D_TEXTURE_ANGLE, buffer, GetConfig(),
  465. pbuffer_attribs.data());
  466. if (!real_surface_) {
  467. DLOG(ERROR) << "eglCreatePbufferFromClientBuffer failed with error "
  468. << ui::GetLastEGLErrorString();
  469. return false;
  470. }
  471. // We make current with the same surface (could be the parent), but its
  472. // handle has changed to |real_surface_|.
  473. if (!context->MakeCurrent(surface)) {
  474. DLOG(ERROR) << "Failed to make current in SetDrawRectangle";
  475. return false;
  476. }
  477. return true;
  478. }
  479. void DirectCompositionChildSurfaceWin::SetDCompSurfaceForTesting(
  480. Microsoft::WRL::ComPtr<IDCompositionSurface> surface) {
  481. offscreen_texture_.Reset();
  482. dcomp_surface_ = std::move(surface);
  483. }
  484. gfx::Vector2d DirectCompositionChildSurfaceWin::GetDrawOffset() const {
  485. return use_angle_texture_offset_ ? gfx::Vector2d() : draw_offset_;
  486. }
  487. void DirectCompositionChildSurfaceWin::SetVSyncEnabled(bool enabled) {
  488. vsync_enabled_ = enabled;
  489. }
  490. bool DirectCompositionChildSurfaceWin::Resize(
  491. const gfx::Size& size,
  492. float scale_factor,
  493. const gfx::ColorSpace& color_space,
  494. bool has_alpha) {
  495. if (size_ == size && has_alpha_ == has_alpha && color_space_ == color_space)
  496. return true;
  497. // This will release indirect references to swap chain (|real_surface_|) by
  498. // binding |default_surface_| as the default framebuffer.
  499. if (!ReleaseDrawTexture(true /* will_discard */))
  500. return false;
  501. bool resize_only = has_alpha_ == has_alpha && color_space_ == color_space;
  502. size_ = size;
  503. color_space_ = color_space;
  504. has_alpha_ = has_alpha;
  505. // ResizeBuffers can't change alpha blending mode.
  506. if (swap_chain_ && resize_only) {
  507. UINT buffer_count = gl::DirectCompositionRootSurfaceBufferCount();
  508. DXGI_FORMAT format = gfx::ColorSpaceWin::GetDXGIFormat(color_space_);
  509. UINT flags = 0;
  510. if (DirectCompositionSwapChainTearingEnabled())
  511. flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
  512. if (IsWaitableSwapChainEnabled())
  513. flags |= DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
  514. HRESULT hr = swap_chain_->ResizeBuffers(buffer_count, size.width(),
  515. size.height(), format, flags);
  516. UMA_HISTOGRAM_BOOLEAN("GPU.DirectComposition.SwapChainResizeResult",
  517. SUCCEEDED(hr));
  518. if (SUCCEEDED(hr))
  519. return true;
  520. // Resizing swap chain buffers causes the internal textures to be released
  521. // and re-created as new textures. We need to label the new textures.
  522. gl::LabelSwapChainBuffers(swap_chain_.Get(),
  523. kDirectCompositionChildSurfaceLabel);
  524. DLOG(ERROR) << "ResizeBuffers failed with error 0x" << std::hex << hr;
  525. }
  526. // Next SetDrawRectangle call will recreate the swap chain or surface.
  527. swap_chain_.Reset();
  528. offscreen_texture_.Reset();
  529. dcomp_surface_.Reset();
  530. return true;
  531. }
  532. bool DirectCompositionChildSurfaceWin::SetEnableDCLayers(bool enable) {
  533. if (enable_dc_layers_ == enable)
  534. return true;
  535. enable_dc_layers_ = enable;
  536. if (!ReleaseDrawTexture(true /* will_discard */))
  537. return false;
  538. // Next SetDrawRectangle call will recreate the swap chain or surface.
  539. swap_chain_.Reset();
  540. offscreen_texture_.Reset();
  541. dcomp_surface_.Reset();
  542. return true;
  543. }
  544. gfx::VSyncProvider* DirectCompositionChildSurfaceWin::GetVSyncProvider() {
  545. return vsync_thread_->vsync_provider();
  546. }
  547. bool DirectCompositionChildSurfaceWin::SupportsGpuVSync() const {
  548. return true;
  549. }
  550. void DirectCompositionChildSurfaceWin::SetGpuVSyncEnabled(bool enabled) {
  551. {
  552. base::AutoLock auto_lock(vsync_callback_enabled_lock_);
  553. vsync_callback_enabled_ = enabled;
  554. }
  555. StartOrStopVSyncThread();
  556. }
  557. void DirectCompositionChildSurfaceWin::OnVSync(base::TimeTicks vsync_time,
  558. base::TimeDelta interval) {
  559. // Main thread will run vsync callback in low latency presentation mode.
  560. if (VSyncCallbackEnabled() && !SupportsLowLatencyPresentation()) {
  561. DCHECK(vsync_callback_);
  562. vsync_callback_.Run(vsync_time, interval);
  563. }
  564. task_runner_->PostTask(
  565. FROM_HERE,
  566. base::BindOnce(&DirectCompositionChildSurfaceWin::HandleVSyncOnMainThread,
  567. weak_factory_.GetWeakPtr(), vsync_time, interval));
  568. }
  569. void DirectCompositionChildSurfaceWin::HandleVSyncOnMainThread(
  570. base::TimeTicks vsync_time,
  571. base::TimeDelta interval) {
  572. last_vsync_time_ = vsync_time;
  573. last_vsync_interval_ = interval;
  574. CheckPendingFrames();
  575. UMA_HISTOGRAM_COUNTS_100("GPU.DirectComposition.NumPendingFrames",
  576. pending_frames_.size());
  577. if (SupportsLowLatencyPresentation() && VSyncCallbackEnabled() &&
  578. pending_frames_.size() < max_pending_frames_) {
  579. DCHECK(vsync_callback_);
  580. vsync_callback_.Run(vsync_time, interval);
  581. }
  582. }
  583. void DirectCompositionChildSurfaceWin::StartOrStopVSyncThread() {
  584. bool start_vsync_thread = VSyncCallbackEnabled() || !pending_frames_.empty();
  585. if (vsync_thread_started_ == start_vsync_thread)
  586. return;
  587. vsync_thread_started_ = start_vsync_thread;
  588. if (start_vsync_thread) {
  589. vsync_thread_->AddObserver(this);
  590. } else {
  591. vsync_thread_->RemoveObserver(this);
  592. }
  593. }
  594. bool DirectCompositionChildSurfaceWin::VSyncCallbackEnabled() const {
  595. base::AutoLock auto_lock(vsync_callback_enabled_lock_);
  596. return vsync_callback_enabled_;
  597. }
  598. void DirectCompositionChildSurfaceWin::CheckPendingFrames() {
  599. TRACE_EVENT1("gpu", "DirectCompositionChildSurfaceWin::CheckPendingFrames",
  600. "num_pending_frames", pending_frames_.size());
  601. if (pending_frames_.empty())
  602. return;
  603. Microsoft::WRL::ComPtr<ID3D11DeviceContext> context;
  604. d3d11_device_->GetImmediateContext(&context);
  605. while (!pending_frames_.empty()) {
  606. auto& frame = pending_frames_.front();
  607. // Query isn't created if there was no damage for previous frame.
  608. if (frame.query) {
  609. HRESULT hr = context->GetData(frame.query.Get(), nullptr, 0,
  610. D3D11_ASYNC_GETDATA_DONOTFLUSH);
  611. // When the GPU completes execution past the event query, GetData() will
  612. // return S_OK, and S_FALSE otherwise. Do not use SUCCEEDED() because
  613. // S_FALSE is also a success code.
  614. if (hr != S_OK)
  615. break;
  616. }
  617. std::move(frame.callback)
  618. .Run(
  619. gfx::PresentationFeedback(last_vsync_time_, last_vsync_interval_,
  620. gfx::PresentationFeedback::kVSync |
  621. gfx::PresentationFeedback::kHWClock));
  622. pending_frames_.pop_front();
  623. }
  624. StartOrStopVSyncThread();
  625. }
  626. void DirectCompositionChildSurfaceWin::EnqueuePendingFrame(
  627. PresentationCallback callback) {
  628. Microsoft::WRL::ComPtr<ID3D11Query> query;
  629. // Do not create query for empty damage so that 3D engine is not used when
  630. // only presenting video in overlay. Callback will be dequeued on next vsync.
  631. if (!swap_rect_.IsEmpty()) {
  632. D3D11_QUERY_DESC desc = {};
  633. desc.Query = D3D11_QUERY_EVENT;
  634. HRESULT hr = d3d11_device_->CreateQuery(&desc, &query);
  635. if (SUCCEEDED(hr)) {
  636. Microsoft::WRL::ComPtr<ID3D11DeviceContext> context;
  637. d3d11_device_->GetImmediateContext(&context);
  638. context->End(query.Get());
  639. context->Flush();
  640. } else {
  641. DLOG(ERROR) << "CreateQuery failed with error 0x" << std::hex << hr;
  642. }
  643. }
  644. pending_frames_.emplace_back(std::move(query), std::move(callback));
  645. StartOrStopVSyncThread();
  646. }
  647. Microsoft::WRL::ComPtr<ID3D11Texture2D>
  648. DirectCompositionChildSurfaceWin::GetOffscreenTexture() {
  649. if (!dcomp_surface_) {
  650. return offscreen_texture_ = nullptr;
  651. }
  652. if (offscreen_texture_) {
  653. return offscreen_texture_;
  654. }
  655. D3D11_TEXTURE2D_DESC desc = {};
  656. desc.Width = size_.width() + features::kVerifyDrawOffsetX.Get();
  657. desc.Height = size_.height() + features::kVerifyDrawOffsetY.Get();
  658. desc.MipLevels = 1;
  659. desc.ArraySize = 1;
  660. desc.Format = gfx::ColorSpaceWin::GetDXGIFormat(color_space_);
  661. desc.SampleDesc.Count = 1;
  662. desc.BindFlags = D3D11_BIND_RENDER_TARGET;
  663. d3d11_device_->CreateTexture2D(&desc, nullptr, &offscreen_texture_);
  664. return offscreen_texture_;
  665. }
  666. void DirectCompositionChildSurfaceWin::CopyOffscreenTextureToDrawTexture() {
  667. if (!offscreen_texture_ || !draw_texture_ || !dcomp_surface_) {
  668. return;
  669. }
  670. D3D11_BOX box = {};
  671. box.left = swap_rect_.origin().x() + features::kVerifyDrawOffsetX.Get();
  672. box.top = swap_rect_.origin().y() + features::kVerifyDrawOffsetY.Get();
  673. box.right = box.left + swap_rect_.width();
  674. box.bottom = box.top + swap_rect_.height();
  675. box.front = 0;
  676. box.back = 1;
  677. Microsoft::WRL::ComPtr<ID3D11DeviceContext> context;
  678. d3d11_device_->GetImmediateContext(&context);
  679. context->CopySubresourceRegion(draw_texture_.Get(), 0, dcomp_update_offset_.x,
  680. dcomp_update_offset_.y, 0,
  681. offscreen_texture_.Get(), 0, &box);
  682. }
  683. } // namespace gl