gl_surface_presentation_helper.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. // Copyright 2018 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/gl_surface_presentation_helper.h"
  5. #include <utility>
  6. #include "base/bind.h"
  7. #include "base/logging.h"
  8. #include "base/threading/thread_task_runner_handle.h"
  9. #include "build/build_config.h"
  10. #include "ui/gfx/vsync_provider.h"
  11. #include "ui/gl/egl_timestamps.h"
  12. #include "ui/gl/gl_context.h"
  13. #include "ui/gl/gl_fence.h"
  14. #include "ui/gl/gpu_timing.h"
  15. namespace gl {
  16. GLSurfacePresentationHelper::ScopedSwapBuffers::ScopedSwapBuffers(
  17. GLSurfacePresentationHelper* helper,
  18. GLSurface::PresentationCallback callback)
  19. : ScopedSwapBuffers(helper, std::move(callback), -1) {}
  20. GLSurfacePresentationHelper::ScopedSwapBuffers::ScopedSwapBuffers(
  21. GLSurfacePresentationHelper* helper,
  22. GLSurface::PresentationCallback callback,
  23. int frame_id)
  24. : helper_(helper) {
  25. if (helper_)
  26. helper_->PreSwapBuffers(std::move(callback), frame_id);
  27. }
  28. GLSurfacePresentationHelper::ScopedSwapBuffers::~ScopedSwapBuffers() {
  29. if (helper_)
  30. helper_->PostSwapBuffers(result_);
  31. }
  32. GLSurfacePresentationHelper::Frame::Frame(Frame&& other) = default;
  33. GLSurfacePresentationHelper::Frame::Frame(
  34. int frame_id,
  35. GLSurface::PresentationCallback callback)
  36. : frame_id(frame_id), callback(std::move(callback)) {}
  37. GLSurfacePresentationHelper::Frame::Frame(
  38. std::unique_ptr<GPUTimer>&& timer,
  39. GLSurface::PresentationCallback callback)
  40. : timer(std::move(timer)), callback(std::move(callback)) {}
  41. GLSurfacePresentationHelper::Frame::Frame(
  42. std::unique_ptr<GLFence>&& fence,
  43. GLSurface::PresentationCallback callback)
  44. : fence(std::move(fence)), callback(std::move(callback)) {}
  45. GLSurfacePresentationHelper::Frame::Frame(
  46. GLSurface::PresentationCallback callback)
  47. : callback(std::move(callback)) {}
  48. GLSurfacePresentationHelper::Frame::~Frame() = default;
  49. GLSurfacePresentationHelper::Frame& GLSurfacePresentationHelper::Frame::
  50. operator=(Frame&& other) = default;
  51. bool GLSurfacePresentationHelper::GetFrameTimestampInfoIfAvailable(
  52. const Frame& frame,
  53. base::TimeTicks* timestamp,
  54. base::TimeDelta* interval,
  55. base::TimeTicks* writes_done,
  56. uint32_t* flags) {
  57. DCHECK(frame.timer || frame.fence || egl_timestamp_client_);
  58. if (egl_timestamp_client_) {
  59. bool result = egl_timestamp_client_->GetFrameTimestampInfoIfAvailable(
  60. timestamp, interval, writes_done, flags, frame.frame_id);
  61. // Workaround null timestamp by setting it to TimeTicks::Now() snapped to
  62. // the next vsync interval. See
  63. // https://bugs.chromium.org/p/chromium/issues/detail?id=966638 for more
  64. // details.
  65. if (result && timestamp->is_null()) {
  66. *timestamp = base::TimeTicks::Now();
  67. *interval = vsync_interval_;
  68. *flags = 0;
  69. if (!vsync_interval_.is_zero()) {
  70. *timestamp =
  71. timestamp->SnappedToNextTick(vsync_timebase_, vsync_interval_);
  72. *flags = gfx::PresentationFeedback::kVSync;
  73. }
  74. }
  75. return result;
  76. } else if (frame.timer) {
  77. if (!frame.timer->IsAvailable())
  78. return false;
  79. int64_t start = 0;
  80. int64_t end = 0;
  81. frame.timer->GetStartEndTimestamps(&start, &end);
  82. *timestamp = base::TimeTicks() + base::Microseconds(start);
  83. } else {
  84. if (!frame.fence->HasCompleted())
  85. return false;
  86. *timestamp = base::TimeTicks::Now();
  87. }
  88. // Below logic is used to calculate final values of timestamp, interval and
  89. // flags when using timer/fence to report the timestamps.
  90. const bool fixed_vsync = !vsync_provider_;
  91. const bool hw_clock = vsync_provider_ && vsync_provider_->IsHWClock();
  92. *interval = vsync_interval_;
  93. *flags = 0;
  94. if (vsync_interval_.is_zero() || fixed_vsync) {
  95. // If VSync parameters are fixed or not available, we just run
  96. // presentation callbacks with timestamp from GPUTimers.
  97. return true;
  98. } else if (*timestamp < vsync_timebase_) {
  99. // We got a VSync whose timestamp is after GPU finished rendering this
  100. // back buffer.
  101. *flags = gfx::PresentationFeedback::kVSync |
  102. gfx::PresentationFeedback::kHWCompletion;
  103. auto delta = vsync_timebase_ - *timestamp;
  104. if (delta < vsync_interval_) {
  105. // The |vsync_timebase_| is the closest VSync's timestamp after the GPU
  106. // finished rendering.
  107. *timestamp = vsync_timebase_;
  108. if (hw_clock)
  109. *flags |= gfx::PresentationFeedback::kHWClock;
  110. } else {
  111. // The |vsync_timebase_| isn't the closest VSync's timestamp after the
  112. // GPU finished rendering. We have to compute the closest VSync's
  113. // timestmp.
  114. *timestamp =
  115. timestamp->SnappedToNextTick(vsync_timebase_, vsync_interval_);
  116. }
  117. } else {
  118. // The |vsync_timebase_| is earlier than |timestamp|, we will compute the
  119. // next vSync's timestamp and use it to run callback.
  120. if (!vsync_interval_.is_zero()) {
  121. *timestamp =
  122. timestamp->SnappedToNextTick(vsync_timebase_, vsync_interval_);
  123. *flags = gfx::PresentationFeedback::kVSync;
  124. }
  125. }
  126. return true;
  127. }
  128. void GLSurfacePresentationHelper::Frame::Destroy(bool has_context) {
  129. if (timer) {
  130. timer->Destroy(has_context);
  131. } else if (fence) {
  132. if (has_context)
  133. fence = nullptr;
  134. else
  135. fence->Invalidate();
  136. }
  137. std::move(callback).Run(gfx::PresentationFeedback::Failure());
  138. }
  139. GLSurfacePresentationHelper::GLSurfacePresentationHelper(
  140. gfx::VSyncProvider* vsync_provider)
  141. : vsync_provider_(vsync_provider) {}
  142. GLSurfacePresentationHelper::GLSurfacePresentationHelper(
  143. base::TimeTicks timebase,
  144. base::TimeDelta interval)
  145. : vsync_provider_(nullptr),
  146. vsync_timebase_(timebase),
  147. vsync_interval_(interval) {}
  148. GLSurfacePresentationHelper::~GLSurfacePresentationHelper() {
  149. // Discard pending frames and run presentation callback with empty
  150. // PresentationFeedback.
  151. bool has_context = gl_context_ && gl_context_->IsCurrent(surface_);
  152. for (auto& frame : pending_frames_) {
  153. frame.Destroy(has_context);
  154. }
  155. pending_frames_.clear();
  156. }
  157. void GLSurfacePresentationHelper::OnMakeCurrent(GLContext* context,
  158. GLSurface* surface) {
  159. DCHECK(context);
  160. DCHECK(surface);
  161. DCHECK(surface == surface_ || !surface_);
  162. if (context == gl_context_)
  163. return;
  164. surface_ = surface;
  165. // If context is changed, we assume SwapBuffers issued for previous context
  166. // will be discarded.
  167. if (gpu_timing_client_)
  168. gpu_timing_client_ = nullptr;
  169. for (auto& frame : pending_frames_) {
  170. frame.Destroy();
  171. }
  172. pending_frames_.clear();
  173. gl_context_ = context;
  174. // Get an egl timestamp client.
  175. egl_timestamp_client_ = surface_->GetEGLTimestampClient();
  176. // If there is an egl timestamp client, check if egl timestamps are supported
  177. // or not. If supported, then return as there is no need to use gpu timestamp
  178. // client or fence.
  179. if (egl_timestamp_client_) {
  180. if (egl_timestamp_client_->IsEGLTimestampSupported())
  181. return;
  182. else
  183. egl_timestamp_client_ = nullptr;
  184. }
  185. gpu_timing_client_ = context->CreateGPUTimingClient();
  186. if (!gpu_timing_client_->IsAvailable())
  187. gpu_timing_client_ = nullptr;
  188. // https://crbug.com/854298 : disable GLFence on Android as they seem to cause
  189. // issues on some devices.
  190. #if !BUILDFLAG(IS_ANDROID)
  191. gl_fence_supported_ = GLFence::IsSupported();
  192. #endif
  193. }
  194. void GLSurfacePresentationHelper::PreSwapBuffers(
  195. GLSurface::PresentationCallback callback,
  196. int frame_id) {
  197. if (egl_timestamp_client_) {
  198. pending_frames_.emplace_back(frame_id, std::move(callback));
  199. } else if (gpu_timing_client_) {
  200. std::unique_ptr<GPUTimer> timer;
  201. timer = gpu_timing_client_->CreateGPUTimer(false /* prefer_elapsed_time */);
  202. timer->QueryTimeStamp();
  203. pending_frames_.push_back(Frame(std::move(timer), std::move(callback)));
  204. } else if (gl_fence_supported_) {
  205. auto fence = GLFence::Create();
  206. pending_frames_.push_back(Frame(std::move(fence), std::move(callback)));
  207. } else {
  208. pending_frames_.push_back(Frame(std::move(callback)));
  209. }
  210. }
  211. void GLSurfacePresentationHelper::PostSwapBuffers(gfx::SwapResult result) {
  212. DCHECK(!pending_frames_.empty());
  213. auto& frame = pending_frames_.back();
  214. frame.result = result;
  215. ScheduleCheckPendingFrames(false /* align_with_next_vsync */);
  216. }
  217. void GLSurfacePresentationHelper::CheckPendingFrames() {
  218. DCHECK(gl_context_ || pending_frames_.empty());
  219. if (vsync_provider_ &&
  220. vsync_provider_->SupportGetVSyncParametersIfAvailable()) {
  221. if (!vsync_provider_->GetVSyncParametersIfAvailable(&vsync_timebase_,
  222. &vsync_interval_)) {
  223. vsync_timebase_ = base::TimeTicks();
  224. vsync_interval_ = base::TimeDelta();
  225. static unsigned int count = 0;
  226. // GetVSyncParametersIfAvailable() could be called and failed frequently,
  227. // so we have to limit the LOG to avoid flooding the log.
  228. LOG_IF(ERROR, ++count < 4 || !(count & 0xffff))
  229. << "GetVSyncParametersIfAvailable() failed for " << count
  230. << " times!";
  231. }
  232. }
  233. if (pending_frames_.empty())
  234. return;
  235. if (!gl_context_->MakeCurrent(surface_)) {
  236. gl_context_ = nullptr;
  237. egl_timestamp_client_ = nullptr;
  238. gpu_timing_client_ = nullptr;
  239. for (auto& frame : pending_frames_)
  240. frame.Destroy();
  241. pending_frames_.clear();
  242. return;
  243. }
  244. bool disjoint_occurred =
  245. gpu_timing_client_ && gpu_timing_client_->CheckAndResetTimerErrors();
  246. if (disjoint_occurred ||
  247. (!egl_timestamp_client_ && !gpu_timing_client_ && !gl_fence_supported_)) {
  248. // If EGLTimestamps, GPUTimer and GLFence are not available or disjoint
  249. // occurred, we will compute the next VSync's timestamp and use it to run
  250. // presentation callback.
  251. uint32_t flags = 0;
  252. auto timestamp = base::TimeTicks::Now();
  253. if (!vsync_interval_.is_zero()) {
  254. timestamp = timestamp.SnappedToNextTick(vsync_timebase_, vsync_interval_);
  255. flags = gfx::PresentationFeedback::kVSync;
  256. }
  257. gfx::PresentationFeedback feedback(timestamp, vsync_interval_, flags);
  258. for (auto& frame : pending_frames_) {
  259. if (frame.timer)
  260. frame.timer->Destroy(true /* has_context */);
  261. if (frame.result == gfx::SwapResult::SWAP_ACK)
  262. std::move(frame.callback).Run(feedback);
  263. else
  264. std::move(frame.callback).Run(gfx::PresentationFeedback::Failure());
  265. }
  266. pending_frames_.clear();
  267. }
  268. while (!pending_frames_.empty()) {
  269. auto& frame = pending_frames_.front();
  270. // Helper lambda for running the presentation callback and releasing the
  271. // frame.
  272. auto frame_presentation_callback =
  273. [this, &frame](const gfx::PresentationFeedback& feedback) {
  274. if (frame.timer)
  275. frame.timer->Destroy(true /* has_context */);
  276. std::move(frame.callback).Run(feedback);
  277. pending_frames_.pop_front();
  278. };
  279. if (frame.result != gfx::SwapResult::SWAP_ACK) {
  280. frame_presentation_callback(gfx::PresentationFeedback::Failure());
  281. continue;
  282. }
  283. base::TimeTicks timestamp;
  284. base::TimeDelta interval;
  285. base::TimeTicks writes_done;
  286. uint32_t flags = 0;
  287. // Get timestamp info for a frame if available. If timestamp is not
  288. // available, it means this frame is not yet done.
  289. if (!GetFrameTimestampInfoIfAvailable(frame, &timestamp, &interval,
  290. &writes_done, &flags))
  291. break;
  292. gfx::PresentationFeedback feedback(timestamp, interval, flags);
  293. feedback.writes_done_timestamp = writes_done;
  294. frame_presentation_callback(feedback);
  295. }
  296. if (!pending_frames_.empty())
  297. ScheduleCheckPendingFrames(true /* align_with_next_vsync */);
  298. }
  299. void GLSurfacePresentationHelper::CheckPendingFramesCallback() {
  300. DCHECK(check_pending_frame_scheduled_);
  301. check_pending_frame_scheduled_ = false;
  302. CheckPendingFrames();
  303. }
  304. void GLSurfacePresentationHelper::UpdateVSyncCallback(
  305. bool should_check_pending_frames,
  306. const base::TimeTicks timebase,
  307. const base::TimeDelta interval) {
  308. DCHECK(update_vsync_pending_);
  309. update_vsync_pending_ = false;
  310. vsync_timebase_ = timebase;
  311. vsync_interval_ = interval;
  312. if (should_check_pending_frames) {
  313. DCHECK(check_pending_frame_scheduled_);
  314. check_pending_frame_scheduled_ = false;
  315. CheckPendingFrames();
  316. }
  317. }
  318. void GLSurfacePresentationHelper::ScheduleCheckPendingFrames(
  319. bool align_with_next_vsync) {
  320. // Always GetVSyncParameters to minimize clock-skew in vsync_timebase_.
  321. bool vsync_provider_schedules_check = false;
  322. if (vsync_provider_ &&
  323. !vsync_provider_->SupportGetVSyncParametersIfAvailable() &&
  324. !update_vsync_pending_) {
  325. update_vsync_pending_ = true;
  326. vsync_provider_schedules_check =
  327. !check_pending_frame_scheduled_ && !align_with_next_vsync;
  328. vsync_provider_->GetVSyncParameters(base::BindOnce(
  329. &GLSurfacePresentationHelper::UpdateVSyncCallback,
  330. weak_ptr_factory_.GetWeakPtr(), vsync_provider_schedules_check));
  331. }
  332. if (check_pending_frame_scheduled_)
  333. return;
  334. check_pending_frame_scheduled_ = true;
  335. if (vsync_provider_schedules_check)
  336. return;
  337. if (!align_with_next_vsync) {
  338. base::ThreadTaskRunnerHandle::Get()->PostTask(
  339. FROM_HERE,
  340. base::BindOnce(&GLSurfacePresentationHelper::CheckPendingFramesCallback,
  341. weak_ptr_factory_.GetWeakPtr()));
  342. return;
  343. }
  344. // If the |vsync_provider_| can not notify us for the next VSync
  345. // asynchronically, we have to compute the next VSync time and post a delayed
  346. // task so we can check the VSync later.
  347. base::TimeDelta interval =
  348. vsync_interval_.is_zero() ? base::Seconds(1) / 60 : vsync_interval_;
  349. auto now = base::TimeTicks::Now();
  350. auto next_vsync = now.SnappedToNextTick(vsync_timebase_, interval);
  351. base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
  352. FROM_HERE,
  353. base::BindOnce(&GLSurfacePresentationHelper::CheckPendingFramesCallback,
  354. weak_ptr_factory_.GetWeakPtr()),
  355. next_vsync - now);
  356. }
  357. } // namespace gl