OLD | NEW |
| (Empty) |
1 // Copyright (c) 2016 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 "gpu/gles2_conform_support/egl/context.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/bind_helpers.h" | |
9 #include "base/command_line.h" | |
10 #include "gpu/command_buffer/client/gles2_implementation.h" | |
11 #include "gpu/command_buffer/client/gles2_lib.h" | |
12 #include "gpu/command_buffer/client/shared_memory_limits.h" | |
13 #include "gpu/command_buffer/client/transfer_buffer.h" | |
14 #include "gpu/command_buffer/common/value_state.h" | |
15 #include "gpu/command_buffer/service/context_group.h" | |
16 #include "gpu/command_buffer/service/mailbox_manager.h" | |
17 #include "gpu/command_buffer/service/memory_tracking.h" | |
18 #include "gpu/command_buffer/service/transfer_buffer_manager.h" | |
19 #include "gpu/command_buffer/service/valuebuffer_manager.h" | |
20 #include "gpu/gles2_conform_support/egl/config.h" | |
21 #include "gpu/gles2_conform_support/egl/display.h" | |
22 #include "gpu/gles2_conform_support/egl/surface.h" | |
23 #include "gpu/gles2_conform_support/egl/thread_state.h" | |
24 | |
25 // The slight complexification in this file comes from following properties: | |
26 // 1) Command buffer connection (context) can not be established without a | |
27 // GLSurface. EGL Context can be created independent of a surface. This is why | |
28 // the connection is created only during first MakeCurrent. | |
29 // 2) Command buffer MakeCurrent calls need the real gl context and surface be | |
30 // current. | |
31 // 3) Client can change real EGL context behind the scenes and then still expect | |
32 // command buffer MakeCurrent re-set the command buffer context. This is why all | |
33 // MakeCurrent calls must actually reset the real context, even though command | |
34 // buffer current context does not change. | |
35 // 4) EGL context can be destroyed without surface, but command buffer would | |
36 // need the surface to run various cleanups. If context is destroyed | |
37 // surfaceless, the context is marked lost before destruction. This is avoided | |
38 // if possible, since command buffer at the time of writing prints out debug | |
39 // text in this case. | |
40 | |
41 namespace { | |
42 const int32_t kCommandBufferSize = 1024 * 1024; | |
43 const int32_t kTransferBufferSize = 512 * 1024; | |
44 const bool kBindGeneratesResources = true; | |
45 const bool kLoseContextWhenOutOfMemory = false; | |
46 const bool kSupportClientSideArrays = true; | |
47 } | |
48 | |
49 namespace egl { | |
50 Context::Context(Display* display, const Config* config) | |
51 : display_(display), | |
52 config_(config), | |
53 is_current_in_some_thread_(false), | |
54 is_destroyed_(false), | |
55 gpu_driver_bug_workarounds_(base::CommandLine::ForCurrentProcess()) {} | |
56 | |
57 Context::~Context() { | |
58 // We might not have a surface, so we must lose the context. Cleanup will | |
59 // execute GL commands otherwise. TODO: if shared contexts are ever | |
60 // implemented, this will leak the GL resources. For pbuffer contexts, one | |
61 // could track the last current surface or create a surface for destroying | |
62 // purposes only. Other option would be to make the service usable without | |
63 // surface. | |
64 if (HasService()) { | |
65 if (!WasServiceContextLost()) | |
66 MarkServiceContextLost(); | |
67 DestroyService(); | |
68 } | |
69 } | |
70 | |
71 void Context::MarkDestroyed() { | |
72 is_destroyed_ = true; | |
73 } | |
74 | |
75 void Context::FlushAndSwapBuffers(Surface* current_surface) { | |
76 DCHECK(HasService() && is_current_in_some_thread_); | |
77 if (!Flush(current_surface->gl_surface())) | |
78 return; | |
79 // Inspect gl_surface()->IsOffscreen() instead of current_surface->config() | |
80 // because GTF windowless window surfaces might be emulated with offscreen | |
81 // surfaces. | |
82 if (current_surface->gl_surface()->IsOffscreen()) | |
83 return; | |
84 current_surface->gl_surface()->SwapBuffers(); | |
85 } | |
86 | |
87 bool Context::MakeCurrent(Context* current_context, | |
88 Surface* current_surface, | |
89 Context* new_context, | |
90 Surface* new_surface) { | |
91 if (!new_context && !current_context) { | |
92 return true; | |
93 } | |
94 | |
95 bool cleanup_old_current_context = false; | |
96 if (current_context) { | |
97 if (current_context->Flush(current_surface->gl_surface())) | |
98 cleanup_old_current_context = new_context != current_context; | |
99 } | |
100 | |
101 if (new_context) { | |
102 if (!new_context->IsCompatibleSurface(new_surface)) | |
103 return false; | |
104 | |
105 if (new_context->HasService()) { | |
106 if (new_context->WasServiceContextLost()) | |
107 return false; | |
108 if (new_context != current_context) { | |
109 // If Flush did not set the current context, set it now. Otherwise | |
110 // calling into the decoder is not ok. | |
111 if (!new_context->gl_context_->MakeCurrent(new_surface->gl_surface())) { | |
112 new_context->MarkServiceContextLost(); | |
113 return false; | |
114 } | |
115 } | |
116 if (new_context != current_context || new_surface != current_surface) | |
117 new_context->decoder_->SetSurface(new_surface->gl_surface()); | |
118 if (!new_context->decoder_->MakeCurrent()) { | |
119 new_context->MarkServiceContextLost(); | |
120 return false; | |
121 } | |
122 } else { | |
123 if (!new_context->CreateService(new_surface->gl_surface())) { | |
124 return false; | |
125 } | |
126 } | |
127 } | |
128 | |
129 // The current_surface will be released when MakeCurrent succeeds. | |
130 // Cleanup in this case only. | |
131 if (cleanup_old_current_context) { | |
132 if (current_context->is_destroyed_ && current_surface != new_surface) { | |
133 current_context->gl_context_->MakeCurrent(current_surface->gl_surface()); | |
134 // If we are releasing the context and we have one ref, it means that the | |
135 // ref will be lost and the object will be destroyed. Destroy the service | |
136 // explicitly here, so that cleanup can happen and client GL | |
137 // implementation does not print errors. | |
138 current_context->DestroyService(); | |
139 } else { | |
140 current_context->decoder_->ReleaseSurface(); | |
141 } | |
142 } | |
143 | |
144 return true; | |
145 } | |
146 | |
147 bool Context::ValidateAttributeList(const EGLint* attrib_list) { | |
148 if (attrib_list) { | |
149 for (int i = 0; attrib_list[i] != EGL_NONE; attrib_list += 2) { | |
150 switch (attrib_list[i]) { | |
151 case EGL_CONTEXT_CLIENT_VERSION: | |
152 break; | |
153 default: | |
154 return false; | |
155 } | |
156 } | |
157 } | |
158 return true; | |
159 } | |
160 | |
161 void Context::SetGpuControlClient(gpu::GpuControlClient*) { | |
162 // The client is not currently called, so don't store it. | |
163 } | |
164 | |
165 gpu::Capabilities Context::GetCapabilities() { | |
166 return decoder_->GetCapabilities(); | |
167 } | |
168 | |
169 int32_t Context::CreateImage(ClientBuffer buffer, | |
170 size_t width, | |
171 size_t height, | |
172 unsigned internalformat) { | |
173 NOTIMPLEMENTED(); | |
174 return -1; | |
175 } | |
176 | |
177 void Context::DestroyImage(int32_t id) { | |
178 NOTIMPLEMENTED(); | |
179 } | |
180 | |
181 int32_t Context::CreateGpuMemoryBufferImage(size_t width, | |
182 size_t height, | |
183 unsigned internalformat, | |
184 unsigned usage) { | |
185 NOTIMPLEMENTED(); | |
186 return -1; | |
187 } | |
188 | |
189 void Context::SignalQuery(uint32_t query, const base::Closure& callback) { | |
190 NOTIMPLEMENTED(); | |
191 } | |
192 | |
193 void Context::SetLock(base::Lock*) { | |
194 NOTIMPLEMENTED(); | |
195 } | |
196 | |
197 bool Context::IsGpuChannelLost() { | |
198 NOTIMPLEMENTED(); | |
199 return false; | |
200 } | |
201 | |
202 void Context::EnsureWorkVisible() { | |
203 // This is only relevant for out-of-process command buffers. | |
204 } | |
205 | |
206 gpu::CommandBufferNamespace Context::GetNamespaceID() const { | |
207 return gpu::CommandBufferNamespace::IN_PROCESS; | |
208 } | |
209 | |
210 gpu::CommandBufferId Context::GetCommandBufferID() const { | |
211 return gpu::CommandBufferId(); | |
212 } | |
213 | |
214 int32_t Context::GetExtraCommandBufferData() const { | |
215 return 0; | |
216 } | |
217 | |
218 uint64_t Context::GenerateFenceSyncRelease() { | |
219 return display_->GenerateFenceSyncRelease(); | |
220 } | |
221 | |
222 bool Context::IsFenceSyncRelease(uint64_t release) { | |
223 return display_->IsFenceSyncRelease(release); | |
224 } | |
225 | |
226 bool Context::IsFenceSyncFlushed(uint64_t release) { | |
227 return display_->IsFenceSyncFlushed(release); | |
228 } | |
229 | |
230 bool Context::IsFenceSyncFlushReceived(uint64_t release) { | |
231 return display_->IsFenceSyncFlushReceived(release); | |
232 } | |
233 | |
234 void Context::SignalSyncToken(const gpu::SyncToken& sync_token, | |
235 const base::Closure& callback) { | |
236 NOTIMPLEMENTED(); | |
237 } | |
238 | |
239 bool Context::CanWaitUnverifiedSyncToken(const gpu::SyncToken* sync_token) { | |
240 return false; | |
241 } | |
242 | |
243 void Context::ApplyCurrentContext(gfx::GLSurface* current_surface) { | |
244 DCHECK(HasService()); | |
245 // The current_surface will be the same as | |
246 // the surface of the decoder. We can not DCHECK as there is | |
247 // no accessor. | |
248 if (!WasServiceContextLost()) { | |
249 if (!gl_context_->MakeCurrent(current_surface)) | |
250 MarkServiceContextLost(); | |
251 } | |
252 gles2::SetGLContext(client_gl_context_.get()); | |
253 } | |
254 | |
255 void Context::ApplyContextReleased() { | |
256 gles2::SetGLContext(nullptr); | |
257 } | |
258 | |
259 bool Context::CreateService(gfx::GLSurface* gl_surface) { | |
260 scoped_refptr<gpu::TransferBufferManager> transfer_buffer_manager( | |
261 new gpu::TransferBufferManager(nullptr)); | |
262 transfer_buffer_manager->Initialize(); | |
263 | |
264 std::unique_ptr<gpu::CommandBufferService> command_buffer( | |
265 new gpu::CommandBufferService(transfer_buffer_manager.get())); | |
266 if (!command_buffer->Initialize()) | |
267 return false; | |
268 scoped_refptr<gpu::gles2::FeatureInfo> feature_info( | |
269 new gpu::gles2::FeatureInfo(gpu_driver_bug_workarounds_)); | |
270 scoped_refptr<gpu::gles2::ContextGroup> group(new gpu::gles2::ContextGroup( | |
271 gpu_preferences_, nullptr, nullptr, | |
272 new gpu::gles2::ShaderTranslatorCache(gpu_preferences_), | |
273 new gpu::gles2::FramebufferCompletenessCache, feature_info, nullptr, | |
274 nullptr, true)); | |
275 | |
276 std::unique_ptr<gpu::gles2::GLES2Decoder> decoder( | |
277 gpu::gles2::GLES2Decoder::Create(group.get())); | |
278 if (!decoder.get()) | |
279 return false; | |
280 | |
281 std::unique_ptr<gpu::CommandExecutor> command_executor( | |
282 new gpu::CommandExecutor(command_buffer.get(), decoder.get(), | |
283 decoder.get())); | |
284 | |
285 decoder->set_engine(command_executor.get()); | |
286 | |
287 scoped_refptr<gfx::GLContext> gl_context(gfx::GLContext::CreateGLContext( | |
288 nullptr, gl_surface, gfx::PreferDiscreteGpu)); | |
289 if (!gl_context) | |
290 return false; | |
291 | |
292 gl_context->MakeCurrent(gl_surface); | |
293 | |
294 gpu::gles2::ContextCreationAttribHelper helper; | |
295 config_->GetAttrib(EGL_ALPHA_SIZE, &helper.alpha_size); | |
296 config_->GetAttrib(EGL_BLUE_SIZE, &helper.blue_size); | |
297 config_->GetAttrib(EGL_GREEN_SIZE, &helper.green_size); | |
298 config_->GetAttrib(EGL_RED_SIZE, &helper.red_size); | |
299 config_->GetAttrib(EGL_DEPTH_SIZE, &helper.depth_size); | |
300 config_->GetAttrib(EGL_STENCIL_SIZE, &helper.stencil_size); | |
301 config_->GetAttrib(EGL_SAMPLES, &helper.samples); | |
302 config_->GetAttrib(EGL_SAMPLE_BUFFERS, &helper.sample_buffers); | |
303 | |
304 helper.buffer_preserved = false; | |
305 helper.bind_generates_resource = kBindGeneratesResources; | |
306 helper.fail_if_major_perf_caveat = false; | |
307 helper.lose_context_when_out_of_memory = kLoseContextWhenOutOfMemory; | |
308 helper.context_type = gpu::gles2::CONTEXT_TYPE_OPENGLES2; | |
309 std::vector<int32_t> attribs; | |
310 helper.Serialize(&attribs); | |
311 | |
312 if (!decoder->Initialize(gl_surface, gl_context.get(), | |
313 gl_surface->IsOffscreen(), gl_surface->GetSize(), | |
314 gpu::gles2::DisallowedFeatures(), attribs)) { | |
315 return false; | |
316 } | |
317 | |
318 command_buffer->SetPutOffsetChangeCallback( | |
319 base::Bind(&gpu::CommandExecutor::PutChanged, | |
320 base::Unretained(command_executor.get()))); | |
321 command_buffer->SetGetBufferChangeCallback( | |
322 base::Bind(&gpu::CommandExecutor::SetGetBuffer, | |
323 base::Unretained(command_executor.get()))); | |
324 | |
325 std::unique_ptr<gpu::gles2::GLES2CmdHelper> gles2_cmd_helper( | |
326 new gpu::gles2::GLES2CmdHelper(command_buffer.get())); | |
327 if (!gles2_cmd_helper->Initialize(kCommandBufferSize)) { | |
328 decoder->Destroy(true); | |
329 return false; | |
330 } | |
331 | |
332 std::unique_ptr<gpu::TransferBuffer> transfer_buffer( | |
333 new gpu::TransferBuffer(gles2_cmd_helper.get())); | |
334 | |
335 gles2_cmd_helper_.reset(gles2_cmd_helper.release()); | |
336 transfer_buffer_.reset(transfer_buffer.release()); | |
337 command_buffer_.reset(command_buffer.release()); | |
338 command_executor_.reset(command_executor.release()); | |
339 decoder_.reset(decoder.release()); | |
340 gl_context_ = gl_context.get(); | |
341 | |
342 std::unique_ptr<gpu::gles2::GLES2Implementation> context( | |
343 new gpu::gles2::GLES2Implementation( | |
344 gles2_cmd_helper_.get(), nullptr, transfer_buffer_.get(), | |
345 kBindGeneratesResources, kLoseContextWhenOutOfMemory, | |
346 kSupportClientSideArrays, this)); | |
347 | |
348 if (!context->Initialize(kTransferBufferSize, kTransferBufferSize / 2, | |
349 kTransferBufferSize * 2, | |
350 gpu::SharedMemoryLimits::kNoLimit)) { | |
351 DestroyService(); | |
352 return false; | |
353 } | |
354 | |
355 context->EnableFeatureCHROMIUM("pepper3d_allow_buffers_on_multiple_targets"); | |
356 context->EnableFeatureCHROMIUM("pepper3d_support_fixed_attribs"); | |
357 client_gl_context_.reset(context.release()); | |
358 return true; | |
359 } | |
360 | |
361 void Context::DestroyService() { | |
362 DCHECK(HasService()); | |
363 bool have_context = !WasServiceContextLost(); | |
364 // The client gl interface might still be set to current global | |
365 // interface. This will be cleaned up in ApplyContextReleased | |
366 // with AutoCurrentContextRestore. | |
367 client_gl_context_.reset(); | |
368 gl_context_ = nullptr; | |
369 | |
370 transfer_buffer_.reset(); | |
371 command_executor_.reset(); | |
372 if (decoder_) | |
373 decoder_->Destroy(have_context); | |
374 gles2_cmd_helper_.reset(); | |
375 command_buffer_.reset(); | |
376 } | |
377 | |
378 bool Context::HasService() const { | |
379 return decoder_ != nullptr; | |
380 } | |
381 | |
382 void Context::MarkServiceContextLost() { | |
383 decoder_->MarkContextLost(gpu::error::kMakeCurrentFailed); | |
384 } | |
385 | |
386 bool Context::WasServiceContextLost() const { | |
387 return decoder_->WasContextLost(); | |
388 } | |
389 | |
390 bool Context::IsCompatibleSurface(Surface* surface) const { | |
391 // Inspect current_surface->config() instead of gl_surface()->IsOffscreen() | |
392 // because GTF windowless window surfaces might be emulated with offscreen | |
393 // surfaces. | |
394 EGLint value = EGL_NONE; | |
395 config_->GetAttrib(EGL_SURFACE_TYPE, &value); | |
396 bool context_config_is_offscreen = (value & EGL_PBUFFER_BIT) != 0; | |
397 surface->config()->GetAttrib(EGL_SURFACE_TYPE, &value); | |
398 bool surface_config_is_offscreen = (value & EGL_PBUFFER_BIT) != 0; | |
399 return surface_config_is_offscreen == context_config_is_offscreen; | |
400 } | |
401 | |
402 bool Context::Flush(gfx::GLSurface* gl_surface) { | |
403 if (WasServiceContextLost()) | |
404 return false; | |
405 if (!gl_context_->MakeCurrent(gl_surface)) { | |
406 MarkServiceContextLost(); | |
407 return false; | |
408 } | |
409 client_gl_context_->Flush(); | |
410 return true; | |
411 } | |
412 | |
413 } // namespace egl | |
OLD | NEW |