capture_mode: Add support for window capture
Implement the proposal presented in the design doc
http://go/window-capturing to make the
FrameSinkVideoCapturer able to record non-root windows.
tl;dr The idea is to tag the layer of the non-root window
with a unique ID (of type viz::SubtreeCaptureId) that will
result in forcing this sub layer tree into a render surface
that draw into a render pass identifiable by that ID to the
FrameSinkVideoCapturerImpl on the viz side.
BUG=1143930
TEST=- Manually.
- Added new tests:
- (viz_unittests) CompositorFrameSinkSupportTest.CopyRequestOnSubtree
- (aura_unittests) WindowTest.*Capturable*
- (ash_unittetss) CaptureModeTest.WindowRecordingCaptureId
- (browser_tests) RecordingServiceBrowserTest.RecordWindow
- (cc_unittests) *LayerTreeHostCaptureTest*
- Modified many tests else where.
Change-Id: Iede1b923db391e6f390bb78a1d1aaf848ad7ab33
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2593724
Commit-Queue: Ahmed Fakhry <afakhry@chromium.org>
Reviewed-by: Scott Violet <sky@chromium.org>
Reviewed-by: kylechar <kylechar@chromium.org>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Reviewed-by: danakj <danakj@chromium.org>
Reviewed-by: James Cook <jamescook@chromium.org>
Reviewed-by: Xianzhu Wang <wangxianzhu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#840924}
diff --git a/android_webview/browser/gfx/hardware_renderer_single_thread.cc b/android_webview/browser/gfx/hardware_renderer_single_thread.cc
index 620320d..931765c2 100644
--- a/android_webview/browser/gfx/hardware_renderer_single_thread.cc
+++ b/android_webview/browser/gfx/hardware_renderer_single_thread.cc
@@ -119,7 +119,8 @@
CopyOutputRequestQueue requests;
requests.swap(child_frame_->copy_requests);
for (auto& copy_request : requests) {
- support_->RequestCopyOfOutput(child_id_, std::move(copy_request));
+ support_->RequestCopyOfOutput(
+ {child_id_, viz::SubtreeCaptureId(), std::move(copy_request)});
}
gfx::Rect clip(params.clip_left, params.clip_top,
diff --git a/ash/capture_mode/capture_mode_controller.cc b/ash/capture_mode/capture_mode_controller.cc
index dd637720..ea45c251 100644
--- a/ash/capture_mode/capture_mode_controller.cc
+++ b/ash/capture_mode/capture_mode_controller.cc
@@ -425,7 +425,6 @@
void CaptureModeController::OnRecordingEnded(bool success) {
delegate_->StopObservingRestrictedContent();
- window_frame_sink_.reset();
// If |success| is false, then recording has been force-terminated due to a
// failure on the service side, or a disconnection to it. We need to terminate
@@ -579,12 +578,9 @@
audio_stream_factory.InitWithNewPipeAndPassReceiver());
}
- auto frame_sink_id = capture_params.window->GetFrameSinkId();
- if (!frame_sink_id.is_valid()) {
- window_frame_sink_ = capture_params.window->CreateLayerTreeFrameSink();
- frame_sink_id = capture_params.window->GetFrameSinkId();
- DCHECK(frame_sink_id.is_valid());
- }
+ const auto frame_sink_id =
+ capture_params.window->GetRootWindow()->GetFrameSinkId();
+
const auto bounds = capture_params.bounds;
switch (source_) {
case CaptureModeSource::kFullscreen:
@@ -594,11 +590,18 @@
break;
case CaptureModeSource::kWindow:
- // TODO(crbug.com/1143930): Window recording doesn't produce any frames at
- // the moment.
+ // Non-root window are not capturable by the |FrameSinkVideoCapturer|
+ // unless its layer tree is identified by a |viz::SubtreeCaptureId|.
+ // The |VideoRecordingWatcher| that we create while recording is in
+ // progress creates a request to mark that window as capturable.
+ // See https://crbug.com/1143930 for more details.
+ DCHECK(!capture_params.window->IsRootWindow());
+ DCHECK(capture_params.window->subtree_capture_id().is_valid());
+
recording_service_remote_->RecordWindow(
std::move(client), std::move(video_capturer),
- std::move(audio_stream_factory), frame_sink_id, bounds.size(),
+ std::move(audio_stream_factory), frame_sink_id,
+ capture_params.window->subtree_capture_id(), bounds.size(),
capture_params.window->GetRootWindow()
->GetBoundsInRootWindow()
.size());
diff --git a/ash/capture_mode/capture_mode_controller.h b/ash/capture_mode/capture_mode_controller.h
index 80f84c9..02b45bd1 100644
--- a/ash/capture_mode/capture_mode_controller.h
+++ b/ash/capture_mode/capture_mode_controller.h
@@ -33,10 +33,6 @@
class SequencedTaskRunner;
} // namespace base
-namespace cc {
-class LayerTreeFrameSink;
-} // namespace cc
-
namespace ash {
class CaptureModeSession;
@@ -283,12 +279,6 @@
// If set, it will be called when either an image or video file is saved.
base::OnceCallback<void(const base::FilePath&)> on_file_saved_callback_;
- // Recording non-root windows require sending their FrameSinkIds to the
- // recording service. Those windows won't have a valid ID unless we create
- // LayerTreeFrameSinks for them. This remains alive while the window is being
- // recorded.
- std::unique_ptr<cc::LayerTreeFrameSink> window_frame_sink_;
-
// Timers used to schedule recording of the number of screenshots taken.
base::RepeatingTimer num_screenshots_taken_in_last_day_scheduler_;
base::RepeatingTimer num_screenshots_taken_in_last_week_scheduler_;
diff --git a/ash/capture_mode/capture_mode_unittests.cc b/ash/capture_mode/capture_mode_unittests.cc
index b302b367..194f61e 100644
--- a/ash/capture_mode/capture_mode_unittests.cc
+++ b/ash/capture_mode/capture_mode_unittests.cc
@@ -1481,6 +1481,25 @@
kTabletHistogram, CaptureModeEntryType::kAccelTakePartialScreenshot, 2);
}
+TEST_F(CaptureModeTest, WindowRecordingCaptureId) {
+ auto window = CreateTestWindow(gfx::Rect(200, 200));
+ StartCaptureSession(CaptureModeSource::kWindow, CaptureModeType::kVideo);
+
+ auto* event_generator = GetEventGenerator();
+ event_generator->MoveMouseToCenterOf(window.get());
+ auto* controller = CaptureModeController::Get();
+ controller->StartVideoRecordingImmediatelyForTesting();
+ EXPECT_TRUE(controller->is_recording_in_progress());
+
+ // The window should have a valid capture ID.
+ EXPECT_TRUE(window->subtree_capture_id().is_valid());
+
+ // Once recording ends, the window should no longer be marked as capturable.
+ controller->EndVideoRecording(EndRecordingReason::kStopRecordingButton);
+ EXPECT_FALSE(controller->is_recording_in_progress());
+ EXPECT_FALSE(window->subtree_capture_id().is_valid());
+}
+
TEST_F(CaptureModeTest, ClosingWindowBeingRecorded) {
auto window = CreateTestWindow(gfx::Rect(200, 200));
StartCaptureSession(CaptureModeSource::kWindow, CaptureModeType::kVideo);
@@ -1491,6 +1510,9 @@
controller->StartVideoRecordingImmediatelyForTesting();
EXPECT_TRUE(controller->is_recording_in_progress());
+ // The window should have a valid capture ID.
+ EXPECT_TRUE(window->subtree_capture_id().is_valid());
+
// Closing the window being recorded should end video recording.
base::HistogramTester histogram_tester;
window.reset();
@@ -1939,6 +1961,7 @@
// mode when capturing a window. Regression test for https://crbug.com/1152938.
TEST_F(CaptureModeTest, TabletTouchCaptureLabelWidgetWindowMode) {
TabletModeControllerTestApi tablet_mode_controller_test_api;
+ tablet_mode_controller_test_api.DetachAllMice();
tablet_mode_controller_test_api.EnterTabletMode();
// Enter capture window mode.
diff --git a/ash/capture_mode/test_capture_mode_delegate.cc b/ash/capture_mode/test_capture_mode_delegate.cc
index 22fed2e..4d606e5 100644
--- a/ash/capture_mode/test_capture_mode_delegate.cc
+++ b/ash/capture_mode/test_capture_mode_delegate.cc
@@ -41,6 +41,7 @@
mojo::PendingRemote<viz::mojom::FrameSinkVideoCapturer> video_capturer,
mojo::PendingRemote<audio::mojom::StreamFactory> audio_stream_factory,
const viz::FrameSinkId& frame_sink_id,
+ const viz::SubtreeCaptureId& subtree_capture_id,
const gfx::Size& initial_window_size,
const gfx::Size& max_window_size) override {
remote_client_.Bind(std::move(client));
diff --git a/ash/capture_mode/video_recording_watcher.cc b/ash/capture_mode/video_recording_watcher.cc
index af56308f..c5d55bd 100644
--- a/ash/capture_mode/video_recording_watcher.cc
+++ b/ash/capture_mode/video_recording_watcher.cc
@@ -22,6 +22,11 @@
DCHECK(window_being_recorded_);
DCHECK(controller_->is_recording_in_progress());
+ if (!window_being_recorded_->IsRootWindow()) {
+ non_root_window_capture_request_ =
+ window_being_recorded_->MakeWindowCapturable();
+ }
+
window_being_recorded_->AddObserver(this);
}
diff --git a/ash/capture_mode/video_recording_watcher.h b/ash/capture_mode/video_recording_watcher.h
index 0130d6b..792d771 100644
--- a/ash/capture_mode/video_recording_watcher.h
+++ b/ash/capture_mode/video_recording_watcher.h
@@ -5,6 +5,7 @@
#ifndef ASH_CAPTURE_MODE_VIDEO_RECORDING_WATCHER_H_
#define ASH_CAPTURE_MODE_VIDEO_RECORDING_WATCHER_H_
+#include "ui/aura/scoped_window_capture_request.h"
#include "ui/aura/window_observer.h"
namespace ash {
@@ -34,6 +35,10 @@
private:
CaptureModeController* const controller_;
aura::Window* const window_being_recorded_;
+
+ // If |window_being_recorded_| is not a root window, we must make a request to
+ // make it capturable by the |FrameSinkVideoCapturer|.
+ aura::ScopedWindowCaptureRequest non_root_window_capture_request_;
};
} // namespace ash
diff --git a/ash/services/recording/public/mojom/recording_service.mojom b/ash/services/recording/public/mojom/recording_service.mojom
index f53cbea..8ec490d 100644
--- a/ash/services/recording/public/mojom/recording_service.mojom
+++ b/ash/services/recording/public/mojom/recording_service.mojom
@@ -8,6 +8,7 @@
import "services/audio/public/mojom/stream_factory.mojom";
import "services/viz/privileged/mojom/compositing/frame_sink_video_capture.mojom";
import "services/viz/public/mojom/compositing/frame_sink_id.mojom";
+import "services/viz/public/mojom/compositing/subtree_capture_id.mojom";
import "ui/gfx/geometry/mojom/geometry.mojom";
// Defines the interface for the recording service client (e.g. ash), which
@@ -30,7 +31,7 @@
// Defines the interface of the recording service which is implemented by
// |recording::RecordingService| and runs in its own utility process. It is
-// launched by the process on which the |RecordingServiceClient| resides, and is
+// launched by the Ash on which the |RecordingServiceClient| resides, and is
// used to perform audio/video recording of the whole screen, a partial region
// of it, or an individual window. The service captures, encodes, and muxes the
// audio and video frames, and sends the WebM muxed video chunks to the client.
@@ -55,7 +56,14 @@
viz.mojom.FrameSinkId frame_sink_id,
gfx.mojom.Size video_size);
- // Starts a recording of a window which has the given |frame_sink_id|.
+ // Starts a recording of a window. If this window has a valid |frame_sink_id|,
+ // and submits its own compositor frames independently, then
+ // |subtree_capture_id| can be default-constructed and won't be used.
+ // Otherwise, for windows that don't submit compositor frames (e.g. non-root
+ // windows), the given |frame_sink_id| must be of the root window they're
+ // descendant from, and they must be made capturable by tagging the with a
+ // valid |subtree_capture_id| before calling this (see
+ // aura::Window::MakeWindowCapturable()).
// |initial_video_size| and |max_video_size| specify a range of acceptable
// capture resolutions in DIPs. The resolution of the output will adapt
// dynamically as the window being recorded gets resized by the end user (e.g.
@@ -68,6 +76,7 @@
pending_remote<viz.mojom.FrameSinkVideoCapturer> video_capturer,
pending_remote<audio.mojom.StreamFactory>? audio_stream_factory,
viz.mojom.FrameSinkId frame_sink_id,
+ viz.mojom.SubtreeCaptureId subtree_capture_id,
gfx.mojom.Size initial_video_size,
gfx.mojom.Size max_video_size);
diff --git a/ash/services/recording/recording_service.cc b/ash/services/recording/recording_service.cc
index 907d5e7ce..62082cd 100644
--- a/ash/services/recording/recording_service.cc
+++ b/ash/services/recording/recording_service.cc
@@ -96,6 +96,7 @@
mojo::PendingRemote<viz::mojom::FrameSinkVideoCapturer> video_capturer,
mojo::PendingRemote<audio::mojom::StreamFactory> audio_stream_factory,
const viz::FrameSinkId& frame_sink_id,
+ const viz::SubtreeCaptureId& subtree_capture_id,
const gfx::Size& initial_video_size,
const gfx::Size& max_video_size) {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
@@ -105,7 +106,8 @@
StartNewRecording(std::move(client), std::move(video_capturer),
std::move(audio_stream_factory),
VideoCaptureParams::CreateForWindowCapture(
- frame_sink_id, initial_video_size, max_video_size));
+ frame_sink_id, subtree_capture_id, initial_video_size,
+ max_video_size));
}
void RecordingService::RecordRegion(
diff --git a/ash/services/recording/recording_service.h b/ash/services/recording/recording_service.h
index 79f8dc6..d529b19 100644
--- a/ash/services/recording/recording_service.h
+++ b/ash/services/recording/recording_service.h
@@ -53,6 +53,7 @@
mojo::PendingRemote<viz::mojom::FrameSinkVideoCapturer> video_capturer,
mojo::PendingRemote<audio::mojom::StreamFactory> audio_stream_factory,
const viz::FrameSinkId& frame_sink_id,
+ const viz::SubtreeCaptureId& subtree_capture_id,
const gfx::Size& initial_video_size,
const gfx::Size& max_video_size) override;
void RecordRegion(
diff --git a/ash/services/recording/video_capture_params.cc b/ash/services/recording/video_capture_params.cc
index e3c52d6..cb9ba0b 100644
--- a/ash/services/recording/video_capture_params.cc
+++ b/ash/services/recording/video_capture_params.cc
@@ -7,6 +7,7 @@
#include "ash/services/recording/recording_service_constants.h"
#include "base/check.h"
#include "base/time/time.h"
+#include "components/viz/common/surfaces/subtree_capture_id.h"
#include "media/base/video_types.h"
#include "services/viz/privileged/mojom/compositing/frame_sink_video_capture.mojom.h"
#include "ui/gfx/geometry/rect.h"
@@ -23,7 +24,8 @@
public:
FullscreenCaptureParams(viz::FrameSinkId frame_sink_id,
const gfx::Size& video_size)
- : VideoCaptureParams(frame_sink_id), video_size_(video_size) {}
+ : VideoCaptureParams(frame_sink_id, viz::SubtreeCaptureId()),
+ video_size_(video_size) {}
FullscreenCaptureParams(const FullscreenCaptureParams&) = delete;
FullscreenCaptureParams& operator=(const FullscreenCaptureParams&) = delete;
~FullscreenCaptureParams() override = default;
@@ -49,9 +51,10 @@
class WindowCaptureParams : public VideoCaptureParams {
public:
WindowCaptureParams(viz::FrameSinkId frame_sink_id,
+ viz::SubtreeCaptureId subtree_capture_id,
const gfx::Size& initial_video_size,
const gfx::Size& max_video_size)
- : VideoCaptureParams(frame_sink_id),
+ : VideoCaptureParams(frame_sink_id, subtree_capture_id),
initial_video_size_(initial_video_size),
max_video_size_(max_video_size) {}
WindowCaptureParams(const WindowCaptureParams&) = delete;
@@ -82,7 +85,7 @@
RegionCaptureParams(viz::FrameSinkId frame_sink_id,
const gfx::Size& full_capture_size,
const gfx::Rect& crop_region)
- : VideoCaptureParams(frame_sink_id),
+ : VideoCaptureParams(frame_sink_id, viz::SubtreeCaptureId()),
full_capture_size_(full_capture_size),
crop_region_(crop_region) {}
RegionCaptureParams(const RegionCaptureParams&) = delete;
@@ -125,10 +128,11 @@
// static
std::unique_ptr<VideoCaptureParams> VideoCaptureParams::CreateForWindowCapture(
viz::FrameSinkId frame_sink_id,
+ viz::SubtreeCaptureId subtree_capture_id,
const gfx::Size& initial_video_size,
const gfx::Size& max_video_size) {
return std::make_unique<WindowCaptureParams>(
- frame_sink_id, initial_video_size, max_video_size);
+ frame_sink_id, subtree_capture_id, initial_video_size, max_video_size);
}
// static
@@ -149,7 +153,7 @@
// TODO(afakhry): Discuss with //media/ team the implications of color space
// conversions.
capturer->SetFormat(media::PIXEL_FORMAT_I420, kColorSpace);
- capturer->ChangeTarget(frame_sink_id_);
+ capturer->ChangeTarget(frame_sink_id_, subtree_capture_id_);
}
gfx::Rect VideoCaptureParams::GetVideoFrameVisibleRect(
@@ -157,8 +161,9 @@
return original_frame_visible_rect;
}
-VideoCaptureParams::VideoCaptureParams(viz::FrameSinkId frame_sink_id)
- : frame_sink_id_(frame_sink_id) {
+VideoCaptureParams::VideoCaptureParams(viz::FrameSinkId frame_sink_id,
+ viz::SubtreeCaptureId subtree_capture_id)
+ : frame_sink_id_(frame_sink_id), subtree_capture_id_(subtree_capture_id) {
DCHECK(frame_sink_id_.is_valid());
}
diff --git a/ash/services/recording/video_capture_params.h b/ash/services/recording/video_capture_params.h
index 0559b61..3726f345 100644
--- a/ash/services/recording/video_capture_params.h
+++ b/ash/services/recording/video_capture_params.h
@@ -8,6 +8,7 @@
#include <memory>
#include "components/viz/common/surfaces/frame_sink_id.h"
+#include "components/viz/common/surfaces/subtree_capture_id.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/viz/privileged/mojom/compositing/frame_sink_video_capture.mojom-forward.h"
@@ -35,14 +36,19 @@
viz::FrameSinkId frame_sink_id,
const gfx::Size& video_size);
- // Returns a capture params instance for a recording of a window which has
- // the given |frame_sink_id| and its initial size is given as
+ // Returns a capture params instance for a recording of a window. The given
+ //|frame_sink_id| is either of that window (if it submits compositor frames
+ // independently), or of the root window it descends from (if it doesn't
+ // submit its compositor frames). In the latter case, the window must be
+ // identifiable by a valid |subtree_capture_id| (created by calling
+ // aura::window::MakeWindowCapturable() before recording starts).
// |initial_video_size| and |max_video_size| specify a range of acceptable
// capture resolutions in DIPs. The resolution of the output will adapt
// dynamically as the window being recorded gets resized by the end user (e.g.
// resized, maximized, fullscreened, ... etc.). |frame_sink_id| must be valid.
static std::unique_ptr<VideoCaptureParams> CreateForWindowCapture(
viz::FrameSinkId frame_sink_id,
+ viz::SubtreeCaptureId subtree_capture_id,
const gfx::Size& initial_video_size,
const gfx::Size& max_video_size);
@@ -73,9 +79,11 @@
virtual gfx::Size GetCaptureSize() const = 0;
protected:
- explicit VideoCaptureParams(viz::FrameSinkId frame_sink_id);
+ explicit VideoCaptureParams(viz::FrameSinkId frame_sink_id,
+ viz::SubtreeCaptureId subtree_capture_id);
const viz::FrameSinkId frame_sink_id_;
+ const viz::SubtreeCaptureId subtree_capture_id_;
};
} // namespace recording
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index c58dc050..8c8f34b 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -767,6 +767,7 @@
"trees/layer_tree_host_pixeltest_tiles.cc",
"trees/layer_tree_host_unittest.cc",
"trees/layer_tree_host_unittest_animation.cc",
+ "trees/layer_tree_host_unittest_capture.cc",
"trees/layer_tree_host_unittest_capture_content.cc",
"trees/layer_tree_host_unittest_checkerimaging.cc",
"trees/layer_tree_host_unittest_context.cc",
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc
index 3f187ae..2a0de3af 100644
--- a/cc/layers/layer.cc
+++ b/cc/layers/layer.cc
@@ -1003,6 +1003,22 @@
EnsureLayerTreeInputs().did_scroll_callback = std::move(callback);
}
+void Layer::SetSubtreeCaptureId(viz::SubtreeCaptureId subtree_id) {
+ DCHECK(IsPropertyChangeAllowed());
+
+ auto& inputs = EnsureLayerTreeInputs();
+ if (inputs.subtree_capture_id == subtree_id)
+ return;
+
+ DCHECK(!inputs.subtree_capture_id.is_valid() || !subtree_id.is_valid())
+ << "Not allowed to change from a valid ID to another valid ID, as it may "
+ "already be in use.";
+
+ inputs.subtree_capture_id = subtree_id;
+ SetPropertyTreesNeedRebuild();
+ SetNeedsCommit();
+}
+
void Layer::SetScrollable(const gfx::Size& bounds) {
DCHECK(IsPropertyChangeAllowed());
auto& inputs = EnsureLayerTreeInputs();
diff --git a/cc/layers/layer.h b/cc/layers/layer.h
index 3f4189e..f73be66 100644
--- a/cc/layers/layer.h
+++ b/cc/layers/layer.h
@@ -31,6 +31,7 @@
#include "cc/trees/effect_node.h"
#include "cc/trees/property_tree.h"
#include "cc/trees/target_property.h"
+#include "components/viz/common/surfaces/subtree_capture_id.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/gfx/geometry/point3_f.h"
#include "ui/gfx/geometry/rect.h"
@@ -472,6 +473,27 @@
void SetDidScrollCallback(base::RepeatingCallback<
void(const gfx::ScrollOffset&, const ElementId&)>);
+ // For layer tree mode only.
+ // Sets the given |subtree_id| on this layer, so that the layer subtree rooted
+ // at this layer can be uniquely identified by a FrameSinkVideoCapturer.
+ // The existence of a valid SubtreeCaptureId on this layer will force it to be
+ // drawn into a separate CompositorRenderPass.
+ // Setting a non-valid (i.e. default-constructed SubtreeCaptureId) will clear
+ // this property.
+ // It is not allowed to change this ID from a valid ID to another valid ID,
+ // since a client might already using the existing valid ID to make this layer
+ // subtree identifiable by a capturer.
+ //
+ // Note that this is useful when it's desired to video record a layer subtree
+ // of a non-root layer using a FrameSinkVideoCapturer, since non-root layers
+ // are usually not drawn into their own CompositorRenderPass.
+ void SetSubtreeCaptureId(viz::SubtreeCaptureId subtree_id);
+ viz::SubtreeCaptureId subtree_capture_id() const {
+ if (layer_tree_inputs())
+ return layer_tree_inputs()->subtree_capture_id;
+ return viz::SubtreeCaptureId();
+ }
+
// Set or get if the layer and its subtree should be cached as a texture in
// the display compositor. This is used as an optimization when it is known
// that the layer will be animated without changing its content, or any of its
@@ -889,6 +911,12 @@
gfx::Transform transform;
gfx::Point3F transform_origin;
+ // A unique ID that identifies the layer subtree rooted at this layer, so
+ // that it can be independently captured by the FrameSinkVideoCapturer. If
+ // this ID is set (i.e. valid), it would force this subtree into a render
+ // surface that darws in a render pass.
+ viz::SubtreeCaptureId subtree_capture_id;
+
SkColor safe_opaque_background_color = SK_ColorTRANSPARENT;
FilterOperations filters;
@@ -927,6 +955,7 @@
int clip_tree_index_;
int scroll_tree_index_;
int property_tree_sequence_number_;
+
gfx::Vector2dF offset_to_transform_parent_;
// When true, the layer is about to perform an update. Any commit requests
diff --git a/cc/layers/render_surface_impl.cc b/cc/layers/render_surface_impl.cc
index ca8fa5a7..98b2d611 100644
--- a/cc/layers/render_surface_impl.cc
+++ b/cc/layers/render_surface_impl.cc
@@ -157,10 +157,19 @@
return OwningEffectNode()->has_copy_request;
}
+viz::SubtreeCaptureId RenderSurfaceImpl::SubtreeCaptureId() const {
+ return OwningEffectNode()->subtree_capture_id;
+}
+
bool RenderSurfaceImpl::ShouldCacheRenderSurface() const {
return OwningEffectNode()->cache_render_surface;
}
+bool RenderSurfaceImpl::CopyOfOutputRequired() const {
+ return HasCopyRequest() || ShouldCacheRenderSurface() ||
+ SubtreeCaptureId().is_valid();
+}
+
int RenderSurfaceImpl::TransformTreeIndex() const {
return OwningEffectNode()->transform_id;
}
@@ -210,7 +219,7 @@
}
gfx::Rect RenderSurfaceImpl::CalculateClippedAccumulatedContentRect() {
- if (ShouldCacheRenderSurface() || HasCopyRequest() || !is_clipped())
+ if (CopyOfOutputRequired() || !is_clipped())
return accumulated_content_rect();
if (accumulated_content_rect().IsEmpty())
@@ -377,6 +386,7 @@
pass->backdrop_filters = BackdropFilters();
pass->backdrop_filter_bounds = BackdropFilterBounds();
pass->generate_mipmap = TrilinearFiltering();
+ pass->subtree_capture_id = SubtreeCaptureId();
pass->cache_render_pass = ShouldCacheRenderSurface();
pass->has_damage_from_contributing_content =
HasDamageFromeContributingContent();
diff --git a/cc/layers/render_surface_impl.h b/cc/layers/render_surface_impl.h
index 51b07a9..105e04a 100644
--- a/cc/layers/render_surface_impl.h
+++ b/cc/layers/render_surface_impl.h
@@ -18,6 +18,7 @@
#include "cc/trees/property_tree.h"
#include "components/viz/common/quads/compositor_render_pass.h"
#include "components/viz/common/quads/shared_quad_state.h"
+#include "components/viz/common/surfaces/subtree_capture_id.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/mask_filter_info.h"
@@ -174,8 +175,15 @@
bool HasCopyRequest() const;
+ viz::SubtreeCaptureId SubtreeCaptureId() const;
+
bool ShouldCacheRenderSurface() const;
+ // Returns true if it's required to copy the output of this surface (i.e. when
+ // it has copy requests, should be cached, or has a valid subtree capture ID),
+ // and should be e.g. immune from occlusion, etc. Returns false otherise.
+ bool CopyOfOutputRequired() const;
+
void ResetPropertyChangedFlags();
bool SurfacePropertyChanged() const;
bool SurfacePropertyChangedOnlyFromDescendant() const;
diff --git a/cc/trees/draw_property_utils.cc b/cc/trees/draw_property_utils.cc
index c2e79fc..aa4433e 100644
--- a/cc/trees/draw_property_utils.cc
+++ b/cc/trees/draw_property_utils.cc
@@ -640,19 +640,20 @@
gfx::Rect LayerVisibleRect(PropertyTrees* property_trees, LayerImpl* layer) {
const EffectNode* effect_node =
property_trees->effect_tree.Node(layer->effect_tree_index());
- int effect_ancestor_with_cache_render_surface =
- effect_node->closest_ancestor_with_cached_render_surface_id;
- int effect_ancestor_with_copy_request =
- effect_node->closest_ancestor_with_copy_request_id;
int lower_effect_closest_ancestor =
- std::max(effect_ancestor_with_cache_render_surface,
- effect_ancestor_with_copy_request);
- bool non_root_copy_request_or_cache_render_surface =
+ effect_node->closest_ancestor_with_cached_render_surface_id;
+ lower_effect_closest_ancestor =
+ std::max(lower_effect_closest_ancestor,
+ effect_node->closest_ancestor_with_copy_request_id);
+ lower_effect_closest_ancestor =
+ std::max(lower_effect_closest_ancestor,
+ effect_node->closest_ancestor_being_captured_id);
+ const bool non_root_with_render_surface =
lower_effect_closest_ancestor > EffectTree::kContentsRootNodeId;
gfx::Rect layer_content_rect = gfx::Rect(layer->bounds());
gfx::RectF accumulated_clip_in_root_space;
- if (non_root_copy_request_or_cache_render_surface) {
+ if (non_root_with_render_surface) {
bool include_expanding_clips = true;
ConditionalClip accumulated_clip = ComputeAccumulatedClip(
property_trees, include_expanding_clips, layer->clip_tree_index(),
@@ -668,7 +669,7 @@
}
const EffectNode* root_effect_node =
- non_root_copy_request_or_cache_render_surface
+ non_root_with_render_surface
? property_trees->effect_tree.Node(lower_effect_closest_ancestor)
: property_trees->effect_tree.Node(EffectTree::kContentsRootNodeId);
ConditionalClip accumulated_clip_in_layer_space =
@@ -890,8 +891,7 @@
// TODO(senorblanco): make this smarter for the SkImageFilter case (check for
// pixel-moving filters)
const FilterOperations& filters = render_surface->Filters();
- bool is_occlusion_immune = render_surface->HasCopyRequest() ||
- render_surface->ShouldCacheRenderSurface() ||
+ bool is_occlusion_immune = render_surface->CopyOfOutputRequired() ||
filters.HasReferenceFilter() ||
filters.HasFilterThatMovesPixels();
if (is_occlusion_immune) {
@@ -1225,9 +1225,11 @@
if (effect_node->HasRenderSurface() && effect_node->subtree_has_copy_request)
return false;
- // Skip if the node's subtree is hidden and no need to cache.
- if (effect_node->subtree_hidden && !effect_node->cache_render_surface)
+ // Skip if the node's subtree is hidden and no need to cache, or capture.
+ if (effect_node->subtree_hidden && !effect_node->cache_render_surface &&
+ !effect_node->subtree_capture_id.is_valid()) {
return true;
+ }
// If the layer transform is not invertible, it should be skipped. In case the
// transform is animating and singular, we should not skip it.
diff --git a/cc/trees/effect_node.cc b/cc/trees/effect_node.cc
index fa07fea5..413f5888 100644
--- a/cc/trees/effect_node.cc
+++ b/cc/trees/effect_node.cc
@@ -42,7 +42,8 @@
clip_id(0),
target_id(1),
closest_ancestor_with_cached_render_surface_id(-1),
- closest_ancestor_with_copy_request_id(-1) {}
+ closest_ancestor_with_copy_request_id(-1),
+ closest_ancestor_being_captured_id(-1) {}
EffectNode::EffectNode(const EffectNode& other) = default;
@@ -54,6 +55,7 @@
stable_id == other.stable_id && opacity == other.opacity &&
screen_space_opacity == other.screen_space_opacity &&
backdrop_filter_quality == other.backdrop_filter_quality &&
+ subtree_capture_id == other.subtree_capture_id &&
cache_render_surface == other.cache_render_surface &&
has_copy_request == other.has_copy_request &&
filters == other.filters &&
@@ -94,7 +96,9 @@
closest_ancestor_with_cached_render_surface_id ==
other.closest_ancestor_with_cached_render_surface_id &&
closest_ancestor_with_copy_request_id ==
- other.closest_ancestor_with_copy_request_id;
+ other.closest_ancestor_with_copy_request_id &&
+ closest_ancestor_being_captured_id ==
+ other.closest_ancestor_being_captured_id;
}
#endif // DCHECK_IS_ON()
@@ -172,6 +176,7 @@
}
}
value->SetString("blend_mode", SkBlendMode_Name(blend_mode));
+ value->SetString("subtree_capture_id", subtree_capture_id.ToString());
value->SetBoolean("cache_render_surface", cache_render_surface);
value->SetBoolean("has_copy_request", has_copy_request);
value->SetBoolean("double_sided", double_sided);
@@ -198,6 +203,8 @@
closest_ancestor_with_cached_render_surface_id);
value->SetInteger("closest_ancestor_with_copy_request_id",
closest_ancestor_with_copy_request_id);
+ value->SetInteger("closest_ancestor_being_captured_id",
+ closest_ancestor_being_captured_id);
value->SetBoolean("affected_by_backdrop_filter", affected_by_backdrop_filter);
}
diff --git a/cc/trees/effect_node.h b/cc/trees/effect_node.h
index 2a2541d1..65edbca 100644
--- a/cc/trees/effect_node.h
+++ b/cc/trees/effect_node.h
@@ -8,6 +8,7 @@
#include "cc/cc_export.h"
#include "cc/paint/element_id.h"
#include "cc/paint/filter_operations.h"
+#include "components/viz/common/surfaces/subtree_capture_id.h"
#include "third_party/skia/include/core/SkBlendMode.h"
#include "ui/gfx/geometry/point_f.h"
#include "ui/gfx/geometry/size_f.h"
@@ -44,6 +45,7 @@
kCache,
kCopyRequest,
kMirrored,
+ kSubtreeIsBeingCaptured,
// This must be the last value because it's used in tracing code to know the
// number of reasons.
kTest,
@@ -90,6 +92,8 @@
gfx::Vector2dF surface_contents_scale;
+ viz::SubtreeCaptureId subtree_capture_id;
+
bool cache_render_surface : 1;
bool has_copy_request : 1;
bool hidden_by_backface_visibility : 1;
@@ -154,6 +158,7 @@
int target_id;
int closest_ancestor_with_cached_render_surface_id;
int closest_ancestor_with_copy_request_id;
+ int closest_ancestor_being_captured_id;
bool HasRenderSurface() const {
return render_surface_reason != RenderSurfaceReason::kNone;
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 55c7a74..7b11dac 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -1170,8 +1170,7 @@
render_surface->EffectTreeIndex() == EffectTree::kContentsRootNodeId;
bool should_draw_into_render_pass =
is_root_surface || render_surface->contributes_to_drawn_surface() ||
- render_surface->HasCopyRequest() ||
- render_surface->ShouldCacheRenderSurface();
+ render_surface->CopyOfOutputRequired();
if (should_draw_into_render_pass)
frame->render_passes.push_back(render_surface->CreateRenderPass());
}
@@ -1598,7 +1597,8 @@
}
if (pass->quad_list.empty() && pass->copy_requests.empty() &&
- pass->filters.IsEmpty() && pass->backdrop_filters.IsEmpty()) {
+ !pass->subtree_capture_id.is_valid() && pass->filters.IsEmpty() &&
+ pass->backdrop_filters.IsEmpty()) {
// Remove the pass and decrement |i| to counter the for loop's increment,
// so we don't skip the next pass in the loop.
frame->render_passes.erase(frame->render_passes.begin() + i);
diff --git a/cc/trees/layer_tree_host_unittest_capture.cc b/cc/trees/layer_tree_host_unittest_capture.cc
new file mode 100644
index 0000000..0336419
--- /dev/null
+++ b/cc/trees/layer_tree_host_unittest_capture.cc
@@ -0,0 +1,143 @@
+// Copyright 2020 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 "cc/test/fake_content_layer_client.h"
+#include "cc/test/fake_picture_layer.h"
+#include "cc/test/layer_tree_test.h"
+#include "cc/test/property_tree_test_utils.h"
+#include "cc/trees/layer_tree_impl.h"
+#include "components/viz/common/quads/compositor_frame.h"
+#include "components/viz/common/surfaces/subtree_capture_id.h"
+
+namespace cc {
+namespace {
+
+constexpr viz::SubtreeCaptureId kCaptureId{22};
+
+// A base class for tests that verifies the bahvior of the layer tree when a
+// sub layer has a valid viz::SubtreeCaptureId.
+class LayerTreeHostCaptureTest : public LayerTreeTest {
+ public:
+ void SetupTree() override {
+ scoped_refptr<Layer> root = FakePictureLayer::Create(&client_);
+ root->SetBounds(gfx::Size(100, 100));
+
+ child_ = FakePictureLayer::Create(&client_);
+ child_->SetBounds(gfx::Size(50, 60));
+ child_->SetPosition(gfx::PointF(10.f, 5.5f));
+ root->AddChild(child_);
+
+ grand_child_ = FakePictureLayer::Create(&client_);
+ grand_child_->SetBounds(gfx::Size(70, 30));
+ grand_child_->SetPosition(gfx::PointF(50.f, 50.f));
+ child_->AddChild(grand_child_);
+
+ layer_tree_host()->SetRootLayer(root);
+ LayerTreeTest::SetupTree();
+ client_.set_bounds(root->bounds());
+ }
+
+ void DrawLayersOnThread(LayerTreeHostImpl* impl) override {
+ LayerImpl* root = impl->active_tree()->root_layer();
+ LayerImpl* child = impl->active_tree()->LayerById(child_->id());
+ LayerImpl* grand_child = impl->active_tree()->LayerById(grand_child_->id());
+
+ VerifyLayerImpls(root, child, grand_child);
+ }
+
+ protected:
+ // Lets test subclasses to verify the LayerImpls of the layers in the tree.
+ virtual void VerifyLayerImpls(LayerImpl* root,
+ LayerImpl* child,
+ LayerImpl* grand_child) = 0;
+
+ FakeContentLayerClient client_;
+ scoped_refptr<Layer> child_;
+ scoped_refptr<Layer> grand_child_;
+};
+
+// -----------------------------------------------------------------------------
+// LayerTreeHostCaptureTestNoExtraRenderPassWhenNotCapturing:
+//
+// Tests that a layer tree that doesn't have a viz::SubtreeCaptureId on any of
+// its layers, draw in a single root render surface, and generates a single
+// compositor render pass.
+class LayerTreeHostCaptureTestNoExtraRenderPassWhenNotCapturing
+ : public LayerTreeHostCaptureTest {
+ public:
+ void BeginTest() override { PostSetNeedsCommitToMainThread(); }
+
+ void VerifyLayerImpls(LayerImpl* root,
+ LayerImpl* child,
+ LayerImpl* grand_child) override {
+ // All layers in the tree draw in the same root render surface.
+ auto* root_surface = GetRenderSurface(root);
+ auto* child_surface = GetRenderSurface(child);
+ auto* grand_child_surface = GetRenderSurface(grand_child);
+ EXPECT_EQ(root_surface, child_surface);
+ EXPECT_EQ(root_surface, grand_child_surface);
+ EXPECT_FALSE(root_surface->CopyOfOutputRequired());
+ EXPECT_FALSE(root_surface->SubtreeCaptureId().is_valid());
+
+ EndTest();
+ }
+
+ void DisplayReceivedCompositorFrameOnThread(
+ const viz::CompositorFrame& frame) override {
+ // There should be a single compositor render pass, which has no valid
+ // SubtreeCaptureId.
+ ASSERT_EQ(frame.render_pass_list.size(), 1u);
+ EXPECT_FALSE(frame.render_pass_list.back()->subtree_capture_id.is_valid());
+ }
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(
+ LayerTreeHostCaptureTestNoExtraRenderPassWhenNotCapturing);
+
+// -----------------------------------------------------------------------------
+// LayerTreeHostCaptureTestLayerWithCaptureIdElevatesToSurface
+//
+// Tests that a layer sub tree whose root has a valid viz::SubtreeCaptureId will
+// draw into a separate render surface and a separate render pass.
+class LayerTreeHostCaptureTestLayerWithCaptureIdElevatesToSurface
+ : public LayerTreeHostCaptureTest {
+ public:
+ void BeginTest() override { child_->SetSubtreeCaptureId(kCaptureId); }
+
+ void VerifyLayerImpls(LayerImpl* root,
+ LayerImpl* child,
+ LayerImpl* grand_child) override {
+ // |child| should draw into a separate render surface from that of the root,
+ // and the |grand_child| should draw into the render surface of its parent
+ // (which is |child|'s).
+ // The |chils|'s surface should have the expected capture ID.
+ auto* root_surface = GetRenderSurface(root);
+ auto* child_surface = GetRenderSurface(child);
+ auto* grand_child_surface = GetRenderSurface(grand_child);
+ EXPECT_NE(root_surface, child_surface);
+ EXPECT_NE(root_surface, grand_child_surface);
+ EXPECT_EQ(child_surface, grand_child_surface);
+ EXPECT_EQ(kCaptureId, child_surface->SubtreeCaptureId());
+ EXPECT_TRUE(child_surface->CopyOfOutputRequired());
+
+ EndTest();
+ }
+
+ void DisplayReceivedCompositorFrameOnThread(
+ const viz::CompositorFrame& frame) override {
+ // There should be 2 render passes. The non-root render pass is associated
+ // with the layer subtree rooted at |child| and should have the expected
+ // capture ID.
+ ASSERT_EQ(frame.render_pass_list.size(), 2u);
+ EXPECT_TRUE(frame.render_pass_list.front()->subtree_capture_id.is_valid());
+ EXPECT_EQ(kCaptureId, frame.render_pass_list.front()->subtree_capture_id);
+ EXPECT_FALSE(frame.render_pass_list.back()->subtree_capture_id.is_valid());
+ }
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(
+ LayerTreeHostCaptureTestLayerWithCaptureIdElevatesToSurface);
+
+} // namespace
+} // namespace cc
diff --git a/cc/trees/occlusion_tracker.cc b/cc/trees/occlusion_tracker.cc
index b3ed41e..2a88893 100644
--- a/cc/trees/occlusion_tracker.cc
+++ b/cc/trees/occlusion_tracker.cc
@@ -196,9 +196,7 @@
// Readbacks always happen on render targets so we only need to check
// for readbacks here.
bool target_is_only_for_copy_request_or_force_render_surface =
- (finished_target_surface->HasCopyRequest() ||
- finished_target_surface->ShouldCacheRenderSurface()) &&
- is_hidden;
+ is_hidden && finished_target_surface->CopyOfOutputRequired();
// If the occlusion within the surface can not be applied to things outside of
// the surface's subtree, then clear the occlusion here so it won't be used.
diff --git a/cc/trees/property_tree.cc b/cc/trees/property_tree.cc
index d9da2f9..c854377 100644
--- a/cc/trees/property_tree.cc
+++ b/cc/trees/property_tree.cc
@@ -729,20 +729,24 @@
// Exceptions:
// 1) Nodes that contribute to copy requests, whether hidden or not, must be
// drawn.
- // 2) Nodes that have a backdrop filter.
- // 3) Nodes with animating screen space opacity on main thread or pending tree
+ // 2) Nodes that have a valid SubtreeCaptureId, must be drawn so that they can
+ // be captured by the FrameSinkVideoCapturer.
+ // 3) Nodes that have a backdrop filter.
+ // 4) Nodes with animating screen space opacity on main thread or pending tree
// are drawn if their parent is drawn irrespective of their opacity.
- if (node->has_copy_request || node->cache_render_surface)
+ if (node->has_copy_request || node->cache_render_surface ||
+ node->subtree_capture_id.is_valid()) {
node->is_drawn = true;
- else if (EffectiveOpacity(node) == 0.f &&
- (!node->has_potential_opacity_animation ||
- property_trees()->is_active) &&
- node->backdrop_filters.IsEmpty())
+ } else if (EffectiveOpacity(node) == 0.f &&
+ (!node->has_potential_opacity_animation ||
+ property_trees()->is_active) &&
+ node->backdrop_filters.IsEmpty()) {
node->is_drawn = false;
- else if (parent_node)
+ } else if (parent_node) {
node->is_drawn = parent_node->is_drawn;
- else
+ } else {
node->is_drawn = true;
+ }
}
void EffectTree::UpdateEffectChanged(EffectNode* node,
@@ -789,7 +793,8 @@
void EffectTree::UpdateOnlyDrawsVisibleContent(EffectNode* node,
EffectNode* parent_node) {
- node->only_draws_visible_content = !node->has_copy_request;
+ node->only_draws_visible_content =
+ !node->has_copy_request && !node->subtree_capture_id.is_valid();
if (parent_node)
node->only_draws_visible_content &= parent_node->only_draws_visible_content;
if (!node->backdrop_filters.IsEmpty()) {
diff --git a/cc/trees/property_tree_builder.cc b/cc/trees/property_tree_builder.cc
index d0f5851..e7d040f 100644
--- a/cc/trees/property_tree_builder.cc
+++ b/cc/trees/property_tree_builder.cc
@@ -39,6 +39,7 @@
int scroll_tree_parent;
int closest_ancestor_with_cached_render_surface;
int closest_ancestor_with_copy_request;
+ int closest_ancestor_being_captured;
SkColor safe_opaque_background_color;
bool animation_axis_aligned_since_render_target;
bool not_axis_aligned_since_last_clip;
@@ -389,6 +390,9 @@
if (layer->mirror_count())
return RenderSurfaceReason::kMirrored;
+ if (layer->subtree_capture_id().is_valid())
+ return RenderSurfaceReason::kSubtreeIsBeingCaptured;
+
return RenderSurfaceReason::kNone;
}
@@ -455,6 +459,7 @@
node->stable_id = layer->id();
node->opacity = layer->opacity();
node->blend_mode = layer->blend_mode();
+ node->subtree_capture_id = layer->subtree_capture_id();
node->cache_render_surface = layer->cache_render_surface();
node->has_copy_request = layer->HasCopyRequest();
node->filters = layer->filters();
@@ -484,6 +489,10 @@
layer->HasCopyRequest()
? node_id
: data_from_ancestor.closest_ancestor_with_copy_request;
+ node->closest_ancestor_being_captured_id =
+ layer->subtree_capture_id().is_valid()
+ ? node_id
+ : data_from_ancestor.closest_ancestor_being_captured;
if (layer->HasRoundedCorner()) {
// This is currently in the local space of the layer and hence in an invalid
@@ -518,6 +527,8 @@
node->closest_ancestor_with_cached_render_surface_id;
data_for_children->closest_ancestor_with_copy_request =
node->closest_ancestor_with_copy_request_id;
+ data_for_children->closest_ancestor_being_captured =
+ node->closest_ancestor_being_captured_id;
data_for_children->effect_tree_parent = node_id;
layer->SetEffectTreeIndex(node_id);
@@ -717,6 +728,8 @@
EffectTree::kInvalidNodeId;
data_for_recursion.closest_ancestor_with_copy_request =
EffectTree::kInvalidNodeId;
+ data_for_recursion.closest_ancestor_being_captured =
+ EffectTree::kInvalidNodeId;
data_for_recursion.compound_transform_since_render_target = gfx::Transform();
data_for_recursion.animation_axis_aligned_since_render_target = true;
data_for_recursion.not_axis_aligned_since_last_clip = false;
diff --git a/chrome/browser/ui/ash/recording_service_browsertest.cc b/chrome/browser/ui/ash/recording_service_browsertest.cc
index 6d8926b..633d56e 100644
--- a/chrome/browser/ui/ash/recording_service_browsertest.cc
+++ b/chrome/browser/ui/ash/recording_service_browsertest.cc
@@ -180,17 +180,13 @@
FinishVideoRecordingTest(&test_api);
}
-// This test is currently disabled since it will always fail on the bots for
-// now, since audio is not captured on the bots, and currently window recording
-// captures no video frames, so the resulting video file will always be empty.
-// TODO(crbug.com/1143930): Re-enable this once window capture is working.
-IN_PROC_BROWSER_TEST_F(RecordingServiceBrowserTest, DISABLED_RecordWindow) {
+IN_PROC_BROWSER_TEST_F(RecordingServiceBrowserTest, RecordWindow) {
ash::CaptureModeTestApi test_api;
test_api.StartForWindow(/*for_video=*/true);
auto* generator = GetEventGenerator();
// Move the mouse cursor above the browser window to select it for window
- // capture.
- generator->MoveMouseTo(GetBrowserWindow()->GetBoundsInScreen().CenterPoint());
+ // capture (make sure it doesn't hover over the capture bar).
+ generator->MoveMouseTo(GetBrowserWindow()->GetBoundsInScreen().top_center());
FinishVideoRecordingTest(&test_api);
}
diff --git a/components/viz/common/BUILD.gn b/components/viz/common/BUILD.gn
index 3bcbec85..ce5490b 100644
--- a/components/viz/common/BUILD.gn
+++ b/components/viz/common/BUILD.gn
@@ -268,6 +268,9 @@
"surfaces/parent_local_surface_id_allocator.h",
"surfaces/scoped_surface_id_allocator.cc",
"surfaces/scoped_surface_id_allocator.h",
+ "surfaces/subtree_capture_id.cc",
+ "surfaces/subtree_capture_id.h",
+ "surfaces/subtree_capture_id_allocator.h",
"surfaces/surface_id.cc",
"surfaces/surface_id.h",
"surfaces/surface_info.cc",
diff --git a/components/viz/common/quads/compositor_render_pass.cc b/components/viz/common/quads/compositor_render_pass.cc
index 0ddff20..96c65f3 100644
--- a/components/viz/common/quads/compositor_render_pass.cc
+++ b/components/viz/common/quads/compositor_render_pass.cc
@@ -88,6 +88,7 @@
const cc::FilterOperations& filters,
const cc::FilterOperations& backdrop_filters,
const base::Optional<gfx::RRectF>& backdrop_filter_bounds,
+ SubtreeCaptureId subtree_capture_id,
bool has_transparent_background,
bool cache_render_pass,
bool has_damage_from_contributing_content,
@@ -101,6 +102,7 @@
this->filters = filters;
this->backdrop_filters = backdrop_filters;
this->backdrop_filter_bounds = backdrop_filter_bounds;
+ this->subtree_capture_id = subtree_capture_id;
this->has_transparent_background = has_transparent_background;
this->cache_render_pass = cache_render_pass;
this->has_damage_from_contributing_content =
@@ -222,8 +224,9 @@
quad_list.size());
copy_pass->SetAll(id, output_rect, damage_rect, transform_to_root_target,
filters, backdrop_filters, backdrop_filter_bounds,
- has_transparent_background, cache_render_pass,
- has_damage_from_contributing_content, generate_mipmap);
+ subtree_capture_id, has_transparent_background,
+ cache_render_pass, has_damage_from_contributing_content,
+ generate_mipmap);
if (shared_quad_state_list.empty()) {
DCHECK(quad_list.empty());
diff --git a/components/viz/common/quads/compositor_render_pass.h b/components/viz/common/quads/compositor_render_pass.h
index c0a6f7b..ca96b4c8 100644
--- a/components/viz/common/quads/compositor_render_pass.h
+++ b/components/viz/common/quads/compositor_render_pass.h
@@ -21,6 +21,7 @@
#include "components/viz/common/quads/largest_draw_quad.h"
#include "components/viz/common/quads/quad_list.h"
#include "components/viz/common/quads/render_pass_internal.h"
+#include "components/viz/common/surfaces/subtree_capture_id.h"
#include "components/viz/common/viz_common_export.h"
#include "ui/gfx/display_color_spaces.h"
#include "ui/gfx/geometry/rect.h"
@@ -67,6 +68,7 @@
const cc::FilterOperations& filters,
const cc::FilterOperations& backdrop_filters,
const base::Optional<gfx::RRectF>& backdrop_filter_bounds,
+ SubtreeCaptureId subtree_capture_id,
bool has_transparent_background,
bool cache_render_pass,
bool has_damage_from_contributing_content,
@@ -85,6 +87,10 @@
// Uniquely identifies the render pass in the compositor's current frame.
CompositorRenderPassId id;
+ // A unique ID that identifies a layer subtree which produces this render
+ // pass, so that it can be captured by a FrameSinkVideoCapturer.
+ SubtreeCaptureId subtree_capture_id;
+
// For testing functions.
// TODO(vmpstr): See if we can clean these up by moving the tests to use
// AggregatedRenderPasses where appropriate.
diff --git a/components/viz/common/quads/compositor_render_pass_unittest.cc b/components/viz/common/quads/compositor_render_pass_unittest.cc
index 720dfd42..e4d22c5 100644
--- a/components/viz/common/quads/compositor_render_pass_unittest.cc
+++ b/components/viz/common/quads/compositor_render_pass_unittest.cc
@@ -13,6 +13,7 @@
#include "components/viz/common/quads/aggregated_render_pass.h"
#include "components/viz/common/quads/compositor_render_pass_draw_quad.h"
#include "components/viz/common/quads/solid_color_draw_quad.h"
+#include "components/viz/common/surfaces/subtree_capture_id.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/effects/SkBlurImageFilter.h"
#include "ui/gfx/geometry/rect_conversions.h"
@@ -37,6 +38,7 @@
EXPECT_EQ(expected->filters, actual->filters);
EXPECT_EQ(expected->backdrop_filters, actual->backdrop_filters);
EXPECT_EQ(expected->backdrop_filter_bounds, actual->backdrop_filter_bounds);
+ EXPECT_EQ(expected->subtree_capture_id, actual->subtree_capture_id);
EXPECT_EQ(expected->has_transparent_background,
actual->has_transparent_background);
EXPECT_EQ(expected->generate_mipmap, actual->generate_mipmap);
@@ -137,7 +139,7 @@
auto pass = CompositorRenderPass::Create();
pass->SetAll(id, output_rect, damage_rect, transform_to_root, filters,
- backdrop_filters, backdrop_filter_bounds,
+ backdrop_filters, backdrop_filter_bounds, SubtreeCaptureId{1u},
has_transparent_background, cache_render_pass,
has_damage_from_contributing_content, generate_mipmap);
@@ -191,12 +193,12 @@
bool contrib_generate_mipmap = false;
auto contrib = CompositorRenderPass::Create();
- contrib->SetAll(contrib_id, contrib_output_rect, contrib_damage_rect,
- contrib_transform_to_root, contrib_filters,
- contrib_backdrop_filters, contrib_backdrop_filter_bounds,
- contrib_has_transparent_background, contrib_cache_render_pass,
- contrib_has_damage_from_contributing_content,
- contrib_generate_mipmap);
+ contrib->SetAll(
+ contrib_id, contrib_output_rect, contrib_damage_rect,
+ contrib_transform_to_root, contrib_filters, contrib_backdrop_filters,
+ contrib_backdrop_filter_bounds, SubtreeCaptureId{2u},
+ contrib_has_transparent_background, contrib_cache_render_pass,
+ contrib_has_damage_from_contributing_content, contrib_generate_mipmap);
SharedQuadState* contrib_shared_state =
contrib->CreateAndAppendSharedQuadState();
@@ -247,7 +249,7 @@
auto pass = CompositorRenderPass::Create();
pass->SetAll(id, output_rect, damage_rect, transform_to_root, filters,
- backdrop_filters, backdrop_filter_bounds,
+ backdrop_filters, backdrop_filter_bounds, SubtreeCaptureId(),
has_transparent_background, cache_render_pass,
has_damage_from_contributing_content, generate_mipmap);
diff --git a/components/viz/common/surfaces/subtree_capture_id.cc b/components/viz/common/surfaces/subtree_capture_id.cc
new file mode 100644
index 0000000..6be32be
--- /dev/null
+++ b/components/viz/common/surfaces/subtree_capture_id.cc
@@ -0,0 +1,15 @@
+// Copyright 2020 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/common/surfaces/subtree_capture_id.h"
+
+#include "base/strings/stringprintf.h"
+
+namespace viz {
+
+std::string SubtreeCaptureId::ToString() const {
+ return base::StringPrintf("SubtreeCaptureId(%u)", subtree_id_);
+}
+
+} // namespace viz
diff --git a/components/viz/common/surfaces/subtree_capture_id.h b/components/viz/common/surfaces/subtree_capture_id.h
new file mode 100644
index 0000000..3f92911
--- /dev/null
+++ b/components/viz/common/surfaces/subtree_capture_id.h
@@ -0,0 +1,48 @@
+// Copyright 2020 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.
+
+#ifndef COMPONENTS_VIZ_COMMON_SURFACES_SUBTREE_CAPTURE_ID_H_
+#define COMPONENTS_VIZ_COMMON_SURFACES_SUBTREE_CAPTURE_ID_H_
+
+#include <cstdint>
+#include <string>
+
+#include "components/viz/common/viz_common_export.h"
+
+namespace viz {
+
+// A SubtreeCaptureId uniquely identifies a layer subtree within a
+// CompositorFrameSink, which can be captured independently from the root
+// CompositorFrameSink by the FrameSinkVideoCapturer.
+//
+// Use the SubtreeCaptureIdAllocator to allocate a valid instace of this class.
+class VIZ_COMMON_EXPORT SubtreeCaptureId {
+ public:
+ constexpr SubtreeCaptureId() = default;
+ constexpr explicit SubtreeCaptureId(uint32_t subtree_id)
+ : subtree_id_(subtree_id) {}
+ constexpr SubtreeCaptureId(const SubtreeCaptureId&) = default;
+ SubtreeCaptureId& operator=(const SubtreeCaptureId&) = default;
+ ~SubtreeCaptureId() = default;
+
+ constexpr bool is_valid() const { return subtree_id_ != 0; }
+ constexpr uint32_t subtree_id() const { return subtree_id_; }
+
+ bool operator==(const SubtreeCaptureId& rhs) const {
+ return subtree_id_ == rhs.subtree_id_;
+ }
+ bool operator!=(const SubtreeCaptureId& rhs) const { return !(*this == rhs); }
+ bool operator<(const SubtreeCaptureId& rhs) const {
+ return subtree_id_ < rhs.subtree_id_;
+ }
+
+ std::string ToString() const;
+
+ private:
+ uint32_t subtree_id_ = 0;
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_COMMON_SURFACES_SUBTREE_CAPTURE_ID_H_
diff --git a/components/viz/common/surfaces/subtree_capture_id_allocator.h b/components/viz/common/surfaces/subtree_capture_id_allocator.h
new file mode 100644
index 0000000..330491f9
--- /dev/null
+++ b/components/viz/common/surfaces/subtree_capture_id_allocator.h
@@ -0,0 +1,33 @@
+// Copyright 2020 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.
+
+#ifndef COMPONENTS_VIZ_COMMON_SURFACES_SUBTREE_CAPTURE_ID_ALLOCATOR_H_
+#define COMPONENTS_VIZ_COMMON_SURFACES_SUBTREE_CAPTURE_ID_ALLOCATOR_H_
+
+#include <cstdint>
+
+#include "components/viz/common/surfaces/subtree_capture_id.h"
+
+namespace viz {
+
+// Generates SubtreeCaptureId's by incrementally increasing the subtree_id's.
+class VIZ_COMMON_EXPORT SubtreeCaptureIdAllocator {
+ public:
+ SubtreeCaptureIdAllocator() = default;
+ SubtreeCaptureIdAllocator(const SubtreeCaptureIdAllocator&) = delete;
+ SubtreeCaptureIdAllocator& operator=(const SubtreeCaptureIdAllocator&) =
+ delete;
+ ~SubtreeCaptureIdAllocator() = default;
+
+ SubtreeCaptureId NextSubtreeCaptureId() {
+ return SubtreeCaptureId(next_subtree_id_++);
+ }
+
+ private:
+ uint32_t next_subtree_id_ = 1u;
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_COMMON_SURFACES_SUBTREE_CAPTURE_ID_ALLOCATOR_H_
diff --git a/components/viz/host/client_frame_sink_video_capturer.cc b/components/viz/host/client_frame_sink_video_capturer.cc
index b77d91d..405ec19 100644
--- a/components/viz/host/client_frame_sink_video_capturer.cc
+++ b/components/viz/host/client_frame_sink_video_capturer.cc
@@ -72,11 +72,13 @@
}
void ClientFrameSinkVideoCapturer::ChangeTarget(
- const base::Optional<FrameSinkId>& frame_sink_id) {
+ const base::Optional<FrameSinkId>& frame_sink_id,
+ SubtreeCaptureId subtree_capture_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
target_ = frame_sink_id;
- capturer_remote_->ChangeTarget(frame_sink_id);
+ subtree_capture_id_ = subtree_capture_id;
+ capturer_remote_->ChangeTarget(frame_sink_id, subtree_capture_id);
}
void ClientFrameSinkVideoCapturer::Start(
@@ -192,7 +194,7 @@
if (auto_throttling_enabled_)
capturer_remote_->SetAutoThrottlingEnabled(*auto_throttling_enabled_);
if (target_)
- capturer_remote_->ChangeTarget(target_);
+ capturer_remote_->ChangeTarget(target_, subtree_capture_id_);
for (Overlay* overlay : overlays_)
overlay->EstablishConnection(capturer_remote_.get());
if (is_started_)
diff --git a/components/viz/host/client_frame_sink_video_capturer.h b/components/viz/host/client_frame_sink_video_capturer.h
index 993a2db..019a5ea 100644
--- a/components/viz/host/client_frame_sink_video_capturer.h
+++ b/components/viz/host/client_frame_sink_video_capturer.h
@@ -13,6 +13,7 @@
#include "base/sequence_checker.h"
#include "base/time/time.h"
#include "components/viz/common/surfaces/frame_sink_id.h"
+#include "components/viz/common/surfaces/subtree_capture_id.h"
#include "components/viz/host/viz_host_export.h"
#include "media/base/video_types.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -83,7 +84,8 @@
const gfx::Size& max_size,
bool use_fixed_aspect_ratio);
void SetAutoThrottlingEnabled(bool enabled);
- void ChangeTarget(const base::Optional<FrameSinkId>& frame_sink_id);
+ void ChangeTarget(const base::Optional<FrameSinkId>& frame_sink_id,
+ SubtreeCaptureId subtree_capture_id);
void Stop();
void RequestRefreshFrame();
@@ -151,6 +153,7 @@
base::Optional<ResolutionConstraints> resolution_constraints_;
base::Optional<bool> auto_throttling_enabled_;
base::Optional<FrameSinkId> target_;
+ SubtreeCaptureId subtree_capture_id_;
// Overlays are owned by the callers of CreateOverlay().
std::vector<Overlay*> overlays_;
bool is_started_ = false;
diff --git a/components/viz/service/BUILD.gn b/components/viz/service/BUILD.gn
index 7d3a7b3..c66d791c 100644
--- a/components/viz/service/BUILD.gn
+++ b/components/viz/service/BUILD.gn
@@ -161,6 +161,8 @@
"hit_test/hit_test_manager.cc",
"hit_test/hit_test_manager.h",
"surfaces/latest_local_surface_id_lookup_delegate.h",
+ "surfaces/pending_copy_output_request.cc",
+ "surfaces/pending_copy_output_request.h",
"surfaces/referenced_surface_tracker.cc",
"surfaces/referenced_surface_tracker.h",
"surfaces/surface.cc",
diff --git a/components/viz/service/display/display_unittest.cc b/components/viz/service/display/display_unittest.cc
index ff06e95..86d958e 100644
--- a/components/viz/service/display/display_unittest.cc
+++ b/components/viz/service/display/display_unittest.cc
@@ -29,6 +29,7 @@
#include "components/viz/common/quads/surface_draw_quad.h"
#include "components/viz/common/surfaces/frame_sink_id.h"
#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
+#include "components/viz/common/surfaces/subtree_capture_id.h"
#include "components/viz/service/display/aggregated_frame.h"
#include "components/viz/service/display/delegated_ink_point_renderer_skia.h"
#include "components/viz/service/display/direct_renderer.h"
@@ -734,8 +735,8 @@
bd_pass->SetAll(render_pass_id_generator.GenerateNextId(),
sub_surface_rect, no_damage, gfx::Transform(),
cc::FilterOperations(), backdrop_filters,
- gfx::RRectF(gfx::RectF(sub_surface_rect), 0), false,
- false, false, false);
+ gfx::RRectF(gfx::RectF(sub_surface_rect), 0),
+ SubtreeCaptureId(), false, false, false, false);
pass_list.push_back(std::move(bd_pass));
CompositorFrame frame = CompositorFrameBuilder()
diff --git a/components/viz/service/display/surface_aggregator_unittest.cc b/components/viz/service/display/surface_aggregator_unittest.cc
index a5a95f40..2dcab90 100644
--- a/components/viz/service/display/surface_aggregator_unittest.cc
+++ b/components/viz/service/display/surface_aggregator_unittest.cc
@@ -32,11 +32,13 @@
#include "components/viz/common/quads/texture_draw_quad.h"
#include "components/viz/common/quads/yuv_video_draw_quad.h"
#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
+#include "components/viz/common/surfaces/subtree_capture_id.h"
#include "components/viz/service/display/aggregated_frame.h"
#include "components/viz/service/display/display_resource_provider.h"
#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
#include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
+#include "components/viz/service/surfaces/pending_copy_output_request.h"
#include "components/viz/service/surfaces/surface.h"
#include "components/viz/service/surfaces/surface_manager.h"
#include "components/viz/test/compositor_frame_helpers.h"
@@ -917,8 +919,8 @@
CopyOutputRequest* RequestCopyOfOutput() {
auto copy_request = CopyOutputRequest::CreateStubForTesting();
auto* copy_request_ptr = copy_request.get();
- root_sink_->RequestCopyOfOutput(local_surface_id(),
- std::move(copy_request));
+ root_sink_->RequestCopyOfOutput(PendingCopyOutputRequest{
+ local_surface_id(), SubtreeCaptureId(), std::move(copy_request)});
return copy_request_ptr;
}
@@ -1590,8 +1592,8 @@
embedded_local_surface_id, device_scale_factor);
auto copy_request = CopyOutputRequest::CreateStubForTesting();
auto* copy_request_ptr = copy_request.get();
- embedded_support->RequestCopyOfOutput(embedded_local_surface_id,
- std::move(copy_request));
+ embedded_support->RequestCopyOfOutput(
+ {embedded_local_surface_id, SubtreeCaptureId(), std::move(copy_request)});
std::vector<Quad> root_quads = {
Quad::SolidColorQuad(SK_ColorWHITE, gfx::Rect(5, 5)),
@@ -1743,8 +1745,8 @@
embedded_local_surface_id, device_scale_factor);
auto copy_request(CopyOutputRequest::CreateStubForTesting());
auto* copy_request_ptr = copy_request.get();
- embedded_support->RequestCopyOfOutput(embedded_local_surface_id,
- std::move(copy_request));
+ embedded_support->RequestCopyOfOutput(
+ {embedded_local_surface_id, SubtreeCaptureId(), std::move(copy_request)});
ParentLocalSurfaceIdAllocator parent_allocator;
parent_allocator.GenerateId();
@@ -8862,8 +8864,8 @@
// Now add a CopyOutputRequest on the child surface, so that the delegated
// ink metadata does get populated on the aggregated frame.
auto copy_request = CopyOutputRequest::CreateStubForTesting();
- child_sink_->RequestCopyOfOutput(child_local_surface_id,
- std::move(copy_request));
+ child_sink_->RequestCopyOfOutput(
+ {child_local_surface_id, SubtreeCaptureId(), std::move(copy_request)});
aggregated_frame = AggregateFrame(root_surface_id);
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support.cc b/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
index e677ecec1..94c8969b 100644
--- a/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
+++ b/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
@@ -256,16 +256,17 @@
surface_resource_holder_.ReceiveFromChild(resources);
}
-std::vector<std::unique_ptr<CopyOutputRequest>>
+std::vector<PendingCopyOutputRequest>
CompositorFrameSinkSupport::TakeCopyOutputRequests(
const LocalSurfaceId& latest_local_id) {
- std::vector<std::unique_ptr<CopyOutputRequest>> results;
+ std::vector<PendingCopyOutputRequest> results;
for (auto it = copy_output_requests_.begin();
it != copy_output_requests_.end();) {
// Requests with a non-valid local id should be satisfied as soon as
// possible.
- if (!it->first.is_valid() || it->first <= latest_local_id) {
- results.push_back(std::move(it->second));
+ if (!it->local_surface_id.is_valid() ||
+ it->local_surface_id <= latest_local_id) {
+ results.push_back(std::move(*it));
it = copy_output_requests_.erase(it);
} else {
++it;
@@ -765,10 +766,8 @@
}
void CompositorFrameSinkSupport::RequestCopyOfOutput(
- const LocalSurfaceId& local_surface_id,
- std::unique_ptr<CopyOutputRequest> copy_request) {
- copy_output_requests_.push_back(
- std::make_pair(local_surface_id, std::move(copy_request)));
+ PendingCopyOutputRequest pending_copy_output_request) {
+ copy_output_requests_.push_back(std::move(pending_copy_output_request));
if (last_activated_surface_id_.is_valid()) {
BeginFrameAck ack;
ack.has_damage = true;
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support.h b/components/viz/service/frame_sinks/compositor_frame_sink_support.h
index 3a5f1fa..82c63a7 100644
--- a/components/viz/service/frame_sinks/compositor_frame_sink_support.h
+++ b/components/viz/service/frame_sinks/compositor_frame_sink_support.h
@@ -6,6 +6,7 @@
#define COMPONENTS_VIZ_SERVICE_FRAME_SINKS_COMPOSITOR_FRAME_SINK_SUPPORT_H_
#include <memory>
+#include <set>
#include <vector>
#include "base/callback.h"
@@ -127,7 +128,7 @@
const std::vector<TransferableResource>& resources) override;
// Takes the CopyOutputRequests that were requested for a surface with at
// most |local_surface_id|.
- std::vector<std::unique_ptr<CopyOutputRequest>> TakeCopyOutputRequests(
+ std::vector<PendingCopyOutputRequest> TakeCopyOutputRequests(
const LocalSurfaceId& local_surface_id) override;
void OnFrameTokenChanged(uint32_t frame_token) override;
void OnSurfaceProcessed(Surface* surface) override;
@@ -179,8 +180,8 @@
void AttachCaptureClient(CapturableFrameSink::Client* client) override;
void DetachCaptureClient(CapturableFrameSink::Client* client) override;
gfx::Size GetActiveFrameSize() override;
- void RequestCopyOfOutput(const LocalSurfaceId& local_surface_id,
- std::unique_ptr<CopyOutputRequest> request) override;
+ void RequestCopyOfOutput(
+ PendingCopyOutputRequest pending_copy_output_request) override;
const CompositorFrameMetadata* GetLastActivatedFrameMetadata() override;
HitTestAggregator* GetHitTestAggregator();
@@ -197,8 +198,7 @@
// string.
static const char* GetSubmitResultAsString(SubmitResult result);
- const std::vector<
- std::pair<LocalSurfaceId, std::unique_ptr<CopyOutputRequest>>>&
+ const std::vector<PendingCopyOutputRequest>&
copy_output_requests_for_testing() const {
return copy_output_requests_;
}
@@ -313,13 +313,13 @@
// These are the CopyOutputRequests made on the frame sink (as opposed to
// being included as a part of a CompositorFrame). They stay here until a
// Surface with a LocalSurfaceId which is at least the stored LocalSurfaceId
- // takes them. For example, if we store a pair of LocalSurfaceId stored_id and
- // a CopyOutputRequest, then a surface with LocalSurfaceId >= stored_id will
- // take it, but a surface with LocalSurfaceId < stored_id will not. Note that
- // if stored_id is default initialized, then the next surface will take it
- // regardless of its LocalSurfaceId.
- std::vector<std::pair<LocalSurfaceId, std::unique_ptr<CopyOutputRequest>>>
- copy_output_requests_;
+ // takes them. For example, for a stored PendingCopyOutputRequest, a surface
+ // with LocalSurfaceId >= PendingCopyOutputRequest::local_surface_id will take
+ // it, but a surface with LocalSurfaceId <
+ // PendingCopyOutputRequest::local_surface_id will not. Note that if the
+ // PendingCopyOutputRequest::local_surface_id is default initialized, then the
+ // next surface will take it regardless of its LocalSurfaceId.
+ std::vector<PendingCopyOutputRequest> copy_output_requests_;
mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback
compositor_frame_callback_;
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc b/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
index 62b7554..48538474 100644
--- a/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
+++ b/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
@@ -14,6 +14,7 @@
#include "components/viz/common/frame_sinks/copy_output_result.h"
#include "components/viz/common/quads/compositor_frame.h"
#include "components/viz/common/surfaces/frame_sink_id.h"
+#include "components/viz/common/surfaces/subtree_capture_id.h"
#include "components/viz/common/surfaces/surface_id.h"
#include "components/viz/common/surfaces/surface_info.h"
#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
@@ -793,6 +794,60 @@
std::move(finished).Run();
}
+TEST_F(CompositorFrameSinkSupportTest, CopyRequestOnSubtree) {
+ const SurfaceId surface_id(support_->frame_sink_id(), local_surface_id_);
+
+ constexpr SubtreeCaptureId kSubtreeId1(22);
+ constexpr SubtreeCaptureId kSubtreeId2(44);
+
+ {
+ auto frame = CompositorFrameBuilder()
+ .AddDefaultRenderPass()
+ .AddDefaultRenderPass()
+ .SetReferencedSurfaces({SurfaceRange(surface_id)})
+ .Build();
+ frame.render_pass_list.front()->subtree_capture_id = kSubtreeId1;
+ support_->SubmitCompositorFrame(local_surface_id_, std::move(frame));
+ EXPECT_EQ(surface_observer_.last_created_surface_id().local_surface_id(),
+ local_surface_id_);
+ }
+
+ // Requesting copy of output of a render pass identifiable by a valid
+ // SubtreeCaptureId.
+ bool called1 = false;
+ base::RunLoop called1_run_loop;
+ auto request = std::make_unique<CopyOutputRequest>(
+ CopyOutputRequest::ResultFormat::RGBA_BITMAP,
+ base::BindOnce(&CopyRequestTestCallback, &called1,
+ called1_run_loop.QuitClosure()));
+ support_->RequestCopyOfOutput(
+ {local_surface_id_, kSubtreeId1, std::move(request)});
+ GetSurfaceForId(surface_id)->TakeCopyOutputRequestsFromClient();
+ EXPECT_FALSE(called1);
+
+ // Requesting copy of output using a SubtreeCaptureId that has no associated
+ // render pass. The callback will be called immediately.
+ bool called2 = false;
+ base::RunLoop called2_run_loop;
+ request = std::make_unique<CopyOutputRequest>(
+ CopyOutputRequest::ResultFormat::RGBA_BITMAP,
+ base::BindOnce(&CopyRequestTestCallback, &called2,
+ called2_run_loop.QuitClosure()));
+ support_->RequestCopyOfOutput(
+ {local_surface_id_, kSubtreeId2, std::move(request)});
+ GetSurfaceForId(surface_id)->TakeCopyOutputRequestsFromClient();
+ called2_run_loop.Run();
+ EXPECT_FALSE(called1);
+ EXPECT_TRUE(called2);
+
+ support_->EvictSurface(local_surface_id_);
+ ExpireAllTemporaryReferences();
+ local_surface_id_ = LocalSurfaceId();
+ manager_.surface_manager()->GarbageCollectSurfaces();
+ called1_run_loop.Run();
+ EXPECT_TRUE(called1);
+}
+
TEST_F(CompositorFrameSinkSupportTest, DuplicateCopyRequest) {
const SurfaceId surface_id(support_->frame_sink_id(), local_surface_id_);
@@ -814,7 +869,8 @@
called1_run_loop.QuitClosure()));
request->set_source(kArbitrarySourceId1);
- support_->RequestCopyOfOutput(local_surface_id_, std::move(request));
+ support_->RequestCopyOfOutput(
+ {local_surface_id_, SubtreeCaptureId(), std::move(request)});
GetSurfaceForId(surface_id)->TakeCopyOutputRequestsFromClient();
EXPECT_FALSE(called1);
@@ -826,7 +882,8 @@
called2_run_loop.QuitClosure()));
request->set_source(kArbitrarySourceId2);
- support_->RequestCopyOfOutput(local_surface_id_, std::move(request));
+ support_->RequestCopyOfOutput(
+ {local_surface_id_, SubtreeCaptureId(), std::move(request)});
GetSurfaceForId(surface_id)->TakeCopyOutputRequestsFromClient();
// Callbacks have different sources so neither should be called.
EXPECT_FALSE(called1);
@@ -840,7 +897,8 @@
called3_run_loop.QuitClosure()));
request->set_source(kArbitrarySourceId1);
- support_->RequestCopyOfOutput(local_surface_id_, std::move(request));
+ support_->RequestCopyOfOutput(
+ {local_surface_id_, SubtreeCaptureId(), std::move(request)});
GetSurfaceForId(surface_id)->TakeCopyOutputRequestsFromClient();
// Two callbacks are from source1, so the first should be called.
called1_run_loop.Run();
@@ -1065,7 +1123,8 @@
auto request = std::make_unique<CopyOutputRequest>(
CopyOutputRequest::ResultFormat::RGBA_BITMAP,
base::BindOnce(StubResultCallback));
- support_->RequestCopyOfOutput(local_surface_id1, std::move(request));
+ support_->RequestCopyOfOutput(
+ {local_surface_id1, SubtreeCaptureId(), std::move(request)});
// First surface takes CopyOutputRequests from its client. Now only the first
// surface should report having CopyOutputRequests.
@@ -1107,7 +1166,8 @@
auto request = std::make_unique<CopyOutputRequest>(
CopyOutputRequest::ResultFormat::RGBA_BITMAP,
base::BindOnce(StubResultCallback));
- support_->RequestCopyOfOutput(local_surface_id2, std::move(request));
+ support_->RequestCopyOfOutput(
+ {local_surface_id2, SubtreeCaptureId(), std::move(request)});
// The first surface doesn't have copy output requests, because it can't
// satisfy the request that the client has.
@@ -1148,7 +1208,8 @@
auto request = std::make_unique<CopyOutputRequest>(
CopyOutputRequest::ResultFormat::RGBA_BITMAP,
base::BindOnce(StubResultCallback));
- support_->RequestCopyOfOutput(local_surface_id1, std::move(request));
+ support_->RequestCopyOfOutput(
+ {local_surface_id1, SubtreeCaptureId(), std::move(request)});
// Create the second surface.
support_->SubmitCompositorFrame(local_surface_id2,
diff --git a/components/viz/service/frame_sinks/frame_sink_manager_impl.cc b/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
index 14d8887..b8c4736 100644
--- a/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
+++ b/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
@@ -7,15 +7,19 @@
#include <stddef.h>
#include <stdint.h>
+#include <utility>
+
#include "base/bind.h"
#include "base/check_op.h"
#include "base/metrics/histogram_functions.h"
#include "base/trace_event/trace_event.h"
+#include "components/viz/common/surfaces/subtree_capture_id.h"
#include "components/viz/service/display/shared_bitmap_manager.h"
#include "components/viz/service/display_embedder/output_surface_provider.h"
#include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
#include "components/viz/service/frame_sinks/video_capture/capturable_frame_sink.h"
#include "components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h"
+#include "components/viz/service/surfaces/pending_copy_output_request.h"
namespace viz {
@@ -300,8 +304,8 @@
// |request| will send an empty result when it goes out of scope.
return;
}
- it->second->RequestCopyOfOutput(surface_id.local_surface_id(),
- std::move(request));
+ it->second->RequestCopyOfOutput(PendingCopyOutputRequest{
+ surface_id.local_surface_id(), SubtreeCaptureId(), std::move(request)});
}
void FrameSinkManagerImpl::SetHitTestAsyncQueriedDebugRegions(
diff --git a/components/viz/service/frame_sinks/video_capture/capturable_frame_sink.h b/components/viz/service/frame_sinks/video_capture/capturable_frame_sink.h
index ed9e14e..fb5ec96 100644
--- a/components/viz/service/frame_sinks/video_capture/capturable_frame_sink.h
+++ b/components/viz/service/frame_sinks/video_capture/capturable_frame_sink.h
@@ -8,6 +8,7 @@
#include <memory>
#include "base/time/time.h"
+#include "components/viz/service/surfaces/pending_copy_output_request.h"
#include "ui/gfx/geometry/size.h"
namespace gfx {
@@ -17,7 +18,6 @@
namespace viz {
class CompositorFrameMetadata;
-class CopyOutputRequest;
class LocalSurfaceId;
// Interface for CompositorFrameSink implementations that support frame sink
@@ -60,8 +60,7 @@
// default constructed, then the next surface will provide the copy output
// regardless of its LocalSurfaceId.
virtual void RequestCopyOfOutput(
- const LocalSurfaceId& local_surface_id,
- std::unique_ptr<CopyOutputRequest> request) = 0;
+ PendingCopyOutputRequest pending_copy_output_request) = 0;
// Returns the CompositorFrameMetadata of the last activated CompositorFrame.
// Return null if no CompositorFrame has activated yet.
diff --git a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc
index d5a5739..ce5fca55 100644
--- a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc
+++ b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc
@@ -226,15 +226,18 @@
}
void FrameSinkVideoCapturerImpl::ChangeTarget(
- const base::Optional<FrameSinkId>& frame_sink_id) {
+ const base::Optional<FrameSinkId>& frame_sink_id,
+ const SubtreeCaptureId& subtree_capture_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (frame_sink_id) {
requested_target_ = *frame_sink_id;
+ request_subtree_id_ = subtree_capture_id;
SetResolvedTarget(
frame_sink_manager_->FindCapturableFrameSink(requested_target_));
} else {
requested_target_ = FrameSinkId();
+ request_subtree_id_ = SubtreeCaptureId();
SetResolvedTarget(nullptr);
}
}
@@ -660,7 +663,8 @@
request->scale_to().ToString().c_str(), utilization));
}
- resolved_target_->RequestCopyOfOutput(LocalSurfaceId(), std::move(request));
+ resolved_target_->RequestCopyOfOutput(
+ {LocalSurfaceId(), request_subtree_id_, std::move(request)});
}
void FrameSinkVideoCapturerImpl::DidCopyFrame(
diff --git a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h
index 2f0e13f..3319829 100644
--- a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h
+++ b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h
@@ -9,7 +9,9 @@
#include <memory>
#include <queue>
+#include <string>
#include <vector>
+
#include "base/callback_forward.h"
#include "base/containers/flat_map.h"
#include "base/macros.h"
@@ -22,6 +24,7 @@
#include "base/unguessable_token.h"
#include "components/viz/common/quads/compositor_frame_metadata.h"
#include "components/viz/common/surfaces/frame_sink_id.h"
+#include "components/viz/common/surfaces/subtree_capture_id.h"
#include "components/viz/service/frame_sinks/video_capture/capturable_frame_sink.h"
#include "components/viz/service/frame_sinks/video_capture/in_flight_frame_delivery.h"
#include "components/viz/service/frame_sinks/video_capture/interprocess_frame_pool.h"
@@ -110,7 +113,8 @@
const gfx::Size& max_size,
bool use_fixed_aspect_ratio) final;
void SetAutoThrottlingEnabled(bool enabled) final;
- void ChangeTarget(const base::Optional<FrameSinkId>& frame_sink_id) final;
+ void ChangeTarget(const base::Optional<FrameSinkId>& frame_sink_id,
+ const SubtreeCaptureId& subtree_capture_id) final;
void Start(mojo::PendingRemote<mojom::FrameSinkVideoConsumer> consumer) final;
void Stop() final;
void RequestRefreshFrame() final;
@@ -258,6 +262,12 @@
// ChangeTarget().
FrameSinkId requested_target_;
+ // If valid, this is the ID of a layer subtree within the requested frame
+ // sink, whose associated render pass should be captured by this capturer.
+ // If not valid, then this capturer capturer the root render pass of the
+ // target frame sink.
+ SubtreeCaptureId request_subtree_id_;
+
// The resolved target of video capture, or null if the requested target does
// not yet exist (or no longer exists).
CapturableFrameSink* resolved_target_ = nullptr;
diff --git a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc
index a0094e46..0f427669 100644
--- a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc
+++ b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc
@@ -19,6 +19,7 @@
#include "components/viz/common/frame_sinks/copy_output_request.h"
#include "components/viz/common/frame_sinks/copy_output_result.h"
#include "components/viz/common/frame_sinks/copy_output_util.h"
+#include "components/viz/common/surfaces/subtree_capture_id.h"
#include "components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_manager.h"
#include "media/base/limits.h"
#include "media/base/video_util.h"
@@ -251,8 +252,8 @@
gfx::Size GetActiveFrameSize() override { return source_size(); }
void RequestCopyOfOutput(
- const LocalSurfaceId& local_surface_id,
- std::unique_ptr<CopyOutputRequest> request) override {
+ PendingCopyOutputRequest pending_copy_output_request) override {
+ auto& request = pending_copy_output_request.copy_output_request;
EXPECT_EQ(CopyOutputResult::Format::I420_PLANES, request->result_format());
EXPECT_NE(base::UnguessableToken(), request->source());
EXPECT_EQ(gfx::Rect(size_set_.source_size), request->area());
@@ -522,7 +523,7 @@
.WillRepeatedly(Return(&frame_sink_));
EXPECT_EQ(FrameSinkId(), capturer_->requested_target());
- capturer_->ChangeTarget(kFrameSinkId);
+ capturer_->ChangeTarget(kFrameSinkId, SubtreeCaptureId());
EXPECT_EQ(kFrameSinkId, capturer_->requested_target());
EXPECT_EQ(capturer_.get(), frame_sink_.attached_client());
}
@@ -534,7 +535,7 @@
.WillRepeatedly(Return(nullptr));
EXPECT_EQ(FrameSinkId(), capturer_->requested_target());
- capturer_->ChangeTarget(kFrameSinkId);
+ capturer_->ChangeTarget(kFrameSinkId, SubtreeCaptureId());
EXPECT_EQ(kFrameSinkId, capturer_->requested_target());
EXPECT_EQ(nullptr, frame_sink_.attached_client());
@@ -569,7 +570,7 @@
// Now, set the target. As it resolves, the capturer will immediately attempt
// a refresh capture, which will cancel the timer and trigger a copy request.
- capturer_->ChangeTarget(kFrameSinkId);
+ capturer_->ChangeTarget(kFrameSinkId, SubtreeCaptureId());
EXPECT_EQ(1, frame_sink_.num_copy_results());
EXPECT_FALSE(IsRefreshRetryTimerRunning());
@@ -585,7 +586,7 @@
EXPECT_CALL(frame_sink_manager_, FindCapturableFrameSink(kFrameSinkId))
.WillRepeatedly(Return(&frame_sink_));
- capturer_->ChangeTarget(kFrameSinkId);
+ capturer_->ChangeTarget(kFrameSinkId, SubtreeCaptureId());
EXPECT_FALSE(IsRefreshRetryTimerRunning());
MockConsumer consumer;
@@ -678,7 +679,7 @@
EXPECT_CALL(frame_sink_manager_, FindCapturableFrameSink(kFrameSinkId))
.WillRepeatedly(Return(&frame_sink_));
- capturer_->ChangeTarget(kFrameSinkId);
+ capturer_->ChangeTarget(kFrameSinkId, SubtreeCaptureId());
NiceMock<MockConsumer> consumer;
StartCapture(&consumer);
@@ -776,7 +777,7 @@
EXPECT_CALL(frame_sink_manager_, FindCapturableFrameSink(kFrameSinkId))
.WillRepeatedly(Return(&frame_sink_));
- capturer_->ChangeTarget(kFrameSinkId);
+ capturer_->ChangeTarget(kFrameSinkId, SubtreeCaptureId());
NiceMock<MockConsumer> consumer;
StartCapture(&consumer);
@@ -833,7 +834,7 @@
EXPECT_CALL(frame_sink_manager_, FindCapturableFrameSink(kFrameSinkId))
.WillRepeatedly(Return(&frame_sink_));
- capturer_->ChangeTarget(kFrameSinkId);
+ capturer_->ChangeTarget(kFrameSinkId, SubtreeCaptureId());
// Start capturing to the first consumer.
MockConsumer consumer;
@@ -920,7 +921,7 @@
EXPECT_CALL(frame_sink_manager_, FindCapturableFrameSink(kFrameSinkId))
.WillRepeatedly(Return(&frame_sink_));
- capturer_->ChangeTarget(kFrameSinkId);
+ capturer_->ChangeTarget(kFrameSinkId, SubtreeCaptureId());
MockConsumer consumer;
const int num_refresh_frames = 2; // Initial, plus later refresh.
@@ -976,7 +977,7 @@
EXPECT_CALL(frame_sink_manager_, FindCapturableFrameSink(kFrameSinkId))
.WillRepeatedly(Return(&frame_sink_));
- capturer_->ChangeTarget(kFrameSinkId);
+ capturer_->ChangeTarget(kFrameSinkId, SubtreeCaptureId());
MockConsumer consumer;
constexpr int num_refresh_frames = 3; // Initial, plus two refreshes after
@@ -1073,7 +1074,7 @@
TEST_F(FrameSinkVideoCapturerTest, CompositorFrameMetadataReachesConsumer) {
EXPECT_CALL(frame_sink_manager_, FindCapturableFrameSink(kFrameSinkId))
.WillRepeatedly(Return(&frame_sink_));
- capturer_->ChangeTarget(kFrameSinkId);
+ capturer_->ChangeTarget(kFrameSinkId, SubtreeCaptureId());
MockConsumer consumer;
// Initial refresh frame for starting capture, plus later refresh.
@@ -1133,7 +1134,7 @@
TEST_F(FrameSinkVideoCapturerTest, DeliversUpdateRectAndCaptureCounter) {
EXPECT_CALL(frame_sink_manager_, FindCapturableFrameSink(kFrameSinkId))
.WillRepeatedly(Return(&frame_sink_));
- capturer_->ChangeTarget(kFrameSinkId);
+ capturer_->ChangeTarget(kFrameSinkId, SubtreeCaptureId());
MockConsumer consumer;
StartCapture(&consumer);
@@ -1248,7 +1249,7 @@
TEST_F(FrameSinkVideoCapturerTest, CaptureCounterSkipsWhenFramesAreDropped) {
EXPECT_CALL(frame_sink_manager_, FindCapturableFrameSink(kFrameSinkId))
.WillRepeatedly(Return(&frame_sink_));
- capturer_->ChangeTarget(kFrameSinkId);
+ capturer_->ChangeTarget(kFrameSinkId, SubtreeCaptureId());
MockConsumer consumer;
StartCapture(&consumer);
diff --git a/components/viz/service/surfaces/pending_copy_output_request.cc b/components/viz/service/surfaces/pending_copy_output_request.cc
new file mode 100644
index 0000000..a541a993
--- /dev/null
+++ b/components/viz/service/surfaces/pending_copy_output_request.cc
@@ -0,0 +1,27 @@
+// Copyright 2020 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/surfaces/pending_copy_output_request.h"
+
+#include <utility>
+
+namespace viz {
+
+PendingCopyOutputRequest::PendingCopyOutputRequest(
+ LocalSurfaceId surface_id,
+ SubtreeCaptureId subtree_id,
+ std::unique_ptr<CopyOutputRequest> request)
+ : local_surface_id(surface_id),
+ subtree_capture_id(subtree_id),
+ copy_output_request(std::move(request)) {}
+
+PendingCopyOutputRequest::PendingCopyOutputRequest(PendingCopyOutputRequest&&) =
+ default;
+
+PendingCopyOutputRequest& PendingCopyOutputRequest::operator=(
+ PendingCopyOutputRequest&&) = default;
+
+PendingCopyOutputRequest::~PendingCopyOutputRequest() = default;
+
+} // namespace viz
diff --git a/components/viz/service/surfaces/pending_copy_output_request.h b/components/viz/service/surfaces/pending_copy_output_request.h
new file mode 100644
index 0000000..4783a5d
--- /dev/null
+++ b/components/viz/service/surfaces/pending_copy_output_request.h
@@ -0,0 +1,43 @@
+// Copyright 2020 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.
+
+#ifndef COMPONENTS_VIZ_SERVICE_SURFACES_PENDING_COPY_OUTPUT_REQUEST_H_
+#define COMPONENTS_VIZ_SERVICE_SURFACES_PENDING_COPY_OUTPUT_REQUEST_H_
+
+#include <memory>
+
+#include "components/viz/common/frame_sinks/copy_output_request.h"
+#include "components/viz/common/surfaces/local_surface_id.h"
+#include "components/viz/common/surfaces/subtree_capture_id.h"
+#include "components/viz/service/viz_service_export.h"
+
+namespace viz {
+
+// Encapsulates the necessary parameters to request a copy-of-output on a
+// surface.
+struct VIZ_SERVICE_EXPORT PendingCopyOutputRequest {
+ PendingCopyOutputRequest(LocalSurfaceId surface_id,
+ SubtreeCaptureId subtree_id,
+ std::unique_ptr<CopyOutputRequest> request);
+ PendingCopyOutputRequest(PendingCopyOutputRequest&&);
+ PendingCopyOutputRequest& operator=(PendingCopyOutputRequest&&);
+ ~PendingCopyOutputRequest();
+
+ // The ID of the local surface which |copy_output_request| will be placed on
+ // its next composited frame. If this ID is default constructed, then the next
+ // surface will provide the copy-of-output regardless of its LocalSurfaceId.
+ LocalSurfaceId local_surface_id;
+
+ // If valid, the |copy_output_request| will be placed on a render pass
+ // associated with a layer subtree identified by this ID. Otherwise, it will
+ // be placed on the root render pass.
+ SubtreeCaptureId subtree_capture_id;
+
+ // The actual copy-of-output request.
+ std::unique_ptr<CopyOutputRequest> copy_output_request;
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_SURFACES_PENDING_COPY_OUTPUT_REQUEST_H_
diff --git a/components/viz/service/surfaces/surface.cc b/components/viz/service/surfaces/surface.cc
index ae5ea2e..e25a769a 100644
--- a/components/viz/service/surfaces/surface.cc
+++ b/components/viz/service/surfaces/surface.cc
@@ -15,7 +15,7 @@
#include "base/stl_util.h"
#include "base/time/tick_clock.h"
#include "base/trace_event/trace_event.h"
-#include "components/viz/common/frame_sinks/copy_output_request.h"
+#include "components/viz/common/quads/compositor_render_pass.h"
#include "components/viz/common/resources/returned_resource.h"
#include "components/viz/common/resources/transferable_resource.h"
#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
@@ -29,6 +29,26 @@
namespace viz {
+namespace {
+
+// Adds the given |request| to the requests of the given |render_pass|, removing
+// any duplicate requests made by the same source.
+void RequestCopyOfOutputOnRenderPass(std::unique_ptr<CopyOutputRequest> request,
+ CompositorRenderPass& render_pass) {
+ if (request->has_source()) {
+ const base::UnguessableToken& source = request->source();
+ // Remove existing CopyOutputRequests made on the Surface by the same
+ // source.
+ base::EraseIf(render_pass.copy_requests,
+ [&source](const std::unique_ptr<CopyOutputRequest>& x) {
+ return x->has_source() && x->source() == source;
+ });
+ }
+ render_pass.copy_requests.push_back(std::move(request));
+}
+
+} // namespace
+
Surface::PresentationHelper::PresentationHelper(
base::WeakPtr<SurfaceClient> surface_client,
uint32_t frame_token)
@@ -262,25 +282,40 @@
}
void Surface::RequestCopyOfOutput(
- std::unique_ptr<CopyOutputRequest> copy_request) {
+ PendingCopyOutputRequest pending_copy_output_request) {
TRACE_EVENT1("viz", "Surface::RequestCopyOfOutput", "has_active_frame_data",
!!active_frame_data_);
+
+ if (!pending_copy_output_request.subtree_capture_id.is_valid()) {
+ RequestCopyOfOutputOnRootRenderPass(
+ std::move(pending_copy_output_request.copy_output_request));
+ return;
+ }
+
+ if (!active_frame_data_)
+ return;
+
+ for (auto& render_pass : active_frame_data_->frame.render_pass_list) {
+ if (render_pass->subtree_capture_id ==
+ pending_copy_output_request.subtree_capture_id) {
+ RequestCopyOfOutputOnRenderPass(
+ std::move(pending_copy_output_request.copy_output_request),
+ *render_pass);
+ return;
+ }
+ }
+}
+
+void Surface::RequestCopyOfOutputOnRootRenderPass(
+ std::unique_ptr<CopyOutputRequest> copy_request) {
+ TRACE_EVENT1("viz", "Surface::RequestCopyOfOutputOnRootRenderPass",
+ "has_active_frame_data", !!active_frame_data_);
if (!active_frame_data_)
return; // |copy_request| auto-sends empty result on out-of-scope.
- std::vector<std::unique_ptr<CopyOutputRequest>>& copy_requests =
- active_frame_data_->frame.render_pass_list.back()->copy_requests;
-
- if (copy_request->has_source()) {
- const base::UnguessableToken& source = copy_request->source();
- // Remove existing CopyOutputRequests made on the Surface by the same
- // source.
- base::EraseIf(copy_requests,
- [&source](const std::unique_ptr<CopyOutputRequest>& x) {
- return x->has_source() && x->source() == source;
- });
- }
- copy_requests.push_back(std::move(copy_request));
+ RequestCopyOfOutputOnRenderPass(
+ std::move(copy_request),
+ *active_frame_data_->frame.render_pass_list.back());
}
void Surface::OnActivationDependencyResolved(
@@ -425,7 +460,7 @@
RecomputeActiveReferencedSurfaces();
for (auto& copy_request : old_copy_requests)
- RequestCopyOfOutput(std::move(copy_request));
+ RequestCopyOfOutputOnRootRenderPass(std::move(copy_request));
UnrefFrameResourcesAndRunCallbacks(std::move(previous_frame_data));
@@ -557,10 +592,10 @@
void Surface::TakeCopyOutputRequestsFromClient() {
if (!surface_client_)
return;
- for (std::unique_ptr<CopyOutputRequest>& request :
+ for (PendingCopyOutputRequest& request_params :
surface_client_->TakeCopyOutputRequests(
surface_id().local_surface_id())) {
- RequestCopyOfOutput(std::move(request));
+ RequestCopyOfOutput(std::move(request_params));
}
}
diff --git a/components/viz/service/surfaces/surface.h b/components/viz/service/surfaces/surface.h
index cdf3329..cdc8c82 100644
--- a/components/viz/service/surfaces/surface.h
+++ b/components/viz/service/surfaces/surface.h
@@ -25,6 +25,7 @@
#include "components/viz/common/quads/compositor_frame.h"
#include "components/viz/common/surfaces/frame_sink_id.h"
#include "components/viz/common/surfaces/surface_info.h"
+#include "components/viz/service/surfaces/pending_copy_output_request.h"
#include "components/viz/service/surfaces/surface_client.h"
#include "components/viz/service/surfaces/surface_dependency_deadline.h"
#include "components/viz/service/viz_service_export.h"
@@ -311,7 +312,15 @@
CompositorFrame* frame,
std::vector<ui::LatencyInfo>* latency_info);
- void RequestCopyOfOutput(std::unique_ptr<CopyOutputRequest> copy_request);
+ // Places the copy-of-output request on the render pass defined by
+ // |PendingCopyOutputRequest::subtree_capture_id| if such a render pass
+ // exists, otherwise the request will be ignored.
+ void RequestCopyOfOutput(
+ PendingCopyOutputRequest pending_copy_output_request);
+
+ // Always placed the given |copy_request| on the root render pass.
+ void RequestCopyOfOutputOnRootRenderPass(
+ std::unique_ptr<CopyOutputRequest> copy_request);
const SurfaceInfo surface_info_;
SurfaceId previous_frame_surface_id_;
diff --git a/components/viz/service/surfaces/surface_client.h b/components/viz/service/surfaces/surface_client.h
index b902e91..d85bca8 100644
--- a/components/viz/service/surfaces/surface_client.h
+++ b/components/viz/service/surfaces/surface_client.h
@@ -9,6 +9,7 @@
#include <vector>
#include "base/macros.h"
+#include "components/viz/service/surfaces/pending_copy_output_request.h"
#include "components/viz/service/viz_service_export.h"
namespace base {
@@ -24,7 +25,6 @@
namespace viz {
struct ReturnedResource;
class CompositorFrame;
-class CopyOutputRequest;
class LocalSurfaceId;
class Surface;
struct TransferableResource;
@@ -64,8 +64,8 @@
// Takes all the CopyOutputRequests made at the client level that happened for
// a LocalSurfaceId preceeding the given one.
- virtual std::vector<std::unique_ptr<CopyOutputRequest>>
- TakeCopyOutputRequests(const LocalSurfaceId& latest_surface_id) = 0;
+ virtual std::vector<PendingCopyOutputRequest> TakeCopyOutputRequests(
+ const LocalSurfaceId& latest_surface_id) = 0;
// Notifies the client that a frame with |token| has been activated.
virtual void OnFrameTokenChanged(uint32_t frame_token) = 0;
diff --git a/components/viz/service/surfaces/surface_unittest.cc b/components/viz/service/surfaces/surface_unittest.cc
index abd259a..6fce1d1 100644
--- a/components/viz/service/surfaces/surface_unittest.cc
+++ b/components/viz/service/surfaces/surface_unittest.cc
@@ -4,15 +4,17 @@
#include <utility>
-#include "components/viz/service/surfaces/surface.h"
#include "base/bind.h"
#include "base/run_loop.h"
#include "cc/test/scheduler_test_common.h"
#include "components/viz/common/frame_sinks/copy_output_result.h"
#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
+#include "components/viz/common/surfaces/subtree_capture_id.h"
#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
#include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
+#include "components/viz/service/surfaces/pending_copy_output_request.h"
+#include "components/viz/service/surfaces/surface.h"
#include "components/viz/test/begin_frame_args_test.h"
#include "components/viz/test/compositor_frame_helpers.h"
#include "components/viz/test/fake_external_begin_frame_source.h"
@@ -100,12 +102,12 @@
bool copy_called = false;
base::RunLoop copy_runloop;
- support->RequestCopyOfOutput(
- local_surface_id,
+ support->RequestCopyOfOutput(PendingCopyOutputRequest{
+ local_surface_id, SubtreeCaptureId(),
std::make_unique<CopyOutputRequest>(
CopyOutputRequest::ResultFormat::RGBA_BITMAP,
base::BindOnce(&TestCopyResultCallback, ©_called,
- copy_runloop.QuitClosure())));
+ copy_runloop.QuitClosure()))});
surface->TakeCopyOutputRequestsFromClient();
EXPECT_TRUE(surface_manager->GetSurfaceForId(surface_id));
EXPECT_FALSE(copy_called);
diff --git a/components/viz/test/stub_surface_client.cc b/components/viz/test/stub_surface_client.cc
index d880e05..bb33424 100644
--- a/components/viz/test/stub_surface_client.cc
+++ b/components/viz/test/stub_surface_client.cc
@@ -12,10 +12,9 @@
StubSurfaceClient::~StubSurfaceClient() = default;
-std::vector<std::unique_ptr<CopyOutputRequest>>
-StubSurfaceClient::TakeCopyOutputRequests(
+std::vector<PendingCopyOutputRequest> StubSurfaceClient::TakeCopyOutputRequests(
const LocalSurfaceId& latest_surface_id) {
- return std::vector<std::unique_ptr<CopyOutputRequest>>();
+ return std::vector<PendingCopyOutputRequest>();
}
} // namespace viz
diff --git a/components/viz/test/stub_surface_client.h b/components/viz/test/stub_surface_client.h
index 627a366..a6dc7df 100644
--- a/components/viz/test/stub_surface_client.h
+++ b/components/viz/test/stub_surface_client.h
@@ -27,7 +27,7 @@
const std::vector<ReturnedResource>& resources) override {}
void ReceiveFromChild(
const std::vector<TransferableResource>& resources) override {}
- std::vector<std::unique_ptr<CopyOutputRequest>> TakeCopyOutputRequests(
+ std::vector<PendingCopyOutputRequest> TakeCopyOutputRequests(
const LocalSurfaceId& latest_surface_id) override;
void OnFrameTokenChanged(uint32_t frame_token) override {}
void OnSurfaceProcessed(Surface* surface) override {}
diff --git a/content/browser/compositor/test/test_image_transport_factory.cc b/content/browser/compositor/test/test_image_transport_factory.cc
index 7650eac..4559caf 100644
--- a/content/browser/compositor/test/test_image_transport_factory.cc
+++ b/content/browser/compositor/test/test_image_transport_factory.cc
@@ -88,6 +88,10 @@
return frame_sink_id_allocator_.NextFrameSinkId();
}
+viz::SubtreeCaptureId TestImageTransportFactory::AllocateSubtreeCaptureId() {
+ return subtree_capture_id_allocator_.NextSubtreeCaptureId();
+}
+
viz::HostFrameSinkManager*
TestImageTransportFactory::GetHostFrameSinkManager() {
return &host_frame_sink_manager_;
diff --git a/content/browser/compositor/test/test_image_transport_factory.h b/content/browser/compositor/test/test_image_transport_factory.h
index 87a9f09..2b47278 100644
--- a/content/browser/compositor/test/test_image_transport_factory.h
+++ b/content/browser/compositor/test/test_image_transport_factory.h
@@ -13,6 +13,7 @@
#include "cc/test/test_task_graph_runner.h"
#include "components/viz/common/display/renderer_settings.h"
#include "components/viz/common/surfaces/frame_sink_id_allocator.h"
+#include "components/viz/common/surfaces/subtree_capture_id_allocator.h"
#include "components/viz/host/host_frame_sink_manager.h"
#include "components/viz/test/test_frame_sink_manager.h"
#include "components/viz/test/test_gpu_memory_buffer_manager.h"
@@ -44,6 +45,7 @@
gpu::GpuMemoryBufferManager* GetGpuMemoryBufferManager() override;
cc::TaskGraphRunner* GetTaskGraphRunner() override;
viz::FrameSinkId AllocateFrameSinkId() override;
+ viz::SubtreeCaptureId AllocateSubtreeCaptureId() override;
viz::HostFrameSinkManager* GetHostFrameSinkManager() override;
// ImageTransportFactory implementation.
@@ -56,6 +58,7 @@
viz::TestGpuMemoryBufferManager gpu_memory_buffer_manager_;
viz::RendererSettings renderer_settings_;
viz::FrameSinkIdAllocator frame_sink_id_allocator_;
+ viz::SubtreeCaptureIdAllocator subtree_capture_id_allocator_;
scoped_refptr<viz::ContextProvider> shared_main_context_provider_;
viz::HostFrameSinkManager host_frame_sink_manager_;
viz::TestFrameSinkManagerImpl test_frame_sink_manager_impl_;
diff --git a/content/browser/compositor/viz_process_transport_factory.cc b/content/browser/compositor/viz_process_transport_factory.cc
index 8532a82..d22a77a 100644
--- a/content/browser/compositor/viz_process_transport_factory.cc
+++ b/content/browser/compositor/viz_process_transport_factory.cc
@@ -281,6 +281,10 @@
return frame_sink_id_allocator_.NextFrameSinkId();
}
+viz::SubtreeCaptureId VizProcessTransportFactory::AllocateSubtreeCaptureId() {
+ return subtree_capture_id_allocator_.NextSubtreeCaptureId();
+}
+
viz::HostFrameSinkManager*
VizProcessTransportFactory::GetHostFrameSinkManager() {
return host_frame_sink_manager_;
diff --git a/content/browser/compositor/viz_process_transport_factory.h b/content/browser/compositor/viz_process_transport_factory.h
index 8097090c..1c34598 100644
--- a/content/browser/compositor/viz_process_transport_factory.h
+++ b/content/browser/compositor/viz_process_transport_factory.h
@@ -10,6 +10,7 @@
#include "base/macros.h"
#include "build/build_config.h"
#include "components/viz/common/surfaces/frame_sink_id_allocator.h"
+#include "components/viz/common/surfaces/subtree_capture_id_allocator.h"
#include "components/viz/service/main/viz_compositor_thread_runner_impl.h"
#include "content/browser/compositor/image_transport_factory.h"
#include "gpu/command_buffer/common/context_result.h"
@@ -70,6 +71,7 @@
gpu::GpuMemoryBufferManager* GetGpuMemoryBufferManager() override;
cc::TaskGraphRunner* GetTaskGraphRunner() override;
viz::FrameSinkId AllocateFrameSinkId() override;
+ viz::SubtreeCaptureId AllocateSubtreeCaptureId() override;
viz::HostFrameSinkManager* GetHostFrameSinkManager() override;
// ImageTransportFactory implementation.
@@ -139,6 +141,7 @@
base::flat_map<ui::Compositor*, CompositorData> compositor_data_map_;
viz::FrameSinkIdAllocator frame_sink_id_allocator_;
+ viz::SubtreeCaptureIdAllocator subtree_capture_id_allocator_;
viz::HostFrameSinkManager* const host_frame_sink_manager_;
scoped_refptr<base::SingleThreadTaskRunner> const resize_task_runner_;
diff --git a/content/browser/devtools/devtools_video_consumer.cc b/content/browser/devtools/devtools_video_consumer.cc
index 8ca2835..32095bf 100644
--- a/content/browser/devtools/devtools_video_consumer.cc
+++ b/content/browser/devtools/devtools_video_consumer.cc
@@ -8,7 +8,9 @@
#include "base/bind.h"
#include "base/memory/shared_memory_mapping.h"
+#include "base/optional.h"
#include "cc/paint/skia_paint_canvas.h"
+#include "components/viz/common/surfaces/subtree_capture_id.h"
#include "components/viz/host/host_frame_sink_manager.h"
#include "content/browser/compositor/surface_utils.h"
#include "media/base/limits.h"
@@ -84,10 +86,11 @@
const viz::FrameSinkId& frame_sink_id) {
frame_sink_id_ = frame_sink_id;
if (capturer_) {
- if (frame_sink_id_.is_valid())
- capturer_->ChangeTarget(frame_sink_id_);
- else
- capturer_->ChangeTarget(base::nullopt);
+ capturer_->ChangeTarget(
+ frame_sink_id_.is_valid()
+ ? base::make_optional<viz::FrameSinkId>(frame_sink_id_)
+ : base::nullopt,
+ viz::SubtreeCaptureId());
}
}
@@ -129,7 +132,7 @@
kDefaultUseFixedAspectRatio);
capturer_->SetFormat(pixel_format_, color_space_);
if (frame_sink_id_.is_valid())
- capturer_->ChangeTarget(frame_sink_id_);
+ capturer_->ChangeTarget(frame_sink_id_, viz::SubtreeCaptureId());
capturer_->Start(this);
}
diff --git a/content/browser/devtools/devtools_video_consumer_unittest.cc b/content/browser/devtools/devtools_video_consumer_unittest.cc
index 7d4f41d..58bd2e96 100644
--- a/content/browser/devtools/devtools_video_consumer_unittest.cc
+++ b/content/browser/devtools/devtools_video_consumer_unittest.cc
@@ -80,8 +80,8 @@
bool use_fixed_aspect_ratio));
// This is never called.
MOCK_METHOD1(SetAutoThrottlingEnabled, void(bool));
- void ChangeTarget(
- const base::Optional<viz::FrameSinkId>& frame_sink_id) final {
+ void ChangeTarget(const base::Optional<viz::FrameSinkId>& frame_sink_id,
+ const viz::SubtreeCaptureId& subtree_capture_id) final {
frame_sink_id_ = frame_sink_id ? *frame_sink_id : viz::FrameSinkId();
MockChangeTarget(frame_sink_id_);
}
diff --git a/content/browser/media/capture/frame_sink_video_capture_device.cc b/content/browser/media/capture/frame_sink_video_capture_device.cc
index 08bfe13..8b00413 100644
--- a/content/browser/media/capture/frame_sink_video_capture_device.cc
+++ b/content/browser/media/capture/frame_sink_video_capture_device.cc
@@ -17,6 +17,7 @@
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "build/build_config.h"
+#include "components/viz/common/surfaces/subtree_capture_id.h"
#include "components/viz/host/host_frame_sink_manager.h"
#include "content/browser/compositor/surface_utils.h"
#include "content/public/browser/browser_task_traits.h"
@@ -120,7 +121,7 @@
constraints.fixed_aspect_ratio);
if (target_.is_valid()) {
- capturer_->ChangeTarget(target_);
+ capturer_->ChangeTarget(target_, viz::SubtreeCaptureId());
}
#if !defined(OS_ANDROID)
@@ -300,11 +301,10 @@
target_ = frame_sink_id;
if (capturer_) {
- if (target_.is_valid()) {
- capturer_->ChangeTarget(target_);
- } else {
- capturer_->ChangeTarget(base::nullopt);
- }
+ capturer_->ChangeTarget(target_.is_valid()
+ ? base::make_optional<viz::FrameSinkId>(target_)
+ : base::nullopt,
+ viz::SubtreeCaptureId());
}
}
diff --git a/content/browser/media/capture/frame_sink_video_capture_device_unittest.cc b/content/browser/media/capture/frame_sink_video_capture_device_unittest.cc
index 7589cce..38dca34bd 100644
--- a/content/browser/media/capture/frame_sink_video_capture_device_unittest.cc
+++ b/content/browser/media/capture/frame_sink_video_capture_device_unittest.cc
@@ -101,8 +101,8 @@
const gfx::Size& max_size,
bool use_fixed_aspect_ratio));
MOCK_METHOD1(SetAutoThrottlingEnabled, void(bool));
- void ChangeTarget(
- const base::Optional<viz::FrameSinkId>& frame_sink_id) final {
+ void ChangeTarget(const base::Optional<viz::FrameSinkId>& frame_sink_id,
+ const viz::SubtreeCaptureId& subtree_capture_id) final {
DCHECK_NOT_ON_DEVICE_THREAD();
MockChangeTarget(frame_sink_id ? *frame_sink_id : viz::FrameSinkId());
}
diff --git a/content/browser/media/capture/slow_window_capturer_chromeos.cc b/content/browser/media/capture/slow_window_capturer_chromeos.cc
index c52d00d..98014b68 100644
--- a/content/browser/media/capture/slow_window_capturer_chromeos.cc
+++ b/content/browser/media/capture/slow_window_capturer_chromeos.cc
@@ -101,7 +101,8 @@
}
void SlowWindowCapturerChromeOS::ChangeTarget(
- const base::Optional<viz::FrameSinkId>& frame_sink_id) {
+ const base::Optional<viz::FrameSinkId>& frame_sink_id,
+ const viz::SubtreeCaptureId& subtree_capture_id) {
// The SlowWindowCapturerChromeOS does not capture from compositor frame
// sinks.
}
diff --git a/content/browser/media/capture/slow_window_capturer_chromeos.h b/content/browser/media/capture/slow_window_capturer_chromeos.h
index 6b402d1..3a2ec7f7 100644
--- a/content/browser/media/capture/slow_window_capturer_chromeos.h
+++ b/content/browser/media/capture/slow_window_capturer_chromeos.h
@@ -67,8 +67,8 @@
const gfx::Size& max_size,
bool use_fixed_aspect_ratio) final;
void SetAutoThrottlingEnabled(bool enabled) final;
- void ChangeTarget(
- const base::Optional<viz::FrameSinkId>& frame_sink_id) final;
+ void ChangeTarget(const base::Optional<viz::FrameSinkId>& frame_sink_id,
+ const viz::SubtreeCaptureId& subtree_capture_id) final;
void Start(
mojo::PendingRemote<viz::mojom::FrameSinkVideoConsumer> consumer) final;
void Stop() final;
diff --git a/content/browser/renderer_host/render_widget_host_view_base.cc b/content/browser/renderer_host/render_widget_host_view_base.cc
index e8c6d3e8..7a90002e 100644
--- a/content/browser/renderer_host/render_widget_host_view_base.cc
+++ b/content/browser/renderer_host/render_widget_host_view_base.cc
@@ -9,6 +9,7 @@
#include "base/unguessable_token.h"
#include "build/build_config.h"
#include "components/viz/common/features.h"
+#include "components/viz/common/surfaces/subtree_capture_id.h"
#include "components/viz/host/host_frame_sink_manager.h"
#include "content/browser/compositor/surface_utils.h"
#include "content/browser/gpu/gpu_data_manager_impl.h"
@@ -275,7 +276,7 @@
RenderWidgetHostViewBase::CreateVideoCapturer() {
std::unique_ptr<viz::ClientFrameSinkVideoCapturer> video_capturer =
GetHostFrameSinkManager()->CreateVideoCapturer();
- video_capturer->ChangeTarget(GetFrameSinkId());
+ video_capturer->ChangeTarget(GetFrameSinkId(), viz::SubtreeCaptureId());
return video_capturer;
}
diff --git a/services/viz/privileged/mojom/compositing/frame_sink_video_capture.mojom b/services/viz/privileged/mojom/compositing/frame_sink_video_capture.mojom
index 8ede898..ee3bc6c 100644
--- a/services/viz/privileged/mojom/compositing/frame_sink_video_capture.mojom
+++ b/services/viz/privileged/mojom/compositing/frame_sink_video_capture.mojom
@@ -9,6 +9,7 @@
import "mojo/public/mojom/base/time.mojom";
import "mojo/public/mojom/base/shared_memory.mojom";
import "services/viz/public/mojom/compositing/frame_sink_id.mojom";
+import "services/viz/public/mojom/compositing/subtree_capture_id.mojom";
import "skia/public/mojom/bitmap.mojom";
import "ui/gfx/geometry/mojom/geometry.mojom";
import "ui/gfx/mojom/color_space.mojom";
@@ -117,9 +118,13 @@
SetAutoThrottlingEnabled(bool enabled);
// Targets a different compositor frame sink. This may be called anytime,
- // before or after Start(). If the argument is null, capture will suspend
- // until a new frame sink target is set.
- ChangeTarget(FrameSinkId? frame_sink_id);
+ // before or after Start(). If |frame_sink_id| is null, capture will suspend
+ // until a new frame sink target is set. If the given |subtree_capture_id| is
+ // valid, the capturer will capture a render pass associated with a layer
+ // subtree under the target frame sink, which is identifiable by that
+ // |subtree_capture_id|. Otherwise, the capturer captures the root render pass
+ // of the target frame sink.
+ ChangeTarget(FrameSinkId? frame_sink_id, SubtreeCaptureId subtree_capture_id);
// Starts emitting video frames to the given |consumer|.
Start(pending_remote<FrameSinkVideoConsumer> consumer);
diff --git a/services/viz/public/cpp/compositing/compositor_render_pass_mojom_traits.cc b/services/viz/public/cpp/compositing/compositor_render_pass_mojom_traits.cc
index 4f939e7..6b428e9 100644
--- a/services/viz/public/cpp/compositing/compositor_render_pass_mojom_traits.cc
+++ b/services/viz/public/cpp/compositing/compositor_render_pass_mojom_traits.cc
@@ -8,6 +8,7 @@
#include "components/viz/common/quads/compositor_render_pass.h"
#include "services/viz/public/cpp/compositing/compositor_render_pass_id_mojom_traits.h"
#include "services/viz/public/cpp/compositing/shared_quad_state_mojom_traits.h"
+#include "services/viz/public/cpp/compositing/subtree_capture_id_mojom_traits.h"
#include "services/viz/public/cpp/crash_keys.h"
#include "ui/gfx/mojom/display_color_spaces_mojom_traits.h"
@@ -25,6 +26,7 @@
!data.ReadFilters(&(*out)->filters) ||
!data.ReadBackdropFilters(&(*out)->backdrop_filters) ||
!data.ReadBackdropFilterBounds(&(*out)->backdrop_filter_bounds) ||
+ !data.ReadSubtreeCaptureId(&(*out)->subtree_capture_id) ||
!data.ReadCopyRequests(&(*out)->copy_requests) ||
!data.ReadId(&(*out)->id)) {
return false;
diff --git a/services/viz/public/cpp/compositing/compositor_render_pass_mojom_traits.h b/services/viz/public/cpp/compositing/compositor_render_pass_mojom_traits.h
index cf79f2b..cbb5a81 100644
--- a/services/viz/public/cpp/compositing/compositor_render_pass_mojom_traits.h
+++ b/services/viz/public/cpp/compositing/compositor_render_pass_mojom_traits.h
@@ -10,6 +10,7 @@
#include "base/check.h"
#include "components/viz/common/quads/compositor_render_pass.h"
+#include "components/viz/common/surfaces/subtree_capture_id.h"
#include "services/viz/public/cpp/compositing/copy_output_request_mojom_traits.h"
#include "services/viz/public/cpp/compositing/quads_mojom_traits.h"
#include "services/viz/public/mojom/compositing/compositor_render_pass.mojom-shared.h"
@@ -58,6 +59,11 @@
return input->backdrop_filter_bounds;
}
+ static viz::SubtreeCaptureId subtree_capture_id(
+ const std::unique_ptr<viz::CompositorRenderPass>& input) {
+ return input->subtree_capture_id;
+ }
+
static bool has_transparent_background(
const std::unique_ptr<viz::CompositorRenderPass>& input) {
return input->has_transparent_background;
diff --git a/services/viz/public/cpp/compositing/mojom_traits_perftest.cc b/services/viz/public/cpp/compositing/mojom_traits_perftest.cc
index a740cc7..b7ba834 100644
--- a/services/viz/public/cpp/compositing/mojom_traits_perftest.cc
+++ b/services/viz/public/cpp/compositing/mojom_traits_perftest.cc
@@ -10,6 +10,7 @@
#include "components/viz/common/quads/solid_color_draw_quad.h"
#include "components/viz/common/quads/texture_draw_quad.h"
#include "components/viz/common/quads/tile_draw_quad.h"
+#include "components/viz/common/surfaces/subtree_capture_id.h"
#include "components/viz/test/compositor_frame_helpers.h"
#include "gpu/ipc/common/mailbox_holder_mojom_traits.h"
#include "gpu/ipc/common/mailbox_mojom_traits.h"
@@ -208,8 +209,8 @@
auto pass_in = CompositorRenderPass::Create();
pass_in->SetAll(root_id, arbitrary_rect1, arbitrary_rect2,
arbitrary_matrix1, arbitrary_filters2, arbitrary_filters1,
- arbitrary_rrectf1, arbitrary_bool1, arbitrary_bool1,
- arbitrary_bool1, arbitrary_bool1);
+ arbitrary_rrectf1, SubtreeCaptureId(), arbitrary_bool1,
+ arbitrary_bool1, arbitrary_bool1, arbitrary_bool1);
// Texture quads
for (uint32_t i = 0; i < 10; ++i) {
diff --git a/services/viz/public/cpp/compositing/mojom_traits_unittest.cc b/services/viz/public/cpp/compositing/mojom_traits_unittest.cc
index 76b3558..4393f48 100644
--- a/services/viz/public/cpp/compositing/mojom_traits_unittest.cc
+++ b/services/viz/public/cpp/compositing/mojom_traits_unittest.cc
@@ -17,6 +17,7 @@
#include "components/viz/common/resources/resource_settings.h"
#include "components/viz/common/resources/returned_resource.h"
#include "components/viz/common/resources/transferable_resource.h"
+#include "components/viz/common/surfaces/subtree_capture_id.h"
#include "components/viz/common/surfaces/surface_info.h"
#include "components/viz/common/surfaces/surface_range.h"
#include "components/viz/test/begin_frame_args_test.h"
@@ -704,6 +705,7 @@
backdrop_filters.Append(cc::FilterOperation::CreateSaturateFilter(2.f));
base::Optional<gfx::RRectF> backdrop_filter_bounds(
{10, 20, 130, 140, 1, 2, 3, 4, 5, 6, 7, 8});
+ SubtreeCaptureId subtree_capture_id{22u};
const bool has_transparent_background = true;
const bool cache_render_pass = true;
const bool has_damage_from_contributing_content = true;
@@ -711,8 +713,9 @@
auto input = CompositorRenderPass::Create();
input->SetAll(render_pass_id, output_rect, damage_rect, transform_to_root,
filters, backdrop_filters, backdrop_filter_bounds,
- has_transparent_background, cache_render_pass,
- has_damage_from_contributing_content, generate_mipmap);
+ subtree_capture_id, has_transparent_background,
+ cache_render_pass, has_damage_from_contributing_content,
+ generate_mipmap);
input->copy_requests.push_back(CopyOutputRequest::CreateStubForTesting());
const gfx::Rect copy_output_area(24, 42, 75, 57);
input->copy_requests.back()->set_area(copy_output_area);
@@ -778,6 +781,7 @@
EXPECT_EQ(filters, output->filters);
EXPECT_EQ(backdrop_filters, output->backdrop_filters);
EXPECT_EQ(backdrop_filter_bounds, output->backdrop_filter_bounds);
+ EXPECT_EQ(subtree_capture_id, output->subtree_capture_id);
EXPECT_EQ(cache_render_pass, output->cache_render_pass);
EXPECT_EQ(has_damage_from_contributing_content,
output->has_damage_from_contributing_content);
@@ -849,6 +853,7 @@
const gfx::Transform transform_to_root =
gfx::Transform(1.0, 0.5, 0.5, -0.5, -1.0, 0.0);
const base::Optional<gfx::RRectF> backdrop_filter_bounds;
+ SubtreeCaptureId subtree_capture_id;
const bool has_transparent_background = true;
const bool cache_render_pass = false;
const bool has_damage_from_contributing_content = false;
@@ -856,9 +861,9 @@
auto input = CompositorRenderPass::Create();
input->SetAll(render_pass_id, output_rect, damage_rect, transform_to_root,
cc::FilterOperations(), cc::FilterOperations(),
- backdrop_filter_bounds, has_transparent_background,
- cache_render_pass, has_damage_from_contributing_content,
- generate_mipmap);
+ backdrop_filter_bounds, subtree_capture_id,
+ has_transparent_background, cache_render_pass,
+ has_damage_from_contributing_content, generate_mipmap);
// Unlike the previous test, don't add any quads to the list; we need to
// verify that the serialization code can deal with that.
@@ -874,6 +879,8 @@
EXPECT_EQ(damage_rect, output->damage_rect);
EXPECT_EQ(transform_to_root, output->transform_to_root_target);
EXPECT_EQ(backdrop_filter_bounds, output->backdrop_filter_bounds);
+ EXPECT_EQ(subtree_capture_id, output->subtree_capture_id);
+ EXPECT_FALSE(output->subtree_capture_id.is_valid());
EXPECT_EQ(has_transparent_background, output->has_transparent_background);
}
diff --git a/services/viz/public/cpp/compositing/subtree_capture_id_mojom_traits.h b/services/viz/public/cpp/compositing/subtree_capture_id_mojom_traits.h
new file mode 100644
index 0000000..a9f44c7
--- /dev/null
+++ b/services/viz/public/cpp/compositing/subtree_capture_id_mojom_traits.h
@@ -0,0 +1,29 @@
+// Copyright 2020 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.
+
+#ifndef SERVICES_VIZ_PUBLIC_CPP_COMPOSITING_SUBTREE_CAPTURE_ID_MOJOM_TRAITS_H_
+#define SERVICES_VIZ_PUBLIC_CPP_COMPOSITING_SUBTREE_CAPTURE_ID_MOJOM_TRAITS_H_
+
+#include "components/viz/common/surfaces/subtree_capture_id.h"
+#include "services/viz/public/mojom/compositing/subtree_capture_id.mojom-shared.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits<viz::mojom::SubtreeCaptureIdDataView,
+ viz::SubtreeCaptureId> {
+ static uint32_t subtree_id(const viz::SubtreeCaptureId& subtree_capture_id) {
+ return subtree_capture_id.subtree_id();
+ }
+
+ static bool Read(viz::mojom::SubtreeCaptureIdDataView data,
+ viz::SubtreeCaptureId* out) {
+ *out = viz::SubtreeCaptureId(data.subtree_id());
+ return true;
+ }
+};
+
+} // namespace mojo
+
+#endif // SERVICES_VIZ_PUBLIC_CPP_COMPOSITING_SUBTREE_CAPTURE_ID_MOJOM_TRAITS_H_
diff --git a/services/viz/public/mojom/BUILD.gn b/services/viz/public/mojom/BUILD.gn
index 3dc726d..2d20b18 100644
--- a/services/viz/public/mojom/BUILD.gn
+++ b/services/viz/public/mojom/BUILD.gn
@@ -32,6 +32,7 @@
"compositing/returned_resource.mojom",
"compositing/selection.mojom",
"compositing/shared_quad_state.mojom",
+ "compositing/subtree_capture_id.mojom",
"compositing/surface_id.mojom",
"compositing/surface_info.mojom",
"compositing/surface_range.mojom",
@@ -109,6 +110,16 @@
{
types = [
{
+ mojom = "viz.mojom.SubtreeCaptureId"
+ cpp = "::viz::SubtreeCaptureId"
+ },
+ ]
+ traits_headers = [ "//services/viz/public/cpp/compositing/subtree_capture_id_mojom_traits.h" ]
+ traits_public_deps = [ "//components/viz/common" ]
+ },
+ {
+ types = [
+ {
mojom = "viz.mojom.SurfaceId"
cpp = "::viz::SurfaceId"
},
diff --git a/services/viz/public/mojom/compositing/compositor_render_pass.mojom b/services/viz/public/mojom/compositing/compositor_render_pass.mojom
index be3cfa7..1db67c6 100644
--- a/services/viz/public/mojom/compositing/compositor_render_pass.mojom
+++ b/services/viz/public/mojom/compositing/compositor_render_pass.mojom
@@ -8,6 +8,7 @@
import "services/viz/public/mojom/compositing/copy_output_request.mojom";
import "services/viz/public/mojom/compositing/filter_operations.mojom";
import "services/viz/public/mojom/compositing/quads.mojom";
+import "services/viz/public/mojom/compositing/subtree_capture_id.mojom";
import "ui/gfx/geometry/mojom/geometry.mojom";
import "ui/gfx/mojom/rrect_f.mojom";
import "ui/gfx/mojom/transform.mojom";
@@ -21,6 +22,7 @@
FilterOperations filters;
FilterOperations backdrop_filters;
gfx.mojom.RRectF? backdrop_filter_bounds;
+ SubtreeCaptureId subtree_capture_id;
bool has_transparent_background;
bool cache_render_pass = false;
bool has_damage_from_contributing_content = false;
diff --git a/services/viz/public/mojom/compositing/subtree_capture_id.mojom b/services/viz/public/mojom/compositing/subtree_capture_id.mojom
new file mode 100644
index 0000000..691c6080
--- /dev/null
+++ b/services/viz/public/mojom/compositing/subtree_capture_id.mojom
@@ -0,0 +1,10 @@
+// Copyright 2020 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.
+
+module viz.mojom;
+
+// See SubtreeCaptureId in components/viz/common/surfaces/subtree_capture_id.h.
+struct SubtreeCaptureId {
+ uint32 subtree_id;
+};
diff --git a/ui/aura/BUILD.gn b/ui/aura/BUILD.gn
index 94a749246..22d0ec9 100644
--- a/ui/aura/BUILD.gn
+++ b/ui/aura/BUILD.gn
@@ -37,6 +37,7 @@
"scoped_enable_unadjusted_mouse_events.h",
"scoped_keyboard_hook.h",
"scoped_simple_keyboard_hook.h",
+ "scoped_window_capture_request.h",
"scoped_window_event_targeting_blocker.h",
"scoped_window_targeter.h",
"window.h",
@@ -79,6 +80,7 @@
"null_window_targeter.cc",
"scoped_keyboard_hook.cc",
"scoped_simple_keyboard_hook.cc",
+ "scoped_window_capture_request.cc",
"scoped_window_event_targeting_blocker.cc",
"scoped_window_targeter.cc",
"window.cc",
diff --git a/ui/aura/scoped_window_capture_request.cc b/ui/aura/scoped_window_capture_request.cc
new file mode 100644
index 0000000..0d7c3f2
--- /dev/null
+++ b/ui/aura/scoped_window_capture_request.cc
@@ -0,0 +1,42 @@
+// Copyright 2020 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 "ui/aura/scoped_window_capture_request.h"
+
+#include "ui/aura/window.h"
+
+namespace aura {
+
+ScopedWindowCaptureRequest::ScopedWindowCaptureRequest(
+ ScopedWindowCaptureRequest&& other)
+ : window_(other.window_) {
+ other.window_ = nullptr;
+}
+
+ScopedWindowCaptureRequest& ScopedWindowCaptureRequest::operator=(
+ ScopedWindowCaptureRequest&& rhs) {
+ if (window_)
+ window_->OnScopedWindowCaptureRequestRemoved();
+ window_ = rhs.window_;
+ rhs.window_ = nullptr;
+ return *this;
+}
+
+ScopedWindowCaptureRequest::~ScopedWindowCaptureRequest() {
+ if (window_)
+ window_->OnScopedWindowCaptureRequestRemoved();
+}
+
+viz::SubtreeCaptureId ScopedWindowCaptureRequest::GetCaptureId() const {
+ return window_ ? window_->subtree_capture_id() : viz::SubtreeCaptureId();
+}
+
+ScopedWindowCaptureRequest::ScopedWindowCaptureRequest(Window* window)
+ : window_(window) {
+ DCHECK(window_);
+ DCHECK(!window_->IsRootWindow());
+ window_->OnScopedWindowCaptureRequestAdded();
+}
+
+} // namespace aura
diff --git a/ui/aura/scoped_window_capture_request.h b/ui/aura/scoped_window_capture_request.h
new file mode 100644
index 0000000..ee7e4f95
--- /dev/null
+++ b/ui/aura/scoped_window_capture_request.h
@@ -0,0 +1,50 @@
+// Copyright 2020 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.
+
+#ifndef UI_AURA_SCOPED_WINDOW_CAPTURE_REQUEST_H_
+#define UI_AURA_SCOPED_WINDOW_CAPTURE_REQUEST_H_
+
+#include "components/viz/common/surfaces/subtree_capture_id.h"
+#include "ui/aura/aura_export.h"
+
+namespace aura {
+
+class Window;
+
+// A scoped move-only object which is associated with a request to make a
+// non-root window individually capturable by a FrameSinkVideoCapturer. This
+// request is tracked as long as this object lives. Once all requests are
+// destroyed, the window will no longer be uniquely identifiable by a
+// viz::SubtreeCaptureId, and can no longer be individually capturable by the
+// FrameSinkVideoCapturer.
+// Note that making a window capturable forces the layer tree root at its layer
+// to be promoted to a render surface that draw into a render pass.
+// See https://crbug.com/1143930 for more details.
+class AURA_EXPORT ScopedWindowCaptureRequest {
+ public:
+ // Creates an empty request that doesn't affect any window.
+ ScopedWindowCaptureRequest() = default;
+ ScopedWindowCaptureRequest(ScopedWindowCaptureRequest&& other);
+ ScopedWindowCaptureRequest& operator=(ScopedWindowCaptureRequest&& rhs);
+ ~ScopedWindowCaptureRequest();
+
+ Window* window() const { return window_; }
+
+ viz::SubtreeCaptureId GetCaptureId() const;
+
+ private:
+ friend class Window;
+
+ // Private so it can only be called through Window::MakeWindowCapturable().
+ explicit ScopedWindowCaptureRequest(Window* window);
+
+ // The window on which this request has been made. Can be |nullptr| if this is
+ // an empty request (created by the default ctor), or if this object was
+ // std::move()'d from.
+ Window* window_ = nullptr;
+};
+
+} // namespace aura
+
+#endif // UI_AURA_SCOPED_WINDOW_CAPTURE_REQUEST_H_
diff --git a/ui/aura/window.cc b/ui/aura/window.cc
index 5fa9986..413d676 100644
--- a/ui/aura/window.cc
+++ b/ui/aura/window.cc
@@ -283,6 +283,13 @@
return visible_ ? layer()->IsDrawn() : false;
}
+ScopedWindowCaptureRequest Window::MakeWindowCapturable() {
+ DCHECK(!IsRootWindow()) << "Root windows can already be captured using their "
+ "FrameSinkId; no need to call this.";
+
+ return ScopedWindowCaptureRequest(this);
+}
+
gfx::Rect Window::GetBoundsInRootWindow() const {
// TODO(beng): There may be a better way to handle this, and the existing code
// is likely wrong anyway in a multi-display world, but this will
@@ -1413,6 +1420,18 @@
return screen_location;
}
+std::unique_ptr<ui::Layer> Window::ReleaseLayer() {
+ if (number_of_capture_requests_) {
+ // Before we release our own layer, if this window was marked for capture,
+ // we need to reset the SubtreeCaptureId on that layer as it will no longer
+ // be associated with us.
+ DCHECK(subtree_capture_id_.is_valid());
+ if (layer())
+ layer()->SetSubtreeCaptureId(viz::SubtreeCaptureId());
+ }
+ return LayerOwner::ReleaseLayer();
+}
+
std::unique_ptr<ui::Layer> Window::RecreateLayer() {
WindowOcclusionTracker::ScopedPause pause_occlusion_tracking;
@@ -1430,6 +1449,12 @@
if (GetFrameSinkId().is_valid() && old_layer)
AllocateLocalSurfaceId();
+ // The old layer subtree must no longer be capturable.
+ // Note that we don't need to worry about the newly recreated layer since
+ // Window::SetLayer() will have taken care of it already.
+ if (number_of_capture_requests_ && old_layer)
+ old_layer->SetSubtreeCaptureId(viz::SubtreeCaptureId());
+
// Observers are guaranteed to be notified when an opacity or transform
// animation ends.
if (was_animating_opacity) {
@@ -1451,6 +1476,17 @@
return old_layer;
}
+void Window::SetLayer(std::unique_ptr<ui::Layer> alayer) {
+ LayerOwner::SetLayer(std::move(alayer));
+ if (number_of_capture_requests_) {
+ // If this window was marked for capture before, then the new layer that we
+ // own now should be given the current SubtreeCaptureId that we have.
+ DCHECK(subtree_capture_id_.is_valid());
+ if (layer())
+ layer()->SetSubtreeCaptureId(subtree_capture_id_);
+ }
+}
+
void Window::OnFirstSurfaceActivation(const viz::SurfaceInfo& surface_info) {
DCHECK_EQ(surface_info.id().frame_sink_id(), GetFrameSinkId());
layer()->SetShowSurface(surface_info.id(), bounds().size(), SK_ColorWHITE,
@@ -1508,4 +1544,32 @@
return parent_local_surface_id_allocator_.get() != nullptr;
}
+void Window::OnScopedWindowCaptureRequestAdded() {
+ if (++number_of_capture_requests_ == 1) {
+ DCHECK(!subtree_capture_id_.is_valid());
+ DCHECK(!layer() || !layer()->GetSubtreeCaptureId().is_valid());
+
+ subtree_capture_id_ =
+ Env::GetInstance()->context_factory()->AllocateSubtreeCaptureId();
+ if (layer())
+ layer()->SetSubtreeCaptureId(subtree_capture_id_);
+ }
+
+ DCHECK(subtree_capture_id_.is_valid());
+}
+
+void Window::OnScopedWindowCaptureRequestRemoved() {
+ DCHECK(subtree_capture_id_.is_valid());
+ DCHECK(number_of_capture_requests_);
+
+ --number_of_capture_requests_;
+ DCHECK_GE(number_of_capture_requests_, 0);
+
+ if (number_of_capture_requests_ == 0) {
+ subtree_capture_id_ = viz::SubtreeCaptureId();
+ if (layer())
+ layer()->SetSubtreeCaptureId(subtree_capture_id_);
+ }
+}
+
} // namespace aura
diff --git a/ui/aura/window.h b/ui/aura/window.h
index 2dfa559..ca46ecf 100644
--- a/ui/aura/window.h
+++ b/ui/aura/window.h
@@ -26,9 +26,11 @@
#include "components/viz/common/surfaces/frame_sink_id.h"
#include "components/viz/common/surfaces/local_surface_id.h"
#include "components/viz/common/surfaces/scoped_surface_id_allocator.h"
+#include "components/viz/common/surfaces/subtree_capture_id.h"
#include "components/viz/host/host_frame_sink_client.h"
#include "ui/aura/aura_export.h"
#include "ui/aura/client/window_types.h"
+#include "ui/aura/scoped_window_capture_request.h"
#include "ui/aura/window_observer.h"
#include "ui/base/class_property.h"
#include "ui/compositor/layer_animator.h"
@@ -233,6 +235,28 @@
return occluded_region_in_root_;
}
+ // Makes this *non-root* window individually capturable by the
+ // |FrameSinkVideoCapturer| by tagging its layer with a unique
+ // |viz::SubtreeCaptureId| which will force the layer tree root at this
+ // window's layer to a render surface that draws into a render pass that is
+ // identifiable by the capturer using that ID.
+ //
+ // Note that this should only be called for non-root windows. Root windows are
+ // already capturable by the capturer as they're identifiable by their
+ // |viz::FrameSinkId| and thei associated root render pass, so there's no need
+ // to call this.
+ //
+ // This returns a scoped object associated with this request to make the
+ // window capturable, since multiple capturers can capture the same window at
+ // the same time. Once all requests are destroyed, this window will no longer
+ // be individually capturable, and its layer won't be tagged with a valid
+ // |viz::SubtreeCaptureId|.
+ // See https://crbug.com/1143930 for more details.
+ ScopedWindowCaptureRequest MakeWindowCapturable() WARN_UNUSED_RESULT;
+ const viz::SubtreeCaptureId& subtree_capture_id() const {
+ return subtree_capture_id_;
+ }
+
// Returns the window's bounds in root window's coordinates.
gfx::Rect GetBoundsInRootWindow() const;
@@ -400,8 +424,10 @@
float new_device_scale_factor) override;
void UpdateVisualState() override;
- // Overridden from ui::LayerOwner:
+ // ui::LayerOwner:
+ std::unique_ptr<ui::Layer> ReleaseLayer() override;
std::unique_ptr<ui::Layer> RecreateLayer() override;
+ void SetLayer(std::unique_ptr<ui::Layer> layer) override;
#if DCHECK_IS_ON()
// These methods are useful when debugging.
@@ -508,6 +534,7 @@
friend class HitTestDataProviderAura;
friend class LayoutManager;
friend class PropertyConverter;
+ friend class ScopedWindowCaptureRequest;
friend class ScopedWindowEventTargetingBlocker;
friend class WindowTargeter;
friend class test::WindowTestApi;
@@ -628,6 +655,14 @@
const viz::LocalSurfaceId& GetCurrentLocalSurfaceId() const;
bool IsEmbeddingExternalContent() const;
+ // Called by the constructor of ScopedWindowCaptureRequest to add a request to
+ // make this non-root window capturable by the FrameSinkVideoCapturer.
+ void OnScopedWindowCaptureRequestAdded();
+
+ // Called by the destructor of ScopedWindowCaptureRequest to remove a request
+ // to make this non-root window capturable by the FrameSinkVideoCapturer.
+ void OnScopedWindowCaptureRequestRemoved();
+
// Bounds of this window relative to the parent. This is cached as the bounds
// of the Layer and Window are not necessarily the same. In particular bounds
// of the Layer are relative to the first ancestor with a Layer, where as this
@@ -688,6 +723,22 @@
base::ReentrantObserverList<WindowObserver, true> observers_;
+ // Video capturing support ---------------------------------------------------
+
+ // A non-root window must be marked with a viz::SubtreeCaptureId so that it
+ // can be captured by a FrameSinkVideoCapturer. Multiple clients can request
+ // to capture the same window at the same time. This is the number of those
+ // requests, which once it goes to zero, we well clear the
+ // viz::SubtreeCaptureId from the layer associated with this window.
+ int number_of_capture_requests_ = 0;
+
+ // The ID allocated for the layer tree rooted at this window's layer, so that
+ // it can be uniquely identified by the FrameSinkVideoCapturer. This can only
+ // be set for non-root windows. Root windows can be captured normally by the
+ // capturer using their frame sink ID, since those root windows are already
+ // associated with a root compositor render pass.
+ viz::SubtreeCaptureId subtree_capture_id_;
+
// Embedding support ---------------------------------------------------------
// Used to detect changes in device scale factor that require generating a
diff --git a/ui/aura/window_unittest.cc b/ui/aura/window_unittest.cc
index 6bf4c71..a30c5ee1 100644
--- a/ui/aura/window_unittest.cc
+++ b/ui/aura/window_unittest.cc
@@ -6,6 +6,7 @@
#include <limits.h>
+#include <memory>
#include <string>
#include <utility>
#include <vector>
@@ -24,6 +25,7 @@
#include "ui/aura/client/visibility_client.h"
#include "ui/aura/client/window_parenting_client.h"
#include "ui/aura/layout_manager.h"
+#include "ui/aura/scoped_window_capture_request.h"
#include "ui/aura/scoped_window_event_targeting_blocker.h"
#include "ui/aura/test/aura_test_base.h"
#include "ui/aura/test/aura_test_utils.h"
@@ -368,6 +370,92 @@
EXPECT_FALSE(w->ContainsPoint(gfx::Point(10, 10)));
}
+TEST_F(WindowTest, MakeWindowCapturable) {
+ std::unique_ptr<Window> w1(CreateTestWindowWithId(1, root_window()));
+ // Initailly the window is not capturable.
+ EXPECT_FALSE(w1->subtree_capture_id().is_valid());
+
+ // Creating requests makes the window capturable as long as those requests
+ // remain alive.
+ ScopedWindowCaptureRequest request1 = w1->MakeWindowCapturable();
+ EXPECT_TRUE(w1->subtree_capture_id().is_valid());
+ EXPECT_EQ(request1.GetCaptureId(), w1->subtree_capture_id());
+ EXPECT_EQ(request1.GetCaptureId(), w1->layer()->GetSubtreeCaptureId());
+
+ // A new request does not affect the subtree capture ID.
+ const viz::SubtreeCaptureId current_id = w1->subtree_capture_id();
+ ScopedWindowCaptureRequest request2 = w1->MakeWindowCapturable();
+ EXPECT_EQ(current_id, w1->subtree_capture_id());
+ EXPECT_EQ(request1.GetCaptureId(), request2.GetCaptureId());
+
+ // Create a new request, then move an existing request into it. This should
+ // invalidate the moved-from request.
+ ScopedWindowCaptureRequest request3 = w1->MakeWindowCapturable();
+ EXPECT_EQ(current_id, request3.GetCaptureId());
+ request3 = std::move(request2);
+ EXPECT_FALSE(request2.window());
+ EXPECT_FALSE(request2.GetCaptureId().is_valid());
+ EXPECT_TRUE(w1->subtree_capture_id().is_valid());
+
+ // Destroying |request2| does nothing.
+ auto consume_request = [](ScopedWindowCaptureRequest request) {};
+ consume_request(std::move(request2));
+ EXPECT_TRUE(w1->subtree_capture_id().is_valid());
+ EXPECT_EQ(current_id, w1->subtree_capture_id());
+
+ // Destroying |request1| won't affect the window, it will remain capturable,
+ // since |request3| is still alive.
+ consume_request(std::move(request1));
+ EXPECT_FALSE(request1.window());
+ EXPECT_FALSE(request1.GetCaptureId().is_valid());
+ EXPECT_TRUE(w1->subtree_capture_id().is_valid());
+ EXPECT_EQ(current_id, w1->subtree_capture_id());
+
+ // Once all requests are destroyed, the window no longer has a valid capture
+ // ID.
+ consume_request(std::move(request3));
+ EXPECT_FALSE(w1->subtree_capture_id().is_valid());
+ EXPECT_FALSE(w1->layer()->GetSubtreeCaptureId().is_valid());
+}
+
+TEST_F(WindowTest, LayerReleasingAndSettingOfCapturableWindow) {
+ std::unique_ptr<Window> w1(CreateTestWindowWithId(1, root_window()));
+ EXPECT_FALSE(w1->subtree_capture_id().is_valid());
+ ScopedWindowCaptureRequest request = w1->MakeWindowCapturable();
+ EXPECT_TRUE(w1->layer()->GetSubtreeCaptureId().is_valid());
+
+ // Releasing the capturable window's layer (i.e. it's no longer associated
+ // with the window) will clear its capture ID. However, the window remains
+ // marked as capturable with a valid SubtreeCaptureId even though it has no
+ // layer.
+ std::unique_ptr<ui::Layer> taken_layer = w1->ReleaseLayer();
+ EXPECT_FALSE(w1->layer());
+ EXPECT_FALSE(taken_layer->GetSubtreeCaptureId().is_valid());
+ EXPECT_TRUE(w1->subtree_capture_id().is_valid());
+
+ // Setting a new layer on the window will set the layer's capture ID.
+ auto new_layer = std::make_unique<ui::Layer>();
+ taken_layer->parent()->Add(new_layer.get());
+ w1->Reset(std::move(new_layer));
+ EXPECT_TRUE(w1->layer()->GetSubtreeCaptureId().is_valid());
+ EXPECT_EQ(request.GetCaptureId(), w1->layer()->GetSubtreeCaptureId());
+}
+
+TEST_F(WindowTest, RecreateLayerOfCapturableWindow) {
+ std::unique_ptr<Window> w1(CreateTestWindowWithId(1, root_window()));
+ EXPECT_FALSE(w1->subtree_capture_id().is_valid());
+ ScopedWindowCaptureRequest request = w1->MakeWindowCapturable();
+ EXPECT_TRUE(w1->layer()->GetSubtreeCaptureId().is_valid());
+
+ // Recreating the layer of a capturable window will preserve the capture ID
+ // on the newly recreated window, and clears it from the old layer.
+ const viz::SubtreeCaptureId current_id = w1->subtree_capture_id();
+ std::unique_ptr<ui::Layer> old_layer = w1->RecreateLayer();
+ EXPECT_FALSE(old_layer->GetSubtreeCaptureId().is_valid());
+ EXPECT_EQ(current_id, w1->subtree_capture_id());
+ EXPECT_EQ(current_id, w1->layer()->GetSubtreeCaptureId());
+}
+
TEST_F(WindowTest, ConvertPointToWindow) {
// Window::ConvertPointToWindow is mostly identical to
// Layer::ConvertPointToLayer, except NULL values for |source| are permitted,
diff --git a/ui/compositor/compositor.h b/ui/compositor/compositor.h
index d87ee0d..e1db8f7 100644
--- a/ui/compositor/compositor.h
+++ b/ui/compositor/compositor.h
@@ -26,6 +26,7 @@
#include "cc/trees/layer_tree_host_single_thread_client.h"
#include "components/viz/common/frame_sinks/begin_frame_args.h"
#include "components/viz/common/surfaces/frame_sink_id.h"
+#include "components/viz/common/surfaces/subtree_capture_id.h"
#include "components/viz/host/host_frame_sink_client.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "services/viz/privileged/mojom/compositing/vsync_parameter_observer.mojom-forward.h"
@@ -125,6 +126,9 @@
// Allocate a new client ID for the display compositor.
virtual viz::FrameSinkId AllocateFrameSinkId() = 0;
+ // Allocates a new capture ID for a layer subtree within a frame sink.
+ virtual viz::SubtreeCaptureId AllocateSubtreeCaptureId() = 0;
+
// Gets the frame sink manager host instance.
virtual viz::HostFrameSinkManager* GetHostFrameSinkManager() = 0;
};
diff --git a/ui/compositor/layer.cc b/ui/compositor/layer.cc
index 5f5eecd..fb7c472 100644
--- a/ui/compositor/layer.cc
+++ b/ui/compositor/layer.cc
@@ -459,6 +459,14 @@
return animator_.get();
}
+void Layer::SetSubtreeCaptureId(viz::SubtreeCaptureId subtree_id) {
+ cc_layer_->SetSubtreeCaptureId(subtree_id);
+}
+
+viz::SubtreeCaptureId Layer::GetSubtreeCaptureId() const {
+ return cc_layer_->subtree_capture_id();
+}
+
void Layer::SetTransform(const gfx::Transform& transform) {
GetAnimator()->SetTransform(transform);
}
@@ -781,6 +789,7 @@
new_layer->SetBackgroundColor(cc_layer_->background_color());
new_layer->SetSafeOpaqueBackgroundColor(
cc_layer_->SafeOpaqueBackgroundColor());
+ new_layer->SetSubtreeCaptureId(cc_layer_->subtree_capture_id());
new_layer->SetCacheRenderSurface(cc_layer_->cache_render_surface());
new_layer->SetTrilinearFiltering(cc_layer_->trilinear_filtering());
new_layer->SetRoundedCorner(cc_layer_->corner_radii());
diff --git a/ui/compositor/layer.h b/ui/compositor/layer.h
index f2a863d..e82ad7e 100644
--- a/ui/compositor/layer.h
+++ b/ui/compositor/layer.h
@@ -23,6 +23,7 @@
#include "cc/layers/surface_layer.h"
#include "cc/layers/texture_layer_client.h"
#include "components/viz/common/resources/transferable_resource.h"
+#include "components/viz/common/surfaces/subtree_capture_id.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/compositor/compositor.h"
#include "ui/compositor/layer_animation_delegate.h"
@@ -169,6 +170,23 @@
// been set. Will not return NULL.
LayerAnimator* GetAnimator();
+ // Sets the given |subtree_id| on the cc::Layer associated with this, so that
+ // the layer subtree rooted here can be uniquely identified by a
+ // FrameSinkVideoCapturer. The existence of a valid SubtreeCaptureId on this
+ // layer will force it to be drawn into a separate CompositorRenderPass.
+ // Setting a non-valid (i.e. default-constructed SubtreeCaptureId) will clear
+ // this property.
+ // It is not allowed to change this ID from a valid ID to another valid ID,
+ // since a client might already using the existing valid ID to make this layer
+ // subtree identifiable by a capturer.
+ //
+ // Note that this is useful when it's desired to video record a layer subtree
+ // of a non-root layer using a FrameSinkVideoCapturer, since non-root layers
+ // are usually not drawn into their own CompositorRenderPass, while the ui
+ // compositor's root layer always is.
+ void SetSubtreeCaptureId(viz::SubtreeCaptureId subtree_id);
+ viz::SubtreeCaptureId GetSubtreeCaptureId() const;
+
// The transform, relative to the parent.
void SetTransform(const gfx::Transform& transform);
const gfx::Transform& transform() const { return cc_layer_->transform(); }
diff --git a/ui/compositor/layer_owner.h b/ui/compositor/layer_owner.h
index c541ca9..f3ea42e7 100644
--- a/ui/compositor/layer_owner.h
+++ b/ui/compositor/layer_owner.h
@@ -44,7 +44,7 @@
std::unique_ptr<Layer> AcquireLayer();
// Similar to AcquireLayer(), but layer() will be set to nullptr immediately.
- std::unique_ptr<Layer> ReleaseLayer();
+ virtual std::unique_ptr<Layer> ReleaseLayer();
// Releases the ownership of the current layer, and takes ownership of
// |layer|.
@@ -63,7 +63,7 @@
bool OwnsLayer() const;
protected:
- void SetLayer(std::unique_ptr<Layer> layer);
+ virtual void SetLayer(std::unique_ptr<Layer> layer);
void DestroyLayer();
private:
diff --git a/ui/compositor/layer_unittest.cc b/ui/compositor/layer_unittest.cc
index b4db7e4..9806e412 100644
--- a/ui/compositor/layer_unittest.cc
+++ b/ui/compositor/layer_unittest.cc
@@ -39,6 +39,7 @@
#include "components/viz/common/frame_sinks/copy_output_result.h"
#include "components/viz/common/resources/transferable_resource.h"
#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
+#include "components/viz/common/surfaces/subtree_capture_id.h"
#include "components/viz/common/surfaces/surface_id.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -755,6 +756,7 @@
layer->SetClipRect(clip_rect);
layer->SetRoundedCornerRadius({1, 2, 4, 5});
layer->SetIsFastRoundedCorner(true);
+ layer->SetSubtreeCaptureId(viz::SubtreeCaptureId(1));
auto clone = layer->Clone();
@@ -773,6 +775,10 @@
EXPECT_EQ(layer->rounded_corner_radii(), clone->rounded_corner_radii());
EXPECT_EQ(layer->is_fast_rounded_corner(), clone->is_fast_rounded_corner());
+ // However, the SubtreeCaptureId is not cloned.
+ EXPECT_TRUE(layer->GetSubtreeCaptureId().is_valid());
+ EXPECT_FALSE(clone->GetSubtreeCaptureId().is_valid());
+
layer->SetTransform(gfx::Transform());
layer->SetColor(SK_ColorGREEN);
layer->SetLayerInverted(false);
@@ -1018,6 +1024,8 @@
constexpr gfx::RoundedCornersF kCornerRadii(1, 2, 3, 4);
l1->SetRoundedCornerRadius(kCornerRadii);
l1->SetIsFastRoundedCorner(true);
+ constexpr viz::SubtreeCaptureId kSubtreeCaptureId(22);
+ l1->SetSubtreeCaptureId(kSubtreeCaptureId);
EXPECT_EQ(gfx::Point3F(), l1->cc_layer_for_testing()->transform_origin());
EXPECT_TRUE(l1->cc_layer_for_testing()->DrawsContent());
@@ -1027,6 +1035,9 @@
EXPECT_TRUE(l1->cc_layer_for_testing()->HasRoundedCorner());
EXPECT_EQ(l1->cc_layer_for_testing()->corner_radii(), kCornerRadii);
EXPECT_TRUE(l1->cc_layer_for_testing()->is_fast_rounded_corner());
+ EXPECT_EQ(kSubtreeCaptureId,
+ l1->cc_layer_for_testing()->subtree_capture_id());
+ EXPECT_EQ(kSubtreeCaptureId, l1->GetSubtreeCaptureId());
cc::Layer* before_layer = l1->cc_layer_for_testing();
@@ -1050,6 +1061,9 @@
EXPECT_TRUE(l1->cc_layer_for_testing()->HasRoundedCorner());
EXPECT_EQ(l1->cc_layer_for_testing()->corner_radii(), kCornerRadii);
EXPECT_TRUE(l1->cc_layer_for_testing()->is_fast_rounded_corner());
+ EXPECT_EQ(kSubtreeCaptureId,
+ l1->cc_layer_for_testing()->subtree_capture_id());
+ EXPECT_EQ(kSubtreeCaptureId, l1->GetSubtreeCaptureId());
EXPECT_FALSE(callback1_run);
bool callback2_run = false;
diff --git a/ui/compositor/test/in_process_context_factory.cc b/ui/compositor/test/in_process_context_factory.cc
index 033f94d8..ea06f14 100644
--- a/ui/compositor/test/in_process_context_factory.cc
+++ b/ui/compositor/test/in_process_context_factory.cc
@@ -437,6 +437,10 @@
return frame_sink_id_allocator_.NextFrameSinkId();
}
+viz::SubtreeCaptureId InProcessContextFactory::AllocateSubtreeCaptureId() {
+ return subtree_capture_id_allocator_.NextSubtreeCaptureId();
+}
+
viz::HostFrameSinkManager* InProcessContextFactory::GetHostFrameSinkManager() {
return host_frame_sink_manager_;
}
diff --git a/ui/compositor/test/in_process_context_factory.h b/ui/compositor/test/in_process_context_factory.h
index 1e8e1c2e..98de309 100644
--- a/ui/compositor/test/in_process_context_factory.h
+++ b/ui/compositor/test/in_process_context_factory.h
@@ -12,6 +12,7 @@
#include "base/macros.h"
#include "cc/test/test_task_graph_runner.h"
#include "components/viz/common/surfaces/frame_sink_id_allocator.h"
+#include "components/viz/common/surfaces/subtree_capture_id_allocator.h"
#include "components/viz/service/display/display.h"
#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
#include "components/viz/test/test_gpu_memory_buffer_manager.h"
@@ -69,6 +70,7 @@
gpu::GpuMemoryBufferManager* GetGpuMemoryBufferManager() override;
cc::TaskGraphRunner* GetTaskGraphRunner() override;
viz::FrameSinkId AllocateFrameSinkId() override;
+ viz::SubtreeCaptureId AllocateSubtreeCaptureId() override;
viz::HostFrameSinkManager* GetHostFrameSinkManager() override;
SkMatrix44 GetOutputColorMatrix(Compositor* compositor) const;
@@ -90,6 +92,7 @@
viz::TestImageFactory image_factory_;
cc::TestTaskGraphRunner task_graph_runner_;
viz::FrameSinkIdAllocator frame_sink_id_allocator_;
+ viz::SubtreeCaptureIdAllocator subtree_capture_id_allocator_;
bool use_test_surface_;
bool disable_vsync_ = false;
double refresh_rate_ = 60.0;