[scenic] Simplified gfx::Session

Pulled all the ApplyCommand stuff out of session into it's own GfxCommandApplier class to make Session more readable, and simplified the destruction of SessionHandler, so that it's now a two-way dependency between SessionHandler and SessionManager instead of a three-way dependency involving Session.

Also removed the exposed reference to scenic from CommandDispatcherContext mentioned in SCN-808.

SCN-1194 #done
SCN-808 #comment

Test: fx build-push scenic_tests && fx run-test scenic_tests

And manually verified that
killall basemgr
didn't kill scenic and that launching
fx shell set_root_view fuchsia-pkg://fuchsia.com/noodles#meta/noodles.cmx
worked afterwards

Change-Id: I51631862e22dd4bebb13946343ccda52aee0c5ea
diff --git a/bin/ui/BUILD.gn b/bin/ui/BUILD.gn
index 9db7743..da72969 100644
--- a/bin/ui/BUILD.gn
+++ b/bin/ui/BUILD.gn
@@ -76,6 +76,10 @@
       dest = "input_unittests.cmx"
     },
     {
+      path = rebase_path("meta/scenic_unittests.cmx")
+      dest = "scenic_unittests.cmx"
+    },
+    {
       path = rebase_path("meta/view_manager_apptests.cmx")
       dest = "view_manager_apptests.cmx"
     },
diff --git a/bin/ui/meta/scenic_unittests.cmx b/bin/ui/meta/scenic_unittests.cmx
new file mode 100644
index 0000000..30a58aa
--- /dev/null
+++ b/bin/ui/meta/scenic_unittests.cmx
@@ -0,0 +1,11 @@
+{
+    "program": {
+        "binary": "test/scenic_unittests"
+    },
+    "sandbox": {
+        "features": [ "vulkan" ],
+        "services": [
+            "fuchsia.vulkan.loader.Loader"
+        ]
+    }
+}
diff --git a/lib/ui/gfx/BUILD.gn b/lib/ui/gfx/BUILD.gn
index 24cc45e..afb865d 100644
--- a/lib/ui/gfx/BUILD.gn
+++ b/lib/ui/gfx/BUILD.gn
@@ -75,6 +75,8 @@
     "engine/frame_scheduler.h",
     "engine/frame_timings.cc",
     "engine/frame_timings.h",
+    "engine/gfx_command_applier.cc",
+    "engine/gfx_command_applier.h",
     "engine/hardware_layer_assignment.cc",
     "engine/hardware_layer_assignment.h",
     "engine/hit.h",
diff --git a/lib/ui/gfx/engine/gfx_command_applier.cc b/lib/ui/gfx/engine/gfx_command_applier.cc
new file mode 100644
index 0000000..fb13818
--- /dev/null
+++ b/lib/ui/gfx/engine/gfx_command_applier.cc
@@ -0,0 +1,1530 @@
+// Copyright 2019 The Fuchsia 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 "garnet/lib/ui/gfx/engine/gfx_command_applier.h"
+
+#include <lib/async/default.h>
+#include <trace/event.h>
+
+#include "garnet/lib/ui/gfx/engine/resource_linker.h"
+#include "garnet/lib/ui/gfx/resources/buffer.h"
+#include "garnet/lib/ui/gfx/resources/camera.h"
+#include "garnet/lib/ui/gfx/resources/compositor/display_compositor.h"
+#include "garnet/lib/ui/gfx/resources/compositor/layer.h"
+#include "garnet/lib/ui/gfx/resources/compositor/layer_stack.h"
+#include "garnet/lib/ui/gfx/resources/image.h"
+#include "garnet/lib/ui/gfx/resources/image_pipe.h"
+#include "garnet/lib/ui/gfx/resources/image_pipe_handler.h"
+#include "garnet/lib/ui/gfx/resources/lights/ambient_light.h"
+#include "garnet/lib/ui/gfx/resources/lights/directional_light.h"
+#include "garnet/lib/ui/gfx/resources/memory.h"
+#include "garnet/lib/ui/gfx/resources/nodes/entity_node.h"
+#include "garnet/lib/ui/gfx/resources/nodes/node.h"
+#include "garnet/lib/ui/gfx/resources/nodes/opacity_node.h"
+#include "garnet/lib/ui/gfx/resources/nodes/scene.h"
+#include "garnet/lib/ui/gfx/resources/nodes/shape_node.h"
+#include "garnet/lib/ui/gfx/resources/renderers/renderer.h"
+#include "garnet/lib/ui/gfx/resources/shapes/circle_shape.h"
+#include "garnet/lib/ui/gfx/resources/shapes/mesh_shape.h"
+#include "garnet/lib/ui/gfx/resources/shapes/rectangle_shape.h"
+#include "garnet/lib/ui/gfx/resources/shapes/rounded_rectangle_shape.h"
+#include "garnet/lib/ui/gfx/resources/snapshot/snapshotter.h"
+#include "garnet/lib/ui/gfx/resources/stereo_camera.h"
+#include "garnet/lib/ui/gfx/resources/variable.h"
+#include "garnet/lib/ui/gfx/resources/view.h"
+#include "garnet/lib/ui/gfx/resources/view_holder.h"
+
+#include "garnet/lib/ui/gfx/engine/hit_tester.h"
+#include "garnet/lib/ui/gfx/engine/session_handler.h"
+#include "garnet/lib/ui/gfx/swapchain/swapchain_factory.h"
+#include "garnet/lib/ui/gfx/util/time.h"
+#include "garnet/lib/ui/gfx/util/unwrap.h"
+#include "garnet/lib/ui/gfx/util/wrap.h"
+#include "garnet/lib/ui/scenic/util/print_command.h"
+#include "lib/escher/hmd/pose_buffer.h"
+#include "lib/escher/renderer/batch_gpu_uploader.h"
+#include "lib/escher/shape/mesh.h"
+#include "lib/escher/shape/rounded_rect_factory.h"
+#include "lib/escher/util/type_utils.h"
+
+namespace scenic_impl {
+namespace gfx {
+
+namespace {
+
+// Makes it convenient to check that a value is constant and of a specific type,
+// or a variable.
+// TODO: There should also be a convenient way of type-checking a variable;
+// this will necessarily involve looking up the value in the ResourceMap.
+constexpr std::array<fuchsia::ui::gfx::Value::Tag, 2> kFloatValueTypes{
+    {fuchsia::ui::gfx::Value::Tag::kVector1,
+     fuchsia::ui::gfx::Value::Tag::kVariableId}};
+
+}  // anonymous namespace
+
+bool GfxCommandApplier::AssertValueIsOfType(
+    const fuchsia::ui::gfx::Value& value,
+    const fuchsia::ui::gfx::Value::Tag* tags, size_t tag_count,
+    Session* session) {
+  using ::operator<<;  // From print_commands.h
+  FXL_DCHECK(tag_count > 0);
+  for (size_t i = 0; i < tag_count; ++i) {
+    if (value.Which() == tags[i]) {
+      return true;
+    }
+  }
+  std::ostringstream str;
+  if (tag_count == 1) {
+    str << ", which is not the expected type: " << tags[0] << ".";
+  } else {
+    str << ", which is not one of the expected types (" << tags[0];
+    for (size_t i = 1; i < tag_count; ++i) {
+      str << ", " << tags[i];
+    }
+    str << ").";
+  }
+  session->error_reporter()->ERROR()
+      << "scenic_impl::gfx::Session: received value of type: " << value.Which()
+      << str.str();
+  return false;
+}
+
+bool GfxCommandApplier::ApplyCommand(Session* session,
+                                     CommandContext* command_context,
+                                     fuchsia::ui::gfx::Command command) {
+  TRACE_DURATION("gfx", "GfxCommandApplier::ApplyCommand");
+
+  switch (command.Which()) {
+    case fuchsia::ui::gfx::Command::Tag::kCreateResource:
+      return ApplyCreateResourceCmd(session, command_context,
+                                    std::move(command.create_resource()));
+    case fuchsia::ui::gfx::Command::Tag::kReleaseResource:
+      return ApplyReleaseResourceCmd(session,
+                                     std::move(command.release_resource()));
+    case fuchsia::ui::gfx::Command::Tag::kExportResource:
+      return ApplyExportResourceCmd(session,
+                                    std::move(command.export_resource()));
+    case fuchsia::ui::gfx::Command::Tag::kImportResource:
+      return ApplyImportResourceCmd(session,
+                                    std::move(command.import_resource()));
+    case fuchsia::ui::gfx::Command::Tag::kSetImportFocus: {
+      // TODO(SCN-1026): Remove this.
+      if (auto import = session->resources()->FindResource<Import>(
+              command.set_import_focus().id,
+              ResourceMap::ErrorBehavior::kDontReportErrors)) {
+        import->set_focusable(command.set_import_focus().focusable);
+        return true;
+      }
+      return false;
+    }
+    case fuchsia::ui::gfx::Command::Tag::kAddChild:
+      return ApplyAddChildCmd(session, std::move(command.add_child()));
+    case fuchsia::ui::gfx::Command::Tag::kAddPart:
+      return ApplyAddPartCmd(session, std::move(command.add_part()));
+    case fuchsia::ui::gfx::Command::Tag::kDetach:
+      return ApplyDetachCmd(session, std::move(command.detach()));
+    case fuchsia::ui::gfx::Command::Tag::kDetachChildren:
+      return ApplyDetachChildrenCmd(session,
+                                    std::move(command.detach_children()));
+    case fuchsia::ui::gfx::Command::Tag::kSetTag:
+      return ApplySetTagCmd(session, std::move(command.set_tag()));
+    case fuchsia::ui::gfx::Command::Tag::kSetTranslation:
+      return ApplySetTranslationCmd(session,
+                                    std::move(command.set_translation()));
+    case fuchsia::ui::gfx::Command::Tag::kSetScale:
+      return ApplySetScaleCmd(session, std::move(command.set_scale()));
+    case fuchsia::ui::gfx::Command::Tag::kSetRotation:
+      return ApplySetRotationCmd(session, std::move(command.set_rotation()));
+    case fuchsia::ui::gfx::Command::Tag::kSetAnchor:
+      return ApplySetAnchorCmd(session, std::move(command.set_anchor()));
+    case fuchsia::ui::gfx::Command::Tag::kSetSize:
+      return ApplySetSizeCmd(session, std::move(command.set_size()));
+    case fuchsia::ui::gfx::Command::Tag::kSetOpacity:
+      return ApplySetOpacityCmd(session, command.set_opacity());
+    case fuchsia::ui::gfx::Command::Tag::kSendSizeChangeHintHack:
+      return ApplySendSizeChangeHintCmd(session,
+                                        command.send_size_change_hint_hack());
+    case fuchsia::ui::gfx::Command::Tag::kSetShape:
+      return ApplySetShapeCmd(session, std::move(command.set_shape()));
+    case fuchsia::ui::gfx::Command::Tag::kSetMaterial:
+      return ApplySetMaterialCmd(session, std::move(command.set_material()));
+    case fuchsia::ui::gfx::Command::Tag::kSetClip:
+      return ApplySetClipCmd(session, std::move(command.set_clip()));
+    case fuchsia::ui::gfx::Command::Tag::kSetHitTestBehavior:
+      return ApplySetHitTestBehaviorCmd(
+          session, std::move(command.set_hit_test_behavior()));
+    case fuchsia::ui::gfx::Command::Tag::kSetViewProperties:
+      return ApplySetViewPropertiesCmd(
+          session, std::move(command.set_view_properties()));
+    case fuchsia::ui::gfx::Command::Tag::kSetCamera:
+      return ApplySetCameraCmd(session, std::move(command.set_camera()));
+    case fuchsia::ui::gfx::Command::Tag::kSetCameraTransform:
+      return ApplySetCameraTransformCmd(
+          session, std::move(command.set_camera_transform()));
+    case fuchsia::ui::gfx::Command::Tag::kSetCameraProjection:
+      return ApplySetCameraProjectionCmd(
+          session, std::move(command.set_camera_projection()));
+    case fuchsia::ui::gfx::Command::Tag::kSetStereoCameraProjection:
+      return ApplySetStereoCameraProjectionCmd(
+          session, std::move(command.set_stereo_camera_projection()));
+    case fuchsia::ui::gfx::Command::Tag::kSetCameraPoseBuffer:
+      return ApplySetCameraPoseBufferCmd(
+          session, std::move(command.set_camera_pose_buffer()));
+    case fuchsia::ui::gfx::Command::Tag::kSetLightColor:
+      return ApplySetLightColorCmd(session,
+                                   std::move(command.set_light_color()));
+    case fuchsia::ui::gfx::Command::Tag::kSetLightDirection:
+      return ApplySetLightDirectionCmd(
+          session, std::move(command.set_light_direction()));
+    case fuchsia::ui::gfx::Command::Tag::kAddLight:
+      return ApplyAddLightCmd(session, std::move(command.add_light()));
+    case fuchsia::ui::gfx::Command::Tag::kDetachLight:
+      return ApplyDetachLightCmd(session, std::move(command.detach_light()));
+    case fuchsia::ui::gfx::Command::Tag::kDetachLights:
+      return ApplyDetachLightsCmd(session, std::move(command.detach_lights()));
+    case fuchsia::ui::gfx::Command::Tag::kSetTexture:
+      return ApplySetTextureCmd(session, std::move(command.set_texture()));
+    case fuchsia::ui::gfx::Command::Tag::kSetColor:
+      return ApplySetColorCmd(session, std::move(command.set_color()));
+    case fuchsia::ui::gfx::Command::Tag::kBindMeshBuffers:
+      return ApplyBindMeshBuffersCmd(session,
+                                     std::move(command.bind_mesh_buffers()));
+    case fuchsia::ui::gfx::Command::Tag::kAddLayer:
+      return ApplyAddLayerCmd(session, std::move(command.add_layer()));
+    case fuchsia::ui::gfx::Command::Tag::kRemoveLayer:
+      return ApplyRemoveLayerCmd(session, std::move(command.remove_layer()));
+    case fuchsia::ui::gfx::Command::Tag::kRemoveAllLayers:
+      return ApplyRemoveAllLayersCmd(session,
+                                     std::move(command.remove_all_layers()));
+    case fuchsia::ui::gfx::Command::Tag::kSetLayerStack:
+      return ApplySetLayerStackCmd(session,
+                                   std::move(command.set_layer_stack()));
+    case fuchsia::ui::gfx::Command::Tag::kSetRenderer:
+      return ApplySetRendererCmd(session, std::move(command.set_renderer()));
+    case fuchsia::ui::gfx::Command::Tag::kSetRendererParam:
+      return ApplySetRendererParamCmd(session,
+                                      std::move(command.set_renderer_param()));
+    case fuchsia::ui::gfx::Command::Tag::kSetEventMask:
+      return ApplySetEventMaskCmd(session, std::move(command.set_event_mask()));
+    case fuchsia::ui::gfx::Command::Tag::kSetLabel:
+      return ApplySetLabelCmd(session, std::move(command.set_label()));
+    case fuchsia::ui::gfx::Command::Tag::kSetDisableClipping:
+      return ApplySetDisableClippingCmd(
+          session, std::move(command.set_disable_clipping()));
+    case fuchsia::ui::gfx::Command::Tag::kTakeSnapshotCmd:
+      return ApplyTakeSnapshotCmdHACK(session,
+                                      std::move(command.take_snapshot_cmd()));
+    case fuchsia::ui::gfx::Command::Tag::Invalid:
+      // FIDL validation should make this impossible.
+      FXL_CHECK(false);
+      return false;
+  }
+}
+
+bool GfxCommandApplier::ApplyCreateResourceCmd(
+    Session* session, CommandContext* command_context,
+    fuchsia::ui::gfx::CreateResourceCmd command) {
+  const ResourceId id = command.id;
+  if (id == 0) {
+    using ::operator<<;  // From print_commands.h
+    session->error_reporter()->ERROR()
+        << "scenic_impl::gfx::GfxCommandApplier::"
+           "ApplyCreateResourceCmd(): invalid ID: "
+        << command;
+    return false;
+  }
+
+  switch (command.resource.Which()) {
+    case fuchsia::ui::gfx::ResourceArgs::Tag::kMemory:
+      return ApplyCreateMemory(session, id,
+                               std::move(command.resource.memory()));
+    case fuchsia::ui::gfx::ResourceArgs::Tag::kImage:
+      return ApplyCreateImage(session, id, std::move(command.resource.image()));
+    case fuchsia::ui::gfx::ResourceArgs::Tag::kImagePipe:
+      return ApplyCreateImagePipe(session, id,
+                                  std::move(command.resource.image_pipe()));
+    case fuchsia::ui::gfx::ResourceArgs::Tag::kBuffer:
+      return ApplyCreateBuffer(session, id,
+                               std::move(command.resource.buffer()));
+    case fuchsia::ui::gfx::ResourceArgs::Tag::kScene:
+      return ApplyCreateScene(session, id, std::move(command.resource.scene()));
+    case fuchsia::ui::gfx::ResourceArgs::Tag::kCamera:
+      return ApplyCreateCamera(session, id,
+                               std::move(command.resource.camera()));
+    case fuchsia::ui::gfx::ResourceArgs::Tag::kStereoCamera:
+      return ApplyCreateStereoCamera(
+          session, id, std::move(command.resource.stereo_camera()));
+    case fuchsia::ui::gfx::ResourceArgs::Tag::kRenderer:
+      return ApplyCreateRenderer(session, id,
+                                 std::move(command.resource.renderer()));
+    case fuchsia::ui::gfx::ResourceArgs::Tag::kAmbientLight:
+      return ApplyCreateAmbientLight(
+          session, id, std::move(command.resource.ambient_light()));
+    case fuchsia::ui::gfx::ResourceArgs::Tag::kDirectionalLight:
+      return ApplyCreateDirectionalLight(
+          session, id, std::move(command.resource.directional_light()));
+    case fuchsia::ui::gfx::ResourceArgs::Tag::kRectangle:
+      return ApplyCreateRectangle(session, id,
+                                  std::move(command.resource.rectangle()));
+    case fuchsia::ui::gfx::ResourceArgs::Tag::kRoundedRectangle:
+      return ApplyCreateRoundedRectangle(
+          session, command_context, id,
+          std::move(command.resource.rounded_rectangle()));
+    case fuchsia::ui::gfx::ResourceArgs::Tag::kCircle:
+      return ApplyCreateCircle(session, id,
+                               std::move(command.resource.circle()));
+    case fuchsia::ui::gfx::ResourceArgs::Tag::kMesh:
+      return ApplyCreateMesh(session, id, std::move(command.resource.mesh()));
+    case fuchsia::ui::gfx::ResourceArgs::Tag::kMaterial:
+      return ApplyCreateMaterial(session, id,
+                                 std::move(command.resource.material()));
+    case fuchsia::ui::gfx::ResourceArgs::Tag::kView:
+      return ApplyCreateView(session, id, std::move(command.resource.view()));
+    case fuchsia::ui::gfx::ResourceArgs::Tag::kViewHolder:
+      return ApplyCreateViewHolder(session, id,
+                                   std::move(command.resource.view_holder()));
+    case fuchsia::ui::gfx::ResourceArgs::Tag::kClipNode:
+      return ApplyCreateClipNode(session, id,
+                                 std::move(command.resource.clip_node()));
+    case fuchsia::ui::gfx::ResourceArgs::Tag::kOpacityNode:
+      return ApplyCreateOpacityNode(session, id,
+                                    command.resource.opacity_node());
+    case fuchsia::ui::gfx::ResourceArgs::Tag::kEntityNode:
+      return ApplyCreateEntityNode(session, id,
+                                   std::move(command.resource.entity_node()));
+    case fuchsia::ui::gfx::ResourceArgs::Tag::kShapeNode:
+      return ApplyCreateShapeNode(session, id,
+                                  std::move(command.resource.shape_node()));
+    case fuchsia::ui::gfx::ResourceArgs::Tag::kCompositor:
+      return ApplyCreateCompositor(session, id,
+                                   std::move(command.resource.compositor()));
+    case fuchsia::ui::gfx::ResourceArgs::Tag::kDisplayCompositor:
+      return ApplyCreateDisplayCompositor(
+          session, id, std::move(command.resource.display_compositor()));
+    case fuchsia::ui::gfx::ResourceArgs::Tag::kImagePipeCompositor:
+      return ApplyCreateImagePipeCompositor(
+          session, id, std::move(command.resource.image_pipe_compositor()));
+    case fuchsia::ui::gfx::ResourceArgs::Tag::kLayerStack:
+      return ApplyCreateLayerStack(session, id,
+                                   std::move(command.resource.layer_stack()));
+    case fuchsia::ui::gfx::ResourceArgs::Tag::kLayer:
+      return ApplyCreateLayer(session, id, std::move(command.resource.layer()));
+    case fuchsia::ui::gfx::ResourceArgs::Tag::kVariable:
+      return ApplyCreateVariable(session, id,
+                                 std::move(command.resource.variable()));
+    case fuchsia::ui::gfx::ResourceArgs::Tag::Invalid:
+      // FIDL validation should make this impossible.
+      FXL_CHECK(false);
+      return false;
+  }
+}
+
+bool GfxCommandApplier::ApplyReleaseResourceCmd(
+    Session* session, fuchsia::ui::gfx::ReleaseResourceCmd command) {
+  return session->resources()->RemoveResource(command.id);
+}
+
+bool GfxCommandApplier::ApplyExportResourceCmd(
+    Session* session, fuchsia::ui::gfx::ExportResourceCmd command) {
+  if (!command.token) {
+    session->error_reporter()->ERROR()
+        << "scenic_impl::gfx::GfxCommandApplier::ApplyExportResourceCmd(): "
+           "no token provided.";
+    return false;
+  }
+  if (auto resource =
+          session->resources()->FindResource<Resource>(command.id)) {
+    return session->session_context().resource_linker->ExportResource(
+        resource.get(), std::move(command.token));
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplyImportResourceCmd(
+    Session* session, fuchsia::ui::gfx::ImportResourceCmd command) {
+  if (!command.token) {
+    session->error_reporter()->ERROR()
+        << "scenic_impl::gfx::GfxCommandApplier::ApplyImportResourceCmd(): "
+           "no token provided.";
+    return false;
+  }
+  auto import = fxl::MakeRefCounted<Import>(
+      session, command.id, command.spec,
+      session->session_context().resource_linker->GetWeakPtr());
+  return session->session_context().resource_linker->ImportResource(
+             import.get(), command.spec, std::move(command.token)) &&
+         session->resources()->AddResource(command.id, std::move(import));
+}
+
+bool GfxCommandApplier::ApplyAddChildCmd(
+    Session* session, fuchsia::ui::gfx::AddChildCmd command) {
+  // Find the parent and child nodes. We can add:
+  // - Nodes to Nodes
+  // - ViewHolders to Nodes
+  // - Nodes to Views
+  // TODO(SCN-795): Split these out into separate commands?
+  if (auto parent = session->resources()->FindResource<Node>(
+          command.node_id, ResourceMap::ErrorBehavior::kDontReportErrors)) {
+    if (auto child = session->resources()->FindResource<Node>(
+            command.child_id, ResourceMap::ErrorBehavior::kDontReportErrors)) {
+      return parent->AddChild(std::move(child));
+    } else if (auto child = session->resources()->FindResource<ViewHolder>(
+                   command.child_id)) {
+      return parent->AddViewHolder(std::move(child));
+    }
+  } else if (auto parent =
+                 session->resources()->FindResource<View>(command.node_id)) {
+    if (auto child =
+            session->resources()->FindResource<Node>(command.child_id)) {
+      return parent->AddChild(std::move(child));
+    }
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplyAddPartCmd(Session* session,
+                                        fuchsia::ui::gfx::AddPartCmd command) {
+  // Find the parent and part nodes.
+  if (auto parent_node =
+          session->resources()->FindResource<Node>(command.node_id)) {
+    if (auto part_node =
+            session->resources()->FindResource<Node>(command.part_id)) {
+      return parent_node->AddPart(std::move(part_node));
+    }
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplyTakeSnapshotCmdHACK(
+    Session* session, fuchsia::ui::gfx::TakeSnapshotCmdHACK command) {
+  async::PostTask(
+      async_get_default_dispatcher(),
+      [weak = session->GetWeakPtr(), command = std::move(command)]() mutable {
+        if (!weak) {
+          if (auto callback = command.callback.Bind()) {
+            // TODO(SCN-978): Return an error to the caller for invalid data.
+            callback->OnData(fuchsia::mem::Buffer{});
+          }
+          return;
+        }
+
+        const auto& context = weak->session_context();
+        Resource* resource = nullptr;
+        if (auto node =
+                weak->resources()->FindResource<Node>(command.node_id)) {
+          resource = node.get();
+        } else if (command.node_id == 0) {
+          // TODO(SCN-1170): get rid of SceneGraph::first_compositor().
+          const auto& first_compositor_weak =
+              context.scene_graph->first_compositor();
+          if (first_compositor_weak) {
+            resource = first_compositor_weak.get();
+          }
+        }
+
+        if (resource == nullptr) {
+          if (auto callback = command.callback.Bind()) {
+            callback->OnData(fuchsia::mem::Buffer{});
+          }
+          return;
+        }
+
+        auto gpu_uploader =
+            escher::BatchGpuUploader::New(context.escher->GetWeakPtr());
+        Snapshotter snapshotter(gpu_uploader);
+        // Take a snapshot and return the data in callback. The closure does
+        // not need the snapshotter instance and is invoked after the instance
+        // is destroyed.
+        snapshotter.TakeSnapshot(resource, [callback = command.callback.Bind()](
+                                               fuchsia::mem::Buffer snapshot) {
+          callback->OnData(std::move(snapshot));
+        });
+      });
+  return true;
+}
+
+bool GfxCommandApplier::ApplyDetachCmd(Session* session,
+                                       fuchsia::ui::gfx::DetachCmd command) {
+  if (auto resource =
+          session->resources()->FindResource<Resource>(command.id)) {
+    return resource->Detach();
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplyDetachChildrenCmd(
+    Session* session, fuchsia::ui::gfx::DetachChildrenCmd command) {
+  if (auto node = session->resources()->FindResource<Node>(command.node_id)) {
+    return node->DetachChildren();
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplySetTagCmd(Session* session,
+                                       fuchsia::ui::gfx::SetTagCmd command) {
+  if (auto node = session->resources()->FindResource<Node>(command.node_id)) {
+    return node->SetTagValue(command.tag_value);
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplySetTranslationCmd(
+    Session* session, fuchsia::ui::gfx::SetTranslationCmd command) {
+  if (auto node = session->resources()->FindResource<Node>(command.id)) {
+    if (IsVariable(command.value)) {
+      if (auto variable =
+              session->resources()->FindVariableResource<Vector3Variable>(
+                  command.value.variable_id)) {
+        return node->SetTranslation(variable);
+      }
+    } else {
+      return node->SetTranslation(UnwrapVector3(command.value));
+    }
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplySetScaleCmd(
+    Session* session, fuchsia::ui::gfx::SetScaleCmd command) {
+  if (auto node = session->resources()->FindResource<Node>(command.id)) {
+    if (IsVariable(command.value)) {
+      if (auto variable =
+              session->resources()->FindVariableResource<Vector3Variable>(
+                  command.value.variable_id)) {
+        return node->SetScale(variable);
+      }
+    } else {
+      return node->SetScale(UnwrapVector3(command.value));
+    }
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplySetRotationCmd(
+    Session* session, fuchsia::ui::gfx::SetRotationCmd command) {
+  if (auto node = session->resources()->FindResource<Node>(command.id)) {
+    if (IsVariable(command.value)) {
+      if (auto variable =
+              session->resources()->FindVariableResource<QuaternionVariable>(
+                  command.value.variable_id)) {
+        return node->SetRotation(variable);
+      }
+    } else {
+      return node->SetRotation(UnwrapQuaternion(command.value));
+    }
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplySetAnchorCmd(
+    Session* session, fuchsia::ui::gfx::SetAnchorCmd command) {
+  if (auto node = session->resources()->FindResource<Node>(command.id)) {
+    if (IsVariable(command.value)) {
+      if (auto variable =
+              session->resources()->FindVariableResource<Vector3Variable>(
+                  command.value.variable_id)) {
+        return node->SetAnchor(variable);
+      }
+    }
+    return node->SetAnchor(UnwrapVector3(command.value));
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplySetSizeCmd(Session* session,
+                                        fuchsia::ui::gfx::SetSizeCmd command) {
+  if (auto layer = session->resources()->FindResource<Layer>(command.id)) {
+    if (IsVariable(command.value)) {
+      session->error_reporter()->ERROR()
+          << "scenic_impl::gfx::GfxCommandApplier::ApplySetSizeCmd(): "
+             "unimplemented for variable value.";
+      return false;
+    }
+    return layer->SetSize(UnwrapVector2(command.value));
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplySetOpacityCmd(
+    Session* session, fuchsia::ui::gfx::SetOpacityCmd command) {
+  if (auto node =
+          session->resources()->FindResource<OpacityNode>(command.node_id)) {
+    node->SetOpacity(command.opacity);
+    return true;
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplySendSizeChangeHintCmd(
+    Session* session, fuchsia::ui::gfx::SendSizeChangeHintCmdHACK command) {
+  if (auto node = session->resources()->FindResource<Node>(command.node_id)) {
+    return node->SendSizeChangeHint(command.width_change_factor,
+                                    command.height_change_factor);
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplySetShapeCmd(
+    Session* session, fuchsia::ui::gfx::SetShapeCmd command) {
+  if (auto node =
+          session->resources()->FindResource<ShapeNode>(command.node_id)) {
+    if (auto shape =
+            session->resources()->FindResource<Shape>(command.shape_id)) {
+      node->SetShape(std::move(shape));
+      return true;
+    }
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplySetMaterialCmd(
+    Session* session, fuchsia::ui::gfx::SetMaterialCmd command) {
+  if (auto node =
+          session->resources()->FindResource<ShapeNode>(command.node_id)) {
+    if (auto material =
+            session->resources()->FindResource<Material>(command.material_id)) {
+      node->SetMaterial(std::move(material));
+      return true;
+    }
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplySetClipCmd(Session* session,
+                                        fuchsia::ui::gfx::SetClipCmd command) {
+  if (command.clip_id != 0) {
+    // TODO(SCN-167): Support non-zero clip_id.
+    session->error_reporter()->ERROR()
+        << "scenic_impl::gfx::GfxCommandApplier::ApplySetClipCmd(): only "
+           "clip_to_self is implemented.";
+    return false;
+  }
+
+  if (auto node = session->resources()->FindResource<Node>(command.node_id)) {
+    return node->SetClipToSelf(command.clip_to_self);
+  }
+
+  return false;
+}
+
+bool GfxCommandApplier::ApplySetHitTestBehaviorCmd(
+    Session* session, fuchsia::ui::gfx::SetHitTestBehaviorCmd command) {
+  if (auto node = session->resources()->FindResource<Node>(command.node_id)) {
+    return node->SetHitTestBehavior(command.hit_test_behavior);
+  }
+
+  return false;
+}
+
+bool GfxCommandApplier::ApplySetViewPropertiesCmd(
+    Session* session, fuchsia::ui::gfx::SetViewPropertiesCmd command) {
+  if (auto view_holder = session->resources()->FindResource<ViewHolder>(
+          command.view_holder_id)) {
+    view_holder->SetViewProperties(std::move(command.properties));
+    return true;
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplySetCameraCmd(
+    Session* session, fuchsia::ui::gfx::SetCameraCmd command) {
+  if (auto renderer =
+          session->resources()->FindResource<Renderer>(command.renderer_id)) {
+    if (command.camera_id == 0) {
+      renderer->SetCamera(nullptr);
+      return true;
+    } else if (auto camera = session->resources()->FindResource<Camera>(
+                   command.camera_id)) {
+      renderer->SetCamera(std::move(camera));
+      return true;
+    }
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplySetTextureCmd(
+    Session* session, fuchsia::ui::gfx::SetTextureCmd command) {
+  if (auto material =
+          session->resources()->FindResource<Material>(command.material_id)) {
+    if (command.texture_id == 0) {
+      material->SetTexture(nullptr);
+      return true;
+    } else if (auto image = session->resources()->FindResource<ImageBase>(
+                   command.texture_id)) {
+      material->SetTexture(std::move(image));
+      return true;
+    }
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplySetColorCmd(
+    Session* session, fuchsia::ui::gfx::SetColorCmd command) {
+  if (auto material =
+          session->resources()->FindResource<Material>(command.material_id)) {
+    if (IsVariable(command.color)) {
+      session->error_reporter()->ERROR()
+          << "scenic_impl::gfx::GfxCommandApplier::ApplySetColorCmd(): "
+             "unimplemented for variable color.";
+      return false;
+    }
+
+    auto& color = command.color.value;
+    float red = static_cast<float>(color.red) / 255.f;
+    float green = static_cast<float>(color.green) / 255.f;
+    float blue = static_cast<float>(color.blue) / 255.f;
+    float alpha = static_cast<float>(color.alpha) / 255.f;
+    material->SetColor(red, green, blue, alpha);
+    return true;
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplyBindMeshBuffersCmd(
+    Session* session, fuchsia::ui::gfx::BindMeshBuffersCmd command) {
+  auto mesh = session->resources()->FindResource<MeshShape>(command.mesh_id);
+  auto index_buffer =
+      session->resources()->FindResource<Buffer>(command.index_buffer_id);
+  auto vertex_buffer =
+      session->resources()->FindResource<Buffer>(command.vertex_buffer_id);
+  if (mesh && index_buffer && vertex_buffer) {
+    return mesh->BindBuffers(std::move(index_buffer), command.index_format,
+                             command.index_offset, command.index_count,
+                             std::move(vertex_buffer), command.vertex_format,
+                             command.vertex_offset, command.vertex_count,
+                             Unwrap(command.bounding_box));
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplyAddLayerCmd(
+    Session* session, fuchsia::ui::gfx::AddLayerCmd command) {
+  auto layer_stack =
+      session->resources()->FindResource<LayerStack>(command.layer_stack_id);
+  auto layer = session->resources()->FindResource<Layer>(command.layer_id);
+  if (layer_stack && layer) {
+    return layer_stack->AddLayer(std::move(layer));
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplyRemoveLayerCmd(
+    Session* session, fuchsia::ui::gfx::RemoveLayerCmd command) {
+  auto layer_stack =
+      session->resources()->FindResource<LayerStack>(command.layer_stack_id);
+  auto layer = session->resources()->FindResource<Layer>(command.layer_id);
+  if (layer_stack && layer) {
+    return layer_stack->RemoveLayer(std::move(layer));
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplyRemoveAllLayersCmd(
+    Session* session, fuchsia::ui::gfx::RemoveAllLayersCmd command) {
+  auto layer_stack =
+      session->resources()->FindResource<LayerStack>(command.layer_stack_id);
+  if (layer_stack) {
+    return layer_stack->RemoveAllLayers();
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplySetLayerStackCmd(
+    Session* session, fuchsia::ui::gfx::SetLayerStackCmd command) {
+  auto compositor =
+      session->resources()->FindResource<Compositor>(command.compositor_id);
+  auto layer_stack =
+      session->resources()->FindResource<LayerStack>(command.layer_stack_id);
+  if (compositor && layer_stack) {
+    return compositor->SetLayerStack(std::move(layer_stack));
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplySetRendererCmd(
+    Session* session, fuchsia::ui::gfx::SetRendererCmd command) {
+  auto layer = session->resources()->FindResource<Layer>(command.layer_id);
+  auto renderer =
+      session->resources()->FindResource<Renderer>(command.renderer_id);
+
+  if (layer && renderer) {
+    return layer->SetRenderer(std::move(renderer));
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplySetRendererParamCmd(
+    Session* session, fuchsia::ui::gfx::SetRendererParamCmd command) {
+  auto renderer =
+      session->resources()->FindResource<Renderer>(command.renderer_id);
+  if (renderer) {
+    switch (command.param.Which()) {
+      case fuchsia::ui::gfx::RendererParam::Tag::kShadowTechnique:
+        return renderer->SetShadowTechnique(command.param.shadow_technique());
+      case fuchsia::ui::gfx::RendererParam::Tag::kRenderFrequency:
+        // TODO(SCN-1169): SetRenderContinuously should only affect the
+        // compositor that has the renderer attached to it.
+        session->session_context().frame_scheduler->SetRenderContinuously(
+            command.param.render_frequency() ==
+            fuchsia::ui::gfx::RenderFrequency::CONTINUOUSLY);
+        return true;
+      case fuchsia::ui::gfx::RendererParam::Tag::Invalid:
+        session->error_reporter()->ERROR()
+            << "scenic_impl::gfx::GfxCommandApplier::"
+               "ApplySetRendererParamCmd(): "
+               "invalid param.";
+    }
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplySetEventMaskCmd(
+    Session* session, fuchsia::ui::gfx::SetEventMaskCmd command) {
+  if (auto r = session->resources()->FindResource<Resource>(command.id)) {
+    return r->SetEventMask(command.event_mask);
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplySetCameraTransformCmd(
+    Session* session, fuchsia::ui::gfx::SetCameraTransformCmd command) {
+  // TODO(SCN-123): support variables.
+  if (IsVariable(command.eye_position) || IsVariable(command.eye_look_at) ||
+      IsVariable(command.eye_up)) {
+    session->error_reporter()->ERROR()
+        << "scenic_impl::gfx::GfxCommandApplier::"
+           "ApplySetCameraTransformCmd(): "
+           "unimplemented: variable properties.";
+    return false;
+  } else if (auto camera = session->resources()->FindResource<Camera>(
+                 command.camera_id)) {
+    camera->SetTransform(UnwrapVector3(command.eye_position),
+                         UnwrapVector3(command.eye_look_at),
+                         UnwrapVector3(command.eye_up));
+    return true;
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplySetCameraProjectionCmd(
+    Session* session, fuchsia::ui::gfx::SetCameraProjectionCmd command) {
+  // TODO(SCN-123): support variables.
+  if (IsVariable(command.fovy)) {
+    session->error_reporter()->ERROR()
+        << "scenic_impl::gfx::GfxCommandApplier::"
+           "ApplySetCameraProjectionCmd(): "
+           "unimplemented: variable properties.";
+    return false;
+  } else if (auto camera = session->resources()->FindResource<Camera>(
+                 command.camera_id)) {
+    camera->SetProjection(UnwrapFloat(command.fovy));
+    return true;
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplySetStereoCameraProjectionCmd(
+    Session* session, fuchsia::ui::gfx::SetStereoCameraProjectionCmd command) {
+  if (IsVariable(command.left_projection) ||
+      IsVariable(command.right_projection)) {
+    session->error_reporter()->ERROR()
+        << "scenic_impl::gfx::GfxCommandApplier::"
+           "ApplySetStereoCameraProjectionOp(): "
+           "unimplemented: variable properties.";
+    return false;
+  } else if (auto stereo_camera =
+                 session->resources()->FindResource<StereoCamera>(
+                     command.camera_id)) {
+    stereo_camera->SetStereoProjection(Unwrap(command.left_projection.value),
+                                       Unwrap(command.right_projection.value));
+    return true;
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplySetCameraPoseBufferCmd(
+    Session* session, fuchsia::ui::gfx::SetCameraPoseBufferCmd command) {
+  if (command.base_time > dispatcher_clock_now()) {
+    session->error_reporter()->ERROR()
+        << "scenic_impl::gfx::GfxCommandApplier::"
+           "ApplySetCameraPoseBufferCmd(): "
+           "base time not in the past";
+    return false;
+  }
+
+  auto buffer = session->resources()->FindResource<Buffer>(command.buffer_id);
+  if (!buffer) {
+    session->error_reporter()->ERROR()
+        << "scenic_impl::gfx::GfxCommandApplier::"
+           "ApplySetCameraPoseBufferCmd(S): "
+           "invalid buffer ID";
+    return false;
+  }
+
+  if (command.num_entries < 1) {
+    session->error_reporter()->ERROR()
+        << "scenic_impl::gfx::GfxCommandApplier::"
+           "ApplySetCameraPoseBufferCmd(): "
+           "must have at least one entry in the pose buffer";
+    return false;
+  }
+
+  if (buffer->size() < command.num_entries * sizeof(escher::hmd::Pose)) {
+    session->error_reporter()->ERROR()
+        << "scenic_impl::gfx::GfxCommandApplier::"
+           "ApplySetCameraPoseBufferCmd(): "
+           "buffer is not large enough";
+    return false;
+  }
+
+  auto camera = session->resources()->FindResource<Camera>(command.camera_id);
+  if (!camera) {
+    session->error_reporter()->ERROR()
+        << "scenic_impl::gfx::GfxCommandApplier::"
+           "ApplySetCameraPoseBufferCmd(): "
+           "invalid camera ID";
+    return false;
+  }
+
+  camera->SetPoseBuffer(buffer, command.num_entries, command.base_time,
+                        command.time_interval);
+
+  return true;
+}
+
+bool GfxCommandApplier::ApplySetLightColorCmd(
+    Session* session, fuchsia::ui::gfx::SetLightColorCmd command) {
+  // TODO(SCN-123): support variables.
+  if (command.color.variable_id) {
+    session->error_reporter()->ERROR()
+        << "scenic_impl::gfx::GfxCommandApplier::ApplySetLightColorCmd(): "
+           "unimplemented: variable color.";
+    return false;
+  } else if (auto light =
+                 session->resources()->FindResource<Light>(command.light_id)) {
+    return light->SetColor(Unwrap(command.color.value));
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplySetLightDirectionCmd(
+    Session* session, fuchsia::ui::gfx::SetLightDirectionCmd command) {
+  // TODO(SCN-123): support variables.
+  if (command.direction.variable_id) {
+    session->error_reporter()->ERROR()
+        << "scenic_impl::gfx::GfxCommandApplier::"
+           "ApplySetLightDirectionCmd(): "
+           "unimplemented: variable direction.";
+    return false;
+  } else if (auto light = session->resources()->FindResource<DirectionalLight>(
+                 command.light_id)) {
+    return light->SetDirection(Unwrap(command.direction.value));
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplyAddLightCmd(
+    Session* session, fuchsia::ui::gfx::AddLightCmd command) {
+  if (auto scene =
+          session->resources()->FindResource<Scene>(command.scene_id)) {
+    if (auto light =
+            session->resources()->FindResource<Light>(command.light_id)) {
+      return scene->AddLight(std::move(light));
+    }
+  }
+
+  session->error_reporter()->ERROR() << "scenic_impl::gfx::GfxCommandApplier::"
+                                        "ApplyAddLightCmd(): unimplemented.";
+  return false;
+}
+
+bool GfxCommandApplier::ApplyDetachLightCmd(
+    Session* session, fuchsia::ui::gfx::DetachLightCmd command) {
+  session->error_reporter()->ERROR() << "scenic_impl::gfx::GfxCommandApplier::"
+                                        "ApplyDetachLightCmd(): unimplemented.";
+  return false;
+}
+
+bool GfxCommandApplier::ApplyDetachLightsCmd(
+    Session* session, fuchsia::ui::gfx::DetachLightsCmd command) {
+  session->error_reporter()->ERROR()
+      << "scenic_impl::gfx::GfxCommandApplier::"
+         "ApplyDetachLightsCmd(): unimplemented.";
+  return false;
+}
+
+bool GfxCommandApplier::ApplySetLabelCmd(
+    Session* session, fuchsia::ui::gfx::SetLabelCmd command) {
+  if (auto r = session->resources()->FindResource<Resource>(command.id)) {
+    return r->SetLabel(command.label);
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplySetDisableClippingCmd(
+    Session* session, fuchsia::ui::gfx::SetDisableClippingCmd command) {
+  if (auto r =
+          session->resources()->FindResource<Renderer>(command.renderer_id)) {
+    r->DisableClipping(command.disable_clipping);
+    return true;
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplyCreateMemory(Session* session, ResourceId id,
+                                          fuchsia::ui::gfx::MemoryArgs args) {
+  auto memory = CreateMemory(session, id, std::move(args));
+  return memory ? session->resources()->AddResource(id, std::move(memory))
+                : false;
+}
+
+bool GfxCommandApplier::ApplyCreateImage(Session* session, ResourceId id,
+                                         fuchsia::ui::gfx::ImageArgs args) {
+  if (auto memory =
+          session->resources()->FindResource<Memory>(args.memory_id)) {
+    if (auto image = CreateImage(session, id, std::move(memory), args)) {
+      return session->resources()->AddResource(id, std::move(image));
+    }
+  }
+
+  return false;
+}
+
+bool GfxCommandApplier::ApplyCreateImagePipe(
+    Session* session, ResourceId id, fuchsia::ui::gfx::ImagePipeArgs args) {
+  auto image_pipe = fxl::MakeRefCounted<ImagePipe>(
+      session, id, std::move(args.image_pipe_request),
+      session->session_context().update_scheduler);
+  return session->resources()->AddResource(id, image_pipe);
+}
+
+bool GfxCommandApplier::ApplyCreateBuffer(Session* session, ResourceId id,
+                                          fuchsia::ui::gfx::BufferArgs args) {
+  if (auto memory =
+          session->resources()->FindResource<Memory>(args.memory_id)) {
+    if (auto buffer = CreateBuffer(session, id, std::move(memory),
+                                   args.memory_offset, args.num_bytes)) {
+      return session->resources()->AddResource(id, std::move(buffer));
+    }
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplyCreateScene(Session* session, ResourceId id,
+                                         fuchsia::ui::gfx::SceneArgs args) {
+  auto scene = CreateScene(session, id, std::move(args));
+  return scene ? session->resources()->AddResource(id, std::move(scene))
+               : false;
+}
+
+bool GfxCommandApplier::ApplyCreateCamera(Session* session, ResourceId id,
+                                          fuchsia::ui::gfx::CameraArgs args) {
+  auto camera = CreateCamera(session, id, std::move(args));
+  return camera ? session->resources()->AddResource(id, std::move(camera))
+                : false;
+}
+
+bool GfxCommandApplier::ApplyCreateStereoCamera(
+    Session* session, ResourceId id, fuchsia::ui::gfx::StereoCameraArgs args) {
+  auto camera = CreateStereoCamera(session, id, args);
+  return camera ? session->resources()->AddResource(id, std::move(camera))
+                : false;
+}
+
+bool GfxCommandApplier::ApplyCreateRenderer(
+    Session* session, ResourceId id, fuchsia::ui::gfx::RendererArgs args) {
+  auto renderer = CreateRenderer(session, id, std::move(args));
+  return renderer ? session->resources()->AddResource(id, std::move(renderer))
+                  : false;
+}
+
+bool GfxCommandApplier::ApplyCreateAmbientLight(
+    Session* session, ResourceId id, fuchsia::ui::gfx::AmbientLightArgs args) {
+  auto light = CreateAmbientLight(session, id);
+  return light ? session->resources()->AddResource(id, std::move(light))
+               : false;
+}
+
+bool GfxCommandApplier::ApplyCreateDirectionalLight(
+    Session* session, ResourceId id,
+    fuchsia::ui::gfx::DirectionalLightArgs args) {
+  auto light = CreateDirectionalLight(session, id);
+  return light ? session->resources()->AddResource(id, std::move(light))
+               : false;
+}
+
+bool GfxCommandApplier::ApplyCreateRectangle(
+    Session* session, ResourceId id, fuchsia::ui::gfx::RectangleArgs args) {
+  if (!AssertValueIsOfType(args.width, kFloatValueTypes, session) ||
+      !AssertValueIsOfType(args.height, kFloatValueTypes, session)) {
+    return false;
+  }
+
+  // TODO(SCN-123): support variables.
+  if (IsVariable(args.width) || IsVariable(args.height)) {
+    session->error_reporter()->ERROR()
+        << "scenic_impl::gfx::GfxCommandApplier::ApplyCreateRectangle(): "
+           "unimplemented: variable width/height.";
+    return false;
+  }
+
+  auto rectangle =
+      CreateRectangle(session, id, args.width.vector1(), args.height.vector1());
+  return rectangle ? session->resources()->AddResource(id, std::move(rectangle))
+                   : false;
+}
+
+bool GfxCommandApplier::ApplyCreateRoundedRectangle(
+    Session* session, CommandContext* command_context, ResourceId id,
+    fuchsia::ui::gfx::RoundedRectangleArgs args) {
+  if (!AssertValueIsOfType(args.width, kFloatValueTypes, session) ||
+      !AssertValueIsOfType(args.height, kFloatValueTypes, session) ||
+      !AssertValueIsOfType(args.top_left_radius, kFloatValueTypes, session) ||
+      !AssertValueIsOfType(args.top_right_radius, kFloatValueTypes, session) ||
+      !AssertValueIsOfType(args.bottom_left_radius, kFloatValueTypes,
+                           session) ||
+      !AssertValueIsOfType(args.bottom_right_radius, kFloatValueTypes,
+                           session)) {
+    return false;
+  }
+
+  // TODO(SCN-123): support variables.
+  if (IsVariable(args.width) || IsVariable(args.height) ||
+      IsVariable(args.top_left_radius) || IsVariable(args.top_right_radius) ||
+      IsVariable(args.bottom_left_radius) ||
+      IsVariable(args.bottom_right_radius)) {
+    session->error_reporter()->ERROR()
+        << "scenic_impl::gfx::GfxCommandApplier::"
+           "ApplyCreateRoundedRectangle(): "
+           "unimplemented: variable width/height/radii.";
+    return false;
+  }
+
+  const float width = args.width.vector1();
+  const float height = args.height.vector1();
+  const float top_left_radius = args.top_left_radius.vector1();
+  const float top_right_radius = args.top_right_radius.vector1();
+  const float bottom_right_radius = args.bottom_right_radius.vector1();
+  const float bottom_left_radius = args.bottom_left_radius.vector1();
+
+  auto rectangle = CreateRoundedRectangle(
+      session, command_context, id, width, height, top_left_radius,
+      top_right_radius, bottom_right_radius, bottom_left_radius);
+  return rectangle ? session->resources()->AddResource(id, std::move(rectangle))
+                   : false;
+}
+
+bool GfxCommandApplier::ApplyCreateCircle(Session* session, ResourceId id,
+                                          fuchsia::ui::gfx::CircleArgs args) {
+  if (!AssertValueIsOfType(args.radius, kFloatValueTypes, session)) {
+    return false;
+  }
+
+  // TODO(SCN-123): support variables.
+  if (IsVariable(args.radius)) {
+    session->error_reporter()->ERROR()
+        << "scenic_impl::gfx::GfxCommandApplier::ApplyCreateCircle(): "
+           "unimplemented: variable radius.";
+    return false;
+  }
+
+  auto circle = CreateCircle(session, id, args.radius.vector1());
+  return circle ? session->resources()->AddResource(id, std::move(circle))
+                : false;
+}
+
+bool GfxCommandApplier::ApplyCreateMesh(Session* session, ResourceId id,
+                                        fuchsia::ui::gfx::MeshArgs args) {
+  auto mesh = CreateMesh(session, id);
+  return mesh ? session->resources()->AddResource(id, std::move(mesh)) : false;
+}
+
+bool GfxCommandApplier::ApplyCreateMaterial(
+    Session* session, ResourceId id, fuchsia::ui::gfx::MaterialArgs args) {
+  auto material = CreateMaterial(session, id);
+  return material ? session->resources()->AddResource(id, std::move(material))
+                  : false;
+}
+
+bool GfxCommandApplier::ApplyCreateView(Session* session, ResourceId id,
+                                        fuchsia::ui::gfx::ViewArgs args) {
+  // Sanity check.  We also rely on FIDL to enforce this for us, although it
+  // does not at the moment.
+  FXL_DCHECK(args.token) << "scenic_impl::gfx::GfxCommandApplier::"
+                            "ApplyCreateView(): no token provided.";
+
+  if (auto view = CreateView(session, id, std::move(args))) {
+    view->As<View>()->Connect();  // Initiate the link.
+    session->resources()->AddResource(id, std::move(view));
+    return true;
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplyCreateViewHolder(
+    Session* session, ResourceId id, fuchsia::ui::gfx::ViewHolderArgs args) {
+  // Sanity check.  We also rely on FIDL to enforce this for us, although it
+  // does not at the moment
+  FXL_DCHECK(args.token)
+      << "scenic_impl::gfx::GfxCommandApplier::ApplyCreateViewHolder()"
+         ": no token provided.";
+
+  if (auto view_holder = CreateViewHolder(session, id, std::move(args))) {
+    view_holder->As<ViewHolder>()->Connect();  // Initiate the link.
+    session->resources()->AddResource(id, std::move(view_holder));
+    return true;
+  }
+  return false;
+}
+
+bool GfxCommandApplier::ApplyCreateClipNode(
+    Session* session, ResourceId id, fuchsia::ui::gfx::ClipNodeArgs args) {
+  auto node = CreateClipNode(session, id, std::move(args));
+  return node ? session->resources()->AddResource(id, std::move(node)) : false;
+}
+
+bool GfxCommandApplier::ApplyCreateEntityNode(
+    Session* session, ResourceId id, fuchsia::ui::gfx::EntityNodeArgs args) {
+  auto node = CreateEntityNode(session, id, std::move(args));
+  return node ? session->resources()->AddResource(id, std::move(node)) : false;
+}
+
+bool GfxCommandApplier::ApplyCreateOpacityNode(
+    Session* session, ResourceId id, fuchsia::ui::gfx::OpacityNodeArgs args) {
+  auto node = CreateOpacityNode(session, id, args);
+  return node ? session->resources()->AddResource(id, std::move(node)) : false;
+}
+
+bool GfxCommandApplier::ApplyCreateShapeNode(
+    Session* session, ResourceId id, fuchsia::ui::gfx::ShapeNodeArgs args) {
+  auto node = CreateShapeNode(session, id, std::move(args));
+  return node ? session->resources()->AddResource(id, std::move(node)) : false;
+}
+
+bool GfxCommandApplier::ApplyCreateCompositor(
+    Session* session, ResourceId id, fuchsia::ui::gfx::CompositorArgs args) {
+  auto compositor = CreateCompositor(session, id, std::move(args));
+  return compositor
+             ? session->resources()->AddResource(id, std::move(compositor))
+             : false;
+}
+
+bool GfxCommandApplier::ApplyCreateDisplayCompositor(
+    Session* session, ResourceId id,
+    fuchsia::ui::gfx::DisplayCompositorArgs args) {
+  auto compositor = CreateDisplayCompositor(session, id, std::move(args));
+  return compositor
+             ? session->resources()->AddResource(id, std::move(compositor))
+             : false;
+}
+
+bool GfxCommandApplier::ApplyCreateImagePipeCompositor(
+    Session* session, ResourceId id,
+    fuchsia::ui::gfx::ImagePipeCompositorArgs args) {
+  auto compositor = CreateImagePipeCompositor(session, id, std::move(args));
+  return compositor
+             ? session->resources()->AddResource(id, std::move(compositor))
+             : false;
+}
+
+bool GfxCommandApplier::ApplyCreateLayerStack(
+    Session* session, ResourceId id, fuchsia::ui::gfx::LayerStackArgs args) {
+  auto layer_stack = CreateLayerStack(session, id, std::move(args));
+  return layer_stack
+             ? session->resources()->AddResource(id, std::move(layer_stack))
+             : false;
+}
+
+bool GfxCommandApplier::ApplyCreateLayer(Session* session, ResourceId id,
+                                         fuchsia::ui::gfx::LayerArgs args) {
+  auto layer = CreateLayer(session, id, std::move(args));
+  return layer ? session->resources()->AddResource(id, std::move(layer))
+               : false;
+}
+
+bool GfxCommandApplier::ApplyCreateVariable(
+    Session* session, ResourceId id, fuchsia::ui::gfx::VariableArgs args) {
+  auto variable = CreateVariable(session, id, std::move(args));
+  return variable ? session->resources()->AddResource(id, std::move(variable))
+                  : false;
+}
+
+ResourcePtr GfxCommandApplier::CreateMemory(Session* session, ResourceId id,
+                                            fuchsia::ui::gfx::MemoryArgs args) {
+  return Memory::New(session, id, std::move(args), session->error_reporter());
+}
+
+ResourcePtr GfxCommandApplier::CreateImage(Session* session, ResourceId id,
+                                           MemoryPtr memory,
+                                           fuchsia::ui::gfx::ImageArgs args) {
+  return Image::New(session, id, memory, args.info, args.memory_offset,
+                    session->error_reporter());
+}
+
+ResourcePtr GfxCommandApplier::CreateBuffer(Session* session, ResourceId id,
+                                            MemoryPtr memory,
+                                            uint32_t memory_offset,
+                                            uint32_t num_bytes) {
+  if (memory_offset + num_bytes > memory->size()) {
+    session->error_reporter()->ERROR()
+        << "scenic_impl::gfx::GfxCommandApplier::CreateBuffer(): "
+           "buffer does not fit within memory (buffer "
+           "offset: "
+        << memory_offset << ", buffer size: " << num_bytes
+        << ", memory size: " << memory->size() << ")";
+    return ResourcePtr();
+  }
+
+  // Make a pointer to a subregion of the memory, if necessary.
+  escher::GpuMemPtr gpu_mem =
+      (memory_offset > 0 || num_bytes < memory->size())
+          ? memory->GetGpuMem()->Suballocate(num_bytes, memory_offset)
+          : memory->GetGpuMem();
+
+  return fxl::MakeRefCounted<Buffer>(session, id, std::move(gpu_mem),
+                                     std::move(memory));
+}
+
+ResourcePtr GfxCommandApplier::CreateScene(Session* session, ResourceId id,
+                                           fuchsia::ui::gfx::SceneArgs args) {
+  return fxl::MakeRefCounted<Scene>(session, id);
+}
+
+ResourcePtr GfxCommandApplier::CreateCamera(Session* session, ResourceId id,
+                                            fuchsia::ui::gfx::CameraArgs args) {
+  if (auto scene = session->resources()->FindResource<Scene>(args.scene_id)) {
+    return fxl::MakeRefCounted<Camera>(session, id, std::move(scene));
+  }
+  return ResourcePtr();
+}
+
+ResourcePtr GfxCommandApplier::CreateStereoCamera(
+    Session* session, ResourceId id,
+    const fuchsia::ui::gfx::StereoCameraArgs args) {
+  if (auto scene = session->resources()->FindResource<Scene>(args.scene_id)) {
+    return fxl::MakeRefCounted<StereoCamera>(session, id, std::move(scene));
+  }
+  return ResourcePtr();
+}
+
+ResourcePtr GfxCommandApplier::CreateRenderer(
+    Session* session, ResourceId id, fuchsia::ui::gfx::RendererArgs args) {
+  return fxl::MakeRefCounted<Renderer>(session, id);
+}
+
+ResourcePtr GfxCommandApplier::CreateAmbientLight(Session* session,
+                                                  ResourceId id) {
+  return fxl::MakeRefCounted<AmbientLight>(session, id);
+}
+
+ResourcePtr GfxCommandApplier::CreateDirectionalLight(Session* session,
+                                                      ResourceId id) {
+  return fxl::MakeRefCounted<DirectionalLight>(session, id);
+}
+
+ResourcePtr GfxCommandApplier::CreateView(Session* session, ResourceId id,
+                                          fuchsia::ui::gfx::ViewArgs args) {
+  ViewLinker* view_linker = session->session_context().view_linker;
+  ViewLinker::ImportLink link = view_linker->CreateImport(
+      std::move(args.token), session->error_reporter());
+
+  // Create a View if the Link was successfully registered.
+  if (link.valid()) {
+    return fxl::MakeRefCounted<View>(session, id, std::move(link));
+  }
+  return nullptr;
+}
+
+ResourcePtr GfxCommandApplier::CreateViewHolder(
+    Session* session, ResourceId id, fuchsia::ui::gfx::ViewHolderArgs args) {
+  ViewLinker* view_linker = session->session_context().view_linker;
+  ViewLinker::ExportLink link = view_linker->CreateExport(
+      std::move(args.token), session->error_reporter());
+
+  // Create a ViewHolder if the Link was successfully registered.
+  if (link.valid()) {
+    return fxl::MakeRefCounted<ViewHolder>(session, id, std::move(link));
+  }
+  return nullptr;
+}
+
+ResourcePtr GfxCommandApplier::CreateClipNode(
+    Session* session, ResourceId id, fuchsia::ui::gfx::ClipNodeArgs args) {
+  session->error_reporter()->ERROR()
+      << "scenic_impl::gfx::GfxCommandApplier::CreateClipNode(): "
+         "unimplemented.";
+  return ResourcePtr();
+}
+
+ResourcePtr GfxCommandApplier::CreateEntityNode(
+    Session* session, ResourceId id, fuchsia::ui::gfx::EntityNodeArgs args) {
+  return fxl::MakeRefCounted<EntityNode>(session, id);
+}
+
+ResourcePtr GfxCommandApplier::CreateOpacityNode(
+    Session* session, ResourceId id, fuchsia::ui::gfx::OpacityNodeArgs args) {
+  return fxl::MakeRefCounted<OpacityNode>(session, id);
+}
+
+ResourcePtr GfxCommandApplier::CreateShapeNode(
+    Session* session, ResourceId id, fuchsia::ui::gfx::ShapeNodeArgs args) {
+  return fxl::MakeRefCounted<ShapeNode>(session, id);
+}
+
+ResourcePtr GfxCommandApplier::CreateCompositor(
+    Session* session, ResourceId id, fuchsia::ui::gfx::CompositorArgs args) {
+  return Compositor::New(session, id, session->session_context().scene_graph);
+}
+
+ResourcePtr GfxCommandApplier::CreateDisplayCompositor(
+    Session* session, ResourceId id,
+    fuchsia::ui::gfx::DisplayCompositorArgs args) {
+  Display* display =
+      session->session_context().display_manager->default_display();
+  if (!display) {
+    session->error_reporter()->ERROR()
+        << "There is no default display available.";
+    return nullptr;
+  }
+
+  if (display->is_claimed()) {
+    session->error_reporter()->ERROR()
+        << "The default display has already been claimed "
+           "by another compositor.";
+    return nullptr;
+  }
+
+  return fxl::AdoptRef(new DisplayCompositor(
+      session, id, session->session_context().scene_graph, display,
+      SwapchainFactory::CreateDisplaySwapchain(
+          display, session->session_context().display_manager,
+          session->session_context().event_timestamper,
+          session->session_context().escher)));
+}
+
+ResourcePtr GfxCommandApplier::CreateImagePipeCompositor(
+    Session* session, ResourceId id,
+    fuchsia::ui::gfx::ImagePipeCompositorArgs args) {
+  // TODO(SCN-179)
+  session->error_reporter()->ERROR() << "scenic_impl::gfx::GfxCommandApplier::"
+                                        "ApplyCreateImagePipeCompositor() "
+                                        "is unimplemented (SCN-179)";
+  return ResourcePtr();
+}
+
+ResourcePtr GfxCommandApplier::CreateLayerStack(
+    Session* session, ResourceId id, fuchsia::ui::gfx::LayerStackArgs args) {
+  return fxl::MakeRefCounted<LayerStack>(session, id);
+}
+
+ResourcePtr GfxCommandApplier::CreateVariable(
+    Session* session, ResourceId id, fuchsia::ui::gfx::VariableArgs args) {
+  fxl::RefPtr<Variable> variable;
+  switch (args.type) {
+    case fuchsia::ui::gfx::ValueType::kVector1:
+      variable = fxl::MakeRefCounted<FloatVariable>(session, id);
+      break;
+    case fuchsia::ui::gfx::ValueType::kVector2:
+      variable = fxl::MakeRefCounted<Vector2Variable>(session, id);
+      break;
+    case fuchsia::ui::gfx::ValueType::kVector3:
+      variable = fxl::MakeRefCounted<Vector3Variable>(session, id);
+      break;
+    case fuchsia::ui::gfx::ValueType::kVector4:
+      variable = fxl::MakeRefCounted<Vector4Variable>(session, id);
+      break;
+    case fuchsia::ui::gfx::ValueType::kMatrix4:
+      variable = fxl::MakeRefCounted<Matrix4x4Variable>(session, id);
+      break;
+    case fuchsia::ui::gfx::ValueType::kColorRgb:
+      // not yet supported
+      variable = nullptr;
+      break;
+    case fuchsia::ui::gfx::ValueType::kColorRgba:
+      // not yet supported
+      variable = nullptr;
+      break;
+    case fuchsia::ui::gfx::ValueType::kQuaternion:
+      variable = fxl::MakeRefCounted<QuaternionVariable>(session, id);
+      break;
+    case fuchsia::ui::gfx::ValueType::kFactoredTransform:
+      /* variable = fxl::MakeRefCounted<TransformVariable>(session, id); */
+      variable = nullptr;
+      break;
+    case fuchsia::ui::gfx::ValueType::kNone:
+      break;
+  }
+  if (variable && variable->SetValue(args.initial_value)) {
+    return variable;
+  }
+  return nullptr;
+}
+
+ResourcePtr GfxCommandApplier::CreateLayer(Session* session, ResourceId id,
+                                           fuchsia::ui::gfx::LayerArgs args) {
+  return fxl::MakeRefCounted<Layer>(session, id);
+}
+
+ResourcePtr GfxCommandApplier::CreateCircle(Session* session, ResourceId id,
+                                            float initial_radius) {
+  return fxl::MakeRefCounted<CircleShape>(session, id, initial_radius);
+}
+
+ResourcePtr GfxCommandApplier::CreateRectangle(Session* session, ResourceId id,
+                                               float width, float height) {
+  return fxl::MakeRefCounted<RectangleShape>(session, id, width, height);
+}
+
+ResourcePtr GfxCommandApplier::CreateRoundedRectangle(
+    Session* session, CommandContext* command_context, ResourceId id,
+    float width, float height, float top_left_radius, float top_right_radius,
+    float bottom_right_radius, float bottom_left_radius) {
+  auto factory = session->session_context().escher_rounded_rect_factory;
+  if (!factory) {
+    session->error_reporter()->ERROR()
+        << "scenic_impl::gfx::GfxCommandApplier::CreateRoundedRectangle(): "
+           "no RoundedRectFactory available.";
+    return ResourcePtr();
+  }
+
+  // If radii sum exceeds width or height, scale them down.
+  float top_radii_sum = top_left_radius + top_right_radius;
+  float top_scale = std::min(width / top_radii_sum, 1.f);
+
+  float bottom_radii_sum = bottom_left_radius + bottom_right_radius;
+  float bottom_scale = std::min(width / bottom_radii_sum, 1.f);
+
+  float left_radii_sum = top_left_radius + bottom_left_radius;
+  float left_scale = std::min(height / left_radii_sum, 1.f);
+
+  float right_radii_sum = top_right_radius + bottom_right_radius;
+  float right_scale = std::min(height / right_radii_sum, 1.f);
+
+  top_left_radius *= std::min(top_scale, left_scale);
+  top_right_radius *= std::min(top_scale, right_scale);
+  bottom_left_radius *= std::min(bottom_scale, left_scale);
+  bottom_right_radius *= std::min(bottom_scale, right_scale);
+
+  escher::RoundedRectSpec rect_spec(width, height, top_left_radius,
+                                    top_right_radius, bottom_right_radius,
+                                    bottom_left_radius);
+  escher::MeshSpec mesh_spec{escher::MeshAttribute::kPosition2D |
+                             escher::MeshAttribute::kUV};
+
+  return fxl::MakeRefCounted<RoundedRectangleShape>(
+      session, id, rect_spec,
+      factory->NewRoundedRect(rect_spec, mesh_spec,
+                              command_context->batch_gpu_uploader()));
+}
+
+ResourcePtr GfxCommandApplier::CreateMesh(Session* session, ResourceId id) {
+  return fxl::MakeRefCounted<MeshShape>(session, id);
+}
+
+ResourcePtr GfxCommandApplier::CreateMaterial(Session* session, ResourceId id) {
+  return fxl::MakeRefCounted<Material>(session, id);
+}
+
+}  // namespace gfx
+}  // namespace scenic_impl
diff --git a/lib/ui/gfx/engine/gfx_command_applier.h b/lib/ui/gfx/engine/gfx_command_applier.h
new file mode 100644
index 0000000..a3cdfed
--- /dev/null
+++ b/lib/ui/gfx/engine/gfx_command_applier.h
@@ -0,0 +1,281 @@
+// Copyright 2019 The Fuchsia 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 GARNET_LIB_UI_GFX_ENGINE_GFX_COMMAND_APPLIER_H_
+#define GARNET_LIB_UI_GFX_ENGINE_GFX_COMMAND_APPLIER_H_
+
+#include "garnet/lib/ui/gfx/engine/resource_map.h"
+#include "garnet/lib/ui/gfx/engine/session_context.h"
+#include "garnet/lib/ui/gfx/id.h"
+#include "garnet/lib/ui/gfx/resources/import.h"
+#include "garnet/lib/ui/gfx/resources/memory.h"
+#include "garnet/lib/ui/gfx/resources/resource.h"
+#include "garnet/lib/ui/gfx/resources/resource_context.h"
+#include "garnet/lib/ui/scenic/event_reporter.h"
+#include "garnet/lib/ui/scenic/util/error_reporter.h"
+#include "garnet/lib/ui/scenic/util/print_command.h"
+#include "lib/escher/flib/fence_set_listener.h"
+#include "lib/fxl/memory/weak_ptr.h"
+#include "lib/fxl/tasks/task_runner.h"
+
+namespace scenic_impl {
+namespace gfx {
+
+class Image;
+using ImagePtr = fxl::RefPtr<Image>;
+
+class ImageBase;
+using ImageBasePtr = fxl::RefPtr<ImageBase>;
+
+class ImagePipe;
+using ImagePipePtr = fxl::RefPtr<ImagePipe>;
+
+class CommandContext;
+class Session;
+
+// Responsible for applying gfx commands to sessions.
+// Does not own any state. The session to be modified is instead
+// passed in as an argument to ApplyCommand.
+class GfxCommandApplier {
+ public:
+  // Apply the operation to the current session state.  Return true if
+  // successful, and false if the op is somehow invalid.  In the latter case,
+  // the Session is left unchanged.
+  static bool ApplyCommand(Session* session, CommandContext* command_context,
+                           fuchsia::ui::gfx::Command command);
+
+ private:
+  // Return false and logs an error to the session's ErrorReporter if the value
+  // is not of the expected type.
+  // NOTE: although failure does not halt execution of the program,
+  // it does indicate client error, and will be used by the caller
+  // to tear down the Session.
+  static bool AssertValueIsOfType(const fuchsia::ui::gfx::Value& value,
+                                  const fuchsia::ui::gfx::Value::Tag* tags,
+                                  size_t tag_count, Session* session);
+  template <size_t N>
+  static bool AssertValueIsOfType(
+      const fuchsia::ui::gfx::Value& value,
+      const std::array<fuchsia::ui::gfx::Value::Tag, N>& tags,
+      Session* session) {
+    return AssertValueIsOfType(value, tags.data(), N, session);
+  }
+
+  // Functions for applying specific Commands. Called by ApplyCommand().
+  static bool ApplyCreateResourceCmd(
+      Session* session, CommandContext* command_context,
+      fuchsia::ui::gfx::CreateResourceCmd command);
+  static bool ApplyReleaseResourceCmd(
+      Session* session, fuchsia::ui::gfx::ReleaseResourceCmd command);
+  static bool ApplyExportResourceCmd(
+      Session* session, fuchsia::ui::gfx::ExportResourceCmd command);
+  static bool ApplyImportResourceCmd(
+      Session* session, fuchsia::ui::gfx::ImportResourceCmd command);
+  static bool ApplyAddChildCmd(Session* session,
+                               fuchsia::ui::gfx::AddChildCmd command);
+  static bool ApplyAddPartCmd(Session* session,
+                              fuchsia::ui::gfx::AddPartCmd command);
+  static bool ApplyDetachCmd(Session* session,
+                             fuchsia::ui::gfx::DetachCmd command);
+  static bool ApplyDetachChildrenCmd(
+      Session* session, fuchsia::ui::gfx::DetachChildrenCmd command);
+  static bool ApplySetTagCmd(Session* session,
+                             fuchsia::ui::gfx::SetTagCmd command);
+  static bool ApplySetTranslationCmd(
+      Session* session, fuchsia::ui::gfx::SetTranslationCmd command);
+  static bool ApplySetScaleCmd(Session* session,
+                               fuchsia::ui::gfx::SetScaleCmd command);
+  static bool ApplySetRotationCmd(Session* session,
+                                  fuchsia::ui::gfx::SetRotationCmd command);
+  static bool ApplySetAnchorCmd(Session* session,
+                                fuchsia::ui::gfx::SetAnchorCmd command);
+  static bool ApplySetSizeCmd(Session* session,
+                              fuchsia::ui::gfx::SetSizeCmd command);
+  static bool ApplySetOpacityCmd(Session* session,
+                                 fuchsia::ui::gfx::SetOpacityCmd command);
+  static bool ApplySendSizeChangeHintCmd(
+      Session* session, fuchsia::ui::gfx::SendSizeChangeHintCmdHACK command);
+  static bool ApplySetShapeCmd(Session* session,
+                               fuchsia::ui::gfx::SetShapeCmd command);
+  static bool ApplySetMaterialCmd(Session* session,
+                                  fuchsia::ui::gfx::SetMaterialCmd command);
+  static bool ApplySetClipCmd(Session* session,
+                              fuchsia::ui::gfx::SetClipCmd command);
+  static bool ApplySetViewPropertiesCmd(
+      Session* session, fuchsia::ui::gfx::SetViewPropertiesCmd command);
+  static bool ApplySetHitTestBehaviorCmd(
+      Session* session, fuchsia::ui::gfx::SetHitTestBehaviorCmd command);
+  static bool ApplySetCameraCmd(Session* session,
+                                fuchsia::ui::gfx::SetCameraCmd command);
+  static bool ApplySetCameraTransformCmd(
+      Session* session, fuchsia::ui::gfx::SetCameraTransformCmd command);
+  static bool ApplySetCameraProjectionCmd(
+      Session* session, fuchsia::ui::gfx::SetCameraProjectionCmd command);
+  static bool ApplySetStereoCameraProjectionCmd(
+      Session* session, fuchsia::ui::gfx::SetStereoCameraProjectionCmd command);
+  static bool ApplySetCameraPoseBufferCmd(
+      Session* session, fuchsia::ui::gfx::SetCameraPoseBufferCmd command);
+  static bool ApplySetLightColorCmd(Session* session,
+                                    fuchsia::ui::gfx::SetLightColorCmd command);
+  static bool ApplySetLightDirectionCmd(
+      Session* session, fuchsia::ui::gfx::SetLightDirectionCmd command);
+  static bool ApplyAddLightCmd(Session* session,
+                               fuchsia::ui::gfx::AddLightCmd command);
+  static bool ApplyDetachLightCmd(Session* session,
+                                  fuchsia::ui::gfx::DetachLightCmd command);
+  static bool ApplyDetachLightsCmd(Session* session,
+                                   fuchsia::ui::gfx::DetachLightsCmd command);
+  static bool ApplySetTextureCmd(Session* session,
+                                 fuchsia::ui::gfx::SetTextureCmd command);
+  static bool ApplySetColorCmd(Session* session,
+                               fuchsia::ui::gfx::SetColorCmd command);
+  static bool ApplyBindMeshBuffersCmd(
+      Session* session, fuchsia::ui::gfx::BindMeshBuffersCmd command);
+  static bool ApplyAddLayerCmd(Session* session,
+                               fuchsia::ui::gfx::AddLayerCmd command);
+  static bool ApplyRemoveLayerCmd(Session* session,
+                                  fuchsia::ui::gfx::RemoveLayerCmd command);
+  static bool ApplyRemoveAllLayersCmd(
+      Session* session, fuchsia::ui::gfx::RemoveAllLayersCmd command);
+  static bool ApplySetLayerStackCmd(Session* session,
+                                    fuchsia::ui::gfx::SetLayerStackCmd command);
+  static bool ApplySetRendererCmd(Session* session,
+                                  fuchsia::ui::gfx::SetRendererCmd command);
+  static bool ApplySetRendererParamCmd(
+      Session* session, fuchsia::ui::gfx::SetRendererParamCmd command);
+  static bool ApplySetEventMaskCmd(Session* session,
+                                   fuchsia::ui::gfx::SetEventMaskCmd command);
+  static bool ApplySetLabelCmd(Session* session,
+                               fuchsia::ui::gfx::SetLabelCmd command);
+  static bool ApplySetDisableClippingCmd(
+      Session* session, fuchsia::ui::gfx::SetDisableClippingCmd command);
+
+  // Resource creation functions, called by ApplyCreateResourceCmd(Session*
+  // session, ).
+  static bool ApplyCreateMemory(Session* session, ResourceId id,
+                                fuchsia::ui::gfx::MemoryArgs args);
+  static bool ApplyCreateImage(Session* session, ResourceId id,
+                               fuchsia::ui::gfx::ImageArgs args);
+  static bool ApplyCreateImagePipe(Session* session, ResourceId id,
+                                   fuchsia::ui::gfx::ImagePipeArgs args);
+  static bool ApplyCreateBuffer(Session* session, ResourceId id,
+                                fuchsia::ui::gfx::BufferArgs args);
+  static bool ApplyCreateScene(Session* session, ResourceId id,
+                               fuchsia::ui::gfx::SceneArgs args);
+  static bool ApplyCreateCamera(Session* session, ResourceId id,
+                                fuchsia::ui::gfx::CameraArgs args);
+  static bool ApplyCreateStereoCamera(Session* session, ResourceId id,
+                                      fuchsia::ui::gfx::StereoCameraArgs args);
+  static bool ApplyCreateRenderer(Session* session, ResourceId id,
+                                  fuchsia::ui::gfx::RendererArgs args);
+  static bool ApplyCreateAmbientLight(Session* session, ResourceId id,
+                                      fuchsia::ui::gfx::AmbientLightArgs args);
+  static bool ApplyCreateDirectionalLight(
+      Session* session, ResourceId id,
+      fuchsia::ui::gfx::DirectionalLightArgs args);
+  static bool ApplyCreateRectangle(Session* session, ResourceId id,
+                                   fuchsia::ui::gfx::RectangleArgs args);
+  static bool ApplyCreateRoundedRectangle(
+      Session* session, CommandContext* command_context, ResourceId id,
+      fuchsia::ui::gfx::RoundedRectangleArgs args);
+  static bool ApplyCreateCircle(Session* session, ResourceId id,
+                                fuchsia::ui::gfx::CircleArgs args);
+  static bool ApplyCreateMesh(Session* session, ResourceId id,
+                              fuchsia::ui::gfx::MeshArgs args);
+  static bool ApplyCreateMaterial(Session* session, ResourceId id,
+                                  fuchsia::ui::gfx::MaterialArgs args);
+  static bool ApplyCreateView(Session* session, ResourceId id,
+                              fuchsia::ui::gfx::ViewArgs args);
+  static bool ApplyCreateViewHolder(Session* session, ResourceId id,
+                                    fuchsia::ui::gfx::ViewHolderArgs args);
+  static bool ApplyCreateClipNode(Session* session, ResourceId id,
+                                  fuchsia::ui::gfx::ClipNodeArgs args);
+  static bool ApplyCreateEntityNode(Session* session, ResourceId id,
+                                    fuchsia::ui::gfx::EntityNodeArgs args);
+  static bool ApplyCreateOpacityNode(Session* session, ResourceId id,
+                                     fuchsia::ui::gfx::OpacityNodeArgs args);
+  static bool ApplyCreateShapeNode(Session* session, ResourceId id,
+                                   fuchsia::ui::gfx::ShapeNodeArgs args);
+  static bool ApplyCreateCompositor(Session* session, ResourceId id,
+                                    fuchsia::ui::gfx::CompositorArgs args);
+  static bool ApplyCreateDisplayCompositor(
+      Session* session, ResourceId id,
+      fuchsia::ui::gfx::DisplayCompositorArgs args);
+  static bool ApplyCreateImagePipeCompositor(
+      Session* session, ResourceId id,
+      fuchsia::ui::gfx::ImagePipeCompositorArgs args);
+  static bool ApplyCreateLayerStack(Session* session, ResourceId id,
+                                    fuchsia::ui::gfx::LayerStackArgs args);
+  static bool ApplyCreateLayer(Session* session, ResourceId id,
+                               fuchsia::ui::gfx::LayerArgs args);
+  static bool ApplyCreateVariable(Session* session, ResourceId id,
+                                  fuchsia::ui::gfx::VariableArgs args);
+  static bool ApplyTakeSnapshotCmdHACK(
+      Session* session, fuchsia::ui::gfx::TakeSnapshotCmdHACK command);
+
+  // Actually create resources.
+  static ResourcePtr CreateMemory(Session* session, ResourceId id,
+                                  fuchsia::ui::gfx::MemoryArgs args);
+  static ResourcePtr CreateImage(Session* session, ResourceId id,
+                                 MemoryPtr memory,
+                                 fuchsia::ui::gfx::ImageArgs args);
+  static ResourcePtr CreateBuffer(Session* session, ResourceId id,
+                                  MemoryPtr memory, uint32_t memory_offset,
+                                  uint32_t num_bytes);
+
+  static ResourcePtr CreateScene(Session* session, ResourceId id,
+                                 fuchsia::ui::gfx::SceneArgs args);
+  static ResourcePtr CreateCamera(Session* session, ResourceId id,
+                                  fuchsia::ui::gfx::CameraArgs args);
+  static ResourcePtr CreateStereoCamera(
+      Session* session, ResourceId id, fuchsia::ui::gfx::StereoCameraArgs args);
+  static ResourcePtr CreateRenderer(Session* session, ResourceId id,
+                                    fuchsia::ui::gfx::RendererArgs args);
+
+  static ResourcePtr CreateAmbientLight(Session* session, ResourceId id);
+  static ResourcePtr CreateDirectionalLight(Session* session, ResourceId id);
+
+  static ResourcePtr CreateView(Session* session, ResourceId id,
+                                fuchsia::ui::gfx::ViewArgs args);
+  static ResourcePtr CreateViewHolder(Session* session, ResourceId id,
+                                      fuchsia::ui::gfx::ViewHolderArgs args);
+  static ResourcePtr CreateClipNode(Session* session, ResourceId id,
+                                    fuchsia::ui::gfx::ClipNodeArgs args);
+  static ResourcePtr CreateEntityNode(Session* session, ResourceId id,
+                                      fuchsia::ui::gfx::EntityNodeArgs args);
+  static ResourcePtr CreateOpacityNode(Session* session, ResourceId id,
+                                       fuchsia::ui::gfx::OpacityNodeArgs args);
+  static ResourcePtr CreateShapeNode(Session* session, ResourceId id,
+                                     fuchsia::ui::gfx::ShapeNodeArgs args);
+
+  static ResourcePtr CreateCompositor(Session* session, ResourceId id,
+                                      fuchsia::ui::gfx::CompositorArgs args);
+  static ResourcePtr CreateDisplayCompositor(
+      Session* session, ResourceId id,
+      fuchsia::ui::gfx::DisplayCompositorArgs args);
+  static ResourcePtr CreateImagePipeCompositor(
+      Session* session, ResourceId id,
+      fuchsia::ui::gfx::ImagePipeCompositorArgs args);
+  static ResourcePtr CreateLayerStack(Session* session, ResourceId id,
+                                      fuchsia::ui::gfx::LayerStackArgs args);
+  static ResourcePtr CreateLayer(Session* session, ResourceId id,
+                                 fuchsia::ui::gfx::LayerArgs args);
+  static ResourcePtr CreateCircle(Session* session, ResourceId id,
+                                  float initial_radius);
+  static ResourcePtr CreateRectangle(Session* session, ResourceId id,
+                                     float width, float height);
+  static ResourcePtr CreateRoundedRectangle(
+      Session* session, CommandContext* command_context, ResourceId id,
+      float width, float height, float top_left_radius, float top_right_radius,
+      float bottom_right_radius, float bottom_left_radius);
+  static ResourcePtr CreateMesh(Session* session, ResourceId id);
+  static ResourcePtr CreateMaterial(Session* session, ResourceId id);
+  static ResourcePtr CreateVariable(Session* session, ResourceId id,
+                                    fuchsia::ui::gfx::VariableArgs args);
+};
+
+}  // namespace gfx
+}  // namespace scenic_impl
+
+#endif  // GARNET_LIB_UI_GFX_ENGINE_GFX_COMMAND_APPLIER_H_
diff --git a/lib/ui/gfx/engine/session.cc b/lib/ui/gfx/engine/session.cc
index 6db1eef..02db4d8 100644
--- a/lib/ui/gfx/engine/session.cc
+++ b/lib/ui/gfx/engine/session.cc
@@ -11,34 +11,11 @@
 #include <lib/async/default.h>
 #include <trace/event.h>
 
+#include "garnet/lib/ui/gfx/engine/gfx_command_applier.h"
 #include "garnet/lib/ui/gfx/engine/hit_tester.h"
 #include "garnet/lib/ui/gfx/engine/session_handler.h"
-#include "garnet/lib/ui/gfx/resources/buffer.h"
-#include "garnet/lib/ui/gfx/resources/camera.h"
-#include "garnet/lib/ui/gfx/resources/compositor/display_compositor.h"
-#include "garnet/lib/ui/gfx/resources/compositor/layer.h"
 #include "garnet/lib/ui/gfx/resources/compositor/layer_stack.h"
-#include "garnet/lib/ui/gfx/resources/image.h"
 #include "garnet/lib/ui/gfx/resources/image_pipe.h"
-#include "garnet/lib/ui/gfx/resources/image_pipe_handler.h"
-#include "garnet/lib/ui/gfx/resources/lights/ambient_light.h"
-#include "garnet/lib/ui/gfx/resources/lights/directional_light.h"
-#include "garnet/lib/ui/gfx/resources/memory.h"
-#include "garnet/lib/ui/gfx/resources/nodes/entity_node.h"
-#include "garnet/lib/ui/gfx/resources/nodes/node.h"
-#include "garnet/lib/ui/gfx/resources/nodes/opacity_node.h"
-#include "garnet/lib/ui/gfx/resources/nodes/scene.h"
-#include "garnet/lib/ui/gfx/resources/nodes/shape_node.h"
-#include "garnet/lib/ui/gfx/resources/renderers/renderer.h"
-#include "garnet/lib/ui/gfx/resources/shapes/circle_shape.h"
-#include "garnet/lib/ui/gfx/resources/shapes/mesh_shape.h"
-#include "garnet/lib/ui/gfx/resources/shapes/rectangle_shape.h"
-#include "garnet/lib/ui/gfx/resources/shapes/rounded_rectangle_shape.h"
-#include "garnet/lib/ui/gfx/resources/snapshot/snapshotter.h"
-#include "garnet/lib/ui/gfx/resources/stereo_camera.h"
-#include "garnet/lib/ui/gfx/resources/variable.h"
-#include "garnet/lib/ui/gfx/resources/view.h"
-#include "garnet/lib/ui/gfx/resources/view_holder.h"
 #include "garnet/lib/ui/gfx/swapchain/swapchain_factory.h"
 #include "garnet/lib/ui/gfx/util/time.h"
 #include "garnet/lib/ui/gfx/util/unwrap.h"
@@ -55,14 +32,6 @@
 
 namespace {
 
-// Makes it convenient to check that a value is constant and of a specific type,
-// or a variable.
-// TODO: There should also be a convenient way of type-checking a variable;
-// this will necessarily involve looking up the value in the ResourceMap.
-constexpr std::array<::fuchsia::ui::gfx::Value::Tag, 2> kFloatValueTypes{
-    {::fuchsia::ui::gfx::Value::Tag::kVector1,
-     ::fuchsia::ui::gfx::Value::Tag::kVariableId}};
-
 // Converts the provided vector of Hits into a fidl array of HitPtrs.
 fidl::VectorPtr<::fuchsia::ui::gfx::Hit> WrapHits(
     const std::vector<Hit>& hits) {
@@ -96,1332 +65,19 @@
                          session_context_.escher_resource_recycler,
                          session_context_.escher_image_factory,
                          session_context_.escher_gpu_uploader}),
-      resources_(error_reporter),
+      resources_(error_reporter_),
       weak_factory_(this) {
   FXL_DCHECK(error_reporter);
 }
 
-Session::~Session() { FXL_DCHECK(!is_valid_); }
-
-bool Session::ApplyCommand(CommandContext* command_context,
-                           ::fuchsia::ui::gfx::Command command) {
-  TRACE_DURATION("gfx", "Session::ApplyCommand");
-  switch (command.Which()) {
-    case ::fuchsia::ui::gfx::Command::Tag::kCreateResource:
-      return ApplyCreateResourceCmd(command_context,
-                                    std::move(command.create_resource()));
-    case ::fuchsia::ui::gfx::Command::Tag::kReleaseResource:
-      return ApplyReleaseResourceCmd(std::move(command.release_resource()));
-    case ::fuchsia::ui::gfx::Command::Tag::kExportResource:
-      return ApplyExportResourceCmd(std::move(command.export_resource()));
-    case ::fuchsia::ui::gfx::Command::Tag::kImportResource:
-      return ApplyImportResourceCmd(std::move(command.import_resource()));
-    case ::fuchsia::ui::gfx::Command::Tag::kSetImportFocus: {
-      // TODO(SCN-1026): Remove this.
-      if (auto import = resources_.FindResource<Import>(
-              command.set_import_focus().id,
-              ResourceMap::ErrorBehavior::kDontReportErrors)) {
-        import->set_focusable(command.set_import_focus().focusable);
-        return true;
-      }
-      return false;
-    }
-    case ::fuchsia::ui::gfx::Command::Tag::kAddChild:
-      return ApplyAddChildCmd(std::move(command.add_child()));
-    case ::fuchsia::ui::gfx::Command::Tag::kAddPart:
-      return ApplyAddPartCmd(std::move(command.add_part()));
-    case ::fuchsia::ui::gfx::Command::Tag::kDetach:
-      return ApplyDetachCmd(std::move(command.detach()));
-    case ::fuchsia::ui::gfx::Command::Tag::kDetachChildren:
-      return ApplyDetachChildrenCmd(std::move(command.detach_children()));
-    case ::fuchsia::ui::gfx::Command::Tag::kSetTag:
-      return ApplySetTagCmd(std::move(command.set_tag()));
-    case ::fuchsia::ui::gfx::Command::Tag::kSetTranslation:
-      return ApplySetTranslationCmd(std::move(command.set_translation()));
-    case ::fuchsia::ui::gfx::Command::Tag::kSetScale:
-      return ApplySetScaleCmd(std::move(command.set_scale()));
-    case ::fuchsia::ui::gfx::Command::Tag::kSetRotation:
-      return ApplySetRotationCmd(std::move(command.set_rotation()));
-    case ::fuchsia::ui::gfx::Command::Tag::kSetAnchor:
-      return ApplySetAnchorCmd(std::move(command.set_anchor()));
-    case ::fuchsia::ui::gfx::Command::Tag::kSetSize:
-      return ApplySetSizeCmd(std::move(command.set_size()));
-    case ::fuchsia::ui::gfx::Command::Tag::kSetOpacity:
-      return ApplySetOpacityCmd(command.set_opacity());
-    case ::fuchsia::ui::gfx::Command::Tag::kSendSizeChangeHintHack:
-      return ApplySendSizeChangeHintCmd(command.send_size_change_hint_hack());
-    case ::fuchsia::ui::gfx::Command::Tag::kSetShape:
-      return ApplySetShapeCmd(std::move(command.set_shape()));
-    case ::fuchsia::ui::gfx::Command::Tag::kSetMaterial:
-      return ApplySetMaterialCmd(std::move(command.set_material()));
-    case ::fuchsia::ui::gfx::Command::Tag::kSetClip:
-      return ApplySetClipCmd(std::move(command.set_clip()));
-    case ::fuchsia::ui::gfx::Command::Tag::kSetHitTestBehavior:
-      return ApplySetHitTestBehaviorCmd(
-          std::move(command.set_hit_test_behavior()));
-    case ::fuchsia::ui::gfx::Command::Tag::kSetViewProperties:
-      return ApplySetViewPropertiesCmd(
-          std::move(command.set_view_properties()));
-    case ::fuchsia::ui::gfx::Command::Tag::kSetCamera:
-      return ApplySetCameraCmd(std::move(command.set_camera()));
-    case ::fuchsia::ui::gfx::Command::Tag::kSetCameraTransform:
-      return ApplySetCameraTransformCmd(
-          std::move(command.set_camera_transform()));
-    case ::fuchsia::ui::gfx::Command::Tag::kSetCameraProjection:
-      return ApplySetCameraProjectionCmd(
-          std::move(command.set_camera_projection()));
-    case ::fuchsia::ui::gfx::Command::Tag::kSetStereoCameraProjection:
-      return ApplySetStereoCameraProjectionCmd(
-          std::move(command.set_stereo_camera_projection()));
-    case ::fuchsia::ui::gfx::Command::Tag::kSetCameraPoseBuffer:
-      return ApplySetCameraPoseBufferCmd(
-          std::move(command.set_camera_pose_buffer()));
-    case ::fuchsia::ui::gfx::Command::Tag::kSetLightColor:
-      return ApplySetLightColorCmd(std::move(command.set_light_color()));
-    case ::fuchsia::ui::gfx::Command::Tag::kSetLightDirection:
-      return ApplySetLightDirectionCmd(
-          std::move(command.set_light_direction()));
-    case ::fuchsia::ui::gfx::Command::Tag::kAddLight:
-      return ApplyAddLightCmd(std::move(command.add_light()));
-    case ::fuchsia::ui::gfx::Command::Tag::kDetachLight:
-      return ApplyDetachLightCmd(std::move(command.detach_light()));
-    case ::fuchsia::ui::gfx::Command::Tag::kDetachLights:
-      return ApplyDetachLightsCmd(std::move(command.detach_lights()));
-    case ::fuchsia::ui::gfx::Command::Tag::kSetTexture:
-      return ApplySetTextureCmd(std::move(command.set_texture()));
-    case ::fuchsia::ui::gfx::Command::Tag::kSetColor:
-      return ApplySetColorCmd(std::move(command.set_color()));
-    case ::fuchsia::ui::gfx::Command::Tag::kBindMeshBuffers:
-      return ApplyBindMeshBuffersCmd(std::move(command.bind_mesh_buffers()));
-    case ::fuchsia::ui::gfx::Command::Tag::kAddLayer:
-      return ApplyAddLayerCmd(std::move(command.add_layer()));
-    case ::fuchsia::ui::gfx::Command::Tag::kRemoveLayer:
-      return ApplyRemoveLayerCmd(std::move(command.remove_layer()));
-    case ::fuchsia::ui::gfx::Command::Tag::kRemoveAllLayers:
-      return ApplyRemoveAllLayersCmd(std::move(command.remove_all_layers()));
-    case ::fuchsia::ui::gfx::Command::Tag::kSetLayerStack:
-      return ApplySetLayerStackCmd(std::move(command.set_layer_stack()));
-    case ::fuchsia::ui::gfx::Command::Tag::kSetRenderer:
-      return ApplySetRendererCmd(std::move(command.set_renderer()));
-    case ::fuchsia::ui::gfx::Command::Tag::kSetRendererParam:
-      return ApplySetRendererParamCmd(std::move(command.set_renderer_param()));
-    case ::fuchsia::ui::gfx::Command::Tag::kSetEventMask:
-      return ApplySetEventMaskCmd(std::move(command.set_event_mask()));
-    case ::fuchsia::ui::gfx::Command::Tag::kSetLabel:
-      return ApplySetLabelCmd(std::move(command.set_label()));
-    case ::fuchsia::ui::gfx::Command::Tag::kSetDisableClipping:
-      return ApplySetDisableClippingCmd(
-          std::move(command.set_disable_clipping()));
-    case ::fuchsia::ui::gfx::Command::Tag::kTakeSnapshotCmd:
-      return ApplyTakeSnapshotCmdHACK(std::move(command.take_snapshot_cmd()));
-    case ::fuchsia::ui::gfx::Command::Tag::Invalid:
-      // FIDL validation should make this impossible.
-      FXL_CHECK(false);
-      return false;
-  }
-}
-
-bool Session::ApplyCreateResourceCmd(
-    CommandContext* command_context,
-    ::fuchsia::ui::gfx::CreateResourceCmd command) {
-  const ResourceId id = command.id;
-  if (id == 0) {
-    using ::operator<<;  // From print_commands.h
-    error_reporter_->ERROR()
-        << "scenic_impl::gfx::Session::ApplyCreateResourceCmd(): invalid ID: "
-        << command;
-    return false;
-  }
-
-  switch (command.resource.Which()) {
-    case ::fuchsia::ui::gfx::ResourceArgs::Tag::kMemory:
-      return ApplyCreateMemory(id, std::move(command.resource.memory()));
-    case ::fuchsia::ui::gfx::ResourceArgs::Tag::kImage:
-      return ApplyCreateImage(id, std::move(command.resource.image()));
-    case ::fuchsia::ui::gfx::ResourceArgs::Tag::kImagePipe:
-      return ApplyCreateImagePipe(id, std::move(command.resource.image_pipe()));
-    case ::fuchsia::ui::gfx::ResourceArgs::Tag::kBuffer:
-      return ApplyCreateBuffer(id, std::move(command.resource.buffer()));
-    case ::fuchsia::ui::gfx::ResourceArgs::Tag::kScene:
-      return ApplyCreateScene(id, std::move(command.resource.scene()));
-    case ::fuchsia::ui::gfx::ResourceArgs::Tag::kCamera:
-      return ApplyCreateCamera(id, std::move(command.resource.camera()));
-    case ::fuchsia::ui::gfx::ResourceArgs::Tag::kStereoCamera:
-      return ApplyCreateStereoCamera(
-          id, std::move(command.resource.stereo_camera()));
-    case ::fuchsia::ui::gfx::ResourceArgs::Tag::kRenderer:
-      return ApplyCreateRenderer(id, std::move(command.resource.renderer()));
-    case ::fuchsia::ui::gfx::ResourceArgs::Tag::kAmbientLight:
-      return ApplyCreateAmbientLight(
-          id, std::move(command.resource.ambient_light()));
-    case ::fuchsia::ui::gfx::ResourceArgs::Tag::kDirectionalLight:
-      return ApplyCreateDirectionalLight(
-          id, std::move(command.resource.directional_light()));
-    case ::fuchsia::ui::gfx::ResourceArgs::Tag::kRectangle:
-      return ApplyCreateRectangle(id, std::move(command.resource.rectangle()));
-    case ::fuchsia::ui::gfx::ResourceArgs::Tag::kRoundedRectangle:
-      return ApplyCreateRoundedRectangle(
-          command_context, id, std::move(command.resource.rounded_rectangle()));
-    case ::fuchsia::ui::gfx::ResourceArgs::Tag::kCircle:
-      return ApplyCreateCircle(id, std::move(command.resource.circle()));
-    case ::fuchsia::ui::gfx::ResourceArgs::Tag::kMesh:
-      return ApplyCreateMesh(id, std::move(command.resource.mesh()));
-    case ::fuchsia::ui::gfx::ResourceArgs::Tag::kMaterial:
-      return ApplyCreateMaterial(id, std::move(command.resource.material()));
-    case ::fuchsia::ui::gfx::ResourceArgs::Tag::kView:
-      return ApplyCreateView(id, std::move(command.resource.view()));
-    case ::fuchsia::ui::gfx::ResourceArgs::Tag::kViewHolder:
-      return ApplyCreateViewHolder(id,
-                                   std::move(command.resource.view_holder()));
-    case ::fuchsia::ui::gfx::ResourceArgs::Tag::kClipNode:
-      return ApplyCreateClipNode(id, std::move(command.resource.clip_node()));
-    case ::fuchsia::ui::gfx::ResourceArgs::Tag::kOpacityNode:
-      return ApplyCreateOpacityNode(id, command.resource.opacity_node());
-    case ::fuchsia::ui::gfx::ResourceArgs::Tag::kEntityNode:
-      return ApplyCreateEntityNode(id,
-                                   std::move(command.resource.entity_node()));
-    case ::fuchsia::ui::gfx::ResourceArgs::Tag::kShapeNode:
-      return ApplyCreateShapeNode(id, std::move(command.resource.shape_node()));
-    case ::fuchsia::ui::gfx::ResourceArgs::Tag::kCompositor:
-      return ApplyCreateCompositor(id,
-                                   std::move(command.resource.compositor()));
-    case ::fuchsia::ui::gfx::ResourceArgs::Tag::kDisplayCompositor:
-      return ApplyCreateDisplayCompositor(
-          id, std::move(command.resource.display_compositor()));
-    case ::fuchsia::ui::gfx::ResourceArgs::Tag::kImagePipeCompositor:
-      return ApplyCreateImagePipeCompositor(
-          id, std::move(command.resource.image_pipe_compositor()));
-    case ::fuchsia::ui::gfx::ResourceArgs::Tag::kLayerStack:
-      return ApplyCreateLayerStack(id,
-                                   std::move(command.resource.layer_stack()));
-    case ::fuchsia::ui::gfx::ResourceArgs::Tag::kLayer:
-      return ApplyCreateLayer(id, std::move(command.resource.layer()));
-    case ::fuchsia::ui::gfx::ResourceArgs::Tag::kVariable:
-      return ApplyCreateVariable(id, std::move(command.resource.variable()));
-    case ::fuchsia::ui::gfx::ResourceArgs::Tag::Invalid:
-      // FIDL validation should make this impossible.
-      FXL_CHECK(false);
-      return false;
-  }
-}
-
-bool Session::ApplyReleaseResourceCmd(
-    ::fuchsia::ui::gfx::ReleaseResourceCmd command) {
-  return resources_.RemoveResource(command.id);
-}
-
-bool Session::ApplyExportResourceCmd(
-    ::fuchsia::ui::gfx::ExportResourceCmd command) {
-  if (!command.token) {
-    error_reporter_->ERROR()
-        << "scenic_impl::gfx::Session::ApplyExportResourceCmd(): "
-           "no token provided.";
-    return false;
-  }
-  if (auto resource = resources_.FindResource<Resource>(command.id)) {
-    return session_context_.resource_linker->ExportResource(
-        resource.get(), std::move(command.token));
-  }
-  return false;
-}
-
-bool Session::ApplyImportResourceCmd(
-    ::fuchsia::ui::gfx::ImportResourceCmd command) {
-  if (!command.token) {
-    error_reporter_->ERROR()
-        << "scenic_impl::gfx::Session::ApplyImportResourceCmd(): "
-           "no token provided.";
-    return false;
-  }
-  ImportPtr import = fxl::MakeRefCounted<Import>(
-      this, command.id, command.spec,
-      session_context_.resource_linker->GetWeakPtr());
-  return session_context_.resource_linker->ImportResource(
-             import.get(), command.spec, std::move(command.token)) &&
-         resources_.AddResource(command.id, std::move(import));
-}
-
-bool Session::ApplyAddChildCmd(::fuchsia::ui::gfx::AddChildCmd command) {
-  // Find the parent and child nodes. We can add:
-  // - Nodes to Nodes
-  // - ViewHolders to Nodes
-  // - Nodes to Views
-  // TODO(SCN-795): Split these out into separate commands?
-  if (auto parent = resources_.FindResource<Node>(
-          command.node_id, ResourceMap::ErrorBehavior::kDontReportErrors)) {
-    if (auto child = resources_.FindResource<Node>(
-            command.child_id, ResourceMap::ErrorBehavior::kDontReportErrors)) {
-      return parent->AddChild(std::move(child));
-    } else if (auto child =
-                   resources_.FindResource<ViewHolder>(command.child_id)) {
-      return parent->AddViewHolder(std::move(child));
-    }
-  } else if (auto parent = resources_.FindResource<View>(command.node_id)) {
-    if (auto child = resources_.FindResource<Node>(command.child_id)) {
-      return parent->AddChild(std::move(child));
-    }
-  }
-  return false;
-}
-
-bool Session::ApplyAddPartCmd(::fuchsia::ui::gfx::AddPartCmd command) {
-  // Find the parent and part nodes.
-  if (auto parent_node = resources_.FindResource<Node>(command.node_id)) {
-    if (auto part_node = resources_.FindResource<Node>(command.part_id)) {
-      return parent_node->AddPart(std::move(part_node));
-    }
-  }
-  return false;
-}
-
-bool Session::ApplyTakeSnapshotCmdHACK(
-    ::fuchsia::ui::gfx::TakeSnapshotCmdHACK command) {
-  async::PostTask(
-      async_get_default_dispatcher(), [weak = weak_factory_.GetWeakPtr(),
-                                       command = std::move(command)]() mutable {
-        if (!weak) {
-          if (auto callback = command.callback.Bind()) {
-            // TODO(SCN-978): Return an error to the caller for invalid data.
-            callback->OnData(fuchsia::mem::Buffer{});
-          }
-          return;
-        }
-
-        const auto& context = weak->session_context_;
-        Resource* resource = nullptr;
-        if (auto node = weak->resources_.FindResource<Node>(command.node_id)) {
-          resource = node.get();
-        } else if (command.node_id == 0) {
-          // TODO(SCN-1170): get rid of SceneGraph::first_compositor().
-          const auto& first_compositor_weak =
-              context.scene_graph->first_compositor();
-          if (first_compositor_weak) {
-            resource = first_compositor_weak.get();
-          }
-        }
-
-        if (resource == nullptr) {
-          if (auto callback = command.callback.Bind()) {
-            callback->OnData(fuchsia::mem::Buffer{});
-          }
-          return;
-        }
-
-        auto gpu_uploader =
-            escher::BatchGpuUploader::New(context.escher->GetWeakPtr());
-        Snapshotter snapshotter(gpu_uploader);
-        // Take a snapshot and return the data in callback. The closure does
-        // not need the snapshotter instance and is invoked after the instance
-        // is destroyed.
-        snapshotter.TakeSnapshot(resource, [callback = command.callback.Bind()](
-                                               fuchsia::mem::Buffer snapshot) {
-          callback->OnData(std::move(snapshot));
-        });
-      });
-  return true;
-}
-
-bool Session::ApplyDetachCmd(::fuchsia::ui::gfx::DetachCmd command) {
-  if (auto resource = resources_.FindResource<Resource>(command.id)) {
-    return resource->Detach();
-  }
-  return false;
-}
-
-bool Session::ApplyDetachChildrenCmd(
-    ::fuchsia::ui::gfx::DetachChildrenCmd command) {
-  if (auto node = resources_.FindResource<Node>(command.node_id)) {
-    return node->DetachChildren();
-  }
-  return false;
-}
-
-bool Session::ApplySetTagCmd(::fuchsia::ui::gfx::SetTagCmd command) {
-  if (auto node = resources_.FindResource<Node>(command.node_id)) {
-    return node->SetTagValue(command.tag_value);
-  }
-  return false;
-}
-
-bool Session::ApplySetTranslationCmd(
-    ::fuchsia::ui::gfx::SetTranslationCmd command) {
-  if (auto node = resources_.FindResource<Node>(command.id)) {
-    if (IsVariable(command.value)) {
-      if (auto variable = resources_.FindVariableResource<Vector3Variable>(
-              command.value.variable_id)) {
-        return node->SetTranslation(variable);
-      }
-    } else {
-      return node->SetTranslation(UnwrapVector3(command.value));
-    }
-  }
-  return false;
-}
-
-bool Session::ApplySetScaleCmd(::fuchsia::ui::gfx::SetScaleCmd command) {
-  if (auto node = resources_.FindResource<Node>(command.id)) {
-    if (IsVariable(command.value)) {
-      if (auto variable = resources_.FindVariableResource<Vector3Variable>(
-              command.value.variable_id)) {
-        return node->SetScale(variable);
-      }
-    } else {
-      return node->SetScale(UnwrapVector3(command.value));
-    }
-  }
-  return false;
-}
-
-bool Session::ApplySetRotationCmd(::fuchsia::ui::gfx::SetRotationCmd command) {
-  if (auto node = resources_.FindResource<Node>(command.id)) {
-    if (IsVariable(command.value)) {
-      if (auto variable = resources_.FindVariableResource<QuaternionVariable>(
-              command.value.variable_id)) {
-        return node->SetRotation(variable);
-      }
-    } else {
-      return node->SetRotation(UnwrapQuaternion(command.value));
-    }
-  }
-  return false;
-}
-
-bool Session::ApplySetAnchorCmd(::fuchsia::ui::gfx::SetAnchorCmd command) {
-  if (auto node = resources_.FindResource<Node>(command.id)) {
-    if (IsVariable(command.value)) {
-      if (auto variable = resources_.FindVariableResource<Vector3Variable>(
-              command.value.variable_id)) {
-        return node->SetAnchor(variable);
-      }
-    }
-    return node->SetAnchor(UnwrapVector3(command.value));
-  }
-  return false;
-}
-
-bool Session::ApplySetSizeCmd(::fuchsia::ui::gfx::SetSizeCmd command) {
-  if (auto layer = resources_.FindResource<Layer>(command.id)) {
-    if (IsVariable(command.value)) {
-      error_reporter_->ERROR()
-          << "scenic_impl::gfx::Session::ApplySetSizeCmd(): "
-             "unimplemented for variable value.";
-      return false;
-    }
-    return layer->SetSize(UnwrapVector2(command.value));
-  }
-  return false;
-}
-
-bool Session::ApplySetOpacityCmd(::fuchsia::ui::gfx::SetOpacityCmd command) {
-  if (auto node = resources_.FindResource<OpacityNode>(command.node_id)) {
-    node->SetOpacity(command.opacity);
-    return true;
-  }
-  return false;
-}
-
-bool Session::ApplySendSizeChangeHintCmd(
-    ::fuchsia::ui::gfx::SendSizeChangeHintCmdHACK command) {
-  if (auto node = resources_.FindResource<Node>(command.node_id)) {
-    return node->SendSizeChangeHint(command.width_change_factor,
-                                    command.height_change_factor);
-  }
-  return false;
-}
-
-bool Session::ApplySetShapeCmd(::fuchsia::ui::gfx::SetShapeCmd command) {
-  if (auto node = resources_.FindResource<ShapeNode>(command.node_id)) {
-    if (auto shape = resources_.FindResource<Shape>(command.shape_id)) {
-      node->SetShape(std::move(shape));
-      return true;
-    }
-  }
-  return false;
-}
-
-bool Session::ApplySetMaterialCmd(::fuchsia::ui::gfx::SetMaterialCmd command) {
-  if (auto node = resources_.FindResource<ShapeNode>(command.node_id)) {
-    if (auto material =
-            resources_.FindResource<Material>(command.material_id)) {
-      node->SetMaterial(std::move(material));
-      return true;
-    }
-  }
-  return false;
-}
-
-bool Session::ApplySetClipCmd(::fuchsia::ui::gfx::SetClipCmd command) {
-  if (command.clip_id != 0) {
-    // TODO(SCN-167): Support non-zero clip_id.
-    error_reporter_->ERROR()
-        << "scenic_impl::gfx::Session::ApplySetClipCmd(): only "
-           "clip_to_self is implemented.";
-    return false;
-  }
-
-  if (auto node = resources_.FindResource<Node>(command.node_id)) {
-    return node->SetClipToSelf(command.clip_to_self);
-  }
-
-  return false;
-}
-
-bool Session::ApplySetHitTestBehaviorCmd(
-    ::fuchsia::ui::gfx::SetHitTestBehaviorCmd command) {
-  if (auto node = resources_.FindResource<Node>(command.node_id)) {
-    return node->SetHitTestBehavior(command.hit_test_behavior);
-  }
-
-  return false;
-}
-
-bool Session::ApplySetViewPropertiesCmd(
-    ::fuchsia::ui::gfx::SetViewPropertiesCmd command) {
-  if (auto view_holder =
-          resources_.FindResource<ViewHolder>(command.view_holder_id)) {
-    view_holder->SetViewProperties(std::move(command.properties));
-    return true;
-  }
-  return false;
-}
-
-bool Session::ApplySetCameraCmd(::fuchsia::ui::gfx::SetCameraCmd command) {
-  if (auto renderer = resources_.FindResource<Renderer>(command.renderer_id)) {
-    if (command.camera_id == 0) {
-      renderer->SetCamera(nullptr);
-      return true;
-    } else if (auto camera =
-                   resources_.FindResource<Camera>(command.camera_id)) {
-      renderer->SetCamera(std::move(camera));
-      return true;
-    }
-  }
-  return false;
-}
-
-bool Session::ApplySetTextureCmd(::fuchsia::ui::gfx::SetTextureCmd command) {
-  if (auto material = resources_.FindResource<Material>(command.material_id)) {
-    if (command.texture_id == 0) {
-      material->SetTexture(nullptr);
-      return true;
-    } else if (auto image =
-                   resources_.FindResource<ImageBase>(command.texture_id)) {
-      material->SetTexture(std::move(image));
-      return true;
-    }
-  }
-  return false;
-}
-
-bool Session::ApplySetColorCmd(::fuchsia::ui::gfx::SetColorCmd command) {
-  if (auto material = resources_.FindResource<Material>(command.material_id)) {
-    if (IsVariable(command.color)) {
-      error_reporter_->ERROR()
-          << "scenic_impl::gfx::Session::ApplySetColorCmd(): "
-             "unimplemented for variable color.";
-      return false;
-    }
-
-    auto& color = command.color.value;
-    float red = static_cast<float>(color.red) / 255.f;
-    float green = static_cast<float>(color.green) / 255.f;
-    float blue = static_cast<float>(color.blue) / 255.f;
-    float alpha = static_cast<float>(color.alpha) / 255.f;
-    material->SetColor(red, green, blue, alpha);
-    return true;
-  }
-  return false;
-}
-
-bool Session::ApplyBindMeshBuffersCmd(
-    ::fuchsia::ui::gfx::BindMeshBuffersCmd command) {
-  auto mesh = resources_.FindResource<MeshShape>(command.mesh_id);
-  auto index_buffer = resources_.FindResource<Buffer>(command.index_buffer_id);
-  auto vertex_buffer =
-      resources_.FindResource<Buffer>(command.vertex_buffer_id);
-  if (mesh && index_buffer && vertex_buffer) {
-    return mesh->BindBuffers(std::move(index_buffer), command.index_format,
-                             command.index_offset, command.index_count,
-                             std::move(vertex_buffer), command.vertex_format,
-                             command.vertex_offset, command.vertex_count,
-                             Unwrap(command.bounding_box));
-  }
-  return false;
-}
-
-bool Session::ApplyAddLayerCmd(::fuchsia::ui::gfx::AddLayerCmd command) {
-  auto layer_stack =
-      resources_.FindResource<LayerStack>(command.layer_stack_id);
-  auto layer = resources_.FindResource<Layer>(command.layer_id);
-  if (layer_stack && layer) {
-    return layer_stack->AddLayer(std::move(layer));
-  }
-  return false;
-}
-
-bool Session::ApplyRemoveLayerCmd(::fuchsia::ui::gfx::RemoveLayerCmd command) {
-  auto layer_stack =
-      resources_.FindResource<LayerStack>(command.layer_stack_id);
-  auto layer = resources_.FindResource<Layer>(command.layer_id);
-  if (layer_stack && layer) {
-    return layer_stack->RemoveLayer(std::move(layer));
-  }
-  return false;
-}
-
-bool Session::ApplyRemoveAllLayersCmd(
-    ::fuchsia::ui::gfx::RemoveAllLayersCmd command) {
-  auto layer_stack =
-      resources_.FindResource<LayerStack>(command.layer_stack_id);
-  if (layer_stack) {
-    return layer_stack->RemoveAllLayers();
-  }
-  return false;
-}
-
-bool Session::ApplySetLayerStackCmd(
-    ::fuchsia::ui::gfx::SetLayerStackCmd command) {
-  auto compositor = resources_.FindResource<Compositor>(command.compositor_id);
-  auto layer_stack =
-      resources_.FindResource<LayerStack>(command.layer_stack_id);
-  if (compositor && layer_stack) {
-    return compositor->SetLayerStack(std::move(layer_stack));
-  }
-  return false;
-}
-
-bool Session::ApplySetRendererCmd(::fuchsia::ui::gfx::SetRendererCmd command) {
-  auto layer = resources_.FindResource<Layer>(command.layer_id);
-  auto renderer = resources_.FindResource<Renderer>(command.renderer_id);
-
-  if (layer && renderer) {
-    return layer->SetRenderer(std::move(renderer));
-  }
-  return false;
-}
-
-bool Session::ApplySetRendererParamCmd(
-    ::fuchsia::ui::gfx::SetRendererParamCmd command) {
-  auto renderer = resources_.FindResource<Renderer>(command.renderer_id);
-  if (renderer) {
-    switch (command.param.Which()) {
-      case ::fuchsia::ui::gfx::RendererParam::Tag::kShadowTechnique:
-        return renderer->SetShadowTechnique(command.param.shadow_technique());
-      case ::fuchsia::ui::gfx::RendererParam::Tag::kRenderFrequency:
-        // TODO(SCN-1169): SetRenderContinuously should only affect the
-        // compositor that has the renderer attached to it.
-        session_context_.frame_scheduler->SetRenderContinuously(
-            command.param.render_frequency() ==
-            ::fuchsia::ui::gfx::RenderFrequency::CONTINUOUSLY);
-        return true;
-      case ::fuchsia::ui::gfx::RendererParam::Tag::Invalid:
-        error_reporter_->ERROR()
-            << "scenic_impl::gfx::Session::ApplySetRendererParamCmd(): "
-               "invalid param.";
-    }
-  }
-  return false;
-}
-
-bool Session::ApplySetEventMaskCmd(
-    ::fuchsia::ui::gfx::SetEventMaskCmd command) {
-  if (auto r = resources_.FindResource<Resource>(command.id)) {
-    return r->SetEventMask(command.event_mask);
-  }
-  return false;
-}
-
-bool Session::ApplySetCameraTransformCmd(
-    ::fuchsia::ui::gfx::SetCameraTransformCmd command) {
-  // TODO(SCN-123): support variables.
-  if (IsVariable(command.eye_position) || IsVariable(command.eye_look_at) ||
-      IsVariable(command.eye_up)) {
-    error_reporter_->ERROR()
-        << "scenic_impl::gfx::Session::ApplySetCameraTransformCmd(): "
-           "unimplemented: variable properties.";
-    return false;
-  } else if (auto camera = resources_.FindResource<Camera>(command.camera_id)) {
-    camera->SetTransform(UnwrapVector3(command.eye_position),
-                         UnwrapVector3(command.eye_look_at),
-                         UnwrapVector3(command.eye_up));
-    return true;
-  }
-  return false;
-}
-
-bool Session::ApplySetCameraProjectionCmd(
-    ::fuchsia::ui::gfx::SetCameraProjectionCmd command) {
-  // TODO(SCN-123): support variables.
-  if (IsVariable(command.fovy)) {
-    error_reporter_->ERROR()
-        << "scenic_impl::gfx::Session::ApplySetCameraProjectionCmd(): "
-           "unimplemented: variable properties.";
-    return false;
-  } else if (auto camera = resources_.FindResource<Camera>(command.camera_id)) {
-    camera->SetProjection(UnwrapFloat(command.fovy));
-    return true;
-  }
-  return false;
-}
-
-bool Session::ApplySetStereoCameraProjectionCmd(
-    ::fuchsia::ui::gfx::SetStereoCameraProjectionCmd command) {
-  if (IsVariable(command.left_projection) ||
-      IsVariable(command.right_projection)) {
-    error_reporter_->ERROR()
-        << "scenic_impl::gfx::Session::ApplySetStereoCameraProjectionOp(): "
-           "unimplemented: variable properties.";
-    return false;
-  } else if (auto stereo_camera =
-                 resources_.FindResource<StereoCamera>(command.camera_id)) {
-    stereo_camera->SetStereoProjection(Unwrap(command.left_projection.value),
-                                       Unwrap(command.right_projection.value));
-    return true;
-  }
-  return false;
-}
-
-bool Session::ApplySetCameraPoseBufferCmd(
-    ::fuchsia::ui::gfx::SetCameraPoseBufferCmd command) {
-  if (command.base_time > dispatcher_clock_now()) {
-    error_reporter_->ERROR()
-        << "scenic_impl::gfx::Session::ApplySetCameraPoseBufferCmd(): "
-           "base time not in the past";
-    return false;
-  }
-
-  auto buffer = resources_.FindResource<Buffer>(command.buffer_id);
-  if (!buffer) {
-    error_reporter_->ERROR()
-        << "scenic_impl::gfx::Session::ApplySetCameraPoseBufferCmd(): "
-           "invalid buffer ID";
-    return false;
-  }
-
-  if (command.num_entries < 1) {
-    error_reporter_->ERROR()
-        << "scenic_impl::gfx::Session::ApplySetCameraPoseBufferCmd(): "
-           "must have at least one entry in the pose buffer";
-    return false;
-  }
-
-  if (buffer->size() < command.num_entries * sizeof(escher::hmd::Pose)) {
-    error_reporter_->ERROR()
-        << "scenic_impl::gfx::Session::ApplySetCameraPoseBufferCmd(): "
-           "buffer is not large enough";
-    return false;
-  }
-
-  auto camera = resources_.FindResource<Camera>(command.camera_id);
-  if (!camera) {
-    error_reporter_->ERROR()
-        << "scenic_impl::gfx::Session::ApplySetCameraPoseBufferCmd(): "
-           "invalid camera ID";
-    return false;
-  }
-
-  camera->SetPoseBuffer(buffer, command.num_entries, command.base_time,
-                        command.time_interval);
-
-  return true;
-}
-
-bool Session::ApplySetLightColorCmd(
-    ::fuchsia::ui::gfx::SetLightColorCmd command) {
-  // TODO(SCN-123): support variables.
-  if (command.color.variable_id) {
-    error_reporter_->ERROR()
-        << "scenic_impl::gfx::Session::ApplySetLightColorCmd(): "
-           "unimplemented: variable color.";
-    return false;
-  } else if (auto light = resources_.FindResource<Light>(command.light_id)) {
-    return light->SetColor(Unwrap(command.color.value));
-  }
-  return false;
-}
-
-bool Session::ApplySetLightDirectionCmd(
-    ::fuchsia::ui::gfx::SetLightDirectionCmd command) {
-  // TODO(SCN-123): support variables.
-  if (command.direction.variable_id) {
-    error_reporter_->ERROR()
-        << "scenic_impl::gfx::Session::ApplySetLightDirectionCmd(): "
-           "unimplemented: variable direction.";
-    return false;
-  } else if (auto light =
-                 resources_.FindResource<DirectionalLight>(command.light_id)) {
-    return light->SetDirection(Unwrap(command.direction.value));
-  }
-  return false;
-}
-
-bool Session::ApplyAddLightCmd(::fuchsia::ui::gfx::AddLightCmd command) {
-  if (auto scene = resources_.FindResource<Scene>(command.scene_id)) {
-    if (auto light = resources_.FindResource<Light>(command.light_id)) {
-      return scene->AddLight(std::move(light));
-    }
-  }
-
-  error_reporter_->ERROR()
-      << "scenic_impl::gfx::Session::ApplyAddLightCmd(): unimplemented.";
-  return false;
-}
-
-bool Session::ApplyDetachLightCmd(::fuchsia::ui::gfx::DetachLightCmd command) {
-  error_reporter_->ERROR()
-      << "scenic_impl::gfx::Session::ApplyDetachLightCmd(): unimplemented.";
-  return false;
-}
-
-bool Session::ApplyDetachLightsCmd(
-    ::fuchsia::ui::gfx::DetachLightsCmd command) {
-  error_reporter_->ERROR()
-      << "scenic_impl::gfx::Session::ApplyDetachLightsCmd(): unimplemented.";
-  return false;
-}
-
-bool Session::ApplySetLabelCmd(::fuchsia::ui::gfx::SetLabelCmd command) {
-  if (auto r = resources_.FindResource<Resource>(command.id)) {
-    return r->SetLabel(command.label);
-  }
-  return false;
-}
-
-bool Session::ApplySetDisableClippingCmd(
-    ::fuchsia::ui::gfx::SetDisableClippingCmd command) {
-  if (auto r = resources_.FindResource<Renderer>(command.renderer_id)) {
-    r->DisableClipping(command.disable_clipping);
-    return true;
-  }
-  return false;
-}
-
-bool Session::ApplyCreateMemory(ResourceId id,
-                                ::fuchsia::ui::gfx::MemoryArgs args) {
-  auto memory = CreateMemory(id, std::move(args));
-  return memory ? resources_.AddResource(id, std::move(memory)) : false;
-}
-
-bool Session::ApplyCreateImage(ResourceId id,
-                               ::fuchsia::ui::gfx::ImageArgs args) {
-  if (auto memory = resources_.FindResource<Memory>(args.memory_id)) {
-    if (auto image = CreateImage(id, std::move(memory), args)) {
-      return resources_.AddResource(id, std::move(image));
-    }
-  }
-
-  return false;
-}
-
-bool Session::ApplyCreateImagePipe(ResourceId id,
-                                   ::fuchsia::ui::gfx::ImagePipeArgs args) {
-  auto image_pipe = fxl::MakeRefCounted<ImagePipe>(
-      this, id, std::move(args.image_pipe_request),
-      session_context_.update_scheduler);
-  return resources_.AddResource(id, image_pipe);
-}
-
-bool Session::ApplyCreateBuffer(ResourceId id,
-                                ::fuchsia::ui::gfx::BufferArgs args) {
-  if (auto memory = resources_.FindResource<Memory>(args.memory_id)) {
-    if (auto buffer = CreateBuffer(id, std::move(memory), args.memory_offset,
-                                   args.num_bytes)) {
-      return resources_.AddResource(id, std::move(buffer));
-    }
-  }
-  return false;
-}
-
-bool Session::ApplyCreateScene(ResourceId id,
-                               ::fuchsia::ui::gfx::SceneArgs args) {
-  auto scene = CreateScene(id, std::move(args));
-  return scene ? resources_.AddResource(id, std::move(scene)) : false;
-}
-
-bool Session::ApplyCreateCamera(ResourceId id,
-                                ::fuchsia::ui::gfx::CameraArgs args) {
-  auto camera = CreateCamera(id, std::move(args));
-  return camera ? resources_.AddResource(id, std::move(camera)) : false;
-}
-
-bool Session::ApplyCreateStereoCamera(
-    ResourceId id, ::fuchsia::ui::gfx::StereoCameraArgs args) {
-  auto camera = CreateStereoCamera(id, args);
-  return camera ? resources_.AddResource(id, std::move(camera)) : false;
-}
-
-bool Session::ApplyCreateRenderer(ResourceId id,
-                                  ::fuchsia::ui::gfx::RendererArgs args) {
-  auto renderer = CreateRenderer(id, std::move(args));
-  return renderer ? resources_.AddResource(id, std::move(renderer)) : false;
-}
-
-bool Session::ApplyCreateAmbientLight(
-    ResourceId id, ::fuchsia::ui::gfx::AmbientLightArgs args) {
-  auto light = CreateAmbientLight(id);
-  return light ? resources_.AddResource(id, std::move(light)) : false;
-}
-
-bool Session::ApplyCreateDirectionalLight(
-    ResourceId id, ::fuchsia::ui::gfx::DirectionalLightArgs args) {
-  auto light = CreateDirectionalLight(id);
-  return light ? resources_.AddResource(id, std::move(light)) : false;
-}
-
-bool Session::ApplyCreateRectangle(ResourceId id,
-                                   ::fuchsia::ui::gfx::RectangleArgs args) {
-  if (!AssertValueIsOfType(args.width, kFloatValueTypes) ||
-      !AssertValueIsOfType(args.height, kFloatValueTypes)) {
-    return false;
-  }
-
-  // TODO(SCN-123): support variables.
-  if (IsVariable(args.width) || IsVariable(args.height)) {
-    error_reporter_->ERROR()
-        << "scenic_impl::gfx::Session::ApplyCreateRectangle(): "
-           "unimplemented: variable width/height.";
-    return false;
-  }
-
-  auto rectangle =
-      CreateRectangle(id, args.width.vector1(), args.height.vector1());
-  return rectangle ? resources_.AddResource(id, std::move(rectangle)) : false;
-}
-
-bool Session::ApplyCreateRoundedRectangle(
-    CommandContext* command_context, ResourceId id,
-    ::fuchsia::ui::gfx::RoundedRectangleArgs args) {
-  if (!AssertValueIsOfType(args.width, kFloatValueTypes) ||
-      !AssertValueIsOfType(args.height, kFloatValueTypes) ||
-      !AssertValueIsOfType(args.top_left_radius, kFloatValueTypes) ||
-      !AssertValueIsOfType(args.top_right_radius, kFloatValueTypes) ||
-      !AssertValueIsOfType(args.bottom_left_radius, kFloatValueTypes) ||
-      !AssertValueIsOfType(args.bottom_right_radius, kFloatValueTypes)) {
-    return false;
-  }
-
-  // TODO(SCN-123): support variables.
-  if (IsVariable(args.width) || IsVariable(args.height) ||
-      IsVariable(args.top_left_radius) || IsVariable(args.top_right_radius) ||
-      IsVariable(args.bottom_left_radius) ||
-      IsVariable(args.bottom_right_radius)) {
-    error_reporter_->ERROR()
-        << "scenic_impl::gfx::Session::ApplyCreateRoundedRectangle(): "
-           "unimplemented: variable width/height/radii.";
-    return false;
-  }
-
-  const float width = args.width.vector1();
-  const float height = args.height.vector1();
-  const float top_left_radius = args.top_left_radius.vector1();
-  const float top_right_radius = args.top_right_radius.vector1();
-  const float bottom_right_radius = args.bottom_right_radius.vector1();
-  const float bottom_left_radius = args.bottom_left_radius.vector1();
-
-  auto rectangle = CreateRoundedRectangle(
-      command_context, id, width, height, top_left_radius, top_right_radius,
-      bottom_right_radius, bottom_left_radius);
-  return rectangle ? resources_.AddResource(id, std::move(rectangle)) : false;
-}
-
-bool Session::ApplyCreateCircle(ResourceId id,
-                                ::fuchsia::ui::gfx::CircleArgs args) {
-  if (!AssertValueIsOfType(args.radius, kFloatValueTypes)) {
-    return false;
-  }
-
-  // TODO(SCN-123): support variables.
-  if (IsVariable(args.radius)) {
-    error_reporter_->ERROR()
-        << "scenic_impl::gfx::Session::ApplyCreateCircle(): "
-           "unimplemented: variable radius.";
-    return false;
-  }
-
-  auto circle = CreateCircle(id, args.radius.vector1());
-  return circle ? resources_.AddResource(id, std::move(circle)) : false;
-}
-
-bool Session::ApplyCreateMesh(ResourceId id,
-                              ::fuchsia::ui::gfx::MeshArgs args) {
-  auto mesh = CreateMesh(id);
-  return mesh ? resources_.AddResource(id, std::move(mesh)) : false;
-}
-
-bool Session::ApplyCreateMaterial(ResourceId id,
-                                  ::fuchsia::ui::gfx::MaterialArgs args) {
-  auto material = CreateMaterial(id);
-  return material ? resources_.AddResource(id, std::move(material)) : false;
-}
-
-bool Session::ApplyCreateView(ResourceId id,
-                              ::fuchsia::ui::gfx::ViewArgs args) {
-  // Sanity check.  We also rely on FIDL to enforce this for us, although it
-  // does not at the moment.
-  FXL_DCHECK(args.token)
-      << "scenic_impl::gfx::Session::ApplyCreateView(): no token provided.";
-
-  if (auto view = CreateView(id, std::move(args))) {
-    view->As<View>()->Connect();  // Initiate the link.
-    resources_.AddResource(id, std::move(view));
-    return true;
-  }
-  return false;
-}
-
-bool Session::ApplyCreateViewHolder(ResourceId id,
-                                    ::fuchsia::ui::gfx::ViewHolderArgs args) {
-  // Sanity check.  We also rely on FIDL to enforce this for us, although it
-  // does not at the moment
-  FXL_DCHECK(args.token) << "scenic_impl::gfx::Session::ApplyCreateViewHolder()"
-                            ": no token provided.";
-
-  if (auto view_holder = CreateViewHolder(id, std::move(args))) {
-    view_holder->As<ViewHolder>()->Connect();  // Initiate the link.
-    resources_.AddResource(id, std::move(view_holder));
-    return true;
-  }
-  return false;
-}
-
-bool Session::ApplyCreateClipNode(ResourceId id,
-                                  ::fuchsia::ui::gfx::ClipNodeArgs args) {
-  auto node = CreateClipNode(id, std::move(args));
-  return node ? resources_.AddResource(id, std::move(node)) : false;
-}
-
-bool Session::ApplyCreateEntityNode(ResourceId id,
-                                    ::fuchsia::ui::gfx::EntityNodeArgs args) {
-  auto node = CreateEntityNode(id, std::move(args));
-  return node ? resources_.AddResource(id, std::move(node)) : false;
-}
-
-bool Session::ApplyCreateOpacityNode(ResourceId id,
-                                     ::fuchsia::ui::gfx::OpacityNodeArgs args) {
-  auto node = CreateOpacityNode(id, args);
-  return node ? resources_.AddResource(id, std::move(node)) : false;
-}
-
-bool Session::ApplyCreateShapeNode(ResourceId id,
-                                   ::fuchsia::ui::gfx::ShapeNodeArgs args) {
-  auto node = CreateShapeNode(id, std::move(args));
-  return node ? resources_.AddResource(id, std::move(node)) : false;
-}
-
-bool Session::ApplyCreateCompositor(ResourceId id,
-                                    ::fuchsia::ui::gfx::CompositorArgs args) {
-  auto compositor = CreateCompositor(id, std::move(args));
-  return compositor ? resources_.AddResource(id, std::move(compositor)) : false;
-}
-
-bool Session::ApplyCreateDisplayCompositor(
-    ResourceId id, ::fuchsia::ui::gfx::DisplayCompositorArgs args) {
-  auto compositor = CreateDisplayCompositor(id, std::move(args));
-  return compositor ? resources_.AddResource(id, std::move(compositor)) : false;
-}
-
-bool Session::ApplyCreateImagePipeCompositor(
-    ResourceId id, ::fuchsia::ui::gfx::ImagePipeCompositorArgs args) {
-  auto compositor = CreateImagePipeCompositor(id, std::move(args));
-  return compositor ? resources_.AddResource(id, std::move(compositor)) : false;
-}
-
-bool Session::ApplyCreateLayerStack(ResourceId id,
-                                    ::fuchsia::ui::gfx::LayerStackArgs args) {
-  auto layer_stack = CreateLayerStack(id, std::move(args));
-  return layer_stack ? resources_.AddResource(id, std::move(layer_stack))
-                     : false;
-}
-
-bool Session::ApplyCreateLayer(ResourceId id,
-                               ::fuchsia::ui::gfx::LayerArgs args) {
-  auto layer = CreateLayer(id, std::move(args));
-  return layer ? resources_.AddResource(id, std::move(layer)) : false;
-}
-
-bool Session::ApplyCreateVariable(ResourceId id,
-                                  ::fuchsia::ui::gfx::VariableArgs args) {
-  auto variable = CreateVariable(id, std::move(args));
-  return variable ? resources_.AddResource(id, std::move(variable)) : false;
-}
-
-ResourcePtr Session::CreateMemory(ResourceId id,
-                                  ::fuchsia::ui::gfx::MemoryArgs args) {
-  return Memory::New(this, id, std::move(args), error_reporter_);
-}
-
-ResourcePtr Session::CreateImage(ResourceId id, MemoryPtr memory,
-                                 ::fuchsia::ui::gfx::ImageArgs args) {
-  return Image::New(this, id, memory, args.info, args.memory_offset,
-                    error_reporter_);
-}
-
-ResourcePtr Session::CreateBuffer(ResourceId id, MemoryPtr memory,
-                                  uint32_t memory_offset, uint32_t num_bytes) {
-  if (memory_offset + num_bytes > memory->size()) {
-    error_reporter_->ERROR() << "scenic_impl::gfx::Session::CreateBuffer(): "
-                                "buffer does not fit within memory (buffer "
-                                "offset: "
-                             << memory_offset << ", buffer size: " << num_bytes
-                             << ", memory size: " << memory->size() << ")";
-    return ResourcePtr();
-  }
-
-  // Make a pointer to a subregion of the memory, if necessary.
-  escher::GpuMemPtr gpu_mem =
-      (memory_offset > 0 || num_bytes < memory->size())
-          ? memory->GetGpuMem()->Suballocate(num_bytes, memory_offset)
-          : memory->GetGpuMem();
-
-  return fxl::MakeRefCounted<Buffer>(this, id, std::move(gpu_mem),
-                                     std::move(memory));
-}
-
-ResourcePtr Session::CreateScene(ResourceId id,
-                                 ::fuchsia::ui::gfx::SceneArgs args) {
-  return fxl::MakeRefCounted<Scene>(this, id);
-}
-
-ResourcePtr Session::CreateCamera(ResourceId id,
-                                  ::fuchsia::ui::gfx::CameraArgs args) {
-  if (auto scene = resources_.FindResource<Scene>(args.scene_id)) {
-    return fxl::MakeRefCounted<Camera>(this, id, std::move(scene));
-  }
-  return ResourcePtr();
-}
-
-ResourcePtr Session::CreateStereoCamera(
-    ResourceId id, const ::fuchsia::ui::gfx::StereoCameraArgs args) {
-  if (auto scene = resources_.FindResource<Scene>(args.scene_id)) {
-    return fxl::MakeRefCounted<StereoCamera>(this, id, std::move(scene));
-  }
-  return ResourcePtr();
-}
-
-ResourcePtr Session::CreateRenderer(ResourceId id,
-                                    ::fuchsia::ui::gfx::RendererArgs args) {
-  return fxl::MakeRefCounted<Renderer>(this, id);
-}
-
-ResourcePtr Session::CreateAmbientLight(ResourceId id) {
-  return fxl::MakeRefCounted<AmbientLight>(this, id);
-}
-
-ResourcePtr Session::CreateDirectionalLight(ResourceId id) {
-  return fxl::MakeRefCounted<DirectionalLight>(this, id);
-}
-
-ResourcePtr Session::CreateView(ResourceId id,
-                                ::fuchsia::ui::gfx::ViewArgs args) {
-  ViewLinker* view_linker = session_context_.view_linker;
-  ViewLinker::ImportLink link =
-      view_linker->CreateImport(std::move(args.token), error_reporter());
-
-  // Create a View if the Link was successfully registered.
-  if (link.valid()) {
-    return fxl::MakeRefCounted<View>(this, id, std::move(link));
-  }
-  return nullptr;
-}
-
-ResourcePtr Session::CreateViewHolder(ResourceId id,
-                                      ::fuchsia::ui::gfx::ViewHolderArgs args) {
-  ViewLinker* view_linker = session_context_.view_linker;
-  ViewLinker::ExportLink link =
-      view_linker->CreateExport(std::move(args.token), error_reporter());
-
-  // Create a ViewHolder if the Link was successfully registered.
-  if (link.valid()) {
-    return fxl::MakeRefCounted<ViewHolder>(this, id, std::move(link));
-  }
-  return nullptr;
-}
-
-ResourcePtr Session::CreateClipNode(ResourceId id,
-                                    ::fuchsia::ui::gfx::ClipNodeArgs args) {
-  error_reporter_->ERROR() << "scenic_impl::gfx::Session::CreateClipNode(): "
-                              "unimplemented.";
-  return ResourcePtr();
-}
-
-ResourcePtr Session::CreateEntityNode(ResourceId id,
-                                      ::fuchsia::ui::gfx::EntityNodeArgs args) {
-  return fxl::MakeRefCounted<EntityNode>(this, id);
-}
-
-ResourcePtr Session::CreateOpacityNode(
-    ResourceId id, ::fuchsia::ui::gfx::OpacityNodeArgs args) {
-  return fxl::MakeRefCounted<OpacityNode>(this, id);
-}
-
-ResourcePtr Session::CreateShapeNode(ResourceId id,
-                                     ::fuchsia::ui::gfx::ShapeNodeArgs args) {
-  return fxl::MakeRefCounted<ShapeNode>(this, id);
-}
-
-ResourcePtr Session::CreateCompositor(ResourceId id,
-                                      ::fuchsia::ui::gfx::CompositorArgs args) {
-  return Compositor::New(this, id, session_context_.scene_graph);
-}
-
-ResourcePtr Session::CreateDisplayCompositor(
-    ResourceId id, ::fuchsia::ui::gfx::DisplayCompositorArgs args) {
-  Display* display = session_context_.display_manager->default_display();
-  if (!display) {
-    error_reporter_->ERROR() << "There is no default display available.";
-    return nullptr;
-  }
-
-  if (display->is_claimed()) {
-    error_reporter_->ERROR() << "The default display has already been claimed "
-                                "by another compositor.";
-    return nullptr;
-  }
-
-  return fxl::AdoptRef(new DisplayCompositor(
-      this, id, session_context_.scene_graph, display,
-      SwapchainFactory::CreateDisplaySwapchain(
-          display, session_context_.display_manager,
-          session_context_.event_timestamper, session_context_.escher)));
-}
-
-ResourcePtr Session::CreateImagePipeCompositor(
-    ResourceId id, ::fuchsia::ui::gfx::ImagePipeCompositorArgs args) {
-  // TODO(SCN-179)
-  error_reporter_->ERROR()
-      << "scenic_impl::gfx::Session::ApplyCreateImagePipeCompositor() "
-         "is unimplemented (SCN-179)";
-  return ResourcePtr();
-}
-
-ResourcePtr Session::CreateLayerStack(ResourceId id,
-                                      ::fuchsia::ui::gfx::LayerStackArgs args) {
-  return fxl::MakeRefCounted<LayerStack>(this, id);
-}
-
-ResourcePtr Session::CreateVariable(ResourceId id,
-                                    ::fuchsia::ui::gfx::VariableArgs args) {
-  fxl::RefPtr<Variable> variable;
-  switch (args.type) {
-    case ::fuchsia::ui::gfx::ValueType::kVector1:
-      variable = fxl::MakeRefCounted<FloatVariable>(this, id);
-      break;
-    case ::fuchsia::ui::gfx::ValueType::kVector2:
-      variable = fxl::MakeRefCounted<Vector2Variable>(this, id);
-      break;
-    case ::fuchsia::ui::gfx::ValueType::kVector3:
-      variable = fxl::MakeRefCounted<Vector3Variable>(this, id);
-      break;
-    case ::fuchsia::ui::gfx::ValueType::kVector4:
-      variable = fxl::MakeRefCounted<Vector4Variable>(this, id);
-      break;
-    case ::fuchsia::ui::gfx::ValueType::kMatrix4:
-      variable = fxl::MakeRefCounted<Matrix4x4Variable>(this, id);
-      break;
-    case ::fuchsia::ui::gfx::ValueType::kColorRgb:
-      // not yet supported
-      variable = nullptr;
-      break;
-    case ::fuchsia::ui::gfx::ValueType::kColorRgba:
-      // not yet supported
-      variable = nullptr;
-      break;
-    case ::fuchsia::ui::gfx::ValueType::kQuaternion:
-      variable = fxl::MakeRefCounted<QuaternionVariable>(this, id);
-      break;
-    case ::fuchsia::ui::gfx::ValueType::kFactoredTransform:
-      /* variable = fxl::MakeRefCounted<TransformVariable>(this, id); */
-      variable = nullptr;
-      break;
-    case ::fuchsia::ui::gfx::ValueType::kNone:
-      break;
-  }
-  if (variable && variable->SetValue(args.initial_value)) {
-    return variable;
-  }
-  return nullptr;
-}
-
-ResourcePtr Session::CreateLayer(ResourceId id,
-                                 ::fuchsia::ui::gfx::LayerArgs args) {
-  return fxl::MakeRefCounted<Layer>(this, id);
-}
-
-ResourcePtr Session::CreateCircle(ResourceId id, float initial_radius) {
-  return fxl::MakeRefCounted<CircleShape>(this, id, initial_radius);
-}
-
-ResourcePtr Session::CreateRectangle(ResourceId id, float width, float height) {
-  return fxl::MakeRefCounted<RectangleShape>(this, id, width, height);
-}
-
-ResourcePtr Session::CreateRoundedRectangle(CommandContext* command_context,
-                                            ResourceId id, float width,
-                                            float height, float top_left_radius,
-                                            float top_right_radius,
-                                            float bottom_right_radius,
-                                            float bottom_left_radius) {
-  auto factory = session_context_.escher_rounded_rect_factory;
-  if (!factory) {
-    error_reporter_->ERROR()
-        << "scenic_impl::gfx::Session::CreateRoundedRectangle(): "
-           "no RoundedRectFactory available.";
-    return ResourcePtr();
-  }
-
-  // If radii sum exceeds width or height, scale them down.
-  float top_radii_sum = top_left_radius + top_right_radius;
-  float top_scale = std::min(width / top_radii_sum, 1.f);
-
-  float bottom_radii_sum = bottom_left_radius + bottom_right_radius;
-  float bottom_scale = std::min(width / bottom_radii_sum, 1.f);
-
-  float left_radii_sum = top_left_radius + bottom_left_radius;
-  float left_scale = std::min(height / left_radii_sum, 1.f);
-
-  float right_radii_sum = top_right_radius + bottom_right_radius;
-  float right_scale = std::min(height / right_radii_sum, 1.f);
-
-  top_left_radius *= std::min(top_scale, left_scale);
-  top_right_radius *= std::min(top_scale, right_scale);
-  bottom_left_radius *= std::min(bottom_scale, left_scale);
-  bottom_right_radius *= std::min(bottom_scale, right_scale);
-
-  escher::RoundedRectSpec rect_spec(width, height, top_left_radius,
-                                    top_right_radius, bottom_right_radius,
-                                    bottom_left_radius);
-  escher::MeshSpec mesh_spec{escher::MeshAttribute::kPosition2D |
-                             escher::MeshAttribute::kUV};
-
-  return fxl::MakeRefCounted<RoundedRectangleShape>(
-      this, id, rect_spec,
-      factory->NewRoundedRect(rect_spec, mesh_spec,
-                              command_context->batch_gpu_uploader()));
-}
-
-ResourcePtr Session::CreateMesh(ResourceId id) {
-  return fxl::MakeRefCounted<MeshShape>(this, id);
-}
-
-ResourcePtr Session::CreateMaterial(ResourceId id) {
-  return fxl::MakeRefCounted<Material>(this, id);
-}
-
-void Session::TearDown() {
-  if (!is_valid_) {
-    // TearDown already called.
-    return;
-  }
-  is_valid_ = false;
+Session::~Session() {
   resources_.Clear();
   scheduled_image_pipe_updates_ = {};
 
-  // We assume the channel for the associated ::fuchsia::ui::gfx::Session is
-  // closed because SessionHandler closes it before calling this method. The
-  // channel *must* be closed before we clear |scheduled_updates_|, since it
-  // contains pending callbacks to ::fuchsia::ui::gfx::Session::Present(); if it
-  // were not closed, we would have to invoke those callbacks before destroying
-  // them.
+  // We assume the channel for the associated gfx::Session is closed by
+  // SessionHandler before this point, since |scheduled_updates_| contains
+  // pending callbacks to gfx::Session::Present(). If the channel was not closed
+  // we would have to invoke those callbacks before destroying them.
   scheduled_updates_ = {};
   fences_to_release_on_next_update_.clear();
 
@@ -1429,12 +85,14 @@
     auto exported_count =
         session_context_.resource_linker->NumExportsForSession(this);
     FXL_CHECK(resource_count_ == 0)
-        << "Session::TearDown(): Not all resources have been collected. "
+        << "Session::~Session(): Not all resources have been collected. "
            "Exported resources: "
         << exported_count
         << ", total outstanding resources: " << resource_count_;
   }
   error_reporter_ = nullptr;
+
+  weak_factory_.InvalidateWeakPtrs();
 }
 
 ErrorReporter* Session::error_reporter() const {
@@ -1443,134 +101,106 @@
 
 EventReporter* Session::event_reporter() const { return event_reporter_; }
 
-bool Session::AssertValueIsOfType(const ::fuchsia::ui::gfx::Value& value,
-                                  const ::fuchsia::ui::gfx::Value::Tag* tags,
-                                  size_t tag_count) {
-  using ::operator<<;  // From print_commands.h
-  FXL_DCHECK(tag_count > 0);
-  for (size_t i = 0; i < tag_count; ++i) {
-    if (value.Which() == tags[i]) {
-      return true;
-    }
-  }
-  std::ostringstream str;
-  if (tag_count == 1) {
-    str << ", which is not the expected type: " << tags[0] << ".";
-  } else {
-    str << ", which is not one of the expected types (" << tags[0];
-    for (size_t i = 1; i < tag_count; ++i) {
-      str << ", " << tags[i];
-    }
-    str << ").";
-  }
-  error_reporter_->ERROR()
-      << "scenic_impl::gfx::Session: received value of type: " << value.Which()
-      << str.str();
-  return false;
-}
-
 bool Session::ScheduleUpdate(
     uint64_t requested_presentation_time,
     std::vector<::fuchsia::ui::gfx::Command> commands,
     ::std::vector<zx::event> acquire_fences,
     ::std::vector<zx::event> release_events,
     fuchsia::ui::scenic::Session::PresentCallback callback) {
-  if (is_valid()) {
-    uint64_t last_scheduled_presentation_time =
-        last_applied_update_presentation_time_;
-    if (!scheduled_updates_.empty()) {
-      last_scheduled_presentation_time =
-          std::max(last_scheduled_presentation_time,
-                   scheduled_updates_.back().presentation_time);
-    }
-
-    if (requested_presentation_time < last_scheduled_presentation_time) {
-      error_reporter_->ERROR()
-          << "scenic_impl::gfx::Session: Present called with out-of-order "
-             "presentation time. "
-          << "requested presentation time=" << requested_presentation_time
-          << ", last scheduled presentation time="
-          << last_scheduled_presentation_time << ".";
-      return false;
-    }
-
-    // If we're not running headless, warn if the requested presesentation time
-    // is not reasonable.
-    if (session_context_.frame_scheduler &&
-        session_context_.display_manager->default_display()) {
-      std::pair<zx_time_t, zx_time_t> target_times =
-          session_context_.frame_scheduler
-              ->ComputeTargetPresentationAndWakeupTimes(
-                  requested_presentation_time);
-      uint64_t target_presentation_time = target_times.first;
-
-      zx_time_t vsync_interval =
-          session_context_.display_manager->default_display()
-              ->GetVsyncInterval();
-      // TODO(SCN-723): Re-enable warning when requested_presentation_time == 0
-      // after Flutter engine is fixed.
-      if (requested_presentation_time != 0 &&
-          target_presentation_time - vsync_interval >
-              requested_presentation_time) {
-        // Present called with too early of a presentation time.
-        TRACE_INSTANT("gfx", "Session requested too early presentation time",
-                      TRACE_SCOPE_PROCESS, "session_id", id(),
-                      "requested presentation time",
-                      requested_presentation_time, "target presentation time",
-                      target_presentation_time);
-      }
-    }
-    auto acquire_fence_set =
-        std::make_unique<escher::FenceSetListener>(std::move(acquire_fences));
-    // TODO: Consider calling ScheduleUpdateForSession immediately if
-    // acquire_fence_set is already ready (which is the case if there are
-    // zero acquire fences).
-
-    acquire_fence_set->WaitReadyAsync(
-        [weak = weak_factory_.GetWeakPtr(), requested_presentation_time] {
-          if (weak)
-            weak->session_context_.session_manager->ScheduleUpdateForSession(
-                weak->session_context_.update_scheduler,
-                requested_presentation_time, SessionPtr(weak.get()));
-        });
-
-    scheduled_updates_.push(
-        Update{requested_presentation_time, std::move(commands),
-               std::move(acquire_fence_set), std::move(release_events),
-               std::move(callback)});
+  uint64_t last_scheduled_presentation_time =
+      last_applied_update_presentation_time_;
+  if (!scheduled_updates_.empty()) {
+    last_scheduled_presentation_time =
+        std::max(last_scheduled_presentation_time,
+                 scheduled_updates_.back().presentation_time);
   }
+
+  if (requested_presentation_time < last_scheduled_presentation_time) {
+    error_reporter_->ERROR()
+        << "scenic_impl::gfx::Session: Present called with out-of-order "
+           "presentation time. "
+        << "requested presentation time=" << requested_presentation_time
+        << ", last scheduled presentation time="
+        << last_scheduled_presentation_time << ".";
+    return false;
+  }
+
+  // If we're not running headless, warn if the requested presesentation time
+  // is not reasonable.
+  if (session_context_.frame_scheduler &&
+      session_context_.display_manager->default_display()) {
+    std::pair<zx_time_t, zx_time_t> target_times =
+        session_context_.frame_scheduler
+            ->ComputeTargetPresentationAndWakeupTimes(
+                requested_presentation_time);
+    uint64_t target_presentation_time = target_times.first;
+    zx_time_t vsync_interval =
+        session_context_.display_manager->default_display()->GetVsyncInterval();
+    // TODO(SCN-723): Re-enable warning when requested_presentation_time == 0
+    // after Flutter engine is fixed.
+    if (requested_presentation_time != 0 &&
+        target_presentation_time - vsync_interval >
+            requested_presentation_time) {
+      // Present called with too early of a presentation time.
+      TRACE_INSTANT("gfx", "Session requested too early presentation time",
+                    TRACE_SCOPE_PROCESS, "session_id", id(),
+                    "requested presentation time", requested_presentation_time,
+                    "target presentation time", target_presentation_time);
+    }
+  }
+
+  auto acquire_fence_set =
+      std::make_unique<escher::FenceSetListener>(std::move(acquire_fences));
+  // TODO: Consider calling ScheduleUpdateForSession immediately if
+  // acquire_fence_set is already ready (which is the case if there are
+  // zero acquire fences).
+
+  acquire_fence_set->WaitReadyAsync(
+      [weak = GetWeakPtr(), requested_presentation_time] {
+        if (weak) {
+          weak->session_context_.session_manager->ScheduleUpdateForSession(
+              weak->session_context_.update_scheduler,
+              requested_presentation_time, std::move(weak));
+        }
+      });
+
+  scheduled_updates_.push(
+      Update{requested_presentation_time, std::move(commands),
+             std::move(acquire_fence_set), std::move(release_events),
+             std::move(callback)});
+
   return true;
 }
 
 void Session::ScheduleImagePipeUpdate(uint64_t presentation_time,
                                       ImagePipePtr image_pipe) {
   FXL_DCHECK(image_pipe);
-  if (is_valid()) {
-    scheduled_image_pipe_updates_.push(
-        {presentation_time, std::move(image_pipe)});
+  scheduled_image_pipe_updates_.push(
+      {presentation_time, std::move(image_pipe)});
 
-    session_context_.session_manager->ScheduleUpdateForSession(
-        session_context_.update_scheduler, presentation_time, SessionPtr(this));
-  }
+  session_context_.session_manager->ScheduleUpdateForSession(
+      session_context_.update_scheduler, presentation_time, GetWeakPtr());
 }
 
-bool Session::ApplyScheduledUpdates(CommandContext* command_context,
-                                    uint64_t presentation_time,
-                                    uint64_t presentation_interval) {
+Session::ApplyUpdateResult Session::ApplyScheduledUpdates(
+    CommandContext* command_context, uint64_t presentation_time,
+    uint64_t presentation_interval) {
   TRACE_DURATION("gfx", "Session::ApplyScheduledUpdates", "session_id", id_,
                  "session_debug_name", debug_name_, "time", presentation_time,
                  "interval", presentation_interval);
 
+  ApplyUpdateResult update_results{false, false};
+
   if (presentation_time < last_presentation_time_) {
     error_reporter_->ERROR()
         << "scenic_impl::gfx::Session: ApplyScheduledUpdates called with "
            "presentation_time="
         << presentation_time << ", which is less than last_presentation_time_="
         << last_presentation_time_ << ".";
-    return false;
+    update_results.success = false;
+    return update_results;
   }
 
-  bool needs_render = false;
   while (!scheduled_updates_.empty() &&
          scheduled_updates_.front().presentation_time <= presentation_time) {
     if (!scheduled_updates_.front().acquire_fences->ready()) {
@@ -1584,7 +214,7 @@
     }
     if (ApplyUpdate(command_context,
                     std::move(scheduled_updates_.front().commands))) {
-      needs_render = true;
+      update_results.needs_render = true;
       auto info = fuchsia::images::PresentationInfo();
       info.presentation_time = presentation_time;
       info.presentation_interval = presentation_interval;
@@ -1611,14 +241,13 @@
       FXL_LOG(WARNING) << "scenic_impl::gfx::Session::ApplyScheduledUpdates(): "
                           "An error was encountered while applying the update. "
                           "Initiating teardown.";
-
+      update_results.success = false;
       scheduled_updates_ = {};
 
-      BeginTearDown();
-
       // Tearing down a session will very probably result in changes to
       // the global scene-graph.
-      return true;
+      update_results.needs_render = true;
+      return update_results;
     }
   }
 
@@ -1629,42 +258,36 @@
     // The bool returned from Update() is 0 or 1, and needs_render is 0 or 1, so
     // bitwise |= is used which doesn't short-circuit.
     if (scheduled_image_pipe_updates_.top().image_pipe) {
-      needs_render |= scheduled_image_pipe_updates_.top().image_pipe->Update(
-          session_context_.release_fence_signaller, presentation_time,
-          presentation_interval);
+      update_results.needs_render |=
+          scheduled_image_pipe_updates_.top().image_pipe->Update(
+              session_context_.release_fence_signaller, presentation_time,
+              presentation_interval);
     }
     scheduled_image_pipe_updates_.pop();
   }
 
-  return needs_render;
+  update_results.success = true;
+  return update_results;
 }
 
 void Session::EnqueueEvent(::fuchsia::ui::gfx::Event event) {
-  if (!is_valid()) {
-    return;
-  }
   event_reporter_->EnqueueEvent(std::move(event));
 }
 
 void Session::EnqueueEvent(::fuchsia::ui::input::InputEvent event) {
-  if (!is_valid()) {
-    return;
-  }
   event_reporter_->EnqueueEvent(std::move(event));
 }
 
 bool Session::ApplyUpdate(CommandContext* command_context,
                           std::vector<::fuchsia::ui::gfx::Command> commands) {
   TRACE_DURATION("gfx", "Session::ApplyUpdate");
-  if (is_valid()) {
-    for (auto& command : commands) {
-      if (!ApplyCommand(command_context, std::move(command))) {
-        using ::operator<<;  // From print_commands.h
-        error_reporter_->ERROR() << "scenic_impl::gfx::Session::ApplyCommand() "
-                                    "failed to apply Command: "
-                                 << command;
-        return false;
-      }
+  for (auto& command : commands) {
+    if (!ApplyCommand(command_context, std::move(command))) {
+      using ::operator<<;  // From print_commands.h
+      error_reporter_->ERROR() << "scenic_impl::gfx::Session::ApplyCommand() "
+                                  "failed to apply Command: "
+                               << command;
+      return false;
     }
   }
   return true;
@@ -1709,9 +332,5 @@
   callback(WrapHits(layer_stack_hits));
 }
 
-void Session::BeginTearDown() {
-  session_context_.session_manager->TearDownSession(id());
-}
-
 }  // namespace gfx
 }  // namespace scenic_impl
diff --git a/lib/ui/gfx/engine/session.h b/lib/ui/gfx/engine/session.h
index 6107cd5..bd2baaa 100644
--- a/lib/ui/gfx/engine/session.h
+++ b/lib/ui/gfx/engine/session.h
@@ -10,11 +10,12 @@
 #include <fuchsia/ui/gfx/cpp/fidl.h>
 #include <fuchsia/ui/scenic/cpp/fidl.h>
 
+#include "garnet/lib/ui/gfx/engine/gfx_command_applier.h"
 #include "garnet/lib/ui/gfx/engine/resource_map.h"
 #include "garnet/lib/ui/gfx/engine/session_context.h"
+#include "garnet/lib/ui/gfx/engine/session_manager.h"
 #include "garnet/lib/ui/gfx/id.h"
 #include "garnet/lib/ui/gfx/resources/memory.h"
-#include "garnet/lib/ui/gfx/resources/resource.h"
 #include "garnet/lib/ui/gfx/resources/resource_context.h"
 #include "garnet/lib/ui/scenic/event_reporter.h"
 #include "garnet/lib/ui/scenic/util/error_reporter.h"
@@ -26,27 +27,23 @@
 namespace scenic_impl {
 namespace gfx {
 
-class Image;
-using ImagePtr = ::fxl::RefPtr<Image>;
-
-class ImageBase;
-using ImageBasePtr = ::fxl::RefPtr<ImageBase>;
-
 class ImagePipe;
-using ImagePipePtr = ::fxl::RefPtr<ImagePipe>;
-
-class Session;
-using SessionPtr = ::fxl::RefPtr<Session>;
+using ImagePipePtr = fxl::RefPtr<ImagePipe>;
 
 class CommandContext;
 class Engine;
 class Resource;
-class SessionHandler;
 
-// TODO: use unsafe ref-counting for better performance (our architecture
-// guarantees that this is safe).
-class Session : public fxl::RefCountedThreadSafe<Session> {
+// gfx::Session is the internal endpoint of the scenic::Session channel.
+// It owns, and is responsible for, all graphics state on the channel
+class Session {
  public:
+  // Return type for ApplyScheduledUpdate
+  struct ApplyUpdateResult {
+    bool success;
+    bool needs_render;
+  };
+
   Session(SessionId id, SessionContext context,
           EventReporter* event_reporter = EventReporter::Default(),
           ErrorReporter* error_reporter = ErrorReporter::Default());
@@ -56,10 +53,17 @@
   // successful, and false if the op is somehow invalid.  In the latter case,
   // the Session is left unchanged.
   bool ApplyCommand(CommandContext* command_context,
-                    ::fuchsia::ui::gfx::Command command);
+                    fuchsia::ui::gfx::Command command) {
+    return GfxCommandApplier::ApplyCommand(this, command_context,
+                                           std::move(command));
+  }
 
   SessionId id() const { return id_; }
 
+  const fxl::WeakPtr<Session> GetWeakPtr() {
+    return weak_factory_.GetWeakPtr();
+  }
+  const SessionContext& session_context() const { return session_context_; }
   const ResourceContext& resource_context() const { return resource_context_; }
 
   // Return the total number of existing resources associated with this Session.
@@ -71,9 +75,6 @@
   // exist if it is referenced by other resources.
   size_t GetMappedResourceCount() const { return resources_.size(); }
 
-  // Session becomes invalid once TearDown is called.
-  bool is_valid() const { return is_valid_; }
-
   ErrorReporter* error_reporter() const;  // Never nullptr.
   EventReporter* event_reporter() const;  // Never nullptr.
 
@@ -83,8 +84,8 @@
   // applying them; they will later be applied by ApplyScheduledUpdates().
   bool ScheduleUpdate(uint64_t presentation_time,
                       std::vector<::fuchsia::ui::gfx::Command> commands,
-                      ::std::vector<zx::event> acquire_fences,
-                      ::std::vector<zx::event> release_fences,
+                      std::vector<zx::event> acquire_fences,
+                      std::vector<zx::event> release_fences,
                       fuchsia::ui::scenic::Session::PresentCallback callback);
 
   // Called by ImagePipe::PresentImage().  Stashes the arguments without
@@ -93,218 +94,32 @@
                                ImagePipePtr image_pipe);
 
   // Called by Engine() when it is notified by the FrameScheduler that
-  // a frame should be rendered for the specified |presentation_time|.  Return
-  // true if any updates were applied, and false otherwise.
-  bool ApplyScheduledUpdates(CommandContext* command_context,
-                             uint64_t presentation_time,
-                             uint64_t presentation_interval);
+  // a frame should be rendered for the specified |presentation_time|.
+  // Returns ApplyUpdateResult.success as true if updates were successfully
+  // applied, false if updates failed to be applied.
+  // Returns ApplyUpdateResult.needs_render as true if any changes were
+  // applied, false if none were.
+  ApplyUpdateResult ApplyScheduledUpdates(CommandContext* command_context,
+                                          uint64_t presentation_time,
+                                          uint64_t presentation_interval);
 
   // Convenience.  Forwards an event to the EventReporter.
   void EnqueueEvent(::fuchsia::ui::gfx::Event event);
   void EnqueueEvent(::fuchsia::ui::input::InputEvent event);
 
   // Called by SessionHandler::HitTest().
-  void HitTest(uint32_t node_id, ::fuchsia::ui::gfx::vec3 ray_origin,
-               ::fuchsia::ui::gfx::vec3 ray_direction,
+  void HitTest(uint32_t node_id, fuchsia::ui::gfx::vec3 ray_origin,
+               fuchsia::ui::gfx::vec3 ray_direction,
                fuchsia::ui::scenic::Session::HitTestCallback callback);
 
   // Called by SessionHandler::HitTestDeviceRay().
   void HitTestDeviceRay(::fuchsia::ui::gfx::vec3 ray_origin,
-                        ::fuchsia::ui::gfx::vec3 ray_direction,
+                        fuchsia::ui::gfx::vec3 ray_direction,
                         fuchsia::ui::scenic::Session::HitTestCallback callback);
 
   void SetDebugName(const std::string& debug_name) { debug_name_ = debug_name; }
 
- protected:
-  friend class SessionHandler;
-  // Called only by SessionHandler. Use BeginTearDown() instead when you need to
-  // teardown from within Session. Virtual to allow test subclasses to override.
-  //
-  // The chain of events is:
-  // Session::BeginTearDown or SessionHandler::BeginTearDown
-  // => Engine::TearDownSession
-  // => SessionHandler::TearDown
-  // => Session::TearDown
-  //
-  // We are guaranteed that by the time TearDown() is closed, SessionHandler
-  // has destroyed the channel to this session.
-  virtual void TearDown();
-
  private:
-  // Called internally to initiate teardown.
-  void BeginTearDown();
-
-  // Cmderation application functions, called by ApplyCommand().
-  bool ApplyCreateResourceCmd(CommandContext* command_context,
-                              ::fuchsia::ui::gfx::CreateResourceCmd command);
-  bool ApplyReleaseResourceCmd(::fuchsia::ui::gfx::ReleaseResourceCmd command);
-  bool ApplyExportResourceCmd(::fuchsia::ui::gfx::ExportResourceCmd command);
-  bool ApplyImportResourceCmd(::fuchsia::ui::gfx::ImportResourceCmd command);
-  bool ApplyAddChildCmd(::fuchsia::ui::gfx::AddChildCmd command);
-  bool ApplyAddPartCmd(::fuchsia::ui::gfx::AddPartCmd command);
-  bool ApplyDetachCmd(::fuchsia::ui::gfx::DetachCmd command);
-  bool ApplyDetachChildrenCmd(::fuchsia::ui::gfx::DetachChildrenCmd command);
-  bool ApplySetTagCmd(::fuchsia::ui::gfx::SetTagCmd command);
-  bool ApplySetTranslationCmd(::fuchsia::ui::gfx::SetTranslationCmd command);
-  bool ApplySetScaleCmd(::fuchsia::ui::gfx::SetScaleCmd command);
-  bool ApplySetRotationCmd(::fuchsia::ui::gfx::SetRotationCmd command);
-  bool ApplySetAnchorCmd(::fuchsia::ui::gfx::SetAnchorCmd command);
-  bool ApplySetSizeCmd(::fuchsia::ui::gfx::SetSizeCmd command);
-  bool ApplySetOpacityCmd(::fuchsia::ui::gfx::SetOpacityCmd command);
-  bool ApplySendSizeChangeHintCmd(
-      ::fuchsia::ui::gfx::SendSizeChangeHintCmdHACK command);
-  bool ApplySetShapeCmd(::fuchsia::ui::gfx::SetShapeCmd command);
-  bool ApplySetMaterialCmd(::fuchsia::ui::gfx::SetMaterialCmd command);
-  bool ApplySetClipCmd(::fuchsia::ui::gfx::SetClipCmd command);
-  bool ApplySetViewPropertiesCmd(
-      ::fuchsia::ui::gfx::SetViewPropertiesCmd command);
-  bool ApplySetHitTestBehaviorCmd(
-      ::fuchsia::ui::gfx::SetHitTestBehaviorCmd command);
-  bool ApplySetCameraCmd(::fuchsia::ui::gfx::SetCameraCmd command);
-  bool ApplySetCameraTransformCmd(
-      ::fuchsia::ui::gfx::SetCameraTransformCmd command);
-  bool ApplySetCameraProjectionCmd(
-      ::fuchsia::ui::gfx::SetCameraProjectionCmd command);
-  bool ApplySetStereoCameraProjectionCmd(
-      ::fuchsia::ui::gfx::SetStereoCameraProjectionCmd command);
-  bool ApplySetCameraPoseBufferCmd(
-      ::fuchsia::ui::gfx::SetCameraPoseBufferCmd command);
-  bool ApplySetLightColorCmd(::fuchsia::ui::gfx::SetLightColorCmd command);
-  bool ApplySetLightDirectionCmd(
-      ::fuchsia::ui::gfx::SetLightDirectionCmd command);
-  bool ApplyAddLightCmd(::fuchsia::ui::gfx::AddLightCmd command);
-  bool ApplyDetachLightCmd(::fuchsia::ui::gfx::DetachLightCmd command);
-  bool ApplyDetachLightsCmd(::fuchsia::ui::gfx::DetachLightsCmd command);
-  bool ApplySetTextureCmd(::fuchsia::ui::gfx::SetTextureCmd command);
-  bool ApplySetColorCmd(::fuchsia::ui::gfx::SetColorCmd command);
-  bool ApplyBindMeshBuffersCmd(::fuchsia::ui::gfx::BindMeshBuffersCmd command);
-  bool ApplyAddLayerCmd(::fuchsia::ui::gfx::AddLayerCmd command);
-  bool ApplyRemoveLayerCmd(::fuchsia::ui::gfx::RemoveLayerCmd command);
-  bool ApplyRemoveAllLayersCmd(::fuchsia::ui::gfx::RemoveAllLayersCmd command);
-  bool ApplySetLayerStackCmd(::fuchsia::ui::gfx::SetLayerStackCmd command);
-  bool ApplySetRendererCmd(::fuchsia::ui::gfx::SetRendererCmd command);
-  bool ApplySetRendererParamCmd(
-      ::fuchsia::ui::gfx::SetRendererParamCmd command);
-  bool ApplySetEventMaskCmd(::fuchsia::ui::gfx::SetEventMaskCmd command);
-  bool ApplySetLabelCmd(::fuchsia::ui::gfx::SetLabelCmd command);
-  bool ApplySetDisableClippingCmd(
-      ::fuchsia::ui::gfx::SetDisableClippingCmd command);
-
-  // Resource creation functions, called by ApplyCreateResourceCmd().
-  bool ApplyCreateMemory(ResourceId id, ::fuchsia::ui::gfx::MemoryArgs args);
-  bool ApplyCreateImage(ResourceId id, ::fuchsia::ui::gfx::ImageArgs args);
-  bool ApplyCreateImagePipe(ResourceId id,
-                            ::fuchsia::ui::gfx::ImagePipeArgs args);
-  bool ApplyCreateBuffer(ResourceId id, ::fuchsia::ui::gfx::BufferArgs args);
-  bool ApplyCreateScene(ResourceId id, ::fuchsia::ui::gfx::SceneArgs args);
-  bool ApplyCreateCamera(ResourceId id, ::fuchsia::ui::gfx::CameraArgs args);
-  bool ApplyCreateStereoCamera(ResourceId id,
-                               ::fuchsia::ui::gfx::StereoCameraArgs args);
-  bool ApplyCreateRenderer(ResourceId id,
-                           ::fuchsia::ui::gfx::RendererArgs args);
-  bool ApplyCreateAmbientLight(ResourceId id,
-                               ::fuchsia::ui::gfx::AmbientLightArgs args);
-  bool ApplyCreateDirectionalLight(
-      ResourceId id, ::fuchsia::ui::gfx::DirectionalLightArgs args);
-  bool ApplyCreateRectangle(ResourceId id,
-                            ::fuchsia::ui::gfx::RectangleArgs args);
-  bool ApplyCreateRoundedRectangle(
-      CommandContext* command_context, ResourceId id,
-      ::fuchsia::ui::gfx::RoundedRectangleArgs args);
-  bool ApplyCreateCircle(ResourceId id, ::fuchsia::ui::gfx::CircleArgs args);
-  bool ApplyCreateMesh(ResourceId id, ::fuchsia::ui::gfx::MeshArgs args);
-  bool ApplyCreateMaterial(ResourceId id,
-                           ::fuchsia::ui::gfx::MaterialArgs args);
-  bool ApplyCreateView(ResourceId id, ::fuchsia::ui::gfx::ViewArgs args);
-  bool ApplyCreateViewHolder(ResourceId id,
-                             ::fuchsia::ui::gfx::ViewHolderArgs args);
-  bool ApplyCreateClipNode(ResourceId id,
-                           ::fuchsia::ui::gfx::ClipNodeArgs args);
-  bool ApplyCreateEntityNode(ResourceId id,
-                             ::fuchsia::ui::gfx::EntityNodeArgs args);
-  bool ApplyCreateOpacityNode(ResourceId id,
-                              ::fuchsia::ui::gfx::OpacityNodeArgs args);
-  bool ApplyCreateShapeNode(ResourceId id,
-                            ::fuchsia::ui::gfx::ShapeNodeArgs args);
-  bool ApplyCreateCompositor(ResourceId id,
-                             ::fuchsia::ui::gfx::CompositorArgs args);
-  bool ApplyCreateDisplayCompositor(
-      ResourceId id, ::fuchsia::ui::gfx::DisplayCompositorArgs args);
-  bool ApplyCreateImagePipeCompositor(
-      ResourceId id, ::fuchsia::ui::gfx::ImagePipeCompositorArgs args);
-  bool ApplyCreateLayerStack(ResourceId id,
-                             ::fuchsia::ui::gfx::LayerStackArgs args);
-  bool ApplyCreateLayer(ResourceId id, ::fuchsia::ui::gfx::LayerArgs args);
-  bool ApplyCreateVariable(ResourceId id,
-                           ::fuchsia::ui::gfx::VariableArgs args);
-  bool ApplyTakeSnapshotCmdHACK(
-      ::fuchsia::ui::gfx::TakeSnapshotCmdHACK command);
-
-  // Actually create resources.
-  ResourcePtr CreateMemory(ResourceId id, ::fuchsia::ui::gfx::MemoryArgs args);
-  ResourcePtr CreateImage(ResourceId id, MemoryPtr memory,
-                          ::fuchsia::ui::gfx::ImageArgs args);
-  ResourcePtr CreateBuffer(ResourceId id, MemoryPtr memory,
-                           uint32_t memory_offset, uint32_t num_bytes);
-
-  ResourcePtr CreateScene(ResourceId id, ::fuchsia::ui::gfx::SceneArgs args);
-  ResourcePtr CreateCamera(ResourceId id, ::fuchsia::ui::gfx::CameraArgs args);
-  ResourcePtr CreateStereoCamera(ResourceId id,
-                                 ::fuchsia::ui::gfx::StereoCameraArgs args);
-  ResourcePtr CreateRenderer(ResourceId id,
-                             ::fuchsia::ui::gfx::RendererArgs args);
-
-  ResourcePtr CreateAmbientLight(ResourceId id);
-  ResourcePtr CreateDirectionalLight(ResourceId id);
-
-  ResourcePtr CreateView(ResourceId id, ::fuchsia::ui::gfx::ViewArgs args);
-  ResourcePtr CreateViewHolder(ResourceId id,
-                               ::fuchsia::ui::gfx::ViewHolderArgs args);
-  ResourcePtr CreateClipNode(ResourceId id,
-                             ::fuchsia::ui::gfx::ClipNodeArgs args);
-  ResourcePtr CreateEntityNode(ResourceId id,
-                               ::fuchsia::ui::gfx::EntityNodeArgs args);
-  ResourcePtr CreateOpacityNode(ResourceId id,
-                                ::fuchsia::ui::gfx::OpacityNodeArgs args);
-  ResourcePtr CreateShapeNode(ResourceId id,
-                              ::fuchsia::ui::gfx::ShapeNodeArgs args);
-
-  ResourcePtr CreateCompositor(ResourceId id,
-                               ::fuchsia::ui::gfx::CompositorArgs args);
-  ResourcePtr CreateDisplayCompositor(
-      ResourceId id, ::fuchsia::ui::gfx::DisplayCompositorArgs args);
-  ResourcePtr CreateImagePipeCompositor(
-      ResourceId id, ::fuchsia::ui::gfx::ImagePipeCompositorArgs args);
-  ResourcePtr CreateLayerStack(ResourceId id,
-                               ::fuchsia::ui::gfx::LayerStackArgs args);
-  ResourcePtr CreateLayer(ResourceId id, ::fuchsia::ui::gfx::LayerArgs args);
-  ResourcePtr CreateCircle(ResourceId id, float initial_radius);
-  ResourcePtr CreateRectangle(ResourceId id, float width, float height);
-  ResourcePtr CreateRoundedRectangle(CommandContext* command_context,
-                                     ResourceId id, float width, float height,
-                                     float top_left_radius,
-                                     float top_right_radius,
-                                     float bottom_right_radius,
-                                     float bottom_left_radius);
-  ResourcePtr CreateMesh(ResourceId id);
-  ResourcePtr CreateMaterial(ResourceId id);
-  ResourcePtr CreateVariable(ResourceId id,
-                             ::fuchsia::ui::gfx::VariableArgs args);
-
-  // Return false and log an error if the value is not of the expected type.
-  // NOTE: although failure does not halt execution of the program, it does
-  // indicate client error, and will be used by the caller to tear down the
-  // Session.
-  bool AssertValueIsOfType(const ::fuchsia::ui::gfx::Value& value,
-                           const ::fuchsia::ui::gfx::Value::Tag* tags,
-                           size_t tag_count);
-  template <size_t N>
-  bool AssertValueIsOfType(
-      const ::fuchsia::ui::gfx::Value& value,
-      const std::array<::fuchsia::ui::gfx::Value::Tag, N>& tags) {
-    return AssertValueIsOfType(value, tags.data(), N);
-  }
-
   friend class Resource;
   void IncrementResourceCount() { ++resource_count_; }
   void DecrementResourceCount() { --resource_count_; }
@@ -314,7 +129,7 @@
 
     std::vector<::fuchsia::ui::gfx::Command> commands;
     std::unique_ptr<escher::FenceSetListener> acquire_fences;
-    ::std::vector<zx::event> release_fences;
+    std::vector<zx::event> release_fences;
 
     // Callback to report when the update has been applied in response to
     // an invocation of |Session.Present()|.
@@ -323,7 +138,7 @@
   bool ApplyUpdate(CommandContext* command_context,
                    std::vector<::fuchsia::ui::gfx::Command> commands);
   std::queue<Update> scheduled_updates_;
-  ::std::vector<zx::event> fences_to_release_on_next_update_;
+  std::vector<zx::event> fences_to_release_on_next_update_;
 
   uint64_t last_applied_update_presentation_time_ = 0;
   uint64_t last_presentation_time_ = 0;
@@ -350,11 +165,8 @@
   // Resources; their lifecycle must exceed that of the Resources.
   SessionContext session_context_;
   ResourceContext resource_context_;
-
   ResourceMap resources_;
-
   size_t resource_count_ = 0;
-  bool is_valid_ = true;
 
   fxl::WeakPtrFactory<Session> weak_factory_;  // must be last
 };
diff --git a/lib/ui/gfx/engine/session_handler.cc b/lib/ui/gfx/engine/session_handler.cc
index 2ab7b52..1406226 100644
--- a/lib/ui/gfx/engine/session_handler.cc
+++ b/lib/ui/gfx/engine/session_handler.cc
@@ -4,6 +4,8 @@
 
 #include "garnet/lib/ui/gfx/engine/session_handler.h"
 
+#include <memory>
+
 #include "garnet/lib/ui/scenic/session.h"
 #include "lib/ui/scenic/cpp/commands.h"
 
@@ -20,15 +22,18 @@
       session_manager_(session_manager),
       event_reporter_(event_reporter),
       error_reporter_(error_reporter),
-      session_(::fxl::MakeRefCounted<scenic_impl::gfx::Session>(
-          session_id, std::move(session_context), event_reporter,
-          error_reporter)) {
+      session_(std::make_unique<Session>(session_id, std::move(session_context),
+                                         event_reporter, error_reporter)) {
   FXL_DCHECK(session_manager_);
 }
 
-SessionHandler::~SessionHandler() {
-  TearDown();
-  session_manager_->RemoveSession(session_->id());
+SessionHandler::~SessionHandler() { CleanUp(); }
+
+void SessionHandler::CleanUp() {
+  if (session_.get() != nullptr) {
+    session_manager_->RemoveSessionHandler(session_->id());
+    session_.reset();
+  }
 }
 
 void SessionHandler::Present(
@@ -40,8 +45,9 @@
           std::move(acquire_fences), std::move(release_fences),
           std::move(callback))) {
     BeginTearDown();
+  } else {
+    buffered_commands_.clear();
   }
-  buffered_commands_.clear();
 }
 
 void SessionHandler::HitTest(
@@ -65,17 +71,9 @@
 }
 
 void SessionHandler::BeginTearDown() {
-  session_manager_->TearDownSession(session_->id());
-  FXL_DCHECK(!session_->is_valid());
-}
-
-void SessionHandler::TearDown() {
-  session_->TearDown();
-
-  // Close the parent Mozart session.
-  if (context() && context()->session()) {
-    context()->scenic()->CloseSession(context()->session());
-  }
+  // Since this is essentially a self destruct
+  // call, it's safest not call anything after this
+  context()->KillSession();
 }
 
 }  // namespace gfx
diff --git a/lib/ui/gfx/engine/session_handler.h b/lib/ui/gfx/engine/session_handler.h
index e694996..30d136f 100644
--- a/lib/ui/gfx/engine/session_handler.h
+++ b/lib/ui/gfx/engine/session_handler.h
@@ -5,13 +5,13 @@
 #ifndef GARNET_LIB_UI_GFX_ENGINE_SESSION_HANDLER_H_
 #define GARNET_LIB_UI_GFX_ENGINE_SESSION_HANDLER_H_
 
+#include "garnet/lib/ui/gfx/engine/session.h"
 #include "lib/fidl/cpp/binding_set.h"
 #include "lib/fidl/cpp/interface_ptr_set.h"
 #include "lib/fxl/tasks/task_runner.h"
 
 #include <fuchsia/ui/scenic/cpp/fidl.h>
 #include "garnet/lib/ui/gfx/engine/engine.h"
-#include "garnet/lib/ui/gfx/engine/session.h"
 #include "garnet/lib/ui/scenic/command_dispatcher.h"
 #include "garnet/lib/ui/scenic/event_reporter.h"
 #include "garnet/lib/ui/scenic/util/error_reporter.h"
@@ -66,19 +66,21 @@
  private:
   friend class SessionManager;
 
-  // Called by |binding_| when the connection closes. Must be invoked within
-  // the SessionHandler MessageLoop.
+  // Called to initiate a session crash when an update fails.
+  // Requests the destruction of client fidl session, which
+  // then triggers the actual destruction of the SessionHandler
   void BeginTearDown();
 
-  // Called only by Engine. Use BeginTearDown() instead when you need to
-  // teardown from within SessionHandler.
-  void TearDown();
+  // Remove SessionHandler reference from SessionManager
+  // and destroy gfx::Session
+  void CleanUp();
 
   SessionManager* const session_manager_;
 
   EventReporter* const event_reporter_;
   ErrorReporter* const error_reporter_;
-  scenic_impl::gfx::SessionPtr session_;
+
+  std::unique_ptr<Session> session_;
 
   // TODO(SCN-710): We reallocate this everytime we std::move it into
   // ScheduleUpdate().  The bug has some ideas about how to do better.
diff --git a/lib/ui/gfx/engine/session_manager.cc b/lib/ui/gfx/engine/session_manager.cc
index 1e915e7..dff2a3b 100644
--- a/lib/ui/gfx/engine/session_manager.cc
+++ b/lib/ui/gfx/engine/session_manager.cc
@@ -28,9 +28,9 @@
   }
 }
 
-SessionHandler* SessionManager::FindSession(SessionId id) {
-  auto it = session_manager_.find(id);
-  if (it != session_manager_.end()) {
+SessionHandler* SessionManager::FindSessionHandler(SessionId id) {
+  auto it = session_handlers_.find(id);
+  if (it != session_handlers_.end()) {
     return it->second;
   }
   return nullptr;
@@ -51,18 +51,23 @@
   scenic_impl::Session* session = context.session();
   auto handler = CreateSessionHandler(std::move(context), engine, session_id,
                                       session, session->error_reporter());
-  session_manager_.insert({session_id, handler.get()});
-  ++session_count_;
-
+  InsertSessionHandler(session_id, handler.get());
   return handler;
 }
 
-void SessionManager::ScheduleUpdateForSession(UpdateScheduler* update_scheduler,
-                                              uint64_t presentation_time,
-                                              fxl::RefPtr<Session> session) {
+void SessionManager::InsertSessionHandler(SessionId session_id,
+                                          SessionHandler* session_handler) {
+  FXL_DCHECK(session_handlers_.find(session_id) == session_handlers_.end());
+  session_handlers_.insert({session_id, session_handler});
+  ++session_count_;
+}
+
+void SessionManager::ScheduleUpdateForSession(
+    UpdateScheduler* update_scheduler, uint64_t presentation_time,
+    fxl::WeakPtr<scenic_impl::gfx::Session> session) {
   FXL_DCHECK(update_scheduler);
-  if (session->is_valid()) {
-    updatable_sessions_.insert({presentation_time, std::move(session)});
+  if (session) {
+    updatable_sessions_.push({presentation_time, std::move(session)});
     update_scheduler->ScheduleUpdate(presentation_time);
   }
 }
@@ -75,14 +80,23 @@
 
   bool needs_render = false;
   while (!updatable_sessions_.empty()) {
-    auto top = updatable_sessions_.begin();
-    if (top->first > presentation_time)
+    auto& top = updatable_sessions_.top();
+    if (top.first > presentation_time)
       break;
-    auto session = std::move(top->second);
-    updatable_sessions_.erase(top);
-    if (session) {
-      needs_render |= session->ApplyScheduledUpdates(
+    auto session = std::move(top.second);
+    updatable_sessions_.pop();
+    if (session.get() != nullptr) {
+      auto update_results = session->ApplyScheduledUpdates(
           command_context, presentation_time, presentation_interval);
+
+      needs_render |= update_results.needs_render;
+
+      // If update fails, kill the entire client session
+      if (!update_results.success) {
+        auto session_handler = FindSessionHandler(session->id());
+        FXL_DCHECK(session_handler);
+        session_handler->BeginTearDown();
+      }
     } else {
       // Corresponds to a call to ScheduleUpdate(), which always triggers a
       // render.
@@ -92,26 +106,10 @@
   return needs_render;
 }
 
-void SessionManager::TearDownSession(SessionId id) {
-  auto it = session_manager_.find(id);
-  FXL_DCHECK(it != session_manager_.end());
-  if (it != session_manager_.end()) {
-    SessionHandler* handler = std::move(it->second);
-    session_manager_.erase(it);
-    FXL_DCHECK(session_count_ > 0);
-    --session_count_;
-
-    // Don't destroy handler immediately, since it may be the one calling
-    // TearDownSession().
-    async::PostTask(async_get_default_dispatcher(),
-                    [handler] { handler->TearDown(); });
-  }
-}
-
-void SessionManager::RemoveSession(SessionId id) {
-  auto it = session_manager_.find(id);
-  if (it != session_manager_.end()) {
-    session_manager_.erase(it);
+void SessionManager::RemoveSessionHandler(SessionId id) {
+  auto it = session_handlers_.find(id);
+  if (it != session_handlers_.end()) {
+    session_handlers_.erase(it);
     FXL_DCHECK(session_count_ > 0);
     --session_count_;
   }
diff --git a/lib/ui/gfx/engine/session_manager.h b/lib/ui/gfx/engine/session_manager.h
index 766de85..9fd4475 100644
--- a/lib/ui/gfx/engine/session_manager.h
+++ b/lib/ui/gfx/engine/session_manager.h
@@ -8,6 +8,7 @@
 #include <set>
 #include <unordered_map>
 
+#include "garnet/lib/ui/gfx/engine/session.h"
 #include "garnet/lib/ui/scenic/command_dispatcher.h"
 #include "lib/escher/renderer/batch_gpu_uploader.h"
 
@@ -20,9 +21,9 @@
 namespace gfx {
 
 using SessionId = ::scenic_impl::SessionId;
+using PresentationTime = uint64_t;
 
 class SessionHandler;
-class Session;
 class Engine;
 class UpdateScheduler;
 
@@ -55,7 +56,7 @@
   virtual ~SessionManager() = default;
 
   // Finds the session handler corresponding to the given id.
-  SessionHandler* FindSession(SessionId id);
+  SessionHandler* FindSessionHandler(SessionId id);
 
   size_t GetSessionCount() { return session_count_; }
 
@@ -66,9 +67,9 @@
 
   // Tell the UpdateScheduler to schedule a frame, and remember the Session so
   // that we can tell it to apply updates in ApplyScheduledSessionUpdates().
-  void ScheduleUpdateForSession(UpdateScheduler* update_scheduler,
-                                uint64_t presentation_time,
-                                fxl::RefPtr<Session> session);
+  void ScheduleUpdateForSession(
+      UpdateScheduler* update_scheduler, uint64_t presentation_time,
+      fxl::WeakPtr<scenic_impl::gfx::Session> session);
 
   // Executes updates that are schedule up to and including a given presentation
   // time. Returns true if rendering is needed.
@@ -76,29 +77,45 @@
                                     uint64_t presentation_time,
                                     uint64_t presentation_interval);
 
+ protected:
+  // Protected for testing subclass
+  void InsertSessionHandler(SessionId session_id,
+                            SessionHandler* session_handler);
+
  private:
-  friend class Session;
   friend class SessionHandler;
 
-  // Destroys the session with the given id.
-  void TearDownSession(SessionId id);
+  // Used to compare presentation times so that the priority_queue acts as a min
+  // heap, placing the earliest PresentationTime at the top
+  class UpdatableSessionsComparator {
+   public:
+    bool operator()(
+        std::pair<PresentationTime, fxl::WeakPtr<Session>> updatable_session1,
+        std::pair<PresentationTime, fxl::WeakPtr<Session>> updatable_session2) {
+      return updatable_session1.first > updatable_session2.first;
+    }
+  };
 
-  // Removes the session from the session_manager_ map.  We assume that the
-  // SessionHandler has already taken care of itself and its Session.
-  void RemoveSession(SessionId id);
+  // Removes the SessionHandler from the session_handlers_ map.  We assume that
+  // the SessionHandler has already taken care of itself and its Session.
+  void RemoveSessionHandler(SessionId id);
 
   virtual std::unique_ptr<SessionHandler> CreateSessionHandler(
       CommandDispatcherContext context, Engine* engine, SessionId session_id,
       EventReporter* event_reporter, ErrorReporter* error_reporter) const;
 
   // Map of all the sessions.
-  std::unordered_map<SessionId, SessionHandler*> session_manager_;
+  std::unordered_map<SessionId, SessionHandler*> session_handlers_;
   size_t session_count_ = 0;
   SessionId next_session_id_ = 1;
 
   // Lists all Session that have updates to apply, sorted by the earliest
   // requested presentation time of each update.
-  std::set<std::pair<uint64_t, fxl::RefPtr<Session>>> updatable_sessions_;
+  std::priority_queue<
+      std::pair<PresentationTime, fxl::WeakPtr<Session>>,
+      std::vector<std::pair<PresentationTime, fxl::WeakPtr<Session>>>,
+      UpdatableSessionsComparator>
+      updatable_sessions_;
 };
 
 }  // namespace gfx
diff --git a/lib/ui/gfx/gfx_system.cc b/lib/ui/gfx/gfx_system.cc
index f05b1e0..ffc3a7d 100644
--- a/lib/ui/gfx/gfx_system.cc
+++ b/lib/ui/gfx/gfx_system.cc
@@ -323,7 +323,8 @@
 }
 
 gfx::Session* GfxSystem::GetSession(SessionId session_id) const {
-  SessionHandler* handler = engine_->session_manager()->FindSession(session_id);
+  SessionHandler* handler =
+      engine_->session_manager()->FindSessionHandler(session_id);
   return handler ? handler->session() : nullptr;
 }
 
diff --git a/lib/ui/gfx/tests/BUILD.gn b/lib/ui/gfx/tests/BUILD.gn
index b9903b1..1f4a79f 100644
--- a/lib/ui/gfx/tests/BUILD.gn
+++ b/lib/ui/gfx/tests/BUILD.gn
@@ -20,6 +20,8 @@
     "gfx_test.h",
     "mocks.cc",
     "mocks.h",
+    "session_handler_test.cc",
+    "session_handler_test.h",
     "session_test.cc",
     "session_test.h",
     "vk_session_test.cc",
@@ -60,6 +62,7 @@
   sources = [
     "escher_vulkan_smoke_test.cc",
     "event_timestamper_unittest.cc",
+    "gfx_command_applier_unittest.cc",
     "hardware_layer_assignment_unittest.cc",
     "hittest_global_unittest.cc",
     "hittest_unittest.cc",
@@ -72,6 +75,7 @@
     "resource_linker_unittest.cc",
     "run_all_unittests.cc",
     "scene_graph_unittest.cc",
+    "session_handler_unittest.cc",
     "session_unittest.cc",
     "shape_unittest.cc",
     "size_change_hint_unittest.cc",
diff --git a/lib/ui/gfx/tests/gfx_apptest.cc b/lib/ui/gfx/tests/gfx_apptest.cc
index 6321bff..97351e1 100644
--- a/lib/ui/gfx/tests/gfx_apptest.cc
+++ b/lib/ui/gfx/tests/gfx_apptest.cc
@@ -66,9 +66,9 @@
   RunLoopUntilIdle();
   EXPECT_EQ(1U, scenic()->num_sessions());
   auto handler = static_cast<SessionHandlerForTest*>(
-      gfx_system()->engine()->session_manager()->FindSession(1));
+      gfx_system()->engine()->session_manager()->FindSessionHandler(1));
   {
-    ::std::vector<fuchsia::ui::scenic::Command> commands;
+    std::vector<fuchsia::ui::scenic::Command> commands;
     commands.push_back(scenic::NewCommand(scenic::NewCreateCircleCmd(1, 50.f)));
     commands.push_back(scenic::NewCommand(scenic::NewCreateCircleCmd(2, 25.f)));
 
@@ -77,22 +77,20 @@
   RunLoopUntilIdle();
   EXPECT_EQ(2u, handler->command_count());
   // Create release fences
-  ::std::vector<zx::event> release_fences = CreateEventArray(2);
+  std::vector<zx::event> release_fences = CreateEventArray(2);
   zx::event release_fence1 = CopyEvent(release_fences.at(0));
   zx::event release_fence2 = CopyEvent(release_fences.at(1));
   EXPECT_FALSE(IsFenceSignalled(release_fence1));
   EXPECT_FALSE(IsFenceSignalled(release_fence2));
   // Call Present with release fences.
-  session->Present(0u, std::vector<zx::event>(),
-                   std::move(release_fences),
+  session->Present(0u, std::vector<zx::event>(), std::move(release_fences),
                    [](fuchsia::images::PresentationInfo info) {});
   RunLoopUntilIdle();
   EXPECT_EQ(1u, handler->present_count());
   EXPECT_FALSE(IsFenceSignalled(release_fence1));
   EXPECT_FALSE(IsFenceSignalled(release_fence2));
   // Call Present again with no release fences.
-  session->Present(0u, ::std::vector<zx::event>(),
-                   ::std::vector<zx::event>(),
+  session->Present(0u, std::vector<zx::event>(), std::vector<zx::event>(),
                    [](fuchsia::images::PresentationInfo info) {});
   RunLoopUntilIdle();
   EXPECT_EQ(2u, handler->present_count());
@@ -109,9 +107,9 @@
   RunLoopUntilIdle();
   EXPECT_EQ(1U, scenic()->num_sessions());
   auto handler = static_cast<SessionHandlerForTest*>(
-      gfx_system()->engine()->session_manager()->FindSession(1));
+      gfx_system()->engine()->session_manager()->FindSessionHandler(1));
   {
-    ::std::vector<fuchsia::ui::scenic::Command> commands;
+    std::vector<fuchsia::ui::scenic::Command> commands;
     commands.push_back(scenic::NewCommand(scenic::NewCreateCircleCmd(1, 50.f)));
     commands.push_back(scenic::NewCommand(scenic::NewCreateCircleCmd(2, 25.f)));
 
@@ -124,9 +122,9 @@
   ASSERT_EQ(ZX_OK, zx::event::create(0, &acquire_fence));
   zx::event release_fence;
   ASSERT_EQ(ZX_OK, zx::event::create(0, &release_fence));
-  ::std::vector<zx::event> acquire_fences;
+  std::vector<zx::event> acquire_fences;
   acquire_fences.push_back(CopyEvent(acquire_fence));
-  ::std::vector<zx::event> release_fences;
+  std::vector<zx::event> release_fences;
   release_fences.push_back(CopyEvent(release_fence));
   // Call Present with both the acquire and release fences.
   session->Present(0u, std::move(acquire_fences), std::move(release_fences),
@@ -135,8 +133,7 @@
   EXPECT_EQ(1u, handler->present_count());
   EXPECT_FALSE(IsFenceSignalled(release_fence));
   // Call Present again with no fences.
-  session->Present(0u, ::std::vector<zx::event>(),
-                   ::std::vector<zx::event>(),
+  session->Present(0u, std::vector<zx::event>(), std::vector<zx::event>(),
                    [](fuchsia::images::PresentationInfo info) {});
   RunLoopUntilIdle();
   EXPECT_EQ(2u, handler->present_count());
diff --git a/lib/ui/gfx/tests/gfx_command_applier_unittest.cc b/lib/ui/gfx/tests/gfx_command_applier_unittest.cc
new file mode 100644
index 0000000..4f7b557
--- /dev/null
+++ b/lib/ui/gfx/tests/gfx_command_applier_unittest.cc
@@ -0,0 +1,72 @@
+// Copyright 2019 The Fuchsia 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 "garnet/lib/ui/gfx/engine/gfx_command_applier.h"
+#include "garnet/lib/ui/gfx/tests/session_test.h"
+
+#include "lib/ui/scenic/cpp/commands.h"
+
+#include "gtest/gtest.h"
+
+namespace scenic_impl {
+namespace gfx {
+namespace test {
+
+using GfxCommandApplierTest = SessionTest;
+
+TEST_F(GfxCommandApplierTest, NewCreateEntityNodeCmd) {
+  CommandContext empty_command_context(nullptr);
+
+  // Valid id passes
+  EXPECT_TRUE(GfxCommandApplier::ApplyCommand(
+      session_.get(), &empty_command_context,
+      scenic::NewCreateEntityNodeCmd(/*id*/ 1)));
+
+  // Invalid id fails
+  EXPECT_FALSE(GfxCommandApplier::ApplyCommand(
+      session_.get(), &empty_command_context,
+      scenic::NewCreateEntityNodeCmd(/*id*/ 0)));
+}
+
+TEST_F(GfxCommandApplierTest, EraseResource) {
+  CommandContext empty_command_context(nullptr);
+  EXPECT_TRUE(GfxCommandApplier::ApplyCommand(
+      session_.get(), &empty_command_context,
+      scenic::NewCreateEntityNodeCmd(/*id*/ 3)));
+  // Erasing non-existent resource fails
+  EXPECT_FALSE(
+      GfxCommandApplier::ApplyCommand(session_.get(), &empty_command_context,
+                                      scenic::NewReleaseResourceCmd(/*id*/ 2)));
+
+  // Erasing existing resource passes
+  EXPECT_TRUE(
+      GfxCommandApplier::ApplyCommand(session_.get(), &empty_command_context,
+                                      scenic::NewReleaseResourceCmd(/*id*/ 3)));
+}
+
+TEST_F(GfxCommandApplierTest, SeparateSessionsAreIndependent) {
+  auto session2 = CreateSession();
+
+  CommandContext empty_command_context(nullptr);
+
+  EXPECT_TRUE(GfxCommandApplier::ApplyCommand(
+      session_.get(), &empty_command_context,
+      scenic::NewCreateEntityNodeCmd(/*id*/ 3)));
+  EXPECT_FALSE(
+      GfxCommandApplier::ApplyCommand(session2.get(), &empty_command_context,
+                                      scenic::NewReleaseResourceCmd(/*id*/ 3)));
+  EXPECT_TRUE(GfxCommandApplier::ApplyCommand(
+      session2.get(), &empty_command_context,
+      scenic::NewCreateEntityNodeCmd(/*id*/ 3)));
+  EXPECT_TRUE(
+      GfxCommandApplier::ApplyCommand(session_.get(), &empty_command_context,
+                                      scenic::NewReleaseResourceCmd(/*id*/ 3)));
+  EXPECT_TRUE(
+      GfxCommandApplier::ApplyCommand(session2.get(), &empty_command_context,
+                                      scenic::NewReleaseResourceCmd(/*id*/ 3)));
+}
+
+}  // namespace test
+}  // namespace gfx
+}  // namespace scenic_impl
diff --git a/lib/ui/gfx/tests/hittest_global_unittest.cc b/lib/ui/gfx/tests/hittest_global_unittest.cc
index 1e4d3dc..8154a50 100644
--- a/lib/ui/gfx/tests/hittest_global_unittest.cc
+++ b/lib/ui/gfx/tests/hittest_global_unittest.cc
@@ -46,14 +46,10 @@
 class CustomSession {
  public:
   CustomSession(SessionId id, SessionContext session_context) {
-    session_ =
-        fxl::MakeRefCounted<SessionForTest>(id, std::move(session_context));
+    session_ = std::make_unique<SessionForTest>(id, std::move(session_context));
   }
 
-  ~CustomSession() {
-    session_->TearDown();
-    session_ = nullptr;
-  }
+  ~CustomSession() {}
 
   void Apply(::fuchsia::ui::gfx::Command command) {
     CommandContext empty_command_context(nullptr);
@@ -63,7 +59,7 @@
   }
 
  private:
-  fxl::RefPtr<SessionForTest> session_;
+  std::unique_ptr<SessionForTest> session_;
 };
 
 // Loop fixture provides dispatcher for Engine's EventTimestamper.
diff --git a/lib/ui/gfx/tests/image_pipe_unittest.cc b/lib/ui/gfx/tests/image_pipe_unittest.cc
index 75a842f..e52a2d4 100644
--- a/lib/ui/gfx/tests/image_pipe_unittest.cc
+++ b/lib/ui/gfx/tests/image_pipe_unittest.cc
@@ -39,7 +39,7 @@
  public:
   ImagePipeTest() : escher::ResourceManager(escher::EscherWeakPtr()) {}
 
-  fxl::RefPtr<SessionForTest> CreateSession() override {
+  std::unique_ptr<SessionForTest> CreateSession() override {
     SessionContext session_context = CreateBarebonesSessionContext();
 
     command_buffer_sequencer_ =
@@ -51,8 +51,8 @@
     session_context.release_fence_signaller =
         mock_release_fence_signaller_.get();
 
-    return fxl::MakeRefCounted<SessionForTest>(1, std::move(session_context),
-                                               this, error_reporter());
+    return std::make_unique<SessionForTest>(1, std::move(session_context), this,
+                                            error_reporter());
   }
 
   void OnReceiveOwnable(std::unique_ptr<escher::Resource> resource) override {}
@@ -364,8 +364,10 @@
       imageIdB, std::move(image_info_b), CopyVmo(gradient_b->vmo()), 0,
       GetVmoSize(gradient_b->vmo()), fuchsia::images::MemoryType::HOST_MEMORY);
 
-  image_pipe->PresentImage(imageIdA, 0, std::vector<zx::event>(), std::vector<zx::event>(), nullptr);
-  image_pipe->PresentImage(imageIdB, 0, std::vector<zx::event>(), std::vector<zx::event>(), nullptr);
+  image_pipe->PresentImage(imageIdA, 0, std::vector<zx::event>(),
+                           std::vector<zx::event>(), nullptr);
+  image_pipe->PresentImage(imageIdB, 0, std::vector<zx::event>(),
+                           std::vector<zx::event>(), nullptr);
 
   RunLoopUntilIdle();
 
@@ -384,9 +386,11 @@
   // In this case, we need to run to idle after presenting image A, so that
   // image B is returned by the pool, marked dirty, and is free to be acquired
   // again.
-  image_pipe->PresentImage(imageIdA, 0, std::vector<zx::event>(), std::vector<zx::event>(), nullptr);
+  image_pipe->PresentImage(imageIdA, 0, std::vector<zx::event>(),
+                           std::vector<zx::event>(), nullptr);
   RunLoopUntilIdle();
-  image_pipe->PresentImage(imageIdB, 0, std::vector<zx::event>(), std::vector<zx::event>(), nullptr);
+  image_pipe->PresentImage(imageIdB, 0, std::vector<zx::event>(),
+                           std::vector<zx::event>(), nullptr);
   RunLoopUntilIdle();
 
   image_out = image_pipe->GetEscherImage();
diff --git a/lib/ui/gfx/tests/import_unittest.cc b/lib/ui/gfx/tests/import_unittest.cc
index 0244ec1..3ceab15 100644
--- a/lib/ui/gfx/tests/import_unittest.cc
+++ b/lib/ui/gfx/tests/import_unittest.cc
@@ -18,14 +18,14 @@
  public:
   ImportTest() {}
 
-  fxl::RefPtr<SessionForTest> CreateSession() override {
+  std::unique_ptr<SessionForTest> CreateSession() override {
     SessionContext session_context = CreateBarebonesSessionContext();
 
     resource_linker_ = std::make_unique<ResourceLinker>();
     session_context.resource_linker = resource_linker_.get();
 
-    return fxl::MakeRefCounted<SessionForTest>(1, std::move(session_context),
-                                               this, error_reporter());
+    return std::make_unique<SessionForTest>(1, std::move(session_context), this,
+                                            error_reporter());
   }
 
   std::unique_ptr<ResourceLinker> resource_linker_;
diff --git a/lib/ui/gfx/tests/mocks.cc b/lib/ui/gfx/tests/mocks.cc
index 0ef6d86..c013d2f 100644
--- a/lib/ui/gfx/tests/mocks.cc
+++ b/lib/ui/gfx/tests/mocks.cc
@@ -4,6 +4,7 @@
 
 #include "garnet/lib/ui/gfx/tests/mocks.h"
 
+#include "garnet/lib/ui/gfx/tests/session_test.h"
 #include "garnet/lib/ui/scenic/command_dispatcher.h"
 
 namespace scenic_impl {
@@ -15,17 +16,15 @@
                                ErrorReporter* error_reporter)
     : Session(id, std::move(context), event_reporter, error_reporter) {}
 
-void SessionForTest::TearDown() { Session::TearDown(); }
-
-SessionHandlerForTest::SessionHandlerForTest(CommandDispatcherContext context,
-                                             SessionManager* session_manager,
+SessionHandlerForTest::SessionHandlerForTest(SessionManager* session_manager,
                                              SessionContext session_context,
                                              SessionId session_id,
+                                             Scenic* scenic,
                                              EventReporter* event_reporter,
                                              ErrorReporter* error_reporter)
-    : SessionHandler(std::move(context), session_manager,
-                     std::move(session_context), session_id, event_reporter,
-                     error_reporter),
+    : SessionHandler(CommandDispatcherContext(scenic, nullptr, session_id),
+                     session_manager, std::move(session_context), session_id,
+                     event_reporter, error_reporter),
       command_count_(0),
       present_count_(0) {}
 
@@ -56,12 +55,9 @@
 
 SessionManagerForTest::SessionManagerForTest() : SessionManager() {}
 
-std::unique_ptr<SessionHandler> SessionManagerForTest::CreateSessionHandler(
-    CommandDispatcherContext context, Engine* engine, SessionId session_id,
-    EventReporter* event_reporter, ErrorReporter* error_reporter) const {
-  return std::make_unique<SessionHandlerForTest>(
-      std::move(context), engine->session_manager(), engine->session_context(),
-      session_id, event_reporter, error_reporter);
+void SessionManagerForTest::InsertSessionHandler(
+    SessionId session_id, SessionHandler* session_handler) {
+  SessionManager::InsertSessionHandler(session_id, session_handler);
 }
 
 EngineForTest::EngineForTest(DisplayManager* display_manager,
diff --git a/lib/ui/gfx/tests/mocks.h b/lib/ui/gfx/tests/mocks.h
index 9739152..578c69d 100644
--- a/lib/ui/gfx/tests/mocks.h
+++ b/lib/ui/gfx/tests/mocks.h
@@ -9,6 +9,7 @@
 #include "garnet/lib/ui/gfx/engine/engine.h"
 #include "garnet/lib/ui/gfx/engine/session.h"
 #include "garnet/lib/ui/gfx/engine/session_handler.h"
+#include "garnet/lib/ui/scenic/scenic.h"
 #include "lib/escher/flib/release_fence_signaller.h"
 
 namespace scenic_impl {
@@ -20,15 +21,13 @@
   SessionForTest(SessionId id, SessionContext context,
                  EventReporter* event_reporter = EventReporter::Default(),
                  ErrorReporter* error_reporter = ErrorReporter::Default());
-
-  virtual void TearDown() override;
 };
 
 class SessionHandlerForTest : public SessionHandler {
  public:
   SessionHandlerForTest(
-      CommandDispatcherContext context, SessionManager* session_manager,
-      SessionContext session_context, SessionId session_id,
+      SessionManager* session_manager, SessionContext session_context,
+      SessionId session_id, Scenic* scenic,
       EventReporter* event_reporter = EventReporter::Default(),
       ErrorReporter* error_reporter = ErrorReporter::Default());
 
@@ -70,12 +69,8 @@
 class SessionManagerForTest : public SessionManager {
  public:
   SessionManagerForTest();
-
- private:
-  std::unique_ptr<SessionHandler> CreateSessionHandler(
-      CommandDispatcherContext context, Engine* engine, SessionId session_id,
-      EventReporter* event_reporter,
-      ErrorReporter* error_reporter) const override;
+  void InsertSessionHandler(SessionId session_id,
+                            SessionHandler* session_handler);
 };
 
 class EngineForTest : public Engine {
diff --git a/lib/ui/gfx/tests/resource_linker_unittest.cc b/lib/ui/gfx/tests/resource_linker_unittest.cc
index 71ec51d..1d57f64 100644
--- a/lib/ui/gfx/tests/resource_linker_unittest.cc
+++ b/lib/ui/gfx/tests/resource_linker_unittest.cc
@@ -21,14 +21,14 @@
  public:
   ResourceLinkerTest() {}
 
-  fxl::RefPtr<SessionForTest> CreateSession() override {
+  std::unique_ptr<SessionForTest> CreateSession() override {
     SessionContext session_context = CreateBarebonesSessionContext();
 
     resource_linker_ = std::make_unique<ResourceLinker>();
     session_context.resource_linker = resource_linker_.get();
 
-    return fxl::MakeRefCounted<SessionForTest>(1, std::move(session_context),
-                                               this, error_reporter());
+    return std::make_unique<SessionForTest>(1, std::move(session_context), this,
+                                            error_reporter());
   }
 
   std::unique_ptr<ResourceLinker> resource_linker_;
diff --git a/lib/ui/gfx/tests/session_handler_test.cc b/lib/ui/gfx/tests/session_handler_test.cc
new file mode 100644
index 0000000..5ac5b68
--- /dev/null
+++ b/lib/ui/gfx/tests/session_handler_test.cc
@@ -0,0 +1,42 @@
+// Copyright 2019 The Fuchsia 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 "garnet/lib/ui/gfx/tests/session_handler_test.h"
+
+namespace scenic_impl {
+namespace gfx {
+namespace test {
+
+void SessionHandlerTest::SetUp() { SessionTest::SetUp(); }
+
+void SessionHandlerTest::TearDown() {
+  SessionTest::TearDown();
+  session_handler_.reset();
+  scenic_.reset();
+  app_context_.reset();
+}
+
+void SessionHandlerTest::InitializeScenic() {
+  // TODO(SCN-720): Wrap CreateFromStartupInfo using ::gtest::Environment
+  // instead of this hack.  This code has the chance to break non-ScenicTests.
+  app_context_ = component::StartupContext::CreateFromStartupInfo();
+  scenic_ = std::make_unique<Scenic>(app_context_.get(), [] {});
+}
+
+void SessionHandlerTest::InitializeSessionHandler() {
+  if (!scenic_) {
+    InitializeScenic();
+  }
+
+  auto session_context = CreateBarebonesSessionContext();
+  session_handler_ = std::make_unique<SessionHandlerForTest>(
+      session_manager_.get(), std::move(session_context), session_->id(),
+      scenic_.get());
+  session_manager_->InsertSessionHandler(session_->id(),
+                                         session_handler_.get());
+}
+
+}  // namespace test
+}  // namespace gfx
+}  // namespace scenic_impl
diff --git a/lib/ui/gfx/tests/session_handler_test.h b/lib/ui/gfx/tests/session_handler_test.h
new file mode 100644
index 0000000..be07096
--- /dev/null
+++ b/lib/ui/gfx/tests/session_handler_test.h
@@ -0,0 +1,45 @@
+// Copyright 2019 The Fuchsia 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 GARNET_LIB_UI_GFX_TESTS_SESSION_HANDLER_TEST_H_
+#define GARNET_LIB_UI_GFX_TESTS_SESSION_HANDLER_TEST_H_
+
+#include "garnet/lib/ui/gfx/tests/session_test.h"
+
+#include <lib/fit/function.h>
+
+#include "garnet/lib/ui/gfx/displays/display_manager.h"
+#include "garnet/lib/ui/gfx/engine/engine.h"
+#include "garnet/lib/ui/gfx/engine/session.h"
+#include "garnet/lib/ui/gfx/tests/mocks.h"
+#include "garnet/lib/ui/scenic/event_reporter.h"
+#include "garnet/lib/ui/scenic/tests/scenic_test.h"
+#include "lib/gtest/test_loop_fixture.h"
+
+namespace scenic_impl {
+namespace gfx {
+namespace test {
+
+// For testing SessionHandler without having to manually provide all the state
+// necessary for SessionHandler to run
+class SessionHandlerTest : public SessionTest {
+ public:
+  void ResetSessionHandler() { session_handler_.reset(); }
+  void InitializeScenic();
+  void InitializeSessionHandler();
+
+  void SetUp() override;
+  void TearDown() override;
+
+ protected:
+  std::unique_ptr<component::StartupContext> app_context_;
+  std::unique_ptr<Scenic> scenic_;
+  std::unique_ptr<SessionHandlerForTest> session_handler_;
+};
+
+}  // namespace test
+}  // namespace gfx
+}  // namespace scenic_impl
+
+#endif  // GARNET_LIB_UI_GFX_TESTS_SESSION_HANDLER_TEST_H_
diff --git a/lib/ui/gfx/tests/session_handler_unittest.cc b/lib/ui/gfx/tests/session_handler_unittest.cc
new file mode 100644
index 0000000..3ba37e7
--- /dev/null
+++ b/lib/ui/gfx/tests/session_handler_unittest.cc
@@ -0,0 +1,30 @@
+// Copyright 2019 The Fuchsia 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 "garnet/lib/ui/gfx/tests/session_handler_test.h"
+
+#include "gtest/gtest.h"
+
+namespace scenic_impl {
+namespace gfx {
+namespace test {
+
+TEST_F(
+    SessionHandlerTest,
+    WhenSessionHandlerDestroyed_ShouldRemoveSessionHandlerPtrFromSessionManager) {
+  InitializeSessionHandler();
+  auto id = session_->id();
+
+  EXPECT_NE(session_handler_.get(), nullptr);
+  EXPECT_EQ(session_manager_->FindSessionHandler(id), session_handler_.get());
+
+  ResetSessionHandler();
+
+  EXPECT_EQ(session_handler_.get(), nullptr);
+  EXPECT_EQ(session_manager_->FindSessionHandler(id), nullptr);
+}
+
+}  // namespace test
+}  // namespace gfx
+}  // namespace scenic_impl
diff --git a/lib/ui/gfx/tests/session_test.cc b/lib/ui/gfx/tests/session_test.cc
index 185008d..a902842 100644
--- a/lib/ui/gfx/tests/session_test.cc
+++ b/lib/ui/gfx/tests/session_test.cc
@@ -6,6 +6,8 @@
 
 #include "garnet/lib/ui/gfx/tests/mocks.h"
 
+#include "lib/fxl/logging.h"
+
 namespace scenic_impl {
 namespace gfx {
 namespace test {
@@ -22,13 +24,15 @@
 void SessionTest::SetUp() { session_ = CreateSession(); }
 
 void SessionTest::TearDown() {
-  session_->TearDown();
-  session_ = nullptr;
+  session_manager_.reset();
+  if (session_) {
+    session_.reset();
+  }
   events_.clear();
 }
 
 SessionContext SessionTest::CreateBarebonesSessionContext() {
-  session_manager_ = std::make_unique<SessionManager>();
+  session_manager_ = std::make_unique<SessionManagerForTest>();
   update_scheduler_ =
       std::make_unique<FakeUpdateScheduler>(session_manager_.get());
   SessionContext session_context{
@@ -52,9 +56,9 @@
   return session_context;
 }
 
-fxl::RefPtr<SessionForTest> SessionTest::CreateSession() {
-  return fxl::MakeRefCounted<SessionForTest>(1, CreateBarebonesSessionContext(),
-                                             this, error_reporter());
+std::unique_ptr<SessionForTest> SessionTest::CreateSession() {
+  return std::make_unique<SessionForTest>(1, CreateBarebonesSessionContext(),
+                                          this, error_reporter());
 }
 
 void SessionTest::EnqueueEvent(fuchsia::ui::gfx::Event event) {
diff --git a/lib/ui/gfx/tests/session_test.h b/lib/ui/gfx/tests/session_test.h
index 7cdccb7..3916eaf 100644
--- a/lib/ui/gfx/tests/session_test.h
+++ b/lib/ui/gfx/tests/session_test.h
@@ -9,11 +9,11 @@
 
 #include <lib/fit/function.h>
 
-#include "garnet/lib/ui/gfx/displays/display_manager.h"
 #include "garnet/lib/ui/gfx/engine/engine.h"
 #include "garnet/lib/ui/gfx/engine/session.h"
 #include "garnet/lib/ui/gfx/tests/mocks.h"
 #include "garnet/lib/ui/scenic/event_reporter.h"
+#include "garnet/lib/ui/scenic/tests/scenic_test.h"
 #include "lib/gtest/test_loop_fixture.h"
 
 namespace scenic_impl {
@@ -42,7 +42,7 @@
   void EnqueueEvent(fuchsia::ui::scenic::Command unhandled) override;
 
   // Subclasses should override to provide their own Session.
-  virtual fxl::RefPtr<SessionForTest> CreateSession();
+  virtual std::unique_ptr<SessionForTest> CreateSession();
 
   // Creates a SessionContext with only a SessionManager and a
   // FakeUpdateScheduler.
@@ -60,9 +60,9 @@
     return session_->resources()->FindResource<ResourceT>(id);
   }
 
-  std::unique_ptr<SessionManager> session_manager_;
   std::unique_ptr<UpdateScheduler> update_scheduler_;
-  fxl::RefPtr<SessionForTest> session_;
+  std::unique_ptr<SessionForTest> session_;
+  std::unique_ptr<SessionManagerForTest> session_manager_;
   std::vector<fuchsia::ui::scenic::Event> events_;
 };
 
diff --git a/lib/ui/gfx/tests/session_unittest.cc b/lib/ui/gfx/tests/session_unittest.cc
index 5041b47..22b823b 100644
--- a/lib/ui/gfx/tests/session_unittest.cc
+++ b/lib/ui/gfx/tests/session_unittest.cc
@@ -19,14 +19,12 @@
 namespace test {
 
 TEST_F(SessionTest, ScheduleUpdateOutOfOrder) {
-  EXPECT_TRUE(
-      session_->ScheduleUpdate(1, std::vector<::fuchsia::ui::gfx::Command>(),
-                               ::std::vector<zx::event>(),
-                               ::std::vector<zx::event>(), [](auto) {}));
-  EXPECT_FALSE(
-      session_->ScheduleUpdate(0, std::vector<::fuchsia::ui::gfx::Command>(),
-                               ::std::vector<zx::event>(),
-                               ::std::vector<zx::event>(), [](auto) {}));
+  EXPECT_TRUE(session_->ScheduleUpdate(
+      /*presentation_time*/ 1, std::vector<::fuchsia::ui::gfx::Command>(),
+      std::vector<zx::event>(), std::vector<zx::event>(), [](auto) {}));
+  EXPECT_FALSE(session_->ScheduleUpdate(
+      /*presentation_time*/ 0, std::vector<::fuchsia::ui::gfx::Command>(),
+      std::vector<zx::event>(), std::vector<zx::event>(), [](auto) {}));
   ExpectLastReportedError(
       "scenic_impl::gfx::Session: Present called with out-of-order "
       "presentation "
@@ -35,14 +33,12 @@
 }
 
 TEST_F(SessionTest, ScheduleUpdateInOrder) {
-  EXPECT_TRUE(
-      session_->ScheduleUpdate(1, std::vector<::fuchsia::ui::gfx::Command>(),
-                               ::std::vector<zx::event>(),
-                               ::std::vector<zx::event>(), [](auto) {}));
-  EXPECT_TRUE(
-      session_->ScheduleUpdate(1, std::vector<::fuchsia::ui::gfx::Command>(),
-                               ::std::vector<zx::event>(),
-                               ::std::vector<zx::event>(), [](auto) {}));
+  EXPECT_TRUE(session_->ScheduleUpdate(
+      /*presentation_time*/ 1, std::vector<::fuchsia::ui::gfx::Command>(),
+      std::vector<zx::event>(), std::vector<zx::event>(), [](auto) {}));
+  EXPECT_TRUE(session_->ScheduleUpdate(
+      /*presentation_time*/ 1, std::vector<::fuchsia::ui::gfx::Command>(),
+      std::vector<zx::event>(), std::vector<zx::event>(), [](auto) {}));
   ExpectLastReportedError(nullptr);
 }
 
@@ -96,14 +92,14 @@
   EXPECT_TRUE(Apply(scenic::NewSetLabelCmd(kNodeId, kLongLabel)));
   EXPECT_EQ(kLongLabel, shape_node->label());
   EXPECT_TRUE(Apply(scenic::NewSetLabelCmd(kNodeId, kTooLongLabel)));
-  EXPECT_EQ(kTooLongLabel.substr(0, ::fuchsia::ui::gfx::kLabelMaxLength),
+  EXPECT_EQ(kTooLongLabel.substr(0, fuchsia::ui::gfx::kLabelMaxLength),
             shape_node->label());
   EXPECT_TRUE(Apply(scenic::NewSetLabelCmd(kNodeId, "")));
   EXPECT_TRUE(shape_node->label().empty());
 
   // Bypass the truncation performed by session helpers.
   shape_node->SetLabel(kTooLongLabel);
-  EXPECT_EQ(kTooLongLabel.substr(0, ::fuchsia::ui::gfx::kLabelMaxLength),
+  EXPECT_EQ(kTooLongLabel.substr(0, fuchsia::ui::gfx::kLabelMaxLength),
             shape_node->label());
 }
 
diff --git a/lib/ui/gfx/tests/size_change_hint_unittest.cc b/lib/ui/gfx/tests/size_change_hint_unittest.cc
index b5377a3..c5698d4 100644
--- a/lib/ui/gfx/tests/size_change_hint_unittest.cc
+++ b/lib/ui/gfx/tests/size_change_hint_unittest.cc
@@ -15,14 +15,14 @@
  public:
   SizeChangeHintTest() {}
 
-  fxl::RefPtr<SessionForTest> CreateSession() override {
+  std::unique_ptr<SessionForTest> CreateSession() override {
     SessionContext session_context = CreateBarebonesSessionContext();
 
     resource_linker_ = std::make_unique<ResourceLinker>();
     session_context.resource_linker = resource_linker_.get();
 
-    return fxl::MakeRefCounted<SessionForTest>(1, std::move(session_context),
-                                               this, error_reporter());
+    return std::make_unique<SessionForTest>(1, std::move(session_context), this,
+                                            error_reporter());
   }
 
   std::unique_ptr<ResourceLinker> resource_linker_;
diff --git a/lib/ui/gfx/tests/view_unittest.cc b/lib/ui/gfx/tests/view_unittest.cc
index 48b72c6..67f9420 100644
--- a/lib/ui/gfx/tests/view_unittest.cc
+++ b/lib/ui/gfx/tests/view_unittest.cc
@@ -23,14 +23,14 @@
  public:
   ViewTest() {}
 
-  fxl::RefPtr<SessionForTest> CreateSession() override {
+  std::unique_ptr<SessionForTest> CreateSession() override {
     SessionContext session_context = CreateBarebonesSessionContext();
 
     view_linker_ = std::make_unique<ViewLinker>();
     session_context.view_linker = view_linker_.get();
 
-    return fxl::MakeRefCounted<SessionForTest>(1, std::move(session_context),
-                                               this, error_reporter());
+    return std::make_unique<SessionForTest>(1, std::move(session_context), this,
+                                            error_reporter());
   }
 
   std::unique_ptr<ViewLinker> view_linker_;
diff --git a/lib/ui/gfx/tests/vk_session_test.cc b/lib/ui/gfx/tests/vk_session_test.cc
index 66aeec9..c5db90d 100644
--- a/lib/ui/gfx/tests/vk_session_test.cc
+++ b/lib/ui/gfx/tests/vk_session_test.cc
@@ -9,7 +9,7 @@
 namespace gfx {
 namespace test {
 
-fxl::RefPtr<SessionForTest> VkSessionTest::CreateSession() {
+std::unique_ptr<SessionForTest> VkSessionTest::CreateSession() {
   SessionContext session_context = CreateBarebonesSessionContext();
 
   // Initialize Vulkan.
@@ -37,8 +37,8 @@
   session_context.escher_image_factory = image_factory_.get();
   session_context.release_fence_signaller = release_fence_signaller_.get();
 
-  return fxl::MakeRefCounted<SessionForTest>(1, std::move(session_context),
-                                             this, error_reporter());
+  return std::make_unique<SessionForTest>(1, std::move(session_context), this,
+                                          error_reporter());
 }
 
 }  // namespace test
diff --git a/lib/ui/gfx/tests/vk_session_test.h b/lib/ui/gfx/tests/vk_session_test.h
index d1b1649..7eb8b7d 100644
--- a/lib/ui/gfx/tests/vk_session_test.h
+++ b/lib/ui/gfx/tests/vk_session_test.h
@@ -14,7 +14,7 @@
 class VkSessionTest : public SessionTest {
  public:
   // |SessionTest|
-  fxl::RefPtr<SessionForTest> CreateSession() override;
+  std::unique_ptr<SessionForTest> CreateSession() override;
 
  private:
   std::unique_ptr<escher::Escher> escher_;
diff --git a/lib/ui/scenic/command_dispatcher.cc b/lib/ui/scenic/command_dispatcher.cc
index d92a6f8..d3ec99c 100644
--- a/lib/ui/scenic/command_dispatcher.cc
+++ b/lib/ui/scenic/command_dispatcher.cc
@@ -4,21 +4,28 @@
 
 #include "garnet/lib/ui/scenic/command_dispatcher.h"
 
+#include "garnet/lib/ui/scenic/scenic.h"
 #include "garnet/lib/ui/scenic/session.h"
 
 namespace scenic_impl {
 
 CommandDispatcherContext::CommandDispatcherContext(Scenic* scenic,
                                                    Session* session)
-    : scenic_(scenic), session_(session), session_id_(session->id()) {
-  FXL_DCHECK(scenic_);
-  FXL_DCHECK(session_);
-  FXL_DCHECK(session_id_);
+    : CommandDispatcherContext(scenic, session, session->id()) {}
+
+CommandDispatcherContext::CommandDispatcherContext(Scenic* scenic,
+                                                   Session* session,
+                                                   SessionId id)
+    : scenic_(scenic), session_(session), session_id_(id) {
+  if (session) {
+    FXL_DCHECK(session->id() == id);
+  }
 }
 
 CommandDispatcherContext::CommandDispatcherContext(
     CommandDispatcherContext&& context)
-    : CommandDispatcherContext(context.scenic_, context.session_) {
+    : CommandDispatcherContext(context.scenic_, context.session_,
+                               context.session_id_) {
   auto& other_scenic = const_cast<Scenic*&>(context.scenic_);
   auto& other_session = const_cast<Session*&>(context.session_);
   auto& other_session_id = const_cast<SessionId&>(context.session_id_);
@@ -27,6 +34,10 @@
   other_session_id = 0;
 }
 
+void CommandDispatcherContext::KillSession() {
+  scenic_->CloseSession(session());
+}
+
 CommandDispatcher::CommandDispatcher(CommandDispatcherContext context)
     : context_(std::move(context)) {}
 
diff --git a/lib/ui/scenic/command_dispatcher.h b/lib/ui/scenic/command_dispatcher.h
index 62a1de6..daffeb9 100644
--- a/lib/ui/scenic/command_dispatcher.h
+++ b/lib/ui/scenic/command_dispatcher.h
@@ -15,15 +15,26 @@
 
 // Provides the capabilities that a CommandDispatcher needs to do its job,
 // without directly exposing the Session.
-class CommandDispatcherContext final {
+class CommandDispatcherContext {
  public:
   explicit CommandDispatcherContext(Scenic* scenic, Session* session);
+
+  explicit CommandDispatcherContext(Scenic* scenic, Session* session,
+                                    SessionId id);
+
   CommandDispatcherContext(CommandDispatcherContext&& context);
 
   // TODO(SCN-808): can/should we avoid exposing any/all of these?
-  Scenic* scenic() { return scenic_; }
-  Session* session() { return session_; }
-  SessionId session_id() { return session_id_; }
+  Session* session() {
+    FXL_DCHECK(session_);
+    return session_;
+  }
+  SessionId session_id() {
+    FXL_DCHECK(session_id_);
+    return session_id_;
+  }
+
+  void KillSession();
 
  private:
   Scenic* const scenic_;
diff --git a/lib/ui/scenic/scenic.cc b/lib/ui/scenic/scenic.cc
index 5036333..d5dd0f6 100644
--- a/lib/ui/scenic/scenic.cc
+++ b/lib/ui/scenic/scenic.cc
@@ -38,11 +38,15 @@
 }
 
 void Scenic::CloseSession(Session* session) {
+  // TODO(SCN-1065): Make it so that close session removes the binding from
+  // session_bindings_, then remove num_sessions_ and return
+  // session_bindings_.size() in num_sessions() instead
   for (auto& binding : session_bindings_.bindings()) {
     // It's possible that this is called by BindingSet::CloseAndCheckForEmpty.
     // In that case, binding could be empty, so check for that.
     if (binding && binding->impl().get() == session) {
       binding->Unbind();
+      --num_sessions_;
       return;
     }
   }
@@ -67,7 +71,7 @@
     ::fidl::InterfaceRequest<fuchsia::ui::scenic::Session> session_request,
     ::fidl::InterfaceHandle<fuchsia::ui::scenic::SessionListener> listener) {
   auto session =
-      std::make_unique<Session>(this, next_session_id_++, std::move(listener));
+      std::make_unique<Session>(next_session_id_++, std::move(listener));
 
   // Give each installed System an opportunity to install a CommandDispatcher in
   // the newly-created Session.
@@ -82,6 +86,7 @@
   session->SetCommandDispatchers(std::move(dispatchers));
 
   session_bindings_.AddBinding(std::move(session), std::move(session_request));
+  ++num_sessions_;
 }
 
 void Scenic::GetDisplayInfo(
diff --git a/lib/ui/scenic/scenic.h b/lib/ui/scenic/scenic.h
index 85521c5..4b28276 100644
--- a/lib/ui/scenic/scenic.h
+++ b/lib/ui/scenic/scenic.h
@@ -43,7 +43,7 @@
 
   component::StartupContext* app_context() const { return app_context_; }
 
-  size_t num_sessions() { return session_bindings_.size(); }
+  size_t num_sessions() { return num_sessions_; }
 
  private:
   void CreateSessionImmediately(
@@ -83,6 +83,7 @@
   fidl::BindingSet<fuchsia::ui::scenic::Scenic> scenic_bindings_;
 
   size_t next_session_id_ = 1;
+  size_t num_sessions_ = 0;
 
   FXL_DISALLOW_COPY_AND_ASSIGN(Scenic);
 };
diff --git a/lib/ui/scenic/session.cc b/lib/ui/scenic/session.cc
index fb2caf9..c39e344 100644
--- a/lib/ui/scenic/session.cc
+++ b/lib/ui/scenic/session.cc
@@ -10,11 +10,9 @@
 namespace scenic_impl {
 
 Session::Session(
-    Scenic* owner, SessionId id,
+    SessionId id,
     ::fidl::InterfaceHandle<fuchsia::ui::scenic::SessionListener> listener)
-    : scenic_(owner), id_(id), listener_(listener.Bind()), weak_factory_(this) {
-  FXL_DCHECK(scenic_);
-}
+    : id_(id), listener_(listener.Bind()), weak_factory_(this) {}
 
 Session::~Session() = default;
 
diff --git a/lib/ui/scenic/session.h b/lib/ui/scenic/session.h
index c8599f9..c3ff924 100644
--- a/lib/ui/scenic/session.h
+++ b/lib/ui/scenic/session.h
@@ -32,7 +32,7 @@
                       public ErrorReporter {
  public:
   Session(
-      Scenic* owner, SessionId id,
+      SessionId id,
       ::fidl::InterfaceHandle<fuchsia::ui::scenic::SessionListener> listener);
   ~Session() override;
 
@@ -102,7 +102,6 @@
   // invoke the callback for each event.
   void FlushEvents();
 
-  Scenic* const scenic_;
   const SessionId id_;
   ::fidl::InterfacePtr<fuchsia::ui::scenic::SessionListener> listener_;
 
diff --git a/lib/ui/scenic/tests/BUILD.gn b/lib/ui/scenic/tests/BUILD.gn
index 5faf04c..dd69baa 100644
--- a/lib/ui/scenic/tests/BUILD.gn
+++ b/lib/ui/scenic/tests/BUILD.gn
@@ -13,30 +13,23 @@
 source_set("testing_deps") {
   testonly = true
   sources = [
+    "scenic_gfx_test.cc",
+    "scenic_gfx_test.h",
     "scenic_test.cc",
     "scenic_test.h",
   ]
   public_deps = [
+    ":mocks",
+    "//garnet/lib/ui/gfx",
     "//garnet/lib/ui/scenic",
     "//garnet/public/lib/component/cpp",
     "//garnet/public/lib/fxl",
     "//garnet/public/lib/gtest",
+    "//garnet/public/lib/ui/scenic/cpp",
     "//third_party/googletest:gtest_main",
   ]
 }
 
-executable("unittests") {
-  output_name = "scenic_unittests"
-  testonly = true
-  sources = [
-    "scenic_unittest.cc",
-  ]
-  deps = [
-    ":dummy_system",
-    ":testing_deps",
-  ]
-}
-
 source_set("dummy_system") {
   sources = [
     "dummy_system.cc",
@@ -47,3 +40,27 @@
     "//garnet/public/fidl/fuchsia.ui.scenic",
   ]
 }
+
+source_set("mocks") {
+  sources = [
+    "mocks.cc",
+    "mocks.h",
+  ]
+  deps = [
+    ":dummy_system",
+    "//garnet/lib/ui/gfx",
+  ]
+}
+
+executable("unittests") {
+  output_name = "scenic_unittests"
+  testonly = true
+  sources = [
+    "scenic_unittest.cc",
+  ]
+  deps = [
+    ":dummy_system",
+    ":mocks",
+    ":testing_deps",
+  ]
+}
diff --git a/lib/ui/scenic/tests/mocks.cc b/lib/ui/scenic/tests/mocks.cc
new file mode 100644
index 0000000..047cac6
--- /dev/null
+++ b/lib/ui/scenic/tests/mocks.cc
@@ -0,0 +1,28 @@
+// Copyright 2019 The Fuchsia 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 "garnet/lib/ui/scenic/tests/mocks.h"
+
+namespace scenic_impl {
+namespace test {
+
+ReleaseFenceSignallerForTest::ReleaseFenceSignallerForTest(
+    escher::impl::CommandBufferSequencer* command_buffer_sequencer)
+    : ReleaseFenceSignaller(command_buffer_sequencer) {}
+
+void ReleaseFenceSignallerForTest::AddCPUReleaseFence(zx::event fence) {
+  ++num_calls_to_add_cpu_release_fence_;
+  // Signal immediately for testing purposes.
+  fence.signal(0u, escher::kFenceSignalled);
+}
+
+EngineForTest::EngineForTest(
+    gfx::DisplayManager* display_manager,
+    std::unique_ptr<escher::ReleaseFenceSignaller> release_signaler,
+    escher::EscherWeakPtr escher)
+    : gfx::Engine(display_manager, std::move(release_signaler),
+                  std::make_unique<gfx::SessionManager>(), std::move(escher)) {}
+
+}  // namespace test
+}  // namespace scenic_impl
diff --git a/lib/ui/scenic/tests/mocks.h b/lib/ui/scenic/tests/mocks.h
new file mode 100644
index 0000000..3dd58ea
--- /dev/null
+++ b/lib/ui/scenic/tests/mocks.h
@@ -0,0 +1,86 @@
+// Copyright 2019 The Fuchsia 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 GARNET_LIB_UI_SCENIC_TESTS_MOCKS_H_
+#define GARNET_LIB_UI_SCENIC_TESTS_MOCKS_H_
+
+#include "garnet/lib/ui/gfx/gfx_system.h"
+#include "garnet/lib/ui/scenic/tests/dummy_system.h"
+#include "lib/escher/flib/release_fence_signaller.h"
+
+namespace scenic_impl {
+namespace test {
+
+class ReleaseFenceSignallerForTest : public escher::ReleaseFenceSignaller {
+ public:
+  ReleaseFenceSignallerForTest(
+      escher::impl::CommandBufferSequencer* command_buffer_sequencer);
+
+  void AddCPUReleaseFence(zx::event fence) override;
+
+  uint32_t num_calls_to_add_cpu_release_fence() {
+    return num_calls_to_add_cpu_release_fence_;
+  }
+
+ private:
+  uint32_t num_calls_to_add_cpu_release_fence_ = 0;
+};
+
+class EngineForTest : public gfx::Engine {
+ public:
+  EngineForTest(gfx::DisplayManager* display_manager,
+                std::unique_ptr<escher::ReleaseFenceSignaller> release_signaler,
+                escher::EscherWeakPtr escher = escher::EscherWeakPtr());
+};
+
+class GfxSystemForTest : public gfx::GfxSystem {
+ public:
+  static constexpr TypeId kTypeId = kGfx;
+
+  explicit GfxSystemForTest(
+      SystemContext context,
+      std::unique_ptr<gfx::DisplayManager> display_manager,
+      escher::impl::CommandBufferSequencer* command_buffer_sequencer)
+      : GfxSystem(std::move(context), std::move(display_manager)),
+        command_buffer_sequencer_(command_buffer_sequencer) {}
+
+  gfx::Engine* engine() { return engine_.get(); }
+
+ private:
+  std::unique_ptr<gfx::Engine> InitializeEngine() override {
+    return std::make_unique<EngineForTest>(
+        display_manager_.get(), std::make_unique<ReleaseFenceSignallerForTest>(
+                                    command_buffer_sequencer_));
+  }
+
+  std::unique_ptr<escher::Escher> InitializeEscher() override {
+    return nullptr;
+  }
+
+  escher::impl::CommandBufferSequencer* command_buffer_sequencer_;
+};
+
+// Device-independent "display"; for testing only. Needed to ensure GfxSystem
+// doesn't wait for a device-driven "display ready" signal.
+class TestDisplay : public scenic_impl::gfx::Display {
+ public:
+  TestDisplay(uint64_t id, uint32_t width_px, uint32_t height_px)
+      : Display(id, width_px, height_px) {}
+  ~TestDisplay() = default;
+  bool is_test_display() const override { return true; }
+};
+
+class MockSystemWithDelayedInitialization : public DummySystem {
+ public:
+  // Expose to tests.
+  using System::SetToInitialized;
+
+  explicit MockSystemWithDelayedInitialization(SystemContext context)
+      : DummySystem(std::move(context), false) {}
+};
+
+}  // namespace test
+}  // namespace scenic_impl
+
+#endif  // GARNET_LIB_UI_SCENIC_TESTS_MOCKS_H_
diff --git a/lib/ui/scenic/tests/scenic_gfx_test.cc b/lib/ui/scenic/tests/scenic_gfx_test.cc
new file mode 100644
index 0000000..31e141a
--- /dev/null
+++ b/lib/ui/scenic/tests/scenic_gfx_test.cc
@@ -0,0 +1,34 @@
+// Copyright 2019 The Fuchsia 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 "garnet/lib/ui/scenic/tests/scenic_gfx_test.h"
+
+#include "garnet/lib/ui/gfx/displays/display.h"
+#include "garnet/lib/ui/gfx/displays/display_manager.h"
+#include "garnet/lib/ui/scenic/tests/mocks.h"
+#include <lib/async-testutils/test_loop.h>
+
+namespace scenic_impl {
+namespace test {
+
+void ScenicGfxTest::InitializeScenic(Scenic* scenic) {
+  auto display_manager = std::make_unique<gfx::DisplayManager>();
+  display_manager->SetDefaultDisplayForTests(
+      std::make_unique<test::TestDisplay>(
+          /*id*/ 0, /* width */ 0, /* height */ 0));
+  command_buffer_sequencer_ =
+      std::make_unique<escher::impl::CommandBufferSequencer>();
+  scenic_->RegisterSystem<GfxSystemForTest>(std::move(display_manager),
+                                            command_buffer_sequencer_.get());
+
+  RunLoopUntilIdle();  // Finish initialization
+}
+
+void ScenicGfxTest::TearDown() {
+  ScenicTest::TearDown();
+  command_buffer_sequencer_.reset();
+}
+
+}  // namespace test
+}  // namespace scenic_impl
diff --git a/lib/ui/scenic/tests/scenic_gfx_test.h b/lib/ui/scenic/tests/scenic_gfx_test.h
new file mode 100644
index 0000000..f94c6f9
--- /dev/null
+++ b/lib/ui/scenic/tests/scenic_gfx_test.h
@@ -0,0 +1,27 @@
+// Copyright 2019 The Fuchsia 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 GARNET_LIB_UI_SCENIC_TESTS_SCENIC_GFX_TEST_H_
+#define GARNET_LIB_UI_SCENIC_TESTS_SCENIC_GFX_TEST_H_
+
+#include "garnet/lib/ui/scenic/tests/scenic_test.h"
+#include "garnet/public/lib/escher/impl/command_buffer_sequencer.h"
+
+namespace scenic_impl {
+namespace test {
+
+// Subclass of ScenicTest for tests requiring Scenic with a gfx system installed
+class ScenicGfxTest : public ScenicTest {
+ protected:
+  void TearDown() override;
+  void InitializeScenic(Scenic* scenic) override;
+
+  std::unique_ptr<escher::impl::CommandBufferSequencer>
+      command_buffer_sequencer_;
+};
+
+}  // namespace test
+}  // namespace scenic_impl
+
+#endif  // GARNET_LIB_UI_SCENIC_TESTS_SCENIC_GFX_TEST_H_
diff --git a/lib/ui/scenic/tests/scenic_test.cc b/lib/ui/scenic/tests/scenic_test.cc
index 585edfd..2ce38e5 100644
--- a/lib/ui/scenic/tests/scenic_test.cc
+++ b/lib/ui/scenic/tests/scenic_test.cc
@@ -2,7 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "garnet/lib/ui/scenic/tests/scenic_test.h"
+#include "garnet/lib/ui/scenic/tests/scenic_gfx_test.h"
+
+#include "garnet/lib/ui/gfx/displays/display.h"
+#include "garnet/lib/ui/gfx/displays/display_manager.h"
+#include "garnet/lib/ui/scenic/tests/mocks.h"
 
 namespace scenic_impl {
 namespace test {
@@ -12,7 +16,7 @@
 void ScenicTest::SetUp() {
   // TODO(SCN-720): Wrap CreateFromStartupInfo using ::gtest::Environment
   // instead of this hack.  This code has the chance to break non-ScenicTests.
-  if (app_context_ == nullptr) {
+  if (app_context_.get() == nullptr) {
     app_context_ = component::StartupContext::CreateFromStartupInfo();
   }
   scenic_ =
@@ -24,10 +28,21 @@
   reported_errors_.clear();
   events_.clear();
   scenic_.reset();
+  app_context_.reset();
 }
 
 void ScenicTest::InitializeScenic(Scenic* scenic) {}
 
+std::unique_ptr<::scenic::Session> ScenicTest::CreateSession() {
+  fuchsia::ui::scenic::SessionPtr session_ptr;
+  fidl::InterfaceHandle<fuchsia::ui::scenic::SessionListener> listener_handle;
+  fidl::InterfaceRequest<fuchsia::ui::scenic::SessionListener>
+      listener_request = listener_handle.NewRequest();
+  scenic()->CreateSession(session_ptr.NewRequest(), std::move(listener_handle));
+  return std::make_unique<::scenic::Session>(std::move(session_ptr),
+                                             std::move(listener_request));
+}
+
 void ScenicTest::ReportError(fxl::LogSeverity severity,
                              std::string error_string) {
 // Typically, we don't want to log expected errors when running the tests.
diff --git a/lib/ui/scenic/tests/scenic_test.h b/lib/ui/scenic/tests/scenic_test.h
index 82eb15f..b2d692a 100644
--- a/lib/ui/scenic/tests/scenic_test.h
+++ b/lib/ui/scenic/tests/scenic_test.h
@@ -9,6 +9,7 @@
 
 #include "garnet/lib/ui/scenic/scenic.h"
 #include "garnet/lib/ui/scenic/util/error_reporter.h"
+#include "garnet/public/lib/ui/scenic/cpp/session.h"
 #include "gtest/gtest.h"
 #include "lib/component/cpp/startup_context.h"
 #include "lib/fxl/tasks/task_runner.h"
@@ -22,6 +23,9 @@
 class ScenicTest : public ::gtest::TestLoopFixture,
                    public ErrorReporter,
                    public EventReporter {
+ public:
+  std::unique_ptr<::scenic::Session> CreateSession();
+
  protected:
   // ::testing::Test virtual method.
   void SetUp() override;
diff --git a/lib/ui/scenic/tests/scenic_unittest.cc b/lib/ui/scenic/tests/scenic_unittest.cc
index 329d06c..ff9743b 100644
--- a/lib/ui/scenic/tests/scenic_unittest.cc
+++ b/lib/ui/scenic/tests/scenic_unittest.cc
@@ -3,39 +3,47 @@
 // found in the LICENSE file.
 
 #include "garnet/lib/ui/scenic/tests/dummy_system.h"
+#include "garnet/lib/ui/scenic/tests/mocks.h"
+#include "garnet/lib/ui/scenic/tests/scenic_gfx_test.h"
 #include "garnet/lib/ui/scenic/tests/scenic_test.h"
 
+#include "gtest/gtest.h"
+
 namespace scenic_impl {
 namespace test {
 
-namespace {
-
-class MockSystemWithDelayedInitialization : public DummySystem {
- public:
-  // Expose to tests.
-  using System::SetToInitialized;
-
-  explicit MockSystemWithDelayedInitialization(SystemContext context)
-      : DummySystem(std::move(context), false) {}
-};
-
-}  // anonymous namespace
-
 TEST_F(ScenicTest, SessionCreatedAfterAllSystemsInitialized) {
   auto mock_system =
-      scenic()->RegisterSystem<test::MockSystemWithDelayedInitialization>();
+      scenic()->RegisterSystem<MockSystemWithDelayedInitialization>();
 
-  EXPECT_EQ(0U, scenic()->num_sessions());
+  EXPECT_EQ(scenic()->num_sessions(), 0U);
 
   // Request session creation, which doesn't occur yet because system isn't
   // initialized.
-  fuchsia::ui::scenic::SessionPtr session;
-  scenic()->CreateSession(session.NewRequest(), nullptr);
-  EXPECT_EQ(0U, scenic()->num_sessions());
+  auto session = CreateSession();
+  EXPECT_EQ(scenic()->num_sessions(), 0U);
 
   // Initializing the system allows the session to be created.
   mock_system->SetToInitialized();
-  EXPECT_EQ(1U, scenic()->num_sessions());
+  EXPECT_EQ(scenic()->num_sessions(), 1U);
+}
+
+TEST_F(ScenicGfxTest, InvalidPresentCall_ShouldDestroySession) {
+  EXPECT_EQ(scenic()->num_sessions(), 0U);
+  auto session = CreateSession();
+  EXPECT_EQ(scenic()->num_sessions(), 1U);
+
+  session->Present(/*Presentation Time*/ 10,
+                   /*Present Callback*/ [](auto){});
+
+  // Trigger error by making a present call with an earlier presentation time
+  // than the previous call to present
+  session->Present(/*Presentation Time*/ 0,
+                   /*Present Callback*/ [](auto){});
+
+  RunLoopUntilIdle();
+
+  EXPECT_EQ(scenic()->num_sessions(), 0U);
 }
 
 }  // namespace test