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 CommandLine
* command_line
= CommandLine::ForCurrentProcess();
77 if (command_line
->HasSwitch(switches::kUIPrioritizeInGpuProcess
))
78 helper_
->SetPreemptByFlag(parent_channel
->GetPreemptionFlag());
84 void TextureImageTransportSurface::Destroy() {
91 bool TextureImageTransportSurface::DeferDraws() {
92 // The command buffer hit a draw/clear command that could clobber the
93 // texture in use by the UI compositor. If a Swap is pending, abort
94 // processing of the command by returning true and unschedule until the Swap
96 DCHECK(!did_unschedule_
);
97 if (is_swap_buffers_pending_
) {
98 did_unschedule_
= true;
99 helper_
->SetScheduled(false);
105 bool TextureImageTransportSurface::IsOffscreen() {
109 unsigned int TextureImageTransportSurface::GetBackingFrameBufferObject() {
110 DCHECK(IsContextValid(helper_
.get()));
112 glGenFramebuffersEXT(1, &fbo_id_
);
113 glBindFramebufferEXT(GL_FRAMEBUFFER
, fbo_id_
);
114 helper_
->stub()->AddDestructionObserver(this);
121 bool TextureImageTransportSurface::SetBackbufferAllocation(bool allocation
) {
122 DCHECK(!is_swap_buffers_pending_
);
123 if (backbuffer_suggested_allocation_
== allocation
)
125 backbuffer_suggested_allocation_
= allocation
;
127 if (backbuffer_suggested_allocation_
) {
128 DCHECK(!backbuffer_
.get());
131 ReleaseBackTexture();
137 void TextureImageTransportSurface::SetFrontbufferAllocation(bool allocation
) {
138 if (frontbuffer_suggested_allocation_
== allocation
)
140 frontbuffer_suggested_allocation_
= allocation
;
142 // If a swapbuffers is in flight, wait for the ack before releasing the front
144 // - we don't know yet which texture the browser will want to keep
145 // - we want to ensure we don't destroy a texture that is in flight before the
146 // browser got a reference on it.
147 if (!frontbuffer_suggested_allocation_
&&
148 !is_swap_buffers_pending_
&&
149 helper_
->MakeCurrent()) {
150 ReleaseFrontTexture();
154 void* TextureImageTransportSurface::GetShareHandle() {
158 void* TextureImageTransportSurface::GetDisplay() {
159 return surface_
.get() ? surface_
->GetDisplay() : NULL
;
162 void* TextureImageTransportSurface::GetConfig() {
163 return surface_
.get() ? surface_
->GetConfig() : NULL
;
166 void TextureImageTransportSurface::OnResize(gfx::Size size
,
167 float scale_factor
) {
168 DCHECK_GE(size
.width(), 1);
169 DCHECK_GE(size
.height(), 1);
170 current_size_
= size
;
171 scale_factor_
= scale_factor
;
172 if (backbuffer_suggested_allocation_
)
176 void TextureImageTransportSurface::OnWillDestroyStub() {
177 bool have_context
= IsContextValid(helper_
.get());
178 helper_
->stub()->RemoveDestructionObserver(this);
180 // We are losing the stub owning us, this is our last chance to clean up the
181 // resources we allocated in the stub's context.
183 ReleaseBackTexture();
184 ReleaseFrontTexture();
187 back_mailbox_
= Mailbox();
189 front_mailbox_
= Mailbox();
192 if (fbo_id_
&& have_context
) {
193 glDeleteFramebuffersEXT(1, &fbo_id_
);
198 stub_destroyed_
= true;
201 void TextureImageTransportSurface::SetLatencyInfo(
202 const std::vector
<ui::LatencyInfo
>& latency_info
) {
203 for (size_t i
= 0; i
< latency_info
.size(); i
++)
204 latency_info_
.push_back(latency_info
[i
]);
207 void TextureImageTransportSurface::WakeUpGpu() {
211 bool TextureImageTransportSurface::SwapBuffers() {
212 DCHECK(IsContextValid(helper_
.get()));
213 DCHECK(backbuffer_suggested_allocation_
);
215 if (!frontbuffer_suggested_allocation_
)
218 if (!backbuffer_
.get()) {
219 LOG(ERROR
) << "Swap without valid backing.";
223 DCHECK(backbuffer_size() == current_size_
);
224 GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params
;
225 params
.size
= backbuffer_size();
226 params
.scale_factor
= scale_factor_
;
227 params
.mailbox
= back_mailbox_
;
231 params
.latency_info
.swap(latency_info_
);
232 helper_
->SendAcceleratedSurfaceBuffersSwapped(params
);
234 DCHECK(!is_swap_buffers_pending_
);
235 is_swap_buffers_pending_
= true;
239 bool TextureImageTransportSurface::PostSubBuffer(
240 int x
, int y
, int width
, int height
) {
241 DCHECK(IsContextValid(helper_
.get()));
242 DCHECK(backbuffer_suggested_allocation_
);
243 if (!frontbuffer_suggested_allocation_
)
245 const gfx::Rect
new_damage_rect(x
, y
, width
, height
);
246 DCHECK(gfx::Rect(gfx::Point(), current_size_
).Contains(new_damage_rect
));
248 // An empty damage rect is a successful no-op.
249 if (new_damage_rect
.IsEmpty())
252 if (!backbuffer_
.get()) {
253 LOG(ERROR
) << "Swap without valid backing.";
257 DCHECK(current_size_
== backbuffer_size());
258 GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params params
;
259 params
.surface_size
= backbuffer_size();
260 params
.surface_scale_factor
= scale_factor_
;
263 params
.width
= width
;
264 params
.height
= height
;
265 params
.mailbox
= back_mailbox_
;
269 params
.latency_info
.swap(latency_info_
);
270 helper_
->SendAcceleratedSurfacePostSubBuffer(params
);
272 DCHECK(!is_swap_buffers_pending_
);
273 is_swap_buffers_pending_
= true;
277 bool TextureImageTransportSurface::SupportsPostSubBuffer() {
281 gfx::Size
TextureImageTransportSurface::GetSize() {
282 return current_size_
;
285 void* TextureImageTransportSurface::GetHandle() {
286 return surface_
.get() ? surface_
->GetHandle() : NULL
;
289 unsigned TextureImageTransportSurface::GetFormat() {
290 return surface_
.get() ? surface_
->GetFormat() : 0;
293 void TextureImageTransportSurface::OnBufferPresented(
294 const AcceleratedSurfaceMsg_BufferPresented_Params
& params
) {
295 if (params
.sync_point
== 0) {
296 BufferPresentedImpl(params
.mailbox
);
298 helper_
->manager()->sync_point_manager()->AddSyncPointCallback(
300 base::Bind(&TextureImageTransportSurface::BufferPresentedImpl
,
306 void TextureImageTransportSurface::BufferPresentedImpl(const Mailbox
& mailbox
) {
307 DCHECK(is_swap_buffers_pending_
);
308 is_swap_buffers_pending_
= false;
310 // When we wait for a sync point, we may get called back after the stub is
311 // destroyed. In that case there's no need to do anything with the returned
316 // We should not have allowed the backbuffer to be discarded while the ack
318 DCHECK(backbuffer_suggested_allocation_
);
319 DCHECK(backbuffer_
.get());
322 if (!mailbox
.IsZero()) {
323 if (mailbox
== back_mailbox_
) {
324 // The browser has skipped the frame to unblock the GPU process, waiting
325 // for one of the right size, and returned the back buffer, so don't swap.
330 std::swap(backbuffer_
, frontbuffer_
);
331 std::swap(back_mailbox_
, front_mailbox_
);
334 // We're relying on the fact that the parent context is
335 // finished with its context when it inserts the sync point that
336 // triggers this callback.
337 if (helper_
->MakeCurrent()) {
338 if (frontbuffer_
.get() && !frontbuffer_suggested_allocation_
)
339 ReleaseFrontTexture();
340 if (!backbuffer_
.get() || backbuffer_size() != current_size_
)
343 AttachBackTextureToFBO();
346 // Even if MakeCurrent fails, schedule anyway, to trigger the lost context
348 if (did_unschedule_
) {
349 did_unschedule_
= false;
350 helper_
->SetScheduled(true);
354 void TextureImageTransportSurface::ReleaseBackTexture() {
355 DCHECK(IsContextValid(helper_
.get()));
357 back_mailbox_
= Mailbox();
362 void TextureImageTransportSurface::ReleaseFrontTexture() {
363 DCHECK(IsContextValid(helper_
.get()));
365 front_mailbox_
= Mailbox();
368 helper_
->SendAcceleratedSurfaceRelease();
371 void TextureImageTransportSurface::CreateBackTexture() {
372 DCHECK(IsContextValid(helper_
.get()));
373 // If |is_swap_buffers_pending| we are waiting for our backbuffer
374 // in the mailbox, so we shouldn't be reallocating it now.
375 DCHECK(!is_swap_buffers_pending_
);
377 if (backbuffer_
.get() && backbuffer_size() == current_size_
)
380 VLOG(1) << "Allocating new backbuffer texture";
382 GLES2Decoder
* decoder
= helper_
->stub()->decoder();
383 TextureManager
* texture_manager
=
384 decoder
->GetContextGroup()->texture_manager();
385 if (!backbuffer_
.get()) {
386 back_mailbox_
= gpu::Mailbox::Generate();
388 glGenTextures(1, &service_id
);
389 backbuffer_
= TextureRef::Create(texture_manager
, 0, service_id
);
390 texture_manager
->SetTarget(backbuffer_
.get(), GL_TEXTURE_2D
);
391 Texture
* texture
= texture_manager
->Produce(backbuffer_
.get());
392 mailbox_manager_
->ProduceTexture(GL_TEXTURE_2D
, back_mailbox_
, texture
);
396 gfx::ScopedTextureBinder
texture_binder(GL_TEXTURE_2D
,
397 backbuffer_
->service_id());
398 glTexImage2D(GL_TEXTURE_2D
, 0, GL_RGBA
,
399 current_size_
.width(), current_size_
.height(), 0,
400 GL_RGBA
, GL_UNSIGNED_BYTE
, NULL
);
401 gpu::gles2::ErrorState
* error_state
= decoder
->GetErrorState();
402 texture_manager
->SetParameteri("Backbuffer",
405 GL_TEXTURE_MIN_FILTER
,
407 texture_manager
->SetParameteri("Backbuffer",
410 GL_TEXTURE_MAG_FILTER
,
412 texture_manager
->SetParameteri("Backbuffer",
417 texture_manager
->SetParameteri("Backbuffer",
422 texture_manager
->SetLevelInfo(backbuffer_
.get(),
426 current_size_
.width(),
427 current_size_
.height(),
433 DCHECK(texture_manager
->CanRender(backbuffer_
.get()));
437 AttachBackTextureToFBO();
440 void TextureImageTransportSurface::AttachBackTextureToFBO() {
441 DCHECK(IsContextValid(helper_
.get()));
442 DCHECK(backbuffer_
.get());
443 gfx::ScopedFrameBufferBinder
fbo_binder(fbo_id_
);
444 glFramebufferTexture2DEXT(GL_FRAMEBUFFER
,
445 GL_COLOR_ATTACHMENT0
,
447 backbuffer_
->service_id(),
452 GLenum status
= glCheckFramebufferStatusEXT(GL_FRAMEBUFFER
);
453 if (status
!= GL_FRAMEBUFFER_COMPLETE
) {
454 DLOG(FATAL
) << "Framebuffer incomplete: " << status
;
459 } // namespace content