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.
4
5 #include "content/common/gpu/texture_image_transport_surface.h"
6
7 #include <string>
8 #include <vector>
9
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"
20
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;
27 using gpu::Mailbox;
28
29 namespace content {
30 namespace {
31
IsContextValid(ImageTransportHelper * helper)32 bool IsContextValid(ImageTransportHelper* helper) {
33 return helper->stub()->decoder()->GetGLContext()->IsCurrent(NULL);
34 }
35
36 } // namespace
37
TextureImageTransportSurface(GpuChannelManager * manager,GpuCommandBufferStub * stub,const gfx::GLSurfaceHandle & handle)38 TextureImageTransportSurface::TextureImageTransportSurface(
39 GpuChannelManager* manager,
40 GpuCommandBufferStub* stub,
41 const gfx::GLSurfaceHandle& handle)
42 : fbo_id_(0),
43 current_size_(1, 1),
44 scale_factor_(1.f),
45 stub_destroyed_(false),
46 backbuffer_suggested_allocation_(true),
47 frontbuffer_suggested_allocation_(true),
48 handle_(handle),
49 is_swap_buffers_pending_(false),
50 did_unschedule_(false) {
51 helper_.reset(new ImageTransportHelper(this,
52 manager,
53 stub,
54 gfx::kNullPluginWindow));
55 }
56
~TextureImageTransportSurface()57 TextureImageTransportSurface::~TextureImageTransportSurface() {
58 DCHECK(stub_destroyed_);
59 Destroy();
60 }
61
Initialize()62 bool TextureImageTransportSurface::Initialize() {
63 mailbox_manager_ =
64 helper_->stub()->decoder()->GetContextGroup()->mailbox_manager();
65
66 GpuChannelManager* manager = helper_->manager();
67 surface_ = manager->GetDefaultOffscreenSurface();
68 if (!surface_.get())
69 return false;
70
71 if (!helper_->Initialize())
72 return false;
73
74 GpuChannel* parent_channel = manager->LookupChannel(handle_.parent_client_id);
75 if (parent_channel) {
76 const base::CommandLine* command_line =
77 base::CommandLine::ForCurrentProcess();
78 if (command_line->HasSwitch(switches::kUIPrioritizeInGpuProcess))
79 helper_->SetPreemptByFlag(parent_channel->GetPreemptionFlag());
80 }
81
82 return true;
83 }
84
Destroy()85 void TextureImageTransportSurface::Destroy() {
86 if (surface_.get())
87 surface_ = NULL;
88
89 helper_->Destroy();
90 }
91
DeferDraws()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
96 // Ack arrives.
97 DCHECK(!did_unschedule_);
98 if (is_swap_buffers_pending_) {
99 did_unschedule_ = true;
100 helper_->SetScheduled(false);
101 return true;
102 }
103 return false;
104 }
105
IsOffscreen()106 bool TextureImageTransportSurface::IsOffscreen() {
107 return true;
108 }
109
GetBackingFrameBufferObject()110 unsigned int TextureImageTransportSurface::GetBackingFrameBufferObject() {
111 DCHECK(IsContextValid(helper_.get()));
112 if (!fbo_id_) {
113 glGenFramebuffersEXT(1, &fbo_id_);
114 glBindFramebufferEXT(GL_FRAMEBUFFER, fbo_id_);
115 helper_->stub()->AddDestructionObserver(this);
116 CreateBackTexture();
117 }
118
119 return fbo_id_;
120 }
121
SetBackbufferAllocation(bool allocation)122 bool TextureImageTransportSurface::SetBackbufferAllocation(bool allocation) {
123 DCHECK(!is_swap_buffers_pending_);
124 if (backbuffer_suggested_allocation_ == allocation)
125 return true;
126 backbuffer_suggested_allocation_ = allocation;
127
128 if (backbuffer_suggested_allocation_) {
129 DCHECK(!backbuffer_.get());
130 CreateBackTexture();
131 } else {
132 ReleaseBackTexture();
133 }
134
135 return true;
136 }
137
SetFrontbufferAllocation(bool allocation)138 void TextureImageTransportSurface::SetFrontbufferAllocation(bool allocation) {
139 if (frontbuffer_suggested_allocation_ == allocation)
140 return;
141 frontbuffer_suggested_allocation_ = allocation;
142
143 // If a swapbuffers is in flight, wait for the ack before releasing the front
144 // buffer:
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();
152 }
153 }
154
GetShareHandle()155 void* TextureImageTransportSurface::GetShareHandle() {
156 return GetHandle();
157 }
158
GetDisplay()159 void* TextureImageTransportSurface::GetDisplay() {
160 return surface_.get() ? surface_->GetDisplay() : NULL;
161 }
162
GetConfig()163 void* TextureImageTransportSurface::GetConfig() {
164 return surface_.get() ? surface_->GetConfig() : NULL;
165 }
166
OnResize(gfx::Size size,float scale_factor)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_)
174 CreateBackTexture();
175 }
176
OnWillDestroyStub()177 void TextureImageTransportSurface::OnWillDestroyStub() {
178 bool have_context = IsContextValid(helper_.get());
179 helper_->stub()->RemoveDestructionObserver(this);
180
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.
183 if (have_context) {
184 ReleaseBackTexture();
185 ReleaseFrontTexture();
186 } else {
187 backbuffer_ = NULL;
188 back_mailbox_ = Mailbox();
189 frontbuffer_ = NULL;
190 front_mailbox_ = Mailbox();
191 }
192
193 if (fbo_id_ && have_context) {
194 glDeleteFramebuffersEXT(1, &fbo_id_);
195 CHECK_GL_ERROR();
196 }
197 fbo_id_ = 0;
198
199 stub_destroyed_ = true;
200 }
201
SetLatencyInfo(const std::vector<ui::LatencyInfo> & latency_info)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]);
206 }
207
WakeUpGpu()208 void TextureImageTransportSurface::WakeUpGpu() {
209 NOTIMPLEMENTED();
210 }
211
SwapBuffers()212 bool TextureImageTransportSurface::SwapBuffers() {
213 DCHECK(IsContextValid(helper_.get()));
214 DCHECK(backbuffer_suggested_allocation_);
215
216 if (!frontbuffer_suggested_allocation_)
217 return true;
218
219 if (!backbuffer_.get()) {
220 LOG(ERROR) << "Swap without valid backing.";
221 return true;
222 }
223
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_;
229
230 glFlush();
231
232 params.latency_info.swap(latency_info_);
233 helper_->SendAcceleratedSurfaceBuffersSwapped(params);
234
235 DCHECK(!is_swap_buffers_pending_);
236 is_swap_buffers_pending_ = true;
237 return true;
238 }
239
PostSubBuffer(int x,int y,int width,int height)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_)
245 return true;
246 const gfx::Rect new_damage_rect(x, y, width, height);
247 DCHECK(gfx::Rect(gfx::Point(), current_size_).Contains(new_damage_rect));
248
249 // An empty damage rect is a successful no-op.
250 if (new_damage_rect.IsEmpty())
251 return true;
252
253 if (!backbuffer_.get()) {
254 LOG(ERROR) << "Swap without valid backing.";
255 return true;
256 }
257
258 DCHECK(current_size_ == backbuffer_size());
259 GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params params;
260 params.surface_size = backbuffer_size();
261 params.surface_scale_factor = scale_factor_;
262 params.x = x;
263 params.y = y;
264 params.width = width;
265 params.height = height;
266 params.mailbox = back_mailbox_;
267
268 glFlush();
269
270 params.latency_info.swap(latency_info_);
271 helper_->SendAcceleratedSurfacePostSubBuffer(params);
272
273 DCHECK(!is_swap_buffers_pending_);
274 is_swap_buffers_pending_ = true;
275 return true;
276 }
277
SupportsPostSubBuffer()278 bool TextureImageTransportSurface::SupportsPostSubBuffer() {
279 return true;
280 }
281
GetSize()282 gfx::Size TextureImageTransportSurface::GetSize() {
283 return current_size_;
284 }
285
GetHandle()286 void* TextureImageTransportSurface::GetHandle() {
287 return surface_.get() ? surface_->GetHandle() : NULL;
288 }
289
GetFormat()290 unsigned TextureImageTransportSurface::GetFormat() {
291 return surface_.get() ? surface_->GetFormat() : 0;
292 }
293
OnBufferPresented(const AcceleratedSurfaceMsg_BufferPresented_Params & params)294 void TextureImageTransportSurface::OnBufferPresented(
295 const AcceleratedSurfaceMsg_BufferPresented_Params& params) {
296 if (params.sync_point == 0) {
297 BufferPresentedImpl(params.mailbox);
298 } else {
299 helper_->manager()->sync_point_manager()->AddSyncPointCallback(
300 params.sync_point,
301 base::Bind(&TextureImageTransportSurface::BufferPresentedImpl,
302 this,
303 params.mailbox));
304 }
305 }
306
BufferPresentedImpl(const Mailbox & mailbox)307 void TextureImageTransportSurface::BufferPresentedImpl(const Mailbox& mailbox) {
308 DCHECK(is_swap_buffers_pending_);
309 is_swap_buffers_pending_ = false;
310
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
313 // mailbox.
314 if (stub_destroyed_)
315 return;
316
317 // We should not have allowed the backbuffer to be discarded while the ack
318 // was pending.
319 DCHECK(backbuffer_suggested_allocation_);
320 DCHECK(backbuffer_.get());
321
322 bool swap = true;
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.
327 swap = false;
328 }
329 }
330 if (swap) {
331 std::swap(backbuffer_, frontbuffer_);
332 std::swap(back_mailbox_, front_mailbox_);
333 }
334
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_)
342 CreateBackTexture();
343 else
344 AttachBackTextureToFBO();
345 }
346
347 // Even if MakeCurrent fails, schedule anyway, to trigger the lost context
348 // logic.
349 if (did_unschedule_) {
350 did_unschedule_ = false;
351 helper_->SetScheduled(true);
352 }
353 }
354
ReleaseBackTexture()355 void TextureImageTransportSurface::ReleaseBackTexture() {
356 DCHECK(IsContextValid(helper_.get()));
357 backbuffer_ = NULL;
358 back_mailbox_ = Mailbox();
359 glFlush();
360 CHECK_GL_ERROR();
361 }
362
ReleaseFrontTexture()363 void TextureImageTransportSurface::ReleaseFrontTexture() {
364 DCHECK(IsContextValid(helper_.get()));
365 frontbuffer_ = NULL;
366 front_mailbox_ = Mailbox();
367 glFlush();
368 CHECK_GL_ERROR();
369 helper_->SendAcceleratedSurfaceRelease();
370 }
371
CreateBackTexture()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_);
377
378 if (backbuffer_.get() && backbuffer_size() == current_size_)
379 return;
380
381 VLOG(1) << "Allocating new backbuffer texture";
382
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();
388 GLuint service_id;
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);
394 }
395
396 {
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",
404 error_state,
405 backbuffer_.get(),
406 GL_TEXTURE_MIN_FILTER,
407 GL_LINEAR);
408 texture_manager->SetParameteri("Backbuffer",
409 error_state,
410 backbuffer_.get(),
411 GL_TEXTURE_MAG_FILTER,
412 GL_LINEAR);
413 texture_manager->SetParameteri("Backbuffer",
414 error_state,
415 backbuffer_.get(),
416 GL_TEXTURE_WRAP_S,
417 GL_CLAMP_TO_EDGE);
418 texture_manager->SetParameteri("Backbuffer",
419 error_state,
420 backbuffer_.get(),
421 GL_TEXTURE_WRAP_T,
422 GL_CLAMP_TO_EDGE);
423 texture_manager->SetLevelInfo(backbuffer_.get(),
424 GL_TEXTURE_2D,
425 0,
426 GL_RGBA,
427 current_size_.width(),
428 current_size_.height(),
429 1,
430 0,
431 GL_RGBA,
432 GL_UNSIGNED_BYTE,
433 true);
434 DCHECK(texture_manager->CanRender(backbuffer_.get()));
435 CHECK_GL_ERROR();
436 }
437
438 AttachBackTextureToFBO();
439 }
440
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,
447 GL_TEXTURE_2D,
448 backbuffer_->service_id(),
449 0);
450 CHECK_GL_ERROR();
451
452 #ifndef NDEBUG
453 GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER);
454 if (status != GL_FRAMEBUFFER_COMPLETE) {
455 DLOG(FATAL) << "Framebuffer incomplete: " << status;
456 }
457 #endif
458 }
459
460 } // namespace content
461