Mozart: Generalize frame scheduling.
Renamed SceneScheduler to FrameScheduler.
Added a SceneScheduler to the Renderer so that clients of the renderer
(like the view manager) can schedule frames more globally without
having to publish a scene.
Added some basic integration tests for the scheduling API.
BUG=
R=mikejurka@google.com
Review URL: https://codereview.chromium.org/1997513002 .
diff --git a/mojo/dart/packages/mojo_services/lib/mojo/gfx/composition/renderers.mojom.dart b/mojo/dart/packages/mojo_services/lib/mojo/gfx/composition/renderers.mojom.dart
index 37944ab..0b3eb61 100644
--- a/mojo/dart/packages/mojo_services/lib/mojo/gfx/composition/renderers.mojom.dart
+++ b/mojo/dart/packages/mojo_services/lib/mojo/gfx/composition/renderers.mojom.dart
@@ -10,6 +10,7 @@
import 'package:mojo_services/mojo/geometry.mojom.dart' as geometry_mojom;
import 'package:mojo_services/mojo/gfx/composition/hit_tests.mojom.dart' as hit_tests_mojom;
import 'package:mojo_services/mojo/gfx/composition/scene_token.mojom.dart' as scene_token_mojom;
+import 'package:mojo_services/mojo/gfx/composition/scheduling.mojom.dart' as scheduling_mojom;
@@ -173,6 +174,77 @@
}
+class _RendererGetSchedulerParams extends bindings.Struct {
+ static const List<bindings.StructDataHeader> kVersions = const [
+ const bindings.StructDataHeader(16, 0)
+ ];
+ Object scheduler = null;
+
+ _RendererGetSchedulerParams() : super(kVersions.last.size);
+
+ static _RendererGetSchedulerParams deserialize(bindings.Message message) {
+ var decoder = new bindings.Decoder(message);
+ var result = decode(decoder);
+ if (decoder.excessHandles != null) {
+ decoder.excessHandles.forEach((h) => h.close());
+ }
+ return result;
+ }
+
+ static _RendererGetSchedulerParams decode(bindings.Decoder decoder0) {
+ if (decoder0 == null) {
+ return null;
+ }
+ _RendererGetSchedulerParams result = new _RendererGetSchedulerParams();
+
+ var mainDataHeader = decoder0.decodeStructDataHeader();
+ if (mainDataHeader.version <= kVersions.last.version) {
+ // Scan in reverse order to optimize for more recent versions.
+ for (int i = kVersions.length - 1; i >= 0; --i) {
+ if (mainDataHeader.version >= kVersions[i].version) {
+ if (mainDataHeader.size == kVersions[i].size) {
+ // Found a match.
+ break;
+ }
+ throw new bindings.MojoCodecError(
+ 'Header size doesn\'t correspond to known version size.');
+ }
+ }
+ } else if (mainDataHeader.size < kVersions.last.size) {
+ throw new bindings.MojoCodecError(
+ 'Message newer than the last known version cannot be shorter than '
+ 'required by the last known version.');
+ }
+ if (mainDataHeader.version >= 0) {
+
+ result.scheduler = decoder0.decodeInterfaceRequest(8, false, scheduling_mojom.FrameSchedulerStub.newFromEndpoint);
+ }
+ return result;
+ }
+
+ void encode(bindings.Encoder encoder) {
+ var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+ try {
+ encoder0.encodeInterfaceRequest(scheduler, 8, false);
+ } on bindings.MojoCodecError catch(e) {
+ e.message = "Error encountered while encoding field "
+ "scheduler of struct _RendererGetSchedulerParams: $e";
+ rethrow;
+ }
+ }
+
+ String toString() {
+ return "_RendererGetSchedulerParams("
+ "scheduler: $scheduler" ")";
+ }
+
+ Map toJson() {
+ throw new bindings.MojoCodecError(
+ 'Object containing handles cannot be encoded to JSON.');
+ }
+}
+
+
class _RendererGetHitTesterParams extends bindings.Struct {
static const List<bindings.StructDataHeader> kVersions = const [
const bindings.StructDataHeader(16, 0)
@@ -245,7 +317,8 @@
const int _rendererMethodSetRootSceneName = 0;
const int _rendererMethodClearRootSceneName = 1;
-const int _rendererMethodGetHitTesterName = 2;
+const int _rendererMethodGetSchedulerName = 2;
+const int _rendererMethodGetHitTesterName = 3;
class _RendererServiceDescription implements service_describer.ServiceDescription {
dynamic getTopLevelInterface([Function responseFactory]) =>
@@ -262,6 +335,7 @@
static const String serviceName = null;
void setRootScene(scene_token_mojom.SceneToken sceneToken, int sceneVersion, geometry_mojom.Rect viewport);
void clearRootScene();
+ void getScheduler(Object scheduler);
void getHitTester(Object hitTester);
}
@@ -345,6 +419,16 @@
ctrl.sendMessage(params,
_rendererMethodClearRootSceneName);
}
+ void getScheduler(Object scheduler) {
+ if (!ctrl.isBound) {
+ ctrl.proxyError("The Proxy is closed.");
+ return;
+ }
+ var params = new _RendererGetSchedulerParams();
+ params.scheduler = scheduler;
+ ctrl.sendMessage(params,
+ _rendererMethodGetSchedulerName);
+ }
void getHitTester(Object hitTester) {
if (!ctrl.isBound) {
ctrl.proxyError("The Proxy is closed.");
@@ -396,6 +480,11 @@
case _rendererMethodClearRootSceneName:
_impl.clearRootScene();
break;
+ case _rendererMethodGetSchedulerName:
+ var params = _RendererGetSchedulerParams.deserialize(
+ message.payload);
+ _impl.getScheduler(params.scheduler);
+ break;
case _rendererMethodGetHitTesterName:
var params = _RendererGetHitTesterParams.deserialize(
message.payload);
@@ -474,6 +563,9 @@
void clearRootScene() {
return impl.clearRootScene();
}
+ void getScheduler(Object scheduler) {
+ return impl.getScheduler(scheduler);
+ }
void getHitTester(Object hitTester) {
return impl.getHitTester(hitTester);
}
diff --git a/mojo/dart/packages/mojo_services/lib/mojo/gfx/composition/scenes.mojom.dart b/mojo/dart/packages/mojo_services/lib/mojo/gfx/composition/scenes.mojom.dart
index ea32f42..72e3cec 100644
--- a/mojo/dart/packages/mojo_services/lib/mojo/gfx/composition/scenes.mojom.dart
+++ b/mojo/dart/packages/mojo_services/lib/mojo/gfx/composition/scenes.mojom.dart
@@ -549,7 +549,7 @@
}
if (mainDataHeader.version >= 0) {
- result.scheduler = decoder0.decodeInterfaceRequest(8, false, scheduling_mojom.SceneSchedulerStub.newFromEndpoint);
+ result.scheduler = decoder0.decodeInterfaceRequest(8, false, scheduling_mojom.FrameSchedulerStub.newFromEndpoint);
}
return result;
}
diff --git a/mojo/dart/packages/mojo_services/lib/mojo/gfx/composition/scheduling.mojom.dart b/mojo/dart/packages/mojo_services/lib/mojo/gfx/composition/scheduling.mojom.dart
index b6d5908..b3128ce 100644
--- a/mojo/dart/packages/mojo_services/lib/mojo/gfx/composition/scheduling.mojom.dart
+++ b/mojo/dart/packages/mojo_services/lib/mojo/gfx/composition/scheduling.mojom.dart
@@ -124,14 +124,14 @@
}
-class _SceneSchedulerScheduleFrameParams extends bindings.Struct {
+class _FrameSchedulerScheduleFrameParams extends bindings.Struct {
static const List<bindings.StructDataHeader> kVersions = const [
const bindings.StructDataHeader(8, 0)
];
- _SceneSchedulerScheduleFrameParams() : super(kVersions.last.size);
+ _FrameSchedulerScheduleFrameParams() : super(kVersions.last.size);
- static _SceneSchedulerScheduleFrameParams deserialize(bindings.Message message) {
+ static _FrameSchedulerScheduleFrameParams deserialize(bindings.Message message) {
var decoder = new bindings.Decoder(message);
var result = decode(decoder);
if (decoder.excessHandles != null) {
@@ -140,11 +140,11 @@
return result;
}
- static _SceneSchedulerScheduleFrameParams decode(bindings.Decoder decoder0) {
+ static _FrameSchedulerScheduleFrameParams decode(bindings.Decoder decoder0) {
if (decoder0 == null) {
return null;
}
- _SceneSchedulerScheduleFrameParams result = new _SceneSchedulerScheduleFrameParams();
+ _FrameSchedulerScheduleFrameParams result = new _FrameSchedulerScheduleFrameParams();
var mainDataHeader = decoder0.decodeStructDataHeader();
if (mainDataHeader.version <= kVersions.last.version) {
@@ -172,7 +172,7 @@
}
String toString() {
- return "_SceneSchedulerScheduleFrameParams("")";
+ return "_FrameSchedulerScheduleFrameParams("")";
}
Map toJson() {
@@ -182,15 +182,15 @@
}
-class SceneSchedulerScheduleFrameResponseParams extends bindings.Struct {
+class FrameSchedulerScheduleFrameResponseParams extends bindings.Struct {
static const List<bindings.StructDataHeader> kVersions = const [
const bindings.StructDataHeader(16, 0)
];
FrameInfo frameInfo = null;
- SceneSchedulerScheduleFrameResponseParams() : super(kVersions.last.size);
+ FrameSchedulerScheduleFrameResponseParams() : super(kVersions.last.size);
- static SceneSchedulerScheduleFrameResponseParams deserialize(bindings.Message message) {
+ static FrameSchedulerScheduleFrameResponseParams deserialize(bindings.Message message) {
var decoder = new bindings.Decoder(message);
var result = decode(decoder);
if (decoder.excessHandles != null) {
@@ -199,11 +199,11 @@
return result;
}
- static SceneSchedulerScheduleFrameResponseParams decode(bindings.Decoder decoder0) {
+ static FrameSchedulerScheduleFrameResponseParams decode(bindings.Decoder decoder0) {
if (decoder0 == null) {
return null;
}
- SceneSchedulerScheduleFrameResponseParams result = new SceneSchedulerScheduleFrameResponseParams();
+ FrameSchedulerScheduleFrameResponseParams result = new FrameSchedulerScheduleFrameResponseParams();
var mainDataHeader = decoder0.decodeStructDataHeader();
if (mainDataHeader.version <= kVersions.last.version) {
@@ -237,13 +237,13 @@
encoder0.encodeStruct(frameInfo, 8, false);
} on bindings.MojoCodecError catch(e) {
e.message = "Error encountered while encoding field "
- "frameInfo of struct SceneSchedulerScheduleFrameResponseParams: $e";
+ "frameInfo of struct FrameSchedulerScheduleFrameResponseParams: $e";
rethrow;
}
}
String toString() {
- return "SceneSchedulerScheduleFrameResponseParams("
+ return "FrameSchedulerScheduleFrameResponseParams("
"frameInfo: $frameInfo" ")";
}
@@ -254,9 +254,9 @@
}
}
-const int _sceneSchedulerMethodScheduleFrameName = 0;
+const int _frameSchedulerMethodScheduleFrameName = 0;
-class _SceneSchedulerServiceDescription implements service_describer.ServiceDescription {
+class _FrameSchedulerServiceDescription implements service_describer.ServiceDescription {
dynamic getTopLevelInterface([Function responseFactory]) =>
responseFactory(null);
@@ -267,31 +267,31 @@
responseFactory(null);
}
-abstract class SceneScheduler {
+abstract class FrameScheduler {
static const String serviceName = null;
dynamic scheduleFrame([Function responseFactory = null]);
}
-class _SceneSchedulerProxyControl
+class _FrameSchedulerProxyControl
extends bindings.ProxyMessageHandler
implements bindings.ProxyControl {
- _SceneSchedulerProxyControl.fromEndpoint(
+ _FrameSchedulerProxyControl.fromEndpoint(
core.MojoMessagePipeEndpoint endpoint) : super.fromEndpoint(endpoint);
- _SceneSchedulerProxyControl.fromHandle(
+ _FrameSchedulerProxyControl.fromHandle(
core.MojoHandle handle) : super.fromHandle(handle);
- _SceneSchedulerProxyControl.unbound() : super.unbound();
+ _FrameSchedulerProxyControl.unbound() : super.unbound();
service_describer.ServiceDescription get serviceDescription =>
- new _SceneSchedulerServiceDescription();
+ new _FrameSchedulerServiceDescription();
- String get serviceName => SceneScheduler.serviceName;
+ String get serviceName => FrameScheduler.serviceName;
void handleResponse(bindings.ServiceMessage message) {
switch (message.header.type) {
- case _sceneSchedulerMethodScheduleFrameName:
- var r = SceneSchedulerScheduleFrameResponseParams.deserialize(
+ case _frameSchedulerMethodScheduleFrameName:
+ var r = FrameSchedulerScheduleFrameResponseParams.deserialize(
message.payload);
if (!message.header.hasRequestId) {
proxyError("Expected a message with a valid request Id.");
@@ -320,69 +320,69 @@
@override
String toString() {
var superString = super.toString();
- return "_SceneSchedulerProxyControl($superString)";
+ return "_FrameSchedulerProxyControl($superString)";
}
}
-class SceneSchedulerProxy
+class FrameSchedulerProxy
extends bindings.Proxy
- implements SceneScheduler {
- SceneSchedulerProxy.fromEndpoint(
+ implements FrameScheduler {
+ FrameSchedulerProxy.fromEndpoint(
core.MojoMessagePipeEndpoint endpoint)
- : super(new _SceneSchedulerProxyControl.fromEndpoint(endpoint));
+ : super(new _FrameSchedulerProxyControl.fromEndpoint(endpoint));
- SceneSchedulerProxy.fromHandle(core.MojoHandle handle)
- : super(new _SceneSchedulerProxyControl.fromHandle(handle));
+ FrameSchedulerProxy.fromHandle(core.MojoHandle handle)
+ : super(new _FrameSchedulerProxyControl.fromHandle(handle));
- SceneSchedulerProxy.unbound()
- : super(new _SceneSchedulerProxyControl.unbound());
+ FrameSchedulerProxy.unbound()
+ : super(new _FrameSchedulerProxyControl.unbound());
- static SceneSchedulerProxy newFromEndpoint(
+ static FrameSchedulerProxy newFromEndpoint(
core.MojoMessagePipeEndpoint endpoint) {
- assert(endpoint.setDescription("For SceneSchedulerProxy"));
- return new SceneSchedulerProxy.fromEndpoint(endpoint);
+ assert(endpoint.setDescription("For FrameSchedulerProxy"));
+ return new FrameSchedulerProxy.fromEndpoint(endpoint);
}
- factory SceneSchedulerProxy.connectToService(
+ factory FrameSchedulerProxy.connectToService(
bindings.ServiceConnector s, String url, [String serviceName]) {
- SceneSchedulerProxy p = new SceneSchedulerProxy.unbound();
+ FrameSchedulerProxy p = new FrameSchedulerProxy.unbound();
s.connectToService(url, p, serviceName);
return p;
}
dynamic scheduleFrame([Function responseFactory = null]) {
- var params = new _SceneSchedulerScheduleFrameParams();
+ var params = new _FrameSchedulerScheduleFrameParams();
return ctrl.sendMessageWithRequestId(
params,
- _sceneSchedulerMethodScheduleFrameName,
+ _frameSchedulerMethodScheduleFrameName,
-1,
bindings.MessageHeader.kMessageExpectsResponse);
}
}
-class _SceneSchedulerStubControl
+class _FrameSchedulerStubControl
extends bindings.StubMessageHandler
- implements bindings.StubControl<SceneScheduler> {
- SceneScheduler _impl;
+ implements bindings.StubControl<FrameScheduler> {
+ FrameScheduler _impl;
- _SceneSchedulerStubControl.fromEndpoint(
- core.MojoMessagePipeEndpoint endpoint, [SceneScheduler impl])
+ _FrameSchedulerStubControl.fromEndpoint(
+ core.MojoMessagePipeEndpoint endpoint, [FrameScheduler impl])
: super.fromEndpoint(endpoint, autoBegin: impl != null) {
_impl = impl;
}
- _SceneSchedulerStubControl.fromHandle(
- core.MojoHandle handle, [SceneScheduler impl])
+ _FrameSchedulerStubControl.fromHandle(
+ core.MojoHandle handle, [FrameScheduler impl])
: super.fromHandle(handle, autoBegin: impl != null) {
_impl = impl;
}
- _SceneSchedulerStubControl.unbound([this._impl]) : super.unbound();
+ _FrameSchedulerStubControl.unbound([this._impl]) : super.unbound();
- SceneSchedulerScheduleFrameResponseParams _sceneSchedulerScheduleFrameResponseParamsFactory(FrameInfo frameInfo) {
- var result = new SceneSchedulerScheduleFrameResponseParams();
+ FrameSchedulerScheduleFrameResponseParams _frameSchedulerScheduleFrameResponseParamsFactory(FrameInfo frameInfo) {
+ var result = new FrameSchedulerScheduleFrameResponseParams();
result.frameInfo = frameInfo;
return result;
}
@@ -397,14 +397,14 @@
throw new core.MojoApiError("$this has no implementation set");
}
switch (message.header.type) {
- case _sceneSchedulerMethodScheduleFrameName:
- var response = _impl.scheduleFrame(_sceneSchedulerScheduleFrameResponseParamsFactory);
+ case _frameSchedulerMethodScheduleFrameName:
+ var response = _impl.scheduleFrame(_frameSchedulerScheduleFrameResponseParamsFactory);
if (response is Future) {
return response.then((response) {
if (response != null) {
return buildResponseWithId(
response,
- _sceneSchedulerMethodScheduleFrameName,
+ _frameSchedulerMethodScheduleFrameName,
message.header.requestId,
bindings.MessageHeader.kMessageIsResponse);
}
@@ -412,7 +412,7 @@
} else if (response != null) {
return buildResponseWithId(
response,
- _sceneSchedulerMethodScheduleFrameName,
+ _frameSchedulerMethodScheduleFrameName,
message.header.requestId,
bindings.MessageHeader.kMessageIsResponse);
}
@@ -424,8 +424,8 @@
return null;
}
- SceneScheduler get impl => _impl;
- set impl(SceneScheduler d) {
+ FrameScheduler get impl => _impl;
+ set impl(FrameScheduler d) {
if (d == null) {
throw new core.MojoApiError("$this: Cannot set a null implementation");
}
@@ -446,7 +446,7 @@
@override
String toString() {
var superString = super.toString();
- return "_SceneSchedulerStubControl($superString)";
+ return "_FrameSchedulerStubControl($superString)";
}
int get version => 0;
@@ -454,34 +454,34 @@
static service_describer.ServiceDescription _cachedServiceDescription;
static service_describer.ServiceDescription get serviceDescription {
if (_cachedServiceDescription == null) {
- _cachedServiceDescription = new _SceneSchedulerServiceDescription();
+ _cachedServiceDescription = new _FrameSchedulerServiceDescription();
}
return _cachedServiceDescription;
}
}
-class SceneSchedulerStub
- extends bindings.Stub<SceneScheduler>
- implements SceneScheduler {
- SceneSchedulerStub.fromEndpoint(
- core.MojoMessagePipeEndpoint endpoint, [SceneScheduler impl])
- : super(new _SceneSchedulerStubControl.fromEndpoint(endpoint, impl));
+class FrameSchedulerStub
+ extends bindings.Stub<FrameScheduler>
+ implements FrameScheduler {
+ FrameSchedulerStub.fromEndpoint(
+ core.MojoMessagePipeEndpoint endpoint, [FrameScheduler impl])
+ : super(new _FrameSchedulerStubControl.fromEndpoint(endpoint, impl));
- SceneSchedulerStub.fromHandle(
- core.MojoHandle handle, [SceneScheduler impl])
- : super(new _SceneSchedulerStubControl.fromHandle(handle, impl));
+ FrameSchedulerStub.fromHandle(
+ core.MojoHandle handle, [FrameScheduler impl])
+ : super(new _FrameSchedulerStubControl.fromHandle(handle, impl));
- SceneSchedulerStub.unbound([SceneScheduler impl])
- : super(new _SceneSchedulerStubControl.unbound(impl));
+ FrameSchedulerStub.unbound([FrameScheduler impl])
+ : super(new _FrameSchedulerStubControl.unbound(impl));
- static SceneSchedulerStub newFromEndpoint(
+ static FrameSchedulerStub newFromEndpoint(
core.MojoMessagePipeEndpoint endpoint) {
- assert(endpoint.setDescription("For SceneSchedulerStub"));
- return new SceneSchedulerStub.fromEndpoint(endpoint);
+ assert(endpoint.setDescription("For FrameSchedulerStub"));
+ return new FrameSchedulerStub.fromEndpoint(endpoint);
}
static service_describer.ServiceDescription get serviceDescription =>
- _SceneSchedulerStubControl.serviceDescription;
+ _FrameSchedulerStubControl.serviceDescription;
dynamic scheduleFrame([Function responseFactory = null]) {
diff --git a/mojo/services/gfx/composition/interfaces/renderers.mojom b/mojo/services/gfx/composition/interfaces/renderers.mojom
index a911d40..97f8d89 100644
--- a/mojo/services/gfx/composition/interfaces/renderers.mojom
+++ b/mojo/services/gfx/composition/interfaces/renderers.mojom
@@ -8,6 +8,7 @@
import "mojo/services/geometry/interfaces/geometry.mojom";
import "mojo/services/gfx/composition/interfaces/hit_tests.mojom";
import "mojo/services/gfx/composition/interfaces/scene_token.mojom";
+import "mojo/services/gfx/composition/interfaces/scheduling.mojom";
// The renderer is a service which renders a scene graph to a display.
//
@@ -36,6 +37,9 @@
// Dissociates the root scene from the renderer.
ClearRootScene();
+ // Gets a scheduler to receive frame timing information for the renderer.
+ GetScheduler(FrameScheduler& scheduler);
+
// Provides an interface which can be used to perform hit tests on the
// contents of the renderer's scene graph.
GetHitTester(HitTester& hit_tester);
diff --git a/mojo/services/gfx/composition/interfaces/scenes.mojom b/mojo/services/gfx/composition/interfaces/scenes.mojom
index 158f964..d4dadec 100644
--- a/mojo/services/gfx/composition/interfaces/scenes.mojom
+++ b/mojo/services/gfx/composition/interfaces/scenes.mojom
@@ -117,8 +117,8 @@
//
// SCHEDULING
//
-// The scene provides support for scheduling update via its associated
-// |SceneScheduler| interface. Use the scheduler to obtain frame timing
+// The scene provides support for scheduling frames via its associated
+// |FrameScheduler| interface. Use the scheduler to obtain frame timing
// information and to throttle updates to match the display refresh rate
// (ie. with vsync).
interface Scene {
@@ -155,8 +155,8 @@
// introducing cycles in the scene's nodes; the connection will be closed.
Publish(SceneMetadata? metadata);
- // Gets a scheduler for scheduling upcoming scene operations.
- GetScheduler(SceneScheduler& scheduler);
+ // Gets a scheduler to receive frame timing information for the scene.
+ GetScheduler(FrameScheduler& scheduler);
};
// An interface applications may implement to receive events from a scene.
diff --git a/mojo/services/gfx/composition/interfaces/scheduling.mojom b/mojo/services/gfx/composition/interfaces/scheduling.mojom
index 7b5dee1..91b09ae 100644
--- a/mojo/services/gfx/composition/interfaces/scheduling.mojom
+++ b/mojo/services/gfx/composition/interfaces/scheduling.mojom
@@ -5,26 +5,25 @@
[DartPackage="mojo_services"]
module mojo.gfx.composition;
-// Provides support for scheduling drawing and composition operations
-// for a particular scene.
+// Provides support for scheduling drawing and composition operations.
//
-// Instances of this interface must be obtained from a |Scene|. This interface
-// is modeled separately so as to allow applications to use different threads
-// for scheduling work as opposed to publishing scene updates.
-interface SceneScheduler {
+// Instances of this interface must be obtained from a |Scene| or |Renderer|.
+interface FrameScheduler {
// Asks the compositor to invoke the callback when it is a good time to
// draw the next frame.
//
// The rate at which the callback is invoked may depend on how the scene
// has been embedded. The scene will only receive frame callbacks while
// it is attached to a scene graph which the compositor has been asked
- // to renderer since timing information ultimately derives from the
+ // to render since timing information ultimately derives from the
// renderer. If the same scene is being rendered to multiple destinations
- // with different timing requirements, the compositor will perform rate
- // adaptation as required behind the scenes.
+ // with different timing requirements, the compositor will couple the
+ // scene scheduling to one of the renderers.
//
// The returned |frame_info| provides information about the frame to
- // be drawn.
+ // be drawn. This information should be passed to a
+ // |mojo::gfx::composition::FrameTracker| to apply compensation for
+ // skipped frames before using it.
//
// TODO(jeffbrown): Consider whether we should have the callback be invoked
// immediately rather than on schedule, letting the client set its own
diff --git a/mojo/tools/data/apptests b/mojo/tools/data/apptests
index b02c076..6d1c170 100644
--- a/mojo/tools/data/apptests
+++ b/mojo/tools/data/apptests
@@ -15,6 +15,10 @@
"test": "mojo:clipboard_apptests",
},
{
+ "test": "mojo:compositor_apptests",
+ "shell-args": ["--args-for=mojo:native_viewport_service --use-test-config"],
+ },
+ {
"test": "mojo:example_apptests",
# ExampleApplicationTest.CheckCommandLineArg checks --example_apptest_arg.
"test-args": ["--example_apptest_arg"],
diff --git a/mojo/ui/choreographer.cc b/mojo/ui/choreographer.cc
index 758b70e..4267267 100644
--- a/mojo/ui/choreographer.cc
+++ b/mojo/ui/choreographer.cc
@@ -15,14 +15,14 @@
ChoreographerDelegate* delegate)
: delegate_(delegate) {
DCHECK(delegate_);
- scene->GetScheduler(mojo::GetProxy(&scene_scheduler_));
+ scene->GetScheduler(mojo::GetProxy(&frame_scheduler_));
}
Choreographer::Choreographer(
- mojo::gfx::composition::SceneSchedulerPtr scene_scheduler,
+ mojo::gfx::composition::FrameSchedulerPtr frame_scheduler,
ChoreographerDelegate* delegate)
- : scene_scheduler_(scene_scheduler.Pass()), delegate_(delegate) {
- DCHECK(scene_scheduler_);
+ : frame_scheduler_(frame_scheduler.Pass()), delegate_(delegate) {
+ DCHECK(frame_scheduler_);
DCHECK(delegate_);
}
@@ -38,7 +38,7 @@
void Choreographer::ScheduleFrame() {
if (!frame_scheduled_) {
frame_scheduled_ = true;
- scene_scheduler_->ScheduleFrame(
+ frame_scheduler_->ScheduleFrame(
base::Bind(&Choreographer::DoFrame, base::Unretained(this)));
}
}
diff --git a/mojo/ui/choreographer.h b/mojo/ui/choreographer.h
index 4e0cad5..a3844fd 100644
--- a/mojo/ui/choreographer.h
+++ b/mojo/ui/choreographer.h
@@ -43,13 +43,13 @@
public:
Choreographer(mojo::gfx::composition::Scene* scene,
ChoreographerDelegate* delegate);
- Choreographer(mojo::gfx::composition::SceneSchedulerPtr scene_scheduler,
+ Choreographer(mojo::gfx::composition::FrameSchedulerPtr frame_scheduler,
ChoreographerDelegate* delegate);
~Choreographer();
// Gets the scene scheduler.
- mojo::gfx::composition::SceneScheduler* scene_scheduler() {
- return scene_scheduler_.get();
+ mojo::gfx::composition::FrameScheduler* frame_scheduler() {
+ return frame_scheduler_.get();
}
// Gets the frame tracker.
@@ -57,11 +57,11 @@
return frame_tracker_;
}
- // Schedules a call to the delegate's |OnDraw| using the scene scheduler.
+ // Schedules a call to the delegate's |OnDraw| using the frame scheduler.
void ScheduleDraw();
private:
- mojo::gfx::composition::SceneSchedulerPtr scene_scheduler_;
+ mojo::gfx::composition::FrameSchedulerPtr frame_scheduler_;
ChoreographerDelegate* delegate_;
mojo::gfx::composition::FrameTracker frame_tracker_;
diff --git a/services/gfx/compositor/BUILD.gn b/services/gfx/compositor/BUILD.gn
index 40250f0..d03b6ce 100644
--- a/services/gfx/compositor/BUILD.gn
+++ b/services/gfx/compositor/BUILD.gn
@@ -22,6 +22,8 @@
"compositor_engine.h",
"compositor_impl.cc",
"compositor_impl.h",
+ "frame_dispatcher.cc",
+ "frame_dispatcher.h",
"graph/nodes.cc",
"graph/nodes.h",
"graph/resources.cc",
@@ -91,12 +93,15 @@
sources = [
"backend/vsync_scheduler_unittest.cc",
+ "tests/scheduling_apptest.cc",
]
deps = [
":common",
"//base/test:test_support",
"//mojo/application:test_support",
+ "//mojo/services/gfx/composition/interfaces:interfaces_sync",
+ "//mojo/services/native_viewport/interfaces",
"//testing/gtest",
]
}
diff --git a/services/gfx/compositor/backend/gpu_output.cc b/services/gfx/compositor/backend/gpu_output.cc
index 531f2ee..d52ef5a 100644
--- a/services/gfx/compositor/backend/gpu_output.cc
+++ b/services/gfx/compositor/backend/gpu_output.cc
@@ -35,6 +35,7 @@
: compositor_task_runner_(base::MessageLoop::current()->task_runner()),
vsync_scheduler_(
new VsyncScheduler(compositor_task_runner_, scheduler_callbacks)),
+ error_callback_(error_callback),
rasterizer_thread_(new base::Thread("gpu_rasterizer")),
rasterizer_initialized_(true, false) {
DCHECK(context_provider);
diff --git a/services/gfx/compositor/compositor_engine.cc b/services/gfx/compositor/compositor_engine.cc
index bc05f2a..eade11f 100644
--- a/services/gfx/compositor/compositor_engine.cc
+++ b/services/gfx/compositor/compositor_engine.cc
@@ -215,10 +215,12 @@
}
void CompositorEngine::ScheduleFrame(SceneState* scene_state,
- const SceneFrameCallback& callback) {
+ const FrameCallback& callback) {
DCHECK(IsSceneStateRegisteredDebug(scene_state));
+ DVLOG(1) << "ScheduleFrame: scene=" << scene_state;
- scene_state->AddSceneFrameCallback(callback);
+ if (!scene_state->frame_dispatcher().AddCallback(callback))
+ return;
// TODO(jeffbrown): Be more selective and do this work only for scenes
// which are strongly associated with the renderer so it doesn't receive
@@ -276,6 +278,18 @@
}
}
+void CompositorEngine::ScheduleFrame(RendererState* renderer_state,
+ const FrameCallback& callback) {
+ DCHECK(IsRendererStateRegisteredDebug(renderer_state));
+ DVLOG(1) << "ScheduleFrame: renderer=" << renderer_state;
+
+ if (!renderer_state->frame_dispatcher().AddCallback(callback))
+ return;
+
+ ScheduleFrameForRenderer(renderer_state,
+ Scheduler::SchedulingMode::kUpdateAndSnapshot);
+}
+
void CompositorEngine::HitTest(
RendererState* renderer_state,
mojo::PointFPtr point,
@@ -483,10 +497,12 @@
return;
DCHECK(IsRendererStateRegisteredDebug(renderer_state));
+ renderer_state->frame_dispatcher().DispatchCallbacks(frame_info);
+
// TODO(jeffbrown): Be more selective and do this work only for scenes
// associated with the renderer.
for (auto& pair : scenes_by_token_) {
- pair.second->DispatchSceneFrameCallbacks(frame_info);
+ pair.second->frame_dispatcher().DispatchCallbacks(frame_info);
}
}
diff --git a/services/gfx/compositor/compositor_engine.h b/services/gfx/compositor/compositor_engine.h
index e4abaad..4a64260 100644
--- a/services/gfx/compositor/compositor_engine.h
+++ b/services/gfx/compositor/compositor_engine.h
@@ -57,7 +57,7 @@
// Schedules a frame callback.
void ScheduleFrame(SceneState* scene_state,
- const SceneFrameCallback& callback);
+ const FrameCallback& callback);
// RENDERER REQUESTS
@@ -72,6 +72,10 @@
// Destroys |renderer_state| if an error occurs.
void ClearRootScene(RendererState* renderer_state);
+ // Schedules a frame callback.
+ void ScheduleFrame(RendererState* renderer_state,
+ const FrameCallback& callback);
+
// Performs a hit test.
void HitTest(
RendererState* renderer_state,
diff --git a/services/gfx/compositor/frame_dispatcher.cc b/services/gfx/compositor/frame_dispatcher.cc
new file mode 100644
index 0000000..0d04e3f
--- /dev/null
+++ b/services/gfx/compositor/frame_dispatcher.cc
@@ -0,0 +1,26 @@
+// Copyright 2016 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 "services/gfx/compositor/frame_dispatcher.h"
+
+namespace compositor {
+
+FrameDispatcher::FrameDispatcher() {}
+
+FrameDispatcher::~FrameDispatcher() {}
+
+bool FrameDispatcher::AddCallback(const FrameCallback& callback) {
+ pending_callbacks_.emplace_back(callback);
+ return pending_callbacks_.size() == 1u;
+}
+
+void FrameDispatcher::DispatchCallbacks(
+ const mojo::gfx::composition::FrameInfo& frame_info) {
+ for (auto& callback : pending_callbacks_) {
+ callback.Run(frame_info.Clone());
+ }
+ pending_callbacks_.clear();
+}
+
+} // namespace compositor
diff --git a/services/gfx/compositor/frame_dispatcher.h b/services/gfx/compositor/frame_dispatcher.h
new file mode 100644
index 0000000..6c9bc26
--- /dev/null
+++ b/services/gfx/compositor/frame_dispatcher.h
@@ -0,0 +1,39 @@
+// Copyright 2016 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_GFX_COMPOSITOR_FRAME_DISPATCHER_H_
+#define SERVICES_GFX_COMPOSITOR_FRAME_DISPATCHER_H_
+
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "mojo/services/gfx/composition/interfaces/scheduling.mojom.h"
+
+namespace compositor {
+
+using FrameCallback =
+ base::Callback<void(mojo::gfx::composition::FrameInfoPtr)>;
+
+// Maintains a list of pending frame callbacks to be dispatched.
+class FrameDispatcher {
+ public:
+ FrameDispatcher();
+ ~FrameDispatcher();
+
+ // Adds a callback, returns true if it was the first pending callback.
+ bool AddCallback(const FrameCallback& callback);
+
+ // Dispatches all pending callbacks then clears the list.
+ void DispatchCallbacks(const mojo::gfx::composition::FrameInfo& frame_info);
+
+ private:
+ std::vector<FrameCallback> pending_callbacks_;
+
+ DISALLOW_COPY_AND_ASSIGN(FrameDispatcher);
+};
+
+} // namespace compositor
+
+#endif // SERVICES_GFX_COMPOSITOR_FRAME_DISPATCHER_H_
diff --git a/services/gfx/compositor/renderer_impl.cc b/services/gfx/compositor/renderer_impl.cc
index 1a0357a..547449c 100644
--- a/services/gfx/compositor/renderer_impl.cc
+++ b/services/gfx/compositor/renderer_impl.cc
@@ -8,6 +8,13 @@
#include "base/bind_helpers.h"
namespace compositor {
+namespace {
+void RunScheduleFrameCallback(
+ const RendererImpl::ScheduleFrameCallback& callback,
+ mojo::gfx::composition::FrameInfoPtr info) {
+ callback.Run(info.Pass());
+}
+} // namespace
RendererImpl::RendererImpl(
CompositorEngine* engine,
@@ -37,6 +44,17 @@
engine_->ClearRootScene(state_);
}
+void RendererImpl::GetScheduler(
+ mojo::InterfaceRequest<mojo::gfx::composition::FrameScheduler>
+ scheduler_request) {
+ scheduler_bindings_.AddBinding(this, scheduler_request.Pass());
+}
+
+void RendererImpl::ScheduleFrame(const ScheduleFrameCallback& callback) {
+ engine_->ScheduleFrame(state_,
+ base::Bind(&RunScheduleFrameCallback, callback));
+}
+
void RendererImpl::HitTest(mojo::PointFPtr point,
const HitTestCallback& callback) {
engine_->HitTest(state_, point.Pass(), callback);
diff --git a/services/gfx/compositor/renderer_impl.h b/services/gfx/compositor/renderer_impl.h
index 2571c33..bac5cf7 100644
--- a/services/gfx/compositor/renderer_impl.h
+++ b/services/gfx/compositor/renderer_impl.h
@@ -18,6 +18,7 @@
// Renderer interface implementation.
// This object is owned by its associated RendererState.
class RendererImpl : public mojo::gfx::composition::Renderer,
+ public mojo::gfx::composition::FrameScheduler,
public mojo::gfx::composition::HitTester {
public:
RendererImpl(CompositorEngine* engine,
@@ -36,15 +37,22 @@
uint32 scene_version,
mojo::RectPtr viewport) override;
void ClearRootScene() override;
+ void GetScheduler(
+ mojo::InterfaceRequest<mojo::gfx::composition::FrameScheduler>
+ scheduler_request) override;
void GetHitTester(mojo::InterfaceRequest<mojo::gfx::composition::HitTester>
hit_tester_request) override;
+ // |FrameScheduler|:
+ void ScheduleFrame(const ScheduleFrameCallback& callback) override;
+
// |HitTester|:
void HitTest(mojo::PointFPtr point, const HitTestCallback& callback) override;
CompositorEngine* const engine_;
RendererState* const state_;
mojo::Binding<mojo::gfx::composition::Renderer> renderer_binding_;
+ mojo::BindingSet<mojo::gfx::composition::FrameScheduler> scheduler_bindings_;
mojo::BindingSet<mojo::gfx::composition::HitTester> hit_tester_bindings;
DISALLOW_COPY_AND_ASSIGN(RendererImpl);
diff --git a/services/gfx/compositor/renderer_state.h b/services/gfx/compositor/renderer_state.h
index 1cfbf4d..4c3c0d1 100644
--- a/services/gfx/compositor/renderer_state.h
+++ b/services/gfx/compositor/renderer_state.h
@@ -13,6 +13,7 @@
#include "mojo/services/gfx/composition/cpp/formatting.h"
#include "mojo/services/gfx/composition/interfaces/compositor.mojom.h"
#include "services/gfx/compositor/backend/output.h"
+#include "services/gfx/compositor/frame_dispatcher.h"
#include "services/gfx/compositor/graph/snapshot.h"
#include "services/gfx/compositor/scene_state.h"
@@ -71,6 +72,8 @@
// If the snapshot is not blocked, also updates |visible_snapshot()|.
void SetSnapshot(const scoped_refptr<const Snapshot>& snapshot);
+ FrameDispatcher& frame_dispatcher() { return frame_dispatcher_; }
+
const std::string& label() { return label_; }
std::string FormattedLabel();
@@ -80,6 +83,7 @@
const std::string label_;
std::string formatted_label_cache_;
+ FrameDispatcher frame_dispatcher_; // must be before renderer_impl_
std::unique_ptr<mojo::gfx::composition::Renderer> renderer_impl_;
SceneState* root_scene_ = nullptr;
diff --git a/services/gfx/compositor/scene_impl.cc b/services/gfx/compositor/scene_impl.cc
index 5db165c..64abed4 100644
--- a/services/gfx/compositor/scene_impl.cc
+++ b/services/gfx/compositor/scene_impl.cc
@@ -42,14 +42,14 @@
}
void SceneImpl::GetScheduler(
- mojo::InterfaceRequest<mojo::gfx::composition::SceneScheduler>
+ mojo::InterfaceRequest<mojo::gfx::composition::FrameScheduler>
scheduler_request) {
scheduler_bindings_.AddBinding(this, scheduler_request.Pass());
}
void SceneImpl::ScheduleFrame(const ScheduleFrameCallback& callback) {
engine_->ScheduleFrame(state_,
- base::Bind(&RunScheduleFrameCallback, callback));
+ base::Bind(RunScheduleFrameCallback, callback));
}
} // namespace compositor
diff --git a/services/gfx/compositor/scene_impl.h b/services/gfx/compositor/scene_impl.h
index f145c15..c7f7e8f 100644
--- a/services/gfx/compositor/scene_impl.h
+++ b/services/gfx/compositor/scene_impl.h
@@ -18,7 +18,7 @@
// Scene interface implementation.
// This object is owned by its associated SceneState.
class SceneImpl : public mojo::gfx::composition::Scene,
- public mojo::gfx::composition::SceneScheduler {
+ public mojo::gfx::composition::FrameScheduler {
public:
SceneImpl(
CompositorEngine* engine,
@@ -37,16 +37,16 @@
void Update(mojo::gfx::composition::SceneUpdatePtr update) override;
void Publish(mojo::gfx::composition::SceneMetadataPtr metadata) override;
void GetScheduler(
- mojo::InterfaceRequest<mojo::gfx::composition::SceneScheduler>
+ mojo::InterfaceRequest<mojo::gfx::composition::FrameScheduler>
scheduler_request) override;
- // |SceneScheduler|:
+ // |FrameScheduler|:
void ScheduleFrame(const ScheduleFrameCallback& callback) override;
CompositorEngine* const engine_;
SceneState* const state_;
mojo::Binding<mojo::gfx::composition::Scene> scene_binding_;
- mojo::BindingSet<mojo::gfx::composition::SceneScheduler> scheduler_bindings_;
+ mojo::BindingSet<mojo::gfx::composition::FrameScheduler> scheduler_bindings_;
DISALLOW_COPY_AND_ASSIGN(SceneImpl);
};
diff --git a/services/gfx/compositor/scene_state.cc b/services/gfx/compositor/scene_state.cc
index a5dab82..e54ce55 100644
--- a/services/gfx/compositor/scene_state.cc
+++ b/services/gfx/compositor/scene_state.cc
@@ -14,24 +14,7 @@
DCHECK(scene_token_);
}
-SceneState::~SceneState() {
- // The scene implementation and all of its bindings must be destroyed
- // before any pending callbacks are dropped on the floor.
- scene_impl_.reset();
- pending_frame_callbacks_.clear();
-}
-
-void SceneState::AddSceneFrameCallback(const SceneFrameCallback& callback) {
- pending_frame_callbacks_.push_back(callback);
-}
-
-void SceneState::DispatchSceneFrameCallbacks(
- const mojo::gfx::composition::FrameInfo& frame_info) {
- for (auto& callback : pending_frame_callbacks_) {
- callback.Run(frame_info.Clone());
- }
- pending_frame_callbacks_.clear();
-}
+SceneState::~SceneState() {}
std::ostream& operator<<(std::ostream& os, SceneState* scene_state) {
if (!scene_state)
diff --git a/services/gfx/compositor/scene_state.h b/services/gfx/compositor/scene_state.h
index c5f07ef..ec26485 100644
--- a/services/gfx/compositor/scene_state.h
+++ b/services/gfx/compositor/scene_state.h
@@ -8,20 +8,17 @@
#include <memory>
#include <string>
#include <unordered_map>
-#include <vector>
#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "mojo/services/gfx/composition/cpp/formatting.h"
#include "mojo/services/gfx/composition/interfaces/scenes.mojom.h"
+#include "services/gfx/compositor/frame_dispatcher.h"
#include "services/gfx/compositor/graph/scene_def.h"
namespace compositor {
-using SceneFrameCallback =
- base::Callback<void(mojo::gfx::composition::FrameInfoPtr)>;
-
// Describes the state of a particular scene.
// This object is owned by the CompositorEngine that created it.
class SceneState {
@@ -54,15 +51,15 @@
// Gets the underlying scene definition, never null.
SceneDef* scene_def() { return &scene_def_; }
- void AddSceneFrameCallback(const SceneFrameCallback& callback);
- void DispatchSceneFrameCallbacks(
- const mojo::gfx::composition::FrameInfo& frame_info);
+ FrameDispatcher& frame_dispatcher() { return frame_dispatcher_; }
private:
mojo::gfx::composition::SceneTokenPtr scene_token_;
+
+ FrameDispatcher frame_dispatcher_; // must be before scene_impl_
std::unique_ptr<mojo::gfx::composition::Scene> scene_impl_;
+
mojo::gfx::composition::SceneListenerPtr scene_listener_;
- std::vector<SceneFrameCallback> pending_frame_callbacks_;
SceneDef scene_def_;
diff --git a/services/gfx/compositor/tests/README.md b/services/gfx/compositor/tests/README.md
new file mode 100644
index 0000000..9188770
--- /dev/null
+++ b/services/gfx/compositor/tests/README.md
@@ -0,0 +1,10 @@
+# Mozart API Tests
+
+This directory contains test cases that exercise the Mozart Compositor APIs
+through IPC as a client would.
+
+These tests need a virtual framebuffer to execute. Run them like this:
+
+$ testing/xvfb.py out/Debug mojo_run mojo:compositor_apptests
+ --shell-path out/Debug/mojo_shell \
+ --args-for="mojo:native_viewport_service --use-test-config"
diff --git a/services/gfx/compositor/tests/scheduling_apptest.cc b/services/gfx/compositor/tests/scheduling_apptest.cc
new file mode 100644
index 0000000..d116e61
--- /dev/null
+++ b/services/gfx/compositor/tests/scheduling_apptest.cc
@@ -0,0 +1,125 @@
+// Copyright 2016 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 "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/application/application_test_base.h"
+#include "mojo/public/cpp/application/connect.h"
+#include "mojo/public/cpp/bindings/synchronous_interface_ptr.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/services/gfx/composition/interfaces/compositor.mojom-sync.h"
+#include "mojo/services/gfx/composition/interfaces/scheduling.mojom-sync.h"
+#include "mojo/services/gpu/interfaces/context_provider.mojom.h"
+#include "mojo/services/native_viewport/interfaces/native_viewport.mojom.h"
+
+namespace test {
+
+using SynchronousCompositorPtr =
+ mojo::SynchronousInterfacePtr<mojo::gfx::composition::Compositor>;
+
+using SynchronousFrameSchedulerPtr =
+ mojo::SynchronousInterfacePtr<mojo::gfx::composition::FrameScheduler>;
+
+class SchedulingTest : public mojo::test::ApplicationTestBase {
+ public:
+ SchedulingTest() {}
+
+ protected:
+ void SetUp() override {
+ mojo::test::ApplicationTestBase::SetUp();
+
+ mojo::ConnectToService(shell(), "mojo:native_viewport_service",
+ GetProxy(&viewport_));
+ auto size = mojo::Size::New();
+ size->width = 320;
+ size->height = 640;
+ auto configuration = mojo::SurfaceConfiguration::New();
+ viewport_->Create(size.Pass(), configuration.Pass(),
+ [](mojo::ViewportMetricsPtr metrics) {
+
+ });
+ viewport_->Show();
+
+ mojo::ContextProviderPtr context_provider;
+ viewport_->GetContextProvider(GetProxy(&context_provider));
+
+ mojo::ConnectToService(shell(), "mojo:compositor_service",
+ mojo::GetSynchronousProxy(&compositor_));
+ compositor_->CreateRenderer(context_provider.Pass(),
+ mojo::GetProxy(&renderer_), "SchedulingTest");
+ }
+
+ void TestScheduler(SynchronousFrameSchedulerPtr scheduler) {
+ mojo::gfx::composition::FrameInfoPtr frame_info1;
+ ASSERT_TRUE(scheduler->ScheduleFrame(&frame_info1));
+ AssertValidFrameInfo(frame_info1.get());
+
+ mojo::gfx::composition::FrameInfoPtr frame_info2;
+ ASSERT_TRUE(scheduler->ScheduleFrame(&frame_info2));
+ AssertValidFrameInfo(frame_info2.get());
+
+ EXPECT_GT(frame_info2->frame_time, frame_info1->frame_time);
+ EXPECT_GT(frame_info2->presentation_time, frame_info1->presentation_time);
+ }
+
+ void AssertValidFrameInfo(mojo::gfx::composition::FrameInfo* frame_info) {
+ ASSERT_NE(nullptr, frame_info);
+ EXPECT_LT(frame_info->frame_time, MojoGetTimeTicksNow());
+ EXPECT_GT(frame_info->frame_interval, 0u);
+ EXPECT_GT(frame_info->frame_deadline, frame_info->frame_time);
+ EXPECT_GT(frame_info->presentation_time, frame_info->frame_deadline);
+ }
+
+ mojo::NativeViewportPtr viewport_;
+ SynchronousCompositorPtr compositor_;
+ mojo::gfx::composition::RendererPtr renderer_;
+
+ private:
+ MOJO_DISALLOW_COPY_AND_ASSIGN(SchedulingTest);
+};
+
+namespace {
+
+TEST_F(SchedulingTest, RendererScheduler) {
+ SynchronousFrameSchedulerPtr scheduler;
+ renderer_->GetScheduler(mojo::GetSynchronousProxy(&scheduler));
+ TestScheduler(scheduler.Pass());
+}
+
+// Test what happens when a scene is not attached to a renderer.
+// It should still receive scheduled frame updates occasionally albeit
+// at some indeterminate rate (enough to keep the scene from hanging).
+TEST_F(SchedulingTest, OrphanedSceneScheduler) {
+ mojo::gfx::composition::ScenePtr scene;
+ mojo::gfx::composition::SceneTokenPtr scene_token;
+ compositor_->CreateScene(mojo::GetProxy(&scene), "SchedulingTest",
+ &scene_token);
+
+ SynchronousFrameSchedulerPtr scheduler;
+ scene->GetScheduler(mojo::GetSynchronousProxy(&scheduler));
+ TestScheduler(scheduler.Pass());
+}
+
+// Test what happens when a scene is attached to a renderer.
+// It should receive scheduled frame updates at a rate determined
+// by the renderer.
+TEST_F(SchedulingTest, RootSceneScheduler) {
+ mojo::gfx::composition::ScenePtr scene;
+ mojo::gfx::composition::SceneTokenPtr scene_token;
+ compositor_->CreateScene(mojo::GetProxy(&scene), "SchedulingTest",
+ &scene_token);
+
+ auto viewport = mojo::Rect::New();
+ viewport->width = 1;
+ viewport->height = 1;
+ renderer_->SetRootScene(scene_token.Pass(),
+ mojo::gfx::composition::kSceneVersionNone,
+ viewport.Pass());
+
+ SynchronousFrameSchedulerPtr scheduler;
+ scene->GetScheduler(mojo::GetSynchronousProxy(&scheduler));
+ TestScheduler(scheduler.Pass());
+}
+
+} // namespace
+} // namespace mojo
diff --git a/services/ui/launcher/launch_instance.cc b/services/ui/launcher/launch_instance.cc
index 82169cb..c8ed4f3 100644
--- a/services/ui/launcher/launch_instance.cc
+++ b/services/ui/launcher/launch_instance.cc
@@ -62,7 +62,7 @@
auto requested_configuration = mojo::SurfaceConfiguration::New();
viewport_->Create(
- size.Clone(), requested_configuration.Pass(),
+ size.Pass(), requested_configuration.Pass(),
base::Bind(&LaunchInstance::OnViewportCreated, base::Unretained(this)));
}