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