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 helper_->SendAcceleratedSurfaceRelease();
375 void TextureImageTransportSurface::CreateBackTexture() {
376 DCHECK(IsContextValid(helper_.get()));
377 // If |is_swap_buffers_pending| we are waiting for our backbuffer
378 // in the mailbox, so we shouldn't be reallocating it now.
379 DCHECK(!is_swap_buffers_pending_);
381 if (backbuffer_.get() && backbuffer_size() == current_size_)
384 VLOG(1) << "Allocating new backbuffer texture";
386 GLES2Decoder* decoder = helper_->stub()->decoder();
387 TextureManager* texture_manager =
388 decoder->GetContextGroup()->texture_manager();
389 if (!backbuffer_.get()) {
390 mailbox_manager_->GenerateMailboxName(&back_mailbox_name_);
392 glGenTextures(1, &service_id);
393 backbuffer_ = TextureRef::Create(texture_manager, 0, service_id);
394 texture_manager->SetTarget(backbuffer_.get(), GL_TEXTURE_2D);
395 Texture* texture = texture_manager->Produce(backbuffer_.get());
396 bool success = mailbox_manager_->ProduceTexture(
397 GL_TEXTURE_2D, back_mailbox_name_, texture);
402 gfx::ScopedTextureBinder texture_binder(GL_TEXTURE_2D,
403 backbuffer_->service_id());
404 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
405 current_size_.width(), current_size_.height(), 0,
406 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
407 gpu::gles2::ErrorState* error_state = decoder->GetErrorState();
408 texture_manager->SetParameter("Backbuffer",
411 GL_TEXTURE_MIN_FILTER,
413 texture_manager->SetParameter("Backbuffer",
416 GL_TEXTURE_MAG_FILTER,
418 texture_manager->SetParameter("Backbuffer",
423 texture_manager->SetParameter("Backbuffer",
428 texture_manager->SetLevelInfo(backbuffer_.get(),
432 current_size_.width(),
433 current_size_.height(),
439 DCHECK(texture_manager->CanRender(backbuffer_.get()));
443 AttachBackTextureToFBO();
446 void TextureImageTransportSurface::AttachBackTextureToFBO() {
447 DCHECK(IsContextValid(helper_.get()));
448 DCHECK(backbuffer_.get());
449 gfx::ScopedFrameBufferBinder fbo_binder(fbo_id_);
450 glFramebufferTexture2DEXT(GL_FRAMEBUFFER,
451 GL_COLOR_ATTACHMENT0,
453 backbuffer_->service_id(),
458 GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER);
459 if (status != GL_FRAMEBUFFER_COMPLETE) {
460 DLOG(FATAL) << "Framebuffer incomplete: " << status;
465 } // namespace content