Respond with QuotaExceededError when IndexedDB has no disk space on open.
[chromium-blink-merge.git] / content / common / gpu / texture_image_transport_surface.cc
blobf42780e2f89da2261b97057e1851344a8b2e9856
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"
7 #include <string>
8 #include <vector>
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;
28 namespace content {
29 namespace {
31 bool IsContextValid(ImageTransportHelper* helper) {
32 return helper->stub()->decoder()->GetGLContext()->IsCurrent(NULL) ||
33 helper->stub()->decoder()->WasContextLost();
36 } // namespace
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));
57 TextureImageTransportSurface::~TextureImageTransportSurface() {
58 DCHECK(stub_destroyed_);
59 Destroy();
62 bool TextureImageTransportSurface::Initialize() {
63 mailbox_manager_ =
64 helper_->stub()->decoder()->GetContextGroup()->mailbox_manager();
66 GpuChannelManager* manager = helper_->manager();
67 surface_ = manager->GetDefaultOffscreenSurface();
68 if (!surface_.get())
69 return false;
71 if (!helper_->Initialize())
72 return false;
74 GpuChannel* parent_channel = manager->LookupChannel(handle_.parent_client_id);
75 if (parent_channel) {
76 const CommandLine* command_line = CommandLine::ForCurrentProcess();
77 if (command_line->HasSwitch(switches::kUIPrioritizeInGpuProcess))
78 helper_->SetPreemptByFlag(parent_channel->GetPreemptionFlag());
81 return true;
84 void TextureImageTransportSurface::Destroy() {
85 if (surface_.get())
86 surface_ = NULL;
88 helper_->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
95 // Ack arrives.
96 DCHECK(!did_unschedule_);
97 if (is_swap_buffers_pending_) {
98 did_unschedule_ = true;
99 helper_->SetScheduled(false);
100 return true;
102 return false;
105 bool TextureImageTransportSurface::IsOffscreen() {
106 return true;
109 unsigned int TextureImageTransportSurface::GetBackingFrameBufferObject() {
110 DCHECK(IsContextValid(helper_.get()));
111 if (!fbo_id_) {
112 glGenFramebuffersEXT(1, &fbo_id_);
113 glBindFramebufferEXT(GL_FRAMEBUFFER, fbo_id_);
114 helper_->stub()->AddDestructionObserver(this);
115 CreateBackTexture();
118 return fbo_id_;
121 bool TextureImageTransportSurface::SetBackbufferAllocation(bool allocation) {
122 DCHECK(!is_swap_buffers_pending_);
123 if (backbuffer_suggested_allocation_ == allocation)
124 return true;
125 backbuffer_suggested_allocation_ = allocation;
127 if (backbuffer_suggested_allocation_) {
128 DCHECK(!backbuffer_.get());
129 CreateBackTexture();
130 } else {
131 ReleaseBackTexture();
134 return true;
137 void TextureImageTransportSurface::SetFrontbufferAllocation(bool allocation) {
138 if (frontbuffer_suggested_allocation_ == allocation)
139 return;
140 frontbuffer_suggested_allocation_ = allocation;
142 // If a swapbuffers is in flight, wait for the ack before releasing the front
143 // buffer:
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() {
155 return GetHandle();
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_)
173 CreateBackTexture();
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();
185 if (fbo_id_) {
186 glDeleteFramebuffersEXT(1, &fbo_id_);
187 CHECK_GL_ERROR();
188 fbo_id_ = 0;
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_)
204 return true;
206 if (!backbuffer_.get()) {
207 LOG(ERROR) << "Swap without valid backing.";
208 return true;
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_));
219 glFlush();
221 params.latency_info = latency_info_;
222 helper_->SendAcceleratedSurfaceBuffersSwapped(params);
224 DCHECK(!is_swap_buffers_pending_);
225 is_swap_buffers_pending_ = true;
226 return 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_)
234 return true;
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())
240 return true;
242 if (!backbuffer_.get()) {
243 LOG(ERROR) << "Swap without valid backing.";
244 return true;
247 DCHECK(current_size_ == backbuffer_size());
248 GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params params;
249 params.surface_size = backbuffer_size();
250 params.surface_scale_factor = scale_factor_;
251 params.x = x;
252 params.y = y;
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_));
259 glFlush();
261 params.latency_info = latency_info_;
262 helper_->SendAcceleratedSurfacePostSubBuffer(params);
264 DCHECK(!is_swap_buffers_pending_);
265 is_swap_buffers_pending_ = true;
266 return 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";
274 return extensions;
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);
293 } else {
294 helper_->manager()->sync_point_manager()->AddSyncPointCallback(
295 params.sync_point,
296 base::Bind(&TextureImageTransportSurface::BufferPresentedImpl,
297 this,
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
309 // mailbox.
310 if (stub_destroyed_)
311 return;
313 // We should not have allowed the backbuffer to be discarded while the ack
314 // was pending.
315 DCHECK(backbuffer_suggested_allocation_);
316 DCHECK(backbuffer_.get());
318 bool swap = true;
319 if (!mailbox_name.empty()) {
320 DCHECK(mailbox_name.length() == GL_MAILBOX_SIZE_CHROMIUM);
321 if (!memcmp(mailbox_name.data(),
322 &back_mailbox_name_,
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.
326 swap = false;
329 if (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_)
341 CreateBackTexture();
342 else
343 AttachBackTextureToFBO();
346 // Even if MakeCurrent fails, schedule anyway, to trigger the lost context
347 // logic.
348 if (did_unschedule_) {
349 did_unschedule_ = false;
350 helper_->SetScheduled(true);
354 void TextureImageTransportSurface::OnResizeViewACK() {
355 NOTREACHED();
358 void TextureImageTransportSurface::ReleaseBackTexture() {
359 DCHECK(IsContextValid(helper_.get()));
360 backbuffer_ = NULL;
361 back_mailbox_name_ = MailboxName();
362 glFlush();
363 CHECK_GL_ERROR();
366 void TextureImageTransportSurface::ReleaseFrontTexture() {
367 DCHECK(IsContextValid(helper_.get()));
368 frontbuffer_ = NULL;
369 front_mailbox_name_ = MailboxName();
370 glFlush();
371 CHECK_GL_ERROR();
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_)
383 return;
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_);
392 GLuint service_id;
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);
399 DCHECK(success);
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",
410 error_state,
411 backbuffer_.get(),
412 GL_TEXTURE_MIN_FILTER,
413 GL_LINEAR);
414 texture_manager->SetParameter("Backbuffer",
415 error_state,
416 backbuffer_.get(),
417 GL_TEXTURE_MAG_FILTER,
418 GL_LINEAR);
419 texture_manager->SetParameter("Backbuffer",
420 error_state,
421 backbuffer_.get(),
422 GL_TEXTURE_WRAP_S,
423 GL_CLAMP_TO_EDGE);
424 texture_manager->SetParameter("Backbuffer",
425 error_state,
426 backbuffer_.get(),
427 GL_TEXTURE_WRAP_T,
428 GL_CLAMP_TO_EDGE);
429 texture_manager->SetLevelInfo(backbuffer_.get(),
430 GL_TEXTURE_2D,
432 GL_RGBA,
433 current_size_.width(),
434 current_size_.height(),
437 GL_RGBA,
438 GL_UNSIGNED_BYTE,
439 true);
440 DCHECK(texture_manager->CanRender(backbuffer_.get()));
441 CHECK_GL_ERROR();
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,
453 GL_TEXTURE_2D,
454 backbuffer_->service_id(),
456 CHECK_GL_ERROR();
458 #ifndef NDEBUG
459 GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER);
460 if (status != GL_FRAMEBUFFER_COMPLETE) {
461 DLOG(FATAL) << "Framebuffer incomplete: " << status;
463 #endif
466 } // namespace content