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 "ui/gl/scoped_binders.h"
20 using gpu::gles2::ContextGroup
;
21 using gpu::gles2::GLES2Decoder
;
22 using gpu::gles2::MailboxManager
;
23 using gpu::gles2::MailboxName
;
24 using gpu::gles2::Texture
;
25 using gpu::gles2::TextureManager
;
26 using gpu::gles2::TextureRef
;
31 bool IsContextValid(ImageTransportHelper
* helper
) {
32 return helper
->stub()->decoder()->GetGLContext()->IsCurrent(NULL
) ||
33 helper
->stub()->decoder()->WasContextLost();
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 DCHECK(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.
182 ReleaseBackTexture();
183 ReleaseFrontTexture();
186 glDeleteFramebuffersEXT(1, &fbo_id_
);
191 stub_destroyed_
= true;
194 void TextureImageTransportSurface::SetLatencyInfo(
195 const ui::LatencyInfo
& latency_info
) {
196 latency_info_
= latency_info
;
199 bool TextureImageTransportSurface::SwapBuffers() {
200 DCHECK(IsContextValid(helper_
.get()));
201 DCHECK(backbuffer_suggested_allocation_
);
203 if (!frontbuffer_suggested_allocation_
)
206 if (!backbuffer_
.get()) {
207 LOG(ERROR
) << "Swap without valid backing.";
211 DCHECK(backbuffer_size() == current_size_
);
212 GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params
;
213 params
.size
= backbuffer_size();
214 params
.scale_factor
= scale_factor_
;
215 params
.mailbox_name
.assign(
216 reinterpret_cast<const char*>(&back_mailbox_name_
),
217 sizeof(back_mailbox_name_
));
221 params
.latency_info
= latency_info_
;
222 helper_
->SendAcceleratedSurfaceBuffersSwapped(params
);
224 DCHECK(!is_swap_buffers_pending_
);
225 is_swap_buffers_pending_
= true;
229 bool TextureImageTransportSurface::PostSubBuffer(
230 int x
, int y
, int width
, int height
) {
231 DCHECK(IsContextValid(helper_
.get()));
232 DCHECK(backbuffer_suggested_allocation_
);
233 if (!frontbuffer_suggested_allocation_
)
235 const gfx::Rect
new_damage_rect(x
, y
, width
, height
);
236 DCHECK(gfx::Rect(gfx::Point(), current_size_
).Contains(new_damage_rect
));
238 // An empty damage rect is a successful no-op.
239 if (new_damage_rect
.IsEmpty())
242 if (!backbuffer_
.get()) {
243 LOG(ERROR
) << "Swap without valid backing.";
247 DCHECK(current_size_
== backbuffer_size());
248 GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params params
;
249 params
.surface_size
= backbuffer_size();
250 params
.surface_scale_factor
= scale_factor_
;
253 params
.width
= width
;
254 params
.height
= height
;
255 params
.mailbox_name
.assign(
256 reinterpret_cast<const char*>(&back_mailbox_name_
),
257 sizeof(back_mailbox_name_
));
261 params
.latency_info
= latency_info_
;
262 helper_
->SendAcceleratedSurfacePostSubBuffer(params
);
264 DCHECK(!is_swap_buffers_pending_
);
265 is_swap_buffers_pending_
= true;
269 std::string
TextureImageTransportSurface::GetExtensions() {
270 std::string extensions
= gfx::GLSurface::GetExtensions();
271 extensions
+= extensions
.empty() ? "" : " ";
272 extensions
+= "GL_CHROMIUM_front_buffer_cached ";
273 extensions
+= "GL_CHROMIUM_post_sub_buffer";
277 gfx::Size
TextureImageTransportSurface::GetSize() {
278 return current_size_
;
281 void* TextureImageTransportSurface::GetHandle() {
282 return surface_
.get() ? surface_
->GetHandle() : NULL
;
285 unsigned TextureImageTransportSurface::GetFormat() {
286 return surface_
.get() ? surface_
->GetFormat() : 0;
289 void TextureImageTransportSurface::OnBufferPresented(
290 const AcceleratedSurfaceMsg_BufferPresented_Params
& params
) {
291 if (params
.sync_point
== 0) {
292 BufferPresentedImpl(params
.mailbox_name
);
294 helper_
->manager()->sync_point_manager()->AddSyncPointCallback(
296 base::Bind(&TextureImageTransportSurface::BufferPresentedImpl
,
298 params
.mailbox_name
));
302 void TextureImageTransportSurface::BufferPresentedImpl(
303 const std::string
& mailbox_name
) {
304 DCHECK(is_swap_buffers_pending_
);
305 is_swap_buffers_pending_
= false;
307 // When we wait for a sync point, we may get called back after the stub is
308 // destroyed. In that case there's no need to do anything with the returned
313 // We should not have allowed the backbuffer to be discarded while the ack
315 DCHECK(backbuffer_suggested_allocation_
);
316 DCHECK(backbuffer_
.get());
319 if (!mailbox_name
.empty()) {
320 DCHECK(mailbox_name
.length() == GL_MAILBOX_SIZE_CHROMIUM
);
321 if (!memcmp(mailbox_name
.data(),
323 mailbox_name
.length())) {
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_name_
, front_mailbox_name_
);
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::OnResizeViewACK() {
358 void TextureImageTransportSurface::ReleaseBackTexture() {
359 DCHECK(IsContextValid(helper_
.get()));
361 back_mailbox_name_
= MailboxName();
366 void TextureImageTransportSurface::ReleaseFrontTexture() {
367 DCHECK(IsContextValid(helper_
.get()));
369 front_mailbox_name_
= MailboxName();
372 GpuHostMsg_AcceleratedSurfaceRelease_Params params
;
373 helper_
->SendAcceleratedSurfaceRelease(params
);
376 void TextureImageTransportSurface::CreateBackTexture() {
377 DCHECK(IsContextValid(helper_
.get()));
378 // If |is_swap_buffers_pending| we are waiting for our backbuffer
379 // in the mailbox, so we shouldn't be reallocating it now.
380 DCHECK(!is_swap_buffers_pending_
);
382 if (backbuffer_
.get() && backbuffer_size() == current_size_
)
385 VLOG(1) << "Allocating new backbuffer texture";
387 GLES2Decoder
* decoder
= helper_
->stub()->decoder();
388 TextureManager
* texture_manager
=
389 decoder
->GetContextGroup()->texture_manager();
390 if (!backbuffer_
.get()) {
391 mailbox_manager_
->GenerateMailboxName(&back_mailbox_name_
);
393 glGenTextures(1, &service_id
);
394 backbuffer_
= TextureRef::Create(texture_manager
, 0, service_id
);
395 texture_manager
->SetTarget(backbuffer_
.get(), GL_TEXTURE_2D
);
396 Texture
* texture
= texture_manager
->Produce(backbuffer_
.get());
397 bool success
= mailbox_manager_
->ProduceTexture(
398 GL_TEXTURE_2D
, back_mailbox_name_
, texture
);
403 gfx::ScopedTextureBinder
texture_binder(GL_TEXTURE_2D
,
404 backbuffer_
->service_id());
405 glTexImage2D(GL_TEXTURE_2D
, 0, GL_RGBA
,
406 current_size_
.width(), current_size_
.height(), 0,
407 GL_RGBA
, GL_UNSIGNED_BYTE
, NULL
);
408 gpu::gles2::ErrorState
* error_state
= decoder
->GetErrorState();
409 texture_manager
->SetParameter("Backbuffer",
412 GL_TEXTURE_MIN_FILTER
,
414 texture_manager
->SetParameter("Backbuffer",
417 GL_TEXTURE_MAG_FILTER
,
419 texture_manager
->SetParameter("Backbuffer",
424 texture_manager
->SetParameter("Backbuffer",
429 texture_manager
->SetLevelInfo(backbuffer_
.get(),
433 current_size_
.width(),
434 current_size_
.height(),
440 DCHECK(texture_manager
->CanRender(backbuffer_
.get()));
444 AttachBackTextureToFBO();
447 void TextureImageTransportSurface::AttachBackTextureToFBO() {
448 DCHECK(IsContextValid(helper_
.get()));
449 DCHECK(backbuffer_
.get());
450 gfx::ScopedFrameBufferBinder
fbo_binder(fbo_id_
);
451 glFramebufferTexture2DEXT(GL_FRAMEBUFFER
,
452 GL_COLOR_ATTACHMENT0
,
454 backbuffer_
->service_id(),
459 GLenum status
= glCheckFramebufferStatusEXT(GL_FRAMEBUFFER
);
460 if (status
!= GL_FRAMEBUFFER_COMPLETE
) {
461 DLOG(FATAL
) << "Framebuffer incomplete: " << status
;
466 } // namespace content