Index: content/common/gpu/gpu_command_buffer_stub.cc |
diff --git a/content/common/gpu/gpu_command_buffer_stub.cc b/content/common/gpu/gpu_command_buffer_stub.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..ea4d94421296975355842dd5016541324907648a |
--- /dev/null |
+++ b/content/common/gpu/gpu_command_buffer_stub.cc |
@@ -0,0 +1,1143 @@ |
+// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "content/common/gpu/gpu_command_buffer_stub.h" |
+ |
+#include <utility> |
+ |
+#include "base/bind.h" |
+#include "base/bind_helpers.h" |
+#include "base/hash.h" |
+#include "base/json/json_writer.h" |
+#include "base/macros.h" |
+#include "base/memory/shared_memory.h" |
+#include "base/time/time.h" |
+#include "base/trace_event/trace_event.h" |
+#include "build/build_config.h" |
+#include "content/common/gpu/gpu_channel.h" |
+#include "content/common/gpu/gpu_channel_manager.h" |
+#include "content/common/gpu/gpu_channel_manager_delegate.h" |
+#include "content/common/gpu/gpu_memory_manager.h" |
+#include "content/common/gpu/gpu_memory_tracking.h" |
+#include "content/common/gpu/gpu_watchdog.h" |
+#include "content/common/gpu/image_transport_surface.h" |
+#include "gpu/command_buffer/common/constants.h" |
+#include "gpu/command_buffer/common/mailbox.h" |
+#include "gpu/command_buffer/common/sync_token.h" |
+#include "gpu/command_buffer/service/gl_context_virtual.h" |
+#include "gpu/command_buffer/service/gl_state_restorer_impl.h" |
+#include "gpu/command_buffer/service/image_factory.h" |
+#include "gpu/command_buffer/service/image_manager.h" |
+#include "gpu/command_buffer/service/logger.h" |
+#include "gpu/command_buffer/service/mailbox_manager.h" |
+#include "gpu/command_buffer/service/memory_tracking.h" |
+#include "gpu/command_buffer/service/query_manager.h" |
+#include "gpu/command_buffer/service/sync_point_manager.h" |
+#include "gpu/command_buffer/service/transfer_buffer_manager.h" |
+#include "gpu/command_buffer/service/valuebuffer_manager.h" |
+#include "gpu/ipc/common/gpu_messages.h" |
+#include "ui/gl/gl_bindings.h" |
+#include "ui/gl/gl_image.h" |
+#include "ui/gl/gl_switches.h" |
+ |
+#if defined(OS_WIN) |
+#include "base/win/win_util.h" |
+#endif |
+ |
+#if defined(OS_ANDROID) |
+#include "content/common/gpu/stream_texture_android.h" |
+#endif |
+ |
+namespace content { |
+struct WaitForCommandState { |
+ WaitForCommandState(int32_t start, int32_t end, IPC::Message* reply) |
+ : start(start), end(end), reply(reply) {} |
+ |
+ int32_t start; |
+ int32_t end; |
+ scoped_ptr<IPC::Message> reply; |
+}; |
+ |
+namespace { |
+ |
+// The GpuCommandBufferMemoryTracker class provides a bridge between the |
+// ContextGroup's memory type managers and the GpuMemoryManager class. |
+class GpuCommandBufferMemoryTracker : public gpu::gles2::MemoryTracker { |
+ public: |
+ explicit GpuCommandBufferMemoryTracker(GpuChannel* channel, |
+ uint64_t share_group_tracing_guid) |
+ : tracking_group_( |
+ channel->gpu_channel_manager() |
+ ->gpu_memory_manager() |
+ ->CreateTrackingGroup(channel->GetClientPID(), this)), |
+ client_tracing_id_(channel->client_tracing_id()), |
+ client_id_(channel->client_id()), |
+ share_group_tracing_guid_(share_group_tracing_guid) {} |
+ |
+ void TrackMemoryAllocatedChange( |
+ size_t old_size, size_t new_size) override { |
+ tracking_group_->TrackMemoryAllocatedChange( |
+ old_size, new_size); |
+ } |
+ |
+ bool EnsureGPUMemoryAvailable(size_t size_needed) override { |
+ return tracking_group_->EnsureGPUMemoryAvailable(size_needed); |
+ }; |
+ |
+ uint64_t ClientTracingId() const override { return client_tracing_id_; } |
+ int ClientId() const override { return client_id_; } |
+ uint64_t ShareGroupTracingGUID() const override { |
+ return share_group_tracing_guid_; |
+ } |
+ |
+ private: |
+ ~GpuCommandBufferMemoryTracker() override {} |
+ scoped_ptr<GpuMemoryTrackingGroup> tracking_group_; |
+ const uint64_t client_tracing_id_; |
+ const int client_id_; |
+ const uint64_t share_group_tracing_guid_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(GpuCommandBufferMemoryTracker); |
+}; |
+ |
+// FastSetActiveURL will shortcut the expensive call to SetActiveURL when the |
+// url_hash matches. |
+void FastSetActiveURL(const GURL& url, size_t url_hash, GpuChannel* channel) { |
+ // Leave the previously set URL in the empty case -- empty URLs are given by |
+ // BlinkPlatformImpl::createOffscreenGraphicsContext3DProvider. Hopefully the |
+ // onscreen context URL was set previously and will show up even when a crash |
+ // occurs during offscreen command processing. |
+ if (url.is_empty()) |
+ return; |
+ static size_t g_last_url_hash = 0; |
+ if (url_hash != g_last_url_hash) { |
+ g_last_url_hash = url_hash; |
+ DCHECK(channel && channel->gpu_channel_manager() && |
+ channel->gpu_channel_manager()->delegate()); |
+ channel->gpu_channel_manager()->delegate()->SetActiveURL(url); |
+ } |
+} |
+ |
+// The first time polling a fence, delay some extra time to allow other |
+// stubs to process some work, or else the timing of the fences could |
+// allow a pattern of alternating fast and slow frames to occur. |
+const int64_t kHandleMoreWorkPeriodMs = 2; |
+const int64_t kHandleMoreWorkPeriodBusyMs = 1; |
+ |
+// Prevents idle work from being starved. |
+const int64_t kMaxTimeSinceIdleMs = 10; |
+ |
+class DevToolsChannelData : public base::trace_event::ConvertableToTraceFormat { |
+ public: |
+ static scoped_ptr<base::trace_event::ConvertableToTraceFormat> |
+ CreateForChannel(GpuChannel* channel); |
+ ~DevToolsChannelData() override {} |
+ |
+ void AppendAsTraceFormat(std::string* out) const override { |
+ std::string tmp; |
+ base::JSONWriter::Write(*value_, &tmp); |
+ *out += tmp; |
+ } |
+ |
+ private: |
+ explicit DevToolsChannelData(base::Value* value) : value_(value) {} |
+ scoped_ptr<base::Value> value_; |
+ DISALLOW_COPY_AND_ASSIGN(DevToolsChannelData); |
+}; |
+ |
+scoped_ptr<base::trace_event::ConvertableToTraceFormat> |
+DevToolsChannelData::CreateForChannel(GpuChannel* channel) { |
+ scoped_ptr<base::DictionaryValue> res(new base::DictionaryValue); |
+ res->SetInteger("renderer_pid", channel->GetClientPID()); |
+ res->SetDouble("used_bytes", channel->GetMemoryUsage()); |
+ return make_scoped_ptr(new DevToolsChannelData(res.release())); |
+} |
+ |
+gpu::CommandBufferId GetCommandBufferID(int channel_id, int32_t route_id) { |
+ return gpu::CommandBufferId::FromUnsafeValue( |
+ (static_cast<uint64_t>(channel_id) << 32) | route_id); |
+} |
+ |
+} // namespace |
+ |
+GpuCommandBufferStub::GpuCommandBufferStub( |
+ GpuChannel* channel, |
+ gpu::SyncPointManager* sync_point_manager, |
+ base::SingleThreadTaskRunner* task_runner, |
+ GpuCommandBufferStub* share_group, |
+ gpu::SurfaceHandle surface_handle, |
+ gpu::gles2::MailboxManager* mailbox_manager, |
+ gpu::PreemptionFlag* preempt_by_flag, |
+ gpu::gles2::SubscriptionRefSet* subscription_ref_set, |
+ gpu::ValueStateMap* pending_valuebuffer_state, |
+ const gfx::Size& size, |
+ const gpu::gles2::DisallowedFeatures& disallowed_features, |
+ const std::vector<int32_t>& attribs, |
+ gfx::GpuPreference gpu_preference, |
+ int32_t stream_id, |
+ int32_t route_id, |
+ GpuWatchdog* watchdog, |
+ const GURL& active_url) |
+ : channel_(channel), |
+ sync_point_manager_(sync_point_manager), |
+ task_runner_(task_runner), |
+ initialized_(false), |
+ surface_handle_(surface_handle), |
+ initial_size_(size), |
+ disallowed_features_(disallowed_features), |
+ requested_attribs_(attribs), |
+ gpu_preference_(gpu_preference), |
+ use_virtualized_gl_context_(false), |
+ command_buffer_id_(GetCommandBufferID(channel->client_id(), route_id)), |
+ stream_id_(stream_id), |
+ route_id_(route_id), |
+ last_flush_count_(0), |
+ surface_format_(gfx::GLSurface::SURFACE_DEFAULT), |
+ watchdog_(watchdog), |
+ waiting_for_sync_point_(false), |
+ previous_processed_num_(0), |
+ preemption_flag_(preempt_by_flag), |
+ active_url_(active_url) { |
+ active_url_hash_ = base::Hash(active_url.possibly_invalid_spec()); |
+ FastSetActiveURL(active_url_, active_url_hash_, channel_); |
+ |
+ gpu::gles2::ContextCreationAttribHelper attrib_parser; |
+ attrib_parser.Parse(requested_attribs_); |
+ |
+ if (share_group) { |
+ context_group_ = share_group->context_group_; |
+ DCHECK(context_group_->bind_generates_resource() == |
+ attrib_parser.bind_generates_resource); |
+ } else { |
+ context_group_ = new gpu::gles2::ContextGroup( |
+ channel_->gpu_channel_manager()->gpu_preferences(), mailbox_manager, |
+ new GpuCommandBufferMemoryTracker(channel, |
+ command_buffer_id_.GetUnsafeValue()), |
+ channel_->gpu_channel_manager()->shader_translator_cache(), |
+ channel_->gpu_channel_manager()->framebuffer_completeness_cache(), NULL, |
+ subscription_ref_set, pending_valuebuffer_state, |
+ attrib_parser.bind_generates_resource); |
+ } |
+ |
+// Virtualize PreferIntegratedGpu contexts by default on OS X to prevent |
+// performance regressions when enabling FCM. |
+// http://crbug.com/180463 |
+#if defined(OS_MACOSX) |
+ if (gpu_preference_ == gfx::PreferIntegratedGpu) |
+ use_virtualized_gl_context_ = true; |
+#endif |
+ |
+ use_virtualized_gl_context_ |= |
+ context_group_->feature_info()->workarounds().use_virtualized_gl_contexts; |
+ |
+ // MailboxManagerSync synchronization correctness currently depends on having |
+ // only a single context. See crbug.com/510243 for details. |
+ use_virtualized_gl_context_ |= mailbox_manager->UsesSync(); |
+ |
+#if defined(OS_ANDROID) |
+ if (attrib_parser.red_size <= 5 && |
+ attrib_parser.green_size <= 6 && |
+ attrib_parser.blue_size <= 5 && |
+ attrib_parser.alpha_size == 0) |
+ surface_format_ = gfx::GLSurface::SURFACE_RGB565; |
+ gfx::GLSurface* defaultOffscreenSurface = |
+ channel_->gpu_channel_manager()->GetDefaultOffscreenSurface(); |
+ bool is_onscreen = (surface_handle_ != gpu::kNullSurfaceHandle); |
+ if (surface_format_ != defaultOffscreenSurface->GetFormat() && is_onscreen) |
+ use_virtualized_gl_context_ = false; |
+#endif |
+ |
+ if ((surface_handle_ == gpu::kNullSurfaceHandle) && initial_size_.IsEmpty()) { |
+ // If we're an offscreen surface with zero width and/or height, set to a |
+ // non-zero size so that we have a complete framebuffer for operations like |
+ // glClear. |
+ initial_size_ = gfx::Size(1, 1); |
+ } |
+} |
+ |
+GpuCommandBufferStub::~GpuCommandBufferStub() { |
+ Destroy(); |
+} |
+ |
+GpuMemoryManager* GpuCommandBufferStub::GetMemoryManager() const { |
+ return channel()->gpu_channel_manager()->gpu_memory_manager(); |
+} |
+ |
+bool GpuCommandBufferStub::OnMessageReceived(const IPC::Message& message) { |
+ TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), |
+ "GPUTask", |
+ "data", |
+ DevToolsChannelData::CreateForChannel(channel())); |
+ FastSetActiveURL(active_url_, active_url_hash_, channel_); |
+ |
+ bool have_context = false; |
+ // Ensure the appropriate GL context is current before handling any IPC |
+ // messages directed at the command buffer. This ensures that the message |
+ // handler can assume that the context is current (not necessary for |
+ // RetireSyncPoint or WaitSyncPoint). |
+ if (decoder_.get() && |
+ message.type() != GpuCommandBufferMsg_SetGetBuffer::ID && |
+ message.type() != GpuCommandBufferMsg_WaitForTokenInRange::ID && |
+ message.type() != GpuCommandBufferMsg_WaitForGetOffsetInRange::ID && |
+ message.type() != GpuCommandBufferMsg_RegisterTransferBuffer::ID && |
+ message.type() != GpuCommandBufferMsg_DestroyTransferBuffer::ID) { |
+ if (!MakeCurrent()) |
+ return false; |
+ have_context = true; |
+ } |
+ |
+ // Always use IPC_MESSAGE_HANDLER_DELAY_REPLY for synchronous message handlers |
+ // here. This is so the reply can be delayed if the scheduler is unscheduled. |
+ bool handled = true; |
+ IPC_BEGIN_MESSAGE_MAP(GpuCommandBufferStub, message) |
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(GpuCommandBufferMsg_Initialize, |
+ OnInitialize); |
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(GpuCommandBufferMsg_SetGetBuffer, |
+ OnSetGetBuffer); |
+ IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_ProduceFrontBuffer, |
+ OnProduceFrontBuffer); |
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(GpuCommandBufferMsg_WaitForTokenInRange, |
+ OnWaitForTokenInRange); |
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(GpuCommandBufferMsg_WaitForGetOffsetInRange, |
+ OnWaitForGetOffsetInRange); |
+ IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_AsyncFlush, OnAsyncFlush); |
+ IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_RegisterTransferBuffer, |
+ OnRegisterTransferBuffer); |
+ IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_DestroyTransferBuffer, |
+ OnDestroyTransferBuffer); |
+ IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_SignalSyncToken, |
+ OnSignalSyncToken) |
+ IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_SignalQuery, |
+ OnSignalQuery) |
+ IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_CreateImage, OnCreateImage); |
+ IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_DestroyImage, OnDestroyImage); |
+ IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_CreateStreamTexture, |
+ OnCreateStreamTexture) |
+ IPC_MESSAGE_UNHANDLED(handled = false) |
+ IPC_END_MESSAGE_MAP() |
+ |
+ CheckCompleteWaits(); |
+ |
+ // Ensure that any delayed work that was created will be handled. |
+ if (have_context) { |
+ if (executor_) |
+ executor_->ProcessPendingQueries(); |
+ ScheduleDelayedWork( |
+ base::TimeDelta::FromMilliseconds(kHandleMoreWorkPeriodMs)); |
+ } |
+ |
+ return handled; |
+} |
+ |
+bool GpuCommandBufferStub::Send(IPC::Message* message) { |
+ return channel_->Send(message); |
+} |
+ |
+bool GpuCommandBufferStub::IsScheduled() { |
+ return (!executor_.get() || executor_->scheduled()); |
+} |
+ |
+void GpuCommandBufferStub::PollWork() { |
+ // Post another delayed task if we have not yet reached the time at which |
+ // we should process delayed work. |
+ base::TimeTicks current_time = base::TimeTicks::Now(); |
+ DCHECK(!process_delayed_work_time_.is_null()); |
+ if (process_delayed_work_time_ > current_time) { |
+ task_runner_->PostDelayedTask( |
+ FROM_HERE, base::Bind(&GpuCommandBufferStub::PollWork, AsWeakPtr()), |
+ process_delayed_work_time_ - current_time); |
+ return; |
+ } |
+ process_delayed_work_time_ = base::TimeTicks(); |
+ |
+ PerformWork(); |
+} |
+ |
+void GpuCommandBufferStub::PerformWork() { |
+ TRACE_EVENT0("gpu", "GpuCommandBufferStub::PerformWork"); |
+ |
+ FastSetActiveURL(active_url_, active_url_hash_, channel_); |
+ if (decoder_.get() && !MakeCurrent()) |
+ return; |
+ |
+ if (executor_) { |
+ uint32_t current_unprocessed_num = |
+ channel()->gpu_channel_manager()->GetUnprocessedOrderNum(); |
+ // We're idle when no messages were processed or scheduled. |
+ bool is_idle = (previous_processed_num_ == current_unprocessed_num); |
+ if (!is_idle && !last_idle_time_.is_null()) { |
+ base::TimeDelta time_since_idle = |
+ base::TimeTicks::Now() - last_idle_time_; |
+ base::TimeDelta max_time_since_idle = |
+ base::TimeDelta::FromMilliseconds(kMaxTimeSinceIdleMs); |
+ |
+ // Force idle when it's been too long since last time we were idle. |
+ if (time_since_idle > max_time_since_idle) |
+ is_idle = true; |
+ } |
+ |
+ if (is_idle) { |
+ last_idle_time_ = base::TimeTicks::Now(); |
+ executor_->PerformIdleWork(); |
+ } |
+ |
+ executor_->ProcessPendingQueries(); |
+ } |
+ |
+ ScheduleDelayedWork( |
+ base::TimeDelta::FromMilliseconds(kHandleMoreWorkPeriodBusyMs)); |
+} |
+ |
+bool GpuCommandBufferStub::HasUnprocessedCommands() { |
+ if (command_buffer_) { |
+ gpu::CommandBuffer::State state = command_buffer_->GetLastState(); |
+ return command_buffer_->GetPutOffset() != state.get_offset && |
+ !gpu::error::IsError(state.error); |
+ } |
+ return false; |
+} |
+ |
+void GpuCommandBufferStub::ScheduleDelayedWork(base::TimeDelta delay) { |
+ bool has_more_work = executor_.get() && (executor_->HasPendingQueries() || |
+ executor_->HasMoreIdleWork()); |
+ if (!has_more_work) { |
+ last_idle_time_ = base::TimeTicks(); |
+ return; |
+ } |
+ |
+ base::TimeTicks current_time = base::TimeTicks::Now(); |
+ // |process_delayed_work_time_| is set if processing of delayed work is |
+ // already scheduled. Just update the time if already scheduled. |
+ if (!process_delayed_work_time_.is_null()) { |
+ process_delayed_work_time_ = current_time + delay; |
+ return; |
+ } |
+ |
+ // Idle when no messages are processed between now and when |
+ // PollWork is called. |
+ previous_processed_num_ = |
+ channel()->gpu_channel_manager()->GetProcessedOrderNum(); |
+ if (last_idle_time_.is_null()) |
+ last_idle_time_ = current_time; |
+ |
+ // IsScheduled() returns true after passing all unschedule fences |
+ // and this is when we can start performing idle work. Idle work |
+ // is done synchronously so we can set delay to 0 and instead poll |
+ // for more work at the rate idle work is performed. This also ensures |
+ // that idle work is done as efficiently as possible without any |
+ // unnecessary delays. |
+ if (executor_.get() && executor_->scheduled() && |
+ executor_->HasMoreIdleWork()) { |
+ delay = base::TimeDelta(); |
+ } |
+ |
+ process_delayed_work_time_ = current_time + delay; |
+ task_runner_->PostDelayedTask( |
+ FROM_HERE, base::Bind(&GpuCommandBufferStub::PollWork, AsWeakPtr()), |
+ delay); |
+} |
+ |
+bool GpuCommandBufferStub::MakeCurrent() { |
+ if (decoder_->MakeCurrent()) |
+ return true; |
+ DLOG(ERROR) << "Context lost because MakeCurrent failed."; |
+ command_buffer_->SetContextLostReason(decoder_->GetContextLostReason()); |
+ command_buffer_->SetParseError(gpu::error::kLostContext); |
+ CheckContextLost(); |
+ return false; |
+} |
+ |
+void GpuCommandBufferStub::Destroy() { |
+ if (wait_for_token_) { |
+ Send(wait_for_token_->reply.release()); |
+ wait_for_token_.reset(); |
+ } |
+ if (wait_for_get_offset_) { |
+ Send(wait_for_get_offset_->reply.release()); |
+ wait_for_get_offset_.reset(); |
+ } |
+ |
+ if (initialized_) { |
+ GpuChannelManager* gpu_channel_manager = channel_->gpu_channel_manager(); |
+ if ((surface_handle_ == gpu::kNullSurfaceHandle) && !active_url_.is_empty()) |
+ gpu_channel_manager->delegate()->DidDestroyOffscreenContext(active_url_); |
+ } |
+ |
+ if (decoder_) |
+ decoder_->set_engine(NULL); |
+ |
+ // The scheduler has raw references to the decoder and the command buffer so |
+ // destroy it before those. |
+ executor_.reset(); |
+ |
+ sync_point_client_.reset(); |
+ |
+ bool have_context = false; |
+ if (decoder_ && decoder_->GetGLContext()) { |
+ // Try to make the context current regardless of whether it was lost, so we |
+ // don't leak resources. |
+ have_context = decoder_->GetGLContext()->MakeCurrent(surface_.get()); |
+ } |
+ FOR_EACH_OBSERVER(DestructionObserver, |
+ destruction_observers_, |
+ OnWillDestroyStub()); |
+ |
+ if (decoder_) { |
+ decoder_->Destroy(have_context); |
+ decoder_.reset(); |
+ } |
+ |
+ command_buffer_.reset(); |
+ |
+ // Remove this after crbug.com/248395 is sorted out. |
+ surface_ = NULL; |
+} |
+ |
+void GpuCommandBufferStub::OnInitializeFailed(IPC::Message* reply_message) { |
+ Destroy(); |
+ GpuCommandBufferMsg_Initialize::WriteReplyParams( |
+ reply_message, false, gpu::Capabilities()); |
+ Send(reply_message); |
+} |
+ |
+scoped_refptr<gfx::GLSurface> GpuCommandBufferStub::CreateSurface() { |
+ GpuChannelManager* manager = channel_->gpu_channel_manager(); |
+ scoped_refptr<gfx::GLSurface> surface; |
+ if (surface_handle_ != gpu::kNullSurfaceHandle) { |
+ surface = ImageTransportSurface::CreateNativeSurface( |
+ manager, this, surface_handle_, surface_format_); |
+ if (!surface || !surface->Initialize(surface_format_)) |
+ return nullptr; |
+ } else { |
+ surface = manager->GetDefaultOffscreenSurface(); |
+ } |
+ return surface; |
+} |
+ |
+void GpuCommandBufferStub::OnInitialize( |
+ base::SharedMemoryHandle shared_state_handle, |
+ IPC::Message* reply_message) { |
+ TRACE_EVENT0("gpu", "GpuCommandBufferStub::OnInitialize"); |
+ DCHECK(!command_buffer_.get()); |
+ |
+ scoped_ptr<base::SharedMemory> shared_state_shm( |
+ new base::SharedMemory(shared_state_handle, false)); |
+ |
+ command_buffer_.reset(new gpu::CommandBufferService( |
+ context_group_->transfer_buffer_manager())); |
+ |
+ bool result = command_buffer_->Initialize(); |
+ DCHECK(result); |
+ |
+ GpuChannelManager* manager = channel_->gpu_channel_manager(); |
+ DCHECK(manager); |
+ |
+ decoder_.reset(::gpu::gles2::GLES2Decoder::Create(context_group_.get())); |
+ executor_.reset(new gpu::CommandExecutor(command_buffer_.get(), |
+ decoder_.get(), decoder_.get())); |
+ sync_point_client_ = sync_point_manager_->CreateSyncPointClient( |
+ channel_->GetSyncPointOrderData(stream_id_), |
+ gpu::CommandBufferNamespace::GPU_IO, command_buffer_id_); |
+ |
+ if (preemption_flag_.get()) |
+ executor_->SetPreemptByFlag(preemption_flag_); |
+ |
+ decoder_->set_engine(executor_.get()); |
+ |
+ surface_ = CreateSurface(); |
+ if (!surface_.get()) { |
+ DLOG(ERROR) << "Failed to create surface."; |
+ OnInitializeFailed(reply_message); |
+ return; |
+ } |
+ |
+ scoped_refptr<gfx::GLContext> context; |
+ gfx::GLShareGroup* share_group = channel_->share_group(); |
+ if (use_virtualized_gl_context_ && share_group) { |
+ context = share_group->GetSharedContext(); |
+ if (!context.get()) { |
+ context = gfx::GLContext::CreateGLContext( |
+ channel_->share_group(), |
+ channel_->gpu_channel_manager()->GetDefaultOffscreenSurface(), |
+ gpu_preference_); |
+ if (!context.get()) { |
+ DLOG(ERROR) << "Failed to create shared context for virtualization."; |
+ OnInitializeFailed(reply_message); |
+ return; |
+ } |
+ channel_->share_group()->SetSharedContext(context.get()); |
+ } |
+ // This should be a non-virtual GL context. |
+ DCHECK(context->GetHandle()); |
+ context = new gpu::GLContextVirtual( |
+ share_group, context.get(), decoder_->AsWeakPtr()); |
+ if (!context->Initialize(surface_.get(), gpu_preference_)) { |
+ // The real context created above for the default offscreen surface |
+ // might not be compatible with this surface. |
+ context = NULL; |
+ |
+ DLOG(ERROR) << "Failed to initialize virtual GL context."; |
+ OnInitializeFailed(reply_message); |
+ return; |
+ } |
+ } |
+ if (!context.get()) { |
+ context = gfx::GLContext::CreateGLContext( |
+ share_group, surface_.get(), gpu_preference_); |
+ } |
+ if (!context.get()) { |
+ DLOG(ERROR) << "Failed to create context."; |
+ OnInitializeFailed(reply_message); |
+ return; |
+ } |
+ |
+ if (!context->MakeCurrent(surface_.get())) { |
+ LOG(ERROR) << "Failed to make context current."; |
+ OnInitializeFailed(reply_message); |
+ return; |
+ } |
+ |
+ if (!context->GetGLStateRestorer()) { |
+ context->SetGLStateRestorer( |
+ new gpu::GLStateRestorerImpl(decoder_->AsWeakPtr())); |
+ } |
+ |
+ if (!context_group_->has_program_cache() && |
+ !context_group_->feature_info()->workarounds().disable_program_cache) { |
+ context_group_->set_program_cache( |
+ channel_->gpu_channel_manager()->program_cache()); |
+ } |
+ |
+ // Initialize the decoder with either the view or pbuffer GLContext. |
+ bool offscreen = (surface_handle_ == gpu::kNullSurfaceHandle); |
+ if (!decoder_->Initialize(surface_, context, offscreen, initial_size_, |
+ disallowed_features_, requested_attribs_)) { |
+ DLOG(ERROR) << "Failed to initialize decoder."; |
+ OnInitializeFailed(reply_message); |
+ return; |
+ } |
+ |
+ if (channel_->gpu_channel_manager()-> |
+ gpu_preferences().enable_gpu_service_logging) { |
+ decoder_->set_log_commands(true); |
+ } |
+ |
+ decoder_->GetLogger()->SetMsgCallback( |
+ base::Bind(&GpuCommandBufferStub::SendConsoleMessage, |
+ base::Unretained(this))); |
+ decoder_->SetShaderCacheCallback( |
+ base::Bind(&GpuCommandBufferStub::SendCachedShader, |
+ base::Unretained(this))); |
+ decoder_->SetFenceSyncReleaseCallback(base::Bind( |
+ &GpuCommandBufferStub::OnFenceSyncRelease, base::Unretained(this))); |
+ decoder_->SetWaitFenceSyncCallback(base::Bind( |
+ &GpuCommandBufferStub::OnWaitFenceSync, base::Unretained(this))); |
+ |
+ command_buffer_->SetPutOffsetChangeCallback( |
+ base::Bind(&GpuCommandBufferStub::PutChanged, base::Unretained(this))); |
+ command_buffer_->SetGetBufferChangeCallback(base::Bind( |
+ &gpu::CommandExecutor::SetGetBuffer, base::Unretained(executor_.get()))); |
+ command_buffer_->SetParseErrorCallback( |
+ base::Bind(&GpuCommandBufferStub::OnParseError, base::Unretained(this))); |
+ executor_->SetSchedulingChangedCallback(base::Bind( |
+ &GpuCommandBufferStub::OnSchedulingChanged, base::Unretained(this))); |
+ |
+ if (watchdog_) { |
+ executor_->SetCommandProcessedCallback(base::Bind( |
+ &GpuCommandBufferStub::OnCommandProcessed, base::Unretained(this))); |
+ } |
+ |
+ const size_t kSharedStateSize = sizeof(gpu::CommandBufferSharedState); |
+ if (!shared_state_shm->Map(kSharedStateSize)) { |
+ DLOG(ERROR) << "Failed to map shared state buffer."; |
+ OnInitializeFailed(reply_message); |
+ return; |
+ } |
+ command_buffer_->SetSharedStateBuffer(gpu::MakeBackingFromSharedMemory( |
+ std::move(shared_state_shm), kSharedStateSize)); |
+ |
+ gpu::Capabilities capabilities = decoder_->GetCapabilities(); |
+ |
+ GpuCommandBufferMsg_Initialize::WriteReplyParams( |
+ reply_message, true, capabilities); |
+ Send(reply_message); |
+ |
+ if ((surface_handle_ == gpu::kNullSurfaceHandle) && !active_url_.is_empty()) |
+ manager->delegate()->DidCreateOffscreenContext(active_url_); |
+ |
+ initialized_ = true; |
+} |
+ |
+void GpuCommandBufferStub::OnCreateStreamTexture(uint32_t texture_id, |
+ int32_t stream_id, |
+ bool* succeeded) { |
+#if defined(OS_ANDROID) |
+ *succeeded = StreamTexture::Create(this, texture_id, stream_id); |
+#else |
+ *succeeded = false; |
+#endif |
+} |
+ |
+void GpuCommandBufferStub::SetLatencyInfoCallback( |
+ const LatencyInfoCallback& callback) { |
+ latency_info_callback_ = callback; |
+} |
+ |
+int32_t GpuCommandBufferStub::GetRequestedAttribute(int attr) const { |
+ // The command buffer is pairs of enum, value |
+ // search for the requested attribute, return the value. |
+ for (std::vector<int32_t>::const_iterator it = requested_attribs_.begin(); |
+ it != requested_attribs_.end(); ++it) { |
+ if (*it++ == attr) { |
+ return *it; |
+ } |
+ } |
+ return -1; |
+} |
+ |
+void GpuCommandBufferStub::OnSetGetBuffer(int32_t shm_id, |
+ IPC::Message* reply_message) { |
+ TRACE_EVENT0("gpu", "GpuCommandBufferStub::OnSetGetBuffer"); |
+ if (command_buffer_) |
+ command_buffer_->SetGetBuffer(shm_id); |
+ Send(reply_message); |
+} |
+ |
+void GpuCommandBufferStub::OnProduceFrontBuffer(const gpu::Mailbox& mailbox) { |
+ TRACE_EVENT0("gpu", "GpuCommandBufferStub::OnProduceFrontBuffer"); |
+ if (!decoder_) { |
+ LOG(ERROR) << "Can't produce front buffer before initialization."; |
+ return; |
+ } |
+ |
+ decoder_->ProduceFrontBuffer(mailbox); |
+} |
+ |
+void GpuCommandBufferStub::OnParseError() { |
+ TRACE_EVENT0("gpu", "GpuCommandBufferStub::OnParseError"); |
+ DCHECK(command_buffer_.get()); |
+ gpu::CommandBuffer::State state = command_buffer_->GetLastState(); |
+ IPC::Message* msg = new GpuCommandBufferMsg_Destroyed( |
+ route_id_, state.context_lost_reason, state.error); |
+ msg->set_unblock(true); |
+ Send(msg); |
+ |
+ // Tell the browser about this context loss as well, so it can |
+ // determine whether client APIs like WebGL need to be immediately |
+ // blocked from automatically running. |
+ GpuChannelManager* gpu_channel_manager = channel_->gpu_channel_manager(); |
+ gpu_channel_manager->delegate()->DidLoseContext( |
+ (surface_handle_ == gpu::kNullSurfaceHandle), state.context_lost_reason, |
+ active_url_); |
+ |
+ CheckContextLost(); |
+} |
+ |
+void GpuCommandBufferStub::OnSchedulingChanged(bool scheduled) { |
+ TRACE_EVENT1("gpu", "GpuCommandBufferStub::OnSchedulingChanged", "scheduled", |
+ scheduled); |
+ channel_->OnStreamRescheduled(stream_id_, scheduled); |
+} |
+ |
+void GpuCommandBufferStub::OnWaitForTokenInRange(int32_t start, |
+ int32_t end, |
+ IPC::Message* reply_message) { |
+ TRACE_EVENT0("gpu", "GpuCommandBufferStub::OnWaitForTokenInRange"); |
+ DCHECK(command_buffer_.get()); |
+ CheckContextLost(); |
+ if (wait_for_token_) |
+ LOG(ERROR) << "Got WaitForToken command while currently waiting for token."; |
+ wait_for_token_ = |
+ make_scoped_ptr(new WaitForCommandState(start, end, reply_message)); |
+ CheckCompleteWaits(); |
+} |
+ |
+void GpuCommandBufferStub::OnWaitForGetOffsetInRange( |
+ int32_t start, |
+ int32_t end, |
+ IPC::Message* reply_message) { |
+ TRACE_EVENT0("gpu", "GpuCommandBufferStub::OnWaitForGetOffsetInRange"); |
+ DCHECK(command_buffer_.get()); |
+ CheckContextLost(); |
+ if (wait_for_get_offset_) { |
+ LOG(ERROR) |
+ << "Got WaitForGetOffset command while currently waiting for offset."; |
+ } |
+ wait_for_get_offset_ = |
+ make_scoped_ptr(new WaitForCommandState(start, end, reply_message)); |
+ CheckCompleteWaits(); |
+} |
+ |
+void GpuCommandBufferStub::CheckCompleteWaits() { |
+ if (wait_for_token_ || wait_for_get_offset_) { |
+ gpu::CommandBuffer::State state = command_buffer_->GetLastState(); |
+ if (wait_for_token_ && |
+ (gpu::CommandBuffer::InRange( |
+ wait_for_token_->start, wait_for_token_->end, state.token) || |
+ state.error != gpu::error::kNoError)) { |
+ ReportState(); |
+ GpuCommandBufferMsg_WaitForTokenInRange::WriteReplyParams( |
+ wait_for_token_->reply.get(), state); |
+ Send(wait_for_token_->reply.release()); |
+ wait_for_token_.reset(); |
+ } |
+ if (wait_for_get_offset_ && |
+ (gpu::CommandBuffer::InRange(wait_for_get_offset_->start, |
+ wait_for_get_offset_->end, |
+ state.get_offset) || |
+ state.error != gpu::error::kNoError)) { |
+ ReportState(); |
+ GpuCommandBufferMsg_WaitForGetOffsetInRange::WriteReplyParams( |
+ wait_for_get_offset_->reply.get(), state); |
+ Send(wait_for_get_offset_->reply.release()); |
+ wait_for_get_offset_.reset(); |
+ } |
+ } |
+} |
+ |
+void GpuCommandBufferStub::OnAsyncFlush( |
+ int32_t put_offset, |
+ uint32_t flush_count, |
+ const std::vector<ui::LatencyInfo>& latency_info) { |
+ TRACE_EVENT1( |
+ "gpu", "GpuCommandBufferStub::OnAsyncFlush", "put_offset", put_offset); |
+ DCHECK(command_buffer_); |
+ |
+ // We received this message out-of-order. This should not happen but is here |
+ // to catch regressions. Ignore the message. |
+ DVLOG_IF(0, flush_count - last_flush_count_ >= 0x8000000U) |
+ << "Received a Flush message out-of-order"; |
+ |
+ if (flush_count > last_flush_count_ && |
+ ui::LatencyInfo::Verify(latency_info, |
+ "GpuCommandBufferStub::OnAsyncFlush") && |
+ !latency_info_callback_.is_null()) { |
+ latency_info_callback_.Run(latency_info); |
+ } |
+ |
+ last_flush_count_ = flush_count; |
+ gpu::CommandBuffer::State pre_state = command_buffer_->GetLastState(); |
+ command_buffer_->Flush(put_offset); |
+ gpu::CommandBuffer::State post_state = command_buffer_->GetLastState(); |
+ |
+ if (pre_state.get_offset != post_state.get_offset) |
+ ReportState(); |
+ |
+#if defined(OS_ANDROID) |
+ GpuChannelManager* manager = channel_->gpu_channel_manager(); |
+ manager->DidAccessGpu(); |
+#endif |
+} |
+ |
+void GpuCommandBufferStub::OnRegisterTransferBuffer( |
+ int32_t id, |
+ base::SharedMemoryHandle transfer_buffer, |
+ uint32_t size) { |
+ TRACE_EVENT0("gpu", "GpuCommandBufferStub::OnRegisterTransferBuffer"); |
+ |
+ // Take ownership of the memory and map it into this process. |
+ // This validates the size. |
+ scoped_ptr<base::SharedMemory> shared_memory( |
+ new base::SharedMemory(transfer_buffer, false)); |
+ if (!shared_memory->Map(size)) { |
+ DVLOG(0) << "Failed to map shared memory."; |
+ return; |
+ } |
+ |
+ if (command_buffer_) { |
+ command_buffer_->RegisterTransferBuffer( |
+ id, gpu::MakeBackingFromSharedMemory(std::move(shared_memory), size)); |
+ } |
+} |
+ |
+void GpuCommandBufferStub::OnDestroyTransferBuffer(int32_t id) { |
+ TRACE_EVENT0("gpu", "GpuCommandBufferStub::OnDestroyTransferBuffer"); |
+ |
+ if (command_buffer_) |
+ command_buffer_->DestroyTransferBuffer(id); |
+} |
+ |
+void GpuCommandBufferStub::OnCommandProcessed() { |
+ if (watchdog_) |
+ watchdog_->CheckArmed(); |
+} |
+ |
+void GpuCommandBufferStub::ReportState() { command_buffer_->UpdateState(); } |
+ |
+void GpuCommandBufferStub::PutChanged() { |
+ FastSetActiveURL(active_url_, active_url_hash_, channel_); |
+ executor_->PutChanged(); |
+} |
+ |
+void GpuCommandBufferStub::PullTextureUpdates( |
+ gpu::CommandBufferNamespace namespace_id, |
+ gpu::CommandBufferId command_buffer_id, |
+ uint32_t release) { |
+ gpu::gles2::MailboxManager* mailbox_manager = |
+ context_group_->mailbox_manager(); |
+ if (mailbox_manager->UsesSync() && MakeCurrent()) { |
+ gpu::SyncToken sync_token(namespace_id, 0, command_buffer_id, release); |
+ mailbox_manager->PullTextureUpdates(sync_token); |
+ } |
+} |
+ |
+void GpuCommandBufferStub::OnSignalSyncToken(const gpu::SyncToken& sync_token, |
+ uint32_t id) { |
+ scoped_refptr<gpu::SyncPointClientState> release_state = |
+ sync_point_manager_->GetSyncPointClientState( |
+ sync_token.namespace_id(), sync_token.command_buffer_id()); |
+ |
+ if (release_state) { |
+ sync_point_client_->Wait(release_state.get(), sync_token.release_count(), |
+ base::Bind(&GpuCommandBufferStub::OnSignalAck, |
+ this->AsWeakPtr(), id)); |
+ } else { |
+ OnSignalAck(id); |
+ } |
+} |
+ |
+void GpuCommandBufferStub::OnSignalAck(uint32_t id) { |
+ Send(new GpuCommandBufferMsg_SignalAck(route_id_, id)); |
+} |
+ |
+void GpuCommandBufferStub::OnSignalQuery(uint32_t query_id, uint32_t id) { |
+ if (decoder_) { |
+ gpu::gles2::QueryManager* query_manager = decoder_->GetQueryManager(); |
+ if (query_manager) { |
+ gpu::gles2::QueryManager::Query* query = |
+ query_manager->GetQuery(query_id); |
+ if (query) { |
+ query->AddCallback( |
+ base::Bind(&GpuCommandBufferStub::OnSignalAck, |
+ this->AsWeakPtr(), |
+ id)); |
+ return; |
+ } |
+ } |
+ } |
+ // Something went wrong, run callback immediately. |
+ OnSignalAck(id); |
+} |
+ |
+void GpuCommandBufferStub::OnFenceSyncRelease(uint64_t release) { |
+ if (sync_point_client_->client_state()->IsFenceSyncReleased(release)) { |
+ DLOG(ERROR) << "Fence Sync has already been released."; |
+ return; |
+ } |
+ |
+ gpu::gles2::MailboxManager* mailbox_manager = |
+ context_group_->mailbox_manager(); |
+ if (mailbox_manager->UsesSync() && MakeCurrent()) { |
+ gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO, 0, |
+ command_buffer_id_, release); |
+ mailbox_manager->PushTextureUpdates(sync_token); |
+ } |
+ |
+ sync_point_client_->ReleaseFenceSync(release); |
+} |
+ |
+bool GpuCommandBufferStub::OnWaitFenceSync( |
+ gpu::CommandBufferNamespace namespace_id, |
+ gpu::CommandBufferId command_buffer_id, |
+ uint64_t release) { |
+ DCHECK(!waiting_for_sync_point_); |
+ DCHECK(executor_->scheduled()); |
+ |
+ scoped_refptr<gpu::SyncPointClientState> release_state = |
+ sync_point_manager_->GetSyncPointClientState(namespace_id, |
+ command_buffer_id); |
+ |
+ if (!release_state) |
+ return true; |
+ |
+ if (release_state->IsFenceSyncReleased(release)) { |
+ PullTextureUpdates(namespace_id, command_buffer_id, release); |
+ return true; |
+ } |
+ |
+ TRACE_EVENT_ASYNC_BEGIN1("gpu", "WaitFenceSync", this, "GpuCommandBufferStub", |
+ this); |
+ waiting_for_sync_point_ = true; |
+ sync_point_client_->WaitNonThreadSafe( |
+ release_state.get(), release, task_runner_, |
+ base::Bind(&GpuCommandBufferStub::OnWaitFenceSyncCompleted, |
+ this->AsWeakPtr(), namespace_id, command_buffer_id, release)); |
+ |
+ if (!waiting_for_sync_point_) |
+ return true; |
+ |
+ executor_->SetScheduled(false); |
+ return false; |
+} |
+ |
+void GpuCommandBufferStub::OnWaitFenceSyncCompleted( |
+ gpu::CommandBufferNamespace namespace_id, |
+ gpu::CommandBufferId command_buffer_id, |
+ uint64_t release) { |
+ DCHECK(waiting_for_sync_point_); |
+ TRACE_EVENT_ASYNC_END1("gpu", "WaitFenceSync", this, "GpuCommandBufferStub", |
+ this); |
+ PullTextureUpdates(namespace_id, command_buffer_id, release); |
+ waiting_for_sync_point_ = false; |
+ executor_->SetScheduled(true); |
+} |
+ |
+void GpuCommandBufferStub::OnCreateImage( |
+ const GpuCommandBufferMsg_CreateImage_Params& params) { |
+ TRACE_EVENT0("gpu", "GpuCommandBufferStub::OnCreateImage"); |
+ const int32_t id = params.id; |
+ const gfx::GpuMemoryBufferHandle& handle = params.gpu_memory_buffer; |
+ const gfx::Size& size = params.size; |
+ const gfx::BufferFormat& format = params.format; |
+ const uint32_t internalformat = params.internal_format; |
+ const uint64_t image_release_count = params.image_release_count; |
+ |
+ if (!decoder_) |
+ return; |
+ |
+ gpu::gles2::ImageManager* image_manager = decoder_->GetImageManager(); |
+ DCHECK(image_manager); |
+ if (image_manager->LookupImage(id)) { |
+ LOG(ERROR) << "Image already exists with same ID."; |
+ return; |
+ } |
+ |
+ if (!gpu::ImageFactory::IsGpuMemoryBufferFormatSupported( |
+ format, decoder_->GetCapabilities())) { |
+ LOG(ERROR) << "Format is not supported."; |
+ return; |
+ } |
+ |
+ if (!gpu::ImageFactory::IsImageSizeValidForGpuMemoryBufferFormat(size, |
+ format)) { |
+ LOG(ERROR) << "Invalid image size for format."; |
+ return; |
+ } |
+ |
+ if (!gpu::ImageFactory::IsImageFormatCompatibleWithGpuMemoryBufferFormat( |
+ internalformat, format)) { |
+ LOG(ERROR) << "Incompatible image format."; |
+ return; |
+ } |
+ |
+ scoped_refptr<gl::GLImage> image = channel()->CreateImageForGpuMemoryBuffer( |
+ handle, size, format, internalformat); |
+ if (!image.get()) |
+ return; |
+ |
+ image_manager->AddImage(image.get(), id); |
+ if (image_release_count) { |
+ sync_point_client_->ReleaseFenceSync(image_release_count); |
+ } |
+} |
+ |
+void GpuCommandBufferStub::OnDestroyImage(int32_t id) { |
+ TRACE_EVENT0("gpu", "GpuCommandBufferStub::OnDestroyImage"); |
+ |
+ if (!decoder_) |
+ return; |
+ |
+ gpu::gles2::ImageManager* image_manager = decoder_->GetImageManager(); |
+ DCHECK(image_manager); |
+ if (!image_manager->LookupImage(id)) { |
+ LOG(ERROR) << "Image with ID doesn't exist."; |
+ return; |
+ } |
+ |
+ image_manager->RemoveImage(id); |
+} |
+ |
+void GpuCommandBufferStub::SendConsoleMessage(int32_t id, |
+ const std::string& message) { |
+ GPUCommandBufferConsoleMessage console_message; |
+ console_message.id = id; |
+ console_message.message = message; |
+ IPC::Message* msg = new GpuCommandBufferMsg_ConsoleMsg( |
+ route_id_, console_message); |
+ msg->set_unblock(true); |
+ Send(msg); |
+} |
+ |
+void GpuCommandBufferStub::SendCachedShader( |
+ const std::string& key, const std::string& shader) { |
+ channel_->CacheShader(key, shader); |
+} |
+ |
+void GpuCommandBufferStub::AddDestructionObserver( |
+ DestructionObserver* observer) { |
+ destruction_observers_.AddObserver(observer); |
+} |
+ |
+void GpuCommandBufferStub::RemoveDestructionObserver( |
+ DestructionObserver* observer) { |
+ destruction_observers_.RemoveObserver(observer); |
+} |
+ |
+const gpu::gles2::FeatureInfo* GpuCommandBufferStub::GetFeatureInfo() const { |
+ return context_group_->feature_info(); |
+} |
+ |
+gpu::gles2::MemoryTracker* GpuCommandBufferStub::GetMemoryTracker() const { |
+ return context_group_->memory_tracker(); |
+} |
+ |
+bool GpuCommandBufferStub::CheckContextLost() { |
+ DCHECK(command_buffer_); |
+ gpu::CommandBuffer::State state = command_buffer_->GetLastState(); |
+ bool was_lost = state.error == gpu::error::kLostContext; |
+ |
+ if (was_lost) { |
+ bool was_lost_by_robustness = |
+ decoder_ && decoder_->WasContextLostByRobustnessExtension(); |
+ |
+ // Work around issues with recovery by allowing a new GPU process to launch. |
+ if ((was_lost_by_robustness || |
+ context_group_->feature_info()->workarounds().exit_on_context_lost) && |
+ !channel_->gpu_channel_manager()->gpu_preferences().single_process && |
+ !channel_->gpu_channel_manager()->gpu_preferences().in_process_gpu) { |
+ LOG(ERROR) << "Exiting GPU process because some drivers cannot recover" |
+ << " from problems."; |
+ // Signal the message loop to quit to shut down other threads |
+ // gracefully. |
+ base::MessageLoop::current()->QuitNow(); |
+ } |
+ |
+ // Lose all other contexts if the reset was triggered by the robustness |
+ // extension instead of being synthetic. |
+ if (was_lost_by_robustness && |
+ (gfx::GLContext::LosesAllContextsOnContextLost() || |
+ use_virtualized_gl_context_)) { |
+ channel_->LoseAllContexts(); |
+ } |
+ } |
+ |
+ CheckCompleteWaits(); |
+ return was_lost; |
+} |
+ |
+void GpuCommandBufferStub::MarkContextLost() { |
+ if (!command_buffer_ || |
+ command_buffer_->GetLastState().error == gpu::error::kLostContext) |
+ return; |
+ |
+ command_buffer_->SetContextLostReason(gpu::error::kUnknown); |
+ if (decoder_) |
+ decoder_->MarkContextLost(gpu::error::kUnknown); |
+ command_buffer_->SetParseError(gpu::error::kLostContext); |
+} |
+ |
+void GpuCommandBufferStub::SendSwapBuffersCompleted( |
+ const std::vector<ui::LatencyInfo>& latency_info, |
+ gfx::SwapResult result) { |
+ Send(new GpuCommandBufferMsg_SwapBuffersCompleted(route_id_, latency_info, |
+ result)); |
+} |
+ |
+void GpuCommandBufferStub::SendUpdateVSyncParameters(base::TimeTicks timebase, |
+ base::TimeDelta interval) { |
+ Send(new GpuCommandBufferMsg_UpdateVSyncParameters(route_id_, timebase, |
+ interval)); |
+} |
+ |
+} // namespace content |