1 // Copyright (c) 2012 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/common/gpu/texture_image_transport_surface.h"
10 #include "base/command_line.h"
11 #include "content/common/gpu/gpu_channel.h"
12 #include "content/common/gpu/gpu_channel_manager.h"
13 #include "content/common/gpu/gpu_messages.h"
14 #include "content/common/gpu/sync_point_manager.h"
15 #include "content/public/common/content_switches.h"
16 #include "gpu/command_buffer/service/context_group.h"
17 #include "gpu/command_buffer/service/gpu_scheduler.h"
18 #include "gpu/command_buffer/service/mailbox_manager.h"
19 #include "ui/gl/scoped_binders.h"
21 using gpu::gles2::ContextGroup
;
22 using gpu::gles2::GLES2Decoder
;
23 using gpu::gles2::MailboxManager
;
24 using gpu::gles2::Texture
;
25 using gpu::gles2::TextureManager
;
26 using gpu::gles2::TextureRef
;
32 bool IsContextValid(ImageTransportHelper
* helper
) {
33 return helper
->stub()->decoder()->GetGLContext()->IsCurrent(NULL
);
38 TextureImageTransportSurface::TextureImageTransportSurface(
39 GpuChannelManager
* manager
,
40 GpuCommandBufferStub
* stub
,
41 const gfx::GLSurfaceHandle
& handle
)
45 stub_destroyed_(false),
46 backbuffer_suggested_allocation_(true),
47 frontbuffer_suggested_allocation_(true),
49 is_swap_buffers_pending_(false),
50 did_unschedule_(false) {
51 helper_
.reset(new ImageTransportHelper(this,
54 gfx::kNullPluginWindow
));
57 TextureImageTransportSurface::~TextureImageTransportSurface() {
58 DCHECK(stub_destroyed_
);
62 bool TextureImageTransportSurface::Initialize() {
64 helper_
->stub()->decoder()->GetContextGroup()->mailbox_manager();
66 GpuChannelManager
* manager
= helper_
->manager();
67 surface_
= manager
->GetDefaultOffscreenSurface();
71 if (!helper_
->Initialize())
74 GpuChannel
* parent_channel
= manager
->LookupChannel(handle_
.parent_client_id
);
76 const base::CommandLine
* command_line
=
77 base::CommandLine::ForCurrentProcess();
78 if (command_line
->HasSwitch(switches::kUIPrioritizeInGpuProcess
))
79 helper_
->SetPreemptByFlag(parent_channel
->GetPreemptionFlag());
85 void TextureImageTransportSurface::Destroy() {
92 bool TextureImageTransportSurface::DeferDraws() {
93 // The command buffer hit a draw/clear command that could clobber the
94 // texture in use by the UI compositor. If a Swap is pending, abort
95 // processing of the command by returning true and unschedule until the Swap
97 DCHECK(!did_unschedule_
);
98 if (is_swap_buffers_pending_
) {
99 did_unschedule_
= true;
100 helper_
->SetScheduled(false);
106 bool TextureImageTransportSurface::IsOffscreen() {
110 unsigned int TextureImageTransportSurface::GetBackingFrameBufferObject() {
111 DCHECK(IsContextValid(helper_
.get()));
113 glGenFramebuffersEXT(1, &fbo_id_
);
114 glBindFramebufferEXT(GL_FRAMEBUFFER
, fbo_id_
);
115 helper_
->stub()->AddDestructionObserver(this);
122 bool TextureImageTransportSurface::SetBackbufferAllocation(bool allocation
) {
123 DCHECK(!is_swap_buffers_pending_
);
124 if (backbuffer_suggested_allocation_
== allocation
)
126 backbuffer_suggested_allocation_
= allocation
;
128 if (backbuffer_suggested_allocation_
) {
129 DCHECK(!backbuffer_
.get());
132 ReleaseBackTexture();
138 void TextureImageTransportSurface::SetFrontbufferAllocation(bool allocation
) {
139 if (frontbuffer_suggested_allocation_
== allocation
)
141 frontbuffer_suggested_allocation_
= allocation
;
143 // If a swapbuffers is in flight, wait for the ack before releasing the front
145 // - we don't know yet which texture the browser will want to keep
146 // - we want to ensure we don't destroy a texture that is in flight before the
147 // browser got a reference on it.
148 if (!frontbuffer_suggested_allocation_
&&
149 !is_swap_buffers_pending_
&&
150 helper_
->MakeCurrent()) {
151 ReleaseFrontTexture();
155 void* TextureImageTransportSurface::GetShareHandle() {
159 void* TextureImageTransportSurface::GetDisplay() {
160 return surface_
.get() ? surface_
->GetDisplay() : NULL
;
163 void* TextureImageTransportSurface::GetConfig() {
164 return surface_
.get() ? surface_
->GetConfig() : NULL
;
167 void TextureImageTransportSurface::OnResize(gfx::Size size
,
168 float scale_factor
) {
169 DCHECK_GE(size
.width(), 1);
170 DCHECK_GE(size
.height(), 1);
171 current_size_
= size
;
172 scale_factor_
= scale_factor
;
173 if (backbuffer_suggested_allocation_
)
177 void TextureImageTransportSurface::OnWillDestroyStub() {
178 bool have_context
= IsContextValid(helper_
.get());
179 helper_
->stub()->RemoveDestructionObserver(this);
181 // We are losing the stub owning us, this is our last chance to clean up the
182 // resources we allocated in the stub's context.
184 ReleaseBackTexture();
185 ReleaseFrontTexture();
188 back_mailbox_
= Mailbox();
190 front_mailbox_
= Mailbox();
193 if (fbo_id_
&& have_context
) {
194 glDeleteFramebuffersEXT(1, &fbo_id_
);
199 stub_destroyed_
= true;
202 void TextureImageTransportSurface::SetLatencyInfo(
203 const std::vector
<ui::LatencyInfo
>& latency_info
) {
204 for (size_t i
= 0; i
< latency_info
.size(); i
++)
205 latency_info_
.push_back(latency_info
[i
]);
208 void TextureImageTransportSurface::WakeUpGpu() {
212 bool TextureImageTransportSurface::SwapBuffers() {
213 DCHECK(IsContextValid(helper_
.get()));
214 DCHECK(backbuffer_suggested_allocation_
);
216 if (!frontbuffer_suggested_allocation_
)
219 if (!backbuffer_
.get()) {
220 LOG(ERROR
) << "Swap without valid backing.";
224 DCHECK(backbuffer_size() == current_size_
);
225 GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params
;
226 params
.size
= backbuffer_size();
227 params
.scale_factor
= scale_factor_
;
228 params
.mailbox
= back_mailbox_
;
232 params
.latency_info
.swap(latency_info_
);
233 helper_
->SendAcceleratedSurfaceBuffersSwapped(params
);
235 DCHECK(!is_swap_buffers_pending_
);
236 is_swap_buffers_pending_
= true;
240 bool TextureImageTransportSurface::PostSubBuffer(
241 int x
, int y
, int width
, int height
) {
242 DCHECK(IsContextValid(helper_
.get()));
243 DCHECK(backbuffer_suggested_allocation_
);
244 if (!frontbuffer_suggested_allocation_
)
246 const gfx::Rect
new_damage_rect(x
, y
, width
, height
);
247 DCHECK(gfx::Rect(gfx::Point(), current_size_
).Contains(new_damage_rect
));
249 // An empty damage rect is a successful no-op.
250 if (new_damage_rect
.IsEmpty())
253 if (!backbuffer_
.get()) {
254 LOG(ERROR
) << "Swap without valid backing.";
258 DCHECK(current_size_
== backbuffer_size());
259 GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params params
;
260 params
.surface_size
= backbuffer_size();
261 params
.surface_scale_factor
= scale_factor_
;
264 params
.width
= width
;
265 params
.height
= height
;
266 params
.mailbox
= back_mailbox_
;
270 params
.latency_info
.swap(latency_info_
);
271 helper_
->SendAcceleratedSurfacePostSubBuffer(params
);
273 DCHECK(!is_swap_buffers_pending_
);
274 is_swap_buffers_pending_
= true;
278 bool TextureImageTransportSurface::SupportsPostSubBuffer() {
282 gfx::Size
TextureImageTransportSurface::GetSize() {
283 return current_size_
;
286 void* TextureImageTransportSurface::GetHandle() {
287 return surface_
.get() ? surface_
->GetHandle() : NULL
;
290 unsigned TextureImageTransportSurface::GetFormat() {
291 return surface_
.get() ? surface_
->GetFormat() : 0;
294 void TextureImageTransportSurface::OnBufferPresented(
295 const AcceleratedSurfaceMsg_BufferPresented_Params
& params
) {
296 if (params
.sync_point
== 0) {
297 BufferPresentedImpl(params
.mailbox
);
299 helper_
->manager()->sync_point_manager()->AddSyncPointCallback(
301 base::Bind(&TextureImageTransportSurface::BufferPresentedImpl
,
307 void TextureImageTransportSurface::BufferPresentedImpl(const Mailbox
& mailbox
) {
308 DCHECK(is_swap_buffers_pending_
);
309 is_swap_buffers_pending_
= false;
311 // When we wait for a sync point, we may get called back after the stub is
312 // destroyed. In that case there's no need to do anything with the returned
317 // We should not have allowed the backbuffer to be discarded while the ack
319 DCHECK(backbuffer_suggested_allocation_
);
320 DCHECK(backbuffer_
.get());
323 if (!mailbox
.IsZero()) {
324 if (mailbox
== back_mailbox_
) {
325 // The browser has skipped the frame to unblock the GPU process, waiting
326 // for one of the right size, and returned the back buffer, so don't swap.
331 std::swap(backbuffer_
, frontbuffer_
);
332 std::swap(back_mailbox_
, front_mailbox_
);
335 // We're relying on the fact that the parent context is
336 // finished with its context when it inserts the sync point that
337 // triggers this callback.
338 if (helper_
->MakeCurrent()) {
339 if (frontbuffer_
.get() && !frontbuffer_suggested_allocation_
)
340 ReleaseFrontTexture();
341 if (!backbuffer_
.get() || backbuffer_size() != current_size_
)
344 AttachBackTextureToFBO();
347 // Even if MakeCurrent fails, schedule anyway, to trigger the lost context
349 if (did_unschedule_
) {
350 did_unschedule_
= false;
351 helper_
->SetScheduled(true);
355 void TextureImageTransportSurface::ReleaseBackTexture() {
356 DCHECK(IsContextValid(helper_
.get()));
358 back_mailbox_
= Mailbox();
363 void TextureImageTransportSurface::ReleaseFrontTexture() {
364 DCHECK(IsContextValid(helper_
.get()));
366 front_mailbox_
= Mailbox();
369 helper_
->SendAcceleratedSurfaceRelease();
372 void TextureImageTransportSurface::CreateBackTexture() {
373 DCHECK(IsContextValid(helper_
.get()));
374 // If |is_swap_buffers_pending| we are waiting for our backbuffer
375 // in the mailbox, so we shouldn't be reallocating it now.
376 DCHECK(!is_swap_buffers_pending_
);
378 if (backbuffer_
.get() && backbuffer_size() == current_size_
)
381 VLOG(1) << "Allocating new backbuffer texture";
383 GLES2Decoder
* decoder
= helper_
->stub()->decoder();
384 TextureManager
* texture_manager
=
385 decoder
->GetContextGroup()->texture_manager();
386 if (!backbuffer_
.get()) {
387 back_mailbox_
= gpu::Mailbox::Generate();
389 glGenTextures(1, &service_id
);
390 backbuffer_
= TextureRef::Create(texture_manager
, 0, service_id
);
391 texture_manager
->SetTarget(backbuffer_
.get(), GL_TEXTURE_2D
);
392 Texture
* texture
= texture_manager
->Produce(backbuffer_
.get());
393 mailbox_manager_
->ProduceTexture(GL_TEXTURE_2D
, back_mailbox_
, texture
);
397 gfx::ScopedTextureBinder
texture_binder(GL_TEXTURE_2D
,
398 backbuffer_
->service_id());
399 glTexImage2D(GL_TEXTURE_2D
, 0, GL_RGBA
,
400 current_size_
.width(), current_size_
.height(), 0,
401 GL_RGBA
, GL_UNSIGNED_BYTE
, NULL
);
402 gpu::gles2::ErrorState
* error_state
= decoder
->GetErrorState();
403 texture_manager
->SetParameteri("Backbuffer",
406 GL_TEXTURE_MIN_FILTER
,
408 texture_manager
->SetParameteri("Backbuffer",
411 GL_TEXTURE_MAG_FILTER
,
413 texture_manager
->SetParameteri("Backbuffer",
418 texture_manager
->SetParameteri("Backbuffer",
423 texture_manager
->SetLevelInfo(backbuffer_
.get(),
427 current_size_
.width(),
428 current_size_
.height(),
434 DCHECK(texture_manager
->CanRender(backbuffer_
.get()));
438 AttachBackTextureToFBO();
441 void TextureImageTransportSurface::AttachBackTextureToFBO() {
442 DCHECK(IsContextValid(helper_
.get()));
443 DCHECK(backbuffer_
.get());
444 gfx::ScopedFrameBufferBinder
fbo_binder(fbo_id_
);
445 glFramebufferTexture2DEXT(GL_FRAMEBUFFER
,
446 GL_COLOR_ATTACHMENT0
,
448 backbuffer_
->service_id(),
453 GLenum status
= glCheckFramebufferStatusEXT(GL_FRAMEBUFFER
);
454 if (status
!= GL_FRAMEBUFFER_COMPLETE
) {
455 DLOG(FATAL
) << "Framebuffer incomplete: " << status
;
460 } // namespace content