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.
5 #include "content/browser/compositor/buffer_queue.h"
7 #include "content/browser/compositor/image_transport_factory.h"
8 #include "content/browser/gpu/browser_gpu_memory_buffer_manager.h"
9 #include "content/common/gpu/client/context_provider_command_buffer.h"
10 #include "content/common/gpu/client/gl_helper.h"
11 #include "gpu/GLES2/gl2extchromium.h"
12 #include "gpu/command_buffer/client/gles2_interface.h"
13 #include "gpu/command_buffer/service/image_factory.h"
14 #include "third_party/skia/include/core/SkRect.h"
15 #include "third_party/skia/include/core/SkRegion.h"
16 #include "ui/gfx/gpu_memory_buffer.h"
20 BufferQueue::BufferQueue(
21 scoped_refptr
<cc::ContextProvider
> context_provider
,
22 unsigned int texture_target
,
23 unsigned int internalformat
,
25 BrowserGpuMemoryBufferManager
* gpu_memory_buffer_manager
,
27 : context_provider_(context_provider
),
30 texture_target_(texture_target
),
31 internal_format_(internalformat
),
32 gl_helper_(gl_helper
),
33 gpu_memory_buffer_manager_(gpu_memory_buffer_manager
),
34 surface_id_(surface_id
) {}
36 BufferQueue::~BufferQueue() {
39 gpu::gles2::GLES2Interface
* gl
= context_provider_
->ContextGL();
41 gl
->DeleteFramebuffers(1, &fbo_
);
44 void BufferQueue::Initialize() {
45 gpu::gles2::GLES2Interface
* gl
= context_provider_
->ContextGL();
46 gl
->GenFramebuffers(1, &fbo_
);
49 void BufferQueue::BindFramebuffer() {
50 gpu::gles2::GLES2Interface
* gl
= context_provider_
->ContextGL();
51 gl
->BindFramebuffer(GL_FRAMEBUFFER
, fbo_
);
53 if (!current_surface_
.texture
) {
54 current_surface_
= GetNextSurface();
55 gl
->FramebufferTexture2D(GL_FRAMEBUFFER
, GL_COLOR_ATTACHMENT0
,
56 texture_target_
, current_surface_
.texture
, 0);
60 void BufferQueue::CopyBufferDamage(int texture
,
62 const gfx::Rect
& new_damage
,
63 const gfx::Rect
& old_damage
) {
64 gl_helper_
->CopySubBufferDamage(
65 texture_target_
, texture
, source_texture
,
66 SkRegion(SkIRect::MakeXYWH(new_damage
.x(), new_damage
.y(),
67 new_damage
.width(), new_damage
.height())),
68 SkRegion(SkIRect::MakeXYWH(old_damage
.x(), old_damage
.y(),
69 old_damage
.width(), old_damage
.height())));
72 void BufferQueue::UpdateBufferDamage(const gfx::Rect
& damage
) {
73 displayed_surface_
.damage
.Union(damage
);
74 for (size_t i
= 0; i
< available_surfaces_
.size(); i
++)
75 available_surfaces_
[i
].damage
.Union(damage
);
77 for (std::deque
<AllocatedSurface
>::iterator it
=
78 in_flight_surfaces_
.begin();
79 it
!= in_flight_surfaces_
.end();
81 it
->damage
.Union(damage
);
84 void BufferQueue::SwapBuffers(const gfx::Rect
& damage
) {
85 if (damage
!= gfx::Rect(size_
)) {
86 // We must have a frame available to copy from.
87 DCHECK(!in_flight_surfaces_
.empty() || displayed_surface_
.texture
);
88 unsigned int texture_id
= !in_flight_surfaces_
.empty()
89 ? in_flight_surfaces_
.back().texture
90 : displayed_surface_
.texture
;
92 CopyBufferDamage(current_surface_
.texture
, texture_id
, damage
,
93 current_surface_
.damage
);
95 UpdateBufferDamage(damage
);
96 current_surface_
.damage
= gfx::Rect();
97 in_flight_surfaces_
.push_back(current_surface_
);
98 current_surface_
.texture
= 0;
99 current_surface_
.image
= 0;
100 // Some things reset the framebuffer (CopySubBufferDamage, some GLRenderer
101 // paths), so ensure we restore it here.
102 context_provider_
->ContextGL()->BindFramebuffer(GL_FRAMEBUFFER
, fbo_
);
105 void BufferQueue::Reshape(const gfx::Size
& size
, float scale_factor
) {
108 // TODO(ccameron): This assert is being hit on Mac try jobs. Determine if that
109 // is cause for concern or if it is benign.
110 // http://crbug.com/524624
111 #if !defined(OS_MACOSX)
112 DCHECK(!current_surface_
.texture
);
116 // TODO: add stencil buffer when needed.
117 gpu::gles2::GLES2Interface
* gl
= context_provider_
->ContextGL();
118 gl
->BindFramebuffer(GL_FRAMEBUFFER
, fbo_
);
119 gl
->FramebufferTexture2D(GL_FRAMEBUFFER
, GL_COLOR_ATTACHMENT0
,
120 texture_target_
, 0, 0);
125 void BufferQueue::RecreateBuffers() {
126 // We need to recreate the buffers, for whatever reason the old ones are not
127 // presentable on the device anymore.
128 // Unused buffers can be freed directly, they will be re-allocated as needed.
129 // Any in flight, current or displayed surface must be replaced.
130 for (size_t i
= 0; i
< available_surfaces_
.size(); i
++)
131 FreeSurface(&available_surfaces_
[i
]);
132 available_surfaces_
.clear();
134 for (auto& surface
: in_flight_surfaces_
)
135 surface
= RecreateBuffer(&surface
);
137 current_surface_
= RecreateBuffer(¤t_surface_
);
138 displayed_surface_
= RecreateBuffer(&displayed_surface_
);
140 if (current_surface_
.texture
) {
141 // If we have a texture bound, we will need to re-bind it.
142 gpu::gles2::GLES2Interface
* gl
= context_provider_
->ContextGL();
143 gl
->BindFramebuffer(GL_FRAMEBUFFER
, fbo_
);
144 gl
->FramebufferTexture2D(GL_FRAMEBUFFER
, GL_COLOR_ATTACHMENT0
,
145 texture_target_
, current_surface_
.texture
, 0);
149 BufferQueue::AllocatedSurface
BufferQueue::RecreateBuffer(
150 AllocatedSurface
* surface
) {
151 if (!surface
->texture
)
153 AllocatedSurface new_surface
= GetNextSurface();
154 new_surface
.damage
= surface
->damage
;
156 // Copy the entire texture.
157 CopyBufferDamage(new_surface
.texture
, surface
->texture
, gfx::Rect(),
160 FreeSurface(surface
);
164 void BufferQueue::PageFlipComplete() {
165 DCHECK(!in_flight_surfaces_
.empty());
167 if (displayed_surface_
.texture
)
168 available_surfaces_
.push_back(displayed_surface_
);
170 displayed_surface_
= in_flight_surfaces_
.front();
171 in_flight_surfaces_
.pop_front();
174 void BufferQueue::FreeAllSurfaces() {
175 FreeSurface(&displayed_surface_
);
176 FreeSurface(¤t_surface_
);
177 // This is intentionally not emptied since the swap buffers acks are still
178 // expected to arrive.
179 for (auto& surface
: in_flight_surfaces_
)
180 FreeSurface(&surface
);
182 for (size_t i
= 0; i
< available_surfaces_
.size(); i
++)
183 FreeSurface(&available_surfaces_
[i
]);
185 available_surfaces_
.clear();
188 void BufferQueue::FreeSurface(AllocatedSurface
* surface
) {
189 if (surface
->texture
) {
190 gpu::gles2::GLES2Interface
* gl
= context_provider_
->ContextGL();
191 gl
->BindTexture(texture_target_
, surface
->texture
);
192 gl
->ReleaseTexImage2DCHROMIUM(texture_target_
, surface
->image
);
193 gl
->DeleteTextures(1, &surface
->texture
);
194 gl
->DestroyImageCHROMIUM(surface
->image
);
196 surface
->texture
= 0;
201 BufferQueue::AllocatedSurface
BufferQueue::GetNextSurface() {
202 if (!available_surfaces_
.empty()) {
203 AllocatedSurface surface
= available_surfaces_
.back();
204 available_surfaces_
.pop_back();
208 unsigned int texture
= 0;
209 gpu::gles2::GLES2Interface
* gl
= context_provider_
->ContextGL();
210 gl
->GenTextures(1, &texture
);
212 return AllocatedSurface();
214 // We don't want to allow anything more than triple buffering.
215 DCHECK_LT(allocated_count_
, 4U);
217 scoped_ptr
<gfx::GpuMemoryBuffer
> buffer(
218 gpu_memory_buffer_manager_
->AllocateGpuMemoryBufferForScanout(
219 size_
, gpu::ImageFactory::DefaultBufferFormatForImageFormat(
223 gl
->DeleteTextures(1, &texture
);
224 DLOG(ERROR
) << "Failed to allocate GPU memory buffer";
225 return AllocatedSurface();
228 unsigned int id
= gl
->CreateImageCHROMIUM(
229 buffer
->AsClientBuffer(), size_
.width(), size_
.height(),
233 LOG(ERROR
) << "Failed to allocate backing image surface";
234 gl
->DeleteTextures(1, &texture
);
235 return AllocatedSurface();
238 gl
->BindTexture(texture_target_
, texture
);
239 gl
->BindTexImage2DCHROMIUM(texture_target_
, id
);
240 return AllocatedSurface(texture
, id
, gfx::Rect(size_
));
243 } // namespace content