| // Copyright 2014 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 "components/viz/service/display_embedder/gl_output_surface.h" |
| |
| #include <utility> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "cc/base/math_util.h" |
| #include "components/viz/common/frame_sinks/begin_frame_source.h" |
| #include "components/viz/common/gpu/context_provider.h" |
| #include "components/viz/service/display/output_surface_client.h" |
| #include "components/viz/service/display/output_surface_frame.h" |
| #include "components/viz/service/display/renderer_utils.h" |
| #include "gpu/command_buffer/client/context_support.h" |
| #include "gpu/command_buffer/client/gles2_interface.h" |
| #include "gpu/command_buffer/common/swap_buffers_complete_params.h" |
| #include "gpu/command_buffer/common/swap_buffers_flags.h" |
| #include "ui/gfx/overlay_transform_utils.h" |
| #include "ui/gl/color_space_utils.h" |
| |
| namespace viz { |
| |
| GLOutputSurface::GLOutputSurface( |
| scoped_refptr<VizProcessContextProvider> context_provider, |
| gpu::SurfaceHandle surface_handle) |
| : OutputSurface(context_provider), |
| viz_context_provider_(context_provider), |
| surface_handle_(surface_handle), |
| use_gpu_fence_( |
| context_provider->ContextCapabilities().chromium_gpu_fence && |
| context_provider->ContextCapabilities() |
| .use_gpu_fences_for_overlay_planes), |
| weak_ptr_factory_(this) { |
| capabilities_.flipped_output_surface = |
| context_provider->ContextCapabilities().flips_vertically; |
| capabilities_.supports_stencil = |
| context_provider->ContextCapabilities().num_stencil_bits > 0; |
| // Since one of the buffers is used by the surface for presentation, there can |
| // be at most |num_surface_buffers - 1| pending buffers that the compositor |
| // can use. |
| capabilities_.max_frames_pending = |
| context_provider->ContextCapabilities().num_surface_buffers - 1; |
| capabilities_.supports_gpu_vsync = |
| context_provider->ContextCapabilities().gpu_vsync; |
| } |
| |
| GLOutputSurface::~GLOutputSurface() { |
| viz_context_provider_->SetUpdateVSyncParametersCallback( |
| UpdateVSyncParametersCallback()); |
| viz_context_provider_->SetGpuVSyncCallback(GpuVSyncCallback()); |
| if (gpu_fence_id_ > 0) |
| context_provider()->ContextGL()->DestroyGpuFenceCHROMIUM(gpu_fence_id_); |
| } |
| |
| void GLOutputSurface::BindToClient(OutputSurfaceClient* client) { |
| DCHECK(client); |
| DCHECK(!client_); |
| client_ = client; |
| } |
| |
| void GLOutputSurface::EnsureBackbuffer() {} |
| |
| void GLOutputSurface::DiscardBackbuffer() { |
| context_provider()->ContextGL()->DiscardBackbufferCHROMIUM(); |
| } |
| |
| void GLOutputSurface::BindFramebuffer() { |
| context_provider()->ContextGL()->BindFramebuffer(GL_FRAMEBUFFER, 0); |
| } |
| |
| void GLOutputSurface::SetDrawRectangle(const gfx::Rect& rect) { |
| if (!context_provider_->ContextCapabilities().dc_layers) |
| return; |
| |
| if (set_draw_rectangle_for_frame_) |
| return; |
| DCHECK(gfx::Rect(size_).Contains(rect)); |
| DCHECK(has_set_draw_rectangle_since_last_resize_ || |
| (gfx::Rect(size_) == rect)); |
| set_draw_rectangle_for_frame_ = true; |
| has_set_draw_rectangle_since_last_resize_ = true; |
| context_provider()->ContextGL()->SetDrawRectangleCHROMIUM( |
| rect.x(), rect.y(), rect.width(), rect.height()); |
| } |
| |
| void GLOutputSurface::Reshape(const gfx::Size& size, |
| float device_scale_factor, |
| const gfx::ColorSpace& color_space, |
| bool has_alpha, |
| bool use_stencil) { |
| size_ = size; |
| has_set_draw_rectangle_since_last_resize_ = false; |
| context_provider()->ContextGL()->ResizeCHROMIUM( |
| size.width(), size.height(), device_scale_factor, |
| gl::ColorSpaceUtils::GetGLColorSpace(color_space), has_alpha); |
| } |
| |
| void GLOutputSurface::SwapBuffers(OutputSurfaceFrame frame) { |
| DCHECK(context_provider_); |
| |
| uint32_t flags = 0; |
| if (wants_vsync_parameter_updates_) |
| flags |= gpu::SwapBuffersFlags::kVSyncParams; |
| |
| // The |swap_size| here should always be in the UI's logical screen space |
| // since it is forwarded to the client code which is unaware of the display |
| // transform optimization. |
| gfx::Size swap_size = ApplyDisplayInverse(gfx::Rect(size_)).size(); |
| auto swap_callback = base::BindOnce( |
| &GLOutputSurface::OnGpuSwapBuffersCompleted, |
| weak_ptr_factory_.GetWeakPtr(), std::move(frame.latency_info), swap_size); |
| gpu::ContextSupport::PresentationCallback presentation_callback; |
| presentation_callback = base::BindOnce(&GLOutputSurface::OnPresentation, |
| weak_ptr_factory_.GetWeakPtr()); |
| |
| set_draw_rectangle_for_frame_ = false; |
| if (frame.sub_buffer_rect) { |
| HandlePartialSwap(*frame.sub_buffer_rect, flags, std::move(swap_callback), |
| std::move(presentation_callback)); |
| } else if (!frame.content_bounds.empty()) { |
| context_provider_->ContextSupport()->SwapWithBounds( |
| frame.content_bounds, flags, std::move(swap_callback), |
| std::move(presentation_callback)); |
| } else { |
| context_provider_->ContextSupport()->Swap(flags, std::move(swap_callback), |
| std::move(presentation_callback)); |
| } |
| } |
| |
| uint32_t GLOutputSurface::GetFramebufferCopyTextureFormat() { |
| auto* gl = static_cast<VizProcessContextProvider*>(context_provider()); |
| return gl->GetCopyTextureInternalFormat(); |
| } |
| |
| bool GLOutputSurface::IsDisplayedAsOverlayPlane() const { |
| return false; |
| } |
| |
| unsigned GLOutputSurface::GetOverlayTextureId() const { |
| return 0; |
| } |
| |
| gfx::BufferFormat GLOutputSurface::GetOverlayBufferFormat() const { |
| return gfx::BufferFormat::RGBX_8888; |
| } |
| |
| bool GLOutputSurface::HasExternalStencilTest() const { |
| return false; |
| } |
| |
| void GLOutputSurface::ApplyExternalStencil() {} |
| |
| void GLOutputSurface::DidReceiveSwapBuffersAck( |
| const gfx::SwapResponse& response) { |
| client_->DidReceiveSwapBuffersAck(response.timings); |
| } |
| |
| void GLOutputSurface::HandlePartialSwap( |
| const gfx::Rect& sub_buffer_rect, |
| uint32_t flags, |
| gpu::ContextSupport::SwapCompletedCallback swap_callback, |
| gpu::ContextSupport::PresentationCallback presentation_callback) { |
| context_provider_->ContextSupport()->PartialSwapBuffers( |
| sub_buffer_rect, flags, std::move(swap_callback), |
| std::move(presentation_callback)); |
| } |
| |
| void GLOutputSurface::OnGpuSwapBuffersCompleted( |
| std::vector<ui::LatencyInfo> latency_info, |
| const gfx::Size& pixel_size, |
| const gpu::SwapBuffersCompleteParams& params) { |
| if (!params.texture_in_use_responses.empty()) |
| client_->DidReceiveTextureInUseResponses(params.texture_in_use_responses); |
| if (!params.ca_layer_params.is_empty) |
| client_->DidReceiveCALayerParams(params.ca_layer_params); |
| DidReceiveSwapBuffersAck(params.swap_response); |
| |
| UpdateLatencyInfoOnSwap(params.swap_response, &latency_info); |
| latency_tracker_.OnGpuSwapBuffersCompleted(latency_info); |
| client_->DidFinishLatencyInfo(latency_info); |
| |
| if (needs_swap_size_notifications_) |
| client_->DidSwapWithSize(pixel_size); |
| } |
| |
| void GLOutputSurface::OnPresentation( |
| const gfx::PresentationFeedback& feedback) { |
| client_->DidReceivePresentationFeedback(feedback); |
| } |
| |
| unsigned GLOutputSurface::UpdateGpuFence() { |
| if (!use_gpu_fence_) |
| return 0; |
| |
| if (gpu_fence_id_ > 0) |
| context_provider()->ContextGL()->DestroyGpuFenceCHROMIUM(gpu_fence_id_); |
| |
| gpu_fence_id_ = context_provider()->ContextGL()->CreateGpuFenceCHROMIUM(); |
| |
| return gpu_fence_id_; |
| } |
| |
| void GLOutputSurface::SetNeedsSwapSizeNotifications( |
| bool needs_swap_size_notifications) { |
| needs_swap_size_notifications_ = needs_swap_size_notifications; |
| } |
| |
| void GLOutputSurface::SetUpdateVSyncParametersCallback( |
| UpdateVSyncParametersCallback callback) { |
| wants_vsync_parameter_updates_ = !callback.is_null(); |
| viz_context_provider_->SetUpdateVSyncParametersCallback(std::move(callback)); |
| } |
| |
| void GLOutputSurface::SetGpuVSyncCallback(GpuVSyncCallback callback) { |
| DCHECK(capabilities_.supports_gpu_vsync); |
| viz_context_provider_->SetGpuVSyncCallback(std::move(callback)); |
| } |
| |
| void GLOutputSurface::SetGpuVSyncEnabled(bool enabled) { |
| DCHECK(capabilities_.supports_gpu_vsync); |
| viz_context_provider_->SetGpuVSyncEnabled(enabled); |
| } |
| |
| gfx::OverlayTransform GLOutputSurface::GetDisplayTransform() { |
| return gfx::OVERLAY_TRANSFORM_NONE; |
| } |
| |
| gfx::Rect GLOutputSurface::ApplyDisplayInverse(const gfx::Rect& input) { |
| gfx::Transform display_inverse = gfx::OverlayTransformToTransform( |
| gfx::InvertOverlayTransform(GetDisplayTransform()), size_); |
| return cc::MathUtil::MapEnclosedRectWith2dAxisAlignedTransform( |
| display_inverse, input); |
| } |
| |
| base::ScopedClosureRunner GLOutputSurface::GetCacheBackBufferCb() { |
| return viz_context_provider_->GetCacheBackBufferCb(); |
| } |
| |
| gpu::SurfaceHandle GLOutputSurface::GetSurfaceHandle() const { |
| return surface_handle_; |
| } |
| } // namespace viz |