1 // Copyright 2014 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 
5 #include "ui/ozone/demo/surfaceless_gl_renderer.h"
6 
7 #include <stddef.h>
8 #include <memory>
9 #include <utility>
10 
11 #include "base/bind.h"
12 #include "base/bind_helpers.h"
13 #include "base/command_line.h"
14 #include "base/macros.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/trace_event/trace_event.h"
17 #include "ui/display/types/display_snapshot.h"
18 #include "ui/gfx/geometry/rect_conversions.h"
19 #include "ui/gfx/gpu_fence.h"
20 #include "ui/gl/gl_bindings.h"
21 #include "ui/gl/gl_context.h"
22 #include "ui/gl/gl_fence.h"
23 #include "ui/gl/gl_image.h"
24 #include "ui/gl/gl_image_native_pixmap.h"
25 #include "ui/gl/gl_surface.h"
26 #include "ui/gl/init/gl_factory.h"
27 #include "ui/ozone/public/overlay_candidates_ozone.h"
28 #include "ui/ozone/public/overlay_manager_ozone.h"
29 #include "ui/ozone/public/ozone_platform.h"
30 #include "ui/ozone/public/platform_window_surface.h"
31 #include "ui/ozone/public/surface_factory_ozone.h"
32 
33 namespace ui {
34 
35 namespace {
36 
MakeOverlayCandidate(int z_order,gfx::Rect bounds_rect,gfx::RectF crop_rect)37 OverlaySurfaceCandidate MakeOverlayCandidate(int z_order,
38                                              gfx::Rect bounds_rect,
39                                              gfx::RectF crop_rect) {
40   // The overlay checking interface is designed to satisfy the needs of CC which
41   // will be producing RectF target rectangles. But we use the bounds produced
42   // in RenderFrame for GLSurface::ScheduleOverlayPlane.
43   gfx::RectF display_rect(bounds_rect.x(), bounds_rect.y(), bounds_rect.width(),
44                           bounds_rect.height());
45 
46   OverlaySurfaceCandidate overlay_candidate;
47 
48   // Use default display format since this should be compatible with most
49   // devices.
50   overlay_candidate.format = display::DisplaySnapshot::PrimaryFormat();
51 
52   // The bounds rectangle of the candidate overlay buffer.
53   overlay_candidate.buffer_size = bounds_rect.size();
54   // The same rectangle in floating point coordinates.
55   overlay_candidate.display_rect = display_rect;
56 
57   overlay_candidate.crop_rect = crop_rect;
58 
59   // The demo overlay instance is always ontop and not clipped. Clipped quads
60   // cannot be placed in overlays.
61   overlay_candidate.is_clipped = false;
62 
63   return overlay_candidate;
64 }
65 
66 }  // namespace
67 
BufferWrapper()68 SurfacelessGlRenderer::BufferWrapper::BufferWrapper() {}
69 
~BufferWrapper()70 SurfacelessGlRenderer::BufferWrapper::~BufferWrapper() {
71   if (gl_fb_)
72     glDeleteFramebuffersEXT(1, &gl_fb_);
73 
74   if (gl_tex_) {
75     image_->ReleaseTexImage(GL_TEXTURE_2D);
76     glDeleteTextures(1, &gl_tex_);
77   }
78 }
79 
Initialize(gfx::AcceleratedWidget widget,const gfx::Size & size)80 bool SurfacelessGlRenderer::BufferWrapper::Initialize(
81     gfx::AcceleratedWidget widget,
82     const gfx::Size& size) {
83   glGenFramebuffersEXT(1, &gl_fb_);
84   glGenTextures(1, &gl_tex_);
85 
86   gfx::BufferFormat format = display::DisplaySnapshot::PrimaryFormat();
87   scoped_refptr<gfx::NativePixmap> pixmap =
88       OzonePlatform::GetInstance()
89           ->GetSurfaceFactoryOzone()
90           ->CreateNativePixmap(widget, nullptr, size, format,
91                                gfx::BufferUsage::SCANOUT);
92   auto image = base::MakeRefCounted<gl::GLImageNativePixmap>(size, format);
93   if (!image->Initialize(std::move(pixmap))) {
94     LOG(ERROR) << "Failed to create GLImage";
95     return false;
96   }
97   image_ = image;
98 
99   glBindFramebufferEXT(GL_FRAMEBUFFER, gl_fb_);
100   glBindTexture(GL_TEXTURE_2D, gl_tex_);
101   image_->BindTexImage(GL_TEXTURE_2D);
102 
103   glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
104                             gl_tex_, 0);
105   if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
106     LOG(ERROR) << "Failed to create framebuffer "
107                << glCheckFramebufferStatusEXT(GL_FRAMEBUFFER);
108     return false;
109   }
110 
111   widget_ = widget;
112   size_ = size;
113 
114   return true;
115 }
116 
BindFramebuffer()117 void SurfacelessGlRenderer::BufferWrapper::BindFramebuffer() {
118   glBindFramebufferEXT(GL_FRAMEBUFFER, gl_fb_);
119 }
120 
SurfacelessGlRenderer(gfx::AcceleratedWidget widget,std::unique_ptr<PlatformWindowSurface> window_surface,const scoped_refptr<gl::GLSurface> & gl_surface,const gfx::Size & size)121 SurfacelessGlRenderer::SurfacelessGlRenderer(
122     gfx::AcceleratedWidget widget,
123     std::unique_ptr<PlatformWindowSurface> window_surface,
124     const scoped_refptr<gl::GLSurface>& gl_surface,
125     const gfx::Size& size)
126     : RendererBase(widget, size),
127       overlay_checker_(ui::OzonePlatform::GetInstance()
128                            ->GetOverlayManager()
129                            ->CreateOverlayCandidates(widget)),
130       window_surface_(std::move(window_surface)),
131       gl_surface_(gl_surface) {}
132 
~SurfacelessGlRenderer()133 SurfacelessGlRenderer::~SurfacelessGlRenderer() {
134   // Need to make current when deleting the framebuffer resources allocated in
135   // the buffers.
136   context_->MakeCurrent(gl_surface_.get());
137   for (size_t i = 0; i < base::size(buffers_); ++i)
138     buffers_[i].reset();
139 
140   for (size_t i = 0; i < kMaxLayers; ++i) {
141     for (size_t j = 0; j < base::size(overlay_buffers_[i]); ++j)
142       overlay_buffers_[i][j].reset();
143   }
144 }
145 
Initialize()146 bool SurfacelessGlRenderer::Initialize() {
147   context_ = gl::init::CreateGLContext(nullptr, gl_surface_.get(),
148                                        gl::GLContextAttribs());
149   if (!context_.get()) {
150     LOG(ERROR) << "Failed to create GL context";
151     return false;
152   }
153 
154   gl_surface_->Resize(size_, 1.f, gfx::ColorSpace(), true);
155 
156   if (!context_->MakeCurrent(gl_surface_.get())) {
157     LOG(ERROR) << "Failed to make GL context current";
158     return false;
159   }
160 
161   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
162   if (command_line->HasSwitch("partial-primary-plane"))
163     primary_plane_rect_ = gfx::Rect(200, 200, 800, 800);
164   else
165     primary_plane_rect_ = gfx::Rect(size_);
166 
167   for (size_t i = 0; i < base::size(buffers_); ++i) {
168     buffers_[i] = std::make_unique<BufferWrapper>();
169     if (!buffers_[i]->Initialize(widget_, primary_plane_rect_.size()))
170       return false;
171   }
172 
173   if (command_line->HasSwitch("enable-overlay")) {
174     int requested_overlay_cnt;
175     base::StringToInt(
176         command_line->GetSwitchValueASCII("enable-overlay").c_str(),
177         &requested_overlay_cnt);
178     overlay_cnt_ = std::max(1, std::min(kMaxLayers, requested_overlay_cnt));
179 
180     const gfx::Size overlay_size =
181         gfx::Size(size_.width() / 8, size_.height() / 8);
182     for (size_t i = 0; i < overlay_cnt_; ++i) {
183       for (size_t j = 0; j < base::size(overlay_buffers_[i]); ++j) {
184         overlay_buffers_[i][j] = std::make_unique<BufferWrapper>();
185         overlay_buffers_[i][j]->Initialize(gfx::kNullAcceleratedWidget,
186                                            overlay_size);
187 
188         glViewport(0, 0, overlay_size.width(), overlay_size.height());
189         glClearColor(j, 1.0, 0.0, 1.0);
190         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
191         // Ensure that the rendering has been committed to the buffer and thus
192         // that the buffer is ready for display without additional
193         // synchronization. This allows us to avoid using fences for display
194         // synchronization of the non-overlay buffers in RenderFrame.
195         glFinish();
196       }
197     }
198   }
199 
200   disable_primary_plane_ = command_line->HasSwitch("disable-primary-plane");
201 
202   use_gpu_fences_ = gl_surface_->SupportsPlaneGpuFences();
203 
204   // Schedule the initial render.
205   PostRenderFrameTask(gfx::SwapResult::SWAP_ACK, nullptr);
206   return true;
207 }
208 
RenderFrame()209 void SurfacelessGlRenderer::RenderFrame() {
210   TRACE_EVENT0("ozone", "SurfacelessGlRenderer::RenderFrame");
211 
212   float fraction = NextFraction();
213 
214   gfx::Rect overlay_rect[kMaxLayers];
215   const gfx::RectF unity_rect = gfx::RectF(0, 0, 1, 1);
216 
217   OverlayCandidatesOzone::OverlaySurfaceCandidateList overlay_list;
218   if (!disable_primary_plane_) {
219     overlay_list.push_back(
220         MakeOverlayCandidate(1, gfx::Rect(size_), unity_rect));
221     // We know at least the primary plane can be scanned out.
222     overlay_list.back().overlay_handled = true;
223   }
224 
225   for (size_t i = 0; i < overlay_cnt_; ++i) {
226     overlay_rect[i] = gfx::Rect(overlay_buffers_[i][0]->size());
227 
228     float steps_num = 5.0f;
229     float stepped_fraction =
230         std::floor((fraction + 0.5f / steps_num) * steps_num) / steps_num;
231     gfx::Vector2d offset(
232         stepped_fraction * (size_.width() - overlay_rect[i].width()),
233         ((size_.height() / (overlay_cnt_ + 1)) * (i + 1) -
234          overlay_rect[i].height() / 2));
235     overlay_rect[i] += offset;
236     overlay_list.push_back(
237         MakeOverlayCandidate(1, overlay_rect[i], unity_rect));
238   }
239 
240   // The actual validation for a specific overlay configuration is done
241   // asynchronously and then cached inside overlay_checker_ once a reply
242   // is sent back.
243   // This means that the first few frames we call this method for a specific
244   // overlay_list, all the overlays but the primary plane, that we explicitly
245   // marked as handled, will be rejected even if they might be handled at a
246   // later time.
247   overlay_checker_->CheckOverlaySupport(&overlay_list);
248 
249   context_->MakeCurrent(gl_surface_.get());
250   buffers_[back_buffer_]->BindFramebuffer();
251 
252   glViewport(0, 0, size_.width(), size_.height());
253   glClearColor(1 - fraction, 0.0, fraction, 1.0);
254   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
255 
256   if (!disable_primary_plane_) {
257     CHECK(overlay_list.front().overlay_handled);
258 
259     // Optionally use a fence to synchronize overlay plane display, if
260     // requested when invoking ozone_demo. Note that currently only the primary
261     // plane needs to use a fence, since its buffers are dynamically updated
262     // every frame. The buffers for non-primary planes are only drawn to during
263     // initialization and guaranteed to be ready for display (see Initialize),
264     // so no additional fence synchronization is needed for them.
265     std::unique_ptr<gl::GLFence> gl_fence =
266         use_gpu_fences_ ? gl::GLFence::CreateForGpuFence() : nullptr;
267 
268     gl_surface_->ScheduleOverlayPlane(
269         0, gfx::OVERLAY_TRANSFORM_NONE, buffers_[back_buffer_]->image(),
270         primary_plane_rect_, unity_rect, false,
271         gl_fence ? gl_fence->GetGpuFence() : nullptr);
272   }
273 
274   for (size_t i = 0; i < overlay_cnt_; ++i) {
275     if (overlay_list.back().overlay_handled) {
276       gl_surface_->ScheduleOverlayPlane(
277           1, gfx::OVERLAY_TRANSFORM_NONE,
278           overlay_buffers_[i][back_buffer_]->image(), overlay_rect[i],
279           unity_rect, false, /* gpu_fence */ nullptr);
280     }
281   }
282 
283   back_buffer_ ^= 1;
284   gl_surface_->SwapBuffersAsync(
285       base::BindOnce(&SurfacelessGlRenderer::PostRenderFrameTask,
286                      weak_ptr_factory_.GetWeakPtr()),
287       base::DoNothing());
288 }
289 
PostRenderFrameTask(gfx::SwapResult result,std::unique_ptr<gfx::GpuFence> gpu_fence)290 void SurfacelessGlRenderer::PostRenderFrameTask(
291     gfx::SwapResult result,
292     std::unique_ptr<gfx::GpuFence> gpu_fence) {
293   if (gpu_fence)
294     gpu_fence->Wait();
295 
296   switch (result) {
297     case gfx::SwapResult::SWAP_NAK_RECREATE_BUFFERS:
298       for (size_t i = 0; i < base::size(buffers_); ++i) {
299         buffers_[i] = std::make_unique<BufferWrapper>();
300         if (!buffers_[i]->Initialize(widget_, primary_plane_rect_.size()))
301           LOG(FATAL) << "Failed to recreate buffer";
302       }
303       FALLTHROUGH;  // We want to render a new frame anyways.
304     case gfx::SwapResult::SWAP_ACK:
305       base::ThreadTaskRunnerHandle::Get()->PostTask(
306           FROM_HERE, base::BindOnce(&SurfacelessGlRenderer::RenderFrame,
307                                     weak_ptr_factory_.GetWeakPtr()));
308       break;
309     case gfx::SwapResult::SWAP_FAILED:
310       LOG(FATAL) << "Failed to swap buffers";
311       break;
312   }
313 }
314 
OnPresentation(const gfx::PresentationFeedback & feedback)315 void SurfacelessGlRenderer::OnPresentation(
316     const gfx::PresentationFeedback& feedback) {
317   LOG_IF(ERROR, feedback.timestamp.is_null()) << "Last frame is discarded!";
318 }
319 
320 }  // namespace ui
321