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