OLD | NEW |
| (Empty) |
1 // Copyright 2014 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/image_transport_surface_fbo_mac.h" | |
6 | |
7 #include "content/common/gpu/gpu_messages.h" | |
8 #include "content/common/gpu/image_transport_surface_iosurface_mac.h" | |
9 #include "ui/gfx/native_widget_types.h" | |
10 #include "ui/gl/gl_context.h" | |
11 #include "ui/gl/gl_implementation.h" | |
12 #include "ui/gl/gl_surface_osmesa.h" | |
13 | |
14 namespace content { | |
15 | |
16 ImageTransportSurfaceFBO::ImageTransportSurfaceFBO( | |
17 StorageProvider* storage_provider, | |
18 GpuChannelManager* manager, | |
19 GpuCommandBufferStub* stub, | |
20 gfx::PluginWindowHandle handle) | |
21 : storage_provider_(storage_provider), | |
22 backbuffer_suggested_allocation_(true), | |
23 frontbuffer_suggested_allocation_(true), | |
24 fbo_id_(0), | |
25 texture_id_(0), | |
26 depth_stencil_renderbuffer_id_(0), | |
27 has_complete_framebuffer_(false), | |
28 context_(NULL), | |
29 scale_factor_(1.f), | |
30 made_current_(false), | |
31 is_swap_buffers_pending_(false), | |
32 did_unschedule_(false) { | |
33 helper_.reset(new ImageTransportHelper(this, manager, stub, handle)); | |
34 } | |
35 | |
36 ImageTransportSurfaceFBO::~ImageTransportSurfaceFBO() { | |
37 } | |
38 | |
39 bool ImageTransportSurfaceFBO::Initialize() { | |
40 // Only support IOSurfaces if the GL implementation is the native desktop GL. | |
41 // IO surfaces will not work with, for example, OSMesa software renderer | |
42 // GL contexts. | |
43 if (gfx::GetGLImplementation() != gfx::kGLImplementationDesktopGL && | |
44 gfx::GetGLImplementation() != gfx::kGLImplementationAppleGL) | |
45 return false; | |
46 | |
47 if (!helper_->Initialize()) | |
48 return false; | |
49 | |
50 helper_->stub()->AddDestructionObserver(this); | |
51 return true; | |
52 } | |
53 | |
54 void ImageTransportSurfaceFBO::Destroy() { | |
55 DestroyFramebuffer(); | |
56 | |
57 helper_->Destroy(); | |
58 } | |
59 | |
60 bool ImageTransportSurfaceFBO::DeferDraws() { | |
61 // The command buffer hit a draw/clear command that could clobber the | |
62 // IOSurface in use by an earlier SwapBuffers. If a Swap is pending, abort | |
63 // processing of the command by returning true and unschedule until the Swap | |
64 // Ack arrives. | |
65 if(did_unschedule_) | |
66 return true; // Still unscheduled, so just return true. | |
67 if (is_swap_buffers_pending_) { | |
68 did_unschedule_ = true; | |
69 helper_->SetScheduled(false); | |
70 return true; | |
71 } | |
72 return false; | |
73 } | |
74 | |
75 bool ImageTransportSurfaceFBO::IsOffscreen() { | |
76 return false; | |
77 } | |
78 | |
79 bool ImageTransportSurfaceFBO::OnMakeCurrent(gfx::GLContext* context) { | |
80 context_ = context; | |
81 | |
82 if (made_current_) | |
83 return true; | |
84 | |
85 OnResize(gfx::Size(1, 1), 1.f); | |
86 | |
87 made_current_ = true; | |
88 return true; | |
89 } | |
90 | |
91 unsigned int ImageTransportSurfaceFBO::GetBackingFrameBufferObject() { | |
92 return fbo_id_; | |
93 } | |
94 | |
95 bool ImageTransportSurfaceFBO::SetBackbufferAllocation(bool allocation) { | |
96 if (backbuffer_suggested_allocation_ == allocation) | |
97 return true; | |
98 backbuffer_suggested_allocation_ = allocation; | |
99 AdjustBufferAllocation(); | |
100 return true; | |
101 } | |
102 | |
103 void ImageTransportSurfaceFBO::SetFrontbufferAllocation(bool allocation) { | |
104 if (frontbuffer_suggested_allocation_ == allocation) | |
105 return; | |
106 frontbuffer_suggested_allocation_ = allocation; | |
107 AdjustBufferAllocation(); | |
108 } | |
109 | |
110 void ImageTransportSurfaceFBO::AdjustBufferAllocation() { | |
111 // On mac, the frontbuffer and backbuffer are the same buffer. The buffer is | |
112 // free'd when both the browser and gpu processes have Unref'd the IOSurface. | |
113 if (!backbuffer_suggested_allocation_ && | |
114 !frontbuffer_suggested_allocation_ && | |
115 has_complete_framebuffer_) { | |
116 DestroyFramebuffer(); | |
117 helper_->Suspend(); | |
118 } else if (backbuffer_suggested_allocation_ && !has_complete_framebuffer_) { | |
119 CreateFramebuffer(); | |
120 } | |
121 } | |
122 | |
123 bool ImageTransportSurfaceFBO::SwapBuffers() { | |
124 DCHECK(backbuffer_suggested_allocation_); | |
125 if (!frontbuffer_suggested_allocation_) | |
126 return true; | |
127 glFlush(); | |
128 | |
129 GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params; | |
130 params.surface_handle = storage_provider_->GetSurfaceHandle(); | |
131 params.size = GetSize(); | |
132 params.scale_factor = scale_factor_; | |
133 params.latency_info.swap(latency_info_); | |
134 helper_->SendAcceleratedSurfaceBuffersSwapped(params); | |
135 | |
136 DCHECK(!is_swap_buffers_pending_); | |
137 is_swap_buffers_pending_ = true; | |
138 | |
139 storage_provider_->WillSwapBuffers(); | |
140 return true; | |
141 } | |
142 | |
143 bool ImageTransportSurfaceFBO::PostSubBuffer( | |
144 int x, int y, int width, int height) { | |
145 DCHECK(backbuffer_suggested_allocation_); | |
146 if (!frontbuffer_suggested_allocation_) | |
147 return true; | |
148 glFlush(); | |
149 | |
150 GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params params; | |
151 params.surface_handle = storage_provider_->GetSurfaceHandle(); | |
152 params.x = x; | |
153 params.y = y; | |
154 params.width = width; | |
155 params.height = height; | |
156 params.surface_size = GetSize(); | |
157 params.surface_scale_factor = scale_factor_; | |
158 params.latency_info.swap(latency_info_); | |
159 helper_->SendAcceleratedSurfacePostSubBuffer(params); | |
160 | |
161 DCHECK(!is_swap_buffers_pending_); | |
162 is_swap_buffers_pending_ = true; | |
163 | |
164 storage_provider_->WillSwapBuffers(); | |
165 return true; | |
166 } | |
167 | |
168 bool ImageTransportSurfaceFBO::SupportsPostSubBuffer() { | |
169 return true; | |
170 } | |
171 | |
172 gfx::Size ImageTransportSurfaceFBO::GetSize() { | |
173 return size_; | |
174 } | |
175 | |
176 void* ImageTransportSurfaceFBO::GetHandle() { | |
177 return NULL; | |
178 } | |
179 | |
180 void* ImageTransportSurfaceFBO::GetDisplay() { | |
181 return NULL; | |
182 } | |
183 | |
184 void ImageTransportSurfaceFBO::OnBufferPresented( | |
185 const AcceleratedSurfaceMsg_BufferPresented_Params& params) { | |
186 DCHECK(is_swap_buffers_pending_); | |
187 | |
188 context_->share_group()->SetRendererID(params.renderer_id); | |
189 is_swap_buffers_pending_ = false; | |
190 if (did_unschedule_) { | |
191 did_unschedule_ = false; | |
192 helper_->SetScheduled(true); | |
193 } | |
194 } | |
195 | |
196 void ImageTransportSurfaceFBO::OnResize(gfx::Size size, | |
197 float scale_factor) { | |
198 // This trace event is used in gpu_feature_browsertest.cc - the test will need | |
199 // to be updated if this event is changed or moved. | |
200 TRACE_EVENT2("gpu", "ImageTransportSurfaceFBO::OnResize", | |
201 "old_width", size_.width(), "new_width", size.width()); | |
202 // Caching |context_| from OnMakeCurrent. It should still be current. | |
203 DCHECK(context_->IsCurrent(this)); | |
204 | |
205 size_ = size; | |
206 scale_factor_ = scale_factor; | |
207 | |
208 CreateFramebuffer(); | |
209 } | |
210 | |
211 void ImageTransportSurfaceFBO::SetLatencyInfo( | |
212 const std::vector<ui::LatencyInfo>& latency_info) { | |
213 for (size_t i = 0; i < latency_info.size(); i++) | |
214 latency_info_.push_back(latency_info[i]); | |
215 } | |
216 | |
217 void ImageTransportSurfaceFBO::WakeUpGpu() { | |
218 NOTIMPLEMENTED(); | |
219 } | |
220 | |
221 void ImageTransportSurfaceFBO::OnWillDestroyStub() { | |
222 helper_->stub()->RemoveDestructionObserver(this); | |
223 Destroy(); | |
224 } | |
225 | |
226 void ImageTransportSurfaceFBO::DestroyFramebuffer() { | |
227 // If we have resources to destroy, then make sure that we have a current | |
228 // context which we can use to delete the resources. | |
229 if (context_ || fbo_id_ || texture_id_ || depth_stencil_renderbuffer_id_) { | |
230 DCHECK(gfx::GLContext::GetCurrent() == context_); | |
231 DCHECK(context_->IsCurrent(this)); | |
232 DCHECK(CGLGetCurrentContext()); | |
233 } | |
234 | |
235 if (fbo_id_) { | |
236 glDeleteFramebuffersEXT(1, &fbo_id_); | |
237 fbo_id_ = 0; | |
238 } | |
239 | |
240 if (texture_id_) { | |
241 glDeleteTextures(1, &texture_id_); | |
242 texture_id_ = 0; | |
243 } | |
244 | |
245 if (depth_stencil_renderbuffer_id_) { | |
246 glDeleteRenderbuffersEXT(1, &depth_stencil_renderbuffer_id_); | |
247 depth_stencil_renderbuffer_id_ = 0; | |
248 } | |
249 | |
250 storage_provider_->FreeColorBufferStorage(); | |
251 | |
252 has_complete_framebuffer_ = false; | |
253 } | |
254 | |
255 void ImageTransportSurfaceFBO::CreateFramebuffer() { | |
256 gfx::Size new_rounded_size = storage_provider_->GetRoundedSize(size_); | |
257 | |
258 // Only recreate surface when the rounded up size has changed. | |
259 if (has_complete_framebuffer_ && new_rounded_size == rounded_size_) | |
260 return; | |
261 | |
262 // This trace event is used in gpu_feature_browsertest.cc - the test will need | |
263 // to be updated if this event is changed or moved. | |
264 TRACE_EVENT2("gpu", "ImageTransportSurfaceFBO::CreateFramebuffer", | |
265 "width", new_rounded_size.width(), | |
266 "height", new_rounded_size.height()); | |
267 | |
268 rounded_size_ = new_rounded_size; | |
269 | |
270 // GL_TEXTURE_RECTANGLE_ARB is the best supported render target on | |
271 // Mac OS X and is required for IOSurface interoperability. | |
272 GLint previous_texture_id = 0; | |
273 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &previous_texture_id); | |
274 | |
275 // Free the old IO Surface first to reduce memory fragmentation. | |
276 DestroyFramebuffer(); | |
277 | |
278 glGenFramebuffersEXT(1, &fbo_id_); | |
279 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_id_); | |
280 | |
281 glGenTextures(1, &texture_id_); | |
282 | |
283 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture_id_); | |
284 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
285 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
286 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, | |
287 GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
288 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, | |
289 GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |
290 | |
291 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, | |
292 GL_COLOR_ATTACHMENT0_EXT, | |
293 GL_TEXTURE_RECTANGLE_ARB, | |
294 texture_id_, | |
295 0); | |
296 | |
297 // Search through the provided attributes; if the caller has | |
298 // requested a stencil buffer, try to get one. | |
299 | |
300 int32 stencil_bits = | |
301 helper_->stub()->GetRequestedAttribute(EGL_STENCIL_SIZE); | |
302 if (stencil_bits > 0) { | |
303 // Create and bind the stencil buffer | |
304 bool has_packed_depth_stencil = | |
305 GLSurface::ExtensionsContain( | |
306 reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)), | |
307 "GL_EXT_packed_depth_stencil"); | |
308 | |
309 if (has_packed_depth_stencil) { | |
310 glGenRenderbuffersEXT(1, &depth_stencil_renderbuffer_id_); | |
311 glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, | |
312 depth_stencil_renderbuffer_id_); | |
313 glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, | |
314 rounded_size_.width(), rounded_size_.height()); | |
315 glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, | |
316 GL_STENCIL_ATTACHMENT_EXT, | |
317 GL_RENDERBUFFER_EXT, | |
318 depth_stencil_renderbuffer_id_); | |
319 } | |
320 | |
321 // If we asked for stencil but the extension isn't present, | |
322 // it's OK to silently fail; subsequent code will/must check | |
323 // for the presence of a stencil buffer before attempting to | |
324 // do stencil-based operations. | |
325 } | |
326 | |
327 bool allocated_color_buffer = storage_provider_->AllocateColorBufferStorage( | |
328 static_cast<CGLContextObj>(context_->GetHandle()), texture_id_, | |
329 rounded_size_, scale_factor_); | |
330 if (!allocated_color_buffer) { | |
331 DLOG(ERROR) << "Failed to allocate color buffer storage."; | |
332 DestroyFramebuffer(); | |
333 return; | |
334 } | |
335 | |
336 GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); | |
337 if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { | |
338 DLOG(ERROR) << "Framebuffer was incomplete: " << status; | |
339 DestroyFramebuffer(); | |
340 return; | |
341 } | |
342 | |
343 has_complete_framebuffer_ = true; | |
344 | |
345 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, previous_texture_id); | |
346 // The FBO remains bound for this GL context. | |
347 } | |
348 | |
349 } // namespace content | |
OLD | NEW |