 core/lib/Drupal/Core/Ajax/AjaxResponse.php         |  6 ++--
 .../Drupal/Core/Render/BareHtmlPageRenderer.php    |  4 +--
 .../Core/Render/MainContent/HtmlRenderer.php       | 10 +++----
 core/lib/Drupal/Core/Render/Renderer.php           | 21 ++++++++++----
 .../block/src/Tests/BlockViewBuilderTest.php       | 33 ++++++++++++++--------
 .../src/Tests/BlockContentCacheTagsTest.php        |  2 +-
 .../ckeditor/src/Plugin/Editor/CKEditor.php        | 18 ++++++++++--
 .../Tests/CommentDefaultFormatterCacheTagsTest.php | 16 +++++++++--
 .../contextual/src/ContextualController.php        |  2 +-
 core/modules/field/field.module                    |  2 +-
 .../EntityReferenceFormatterTest.php               |  8 ++++--
 .../simpletest/src/Form/SimpletestResultsForm.php  | 10 +++----
 .../simpletest/src/Form/SimpletestTestForm.php     | 32 +++++++++++++++++++--
 .../simpletest/src/Tests/KernelTestBaseTest.php    |  5 ++--
 .../src/Plugin/views/style/StylePluginBase.php     |  2 +-
 .../src/Tests/Handler/FieldFieldAccessTestBase.php |  4 +--
 core/modules/views/src/Tests/ViewElementTest.php   | 10 ++++---
 core/modules/views/src/Tests/ViewRenderTest.php    |  2 +-
 .../Tests/Core/Render/RendererBubblingTest.php     | 22 +++++++--------
 .../Drupal/Tests/Core/Render/RendererTest.php      | 22 +++++++--------
 20 files changed, 154 insertions(+), 77 deletions(-)

diff --git a/core/lib/Drupal/Core/Ajax/AjaxResponse.php b/core/lib/Drupal/Core/Ajax/AjaxResponse.php
index a83898d..7d67454 100644
--- a/core/lib/Drupal/Core/Ajax/AjaxResponse.php
+++ b/core/lib/Drupal/Core/Ajax/AjaxResponse.php
@@ -198,15 +198,15 @@ protected function ajaxRender(Request $request) {
     $renderer = $this->getRenderer();
     if (!empty($css_assets)) {
       $css_render_array = \Drupal::service('asset.css.collection_renderer')->render($css_assets);
-      $resource_commands[] = new AddCssCommand($renderer->render($css_render_array));
+      $resource_commands[] = new AddCssCommand($renderer->renderPlain($css_render_array));
     }
     if (!empty($js_assets_header)) {
       $js_header_render_array = \Drupal::service('asset.js.collection_renderer')->render($js_assets_header);
-      $resource_commands[] = new PrependCommand('head', $renderer->render($js_header_render_array));
+      $resource_commands[] = new PrependCommand('head', $renderer->renderPlain($js_header_render_array));
     }
     if (!empty($js_assets_footer)) {
       $js_footer_render_array = \Drupal::service('asset.js.collection_renderer')->render($js_assets_footer);
-      $resource_commands[] = new AppendCommand('body', $renderer->render($js_footer_render_array));
+      $resource_commands[] = new AppendCommand('body', $renderer->renderPlain($js_footer_render_array));
     }
     foreach (array_reverse($resource_commands) as $resource_command) {
       $this->addCommand($resource_command, TRUE);
diff --git a/core/lib/Drupal/Core/Render/BareHtmlPageRenderer.php b/core/lib/Drupal/Core/Render/BareHtmlPageRenderer.php
index c8012c6..58c5507 100644
--- a/core/lib/Drupal/Core/Render/BareHtmlPageRenderer.php
+++ b/core/lib/Drupal/Core/Render/BareHtmlPageRenderer.php
@@ -59,12 +59,12 @@ public function renderBarePage(array $content, $title, $page_theme_property, arr
     // \Drupal\Core\Render\MainContent\HtmlRenderer::renderResponse() for more
     // information about this; the exact same pattern is used there and
     // explained in detail there.
-    $this->renderer->render($html['page'], TRUE);
+    $this->renderer->renderRoot($html['page']);
 
     // Add the bare minimum of attachments from the system module and the
     // current maintenance theme.
     system_page_attachments($html['page']);
-    return $this->renderer->render($html);
+    return $this->renderer->renderPlain($html);
   }
 
 }
diff --git a/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php b/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php
index d61bb24..05e38ec 100644
--- a/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php
+++ b/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php
@@ -126,14 +126,14 @@ public function renderResponse(array $main_content, Request $request, RouteMatch
     // and hence may not replace any placeholders (because they might add yet
     // more assets to be attached), and therefore it must be rendered with
     // drupal_render(), not drupal_render_root().
-    $this->renderer->render($html['page'], TRUE);
+    $this->renderer->renderRoot($html['page']);
     if (isset($html['page_top'])) {
-      $this->renderer->render($html['page_top'], TRUE);
+      $this->renderer->renderRoot($html['page_top']);
     }
     if (isset($html['page_bottom'])) {
-      $this->renderer->render($html['page_bottom'], TRUE);
+      $this->renderer->renderRoot($html['page_bottom']);
     }
-    $content = $this->renderer->render($html);
+    $content = $this->renderer->renderPlain($html, FALSE);
 
     $response = new CacheableResponse($content, 200,[
       'Content-Type' => 'text/html; charset=UTF-8',
@@ -197,7 +197,7 @@ protected function prepare(array $main_content, Request $request, RouteMatchInte
       // ::renderResponse().
       // @todo Remove this once https://www.drupal.org/node/2359901 lands.
       if (!empty($main_content)) {
-        $this->renderer->render($main_content, FALSE);
+        $this->renderer->renderPlain($main_content, FALSE);
         $main_content = $this->renderCache->getCacheableRenderArray($main_content) + [
           '#title' => isset($main_content['#title']) ? $main_content['#title'] : NULL
         ];
diff --git a/core/lib/Drupal/Core/Render/Renderer.php b/core/lib/Drupal/Core/Render/Renderer.php
index a91b14b..76153bd 100644
--- a/core/lib/Drupal/Core/Render/Renderer.php
+++ b/core/lib/Drupal/Core/Render/Renderer.php
@@ -89,16 +89,27 @@ public function __construct(ControllerResolverInterface $controller_resolver, Th
    * {@inheritdoc}
    */
   public function renderRoot(&$elements) {
-    return $this->render($elements, TRUE);
+    if (isset(static::$stack)) {
+      throw new \LogicException('A stray renderRoot() invocation is causing bubbling of attached assets to break.');
+    }
+    static::$stack = new \SplStack();
+    $output = $this->render($elements, TRUE);
+    $this->resetStack();
+
+    return $output;
   }
 
   /**
    * {@inheritdoc}
+   *
+   * @todo Rename to ::renderInIsolation()
    */
-  public function renderPlain(&$elements) {
+  public function renderPlain(&$elements, $is_root_call = TRUE) {
     $current_stack = static::$stack;
-    $this->resetStack();
-    $output = $this->renderRoot($elements);
+
+    static::$stack = new \SplStack();
+    $output = $this->render($elements, $is_root_call);
+
     static::$stack = $current_stack;
     return $output;
   }
@@ -186,7 +197,7 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
     }
 
     if (!isset(static::$stack)) {
-      static::$stack = new \SplStack();
+      throw new \LogicException("Render Stack is empty, because render() was called outside of a renderRoot() or renderPlain() call. Use renderPlain() / renderRoot() or #pre_render pattern instead.");
     }
     static::$stack->push(new BubbleableMetadata());
 
diff --git a/core/modules/block/src/Tests/BlockViewBuilderTest.php b/core/modules/block/src/Tests/BlockViewBuilderTest.php
index 7a7aa64..ccb53e4 100644
--- a/core/modules/block/src/Tests/BlockViewBuilderTest.php
+++ b/core/modules/block/src/Tests/BlockViewBuilderTest.php
@@ -44,6 +44,13 @@ class BlockViewBuilderTest extends KernelTestBase {
   protected $controller;
 
   /**
+   * The renderer.
+   *
+   * @var \Drupal\Core\Render\RendererInterface
+   */
+  protected $renderer;
+
+  /**
    * {@inheritdoc}
    */
   protected function setUp() {
@@ -64,6 +71,8 @@ protected function setUp() {
     $this->block->save();
 
     $this->container->get('cache.render')->deleteAll();
+
+    $this->renderer = $this->container->get('renderer');
   }
 
   /**
@@ -90,7 +99,7 @@ public function testBasicRendering() {
     $expected[] = '  </div>';
     $expected[] = '';
     $expected_output = implode("\n", $expected);
-    $this->assertEqual(drupal_render($output), $expected_output);
+    $this->assertEqual($this->renderer->renderRoot($output), $expected_output);
 
     // Reset the HTML IDs so that the next render is not affected.
     Html::resetSeenIds();
@@ -115,7 +124,7 @@ public function testBasicRendering() {
     $expected[] = '  </div>';
     $expected[] = '';
     $expected_output = implode("\n", $expected);
-    $this->assertEqual(drupal_render($output), $expected_output);
+    $this->assertEqual($this->renderer->renderRoot($output), $expected_output);
   }
 
   /**
@@ -144,7 +153,7 @@ public function testBlockViewBuilderCache() {
    * @see ::testBlockViewBuilderCache()
    */
   protected function verifyRenderCacheHandling() {
-    // Force a request via GET so we can get drupal_render() cache working.
+    // Force a request via GET so we can test the render cache.
     $request = \Drupal::request();
     $request_method = $request->server->get('REQUEST_METHOD');
     $request->setMethod('GET');
@@ -152,7 +161,7 @@ protected function verifyRenderCacheHandling() {
     // Test that a cache entry is created.
     $build = $this->getBlockRenderArray();
     $cid = 'entity_view:block:test_block:en:core';
-    drupal_render($build);
+    $this->renderer->renderRoot($build);
     $this->assertTrue($this->container->get('cache.render')->get($cid), 'The block render element has been cached.');
 
     // Re-save the block and check that the cache entry has been deleted.
@@ -166,7 +175,7 @@ protected function verifyRenderCacheHandling() {
     // removes it.
     $build['#block'] = $this->block;
 
-    drupal_render($build);
+    $this->renderer->renderRoot($build);
     $this->assertTrue($this->container->get('cache.render')->get($cid), 'The block render element has been cached.');
     $this->block->delete();
     $this->assertFalse($this->container->get('cache.render')->get($cid), 'The block render cache entry has been cleared when the block was deleted.');
@@ -181,17 +190,17 @@ protected function verifyRenderCacheHandling() {
   public function testBlockViewBuilderAlter() {
     // Establish baseline.
     $build = $this->getBlockRenderArray();
-    $this->assertIdentical(drupal_render($build), 'Llamas &gt; unicorns!');
+    $this->assertIdentical($this->renderer->renderRoot($build), 'Llamas &gt; unicorns!');
 
     // Enable the block view alter hook that adds a suffix, for basic testing.
     \Drupal::state()->set('block_test_view_alter_suffix', TRUE);
     Cache::invalidateTags($this->block->getCacheTags());
     $build = $this->getBlockRenderArray();
     $this->assertTrue(isset($build['#suffix']) && $build['#suffix'] === '<br>Goodbye!', 'A block with content is altered.');
-    $this->assertIdentical(drupal_render($build), 'Llamas &gt; unicorns!<br>Goodbye!');
+    $this->assertIdentical($this->renderer->renderRoot($build), 'Llamas &gt; unicorns!<br>Goodbye!');
     \Drupal::state()->set('block_test_view_alter_suffix', FALSE);
 
-    // Force a request via GET so we can get drupal_render() cache working.
+    // Force a request via GET so we can test the render cache.
     $request = \Drupal::request();
     $request_method = $request->server->get('REQUEST_METHOD');
     $request->setMethod('GET');
@@ -209,7 +218,7 @@ public function testBlockViewBuilderAlter() {
     $expected_keys = array_merge($default_keys, array($alter_add_key));
     $build = $this->getBlockRenderArray();
     $this->assertIdentical($expected_keys, $build['#cache']['keys'], 'An altered cacheable block has the expected cache keys.');
-    $this->assertIdentical(drupal_render($build), '');
+    $this->assertIdentical($this->renderer->renderRoot($build), '');
     $cache_entry = $this->container->get('cache.render')->get($cid);
     $this->assertTrue($cache_entry, 'The block render element has been cached with the expected cache ID.');
     $expected_tags = array_merge($default_tags, ['rendered']);
@@ -224,7 +233,7 @@ public function testBlockViewBuilderAlter() {
     $build = $this->getBlockRenderArray();
     sort($build['#cache']['tags']);
     $this->assertIdentical($expected_tags, $build['#cache']['tags'], 'An altered cacheable block has the expected cache tags.');
-    $this->assertIdentical(drupal_render($build), '');
+    $this->assertIdentical($this->renderer->renderRoot($build), '');
     $cache_entry = $this->container->get('cache.render')->get($cid);
     $this->assertTrue($cache_entry, 'The block render element has been cached with the expected cache ID.');
     $expected_tags = array_merge($default_tags, [$alter_add_tag, 'rendered']);
@@ -236,8 +245,8 @@ public function testBlockViewBuilderAlter() {
     // alter the eventual content.
     \Drupal::state()->set('block_test_view_alter_append_pre_render_prefix', TRUE);
     $build = $this->getBlockRenderArray();
-    $this->assertFalse(isset($build['#prefix']), 'The appended #pre_render callback has not yet run before calling drupal_render().');
-    $this->assertIdentical(drupal_render($build), 'Hiya!<br>');
+    $this->assertFalse(isset($build['#prefix']), 'The appended #pre_render callback has not yet run before rendering.');
+    $this->assertIdentical($this->renderer->renderRoot($build), 'Hiya!<br>');
     $this->assertTrue(isset($build['#prefix']) && $build['#prefix'] === 'Hiya!<br>', 'A cached block without content is altered.');
 
     // Restore the previous request method.
diff --git a/core/modules/block_content/src/Tests/BlockContentCacheTagsTest.php b/core/modules/block_content/src/Tests/BlockContentCacheTagsTest.php
index 582d832..fe2c789 100644
--- a/core/modules/block_content/src/Tests/BlockContentCacheTagsTest.php
+++ b/core/modules/block_content/src/Tests/BlockContentCacheTagsTest.php
@@ -84,7 +84,7 @@ public function testBlock() {
     //   Drupal\Core\Render\Renderer.
     $request_stack = $this->container->get('request_stack');
     $request_stack->push(new Request());
-    $this->container->get('renderer')->render($build);
+    $this->container->get('renderer')->renderRoot($build);
     $request_stack->pop();
 
     // Expected keys, contexts, and tags for the block.
diff --git a/core/modules/ckeditor/src/Plugin/Editor/CKEditor.php b/core/modules/ckeditor/src/Plugin/Editor/CKEditor.php
index 949c8e5..0e237b1 100644
--- a/core/modules/ckeditor/src/Plugin/Editor/CKEditor.php
+++ b/core/modules/ckeditor/src/Plugin/Editor/CKEditor.php
@@ -13,6 +13,7 @@
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\Core\Render\Element;
+use Drupal\Core\Render\RendererInterface;
 use Drupal\editor\Plugin\EditorBase;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 use Drupal\editor\Entity\Editor as EditorEntity;
@@ -56,6 +57,13 @@ class CKEditor extends EditorBase implements ContainerFactoryPluginInterface {
   protected $ckeditorPluginManager;
 
   /**
+   * The renderer.
+   *
+   * @var \Drupal\Core\Render\RendererInterface
+   */
+  protected $renderer;
+
+  /**
    * Constructs a Drupal\Component\Plugin\PluginBase object.
    *
    * @param array $configuration
@@ -70,12 +78,15 @@ class CKEditor extends EditorBase implements ContainerFactoryPluginInterface {
    *   The module handler to invoke hooks on.
    * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
    *   The language manager.
+   * @param \Drupal\Core\Render\RendererInterface $renderer
+   *   The renderer.
    */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, CKEditorPluginManager $ckeditor_plugin_manager, ModuleHandlerInterface $module_handler, LanguageManagerInterface $language_manager) {
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, CKEditorPluginManager $ckeditor_plugin_manager, ModuleHandlerInterface $module_handler, LanguageManagerInterface $language_manager, RendererInterface $renderer) {
     parent::__construct($configuration, $plugin_id, $plugin_definition);
     $this->ckeditorPluginManager = $ckeditor_plugin_manager;
     $this->moduleHandler = $module_handler;
     $this->languageManager = $language_manager;
+    $this->renderer = $renderer;
   }
 
   /**
@@ -88,7 +99,8 @@ public static function create(ContainerInterface $container, array $configuratio
       $plugin_definition,
       $container->get('plugin.manager.ckeditor.plugin'),
       $container->get('module_handler'),
-      $container->get('language_manager')
+      $container->get('language_manager'),
+      $container->get('renderer')
     );
   }
 
@@ -145,7 +157,7 @@ public function settingsForm(array $form, FormStateInterface $form_state, Editor
         'library' => array('ckeditor/drupal.ckeditor.admin'),
         'drupalSettings' => [
           'ckeditor' => [
-            'toolbarAdmin' => drupal_render($ckeditor_settings_toolbar),
+            'toolbarAdmin' => $this->renderer->renderPlain($ckeditor_settings_toolbar),
           ],
         ],
       ),
diff --git a/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php b/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php
index 342765f..884bc03 100644
--- a/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php
+++ b/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php
@@ -57,6 +57,8 @@ protected function setUp() {
    * Tests the bubbling of cache tags.
    */
   public function testCacheTags() {
+    $renderer = $this->container->get('renderer');
+
     // Create the entity that will be commented upon.
     $commented_entity = entity_create('entity_test', array('name' => $this->randomMachineName()));
     $commented_entity->save();
@@ -65,11 +67,16 @@ public function testCacheTags() {
     $build = \Drupal::entityManager()
       ->getViewBuilder('entity_test')
       ->view($commented_entity);
-    drupal_render($build);
+    $renderer->renderRoot($build);
     $expected_cache_tags = array(
       'entity_test_view',
       'entity_test:'  . $commented_entity->id(),
       'comment_list',
+      'config:core.entity_form_display.comment.comment.default',
+      'config:field.field.comment.comment.comment_body',
+      'config:field.field.entity_test.entity_test.comment',
+      'config:field.storage.comment.comment_body',
+      'config:user.settings',
     );
     sort($expected_cache_tags);
     $this->assertEqual($build['#cache']['tags'], $expected_cache_tags, 'The test entity has the expected cache tags before it has comments.');
@@ -102,7 +109,7 @@ public function testCacheTags() {
     $build = \Drupal::entityManager()
       ->getViewBuilder('entity_test')
       ->view($commented_entity);
-    drupal_render($build);
+    $renderer->renderRoot($build);
     $expected_cache_tags = array(
       'entity_test_view',
       'entity_test:' . $commented_entity->id(),
@@ -112,6 +119,11 @@ public function testCacheTags() {
       'config:filter.format.plain_text',
       'user_view',
       'user:2',
+      'config:core.entity_form_display.comment.comment.default',
+      'config:field.field.comment.comment.comment_body',
+      'config:field.field.entity_test.entity_test.comment',
+      'config:field.storage.comment.comment_body',
+      'config:user.settings',
     );
     sort($expected_cache_tags);
     $this->assertEqual($build['#cache']['tags'], $expected_cache_tags, 'The test entity has the expected cache tags when it has comments.');
diff --git a/core/modules/contextual/src/ContextualController.php b/core/modules/contextual/src/ContextualController.php
index aeee4a6..975113d 100644
--- a/core/modules/contextual/src/ContextualController.php
+++ b/core/modules/contextual/src/ContextualController.php
@@ -44,7 +44,7 @@ public function render(Request $request) {
         '#type' => 'contextual_links',
         '#contextual_links' => _contextual_id_to_links($id),
       );
-      $rendered[$id] = drupal_render($element);
+      $rendered[$id] = $this->container->get('renderer')->renderRoot($element);
     }
 
     return new JsonResponse($rendered);
diff --git a/core/modules/field/field.module b/core/modules/field/field.module
index 88ccff9..7e52327 100644
--- a/core/modules/field/field.module
+++ b/core/modules/field/field.module
@@ -121,7 +121,7 @@ function field_help($route_name, RouteMatchInterface $route_match) {
           '#theme' => 'item_list',
           '#items' => $items,
         );
-        $output .= drupal_render($item_list);
+        $output .= \Drupal::service('renderer')->render($item_list);
         $output .= '</dd>';
       }
 
diff --git a/core/modules/field/src/Tests/EntityReference/EntityReferenceFormatterTest.php b/core/modules/field/src/Tests/EntityReference/EntityReferenceFormatterTest.php
index 52fdc08..022ec5c 100644
--- a/core/modules/field/src/Tests/EntityReference/EntityReferenceFormatterTest.php
+++ b/core/modules/field/src/Tests/EntityReference/EntityReferenceFormatterTest.php
@@ -179,6 +179,7 @@ public function testIdFormatter() {
    * Tests the entity formatter.
    */
   public function testEntityFormatter() {
+    $renderer = $this->container->get('renderer');
     $formatter = 'entity_reference_entity_view';
     $build = $this->buildRenderArray([$this->referencedEntity, $this->unsavedReferencedEntity], $formatter);
 
@@ -196,7 +197,7 @@ public function testEntityFormatter() {
       </div>
 </div>
 ';
-    drupal_render($build[0]);
+    $renderer->renderRoot($build[0]);
     $this->assertEqual($build[0]['#markup'], 'default | ' . $this->referencedEntity->label() .  $expected_rendered_name_field_1 . $expected_rendered_body_field_1, sprintf('The markup returned by the %s formatter is correct for an item with a saved entity.', $formatter));
     $expected_cache_tags = Cache::mergeTags(
       \Drupal::entityManager()->getViewBuilder($this->entityType)->getCacheTags(),
@@ -206,7 +207,7 @@ public function testEntityFormatter() {
     $this->assertEqual($build[0]['#cache']['tags'], $expected_cache_tags, format_string('The @formatter formatter has the expected cache tags.', array('@formatter' => $formatter)));
 
     // Test the second field item.
-    drupal_render($build[1]);
+    $renderer->renderRoot($build[1]);
     $this->assertEqual($build[1]['#markup'], $this->unsavedReferencedEntity->label(), sprintf('The markup returned by the %s formatter is correct for an item with a unsaved entity.', $formatter));
   }
 
@@ -214,6 +215,7 @@ public function testEntityFormatter() {
    * Tests the label formatter.
    */
   public function testLabelFormatter() {
+    $renderer = $this->container->get('renderer');
     $formatter = 'entity_reference_label';
 
     // The 'link' settings is TRUE by default.
@@ -237,7 +239,7 @@ public function testLabelFormatter() {
         'tags' => $this->referencedEntity->getCacheTags(),
       ),
     );
-    $this->assertEqual(drupal_render($build[0]), drupal_render($expected_item_1), sprintf('The markup returned by the %s formatter is correct for an item with a saved entity.', $formatter));
+    $this->assertEqual($renderer->renderRoot($build[0]), $renderer->renderRoot($expected_item_1), sprintf('The markup returned by the %s formatter is correct for an item with a saved entity.', $formatter));
     $this->assertEqual(CacheableMetadata::createFromRenderArray($build[0]), CacheableMetadata::createFromRenderArray($expected_item_1));
 
     // The second referenced entity is "autocreated", therefore not saved and
diff --git a/core/modules/simpletest/src/Form/SimpletestResultsForm.php b/core/modules/simpletest/src/Form/SimpletestResultsForm.php
index d41654f..a1b97ba 100644
--- a/core/modules/simpletest/src/Form/SimpletestResultsForm.php
+++ b/core/modules/simpletest/src/Form/SimpletestResultsForm.php
@@ -94,10 +94,10 @@ protected static function buildStatusImageMap() {
       '#alt' => 'Debug',
     );
     return array(
-      'pass' => drupal_render($image_pass),
-      'fail' => drupal_render($image_fail),
-      'exception' => drupal_render($image_exception),
-      'debug' => drupal_render($image_debug),
+      'pass' => $image_pass,
+      'fail' => $image_fail,
+      'exception' => $image_exception,
+      'debug' => $image_debug,
     );
   }
 
@@ -205,7 +205,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
     // Under normal circumstances, a form object's submitForm() should never be
     // called directly, FormBuilder::submitForm() should be called instead.
     // However, it calls $form_state->setProgrammed(), which disables the Batch API.
-    $simpletest_test_form = new SimpletestTestForm();
+    $simpletest_test_form = SimpletestTestForm::create(\Drupal::getContainer());
     $simpletest_test_form->buildForm($form_execute, $form_state_execute);
     $simpletest_test_form->submitForm($form_execute, $form_state_execute);
     if ($redirect = $form_state_execute->getRedirect()) {
diff --git a/core/modules/simpletest/src/Form/SimpletestTestForm.php b/core/modules/simpletest/src/Form/SimpletestTestForm.php
index 92c8a54..f6d0367 100644
--- a/core/modules/simpletest/src/Form/SimpletestTestForm.php
+++ b/core/modules/simpletest/src/Form/SimpletestTestForm.php
@@ -11,6 +11,8 @@
 use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Core\Form\FormBase;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Render\RendererInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * List tests arranged in groups that can be selected and run.
@@ -18,6 +20,32 @@
 class SimpletestTestForm extends FormBase {
 
   /**
+   * The renderer.
+   *
+   * @var \Drupal\Core\Render\RendererInterface
+   */
+  protected $renderer;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('renderer')
+    );
+  }
+
+  /**
+   * Constructs a new SimpletestTestForm.
+   *
+   * @param \Drupal\Core\Render\RendererInterface $renderer
+   *   The renderer.
+   */
+  public function __construct(RendererInterface $renderer) {
+    $this->renderer = $renderer;
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function getFormId() {
@@ -99,8 +127,8 @@ public function buildForm(array $form, FormStateInterface $form_state) {
       '#suffix' => '<a href="#" class="simpletest-collapse">(' . $this->t('Collapse') . ')</a>',
     );
     $form['tests']['#attached']['drupalSettings']['simpleTest']['images'] = [
-      drupal_render($image_collapsed),
-      drupal_render($image_extended),
+      $this->renderer->renderPlain($image_collapsed),
+      $this->renderer->renderPlain($image_extended),
     ];
 
     // Generate the list of tests arranged by group.
diff --git a/core/modules/simpletest/src/Tests/KernelTestBaseTest.php b/core/modules/simpletest/src/Tests/KernelTestBaseTest.php
index 2e8a8c2..4ae7ef0 100644
--- a/core/modules/simpletest/src/Tests/KernelTestBaseTest.php
+++ b/core/modules/simpletest/src/Tests/KernelTestBaseTest.php
@@ -284,6 +284,7 @@ function testEnableModulesFixedList() {
    * Tests that _theme() works right after loading a module.
    */
   function testEnableModulesTheme() {
+    $renderer = $this->container->get('renderer');
     $original_element = $element = array(
       '#type' => 'container',
       '#markup' => 'Foo',
@@ -291,11 +292,11 @@ function testEnableModulesTheme() {
     );
     $this->enableModules(array('system'));
     // _theme() throws an exception if modules are not loaded yet.
-    $this->assertTrue(drupal_render($element));
+    $this->assertTrue($renderer->renderRoot($element));
 
     $element = $original_element;
     $this->disableModules(array('entity_test'));
-    $this->assertTrue(drupal_render($element));
+    $this->assertTrue($renderer->renderRoot($element));
   }
 
   /**
diff --git a/core/modules/views/src/Plugin/views/style/StylePluginBase.php b/core/modules/views/src/Plugin/views/style/StylePluginBase.php
index 8fa4617..af10867 100644
--- a/core/modules/views/src/Plugin/views/style/StylePluginBase.php
+++ b/core/modules/views/src/Plugin/views/style/StylePluginBase.php
@@ -678,7 +678,7 @@ protected function renderFields(array $result) {
             '#cache_properties' => $field_ids,
           ];
           $renderer->addCacheableDependency($data, $this->view->storage);
-          $renderer->render($data);
+          $renderer->renderPlain($data);
 
           // Extract field output from the render array and post process it.
           $fields = $this->view->field;
diff --git a/core/modules/views/src/Tests/Handler/FieldFieldAccessTestBase.php b/core/modules/views/src/Tests/Handler/FieldFieldAccessTestBase.php
index 5554a8a..09b397d 100644
--- a/core/modules/views/src/Tests/Handler/FieldFieldAccessTestBase.php
+++ b/core/modules/views/src/Tests/Handler/FieldFieldAccessTestBase.php
@@ -125,7 +125,7 @@ protected function assertFieldAccess($entity_type_id, $field_name, $field_conten
     $account_switcher->switchTo($this->userWithAccess);
     $executable = Views::getView($view_id);
     $build = $executable->preview();
-    $this->setRawContent($renderer->render($build));
+    $this->setRawContent($renderer->renderRoot($build));
 
     $this->assertText($field_content);
     $this->assertTrue(isset($executable->field[$field_name]));
@@ -133,7 +133,7 @@ protected function assertFieldAccess($entity_type_id, $field_name, $field_conten
     $account_switcher->switchTo($this->userWithoutAccess);
     $executable = Views::getView($view_id);
     $build = $executable->preview();
-    $this->setRawContent($renderer->render($build));
+    $this->setRawContent($renderer->renderRoot($build));
 
     $this->assertNoText($field_content);
     $this->assertFalse(isset($executable->field[$field_name]));
diff --git a/core/modules/views/src/Tests/ViewElementTest.php b/core/modules/views/src/Tests/ViewElementTest.php
index bd17747..5b53c57 100644
--- a/core/modules/views/src/Tests/ViewElementTest.php
+++ b/core/modules/views/src/Tests/ViewElementTest.php
@@ -52,12 +52,13 @@ protected function setUp() {
    * Tests the rendered output and form output of a view element.
    */
   public function testViewElement() {
+    $renderer = $this->container->get('renderer');
     $view = Views::getView('test_view_embed');
     $view->setDisplay();
 
     // Set the content as our rendered array.
     $render = $this->render;
-    $this->setRawContent(drupal_render($render));
+    $this->setRawContent($renderer->renderRoot($render));
 
     $xpath = $this->xpath('//div[@class="views-element-container"]');
     $this->assertTrue($xpath, 'The view container has been found in the rendered output.');
@@ -102,7 +103,7 @@ public function testViewElement() {
 
     // Test the render array again.
     $render = $this->render;
-    $this->setRawContent(drupal_render($render));
+    $this->setRawContent($renderer->renderRoot($render));
     // There should be 1 row in the results, 'John' arg 25.
     $xpath = $this->xpath('//div[@class="view-content"]/div');
     $this->assertEqual(count($xpath), 1);
@@ -118,13 +119,14 @@ public function testViewElement() {
    * embed display plugin.
    */
   public function testViewElementEmbed() {
+    $renderer = $this->container->get('renderer');
     $view = Views::getView('test_view_embed');
     $view->setDisplay('embed_1');
 
     // Set the content as our rendered array.
     $render = $this->render;
     $render['#embed'] = TRUE;
-    $this->setRawContent(drupal_render($render));
+    $this->setRawContent($renderer->renderRoot($render));
 
     $xpath = $this->xpath('//div[@class="views-element-container"]');
     $this->assertTrue($xpath, 'The view container has been found in the rendered output.');
@@ -170,7 +172,7 @@ public function testViewElementEmbed() {
     // Test the render array again.
     $render = $this->render;
     $render['#embed'] = TRUE;
-    $this->setRawContent(drupal_render($render));
+    $this->setRawContent($renderer->renderRoot($render));
     // There should be 1 row in the results, 'John' arg 25.
     $xpath = $this->xpath('//div[@class="view-content"]/div');
     $this->assertEqual(count($xpath), 1);
diff --git a/core/modules/views/src/Tests/ViewRenderTest.php b/core/modules/views/src/Tests/ViewRenderTest.php
index ac60e6a..bbf3fe1 100644
--- a/core/modules/views/src/Tests/ViewRenderTest.php
+++ b/core/modules/views/src/Tests/ViewRenderTest.php
@@ -40,7 +40,7 @@ public function testRender() {
     // Make sure that the rendering just calls the preprocess function once.
     $view = Views::getView('test_view_render');
     $output = $view->preview();
-    drupal_render($output);
+    $this->container->get('renderer')->renderRoot($output);
 
     $this->assertEqual(\Drupal::state()->get('views_render.test'), 1);
   }
diff --git a/core/tests/Drupal/Tests/Core/Render/RendererBubblingTest.php b/core/tests/Drupal/Tests/Core/Render/RendererBubblingTest.php
index e0c7ff4..2037588 100644
--- a/core/tests/Drupal/Tests/Core/Render/RendererBubblingTest.php
+++ b/core/tests/Drupal/Tests/Core/Render/RendererBubblingTest.php
@@ -66,14 +66,14 @@ public function testBubblingWithoutPreRender() {
     ];
 
     // Render the element and verify the presence of #attached JavaScript.
-    $this->renderer->render($element);
+    $this->renderer->renderRoot($element);
     $expected_libraries = ['test/parent', 'test/child', 'test/subchild'];
     $this->assertEquals($element['#attached']['library'], $expected_libraries, 'The element, child and subchild #attached libraries are included.');
 
     // Load the element from cache and verify the presence of the #attached
     // JavaScript.
     $element = ['#cache' => ['keys' => ['simpletest', 'drupal_render', 'children_attached']]];
-    $this->assertTrue(strlen($this->renderer->render($element)) > 0, 'The element was retrieved from cache.');
+    $this->assertTrue(strlen($this->renderer->renderRoot($element)) > 0, 'The element was retrieved from cache.');
     $this->assertEquals($element['#attached']['library'], $expected_libraries, 'The element, child and subchild #attached libraries are included.');
   }
 
@@ -116,7 +116,7 @@ public function testContextBubblingCustomCacheBin() {
         ],
       ],
     ];
-    $this->renderer->render($build);
+    $this->renderer->renderRoot($build);
 
     $this->assertRenderCacheItem('parent:foo', [
       '#cache_redirect' => TRUE,
@@ -143,7 +143,7 @@ public function testContextBubblingEdgeCases(array $element, array $expected_top
       ->method('convertTokensToKeys')
       ->willReturnArgument(0);
 
-    $this->renderer->render($element);
+    $this->renderer->renderRoot($element);
 
     $this->assertEquals($expected_top_level_contexts, $element['#cache']['contexts'], 'Expected cache contexts found.');
     foreach ($expected_cache_items as $cid => $expected_cache_item) {
@@ -345,7 +345,7 @@ public function testConditionalCacheContextBubblingSelfHealing() {
     // contexts: user.roles.
     $element = $test_element;
     $current_user_role = 'A';
-    $this->renderer->render($element);
+    $this->renderer->renderRoot($element);
     $this->assertRenderCacheItem('parent', [
       '#cache_redirect' => TRUE,
       '#cache' => [
@@ -369,7 +369,7 @@ public function testConditionalCacheContextBubblingSelfHealing() {
     // contexts: foo, user.roles.
     $element = $test_element;
     $current_user_role = 'B';
-    $this->renderer->render($element);
+    $this->renderer->renderRoot($element);
     $this->assertRenderCacheItem('parent', [
       '#cache_redirect' => TRUE,
       '#cache' => [
@@ -401,7 +401,7 @@ public function testConditionalCacheContextBubblingSelfHealing() {
     // and 'user.roles' cache contexts, resulting in a cache miss every time.)
     $element = $test_element;
     $current_user_role = 'A';
-    $this->renderer->render($element);
+    $this->renderer->renderRoot($element);
     $this->assertRenderCacheItem('parent', [
       '#cache_redirect' => TRUE,
       '#cache' => [
@@ -425,7 +425,7 @@ public function testConditionalCacheContextBubblingSelfHealing() {
     // accessible => bubbled cache contexts: foo, bar, user.roles.
     $element = $test_element;
     $current_user_role = 'C';
-    $this->renderer->render($element);
+    $this->renderer->renderRoot($element);
     $final_parent_cache_item = [
       '#cache_redirect' => TRUE,
       '#cache' => [
@@ -449,7 +449,7 @@ public function testConditionalCacheContextBubblingSelfHealing() {
     // Request 5: role A again, verifying the merging like we did for request 3.
     $element = $test_element;
     $current_user_role = 'A';
-    $this->renderer->render($element);
+    $this->renderer->renderRoot($element);
     $this->assertRenderCacheItem('parent', $final_parent_cache_item);
     $this->assertRenderCacheItem('parent:bar:foo:r.A', [
       '#attached' => [],
@@ -464,7 +464,7 @@ public function testConditionalCacheContextBubblingSelfHealing() {
     // Request 6: role B again, verifying the merging like we did for request 3.
     $element = $test_element;
     $current_user_role = 'B';
-    $this->renderer->render($element);
+    $this->renderer->renderRoot($element);
     $this->assertRenderCacheItem('parent', $final_parent_cache_item);
     $this->assertRenderCacheItem('parent:bar:foo:r.B', [
       '#attached' => [],
@@ -570,7 +570,7 @@ public function testOverWriteCacheKeys() {
        ],
       '#pre_render' => [__NAMESPACE__ . '\\BubblingTest::bubblingCacheOverwritePrerender'],
     ];
-    $this->renderer->render($data);
+    $this->renderer->renderRoot($data);
   }
 }
 
diff --git a/core/tests/Drupal/Tests/Core/Render/RendererTest.php b/core/tests/Drupal/Tests/Core/Render/RendererTest.php
index a2f91ea..3e3e3bd 100644
--- a/core/tests/Drupal/Tests/Core/Render/RendererTest.php
+++ b/core/tests/Drupal/Tests/Core/Render/RendererTest.php
@@ -43,7 +43,7 @@ public function testRenderBasic($build, $expected, callable $setup_code = NULL)
       $setup_code();
     }
 
-    $this->assertSame($expected, $this->renderer->render($build));
+    $this->assertSame($expected, $this->renderer->renderRoot($build));
   }
 
   /**
@@ -322,7 +322,7 @@ public function testRenderSorting() {
         '#markup' => $first,
       ],
     ];
-    $output = $this->renderer->render($elements);
+    $output = $this->renderer->renderRoot($elements);
 
     // The lowest weight element should appear last in $output.
     $this->assertTrue(strpos($output, $second) > strpos($output, $first), 'Elements were sorted correctly by weight.');
@@ -357,7 +357,7 @@ public function testRenderSortingWithSetHashSorted() {
       ),
       '#sorted' => TRUE,
     );
-    $output = $this->renderer->render($elements);
+    $output = $this->renderer->renderRoot($elements);
 
     // The elements should appear in output in the same order as the array.
     $this->assertTrue(strpos($output, $second) < strpos($output, $first), 'Elements were not sorted.');
@@ -439,11 +439,11 @@ public function testRenderTwice() {
       '#markup' => 'test',
     ];
 
-    $this->assertEquals('test', $this->renderer->render($build));
+    $this->assertEquals('test', $this->renderer->renderRoot($build));
     $this->assertTrue($build['#printed']);
 
     // We don't want to reprint already printed render arrays.
-    $this->assertEquals('', $this->renderer->render($build));
+    $this->assertEquals('', $this->renderer->renderRoot($build));
   }
 
   /**
@@ -470,10 +470,10 @@ protected function assertAccess($build, $access) {
     $sensitive_content = $this->randomContextValue();
     $build['#markup'] = $sensitive_content;
     if ($access) {
-      $this->assertSame($sensitive_content, $this->renderer->render($build));
+      $this->assertSame($sensitive_content, $this->renderer->renderRoot($build));
     }
     else {
-      $this->assertSame('', $this->renderer->render($build));
+      $this->assertSame('', $this->renderer->renderRoot($build));
     }
   }
 
@@ -565,13 +565,13 @@ public function testRenderCache() {
     // Render the element and confirm that it goes through the rendering
     // process (which will set $element['#printed']).
     $element = $test_element;
-    $this->renderer->render($element);
+    $this->renderer->renderRoot($element);
     $this->assertTrue(isset($element['#printed']), 'No cache hit');
 
     // Render the element again and confirm that it is retrieved from the cache
     // instead (so $element['#printed'] will not be set).
     $element = $test_element;
-    $this->renderer->render($element);
+    $this->renderer->renderRoot($element);
     $this->assertFalse(isset($element['#printed']), 'Cache hit');
 
     // Test that cache tags are correctly collected from the render element,
@@ -608,7 +608,7 @@ public function testRenderCacheMaxAge($max_age, $is_render_cached, $render_cache
       ],
       '#markup' => '',
     ];
-    $this->renderer->render($element);
+    $this->renderer->renderRoot($element);
 
     $cache_item = $this->cacheFactory->get('render')->get('render_cache_test:en:stark');
     if (!$is_render_cached) {
@@ -657,7 +657,7 @@ public function testRenderCacheProperties(array $expected_results) {
       'child2' => ['#markup' => 2],
       '#custom_property' => ['custom_value'],
     ];
-    $this->renderer->render($element);
+    $this->renderer->renderRoot($element);
 
     $cache = $this->cacheFactory->get('render');
     $data = $cache->get('render_cache_test:en:stark')->data;
