Chromium Code Reviews| 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 "gpu/command_buffer/client/gles2_implementation.h" | |
| 10 #include "gpu/command_buffer/client/gles2_lib.h" | |
| 11 #include "gpu/command_buffer/client/transfer_buffer.h" | |
| 12 #include "gpu/command_buffer/common/value_state.h" | |
| 13 #include "gpu/command_buffer/service/context_group.h" | |
| 14 #include "gpu/command_buffer/service/mailbox_manager.h" | |
| 15 #include "gpu/command_buffer/service/memory_tracking.h" | |
| 16 #include "gpu/command_buffer/service/transfer_buffer_manager.h" | |
| 17 #include "gpu/command_buffer/service/valuebuffer_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), config_(config), is_current_in_some_thread_(false) {} | |
| 50 | |
| 51 Context::~Context() { | |
| 52 // We might not have a surface, so we must lose the context. | |
|
piman
2016/02/23 23:37:16
If we lose the context, we will leak any resource
Kimmo Kinnunen
2016/02/24 07:33:26
.. to the extent that there ever will be "normal a
piman
2016/02/24 21:53:45
It's more a heads-up/documentation thing for somet
| |
| 53 // Cleanup will execute GL commands otherwise. | |
| 54 if (HasService()) { | |
| 55 if (!WasServiceContextLost()) | |
| 56 MarkServiceContextLost(); | |
| 57 DestroyService(); | |
| 58 } | |
| 59 } | |
| 60 | |
| 61 void Context::FlushAndSwapBuffers(gfx::GLSurface* current_surface) { | |
| 62 DCHECK(HasService() && is_current_in_some_thread_); | |
| 63 if (!Flush(current_surface)) | |
| 64 return; | |
| 65 current_surface->SwapBuffers(); | |
| 66 } | |
| 67 | |
| 68 bool Context::MakeCurrent(Context* current_context, | |
| 69 gfx::GLSurface* current_surface, | |
| 70 Context* new_context, | |
| 71 gfx::GLSurface* new_surface) { | |
| 72 if (!new_context && !current_context) { | |
| 73 return true; | |
| 74 } | |
| 75 | |
| 76 bool cleanup_old_current_context = false; | |
| 77 if (current_context) { | |
| 78 if (current_context->Flush(current_surface)) | |
| 79 cleanup_old_current_context = new_context != current_context; | |
| 80 } | |
| 81 | |
| 82 if (new_context) { | |
| 83 if (!new_context->IsCompatibleSurface(new_surface)) | |
| 84 return false; | |
| 85 | |
| 86 if (new_context->HasService()) { | |
| 87 if (new_context->WasServiceContextLost()) | |
| 88 return false; | |
| 89 if (new_context != current_context) { | |
| 90 // If Flush did not set the current context, set it now. Otherwise | |
| 91 // calling into the decoder is not ok. | |
| 92 if (!new_context->gl_context_->MakeCurrent(new_surface)) { | |
| 93 new_context->MarkServiceContextLost(); | |
| 94 return false; | |
| 95 } | |
| 96 } | |
| 97 if (new_context != current_context || new_surface != current_surface) | |
| 98 new_context->decoder_->SetSurface(new_surface); | |
| 99 if (!new_context->decoder_->MakeCurrent()) { | |
| 100 new_context->MarkServiceContextLost(); | |
| 101 return false; | |
| 102 } | |
| 103 } else { | |
| 104 if (!new_context->CreateService(new_surface)) { | |
| 105 return false; | |
| 106 } | |
| 107 } | |
| 108 } | |
| 109 | |
| 110 // The current_surface will be released when MakeCurrent succeeds. | |
| 111 // Cleanup in this case only. | |
| 112 if (cleanup_old_current_context) { | |
| 113 if (current_context->HasOneRef() && current_surface != new_surface) { | |
|
piman
2016/02/23 23:37:16
I'm somewhat uncomfortable with HasOneRef(), becau
Kimmo Kinnunen
2016/02/24 07:33:26
Done.
| |
| 114 current_context->gl_context_->MakeCurrent(current_surface); | |
| 115 // If we are releasing the context and we have one ref, it means that the | |
| 116 // ref will be lost and the object will be destroyed. Destroy the service | |
| 117 // explicitly here, so that cleanup can happen and client GL | |
| 118 // implementation does not print errors. | |
| 119 current_context->DestroyService(); | |
| 120 } else { | |
| 121 current_context->decoder_->ReleaseSurface(); | |
| 122 } | |
| 123 } | |
| 124 | |
| 125 return true; | |
| 126 } | |
| 127 | |
| 128 bool Context::ValidateAttributeList(const EGLint* attrib_list) { | |
| 129 if (attrib_list) { | |
| 130 for (int i = 0; attrib_list[i] != EGL_NONE; attrib_list += 2) { | |
| 131 switch (attrib_list[i]) { | |
| 132 case EGL_CONTEXT_CLIENT_VERSION: | |
| 133 break; | |
| 134 default: | |
| 135 return false; | |
| 136 } | |
| 137 } | |
| 138 } | |
| 139 return true; | |
| 140 } | |
| 141 | |
| 142 gpu::Capabilities Context::GetCapabilities() { | |
| 143 return decoder_->GetCapabilities(); | |
| 144 } | |
| 145 | |
| 146 int32_t Context::CreateImage(ClientBuffer buffer, | |
| 147 size_t width, | |
| 148 size_t height, | |
| 149 unsigned internalformat) { | |
| 150 NOTIMPLEMENTED(); | |
| 151 return -1; | |
| 152 } | |
| 153 | |
| 154 void Context::DestroyImage(int32_t id) { | |
| 155 NOTIMPLEMENTED(); | |
| 156 } | |
| 157 | |
| 158 int32_t Context::CreateGpuMemoryBufferImage(size_t width, | |
| 159 size_t height, | |
| 160 unsigned internalformat, | |
| 161 unsigned usage) { | |
| 162 NOTIMPLEMENTED(); | |
| 163 return -1; | |
| 164 } | |
| 165 | |
| 166 void Context::SignalQuery(uint32_t query, const base::Closure& callback) { | |
| 167 NOTIMPLEMENTED(); | |
| 168 } | |
| 169 | |
| 170 void Context::SetLock(base::Lock*) { | |
| 171 NOTIMPLEMENTED(); | |
| 172 } | |
| 173 | |
| 174 bool Context::IsGpuChannelLost() { | |
| 175 NOTIMPLEMENTED(); | |
| 176 return false; | |
| 177 } | |
| 178 | |
| 179 void Context::EnsureWorkVisible() { | |
| 180 // This is only relevant for out-of-process command buffers. | |
| 181 } | |
| 182 | |
| 183 gpu::CommandBufferNamespace Context::GetNamespaceID() const { | |
| 184 return gpu::CommandBufferNamespace::IN_PROCESS; | |
| 185 } | |
| 186 | |
| 187 gpu::CommandBufferId Context::GetCommandBufferID() const { | |
| 188 return gpu::CommandBufferId(); | |
| 189 } | |
| 190 | |
| 191 int32_t Context::GetExtraCommandBufferData() const { | |
| 192 return 0; | |
| 193 } | |
| 194 | |
| 195 uint64_t Context::GenerateFenceSyncRelease() { | |
| 196 return display_->GenerateFenceSyncRelease(); | |
| 197 } | |
| 198 | |
| 199 bool Context::IsFenceSyncRelease(uint64_t release) { | |
| 200 return display_->IsFenceSyncRelease(release); | |
| 201 } | |
| 202 | |
| 203 bool Context::IsFenceSyncFlushed(uint64_t release) { | |
| 204 return display_->IsFenceSyncFlushed(release); | |
| 205 } | |
| 206 | |
| 207 bool Context::IsFenceSyncFlushReceived(uint64_t release) { | |
| 208 return display_->IsFenceSyncFlushReceived(release); | |
| 209 } | |
| 210 | |
| 211 void Context::SignalSyncToken(const gpu::SyncToken& sync_token, | |
| 212 const base::Closure& callback) { | |
| 213 NOTIMPLEMENTED(); | |
| 214 } | |
| 215 | |
| 216 bool Context::CanWaitUnverifiedSyncToken(const gpu::SyncToken* sync_token) { | |
| 217 return false; | |
| 218 } | |
| 219 | |
| 220 void Context::ApplyCurrentContext(gfx::GLSurface* current_surface) { | |
| 221 DCHECK(HasService()); | |
| 222 // The current_surface will be the same as | |
| 223 // the surface of the decoder. We can not DCHECK as there is | |
| 224 // no accessor. | |
| 225 if (!WasServiceContextLost()) { | |
| 226 if (!gl_context_->MakeCurrent(current_surface)) | |
| 227 MarkServiceContextLost(); | |
| 228 } | |
| 229 gles2::SetGLContext(client_gl_context_.get()); | |
| 230 } | |
| 231 | |
| 232 void Context::ApplyContextReleased() { | |
| 233 gles2::SetGLContext(nullptr); | |
| 234 } | |
| 235 | |
| 236 bool Context::CreateService(gfx::GLSurface* gl_surface) { | |
| 237 scoped_refptr<gpu::TransferBufferManager> transfer_buffer_manager( | |
| 238 new gpu::TransferBufferManager(nullptr)); | |
| 239 transfer_buffer_manager->Initialize(); | |
| 240 | |
| 241 scoped_ptr<gpu::CommandBufferService> command_buffer( | |
| 242 new gpu::CommandBufferService(transfer_buffer_manager.get())); | |
| 243 if (!command_buffer->Initialize()) | |
| 244 return false; | |
| 245 | |
| 246 scoped_refptr<gpu::gles2::ContextGroup> group(new gpu::gles2::ContextGroup( | |
| 247 NULL, NULL, new gpu::gles2::ShaderTranslatorCache, | |
| 248 new gpu::gles2::FramebufferCompletenessCache, NULL, NULL, NULL, true)); | |
| 249 | |
| 250 scoped_ptr<gpu::gles2::GLES2Decoder> decoder( | |
| 251 gpu::gles2::GLES2Decoder::Create(group.get())); | |
| 252 if (!decoder.get()) | |
| 253 return false; | |
| 254 | |
| 255 scoped_ptr<gpu::GpuScheduler> gpu_scheduler(new gpu::GpuScheduler( | |
| 256 command_buffer.get(), decoder.get(), decoder.get())); | |
| 257 | |
| 258 decoder->set_engine(gpu_scheduler.get()); | |
| 259 | |
| 260 scoped_refptr<gfx::GLContext> gl_context(gfx::GLContext::CreateGLContext( | |
| 261 nullptr, gl_surface, gfx::PreferDiscreteGpu)); | |
| 262 if (!gl_context) | |
| 263 return false; | |
| 264 | |
| 265 gl_context->MakeCurrent(gl_surface); | |
| 266 | |
| 267 gpu::gles2::ContextCreationAttribHelper helper; | |
| 268 config_->GetAttrib(EGL_ALPHA_SIZE, &helper.alpha_size); | |
| 269 config_->GetAttrib(EGL_BLUE_SIZE, &helper.blue_size); | |
| 270 config_->GetAttrib(EGL_GREEN_SIZE, &helper.green_size); | |
| 271 config_->GetAttrib(EGL_RED_SIZE, &helper.red_size); | |
| 272 config_->GetAttrib(EGL_DEPTH_SIZE, &helper.depth_size); | |
| 273 config_->GetAttrib(EGL_STENCIL_SIZE, &helper.stencil_size); | |
| 274 config_->GetAttrib(EGL_SAMPLES, &helper.samples); | |
| 275 config_->GetAttrib(EGL_SAMPLE_BUFFERS, &helper.sample_buffers); | |
| 276 | |
| 277 helper.buffer_preserved = false; | |
| 278 helper.bind_generates_resource = kBindGeneratesResources; | |
| 279 helper.fail_if_major_perf_caveat = false; | |
| 280 helper.lose_context_when_out_of_memory = kLoseContextWhenOutOfMemory; | |
| 281 helper.context_type = gpu::gles2::CONTEXT_TYPE_OPENGLES2; | |
| 282 std::vector<int32_t> attribs; | |
| 283 helper.Serialize(&attribs); | |
| 284 | |
| 285 if (!decoder->Initialize(gl_surface, gl_context.get(), | |
| 286 gl_surface->IsOffscreen(), gl_surface->GetSize(), | |
| 287 gpu::gles2::DisallowedFeatures(), attribs)) { | |
| 288 return false; | |
| 289 } | |
| 290 | |
| 291 command_buffer->SetPutOffsetChangeCallback(base::Bind( | |
| 292 &gpu::GpuScheduler::PutChanged, base::Unretained(gpu_scheduler.get()))); | |
| 293 command_buffer->SetGetBufferChangeCallback(base::Bind( | |
| 294 &gpu::GpuScheduler::SetGetBuffer, base::Unretained(gpu_scheduler.get()))); | |
| 295 | |
| 296 scoped_ptr<gpu::gles2::GLES2CmdHelper> gles2_cmd_helper( | |
| 297 new gpu::gles2::GLES2CmdHelper(command_buffer.get())); | |
| 298 if (!gles2_cmd_helper->Initialize(kCommandBufferSize)) { | |
| 299 decoder->Destroy(true); | |
| 300 return false; | |
| 301 } | |
| 302 | |
| 303 scoped_ptr<gpu::TransferBuffer> transfer_buffer( | |
| 304 new gpu::TransferBuffer(gles2_cmd_helper.get())); | |
| 305 | |
| 306 gles2_cmd_helper_.reset(gles2_cmd_helper.release()); | |
| 307 transfer_buffer_.reset(transfer_buffer.release()); | |
| 308 command_buffer_.reset(command_buffer.release()); | |
| 309 gpu_scheduler_.reset(gpu_scheduler.release()); | |
| 310 decoder_.reset(decoder.release()); | |
| 311 gl_context_ = gl_context.get(); | |
| 312 | |
| 313 scoped_ptr<gpu::gles2::GLES2Implementation> context( | |
| 314 new gpu::gles2::GLES2Implementation( | |
| 315 gles2_cmd_helper_.get(), nullptr, transfer_buffer_.get(), | |
| 316 kBindGeneratesResources, kLoseContextWhenOutOfMemory, | |
| 317 kSupportClientSideArrays, this)); | |
| 318 | |
| 319 if (!context->Initialize(kTransferBufferSize, kTransferBufferSize / 2, | |
| 320 kTransferBufferSize * 2, | |
| 321 gpu::gles2::GLES2Implementation::kNoLimit)) { | |
| 322 DestroyService(); | |
| 323 return false; | |
| 324 } | |
| 325 | |
| 326 context->EnableFeatureCHROMIUM("pepper3d_allow_buffers_on_multiple_targets"); | |
| 327 context->EnableFeatureCHROMIUM("pepper3d_support_fixed_attribs"); | |
| 328 client_gl_context_.reset(context.release()); | |
| 329 return true; | |
| 330 } | |
| 331 | |
| 332 void Context::DestroyService() { | |
| 333 DCHECK(HasService()); | |
| 334 bool have_context = !WasServiceContextLost(); | |
| 335 // The client gl interface might still be set to current global | |
| 336 // interface. This will be cleaned up in ApplyContextReleased | |
| 337 // with AutoCurrentContextRestore. | |
| 338 client_gl_context_.reset(); | |
| 339 gl_context_ = nullptr; | |
| 340 | |
| 341 transfer_buffer_.reset(); | |
| 342 gpu_scheduler_.reset(); | |
| 343 if (decoder_) | |
| 344 decoder_->Destroy(have_context); | |
| 345 gles2_cmd_helper_.reset(); | |
| 346 command_buffer_.reset(); | |
| 347 } | |
| 348 | |
| 349 bool Context::HasService() const { | |
| 350 return decoder_ != nullptr; | |
| 351 } | |
| 352 | |
| 353 void Context::MarkServiceContextLost() { | |
| 354 decoder_->MarkContextLost(gpu::error::kMakeCurrentFailed); | |
| 355 } | |
| 356 | |
| 357 bool Context::WasServiceContextLost() const { | |
| 358 return decoder_->WasContextLost(); | |
| 359 } | |
| 360 | |
| 361 bool Context::IsCompatibleSurface(gfx::GLSurface* gl_surface) { | |
| 362 EGLint value = EGL_NONE; | |
| 363 config_->GetAttrib(EGL_SURFACE_TYPE, &value); | |
| 364 bool config_is_offscreen = (value & EGL_PBUFFER_BIT) != 0; | |
| 365 return gl_surface->IsOffscreen() == config_is_offscreen; | |
| 366 } | |
| 367 | |
| 368 bool Context::Flush(gfx::GLSurface* gl_surface) { | |
| 369 if (WasServiceContextLost()) | |
| 370 return false; | |
| 371 if (!gl_context_->MakeCurrent(gl_surface)) { | |
| 372 MarkServiceContextLost(); | |
| 373 return false; | |
| 374 } | |
| 375 client_gl_context_->Flush(); | |
| 376 return true; | |
| 377 } | |
| 378 | |
| 379 } // namespace egl | |
| OLD | NEW |