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 
5 #include "components/viz/service/display_embedder/gl_output_surface_buffer_queue.h"
6 
7 #include <utility>
8 
9 #include "base/bind.h"
10 #include "base/command_line.h"
11 #include "components/viz/common/frame_sinks/begin_frame_source.h"
12 #include "components/viz/common/gpu/context_provider.h"
13 #include "components/viz/common/switches.h"
14 #include "components/viz/service/display/output_surface_client.h"
15 #include "components/viz/service/display/output_surface_frame.h"
16 #include "gpu/GLES2/gl2extchromium.h"
17 #include "gpu/command_buffer/client/context_support.h"
18 #include "gpu/command_buffer/client/gles2_interface.h"
19 #include "gpu/command_buffer/common/gpu_memory_buffer_support.h"
20 #include "gpu/command_buffer/common/sync_token.h"
21 #include "ui/gl/buffer_format_utils.h"
22 #include "ui/gl/gl_enums.h"
23 
24 namespace viz {
25 
GLOutputSurfaceBufferQueue(scoped_refptr<VizProcessContextProvider> context_provider,gpu::SurfaceHandle surface_handle,std::unique_ptr<BufferQueue> buffer_queue)26 GLOutputSurfaceBufferQueue::GLOutputSurfaceBufferQueue(
27     scoped_refptr<VizProcessContextProvider> context_provider,
28     gpu::SurfaceHandle surface_handle,
29     std::unique_ptr<BufferQueue> buffer_queue)
30     : GLOutputSurface(context_provider, surface_handle),
31       buffer_queue_(std::move(buffer_queue)) {
32   capabilities_.only_invalidates_damage_rect = false;
33   capabilities_.uses_default_gl_framebuffer = false;
34   capabilities_.output_surface_origin = gfx::SurfaceOrigin::kTopLeft;
35   // Set |max_frames_pending| to 2 for buffer_queue, which aligns scheduling
36   // more closely with the previous surfaced behavior.
37   // With a surface, swap buffer ack used to return early, before actually
38   // presenting the back buffer, enabling the browser compositor to run ahead.
39   // BufferQueue implementation acks at the time of actual buffer swap, which
40   // shifts the start of the new frame forward relative to the old
41   // implementation.
42   capabilities_.max_frames_pending = 2;
43 
44   // Force the number of max pending frames to one when the switch
45   // "double-buffer-compositing" is passed.
46   // This will keep compositing in double buffered mode assuming |buffer_queue_|
47   // allocates at most one additional buffer.
48   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
49   if (command_line->HasSwitch(switches::kDoubleBufferCompositing)) {
50     capabilities_.max_frames_pending = 1;
51     buffer_queue_->SetMaxBuffers(2);
52   }
53 
54   // It is safe to pass a raw pointer to *this because |buffer_queue_| is fully
55   // owned and it doesn't use the SyncTokenProvider after it's destroyed.
56   DCHECK(buffer_queue_);
57   buffer_queue_->SetSyncTokenProvider(this);
58   context_provider_->ContextGL()->GenFramebuffers(1, &fbo_);
59 }
60 
~GLOutputSurfaceBufferQueue()61 GLOutputSurfaceBufferQueue::~GLOutputSurfaceBufferQueue() {
62   auto* gl = context_provider_->ContextGL();
63   DCHECK_NE(0u, fbo_);
64   gl->DeleteFramebuffers(1, &fbo_);
65   if (stencil_buffer_)
66     gl->DeleteRenderbuffers(1, &stencil_buffer_);
67   for (const auto& buffer_texture : buffer_queue_textures_)
68     gl->DeleteTextures(1u, &buffer_texture.second);
69   buffer_queue_textures_.clear();
70   current_texture_ = 0u;
71   last_bound_texture_ = 0u;
72   last_bound_mailbox_.SetZero();
73 
74   // Freeing the BufferQueue here ensures that *this is fully alive in case the
75   // BufferQueue needs the SyncTokenProvider functionality.
76   buffer_queue_.reset();
77   fbo_ = 0u;
78   stencil_buffer_ = 0u;
79 }
80 
BindFramebuffer()81 void GLOutputSurfaceBufferQueue::BindFramebuffer() {
82   auto* gl = context_provider_->ContextGL();
83   gl->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
84 
85   // If we have a |current_texture_|, it means we haven't swapped the buffer, so
86   // we're just wanting to rebind the GL framebuffer.
87   if (current_texture_)
88     return;
89 
90   DCHECK(buffer_queue_);
91   gpu::SyncToken creation_sync_token;
92   const gpu::Mailbox current_buffer =
93       buffer_queue_->GetCurrentBuffer(&creation_sync_token);
94   if (current_buffer.IsZero())
95     return;
96   gl->WaitSyncTokenCHROMIUM(creation_sync_token.GetConstData());
97   unsigned& buffer_texture = buffer_queue_textures_[current_buffer];
98   if (!buffer_texture) {
99     buffer_texture =
100         gl->CreateAndTexStorage2DSharedImageCHROMIUM(current_buffer.name);
101   }
102   current_texture_ = buffer_texture;
103   gl->BeginSharedImageAccessDirectCHROMIUM(
104       current_texture_, GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM);
105   gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
106                            texture_target_, current_texture_, 0);
107   last_bound_texture_ = current_texture_;
108   last_bound_mailbox_ = current_buffer;
109 
110 #if DCHECK_IS_ON() && defined(OS_CHROMEOS)
111   const GLenum result = gl->CheckFramebufferStatus(GL_FRAMEBUFFER);
112   if (result != GL_FRAMEBUFFER_COMPLETE)
113     DLOG(ERROR) << " Incomplete fb: " << gl::GLEnums::GetStringError(result);
114 #endif
115 
116   // Reshape() must be called to go from using a stencil buffer to not using it.
117   DCHECK(use_stencil_ || !stencil_buffer_);
118   if (use_stencil_ && !stencil_buffer_) {
119     gl->GenRenderbuffers(1, &stencil_buffer_);
120     CHECK_NE(stencil_buffer_, 0u);
121     gl->BindRenderbuffer(GL_RENDERBUFFER, stencil_buffer_);
122     gl->RenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8,
123                             reshape_size_.width(), reshape_size_.height());
124     gl->BindRenderbuffer(GL_RENDERBUFFER, 0);
125     gl->FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
126                                 GL_RENDERBUFFER, stencil_buffer_);
127   }
128 }
129 
130 // We call this on every frame that a value changes, but changing the size once
131 // we've allocated backing NativePixmapBufferQueue instances will cause a DCHECK
132 // because Chrome never Reshape(s) after the first one from (0,0). NB: this
133 // implies that screen size changes need to be plumbed differently. In
134 // particular, we must create the native window in the size that the hardware
135 // reports.
Reshape(const gfx::Size & size,float device_scale_factor,const gfx::ColorSpace & color_space,gfx::BufferFormat format,bool use_stencil)136 void GLOutputSurfaceBufferQueue::Reshape(const gfx::Size& size,
137                                          float device_scale_factor,
138                                          const gfx::ColorSpace& color_space,
139                                          gfx::BufferFormat format,
140                                          bool use_stencil) {
141   reshape_size_ = size;
142   use_stencil_ = use_stencil;
143   GLOutputSurface::Reshape(size, device_scale_factor, color_space, format,
144                            use_stencil);
145   DCHECK(buffer_queue_);
146   const bool may_have_freed_buffers =
147       buffer_queue_->Reshape(size, color_space, format);
148   if (may_have_freed_buffers || (stencil_buffer_ && !use_stencil)) {
149     auto* gl = context_provider_->ContextGL();
150     gl->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
151     if (stencil_buffer_) {
152       gl->FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
153                                   GL_RENDERBUFFER, 0);
154       gl->DeleteRenderbuffers(1, &stencil_buffer_);
155       stencil_buffer_ = 0u;
156     }
157 
158     // Note that |texture_target_| is initially set to 0, and so if it has not
159     // been set to a valid value, then no buffers have been allocated.
160     if (texture_target_ && may_have_freed_buffers) {
161       gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
162                                texture_target_, 0, 0);
163       for (const auto& buffer_texture : buffer_queue_textures_)
164         gl->DeleteTextures(1u, &buffer_texture.second);
165       buffer_queue_textures_.clear();
166       current_texture_ = 0u;
167       last_bound_texture_ = 0u;
168       last_bound_mailbox_.SetZero();
169     }
170   }
171 
172   texture_target_ =
173       gpu::GetBufferTextureTarget(gfx::BufferUsage::SCANOUT, format,
174                                   context_provider_->ContextCapabilities());
175 }
176 
SwapBuffers(OutputSurfaceFrame frame)177 void GLOutputSurfaceBufferQueue::SwapBuffers(OutputSurfaceFrame frame) {
178   DCHECK(buffer_queue_);
179 
180   // TODO(rjkroege): What if swap happens again before DidReceiveSwapBuffersAck
181   // then it would see the wrong size?
182   DCHECK(reshape_size_ == frame.size);
183   swap_size_ = reshape_size_;
184 
185   gfx::Rect damage_rect =
186       frame.sub_buffer_rect ? *frame.sub_buffer_rect : gfx::Rect(swap_size_);
187 
188   // If the client is currently drawing, we first end access to the
189   // corresponding shared image. Then, we can swap the buffers. That way, we
190   // know that whatever GL commands GLOutputSurface::SwapBuffers() emits can
191   // access the shared image.
192   auto* gl = context_provider_->ContextGL();
193   if (current_texture_) {
194     gl->EndSharedImageAccessDirectCHROMIUM(current_texture_);
195     gl->BindFramebuffer(GL_FRAMEBUFFER, 0u);
196     current_texture_ = 0u;
197   }
198   buffer_queue_->SwapBuffers(damage_rect);
199   GLOutputSurface::SwapBuffers(std::move(frame));
200 }
201 
GetCurrentFramebufferDamage() const202 gfx::Rect GLOutputSurfaceBufferQueue::GetCurrentFramebufferDamage() const {
203   return buffer_queue_->CurrentBufferDamage();
204 }
205 
GetFramebufferCopyTextureFormat()206 uint32_t GLOutputSurfaceBufferQueue::GetFramebufferCopyTextureFormat() {
207   return base::strict_cast<GLenum>(
208       gl::BufferFormatToGLInternalFormat(buffer_queue_->buffer_format()));
209 }
210 
IsDisplayedAsOverlayPlane() const211 bool GLOutputSurfaceBufferQueue::IsDisplayedAsOverlayPlane() const {
212   return true;
213 }
214 
GetOverlayTextureId() const215 unsigned GLOutputSurfaceBufferQueue::GetOverlayTextureId() const {
216   DCHECK(last_bound_texture_);
217   return last_bound_texture_;
218 }
219 
GetOverlayMailbox() const220 gpu::Mailbox GLOutputSurfaceBufferQueue::GetOverlayMailbox() const {
221   return last_bound_mailbox_;
222 }
223 
DidReceiveSwapBuffersAck(const gfx::SwapResponse & response)224 void GLOutputSurfaceBufferQueue::DidReceiveSwapBuffersAck(
225     const gfx::SwapResponse& response) {
226   bool force_swap = false;
227   if (response.result == gfx::SwapResult::SWAP_NAK_RECREATE_BUFFERS) {
228     // Even through the swap failed, this is a fixable error so we can pretend
229     // it succeeded to the rest of the system.
230     buffer_queue_->FreeAllSurfaces();
231 
232     // TODO(andrescj): centralize the logic that deletes the stencil buffer and
233     // the textures since we do this in multiple places.
234     auto* gl = context_provider_->ContextGL();
235     gl->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
236     if (stencil_buffer_) {
237       gl->FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
238                                   GL_RENDERBUFFER, 0);
239       gl->DeleteRenderbuffers(1, &stencil_buffer_);
240       stencil_buffer_ = 0u;
241     }
242 
243     // Reshape() must have been called before we got here, so |texture_target_|
244     // should contain a valid value.
245     DCHECK(texture_target_);
246     gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
247                              texture_target_, 0, 0);
248     for (const auto& buffer_texture : buffer_queue_textures_)
249       gl->DeleteTextures(1u, &buffer_texture.second);
250     buffer_queue_textures_.clear();
251     current_texture_ = 0u;
252     last_bound_texture_ = 0u;
253     last_bound_mailbox_.SetZero();
254 
255     force_swap = true;
256   }
257 
258   buffer_queue_->PageFlipComplete();
259   client()->DidReceiveSwapBuffersAck(response.timings);
260 
261   if (force_swap)
262     client()->SetNeedsRedrawRect(gfx::Rect(swap_size_));
263 }
264 
GenSyncToken()265 gpu::SyncToken GLOutputSurfaceBufferQueue::GenSyncToken() {
266   // This should only be called as long as the BufferQueue is alive. We cannot
267   // use |buffer_queue_| to detect this because in the dtor, |buffer_queue_|
268   // becomes nullptr before BufferQueue's dtor is called, so GenSyncToken()
269   // would be called after |buffer_queue_| is nullptr when in fact, the
270   // BufferQueue is still alive. Hence, we use |fbo_| to detect that the
271   // BufferQueue is still alive.
272   DCHECK(fbo_);
273   gpu::SyncToken sync_token;
274   context_provider_->ContextGL()->GenUnverifiedSyncTokenCHROMIUM(
275       sync_token.GetData());
276   return sync_token;
277 }
278 
SetDisplayTransformHint(gfx::OverlayTransform transform)279 void GLOutputSurfaceBufferQueue::SetDisplayTransformHint(
280     gfx::OverlayTransform transform) {
281   display_transform_ = transform;
282 
283   if (context_provider_)
284     context_provider_->ContextSupport()->SetDisplayTransform(transform);
285 }
286 
GetDisplayTransform()287 gfx::OverlayTransform GLOutputSurfaceBufferQueue::GetDisplayTransform() {
288   return display_transform_;
289 }
290 
291 }  // namespace viz
292