diff --git a/plugins/display_renderers/panels_renderer_editor.class.php b/plugins/display_renderers/panels_renderer_editor.class.php
index f6f0b16..590ca2f 100644
--- a/plugins/display_renderers/panels_renderer_editor.class.php
+++ b/plugins/display_renderers/panels_renderer_editor.class.php
@@ -73,13 +73,49 @@ class panels_renderer_editor extends panels_renderer_standard {
 
     $this->add_meta();
 
-    $output = '<div class="panels-dnd" id="panels-dnd-main">';
+    $output = NULL;
+
+    // Warn user if the display contains region caching but has panes with the
+    // "render first" / "render last" flag.
+    $output .= $this->get_region_caching_warning();
+
+    $output .= '<div class="panels-dnd" id="panels-dnd-main">';
     $output .= $this->render_layout();
     $output .= '</div>';
 
     return $output;
   }
 
+  /**
+   * Check if the user has to be warned about the region rendering.
+   *
+   * Checks if region caching is used along content types with the flags
+   * "render first" / "render last". If
+   *
+   * @return string
+   *   TRUE if the warning should be displayed.
+   */
+  function get_region_caching_warning() {
+    if ($this->has_region_caching()) {
+      foreach ($this->display->content as $pid => $pane) {
+        // If the pane's subtype is unique, get it so that
+        // hook_ctools_content_subtype_alter() and/or
+        // hook_ctools_block_info() will be called.
+        if ($pane->type != $pane->subtype) {
+          $content_type = ctools_content_get_subtype($pane->type, $pane->subtype);
+        }
+        else {
+          $content_type = ctools_get_content_type($pane->type);
+        }
+        if (!empty($content_type['render first']) || !empty($content_type['render last'])) {
+          $msg = t('This display uses region caching but has content types with the flag "render first" / "render last" this might lead to unexpected behaviour since the flags are only respected per region and not globally.');
+          return '<div id="region-caching-warning" class="warning">' . $msg . '</div>';
+        }
+      }
+    }
+    return '<div id="region-caching-warning"></div>';
+  }
+
   function render_region($region_id, $panes) {
     // Pass through to normal rendering if not in admin mode.
     if (!$this->admin) {
@@ -233,26 +269,7 @@ class panels_renderer_editor extends panels_renderer_standard {
         'html' => TRUE,
       );
 
-      $method = isset($this->display->cache['method']) ? $this->display->cache['method'] : 0;
-      $info = panels_get_cache($method);
-      $cache_method = isset($info['title']) ? $info['title'] : t('No caching');
-
-      $cache_links[] = array(
-        'title' => $cache_method,
-        'attributes' => array('class' => array('panels-text')),
-      );
-      $cache_links[] = array(
-        'title' => t('Change'),
-        'href' => $this->get_url('cache-method', 'display'),
-        'attributes' => array('class' => array('ctools-use-modal')),
-      );
-      if (panels_plugin_get_function('cache', $info, 'settings form')) {
-        $cache_links[] = array(
-          'title' => t('Settings'),
-          'href' => $this->get_url('cache-settings', 'display'),
-          'attributes' => array('class' => array('ctools-use-modal')),
-        );
-      }
+      $cache_links = $this->get_cache_links('display');
 
       $links[] = array(
         'title' => '<span class="dropdown-header">' . t('Caching') . '</span>' . theme_links(array('links' => $cache_links, 'attributes' => array(), 'heading' => array())),
@@ -295,9 +312,86 @@ class panels_renderer_editor extends panels_renderer_standard {
       );
     }
 
+    if (user_access('use panels caching features')) {
+      $links[] = array(
+        'title' => '<hr />',
+        'html' => TRUE,
+      );
+
+      $cache_links = $this->get_cache_links('region', $region_id);
+
+      $links[] = array(
+        'title' => '<span class="dropdown-header">' . t('Caching') . '</span>' . theme_links(array('links' => $cache_links, 'attributes' => array(), 'heading' => array())),
+        'html' => TRUE,
+        'attributes' => array('class' => array('panels-sub-menu')),
+      );
+    }
+
     return theme('ctools_dropdown', array('title' => theme('image', array('path' => ctools_image_path('icon-addcontent.png', 'panels'))), 'links' => $links, 'image' => TRUE, 'class' => 'pane-add-link panels-region-links-' . $region_id));
   }
 
+  function get_cache_links($type, $id = NULL) {
+    $conf = $this->get_cache($type, $id);
+    $method = isset($conf['method']) ? $conf['method'] : 0;
+    $info = panels_get_cache($method);
+    $cache_method = isset($info['title']) ? $info['title'] : t('No caching');
+
+    $cache_links = array();
+    $cache_links['title'] = array(
+      'title' => $cache_method,
+      'attributes' => array('class' => array('panels-text')),
+    );
+    $cache_links['change'] = array(
+      'title' => t('Change'),
+      'href' => $this->get_url('cache-method', $type, $id),
+      'attributes' => array('class' => array('ctools-use-modal')),
+    );
+
+    if (ctools_plugin_get_function($info, 'settings form')) {
+      $cache_links['settings'] = array(
+        'title' => t('Settings'),
+        'href' => $this->get_url('cache-settings', $type, $id),
+        'attributes' => array('class' => array('ctools-use-modal')),
+      );
+    }
+    return $cache_links;
+  }
+
+  /**
+   * Get the appropriate cache from the panel in the cache.
+   *
+   * Since we have cache for regions, panes and the display itself, and
+   * they are stored differently, we use this method to simplify getting
+   * cache information into a way that's easy to cope with.
+   */
+  function &get_cache($type, $pid = '') {
+    // This lets us choose whether we're doing the display's cache or
+    // a pane's.
+    switch ($type) {
+      case 'display':
+        if (!isset($this->display->cache)) {
+          $this->display->cache = array();
+        }
+        $conf = &$this->display->cache;
+        break;
+
+      case 'region':
+        if (!isset($this->display->panel_settings['cache_settings'][$pid])) {
+          $this->display->panel_settings['cache_settings'][$pid] = array();
+        }
+        $conf = &$this->display->panel_settings['cache_settings'][$pid];
+        break;
+
+      case 'pane':
+        if (!isset($this->display->content[$pid]->cache)) {
+          $this->display->content[$pid]->cache = array();
+        }
+        $conf = &$this->display->content[$pid]->cache;
+        break;
+    }
+    return $conf;
+  }
+
   /**
    * Render the links to display when editing a pane.
    */
@@ -431,27 +525,7 @@ class panels_renderer_editor extends panels_renderer_standard {
     }
 
     if (panels_get_caches() && user_access('use panels caching features')) {
-      $method = isset($pane->cache['method']) ? $pane->cache['method'] : 0;
-      $info = panels_get_cache($method);
-      $cache_method = isset($info['title']) ? $info['title'] : t('No caching');
-      $cache_links['title'] = array(
-        'title' => $cache_method,
-        'attributes' => array('class' => array('panels-text')),
-      );
-      $cache_links['change'] = array(
-        'title' => t('Change'),
-        'href' => $this->get_url('cache-method', $pane->pid),
-        'attributes' => array('class' => array('ctools-use-modal')),
-      );
-      if (panels_plugin_get_function('cache', $info, 'settings form')) {
-        $cache_links['settings'] = array(
-          'title' => t('Settings'),
-          'href' => $this->get_url('cache-settings', $pane->pid),
-          'attributes' => array('class' => array('ctools-use-modal')),
-        );
-      }
-
-      $links['cache'] = $cache_links;
+      $links['cache'] = $this->get_cache_links('pane', $pane->pid);
     }
 
     $links['bottom']['remove'] = array(
@@ -706,6 +780,9 @@ class panels_renderer_editor extends panels_renderer_standard {
       // Tell the client to draw the pane
       $this->command_add_pane($pane);
 
+      // Update warning about region caching.
+      $this->command_update_region_caching_warning();
+
       // Dismiss the modal.
       $this->commands[] = ctools_modal_command_dismiss();
     }
@@ -810,26 +887,39 @@ class panels_renderer_editor extends panels_renderer_standard {
    *   Either a pane id for a pane in the display, or 'display' to edit the
    *   display cache settings.
    */
-  function ajax_cache_method($pid = NULL) {
+  function ajax_cache_method($type, $pid = NULL) {
     ctools_include('content');
-    // This lets us choose whether we're doing the display's cache or
-    // a pane's.
-    if ($pid == 'display') {
-      $conf = &$this->display->cache;
-      $title = t('Cache method for this display');
-    }
-    else if (!empty($this->display->content[$pid])) {
-      $pane = &$this->display->content[$pid];
-      $subtype = ctools_content_get_subtype($pane->type, $pane->subtype);
-      $conf = &$pane->cache;
-      $title = t('Cache method for !subtype_title', array('!subtype_title' => $subtype['title']));
+
+    $conf = &$this->get_cache($type, $pid);
+    if (isset($this->cache->method) && (empty($conf['method']) || $conf['method'] != $this->cache->method)) {
+      $conf['method'] = $this->cache->method;
+      $info = panels_get_cache($conf['method']);
+      $conf['settings'] = isset($info['defaults']) ? $info['defaults'] : array();
     }
-    else {
-      ctools_modal_render(t('Error'), t('Invalid pane id.'));
+
+    switch ($type) {
+      case 'display':
+        $title = t('Cache method for this display');
+        break;
+
+      case 'region':
+        $title = t('Cache method for region !subtype_title', array('!subtype_title' => $pid));
+        break;
+
+      case 'pane':
+        $pane = &$this->display->content[$pid];
+        $subtype = ctools_content_get_subtype($pane->type, $pane->subtype);
+        $title = t('Cache method for !subtype_title', array('!subtype_title' => $subtype['title']));
+        break;
+
+      default:
+        $title = NULL;
+        ctools_modal_render(t('Error'), t('Invalid pane id.'));
     }
 
     $form_state = array(
       'display' => &$this->display,
+      'type' => $type,
       'conf' => &$conf,
       'title' => $title,
       'ajax' => TRUE,
@@ -846,24 +936,39 @@ class panels_renderer_editor extends panels_renderer_standard {
     $info = panels_get_cache($form_state['method']);
     $function = panels_plugin_get_function('cache', $info, 'settings form');
     if (!$function) {
-      $conf['method'] = $form_state['method'];
-      $conf['settings'] = array();
+      if (!empty($form_state['method'])) {
+        $conf['method'] = $form_state['method'];
+        $conf['settings'] = array();
+      }
+      else {
+        $conf = array();
+      }
       panels_edit_cache_set($this->cache);
 
       $this->commands[] = ctools_modal_command_dismiss();
+      $this->command_update_region_caching_warning();
 
-      if ($pid != 'display') {
-        $this->command_update_pane($pane);
-      }
-      else {
-        $this->command_update_display_links();
+      switch ($type) {
+        case 'display':
+          $this->command_update_display_links();
+          break;
+
+        case 'region':
+          $this->command_update_region_links($pid);
+          break;
+
+        case 'pane':
+          $this->command_update_pane($pane);
+          break;
+
+        default:
       }
     }
     else {
       $this->cache->method = $form_state['method'];
       panels_edit_cache_set($this->cache);
       // send them to next form.
-      return $this->ajax_cache_settings($pid);
+      return $this->ajax_cache_settings($type, $pid);
     }
   }
 
@@ -874,24 +979,34 @@ class panels_renderer_editor extends panels_renderer_standard {
    *   Either a pane id for a pane in the display, or 'display' to edit the
    *   display cache settings.
    */
-  function ajax_cache_settings($pid = 0) {
+  function ajax_cache_settings($type, $pid = 0) {
     ctools_include('content');
 
-    // This lets us choose whether we're doing the display's cache or
-    // a pane's.
-    if ($pid == 'display') {
-      $conf = &$this->display->cache;
-      $title = t('Cache settings for this display');
+    $conf = &$this->get_cache($type, $pid);
+    if (isset($this->cache->method) && (empty($conf['method']) || $conf['method'] != $this->cache->method)) {
+      $conf['method'] = $this->cache->method;
+      $info = panels_get_cache($conf['method']);
+      $conf['settings'] = isset($info['defaults']) ? $info['defaults'] : array();
     }
-    else if (!empty($this->display->content[$pid])) {
-      $pane = &$this->display->content[$pid];
-      $subtype = ctools_content_get_subtype($pane->type, $pane->subtype);
 
-      $conf = &$pane->cache;
-      $title = t('Cache settings for !subtype_title', array('!subtype_title' => $subtype['title']));
-    }
-    else {
-      ctools_modal_render(t('Error'), t('Invalid pane id.'));
+    switch ($type) {
+      case 'display':
+        $title = t('Cache settings for this display');
+        break;
+
+      case 'region':
+        $title = t('Cache method for region !subtype_title', array('!subtype_title' => $pid));
+        break;
+
+      case 'pane':
+        $pane = &$this->display->content[$pid];
+        $subtype = ctools_content_get_subtype($pane->type, $pane->subtype);
+        $title = t('Cache settings for !subtype_title', array('!subtype_title' => $subtype['title']));
+        break;
+
+      default:
+        $title = NULL;
+        ctools_modal_render(t('Error'), t('Invalid pane id.'));
     }
 
     if (isset($this->cache->method) && (empty($conf['method']) || $conf['method'] != $this->cache->method)) {
@@ -902,11 +1017,12 @@ class panels_renderer_editor extends panels_renderer_standard {
 
     $form_state = array(
       'display' => &$this->display,
+      'type' => $type,
       'pid' => $pid,
       'conf' => &$conf,
       'ajax' => TRUE,
       'title' => $title,
-      'url' => url($this->get_url('cache-settings', $pid), array('absolute' => TRUE)),
+      'url' => url($this->get_url('cache-settings', $type, $pid), array('absolute' => TRUE)),
     );
 
     $output = ctools_modal_form_wrapper('panels_edit_cache_settings_form', $form_state);
@@ -914,16 +1030,26 @@ class panels_renderer_editor extends panels_renderer_standard {
       $this->commands = $output;
       return;
     }
+    if (empty($conf['method'])) {
+      $conf = array();
+    }
 
     panels_edit_cache_set($this->cache);
 
     $this->commands[] = ctools_modal_command_dismiss();
 
-    if ($pid != 'display') {
-      $this->command_update_pane($pane);
-    }
-    else {
-      $this->command_update_display_links();
+    switch ($type) {
+      case 'display':
+        $this->command_update_display_links();
+        break;
+
+      case 'region':
+        $this->command_update_region_links($pid);
+        break;
+
+      case 'pane':
+        $this->command_update_pane($pane);
+        break;
     }
   }
 
@@ -1493,6 +1619,13 @@ class panels_renderer_editor extends panels_renderer_standard {
   function command_update_region_links($id) {
     $this->commands[] = ajax_command_replace('.panels-region-links-' . $id, $this->get_region_links($id));
   }
+
+  /**
+   * Create a command to update the region cache content type order warning.
+   */
+  function command_update_region_caching_warning() {
+    $this->commands[] = ajax_command_replace('#region-caching-warning', $this->get_region_caching_warning());
+  }
 }
 
 /**
diff --git a/plugins/display_renderers/panels_renderer_standard.class.php b/plugins/display_renderers/panels_renderer_standard.class.php
index 5f134a9..b6b5713 100644
--- a/plugins/display_renderers/panels_renderer_standard.class.php
+++ b/plugins/display_renderers/panels_renderer_standard.class.php
@@ -90,6 +90,20 @@ class panels_renderer_standard {
   var $rendered = array();
 
   /**
+   * A multilevel array of rendered data. The first level of the array
+   * indicates the type of rendered data, typically with up to three keys:
+   * 'layout', 'regions', and 'panes'. The relevant rendered data is stored as
+   * the value for each of these keys as it is generated:
+   *  - 'panes' are an associative array of rendered output, keyed on pane id.
+   *  - 'regions' are an associative array of rendered output, keyed on region
+   *    name.
+   *  - 'layout' is the whole of the rendered output.
+   *
+   * @var array
+   */
+  var $rendered_empty = array();
+
+  /**
    * A multilevel array of data prepared for rendering. The first level of the
    * array indicates the type of prepared data. The standard renderer populates
    * and uses two top-level keys, 'panes' and 'regions':
@@ -314,6 +328,7 @@ class panels_renderer_standard {
     $default = array(
       'style' => panels_get_style(!empty($settings['style']) ? $settings['style'] : 'default'),
       'style settings' => isset($settings['style_settings']['default']) ? $settings['style_settings']['default'] : array(),
+      'cache' => array(),
     );
 
     $regions = array();
@@ -339,6 +354,7 @@ class panels_renderer_standard {
           $regions[$region_id]['style'] = panels_get_style($settings[$region_id]['style']);
           $regions[$region_id]['style settings'] = isset($settings['style_settings'][$region_id]) ? $settings['style_settings'][$region_id] : array();
         }
+        $regions[$region_id]['cache'] = isset($settings['cache_settings'][$region_id]) ? $settings['cache_settings'][$region_id] : array();
         $regions[$region_id]['pids'] = $panes;
       }
     }
@@ -380,6 +396,16 @@ class panels_renderer_standard {
   }
 
   /**
+   * Checks if this display has regions with caching enabled.
+   *
+   * @return bool
+   *   TRUE if the display has a region with enabled cahing.
+   */
+  function has_region_caching() {
+    return !empty($this->display->panel_settings['cache_settings']) && count(array_filter($this->display->panel_settings['cache_settings']));
+  }
+
+  /**
    * Perform display/layout-level render operations.
    *
    * This method triggers all the inner pane/region rendering processes, passes
@@ -395,7 +421,12 @@ class panels_renderer_standard {
     if (empty($this->prep_run)) {
       $this->prepare();
     }
-    $this->render_panes();
+    // If no region caching is configured render all panes at once. This ensures
+    // that the pane flags "render first" / "render last" are respected
+    // globally.
+    if (!$this->has_region_caching()) {
+      $this->render_panes();
+    }
     $this->render_regions();
 
     if ($this->admin && !empty($this->plugins['layout']['admin theme'])) {
@@ -495,20 +526,42 @@ class panels_renderer_standard {
    * Render all prepared panes, first by dispatching to their plugin's render
    * callback, then handing that output off to the pane's style plugin.
    *
+   *
+   * @param array $panes_to_render
+   *   List if pane ids (pid) to render.
+   *
    * @return array
    *   The array of rendered panes, keyed on pane pid.
    */
-  function render_panes() {
+  function render_panes($panes_to_render = array()) {
     drupal_alter('panels_prerender_panes', $this);
     ctools_include('content');
 
     // First, render all the panes into little boxes.
-    $this->rendered['panes'] = array();
-    foreach ($this->prepared['panes'] as $pid => $pane) {
+    if (!isset($this->rendered['panes'])) {
+      $this->rendered['panes'] = array();
+    }
+    if (!isset($this->rendered_empty['panes'])) {
+      $this->rendered_empty['panes'] = array();
+    }
+    $panes = $this->prepared['panes'];
+    // If the panes to render where defined slice them from the source.
+    if (!empty($panes_to_render)) {
+      $panes = array_intersect_key($panes, array_flip($panes_to_render));
+    }
+    // Now remove already rendered panes from the render pipe.
+    $panes = array_diff_key($panes, $this->rendered['panes'], $this->rendered_empty['panes']);
+
+    foreach ($panes as $pid => $pane) {
       $content = $this->render_pane($pane);
       if ($content) {
         $this->rendered['panes'][$pid] = $content;
       }
+      else {
+        // Also track panes that where empty. That way we can skip them straight
+        // away in a possible second run.
+        $this->rendered_empty['panes'][$pid] = TRUE;
+      }
     }
     return $this->rendered['panes'];
   }
@@ -635,16 +688,45 @@ class panels_renderer_standard {
     // Loop through all panel regions, put all panes that belong to the current
     // region in an array, then render the region. Primarily this ensures that
     // the panes are arranged in the proper order.
-    $content = array();
     foreach ($this->prepared['regions'] as $region_id => $conf) {
-      $region_panes = array();
-      foreach ($conf['pids'] as $pid) {
-        // Only include panes for region rendering if they had some output.
-        if (!empty($this->rendered['panes'][$pid])) {
-          $region_panes[$pid] = $this->rendered['panes'][$pid];
+      $caching = !empty($conf['cache']['method']) && empty($this->display->skip_cache) && empty($this->admin);
+      $cache = $content = NULL;
+      $cache_hit = FALSE;
+      // If caching is enabled try to fetch content from cache.
+      if ($caching) {
+        // Fake pane object with a generic pid. This way we can re-use the
+        // existing cache handling.
+        $region_cache_pane = (object) $conf;
+        $region_cache_pane->pid = 'reg:' . $region_id;
+        if ($cache = panels_get_cached_content($this->display, $this->display->args, $this->display->context, $region_cache_pane)) {
+          $content = $cache->content;
+          $cache_hit = TRUE;
         }
       }
-      $this->rendered['regions'][$region_id] = $this->render_region($region_id, $region_panes);
+      // If we didn't have a cache hit render the content now.
+      if (!$cache_hit) {
+        if ($caching) {
+          // This is created before rendering so that calls to drupal_add_js
+          // and drupal_add_css will be captured.
+          $cache = new panels_cache_object();
+        }
+        $region_panes = array();
+        // Render the panes of this region.
+        $this->render_panes($conf['pids']);
+        foreach ($conf['pids'] as $pid) {
+          // Only include panes for region rendering if they had some output.
+          if (!empty($this->rendered['panes'][$pid])) {
+            $region_panes[$pid] = $this->rendered['panes'][$pid];
+          }
+        }
+        $content = $this->render_region($region_id, $region_panes);
+      }
+      // If a cache object waits to be filled - write it.
+      if ($caching && isset($cache)) {
+        $cache->set_content($content);
+        panels_set_cached_content($cache, $this->display, $this->display->args, $this->display->context, $region_cache_pane);
+      }
+      $this->rendered['regions'][$region_id] = $content;
     }
 
     return $this->rendered['regions'];
