diff --git a/CMakeLists.txt b/CMakeLists.txt
index d45cfefcb78c03565f40b365d247888a5fb8dcf8..0699c49a053f4b39d350c0928e8af11482eb4e7b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -253,6 +253,8 @@ option(WITH_OPENSUBDIV    "Enable OpenSubdiv for surface subdivision" _init_OPEN
 
 option(WITH_OPENVDB       "Enable features relying on OpenVDB" OFF)
 option(WITH_OPENVDB_BLOSC "Enable blosc compression for OpenVDB, only enable if OpenVDB was built with blosc support" OFF)
+option(WITH_OPENVDB_3_ABI_COMPATIBLE "Assume OpenVDB library has been compiled with version 3 ABI compatibility" OFF)
+mark_as_advanced(WITH_OPENVDB_3_ABI_COMPATIBLE)
 
 # GHOST Windowing Library Options
 option(WITH_GHOST_DEBUG   "Enable debugging output for the GHOST library" OFF)
@@ -1241,7 +1243,7 @@ if(WITH_LIBMV OR WITH_GTESTS OR (WITH_CYCLES AND WITH_CYCLES_LOGGING))
 		if(WIN32)
 			set(GLOG_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/extern/glog/src/windows)
 		else()
-			set(GLOG_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/extern/glog/src)
+			set(GLOG_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/extern/glog/include)
 		endif()
 	endif()
 endif()
@@ -1378,6 +1380,7 @@ if(CMAKE_COMPILER_IS_GNUCC)
 	# flags to undo strict flags
 	ADD_CHECK_C_COMPILER_FLAG(CC_REMOVE_STRICT_FLAGS C_WARN_NO_DEPRECATED_DECLARATIONS -Wno-deprecated-declarations)
 	ADD_CHECK_C_COMPILER_FLAG(CC_REMOVE_STRICT_FLAGS C_WARN_NO_UNUSED_PARAMETER        -Wno-unused-parameter)
+	ADD_CHECK_C_COMPILER_FLAG(CC_REMOVE_STRICT_FLAGS C_WARN_NO_UNUSED_FUNCTION         -Wno-unused-function)
 
 	if(CMAKE_COMPILER_IS_GNUCC AND (NOT "${CMAKE_C_COMPILER_VERSION}" VERSION_LESS "7.0"))
 		ADD_CHECK_C_COMPILER_FLAG(CC_REMOVE_STRICT_FLAGS C_WARN_NO_IMPLICIT_FALLTHROUGH    -Wno-implicit-fallthrough)
@@ -1442,8 +1445,8 @@ elseif(CMAKE_C_COMPILER_ID MATCHES "Intel")
 	ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_NO_SIGN_COMPARE -Wno-sign-compare)
 
 	# disable numbered, false positives
-	set(C_WARNINGS "${C_WARNINGS} -wd188,186,144,913,556")
-	set(CXX_WARNINGS "${CXX_WARNINGS} -wd188,186,144,913,556")
+	set(C_WARNINGS "${C_WARNINGS} -wd188,186,144,913,556,858,597,177,1292,167,279,592,94,2722,3199")
+	set(CXX_WARNINGS "${CXX_WARNINGS} -wd188,186,144,913,556,858,597,177,1292,167,279,592,94,2722,3199")
 elseif(CMAKE_C_COMPILER_ID MATCHES "MSVC")
 	# most msvc warnings are C & C++
 	set(_WARNINGS
diff --git a/build_files/build_environment/CMakeLists.txt b/build_files/build_environment/CMakeLists.txt
index 885133f970c90578ba5ea33d0ba7a1d87195a073..f177560c5f63aa58b248df8b1c6a1ca8af84b669 100644
--- a/build_files/build_environment/CMakeLists.txt
+++ b/build_files/build_environment/CMakeLists.txt
@@ -58,7 +58,6 @@ include(cmake/openexr.cmake)
 include(cmake/freetype.cmake)
 include(cmake/freeglut.cmake)
 include(cmake/glew.cmake)
-include(cmake/hdf5.cmake)
 include(cmake/alembic.cmake)
 include(cmake/glfw.cmake)
 include(cmake/clew.cmake)
diff --git a/build_files/build_environment/cmake/harvest.cmake b/build_files/build_environment/cmake/harvest.cmake
index f23939a7ba6a9b01afca5cf8f278fa5e82d58859..9ebd5206d27b5280fdb0e1b8b30516b2bf971f15 100644
--- a/build_files/build_environment/cmake/harvest.cmake
+++ b/build_files/build_environment/cmake/harvest.cmake
@@ -102,8 +102,6 @@ if(BUILD_MODE STREQUAL Release)
 				${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/python/ ${HARVEST_TARGET}/python/ &&
 				# alembic
 				${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/alembic ${HARVEST_TARGET}/alembic &&
-				# hdf5
-				${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/hdf5 ${HARVEST_TARGET}/hdf5 &&
 				# BlendThumb
 				${CMAKE_COMMAND} -E copy ${LIBDIR}/BlendThumb64/bin/blendthumb.dll ${HARVEST_TARGET}/ThumbHandler/lib/BlendThumb64.dll &&
 				${CMAKE_COMMAND} -E copy ${LIBDIR}/BlendThumb32/bin/blendthumb.dll ${HARVEST_TARGET}/ThumbHandler/lib/BlendThumb.dll &&
diff --git a/build_files/build_environment/cmake/sndfile.cmake b/build_files/build_environment/cmake/sndfile.cmake
index 282033d49055238a11a25629a583cb8209857e54..2c4da159280c4434d56a8e2f17caa048de2243ed 100644
--- a/build_files/build_environment/cmake/sndfile.cmake
+++ b/build_files/build_environment/cmake/sndfile.cmake
@@ -27,7 +27,7 @@ else()
 	set(SNDFILE_OPTIONS --enable-static --disable-shared )
 endif()
 
-if(APPLE)
+if(UNIX)
 	set(SNDFILE_PATCH_CMD ${PATCH_CMD} --verbose -p 0 -d ${BUILD_DIR}/sndfile/src/external_sndfile < ${PATCH_DIR}/sndfile.diff)
 else()
 	set(SNDFILE_PATCH_CMD)
diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh
index c1ba2a3ab25f2b24368ff6a00682ffbfa8cde35b..c474c39d91050fdba8509f2af4b07fea82dca4a2 100755
--- a/build_files/build_environment/install_deps.sh
+++ b/build_files/build_environment/install_deps.sh
@@ -1650,7 +1650,6 @@ compile_OIIO() {
     cmake_d="$cmake_d -D LINKSTATIC=OFF"
     cmake_d="$cmake_d -D USE_SIMD=sse2"
 
-    cmake_d="$cmake_d -D ILMBASE_VERSION=$ILMBASE_VERSION"
     cmake_d="$cmake_d -D OPENEXR_VERSION=$OPENEXR_VERSION"
 
     if [ "$_with_built_openexr" = true ]; then
@@ -1669,6 +1668,7 @@ compile_OIIO() {
     cmake_d="$cmake_d -D BUILD_TESTING=OFF"
     cmake_d="$cmake_d -D OIIO_BUILD_TESTS=OFF"
     cmake_d="$cmake_d -D OIIO_BUILD_TOOLS=OFF"
+    cmake_d="$cmake_d -D TXT2MAN="
     #cmake_d="$cmake_d -D CMAKE_EXPORT_COMPILE_COMMANDS=ON"
     #cmake_d="$cmake_d -D CMAKE_VERBOSE_MAKEFILE=ON"
 
diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake
index 64ff4fc3affac77787935ac9999b6d30f0247fd6..dcb8103bc106d87c2d1c5aa356f35ffb30b4acf8 100644
--- a/build_files/cmake/macros.cmake
+++ b/build_files/cmake/macros.cmake
@@ -565,6 +565,8 @@ function(SETUP_BLENDER_SORTED_LIBS)
 	set(BLENDER_SORTED_LIBS
 		bf_windowmanager
 
+		bf_editor_undo
+
 		bf_editor_space_api
 		bf_editor_space_action
 		bf_editor_space_buttons
@@ -689,6 +691,7 @@ function(SETUP_BLENDER_SORTED_LIBS)
 		extern_sdlew
 
 		bf_intern_glew_mx
+		bf_intern_clog
 	)
 
 	if(NOT WITH_SYSTEM_GLOG)
@@ -819,7 +822,7 @@ macro(TEST_SSE_SUPPORT
 		endif()
 	elseif(CMAKE_C_COMPILER_ID MATCHES "Intel")
 		set(${_sse_flags} "")  # icc defaults to -msse
-		set(${_sse2_flags} "-msse2")
+		set(${_sse2_flags} "")  # icc defaults to -msse2
 	else()
 		message(WARNING "SSE flags for this compiler: '${CMAKE_C_COMPILER_ID}' not known")
 		set(${_sse_flags})
diff --git a/doc/doxygen/doxygen.intern.h b/doc/doxygen/doxygen.intern.h
index e3cc11b3404d335ffa7cb3f264daef9df3e94ac0..777f35ef50a70e72c8b0750faa0de8160c0b6e92 100644
--- a/doc/doxygen/doxygen.intern.h
+++ b/doc/doxygen/doxygen.intern.h
@@ -10,6 +10,10 @@
  *  \ingroup intern
  */
 
+/** \defgroup clog C-Logging (CLOG)
+ *  \ingroup intern
+ */
+
 /** \defgroup ctr container
  *  \ingroup intern
  */
diff --git a/doc/doxygen/doxygen.source.h b/doc/doxygen/doxygen.source.h
index ebc209405293401890ffd47de53f86ecbf2d7ce6..6f159bb74df82638152f9d6de72662bae3613026 100644
--- a/doc/doxygen/doxygen.source.h
+++ b/doc/doxygen/doxygen.source.h
@@ -320,6 +320,10 @@
  *  \ingroup editors
  */
 
+/** \defgroup edundo undo utilities
+ *  \ingroup editors
+ */
+
 /** \defgroup spuv UV editing
  *  \ingroup editors
  */
diff --git a/doc/guides/blender-guardedalloc.txt b/doc/guides/blender-guardedalloc.txt
index 3db647a4e7775291d53335e635e03cb3429c17c4..2e49f25bd9273f2f022d438cd88567145925caa1 100644
--- a/doc/guides/blender-guardedalloc.txt
+++ b/doc/guides/blender-guardedalloc.txt
@@ -51,7 +51,7 @@ void MEM_printmemlist(void);
 - if err_stream is set by MEM_set_error_stream() this function dumps a list of all
 currently allocated memory blocks with length and name to the stream
 
-int MEM_check_memory_integrity(void);
+bool MEM_consistency_check(void);
 
 - this function tests if the internal structures of the memory manager are intact
 - returns 0 on success and !=0 on error
diff --git a/doc/python_api/examples/bge.texture.py b/doc/python_api/examples/bge.texture.py
index ac1f5a214479b71a4b38ad1fccc7e5260f7d396e..8b24530b10abf6a49e0a032b0c9b1a98d80646c8 100644
--- a/doc/python_api/examples/bge.texture.py
+++ b/doc/python_api/examples/bge.texture.py
@@ -1,8 +1,11 @@
 """
 Basic Video Playback
 ++++++++++++++++++++
-Example of how to replace a texture in game with a video. It needs to run
-everyframe.
+Example of how to replace a texture in game with a video.
+It needs to run everyframe.
+To avoid any confusion with the location of the file,
+we will use ``GameLogic.expandPath()`` to build an absolute file name,
+assuming the video file is in the same directory as the blend-file.
 """
 import bge
 from bge import texture
@@ -26,8 +29,18 @@ if not hasattr(logic, 'video'):
     logic.video.source = texture.VideoFFmpeg(movie)
     logic.video.source.scale = True
 
+    # Note that we can change the ``Texture`` source at any time.
+    # Suppose we want to switch between two movies during the game:
+    logic.mySources[0] = texture.VideoFFmpeg('movie1.avi')
+    logic.mySources[1] = texture.VideoFFmpeg('movie2.avi')
+
+    #And then assign (and reassign) the source during the game
+    logic.video.source = logic.mySources[movieSel]
+
     # quick off the movie, but it wont play in the background
     logic.video.source.play()
 
-# you need to call this function every frame to ensure update of the texture.
+
+# Video playback is not a background process: it happens only when we refresh the texture.
+# So you need to call this function every frame to ensure update of the texture.
 logic.video.refresh(True)
diff --git a/doc/python_api/rst/bge.texture.rst b/doc/python_api/rst/bge.texture.rst
index 3028ee653f8765f2b89384bcde19b4afb8387fb9..cd99cde0bc896601441914d293002d10bdc7fc65 100644
--- a/doc/python_api/rst/bge.texture.rst
+++ b/doc/python_api/rst/bge.texture.rst
@@ -6,18 +6,15 @@ Video Texture (bge.texture)
 Introduction
 ************
 
-The bge.texture module allows you to manipulate textures during the game.
-
-Several sources for texture are possible: video files, image files, video capture, memory buffer,
-camera render or a mix of that.
-
-The video and image files can be loaded from the internet using an URL instead of a file name.
-
-In addition, you can apply filters on the images before sending them to the GPU, allowing video effect:
-blue screen, color band, gray, normal map.
-
-bge.texture uses FFmpeg to load images and videos.
-All the formats and codecs that FFmpeg supports are supported by this module, including but not limited to:
+The ``bge.texture`` module allows you to manipulate textures during the game.
+Several sources for texture are possible: video files, image files, video capture,
+memory buffer, camera render or a mix of that.
+The video and image files can be loaded from the Internet using a URL instead of a file name.
+In addition, you can apply filters on the images before sending them to the GPU,
+allowing video effect: blue screen, color band, gray, normal map.
+``bge.texture`` uses FFmpeg to load images and videos.
+All the formats and codecs that FFmpeg supports are supported by ``bge.texture``,
+including but not limited to:
 
 * AVI
 * Ogg
@@ -28,16 +25,45 @@ All the formats and codecs that FFmpeg supports are supported by this module, in
 * videoForWindows capture card (this includes many webcams)
 * JPG
 
-The principle is simple: first you identify a texture on an existing object using
-the :class:`~bge.texture.materialID` function, then you create a new texture with dynamic content
+
+How it works
+------------
+
+The principle is simple: first you identify a texture on an existing object using the
+:class:`~bge.texture.materialID` function, then you create a new texture with dynamic content
 and swap the two textures in the GPU.
 
-The GE is not aware of the substitution and continues to display the object as always,
+The game engine is not aware of the substitution and continues to display the object as always,
 except that you are now in control of the texture.
 
 When the texture object is deleted, the new texture is deleted and the old texture restored.
 
-.. module:: bge.texture
+
+Game Preparation
+----------------
+
+Before you can use the :mod:`bge.texture` module,
+you must have objects with textures applied appropriately.
+
+Imagine you want to have a television showing live broadcast programs in the game.
+You will create a television object and UV-apply a different texture at the place of the screen,
+for example ``tv.png``. What this texture looks like is not important;
+probably you want to make it dark gray to simulate power-off state.
+When the television must be turned on, you create a dynamic texture from a video capture card
+and use it instead of ``tv.png``: the TV screen will come to life.
+
+You have two ways to define textures that ``bge.texture`` can grab:
+
+- Simple UV texture.
+- Blender material with image texture channel.
+
+Because ``bge.texture`` works at texture level, it is compatible with all
+the Blender Game Engine's fancy texturing features: GLSL, multi-texture, custom shaders, etc.
+
+
+********
+Examples
+********
 
 .. include:: __/examples/bge.texture.py
    :start-line: 1
@@ -53,7 +79,6 @@ When the texture object is deleted, the new texture is deleted and the old textu
 .. literalinclude:: __/examples/bge.texture.1.py
    :lines: 8-
 
-
 .. include:: __/examples/bge.texture.2.py
    :start-line: 1
    :end-line: 6
@@ -62,13 +87,15 @@ When the texture object is deleted, the new texture is deleted and the old textu
    :lines: 8-
 
 
+.. module:: bge.texture
+
 *************
 Video classes
 *************
 
 .. class:: VideoFFmpeg(file, capture=-1, rate=25.0, width=0, height=0)
 
-   FFmpeg video source.
+   FFmpeg video source, used for video files, video captures, or video streams.
 
    :arg file: Path to the video to load; if capture >= 0 on Windows, this parameter will not be used.
    :type file: str
@@ -90,19 +117,20 @@ Video classes
 
    .. attribute:: range
 
-      Replay range.
+      The start and stop time of the video playback, expressed in seconds from beginning.
+      By default the entire video.
 
       :type: sequence of two floats
 
    .. attribute:: repeat
 
-      Repeat count, -1 for infinite repeat.
+      Number of times to replay the video, -1 for infinite repeat.
 
       :type: int
 
    .. attribute:: framerate
 
-      Frame rate.
+      Relative frame rate, <1.0 for slow, >1.0 for fast.
 
       :type: float
 
@@ -126,21 +154,26 @@ Video classes
 
    .. attribute:: scale
 
-      Fast scale of image (near neighbour).
+      Set to True to activate fast nearest neighbor scaling algorithm.
+      Texture width and height must be a power of 2.
+      If the video picture size is not a power of 2, rescaling is required.
+      By default ``bge.texture`` uses the precise but slow ``gluScaleImage()`` function.
+      Best is to rescale the video offline so that no scaling is necessary at runtime!
 
       :type: bool
 
    .. attribute:: flip
 
-      Flip image vertically.
+      If True the imaged will be flipped vertically.
+      FFmpeg always delivers the image upside down, so this attribute is set to True by default.
 
       :type: bool
 
    .. attribute:: filter
 
-      Pixel filter.
+      An additional filter that is applied on the video before sending it to the GPU.
 
-      :type: one of...
+      :type: one of:
 
          * :class:`FilterBGR24`
          * :class:`FilterBlueScreen`
@@ -207,7 +240,7 @@ Image classes
 
 .. class:: ImageFFmpeg(file)
 
-   FFmpeg image source.
+   FFmpeg image source, used for image files and web based images.
 
    :arg file: Path to the image to load.
    :type file: str
@@ -286,7 +319,8 @@ Image classes
 
 .. class:: ImageBuff(width, height, color=0, scale=False)
 
-   Image source from image buffer.
+   Image from application memory.
+   For computer generated images, drawing applications.
 
    :arg width: Width of the image.
    :type width: int
@@ -477,7 +511,7 @@ Image classes
 
 .. class:: ImageMix
 
-   Image mixer.
+   Image mixer used to mix multiple image sources together.
 
    .. attribute:: filter
 
@@ -592,7 +626,7 @@ Image classes
 
 .. class:: ImageRender(scene, camera)
 
-   Image source from render.
+   Image source from a render of a non active camera.
    The render is done on a custom framebuffer object if fbo is specified,
    otherwise on the default framebuffer.
 
@@ -723,7 +757,8 @@ Image classes
 
 .. class:: ImageViewport
 
-   Image source from viewport.
+   Image source from viewport rendered by the active camera.
+   To render from a non active camera see :class:`ImageRender`.
 
    .. attribute:: alpha
 
@@ -774,11 +809,10 @@ Image classes
 
       Refresh video - copy the viewport to an external buffer (optional) then invalidate its current content.
 
-      :arg buffer: An optional object that implements the buffer protocol.
-         If specified, the image is copied to the buffer, which must be big enough or an exception is thrown.
-         The transfer to the buffer is optimal if no processing of the image is needed.
-         This is the case if ``flip=False, alpha=True, scale=False, whole=True, depth=False, zbuff=False``
-         and no filter is set.
+      :arg buffer: An optional object that implements the buffer protocol. If specified,
+         the image is copied to the buffer, which must be big enough or an exception is thrown.
+         The transfer to the buffer is optimal if no processing of the image is needed. This is the case if
+         ``flip=False, alpha=True, scale=False, whole=True, depth=False, zbuff=False`` and no filter is set.
       :type buffer: any buffer type
       :arg format: An optional image format specifier for the image that will be copied to the buffer.
          Only valid values are "RGBA" or "BGRA"
@@ -838,18 +872,16 @@ Image classes
    :arg capture: Card number from which the input video must be captured.
    :type capture: int
 
-   The format argument must be written as ``<displayMode>/<pixelFormat>[/3D][:<cacheSize>]`` where ``<displayMode>``
-   describes the frame size and rate and <pixelFormat> the encoding of the pixels.
+   The format argument must be written as ``<displayMode>/<pixelFormat>[/3D][:<cacheSize>]``
+   where ``<displayMode>`` describes the frame size and rate and <pixelFormat> the encoding of the pixels.
    The optional ``/3D`` suffix is to be used if the video stream is stereo with a left and right eye feed.
    The optional ``:<cacheSize>`` suffix determines the number of the video frames kept in cache, by default 8.
-   Some DeckLink cards won't work below a certain cache size.
-   The default value 8 should be sufficient for all cards.
+   Some DeckLink cards won't work below a certain cache size. The default value 8 should be sufficient for all cards.
    You may try to reduce the cache size to reduce the memory footprint. For example the The 4K Extreme is known
    to work with 3 frames only, the Extreme 2 needs 4 frames and the Intensity Shuttle needs 6 frames, etc.
    Reducing the cache size may be useful when Decklink is used in conjunction with GPUDirect:
    all frames must be locked in memory in that case and that puts a lot of pressure on memory.
-   If you reduce the cache size too much,
-   you'll get no error but no video feed either.
+   If you reduce the cache size too much, you'll get no error but no video feed either.
 
    The valid ``<displayMode>`` values are copied from the ``BMDDisplayMode`` enum in the DeckLink API
    without the 'bmdMode' prefix. In case a mode that is not in this list is added in a later version
@@ -1006,15 +1038,20 @@ Texture classes
 
 .. class:: Texture(gameObj, materialID=0, textureID=0, textureObj=None)
 
-   Texture object.
+   Class that creates the ``Texture`` object that loads the dynamic texture on the GPU.
 
    :arg gameObj: Game object to be created a video texture on.
    :type gameObj: :class:`~bge.types.KX_GameObject`
-   :arg materialID: Material ID. (optional)
+   :arg materialID: Material ID default, 0 is the first material. (optional)
    :type materialID: int
-   :arg textureID: Texture ID. (optional)
+   :arg textureID: Texture index in case of multi-texture channel, 0 = first channel by default.
+      In case of UV texture, this parameter should always be 0. (optional)
    :type textureID: int
-   :arg textureObj: Texture object with shared bindId. (optional)
+   :arg textureObj: Reference to another ``Texture`` object with shared bindId
+      which he user might want to reuse the texture.
+      If this argument is used, you should not create any source on this texture
+      and there is no need to refresh it either: the other ``Texture`` object will
+      provide the texture for both materials/textures.(optional)
    :type textureObj: :class:`Texture`
 
    .. attribute:: bindId
@@ -1094,7 +1131,7 @@ Texture classes
 
    .. attribute:: source
 
-      This attribute must be set to one of the image source. If the image size does not fit exactly
+      This attribute must be set to one of the image sources. If the image size does not fit exactly
       the frame size, the extend attribute determines what to do.
 
       For best performance, the source image should match exactly the size of the output frame.
@@ -1368,7 +1405,7 @@ Functions
 
    Returns a :class:`~bgl.Buffer` corresponding to the current image stored in a texture source object.
 
-   :arg image: Image source object of type ...
+   :arg image: Image source object of type:
 
       * :class:`VideoFFmpeg`
       * :class:`ImageFFmpeg`
@@ -1387,7 +1424,7 @@ Functions
 
         - "BGR" will return 3 bytes per pixel with the
           Blue, Green and Red channels in that order.
-        - "RGB1" will return 4 bytes per pixel with the 
+        - "RGB1" will return 4 bytes per pixel with the
           Red, Green, Blue channels in that order and the alpha channel forced to 255.
 
       - A special mode "F" allows to return the image as an array of float.
@@ -1429,9 +1466,10 @@ Functions
 
 .. function:: setLogFile(filename)
 
-   Sets the name of a text file in which runtime error messages will be written, in addition to the printing
-   of the messages on the Python console. Only the runtime errors specific to the VideoTexture module
-   are written in that file, ordinary runtime time errors are not written.
+   Sets the name of a text file in which runtime error messages will be written,
+   in addition to the printing of the messages on the Python console.
+   Only the runtime errors specific to the VideoTexture module are written in that file,
+   ordinary runtime time errors are not written.
 
    :arg filename: Name of the error log file.
    :type filename: str
@@ -1517,4 +1555,3 @@ See Wikipedia's `Blend Modes <https://en.wikipedia.org/wiki/Blend_modes>`_ for r
 .. data:: IMB_BLEND_COPY_RGB
 
 .. data:: IMB_BLEND_COPY_ALPHA
-
diff --git a/doc/python_api/rst/info_overview.rst b/doc/python_api/rst/info_overview.rst
index 721374cd472397346fd4967917d3db6f257f0bf9..4b8df47990c525cc766ab2addb95ad0c4b38abba 100644
--- a/doc/python_api/rst/info_overview.rst
+++ b/doc/python_api/rst/info_overview.rst
@@ -194,10 +194,11 @@ User interface classes are given a context in which to draw, buttons window, fil
 then they are drawn when that area is displayed so they are never called by Python scripts directly.
 
 
+.. _info_overview_registration:
+
 Registration
 ============
 
-
 Module Registration
 -------------------
 
diff --git a/extern/bullet2/src/BulletDynamics/ConstraintSolver/btGeneric6DofSpring2Constraint.cpp b/extern/bullet2/src/BulletDynamics/ConstraintSolver/btGeneric6DofSpring2Constraint.cpp
index 49ff78c26215a9437dc610bc47f0354552e643eb..313719448645c1f9d5fee6fd5b95c7c15c4651ff 100644
--- a/extern/bullet2/src/BulletDynamics/ConstraintSolver/btGeneric6DofSpring2Constraint.cpp
+++ b/extern/bullet2/src/BulletDynamics/ConstraintSolver/btGeneric6DofSpring2Constraint.cpp
@@ -782,6 +782,12 @@ int btGeneric6DofSpring2Constraint::get_limit_motor_info2(
 		btScalar cfm = BT_ZERO;
 		btScalar mA = BT_ONE / m_rbA.getInvMass();
 		btScalar mB = BT_ONE / m_rbB.getInvMass();
+		if (rotational) {
+			btScalar rrA = (m_calculatedTransformA.getOrigin() - transA.getOrigin()).length2();
+			btScalar rrB = (m_calculatedTransformB.getOrigin() - transB.getOrigin()).length2();
+			if (m_rbA.getInvMass()) mA = mA * rrA + 1 / (m_rbA.getInvInertiaTensorWorld() * ax1).length();
+			if (m_rbB.getInvMass()) mB = mB * rrB + 1 / (m_rbB.getInvInertiaTensorWorld() * ax1).length();
+		}
 		btScalar m = mA > mB ? mB : mA;
 		btScalar angularfreq = sqrt(ks / m);
 
@@ -800,7 +806,18 @@ int btGeneric6DofSpring2Constraint::get_limit_motor_info2(
 		btScalar fd = -kd * (vel) * (rotational ? -1 : 1) * dt;
 		btScalar f = (fs+fd);
 
-		info->m_constraintError[srow] = (vel + f * (rotational ? -1 : 1)) ;
+	// after the spring force affecting the body(es) the new velocity will be
+	// vel + f / m * (rotational ? -1 : 1)
+	// so in theory this should be set here for m_constraintError
+	// (with m_constraintError we set a desired velocity for the affected body(es))
+	// however in practice any value is fine as long as it is greater then the "proper" velocity,
+	// because the m_lowerLimit and the m_upperLimit will determinate the strength of the final pulling force
+	// so it is much simpler (and more robust) just to simply use inf (with the proper sign)
+	// you may also wonder what if the current velocity (vel) so high that the pulling force will not change its direction (in this iteration)
+	// will we not request a velocity with the wrong direction ?
+	// and the answare is not, because in practice during the solving the current velocity is subtracted from the m_constraintError
+	// so the sign of the force that is really matters
+	info->m_constraintError[srow] = (rotational ? -1 : 1) * (f < 0 ? -SIMD_INFINITY : SIMD_INFINITY);
 
 		btScalar minf = f < fd ? f : fd;
 		btScalar maxf = f < fd ? fd : f;
diff --git a/extern/ceres/CMakeLists.txt b/extern/ceres/CMakeLists.txt
index a6e9cd9c356d6b17fd6f542a1786d72e387262af..82695133a15be59b94ea3280b42e0d5c86e9d424 100644
--- a/extern/ceres/CMakeLists.txt
+++ b/extern/ceres/CMakeLists.txt
@@ -36,6 +36,8 @@ set(INC
 
 set(INC_SYS
 	${EIGEN3_INCLUDE_DIRS}
+	${GFLAGS_INCLUDE_DIRS}
+	${GLOG_INCLUDE_DIRS}
 )
 
 set(SRC
@@ -303,16 +305,6 @@ else()
 	add_definitions(-DCERES_RESTRICT_SCHUR_SPECIALIZATION)
 endif()
 
-if(WIN32)
-	list(APPEND INC
-		../glog/src/windows
-	)
-else()
-	list(APPEND INC
-		../glog/src
-	)
-endif()
-
 add_definitions(${GFLAGS_DEFINES})
 add_definitions(${GLOG_DEFINES})
 add_definitions(${CERES_DEFINES})
diff --git a/extern/ceres/bundle.sh b/extern/ceres/bundle.sh
index a4f703ac33d5c8952e05f9daf409503eb74715df..1c9a2e729e589d59a9469bbc98bbf47c3345672c 100755
--- a/extern/ceres/bundle.sh
+++ b/extern/ceres/bundle.sh
@@ -129,6 +129,8 @@ set(INC
 
 set(INC_SYS
 	\${EIGEN3_INCLUDE_DIRS}
+	\${GFLAGS_INCLUDE_DIRS}
+	\${GLOG_INCLUDE_DIRS}
 )
 
 set(SRC
@@ -145,16 +147,6 @@ else()
 	add_definitions(-DCERES_RESTRICT_SCHUR_SPECIALIZATION)
 endif()
 
-if(WIN32)
-	list(APPEND INC
-		../glog/src/windows
-	)
-else()
-	list(APPEND INC
-		../glog/src
-	)
-endif()
-
 add_definitions(\${GFLAGS_DEFINES})
 add_definitions(\${GLOG_DEFINES})
 add_definitions(\${CERES_DEFINES})
diff --git a/extern/gflags/CMakeLists.txt b/extern/gflags/CMakeLists.txt
index 8977fcca457bff8a10bcab0717019a472255a54d..a473e6ffb40668404c49d8413b44399168e2c816 100644
--- a/extern/gflags/CMakeLists.txt
+++ b/extern/gflags/CMakeLists.txt
@@ -35,7 +35,7 @@ set(SRC
 	src/gflags_completions.cc
 	src/gflags_reporting.cc
 
-	src/config.h
+	src/gflags/config.h
 	src/gflags/gflags_completions.h
 	src/gflags/gflags_declare.h
 	src/gflags/gflags_gflags.h
diff --git a/extern/gflags/README.blender b/extern/gflags/README.blender
index b48d4d5d1104051003d9e0373fac91d127d06b8d..c57f5ce53ca0873e2dc4be47393ce5473f5863c8 100644
--- a/extern/gflags/README.blender
+++ b/extern/gflags/README.blender
@@ -1,25 +1,21 @@
 Project: Google Flags
 URL: https://github.com/gflags/gflags
 License: New BSD
-Upstream version: 2.2.0 (9db82895)
+Upstream version: 2.2.1 (46f73f88b18)
 Local modifications:
 
-- Flattened the tree and only included files needed for libmv.
+- Flattened the tree and only included files needed for Blender.
 
 - config.h was originally generated on linux machine with some
   further tweaks:
 
   * OS_WINDOWS need to be conditinally defined from inside #ifdef WIN32
-  * Same applies yo HAVE_SHLWAPI_H
+  * Same applies to HAVE_SHLWAPI_H
   * Disabeld HAVE_FNMATCH_H
-
-- Removed attribute(unused) from FlagSaver.
+  * Forced disabled GFLAGS_IS_A_DLL
 
 - Applied some modifications from fork https://github.com/Nazg-Gul/gflags.git
   (see https://github.com/gflags/gflags/pull/129)
 
-- Avoid attempt of acquiring mutex lock in FlagRegistry::GlobalRegistry when
-  doing static flags initialization. See d81dd2d in Blender repository.
-
-- Made `google::{anonymous}::FlagValue::ValueSize() const` inlined, so it does
-  not trigger strict compiler warning.
\ No newline at end of file
+- Ifdef-ed __attribute((unused)) in gflags.h.
+  This file is compile-time configurable in upstream, so can not avoid change here.
diff --git a/extern/gflags/src/gflags.cc b/extern/gflags/src/gflags.cc
index 6dcc5170bcc4409a2372a521726e908a4d00d7c2..f27079862f0613527c97504c07d8887fef67db5e 100644
--- a/extern/gflags/src/gflags.cc
+++ b/extern/gflags/src/gflags.cc
@@ -88,7 +88,7 @@
 // are, similarly, mostly hooks into the functionality described above.
 
 #include "config.h"
-#include "gflags.h"
+#include "gflags/gflags.h"
 
 #include <assert.h>
 #include <ctype.h>
@@ -96,6 +96,7 @@
 #if defined(HAVE_FNMATCH_H)
 #  include <fnmatch.h>
 #elif defined(HAVE_SHLWAPI_H)
+#  define NO_SHLWAPI_ISOS
 #  include <shlwapi.h>
 #endif
 #include <stdarg.h> // For va_list and related operations
@@ -170,12 +171,10 @@ enum DieWhenReporting { DIE, DO_NOT_DIE };
 
 // Report Error and exit if requested.
 static void ReportError(DieWhenReporting should_die, const char* format, ...) {
-  char error_message[255];
   va_list ap;
   va_start(ap, format);
-  vsnprintf(error_message, sizeof(error_message), format, ap);
+  vfprintf(stderr, format, ap);
   va_end(ap);
-  fprintf(stderr, "%s", error_message);
   fflush(stderr);   // should be unnecessary, but cygwin's rxvt buffers stderr
   if (should_die == DIE) gflags_exitfunc(1);
 }
@@ -191,34 +190,42 @@ static void ReportError(DieWhenReporting should_die, const char* format, ...) {
 class CommandLineFlag;
 class FlagValue {
  public:
-  FlagValue(void* valbuf, const char* type, bool transfer_ownership_of_value);
+  enum ValueType {
+    FV_BOOL = 0,
+    FV_INT32 = 1,
+    FV_UINT32 = 2,
+    FV_INT64 = 3,
+    FV_UINT64 = 4,
+    FV_DOUBLE = 5,
+    FV_STRING = 6,
+    FV_MAX_INDEX = 6,
+  };
+
+  template <typename FlagType>
+  FlagValue(FlagType* valbuf, bool transfer_ownership_of_value);
   ~FlagValue();
 
   bool ParseFrom(const char* spec);
   string ToString() const;
 
+  ValueType Type() const { return static_cast<ValueType>(type_); }
+
  private:
   friend class CommandLineFlag;  // for many things, including Validate()
   friend class GFLAGS_NAMESPACE::FlagSaverImpl;  // calls New()
   friend class FlagRegistry;     // checks value_buffer_ for flags_by_ptr_ map
-  template <typename T> friend T GetFromEnv(const char*, const char*, T);
+  template <typename T> friend T GetFromEnv(const char*, T);
   friend bool TryParseLocked(const CommandLineFlag*, FlagValue*,
                              const char*, string*);  // for New(), CopyFrom()
 
-  enum ValueType {
-    FV_BOOL = 0,
-    FV_INT32 = 1,
-    FV_INT64 = 2,
-    FV_UINT64 = 3,
-    FV_DOUBLE = 4,
-    FV_STRING = 5,
-    FV_MAX_INDEX = 5,
-  };
+  template <typename FlagType>
+  struct FlagValueTraits;
+
   const char* TypeName() const;
   bool Equal(const FlagValue& x) const;
   FlagValue* New() const;   // creates a new one with default value
   void CopyFrom(const FlagValue& x);
-  inline int ValueSize() const;
+  int ValueSize() const;
 
   // Calls the given validate-fn on value_buffer_, and returns
   // whatever it returns.  But first casts validate_fn_proto to a
@@ -226,14 +233,33 @@ class FlagValue {
   // (*validate_fn)(bool) for a bool flag).
   bool Validate(const char* flagname, ValidateFnProto validate_fn_proto) const;
 
-  void* value_buffer_;          // points to the buffer holding our data
-  int8 type_;                   // how to interpret value_
-  bool owns_value_;         // whether to free value on destruct
+  void* const value_buffer_;          // points to the buffer holding our data
+  const int8 type_;                   // how to interpret value_
+  const bool owns_value_;             // whether to free value on destruct
 
   FlagValue(const FlagValue&);   // no copying!
   void operator=(const FlagValue&);
 };
 
+// Map the given C++ type to a value of the ValueType enum at compile time.
+#define DEFINE_FLAG_TRAITS(type, value)        \
+  template <>                                  \
+  struct FlagValue::FlagValueTraits<type> {    \
+    static const ValueType kValueType = value; \
+  }
+
+// Define full template specializations of the FlagValueTraits template
+// for all supported flag types.
+DEFINE_FLAG_TRAITS(bool, FV_BOOL);
+DEFINE_FLAG_TRAITS(int32, FV_INT32);
+DEFINE_FLAG_TRAITS(uint32, FV_UINT32);
+DEFINE_FLAG_TRAITS(int64, FV_INT64);
+DEFINE_FLAG_TRAITS(uint64, FV_UINT64);
+DEFINE_FLAG_TRAITS(double, FV_DOUBLE);
+DEFINE_FLAG_TRAITS(std::string, FV_STRING);
+
+#undef DEFINE_FLAG_TRAITS
+
 
 // This could be a templated method of FlagValue, but doing so adds to the
 // size of the .o.  Since there's no type-safety here anyway, macro is ok.
@@ -241,16 +267,12 @@ class FlagValue {
 #define OTHER_VALUE_AS(fv, type)  *reinterpret_cast<type*>(fv.value_buffer_)
 #define SET_VALUE_AS(type, value)  VALUE_AS(type) = (value)
 
-FlagValue::FlagValue(void* valbuf, const char* type,
+template <typename FlagType>
+FlagValue::FlagValue(FlagType* valbuf,
                      bool transfer_ownership_of_value)
     : value_buffer_(valbuf),
+      type_(FlagValueTraits<FlagType>::kValueType),
       owns_value_(transfer_ownership_of_value) {
-  for (type_ = 0; type_ <= FV_MAX_INDEX; ++type_) {
-    if (!strcmp(type, TypeName())) {
-      break;
-    }
-  }
-  assert(type_ <= FV_MAX_INDEX);  // Unknown typename
 }
 
 FlagValue::~FlagValue() {
@@ -260,6 +282,7 @@ FlagValue::~FlagValue() {
   switch (type_) {
     case FV_BOOL: delete reinterpret_cast<bool*>(value_buffer_); break;
     case FV_INT32: delete reinterpret_cast<int32*>(value_buffer_); break;
+    case FV_UINT32: delete reinterpret_cast<uint32*>(value_buffer_); break;
     case FV_INT64: delete reinterpret_cast<int64*>(value_buffer_); break;
     case FV_UINT64: delete reinterpret_cast<uint64*>(value_buffer_); break;
     case FV_DOUBLE: delete reinterpret_cast<double*>(value_buffer_); break;
@@ -308,6 +331,16 @@ bool FlagValue::ParseFrom(const char* value) {
       SET_VALUE_AS(int32, static_cast<int32>(r));
       return true;
     }
+    case FV_UINT32: {
+      while (*value == ' ') value++;
+      if (*value == '-') return false;  // negative number
+      const uint64 r = strtou64(value, &end, base);
+      if (errno || end != value + strlen(value))  return false;  // bad parse
+        if (static_cast<uint32>(r) != r)  // worked, but number out of range
+        return false;
+      SET_VALUE_AS(uint32, static_cast<uint32>(r));
+      return true;
+    }
     case FV_INT64: {
       const int64 r = strto64(value, &end, base);
       if (errno || end != value + strlen(value))  return false;  // bad parse
@@ -343,6 +376,9 @@ string FlagValue::ToString() const {
     case FV_INT32:
       snprintf(intbuf, sizeof(intbuf), "%" PRId32, VALUE_AS(int32));
       return intbuf;
+    case FV_UINT32:
+      snprintf(intbuf, sizeof(intbuf), "%" PRIu32, VALUE_AS(uint32));
+      return intbuf;
     case FV_INT64:
       snprintf(intbuf, sizeof(intbuf), "%" PRId64, VALUE_AS(int64));
       return intbuf;
@@ -369,6 +405,9 @@ bool FlagValue::Validate(const char* flagname,
     case FV_INT32:
       return reinterpret_cast<bool (*)(const char*, int32)>(
           validate_fn_proto)(flagname, VALUE_AS(int32));
+    case FV_UINT32:
+      return reinterpret_cast<bool (*)(const char*, uint32)>(
+          validate_fn_proto)(flagname, VALUE_AS(uint32));
     case FV_INT64:
       return reinterpret_cast<bool (*)(const char*, int64)>(
           validate_fn_proto)(flagname, VALUE_AS(int64));
@@ -391,6 +430,7 @@ const char* FlagValue::TypeName() const {
   static const char types[] =
       "bool\0xx"
       "int32\0x"
+      "uint32\0"
       "int64\0x"
       "uint64\0"
       "double\0"
@@ -409,6 +449,7 @@ bool FlagValue::Equal(const FlagValue& x) const {
   switch (type_) {
     case FV_BOOL:   return VALUE_AS(bool) == OTHER_VALUE_AS(x, bool);
     case FV_INT32:  return VALUE_AS(int32) == OTHER_VALUE_AS(x, int32);
+    case FV_UINT32: return VALUE_AS(uint32) == OTHER_VALUE_AS(x, uint32);
     case FV_INT64:  return VALUE_AS(int64) == OTHER_VALUE_AS(x, int64);
     case FV_UINT64: return VALUE_AS(uint64) == OTHER_VALUE_AS(x, uint64);
     case FV_DOUBLE: return VALUE_AS(double) == OTHER_VALUE_AS(x, double);
@@ -418,14 +459,14 @@ bool FlagValue::Equal(const FlagValue& x) const {
 }
 
 FlagValue* FlagValue::New() const {
-  const char *type = TypeName();
   switch (type_) {
-    case FV_BOOL:   return new FlagValue(new bool(false), type, true);
-    case FV_INT32:  return new FlagValue(new int32(0), type, true);
-    case FV_INT64:  return new FlagValue(new int64(0), type, true);
-    case FV_UINT64: return new FlagValue(new uint64(0), type, true);
-    case FV_DOUBLE: return new FlagValue(new double(0.0), type, true);
-    case FV_STRING: return new FlagValue(new string, type, true);
+    case FV_BOOL:   return new FlagValue(new bool(false), true);
+    case FV_INT32:  return new FlagValue(new int32(0), true);
+    case FV_UINT32: return new FlagValue(new uint32(0), true);
+    case FV_INT64:  return new FlagValue(new int64(0), true);
+    case FV_UINT64: return new FlagValue(new uint64(0), true);
+    case FV_DOUBLE: return new FlagValue(new double(0.0), true);
+    case FV_STRING: return new FlagValue(new string, true);
     default: assert(false); return NULL;  // unknown type
   }
 }
@@ -435,6 +476,7 @@ void FlagValue::CopyFrom(const FlagValue& x) {
   switch (type_) {
     case FV_BOOL:   SET_VALUE_AS(bool, OTHER_VALUE_AS(x, bool));      break;
     case FV_INT32:  SET_VALUE_AS(int32, OTHER_VALUE_AS(x, int32));    break;
+    case FV_UINT32: SET_VALUE_AS(uint32, OTHER_VALUE_AS(x, uint32));  break;
     case FV_INT64:  SET_VALUE_AS(int64, OTHER_VALUE_AS(x, int64));    break;
     case FV_UINT64: SET_VALUE_AS(uint64, OTHER_VALUE_AS(x, uint64));  break;
     case FV_DOUBLE: SET_VALUE_AS(double, OTHER_VALUE_AS(x, double));  break;
@@ -443,7 +485,7 @@ void FlagValue::CopyFrom(const FlagValue& x) {
   }
 }
 
-inline int FlagValue::ValueSize() const {
+int FlagValue::ValueSize() const {
   if (type_ > FV_MAX_INDEX) {
     assert(false);  // unknown type
     return 0;
@@ -451,6 +493,7 @@ inline int FlagValue::ValueSize() const {
   static const uint8 valuesize[] = {
     sizeof(bool),
     sizeof(int32),
+    sizeof(uint32),
     sizeof(int64),
     sizeof(uint64),
     sizeof(double),
@@ -487,11 +530,14 @@ class CommandLineFlag {
   ValidateFnProto validate_function() const { return validate_fn_proto_; }
   const void* flag_ptr() const { return current_->value_buffer_; }
 
+  FlagValue::ValueType Type() const { return defvalue_->Type(); }
+
   void FillCommandLineFlagInfo(struct CommandLineFlagInfo* result);
 
   // If validate_fn_proto_ is non-NULL, calls it on value, returns result.
   bool Validate(const FlagValue& value) const;
   bool ValidateCurrent() const { return Validate(*current_); }
+  bool Modified() const { return modified_; }
 
  private:
   // for SetFlagLocked() and setting flags_by_ptr_
@@ -545,7 +591,7 @@ const char* CommandLineFlag::CleanFileName() const {
   const char* clean_name = filename() + strlen(filename()) - 1;
   while ( clean_name > filename() ) {
     if (*clean_name == PATH_SEPARATOR) {
-      if (strncmp(clean_name, kRootDir, sizeof(kRootDir)-1) == 0) {
+      if (sizeof(kRootDir) > 1 && strncmp(clean_name, kRootDir, sizeof(kRootDir)-1) == 0) {
         clean_name += sizeof(kRootDir)-1;    // past root-dir
         break;
       }
@@ -664,7 +710,7 @@ class FlagRegistry {
 
  private:
   friend class GFLAGS_NAMESPACE::FlagSaverImpl;  // reads all the flags in order to copy them
-  friend class CommandLineFlagParser;    // for ValidateAllFlags
+  friend class CommandLineFlagParser;    // for ValidateUnmodifiedFlags
   friend void GFLAGS_NAMESPACE::GetAllFlags(vector<CommandLineFlagInfo>*);
 
   // The map from name to flag, for FindFlagLocked().
@@ -680,7 +726,6 @@ class FlagRegistry {
   static FlagRegistry* global_registry_;   // a singleton registry
 
   Mutex lock_;
-  static Mutex global_registry_lock_;
 
   static void InitGlobalRegistry();
 
@@ -725,7 +770,12 @@ void FlagRegistry::RegisterFlag(CommandLineFlag* flag) {
 CommandLineFlag* FlagRegistry::FindFlagLocked(const char* name) {
   FlagConstIterator i = flags_.find(name);
   if (i == flags_.end()) {
-    return NULL;
+    // If the name has dashes in it, try again after replacing with
+    // underscores.
+    if (strchr(name, '-') == NULL) return NULL;
+    string name_rep = name;
+    std::replace(name_rep.begin(), name_rep.end(), '-', '_');
+    return FindFlagLocked(name_rep.c_str());
   } else {
     return i->second;
   }
@@ -777,7 +827,7 @@ CommandLineFlag* FlagRegistry::SplitArgumentLocked(const char* arg,
                                     kError, key->c_str());
       return NULL;
     }
-    if (strcmp(flag->type_name(), "bool") != 0) {
+    if (flag->Type() != FlagValue::FV_BOOL) {
       // 'x' exists but is not boolean, so we're not in the exception case.
       *error_message = StringPrintf(
           "%sboolean value (%s) specified for %s command line flag\n",
@@ -791,7 +841,7 @@ CommandLineFlag* FlagRegistry::SplitArgumentLocked(const char* arg,
   }
 
   // Assign a value if this is a boolean flag
-  if (*v == NULL && strcmp(flag->type_name(), "bool") == 0) {
+  if (*v == NULL && flag->Type() == FlagValue::FV_BOOL) {
     *v = "1";    // the --nox case was already handled, so this is the --x case
   }
 
@@ -878,18 +928,12 @@ bool FlagRegistry::SetFlagLocked(CommandLineFlag* flag,
 
 // Get the singleton FlagRegistry object
 FlagRegistry* FlagRegistry::global_registry_ = NULL;
-Mutex FlagRegistry::global_registry_lock_(Mutex::LINKER_INITIALIZED);
 
 FlagRegistry* FlagRegistry::GlobalRegistry() {
-  if (GetArgvSum() != 0) {
-    MutexLock acquire_lock(&global_registry_lock_);
-    if (!global_registry_) {
-      global_registry_ = new FlagRegistry;
-    }
-  } else {
-    if (!global_registry_) {
-      global_registry_ = new FlagRegistry;
-    }
+  static Mutex lock(Mutex::LINKER_INITIALIZED);
+  MutexLock acquire_lock(&lock);
+  if (!global_registry_) {
+    global_registry_ = new FlagRegistry;
   }
   return global_registry_;
 }
@@ -926,8 +970,10 @@ class CommandLineFlagParser {
   // In gflags_reporting.cc:HandleCommandLineHelpFlags().
 
   // Stage 3: validate all the commandline flags that have validators
-  // registered.
+  // registered and were not set/modified by ParseNewCommandLineFlags.
+  void ValidateFlags(bool all);
   void ValidateAllFlags();
+  void ValidateUnmodifiedFlags();
 
   // Stage 4: report any errors and return true if any were found.
   bool ReportErrors();
@@ -1014,9 +1060,6 @@ static string ReadFileIntoString(const char* filename) {
 
 uint32 CommandLineFlagParser::ParseNewCommandLineFlags(int* argc, char*** argv,
                                                        bool remove_flags) {
-  const char *program_name = strrchr((*argv)[0], PATH_SEPARATOR);   // nix path
-  program_name = (program_name == NULL ? (*argv)[0] : program_name+1);
-
   int first_nonopt = *argc;        // for non-options moved to the end
 
   registry_->Lock();
@@ -1056,7 +1099,7 @@ uint32 CommandLineFlagParser::ParseNewCommandLineFlags(int* argc, char*** argv,
 
     if (value == NULL) {
       // Boolean options are always assigned a value by SplitArgumentLocked()
-      assert(strcmp(flag->type_name(), "bool") != 0);
+      assert(flag->Type() != FlagValue::FV_BOOL);
       if (i+1 >= first_nonopt) {
         // This flag needs a value, but there is nothing available
         error_flags_[key] = (string(kError) + "flag '" + (*argv)[i] + "'"
@@ -1081,7 +1124,7 @@ uint32 CommandLineFlagParser::ParseNewCommandLineFlags(int* argc, char*** argv,
         // "-lat -30.5" would trigger the warning.  The common cases we
         // want to solve talk about true and false as values.
         if (value[0] == '-'
-            && strcmp(flag->type_name(), "string") == 0
+            && flag->Type() == FlagValue::FV_STRING
             && (strstr(flag->help(), "true")
                 || strstr(flag->help(), "false"))) {
           LOG(WARNING) << "Did you really mean to set flag '"
@@ -1146,8 +1189,8 @@ string CommandLineFlagParser::ProcessFromenvLocked(const string& flagval,
     }
 
     const string envname = string("FLAGS_") + string(flagname);
-	string envval;
-	if (!SafeGetEnv(envname.c_str(), envval)) {
+    string envval;
+    if (!SafeGetEnv(envname.c_str(), envval)) {
       if (errors_are_fatal) {
         error_flags_[flagname] = (string(kError) + envname +
                                   " not found in environment\n");
@@ -1193,23 +1236,35 @@ string CommandLineFlagParser::ProcessSingleOptionLocked(
   return msg;
 }
 
-void CommandLineFlagParser::ValidateAllFlags() {
+void CommandLineFlagParser::ValidateFlags(bool all) {
   FlagRegistryLock frl(registry_);
   for (FlagRegistry::FlagConstIterator i = registry_->flags_.begin();
        i != registry_->flags_.end(); ++i) {
-    if (!i->second->ValidateCurrent()) {
+    if ((all || !i->second->Modified()) && !i->second->ValidateCurrent()) {
       // only set a message if one isn't already there.  (If there's
       // an error message, our job is done, even if it's not exactly
       // the same error.)
-      if (error_flags_[i->second->name()].empty())
+      if (error_flags_[i->second->name()].empty()) {
         error_flags_[i->second->name()] =
             string(kError) + "--" + i->second->name() +
-            " must be set on the commandline"
-            " (default value fails validation)\n";
+            " must be set on the commandline";
+        if (!i->second->Modified()) {
+          error_flags_[i->second->name()] += " (default value fails validation)";
+        }
+        error_flags_[i->second->name()] += "\n";
+      }
     }
   }
 }
 
+void CommandLineFlagParser::ValidateAllFlags() {
+  ValidateFlags(true);
+}
+
+void CommandLineFlagParser::ValidateUnmodifiedFlags() {
+  ValidateFlags(false);
+}
+
 bool CommandLineFlagParser::ReportErrors() {
   // error_flags_ indicates errors we saw while parsing.
   // But we ignore undefined-names if ok'ed by --undef_ok
@@ -1261,7 +1316,11 @@ string CommandLineFlagParser::ProcessOptionsFromStringLocked(
   for (; line_end; flagfile_contents = line_end + 1) {
     while (*flagfile_contents && isspace(*flagfile_contents))
       ++flagfile_contents;
-    line_end = strchr(flagfile_contents, '\n');
+    // Windows uses "\r\n"
+    line_end = strchr(flagfile_contents, '\r');
+    if (line_end == NULL)
+        line_end = strchr(flagfile_contents, '\n');
+
     size_t len = line_end ? line_end - flagfile_contents
                           : strlen(flagfile_contents);
     string line(flagfile_contents, len);
@@ -1341,14 +1400,14 @@ string CommandLineFlagParser::ProcessOptionsFromStringLocked(
 // --------------------------------------------------------------------
 
 template<typename T>
-T GetFromEnv(const char *varname, const char* type, T dflt) {
+T GetFromEnv(const char *varname, T dflt) {
   std::string valstr;
   if (SafeGetEnv(varname, valstr)) {
-    FlagValue ifv(new T, type, true);
+    FlagValue ifv(new T, true);
     if (!ifv.ParseFrom(valstr.c_str())) {
       ReportError(DIE, "ERROR: error parsing env variable '%s' with value '%s'\n",
                   varname, valstr.c_str());
-	}
+    }
     return OTHER_VALUE_AS(ifv, T);
   } else return dflt;
 }
@@ -1395,23 +1454,49 @@ bool AddFlagValidator(const void* flag_ptr, ValidateFnProto validate_fn_proto) {
 //    values in a global destructor.
 // --------------------------------------------------------------------
 
-FlagRegisterer::FlagRegisterer(const char* name, const char* type,
-                               const char* help, const char* filename,
-                               void* current_storage, void* defvalue_storage) {
+namespace {
+void RegisterCommandLineFlag(const char* name,
+                             const char* help,
+                             const char* filename,
+                             FlagValue* current,
+                             FlagValue* defvalue) {
   if (help == NULL)
     help = "";
-  // FlagValue expects the type-name to not include any namespace
-  // components, so we get rid of those, if any.
-  if (strchr(type, ':'))
-    type = strrchr(type, ':') + 1;
-  FlagValue* current = new FlagValue(current_storage, type, false);
-  FlagValue* defvalue = new FlagValue(defvalue_storage, type, false);
   // Importantly, flag_ will never be deleted, so storage is always good.
-  CommandLineFlag* flag = new CommandLineFlag(name, help, filename,
-                                              current, defvalue);
-  FlagRegistry::GlobalRegistry()->RegisterFlag(flag);   // default registry
+  CommandLineFlag* flag =
+      new CommandLineFlag(name, help, filename, current, defvalue);
+  FlagRegistry::GlobalRegistry()->RegisterFlag(flag);  // default registry
+}
+}
+
+template <typename FlagType>
+FlagRegisterer::FlagRegisterer(const char* name,
+                               const char* help,
+                               const char* filename,
+                               FlagType* current_storage,
+                               FlagType* defvalue_storage) {
+  FlagValue* const current = new FlagValue(current_storage, false);
+  FlagValue* const defvalue = new FlagValue(defvalue_storage, false);
+  RegisterCommandLineFlag(name, help, filename, current, defvalue);
 }
 
+// Force compiler to generate code for the given template specialization.
+#define INSTANTIATE_FLAG_REGISTERER_CTOR(type)                  \
+  template GFLAGS_DLL_DECL FlagRegisterer::FlagRegisterer(      \
+      const char* name, const char* help, const char* filename, \
+      type* current_storage, type* defvalue_storage)
+
+// Do this for all supported flag types.
+INSTANTIATE_FLAG_REGISTERER_CTOR(bool);
+INSTANTIATE_FLAG_REGISTERER_CTOR(int32);
+INSTANTIATE_FLAG_REGISTERER_CTOR(uint32);
+INSTANTIATE_FLAG_REGISTERER_CTOR(int64);
+INSTANTIATE_FLAG_REGISTERER_CTOR(uint64);
+INSTANTIATE_FLAG_REGISTERER_CTOR(double);
+INSTANTIATE_FLAG_REGISTERER_CTOR(std::string);
+
+#undef INSTANTIATE_FLAG_REGISTERER_CTOR
+
 // --------------------------------------------------------------------
 // GetAllFlags()
 //    The main way the FlagRegistry class exposes its data.  This
@@ -1460,65 +1545,58 @@ void GetAllFlags(vector<CommandLineFlagInfo>* OUTPUT) {
 
 // These values are not protected by a Mutex because they are normally
 // set only once during program startup.
-static const char* argv0 = "UNKNOWN";      // just the program name
-static const char* cmdline = "";           // the entire command-line
+static string argv0("UNKNOWN");  // just the program name
+static string cmdline;           // the entire command-line
+static string program_usage;
 static vector<string> argvs;
 static uint32 argv_sum = 0;
-static const char* program_usage = NULL;
 
 void SetArgv(int argc, const char** argv) {
   static bool called_set_argv = false;
-  if (called_set_argv)         // we already have an argv for you
-    return;
-
+  if (called_set_argv) return;
   called_set_argv = true;
 
-  assert(argc > 0);            // every program has at least a progname
-  argv0 = strdup(argv[0]);     // small memory leak, but fn only called once
-  assert(argv0);
+  assert(argc > 0); // every program has at least a name
+  argv0 = argv[0];
 
-  string cmdline_string;       // easier than doing strcats
+  cmdline.clear();
   for (int i = 0; i < argc; i++) {
-    if (i != 0) {
-      cmdline_string += " ";
-    }
-    cmdline_string += argv[i];
+    if (i != 0) cmdline += " ";
+    cmdline += argv[i];
     argvs.push_back(argv[i]);
   }
-  cmdline = strdup(cmdline_string.c_str());  // another small memory leak
-  assert(cmdline);
 
   // Compute a simple sum of all the chars in argv
-  for (const char* c = cmdline; *c; c++)
+  argv_sum = 0;
+  for (string::const_iterator c = cmdline.begin(); c != cmdline.end(); ++c) {
     argv_sum += *c;
+  }
 }
 
 const vector<string>& GetArgvs() { return argvs; }
-const char* GetArgv()            { return cmdline; }
-const char* GetArgv0()           { return argv0; }
+const char* GetArgv()            { return cmdline.c_str(); }
+const char* GetArgv0()           { return argv0.c_str(); }
 uint32 GetArgvSum()              { return argv_sum; }
 const char* ProgramInvocationName() {             // like the GNU libc fn
   return GetArgv0();
 }
 const char* ProgramInvocationShortName() {        // like the GNU libc fn
-  const char* slash = strrchr(argv0, '/');
+  size_t pos = argv0.rfind('/');
 #ifdef OS_WINDOWS
-  if (!slash)  slash = strrchr(argv0, '\\');
+  if (pos == string::npos) pos = argv0.rfind('\\');
 #endif
-  return slash ? slash + 1 : argv0;
+  return (pos == string::npos ? argv0.c_str() : (argv0.c_str() + pos + 1));
 }
 
 void SetUsageMessage(const string& usage) {
-  if (program_usage != NULL)
-    ReportError(DIE, "ERROR: SetUsageMessage() called twice\n");
-  program_usage = strdup(usage.c_str());      // small memory leak
+  program_usage = usage;
 }
 
 const char* ProgramUsage() {
-  if (program_usage) {
-    return program_usage;
+  if (program_usage.empty()) {
+    return "Warning: SetUsageMessage() never called";
   }
-  return "Warning: SetUsageMessage() never called";
+  return program_usage.c_str();
 }
 
 // --------------------------------------------------------------------
@@ -1526,16 +1604,14 @@ const char* ProgramUsage() {
 // VersionString()
 // --------------------------------------------------------------------
 
-static const char* version_string = NULL;
+static string version_string;
 
 void SetVersionString(const string& version) {
-  if (version_string != NULL)
-    ReportError(DIE, "ERROR: SetVersionString() called twice\n");
-  version_string = strdup(version.c_str());   // small memory leak
+  version_string = version;
 }
 
 const char* VersionString() {
-  return version_string ? version_string : "";
+  return version_string.c_str();
 }
 
 
@@ -1796,6 +1872,7 @@ bool ReadFromFlagsFile(const string& filename, const char* prog_name,
 // --------------------------------------------------------------------
 // BoolFromEnv()
 // Int32FromEnv()
+// Uint32FromEnv()
 // Int64FromEnv()
 // Uint64FromEnv()
 // DoubleFromEnv()
@@ -1807,19 +1884,22 @@ bool ReadFromFlagsFile(const string& filename, const char* prog_name,
 // --------------------------------------------------------------------
 
 bool BoolFromEnv(const char *v, bool dflt) {
-  return GetFromEnv(v, "bool", dflt);
+  return GetFromEnv(v, dflt);
 }
 int32 Int32FromEnv(const char *v, int32 dflt) {
-  return GetFromEnv(v, "int32", dflt);
+  return GetFromEnv(v, dflt);
+}
+uint32 Uint32FromEnv(const char *v, uint32 dflt) {
+  return GetFromEnv(v, dflt);
 }
 int64 Int64FromEnv(const char *v, int64 dflt)    {
-  return GetFromEnv(v, "int64", dflt);
+  return GetFromEnv(v, dflt);
 }
 uint64 Uint64FromEnv(const char *v, uint64 dflt) {
-  return GetFromEnv(v, "uint64", dflt);
+  return GetFromEnv(v, dflt);
 }
 double DoubleFromEnv(const char *v, double dflt) {
-  return GetFromEnv(v, "double", dflt);
+  return GetFromEnv(v, dflt);
 }
 
 #ifdef _MSC_VER
@@ -1855,6 +1935,10 @@ bool RegisterFlagValidator(const int32* flag,
                            bool (*validate_fn)(const char*, int32)) {
   return AddFlagValidator(flag, reinterpret_cast<ValidateFnProto>(validate_fn));
 }
+bool RegisterFlagValidator(const uint32* flag,
+                           bool (*validate_fn)(const char*, uint32)) {
+  return AddFlagValidator(flag, reinterpret_cast<ValidateFnProto>(validate_fn));
+}
 bool RegisterFlagValidator(const int64* flag,
                            bool (*validate_fn)(const char*, int64)) {
   return AddFlagValidator(flag, reinterpret_cast<ValidateFnProto>(validate_fn));
@@ -1910,7 +1994,7 @@ static uint32 ParseCommandLineFlagsInternal(int* argc, char*** argv,
     HandleCommandLineHelpFlags();   // may cause us to exit on --help, etc.
 
   // See if any of the unset flags fail their validation checks
-  parser.ValidateAllFlags();
+  parser.ValidateUnmodifiedFlags();
 
   if (parser.ReportErrors())        // may cause us to exit on illegal flags
     gflags_exitfunc(1);
diff --git a/extern/gflags/src/config.h b/extern/gflags/src/gflags/config.h
similarity index 92%
rename from extern/gflags/src/config.h
rename to extern/gflags/src/gflags/config.h
index 8d20e2223623b382e08a7e9976d31779dca10881..a65a94b057138351f148e601d90385f0b0fd7ccd 100644
--- a/extern/gflags/src/config.h
+++ b/extern/gflags/src/gflags/config.h
@@ -2,11 +2,15 @@
 
 // Note: This header file is only used internally. It is not part of public interface!
 
+#ifndef GFLAGS_CONFIG_H_
+#define GFLAGS_CONFIG_H_
+
+
 // ---------------------------------------------------------------------------
 // System checks
 
 // Define if you build this library for a MS Windows OS.
-#ifdef WIN32
+#ifdef _WIN32
 #  define OS_WINDOWS
 #endif
 
@@ -58,19 +62,19 @@
 #define PACKAGE_NAME gflags
 
 // Define to the full name and version of this package.
-#define PACKAGE_STRING gflags 2.2.0
+#define PACKAGE_STRING gflags 2.2.1
 
 // Define to the one symbol short name of this package.
-#define PACKAGE_TARNAME gflags-2.2.0
+#define PACKAGE_TARNAME gflags-2.2.1
 
 // Define to the version of this package.
-#define PACKAGE_VERSION 2.2.0
+#define PACKAGE_VERSION 2.2.1
 
 // Version number of package.
 #define VERSION PACKAGE_VERSION
 
 // Define to the address where bug reports for this package should be sent.
-#define PACKAGE_BUGREPORT https://github.com/schuhschuh/gflags/issues
+#define PACKAGE_BUGREPORT https://github.com/gflags/gflags/issues
 
 // ---------------------------------------------------------------------------
 // Path separator
@@ -112,3 +116,6 @@
 #  endif
 #  include "windows_port.h"
 #endif
+
+
+#endif // GFLAGS_CONFIG_H_
diff --git a/extern/gflags/src/gflags/gflags.h b/extern/gflags/src/gflags/gflags.h
index 357eec6be7ce2f714db9c5e9cdbab78b113337f6..d5401f4f5757741b1b307df7c0fb9f23380532cd 100644
--- a/extern/gflags/src/gflags/gflags.h
+++ b/extern/gflags/src/gflags/gflags.h
@@ -81,12 +81,12 @@
 #include <string>
 #include <vector>
 
-#include "gflags_declare.h" // IWYU pragma: export
+#include "gflags/gflags_declare.h" // IWYU pragma: export
 
 
 // We always want to export variables defined in user code
 #ifndef GFLAGS_DLL_DEFINE_FLAG
-#  ifdef _MSC_VER
+#  if GFLAGS_IS_A_DLL && defined(_MSC_VER)
 #    define GFLAGS_DLL_DEFINE_FLAG __declspec(dllexport)
 #  else
 #    define GFLAGS_DLL_DEFINE_FLAG
@@ -128,6 +128,7 @@ namespace GFLAGS_NAMESPACE {
 // validator is already registered for this flag).
 extern GFLAGS_DLL_DECL bool RegisterFlagValidator(const bool*        flag, bool (*validate_fn)(const char*, bool));
 extern GFLAGS_DLL_DECL bool RegisterFlagValidator(const int32*       flag, bool (*validate_fn)(const char*, int32));
+extern GFLAGS_DLL_DECL bool RegisterFlagValidator(const uint32*      flag, bool (*validate_fn)(const char*, uint32));
 extern GFLAGS_DLL_DECL bool RegisterFlagValidator(const int64*       flag, bool (*validate_fn)(const char*, int64));
 extern GFLAGS_DLL_DECL bool RegisterFlagValidator(const uint64*      flag, bool (*validate_fn)(const char*, uint64));
 extern GFLAGS_DLL_DECL bool RegisterFlagValidator(const double*      flag, bool (*validate_fn)(const char*, double));
@@ -284,7 +285,11 @@ class GFLAGS_DLL_DECL FlagSaver {
 
   FlagSaver(const FlagSaver&);  // no copying!
   void operator=(const FlagSaver&);
-};
+}
+#ifdef __GNUC__
+__attribute((unused))
+#endif
+;
 
 // --------------------------------------------------------------------
 // Some deprecated or hopefully-soon-to-be-deprecated functions.
@@ -313,6 +318,7 @@ extern GFLAGS_DLL_DECL bool ReadFromFlagsFile(const std::string& filename, const
 
 extern GFLAGS_DLL_DECL bool BoolFromEnv(const char *varname, bool defval);
 extern GFLAGS_DLL_DECL int32 Int32FromEnv(const char *varname, int32 defval);
+extern GFLAGS_DLL_DECL uint32 Uint32FromEnv(const char *varname, uint32 defval);
 extern GFLAGS_DLL_DECL int64 Int64FromEnv(const char *varname, int64 defval);
 extern GFLAGS_DLL_DECL uint64 Uint64FromEnv(const char *varname, uint64 defval);
 extern GFLAGS_DLL_DECL double DoubleFromEnv(const char *varname, double defval);
@@ -429,9 +435,14 @@ extern GFLAGS_DLL_DECL void ShutDownCommandLineFlags();
 
 class GFLAGS_DLL_DECL FlagRegisterer {
  public:
-  FlagRegisterer(const char* name, const char* type,
+  // We instantiate this template ctor for all supported types,
+  // so it is possible to place implementation of the FlagRegisterer ctor in
+  // .cc file.
+  // Calling this constructor with unsupported type will produce linker error.
+  template <typename FlagType>
+  FlagRegisterer(const char* name,
                  const char* help, const char* filename,
-                 void* current_storage, void* defvalue_storage);
+                 FlagType* current_storage, FlagType* defvalue_storage);
 };
 
 // If your application #defines STRIP_FLAG_HELP to a non-zero value
@@ -473,7 +484,7 @@ extern GFLAGS_DLL_DECL const char kStrippedFlagHelp[];
     GFLAGS_DLL_DEFINE_FLAG type FLAGS_##name = FLAGS_nono##name;        \
     type FLAGS_no##name = FLAGS_nono##name;                             \
     static GFLAGS_NAMESPACE::FlagRegisterer o_##name(                   \
-      #name, #type, MAYBE_STRIPPED_HELP(help), __FILE__,                \
+      #name, MAYBE_STRIPPED_HELP(help), __FILE__,                       \
       &FLAGS_##name, &FLAGS_no##name);                                  \
   }                                                                     \
   using fL##shorttype::FLAGS_##name
@@ -508,6 +519,10 @@ GFLAGS_DLL_DECL bool IsBoolFlag(bool from);
    DEFINE_VARIABLE(GFLAGS_NAMESPACE::int32, I, \
                    name, val, txt)
 
+#define DEFINE_uint32(name,val, txt) \
+   DEFINE_VARIABLE(GFLAGS_NAMESPACE::uint32, U, \
+                   name, val, txt)
+
 #define DEFINE_int64(name, val, txt) \
    DEFINE_VARIABLE(GFLAGS_NAMESPACE::int64, I64, \
                    name, val, txt)
@@ -538,6 +553,26 @@ inline clstring* dont_pass0toDEFINE_string(char *stringspot,
 }
 inline clstring* dont_pass0toDEFINE_string(char *stringspot,
                                            int value);
+
+// Auxiliary class used to explicitly call destructor of string objects
+// allocated using placement new during static program deinitialization.
+// The destructor MUST be an inline function such that the explicit
+// destruction occurs in the same compilation unit as the placement new.
+class StringFlagDestructor {
+  void *current_storage_;
+  void *defvalue_storage_;
+
+public: 
+
+  StringFlagDestructor(void *current, void *defvalue)
+  : current_storage_(current), defvalue_storage_(defvalue) {}
+
+  ~StringFlagDestructor() {
+    reinterpret_cast<clstring*>(current_storage_ )->~clstring();
+    reinterpret_cast<clstring*>(defvalue_storage_)->~clstring();
+  }
+};
+
 }  // namespace fLS
 
 // We need to define a var named FLAGS_no##name so people don't define
@@ -550,13 +585,15 @@ inline clstring* dont_pass0toDEFINE_string(char *stringspot,
 #define DEFINE_string(name, val, txt)                                       \
   namespace fLS {                                                           \
     using ::fLS::clstring;                                                  \
+    using ::fLS::StringFlagDestructor;                                      \
     static union { void* align; char s[sizeof(clstring)]; } s_##name[2];    \
     clstring* const FLAGS_no##name = ::fLS::                                \
                                    dont_pass0toDEFINE_string(s_##name[0].s, \
                                                              val);          \
     static GFLAGS_NAMESPACE::FlagRegisterer o_##name(                       \
-        #name, "string", MAYBE_STRIPPED_HELP(txt), __FILE__,                \
-        s_##name[0].s, new (s_##name[1].s) clstring(*FLAGS_no##name));      \
+        #name, MAYBE_STRIPPED_HELP(txt), __FILE__,                          \
+        FLAGS_no##name, new (s_##name[1].s) clstring(*FLAGS_no##name));     \
+    static StringFlagDestructor d_##name(s_##name[0].s, s_##name[1].s);     \
     extern GFLAGS_DLL_DEFINE_FLAG clstring& FLAGS_##name;                   \
     using fLS::FLAGS_##name;                                                \
     clstring& FLAGS_##name = *FLAGS_no##name;                               \
diff --git a/extern/gflags/src/gflags/gflags_declare.h b/extern/gflags/src/gflags/gflags_declare.h
index 9b85f46cfdc7fa260d0017e53f120fd8459d0dc9..98747f3cda1dc17ffcf9dbbbd9be0637868f5551 100644
--- a/extern/gflags/src/gflags/gflags_declare.h
+++ b/extern/gflags/src/gflags/gflags_declare.h
@@ -45,18 +45,27 @@
 // ---------------------------------------------------------------------------
 // Windows DLL import/export.
 
-// We always want to import the symbols of the gflags library
+// Whether gflags library is a DLL.
+//
+// Set to 1 by default when the shared gflags library was built on Windows.
+// Must be overwritten when this header file is used with the optionally also
+// built static library instead; set by CMake's INTERFACE_COMPILE_DEFINITIONS.
+#ifndef GFLAGS_IS_A_DLL
+#  define GFLAGS_IS_A_DLL 1
+#endif
+
+// We always want to import the symbols of the gflags library.
 #ifndef GFLAGS_DLL_DECL
-#  if 1 && defined(_MSC_VER)
+#  if GFLAGS_IS_A_DLL && defined(_MSC_VER)
 #    define GFLAGS_DLL_DECL __declspec(dllimport)
 #  else
 #    define GFLAGS_DLL_DECL
 #  endif
 #endif
 
-// We always want to import variables declared in user code
+// We always want to import variables declared in user code.
 #ifndef GFLAGS_DLL_DECLARE_FLAG
-#  ifdef _MSC_VER
+#  if GFLAGS_IS_A_DLL && defined(_MSC_VER)
 #    define GFLAGS_DLL_DECLARE_FLAG __declspec(dllimport)
 #  else
 #    define GFLAGS_DLL_DECLARE_FLAG
@@ -120,6 +129,9 @@ typedef std::string clstring;
 #define DECLARE_int32(name) \
   DECLARE_VARIABLE(::GFLAGS_NAMESPACE::int32, I, name)
 
+#define DECLARE_uint32(name) \
+  DECLARE_VARIABLE(::GFLAGS_NAMESPACE::uint32, U, name)
+
 #define DECLARE_int64(name) \
   DECLARE_VARIABLE(::GFLAGS_NAMESPACE::int64, I64, name)
 
diff --git a/extern/gflags/src/gflags/gflags_gflags.h b/extern/gflags/src/gflags/gflags_gflags.h
index 0c17825dd62371d0f2827cdf8773b403fe410f59..36d0ba1d54cdc3140e5abd0b796dd813ec541dc2 100644
--- a/extern/gflags/src/gflags/gflags_gflags.h
+++ b/extern/gflags/src/gflags/gflags_gflags.h
@@ -77,6 +77,7 @@ using GFLAGS_NAMESPACE::AppendFlagsIntoFile;
 using GFLAGS_NAMESPACE::ReadFromFlagsFile;
 using GFLAGS_NAMESPACE::BoolFromEnv;
 using GFLAGS_NAMESPACE::Int32FromEnv;
+using GFLAGS_NAMESPACE::Uint32FromEnv;
 using GFLAGS_NAMESPACE::Int64FromEnv;
 using GFLAGS_NAMESPACE::Uint64FromEnv;
 using GFLAGS_NAMESPACE::DoubleFromEnv;
diff --git a/extern/gflags/src/gflags_completions.cc b/extern/gflags/src/gflags_completions.cc
index d7097caeef734efa8e0f044ad15c26c838111028..f7724864d5881b6b2b508931bc647cbaa545d56b 100644
--- a/extern/gflags/src/gflags_completions.cc
+++ b/extern/gflags/src/gflags_completions.cc
@@ -46,11 +46,6 @@
 //     5a) Force bash to place most-relevent groups at the top of the list
 //     5b) Trim most flag's descriptions to fit on a single terminal line
 
-
-#include "gflags_completions.h"
-
-#include "config.h"
-
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>   // for strlen
@@ -60,7 +55,9 @@
 #include <utility>
 #include <vector>
 
-#include "gflags.h"
+#include "config.h"
+#include "gflags/gflags.h"
+#include "gflags/gflags_completions.h"
 #include "util.h"
 
 using std::set;
@@ -122,7 +119,7 @@ static void CategorizeAllMatchingFlags(
     NotableFlags *notable_flags);
 
 static void TryFindModuleAndPackageDir(
-    const vector<CommandLineFlagInfo> all_flags,
+    const vector<CommandLineFlagInfo> &all_flags,
     string *module,
     string *package_dir);
 
@@ -472,7 +469,7 @@ static void PushNameWithSuffix(vector<string>* suffixes, const char* suffix) {
 }
 
 static void TryFindModuleAndPackageDir(
-    const vector<CommandLineFlagInfo> all_flags,
+    const vector<CommandLineFlagInfo> &all_flags,
     string *module,
     string *package_dir) {
   module->clear();
diff --git a/extern/gflags/src/gflags_reporting.cc b/extern/gflags/src/gflags_reporting.cc
index 9cc41a7488ce5dcfd04c94431ee473b87b5a381d..7cc669134599aa5ce5d3e36afffac9f0947939c7 100644
--- a/extern/gflags/src/gflags_reporting.cc
+++ b/extern/gflags/src/gflags_reporting.cc
@@ -56,8 +56,8 @@
 #include <vector>
 
 #include "config.h"
-#include "gflags.h"
-#include "gflags_completions.h"
+#include "gflags/gflags.h"
+#include "gflags/gflags_completions.h"
 #include "util.h"
 
 
@@ -126,7 +126,8 @@ string DescribeOneFlag(const CommandLineFlagInfo& flag) {
   string final_string = "";
   int chars_in_line = 0;  // how many chars in current line so far?
   while (1) {
-    assert(chars_left == strlen(c_string));  // Unless there's a \0 in there?
+    assert(static_cast<size_t>(chars_left)
+            == strlen(c_string));  // Unless there's a \0 in there?
     const char* newline = strchr(c_string, '\n');
     if (newline == NULL && chars_in_line+chars_left < kLineLength) {
       // The whole remainder of the string fits on this line
diff --git a/extern/gflags/src/mutex.h b/extern/gflags/src/mutex.h
index ff96f2b67f7e973360ea105659550ddb49dfeb16..7d7c364b79538fb5dbfbfbb83dc86c6fb4273bca 100644
--- a/extern/gflags/src/mutex.h
+++ b/extern/gflags/src/mutex.h
@@ -106,7 +106,7 @@
 #ifndef GFLAGS_MUTEX_H_
 #define GFLAGS_MUTEX_H_
 
-#include "gflags_declare.h"     // to figure out pthreads support
+#include "gflags/gflags_declare.h"     // to figure out pthreads support
 
 #if defined(NO_THREADS)
   typedef int MutexType;        // to keep a lock-count
@@ -166,7 +166,7 @@ class Mutex {
   // It inhibits work being done by the destructor, which makes it
   // safer for code that tries to acqiure this mutex in their global
   // destructor.
-  inline Mutex(LinkerInitialized);
+  explicit inline Mutex(LinkerInitialized);
 
   // Destructor
   inline ~Mutex();
@@ -197,7 +197,7 @@ class Mutex {
   inline void SetIsSafe() { is_safe_ = true; }
 
   // Catch the error of writing Mutex when intending MutexLock.
-  Mutex(Mutex* /*ignored*/) {}
+  explicit Mutex(Mutex* /*ignored*/) {}
   // Disallow "evil" constructors
   Mutex(const Mutex&);
   void operator=(const Mutex&);
diff --git a/extern/gflags/src/util.h b/extern/gflags/src/util.h
index fb59b38ddc4d809ebb4926d687125302fc8e42d9..164e3cf86ab8389d4ad9966266daa779e36f86fd 100644
--- a/extern/gflags/src/util.h
+++ b/extern/gflags/src/util.h
@@ -37,7 +37,6 @@
 #include "config.h"
 
 #include <assert.h>
-#include <config.h>
 #ifdef HAVE_INTTYPES_H
 #  include <inttypes.h>
 #endif
diff --git a/extern/gflags/src/windows_port.h b/extern/gflags/src/windows_port.h
index 1f546996783d9798f11fbde2158bab2c7ecd3cb8..61cf5b7e3a13cad5e939a5f7b08291be16e45796 100644
--- a/extern/gflags/src/windows_port.h
+++ b/extern/gflags/src/windows_port.h
@@ -69,10 +69,8 @@ extern GFLAGS_DLL_DECL int snprintf(char *str, size_t size,
 extern int GFLAGS_DLL_DECL safe_vsnprintf(char *str, size_t size,
                                              const char *format, va_list ap);
 #define vsnprintf(str, size, format, ap)  safe_vsnprintf(str, size, format, ap)
-#if defined(_MSC_VER) &&  (_MSC_VER < 1400)
 #define va_copy(dst, src)  (dst) = (src)
 #endif
-#endif
 #endif  /* #if !defined(__MINGW32__) && !defined(__MINGW64__) */
 
 #ifdef _MSC_VER
@@ -111,7 +109,9 @@ inline void setenv(const char* name, const char* value, int) {
 #define unlink   _unlink
 #endif
 
-#if !(defined(_MSC_VER) && _MSC_VER >= 1400)
+#if defined(_MSC_VER) && _MSC_VER >= 1800
+#include <inttypes.h>
+#else
 #define PRId32  "d"
 #define PRIu32  "u"
 #define PRId64  "I64d"
diff --git a/extern/glog/AUTHORS b/extern/glog/AUTHORS
index 72959a02585dc522a0d7d08d13c39831f2a064f4..8e654101b7c9c898b5aa5f617f3de07e82e061ea 100644
--- a/extern/glog/AUTHORS
+++ b/extern/glog/AUTHORS
@@ -8,11 +8,15 @@
 #
 # Please keep the list sorted.
 
+Abhishek Dasgupta <abhi2743@gmail.com>
 Abhishek Parmar <abhishek@orng.net>
+Andy Ying <andy@trailofbits.com>
 Brian Silverman <bsilver16384@gmail.com>
 Google Inc.
+Guillaume Dumont <dumont.guillaume@gmail.com>
 Michael Tanner <michael@tannertaxpro.com>
+MiniLight <MiniLightAR@Gmail.com>
 romange <romange@users.noreply.github.com>
-Sergiu Dotenco <sergiu.dotenco@th-nuernberg.de>
+Sergiu Deitsch <sergiu.deitsch@gmail.com>
 tbennun <tbennun@gmail.com>
 Teddy Reed <teddy@prosauce.org>
diff --git a/extern/glog/CMakeLists.txt b/extern/glog/CMakeLists.txt
index 32f4dca77e8569d12e02d904f198d339557ef385..df64718ee95c6024fe69eab70cc6fde0cc87c934 100644
--- a/extern/glog/CMakeLists.txt
+++ b/extern/glog/CMakeLists.txt
@@ -73,17 +73,21 @@ if(WIN32)
 		src/windows
 	)
 else()
+	list(APPEND INC
+		include
+	)
 	list(APPEND SRC
 		src/demangle.cc
 		src/signalhandler.cc
 		src/symbolize.cc
 
 		src/demangle.h
-		src/glog/logging.h
-		src/glog/log_severity.h
-		src/glog/raw_logging.h
-		src/glog/vlog_is_on.h
 		src/symbolize.h
+
+		include/glog/logging.h
+		include/glog/log_severity.h
+		include/glog/raw_logging.h
+		include/glog/vlog_is_on.h
 	)
 endif()
 
diff --git a/extern/glog/README.blender b/extern/glog/README.blender
index 288e78b3259f6a0330bc447c1982cda5ed52a9f7..38d5ff05c863e8e9812e0880b24aad7e93972c14 100644
--- a/extern/glog/README.blender
+++ b/extern/glog/README.blender
@@ -1,9 +1,9 @@
 Project: Google Logging
 URL: https://github.com/google/glog
 License: New BSD
-Upstream version: 0.3.4, 4d391fe
+Upstream version: 0.3.5, a6a166db069
 Local modifications:
 * Added per-platform config.h files so no configuration-time
   checks for functions and so are needed.
-* Applied changes from a fork https://github.com/Nazg-Gul/glog
-  (see https://github.com/google/glog/pull/81)
+* Added special definitions of HAVE_SNPRINTF and HAVE_LIB_GFLAGS
+  in Windows' specific config.h.
diff --git a/extern/glog/src/glog/log_severity.h b/extern/glog/include/glog/log_severity.h
similarity index 100%
rename from extern/glog/src/glog/log_severity.h
rename to extern/glog/include/glog/log_severity.h
diff --git a/extern/glog/src/glog/logging.h b/extern/glog/include/glog/logging.h
similarity index 99%
rename from extern/glog/src/glog/logging.h
rename to extern/glog/include/glog/logging.h
index c632fcaca1b0ba9bb119bd19948662b8026821fd..8238ca9610fc7ba341add5591bfb426baa897931 100644
--- a/extern/glog/src/glog/logging.h
+++ b/extern/glog/include/glog/logging.h
@@ -33,10 +33,6 @@
 // Pretty much everybody needs to #include this file so that they can
 // log various happenings.
 //
-#ifdef WIN32
-#  include "windows/glog/logging.h"
-#else  // WIN32
-
 #ifndef _LOGGING_H_
 #define _LOGGING_H_
 
@@ -648,7 +644,7 @@ void MakeCheckOpValueString(std::ostream* os, const unsigned char& v);
 // Build the error message string. Specify no inlining for code size.
 template <typename T1, typename T2>
 std::string* MakeCheckOpString(const T1& v1, const T2& v2, const char* exprtext)
-    __attribute__((noinline));
+    __attribute__ ((noinline));
 
 namespace base {
 namespace internal {
@@ -1244,7 +1240,7 @@ public:
   void SendToSyslogAndLog();  // Actually dispatch to syslog and the logs
 
   // Call abort() or similar to perform LOG(FATAL) crash.
-  static void __attribute__((noreturn)) Fail();
+  static void __attribute__ ((noreturn)) Fail();
 
   std::ostream& stream();
 
@@ -1292,7 +1288,7 @@ class GOOGLE_GLOG_DLL_DECL LogMessageFatal : public LogMessage {
  public:
   LogMessageFatal(const char* file, int line);
   LogMessageFatal(const char* file, int line, const CheckOpString& result);
-  __attribute__((noreturn)) ~LogMessageFatal();
+  __attribute__ ((noreturn)) ~LogMessageFatal();
 };
 
 // A non-macro interface to the log facility; (useful
@@ -1596,7 +1592,7 @@ class GOOGLE_GLOG_DLL_DECL NullStreamFatal : public NullStream {
   NullStreamFatal() { }
   NullStreamFatal(const char* file, int line, const CheckOpString& result) :
       NullStream(file, line, result) { }
-  __attribute__((noreturn)) ~NullStreamFatal() throw () { _exit(1); }
+  __attribute__ ((noreturn)) ~NullStreamFatal() throw () { _exit(1); }
 };
 
 // Install a signal handler that will dump signal information and a stack
@@ -1627,5 +1623,3 @@ GOOGLE_GLOG_DLL_DECL void InstallFailureWriter(
 }
 
 #endif // _LOGGING_H_
-
-#endif  // WIN32
diff --git a/extern/glog/src/glog/raw_logging.h b/extern/glog/include/glog/raw_logging.h
similarity index 98%
rename from extern/glog/src/glog/raw_logging.h
rename to extern/glog/include/glog/raw_logging.h
index de751d8a6b24536fe168599f1d9c8ef71a951af1..65278f62803f4782c3c7c7cc286fcc9d6389b670 100644
--- a/extern/glog/src/glog/raw_logging.h
+++ b/extern/glog/include/glog/raw_logging.h
@@ -32,9 +32,6 @@
 // Thread-safe logging routines that do not allocate any memory or
 // acquire any locks, and can therefore be used by low-level memory
 // allocation and synchronization code.
-#ifdef WIN32
-#  include "windows/glog/raw_logging.h"
-#else  // WIN32
 
 #ifndef BASE_RAW_LOGGING_H_
 #define BASE_RAW_LOGGING_H_
@@ -176,7 +173,7 @@ GOOGLE_GLOG_DLL_DECL void RawLog__(LogSeverity severity,
                                    const char* file,
                                    int line,
                                    const char* format, ...)
-   ;
+   __attribute__((__format__ (__printf__, 4, 5)));
 
 // Hack to propagate time information into this module so that
 // this module does not have to directly call localtime_r(),
@@ -186,5 +183,3 @@ GOOGLE_GLOG_DLL_DECL void RawLog__SetLastTime(const struct tm& t, int usecs);
 }
 
 #endif  // BASE_RAW_LOGGING_H_
-
-#endif  // WIN32
diff --git a/extern/glog/include/glog/stl_logging.h b/extern/glog/include/glog/stl_logging.h
new file mode 100644
index 0000000000000000000000000000000000000000..40a15aa457803be13315cb2411bf8aaeef107528
--- /dev/null
+++ b/extern/glog/include/glog/stl_logging.h
@@ -0,0 +1,220 @@
+// Copyright (c) 2003, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Stream output operators for STL containers; to be used for logging *only*.
+// Inclusion of this file lets you do:
+//
+// list<string> x;
+// LOG(INFO) << "data: " << x;
+// vector<int> v1, v2;
+// CHECK_EQ(v1, v2);
+//
+// If you want to use this header file with hash maps or slist, you
+// need to define macros before including this file:
+//
+// - GLOG_STL_LOGGING_FOR_UNORDERED     - <unordered_map> and <unordered_set>
+// - GLOG_STL_LOGGING_FOR_TR1_UNORDERED - <tr1/unordered_(map|set)>
+// - GLOG_STL_LOGGING_FOR_EXT_HASH      - <ext/hash_(map|set)>
+// - GLOG_STL_LOGGING_FOR_EXT_SLIST     - <ext/slist>
+//
+
+#ifndef UTIL_GTL_STL_LOGGING_INL_H_
+#define UTIL_GTL_STL_LOGGING_INL_H_
+
+#if !1
+# error We do not support stl_logging for this compiler
+#endif
+
+#include <deque>
+#include <list>
+#include <map>
+#include <ostream>
+#include <set>
+#include <utility>
+#include <vector>
+
+#ifdef GLOG_STL_LOGGING_FOR_UNORDERED
+# include <unordered_map>
+# include <unordered_set>
+#endif
+
+#ifdef GLOG_STL_LOGGING_FOR_TR1_UNORDERED
+# include <tr1/unordered_map>
+# include <tr1/unordered_set>
+#endif
+
+#ifdef GLOG_STL_LOGGING_FOR_EXT_HASH
+# include <ext/hash_set>
+# include <ext/hash_map>
+#endif
+#ifdef GLOG_STL_LOGGING_FOR_EXT_SLIST
+# include <ext/slist>
+#endif
+
+// Forward declare these two, and define them after all the container streams
+// operators so that we can recurse from pair -> container -> container -> pair
+// properly.
+template<class First, class Second>
+std::ostream& operator<<(std::ostream& out, const std::pair<First, Second>& p);
+
+namespace google {
+
+template<class Iter>
+void PrintSequence(std::ostream& out, Iter begin, Iter end);
+
+}
+
+#define OUTPUT_TWO_ARG_CONTAINER(Sequence) \
+template<class T1, class T2> \
+inline std::ostream& operator<<(std::ostream& out, \
+                                const Sequence<T1, T2>& seq) { \
+  google::PrintSequence(out, seq.begin(), seq.end()); \
+  return out; \
+}
+
+OUTPUT_TWO_ARG_CONTAINER(std::vector)
+OUTPUT_TWO_ARG_CONTAINER(std::deque)
+OUTPUT_TWO_ARG_CONTAINER(std::list)
+#ifdef GLOG_STL_LOGGING_FOR_EXT_SLIST
+OUTPUT_TWO_ARG_CONTAINER(__gnu_cxx::slist)
+#endif
+
+#undef OUTPUT_TWO_ARG_CONTAINER
+
+#define OUTPUT_THREE_ARG_CONTAINER(Sequence) \
+template<class T1, class T2, class T3> \
+inline std::ostream& operator<<(std::ostream& out, \
+                                const Sequence<T1, T2, T3>& seq) { \
+  google::PrintSequence(out, seq.begin(), seq.end()); \
+  return out; \
+}
+
+OUTPUT_THREE_ARG_CONTAINER(std::set)
+OUTPUT_THREE_ARG_CONTAINER(std::multiset)
+
+#undef OUTPUT_THREE_ARG_CONTAINER
+
+#define OUTPUT_FOUR_ARG_CONTAINER(Sequence) \
+template<class T1, class T2, class T3, class T4> \
+inline std::ostream& operator<<(std::ostream& out, \
+                                const Sequence<T1, T2, T3, T4>& seq) { \
+  google::PrintSequence(out, seq.begin(), seq.end()); \
+  return out; \
+}
+
+OUTPUT_FOUR_ARG_CONTAINER(std::map)
+OUTPUT_FOUR_ARG_CONTAINER(std::multimap)
+#ifdef GLOG_STL_LOGGING_FOR_UNORDERED
+OUTPUT_FOUR_ARG_CONTAINER(std::unordered_set)
+OUTPUT_FOUR_ARG_CONTAINER(std::unordered_multiset)
+#endif
+#ifdef GLOG_STL_LOGGING_FOR_TR1_UNORDERED
+OUTPUT_FOUR_ARG_CONTAINER(std::tr1::unordered_set)
+OUTPUT_FOUR_ARG_CONTAINER(std::tr1::unordered_multiset)
+#endif
+#ifdef GLOG_STL_LOGGING_FOR_EXT_HASH
+OUTPUT_FOUR_ARG_CONTAINER(__gnu_cxx::hash_set)
+OUTPUT_FOUR_ARG_CONTAINER(__gnu_cxx::hash_multiset)
+#endif
+
+#undef OUTPUT_FOUR_ARG_CONTAINER
+
+#define OUTPUT_FIVE_ARG_CONTAINER(Sequence) \
+template<class T1, class T2, class T3, class T4, class T5> \
+inline std::ostream& operator<<(std::ostream& out, \
+                                const Sequence<T1, T2, T3, T4, T5>& seq) { \
+  google::PrintSequence(out, seq.begin(), seq.end()); \
+  return out; \
+}
+
+#ifdef GLOG_STL_LOGGING_FOR_UNORDERED
+OUTPUT_FIVE_ARG_CONTAINER(std::unordered_map)
+OUTPUT_FIVE_ARG_CONTAINER(std::unordered_multimap)
+#endif
+#ifdef GLOG_STL_LOGGING_FOR_TR1_UNORDERED
+OUTPUT_FIVE_ARG_CONTAINER(std::tr1::unordered_map)
+OUTPUT_FIVE_ARG_CONTAINER(std::tr1::unordered_multimap)
+#endif
+#ifdef GLOG_STL_LOGGING_FOR_EXT_HASH
+OUTPUT_FIVE_ARG_CONTAINER(__gnu_cxx::hash_map)
+OUTPUT_FIVE_ARG_CONTAINER(__gnu_cxx::hash_multimap)
+#endif
+
+#undef OUTPUT_FIVE_ARG_CONTAINER
+
+template<class First, class Second>
+inline std::ostream& operator<<(std::ostream& out,
+                                const std::pair<First, Second>& p) {
+  out << '(' << p.first << ", " << p.second << ')';
+  return out;
+}
+
+namespace google {
+
+template<class Iter>
+inline void PrintSequence(std::ostream& out, Iter begin, Iter end) {
+  // Output at most 100 elements -- appropriate if used for logging.
+  for (int i = 0; begin != end && i < 100; ++i, ++begin) {
+    if (i > 0) out << ' ';
+    out << *begin;
+  }
+  if (begin != end) {
+    out << " ...";
+  }
+}
+
+}
+
+// Note that this is technically undefined behavior! We are adding things into
+// the std namespace for a reason though -- we are providing new operations on
+// types which are themselves defined with this namespace. Without this, these
+// operator overloads cannot be found via ADL. If these definitions are not
+// found via ADL, they must be #included before they're used, which requires
+// this header to be included before apparently independent other headers.
+//
+// For example, base/logging.h defines various template functions to implement
+// CHECK_EQ(x, y) and stream x and y into the log in the event the check fails.
+// It does so via the function template MakeCheckOpValueString:
+//   template<class T>
+//   void MakeCheckOpValueString(strstream* ss, const T& v) {
+//     (*ss) << v;
+//   }
+// Because 'glog/logging.h' is included before 'glog/stl_logging.h',
+// subsequent CHECK_EQ(v1, v2) for vector<...> typed variable v1 and v2 can only
+// find these operator definitions via ADL.
+//
+// Even this solution has problems -- it may pull unintended operators into the
+// namespace as well, allowing them to also be found via ADL, and creating code
+// that only works with a particular order of includes. Long term, we need to
+// move all of the *definitions* into namespace std, bet we need to ensure no
+// one references them first. This lets us take that step. We cannot define them
+// in both because that would create ambiguous overloads when both are found.
+namespace std { using ::operator<<; }
+
+#endif  // UTIL_GTL_STL_LOGGING_INL_H_
diff --git a/extern/glog/src/glog/vlog_is_on.h b/extern/glog/include/glog/vlog_is_on.h
similarity index 100%
rename from extern/glog/src/glog/vlog_is_on.h
rename to extern/glog/include/glog/vlog_is_on.h
diff --git a/extern/glog/src/demangle.h b/extern/glog/src/demangle.h
index 265302997fc8ee0f7fa620afbafdae3609609248..991b6ffcf2e64b65d2e25a5222974a5deb8e352e 100644
--- a/extern/glog/src/demangle.h
+++ b/extern/glog/src/demangle.h
@@ -71,6 +71,7 @@
 #define BASE_DEMANGLE_H_
 
 #include "config.h"
+#include "glog/logging.h"
 
 _START_GOOGLE_NAMESPACE_
 
diff --git a/extern/glog/src/logging.cc b/extern/glog/src/logging.cc
index 6552f46efdd655a908a1effb7a73462ae4715576..3e8f48e814de6f2246d7b50a49bef71b0d06b70a 100644
--- a/extern/glog/src/logging.cc
+++ b/extern/glog/src/logging.cc
@@ -260,6 +260,9 @@ static bool TerminalSupportsColor() {
       !strcmp(term, "xterm-color") ||
       !strcmp(term, "xterm-256color") ||
       !strcmp(term, "screen-256color") ||
+      !strcmp(term, "konsole") ||
+      !strcmp(term, "konsole-16color") ||
+      !strcmp(term, "konsole-256color") ||
       !strcmp(term, "screen") ||
       !strcmp(term, "linux") ||
       !strcmp(term, "cygwin");
@@ -301,7 +304,7 @@ static GLogColor SeverityToColor(LogSeverity severity) {
 #ifdef OS_WINDOWS
 
 // Returns the character attribute for the given color.
-static WORD GetColorAttribute(GLogColor color) {
+WORD GetColorAttribute(GLogColor color) {
   switch (color) {
     case COLOR_RED:    return FOREGROUND_RED;
     case COLOR_GREEN:  return FOREGROUND_GREEN;
@@ -313,7 +316,7 @@ static WORD GetColorAttribute(GLogColor color) {
 #else
 
 // Returns the ANSI color code for the given color.
-static const char* GetAnsiColorCode(GLogColor color) {
+const char* GetAnsiColorCode(GLogColor color) {
   switch (color) {
   case COLOR_RED:     return "1";
   case COLOR_GREEN:   return "2";
@@ -825,6 +828,7 @@ void LogDestination::DeleteLogDestinations() {
   }
   MutexLock l(&sink_mutex_);
   delete sinks_;
+  sinks_ = NULL;
 }
 
 namespace {
@@ -1677,7 +1681,6 @@ void LogToStderr() {
 namespace base {
 namespace internal {
 
-bool GetExitOnDFatal();
 bool GetExitOnDFatal() {
   MutexLock l(&log_mutex);
   return exit_on_dfatal;
@@ -1693,7 +1696,6 @@ bool GetExitOnDFatal() {
 // and the stack trace is not recorded.  The LOG(FATAL) *will* still
 // exit the program.  Since this function is used only in testing,
 // these differences are acceptable.
-void SetExitOnDFatal(bool value);
 void SetExitOnDFatal(bool value) {
   MutexLock l(&log_mutex);
   exit_on_dfatal = value;
diff --git a/extern/glog/src/raw_logging.cc b/extern/glog/src/raw_logging.cc
index 8517129fa81a64ffa5c438021349e17acb68c55b..7a7409bbf34b0bb3103fd33df76de0bf211ccc91 100644
--- a/extern/glog/src/raw_logging.cc
+++ b/extern/glog/src/raw_logging.cc
@@ -59,8 +59,7 @@
 # include <unistd.h>
 #endif
 
-// Hurd does not have SYS_write.
-#if (defined(HAVE_SYSCALL_H) || defined(HAVE_SYS_SYSCALL_H)) && !defined(__GNU__)
+#if defined(HAVE_SYSCALL_H) || defined(HAVE_SYS_SYSCALL_H)
 # define safe_write(fd, s, len)  syscall(SYS_write, fd, s, len)
 #else
   // Not so safe, but what can you do?
diff --git a/extern/glog/src/symbolize.cc b/extern/glog/src/symbolize.cc
index 6211e85e5db37befde4515beb2a10176611b3919..f83c309738ea4dae3b3afa2ddfebd6b84d2d85e8 100644
--- a/extern/glog/src/symbolize.cc
+++ b/extern/glog/src/symbolize.cc
@@ -327,7 +327,7 @@ FindSymbol(uint64_t pc, const int fd, char *out, int out_size,
 // false.
 static bool GetSymbolFromObjectFile(const int fd, uint64_t pc,
                                     char *out, int out_size,
-                                    uint64_t map_start_address) {
+                                    uint64_t map_base_address) {
   // Read the ELF header.
   ElfW(Ehdr) elf_header;
   if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) {
@@ -336,7 +336,28 @@ static bool GetSymbolFromObjectFile(const int fd, uint64_t pc,
 
   uint64_t symbol_offset = 0;
   if (elf_header.e_type == ET_DYN) {  // DSO needs offset adjustment.
-    symbol_offset = map_start_address;
+    ElfW(Phdr) phdr;
+    // We need to find the PT_LOAD segment corresponding to the read-execute
+    // file mapping in order to correctly perform the offset adjustment.
+    for (unsigned i = 0; i != elf_header.e_phnum; ++i) {
+      if (!ReadFromOffsetExact(fd, &phdr, sizeof(phdr),
+                               elf_header.e_phoff + i * sizeof(phdr)))
+        return false;
+      if (phdr.p_type == PT_LOAD &&
+          (phdr.p_flags & (PF_R | PF_X)) == (PF_R | PF_X)) {
+        // Find the mapped address corresponding to virtual address zero. We do
+        // this by first adding p_offset. This gives us the mapped address of
+        // the start of the segment, or in other words the mapped address
+        // corresponding to the virtual address of the segment. (Note that this
+        // is distinct from the start address, as p_offset is not guaranteed to
+        // be page aligned.) We then subtract p_vaddr, which takes us to virtual
+        // address zero.
+        symbol_offset = map_base_address + phdr.p_offset - phdr.p_vaddr;
+        break;
+      }
+    }
+    if (symbol_offset == 0)
+      return false;
   }
 
   ElfW(Shdr) symtab, strtab;
@@ -569,8 +590,8 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc,
       return -1;  // Malformed line.
     }
 
-    // Check flags.  We are only interested in "r-x" maps.
-    if (memcmp(flags_start, "r-x", 3) != 0) {  // Not a "r-x" map.
+   // Check flags.  We are only interested in "r*x" maps.
+    if (flags_start[0] != 'r' || flags_start[2] != 'x') {
       continue;  // We skip this map.
     }
     ++cursor;  // Skip ' '.
@@ -634,7 +655,7 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc,
 // bytes. Output will be truncated as needed, and a NUL character is always
 // appended.
 // NOTE: code from sandbox/linux/seccomp-bpf/demo.cc.
-static char *itoa_r(intptr_t i, char *buf, size_t sz, int base, size_t padding) {
+char *itoa_r(intptr_t i, char *buf, size_t sz, int base, size_t padding) {
   // Make sure we can write at least one NUL byte.
   size_t n = 1;
   if (n > sz)
@@ -696,7 +717,7 @@ static char *itoa_r(intptr_t i, char *buf, size_t sz, int base, size_t padding)
 
 // Safely appends string |source| to string |dest|.  Never writes past the
 // buffer size |dest_size| and guarantees that |dest| is null-terminated.
-static void SafeAppendString(const char* source, char* dest, int dest_size) {
+void SafeAppendString(const char* source, char* dest, int dest_size) {
   int dest_string_length = strlen(dest);
   SAFE_ASSERT(dest_string_length < dest_size);
   dest += dest_string_length;
@@ -709,7 +730,7 @@ static void SafeAppendString(const char* source, char* dest, int dest_size) {
 // Converts a 64-bit value into a hex string, and safely appends it to |dest|.
 // Never writes past the buffer size |dest_size| and guarantees that |dest| is
 // null-terminated.
-static void SafeAppendHexNumber(uint64_t value, char* dest, int dest_size) {
+void SafeAppendHexNumber(uint64_t value, char* dest, int dest_size) {
   // 64-bit numbers in hex can have up to 16 digits.
   char buf[17] = {'\0'};
   SafeAppendString(itoa_r(value, buf, sizeof(buf), 16, 0), dest, dest_size);
@@ -782,7 +803,7 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out,
     }
   }
   if (!GetSymbolFromObjectFile(wrapped_object_fd.get(), pc0,
-                               out, out_size, start_address)) {
+                               out, out_size, base_address)) {
     return false;
   }
 
diff --git a/extern/glog/src/utilities.cc b/extern/glog/src/utilities.cc
index 296fa7a67f3dde91b6b5cf5dc5f127783588865d..5c88e58d3c065fc51feb59dfc1f69baa4465c117 100644
--- a/extern/glog/src/utilities.cc
+++ b/extern/glog/src/utilities.cc
@@ -84,7 +84,7 @@ static void DebugWriteToStderr(const char* data, void *) {
   }
 }
 
-static void DebugWriteToString(const char* data, void *arg) {
+void DebugWriteToString(const char* data, void *arg) {
   reinterpret_cast<string*>(arg)->append(data);
 }
 
diff --git a/extern/glog/src/utilities.h b/extern/glog/src/utilities.h
index 4f41c92e434393895574663db946a7eaadf140e9..5f79968ef557ca48cca79014cdc1d82897418821 100644
--- a/extern/glog/src/utilities.h
+++ b/extern/glog/src/utilities.h
@@ -101,9 +101,7 @@
 // correctly when GetStackTrace() is called with max_depth == 0.
 // Some code may do that.
 
-#if defined(__MINGW32__) || defined(__FreeBSD__)
-# undef STACKTRACE_H
-#elif defined(HAVE_LIB_UNWIND)
+#if defined(HAVE_LIB_UNWIND)
 # define STACKTRACE_H "stacktrace_libunwind-inl.h"
 #elif !defined(NO_FRAME_POINTER)
 # if defined(__i386__) && __GNUC__ >= 2
diff --git a/extern/glog/src/vlog_is_on.cc b/extern/glog/src/vlog_is_on.cc
index e8fdbae7dcba311076a25dd845d17070387991f6..4c95583b6838d974c261c2c926c7b24b17d6d326 100644
--- a/extern/glog/src/vlog_is_on.cc
+++ b/extern/glog/src/vlog_is_on.cc
@@ -62,12 +62,6 @@ _START_GOOGLE_NAMESPACE_
 
 namespace glog_internal_namespace_ {
 
-// Used by logging_unittests.cc so can't make it static here.
-GOOGLE_GLOG_DLL_DECL bool SafeFNMatch_(const char* pattern,
-                                       size_t patt_len,
-                                       const char* str,
-                                       size_t str_len);
-
 // Implementation of fnmatch that does not need 0-termination
 // of arguments and does not allocate any memory,
 // but we only support "*" and "?" wildcards, not the "[...]" patterns.
diff --git a/extern/glog/src/windows/glog/logging.h b/extern/glog/src/windows/glog/logging.h
index 50135329d773834c2e4ef483d19555cd9414ef31..f521a2b9424d34677c86255178fbfc41525ad784 100644
--- a/extern/glog/src/windows/glog/logging.h
+++ b/extern/glog/src/windows/glog/logging.h
@@ -52,14 +52,6 @@
 #endif
 #include <vector>
 
-// Annoying stuff for windows -- makes sure clients can import these functions
-#ifndef GOOGLE_GLOG_DLL_DECL
-# if defined(_WIN32) && !defined(__CYGWIN__)
-#   define GOOGLE_GLOG_DLL_DECL  __declspec(dllimport)
-# else
-#   define GOOGLE_GLOG_DLL_DECL
-# endif
-#endif
 #if defined(_MSC_VER)
 #define GLOG_MSVC_PUSH_DISABLE_WARNING(n) __pragma(warning(push)) \
                                      __pragma(warning(disable:n))
@@ -69,35 +61,38 @@
 #define GLOG_MSVC_POP_WARNING()
 #endif
 
+// Annoying stuff for windows -- makes sure clients can import these functions
+#ifndef GOOGLE_GLOG_DLL_DECL
+# if defined(_WIN32) && !defined(__CYGWIN__)
+#   define GOOGLE_GLOG_DLL_DECL  __declspec(dllimport)
+# else
+#   define GOOGLE_GLOG_DLL_DECL
+# endif
+#endif
+
 // We care a lot about number of bits things take up.  Unfortunately,
 // systems define their bit-specific ints in a lot of different ways.
 // We use our own way, and have a typedef to get there.
 // Note: these commands below may look like "#if 1" or "#if 0", but
 // that's because they were constructed that way at ./configure time.
 // Look at logging.h.in to see how they're calculated (based on your config).
-#ifdef __MINGW32__
+#if 0
 #include <stdint.h>             // the normal place uint16_t is defined
 #endif
-#ifdef __MINGW32__
+#if 0
 #include <sys/types.h>          // the normal place u_int16_t is defined
 #endif
-#ifdef __MINGW32__
+#if 0
 #include <inttypes.h>           // a third place for uint16_t or u_int16_t
 #endif
 
-#if 1
+#if 0
 #include <gflags/gflags.h>
 #endif
 
-#ifdef __MINGW32__
-#  include <stdlib.h>
-#  include <unistd.h>
-#  define _exit(x) exit(x)
-#endif
-
 namespace google {
 
-#if defined(__MINGW32__)      // the C99 format
+#if 0      // the C99 format
 typedef int32_t int32;
 typedef uint32_t uint32;
 typedef int64_t int64;
@@ -107,7 +102,7 @@ typedef int32_t int32;
 typedef u_int32_t uint32;
 typedef int64_t int64;
 typedef u_int64_t uint64;
-#elif defined(_MSC_VER)    // the windows (vc7) format
+#elif 1    // the windows (vc7) format
 typedef __int32 int32;
 typedef unsigned __int32 uint32;
 typedef __int64 int64;
@@ -139,15 +134,28 @@ typedef unsigned __int64 uint64;
 #ifndef GOOGLE_PREDICT_BRANCH_NOT_TAKEN
 #if 0
 #define GOOGLE_PREDICT_BRANCH_NOT_TAKEN(x) (__builtin_expect(x, 0))
-#define GOOGLE_PREDICT_FALSE(x) (__builtin_expect(x, 0))
-#define GOOGLE_PREDICT_TRUE(x) (__builtin_expect(!!(x), 1))
 #else
 #define GOOGLE_PREDICT_BRANCH_NOT_TAKEN(x) x
+#endif
+#endif
+
+#ifndef GOOGLE_PREDICT_FALSE
+#if 0
+#define GOOGLE_PREDICT_FALSE(x) (__builtin_expect(x, 0))
+#else
 #define GOOGLE_PREDICT_FALSE(x) x
+#endif
+#endif
+
+#ifndef GOOGLE_PREDICT_TRUE
+#if 0
+#define GOOGLE_PREDICT_TRUE(x) (__builtin_expect(!!(x), 1))
+#else
 #define GOOGLE_PREDICT_TRUE(x) x
 #endif
 #endif
 
+
 // Make a bunch of macros for logging.  The way to log things is to stream
 // things to LOG(<a particular severity level>).  E.g.,
 //
@@ -357,6 +365,9 @@ DECLARE_int32(minloglevel);
 // default logging directory.
 DECLARE_string(log_dir);
 
+// Set the log file mode.
+DECLARE_int32(logfile_mode);
+
 // Sets the path of the directory into which to put additional links
 // to the log files.
 DECLARE_string(log_link);
@@ -424,9 +435,15 @@ DECLARE_bool(stop_logging_if_full_disk);
 #define LOG_TO_STRING_FATAL(message) google::NullStreamFatal()
 #endif
 
+#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+#define DCHECK_IS_ON() 0
+#else
+#define DCHECK_IS_ON() 1
+#endif
+
 // For DFATAL, we want to use LogMessage (as opposed to
 // LogMessageFatal), to be consistent with the original behavior.
-#ifdef NDEBUG
+#if !DCHECK_IS_ON()
 #define COMPACT_GOOGLE_LOG_DFATAL COMPACT_GOOGLE_LOG_ERROR
 #elif GOOGLE_STRIP_LOG <= 3
 #define COMPACT_GOOGLE_LOG_DFATAL google::LogMessage( \
@@ -552,7 +569,7 @@ class LogSink;  // defined below
 //   vector<string> *outvec;
 // The cast is to disambiguate NULL arguments.
 #define LOG_STRING(severity, outvec) \
-  LOG_TO_STRING_##severity(static_cast<vector<string>*>(outvec)).stream()
+  LOG_TO_STRING_##severity(static_cast<std::vector<std::string>*>(outvec)).stream()
 
 #define LOG_IF(severity, condition) \
   !(condition) ? (void) 0 : google::LogMessageVoidify() & LOG(severity)
@@ -565,7 +582,7 @@ class LogSink;  // defined below
   SYSLOG_IF(FATAL, !(condition)) << "Assert failed: " #condition
 
 // CHECK dies with a fatal error if condition is not true.  It is *not*
-// controlled by NDEBUG, so the check will be executed regardless of
+// controlled by DCHECK_IS_ON(), so the check will be executed regardless of
 // compilation mode.  Therefore, it is safe to do things like:
 //    CHECK(fp->Write(x) == 4)
 #define CHECK(condition)  \
@@ -715,15 +732,15 @@ DEFINE_CHECK_OP_IMPL(Check_GT, > )
 #if defined(STATIC_ANALYSIS)
 // Only for static analysis tool to know that it is equivalent to assert
 #define CHECK_OP_LOG(name, op, val1, val2, log) CHECK((val1) op (val2))
-#elif !defined(NDEBUG)
+#elif DCHECK_IS_ON()
 // In debug mode, avoid constructing CheckOpStrings if possible,
 // to reduce the overhead of CHECK statments by 2x.
 // Real DCHECK-heavy tests have seen 1.5x speedups.
 
-// The meaning of "string" might be different between now and 
+// The meaning of "string" might be different between now and
 // when this macro gets invoked (e.g., if someone is experimenting
 // with other string implementations that get defined after this
-// file is included).  Save the current meaning now and use it 
+// file is included).  Save the current meaning now and use it
 // in the macro.
 typedef std::string _Check_string;
 #define CHECK_OP_LOG(name, op, val1, val2, log)                         \
@@ -744,7 +761,7 @@ typedef std::string _Check_string;
              google::GetReferenceableValue(val2),        \
              #val1 " " #op " " #val2))                                  \
     log(__FILE__, __LINE__, _result).stream()
-#endif  // STATIC_ANALYSIS, !NDEBUG
+#endif  // STATIC_ANALYSIS, DCHECK_IS_ON()
 
 #if GOOGLE_STRIP_LOG <= 3
 #define CHECK_OP(name, op, val1, val2) \
@@ -969,7 +986,7 @@ const LogSeverity GLOG_0 = GLOG_ERROR;
 
 // Plus some debug-logging macros that get compiled to nothing for production
 
-#ifndef NDEBUG
+#if DCHECK_IS_ON()
 
 #define DLOG(severity) LOG(severity)
 #define DVLOG(verboselevel) VLOG(verboselevel)
@@ -979,7 +996,7 @@ const LogSeverity GLOG_0 = GLOG_ERROR;
   LOG_IF_EVERY_N(severity, condition, n)
 #define DLOG_ASSERT(condition) LOG_ASSERT(condition)
 
-// debug-only checking.  not executed in NDEBUG mode.
+// debug-only checking.  executed if DCHECK_IS_ON().
 #define DCHECK(condition) CHECK(condition)
 #define DCHECK_EQ(val1, val2) CHECK_EQ(val1, val2)
 #define DCHECK_NE(val1, val2) CHECK_NE(val1, val2)
@@ -993,7 +1010,7 @@ const LogSeverity GLOG_0 = GLOG_ERROR;
 #define DCHECK_STRNE(str1, str2) CHECK_STRNE(str1, str2)
 #define DCHECK_STRCASENE(str1, str2) CHECK_STRCASENE(str1, str2)
 
-#else  // NDEBUG
+#else  // !DCHECK_IS_ON()
 
 #define DLOG(severity) \
   true ? (void) 0 : google::LogMessageVoidify() & LOG(severity)
@@ -1074,7 +1091,7 @@ const LogSeverity GLOG_0 = GLOG_ERROR;
   while (false) \
     GLOG_MSVC_POP_WARNING() CHECK_STRCASENE(str1, str2)
 
-#endif  // NDEBUG
+#endif  // DCHECK_IS_ON()
 
 // Log only in verbose mode.
 
@@ -1164,6 +1181,8 @@ public:
     char* str() const { return pbase(); }
 
   private:
+    LogStream(const LogStream&);
+    LogStream& operator=(const LogStream&);
     base_logging::LogStreamBuf streambuf_;
     int ctr_;  // Counter hack (for the LOG_EVERY_X() macro)
     LogStream *self_;  // Consistency check hack
@@ -1231,7 +1250,7 @@ public:
   void SendToSyslogAndLog();  // Actually dispatch to syslog and the logs
 
   // Call abort() or similar to perform LOG(FATAL) crash.
-  static void Fail() ;
+  static void __declspec(noreturn) Fail();
 
   std::ostream& stream();
 
@@ -1279,7 +1298,7 @@ class GOOGLE_GLOG_DLL_DECL LogMessageFatal : public LogMessage {
  public:
   LogMessageFatal(const char* file, int line);
   LogMessageFatal(const char* file, int line, const CheckOpString& result);
-  ~LogMessageFatal() ;
+  __declspec(noreturn) ~LogMessageFatal();
 };
 
 // A non-macro interface to the log facility; (useful
@@ -1294,6 +1313,35 @@ inline void LogAtLevel(int const severity, std::string const &msg) {
 // LOG macros, 2. this macro can be used as C++ stream.
 #define LOG_AT_LEVEL(severity) google::LogMessage(__FILE__, __LINE__, severity).stream()
 
+// Check if it's compiled in C++11 mode.
+//
+// GXX_EXPERIMENTAL_CXX0X is defined by gcc and clang up to at least
+// gcc-4.7 and clang-3.1 (2011-12-13).  __cplusplus was defined to 1
+// in gcc before 4.7 (Crosstool 16) and clang before 3.1, but is
+// defined according to the language version in effect thereafter.
+// Microsoft Visual Studio 14 (2015) sets __cplusplus==199711 despite
+// reasonably good C++11 support, so we set LANG_CXX for it and
+// newer versions (_MSC_VER >= 1900).
+#if (defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L || \
+     (defined(_MSC_VER) && _MSC_VER >= 1900))
+// Helper for CHECK_NOTNULL().
+//
+// In C++11, all cases can be handled by a single function. Since the value
+// category of the argument is preserved (also for rvalue references),
+// member initializer lists like the one below will compile correctly:
+//
+//   Foo()
+//     : x_(CHECK_NOTNULL(MethodReturningUniquePtr())) {}
+template <typename T>
+T CheckNotNull(const char* file, int line, const char* names, T&& t) {
+ if (t == nullptr) {
+   LogMessageFatal(file, line, new std::string(names));
+ }
+ return std::forward<T>(t);
+}
+
+#else
+
 // A small helper for CHECK_NOTNULL().
 template <typename T>
 T* CheckNotNull(const char *file, int line, const char *names, T* t) {
@@ -1302,6 +1350,7 @@ T* CheckNotNull(const char *file, int line, const char *names, T* t) {
   }
   return t;
 }
+#endif
 
 // Allow folks to put a counter in the LOG_EVERY_X()'ed messages. This
 // only works if ostream is a LogStream. If the ostream is not a
@@ -1583,7 +1632,7 @@ class GOOGLE_GLOG_DLL_DECL NullStreamFatal : public NullStream {
   NullStreamFatal() { }
   NullStreamFatal(const char* file, int line, const CheckOpString& result) :
       NullStream(file, line, result) { }
-   ~NullStreamFatal() { _exit(1); }
+  __declspec(noreturn) ~NullStreamFatal() throw () { _exit(1); }
 };
 
 // Install a signal handler that will dump signal information and a stack
diff --git a/extern/glog/src/windows/glog/stl_logging.h b/extern/glog/src/windows/glog/stl_logging.h
new file mode 100644
index 0000000000000000000000000000000000000000..a97a90895959a3f6f935b92363f59c6efb27ad8d
--- /dev/null
+++ b/extern/glog/src/windows/glog/stl_logging.h
@@ -0,0 +1,224 @@
+// This file is automatically generated from src/glog/stl_logging.h.in
+// using src/windows/preprocess.sh.
+// DO NOT EDIT!
+
+// Copyright (c) 2003, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Stream output operators for STL containers; to be used for logging *only*.
+// Inclusion of this file lets you do:
+//
+// list<string> x;
+// LOG(INFO) << "data: " << x;
+// vector<int> v1, v2;
+// CHECK_EQ(v1, v2);
+//
+// If you want to use this header file with hash_compare maps or slist, you
+// need to define macros before including this file:
+//
+// - GLOG_STL_LOGGING_FOR_UNORDERED     - <unordered_map> and <unordered_set>
+// - GLOG_STL_LOGGING_FOR_TR1_UNORDERED - <tr1/unordered_(map|set)>
+// - GLOG_STL_LOGGING_FOR_EXT_HASH      - <ext/hash_(map|set)>
+// - GLOG_STL_LOGGING_FOR_EXT_SLIST     - <ext/slist>
+//
+
+#ifndef UTIL_GTL_STL_LOGGING_INL_H_
+#define UTIL_GTL_STL_LOGGING_INL_H_
+
+#if !1
+# error We do not support stl_logging for this compiler
+#endif
+
+#include <deque>
+#include <list>
+#include <map>
+#include <ostream>
+#include <set>
+#include <utility>
+#include <vector>
+
+#ifdef GLOG_STL_LOGGING_FOR_UNORDERED
+# include <unordered_map>
+# include <unordered_set>
+#endif
+
+#ifdef GLOG_STL_LOGGING_FOR_TR1_UNORDERED
+# include <tr1/unordered_map>
+# include <tr1/unordered_set>
+#endif
+
+#ifdef GLOG_STL_LOGGING_FOR_EXT_HASH
+# include <ext/hash_set>
+# include <ext/hash_map>
+#endif
+#ifdef GLOG_STL_LOGGING_FOR_EXT_SLIST
+# include <ext/slist>
+#endif
+
+// Forward declare these two, and define them after all the container streams
+// operators so that we can recurse from pair -> container -> container -> pair
+// properly.
+template<class First, class Second>
+std::ostream& operator<<(std::ostream& out, const std::pair<First, Second>& p);
+
+namespace google {
+
+template<class Iter>
+void PrintSequence(std::ostream& out, Iter begin, Iter end);
+
+}
+
+#define OUTPUT_TWO_ARG_CONTAINER(Sequence) \
+template<class T1, class T2> \
+inline std::ostream& operator<<(std::ostream& out, \
+                                const Sequence<T1, T2>& seq) { \
+  google::PrintSequence(out, seq.begin(), seq.end()); \
+  return out; \
+}
+
+OUTPUT_TWO_ARG_CONTAINER(std::vector)
+OUTPUT_TWO_ARG_CONTAINER(std::deque)
+OUTPUT_TWO_ARG_CONTAINER(std::list)
+#ifdef GLOG_STL_LOGGING_FOR_EXT_SLIST
+OUTPUT_TWO_ARG_CONTAINER(__gnu_cxx::slist)
+#endif
+
+#undef OUTPUT_TWO_ARG_CONTAINER
+
+#define OUTPUT_THREE_ARG_CONTAINER(Sequence) \
+template<class T1, class T2, class T3> \
+inline std::ostream& operator<<(std::ostream& out, \
+                                const Sequence<T1, T2, T3>& seq) { \
+  google::PrintSequence(out, seq.begin(), seq.end()); \
+  return out; \
+}
+
+OUTPUT_THREE_ARG_CONTAINER(std::set)
+OUTPUT_THREE_ARG_CONTAINER(std::multiset)
+
+#undef OUTPUT_THREE_ARG_CONTAINER
+
+#define OUTPUT_FOUR_ARG_CONTAINER(Sequence) \
+template<class T1, class T2, class T3, class T4> \
+inline std::ostream& operator<<(std::ostream& out, \
+                                const Sequence<T1, T2, T3, T4>& seq) { \
+  google::PrintSequence(out, seq.begin(), seq.end()); \
+  return out; \
+}
+
+OUTPUT_FOUR_ARG_CONTAINER(std::map)
+OUTPUT_FOUR_ARG_CONTAINER(std::multimap)
+#ifdef GLOG_STL_LOGGING_FOR_UNORDERED
+OUTPUT_FOUR_ARG_CONTAINER(std::unordered_set)
+OUTPUT_FOUR_ARG_CONTAINER(std::unordered_multiset)
+#endif
+#ifdef GLOG_STL_LOGGING_FOR_TR1_UNORDERED
+OUTPUT_FOUR_ARG_CONTAINER(std::tr1::unordered_set)
+OUTPUT_FOUR_ARG_CONTAINER(std::tr1::unordered_multiset)
+#endif
+#ifdef GLOG_STL_LOGGING_FOR_EXT_HASH
+OUTPUT_FOUR_ARG_CONTAINER(__gnu_cxx::hash_set)
+OUTPUT_FOUR_ARG_CONTAINER(__gnu_cxx::hash_multiset)
+#endif
+
+#undef OUTPUT_FOUR_ARG_CONTAINER
+
+#define OUTPUT_FIVE_ARG_CONTAINER(Sequence) \
+template<class T1, class T2, class T3, class T4, class T5> \
+inline std::ostream& operator<<(std::ostream& out, \
+                                const Sequence<T1, T2, T3, T4, T5>& seq) { \
+  google::PrintSequence(out, seq.begin(), seq.end()); \
+  return out; \
+}
+
+#ifdef GLOG_STL_LOGGING_FOR_UNORDERED
+OUTPUT_FIVE_ARG_CONTAINER(std::unordered_map)
+OUTPUT_FIVE_ARG_CONTAINER(std::unordered_multimap)
+#endif
+#ifdef GLOG_STL_LOGGING_FOR_TR1_UNORDERED
+OUTPUT_FIVE_ARG_CONTAINER(std::tr1::unordered_map)
+OUTPUT_FIVE_ARG_CONTAINER(std::tr1::unordered_multimap)
+#endif
+#ifdef GLOG_STL_LOGGING_FOR_EXT_HASH
+OUTPUT_FIVE_ARG_CONTAINER(__gnu_cxx::hash_map)
+OUTPUT_FIVE_ARG_CONTAINER(__gnu_cxx::hash_multimap)
+#endif
+
+#undef OUTPUT_FIVE_ARG_CONTAINER
+
+template<class First, class Second>
+inline std::ostream& operator<<(std::ostream& out,
+                                const std::pair<First, Second>& p) {
+  out << '(' << p.first << ", " << p.second << ')';
+  return out;
+}
+
+namespace google {
+
+template<class Iter>
+inline void PrintSequence(std::ostream& out, Iter begin, Iter end) {
+  // Output at most 100 elements -- appropriate if used for logging.
+  for (int i = 0; begin != end && i < 100; ++i, ++begin) {
+    if (i > 0) out << ' ';
+    out << *begin;
+  }
+  if (begin != end) {
+    out << " ...";
+  }
+}
+
+}
+
+// Note that this is technically undefined behavior! We are adding things into
+// the std namespace for a reason though -- we are providing new operations on
+// types which are themselves defined with this namespace. Without this, these
+// operator overloads cannot be found via ADL. If these definitions are not
+// found via ADL, they must be #included before they're used, which requires
+// this header to be included before apparently independent other headers.
+//
+// For example, base/logging.h defines various template functions to implement
+// CHECK_EQ(x, y) and stream x and y into the log in the event the check fails.
+// It does so via the function template MakeCheckOpValueString:
+//   template<class T>
+//   void MakeCheckOpValueString(strstream* ss, const T& v) {
+//     (*ss) << v;
+//   }
+// Because 'glog/logging.h' is included before 'glog/stl_logging.h',
+// subsequent CHECK_EQ(v1, v2) for vector<...> typed variable v1 and v2 can only
+// find these operator definitions via ADL.
+//
+// Even this solution has problems -- it may pull unintended operators into the
+// namespace as well, allowing them to also be found via ADL, and creating code
+// that only works with a particular order of includes. Long term, we need to
+// move all of the *definitions* into namespace std, bet we need to ensure no
+// one references them first. This lets us take that step. We cannot define them
+// in both because that would create ambiguous overloads when both are found.
+namespace std { using ::operator<<; }
+
+#endif  // UTIL_GTL_STL_LOGGING_INL_H_
diff --git a/extern/glog/src/windows/port.h b/extern/glog/src/windows/port.h
index 02c6f6e77b22897549440f7ff4a34f7538249d95..819846151b956c8898b0d327b6aab662ec76cddf 100644
--- a/extern/glog/src/windows/port.h
+++ b/extern/glog/src/windows/port.h
@@ -136,6 +136,7 @@ typedef int pid_t;
 #endif  // _MSC_VER
 
 // ----------------------------------- THREADS
+#ifndef __MINGW32__
 typedef DWORD pthread_t;
 typedef DWORD pthread_key_t;
 typedef LONG pthread_once_t;
@@ -147,15 +148,11 @@ inline struct tm* localtime_r(const time_t* timep, struct tm* result) {
   localtime_s(result, timep);
   return result;
 }
+#endif
 
 inline char* strerror_r(int errnum, char* buf, size_t buflen) {
-#ifdef FREE_WINDOWS
-  strncpy(buf, "Not implemented yet", buflen);
-  return buf;
-#else
   strerror_s(buf, buflen, errnum);
   return buf;
-#endif
 }
 
 #ifndef __cplusplus
diff --git a/extern/gmock/CHANGES b/extern/gmock/CHANGES
new file mode 100644
index 0000000000000000000000000000000000000000..d6f2f760e3424715262ec14f318e1df9dac909bf
--- /dev/null
+++ b/extern/gmock/CHANGES
@@ -0,0 +1,126 @@
+Changes for 1.7.0:
+
+* All new improvements in Google Test 1.7.0.
+* New feature: matchers DoubleNear(), FloatNear(),
+  NanSensitiveDoubleNear(), NanSensitiveFloatNear(),
+  UnorderedElementsAre(), UnorderedElementsAreArray(), WhenSorted(),
+  WhenSortedBy(), IsEmpty(), and SizeIs().
+* Improvement: Google Mock can now be built as a DLL.
+* Improvement: when compiled by a C++11 compiler, matchers AllOf()
+  and AnyOf() can accept an arbitrary number of matchers.
+* Improvement: when compiled by a C++11 compiler, matchers
+  ElementsAreArray() can accept an initializer list.
+* Improvement: when exceptions are enabled, a mock method with no
+  default action now throws instead crashing the test.
+* Improvement: added class testing::StringMatchResultListener to aid
+  definition of composite matchers.
+* Improvement: function return types used in MOCK_METHOD*() macros can
+  now contain unprotected commas.
+* Improvement (potentially breaking): EXPECT_THAT() and ASSERT_THAT()
+  are now more strict in ensuring that the value type and the matcher
+  type are compatible, catching potential bugs in tests.
+* Improvement: Pointee() now works on an optional<T>.
+* Improvement: the ElementsAreArray() matcher can now take a vector or
+  iterator range as input, and makes a copy of its input elements
+  before the conversion to a Matcher.
+* Improvement: the Google Mock Generator can now generate mocks for
+  some class templates.
+* Bug fix: mock object destruction triggerred by another mock object's
+  destruction no longer hangs.
+* Improvement: Google Mock Doctor works better with newer Clang and
+  GCC now.
+* Compatibility fixes.
+* Bug/warning fixes.
+
+Changes for 1.6.0:
+
+* Compilation is much faster and uses much less memory, especially
+  when the constructor and destructor of a mock class are moved out of
+  the class body.
+* New matchers: Pointwise(), Each().
+* New actions: ReturnPointee() and ReturnRefOfCopy().
+* CMake support.
+* Project files for Visual Studio 2010.
+* AllOf() and AnyOf() can handle up-to 10 arguments now.
+* Google Mock doctor understands Clang error messages now.
+* SetArgPointee<> now accepts string literals.
+* gmock_gen.py handles storage specifier macros and template return
+  types now.
+* Compatibility fixes.
+* Bug fixes and implementation clean-ups.
+* Potentially incompatible changes: disables the harmful 'make install'
+  command in autotools.
+
+Potentially breaking changes:
+
+* The description string for MATCHER*() changes from Python-style
+  interpolation to an ordinary C++ string expression.
+* SetArgumentPointee is deprecated in favor of SetArgPointee.
+* Some non-essential project files for Visual Studio 2005 are removed.
+
+Changes for 1.5.0:
+
+ * New feature: Google Mock can be safely used in multi-threaded tests
+   on platforms having pthreads.
+ * New feature: function for printing a value of arbitrary type.
+ * New feature: function ExplainMatchResult() for easy definition of
+   composite matchers.
+ * The new matcher API lets user-defined matchers generate custom
+   explanations more directly and efficiently.
+ * Better failure messages all around.
+ * NotNull() and IsNull() now work with smart pointers.
+ * Field() and Property() now work when the matcher argument is a pointer
+   passed by reference.
+ * Regular expression matchers on all platforms.
+ * Added GCC 4.0 support for Google Mock Doctor.
+ * Added gmock_all_test.cc for compiling most Google Mock tests
+   in a single file.
+ * Significantly cleaned up compiler warnings.
+ * Bug fixes, better test coverage, and implementation clean-ups.
+
+ Potentially breaking changes:
+
+ * Custom matchers defined using MatcherInterface or MakePolymorphicMatcher()
+   need to be updated after upgrading to Google Mock 1.5.0; matchers defined
+   using MATCHER or MATCHER_P* aren't affected.
+ * Dropped support for 'make install'.
+
+Changes for 1.4.0 (we skipped 1.2.* and 1.3.* to match the version of
+Google Test):
+
+ * Works in more environments: Symbian and minGW, Visual C++ 7.1.
+ * Lighter weight: comes with our own implementation of TR1 tuple (no
+   more dependency on Boost!).
+ * New feature: --gmock_catch_leaked_mocks for detecting leaked mocks.
+ * New feature: ACTION_TEMPLATE for defining templatized actions.
+ * New feature: the .After() clause for specifying expectation order.
+ * New feature: the .With() clause for for specifying inter-argument
+   constraints.
+ * New feature: actions ReturnArg<k>(), ReturnNew<T>(...), and
+   DeleteArg<k>().
+ * New feature: matchers Key(), Pair(), Args<...>(), AllArgs(), IsNull(),
+   and Contains().
+ * New feature: utility class MockFunction<F>, useful for checkpoints, etc.
+ * New feature: functions Value(x, m) and SafeMatcherCast<T>(m).
+ * New feature: copying a mock object is rejected at compile time.
+ * New feature: a script for fusing all Google Mock and Google Test
+   source files for easy deployment.
+ * Improved the Google Mock doctor to diagnose more diseases.
+ * Improved the Google Mock generator script.
+ * Compatibility fixes for Mac OS X and gcc.
+ * Bug fixes and implementation clean-ups.
+
+Changes for 1.1.0:
+
+ * New feature: ability to use Google Mock with any testing framework.
+ * New feature: macros for easily defining new matchers
+ * New feature: macros for easily defining new actions.
+ * New feature: more container matchers.
+ * New feature: actions for accessing function arguments and throwing
+   exceptions.
+ * Improved the Google Mock doctor script for diagnosing compiler errors.
+ * Bug fixes and implementation clean-ups.
+
+Changes for 1.0.0:
+
+ * Initial Open Source release of Google Mock
diff --git a/extern/gmock/CONTRIBUTORS b/extern/gmock/CONTRIBUTORS
new file mode 100644
index 0000000000000000000000000000000000000000..6e9ae362b60c384c743cd654903ec287fb6a270c
--- /dev/null
+++ b/extern/gmock/CONTRIBUTORS
@@ -0,0 +1,40 @@
+# This file contains a list of people who've made non-trivial
+# contribution to the Google C++ Mocking Framework project.  People
+# who commit code to the project are encouraged to add their names
+# here.  Please keep the list sorted by first names.
+
+Benoit Sigoure <tsuna@google.com>
+Bogdan Piloca <boo@google.com>
+Chandler Carruth <chandlerc@google.com>
+Dave MacLachlan <dmaclach@gmail.com>
+David Anderson <danderson@google.com>
+Dean Sturtevant
+Gene Volovich <gv@cite.com>
+Hal Burch <gmock@hburch.com>
+Jeffrey Yasskin <jyasskin@google.com>
+Jim Keller <jimkeller@google.com>
+Joe Walnes <joe@truemesh.com>
+Jon Wray <jwray@google.com>
+Keir Mierle <mierle@gmail.com>
+Keith Ray <keith.ray@gmail.com>
+Kostya Serebryany <kcc@google.com>
+Lev Makhlis
+Manuel Klimek <klimek@google.com>
+Mario Tanev <radix@google.com>
+Mark Paskin
+Markus Heule <markus.heule@gmail.com>
+Matthew Simmons <simmonmt@acm.org>
+Mike Bland <mbland@google.com>
+Neal Norwitz <nnorwitz@gmail.com>
+Nermin Ozkiranartli <nermin@google.com>
+Owen Carlsen <ocarlsen@google.com>
+Paneendra Ba <paneendra@google.com>
+Paul Menage <menage@google.com>
+Piotr Kaminski <piotrk@google.com>
+Russ Rufer <russ@pentad.com>
+Sverre Sundsdal <sundsdal@gmail.com>
+Takeshi Yoshino <tyoshino@google.com>
+Vadim Berman <vadimb@google.com>
+Vlad Losev <vladl@google.com>
+Wolfgang Klier <wklier@google.com>
+Zhanyong Wan <wan@google.com>
diff --git a/extern/gmock/README.blender b/extern/gmock/README.blender
index 41dda92c19c3f5bbe765c211ad6aa4bfa47c9169..6165bd6f7174a62500c0d67b5a2edd3101bc5ea8 100644
--- a/extern/gmock/README.blender
+++ b/extern/gmock/README.blender
@@ -1,7 +1,7 @@
 Project: Google C++ Testing Framework
 URL: https://github.com/google/googletest
 License: New BSD
-Upstream version: 1.7.0 (ec44c6c)
+Upstream version: 1.8.0 (ec44c6c1675)
 Local modifications:
 
 None.
diff --git a/extern/gtest/README.blender b/extern/gtest/README.blender
index 41dda92c19c3f5bbe765c211ad6aa4bfa47c9169..6165bd6f7174a62500c0d67b5a2edd3101bc5ea8 100644
--- a/extern/gtest/README.blender
+++ b/extern/gtest/README.blender
@@ -1,7 +1,7 @@
 Project: Google C++ Testing Framework
 URL: https://github.com/google/googletest
 License: New BSD
-Upstream version: 1.7.0 (ec44c6c)
+Upstream version: 1.8.0 (ec44c6c1675)
 Local modifications:
 
 None.
diff --git a/extern/gtest/README.md b/extern/gtest/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..22df99bd4b0bef0365b7f8b0894adf3e2718e6ec
--- /dev/null
+++ b/extern/gtest/README.md
@@ -0,0 +1,138 @@
+
+# Google Test #
+
+[![Build Status](https://travis-ci.org/google/googletest.svg?branch=master)](https://travis-ci.org/google/googletest)
+
+Welcome to **Google Test**, Google's C++ test framework!
+
+This repository is a merger of the formerly separate GoogleTest and
+GoogleMock projects. These were so closely related that it makes sense to
+maintain and release them together.
+
+Please see the project page above for more information as well as the
+mailing list for questions, discussions, and development.  There is
+also an IRC channel on OFTC (irc.oftc.net) #gtest available.  Please
+join us!
+
+**Google Mock** is an extension to Google Test for writing and using C++ mock
+classes.  See the separate [Google Mock documentation](googlemock/README.md).
+
+More detailed documentation for googletest (including build instructions) are
+in its interior [googletest/README.md](googletest/README.md) file.
+
+## Features ##
+
+  * An [XUnit](https://en.wikipedia.org/wiki/XUnit) test framework.
+  * Test discovery.
+  * A rich set of assertions.
+  * User-defined assertions.
+  * Death tests.
+  * Fatal and non-fatal failures.
+  * Value-parameterized tests.
+  * Type-parameterized tests.
+  * Various options for running the tests.
+  * XML test report generation.
+
+## Platforms ##
+
+Google test has been used on a variety of platforms:
+
+  * Linux
+  * Mac OS X
+  * Windows
+  * Cygwin
+  * MinGW
+  * Windows Mobile
+  * Symbian
+
+## Who Is Using Google Test? ##
+
+In addition to many internal projects at Google, Google Test is also used by
+the following notable projects:
+
+  * The [Chromium projects](http://www.chromium.org/) (behind the Chrome
+    browser and Chrome OS).
+  * The [LLVM](http://llvm.org/) compiler.
+  * [Protocol Buffers](https://github.com/google/protobuf), Google's data
+    interchange format.
+  * The [OpenCV](http://opencv.org/) computer vision library.
+
+## Related Open Source Projects ##
+
+[Google Test UI](https://github.com/ospector/gtest-gbar) is test runner that runs
+your test binary, allows you to track its progress via a progress bar, and
+displays a list of test failures. Clicking on one shows failure text. Google
+Test UI is written in C#.
+
+[GTest TAP Listener](https://github.com/kinow/gtest-tap-listener) is an event
+listener for Google Test that implements the
+[TAP protocol](https://en.wikipedia.org/wiki/Test_Anything_Protocol) for test
+result output. If your test runner understands TAP, you may find it useful.
+
+## Requirements ##
+
+Google Test is designed to have fairly minimal requirements to build
+and use with your projects, but there are some.  Currently, we support
+Linux, Windows, Mac OS X, and Cygwin.  We will also make our best
+effort to support other platforms (e.g. Solaris, AIX, and z/OS).
+However, since core members of the Google Test project have no access
+to these platforms, Google Test may have outstanding issues there.  If
+you notice any problems on your platform, please notify
+<googletestframework@googlegroups.com>. Patches for fixing them are
+even more welcome!
+
+### Linux Requirements ###
+
+These are the base requirements to build and use Google Test from a source
+package (as described below):
+
+  * GNU-compatible Make or gmake
+  * POSIX-standard shell
+  * POSIX(-2) Regular Expressions (regex.h)
+  * A C++98-standard-compliant compiler
+
+### Windows Requirements ###
+
+  * Microsoft Visual C++ v7.1 or newer
+
+### Cygwin Requirements ###
+
+  * Cygwin v1.5.25-14 or newer
+
+### Mac OS X Requirements ###
+
+  * Mac OS X v10.4 Tiger or newer
+  * XCode Developer Tools
+
+### Requirements for Contributors ###
+
+We welcome patches.  If you plan to contribute a patch, you need to
+build Google Test and its own tests from a git checkout (described
+below), which has further requirements:
+
+  * [Python](https://www.python.org/) v2.3 or newer (for running some of
+    the tests and re-generating certain source files from templates)
+  * [CMake](https://cmake.org/) v2.6.4 or newer
+
+## Regenerating Source Files ##
+
+Some of Google Test's source files are generated from templates (not
+in the C++ sense) using a script.
+For example, the
+file include/gtest/internal/gtest-type-util.h.pump is used to generate
+gtest-type-util.h in the same directory.
+
+You don't need to worry about regenerating the source files
+unless you need to modify them.  You would then modify the
+corresponding `.pump` files and run the '[pump.py](googletest/scripts/pump.py)'
+generator script.  See the [Pump Manual](googletest/docs/PumpManual.md).
+
+### Contributing Code ###
+
+We welcome patches.  Please read the
+[Developer's Guide](googletest/docs/DevGuide.md)
+for how you can contribute. In particular, make sure you have signed
+the Contributor License Agreement, or we won't be able to accept the
+patch.
+
+Happy testing!
diff --git a/extern/gtest/include/gtest/gtest-printers.h b/extern/gtest/include/gtest/gtest-printers.h
index 27a1edc37284427b1850dc19adaf2f92cce6ad98..8a33164cb38ab505962f75b157654aeee5459f3a 100644
--- a/extern/gtest/include/gtest/gtest-printers.h
+++ b/extern/gtest/include/gtest/gtest-printers.h
@@ -103,7 +103,7 @@
 #include "gtest/internal/gtest-port.h"
 #include "gtest/internal/gtest-internal.h"
 
-#if defined(GTEST_HAS_STD_TUPLE_) && GTEST_HAS_STD_TUPLE_
+#if GTEST_HAS_STD_TUPLE_
 # include <tuple>
 #endif
 
@@ -581,7 +581,7 @@ inline void PrintTo(const ::std::wstring& s, ::std::ostream* os) {
 }
 #endif  // GTEST_HAS_STD_WSTRING
 
-#if GTEST_HAS_TR1_TUPLE || (defined(GTEST_HAS_STD_TUPLE_) && GTEST_HAS_STD_TUPLE_)
+#if GTEST_HAS_TR1_TUPLE || GTEST_HAS_STD_TUPLE_
 // Helper function for printing a tuple.  T must be instantiated with
 // a tuple type.
 template <typename T>
@@ -664,7 +664,7 @@ void PrintTo(
 }
 #endif  // GTEST_HAS_TR1_TUPLE
 
-#if defined(GTEST_HAS_STD_TUPLE_) && GTEST_HAS_STD_TUPLE_
+#if GTEST_HAS_STD_TUPLE_
 template <typename... Types>
 void PrintTo(const ::std::tuple<Types...>& t, ::std::ostream* os) {
   PrintTupleTo(t, os);
@@ -888,7 +888,7 @@ template <typename TupleT>
 const size_t TuplePolicy<TupleT>::tuple_size;
 #endif  // GTEST_HAS_TR1_TUPLE
 
-#if defined(GTEST_HAS_STD_TUPLE_) && GTEST_HAS_STD_TUPLE_
+#if GTEST_HAS_STD_TUPLE_
 template <typename... Types>
 struct TuplePolicy< ::std::tuple<Types...> > {
   typedef ::std::tuple<Types...> Tuple;
diff --git a/extern/gtest/include/gtest/gtest.h b/extern/gtest/include/gtest/gtest.h
index 18d3ed4b469fe3add53446f84e1eba336ffe7786..f846c5bd66964977fa91eff90597ddd0bdcdce8c 100644
--- a/extern/gtest/include/gtest/gtest.h
+++ b/extern/gtest/include/gtest/gtest.h
@@ -1818,7 +1818,7 @@ class TestWithParam : public Test, public WithParamInterface<T> {
 
 // Define this macro to 1 to omit the definition of FAIL(), which is a
 // generic name and clashes with some other libraries.
-#if !defined(GTEST_DONT_DEFINE_FAIL) || !GTEST_DONT_DEFINE_FAIL
+#if !GTEST_DONT_DEFINE_FAIL
 # define FAIL() GTEST_FAIL()
 #endif
 
@@ -1827,7 +1827,7 @@ class TestWithParam : public Test, public WithParamInterface<T> {
 
 // Define this macro to 1 to omit the definition of SUCCEED(), which
 // is a generic name and clashes with some other libraries.
-#if !defined(GTEST_DONT_DEFINE_SUCCEED) || !GTEST_DONT_DEFINE_SUCCEED
+#if !GTEST_DONT_DEFINE_SUCCEED
 # define SUCCEED() GTEST_SUCCEED()
 #endif
 
@@ -1952,27 +1952,27 @@ class TestWithParam : public Test, public WithParamInterface<T> {
 // Define macro GTEST_DONT_DEFINE_ASSERT_XY to 1 to omit the definition of
 // ASSERT_XY(), which clashes with some users' own code.
 
-#if !defined(GTEST_DONT_DEFINE_ASSERT_EQ) || !GTEST_DONT_DEFINE_ASSERT_EQ
+#if !GTEST_DONT_DEFINE_ASSERT_EQ
 # define ASSERT_EQ(val1, val2) GTEST_ASSERT_EQ(val1, val2)
 #endif
 
-#if !defined(GTEST_DONT_DEFINE_ASSERT_NE) || !GTEST_DONT_DEFINE_ASSERT_NE
+#if !GTEST_DONT_DEFINE_ASSERT_NE
 # define ASSERT_NE(val1, val2) GTEST_ASSERT_NE(val1, val2)
 #endif
 
-#if !defined(GTEST_DONT_DEFINE_ASSERT_LE) || !GTEST_DONT_DEFINE_ASSERT_LE
+#if !GTEST_DONT_DEFINE_ASSERT_LE
 # define ASSERT_LE(val1, val2) GTEST_ASSERT_LE(val1, val2)
 #endif
 
-#if !defined(GTEST_DONT_DEFINE_ASSERT_LT) || !GTEST_DONT_DEFINE_ASSERT_LT
+#if !GTEST_DONT_DEFINE_ASSERT_LT
 # define ASSERT_LT(val1, val2) GTEST_ASSERT_LT(val1, val2)
 #endif
 
-#if !defined(GTEST_DONT_DEFINE_ASSERT_GE) || !GTEST_DONT_DEFINE_ASSERT_GE
+#if !GTEST_DONT_DEFINE_ASSERT_GE
 # define ASSERT_GE(val1, val2) GTEST_ASSERT_GE(val1, val2)
 #endif
 
-#if !defined(GTEST_DONT_DEFINE_ASSERT_GT) || !GTEST_DONT_DEFINE_ASSERT_GT
+#if !GTEST_DONT_DEFINE_ASSERT_GT
 # define ASSERT_GT(val1, val2) GTEST_ASSERT_GT(val1, val2)
 #endif
 
@@ -2183,7 +2183,7 @@ bool StaticAssertTypeEq() {
 
 // Define this macro to 1 to omit the definition of TEST(), which
 // is a generic name and clashes with some other libraries.
-#if !defined(GTEST_DONT_DEFINE_TEST) || !GTEST_DONT_DEFINE_TEST
+#if !GTEST_DONT_DEFINE_TEST
 # define TEST(test_case_name, test_name) GTEST_TEST(test_case_name, test_name)
 #endif
 
diff --git a/extern/gtest/include/gtest/internal/gtest-internal.h b/extern/gtest/include/gtest/internal/gtest-internal.h
index ca15a2f3a781122d67b421a9fb90ba5da2c02ebd..ebd1cf615de07d9b5ab0e60c71008261efe54213 100644
--- a/extern/gtest/include/gtest/internal/gtest-internal.h
+++ b/extern/gtest/include/gtest/internal/gtest-internal.h
@@ -60,10 +60,6 @@
 #include <string>
 #include <vector>
 
-#if (__cplusplus > 199711L) || (defined(_MSC_VER) && _MSC_VER >= 1800)
-# include <type_traits>
-#endif
-
 #include "gtest/gtest-message.h"
 #include "gtest/internal/gtest-string.h"
 #include "gtest/internal/gtest-filepath.h"
@@ -858,7 +854,6 @@ struct AddReference<T&> { typedef T& type; };  // NOLINT
 template <typename From, typename To>
 class ImplicitlyConvertible {
  private:
-#if !((__cplusplus > 199711L) || (defined(_MSC_VER) && _MSC_VER >= 1800))
   // We need the following helper functions only for their types.
   // They have no implementations.
 
@@ -879,7 +874,6 @@ class ImplicitlyConvertible {
   // implicitly converted to type To.
   static char Helper(To);
   static char (&Helper(...))[2];  // NOLINT
-#endif
 
   // We have to put the 'public' section after the 'private' section,
   // or MSVC refuses to compile the code.
@@ -889,8 +883,6 @@ class ImplicitlyConvertible {
   // instantiation.  The simplest workaround is to use its C++0x type traits
   // functions (C++Builder 2009 and above only).
   static const bool value = __is_convertible(From, To);
-#elif (__cplusplus > 199711L) || (defined(_MSC_VER) && _MSC_VER >= 1800)
-  static const bool value = std::is_convertible<From, To>::value;
 #else
   // MSVC warns about implicitly converting from double to int for
   // possible loss of data, so we need to temporarily disable the
diff --git a/extern/gtest/include/gtest/internal/gtest-port.h b/extern/gtest/include/gtest/internal/gtest-port.h
index ad2a43df17dc8b86c3143e8a1ea1ec626f38e610..0094ed5077e69a59afc3f535c9dcfc403b31fbbc 100644
--- a/extern/gtest/include/gtest/internal/gtest-port.h
+++ b/extern/gtest/include/gtest/internal/gtest-port.h
@@ -306,7 +306,7 @@
 //   GTEST_DISABLE_MSC_WARNINGS_PUSH_(4800 4385)
 //   /* code that triggers warnings C4800 and C4385 */
 //   GTEST_DISABLE_MSC_WARNINGS_POP_()
-#if defined(_MSC_VER) && _MSC_VER >= 1500
+#if _MSC_VER >= 1500
 # define GTEST_DISABLE_MSC_WARNINGS_PUSH_(warnings) \
     __pragma(warning(push))                        \
     __pragma(warning(disable: warnings))
@@ -323,7 +323,7 @@
 // -std={c,gnu}++{0x,11} is passed.  The C++11 standard specifies a
 // value for __cplusplus, and recent versions of clang, gcc, and
 // probably other compilers set that too in C++11 mode.
-# if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L
+# if __GXX_EXPERIMENTAL_CXX0X__ || __cplusplus >= 201103L
 // Compiling in at least C++11 mode.
 #  define GTEST_LANG_CXX11 1
 # else
@@ -352,7 +352,7 @@
 #endif
 
 // Only use C++11 library features if the library provides them.
-#if defined(GTEST_STDLIB_CXX11) && GTEST_STDLIB_CXX11
+#if GTEST_STDLIB_CXX11
 # define GTEST_HAS_STD_BEGIN_AND_END_ 1
 # define GTEST_HAS_STD_FORWARD_LIST_ 1
 # define GTEST_HAS_STD_FUNCTION_ 1
@@ -387,37 +387,6 @@
 # endif
 #endif
 
-#ifndef GTEST_OS_WINDOWS
-#  define GTEST_OS_WINDOWS 0
-#endif
-#ifndef GTEST_OS_WINDOWS_MINGW
-#  define GTEST_OS_WINDOWS_MINGW 0
-#endif
-#ifndef GTEST_OS_WINDOWS_PHONE
-#  define GTEST_OS_WINDOWS_PHONE 0
-#endif
-#ifndef GTEST_OS_WINDOWS_MOBILE
-#  define GTEST_OS_WINDOWS_MOBILE 0
-#endif
-#ifndef GTEST_OS_WINDOWS_RT
-#  define GTEST_OS_WINDOWS_RT 0
-#endif
-#ifndef GTEST_OS_LINUX_ANDROID
-#  define GTEST_OS_LINUX_ANDROID 0
-#endif
-#ifndef GTEST_OS_QNX
-#  define GTEST_OS_QNX 0
-#endif
-#ifndef GTEST_OS_SYMBIAN
-#  define GTEST_OS_SYMBIAN 0
-#endif
-#ifndef GTEST_OS_CYGWIN
-#  define GTEST_OS_CYGWIN 0
-#endif
-#ifndef GTEST_OS_SOLARIS
-#  define GTEST_OS_SOLARIS 0
-#endif
-
 // Brings in definitions for functions used in the testing::internal::posix
 // namespace (read, write, close, chdir, isatty, stat). We do not currently
 // use them on Windows Mobile.
@@ -454,7 +423,7 @@ struct _RTL_CRITICAL_SECTION;
 # endif
 #endif
 
-#if defined(GTEST_USES_PCRE) && GTEST_USES_PCRE
+#if GTEST_USES_PCRE
 // The appropriate headers have already been included.
 
 #elif GTEST_HAS_POSIX_RE
@@ -647,7 +616,7 @@ struct _RTL_CRITICAL_SECTION;
 // Determines if hash_map/hash_set are available.
 // Only used for testing against those containers.
 #if !defined(GTEST_HAS_HASH_MAP_)
-# if defined(_MSC_VER)
+# if _MSC_VER
 #  define GTEST_HAS_HASH_MAP_ 1  // Indicates that hash_map is available.
 #  define GTEST_HAS_HASH_SET_ 1  // Indicates that hash_set is available.
 # endif  // _MSC_VER
@@ -691,8 +660,6 @@ struct _RTL_CRITICAL_SECTION;
 // can build with clang but need to use gcc4.2's libstdc++).
 # if GTEST_LANG_CXX11 && (!defined(__GLIBCXX__) || __GLIBCXX__ > 20110325)
 #  define GTEST_ENV_HAS_STD_TUPLE_ 1
-# else
-#  define GTEST_ENV_HAS_STD_TUPLE_ 0
 # endif
 
 # if GTEST_ENV_HAS_TR1_TUPLE_ || GTEST_ENV_HAS_STD_TUPLE_
@@ -706,7 +673,7 @@ struct _RTL_CRITICAL_SECTION;
 // To avoid conditional compilation everywhere, we make it
 // gtest-port.h's responsibility to #include the header implementing
 // tuple.
-#if defined(GTEST_HAS_STD_TUPLE_) && GTEST_HAS_STD_TUPLE_
+#if GTEST_HAS_STD_TUPLE_
 # include <tuple>  // IWYU pragma: export
 # define GTEST_TUPLE_NAMESPACE_ ::std
 #endif  // GTEST_HAS_STD_TUPLE_
@@ -947,7 +914,7 @@ using ::std::tuple_size;
 # endif
 
 #define GTEST_IS_THREADSAFE \
-    ((defined(GTEST_HAS_MUTEX_AND_THREAD_LOCAL_) && GTEST_HAS_MUTEX_AND_THREAD_LOCAL_) \
+    (GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ \
      || (GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT) \
      || GTEST_HAS_PTHREAD)
 
@@ -1343,7 +1310,7 @@ inline void FlushInfoLog() { fflush(NULL); }
     GTEST_LOG_(FATAL) << #posix_call << "failed with error " \
                       << gtest_error
 
-#if defined(GTEST_HAS_STD_MOVE_) && GTEST_HAS_STD_MOVE_
+#if GTEST_HAS_STD_MOVE_
 using std::move;
 #else  // GTEST_HAS_STD_MOVE_
 template <typename T>
@@ -1427,7 +1394,7 @@ Derived* CheckedDowncastToActualType(Base* base) {
   GTEST_CHECK_(typeid(*base) == typeid(Derived));
 #endif
 
-#if defined(GTEST_HAS_DOWNCAST_) && GTEST_HAS_DOWNCAST_
+#if GTEST_HAS_DOWNCAST_
   return ::down_cast<Derived*>(base);
 #elif GTEST_HAS_RTTI
   return dynamic_cast<Derived*>(base);  // NOLINT
@@ -1487,7 +1454,7 @@ inline void SleepMilliseconds(int n) {
 }
 # endif  // GTEST_HAS_PTHREAD
 
-# if defined(GTEST_HAS_NOTIFICATION_) && GTEST_HAS_NOTIFICATION_
+# if GTEST_HAS_NOTIFICATION_
 // Notification has already been imported into the namespace.
 // Nothing to do here.
 
@@ -1670,7 +1637,7 @@ class ThreadWithParam : public ThreadWithParamBase {
 # endif  // !GTEST_OS_WINDOWS && GTEST_HAS_PTHREAD ||
          // GTEST_HAS_MUTEX_AND_THREAD_LOCAL_
 
-# if defined(GTEST_HAS_MUTEX_AND_THREAD_LOCAL_) && GTEST_HAS_MUTEX_AND_THREAD_LOCAL_
+# if GTEST_HAS_MUTEX_AND_THREAD_LOCAL_
 // Mutex and ThreadLocal have already been imported into the namespace.
 // Nothing to do here.
 
@@ -2450,7 +2417,7 @@ inline void Abort() { abort(); }
 // MSVC-based platforms.  We map the GTEST_SNPRINTF_ macro to the appropriate
 // function in order to achieve that.  We use macro definition here because
 // snprintf is a variadic function.
-#if defined(_MSC_VER) && _MSC_VER >= 1400 && !GTEST_OS_WINDOWS_MOBILE
+#if _MSC_VER >= 1400 && !GTEST_OS_WINDOWS_MOBILE
 // MSVC 2005 and above support variadic macros.
 # define GTEST_SNPRINTF_(buffer, size, format, ...) \
      _snprintf_s(buffer, size, size, format, __VA_ARGS__)
diff --git a/intern/CMakeLists.txt b/intern/CMakeLists.txt
index af3b9296077143af1a7023585270d5aa310c74c3..4b3ccfc808a44288108b33f6753f7533d22ef158 100644
--- a/intern/CMakeLists.txt
+++ b/intern/CMakeLists.txt
@@ -24,6 +24,7 @@
 # ***** END GPL LICENSE BLOCK *****
 
 # add_subdirectory(atomic)  # header only
+add_subdirectory(clog)
 add_subdirectory(string)
 add_subdirectory(ghost)
 add_subdirectory(guardedalloc)
diff --git a/intern/clog/CLG_log.h b/intern/clog/CLG_log.h
new file mode 100644
index 0000000000000000000000000000000000000000..8afa9edd75b487ba189caa579382f2b6efefa44c
--- /dev/null
+++ b/intern/clog/CLG_log.h
@@ -0,0 +1,211 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __CLOG_H__
+#define __CLOG_H__
+
+/** \file clog/CLG_log.h
+ *  \ingroup clog
+ *
+ * C Logging Library (clog)
+ * ========================
+ *
+ * Usage
+ * -----
+ *
+ * - `CLG_LOGREF_DECLARE_GLOBAL` macro to declare #CLG_LogRef pointers.
+ * - `CLOG_` prefixed macros for logging.
+ *
+ * Identifiers
+ * -----------
+ *
+ * #CLG_LogRef holds an identifier which defines the category of the logger.
+ *
+ * You can define and use identifiers as needed, logging will lazily initialize them.
+ *
+ * By convention lower case dot separated identifiers are used, eg:
+ * `module.sub_module`, this allows filtering by `module.*`,
+ * see #CLG_type_filter_include, #CLG_type_filter_exclude
+ *
+ * There is currently no functionality to remove a category once it's created.
+ *
+ * Severity
+ * --------
+ *
+ * - `INFO`: Simply log events, uses verbosity levels to control how much information to show.
+ * - `WARN`: General warnings (which aren't necessary to show to users).
+ * - `ERROR`: An error we can recover from, should not happen.
+ * - `FATAL`: Similar to assert. This logs the message, then a stack trace and abort.
+ *
+ *
+ * Verbosity Level
+ * ---------------
+ *
+ * Usage:
+ *
+ * - 0: Always show (used for warnings, errors).
+ *   Should never get in the way or become annoying.
+ *
+ * - 1: Top level module actions (eg: load a file, create a new window .. etc).
+ *
+ * - 2: Actions within a module (steps which compose an action, but don't flood output).
+ *   Running a tool, full data recalculation.
+ *
+ * - 3: Detailed actions which may be of interest when debugging internal logic of a module
+ *   These *may* flood the log with details.
+ *
+ * - 4+: May be used for more details than 3, should be avoided but not prevented.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#ifdef __GNUC__
+#  define _CLOG_ATTR_NONNULL(args ...) __attribute__((nonnull(args)))
+#else
+#  define _CLOG_ATTR_NONNULL(...)
+#endif
+
+#ifdef __GNUC__
+#  define _CLOG_ATTR_PRINTF_FORMAT(format_param, dots_param) __attribute__((format(printf, format_param, dots_param)))
+#else
+#  define _CLOG_ATTR_PRINTF_FORMAT(format_param, dots_param)
+#endif
+
+#if defined(_MSC_VER) && !defined(__func__)
+#  define __func__MSVC
+#  define __func__ __FUNCTION__
+#endif
+
+#define STRINGIFY_ARG(x) "" #x
+#define STRINGIFY_APPEND(a, b) "" a #b
+#define STRINGIFY(x) STRINGIFY_APPEND("", x)
+
+struct CLogContext;
+
+/* Don't typedef enums. */
+enum CLG_LogFlag {
+	CLG_FLAG_USE = (1 << 0),
+};
+
+enum CLG_Severity {
+	CLG_SEVERITY_INFO = 0,
+	CLG_SEVERITY_WARN,
+	CLG_SEVERITY_ERROR,
+	CLG_SEVERITY_FATAL,
+};
+#define CLG_SEVERITY_LEN (CLG_SEVERITY_FATAL + 1)
+
+/* Each logger ID has one of these. */
+typedef struct CLG_LogType {
+	struct CLG_LogType *next;
+	char identifier[64];
+	/** FILE output. */
+	struct CLogContext *ctx;
+	/** Control behavior. */
+	int level;
+	enum CLG_LogFlag flag;
+} CLG_LogType;
+
+typedef struct CLG_LogRef {
+	const char *identifier;
+	CLG_LogType *type;
+} CLG_LogRef;
+
+void CLG_log_str(
+        CLG_LogType *lg, enum CLG_Severity severity, const char *file_line, const char *fn,
+        const char *message)
+	_CLOG_ATTR_NONNULL(1, 3, 4, 5);
+void CLG_logf(
+        CLG_LogType *lg, enum CLG_Severity severity, const char *file_line, const char *fn,
+        const char *format, ...)
+	_CLOG_ATTR_NONNULL(1, 3, 4, 5) _CLOG_ATTR_PRINTF_FORMAT(5, 6);
+
+/* Main initializer and distructor (per session, not logger). */
+void CLG_init(void);
+void CLG_exit(void);
+
+void CLG_output_set(void *file_handle);
+void CLG_output_use_basename_set(int value);
+void CLG_fatal_fn_set(void (*fatal_fn)(void *file_handle));
+
+void CLG_type_filter_include(const char *type_filter, int type_filter_len);
+void CLG_type_filter_exclude(const char *type_filter, int type_filter_len);
+
+void CLG_logref_init(CLG_LogRef *clg_ref);
+
+/** Declare outside function, declare as extern in header. */
+#define CLG_LOGREF_DECLARE_GLOBAL(var, id) \
+	static CLG_LogRef _static_ ## var = {id}; \
+	CLG_LogRef *var = &_static_ ## var
+
+/** Initialize struct once. */
+#define CLOG_ENSURE(clg_ref) \
+	((clg_ref)->type ? (clg_ref)->type : (CLG_logref_init(clg_ref), (clg_ref)->type))
+
+#define CLOG_AT_SEVERITY(clg_ref, severity, verbose_level, ...) { \
+	CLG_LogType *_lg_ty = CLOG_ENSURE(clg_ref); \
+	if (((_lg_ty->flag & CLG_FLAG_USE) && (_lg_ty->level >= verbose_level)) || (severity >= CLG_SEVERITY_WARN)) { \
+		CLG_logf(_lg_ty, severity, __FILE__ ":" STRINGIFY(__LINE__), __func__, __VA_ARGS__); \
+	} \
+} ((void)0)
+
+#define CLOG_STR_AT_SEVERITY(clg_ref, severity, verbose_level, str) { \
+	CLG_LogType *_lg_ty = CLOG_ENSURE(clg_ref); \
+	if (((_lg_ty->flag & CLG_FLAG_USE) && (_lg_ty->level >= verbose_level)) || (severity >= CLG_SEVERITY_WARN)) { \
+		CLG_log_str(lg, severity, __FILE__ ":" STRINGIFY(__LINE__), __func__, str); \
+	} \
+} ((void)0)
+
+#define CLOG_STR_AT_SEVERITY_N(clg_ref, severity, verbose_level, str) { \
+	CLG_LogType *_lg_ty = CLOG_ENSURE(clg_ref); \
+	if (((_lg_ty->flag & CLG_FLAG_USE) && (_lg_ty->level >= verbose_level)) || (severity >= CLG_SEVERITY_WARN)) { \
+		const char *_str = str; \
+		CLG_log_str(_lg_ty, severity, __FILE__ ":" STRINGIFY(__LINE__), __func__, _str); \
+		MEM_freeN((void *)_str); \
+	} \
+} ((void)0)
+
+#define CLOG_INFO(clg_ref, level, ...) CLOG_AT_SEVERITY(clg_ref, CLG_SEVERITY_INFO, level, __VA_ARGS__)
+#define CLOG_WARN(clg_ref, ...)        CLOG_AT_SEVERITY(clg_ref, CLG_SEVERITY_WARN, 0, __VA_ARGS__)
+#define CLOG_ERROR(clg_ref, ...)       CLOG_AT_SEVERITY(clg_ref, CLG_SEVERITY_ERROR, 0, __VA_ARGS__)
+#define CLOG_FATAL(clg_ref, ...)       CLOG_AT_SEVERITY(clg_ref, CLG_SEVERITY_FATAL, 0, __VA_ARGS__)
+
+#define CLOG_STR_INFO(clg_ref, level, ...) CLOG_STR_AT_SEVERITY(clg_ref, CLG_SEVERITY_INFO, level, __VA_ARGS__)
+#define CLOG_STR_WARN(clg_ref, ...)        CLOG_STR_AT_SEVERITY(clg_ref, CLG_SEVERITY_WARN, 0, __VA_ARGS__)
+#define CLOG_STR_ERROR(clg_ref, ...)       CLOG_STR_AT_SEVERITY(clg_ref, CLG_SEVERITY_ERROR, 0, __VA_ARGS__)
+#define CLOG_STR_FATAL(clg_ref, ...)       CLOG_STR_AT_SEVERITY(clg_ref, CLG_SEVERITY_FATAL, 0, __VA_ARGS__)
+
+/* Allocated string which is immediately freed. */
+#define CLOG_STR_INFO_N(clg_ref, level, ...) CLOG_STR_AT_SEVERITY_N(clg_ref, CLG_SEVERITY_INFO, level, __VA_ARGS__)
+#define CLOG_STR_WARN_N(clg_ref, ...)        CLOG_STR_AT_SEVERITY_N(clg_ref, CLG_SEVERITY_WARN, 0, __VA_ARGS__)
+#define CLOG_STR_ERROR_N(clg_ref, ...)       CLOG_STR_AT_SEVERITY_N(clg_ref, CLG_SEVERITY_ERROR, 0, __VA_ARGS__)
+#define CLOG_STR_FATAL_N(clg_ref, ...)       CLOG_STR_AT_SEVERITY_N(clg_ref, CLG_SEVERITY_FATAL, 0, __VA_ARGS__)
+
+#ifdef __func__MSVC
+#  undef __func__MSVC
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CLOG_H__ */
diff --git a/intern/clog/CMakeLists.txt b/intern/clog/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..8bf7b66f7ec40f5ffb414bf78f8f243327793075
--- /dev/null
+++ b/intern/clog/CMakeLists.txt
@@ -0,0 +1,34 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ***** END GPL LICENSE BLOCK *****
+
+set(INC
+	.
+	../guardedalloc
+)
+
+set(INC_SYS
+
+)
+
+set(SRC
+	clog.c
+
+	CLG_log.h
+)
+
+blender_add_lib(bf_intern_clog "${SRC}" "${INC}" "${INC_SYS}")
diff --git a/intern/clog/clog.c b/intern/clog/clog.c
new file mode 100644
index 0000000000000000000000000000000000000000..dfbd34d341ac163e6137b6624e68a14532ab1bed
--- /dev/null
+++ b/intern/clog/clog.c
@@ -0,0 +1,583 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file clog/clog.c
+ *  \ingroup clog
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <assert.h>
+
+/* For 'isatty' to check for color. */
+#if defined(__unix__)
+#  include <unistd.h>
+#endif
+
+/* Only other dependency (could use regular malloc too). */
+#include "MEM_guardedalloc.h"
+
+/* own include. */
+#include "CLG_log.h"
+
+/* Local utility defines */
+#define STREQ(a, b) (strcmp(a, b) == 0)
+#define STREQLEN(a, b, n) (strncmp(a, b, n) == 0)
+
+#ifdef _WIN32
+#  define PATHSEP_CHAR '\\'
+#else
+#  define PATHSEP_CHAR '/'
+#endif
+
+/* -------------------------------------------------------------------- */
+/** \name Internal Types
+ * \{ */
+
+typedef struct CLG_IDFilter {
+	struct CLG_IDFilter *next;
+	/** Over alloc. */
+	char match[0];
+} CLG_IDFilter;
+
+typedef struct CLogContext {
+	/** Single linked list of types.  */
+	CLG_LogType *types;
+	/* exclude, include filters.  */
+	CLG_IDFilter *filters[2];
+	bool use_color;
+	bool use_basename;
+
+	/** Borrowed, not owned. */
+	FILE *output;
+
+	/** For new types. */
+	struct {
+		int level;
+	} default_type;
+
+	struct {
+		void (*fatal_fn)(void *file_handle);
+	} callbacks;
+} CLogContext;
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Mini Buffer Functionality
+ *
+ * Use so we can do a single call to write.
+ * \{ */
+
+#define CLOG_BUF_LEN_INIT 512
+
+typedef struct CLogStringBuf {
+	char *data;
+	uint  len;
+	uint  len_alloc;
+	bool is_alloc;
+} CLogStringBuf;
+
+static void clg_str_init(CLogStringBuf *cstr, char *buf_stack, uint buf_stack_len)
+{
+	cstr->data = buf_stack;
+	cstr->len_alloc = buf_stack_len;
+	cstr->len = 0;
+	cstr->is_alloc = false;
+}
+
+static void clg_str_free(CLogStringBuf *cstr)
+{
+	if (cstr->is_alloc) {
+		MEM_freeN(cstr->data);
+	}
+}
+
+static void clg_str_reserve(CLogStringBuf *cstr, const uint len)
+{
+	if (len > cstr->len_alloc) {
+		cstr->len_alloc *= 2;
+		if (len > cstr->len_alloc) {
+			cstr->len_alloc = len;
+		}
+
+		if (cstr->is_alloc) {
+			cstr->data = MEM_reallocN(cstr->data, cstr->len_alloc);
+		}
+		else {
+			/* Copy the static buffer. */
+			char *data = MEM_mallocN(cstr->len_alloc, __func__);
+			memcpy(data, cstr->data, cstr->len);
+			cstr->data = data;
+			cstr->is_alloc = true;
+		}
+		cstr->len_alloc = len;
+	}
+}
+
+static void clg_str_append_with_len(CLogStringBuf *cstr, const char *str, const uint len)
+{
+	uint len_next = cstr->len + len;
+	clg_str_reserve(cstr, len_next);
+	char *str_dst = cstr->data + cstr->len;
+	memcpy(str_dst, str, len);
+#if 0 /* no need. */
+	str_dst[len] = '\0';
+#endif
+	cstr->len = len_next;
+}
+
+static void clg_str_append(CLogStringBuf *cstr, const char *str)
+{
+	clg_str_append_with_len(cstr, str, strlen(str));
+}
+
+static void clg_str_vappendf(CLogStringBuf *cstr, const char *fmt, va_list args)
+{
+	/* Use limit because windows may use '-1' for a formatting error. */
+	const uint len_max = 65535;
+	uint len_avail = (cstr->len_alloc - cstr->len);
+	if (len_avail == 0) {
+		len_avail = CLOG_BUF_LEN_INIT;
+		clg_str_reserve(cstr, len_avail);
+	}
+	while (true) {
+		va_list args_cpy;
+		va_copy(args_cpy, args);
+		int retval = vsnprintf(cstr->data + cstr->len, len_avail, fmt, args_cpy);
+		va_end(args_cpy);
+		if (retval != -1) {
+			cstr->len += retval;
+			break;
+		}
+		else {
+			len_avail *= 2;
+			if (len_avail >= len_max) {
+				break;
+			}
+			clg_str_reserve(cstr, len_avail);
+		}
+	}
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Internal Utilities
+ * \{ */
+
+enum eCLogColor {
+	COLOR_DEFAULT,
+	COLOR_RED,
+	COLOR_GREEN,
+	COLOR_YELLOW,
+
+	COLOR_RESET,
+};
+#define COLOR_LEN (COLOR_RESET + 1)
+
+static const char *clg_color_table[COLOR_LEN] = {NULL};
+
+static void clg_color_table_init(bool use_color)
+{
+	for (int i = 0; i < COLOR_LEN; i++) {
+		clg_color_table[i] = "";
+	}
+	if (use_color) {
+#ifdef _WIN32
+		/* TODO */
+#else
+		clg_color_table[COLOR_DEFAULT]      = "\033[1;37m";
+		clg_color_table[COLOR_RED]          = "\033[1;31m";
+		clg_color_table[COLOR_GREEN]        = "\033[1;32m";
+		clg_color_table[COLOR_YELLOW]       = "\033[1;33m";
+		clg_color_table[COLOR_RESET]        = "\033[0m";
+#endif
+	}
+}
+
+static const char *clg_severity_str[CLG_SEVERITY_LEN] = {
+	[CLG_SEVERITY_INFO] =       "INFO",
+	[CLG_SEVERITY_WARN] =       "WARN",
+	[CLG_SEVERITY_ERROR] =      "ERROR",
+	[CLG_SEVERITY_FATAL] =      "FATAL",
+};
+
+static const char *clg_severity_as_text(enum CLG_Severity severity)
+{
+	bool ok = (unsigned int)severity < CLG_SEVERITY_LEN;
+	assert(ok);
+	if (ok) {
+		return clg_severity_str[severity];
+	}
+	else {
+		return "INVALID_SEVERITY";
+	}
+}
+
+static enum eCLogColor clg_severity_to_color(enum CLG_Severity severity)
+{
+	assert((unsigned int)severity < CLG_SEVERITY_LEN);
+	enum eCLogColor color = COLOR_DEFAULT;
+	switch (severity) {
+		case CLG_SEVERITY_INFO:
+			color = COLOR_DEFAULT;
+			break;
+		case CLG_SEVERITY_WARN:
+			color = COLOR_YELLOW;
+			break;
+		case CLG_SEVERITY_ERROR:
+		case CLG_SEVERITY_FATAL:
+			color = COLOR_RED;
+			break;
+		default:
+			/* should never get here. */
+			assert(false);
+	}
+	return color;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Context Type Access
+ * \{ */
+
+/**
+ * Filter the indentifier based on very basic globbing.
+ * - `foo` exact match of `foo`.
+ * - `foo.bar` exact match for `foo.bar`
+ * - `foo.*` match for `foo` & `foo.bar` & `foo.bar.baz`
+ * - `*` matches everything.
+ */
+static bool clg_ctx_filter_check(CLogContext *ctx, const char *identifier)
+{
+	const int identifier_len = strlen(identifier);
+	for (uint i = 0; i < 2; i++) {
+		const CLG_IDFilter *flt = ctx->filters[i];
+		while (flt != NULL) {
+			const int len = strlen(flt->match);
+			if (STREQ(flt->match, "*") ||
+				((len == identifier_len) && (STREQ(identifier, flt->match))))
+			{
+				return (bool)i;
+			}
+			if ((len >= 2) && (STREQLEN(".*", &flt->match[len - 2], 2))) {
+				if (((identifier_len == len - 2) && STREQLEN(identifier, flt->match, len - 2)) ||
+					((identifier_len >= len - 1) && STREQLEN(identifier, flt->match, len - 1)))
+				{
+					return (bool)i;
+				}
+			}
+			flt = flt->next;
+		}
+	}
+	return false;
+}
+
+/**
+ * \note This should never be called per logging call.
+ * Searching is only to get an initial handle.
+ */
+static CLG_LogType *clg_ctx_type_find_by_name(CLogContext *ctx, const char *identifier)
+{
+	for (CLG_LogType *ty = ctx->types; ty; ty = ty->next) {
+		if (STREQ(identifier, ty->identifier)) {
+			return ty;
+		}
+	}
+	return NULL;
+}
+
+static CLG_LogType *clg_ctx_type_register(CLogContext *ctx, const char *identifier)
+{
+	assert(clg_ctx_type_find_by_name(ctx, identifier) == NULL);
+	CLG_LogType *ty = MEM_callocN(sizeof(*ty), __func__);
+	ty->next = ctx->types;
+	ctx->types = ty;
+	strncpy(ty->identifier, identifier, sizeof(ty->identifier) - 1);
+	ty->ctx = ctx;
+	ty->level = ctx->default_type.level;
+
+	if (clg_ctx_filter_check(ctx, ty->identifier)) {
+		ty->flag |= CLG_FLAG_USE;
+	}
+	return ty;
+}
+
+static void clg_ctx_fatal_action(CLogContext *ctx, FILE *file_handle)
+{
+	if (ctx->callbacks.fatal_fn != NULL) {
+		ctx->callbacks.fatal_fn(file_handle);
+	}
+	fflush(file_handle);
+	abort();
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Logging API
+ * \{ */
+
+static void write_severity(CLogStringBuf *cstr, enum CLG_Severity severity, bool use_color)
+{
+	assert((unsigned int)severity < CLG_SEVERITY_LEN);
+	if (use_color) {
+		enum eCLogColor color = clg_severity_to_color(severity);
+		clg_str_append(cstr, clg_color_table[color]);
+		clg_str_append(cstr, clg_severity_as_text(severity));
+		clg_str_append(cstr, clg_color_table[COLOR_RESET]);
+	}
+	else {
+		clg_str_append(cstr, clg_severity_as_text(severity));
+	}
+}
+
+static void write_type(CLogStringBuf *cstr, CLG_LogType *lg)
+{
+	clg_str_append(cstr, " (");
+	clg_str_append(cstr, lg->identifier);
+	clg_str_append(cstr, "): ");
+}
+
+static void write_file_line_fn(CLogStringBuf *cstr, const char *file_line, const char *fn, const bool use_basename)
+{
+	uint file_line_len = strlen(file_line);
+	if (use_basename) {
+		uint file_line_offset = file_line_len;
+		while (file_line_offset-- > 0) {
+			if (file_line[file_line_offset] == PATHSEP_CHAR) {
+				file_line_offset++;
+				break;
+			}
+		}
+		file_line += file_line_offset;
+		file_line_len -= file_line_offset;
+	}
+	clg_str_append_with_len(cstr, file_line, file_line_len);
+
+
+	clg_str_append(cstr, " ");
+	clg_str_append(cstr, fn);
+	clg_str_append(cstr, ": ");
+}
+
+void CLG_log_str(
+        CLG_LogType *lg, enum CLG_Severity severity, const char *file_line, const char *fn,
+        const char *message)
+{
+	CLogStringBuf cstr;
+	char cstr_stack_buf[CLOG_BUF_LEN_INIT];
+	clg_str_init(&cstr, cstr_stack_buf, sizeof(cstr_stack_buf));
+
+	write_severity(&cstr, severity, lg->ctx->use_color);
+	write_type(&cstr, lg);
+
+	{
+		write_file_line_fn(&cstr, file_line, fn, lg->ctx->use_basename);
+		clg_str_append(&cstr, message);
+	}
+	clg_str_append(&cstr, "\n");
+
+	/* could be optional */
+	fwrite(cstr.data, cstr.len, 1, lg->ctx->output);
+	fflush(lg->ctx->output);
+
+	clg_str_free(&cstr);
+
+	if (severity == CLG_SEVERITY_FATAL) {
+		clg_ctx_fatal_action(lg->ctx, lg->ctx->output);
+	}
+}
+
+void CLG_logf(
+        CLG_LogType *lg, enum CLG_Severity severity, const char *file_line, const char *fn,
+        const char *fmt, ...)
+{
+	CLogStringBuf cstr;
+	char cstr_stack_buf[CLOG_BUF_LEN_INIT];
+	clg_str_init(&cstr, cstr_stack_buf, sizeof(cstr_stack_buf));
+
+	write_severity(&cstr, severity, lg->ctx->use_color);
+	write_type(&cstr, lg);
+
+	{
+		write_file_line_fn(&cstr, file_line, fn, lg->ctx->use_basename);
+
+		va_list ap;
+		va_start(ap, fmt);
+		clg_str_vappendf(&cstr, fmt, ap);
+		va_end(ap);
+	}
+	clg_str_append(&cstr, "\n");
+
+	/* could be optional */
+	fwrite(cstr.data, cstr.len, 1, lg->ctx->output);
+	fflush(lg->ctx->output);
+
+	clg_str_free(&cstr);
+
+	if (severity == CLG_SEVERITY_FATAL) {
+		clg_ctx_fatal_action(lg->ctx, lg->ctx->output);
+	}
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Logging Context API
+ * \{ */
+
+static void CLG_ctx_output_set(CLogContext *ctx, void *file_handle)
+{
+	ctx->output = file_handle;
+#if defined(__unix__)
+	ctx->use_color = isatty(fileno(file_handle));
+#endif
+}
+
+static void CLG_ctx_output_use_basename_set(CLogContext *ctx, int value)
+{
+	ctx->use_basename = (bool)value;
+}
+
+/** Action on fatal severity. */
+static void CLG_ctx_fatal_fn_set(CLogContext *ctx, void (*fatal_fn)(void *file_handle))
+{
+	ctx->callbacks.fatal_fn = fatal_fn;
+}
+
+static void clg_ctx_type_filter_append(CLG_IDFilter **flt_list, const char *type_match, int type_match_len)
+{
+	if (type_match_len == 0) {
+		return;
+	}
+	CLG_IDFilter *flt = MEM_callocN(sizeof(*flt) + (type_match_len + 1), __func__);
+	flt->next = *flt_list;
+	*flt_list = flt;
+	memcpy(flt->match, type_match, type_match_len);
+	/* no need to null terminate since we calloc'd */
+}
+
+static void CLG_ctx_type_filter_exclude(CLogContext *ctx, const char *type_match, int type_match_len)
+{
+	clg_ctx_type_filter_append(&ctx->filters[0], type_match, type_match_len);
+}
+
+static void CLG_ctx_type_filter_include(CLogContext *ctx, const char *type_match, int type_match_len)
+{
+	clg_ctx_type_filter_append(&ctx->filters[1], type_match, type_match_len);
+}
+
+static CLogContext *CLG_ctx_init(void)
+{
+	CLogContext *ctx = MEM_callocN(sizeof(*ctx), __func__);
+	ctx->use_color = true;
+	ctx->default_type.level = 1;
+	CLG_ctx_output_set(ctx, stdout);
+
+	return ctx;
+}
+
+static void CLG_ctx_free(CLogContext *ctx)
+{
+	while (ctx->types != NULL) {
+		CLG_LogType *item = ctx->types;
+		ctx->types = item->next;
+		MEM_freeN(item);
+	}
+
+	for (uint i = 0; i < 2; i++) {
+		while (ctx->filters[i] != NULL) {
+			CLG_IDFilter *item = ctx->filters[i];
+			ctx->filters[i] = item->next;
+			MEM_freeN(item);
+		}
+	}
+	MEM_freeN(ctx);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Public Logging API
+ *
+ * Currently uses global context.
+ * \{ */
+
+/* We could support multiple at once, for now this seems not needed. */
+struct CLogContext *g_ctx = NULL;
+
+void CLG_init(void)
+{
+	g_ctx = CLG_ctx_init();
+
+	clg_color_table_init(g_ctx->use_color);
+}
+
+void CLG_exit(void)
+{
+	CLG_ctx_free(g_ctx);
+}
+
+void CLG_output_set(void *file_handle)
+{
+	CLG_ctx_output_set(g_ctx, file_handle);
+}
+
+void CLG_output_use_basename_set(int value)
+{
+	CLG_ctx_output_use_basename_set(g_ctx, value);
+}
+
+
+void CLG_fatal_fn_set(void (*fatal_fn)(void *file_handle))
+{
+	CLG_ctx_fatal_fn_set(g_ctx, fatal_fn);
+}
+
+void CLG_type_filter_exclude(const char *type_match, int type_match_len)
+{
+	CLG_ctx_type_filter_exclude(g_ctx, type_match, type_match_len);
+}
+
+void CLG_type_filter_include(const char *type_match, int type_match_len)
+{
+	CLG_ctx_type_filter_include(g_ctx, type_match, type_match_len);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Logging Reference API
+ * Use to avoid lookups each time.
+ * \{ */
+
+void CLG_logref_init(CLG_LogRef *clg_ref)
+{
+	assert(clg_ref->type == NULL);
+	CLG_LogType *clg_ty = clg_ctx_type_find_by_name(g_ctx, clg_ref->identifier);
+	clg_ref->type = clg_ty ? clg_ty : clg_ctx_type_register(g_ctx, clg_ref->identifier);
+}
+
+/** \} */
diff --git a/intern/cycles/CMakeLists.txt b/intern/cycles/CMakeLists.txt
index 9df1e91e2390d1f74de0c686472b674517bdfbe3..c3305ac3dd82effda4e8c066e75874d5d2afbdbb 100644
--- a/intern/cycles/CMakeLists.txt
+++ b/intern/cycles/CMakeLists.txt
@@ -104,6 +104,54 @@ elseif(CMAKE_COMPILER_IS_GNUCC OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
 	endif()
 
 	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CYCLES_KERNEL_FLAGS}")
+elseif(WIN32 AND CMAKE_CXX_COMPILER_ID MATCHES "Intel")
+	check_cxx_compiler_flag(/QxSSE2 CXX_HAS_SSE)
+	check_cxx_compiler_flag(/arch:AVX CXX_HAS_AVX)
+	check_cxx_compiler_flag(/QxCORE-AVX2 CXX_HAS_AVX2)
+
+	if(CXX_HAS_SSE)
+		set(CYCLES_SSE2_KERNEL_FLAGS "/QxSSE2")
+		set(CYCLES_SSE3_KERNEL_FLAGS "/QxSSSE3")
+		set(CYCLES_SSE41_KERNEL_FLAGS "/QxSSE4.1")
+
+		if(CXX_HAS_AVX)
+			set(CYCLES_AVX_KERNEL_FLAGS "/arch:AVX")
+		endif()
+
+		if(CXX_HAS_AVX2)
+			set(CYCLES_AVX2_KERNEL_FLAGS "/QxCORE-AVX2")
+		endif()
+	endif()
+elseif(CMAKE_CXX_COMPILER_ID MATCHES "Intel")
+	if(APPLE)
+		# ICC does not support SSE2 flag on MacOSX
+		check_cxx_compiler_flag(-xssse3 CXX_HAS_SSE)
+	else()
+		check_cxx_compiler_flag(-xsse2 CXX_HAS_SSE)
+	endif()
+
+	check_cxx_compiler_flag(-xavx CXX_HAS_AVX)
+	check_cxx_compiler_flag(-xcore-avx2 CXX_HAS_AVX2)
+
+	if(CXX_HAS_SSE)
+		if(APPLE)
+			# ICC does not support SSE2 flag on MacOSX
+			set(CYCLES_SSE2_KERNEL_FLAGS "-xssse3")
+		else()
+			set(CYCLES_SSE2_KERNEL_FLAGS "-xsse2")
+		endif()
+
+		set(CYCLES_SSE3_KERNEL_FLAGS "-xssse3")
+		set(CYCLES_SSE41_KERNEL_FLAGS "-xsse4.1")
+
+		if(CXX_HAS_AVX)
+			set(CYCLES_AVX_KERNEL_FLAGS "-xavx")
+		endif()
+
+		if(CXX_HAS_AVX2)
+			set(CYCLES_AVX2_KERNEL_FLAGS "-xcore-avx2")
+		endif()
+	endif()
 endif()
 
 if(CXX_HAS_SSE)
diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py
index 6e4b0373a7a02b730f6b9cca42c137660aaa297c..163131392e3d982e9220d440ae9c6b75daf42210 100644
--- a/intern/cycles/blender/addon/ui.py
+++ b/intern/cycles/blender/addon/ui.py
@@ -50,7 +50,7 @@ class CyclesButtonsPanel:
 
     @classmethod
     def poll(cls, context):
-        return context.scene.view_render.engine in cls.COMPAT_ENGINES
+        return context.engine in cls.COMPAT_ENGINES
 
 
 def get_device_type(context):
@@ -753,7 +753,7 @@ class CYCLES_PT_context_material(CyclesButtonsPanel, Panel):
                 col.operator("object.material_slot_move", icon='TRIA_UP', text="").direction = 'UP'
                 col.operator("object.material_slot_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
 
-            if context.workspace.object_mode == 'EDIT':
+            if ob.mode == 'EDIT':
                 row = layout.row(align=True)
                 row.operator("object.material_slot_assign", text="Assign")
                 row.operator("object.material_slot_select", text="Select")
diff --git a/intern/cycles/blender/blender_curves.cpp b/intern/cycles/blender/blender_curves.cpp
index 984442fb08c6e2e2c0ff06bd2726d7608a134227..ba936a3f369a7f268299e97f28f9b9607f9be509 100644
--- a/intern/cycles/blender/blender_curves.cpp
+++ b/intern/cycles/blender/blender_curves.cpp
@@ -903,8 +903,7 @@ void BlenderSync::sync_curves(BL::Depsgraph& b_depsgraph,
 	/* obtain general settings */
 	const bool use_curves = scene->curve_system_manager->use_curves;
 
-	/* TODO/OBMODE, make cycles mode aware. */
-	if(!(use_curves /* && b_ob.mode() != b_ob.mode_PARTICLE_EDIT */ )) {
+	if(!(use_curves && b_ob.mode() != b_ob.mode_PARTICLE_EDIT)) {
 		if(!motion)
 			mesh->compute_bounds();
 		return;
diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp
index be4214ede65787c7fea1311bd46369b89866e06d..d0df8e1800fa39731dc3507884bd9413dccf4119 100644
--- a/intern/cycles/blender/blender_object.cpp
+++ b/intern/cycles/blender/blender_object.cpp
@@ -437,6 +437,9 @@ Object *BlenderSync::sync_object(BL::Depsgraph& b_depsgraph,
 			object->dupli_generated = 0.5f*get_float3(b_dupli_iter->orco()) - make_float3(0.5f, 0.5f, 0.5f);
 			object->dupli_uv = get_float2(b_dupli_iter->uv());
 			object->random_id = b_dupli_iter->random_id();
+
+			/* Sync possible particle data. */
+			sync_dupli_particle(b_ob, *b_dupli_iter, object);
 		}
 		else {
 			object->dupli_generated = make_float3(0.0f, 0.0f, 0.0f);
@@ -630,7 +633,6 @@ void BlenderSync::sync_motion(BL::RenderSettings& b_render,
 			frame_center_delta = shuttertime * 0.5f;
 		}
 
-		/* TODO: move frame on depsgraph. */
 		float time = frame_center + subframe_center + frame_center_delta;
 		int frame = (int)floorf(time);
 		float subframe = time - frame;
@@ -663,7 +665,6 @@ void BlenderSync::sync_motion(BL::RenderSettings& b_render,
 		int frame = (int)floorf(time);
 		float subframe = time - frame;
 
-		/* TODO: move frame on depsgraph. */
 		/* change frame */
 		python_thread_state_restore(python_thread_state);
 		b_engine.frame_set(frame, subframe);
diff --git a/intern/cycles/blender/blender_particles.cpp b/intern/cycles/blender/blender_particles.cpp
index 00f8cb3cf1b207c0e53b175dc96cc5401a96dcdb..e365061722e07365567fb15382b744dd0455de47 100644
--- a/intern/cycles/blender/blender_particles.cpp
+++ b/intern/cycles/blender/blender_particles.cpp
@@ -28,7 +28,7 @@ CCL_NAMESPACE_BEGIN
 /* Utilities */
 
 bool BlenderSync::sync_dupli_particle(BL::Object& b_ob,
-                                      BL::DupliObject& b_dup,
+                                      BL::DepsgraphIter& b_dup,
                                       Object *object)
 {
 	/* test if this dupli was generated from a particle sytem */
diff --git a/intern/cycles/blender/blender_sync.h b/intern/cycles/blender/blender_sync.h
index 2ea86ba11332eed310a2f2072d19c66475277d08..803167577980c9b49f247cf99e8677c057376f2d 100644
--- a/intern/cycles/blender/blender_sync.h
+++ b/intern/cycles/blender/blender_sync.h
@@ -152,7 +152,7 @@ private:
 
 	/* particles */
 	bool sync_dupli_particle(BL::Object& b_ob,
-	                         BL::DupliObject& b_dup,
+	                         BL::DepsgraphIter& b_dup,
 	                         Object *object);
 
 	/* Images. */
diff --git a/intern/cycles/device/opencl/opencl_util.cpp b/intern/cycles/device/opencl/opencl_util.cpp
index a776f48b5e95438c4661ba260cd2ce6dafac4671..78ed401bff53489640fc85a432dcfbac4290a88a 100644
--- a/intern/cycles/device/opencl/opencl_util.cpp
+++ b/intern/cycles/device/opencl/opencl_util.cpp
@@ -633,7 +633,7 @@ bool OpenCLInfo::device_supported(const string& platform_name,
 		}
 		const char *blacklist[] = {
 			/* GCN 1 */
-			"Tahiti", "Pitcairn", "Capeverde", "Oland",
+			"Tahiti", "Pitcairn", "Capeverde", "Oland", "Hainan",
 			NULL
 		};
 		for(int i = 0; blacklist[i] != NULL; i++) {
diff --git a/intern/cycles/kernel/closure/bsdf.h b/intern/cycles/kernel/closure/bsdf.h
index e3beff6675a9ce77852156ac3a80d1daf4e7bc05..d8ff69ca2417d091405d671f9e77f92df28e1ad2 100644
--- a/intern/cycles/kernel/closure/bsdf.h
+++ b/intern/cycles/kernel/closure/bsdf.h
@@ -36,20 +36,42 @@ CCL_NAMESPACE_BEGIN
 
 /* Returns the square of the roughness of the closure if it has roughness,
  * 0 for singular closures and 1 otherwise. */
-ccl_device_inline float bsdf_get_roughness_squared(const ShaderClosure *sc)
+ccl_device_inline float bsdf_get_specular_roughness_squared(const ShaderClosure *sc)
 {
 	if(CLOSURE_IS_BSDF_SINGULAR(sc->type)) {
 		return 0.0f;
 	}
 
 	if(CLOSURE_IS_BSDF_MICROFACET(sc->type)) {
-		MicrofacetBsdf *bsdf = (MicrofacetBsdf*) sc;
+		MicrofacetBsdf *bsdf = (MicrofacetBsdf*)sc;
 		return bsdf->alpha_x*bsdf->alpha_y;
 	}
 
 	return 1.0f;
 }
 
+ccl_device_inline float bsdf_get_roughness_squared(const ShaderClosure *sc)
+{
+	/* This version includes diffuse, mainly for baking Principled BSDF
+	 * where specular and metallic zero otherwise does not bake the
+	 * specified roughness parameter. */
+	if(sc->type == CLOSURE_BSDF_OREN_NAYAR_ID) {
+		OrenNayarBsdf *bsdf = (OrenNayarBsdf*)sc;
+		return sqr(sqr(bsdf->roughness));
+	}
+
+	if(sc->type == CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID) {
+		PrincipledDiffuseBsdf *bsdf = (PrincipledDiffuseBsdf*)sc;
+		return sqr(sqr(bsdf->roughness));
+	}
+
+	if(CLOSURE_IS_BSDF_DIFFUSE(sc->type)) {
+		return 0.0f;
+	}
+
+	return bsdf_get_specular_roughness_squared(sc);
+}
+
 ccl_device_forceinline int bsdf_sample(KernelGlobals *kg,
                                        ShaderData *sd,
                                        const ShaderClosure *sc,
@@ -176,7 +198,7 @@ ccl_device_forceinline int bsdf_sample(KernelGlobals *kg,
 		float threshold_squared = kernel_data.background.transparent_roughness_squared_threshold;
 
 		if(threshold_squared >= 0.0f) {
-			if(bsdf_get_roughness_squared(sc) <= threshold_squared) {
+			if(bsdf_get_specular_roughness_squared(sc) <= threshold_squared) {
 				label |= LABEL_TRANSMIT_TRANSPARENT;
 			}
 		}
diff --git a/intern/cycles/kernel/geom/geom_volume.h b/intern/cycles/kernel/geom/geom_volume.h
index a4e47384b25cbda9cf1fe43cceb0fd539720ef22..346f228e961272c06f5944bfa6fbe0857040e62b 100644
--- a/intern/cycles/kernel/geom/geom_volume.h
+++ b/intern/cycles/kernel/geom/geom_volume.h
@@ -68,7 +68,7 @@ ccl_device float3 volume_attribute_float3(KernelGlobals *kg, const ShaderData *s
 	if(dx) *dx = make_float3(0.0f, 0.0f, 0.0f);
 	if(dy) *dy = make_float3(0.0f, 0.0f, 0.0f);
 
-	if(r.w > 1e-8f && r.w != 1.0f) {
+	if(r.w > 1e-6f && r.w != 1.0f) {
 		/* For RGBA colors, unpremultiply after interpolation. */
 		return float4_to_float3(r) / r.w;
 	}
diff --git a/intern/cycles/kernel/kernel_passes.h b/intern/cycles/kernel/kernel_passes.h
index 1933d695f92ae1943357b09e76c55b3a17da3bcc..a42a8e9812f311c51a26f62abbfc7fa2db370b7b 100644
--- a/intern/cycles/kernel/kernel_passes.h
+++ b/intern/cycles/kernel/kernel_passes.h
@@ -140,7 +140,7 @@ ccl_device_inline void kernel_update_denoising_features(KernelGlobals *kg,
 		/* All closures contribute to the normal feature, but only diffuse-like ones to the albedo. */
 		normal += sc->N * sc->sample_weight;
 		sum_weight += sc->sample_weight;
-		if(bsdf_get_roughness_squared(sc) > sqr(0.075f)) {
+		if(bsdf_get_specular_roughness_squared(sc) > sqr(0.075f)) {
 			albedo += sc->weight;
 			sum_nonspecular_weight += sc->sample_weight;
 		}
diff --git a/intern/cycles/kernel/kernel_path_branched.h b/intern/cycles/kernel/kernel_path_branched.h
index d43d418db29f56856e1812f29ef1c437c002e619..66f67c3e2c4388af8075147774c7dc330063c4c7 100644
--- a/intern/cycles/kernel/kernel_path_branched.h
+++ b/intern/cycles/kernel/kernel_path_branched.h
@@ -567,10 +567,7 @@ ccl_device void kernel_branched_path_integrate(KernelGlobals *kg,
 #ifdef __VOLUME__
 		}
 		else {
-			/* For volume bounding meshes we pass through without counting transparent
-			 * bounces, only sanity check in case self intersection gets us stuck. */
-			state.volume_bounds_bounce++;
-			if (state.volume_bounds_bounce > VOLUME_BOUNDS_MAX) {
+			if(!path_state_volume_next(kg, &state)) {
 				break;
 			}
 		}
diff --git a/intern/cycles/kernel/kernel_path_state.h b/intern/cycles/kernel/kernel_path_state.h
index ff7d1307a6c35a2fc9b550f1e2d9704424787f28..8a358e51f94c506ebf0243a486b42e606e6b6fb8 100644
--- a/intern/cycles/kernel/kernel_path_state.h
+++ b/intern/cycles/kernel/kernel_path_state.h
@@ -60,8 +60,6 @@ ccl_device_inline void path_state_init(KernelGlobals *kg,
 	if(kernel_data.integrator.use_volumes) {
 		/* Initialize volume stack with volume we are inside of. */
 		kernel_volume_stack_init(kg, stack_sd, state, ray, state->volume_stack);
-		/* Seed RNG for cases where we can't use stratified samples .*/
-		state->rng_congruential = lcg_init(rng_hash + sample*0x51633e2d);
 	}
 	else {
 		state->volume_stack[0].shader = SHADER_NONE;
@@ -166,6 +164,25 @@ ccl_device_inline void path_state_next(KernelGlobals *kg, ccl_addr_space PathSta
 #endif
 }
 
+#ifdef __VOLUME__
+ccl_device_inline bool path_state_volume_next(KernelGlobals *kg, ccl_addr_space PathState *state)
+{
+	/* For volume bounding meshes we pass through without counting transparent
+	 * bounces, only sanity check in case self intersection gets us stuck. */
+	state->volume_bounds_bounce++;
+	if (state->volume_bounds_bounce > VOLUME_BOUNDS_MAX) {
+		return false;
+	}
+
+	/* Random number generator next bounce. */
+	if(state->volume_bounds_bounce > 1) {
+		state->rng_offset += PRNG_BOUNCE_NUM;
+	}
+
+	return true;
+}
+#endif
+
 ccl_device_inline uint path_state_ray_visibility(KernelGlobals *kg, ccl_addr_space PathState *state)
 {
 	uint flag = state->flag & PATH_RAY_ALL_VISIBILITY;
diff --git a/intern/cycles/kernel/kernel_path_surface.h b/intern/cycles/kernel/kernel_path_surface.h
index bca346d5ee0a2dd5c3bdf58c5aace3c4b1196d07..27be90d50599c34011d13a5d648d3ed064b76a5f 100644
--- a/intern/cycles/kernel/kernel_path_surface.h
+++ b/intern/cycles/kernel/kernel_path_surface.h
@@ -329,10 +329,7 @@ ccl_device bool kernel_path_surface_bounce(KernelGlobals *kg,
 	}
 #ifdef __VOLUME__
 	else if(sd->flag & SD_HAS_ONLY_VOLUME) {
-		/* For volume bounding meshes we pass through without counting transparent
-		 * bounces, only sanity check in case self intersection gets us stuck. */
-		state->volume_bounds_bounce++;
-		if (state->volume_bounds_bounce > VOLUME_BOUNDS_MAX) {
+		if(!path_state_volume_next(kg, state)) {
 			return false;
 		}
 
diff --git a/intern/cycles/kernel/kernel_random.h b/intern/cycles/kernel/kernel_random.h
index efb9048beb8b44b6efaf7cb11bb6b0fdc42730a1..93152e9ff1c4ab3fa8297aae15192a1f82ab81eb 100644
--- a/intern/cycles/kernel/kernel_random.h
+++ b/intern/cycles/kernel/kernel_random.h
@@ -199,6 +199,19 @@ ccl_device_inline void path_state_rng_2D(KernelGlobals *kg,
 	            fx, fy);
 }
 
+ccl_device_inline float path_state_rng_1D_hash(KernelGlobals *kg,
+                                          const ccl_addr_space PathState *state,
+                                          uint hash)
+{
+	/* Use a hash instead of dimension, this is not great but avoids adding
+	 * more dimensions to each bounce which reduces quality of dimensions we
+	 * are already using. */
+	return path_rng_1D(kg,
+	                   cmj_hash_simple(state->rng_hash, hash),
+	                   state->sample, state->num_samples,
+	                   state->rng_offset);
+}
+
 ccl_device_inline float path_branched_rng_1D(
         KernelGlobals *kg,
         uint rng_hash,
diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h
index 1165700325902957bcdae80b8085982bd4557fb7..af1ecb057880cd6cb4c6b1e19cd4ea21b858efbf 100644
--- a/intern/cycles/kernel/kernel_types.h
+++ b/intern/cycles/kernel/kernel_types.h
@@ -1107,7 +1107,6 @@ typedef struct PathState {
 #ifdef __VOLUME__
 	int volume_bounce;
 	int volume_bounds_bounce;
-	uint rng_congruential;
 	VolumeStack volume_stack[VOLUME_STACK_SIZE];
 #endif
 } PathState;
@@ -1462,6 +1461,7 @@ typedef struct KernelSpotLight {
 	float spot_angle;
 	float spot_smooth;
 	float dir[3];
+	float pad;
 } KernelSpotLight;
 
 /* PointLight is SpotLight with only radius and invarea being used. */
@@ -1470,13 +1470,16 @@ typedef struct KernelAreaLight {
 	float axisu[3];
 	float invarea;
 	float axisv[3];
+	float pad1;
 	float dir[3];
+	float pad2;
 } KernelAreaLight;
 
 typedef struct KernelDistantLight {
 	float radius;
 	float cosangle;
 	float invarea;
+	float pad;
 } KernelDistantLight;
 
 typedef struct KernelLight {
diff --git a/intern/cycles/kernel/kernel_volume.h b/intern/cycles/kernel/kernel_volume.h
index 88360e5f1ae5afde85cdcd4b4cd8789f1c6e8612..86378289b02a70fcc9dcc4b212908acdb1d3e477 100644
--- a/intern/cycles/kernel/kernel_volume.h
+++ b/intern/cycles/kernel/kernel_volume.h
@@ -156,6 +156,24 @@ ccl_device int volume_stack_sampling_method(KernelGlobals *kg, VolumeStack *stac
 	return method;
 }
 
+ccl_device_inline void kernel_volume_step_init(KernelGlobals *kg,
+                                               ccl_addr_space PathState *state,
+                                               float t,
+                                               float *step_size,
+                                               float *step_offset)
+{
+	const int max_steps = kernel_data.integrator.volume_max_steps;
+	float step = min(kernel_data.integrator.volume_step_size, t);
+
+	/* compute exact steps in advance for malloc */
+	if(t > max_steps * step) {
+		step = t / (float)max_steps;
+	}
+
+	*step_size = step;
+	*step_offset = path_state_rng_1D_hash(kg, state, 0x1e31d8a4) * step;
+}
+
 /* Volume Shadows
  *
  * These functions are used to attenuate shadow rays to lights. Both absorption
@@ -188,8 +206,8 @@ ccl_device void kernel_volume_shadow_heterogeneous(KernelGlobals *kg,
 
 	/* prepare for stepping */
 	int max_steps = kernel_data.integrator.volume_max_steps;
-	float step = kernel_data.integrator.volume_step_size;
-	float random_jitter_offset = lcg_step_float_addrspace(&state->rng_congruential) * step;
+	float step_offset, step_size;
+	kernel_volume_step_init(kg, state, ray->t, &step_size, &step_offset);
 
 	/* compute extinction at the start */
 	float t = 0.0f;
@@ -198,14 +216,15 @@ ccl_device void kernel_volume_shadow_heterogeneous(KernelGlobals *kg,
 
 	for(int i = 0; i < max_steps; i++) {
 		/* advance to new position */
-		float new_t = min(ray->t, (i+1) * step);
-		float dt = new_t - t;
+		float new_t = min(ray->t, (i+1) * step_size);
 
-		/* use random position inside this segment to sample shader */
-		if(new_t == ray->t)
-			random_jitter_offset = lcg_step_float_addrspace(&state->rng_congruential) * dt;
+		/* use random position inside this segment to sample shader, adjust
+		 * for last step that is shorter than other steps. */
+		if(new_t == ray->t) {
+			step_offset *= (new_t - t) / step_size;
+		}
 
-		float3 new_P = ray->P + ray->D * (t + random_jitter_offset);
+		float3 new_P = ray->P + ray->D * (t + step_offset);
 		float3 sigma_t;
 
 		/* compute attenuation over segment */
@@ -504,8 +523,8 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_heterogeneous_distance(
 
 	/* prepare for stepping */
 	int max_steps = kernel_data.integrator.volume_max_steps;
-	float step_size = kernel_data.integrator.volume_step_size;
-	float random_jitter_offset = lcg_step_float_addrspace(&state->rng_congruential) * step_size;
+	float step_offset, step_size;
+	kernel_volume_step_init(kg, state, ray->t, &step_size, &step_offset);
 
 	/* compute coefficients at the start */
 	float t = 0.0f;
@@ -522,11 +541,13 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_heterogeneous_distance(
 		float new_t = min(ray->t, (i+1) * step_size);
 		float dt = new_t - t;
 
-		/* use random position inside this segment to sample shader */
-		if(new_t == ray->t)
-			random_jitter_offset = lcg_step_float_addrspace(&state->rng_congruential) * dt;
+		/* use random position inside this segment to sample shader,
+		* for last shorter step we remap it to fit within the segment. */
+		if(new_t == ray->t) {
+			step_offset *= (new_t - t) / step_size;
+		}
 
-		float3 new_P = ray->P + ray->D * (t + random_jitter_offset);
+		float3 new_P = ray->P + ray->D * (t + step_offset);
 		VolumeShaderCoefficients coeff;
 
 		/* compute segment */
@@ -694,19 +715,12 @@ ccl_device void kernel_volume_decoupled_record(KernelGlobals *kg, PathState *sta
 
 	/* prepare for volume stepping */
 	int max_steps;
-	float step_size, random_jitter_offset;
+	float step_size, step_offset;
 
 	if(heterogeneous) {
-		const int global_max_steps = kernel_data.integrator.volume_max_steps;
-		step_size = kernel_data.integrator.volume_step_size;
-		/* compute exact steps in advance for malloc */
-		if(ray->t > global_max_steps*step_size) {
-			max_steps = global_max_steps;
-			step_size = ray->t / (float)max_steps;
-		}
-		else {
-			max_steps = max((int)ceilf(ray->t/step_size), 1);
-		}
+		max_steps = kernel_data.integrator.volume_max_steps;
+		kernel_volume_step_init(kg, state, ray->t, &step_size, &step_offset);
+
 #ifdef __KERNEL_CPU__
 		/* NOTE: For the branched path tracing it's possible to have direct
 		 * and indirect light integration both having volume segments allocated.
@@ -723,19 +737,18 @@ ccl_device void kernel_volume_decoupled_record(KernelGlobals *kg, PathState *sta
 		               sizeof(*kg->decoupled_volume_steps));
 		if(kg->decoupled_volume_steps[index] == NULL) {
 			kg->decoupled_volume_steps[index] =
-			        (VolumeStep*)malloc(sizeof(VolumeStep)*global_max_steps);
+			        (VolumeStep*)malloc(sizeof(VolumeStep)*max_steps);
 		}
 		segment->steps = kg->decoupled_volume_steps[index];
 		++kg->decoupled_volume_steps_index;
 #else
 		segment->steps = (VolumeStep*)malloc(sizeof(VolumeStep)*max_steps);
 #endif
-		random_jitter_offset = lcg_step_float(&state->rng_congruential) * step_size;
 	}
 	else {
 		max_steps = 1;
 		step_size = ray->t;
-		random_jitter_offset = 0.0f;
+		step_offset = 0.0f;
 		segment->steps = &segment->stack_step;
 	}
 	
@@ -757,11 +770,13 @@ ccl_device void kernel_volume_decoupled_record(KernelGlobals *kg, PathState *sta
 		float new_t = min(ray->t, (i+1) * step_size);
 		float dt = new_t - t;
 
-		/* use random position inside this segment to sample shader */
-		if(heterogeneous && new_t == ray->t)
-			random_jitter_offset = lcg_step_float(&state->rng_congruential) * dt;
+		/* use random position inside this segment to sample shader,
+		* for last shorter step we remap it to fit within the segment. */
+		if(new_t == ray->t) {
+			step_offset *= (new_t - t) / step_size;
+		}
 
-		float3 new_P = ray->P + ray->D * (t + random_jitter_offset);
+		float3 new_P = ray->P + ray->D * (t + step_offset);
 		VolumeShaderCoefficients coeff;
 
 		/* compute segment */
@@ -818,7 +833,7 @@ ccl_device void kernel_volume_decoupled_record(KernelGlobals *kg, PathState *sta
 		step->accum_transmittance = accum_transmittance;
 		step->cdf_distance = cdf_distance;
 		step->t = new_t;
-		step->shade_t = t + random_jitter_offset;
+		step->shade_t = t + step_offset;
 
 		/* stop if at the end of the volume */
 		t = new_t;
diff --git a/intern/cycles/kernel/split/kernel_next_iteration_setup.h b/intern/cycles/kernel/split/kernel_next_iteration_setup.h
index 8092419e796f87d657aa6f0f5be739a0d85781b8..e388955f1af468e5ff5a668f689d5d4291151ac5 100644
--- a/intern/cycles/kernel/split/kernel_next_iteration_setup.h
+++ b/intern/cycles/kernel/split/kernel_next_iteration_setup.h
@@ -76,10 +76,7 @@ ccl_device void kernel_split_branched_transparent_bounce(KernelGlobals *kg, int
 #  ifdef __VOLUME__
 	}
 	else {
-		/* For volume bounding meshes we pass through without counting transparent
-		 * bounces, only sanity check in case self intersection gets us stuck. */
-		state->volume_bounds_bounce++;
-		if (state->volume_bounds_bounce > VOLUME_BOUNDS_MAX) {
+		if(!path_state_volume_next(kg, state)) {
 			kernel_split_path_end(kg, ray_index);
 			return;
 		}
diff --git a/intern/cycles/render/integrator.cpp b/intern/cycles/render/integrator.cpp
index c337c19ced18473cae122351a532af3c10d2e64f..9c276bcab31bd24e17f54bb0943750789e4e5848 100644
--- a/intern/cycles/render/integrator.cpp
+++ b/intern/cycles/render/integrator.cpp
@@ -187,7 +187,9 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene
 		max_samples = max(max_samples, volume_samples);
 	}
 
-	uint total_bounces = max_bounce + transparent_max_bounce + 3 +
+	uint total_bounces = max_bounce +
+	                     transparent_max_bounce + 3 +
+	                     VOLUME_BOUNDS_MAX +
 	                     max(BSSRDF_MAX_HITS, BSSRDF_MAX_BOUNCES);
 
 	max_samples *= total_bounces;
diff --git a/intern/cycles/render/mesh_subdivision.cpp b/intern/cycles/render/mesh_subdivision.cpp
index 585ed77b026321d741bd2cb0fd285697e16d4ed4..9dd81eb67003c6b013537a62aaab34aeef991784 100644
--- a/intern/cycles/render/mesh_subdivision.cpp
+++ b/intern/cycles/render/mesh_subdivision.cpp
@@ -204,7 +204,9 @@ public:
 			src = dest;
 		}
 
-		patch_table->ComputeLocalPointValues(&verts[0], &verts[num_refiner_verts]);
+		if(num_local_points) {
+			patch_table->ComputeLocalPointValues(&verts[0], &verts[num_refiner_verts]);
+		}
 
 		/* create patch map */
 		patch_map = new Far::PatchMap(*patch_table);
@@ -236,13 +238,15 @@ public:
 				src = dest;
 			}
 
-			if(attr.same_storage(attr.type, TypeDesc::TypeFloat)) {
-				patch_table->ComputeLocalPointValues((OsdValue<float>*)&attr.buffer[0],
-					                                 (OsdValue<float>*)&attr.buffer[num_refiner_verts * attr.data_sizeof()]);
-			}
-			else {
-				patch_table->ComputeLocalPointValues((OsdValue<float4>*)&attr.buffer[0],
-					                                 (OsdValue<float4>*)&attr.buffer[num_refiner_verts * attr.data_sizeof()]);
+			if(num_local_points) {
+				if(attr.same_storage(attr.type, TypeDesc::TypeFloat)) {
+					patch_table->ComputeLocalPointValues((OsdValue<float>*)&attr.buffer[0],
+							                             (OsdValue<float>*)&attr.buffer[num_refiner_verts * attr.data_sizeof()]);
+				}
+				else {
+					patch_table->ComputeLocalPointValues((OsdValue<float4>*)&attr.buffer[0],
+							                             (OsdValue<float4>*)&attr.buffer[num_refiner_verts * attr.data_sizeof()]);
+				}
 			}
 		}
 		else if(attr.element == ATTR_ELEMENT_CORNER || attr.element == ATTR_ELEMENT_CORNER_BYTE) {
diff --git a/intern/cycles/render/mesh_volume.cpp b/intern/cycles/render/mesh_volume.cpp
index 3571beb40d66df53b44e52f7e30d07b5f9d543ea..d1c49b456ffcdc97725da8cad9056f674915eb7e 100644
--- a/intern/cycles/render/mesh_volume.cpp
+++ b/intern/cycles/render/mesh_volume.cpp
@@ -495,54 +495,14 @@ void MeshManager::create_volume_mesh(Scene *scene,
 
 				for(size_t i = 0; i < voxel_grids.size(); ++i) {
 					const VoxelAttributeGrid &voxel_grid = voxel_grids[i];
+					const int channels = voxel_grid.channels;
 
-					if(voxel_grid.channels == 1) {
-						if(voxel_grid.data[voxel_index] >= isovalue) {
+					for(int c = 0; c < channels; c++) {
+						if(voxel_grid.data[voxel_index * channels + c] >= isovalue) {
 							builder.add_node_with_padding(x, y, z);
 							break;
 						}
 					}
-					else if(voxel_grid.channels == 3) {
-						voxel_index = compute_voxel_index(resolution, x*3, y, z);
-
-						if(voxel_grid.data[voxel_index] >= isovalue) {
-							builder.add_node_with_padding(x, y, z);
-							break;
-						}
-
-						if(voxel_grid.data[voxel_index + 1] >= isovalue) {
-							builder.add_node_with_padding(x, y, z);
-							break;
-						}
-
-						if(voxel_grid.data[voxel_index + 2] >= isovalue) {
-							builder.add_node_with_padding(x, y, z);
-							break;
-						}
-					}
-					else if(voxel_grid.channels == 4) {
-						voxel_index = compute_voxel_index(resolution, x*4, y, z);
-
-						/* check alpha first */
-						if(voxel_grid.data[voxel_index + 3] < isovalue) {
-							continue;
-						}
-
-						if(voxel_grid.data[voxel_index] >= isovalue) {
-							builder.add_node_with_padding(x, y, z);
-							continue;
-						}
-
-						if(voxel_grid.data[voxel_index + 1] >= isovalue) {
-							builder.add_node_with_padding(x, y, z);
-							continue;
-						}
-
-						if(voxel_grid.data[voxel_index + 2] >= isovalue) {
-							builder.add_node_with_padding(x, y, z);
-							continue;
-						}
-					}
 				}
 			}
 		}
diff --git a/intern/cycles/util/util_logging.h b/intern/cycles/util/util_logging.h
index 492f830e67cc9b29619d985c1a9cda67b7657eeb..5c84b6593d3b0153f1c5e145faa832e457e16aba 100644
--- a/intern/cycles/util/util_logging.h
+++ b/intern/cycles/util/util_logging.h
@@ -18,6 +18,7 @@
 #define __UTIL_LOGGING_H__
 
 #if defined(WITH_CYCLES_LOGGING) && !defined(__KERNEL_GPU__)
+#  include <gflags/gflags.h>
 #  include <glog/logging.h>
 #endif
 
diff --git a/intern/cycles/util/util_sseb.h b/intern/cycles/util/util_sseb.h
index 93c22aafdcd1c894d1f1b33970d105d4aca76ad7..977976c3fc03351935862442e04fc9d2ffb94139 100644
--- a/intern/cycles/util/util_sseb.h
+++ b/intern/cycles/util/util_sseb.h
@@ -119,7 +119,7 @@ __forceinline const sseb unpacklo( const sseb& a, const sseb& b ) { return _mm_u
 __forceinline const sseb unpackhi( const sseb& a, const sseb& b ) { return _mm_unpackhi_ps(a, b); }
 
 template<size_t i0, size_t i1, size_t i2, size_t i3> __forceinline const sseb shuffle( const sseb& a ) {
-	return _mm_shuffle_epi32(a, _MM_SHUFFLE(i3, i2, i1, i0));
+	return _mm_castsi128_ps(_mm_shuffle_epi32(a, _MM_SHUFFLE(i3, i2, i1, i0)));
 }
 
 template<> __forceinline const sseb shuffle<0, 1, 0, 1>( const sseb& a ) {
diff --git a/intern/cycles/util/util_thread.cpp b/intern/cycles/util/util_thread.cpp
index 3dcb09804b07854107fc356c00b7d9b0af5952af..c66aa484264ccdeff223e304fc4b572afc4fdef8 100644
--- a/intern/cycles/util/util_thread.cpp
+++ b/intern/cycles/util/util_thread.cpp
@@ -26,7 +26,11 @@ thread::thread(function<void(void)> run_cb, int group)
     joined_(false),
 	group_(group)
 {
+#if (__cplusplus > 199711L) || (defined(_MSC_VER) && _MSC_VER >= 1800)
+	thread_ = std::thread(&thread::run, this);
+#else
 	pthread_create(&pthread_id_, NULL, run, (void*)this);
+#endif
 }
 
 thread::~thread()
@@ -60,7 +64,17 @@ void *thread::run(void *arg)
 bool thread::join()
 {
 	joined_ = true;
+#if (__cplusplus > 199711L) || (defined(_MSC_VER) && _MSC_VER >= 1800)
+	try {
+		thread_.join();
+		return true;
+	}
+	catch (const std::system_error&) {
+		return false;
+	}
+#else
 	return pthread_join(pthread_id_, NULL) == 0;
+#endif
 }
 
 CCL_NAMESPACE_END
diff --git a/intern/cycles/util/util_thread.h b/intern/cycles/util/util_thread.h
index 1e91fb8a7063dc3ce11c82e514ba8fa6e83f272f..4d8f464359cf953e43d54528add326a1d641e640 100644
--- a/intern/cycles/util/util_thread.h
+++ b/intern/cycles/util/util_thread.h
@@ -24,10 +24,16 @@
 #  include <functional>
 #else
 #  include <boost/thread.hpp>
+#  include <pthread.h>
 #endif
-#include <pthread.h>
 #include <queue>
 
+#ifdef _WIN32
+#  include "util_windows.h"
+#else
+#  include <pthread.h>
+#endif
+
 #ifdef __APPLE__
 #  include <libkern/OSAtomic.h>
 #endif
@@ -60,7 +66,11 @@ public:
 
 protected:
 	function<void(void)> run_cb_;
+#if (__cplusplus > 199711L) || (defined(_MSC_VER) && _MSC_VER >= 1800)
+	std::thread thread_;
+#else
 	pthread_t pthread_id_;
+#endif
 	bool joined_;
 	int group_;
 };
@@ -81,7 +91,24 @@ public:
 	inline void unlock() {
 		OSSpinLockUnlock(&spin_);
 	}
-#else  /* __APPLE__ */
+#elif defined(_WIN32)
+	inline thread_spin_lock() {
+		const DWORD SPIN_COUNT = 50000;
+		InitializeCriticalSectionAndSpinCount(&cs_, SPIN_COUNT);
+	}
+
+	inline ~thread_spin_lock() {
+		DeleteCriticalSection(&cs_);
+	}
+
+	inline void lock() {
+		EnterCriticalSection(&cs_);
+	}
+
+	inline void unlock() {
+		LeaveCriticalSection(&cs_);
+	}
+#else
 	inline thread_spin_lock() {
 		pthread_spin_init(&spin_, 0);
 	}
@@ -97,10 +124,12 @@ public:
 	inline void unlock() {
 		pthread_spin_unlock(&spin_);
 	}
-#endif  /* __APPLE__ */
+#endif
 protected:
 #ifdef __APPLE__
 	OSSpinLock spin_;
+#elif defined(_WIN32)
+	CRITICAL_SECTION cs_;
 #else
 	pthread_spinlock_t spin_;
 #endif
diff --git a/intern/cycles/util/util_types_float4.h b/intern/cycles/util/util_types_float4.h
index a7d9abe1b955ecd6897652d0b5747a7f4e3d61c8..154391f6881ac31b7aa25c6359d167cef76dd958 100644
--- a/intern/cycles/util/util_types_float4.h
+++ b/intern/cycles/util/util_types_float4.h
@@ -34,7 +34,6 @@ struct ccl_try_align(16) float4 {
 	};
 
 	__forceinline float4();
-	__forceinline float4(const float4& a);
 	__forceinline explicit float4(const __m128& a);
 
 	__forceinline operator const __m128&(void) const;
diff --git a/intern/cycles/util/util_types_float4_impl.h b/intern/cycles/util/util_types_float4_impl.h
index ff3ec4d4ecf0e23b095574b6e673c7b15a079f7e..09f45f47d3847fc5a97d1385f80689c6be380cf1 100644
--- a/intern/cycles/util/util_types_float4_impl.h
+++ b/intern/cycles/util/util_types_float4_impl.h
@@ -33,11 +33,6 @@ __forceinline float4::float4()
 {
 }
 
-__forceinline float4::float4(const float4& a)
-        : m128(a.m128)
-{
-}
-
 __forceinline float4::float4(const __m128& a)
         : m128(a)
 {
diff --git a/intern/gawain/gawain/gwn_batch.h b/intern/gawain/gawain/gwn_batch.h
index 3e90ef091f119368e33c81776c04e9b2acbb38c0..23bec80a0d12fd098d5d7c03cbadcce876c74d37 100644
--- a/intern/gawain/gawain/gwn_batch.h
+++ b/intern/gawain/gawain/gwn_batch.h
@@ -113,6 +113,8 @@ void GWN_batch_uniform_4f(Gwn_Batch*, const char* name, float x, float y, float
 void GWN_batch_uniform_2fv(Gwn_Batch*, const char* name, const float data[2]);
 void GWN_batch_uniform_3fv(Gwn_Batch*, const char* name, const float data[3]);
 void GWN_batch_uniform_4fv(Gwn_Batch*, const char* name, const float data[4]);
+void GWN_batch_uniform_2fv_array(Gwn_Batch*, const char* name, int len, const float *data);
+void GWN_batch_uniform_4fv_array(Gwn_Batch*, const char* name, int len, const float *data);
 
 void GWN_batch_draw(Gwn_Batch*);
 
diff --git a/intern/gawain/gawain/gwn_common.h b/intern/gawain/gawain/gwn_common.h
index e96a3b5c2a2e54671dab67e362f2b309b0956004..dc0a52ca0961563b051b02d926e1d50907004848 100644
--- a/intern/gawain/gawain/gwn_common.h
+++ b/intern/gawain/gawain/gwn_common.h
@@ -11,6 +11,8 @@
 
 #pragma once
 
+#define PROGRAM_NO_OPTI 0
+
 #if defined(NDEBUG)
   #define TRUST_NO_ONE 0
 #else
diff --git a/intern/gawain/gawain/gwn_immediate.h b/intern/gawain/gawain/gwn_immediate.h
index 386b26b63b10acce72624e2b7bd114ae3a260ad8..7866f83e774113c64d739d71e196dc128e13e4e9 100644
--- a/intern/gawain/gawain/gwn_immediate.h
+++ b/intern/gawain/gawain/gwn_immediate.h
@@ -70,6 +70,7 @@ void immSkipAttrib(unsigned attrib_id);
 // this is most often used for 2D or 3D position (similar to glVertex)
 void immVertex2f(unsigned attrib_id, float x, float y);
 void immVertex3f(unsigned attrib_id, float x, float y, float z);
+void immVertex4f(unsigned attrib_id, float x, float y, float z, float w);
 
 void immVertex2i(unsigned attrib_id, int x, int y);
 
diff --git a/intern/gawain/gawain/gwn_vertex_buffer.h b/intern/gawain/gawain/gwn_vertex_buffer.h
index ddb77368a027c0589f9007fee4c470c65716f3c9..e9a37519b3656b8176144f7063a41e520a594456 100644
--- a/intern/gawain/gawain/gwn_vertex_buffer.h
+++ b/intern/gawain/gawain/gwn_vertex_buffer.h
@@ -31,7 +31,8 @@ typedef enum {
 
 typedef struct Gwn_VertBuf {
 	Gwn_VertFormat format;
-	unsigned vertex_ct;
+	unsigned vertex_ct;    // number of verts we want to draw
+	unsigned vertex_alloc; // number of verts data
 	bool dirty;
 	GLubyte* data; // NULL indicates data in VRAM (unmapped)
 	GLuint vbo_id; // 0 indicates not yet allocated
@@ -55,6 +56,7 @@ void GWN_vertbuf_init_with_format_ex(Gwn_VertBuf*, const Gwn_VertFormat*, Gwn_Us
 unsigned GWN_vertbuf_size_get(const Gwn_VertBuf*);
 void GWN_vertbuf_data_alloc(Gwn_VertBuf*, unsigned v_ct);
 void GWN_vertbuf_data_resize(Gwn_VertBuf*, unsigned v_ct);
+void GWN_vertbuf_vertex_count_set(Gwn_VertBuf*, unsigned v_ct);
 
 // The most important set_attrib variant is the untyped one. Get it right first.
 // It takes a void* so the app developer is responsible for matching their app data types
diff --git a/intern/gawain/src/gwn_batch.c b/intern/gawain/src/gwn_batch.c
index c720ba967b598145c008e0f8b46b22690b533f45..9243c9f6fc43cb8453eada1d120c54d21b4422db 100644
--- a/intern/gawain/src/gwn_batch.c
+++ b/intern/gawain/src/gwn_batch.c
@@ -418,7 +418,9 @@ void GWN_batch_program_use_end(Gwn_Batch* batch)
 	{
 	if (batch->program_in_use)
 		{
+#if PROGRAM_NO_OPTI
 		glUseProgram(0);
+#endif
 		batch->program_in_use = false;
 		}
 	}
@@ -489,6 +491,18 @@ void GWN_batch_uniform_4fv(Gwn_Batch* batch, const char* name, const float data[
 	glUniform4fv(uniform->location, 1, data);
 	}
 
+void GWN_batch_uniform_2fv_array(Gwn_Batch* batch, const char* name, const int len, const float *data)
+	{
+	GET_UNIFORM
+	glUniform2fv(uniform->location, len, data);
+	}
+
+void GWN_batch_uniform_4fv_array(Gwn_Batch* batch, const char* name, const int len, const float *data)
+	{
+	GET_UNIFORM
+	glUniform4fv(uniform->location, len, data);
+	}
+
 static void primitive_restart_enable(const Gwn_IndexBuf *el)
 {
 	// TODO(fclem) Replace by GL_PRIMITIVE_RESTART_FIXED_INDEX when we have ogl 4.3
diff --git a/intern/gawain/src/gwn_element.c b/intern/gawain/src/gwn_element.c
index 0b7dc675f87f53ad383b4e19601abbcfbf95032e..c56516817d65876f4d62e455c7a4e8107112db81 100644
--- a/intern/gawain/src/gwn_element.c
+++ b/intern/gawain/src/gwn_element.c
@@ -262,8 +262,11 @@ void GWN_indexbuf_build_in_place(Gwn_IndexBufBuilder* builder, Gwn_IndexBuf* ele
 		elem->vbo_id = GWN_buf_id_alloc();
 
 	// send data to GPU
-	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elem->vbo_id);
-	glBufferData(GL_ELEMENT_ARRAY_BUFFER, GWN_indexbuf_size_get(elem), builder->data, GL_STATIC_DRAW);
+	// GL_ELEMENT_ARRAY_BUFFER changes the state of the last VAO bound,
+	// so we use the GL_ARRAY_BUFFER here to create a buffer without
+	// interfering in the VAO state.
+	glBindBuffer(GL_ARRAY_BUFFER, elem->vbo_id);
+	glBufferData(GL_ARRAY_BUFFER, GWN_indexbuf_size_get(elem), builder->data, GL_STATIC_DRAW);
 
 	// discard builder (one-time use)
 	free(builder->data);
diff --git a/intern/gawain/src/gwn_immediate.c b/intern/gawain/src/gwn_immediate.c
index c6df3ada0185baf3d8ca365c7030a91533decc0e..0e57aefebe2a0088c7e70fa5cb5cb541c71fe04f 100644
--- a/intern/gawain/src/gwn_immediate.c
+++ b/intern/gawain/src/gwn_immediate.c
@@ -140,8 +140,9 @@ void immUnbindProgram(void)
 #if TRUST_NO_ONE
 	assert(imm.bound_program != 0);
 #endif
-
+#if PROGRAM_NO_OPTI
 	glUseProgram(0);
+#endif
 	imm.bound_program = 0;
 	}
 
@@ -710,6 +711,12 @@ void immVertex3f(unsigned attrib_id, float x, float y, float z)
 	immEndVertex();
 	}
 
+void immVertex4f(unsigned attrib_id, float x, float y, float z, float w)
+	{
+	immAttrib4f(attrib_id, x, y, z, w);
+	immEndVertex();
+	}
+
 void immVertex2i(unsigned attrib_id, int x, int y)
 	{
 	immAttrib2i(attrib_id, x, y);
diff --git a/intern/gawain/src/gwn_vertex_array_id.cpp b/intern/gawain/src/gwn_vertex_array_id.cpp
index ed54562c43461154f82b43efd0cfb0bcc5fa0ac4..ad60dea7542d66041b9ac8d791eb90a254dd89e7 100644
--- a/intern/gawain/src/gwn_vertex_array_id.cpp
+++ b/intern/gawain/src/gwn_vertex_array_id.cpp
@@ -166,5 +166,7 @@ void gwn_context_add_batch(Gwn_Context* ctx, Gwn_Batch* batch)
 
 void gwn_context_remove_batch(Gwn_Context* ctx, Gwn_Batch* batch)
 	{
+	ctx->orphans_mutex.lock();
 	ctx->batches.erase(batch);
+	ctx->orphans_mutex.unlock();
 	}
diff --git a/intern/gawain/src/gwn_vertex_buffer.c b/intern/gawain/src/gwn_vertex_buffer.c
index 39fc1885b92d1339220b3cd30fe81ef366ac48d0..f621b4c01b93a8208694f3eca364fcf2c6956376 100644
--- a/intern/gawain/src/gwn_vertex_buffer.c
+++ b/intern/gawain/src/gwn_vertex_buffer.c
@@ -93,7 +93,7 @@ void GWN_vertbuf_data_alloc(Gwn_VertBuf* verts, unsigned v_ct)
 
 #if TRUST_NO_ONE
 	// catch any unnecessary use
-	assert(verts->vertex_ct != v_ct || verts->data == NULL);
+	assert(verts->vertex_alloc != v_ct || verts->data == NULL);
 #endif
 
 	// only create the buffer the 1st time
@@ -105,16 +105,13 @@ void GWN_vertbuf_data_alloc(Gwn_VertBuf* verts, unsigned v_ct)
 		free(verts->data);
 
 #if VRAM_USAGE
-	vbo_memory_usage -= GWN_vertbuf_size_get(verts);
+	unsigned new_size = vertex_buffer_size(&verts->format, v_ct);
+	vbo_memory_usage += new_size - GWN_vertbuf_size_get(verts);
 #endif
 
 	verts->dirty = true;
-	verts->vertex_ct = v_ct;
+	verts->vertex_ct = verts->vertex_alloc = v_ct;
 	verts->data = malloc(sizeof(GLubyte) * GWN_vertbuf_size_get(verts));
-
-#if VRAM_USAGE
-	vbo_memory_usage += GWN_vertbuf_size_get(verts);
-#endif
 	}
 
 // resize buffer keeping existing data
@@ -122,20 +119,35 @@ void GWN_vertbuf_data_resize(Gwn_VertBuf* verts, unsigned v_ct)
 	{
 #if TRUST_NO_ONE
 	assert(verts->data != NULL);
-	assert(verts->vertex_ct != v_ct);
+	assert(verts->vertex_alloc != v_ct);
 #endif
 
 #if VRAM_USAGE
-	vbo_memory_usage -= GWN_vertbuf_size_get(verts);
+	unsigned new_size = vertex_buffer_size(&verts->format, v_ct);
+	vbo_memory_usage += new_size - GWN_vertbuf_size_get(verts);
 #endif
 
 	verts->dirty = true;
-	verts->vertex_ct = v_ct;
+	verts->vertex_ct = verts->vertex_alloc = v_ct;
 	verts->data = realloc(verts->data, sizeof(GLubyte) * GWN_vertbuf_size_get(verts));
+	}
+
+// set vertex count but does not change allocation
+// only this many verts will be uploaded to the GPU and rendered
+// this is usefull for streaming data
+void GWN_vertbuf_vertex_count_set(Gwn_VertBuf* verts, unsigned v_ct)
+	{
+#if TRUST_NO_ONE
+	assert(verts->data != NULL); // only for dynamic data
+	assert(v_ct <= verts->vertex_alloc);
+#endif
 
 #if VRAM_USAGE
-	vbo_memory_usage += GWN_vertbuf_size_get(verts);
+	unsigned new_size = vertex_buffer_size(&verts->format, v_ct);
+	vbo_memory_usage += new_size - GWN_vertbuf_size_get(verts);
 #endif
+
+	verts->vertex_ct = v_ct;
 	}
 
 void GWN_vertbuf_attr_set(Gwn_VertBuf* verts, unsigned a_idx, unsigned v_idx, const void* data)
@@ -145,7 +157,7 @@ void GWN_vertbuf_attr_set(Gwn_VertBuf* verts, unsigned a_idx, unsigned v_idx, co
 
 #if TRUST_NO_ONE
 	assert(a_idx < format->attrib_ct);
-	assert(v_idx < verts->vertex_ct);
+	assert(v_idx < verts->vertex_alloc);
 	assert(verts->data != NULL);
 #endif
 
@@ -210,7 +222,7 @@ void GWN_vertbuf_attr_get_raw_data(Gwn_VertBuf* verts, unsigned a_idx, Gwn_VertB
 	access->data = (GLubyte*)verts->data + a->offset;
 	access->data_init = access->data;
 #if TRUST_NO_ONE
-	access->_data_end = access->data_init + (size_t)(verts->vertex_ct * format->stride);
+	access->_data_end = access->data_init + (size_t)(verts->vertex_alloc * format->stride);
 #endif
 	}
 
diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h
index d5d8be7db8e225b7c61ea66e65b9dc39e4b1473b..071474e57dc119b6dadfc20a44dc64988ca4f01c 100644
--- a/intern/ghost/GHOST_C-api.h
+++ b/intern/ghost/GHOST_C-api.h
@@ -929,6 +929,11 @@ extern int GHOST_toggleConsole(int action);
  */
 extern int GHOST_confirmQuit(GHOST_WindowHandle windowhandle);
 
+/**
+ * Informs if the system provides native dialogs (eg. confirm quit)
+ */
+extern int GHOST_SupportsNativeDialogs(void);
+
 /**
  * Use native pixel size (MacBook pro 'retina'), if supported.
  */
diff --git a/intern/ghost/GHOST_ISystem.h b/intern/ghost/GHOST_ISystem.h
index 5c5590ef06975a5663e09b7e243570e748c331d1..25b7fb26e0e7d8cfa90ac0c750f7f3d8190ea276 100644
--- a/intern/ghost/GHOST_ISystem.h
+++ b/intern/ghost/GHOST_ISystem.h
@@ -433,6 +433,12 @@ public:
 	 * in the application
 	 */
 	virtual int confirmQuit(GHOST_IWindow *window) const = 0;
+
+	/**
+	 * Informs if the system provides native dialogs (eg. confirm quit)
+	 */
+	virtual bool supportsNativeDialogs(void) = 0;
+
 protected:
 	/**
 	 * Initialize the system.
diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp
index 2fe94171cf8eef43bef3d83e3defed4dba2be98e..c89ef49de33b8e9aca998559dfbad5422df7771f 100644
--- a/intern/ghost/intern/GHOST_C-api.cpp
+++ b/intern/ghost/intern/GHOST_C-api.cpp
@@ -921,6 +921,11 @@ int GHOST_toggleConsole(int action)
 	return system->toggleConsole(action);
 }
 
+int GHOST_SupportsNativeDialogs(void)
+{
+	GHOST_ISystem *system = GHOST_ISystem::getSystem();
+	return system->supportsNativeDialogs();
+}
 
 int GHOST_confirmQuit(GHOST_WindowHandle windowhandle)
 {
diff --git a/intern/ghost/intern/GHOST_ContextWGL.h b/intern/ghost/intern/GHOST_ContextWGL.h
index b3b66c5f6e209a44e904c5412a3252203496c8a1..6f575db73c632b184b44128dfeaaa248e42f5488 100644
--- a/intern/ghost/intern/GHOST_ContextWGL.h
+++ b/intern/ghost/intern/GHOST_ContextWGL.h
@@ -167,8 +167,6 @@ private:
 
 	static HGLRC s_sharedHGLRC;
 	static int   s_sharedCount;
-
-	static bool s_singleContextMode;
 };
 
 #endif  // __GHOST_CONTEXTWGL_H__
diff --git a/intern/ghost/intern/GHOST_System.cpp b/intern/ghost/intern/GHOST_System.cpp
index 56d68b98ce0d1a4c2b2b4e1178bcd6846e727d2b..4db2f0616d7a2d4641cf480213eeb76742e618bf 100644
--- a/intern/ghost/intern/GHOST_System.cpp
+++ b/intern/ghost/intern/GHOST_System.cpp
@@ -380,6 +380,11 @@ int GHOST_System::confirmQuit(GHOST_IWindow * /*window*/) const
 	return 1;
 }
 
+bool GHOST_System::supportsNativeDialogs(void)
+{
+	return 1;
+}
+
 bool GHOST_System::useNativePixel(void)
 {
 	m_nativePixel = true;
diff --git a/intern/ghost/intern/GHOST_System.h b/intern/ghost/intern/GHOST_System.h
index af083996d91b6865003b684047ac6feb2672466b..50c893b11136fd4809c5588f63020418a58b9173 100644
--- a/intern/ghost/intern/GHOST_System.h
+++ b/intern/ghost/intern/GHOST_System.h
@@ -319,6 +319,10 @@ public:
 	 */
 	virtual int confirmQuit(GHOST_IWindow *window) const;
 
+	/**
+	 * Informs if the system provides native dialogs (eg. confirm quit)
+	 */
+	virtual bool supportsNativeDialogs(void);
 
 	
 protected:
diff --git a/intern/ghost/intern/GHOST_SystemCocoa.h b/intern/ghost/intern/GHOST_SystemCocoa.h
index 62d9774d81d97165a27b34f5a0bf3e5a6f1b638b..8586c91b615a626f5f6b7f4ce490fdd4ce67c385 100644
--- a/intern/ghost/intern/GHOST_SystemCocoa.h
+++ b/intern/ghost/intern/GHOST_SystemCocoa.h
@@ -280,6 +280,11 @@ public:
 	 */
 	GHOST_TSuccess handleKeyEvent(void *eventPtr);
 	
+	/**
+	 * Informs if the system provides native dialogs (eg. confirm quit)
+	 */
+	virtual bool supportsNativeDialogs(void);
+
 protected:
 	/**
 	 * Initializes the system.
diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm
index 011719ea946f90ea5429e1ddcf968695c21e847e..43ed30bd4150d4755ca47f6d8b4ba438451f98a5 100644
--- a/intern/ghost/intern/GHOST_SystemCocoa.mm
+++ b/intern/ghost/intern/GHOST_SystemCocoa.mm
@@ -1710,3 +1710,9 @@ void GHOST_SystemCocoa::putClipboard(GHOST_TInt8 *buffer, bool selection) const
 
 	[pool drain];
 }
+
+bool
+GHOST_SystemCocoa::supportsNativeDialogs(void)
+{
+	return false;
+}
diff --git a/intern/ghost/intern/GHOST_SystemNULL.h b/intern/ghost/intern/GHOST_SystemNULL.h
index 868416cd2275fbe06686cd3e91a56c70dc2b2d00..7c8d26d7486706d29b01c560b219364c8efd5442 100644
--- a/intern/ghost/intern/GHOST_SystemNULL.h
+++ b/intern/ghost/intern/GHOST_SystemNULL.h
@@ -52,6 +52,7 @@ public:
 	GHOST_TSuccess setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y) { return GHOST_kFailure; }
 	void getMainDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const { /* nop */ }
 	void getAllDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const { /* nop */ }
+	bool supportsNativeDialogs(void) { return false;}
 
 	GHOST_TSuccess init() {
 		GHOST_TSuccess success = GHOST_System::init();
diff --git a/intern/ghost/intern/GHOST_SystemSDL.cpp b/intern/ghost/intern/GHOST_SystemSDL.cpp
index e9768e4b8452ed62f74e02d3c983f0d6bf9d48d8..d78605773388bea52011fc98220fe3143d0e0e54 100644
--- a/intern/ghost/intern/GHOST_SystemSDL.cpp
+++ b/intern/ghost/intern/GHOST_SystemSDL.cpp
@@ -635,6 +635,11 @@ GHOST_SystemSDL::addDirtyWindow(GHOST_WindowSDL *bad_wind)
 	m_dirty_windows.push_back(bad_wind);
 }
 
+bool
+GHOST_SystemSDL::supportsNativeDialogs(void)
+{
+	return false;
+}
 
 GHOST_TSuccess GHOST_SystemSDL::getButtons(GHOST_Buttons& buttons) const
 {
diff --git a/intern/ghost/intern/GHOST_SystemSDL.h b/intern/ghost/intern/GHOST_SystemSDL.h
index 6f4ecec586b41f7d306cc866a1f8e50f250bf148..41f110ed15d57f79bbf7b02fc830f1378d8b9df9 100644
--- a/intern/ghost/intern/GHOST_SystemSDL.h
+++ b/intern/ghost/intern/GHOST_SystemSDL.h
@@ -95,6 +95,10 @@ public:
 	getMainDisplayDimensions(GHOST_TUns32& width,
 	                         GHOST_TUns32& height) const;
 
+	/**
+	 * Informs if the system provides native dialogs (eg. confirm quit)
+	 */
+	virtual bool supportsNativeDialogs(void);
 private:
 
 	GHOST_TSuccess
diff --git a/intern/ghost/intern/GHOST_SystemWin32.h b/intern/ghost/intern/GHOST_SystemWin32.h
index 6bf2c5b8d6f5742ecdf17a4fbb77a0d4be137883..0f15f9180ae4ec5ae9fdb41702950ff67b7c9187 100644
--- a/intern/ghost/intern/GHOST_SystemWin32.h
+++ b/intern/ghost/intern/GHOST_SystemWin32.h
@@ -223,10 +223,10 @@ public:
 	 */
 	static GHOST_TSuccess pushDragDropEvent(GHOST_TEventType eventType, GHOST_TDragnDropTypes draggedObjectType, GHOST_WindowWin32 *window, int mouseX, int mouseY, void *data);
 	
-/**
- * Confirms quitting he program when there is just one window left open
- * in the application
- */
+	/**
+	 * Confirms quitting he program when there is just one window left open
+	 * in the application
+	 */
 	int confirmQuit(GHOST_IWindow *window) const;
 
 protected:
diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp
index 0de6e2f7d4ac638acefc4437e17f37db6d905e37..69aa3a09977ca7ff456a1a25986716592ecd8527 100644
--- a/intern/ghost/intern/GHOST_SystemX11.cpp
+++ b/intern/ghost/intern/GHOST_SystemX11.cpp
@@ -386,6 +386,10 @@ createWindow(const STR_String& title,
 	return window;
 }
 
+bool GHOST_SystemX11::supportsNativeDialogs(void) 
+{
+	return false;
+}
 
 /**
  * Create a new offscreen context.
diff --git a/intern/ghost/intern/GHOST_SystemX11.h b/intern/ghost/intern/GHOST_SystemX11.h
index 1ad8277a43117d17874627ff21a6881de1d55032..b910589a1ad3691b2c3700560ad1e58b95865390 100644
--- a/intern/ghost/intern/GHOST_SystemX11.h
+++ b/intern/ghost/intern/GHOST_SystemX11.h
@@ -97,6 +97,10 @@ public:
 	init(
 	    );
 
+	/**
+	 * Informs if the system provides native dialogs (eg. confirm quit)
+	 */
+	virtual bool supportsNativeDialogs(void);
 
 	/**
 	 * \section Interface Inherited from GHOST_ISystem
diff --git a/intern/ghost/test/CMakeLists.txt b/intern/ghost/test/CMakeLists.txt
index ef6e8915871b890f9c00b677b87ab250a9a40f08..0cec217630c330a23e31bec8e0e8df18f03459e8 100644
--- a/intern/ghost/test/CMakeLists.txt
+++ b/intern/ghost/test/CMakeLists.txt
@@ -203,6 +203,7 @@ target_link_libraries(gears_c
 		glewmx_lib
 		string_lib
 		${OPENGL_gl_LIBRARY}
+		${CMAKE_DL_LIBS}
 		${PLATFORM_LINKLIBS}
 		)
 
@@ -216,6 +217,7 @@ target_link_libraries(gears_cpp
 		glewmx_lib
 		string_lib
 		${OPENGL_gl_LIBRARY}
+		${CMAKE_DL_LIBS}
 		${PLATFORM_LINKLIBS}
 		)
 
@@ -248,5 +250,6 @@ target_link_libraries(multitest_c
 		${OPENGL_gl_LIBRARY}
 		${FREETYPE_LIBRARY}
 		${ZLIB_LIBRARIES}
+		${CMAKE_DL_LIBS}
 		${PLATFORM_LINKLIBS}
 		)
diff --git a/intern/ghost/test/multitest/MultiTest.c b/intern/ghost/test/multitest/MultiTest.c
index a5b5a511cb20680382bc6c321de8373265fa6264..833b5c720a1ea9088f5c1af895ccbc24719344b2 100644
--- a/intern/ghost/test/multitest/MultiTest.c
+++ b/intern/ghost/test/multitest/MultiTest.c
@@ -949,7 +949,7 @@ int main(int argc, char **argv)
 	MultiTestApp *app;
 
 #ifndef USE_BMF
-	BLF_init(11, 72);
+	BLF_init();
 #endif
 
 	app = multitestapp_new();
diff --git a/intern/guardedalloc/MEM_guardedalloc.h b/intern/guardedalloc/MEM_guardedalloc.h
index a57921223e23d4a75afc7e96f5c379faf7ab4697..65650209711cde215a8176ade19d09cc6f3d9b1d 100644
--- a/intern/guardedalloc/MEM_guardedalloc.h
+++ b/intern/guardedalloc/MEM_guardedalloc.h
@@ -165,8 +165,8 @@ extern "C" {
 	/**
 	 * Are the start/end block markers still correct ?
 	 *
-	 * @retval 0 for correct memory, 1 for corrupted memory. */
-	extern bool (*MEM_check_memory_integrity)(void);
+	 * @retval true for correct memory, false for corrupted memory. */
+	extern bool (*MEM_consistency_check)(void);
 
 	/** Set thread locking functions for safe memory allocation from multiple
 	 * threads, pass NULL pointers to disable thread locking again. */
diff --git a/intern/guardedalloc/intern/mallocn.c b/intern/guardedalloc/intern/mallocn.c
index b973037358b06cbd1d45424bed5549e1aded09aa..a95cc9163c421b4c3cc0ce98e4aec82b6504df80 100644
--- a/intern/guardedalloc/intern/mallocn.c
+++ b/intern/guardedalloc/intern/mallocn.c
@@ -53,7 +53,7 @@ void (*MEM_printmemlist)(void) = MEM_lockfree_printmemlist;
 void (*MEM_callbackmemlist)(void (*func)(void *)) = MEM_lockfree_callbackmemlist;
 void (*MEM_printmemlist_stats)(void) = MEM_lockfree_printmemlist_stats;
 void (*MEM_set_error_callback)(void (*func)(const char *)) = MEM_lockfree_set_error_callback;
-bool (*MEM_check_memory_integrity)(void) = MEM_lockfree_check_memory_integrity;
+bool (*MEM_consistency_check)(void) = MEM_lockfree_consistency_check;
 void (*MEM_set_lock_callback)(void (*lock)(void), void (*unlock)(void)) = MEM_lockfree_set_lock_callback;
 void (*MEM_set_memory_debug)(void) = MEM_lockfree_set_memory_debug;
 size_t (*MEM_get_memory_in_use)(void) = MEM_lockfree_get_memory_in_use;
@@ -119,7 +119,7 @@ void MEM_use_guarded_allocator(void)
 	MEM_callbackmemlist = MEM_guarded_callbackmemlist;
 	MEM_printmemlist_stats = MEM_guarded_printmemlist_stats;
 	MEM_set_error_callback = MEM_guarded_set_error_callback;
-	MEM_check_memory_integrity = MEM_guarded_check_memory_integrity;
+	MEM_consistency_check = MEM_guarded_consistency_check;
 	MEM_set_lock_callback = MEM_guarded_set_lock_callback;
 	MEM_set_memory_debug = MEM_guarded_set_memory_debug;
 	MEM_get_memory_in_use = MEM_guarded_get_memory_in_use;
diff --git a/intern/guardedalloc/intern/mallocn_guarded_impl.c b/intern/guardedalloc/intern/mallocn_guarded_impl.c
index d012cce53345c44df5fc38d09118f1d307a70aeb..d09f1b83ad4e36fef12cf11f0b33c7ba01a2c13c 100644
--- a/intern/guardedalloc/intern/mallocn_guarded_impl.c
+++ b/intern/guardedalloc/intern/mallocn_guarded_impl.c
@@ -282,7 +282,7 @@ static void mem_unlock_thread(void)
 		thread_unlock_callback();
 }
 
-bool MEM_guarded_check_memory_integrity(void)
+bool MEM_guarded_consistency_check(void)
 {
 	const char *err_val = NULL;
 	MemHead *listend;
@@ -292,7 +292,7 @@ bool MEM_guarded_check_memory_integrity(void)
 	
 	err_val = check_memlist(listend);
 
-	return (err_val != NULL);
+	return (err_val == NULL);
 }
 
 
diff --git a/intern/guardedalloc/intern/mallocn_intern.h b/intern/guardedalloc/intern/mallocn_intern.h
index 4b2282ae9ad4ded7c914155e874132da07ec76c3..754a79f08b5ed393d792c1c80fd76dd11fc5f709 100644
--- a/intern/guardedalloc/intern/mallocn_intern.h
+++ b/intern/guardedalloc/intern/mallocn_intern.h
@@ -137,7 +137,7 @@ void MEM_lockfree_printmemlist(void);
 void MEM_lockfree_callbackmemlist(void (*func)(void *));
 void MEM_lockfree_printmemlist_stats(void);
 void MEM_lockfree_set_error_callback(void (*func)(const char *));
-bool MEM_lockfree_check_memory_integrity(void);
+bool MEM_lockfree_consistency_check(void);
 void MEM_lockfree_set_lock_callback(void (*lock)(void), void (*unlock)(void));
 void MEM_lockfree_set_memory_debug(void);
 size_t MEM_lockfree_get_memory_in_use(void);
@@ -166,7 +166,7 @@ void MEM_guarded_printmemlist(void);
 void MEM_guarded_callbackmemlist(void (*func)(void *));
 void MEM_guarded_printmemlist_stats(void);
 void MEM_guarded_set_error_callback(void (*func)(const char *));
-bool MEM_guarded_check_memory_integrity(void);
+bool MEM_guarded_consistency_check(void);
 void MEM_guarded_set_lock_callback(void (*lock)(void), void (*unlock)(void));
 void MEM_guarded_set_memory_debug(void);
 size_t MEM_guarded_get_memory_in_use(void);
diff --git a/intern/guardedalloc/intern/mallocn_lockfree_impl.c b/intern/guardedalloc/intern/mallocn_lockfree_impl.c
index 2899a37a1b28ae4b4e6310a79e853892e32752ef..4c6ba0cb256d0db41017002572372a7dfe376659 100644
--- a/intern/guardedalloc/intern/mallocn_lockfree_impl.c
+++ b/intern/guardedalloc/intern/mallocn_lockfree_impl.c
@@ -469,7 +469,7 @@ void MEM_lockfree_set_error_callback(void (*func)(const char *))
 	error_callback = func;
 }
 
-bool MEM_lockfree_check_memory_integrity(void)
+bool MEM_lockfree_consistency_check(void)
 {
 	return true;
 }
diff --git a/intern/guardedalloc/test/simpletest/memtest.c b/intern/guardedalloc/test/simpletest/memtest.c
index 79d55dd02cc321da6d28b5e48333d93eb8014a0a..d306337d0abd568ba1e3e09ba3082607b7391455 100644
--- a/intern/guardedalloc/test/simpletest/memtest.c
+++ b/intern/guardedalloc/test/simpletest/memtest.c
@@ -90,7 +90,7 @@ int main(int argc, char *argv[])
 	if (verbose > 1) MEM_printmemlist();
 
 	/* memory is there: test it */
-	error_status = MEM_check_memory_integrity();
+	error_status = MEM_consistency_check();
 
 	if (verbose) {
 		if (error_status) {
@@ -125,7 +125,7 @@ int main(int argc, char *argv[])
 	ip = (int*) p[6];
 	*(ip+10005) = 0;
 	
-	retval = MEM_check_memory_integrity();
+	retval = MEM_consistency_check();
 
 	/* the test should have failed */
 	error_status |= !retval;		
diff --git a/intern/libmv/CMakeLists.txt b/intern/libmv/CMakeLists.txt
index b67a23b415929b411550ed03f2a0008f268fafb6..bc555fa7ff606ebe34be6dc6c90f9a0c39563c29 100644
--- a/intern/libmv/CMakeLists.txt
+++ b/intern/libmv/CMakeLists.txt
@@ -45,7 +45,7 @@ if(WITH_LIBMV)
 
 	list(APPEND INC
 		${GFLAGS_INCLUDE_DIRS}
-		../../extern/glog/src
+		${GLOG_INCLUDE_DIRS}
 		../../extern/ceres/include
 		../../extern/ceres/config
 		../guardedalloc
diff --git a/intern/libmv/bundle.sh b/intern/libmv/bundle.sh
index d155d0507823e55bc24556ec62e96a33ceaddce6..093095f5f5cfa8e990cefbae02e4857b9f6fbd2c 100755
--- a/intern/libmv/bundle.sh
+++ b/intern/libmv/bundle.sh
@@ -124,7 +124,7 @@ if(WITH_LIBMV)
 
 	list(APPEND INC
 		\${GFLAGS_INCLUDE_DIRS}
-		../../extern/glog/src
+		\${GLOG_INCLUDE_DIRS}
 		../../extern/ceres/include
 		../../extern/ceres/config
 		../guardedalloc
diff --git a/intern/libmv/intern/logging.cc b/intern/libmv/intern/logging.cc
index 863832cb72bfe01c6316f4da80c6e4009b971d50..701881b3971ef87869d7dd4b27b03a4b759b6ac6 100644
--- a/intern/libmv/intern/logging.cc
+++ b/intern/libmv/intern/logging.cc
@@ -24,6 +24,8 @@
  * ***** END GPL LICENSE BLOCK *****
  */
 
+#include <gflags/gflags.h>
+
 #include "intern/logging.h"
 #include "intern/utildefines.h"
 #include "libmv/logging/logging.h"
diff --git a/intern/openvdb/CMakeLists.txt b/intern/openvdb/CMakeLists.txt
index e0ecdb52929bae42c343411750869ad07280925d..f666dc78e75b69936f36f1142eca12d821d84b25 100644
--- a/intern/openvdb/CMakeLists.txt
+++ b/intern/openvdb/CMakeLists.txt
@@ -40,6 +40,12 @@ if(WITH_OPENVDB)
 		-DWITH_OPENVDB
 	)
 
+	if(WITH_OPENVDB_3_ABI_COMPATIBLE)
+		add_definitions(
+			-DOPENVDB_3_ABI_COMPATIBLE
+		)
+	endif()
+
 	list(APPEND INC_SYS
 		${BOOST_INCLUDE_DIR}
 		${TBB_INCLUDE_DIRS}
diff --git a/intern/openvdb/intern/openvdb_writer.cc b/intern/openvdb/intern/openvdb_writer.cc
index e886c5a76a8c2cfbd81b425c76901bb7c0b47ace..bedcfe6555283a45fbcedb58bc543a605656d2a8 100644
--- a/intern/openvdb/intern/openvdb_writer.cc
+++ b/intern/openvdb/intern/openvdb_writer.cc
@@ -45,7 +45,7 @@ void OpenVDBWriter::insert(const openvdb::GridBase::Ptr &grid)
 
 void OpenVDBWriter::insert(const openvdb::GridBase &grid)
 {
-#if (OPENVDB_LIBRARY_MAJOR_VERSION_NUMBER == 3)
+#if (OPENVDB_LIBRARY_MAJOR_VERSION_NUMBER <= 3) || defined(OPENVDB_3_ABI_COMPATIBLE)
 	m_grids->push_back(grid.copyGrid());
 #else
 	m_grids->push_back(grid.copyGridWithNewTree());
diff --git a/release/scripts/modules/bpy_extras/object_utils.py b/release/scripts/modules/bpy_extras/object_utils.py
index 31938f3ad3ebf3b34bdc3fb088de9c41ebc57f6f..04b3858bb0d82fb2cf1ef1f0dd81283962b00f2b 100644
--- a/release/scripts/modules/bpy_extras/object_utils.py
+++ b/release/scripts/modules/bpy_extras/object_utils.py
@@ -119,7 +119,6 @@ def object_data_add(context, obdata, operator=None, name=None):
     :return: the newly created object in the scene.
     :rtype: :class:`bpy.types.Object`
     """
-    workspace = context.workspace
     scene = context.scene
     layer = context.view_layer
     layer_collection = context.layer_collection
@@ -147,9 +146,9 @@ def object_data_add(context, obdata, operator=None, name=None):
     # caused because entering edit-mode does not add a empty undo slot!
     if context.user_preferences.edit.use_enter_edit_mode:
         if not (obj_act and
-                obj_act.type == obj_new.type and
-                workspace.object_mode == 'EDIT'
-        ):
+                obj_act.mode == 'EDIT' and
+                obj_act.type == obj_new.type):
+
             _obdata = bpy.data.meshes.new(name)
             obj_act = bpy.data.objects.new(_obdata.name, _obdata)
             obj_act.matrix_world = obj_new.matrix_world
@@ -160,10 +159,7 @@ def object_data_add(context, obdata, operator=None, name=None):
             bpy.ops.ed.undo_push(message="Enter Editmode")
     # XXX
 
-    if (obj_act and
-        obj_act.type == obj_new.type and
-        workspace.object_mode == 'EDIT'
-    ):
+    if obj_act and obj_act.mode == 'EDIT' and obj_act.type == obj_new.type:
         bpy.ops.mesh.select_all(action='DESELECT')
         obj_act.select_set(action='SELECT')
         bpy.ops.object.mode_set(mode='OBJECT')
@@ -253,10 +249,9 @@ def object_image_guess(obj, bm=None):
     first checking the texture-faces, then the material.
     """
     # TODO, cycles/nodes materials
-    workspace = context.workspace
     me = obj.data
     if bm is None:
-        if workspace.object_mode == 'EDIT':
+        if obj.mode == 'EDIT':
             import bmesh
             bm = bmesh.from_edit_mesh(me)
 
diff --git a/release/scripts/modules/keyingsets_utils.py b/release/scripts/modules/keyingsets_utils.py
index 40e74e27ed2112430c6ce278bb986a2a1ffdfc84..7ce5f3e029b6f19db85161d37a776a497a14f7d1 100644
--- a/release/scripts/modules/keyingsets_utils.py
+++ b/release/scripts/modules/keyingsets_utils.py
@@ -57,10 +57,9 @@ def path_add_property(path, prop):
 
 # selected objects (active object must be in object mode)
 def RKS_POLL_selected_objects(ksi, context):
-    workspace = context.workspace
     ob = context.active_object
     if ob:
-        return workspace.object_mode == 'OBJECT'
+        return ob.mode == 'OBJECT'
     else:
         return bool(context.selected_objects)
 
@@ -68,9 +67,8 @@ def RKS_POLL_selected_objects(ksi, context):
 # selected bones
 def RKS_POLL_selected_bones(ksi, context):
     # we must be in Pose Mode, and there must be some bones selected
-    workspace = context.workspace
     ob = context.active_object
-    if ob and workspace.object_mode == 'POSE':
+    if ob and ob.mode == 'POSE':
         if context.active_pose_bone or context.selected_pose_bones:
             return True
 
@@ -89,9 +87,8 @@ def RKS_POLL_selected_items(ksi, context):
 
 # all selected objects or pose bones, depending on which we've got
 def RKS_ITER_selected_item(ksi, context, ks):
-    workspace = context.workspace
     ob = context.active_object
-    if ob and workspace.object_mode == 'POSE':
+    if ob and ob.mode == 'POSE':
         for bone in context.selected_pose_bones:
             ksi.generate(context, ks, bone)
     else:
diff --git a/release/scripts/startup/bl_operators/bmesh/find_adjacent.py b/release/scripts/startup/bl_operators/bmesh/find_adjacent.py
index 77e590fd8ce768ece592e166fce3575ce4086f3f..1555f465aa567d4f57451372c4d8b884558f4611 100644
--- a/release/scripts/startup/bl_operators/bmesh/find_adjacent.py
+++ b/release/scripts/startup/bl_operators/bmesh/find_adjacent.py
@@ -113,7 +113,7 @@ def elems_depth_search(ele_init, depths, other_edges_over_cb, results_init=None)
     else:
         test_ele = {
             v for v, depth in vert_depths.items()
-            if depth >= depth_min for e in v.link_edges if not e.is_wire}
+            if depth >= depth_min}
 
     result_ele = set()
 
@@ -203,6 +203,7 @@ def find_next(ele_dst, ele_src):
     candidates = elems_depth_search(ele_dst, depth_src_a, other_edges_over_edge)
     candidates = elems_depth_search(ele_dst, depth_src_b, other_edges_over_face, candidates)
     candidates.discard(ele_src)
+    candidates.discard(ele_dst)
     if not candidates:
         return []
 
@@ -217,6 +218,8 @@ def find_next(ele_dst, ele_src):
     for ele_test in candidates:
         depth_test_a = elems_depth_measure(ele_dst, ele_test, other_edges_over_edge)
         depth_test_b = elems_depth_measure(ele_dst, ele_test, other_edges_over_face)
+        if depth_test_a is None or depth_test_b is None:
+            continue
         depth_test = tuple(zip(depth_test_a, depth_test_b))
         # square so a few high values win over many small ones
         diff_test = sum((abs(a[0] - b[0]) ** 2) +
@@ -237,9 +240,12 @@ def find_next(ele_dst, ele_src):
         ele_best_ls = []
         depth_accum_max = -1
         for ele_test in ele_best_ls_init:
+            depth_test_a = elems_depth_measure(ele_src, ele_test, other_edges_over_edge)
+            depth_test_b = elems_depth_measure(ele_src, ele_test, other_edges_over_face)
+            if depth_test_a is None or depth_test_b is None:
+                continue
             depth_accum_test = (
-                sum(elems_depth_measure(ele_src, ele_test, other_edges_over_edge)) +
-                sum(elems_depth_measure(ele_src, ele_test, other_edges_over_face)))
+                sum(depth_test_a) + sum(depth_test_b))
 
             if depth_accum_test > depth_accum_max:
                 depth_accum_max = depth_accum_test
diff --git a/release/scripts/startup/bl_operators/clip.py b/release/scripts/startup/bl_operators/clip.py
index 467512a9746208713294b763f2cae4de19dbb752..e67b275a3ee34b76c222eb7a5eeb4819f3f97f91 100644
--- a/release/scripts/startup/bl_operators/clip.py
+++ b/release/scripts/startup/bl_operators/clip.py
@@ -301,20 +301,21 @@ class CLIP_OT_bundles_to_mesh(Operator):
             reconstructed_matrix = reconstruction.cameras.matrix_from_frame(framenr)
             matrix = camera.matrix_world * reconstructed_matrix.inverted()
 
-        mesh = bpy.data.meshes.new(name="Tracks")
         for track in tracking_object.tracks:
-            if track.has_bundle and track.select == True:
+            if track.has_bundle and track.select:
                 new_verts.append(track.bundle)
 
         if new_verts:
+            mesh = bpy.data.meshes.new(name="Tracks")
             mesh.vertices.add(len(new_verts))
             mesh.vertices.foreach_set("co", unpack_list(new_verts))
-
-        ob = bpy.data.objects.new(name="Tracks", object_data=mesh)
-
-        ob.matrix_world = matrix
-
-        context.scene.objects.link(ob)
+            ob = bpy.data.objects.new(name="Tracks", object_data=mesh)
+            ob.matrix_world = matrix
+            context.scene.objects.link(ob)
+            ob.select = True
+            context.scene.objects.active = ob
+        else:
+            self.report({'WARNING'}, "No usable tracks selected")
 
         return {'FINISHED'}
 
diff --git a/release/scripts/startup/bl_operators/file.py b/release/scripts/startup/bl_operators/file.py
index 1b51906a0322af95c21ec575bcf9d19b28275feb..4ab8d59f263b0995355a87cddb38402e0a9f2b7e 100644
--- a/release/scripts/startup/bl_operators/file.py
+++ b/release/scripts/startup/bl_operators/file.py
@@ -249,7 +249,61 @@ class WM_OT_previews_batch_clear(Operator):
         return {'FINISHED'}
 
 
+class WM_OT_blend_strings_utf8_validate(Operator):
+    """Check and fix all strings in current .blend file to be valid UTF-8 Unicode (needed for some old, 2.4x area files)"""
+    bl_idname = "wm.blend_strings_utf8_validate"
+    bl_label = "Validate .blend strings"
+    bl_options = {'REGISTER'}
+
+    def validate_strings(self, item, done_items):
+        if item is None:
+            return False
+
+        if item in done_items:
+            return False
+        done_items.add(item)
+
+        if getattr(item, 'library', None) is not None:
+            return False  # No point in checking library data, we cannot fix it anyway...
+
+        changed = False
+        for prop in item.bl_rna.properties:
+            if prop.identifier in {'bl_rna', 'rna_type'}:
+                continue  # Or we'd recurse 'till Hell freezes.
+            if prop.is_readonly:
+                continue
+            if prop.type == 'STRING':
+                val_bytes = item.path_resolve(prop.identifier, False).as_bytes()
+                val_utf8 = val_bytes.decode('utf-8', 'replace')
+                val_bytes_valid = val_utf8.encode('utf-8')
+                if val_bytes_valid != val_bytes:
+                    print("found bad utf8 encoded string %r, fixing to %r (%r)..."
+                          "" % (val_bytes, val_bytes_valid, val_utf8))
+                    setattr(item, prop.identifier, val_utf8)
+                    changed = True
+            elif prop.type == 'POINTER':
+                it = getattr(item, prop.identifier)
+                changed |= self.validate_strings(it, done_items)
+            elif prop.type == 'COLLECTION':
+                for it in getattr(item, prop.identifier):
+                    changed |= self.validate_strings(it, done_items)
+        return changed
+
+    def execute(self, context):
+        changed = False
+        done_items = set()
+        for prop in bpy.data.bl_rna.properties:
+            if prop.type == 'COLLECTION':
+                for it in getattr(bpy.data, prop.identifier):
+                    changed |= self.validate_strings(it, done_items)
+        if changed:
+            self.report({'WARNING'},
+                        "Some strings were fixed, don't forget to save the .blend file to keep those changes")
+        return {'FINISHED'}
+
+
 classes = (
     WM_OT_previews_batch_clear,
     WM_OT_previews_batch_generate,
+    WM_OT_blend_strings_utf8_validate,
 )
diff --git a/release/scripts/startup/bl_operators/freestyle.py b/release/scripts/startup/bl_operators/freestyle.py
index 9b1d054cc15fce8ef39b9d787c9853397e03f577..0cfe78879db569ae42fc67d1ad7fb3d05833e3bc 100644
--- a/release/scripts/startup/bl_operators/freestyle.py
+++ b/release/scripts/startup/bl_operators/freestyle.py
@@ -53,7 +53,6 @@ class SCENE_OT_freestyle_fill_range_by_selection(bpy.types.Operator):
     def execute(self, context):
         import sys
 
-        workspace = context.workspace
         scene = context.scene
         view_layer = scene.view_layers.active
         lineset = view_layer.freestyle_settings.linesets.active
@@ -80,7 +79,7 @@ class SCENE_OT_freestyle_fill_range_by_selection(bpy.types.Operator):
             return {'CANCELLED'}
         # Find selected vertices in editmesh
         ob = context.active_object
-        if ob.type == 'MESH' and workspace.object_mode == 'EDIT' and ob.name != ref.name:
+        if ob.type == 'MESH' and ob.mode == 'EDIT' and ob.name != ref.name:
             bpy.ops.object.mode_set(mode='OBJECT')
             selected_verts = [v for v in ob.data.vertices if v.select]
             bpy.ops.object.mode_set(mode='EDIT')
@@ -144,7 +143,6 @@ class SCENE_OT_freestyle_add_edge_marks_to_keying_set(bpy.types.Operator):
 
     def execute(self, context):
         # active keying set
-        workspace = context.workspace
         scene = context.scene
         ks = scene.keying_sets.active
         if ks is None:
@@ -152,7 +150,7 @@ class SCENE_OT_freestyle_add_edge_marks_to_keying_set(bpy.types.Operator):
             ks.bl_description = ""
         # add data paths to the keying set
         ob = context.active_object
-        ob_mode = workspace.object_mode
+        ob_mode = ob.mode
         mesh = ob.data
         bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
         for i, edge in enumerate(mesh.edges):
@@ -176,7 +174,6 @@ class SCENE_OT_freestyle_add_face_marks_to_keying_set(bpy.types.Operator):
 
     def execute(self, context):
         # active keying set
-        workspace = context.workspace
         scene = context.scene
         ks = scene.keying_sets.active
         if ks is None:
@@ -184,7 +181,7 @@ class SCENE_OT_freestyle_add_face_marks_to_keying_set(bpy.types.Operator):
             ks.bl_description = ""
         # add data paths to the keying set
         ob = context.active_object
-        ob_mode = workspace.object_mode
+        ob_mode = ob.mode
         mesh = ob.data
         bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
         for i, polygon in enumerate(mesh.polygons):
diff --git a/release/scripts/startup/bl_operators/mesh.py b/release/scripts/startup/bl_operators/mesh.py
index ccc592e80b83a18807eb93f6544271280c51c647..c7a11c23c3f2820a456b4a30c9ae2c63a97a8e1c 100644
--- a/release/scripts/startup/bl_operators/mesh.py
+++ b/release/scripts/startup/bl_operators/mesh.py
@@ -57,9 +57,8 @@ class MeshMirrorUV(Operator):
         precision = self.precision
         double_warn = 0
 
-        workspace = context.workspace
         ob = context.active_object
-        is_editmode = (workspace.object_mode == 'EDIT')
+        is_editmode = (ob.mode == 'EDIT')
         if is_editmode:
             bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
 
diff --git a/release/scripts/startup/bl_operators/object.py b/release/scripts/startup/bl_operators/object.py
index 58f3afa3d6c1ad824959a4d52c0af18725b336db..566487d9d0e701f99f904ada8fa87e74bfbacba0 100644
--- a/release/scripts/startup/bl_operators/object.py
+++ b/release/scripts/startup/bl_operators/object.py
@@ -63,13 +63,12 @@ class SelectPattern(Operator):
             pattern_match = (lambda a, b:
                              fnmatch.fnmatchcase(a.upper(), b.upper()))
         is_ebone = False
-        workspace = context.workspace
         obj = context.object
-        if obj and workspace.object_mode == 'POSE':
+        if obj and obj.mode == 'POSE':
             items = obj.data.bones
             if not self.extend:
                 bpy.ops.pose.select_all(action='DESELECT')
-        elif obj and obj.type == 'ARMATURE' and workspace.object_mode == 'EDIT':
+        elif obj and obj.type == 'ARMATURE' and obj.mode == 'EDIT':
             items = obj.data.edit_bones
             if not self.extend:
                 bpy.ops.armature.select_all(action='DESELECT')
@@ -249,8 +248,6 @@ class SubdivisionSet(Operator):
         if not relative and level < 0:
             self.level = level = 0
 
-        workspace = context.workspace
-
         def set_object_subd(obj):
             for mod in obj.modifiers:
                 if mod.type == 'MULTIRES':
@@ -260,18 +257,18 @@ class SubdivisionSet(Operator):
                             for i in range(sub):
                                 bpy.ops.object.multires_subdivide(modifier="Multires")
 
-                        if workspace.object_mode == 'SCULPT':
+                        if obj.mode == 'SCULPT':
                             if mod.sculpt_levels != level:
                                 mod.sculpt_levels = level
-                        elif workspace.object_mode == 'OBJECT':
+                        elif obj.mode == 'OBJECT':
                             if mod.levels != level:
                                 mod.levels = level
                         return
                     else:
-                        if workspace.object_mode == 'SCULPT':
+                        if obj.mode == 'SCULPT':
                             if mod.sculpt_levels + level <= mod.total_levels:
                                 mod.sculpt_levels += level
-                        elif workspace.object_mode == 'OBJECT':
+                        elif obj.mode == 'OBJECT':
                             if mod.levels + level <= mod.total_levels:
                                 mod.levels += level
                         return
@@ -287,7 +284,7 @@ class SubdivisionSet(Operator):
 
             # add a new modifier
             try:
-                if workspace.object_mode == 'SCULPT':
+                if obj.mode == 'SCULPT':
                     mod = obj.modifiers.new("Multires", 'MULTIRES')
                     if level > 0:
                         for i in range(0, level):
@@ -470,9 +467,8 @@ class ShapeTransfer(Operator):
 
     @classmethod
     def poll(cls, context):
-        workspace = context.workspace
         obj = context.active_object
-        return (obj and workspace.object_mode != 'EDIT')
+        return (obj and obj.mode != 'EDIT')
 
     def execute(self, context):
         ob_act = context.active_object
@@ -512,11 +508,10 @@ class JoinUVs(Operator):
 
     def _main(self, context):
         import array
-        workspace = context.workspace
         obj = context.active_object
         mesh = obj.data
 
-        is_editmode = (workspace.object_mode == 'EDIT')
+        is_editmode = (obj.mode == 'EDIT')
         if is_editmode:
             bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
 
diff --git a/release/scripts/startup/bl_operators/object_quick_effects.py b/release/scripts/startup/bl_operators/object_quick_effects.py
index 91a0120f6ac2a288fd2e0eceb5eb11ef4a5093dc..27cc118d99d2b258c0e3ab170c1e19ccca267664 100644
--- a/release/scripts/startup/bl_operators/object_quick_effects.py
+++ b/release/scripts/startup/bl_operators/object_quick_effects.py
@@ -73,10 +73,9 @@ class QuickFur(Operator):
             )
 
     def execute(self, context):
-        workspace = context.workspace
         fake_context = context.copy()
         mesh_objects = [obj for obj in context.selected_objects
-                        if obj.type == 'MESH' and workspace.object_mode == 'OBJECT']
+                        if obj.type == 'MESH' and obj.mode == 'OBJECT']
 
         if not mesh_objects:
             self.report({'ERROR'}, "Select at least one mesh object")
diff --git a/release/scripts/startup/bl_operators/uvcalc_lightmap.py b/release/scripts/startup/bl_operators/uvcalc_lightmap.py
index dde98ce9013ea045a33d90f0e0b862214b8d9a52..61ceb3c04c473a2f920523649d777ffc422aace3 100644
--- a/release/scripts/startup/bl_operators/uvcalc_lightmap.py
+++ b/release/scripts/startup/bl_operators/uvcalc_lightmap.py
@@ -558,8 +558,8 @@ def lightmap_uvpack(meshes,
 
 
 def unwrap(operator, context, **kwargs):
-    workspace = context.workspace
-    is_editmode = (workspace.object_mode == 'EDIT')
+
+    is_editmode = (context.object.mode == 'EDIT')
     if is_editmode:
         bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
 
diff --git a/release/scripts/startup/bl_operators/uvcalc_smart_project.py b/release/scripts/startup/bl_operators/uvcalc_smart_project.py
index f648bebed26a48c732ce9f7b7700977d4fe4be94..2578365341404302505dfa4ec78acfb34a302e0d 100644
--- a/release/scripts/startup/bl_operators/uvcalc_smart_project.py
+++ b/release/scripts/startup/bl_operators/uvcalc_smart_project.py
@@ -748,8 +748,7 @@ def main(context,
     USER_FILL_HOLES_QUALITY = 50 # Only for hole filling.
     USER_VIEW_INIT = 0 # Only for hole filling.
 
-    workspace = context.workspace
-    is_editmode = (workspace.object_mode == 'EDIT')
+    is_editmode = (context.active_object.mode == 'EDIT')
     if is_editmode:
         obList =  [ob for ob in [context.active_object] if ob and ob.type == 'MESH']
     else:
@@ -782,7 +781,7 @@ def main(context,
 
 
     # Toggle Edit mode
-    is_editmode = (workspace.object_mode == 'EDIT')
+    is_editmode = (context.active_object.mode == 'EDIT')
     if is_editmode:
         bpy.ops.object.mode_set(mode='OBJECT')
     # Assume face select mode! an annoying hack to toggle face select mode because Mesh doesn't like faceSelectMode.
diff --git a/release/scripts/startup/bl_operators/view3d.py b/release/scripts/startup/bl_operators/view3d.py
index e54ead6a5fc04e0bf12981ab7ac86fa706e032f8..18f911100539a6d6bf24073f32bc65186c5929e5 100644
--- a/release/scripts/startup/bl_operators/view3d.py
+++ b/release/scripts/startup/bl_operators/view3d.py
@@ -30,9 +30,8 @@ class VIEW3D_OT_edit_mesh_extrude_individual_move(Operator):
 
     @classmethod
     def poll(cls, context):
-        workspace = context.workspace
         obj = context.active_object
-        return (obj is not None and workspace.object_mode == 'EDIT')
+        return (obj is not None and obj.mode == 'EDIT')
 
     def execute(self, context):
         mesh = context.object.data
@@ -69,9 +68,8 @@ class VIEW3D_OT_edit_mesh_extrude_move(Operator):
 
     @classmethod
     def poll(cls, context):
-        workspace = context.workspace
         obj = context.active_object
-        return (obj is not None and workspace.object_mode == 'EDIT')
+        return (obj is not None and obj.mode == 'EDIT')
 
     @staticmethod
     def extrude_region(context, use_vert_normals):
@@ -119,9 +117,8 @@ class VIEW3D_OT_edit_mesh_extrude_shrink_fatten(Operator):
 
     @classmethod
     def poll(cls, context):
-        workspace = context.workspace
         obj = context.active_object
-        return (obj is not None and workspace.object_mode == 'EDIT')
+        return (obj is not None and obj.mode == 'EDIT')
 
     def execute(self, context):
         return VIEW3D_OT_edit_mesh_extrude_move.extrude_region(context, True)
@@ -176,8 +173,7 @@ class VIEW3D_OT_select_or_deselect_all(Operator):
     def poll(cls, context):
         active_object = context.active_object
         if active_object:
-            workspace = context.workspace
-            return workspace.object_mode in {'EDIT', 'OBJECT', 'POSE'}
+            return active_object.mode in {'EDIT', 'OBJECT', 'POSE'}
         return True
 
     def invoke(self, context, event):
@@ -188,9 +184,7 @@ class VIEW3D_OT_select_or_deselect_all(Operator):
             active_object = context.active_object
 
             if active_object:
-                workspace = context.workspace
-                object_mode = workspace.object_mode
-                if object_mode == 'EDIT':
+                if active_object.mode == 'EDIT':
                     if active_object.type == 'MESH':
                         bpy.ops.mesh.select_all(action='DESELECT')
                     elif active_object.type == 'CURVE':
@@ -203,9 +197,9 @@ class VIEW3D_OT_select_or_deselect_all(Operator):
                         bpy.ops.mball.select_all(action='DESELECT')
                     elif active_object.type == 'ARMATURE':
                         bpy.ops.armature.select_all(action='DESELECT')
-                elif object_mode == 'POSE':
+                elif active_object.mode == 'POSE':
                     bpy.ops.pose.select_all(action='DESELECT')
-                elif object_mode == 'PARTICLE_EDIT':
+                elif active_object.mode == 'PARTICLE_EDIT':
                     bpy.ops.particle.select_all(action='DESELECT')
                 else:
                     bpy.ops.object.select_all(action='DESELECT')
diff --git a/release/scripts/startup/bl_ui/properties_collection.py b/release/scripts/startup/bl_ui/properties_collection.py
index f093b3732510b19692f3634ddbac964d2a66090e..e48df3a0a610559642e5f067524281ec52bac1ac 100644
--- a/release/scripts/startup/bl_ui/properties_collection.py
+++ b/release/scripts/startup/bl_ui/properties_collection.py
@@ -91,7 +91,8 @@ class COLLECTION_PT_object_mode_settings(CollectionButtonsPanel, Panel):
 
     @classmethod
     def poll(cls, context):
-        return context.workspace.object_mode == 'OBJECT'
+        ob = context.object
+        return ob and ob.mode == 'OBJECT'
 
     def draw(self, context):
         layout = self.layout
@@ -109,7 +110,8 @@ class COLLECTION_PT_edit_mode_settings(CollectionButtonsPanel, Panel):
 
     @classmethod
     def poll(cls, context):
-        return context.workspace.object_mode == 'EDIT'
+        ob = context.object
+        return ob and ob.mode == 'EDIT'
 
     def draw(self, context):
         layout = self.layout
@@ -132,7 +134,8 @@ class COLLECTION_PT_paint_weight_mode_settings(CollectionButtonsPanel, Panel):
 
     @classmethod
     def poll(cls, context):
-        return context.workspace.object_mode == 'WEIGHT_PAINT'
+        ob = context.object
+        return ob and ob.mode == 'WEIGHT_PAINT'
 
     def draw(self, context):
         layout = self.layout
@@ -150,7 +153,8 @@ class COLLECTION_PT_paint_vertex_mode_settings(CollectionButtonsPanel, Panel):
 
     @classmethod
     def poll(cls, context):
-        return context.workspace.object_mode == 'VERTEX_PAINT'
+        ob = context.object
+        return ob and ob.mode == 'VERTEX_PAINT'
 
     def draw(self, context):
         layout = self.layout
diff --git a/release/scripts/startup/bl_ui/properties_constraint.py b/release/scripts/startup/bl_ui/properties_constraint.py
index f374d95c4930e21bd0922d43aa7f99e75a1c2583..9b61101778f25594e47f0cd745118be38b9036e9 100644
--- a/release/scripts/startup/bl_ui/properties_constraint.py
+++ b/release/scripts/startup/bl_ui/properties_constraint.py
@@ -910,9 +910,8 @@ class OBJECT_PT_constraints(ConstraintButtonsPanel, Panel):
         layout = self.layout
 
         obj = context.object
-        workspace = context.workspace
 
-        if obj.type == 'ARMATURE' and workspace.object_mode == 'POSE':
+        if obj.type == 'ARMATURE' and obj.mode == 'POSE':
             box = layout.box()
             box.alert = True  # XXX: this should apply to the box background
             box.label(icon='INFO', text="Constraints for active bone do not live here")
diff --git a/release/scripts/startup/bl_ui/properties_data_bone.py b/release/scripts/startup/bl_ui/properties_data_bone.py
index e8f290772d882780dda4eb880d07dd5972265015..f0ef00320591be7e81d055a870b0e3360c5e8be3 100644
--- a/release/scripts/startup/bl_ui/properties_data_bone.py
+++ b/release/scripts/startup/bl_ui/properties_data_bone.py
@@ -58,8 +58,7 @@ class BONE_PT_transform(BoneButtonsPanel, Panel):
             return True
 
         ob = context.object
-        workspace = context.workspace
-        return ob and workspace.object_mode == 'POSE' and context.bone
+        return ob and ob.mode == 'POSE' and context.bone
 
     def draw(self, context):
         layout = self.layout
@@ -111,8 +110,7 @@ class BONE_PT_transform_locks(BoneButtonsPanel, Panel):
     @classmethod
     def poll(cls, context):
         ob = context.object
-        workspace = context.workspace
-        return ob and workspace.object_mode == 'POSE' and context.bone
+        return ob and ob.mode == 'POSE' and context.bone
 
     def draw(self, context):
         layout = self.layout
@@ -313,8 +311,7 @@ class BONE_PT_inverse_kinematics(BoneButtonsPanel, Panel):
     @classmethod
     def poll(cls, context):
         ob = context.object
-        workspace = context.workspace
-        return ob and workspace.object_mode == 'POSE' and context.bone
+        return ob and ob.mode == 'POSE' and context.bone
 
     def draw(self, context):
         layout = self.layout
@@ -442,8 +439,7 @@ class BONE_PT_custom_props(BoneButtonsPanel, PropertyPanel, Panel):
     @property
     def _context_path(self):
         obj = bpy.context.object
-        workspace = context.workspace
-        if obj and workspace.object_mode == 'POSE':
+        if obj and obj.mode == 'POSE':
             return "active_pose_bone"
         else:
             return "active_bone"
diff --git a/release/scripts/startup/bl_ui/properties_data_mesh.py b/release/scripts/startup/bl_ui/properties_data_mesh.py
index ee6e09039beb44eead95786de49b53192ae740fe..9f927fe33682cc3670c5152115d2bb7c0fdf9512 100644
--- a/release/scripts/startup/bl_ui/properties_data_mesh.py
+++ b/release/scripts/startup/bl_ui/properties_data_mesh.py
@@ -94,13 +94,10 @@ class MESH_UL_shape_keys(UIList):
         # key = data
         key_block = item
         if self.layout_type in {'DEFAULT', 'COMPACT'}:
-            workspace = context.workspace
             split = layout.split(0.66, False)
             split.prop(key_block, "name", text="", emboss=False, icon_value=icon)
             row = split.row(align=True)
-            if (key_block.mute or
-                (workspace.object_mode == 'EDIT' and not (obj.use_shape_key_edit_mode and obj.type == 'MESH'))
-            ):
+            if key_block.mute or (obj.mode == 'EDIT' and not (obj.use_shape_key_edit_mode and obj.type == 'MESH')):
                 row.active = False
             if not item.id_data.use_relative:
                 row.prop(key_block, "frame", text="", emboss=False)
@@ -208,7 +205,6 @@ class DATA_PT_vertex_groups(MeshButtonsPanel, Panel):
     def draw(self, context):
         layout = self.layout
 
-        workspace = context.workspace
         ob = context.object
         group = ob.vertex_groups.active
 
@@ -229,10 +225,7 @@ class DATA_PT_vertex_groups(MeshButtonsPanel, Panel):
             col.operator("object.vertex_group_move", icon='TRIA_UP', text="").direction = 'UP'
             col.operator("object.vertex_group_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
 
-        if (ob.vertex_groups and
-            ((workspace.object_mode == 'EDIT') or
-             (workspace.object_mode == 'WEIGHT_PAINT' and ob.type == 'MESH' and ob.data.use_paint_mask_vertex))
-        ):
+        if ob.vertex_groups and (ob.mode == 'EDIT' or (ob.mode == 'WEIGHT_PAINT' and ob.type == 'MESH' and ob.data.use_paint_mask_vertex)):
             row = layout.row()
 
             sub = row.row(align=True)
@@ -258,7 +251,6 @@ class DATA_PT_face_maps(MeshButtonsPanel, Panel):
     def draw(self, context):
         layout = self.layout
 
-        workspace = context.workspace
         ob = context.object
         facemap = ob.face_maps.active
 
@@ -277,7 +269,7 @@ class DATA_PT_face_maps(MeshButtonsPanel, Panel):
             col.operator("object.face_map_move", icon='TRIA_UP', text="").direction = 'UP'
             col.operator("object.face_map_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
 
-        if ob.face_maps and (workspace.object_mode == 'EDIT' and ob.type == 'MESH'):
+        if ob.face_maps and (ob.mode == 'EDIT' and ob.type == 'MESH'):
             row = layout.row()
 
             sub = row.row(align=True)
@@ -301,12 +293,11 @@ class DATA_PT_shape_keys(MeshButtonsPanel, Panel):
     def draw(self, context):
         layout = self.layout
 
-        workspace = context.workspace
         ob = context.object
         key = ob.data.shape_keys
         kb = ob.active_shape_key
 
-        enable_edit = workspace.object_mode != 'EDIT'
+        enable_edit = ob.mode != 'EDIT'
         enable_edit_value = False
 
         if ob.show_only_shape_key is False:
@@ -428,7 +419,6 @@ class DATA_PT_customdata(MeshButtonsPanel, Panel):
     def draw(self, context):
         layout = self.layout
 
-        workspace = context.workspace
         obj = context.object
         me = context.mesh
         col = layout.column()
@@ -443,7 +433,7 @@ class DATA_PT_customdata(MeshButtonsPanel, Panel):
 
         col = layout.column()
 
-        col.enabled = (workspace.object_mode != 'EDIT')
+        col.enabled = (obj.mode != 'EDIT')
         col.prop(me, "use_customdata_vertex_bevel")
         col.prop(me, "use_customdata_edge_bevel")
         col.prop(me, "use_customdata_edge_crease")
diff --git a/release/scripts/startup/bl_ui/properties_data_modifier.py b/release/scripts/startup/bl_ui/properties_data_modifier.py
index cd29c3f10e7cedcd5ba3d0d5606f857227a7a8cc..4c2e8e036419f4f4926448d5f42f21e587069c27 100644
--- a/release/scripts/startup/bl_ui/properties_data_modifier.py
+++ b/release/scripts/startup/bl_ui/properties_data_modifier.py
@@ -404,8 +404,6 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
         layout.label(text="Settings are inside the Physics tab")
 
     def HOOK(self, layout, ob, md):
-        from bpy import context
-        workspace = context.workspace
         use_falloff = (md.falloff_type != 'NONE')
         split = layout.split()
 
@@ -437,7 +435,7 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
         col = split.column()
         col.prop(md, "use_falloff_uniform")
 
-        if workspace.object_mode == 'EDIT':
+        if ob.mode == 'EDIT':
             row = col.row(align=True)
             row.operator("object.hook_reset", text="Reset")
             row.operator("object.hook_recenter", text="Recenter")
@@ -593,9 +591,6 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
         col.prop(md, "mirror_object", text="")
 
     def MULTIRES(self, layout, ob, md):
-        from bpy import context
-        workspace = context.workspace
-
         layout.row().prop(md, "subdivision_type", expand=True)
 
         split = layout.split()
@@ -606,7 +601,7 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
 
         col = split.column()
 
-        col.enabled = workspace.object_mode != 'EDIT'
+        col.enabled = ob.mode != 'EDIT'
         col.operator("object.multires_subdivide", text="Subdivide")
         col.operator("object.multires_higher_levels_delete", text="Delete Higher")
         col.operator("object.multires_reshape", text="Reshape")
diff --git a/release/scripts/startup/bl_ui/properties_material.py b/release/scripts/startup/bl_ui/properties_material.py
index dfbf3eab2443498e69015922c10a108da07261e0..edbe6816e5b1447577d7ad2426bcf36482f3039e 100644
--- a/release/scripts/startup/bl_ui/properties_material.py
+++ b/release/scripts/startup/bl_ui/properties_material.py
@@ -148,7 +148,7 @@ class MATERIAL_PT_context_material(MaterialButtonsPanel, Panel):
                 col.operator("object.material_slot_move", icon='TRIA_UP', text="").direction = 'UP'
                 col.operator("object.material_slot_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
 
-            if context.workspace.object_mode == 'EDIT':
+            if ob.mode == 'EDIT':
                 row = layout.row(align=True)
                 row.operator("object.material_slot_assign", text="Assign")
                 row.operator("object.material_slot_select", text="Select")
@@ -1094,7 +1094,7 @@ class EEVEE_MATERIAL_PT_context_material(MaterialButtonsPanel, Panel):
                 col.operator("object.material_slot_move", icon='TRIA_UP', text="").direction = 'UP'
                 col.operator("object.material_slot_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
 
-            if context.workspace.object_mode == 'EDIT':
+            if ob.mode == 'EDIT':
                 row = layout.row(align=True)
                 row.operator("object.material_slot_assign", text="Assign")
                 row.operator("object.material_slot_select", text="Select")
diff --git a/release/scripts/startup/bl_ui/properties_render.py b/release/scripts/startup/bl_ui/properties_render.py
index 12b0df795c75991fdbab452c14ec98ed4b16ef79..26fb05363ff3db23987f1cca9a25775091cf630b 100644
--- a/release/scripts/startup/bl_ui/properties_render.py
+++ b/release/scripts/startup/bl_ui/properties_render.py
@@ -395,6 +395,7 @@ class RENDER_PT_stamp(RenderButtonsPanel, Panel):
         col.prop(rd, "use_stamp_camera", text="Camera")
         col.prop(rd, "use_stamp_lens", text="Lens")
         col.prop(rd, "use_stamp_filename", text="Filename")
+        col.prop(rd, "use_stamp_frame_range", text="Frame range")
         col.prop(rd, "use_stamp_marker", text="Marker")
         col.prop(rd, "use_stamp_sequencer_strip", text="Seq. Strip")
 
@@ -459,17 +460,38 @@ class RENDER_PT_encoding(RenderButtonsPanel, Panel):
         split.prop(rd.ffmpeg, "format")
         split.prop(ffmpeg, "use_autosplit")
 
+        # Video:
         layout.separator()
+        self.draw_vcodec(context)
+
+        # Audio:
+        layout.separator()
+        if ffmpeg.format != 'MP3':
+            layout.prop(ffmpeg, "audio_codec", text="Audio Codec")
+
+        if ffmpeg.audio_codec != 'NONE':
+            row = layout.row()
+            row.prop(ffmpeg, "audio_bitrate")
+            row.prop(ffmpeg, "audio_volume", slider=True)
+
+    def draw_vcodec(self, context):
+        """Video codec options."""
+        layout = self.layout
+        ffmpeg = context.scene.render.ffmpeg
 
         needs_codec = ffmpeg.format in {'AVI', 'QUICKTIME', 'MKV', 'OGG', 'MPEG4'}
         if needs_codec:
             layout.prop(ffmpeg, "codec")
 
+        if needs_codec and ffmpeg.codec == 'NONE':
+            return
+
         if ffmpeg.codec in {'DNXHD'}:
             layout.prop(ffmpeg, "use_lossless_output")
 
         # Output quality
-        if needs_codec and ffmpeg.codec in {'H264', 'MPEG4'}:
+        use_crf = needs_codec and ffmpeg.codec in {'H264', 'MPEG4'}
+        if use_crf:
             layout.prop(ffmpeg, "constant_rate_factor")
 
         # Encoding speed
@@ -483,7 +505,7 @@ class RENDER_PT_encoding(RenderButtonsPanel, Panel):
         pbox.prop(ffmpeg, "max_b_frames", text="")
         pbox.enabled = ffmpeg.use_max_b_frames
 
-        if ffmpeg.constant_rate_factor == 'NONE':
+        if not use_crf or ffmpeg.constant_rate_factor == 'NONE':
             split = layout.split()
             col = split.column()
             col.label(text="Rate:")
@@ -497,17 +519,6 @@ class RENDER_PT_encoding(RenderButtonsPanel, Panel):
             col.prop(ffmpeg, "muxrate", text="Rate")
             col.prop(ffmpeg, "packetsize", text="Packet Size")
 
-        layout.separator()
-
-        # Audio:
-        if ffmpeg.format != 'MP3':
-            layout.prop(ffmpeg, "audio_codec", text="Audio Codec")
-
-        if ffmpeg.audio_codec != 'NONE':
-            row = layout.row()
-            row.prop(ffmpeg, "audio_bitrate")
-            row.prop(ffmpeg, "audio_volume", slider=True)
-
 
 class RENDER_PT_bake(RenderButtonsPanel, Panel):
     bl_label = "Bake"
diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py
index 14ecb86e5773d0ad3989500e7c5bfd587dfda6d7..123e95c013ccc02565ecc771936c02fd3d5c9b39 100644
--- a/release/scripts/startup/bl_ui/space_image.py
+++ b/release/scripts/startup/bl_ui/space_image.py
@@ -1208,8 +1208,7 @@ class ImageScopesPanel:
         if sima.mode == 'PAINT':
             return False
         ob = context.active_object
-        workspace = context.workspace
-        if ob and workspace.object_mode in {'TEXTURE_PAINT', 'EDIT'}:
+        if ob and ob.mode in {'TEXTURE_PAINT', 'EDIT'}:
             return False
         return True
 
diff --git a/release/scripts/startup/bl_ui/space_info.py b/release/scripts/startup/bl_ui/space_info.py
index 46741399202f0517b68b186b0a7a05cd0da971cd..5424a7050c70f4b2b2bd2c45676377d7fbca8682 100644
--- a/release/scripts/startup/bl_ui/space_info.py
+++ b/release/scripts/startup/bl_ui/space_info.py
@@ -48,7 +48,7 @@ class INFO_HT_header(Header):
             layout.template_ID(window, "workspace", new="workspace.workspace_add_menu", unlink="workspace.workspace_delete")
             layout.template_search_preview(window, "screen", workspace, "screens", new="screen.new", unlink="screen.delete", rows=2, cols=6)
 
-        act_mode_item = bpy.types.WorkSpace.bl_rna.properties['object_mode'].enum_items[workspace.object_mode]
+        act_mode_item = bpy.types.Object.bl_rna.properties["mode"].enum_items[layer.objects.active.mode]
         layout.operator_menu_enum("object.mode_set", "mode", text=act_mode_item.name, icon=act_mode_item.icon)
 
         row = layout.row()
@@ -84,7 +84,7 @@ class INFO_HT_header(Header):
             return
 
         row.operator("wm.splash", text="", icon='BLENDER', emboss=False)
-        row.label(text=scene.statistics(workspace, context.view_layer), translate=False)
+        row.label(text=scene.statistics(context.view_layer), translate=False)
 
 
 class INFO_MT_editor_menus(Menu):
@@ -168,6 +168,7 @@ class INFO_MT_file(Menu):
         layout.separator()
 
         layout.menu("INFO_MT_file_external_data", icon='EXTERNAL_DATA')
+        layout.operator("wm.blend_strings_utf8_validate", icon='FILE_BLEND')
 
         layout.separator()
 
diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py
index 918ce0da32ff4cce3ad12be01f58240fd23362b0..0697cf8e56b9ef2bbc65ff52a4dac7d9fdcfce77 100644
--- a/release/scripts/startup/bl_ui/space_sequencer.py
+++ b/release/scripts/startup/bl_ui/space_sequencer.py
@@ -584,6 +584,8 @@ class SEQUENCER_PT_edit(SequencerButtonsPanel, Panel):
 
         if elem and elem.orig_width > 0 and elem.orig_height > 0:
             col.label(text=iface_("Original Dimension: %dx%d") % (elem.orig_width, elem.orig_height), translate=False)
+        else:
+            col.label(text="Original Dimension: None")
 
 
 class SEQUENCER_PT_effect(SequencerButtonsPanel, Panel):
diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py
index b1dadbab6ca52dd3803903a2806b661d9099b3a3..69382e79758759db7d7a042f8a992fc4fe14df91 100644
--- a/release/scripts/startup/bl_ui/space_userpref.py
+++ b/release/scripts/startup/bl_ui/space_userpref.py
@@ -215,7 +215,6 @@ class USERPREF_PT_interface(Panel):
         return (userpref.active_section == 'INTERFACE')
 
     def draw(self, context):
-        import sys
         layout = self.layout
 
         userpref = context.user_preferences
@@ -247,9 +246,8 @@ class USERPREF_PT_interface(Panel):
 
         col.separator()
 
-        if sys.platform[:3] == "win":
-            col.label("Warnings")
-            col.prop(view, "use_quit_dialog")
+        col.label("Warnings")
+        col.prop(view, "use_quit_dialog")
 
         row.separator()
         row.separator()
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index a4c452857f26552ab956d4f4d41b21937a577b20..7f898cd065ef31accc4403cdd912911e403999c6 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -48,7 +48,7 @@ class VIEW3D_HT_header(Header):
         layout.template_header_3D()
 
         if obj:
-            mode = context.workspace.object_mode
+            mode = obj.mode
             # Particle edit
             if mode == 'PARTICLE_EDIT':
                 row.prop(toolsettings.particle_edit, "select_mode", text="", expand=True)
@@ -317,9 +317,8 @@ class VIEW3D_MT_transform_armature(VIEW3D_MT_transform_base):
         VIEW3D_MT_transform_base.draw(self, context)
 
         # armature specific extensions follow...
-        workspace = context.workspace
         obj = context.object
-        if obj.type == 'ARMATURE' and workspace.object_mode in {'EDIT', 'POSE'}:
+        if obj.type == 'ARMATURE' and obj.mode in {'EDIT', 'POSE'}:
             if obj.data.draw_type == 'BBONE':
                 layout.separator()
 
@@ -1956,10 +1955,7 @@ class VIEW3D_MT_vertex_group(Menu):
         layout.operator("object.vertex_group_assign_new")
 
         ob = context.active_object
-        workspace = context.workspace
-        if ((workspace.object_mode == 'EDIT') or
-            (workspace.object_mode == 'WEIGHT_PAINT' and ob.type == 'MESH' and ob.data.use_paint_mask_vertex)
-        ):
+        if ob.mode == 'EDIT' or (ob.mode == 'WEIGHT_PAINT' and ob.type == 'MESH' and ob.data.use_paint_mask_vertex):
             if ob.vertex_groups.active:
                 layout.separator()
 
@@ -2662,8 +2658,8 @@ class VIEW3D_MT_edit_mesh_vertices(Menu):
         layout.operator("object.vertex_parent_set")
 
 
-class VIEW3D_MT_edit_mesh_edges(Menu):
-    bl_label = "Edges"
+class VIEW3D_MT_edit_mesh_edges_data(Menu):
+    bl_label = "Edge Data"
 
     def draw(self, context):
         layout = self.layout
@@ -2672,13 +2668,6 @@ class VIEW3D_MT_edit_mesh_edges(Menu):
 
         layout.operator_context = 'INVOKE_REGION_WIN'
 
-        layout.operator("mesh.edge_face_add")
-        layout.operator("mesh.subdivide")
-        layout.operator("mesh.subdivide_edgering")
-        layout.operator("mesh.unsubdivide")
-
-        layout.separator()
-
         layout.operator("transform.edge_crease")
         layout.operator("transform.edge_bevelweight")
 
@@ -2699,6 +2688,26 @@ class VIEW3D_MT_edit_mesh_edges(Menu):
             layout.operator("mesh.mark_freestyle_edge", text="Clear Freestyle Edge").clear = True
             layout.separator()
 
+
+class VIEW3D_MT_edit_mesh_edges(Menu):
+    bl_label = "Edges"
+
+    def draw(self, context):
+        layout = self.layout
+
+        layout.operator_context = 'INVOKE_REGION_WIN'
+
+        layout.operator("mesh.edge_face_add")
+        layout.operator("mesh.subdivide")
+        layout.operator("mesh.subdivide_edgering")
+        layout.operator("mesh.unsubdivide")
+
+        layout.separator()
+
+        layout.menu("VIEW3D_MT_edit_mesh_edges_data")
+
+        layout.separator()
+
         layout.operator("mesh.edge_rotate", text="Rotate Edge CW").use_ccw = False
         layout.operator("mesh.edge_rotate", text="Rotate Edge CCW").use_ccw = True
 
@@ -3392,7 +3401,7 @@ class VIEW3D_PT_view3d_properties(Panel):
         if lock_object:
             if lock_object.type == 'ARMATURE':
                 col.prop_search(view, "lock_bone", lock_object.data,
-                                "edit_bones" if context.mode == 'EDIT_ARMATURE'
+                                "edit_bones" if lock_object.mode == 'EDIT'
                                 else "bones",
                                 text="")
         else:
@@ -3445,13 +3454,12 @@ class VIEW3D_PT_view3d_name(Panel):
     def draw(self, context):
         layout = self.layout
 
-        workspace = context.workspace
         ob = context.active_object
         row = layout.row()
         row.label(text="", icon='OBJECT_DATA')
         row.prop(ob, "name", text="")
 
-        if ob.type == 'ARMATURE' and workspace.object_mode in {'EDIT', 'POSE'}:
+        if ob.type == 'ARMATURE' and ob.mode in {'EDIT', 'POSE'}:
             bone = context.active_bone
             if bone:
                 row = layout.row()
@@ -3769,8 +3777,7 @@ class VIEW3D_PT_etch_a_ton(Panel):
     def poll(cls, context):
         scene = context.space_data
         ob = context.active_object
-        workspace = context.workspace
-        return scene and ob and (ob.type == 'ARMATURE') and (workspace.object_mode == 'EDIT')
+        return scene and ob and ob.type == 'ARMATURE' and ob.mode == 'EDIT'
 
     def draw_header(self, context):
         layout = self.layout
@@ -3826,8 +3833,7 @@ class VIEW3D_PT_context_properties(Panel):
     def _active_context_member(context):
         obj = context.object
         if obj:
-            workspace = context.workspace
-            mode = workspace.object_mode
+            mode = obj.mode
             if mode == 'POSE':
                 return "active_pose_bone"
             elif mode == 'EDIT' and obj.type == 'ARMATURE':
@@ -3954,6 +3960,7 @@ classes = (
     VIEW3D_MT_edit_mesh_extrude,
     VIEW3D_MT_edit_mesh_vertices,
     VIEW3D_MT_edit_mesh_edges,
+    VIEW3D_MT_edit_mesh_edges_data,
     VIEW3D_MT_edit_mesh_faces,
     VIEW3D_MT_edit_mesh_normals,
     VIEW3D_MT_edit_mesh_clean,
diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
index 7a3b0f26edc41362fb3d3dcc7a77e4e69002b8f3..e29971e18353a15c8d2c9ebae4f7d105835fe21c 100644
--- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
@@ -2051,7 +2051,7 @@ class VIEW3D_PT_tools_history(View3DPanel, Panel):
         row = col.row(align=True)
         row.operator("ed.undo")
         row.operator("ed.redo")
-        if obj is None or workspace.object_mode != 'SCULPT':
+        if obj is None or obj.mode != 'SCULPT':
             # Sculpt mode does not generate an undo menu it seems...
             col.operator("ed.undo_history")
 
diff --git a/release/scripts/startup/keyingsets_builtins.py b/release/scripts/startup/keyingsets_builtins.py
index 0a6ece6eb5a8579e2d8f24a06d88512309406cae..390c043bb31c94976aff58433ab58916667e0805 100644
--- a/release/scripts/startup/keyingsets_builtins.py
+++ b/release/scripts/startup/keyingsets_builtins.py
@@ -388,9 +388,8 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo):
 
     # poll - pose-mode on active object only
     def poll(ksi, context):
-        workspace = context.workspace
-        ob = context.active_object
-        return (ob and ob.pose and (workspace.object_mode == 'POSE'))
+        return ((context.active_object) and (context.active_object.pose) and
+                (context.active_object.mode == 'POSE'))
 
     # iterator - all bones regardless of selection
     def iterator(ksi, context, ks):
diff --git a/source/blender/alembic/intern/alembic_capi.cc b/source/blender/alembic/intern/alembic_capi.cc
index 5e93779b5f646096d27c08395d7403190442786d..3221ebe777873808e87d4985fa579d49bb0a6955 100644
--- a/source/blender/alembic/intern/alembic_capi.cc
+++ b/source/blender/alembic/intern/alembic_capi.cc
@@ -843,7 +843,7 @@ static void import_endjob(void *user_data)
 
 		lc = BKE_layer_collection_get_active(view_layer);
 		if (lc == NULL) {
-			BLI_assert(BLI_listbase_count_ex(&view_layer->layer_collections, 1) == 0);
+			BLI_assert(BLI_listbase_count_at_most(&view_layer->layer_collections, 1) == 0);
 			/* when there is no collection linked to this ViewLayer, create one */
 			SceneCollection *sc = BKE_collection_add(&data->scene->id, NULL, COLLECTION_TYPE_NONE, NULL);
 			lc = BKE_collection_link(view_layer, sc);
diff --git a/source/blender/blenfont/BLF_api.h b/source/blender/blenfont/BLF_api.h
index 22559edad22112a5a585b3818bf99cea48f67d9d..668588435b423a60c7cd355289773e2ed789fff2 100644
--- a/source/blender/blenfont/BLF_api.h
+++ b/source/blender/blenfont/BLF_api.h
@@ -33,6 +33,7 @@
 #define __BLF_API_H__
 
 #include "BLI_compiler_attrs.h"
+#include "BLI_sys_types.h"
 
 /* enable this only if needed (unused circa 2016) */
 #define BLF_BLUR_ENABLE 0
@@ -46,6 +47,10 @@ void BLF_exit(void);
 void BLF_default_dpi(int dpi);
 void BLF_default_set(int fontid);
 int BLF_default(void); /* get default font ID so we can pass it to other functions */
+void BLF_batch_reset(void); /* call when changing opengl context. */
+
+void BLF_antialias_set(bool enabled);
+bool BLF_antialias_get(void);
 
 void BLF_cache_clear(void);
 
@@ -90,6 +95,12 @@ void BLF_color3fv_alpha(int fontid, const float rgb[3], float alpha);
  */
 void BLF_matrix(int fontid, const float m[16]);
 
+/* Batch drawcalls together as long as
+ * the modelview matrix and the font remain unchanged. */
+void BLF_batch_draw_begin(void);
+void BLF_batch_draw_flush(void);
+void BLF_batch_draw_end(void);
+
 /* Draw the string using the default font, size and dpi. */
 void BLF_draw_default(float x, float y, float z, const char *str, size_t len) ATTR_NONNULL();
 void BLF_draw_default_ascii(float x, float y, float z, const char *str, size_t len) ATTR_NONNULL();
diff --git a/source/blender/blenfont/intern/blf.c b/source/blender/blenfont/intern/blf.c
index 0cca1852c911239db372885403b9e63420557389..fafee125264089b5e7ae7ed9e969983e5b4da221 100644
--- a/source/blender/blenfont/intern/blf.c
+++ b/source/blender/blenfont/intern/blf.c
@@ -85,6 +85,7 @@ static FontBLF *global_font[BLF_MAX_FONT] = {NULL};
 static int global_font_default = -1;
 static int global_font_points = 11;
 static int global_font_dpi = 72;
+static bool global_use_antialias = true;
 
 /* XXX, should these be made into global_font_'s too? */
 int blf_mono_font = -1;
@@ -131,6 +132,11 @@ void BLF_exit(void)
 	blf_font_exit();
 }
 
+void BLF_batch_reset(void)
+{
+	blf_batch_draw_vao_clear();
+}
+
 void BLF_cache_clear(void)
 {
 	FontBLF *font;
@@ -138,8 +144,10 @@ void BLF_cache_clear(void)
 
 	for (i = 0; i < BLF_MAX_FONT; i++) {
 		font = global_font[i];
-		if (font)
+		if (font) {
 			blf_glyph_cache_clear(font);
+			blf_kerning_cache_clear(font);
+		}
 	}
 }
 
@@ -182,6 +190,16 @@ int BLF_default(void)
 	return global_font_default;
 }
 
+void BLF_antialias_set(bool enabled)
+{
+	global_use_antialias = enabled;
+}
+
+bool BLF_antialias_get(void)
+{
+	return global_use_antialias;
+}
+
 int BLF_load(const char *name)
 {
 	FontBLF *font;
@@ -524,6 +542,26 @@ void BLF_color3f(int fontid, float r, float g, float b)
 	BLF_color4fv(fontid, rgba);
 }
 
+void BLF_batch_draw_begin(void)
+{
+	BLI_assert(g_batch.enabled == false);
+	g_batch.enabled = true;
+}
+
+void BLF_batch_draw_flush(void)
+{
+	if (g_batch.enabled) {
+		blf_batch_draw();
+	}
+}
+
+void BLF_batch_draw_end(void)
+{
+	BLI_assert(g_batch.enabled == true);
+	blf_batch_draw(); /* Draw remaining glyphs */
+	g_batch.enabled = false;
+}
+
 void BLF_draw_default(float x, float y, float z, const char *str, size_t len)
 {
 	ASSERT_DEFAULT_SET;
@@ -550,8 +588,11 @@ static void blf_draw_gl__start(FontBLF *font)
 	 * in BLF_position (old ui_rasterpos_safe).
 	 */
 
-	glEnable(GL_BLEND);
-	glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+	/* always bind the texture for the first glyph */
+	font->tex_bind_state = 0;
+
+	if ((font->flags & (BLF_ROTATION | BLF_MATRIX | BLF_ASPECT)) == 0)
+		return; /* glyphs will be translated individually and batched. */
 
 	gpuPushMatrix();
 
@@ -565,35 +606,12 @@ static void blf_draw_gl__start(FontBLF *font)
 
 	if (font->flags & BLF_ROTATION)
 		gpuRotate2D(RAD2DEG(font->angle));
-
-#ifndef BLF_STANDALONE
-	Gwn_VertFormat *format = immVertexFormat();
-	unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
-	unsigned int texCoord = GWN_vertformat_attr_add(format, "texCoord", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
-	unsigned int color = GWN_vertformat_attr_add(format, "color", GWN_COMP_U8, 4, GWN_FETCH_INT_TO_FLOAT_UNIT);
-
-	BLI_assert(pos == BLF_POS_ID);
-	BLI_assert(texCoord == BLF_COORD_ID);
-	BLI_assert(color == BLF_COLOR_ID);
-
-	UNUSED_VARS_NDEBUG(pos, texCoord, color);
-
-	immBindBuiltinProgram(GPU_SHADER_TEXT);
-#endif
-
-	/* always bind the texture for the first glyph */
-	font->tex_bind_state = -1;
 }
 
-static void blf_draw_gl__end(void)
+static void blf_draw_gl__end(FontBLF *font)
 {
-	gpuPopMatrix();
-
-#ifndef BLF_STANDALONE
-	immUnbindProgram();
-#endif
-
-	glDisable(GL_BLEND);
+	if ((font->flags & (BLF_ROTATION | BLF_MATRIX | BLF_ASPECT)) != 0)
+		gpuPopMatrix();
 }
 
 void BLF_draw_ex(
@@ -612,7 +630,7 @@ void BLF_draw_ex(
 		else {
 			blf_font_draw(font, str, len, r_info);
 		}
-		blf_draw_gl__end();
+		blf_draw_gl__end(font);
 	}
 }
 void BLF_draw(int fontid, const char *str, size_t len)
@@ -641,7 +659,7 @@ void BLF_draw_ascii_ex(
 		else {
 			blf_font_draw_ascii(font, str, len, r_info);
 		}
-		blf_draw_gl__end();
+		blf_draw_gl__end(font);
 	}
 }
 void BLF_draw_ascii(int fontid, const char *str, size_t len)
@@ -665,7 +683,7 @@ int BLF_draw_mono(int fontid, const char *str, size_t len, int cwidth)
 	if (font && font->glyph_cache) {
 		blf_draw_gl__start(font);
 		columns = blf_font_draw_mono(font, str, len, cwidth);
-		blf_draw_gl__end();
+		blf_draw_gl__end(font);
 	}
 
 	return columns;
@@ -799,7 +817,7 @@ int BLF_height_max(int fontid)
 	FontBLF *font = blf_get(fontid);
 
 	if (font && font->glyph_cache) {
-		return font->glyph_cache->max_glyph_height;
+		return font->glyph_cache->glyph_height_max;
 	}
 
 	return 0;
@@ -810,7 +828,7 @@ float BLF_width_max(int fontid)
 	FontBLF *font = blf_get(fontid);
 
 	if (font && font->glyph_cache) {
-		return font->glyph_cache->max_glyph_width;
+		return font->glyph_cache->glyph_width_max;
 	}
 
 	return 0.0f;
diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c
index 5846f7a29e4b79717d79ef175e6c0d28d46c98b6..c12303dcfc04e64c14568e0c6506eef3352e638d 100644
--- a/source/blender/blenfont/intern/blf_font.c
+++ b/source/blender/blenfont/intern/blf_font.c
@@ -58,7 +58,11 @@
 #include "BIF_gl.h"
 #include "BLF_api.h"
 
+#include "UI_interface.h"
+
 #include "GPU_immediate.h"
+#include "GPU_matrix.h"
+#include "GPU_batch.h"
 
 #include "blf_internal_types.h"
 #include "blf_internal.h"
@@ -69,12 +73,157 @@
 #  define FT_New_Face FT_New_Face__win32_compat
 #endif
 
+/* Batching buffer for drawing. */
+BatchBLF g_batch;
+
 /* freetype2 handle ONLY for this file!. */
 static FT_Library ft_lib;
 static SpinLock ft_lib_mutex;
 
+/* -------------------------------------------------------------------- */
+/** \name Glyph Batching
+ * \{ */
+/**
+ * Drawcalls are precious! make them count!
+ * Since most of the Text elems are not covered by other UI elements, we can
+ * group some strings together and render them in one drawcall. This behaviour
+ * is on demand only, between BLF_batch_start() and BLF_batch_end().
+ **/
+static void blf_batch_draw_init(void)
+{
+	Gwn_VertFormat format = {0};
+	g_batch.pos_loc = GWN_vertformat_attr_add(&format, "pos", GWN_COMP_F32, 4, GWN_FETCH_FLOAT);
+	g_batch.tex_loc = GWN_vertformat_attr_add(&format, "tex", GWN_COMP_F32, 4, GWN_FETCH_FLOAT);
+	g_batch.col_loc = GWN_vertformat_attr_add(&format, "col", GWN_COMP_U8, 4, GWN_FETCH_INT_TO_FLOAT_UNIT);
+
+	g_batch.verts = GWN_vertbuf_create_with_format_ex(&format, GWN_USAGE_STREAM);
+	GWN_vertbuf_data_alloc(g_batch.verts, BLF_BATCH_DRAW_LEN_MAX);
+
+	GWN_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.pos_loc, &g_batch.pos_step);
+	GWN_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.tex_loc, &g_batch.tex_step);
+	GWN_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.col_loc, &g_batch.col_step);
+	g_batch.glyph_len = 0;
+
+	g_batch.batch = GWN_batch_create_ex(GWN_PRIM_POINTS, g_batch.verts, NULL, GWN_BATCH_OWNS_VBO);
+}
+
+static void blf_batch_draw_exit(void)
+{
+	GWN_BATCH_DISCARD_SAFE(g_batch.batch);
+}
+
+void blf_batch_draw_vao_clear(void)
+{
+	if (g_batch.batch) {
+		gwn_batch_vao_cache_clear(g_batch.batch);
+	}
+}
+
+void blf_batch_draw_begin(FontBLF *font)
+{
+	if (g_batch.batch == NULL) {
+		blf_batch_draw_init();
+	}
+
+	const bool font_changed = (g_batch.font != font);
+	const bool simple_shader = ((font->flags & (BLF_ROTATION | BLF_MATRIX | BLF_ASPECT)) == 0);
+	const bool shader_changed = (simple_shader != g_batch.simple_shader);
+
+	g_batch.active = g_batch.enabled && simple_shader;
+
+	if (simple_shader) {
+		/* Offset is applied to each glyph. */
+		copy_v2_v2(g_batch.ofs, font->pos);
+	}
+	else {
+		/* Offset is baked in modelview mat. */
+		zero_v2(g_batch.ofs);
+	}
+
+	if (g_batch.active) {
+		float gpumat[4][4];
+		gpuGetModelViewMatrix(gpumat);
+
+		bool mat_changed = (memcmp(gpumat, g_batch.mat, sizeof(g_batch.mat)) != 0);
+
+		if (mat_changed) {
+			/* Modelviewmat is no longer the same.
+			 * Flush cache but with the previous mat. */
+			gpuPushMatrix();
+			gpuLoadMatrix(g_batch.mat);
+		}
+
+		/* flush cache if config is not the same. */
+		if (mat_changed || font_changed || shader_changed) {
+			blf_batch_draw();
+			g_batch.simple_shader = simple_shader;
+			g_batch.font = font;
+		}
+		else {
+			/* Nothing changed continue batching. */
+			return;
+		}
+
+		if (mat_changed) {
+			gpuPopMatrix();
+			/* Save for next memcmp. */
+			memcpy(g_batch.mat, gpumat, sizeof(g_batch.mat));
+		}
+	}
+	else {
+		/* flush cache */
+		blf_batch_draw();
+		g_batch.font = font;
+		g_batch.simple_shader = simple_shader;
+	}
+}
+
+void blf_batch_draw(void)
+{
+	if (g_batch.glyph_len == 0)
+		return;
+
+	glEnable(GL_BLEND);
+	glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+
+	/* We need to flush widget base first to ensure correct ordering. */
+	UI_widgetbase_draw_cache_flush();
+
+	BLI_assert(g_batch.tex_bind_state != 0); /* must still be valid */
+	glActiveTexture(GL_TEXTURE0);
+	glBindTexture(GL_TEXTURE_2D, g_batch.tex_bind_state);
+
+	GWN_vertbuf_vertex_count_set(g_batch.verts, g_batch.glyph_len);
+	GWN_vertbuf_use(g_batch.verts); /* send data */
+
+	GPUBuiltinShader shader = (g_batch.simple_shader) ? GPU_SHADER_TEXT_SIMPLE : GPU_SHADER_TEXT;
+	GWN_batch_program_set_builtin(g_batch.batch, shader);
+	GWN_batch_uniform_1i(g_batch.batch, "glyph", 0);
+	GWN_batch_draw(g_batch.batch);
+
+	glDisable(GL_BLEND);
+
+	/* restart to 1st vertex data pointers */
+	GWN_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.pos_loc, &g_batch.pos_step);
+	GWN_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.tex_loc, &g_batch.tex_step);
+	GWN_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.col_loc, &g_batch.col_step);
+	g_batch.glyph_len = 0;
+}
+
+static void blf_batch_draw_end(void)
+{
+	if (!g_batch.active) {
+		blf_batch_draw();
+	}
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+
 int blf_font_init(void)
 {
+	memset(&g_batch, 0, sizeof(g_batch));
 	BLI_spin_init(&ft_lib_mutex);
 	return FT_Init_FreeType(&ft_lib);
 }
@@ -83,6 +232,7 @@ void blf_font_exit(void)
 {
 	FT_Done_FreeType(ft_lib);
 	BLI_spin_end(&ft_lib_mutex);
+	blf_batch_draw_exit();
 }
 
 void blf_font_size(FontBLF *font, unsigned int size, unsigned int dpi)
@@ -90,6 +240,14 @@ void blf_font_size(FontBLF *font, unsigned int size, unsigned int dpi)
 	GlyphCacheBLF *gc;
 	FT_Error err;
 
+	gc = blf_glyph_cache_find(font, size, dpi);
+	if (gc) {
+		font->glyph_cache = gc;
+		/* Optimization: do not call FT_Set_Char_Size if size did not change. */
+		if (font->size == size && font->dpi == dpi)
+			return;
+	}
+
 	err = FT_Set_Char_Size(font->face, 0, (FT_F26Dot6)(size * 64), dpi, dpi);
 	if (err) {
 		/* FIXME: here we can go through the fixed size and choice a close one */
@@ -100,10 +258,7 @@ void blf_font_size(FontBLF *font, unsigned int size, unsigned int dpi)
 	font->size = size;
 	font->dpi = dpi;
 
-	gc = blf_glyph_cache_find(font, size, dpi);
-	if (gc)
-		font->glyph_cache = gc;
-	else {
+	if (!gc) {
 		gc = blf_glyph_cache_new(font);
 		if (gc)
 			font->glyph_cache = gc;
@@ -131,6 +286,20 @@ static void blf_font_ensure_ascii_table(FontBLF *font)
 	}
 }
 
+static void blf_font_ensure_ascii_kerning(FontBLF *font, const FT_UInt kern_mode)
+{
+	KerningCacheBLF *kc = font->kerning_cache;
+
+	font->kerning_mode = kern_mode;
+
+	if (!kc || kc->mode != kern_mode) {
+		font->kerning_cache = kc = blf_kerning_cache_find(font);
+		if (!kc) {
+			font->kerning_cache = kc = blf_kerning_cache_new(font);
+		}
+	}
+}
+
 /* Fast path for runs of ASCII characters. Given that common UTF-8
  * input will consist of an overwhelming majority of ASCII
  * characters.
@@ -158,6 +327,26 @@ static void blf_font_ensure_ascii_table(FontBLF *font)
 	                         (((_font)->flags & BLF_KERNING_DEFAULT) ?           \
 	                          ft_kerning_default : (FT_UInt)FT_KERNING_UNFITTED) \
 
+/* Note,
+ * blf_font_ensure_ascii_kerning(font, kern_mode); must be called before this macro */
+
+#define BLF_KERNING_STEP_FAST(_font, _kern_mode, _g_prev, _g, _c_prev, _c, _pen_x) \
+{                                                                                \
+	if (_g_prev) {                                                               \
+		FT_Vector _delta;                                                        \
+		if (_c_prev < 0x80 && _c < 0x80) {                                       \
+			_pen_x += (_font)->kerning_cache->table[_c][_c_prev];                 \
+		}                                                                        \
+		else if (FT_Get_Kerning((_font)->face,                                   \
+		                        (_g_prev)->idx,                                  \
+		                        (_g)->idx,                                       \
+		                        _kern_mode,                                      \
+		                        &(_delta)) == 0)                                 \
+		{                                                                        \
+			_pen_x += (int)_delta.x >> 6;                                        \
+		}                                                                        \
+	}                                                                            \
+} (void)0
 
 #define BLF_KERNING_STEP(_font, _kern_mode, _g_prev, _g, _delta, _pen_x)         \
 {                                                                                \
@@ -174,30 +363,12 @@ static void blf_font_ensure_ascii_table(FontBLF *font)
 	}                                                                            \
 } (void)0
 
-static unsigned int verts_needed(const FontBLF *font, const char *str, size_t len)
-{
-	unsigned int length = (unsigned int)((len == INT_MAX) ? strlen(str) : len);
-	unsigned int quad_ct = 1;
-
-	if (font->flags & BLF_SHADOW) {
-		if (font->shadow == 0)
-			quad_ct += 1;
-		if (font->shadow <= 4)
-			quad_ct += 9; /* 3x3 kernel */
-		else
-			quad_ct += 25; /* 5x5 kernel */
-	}
-
-	return length * quad_ct * 6;
-}
-
 static void blf_font_draw_ex(
         FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info,
         int pen_y)
 {
-	unsigned int c;
+	unsigned int c, c_prev = BLI_UTF8_ERR;
 	GlyphBLF *g, *g_prev = NULL;
-	FT_Vector delta;
 	int pen_x = 0;
 	size_t i = 0;
 	GlyphBLF **glyph_ascii_table = font->glyph_cache->glyph_ascii_table;
@@ -210,9 +381,9 @@ static void blf_font_draw_ex(
 	BLF_KERNING_VARS(font, has_kerning, kern_mode);
 
 	blf_font_ensure_ascii_table(font);
+	blf_font_ensure_ascii_kerning(font, kern_mode);
 
-	immBeginAtMost(GWN_PRIM_TRIS, verts_needed(font, str, len));
-	/* at most because some glyphs might be clipped & not drawn */
+	blf_batch_draw_begin(font);
 
 	while ((i < len) && str[i]) {
 		BLF_UTF8_NEXT_FAST(font, g, str, i, c, glyph_ascii_table);
@@ -222,16 +393,17 @@ static void blf_font_draw_ex(
 		if (UNLIKELY(g == NULL))
 			continue;
 		if (has_kerning)
-			BLF_KERNING_STEP(font, kern_mode, g_prev, g, delta, pen_x);
+			BLF_KERNING_STEP_FAST(font, kern_mode, g_prev, g, c_prev, c, pen_x);
 
 		/* do not return this loop if clipped, we want every character tested */
 		blf_glyph_render(font, g, (float)pen_x, (float)pen_y);
 
 		pen_x += g->advance_i;
 		g_prev = g;
+		c_prev = c;
 	}
 
-	immEnd();
+	blf_batch_draw_end();
 
 	if (r_info) {
 		r_info->lines = 1;
@@ -248,33 +420,34 @@ static void blf_font_draw_ascii_ex(
         FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info,
         int pen_y)
 {
-	unsigned char c;
+	unsigned int c, c_prev = BLI_UTF8_ERR;
 	GlyphBLF *g, *g_prev = NULL;
-	FT_Vector delta;
 	int pen_x = 0;
 	GlyphBLF **glyph_ascii_table = font->glyph_cache->glyph_ascii_table;
 
 	BLF_KERNING_VARS(font, has_kerning, kern_mode);
 
 	blf_font_ensure_ascii_table(font);
+	blf_font_ensure_ascii_kerning(font, kern_mode);
 
-	immBeginAtMost(GWN_PRIM_TRIS, verts_needed(font, str, len));
+	blf_batch_draw_begin(font);
 
 	while ((c = *(str++)) && len--) {
 		BLI_assert(c < 128);
 		if ((g = glyph_ascii_table[c]) == NULL)
 			continue;
 		if (has_kerning)
-			BLF_KERNING_STEP(font, kern_mode, g_prev, g, delta, pen_x);
+			BLF_KERNING_STEP_FAST(font, kern_mode, g_prev, g, c_prev, c, pen_x);
 
 		/* do not return this loop if clipped, we want every character tested */
 		blf_glyph_render(font, g, (float)pen_x, (float)pen_y);
 
 		pen_x += g->advance_i;
 		g_prev = g;
+		c_prev = c;
 	}
 
-	immEnd();
+	blf_batch_draw_end();
 
 	if (r_info) {
 		r_info->lines = 1;
@@ -298,7 +471,7 @@ int blf_font_draw_mono(FontBLF *font, const char *str, size_t len, int cwidth)
 
 	blf_font_ensure_ascii_table(font);
 
-	immBeginAtMost(GWN_PRIM_TRIS, verts_needed(font, str, len));
+	blf_batch_draw_begin(font);
 
 	while ((i < len) && str[i]) {
 		BLF_UTF8_NEXT_FAST(font, g, str, i, c, glyph_ascii_table);
@@ -319,7 +492,7 @@ int blf_font_draw_mono(FontBLF *font, const char *str, size_t len, int cwidth)
 		pen_x += cwidth * col;
 	}
 
-	immEnd();
+	blf_batch_draw_end();
 
 	return columns;
 }
@@ -329,9 +502,8 @@ static void blf_font_draw_buffer_ex(
         FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info,
         int pen_y)
 {
-	unsigned int c;
+	unsigned int c, c_prev = BLI_UTF8_ERR;
 	GlyphBLF *g, *g_prev = NULL;
-	FT_Vector delta;
 	int pen_x = (int)font->pos[0];
 	int pen_y_basis = (int)font->pos[1] + pen_y;
 	size_t i = 0;
@@ -347,6 +519,7 @@ static void blf_font_draw_buffer_ex(
 	BLF_KERNING_VARS(font, has_kerning, kern_mode);
 
 	blf_font_ensure_ascii_table(font);
+	blf_font_ensure_ascii_kerning(font, kern_mode);
 
 	/* another buffer specific call for color conversion */
 
@@ -358,7 +531,7 @@ static void blf_font_draw_buffer_ex(
 		if (UNLIKELY(g == NULL))
 			continue;
 		if (has_kerning)
-			BLF_KERNING_STEP(font, kern_mode, g_prev, g, delta, pen_x);
+			BLF_KERNING_STEP_FAST(font, kern_mode, g_prev, g, c_prev, c, pen_x);
 
 		chx = pen_x + ((int)g->pos_x);
 		chy = pen_y_basis + g->height;
@@ -460,6 +633,7 @@ static void blf_font_draw_buffer_ex(
 
 		pen_x += g->advance_i;
 		g_prev = g;
+		c_prev = c;
 	}
 
 	if (r_info) {
@@ -607,9 +781,8 @@ static void blf_font_boundbox_ex(
         FontBLF *font, const char *str, size_t len, rctf *box, struct ResultBLF *r_info,
         int pen_y)
 {
-	unsigned int c;
+	unsigned int c, c_prev = BLI_UTF8_ERR;
 	GlyphBLF *g, *g_prev = NULL;
-	FT_Vector delta;
 	int pen_x = 0;
 	size_t i = 0;
 	GlyphBLF **glyph_ascii_table = font->glyph_cache->glyph_ascii_table;
@@ -624,6 +797,7 @@ static void blf_font_boundbox_ex(
 	box->ymax = -32000.0f;
 
 	blf_font_ensure_ascii_table(font);
+	blf_font_ensure_ascii_kerning(font, kern_mode);
 
 	while ((i < len) && str[i]) {
 		BLF_UTF8_NEXT_FAST(font, g, str, i, c, glyph_ascii_table);
@@ -633,7 +807,7 @@ static void blf_font_boundbox_ex(
 		if (UNLIKELY(g == NULL))
 			continue;
 		if (has_kerning)
-			BLF_KERNING_STEP(font, kern_mode, g_prev, g, delta, pen_x);
+			BLF_KERNING_STEP_FAST(font, kern_mode, g_prev, g, c_prev, c, pen_x);
 
 		gbox.xmin = (float)pen_x;
 		gbox.xmax = (float)pen_x + g->advance;
@@ -648,6 +822,7 @@ static void blf_font_boundbox_ex(
 
 		pen_x += g->advance_i;
 		g_prev = g;
+		c_prev = c;
 	}
 
 	if (box->xmin > box->xmax) {
@@ -756,7 +931,7 @@ static void blf_font_wrap_apply(
 			wrap.start = wrap.last[0];
 			i = wrap.last[1];
 			pen_x = 0;
-			pen_y -= font->glyph_cache->max_glyph_height;
+			pen_y -= font->glyph_cache->glyph_height_max;
 			g_prev = NULL;
 			lines += 1;
 			continue;
@@ -928,6 +1103,8 @@ void blf_font_free(FontBLF *font)
 		blf_glyph_cache_free(gc);
 	}
 
+	blf_kerning_cache_clear(font);
+
 	FT_Done_Face(font->face);
 	if (font->filename)
 		MEM_freeN(font->filename);
@@ -962,11 +1139,13 @@ static void blf_font_fill(FontBLF *font)
 	font->dpi = 0;
 	font->size = 0;
 	BLI_listbase_clear(&font->cache);
+	BLI_listbase_clear(&font->kerning_caches);
 	font->glyph_cache = NULL;
+	font->kerning_cache = NULL;
 #if BLF_BLUR_ENABLE
 	font->blur = 0;
 #endif
-	font->max_tex_size = -1;
+	font->tex_size_max = -1;
 
 	font->buf_info.fbuf = NULL;
 	font->buf_info.cbuf = NULL;
diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c
index a7d52e968006e7090755c0bfa9a1af6cdf8e820c..21c11d96b07329d1c61b4dd720a6babac1b3a63a 100644
--- a/source/blender/blenfont/intern/blf_glyph.c
+++ b/source/blender/blenfont/intern/blf_glyph.c
@@ -56,13 +56,69 @@
 #include "BLF_api.h"
 
 #ifndef BLF_STANDALONE
-#include "GPU_immediate.h"
+#  include "GPU_immediate.h"
 #endif
 
 #include "blf_internal_types.h"
 #include "blf_internal.h"
 
 #include "BLI_strict_flags.h"
+#include "BLI_math_vector.h"
+
+KerningCacheBLF *blf_kerning_cache_find(FontBLF *font)
+{
+	KerningCacheBLF *p;
+
+	p = (KerningCacheBLF *)font->kerning_caches.first;
+	while (p) {
+		if (p->mode == font->kerning_mode)
+			return p;
+		p = p->next;
+	}
+	return NULL;
+}
+
+/* Create a new glyph cache for the current kerning mode. */
+KerningCacheBLF *blf_kerning_cache_new(FontBLF *font)
+{
+	KerningCacheBLF *kc;
+
+	kc = (KerningCacheBLF *)MEM_callocN(sizeof(KerningCacheBLF), "blf_kerning_cache_new");
+	kc->next = NULL;
+	kc->prev = NULL;
+	kc->mode = font->kerning_mode;
+
+	unsigned int i, j;
+	for (i = 0; i < 0x80; i++) {
+		for (j = 0; j < 0x80; j++) {
+			GlyphBLF *g = blf_glyph_search(font->glyph_cache, i);
+			if (!g) {
+				FT_UInt glyph_index = FT_Get_Char_Index(font->face, i);
+				g = blf_glyph_add(font, glyph_index, i);
+			}
+			/* Cannot fail since it has been added just before. */
+			GlyphBLF *g_prev = blf_glyph_search(font->glyph_cache, j);
+
+			FT_Vector delta = {.x = 0, .y = 0};
+			if (FT_Get_Kerning(font->face, g_prev->idx, g->idx, kc->mode,
+			                   &delta) == 0) {
+				kc->table[i][j] = (int)delta.x >> 6;
+			}
+			else {
+				kc->table[i][j] = 0;
+			}
+		}
+	}
+
+	BLI_addhead(&font->kerning_caches, kc);
+	return kc;
+}
+
+void blf_kerning_cache_clear(FontBLF *font)
+{
+	font->kerning_cache = NULL;
+	BLI_freelistN(&font->kerning_caches);
+}
 
 GlyphCacheBLF *blf_glyph_cache_find(FontBLF *font, unsigned int size, unsigned int dpi)
 {
@@ -92,34 +148,34 @@ GlyphCacheBLF *blf_glyph_cache_new(FontBLF *font)
 	memset(gc->bucket, 0, sizeof(gc->bucket));
 
 	gc->textures = (GLuint *)MEM_mallocN(sizeof(GLuint) * 256, __func__);
-	gc->ntex = 256;
-	gc->cur_tex = BLF_CURTEX_UNSET;
-	gc->x_offs = 0;
-	gc->y_offs = 0;
-	gc->pad = 3;
-
-	gc->num_glyphs = (int)font->face->num_glyphs;
-	gc->rem_glyphs = (int)font->face->num_glyphs;
+	gc->textures_len = 256;
+	gc->texture_current = BLF_TEXTURE_UNSET;
+	gc->offset_x = 3; /* enough padding for blur */
+	gc->offset_y = 3; /* enough padding for blur */
+	gc->pad = 6;
+
+	gc->glyphs_len_max = (int)font->face->num_glyphs;
+	gc->glyphs_len_free = (int)font->face->num_glyphs;
 	gc->ascender = ((float)font->face->size->metrics.ascender) / 64.0f;
 	gc->descender = ((float)font->face->size->metrics.descender) / 64.0f;
 
 	if (FT_IS_SCALABLE(font->face)) {
-		gc->max_glyph_width = (int)((float)(font->face->bbox.xMax - font->face->bbox.xMin) *
+		gc->glyph_width_max = (int)((float)(font->face->bbox.xMax - font->face->bbox.xMin) *
 		                            (((float)font->face->size->metrics.x_ppem) /
 		                             ((float)font->face->units_per_EM)));
 
-		gc->max_glyph_height = (int)((float)(font->face->bbox.yMax - font->face->bbox.yMin) *
+		gc->glyph_height_max = (int)((float)(font->face->bbox.yMax - font->face->bbox.yMin) *
 		                             (((float)font->face->size->metrics.y_ppem) /
 		                              ((float)font->face->units_per_EM)));
 	}
 	else {
-		gc->max_glyph_width = (int)(((float)font->face->size->metrics.max_advance) / 64.0f);
-		gc->max_glyph_height = (int)(((float)font->face->size->metrics.height) / 64.0f);
+		gc->glyph_width_max = (int)(((float)font->face->size->metrics.max_advance) / 64.0f);
+		gc->glyph_height_max = (int)(((float)font->face->size->metrics.height) / 64.0f);
 	}
 
 	/* can happen with size 1 fonts */
-	CLAMP_MIN(gc->max_glyph_width, 1);
-	CLAMP_MIN(gc->max_glyph_height, 1);
+	CLAMP_MIN(gc->glyph_width_max, 1);
+	CLAMP_MIN(gc->glyph_height_max, 1);
 
 	gc->p2_width = 0;
 	gc->p2_height = 0;
@@ -149,9 +205,10 @@ void blf_glyph_cache_free(GlyphCacheBLF *gc)
 		}
 	}
 
-	if (gc->cur_tex != BLF_CURTEX_UNSET)
-		glDeleteTextures((int)gc->cur_tex + 1, gc->textures);
-	MEM_freeN((void *)gc->textures);
+	if (gc->texture_current != BLF_TEXTURE_UNSET) {
+		glDeleteTextures((int)gc->texture_current + 1, gc->textures);
+	}
+	MEM_freeN(gc->textures);
 	MEM_freeN(gc);
 }
 
@@ -160,28 +217,30 @@ static void blf_glyph_cache_texture(FontBLF *font, GlyphCacheBLF *gc)
 	int i;
 
 	/* move the index. */
-	gc->cur_tex++;
+	gc->texture_current++;
 
-	if (UNLIKELY(gc->cur_tex >= gc->ntex)) {
-		gc->ntex *= 2;
-		gc->textures = (GLuint *)MEM_reallocN((void *)gc->textures, sizeof(GLuint) * gc->ntex);
+	if (UNLIKELY(gc->texture_current >= gc->textures_len)) {
+		gc->textures_len *= 2;
+		gc->textures = MEM_reallocN((void *)gc->textures, sizeof(GLuint) * gc->textures_len);
 	}
 
-	gc->p2_width = (int)blf_next_p2((unsigned int)((gc->rem_glyphs * gc->max_glyph_width) + (gc->pad * 2)));
-	if (gc->p2_width > font->max_tex_size)
-		gc->p2_width = font->max_tex_size;
+	gc->p2_width = (int)blf_next_p2((unsigned int)((gc->glyphs_len_free * gc->glyph_width_max) + (gc->pad * 2)));
+	if (gc->p2_width > font->tex_size_max) {
+		gc->p2_width = font->tex_size_max;
+	}
 
-	i = (int)((gc->p2_width - (gc->pad * 2)) / gc->max_glyph_width);
-	gc->p2_height = (int)blf_next_p2((unsigned int)(((gc->num_glyphs / i) + 1) * gc->max_glyph_height));
+	i = (int)((gc->p2_width - (gc->pad * 2)) / gc->glyph_width_max);
+	gc->p2_height = (int)blf_next_p2((unsigned int)(((gc->glyphs_len_max / i) + 1) * gc->glyph_height_max));
 
-	if (gc->p2_height > font->max_tex_size)
-		gc->p2_height = font->max_tex_size;
+	if (gc->p2_height > font->tex_size_max) {
+		gc->p2_height = font->tex_size_max;
+	}
 
-	glGenTextures(1, &gc->textures[gc->cur_tex]);
-	glBindTexture(GL_TEXTURE_2D, (font->tex_bind_state = gc->textures[gc->cur_tex]));
+	glGenTextures(1, &gc->textures[gc->texture_current]);
+	glBindTexture(GL_TEXTURE_2D, (font->tex_bind_state = gc->textures[gc->texture_current]));
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 	glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, gc->p2_width, gc->p2_height, 0, GL_RED, GL_UNSIGNED_BYTE, NULL);
 }
@@ -207,7 +266,7 @@ GlyphBLF *blf_glyph_add(FontBLF *font, unsigned int index, unsigned int c)
 	GlyphBLF *g;
 	FT_Error err;
 	FT_Bitmap bitmap, tempbitmap;
-	const bool is_sharp = (U.text_render & USER_TEXT_DISABLE_AA) != 0;
+	const bool is_sharp = !BLF_antialias_get();
 	int flags = FT_LOAD_TARGET_NORMAL | FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP;
 	FT_BBox bbox;
 	unsigned int key;
@@ -267,8 +326,8 @@ GlyphBLF *blf_glyph_add(FontBLF *font, unsigned int index, unsigned int c)
 	g = (GlyphBLF *)MEM_callocN(sizeof(GlyphBLF), "blf_glyph_add");
 	g->c = c;
 	g->idx = (FT_UInt)index;
-	g->xoff = -1;
-	g->yoff = -1;
+	g->offset_x = -1;
+	g->offset_y = -1;
 	bitmap = slot->bitmap;
 	g->width = (int)bitmap.width;
 	g->height = (int)bitmap.rows;
@@ -316,83 +375,49 @@ void blf_glyph_free(GlyphBLF *g)
 	MEM_freeN(g);
 }
 
-static void blf_texture_draw(const unsigned char color[4], float uv[2][2], float dx, float y1, float dx1, float y2)
+static void blf_texture_draw(const unsigned char color[4], const float uv[2][2], float x1, float y1, float x2, float y2)
 {
-	/* First triangle. */
-	immAttrib2f(BLF_COORD_ID, uv[0][0], uv[0][1]);
-	immSkipAttrib(BLF_COLOR_ID); /* skip color of most vertices */
-	immVertex2f(BLF_POS_ID, dx, y1);
-
-	immAttrib2f(BLF_COORD_ID, uv[0][0], uv[1][1]);
-	immSkipAttrib(BLF_COLOR_ID);
-	immVertex2f(BLF_POS_ID, dx, y2);
-
-	immAttrib2f(BLF_COORD_ID, uv[1][0], uv[1][1]);
-	immAttrib4ubv(BLF_COLOR_ID, color); /* set color of provoking vertex */
-	immVertex2f(BLF_POS_ID, dx1, y2);
-
-	/* Second triangle. */
-	immAttrib2f(BLF_COORD_ID, uv[0][0], uv[0][1]);
-	immSkipAttrib(BLF_COLOR_ID); /* skip color of most vertices */
-	immVertex2f(BLF_POS_ID, dx, y1);
-
-	immAttrib2f(BLF_COORD_ID, uv[1][0], uv[1][1]);
-	immSkipAttrib(BLF_COLOR_ID);
-	immVertex2f(BLF_POS_ID, dx1, y2);
-
-	immAttrib2f(BLF_COORD_ID, uv[1][0], uv[0][1]);
-	immAttrib4ubv(BLF_COLOR_ID, color); /* set color of provoking vertex */
-	immVertex2f(BLF_POS_ID, dx1, y1);
+	/* Only one vertex per glyph, geometry shader expand it into a quad. */
+	/* TODO Get rid of Geom Shader because it's not optimal AT ALL for the GPU */
+	copy_v4_fl4(GWN_vertbuf_raw_step(&g_batch.pos_step), x1 + g_batch.ofs[0], y1 + g_batch.ofs[1],
+	                                                     x2 + g_batch.ofs[0], y2 + g_batch.ofs[1]);
+	copy_v4_v4(GWN_vertbuf_raw_step(&g_batch.tex_step), (float *)uv);
+	copy_v4_v4_uchar(GWN_vertbuf_raw_step(&g_batch.col_step), color);
+	g_batch.glyph_len++;
+	/* Flush cache if it's full. */
+	if (g_batch.glyph_len == BLF_BATCH_DRAW_LEN_MAX) {
+		blf_batch_draw();
+	}
 }
 
-static void blf_texture5_draw(const unsigned char color_in[4], float uv[2][2], float x1, float y1, float x2, float y2)
+static void blf_texture5_draw(const unsigned char color_in[4], int tex_w, int tex_h, const float uv[2][2],
+                              float x1, float y1, float x2, float y2)
 {
-	const float soft[25] = {1 / 60.0f, 1 / 60.0f, 2 / 60.0f, 1 / 60.0f, 1 / 60.0f,
-	                        1 / 60.0f, 3 / 60.0f, 5 / 60.0f, 3 / 60.0f, 1 / 60.0f,
-	                        2 / 60.0f, 5 / 60.0f, 8 / 60.0f, 5 / 60.0f, 2 / 60.0f,
-	                        1 / 60.0f, 3 / 60.0f, 5 / 60.0f, 3 / 60.0f, 1 / 60.0f,
-	                        1 / 60.0f, 1 / 60.0f, 2 / 60.0f, 1 / 60.0f, 1 / 60.0f};
-
-	const float *fp = soft;
-	unsigned char color[4];
-	float dx, dy;
-
-	color[0] = color_in[0];
-	color[1] = color_in[1];
-	color[2] = color_in[2];
-
-	const float alpha_in = (1 / 255.0f) * color_in[3];
-
-	for (dx = -2; dx < 3; dx++) {
-		for (dy = -2; dy < 3; dy++, fp++) {
-			color[3] = FTOCHAR(*fp * alpha_in);
-			blf_texture_draw(color, uv, x1 + dx, y1 + dy, x2 + dx, y2 + dy);
-		}
-	}
+	float ofs[2] = { 2 / (float)tex_w, 2 / (float)tex_h };
+	float uv_flag[2][2];
+	copy_v4_v4((float *)uv_flag, (float *)uv);
+	/* flag the x and y component signs for 5x5 bluring */
+	uv_flag[0][0] = -(uv_flag[0][0] - ofs[0]);
+	uv_flag[0][1] = -(uv_flag[0][1] - ofs[1]);
+	uv_flag[1][0] = -(uv_flag[1][0] + ofs[0]);
+	uv_flag[1][1] = -(uv_flag[1][1] + ofs[1]);
+
+	blf_texture_draw(color_in, uv_flag, x1 - 2, y1 + 2, x2 + 2, y2 - 2);
 }
 
-static void blf_texture3_draw(const unsigned char color_in[4], float uv[2][2], float x1, float y1, float x2, float y2)
+static void blf_texture3_draw(const unsigned char color_in[4], int tex_w, int tex_h, const float uv[2][2],
+                              float x1, float y1, float x2, float y2)
 {
-	const float soft[9] = {1 / 16.0f, 2 / 16.0f, 1 / 16.0f,
-	                       2 / 16.0f, 4 / 16.0f, 2 / 16.0f,
-	                       1 / 16.0f, 2 / 16.0f, 1 / 16.0f};
-
-	const float *fp = soft;
-	unsigned char color[4];
-	float dx, dy;
-
-	color[0] = color_in[0];
-	color[1] = color_in[1];
-	color[2] = color_in[2];
-
-	const float alpha_in = (1 / 255.0f) * color_in[3];
-
-	for (dx = -1; dx < 2; dx++) {
-		for (dy = -1; dy < 2; dy++, fp++) {
-			color[3] = FTOCHAR(*fp * alpha_in);
-			blf_texture_draw(color, uv, x1 + dx, y1 + dy, x2 + dx, y2 + dy);
-		}
-	}
+	float ofs[2] = { 1 / (float)tex_w, 1 / (float)tex_h };
+	float uv_flag[2][2];
+	copy_v4_v4((float *)uv_flag, (float *)uv);
+	/* flag the x component sign for 3x3 bluring */
+	uv_flag[0][0] = -(uv_flag[0][0] - ofs[0]);
+	uv_flag[0][1] =  (uv_flag[0][1] - ofs[1]);
+	uv_flag[1][0] = -(uv_flag[1][0] + ofs[0]);
+	uv_flag[1][1] =  (uv_flag[1][1] + ofs[1]);
+
+	blf_texture_draw(color_in, uv_flag, x1 - 1, y1 + 1, x2 + 1, y2 - 1);
 }
 
 static void blf_glyph_calc_rect(rctf *rect, GlyphBLF *g, float x, float y)
@@ -410,41 +435,43 @@ void blf_glyph_render(FontBLF *font, GlyphBLF *g, float x, float y)
 	if ((!g->width) || (!g->height))
 		return;
 
+	glActiveTexture(GL_TEXTURE0);
+
 	if (g->build_tex == 0) {
 		GlyphCacheBLF *gc = font->glyph_cache;
 
-		if (font->max_tex_size == -1)
-			glGetIntegerv(GL_MAX_TEXTURE_SIZE, (GLint *)&font->max_tex_size);
+		if (font->tex_size_max == -1)
+			glGetIntegerv(GL_MAX_TEXTURE_SIZE, (GLint *)&font->tex_size_max);
 
-		if (gc->cur_tex == BLF_CURTEX_UNSET) {
+		if (gc->texture_current == BLF_TEXTURE_UNSET) {
 			blf_glyph_cache_texture(font, gc);
-			gc->x_offs = gc->pad;
-			gc->y_offs = 0;
+			gc->offset_x = gc->pad;
+			gc->offset_y = 3; /* enough padding for blur */
 		}
 
-		if (gc->x_offs > (gc->p2_width - gc->max_glyph_width)) {
-			gc->x_offs = gc->pad;
-			gc->y_offs += gc->max_glyph_height;
+		if (gc->offset_x > (gc->p2_width - gc->glyph_width_max)) {
+			gc->offset_x = gc->pad;
+			gc->offset_y += gc->glyph_height_max;
 
-			if (gc->y_offs > (gc->p2_height - gc->max_glyph_height)) {
-				gc->y_offs = 0;
+			if (gc->offset_y > (gc->p2_height - gc->glyph_height_max)) {
+				gc->offset_y = 3; /* enough padding for blur */
 				blf_glyph_cache_texture(font, gc);
 			}
 		}
 
-		g->tex = gc->textures[gc->cur_tex];
-		g->xoff = gc->x_offs;
-		g->yoff = gc->y_offs;
+		g->tex = gc->textures[gc->texture_current];
+		g->offset_x = gc->offset_x;
+		g->offset_y = gc->offset_y;
 
 		/* prevent glTexSubImage2D from failing if the character
 		 * asks for pixels out of bounds, this tends only to happen
 		 * with very small sizes (5px high or less) */
-		if (UNLIKELY((g->xoff + g->width)  > gc->p2_width)) {
-			g->width  -= (g->xoff + g->width)  - gc->p2_width;
+		if (UNLIKELY((g->offset_x + g->width)  > gc->p2_width)) {
+			g->width  -= (g->offset_x + g->width)  - gc->p2_width;
 			BLI_assert(g->width > 0);
 		}
-		if (UNLIKELY((g->yoff + g->height) > gc->p2_height)) {
-			g->height -= (g->yoff + g->height) - gc->p2_height;
+		if (UNLIKELY((g->offset_y + g->height) > gc->p2_height)) {
+			g->height -= (g->offset_y + g->height) - gc->p2_height;
 			BLI_assert(g->height > 0);
 		}
 
@@ -459,21 +486,21 @@ void blf_glyph_render(FontBLF *font, GlyphBLF *g, float x, float y)
 		glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
 
 		glBindTexture(GL_TEXTURE_2D, g->tex);
-		glTexSubImage2D(GL_TEXTURE_2D, 0, g->xoff, g->yoff, g->width, g->height, GL_RED, GL_UNSIGNED_BYTE, g->bitmap);
+		glTexSubImage2D(GL_TEXTURE_2D, 0, g->offset_x, g->offset_y, g->width, g->height, GL_RED, GL_UNSIGNED_BYTE, g->bitmap);
 
 		glPixelStorei(GL_UNPACK_LSB_FIRST, lsb_first);
 		glPixelStorei(GL_UNPACK_ROW_LENGTH, row_length);
 		glPixelStorei(GL_UNPACK_ALIGNMENT, alignment);
 
-		g->uv[0][0] = ((float)g->xoff) / ((float)gc->p2_width);
-		g->uv[0][1] = ((float)g->yoff) / ((float)gc->p2_height);
-		g->uv[1][0] = ((float)(g->xoff + g->width)) / ((float)gc->p2_width);
-		g->uv[1][1] = ((float)(g->yoff + g->height)) / ((float)gc->p2_height);
+		g->uv[0][0] = ((float)g->offset_x) / ((float)gc->p2_width);
+		g->uv[0][1] = ((float)g->offset_y) / ((float)gc->p2_height);
+		g->uv[1][0] = ((float)(g->offset_x + g->width)) / ((float)gc->p2_width);
+		g->uv[1][1] = ((float)(g->offset_y + g->height)) / ((float)gc->p2_height);
 
 		/* update the x offset for the next glyph. */
-		gc->x_offs += (int)BLI_rctf_size_x(&g->box) + gc->pad;
+		gc->offset_x += (int)BLI_rctf_size_x(&g->box) + gc->pad;
 
-		gc->rem_glyphs--;
+		gc->glyphs_len_free--;
 		g->build_tex = 1;
 	}
 
@@ -493,7 +520,7 @@ void blf_glyph_render(FontBLF *font, GlyphBLF *g, float x, float y)
 		glBindTexture(GL_TEXTURE_2D, (font->tex_bind_state = g->tex));
 	}
 
-	/* TODO: blur & shadow in shader, single quad per glyph */
+	g_batch.tex_bind_state = g->tex;
 
 	if (font->flags & BLF_SHADOW) {
 		rctf rect_ofs;
@@ -505,20 +532,24 @@ void blf_glyph_render(FontBLF *font, GlyphBLF *g, float x, float y)
 			blf_texture_draw(font->shadow_color, g->uv, rect_ofs.xmin, rect_ofs.ymin, rect_ofs.xmax, rect_ofs.ymax);
 		}
 		else if (font->shadow <= 4) {
-			blf_texture3_draw(font->shadow_color, g->uv, rect_ofs.xmin, rect_ofs.ymin, rect_ofs.xmax, rect_ofs.ymax);
+			blf_texture3_draw(font->shadow_color, font->glyph_cache->p2_width, font->glyph_cache->p2_height, g->uv,
+			                  rect_ofs.xmin, rect_ofs.ymin, rect_ofs.xmax, rect_ofs.ymax);
 		}
 		else {
-			blf_texture5_draw(font->shadow_color, g->uv, rect_ofs.xmin, rect_ofs.ymin, rect_ofs.xmax, rect_ofs.ymax);
+			blf_texture5_draw(font->shadow_color, font->glyph_cache->p2_width, font->glyph_cache->p2_height, g->uv,
+			                  rect_ofs.xmin, rect_ofs.ymin, rect_ofs.xmax, rect_ofs.ymax);
 		}
 	}
 
 #if BLF_BLUR_ENABLE
 	switch (font->blur) {
 		case 3:
-			blf_texture3_draw(font->color, g->uv, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
+			blf_texture3_draw(font->color, font->glyph_cache->p2_width, font->glyph_cache->p2_height, g->uv,
+			                  rect.xmin, rect.ymin, rect.xmax, rect.ymax);
 			break;
 		case 5:
-			blf_texture5_draw(font->color, g->uv, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
+			blf_texture5_draw(font->color, font->glyph_cache->p2_width, font->glyph_cache->p2_height, g->uv,
+			                  rect.xmin, rect.ymin, rect.xmax, rect.ymax);
 			break;
 		default:
 			blf_texture_draw(font->color, g->uv, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
diff --git a/source/blender/blenfont/intern/blf_internal.h b/source/blender/blenfont/intern/blf_internal.h
index ba17e0503997a63b80aa2ceb54467bb1a851f9b6..b48bd2fbae7020b2009a17b7f2fcdcc0f1f1aa51 100644
--- a/source/blender/blenfont/intern/blf_internal.h
+++ b/source/blender/blenfont/intern/blf_internal.h
@@ -37,10 +37,9 @@ struct GlyphBLF;
 struct GlyphCacheBLF;
 struct rctf;
 
-/* vertex attribute IDs (fixed IDs so we don't have to pass them around) */
-#define BLF_POS_ID 0
-#define BLF_COORD_ID 1
-#define BLF_COLOR_ID 2
+void blf_batch_draw_vao_clear(void);
+void blf_batch_draw_begin(struct FontBLF *font);
+void blf_batch_draw(void);
 
 unsigned int blf_next_p2(unsigned int x);
 unsigned int blf_hash(unsigned int val);
@@ -79,6 +78,10 @@ int blf_font_count_missing_chars(struct FontBLF *font, const char *str, const si
 
 void blf_font_free(struct FontBLF *font);
 
+struct KerningCacheBLF *blf_kerning_cache_find(struct FontBLF *font);
+struct KerningCacheBLF *blf_kerning_cache_new(struct FontBLF *font);
+void blf_kerning_cache_clear(struct FontBLF *font);
+
 struct GlyphCacheBLF *blf_glyph_cache_find(struct FontBLF *font, unsigned int size, unsigned int dpi);
 struct GlyphCacheBLF *blf_glyph_cache_new(struct FontBLF *font);
 void blf_glyph_cache_clear(struct FontBLF *font);
diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h
index 9164a02b2ccf5684cb3be4a73e7ba2528bcc09d0..1d70980d33d5eeda2c9b688aa70c8676ce291e27 100644
--- a/source/blender/blenfont/intern/blf_internal_types.h
+++ b/source/blender/blenfont/intern/blf_internal_types.h
@@ -31,6 +31,36 @@
 #ifndef __BLF_INTERNAL_TYPES_H__
 #define __BLF_INTERNAL_TYPES_H__
 
+#include "../../../intern/gawain/gawain/gwn_vertex_buffer.h"
+
+#define BLF_BATCH_DRAW_LEN_MAX 2048 /* in glyph */
+
+typedef struct BatchBLF{
+	struct FontBLF *font; /* can only batch glyph from the same font */
+	struct Gwn_Batch *batch;
+	struct Gwn_VertBuf *verts;
+	struct Gwn_VertBufRaw pos_step, tex_step, col_step;
+	unsigned int pos_loc, tex_loc, col_loc;
+	unsigned int glyph_len;
+	float ofs[2];    /* copy of font->pos */
+	float mat[4][4]; /* previous call modelmatrix. */
+	bool enabled, active, simple_shader;
+	unsigned int tex_bind_state;
+} BatchBLF;
+
+extern BatchBLF g_batch;
+
+typedef struct KerningCacheBLF {
+	struct KerningCacheBLF *next, *prev;
+
+	/* kerning mode. */
+	FT_UInt mode;
+
+	/* only cache a ascii glyph pairs. Only store the x
+	 * offset we are interested in, instead of the full FT_Vector. */
+	int table[0x80][0x80];
+} KerningCacheBLF;
+
 typedef struct GlyphCacheBLF {
 	struct GlyphCacheBLF *next;
 	struct GlyphCacheBLF *prev;
@@ -51,33 +81,33 @@ typedef struct GlyphCacheBLF {
 	unsigned int *textures;
 
 	/* size of the array. */
-	unsigned int ntex;
+	unsigned int textures_len;
 
 	/* and the last texture, aka. the current texture. */
-	unsigned int cur_tex;
+	unsigned int texture_current;
 
 	/* like bftgl, we draw every glyph in a big texture, so this is the
 	 * current position inside the texture.
 	 */
-	int x_offs;
-	int y_offs;
+	int offset_x;
+	int offset_y;
 
 	/* and the space from one to other. */
 	int pad;
 
 	/* and the bigger glyph in the font. */
-	int max_glyph_width;
-	int max_glyph_height;
+	int glyph_width_max;
+	int glyph_height_max;
 
 	/* next two integer power of two, to build the texture. */
 	int p2_width;
 	int p2_height;
 
 	/* number of glyphs in the font. */
-	int num_glyphs;
+	int glyphs_len_max;
 
-	/* number of glyphs that we load here. */
-	int rem_glyphs;
+	/* number of glyphs not yet loaded (decreases every glyph loaded). */
+	int glyphs_len_free;
 
 	/* ascender and descender value. */
 	float ascender;
@@ -106,8 +136,8 @@ typedef struct GlyphBLF {
 	unsigned int tex;
 
 	/* position inside the texture where this glyph is store. */
-	int xoff;
-	int yoff;
+	int offset_x;
+	int offset_y;
 
 	/* Bitmap data, from freetype. Take care that this
 	 * can be NULL.
@@ -130,7 +160,7 @@ typedef struct GlyphBLF {
 	float pos_y;
 
 	/* with value of zero mean that we need build the texture. */
-	short build_tex;
+	char build_tex;
 } GlyphBLF;
 
 typedef struct FontBufInfoBLF {
@@ -211,7 +241,7 @@ typedef struct FontBLF {
 	unsigned int size;
 
 	/* max texture size. */
-	int max_tex_size;
+	int tex_size_max;
 
 	/* cache current OpenGL texture to save calls into the API */
 	unsigned int tex_bind_state;
@@ -225,6 +255,12 @@ typedef struct FontBLF {
 	/* current glyph cache, size and dpi. */
 	GlyphCacheBLF *glyph_cache;
 
+	/* list of kerning cache for this font. */
+	ListBase kerning_caches;
+
+	/* current kerning cache for this font and kerning mode. */
+	KerningCacheBLF *kerning_cache;
+
 	/* freetype2 lib handle. */
 	FT_Library ft_lib;
 
@@ -234,6 +270,9 @@ typedef struct FontBLF {
 	/* freetype2 face. */
 	FT_Face face;
 
+	/* freetype kerning */
+	FT_UInt kerning_mode;
+
 	/* data for buffer usage (drawing into a texture buffer) */
 	FontBufInfoBLF buf_info;
 } FontBLF;
@@ -246,6 +285,6 @@ typedef struct DirBLF {
 	char *path;
 } DirBLF;
 
-#define BLF_CURTEX_UNSET ((unsigned int)-1)
+#define BLF_TEXTURE_UNSET ((unsigned int)-1)
 
 #endif /* __BLF_INTERNAL_TYPES_H__ */
diff --git a/source/blender/blenkernel/BKE_DerivedMesh.h b/source/blender/blenkernel/BKE_DerivedMesh.h
index 9d48ddb4f32587db21f3692ea93de9407ffb2734..4ae11b5cca53d11d98a68a71e1cc4e74797b6a2e 100644
--- a/source/blender/blenkernel/BKE_DerivedMesh.h
+++ b/source/blender/blenkernel/BKE_DerivedMesh.h
@@ -100,8 +100,6 @@ struct GPUDrawObject;
 struct PBVH;
 struct EvaluationContext;
 
-#include "DNA_object_enums.h"
-
 /* number of sub-elements each mesh element has (for interpolation) */
 #define SUB_ELEMS_VERT 0
 #define SUB_ELEMS_EDGE 2
@@ -383,7 +381,7 @@ struct DerivedMesh {
 
 	/** Get the BVH used for paint modes
 	 */
-	struct PBVH *(*getPBVH)(struct Object *ob, DerivedMesh *dm, eObjectMode object_mode);
+	struct PBVH *(*getPBVH)(struct Object *ob, DerivedMesh *dm);
 
 	/* Drawing Operations */
 
diff --git a/source/blender/blenkernel/BKE_armature.h b/source/blender/blenkernel/BKE_armature.h
index 192690c074c3c7879d16a885288f4a3b927a6534..bf37224c85fb6930dc22b478503637bda74c5eef 100644
--- a/source/blender/blenkernel/BKE_armature.h
+++ b/source/blender/blenkernel/BKE_armature.h
@@ -171,41 +171,39 @@ void BKE_splineik_execute_tree(
 
 void BKE_pose_eval_init(const struct EvaluationContext *eval_ctx,
                         struct Scene *scene,
-                        struct Object *ob,
-                        struct bPose *pose);
+                        struct Object *ob);
 
 void BKE_pose_eval_init_ik(const struct EvaluationContext *eval_ctx,
                            struct Scene *scene,
-                           struct Object *ob,
-                           struct bPose *pose);
+                           struct Object *ob);
 
 void BKE_pose_eval_bone(const struct EvaluationContext *eval_ctx,
                         struct Scene *scene,
                         struct Object *ob,
-                        struct bPoseChannel *pchan);
+                        int pchan_index);
 
 void BKE_pose_constraints_evaluate(const struct EvaluationContext *eval_ctx,
                                    struct Scene *scene,
                                    struct Object *ob,
-                                   struct bPoseChannel *pchan);
+                                   int pchan_index);
 
 void BKE_pose_bone_done(const struct EvaluationContext *eval_ctx,
-                        struct bPoseChannel *pchan);
+                        struct Object *ob,
+                        int pchan_index);
 
 void BKE_pose_iktree_evaluate(const struct EvaluationContext *eval_ctx,
                               struct Scene *scene,
                               struct Object *ob,
-                              struct bPoseChannel *rootchan);
+                              int rootchan_index);
 
 void BKE_pose_splineik_evaluate(const struct EvaluationContext *eval_ctx,
                                 struct Scene *scene,
                                 struct Object *ob,
-                                struct bPoseChannel *rootchan);
+                                int rootchan_index);
 
 void BKE_pose_eval_flush(const struct EvaluationContext *eval_ctx,
                          struct Scene *scene,
-                         struct Object *ob,
-                         struct bPose *pose);
+                         struct Object *ob);
 
 void BKE_pose_eval_proxy_copy(const struct EvaluationContext *eval_ctx,
                               struct Object *ob);
diff --git a/source/blender/blenkernel/BKE_blender_undo.h b/source/blender/blenkernel/BKE_blender_undo.h
index 84a6d07be7da94554556ce727123203b2f18826f..a96f8af1fdb34f1d04b573d04552260d4a47974e 100644
--- a/source/blender/blenkernel/BKE_blender_undo.h
+++ b/source/blender/blenkernel/BKE_blender_undo.h
@@ -31,22 +31,13 @@ extern "C" {
 struct bContext;
 struct Scene;
 struct Main;
+struct MemFileUndoData;
 
 #define BKE_UNDO_STR_MAX 64
 
-/* global undo */
-extern void          BKE_undo_write(struct bContext *C, const char *name);
-extern void          BKE_undo_step(struct bContext *C, int step);
-extern void          BKE_undo_name(struct bContext *C, const char *name);
-extern bool          BKE_undo_is_valid(const char *name);
-extern void          BKE_undo_reset(void);
-extern void          BKE_undo_number(struct bContext *C, int nr);
-extern const char   *BKE_undo_get_name(int nr, bool *r_active);
-extern const char   *BKE_undo_get_name_last(void);
-extern bool          BKE_undo_save_file(const char *filename);
-extern struct Main  *BKE_undo_get_main(struct Scene **r_scene);
-
-extern void          BKE_undo_callback_wm_kill_jobs_set(void (*callback)(struct bContext *C));
+struct MemFileUndoData *BKE_memfile_undo_encode(struct Main *bmain, struct MemFileUndoData *mfu_prev);
+bool                    BKE_memfile_undo_decode(struct MemFileUndoData *mfu, struct bContext *C);
+void                    BKE_memfile_undo_free(struct MemFileUndoData *mfu);
 
 #ifdef __cplusplus
 }
diff --git a/source/blender/blenkernel/BKE_cloth.h b/source/blender/blenkernel/BKE_cloth.h
index 27b928c1671a11b321ca45d2440f30df9f8c5ad1..8923edacb07a7476406a9d03a56dade556ea6853 100644
--- a/source/blender/blenkernel/BKE_cloth.h
+++ b/source/blender/blenkernel/BKE_cloth.h
@@ -237,7 +237,6 @@ void bvhselftree_update_from_cloth(struct ClothModifierData *clmd, bool moving);
 
 // needed for button_object.c
 void cloth_clear_cache(
-        const struct EvaluationContext *eval_ctx,
         struct Object *ob, struct ClothModifierData *clmd, float framenr );
 
 void cloth_parallel_transport_hair_frame(float mat[3][3], const float dir_old[3], const float dir_new[3]);
diff --git a/source/blender/blenkernel/BKE_collection.h b/source/blender/blenkernel/BKE_collection.h
index af8047bf373e9abc21c2cbe56a73eb590ce12963..d3a4d2b8d5bd885695a54f1725bd82c865fe4daa 100644
--- a/source/blender/blenkernel/BKE_collection.h
+++ b/source/blender/blenkernel/BKE_collection.h
@@ -44,6 +44,7 @@ struct Main;
 struct Object;
 struct Scene;
 struct SceneCollection;
+struct ViewLayer;
 
 struct SceneCollection *BKE_collection_add(
         struct ID *owner_id, struct SceneCollection *sc_parent, const int type, const char *name);
@@ -58,6 +59,12 @@ void BKE_collection_object_add_from(struct Scene *scene, struct Object *ob_src,
 bool BKE_collection_object_remove(struct Main *bmain, struct ID *owner_id, struct SceneCollection *sc, struct Object *object, const bool free_us);
 bool BKE_collections_object_remove(struct Main *bmain, struct ID *owner_id, struct Object *object, const bool free_us);
 void BKE_collection_object_move(struct ID *owner_id, struct SceneCollection *sc_dst, struct SceneCollection *sc_src, struct Object *ob);
+bool BKE_collection_object_exists(struct SceneCollection *scene_collection, struct Object *ob);
+struct SceneCollection *BKE_collection_from_index(struct Scene *scene, const int index);
+
+void BKE_collection_new_name_get(struct ID *owner_id, struct SceneCollection *sc_parent, char *rname);
+
+bool BKE_collection_objects_select(struct ViewLayer *view_layer, struct SceneCollection *scene_collection);
 
 struct Group *BKE_collection_group_create(struct Main *bmain, struct Scene *scene, struct LayerCollection *lc);
 
diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h
index 9adc00a67e66ad9b647b1ad162cbd543b974012c..c1b437661c5e678d3d63f77aba5c26a05921d98b 100644
--- a/source/blender/blenkernel/BKE_global.h
+++ b/source/blender/blenkernel/BKE_global.h
@@ -80,6 +80,13 @@ typedef struct Global {
 	 * however this is now only used for runtime options */
 	int f;
 
+	struct {
+		/* Logging vars (different loggers may use). */
+		int level;
+		/* FILE handle or use stderr (we own this so close when done). */
+		void *file;
+	} log;
+
 	/* debug flag, G_DEBUG, G_DEBUG_PYTHON & friends, set python or command line args */
 	int debug;
 
diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h
index 57459412efc089ec099c83a4b448256073867902..1af123759e6ef1ce752710069a482fe16fa7b49e 100644
--- a/source/blender/blenkernel/BKE_image.h
+++ b/source/blender/blenkernel/BKE_image.h
@@ -67,6 +67,11 @@ void    BKE_image_init(struct Image *image);
 typedef void (StampCallback)(void *data, const char *propname, char *propvalue, int len);
 
 void    BKE_render_result_stamp_info(struct Scene *scene, struct Object *camera, struct RenderResult *rr, bool allocate_only);
+/**
+ * Fills in the static stamp data (i.e. everything except things that can change per frame).
+ * The caller is responsible for freeing the allocated memory.
+ */
+struct StampData *BKE_stamp_info_from_scene_static(struct Scene *scene);
 void    BKE_imbuf_stamp_info(struct RenderResult *rr, struct ImBuf *ibuf);
 void    BKE_stamp_info_from_imbuf(struct RenderResult *rr, struct ImBuf *ibuf);
 void    BKE_stamp_info_callback(void *data, struct StampData *stamp_data, StampCallback callback, bool noskip);
diff --git a/source/blender/blenkernel/BKE_layer.h b/source/blender/blenkernel/BKE_layer.h
index 59aec53aa1e4059575840cce73d3a274071226d0..8140dc5b31da8089a4b3539988d31a6bd0f7225d 100644
--- a/source/blender/blenkernel/BKE_layer.h
+++ b/source/blender/blenkernel/BKE_layer.h
@@ -112,6 +112,7 @@ void BKE_collection_unlink(struct ViewLayer *view_layer, struct LayerCollection
 
 void BKE_collection_enable(struct ViewLayer *view_layer, struct LayerCollection *lc);
 
+struct LayerCollection *BKE_layer_collection_first_from_scene_collection(struct ViewLayer *view_layer, const struct SceneCollection *scene_collection);
 bool BKE_view_layer_has_collection(struct ViewLayer *view_layer, const struct SceneCollection *sc);
 bool BKE_scene_has_object(struct Scene *scene, struct Object *ob);
 
diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h
index 79f56cf25ef385a2bf7ba9c668612a42bc220eec..aac43768acf5236d1c7294e930826f0145bd3dc8 100644
--- a/source/blender/blenkernel/BKE_main.h
+++ b/source/blender/blenkernel/BKE_main.h
@@ -84,10 +84,12 @@ typedef struct Main {
 	short minversionfile, minsubversionfile;
 	uint64_t build_commit_timestamp; /* commit's timestamp from buildinfo */
 	char build_hash[16];  /* hash from buildinfo */
-	short recovered;	/* indicate the main->name (file) is the recovered one */
+	char recovered;	/* indicate the main->name (file) is the recovered one */
+	/** All current ID's exist in the last memfile undo step. */
+	char is_memfile_undo_written;
 
 	BlendThumbnail *blen_thumb;
-	
+
 	struct Library *curlib;
 	ListBase scene;
 	ListBase library;
diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h
index e1ce58ba69ba94d358b1d7cc442e16d08843615c..329defbc77c4188a92503e5f552fa4075bcf8b6e 100644
--- a/source/blender/blenkernel/BKE_modifier.h
+++ b/source/blender/blenkernel/BKE_modifier.h
@@ -332,6 +332,7 @@ const ModifierTypeInfo *modifierType_getInfo(ModifierType type);
  * default values if pointer is optional.
  */
 struct ModifierData  *modifier_new(int type);
+void          modifier_free_ex(struct ModifierData *md, const int flag);
 void          modifier_free(struct ModifierData *md);
 
 bool          modifier_unique_name(struct ListBase *modifiers, struct ModifierData *md);
@@ -375,8 +376,7 @@ struct Object *modifiers_isDeformedByArmature(struct Object *ob);
 struct Object *modifiers_isDeformedByLattice(struct Object *ob);
 struct Object *modifiers_isDeformedByCurve(struct Object *ob);
 bool          modifiers_usesArmature(struct Object *ob, struct bArmature *arm);
-bool          modifiers_isCorrectableDeformed(
-        const struct EvaluationContext *eval_ctx, struct Scene *scene, struct Object *ob);
+bool          modifiers_isCorrectableDeformed(struct Scene *scene, struct Object *ob);
 void          modifier_freeTemporaryData(struct ModifierData *md);
 bool          modifiers_isPreview(struct Object *ob);
 
diff --git a/source/blender/blenkernel/BKE_multires.h b/source/blender/blenkernel/BKE_multires.h
index 22933a093ed9a420b6596a267a8d5734976e24ab..761bb7e8acb2c6a51cd697ee79f63f83ce4a4775 100644
--- a/source/blender/blenkernel/BKE_multires.h
+++ b/source/blender/blenkernel/BKE_multires.h
@@ -48,21 +48,20 @@ struct MVert;
 struct MPoly;
 struct MLoopTri;
 
-#include "DNA_object_enums.h"
-
 /* Delete mesh mdisps and grid paint masks */
 void multires_customdata_delete(struct Mesh *me);
 
-void multires_set_tot_level(struct MultiresModifierData *mmd, int lvl, eObjectMode object_mode);
+void multires_set_tot_level(struct Object *ob,
+                            struct MultiresModifierData *mmd, int lvl);
 
 void multires_mark_as_modified(struct Object *ob, enum MultiresModifiedFlags flags);
 
 void multires_force_update(struct Object *ob);
-void multires_force_render_update(struct Object *ob, eObjectMode object_mode);
+void multires_force_render_update(struct Object *ob);
 void multires_force_external_reload(struct Object *ob);
 
 /* internal, only called in subsurf_ccg.c */
-void multires_modifier_update_mdisps(struct DerivedMesh *dm, eObjectMode object_mode);
+void multires_modifier_update_mdisps(struct DerivedMesh *dm);
 void multires_modifier_update_hidden(struct DerivedMesh *dm);
 
 void multiresModifier_set_levels_from_disps(struct MultiresModifierData *mmd, struct Object *ob);
@@ -74,27 +73,21 @@ typedef enum {
 	MULTIRES_IGNORE_SIMPLIFY = 8
 } MultiresFlags;
 
-struct DerivedMesh *multires_make_derived_from_derived(
-        struct DerivedMesh *dm,
-        struct MultiresModifierData *mmd,
-        struct Object *ob,
-        MultiresFlags flags,
-        eObjectMode object_mode);
+struct DerivedMesh *multires_make_derived_from_derived(struct DerivedMesh *dm,
+                                                       struct MultiresModifierData *mmd,
+                                                       struct Object *ob,
+                                                       MultiresFlags flags);
 
 struct MultiresModifierData *find_multires_modifier_before(struct Scene *scene,
                                                            struct ModifierData *lastmd);
 struct MultiresModifierData *get_multires_modifier(struct Scene *scene, struct Object *ob, bool use_first);
 struct DerivedMesh *get_multires_dm(const struct EvaluationContext *eval_ctx, struct Scene *scene, struct MultiresModifierData *mmd,
                                     struct Object *ob);
-void multiresModifier_del_levels(
-        struct MultiresModifierData *, struct Object *, int direction, eObjectMode object_mode);
-void multiresModifier_base_apply(
-        struct MultiresModifierData *mmd, struct Object *ob, eObjectMode object_mode);
-void multiresModifier_subdivide(
-        struct MultiresModifierData *mmd, struct Object *ob, int updateblock, int simple, eObjectMode object_mode);
+void multiresModifier_del_levels(struct MultiresModifierData *, struct Object *, int direction);
+void multiresModifier_base_apply(struct MultiresModifierData *mmd, struct Object *ob);
+void multiresModifier_subdivide(struct MultiresModifierData *mmd, struct Object *ob, int updateblock, int simple);
 void multiresModifier_sync_levels_ex(
-        struct Object *ob_dst, struct MultiresModifierData *mmd_src, struct MultiresModifierData *mmd_dst,
-        eObjectMode object_mode);
+        struct Object *ob_dst, struct MultiresModifierData *mmd_src, struct MultiresModifierData *mmd_dst);
 int multiresModifier_reshape(const struct EvaluationContext *eval_ctx, struct Scene *scene, struct MultiresModifierData *mmd,
                              struct Object *dst, struct Object *src);
 int multiresModifier_reshapeFromDM(const struct EvaluationContext *eval_ctx, struct Scene *scene, struct MultiresModifierData *mmd,
@@ -110,7 +103,7 @@ enum {
 	MULTIRES_SPACE_OBJECT,
 	MULTIRES_SPACE_ABSOLUTE
 };
-void multires_set_space(struct DerivedMesh *dm, struct Object *ob, int from, int to, eObjectMode object_mode);
+void multires_set_space(struct DerivedMesh *dm, struct Object *ob, int from, int to);
 
 /* Related to the old multires */
 void multires_free(struct Multires *mr);
diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h
index 11e259e031813e445d83b9f258a19e4579f5fe9a..c5eefedcfad1fb4ed6b3be983a8c3a905d6fe9bc 100644
--- a/source/blender/blenkernel/BKE_object.h
+++ b/source/blender/blenkernel/BKE_object.h
@@ -74,18 +74,16 @@ void BKE_object_modifier_hook_reset(struct Object *ob, struct HookModifierData *
 
 bool BKE_object_support_modifier_type_check(struct Object *ob, int modifier_type);
 
-void BKE_object_link_modifiers(
-        struct Object *ob_dst, const struct Object *ob_src,
-        eObjectMode object_mode);
-void BKE_object_free_modifiers(struct Object *ob);
+void BKE_object_link_modifiers(struct Object *ob_dst, const struct Object *ob_src);
+void BKE_object_free_modifiers(struct Object *ob, const int flag);
 
 void BKE_object_make_proxy(struct Object *ob, struct Object *target, struct Object *gob);
 void BKE_object_copy_proxy_drivers(struct Object *ob, struct Object *target);
 
 bool BKE_object_exists_check(struct Object *obtest);
-bool BKE_object_is_in_editmode(const struct Object *ob);
+bool BKE_object_is_in_editmode(struct Object *ob);
 bool BKE_object_is_in_editmode_vgroup(struct Object *ob);
-bool BKE_object_is_in_wpaint_select_vert(const struct Object *ob, eObjectMode object_mode);
+bool BKE_object_is_in_wpaint_select_vert(const struct Object *ob);
 
 typedef enum eObjectVisibilityCheck {
 	OB_VISIBILITY_CHECK_FOR_VIEWPORT,
@@ -117,12 +115,9 @@ void BKE_object_lod_add(struct Object *ob);
 void BKE_object_lod_sort(struct Object *ob);
 bool BKE_object_lod_remove(struct Object *ob, int level);
 void BKE_object_lod_update(struct Object *ob, const float camera_position[3]);
-bool BKE_object_lod_is_usable(
-        struct Object *ob, struct ViewLayer *view_layer, const eObjectMode object_mode);
-struct Object *BKE_object_lod_meshob_get(
-        struct Object *ob, struct ViewLayer *view_layer, const eObjectMode object_mode);
-struct Object *BKE_object_lod_matob_get(
-        struct Object *ob, struct ViewLayer *view_layer, const eObjectMode object_mode);
+bool BKE_object_lod_is_usable(struct Object *ob, struct ViewLayer *view_layer);
+struct Object *BKE_object_lod_meshob_get(struct Object *ob, struct ViewLayer *view_layer);
+struct Object *BKE_object_lod_matob_get(struct Object *ob, struct ViewLayer *view_layer);
 
 void BKE_object_copy_data(struct Main *bmain, struct Object *ob_dst, const struct Object *ob_src, const int flag);
 struct Object *BKE_object_copy(struct Main *bmain, const struct Object *ob);
@@ -141,7 +136,6 @@ void BKE_object_to_mat4(struct Object *ob, float mat[4][4]);
 void BKE_object_apply_mat4(struct Object *ob, float mat[4][4], const bool use_compat, const bool use_parent);
 void BKE_object_matrix_local_get(struct Object *ob, float mat[4][4]);
 
-bool BKE_object_pose_context_check_ex(struct Object *ob, bool selected);
 bool BKE_object_pose_context_check(struct Object *ob);
 struct Object *BKE_object_pose_armature_get(struct Object *ob);
 struct Object *BKE_object_pose_armature_get_visible(struct Object *ob, struct ViewLayer *view_layer);
diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h
index 2840971f1573380987aaf62ed20797d55cd3b2aa..fa67c07395d15501efbddf357a085d2b29772080 100644
--- a/source/blender/blenkernel/BKE_paint.h
+++ b/source/blender/blenkernel/BKE_paint.h
@@ -93,10 +93,8 @@ typedef enum eOverlayControlFlags {
 						     PAINT_OVERLAY_OVERRIDE_PRIMARY | \
 						     PAINT_OVERLAY_OVERRIDE_CURSOR)
 
-void BKE_paint_invalidate_overlay_tex(
-        struct Scene *scene, struct ViewLayer *view_layer, const struct Tex *tex, eObjectMode object_mode);
-void BKE_paint_invalidate_cursor_overlay(
-        struct Scene *scene, struct ViewLayer *view_layer, struct CurveMapping *curve, eObjectMode object_mode);
+void BKE_paint_invalidate_overlay_tex(struct Scene *scene, struct ViewLayer *view_layer, const struct Tex *tex);
+void BKE_paint_invalidate_cursor_overlay(struct Scene *scene, struct ViewLayer *view_layer, struct CurveMapping *curve);
 void BKE_paint_invalidate_overlay_all(void);
 eOverlayControlFlags BKE_paint_get_overlay_flags(void);
 void BKE_paint_reset_overlay_invalid(eOverlayControlFlags flag);
@@ -130,8 +128,7 @@ void BKE_paint_cavity_curve_preset(struct Paint *p, int preset);
 
 eObjectMode BKE_paint_object_mode_from_paint_mode(ePaintMode mode);
 struct Paint *BKE_paint_get_active_from_paintmode(struct Scene *sce, ePaintMode mode);
-struct Paint *BKE_paint_get_active(
-        struct Scene *sce, struct ViewLayer *view_layer, const eObjectMode object_mode);
+struct Paint *BKE_paint_get_active(struct Scene *sce, struct ViewLayer *view_layer);
 struct Paint *BKE_paint_get_active_from_context(const struct bContext *C);
 ePaintMode BKE_paintmode_get_active_from_context(const struct bContext *C);
 struct Brush *BKE_paint_brush(struct Paint *paint);
@@ -147,9 +144,9 @@ bool BKE_paint_proj_mesh_data_check(struct Scene *scene, struct Object *ob, bool
 /* testing face select mode
  * Texture paint could be removed since selected faces are not used
  * however hiding faces is useful */
-bool BKE_paint_select_face_test(struct Object *ob, eObjectMode object_mode);
-bool BKE_paint_select_vert_test(struct Object *ob, eObjectMode object_mode);
-bool BKE_paint_select_elem_test(struct Object *ob, eObjectMode object_mode);
+bool BKE_paint_select_face_test(struct Object *ob);
+bool BKE_paint_select_vert_test(struct Object *ob);
+bool BKE_paint_select_elem_test(struct Object *ob);
 
 /* partial visibility */
 bool paint_is_face_hidden(const struct MLoopTri *lt, const struct MVert *mvert, const struct MLoop *mloop);
diff --git a/source/blender/blenkernel/BKE_particle.h b/source/blender/blenkernel/BKE_particle.h
index 2db4126057abc00dcce6ec336523922751f16543..886ccb9113af5673c21ef2b109841db287a9413a 100644
--- a/source/blender/blenkernel/BKE_particle.h
+++ b/source/blender/blenkernel/BKE_particle.h
@@ -301,8 +301,7 @@ void psys_set_current_num(Object *ob, int index);
 
 struct LatticeDeformData *psys_create_lattice_deform_data(struct ParticleSimulationData *sim);
 
-bool psys_in_edit_mode(
-        const struct EvaluationContext *eval_ctx, struct ViewLayer *view_layer, struct ParticleSystem *psys);
+bool psys_in_edit_mode(struct ViewLayer *view_layer, struct ParticleSystem *psys);
 bool psys_check_enabled(struct Object *ob, struct ParticleSystem *psys, const bool use_render_params);
 bool psys_check_edited(struct ParticleSystem *psys);
 
@@ -485,11 +484,6 @@ typedef struct ParticleRenderData {
 
 struct EvaluationContext;
 
-void BKE_particle_system_settings_eval(const struct EvaluationContext *eval_ctx,
-                                       struct ParticleSystem *psys);
-void BKE_particle_system_settings_recalc_clear(struct EvaluationContext *UNUSED(eval_ctx),
-                                               struct ParticleSettings *particle_settings);
-
 void BKE_particle_system_eval_init(const struct EvaluationContext *eval_ctx,
                                    struct Scene *scene,
                                    struct Object *ob);
diff --git a/source/blender/blenkernel/BKE_pointcache.h b/source/blender/blenkernel/BKE_pointcache.h
index e9be6d9f7ca7d9d63b63e998c5b3e35b90ca73db..4a3dd72836a8228b18b54ed22ff398ef3f83835e 100644
--- a/source/blender/blenkernel/BKE_pointcache.h
+++ b/source/blender/blenkernel/BKE_pointcache.h
@@ -230,7 +230,6 @@ typedef struct PTCacheEditPoint {
 } PTCacheEditPoint;
 
 typedef struct PTCacheUndo {
-	struct PTCacheUndo *next, *prev;
 	struct PTCacheEditPoint *points;
 
 	/* particles stuff */
@@ -243,12 +242,11 @@ typedef struct PTCacheUndo {
 	struct ListBase mem_cache;
 
 	int totpoint;
-	char name[64];
+
+	size_t undo_size;
 } PTCacheUndo;
 
 typedef struct PTCacheEdit {
-	ListBase undo;
-	struct PTCacheUndo *curundo;
 	PTCacheEditPoint *points;
 
 	struct PTCacheID pid;
diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h
index 4782d87d67d4539b909f2953692a372f0dbf65c3..4984a33df2b0e753f5763d97b8e0f34bd1235202 100644
--- a/source/blender/blenkernel/BKE_screen.h
+++ b/source/blender/blenkernel/BKE_screen.h
@@ -331,7 +331,6 @@ void BKE_screen_view3d_scene_sync(struct bScreen *sc, struct Scene *scene);
 void BKE_screen_transform_orientation_remove(
         const struct bScreen *screen, const struct WorkSpace *workspace,
         const struct TransformOrientation *orientation) ATTR_NONNULL();
-void BKE_screen_gpu_fx_validate(struct GPUFXSettings *fx_settings);
 bool BKE_screen_is_fullscreen_area(const struct bScreen *screen) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
 bool BKE_screen_is_used(const struct bScreen *screen) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
 
diff --git a/source/blender/blenkernel/BKE_sequencer.h b/source/blender/blenkernel/BKE_sequencer.h
index 62e09809dea782d3f2f1da52b716edc32b69a351..140f3dd513de2f05d17093736aabeb80c8b09d37 100644
--- a/source/blender/blenkernel/BKE_sequencer.h
+++ b/source/blender/blenkernel/BKE_sequencer.h
@@ -438,7 +438,7 @@ typedef struct ImBuf *(*SequencerDrawView)(
         struct ViewLayer *view_layer, struct Object *camera, int width, int height,
         unsigned int flag, unsigned int draw_flags, int drawtype, int alpha_mode,
         int samples, const char *viewname,
-        struct GPUFX *fx, struct GPUOffScreen *ofs, char err_out[256]);
+        struct GPUOffScreen *ofs, char err_out[256]);
 extern SequencerDrawView sequencer_view3d_cb;
 
 /* copy/paste */
diff --git a/source/blender/blenkernel/BKE_shrinkwrap.h b/source/blender/blenkernel/BKE_shrinkwrap.h
index 0c4f2bc9c05f618530949b07c122f719519bdeff..d2ab4f3164c50b53ae788a459362c54a1a200841 100644
--- a/source/blender/blenkernel/BKE_shrinkwrap.h
+++ b/source/blender/blenkernel/BKE_shrinkwrap.h
@@ -54,7 +54,6 @@ struct ShrinkwrapModifierData;
 struct MDeformVert;
 struct BVHTree;
 struct SpaceTransform;
-struct EvaluationContext;
 
 
 typedef struct ShrinkwrapCalcData {
@@ -77,10 +76,8 @@ typedef struct ShrinkwrapCalcData {
 
 } ShrinkwrapCalcData;
 
-void shrinkwrapModifier_deform(
-        const struct EvaluationContext *eval_ctx,
-        struct ShrinkwrapModifierData *smd, struct Object *ob, struct DerivedMesh *dm,
-        float (*vertexCos)[3], int numVerts, bool for_render);
+void shrinkwrapModifier_deform(struct ShrinkwrapModifierData *smd, struct Object *ob, struct DerivedMesh *dm,
+                               float (*vertexCos)[3], int numVerts, bool for_render);
 
 /*
  * This function casts a ray in the given BVHTree.. but it takes into consideration the space_transform, that is:
diff --git a/source/blender/blenkernel/BKE_text.h b/source/blender/blenkernel/BKE_text.h
index 14d3318e0599ac7b71377a71c9a65959d6e9bc94..98db06752c8ab0932b4cd9a8d125784e8ac365ee 100644
--- a/source/blender/blenkernel/BKE_text.h
+++ b/source/blender/blenkernel/BKE_text.h
@@ -40,6 +40,7 @@ extern "C" {
 struct Main;
 struct Text;
 struct TextLine;
+struct TextUndoBuf;
 
 void			BKE_text_free_lines	(struct Text *text);
 void			BKE_text_free		(struct Text *text);
@@ -55,8 +56,8 @@ struct Text    *BKE_text_load	(struct Main *bmain, const char *file, const char
 void            BKE_text_copy_data(struct Main *bmain, struct Text *ta_dst, const struct Text *ta_src, const int flag);
 struct Text    *BKE_text_copy		(struct Main *bmain, const struct Text *ta);
 void            BKE_text_make_local (struct Main *bmain, struct Text *text, const bool lib_local);
-void			BKE_text_clear      (struct Text *text);
-void			BKE_text_write      (struct Text *text, const char *str);
+void			BKE_text_clear      (struct Text *text, struct TextUndoBuf *utxt);
+void			BKE_text_write      (struct Text *text, struct TextUndoBuf *utxt, const char *str);
 int             BKE_text_file_modified_check(struct Text *text);
 void            BKE_text_file_modified_ignore(struct Text *text);
 
@@ -83,29 +84,29 @@ void	txt_move_eol		(struct Text *text, const bool sel);
 void	txt_move_toline		(struct Text *text, unsigned int line, const bool sel);
 void	txt_move_to			(struct Text *text, unsigned int line, unsigned int ch, const bool sel);
 void	txt_pop_sel			(struct Text *text);
-void	txt_delete_char		(struct Text *text);
-void	txt_delete_word		(struct Text *text);
-void	txt_delete_selected	(struct Text *text);
+void	txt_delete_char		(struct Text *text, struct TextUndoBuf *utxt);
+void	txt_delete_word		(struct Text *text, struct TextUndoBuf *utxt);
+void	txt_delete_selected	(struct Text *text, struct TextUndoBuf *utxt);
 void	txt_sel_all			(struct Text *text);
 void	txt_sel_clear		(struct Text *text);
 void	txt_sel_line		(struct Text *text);
 char   *txt_sel_to_buf		(struct Text *text);
-void	txt_insert_buf		(struct Text *text, const char *in_buffer);
-void	txt_undo_add_op		(struct Text *text, int op);
-void	txt_do_undo			(struct Text *text);
-void	txt_do_redo			(struct Text *text);
-void	txt_split_curline	(struct Text *text);
-void	txt_backspace_char	(struct Text *text);
-void	txt_backspace_word	(struct Text *text);
-bool	txt_add_char		(struct Text *text, unsigned int add);
-bool	txt_add_raw_char	(struct Text *text, unsigned int add);
-bool	txt_replace_char	(struct Text *text, unsigned int add);
-void	txt_unindent		(struct Text *text);
-void 	txt_comment			(struct Text *text);
-void 	txt_indent			(struct Text *text);
-void	txt_uncomment		(struct Text *text);
-void	txt_move_lines		(struct Text *text, const int direction);
-void	txt_duplicate_line	(struct Text *text);
+void	txt_insert_buf		(struct Text *text, struct TextUndoBuf *utxt, const char *in_buffer);
+void	txt_undo_add_op		(struct Text *text, struct TextUndoBuf *utxt, int op);
+void	txt_do_undo			(struct Text *text, struct TextUndoBuf *utxt);
+void	txt_do_redo			(struct Text *text, struct TextUndoBuf *utxt);
+void	txt_split_curline	(struct Text *text, struct TextUndoBuf *utxt);
+void	txt_backspace_char	(struct Text *text, struct TextUndoBuf *utxt);
+void	txt_backspace_word	(struct Text *text, struct TextUndoBuf *utxt);
+bool	txt_add_char		(struct Text *text, struct TextUndoBuf *utxt, unsigned int add);
+bool	txt_add_raw_char	(struct Text *text, struct TextUndoBuf *utxt, unsigned int add);
+bool	txt_replace_char	(struct Text *text, struct TextUndoBuf *utxt, unsigned int add);
+void	txt_unindent		(struct Text *text, struct TextUndoBuf *utxt);
+void 	txt_comment		(struct Text *text, struct TextUndoBuf *utxt);
+void 	txt_indent			(struct Text *text, struct TextUndoBuf *utxt);
+void	txt_uncomment		(struct Text *text, struct TextUndoBuf *utxt);
+void	txt_move_lines		(struct Text *text, struct TextUndoBuf *utxt, const int direction);
+void	txt_duplicate_line	(struct Text *text, struct TextUndoBuf *utxt);
 int		txt_setcurr_tab_spaces(struct Text *text, int space);
 bool	txt_cursor_is_line_start(struct Text *text);
 bool	txt_cursor_is_line_end(struct Text *text);
@@ -135,46 +136,10 @@ enum {
 	TXT_MOVE_LINE_DOWN =  1
 };
 
-
-/* Undo opcodes */
-
-/* Complex editing */
-/* 1 - opcode is followed by 1 byte for ascii character and opcode (repeat)) */
-/* 2 - opcode is followed by 2 bytes for utf-8 character and opcode (repeat)) */
-/* 3 - opcode is followed by 3 bytes for utf-8 character and opcode (repeat)) */
-/* 4 - opcode is followed by 4 bytes for unicode character and opcode (repeat)) */
-#define UNDO_INSERT_1   013
-#define UNDO_INSERT_2   014
-#define UNDO_INSERT_3   015
-#define UNDO_INSERT_4   016
-
-#define UNDO_BS_1       017
-#define UNDO_BS_2       020
-#define UNDO_BS_3       021
-#define UNDO_BS_4       022
-
-#define UNDO_DEL_1      023
-#define UNDO_DEL_2      024
-#define UNDO_DEL_3      025
-#define UNDO_DEL_4      026
-
-/* Text block (opcode is followed
- * by 4 character length ID + the text
- * block itself + the 4 character length
- * ID (repeat) and opcode (repeat)) */
-#define UNDO_DBLOCK     027 /* Delete block */
-#define UNDO_IBLOCK     030 /* Insert block */
-
-/* Misc */
-#define UNDO_INDENT     032
-#define UNDO_UNINDENT   033
-#define UNDO_COMMENT    034
-#define UNDO_UNCOMMENT  035
-
-#define UNDO_MOVE_LINES_UP      036
-#define UNDO_MOVE_LINES_DOWN    037
-
-#define UNDO_DUPLICATE  040
+typedef struct TextUndoBuf {
+	char *buf;
+	int pos, len;
+} TextUndoBuf;
 
 #ifdef __cplusplus
 }
diff --git a/source/blender/blenkernel/BKE_undo_system.h b/source/blender/blenkernel/BKE_undo_system.h
new file mode 100644
index 0000000000000000000000000000000000000000..9697c7dd8e2a184773a2a9744e03f379119bf751
--- /dev/null
+++ b/source/blender/blenkernel/BKE_undo_system.h
@@ -0,0 +1,196 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+#ifndef __BKE_UNDO_SYSTEM_H__
+#define __BKE_UNDO_SYSTEM_H__
+
+/** \file BKE_undo_system.h
+ *  \ingroup bke
+ */
+
+struct Main;
+struct UndoStep;
+struct bContext;
+
+/* ID's */
+struct Mesh;
+struct Object;
+struct Scene;
+struct Text;
+
+#include "DNA_ID.h"
+#include "DNA_listBase.h"
+
+typedef struct UndoRefID { struct ID *ptr; char name[MAX_ID_NAME]; } UndoRefID;
+/* UndoRefID_Mesh & friends. */
+#define UNDO_REF_ID_TYPE(ptr_ty) \
+	typedef struct UndoRefID_##ptr_ty { struct ptr_ty *ptr; char name[MAX_ID_NAME]; } UndoRefID_##ptr_ty
+UNDO_REF_ID_TYPE(Mesh);
+UNDO_REF_ID_TYPE(Object);
+UNDO_REF_ID_TYPE(Scene);
+UNDO_REF_ID_TYPE(Text);
+
+typedef struct UndoStack {
+	ListBase         steps;
+	struct UndoStep *step_active;
+
+	/**
+	 * Some undo systems require begin/end, see: #UndoType.step_encode_init
+	 *
+	 * \note This is not included in the 'steps' list.
+	 * That is done once end is called.
+	 */
+	struct UndoStep *step_init;
+} UndoStack;
+
+
+typedef struct UndoStep {
+	struct UndoStep *next, *prev;
+	char name[64];
+	const struct UndoType *type;
+	/** Size in bytes of all data in step (not including the step). */
+	size_t data_size;
+	/** Users should never see this step (only use for internal consistency). */
+	bool skip;
+	/* Over alloc 'type->struct_size'. */
+} UndoStep;
+
+typedef enum eUndoTypeMode {
+	/**
+	 * Each undo step stores a version of the state.
+	 * This means we can simply load in a previous state at any time.
+	 */
+	BKE_UNDOTYPE_MODE_STORE = 1,
+	/**
+	 * Each undo step is a series of edits.
+	 * This means to change states we need to apply each edit.
+	 * It also means the 'step_decode' callback needs to detect the difference between undo and redo.
+	 * (Currently used for text edit and image & sculpt painting).
+	 */
+	BKE_UNDOTYPE_MODE_ACCUMULATE = 2,
+} eUndoTypeMode;
+
+typedef void (*UndoTypeForEachIDRefFn)(void *user_data, struct UndoRefID *id_ref);
+
+typedef struct UndoType {
+	struct UndoType *next, *prev;
+	/** Only for debugging. */
+	const char *name;
+
+	/**
+	 * When NULL, we don't consider this undo type for context checks.
+	 * Operators must explicitly set the undo type and handle adding the undo step.
+	 * This is needed when tools operate on data which isn't the primary mode (eg, paint-curve in sculpt mode).
+	 */
+	bool (*poll)(struct bContext *C);
+
+	/**
+	 * None of these callbacks manage list add/removal.
+	 *
+	 * Note that 'step_encode_init' is optional,
+	 * some undo types need to perform operatons before undo push finishes.
+	 */
+	void (*step_encode_init)(struct bContext *C, UndoStep *us);
+
+	bool (*step_encode)(struct bContext *C, UndoStep *us);
+	void (*step_decode)(struct bContext *C, UndoStep *us, int dir);
+
+	/**
+	 * \note When freeing all steps,
+	 * free from the last since #MemFileUndoType will merge with the next undo type in the list. */
+	void (*step_free)(UndoStep *us);
+
+	void (*step_foreach_ID_ref)(UndoStep *us, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data);
+
+	eUndoTypeMode mode;
+	bool use_context;
+
+	int step_size;
+} UndoType;
+
+/* expose since we need to perform operations on spesific undo types (rarely). */
+extern const UndoType *BKE_UNDOSYS_TYPE_IMAGE;
+extern const UndoType *BKE_UNDOSYS_TYPE_MEMFILE;
+extern const UndoType *BKE_UNDOSYS_TYPE_PAINTCURVE;
+extern const UndoType *BKE_UNDOSYS_TYPE_PARTICLE;
+extern const UndoType *BKE_UNDOSYS_TYPE_SCULPT;
+extern const UndoType *BKE_UNDOSYS_TYPE_TEXT;
+
+UndoStack      *BKE_undosys_stack_create(void);
+void            BKE_undosys_stack_destroy(UndoStack *ustack);
+void            BKE_undosys_stack_clear(UndoStack *ustack);
+bool            BKE_undosys_stack_has_undo(UndoStack *ustack, const char *name);
+void            BKE_undosys_stack_init_from_main(UndoStack *ustack, struct Main *bmain);
+UndoStep       *BKE_undosys_stack_active_with_type(UndoStack *ustack, const UndoType *ut);
+UndoStep       *BKE_undosys_stack_init_or_active_with_type(UndoStack *ustack, const UndoType *ut);
+void            BKE_undosys_stack_limit_steps_and_memory(UndoStack *ustack, int steps, size_t memory_limit);
+
+/* Only some UndoType's require init. */
+UndoStep *BKE_undosys_step_push_init_with_type(UndoStack *ustack, struct bContext *C, const char *name, const UndoType *ut);
+UndoStep *BKE_undosys_step_push_init(UndoStack *ustack, struct bContext *C, const char *name);
+
+bool BKE_undosys_step_push_with_type(UndoStack *ustack, struct bContext *C, const char *name, const UndoType *ut);
+bool BKE_undosys_step_push(UndoStack *ustack, struct bContext *C, const char *name);
+
+UndoStep *BKE_undosys_step_find_by_name_with_type(UndoStack *ustack, const char *name, const UndoType *ut);
+UndoStep *BKE_undosys_step_find_by_name(UndoStack *ustack, const char *name);
+
+bool BKE_undosys_step_undo_with_data_ex(UndoStack *ustack, struct bContext *C, UndoStep *us, bool use_skip);
+bool BKE_undosys_step_undo_with_data(UndoStack *ustack, struct bContext *C, UndoStep *us);
+bool BKE_undosys_step_undo(UndoStack *ustack, struct bContext *C);
+
+bool BKE_undosys_step_redo_with_data_ex(UndoStack *ustack, struct bContext *C, UndoStep *us, bool use_skip);
+bool BKE_undosys_step_redo_with_data(UndoStack *ustack, struct bContext *C, UndoStep *us);
+bool BKE_undosys_step_redo(UndoStack *ustack, struct bContext *C);
+
+bool BKE_undosys_step_load_data(UndoStack *ustack, struct bContext *C, UndoStep *us);
+
+bool BKE_undosys_step_undo_compat_only(UndoStack *ustack, struct bContext *C, int step);
+void BKE_undosys_step_undo_from_index(UndoStack *ustack, struct bContext *C, int index);
+UndoStep *BKE_undosys_step_same_type_next(UndoStep *us);
+UndoStep *BKE_undosys_step_same_type_prev(UndoStep *us);
+
+/* Type System */
+UndoType *BKE_undosys_type_append(void (*undosys_fn)(UndoType *));
+void      BKE_undosys_type_free_all(void);
+
+/* ID Accessor */
+#if 0  /* functionality is only used internally for now. */
+void BKE_undosys_foreach_ID_ref(UndoStack *ustack, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data);
+#endif
+
+/* Use when the undo step stores many arbitrary pointers. */
+struct UndoIDPtrMap;
+struct UndoIDPtrMap *BKE_undosys_ID_map_create(void);
+void                 BKE_undosys_ID_map_destroy(struct UndoIDPtrMap *map);
+void                 BKE_undosys_ID_map_add(struct UndoIDPtrMap *map, ID *id);
+struct ID           *BKE_undosys_ID_map_lookup(const struct UndoIDPtrMap *map, const struct ID *id_src);
+
+void BKE_undosys_ID_map_add_with_prev(
+        struct UndoIDPtrMap *map, struct ID *id,
+        struct ID **id_prev);
+struct ID *BKE_undosys_ID_map_lookup_with_prev(
+        const struct UndoIDPtrMap *map, struct ID *id_src,
+        struct ID *id_prev_match[2]);
+
+void BKE_undosys_ID_map_foreach_ID_ref(
+        struct UndoIDPtrMap *map,
+        UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data);
+
+#endif  /* __BKE_UNDO_SYSTEM_H__ */
diff --git a/source/blender/blenkernel/BKE_workspace.h b/source/blender/blenkernel/BKE_workspace.h
index 0aff79b7e304a8b44a0a19f3ec04067cbd76ce56..9ba4105edda4590e1b51661f580271370ad80fac 100644
--- a/source/blender/blenkernel/BKE_workspace.h
+++ b/source/blender/blenkernel/BKE_workspace.h
@@ -95,6 +95,7 @@ void             BKE_workspace_active_layout_set(struct WorkSpaceInstanceHook *h
 struct bScreen *BKE_workspace_active_screen_get(const struct WorkSpaceInstanceHook *hook) GETTER_ATTRS;
 void            BKE_workspace_active_screen_set(
         struct WorkSpaceInstanceHook *hook, struct WorkSpace *workspace, struct bScreen *screen) SETTER_ATTRS;
+
 struct Base *BKE_workspace_active_base_get(const struct WorkSpace *workspace, const struct Scene *scene);
 struct ListBase *BKE_workspace_transform_orientations_get(struct WorkSpace *workspace) GETTER_ATTRS;
 struct ViewLayer *BKE_workspace_view_layer_get(
@@ -129,13 +130,6 @@ void BKE_workspace_update_tagged(struct EvaluationContext *eval_ctx,
                                  struct WorkSpace *workspace,
                                  struct Scene *scene);
 
-void BKE_workspace_update_object_mode(
-        struct EvaluationContext *eval_ctx,
-        struct WorkSpace *workspace);
-
-struct Object *BKE_workspace_edit_object(
-        struct WorkSpace *workspace, struct Scene *scene);
-
 bool BKE_workspace_owner_id_check(
         const struct WorkSpace *workspace, const char *owner_id) ATTR_NONNULL();
 
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index f36a9e99a64f7853d86408a89fa699a9b51d13fc..6a1c3ea883c0ed20a6b9a39e508cbb8731eff4a8 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -49,6 +49,7 @@ set(INC
 	../../../intern/mikktspace
 	../../../intern/smoke/extern
 	../../../intern/atomic
+	../../../intern/clog
 	../../../intern/libmv
 	../../../extern/curve_fit_nd
 )
@@ -194,6 +195,7 @@ set(SRC
 	intern/tracking_solver.c
 	intern/tracking_stabilize.c
 	intern/tracking_util.c
+	intern/undo_system.c
 	intern/unit.c
 	intern/workspace.c
 	intern/world.c
@@ -309,6 +311,7 @@ set(SRC
 	BKE_text.h
 	BKE_texture.h
 	BKE_tracking.h
+	BKE_undo_system.h
 	BKE_unit.h
 	BKE_workspace.h
 	BKE_world.h
diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c
index 3001b1f05d553df7f30ca3695ba527b21d4f5557..916e11dbd46f2da56923fb478022a83cd9c70fee 100644
--- a/source/blender/blenkernel/intern/DerivedMesh.c
+++ b/source/blender/blenkernel/intern/DerivedMesh.c
@@ -51,8 +51,6 @@
 #include "BLI_linklist.h"
 #include "BLI_task.h"
 
-#include "DEG_depsgraph.h"
-
 #include "BKE_cdderivedmesh.h"
 #include "BKE_colorband.h"
 #include "BKE_editmesh.h"
@@ -352,7 +350,7 @@ void DM_init(
 	dm->numPolyData = numPolys;
 
 	DM_init_funcs(dm);
-
+	
 	dm->needsFree = 1;
 	dm->auto_bump_scale = -1.0f;
 	dm->dirty = 0;
@@ -412,7 +410,6 @@ int DM_release(DerivedMesh *dm)
 	if (dm->needsFree) {
 		bvhcache_free(&dm->bvhCache);
 		GPU_drawobject_free(dm);
-
 		CustomData_free(&dm->vertData, dm->numVertData);
 		CustomData_free(&dm->edgeData, dm->numEdgeData);
 		CustomData_free(&dm->faceData, dm->numTessFaceData);
@@ -1778,19 +1775,17 @@ static void mesh_calc_modifiers(
 	MultiresModifierData *mmd = get_multires_modifier(scene, ob, 0);
 	const bool has_multires = (mmd && mmd->sculptlvl != 0);
 	bool multires_applied = false;
-	const bool sculpt_mode = eval_ctx->object_mode & OB_MODE_SCULPT && ob->sculpt && !useRenderParams;
+	const bool sculpt_mode = ob->mode & OB_MODE_SCULPT && ob->sculpt && !useRenderParams;
 	const bool sculpt_dyntopo = (sculpt_mode && ob->sculpt->bm)  && !useRenderParams;
 	const int draw_flag = dm_drawflag_calc(scene->toolsettings, me);
 
 	/* Generic preview only in object mode! */
-	const bool do_mod_mcol = (eval_ctx->object_mode == OB_MODE_OBJECT);
+	const bool do_mod_mcol = (ob->mode == OB_MODE_OBJECT);
 #if 0 /* XXX Will re-enable this when we have global mod stack options. */
 	const bool do_final_wmcol = (scene->toolsettings->weights_preview == WP_WPREVIEW_FINAL) && do_wmcol;
 #endif
 	const bool do_final_wmcol = false;
-	const bool do_init_wmcol = (
-	        (dataMask & CD_MASK_PREVIEW_MLOOPCOL) &&
-	        (eval_ctx->object_mode & OB_MODE_WEIGHT_PAINT) && !do_final_wmcol);
+	const bool do_init_wmcol = ((dataMask & CD_MASK_PREVIEW_MLOOPCOL) && (ob->mode & OB_MODE_WEIGHT_PAINT) && !do_final_wmcol);
 	/* XXX Same as above... For now, only weights preview in WPaint mode. */
 	const bool do_mod_wmcol = do_init_wmcol;
 
@@ -2628,7 +2623,7 @@ static bool calc_modifiers_skip_orco(const EvaluationContext *eval_ctx,
 		if (U.opensubdiv_compute_type == USER_OPENSUBDIV_COMPUTE_NONE) {
 			return false;
 		}
-		else if ((eval_ctx->object_mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)) != 0) {
+		else if ((ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)) != 0) {
 			return false;
 		}
 		else if ((DEG_get_eval_flags_for_id(eval_ctx->depsgraph, &ob->id) & DAG_EVAL_NEED_CPU) != 0) {
@@ -2669,7 +2664,7 @@ static void mesh_build_data(
 	ob->lastDataMask = dataMask;
 	ob->lastNeedMapping = need_mapping;
 
-	if ((eval_ctx->object_mode & OB_MODE_ALL_SCULPT) && ob->sculpt) {
+	if ((ob->mode & OB_MODE_ALL_SCULPT) && ob->sculpt) {
 		/* create PBVH immediately (would be created on the fly too,
 		 * but this avoids waiting on first stroke) */
 
@@ -2707,9 +2702,7 @@ static void editbmesh_build_data(
 	BLI_assert(!(em->derivedFinal->dirty & DM_DIRTY_NORMALS));
 }
 
-static CustomDataMask object_get_datamask(
-        const EvaluationContext *eval_ctx,
-        const Scene *scene, Object *ob, bool *r_need_mapping)
+static CustomDataMask object_get_datamask(const Scene *scene, Object *ob, bool *r_need_mapping)
 {
 	/* TODO(sergey): Avoid this linear list lookup. */
 	ViewLayer *view_layer = BKE_view_layer_context_active_PLACEHOLDER(scene);
@@ -2721,28 +2714,28 @@ static CustomDataMask object_get_datamask(
 	}
 
 	if (ob == actob) {
-		bool editing = BKE_paint_select_face_test(ob, eval_ctx->object_mode);
+		bool editing = BKE_paint_select_face_test(ob);
 
 		/* weight paint and face select need original indices because of selection buffer drawing */
 		if (r_need_mapping) {
-			*r_need_mapping = (editing || (eval_ctx->object_mode & (OB_MODE_WEIGHT_PAINT | OB_MODE_VERTEX_PAINT)));
+			*r_need_mapping = (editing || (ob->mode & (OB_MODE_WEIGHT_PAINT | OB_MODE_VERTEX_PAINT)));
 		}
 
 		/* check if we need tfaces & mcols due to face select or texture paint */
-		if ((eval_ctx->object_mode & OB_MODE_TEXTURE_PAINT) || editing) {
+		if ((ob->mode & OB_MODE_TEXTURE_PAINT) || editing) {
 			mask |= CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL;
 		}
 
 		/* check if we need mcols due to vertex paint or weightpaint */
-		if (eval_ctx->object_mode & OB_MODE_VERTEX_PAINT) {
+		if (ob->mode & OB_MODE_VERTEX_PAINT) {
 			mask |= CD_MASK_MLOOPCOL;
 		}
 
-		if (eval_ctx->object_mode & OB_MODE_WEIGHT_PAINT) {
+		if (ob->mode & OB_MODE_WEIGHT_PAINT) {
 			mask |= CD_MASK_PREVIEW_MLOOPCOL;
 		}
 
-		if (eval_ctx->object_mode & OB_MODE_EDIT)
+		if (ob->mode & OB_MODE_EDIT)
 			mask |= CD_MASK_MVERT_SKIN;
 	}
 
@@ -2754,7 +2747,7 @@ void makeDerivedMesh(
         CustomDataMask dataMask, const bool build_shapekey_layers)
 {
 	bool need_mapping;
-	dataMask |= object_get_datamask(eval_ctx, scene, ob, &need_mapping);
+	dataMask |= object_get_datamask(scene, ob, &need_mapping);
 
 	if (em) {
 		editbmesh_build_data(eval_ctx, scene, ob, em, dataMask);
@@ -2773,7 +2766,7 @@ DerivedMesh *mesh_get_derived_final(
 	 * the data we need, rebuild the derived mesh
 	 */
 	bool need_mapping;
-	dataMask |= object_get_datamask(eval_ctx, scene, ob, &need_mapping);
+	dataMask |= object_get_datamask(scene, ob, &need_mapping);
 
 	if (!ob->derivedFinal ||
 	    ((dataMask & ob->lastDataMask) != dataMask) ||
@@ -2793,7 +2786,7 @@ DerivedMesh *mesh_get_derived_deform(const struct EvaluationContext *eval_ctx, S
 	 */
 	bool need_mapping;
 
-	dataMask |= object_get_datamask(eval_ctx, scene, ob, &need_mapping);
+	dataMask |= object_get_datamask(scene, ob, &need_mapping);
 
 	if (!ob->derivedDeform ||
 	    ((dataMask & ob->lastDataMask) != dataMask) ||
@@ -2912,7 +2905,7 @@ DerivedMesh *editbmesh_get_derived_cage_and_final(
 	/* if there's no derived mesh or the last data mask used doesn't include
 	 * the data we need, rebuild the derived mesh
 	 */
-	dataMask |= object_get_datamask(eval_ctx, scene, obedit, NULL);
+	dataMask |= object_get_datamask(scene, obedit, NULL);
 
 	if (!em->derivedCage ||
 	    (em->lastDataMask & dataMask) != dataMask)
@@ -2932,7 +2925,7 @@ DerivedMesh *editbmesh_get_derived_cage(
 	/* if there's no derived mesh or the last data mask used doesn't include
 	 * the data we need, rebuild the derived mesh
 	 */
-	dataMask |= object_get_datamask(eval_ctx, scene, obedit, NULL);
+	dataMask |= object_get_datamask(scene, obedit, NULL);
 
 	if (!em->derivedCage ||
 	    (em->lastDataMask & dataMask) != dataMask)
diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c
index 953fef067b497fc89c976a650a8c4b11025fe7a8..0a8c97ff175b4fc1f2798569213623e3bb71a36d 100644
--- a/source/blender/blenkernel/intern/armature.c
+++ b/source/blender/blenkernel/intern/armature.c
@@ -63,7 +63,6 @@
 #include "BKE_DerivedMesh.h"
 #include "BKE_deform.h"
 #include "BKE_displist.h"
-#include "BKE_global.h"
 #include "BKE_idprop.h"
 #include "BKE_library.h"
 #include "BKE_library_query.h"
diff --git a/source/blender/blenkernel/intern/armature_update.c b/source/blender/blenkernel/intern/armature_update.c
index bf065ef992c8931bf2cda5403ed33a3ad71f8183..29baaff32ba8e1c4b7d9d9d80905842e7d85c222 100644
--- a/source/blender/blenkernel/intern/armature_update.c
+++ b/source/blender/blenkernel/intern/armature_update.c
@@ -47,7 +47,6 @@
 
 #include "BIK_api.h"
 
-#include "BKE_global.h"
 #include "BKE_main.h"
 
 #include "DEG_depsgraph.h"
@@ -559,12 +558,21 @@ void BKE_splineik_execute_tree(
 
 /* *************** Depsgraph evaluation callbacks ************ */
 
+BLI_INLINE bPoseChannel *pose_pchan_get_indexed(Object *ob, int pchan_index)
+{
+	bPose *pose = ob->pose;
+	BLI_assert(pose != NULL);
+	BLI_assert(pchan_index >= 0);
+	BLI_assert(pchan_index < MEM_allocN_len(pose->chan_array) / sizeof(bPoseChannel *));
+	return pose->chan_array[pchan_index];
+}
+
 void BKE_pose_eval_init(const struct EvaluationContext *UNUSED(eval_ctx),
                         Scene *UNUSED(scene),
-                        Object *ob,
-                        bPose *pose)
+                        Object *ob)
 {
-	bPoseChannel *pchan;
+	bPose *pose = ob->pose;
+	BLI_assert(pose != NULL);
 
 	DEG_debug_print_eval(__func__, ob->id.name, ob);
 
@@ -577,16 +585,21 @@ void BKE_pose_eval_init(const struct EvaluationContext *UNUSED(eval_ctx),
 	/* imat is needed for solvers. */
 	invert_m4_m4(ob->imat, ob->obmat);
 
-	/* 1. clear flags */
-	for (pchan = pose->chanbase.first; pchan != NULL; pchan = pchan->next) {
+	const int num_channels = BLI_listbase_count(&pose->chanbase);
+	pose->chan_array = MEM_malloc_arrayN(
+	        num_channels, sizeof(bPoseChannel *), "pose->chan_array");
+
+	/* clear flags */
+	int pchan_index = 0;
+	for (bPoseChannel *pchan = pose->chanbase.first; pchan != NULL; pchan = pchan->next) {
 		pchan->flag &= ~(POSE_DONE | POSE_CHAIN | POSE_IKTREE | POSE_IKSPLINE);
+		pose->chan_array[pchan_index++] = pchan;
 	}
 }
 
 void BKE_pose_eval_init_ik(const struct EvaluationContext *eval_ctx,
                            Scene *scene,
-                           Object *ob,
-                           bPose *UNUSED(pose))
+                           Object *ob)
 {
 	DEG_debug_print_eval(__func__, ob->id.name, ob);
 	BLI_assert(ob->type == OB_ARMATURE);
@@ -595,11 +608,11 @@ void BKE_pose_eval_init_ik(const struct EvaluationContext *eval_ctx,
 	if (arm->flag & ARM_RESTPOS) {
 		return;
 	}
-	/* 2a. construct the IK tree (standard IK) */
+	/* construct the IK tree (standard IK) */
 	BIK_initialize_tree(eval_ctx, scene, ob, ctime);
-	/* 2b. construct the Spline IK trees
+	/* construct the Spline IK trees
 	 *  - this is not integrated as an IK plugin, since it should be able
-	 *	  to function in conjunction with standard IK
+	 *    to function in conjunction with standard IK
 	 */
 	BKE_pose_splineik_init_tree(scene, ob, ctime);
 }
@@ -607,8 +620,9 @@ void BKE_pose_eval_init_ik(const struct EvaluationContext *eval_ctx,
 void BKE_pose_eval_bone(const struct EvaluationContext *eval_ctx,
                         Scene *scene,
                         Object *ob,
-                        bPoseChannel *pchan)
+                        int pchan_index)
 {
+	bPoseChannel *pchan = pose_pchan_get_indexed(ob, pchan_index);
 	DEG_debug_print_eval_subdata(
 	        __func__, ob->id.name, ob, "pchan", pchan->name, pchan);
 	BLI_assert(ob->type == OB_ARMATURE);
@@ -643,8 +657,9 @@ void BKE_pose_eval_bone(const struct EvaluationContext *eval_ctx,
 void BKE_pose_constraints_evaluate(const struct EvaluationContext *eval_ctx,
                                    Scene *scene,
                                    Object *ob,
-                                   bPoseChannel *pchan)
+                                   int pchan_index)
 {
+	bPoseChannel *pchan = pose_pchan_get_indexed(ob, pchan_index);
 	DEG_debug_print_eval_subdata(
 	        __func__, ob->id.name, ob, "pchan", pchan->name, pchan);
 	bArmature *arm = (bArmature *)ob->data;
@@ -663,8 +678,10 @@ void BKE_pose_constraints_evaluate(const struct EvaluationContext *eval_ctx,
 }
 
 void BKE_pose_bone_done(const struct EvaluationContext *UNUSED(eval_ctx),
-                        bPoseChannel *pchan)
+                        struct Object *ob,
+                        int pchan_index)
 {
+	bPoseChannel *pchan = pose_pchan_get_indexed(ob, pchan_index);
 	float imat[4][4];
 	DEG_debug_print_eval(__func__, pchan->name, pchan);
 	if (pchan->bone) {
@@ -676,8 +693,9 @@ void BKE_pose_bone_done(const struct EvaluationContext *UNUSED(eval_ctx),
 void BKE_pose_iktree_evaluate(const struct EvaluationContext *eval_ctx,
                               Scene *scene,
                               Object *ob,
-                              bPoseChannel *rootchan)
+                              int rootchan_index)
 {
+	bPoseChannel *rootchan = pose_pchan_get_indexed(ob, rootchan_index);
 	DEG_debug_print_eval_subdata(
 	        __func__, ob->id.name, ob, "rootchan", rootchan->name, rootchan);
 	BLI_assert(ob->type == OB_ARMATURE);
@@ -692,9 +710,10 @@ void BKE_pose_iktree_evaluate(const struct EvaluationContext *eval_ctx,
 void BKE_pose_splineik_evaluate(const struct EvaluationContext *eval_ctx,
                                 Scene *scene,
                                 Object *ob,
-                                bPoseChannel *rootchan)
+                                int rootchan_index)
 
 {
+	bPoseChannel *rootchan = pose_pchan_get_indexed(ob, rootchan_index);
 	DEG_debug_print_eval_subdata(
 	        __func__, ob->id.name, ob, "rootchan", rootchan->name, rootchan);
 	BLI_assert(ob->type == OB_ARMATURE);
@@ -708,15 +727,21 @@ void BKE_pose_splineik_evaluate(const struct EvaluationContext *eval_ctx,
 
 void BKE_pose_eval_flush(const struct EvaluationContext *UNUSED(eval_ctx),
                          Scene *scene,
-                         Object *ob,
-                         bPose *UNUSED(pose))
+                         Object *ob)
 {
+	bPose *pose = ob->pose;
+	BLI_assert(pose != NULL);
+
 	float ctime = BKE_scene_frame_get(scene); /* not accurate... */
 	DEG_debug_print_eval(__func__, ob->id.name, ob);
 	BLI_assert(ob->type == OB_ARMATURE);
 
-	/* 6. release the IK tree */
+	/* release the IK tree */
 	BIK_release_tree(scene, ob, ctime);
+
+	BLI_assert(pose->chan_array != NULL);
+	MEM_freeN(pose->chan_array);
+	pose->chan_array = NULL;
 }
 
 void BKE_pose_eval_proxy_copy(const struct EvaluationContext *UNUSED(eval_ctx), Object *ob)
diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c
index e9a6d0fe5a5c9db9e04daa5e31276a141a6961e5..236b965ec348cd9a7f00e6d2cbe990848ceaf367 100644
--- a/source/blender/blenkernel/intern/blender.c
+++ b/source/blender/blenkernel/intern/blender.c
@@ -85,6 +85,10 @@ void BKE_blender_free(void)
 	BKE_main_free(G.main);
 	G.main = NULL;
 
+	if (G.log.file != NULL) {
+		fclose(G.log.file);
+	}
+
 	BKE_spacetypes_free();      /* after free main, it uses space callbacks */
 	
 	IMB_exit();
@@ -134,6 +138,8 @@ void BKE_blender_globals_init(void)
 #else
 	G.f &= ~G_SCRIPT_AUTOEXEC;
 #endif
+
+	G.log.level = 1;
 }
 
 void BKE_blender_globals_clear(void)
diff --git a/source/blender/blenkernel/intern/blender_undo.c b/source/blender/blenkernel/intern/blender_undo.c
index b8f46756445f4cd3031767d731009b2533c12169..98482bcc8b1d58955ba5d7673c2e9d0c77c06811 100644
--- a/source/blender/blenkernel/intern/blender_undo.c
+++ b/source/blender/blenkernel/intern/blender_undo.c
@@ -42,27 +42,18 @@
 
 #include "DNA_scene_types.h"
 
-#include "BLI_fileops.h"
-#include "BLI_listbase.h"
 #include "BLI_path_util.h"
 #include "BLI_string.h"
 #include "BLI_utildefines.h"
 
-#include "IMB_imbuf.h"
-#include "IMB_moviecache.h"
-
 #include "BKE_blender_undo.h"  /* own include */
 #include "BKE_blendfile.h"
 #include "BKE_appdir.h"
-#include "BKE_brush.h"
 #include "BKE_context.h"
 #include "BKE_global.h"
-#include "BKE_image.h"
 #include "BKE_main.h"
-#include "RE_pipeline.h"
 
 #include "BLO_undofile.h"
-#include "BLO_readfile.h"
 #include "BLO_writefile.h"
 
 #include "DEG_depsgraph.h"
@@ -74,44 +65,22 @@
 
 #define UNDO_DISK   0
 
-typedef struct UndoElem {
-	struct UndoElem *next, *prev;
-	char str[FILE_MAX];
-	char name[BKE_UNDO_STR_MAX];
-	MemFile memfile;
-	uintptr_t undosize;
-} UndoElem;
-
-static ListBase undobase = {NULL, NULL};
-static UndoElem *curundo = NULL;
-
-/**
- * Avoid bad-level call to #WM_jobs_kill_all_except()
- */
-static void (*undo_wm_job_kill_callback)(struct bContext *C) = NULL;
-
-void BKE_undo_callback_wm_kill_jobs_set(void (*callback)(struct bContext *C))
-{
-	undo_wm_job_kill_callback = callback;
-}
-
-static int read_undosave(bContext *C, UndoElem *uel)
+bool BKE_memfile_undo_decode(MemFileUndoData *mfu, bContext *C)
 {
 	char mainstr[sizeof(G.main->name)];
 	int success = 0, fileflags;
 
-	/* This is needed so undoing/redoing doesn't crash with threaded previews going */
-	undo_wm_job_kill_callback(C);
-
 	BLI_strncpy(mainstr, G.main->name, sizeof(mainstr));    /* temporal store */
 
 	fileflags = G.fileflags;
 	G.fileflags |= G_FILE_NO_UI;
 
-	if (UNDO_DISK)
-		success = (BKE_blendfile_read(C, uel->str, NULL, 0) != BKE_BLENDFILE_READ_FAIL);
-	else
-		success = BKE_blendfile_read_from_memfile(C, &uel->memfile, NULL, 0);
+	if (UNDO_DISK) {
+		success = (BKE_blendfile_read(C, mfu->filename, NULL, 0) != BKE_BLENDFILE_READ_FAIL);
+	}
+	else {
+		success = BKE_blendfile_read_from_memfile(C, &mfu->memfile, NULL, 0);
+	}
 
 	/* restore */
 	BLI_strncpy(G.main->name, mainstr, sizeof(G.main->name)); /* restore */
@@ -125,283 +94,43 @@ static int read_undosave(bContext *C, UndoElem *uel)
 	return success;
 }
 
-/* name can be a dynamic string */
-void BKE_undo_write(bContext *C, const char *name)
+MemFileUndoData *BKE_memfile_undo_encode(Main *bmain, MemFileUndoData *mfu_prev)
 {
-	uintptr_t maxmem, totmem, memused;
-	int nr /*, success */ /* UNUSED */;
-	UndoElem *uel;
-
-	if ((U.uiflag & USER_GLOBALUNDO) == 0) {
-		return;
-	}
-
-	if (U.undosteps == 0) {
-		return;
-	}
-
-	/* remove all undos after (also when curundo == NULL) */
-	while (undobase.last != curundo) {
-		uel = undobase.last;
-		BLI_remlink(&undobase, uel);
-		BLO_memfile_free(&uel->memfile);
-		MEM_freeN(uel);
-	}
-
-	/* make new */
-	curundo = uel = MEM_callocN(sizeof(UndoElem), "undo file");
-	BLI_strncpy(uel->name, name, sizeof(uel->name));
-	BLI_addtail(&undobase, uel);
-
-	/* and limit amount to the maximum */
-	nr = 0;
-	uel = undobase.last;
-	while (uel) {
-		nr++;
-		if (nr == U.undosteps) break;
-		uel = uel->prev;
-	}
-	if (uel) {
-		while (undobase.first != uel) {
-			UndoElem *first = undobase.first;
-			BLI_remlink(&undobase, first);
-			/* the merge is because of compression */
-			BLO_memfile_merge(&first->memfile, &first->next->memfile);
-			MEM_freeN(first);
-		}
-	}
-
+	MemFileUndoData *mfu = MEM_callocN(sizeof(MemFileUndoData), __func__);
 
 	/* disk save version */
 	if (UNDO_DISK) {
 		static int counter = 0;
-		char filepath[FILE_MAX];
+		char filename[FILE_MAX];
 		char numstr[32];
 		int fileflags = G.fileflags & ~(G_FILE_HISTORY); /* don't do file history on undo */
 
-		/* calculate current filepath */
+		/* Calculate current filename. */
 		counter++;
 		counter = counter % U.undosteps;
 
 		BLI_snprintf(numstr, sizeof(numstr), "%d.blend", counter);
-		BLI_make_file_string("/", filepath, BKE_tempdir_session(), numstr);
-
-		/* success = */ /* UNUSED */ BLO_write_file(CTX_data_main(C), filepath, fileflags, NULL, NULL);
-
-		BLI_strncpy(curundo->str, filepath, sizeof(curundo->str));
-	}
-	else {
-		MemFile *prevfile = NULL;
-
-		if (curundo->prev) prevfile = &(curundo->prev->memfile);
-
-		memused = MEM_get_memory_in_use();
-		/* success = */ /* UNUSED */ BLO_write_file_mem(CTX_data_main(C), prevfile, &curundo->memfile, G.fileflags);
-		curundo->undosize = MEM_get_memory_in_use() - memused;
-	}
-
-	if (U.undomemory != 0) {
-		/* limit to maximum memory (afterwards, we can't know in advance) */
-		totmem = 0;
-		maxmem = ((uintptr_t)U.undomemory) * 1024 * 1024;
-
-		/* keep at least two (original + other) */
-		uel = undobase.last;
-		while (uel && uel->prev) {
-			totmem += uel->undosize;
-			if (totmem > maxmem) break;
-			uel = uel->prev;
-		}
-
-		if (uel) {
-			if (uel->prev && uel->prev->prev)
-				uel = uel->prev;
+		BLI_make_file_string("/", filename, BKE_tempdir_session(), numstr);
 
-			while (undobase.first != uel) {
-				UndoElem *first = undobase.first;
-				BLI_remlink(&undobase, first);
-				/* the merge is because of compression */
-				BLO_memfile_merge(&first->memfile, &first->next->memfile);
-				MEM_freeN(first);
-			}
-		}
-	}
-}
-
-/* 1 = an undo, -1 is a redo. we have to make sure 'curundo' remains at current situation */
-void BKE_undo_step(bContext *C, int step)
-{
+		/* success = */ /* UNUSED */ BLO_write_file(bmain, filename, fileflags, NULL, NULL);
 
-	if (step == 0) {
-		read_undosave(C, curundo);
-	}
-	else if (step == 1) {
-		/* curundo should never be NULL, after restart or load file it should call undo_save */
-		if (curundo == NULL || curundo->prev == NULL) {
-			// XXX error("No undo available");
-		}
-		else {
-			if (G.debug & G_DEBUG) printf("undo %s\n", curundo->name);
-			curundo = curundo->prev;
-			read_undosave(C, curundo);
-		}
+		BLI_strncpy(mfu->filename, filename, sizeof(mfu->filename));
 	}
 	else {
-		/* curundo has to remain current situation! */
-
-		if (curundo == NULL || curundo->next == NULL) {
-			// XXX error("No redo available");
-		}
-		else {
-			read_undosave(C, curundo->next);
-			curundo = curundo->next;
-			if (G.debug & G_DEBUG) printf("redo %s\n", curundo->name);
-		}
-	}
-}
-
-void BKE_undo_reset(void)
-{
-	UndoElem *uel;
-
-	uel = undobase.first;
-	while (uel) {
-		BLO_memfile_free(&uel->memfile);
-		uel = uel->next;
+		MemFile *prevfile = (mfu_prev) ? &(mfu_prev->memfile) : NULL;
+		/* success = */ /* UNUSED */ BLO_write_file_mem(bmain, prevfile, &mfu->memfile, G.fileflags);
+		mfu->undo_size = mfu->memfile.size;
 	}
 
-	BLI_freelistN(&undobase);
-	curundo = NULL;
-}
-
-/* based on index nr it does a restore */
-void BKE_undo_number(bContext *C, int nr)
-{
-	curundo = BLI_findlink(&undobase, nr);
-	BKE_undo_step(C, 0);
-}
-
-/* go back to the last occurance of name in stack */
-void BKE_undo_name(bContext *C, const char *name)
-{
-	UndoElem *uel = BLI_rfindstring(&undobase, name, offsetof(UndoElem, name));
-
-	if (uel && uel->prev) {
-		curundo = uel->prev;
-		BKE_undo_step(C, 0);
-	}
-}
-
-/* name optional */
-bool BKE_undo_is_valid(const char *name)
-{
-	if (name) {
-		UndoElem *uel = BLI_rfindstring(&undobase, name, offsetof(UndoElem, name));
-		return uel && uel->prev;
-	}
+	bmain->is_memfile_undo_written = true;
 
-	return undobase.last != undobase.first;
+	return mfu;
 }
 
-/* get name of undo item, return null if no item with this index */
-/* if active pointer, set it to 1 if true */
-const char *BKE_undo_get_name(int nr, bool *r_active)
-{
-	UndoElem *uel = BLI_findlink(&undobase, nr);
-
-	if (r_active) *r_active = false;
-
-	if (uel) {
-		if (r_active && (uel == curundo)) {
-			*r_active = true;
-		}
-		return uel->name;
-	}
-	return NULL;
-}
-
-/* return the name of the last item */
-const char *BKE_undo_get_name_last(void)
-{
-	UndoElem *uel = undobase.last;
-	return (uel ? uel->name : NULL);
-}
-
-/**
- * Saves .blend using undo buffer.
- *
- * \return success.
- */
-bool BKE_undo_save_file(const char *filename)
+void BKE_memfile_undo_free(MemFileUndoData *mfu)
 {
-	UndoElem *uel;
-	MemFileChunk *chunk;
-	int file, oflags;
-
-	if ((U.uiflag & USER_GLOBALUNDO) == 0) {
-		return false;
-	}
-
-	uel = curundo;
-	if (uel == NULL) {
-		fprintf(stderr, "No undo buffer to save recovery file\n");
-		return false;
-	}
-
-	/* note: This is currently used for autosave and 'quit.blend', where _not_ following symlinks is OK,
-	 * however if this is ever executed explicitly by the user, we may want to allow writing to symlinks.
-	 */
-
-	oflags = O_BINARY | O_WRONLY | O_CREAT | O_TRUNC;
-#ifdef O_NOFOLLOW
-	/* use O_NOFOLLOW to avoid writing to a symlink - use 'O_EXCL' (CVE-2008-1103) */
-	oflags |= O_NOFOLLOW;
-#else
-	/* TODO(sergey): How to deal with symlinks on windows? */
-#  ifndef _MSC_VER
-#    warning "Symbolic links will be followed on undo save, possibly causing CVE-2008-1103"
-#  endif
-#endif
-	file = BLI_open(filename,  oflags, 0666);
-
-	if (file == -1) {
-		fprintf(stderr, "Unable to save '%s': %s\n",
-		        filename, errno ? strerror(errno) : "Unknown error opening file");
-		return false;
-	}
-
-	for (chunk = uel->memfile.chunks.first; chunk; chunk = chunk->next) {
-		if (write(file, chunk->buf, chunk->size) != chunk->size) {
-			break;
-		}
-	}
-
-	close(file);
-
-	if (chunk) {
-		fprintf(stderr, "Unable to save '%s': %s\n",
-		        filename, errno ? strerror(errno) : "Unknown error writing file");
-		return false;
-	}
-	return true;
-}
-
-/* sets curscene */
-Main *BKE_undo_get_main(Scene **r_scene)
-{
-	Main *mainp = NULL;
-	BlendFileData *bfd = BLO_read_from_memfile(G.main, G.main->name, &curundo->memfile, NULL, BLO_READ_SKIP_NONE);
-
-	if (bfd) {
-		mainp = bfd->main;
-		if (r_scene) {
-			*r_scene = bfd->curscene;
-		}
-
-		MEM_freeN(bfd);
-	}
-
-	return mainp;
+	BLO_memfile_free(&mfu->memfile);
+	MEM_freeN(mfu);
 }
 
 /** \} */
diff --git a/source/blender/blenkernel/intern/cachefile.c b/source/blender/blenkernel/intern/cachefile.c
index f8215668034be3add7946591ef25392448465c71..8f156e8f267413b8d0d4036a61dc03e8e5191c6a 100644
--- a/source/blender/blenkernel/intern/cachefile.c
+++ b/source/blender/blenkernel/intern/cachefile.c
@@ -42,7 +42,6 @@
 
 #include "BKE_animsys.h"
 #include "BKE_cachefile.h"
-#include "BKE_global.h"
 #include "BKE_library.h"
 #include "BKE_main.h"
 #include "BKE_modifier.h"
@@ -181,7 +180,6 @@ void BKE_cachefile_update_frame(Main *bmain, Scene *scene, const float ctime, co
 			ABC_free_handle(cache_file->handle);
 			cache_file->handle = ABC_create_handle(filename, NULL);
 #endif
-			break;
 		}
 	}
 }
diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c
index 869e312614e726c285b1066d45a939bf31610624..8c4bced1563c7a6454f54e9fe87cee8a2fba3d81 100644
--- a/source/blender/blenkernel/intern/camera.c
+++ b/source/blender/blenkernel/intern/camera.c
@@ -48,7 +48,6 @@
 #include "BKE_animsys.h"
 #include "BKE_camera.h"
 #include "BKE_object.h"
-#include "BKE_global.h"
 #include "BKE_layer.h"
 #include "BKE_library.h"
 #include "BKE_library_query.h"
@@ -61,8 +60,6 @@
 
 #include "MEM_guardedalloc.h"
 
-#include "GPU_compositing.h"
-
 /****************************** Camera Datablock *****************************/
 
 void BKE_camera_init(Camera *cam)
@@ -79,8 +76,6 @@ void BKE_camera_init(Camera *cam)
 	cam->flag |= CAM_SHOWPASSEPARTOUT;
 	cam->passepartalpha = 0.5f;
 
-	GPU_fx_compositor_init_dof_settings(&cam->gpu_dof);
-
 	/* stereoscopy 3d */
 	cam->stereo.interocular_distance = 0.065f;
 	cam->stereo.convergence_distance = 30.f * 0.065f;
diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c
index fc860817a83b55856f35a162f757393a01261a41..a6c6d36076935d8d6335280124604eb0d8cb49aa 100644
--- a/source/blender/blenkernel/intern/cdderivedmesh.c
+++ b/source/blender/blenkernel/intern/cdderivedmesh.c
@@ -64,8 +64,6 @@
 #include "GPU_shader.h"
 #include "GPU_basic_shader.h"
 
-#include "DEG_depsgraph.h"
-
 #include <string.h>
 #include <limits.h>
 #include <math.h>
@@ -262,8 +260,7 @@ static bool can_pbvh_draw(Object *ob, DerivedMesh *dm)
 	return cddm->mvert == me->mvert || ob->sculpt->kb;
 }
 
-static PBVH *cdDM_getPBVH(
-        Object *ob, DerivedMesh *dm, eObjectMode UNUSED(object_mode))
+static PBVH *cdDM_getPBVH(Object *ob, DerivedMesh *dm)
 {
 	CDDerivedMesh *cddm = (CDDerivedMesh *) dm;
 
diff --git a/source/blender/blenkernel/intern/cloth.c b/source/blender/blenkernel/intern/cloth.c
index 22fd0bbfa9c9aac5ae4aaf5cc76f4bb2f322202d..339dcc4a62e2074007834c908850c9c93323acd1 100644
--- a/source/blender/blenkernel/intern/cloth.c
+++ b/source/blender/blenkernel/intern/cloth.c
@@ -306,14 +306,14 @@ void bvhselftree_update_from_cloth(ClothModifierData *clmd, bool moving)
 	}
 }
 
-void cloth_clear_cache(const EvaluationContext *eval_ctx, Object *ob, ClothModifierData *clmd, float framenr)
+void cloth_clear_cache(Object *ob, ClothModifierData *clmd, float framenr)
 {
 	PTCacheID pid;
 	
 	BKE_ptcache_id_from_cloth(&pid, ob, clmd);
 
 	// don't do anything as long as we're in editmode!
-	if (pid.cache->edit && eval_ctx->object_mode & OB_MODE_PARTICLE_EDIT)
+	if (pid.cache->edit && ob->mode & OB_MODE_PARTICLE_EDIT)
 		return;
 	
 	BKE_ptcache_id_clear(&pid, PTCACHE_CLEAR_AFTER, framenr);
@@ -480,8 +480,6 @@ void clothModifier_do(ClothModifierData *clmd, const struct EvaluationContext *e
 		return;
 	}
 
-		return;
-
 	/* if on second frame, write cache for first frame */
 	if (cache->simframe == startframe && (cache->flag & PTCACHE_OUTDATED || cache->last_exact==0))
 		BKE_ptcache_write(&pid, startframe);
@@ -641,7 +639,7 @@ void cloth_free_modifier_extern(ClothModifierData *clmd )
  **/
 static void cloth_to_object (Object *ob,  ClothModifierData *clmd, float (*vertexCos)[3])
 {
-	unsigned int	i = 0;
+	unsigned int i = 0;
 	Cloth *cloth = clmd->clothObject;
 
 	if (clmd->clothObject) {
diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c
index fb27249402b7c3dc9b0405a02ac9f0c518c913d9..15fda8a9786e4f8a74f0773d1aab789dc1919cc8 100644
--- a/source/blender/blenkernel/intern/collection.c
+++ b/source/blender/blenkernel/intern/collection.c
@@ -67,6 +67,28 @@ static SceneCollection *collection_master_from_id(const ID *owner_id)
 	}
 }
 
+/**
+ * The automatic/fallback name of a new collection.
+ */
+void BKE_collection_new_name_get(ID *owner_id, SceneCollection *sc_parent, char *rname)
+{
+	SceneCollection *sc_master = collection_master_from_id(owner_id);
+	char *name;
+
+	if (sc_parent == sc_master) {
+		name = BLI_sprintfN("Collection %d", BLI_listbase_count(&sc_master->scene_collections) + 1);
+	}
+	else {
+		const int number = BLI_listbase_count(&sc_parent->scene_collections) + 1;
+		const int digits = integer_digits_i(number);
+		const int max_len = sizeof(sc_parent->name) - 1 /* NULL terminator */ - (1 + digits) /* " %d" */;
+		name = BLI_sprintfN("%.*s %d", max_len, sc_parent->name, number);
+	}
+
+	BLI_strncpy(rname, name, MAX_NAME);
+	MEM_freeN(name);
+}
+
 /**
  * Add a new collection, but don't handle syncing with layer collections
  */
@@ -75,31 +97,22 @@ static SceneCollection *collection_add(ID *owner_id, SceneCollection *sc_parent,
 	SceneCollection *sc_master = collection_master_from_id(owner_id);
 	SceneCollection *sc = MEM_callocN(sizeof(SceneCollection), "New Collection");
 	sc->type = type;
-	const char *name = name_custom;
+	char name[MAX_NAME];
 
 	if (!sc_parent) {
 		sc_parent = sc_master;
 	}
 
-	if (!name) {
-		if (sc_parent == sc_master) {
-			name = BLI_sprintfN("Collection %d", BLI_listbase_count(&sc_master->scene_collections) + 1);
-		}
-		else {
-			const int number = BLI_listbase_count(&sc_parent->scene_collections) + 1;
-			const int digits = integer_digits_i(number);
-			const int max_len = sizeof(sc_parent->name) - 1 /* NULL terminator */ - (1 + digits) /* " %d" */;
-			name = BLI_sprintfN("%.*s %d", max_len, sc_parent->name, number);
-		}
+	if (name_custom != NULL) {
+		BLI_strncpy(name, name_custom, MAX_NAME);
+	}
+	else {
+		BKE_collection_new_name_get(owner_id, sc_parent, name);
 	}
 
 	BLI_addtail(&sc_parent->scene_collections, sc);
 	BKE_collection_rename(owner_id, sc, name);
 
-	if (name != name_custom) {
-		MEM_freeN((char *)name);
-	}
-
 	return sc;
 }
 
@@ -390,7 +403,7 @@ static void collection_object_add(const ID *owner_id, SceneCollection *sc, Objec
  */
 bool BKE_collection_object_add(const ID *owner_id, SceneCollection *sc, Object *ob)
 {
-	if (BLI_findptr(&sc->objects, ob, offsetof(LinkData, data))) {
+	if (BKE_collection_object_exists(sc, ob)) {
 		/* don't add the same object twice */
 		return false;
 	}
@@ -457,20 +470,12 @@ bool BKE_collection_object_remove(Main *bmain, ID *owner_id, SceneCollection *sc
 	return true;
 }
 
-/**
- * Move object from a collection into another
- */
-void BKE_collection_object_move(ID *owner_id, SceneCollection *sc_dst, SceneCollection *sc_src, Object *ob)
-{
-	if (BKE_collection_object_add(owner_id, sc_dst, ob)) {
-		BKE_collection_object_remove(NULL, owner_id, sc_src, ob, false);
-	}
-}
-
 /**
  * Remove object from all collections of scene
+ * \param scene_collection_skip: Don't remove base from this collection.
  */
-bool BKE_collections_object_remove(Main *bmain, ID *owner_id, Object *ob, const bool free_us)
+static bool collections_object_remove_ex(Main *bmain, ID *owner_id, Object *ob, const bool free_us,
+                                         SceneCollection *scene_collection_skip)
 {
 	bool removed = false;
 	if (GS(owner_id->name) == ID_SCE) {
@@ -482,12 +487,87 @@ bool BKE_collections_object_remove(Main *bmain, ID *owner_id, Object *ob, const
 
 	FOREACH_SCENE_COLLECTION_BEGIN(owner_id, sc)
 	{
-		removed |= BKE_collection_object_remove(bmain, owner_id, sc, ob, free_us);
+		if (sc != scene_collection_skip) {
+			removed |= BKE_collection_object_remove(bmain, owner_id, sc, ob, free_us);
+		}
 	}
 	FOREACH_SCENE_COLLECTION_END;
 	return removed;
 }
 
+/**
+ * Remove object from all collections of scene
+ */
+bool BKE_collections_object_remove(Main *bmain, ID *owner_id, Object *ob, const bool free_us)
+{
+	return collections_object_remove_ex(bmain, owner_id, ob, free_us, NULL);
+}
+
+/**
+ * Move object from a collection into another
+ *
+ * If source collection is NULL move it from all the existing collections.
+ */
+void BKE_collection_object_move(ID *owner_id, SceneCollection *sc_dst, SceneCollection *sc_src, Object *ob)
+{
+	/* In both cases we first add the object, then remove it from the other collections.
+	 * Otherwise we lose the original base and whether it was active and selected. */
+	if (sc_src != NULL) {
+		if (BKE_collection_object_add(owner_id, sc_dst, ob)) {
+			BKE_collection_object_remove(NULL, owner_id, sc_src, ob, false);
+		}
+	}
+	else {
+		/* Adding will fail if object is already in collection.
+		 * However we still need to remove it from the other collections. */
+		BKE_collection_object_add(owner_id, sc_dst, ob);
+		collections_object_remove_ex(NULL, owner_id, ob, false, sc_dst);
+	}
+}
+
+/**
+ * Whether the object is directly inside the collection.
+ */
+bool BKE_collection_object_exists(struct SceneCollection *scene_collection, struct Object *ob)
+{
+	if (BLI_findptr(&scene_collection->objects, ob, offsetof(LinkData, data))) {
+		return true;
+	}
+	return false;
+}
+
+static SceneCollection *scene_collection_from_index_recursive(SceneCollection *scene_collection, const int index, int *index_current)
+{
+	if (index == (*index_current)) {
+		return scene_collection;
+	}
+
+	(*index_current)++;
+
+	for (SceneCollection *scene_collection_iter = scene_collection->scene_collections.first;
+	     scene_collection_iter != NULL;
+	     scene_collection_iter = scene_collection_iter->next)
+	{
+		SceneCollection *nested = scene_collection_from_index_recursive(scene_collection_iter, index, index_current);
+		if (nested != NULL) {
+			return nested;
+		}
+	}
+	return NULL;
+}
+
+/**
+ * Return Scene Collection for a given index.
+ *
+ * The index is calculated from top to bottom counting the children before the siblings.
+ */
+SceneCollection *BKE_collection_from_index(Scene *scene, const int index)
+{
+	int index_current = 0;
+	SceneCollection *master_collection = BKE_collection_master(&scene->id);
+	return scene_collection_from_index_recursive(master_collection, index, &index_current);
+}
+
 static void layer_collection_sync(LayerCollection *lc_dst, LayerCollection *lc_src)
 {
 	lc_dst->flag = lc_src->flag;
@@ -506,6 +586,33 @@ static void layer_collection_sync(LayerCollection *lc_dst, LayerCollection *lc_s
 	}
 }
 
+/**
+ * Select all the objects in this SceneCollection (and its nested collections) for this ViewLayer.
+ * Return true if any object was selected.
+ */
+bool BKE_collection_objects_select(ViewLayer *view_layer, SceneCollection *scene_collection)
+{
+	LayerCollection *layer_collection = BKE_layer_collection_first_from_scene_collection(view_layer, scene_collection);
+	if (layer_collection != NULL) {
+		BKE_layer_collection_objects_select(layer_collection);
+		return true;
+	}
+	else {
+		/* Slower approach, we need to iterate over all the objects and for each one we see if there is a base. */
+		bool changed = false;
+		for (LinkData *link = scene_collection->objects.first; link; link = link->next) {
+			Base *base = BKE_view_layer_base_find(view_layer, link->data);
+			if (base != NULL) {
+				if (((base->flag & BASE_SELECTED) == 0) && ((base->flag & BASE_SELECTABLED) != 0)) {
+					base->flag |= BASE_SELECTED;
+					changed = true;
+				}
+			}
+		}
+		return changed;
+	}
+}
+
 /**
  * Leave only the master collection in, remove everything else.
  * @param group
diff --git a/source/blender/blenkernel/intern/collision.c b/source/blender/blenkernel/intern/collision.c
index c1872c92418eca37d424f62b8c2c9e09e1b841d9..ee3c38b9282c769f9be0d9cabf09c071af1da566 100644
--- a/source/blender/blenkernel/intern/collision.c
+++ b/source/blender/blenkernel/intern/collision.c
@@ -180,8 +180,8 @@ static void collision_compute_barycentric ( float pv[3], float p1[3], float p2[3
 	/* dot_v3v3 */
 #define INPR(v1, v2) ( (v1)[0] * (v2)[0] + (v1)[1] * (v2)[1] + (v1)[2] * (v2)[2])
 
-	double	tempV1[3], tempV2[3], tempV4[3];
-	double	a, b, c, d, e, f;
+	double tempV1[3], tempV2[3], tempV4[3];
+	double a, b, c, d, e, f;
 
 	VECSUB ( tempV1, p1, p3 );
 	VECSUB ( tempV2, p2, p3 );
diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c
index 2af3b6ce593ef11db1ee17129f9541890da6cf24..98e5bb8ce6e2b1148f260384007ddbbee05498a0 100644
--- a/source/blender/blenkernel/intern/context.c
+++ b/source/blender/blenkernel/intern/context.c
@@ -1035,10 +1035,9 @@ int CTX_data_mode_enum_ex(const Object *obedit, const Object *ob, const eObjectM
 
 int CTX_data_mode_enum(const bContext *C)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Object *obedit = CTX_data_edit_object(C);
 	Object *obact = obedit ? NULL : CTX_data_active_object(C);
-	return CTX_data_mode_enum_ex(obedit, obact, workspace->object_mode);
+	return CTX_data_mode_enum_ex(obedit, obact, obact ? obact->mode : OB_MODE_OBJECT);
 }
 
 /* would prefer if we can use the enum version below over this one - Campbell */
@@ -1275,9 +1274,8 @@ void CTX_data_eval_ctx(const bContext *C, EvaluationContext *eval_ctx)
 	Scene *scene = CTX_data_scene(C);
 	ViewLayer *view_layer = CTX_data_view_layer(C);
 	RenderEngineType *engine_type = CTX_data_engine_type(C);
-	WorkSpace *workspace = CTX_wm_workspace(C);
 	DEG_evaluation_context_init_from_scene(
 	        eval_ctx,
 	        scene, view_layer, engine_type,
-	        workspace->object_mode, DAG_EVAL_VIEWPORT);
+	        DAG_EVAL_VIEWPORT);
 }
diff --git a/source/blender/blenkernel/intern/displist.c b/source/blender/blenkernel/intern/displist.c
index 15a520fe8ceee61973d107a0a56e971f8b23f113..ffa4e6520564c83593d3dc10c66d4a990a3f8f1a 100644
--- a/source/blender/blenkernel/intern/displist.c
+++ b/source/blender/blenkernel/intern/displist.c
@@ -299,7 +299,10 @@ bool BKE_displist_surfindex_get(DispList *dl, int a, int *b, int *p1, int *p2, i
 }
 
 /* ****************** make displists ********************* */
-
+#ifdef __INTEL_COMPILER
+/* ICC with the optimization -02 causes crashes. */
+#   pragma intel optimization_level 1
+#endif
 static void curve_to_displist(Curve *cu, ListBase *nubase, ListBase *dispbase,
                               const bool for_render, const bool use_render_resolution)
 {
diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c
index 22a1f9b4b18eff6c5944b6fc28d805155bd8af28..ee678dd4c416b5456ddd987ae09d976c84fc302a 100644
--- a/source/blender/blenkernel/intern/dynamicpaint.c
+++ b/source/blender/blenkernel/intern/dynamicpaint.c
@@ -54,7 +54,7 @@
 
 #include "BKE_animsys.h"
 #include "BKE_armature.h"
-#include "BKE_bvhutils.h"   /* bvh tree	*/
+#include "BKE_bvhutils.h"   /* bvh tree */
 #include "BKE_colorband.h"
 #include "BKE_cdderivedmesh.h"
 #include "BKE_constraint.h"
@@ -4516,7 +4516,7 @@ static int dynamicPaint_paintParticles(DynamicPaintSurface *surface,
 	 */
 	tree = BLI_kdtree_new(psys->totpart);
 
-	/* loop through particles and insert valid ones	to the tree	*/
+	/* loop through particles and insert valid ones to the tree */
 	p = 0;
 	for (ParticleData *pa = psys->particles; p < psys->totpart; p++, pa++) {
 		/* Proceed only if particle is active	*/
diff --git a/source/blender/blenkernel/intern/fluidsim.c b/source/blender/blenkernel/intern/fluidsim.c
index a4ed7a9d63f343270608aed768e7053823f8fcca..d0d3317b4776031a7eb7a2f4f6f072e06346b3b6 100644
--- a/source/blender/blenkernel/intern/fluidsim.c
+++ b/source/blender/blenkernel/intern/fluidsim.c
@@ -54,7 +54,6 @@
 #include "BKE_customdata.h"
 #include "BKE_DerivedMesh.h"
 #include "BKE_fluidsim.h"
-#include "BKE_global.h"
 #include "BKE_modifier.h"
 #include "BKE_mesh.h"
 
diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c
index d38987c942fbac5a0fdf51012356e3808f8c8a40..28245f9a4d8b1bfc3adee8558e327ca0782e2693 100644
--- a/source/blender/blenkernel/intern/image.c
+++ b/source/blender/blenkernel/intern/image.c
@@ -46,6 +46,7 @@
 #include "IMB_imbuf_types.h"
 #include "IMB_imbuf.h"
 #include "IMB_moviecache.h"
+#include "IMB_metadata.h"
 
 #ifdef WITH_OPENEXR
 #  include "intern/openexr/openexr_multi.h"
@@ -152,7 +153,7 @@ static void imagecache_put(Image *image, int index, ImBuf *ibuf)
 
 	if (image->cache == NULL) {
 		// char cache_name[64];
-		// BLI_snprintf(cache_name, sizeof(cache_name), "Image Datablock %s", image->id.name);
+		// SNPRINTF(cache_name, "Image Datablock %s", image->id.name);
 
 		image->cache = IMB_moviecache_create("Image Datablock Cache", sizeof(ImageCacheKey),
 		                                     imagecache_hashhash, imagecache_hashcmp);
@@ -437,7 +438,7 @@ static void copy_image_packedfiles(ListBase *lb_dst, const ListBase *lb_src)
 	BLI_listbase_clear(lb_dst);
 	for (imapf_src = lb_src->first; imapf_src; imapf_src = imapf_src->next) {
 		ImagePackedFile *imapf_dst = MEM_mallocN(sizeof(ImagePackedFile), "Image Packed Files (copy)");
-		BLI_strncpy(imapf_dst->filepath, imapf_src->filepath, sizeof(imapf_dst->filepath));
+		STRNCPY(imapf_dst->filepath, imapf_src->filepath);
 
 		if (imapf_src->packedfile)
 			imapf_dst->packedfile = dupPackedFile(imapf_src->packedfile);
@@ -593,7 +594,7 @@ Image *BKE_image_load(Main *bmain, const char *filepath)
 	int file;
 	char str[FILE_MAX];
 
-	BLI_strncpy(str, filepath, sizeof(str));
+	STRNCPY(str, filepath);
 	BLI_path_abs(str, bmain->name);
 
 	/* exists? */
@@ -603,7 +604,7 @@ Image *BKE_image_load(Main *bmain, const char *filepath)
 	close(file);
 
 	ima = image_alloc(bmain, BLI_path_basename(filepath), IMA_SRC_FILE, IMA_TYPE_IMAGE);
-	BLI_strncpy(ima->name, filepath, sizeof(ima->name));
+	STRNCPY(ima->name, filepath);
 
 	if (BLI_testextensie_array(filepath, imb_ext_movie))
 		ima->source = IMA_SRC_MOVIE;
@@ -622,13 +623,13 @@ Image *BKE_image_load_exists_ex(const char *filepath, bool *r_exists)
 	Image *ima;
 	char str[FILE_MAX], strtest[FILE_MAX];
 
-	BLI_strncpy(str, filepath, sizeof(str));
+	STRNCPY(str, filepath);
 	BLI_path_abs(str, G.main->name);
 
 	/* first search an identical filepath */
 	for (ima = G.main->image.first; ima; ima = ima->id.next) {
 		if (ima->source != IMA_SRC_VIEWER && ima->source != IMA_SRC_GENERATED) {
-			BLI_strncpy(strtest, ima->name, sizeof(ima->name));
+			STRNCPY(strtest, ima->name);
 			BLI_path_abs(strtest, ID_BLEND_PATH(G.main, &ima->id));
 
 			if (BLI_path_cmp(strtest, str) == 0) {
@@ -669,7 +670,7 @@ static ImBuf *add_ibuf_size(unsigned int width, unsigned int height, const char
 		if (colorspace_settings->name[0] == '\0') {
 			const char *colorspace = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DEFAULT_FLOAT);
 
-			BLI_strncpy(colorspace_settings->name, colorspace, sizeof(colorspace_settings->name));
+			STRNCPY(colorspace_settings->name, colorspace);
 		}
 
 		if (ibuf != NULL) {
@@ -683,7 +684,7 @@ static ImBuf *add_ibuf_size(unsigned int width, unsigned int height, const char
 		if (colorspace_settings->name[0] == '\0') {
 			const char *colorspace = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DEFAULT_BYTE);
 
-			BLI_strncpy(colorspace_settings->name, colorspace, sizeof(colorspace_settings->name));
+			STRNCPY(colorspace_settings->name, colorspace);
 		}
 
 		if (ibuf != NULL) {
@@ -696,7 +697,7 @@ static ImBuf *add_ibuf_size(unsigned int width, unsigned int height, const char
 		return NULL;
 	}
 
-	BLI_strncpy(ibuf->name, name, sizeof(ibuf->name));
+	STRNCPY(ibuf->name, name);
 	ibuf->userflags |= IB_BITMAPDIRTY;
 
 	switch (gen_type) {
@@ -726,7 +727,7 @@ Image *BKE_image_add_generated(
 		int view_id;
 		const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME};
 
-		/* BLI_strncpy(ima->name, name, FILE_MAX); */ /* don't do this, this writes in ain invalid filepath! */
+		/* STRNCPY(ima->name, name); */ /* don't do this, this writes in ain invalid filepath! */
 		ima->gen_x = width;
 		ima->gen_y = height;
 		ima->gen_type = gen_type;
@@ -767,7 +768,7 @@ Image *BKE_image_add_from_imbuf(ImBuf *ibuf, const char *name)
 	ima = image_alloc(G.main, name, IMA_SRC_FILE, IMA_TYPE_IMAGE);
 
 	if (ima) {
-		BLI_strncpy(ima->name, ibuf->name, FILE_MAX);
+		STRNCPY(ima->name, ibuf->name);
 		image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0);
 		ima->ok = IMA_OK_LOADED;
 	}
@@ -813,7 +814,7 @@ static void image_memorypack_multiview(Image *ima)
 			pf->size = ibuf->encodedsize;
 
 			imapf = MEM_mallocN(sizeof(ImagePackedFile), "Image PackedFile");
-			BLI_strncpy(imapf->filepath, iv->filepath, sizeof(imapf->filepath));
+			STRNCPY(imapf->filepath, iv->filepath);
 			imapf->packedfile = pf;
 			BLI_addtail(&ima->packedfiles, imapf);
 
@@ -863,7 +864,7 @@ void BKE_image_memorypack(Image *ima)
 		pf->size = ibuf->encodedsize;
 
 		imapf = MEM_mallocN(sizeof(ImagePackedFile), "Image PackedFile");
-		BLI_strncpy(imapf->filepath, ima->name, sizeof(imapf->filepath));
+		STRNCPY(imapf->filepath, ima->name);
 		imapf->packedfile = pf;
 		BLI_addtail(&ima->packedfiles, imapf);
 
@@ -889,7 +890,7 @@ void BKE_image_packfiles(ReportList *reports, Image *ima, const char *basepath)
 		BLI_addtail(&ima->packedfiles, imapf);
 		imapf->packedfile = newPackedFile(reports, ima->name, basepath);
 		if (imapf->packedfile) {
-			BLI_strncpy(imapf->filepath, ima->name, sizeof(imapf->filepath));
+			STRNCPY(imapf->filepath, ima->name);
 		}
 		else {
 			BLI_freelinkN(&ima->packedfiles, imapf);
@@ -903,7 +904,7 @@ void BKE_image_packfiles(ReportList *reports, Image *ima, const char *basepath)
 
 			imapf->packedfile = newPackedFile(reports, iv->filepath, basepath);
 			if (imapf->packedfile) {
-				BLI_strncpy(imapf->filepath, iv->filepath, sizeof(imapf->filepath));
+				STRNCPY(imapf->filepath, iv->filepath);
 			}
 			else {
 				BLI_freelinkN(&ima->packedfiles, imapf);
@@ -923,7 +924,7 @@ void BKE_image_packfiles_from_mem(ReportList *reports, Image *ima, char *data, c
 		ImagePackedFile *imapf = MEM_mallocN(sizeof(ImagePackedFile), __func__);
 		BLI_addtail(&ima->packedfiles, imapf);
 		imapf->packedfile = newPackedFileMemory(data, data_len);
-		BLI_strncpy(imapf->filepath, ima->name, sizeof(imapf->filepath));
+		STRNCPY(imapf->filepath, ima->name);
 	}
 }
 
@@ -1623,6 +1624,7 @@ typedef struct StampData {
 	char marker[512];
 	char time[512];
 	char frame[512];
+	char frame_range[512];
 	char camera[STAMP_NAME_SIZE];
 	char cameralens[STAMP_NAME_SIZE];
 	char scene[STAMP_NAME_SIZE];
@@ -1639,14 +1641,19 @@ typedef struct StampData {
 } StampData;
 #undef STAMP_NAME_SIZE
 
-static void stampdata(Scene *scene, Object *camera, StampData *stamp_data, int do_prefix)
+/**
+ * \param do_prefix: Include a label like "File ", "Date ", etc. in the stamp data strings.
+ * \param use_dynamic: Also include data that can change on a per-frame basis.
+ */
+static void stampdata(Scene *scene, Object *camera, StampData *stamp_data, int do_prefix,
+                      bool use_dynamic)
 {
 	char text[256];
 	struct tm *tl;
 	time_t t;
 
 	if (scene->r.stamp & R_STAMP_FILENAME) {
-		BLI_snprintf(stamp_data->file, sizeof(stamp_data->file), do_prefix ? "File %s" : "%s", G.relbase_valid ? G.main->name : "<untitled>");
+		SNPRINTF(stamp_data->file, do_prefix ? "File %s" : "%s", G.relbase_valid ? G.main->name : "<untitled>");
 	}
 	else {
 		stamp_data->file[0] = '\0';
@@ -1654,7 +1661,7 @@ static void stampdata(Scene *scene, Object *camera, StampData *stamp_data, int d
 
 	if (scene->r.stamp & R_STAMP_NOTE) {
 		/* Never do prefix for Note */
-		BLI_snprintf(stamp_data->note, sizeof(stamp_data->note), "%s", scene->r.stamp_udata);
+		SNPRINTF(stamp_data->note, "%s", scene->r.stamp_udata);
 	}
 	else {
 		stamp_data->note[0] = '\0';
@@ -1663,83 +1670,93 @@ static void stampdata(Scene *scene, Object *camera, StampData *stamp_data, int d
 	if (scene->r.stamp & R_STAMP_DATE) {
 		t = time(NULL);
 		tl = localtime(&t);
-		BLI_snprintf(text, sizeof(text), "%04d/%02d/%02d %02d:%02d:%02d", tl->tm_year + 1900, tl->tm_mon + 1, tl->tm_mday, tl->tm_hour, tl->tm_min, tl->tm_sec);
-		BLI_snprintf(stamp_data->date, sizeof(stamp_data->date), do_prefix ? "Date %s" : "%s", text);
+		SNPRINTF(text, "%04d/%02d/%02d %02d:%02d:%02d",
+		         tl->tm_year + 1900, tl->tm_mon + 1, tl->tm_mday, tl->tm_hour, tl->tm_min, tl->tm_sec);
+		SNPRINTF(stamp_data->date, do_prefix ? "Date %s" : "%s", text);
 	}
 	else {
 		stamp_data->date[0] = '\0';
 	}
 
-	if (scene->r.stamp & R_STAMP_MARKER) {
+	if (use_dynamic && scene->r.stamp & R_STAMP_MARKER) {
 		const char *name = BKE_scene_find_last_marker_name(scene, CFRA);
 
-		if (name) BLI_strncpy(text, name, sizeof(text));
-		else BLI_strncpy(text, "<none>", sizeof(text));
+		if (name) STRNCPY(text, name);
+		else STRNCPY(text, "<none>");
 
-		BLI_snprintf(stamp_data->marker, sizeof(stamp_data->marker), do_prefix ? "Marker %s" : "%s", text);
+		SNPRINTF(stamp_data->marker, do_prefix ? "Marker %s" : "%s", text);
 	}
 	else {
 		stamp_data->marker[0] = '\0';
 	}
 
-	if (scene->r.stamp & R_STAMP_TIME) {
+	if (use_dynamic && scene->r.stamp & R_STAMP_TIME) {
 		const short timecode_style = USER_TIMECODE_SMPTE_FULL;
 		BLI_timecode_string_from_time(text, sizeof(text), 0, FRA2TIME(scene->r.cfra), FPS, timecode_style);
-		BLI_snprintf(stamp_data->time, sizeof(stamp_data->time), do_prefix ? "Timecode %s" : "%s", text);
+		SNPRINTF(stamp_data->time, do_prefix ? "Timecode %s" : "%s", text);
 	}
 	else {
 		stamp_data->time[0] = '\0';
 	}
 
-	if (scene->r.stamp & R_STAMP_FRAME) {
+	if (use_dynamic && scene->r.stamp & R_STAMP_FRAME) {
 		char fmtstr[32];
 		int digits = 1;
 
 		if (scene->r.efra > 9)
 			digits = integer_digits_i(scene->r.efra);
 
-		BLI_snprintf(fmtstr, sizeof(fmtstr), do_prefix ? "Frame %%0%di" : "%%0%di", digits);
-		BLI_snprintf(stamp_data->frame, sizeof(stamp_data->frame), fmtstr, scene->r.cfra);
+		SNPRINTF(fmtstr, do_prefix ? "Frame %%0%di" : "%%0%di", digits);
+		SNPRINTF(stamp_data->frame, fmtstr, scene->r.cfra);
 	}
 	else {
 		stamp_data->frame[0] = '\0';
 	}
 
-	if (scene->r.stamp & R_STAMP_CAMERA) {
-		BLI_snprintf(stamp_data->camera, sizeof(stamp_data->camera), do_prefix ? "Camera %s" : "%s", camera ? camera->id.name + 2 : "<none>");
+	if (scene->r.stamp & R_STAMP_FRAME_RANGE) {
+		SNPRINTF(stamp_data->frame_range,
+		         do_prefix ? "Frame Range %d:%d" : "%d:%d",
+		         scene->r.sfra, scene->r.efra);
+	}
+	else {
+		stamp_data->frame_range[0] = '\0';
+	}
+
+	if (use_dynamic && scene->r.stamp & R_STAMP_CAMERA) {
+		SNPRINTF(stamp_data->camera, do_prefix ? "Camera %s" : "%s", camera ? camera->id.name + 2 : "<none>");
 	}
 	else {
 		stamp_data->camera[0] = '\0';
 	}
 
-	if (scene->r.stamp & R_STAMP_CAMERALENS) {
+	if (use_dynamic && scene->r.stamp & R_STAMP_CAMERALENS) {
 		if (camera && camera->type == OB_CAMERA) {
-			BLI_snprintf(text, sizeof(text), "%.2f", ((Camera *)camera->data)->lens);
+			SNPRINTF(text, "%.2f", ((Camera *)camera->data)->lens);
 		}
 		else {
-			BLI_strncpy(text, "<none>", sizeof(text));
+			STRNCPY(text, "<none>");
 		}
 
-		BLI_snprintf(stamp_data->cameralens, sizeof(stamp_data->cameralens), do_prefix ? "Lens %s" : "%s", text);
+		SNPRINTF(stamp_data->cameralens, do_prefix ? "Lens %s" : "%s", text);
 	}
 	else {
 		stamp_data->cameralens[0] = '\0';
 	}
 
 	if (scene->r.stamp & R_STAMP_SCENE) {
-		BLI_snprintf(stamp_data->scene, sizeof(stamp_data->scene), do_prefix ? "Scene %s" : "%s", scene->id.name + 2);
+		SNPRINTF(stamp_data->scene, do_prefix ? "Scene %s" : "%s", scene->id.name + 2);
 	}
 	else {
 		stamp_data->scene[0] = '\0';
 	}
 
-	if (scene->r.stamp & R_STAMP_SEQSTRIP) {
+	if (use_dynamic && scene->r.stamp & R_STAMP_SEQSTRIP) {
 		Sequence *seq = BKE_sequencer_foreground_frame_get(scene, scene->r.cfra);
 
-		if (seq) BLI_strncpy(text, seq->name + 2, sizeof(text));
-		else BLI_strncpy(text, "<none>", sizeof(text));
+		if (seq) STRNCPY(text, seq->name + 2);
+		else STRNCPY(text, "<none>");
 
-		BLI_snprintf(stamp_data->strip, sizeof(stamp_data->strip), do_prefix ? "Strip %s" : "%s", text);
+		SNPRINTF(stamp_data->strip, do_prefix ? "Strip %s" : "%s", text);
 	}
 	else {
 		stamp_data->strip[0] = '\0';
@@ -1749,22 +1766,29 @@ static void stampdata(Scene *scene, Object *camera, StampData *stamp_data, int d
 		Render *re = RE_GetSceneRender(scene);
 		RenderStats *stats = re ? RE_GetStats(re) : NULL;
 
-		if (stats && (scene->r.stamp & R_STAMP_RENDERTIME)) {
+		if (use_dynamic && stats && (scene->r.stamp & R_STAMP_RENDERTIME)) {
 			BLI_timecode_string_from_time_simple(text, sizeof(text), stats->lastframetime);
 
-			BLI_snprintf(stamp_data->rendertime, sizeof(stamp_data->rendertime), do_prefix ? "RenderTime %s" : "%s", text);
+			SNPRINTF(stamp_data->rendertime, do_prefix ? "RenderTime %s" : "%s", text);
 		}
 		else {
 			stamp_data->rendertime[0] = '\0';
 		}
 
-		if (stats && (scene->r.stamp & R_STAMP_MEMORY)) {
-			BLI_snprintf(stamp_data->memory, sizeof(stamp_data->memory), do_prefix ? "Peak Memory %.2fM" : "%.2fM", stats->mem_peak);
+		if (use_dynamic && stats && (scene->r.stamp & R_STAMP_MEMORY)) {
+			SNPRINTF(stamp_data->memory, do_prefix ? "Peak Memory %.2fM" : "%.2fM", stats->mem_peak);
 		}
 		else {
 			stamp_data->memory[0] = '\0';
 		}
 	}
+	if (scene->r.stamp & R_STAMP_FRAME_RANGE) {
+		SNPRINTF(stamp_data->frame_range,
+		             do_prefix ? "Frame Range %d:%d" : "%d:%d", scene->r.sfra, scene->r.efra);
+	}
+	else {
+		stamp_data->frame_range[0] = '\0';
+	}
 }
 
 /* Will always add prefix. */
@@ -1773,73 +1797,73 @@ static void stampdata_from_template(StampData *stamp_data,
                                     const StampData *stamp_data_template)
 {
 	if (scene->r.stamp & R_STAMP_FILENAME) {
-		BLI_snprintf(stamp_data->file, sizeof(stamp_data->file), "File %s", stamp_data_template->file);
+		SNPRINTF(stamp_data->file, "File %s", stamp_data_template->file);
 	}
 	else {
 		stamp_data->file[0] = '\0';
 	}
 	if (scene->r.stamp & R_STAMP_NOTE) {
-		BLI_snprintf(stamp_data->note, sizeof(stamp_data->note), "%s", stamp_data_template->note);
+		SNPRINTF(stamp_data->note, "%s", stamp_data_template->note);
 	}
 	else {
 		stamp_data->note[0] = '\0';
 	}
 	if (scene->r.stamp & R_STAMP_DATE) {
-		BLI_snprintf(stamp_data->date, sizeof(stamp_data->date), "Date %s", stamp_data_template->date);
+		SNPRINTF(stamp_data->date, "Date %s", stamp_data_template->date);
 	}
 	else {
 		stamp_data->date[0] = '\0';
 	}
 	if (scene->r.stamp & R_STAMP_MARKER) {
-		BLI_snprintf(stamp_data->marker, sizeof(stamp_data->marker), "Marker %s", stamp_data_template->marker);
+		SNPRINTF(stamp_data->marker, "Marker %s", stamp_data_template->marker);
 	}
 	else {
 		stamp_data->marker[0] = '\0';
 	}
 	if (scene->r.stamp & R_STAMP_TIME) {
-		BLI_snprintf(stamp_data->time, sizeof(stamp_data->time), "Timecode %s", stamp_data_template->time);
+		SNPRINTF(stamp_data->time, "Timecode %s", stamp_data_template->time);
 	}
 	else {
 		stamp_data->time[0] = '\0';
 	}
 	if (scene->r.stamp & R_STAMP_FRAME) {
-		BLI_snprintf(stamp_data->frame, sizeof(stamp_data->frame), "Frame %s", stamp_data_template->frame);
+		SNPRINTF(stamp_data->frame, "Frame %s", stamp_data_template->frame);
 	}
 	else {
 		stamp_data->frame[0] = '\0';
 	}
 	if (scene->r.stamp & R_STAMP_CAMERA) {
-		BLI_snprintf(stamp_data->camera, sizeof(stamp_data->camera), "Camera %s", stamp_data_template->camera);
+		SNPRINTF(stamp_data->camera, "Camera %s", stamp_data_template->camera);
 	}
 	else {
 		stamp_data->camera[0] = '\0';
 	}
 	if (scene->r.stamp & R_STAMP_CAMERALENS) {
-		BLI_snprintf(stamp_data->cameralens, sizeof(stamp_data->cameralens), "Lens %s", stamp_data_template->cameralens);
+		SNPRINTF(stamp_data->cameralens, "Lens %s", stamp_data_template->cameralens);
 	}
 	else {
 		stamp_data->cameralens[0] = '\0';
 	}
 	if (scene->r.stamp & R_STAMP_SCENE) {
-		BLI_snprintf(stamp_data->scene, sizeof(stamp_data->scene), "Scene %s", stamp_data_template->scene);
+		SNPRINTF(stamp_data->scene, "Scene %s", stamp_data_template->scene);
 	}
 	else {
 		stamp_data->scene[0] = '\0';
 	}
 	if (scene->r.stamp & R_STAMP_SEQSTRIP) {
-		BLI_snprintf(stamp_data->strip, sizeof(stamp_data->strip), "Strip %s", stamp_data_template->strip);
+		SNPRINTF(stamp_data->strip, "Strip %s", stamp_data_template->strip);
 	}
 	else {
 		stamp_data->strip[0] = '\0';
 	}
 	if (scene->r.stamp & R_STAMP_RENDERTIME) {
-		BLI_snprintf(stamp_data->rendertime, sizeof(stamp_data->rendertime), "RenderTime %s", stamp_data_template->rendertime);
+		SNPRINTF(stamp_data->rendertime, "RenderTime %s", stamp_data_template->rendertime);
 	}
 	else {
 		stamp_data->rendertime[0] = '\0';
 	}
 	if (scene->r.stamp & R_STAMP_MEMORY) {
-		BLI_snprintf(stamp_data->memory, sizeof(stamp_data->memory), "Peak Memory %s", stamp_data_template->memory);
+		SNPRINTF(stamp_data->memory, "Peak Memory %s", stamp_data_template->memory);
 	}
 	else {
 		stamp_data->memory[0] = '\0';
@@ -1885,7 +1909,7 @@ void BKE_image_stamp_buf(
 	display = IMB_colormanagement_display_get_named(display_device);
 
 	if (stamp_data_template == NULL) {
-		stampdata(scene, camera, &stamp_data, (scene->r.stamp & R_STAMP_HIDE_LABELS) == 0);
+		stampdata(scene, camera, &stamp_data, (scene->r.stamp & R_STAMP_HIDE_LABELS) == 0, true);
 	}
 	else {
 		stampdata_from_template(&stamp_data, scene, stamp_data_template);
@@ -2106,13 +2130,28 @@ void BKE_render_result_stamp_info(Scene *scene, Object *camera, struct RenderRes
 	}
 
 	if (!allocate_only)
-		stampdata(scene, camera, stamp_data, 0);
+		stampdata(scene, camera, stamp_data, 0, true);
 
 	if (!rr->stamp_data) {
 		rr->stamp_data = stamp_data;
 	}
 }
 
+struct StampData *BKE_stamp_info_from_scene_static(Scene *scene)
+{
+	struct StampData *stamp_data;
+
+	if (!(scene && (scene->r.stamp & R_STAMP_ALL)))
+		return NULL;
+
+	/* Memory is allocated here (instead of by the caller) so that the caller
+	 * doesn't have to know the size of the StampData struct. */
+	stamp_data = MEM_callocN(sizeof(StampData), __func__);
+	stampdata(scene, NULL, stamp_data, 0, false);
+
+	return stamp_data;
+}
+
 void BKE_stamp_info_callback(void *data, struct StampData *stamp_data, StampCallback callback, bool noskip)
 {
 	if ((callback == NULL) || (stamp_data == NULL)) {
@@ -2130,6 +2169,7 @@ void BKE_stamp_info_callback(void *data, struct StampData *stamp_data, StampCall
 	CALL(marker, "Marker");
 	CALL(time, "Time");
 	CALL(frame, "Frame");
+	CALL(frame_range, "FrameRange");
 	CALL(camera, "Camera");
 	CALL(cameralens, "Lens");
 	CALL(scene, "Scene");
@@ -2158,8 +2198,8 @@ void BKE_render_result_stamp_data(RenderResult *rr, const char *key, const char
 	stamp_data = rr->stamp_data;
 	StampDataCustomField *field = MEM_mallocN(sizeof(StampDataCustomField),
 	                                          "StampData Custom Field");
-	BLI_strncpy(field->key, key, sizeof(field->key));
-	BLI_strncpy(field->value, value, sizeof(field->value));
+	STRNCPY(field->key, key);
+	STRNCPY(field->value, value);
 	BLI_addtail(&stamp_data->custom_fields, field);
 }
 
@@ -2173,27 +2213,31 @@ void BKE_stamp_data_free(struct StampData *stamp_data)
 }
 
 /* wrap for callback only */
-static void metadata_change_field(void *data, const char *propname, char *propvalue, int UNUSED(len))
+static void metadata_set_field(void *data, const char *propname, char *propvalue, int UNUSED(len))
 {
-	IMB_metadata_change_field(data, propname, propvalue);
+	/* We know it is an ImBuf* because that's what we pass to BKE_stamp_info_callback. */
+	struct ImBuf *imbuf = data;
+	IMB_metadata_set_field(imbuf->metadata, propname, propvalue);
 }
 
 static void metadata_get_field(void *data, const char *propname, char *propvalue, int len)
 {
-	IMB_metadata_get_field(data, propname, propvalue, len);
+	/* We know it is an ImBuf* because that's what we pass to BKE_stamp_info_callback. */
+	struct ImBuf *imbuf = data;
+	IMB_metadata_get_field(imbuf->metadata, propname, propvalue, len);
 }
 
 void BKE_imbuf_stamp_info(RenderResult *rr, struct ImBuf *ibuf)
 {
 	struct StampData *stamp_data = rr->stamp_data;
-
-	BKE_stamp_info_callback(ibuf, stamp_data, metadata_change_field, false);
+	IMB_metadata_ensure(&ibuf->metadata);
+	BKE_stamp_info_callback(ibuf, stamp_data, metadata_set_field, false);
 }
 
 void BKE_stamp_info_from_imbuf(RenderResult *rr, struct ImBuf *ibuf)
 {
 	struct StampData *stamp_data = rr->stamp_data;
-
+	IMB_metadata_ensure(&ibuf->metadata);
 	BKE_stamp_info_callback(ibuf, stamp_data, metadata_get_field, true);
 }
 
@@ -2740,7 +2784,7 @@ void BKE_image_signal(Image *ima, ImageUser *iuser, int signal)
 			if (BKE_image_has_packedfile(ima)) {
 				const int totfiles = image_num_files(ima);
 
-				if (totfiles != BLI_listbase_count_ex(&ima->packedfiles, totfiles + 1)) {
+				if (totfiles != BLI_listbase_count_at_most(&ima->packedfiles, totfiles + 1)) {
 					/* in case there are new available files to be loaded */
 					image_free_packedfiles(ima);
 					BKE_image_packfiles(NULL, ima, ID_BLEND_PATH(G.main, &ima->id));
@@ -2895,7 +2939,7 @@ void BKE_image_multiview_index(Image *ima, ImageUser *iuser)
 			iuser->multi_index = iuser->multiview_eye;
 		}
 		else {
-			if ((iuser->view < 0) || (iuser->view >= BLI_listbase_count_ex(&ima->views, iuser->view + 1))) {
+			if ((iuser->view < 0) || (iuser->view >= BLI_listbase_count_at_most(&ima->views, iuser->view + 1))) {
 				iuser->multi_index = iuser->view = 0;
 			}
 			else {
@@ -2958,7 +3002,7 @@ static void image_init_multilayer_multiview(Image *ima, RenderResult *rr)
 	if (rr) {
 		for (RenderView *rv = rr->views.first; rv; rv = rv->next) {
 			ImageView *iv = MEM_callocN(sizeof(ImageView), "Viewer Image View");
-			BLI_strncpy(iv->name, rv->name, sizeof(iv->name));
+			STRNCPY(iv->name, rv->name);
 			BLI_addtail(&ima->views, iv);
 		}
 	}
@@ -3039,8 +3083,8 @@ static void image_add_view(Image *ima, const char *viewname, const char *filepat
 	ImageView *iv;
 
 	iv = MEM_mallocN(sizeof(ImageView), "Viewer Image View");
-	BLI_strncpy(iv->name, viewname, sizeof(iv->name));
-	BLI_strncpy(iv->filepath, filepath, sizeof(iv->filepath));
+	STRNCPY(iv->name, viewname);
+	STRNCPY(iv->filepath, filepath);
 
 	/* For stereo drawing we need to ensure:
 	 * STEREO_LEFT_NAME  == STEREO_LEFT_ID and
@@ -3362,7 +3406,7 @@ static ImBuf *image_load_movie_file(Image *ima, ImageUser *iuser, int frame)
 	const int totfiles = image_num_files(ima);
 	int i;
 
-	if (totfiles != BLI_listbase_count_ex(&ima->anims, totfiles + 1)) {
+	if (totfiles != BLI_listbase_count_at_most(&ima->anims, totfiles + 1)) {
 		image_free_anims(ima);
 
 		for (i = 0; i < totfiles; i++) {
@@ -3490,7 +3534,7 @@ static ImBuf *load_image_single(
 				ImagePackedFile *imapf = MEM_mallocN(sizeof(ImagePackedFile), "Image Packefile");
 				BLI_addtail(&ima->packedfiles, imapf);
 
-				BLI_strncpy(imapf->filepath, filepath, sizeof(imapf->filepath));
+				STRNCPY(imapf->filepath, filepath);
 				imapf->packedfile = newPackedFile(NULL, filepath, ID_BLEND_PATH(G.main, &ima->id));
 			}
 		}
@@ -3518,7 +3562,7 @@ static ImBuf *image_load_image_file(Image *ima, ImageUser *iuser, int cfra)
 
 	/* this should never happen, but just playing safe */
 	if (has_packed) {
-		if (totfiles != BLI_listbase_count_ex(&ima->packedfiles, totfiles + 1)) {
+		if (totfiles != BLI_listbase_count_at_most(&ima->packedfiles, totfiles + 1)) {
 			image_free_packedfiles(ima);
 			has_packed = false;
 		}
@@ -4645,7 +4689,7 @@ static void image_update_views_format(Image *ima, ImageUser *iuser)
 		for (srv = scene->r.views.first; srv; srv = srv->next) {
 			if (BKE_scene_multiview_is_render_view_active(&scene->r, srv)) {
 				char filepath[FILE_MAX];
-				BLI_snprintf(filepath, sizeof(filepath), "%s%s%s", prefix, srv->suffix, ext);
+				SNPRINTF(filepath, "%s%s%s", prefix, srv->suffix, ext);
 				image_add_view(ima, srv->name, filepath);
 			}
 		}
@@ -4656,7 +4700,7 @@ static void image_update_views_format(Image *ima, ImageUser *iuser)
 			int file;
 			char str[FILE_MAX];
 
-			BLI_strncpy(str, iv->filepath, sizeof(str));
+			STRNCPY(str, iv->filepath);
 			BLI_path_abs(str, G.main->name);
 
 			/* exists? */
diff --git a/source/blender/blenkernel/intern/lamp.c b/source/blender/blenkernel/intern/lamp.c
index 931fc09d2350813120a5222c36859bb4cad0a7a7..bf36c437d6045d9a5b2d676e86fd1b22d389b23b 100644
--- a/source/blender/blenkernel/intern/lamp.c
+++ b/source/blender/blenkernel/intern/lamp.c
@@ -47,7 +47,6 @@
 #include "BKE_animsys.h"
 #include "BKE_colortools.h"
 #include "BKE_icons.h"
-#include "BKE_global.h"
 #include "BKE_lamp.h"
 #include "BKE_library.h"
 #include "BKE_library_query.h"
diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c
index d92d0d9edbf3299ff596f9601515f23282652be6..c41ad78977ede1601c645d3139725601397be459 100644
--- a/source/blender/blenkernel/intern/lattice.c
+++ b/source/blender/blenkernel/intern/lattice.c
@@ -54,7 +54,6 @@
 #include "BKE_cdderivedmesh.h"
 #include "BKE_curve.h"
 #include "BKE_displist.h"
-#include "BKE_global.h"
 #include "BKE_key.h"
 #include "BKE_lattice.h"
 #include "BKE_library.h"
diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c
index 78af18fa3622c1f1a72bc65746704a2308e61edb..65e76e619f2f76cbc360ec988756793c6de21dcb 100644
--- a/source/blender/blenkernel/intern/layer.c
+++ b/source/blender/blenkernel/intern/layer.c
@@ -1282,16 +1282,29 @@ static LayerCollection *layer_collection_add(ViewLayer *view_layer, LayerCollect
 /* ---------------------------------------------------------------------- */
 
 /**
- * See if render layer has the scene collection linked directly, or indirectly (nested)
+ * Return the first matching LayerCollection in the ViewLayer for the SceneCollection.
  */
-bool BKE_view_layer_has_collection(ViewLayer *view_layer, const SceneCollection *sc)
+LayerCollection *BKE_layer_collection_first_from_scene_collection(ViewLayer *view_layer, const SceneCollection *scene_collection)
 {
-	for (LayerCollection *lc = view_layer->layer_collections.first; lc; lc = lc->next) {
-		if (find_layer_collection_by_scene_collection(lc, sc) != NULL) {
-			return true;
+	for (LayerCollection *layer_collection = view_layer->layer_collections.first;
+	     layer_collection != NULL;
+	     layer_collection = layer_collection->next)
+	{
+		LayerCollection *found = find_layer_collection_by_scene_collection(layer_collection, scene_collection);
+
+		if (found != NULL) {
+			return found;
 		}
 	}
-	return false;
+	return NULL;
+}
+
+/**
+ * See if view layer has the scene collection linked directly, or indirectly (nested)
+ */
+bool BKE_view_layer_has_collection(ViewLayer *view_layer, const SceneCollection *scene_collection)
+{
+	return BKE_layer_collection_first_from_scene_collection(view_layer, scene_collection) != NULL;
 }
 
 /**
diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c
index e0608efece21037b5125c5221160a299d549ba23..6ec2d223e8496611b70d7832bd83e4483ba57b22 100644
--- a/source/blender/blenkernel/intern/library.c
+++ b/source/blender/blenkernel/intern/library.c
@@ -840,6 +840,7 @@ void BKE_libblock_management_main_add(Main *bmain, void *idv)
 	new_id(lb, id, NULL);
 	/* alphabetic insertion: is in new_id */
 	id->tag &= ~(LIB_TAG_NO_MAIN | LIB_TAG_NO_USER_REFCOUNT);
+	bmain->is_memfile_undo_written = false;
 	BKE_main_unlock(bmain);
 }
 
@@ -859,6 +860,7 @@ void BKE_libblock_management_main_remove(Main *bmain, void *idv)
 	BKE_main_lock(bmain);
 	BLI_remlink(lb, id);
 	id->tag |= LIB_TAG_NO_MAIN;
+	bmain->is_memfile_undo_written = false;
 	BKE_main_unlock(bmain);
 }
 
@@ -1229,6 +1231,7 @@ void *BKE_libblock_alloc(Main *bmain, short type, const char *name, const int fl
 			BKE_main_lock(bmain);
 			BLI_addtail(lb, id);
 			new_id(lb, id, name);
+			bmain->is_memfile_undo_written = false;
 			/* alphabetic insertion: is in new_id */
 			BKE_main_unlock(bmain);
 
diff --git a/source/blender/blenkernel/intern/library_query.c b/source/blender/blenkernel/intern/library_query.c
index 27a2916ef922b65db54a0ea67cceb8020c5220ea..bed2244702f00ca3fe53389af6b25c47ada6bc10 100644
--- a/source/blender/blenkernel/intern/library_query.c
+++ b/source/blender/blenkernel/intern/library_query.c
@@ -673,6 +673,9 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call
 				}
 				CALLBACK_INVOKE(material->group, IDWALK_CB_USER);
 				CALLBACK_INVOKE(material->edit_image, IDWALK_CB_USER);
+				if (material->texpaintslot != NULL) {
+					CALLBACK_INVOKE(material->texpaintslot->ima, IDWALK_CB_NOP);
+				}
 				break;
 			}
 
diff --git a/source/blender/blenkernel/intern/linestyle.c b/source/blender/blenkernel/intern/linestyle.c
index 2fc4c81cb0beadb2c7c7b893da739a28c3bfb634..dd1315fe3fa75364585d813ec989309fb9d641f4 100644
--- a/source/blender/blenkernel/intern/linestyle.c
+++ b/source/blender/blenkernel/intern/linestyle.c
@@ -47,7 +47,6 @@
 #include "BKE_colorband.h"
 #include "BKE_context.h"
 #include "BKE_freestyle.h"
-#include "BKE_global.h"
 #include "BKE_library.h"
 #include "BKE_linestyle.h"
 #include "BKE_node.h"
diff --git a/source/blender/blenkernel/intern/mask.c b/source/blender/blenkernel/intern/mask.c
index 3256c16e2f658254cabe0fc87678ea6d99ff9137..ba5a6a2504824efbfa5c70aa07bb55a31e091b4a 100644
--- a/source/blender/blenkernel/intern/mask.c
+++ b/source/blender/blenkernel/intern/mask.c
@@ -51,7 +51,7 @@
 
 #include "BKE_animsys.h"
 #include "BKE_curve.h"
-#include "BKE_global.h"
+
 #include "BKE_library.h"
 #include "BKE_main.h"
 #include "BKE_mask.h"
diff --git a/source/blender/blenkernel/intern/mask_evaluate.c b/source/blender/blenkernel/intern/mask_evaluate.c
index 7d977463abf0b3fb8357485205f3a8b8ec75acb1..61c136d2c4f2a0c82fa32e51b5a0419f2d18d7b5 100644
--- a/source/blender/blenkernel/intern/mask_evaluate.c
+++ b/source/blender/blenkernel/intern/mask_evaluate.c
@@ -42,7 +42,6 @@
 #include "DNA_mask_types.h"
 
 #include "BKE_curve.h"
-#include "BKE_global.h"
 #include "BKE_mask.h"
 
 #include "DEG_depsgraph.h"
diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c
index a0059a99473f246b80f60f23423dbc487d1d08e8..2971f56c775a01093c7473889e3970c32766aed6 100644
--- a/source/blender/blenkernel/intern/material.c
+++ b/source/blender/blenkernel/intern/material.c
@@ -261,6 +261,10 @@ void BKE_material_copy_data(Main *bmain, Material *ma_dst, const Material *ma_sr
 		ma_dst->preview = NULL;
 	}
 
+	if (ma_src->texpaintslot != NULL) {
+		ma_dst->texpaintslot = MEM_dupallocN(ma_src->texpaintslot);
+	}
+
 	BLI_listbase_clear(&ma_dst->gpumaterial);
 
 	/* TODO Duplicate Engine Settings and set runtime to NULL */
diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c
index abbf073f6463d3be3085e7e3476bcb55000c6553..8c88430aa100bbf989e7b0e895b8999ef004b835 100644
--- a/source/blender/blenkernel/intern/mesh.c
+++ b/source/blender/blenkernel/intern/mesh.c
@@ -2469,7 +2469,7 @@ Mesh *BKE_mesh_new_from_object(
 
 			/* if getting the original caged mesh, delete object modifiers */
 			if (cage)
-				BKE_object_free_modifiers(tmpobj);
+				BKE_object_free_modifiers(tmpobj, 0);
 
 			/* copies the data */
 			copycu = tmpobj->data = BKE_curve_copy(bmain, (Curve *) ob->data);
diff --git a/source/blender/blenkernel/intern/mesh_validate.c b/source/blender/blenkernel/intern/mesh_validate.c
index f7509d07ca44f3e2709fe1f84f4aa7c265eb7b21..8301b6a42b1c3f3004dcdf4d5b3d094628eafd31 100644
--- a/source/blender/blenkernel/intern/mesh_validate.c
+++ b/source/blender/blenkernel/intern/mesh_validate.c
@@ -63,8 +63,8 @@ typedef union {
 } EdgeUUID;
 
 typedef struct SortFace {
-	EdgeUUID		es[4];
-	unsigned int	index;
+	EdgeUUID     es[4];
+	unsigned int index;
 } SortFace;
 
 /* Used to detect polys (faces) using exactly the same vertices. */
@@ -127,28 +127,28 @@ static int search_face_cmp(const void *v1, const void *v2)
 	if (sfa->es[0].edval > sfb->es[0].edval) {
 		return 1;
 	}
-	else if	(sfa->es[0].edval < sfb->es[0].edval) {
+	else if (sfa->es[0].edval < sfb->es[0].edval) {
 		return -1;
 	}
 
-	else if	(sfa->es[1].edval > sfb->es[1].edval) {
+	else if (sfa->es[1].edval > sfb->es[1].edval) {
 		return 1;
 	}
-	else if	(sfa->es[1].edval < sfb->es[1].edval) {
+	else if (sfa->es[1].edval < sfb->es[1].edval) {
 		return -1;
 	}
 
-	else if	(sfa->es[2].edval > sfb->es[2].edval) {
+	else if (sfa->es[2].edval > sfb->es[2].edval) {
 		return 1;
 	}
-	else if	(sfa->es[2].edval < sfb->es[2].edval) {
+	else if (sfa->es[2].edval < sfb->es[2].edval) {
 		return -1;
 	}
 
-	else if	(sfa->es[3].edval > sfb->es[3].edval) {
+	else if (sfa->es[3].edval > sfb->es[3].edval) {
 		return 1;
 	}
-	else if	(sfa->es[3].edval < sfb->es[3].edval) {
+	else if (sfa->es[3].edval < sfb->es[3].edval) {
 		return -1;
 	}
 
diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c
index 1604a0170541a35c470cdf8f88b187b499641001..5eb5272f3e5b8450c753bea1aebecc6d4ad22c9a 100644
--- a/source/blender/blenkernel/intern/modifier.c
+++ b/source/blender/blenkernel/intern/modifier.c
@@ -138,16 +138,38 @@ ModifierData *modifier_new(int type)
 	return md;
 }
 
-void modifier_free(ModifierData *md) 
+static void modifier_free_data_id_us_cb(void *UNUSED(userData), Object *UNUSED(ob), ID **idpoin, int cb_flag)
+{
+	ID *id = *idpoin;
+	if (id != NULL && (cb_flag & IDWALK_CB_USER) != 0) {
+		id_us_min(id);
+	}
+}
+
+void modifier_free_ex(ModifierData *md, const int flag)
 {
 	const ModifierTypeInfo *mti = modifierType_getInfo(md->type);
 
+	if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
+		if (mti->foreachIDLink) {
+			mti->foreachIDLink(md, NULL, modifier_free_data_id_us_cb, NULL);
+		}
+		else if (mti->foreachObjectLink) {
+			mti->foreachObjectLink(md, NULL, (ObjectWalkFunc)modifier_free_data_id_us_cb, NULL);
+		}
+	}
+
 	if (mti->freeData) mti->freeData(md);
 	if (md->error) MEM_freeN(md->error);
 
 	MEM_freeN(md);
 }
 
+void modifier_free(ModifierData *md)
+{
+	modifier_free_ex(md, 0);
+}
+
 bool modifier_unique_name(ListBase *modifiers, ModifierData *md)
 {
 	if (modifiers && md) {
@@ -662,13 +684,13 @@ bool modifier_isCorrectableDeformed(ModifierData *md)
 	return (mti->deformMatricesEM != NULL);
 }
 
-bool modifiers_isCorrectableDeformed(const EvaluationContext *eval_ctx, struct Scene *scene, Object *ob)
+bool modifiers_isCorrectableDeformed(struct Scene *scene, Object *ob)
 {
 	VirtualModifierData virtualModifierData;
 	ModifierData *md = modifiers_getVirtualModifierList(ob, &virtualModifierData);
 	int required_mode = eModifierMode_Realtime;
 
-	if (eval_ctx->object_mode == OB_MODE_EDIT) {
+	if (ob->mode == OB_MODE_EDIT) {
 		required_mode |= eModifierMode_Editmode;
 	}
 	for (; md; md = md->next) {
diff --git a/source/blender/blenkernel/intern/multires.c b/source/blender/blenkernel/intern/multires.c
index e7c36685c42a2add5da8f299fc72abc4b171e11d..55f11604710413bc4bdb86cdec419a68ba7686cf 100644
--- a/source/blender/blenkernel/intern/multires.c
+++ b/source/blender/blenkernel/intern/multires.c
@@ -60,8 +60,6 @@
 
 #include "BKE_object.h"
 
-#include "DEG_depsgraph.h"
-
 #include "CCGSubSurf.h"
 
 #include <math.h>
@@ -338,13 +336,12 @@ MultiresModifierData *get_multires_modifier(Scene *scene, Object *ob, bool use_f
 	return mmd;
 }
 
-static int multires_get_level(
-        MultiresModifierData *mmd,
-        bool render, bool ignore_simplify, eObjectMode object_mode)
+static int multires_get_level(Object *ob, MultiresModifierData *mmd,
+                              bool render, bool ignore_simplify)
 {
 	if (render)
 		return (mmd->modifier.scene) ? get_render_subsurf_level(&mmd->modifier.scene->r, mmd->renderlvl, true) : mmd->renderlvl;
-	else if (object_mode == OB_MODE_SCULPT)
+	else if (ob->mode == OB_MODE_SCULPT)
 		return mmd->sculptlvl;
 	else if (ignore_simplify)
 		return mmd->lvl;
@@ -352,13 +349,12 @@ static int multires_get_level(
 		return (mmd->modifier.scene) ? get_render_subsurf_level(&mmd->modifier.scene->r, mmd->lvl, false) : mmd->lvl;
 }
 
-void multires_set_tot_level(MultiresModifierData *mmd, int lvl, eObjectMode object_mode)
+void multires_set_tot_level(Object *ob, MultiresModifierData *mmd, int lvl)
 {
 	mmd->totlvl = lvl;
 
-	if (object_mode != OB_MODE_SCULPT) {
+	if (ob->mode != OB_MODE_SCULPT)
 		mmd->lvl = CLAMPIS(MAX2(mmd->lvl, lvl), 0, mmd->totlvl);
-	}
 
 	mmd->sculptlvl = CLAMPIS(MAX2(mmd->sculptlvl, lvl), 0, mmd->totlvl);
 	mmd->renderlvl = CLAMPIS(MAX2(mmd->renderlvl, lvl), 0, mmd->totlvl);
@@ -396,9 +392,9 @@ void multires_force_external_reload(Object *ob)
 	multires_force_update(ob);
 }
 
-void multires_force_render_update(Object *ob, eObjectMode object_mode)
+void multires_force_render_update(Object *ob)
 {
-	if (ob && (object_mode & OB_MODE_SCULPT) && modifiers_findByType(ob, eModifierType_Multires))
+	if (ob && (ob->mode & OB_MODE_SCULPT) && modifiers_findByType(ob, eModifierType_Multires))
 		multires_force_update(ob);
 }
 
@@ -438,7 +434,7 @@ int multiresModifier_reshapeFromDeformMod(const struct EvaluationContext *eval_c
 	int numVerts, result;
 	float (*deformedVerts)[3];
 
-	if (multires_get_level(mmd, false, true, eval_ctx->object_mode) == 0)
+	if (multires_get_level(ob, mmd, false, true) == 0)
 		return 0;
 
 	/* Create DerivedMesh for deformation modifier */
@@ -618,7 +614,7 @@ static void multires_grid_paint_mask_downsample(GridPaintMask *gpm, int level)
 	}
 }
 
-static void multires_del_higher(MultiresModifierData *mmd, Object *ob, int lvl, eObjectMode object_mode)
+static void multires_del_higher(MultiresModifierData *mmd, Object *ob, int lvl)
 {
 	Mesh *me = (Mesh *)ob->data;
 	int levels = mmd->totlvl - lvl;
@@ -680,14 +676,14 @@ static void multires_del_higher(MultiresModifierData *mmd, Object *ob, int lvl,
 		}
 	}
 
-	multires_set_tot_level(mmd, lvl, object_mode);
+	multires_set_tot_level(ob, mmd, lvl);
 }
 
 /* (direction = 1) for delete higher, (direction = 0) for lower (not implemented yet) */
-void multiresModifier_del_levels(MultiresModifierData *mmd, Object *ob, int direction, eObjectMode object_mode)
+void multiresModifier_del_levels(MultiresModifierData *mmd, Object *ob, int direction)
 {
 	Mesh *me = BKE_mesh_from_object(ob);
-	int lvl = multires_get_level(mmd, false, true, object_mode);
+	int lvl = multires_get_level(ob, mmd, false, true);
 	int levels = mmd->totlvl - lvl;
 	MDisps *mdisps;
 
@@ -698,14 +694,13 @@ void multiresModifier_del_levels(MultiresModifierData *mmd, Object *ob, int dire
 	multires_force_update(ob);
 
 	if (mdisps && levels > 0 && direction == 1) {
-		multires_del_higher(mmd, ob, lvl, object_mode);
+		multires_del_higher(mmd, ob, lvl);
 	}
 
-	multires_set_tot_level(mmd, lvl, object_mode);
+	multires_set_tot_level(ob, mmd, lvl);
 }
 
-static DerivedMesh *multires_dm_create_local(
-        Object *ob, DerivedMesh *dm, int lvl, int totlvl, int simple, bool alloc_paint_mask, eObjectMode object_mode)
+static DerivedMesh *multires_dm_create_local(Object *ob, DerivedMesh *dm, int lvl, int totlvl, int simple, bool alloc_paint_mask)
 {
 	MultiresModifierData mmd = {{NULL}};
 	MultiresFlags flags = MULTIRES_USE_LOCAL_MMD;
@@ -719,12 +714,10 @@ static DerivedMesh *multires_dm_create_local(
 	if (alloc_paint_mask)
 		flags |= MULTIRES_ALLOC_PAINT_MASK;
 
-	return multires_make_derived_from_derived(dm, &mmd, ob, flags, object_mode);
+	return multires_make_derived_from_derived(dm, &mmd, ob, flags);
 }
 
-static DerivedMesh *subsurf_dm_create_local(
-        DerivedMesh *dm, int lvl, int simple,
-        int optimal, int plain_uv, int alloc_paint_mask, eObjectMode object_mode)
+static DerivedMesh *subsurf_dm_create_local(Object *ob, DerivedMesh *dm, int lvl, int simple, int optimal, int plain_uv, int alloc_paint_mask)
 {
 	SubsurfModifierData smd = {{NULL}};
 	SubsurfFlags flags = 0;
@@ -737,7 +730,7 @@ static DerivedMesh *subsurf_dm_create_local(
 	if (optimal)
 		smd.flags |= eSubsurfModifierFlag_ControlEdges;
 
-	if (object_mode & OB_MODE_EDIT)
+	if (ob->mode & OB_MODE_EDIT)
 		flags |= SUBSURF_IN_EDIT_MODE;
 
 	if (alloc_paint_mask)
@@ -757,7 +750,7 @@ static float v3_dist_from_plane(float v[3], float center[3], float no[3])
 	return dot_v3v3(s, no);
 }
 
-void multiresModifier_base_apply(MultiresModifierData *mmd, Object *ob, eObjectMode object_mode)
+void multiresModifier_base_apply(MultiresModifierData *mmd, Object *ob)
 {
 	DerivedMesh *cddm, *dispdm, *origdm;
 	Mesh *me;
@@ -779,7 +772,7 @@ void multiresModifier_base_apply(MultiresModifierData *mmd, Object *ob, eObjectM
 	/* generate highest level with displacements */
 	cddm = CDDM_from_mesh(me);
 	DM_set_only_copy(cddm, CD_MASK_BAREMESH);
-	dispdm = multires_dm_create_local(ob, cddm, totlvl, totlvl, 0, 0, object_mode);
+	dispdm = multires_dm_create_local(ob, cddm, totlvl, totlvl, 0, 0);
 	cddm->release(cddm);
 
 	/* copy the new locations of the base verts into the mesh */
@@ -875,9 +868,7 @@ void multiresModifier_base_apply(MultiresModifierData *mmd, Object *ob, eObjectM
 	/* subdivide the mesh to highest level without displacements */
 	cddm = CDDM_from_mesh(me);
 	DM_set_only_copy(cddm, CD_MASK_BAREMESH);
-	origdm = subsurf_dm_create_local(
-	        cddm, totlvl, 0,
-	        0, mmd->flags & eMultiresModifierFlag_PlainUv, 0, object_mode);
+	origdm = subsurf_dm_create_local(ob, cddm, totlvl, 0, 0, mmd->flags & eMultiresModifierFlag_PlainUv, 0);
 	cddm->release(cddm);
 
 	/* calc disps */
@@ -887,8 +878,7 @@ void multiresModifier_base_apply(MultiresModifierData *mmd, Object *ob, eObjectM
 	dispdm->release(dispdm);
 }
 
-static void multires_subdivide(
-        MultiresModifierData *mmd, Object *ob, int totlvl, int updateblock, int simple, eObjectMode object_mode)
+static void multires_subdivide(MultiresModifierData *mmd, Object *ob, int totlvl, int updateblock, int simple)
 {
 	Mesh *me = ob->data;
 	MDisps *mdisps;
@@ -917,13 +907,11 @@ static void multires_subdivide(
 		/* create subsurf DM from original mesh at high level */
 		cddm = CDDM_from_mesh(me);
 		DM_set_only_copy(cddm, CD_MASK_BAREMESH);
-		highdm = subsurf_dm_create_local(
-		        cddm, totlvl, simple,
-		        0, mmd->flags & eMultiresModifierFlag_PlainUv, has_mask, object_mode);
+		highdm = subsurf_dm_create_local(ob, cddm, totlvl, simple, 0, mmd->flags & eMultiresModifierFlag_PlainUv, has_mask);
 		ss = ((CCGDerivedMesh *)highdm)->ss;
 
 		/* create multires DM from original mesh at low level */
-		lowdm = multires_dm_create_local(ob, cddm, lvl, lvl, simple, has_mask, object_mode);
+		lowdm = multires_dm_create_local(ob, cddm, lvl, lvl, simple, has_mask);
 		BLI_assert(lowdm != cddm);
 		cddm->release(cddm);
 
@@ -970,13 +958,12 @@ static void multires_subdivide(
 		multires_reallocate_mdisps(me->totloop, mdisps, totlvl); 
 	}
 
-	multires_set_tot_level(mmd, totlvl, object_mode);
+	multires_set_tot_level(ob, mmd, totlvl);
 }
 
-void multiresModifier_subdivide(
-        MultiresModifierData *mmd, Object *ob, int updateblock, int simple, eObjectMode object_mode)
+void multiresModifier_subdivide(MultiresModifierData *mmd, Object *ob, int updateblock, int simple)
 {
-	multires_subdivide(mmd, ob, mmd->totlvl + 1, updateblock, simple, object_mode);
+	multires_subdivide(mmd, ob, mmd->totlvl + 1, updateblock, simple);
 }
 
 static void grid_tangent(const CCGKey *key, int x, int y, int axis, CCGElem *grid, float t[3])
@@ -1209,7 +1196,7 @@ static void multiresModifier_disp_run(DerivedMesh *dm, Mesh *me, DerivedMesh *dm
 	}
 }
 
-void multires_modifier_update_mdisps(struct DerivedMesh *dm, eObjectMode object_mode)
+void multires_modifier_update_mdisps(struct DerivedMesh *dm)
 {
 	CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm;
 	Object *ob;
@@ -1241,13 +1228,11 @@ void multires_modifier_update_mdisps(struct DerivedMesh *dm, eObjectMode object_
 			else cddm = CDDM_from_mesh(me);
 			DM_set_only_copy(cddm, CD_MASK_BAREMESH);
 
-			highdm = subsurf_dm_create_local(
-			        cddm, totlvl, mmd->simple,
-			        0, mmd->flags & eMultiresModifierFlag_PlainUv, has_mask, object_mode);
+			highdm = subsurf_dm_create_local(ob, cddm, totlvl, mmd->simple, 0, mmd->flags & eMultiresModifierFlag_PlainUv, has_mask);
 			ss = ((CCGDerivedMesh *)highdm)->ss;
 
 			/* create multires DM from original mesh and displacements */
-			lowdm = multires_dm_create_local(ob, cddm, lvl, totlvl, mmd->simple, has_mask, object_mode);
+			lowdm = multires_dm_create_local(ob, cddm, lvl, totlvl, mmd->simple, has_mask);
 			cddm->release(cddm);
 
 			/* gather grid data */
@@ -1305,9 +1290,7 @@ void multires_modifier_update_mdisps(struct DerivedMesh *dm, eObjectMode object_
 			else cddm = CDDM_from_mesh(me);
 			DM_set_only_copy(cddm, CD_MASK_BAREMESH);
 
-			subdm = subsurf_dm_create_local(
-			        cddm, mmd->totlvl, mmd->simple,
-			        0, mmd->flags & eMultiresModifierFlag_PlainUv, has_mask, object_mode);
+			subdm = subsurf_dm_create_local(ob, cddm, mmd->totlvl, mmd->simple, 0, mmd->flags & eMultiresModifierFlag_PlainUv, has_mask);
 			cddm->release(cddm);
 
 			multiresModifier_disp_run(dm, me, NULL, CALC_DISPLACEMENTS, subdm->getGridData(subdm), mmd->totlvl);
@@ -1349,7 +1332,7 @@ void multires_modifier_update_hidden(DerivedMesh *dm)
 	}
 }
 
-void multires_set_space(DerivedMesh *dm, Object *ob, int from, int to, eObjectMode object_mode)
+void multires_set_space(DerivedMesh *dm, Object *ob, int from, int to)
 {
 	DerivedMesh *ccgdm = NULL, *subsurf = NULL;
 	CCGElem **gridData, **subGridData = NULL;
@@ -1370,11 +1353,10 @@ void multires_set_space(DerivedMesh *dm, Object *ob, int from, int to, eObjectMo
 	}
 
 	totlvl = mmd->totlvl;
-	ccgdm = multires_dm_create_local(ob, dm, totlvl, totlvl, mmd->simple, false, object_mode);
+	ccgdm = multires_dm_create_local(ob, dm, totlvl, totlvl, mmd->simple, false);
 	
-	subsurf = subsurf_dm_create_local(
-	        dm, totlvl, mmd->simple,
-	        mmd->flags & eMultiresModifierFlag_ControlEdges, mmd->flags & eMultiresModifierFlag_PlainUv, 0, object_mode);
+	subsurf = subsurf_dm_create_local(ob, dm, totlvl,
+	                                  mmd->simple, mmd->flags & eMultiresModifierFlag_ControlEdges, mmd->flags & eMultiresModifierFlag_PlainUv, 0);
 
 	numGrids = subsurf->getNumGrids(subsurf);
 	gridSize = subsurf->getGridSize(subsurf);
@@ -1489,12 +1471,10 @@ void multires_stitch_grids(Object *ob)
 	}
 }
 
-DerivedMesh *multires_make_derived_from_derived(
-        DerivedMesh *dm,
-        MultiresModifierData *mmd,
-        Object *ob,
-        MultiresFlags flags,
-        eObjectMode object_mode)
+DerivedMesh *multires_make_derived_from_derived(DerivedMesh *dm,
+                                                MultiresModifierData *mmd,
+                                                Object *ob,
+                                                MultiresFlags flags)
 {
 	Mesh *me = ob->data;
 	DerivedMesh *result;
@@ -1503,18 +1483,16 @@ DerivedMesh *multires_make_derived_from_derived(
 	CCGKey key;
 	const bool render = (flags & MULTIRES_USE_RENDER_PARAMS) != 0;
 	const bool ignore_simplify = (flags & MULTIRES_IGNORE_SIMPLIFY) != 0;
-	int lvl = multires_get_level(mmd, render, ignore_simplify, object_mode);
+	int lvl = multires_get_level(ob, mmd, render, ignore_simplify);
 	int i, gridSize, numGrids;
 
 	if (lvl == 0)
 		return dm;
 
-	result = subsurf_dm_create_local(
-	        dm, lvl, mmd->simple,
-	        mmd->flags & eMultiresModifierFlag_ControlEdges,
-	        mmd->flags & eMultiresModifierFlag_PlainUv,
-	        flags & MULTIRES_ALLOC_PAINT_MASK,
-	        object_mode);
+	result = subsurf_dm_create_local(ob, dm, lvl,
+	                                 mmd->simple, mmd->flags & eMultiresModifierFlag_ControlEdges,
+	                                 mmd->flags & eMultiresModifierFlag_PlainUv,
+	                                 flags & MULTIRES_ALLOC_PAINT_MASK);
 
 	if (!(flags & MULTIRES_USE_LOCAL_MMD)) {
 		ccgdm = (CCGDerivedMesh *)result;
@@ -2142,7 +2120,6 @@ void multires_load_old(Object *ob, Mesh *me)
 	DerivedMesh *dm, *orig;
 	CustomDataLayer *l;
 	int i;
-	const eObjectMode object_mode = OB_MODE_OBJECT;
 
 	/* Load original level into the mesh */
 	lvl = me->mr->levels.first;
@@ -2191,7 +2168,7 @@ void multires_load_old(Object *ob, Mesh *me)
 	BLI_insertlinkbefore(&ob->modifiers, md, mmd);
 
 	for (i = 0; i < me->mr->level_count - 1; ++i)
-		multiresModifier_subdivide(mmd, ob, 1, 0, object_mode);
+		multiresModifier_subdivide(mmd, ob, 1, 0);
 
 	mmd->lvl = mmd->totlvl;
 	orig = CDDM_from_mesh(me);
@@ -2200,7 +2177,7 @@ void multires_load_old(Object *ob, Mesh *me)
 	 *     reference subsurfed dm with this option, before calling multiresModifier_disp_run(),
 	 *     which implicitly expects both subsurfs from its first dm and oldGridData parameters to
 	 *     be of the same "format"! */
-	dm = multires_make_derived_from_derived(orig, mmd, ob, 0, object_mode);
+	dm = multires_make_derived_from_derived(orig, mmd, ob, 0);
 
 	multires_load_old_dm(dm, me, mmd->totlvl + 1);
 
@@ -2215,22 +2192,21 @@ void multires_load_old(Object *ob, Mesh *me)
 
 /* If 'ob_src' and 'ob_dst' both have multires modifiers, synchronize them
  * such that 'ob_dst' has the same total number of levels as 'ob_src'. */
-void multiresModifier_sync_levels_ex(
-        Object *ob_dst, MultiresModifierData *mmd_src, MultiresModifierData *mmd_dst, eObjectMode object_mode)
+void multiresModifier_sync_levels_ex(Object *ob_dst, MultiresModifierData *mmd_src, MultiresModifierData *mmd_dst)
 {
 	if (mmd_src->totlvl == mmd_dst->totlvl) {
 		return;
 	}
 
 	if (mmd_src->totlvl > mmd_dst->totlvl) {
-		multires_subdivide(mmd_dst, ob_dst, mmd_src->totlvl, false, mmd_dst->simple, object_mode);
+		multires_subdivide(mmd_dst, ob_dst, mmd_src->totlvl, false, mmd_dst->simple);
 	}
 	else {
-		multires_del_higher(mmd_dst, ob_dst, mmd_src->totlvl, object_mode);
+		multires_del_higher(mmd_dst, ob_dst, mmd_src->totlvl);
 	}
 }
 
-static void multires_sync_levels(Scene *scene, Object *ob_src, Object *ob_dst, eObjectMode object_mode)
+static void multires_sync_levels(Scene *scene, Object *ob_src, Object *ob_dst)
 {
 	MultiresModifierData *mmd_src = get_multires_modifier(scene, ob_src, true);
 	MultiresModifierData *mmd_dst = get_multires_modifier(scene, ob_dst, true);
@@ -2245,7 +2221,7 @@ static void multires_sync_levels(Scene *scene, Object *ob_src, Object *ob_dst, e
 	}
 
 	if (mmd_src && mmd_dst) {
-		multiresModifier_sync_levels_ex(ob_dst, mmd_src, mmd_dst, object_mode);
+		multiresModifier_sync_levels_ex(ob_dst, mmd_src, mmd_dst);
 	}
 }
 
@@ -2338,9 +2314,7 @@ static void multires_apply_smat(const struct EvaluationContext *eval_ctx, Scene
 	MEM_freeN(vertCos);
 
 	/* scaled ccgDM for tangent space of object with applied scale */
-	dm = subsurf_dm_create_local(
-	        cddm, high_mmd.totlvl, high_mmd.simple,
-	        0, mmd->flags & eMultiresModifierFlag_PlainUv, 0, eval_ctx->object_mode);
+	dm = subsurf_dm_create_local(ob, cddm, high_mmd.totlvl, high_mmd.simple, 0, mmd->flags & eMultiresModifierFlag_PlainUv, 0);
 	cddm->release(cddm);
 
 	gridSize = dm->getGridSize(dm);
@@ -2403,7 +2377,7 @@ void multiresModifier_scale_disp(const struct EvaluationContext *eval_ctx, Scene
 void multiresModifier_prepare_join(const struct EvaluationContext *eval_ctx, Scene *scene, Object *ob, Object *to_ob)
 {
 	float smat[3][3], tmat[3][3], mat[3][3];
-	multires_sync_levels(scene, to_ob, ob, eval_ctx->object_mode);
+	multires_sync_levels(scene, to_ob, ob);
 
 	/* construct scale matrix for displacement */
 	BKE_object_scale_to_mat3(to_ob, tmat);
diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c
index 73f47dec9cb29d1739489c143887e70f503845e7..98fcd478b0797406026c46d477019f2ce627dbc7 100644
--- a/source/blender/blenkernel/intern/object.c
+++ b/source/blender/blenkernel/intern/object.c
@@ -198,12 +198,12 @@ void BKE_object_free_curve_cache(Object *ob)
 	}
 }
 
-void BKE_object_free_modifiers(Object *ob)
+void BKE_object_free_modifiers(Object *ob, const int flag)
 {
 	ModifierData *md;
 
 	while ((md = BLI_pophead(&ob->modifiers))) {
-		modifier_free(md);
+		modifier_free_ex(md, flag);
 	}
 
 	/* particle modifiers were freed, so free the particlesystems as well */
@@ -262,12 +262,10 @@ bool BKE_object_support_modifier_type_check(Object *ob, int modifier_type)
 	return true;
 }
 
-void BKE_object_link_modifiers(
-        struct Object *ob_dst, const struct Object *ob_src,
-        eObjectMode object_mode)
+void BKE_object_link_modifiers(struct Object *ob_dst, const struct Object *ob_src)
 {
 	ModifierData *md;
-	BKE_object_free_modifiers(ob_dst);
+	BKE_object_free_modifiers(ob_dst, 0);
 
 	if (!ELEM(ob_dst->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) {
 		/* only objects listed above can have modifiers and linking them to objects
@@ -303,8 +301,7 @@ void BKE_object_link_modifiers(
 
 		if (md->type == eModifierType_Multires) {
 			/* Has to be done after mod creation, but *before* we actually copy its settings! */
-			multiresModifier_sync_levels_ex(
-			        ob_dst, (MultiresModifierData *)md, (MultiresModifierData *)nmd, object_mode);
+			multiresModifier_sync_levels_ex(ob_dst, (MultiresModifierData *)md, (MultiresModifierData *)nmd);
 		}
 
 		modifier_copyData(md, nmd);
@@ -421,7 +418,8 @@ void BKE_object_free(Object *ob)
 {
 	BKE_animdata_free((ID *)ob, false);
 
-	BKE_object_free_modifiers(ob);
+	/* BKE_<id>_free shall never touch to ID->us. Never ever. */
+	BKE_object_free_modifiers(ob, LIB_ID_CREATE_NO_USER_REFCOUNT);
 
 	MEM_SAFE_FREE(ob->mat);
 	MEM_SAFE_FREE(ob->matbits);
@@ -489,7 +487,7 @@ void BKE_object_free(Object *ob)
 }
 
 /* actual check for internal data, not context or flags */
-bool BKE_object_is_in_editmode(const Object *ob)
+bool BKE_object_is_in_editmode(Object *ob)
 {
 	if (ob->data == NULL)
 		return false;
@@ -538,11 +536,11 @@ bool BKE_object_is_in_editmode_vgroup(Object *ob)
 	        BKE_object_is_in_editmode(ob));
 }
 
-bool BKE_object_is_in_wpaint_select_vert(const Object *ob, eObjectMode object_mode)
+bool BKE_object_is_in_wpaint_select_vert(const Object *ob)
 {
 	if (ob->type == OB_MESH) {
-		const Mesh *me = ob->data;
-		return ((object_mode & OB_MODE_WEIGHT_PAINT) &&
+		Mesh *me = ob->data;
+		return ((ob->mode & OB_MODE_WEIGHT_PAINT) &&
 		        (me->edit_btmesh == NULL) &&
 		        (ME_EDIT_PAINT_SEL_MODE(me) == SCE_SELECT_VERTEX));
 	}
@@ -887,10 +885,10 @@ static LodLevel *lod_level_select(Object *ob, const float camera_position[3])
 	return current;
 }
 
-bool BKE_object_lod_is_usable(Object *ob, ViewLayer *view_layer, const eObjectMode object_mode)
+bool BKE_object_lod_is_usable(Object *ob, ViewLayer *view_layer)
 {
 	bool active = (view_layer) ? ob == OBACT(view_layer) : false;
-	return (object_mode == OB_MODE_OBJECT || !active);
+	return (ob->mode == OB_MODE_OBJECT || !active);
 }
 
 void BKE_object_lod_update(Object *ob, const float camera_position[3])
@@ -903,11 +901,11 @@ void BKE_object_lod_update(Object *ob, const float camera_position[3])
 	}
 }
 
-static Object *lod_ob_get(Object *ob, ViewLayer *view_layer, int flag, const eObjectMode object_mode)
+static Object *lod_ob_get(Object *ob, ViewLayer *view_layer, int flag)
 {
 	LodLevel *current = ob->currentlod;
 
-	if (!current || !BKE_object_lod_is_usable(ob, view_layer, object_mode))
+	if (!current || !BKE_object_lod_is_usable(ob, view_layer))
 		return ob;
 
 	while (current->prev && (!(current->flags & flag) || !current->source || current->source->type != OB_MESH)) {
@@ -917,14 +915,14 @@ static Object *lod_ob_get(Object *ob, ViewLayer *view_layer, int flag, const eOb
 	return current->source;
 }
 
-struct Object *BKE_object_lod_meshob_get(Object *ob, ViewLayer *view_layer, const eObjectMode object_mode)
+struct Object *BKE_object_lod_meshob_get(Object *ob, ViewLayer *view_layer)
 {
-	return lod_ob_get(ob, view_layer, OB_LOD_USE_MESH, object_mode);
+	return lod_ob_get(ob, view_layer, OB_LOD_USE_MESH);
 }
 
-struct Object *BKE_object_lod_matob_get(Object *ob, ViewLayer *view_layer, const eObjectMode object_mode)
+struct Object *BKE_object_lod_matob_get(Object *ob, ViewLayer *view_layer)
 {
-	return lod_ob_get(ob, view_layer, OB_LOD_USE_MAT, object_mode);
+	return lod_ob_get(ob, view_layer, OB_LOD_USE_MAT);
 }
 
 #endif  /* WITH_GAMEENGINE */
@@ -1153,13 +1151,12 @@ static void copy_object_lod(Object *obn, const Object *ob, const int UNUSED(flag
 	obn->currentlod = (LodLevel *)obn->lodlevels.first;
 }
 
-bool BKE_object_pose_context_check_ex(Object *ob, bool selected)
+bool BKE_object_pose_context_check(Object *ob)
 {
 	if ((ob) &&
 	    (ob->type == OB_ARMATURE) &&
 	    (ob->pose) &&
-	    /* Currently using selection when the object isn't active. */
-	    ((selected == false) || (ob->flag & SELECT)))
+	    (ob->mode & OB_MODE_POSE))
 	{
 		return true;
 	}
@@ -1168,23 +1165,18 @@ bool BKE_object_pose_context_check_ex(Object *ob, bool selected)
 	}
 }
 
-bool BKE_object_pose_context_check(Object *ob)
-{
-	return BKE_object_pose_context_check_ex(ob, false);
-}
-
 Object *BKE_object_pose_armature_get(Object *ob)
 {
 	if (ob == NULL)
 		return NULL;
 
-	if (BKE_object_pose_context_check_ex(ob, false))
+	if (BKE_object_pose_context_check(ob))
 		return ob;
 
 	ob = modifiers_isDeformedByArmature(ob);
 
 	/* Only use selected check when non-active. */
-	if (BKE_object_pose_context_check_ex(ob, true))
+	if (BKE_object_pose_context_check(ob))
 		return ob;
 
 	return NULL;
@@ -1264,6 +1256,7 @@ void BKE_object_copy_data(Main *UNUSED(bmain), Object *ob_dst, const Object *ob_
 	BKE_object_facemap_copy_list(&ob_dst->fmaps, &ob_src->fmaps);
 	BKE_constraints_copy_ex(&ob_dst->constraints, &ob_src->constraints, flag_subdata, true);
 
+	ob_dst->mode = OB_MODE_OBJECT;
 	ob_dst->sculpt = NULL;
 
 	if (ob_src->pd) {
diff --git a/source/blender/blenkernel/intern/object_dupli.c b/source/blender/blenkernel/intern/object_dupli.c
index 7419efba06546145d585902fb2dacc10a84bd963..0db77432d536154ec56a1bf44dccd05a1e929f12 100644
--- a/source/blender/blenkernel/intern/object_dupli.c
+++ b/source/blender/blenkernel/intern/object_dupli.c
@@ -108,8 +108,8 @@ static void init_context(DupliContext *r_ctx, const EvaluationContext *eval_ctx,
 	r_ctx->animated = false;
 	r_ctx->group = NULL;
 
-	r_ctx->obedit = OBEDIT_FROM_EVAL_CTX(eval_ctx);
 	r_ctx->object = ob;
+	r_ctx->obedit = OBEDIT_FROM_OBACT(ob);
 	if (space_mat)
 		copy_m4_m4(r_ctx->space_mat, space_mat);
 	else
diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c
index 2a33937e6edd10d3768404d5f839f15da15963ae..de2002624c02eb0ec5760d4554a441a66322e344 100644
--- a/source/blender/blenkernel/intern/object_update.c
+++ b/source/blender/blenkernel/intern/object_update.c
@@ -172,7 +172,7 @@ void BKE_object_handle_data_update(
 	switch (ob->type) {
 		case OB_MESH:
 		{
-			BMEditMesh *em = (eval_ctx->object_mode & OB_MODE_EDIT) ? BKE_editmesh_from_object(ob) : NULL;
+			BMEditMesh *em = (ob->mode & OB_MODE_EDIT) ? BKE_editmesh_from_object(ob) : NULL;
 			uint64_t data_mask = scene->customdata_mask | CD_MASK_BAREMESH;
 #ifdef WITH_FREESTYLE
 			/* make sure Freestyle edge/face marks appear in DM for render (see T40315) */
@@ -222,7 +222,7 @@ void BKE_object_handle_data_update(
 	}
 
 	/* particles */
-	if ((ob != OBEDIT_FROM_EVAL_CTX(eval_ctx)) && ob->particlesystem.first) {
+	if ((ob != OBEDIT_FROM_VIEW_LAYER(eval_ctx->view_layer)) && ob->particlesystem.first) {
 		ParticleSystem *tpsys, *psys;
 		DerivedMesh *dm;
 		ob->transflag &= ~OB_DUPLIPARTS;
diff --git a/source/blender/blenkernel/intern/packedFile.c b/source/blender/blenkernel/intern/packedFile.c
index 35e2531675df73ad1e3e30bb07c01deab1f8a20e..f17c10765b80a3fd231e74b8aa10aca836d3e994 100644
--- a/source/blender/blenkernel/intern/packedFile.c
+++ b/source/blender/blenkernel/intern/packedFile.c
@@ -354,16 +354,15 @@ int writePackedFile(ReportList *reports, const char *filename, PackedFile *pf, i
 
 	return (ret_value);
 }
-	
-/*
+
+/**
  * This function compares a packed file to a 'real' file.
  * It returns an integer indicating if:
  *
- * PF_EQUAL		- the packed file and original file are identical
- * PF_DIFFERENT	- the packed file and original file differ
- * PF_NOFILE	- the original file doens't exist
+ * - PF_EQUAL:     the packed file and original file are identical
+ * - PF_DIFFERENT: the packed file and original file differ
+ * - PF_NOFILE:    the original file doens't exist
  */
-
 int checkPackedFile(const char *filename, PackedFile *pf)
 {
 	BLI_stat_t st;
diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c
index 81943d470dc3d1a956da7255608174dd95df342a..20375fe69533ac72afa2af8a85995699786599a0 100644
--- a/source/blender/blenkernel/intern/paint.c
+++ b/source/blender/blenkernel/intern/paint.c
@@ -76,67 +76,27 @@ const char PAINT_CURSOR_TEXTURE_PAINT[3] = {255, 255, 255};
 
 static eOverlayControlFlags overlay_flags = 0;
 
-/* Keep in sync with 'BKE_paint_get_active' */
-#define OB_MODE_HAS_PAINT_STRUCT(SEP) \
-	OB_MODE_SCULPT SEP \
-	OB_MODE_VERTEX_PAINT SEP \
-	OB_MODE_WEIGHT_PAINT SEP \
-	OB_MODE_TEXTURE_PAINT SEP \
-	OB_MODE_EDIT
-
-#define COMMA ,
-static const eObjectMode ob_mode_has_paint_struct = OB_MODE_HAS_PAINT_STRUCT(|);
-static const eObjectMode ob_mode_has_paint_struct_array[] = {OB_MODE_HAS_PAINT_STRUCT(COMMA)};
-#undef COMMA
-
-#define FOREACH_OB_MODE_PAINT_ITER_BEGIN(scene, view_layer, object_mode, p) \
-{ \
-	eObjectMode object_mode_test = object_mode & ob_mode_has_paint_struct; \
-	for (uint _i = 0; _i < ARRAY_SIZE(ob_mode_has_paint_struct_array) && object_mode_test; _i++) { \
-		eObjectMode object_mode_single = ob_mode_has_paint_struct_array[_i]; \
-		if (object_mode_test & object_mode_single) { \
-			object_mode_test &= ~object_mode_single; \
-			Paint *p = BKE_paint_get_active(scene, view_layer, object_mode_single); \
-			{
-
-#define FOREACH_OB_MODE_PAINT_ITER_END \
-			} \
-		} \
-	} \
-} ((void)0)
-
-void BKE_paint_invalidate_overlay_tex(
-        Scene *scene, ViewLayer *view_layer, const Tex *tex, eObjectMode object_mode)
+void BKE_paint_invalidate_overlay_tex(Scene *scene, ViewLayer *view_layer, const Tex *tex)
 {
-	FOREACH_OB_MODE_PAINT_ITER_BEGIN(scene, view_layer, object_mode, p)
-	{
-		Brush *br = p->brush;
-		if (br) {
-			if (br->mtex.tex == tex) {
-				overlay_flags |= PAINT_INVALID_OVERLAY_TEXTURE_PRIMARY;
-			}
-			if (br->mask_mtex.tex == tex) {
-				overlay_flags |= PAINT_INVALID_OVERLAY_TEXTURE_SECONDARY;
-			}
-		}
-	}
-	FOREACH_OB_MODE_PAINT_ITER_END;
+	Paint *p = BKE_paint_get_active(scene, view_layer);
+	Brush *br = p->brush;
+
+	if (!br)
+		return;
+
+	if (br->mtex.tex == tex)
+		overlay_flags |= PAINT_INVALID_OVERLAY_TEXTURE_PRIMARY;
+	if (br->mask_mtex.tex == tex)
+		overlay_flags |= PAINT_INVALID_OVERLAY_TEXTURE_SECONDARY;
 }
 
-void BKE_paint_invalidate_cursor_overlay(
-        Scene *scene, ViewLayer *view_layer, CurveMapping *curve, eObjectMode object_mode)
+void BKE_paint_invalidate_cursor_overlay(Scene *scene, ViewLayer *view_layer, CurveMapping *curve)
 {
-	FOREACH_OB_MODE_PAINT_ITER_BEGIN(scene, view_layer, object_mode, p)
-	{
-		Brush *br = p->brush;
-		if (br) {
-			if (br->curve == curve) {
-				overlay_flags |= PAINT_INVALID_OVERLAY_CURVE;
-				break;
-			}
-		}
-	}
-	FOREACH_OB_MODE_PAINT_ITER_END;
+	Paint *p = BKE_paint_get_active(scene, view_layer);
+	Brush *br = p->brush;
+
+	if (br && br->curve == curve)
+		overlay_flags |= PAINT_INVALID_OVERLAY_CURVE;
 }
 
 void BKE_paint_invalidate_overlay_all(void)
@@ -198,13 +158,13 @@ Paint *BKE_paint_get_active_from_paintmode(Scene *sce, ePaintMode mode)
 	return NULL;
 }
 
-Paint *BKE_paint_get_active(Scene *sce, ViewLayer *view_layer, const eObjectMode object_mode)
+Paint *BKE_paint_get_active(Scene *sce, ViewLayer *view_layer)
 {
 	if (sce && view_layer) {
 		ToolSettings *ts = sce->toolsettings;
 		
 		if (view_layer->basact && view_layer->basact->object) {
-			switch (object_mode) {
+			switch (view_layer->basact->object->mode) {
 				case OB_MODE_SCULPT:
 					return &ts->sculpt->paint;
 				case OB_MODE_VERTEX_PAINT:
@@ -236,7 +196,6 @@ Paint *BKE_paint_get_active_from_context(const bContext *C)
 	SpaceImage *sima;
 
 	if (sce && view_layer) {
-		const WorkSpace *workspace = CTX_wm_workspace(C);
 		ToolSettings *ts = sce->toolsettings;
 		Object *obact = NULL;
 
@@ -244,7 +203,7 @@ Paint *BKE_paint_get_active_from_context(const bContext *C)
 			obact = view_layer->basact->object;
 
 		if ((sima = CTX_wm_space_image(C)) != NULL) {
-			if (obact && workspace->object_mode == OB_MODE_EDIT) {
+			if (obact && obact->mode == OB_MODE_EDIT) {
 				if (sima->mode == SI_MODE_PAINT)
 					return &ts->imapaint.paint;
 				else if (ts->use_uv_sculpt)
@@ -255,7 +214,7 @@ Paint *BKE_paint_get_active_from_context(const bContext *C)
 			}
 		}
 		else if (obact) {
-			switch (workspace->object_mode) {
+			switch (obact->mode) {
 				case OB_MODE_SCULPT:
 					return &ts->sculpt->paint;
 				case OB_MODE_VERTEX_PAINT:
@@ -288,7 +247,6 @@ ePaintMode BKE_paintmode_get_active_from_context(const bContext *C)
 	SpaceImage *sima;
 
 	if (sce && view_layer) {
-		const WorkSpace *workspace = CTX_wm_workspace(C);
 		ToolSettings *ts = sce->toolsettings;
 		Object *obact = NULL;
 
@@ -296,7 +254,7 @@ ePaintMode BKE_paintmode_get_active_from_context(const bContext *C)
 			obact = view_layer->basact->object;
 
 		if ((sima = CTX_wm_space_image(C)) != NULL) {
-			if (obact && workspace->object_mode == OB_MODE_EDIT) {
+			if (obact && obact->mode == OB_MODE_EDIT) {
 				if (sima->mode == SI_MODE_PAINT)
 					return ePaintTexture2D;
 				else if (ts->use_uv_sculpt)
@@ -307,7 +265,7 @@ ePaintMode BKE_paintmode_get_active_from_context(const bContext *C)
 			}
 		}
 		else if (obact) {
-			switch (workspace->object_mode) {
+			switch (obact->mode) {
 				case OB_MODE_SCULPT:
 					return ePaintSculpt;
 				case OB_MODE_VERTEX_PAINT:
@@ -421,7 +379,7 @@ void BKE_paint_curve_clamp_endpoint_add_index(PaintCurve *pc, const int add_inde
 /* remove colour from palette. Must be certain color is inside the palette! */
 void BKE_palette_color_remove(Palette *palette, PaletteColor *color)
 {
-	if (BLI_listbase_count_ex(&palette->colors, palette->active_color) == palette->active_color) {
+	if (BLI_listbase_count_at_most(&palette->colors, palette->active_color) == palette->active_color) {
 		palette->active_color--;
 	}
 
@@ -498,24 +456,24 @@ bool BKE_palette_is_empty(const struct Palette *palette)
 
 
 /* are we in vertex paint or weight pain face select mode? */
-bool BKE_paint_select_face_test(Object *ob, eObjectMode object_mode)
+bool BKE_paint_select_face_test(Object *ob)
 {
 	return ( (ob != NULL) &&
 	         (ob->type == OB_MESH) &&
 	         (ob->data != NULL) &&
 	         (((Mesh *)ob->data)->editflag & ME_EDIT_PAINT_FACE_SEL) &&
-	         (object_mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT))
+	         (ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT))
 	         );
 }
 
 /* are we in weight paint vertex select mode? */
-bool BKE_paint_select_vert_test(Object *ob, eObjectMode object_mode)
+bool BKE_paint_select_vert_test(Object *ob)
 {
 	return ( (ob != NULL) &&
 	         (ob->type == OB_MESH) &&
 	         (ob->data != NULL) &&
 	         (((Mesh *)ob->data)->editflag & ME_EDIT_PAINT_VERT_SEL) &&
-	         (object_mode & OB_MODE_WEIGHT_PAINT || object_mode & OB_MODE_VERTEX_PAINT)
+	         (ob->mode & OB_MODE_WEIGHT_PAINT || ob->mode & OB_MODE_VERTEX_PAINT)
 	         );
 }
 
@@ -523,10 +481,10 @@ bool BKE_paint_select_vert_test(Object *ob, eObjectMode object_mode)
  * used to check if selection is possible
  * (when we don't care if its face or vert)
  */
-bool BKE_paint_select_elem_test(Object *ob, eObjectMode object_mode)
+bool BKE_paint_select_elem_test(Object *ob)
 {
-	return (BKE_paint_select_vert_test(ob, object_mode) ||
-	        BKE_paint_select_face_test(ob, object_mode));
+	return (BKE_paint_select_vert_test(ob) ||
+	        BKE_paint_select_face_test(ob));
 }
 
 void BKE_paint_cavity_curve_preset(Paint *p, int preset)
@@ -829,7 +787,7 @@ void BKE_sculptsession_free(Object *ob)
 			BM_log_free(ss->bm_log);
 
 		if (dm && dm->getPBVH)
-			dm->getPBVH(NULL, dm, OB_MODE_OBJECT);  /* signal to clear */
+			dm->getPBVH(NULL, dm);  /* signal to clear */
 
 		if (ss->texcache)
 			MEM_freeN(ss->texcache);
@@ -947,7 +905,7 @@ void BKE_sculpt_update_mesh_elements(
 			if (!CustomData_has_layer(&me->ldata, CD_GRID_PAINT_MASK)) {
 #if 1
 				BKE_sculpt_mask_layers_ensure(ob, mmd);
-#else				/* if we wanted to support adding mask data while multi-res painting, we would need to do this */
+#else			/* if we wanted to support adding mask data while multi-res painting, we would need to do this */
 				if ((ED_sculpt_mask_layers_ensure(ob, mmd) & ED_SCULPT_MASK_LAYER_CALC_LOOP)) {
 					/* remake the derived mesh */
 					ob->recalc |= OB_RECALC_DATA;
@@ -966,7 +924,7 @@ void BKE_sculpt_update_mesh_elements(
 	dm = mesh_get_derived_final(eval_ctx, scene, ob, CD_MASK_BAREMESH);
 
 	/* VWPaint require mesh info for loop lookup, so require sculpt mode here */
-	if (mmd && eval_ctx->object_mode & OB_MODE_SCULPT) {
+	if (mmd && ob->mode & OB_MODE_SCULPT) {
 		ss->multires = mmd;
 		ss->totvert = dm->getNumVerts(dm);
 		ss->totpoly = dm->getNumPolys(dm);
@@ -984,7 +942,7 @@ void BKE_sculpt_update_mesh_elements(
 		ss->vmask = CustomData_get_layer(&me->vdata, CD_PAINT_MASK);
 	}
 
-	ss->pbvh = dm->getPBVH(ob, dm, eval_ctx->object_mode);
+	ss->pbvh = dm->getPBVH(ob, dm);
 	ss->pmap = (need_pmap && dm->getPolyMap) ? dm->getPolyMap(ob, dm) : NULL;
 
 	pbvh_show_diffuse_color_set(ss->pbvh, ss->show_diffuse_color);
diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c
index 94f37c3e02c7e3266399e74b5c18d388da8e9618..3e9c525b92d391b6ecb84eb00a6e1f34c4de477c 100644
--- a/source/blender/blenkernel/intern/particle.c
+++ b/source/blender/blenkernel/intern/particle.c
@@ -251,7 +251,7 @@ struct LatticeDeformData *psys_create_lattice_deform_data(ParticleSimulationData
 {
 	struct LatticeDeformData *lattice_deform_data = NULL;
 
-	if (psys_in_edit_mode(sim->eval_ctx, sim->eval_ctx->view_layer, sim->psys) == 0) {
+	if (psys_in_edit_mode(sim->eval_ctx->view_layer, sim->psys) == 0) {
 		Object *lattice = NULL;
 		ModifierData *md = (ModifierData *)psys_get_modifier(sim->ob, sim->psys);
 		int mode = G.is_rendering ? eModifierMode_Render : eModifierMode_Realtime;
@@ -288,12 +288,11 @@ void psys_enable_all(Object *ob)
 		psys->flag &= ~PSYS_DISABLED;
 }
 
-bool psys_in_edit_mode(const EvaluationContext *eval_ctx, ViewLayer *view_layer, ParticleSystem *psys)
+bool psys_in_edit_mode(ViewLayer *view_layer, ParticleSystem *psys)
 {
-	/* TODO(mai): the check for view_layer shouldnt be needed, remove when render engine api is updated for this */
-	return (view_layer && view_layer->basact &&
-	        (eval_ctx->object_mode & OB_MODE_PARTICLE_EDIT) &&
-	        psys == psys_get_current(view_layer->basact->object) &&
+	return (view_layer->basact &&
+	        (view_layer->basact->object->mode & OB_MODE_PARTICLE_EDIT) &&
+	        psys == psys_get_current((view_layer->basact)->object) &&
 	        (psys->edit || psys->pointcache->edit) &&
 	        !psys->renderdata);
 }
@@ -2095,7 +2094,7 @@ static bool psys_thread_context_init_path(
 	psys_thread_context_init(ctx, sim);
 
 	/*---start figuring out what is actually wanted---*/
-	if (psys_in_edit_mode(sim->eval_ctx, sim->eval_ctx->view_layer, psys)) {
+	if (psys_in_edit_mode(sim->eval_ctx->view_layer, psys)) {
 		ParticleEditSettings *pset = &scene->toolsettings->particle;
 
 		if ((psys->renderdata == 0 && use_render_params == 0) && (psys->edit == NULL || pset->flag & PE_DRAW_PART) == 0)
@@ -2192,7 +2191,7 @@ static void psys_thread_create_path(ParticleTask *task, struct ChildParticle *cp
 	ParticleSystem *psys = ctx->sim.psys;
 	ParticleSettings *part = psys->part;
 	ParticleCacheKey **cache = psys->childcache;
-	ParticleCacheKey **pcache = psys_in_edit_mode(ctx->sim.eval_ctx, ctx->sim.eval_ctx->view_layer, psys) && psys->edit ? psys->edit->pathcache : psys->pathcache;
+	ParticleCacheKey **pcache = psys_in_edit_mode(ctx->sim.eval_ctx->view_layer, psys) && psys->edit ? psys->edit->pathcache : psys->pathcache;
 	ParticleCacheKey *child, *key[4];
 	ParticleTexture ptex;
 	float *cpa_fuv = 0, *par_rot = 0, rot[4];
@@ -2616,7 +2615,7 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra, const bool use_re
 		return;
 
 #if 0 /* TODO(mai): something is very wrong with these conditionals, they dont make sense and the cache isnt updating */
-	if (psys_in_edit_mode(sim->eval_ctx, sim->eval_ctx->view_layer, psys))
+	if (psys_in_edit_mode(sim->eval_ctx->view_layer, psys))
 		if (psys->renderdata == 0 && (psys->edit == NULL || pset->flag & PE_DRAW_PART) == 0)
 			return;
 #endif
@@ -3180,7 +3179,7 @@ ModifierData *object_add_particle_system(Scene *scene, Object *ob, const char *n
 
 	psys->part = BKE_particlesettings_add(NULL, DATA_("ParticleSettings"));
 
-	if (BLI_listbase_count_ex(&ob->particlesystem, 2) > 1)
+	if (BLI_listbase_count_at_most(&ob->particlesystem, 2) > 1)
 		BLI_snprintf(psys->name, sizeof(psys->name), DATA_("ParticleSystem %i"), BLI_listbase_count(&ob->particlesystem));
 	else
 		BLI_strncpy(psys->name, DATA_("ParticleSystem"), sizeof(psys->name));
@@ -3244,6 +3243,8 @@ void object_remove_particle_system(Scene *UNUSED(scene), Object *ob)
 
 	if (ob->particlesystem.first)
 		((ParticleSystem *) ob->particlesystem.first)->flag |= PSYS_CURRENT;
+	else
+		ob->mode &= ~OB_MODE_PARTICLE_EDIT;
 
 	DEG_relations_tag_update(G.main);
 	DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
@@ -3829,7 +3830,7 @@ void psys_get_particle_on_path(ParticleSimulationData *sim, int p, ParticleKey *
 			pind.bspline = (psys->part->flag & PART_HAIR_BSPLINE);
 			/* pind.dm disabled in editmode means we don't get effectors taken into
 			 * account when subdividing for instance */
-			pind.dm = psys_in_edit_mode(sim->eval_ctx, sim->eval_ctx->view_layer, psys) ? NULL : psys->hair_out_dm;
+			pind.dm = psys_in_edit_mode(sim->eval_ctx->view_layer, psys) ? NULL : psys->hair_out_dm;
 			init_particle_interpolation(sim->ob, psys, pa, &pind);
 			do_particle_interpolation(psys, p, pa, t, &pind, state);
 
diff --git a/source/blender/blenkernel/intern/particle_distribute.c b/source/blender/blenkernel/intern/particle_distribute.c
index f411a605c7f648ce4e380a8947732fa564ff9122..e9eacfe1eb56d05bee411a4046665f2559a10ec0 100644
--- a/source/blender/blenkernel/intern/particle_distribute.c
+++ b/source/blender/blenkernel/intern/particle_distribute.c
@@ -800,7 +800,7 @@ static void distribute_invalid(Scene *scene, ParticleSystem *psys, int from)
 	}
 }
 
-/* Creates a distribution of coordinates on a DerivedMesh	*/
+/* Creates a distribution of coordinates on a DerivedMesh */
 /* This is to denote functionality that does not yet work with mesh - only derived mesh */
 static int psys_thread_context_init_distribute(ParticleThreadContext *ctx, ParticleSimulationData *sim, int from)
 {
@@ -950,9 +950,9 @@ static int psys_thread_context_init_distribute(ParticleThreadContext *ctx, Parti
 		return 0;
 	}
 
-	element_weight	= MEM_callocN(sizeof(float)*totelem, "particle_distribution_weights");
-	particle_element= MEM_callocN(sizeof(int)*totpart, "particle_distribution_indexes");
-	jitter_offset	= MEM_callocN(sizeof(float)*totelem, "particle_distribution_jitoff");
+	element_weight   = MEM_callocN(sizeof(float) * totelem, "particle_distribution_weights");
+	particle_element = MEM_callocN(sizeof(int) * totpart, "particle_distribution_indexes");
+	jitter_offset    = MEM_callocN(sizeof(float) * totelem, "particle_distribution_jitoff");
 
 	/* Calculate weights from face areas */
 	if ((part->flag&PART_EDISTR || children) && from != PART_FROM_VERT) {
diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c
index f27a570ca29e4bd73573f244b4865d651e0ef8a9..2b00e52246f35b39460bcfe4dcd86b6b67ddc110 100644
--- a/source/blender/blenkernel/intern/particle_system.c
+++ b/source/blender/blenkernel/intern/particle_system.c
@@ -76,7 +76,6 @@
 #include "BKE_effect.h"
 #include "BKE_library_query.h"
 #include "BKE_particle.h"
-#include "BKE_global.h"
 
 #include "BKE_collection.h"
 #include "BKE_DerivedMesh.h"
@@ -2912,11 +2911,8 @@ static void psys_update_path_cache(ParticleSimulationData *sim, float cfra, cons
 	if ((psys->part->childtype && psys->totchild != psys_get_tot_child(sim->scene, psys)) || psys->recalc&PSYS_RECALC_RESET)
 		alloc=1;
 
-	if (alloc || psys->recalc&PSYS_RECALC_CHILD ||
-	    (psys->vgroup[PSYS_VG_DENSITY] && (sim->ob && sim->eval_ctx->object_mode & OB_MODE_WEIGHT_PAINT)))
-	{
+	if (alloc || psys->recalc&PSYS_RECALC_CHILD || (psys->vgroup[PSYS_VG_DENSITY] && (sim->ob && sim->ob->mode & OB_MODE_WEIGHT_PAINT)))
 		distr=1;
-	}
 
 	if (distr) {
 		if (alloc)
@@ -2946,7 +2942,7 @@ static void psys_update_path_cache(ParticleSimulationData *sim, float cfra, cons
 			skip = 1; /* no need to cache paths while baking dynamics */
 
 #if 0 /* TODO(mai): something is very wrong with these conditionals, they dont make sense and the cache isnt updating */
-		else if (psys_in_edit_mode(sim->eval_ctx, sim->eval_ctx->view_layer, psys)) {
+		else if (psys_in_edit_mode(sim->eval_ctx->view_layer, psys)) {
 			if ((pset->flag & PE_DRAW_PART)==0)
 				skip = 1;
 			else if (part->childtype==0 && (psys->flag & PSYS_HAIR_DYNAMICS && psys->pointcache->flag & PTCACHE_BAKED)==0)
@@ -4417,24 +4413,16 @@ void BKE_particlesystem_id_loop(ParticleSystem *psys, ParticleSystemIDFunc func,
 
 /* **** Depsgraph evaluation **** */
 
-void BKE_particle_system_settings_eval(const struct EvaluationContext *UNUSED(eval_ctx),
-                                       ParticleSystem *psys)
-{
-	DEG_debug_print_eval(__func__, psys->name, psys);
-	psys->recalc |= psys->part->recalc;
-}
-
-void BKE_particle_system_settings_recalc_clear(struct EvaluationContext *UNUSED(eval_ctx),
-                                               ParticleSettings *particle_settings)
-{
-	DEG_debug_print_eval(__func__, particle_settings->id.name, particle_settings);
-	particle_settings->recalc = 0;
-}
-
 void BKE_particle_system_eval_init(const struct EvaluationContext *UNUSED(eval_ctx),
                                    Scene *scene,
                                    Object *ob)
 {
 	DEG_debug_print_eval(__func__, ob->id.name, ob);
+	for (ParticleSystem *psys = ob->particlesystem.first;
+	     psys != NULL;
+	     psys = psys->next)
+	{
+		psys->recalc |= (psys->part->id.recalc & DEG_TAG_PSYS_ALL);
+	}
 	BKE_ptcache_object_reset(scene, ob, PTCACHE_RESET_DEPSGRAPH);
 }
diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.c b/source/blender/blenkernel/intern/pbvh_bmesh.c
index 6b6f2605dc36315f269fc1088d4a3032521e93d2..e32a5d0681e0419da1e0b425b817b5edd1ce6ce8 100644
--- a/source/blender/blenkernel/intern/pbvh_bmesh.c
+++ b/source/blender/blenkernel/intern/pbvh_bmesh.c
@@ -555,9 +555,9 @@ static int pbvh_bmesh_node_vert_use_count(PBVH *bvh, PBVHNode *node, BMVert *v)
 #endif
 
 #define pbvh_bmesh_node_vert_use_count_is_equal(bvh, node, v, n) \
-	(pbvh_bmesh_node_vert_use_count_ex(bvh, node, v, (n) + 1) == n)
+	(pbvh_bmesh_node_vert_use_count_at_most(bvh, node, v, (n) + 1) == n)
 
-static int pbvh_bmesh_node_vert_use_count_ex(PBVH *bvh, PBVHNode *node, BMVert *v, const int count_max)
+static int pbvh_bmesh_node_vert_use_count_at_most(PBVH *bvh, PBVHNode *node, BMVert *v, const int count_max)
 {
 	int count = 0;
 	BMFace *f;
diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c
index ca9cddde65a67d0fc029dd23dd373f187a756a4a..b27f7370ff4ba0fc9902b838d21bdf96fe61d034 100644
--- a/source/blender/blenkernel/intern/scene.c
+++ b/source/blender/blenkernel/intern/scene.c
@@ -1353,7 +1353,7 @@ static bool check_rendered_viewport_visible(Main *bmain)
 /* TODO(campbell): shouldn't we be able to use 'eval_ctx->view_layer' here?
  * Currently this is NULL on load, so don't. */
 static void prepare_mesh_for_viewport_render(
-        Main *bmain, const EvaluationContext *eval_ctx, const ViewLayer *view_layer)
+        Main *bmain, const ViewLayer *view_layer)
 {
 	/* This is needed to prepare mesh to be used by the render
 	 * engine from the viewport rendering. We do loading here
@@ -1364,8 +1364,7 @@ static void prepare_mesh_for_viewport_render(
 	 * call loading of the edit data for the mesh objects.
 	 */
 
-	/* Expanded 'OBEDIT_FROM_EVAL_CTX' */
-	Object *obedit = (eval_ctx->object_mode & OB_MODE_EDIT) ? OBACT(view_layer) : NULL;
+	Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
 	if (obedit) {
 		Mesh *mesh = obedit->data;
 		if ((obedit->type == OB_MESH) &&
@@ -1407,7 +1406,7 @@ void BKE_scene_graph_update_tagged(EvaluationContext *eval_ctx,
 	/* Uncomment this to check if graph was properly tagged for update. */
 	// DEG_debug_graph_relations_validate(depsgraph, bmain, scene);
 	/* Flush editing data if needed. */
-	prepare_mesh_for_viewport_render(bmain, eval_ctx, view_layer);
+	prepare_mesh_for_viewport_render(bmain, view_layer);
 	/* Flush recalc flags to dependencies. */
 	DEG_graph_flush_update(bmain, depsgraph);
 	/* Update all objects: drivers, matrices, displists, etc. flags set
diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c
index 780e2cb95b40ba8b5db789d9859293f1f334e3a9..0e4a537f0b29cb7badd0b0829898e1608c9d2780 100644
--- a/source/blender/blenkernel/intern/screen.c
+++ b/source/blender/blenkernel/intern/screen.c
@@ -39,8 +39,6 @@
 
 #include "MEM_guardedalloc.h"
 
-#include "GPU_compositing.h"
-
 #include "DNA_scene_types.h"
 #include "DNA_screen_types.h"
 #include "DNA_space_types.h"
@@ -712,29 +710,6 @@ float BKE_screen_view3d_zoom_from_fac(float zoomfac)
 	return ((sqrtf(4.0f * zoomfac) - (float)M_SQRT2) * 50.0f);
 }
 
-void BKE_screen_gpu_fx_validate(GPUFXSettings *fx_settings)
-{
-	/* currently we use DOF from the camera _only_,
-	 * so we never allocate this, only copy from the Camera */
-#if 0
-	if ((fx_settings->dof == NULL) &&
-	    (fx_settings->fx_flag & GPU_FX_FLAG_DOF))
-	{
-		GPUDOFSettings *fx_dof;
-		fx_dof = fx_settings->dof = MEM_callocN(sizeof(GPUDOFSettings), __func__);
-	}
-#endif
-
-	if ((fx_settings->ssao == NULL) &&
-	    (fx_settings->fx_flag & GPU_FX_FLAG_SSAO))
-	{
-		GPUSSAOSettings *fx_ssao;
-		fx_ssao = fx_settings->ssao = MEM_callocN(sizeof(GPUSSAOSettings), __func__);
-
-		GPU_fx_compositor_init_ssao_settings(fx_ssao);
-	}
-}
-
 bool BKE_screen_is_fullscreen_area(const bScreen *screen)
 {
 	return ELEM(screen->state, SCREENMAXIMIZED, SCREENFULL);
diff --git a/source/blender/blenkernel/intern/seqeffects.c b/source/blender/blenkernel/intern/seqeffects.c
index ee80438db6442fe002efe712e9550cc1baef07a5..49f120de2502ff996a643472b8e0afe08240d27c 100644
--- a/source/blender/blenkernel/intern/seqeffects.c
+++ b/source/blender/blenkernel/intern/seqeffects.c
@@ -52,6 +52,7 @@
 #include "IMB_imbuf_types.h"
 #include "IMB_imbuf.h"
 #include "IMB_colormanagement.h"
+#include "IMB_metadata.h"
 
 #include "BLI_math_color_blend.h"
 
diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c
index 66221dd470f00aea6a887f9e8eb38492e51fc27e..263c30cabca8c00d54139c7379290a85335dc99f 100644
--- a/source/blender/blenkernel/intern/sequencer.c
+++ b/source/blender/blenkernel/intern/sequencer.c
@@ -86,6 +86,7 @@
 #include "IMB_imbuf.h"
 #include "IMB_imbuf_types.h"
 #include "IMB_colormanagement.h"
+#include "IMB_metadata.h"
 
 #include "BKE_context.h"
 #include "BKE_sound.h"
@@ -946,6 +947,8 @@ void BKE_sequence_reload_new_file(Scene *scene, Sequence *seq, const bool lock_r
 				return;
 			}
 
+			IMB_anim_load_metadata(sanim->anim);
+
 			seq->len = IMB_anim_get_duration(sanim->anim, seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN);
 
 			seq->anim_preseek = IMB_anim_get_preseek(sanim->anim);
@@ -2959,7 +2962,7 @@ static ImBuf *seq_render_movie_strip(const SeqRenderData *context, Sequence *seq
 		int totviews;
 		int i;
 
-		if (totfiles != BLI_listbase_count_ex(&seq->anims, totfiles + 1))
+		if (totfiles != BLI_listbase_count_at_most(&seq->anims, totfiles + 1))
 			goto monoview_movie;
 
 		totviews = BKE_scene_multiview_num_views_get(&context->scene->r);
@@ -3337,7 +3340,7 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context, Sequence *seq
 		        context->eval_ctx, scene, view_layer, camera, width, height, IB_rect,
 		        draw_flags, context->scene->r.seq_prev_type,
 		        scene->r.alphamode, context->gpu_samples, viewname,
-		        context->gpu_fx, context->gpu_offscreen, err_out);
+		        context->gpu_offscreen, err_out);
 		if (ibuf == NULL) {
 			fprintf(stderr, "seq_render_scene_strip failed to get opengl buffer: %s\n", err_out);
 		}
@@ -5392,6 +5395,8 @@ Sequence *BKE_sequencer_add_movie_strip(bContext *C, ListBase *seqbasep, SeqLoad
 		}
 	}
 
+	IMB_anim_load_metadata(anim_arr[0]);
+
 	seq->anim_preseek = IMB_anim_get_preseek(anim_arr[0]);
 	BLI_strncpy(seq->name + 2, "Movie", SEQ_NAME_MAXSTR - 2);
 	BKE_sequence_base_unique_name_recursive(&scene->ed->seqbase, seq);
diff --git a/source/blender/blenkernel/intern/shrinkwrap.c b/source/blender/blenkernel/intern/shrinkwrap.c
index e91c3e43b83542b08c321e90ad796f51d1ef496f..618f495dbf1845673f84849ab8db92a005917677 100644
--- a/source/blender/blenkernel/intern/shrinkwrap.c
+++ b/source/blender/blenkernel/intern/shrinkwrap.c
@@ -57,8 +57,6 @@
 
 #include "BLI_strict_flags.h"
 
-#include "DEG_depsgraph.h"
-
 /* for timing... */
 #if 0
 #  include "PIL_time_utildefines.h"
@@ -616,9 +614,8 @@ static void shrinkwrap_calc_nearest_surface_point(ShrinkwrapCalcData *calc)
 }
 
 /* Main shrinkwrap function */
-void shrinkwrapModifier_deform(
-        const EvaluationContext *eval_ctx, ShrinkwrapModifierData *smd, Object *ob, DerivedMesh *dm,
-        float (*vertexCos)[3], int numVerts, bool for_render)
+void shrinkwrapModifier_deform(ShrinkwrapModifierData *smd, Object *ob, DerivedMesh *dm,
+                               float (*vertexCos)[3], int numVerts, bool for_render)
 {
 
 	DerivedMesh *ss_mesh    = NULL;
@@ -673,8 +670,7 @@ void shrinkwrapModifier_deform(
 			ssmd.subdivType = ME_CC_SUBSURF;        /* catmull clark */
 			ssmd.levels     = smd->subsurfLevels;   /* levels */
 
-			ss_mesh = subsurf_make_derived_from_derived(
-			        dm, &ssmd, NULL, (eval_ctx->object_mode & OB_MODE_EDIT) ? SUBSURF_IN_EDIT_MODE : 0);
+			ss_mesh = subsurf_make_derived_from_derived(dm, &ssmd, NULL, (ob->mode & OB_MODE_EDIT) ? SUBSURF_IN_EDIT_MODE : 0);
 
 			if (ss_mesh) {
 				calc.vert = ss_mesh->getVertDataArray(ss_mesh, CD_MVERT);
diff --git a/source/blender/blenkernel/intern/smoke.c b/source/blender/blenkernel/intern/smoke.c
index 6fec55200d6fe2aaedc89eb6b71f3d00a6dc87a7..9215d56eb30dd7a12b9e04e2f2d1d0066da8f266 100644
--- a/source/blender/blenkernel/intern/smoke.c
+++ b/source/blender/blenkernel/intern/smoke.c
@@ -72,7 +72,6 @@
 #include "BKE_customdata.h"
 #include "BKE_deform.h"
 #include "BKE_DerivedMesh.h"
-#include "BKE_global.h"
 #include "BKE_effect.h"
 #include "BKE_main.h"
 #include "BKE_modifier.h"
diff --git a/source/blender/blenkernel/intern/speaker.c b/source/blender/blenkernel/intern/speaker.c
index 9d604a9382a9bba1e970073a0e7863f9dc847159..1d2e12f34ac37b47332838c5ec91796dbca3f201 100644
--- a/source/blender/blenkernel/intern/speaker.c
+++ b/source/blender/blenkernel/intern/speaker.c
@@ -32,7 +32,6 @@
 #include "BLI_utildefines.h"
 
 #include "BKE_animsys.h"
-#include "BKE_global.h"
 #include "BKE_library.h"
 #include "BKE_library_query.h"
 #include "BKE_library_remap.h"
diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c
index 10792b7d5791d6cf50a42d53c65872cd346f5002..9280341b4e4dd25336a7caa6bc85ebd573668f56 100644
--- a/source/blender/blenkernel/intern/subsurf_ccg.c
+++ b/source/blender/blenkernel/intern/subsurf_ccg.c
@@ -85,8 +85,6 @@
 
 #include "CCGSubSurf.h"
 
-#include "DEG_depsgraph.h"
-
 #ifdef WITH_OPENSUBDIV
 #  include "opensubdiv_capi.h"
 #endif
@@ -3790,14 +3788,12 @@ static void ccgDM_release(DerivedMesh *dm)
 			{
 				ccgdm->multires.mmd = NULL;
 			}
+			
 			if (ccgdm->multires.mmd) {
-				if (ccgdm->multires.modified_flags & MULTIRES_COORDS_MODIFIED) {
-					/* TODO/OBMODE, pass real mode? */
-					multires_modifier_update_mdisps(dm, OB_MODE_OBJECT);
-				}
-				if (ccgdm->multires.modified_flags & MULTIRES_HIDDEN_MODIFIED) {
+				if (ccgdm->multires.modified_flags & MULTIRES_COORDS_MODIFIED)
+					multires_modifier_update_mdisps(dm);
+				if (ccgdm->multires.modified_flags & MULTIRES_HIDDEN_MODIFIED)
 					multires_modifier_update_hidden(dm);
-				}
 			}
 		}
 
@@ -4191,8 +4187,7 @@ static int ccgDM_use_grid_pbvh(CCGDerivedMesh *ccgdm)
 	return 1;
 }
 
-static struct PBVH *ccgDM_getPBVH(
-        Object *ob, DerivedMesh *dm, eObjectMode object_mode)
+static struct PBVH *ccgDM_getPBVH(Object *ob, DerivedMesh *dm)
 {
 	CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm;
 	CCGKey key;
@@ -4209,7 +4204,7 @@ static struct PBVH *ccgDM_getPBVH(
 		return NULL;
 
 	bool grid_pbvh = ccgDM_use_grid_pbvh(ccgdm);
-	if ((object_mode & OB_MODE_SCULPT) == 0) {
+	if ((ob->mode & OB_MODE_SCULPT) == 0) {
 		/* In vwpaint, we may use a grid_pbvh for multires/subsurf, under certain conditions.
 		 * More complex cases break 'history' trail back to original vertices, in that case we fall back to
 		 * deformed cage only (i.e. original deformed mesh). */
diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c
index 4ae9818f891e8361e45769e2cc33ff04ea140478..cd9b8ae339d37f51b92f353beb64507b48f6d5fe 100644
--- a/source/blender/blenkernel/intern/text.c
+++ b/source/blender/blenkernel/intern/text.c
@@ -120,13 +120,56 @@
  *
  */
 
+
+/* Undo opcodes */
+
+enum {
+	/* Complex editing */
+	/* 1 - opcode is followed by 1 byte for ascii character and opcode (repeat)) */
+	/* 2 - opcode is followed by 2 bytes for utf-8 character and opcode (repeat)) */
+	/* 3 - opcode is followed by 3 bytes for utf-8 character and opcode (repeat)) */
+	/* 4 - opcode is followed by 4 bytes for unicode character and opcode (repeat)) */
+	UNDO_INSERT_1   = 013,
+	UNDO_INSERT_2   = 014,
+	UNDO_INSERT_3   = 015,
+	UNDO_INSERT_4   = 016,
+
+	UNDO_BS_1       = 017,
+	UNDO_BS_2       = 020,
+	UNDO_BS_3       = 021,
+	UNDO_BS_4       = 022,
+
+	UNDO_DEL_1      = 023,
+	UNDO_DEL_2      = 024,
+	UNDO_DEL_3      = 025,
+	UNDO_DEL_4      = 026,
+
+	/* Text block (opcode is followed
+	 * by 4 character length ID + the text
+	 * block itself + the 4 character length
+	 * ID (repeat) and opcode (repeat)) */
+	UNDO_DBLOCK     = 027, /* Delete block */
+	UNDO_IBLOCK     = 030, /* Insert block */
+
+	/* Misc */
+	UNDO_INDENT     = 032,
+	UNDO_UNINDENT   = 033,
+	UNDO_COMMENT    = 034,
+	UNDO_UNCOMMENT  = 035,
+
+	UNDO_MOVE_LINES_UP      = 036,
+	UNDO_MOVE_LINES_DOWN    = 037,
+
+	UNDO_DUPLICATE  = 040,
+};
+
 /***/
 
 static void txt_pop_first(Text *text);
 static void txt_pop_last(Text *text);
-static void txt_undo_add_blockop(Text *text, int op, const char *buf);
+static void txt_undo_add_blockop(Text *text, TextUndoBuf *utxt, int op, const char *buf);
 static void txt_delete_line(Text *text, TextLine *line);
-static void txt_delete_sel(Text *text);
+static void txt_delete_sel(Text *text, TextUndoBuf *utxt);
 static void txt_make_dirty(Text *text);
 
 /***/
@@ -144,13 +187,6 @@ int txt_get_undostate(void)
 	return undoing;
 }
 
-static void init_undo_text(Text *text)
-{
-	text->undo_pos = -1;
-	text->undo_len = TXT_INIT_UNDO;
-	text->undo_buf = MEM_mallocN(text->undo_len, "undo buf");
-}
-
 /**
  * \note caller must handle `undo_buf` and `compiled` members.
  */
@@ -178,7 +214,6 @@ void BKE_text_free(Text *text)
 	BKE_text_free_lines(text);
 
 	MEM_SAFE_FREE(text->name);
-	MEM_SAFE_FREE(text->undo_buf);
 #ifdef WITH_PYTHON
 	BPY_text_free_code(text);
 #endif
@@ -192,8 +227,6 @@ void BKE_text_init(Text *ta)
 
 	ta->name = NULL;
 
-	init_undo_text(ta);
-
 	ta->nlines = 1;
 	ta->flags = TXT_ISDIRTY | TXT_ISMEM;
 	if ((U.flag & USER_TXT_TABSTOSPACES_DISABLE) == 0)
@@ -375,10 +408,6 @@ bool BKE_text_reload(Text *text)
 	txt_make_dirty(text);
 
 	/* clear undo buffer */
-	MEM_freeN(text->undo_buf);
-	init_undo_text(text);
-
-
 	if (BLI_stat(filepath_abs, &st) != -1) {
 		text->mtime = st.st_mtime;
 	}
@@ -427,8 +456,6 @@ Text *BKE_text_load_ex(Main *bmain, const char *file, const char *relpath, const
 	}
 
 	/* clear undo buffer */
-	init_undo_text(ta);
-
 	if (BLI_stat(filepath_abs, &st) != -1) {
 		ta->mtime = st.st_mtime;
 	}
@@ -482,8 +509,6 @@ void BKE_text_copy_data(Main *UNUSED(bmain), Text *ta_dst, const Text *ta_src, c
 
 	ta_dst->curl = ta_dst->sell = ta_dst->lines.first;
 	ta_dst->curc = ta_dst->selc = 0;
-
-	init_undo_text(ta_dst);
 }
 
 Text *BKE_text_copy(Main *bmain, const Text *ta)
@@ -498,25 +523,29 @@ void BKE_text_make_local(Main *bmain, Text *text, const bool lib_local)
 	BKE_id_make_local_generic(bmain, &text->id, true, lib_local);
 }
 
-void BKE_text_clear(Text *text) /* called directly from rna */
+void BKE_text_clear(Text *text, TextUndoBuf *utxt) /* called directly from rna */
 {
 	int oldstate;
 
-	oldstate = txt_get_undostate();
-	txt_set_undostate(1);
+	if (utxt) {
+		oldstate = txt_get_undostate();
+	}
+	txt_set_undostate(utxt != NULL);
+
 	txt_sel_all(text);
-	txt_delete_sel(text);
+	txt_delete_sel(text, utxt);
+
 	txt_set_undostate(oldstate);
 
 	txt_make_dirty(text);
 }
 
-void BKE_text_write(Text *text, const char *str) /* called directly from rna */
+void BKE_text_write(Text *text, TextUndoBuf *utxt, const char *str) /* called directly from rna */
 {
 	int oldstate;
 
 	oldstate = txt_get_undostate();
-	txt_insert_buf(text, str);
+	txt_insert_buf(text, utxt, str);
 	txt_move_eof(text, 0);
 	txt_set_undostate(oldstate);
 
@@ -1109,7 +1138,7 @@ bool txt_has_sel(Text *text)
 	return ((text->curl != text->sell) || (text->curc != text->selc));
 }
 
-static void txt_delete_sel(Text *text)
+static void txt_delete_sel(Text *text, TextUndoBuf *utxt)
 {
 	TextLine *tmpl;
 	char *buf;
@@ -1123,7 +1152,7 @@ static void txt_delete_sel(Text *text)
 
 	if (!undoing) {
 		buf = txt_sel_to_buf(text);
-		txt_undo_add_blockop(text, UNDO_DBLOCK, buf);
+		txt_undo_add_blockop(text, utxt, UNDO_DBLOCK, buf);
 		MEM_freeN(buf);
 	}
 
@@ -1364,7 +1393,7 @@ char *txt_sel_to_buf(Text *text)
 	return buf;
 }
 
-void txt_insert_buf(Text *text, const char *in_buffer)
+void txt_insert_buf(Text *text, TextUndoBuf *utxt, const char *in_buffer)
 {
 	int l = 0, u, len;
 	size_t i = 0, j;
@@ -1373,22 +1402,22 @@ void txt_insert_buf(Text *text, const char *in_buffer)
 
 	if (!in_buffer) return;
 
-	txt_delete_sel(text);
+	txt_delete_sel(text, utxt);
 	
 	len = strlen(in_buffer);
 	buffer = BLI_strdupn(in_buffer, len);
 	len += txt_extended_ascii_as_utf8(&buffer);
 	
-	if (!undoing) txt_undo_add_blockop(text, UNDO_IBLOCK, buffer);
+	if (!undoing) txt_undo_add_blockop(text, utxt, UNDO_IBLOCK, buffer);
 
 	u = undoing;
 	undoing = 1;
 
 	/* Read the first line (or as close as possible */
 	while (buffer[i] && buffer[i] != '\n')
-		txt_add_raw_char(text, BLI_str_utf8_as_unicode_step(buffer, &i));
+		txt_add_raw_char(text, utxt, BLI_str_utf8_as_unicode_step(buffer, &i));
 	
-	if (buffer[i] == '\n') txt_split_curline(text);
+	if (buffer[i] == '\n') txt_split_curline(text, utxt);
 	else { undoing = u; MEM_freeN(buffer); return; }
 	i++;
 
@@ -1406,7 +1435,7 @@ void txt_insert_buf(Text *text, const char *in_buffer)
 		}
 		else {
 			for (j = i - l; j < i && j < len; )
-				txt_add_raw_char(text, BLI_str_utf8_as_unicode_step(buffer, &j));
+				txt_add_raw_char(text, utxt, BLI_str_utf8_as_unicode_step(buffer, &j));
 			break;
 		}
 	}
@@ -1420,33 +1449,47 @@ void txt_insert_buf(Text *text, const char *in_buffer)
 /* Undo functions */
 /******************/
 
-static bool max_undo_test(Text *text, int x)
+static bool max_undo_test(TextUndoBuf *utxt, int x)
 {
-	while (text->undo_pos + x >= text->undo_len) {
-		if (text->undo_len * 2 > TXT_MAX_UNDO) {
-			/* XXX error("Undo limit reached, buffer cleared\n"); */
-			MEM_freeN(text->undo_buf);
-			init_undo_text(text);
-			return false;
-		}
-		else {
-			void *tmp = text->undo_buf;
-			text->undo_buf = MEM_callocN(text->undo_len * 2, "undo buf");
-			memcpy(text->undo_buf, tmp, text->undo_len);
-			text->undo_len *= 2;
-			MEM_freeN(tmp);
-		}
-	}
+	/* Normally over-allocating is preferred,
+	 * however in this case the buffer is small enough and re-allocation
+	 * fast enough for each undo step that it's not a problem to allocate each time.
+	 * This also saves on some memory when we have many text buffers
+	 * that would have an empty undo memory allocated.
+	 */
 
+	/* Add one for the null terminator. */
+	utxt->len = utxt->pos + x + 1;
+	if (utxt->len > TXT_MAX_UNDO) {
+		/* XXX error("Undo limit reached, buffer cleared\n"); */
+		MEM_freeN(utxt->buf);
+		return false;
+	}
+	else {
+		/* Small reallocations on each undo step is fine. */
+		utxt->buf = MEM_recallocN(utxt->buf, utxt->len);
+	}
 	return true;
 }
 
+static void txt_undo_end(Text *UNUSED(text), TextUndoBuf *utxt)
+{
+	int undo_pos_end = utxt->pos + 1;
+	BLI_assert(undo_pos_end + 1 == utxt->len);
+	utxt->buf[undo_pos_end] = '\0';
+}
+
+/* Call once undo is done. */
+#ifndef NDEBUG
+
+#endif
+
 #if 0  /* UNUSED */
-static void dump_buffer(Text *text) 
+static void dump_buffer(TextUndoBuf *utxt)
 {
 	int i = 0;
-	
-	while (i++ < text->undo_pos) printf("%d: %d %c\n", i, text->undo_buf[i], text->undo_buf[i]);
+
+	while (i++ < utxt->undo_pos) printf("%d: %d %c\n", i, utxt->buf[i], utxt->buf[i]);
 }
 
 /* Note: this function is outdated and must be updated if needed for future use */
@@ -1461,10 +1504,10 @@ void txt_print_undo(Text *text)
 	
 	printf("---< Undo Buffer >---\n");
 	
-	printf("UndoPosition is %d\n", text->undo_pos);
+	printf("UndoPosition is %d\n", utxt->pos);
 	
-	while (i <= text->undo_pos) {
-		op = text->undo_buf[i];
+	while (i <= utxt->pos) {
+		op = utxt->buf[i];
 		
 		if (op == UNDO_INSERT_1) {
 			ops = "Insert ascii ";
@@ -1530,15 +1573,15 @@ void txt_print_undo(Text *text)
 			printf(" - Char is ");
 			switch (op) {
 				case UNDO_INSERT_1: case UNDO_BS_1: case UNDO_DEL_1:
-					printf("%c", text->undo_buf[i]);
+					printf("%c", utxt->buf[i]);
 					i++;
 					break;
 				case UNDO_INSERT_2: case UNDO_BS_2: case UNDO_DEL_2:
-					printf("%c%c", text->undo_buf[i], text->undo_buf[i + 1]);
+					printf("%c%c", utxt->buf[i], utxt->buf[i + 1]);
 					i += 2;
 					break;
 				case UNDO_INSERT_3: case UNDO_BS_3: case UNDO_DEL_3:
-					printf("%c%c%c", text->undo_buf[i], text->undo_buf[i + 1], text->undo_buf[i + 2]);
+					printf("%c%c%c", utxt->buf[i], utxt->buf[i + 1], utxt->buf[i + 2]);
 					i += 3;
 					break;
 				case UNDO_INSERT_4: case UNDO_BS_4: case UNDO_DEL_4:
@@ -1546,10 +1589,10 @@ void txt_print_undo(Text *text)
 					unsigned int uc;
 					char c[BLI_UTF8_MAX + 1];
 					size_t c_len;
-					uc = text->undo_buf[i]; i++;
-					uc = uc + (text->undo_buf[i] << 8); i++;
-					uc = uc + (text->undo_buf[i] << 16); i++;
-					uc = uc + (text->undo_buf[i] << 24); i++;
+					uc = utxt->buf[i]; i++;
+					uc = uc + (utxt->buf[i] << 8); i++;
+					uc = uc + (utxt->buf[i] << 16); i++;
+					uc = uc + (utxt->buf[i] << 24); i++;
 					c_len = BLI_str_utf8_from_unicode(uc, c);
 					c[c_len] = '\0';
 					puts(c);
@@ -1560,44 +1603,44 @@ void txt_print_undo(Text *text)
 		else if (op == UNDO_DBLOCK || op == UNDO_IBLOCK) {
 			i++;
 
-			linep = text->undo_buf[i]; i++;
-			linep = linep + (text->undo_buf[i] << 8); i++;
-			linep = linep + (text->undo_buf[i] << 16); i++;
-			linep = linep + (text->undo_buf[i] << 24); i++;
+			linep = utxt->buf[i]; i++;
+			linep = linep + (utxt->buf[i] << 8); i++;
+			linep = linep + (utxt->buf[i] << 16); i++;
+			linep = linep + (utxt->buf[i] << 24); i++;
 			
 			printf(" (length %d) <", linep);
 			
 			while (linep > 0) {
-				putchar(text->undo_buf[i]);
+				putchar(utxt->buf[i]);
 				linep--; i++;
 			}
 			
-			linep = text->undo_buf[i]; i++;
-			linep = linep + (text->undo_buf[i] << 8); i++;
-			linep = linep + (text->undo_buf[i] << 16); i++;
-			linep = linep + (text->undo_buf[i] << 24); i++;
+			linep = utxt->buf[i]; i++;
+			linep = linep + (utxt->buf[i] << 8); i++;
+			linep = linep + (utxt->buf[i] << 16); i++;
+			linep = linep + (utxt->buf[i] << 24); i++;
 			printf("> (%d)", linep);
 		}
 		else if (op == UNDO_INDENT || op == UNDO_UNINDENT) {
 			i++;
 
-			charp = text->undo_buf[i]; i++;
-			charp = charp + (text->undo_buf[i] << 8); i++;
+			charp = utxt->buf[i]; i++;
+			charp = charp + (utxt->buf[i] << 8); i++;
 
-			linep = text->undo_buf[i]; i++;
-			linep = linep + (text->undo_buf[i] << 8); i++;
-			linep = linep + (text->undo_buf[i] << 16); i++;
-			linep = linep + (text->undo_buf[i] << 24); i++;
+			linep = utxt->buf[i]; i++;
+			linep = linep + (utxt->buf[i] << 8); i++;
+			linep = linep + (utxt->buf[i] << 16); i++;
+			linep = linep + (utxt->buf[i] << 24); i++;
 			
 			printf("to <%d, %d> ", linep, charp);
 
-			charp = text->undo_buf[i]; i++;
-			charp = charp + (text->undo_buf[i] << 8); i++;
+			charp = utxt->buf[i]; i++;
+			charp = charp + (utxt->buf[i] << 8); i++;
 
-			linep = text->undo_buf[i]; i++;
-			linep = linep + (text->undo_buf[i] << 8); i++;
-			linep = linep + (text->undo_buf[i] << 16); i++;
-			linep = linep + (text->undo_buf[i] << 24); i++;
+			linep = utxt->buf[i]; i++;
+			linep = linep + (utxt->buf[i] << 8); i++;
+			linep = linep + (utxt->buf[i] << 16); i++;
+			linep = linep + (utxt->buf[i] << 24); i++;
 			
 			printf("from <%d, %d>", linep, charp);
 		}
@@ -1628,104 +1671,114 @@ static void txt_undo_store_uint32(char *undo_buf, int *undo_pos, unsigned int va
 	(*undo_pos)++;
 }
 
-/* store the cur cursor to the undo buffer */
-static void txt_undo_store_cur(Text *text)
+/* store the cur cursor to the undo buffer (6 bytes)*/
+static void txt_undo_store_cur(Text *text, TextUndoBuf *utxt)
 {
-	txt_undo_store_uint16(text->undo_buf, &text->undo_pos, text->curc);
-	txt_undo_store_uint32(text->undo_buf, &text->undo_pos, txt_get_span(text->lines.first, text->curl));
+	txt_undo_store_uint16(utxt->buf, &utxt->pos, text->curc);
+	txt_undo_store_uint32(utxt->buf, &utxt->pos, txt_get_span(text->lines.first, text->curl));
 }
 
-/* store the sel cursor to the undo buffer */
-static void txt_undo_store_sel(Text *text)
+/* store the sel cursor to the undo buffer (6 bytes) */
+static void txt_undo_store_sel(Text *text, TextUndoBuf *utxt)
 {
-	txt_undo_store_uint16(text->undo_buf, &text->undo_pos, text->selc);
-	txt_undo_store_uint32(text->undo_buf, &text->undo_pos, txt_get_span(text->lines.first, text->sell));
+	txt_undo_store_uint16(utxt->buf, &utxt->pos, text->selc);
+	txt_undo_store_uint32(utxt->buf, &utxt->pos, txt_get_span(text->lines.first, text->sell));
 }
 
-/* store both cursors to the undo buffer */
-static void txt_undo_store_cursors(Text *text)
+/* store both cursors to the undo buffer (12 bytes) */
+static void txt_undo_store_cursors(Text *text, TextUndoBuf *utxt)
 {
-	txt_undo_store_cur(text);
-	txt_undo_store_sel(text);
+	txt_undo_store_cur(text, utxt);
+	txt_undo_store_sel(text, utxt);
 }
 
 /* store an operator along with a block of data */
-static void txt_undo_add_blockop(Text *text, int op, const char *buf)
+static void txt_undo_add_blockop(Text *text, TextUndoBuf *utxt, int op, const char *buf)
 {
 	unsigned int length = strlen(buf);
-	
-	if (!max_undo_test(text, length + 11 + 12))
-		return;
-
-	text->undo_pos++;
-	text->undo_buf[text->undo_pos] = op;
-	text->undo_pos++;
-	
-	txt_undo_store_cursors(text);
-
-	txt_undo_store_uint32(text->undo_buf, &text->undo_pos, length);
-	
-	strncpy(text->undo_buf + text->undo_pos, buf, length);
-	text->undo_pos += length;
 
-	txt_undo_store_uint32(text->undo_buf, &text->undo_pos, length);
-	text->undo_buf[text->undo_pos] = op;
-	
-	text->undo_buf[text->undo_pos + 1] = 0;
+	if (!max_undo_test(utxt, 2 + 12 + 4 + length + 4 + 1)) {
+		return;
+	}
+	/* 2 bytes */
+	utxt->pos++;
+	utxt->buf[utxt->pos] = op;
+	utxt->pos++;
+	/* 12 bytes */
+	txt_undo_store_cursors(text, utxt);
+	/* 4 bytes */
+	txt_undo_store_uint32(utxt->buf, &utxt->pos, length);
+	/* 'length' bytes */
+	strncpy(utxt->buf + utxt->pos, buf, length);
+	utxt->pos += length;
+	/* 4 bytes */
+	txt_undo_store_uint32(utxt->buf, &utxt->pos, length);
+	/* 1 byte */
+	utxt->buf[utxt->pos] = op;
+
+	txt_undo_end(text, utxt);
 }
 
 /* store a regular operator */
-void txt_undo_add_op(Text *text, int op)
+void txt_undo_add_op(Text *text, TextUndoBuf *utxt, int op)
 {
-	if (!max_undo_test(text, 15))
+	if (!max_undo_test(utxt, 2 + 12 + 1)) {
 		return;
+	}
 
-	text->undo_pos++;
-	text->undo_buf[text->undo_pos] = op;
+	/* 2 bytes */
+	utxt->pos++;
+	utxt->buf[utxt->pos] = op;
+	utxt->pos++;
+	/* 12 bytes */
+	txt_undo_store_cursors(text, utxt);
+	/* 1 byte */
+	utxt->buf[utxt->pos] = op;
 
-	text->undo_pos++;
-	
-	txt_undo_store_cursors(text);
-		
-	text->undo_buf[text->undo_pos] = op;
-	text->undo_buf[text->undo_pos + 1] = 0;
+	txt_undo_end(text, utxt);
 }
 
 /* store an operator for a single character */
-static void txt_undo_add_charop(Text *text, int op_start, unsigned int c)
+static void txt_undo_add_charop(Text *text, TextUndoBuf *utxt, int op_start, unsigned int c)
 {
 	char utf8[BLI_UTF8_MAX];
 	size_t i, utf8_size = BLI_str_utf8_from_unicode(c, utf8);
 	
-	if (!max_undo_test(text, 3 + utf8_size + 12))
-		return;
-	
-	text->undo_pos++;
-	
-	if (utf8_size < 4) {
-		text->undo_buf[text->undo_pos] = op_start + utf8_size - 1;
-		text->undo_pos++;
-		
-		txt_undo_store_cur(text);
-
+	if (utf8_size < 4 && 0) {
+		if (!max_undo_test(utxt, 2 + 6 + utf8_size + 1)) {
+			return;
+		}
+		/* 2 bytes */
+		utxt->pos++;
+		utxt->buf[utxt->pos] = op_start + utf8_size - 1;
+		utxt->pos++;
+		/* 6 bytes */
+		txt_undo_store_cur(text, utxt);
+		/* 'utf8_size' bytes */
 		for (i = 0; i < utf8_size; i++) {
-			text->undo_buf[text->undo_pos] = utf8[i];
-			text->undo_pos++;
+			utxt->buf[utxt->pos] = utf8[i];
+			utxt->pos++;
 		}
-		
-		text->undo_buf[text->undo_pos] = op_start + utf8_size - 1;
+		/* 1 byte */
+		utxt->buf[utxt->pos] = op_start + utf8_size - 1;
 	}
 	else {
-		text->undo_buf[text->undo_pos] = op_start + 3;
-		text->undo_pos++;
-
-		txt_undo_store_cursors(text);
-
-		txt_undo_store_uint32(text->undo_buf, &text->undo_pos, c);
-		text->undo_buf[text->undo_pos] = op_start + 3;
+		if (!max_undo_test(utxt, 2 + 6 + 4 + 1)) {
+			return;
+		}
+		/* 2 bytes */
+		utxt->pos++;
+		utxt->buf[utxt->pos] = op_start + 3;
+		utxt->pos++;
+		/* 6 bytes */
+		txt_undo_store_cur(text, utxt);
+		/* 4 bytes */
+		txt_undo_store_uint32(utxt->buf, &utxt->pos, c);
+		/* 1 byte */
+		utxt->buf[utxt->pos] = op_start + 3;
 	}
 	
-	text->undo_buf[text->undo_pos + 1] = 0;
+	txt_undo_end(text, utxt);
 }
 
 /* extends Link */
@@ -1739,7 +1792,7 @@ struct LinkInt {
  * of the lines that should not be indented back.
  */
 static void txt_undo_add_unprefix_op(
-        Text *text, char undo_op,
+        Text *text, TextUndoBuf *utxt, char undo_op,
         const ListBase *line_index_mask, const int line_index_mask_len)
 {
 	struct LinkInt *idata;
@@ -1747,30 +1800,35 @@ static void txt_undo_add_unprefix_op(
 	BLI_assert(BLI_listbase_count(line_index_mask) == line_index_mask_len);
 
 	/* OP byte + UInt32 count + counted UInt32 line numbers + UInt32 count + 12-bytes selection + OP byte */
-	if (!max_undo_test(text, 1 + 4 + (line_index_mask_len * 4) + 4 + 12 + 1)) {
+	if (!max_undo_test(utxt, 2 + 4 + (line_index_mask_len * 4) + 4 + 12 + 1)) {
 		return;
 	}
 
-	/* Opening buffer sequence with OP */
-	text->undo_pos++;
-	text->undo_buf[text->undo_pos] = undo_op;
-	text->undo_pos++;
-	/* Adding number of line numbers to read */
-	txt_undo_store_uint32(text->undo_buf, &text->undo_pos, line_index_mask_len);
+	/* 2 bytes */
+	utxt->pos++;
+	utxt->buf[utxt->pos] = undo_op;
+	utxt->pos++;
+	/* Adding number of line numbers to read
+	 * 4 bytes */
+	txt_undo_store_uint32(utxt->buf, &utxt->pos, line_index_mask_len);
 
-	/* Adding linenumbers of lines that shall not be indented if undoing */
+	/* Adding linenumbers of lines that shall not be indented if undoing.
+	 * 'line_index_mask_len * 4' bytes */
 	for (idata = line_index_mask->first; idata; idata = idata->next) {
-		txt_undo_store_uint32(text->undo_buf, &text->undo_pos, idata->value);
-	}
-
-	/* Adding number of line numbers to read again */
-	txt_undo_store_uint32(text->undo_buf, &text->undo_pos, line_index_mask_len);
-	/* Adding current selection */
-	txt_undo_store_cursors(text);
-	/* Closing with OP (same as above) */
-	text->undo_buf[text->undo_pos] = undo_op;
+		txt_undo_store_uint32(utxt->buf, &utxt->pos, idata->value);
+	}
+
+	/* Adding number of line numbers to read again.
+	 * 4 bytes */
+	txt_undo_store_uint32(utxt->buf, &utxt->pos, line_index_mask_len);
+	/* Adding current selection.
+	 * 12 bytes */
+	txt_undo_store_cursors(text, utxt);
+	/* Closing with OP (same as above).
+	 * 1 byte */
+	utxt->buf[utxt->pos] = undo_op;
 	/* Marking as last undo operation */
-	text->undo_buf[text->undo_pos + 1] = 0;
+	txt_undo_end(text, utxt);
 }
 
 static unsigned short txt_undo_read_uint16(const char *undo_buf, int *undo_pos)
@@ -1913,7 +1971,7 @@ static unsigned int txt_redo_read_unicode(const char *undo_buf, int *undo_pos, s
 			unicode = BLI_str_utf8_as_unicode(utf8);
 			break;
 		case 4: /* 32-bit unicode symbol */
-			unicode = txt_undo_read_uint32(undo_buf, undo_pos);
+			unicode = txt_redo_read_uint32(undo_buf, undo_pos);
 			break;
 		default:
 			/* should never happen */
@@ -1925,9 +1983,9 @@ static unsigned int txt_redo_read_unicode(const char *undo_buf, int *undo_pos, s
 	return unicode;
 }
 
-void txt_do_undo(Text *text)
+void txt_do_undo(Text *text, TextUndoBuf *utxt)
 {
-	int op = text->undo_buf[text->undo_pos];
+	int op = utxt->buf[utxt->pos];
 	int prev_flags;
 	unsigned int linep;
 	unsigned int uni_char;
@@ -1936,11 +1994,11 @@ void txt_do_undo(Text *text)
 	unsigned short charp;
 	char *buf;
 	
-	if (text->undo_pos < 0) {
+	if (utxt->pos < 0) {
 		return;
 	}
 
-	text->undo_pos--;
+	utxt->pos--;
 
 	undoing = 1;
 	
@@ -1949,16 +2007,16 @@ void txt_do_undo(Text *text)
 		case UNDO_INSERT_2:
 		case UNDO_INSERT_3:
 		case UNDO_INSERT_4:
-			text->undo_pos -= op - UNDO_INSERT_1 + 1;
+			utxt->pos -= op - UNDO_INSERT_1 + 1;
 			
 			/* get and restore the cursors */
-			txt_undo_read_cur(text->undo_buf, &text->undo_pos, &curln, &curc);
+			txt_undo_read_cur(utxt->buf, &utxt->pos, &curln, &curc);
 			txt_move_to(text, curln, curc, 0);
 			txt_move_to(text, curln, curc, 1);
 			
-			txt_delete_char(text);
+			txt_delete_char(text, utxt);
 			
-			text->undo_pos--;
+			utxt->pos--;
 			break;
 
 		case UNDO_BS_1:
@@ -1966,16 +2024,16 @@ void txt_do_undo(Text *text)
 		case UNDO_BS_3:
 		case UNDO_BS_4:
 			charp = op - UNDO_BS_1 + 1;
-			uni_char = txt_undo_read_unicode(text->undo_buf, &text->undo_pos, charp);
+			uni_char = txt_undo_read_unicode(utxt->buf, &utxt->pos, charp);
 			
 			/* get and restore the cursors */
-			txt_undo_read_cur(text->undo_buf, &text->undo_pos, &curln, &curc);
+			txt_undo_read_cur(utxt->buf, &utxt->pos, &curln, &curc);
 			txt_move_to(text, curln, curc, 0);
 			txt_move_to(text, curln, curc, 1);
 			
-			txt_add_char(text, uni_char);
+			txt_add_char(text, utxt, uni_char);
 
-			text->undo_pos--;
+			utxt->pos--;
 			break;
 
 		case UNDO_DEL_1:
@@ -1983,50 +2041,50 @@ void txt_do_undo(Text *text)
 		case UNDO_DEL_3:
 		case UNDO_DEL_4:
 			charp = op - UNDO_DEL_1 + 1;
-			uni_char = txt_undo_read_unicode(text->undo_buf, &text->undo_pos, charp);
+			uni_char = txt_undo_read_unicode(utxt->buf, &utxt->pos, charp);
 
 			/* get and restore the cursors */
-			txt_undo_read_cur(text->undo_buf, &text->undo_pos, &curln, &curc);
+			txt_undo_read_cur(utxt->buf, &utxt->pos, &curln, &curc);
 			txt_move_to(text, curln, curc, 0);
 			txt_move_to(text, curln, curc, 1);
 
-			txt_add_char(text, uni_char);
+			txt_add_char(text, utxt, uni_char);
 
 			txt_move_left(text, 0);
 
-			text->undo_pos--;
+			utxt->pos--;
 			break;
 
 		case UNDO_DBLOCK:
 		{
 			int i;
 			/* length of the string in the buffer */
-			linep = txt_undo_read_uint32(text->undo_buf, &text->undo_pos);
+			linep = txt_undo_read_uint32(utxt->buf, &utxt->pos);
 
 			buf = MEM_mallocN(linep + 1, "dblock buffer");
 			for (i = 0; i < linep; i++) {
-				buf[(linep - 1) - i] = text->undo_buf[text->undo_pos];
-				text->undo_pos--;
+				buf[(linep - 1) - i] = utxt->buf[utxt->pos];
+				utxt->pos--;
 			}
 			buf[i] = 0;
 
 			/* skip over the length that was stored again */
-			text->undo_pos -= 4;
+			utxt->pos -= 4;
 
 			/* Get the cursor positions */
-			txt_undo_read_cursors(text->undo_buf, &text->undo_pos, &curln, &curc, &selln, &selc);
+			txt_undo_read_cursors(utxt->buf, &utxt->pos, &curln, &curc, &selln, &selc);
 
 			/* move cur to location that needs buff inserted */
 			txt_move_to(text, curln, curc, 0);
 			
-			txt_insert_buf(text, buf);
+			txt_insert_buf(text, utxt, buf);
 			MEM_freeN(buf);
 
 			/* restore the cursors */
 			txt_move_to(text, curln, curc, 0);
 			txt_move_to(text, selln, selc, 1);
 
-			text->undo_pos--;
+			utxt->pos--;
 			
 			break;
 		}
@@ -2034,23 +2092,23 @@ void txt_do_undo(Text *text)
 		{
 			int i;
 			/* length of the string in the buffer */
-			linep = txt_undo_read_uint32(text->undo_buf, &text->undo_pos);
+			linep = txt_undo_read_uint32(utxt->buf, &utxt->pos);
 			
 			/* txt_backspace_char removes utf8-characters, not bytes */
 			buf = MEM_mallocN(linep + 1, "iblock buffer");
 			for (i = 0; i < linep; i++) {
-				buf[(linep - 1) - i] = text->undo_buf[text->undo_pos];
-				text->undo_pos--;
+				buf[(linep - 1) - i] = utxt->buf[utxt->pos];
+				utxt->pos--;
 			}
 			buf[i] = 0;
 			linep = BLI_strlen_utf8(buf);
 			MEM_freeN(buf);
 			
 			/* skip over the length that was stored again */
-			text->undo_pos -= 4;
+			utxt->pos -= 4;
 
 			/* get and restore the cursors */
-			txt_undo_read_cursors(text->undo_buf, &text->undo_pos, &curln, &curc, &selln, &selc);
+			txt_undo_read_cursors(utxt->buf, &utxt->pos, &curln, &curc, &selln, &selc);
 			
 			txt_move_to(text, curln, curc, 0);
 			txt_move_to(text, selln, selc, 1);
@@ -2066,9 +2124,9 @@ void txt_do_undo(Text *text)
 				text->flags = prev_flags;
 			}
 			
-			txt_delete_selected(text);
+			txt_delete_selected(text, utxt);
 			
-			text->undo_pos--;
+			utxt->pos--;
 			break;
 		}
 		case UNDO_INDENT:
@@ -2077,37 +2135,37 @@ void txt_do_undo(Text *text)
 		case UNDO_MOVE_LINES_UP:
 		case UNDO_MOVE_LINES_DOWN:
 			/* get and restore the cursors */
-			txt_undo_read_cursors(text->undo_buf, &text->undo_pos, &curln, &curc, &selln, &selc);
+			txt_undo_read_cursors(utxt->buf, &utxt->pos, &curln, &curc, &selln, &selc);
 			txt_move_to(text, curln, curc, 0);
 			txt_move_to(text, selln, selc, 1);
 			
 			if (op == UNDO_INDENT) {
-				txt_unindent(text);
+				txt_unindent(text, utxt);
 			}
 			else if (op == UNDO_COMMENT) {
-				txt_uncomment(text);
+				txt_uncomment(text, utxt);
 			}
 			else if (op == UNDO_DUPLICATE) {
 				txt_delete_line(text, text->curl->next);
 			}
 			else if (op == UNDO_MOVE_LINES_UP) {
-				txt_move_lines(text, TXT_MOVE_LINE_DOWN);
+				txt_move_lines(text, utxt, TXT_MOVE_LINE_DOWN);
 			}
 			else if (op == UNDO_MOVE_LINES_DOWN) {
-				txt_move_lines(text, TXT_MOVE_LINE_UP);
+				txt_move_lines(text, utxt, TXT_MOVE_LINE_UP);
 			}
 			
-			text->undo_pos--;
+			utxt->pos--;
 			break;
 		case UNDO_UNINDENT:
 		case UNDO_UNCOMMENT:
 		{
-			void (*txt_prefix_fn)(Text *);
-			void (*txt_unprefix_fn)(Text *);
+			void (*txt_prefix_fn)(Text *, TextUndoBuf *);
+			void (*txt_unprefix_fn)(Text *, TextUndoBuf *);
 			int count;
 			int i;
 			/* Get and restore the cursors */
-			txt_undo_read_cursors(text->undo_buf, &text->undo_pos, &curln, &curc, &selln, &selc);
+			txt_undo_read_cursors(utxt->buf, &utxt->pos, &curln, &curc, &selln, &selc);
 			txt_move_to(text, curln, curc, 0);
 			txt_move_to(text, selln, selc, 1);
 
@@ -2121,30 +2179,30 @@ void txt_do_undo(Text *text)
 				txt_unprefix_fn = txt_uncomment;
 			}
 
-			txt_prefix_fn(text);
+			txt_prefix_fn(text, utxt);
 
 			/* Get the count */
-			count = txt_undo_read_uint32(text->undo_buf, &text->undo_pos);
+			count = txt_undo_read_uint32(utxt->buf, &utxt->pos);
 			/* Iterate! */
 			txt_pop_sel(text);
 
 			for (i = 0; i < count; i++) {
-				txt_move_to(text, txt_undo_read_uint32(text->undo_buf, &text->undo_pos), 0, 0);
+				txt_move_to(text, txt_undo_read_uint32(utxt->buf, &utxt->pos), 0, 0);
 				/* Un-un-unindent/comment */
-				txt_unprefix_fn(text);
+				txt_unprefix_fn(text, utxt);
 			}
 			/* Restore selection */
 			txt_move_to(text, curln, curc, 0);
 			txt_move_to(text, selln, selc, 1);
 			/* Jumo over count */
-			txt_undo_read_uint32(text->undo_buf, &text->undo_pos);
+			txt_undo_read_uint32(utxt->buf, &utxt->pos);
 			/* Jump over closing OP byte */
-			text->undo_pos--;
+			utxt->pos--;
 			break;
 		}
 		default:
 			//XXX error("Undo buffer error - resetting");
-			text->undo_pos = -1;
+			utxt->pos = -1;
 			
 			break;
 	}
@@ -2152,7 +2210,7 @@ void txt_do_undo(Text *text)
 	undoing = 0;
 }
 
-void txt_do_redo(Text *text)
+void txt_do_redo(Text *text, TextUndoBuf *utxt)
 {
 	char op;
 	char *buf;
@@ -2162,11 +2220,11 @@ void txt_do_redo(Text *text)
 	unsigned int curln, selln;
 	unsigned short curc, selc;
 	
-	text->undo_pos++;
-	op = text->undo_buf[text->undo_pos];
+	utxt->pos++;
+	op = utxt->buf[utxt->pos];
 	
 	if (!op) {
-		text->undo_pos--;
+		utxt->pos--;
 		return;
 	}
 	
@@ -2177,35 +2235,35 @@ void txt_do_redo(Text *text)
 		case UNDO_INSERT_2:
 		case UNDO_INSERT_3:
 		case UNDO_INSERT_4:
-			text->undo_pos++;
+			utxt->pos++;
 			
 			/* get and restore the cursors */
-			txt_redo_read_cur(text->undo_buf, &text->undo_pos, &curln, &curc);
+			txt_redo_read_cur(utxt->buf, &utxt->pos, &curln, &curc);
 			txt_move_to(text, curln, curc, 0);
 			txt_move_to(text, curln, curc, 1);
 			
 			charp = op - UNDO_INSERT_1 + 1;
-			uni_uchar = txt_redo_read_unicode(text->undo_buf, &text->undo_pos, charp);
+			uni_uchar = txt_redo_read_unicode(utxt->buf, &utxt->pos, charp);
 
-			txt_add_char(text, uni_uchar);
+			txt_add_char(text, utxt, uni_uchar);
 			break;
 
 		case UNDO_BS_1:
 		case UNDO_BS_2:
 		case UNDO_BS_3:
 		case UNDO_BS_4:
-			text->undo_pos++;
+			utxt->pos++;
 
 			/* get and restore the cursors */
-			txt_redo_read_cur(text->undo_buf, &text->undo_pos, &curln, &curc);
+			txt_redo_read_cur(utxt->buf, &utxt->pos, &curln, &curc);
 			txt_move_to(text, curln, curc, 0);
 			txt_move_to(text, curln, curc, 1);
 
-			text->undo_pos += op - UNDO_BS_1 + 1;
+			utxt->pos += op - UNDO_BS_1 + 1;
 			
 			/* move right so we backspace the correct char */
 			txt_move_right(text, 0);
-			txt_backspace_char(text);
+			txt_backspace_char(text, utxt);
 
 			break;
 
@@ -2213,60 +2271,60 @@ void txt_do_redo(Text *text)
 		case UNDO_DEL_2:
 		case UNDO_DEL_3:
 		case UNDO_DEL_4:
-			text->undo_pos++;
+			utxt->pos++;
 
 			/* get and restore the cursors */
-			txt_redo_read_cur(text->undo_buf, &text->undo_pos, &curln, &curc);
+			txt_redo_read_cur(utxt->buf, &utxt->pos, &curln, &curc);
 			txt_move_to(text, curln, curc, 0);
 			txt_move_to(text, curln, curc, 1);
 			
-			text->undo_pos += op - UNDO_DEL_1 + 1;
+			utxt->pos += op - UNDO_DEL_1 + 1;
 
-			txt_delete_char(text);
+			txt_delete_char(text, utxt);
 
 			break;
 
 		case UNDO_DBLOCK:
-			text->undo_pos++;
+			utxt->pos++;
 
 			/* get and restore the cursors */
-			txt_redo_read_cursors(text->undo_buf, &text->undo_pos, &curln, &curc, &selln, &selc);
+			txt_redo_read_cursors(utxt->buf, &utxt->pos, &curln, &curc, &selln, &selc);
 			txt_move_to(text, curln, curc, 0);
 			txt_move_to(text, selln, selc, 1);
 
 			/* length of the block */
-			linep = txt_redo_read_uint32(text->undo_buf, &text->undo_pos);
+			linep = txt_redo_read_uint32(utxt->buf, &utxt->pos);
 			
-			text->undo_pos += linep;
+			utxt->pos += linep;
 
 			/* skip over the length that was stored again */
-			text->undo_pos += 4;
+			utxt->pos += 4;
 			
-			txt_delete_sel(text);
+			txt_delete_sel(text, utxt);
 
 			break;
 
 		case UNDO_IBLOCK:
-			text->undo_pos++;
+			utxt->pos++;
 
 			/* get and restore the cursors */
-			txt_redo_read_cursors(text->undo_buf, &text->undo_pos, &curln, &curc, &selln, &selc);
+			txt_redo_read_cursors(utxt->buf, &utxt->pos, &curln, &curc, &selln, &selc);
 			txt_move_to(text, curln, curc, 0);
 			txt_move_to(text, curln, curc, 1);
 
 			/* length of the block */
-			linep = txt_redo_read_uint32(text->undo_buf, &text->undo_pos);
+			linep = txt_redo_read_uint32(utxt->buf, &utxt->pos);
 
 			buf = MEM_mallocN(linep + 1, "iblock buffer");
-			memcpy(buf, &text->undo_buf[text->undo_pos], linep);
-			text->undo_pos += linep;
+			memcpy(buf, &utxt->buf[utxt->pos], linep);
+			utxt->pos += linep;
 			buf[linep] = 0;
 			
-			txt_insert_buf(text, buf);
+			txt_insert_buf(text, utxt, buf);
 			MEM_freeN(buf);
 
 			/* skip over the length that was stored again */
-			text->undo_pos += 4;
+			utxt->pos += 4;
 
 			break;
 			
@@ -2276,38 +2334,38 @@ void txt_do_redo(Text *text)
 		case UNDO_DUPLICATE:
 		case UNDO_MOVE_LINES_UP:
 		case UNDO_MOVE_LINES_DOWN:
-			text->undo_pos++;
+			utxt->pos++;
 
 			/* get and restore the cursors */
-			txt_redo_read_cursors(text->undo_buf, &text->undo_pos, &curln, &curc, &selln, &selc);
+			txt_redo_read_cursors(utxt->buf, &utxt->pos, &curln, &curc, &selln, &selc);
 			txt_move_to(text, curln, curc, 0);
 			txt_move_to(text, selln, selc, 1);
 
 			if (op == UNDO_INDENT) {
-				txt_indent(text);
+				txt_indent(text, utxt);
 			}
 			else if (op == UNDO_COMMENT) {
-				txt_comment(text);
+				txt_comment(text, utxt);
 			}
 			else if (op == UNDO_UNCOMMENT) {
-				txt_uncomment(text);
+				txt_uncomment(text, utxt);
 			}
 			else if (op == UNDO_DUPLICATE) {
-				txt_duplicate_line(text);
+				txt_duplicate_line(text, utxt);
 			}
 			else if (op == UNDO_MOVE_LINES_UP) {
 				/* offset the cursor by + 1 */
 				txt_move_to(text, curln + 1, curc, 0);
 				txt_move_to(text, selln + 1, selc, 1);
 
-				txt_move_lines(text, TXT_MOVE_LINE_UP);
+				txt_move_lines(text, utxt, TXT_MOVE_LINE_UP);
 			}
 			else if (op == UNDO_MOVE_LINES_DOWN) {
 				/* offset the cursor by - 1 */
 				txt_move_to(text, curln - 1, curc, 0);
 				txt_move_to(text, selln - 1, selc, 1);
 
-				txt_move_lines(text, TXT_MOVE_LINE_DOWN);
+				txt_move_lines(text, utxt, TXT_MOVE_LINE_DOWN);
 			}
 
 			/* re-restore the cursors since they got moved when redoing */
@@ -2320,24 +2378,24 @@ void txt_do_redo(Text *text)
 			int count;
 			int i;
 
-			text->undo_pos++;
+			utxt->pos++;
 			/* Scan all the stuff described in txt_undo_add_unindent_op */
-			count = txt_redo_read_uint32(text->undo_buf, &text->undo_pos);
+			count = txt_redo_read_uint32(utxt->buf, &utxt->pos);
 			for (i = 0; i < count; i++) {
-				txt_redo_read_uint32(text->undo_buf, &text->undo_pos);
+				txt_redo_read_uint32(utxt->buf, &utxt->pos);
 			}
 			/* Count again */
-			txt_redo_read_uint32(text->undo_buf, &text->undo_pos);
+			txt_redo_read_uint32(utxt->buf, &utxt->pos);
 			/* Get the selection and re-unindent */
-			txt_redo_read_cursors(text->undo_buf, &text->undo_pos, &curln, &curc, &selln, &selc);
+			txt_redo_read_cursors(utxt->buf, &utxt->pos, &curln, &curc, &selln, &selc);
 			txt_move_to(text, curln, curc, 0);
 			txt_move_to(text, selln, selc, 1);
-			txt_unindent(text);
+			txt_unindent(text, utxt);
 			break;
 		}
 		default:
 			//XXX error("Undo buffer error - resetting");
-			text->undo_pos = -1;
+			utxt->pos = -1;
 			
 			break;
 	}
@@ -2349,16 +2407,16 @@ void txt_do_redo(Text *text)
 /* Line editing functions */ 
 /**************************/
 
-void txt_split_curline(Text *text)
+void txt_split_curline(Text *text, TextUndoBuf *utxt)
 {
 	TextLine *ins;
 	char *left, *right;
 
 	if (!text->curl) return;
 
-	txt_delete_sel(text);
+	txt_delete_sel(text, utxt);
 
-	if (!undoing) txt_undo_add_charop(text, UNDO_INSERT_1, '\n');
+	if (!undoing) txt_undo_add_charop(text, utxt, UNDO_INSERT_1, '\n');
 	
 	/* Make the two half strings */
 
@@ -2430,7 +2488,7 @@ static void txt_combine_lines(Text *text, TextLine *linea, TextLine *lineb)
 	txt_clean_text(text);
 }
 
-void txt_duplicate_line(Text *text)
+void txt_duplicate_line(Text *text, TextUndoBuf *utxt)
 {
 	TextLine *textline;
 	
@@ -2443,18 +2501,18 @@ void txt_duplicate_line(Text *text)
 		txt_make_dirty(text);
 		txt_clean_text(text);
 		
-		if (!undoing) txt_undo_add_op(text, UNDO_DUPLICATE);
+		if (!undoing) txt_undo_add_op(text, utxt, UNDO_DUPLICATE);
 	}
 }
 
-void txt_delete_char(Text *text) 
+void txt_delete_char(Text *text, TextUndoBuf *utxt)
 {
 	unsigned int c = '\n';
 
 	if (!text->curl) return;
 
 	if (txt_has_sel(text)) { /* deleting a selection */
-		txt_delete_sel(text);
+		txt_delete_sel(text, utxt);
 		txt_make_dirty(text);
 		return;
 	}
@@ -2480,24 +2538,24 @@ void txt_delete_char(Text *text)
 	txt_make_dirty(text);
 	txt_clean_text(text);
 	
-	if (!undoing) txt_undo_add_charop(text, UNDO_DEL_1, c);
+	if (!undoing) txt_undo_add_charop(text, utxt, UNDO_DEL_1, c);
 }
 
-void txt_delete_word(Text *text)
+void txt_delete_word(Text *text, TextUndoBuf *utxt)
 {
 	txt_jump_right(text, true, true);
-	txt_delete_sel(text);
+	txt_delete_sel(text, utxt);
 	txt_make_dirty(text);
 }
 
-void txt_backspace_char(Text *text)
+void txt_backspace_char(Text *text, TextUndoBuf *utxt)
 {
 	unsigned int c = '\n';
 	
 	if (!text->curl) return;
 	
 	if (txt_has_sel(text)) { /* deleting a selection */
-		txt_delete_sel(text);
+		txt_delete_sel(text, utxt);
 		txt_make_dirty(text);
 		return;
 	}
@@ -2529,13 +2587,13 @@ void txt_backspace_char(Text *text)
 	txt_make_dirty(text);
 	txt_clean_text(text);
 	
-	if (!undoing) txt_undo_add_charop(text, UNDO_BS_1, c);
+	if (!undoing) txt_undo_add_charop(text, utxt, UNDO_BS_1, c);
 }
 
-void txt_backspace_word(Text *text)
+void txt_backspace_word(Text *text, TextUndoBuf *utxt)
 {
 	txt_jump_left(text, true, true);
-	txt_delete_sel(text);
+	txt_delete_sel(text, utxt);
 	txt_make_dirty(text);
 }
 
@@ -2544,17 +2602,17 @@ void txt_backspace_word(Text *text)
  * Remember to change this string according to max tab size */
 static char tab_to_spaces[] = "    ";
 
-static void txt_convert_tab_to_spaces(Text *text)
+static void txt_convert_tab_to_spaces(Text *text, TextUndoBuf *utxt)
 {
 	/* sb aims to pad adjust the tab-width needed so that the right number of spaces
 	 * is added so that the indention of the line is the right width (i.e. aligned
 	 * to multiples of TXT_TABSIZE)
 	 */
 	const char *sb = &tab_to_spaces[text->curc % TXT_TABSIZE];
-	txt_insert_buf(text, sb);
+	txt_insert_buf(text, utxt, sb);
 }
 
-static bool txt_add_char_intern(Text *text, unsigned int add, bool replace_tabs)
+static bool txt_add_char_intern(Text *text, TextUndoBuf *utxt, unsigned int add, bool replace_tabs)
 {
 	char *tmp, ch[BLI_UTF8_MAX];
 	size_t add_len;
@@ -2562,19 +2620,19 @@ static bool txt_add_char_intern(Text *text, unsigned int add, bool replace_tabs)
 	if (!text->curl) return 0;
 
 	if (add == '\n') {
-		txt_split_curline(text);
+		txt_split_curline(text, utxt);
 		return true;
 	}
 	
 	/* insert spaces rather than tabs */
 	if (add == '\t' && replace_tabs) {
-		txt_convert_tab_to_spaces(text);
+		txt_convert_tab_to_spaces(text, utxt);
 		return true;
 	}
 
-	txt_delete_sel(text);
+	txt_delete_sel(text, utxt);
 	
-	if (!undoing) txt_undo_add_charop(text, UNDO_INSERT_1, add);
+	if (!undoing) txt_undo_add_charop(text, utxt, UNDO_INSERT_1, add);
 
 	add_len = BLI_str_utf8_from_unicode(add, ch);
 	
@@ -2596,23 +2654,23 @@ static bool txt_add_char_intern(Text *text, unsigned int add, bool replace_tabs)
 	return 1;
 }
 
-bool txt_add_char(Text *text, unsigned int add)
+bool txt_add_char(Text *text, TextUndoBuf *utxt, unsigned int add)
 {
-	return txt_add_char_intern(text, add, (text->flags & TXT_TABSTOSPACES) != 0);
+	return txt_add_char_intern(text, utxt, add, (text->flags & TXT_TABSTOSPACES) != 0);
 }
 
-bool txt_add_raw_char(Text *text, unsigned int add)
+bool txt_add_raw_char(Text *text, TextUndoBuf *utxt, unsigned int add)
 {
-	return txt_add_char_intern(text, add, 0);
+	return txt_add_char_intern(text, utxt, add, 0);
 }
 
-void txt_delete_selected(Text *text)
+void txt_delete_selected(Text *text, TextUndoBuf *utxt)
 {
-	txt_delete_sel(text);
+	txt_delete_sel(text, utxt);
 	txt_make_dirty(text);
 }
 
-bool txt_replace_char(Text *text, unsigned int add)
+bool txt_replace_char(Text *text, TextUndoBuf *utxt, unsigned int add)
 {
 	unsigned int del;
 	size_t del_size = 0, add_size;
@@ -2622,7 +2680,7 @@ bool txt_replace_char(Text *text, unsigned int add)
 
 	/* If text is selected or we're at the end of the line just use txt_add_char */
 	if (text->curc == text->curl->len || txt_has_sel(text) || add == '\n') {
-		return txt_add_char(text, add);
+		return txt_add_char(text, utxt, add);
 	}
 	
 	del = BLI_str_utf8_as_unicode_and_size(text->curl->line + text->curc, &del_size);
@@ -2650,10 +2708,10 @@ bool txt_replace_char(Text *text, unsigned int add)
 
 	/* Should probably create a new op for this */
 	if (!undoing) {
-		txt_undo_add_charop(text, UNDO_INSERT_1, add);
+		txt_undo_add_charop(text, utxt, UNDO_INSERT_1, add);
 		text->curc -= add_size;
 		txt_pop_sel(text);
-		txt_undo_add_charop(text, UNDO_DEL_1, del);
+		txt_undo_add_charop(text, utxt, UNDO_DEL_1, del);
 		text->curc += add_size;
 		txt_pop_sel(text);
 	}
@@ -2793,7 +2851,7 @@ static void txt_select_unprefix(
 	/* caller must handle undo */
 }
 
-void txt_comment(Text *text)
+void txt_comment(Text *text, TextUndoBuf *utxt)
 {
 	const char *prefix = "#";
 
@@ -2804,11 +2862,11 @@ void txt_comment(Text *text)
 	txt_select_prefix(text, prefix);
 
 	if (!undoing) {
-		txt_undo_add_op(text, UNDO_COMMENT);
+		txt_undo_add_op(text, utxt, UNDO_COMMENT);
 	}
 }
 
-void txt_uncomment(Text *text)
+void txt_uncomment(Text *text, TextUndoBuf *utxt)
 {
 	const char *prefix = "#";
 	ListBase line_index_mask;
@@ -2821,13 +2879,13 @@ void txt_uncomment(Text *text)
 	txt_select_unprefix(text, prefix, &line_index_mask, &line_index_mask_len);
 
 	if (!undoing) {
-		txt_undo_add_unprefix_op(text, UNDO_UNCOMMENT, &line_index_mask, line_index_mask_len);
+		txt_undo_add_unprefix_op(text, utxt, UNDO_UNCOMMENT, &line_index_mask, line_index_mask_len);
 	}
 
 	BLI_freelistN(&line_index_mask);
 }
 
-void txt_indent(Text *text)
+void txt_indent(Text *text, TextUndoBuf *utxt)
 {
 	const char *prefix = (text->flags & TXT_TABSTOSPACES) ? tab_to_spaces : "\t";
 
@@ -2838,11 +2896,11 @@ void txt_indent(Text *text)
 	txt_select_prefix(text, prefix);
 
 	if (!undoing) {
-		txt_undo_add_op(text, UNDO_INDENT);
+		txt_undo_add_op(text, utxt, UNDO_INDENT);
 	}
 }
 
-void txt_unindent(Text *text)
+void txt_unindent(Text *text, TextUndoBuf *utxt)
 {
 	const char *prefix = (text->flags & TXT_TABSTOSPACES) ? tab_to_spaces : "\t";
 	ListBase line_index_mask;
@@ -2855,13 +2913,13 @@ void txt_unindent(Text *text)
 	txt_select_unprefix(text, prefix, &line_index_mask, &line_index_mask_len);
 
 	if (!undoing) {
-		txt_undo_add_unprefix_op(text, UNDO_UNINDENT, &line_index_mask, line_index_mask_len);
+		txt_undo_add_unprefix_op(text, utxt, UNDO_UNINDENT, &line_index_mask, line_index_mask_len);
 	}
 
 	BLI_freelistN(&line_index_mask);
 }
 
-void txt_move_lines(struct Text *text, const int direction)
+void txt_move_lines(struct Text *text, TextUndoBuf *utxt, const int direction)
 {
 	TextLine *line_other;
 
@@ -2888,7 +2946,7 @@ void txt_move_lines(struct Text *text, const int direction)
 	txt_clean_text(text);
 	
 	if (!undoing) {
-		txt_undo_add_op(text, (direction == TXT_MOVE_LINE_DOWN) ? UNDO_MOVE_LINES_DOWN : UNDO_MOVE_LINES_UP);
+		txt_undo_add_op(text, utxt, (direction == TXT_MOVE_LINE_DOWN) ? UNDO_MOVE_LINES_DOWN : UNDO_MOVE_LINES_UP);
 	}
 }
 
diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c
index 0e2ac811a41def3e4322129424774c2f760004a8..250408642bb011de3b127014e8fbab2be1cfb76c 100644
--- a/source/blender/blenkernel/intern/texture.c
+++ b/source/blender/blenkernel/intern/texture.c
@@ -55,7 +55,6 @@
 
 #include "IMB_imbuf.h"
 
-#include "BKE_global.h"
 #include "BKE_main.h"
 
 #include "BKE_colorband.h"
diff --git a/source/blender/blenkernel/intern/undo_system.c b/source/blender/blenkernel/intern/undo_system.c
new file mode 100644
index 0000000000000000000000000000000000000000..760c6a609761c6c65b596f103c9bed4037693503
--- /dev/null
+++ b/source/blender/blenkernel/intern/undo_system.c
@@ -0,0 +1,831 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/blenkernel/intern/undo_system.c
+ *  \ingroup bke
+ *
+ * Used by ED_undo.h, internal implementation.
+ */
+
+#include <string.h>
+
+#include "CLG_log.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_sys_types.h"
+#include "BLI_listbase.h"
+#include "BLI_string.h"
+#include "BLI_sort_utils.h"
+
+#include "DNA_listBase.h"
+#include "DNA_windowmanager_types.h"
+
+#include "BKE_context.h"
+#include "BKE_global.h"
+#include "BKE_main.h"
+#include "BKE_undo_system.h"
+
+#include "MEM_guardedalloc.h"
+
+#define undo_stack _wm_undo_stack_disallow  /* pass in as a variable always. */
+
+/** Odd requirement of Blender that we always keep a memfile undo in the stack. */
+#define WITH_GLOBAL_UNDO_KEEP_ONE
+
+/** Make sure all ID's created at the point we add an undo step that uses ID's. */
+#define WITH_GLOBAL_UNDO_ENSURE_UPDATED
+
+/** We only need this locally. */
+static CLG_LogRef LOG = {"bke.undosys"};
+
+/* -------------------------------------------------------------------- */
+/** \name Internal Nested Undo Checks
+ *
+ * Make sure we're not running undo operations from 'step_encode', 'step_decode' callbacks.
+ * bugs caused by this situation aren't _that_ hard to spot but aren't always so obvious.
+ * Best we have a check which shows the problem immediately.
+ *
+ * \{ */
+#define WITH_NESTED_UNDO_CHECK
+
+#ifdef WITH_NESTED_UNDO_CHECK
+static bool g_undo_callback_running = false;
+#  define UNDO_NESTED_ASSERT(state) BLI_assert(g_undo_callback_running == state)
+#  define UNDO_NESTED_CHECK_BEGIN { \
+	UNDO_NESTED_ASSERT(false); \
+	g_undo_callback_running = true; \
+} ((void)0)
+#  define UNDO_NESTED_CHECK_END { \
+	UNDO_NESTED_ASSERT(true); \
+	g_undo_callback_running = false; \
+} ((void)0)
+#else
+#  define UNDO_NESTED_ASSERT(state) ((void)0)
+#  define UNDO_NESTED_CHECK_BEGIN ((void)0)
+#  define UNDO_NESTED_CHECK_END   ((void)0)
+#endif
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Public Undo Types
+ *
+ * Unfortunately we need this for a handful of places.
+ */
+const UndoType *BKE_UNDOSYS_TYPE_IMAGE = NULL;
+const UndoType *BKE_UNDOSYS_TYPE_MEMFILE = NULL;
+const UndoType *BKE_UNDOSYS_TYPE_PAINTCURVE = NULL;
+const UndoType *BKE_UNDOSYS_TYPE_PARTICLE = NULL;
+const UndoType *BKE_UNDOSYS_TYPE_SCULPT = NULL;
+const UndoType *BKE_UNDOSYS_TYPE_TEXT = NULL;
+/** \} */
+
+/* UndoType */
+
+static ListBase g_undo_types = {NULL, NULL};
+
+static const UndoType *BKE_undosys_type_from_context(bContext *C)
+{
+	for (const UndoType *ut = g_undo_types.first; ut; ut = ut->next) {
+		/* No poll means we don't check context. */
+		if (ut->poll && ut->poll(C)) {
+			return ut;
+		}
+	}
+	return NULL;
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Internal Callback Wrappers
+ *
+ * #UndoRefID is simply a way to avoid inlining name copy and lookups,
+ * since it's easy to forget a single case when done inline (crashing in some cases).
+ *
+ * \{ */
+
+static void undosys_id_ref_store(void *UNUSED(user_data), UndoRefID *id_ref)
+{
+	BLI_assert(id_ref->name[0] == '\0');
+	if (id_ref->ptr) {
+		BLI_strncpy(id_ref->name, id_ref->ptr->name, sizeof(id_ref->name));
+		/* Not needed, just prevents stale data access. */
+		id_ref->ptr = NULL;
+	}
+}
+
+static void undosys_id_ref_resolve(void *user_data, UndoRefID *id_ref)
+{
+	/* Note: we could optimize this, for now it's not too bad since it only runs when we access undo! */
+	Main *bmain = user_data;
+	ListBase *lb = which_libbase(bmain, GS(id_ref->name));
+	for (ID *id = lb->first; id; id = id->next) {
+		if (STREQ(id_ref->name, id->name) && (id->lib == NULL)) {
+			id_ref->ptr = id;
+			break;
+		}
+	}
+}
+
+static bool undosys_step_encode(bContext *C, UndoStep *us)
+{
+	CLOG_INFO(&LOG, 2, "addr=%p, name='%s', type='%s'", us, us->name, us->type->name);
+	UNDO_NESTED_CHECK_BEGIN;
+	bool ok = us->type->step_encode(C, us);
+	UNDO_NESTED_CHECK_END;
+	if (ok) {
+		if (us->type->step_foreach_ID_ref != NULL) {
+			/* Don't use from context yet because sometimes context is fake and not all members are filled in. */
+			Main *bmain = G.main;
+			us->type->step_foreach_ID_ref(us, undosys_id_ref_store, bmain);
+		}
+	}
+	if (ok == false) {
+		CLOG_INFO(&LOG, 2, "encode callback didn't create undo step");
+	}
+	return ok;
+}
+
+static void undosys_step_decode(bContext *C, UndoStep *us, int dir)
+{
+	CLOG_INFO(&LOG, 2, "addr=%p, name='%s', type='%s'", us, us->name, us->type->name);
+	if (us->type->step_foreach_ID_ref) {
+		/* Don't use from context yet because sometimes context is fake and not all members are filled in. */
+		Main *bmain = G.main;
+		us->type->step_foreach_ID_ref(us, undosys_id_ref_resolve, bmain);
+	}
+
+	UNDO_NESTED_CHECK_BEGIN;
+	us->type->step_decode(C, us, dir);
+	UNDO_NESTED_CHECK_END;
+}
+
+static void undosys_step_free_and_unlink(UndoStack *ustack, UndoStep *us)
+{
+	CLOG_INFO(&LOG, 2, "addr=%p, name='%s', type='%s'", us, us->name, us->type->name);
+	UNDO_NESTED_CHECK_BEGIN;
+	us->type->step_free(us);
+	UNDO_NESTED_CHECK_END;
+
+	BLI_remlink(&ustack->steps, us);
+	MEM_freeN(us);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Undo Stack
+ * \{ */
+
+#ifndef NDEBUG
+static void undosys_stack_validate(UndoStack *ustack, bool expect_non_empty)
+{
+	if (ustack->step_active != NULL) {
+		BLI_assert(!BLI_listbase_is_empty(&ustack->steps));
+		BLI_assert(BLI_findindex(&ustack->steps, ustack->step_active) != -1);
+	}
+	if (expect_non_empty) {
+		BLI_assert(!BLI_listbase_is_empty(&ustack->steps));
+	}
+}
+#else
+static void undosys_stack_validate(UndoStack *ustack, bool expect_non_empty)
+{
+	UNUSED_VARS(ustack, expect_non_empty);
+}
+#endif
+
+UndoStack *BKE_undosys_stack_create(void)
+{
+	UndoStack *ustack = MEM_callocN(sizeof(UndoStack), __func__);
+	return ustack;
+}
+
+void BKE_undosys_stack_destroy(UndoStack *ustack)
+{
+	BKE_undosys_stack_clear(ustack);
+	MEM_freeN(ustack);
+}
+
+void BKE_undosys_stack_clear(UndoStack *ustack)
+{
+	UNDO_NESTED_ASSERT(false);
+	CLOG_INFO(&LOG, 1, "steps=%d", BLI_listbase_count(&ustack->steps));
+	for (UndoStep *us = ustack->steps.last, *us_prev; us; us = us_prev) {
+		us_prev = us->prev;
+		undosys_step_free_and_unlink(ustack, us);
+	}
+	BLI_listbase_clear(&ustack->steps);
+	ustack->step_active = NULL;
+}
+
+static bool undosys_stack_push_main(UndoStack *ustack, const char *name, struct Main *bmain)
+{
+	UNDO_NESTED_ASSERT(false);
+	CLOG_INFO(&LOG, 1, "'%s'", name);
+	bContext *C_temp = CTX_create();
+	CTX_data_main_set(C_temp, bmain);
+	bool ok = BKE_undosys_step_push_with_type(ustack, C_temp, name, BKE_UNDOSYS_TYPE_MEMFILE);
+	CTX_free(C_temp);
+	return ok;
+}
+
+void BKE_undosys_stack_init_from_main(UndoStack *ustack, struct Main *bmain)
+{
+	UNDO_NESTED_ASSERT(false);
+	undosys_stack_push_main(ustack, "original", bmain);
+}
+
+/* name optional */
+bool BKE_undosys_stack_has_undo(UndoStack *ustack, const char *name)
+{
+	if (name) {
+		UndoStep *us = BLI_rfindstring(&ustack->steps, name, offsetof(UndoStep, name));
+		return us && us->prev;
+	}
+
+	return !BLI_listbase_is_empty(&ustack->steps);
+}
+
+UndoStep *BKE_undosys_stack_active_with_type(UndoStack *ustack, const UndoType *ut)
+{
+	UndoStep *us = ustack->step_active;
+	while (us && (us->type != ut)) {
+		us = us->prev;
+	}
+	return us;
+}
+
+UndoStep *BKE_undosys_stack_init_or_active_with_type(UndoStack *ustack, const UndoType *ut)
+{
+	UNDO_NESTED_ASSERT(false);
+	CLOG_INFO(&LOG, 1, "type='%s'", ut->name);
+	if (ustack->step_init && (ustack->step_init->type == ut)) {
+		return ustack->step_init;
+	}
+	return BKE_undosys_stack_active_with_type(ustack, ut);
+}
+
+/**
+ * \param steps: Limit the number of undo steps.
+ * \param memory_limit: Limit the amount of memory used by the undo stack.
+ */
+void BKE_undosys_stack_limit_steps_and_memory(UndoStack *ustack, int steps, size_t memory_limit)
+{
+	UNDO_NESTED_ASSERT(false);
+	if (!(steps || memory_limit)) {
+		return;
+	}
+
+	CLOG_INFO(&LOG, 1, "steps=%d, memory_limit=%zu", steps, memory_limit);
+	UndoStep *us;
+#ifdef WITH_GLOBAL_UNDO_KEEP_ONE
+	UndoStep *us_exclude = NULL;
+#endif
+	/* keep at least two (original + other) */
+	size_t data_size_all = 0;
+	size_t us_count = 0;
+	for (us = ustack->steps.last; us && us->prev; us = us->prev) {
+		if (memory_limit) {
+			data_size_all += us->data_size;
+			if (data_size_all > memory_limit) {
+				break;
+			}
+		}
+		if (steps) {
+			if (us_count == steps) {
+				break;
+			}
+			if (us->skip == false) {
+				us_count += 1;
+			}
+		}
+	}
+
+	if (us) {
+		if (us->prev && us->prev->prev) {
+			us = us->prev;
+		}
+
+#ifdef WITH_GLOBAL_UNDO_KEEP_ONE
+		/* Hack, we need to keep at least one BKE_UNDOSYS_TYPE_MEMFILE. */
+		if (us->type != BKE_UNDOSYS_TYPE_MEMFILE) {
+			us_exclude = us->prev;
+			while (us_exclude && us->type != BKE_UNDOSYS_TYPE_MEMFILE) {
+				us_exclude = us_exclude->prev;
+			}
+			if (us_exclude) {
+				BLI_remlink(&ustack->steps, us_exclude);
+			}
+		}
+#endif
+		/* Free from first to last, free functions may update de-duplication info (see #MemFileUndoStep). */
+		while (ustack->steps.first != us) {
+			UndoStep *us_first = ustack->steps.first;
+			BLI_assert(us_first != ustack->step_active);
+			undosys_step_free_and_unlink(ustack, us_first);
+		}
+
+#ifdef WITH_GLOBAL_UNDO_KEEP_ONE
+		if (us_exclude) {
+			BLI_addhead(&ustack->steps, us_exclude);
+		}
+#endif
+	}
+}
+
+/** \} */
+
+UndoStep *BKE_undosys_step_push_init_with_type(UndoStack *ustack, bContext *C, const char *name, const UndoType *ut)
+{
+	UNDO_NESTED_ASSERT(false);
+	/* We could detect and clean this up (but it should never happen!). */
+	BLI_assert(ustack->step_init == NULL);
+	if (ut->step_encode_init) {
+		undosys_stack_validate(ustack, false);
+		UndoStep *us = MEM_callocN(ut->step_size, __func__);
+		CLOG_INFO(&LOG, 1, "addr=%p, name='%s', type='%s'", us, name, ut->name);
+		if (name != NULL) {
+			BLI_strncpy(us->name, name, sizeof(us->name));
+		}
+		us->type = ut;
+		ustack->step_init = us;
+		ut->step_encode_init(C, us);
+		undosys_stack_validate(ustack, true);
+		return us;
+	}
+	else {
+		return NULL;
+	}
+}
+
+UndoStep *BKE_undosys_step_push_init(UndoStack *ustack, bContext *C, const char *name)
+{
+	UNDO_NESTED_ASSERT(false);
+	/* We could detect and clean this up (but it should never happen!). */
+	BLI_assert(ustack->step_init == NULL);
+	const UndoType *ut = BKE_undosys_type_from_context(C);
+	if (ut == NULL) {
+		return NULL;
+	}
+	return BKE_undosys_step_push_init_with_type(ustack, C, name, ut);
+}
+
+bool BKE_undosys_step_push_with_type(UndoStack *ustack, bContext *C, const char *name, const UndoType *ut)
+{
+	UNDO_NESTED_ASSERT(false);
+	undosys_stack_validate(ustack, false);
+	bool is_not_empty = ustack->step_active != NULL;
+
+	/* Remove all undos after (also when 'ustack->step_active == NULL'). */
+	while (ustack->steps.last != ustack->step_active) {
+		UndoStep *us_iter = ustack->steps.last;
+		undosys_step_free_and_unlink(ustack, us_iter);
+		undosys_stack_validate(ustack, is_not_empty);
+	}
+
+	if (ustack->step_active) {
+		BLI_assert(BLI_findindex(&ustack->steps, ustack->step_active) != -1);
+	}
+
+#ifdef WITH_GLOBAL_UNDO_ENSURE_UPDATED
+	if (ut->step_foreach_ID_ref != NULL) {
+		Main *bmain = G.main;
+		if (bmain->is_memfile_undo_written == false) {
+			const char *name_internal = "MemFile Internal";
+			if (undosys_stack_push_main(ustack, name_internal, bmain)) {
+				UndoStep *us = ustack->steps.last;
+				BLI_assert(STREQ(us->name, name_internal));
+				us->skip = true;
+			}
+		}
+	}
+#endif
+
+	UndoStep *us = ustack->step_init ? ustack->step_init : MEM_callocN(ut->step_size, __func__);
+	ustack->step_init = NULL;
+	if (us->name[0] == '\0') {
+		BLI_strncpy(us->name, name, sizeof(us->name));
+	}
+	us->type = ut;
+	/* initialized, not added yet. */
+
+	if (undosys_step_encode(C, us)) {
+		ustack->step_active = us;
+		BLI_addtail(&ustack->steps, us);
+		undosys_stack_validate(ustack, true);
+		return true;
+	}
+	else {
+		MEM_freeN(us);
+		undosys_stack_validate(ustack, true);
+		return false;
+	}
+}
+
+bool BKE_undosys_step_push(UndoStack *ustack, bContext *C, const char *name)
+{
+	UNDO_NESTED_ASSERT(false);
+	const UndoType *ut = ustack->step_init ? ustack->step_init->type : BKE_undosys_type_from_context(C);
+	if (ut == NULL) {
+		return false;
+	}
+	return BKE_undosys_step_push_with_type(ustack, C, name, ut);
+}
+
+
+/**
+ * Useful when we want to diff against previous undo data but can't be sure the types match.
+ */
+UndoStep *BKE_undosys_step_same_type_next(UndoStep *us)
+{
+	if (us) {
+		const UndoType *ut = us->type;
+		while ((us = us->next)) {
+			if (us->type == ut) {
+				return us;
+			}
+		}
+
+	}
+	return us;
+}
+
+/**
+ * Useful when we want to diff against previous undo data but can't be sure the types match.
+ */
+UndoStep *BKE_undosys_step_same_type_prev(UndoStep *us)
+{
+	if (us) {
+		const UndoType *ut = us->type;
+		while ((us = us->prev)) {
+			if (us->type == ut) {
+				return us;
+			}
+		}
+
+	}
+	return us;
+}
+
+UndoStep *BKE_undosys_step_find_by_name_with_type(UndoStack *ustack, const char *name, const UndoType *ut)
+{
+	for (UndoStep *us = ustack->steps.last; us; us = us->prev) {
+		if (us->type == ut) {
+			if (STREQ(name, us->name)) {
+				return us;
+			}
+		}
+	}
+	return NULL;
+}
+
+UndoStep *BKE_undosys_step_find_by_name(UndoStack *ustack, const char *name)
+{
+	return BLI_rfindstring(&ustack->steps, name, offsetof(UndoStep, name));
+}
+
+bool BKE_undosys_step_undo_with_data_ex(
+        UndoStack *ustack, bContext *C, UndoStep *us,
+        bool use_skip)
+{
+	UNDO_NESTED_ASSERT(false);
+	if (us) {
+		undosys_stack_validate(ustack, true);
+	}
+	UndoStep *us_prev = us ? us->prev : NULL;
+	if (us && us->type->mode == BKE_UNDOTYPE_MODE_STORE) {
+		/* The current state is a copy, we need to load the previous state. */
+		us = us_prev;
+	}
+
+	if (us != NULL) {
+		CLOG_INFO(&LOG, 1, "addr=%p, name='%s', type='%s'", us, us->name, us->type->name);
+		undosys_step_decode(C, us, -1);
+		ustack->step_active = us_prev;
+		undosys_stack_validate(ustack, true);
+		if (use_skip) {
+			if (ustack->step_active && ustack->step_active->skip) {
+				CLOG_INFO(&LOG, 2, "undo continue with skip %p '%s', type='%s'", us, us->name, us->type->name);
+				BKE_undosys_step_undo_with_data(ustack, C, ustack->step_active);
+			}
+		}
+		return true;
+	}
+	return false;
+}
+bool BKE_undosys_step_undo_with_data(UndoStack *ustack, bContext *C, UndoStep *us)
+{
+	return BKE_undosys_step_undo_with_data_ex(ustack, C, us, true);
+}
+
+bool BKE_undosys_step_undo(UndoStack *ustack, bContext *C)
+{
+	return BKE_undosys_step_undo_with_data(ustack, C, ustack->step_active);
+}
+
+void BKE_undosys_step_undo_from_index(UndoStack *ustack, bContext *C, int index)
+{
+	UndoStep *us = BLI_findlink(&ustack->steps, index);
+	BLI_assert(us->skip == false);
+	BKE_undosys_step_load_data(ustack, C, us);
+}
+
+bool BKE_undosys_step_redo_with_data_ex(
+        UndoStack *ustack, bContext *C, UndoStep *us,
+        bool use_skip)
+{
+	UNDO_NESTED_ASSERT(false);
+	UndoStep *us_next = us ? us->next : NULL;
+	/* Unlike undo accumulate, we always use the next. */
+	us = us_next;
+
+	if (us != NULL) {
+		CLOG_INFO(&LOG, 1, "addr=%p, name='%s', type='%s'", us, us->name, us->type->name);
+		undosys_step_decode(C, us, 1);
+		ustack->step_active = us_next;
+		if (use_skip) {
+			if (ustack->step_active && ustack->step_active->skip) {
+				CLOG_INFO(&LOG, 2, "redo continue with skip %p '%s', type='%s'", us, us->name, us->type->name);
+				BKE_undosys_step_redo_with_data(ustack, C, ustack->step_active);
+			}
+		}
+		return true;
+	}
+	return false;
+}
+bool BKE_undosys_step_redo_with_data(UndoStack *ustack, bContext *C, UndoStep *us)
+{
+	return BKE_undosys_step_redo_with_data_ex(ustack, C, us, true);
+}
+
+bool BKE_undosys_step_redo(UndoStack *ustack, bContext *C)
+{
+	return BKE_undosys_step_redo_with_data(ustack, C, ustack->step_active);
+}
+
+bool BKE_undosys_step_load_data(UndoStack *ustack, bContext *C, UndoStep *us)
+{
+	UNDO_NESTED_ASSERT(false);
+	const int index_active = BLI_findindex(&ustack->steps, ustack->step_active);
+	const int index_target = BLI_findindex(&ustack->steps, us);
+	BLI_assert(!ELEM(-1, index_active, index_target));
+	bool ok = true;
+
+	if (index_target < index_active) {
+		uint i = index_active - index_target;
+		while (i-- && ok) {
+			ok = BKE_undosys_step_undo_with_data_ex(ustack, C, ustack->step_active, false);
+		}
+	}
+	else if (index_target > index_active) {
+		uint i = index_target - index_active;
+		while (i-- && ok) {
+			ok = BKE_undosys_step_redo_with_data_ex(ustack, C, ustack->step_active, false);
+		}
+	}
+
+	if (ok) {
+		BLI_assert(ustack->step_active == us);
+	}
+
+	return ok;
+}
+
+bool BKE_undosys_step_undo_compat_only(UndoStack *ustack, bContext *C, int step)
+{
+	if (step == 0) {
+		return BKE_undosys_step_undo_with_data(ustack, C, ustack->step_active);
+	}
+	else if (step == 1) {
+		return BKE_undosys_step_undo(ustack, C);
+	}
+	else {
+		return BKE_undosys_step_redo(ustack, C);
+	}
+}
+/**
+ * Similar to #WM_operatortype_append
+ */
+UndoType *BKE_undosys_type_append(void (*undosys_fn)(UndoType *))
+{
+	UndoType *ut;
+
+	ut = MEM_callocN(sizeof(UndoType), __func__);
+
+	undosys_fn(ut);
+
+	BLI_assert(ut->mode != 0);
+
+	BLI_addtail(&g_undo_types, ut);
+
+	return ut;
+}
+
+void BKE_undosys_type_free_all(void)
+{
+	UndoType *ut;
+	while ((ut = BLI_pophead(&g_undo_types))) {
+		MEM_freeN(ut);
+	}
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name ID Reference Utilities
+ *
+ * Unfortunately we need this for a handful of places.
+ */
+
+static void UNUSED_FUNCTION(BKE_undosys_foreach_ID_ref(
+        UndoStack *ustack, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data))
+{
+	for (UndoStep *us = ustack->steps.first; us; us = us->next) {
+		const UndoType *ut = us->type;
+		if (ut->step_foreach_ID_ref != NULL) {
+			ut->step_foreach_ID_ref(us, foreach_ID_ref_fn, user_data);
+		}
+	}
+}
+
+typedef struct UndoIDPtrMapItem {
+	/** Never changes (matches undo data). Use as sort key for binary search. */
+	const void *ptr;
+	/** Write the new pointers here. */
+	uint index;
+} UndoIDPtrMapItem;
+
+typedef struct UndoIDPtrMap {
+	UndoRefID *refs;
+	/**
+	 * Pointer map, update 'dst' members before use.
+	 * This is always sorted (adds some overhead when adding, in practice it's acceptable since).
+	 */
+	UndoIDPtrMapItem *pmap;
+
+	/** Length for both 'refs' & 'pmap' */
+	uint len;
+	uint len_alloc;
+} UndoIDPtrMap;
+
+#ifdef DEBUG
+#  define PMAP_DEFAULT_ALLOC 1
+#else
+#  define PMAP_DEFAULT_ALLOC 32
+#endif
+
+void BKE_undosys_ID_map_foreach_ID_ref(
+        UndoIDPtrMap *map,
+        UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
+{
+	for (uint i = 0; i < map->len; i++) {
+		foreach_ID_ref_fn(user_data, &map->refs[i]);
+	}
+}
+
+/**
+ * Return true when found, otherwise index is set to the index we should insert.
+ */
+static bool undosys_ID_map_lookup_index(const UndoIDPtrMap *map, const void *key, uint *r_index)
+{
+	const UndoIDPtrMapItem *pmap = map->pmap;
+	const uint len = map->len;
+	if (len == 0) {
+		if (*r_index) {
+			*r_index = 0;
+		}
+		return false;
+	}
+	int min = 0, max = len - 1;
+	while (min <= max) {
+		const uint mid = (min + max) / 2;
+		if (pmap[mid].ptr < key) {
+			min = mid + 1;
+		}
+		else if (pmap[mid].ptr == key) {
+			if (r_index) {
+				*r_index = mid;
+			}
+			return true;
+		}
+		else if (pmap[mid].ptr > key) {
+			max = mid - 1;
+		}
+	}
+	if (r_index) {
+		*r_index = min;
+	}
+	return false;
+}
+
+/**
+ * A set of ID's use for efficient decoding, so we can map pointers back to the newly loaded data
+ * without performing full look ups each time.
+ *
+ * This can be used as an old_pointer -> new_pointer lookup.
+ */
+UndoIDPtrMap *BKE_undosys_ID_map_create(void)
+{
+	UndoIDPtrMap *map = MEM_mallocN(sizeof(*map), __func__);
+	map->len_alloc = PMAP_DEFAULT_ALLOC;
+	map->refs = MEM_mallocN(sizeof(*map->refs) * map->len_alloc, __func__);
+	map->pmap = MEM_mallocN(sizeof(*map->pmap) * map->len_alloc, __func__);
+	map->len = 0;
+	return map;
+}
+void BKE_undosys_ID_map_destroy(UndoIDPtrMap *idpmap)
+{
+	MEM_SAFE_FREE(idpmap->refs);
+	MEM_SAFE_FREE(idpmap->pmap);
+	MEM_freeN(idpmap);
+}
+
+void BKE_undosys_ID_map_add(UndoIDPtrMap *map, ID *id)
+{
+	uint index;
+	if (id->lib != NULL) {
+		return;
+	}
+
+	if (undosys_ID_map_lookup_index(map, id, &index)) {
+		return;  /* exists. */
+	}
+
+	const uint len_src = map->len;
+	const uint len_dst = map->len + 1;
+	if (len_dst > map->len_alloc) {
+		map->len_alloc *= 2;
+		BLI_assert(map->len_alloc >= len_dst);
+		map->pmap = MEM_reallocN(map->pmap, sizeof(*map->pmap) * map->len_alloc);
+		map->refs = MEM_reallocN(map->refs, sizeof(*map->refs) * map->len_alloc);
+	}
+
+#if 0  /* Will be done automatically in callback. */
+	BLI_strncpy(map->refs[len_src].name, id->name, sizeof(id->name));
+#else
+	map->refs[len_src].name[0] = '\0';
+#endif
+	map->refs[len_src].ptr = id;
+
+	if (len_src != 0 && index != len_src) {
+		memmove(&map->pmap[index + 1], &map->pmap[index], sizeof(*map->pmap) * (len_src - index));
+	}
+	map->pmap[index].ptr = id;
+	map->pmap[index].index = len_src;
+
+	map->len = len_dst;
+}
+
+ID *BKE_undosys_ID_map_lookup(const UndoIDPtrMap *map, const ID *id_src)
+{
+	/* We should only ever lookup indices which exist! */
+	uint index;
+	if (!undosys_ID_map_lookup_index(map, id_src, &index)) {
+		BLI_assert(0);
+	}
+	index = map->pmap[index].index;
+	ID *id_dst = map->refs[index].ptr;
+	BLI_assert(id_dst != NULL);
+	BLI_assert(STREQ(id_dst->name, map->refs[index].name));
+	return id_dst;
+}
+
+void BKE_undosys_ID_map_add_with_prev(UndoIDPtrMap *map, ID *id, ID **id_prev)
+{
+	if (id == *id_prev) {
+		return;
+	}
+	*id_prev = id;
+	BKE_undosys_ID_map_add(map, id);
+}
+
+ID *BKE_undosys_ID_map_lookup_with_prev(const UndoIDPtrMap *map, ID *id_src, ID *id_prev_match[2])
+{
+	if (id_src == id_prev_match[0]) {
+		return id_prev_match[1];
+	}
+	else {
+		ID *id_dst = BKE_undosys_ID_map_lookup(map, id_src);
+		id_prev_match[0] = id_src;
+		id_prev_match[1] = id_dst;
+		return id_dst;
+	}
+}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/workspace.c b/source/blender/blenkernel/intern/workspace.c
index 301084e22fc053a514af4048cff2ee812edbde37..26a680c881ff7c1585c256036acc852c6703d8bd 100644
--- a/source/blender/blenkernel/intern/workspace.c
+++ b/source/blender/blenkernel/intern/workspace.c
@@ -511,30 +511,6 @@ void BKE_workspace_update_tagged(struct EvaluationContext *eval_ctx,
 	BKE_scene_graph_update_tagged(eval_ctx, depsgraph, bmain, scene, view_layer);
 }
 
-void BKE_workspace_update_object_mode(
-        struct EvaluationContext *eval_ctx,
-        WorkSpace *workspace)
-{
-	/* TODO(campbell): Investigate how this should work exactly,
-	 * for now without this 'bmain->eval_ctx' is never set. */
-
-	eval_ctx->object_mode = workspace->object_mode;
-}
-
-Object *BKE_workspace_edit_object(WorkSpace *workspace, Scene *scene)
-{
-	if (workspace->object_mode & OB_MODE_EDIT) {
-		ViewLayer *view_layer = BKE_workspace_view_layer_get(workspace, scene);
-		if (view_layer) {
-			Object *obedit = OBACT(view_layer);
-			if (obedit) {
-				BLI_assert(BKE_object_is_in_editmode(obedit));
-				return obedit;
-			}
-		}
-	}
-	return NULL;
-}
 
 bool BKE_workspace_owner_id_check(
         const WorkSpace *workspace, const char *owner_id)
@@ -549,3 +525,4 @@ bool BKE_workspace_owner_id_check(
 		return BLI_findstring(&workspace->owner_ids, owner_id, offsetof(wmOwnerID, name)) != NULL;
 	}
 }
+
diff --git a/source/blender/blenkernel/intern/world.c b/source/blender/blenkernel/intern/world.c
index a57fc1fe027e9d6361876a2ef7063ca9197d0a32..e87e84736c82b4bde98f6fa6053270389ce82cd0 100644
--- a/source/blender/blenkernel/intern/world.c
+++ b/source/blender/blenkernel/intern/world.c
@@ -33,6 +33,7 @@
 #include <string.h>
 #include <stdlib.h>
 #include <math.h>
+
 #include "MEM_guardedalloc.h"
 
 #include "DNA_world_types.h"
diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c
index f745a840ceaab33b3255eb31a0cb5882ae64fbc4..85582ceea4c2d4420b3542b4a89776ee9f3bc659 100644
--- a/source/blender/blenkernel/intern/writeffmpeg.c
+++ b/source/blender/blenkernel/intern/writeffmpeg.c
@@ -53,6 +53,7 @@
 
 #include "BKE_global.h"
 #include "BKE_idprop.h"
+#include "BKE_image.h"
 #include "BKE_main.h"
 #include "BKE_report.h"
 #include "BKE_sound.h"
@@ -62,6 +63,8 @@
 
 #include "ffmpeg_compat.h"
 
+struct StampData;
+
 typedef struct FFMpegContext {
 	int ffmpeg_type;
 	int ffmpeg_codec;
@@ -94,6 +97,8 @@ typedef struct FFMpegContext {
 	bool audio_deinterleave;
 	int audio_sample_size;
 
+	struct StampData *stamp_data;
+
 #ifdef WITH_AUDASPACE
 	AUD_Device *audio_mixdown_device;
 #endif
@@ -836,6 +841,12 @@ static void ffmpeg_dict_set_float(AVDictionary **dict, const char *key, float va
 	av_dict_set(dict, key, buffer, 0);
 }
 
+static void ffmpeg_add_metadata_callback(void *data, const char *propname, char *propvalue, int len)
+{
+	AVDictionary **metadata = (AVDictionary **)data;
+	av_dict_set(metadata, propname, propvalue, 0);
+}
+
 static int start_ffmpeg_impl(FFMpegContext *context, struct RenderData *rd, int rectx, int recty, const char *suffix, ReportList *reports)
 {
 	/* Handle to the output file */
@@ -994,6 +1005,11 @@ static int start_ffmpeg_impl(FFMpegContext *context, struct RenderData *rd, int
 			goto fail;
 		}
 	}
+
+	if (context->stamp_data != NULL) {
+		BKE_stamp_info_callback(&of->metadata, context->stamp_data, ffmpeg_add_metadata_callback, false);
+	}
+
 	if (avformat_write_header(of, NULL) < 0) {
 		BKE_report(reports, RPT_ERROR, "Could not initialize streams, probably unsupported codec combination");
 		goto fail;
@@ -1168,6 +1184,7 @@ int BKE_ffmpeg_start(void *context_v, struct Scene *scene, RenderData *rd, int r
 
 	context->ffmpeg_autosplit_count = 0;
 	context->ffmpeg_preview = preview;
+	context->stamp_data = BKE_stamp_info_from_scene_static(scene);
 
 	success = start_ffmpeg_impl(context, rd, rectx, recty, suffix, reports);
 #ifdef WITH_AUDASPACE
@@ -1734,6 +1751,7 @@ void *BKE_ffmpeg_context_create(void)
 	context->ffmpeg_autosplit = 0;
 	context->ffmpeg_autosplit_count = 0;
 	context->ffmpeg_preview = false;
+	context->stamp_data = NULL;
 
 	return context;
 }
@@ -1741,9 +1759,13 @@ void *BKE_ffmpeg_context_create(void)
 void BKE_ffmpeg_context_free(void *context_v)
 {
 	FFMpegContext *context = context_v;
-	if (context) {
-		MEM_freeN(context);
+	if (context == NULL) {
+		return;
+	}
+	if (context->stamp_data) {
+		MEM_freeN(context->stamp_data);
 	}
+	MEM_freeN(context);
 }
 
 #endif /* WITH_FFMPEG */
diff --git a/source/blender/blenlib/BLI_array_utils.h b/source/blender/blenlib/BLI_array_utils.h
index a46c87cec40d9469ea487f6d38b07760a43f603e..9a510bcfc3b7dd4c944685721693526783480526 100644
--- a/source/blender/blenlib/BLI_array_utils.h
+++ b/source/blender/blenlib/BLI_array_utils.h
@@ -74,10 +74,18 @@ bool _bli_array_iter_span(
         bool use_wrap, bool use_delimit_bounds,
         bool (*test_fn)(const void *arr_item, void *user_data), void *user_data,
         unsigned int span_step[2], unsigned int *r_span_len);
-#define BLI_array_iter_span(arr, arr_len, use_wrap, use_delimit_bounds, test_fn, user_data, \
-	        span_step, r_span_len) \
+#define BLI_array_iter_span( \
+        arr, arr_len, use_wrap, use_delimit_bounds, test_fn, user_data, \
+        span_step, r_span_len) \
 	_bli_array_iter_span( \
 	        arr, arr_len, sizeof(*(arr)), use_wrap, use_delimit_bounds, test_fn, user_data, \
 	        span_step, r_span_len)
 
+bool _bli_array_is_zeroed(
+        const void *arr,
+        unsigned int arr_len, size_t arr_stride);
+#define BLI_array_is_zeroed(arr, arr_len) \
+	_bli_array_is_zeroed( \
+	        arr, arr_len, sizeof(*(arr)))
+
 #endif  /* __BLI_ARRAY_UTILS_H__ */
diff --git a/source/blender/blenlib/BLI_assert.h b/source/blender/blenlib/BLI_assert.h
index 9fb0954e77fee5daaf20465b05320b2b534cc76e..b66b95b21fe45a11f89939bfe681b43956e47e18 100644
--- a/source/blender/blenlib/BLI_assert.h
+++ b/source/blender/blenlib/BLI_assert.h
@@ -46,33 +46,28 @@ extern "C" {
 
 #ifndef NDEBUG
 #  include "BLI_system.h"
-#  ifdef WITH_ASSERT_ABORT
-#    define _BLI_DUMMY_ABORT abort
+   /* _BLI_ASSERT_PRINT_POS */
+#  if defined(__GNUC__)
+#    define _BLI_ASSERT_PRINT_POS(a) \
+	fprintf(stderr, "BLI_assert failed: %s:%d, %s(), at \'%s\'\n", __FILE__, __LINE__, __func__, #a)
+#  elif defined(_MSC_VER)
+#    define _BLI_ASSERT_PRINT_POS(a) \
+	fprintf(stderr, "BLI_assert failed: %s:%d, %s(), at \'%s\'\n", __FILE__, __LINE__, __FUNCTION__, #a)
 #  else
-#    define _BLI_DUMMY_ABORT() (void)0
+#    define _BLI_ASSERT_PRINT_POS(a) \
+	fprintf(stderr, "BLI_assert failed: %s:%d, at \'%s\'\n", __FILE__, __LINE__, #a)
 #  endif
-#  if defined(__GNUC__) || defined(_MSC_VER) /* check __func__ is available */
-#    define BLI_assert(a)                                                     \
-	(void)((!(a)) ?  (                                                        \
-		(                                                                     \
-		BLI_system_backtrace(stderr),                                         \
-		fprintf(stderr,                                                       \
-			"BLI_assert failed: %s:%d, %s(), at \'%s\'\n",                    \
-			__FILE__, __LINE__, __func__, "" #a),                             \
-		_BLI_DUMMY_ABORT(),                                                   \
-		NULL)) : NULL)
+   /* _BLI_ASSERT_ABORT */
+#  ifdef WITH_ASSERT_ABORT
+#    define _BLI_ASSERT_ABORT abort
 #  else
-#    define BLI_assert(a)                                                     \
-	(void)((!(a)) ?  (                                                        \
-		(                                                                     \
-		fprintf(stderr,                                                       \
-			"BLI_assert failed: %s:%d, at \'%s\'\n",                          \
-			__FILE__, __LINE__, "" #a),                                       \
-		_BLI_DUMMY_ABORT(),                                                   \
-		NULL)) : NULL)
+#    define _BLI_ASSERT_ABORT() (void)0
 #  endif
+   /* BLI_assert */
+#  define BLI_assert(a) \
+	(void)((!(a)) ? ((BLI_system_backtrace(stderr), _BLI_ASSERT_PRINT_POS(a), _BLI_ASSERT_ABORT(), NULL)) : NULL)
 #else
-#  define BLI_assert(a) (void)0
+#  define BLI_assert(a) ((void)0)
 #endif
 
 /* C++ can't use _Static_assert, expects static_assert() but c++0x only,
@@ -85,19 +80,19 @@ extern "C" {
 /* Code adapted from http://www.pixelbeat.org/programming/gcc/static_assert.html */
 /* Note we need the two concats below because arguments to ## are not expanded, so we need to
  * expand __LINE__ with one indirection before doing the actual concatenation. */
-#  define ASSERT_CONCAT_(a, b) a##b
-#  define ASSERT_CONCAT(a, b) ASSERT_CONCAT_(a, b)
+#  define _BLI_ASSERT_CONCAT_(a, b) a##b
+#  define _BLI_ASSERT_CONCAT(a, b) _BLI_ASSERT_CONCAT_(a, b)
    /* These can't be used after statements in c89. */
 #  if defined(__COUNTER__)  /* MSVC */
 #    define BLI_STATIC_ASSERT(a, msg) \
-         ; enum { ASSERT_CONCAT(static_assert_, __COUNTER__) = 1 / (int)(!!(a)) };
+         ; enum { _BLI_ASSERT_CONCAT(static_assert_, __COUNTER__) = 1 / (int)(!!(a)) };
 #  else  /* older gcc, clang... */
     /* This can't be used twice on the same line so ensure if using in headers
      * that the headers are not included twice (by wrapping in #ifndef...#endif)
      * Note it doesn't cause an issue when used on same line of separate modules
      * compiled with gcc -combine -fwhole-program. */
 #    define BLI_STATIC_ASSERT(a, msg) \
-         ; enum { ASSERT_CONCAT(assert_line_, __LINE__) = 1 / (int)(!!(a)) };
+         ; enum { _BLI_ASSERT_CONCAT(assert_line_, __LINE__) = 1 / (int)(!!(a)) };
 #  endif
 #endif
 
diff --git a/source/blender/blenlib/BLI_listbase.h b/source/blender/blenlib/BLI_listbase.h
index 6be9408ac1eda70d5bfc8e24bf60c96ed2ea6ecf..c4ad5acfe4bf3a4bf73d17500109d910153413ad 100644
--- a/source/blender/blenlib/BLI_listbase.h
+++ b/source/blender/blenlib/BLI_listbase.h
@@ -74,7 +74,7 @@ void BLI_listbase_sort(struct ListBase *listbase, int (*cmp)(const void *, const
 void BLI_listbase_sort_r(ListBase *listbase, int (*cmp)(void *, const void *, const void *), void *thunk) ATTR_NONNULL(1, 2);
 bool BLI_listbase_link_move(ListBase *listbase, void *vlink, int step) ATTR_NONNULL();
 void BLI_freelist(struct ListBase *listbase) ATTR_NONNULL(1);
-int  BLI_listbase_count_ex(const struct ListBase *listbase, const int count_max) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1);
+int  BLI_listbase_count_at_most(const struct ListBase *listbase, const int count_max) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1);
 int  BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1);
 void BLI_freelinkN(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1);
 
diff --git a/source/blender/blenlib/BLI_sort_utils.h b/source/blender/blenlib/BLI_sort_utils.h
index e08f4e5ac8324e1d63c4a6b22cf29634d96a3c7c..f6bd80b30d33a75f3692e5bffc6e41644ea27f57 100644
--- a/source/blender/blenlib/BLI_sort_utils.h
+++ b/source/blender/blenlib/BLI_sort_utils.h
@@ -32,7 +32,7 @@
  * \note keep \a sort_value first,
  * so cmp functions can be reused.
  */
-struct SortPointerByFloat {
+struct SortPtrByFloat {
 	float sort_value;
 	void *data;
 };
@@ -42,7 +42,7 @@ struct SortIntByFloat {
 	int data;
 };
 
-struct SortPointerByInt {
+struct SortPtrByInt {
 	int sort_value;
 	void *data;
 };
@@ -58,4 +58,7 @@ int BLI_sortutil_cmp_float_reverse(const void *a_, const void *b_);
 int BLI_sortutil_cmp_int(const void *a_, const void *b_);
 int BLI_sortutil_cmp_int_reverse(const void *a_, const void *b_);
 
+int BLI_sortutil_cmp_ptr(const void *a_, const void *b_);
+int BLI_sortutil_cmp_ptr_reverse(const void *a_, const void *b_);
+
 #endif  /* __BLI_SORT_UTILS_H__ */
diff --git a/source/blender/blenlib/BLI_string.h b/source/blender/blenlib/BLI_string.h
index d137806c57549367a7f82d42031e90986f7bd334..48be9d1842f1d2037d36e0502486c52492b88214 100644
--- a/source/blender/blenlib/BLI_string.h
+++ b/source/blender/blenlib/BLI_string.h
@@ -103,6 +103,20 @@ int BLI_string_find_split_words(
         const char delim, int r_words[][2], int words_max)
         ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
 
+/** \name String Copy/Format Macros
+ * Avoid repeating destination with `sizeof(..)`.
+ * \note `ARRAY_SIZE` allows pointers on some platforms.
+ * \{ */
+#define STRNCPY(dst, src) \
+    BLI_strncpy(dst, src, ARRAY_SIZE(dst))
+#define STRNCPY_RLEN(dst, src) \
+    BLI_strncpy_rlen(dst, src, ARRAY_SIZE(dst))
+#define SNPRINTF(dst, format, ...) \
+    BLI_snprintf(dst, ARRAY_SIZE(dst), format, __VA_ARGS__)
+#define SNPRINTF_RLEN(dst, format, ...) \
+    BLI_snprintf_rlen(dst, ARRAY_SIZE(dst), format, __VA_ARGS__)
+/** \} */
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/source/blender/blenlib/BLI_string_utf8.h b/source/blender/blenlib/BLI_string_utf8.h
index 32504a88b48ac70b84d4c3a035b29800be3c6039..21542d0d6e1e1c9ecef7665f73d0f3ae743d4671 100644
--- a/source/blender/blenlib/BLI_string_utf8.h
+++ b/source/blender/blenlib/BLI_string_utf8.h
@@ -77,6 +77,16 @@ size_t       BLI_str_partition_ex_utf8(
 #define      BLI_UTF8_WIDTH_MAX 2  /* columns */
 #define      BLI_UTF8_ERR ((unsigned int)-1)
 
+/** \name String Copy/Format Macros
+ * Avoid repeating destination with `sizeof(..)`.
+ * \note `ARRAY_SIZE` allows pointers on some platforms.
+ * \{ */
+#define STRNCPY_UTF8(dst, src) \
+    BLI_strncpy_utf8(dst, src, ARRAY_SIZE(dst))
+#define STRNCPY_UTF8_RLEN(dst, src) \
+    BLI_strncpy_utf8_rlen(dst, src, ARRAY_SIZE(dst))
+/** \} */
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/source/blender/blenlib/BLI_utildefines.h b/source/blender/blenlib/BLI_utildefines.h
index 11c8a586784076290604fde538d13baea7c3aecd..46b3748c7cef0e5ba984f1e9e4c735f606273a09 100644
--- a/source/blender/blenlib/BLI_utildefines.h
+++ b/source/blender/blenlib/BLI_utildefines.h
@@ -411,7 +411,7 @@ extern "C" {
 	} (void)0
 
 /* assuming a static array */
-#if defined(__GNUC__) && !defined(__cplusplus) && !defined(__clang__)
+#if defined(__GNUC__) && !defined(__cplusplus) && !defined(__clang__) && !defined(__INTEL_COMPILER)
 #  define ARRAY_SIZE(arr) \
 	((sizeof(struct {int isnt_array : ((const void *)&(arr) == &(arr)[0]);}) * 0) + \
 	 (sizeof(arr) / sizeof(*(arr))))
diff --git a/source/blender/blenlib/intern/array_utils.c b/source/blender/blenlib/intern/array_utils.c
index 32f0111babd43d32757c7eb2a6d438eef7cf3c2e..7b2d35a763c2d495438ab14ed7dc74f929f7f13f 100644
--- a/source/blender/blenlib/intern/array_utils.c
+++ b/source/blender/blenlib/intern/array_utils.c
@@ -308,3 +308,20 @@ bool _bli_array_iter_span(
 
 	return false;
 }
+
+/**
+ * Simple utility to check memory is zeroed.
+ */
+bool _bli_array_is_zeroed(
+        const void *arr_v,
+        unsigned int arr_len, size_t arr_stride)
+{
+	const char *arr_step = (const char *)arr_v;
+	size_t i = arr_stride * arr_len;
+	while (i--) {
+		if (*(arr_step++)) {
+			return false;
+		}
+	}
+	return true;
+}
diff --git a/source/blender/blenlib/intern/listbase.c b/source/blender/blenlib/intern/listbase.c
index 96c3972d80214586e125c7e499d133a02ec1ef7a..b87fed571b783f37c543636d85d3c1e7825d60f9 100644
--- a/source/blender/blenlib/intern/listbase.c
+++ b/source/blender/blenlib/intern/listbase.c
@@ -484,7 +484,7 @@ void BLI_freelistN(ListBase *listbase)
  *
  * \note Use to avoid redundant looping.
  */
-int BLI_listbase_count_ex(const ListBase *listbase, const int count_max)
+int BLI_listbase_count_at_most(const ListBase *listbase, const int count_max)
 {
 	Link *link;
 	int count = 0;
diff --git a/source/blender/blenlib/intern/math_base_inline.c b/source/blender/blenlib/intern/math_base_inline.c
index eed06c7841b3dc69173462990e044d0e9b2c455c..4bedcbdf5bf1ab23f9cb25475777003525a383c6 100644
--- a/source/blender/blenlib/intern/math_base_inline.c
+++ b/source/blender/blenlib/intern/math_base_inline.c
@@ -473,7 +473,7 @@ MALWAYS_INLINE __m128 _bli_math_fastpow24(const __m128 arg)
 	 */
 	/* 0x3F4CCCCD = 4/5 */
 	/* 0x4F55A7FB = 2^(127/(4/5) - 127) * 0.994^(1/(4/5)) */
-	/* error max = 0.17	avg = 0.0018	|avg| = 0.05 */
+	/* error max = 0.17, avg = 0.0018, |avg| = 0.05 */
 	__m128 x = _bli_math_fastpow(0x3F4CCCCD, 0x4F55A7FB, arg);
 	__m128 arg2 = _mm_mul_ps(arg, arg);
 	__m128 arg4 = _mm_mul_ps(arg2, arg2);
diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c
index a3d850f9551736ddc7aa050fe74236547927f39e..e179447936a737814d7df13180c0dd398394db2b 100644
--- a/source/blender/blenlib/intern/math_geom.c
+++ b/source/blender/blenlib/intern/math_geom.c
@@ -3748,7 +3748,7 @@ void orthographic_m4(float matrix[4][4], const float left, const float right, co
 	matrix[3][0] = -(right + left) / Xdelta;
 	matrix[1][1] = 2.0f / Ydelta;
 	matrix[3][1] = -(top + bottom) / Ydelta;
-	matrix[2][2] = -2.0f / Zdelta; /* note: negate Z	*/
+	matrix[2][2] = -2.0f / Zdelta; /* note: negate Z */
 	matrix[3][2] = -(farClip + nearClip) / Zdelta;
 }
 
@@ -3767,7 +3767,7 @@ void perspective_m4(float mat[4][4], const float left, const float right, const
 	}
 	mat[0][0] = nearClip * 2.0f / Xdelta;
 	mat[1][1] = nearClip * 2.0f / Ydelta;
-	mat[2][0] = (right + left) / Xdelta; /* note: negate Z	*/
+	mat[2][0] = (right + left) / Xdelta; /* note: negate Z */
 	mat[2][1] = (top + bottom) / Ydelta;
 	mat[2][2] = -(farClip + nearClip) / Zdelta;
 	mat[2][3] = -1.0f;
diff --git a/source/blender/blenlib/intern/sort_utils.c b/source/blender/blenlib/intern/sort_utils.c
index c75e8e7455f5b501f0fab98dc18904cc4d6c7812..2d55e77b98b3b64706ec845cfdc48d52dda9c681 100644
--- a/source/blender/blenlib/intern/sort_utils.c
+++ b/source/blender/blenlib/intern/sort_utils.c
@@ -37,6 +37,10 @@ struct SortAnyByInt {
 	int sort_value;
 };
 
+struct SortAnyByPtr {
+	const void *sort_value;
+};
+
 int BLI_sortutil_cmp_float(const void *a_, const void *b_)
 {
 	const struct SortAnyByFloat *a = a_;
@@ -72,3 +76,21 @@ int BLI_sortutil_cmp_int_reverse(const void *a_, const void *b_)
 	else if (a->sort_value > b->sort_value) return -1;
 	else                                    return  0;
 }
+
+int BLI_sortutil_cmp_ptr(const void *a_, const void *b_)
+{
+	const struct SortAnyByPtr *a = a_;
+	const struct SortAnyByPtr *b = b_;
+	if      (a->sort_value > b->sort_value) return  1;
+	else if (a->sort_value < b->sort_value) return -1;
+	else                                    return  0;
+}
+
+int BLI_sortutil_cmp_ptr_reverse(const void *a_, const void *b_)
+{
+	const struct SortAnyByPtr *a = a_;
+	const struct SortAnyByPtr *b = b_;
+	if      (a->sort_value < b->sort_value) return  1;
+	else if (a->sort_value > b->sort_value) return -1;
+	else                                    return  0;
+}
diff --git a/source/blender/blenlib/intern/string.c b/source/blender/blenlib/intern/string.c
index c0e4b8f8168fb6b7f2c6d1cfd33cfcc73360a89b..1a6fd082e95c5dfffafe5b3ec3f972f6aa945b7f 100644
--- a/source/blender/blenlib/intern/string.c
+++ b/source/blender/blenlib/intern/string.c
@@ -931,13 +931,13 @@ size_t BLI_str_partition_ex(
 		if (end) {
 			if (from_right) {
 				for (tmp = end - 1; (tmp >= str) && (*tmp != *d); tmp--);
-				if (tmp	< str) {
+				if (tmp < str) {
 					tmp = NULL;
 				}
 			}
 			else {
 				tmp = func(str, *d);
-				if (tmp	>= end) {
+				if (tmp >= end) {
 					tmp = NULL;
 				}
 			}
diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h
index 0da15fe5bbc6657c8b505c85242062cdc3ca2794..7b31415ddcf8ee6b7f1c68ae6d0bb8703aac075a 100644
--- a/source/blender/blenloader/BLO_readfile.h
+++ b/source/blender/blenloader/BLO_readfile.h
@@ -160,6 +160,8 @@ void BLO_update_defaults_startup_blend(struct Main *mainvar);
 
 struct BlendThumbnail *BLO_thumbnail_from_file(const char *filepath);
 
+struct Main *BLO_main_from_memfile(struct MemFile *memfile, struct Main *bmain, struct Scene **r_scene);
+
 #ifdef __cplusplus
 } 
 #endif
diff --git a/source/blender/blenloader/BLO_undofile.h b/source/blender/blenloader/BLO_undofile.h
index fb96ec75e625b1aee3f39c6041482d221b25cac9..b713b963056b52b8d5407122b0b3a7be0e0c9362 100644
--- a/source/blender/blenloader/BLO_undofile.h
+++ b/source/blender/blenloader/BLO_undofile.h
@@ -33,19 +33,28 @@
  *  \ingroup blenloader
  */
 
+struct Scene;
+
 typedef struct {
 	void *next, *prev;
-	
-	char *buf;
-	unsigned int ident, size;
-	
+	const char *buf;
+	/** Size in bytes. */
+	unsigned int size;
+	/** When true, this chunk doesn't own the memory, it's shared with a previous #MemFileChunk */
+	bool is_identical;
 } MemFileChunk;
 
 typedef struct MemFile {
 	ListBase chunks;
-	unsigned int size;
+	size_t size;
 } MemFile;
 
+typedef struct MemFileUndoData {
+	char filename[1024];  /* FILE_MAX */
+	MemFile memfile;
+	size_t undo_size;
+} MemFileUndoData;
+
 /* actually only used writefile.c */
 extern void memfile_chunk_add(MemFile *compare, MemFile *current, const char *buf, unsigned int size);
 
@@ -53,5 +62,9 @@ extern void memfile_chunk_add(MemFile *compare, MemFile *current, const char *bu
 extern void BLO_memfile_free(MemFile *memfile);
 extern void BLO_memfile_merge(MemFile *first, MemFile *second);
 
-#endif
+/* utilities */
+extern struct Main *BLO_memfile_main_get(struct MemFile *memfile, struct Main *bmain, struct Scene **r_scene);
+extern bool BLO_memfile_write_file(struct MemFile *memfile, const char *filename);
+
+#endif  /* __BLO_UNDOFILE_H__ */
 
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 21eccaff1686e099bb7579d5d5dc84433060b48a..7ae8f0410c59cb7c50ef285cf5d8610fb1a7a379 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -2287,6 +2287,10 @@ static void direct_link_id(FileData *fd, ID *id)
 	}
 	id->py_instance = NULL;
 
+	/* That way datablock reading not going through main read_libblock() function are still in a clear tag state.
+	 * (glowering at certain nodetree fake datablock here...). */
+	id->tag = 0;
+
 	/* Link direct data of overrides. */
 	if (id->override_static) {
 		id->override_static = newdataadr(fd, id->override_static);
@@ -3861,15 +3865,11 @@ static void lib_link_text(FileData *fd, Main *main)
 static void direct_link_text(FileData *fd, Text *text)
 {
 	TextLine *ln;
-	
+
 	text->name = newdataadr(fd, text->name);
-	
-	text->undo_pos = -1;
-	text->undo_len = TXT_INIT_UNDO;
-	text->undo_buf = MEM_mallocN(text->undo_len, "undo buf");
-	
+
 	text->compiled = NULL;
-	
+
 #if 0
 	if (text->flags & TXT_ISEXT) {
 		BKE_text_reload(text);
@@ -4944,7 +4944,8 @@ static void lib_link_object(FileData *fd, Main *main)
 #else
 					MEM_freeN(ob->pose);
 #endif
-					ob->pose = NULL;
+					ob->pose= NULL;
+					ob->mode &= ~OB_MODE_POSE;
 				}
 			}
 			for (a=0; a < ob->totcol; a++) 
@@ -5181,6 +5182,7 @@ static void direct_link_pose(FileData *fd, bPose *pose)
 	link_list(fd, &pose->agroups);
 
 	pose->chanhash = NULL;
+	pose->chan_array = NULL;
 
 	for (pchan = pose->chanbase.first; pchan; pchan=pchan->next) {
 		pchan->bone = NULL;
@@ -5552,6 +5554,19 @@ static void direct_link_object(FileData *fd, Object *ob)
 
 	/* XXX This should not be needed - but seems like it can happen in some cases, so for now play safe... */
 	ob->proxy_from = NULL;
+
+	/* loading saved files with editmode enabled works, but for undo we like
+	 * to stay in object mode during undo presses so keep editmode disabled.
+	 *
+	 * Also when linking in a file don't allow edit and pose modes.
+	 * See [#34776, #42780] for more information.
+	 */
+	if (fd->memfile || (ob->id.tag & (LIB_TAG_EXTERN | LIB_TAG_INDIRECT))) {
+		ob->mode &= ~(OB_MODE_EDIT | OB_MODE_PARTICLE_EDIT);
+		if (!fd->memfile) {
+			ob->mode &= ~OB_MODE_POSE;
+		}
+	}
 	
 	ob->adt = newdataadr(fd, ob->adt);
 	direct_link_animdata(fd, ob->adt);
@@ -5846,6 +5861,18 @@ static void lib_link_scene_collection(FileData *fd, Library *lib, SceneCollectio
 	}
 }
 
+static void lib_link_layer_collection(FileData *fd, LayerCollection *layer_collection)
+{
+	IDP_LibLinkProperty(layer_collection->properties, fd);
+
+	for (LayerCollection *layer_collection_nested = layer_collection->layer_collections.first;
+	     layer_collection_nested != NULL;
+	     layer_collection_nested = layer_collection_nested->next)
+	{
+		lib_link_layer_collection(fd, layer_collection_nested);
+	}
+}
+
 static void lib_link_view_layer(FileData *fd, Library *lib, ViewLayer *view_layer)
 {
 	/* tag scene layer to update for collection tree evaluation */
@@ -5866,6 +5893,16 @@ static void lib_link_view_layer(FileData *fd, Library *lib, ViewLayer *view_laye
 		base->flag |= BASE_DIRTY_ENGINE_SETTINGS;
 		base->collection_properties = NULL;
 	}
+
+	for (LayerCollection *layer_collection = view_layer->layer_collections.first;
+	     layer_collection != NULL;
+	     layer_collection = layer_collection->next)
+	{
+		lib_link_layer_collection(fd, layer_collection);
+	}
+
+	IDP_LibLinkProperty(view_layer->properties, fd);
+	IDP_LibLinkProperty(view_layer->id_properties, fd);
 }
 
 static void lib_link_scene(FileData *fd, Main *main)
@@ -6252,7 +6289,6 @@ static void direct_link_scene(FileData *fd, Scene *sce, Main *bmain)
 		sce->toolsettings->imapaint.paintcursor = NULL;
 		sce->toolsettings->particle.paintcursor = NULL;
 		sce->toolsettings->particle.scene = NULL;
-		sce->toolsettings->particle.view_layer = NULL;
 		sce->toolsettings->particle.object = NULL;
 		sce->toolsettings->gp_sculpt.paintcursor = NULL;
 		
@@ -6539,7 +6575,8 @@ static void direct_link_windowmanager(FileData *fd, wmWindowManager *wm)
 	wm->defaultconf = NULL;
 	wm->addonconf = NULL;
 	wm->userconf = NULL;
-	
+	wm->undo_stack = NULL;
+
 	wm->message_bus = NULL;
 
 	BLI_listbase_clear(&wm->jobs);
@@ -8384,7 +8421,7 @@ static const char *dataname(short id_code)
 		case ID_WO: return "Data from WO";
 		case ID_SCR: return "Data from SCR";
 		case ID_VF: return "Data from VF";
-		case ID_TXT	: return "Data from TXT";
+		case ID_TXT: return "Data from TXT";
 		case ID_SPK: return "Data from SPK";
 		case ID_LP: return "Data from LP";
 		case ID_SO: return "Data from SO";
@@ -8518,7 +8555,6 @@ static BHead *read_libblock(FileData *fd, Main *main, BHead *bhead, const short
 	if (!id)
 		return blo_nextbhead(fd, bhead);
 	
-	id->tag = tag | LIB_TAG_NEED_LINK;
 	id->lib = main->curlib;
 	id->us = ID_FAKE_USERS(id);
 	id->icon_id = 0;
@@ -8528,12 +8564,12 @@ static BHead *read_libblock(FileData *fd, Main *main, BHead *bhead, const short
 	
 	/* this case cannot be direct_linked: it's just the ID part */
 	if (bhead->code == ID_ID) {
+		/* That way, we know which datablock needs do_versions (required currently for linking). */
+		id->tag = tag | LIB_TAG_NEED_LINK | LIB_TAG_NEW;
+
 		return blo_nextbhead(fd, bhead);
 	}
 
-	/* That way, we know which datablock needs do_versions (required currently for linking). */
-	id->tag |= LIB_TAG_NEW;
-
 	/* need a name for the mallocN, just for debugging and sane prints on leaks */
 	allocname = dataname(GS(id->name));
 	
@@ -8542,7 +8578,11 @@ static BHead *read_libblock(FileData *fd, Main *main, BHead *bhead, const short
 	
 	/* init pointers direct data */
 	direct_link_id(fd, id);
-	
+
+	/* That way, we know which datablock needs do_versions (required currently for linking). */
+	/* Note: doing this after driect_link_id(), which resets that field. */
+	id->tag = tag | LIB_TAG_NEED_LINK | LIB_TAG_NEW;
+
 	switch (GS(id->name)) {
 		case ID_WM:
 			direct_link_windowmanager(fd, (wmWindowManager *)id);
@@ -9504,15 +9544,15 @@ static void expand_nodetree(FileData *fd, Main *mainvar, bNodeTree *ntree)
 		expand_idprops(fd, mainvar, node->prop);
 
 		for (sock = node->inputs.first; sock; sock = sock->next)
-			expand_doit(fd, mainvar, sock->prop);
+			expand_idprops(fd, mainvar, sock->prop);
 		for (sock = node->outputs.first; sock; sock = sock->next)
-			expand_doit(fd, mainvar, sock->prop);
+			expand_idprops(fd, mainvar, sock->prop);
 	}
 
 	for (sock = ntree->inputs.first; sock; sock = sock->next)
-		expand_doit(fd, mainvar, sock->prop);
+		expand_idprops(fd, mainvar, sock->prop);
 	for (sock = ntree->outputs.first; sock; sock = sock->next)
-		expand_doit(fd, mainvar, sock->prop);
+		expand_idprops(fd, mainvar, sock->prop);
 }
 
 static void expand_texture(FileData *fd, Main *mainvar, Tex *tex)
@@ -9913,6 +9953,18 @@ static void expand_scene_collection(FileData *fd, Main *mainvar, SceneCollection
 	}
 }
 
+static void expand_layer_collection(FileData *fd, Main *mainvar, LayerCollection *layer_collection)
+{
+	expand_idprops(fd, mainvar, layer_collection->properties);
+
+	for (LayerCollection *layer_collection_nested = layer_collection->layer_collections.first;
+	     layer_collection_nested != NULL;
+	     layer_collection_nested = layer_collection_nested->next)
+	{
+		expand_layer_collection(fd, mainvar, layer_collection_nested);
+	}
+}
+
 static void expand_scene(FileData *fd, Main *mainvar, Scene *sce)
 {
 	SceneRenderLayer *srl;
@@ -9949,6 +10001,9 @@ static void expand_scene(FileData *fd, Main *mainvar, Scene *sce)
 	}
 
 	for (ViewLayer *view_layer = sce->view_layers.first; view_layer; view_layer = view_layer->next) {
+		expand_idprops(fd, mainvar, view_layer->properties);
+		expand_idprops(fd, mainvar, view_layer->id_properties);
+
 		for (module = view_layer->freestyle_config.modules.first; module; module = module->next) {
 			if (module->script) {
 				expand_doit(fd, mainvar, module->script);
@@ -9961,6 +10016,13 @@ static void expand_scene(FileData *fd, Main *mainvar, Scene *sce)
 			}
 			expand_doit(fd, mainvar, lineset->linestyle);
 		}
+
+		for (LayerCollection *layer_collection = view_layer->layer_collections.first;
+		     layer_collection != NULL;
+		     layer_collection = layer_collection->next)
+		{
+			expand_layer_collection(fd, mainvar, layer_collection);
+		}
 	}
 
 	if (sce->r.dometext)
@@ -10452,6 +10514,7 @@ static void link_object_postprocess(ID *id, Scene *scene, ViewLayer *view_layer,
 		SceneCollection *sc;
 
 		ob = (Object *)id;
+		ob->mode = OB_MODE_OBJECT;
 
 		sc =  get_scene_collection_active_or_create(scene, view_layer, flag);
 		BKE_collection_object_add(&scene->id, sc, ob);
@@ -10494,6 +10557,8 @@ void BLO_library_link_copypaste(Main *mainl, BlendHandle *bh)
 			if (bhead->code == ID_OB) {
 				/* Instead of instancing Base's directly, postpone until after groups are loaded
 				 * otherwise the base's flag is set incorrectly when groups are used */
+				Object *ob = (Object *)id;
+				ob->mode = OB_MODE_OBJECT;
 				/* ensure give_base_to_objects runs on this object */
 				BLI_assert(id->us == 0);
 			}
diff --git a/source/blender/blenloader/intern/undofile.c b/source/blender/blenloader/intern/undofile.c
index c191e48a1432c2a1798cc0a0975c660e4a6e23ac..f6584ecf25fbbccb825675e123bc8e97a2a08eb2 100644
--- a/source/blender/blenloader/intern/undofile.c
+++ b/source/blender/blenloader/intern/undofile.c
@@ -34,6 +34,15 @@
 #include <string.h>
 #include <stdio.h>
 #include <math.h>
+#include <fcntl.h>
+#include <errno.h>
+
+/* open/close */
+#ifndef _WIN32
+#  include <unistd.h>
+#else
+#  include <io.h>
+#endif
 
 #include "MEM_guardedalloc.h"
 
@@ -42,6 +51,12 @@
 #include "BLI_blenlib.h"
 
 #include "BLO_undofile.h"
+#include "BLO_readfile.h"
+
+#include "BKE_main.h"
+
+/* keep last */
+#include "BLI_strict_flags.h"
 
 /* **************** support for memory-write, for undo buffers *************** */
 
@@ -51,8 +66,9 @@ void BLO_memfile_free(MemFile *memfile)
 	MemFileChunk *chunk;
 	
 	while ((chunk = BLI_pophead(&memfile->chunks))) {
-		if (chunk->ident == 0)
-			MEM_freeN(chunk->buf);
+		if (chunk->is_identical == false) {
+			MEM_freeN((void *)chunk->buf);
+		}
 		MEM_freeN(chunk);
 	}
 	memfile->size = 0;
@@ -68,9 +84,9 @@ void BLO_memfile_merge(MemFile *first, MemFile *second)
 	sc = second->chunks.first;
 	while (fc || sc) {
 		if (fc && sc) {
-			if (sc->ident) {
-				sc->ident = 0;
-				fc->ident = 1;
+			if (sc->is_identical) {
+				sc->is_identical = false;
+				fc->is_identical = true;
 			}
 		}
 		if (fc) fc = fc->next;
@@ -98,7 +114,7 @@ void memfile_chunk_add(MemFile *compare, MemFile *current, const char *buf, unsi
 	curchunk = MEM_mallocN(sizeof(MemFileChunk), "MemFileChunk");
 	curchunk->size = size;
 	curchunk->buf = NULL;
-	curchunk->ident = 0;
+	curchunk->is_identical = false;
 	BLI_addtail(&current->chunks, curchunk);
 	
 	/* we compare compchunk with buf */
@@ -106,17 +122,83 @@ void memfile_chunk_add(MemFile *compare, MemFile *current, const char *buf, unsi
 		if (compchunk->size == curchunk->size) {
 			if (memcmp(compchunk->buf, buf, size) == 0) {
 				curchunk->buf = compchunk->buf;
-				curchunk->ident = 1;
+				curchunk->is_identical = true;
 			}
 		}
 		compchunk = compchunk->next;
 	}
-	
+
 	/* not equal... */
 	if (curchunk->buf == NULL) {
-		curchunk->buf = MEM_mallocN(size, "Chunk buffer");
-		memcpy(curchunk->buf, buf, size);
+		char *buf_new = MEM_mallocN(size, "Chunk buffer");
+		memcpy(buf_new, buf, size);
+		curchunk->buf = buf_new;
 		current->size += size;
 	}
 }
 
+struct Main *BLO_memfile_main_get(struct MemFile *memfile, struct Main *oldmain, struct Scene **r_scene)
+{
+	struct Main *bmain_undo = NULL;
+	BlendFileData *bfd = BLO_read_from_memfile(oldmain, oldmain->name, memfile, NULL, BLO_READ_SKIP_NONE);
+
+	if (bfd) {
+		bmain_undo = bfd->main;
+		if (r_scene) {
+			*r_scene = bfd->curscene;
+		}
+
+		MEM_freeN(bfd);
+	}
+
+	return bmain_undo;
+}
+
+
+/**
+ * Saves .blend using undo buffer.
+ *
+ * \return success.
+ */
+bool BLO_memfile_write_file(struct MemFile *memfile, const char *filename)
+{
+	MemFileChunk *chunk;
+	int file, oflags;
+
+	/* note: This is currently used for autosave and 'quit.blend', where _not_ following symlinks is OK,
+	 * however if this is ever executed explicitly by the user, we may want to allow writing to symlinks.
+	 */
+
+	oflags = O_BINARY | O_WRONLY | O_CREAT | O_TRUNC;
+#ifdef O_NOFOLLOW
+	/* use O_NOFOLLOW to avoid writing to a symlink - use 'O_EXCL' (CVE-2008-1103) */
+	oflags |= O_NOFOLLOW;
+#else
+	/* TODO(sergey): How to deal with symlinks on windows? */
+#  ifndef _MSC_VER
+#    warning "Symbolic links will be followed on undo save, possibly causing CVE-2008-1103"
+#  endif
+#endif
+	file = BLI_open(filename,  oflags, 0666);
+
+	if (file == -1) {
+		fprintf(stderr, "Unable to save '%s': %s\n",
+		        filename, errno ? strerror(errno) : "Unknown error opening file");
+		return false;
+	}
+
+	for (chunk = memfile->chunks.first; chunk; chunk = chunk->next) {
+		if ((size_t)write(file, chunk->buf, chunk->size) != chunk->size) {
+			break;
+		}
+	}
+
+	close(file);
+
+	if (chunk) {
+		fprintf(stderr, "Unable to save '%s': %s\n",
+		        filename, errno ? strerror(errno) : "Unknown error writing file");
+		return false;
+	}
+	return true;
+}
diff --git a/source/blender/blenloader/intern/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c
index 59dc6e9d6b71989c3a546931009d0bd5acd9de76..f5a4a33860fb0378f462f162979c17b3c80bee31 100644
--- a/source/blender/blenloader/intern/versioning_250.c
+++ b/source/blender/blenloader/intern/versioning_250.c
@@ -1087,6 +1087,8 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *main)
 
 	if (main->versionfile < 250 || (main->versionfile == 250 && main->subversionfile < 2)) {
 		Scene *sce;
+		Object *ob;
+
 		for (sce = main->scene.first; sce; sce = sce->id.next) {
 			if (fd->fileflags & G_FILE_ENABLE_ALL_FRAMES)
 				sce->gm.flag |= GAME_ENABLE_ALL_FRAMES;
@@ -1118,6 +1120,11 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *main)
 			else
 				sce->gm.matmode = GAME_MAT_TEXFACE;
 		}
+
+		for (ob = main->object.first; ob; ob = ob->id.next) {
+			if (ob->flag & 8192) // OB_POSEMODE = 8192
+				ob->mode |= OB_MODE_POSE;
+		}
 	}
 
 	if (main->versionfile < 250 || (main->versionfile == 250 && main->subversionfile < 4)) {
diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c
index 781814a3db088a175f162b8df9e0461beb24a162..40822f08a142da2afba0270c4704a2451e172986 100644
--- a/source/blender/blenloader/intern/versioning_280.c
+++ b/source/blender/blenloader/intern/versioning_280.c
@@ -506,7 +506,7 @@ void do_versions_after_linking_280(Main *main)
 
 						soutliner->outlinevis = SO_VIEW_LAYER;
 
-						if (BLI_listbase_count_ex(&layer->layer_collections, 2) == 1) {
+						if (BLI_listbase_count_at_most(&layer->layer_collections, 2) == 1) {
 							if (soutliner->treestore == NULL) {
 								soutliner->treestore = BLI_mempool_create(
 								        sizeof(TreeStoreElem), 1, 512, BLI_MEMPOOL_ALLOW_ITER);
diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c
index c4b29e91fb421b9af01e61f5d8e81838786d8430..7e35866887d40b6ca5450719d79286c0842b01b3 100644
--- a/source/blender/bmesh/intern/bmesh_core.c
+++ b/source/blender/bmesh/intern/bmesh_core.c
@@ -685,7 +685,7 @@ int bmesh_elem_check(void *element, const char htype)
 						err |= IS_FACE_LOOP_WRONG_RADIAL_LENGTH;
 					}
 
-					if (bmesh_disk_count_ex(l_iter->v, 2) < 2) {
+					if (bmesh_disk_count_at_most(l_iter->v, 2) < 2) {
 						err |= IS_FACE_LOOP_WRONG_DISK_LENGTH;
 					}
 				}
@@ -1785,7 +1785,7 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(
 		return NULL;
 	}
 	
-	if (bmesh_disk_count_ex(v_kill, 3) == 2) {
+	if (bmesh_disk_count_at_most(v_kill, 3) == 2) {
 #ifndef NDEBUG
 		int valence1, valence2;
 		BMLoop *l;
diff --git a/source/blender/bmesh/intern/bmesh_mesh.c b/source/blender/bmesh/intern/bmesh_mesh.c
index ef7b54195ffc56b320b6ca67f74a5492ffd80020..9e03c28ba1b8f538098d45ad97a317c8c876a8ef 100644
--- a/source/blender/bmesh/intern/bmesh_mesh.c
+++ b/source/blender/bmesh/intern/bmesh_mesh.c
@@ -1042,7 +1042,7 @@ void BM_edges_sharp_from_angle_set(BMesh *bm, const float split_angle)
 }
 
 static void UNUSED_FUNCTION(bm_mdisps_space_set)(
-        Object *ob, BMesh *bm, int from, int to, eObjectMode object_mode)
+        Object *ob, BMesh *bm, int from, int to)
 {
 	/* switch multires data out of tangent space */
 	if (CustomData_has_layer(&bm->ldata, CD_MDISPS)) {
@@ -1053,7 +1053,7 @@ static void UNUSED_FUNCTION(bm_mdisps_space_set)(
 		BMIter iter;
 		// int i = 0; // UNUSED
 		
-		multires_set_space(dm, ob, from, to, object_mode);
+		multires_set_space(dm, ob, from, to);
 		
 		mdisps = CustomData_get_layer(&dm->loopData, CD_MDISPS);
 		
diff --git a/source/blender/bmesh/intern/bmesh_mods.c b/source/blender/bmesh/intern/bmesh_mods.c
index 961cc4587840bd4abcfab486bf8475185ba14955..4290f94bba18bf2fb1f27939e5507759c880ca39 100644
--- a/source/blender/bmesh/intern/bmesh_mods.c
+++ b/source/blender/bmesh/intern/bmesh_mods.c
@@ -65,7 +65,7 @@
 bool BM_vert_dissolve(BMesh *bm, BMVert *v)
 {
 	/* logic for 3 or more is identical */
-	const int len = BM_vert_edge_count_ex(v, 3);
+	const int len = BM_vert_edge_count_at_most(v, 3);
 	
 	if (len == 1) {
 		BM_vert_kill(bm, v); /* will kill edges too */
diff --git a/source/blender/bmesh/intern/bmesh_private.h b/source/blender/bmesh/intern/bmesh_private.h
index 4dcf97e3f356c58e0dbd2297f6591072290380bb..daee22ffe76ab06399ff7b98b09037c24b676742 100644
--- a/source/blender/bmesh/intern/bmesh_private.h
+++ b/source/blender/bmesh/intern/bmesh_private.h
@@ -55,7 +55,7 @@ int bmesh_elem_check(void *element, const char htype);
 #endif
 
 int bmesh_radial_length(const BMLoop *l);
-int bmesh_disk_count_ex(const BMVert *v, const int count_max);
+int bmesh_disk_count_at_most(const BMVert *v, const int count_max);
 int bmesh_disk_count(const BMVert *v);
 
 /**
diff --git a/source/blender/bmesh/intern/bmesh_queries.c b/source/blender/bmesh/intern/bmesh_queries.c
index 3a76f4ca271fa926f4ba5bbfcaba5b5c7936905d..ab2f017d103a011e7f600e5873325b559101a829 100644
--- a/source/blender/bmesh/intern/bmesh_queries.c
+++ b/source/blender/bmesh/intern/bmesh_queries.c
@@ -799,9 +799,9 @@ int BM_vert_edge_count(const BMVert *v)
 	return bmesh_disk_count(v);
 }
 
-int BM_vert_edge_count_ex(const BMVert *v, const int count_max)
+int BM_vert_edge_count_at_most(const BMVert *v, const int count_max)
 {
-	return bmesh_disk_count_ex(v, count_max);
+	return bmesh_disk_count_at_most(v, count_max);
 }
 
 int BM_vert_edge_count_nonwire(const BMVert *v)
@@ -835,7 +835,7 @@ int BM_edge_face_count(const BMEdge *e)
 	return count;
 }
 
-int BM_edge_face_count_ex(const BMEdge *e, const int count_max)
+int BM_edge_face_count_at_most(const BMEdge *e, const int count_max)
 {
 	int count = 0;
 
@@ -863,9 +863,9 @@ int BM_vert_face_count(const BMVert *v)
 	return bmesh_disk_facevert_count(v);
 }
 
-int BM_vert_face_count_ex(const BMVert *v, int count_max)
+int BM_vert_face_count_at_most(const BMVert *v, int count_max)
 {
-	return bmesh_disk_facevert_count_ex(v, count_max);
+	return bmesh_disk_facevert_count_at_most(v, count_max);
 }
 
 /**
@@ -1044,7 +1044,7 @@ static int bm_loop_region_count__clear(BMLoop *l)
 /**
  * The number of loops connected to this loop (not including disconnected regions).
  */
-int BM_loop_region_loops_count_ex(BMLoop *l, int *r_loop_total)
+int BM_loop_region_loops_count_at_most(BMLoop *l, int *r_loop_total)
 {
 	const int count       = bm_loop_region_count__recursive(l->e, l->v);
 	const int count_total = bm_loop_region_count__clear(l);
@@ -1059,7 +1059,7 @@ int BM_loop_region_loops_count_ex(BMLoop *l, int *r_loop_total)
 
 int BM_loop_region_loops_count(BMLoop *l)
 {
-	return BM_loop_region_loops_count_ex(l, NULL);
+	return BM_loop_region_loops_count_at_most(l, NULL);
 }
 
 /**
@@ -1071,7 +1071,7 @@ bool BM_vert_is_manifold_region(const BMVert *v)
 	BMLoop *l_first = BM_vert_find_first_loop((BMVert *)v);
 	if (l_first) {
 		int count, count_total;
-		count = BM_loop_region_loops_count_ex(l_first, &count_total);
+		count = BM_loop_region_loops_count_at_most(l_first, &count_total);
 		return (count == count_total);
 	}
 	return true;
diff --git a/source/blender/bmesh/intern/bmesh_queries.h b/source/blender/bmesh/intern/bmesh_queries.h
index c9fce96c798f163624bca27961fd191d5300bfab..e602c63da9481aa88758461d924b3a94dfd5b08c 100644
--- a/source/blender/bmesh/intern/bmesh_queries.h
+++ b/source/blender/bmesh/intern/bmesh_queries.h
@@ -70,17 +70,17 @@ BMFace *BM_edge_pair_share_face_by_len(
         const bool allow_adjacent) ATTR_NONNULL();
 
 int     BM_vert_edge_count_nonwire(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
-#define BM_vert_edge_count_is_equal(v, n) (BM_vert_edge_count_ex(v, (n) + 1) == n)
-#define BM_vert_edge_count_is_over(v, n) (BM_vert_edge_count_ex(v, (n) + 1) == (n) + 1)
-int     BM_vert_edge_count_ex(const BMVert *v, const int count_max) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
+#define BM_vert_edge_count_is_equal(v, n) (BM_vert_edge_count_at_most(v, (n) + 1) == n)
+#define BM_vert_edge_count_is_over(v, n) (BM_vert_edge_count_at_most(v, (n) + 1) == (n) + 1)
+int     BM_vert_edge_count_at_most(const BMVert *v, const int count_max) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
 int     BM_vert_edge_count(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
-#define BM_edge_face_count_is_equal(e, n) (BM_edge_face_count_ex(e, (n) + 1) == n)
-#define BM_edge_face_count_is_over(e, n) (BM_edge_face_count_ex(e, (n) + 1) == (n) + 1)
-int     BM_edge_face_count_ex(const BMEdge *e, const int count_max) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
+#define BM_edge_face_count_is_equal(e, n) (BM_edge_face_count_at_most(e, (n) + 1) == n)
+#define BM_edge_face_count_is_over(e, n) (BM_edge_face_count_at_most(e, (n) + 1) == (n) + 1)
+int     BM_edge_face_count_at_most(const BMEdge *e, const int count_max) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
 int     BM_edge_face_count(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
-#define BM_vert_face_count_is_equal(v, n) (BM_vert_face_count_ex(v, (n) + 1) == n)
-#define BM_vert_face_count_is_over(v, n) (BM_vert_face_count_ex(v, (n) + 1) == (n) + 1)
-int     BM_vert_face_count_ex(const BMVert *v, int count_max) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
+#define BM_vert_face_count_is_equal(v, n) (BM_vert_face_count_at_most(v, (n) + 1) == n)
+#define BM_vert_face_count_is_over(v, n) (BM_vert_face_count_at_most(v, (n) + 1) == (n) + 1)
+int     BM_vert_face_count_at_most(const BMVert *v, int count_max) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
 int     BM_vert_face_count(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
 BMEdge *BM_vert_other_disk_edge(BMVert *v, BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
 
@@ -103,7 +103,7 @@ bool    BM_edge_is_contiguous_loop_cd(
         const int cd_loop_type, const int cd_loop_offset)
         ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
 
-int     BM_loop_region_loops_count_ex(BMLoop *l, int *r_loop_total) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1);
+int     BM_loop_region_loops_count_at_most(BMLoop *l, int *r_loop_total) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1);
 int     BM_loop_region_loops_count(BMLoop *l) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1);
 bool    BM_loop_is_convex(const BMLoop *l) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
 BLI_INLINE bool BM_loop_is_adjacent(const BMLoop *l_a, const BMLoop *l_b) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
diff --git a/source/blender/bmesh/intern/bmesh_structure.c b/source/blender/bmesh/intern/bmesh_structure.c
index 8e484841568d4a223f1779deec82d01815faa4cb..8aa9502c0f7b07080abd045fa0ad108928c1759b 100644
--- a/source/blender/bmesh/intern/bmesh_structure.c
+++ b/source/blender/bmesh/intern/bmesh_structure.c
@@ -244,7 +244,7 @@ int bmesh_disk_count(const BMVert *v)
 	return count;
 }
 
-int bmesh_disk_count_ex(const BMVert *v, const int count_max)
+int bmesh_disk_count_at_most(const BMVert *v, const int count_max)
 {
 	int count = 0;
 	if (v->e) {
@@ -267,7 +267,7 @@ bool bmesh_disk_validate(int len, BMEdge *e, BMVert *v)
 	if (!BM_vert_in_edge(e, v)) {
 		return false;
 	}
-	if (len == 0 || bmesh_disk_count_ex(v, len + 1) != len) {
+	if (len == 0 || bmesh_disk_count_at_most(v, len + 1) != len) {
 		return false;
 	}
 
@@ -307,7 +307,7 @@ int bmesh_disk_facevert_count(const BMVert *v)
 	return count;
 }
 
-int bmesh_disk_facevert_count_ex(const BMVert *v, const int count_max)
+int bmesh_disk_facevert_count_at_most(const BMVert *v, const int count_max)
 {
 	/* is there an edge on this vert at all */
 	int count = 0;
@@ -318,7 +318,7 @@ int bmesh_disk_facevert_count_ex(const BMVert *v, const int count_max)
 		e_first = e_iter = v->e;
 		do {
 			if (e_iter->l) {
-				count += bmesh_radial_facevert_count_ex(e_iter->l, v, count_max - count);
+				count += bmesh_radial_facevert_count_at_most(e_iter->l, v, count_max - count);
 				if (count == count_max) {
 					break;
 				}
@@ -560,7 +560,7 @@ int bmesh_radial_facevert_count(const BMLoop *l, const BMVert *v)
 	return count;
 }
 
-int bmesh_radial_facevert_count_ex(const BMLoop *l, const BMVert *v, const int count_max)
+int bmesh_radial_facevert_count_at_most(const BMLoop *l, const BMVert *v, const int count_max)
 {
 	const BMLoop *l_iter;
 	int count = 0;
diff --git a/source/blender/bmesh/intern/bmesh_structure.h b/source/blender/bmesh/intern/bmesh_structure.h
index 0efb25da37c6126d3dd5cf2c9ceceb5519ff7b08..974b276f8b3ffac4306389a0658cbb4febf71d89 100644
--- a/source/blender/bmesh/intern/bmesh_structure.h
+++ b/source/blender/bmesh/intern/bmesh_structure.h
@@ -49,7 +49,7 @@ BLI_INLINE BMEdge *bmesh_disk_edge_next_safe(const BMEdge *e, const BMVert *v) A
 BLI_INLINE BMEdge *bmesh_disk_edge_prev_safe(const BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
 BLI_INLINE BMEdge *bmesh_disk_edge_next(const BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
 BLI_INLINE BMEdge *bmesh_disk_edge_prev(const BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
-int     bmesh_disk_facevert_count_ex(const BMVert *v, const int count_max) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
+int     bmesh_disk_facevert_count_at_most(const BMVert *v, const int count_max) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
 int     bmesh_disk_facevert_count(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
 BMEdge *bmesh_disk_faceedge_find_first(const BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
 BMLoop *bmesh_disk_faceloop_find_first(const BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
@@ -63,7 +63,7 @@ void    bmesh_radial_loop_unlink(BMLoop *l) ATTR_NONNULL();
  *      bmesh_radial_loop_next(BMLoop *l) / prev.
  * just use member access l->radial_next, l->radial_prev now */
 
-int     bmesh_radial_facevert_count_ex(const BMLoop *l, const BMVert *v, const int count_max) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
+int     bmesh_radial_facevert_count_at_most(const BMLoop *l, const BMVert *v, const int count_max) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
 int     bmesh_radial_facevert_count(const BMLoop *l, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
 bool    bmesh_radial_facevert_check(const BMLoop *l, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
 BMLoop *bmesh_radial_faceloop_find_first(const BMLoop *l, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
diff --git a/source/blender/bmesh/operators/bmo_join_triangles.c b/source/blender/bmesh/operators/bmo_join_triangles.c
index 69198ff35abdbee740c06422dce6e45ddefc1c42..b1053e6d8c2c898c7cd7adf47ca12f4f2d7c6e94 100644
--- a/source/blender/bmesh/operators/bmo_join_triangles.c
+++ b/source/blender/bmesh/operators/bmo_join_triangles.c
@@ -270,7 +270,7 @@ void bmo_join_triangles_exec(BMesh *bm, BMOperator *op)
 	BMFace *f;
 	BMEdge *e;
 	/* data: edge-to-join, sort_value: error weight */
-	struct SortPointerByFloat *jedges;
+	struct SortPtrByFloat *jedges;
 	unsigned i, totedge;
 	uint totedge_tag = 0;
 
diff --git a/source/blender/collada/AnimationExporter.cpp b/source/blender/collada/AnimationExporter.cpp
index 1df0705c855f96c5e5086a75be4f78caf8bc751d..2f692cb0f33e105434b9f8d90249e0cad4445bdf 100644
--- a/source/blender/collada/AnimationExporter.cpp
+++ b/source/blender/collada/AnimationExporter.cpp
@@ -403,7 +403,7 @@ void AnimationExporter::export_morph_animation(Object *ob)
 
 void AnimationExporter::make_anim_frames_from_targets(Object *ob, std::vector<float> &frames )
 {
-	ListBase *conlist = get_active_constraints(this->eval_ctx, ob);
+	ListBase *conlist = get_active_constraints(ob);
 	if (conlist == NULL) return;
 	bConstraint *con;
 	for (con = (bConstraint *)conlist->first; con; con = con->next) {
@@ -1006,7 +1006,7 @@ std::string AnimationExporter::create_source_from_fcurve(COLLADASW::InputSemanti
 void AnimationExporter::evaluate_anim_with_constraints(Object *ob, float ctime)
 {
 	BKE_animsys_evaluate_animdata(scene, &ob->id, ob->adt, ctime, ADT_RECALC_ALL);
-	ListBase *conlist = get_active_constraints(this->eval_ctx, ob);
+	ListBase *conlist = get_active_constraints(ob);
 	bConstraint *con;
 	for (con = (bConstraint *)conlist->first; con; con = con->next) {
 		ListBase targets = { NULL, NULL };
diff --git a/source/blender/collada/AnimationImporter.cpp b/source/blender/collada/AnimationImporter.cpp
index ee3fddbf5b7fc01cfd8eae2002caac086741d824..b12ecb50e32f64fbc40e21cb3af25ce6e04d3a08 100644
--- a/source/blender/collada/AnimationImporter.cpp
+++ b/source/blender/collada/AnimationImporter.cpp
@@ -109,7 +109,7 @@ void AnimationImporter::animation_to_fcurves(COLLADAFW::AnimationCurve *curve)
 				fcu->flag = (FCURVE_VISIBLE | FCURVE_AUTO_HANDLES | FCURVE_SELECTED);
 				// fcu->rna_path = BLI_strdupn(path, strlen(path));
 				fcu->array_index = 0;
-				fcu->totvert = curve->getKeyCount();
+				//fcu->totvert = curve->getKeyCount();
 
 				// create beztriple for each key
 				for (unsigned int j = 0; j < curve->getKeyCount(); j++) {
@@ -592,6 +592,12 @@ void AnimationImporter:: Assign_color_animations(const COLLADAFW::UniqueId& list
 	BLI_strncpy(rna_path, anim_type, sizeof(rna_path));
 
 	const COLLADAFW::AnimationList *animlist = animlist_map[listid];
+	if (animlist == NULL)
+	{
+		fprintf(stderr, "Collada: No animlist found for ID: %s of type %s\n", listid.toAscii().c_str(), anim_type);
+		return;
+	}
+
 	const COLLADAFW::AnimationList::AnimationBindings& bindings = animlist->getAnimationBindings();
 	//all the curves belonging to the current binding
 	std::vector<FCurve *> animcurves;
@@ -669,6 +675,13 @@ void AnimationImporter:: Assign_float_animations(const COLLADAFW::UniqueId& list
 	
 }
 
+float AnimationImporter::convert_to_focal_length(float in_xfov, int fov_type, float aspect, float sensorx)
+{
+	// NOTE: Needs more testing (As we curretnly have no official test data for this)
+	float xfov = (fov_type == CAMERA_YFOV) ? (2.0f * atanf(aspect * tanf(DEG2RADF(in_xfov) * 0.5f))) : DEG2RADF(in_xfov);
+	return fov_to_focallength(xfov, sensorx);
+}
+
 /*
  * Lens animations must be stored in COLLADA by using FOV,
  * while blender internally uses focal length.
@@ -698,13 +711,9 @@ void AnimationImporter::Assign_lens_animations(const COLLADAFW::UniqueId& listid
 				FCurve *fcu = *iter;
 				
 				for (unsigned int i = 0; i < fcu->totvert; i++) {
-
-					double input_fov = fcu->bezt[i].vec[1][1];
-
-					// NOTE: Needs more testing (As we curretnly have no official test data for this)
-					double xfov = (fov_type == CAMERA_YFOV) ? (2.0f * atanf(aspect * tanf(DEG2RADF(input_fov) * 0.5f))) : DEG2RADF(input_fov);
-
-					fcu->bezt[i].vec[1][1] = fov_to_focallength(xfov, cam->sensor_x);
+					fcu->bezt[i].vec[0][1] = convert_to_focal_length(fcu->bezt[i].vec[0][1], fov_type, aspect, cam->sensor_x);
+					fcu->bezt[i].vec[1][1] = convert_to_focal_length(fcu->bezt[i].vec[1][1], fov_type, aspect, cam->sensor_x);
+					fcu->bezt[i].vec[2][1] = convert_to_focal_length(fcu->bezt[i].vec[2][1], fov_type, aspect, cam->sensor_x);
 				}
 
 				BLI_addtail(AnimCurves, fcu);
@@ -886,11 +895,22 @@ static const double get_aspect_ratio(const COLLADAFW::Camera *camera)
 	return aspect;
 }
 
+static ListBase &get_animation_curves(Material *ma)
+{
+	bAction *act;
+	if (!ma->adt || !ma->adt->action)
+		act = verify_adt_action((ID *)&ma->id, 1);
+	else
+		act = ma->adt->action;
+
+	return act->curves;
+}
 
 void AnimationImporter::translate_Animations(COLLADAFW::Node *node,
                                              std::map<COLLADAFW::UniqueId, COLLADAFW::Node *>& root_map,
                                              std::multimap<COLLADAFW::UniqueId, Object *>& object_map,
-                                             std::map<COLLADAFW::UniqueId, const COLLADAFW::Object *> FW_object_map)
+                                             std::map<COLLADAFW::UniqueId, const COLLADAFW::Object *> FW_object_map,
+                                             std::map<COLLADAFW::UniqueId, Material*> uid_material_map)
 {
 	bool is_joint = node->getType() == COLLADAFW::Node::JOINT;
 	COLLADAFW::UniqueId uid = node->getUniqueId();
@@ -1068,11 +1088,6 @@ void AnimationImporter::translate_Animations(COLLADAFW::Node *node,
 		}
 	}
 	if (animType->material != 0) {
-		Material *ma = give_current_material(ob, 1);
-		if (!ma->adt || !ma->adt->action) act = verify_adt_action((ID *)&ma->id, 1);
-		else act = ma->adt->action;
-
-		ListBase *AnimCurves = &(act->curves);
 
 		const COLLADAFW::InstanceGeometryPointerArray& nodeGeoms = node->getInstanceGeometries();
 		for (unsigned int i = 0; i < nodeGeoms.getCount(); i++) {
@@ -1081,30 +1096,36 @@ void AnimationImporter::translate_Animations(COLLADAFW::Node *node,
 				const COLLADAFW::UniqueId & matuid = matBinds[j].getReferencedMaterial();
 				const COLLADAFW::Effect *ef = (COLLADAFW::Effect *) (FW_object_map[matuid]);
 				if (ef != NULL) { /* can be NULL [#28909] */
-					const COLLADAFW::CommonEffectPointerArray& commonEffects  =  ef->getCommonEffects();
+					Material *ma = uid_material_map[matuid];
+					if (!ma) {
+						fprintf(stderr, "Collada: Node %s refers to undefined material\n", node->getName().c_str());
+						continue;
+					}
+					ListBase &AnimCurves = get_animation_curves(ma);
+					const COLLADAFW::CommonEffectPointerArray& commonEffects = ef->getCommonEffects();
 					COLLADAFW::EffectCommon *efc = commonEffects[0];
 					if ((animType->material & MATERIAL_SHININESS) != 0) {
 						const COLLADAFW::FloatOrParam *shin = &(efc->getShininess());
-						const COLLADAFW::UniqueId& listid =  shin->getAnimationList();
-						Assign_float_animations(listid, AnimCurves, "specular_hardness");
+						const COLLADAFW::UniqueId& listid = shin->getAnimationList();
+						Assign_float_animations(listid, &AnimCurves, "specular_hardness");
 					}
 
 					if ((animType->material & MATERIAL_IOR) != 0) {
 						const COLLADAFW::FloatOrParam *ior = &(efc->getIndexOfRefraction());
-						const COLLADAFW::UniqueId& listid =  ior->getAnimationList();
-						Assign_float_animations(listid, AnimCurves, "raytrace_transparency.ior");
+						const COLLADAFW::UniqueId& listid = ior->getAnimationList();
+						Assign_float_animations(listid, &AnimCurves, "raytrace_transparency.ior");
 					}
 
 					if ((animType->material & MATERIAL_SPEC_COLOR) != 0) {
 						const COLLADAFW::ColorOrTexture *cot = &(efc->getSpecular());
-						const COLLADAFW::UniqueId& listid =  cot->getColor().getAnimationList();
-						Assign_color_animations(listid, AnimCurves, "specular_color");
+						const COLLADAFW::UniqueId& listid = cot->getColor().getAnimationList();
+						Assign_color_animations(listid, &AnimCurves, "specular_color");
 					}
 
 					if ((animType->material & MATERIAL_DIFF_COLOR) != 0) {
 						const COLLADAFW::ColorOrTexture *cot = &(efc->getDiffuse());
-						const COLLADAFW::UniqueId& listid =  cot->getColor().getAnimationList();
-						Assign_color_animations(listid, AnimCurves, "diffuse_color");
+						const COLLADAFW::UniqueId& listid = cot->getColor().getAnimationList();
+						Assign_color_animations(listid, &AnimCurves, "diffuse_color");
 					}
 				}
 			}
diff --git a/source/blender/collada/AnimationImporter.h b/source/blender/collada/AnimationImporter.h
index 7dc4131dd69971a672bcc28a5054ed4af6323935..e25116cac9f9a809b9b68a8b385dbd0baebd237e 100644
--- a/source/blender/collada/AnimationImporter.h
+++ b/source/blender/collada/AnimationImporter.h
@@ -156,7 +156,8 @@ public:
 	void translate_Animations(COLLADAFW::Node * Node,
 	                          std::map<COLLADAFW::UniqueId, COLLADAFW::Node*>& root_map,
 	                          std::multimap<COLLADAFW::UniqueId, Object*>& object_map,
-	                          std::map<COLLADAFW::UniqueId, const COLLADAFW::Object*> FW_object_map);
+	                          std::map<COLLADAFW::UniqueId, const COLLADAFW::Object*> FW_object_map,
+	                          std::map<COLLADAFW::UniqueId, Material*> uid_material_map);
 
 	AnimMix* get_animation_type( const COLLADAFW::Node * node, std::map<COLLADAFW::UniqueId, const COLLADAFW::Object*> FW_object_map );
 
@@ -202,6 +203,8 @@ public:
 	// gives a world-space mat, end's mat not included
 	bool calc_joint_parent_mat_rest(float mat[4][4], float par[4][4], COLLADAFW::Node *node, COLLADAFW::Node *end);
 
+	float convert_to_focal_length(float in_xfov, int fov_type, float aspect, float sensorx);
+
 #ifdef ARMATURE_TEST
 	Object *get_joint_object(COLLADAFW::Node *root, COLLADAFW::Node *node, Object *par_job);
 #endif
diff --git a/source/blender/collada/DocumentImporter.cpp b/source/blender/collada/DocumentImporter.cpp
index 7999b3c472769613a23f91795c84e8d4b28800b9..97b11f72509b398f376ad52db702b60e9bb6a824 100644
--- a/source/blender/collada/DocumentImporter.cpp
+++ b/source/blender/collada/DocumentImporter.cpp
@@ -317,7 +317,7 @@ void DocumentImporter::translate_anim_recursive(COLLADAFW::Node *node, COLLADAFW
 		translate_anim_recursive(node, node, parob);
 	}
 	else {
-		anim_importer.translate_Animations(node, root_map, object_map, FW_object_map);
+		anim_importer.translate_Animations(node, root_map, object_map, FW_object_map, uid_material_map);
 		COLLADAFW::NodePointerArray &children = node->getChildNodes();
 		for (i = 0; i < children.getCount(); i++) {
 			translate_anim_recursive(children[i], node, NULL);
diff --git a/source/blender/collada/SkinInfo.cpp b/source/blender/collada/SkinInfo.cpp
index 24b47f8db3c038274c582b2b9ca7c40edcc55897..a2cb8237d08a5e423f0137e6baa487047cec7a14 100644
--- a/source/blender/collada/SkinInfo.cpp
+++ b/source/blender/collada/SkinInfo.cpp
@@ -227,7 +227,7 @@ void SkinInfo::link_armature(bContext *C, Object *ob, std::map<COLLADAFW::Unique
 	Main *bmain = CTX_data_main(C);
 	Scene *scene = CTX_data_scene(C);
 
-	ModifierData *md = ED_object_modifier_add(NULL, bmain, scene, ob, OB_MODE_OBJECT, NULL, eModifierType_Armature);
+	ModifierData *md = ED_object_modifier_add(NULL, bmain, scene, ob, NULL, eModifierType_Armature);
 	ArmatureModifierData *amd = (ArmatureModifierData *)md;
 	amd->object = ob_arm;
 
diff --git a/source/blender/compositor/nodes/COM_ImageNode.cpp b/source/blender/compositor/nodes/COM_ImageNode.cpp
index 81891d853d23206126809b6872887bdf2a00a032..1399749fc8d2a02bc615cbcebfbfe461046a0cdc 100644
--- a/source/blender/compositor/nodes/COM_ImageNode.cpp
+++ b/source/blender/compositor/nodes/COM_ImageNode.cpp
@@ -105,7 +105,7 @@ void ImageNode::convertToOperations(NodeConverter &converter, const CompositorCo
 					}
 
 					/* returns the image view to use for the current active view */
-					if (BLI_listbase_count_ex(&image->rr->views, 2) > 1) {
+					if (BLI_listbase_count_at_most(&image->rr->views, 2) > 1) {
 						const int view_image = imageuser->view;
 						const bool is_allview = (view_image == 0); /* if view selected == All (0) */
 
diff --git a/source/blender/depsgraph/DEG_depsgraph.h b/source/blender/depsgraph/DEG_depsgraph.h
index 5cd1b48e80e332bf7477fd3661ad215bf1f3ec0f..43fb83e205bbac61969c0f9ac6229f573ac7fde6 100644
--- a/source/blender/depsgraph/DEG_depsgraph.h
+++ b/source/blender/depsgraph/DEG_depsgraph.h
@@ -75,8 +75,6 @@ typedef enum eEvaluationMode {
 	DAG_EVAL_RENDER         = 2,    /* evaluate for render purposes */
 } eEvaluationMode;
 
-#include "DNA_object_enums.h"
-
 /* Dependency graph evaluation context
  *
  * This structure stores all the local dependency graph data,
@@ -85,7 +83,6 @@ typedef enum eEvaluationMode {
 typedef struct EvaluationContext {
 	eEvaluationMode mode;
 	float ctime;
-	eObjectMode object_mode;
 
 	struct Depsgraph *depsgraph;
 	struct ViewLayer *view_layer;
@@ -221,7 +218,6 @@ void DEG_evaluation_context_init_from_scene(
         struct Scene *scene,
         struct ViewLayer *view_layer,
         struct RenderEngineType *engine_type,
-        const eObjectMode object_mode,
         eEvaluationMode mode);
 
 void DEG_evaluation_context_init_from_view_layer_for_render(
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
index e7a9b4b5a69767732e0d31cd6d4f8c444cf22d82..eff6b34fee6f88deb540da20fafdf75539b34630 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
@@ -118,43 +118,11 @@ namespace DEG {
 
 namespace {
 
-struct BuilderWalkUserData {
-	DepsgraphNodeBuilder *builder;
-};
-
-static void modifier_walk(void *user_data,
-                          struct Object * /*object*/,
-                          struct Object **obpoin,
-                          int /*cb_flag*/)
-{
-	BuilderWalkUserData *data = (BuilderWalkUserData *)user_data;
-	if (*obpoin) {
-		data->builder->build_object(NULL,
-		                            *obpoin,
-		                            DEG_ID_LINKED_INDIRECTLY);
-	}
-}
-
-void constraint_walk(bConstraint * /*con*/,
-                     ID **idpoin,
-                     bool /*is_reference*/,
-                     void *user_data)
-{
-	BuilderWalkUserData *data = (BuilderWalkUserData *)user_data;
-	if (*idpoin) {
-		ID *id = *idpoin;
-		if (GS(id->name) == ID_OB) {
-			data->builder->build_object(NULL,
-			                            (Object *)id,
-			                            DEG_ID_LINKED_INDIRECTLY);
-		}
-	}
-}
-
 void free_copy_on_write_datablock(void *id_v)
 {
 	ID *id = (ID *)id_v;
 	deg_free_copy_on_write_datablock(id);
+	MEM_freeN(id);
 }
 
 }  /* namespace */
@@ -374,6 +342,9 @@ void DepsgraphNodeBuilder::begin_build() {
 		cow_id_hash_ = BLI_ghash_ptr_new("Depsgraph id hash");
 		foreach (IDDepsNode *id_node, graph_->id_nodes) {
 			if (deg_copy_on_write_is_expanded(id_node->id_cow)) {
+				if (id_node->id_orig == id_node->id_cow) {
+					continue;
+				}
 				BLI_ghash_insert(cow_id_hash_,
 				                 id_node->id_orig,
 				                 id_node->id_cow);
@@ -479,7 +450,7 @@ void DepsgraphNodeBuilder::build_object(Base *base,
 	if (object->modifiers.first != NULL) {
 		BuilderWalkUserData data;
 		data.builder = this;
-		modifiers_foreachObjectLink(object, modifier_walk, &data);
+		modifiers_foreachIDLink(object, modifier_walk, &data);
 	}
 	/* Constraints. */
 	if (object->constraints.first != NULL) {
@@ -495,6 +466,11 @@ void DepsgraphNodeBuilder::build_object(Base *base,
 	 * on object's level animation, for example in case of rebuilding
 	 * pose for proxy.
 	 */
+	OperationDepsNode *op_node = add_operation_node(&object->id,
+	                                                DEG_NODE_TYPE_PARAMETERS,
+	                                                NULL,
+	                                                DEG_OPCODE_PARAMETERS_EVAL);
+	op_node->set_as_exit();
 	build_animdata(&object->id);
 	/* Particle systems. */
 	if (object->particlesystem.first != NULL) {
@@ -892,13 +868,6 @@ void DepsgraphNodeBuilder::build_particles(Object *object)
 		 * NOTE: The call itself ensures settings are only build once.
 		 */
 		build_particle_settings(part);
-		/* Update on particle settings change. */
-		add_operation_node(psys_comp,
-		                   function_bind(BKE_particle_system_settings_eval,
-		                                 _1,
-		                                 psys),
-		                   DEG_OPCODE_PARTICLE_SETTINGS_EVAL,
-		                   psys->name);
 		/* Particle system evaluation. */
 		add_operation_node(psys_comp,
 		                   NULL,
@@ -942,12 +911,6 @@ void DepsgraphNodeBuilder::build_particle_settings(ParticleSettings *part) {
 	                   DEG_NODE_TYPE_PARAMETERS,
 	                   NULL,
 	                   DEG_OPCODE_PARTICLE_SETTINGS_EVAL);
-	add_operation_node(&part->id,
-	                   DEG_NODE_TYPE_PARAMETERS,
-	                   function_bind(BKE_particle_system_settings_recalc_clear,
-	                                 _1,
-	                                 part),
-	                   DEG_OPCODE_PARTICLE_SETTINGS_RECALC_CLEAR);
 }
 
 void DepsgraphNodeBuilder::build_cloth(Object *object)
@@ -981,17 +944,6 @@ void DepsgraphNodeBuilder::build_obdata_geom(Object *object)
 	Scene *scene_cow = get_cow_datablock(scene_);
 	Object *object_cow = get_cow_datablock(object);
 
-	/* TODO(sergey): This way using this object's properties as driver target
-	 * works fine.
-	 *
-	 * Does this depend on other nodes?
-	 */
-	op_node = add_operation_node(&object->id,
-	                             DEG_NODE_TYPE_PARAMETERS,
-	                             NULL,
-	                             DEG_OPCODE_PARAMETERS_EVAL);
-	op_node->set_as_exit();
-
 	/* Temporary uber-update node, which does everything.
 	 * It is for the being we're porting old dependencies into the new system.
 	 * We'll get rid of this node as soon as all the granular update functions
@@ -1179,12 +1131,6 @@ void DepsgraphNodeBuilder::build_obdata_geom(Object *object)
 /* Cameras */
 void DepsgraphNodeBuilder::build_camera(Object *object)
 {
-	/* Object itself. */
-	add_operation_node(&object->id,
-	                   DEG_NODE_TYPE_PARAMETERS,
-	                   NULL,
-	                   DEG_OPCODE_PARAMETERS_EVAL,
-	                   "Camera Parameters");
 	/* Object data. */
 	/* TODO: Link scene-camera links in somehow... */
 	Camera *camera = (Camera *)object->data;
@@ -1201,12 +1147,6 @@ void DepsgraphNodeBuilder::build_camera(Object *object)
 /* Lamps */
 void DepsgraphNodeBuilder::build_lamp(Object *object)
 {
-	/* Object itself. */
-	add_operation_node(&object->id,
-	                   DEG_NODE_TYPE_PARAMETERS,
-	                   NULL,
-	                   DEG_OPCODE_PARAMETERS_EVAL,
-	                   "Lamp Parameters");
 	/* Object data. */
 	Lamp *lamp = (Lamp *)object->data;
 	if (built_map_.checkIsBuiltAndTag(lamp)) {
@@ -1342,6 +1282,11 @@ void DepsgraphNodeBuilder::build_texture(Tex *texture)
 			build_image(texture->ima);
 		}
 	}
+	/* Placeholder so we can add relations and tag ID node for update. */
+	add_operation_node(&texture->id,
+	                   DEG_NODE_TYPE_PARAMETERS,
+	                   NULL,
+	                   DEG_OPCODE_PLACEHOLDER);
 }
 
 void DepsgraphNodeBuilder::build_image(Image *image) {
@@ -1446,4 +1391,53 @@ void DepsgraphNodeBuilder::build_lightprobe(Object *object)
 	build_animdata(&probe->id);
 }
 
+/* **** ID traversal callbacks functions **** */
+
+void DepsgraphNodeBuilder::modifier_walk(void *user_data,
+                                         struct Object * /*object*/,
+                                         struct ID **idpoin,
+                                         int /*cb_flag*/)
+{
+	BuilderWalkUserData *data = (BuilderWalkUserData *)user_data;
+	ID *id = *idpoin;
+	if (id == NULL) {
+		return;
+	}
+	switch (GS(id->name)) {
+		case ID_OB:
+			data->builder->build_object(NULL,
+			                            (Object *)id,
+			                            DEG_ID_LINKED_INDIRECTLY);
+			break;
+		case ID_TE:
+			data->builder->build_texture((Tex *)id);
+			break;
+		default:
+			/* pass */
+			break;
+	}
+}
+
+void DepsgraphNodeBuilder::constraint_walk(bConstraint * /*con*/,
+                                           ID **idpoin,
+                                           bool /*is_reference*/,
+                                           void *user_data)
+{
+	BuilderWalkUserData *data = (BuilderWalkUserData *)user_data;
+	ID *id = *idpoin;
+	if (id == NULL) {
+		return;
+	}
+	switch (GS(id->name)) {
+		case ID_OB:
+			data->builder->build_object(NULL,
+			                            (Object *)id,
+			                            DEG_ID_LINKED_INDIRECTLY);
+			break;
+		default:
+			/* pass */
+			break;
+	}
+}
+
 }  // namespace DEG
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
index 49cccb60843939151887aa6f4f008a0c27a0ec56..fd72ae527b84a027d913dd70863ebb16b46f174c 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
@@ -180,7 +180,7 @@ struct DepsgraphNodeBuilder {
 	void build_object_data(Object *object);
 	void build_object_transform(Object *object);
 	void build_object_constraints(Object *object);
-	void build_pose_constraints(Object *object, bPoseChannel *pchan);
+	void build_pose_constraints(Object *object, bPoseChannel *pchan, int pchan_index);
 	void build_rigidbody(Scene *scene);
 	void build_particles(Object *object);
 	void build_particle_settings(ParticleSettings *part);
@@ -233,6 +233,20 @@ protected:
 	};
 	vector<SavedEntryTag> saved_entry_tags_;
 
+	struct BuilderWalkUserData {
+		DepsgraphNodeBuilder *builder;
+	};
+
+	static void modifier_walk(void *user_data,
+	                          struct Object *object,
+	                          struct ID **idpoin,
+	                          int cb_flag);
+
+	static void constraint_walk(bConstraint *constraint,
+	                            ID **idpoin,
+	                            bool is_reference,
+	                            void *user_data);
+
 	/* State which never changes, same for the whole builder time. */
 	Main *bmain_;
 	Depsgraph *graph_;
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc
index aabcbd1c6d4c0009d7f7b0af23ac16c97e89fd89..c438efc1644d639f33c8ed73995be6bdbeb776dd 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc
@@ -50,6 +50,7 @@ extern "C" {
 
 #include "BKE_action.h"
 #include "BKE_armature.h"
+#include "BKE_constraint.h"
 } /* extern "C" */
 
 #include "DEG_depsgraph.h"
@@ -67,15 +68,20 @@ extern "C" {
 namespace DEG {
 
 void DepsgraphNodeBuilder::build_pose_constraints(Object *object,
-                                                  bPoseChannel *pchan)
+                                                  bPoseChannel *pchan,
+                                                  int pchan_index)
 {
-	/* create node for constraint stack */
+	/* Pull indirect dependencies via constraints. */
+	BuilderWalkUserData data;
+	data.builder = this;
+	BKE_constraints_id_loop(&pchan->constraints, constraint_walk, &data);
+	/* Create node for constraint stack. */
 	add_operation_node(&object->id, DEG_NODE_TYPE_BONE, pchan->name,
 	                   function_bind(BKE_pose_constraints_evaluate,
 	                                 _1,
 	                                 get_cow_datablock(scene_),
 	                                 get_cow_datablock(object),
-	                                 pchan),
+	                                 pchan_index),
 	                   DEG_OPCODE_BONE_CONSTRAINTS);
 }
 
@@ -98,13 +104,15 @@ void DepsgraphNodeBuilder::build_ik_pose(Object *object,
 		return;
 	}
 
+	int rootchan_index = BLI_findindex(&object->pose->chanbase, rootchan);
+	BLI_assert(rootchan_index != -1);
 	/* Operation node for evaluating/running IK Solver. */
 	add_operation_node(&object->id, DEG_NODE_TYPE_EVAL_POSE, rootchan->name,
 	                   function_bind(BKE_pose_iktree_evaluate,
 	                                 _1,
 	                                 get_cow_datablock(scene_),
 	                                 get_cow_datablock(object),
-	                                 rootchan),
+	                                 rootchan_index),
 	                   DEG_OPCODE_POSE_IK_SOLVER);
 }
 
@@ -122,12 +130,14 @@ void DepsgraphNodeBuilder::build_splineik_pose(Object *object,
 	 * Store the "root bone" of this chain in the solver, so it knows where to
 	 * start.
 	 */
+	int rootchan_index = BLI_findindex(&object->pose->chanbase, rootchan);
+	BLI_assert(rootchan_index != -1);
 	add_operation_node(&object->id, DEG_NODE_TYPE_EVAL_POSE, rootchan->name,
 	                   function_bind(BKE_pose_splineik_evaluate,
 	                                 _1,
 	                                 get_cow_datablock(scene_),
 	                                 get_cow_datablock(object),
-	                                 rootchan),
+	                                 rootchan_index),
 	                   DEG_OPCODE_POSE_SPLINE_IK_SOLVER);
 }
 
@@ -137,19 +147,13 @@ void DepsgraphNodeBuilder::build_rig(Object *object)
 	bArmature *armature = (bArmature *)object->data;
 	Scene *scene_cow;
 	Object *object_cow;
-	bArmature *armature_cow;
 	if (DEG_depsgraph_use_copy_on_write()) {
-		/* NOTE: We need to expand both object and armature, so this way we can
-		 * safely create object level pose.
-		 */
 		scene_cow = get_cow_datablock(scene_);
-		object_cow = expand_cow_datablock(object);
-		armature_cow = expand_cow_datablock(armature);
+		object_cow = get_cow_datablock(object);
 	}
 	else {
 		scene_cow = scene_;
 		object_cow = object;
-		armature_cow = armature;
 	}
 	OperationDepsNode *op_node;
 
@@ -173,22 +177,22 @@ void DepsgraphNodeBuilder::build_rig(Object *object)
 	}
 
 	/* Rebuild pose if not up to date. */
-	if (object_cow->pose == NULL || (object->pose->flag & POSE_RECALC)) {
-		BKE_pose_rebuild(object_cow, armature_cow);
+	if (object->pose == NULL || (object->pose->flag & POSE_RECALC)) {
+		BKE_pose_rebuild(object, armature);
 		/* XXX: Without this animation gets lost in certain circumstances
 		 * after loading file. Need to investigate further since it does
 		 * not happen with simple scenes..
 		 */
-		if (object_cow->adt) {
-			object_cow->adt->recalc |= ADT_RECALC_ANIM;
+		if (object->adt) {
+			object->adt->recalc |= ADT_RECALC_ANIM;
 		}
 	}
 
 	/* speed optimization for animation lookups */
-	if (object_cow->pose != NULL) {
-		BKE_pose_channels_hash_make(object_cow->pose);
-		if (object_cow->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) {
-			BKE_pose_update_constraint_flags(object_cow->pose);
+	if (object->pose != NULL) {
+		BKE_pose_channels_hash_make(object->pose);
+		if (object->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) {
+			BKE_pose_update_constraint_flags(object->pose);
 		}
 	}
 
@@ -220,8 +224,7 @@ void DepsgraphNodeBuilder::build_rig(Object *object)
 	                             function_bind(BKE_pose_eval_init,
 	                                           _1,
 	                                           scene_cow,
-	                                           object_cow,
-	                                           object_cow->pose),
+	                                           object_cow),
 	                             DEG_OPCODE_POSE_INIT);
 	op_node->set_as_entry();
 
@@ -230,8 +233,7 @@ void DepsgraphNodeBuilder::build_rig(Object *object)
 	                             function_bind(BKE_pose_eval_init_ik,
 	                                           _1,
 	                                           scene_cow,
-	                                           object_cow,
-	                                           object_cow->pose),
+	                                           object_cow),
 	                             DEG_OPCODE_POSE_INIT_IK);
 
 	op_node = add_operation_node(&object->id,
@@ -239,13 +241,13 @@ void DepsgraphNodeBuilder::build_rig(Object *object)
 	                             function_bind(BKE_pose_eval_flush,
 	                                           _1,
 	                                           scene_cow,
-	                                           object_cow,
-	                                           object_cow->pose),
+	                                           object_cow),
 	                             DEG_OPCODE_POSE_DONE);
 	op_node->set_as_exit();
 
 	/* bones */
-	LISTBASE_FOREACH (bPoseChannel *, pchan, &object_cow->pose->chanbase) {
+	int pchan_index = 0;
+	LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) {
 		/* Node for bone evaluation. */
 		op_node = add_operation_node(&object->id, DEG_NODE_TYPE_BONE, pchan->name, NULL,
 		                             DEG_OPCODE_BONE_LOCAL);
@@ -255,7 +257,7 @@ void DepsgraphNodeBuilder::build_rig(Object *object)
 		                   function_bind(BKE_pose_eval_bone, _1,
 		                                 scene_cow,
 		                                 object_cow,
-		                                 pchan),
+		                                 pchan_index),
 		                   DEG_OPCODE_BONE_POSE_PARENT);
 
 		/* NOTE: Dedicated noop for easier relationship construction. */
@@ -264,7 +266,10 @@ void DepsgraphNodeBuilder::build_rig(Object *object)
 		                   DEG_OPCODE_BONE_READY);
 
 		op_node = add_operation_node(&object->id, DEG_NODE_TYPE_BONE, pchan->name,
-		                             function_bind(BKE_pose_bone_done, _1, pchan),
+		                             function_bind(BKE_pose_bone_done,
+		                                           _1,
+		                                           object_cow,
+		                                           pchan_index),
 		                             DEG_OPCODE_BONE_DONE);
 		op_node->set_as_exit();
 		/* Custom properties. */
@@ -277,7 +282,7 @@ void DepsgraphNodeBuilder::build_rig(Object *object)
 		}
 		/* Build constraints. */
 		if (pchan->constraints.first != NULL) {
-			build_pose_constraints(object, pchan);
+			build_pose_constraints(object, pchan, pchan_index);
 		}
 		/**
 		 * IK Solvers.
@@ -305,13 +310,13 @@ void DepsgraphNodeBuilder::build_rig(Object *object)
 					break;
 			}
 		}
+
 		/* Custom shape. */
-		/* NOTE: Custom shape datablock is already remapped to CoW version. */
 		if (pchan->custom != NULL) {
-			build_object(NULL,
-			             get_orig_datablock(pchan->custom),
-			             DEG_ID_LINKED_INDIRECTLY);
+			build_object(NULL, pchan->custom, DEG_ID_LINKED_INDIRECTLY);
 		}
+
+		pchan_index++;
 	}
 }
 
@@ -321,10 +326,7 @@ void DepsgraphNodeBuilder::build_proxy_rig(Object *object)
 	OperationDepsNode *op_node;
 	Object *object_cow;
 	if (DEG_depsgraph_use_copy_on_write()) {
-		/* NOTE: We need to expand both object and armature, so this way we can
-		 * safely create object level pose.
-		 */
-		object_cow = expand_cow_datablock(object);
+		object_cow = get_cow_datablock(object);
 	}
 	else {
 		object_cow = object;
@@ -335,8 +337,8 @@ void DepsgraphNodeBuilder::build_proxy_rig(Object *object)
 	build_animdata(&arm->id);
 	/* speed optimization for animation lookups */
 	BKE_pose_channels_hash_make(object->pose);
-	if (object_cow->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) {
-		BKE_pose_update_constraint_flags(object_cow->pose);
+	if (object->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) {
+		BKE_pose_update_constraint_flags(object->pose);
 	}
 	op_node = add_operation_node(&object->id,
 	                             DEG_NODE_TYPE_EVAL_POSE,
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
index ddb0f809a532feab60aea8560391795ba0fc4863..88c621824664359fde040159b804106125934803 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
@@ -123,12 +123,24 @@ struct BuilderWalkUserData {
 
 void modifier_walk(void *user_data,
                    struct Object * /*object*/,
-                   struct Object **obpoin,
+                   struct ID **idpoin,
                    int /*cb_flag*/)
 {
 	BuilderWalkUserData *data = (BuilderWalkUserData *)user_data;
-	if (*obpoin) {
-		data->builder->build_object(NULL, *obpoin);
+	ID *id = *idpoin;
+	if (id == NULL) {
+		return;
+	}
+	switch (GS(id->name)) {
+		case ID_OB:
+			data->builder->build_object(NULL, (Object *)id);
+			break;
+		case ID_TE:
+			data->builder->build_texture((Tex *)id);
+			break;
+		default:
+			/* pass */
+			break;
 	}
 }
 
@@ -463,7 +475,7 @@ void DepsgraphRelationBuilder::build_object(Base *base, Object *object)
 	if (object->modifiers.first != NULL) {
 		BuilderWalkUserData data;
 		data.builder = this;
-		modifiers_foreachObjectLink(object, modifier_walk, &data);
+		modifiers_foreachIDLink(object, modifier_walk, &data);
 	}
 	/* Constraints. */
 	if (object->constraints.first != NULL) {
@@ -1430,19 +1442,7 @@ void DepsgraphRelationBuilder::build_particles(Object *object)
 		OperationKey particle_settings_key(&part->id,
 		                                   DEG_NODE_TYPE_PARAMETERS,
 		                                   DEG_OPCODE_PARTICLE_SETTINGS_EVAL);
-		OperationKey particle_settings_recalc_clear_key(
-		        &part->id,
-		        DEG_NODE_TYPE_PARAMETERS,
-		        DEG_OPCODE_PARTICLE_SETTINGS_RECALC_CLEAR);
-		OperationKey psys_settings_key(&object->id,
-		                               DEG_NODE_TYPE_EVAL_PARTICLES,
-		                               DEG_OPCODE_PARTICLE_SETTINGS_EVAL,
-		                               psys->name);
-		add_relation(particle_settings_key, psys_settings_key, "Particle Settings Change");
-		add_relation(psys_settings_key, psys_key, "Particle Settings Update");
-		add_relation(psys_key,
-		             particle_settings_recalc_clear_key,
-		             "Particle Settings Recalc Clear");
+		add_relation(particle_settings_key, eval_init_key, "Particle Settings Change");
 		add_relation(eval_init_key, psys_key, "Init -> PSys");
 		/* TODO(sergey): Currently particle update is just a placeholder,
 		 * hook it to the ubereval node so particle system is getting updated
@@ -1543,14 +1543,6 @@ void DepsgraphRelationBuilder::build_particle_settings(ParticleSettings *part)
 	}
 	/* Animation data relations. */
 	build_animdata(&part->id);
-
-	OperationKey eval_key(&part->id,
-	                      DEG_NODE_TYPE_PARAMETERS,
-	                      DEG_OPCODE_PARTICLE_SETTINGS_EVAL);
-	OperationKey recalc_clear_key(&part->id,
-	                             DEG_NODE_TYPE_PARAMETERS,
-	                             DEG_OPCODE_PARTICLE_SETTINGS_RECALC_CLEAR);
-	add_relation(eval_key, recalc_clear_key, "Particle Settings Clear Recalc");
 }
 
 void DepsgraphRelationBuilder::build_particles_visualization_object(
@@ -2081,18 +2073,18 @@ void DepsgraphRelationBuilder::build_copy_on_write_relations(IDDepsNode *id_node
 				graph_->add_new_relation(op_cow, op_node, "CoW Dependency");
 			}
 			else {
-				bool has_same_id_dependency = false;
+				bool has_same_comp_dependency = false;
 				foreach (DepsRelation *rel, op_node->inlinks) {
 					if (rel->from->type != DEG_NODE_TYPE_OPERATION) {
 						continue;
 					}
 					OperationDepsNode *op_node_from = (OperationDepsNode *)rel->from;
-					if (op_node_from->owner->owner == op_node->owner->owner) {
-						has_same_id_dependency = true;
+					if (op_node_from->owner == op_node->owner) {
+						has_same_comp_dependency = true;
 						break;
 					}
 				}
-				if (!has_same_id_dependency) {
+				if (!has_same_comp_dependency) {
 					graph_->add_new_relation(op_cow, op_node, "CoW Dependency");
 				}
 			}
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc
index c8fbc5e2e9a306634dcc886df9e4848319a2dd96..2147ffce7b8b5e6840b29130b67180e42603415c 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc
@@ -431,6 +431,7 @@ void DepsgraphRelationBuilder::build_rig(Object *object)
 
 		/* assume that all bones must be done for the pose to be ready (for deformers) */
 		add_relation(bone_done_key, flush_key, "PoseEval Result-Bone Link");
+
 		/* Custom shape. */
 		if (pchan->custom != NULL) {
 			build_object(NULL, pchan->custom);
diff --git a/source/blender/depsgraph/intern/depsgraph_eval.cc b/source/blender/depsgraph/intern/depsgraph_eval.cc
index d76eba2962899a5ee3c3407fc93d7a2b5f8e47f9..0b3e4fd8db98f9c1ea6ed430b7606489abeab67e 100644
--- a/source/blender/depsgraph/intern/depsgraph_eval.cc
+++ b/source/blender/depsgraph/intern/depsgraph_eval.cc
@@ -84,7 +84,6 @@ void DEG_evaluation_context_init_from_scene(
         Scene *scene,
         ViewLayer *view_layer,
         RenderEngineType *engine_type,
-        eObjectMode object_mode,
         eEvaluationMode mode)
 {
 	DEG_evaluation_context_init(eval_ctx, mode);
@@ -92,7 +91,6 @@ void DEG_evaluation_context_init_from_scene(
 	eval_ctx->view_layer = view_layer;
 	eval_ctx->engine_type = engine_type;
 	eval_ctx->ctime = BKE_scene_frame_get(scene);
-	eval_ctx->object_mode = object_mode;
 }
 
 void DEG_evaluation_context_init_from_view_layer_for_render(
@@ -107,7 +105,6 @@ void DEG_evaluation_context_init_from_view_layer_for_render(
 
 	DEG_evaluation_context_init(eval_ctx, DAG_EVAL_RENDER);
 	eval_ctx->ctime = BKE_scene_frame_get(scene);
-	eval_ctx->object_mode = OB_MODE_OBJECT;
 	eval_ctx->depsgraph = depsgraph;
 	eval_ctx->view_layer = view_layer_original;
 	eval_ctx->engine_type = NULL;
@@ -121,7 +118,6 @@ void DEG_evaluation_context_init_from_depsgraph(
 	Scene *scene = DEG_get_evaluated_scene(depsgraph);
 	DEG_evaluation_context_init(eval_ctx, mode);
 	eval_ctx->ctime = (float)scene->r.cfra + scene->r.subframe;
-	eval_ctx->object_mode = OB_MODE_OBJECT;
 	eval_ctx->depsgraph = depsgraph;
 	eval_ctx->view_layer = DEG_get_evaluated_view_layer(depsgraph);
 	eval_ctx->engine_type = NULL;
diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc
index ea53b18f55bbde28e2a8ec9579d200e524df9b9b..b3796f560838407084ea5de39dc4174b38c45199 100644
--- a/source/blender/depsgraph/intern/depsgraph_tag.cc
+++ b/source/blender/depsgraph/intern/depsgraph_tag.cc
@@ -199,13 +199,7 @@ void depsgraph_tag_to_component_opcode(const ID *id,
 				 *   component. Will be nice to get this unified with object,
 				 *   but we can survive for now with single exception here.
 				 *   Particles needs reconsideration anyway,
-				 * - We do direct injection of particle settings recalc flag
-				 *   here. This is what we need to do for until particles
-				 *   are switched away from own recalc flag and are using
-				 *   ID->recalc flags instead.
 				 */
-				ParticleSettings *particle_settings = (ParticleSettings *)id;
-				particle_settings->recalc |= (tag & DEG_TAG_PSYS_ALL);
 				*component_type = DEG_NODE_TYPE_PARAMETERS;
 			}
 			else {
@@ -403,12 +397,13 @@ void deg_graph_id_tag_update(Main *bmain, Depsgraph *graph, ID *id, int flag)
 	DEG_id_type_tag(bmain, GS(id->name));
 	if (flag == 0) {
 		/* TODO(sergey): Which recalc flags to set here? */
-		id->recalc |= ID_RECALC_ALL;
+		id->recalc |= ID_RECALC_ALL & ~DEG_TAG_PSYS_ALL;
 		if (id_node != NULL) {
 			id_node->tag_update(graph);
 		}
 		deg_graph_id_tag_legacy_compat(bmain, id, (eDepsgraph_Tag)0);
 	}
+	id->recalc |= flag;
 	int current_flag = flag;
 	while (current_flag != 0) {
 		eDepsgraph_Tag tag =
diff --git a/source/blender/depsgraph/intern/depsgraph_type_defines.cc b/source/blender/depsgraph/intern/depsgraph_type_defines.cc
index 1ae1b52b8d2eef26564b612b1b2cd81129e623b4..15144a8f31f4e69c3131e8c763a3688636cd14e9 100644
--- a/source/blender/depsgraph/intern/depsgraph_type_defines.cc
+++ b/source/blender/depsgraph/intern/depsgraph_type_defines.cc
@@ -119,7 +119,6 @@ static const char *stringify_opcode(eDepsOperation_Code opcode)
 		STRINGIFY_OPCODE(PARTICLE_SYSTEM_EVAL_INIT);
 		STRINGIFY_OPCODE(PARTICLE_SYSTEM_EVAL);
 		STRINGIFY_OPCODE(PARTICLE_SETTINGS_EVAL);
-		STRINGIFY_OPCODE(PARTICLE_SETTINGS_RECALC_CLEAR);
 		/* Point Cache. */
 		STRINGIFY_OPCODE(POINT_CACHE_RESET);
 		/* Batch cache. */
diff --git a/source/blender/depsgraph/intern/depsgraph_types.h b/source/blender/depsgraph/intern/depsgraph_types.h
index f8699186866f2c2a83a84ec38ee39086c98ae97c..3aa204490eb7fa82772416f3b14041f2bb46ec00 100644
--- a/source/blender/depsgraph/intern/depsgraph_types.h
+++ b/source/blender/depsgraph/intern/depsgraph_types.h
@@ -43,6 +43,7 @@
  */
 #include <string>
 #include <vector>
+#include <algorithm>
 
 struct bAction;
 struct ChannelDriver;
@@ -239,7 +240,6 @@ typedef enum eDepsOperation_Code {
 	DEG_OPCODE_PARTICLE_SYSTEM_EVAL_INIT,
 	DEG_OPCODE_PARTICLE_SYSTEM_EVAL,
 	DEG_OPCODE_PARTICLE_SETTINGS_EVAL,
-	DEG_OPCODE_PARTICLE_SETTINGS_RECALC_CLEAR,
 
 	/* Point Cache. ------------------------------------- */
 	DEG_OPCODE_POINT_CACHE_RESET,
diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc
index a8fa73654a410f2bab4be5047bd78222eb4abdfe..1355e68097b4b65e2ba70cccfffd1d502e0cd3c0 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval.cc
@@ -230,16 +230,17 @@ static void schedule_children(TaskPool *pool,
 void deg_evaluate_on_refresh(EvaluationContext *eval_ctx,
                              Depsgraph *graph)
 {
+	/* Set time for the current graph evaluation context. */
+	TimeSourceDepsNode *time_src = graph->find_time_source();
+	eval_ctx->ctime = time_src->cfra;
+	eval_ctx->depsgraph = (::Depsgraph *)graph;
+	eval_ctx->view_layer = DEG_get_evaluated_view_layer((::Depsgraph *)graph);
 	/* Nothing to update, early out. */
 	if (BLI_gset_len(graph->entry_tags) == 0) {
 		return;
 	}
 	const bool do_time_debug = ((G.debug & G_DEBUG_DEPSGRAPH_TIME) != 0);
-	/* Set time for the current graph evaluation context. */
-	TimeSourceDepsNode *time_src = graph->find_time_source();
-	eval_ctx->depsgraph = (::Depsgraph *)graph;
-	eval_ctx->view_layer = DEG_get_evaluated_view_layer((::Depsgraph *)graph);
-	eval_ctx->ctime = time_src->cfra;
+	const double start_time = do_time_debug ? PIL_check_seconds_timer() : 0;
 	/* Set up evaluation context for depsgraph itself. */
 	DepsgraphEvalState state;
 	state.eval_ctx = eval_ctx;
@@ -275,6 +276,10 @@ void deg_evaluate_on_refresh(EvaluationContext *eval_ctx,
 	if (need_free_scheduler) {
 		BLI_task_scheduler_free(task_scheduler);
 	}
+	if (do_time_debug) {
+		printf("Depsgraph updated in %f seconds.\n",
+		       PIL_check_seconds_timer() - start_time);
+	}
 }
 
 }  // namespace DEG
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc
index 5be955438010a3e6064a4e6cda152b6ffcf635db..3c40abe6a682d45c0f376dfff2656b80ebf502b6 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc
@@ -317,9 +317,7 @@ BLI_INLINE bool check_datablock_expanded(const ID *id_cow)
 static bool check_datablock_expanded_at_construction(const ID *id_orig)
 {
 	const ID_Type id_type = GS(id_orig->name);
-	return (id_type == ID_SCE) ||
-	       (id_type == ID_OB && ((Object *)id_orig)->type == OB_ARMATURE) ||
-	       (id_type == ID_AR);
+	return (id_type == ID_SCE);
 }
 
 /* Those are datablocks which are not covered by dependency graph and hence
@@ -373,49 +371,50 @@ int foreach_libblock_remap_callback(void *user_data_v,
                                     ID **id_p,
                                     int /*cb_flag*/)
 {
+	if (*id_p == NULL) {
+		return IDWALK_RET_NOP;
+	}
 	RemapCallbackUserData *user_data = (RemapCallbackUserData *)user_data_v;
 	const Depsgraph *depsgraph = user_data->depsgraph;
-	if (*id_p != NULL) {
-		ID *id_orig = *id_p;
-		if (id_orig == user_data->temp_id) {
-			DEG_COW_PRINT("    Remapping datablock for %s: id_temp=%p id_cow=%p\n",
-			              id_orig->name, id_orig, user_data->real_id);
-			*id_p = user_data->real_id;
-		}
-		else if (check_datablocks_copy_on_writable(id_orig)) {
-			ID *id_cow;
-			if (user_data->create_placeholders) {
-				/* Special workaround to stop creating temp datablocks for
-				 * objects which are coming from scene's collection and which
-				 * are never linked to any of layers.
-				 *
-				 * TODO(sergey): Ideally we need to tell ID looper to ignore
-				 * those or at least make it more reliable check where the
-				 * pointer is coming from.
-				 */
-				const ID_Type id_type = GS(id_orig->name);
-				const ID_Type id_type_self = GS(id_self->name);
-				if (id_type == ID_OB && id_type_self == ID_SCE) {
-					IDDepsNode *id_node = depsgraph->find_id_node(id_orig);
-					if (id_node == NULL) {
-						id_cow = id_orig;
-					}
-					else {
-						id_cow = id_node->id_cow;
-					}
+	ID *id_orig = *id_p;
+	if (id_orig == user_data->temp_id) {
+		DEG_COW_PRINT("    Remapping datablock for %s: id_temp=%p id_cow=%p\n",
+		              id_orig->name, id_orig, user_data->real_id);
+		*id_p = user_data->real_id;
+	}
+	else if (check_datablocks_copy_on_writable(id_orig)) {
+		ID *id_cow;
+		if (user_data->create_placeholders) {
+			/* Special workaround to stop creating temp datablocks for
+			 * objects which are coming from scene's collection and which
+			 * are never linked to any of layers.
+			 *
+			 * TODO(sergey): Ideally we need to tell ID looper to ignore
+			 * those or at least make it more reliable check where the
+			 * pointer is coming from.
+			 */
+			const ID_Type id_type = GS(id_orig->name);
+			const ID_Type id_type_self = GS(id_self->name);
+			if (id_type == ID_OB && id_type_self == ID_SCE) {
+				IDDepsNode *id_node = depsgraph->find_id_node(id_orig);
+				if (id_node == NULL) {
+					id_cow = id_orig;
 				}
 				else {
-					id_cow = user_data->node_builder->ensure_cow_id(id_orig);
+					id_cow = id_node->id_cow;
 				}
 			}
 			else {
-				id_cow = depsgraph->get_cow_id(id_orig);
+				id_cow = user_data->node_builder->ensure_cow_id(id_orig);
 			}
-			BLI_assert(id_cow != NULL);
-			DEG_COW_PRINT("    Remapping datablock for %s: id_orig=%p id_cow=%p\n",
-			              id_orig->name, id_orig, id_cow);
-			*id_p = id_cow;
 		}
+		else {
+			id_cow = depsgraph->get_cow_id(id_orig);
+		}
+		BLI_assert(id_cow != NULL);
+		DEG_COW_PRINT("    Remapping datablock for %s: id_orig=%p id_cow=%p\n",
+		              id_orig->name, id_orig, id_cow);
+		*id_p = id_cow;
 	}
 	return IDWALK_RET_NOP;
 }
@@ -436,9 +435,11 @@ void update_special_pointers(const Depsgraph *depsgraph,
 			 * new copy of the object.
 			 */
 			Object *object_cow = (Object *)id_cow;
+			const Object *object_orig = (const Object *)id_orig;
 			(void) object_cow;  /* Ignored for release builds. */
 			BLI_assert(object_cow->derivedFinal == NULL);
 			BLI_assert(object_cow->derivedDeform == NULL);
+			object_cow->mode = object_orig->mode;
 			break;
 		}
 		case ID_ME:
@@ -633,6 +634,7 @@ void update_copy_on_write_object(const Depsgraph * /*depsgraph*/,
 	extract_pose_from_pose(pose_cow, pose_orig);
 	/* Update object itself. */
 	BKE_object_transform_copy(object_cow, object_orig);
+	object_cow->mode = object_orig->mode;
 }
 
 /* Update copy-on-write version of datablock from it's original ID without re-building
diff --git a/source/blender/depsgraph/util/deg_util_function.h b/source/blender/depsgraph/util/deg_util_function.h
index da31bb037f9b0269e90684daec60576adb07de5e..41669919c99c3224823ff09a50036ce27f8bc0d2 100644
--- a/source/blender/depsgraph/util/deg_util_function.h
+++ b/source/blender/depsgraph/util/deg_util_function.h
@@ -30,7 +30,7 @@
 
 #pragma once
 
-#if (__cplusplus > 199711L)
+#if (__cplusplus > 199711L) || (defined(_MSC_VER) && _MSC_VER >= 1900)
 
 #include <functional>
 
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt
index 733e4b0b52412113d42076f4ffe88975003dec7a..eb66e9c20ea49bbc5a654610a1e84570215458a4 100644
--- a/source/blender/draw/CMakeLists.txt
+++ b/source/blender/draw/CMakeLists.txt
@@ -66,7 +66,6 @@ set(SRC
 	intern/draw_manager.c
 	intern/draw_manager_data.c
 	intern/draw_manager_exec.c
-	intern/draw_manager_framebuffer.c
 	intern/draw_manager_shader.c
 	intern/draw_manager_text.c
 	intern/draw_manager_texture.c
@@ -208,6 +207,7 @@ data_to_c_simple(engines/eevee/shaders/volumetric_integration_frag.glsl SRC)
 data_to_c_simple(modes/shaders/common_globals_lib.glsl SRC)
 data_to_c_simple(modes/shaders/common_view_lib.glsl SRC)
 data_to_c_simple(modes/shaders/common_fxaa_lib.glsl SRC)
+data_to_c_simple(modes/shaders/common_fullscreen_vert.glsl SRC)
 data_to_c_simple(modes/shaders/edit_mesh_overlay_frag.glsl SRC)
 data_to_c_simple(modes/shaders/edit_mesh_overlay_vert.glsl SRC)
 data_to_c_simple(modes/shaders/edit_mesh_overlay_geom_tri.glsl SRC)
diff --git a/source/blender/draw/DRW_engine.h b/source/blender/draw/DRW_engine.h
index cc4c0ed10e8437a98e205440806bf30b36005332..cf76bfdeef580e9871e9112baa15f2a018dc117f 100644
--- a/source/blender/draw/DRW_engine.h
+++ b/source/blender/draw/DRW_engine.h
@@ -58,6 +58,8 @@ struct WorkSpace;
 /* Buffer and textures used by the viewport by default */
 typedef struct DefaultFramebufferList {
 	struct GPUFrameBuffer *default_fb;
+	struct GPUFrameBuffer *color_only_fb;
+	struct GPUFrameBuffer *depth_only_fb;
 	struct GPUFrameBuffer *multisample_fb;
 } DefaultFramebufferList;
 
@@ -99,26 +101,26 @@ void DRW_draw_view(const struct bContext *C);
 void DRW_draw_render_loop_ex(
         struct Depsgraph *depsgraph,
         struct RenderEngineType *engine_type,
-        struct ARegion *ar, struct View3D *v3d, const eObjectMode object_mode,
+        struct ARegion *ar, struct View3D *v3d,
         const struct bContext *evil_C);
 void DRW_draw_render_loop(
         struct Depsgraph *depsgraph,
-        struct ARegion *ar, struct View3D *v3d, const eObjectMode object_mode);
+        struct ARegion *ar, struct View3D *v3d);
 void DRW_draw_render_loop_offscreen(
         struct Depsgraph *depsgraph,
         struct RenderEngineType *engine_type,
-        struct ARegion *ar, struct View3D *v3d, const eObjectMode object_mode,
+        struct ARegion *ar, struct View3D *v3d,
         const bool draw_background,
         struct GPUOffScreen *ofs,
         struct GPUViewport *viewport);
 void DRW_draw_select_loop(
         struct Depsgraph *depsgraph,
-        struct ARegion *ar, struct View3D *v3d, const eObjectMode object_mode,
+        struct ARegion *ar, struct View3D *v3d,
         bool use_obedit_skip, bool use_nearest, const struct rcti *rect,
         DRW_SelectPassFn select_pass_fn, void *select_pass_user_data);
 void DRW_draw_depth_loop(
         struct Depsgraph *depsgraph,
-        struct ARegion *ar, struct View3D *v3d, const eObjectMode object_mode);
+        struct ARegion *ar, struct View3D *v3d);
 
 /* This is here because GPUViewport needs it */
 void DRW_pass_free(struct DRWPass *pass);
diff --git a/source/blender/draw/engines/basic/basic_engine.c b/source/blender/draw/engines/basic/basic_engine.c
index 31a431e30013b128d006e25732594b2faf51df05..171b9111bac72b6f8aa507802c6ac2e07a40fb2e 100644
--- a/source/blender/draw/engines/basic/basic_engine.c
+++ b/source/blender/draw/engines/basic/basic_engine.c
@@ -46,34 +46,10 @@
 
 /* GPUViewport.storage
  * Is freed everytime the viewport engine changes */
-typedef struct BASIC_Storage {
-	int dummy;
-} BASIC_Storage;
-
 typedef struct BASIC_StorageList {
-	struct BASIC_Storage *storage;
 	struct BASIC_PrivateData *g_data;
 } BASIC_StorageList;
 
-typedef struct BASIC_FramebufferList {
-	/* default */
-	struct GPUFrameBuffer *default_fb;
-	/* engine specific */
-#ifdef USE_DEPTH
-	struct GPUFrameBuffer *dupli_depth;
-#endif
-} BASIC_FramebufferList;
-
-typedef struct BASIC_TextureList {
-	/* default */
-	struct GPUTexture *color;
-#ifdef USE_DEPTH
-	struct GPUTexture *depth;
-	/* engine specific */
-	struct GPUTexture *depth_dup;
-#endif
-} BASIC_TextureList;
-
 typedef struct BASIC_PassList {
 #ifdef USE_DEPTH
 	struct DRWPass *depth_pass;
@@ -84,8 +60,8 @@ typedef struct BASIC_PassList {
 
 typedef struct BASIC_Data {
 	void *engine_type;
-	BASIC_FramebufferList *fbl;
-	BASIC_TextureList *txl;
+	DRWViewportEmptyList *fbl;
+	DRWViewportEmptyList *txl;
 	BASIC_PassList *psl;
 	BASIC_StorageList *stl;
 } BASIC_Data;
@@ -111,12 +87,8 @@ typedef struct BASIC_PrivateData {
 
 /* Functions */
 
-static void basic_engine_init(void *vedata)
+static void basic_engine_init(void *UNUSED(vedata))
 {
-	BASIC_StorageList *stl = ((BASIC_Data *)vedata)->stl;
-	BASIC_TextureList *txl = ((BASIC_Data *)vedata)->txl;
-	BASIC_FramebufferList *fbl = ((BASIC_Data *)vedata)->fbl;
-
 #ifdef USE_DEPTH
 	/* Depth prepass */
 	if (!e_data.depth_sh) {
@@ -128,20 +100,6 @@ static void basic_engine_init(void *vedata)
 	if (!e_data.color_sh) {
 		e_data.color_sh = GPU_shader_get_builtin_shader(GPU_SHADER_3D_UNIFORM_COLOR);
 	}
-
-	if (!stl->storage) {
-		stl->storage = MEM_callocN(sizeof(BASIC_Storage), "BASIC_Storage");
-	}
-
-#ifdef USE_DEPTH
-	if (DRW_state_is_fbo()) {
-		const float *viewport_size = DRW_viewport_size_get();
-		DRWFboTexture tex = {&txl->depth_dup, DRW_TEX_DEPTH_24_STENCIL_8, 0};
-		DRW_framebuffer_init(&fbl->dupli_depth, &draw_engine_basic_type,
-		                     (int)viewport_size[0], (int)viewport_size[1],
-		                     &tex, 1);
-	}
-#endif
 }
 
 static void basic_cache_init(void *vedata)
@@ -204,8 +162,6 @@ static void basic_draw_scene(void *vedata)
 {
 
 	BASIC_PassList *psl = ((BASIC_Data *)vedata)->psl;
-	BASIC_FramebufferList *fbl = ((BASIC_Data *)vedata)->fbl;
-	DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
 	const bool is_select = DRW_state_is_select();
 
 	bool use_color = true;
@@ -228,14 +184,6 @@ static void basic_draw_scene(void *vedata)
 	if (use_depth_cull) {
 		DRW_draw_pass(psl->depth_pass_cull);
 	}
-
-	/* Pass 2 : Duplicate depth */
-	if (use_depth || use_depth_cull) {
-		/* Unless we go for deferred shading we need this to avoid manual depth test and artifacts */
-		if (DRW_state_is_fbo()) {
-			DRW_framebuffer_blit(dfbl->default_fb, fbl->dupli_depth, true, false);
-		}
-	}
 #endif
 
 	/* Pass 3 : Shading */
diff --git a/source/blender/draw/engines/clay/clay_engine.c b/source/blender/draw/engines/clay/clay_engine.c
index 382551b16e40df596b2c08baa20b806a272220b2..4976cd01d1195914b4f445d4f8bb035446824b66 100644
--- a/source/blender/draw/engines/clay/clay_engine.c
+++ b/source/blender/draw/engines/clay/clay_engine.c
@@ -148,14 +148,11 @@ typedef struct CLAY_PassList {
 	struct DRWPass *hair_pass;
 } CLAY_PassList;
 
-typedef struct CLAY_TextureList {
-	struct GPUTexture *color_copy; /* only used if fxaa */
-} CLAY_TextureList;
 
 typedef struct CLAY_Data {
 	void *engine_type;
 	CLAY_FramebufferList *fbl;
-	CLAY_TextureList *txl;
+	DRWViewportEmptyList *txl;
 	CLAY_PassList *psl;
 	CLAY_StorageList *stl;
 } CLAY_Data;
@@ -201,6 +198,7 @@ typedef struct CLAY_PrivateData {
 	struct GPUTexture *depth_tx; /* ref only, not alloced */
 	struct GPUTexture *normal_tx; /* ref only, not alloced */
 	struct GPUTexture *id_tx; /* ref only, not alloced */
+	struct GPUTexture *color_copy; /* ref only, not alloced */
 	bool enable_deferred_path;
 	/* Ssao */
 	float winmat[4][4];
@@ -351,9 +349,9 @@ static struct GPUTexture *create_jitter_texture(int num_samples)
 static void clay_engine_init(void *vedata)
 {
 	CLAY_StorageList *stl = ((CLAY_Data *)vedata)->stl;
-	CLAY_TextureList *txl = ((CLAY_Data *)vedata)->txl;
 	CLAY_FramebufferList *fbl = ((CLAY_Data *)vedata)->fbl;
 	CLAY_ViewLayerData *sldata = CLAY_view_layer_data_get();
+	DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
 
 	/* Create Texture Array */
 	if (!e_data.matcap_array) {
@@ -428,12 +426,6 @@ static void clay_engine_init(void *vedata)
 		e_data.copy_sh = DRW_shader_create_fullscreen(datatoc_clay_copy_glsl, NULL);
 	}
 
-	if (!e_data.hair_sh) {
-		e_data.hair_sh = DRW_shader_create(
-		        datatoc_clay_particle_vert_glsl, NULL, datatoc_clay_particle_strand_frag_glsl,
-		        "#define MAX_MATERIAL 512\n");
-	}
-
 	if (!stl->storage) {
 		stl->storage = MEM_callocN(sizeof(CLAY_Storage), "CLAY_Storage");
 	}
@@ -463,19 +455,29 @@ static void clay_engine_init(void *vedata)
 		}
 	}
 
-	if (DRW_state_is_fbo()) {
+	/* FBO setup */
+	{
 		const float *viewport_size = DRW_viewport_size_get();
-		DRWFboTexture texs[2] = {{&g_data->normal_tx, DRW_TEX_RG_8, DRW_TEX_TEMP},
-		                         {&g_data->id_tx, DRW_TEX_R_16I, DRW_TEX_TEMP}};
-		DRW_framebuffer_init(&fbl->prepass_fb, &draw_engine_clay_type,
-		                     (int)viewport_size[0], (int)viewport_size[1],
-		                     texs, 2);
+		const int size[2] = {(int)viewport_size[0], (int)viewport_size[1]};
+
+		g_data->normal_tx = DRW_texture_pool_query_2D(size[0], size[1], DRW_TEX_RG_8, &draw_engine_clay_type);
+		g_data->id_tx =     DRW_texture_pool_query_2D(size[0], size[1], DRW_TEX_R_16I, &draw_engine_clay_type);
+
+		GPU_framebuffer_ensure_config(&fbl->prepass_fb, {
+			GPU_ATTACHMENT_TEXTURE(dtxl->depth),
+			GPU_ATTACHMENT_TEXTURE(g_data->normal_tx),
+			GPU_ATTACHMENT_TEXTURE(g_data->id_tx)
+		});
 
 		/* For FXAA */
-		DRWFboTexture tex = {&txl->color_copy, DRW_TEX_RGBA_8, DRW_TEX_FILTER};
-		DRW_framebuffer_init(&fbl->antialias_fb, &draw_engine_clay_type,
-		                     (int)viewport_size[0], (int)viewport_size[1],
-		                     &tex, 1);
+		/* TODO(fclem): OPTI: we could merge normal_tx and id_tx into a DRW_TEX_RGBA_8
+		 * and reuse it for the fxaa target. */
+		g_data->color_copy = DRW_texture_pool_query_2D(size[0], size[1], DRW_TEX_RGBA_8, &draw_engine_clay_type);
+
+		GPU_framebuffer_ensure_config(&fbl->antialias_fb, {
+			GPU_ATTACHMENT_NONE,
+			GPU_ATTACHMENT_TEXTURE(g_data->color_copy)
+		});
 	}
 
 	/* SSAO setup */
@@ -576,9 +578,9 @@ static DRWShadingGroup *CLAY_shgroup_deferred_shading_create(DRWPass *pass, CLAY
 {
 	CLAY_ViewLayerData *sldata = CLAY_view_layer_data_get();
 	DRWShadingGroup *grp = DRW_shgroup_create(e_data.clay_deferred_shading_sh, pass);
-	DRW_shgroup_uniform_buffer(grp, "depthtex", &g_data->depth_tx);
-	DRW_shgroup_uniform_buffer(grp, "normaltex", &g_data->normal_tx);
-	DRW_shgroup_uniform_buffer(grp, "idtex", &g_data->id_tx);
+	DRW_shgroup_uniform_texture_ref(grp, "depthtex", &g_data->depth_tx);
+	DRW_shgroup_uniform_texture_ref(grp, "normaltex", &g_data->normal_tx);
+	DRW_shgroup_uniform_texture_ref(grp, "idtex", &g_data->id_tx);
 	DRW_shgroup_uniform_texture(grp, "matcaps", e_data.matcap_array);
 	DRW_shgroup_uniform_texture(grp, "ssao_jitter", sldata->jitter_tx);
 	DRW_shgroup_uniform_block(grp, "samples_block", sldata->sampling_ubo);
@@ -596,10 +598,15 @@ static DRWShadingGroup *CLAY_hair_shgroup_create(DRWPass *pass, int id)
 {
 	CLAY_ViewLayerData *sldata = CLAY_view_layer_data_get();
 
+	if (!e_data.hair_sh) {
+		e_data.hair_sh = DRW_shader_create(
+		        datatoc_clay_particle_vert_glsl, NULL, datatoc_clay_particle_strand_frag_glsl,
+		        "#define MAX_MATERIAL 512\n");
+	}
+
 	DRWShadingGroup *grp = DRW_shgroup_create(e_data.hair_sh, pass);
 	DRW_shgroup_uniform_texture(grp, "matcaps", e_data.matcap_array);
 	DRW_shgroup_uniform_block(grp, "material_block", sldata->mat_ubo);
-	DRW_shgroup_uniform_block(grp, "matcaps_block", sldata->matcaps_ubo);
 	DRW_shgroup_uniform_int(grp, "mat_id", &e_data.ubo_mat_idxs[id], 1);
 
 	return grp;
@@ -809,7 +816,6 @@ static void clay_cache_init(void *vedata)
 	DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
 	CLAY_PassList *psl = ((CLAY_Data *)vedata)->psl;
 	CLAY_StorageList *stl = ((CLAY_Data *)vedata)->stl;
-	CLAY_TextureList *txl = ((CLAY_Data *)vedata)->txl;
 
 	/* Disable AO unless a material needs it. */
 	stl->g_data->enable_deferred_path = false;
@@ -848,13 +854,13 @@ static void clay_cache_init(void *vedata)
 	{
 		psl->fxaa_ps = DRW_pass_create("Fxaa", DRW_STATE_WRITE_COLOR);
 		DRWShadingGroup *grp = DRW_shgroup_create(e_data.fxaa_sh, psl->fxaa_ps);
-		DRW_shgroup_uniform_buffer(grp, "colortex", &dtxl->color);
+		DRW_shgroup_uniform_texture_ref(grp, "colortex", &dtxl->color);
 		DRW_shgroup_uniform_vec2(grp, "invscreenres", DRW_viewport_invert_size_get(), 1);
 		DRW_shgroup_call_add(grp, DRW_cache_fullscreen_quad_get(), NULL);
 
 		psl->copy_ps = DRW_pass_create("Copy", DRW_STATE_WRITE_COLOR);
 		grp = DRW_shgroup_create(e_data.copy_sh, psl->copy_ps);
-		DRW_shgroup_uniform_buffer(grp, "colortex", &txl->color_copy);
+		DRW_shgroup_uniform_texture_ref(grp, "colortex", &stl->g_data->color_copy);
 		DRW_shgroup_call_add(grp, DRW_cache_fullscreen_quad_get(), NULL);
 	}
 }
@@ -956,37 +962,25 @@ static void clay_draw_scene(void *vedata)
 	DRW_draw_pass(psl->hair_pass);
 
 	if (stl->g_data->enable_deferred_path) {
-		if (DRW_state_is_fbo()) {
-			DRW_framebuffer_texture_detach(dtxl->depth);
-			DRW_framebuffer_texture_attach(fbl->prepass_fb, dtxl->depth, 0, 0);
-			DRW_framebuffer_texture_attach(fbl->prepass_fb, stl->g_data->normal_tx, 0, 0);
-			DRW_framebuffer_texture_attach(fbl->prepass_fb, stl->g_data->id_tx, 1, 0);
-			DRW_framebuffer_bind(fbl->prepass_fb);
-			/* We need to clear the id texture unfortunately. */
-			DRW_framebuffer_clear(true, false, false, (float[4]){0.0f, 0.0f, 0.0f, 0.0f}, 0.0f);
-		}
+		GPU_framebuffer_bind(fbl->prepass_fb);
+		/* We need to clear the id texture unfortunately. */
+		const float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+		GPU_framebuffer_clear_color(fbl->prepass_fb, clear_col);
 
 		DRW_draw_pass(psl->clay_pre_cull_ps);
 		DRW_draw_pass(psl->clay_flat_pre_cull_ps);
 		DRW_draw_pass(psl->clay_pre_ps);
 		DRW_draw_pass(psl->clay_flat_pre_ps);
 
-		if (DRW_state_is_fbo()) {
-			DRW_framebuffer_texture_detach(dtxl->depth);
-			DRW_framebuffer_bind(dfbl->default_fb);
-
-			DRW_draw_pass(psl->clay_deferred_ps);
-
-			DRW_framebuffer_texture_attach(dfbl->default_fb, dtxl->depth, 0, 0);
-			DRW_framebuffer_bind(dfbl->default_fb);
-		}
+		GPU_framebuffer_bind(dfbl->color_only_fb);
+		DRW_draw_pass(psl->clay_deferred_ps);
 	}
 
 	if (true) { /* Always on for now. We might want a parameter for this. */
-		DRW_framebuffer_bind(fbl->antialias_fb);
+		GPU_framebuffer_bind(fbl->antialias_fb);
 		DRW_draw_pass(psl->fxaa_ps);
 
-		DRW_framebuffer_bind(dfbl->default_fb);
+		GPU_framebuffer_bind(dfbl->color_only_fb);
 		DRW_draw_pass(psl->copy_ps);
 	}
 }
diff --git a/source/blender/draw/engines/clay/shaders/clay_frag.glsl b/source/blender/draw/engines/clay/shaders/clay_frag.glsl
index f2c6cd5f78079dcaa0b22000d17f15d4d9ddb87d..1939e4b735dd9a02b7db4cae69523e900f50c8f6 100644
--- a/source/blender/draw/engines/clay/shaders/clay_frag.glsl
+++ b/source/blender/draw/engines/clay/shaders/clay_frag.glsl
@@ -225,14 +225,15 @@ void main()
 	vec2 screenco = vec2(gl_FragCoord.xy) * invscreenres;
 
 #ifdef DEFERRED_SHADING
-	mat_id = texture(idtex, screenco).r;
+	ivec2 texel = ivec2(gl_FragCoord.xy);
+	mat_id = texelFetch(idtex, texel, 0).r;
 
 	/* early out (manual stencil test) */
 	if (mat_id == 0)
 		discard;
 
-	float depth = texture(depthtex, screenco).r;
-	vec3 N = normal_decode(texture(normaltex, screenco).rg);
+	float depth = texelFetch(depthtex, texel, 0).r;
+	vec3 N = normal_decode(texelFetch(normaltex, texel, 0).rg);
 	/* see the prepass for explanations. */
 	if (mat_id < 0) {
 		N = -N;
diff --git a/source/blender/draw/engines/clay/shaders/clay_fxaa.glsl b/source/blender/draw/engines/clay/shaders/clay_fxaa.glsl
index 1a34927aaa65677e99dfc931c0ad4feff558ae43..924e51421aa3b11a9e0db3726b6dea99f7b223aa 100644
--- a/source/blender/draw/engines/clay/shaders/clay_fxaa.glsl
+++ b/source/blender/draw/engines/clay/shaders/clay_fxaa.glsl
@@ -7,12 +7,12 @@ uniform sampler2D colortex;
 
 void main()
 {
-	fragColor = FxaaPixelShader(
+	fragColor = vec4(FxaaPixelShader(
 		uvcoordsvar.st,
 		colortex,
 		invscreenres,
 		1.0,
 		0.166,
 		0.0833
-	);
+	).rgb, 1.0);
 }
diff --git a/source/blender/draw/engines/eevee/eevee_bloom.c b/source/blender/draw/engines/eevee/eevee_bloom.c
index c62f35a70e706a4fa43e76ece50a79688fed0e71..89a7aeab4b288aee86107237b72ffd20c7f64503 100644
--- a/source/blender/draw/engines/eevee/eevee_bloom.c
+++ b/source/blender/draw/engines/eevee/eevee_bloom.c
@@ -84,7 +84,6 @@ int EEVEE_bloom_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
 {
 	EEVEE_StorageList *stl = vedata->stl;
 	EEVEE_FramebufferList *fbl = vedata->fbl;
-	EEVEE_TextureList *txl = vedata->txl;
 	EEVEE_EffectsInfo *effects = stl->effects;
 
 	const DRWContextState *draw_ctx = DRW_context_state_get();
@@ -112,10 +111,13 @@ int EEVEE_bloom_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
 		effects->blit_texel_size[0] = 1.0f / (float)blitsize[0];
 		effects->blit_texel_size[1] = 1.0f / (float)blitsize[1];
 
-		DRWFboTexture tex_blit = {&txl->bloom_blit, DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER};
-		DRW_framebuffer_init(&fbl->bloom_blit_fb, &draw_engine_eevee_type,
-		                    (int)blitsize[0], (int)blitsize[1],
-		                    &tex_blit, 1);
+		effects->bloom_blit = DRW_texture_pool_query_2D(blitsize[0], blitsize[1], DRW_TEX_RGB_11_11_10,
+		                                                &draw_engine_eevee_type);
+
+		GPU_framebuffer_ensure_config(&fbl->bloom_blit_fb, {
+			GPU_ATTACHMENT_NONE,
+			GPU_ATTACHMENT_TEXTURE(effects->bloom_blit)
+		});
 
 		/* Parameters */
 		float threshold = BKE_collection_engine_property_value_get_float(props, "bloom_threshold");
@@ -151,10 +153,12 @@ int EEVEE_bloom_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
 			effects->downsamp_texel_size[i][0] = 1.0f / (float)texsize[0];
 			effects->downsamp_texel_size[i][1] = 1.0f / (float)texsize[1];
 
-			DRWFboTexture tex_bloom = {&txl->bloom_downsample[i], DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER};
-			DRW_framebuffer_init(&fbl->bloom_down_fb[i], &draw_engine_eevee_type,
-			                    (int)texsize[0], (int)texsize[1],
-			                    &tex_bloom, 1);
+			effects->bloom_downsample[i] = DRW_texture_pool_query_2D(texsize[0], texsize[1], DRW_TEX_RGB_11_11_10,
+			                                                         &draw_engine_eevee_type);
+			GPU_framebuffer_ensure_config(&fbl->bloom_down_fb[i], {
+				GPU_ATTACHMENT_NONE,
+				GPU_ATTACHMENT_TEXTURE(effects->bloom_downsample[i])
+			});
 		}
 
 		/* Upsample buffers */
@@ -165,30 +169,23 @@ int EEVEE_bloom_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
 			texsize[0] = MAX2(texsize[0], 2);
 			texsize[1] = MAX2(texsize[1], 2);
 
-			DRWFboTexture tex_bloom = {&txl->bloom_upsample[i], DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER};
-			DRW_framebuffer_init(&fbl->bloom_accum_fb[i], &draw_engine_eevee_type,
-			                    (int)texsize[0], (int)texsize[1],
-			                    &tex_bloom, 1);
+			effects->bloom_upsample[i] = DRW_texture_pool_query_2D(texsize[0], texsize[1], DRW_TEX_RGB_11_11_10,
+			                                                       &draw_engine_eevee_type);
+			GPU_framebuffer_ensure_config(&fbl->bloom_accum_fb[i], {
+				GPU_ATTACHMENT_NONE,
+				GPU_ATTACHMENT_TEXTURE(effects->bloom_upsample[i])
+			});
 		}
 
 		return EFFECT_BLOOM | EFFECT_POST_BUFFER;
 	}
 
 	/* Cleanup to release memory */
-	DRW_TEXTURE_FREE_SAFE(txl->bloom_blit);
-	DRW_FRAMEBUFFER_FREE_SAFE(fbl->bloom_blit_fb);
-
-	/* Bloom and dof share this buffer. This
-	 * tells dof to reconfigure it's framebuffer. */
-	if (txl->bloom_downsample[0] != NULL) {
-		DRW_FRAMEBUFFER_FREE_SAFE(fbl->dof_down_fb);
-	}
+	GPU_FRAMEBUFFER_FREE_SAFE(fbl->bloom_blit_fb);
 
 	for (int i = 0; i < MAX_BLOOM_STEP - 1; ++i) {
-		DRW_TEXTURE_FREE_SAFE(txl->bloom_downsample[i]);
-		DRW_TEXTURE_FREE_SAFE(txl->bloom_upsample[i]);
-		DRW_FRAMEBUFFER_FREE_SAFE(fbl->bloom_down_fb[i]);
-		DRW_FRAMEBUFFER_FREE_SAFE(fbl->bloom_accum_fb[i]);
+		GPU_FRAMEBUFFER_FREE_SAFE(fbl->bloom_down_fb[i]);
+		GPU_FRAMEBUFFER_FREE_SAFE(fbl->bloom_accum_fb[i]);
 	}
 
 	return 0;
@@ -203,10 +200,10 @@ static DRWShadingGroup *eevee_create_bloom_pass(
 
 	DRWShadingGroup *grp = DRW_shgroup_create(sh, *pass);
 	DRW_shgroup_call_add(grp, quad, NULL);
-	DRW_shgroup_uniform_buffer(grp, "sourceBuffer", &effects->unf_source_buffer);
+	DRW_shgroup_uniform_texture_ref(grp, "sourceBuffer", &effects->unf_source_buffer);
 	DRW_shgroup_uniform_vec2(grp, "sourceBufferTexelSize", effects->unf_source_texel_size, 1);
 	if (upsample) {
-		DRW_shgroup_uniform_buffer(grp, "baseBuffer", &effects->unf_base_buffer);
+		DRW_shgroup_uniform_texture_ref(grp, "baseBuffer", &effects->unf_base_buffer);
 		DRW_shgroup_uniform_float(grp, "sampleScale", &effects->bloom_sample_scale, 1);
 	}
 
@@ -280,39 +277,39 @@ void EEVEE_bloom_draw(EEVEE_Data *vedata)
 		copy_v2_v2(effects->unf_source_texel_size, effects->source_texel_size);
 		effects->unf_source_buffer = effects->source_buffer;
 
-		DRW_framebuffer_bind(fbl->bloom_blit_fb);
+		GPU_framebuffer_bind(fbl->bloom_blit_fb);
 		DRW_draw_pass(psl->bloom_blit);
 
 		/* Downsample */
 		copy_v2_v2(effects->unf_source_texel_size, effects->blit_texel_size);
-		effects->unf_source_buffer = txl->bloom_blit;
+		effects->unf_source_buffer = effects->bloom_blit;
 
-		DRW_framebuffer_bind(fbl->bloom_down_fb[0]);
+		GPU_framebuffer_bind(fbl->bloom_down_fb[0]);
 		DRW_draw_pass(psl->bloom_downsample_first);
 
-		last = txl->bloom_downsample[0];
+		last = effects->bloom_downsample[0];
 
 		for (int i = 1; i < effects->bloom_iteration_ct; ++i) {
 			copy_v2_v2(effects->unf_source_texel_size, effects->downsamp_texel_size[i - 1]);
 			effects->unf_source_buffer = last;
 
-			DRW_framebuffer_bind(fbl->bloom_down_fb[i]);
+			GPU_framebuffer_bind(fbl->bloom_down_fb[i]);
 			DRW_draw_pass(psl->bloom_downsample);
 
 			/* Used in next loop */
-			last = txl->bloom_downsample[i];
+			last = effects->bloom_downsample[i];
 		}
 
 		/* Upsample and accumulate */
 		for (int i = effects->bloom_iteration_ct - 2; i >= 0; --i) {
 			copy_v2_v2(effects->unf_source_texel_size, effects->downsamp_texel_size[i]);
-			effects->unf_source_buffer = txl->bloom_downsample[i];
+			effects->unf_source_buffer = effects->bloom_downsample[i];
 			effects->unf_base_buffer = last;
 
-			DRW_framebuffer_bind(fbl->bloom_accum_fb[i]);
+			GPU_framebuffer_bind(fbl->bloom_accum_fb[i]);
 			DRW_draw_pass(psl->bloom_upsample);
 
-			last = txl->bloom_upsample[i];
+			last = effects->bloom_upsample[i];
 		}
 
 		/* Resolve */
@@ -320,7 +317,7 @@ void EEVEE_bloom_draw(EEVEE_Data *vedata)
 		effects->unf_source_buffer = last;
 		effects->unf_base_buffer = effects->source_buffer;
 
-		DRW_framebuffer_bind(effects->target_buffer);
+		GPU_framebuffer_bind(effects->target_buffer);
 		DRW_draw_pass(psl->bloom_resolve);
 		SWAP_BUFFERS();
 	}
diff --git a/source/blender/draw/engines/eevee/eevee_data.c b/source/blender/draw/engines/eevee/eevee_data.c
index 0b6ab905e3296f10d02a9197901f05e66d17b868..5adcf9e9ffb8a8acdbd825c10fcd4d7bb60d25f2 100644
--- a/source/blender/draw/engines/eevee/eevee_data.c
+++ b/source/blender/draw/engines/eevee/eevee_data.c
@@ -38,8 +38,9 @@ static void eevee_view_layer_data_free(void *storage)
 	DRW_UBO_FREE_SAFE(sldata->light_ubo);
 	DRW_UBO_FREE_SAFE(sldata->shadow_ubo);
 	DRW_UBO_FREE_SAFE(sldata->shadow_render_ubo);
-	DRW_FRAMEBUFFER_FREE_SAFE(sldata->shadow_target_fb);
-	DRW_FRAMEBUFFER_FREE_SAFE(sldata->shadow_store_fb);
+	GPU_FRAMEBUFFER_FREE_SAFE(sldata->shadow_cube_target_fb);
+	GPU_FRAMEBUFFER_FREE_SAFE(sldata->shadow_cascade_target_fb);
+	GPU_FRAMEBUFFER_FREE_SAFE(sldata->shadow_store_fb);
 	DRW_TEXTURE_FREE_SAFE(sldata->shadow_cube_target);
 	DRW_TEXTURE_FREE_SAFE(sldata->shadow_cube_blur);
 	DRW_TEXTURE_FREE_SAFE(sldata->shadow_cascade_target);
@@ -57,8 +58,10 @@ static void eevee_view_layer_data_free(void *storage)
 	DRW_UBO_FREE_SAFE(sldata->planar_ubo);
 	DRW_UBO_FREE_SAFE(sldata->common_ubo);
 	DRW_UBO_FREE_SAFE(sldata->clip_ubo);
-	DRW_FRAMEBUFFER_FREE_SAFE(sldata->probe_fb);
-	DRW_FRAMEBUFFER_FREE_SAFE(sldata->probe_filter_fb);
+	GPU_FRAMEBUFFER_FREE_SAFE(sldata->probe_filter_fb);
+	for (int i = 0; i < 6; ++i) {
+		GPU_FRAMEBUFFER_FREE_SAFE(sldata->probe_face_fb[i]);
+	}
 	DRW_TEXTURE_FREE_SAFE(sldata->probe_rt);
 	DRW_TEXTURE_FREE_SAFE(sldata->probe_depth_rt);
 	DRW_TEXTURE_FREE_SAFE(sldata->probe_pool);
diff --git a/source/blender/draw/engines/eevee/eevee_depth_of_field.c b/source/blender/draw/engines/eevee/eevee_depth_of_field.c
index dcd45e27337d8eb118ad01da7d5d9480d314a024..c7a94a877a7abb41b470681e85f7b35c8c8cba14 100644
--- a/source/blender/draw/engines/eevee/eevee_depth_of_field.c
+++ b/source/blender/draw/engines/eevee/eevee_depth_of_field.c
@@ -79,7 +79,6 @@ int EEVEE_depth_of_field_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *v
 {
 	EEVEE_StorageList *stl = vedata->stl;
 	EEVEE_FramebufferList *fbl = vedata->fbl;
-	EEVEE_TextureList *txl = vedata->txl;
 	EEVEE_EffectsInfo *effects = stl->effects;
 
 	const DRWContextState *draw_ctx = DRW_context_state_get();
@@ -104,39 +103,36 @@ int EEVEE_depth_of_field_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *v
 
 			int buffer_size[2] = {(int)viewport_size[0] / 2, (int)viewport_size[1] / 2};
 
-			/* Reuse buffer from Bloom if available */
-			/* WATCH IT : must have the same size */
-			struct GPUTexture **dof_down_near;
-
-			if ((effects->enabled_effects & EFFECT_BLOOM) != 0) {
-				dof_down_near = &txl->bloom_downsample[0];
-			}
-			else {
-				dof_down_near = &txl->dof_down_near;
-			}
+			effects->dof_down_near = DRW_texture_pool_query_2D(buffer_size[0], buffer_size[1], DRW_TEX_RGB_11_11_10,
+			                                                   &draw_engine_eevee_type);
+			effects->dof_down_far =  DRW_texture_pool_query_2D(buffer_size[0], buffer_size[1], DRW_TEX_RGB_11_11_10,
+			                                                   &draw_engine_eevee_type);
+			effects->dof_coc =       DRW_texture_pool_query_2D(buffer_size[0], buffer_size[1], DRW_TEX_RG_16,
+			                                                   &draw_engine_eevee_type);
 
-			/* Setup buffers */
-			DRWFboTexture tex_down[3] = {
-				{dof_down_near, DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER}, /* filter to not interfeer with bloom */
-				{&txl->dof_down_far, DRW_TEX_RGB_11_11_10, 0},
-				{&txl->dof_coc, DRW_TEX_RG_16, 0},
-			};
-			DRW_framebuffer_init(
-			        &fbl->dof_down_fb, &draw_engine_eevee_type,
-			        buffer_size[0], buffer_size[1], tex_down, 3);
+			GPU_framebuffer_ensure_config(&fbl->dof_down_fb, {
+				GPU_ATTACHMENT_NONE,
+				GPU_ATTACHMENT_TEXTURE(effects->dof_down_near),
+				GPU_ATTACHMENT_TEXTURE(effects->dof_down_far),
+				GPU_ATTACHMENT_TEXTURE(effects->dof_coc)
+			});
 
 			/* Go full 32bits for rendering and reduce the color artifacts. */
 			DRWTextureFormat fb_format = DRW_state_is_image_render() ? DRW_TEX_RGBA_32 : DRW_TEX_RGBA_16;
 
-			DRWFboTexture tex_scatter_far = {&txl->dof_far_blur, fb_format, DRW_TEX_FILTER};
-			DRW_framebuffer_init(
-			        &fbl->dof_scatter_far_fb, &draw_engine_eevee_type,
-			        buffer_size[0], buffer_size[1], &tex_scatter_far, 1);
+			effects->dof_far_blur = DRW_texture_pool_query_2D(buffer_size[0], buffer_size[1], fb_format,
+			                                                  &draw_engine_eevee_type);
+			GPU_framebuffer_ensure_config(&fbl->dof_scatter_far_fb, {
+				GPU_ATTACHMENT_NONE,
+				GPU_ATTACHMENT_TEXTURE(effects->dof_far_blur),
+			});
 
-			DRWFboTexture tex_scatter_near = {&txl->dof_near_blur, fb_format, DRW_TEX_FILTER};
-			DRW_framebuffer_init(
-			        &fbl->dof_scatter_near_fb, &draw_engine_eevee_type,
-			        buffer_size[0], buffer_size[1], &tex_scatter_near, 1);
+			effects->dof_near_blur = DRW_texture_pool_query_2D(buffer_size[0], buffer_size[1], fb_format,
+			                                                   &draw_engine_eevee_type);
+			GPU_framebuffer_ensure_config(&fbl->dof_scatter_near_fb, {
+				GPU_ATTACHMENT_NONE,
+				GPU_ATTACHMENT_TEXTURE(effects->dof_near_blur),
+			});
 
 			/* Parameters */
 			/* TODO UI Options */
@@ -178,14 +174,9 @@ int EEVEE_depth_of_field_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *v
 	}
 
 	/* Cleanup to release memory */
-	DRW_TEXTURE_FREE_SAFE(txl->dof_down_near);
-	DRW_TEXTURE_FREE_SAFE(txl->dof_down_far);
-	DRW_TEXTURE_FREE_SAFE(txl->dof_coc);
-	DRW_TEXTURE_FREE_SAFE(txl->dof_far_blur);
-	DRW_TEXTURE_FREE_SAFE(txl->dof_near_blur);
-	DRW_FRAMEBUFFER_FREE_SAFE(fbl->dof_down_fb);
-	DRW_FRAMEBUFFER_FREE_SAFE(fbl->dof_scatter_far_fb);
-	DRW_FRAMEBUFFER_FREE_SAFE(fbl->dof_scatter_near_fb);
+	GPU_FRAMEBUFFER_FREE_SAFE(fbl->dof_down_fb);
+	GPU_FRAMEBUFFER_FREE_SAFE(fbl->dof_scatter_far_fb);
+	GPU_FRAMEBUFFER_FREE_SAFE(fbl->dof_scatter_near_fb);
 
 	return 0;
 }
@@ -194,7 +185,6 @@ void EEVEE_depth_of_field_cache_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_
 {
 	EEVEE_PassList *psl = vedata->psl;
 	EEVEE_StorageList *stl = vedata->stl;
-	EEVEE_TextureList *txl = vedata->txl;
 	EEVEE_EffectsInfo *effects = stl->effects;
 	DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
 
@@ -214,8 +204,8 @@ void EEVEE_depth_of_field_cache_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_
 		psl->dof_down = DRW_pass_create("DoF Downsample", DRW_STATE_WRITE_COLOR);
 
 		grp = DRW_shgroup_create(e_data.dof_downsample_sh, psl->dof_down);
-		DRW_shgroup_uniform_buffer(grp, "colorBuffer", &effects->source_buffer);
-		DRW_shgroup_uniform_buffer(grp, "depthBuffer", &dtxl->depth);
+		DRW_shgroup_uniform_texture_ref(grp, "colorBuffer", &effects->source_buffer);
+		DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
 		DRW_shgroup_uniform_vec2(grp, "nearFar", effects->dof_near_far, 1);
 		DRW_shgroup_uniform_vec3(grp, "dofParams", effects->dof_params, 1);
 		DRW_shgroup_call_add(grp, quad, NULL);
@@ -228,18 +218,18 @@ void EEVEE_depth_of_field_cache_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_
 		const int sprite_ct = ((int)viewport_size[0] / 2) * ((int)viewport_size[1] / 2); /* brackets matters */
 		grp = DRW_shgroup_empty_tri_batch_create(e_data.dof_scatter_sh, psl->dof_scatter, sprite_ct);
 
-		DRW_shgroup_uniform_buffer(grp, "colorBuffer", &effects->unf_source_buffer);
-		DRW_shgroup_uniform_buffer(grp, "cocBuffer", &txl->dof_coc);
+		DRW_shgroup_uniform_texture_ref(grp, "colorBuffer", &effects->unf_source_buffer);
+		DRW_shgroup_uniform_texture_ref(grp, "cocBuffer", &effects->dof_coc);
 		DRW_shgroup_uniform_vec2(grp, "layerSelection", effects->dof_layer_select, 1);
 		DRW_shgroup_uniform_vec4(grp, "bokehParams", effects->dof_bokeh, 1);
 
 		psl->dof_resolve = DRW_pass_create("DoF Resolve", DRW_STATE_WRITE_COLOR);
 
 		grp = DRW_shgroup_create(e_data.dof_resolve_sh, psl->dof_resolve);
-		DRW_shgroup_uniform_buffer(grp, "colorBuffer", &effects->source_buffer);
-		DRW_shgroup_uniform_buffer(grp, "nearBuffer", &txl->dof_near_blur);
-		DRW_shgroup_uniform_buffer(grp, "farBuffer", &txl->dof_far_blur);
-		DRW_shgroup_uniform_buffer(grp, "depthBuffer", &dtxl->depth);
+		DRW_shgroup_uniform_texture_ref(grp, "colorBuffer", &effects->source_buffer);
+		DRW_shgroup_uniform_texture_ref(grp, "nearBuffer", &effects->dof_near_blur);
+		DRW_shgroup_uniform_texture_ref(grp, "farBuffer", &effects->dof_far_blur);
+		DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
 		DRW_shgroup_uniform_vec2(grp, "nearFar", effects->dof_near_far, 1);
 		DRW_shgroup_uniform_vec3(grp, "dofParams", effects->dof_params, 1);
 		DRW_shgroup_call_add(grp, quad, NULL);
@@ -259,31 +249,25 @@ void EEVEE_depth_of_field_draw(EEVEE_Data *vedata)
 		float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f};
 
 		/* Downsample */
-		DRW_framebuffer_bind(fbl->dof_down_fb);
+		GPU_framebuffer_bind(fbl->dof_down_fb);
 		DRW_draw_pass(psl->dof_down);
 
 		/* Scatter Far */
-		effects->unf_source_buffer = txl->dof_down_far;
+		effects->unf_source_buffer = effects->dof_down_far;
 		copy_v2_fl2(effects->dof_layer_select, 0.0f, 1.0f);
-		DRW_framebuffer_bind(fbl->dof_scatter_far_fb);
-		DRW_framebuffer_clear(true, false, false, clear_col, 0.0f);
+		GPU_framebuffer_bind(fbl->dof_scatter_far_fb);
+		GPU_framebuffer_clear_color(fbl->dof_scatter_far_fb, clear_col);
 		DRW_draw_pass(psl->dof_scatter);
 
 		/* Scatter Near */
-		if ((effects->enabled_effects & EFFECT_BLOOM) != 0) {
-			/* Reuse bloom half res buffer */
-			effects->unf_source_buffer = txl->bloom_downsample[0];
-		}
-		else {
-			effects->unf_source_buffer = txl->dof_down_near;
-		}
+		effects->unf_source_buffer = effects->dof_down_near;
 		copy_v2_fl2(effects->dof_layer_select, 1.0f, 0.0f);
-		DRW_framebuffer_bind(fbl->dof_scatter_near_fb);
-		DRW_framebuffer_clear(true, false, false, clear_col, 0.0f);
+		GPU_framebuffer_bind(fbl->dof_scatter_near_fb);
+		GPU_framebuffer_clear_color(fbl->dof_scatter_near_fb, clear_col);
 		DRW_draw_pass(psl->dof_scatter);
 
 		/* Resolve */
-		DRW_framebuffer_bind(effects->target_buffer);
+		GPU_framebuffer_bind(effects->target_buffer);
 		DRW_draw_pass(psl->dof_resolve);
 		SWAP_BUFFERS();
 	}
diff --git a/source/blender/draw/engines/eevee/eevee_effects.c b/source/blender/draw/engines/eevee/eevee_effects.c
index 1030fe1ce3ac04745398d4cf688005f4d86fe60b..977eb14a1bb2b11e6de31a9dcf064e08b4d4c5e0 100644
--- a/source/blender/draw/engines/eevee/eevee_effects.c
+++ b/source/blender/draw/engines/eevee/eevee_effects.c
@@ -105,6 +105,7 @@ void EEVEE_effects_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, Object
 	EEVEE_FramebufferList *fbl = vedata->fbl;
 	EEVEE_TextureList *txl = vedata->txl;
 	EEVEE_EffectsInfo *effects;
+	DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
 	const DRWContextState *draw_ctx = DRW_context_state_get();
 	ViewLayer *view_layer = draw_ctx->view_layer;
 
@@ -141,31 +142,42 @@ void EEVEE_effects_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, Object
 	 * Ping Pong buffer
 	 */
 	if ((effects->enabled_effects & EFFECT_POST_BUFFER) != 0) {
-		DRWFboTexture tex = {&txl->color_post, DRW_TEX_RGBA_16, DRW_TEX_FILTER | DRW_TEX_MIPMAP};
-		DRW_framebuffer_init(&fbl->effect_fb, &draw_engine_eevee_type,
-		                    (int)viewport_size[0], (int)viewport_size[1],
-		                    &tex, 1);
+		DRW_texture_ensure_fullscreen_2D(&txl->color_post, DRW_TEX_RGBA_16, DRW_TEX_FILTER | DRW_TEX_MIPMAP);
+
+		GPU_framebuffer_ensure_config(&fbl->effect_fb, {
+			GPU_ATTACHMENT_TEXTURE(dtxl->depth),
+			GPU_ATTACHMENT_TEXTURE(txl->color_post),
+		});
+
+		GPU_framebuffer_ensure_config(&fbl->effect_color_fb, {
+			GPU_ATTACHMENT_NONE,
+			GPU_ATTACHMENT_TEXTURE(txl->color_post),
+		});
 	}
 	else {
 		/* Cleanup to release memory */
 		DRW_TEXTURE_FREE_SAFE(txl->color_post);
-		DRW_FRAMEBUFFER_FREE_SAFE(fbl->effect_fb);
+		GPU_FRAMEBUFFER_FREE_SAFE(fbl->effect_fb);
 	}
 
 	/**
 	 * MinMax Pyramid
 	 */
-	DRWFboTexture texmax = {&txl->maxzbuffer, DRW_TEX_DEPTH_24, DRW_TEX_MIPMAP};
+	int size[2] = {(int)viewport_size[0], (int)viewport_size[1]};
+	size[0] = max_ii(size[0] / 2, 1);
+	size[1] = max_ii(size[1] / 2, 1);
 
 	if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY)) {
 		/* Intel gpu seems to have problem rendering to only depth format */
-		texmax.format = DRW_TEX_R_32;
+		DRW_texture_ensure_2D(&txl->maxzbuffer, size[0], size[1], DRW_TEX_R_32, DRW_TEX_MIPMAP);
+	}
+	else {
+		DRW_texture_ensure_2D(&txl->maxzbuffer, size[0], size[1], DRW_TEX_DEPTH_24, DRW_TEX_MIPMAP);
 	}
 
-	DRW_framebuffer_init(&fbl->downsample_fb, &draw_engine_eevee_type,
-	                    max_ii((int)viewport_size[0] / 2, 1), max_ii((int)viewport_size[1] / 2, 1),
-	                    &texmax, 1);
-
+	if (fbl->downsample_fb == NULL) {
+		fbl->downsample_fb = GPU_framebuffer_create();
+	}
 
 	/**
 	 * Compute Mipmap texel alignement.
@@ -185,34 +197,37 @@ void EEVEE_effects_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, Object
 	 * Normal buffer for deferred passes.
 	 */
 	if ((effects->enabled_effects & EFFECT_NORMAL_BUFFER) != 0)	{
-		if (txl->ssr_normal_input == NULL) {
-			DRWTextureFormat nor_format = DRW_TEX_RG_16;
-			txl->ssr_normal_input = DRW_texture_create_2D((int)viewport_size[0], (int)viewport_size[1], nor_format, 0, NULL);
-		}
+		int size_fs[2] = {(int)viewport_size[0], (int)viewport_size[1]};
+
+		effects->ssr_normal_input = DRW_texture_pool_query_2D(size_fs[0], size_fs[1], DRW_TEX_RG_16,
+		                                                      &draw_engine_eevee_type);
 
-		/* Reattach textures to the right buffer (because we are alternating between buffers) */
-		/* TODO multiple FBO per texture!!!! */
-		DRW_framebuffer_texture_detach(txl->ssr_normal_input);
-		DRW_framebuffer_texture_attach(fbl->main, txl->ssr_normal_input, 1, 0);
+		GPU_framebuffer_texture_attach(fbl->main_fb, effects->ssr_normal_input, 1, 0);
 	}
 	else {
-		/* Cleanup to release memory */
-		DRW_TEXTURE_FREE_SAFE(txl->ssr_normal_input);
+		effects->ssr_normal_input = NULL;
 	}
 
 	/**
 	 * Setup double buffer so we can access last frame as it was before post processes.
 	 */
 	if ((effects->enabled_effects & EFFECT_DOUBLE_BUFFER) != 0) {
-		DRWFboTexture tex_double_buffer = {&txl->color_double_buffer, DRW_TEX_RGBA_16, DRW_TEX_FILTER | DRW_TEX_MIPMAP};
-		DRW_framebuffer_init(&fbl->double_buffer, &draw_engine_eevee_type,
-		                    (int)viewport_size[0], (int)viewport_size[1],
-		                    &tex_double_buffer, 1);
+		DRW_texture_ensure_fullscreen_2D(&txl->color_double_buffer, DRW_TEX_RGBA_16, DRW_TEX_FILTER | DRW_TEX_MIPMAP);
+
+		GPU_framebuffer_ensure_config(&fbl->double_buffer_fb, {
+			GPU_ATTACHMENT_TEXTURE(dtxl->depth),
+			GPU_ATTACHMENT_TEXTURE(txl->color_double_buffer)
+		});
+
+		GPU_framebuffer_ensure_config(&fbl->double_buffer_color_fb, {
+			GPU_ATTACHMENT_NONE,
+			GPU_ATTACHMENT_TEXTURE(txl->color_double_buffer)
+		});
 	}
 	else {
 		/* Cleanup to release memory */
 		DRW_TEXTURE_FREE_SAFE(txl->color_double_buffer);
-		DRW_FRAMEBUFFER_FREE_SAFE(fbl->double_buffer);
+		GPU_FRAMEBUFFER_FREE_SAFE(fbl->double_buffer_fb);
 	}
 }
 
@@ -233,7 +248,7 @@ void EEVEE_effects_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
 	{
 		psl->color_downsample_ps = DRW_pass_create("Downsample", DRW_STATE_WRITE_COLOR);
 		DRWShadingGroup *grp = DRW_shgroup_create(e_data.downsample_sh, psl->color_downsample_ps);
-		DRW_shgroup_uniform_buffer(grp, "source", &e_data.color_src);
+		DRW_shgroup_uniform_texture_ref(grp, "source", &e_data.color_src);
 		DRW_shgroup_uniform_float(grp, "fireflyFactor", &sldata->common_data.ssr_firefly_fac, 1);
 		DRW_shgroup_call_add(grp, quad, NULL);
 	}
@@ -243,7 +258,7 @@ void EEVEE_effects_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
 		static unsigned int six = 6;
 		psl->color_downsample_cube_ps = DRW_pass_create("Downsample Cube", DRW_STATE_WRITE_COLOR);
 		DRWShadingGroup *grp = DRW_shgroup_create(e_data.downsample_cube_sh, psl->color_downsample_cube_ps);
-		DRW_shgroup_uniform_buffer(grp, "source", &e_data.color_src);
+		DRW_shgroup_uniform_texture_ref(grp, "source", &e_data.color_src);
 		DRW_shgroup_uniform_float(grp, "texelSize", &e_data.cube_texel_size, 1);
 		DRW_shgroup_uniform_int(grp, "Layer", &zero, 1);
 		DRW_shgroup_call_instances_add(grp, quad, NULL, &six);
@@ -253,56 +268,27 @@ void EEVEE_effects_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
 		/* Perform min/max downsample */
 		DRWShadingGroup *grp;
 
-#if 0 /* Not used for now */
-		psl->minz_downlevel_ps = DRW_pass_create("HiZ Min Down Level", downsample_write | DRW_STATE_DEPTH_ALWAYS);
-		grp = DRW_shgroup_create(e_data.minz_downlevel_sh, psl->minz_downlevel_ps);
-		DRW_shgroup_uniform_buffer(grp, "depthBuffer", &stl->g_data->minzbuffer);
-		DRW_shgroup_call_add(grp, quad, NULL);
-#endif
-
 		psl->maxz_downlevel_ps = DRW_pass_create("HiZ Max Down Level", downsample_write | DRW_STATE_DEPTH_ALWAYS);
 		grp = DRW_shgroup_create(e_data.maxz_downlevel_sh, psl->maxz_downlevel_ps);
-		DRW_shgroup_uniform_buffer(grp, "depthBuffer", &txl->maxzbuffer);
+		DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &txl->maxzbuffer);
 		DRW_shgroup_call_add(grp, quad, NULL);
 
 		/* Copy depth buffer to halfres top level of HiZ */
-#if 0 /* Not used for now */
-		psl->minz_downdepth_ps = DRW_pass_create("HiZ Min Copy Depth Halfres", downsample_write | DRW_STATE_DEPTH_ALWAYS);
-		grp = DRW_shgroup_create(e_data.minz_downdepth_sh, psl->minz_downdepth_ps);
-		DRW_shgroup_uniform_buffer(grp, "depthBuffer", &e_data.depth_src);
-		DRW_shgroup_call_add(grp, quad, NULL);
-#endif
 
 		psl->maxz_downdepth_ps = DRW_pass_create("HiZ Max Copy Depth Halfres", downsample_write | DRW_STATE_DEPTH_ALWAYS);
 		grp = DRW_shgroup_create(e_data.maxz_downdepth_sh, psl->maxz_downdepth_ps);
-		DRW_shgroup_uniform_buffer(grp, "depthBuffer", &e_data.depth_src);
+		DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &e_data.depth_src);
 		DRW_shgroup_call_add(grp, quad, NULL);
 
-#if 0 /* Not used for now */
-		psl->minz_downdepth_layer_ps = DRW_pass_create("HiZ Min Copy DepthLayer Halfres", downsample_write | DRW_STATE_DEPTH_ALWAYS);
-		grp = DRW_shgroup_create(e_data.minz_downdepth_layer_sh, psl->minz_downdepth_layer_ps);
-		DRW_shgroup_uniform_buffer(grp, "depthBuffer", &e_data.depth_src);
-		DRW_shgroup_uniform_int(grp, "depthLayer", &e_data.depth_src_layer, 1);
-		DRW_shgroup_call_add(grp, quad, NULL);
-#endif
-
 		psl->maxz_downdepth_layer_ps = DRW_pass_create("HiZ Max Copy DepthLayer Halfres", downsample_write | DRW_STATE_DEPTH_ALWAYS);
 		grp = DRW_shgroup_create(e_data.maxz_downdepth_layer_sh, psl->maxz_downdepth_layer_ps);
-		DRW_shgroup_uniform_buffer(grp, "depthBuffer", &e_data.depth_src);
+		DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &e_data.depth_src);
 		DRW_shgroup_uniform_int(grp, "depthLayer", &e_data.depth_src_layer, 1);
 		DRW_shgroup_call_add(grp, quad, NULL);
 
-		/* Copy depth buffer to halfres top level of HiZ */
-#if 0 /* Not used for now */
-		psl->minz_copydepth_ps = DRW_pass_create("HiZ Min Copy Depth Fullres", downsample_write | DRW_STATE_DEPTH_ALWAYS);
-		grp = DRW_shgroup_create(e_data.minz_copydepth_sh, psl->minz_copydepth_ps);
-		DRW_shgroup_uniform_buffer(grp, "depthBuffer", &e_data.depth_src);
-		DRW_shgroup_call_add(grp, quad, NULL);
-#endif
-
 		psl->maxz_copydepth_ps = DRW_pass_create("HiZ Max Copy Depth Fullres", downsample_write | DRW_STATE_DEPTH_ALWAYS);
 		grp = DRW_shgroup_create(e_data.maxz_copydepth_sh, psl->maxz_copydepth_ps);
-		DRW_shgroup_uniform_buffer(grp, "depthBuffer", &e_data.depth_src);
+		DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &e_data.depth_src);
 		DRW_shgroup_call_add(grp, quad, NULL);
 	}
 }
@@ -346,64 +332,70 @@ void EEVEE_create_minmax_buffer(EEVEE_Data *vedata, GPUTexture *depth_src, int l
 #if 0 /* Not required for now */
 	DRW_stats_group_start("Min buffer");
 	/* Copy depth buffer to min texture top level */
-	DRW_framebuffer_texture_attach(fbl->downsample_fb, stl->g_data->minzbuffer, 0, 0);
-	DRW_framebuffer_bind(fbl->downsample_fb);
+	GPU_framebuffer_texture_attach(fbl->downsample_fb, stl->g_data->minzbuffer, 0, 0);
+	GPU_framebuffer_bind(fbl->downsample_fb);
 	if (layer >= 0) {
 		DRW_draw_pass(psl->minz_downdepth_layer_ps);
 	}
 	else {
 		DRW_draw_pass(psl->minz_downdepth_ps);
 	}
-	DRW_framebuffer_texture_detach(stl->g_data->minzbuffer);
+	GPU_framebuffer_texture_detach(stl->g_data->minzbuffer);
 
 	/* Create lower levels */
-	DRW_framebuffer_recursive_downsample(fbl->downsample_fb, stl->g_data->minzbuffer, 8, &min_downsample_cb, vedata);
+	GPU_framebuffer_recursive_downsample(fbl->downsample_fb, stl->g_data->minzbuffer, 8, &min_downsample_cb, vedata);
 	DRW_stats_group_end();
 #endif
 
 	DRW_stats_group_start("Max buffer");
 	/* Copy depth buffer to max texture top level */
-	DRW_framebuffer_texture_attach(fbl->downsample_fb, txl->maxzbuffer, 0, 0);
-	DRW_framebuffer_bind(fbl->downsample_fb);
+	GPU_framebuffer_texture_attach(fbl->downsample_fb, txl->maxzbuffer, 0, 0);
+	GPU_framebuffer_bind(fbl->downsample_fb);
 	if (layer >= 0) {
 		DRW_draw_pass(psl->maxz_downdepth_layer_ps);
 	}
 	else {
 		DRW_draw_pass(psl->maxz_downdepth_ps);
 	}
-	DRW_framebuffer_texture_detach(txl->maxzbuffer);
 
 	/* Create lower levels */
-	DRW_framebuffer_recursive_downsample(fbl->downsample_fb, txl->maxzbuffer, 8, &max_downsample_cb, vedata);
+	GPU_framebuffer_recursive_downsample(fbl->downsample_fb, 8, &max_downsample_cb, vedata);
+	GPU_framebuffer_texture_detach(fbl->downsample_fb, txl->maxzbuffer);
 	DRW_stats_group_end();
 
 	/* Restore */
-	DRW_framebuffer_bind(fbl->main);
+	GPU_framebuffer_bind(fbl->main_fb);
 }
 
 /**
  * Simple downsampling algorithm. Reconstruct mip chain up to mip level.
  **/
-void EEVEE_downsample_buffer(EEVEE_Data *vedata, struct GPUFrameBuffer *fb_src, GPUTexture *texture_src, int level)
+void EEVEE_downsample_buffer(EEVEE_Data *vedata, GPUTexture *texture_src, int level)
 {
+	EEVEE_FramebufferList *fbl = vedata->fbl;
 	e_data.color_src = texture_src;
 
-	DRW_stats_group_start("Downsample buffer");
 	/* Create lower levels */
-	DRW_framebuffer_recursive_downsample(fb_src, texture_src, level, &simple_downsample_cb, vedata);
+	DRW_stats_group_start("Downsample buffer");
+	GPU_framebuffer_texture_attach(fbl->downsample_fb, texture_src, 0, 0);
+	GPU_framebuffer_recursive_downsample(fbl->downsample_fb, level, &simple_downsample_cb, vedata);
+	GPU_framebuffer_texture_detach(fbl->downsample_fb, texture_src);
 	DRW_stats_group_end();
 }
 
 /**
  * Simple downsampling algorithm for cubemap. Reconstruct mip chain up to mip level.
  **/
-void EEVEE_downsample_cube_buffer(EEVEE_Data *vedata, struct GPUFrameBuffer *fb_src, GPUTexture *texture_src, int level)
+void EEVEE_downsample_cube_buffer(EEVEE_Data *vedata, GPUTexture *texture_src, int level)
 {
+	EEVEE_FramebufferList *fbl = vedata->fbl;
 	e_data.color_src = texture_src;
 
-	DRW_stats_group_start("Downsample Cube buffer");
 	/* Create lower levels */
-	DRW_framebuffer_recursive_downsample(fb_src, texture_src, level, &simple_downsample_cube_cb, vedata);
+	DRW_stats_group_start("Downsample Cube buffer");
+	GPU_framebuffer_texture_attach(fbl->downsample_fb, texture_src, 0, 0);
+	GPU_framebuffer_recursive_downsample(fbl->downsample_fb, level, &simple_downsample_cube_cb, vedata);
+	GPU_framebuffer_texture_detach(fbl->downsample_fb, texture_src);
 	DRW_stats_group_end();
 }
 
@@ -413,21 +405,17 @@ void EEVEE_draw_effects(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
 	EEVEE_FramebufferList *fbl = vedata->fbl;
 	EEVEE_StorageList *stl = vedata->stl;
 	EEVEE_EffectsInfo *effects = stl->effects;
-	DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
 
 	/* only once per frame after the first post process */
 	effects->swap_double_buffer = ((effects->enabled_effects & EFFECT_DOUBLE_BUFFER) != 0);
 
 	/* Init pointers */
 	effects->source_buffer = txl->color; /* latest updated texture */
-	effects->target_buffer = fbl->effect_fb; /* next target to render to */
+	effects->target_buffer = fbl->effect_color_fb; /* next target to render to */
 
 	/* Temporal Anti-Aliasing MUST come first */
 	EEVEE_temporal_sampling_draw(vedata);
 
-	/* Detach depth for effects to use it */
-	DRW_framebuffer_texture_detach(dtxl->depth);
-
 	/* Post process stack (order matters) */
 	EEVEE_motion_blur_draw(vedata);
 	EEVEE_depth_of_field_draw(vedata);
@@ -435,7 +423,7 @@ void EEVEE_draw_effects(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
 
 	/* Save the final texture and framebuffer for final transformation or read. */
 	effects->final_tx = effects->source_buffer;
-	effects->final_fb = (effects->target_buffer != fbl->main) ? fbl->main : fbl->effect_fb;
+	effects->final_fb = (effects->target_buffer != fbl->main_fb) ? fbl->main_fb : fbl->effect_fb;
 
 	/* If no post processes is enabled, buffers are still not swapped, do it now. */
 	SWAP_DOUBLE_BUFFERS();
diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c
index 6cfecd0a226fe06660ebae0276516b5603747250..276f23c7cf79f601d9fe1d2b8af8cccd87f6ff86 100644
--- a/source/blender/draw/engines/eevee/eevee_engine.c
+++ b/source/blender/draw/engines/eevee/eevee_engine.c
@@ -55,6 +55,7 @@ static void eevee_engine_init(void *ved)
 	EEVEE_FramebufferList *fbl = vedata->fbl;
 	EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
 	EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
+	DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
 
 	const DRWContextState *draw_ctx = DRW_context_state_get();
 	View3D *v3d = draw_ctx->v3d;
@@ -68,12 +69,22 @@ static void eevee_engine_init(void *ved)
 	stl->g_data->background_alpha = DRW_state_draw_background() ? 1.0f : 0.0f;
 	stl->g_data->valid_double_buffer = (txl->color_double_buffer != NULL);
 
-	DRWFboTexture tex = {&txl->color, DRW_TEX_RGBA_16, DRW_TEX_FILTER | DRW_TEX_MIPMAP};
+	/* Main Buffer */
+	DRW_texture_ensure_fullscreen_2D(&txl->color, DRW_TEX_RGBA_16, DRW_TEX_FILTER | DRW_TEX_MIPMAP);
+
+	GPU_framebuffer_ensure_config(&fbl->main_fb, {
+		GPU_ATTACHMENT_TEXTURE(dtxl->depth),
+		GPU_ATTACHMENT_TEXTURE(txl->color),
+		GPU_ATTACHMENT_LEAVE,
+		GPU_ATTACHMENT_LEAVE,
+		GPU_ATTACHMENT_LEAVE,
+		GPU_ATTACHMENT_LEAVE
+	});
 
-	const float *viewport_size = DRW_viewport_size_get();
-	DRW_framebuffer_init(&fbl->main, &draw_engine_eevee_type,
-	                    (int)viewport_size[0], (int)viewport_size[1],
-	                    &tex, 1);
+	GPU_framebuffer_ensure_config(&fbl->main_color_fb, {
+		GPU_ATTACHMENT_NONE,
+		GPU_ATTACHMENT_TEXTURE(txl->color)
+	});
 
 	if (sldata->common_ubo == NULL) {
 		sldata->common_ubo = DRW_uniformbuffer_create(sizeof(sldata->common_data), &sldata->common_data);
@@ -170,6 +181,7 @@ static void eevee_draw_background(void *vedata)
 	EEVEE_TextureList *txl = ((EEVEE_Data *)vedata)->txl;
 	EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
 	EEVEE_FramebufferList *fbl = ((EEVEE_Data *)vedata)->fbl;
+	EEVEE_EffectsInfo *effects = stl->effects;
 	EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
 
 	/* Default framebuffer and texture */
@@ -185,6 +197,9 @@ static void eevee_draw_background(void *vedata)
 	               (stl->effects->enabled_effects & (EFFECT_VOLUMETRIC | EFFECT_SSR)) != 0) ? 4 : 1;
 
 	while (loop_ct--) {
+		float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+		float clear_depth = 1.0f;
+		unsigned int clear_stencil = 0xFF;
 		unsigned int primes[3] = {2, 3, 7};
 		double offset[3] = {0.0, 0.0, 0.0};
 		double r[3];
@@ -228,18 +243,11 @@ static void eevee_draw_background(void *vedata)
 		EEVEE_draw_shadows(sldata, psl);
 		DRW_stats_group_end();
 
-		/* Attach depth to the hdr buffer and bind it */
-		DRW_framebuffer_texture_detach(dtxl->depth);
-		DRW_framebuffer_texture_attach(fbl->main, dtxl->depth, 0, 0);
-		DRW_framebuffer_bind(fbl->main);
-		if (DRW_state_draw_background()) {
-			DRW_framebuffer_clear(false, true, true, NULL, 1.0f);
-		}
-		else {
-			/* We need to clear the alpha chanel in this case. */
-			float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f};
-			DRW_framebuffer_clear(true, true, true, clear_col, 1.0f);
-		}
+		GPU_framebuffer_bind(fbl->main_fb);
+		GPUFrameBufferBits clear_bits = GPU_DEPTH_BIT;
+		clear_bits |= (DRW_state_draw_background()) ? 0 : GPU_COLOR_BIT;
+		clear_bits |= ((stl->effects->enabled_effects & EFFECT_SSS) != 0) ? GPU_STENCIL_BIT : 0;
+		GPU_framebuffer_clear(fbl->main_fb, clear_bits, clear_col, clear_depth, clear_stencil);
 
 		/* Depth prepass */
 		DRW_stats_group_start("Prepass");
@@ -295,11 +303,8 @@ static void eevee_draw_background(void *vedata)
 		}
 	}
 
-	/* Restore default framebuffer */
-	DRW_framebuffer_texture_attach(dfbl->default_fb, dtxl->depth, 0, 0);
-	DRW_framebuffer_bind(dfbl->default_fb);
-
-	/* Tonemapping */
+	/* Tonemapping and transfer result to default framebuffer. */
+	GPU_framebuffer_bind(dfbl->default_fb);
 	DRW_transform_to_display(stl->effects->final_tx);
 
 	/* Debug : Ouput buffer to view. */
@@ -308,25 +313,25 @@ static void eevee_draw_background(void *vedata)
 			if (txl->maxzbuffer) DRW_transform_to_display(txl->maxzbuffer);
 			break;
 		case 2:
-			if (stl->g_data->ssr_pdf_output) DRW_transform_to_display(stl->g_data->ssr_pdf_output);
+			if (effects->ssr_pdf_output) DRW_transform_to_display(effects->ssr_pdf_output);
 			break;
 		case 3:
-			if (txl->ssr_normal_input) DRW_transform_to_display(txl->ssr_normal_input);
+			if (effects->ssr_normal_input) DRW_transform_to_display(effects->ssr_normal_input);
 			break;
 		case 4:
-			if (txl->ssr_specrough_input) DRW_transform_to_display(txl->ssr_specrough_input);
+			if (effects->ssr_specrough_input) DRW_transform_to_display(effects->ssr_specrough_input);
 			break;
 		case 5:
 			if (txl->color_double_buffer) DRW_transform_to_display(txl->color_double_buffer);
 			break;
 		case 6:
-			if (stl->g_data->gtao_horizons_debug) DRW_transform_to_display(stl->g_data->gtao_horizons_debug);
+			if (effects->gtao_horizons_debug) DRW_transform_to_display(effects->gtao_horizons_debug);
 			break;
 		case 7:
-			if (txl->gtao_horizons) DRW_transform_to_display(txl->gtao_horizons);
+			if (effects->gtao_horizons) DRW_transform_to_display(effects->gtao_horizons);
 			break;
 		case 8:
-			if (txl->sss_data) DRW_transform_to_display(txl->sss_data);
+			if (effects->sss_data) DRW_transform_to_display(effects->sss_data);
 			break;
 		default:
 			break;
diff --git a/source/blender/draw/engines/eevee/eevee_lightprobes.c b/source/blender/draw/engines/eevee/eevee_lightprobes.c
index e751f2dd6b454e39ea45b1bfd134236bbd87bab1..59e8e76bc52d966e656419b8c1d966cfdea627c2 100644
--- a/source/blender/draw/engines/eevee/eevee_lightprobes.c
+++ b/source/blender/draw/engines/eevee/eevee_lightprobes.c
@@ -155,7 +155,6 @@ static void planar_pool_ensure_alloc(EEVEE_Data *vedata, int num_planar_ref)
 	/* XXX TODO OPTIMISATION : This is a complete waist of texture memory.
 	 * Instead of allocating each planar probe for each viewport,
 	 * only alloc them once using the biggest viewport resolution. */
-	EEVEE_FramebufferList *fbl = vedata->fbl;
 	EEVEE_TextureList *txl = vedata->txl;
 
 	const float *viewport_size = DRW_viewport_size_get();
@@ -182,15 +181,6 @@ static void planar_pool_ensure_alloc(EEVEE_Data *vedata, int num_planar_ref)
 			txl->planar_depth = DRW_texture_create_2D_array(1, 1, 1, DRW_TEX_DEPTH_24, 0, NULL);
 		}
 	}
-
-	if (num_planar_ref > 0) {
-		/* NOTE : Depth buffer is 2D but the planar_pool tex is 2D array.
-		 * DRW_framebuffer_init binds the whole texture making the framebuffer invalid.
-		 * To overcome this, we bind the planar pool ourselves later */
-
-		/* XXX Do this one first so it gets it's mipmap done. */
-		DRW_framebuffer_init(&fbl->planarref_fb, &draw_engine_eevee_type, 1, 1, NULL, 0);
-	}
 }
 
 static void lightprobe_shaders_init(void)
@@ -349,7 +339,6 @@ void EEVEE_lightprobes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *UNUSED(veda
 		DRW_TEXTURE_FREE_SAFE(sldata->probe_depth_rt);
 		DRW_TEXTURE_FREE_SAFE(sldata->probe_rt);
 		DRW_TEXTURE_FREE_SAFE(sldata->probe_pool);
-		DRW_FRAMEBUFFER_FREE_SAFE(sldata->probe_fb);
 	}
 
 	int visibility_res = BKE_collection_engine_property_value_get_int(props, "gi_visibility_resolution");
@@ -370,13 +359,12 @@ void EEVEE_lightprobes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *UNUSED(veda
 		sldata->probe_rt = DRW_texture_create_cube(sldata->probes->target_size, DRW_TEX_RGBA_16, DRW_TEX_FILTER | DRW_TEX_MIPMAP, NULL);
 	}
 
-	DRWFboTexture tex_probe[2] = {{&sldata->probe_depth_rt, DRW_TEX_DEPTH_24, 0},
-	                              {&sldata->probe_rt, DRW_TEX_RGBA_16, DRW_TEX_FILTER | DRW_TEX_MIPMAP}};
-	DRW_framebuffer_init(&sldata->probe_fb, &draw_engine_eevee_type, sldata->probes->target_size, sldata->probes->target_size, tex_probe, 2);
-
-	/* Minmaxz Pyramid */
-	// DRWFboTexture tex_minmaxz = {&e_data.cube_face_minmaxz, DRW_TEX_RG_32, DRW_TEX_MIPMAP | DRW_TEX_TEMP};
-	// DRW_framebuffer_init(&vedata->fbl->downsample_fb, &draw_engine_eevee_type, PROBE_RT_SIZE / 2, PROBE_RT_SIZE / 2, &tex_minmaxz, 1);
+	for (int i = 0; i < 6; ++i) {
+		GPU_framebuffer_ensure_config(&sldata->probe_face_fb[i], {
+			GPU_ATTACHMENT_TEXTURE_CUBEFACE(sldata->probe_depth_rt, i),
+			GPU_ATTACHMENT_TEXTURE_CUBEFACE(sldata->probe_rt, i)
+		});
+	}
 
 	/* Placeholder planar pool: used when rendering planar reflections (avoid dependency loop). */
 	if (!e_data.planar_pool_placeholder) {
@@ -534,7 +522,7 @@ void EEVEE_lightprobes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedat
 		psl->probe_grid_fill = DRW_pass_create("LightProbe Grid Floodfill", DRW_STATE_WRITE_COLOR);
 
 		DRWShadingGroup *grp = DRW_shgroup_create(e_data.probe_grid_fill_sh, psl->probe_grid_fill);
-		DRW_shgroup_uniform_buffer(grp, "irradianceGrid", &sldata->irradiance_pool);
+		DRW_shgroup_uniform_texture_ref(grp, "irradianceGrid", &sldata->irradiance_pool);
 
 		struct Gwn_Batch *geom = DRW_cache_fullscreen_quad_get();
 		DRW_shgroup_call_add(grp, geom, NULL);
@@ -556,7 +544,7 @@ void EEVEE_lightprobes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedat
 		        DRW_cache_sphere_get(),
 		        e_data.format_probe_display_cube);
 		stl->g_data->cube_display_shgrp = grp;
-		DRW_shgroup_uniform_buffer(grp, "probeCubes", &sldata->probe_pool);
+		DRW_shgroup_uniform_texture_ref(grp, "probeCubes", &sldata->probe_pool);
 		DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
 
 		DRW_shgroup_instance_format(e_data.format_probe_display_planar, {
@@ -570,14 +558,14 @@ void EEVEE_lightprobes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedat
 		        DRW_cache_quad_get(),
 		        e_data.format_probe_display_planar);
 		stl->g_data->planar_display_shgrp = grp;
-		DRW_shgroup_uniform_buffer(grp, "probePlanars", &txl->planar_pool);
+		DRW_shgroup_uniform_texture_ref(grp, "probePlanars", &txl->planar_pool);
 	}
 
 	{
 		psl->probe_planar_downsample_ps = DRW_pass_create("LightProbe Planar Downsample", DRW_STATE_WRITE_COLOR);
 
 		DRWShadingGroup *grp = DRW_shgroup_create(e_data.probe_planar_downsample_sh, psl->probe_planar_downsample_ps);
-		DRW_shgroup_uniform_buffer(grp, "source", &txl->planar_pool);
+		DRW_shgroup_uniform_texture_ref(grp, "source", &txl->planar_pool);
 		DRW_shgroup_uniform_float(grp, "fireflyFactor", &sldata->common_data.ssr_firefly_fac, 1);
 		DRW_shgroup_call_instances_add(grp, DRW_cache_fullscreen_quad_get(), NULL, (unsigned int *)&pinfo->num_planar);
 	}
@@ -912,7 +900,7 @@ static void EEVEE_lightprobes_updates(EEVEE_ViewLayerData *sldata, EEVEE_PassLis
 			DRW_shgroup_uniform_vec3(grp, "increment_x", egrid->increment_x, 1);
 			DRW_shgroup_uniform_vec3(grp, "increment_y", egrid->increment_y, 1);
 			DRW_shgroup_uniform_vec3(grp, "increment_z", egrid->increment_z, 1);
-			DRW_shgroup_uniform_buffer(grp, "irradianceGrid", &sldata->irradiance_pool);
+			DRW_shgroup_uniform_texture_ref(grp, "irradianceGrid", &sldata->irradiance_pool);
 			DRW_shgroup_uniform_float(grp, "sphere_size", &probe->data_draw_size, 1);
 			DRW_shgroup_call_instances_add(grp, DRW_cache_sphere_get(), NULL, (unsigned int *)&ped->num_cell);
 		}
@@ -957,7 +945,7 @@ void EEVEE_lightprobes_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *ved
 		sldata->probe_pool = DRW_texture_create_2D_array(pinfo->cubemap_res, pinfo->cubemap_res, max_ff(1, pinfo->num_cube),
 		                                                 DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER | DRW_TEX_MIPMAP, NULL);
 		if (sldata->probe_filter_fb) {
-			DRW_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->probe_pool, 0, 0);
+			GPU_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->probe_pool, 0, 0);
 		}
 		/* Tag probes to refresh */
 		pinfo->update_world |= PROBE_UPDATE_CUBE;
@@ -973,11 +961,6 @@ void EEVEE_lightprobes_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *ved
 		}
 	}
 
-	DRWFboTexture tex_filter = {&sldata->probe_pool, DRW_TEX_RGBA_16, DRW_TEX_FILTER | DRW_TEX_MIPMAP};
-
-	DRW_framebuffer_init(&sldata->probe_filter_fb, &draw_engine_eevee_type, pinfo->cubemap_res, pinfo->cubemap_res, &tex_filter, 1);
-
-
 #ifdef IRRADIANCE_SH_L2
 	/* we need a signed format for Spherical Harmonics */
 	int irradiance_format = DRW_TEX_RGBA_16;
@@ -1051,14 +1034,17 @@ static void glossy_filter_probe(
 	/* Max lod used from the render target probe */
 	pinfo->lod_rt_max = floorf(log2f(pinfo->target_size)) - 2.0f;
 
+	/* Start fresh */
+	GPU_framebuffer_ensure_config(&sldata->probe_filter_fb, {
+		GPU_ATTACHMENT_NONE,
+		GPU_ATTACHMENT_NONE
+	});
+
 	/* 2 - Let gpu create Mipmaps for Filtered Importance Sampling. */
 	/* Bind next framebuffer to be able to gen. mips for probe_rt. */
-	DRW_framebuffer_bind(sldata->probe_filter_fb);
-	EEVEE_downsample_cube_buffer(vedata, sldata->probe_filter_fb, sldata->probe_rt, (int)(pinfo->lod_rt_max));
+	EEVEE_downsample_cube_buffer(vedata, sldata->probe_rt, (int)(pinfo->lod_rt_max));
 
 	/* 3 - Render to probe array to the specified layer, do prefiltering. */
-	/* Detach to rebind the right mipmap. */
-	DRW_framebuffer_texture_detach(sldata->probe_pool);
 	float mipsize = pinfo->cubemap_res;
 	const int maxlevel = (int)floorf(log2f(pinfo->cubemap_res));
 	const int min_lod_level = 3;
@@ -1101,19 +1087,19 @@ static void glossy_filter_probe(
 		pinfo->invsamples_ct = 1.0f / pinfo->samples_ct;
 		pinfo->lodfactor = bias + 0.5f * log((float)(pinfo->target_size * pinfo->target_size) * pinfo->invsamples_ct) / log(2);
 
-		DRW_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->probe_pool, 0, i);
-		DRW_framebuffer_viewport_size(sldata->probe_filter_fb, 0, 0, mipsize, mipsize);
+		GPU_framebuffer_ensure_config(&sldata->probe_filter_fb, {
+			GPU_ATTACHMENT_NONE,
+			GPU_ATTACHMENT_TEXTURE_MIP(sldata->probe_pool, i)
+		});
+		GPU_framebuffer_bind(sldata->probe_filter_fb);
+		GPU_framebuffer_viewport_set(sldata->probe_filter_fb, 0, 0, mipsize, mipsize);
 		DRW_draw_pass(psl->probe_glossy_compute);
-		DRW_framebuffer_texture_detach(sldata->probe_pool);
 
 		mipsize /= 2;
 		CLAMP_MIN(mipsize, 1);
 	}
 	/* For shading, save max level of the octahedron map */
 	sldata->common_data.prb_lod_cube_max = (float)(maxlevel - min_lod_level) - 1.0f;
-
-	/* reattach to have a valid framebuffer. */
-	DRW_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->probe_pool, 0, 0);
 }
 
 /* Diffuse filter probe_rt to irradiance_pool at index probe_idx */
@@ -1156,14 +1142,21 @@ static void diffuse_filter_probe(
 	pinfo->lod_rt_max = 2.0f; /* Improve cache reuse */
 #endif
 
-	/* 4 - Compute spherical harmonics */
-	DRW_framebuffer_bind(sldata->probe_filter_fb);
-	EEVEE_downsample_cube_buffer(vedata, sldata->probe_filter_fb, sldata->probe_rt, (int)(pinfo->lod_rt_max));
-
-	DRW_framebuffer_texture_detach(sldata->probe_pool);
-	DRW_framebuffer_texture_layer_attach(sldata->probe_filter_fb, sldata->irradiance_rt, 0, 0, 0);
+	/* Start fresh */
+	GPU_framebuffer_ensure_config(&sldata->probe_filter_fb, {
+		GPU_ATTACHMENT_NONE,
+		GPU_ATTACHMENT_NONE
+	});
 
-	DRW_framebuffer_viewport_size(sldata->probe_filter_fb, x, y, size[0], size[1]);
+	/* 4 - Compute spherical harmonics */
+	EEVEE_downsample_cube_buffer(vedata, sldata->probe_rt, (int)(pinfo->lod_rt_max));
+
+	GPU_framebuffer_ensure_config(&sldata->probe_filter_fb, {
+		GPU_ATTACHMENT_NONE,
+		GPU_ATTACHMENT_TEXTURE_LAYER(sldata->irradiance_rt, 0)
+	});
+	GPU_framebuffer_bind(sldata->probe_filter_fb);
+	GPU_framebuffer_viewport_set(sldata->probe_filter_fb, x, y, size[0], size[1]);
 	DRW_draw_pass(psl->probe_diffuse_compute);
 
 	/* World irradiance have no visibility */
@@ -1183,18 +1176,16 @@ static void diffuse_filter_probe(
 		x = common_data->prb_irradiance_vis_size * (offset % cell_per_row);
 		y = common_data->prb_irradiance_vis_size * ((offset / cell_per_row) % cell_per_col);
 		int layer = 1 + ((offset / cell_per_row) / cell_per_col);
+		const int vis_size = common_data->prb_irradiance_vis_size;
 
-		DRW_framebuffer_texture_detach(sldata->irradiance_rt);
-		DRW_framebuffer_texture_layer_attach(sldata->probe_filter_fb, sldata->irradiance_rt, 0, layer, 0);
-
-		DRW_framebuffer_viewport_size(sldata->probe_filter_fb, x, y, common_data->prb_irradiance_vis_size,
-		                                                             common_data->prb_irradiance_vis_size);
+		GPU_framebuffer_ensure_config(&sldata->probe_filter_fb, {
+			GPU_ATTACHMENT_NONE,
+			GPU_ATTACHMENT_TEXTURE_LAYER(sldata->irradiance_rt, layer)
+		});
+		GPU_framebuffer_bind(sldata->probe_filter_fb);
+		GPU_framebuffer_viewport_set(sldata->probe_filter_fb, x, y, vis_size, vis_size);
 		DRW_draw_pass(psl->probe_visibility_compute);
 	}
-
-	/* reattach to have a valid framebuffer. */
-	DRW_framebuffer_texture_detach(sldata->irradiance_rt);
-	DRW_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->probe_pool, 0, 0);
 }
 
 /* Render the scene to the probe_rt texture. */
@@ -1204,7 +1195,6 @@ static void render_scene_to_probe(
 {
 	EEVEE_TextureList *txl = vedata->txl;
 	EEVEE_PassList *psl = vedata->psl;
-	EEVEE_StorageList *stl = vedata->stl;
 	EEVEE_LightProbesInfo *pinfo = sldata->probes;
 
 	DRWMatrixState matstate;
@@ -1230,19 +1220,13 @@ static void render_scene_to_probe(
 	/* Avoid using the texture attached to framebuffer when rendering. */
 	/* XXX */
 	GPUTexture *tmp_planar_pool = txl->planar_pool;
-	GPUTexture *tmp_minz = stl->g_data->minzbuffer;
 	GPUTexture *tmp_maxz = txl->maxzbuffer;
 	txl->planar_pool = e_data.planar_pool_placeholder;
-	stl->g_data->minzbuffer = e_data.depth_placeholder;
 	txl->maxzbuffer = e_data.depth_placeholder;
 
 	/* Update common uniforms */
 	DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data);
 
-	/* Detach to rebind the right cubeface. */
-	DRW_framebuffer_bind(sldata->probe_fb);
-	DRW_framebuffer_texture_detach(sldata->probe_rt);
-	DRW_framebuffer_texture_detach(sldata->probe_depth_rt);
 	for (int i = 0; i < 6; ++i) {
 		/* Setup custom matrices */
 		mul_m4_m4m4(viewmat, cubefacemat[i], posmat);
@@ -1256,11 +1240,8 @@ static void render_scene_to_probe(
 		/* Be sure that cascaded shadow maps are updated. */
 		EEVEE_draw_shadows(sldata, psl);
 
-		DRW_framebuffer_cubeface_attach(sldata->probe_fb, sldata->probe_rt, 0, i, 0);
-		DRW_framebuffer_cubeface_attach(sldata->probe_fb, sldata->probe_depth_rt, 0, i, 0);
-		DRW_framebuffer_viewport_size(sldata->probe_fb, 0, 0, pinfo->target_size, pinfo->target_size);
-
-		DRW_framebuffer_clear(false, true, false, NULL, 1.0);
+		GPU_framebuffer_bind(sldata->probe_face_fb[i]);
+		GPU_framebuffer_clear_depth(sldata->probe_face_fb[i], 1.0);
 
 		/* Depth prepass */
 		DRW_draw_pass(psl->depth_pass);
@@ -1270,23 +1251,17 @@ static void render_scene_to_probe(
 
 		// EEVEE_create_minmax_buffer(vedata, sldata->probe_depth_rt);
 
-		/* Rebind Planar FB */
-		DRW_framebuffer_bind(sldata->probe_fb);
+		/* Rebind Target FB */
+		GPU_framebuffer_bind(sldata->probe_face_fb[i]);
 
 		/* Shading pass */
 		EEVEE_draw_default_passes(psl);
 		DRW_draw_pass(psl->material_pass);
 		DRW_draw_pass(psl->sss_pass); /* Only output standard pass */
-
-		DRW_framebuffer_texture_detach(sldata->probe_rt);
-		DRW_framebuffer_texture_detach(sldata->probe_depth_rt);
 	}
-	DRW_framebuffer_texture_attach(sldata->probe_fb, sldata->probe_rt, 0, 0);
-	DRW_framebuffer_texture_attach(sldata->probe_fb, sldata->probe_depth_rt, 0, 0);
 
 	/* Restore */
 	txl->planar_pool = tmp_planar_pool;
-	stl->g_data->minzbuffer = tmp_minz;
 	txl->maxzbuffer = tmp_maxz;
 }
 
@@ -1322,12 +1297,13 @@ static void render_scene_to_planar(
 	DRW_uniformbuffer_update(sldata->clip_ubo, &sldata->clip_data);
 	DRW_state_clip_planes_count_set(1);
 
-	/* Attach depth here since it's a DRW_TEX_TEMP */
-	DRW_framebuffer_texture_layer_attach(fbl->planarref_fb, txl->planar_depth, 0, layer, 0);
-	DRW_framebuffer_texture_layer_attach(fbl->planarref_fb, txl->planar_pool, 0, layer, 0);
-	DRW_framebuffer_bind(fbl->planarref_fb);
+	GPU_framebuffer_ensure_config(&fbl->planarref_fb, {
+		GPU_ATTACHMENT_TEXTURE_LAYER(txl->planar_depth, layer),
+		GPU_ATTACHMENT_TEXTURE_LAYER(txl->planar_pool, layer)
+	});
 
-	DRW_framebuffer_clear(false, true, false, NULL, 1.0);
+	GPU_framebuffer_bind(fbl->planarref_fb);
+	GPU_framebuffer_clear_depth(fbl->planarref_fb, 1.0);
 
 	/* Avoid using the texture attached to framebuffer when rendering. */
 	/* XXX */
@@ -1354,7 +1330,7 @@ static void render_scene_to_planar(
 	EEVEE_occlusion_compute(sldata, vedata, tmp_planar_depth, layer);
 
 	/* Rebind Planar FB */
-	DRW_framebuffer_bind(fbl->planarref_fb);
+	GPU_framebuffer_bind(fbl->planarref_fb);
 
 	/* Shading pass */
 	EEVEE_draw_default_passes(psl);
@@ -1375,9 +1351,6 @@ static void render_scene_to_planar(
 	/* Restore */
 	txl->planar_pool = tmp_planar_pool;
 	txl->planar_depth = tmp_planar_depth;
-
-	DRW_framebuffer_texture_detach(txl->planar_pool);
-	DRW_framebuffer_texture_detach(txl->planar_depth);
 }
 
 static void render_world_to_probe(EEVEE_ViewLayerData *sldata, EEVEE_PassList *psl)
@@ -1397,10 +1370,6 @@ static void render_world_to_probe(EEVEE_ViewLayerData *sldata, EEVEE_PassList *p
 	perspective_m4(winmat, -0.1f, 0.1f, -0.1f, 0.1f, 0.1f, 1.0f);
 	invert_m4_m4(wininv, winmat);
 
-	/* Detach to rebind the right cubeface. */
-	DRW_framebuffer_bind(sldata->probe_fb);
-	DRW_framebuffer_texture_detach(sldata->probe_rt);
-	DRW_framebuffer_texture_detach(sldata->probe_depth_rt);
 	for (int i = 0; i < 6; ++i) {
 		/* Setup custom matrices */
 		copy_m4_m4(viewmat, cubefacemat[i]);
@@ -1409,15 +1378,10 @@ static void render_world_to_probe(EEVEE_ViewLayerData *sldata, EEVEE_PassList *p
 		invert_m4_m4(viewinv, viewmat);
 		DRW_viewport_matrix_override_set_all(&matstate);
 
-		DRW_framebuffer_cubeface_attach(sldata->probe_fb, sldata->probe_rt, 0, i, 0);
-		DRW_framebuffer_viewport_size(sldata->probe_fb, 0, 0, pinfo->target_size, pinfo->target_size);
-
+		GPU_framebuffer_bind(sldata->probe_face_fb[i]);
+		GPU_framebuffer_clear_depth(sldata->probe_face_fb[i], 1.0f);
 		DRW_draw_pass(psl->probe_background);
-
-		DRW_framebuffer_texture_detach(sldata->probe_rt);
 	}
-	DRW_framebuffer_texture_attach(sldata->probe_fb, sldata->probe_rt, 0, 0);
-	DRW_framebuffer_texture_attach(sldata->probe_fb, sldata->probe_depth_rt, 0, 0);
 }
 
 static void lightprobe_cell_grid_location_get(EEVEE_LightGrid *egrid, int cell_idx, float r_local_cell[3])
@@ -1459,12 +1423,13 @@ static void lightprobes_refresh_world(EEVEE_ViewLayerData *sldata, EEVEE_Data *v
 	}
 	if (pinfo->update_world & PROBE_UPDATE_GRID) {
 		diffuse_filter_probe(sldata, vedata, psl, 0, 0.0, 0.0, 0.0, 0.0, 1.0);
+
 		SWAP(GPUTexture *, sldata->irradiance_pool, sldata->irradiance_rt);
-		DRW_framebuffer_texture_detach(sldata->probe_pool);
-		DRW_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->irradiance_rt, 0, 0);
+
+		GPU_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->irradiance_rt, 0, 0);
+		GPU_framebuffer_bind(sldata->probe_filter_fb);
 		DRW_draw_pass(psl->probe_grid_fill);
-		DRW_framebuffer_texture_detach(sldata->irradiance_rt);
-		DRW_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->probe_pool, 0, 0);
+
 		common_data->prb_num_render_grid = 1;
 		/* Reset volume history. */
 		stl->effects->volume_current_sample = -1;
@@ -1486,27 +1451,26 @@ static void lightprobes_refresh_initialize_grid(EEVEE_ViewLayerData *sldata, EEV
 		/* Grid is already initialized, nothing to do. */
 		return;
 	}
-	DRW_framebuffer_texture_detach(sldata->probe_pool);
 	/* Flood fill with world irradiance. */
-	DRW_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->irradiance_rt, 0, 0);
-	DRW_framebuffer_bind(sldata->probe_filter_fb);
+	GPU_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->irradiance_rt, 0, 0);
+	GPU_framebuffer_bind(sldata->probe_filter_fb);
 	DRW_draw_pass(psl->probe_grid_fill);
-	DRW_framebuffer_texture_detach(sldata->irradiance_rt);
 
 	SWAP(GPUTexture *, sldata->irradiance_pool, sldata->irradiance_rt);
-	DRW_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->irradiance_rt, 0, 0);
+
+	GPU_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->irradiance_rt, 0, 0);
+	GPU_framebuffer_bind(sldata->probe_filter_fb);
 	DRW_draw_pass(psl->probe_grid_fill);
 
-	DRW_framebuffer_texture_detach(sldata->irradiance_rt);
 	SWAP(GPUTexture *, sldata->irradiance_pool, sldata->irradiance_rt);
-	/* Reattach to have a valid framebuffer. */
-	DRW_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->probe_pool, 0, 0);
+
 	pinfo->grid_initialized = true;
 }
 
 void EEVEE_lightprobes_refresh_planar(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
 {
 	EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
+	EEVEE_FramebufferList *fbl = vedata->fbl;
 	EEVEE_TextureList *txl = vedata->txl;
 	Object *ob;
 	EEVEE_LightProbesInfo *pinfo = sldata->probes;
@@ -1547,7 +1511,12 @@ void EEVEE_lightprobes_refresh_planar(EEVEE_ViewLayerData *sldata, EEVEE_Data *v
 	if ((vedata->stl->effects->enabled_effects & EFFECT_SSR) != 0) {
 		const int max_lod = 9;
 		DRW_stats_group_start("Planar Probe Downsample");
-		DRW_framebuffer_recursive_downsample(vedata->fbl->downsample_fb, txl->planar_pool, max_lod, &downsample_planar, vedata);
+
+		GPU_framebuffer_ensure_config(&fbl->planar_downsample_fb, {
+			GPU_ATTACHMENT_NONE,
+			GPU_ATTACHMENT_TEXTURE(txl->planar_pool)
+		});
+		GPU_framebuffer_recursive_downsample(fbl->planar_downsample_fb, max_lod, &downsample_planar, vedata);
 		/* For shading, save max level of the planar map */
 		common_data->prb_lod_planar_max = (float)(max_lod);
 		DRW_stats_group_end();
@@ -1738,12 +1707,11 @@ static void lightprobes_refresh_all_no_world(EEVEE_ViewLayerData *sldata, EEVEE_
 			}
 			/* Reset the next buffer so we can see the progress. */
 			/* irradiance_rt is already the next rt because of the previous SWAP */
-			DRW_framebuffer_texture_detach(sldata->probe_pool);
-			DRW_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->irradiance_rt, 0, 0);
-			DRW_framebuffer_bind(sldata->probe_filter_fb);
+			GPU_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->irradiance_rt, 0, 0);
+			GPU_framebuffer_bind(sldata->probe_filter_fb);
 			DRW_draw_pass(psl->probe_grid_fill);
-			DRW_framebuffer_texture_detach(sldata->irradiance_rt);
-			DRW_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->probe_pool, 0, 0);
+
+			GPU_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->probe_pool, 0, 0);
 			/* Swap AFTER */
 			SWAP(GPUTexture *, sldata->irradiance_pool, sldata->irradiance_rt);
 		}
diff --git a/source/blender/draw/engines/eevee/eevee_lights.c b/source/blender/draw/engines/eevee/eevee_lights.c
index b4afb8bb555da2066242098fce066af975d1b256..7496142a26e453daf6b56d7c24f9f3118ad35491 100644
--- a/source/blender/draw/engines/eevee/eevee_lights.c
+++ b/source/blender/draw/engines/eevee/eevee_lights.c
@@ -107,50 +107,6 @@ void EEVEE_lights_init(EEVEE_ViewLayerData *sldata)
 	if (!e_data.shadow_sh) {
 		e_data.shadow_sh = DRW_shader_create(
 		        datatoc_shadow_vert_glsl, datatoc_shadow_geom_glsl, datatoc_shadow_frag_glsl, NULL);
-
-		DynStr *ds_frag = BLI_dynstr_new();
-		BLI_dynstr_append(ds_frag, datatoc_concentric_samples_lib_glsl);
-		BLI_dynstr_append(ds_frag, datatoc_shadow_store_frag_glsl);
-		char *store_shadow_shader_str = BLI_dynstr_get_cstring(ds_frag);
-		BLI_dynstr_free(ds_frag);
-
-		e_data.shadow_store_cube_sh[SHADOW_ESM] = DRW_shader_create_fullscreen(
-		        store_shadow_shader_str,
-		        "#define ESM\n");
-		e_data.shadow_store_cascade_sh[SHADOW_ESM] = DRW_shader_create_fullscreen(
-		        store_shadow_shader_str,
-		        "#define ESM\n"
-		        "#define CSM\n");
-
-		e_data.shadow_store_cube_sh[SHADOW_VSM] = DRW_shader_create_fullscreen(
-		        store_shadow_shader_str,
-		        "#define VSM\n");
-		e_data.shadow_store_cascade_sh[SHADOW_VSM] = DRW_shader_create_fullscreen(
-		        store_shadow_shader_str,
-		        "#define VSM\n"
-		        "#define CSM\n");
-
-		MEM_freeN(store_shadow_shader_str);
-
-		e_data.shadow_copy_cube_sh[SHADOW_ESM] = DRW_shader_create_fullscreen(
-		        datatoc_shadow_copy_frag_glsl,
-		        "#define ESM\n"
-		        "#define COPY\n");
-		e_data.shadow_copy_cascade_sh[SHADOW_ESM] = DRW_shader_create_fullscreen(
-		        datatoc_shadow_copy_frag_glsl,
-		        "#define ESM\n"
-		        "#define COPY\n"
-		        "#define CSM\n");
-
-		e_data.shadow_copy_cube_sh[SHADOW_VSM] = DRW_shader_create_fullscreen(
-		        datatoc_shadow_copy_frag_glsl,
-		        "#define VSM\n"
-		        "#define COPY\n");
-		e_data.shadow_copy_cascade_sh[SHADOW_VSM] = DRW_shader_create_fullscreen(
-		        datatoc_shadow_copy_frag_glsl,
-		        "#define VSM\n"
-		        "#define COPY\n"
-		        "#define CSM\n");
 	}
 
 	if (!sldata->lamps) {
@@ -204,6 +160,60 @@ void EEVEE_lights_init(EEVEE_ViewLayerData *sldata)
 		linfo->shadow_cube_target_size = new_cube_target_size;
 		linfo->shadow_render_data.cube_texel_size = 1.0 / (float)linfo->shadow_cube_target_size;
 	}
+
+	/* only compile the ones needed. reduce startup time. */
+	if ((sh_method == SHADOW_ESM) && !e_data.shadow_store_cube_sh[SHADOW_ESM]) {
+		DynStr *ds_frag = BLI_dynstr_new();
+		BLI_dynstr_append(ds_frag, datatoc_concentric_samples_lib_glsl);
+		BLI_dynstr_append(ds_frag, datatoc_shadow_store_frag_glsl);
+		char *store_shadow_shader_str = BLI_dynstr_get_cstring(ds_frag);
+		BLI_dynstr_free(ds_frag);
+
+		e_data.shadow_store_cube_sh[SHADOW_ESM] = DRW_shader_create_fullscreen(
+		        store_shadow_shader_str,
+		        "#define ESM\n");
+		e_data.shadow_store_cascade_sh[SHADOW_ESM] = DRW_shader_create_fullscreen(
+		        store_shadow_shader_str,
+		        "#define ESM\n"
+		        "#define CSM\n");
+		MEM_freeN(store_shadow_shader_str);
+
+		e_data.shadow_copy_cube_sh[SHADOW_ESM] = DRW_shader_create_fullscreen(
+		        datatoc_shadow_copy_frag_glsl,
+		        "#define ESM\n"
+		        "#define COPY\n");
+		e_data.shadow_copy_cascade_sh[SHADOW_ESM] = DRW_shader_create_fullscreen(
+		        datatoc_shadow_copy_frag_glsl,
+		        "#define ESM\n"
+		        "#define COPY\n"
+		        "#define CSM\n");
+	}
+	else if ((sh_method == SHADOW_VSM) && !e_data.shadow_store_cube_sh[SHADOW_VSM]) {
+		DynStr *ds_frag = BLI_dynstr_new();
+		BLI_dynstr_append(ds_frag, datatoc_concentric_samples_lib_glsl);
+		BLI_dynstr_append(ds_frag, datatoc_shadow_store_frag_glsl);
+		char *store_shadow_shader_str = BLI_dynstr_get_cstring(ds_frag);
+		BLI_dynstr_free(ds_frag);
+
+		e_data.shadow_store_cube_sh[SHADOW_VSM] = DRW_shader_create_fullscreen(
+		        store_shadow_shader_str,
+		        "#define VSM\n");
+		e_data.shadow_store_cascade_sh[SHADOW_VSM] = DRW_shader_create_fullscreen(
+		        store_shadow_shader_str,
+		        "#define VSM\n"
+		        "#define CSM\n");
+		MEM_freeN(store_shadow_shader_str);
+
+		e_data.shadow_copy_cube_sh[SHADOW_VSM] = DRW_shader_create_fullscreen(
+		        datatoc_shadow_copy_frag_glsl,
+		        "#define VSM\n"
+		        "#define COPY\n");
+		e_data.shadow_copy_cascade_sh[SHADOW_VSM] = DRW_shader_create_fullscreen(
+		        datatoc_shadow_copy_frag_glsl,
+		        "#define VSM\n"
+		        "#define COPY\n"
+		        "#define CSM\n");
+	}
 }
 
 void EEVEE_lights_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
@@ -231,7 +241,7 @@ void EEVEE_lights_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
 
 		DRWShadingGroup *grp = DRW_shgroup_create(
 		        e_data.shadow_store_cube_sh[linfo->shadow_method], psl->shadow_cube_store_pass);
-		DRW_shgroup_uniform_buffer(grp, "shadowTexture", &sldata->shadow_cube_blur);
+		DRW_shgroup_uniform_texture_ref(grp, "shadowTexture", &sldata->shadow_cube_blur);
 		DRW_shgroup_uniform_block(grp, "shadow_render_block", sldata->shadow_render_ubo);
 		DRW_shgroup_uniform_float(grp, "shadowFilterSize", &linfo->filter_size, 1);
 		DRW_shgroup_call_add(grp, DRW_cache_fullscreen_quad_get(), NULL);
@@ -242,7 +252,7 @@ void EEVEE_lights_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
 
 		DRWShadingGroup *grp = DRW_shgroup_create(
 		        e_data.shadow_store_cascade_sh[linfo->shadow_method], psl->shadow_cascade_store_pass);
-		DRW_shgroup_uniform_buffer(grp, "shadowTexture", &sldata->shadow_cascade_blur);
+		DRW_shgroup_uniform_texture_ref(grp, "shadowTexture", &sldata->shadow_cascade_blur);
 		DRW_shgroup_uniform_block(grp, "shadow_render_block", sldata->shadow_render_ubo);
 		DRW_shgroup_uniform_int(grp, "cascadeId", &linfo->current_shadow_cascade, 1);
 		DRW_shgroup_uniform_float(grp, "shadowFilterSize", &linfo->filter_size, 1);
@@ -254,7 +264,7 @@ void EEVEE_lights_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
 
 		DRWShadingGroup *grp = DRW_shgroup_create(
 		        e_data.shadow_copy_cube_sh[linfo->shadow_method], psl->shadow_cube_copy_pass);
-		DRW_shgroup_uniform_buffer(grp, "shadowTexture", &sldata->shadow_cube_target);
+		DRW_shgroup_uniform_texture_ref(grp, "shadowTexture", &sldata->shadow_cube_target);
 		DRW_shgroup_uniform_block(grp, "shadow_render_block", sldata->shadow_render_ubo);
 		DRW_shgroup_uniform_float(grp, "shadowFilterSize", &linfo->filter_size, 1);
 		DRW_shgroup_uniform_int(grp, "faceId", &linfo->current_shadow_face, 1);
@@ -266,7 +276,7 @@ void EEVEE_lights_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
 
 		DRWShadingGroup *grp = DRW_shgroup_create(
 		        e_data.shadow_copy_cascade_sh[linfo->shadow_method], psl->shadow_cascade_copy_pass);
-		DRW_shgroup_uniform_buffer(grp, "shadowTexture", &sldata->shadow_cascade_target);
+		DRW_shgroup_uniform_texture_ref(grp, "shadowTexture", &sldata->shadow_cascade_target);
 		DRW_shgroup_uniform_block(grp, "shadow_render_block", sldata->shadow_render_ubo);
 		DRW_shgroup_uniform_float(grp, "shadowFilterSize", &linfo->filter_size, 1);
 		DRW_shgroup_uniform_int(grp, "cascadeId", &linfo->current_shadow_cascade, 1);
@@ -505,7 +515,6 @@ void EEVEE_lights_cache_finish(EEVEE_ViewLayerData *sldata)
 		        linfo->shadow_size, linfo->shadow_size, MAX_CASCADE_NUM, shadow_pool_format, DRW_TEX_FILTER, NULL);
 	}
 
-	/* Initialize Textures Array first so DRW_framebuffer_init just bind them. */
 	if (!sldata->shadow_pool) {
 		/* All shadows fit in this array */
 		sldata->shadow_pool = DRW_texture_create_2D_array(
@@ -514,19 +523,18 @@ void EEVEE_lights_cache_finish(EEVEE_ViewLayerData *sldata)
 	}
 
 	/* Render FB */
-	DRWFboTexture tex_cascade = {&sldata->shadow_cube_target, DRW_TEX_DEPTH_24, 0};
-	DRW_framebuffer_init(&sldata->shadow_target_fb, &draw_engine_eevee_type,
-	                     linfo->shadow_size, linfo->shadow_size,
-	                     &tex_cascade, 1);
+	GPU_framebuffer_ensure_config(&sldata->shadow_cube_target_fb, {
+		GPU_ATTACHMENT_TEXTURE(sldata->shadow_cube_target)
+	});
+	GPU_framebuffer_ensure_config(&sldata->shadow_cascade_target_fb, {
+		GPU_ATTACHMENT_TEXTURE(sldata->shadow_cascade_target)
+	});
 
 	/* Storage FB */
-	DRWFboTexture tex_pool = {&sldata->shadow_pool, shadow_pool_format, DRW_TEX_FILTER};
-	DRW_framebuffer_init(&sldata->shadow_store_fb, &draw_engine_eevee_type,
-	                     linfo->shadow_size, linfo->shadow_size,
-	                     &tex_pool, 1);
-
-	/* Restore */
-	DRW_framebuffer_texture_detach(sldata->shadow_cube_target);
+	GPU_framebuffer_ensure_config(&sldata->shadow_store_fb, {
+		GPU_ATTACHMENT_NONE,
+		GPU_ATTACHMENT_TEXTURE(sldata->shadow_pool)
+	});
 
 	/* Update Lamps UBOs. */
 	EEVEE_lights_update(sldata);
@@ -1033,7 +1041,6 @@ void EEVEE_draw_shadows(EEVEE_ViewLayerData *sldata, EEVEE_PassList *psl)
 	EEVEE_LampsInfo *linfo = sldata->lamps;
 	Object *ob;
 	int i;
-	float clear_col[4] = {FLT_MAX};
 
 	DRWMatrixState saved_mats;
 
@@ -1042,7 +1049,6 @@ void EEVEE_draw_shadows(EEVEE_ViewLayerData *sldata, EEVEE_PassList *psl)
 
 	/* Cube Shadow Maps */
 	DRW_stats_group_start("Cube Shadow Maps");
-	DRW_framebuffer_texture_attach(sldata->shadow_target_fb, sldata->shadow_cube_target, 0, 0);
 	/* Render each shadow to one layer of the array */
 	for (i = 0; (ob = linfo->shadow_cube_ref[i]) && (i < MAX_SHADOW_CUBE); i++) {
 		EEVEE_LampEngineData *led = EEVEE_lamp_data_ensure(ob);
@@ -1073,11 +1079,10 @@ void EEVEE_draw_shadows(EEVEE_ViewLayerData *sldata, EEVEE_PassList *psl)
 
 		eevee_shadows_cube_culling_frustum(srd);
 
-		DRW_framebuffer_bind(sldata->shadow_target_fb);
-		DRW_framebuffer_clear(true, true, false, clear_col, 1.0f);
-
 		/* Render shadow cube */
 		linfo->shadow_instance_count = 6;
+		GPU_framebuffer_bind(sldata->shadow_cube_target_fb);
+		GPU_framebuffer_clear_depth(sldata->shadow_cube_target_fb, 1.0f);
 		DRW_draw_pass(psl->shadow_pass);
 
 		/* 0.001f is arbitrary, but it should be relatively small so that filter size is not too big. */
@@ -1091,10 +1096,10 @@ void EEVEE_draw_shadows(EEVEE_ViewLayerData *sldata, EEVEE_PassList *psl)
 		     linfo->current_shadow_face++)
 		{
 			/* Copy using a small 3x3 box filter */
-			DRW_framebuffer_cubeface_attach(sldata->shadow_store_fb, sldata->shadow_cube_blur, 0, linfo->current_shadow_face, 0);
-			DRW_framebuffer_bind(sldata->shadow_store_fb);
+			GPU_framebuffer_texture_cubeface_attach(sldata->shadow_store_fb, sldata->shadow_cube_blur, 0,
+			                                        linfo->current_shadow_face, 0);
+			GPU_framebuffer_bind(sldata->shadow_store_fb);
 			DRW_draw_pass(psl->shadow_cube_copy_pass);
-			DRW_framebuffer_texture_detach(sldata->shadow_cube_blur);
 		}
 
 		/* Push it to shadowmap array */
@@ -1118,22 +1123,19 @@ void EEVEE_draw_shadows(EEVEE_ViewLayerData *sldata, EEVEE_PassList *psl)
 		srd->shadow_inv_samples_ct = 1.0f / (float)srd->shadow_samples_ct;
 		DRW_uniformbuffer_update(sldata->shadow_render_ubo, srd);
 
-		DRW_framebuffer_texture_layer_attach(sldata->shadow_store_fb, sldata->shadow_pool, 0, evscd->layer_id, 0);
-		DRW_framebuffer_bind(sldata->shadow_store_fb);
+		GPU_framebuffer_texture_layer_attach(sldata->shadow_store_fb, sldata->shadow_pool, 0, evscd->layer_id, 0);
+		GPU_framebuffer_bind(sldata->shadow_store_fb);
 		DRW_draw_pass(psl->shadow_cube_store_pass);
 
 		led->need_update = false;
 	}
 	linfo->update_flag &= ~LIGHT_UPDATE_SHADOW_CUBE;
-
-	DRW_framebuffer_texture_detach(sldata->shadow_cube_target);
 	DRW_stats_group_end();
 
 	DRW_viewport_matrix_override_set_all(&saved_mats);
 
 	/* Cascaded Shadow Maps */
 	DRW_stats_group_start("Cascaded Shadow Maps");
-	DRW_framebuffer_texture_attach(sldata->shadow_target_fb, sldata->shadow_cascade_target, 0, 0);
 	for (i = 0; (ob = linfo->shadow_cascade_ref[i]) && (i < MAX_SHADOW_CASCADE); i++) {
 		EEVEE_LampEngineData *led = EEVEE_lamp_data_ensure(ob);
 		Lamp *la = (Lamp *)ob->data;
@@ -1150,13 +1152,12 @@ void EEVEE_draw_shadows(EEVEE_ViewLayerData *sldata, EEVEE_PassList *psl)
 		}
 		DRW_uniformbuffer_update(sldata->shadow_render_ubo, &linfo->shadow_render_data);
 
-		DRW_framebuffer_bind(sldata->shadow_target_fb);
-		DRW_framebuffer_clear(false, true, false, NULL, 1.0);
-
 		eevee_shadows_cascade_culling_frustum(evscd);
 
 		/* Render shadow cascades */
 		linfo->shadow_instance_count = la->cascade_count;
+		GPU_framebuffer_bind(sldata->shadow_cascade_target_fb);
+		GPU_framebuffer_clear_depth(sldata->shadow_cascade_target_fb, 1.0);
 		DRW_draw_pass(psl->shadow_pass);
 
 		/* TODO: OPTI: Filter all cascade in one/two draw call */
@@ -1170,11 +1171,10 @@ void EEVEE_draw_shadows(EEVEE_ViewLayerData *sldata, EEVEE_PassList *psl)
 
 			/* Copy using a small 3x3 box filter */
 			linfo->filter_size = linfo->shadow_render_data.stored_texel_size * ((filter_pixel_size > 1.0f) ? 1.0f : 0.0f);
-			DRW_framebuffer_texture_layer_attach(
+			GPU_framebuffer_texture_layer_attach(
 			        sldata->shadow_store_fb, sldata->shadow_cascade_blur, 0, linfo->current_shadow_cascade, 0);
-			DRW_framebuffer_bind(sldata->shadow_store_fb);
+			GPU_framebuffer_bind(sldata->shadow_store_fb);
 			DRW_draw_pass(psl->shadow_cascade_copy_pass);
-			DRW_framebuffer_texture_detach(sldata->shadow_cascade_blur);
 
 			/* Push it to shadowmap array and blur more */
 
@@ -1198,13 +1198,12 @@ void EEVEE_draw_shadows(EEVEE_ViewLayerData *sldata, EEVEE_PassList *psl)
 			DRW_uniformbuffer_update(sldata->shadow_render_ubo, &linfo->shadow_render_data);
 
 			int layer = evscd->layer_id + linfo->current_shadow_cascade;
-			DRW_framebuffer_texture_layer_attach(sldata->shadow_store_fb, sldata->shadow_pool, 0, layer, 0);
-			DRW_framebuffer_bind(sldata->shadow_store_fb);
+			GPU_framebuffer_texture_layer_attach(sldata->shadow_store_fb, sldata->shadow_pool, 0, layer, 0);
+			GPU_framebuffer_bind(sldata->shadow_store_fb);
 			DRW_draw_pass(psl->shadow_cascade_store_pass);
 		}
 	}
 
-	DRW_framebuffer_texture_detach(sldata->shadow_cascade_target);
 	DRW_stats_group_end();
 
 	DRW_viewport_matrix_override_set_all(&saved_mats);
diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c
index 6215445e11371f7996a77eab405fb5d5596293a0..fc4439a253c186b6caa996602bee0b0356b54892 100644
--- a/source/blender/draw/engines/eevee/eevee_materials.c
+++ b/source/blender/draw/engines/eevee/eevee_materials.c
@@ -138,9 +138,9 @@ static struct GPUTexture *create_ggx_lut_texture(int UNUSED(w), int UNUSED(h))
 	tex = DRW_texture_create_2D(w, h, DRW_TEX_RG_16, DRW_TEX_FILTER, (float *)texels);
 
 	DRWFboTexture tex_filter = {&tex, DRW_TEX_RG_16, DRW_TEX_FILTER};
-	DRW_framebuffer_init(&fb, &draw_engine_eevee_type, w, h, &tex_filter, 1);
+	GPU_framebuffer_init(&fb, &draw_engine_eevee_type, w, h, &tex_filter, 1);
 
-	DRW_framebuffer_bind(fb);
+	GPU_framebuffer_bind(fb);
 	DRW_draw_pass(pass);
 
 	float *data = MEM_mallocN(sizeof(float[3]) * w * h, "lut");
@@ -200,9 +200,9 @@ static struct GPUTexture *create_ggx_refraction_lut_texture(int w, int h)
 	tex = DRW_texture_create_2D(w, h, DRW_TEX_R_16, DRW_TEX_FILTER, (float *)texels);
 
 	DRWFboTexture tex_filter = {&tex, DRW_TEX_R_16, DRW_TEX_FILTER};
-	DRW_framebuffer_init(&fb, &draw_engine_eevee_type, w, h, &tex_filter, 1);
+	GPU_framebuffer_init(&fb, &draw_engine_eevee_type, w, h, &tex_filter, 1);
 
-	DRW_framebuffer_bind(fb);
+	GPU_framebuffer_bind(fb);
 
 	float *data = MEM_mallocN(sizeof(float[3]) * w * h, "lut");
 
@@ -216,7 +216,7 @@ static struct GPUTexture *create_ggx_refraction_lut_texture(int w, int h)
 		a2 = powf(roughness, 4.0f);
 		DRW_draw_pass(pass);
 
-		DRW_framebuffer_read_data(0, 0, w, h, 3, 0, data);
+		GPU_framebuffer_read_data(0, 0, w, h, 3, 0, data);
 
 #if 1
 		fprintf(f, "\t{\n\t\t");
@@ -370,42 +370,42 @@ static void add_standard_uniforms(
 	/* TODO if glossy or diffuse bsdf */
 	if (true) {
 		DRW_shgroup_uniform_texture(shgrp, "utilTex", e_data.util_tex);
-		DRW_shgroup_uniform_buffer(shgrp, "shadowTexture", &sldata->shadow_pool);
-		DRW_shgroup_uniform_buffer(shgrp, "maxzBuffer", &vedata->txl->maxzbuffer);
+		DRW_shgroup_uniform_texture_ref(shgrp, "shadowTexture", &sldata->shadow_pool);
+		DRW_shgroup_uniform_texture_ref(shgrp, "maxzBuffer", &vedata->txl->maxzbuffer);
 
 		if ((vedata->stl->effects->enabled_effects & EFFECT_GTAO) != 0) {
-			DRW_shgroup_uniform_buffer(shgrp, "horizonBuffer", &vedata->txl->gtao_horizons);
+			DRW_shgroup_uniform_texture_ref(shgrp, "horizonBuffer", &vedata->stl->effects->gtao_horizons);
 		}
 		else {
 			/* Use maxzbuffer as fallback to avoid sampling problem on certain platform, see: T52593 */
-			DRW_shgroup_uniform_buffer(shgrp, "horizonBuffer", &vedata->txl->maxzbuffer);
+			DRW_shgroup_uniform_texture_ref(shgrp, "horizonBuffer", &vedata->txl->maxzbuffer);
 		}
 	}
 
 	/* TODO if diffuse bsdf */
 	if (true) {
-		DRW_shgroup_uniform_buffer(shgrp, "irradianceGrid", &sldata->irradiance_pool);
+		DRW_shgroup_uniform_texture_ref(shgrp, "irradianceGrid", &sldata->irradiance_pool);
 	}
 
 	/* TODO if glossy bsdf */
 	if (true) {
-		DRW_shgroup_uniform_buffer(shgrp, "probeCubes", &sldata->probe_pool);
-		DRW_shgroup_uniform_buffer(shgrp, "probePlanars", &vedata->txl->planar_pool);
+		DRW_shgroup_uniform_texture_ref(shgrp, "probeCubes", &sldata->probe_pool);
+		DRW_shgroup_uniform_texture_ref(shgrp, "probePlanars", &vedata->txl->planar_pool);
 		DRW_shgroup_uniform_int(shgrp, "outputSsrId", ssr_id, 1);
 	}
 
 	if (use_ssrefraction) {
 		BLI_assert(refract_depth != NULL);
 		DRW_shgroup_uniform_float(shgrp, "refractionDepth", refract_depth, 1);
-		DRW_shgroup_uniform_buffer(shgrp, "colorBuffer", &vedata->txl->refract_color);
+		DRW_shgroup_uniform_texture_ref(shgrp, "colorBuffer", &vedata->txl->refract_color);
 	}
 
 	if ((vedata->stl->effects->enabled_effects & EFFECT_VOLUMETRIC) != 0 &&
 	     use_alpha_blend)
 	{
 		/* Do not use history buffers as they already have been swapped */
-		DRW_shgroup_uniform_buffer(shgrp, "inScattering", &vedata->txl->volume_scatter);
-		DRW_shgroup_uniform_buffer(shgrp, "inTransmittance", &vedata->txl->volume_transmittance);
+		DRW_shgroup_uniform_texture_ref(shgrp, "inScattering", &vedata->txl->volume_scatter);
+		DRW_shgroup_uniform_texture_ref(shgrp, "inTransmittance", &vedata->txl->volume_transmittance);
 	}
 }
 
@@ -482,10 +482,8 @@ void EEVEE_update_noise(EEVEE_PassList *psl, EEVEE_FramebufferList *fbl, const d
 
 	/* Attach & detach because we don't currently support multiple FB per texture,
 	 * and this would be the case for multiple viewport. */
-	DRW_framebuffer_texture_layer_attach(fbl->update_noise_fb, e_data.util_tex, 0, 2, 0);
-	DRW_framebuffer_bind(fbl->update_noise_fb);
+	GPU_framebuffer_bind(fbl->update_noise_fb);
 	DRW_draw_pass(psl->update_noise_pass);
-	DRW_framebuffer_texture_detach(e_data.util_tex);
 }
 
 static void EEVEE_update_viewvecs(float invproj[4][4], float winmat[4][4], float (*r_viewvecs)[4])
@@ -623,9 +621,10 @@ void EEVEE_materials_init(EEVEE_ViewLayerData *sldata, EEVEE_StorageList *stl, E
 
 	{
 		/* Update noise Framebuffer. */
-		if (fbl->update_noise_fb == NULL) {
-			fbl->update_noise_fb = DRW_framebuffer_create();
-		}
+		GPU_framebuffer_ensure_config(&fbl->update_noise_fb, {
+			GPU_ATTACHMENT_NONE,
+			GPU_ATTACHMENT_TEXTURE_LAYER(e_data.util_tex, 2)
+		});
 	}
 }
 
diff --git a/source/blender/draw/engines/eevee/eevee_mist.c b/source/blender/draw/engines/eevee/eevee_mist.c
index e221ed865b53738d632fefcffff5d7f7e4d34b23..1675142613dfc07f17ae904072431500e04a96ec 100644
--- a/source/blender/draw/engines/eevee/eevee_mist.c
+++ b/source/blender/draw/engines/eevee/eevee_mist.c
@@ -55,7 +55,6 @@ void EEVEE_mist_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
 	EEVEE_PrivateData *g_data = stl->g_data;
 	Scene *scene = draw_ctx->scene;
 
-	const float *viewport_size = DRW_viewport_size_get();
 	float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
 
 	if (e_data.mist_sh == NULL) {
@@ -71,13 +70,16 @@ void EEVEE_mist_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
 	}
 
 	/* Create FrameBuffer. */
-	DRWFboTexture tex_data = {&txl->mist_accum, DRW_TEX_R_32, 0}; /* Should be enough precision for many samples. */
-	DRW_framebuffer_init(&fbl->mist_accum_fb, &draw_engine_eevee_type, (int)viewport_size[0], (int)viewport_size[1],
-	                     &tex_data, 1);
+	DRW_texture_ensure_fullscreen_2D(&txl->mist_accum, DRW_TEX_R_32, 0); /* Should be enough precision for many samples. */
+
+	GPU_framebuffer_ensure_config(&fbl->mist_accum_fb, {
+		GPU_ATTACHMENT_NONE,
+		GPU_ATTACHMENT_TEXTURE(txl->mist_accum)
+	});
 
 	/* Clear texture. */
-	DRW_framebuffer_bind(fbl->mist_accum_fb);
-	DRW_framebuffer_clear(true, false, false, clear, 0.0f);
+	GPU_framebuffer_bind(fbl->mist_accum_fb);
+	GPU_framebuffer_clear_color(fbl->mist_accum_fb, clear);
 
 	/* Mist settings. */
 	if (scene && scene->world) {
@@ -111,7 +113,7 @@ void EEVEE_mist_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
 	/* Create Pass and shgroup. */
 	psl->mist_accum_ps = DRW_pass_create("Mist Accum", DRW_STATE_WRITE_COLOR | DRW_STATE_ADDITIVE);
 	DRWShadingGroup *grp = DRW_shgroup_create(e_data.mist_sh, psl->mist_accum_ps);
-	DRW_shgroup_uniform_buffer(grp, "depthBuffer", &dtxl->depth);
+	DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
 	DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
 	DRW_shgroup_uniform_vec3(grp, "mistSettings", &g_data->mist_start, 1);
 	DRW_shgroup_call_add(grp, DRW_cache_fullscreen_quad_get(), NULL);
@@ -123,11 +125,11 @@ void EEVEE_mist_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Dat
 	EEVEE_PassList *psl = vedata->psl;
 
 	if (fbl->mist_accum_fb != NULL) {
-		DRW_framebuffer_bind(fbl->mist_accum_fb);
+		GPU_framebuffer_bind(fbl->mist_accum_fb);
 		DRW_draw_pass(psl->mist_accum_ps);
 
 		/* Restore */
-		DRW_framebuffer_bind(fbl->main);
+		GPU_framebuffer_bind(fbl->main_fb);
 	}
 }
 
diff --git a/source/blender/draw/engines/eevee/eevee_motion_blur.c b/source/blender/draw/engines/eevee/eevee_motion_blur.c
index 53fff5de50e1043d1e537a9b28687d266d628961..9b19163c8d7eaad325810c324a5aad4faf6b01ef 100644
--- a/source/blender/draw/engines/eevee/eevee_motion_blur.c
+++ b/source/blender/draw/engines/eevee/eevee_motion_blur.c
@@ -59,7 +59,6 @@ static void eevee_motion_blur_camera_get_matrix_at_time(
         float time,
         float r_mat[4][4])
 {
-	EvaluationContext eval_ctx;
 	float obmat[4][4];
 
 	/* HACK */
@@ -68,19 +67,9 @@ static void eevee_motion_blur_camera_get_matrix_at_time(
 	memcpy(&camdata_cpy, camera->data, sizeof(camdata_cpy));
 	cam_cpy.data = &camdata_cpy;
 
-	/* NOTE: Mode corresponds to old usage of eval_ctx from viewport (which was
-	 * actually coming from bmain). It was always DAG_EVAL_VIEWPORT. For F12
-	 * render this should be DAG_EVAL_RENDER, but the whole hack is to be
-	 * reconsidered first anyway.
-	 */
 	const DRWContextState *draw_ctx = DRW_context_state_get();
-	DEG_evaluation_context_init_from_scene(
-	        &eval_ctx,
-	        scene,
-	        draw_ctx->view_layer,
-	        draw_ctx->engine_type,
-	        draw_ctx->object_mode,
-	        DAG_EVAL_VIEWPORT);
+	/* We will be modifying time, so we create copy of eval_ctx. */
+	EvaluationContext eval_ctx = draw_ctx->eval_ctx;
 	eval_ctx.ctime = time;
 
 	/* Past matrix */
@@ -201,8 +190,8 @@ void EEVEE_motion_blur_cache_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Dat
 		DRW_shgroup_uniform_int(grp, "samples", &effects->motion_blur_samples, 1);
 		DRW_shgroup_uniform_mat4(grp, "currInvViewProjMatrix", (float *)effects->current_ndc_to_world);
 		DRW_shgroup_uniform_mat4(grp, "pastViewProjMatrix", (float *)effects->past_world_to_ndc);
-		DRW_shgroup_uniform_buffer(grp, "colorBuffer", &effects->source_buffer);
-		DRW_shgroup_uniform_buffer(grp, "depthBuffer", &dtxl->depth);
+		DRW_shgroup_uniform_texture_ref(grp, "colorBuffer", &effects->source_buffer);
+		DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
 		DRW_shgroup_call_add(grp, quad, NULL);
 	}
 }
@@ -217,7 +206,7 @@ void EEVEE_motion_blur_draw(EEVEE_Data *vedata)
 
 	/* Motion Blur */
 	if ((effects->enabled_effects & EFFECT_MOTION_BLUR) != 0) {
-		DRW_framebuffer_bind(effects->target_buffer);
+		GPU_framebuffer_bind(effects->target_buffer);
 		DRW_draw_pass(psl->motion_blur);
 		SWAP_BUFFERS();
 	}
diff --git a/source/blender/draw/engines/eevee/eevee_occlusion.c b/source/blender/draw/engines/eevee/eevee_occlusion.c
index d7d022e9b98372aef7479b217185f91c41506ed1..9fd7f6d4126bd35784a0fa8031ae7b0325a8cc73 100644
--- a/source/blender/draw/engines/eevee/eevee_occlusion.c
+++ b/source/blender/draw/engines/eevee/eevee_occlusion.c
@@ -68,9 +68,9 @@ static void eevee_create_shader_occlusion(void)
 int EEVEE_occlusion_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
 {
 	EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
-	EEVEE_StorageList *stl = vedata->stl;
 	EEVEE_FramebufferList *fbl = vedata->fbl;
-	EEVEE_TextureList *txl = vedata->txl;
+	EEVEE_StorageList *stl = vedata->stl;
+	EEVEE_EffectsInfo *effects = stl->effects;
 
 	const DRWContextState *draw_ctx = DRW_context_state_get();
 	ViewLayer *view_layer = draw_ctx->view_layer;
@@ -80,6 +80,7 @@ int EEVEE_occlusion_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
 
 	if (BKE_collection_engine_property_value_get_bool(props, "gtao_enable")) {
 		const float *viewport_size = DRW_viewport_size_get();
+		const int fs_size[2] = {(int)viewport_size[0], (int)viewport_size[1]};
 
 		/* Shaders */
 		if (!e_data.gtao_sh) {
@@ -100,26 +101,31 @@ int EEVEE_occlusion_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
 
 		common_data->ao_bounce_fac = (float)BKE_collection_engine_property_value_get_bool(props, "gtao_bounce");
 
-		DRWFboTexture tex = {&txl->gtao_horizons, DRW_TEX_RGBA_8, 0};
-
-		DRW_framebuffer_init(&fbl->gtao_fb, &draw_engine_eevee_type,
-		                    (int)viewport_size[0], (int)viewport_size[1],
-		                    &tex, 1);
+		effects->gtao_horizons = DRW_texture_pool_query_2D(fs_size[0], fs_size[1], DRW_TEX_RGBA_8,
+		                                                   &draw_engine_eevee_type);
+		GPU_framebuffer_ensure_config(&fbl->gtao_fb, {
+			GPU_ATTACHMENT_NONE,
+			GPU_ATTACHMENT_TEXTURE(effects->gtao_horizons)
+		});
 
 		if (G.debug_value == 6) {
-			DRWFboTexture tex_debug = {&stl->g_data->gtao_horizons_debug, DRW_TEX_RGBA_8, DRW_TEX_TEMP};
-
-			DRW_framebuffer_init(&fbl->gtao_debug_fb, &draw_engine_eevee_type,
-			                    (int)viewport_size[0], (int)viewport_size[1],
-			                    &tex_debug, 1);
+			effects->gtao_horizons_debug = DRW_texture_pool_query_2D(fs_size[0], fs_size[1], DRW_TEX_RGBA_8,
+			                                                         &draw_engine_eevee_type);
+			GPU_framebuffer_ensure_config(&fbl->gtao_debug_fb, {
+				GPU_ATTACHMENT_NONE,
+				GPU_ATTACHMENT_TEXTURE(effects->gtao_horizons_debug)
+			});
+		}
+		else {
+			effects->gtao_horizons_debug = NULL;
 		}
 
 		return EFFECT_GTAO | EFFECT_NORMAL_BUFFER;
 	}
 
 	/* Cleanup */
-	DRW_TEXTURE_FREE_SAFE(txl->gtao_horizons);
-	DRW_FRAMEBUFFER_FREE_SAFE(fbl->gtao_fb);
+	effects->gtao_horizons = NULL;
+	GPU_FRAMEBUFFER_FREE_SAFE(fbl->gtao_fb);
 	common_data->ao_settings = 0.0f;
 
 	return 0;
@@ -129,8 +135,9 @@ void EEVEE_occlusion_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata
 {
 	EEVEE_FramebufferList *fbl = vedata->fbl;
 	EEVEE_TextureList *txl = vedata->txl;
+	EEVEE_StorageList *stl = vedata->stl;
 	EEVEE_PassList *psl = vedata->psl;
-	const float *viewport_size = DRW_viewport_size_get();
+	EEVEE_EffectsInfo *effects = stl->effects;
 
 	const DRWContextState *draw_ctx = DRW_context_state_get();
 	ViewLayer *view_layer = draw_ctx->view_layer;
@@ -140,30 +147,33 @@ void EEVEE_occlusion_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata
 		DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
 		float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
 
-		DRWFboTexture tex_data = {&txl->ao_accum, DRW_TEX_R_32, 0};
-		DRW_framebuffer_init(&fbl->ao_accum_fb, &draw_engine_eevee_type, (int)viewport_size[0], (int)viewport_size[1],
-		                     &tex_data, 1);
+		DRW_texture_ensure_fullscreen_2D(&txl->ao_accum, DRW_TEX_R_32, 0); /* Should be enough precision for many samples. */
+
+		GPU_framebuffer_ensure_config(&fbl->ao_accum_fb, {
+			GPU_ATTACHMENT_NONE,
+			GPU_ATTACHMENT_TEXTURE(txl->ao_accum)
+		});
 
 		/* Clear texture. */
-		DRW_framebuffer_bind(fbl->ao_accum_fb);
-		DRW_framebuffer_clear(true, false, false, clear, 0.0f);
+		GPU_framebuffer_bind(fbl->ao_accum_fb);
+		GPU_framebuffer_clear_color(fbl->ao_accum_fb, clear);
 
 		/* Accumulation pass */
 		DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_ADDITIVE;
 		psl->ao_accum_ps = DRW_pass_create("AO Accum", state);
 		DRWShadingGroup *grp = DRW_shgroup_create(e_data.gtao_debug_sh, psl->ao_accum_ps);
 		DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
-		DRW_shgroup_uniform_buffer(grp, "maxzBuffer", &txl->maxzbuffer);
-		DRW_shgroup_uniform_buffer(grp, "depthBuffer", &dtxl->depth);
-		DRW_shgroup_uniform_buffer(grp, "normalBuffer", &txl->ssr_normal_input);
-		DRW_shgroup_uniform_buffer(grp, "horizonBuffer", &txl->gtao_horizons);
+		DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &txl->maxzbuffer);
+		DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
+		DRW_shgroup_uniform_texture_ref(grp, "normalBuffer", &effects->ssr_normal_input);
+		DRW_shgroup_uniform_texture_ref(grp, "horizonBuffer", &effects->gtao_horizons);
 		DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
 		DRW_shgroup_call_add(grp, DRW_cache_fullscreen_quad_get(), NULL);
 	}
 	else {
 		/* Cleanup to release memory */
 		DRW_TEXTURE_FREE_SAFE(txl->ao_accum);
-		DRW_FRAMEBUFFER_FREE_SAFE(fbl->ao_accum_fb);
+		GPU_FRAMEBUFFER_FREE_SAFE(fbl->ao_accum_fb);
 	}
 }
 
@@ -192,16 +202,16 @@ void EEVEE_occlusion_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
 		psl->ao_horizon_search = DRW_pass_create("GTAO Horizon Search", DRW_STATE_WRITE_COLOR);
 		DRWShadingGroup *grp = DRW_shgroup_create(e_data.gtao_sh, psl->ao_horizon_search);
 		DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
-		DRW_shgroup_uniform_buffer(grp, "maxzBuffer", &txl->maxzbuffer);
-		DRW_shgroup_uniform_buffer(grp, "depthBuffer", &effects->ao_src_depth);
+		DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &txl->maxzbuffer);
+		DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &effects->ao_src_depth);
 		DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
 		DRW_shgroup_call_add(grp, quad, NULL);
 
 		psl->ao_horizon_search_layer = DRW_pass_create("GTAO Horizon Search Layer", DRW_STATE_WRITE_COLOR);
 		grp = DRW_shgroup_create(e_data.gtao_layer_sh, psl->ao_horizon_search_layer);
 		DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
-		DRW_shgroup_uniform_buffer(grp, "maxzBuffer", &txl->maxzbuffer);
-		DRW_shgroup_uniform_buffer(grp, "depthBufferLayered", &effects->ao_src_depth);
+		DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &txl->maxzbuffer);
+		DRW_shgroup_uniform_texture_ref(grp, "depthBufferLayered", &effects->ao_src_depth);
 		DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
 		DRW_shgroup_uniform_int(grp, "layer", &stl->effects->ao_depth_layer, 1);
 		DRW_shgroup_call_add(grp, quad, NULL);
@@ -210,10 +220,10 @@ void EEVEE_occlusion_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
 			psl->ao_horizon_debug = DRW_pass_create("GTAO Horizon Debug", DRW_STATE_WRITE_COLOR);
 			grp = DRW_shgroup_create(e_data.gtao_debug_sh, psl->ao_horizon_debug);
 			DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
-			DRW_shgroup_uniform_buffer(grp, "maxzBuffer", &txl->maxzbuffer);
-			DRW_shgroup_uniform_buffer(grp, "depthBuffer", &dtxl->depth);
-			DRW_shgroup_uniform_buffer(grp, "normalBuffer", &txl->ssr_normal_input);
-			DRW_shgroup_uniform_buffer(grp, "horizonBuffer", &txl->gtao_horizons);
+			DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &txl->maxzbuffer);
+			DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
+			DRW_shgroup_uniform_texture_ref(grp, "normalBuffer", &effects->ssr_normal_input);
+			DRW_shgroup_uniform_texture_ref(grp, "horizonBuffer", &effects->gtao_horizons);
 			DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
 			DRW_shgroup_call_add(grp, quad, NULL);
 		}
@@ -233,7 +243,7 @@ void EEVEE_occlusion_compute(
 		effects->ao_src_depth = depth_src;
 		effects->ao_depth_layer = layer;
 
-		DRW_framebuffer_bind(fbl->gtao_fb);
+		GPU_framebuffer_bind(fbl->gtao_fb);
 
 		if (layer >= 0) {
 			DRW_draw_pass(psl->ao_horizon_search_layer);
@@ -243,7 +253,7 @@ void EEVEE_occlusion_compute(
 		}
 
 		/* Restore */
-		DRW_framebuffer_bind(fbl->main);
+		GPU_framebuffer_bind(fbl->main_fb);
 
 		DRW_stats_group_end();
 	}
@@ -259,14 +269,11 @@ void EEVEE_occlusion_draw_debug(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data
 	if (((effects->enabled_effects & EFFECT_GTAO) != 0) && (G.debug_value == 6)) {
 		DRW_stats_group_start("GTAO Debug");
 
-		DRW_framebuffer_texture_attach(fbl->gtao_debug_fb, stl->g_data->gtao_horizons_debug, 0, 0);
-		DRW_framebuffer_bind(fbl->gtao_debug_fb);
-
+		GPU_framebuffer_bind(fbl->gtao_debug_fb);
 		DRW_draw_pass(psl->ao_horizon_debug);
 
 		/* Restore */
-		DRW_framebuffer_texture_detach(stl->g_data->gtao_horizons_debug);
-		DRW_framebuffer_bind(fbl->main);
+		GPU_framebuffer_bind(fbl->main_fb);
 
 		DRW_stats_group_end();
 	}
@@ -284,11 +291,11 @@ void EEVEE_occlusion_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *
 		EEVEE_create_minmax_buffer(vedata, dtxl->depth, -1);
 		EEVEE_occlusion_compute(sldata, vedata, dtxl->depth, -1);
 
-		DRW_framebuffer_bind(fbl->ao_accum_fb);
+		GPU_framebuffer_bind(fbl->ao_accum_fb);
 		DRW_draw_pass(psl->ao_accum_ps);
 
 		/* Restore */
-		DRW_framebuffer_bind(fbl->main);
+		GPU_framebuffer_bind(fbl->main_fb);
 	}
 }
 
diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h
index 88fb55cfbdf3484bf32423f54f1941f4f8811e65..bf77914832e4b20e4be2cebac5065712fd7be7c4 100644
--- a/source/blender/draw/engines/eevee/eevee_private.h
+++ b/source/blender/draw/engines/eevee/eevee_private.h
@@ -72,22 +72,23 @@ extern struct DrawEngineType draw_engine_eevee_type;
 
 #define SWAP_DOUBLE_BUFFERS() {                                       \
 	if (effects->swap_double_buffer) {                                \
-		SWAP(struct GPUFrameBuffer *, fbl->main, fbl->double_buffer); \
+		SWAP(struct GPUFrameBuffer *, fbl->main_fb, fbl->double_buffer_fb); \
+		SWAP(struct GPUFrameBuffer *, fbl->main_color_fb, fbl->double_buffer_color_fb); \
 		SWAP(GPUTexture *, txl->color, txl->color_double_buffer);     \
 		effects->swap_double_buffer = false;                          \
 	}                                                                 \
 } ((void)0)
 
 #define SWAP_BUFFERS() {                           \
-	if (effects->target_buffer != fbl->main) {     \
+	if (effects->target_buffer == fbl->effect_color_fb) { \
 		SWAP_DOUBLE_BUFFERS();                     \
 		effects->source_buffer = txl->color_post;  \
-		effects->target_buffer = fbl->main;        \
+		effects->target_buffer = fbl->main_color_fb;  \
 	}                                              \
 	else {                                         \
 		SWAP_DOUBLE_BUFFERS();                     \
 		effects->source_buffer = txl->color;       \
-		effects->target_buffer = fbl->effect_fb;   \
+		effects->target_buffer = fbl->effect_color_fb; \
 	}                                              \
 } ((void)0)
 
@@ -217,11 +218,11 @@ typedef struct EEVEE_FramebufferList {
 	struct GPUFrameBuffer *gtao_fb;
 	struct GPUFrameBuffer *gtao_debug_fb;
 	struct GPUFrameBuffer *downsample_fb;
-	struct GPUFrameBuffer *effect_fb;
 	struct GPUFrameBuffer *bloom_blit_fb;
 	struct GPUFrameBuffer *bloom_down_fb[MAX_BLOOM_STEP];
 	struct GPUFrameBuffer *bloom_accum_fb[MAX_BLOOM_STEP - 1];
 	struct GPUFrameBuffer *sss_blur_fb;
+	struct GPUFrameBuffer *sss_resolve_fb;
 	struct GPUFrameBuffer *sss_clear_fb;
 	struct GPUFrameBuffer *sss_accum_fb;
 	struct GPUFrameBuffer *dof_down_fb;
@@ -238,30 +239,24 @@ typedef struct EEVEE_FramebufferList {
 	struct GPUFrameBuffer *update_noise_fb;
 
 	struct GPUFrameBuffer *planarref_fb;
+	struct GPUFrameBuffer *planar_downsample_fb;
 
-	struct GPUFrameBuffer *main;
-	struct GPUFrameBuffer *double_buffer;
-	struct GPUFrameBuffer *depth_double_buffer_fb;
+	struct GPUFrameBuffer *main_fb;
+	struct GPUFrameBuffer *main_color_fb;
+	struct GPUFrameBuffer *effect_fb;
+	struct GPUFrameBuffer *effect_color_fb;
+	struct GPUFrameBuffer *double_buffer_fb;
+	struct GPUFrameBuffer *double_buffer_color_fb;
+	struct GPUFrameBuffer *double_buffer_depth_fb;
 } EEVEE_FramebufferList;
 
 typedef struct EEVEE_TextureList {
 	/* Effects */
 	struct GPUTexture *color_post; /* R16_G16_B16 */
-	struct GPUTexture *dof_down_near; /* R16_G16_B16_A16 */
-	struct GPUTexture *dof_down_far; /* R16_G16_B16_A16 */
-	struct GPUTexture *dof_coc; /* R16_G16 */
-	struct GPUTexture *dof_near_blur; /* R16_G16_B16_A16 */
-	struct GPUTexture *dof_far_blur; /* R16_G16_B16_A16 */
-	struct GPUTexture *bloom_blit; /* R16_G16_B16 */
-	struct GPUTexture *bloom_downsample[MAX_BLOOM_STEP]; /* R16_G16_B16 */
-	struct GPUTexture *bloom_upsample[MAX_BLOOM_STEP - 1]; /* R16_G16_B16 */
 	struct GPUTexture *mist_accum;
 	struct GPUTexture *ao_accum;
 	struct GPUTexture *sss_dir_accum;
 	struct GPUTexture *sss_col_accum;
-	struct GPUTexture *ssr_normal_input;
-	struct GPUTexture *ssr_specrough_input;
-	struct GPUTexture *ssr_hit_output;
 	struct GPUTexture *refract_color;
 
 	struct GPUTexture *volume_prop_scattering;
@@ -276,13 +271,6 @@ typedef struct EEVEE_TextureList {
 	struct GPUTexture *planar_pool;
 	struct GPUTexture *planar_depth;
 
-	struct GPUTexture *gtao_horizons;
-
-	struct GPUTexture *sss_data;
-	struct GPUTexture *sss_albedo;
-	struct GPUTexture *sss_blur;
-	struct GPUTexture *sss_stencil;
-
 	struct GPUTexture *maxzbuffer;
 
 	struct GPUTexture *color; /* R16_G16_B16 */
@@ -480,18 +468,42 @@ enum {
 };
 
 /* ************ EFFECTS DATA ************* */
+
+typedef enum EEVEE_EffectsFlag {
+	EFFECT_MOTION_BLUR         = (1 << 0),
+	EFFECT_BLOOM               = (1 << 1),
+	EFFECT_DOF                 = (1 << 2),
+	EFFECT_VOLUMETRIC          = (1 << 3),
+	EFFECT_SSR                 = (1 << 4),
+	EFFECT_DOUBLE_BUFFER       = (1 << 5), /* Not really an effect but a feature */
+	EFFECT_REFRACT             = (1 << 6),
+	EFFECT_GTAO                = (1 << 7),
+	EFFECT_TAA                 = (1 << 8),
+	EFFECT_POST_BUFFER         = (1 << 9), /* Not really an effect but a feature */
+	EFFECT_NORMAL_BUFFER       = (1 << 10), /* Not really an effect but a feature */
+	EFFECT_SSS                 = (1 << 11),
+} EEVEE_EffectsFlag;
+
 typedef struct EEVEE_EffectsInfo {
-	int enabled_effects;
+	EEVEE_EffectsFlag enabled_effects;
 	bool swap_double_buffer;
 	/* SSSS */
 	int sss_sample_count;
 	bool sss_separate_albedo;
+	struct GPUTexture *sss_data; /* Textures from pool */
+	struct GPUTexture *sss_albedo;
+	struct GPUTexture *sss_blur;
+	struct GPUTexture *sss_stencil;
 	/* Volumetrics */
 	int volume_current_sample;
 	/* SSR */
 	bool reflection_trace_full;
 	int ssr_neighbor_ofs;
 	int ssr_halfres_ofs[2];
+	struct GPUTexture *ssr_normal_input; /* Textures from pool */
+	struct GPUTexture *ssr_specrough_input;
+	struct GPUTexture *ssr_hit_output;
+	struct GPUTexture *ssr_pdf_output;
 	/* Temporal Anti Aliasing */
 	int taa_current_sample;
 	int taa_render_sample;
@@ -506,6 +518,8 @@ typedef struct EEVEE_EffectsInfo {
 	/* Ambient Occlusion */
 	int ao_depth_layer;
 	struct GPUTexture *ao_src_depth; /* pointer copy */
+	struct GPUTexture *gtao_horizons; /* Textures from pool */
+	struct GPUTexture *gtao_horizons_debug;
 	/* Motion Blur */
 	float current_ndc_to_world[4][4];
 	float past_world_to_ndc[4][4];
@@ -516,6 +530,11 @@ typedef struct EEVEE_EffectsInfo {
 	float dof_bokeh[4];
 	float dof_layer_select[2];
 	int dof_target_size[2];
+	struct GPUTexture *dof_down_near; /* Textures from pool */
+	struct GPUTexture *dof_down_far;
+	struct GPUTexture *dof_coc;
+	struct GPUTexture *dof_near_blur;
+	struct GPUTexture *dof_far_blur;
 	/* Other */
 	float prev_persmat[4][4];
 	/* Bloom */
@@ -528,6 +547,9 @@ typedef struct EEVEE_EffectsInfo {
 	float bloom_sample_scale;
 	float bloom_curve_threshold[4];
 	float unf_source_texel_size[2];
+	struct GPUTexture *bloom_blit; /* Textures from pool */
+	struct GPUTexture *bloom_downsample[MAX_BLOOM_STEP];
+	struct GPUTexture *bloom_upsample[MAX_BLOOM_STEP - 1];
 	struct GPUTexture *unf_source_buffer; /* pointer copy */
 	struct GPUTexture *unf_base_buffer; /* pointer copy */
 	/* Not alloced, just a copy of a *GPUtexture in EEVEE_TextureList. */
@@ -537,21 +559,6 @@ typedef struct EEVEE_EffectsInfo {
 	struct GPUFrameBuffer *final_fb;        /* Framebuffer with final_tx as attachement. */
 } EEVEE_EffectsInfo;
 
-enum {
-	EFFECT_MOTION_BLUR         = (1 << 0),
-	EFFECT_BLOOM               = (1 << 1),
-	EFFECT_DOF                 = (1 << 2),
-	EFFECT_VOLUMETRIC          = (1 << 3),
-	EFFECT_SSR                 = (1 << 4),
-	EFFECT_DOUBLE_BUFFER       = (1 << 5), /* Not really an effect but a feature */
-	EFFECT_REFRACT             = (1 << 6),
-	EFFECT_GTAO                = (1 << 7),
-	EFFECT_TAA                 = (1 << 8),
-	EFFECT_POST_BUFFER         = (1 << 9), /* Not really an effect but a feature */
-	EFFECT_NORMAL_BUFFER       = (1 << 10), /* Not really an effect but a feature */
-	EFFECT_SSS                 = (1 << 11),
-};
-
 /* ***************** COMMON DATA **************** */
 
 /* Common uniform buffer containing all "constant" data over the whole drawing pipeline. */
@@ -620,7 +627,8 @@ typedef struct EEVEE_ViewLayerData {
 	struct GPUUniformBuffer *shadow_render_ubo;
 	struct GPUUniformBuffer *shadow_samples_ubo;
 
-	struct GPUFrameBuffer *shadow_target_fb;
+	struct GPUFrameBuffer *shadow_cube_target_fb;
+	struct GPUFrameBuffer *shadow_cascade_target_fb;
 	struct GPUFrameBuffer *shadow_store_fb;
 
 	struct GPUTexture *shadow_cube_target;
@@ -638,8 +646,8 @@ typedef struct EEVEE_ViewLayerData {
 	struct GPUUniformBuffer *grid_ubo;
 	struct GPUUniformBuffer *planar_ubo;
 
-	struct GPUFrameBuffer *probe_fb;
 	struct GPUFrameBuffer *probe_filter_fb;
+	struct GPUFrameBuffer *probe_face_fb[6];
 
 	struct GPUTexture *probe_rt;
 	struct GPUTexture *probe_depth_rt;
@@ -746,9 +754,6 @@ typedef struct EEVEE_PrivateData {
 	struct DRWShadingGroup *planar_display_shgrp;
 	struct GHash *material_hash;
 	struct GHash *hair_material_hash;
-	struct GPUTexture *minzbuffer;
-	struct GPUTexture *ssr_pdf_output;
-	struct GPUTexture *gtao_horizons_debug;
 	float background_alpha; /* TODO find a better place for this. */
 	/* For planar probes */
 	float planar_texel_size[2];
@@ -890,8 +895,8 @@ void EEVEE_volumes_free(void);
 void EEVEE_effects_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, Object *camera);
 void EEVEE_effects_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
 void EEVEE_create_minmax_buffer(EEVEE_Data *vedata, struct GPUTexture *depth_src, int layer);
-void EEVEE_downsample_buffer(EEVEE_Data *vedata, struct GPUFrameBuffer *fb_src, struct GPUTexture *texture_src, int level);
-void EEVEE_downsample_cube_buffer(EEVEE_Data *vedata, struct GPUFrameBuffer *fb_src, struct GPUTexture *texture_src, int level);
+void EEVEE_downsample_buffer(EEVEE_Data *vedata, struct GPUTexture *texture_src, int level);
+void EEVEE_downsample_cube_buffer(EEVEE_Data *vedata, struct GPUTexture *texture_src, int level);
 void EEVEE_effects_do_gtao(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
 void EEVEE_draw_effects(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
 void EEVEE_effects_free(void);
diff --git a/source/blender/draw/engines/eevee/eevee_render.c b/source/blender/draw/engines/eevee/eevee_render.c
index 503c2da0544ce92edb152bee2334741049906f8a..130999adb39bcc47ca2e9e4af657d8a98666d8c2 100644
--- a/source/blender/draw/engines/eevee/eevee_render.c
+++ b/source/blender/draw/engines/eevee/eevee_render.c
@@ -53,7 +53,6 @@ void EEVEE_render_init(EEVEE_Data *ved, RenderEngine *engine, struct Depsgraph *
 	EEVEE_FramebufferList *fbl = vedata->fbl;
 	EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
 	Scene *scene = DEG_get_evaluated_scene(depsgraph);
-	const float *viewport_size = DRW_viewport_size_get();
 
 	/* Init default FB and render targets:
 	 * In render mode the default framebuffer is not generated
@@ -62,16 +61,22 @@ void EEVEE_render_init(EEVEE_Data *ved, RenderEngine *engine, struct Depsgraph *
 	DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
 	DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
 
-	/* NOTE : use 32 bit format for precision in render mode. */
-	DRWFboTexture dtex = {&dtxl->depth, DRW_TEX_DEPTH_24_STENCIL_8, 0};
-	DRW_framebuffer_init(&dfbl->default_fb, &draw_engine_eevee_type,
-	                     (int)viewport_size[0], (int)viewport_size[1],
-	                     &dtex, 1);
-
-	DRWFboTexture tex = {&txl->color, DRW_TEX_RGBA_32, DRW_TEX_FILTER | DRW_TEX_MIPMAP};
-	DRW_framebuffer_init(&fbl->main, &draw_engine_eevee_type,
-	                     (int)viewport_size[0], (int)viewport_size[1],
-	                     &tex, 1);
+	/* TODO 32 bit depth */
+	DRW_texture_ensure_fullscreen_2D(&dtxl->depth, DRW_TEX_DEPTH_24_STENCIL_8, 0);
+	DRW_texture_ensure_fullscreen_2D(&txl->color, DRW_TEX_RGBA_32, DRW_TEX_FILTER | DRW_TEX_MIPMAP);
+
+	GPU_framebuffer_ensure_config(&dfbl->default_fb, {
+		GPU_ATTACHMENT_TEXTURE(dtxl->depth),
+		GPU_ATTACHMENT_TEXTURE(txl->color)
+	});
+	GPU_framebuffer_ensure_config(&fbl->main_fb, {
+		GPU_ATTACHMENT_TEXTURE(dtxl->depth),
+		GPU_ATTACHMENT_TEXTURE(txl->color)
+	});
+	GPU_framebuffer_ensure_config(&fbl->main_color_fb, {
+		GPU_ATTACHMENT_NONE,
+		GPU_ATTACHMENT_TEXTURE(txl->color)
+	});
 
 	/* Alloc transient data. */
 	if (!stl->g_data) {
@@ -90,7 +95,8 @@ void EEVEE_render_init(EEVEE_Data *ved, RenderEngine *engine, struct Depsgraph *
 	}
 
 	/* Set the pers & view matrix. */
-	struct Object *camera = RE_GetCamera(engine->re);
+	/* TODO(sergey): Shall render hold pointer to an evaluated camera instead? */
+	struct Object *camera = DEG_get_evaluated_object(depsgraph, RE_GetCamera(engine->re));
 	float frame = BKE_scene_frame_get(scene);
 	RE_GetCameraWindow(engine->re, camera, frame, g_data->winmat);
 	RE_GetCameraModelMatrix(engine->re, camera, g_data->viewinv);
@@ -165,8 +171,11 @@ static void eevee_render_result_combined(
 {
 	RenderPass *rp = RE_pass_find_by_name(rl, RE_PASSNAME_COMBINED, viewname);
 
-	DRW_framebuffer_bind(vedata->stl->effects->final_fb);
-	DRW_framebuffer_read_data(rect->xmin, rect->ymin, BLI_rcti_size_x(rect), BLI_rcti_size_y(rect), 4, 0, rp->rect);
+	GPU_framebuffer_bind(vedata->stl->effects->final_fb);
+	GPU_framebuffer_read_color(vedata->stl->effects->final_fb,
+	                           rect->xmin, rect->ymin,
+	                           BLI_rcti_size_x(rect), BLI_rcti_size_y(rect),
+	                           4, 0, rp->rect);
 }
 
 static void eevee_render_result_subsurface(
@@ -184,8 +193,11 @@ static void eevee_render_result_subsurface(
 	if ((view_layer->passflag & SCE_PASS_SUBSURFACE_COLOR) != 0) {
 		RenderPass *rp = RE_pass_find_by_name(rl, RE_PASSNAME_SUBSURFACE_COLOR, viewname);
 
-		DRW_framebuffer_bind(vedata->fbl->sss_accum_fb);
-		DRW_framebuffer_read_data(rect->xmin, rect->ymin, BLI_rcti_size_x(rect), BLI_rcti_size_y(rect), 3, 1, rp->rect);
+		GPU_framebuffer_bind(vedata->fbl->sss_accum_fb);
+		GPU_framebuffer_read_color(vedata->fbl->sss_accum_fb,
+		                           rect->xmin, rect->ymin,
+		                           BLI_rcti_size_x(rect), BLI_rcti_size_y(rect),
+		                           3, 1, rp->rect);
 
 		/* This is the accumulated color. Divide by the number of samples. */
 		for (int i = 0; i < rp->rectx * rp->recty * 3; i++) {
@@ -196,8 +208,11 @@ static void eevee_render_result_subsurface(
 	if ((view_layer->passflag & SCE_PASS_SUBSURFACE_DIRECT) != 0) {
 		RenderPass *rp = RE_pass_find_by_name(rl, RE_PASSNAME_SUBSURFACE_DIRECT, viewname);
 
-		DRW_framebuffer_bind(vedata->fbl->sss_accum_fb);
-		DRW_framebuffer_read_data(rect->xmin, rect->ymin, BLI_rcti_size_x(rect), BLI_rcti_size_y(rect), 3, 0, rp->rect);
+		GPU_framebuffer_bind(vedata->fbl->sss_accum_fb);
+		GPU_framebuffer_read_color(vedata->fbl->sss_accum_fb,
+		                           rect->xmin, rect->ymin,
+		                           BLI_rcti_size_x(rect), BLI_rcti_size_y(rect),
+		                           3, 0, rp->rect);
 
 		/* This is the accumulated color. Divide by the number of samples. */
 		for (int i = 0; i < rp->rectx * rp->recty * 3; i++) {
@@ -227,7 +242,11 @@ static void eevee_render_result_normal(
 	if ((view_layer->passflag & SCE_PASS_NORMAL) != 0) {
 		RenderPass *rp = RE_pass_find_by_name(rl, RE_PASSNAME_NORMAL, viewname);
 
-		DRW_framebuffer_read_data(rect->xmin, rect->ymin, BLI_rcti_size_x(rect), BLI_rcti_size_y(rect), 3, 1, rp->rect);
+		GPU_framebuffer_bind(vedata->fbl->main_fb);
+		GPU_framebuffer_read_color(vedata->fbl->main_fb,
+		                           rect->xmin, rect->ymin,
+		                           BLI_rcti_size_x(rect), BLI_rcti_size_y(rect),
+		                           3, 1, rp->rect);
 
 		/* Convert Eevee encoded normals to Blender normals. */
 		for (int i = 0; i < rp->rectx * rp->recty * 3; i += 3) {
@@ -269,7 +288,10 @@ static void eevee_render_result_z(
 	if ((view_layer->passflag & SCE_PASS_Z) != 0) {
 		RenderPass *rp = RE_pass_find_by_name(rl, RE_PASSNAME_Z, viewname);
 
-		DRW_framebuffer_read_depth(rect->xmin, rect->ymin, BLI_rcti_size_x(rect), BLI_rcti_size_y(rect), rp->rect);
+		GPU_framebuffer_read_depth(vedata->fbl->main_fb,
+		                           rect->xmin, rect->ymin,
+		                           BLI_rcti_size_x(rect), BLI_rcti_size_y(rect),
+		                           rp->rect);
 
 		bool is_persp = DRW_viewport_is_persp_get();
 
@@ -301,8 +323,11 @@ static void eevee_render_result_mist(
 	if ((view_layer->passflag & SCE_PASS_MIST) != 0) {
 		RenderPass *rp = RE_pass_find_by_name(rl, RE_PASSNAME_MIST, viewname);
 
-		DRW_framebuffer_bind(vedata->fbl->mist_accum_fb);
-		DRW_framebuffer_read_data(rect->xmin, rect->ymin, BLI_rcti_size_x(rect), BLI_rcti_size_y(rect), 1, 0, rp->rect);
+		GPU_framebuffer_bind(vedata->fbl->mist_accum_fb);
+		GPU_framebuffer_read_color(vedata->fbl->mist_accum_fb,
+		                           rect->xmin, rect->ymin,
+		                           BLI_rcti_size_x(rect), BLI_rcti_size_y(rect),
+		                           1, 0, rp->rect);
 
 		/* This is the accumulated color. Divide by the number of samples. */
 		for (int i = 0; i < rp->rectx * rp->recty; i++) {
@@ -326,8 +351,11 @@ static void eevee_render_result_occlusion(
 	if ((view_layer->passflag & SCE_PASS_AO) != 0) {
 		RenderPass *rp = RE_pass_find_by_name(rl, RE_PASSNAME_AO, viewname);
 
-		DRW_framebuffer_bind(vedata->fbl->ao_accum_fb);
-		DRW_framebuffer_read_data(rect->xmin, rect->ymin, BLI_rcti_size_x(rect), BLI_rcti_size_y(rect), 3, 0, rp->rect);
+		GPU_framebuffer_bind(vedata->fbl->ao_accum_fb);
+		GPU_framebuffer_read_color(vedata->fbl->ao_accum_fb,
+		                           rect->xmin, rect->ymin,
+		                           BLI_rcti_size_x(rect), BLI_rcti_size_y(rect),
+		                           3, 0, rp->rect);
 
 		/* This is the accumulated color. Divide by the number of samples. */
 		for (int i = 0; i < rp->rectx * rp->recty * 3; i += 3) {
@@ -338,30 +366,34 @@ static void eevee_render_result_occlusion(
 
 static void eevee_render_draw_background(EEVEE_Data *vedata)
 {
-	EEVEE_TextureList *txl = vedata->txl;
 	EEVEE_FramebufferList *fbl = vedata->fbl;
+	EEVEE_StorageList *stl = vedata->stl;
 	EEVEE_PassList *psl = vedata->psl;
 
 	/* Prevent background to write to data buffers.
 	 * NOTE : This also make sure the textures are bound
 	 *        to the right double buffer. */
-	if (txl->ssr_normal_input != NULL) {
-		DRW_framebuffer_texture_detach(txl->ssr_normal_input);
-	}
-	if (txl->ssr_specrough_input != NULL) {
-		DRW_framebuffer_texture_detach(txl->ssr_specrough_input);
-	}
-	DRW_framebuffer_bind(fbl->main);
+	GPU_framebuffer_ensure_config(&fbl->main_fb, {
+		GPU_ATTACHMENT_LEAVE,
+		GPU_ATTACHMENT_LEAVE,
+		GPU_ATTACHMENT_NONE,
+		GPU_ATTACHMENT_NONE,
+		GPU_ATTACHMENT_NONE,
+		GPU_ATTACHMENT_NONE
+	});
+	GPU_framebuffer_bind(fbl->main_fb);
 
 	DRW_draw_pass(psl->background_pass);
 
-	if (txl->ssr_normal_input != NULL) {
-		DRW_framebuffer_texture_attach(fbl->main, txl->ssr_normal_input, 1, 0);
-	}
-	if (txl->ssr_specrough_input != NULL) {
-		DRW_framebuffer_texture_attach(fbl->main, txl->ssr_specrough_input, 2, 0);
-	}
-	DRW_framebuffer_bind(fbl->main);
+	GPU_framebuffer_ensure_config(&fbl->main_fb, {
+		GPU_ATTACHMENT_LEAVE,
+		GPU_ATTACHMENT_LEAVE,
+		GPU_ATTACHMENT_TEXTURE(stl->effects->ssr_normal_input),
+		GPU_ATTACHMENT_TEXTURE(stl->effects->ssr_specrough_input),
+		GPU_ATTACHMENT_TEXTURE(stl->effects->sss_data),
+		GPU_ATTACHMENT_TEXTURE(stl->effects->sss_albedo)
+	});
+	GPU_framebuffer_bind(fbl->main_fb);
 }
 
 void EEVEE_render_draw(EEVEE_Data *vedata, RenderEngine *engine, RenderLayer *rl, const rcti *rect)
@@ -412,6 +444,8 @@ void EEVEE_render_draw(EEVEE_Data *vedata, RenderEngine *engine, RenderLayer *rl
 
 	while (render_samples < tot_sample && !RE_engine_test_break(engine)) {
 		float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+		float clear_depth = 1.0f;
+		unsigned int clear_stencil = 0xFF;
 		unsigned int primes[3] = {2, 3, 7};
 		double offset[3] = {0.0, 0.0, 0.0};
 		double r[3];
@@ -454,10 +488,8 @@ void EEVEE_render_draw(EEVEE_Data *vedata, RenderEngine *engine, RenderLayer *rl
 		/* Refresh Shadows */
 		EEVEE_draw_shadows(sldata, psl);
 
-		DRW_framebuffer_texture_detach(dtxl->depth);
-		DRW_framebuffer_texture_attach(fbl->main, dtxl->depth, 0, 0);
-		DRW_framebuffer_bind(fbl->main);
-		DRW_framebuffer_clear(true, true, true, clear_col, 1.0f);
+		GPU_framebuffer_bind(fbl->main_fb);
+		GPU_framebuffer_clear_color_depth_stencil(fbl->main_fb, clear_col, clear_depth, clear_stencil);
 		/* Depth prepass */
 		DRW_draw_pass(psl->depth_pass);
 		DRW_draw_pass(psl->depth_pass_cull);
@@ -467,7 +499,7 @@ void EEVEE_render_draw(EEVEE_Data *vedata, RenderEngine *engine, RenderLayer *rl
 		EEVEE_volumes_compute(sldata, vedata);
 		/* Shading pass */
 		eevee_render_draw_background(vedata);
-		DRW_framebuffer_bind(fbl->main);
+		GPU_framebuffer_bind(fbl->main_fb);
 		EEVEE_draw_default_passes(psl);
 		DRW_draw_pass(psl->material_pass);
 		EEVEE_subsurface_data_render(sldata, vedata);
diff --git a/source/blender/draw/engines/eevee/eevee_screen_raytrace.c b/source/blender/draw/engines/eevee/eevee_screen_raytrace.c
index 96d560688f342ac9257b7522b70f3773ccd13ce9..7d4860ea1b51f7a2cecaff210d41ad0b84d19b02 100644
--- a/source/blender/draw/engines/eevee/eevee_screen_raytrace.c
+++ b/source/blender/draw/engines/eevee/eevee_screen_raytrace.c
@@ -124,14 +124,15 @@ int EEVEE_screen_raytrace_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
 		const bool use_refraction = BKE_collection_engine_property_value_get_bool(props, "ssr_refraction");
 
 		if (use_refraction) {
-			DRWFboTexture tex = {&txl->refract_color, DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER | DRW_TEX_MIPMAP};
+			/* TODO: Opti: Could be shared. */
+			DRW_texture_ensure_fullscreen_2D(&txl->refract_color, DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER | DRW_TEX_MIPMAP);
 
-			DRW_framebuffer_init(&fbl->refract_fb, &draw_engine_eevee_type,
-			                     (int)viewport_size[0], (int)viewport_size[1],
-			                     &tex, 1);
+			GPU_framebuffer_ensure_config(&fbl->refract_fb, {
+				GPU_ATTACHMENT_NONE,
+				GPU_ATTACHMENT_TEXTURE(txl->refract_color)
+			});
 		}
 
-		bool prev_trace_full = effects->reflection_trace_full;
 		effects->reflection_trace_full = !BKE_collection_engine_property_value_get_bool(props, "ssr_halfres");
 		common_data->ssr_thickness = BKE_collection_engine_property_value_get_float(props, "ssr_thickness");
 		common_data->ssr_border_fac = BKE_collection_engine_property_value_get_float(props, "ssr_border_fade");
@@ -144,47 +145,39 @@ int EEVEE_screen_raytrace_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
 			common_data->ssr_firefly_fac = FLT_MAX;
 		}
 
-		if (prev_trace_full != effects->reflection_trace_full) {
-			DRW_TEXTURE_FREE_SAFE(txl->ssr_hit_output);
-		}
-
 		const int divisor = (effects->reflection_trace_full) ? 1 : 2;
 		int tracing_res[2] = {(int)viewport_size[0] / divisor, (int)viewport_size[1] / divisor};
+		int size_fs[2] = {(int)viewport_size[0], (int)viewport_size[1]};
 		const bool high_qual_input = true; /* TODO dither low quality input */
+		const DRWTextureFormat format = (high_qual_input) ? DRW_TEX_RGBA_16 : DRW_TEX_RGBA_8;
 
 		/* MRT for the shading pass in order to output needed data for the SSR pass. */
-		/* TODO create one texture layer per lobe */
-		if (txl->ssr_specrough_input == NULL) {
-			DRWTextureFormat specrough_format = (high_qual_input) ? DRW_TEX_RGBA_16 : DRW_TEX_RGBA_8;
-			txl->ssr_specrough_input = DRW_texture_create_2D((int)viewport_size[0], (int)viewport_size[1],
-			                                                 specrough_format, 0, NULL);
-		}
+		effects->ssr_specrough_input = DRW_texture_pool_query_2D(size_fs[0], size_fs[1], format,
+		                                                         &draw_engine_eevee_type);
 
-		/* Reattach textures to the right buffer (because we are alternating between buffers) */
-		/* TODO multiple FBO per texture!!!! */
-		DRW_framebuffer_texture_detach(txl->ssr_specrough_input);
-		DRW_framebuffer_texture_attach(fbl->main, txl->ssr_specrough_input, 2, 0);
+		GPU_framebuffer_texture_attach(fbl->main_fb, effects->ssr_specrough_input, 2, 0);
 
 		/* Raytracing output */
-		/* (AMD or Intel) For some reason DRW_TEX_TEMP with DRW_TEX_RG_16I
-		 * creates problems when toggling ssr_halfres. Texture is not read correctly (black output).
-		 * So using a persistent buffer instead. */
-		DRWFboTexture tex_output[2] = {{&txl->ssr_hit_output, DRW_TEX_RG_16I, 0},
-		                               {&stl->g_data->ssr_pdf_output, DRW_TEX_R_16, DRW_TEX_TEMP}};
+		effects->ssr_hit_output = DRW_texture_pool_query_2D(tracing_res[0], tracing_res[1], DRW_TEX_RG_16I,
+		                                                    &draw_engine_eevee_type);
+		effects->ssr_pdf_output = DRW_texture_pool_query_2D(tracing_res[0], tracing_res[1], DRW_TEX_R_16,
+		                                                    &draw_engine_eevee_type);
 
-		DRW_framebuffer_init(&fbl->screen_tracing_fb, &draw_engine_eevee_type,
-		                     tracing_res[0], tracing_res[1],
-		                     tex_output, 2);
+		GPU_framebuffer_ensure_config(&fbl->screen_tracing_fb, {
+			GPU_ATTACHMENT_NONE,
+			GPU_ATTACHMENT_TEXTURE(effects->ssr_hit_output),
+			GPU_ATTACHMENT_TEXTURE(effects->ssr_pdf_output)
+		});
 
 		/* Enable double buffering to be able to read previous frame color */
 		return EFFECT_SSR | EFFECT_NORMAL_BUFFER | EFFECT_DOUBLE_BUFFER | ((use_refraction) ? EFFECT_REFRACT : 0);
 	}
 
 	/* Cleanup to release memory */
-	DRW_TEXTURE_FREE_SAFE(txl->ssr_specrough_input);
-	DRW_TEXTURE_FREE_SAFE(txl->ssr_hit_output);
-	DRW_FRAMEBUFFER_FREE_SAFE(fbl->screen_tracing_fb);
-	stl->g_data->ssr_pdf_output = NULL;
+	GPU_FRAMEBUFFER_FREE_SAFE(fbl->screen_tracing_fb);
+	effects->ssr_specrough_input = NULL;
+	effects->ssr_hit_output = NULL;
+	effects->ssr_pdf_output = NULL;
 
 	return 0;
 }
@@ -220,11 +213,11 @@ void EEVEE_screen_raytrace_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *v
 		 */
 		psl->ssr_raytrace = DRW_pass_create("SSR Raytrace", DRW_STATE_WRITE_COLOR);
 		DRWShadingGroup *grp = DRW_shgroup_create(trace_shader, psl->ssr_raytrace);
-		DRW_shgroup_uniform_buffer(grp, "depthBuffer", &e_data.depth_src);
-		DRW_shgroup_uniform_buffer(grp, "normalBuffer", &txl->ssr_normal_input);
-		DRW_shgroup_uniform_buffer(grp, "specroughBuffer", &txl->ssr_specrough_input);
-		DRW_shgroup_uniform_buffer(grp, "maxzBuffer", &txl->maxzbuffer);
-		DRW_shgroup_uniform_buffer(grp, "planarDepth", &vedata->txl->planar_depth);
+		DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &e_data.depth_src);
+		DRW_shgroup_uniform_texture_ref(grp, "normalBuffer", &effects->ssr_normal_input);
+		DRW_shgroup_uniform_texture_ref(grp, "specroughBuffer", &effects->ssr_specrough_input);
+		DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &txl->maxzbuffer);
+		DRW_shgroup_uniform_texture_ref(grp, "planarDepth", &vedata->txl->planar_depth);
 		DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
 		DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo);
 		DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
@@ -235,22 +228,22 @@ void EEVEE_screen_raytrace_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *v
 
 		psl->ssr_resolve = DRW_pass_create("SSR Resolve", DRW_STATE_WRITE_COLOR | DRW_STATE_ADDITIVE);
 		grp = DRW_shgroup_create(resolve_shader, psl->ssr_resolve);
-		DRW_shgroup_uniform_buffer(grp, "depthBuffer", &e_data.depth_src);
-		DRW_shgroup_uniform_buffer(grp, "normalBuffer", &txl->ssr_normal_input);
-		DRW_shgroup_uniform_buffer(grp, "specroughBuffer", &txl->ssr_specrough_input);
-		DRW_shgroup_uniform_buffer(grp, "probeCubes", &sldata->probe_pool);
-		DRW_shgroup_uniform_buffer(grp, "probePlanars", &vedata->txl->planar_pool);
-		DRW_shgroup_uniform_buffer(grp, "planarDepth", &vedata->txl->planar_depth);
-		DRW_shgroup_uniform_buffer(grp, "hitBuffer", &vedata->txl->ssr_hit_output);
-		DRW_shgroup_uniform_buffer(grp, "pdfBuffer", &stl->g_data->ssr_pdf_output);
-		DRW_shgroup_uniform_buffer(grp, "prevColorBuffer", &txl->color_double_buffer);
+		DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &e_data.depth_src);
+		DRW_shgroup_uniform_texture_ref(grp, "normalBuffer", &effects->ssr_normal_input);
+		DRW_shgroup_uniform_texture_ref(grp, "specroughBuffer", &effects->ssr_specrough_input);
+		DRW_shgroup_uniform_texture_ref(grp, "probeCubes", &sldata->probe_pool);
+		DRW_shgroup_uniform_texture_ref(grp, "probePlanars", &vedata->txl->planar_pool);
+		DRW_shgroup_uniform_texture_ref(grp, "planarDepth", &vedata->txl->planar_depth);
+		DRW_shgroup_uniform_texture_ref(grp, "hitBuffer", &effects->ssr_hit_output);
+		DRW_shgroup_uniform_texture_ref(grp, "pdfBuffer", &effects->ssr_pdf_output);
+		DRW_shgroup_uniform_texture_ref(grp, "prevColorBuffer", &txl->color_double_buffer);
 		DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo);
 		DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo);
 		DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
 		DRW_shgroup_uniform_int(grp, "neighborOffset", &effects->ssr_neighbor_ofs, 1);
 		if ((effects->enabled_effects & EFFECT_GTAO) != 0) {
 			DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
-			DRW_shgroup_uniform_buffer(grp, "horizonBuffer", &vedata->txl->gtao_horizons);
+			DRW_shgroup_uniform_texture_ref(grp, "horizonBuffer", &effects->gtao_horizons);
 		}
 
 		DRW_shgroup_call_add(grp, quad, NULL);
@@ -265,12 +258,11 @@ void EEVEE_refraction_compute(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *v
 	EEVEE_EffectsInfo *effects = stl->effects;
 
 	if ((effects->enabled_effects & EFFECT_REFRACT) != 0) {
-		DRW_framebuffer_texture_attach(fbl->refract_fb, txl->refract_color, 0, 0);
-		DRW_framebuffer_blit(fbl->main, fbl->refract_fb, false, false);
-		EEVEE_downsample_buffer(vedata, fbl->downsample_fb, txl->refract_color, 9);
+		GPU_framebuffer_blit(fbl->main_fb, 0, fbl->refract_fb, 0, GPU_COLOR_BIT);
+		EEVEE_downsample_buffer(vedata, txl->refract_color, 9);
 
 		/* Restore */
-		DRW_framebuffer_bind(fbl->main);
+		GPU_framebuffer_bind(fbl->main_fb);
 	}
 }
 
@@ -287,15 +279,12 @@ void EEVEE_reflection_compute(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *v
 		e_data.depth_src = dtxl->depth;
 
 		DRW_stats_group_start("SSR");
-		DRW_framebuffer_texture_attach(fbl->screen_tracing_fb, stl->g_data->ssr_pdf_output, 1, 0);
-		DRW_framebuffer_bind(fbl->screen_tracing_fb);
 
 		/* Raytrace. */
+		GPU_framebuffer_bind(fbl->screen_tracing_fb);
 		DRW_draw_pass(psl->ssr_raytrace);
 
-		DRW_framebuffer_texture_detach(stl->g_data->ssr_pdf_output);
-
-		EEVEE_downsample_buffer(vedata, fbl->downsample_fb, txl->color_double_buffer, 9);
+		EEVEE_downsample_buffer(vedata, txl->color_double_buffer, 9);
 
 		/* Resolve at fullres */
 		int sample = (DRW_state_is_image_render()) ? effects->taa_render_sample : effects->taa_current_sample;
@@ -320,18 +309,11 @@ void EEVEE_reflection_compute(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *v
 				effects->ssr_halfres_ofs[1] = 1;
 				break;
 		}
-		DRW_framebuffer_texture_detach(dtxl->depth);
-		DRW_framebuffer_texture_detach(txl->ssr_normal_input);
-		DRW_framebuffer_texture_detach(txl->ssr_specrough_input);
-		DRW_framebuffer_bind(fbl->main);
+		GPU_framebuffer_bind(fbl->main_color_fb);
 		DRW_draw_pass(psl->ssr_resolve);
 
 		/* Restore */
-		DRW_framebuffer_texture_attach(fbl->main, dtxl->depth, 0, 0);
-		DRW_framebuffer_texture_attach(fbl->main, txl->ssr_normal_input, 1, 0);
-		DRW_framebuffer_texture_attach(fbl->main, txl->ssr_specrough_input, 2, 0);
-		DRW_framebuffer_bind(fbl->main);
-
+		GPU_framebuffer_bind(fbl->main_fb);
 		DRW_stats_group_end();
 	}
 }
diff --git a/source/blender/draw/engines/eevee/eevee_subsurface.c b/source/blender/draw/engines/eevee/eevee_subsurface.c
index 12a70cc2fe746e58710df557c410996efcf1a562..ebaf559d22bcbc2563651675746f56d39597b9ef 100644
--- a/source/blender/draw/engines/eevee/eevee_subsurface.c
+++ b/source/blender/draw/engines/eevee/eevee_subsurface.c
@@ -66,6 +66,7 @@ int EEVEE_subsurface_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
 	EEVEE_FramebufferList *fbl = vedata->fbl;
 	EEVEE_TextureList *txl = vedata->txl;
 	const float *viewport_size = DRW_viewport_size_get();
+	const int fs_size[2] = {(int)viewport_size[0], (int)viewport_size[1]};
 
 	const DRWContextState *draw_ctx = DRW_context_state_get();
 	ViewLayer *view_layer = draw_ctx->view_layer;
@@ -90,36 +91,45 @@ int EEVEE_subsurface_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
 		 * as the depth buffer we are sampling from. This could be avoided if the stencil is
 		 * a separate texture but that needs OpenGL 4.4 or ARB_texture_stencil8.
 		 * OR OpenGL 4.3 / ARB_ES3_compatibility if using a renderbuffer instead */
-		DRWFboTexture texs[2] = {{&txl->sss_stencil, DRW_TEX_DEPTH_24_STENCIL_8, 0},
-		                         {&txl->sss_blur, DRW_TEX_RGBA_16, DRW_TEX_FILTER}};
-
-		DRW_framebuffer_init(&fbl->sss_blur_fb, &draw_engine_eevee_type, (int)viewport_size[0], (int)viewport_size[1],
-		                     texs, 2);
-
-		DRWFboTexture tex_data = {&txl->sss_data, DRW_TEX_RGBA_16, DRW_TEX_FILTER};
-		DRW_framebuffer_init(&fbl->sss_clear_fb, &draw_engine_eevee_type, (int)viewport_size[0], (int)viewport_size[1],
-		                     &tex_data, 1);
+		effects->sss_stencil = DRW_texture_pool_query_2D(fs_size[0], fs_size[1], DRW_TEX_DEPTH_24_STENCIL_8,
+		                                                 &draw_engine_eevee_type);
+		effects->sss_blur =    DRW_texture_pool_query_2D(fs_size[0], fs_size[1], DRW_TEX_RGBA_16,
+		                                                 &draw_engine_eevee_type);
+		effects->sss_data =    DRW_texture_pool_query_2D(fs_size[0], fs_size[1], DRW_TEX_RGBA_16,
+		                                                 &draw_engine_eevee_type);
+
+		GPU_framebuffer_ensure_config(&fbl->sss_blur_fb, {
+			GPU_ATTACHMENT_TEXTURE(effects->sss_stencil),
+			GPU_ATTACHMENT_TEXTURE(effects->sss_blur)
+		});
+
+		GPU_framebuffer_ensure_config(&fbl->sss_resolve_fb, {
+			GPU_ATTACHMENT_TEXTURE(effects->sss_stencil),
+			GPU_ATTACHMENT_TEXTURE(txl->color)
+		});
+
+		GPU_framebuffer_ensure_config(&fbl->sss_clear_fb, {
+			GPU_ATTACHMENT_NONE,
+			GPU_ATTACHMENT_TEXTURE(effects->sss_data)
+		});
 
 		if (effects->sss_separate_albedo) {
-			if (txl->sss_albedo == NULL) {
-				txl->sss_albedo = DRW_texture_create_2D((int)viewport_size[0], (int)viewport_size[1],
-				                                        DRW_TEX_RGB_11_11_10, 0, NULL);
-			}
+			effects->sss_albedo = DRW_texture_pool_query_2D(fs_size[0], fs_size[1], DRW_TEX_RGB_11_11_10,
+			                                                &draw_engine_eevee_type);
 		}
 		else {
-			/* Cleanup to release memory */
-			DRW_TEXTURE_FREE_SAFE(txl->sss_albedo);
+			effects->sss_albedo = NULL;
 		}
 		return EFFECT_SSS;
 	}
 
 	/* Cleanup to release memory */
-	DRW_TEXTURE_FREE_SAFE(txl->sss_albedo);
-	DRW_TEXTURE_FREE_SAFE(txl->sss_data);
-	DRW_TEXTURE_FREE_SAFE(txl->sss_blur);
-	DRW_TEXTURE_FREE_SAFE(txl->sss_stencil);
-	DRW_FRAMEBUFFER_FREE_SAFE(fbl->sss_blur_fb);
-	DRW_FRAMEBUFFER_FREE_SAFE(fbl->sss_clear_fb);
+	GPU_FRAMEBUFFER_FREE_SAFE(fbl->sss_blur_fb);
+	GPU_FRAMEBUFFER_FREE_SAFE(fbl->sss_resolve_fb);
+	GPU_FRAMEBUFFER_FREE_SAFE(fbl->sss_clear_fb);
+	effects->sss_stencil = NULL;
+	effects->sss_blur = NULL;
+	effects->sss_data = NULL;
 
 	return 0;
 }
@@ -133,23 +143,27 @@ void EEVEE_subsurface_output_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Dat
 {
 	EEVEE_FramebufferList *fbl = vedata->fbl;
 	EEVEE_TextureList *txl = vedata->txl;
-	const float *viewport_size = DRW_viewport_size_get();
+	EEVEE_StorageList *stl = vedata->stl;
+	EEVEE_EffectsInfo *effects = stl->effects;
 
 	const DRWContextState *draw_ctx = DRW_context_state_get();
 	ViewLayer *view_layer = draw_ctx->view_layer;
 	IDProperty *props = BKE_view_layer_engine_evaluated_get(view_layer, COLLECTION_MODE_NONE, RE_engine_id_BLENDER_EEVEE);
 
 	if (BKE_collection_engine_property_value_get_bool(props, "sss_enable")) {
-		float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+		DRW_texture_ensure_fullscreen_2D(&txl->sss_dir_accum, DRW_TEX_RGBA_16, 0);
+		DRW_texture_ensure_fullscreen_2D(&txl->sss_col_accum, DRW_TEX_RGBA_16, 0);
 
-		DRWFboTexture tex_data[2] = {{&txl->sss_dir_accum, DRW_TEX_RGBA_16, 0},
-		                             {&txl->sss_col_accum, DRW_TEX_RGBA_16, 0}};
-		DRW_framebuffer_init(&fbl->sss_accum_fb, &draw_engine_eevee_type, (int)viewport_size[0], (int)viewport_size[1],
-		                     tex_data, 2);
+		GPU_framebuffer_ensure_config(&fbl->sss_accum_fb, {
+			GPU_ATTACHMENT_TEXTURE(effects->sss_stencil),
+			GPU_ATTACHMENT_TEXTURE(txl->sss_dir_accum),
+			GPU_ATTACHMENT_TEXTURE(txl->sss_col_accum)
+		});
 
 		/* Clear texture. */
-		DRW_framebuffer_bind(fbl->sss_accum_fb);
-		DRW_framebuffer_clear(true, false, false, clear, 0.0f);
+		float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+		GPU_framebuffer_bind(fbl->sss_accum_fb);
+		GPU_framebuffer_clear_color(fbl->sss_accum_fb, clear);
 
 		/* Make the opaque refraction pass mask the sss. */
 		DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_CLIP_PLANES |
@@ -161,7 +175,7 @@ void EEVEE_subsurface_output_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Dat
 		/* Cleanup to release memory */
 		DRW_TEXTURE_FREE_SAFE(txl->sss_dir_accum);
 		DRW_TEXTURE_FREE_SAFE(txl->sss_col_accum);
-		DRW_FRAMEBUFFER_FREE_SAFE(fbl->sss_accum_fb);
+		GPU_FRAMEBUFFER_FREE_SAFE(fbl->sss_accum_fb);
 	}
 }
 
@@ -187,7 +201,6 @@ void EEVEE_subsurface_add_pass(
         EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, unsigned int sss_id, struct GPUUniformBuffer *sss_profile)
 {
 	DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
-	EEVEE_TextureList *txl = vedata->txl;
 	EEVEE_PassList *psl = vedata->psl;
 	EEVEE_StorageList *stl = vedata->stl;
 	EEVEE_EffectsInfo *effects = stl->effects;
@@ -195,8 +208,8 @@ void EEVEE_subsurface_add_pass(
 
 	DRWShadingGroup *grp = DRW_shgroup_create(e_data.sss_sh[0], psl->sss_blur_ps);
 	DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
-	DRW_shgroup_uniform_buffer(grp, "depthBuffer", &dtxl->depth);
-	DRW_shgroup_uniform_buffer(grp, "sssData", &txl->sss_data);
+	DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
+	DRW_shgroup_uniform_texture_ref(grp, "sssData", &effects->sss_data);
 	DRW_shgroup_uniform_block(grp, "sssProfile", sss_profile);
 	DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
 	DRW_shgroup_stencil_mask(grp, sss_id);
@@ -205,23 +218,23 @@ void EEVEE_subsurface_add_pass(
 	struct GPUShader *sh = (effects->sss_separate_albedo) ? e_data.sss_sh[2] : e_data.sss_sh[1];
 	grp = DRW_shgroup_create(sh, psl->sss_resolve_ps);
 	DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
-	DRW_shgroup_uniform_buffer(grp, "depthBuffer", &dtxl->depth);
-	DRW_shgroup_uniform_buffer(grp, "sssData", &txl->sss_blur);
+	DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
+	DRW_shgroup_uniform_texture_ref(grp, "sssData", &effects->sss_blur);
 	DRW_shgroup_uniform_block(grp, "sssProfile", sss_profile);
 	DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
 	DRW_shgroup_stencil_mask(grp, sss_id);
 	DRW_shgroup_call_add(grp, quad, NULL);
 
 	if (effects->sss_separate_albedo) {
-		DRW_shgroup_uniform_buffer(grp, "sssAlbedo", &txl->sss_albedo);
+		DRW_shgroup_uniform_texture_ref(grp, "sssAlbedo", &effects->sss_albedo);
 	}
 
 	if (DRW_state_is_image_render()) {
 		grp = DRW_shgroup_create(e_data.sss_sh[3], psl->sss_accum_ps);
 		DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
-		DRW_shgroup_uniform_buffer(grp, "depthBuffer", &dtxl->depth);
-		DRW_shgroup_uniform_buffer(grp, "sssData", &txl->sss_blur);
-		DRW_shgroup_uniform_buffer(grp, "sssAlbedo", &txl->sss_albedo);
+		DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
+		DRW_shgroup_uniform_texture_ref(grp, "sssData", &effects->sss_blur);
+		DRW_shgroup_uniform_texture_ref(grp, "sssAlbedo", &effects->sss_albedo);
 		DRW_shgroup_uniform_block(grp, "sssProfile", sss_profile);
 		DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
 		DRW_shgroup_stencil_mask(grp, sss_id);
@@ -232,7 +245,6 @@ void EEVEE_subsurface_add_pass(
 void EEVEE_subsurface_data_render(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
 {
 	EEVEE_PassList *psl = vedata->psl;
-	EEVEE_TextureList *txl = vedata->txl;
 	EEVEE_FramebufferList *fbl = vedata->fbl;
 	EEVEE_StorageList *stl = vedata->stl;
 	EEVEE_EffectsInfo *effects = stl->effects;
@@ -240,103 +252,60 @@ void EEVEE_subsurface_data_render(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Dat
 	if ((effects->enabled_effects & EFFECT_SSS) != 0) {
 		float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
 		/* Clear sss_data texture only... can this be done in a more clever way? */
-		DRW_framebuffer_bind(fbl->sss_clear_fb);
-		DRW_framebuffer_clear(true, false, false, clear, 0.0f);
-
-
-		DRW_framebuffer_texture_detach(txl->sss_data);
-		if ((effects->enabled_effects & EFFECT_NORMAL_BUFFER) != 0) {
-			DRW_framebuffer_texture_detach(txl->ssr_normal_input);
-		}
-		if ((effects->enabled_effects & EFFECT_SSR) != 0) {
-			DRW_framebuffer_texture_detach(txl->ssr_specrough_input);
-		}
-
-		/* Start at slot 1 because slot 0 is txl->color */
-		int tex_slot = 1;
-		DRW_framebuffer_texture_attach(fbl->main, txl->sss_data, tex_slot++, 0);
-		if (effects->sss_separate_albedo) {
-			DRW_framebuffer_texture_attach(fbl->main, txl->sss_albedo, tex_slot++, 0);
-		}
-		if ((effects->enabled_effects & EFFECT_NORMAL_BUFFER) != 0) {
-			DRW_framebuffer_texture_attach(fbl->main, txl->ssr_normal_input, tex_slot++, 0);
-		}
-		if ((effects->enabled_effects & EFFECT_SSR) != 0) {
-			DRW_framebuffer_texture_attach(fbl->main, txl->ssr_specrough_input, tex_slot++, 0);
-		}
-		DRW_framebuffer_bind(fbl->main);
-
+		GPU_framebuffer_bind(fbl->sss_clear_fb);
+		GPU_framebuffer_clear_color(fbl->sss_clear_fb, clear);
+
+		GPU_framebuffer_ensure_config(&fbl->main_fb, {
+			GPU_ATTACHMENT_LEAVE,
+			GPU_ATTACHMENT_LEAVE,
+			GPU_ATTACHMENT_LEAVE,
+			GPU_ATTACHMENT_LEAVE,
+			GPU_ATTACHMENT_TEXTURE(effects->sss_data),
+			GPU_ATTACHMENT_TEXTURE(effects->sss_albedo)
+		});
+
+		GPU_framebuffer_bind(fbl->main_fb);
 		DRW_draw_pass(psl->sss_pass);
 
 		/* Restore */
-		DRW_framebuffer_texture_detach(txl->sss_data);
-		if (effects->sss_separate_albedo) {
-			DRW_framebuffer_texture_detach(txl->sss_albedo);
-		}
-		if ((effects->enabled_effects & EFFECT_NORMAL_BUFFER) != 0) {
-			DRW_framebuffer_texture_detach(txl->ssr_normal_input);
-		}
-		if ((effects->enabled_effects & EFFECT_SSR) != 0) {
-			DRW_framebuffer_texture_detach(txl->ssr_specrough_input);
-		}
-
-		DRW_framebuffer_texture_attach(fbl->sss_clear_fb, txl->sss_data, 0, 0);
-		if ((effects->enabled_effects & EFFECT_NORMAL_BUFFER) != 0) {
-			DRW_framebuffer_texture_attach(fbl->main, txl->ssr_normal_input, 1, 0);
-		}
-		if ((effects->enabled_effects & EFFECT_SSR) != 0) {
-			DRW_framebuffer_texture_attach(fbl->main, txl->ssr_specrough_input, 2, 0);
-		}
+		GPU_framebuffer_ensure_config(&fbl->main_fb, {
+			GPU_ATTACHMENT_LEAVE,
+			GPU_ATTACHMENT_LEAVE,
+			GPU_ATTACHMENT_LEAVE,
+			GPU_ATTACHMENT_LEAVE,
+			GPU_ATTACHMENT_NONE,
+			GPU_ATTACHMENT_NONE
+		});
 	}
 }
 
 void EEVEE_subsurface_compute(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
 {
 	EEVEE_PassList *psl = vedata->psl;
-	EEVEE_FramebufferList *fbl = vedata->fbl;
-	EEVEE_TextureList *txl = vedata->txl;
 	EEVEE_StorageList *stl = vedata->stl;
+	EEVEE_TextureList *txl = vedata->txl;
+	EEVEE_FramebufferList *fbl = vedata->fbl;
 	EEVEE_EffectsInfo *effects = stl->effects;
 
 	if ((effects->enabled_effects & EFFECT_SSS) != 0) {
 		float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
-		DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
 
 		DRW_stats_group_start("SSS");
 
 		/* Copy stencil channel, could be avoided (see EEVEE_subsurface_init) */
-		DRW_framebuffer_blit(fbl->main, fbl->sss_blur_fb, false, true);
-
-		DRW_framebuffer_texture_detach(dtxl->depth);
+		GPU_framebuffer_blit(fbl->main_fb, 0, fbl->sss_blur_fb, 0, GPU_STENCIL_BIT);
 
 		/* 1. horizontal pass */
-		DRW_framebuffer_bind(fbl->sss_blur_fb);
-		DRW_framebuffer_clear(true, false, false, clear, 0.0f);
+		GPU_framebuffer_bind(fbl->sss_blur_fb);
+		GPU_framebuffer_clear_color(fbl->sss_blur_fb, clear);
 		DRW_draw_pass(psl->sss_blur_ps);
 
 		/* 2. vertical pass + Resolve */
-		DRW_framebuffer_texture_detach(txl->sss_stencil);
-		if ((effects->enabled_effects & EFFECT_NORMAL_BUFFER) != 0) {
-			DRW_framebuffer_texture_detach(txl->ssr_normal_input);
-		}
-		if ((effects->enabled_effects & EFFECT_SSR) != 0) {
-			DRW_framebuffer_texture_detach(txl->ssr_specrough_input);
-		}
-		DRW_framebuffer_texture_attach(fbl->main, txl->sss_stencil, 0, 0);
-		DRW_framebuffer_bind(fbl->main);
+		GPU_framebuffer_texture_attach(fbl->sss_resolve_fb, txl->color, 0, 0);
+		GPU_framebuffer_bind(fbl->sss_resolve_fb);
 		DRW_draw_pass(psl->sss_resolve_ps);
 
-		/* Restore */
-		DRW_framebuffer_texture_detach(txl->sss_stencil);
-		DRW_framebuffer_texture_attach(fbl->sss_blur_fb, txl->sss_stencil, 0, 0);
-		DRW_framebuffer_texture_attach(fbl->main, dtxl->depth, 0, 0);
-		if ((effects->enabled_effects & EFFECT_NORMAL_BUFFER) != 0) {
-			DRW_framebuffer_texture_attach(fbl->main, txl->ssr_normal_input, 1, 0);
-		}
-		if ((effects->enabled_effects & EFFECT_SSR) != 0) {
-			DRW_framebuffer_texture_attach(fbl->main, txl->ssr_specrough_input, 2, 0);
-		}
-
+		GPU_framebuffer_bind(fbl->main_fb);
 		DRW_stats_group_end();
 	}
 }
@@ -345,24 +314,19 @@ void EEVEE_subsurface_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEV
 {
 	EEVEE_PassList *psl = vedata->psl;
 	EEVEE_FramebufferList *fbl = vedata->fbl;
-	EEVEE_TextureList *txl = vedata->txl;
 	EEVEE_StorageList *stl = vedata->stl;
 	EEVEE_EffectsInfo *effects = stl->effects;
 
 	if (((effects->enabled_effects & EFFECT_SSS) != 0) && (fbl->sss_accum_fb != NULL)) {
 		/* Copy stencil channel, could be avoided (see EEVEE_subsurface_init) */
-		DRW_framebuffer_blit(fbl->main, fbl->sss_blur_fb, false, true);
+		GPU_framebuffer_blit(fbl->main_fb, 0, fbl->sss_blur_fb, 0, GPU_STENCIL_BIT);
 
 		/* Only do vertical pass + Resolve */
-		DRW_framebuffer_texture_detach(txl->sss_stencil);
-		DRW_framebuffer_texture_attach(fbl->sss_accum_fb, txl->sss_stencil, 0, 0);
-		DRW_framebuffer_bind(fbl->sss_accum_fb);
+		GPU_framebuffer_bind(fbl->sss_accum_fb);
 		DRW_draw_pass(psl->sss_accum_ps);
 
 		/* Restore */
-		DRW_framebuffer_texture_detach(txl->sss_stencil);
-		DRW_framebuffer_texture_attach(fbl->sss_blur_fb, txl->sss_stencil, 0, 0);
-		DRW_framebuffer_bind(fbl->main);
+		GPU_framebuffer_bind(fbl->main_fb);
 	}
 }
 
diff --git a/source/blender/draw/engines/eevee/eevee_temporal_sampling.c b/source/blender/draw/engines/eevee/eevee_temporal_sampling.c
index a417a9bdf564ba523f660fb98566cf2be9d1c2bc..acc1bff63312a15a7b2980e70f42957c4df2e73d 100644
--- a/source/blender/draw/engines/eevee/eevee_temporal_sampling.c
+++ b/source/blender/draw/engines/eevee/eevee_temporal_sampling.c
@@ -179,7 +179,6 @@ int EEVEE_temporal_sampling_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data
 	    (effects->enabled_effects & EFFECT_MOTION_BLUR) == 0) ||
 	    DRW_state_is_image_render())
 	{
-		const float *viewport_size = DRW_viewport_size_get();
 		float persmat[4][4], viewmat[4][4];
 
 		if (!e_data.taa_resolve_sh) {
@@ -239,11 +238,11 @@ int EEVEE_temporal_sampling_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data
 			effects->taa_current_sample = 1;
 		}
 
-		DRWFboTexture tex_double_buffer = {&txl->depth_double_buffer, DRW_TEX_DEPTH_24_STENCIL_8, 0};
+		DRW_texture_ensure_fullscreen_2D(&txl->depth_double_buffer, DRW_TEX_DEPTH_24_STENCIL_8, 0);
 
-		DRW_framebuffer_init(&fbl->depth_double_buffer_fb, &draw_engine_eevee_type,
-		                    (int)viewport_size[0], (int)viewport_size[1],
-		                    &tex_double_buffer, 1);
+		GPU_framebuffer_ensure_config(&fbl->double_buffer_depth_fb, {
+			GPU_ATTACHMENT_TEXTURE(txl->depth_double_buffer)
+		});
 
 		return EFFECT_TAA | EFFECT_DOUBLE_BUFFER | EFFECT_POST_BUFFER;
 	}
@@ -252,7 +251,7 @@ int EEVEE_temporal_sampling_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data
 
 	/* Cleanup to release memory */
 	DRW_TEXTURE_FREE_SAFE(txl->depth_double_buffer);
-	DRW_FRAMEBUFFER_FREE_SAFE(fbl->depth_double_buffer_fb);
+	GPU_FRAMEBUFFER_FREE_SAFE(fbl->double_buffer_depth_fb);
 
 	return 0;
 }
@@ -268,8 +267,8 @@ void EEVEE_temporal_sampling_cache_init(EEVEE_ViewLayerData *UNUSED(sldata), EEV
 		psl->taa_resolve = DRW_pass_create("Temporal AA Resolve", DRW_STATE_WRITE_COLOR);
 		DRWShadingGroup *grp = DRW_shgroup_create(e_data.taa_resolve_sh, psl->taa_resolve);
 
-		DRW_shgroup_uniform_buffer(grp, "historyBuffer", &txl->color_double_buffer);
-		DRW_shgroup_uniform_buffer(grp, "colorBuffer", &txl->color);
+		DRW_shgroup_uniform_texture_ref(grp, "historyBuffer", &txl->color_double_buffer);
+		DRW_shgroup_uniform_texture_ref(grp, "colorBuffer", &txl->color);
 		DRW_shgroup_uniform_float(grp, "alpha", &effects->taa_alpha, 1);
 		DRW_shgroup_call_add(grp, DRW_cache_fullscreen_quad_get(), NULL);
 	}
@@ -293,27 +292,28 @@ void EEVEE_temporal_sampling_draw(EEVEE_Data *vedata)
 				effects->taa_alpha = 1.0f / (float)(effects->taa_current_sample);
 			}
 
-			DRW_framebuffer_bind(fbl->effect_fb);
+			GPU_framebuffer_bind(fbl->effect_color_fb);
 			DRW_draw_pass(psl->taa_resolve);
 
 			/* Restore the depth from sample 1. */
 			if (!DRW_state_is_image_render()) {
-				DRW_framebuffer_blit(fbl->depth_double_buffer_fb, fbl->main, true, false);
+				GPU_framebuffer_blit(fbl->double_buffer_depth_fb, 0, fbl->main_fb, 0, GPU_DEPTH_BIT);
 			}
 
 			/* Special Swap */
-			SWAP(struct GPUFrameBuffer *, fbl->effect_fb, fbl->double_buffer);
+			SWAP(struct GPUFrameBuffer *, fbl->effect_fb, fbl->double_buffer_fb);
+			SWAP(struct GPUFrameBuffer *, fbl->effect_color_fb, fbl->double_buffer_color_fb);
 			SWAP(GPUTexture *, txl->color_post, txl->color_double_buffer);
 			effects->swap_double_buffer = false;
 			effects->source_buffer = txl->color_double_buffer;
-			effects->target_buffer = fbl->main;
+			effects->target_buffer = fbl->main_color_fb;
 		}
 		else {
 			/* Save the depth buffer for the next frame.
 			 * This saves us from doing anything special
 			 * in the other mode engines. */
 			if (!DRW_state_is_image_render()) {
-				DRW_framebuffer_blit(fbl->main, fbl->depth_double_buffer_fb, true, false);
+				GPU_framebuffer_blit(fbl->main_fb, 0, fbl->double_buffer_depth_fb, 0, GPU_DEPTH_BIT);
 			}
 		}
 
diff --git a/source/blender/draw/engines/eevee/eevee_volumes.c b/source/blender/draw/engines/eevee/eevee_volumes.c
index 0c0ffa391463a5663e617682209f2b1cb3f6c75a..408015addd4eee4482c089e9f7abe6584556e564 100644
--- a/source/blender/draw/engines/eevee/eevee_volumes.c
+++ b/source/blender/draw/engines/eevee/eevee_volumes.c
@@ -76,7 +76,7 @@ extern char datatoc_volumetric_resolve_frag_glsl[];
 extern char datatoc_volumetric_scatter_frag_glsl[];
 extern char datatoc_volumetric_integration_frag_glsl[];
 extern char datatoc_volumetric_lib_glsl[];
-extern char datatoc_gpu_shader_fullscreen_vert_glsl[];
+extern char datatoc_common_fullscreen_vert_glsl[];
 
 static void eevee_create_shader_volumes(void)
 {
@@ -126,7 +126,7 @@ static void eevee_create_shader_volumes(void)
 	        datatoc_volumetric_integration_frag_glsl,
 	        e_data.volumetric_common_lib, NULL);
 	e_data.volumetric_resolve_sh = DRW_shader_create_with_lib(
-	        datatoc_gpu_shader_fullscreen_vert_glsl, NULL,
+	        datatoc_common_fullscreen_vert_glsl, NULL,
 	        datatoc_volumetric_resolve_frag_glsl,
 	        e_data.volumetric_common_lib, NULL);
 }
@@ -195,9 +195,9 @@ int EEVEE_volumes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
 			DRW_TEXTURE_FREE_SAFE(txl->volume_transmittance);
 			DRW_TEXTURE_FREE_SAFE(txl->volume_scatter_history);
 			DRW_TEXTURE_FREE_SAFE(txl->volume_transmittance_history);
-			DRW_FRAMEBUFFER_FREE_SAFE(fbl->volumetric_fb);
-			DRW_FRAMEBUFFER_FREE_SAFE(fbl->volumetric_scat_fb);
-			DRW_FRAMEBUFFER_FREE_SAFE(fbl->volumetric_integ_fb);
+			GPU_FRAMEBUFFER_FREE_SAFE(fbl->volumetric_fb);
+			GPU_FRAMEBUFFER_FREE_SAFE(fbl->volumetric_scat_fb);
+			GPU_FRAMEBUFFER_FREE_SAFE(fbl->volumetric_integ_fb);
 			common_data->vol_tex_size[0] = tex_size[0];
 			common_data->vol_tex_size[1] = tex_size[1];
 			common_data->vol_tex_size[2] = tex_size[2];
@@ -268,28 +268,23 @@ int EEVEE_volumes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
 		EEVEE_volumes_set_jitter(sldata, current_sample);
 
 		/* Framebuffer setup */
-		DRWFboTexture tex_vol[4] = {{&txl->volume_prop_scattering, DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER},
-		                            {&txl->volume_prop_extinction, DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER},
-		                            {&txl->volume_prop_emission, DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER},
-		                            {&txl->volume_prop_phase, DRW_TEX_RG_16, DRW_TEX_FILTER}};
-
-		DRW_framebuffer_init(&fbl->volumetric_fb, &draw_engine_eevee_type,
-		                     (int)tex_size[0], (int)tex_size[1],
-		                     tex_vol, 4);
-
-		DRWFboTexture tex_vol_scat[2] = {{&txl->volume_scatter, DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER},
-		                                 {&txl->volume_transmittance, DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER}};
-
-		DRW_framebuffer_init(&fbl->volumetric_scat_fb, &draw_engine_eevee_type,
-		                     (int)tex_size[0], (int)tex_size[1],
-		                     tex_vol_scat, 2);
-
-		DRWFboTexture tex_vol_integ[2] = {{&txl->volume_scatter_history, DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER},
-		                                  {&txl->volume_transmittance_history, DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER}};
-
-		DRW_framebuffer_init(&fbl->volumetric_integ_fb, &draw_engine_eevee_type,
-		                     (int)tex_size[0], (int)tex_size[1],
-		                     tex_vol_integ, 2);
+		GPU_framebuffer_ensure_config(&fbl->volumetric_fb, {
+			GPU_ATTACHMENT_NONE,
+			GPU_ATTACHMENT_TEXTURE(txl->volume_prop_scattering),
+			GPU_ATTACHMENT_TEXTURE(txl->volume_prop_extinction),
+			GPU_ATTACHMENT_TEXTURE(txl->volume_prop_emission),
+			GPU_ATTACHMENT_TEXTURE(txl->volume_prop_phase)
+		});
+		GPU_framebuffer_ensure_config(&fbl->volumetric_scat_fb, {
+			GPU_ATTACHMENT_NONE,
+			GPU_ATTACHMENT_TEXTURE(txl->volume_scatter),
+			GPU_ATTACHMENT_TEXTURE(txl->volume_transmittance)
+		});
+		GPU_framebuffer_ensure_config(&fbl->volumetric_integ_fb, {
+			GPU_ATTACHMENT_NONE,
+			GPU_ATTACHMENT_TEXTURE(txl->volume_scatter_history),
+			GPU_ATTACHMENT_TEXTURE(txl->volume_transmittance_history)
+		});
 
 		float integration_start = BKE_collection_engine_property_value_get_float(props, "volumetric_start");
 		float integration_end = BKE_collection_engine_property_value_get_float(props, "volumetric_end");
@@ -345,9 +340,9 @@ int EEVEE_volumes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
 	DRW_TEXTURE_FREE_SAFE(txl->volume_transmittance);
 	DRW_TEXTURE_FREE_SAFE(txl->volume_scatter_history);
 	DRW_TEXTURE_FREE_SAFE(txl->volume_transmittance_history);
-	DRW_FRAMEBUFFER_FREE_SAFE(fbl->volumetric_fb);
-	DRW_FRAMEBUFFER_FREE_SAFE(fbl->volumetric_scat_fb);
-	DRW_FRAMEBUFFER_FREE_SAFE(fbl->volumetric_integ_fb);
+	GPU_FRAMEBUFFER_FREE_SAFE(fbl->volumetric_fb);
+	GPU_FRAMEBUFFER_FREE_SAFE(fbl->volumetric_scat_fb);
+	GPU_FRAMEBUFFER_FREE_SAFE(fbl->volumetric_integ_fb);
 
 	return 0;
 }
@@ -426,14 +421,14 @@ void EEVEE_volumes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
 		psl->volumetric_scatter_ps = DRW_pass_create("Volumetric Scattering", DRW_STATE_WRITE_COLOR);
 		grp = DRW_shgroup_empty_tri_batch_create(scatter_sh, psl->volumetric_scatter_ps,
 		                                         common_data->vol_tex_size[2]);
-		DRW_shgroup_uniform_buffer(grp, "irradianceGrid", &sldata->irradiance_pool);
-		DRW_shgroup_uniform_buffer(grp, "shadowTexture", &sldata->shadow_pool);
-		DRW_shgroup_uniform_buffer(grp, "volumeScattering", &txl->volume_prop_scattering);
-		DRW_shgroup_uniform_buffer(grp, "volumeExtinction", &txl->volume_prop_extinction);
-		DRW_shgroup_uniform_buffer(grp, "volumeEmission", &txl->volume_prop_emission);
-		DRW_shgroup_uniform_buffer(grp, "volumePhase", &txl->volume_prop_phase);
-		DRW_shgroup_uniform_buffer(grp, "historyScattering", &txl->volume_scatter_history);
-		DRW_shgroup_uniform_buffer(grp, "historyTransmittance", &txl->volume_transmittance_history);
+		DRW_shgroup_uniform_texture_ref(grp, "irradianceGrid", &sldata->irradiance_pool);
+		DRW_shgroup_uniform_texture_ref(grp, "shadowTexture", &sldata->shadow_pool);
+		DRW_shgroup_uniform_texture_ref(grp, "volumeScattering", &txl->volume_prop_scattering);
+		DRW_shgroup_uniform_texture_ref(grp, "volumeExtinction", &txl->volume_prop_extinction);
+		DRW_shgroup_uniform_texture_ref(grp, "volumeEmission", &txl->volume_prop_emission);
+		DRW_shgroup_uniform_texture_ref(grp, "volumePhase", &txl->volume_prop_phase);
+		DRW_shgroup_uniform_texture_ref(grp, "historyScattering", &txl->volume_scatter_history);
+		DRW_shgroup_uniform_texture_ref(grp, "historyTransmittance", &txl->volume_transmittance_history);
 		DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo);
 		DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo);
 		DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
@@ -442,16 +437,16 @@ void EEVEE_volumes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
 		grp = DRW_shgroup_empty_tri_batch_create(e_data.volumetric_integration_sh,
 		                                         psl->volumetric_integration_ps,
 		                                         common_data->vol_tex_size[2]);
-		DRW_shgroup_uniform_buffer(grp, "volumeScattering", &txl->volume_scatter);
-		DRW_shgroup_uniform_buffer(grp, "volumeExtinction", &txl->volume_transmittance);
+		DRW_shgroup_uniform_texture_ref(grp, "volumeScattering", &txl->volume_scatter);
+		DRW_shgroup_uniform_texture_ref(grp, "volumeExtinction", &txl->volume_transmittance);
 		DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
 
 		psl->volumetric_resolve_ps = DRW_pass_create("Volumetric Resolve", DRW_STATE_WRITE_COLOR);
 		grp = DRW_shgroup_create(e_data.volumetric_resolve_sh, psl->volumetric_resolve_ps);
-		DRW_shgroup_uniform_buffer(grp, "inScattering", &txl->volume_scatter);
-		DRW_shgroup_uniform_buffer(grp, "inTransmittance", &txl->volume_transmittance);
-		DRW_shgroup_uniform_buffer(grp, "inSceneColor", &e_data.color_src);
-		DRW_shgroup_uniform_buffer(grp, "inSceneDepth", &e_data.depth_src);
+		DRW_shgroup_uniform_texture_ref(grp, "inScattering", &txl->volume_scatter);
+		DRW_shgroup_uniform_texture_ref(grp, "inTransmittance", &txl->volume_transmittance);
+		DRW_shgroup_uniform_texture_ref(grp, "inSceneColor", &e_data.color_src);
+		DRW_shgroup_uniform_texture_ref(grp, "inSceneDepth", &e_data.depth_src);
 		DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
 		DRW_shgroup_call_add(grp, DRW_cache_fullscreen_quad_get(), NULL);
 	}
@@ -508,10 +503,10 @@ void EEVEE_volumes_cache_object_add(EEVEE_ViewLayerData *sldata, EEVEE_Data *ved
 		}
 
 		if (sds->tex != NULL) {
-			DRW_shgroup_uniform_buffer(grp, "sampdensity", &sds->tex);
+			DRW_shgroup_uniform_texture_ref(grp, "sampdensity", &sds->tex);
 		}
 		if (sds->tex_flame != NULL) {
-			DRW_shgroup_uniform_buffer(grp, "sampflame", &sds->tex_flame);
+			DRW_shgroup_uniform_texture_ref(grp, "sampflame", &sds->tex_flame);
 		}
 
 		/* Output is such that 0..1 maps to 0..1000K */
@@ -530,16 +525,16 @@ void EEVEE_volumes_compute(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *veda
 		DRW_stats_group_start("Volumetrics");
 
 		/* Step 1: Participating Media Properties */
-		DRW_framebuffer_bind(fbl->volumetric_fb);
+		GPU_framebuffer_bind(fbl->volumetric_fb);
 		DRW_draw_pass(psl->volumetric_world_ps);
 		DRW_draw_pass(psl->volumetric_objects_ps);
 
 		/* Step 2: Scatter Light */
-		DRW_framebuffer_bind(fbl->volumetric_scat_fb);
+		GPU_framebuffer_bind(fbl->volumetric_scat_fb);
 		DRW_draw_pass(psl->volumetric_scatter_ps);
 
 		/* Step 3: Integration */
-		DRW_framebuffer_bind(fbl->volumetric_integ_fb);
+		GPU_framebuffer_bind(fbl->volumetric_integ_fb);
 		DRW_draw_pass(psl->volumetric_integration_ps);
 
 		/* Swap volume history buffers */
@@ -548,7 +543,7 @@ void EEVEE_volumes_compute(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *veda
 		SWAP(GPUTexture *, txl->volume_transmittance, txl->volume_transmittance_history);
 
 		/* Restore */
-		DRW_framebuffer_bind(fbl->main);
+		GPU_framebuffer_bind(fbl->main_fb);
 
 		DRW_stats_group_end();
 	}
@@ -569,14 +564,14 @@ void EEVEE_volumes_resolve(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *veda
 		e_data.depth_src = dtxl->depth;
 
 		/* Step 4: Apply for opaque */
-		DRW_framebuffer_bind(fbl->effect_fb);
+		GPU_framebuffer_bind(fbl->effect_color_fb);
 		DRW_draw_pass(psl->volumetric_resolve_ps);
 
 		/* Swap the buffers and rebind depth to the current buffer */
-		DRW_framebuffer_texture_detach(dtxl->depth);
-		SWAP(struct GPUFrameBuffer *, fbl->main, fbl->effect_fb);
+		SWAP(GPUFrameBuffer *, fbl->main_fb, fbl->effect_fb);
+		SWAP(GPUFrameBuffer *, fbl->main_color_fb, fbl->effect_color_fb);
 		SWAP(GPUTexture *, txl->color, txl->color_post);
-		DRW_framebuffer_texture_attach(fbl->main, dtxl->depth, 0, 0);
+		GPU_framebuffer_texture_attach(fbl->main_fb, dtxl->depth, 0, 0);
 	}
 }
 
diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl
index 23487e66a171fa745df527caf55084d8d09fa245..77c873c15030a69d2855f6d5f9cd3dfc318725c2 100644
--- a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl
@@ -33,9 +33,9 @@ mat4(vec4(1.0, 0.0, 0.0, 0.0), \
      vec4(0.0, 0.0, -(farClip + nearClip) / (farClip - nearClip), -1.0), \
      vec4(0.0, 0.0, (-2.0 * farClip * nearClip) / (farClip - nearClip), 0.0))
 
-#define ViewMatrixInverse             invert(ViewMatrix)
-#define ViewProjectionMatrixInverse   invert(ViewProjectionMatrix)
-#define ProjectionMatrixInverse       invert(ProjectionMatrix)
+#define ViewMatrixInverse             inverse(ViewMatrix)
+#define ViewProjectionMatrixInverse   inverse(ViewProjectionMatrix)
+#define ProjectionMatrixInverse       inverse(ProjectionMatrix)
 #define CameraTexCoFactors            vec4(1.0f, 1.0f, 0.0f, 0.0f)
 #endif
 
@@ -760,20 +760,13 @@ Closure closure_add(Closure cl1, Closure cl2)
 
 #  if defined(MESH_SHADER) && !defined(USE_ALPHA_HASH) && !defined(USE_ALPHA_CLIP) && !defined(SHADOW_SHADER) && !defined(USE_MULTIPLY)
 layout(location = 0) out vec4 fragColor;
+layout(location = 1) out vec4 ssrNormals;
+layout(location = 2) out vec4 ssrData;
 #    ifdef USE_SSS
+layout(location = 3) out vec4 sssData;
 #      ifdef USE_SSS_ALBEDO
-layout(location = 1) out vec4 sssData;
-layout(location = 2) out vec4 sssAlbedo;
-layout(location = 3) out vec4 ssrNormals;
-layout(location = 4) out vec4 ssrData;
-#      else
-layout(location = 1) out vec4 sssData;
-layout(location = 2) out vec4 ssrNormals;
-layout(location = 3) out vec4 ssrData;
+layout(location = 4) out vec4 sssAlbedo;
 #      endif /* USE_SSS_ALBEDO */
-#    else
-layout(location = 1) out vec4 ssrNormals;
-layout(location = 2) out vec4 ssrData;
 #    endif /* USE_SSS */
 
 Closure nodetree_exec(void); /* Prototype */
diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h
index 791d38ff62f721f2d53e8bf5855d1a1b800b1148..60e855108f979e95f6685891b236577b4c5c0a91 100644
--- a/source/blender/draw/intern/DRW_render.h
+++ b/source/blender/draw/intern/DRW_render.h
@@ -45,6 +45,8 @@
 #include "DNA_material_types.h"
 #include "DNA_scene_types.h"
 
+#include "GPU_framebuffer.h"
+
 #include "draw_common.h"
 #include "draw_cache.h"
 #include "draw_view.h"
@@ -55,6 +57,8 @@
 
 #include "RE_engine.h"
 
+#include "DEG_depsgraph.h"
+
 struct rcti;
 struct bContext;
 struct GPUFrameBuffer;
@@ -100,9 +104,8 @@ typedef char DRWViewportEmptyList;
 #define MULTISAMPLE_SYNC_ENABLE(dfbl) { \
 	if (dfbl->multisample_fb != NULL) { \
 		DRW_stats_query_start("Multisample Blit"); \
-		DRW_framebuffer_blit(dfbl->default_fb, dfbl->multisample_fb, false, false); \
-		DRW_framebuffer_blit(dfbl->default_fb, dfbl->multisample_fb, true, false); \
-		DRW_framebuffer_bind(dfbl->multisample_fb); \
+		GPU_framebuffer_blit(dfbl->default_fb, 0, dfbl->multisample_fb, 0, GPU_COLOR_BIT | GPU_DEPTH_BIT); \
+		GPU_framebuffer_bind(dfbl->multisample_fb); \
 		DRW_stats_query_end(); \
 	} \
 }
@@ -110,9 +113,8 @@ typedef char DRWViewportEmptyList;
 #define MULTISAMPLE_SYNC_DISABLE(dfbl) { \
 	if (dfbl->multisample_fb != NULL) { \
 		DRW_stats_query_start("Multisample Resolve"); \
-		DRW_framebuffer_blit(dfbl->multisample_fb, dfbl->default_fb, false, false); \
-		DRW_framebuffer_blit(dfbl->multisample_fb, dfbl->default_fb, true, false); \
-		DRW_framebuffer_bind(dfbl->default_fb); \
+		GPU_framebuffer_blit(dfbl->multisample_fb, 0, dfbl->default_fb, 0, GPU_COLOR_BIT | GPU_DEPTH_BIT); \
+		GPU_framebuffer_bind(dfbl->default_fb); \
 		DRW_stats_query_end(); \
 	} \
 }
@@ -153,6 +155,8 @@ typedef struct DrawEngineType {
 /* Buffer and textures used by the viewport by default */
 typedef struct DefaultFramebufferList {
 	struct GPUFrameBuffer *default_fb;
+	struct GPUFrameBuffer *color_only_fb;
+	struct GPUFrameBuffer *depth_only_fb;
 	struct GPUFrameBuffer *multisample_fb;
 } DefaultFramebufferList;
 
@@ -195,9 +199,13 @@ typedef enum {
 	DRW_TEX_WRAP = (1 << 1),
 	DRW_TEX_COMPARE = (1 << 2),
 	DRW_TEX_MIPMAP = (1 << 3),
-	DRW_TEX_TEMP = (1 << 4),
 } DRWTextureFlag;
 
+/* Textures from DRW_texture_pool_query_* have the options
+ * DRW_TEX_FILTER for color float textures, and no options
+ * for depth textures and integer textures. */
+struct GPUTexture *DRW_texture_pool_query_2D(int w, int h, DRWTextureFormat format, DrawEngineType *engine_type);
+
 struct GPUTexture *DRW_texture_create_1D(
         int w, DRWTextureFormat format, DRWTextureFlag flags, const float *fpixels);
 struct GPUTexture *DRW_texture_create_2D(
@@ -208,8 +216,13 @@ struct GPUTexture *DRW_texture_create_3D(
         int w, int h, int d, DRWTextureFormat format, DRWTextureFlag flags, const float *fpixels);
 struct GPUTexture *DRW_texture_create_cube(
         int w, DRWTextureFormat format, DRWTextureFlag flags, const float *fpixels);
+
+void DRW_texture_ensure_fullscreen_2D(
+        struct GPUTexture **tex, DRWTextureFormat format, DRWTextureFlag flags);
+void DRW_texture_ensure_2D(
+        struct GPUTexture **tex, int w, int h, DRWTextureFormat format, DRWTextureFlag flags);
+
 void DRW_texture_generate_mipmaps(struct GPUTexture *tex);
-void DRW_texture_update(struct GPUTexture *tex, const float *pixels);
 void DRW_texture_free(struct GPUTexture *tex);
 #define DRW_TEXTURE_FREE_SAFE(tex) do { \
 	if (tex != NULL) { \
@@ -229,40 +242,6 @@ void DRW_uniformbuffer_free(struct GPUUniformBuffer *ubo);
 	} \
 } while (0)
 
-/* Buffers */
-#define MAX_FBO_TEX			5
-
-typedef struct DRWFboTexture {
-	struct GPUTexture **tex;
-	int format;
-	DRWTextureFlag flag;
-} DRWFboTexture;
-
-struct GPUFrameBuffer *DRW_framebuffer_create(void);
-void DRW_framebuffer_init(
-        struct GPUFrameBuffer **fb, void *engine_type, int width, int height,
-        DRWFboTexture textures[MAX_FBO_TEX], int textures_len);
-void DRW_framebuffer_bind(struct GPUFrameBuffer *fb);
-void DRW_framebuffer_clear(bool color, bool depth, bool stencil, float clear_col[4], float clear_depth);
-void DRW_framebuffer_read_data(int x, int y, int w, int h, int channels, int slot, float *data);
-void DRW_framebuffer_read_depth(int x, int y, int w, int h, float *data);
-void DRW_framebuffer_texture_attach(struct GPUFrameBuffer *fb, struct GPUTexture *tex, int slot, int mip);
-void DRW_framebuffer_texture_layer_attach(struct GPUFrameBuffer *fb, struct GPUTexture *tex, int slot, int layer, int mip);
-void DRW_framebuffer_cubeface_attach(struct GPUFrameBuffer *fb, struct GPUTexture *tex, int slot, int face, int mip);
-void DRW_framebuffer_texture_detach(struct GPUTexture *tex);
-void DRW_framebuffer_blit(struct GPUFrameBuffer *fb_read, struct GPUFrameBuffer *fb_write, bool depth, bool stencil);
-void DRW_framebuffer_recursive_downsample(
-        struct GPUFrameBuffer *fb, struct GPUTexture *tex, int num_iter,
-        void (*callback)(void *userData, int level), void *userData);
-void DRW_framebuffer_viewport_size(struct GPUFrameBuffer *fb_read, int x, int y, int w, int h);
-void DRW_framebuffer_free(struct GPUFrameBuffer *fb);
-#define DRW_FRAMEBUFFER_FREE_SAFE(fb) do { \
-	if (fb != NULL) { \
-		DRW_framebuffer_free(fb); \
-		fb = NULL; \
-	} \
-} while (0)
-
 void DRW_transform_to_display(struct GPUTexture *tex);
 
 /* Shaders */
@@ -385,7 +364,7 @@ void DRW_shgroup_uniform_texture(DRWShadingGroup *shgroup, const char *name, con
 void DRW_shgroup_uniform_texture_persistent(DRWShadingGroup *shgroup, const char *name, const struct GPUTexture *tex);
 void DRW_shgroup_uniform_block(DRWShadingGroup *shgroup, const char *name, const struct GPUUniformBuffer *ubo);
 void DRW_shgroup_uniform_block_persistent(DRWShadingGroup *shgroup, const char *name, const struct GPUUniformBuffer *ubo);
-void DRW_shgroup_uniform_buffer(DRWShadingGroup *shgroup, const char *name, struct GPUTexture **tex);
+void DRW_shgroup_uniform_texture_ref(DRWShadingGroup *shgroup, const char *name, struct GPUTexture **tex);
 void DRW_shgroup_uniform_float(DRWShadingGroup *shgroup, const char *name, const float *value, int arraysize);
 void DRW_shgroup_uniform_vec2(DRWShadingGroup *shgroup, const char *name, const float *value, int arraysize);
 void DRW_shgroup_uniform_vec3(DRWShadingGroup *shgroup, const char *name, const float *value, int arraysize);
@@ -527,6 +506,7 @@ typedef struct DRWContextState {
 
 	struct RenderEngineType *engine_type;
 
+	EvaluationContext eval_ctx;
 	struct Depsgraph *depsgraph;
 
 	eObjectMode object_mode;
diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c
index 6b1a3356b51637ba26a7e2668546875163c3cf3b..301a39d053f5942d5f24fbba1f82d186b6a677d9 100644
--- a/source/blender/draw/intern/draw_cache.c
+++ b/source/blender/draw/intern/draw_cache.c
@@ -32,6 +32,8 @@
 #include "DNA_modifier_types.h"
 #include "DNA_lattice_types.h"
 
+#include "UI_resources.h"
+
 #include "BLI_utildefines.h"
 #include "BLI_math.h"
 
@@ -43,6 +45,7 @@
 /* Batch's only (free'd as an array) */
 static struct DRWShapeCache {
 	Gwn_Batch *drw_single_vertice;
+	Gwn_Batch *drw_cursor;
 	Gwn_Batch *drw_fullscreen_quad;
 	Gwn_Batch *drw_quad;
 	Gwn_Batch *drw_sphere;
@@ -2570,9 +2573,9 @@ Gwn_Batch *DRW_cache_particles_get_hair(ParticleSystem *psys, ModifierData *md)
 	return DRW_particles_batch_cache_get_hair(psys, md);
 }
 
-Gwn_Batch *DRW_cache_particles_get_dots(ParticleSystem *psys)
+Gwn_Batch *DRW_cache_particles_get_dots(Object *object, ParticleSystem *psys)
 {
-	return DRW_particles_batch_cache_get_dots(psys);
+	return DRW_particles_batch_cache_get_dots(object, psys);
 }
 
 Gwn_Batch *DRW_cache_particles_get_prim(int type)
@@ -2709,3 +2712,91 @@ Gwn_Batch *DRW_cache_particles_get_prim(int type)
 
 	return NULL;
 }
+
+/* 3D cursor */
+Gwn_Batch *DRW_cache_cursor_get(void)
+{
+	if (!SHC.drw_cursor) {
+		const float f5 = 0.25f;
+		const float f10 = 0.5f;
+		const float f20 = 1.0f;
+
+		const int segments = 16;
+		const int vert_ct = segments + 8;
+		const int index_ct = vert_ct + 5;
+
+		unsigned char red[3] = {255, 0, 0};
+		unsigned char white[3] = {255, 255, 255};
+		unsigned char crosshair_color[3];
+		UI_GetThemeColor3ubv(TH_VIEW_OVERLAY, crosshair_color);
+
+		static Gwn_VertFormat format = { 0 };
+		static struct { uint pos, color; } attr_id;
+		if (format.attrib_ct == 0) {
+			attr_id.pos = GWN_vertformat_attr_add(&format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
+			attr_id.color = GWN_vertformat_attr_add(&format, "color", GWN_COMP_U8, 3, GWN_FETCH_INT_TO_FLOAT_UNIT);
+		}
+
+		Gwn_IndexBufBuilder elb;
+		GWN_indexbuf_init_ex(&elb, GWN_PRIM_LINE_STRIP, index_ct, vert_ct, true);
+
+		Gwn_VertBuf *vbo = GWN_vertbuf_create_with_format(&format);
+		GWN_vertbuf_data_alloc(vbo, vert_ct);
+
+		int v = 0;
+		for (int i = 0; i < segments; ++i) {
+			float angle = (float)(2 * M_PI) * ((float)i / (float)segments);
+			float x = f10 * cosf(angle);
+			float y = f10 * sinf(angle);
+
+			if (i % 2 == 0)
+				GWN_vertbuf_attr_set(vbo, attr_id.color, v, red);
+			else
+				GWN_vertbuf_attr_set(vbo, attr_id.color, v, white);
+
+			GWN_vertbuf_attr_set(vbo, attr_id.pos, v, (const float[2]){x, y});
+			GWN_indexbuf_add_generic_vert(&elb, v++);
+		}
+		GWN_indexbuf_add_generic_vert(&elb, 0);
+		GWN_indexbuf_add_primitive_restart(&elb);
+
+		GWN_vertbuf_attr_set(vbo, attr_id.pos, v, (const float[2]){-f20, 0});
+		GWN_vertbuf_attr_set(vbo, attr_id.color, v, crosshair_color);
+		GWN_indexbuf_add_generic_vert(&elb, v++);
+		GWN_vertbuf_attr_set(vbo, attr_id.pos, v, (const float[2]){-f5, 0});
+		GWN_vertbuf_attr_set(vbo, attr_id.color, v, crosshair_color);
+		GWN_indexbuf_add_generic_vert(&elb, v++);
+
+		GWN_indexbuf_add_primitive_restart(&elb);
+
+		GWN_vertbuf_attr_set(vbo, attr_id.pos, v, (const float[2]){+f5, 0});
+		GWN_vertbuf_attr_set(vbo, attr_id.color, v, crosshair_color);
+		GWN_indexbuf_add_generic_vert(&elb, v++);
+		GWN_vertbuf_attr_set(vbo, attr_id.pos, v, (const float[2]){+f20, 0});
+		GWN_vertbuf_attr_set(vbo, attr_id.color, v, crosshair_color);
+		GWN_indexbuf_add_generic_vert(&elb, v++);
+
+		GWN_indexbuf_add_primitive_restart(&elb);
+
+		GWN_vertbuf_attr_set(vbo, attr_id.pos, v, (const float[2]){0, -f20});
+		GWN_vertbuf_attr_set(vbo, attr_id.color, v, crosshair_color);
+		GWN_indexbuf_add_generic_vert(&elb, v++);
+		GWN_vertbuf_attr_set(vbo, attr_id.pos, v, (const float[2]){0, -f5});
+		GWN_vertbuf_attr_set(vbo, attr_id.color, v, crosshair_color);
+		GWN_indexbuf_add_generic_vert(&elb, v++);
+
+		GWN_indexbuf_add_primitive_restart(&elb);
+
+		GWN_vertbuf_attr_set(vbo, attr_id.pos, v, (const float[2]){0, +f5});
+		GWN_vertbuf_attr_set(vbo, attr_id.color, v, crosshair_color);
+		GWN_indexbuf_add_generic_vert(&elb, v++);
+		GWN_vertbuf_attr_set(vbo, attr_id.pos, v, (const float[2]){0, +f20});
+		GWN_vertbuf_attr_set(vbo, attr_id.color, v, crosshair_color);
+		GWN_indexbuf_add_generic_vert(&elb, v++);
+
+		Gwn_IndexBuf *ibo = GWN_indexbuf_build(&elb);
+
+		SHC.drw_cursor = GWN_batch_create_ex(GWN_PRIM_LINE_STRIP, vbo, ibo, GWN_BATCH_OWNS_VBO | GWN_BATCH_OWNS_INDEX);
+	}
+	return SHC.drw_cursor;
+}
\ No newline at end of file
diff --git a/source/blender/draw/intern/draw_cache.h b/source/blender/draw/intern/draw_cache.h
index c039bb8883d62b115f9041a10d6d4730ada9b720..2ef57884a44ebbf8a6d8058af16ef585d4c7cb21 100644
--- a/source/blender/draw/intern/draw_cache.h
+++ b/source/blender/draw/intern/draw_cache.h
@@ -33,6 +33,9 @@ struct ModifierData;
 
 void DRW_shape_cache_free(void);
 
+/* 3D cursor */
+struct Gwn_Batch *DRW_cache_cursor_get(void);
+
 /* Common Shapes */
 struct Gwn_Batch *DRW_cache_fullscreen_quad_get(void);
 struct Gwn_Batch *DRW_cache_quad_get(void);
@@ -162,7 +165,7 @@ struct Gwn_Batch *DRW_cache_lattice_vert_overlay_get(struct Object *ob);
 
 /* Particles */
 struct Gwn_Batch *DRW_cache_particles_get_hair(struct ParticleSystem *psys, struct ModifierData *md);
-struct Gwn_Batch *DRW_cache_particles_get_dots(struct ParticleSystem *psys);
+struct Gwn_Batch *DRW_cache_particles_get_dots(struct Object *object, struct ParticleSystem *psys);
 struct Gwn_Batch *DRW_cache_particles_get_prim(int type);
 
 /* Metaball */
diff --git a/source/blender/draw/intern/draw_cache_impl.h b/source/blender/draw/intern/draw_cache_impl.h
index 0b18f3a257fe917fb19864e942b85efec51cf2c9..83cc87307b571304dc208f4b80adcf1508077b7d 100644
--- a/source/blender/draw/intern/draw_cache_impl.h
+++ b/source/blender/draw/intern/draw_cache_impl.h
@@ -122,6 +122,6 @@ void DRW_mesh_cache_sculpt_coords_ensure(struct Mesh *me);
 
 /* Particles */
 struct Gwn_Batch *DRW_particles_batch_cache_get_hair(struct ParticleSystem *psys, struct ModifierData *md);
-struct Gwn_Batch *DRW_particles_batch_cache_get_dots(struct ParticleSystem *psys);
+struct Gwn_Batch *DRW_particles_batch_cache_get_dots(struct Object *object, struct ParticleSystem *psys);
 
 #endif /* __DRAW_CACHE_IMPL_H__ */
diff --git a/source/blender/draw/intern/draw_cache_impl_particles.c b/source/blender/draw/intern/draw_cache_impl_particles.c
index da0cc457b990933f1dba9c1082d62dca2edc9a1a..55d528e49b04381dc6e97e2ecc52c3329dc3cf33 100644
--- a/source/blender/draw/intern/draw_cache_impl_particles.c
+++ b/source/blender/draw/intern/draw_cache_impl_particles.c
@@ -29,6 +29,8 @@
  * \brief Particle API for render engines
  */
 
+#include "DRW_render.h"
+
 #include "MEM_guardedalloc.h"
 
 #include "BLI_utildefines.h"
@@ -176,128 +178,114 @@ static void ensure_seg_pt_count(ParticleSystem *psys, ParticleBatchCache *cache)
 /* Gwn_Batch cache usage. */
 static void particle_batch_cache_ensure_pos_and_seg(ParticleSystem *psys, ModifierData *md, ParticleBatchCache *cache)
 {
-	if (cache->pos == NULL || cache->segments == NULL) {
-		int curr_point = 0;
-		ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md;
-
-		GWN_VERTBUF_DISCARD_SAFE(cache->pos);
-		GWN_INDEXBUF_DISCARD_SAFE(cache->segments);
-
-		static Gwn_VertFormat format = { 0 };
-		static struct { uint pos, tan, ind; } attr_id;
-		unsigned int *uv_id = NULL;
-		int uv_layers = 0;
-		MTFace **mtfaces = NULL;
-		float (**parent_uvs)[2] = NULL;
-		bool simple = psys->part->childtype == PART_CHILD_PARTICLES;
-
-		if (psmd) {
-			if (CustomData_has_layer(&psmd->dm_final->loopData, CD_MLOOPUV)) {
-				uv_layers = CustomData_number_of_layers(&psmd->dm_final->loopData, CD_MLOOPUV);
-			}
+	if (cache->pos != NULL && cache->segments != NULL) {
+		return;
+	}
+
+	int curr_point = 0;
+	ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md;
+
+	GWN_VERTBUF_DISCARD_SAFE(cache->pos);
+	GWN_INDEXBUF_DISCARD_SAFE(cache->segments);
+
+	static Gwn_VertFormat format = { 0 };
+	static struct { uint pos, tan, ind; } attr_id;
+	unsigned int *uv_id = NULL;
+	int uv_layers = 0;
+	MTFace **mtfaces = NULL;
+	float (**parent_uvs)[2] = NULL;
+	bool simple = psys->part->childtype == PART_CHILD_PARTICLES;
+
+	if (psmd) {
+		if (CustomData_has_layer(&psmd->dm_final->loopData, CD_MLOOPUV)) {
+			uv_layers = CustomData_number_of_layers(&psmd->dm_final->loopData, CD_MLOOPUV);
 		}
+	}
 
-		GWN_vertformat_clear(&format);
+	GWN_vertformat_clear(&format);
 
-		/* initialize vertex format */
-		attr_id.pos = GWN_vertformat_attr_add(&format, "pos", GWN_COMP_F32, 3, GWN_FETCH_FLOAT);
-		attr_id.tan = GWN_vertformat_attr_add(&format, "nor", GWN_COMP_F32, 3, GWN_FETCH_FLOAT);
-		attr_id.ind = GWN_vertformat_attr_add(&format, "ind", GWN_COMP_I32, 1, GWN_FETCH_INT);
+	/* initialize vertex format */
+	attr_id.pos = GWN_vertformat_attr_add(&format, "pos", GWN_COMP_F32, 3, GWN_FETCH_FLOAT);
+	attr_id.tan = GWN_vertformat_attr_add(&format, "nor", GWN_COMP_F32, 3, GWN_FETCH_FLOAT);
+	attr_id.ind = GWN_vertformat_attr_add(&format, "ind", GWN_COMP_I32, 1, GWN_FETCH_INT);
 
-		if (psmd) {
-			uv_id = MEM_mallocN(sizeof(*uv_id) * uv_layers, "UV attrib format");
+	if (psmd) {
+		uv_id = MEM_mallocN(sizeof(*uv_id) * uv_layers, "UV attrib format");
 
-			for (int i = 0; i < uv_layers; i++) {
-				const char *name = CustomData_get_layer_name(&psmd->dm_final->loopData, CD_MLOOPUV, i);
-				char uuid[32];
+		for (int i = 0; i < uv_layers; i++) {
+			const char *name = CustomData_get_layer_name(&psmd->dm_final->loopData, CD_MLOOPUV, i);
+			char uuid[32];
 
-				BLI_snprintf(uuid, sizeof(uuid), "u%u", BLI_ghashutil_strhash_p(name));
-				uv_id[i] = GWN_vertformat_attr_add(&format, uuid, GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
-			}
+			BLI_snprintf(uuid, sizeof(uuid), "u%u", BLI_ghashutil_strhash_p(name));
+			uv_id[i] = GWN_vertformat_attr_add(&format, uuid, GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
 		}
+	}
 
-		cache->pos = GWN_vertbuf_create_with_format(&format);
-		GWN_vertbuf_data_alloc(cache->pos, cache->point_count);
+	cache->pos = GWN_vertbuf_create_with_format(&format);
+	GWN_vertbuf_data_alloc(cache->pos, cache->point_count);
 
-		Gwn_IndexBufBuilder elb;
-		GWN_indexbuf_init(&elb, GWN_PRIM_LINES, cache->segment_count, cache->point_count);
+	Gwn_IndexBufBuilder elb;
+	GWN_indexbuf_init(&elb, GWN_PRIM_LINES, cache->segment_count, cache->point_count);
 
-		if (uv_layers) {
-			DM_ensure_tessface(psmd->dm_final);
+	if (uv_layers) {
+		DM_ensure_tessface(psmd->dm_final);
 
-			mtfaces = MEM_mallocN(sizeof(*mtfaces) * uv_layers, "Faces UV layers");
+		mtfaces = MEM_mallocN(sizeof(*mtfaces) * uv_layers, "Faces UV layers");
 
-			for (int i = 0; i < uv_layers; i++) {
-				mtfaces[i] = (MTFace *)CustomData_get_layer_n(&psmd->dm_final->faceData, CD_MTFACE, i);
-			}
+		for (int i = 0; i < uv_layers; i++) {
+			mtfaces[i] = (MTFace *)CustomData_get_layer_n(&psmd->dm_final->faceData, CD_MTFACE, i);
 		}
+	}
 
-		if (psys->pathcache && (!psys->childcache || (psys->part->draw & PART_DRAW_PARENT))) {
-			if (simple) {
-				parent_uvs = MEM_callocN(sizeof(*parent_uvs) * psys->totpart, "Parent particle UVs");
-			}
+	if (psys->pathcache && (!psys->childcache || (psys->part->draw & PART_DRAW_PARENT))) {
+		if (simple) {
+			parent_uvs = MEM_callocN(sizeof(*parent_uvs) * psys->totpart, "Parent particle UVs");
+		}
 
-			for (int i = 0; i < psys->totpart; i++) {
-				ParticleCacheKey *path = psys->pathcache[i];
+		for (int i = 0; i < psys->totpart; i++) {
+			ParticleCacheKey *path = psys->pathcache[i];
 
-				if (path->segments > 0) {
-					float tangent[3];
-					int from = psmd ? psmd->psys->part->from : 0;
-					float (*uv)[2] = NULL;
+			if (path->segments > 0) {
+				float tangent[3];
+				int from = psmd ? psmd->psys->part->from : 0;
+				float (*uv)[2] = NULL;
 
-					if (psmd) {
-						uv = MEM_callocN(sizeof(*uv) * uv_layers, "Particle UVs");
+				if (psmd) {
+					uv = MEM_callocN(sizeof(*uv) * uv_layers, "Particle UVs");
 
-						if (simple) {
-							parent_uvs[i] = uv;
-						}
+					if (simple) {
+						parent_uvs[i] = uv;
 					}
+				}
 
-					if (ELEM(from, PART_FROM_FACE, PART_FROM_VOLUME)) {
-						ParticleData *particle = &psys->particles[i];
-						int num = particle->num_dmcache;
-
-						if (num == DMCACHE_NOTFOUND) {
-							if (particle->num < psmd->dm_final->getNumTessFaces(psmd->dm_final)) {
-								num = particle->num;
-							}
-						}
-
-						if (num != DMCACHE_NOTFOUND) {
-							MFace *mface = psmd->dm_final->getTessFaceData(psmd->dm_final, num, CD_MFACE);
+				if (ELEM(from, PART_FROM_FACE, PART_FROM_VOLUME)) {
+					ParticleData *particle = &psys->particles[i];
+					int num = particle->num_dmcache;
 
-							for (int j = 0; j < uv_layers; j++) {
-								psys_interpolate_uvs(mtfaces[j] + num, mface->v4, particle->fuv, uv[j]);
-							}
+					if (num == DMCACHE_NOTFOUND) {
+						if (particle->num < psmd->dm_final->getNumTessFaces(psmd->dm_final)) {
+							num = particle->num;
 						}
 					}
 
-					for (int j = 0; j < path->segments; j++) {
-						if (j == 0) {
-							sub_v3_v3v3(tangent, path[j + 1].co, path[j].co);
-						}
-						else {
-							sub_v3_v3v3(tangent, path[j + 1].co, path[j - 1].co);
-						}
+					if (num != DMCACHE_NOTFOUND) {
+						MFace *mface = psmd->dm_final->getTessFaceData(psmd->dm_final, num, CD_MFACE);
 
-						GWN_vertbuf_attr_set(cache->pos, attr_id.pos, curr_point, path[j].co);
-						GWN_vertbuf_attr_set(cache->pos, attr_id.tan, curr_point, tangent);
-						GWN_vertbuf_attr_set(cache->pos, attr_id.ind, curr_point, &i);
-
-						if (psmd) {
-							for (int k = 0; k < uv_layers; k++) {
-								GWN_vertbuf_attr_set(cache->pos, uv_id[k], curr_point, uv[k]);
-							}
+						for (int j = 0; j < uv_layers; j++) {
+							psys_interpolate_uvs(mtfaces[j] + num, mface->v4, particle->fuv, uv[j]);
 						}
-
-						GWN_indexbuf_add_line_verts(&elb, curr_point, curr_point + 1);
-
-						curr_point++;
 					}
+				}
 
-					sub_v3_v3v3(tangent, path[path->segments].co, path[path->segments - 1].co);
+				for (int j = 0; j < path->segments; j++) {
+					if (j == 0) {
+						sub_v3_v3v3(tangent, path[j + 1].co, path[j].co);
+					}
+					else {
+						sub_v3_v3v3(tangent, path[j + 1].co, path[j - 1].co);
+					}
 
-					GWN_vertbuf_attr_set(cache->pos, attr_id.pos, curr_point, path[path->segments].co);
+					GWN_vertbuf_attr_set(cache->pos, attr_id.pos, curr_point, path[j].co);
 					GWN_vertbuf_attr_set(cache->pos, attr_id.tan, curr_point, tangent);
 					GWN_vertbuf_attr_set(cache->pos, attr_id.ind, curr_point, &i);
 
@@ -305,102 +293,101 @@ static void particle_batch_cache_ensure_pos_and_seg(ParticleSystem *psys, Modifi
 						for (int k = 0; k < uv_layers; k++) {
 							GWN_vertbuf_attr_set(cache->pos, uv_id[k], curr_point, uv[k]);
 						}
-
-						if (!simple) {
-							MEM_freeN(uv);
-						}
 					}
 
+					GWN_indexbuf_add_line_verts(&elb, curr_point, curr_point + 1);
+
 					curr_point++;
 				}
-			}
-		}
 
-		if (psys->childcache) {
-			int child_count = psys->totchild * psys->part->disp / 100;
+				sub_v3_v3v3(tangent, path[path->segments].co, path[path->segments - 1].co);
 
-			if (simple && !parent_uvs) {
-				parent_uvs = MEM_callocN(sizeof(*parent_uvs) * psys->totpart, "Parent particle UVs");
-			}
+				GWN_vertbuf_attr_set(cache->pos, attr_id.pos, curr_point, path[path->segments].co);
+				GWN_vertbuf_attr_set(cache->pos, attr_id.tan, curr_point, tangent);
+				GWN_vertbuf_attr_set(cache->pos, attr_id.ind, curr_point, &i);
 
-			for (int i = 0, x = psys->totpart; i < child_count; i++, x++) {
-				ParticleCacheKey *path = psys->childcache[i];
-				float tangent[3];
-
-				if (path->segments > 0) {
-					int from = psmd ? psmd->psys->part->from : 0;
-					float (*uv)[2] = NULL;
+				if (psmd) {
+					for (int k = 0; k < uv_layers; k++) {
+						GWN_vertbuf_attr_set(cache->pos, uv_id[k], curr_point, uv[k]);
+					}
 
 					if (!simple) {
-						if (psmd) {
-							uv = MEM_callocN(sizeof(*uv) * uv_layers, "Particle UVs");
-						}
+						MEM_freeN(uv);
+					}
+				}
 
-						if (ELEM(from, PART_FROM_FACE, PART_FROM_VOLUME)) {
-							ChildParticle *particle = &psys->child[i];
-							int num = particle->num;
+				curr_point++;
+			}
+		}
+	}
 
-							if (num != DMCACHE_NOTFOUND) {
-								MFace *mface = psmd->dm_final->getTessFaceData(psmd->dm_final, num, CD_MFACE);
+	if (psys->childcache) {
+		int child_count = psys->totchild * psys->part->disp / 100;
 
-								for (int j = 0; j < uv_layers; j++) {
-									psys_interpolate_uvs(mtfaces[j] + num, mface->v4, particle->fuv, uv[j]);
-								}
-							}
-						}
-					}
-					else if (!parent_uvs[psys->child[i].parent]) {
-						if (psmd) {
-							parent_uvs[psys->child[i].parent] = MEM_callocN(sizeof(*uv) * uv_layers, "Particle UVs");
-						}
+		if (simple && !parent_uvs) {
+			parent_uvs = MEM_callocN(sizeof(*parent_uvs) * psys->totpart, "Parent particle UVs");
+		}
 
-						if (ELEM(from, PART_FROM_FACE, PART_FROM_VOLUME)) {
-							ParticleData *particle = &psys->particles[psys->child[i].parent];
-							int num = particle->num_dmcache;
+		for (int i = 0, x = psys->totpart; i < child_count; i++, x++) {
+			ParticleCacheKey *path = psys->childcache[i];
+			float tangent[3];
 
-							if (num == DMCACHE_NOTFOUND) {
-								if (particle->num < psmd->dm_final->getNumTessFaces(psmd->dm_final)) {
-									num = particle->num;
-								}
-							}
+			if (path->segments > 0) {
+				int from = psmd ? psmd->psys->part->from : 0;
+				float (*uv)[2] = NULL;
+
+				if (!simple) {
+					if (psmd) {
+						uv = MEM_callocN(sizeof(*uv) * uv_layers, "Particle UVs");
+					}
+
+					if (ELEM(from, PART_FROM_FACE, PART_FROM_VOLUME)) {
+						ChildParticle *particle = &psys->child[i];
+						int num = particle->num;
 
-							if (num != DMCACHE_NOTFOUND) {
-								MFace *mface = psmd->dm_final->getTessFaceData(psmd->dm_final, num, CD_MFACE);
+						if (num != DMCACHE_NOTFOUND) {
+							MFace *mface = psmd->dm_final->getTessFaceData(psmd->dm_final, num, CD_MFACE);
 
-								for (int j = 0; j < uv_layers; j++) {
-									psys_interpolate_uvs(mtfaces[j] + num, mface->v4, particle->fuv, parent_uvs[psys->child[i].parent][j]);
-								}
+							for (int j = 0; j < uv_layers; j++) {
+								psys_interpolate_uvs(mtfaces[j] + num, mface->v4, particle->fuv, uv[j]);
 							}
 						}
 					}
+				}
+				else if (!parent_uvs[psys->child[i].parent]) {
+					if (psmd) {
+						parent_uvs[psys->child[i].parent] = MEM_callocN(sizeof(*uv) * uv_layers, "Particle UVs");
+					}
 
-					for (int j = 0; j < path->segments; j++) {
-						if (j == 0) {
-							sub_v3_v3v3(tangent, path[j + 1].co, path[j].co);
-						}
-						else {
-							sub_v3_v3v3(tangent, path[j + 1].co, path[j - 1].co);
-						}
-
-						GWN_vertbuf_attr_set(cache->pos, attr_id.pos, curr_point, path[j].co);
-						GWN_vertbuf_attr_set(cache->pos, attr_id.tan, curr_point, tangent);
-						GWN_vertbuf_attr_set(cache->pos, attr_id.ind, curr_point, &x);
+					if (ELEM(from, PART_FROM_FACE, PART_FROM_VOLUME)) {
+						ParticleData *particle = &psys->particles[psys->child[i].parent];
+						int num = particle->num_dmcache;
 
-						if (psmd) {
-							for (int k = 0; k < uv_layers; k++) {
-								GWN_vertbuf_attr_set(cache->pos, uv_id[k], curr_point,
-								                     simple ? parent_uvs[psys->child[i].parent][k] : uv[k]);
+						if (num == DMCACHE_NOTFOUND) {
+							if (particle->num < psmd->dm_final->getNumTessFaces(psmd->dm_final)) {
+								num = particle->num;
 							}
 						}
 
-						GWN_indexbuf_add_line_verts(&elb, curr_point, curr_point + 1);
+						if (num != DMCACHE_NOTFOUND) {
+							MFace *mface = psmd->dm_final->getTessFaceData(psmd->dm_final, num, CD_MFACE);
 
-						curr_point++;
+							for (int j = 0; j < uv_layers; j++) {
+								psys_interpolate_uvs(mtfaces[j] + num, mface->v4, particle->fuv, parent_uvs[psys->child[i].parent][j]);
+							}
+						}
 					}
+				}
 
-					sub_v3_v3v3(tangent, path[path->segments].co, path[path->segments - 1].co);
+				for (int j = 0; j < path->segments; j++) {
+					if (j == 0) {
+						sub_v3_v3v3(tangent, path[j + 1].co, path[j].co);
+					}
+					else {
+						sub_v3_v3v3(tangent, path[j + 1].co, path[j - 1].co);
+					}
 
-					GWN_vertbuf_attr_set(cache->pos, attr_id.pos, curr_point, path[path->segments].co);
+					GWN_vertbuf_attr_set(cache->pos, attr_id.pos, curr_point, path[j].co);
 					GWN_vertbuf_attr_set(cache->pos, attr_id.tan, curr_point, tangent);
 					GWN_vertbuf_attr_set(cache->pos, attr_id.ind, curr_point, &x);
 
@@ -409,88 +396,125 @@ static void particle_batch_cache_ensure_pos_and_seg(ParticleSystem *psys, Modifi
 							GWN_vertbuf_attr_set(cache->pos, uv_id[k], curr_point,
 							                     simple ? parent_uvs[psys->child[i].parent][k] : uv[k]);
 						}
-
-						if (!simple) {
-							MEM_freeN(uv);
-						}
 					}
 
+					GWN_indexbuf_add_line_verts(&elb, curr_point, curr_point + 1);
+
 					curr_point++;
 				}
-			}
-		}
 
-		if (parent_uvs) {
-			for (int i = 0; i < psys->totpart; i++) {
-				MEM_SAFE_FREE(parent_uvs[i]);
-			}
+				sub_v3_v3v3(tangent, path[path->segments].co, path[path->segments - 1].co);
 
-			MEM_freeN(parent_uvs);
-		}
+				GWN_vertbuf_attr_set(cache->pos, attr_id.pos, curr_point, path[path->segments].co);
+				GWN_vertbuf_attr_set(cache->pos, attr_id.tan, curr_point, tangent);
+				GWN_vertbuf_attr_set(cache->pos, attr_id.ind, curr_point, &x);
+
+				if (psmd) {
+					for (int k = 0; k < uv_layers; k++) {
+						GWN_vertbuf_attr_set(cache->pos, uv_id[k], curr_point,
+						                     simple ? parent_uvs[psys->child[i].parent][k] : uv[k]);
+					}
+
+					if (!simple) {
+						MEM_freeN(uv);
+					}
+				}
 
-		if (uv_layers) {
-			MEM_freeN(mtfaces);
+				curr_point++;
+			}
 		}
+	}
 
-		if (psmd) {
-			MEM_freeN(uv_id);
+	if (parent_uvs) {
+		for (int i = 0; i < psys->totpart; i++) {
+			MEM_SAFE_FREE(parent_uvs[i]);
 		}
 
-		cache->segments = GWN_indexbuf_build(&elb);
+		MEM_freeN(parent_uvs);
+	}
+
+	if (uv_layers) {
+		MEM_freeN(mtfaces);
+	}
+
+	if (psmd) {
+		MEM_freeN(uv_id);
 	}
+
+	cache->segments = GWN_indexbuf_build(&elb);
 }
 
-static void particle_batch_cache_ensure_pos(ParticleSystem *psys, ParticleBatchCache *cache)
+static void particle_batch_cache_ensure_pos(Object *object, ParticleSystem *psys, ParticleBatchCache *cache)
 {
-	if (cache->pos == NULL) {
-		static Gwn_VertFormat format = { 0 };
-		static unsigned pos_id, rot_id, val_id;
-		int i, curr_point;
-		ParticleData *pa;
-
-		GWN_VERTBUF_DISCARD_SAFE(cache->pos);
-		GWN_INDEXBUF_DISCARD_SAFE(cache->segments);
-
-		if (format.attrib_ct == 0) {
-			/* initialize vertex format */
-			pos_id = GWN_vertformat_attr_add(&format, "pos", GWN_COMP_F32, 3, GWN_FETCH_FLOAT);
-			rot_id = GWN_vertformat_attr_add(&format, "rot", GWN_COMP_F32, 4, GWN_FETCH_FLOAT);
-			val_id = GWN_vertformat_attr_add(&format, "val", GWN_COMP_F32, 1, GWN_FETCH_FLOAT);
+	if (cache->pos != NULL) {
+		return;
+	}
+
+	static Gwn_VertFormat format = { 0 };
+	static unsigned pos_id, rot_id, val_id;
+	int i, curr_point;
+	ParticleData *pa;
+	ParticleKey state;
+	ParticleSimulationData sim = {NULL};
+	const DRWContextState *draw_ctx = DRW_context_state_get();
+
+	sim.eval_ctx = &draw_ctx->eval_ctx;
+	sim.scene = draw_ctx->scene;
+	sim.ob = object;
+	sim.psys = psys;
+	sim.psmd = psys_get_modifier(object, psys);
+
+	if (psys->part->phystype == PART_PHYS_KEYED) {
+		if (psys->flag & PSYS_KEYED) {
+			psys_count_keyed_targets(&sim);
+			if (psys->totkeyed == 0)
+				return;
 		}
+	}
 
-		cache->pos = GWN_vertbuf_create_with_format(&format);
-		GWN_vertbuf_data_alloc(cache->pos, psys->totpart);
-
-		for (curr_point = 0, i = 0, pa = psys->particles; i < psys->totpart; i++, pa++) {
-			if (pa->state.time >= pa->time && pa->state.time < pa->dietime &&
-			    !(pa->flag & (PARS_NO_DISP | PARS_UNEXIST)))
-			{
-				float val;
-
-				GWN_vertbuf_attr_set(cache->pos, pos_id, curr_point, pa->state.co);
-				GWN_vertbuf_attr_set(cache->pos, rot_id, curr_point, pa->state.rot);
-
-				switch (psys->part->draw_col) {
-					case PART_DRAW_COL_VEL:
-						val = len_v3(pa->state.vel) / psys->part->color_vec_max;
-						break;
-					case PART_DRAW_COL_ACC:
-						val = len_v3v3(pa->state.vel, pa->prev_state.vel) / ((pa->state.time - pa->prev_state.time) * psys->part->color_vec_max);
-						break;
-					default:
-						val = -1.0f;
-						break;
-				}
+	GWN_VERTBUF_DISCARD_SAFE(cache->pos);
+	GWN_INDEXBUF_DISCARD_SAFE(cache->segments);
 
-				GWN_vertbuf_attr_set(cache->pos, val_id, curr_point, &val);
+	if (format.attrib_ct == 0) {
+		/* initialize vertex format */
+		pos_id = GWN_vertformat_attr_add(&format, "pos", GWN_COMP_F32, 3, GWN_FETCH_FLOAT);
+		rot_id = GWN_vertformat_attr_add(&format, "rot", GWN_COMP_F32, 4, GWN_FETCH_FLOAT);
+		val_id = GWN_vertformat_attr_add(&format, "val", GWN_COMP_F32, 1, GWN_FETCH_FLOAT);
+	}
 
-				curr_point++;
-			}
+	cache->pos = GWN_vertbuf_create_with_format(&format);
+	GWN_vertbuf_data_alloc(cache->pos, psys->totpart);
+
+	for (curr_point = 0, i = 0, pa = psys->particles; i < psys->totpart; i++, pa++) {
+		state.time = draw_ctx->eval_ctx.ctime;
+		if (!psys_get_particle_state(&sim, curr_point, &state, 0)) {
+			continue;
 		}
 
-		if (curr_point != psys->totpart) {
-			GWN_vertbuf_data_resize(cache->pos, curr_point);
+		float val;
+
+		GWN_vertbuf_attr_set(cache->pos, pos_id, curr_point, pa->state.co);
+		GWN_vertbuf_attr_set(cache->pos, rot_id, curr_point, pa->state.rot);
+
+		switch (psys->part->draw_col) {
+			case PART_DRAW_COL_VEL:
+				val = len_v3(pa->state.vel) / psys->part->color_vec_max;
+				break;
+			case PART_DRAW_COL_ACC:
+				val = len_v3v3(pa->state.vel, pa->prev_state.vel) / ((pa->state.time - pa->prev_state.time) * psys->part->color_vec_max);
+				break;
+			default:
+				val = -1.0f;
+				break;
 		}
+
+		GWN_vertbuf_attr_set(cache->pos, val_id, curr_point, &val);
+
+		curr_point++;
+	}
+
+	if (curr_point != psys->totpart) {
+		GWN_vertbuf_data_resize(cache->pos, curr_point);
 	}
 }
 
@@ -507,12 +531,12 @@ Gwn_Batch *DRW_particles_batch_cache_get_hair(ParticleSystem *psys, ModifierData
 	return cache->hairs;
 }
 
-Gwn_Batch *DRW_particles_batch_cache_get_dots(ParticleSystem *psys)
+Gwn_Batch *DRW_particles_batch_cache_get_dots(Object *object, ParticleSystem *psys)
 {
 	ParticleBatchCache *cache = particle_batch_cache_get(psys);
 
 	if (cache->hairs == NULL) {
-		particle_batch_cache_ensure_pos(psys, cache);
+		particle_batch_cache_ensure_pos(object, psys, cache);
 		cache->hairs = GWN_batch_create(GWN_PRIM_POINTS, cache->pos, NULL);
 	}
 
diff --git a/source/blender/draw/intern/draw_common.c b/source/blender/draw/intern/draw_common.c
index 1299b1b757bace1713c44cd00214aa503f795209..8d23688959c34bd154536290e94ba829fb186370 100644
--- a/source/blender/draw/intern/draw_common.c
+++ b/source/blender/draw/intern/draw_common.c
@@ -426,7 +426,7 @@ DRWShadingGroup *shgroup_instance_mball_handles(DRWPass *pass, struct Gwn_Batch
 	GPUShader *sh = GPU_shader_get_builtin_shader(GPU_SHADER_3D_INSTANCE_MBALL_HANDLES);
 
 	DRW_shgroup_instance_format(g_formats.instance_mball_handles, {
-		{"ScaleTranslationMatrix" , DRW_ATTRIB_FLOAT, 16},
+		{"ScaleTranslationMatrix" , DRW_ATTRIB_FLOAT, 12},
 		{"radius"                 , DRW_ATTRIB_FLOAT, 1},
 		{"color"                  , DRW_ATTRIB_FLOAT, 3}
 	});
diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c
index dbcfa02c555713346f0f44285e19a30a0c223c99..15b0c793a50cf0088d3b208fb3c02133bdceede4 100644
--- a/source/blender/draw/intern/draw_manager.c
+++ b/source/blender/draw/intern/draw_manager.c
@@ -31,6 +31,8 @@
 #include "BLI_string.h"
 #include "BLI_threads.h"
 
+#include "BLF_api.h"
+
 #include "BKE_global.h"
 #include "BKE_mesh.h"
 #include "BKE_object.h"
@@ -92,6 +94,22 @@ ListBase DRW_engines = {NULL, NULL};
 
 extern struct GPUUniformBuffer *view_ubo; /* draw_manager_exec.c */
 
+static void drw_state_prepare_clean_for_draw(DRWManager *dst)
+{
+	memset(dst, 0x0, offsetof(DRWManager, ogl_context));
+}
+
+/* This function is used to reset draw manager to a state
+ * where we don't re-use data by accident across different
+ * draw calls.
+ */
+#ifdef DEBUG
+static void drw_state_ensure_not_reused(DRWManager *dst)
+{
+	memset(dst, 0xff, offsetof(DRWManager, ogl_context));
+}
+#endif
+
 /* -------------------------------------------------------------------- */
 
 void DRW_draw_callbacks_pre_scene(void)
@@ -332,10 +350,27 @@ static void drw_viewport_cache_resize(void)
 	DRW_instance_data_list_resize(DST.idatalist);
 }
 
+static void drw_state_eval_ctx_init(DRWManager *dst)
+{
+	DRWContextState *draw_ctx = &dst->draw_ctx;
+	DEG_evaluation_context_init_from_scene(
+	        &draw_ctx->eval_ctx,
+	        draw_ctx->scene,
+	        draw_ctx->view_layer,
+	        draw_ctx->engine_type,
+	        DST.options.is_scene_render ? DAG_EVAL_RENDER : DAG_EVAL_VIEWPORT);
+}
 
 /* Not a viewport variable, we could split this out. */
 static void drw_context_state_init(void)
 {
+	if (DST.draw_ctx.obact) {
+		DST.draw_ctx.object_mode = DST.draw_ctx.obact->mode;
+	}
+	else {
+		DST.draw_ctx.object_mode = OB_MODE_OBJECT;
+	}
+
 	/* Edit object. */
 	if (DST.draw_ctx.object_mode & OB_MODE_EDIT) {
 		DST.draw_ctx.object_edit = DST.draw_ctx.obact;
@@ -354,6 +389,8 @@ static void drw_context_state_init(void)
 	else {
 		DST.draw_ctx.object_pose = NULL;
 	}
+
+	drw_state_eval_ctx_init(&DST);
 }
 
 /* It also stores viewport variable to an immutable place: DST
@@ -452,7 +489,7 @@ static void drw_viewport_var_init(void)
 		DST.RST.bound_ubos = MEM_callocN(sizeof(GPUUniformBuffer *) * GPU_max_ubo_binds(), "Bound GPUUniformBuffer refs");
 	}
 	if (DST.RST.bound_ubo_slots == NULL) {
-		DST.RST.bound_ubo_slots = MEM_callocN(sizeof(char) * GPU_max_textures(), "Bound Ubo Slots");
+		DST.RST.bound_ubo_slots = MEM_callocN(sizeof(char) * GPU_max_ubo_binds(), "Bound Ubo Slots");
 	}
 
 	if (view_ubo == NULL) {
@@ -744,6 +781,10 @@ static void drw_engines_draw_scene(void)
 		if (engine->draw_scene) {
 			DRW_stats_group_start(engine->idname);
 			engine->draw_scene(data);
+			/* Restore for next engine */
+			if (DRW_state_is_fbo()) {
+				GPU_framebuffer_bind(DST.default_framebuffer);
+			}
 			DRW_stats_group_end();
 		}
 
@@ -998,12 +1039,14 @@ void DRW_notify_view_update(const DRWUpdateContext *update_ctx)
 	BLI_mutex_lock(&DST.ogl_context_mutex);
 
 	/* Reset before using it. */
-	memset(&DST, 0x0, offsetof(DRWManager, ogl_context));
+	drw_state_prepare_clean_for_draw(&DST);
 
 	DST.viewport = rv3d->viewport;
 	DST.draw_ctx = (DRWContextState){
-		ar, rv3d, v3d, scene, view_layer, OBACT(view_layer), engine_type, depsgraph, OB_MODE_OBJECT,
-		NULL,
+		.ar = ar, .rv3d = rv3d, .v3d = v3d,
+		.scene = scene, .view_layer = view_layer, .obact = OBACT(view_layer),
+		.engine_type = engine_type,
+		.depsgraph = depsgraph, .object_mode = OB_MODE_OBJECT,
 	};
 
 	drw_engines_enable(view_layer, engine_type);
@@ -1048,10 +1091,13 @@ void DRW_notify_id_update(const DRWUpdateContext *update_ctx, ID *id)
 		return;
 	}
 	/* Reset before using it. */
-	memset(&DST, 0x0, offsetof(DRWManager, ogl_context));
+	drw_state_prepare_clean_for_draw(&DST);
 	DST.viewport = rv3d->viewport;
 	DST.draw_ctx = (DRWContextState){
-		ar, rv3d, v3d, scene, view_layer, OBACT(view_layer), engine_type, depsgraph, OB_MODE_OBJECT, NULL,
+		.ar = ar, .rv3d = rv3d, .v3d = v3d,
+		.scene = scene, .view_layer = view_layer, .obact = OBACT(view_layer),
+		.engine_type = engine_type,
+		.depsgraph = depsgraph, .object_mode = OB_MODE_OBJECT,
 	};
 	drw_engines_enable(view_layer, engine_type);
 	for (LinkData *link = DST.enabled_engines.first; link; link = link->next) {
@@ -1084,8 +1130,8 @@ void DRW_draw_view(const bContext *C)
 	View3D *v3d = CTX_wm_view3d(C);
 
 	/* Reset before using it. */
-	memset(&DST, 0x0, offsetof(DRWManager, ogl_context));
-	DRW_draw_render_loop_ex(eval_ctx.depsgraph, engine_type, ar, v3d, eval_ctx.object_mode, C);
+	drw_state_prepare_clean_for_draw(&DST);
+	DRW_draw_render_loop_ex(eval_ctx.depsgraph, engine_type, ar, v3d, C);
 }
 
 /**
@@ -1095,7 +1141,7 @@ void DRW_draw_view(const bContext *C)
 void DRW_draw_render_loop_ex(
         struct Depsgraph *depsgraph,
         RenderEngineType *engine_type,
-        ARegion *ar, View3D *v3d, const eObjectMode object_mode,
+        ARegion *ar, View3D *v3d,
         const bContext *evil_C)
 {
 
@@ -1112,10 +1158,13 @@ void DRW_draw_render_loop_ex(
 	GPU_viewport_engines_data_validate(DST.viewport, DRW_engines_get_hash());
 
 	DST.draw_ctx = (DRWContextState){
-	    ar, rv3d, v3d, scene, view_layer, OBACT(view_layer), engine_type, depsgraph, object_mode,
+	    .ar = ar, .rv3d = rv3d, .v3d = v3d,
+	    .scene = scene, .view_layer = view_layer, .obact = OBACT(view_layer),
+	    .engine_type = engine_type,
+	    .depsgraph = depsgraph,
 
 	    /* reuse if caller sets */
-	    DST.draw_ctx.evil_C,
+	    .evil_C = DST.draw_ctx.evil_C,
 	};
 	drw_context_state_init();
 	drw_viewport_var_init();
@@ -1126,6 +1175,9 @@ void DRW_draw_render_loop_ex(
 	/* Update ubos */
 	DRW_globals_update();
 
+	/* No framebuffer allowed before drawing. */
+	BLI_assert(GPU_framebuffer_current_get() == 0);
+
 	/* Init engines */
 	drw_engines_init();
 
@@ -1152,6 +1204,8 @@ void DRW_draw_render_loop_ex(
 
 	DRW_stats_begin();
 
+	GPU_framebuffer_bind(DST.default_framebuffer);
+
 	/* Start Drawing */
 	DRW_state_reset();
 
@@ -1217,6 +1271,8 @@ void DRW_draw_render_loop_ex(
 		glEnable(GL_DEPTH_TEST);
 	}
 
+	GPU_framebuffer_restore();
+
 	DRW_state_reset();
 	drw_engines_disable();
 
@@ -1224,27 +1280,27 @@ void DRW_draw_render_loop_ex(
 
 #ifdef DEBUG
 	/* Avoid accidental reuse. */
-	memset(&DST, 0xFF, offsetof(DRWManager, ogl_context));
+	drw_state_ensure_not_reused(&DST);
 #endif
 }
 
 void DRW_draw_render_loop(
         struct Depsgraph *depsgraph,
-        ARegion *ar, View3D *v3d, const eObjectMode object_mode)
+        ARegion *ar, View3D *v3d)
 {
 	/* Reset before using it. */
-	memset(&DST, 0x0, offsetof(DRWManager, ogl_context));
+	drw_state_prepare_clean_for_draw(&DST);
 
 	Scene *scene = DEG_get_evaluated_scene(depsgraph);
 	RenderEngineType *engine_type = RE_engines_find(scene->view_render.engine_id);
 
-	DRW_draw_render_loop_ex(depsgraph, engine_type, ar, v3d, object_mode, NULL);
+	DRW_draw_render_loop_ex(depsgraph, engine_type, ar, v3d, NULL);
 }
 
 /* @viewport CAN be NULL, in this case we create one. */
 void DRW_draw_render_loop_offscreen(
         struct Depsgraph *depsgraph, RenderEngineType *engine_type,
-        ARegion *ar, View3D *v3d, const eObjectMode object_mode,
+        ARegion *ar, View3D *v3d,
         const bool draw_background, GPUOffScreen *ofs,
         GPUViewport *viewport)
 {
@@ -1262,11 +1318,13 @@ void DRW_draw_render_loop_offscreen(
 		}
 	}
 
+	GPU_framebuffer_restore();
+
 	/* Reset before using it. */
-	memset(&DST, 0x0, offsetof(DRWManager, ogl_context));
+	drw_state_prepare_clean_for_draw(&DST);
 	DST.options.is_image_render = true;
 	DST.options.draw_background = draw_background;
-	DRW_draw_render_loop_ex(depsgraph, engine_type, ar, v3d, object_mode, NULL);
+	DRW_draw_render_loop_ex(depsgraph, engine_type, ar, v3d, NULL);
 
 	/* restore */
 	{
@@ -1298,13 +1356,15 @@ void DRW_render_to_image(RenderEngine *engine, struct Depsgraph *depsgraph)
 	 * multiple threads. */
 
 	/* Reset before using it. */
-	memset(&DST, 0x0, offsetof(DRWManager, ogl_context));
+	drw_state_prepare_clean_for_draw(&DST);
 	DST.options.is_image_render = true;
 	DST.options.is_scene_render = true;
 	DST.options.draw_background = scene->r.alphamode == R_ADDSKY;
 
 	DST.draw_ctx = (DRWContextState){
-	    NULL, NULL, NULL, scene, view_layer, NULL, engine_type, depsgraph, OB_MODE_OBJECT, NULL,
+	    .scene = scene, .view_layer = view_layer,
+	    .engine_type = engine_type,
+	    .depsgraph = depsgraph, .object_mode = OB_MODE_OBJECT,
 	};
 	drw_context_state_init();
 
@@ -1317,8 +1377,6 @@ void DRW_render_to_image(RenderEngine *engine, struct Depsgraph *depsgraph)
 	ViewportEngineData *data = drw_viewport_engine_data_ensure(draw_engine_type);
 
 	/* set default viewport */
-	gpuPushAttrib(GPU_ENABLE_BIT | GPU_VIEWPORT_BIT);
-	glDisable(GL_SCISSOR_TEST);
 	glViewport(0, 0, size[0], size[1]);
 
 	/* Main rendering. */
@@ -1357,16 +1415,6 @@ void DRW_render_to_image(RenderEngine *engine, struct Depsgraph *depsgraph)
 	/* TODO grease pencil */
 
 	GPU_viewport_free(DST.viewport);
-
-	DRW_state_reset();
-	/* FIXME GL_DEPTH_TEST is enabled by default but it seems
-	 * to trigger some bad behaviour / artifacts if it's turned
-	 * on at this point. */
-	glDisable(GL_DEPTH_TEST);
-
-	/* Restore Drawing area. */
-	gpuPopAttrib();
-	glEnable(GL_SCISSOR_TEST);
 	GPU_framebuffer_restore();
 
 	/* Changing Context */
@@ -1374,7 +1422,7 @@ void DRW_render_to_image(RenderEngine *engine, struct Depsgraph *depsgraph)
 
 #ifdef DEBUG
 	/* Avoid accidental reuse. */
-	memset(&DST, 0xFF, offsetof(DRWManager, ogl_context));
+	drw_state_ensure_not_reused(&DST);
 #endif
 }
 
@@ -1434,7 +1482,7 @@ void DRW_render_instance_buffer_finish(void)
  */
 void DRW_draw_select_loop(
         struct Depsgraph *depsgraph,
-        ARegion *ar, View3D *v3d, const eObjectMode object_mode,
+        ARegion *ar, View3D *v3d,
         bool UNUSED(use_obedit_skip), bool UNUSED(use_nearest), const rcti *rect,
         DRW_SelectPassFn select_pass_fn, void *select_pass_user_data)
 {
@@ -1442,13 +1490,14 @@ void DRW_draw_select_loop(
 	RenderEngineType *engine_type = RE_engines_find(scene->view_render.engine_id);
 	ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph);
 	Object *obact = OBACT(view_layer);
+	Object *obedit = OBEDIT_FROM_OBACT(obact);
 #ifndef USE_GPU_SELECT
 	UNUSED_VARS(vc, scene, view_layer, v3d, ar, rect);
 #else
 	RegionView3D *rv3d = ar->regiondata;
 
 	/* Reset before using it. */
-	memset(&DST, 0x0, offsetof(DRWManager, ogl_context));
+	drw_state_prepare_clean_for_draw(&DST);
 
 	/* backup (_never_ use rv3d->viewport) */
 	void *backup_viewport = rv3d->viewport;
@@ -1456,31 +1505,23 @@ void DRW_draw_select_loop(
 
 	bool use_obedit = false;
 	int obedit_mode = 0;
-	if (object_mode & OB_MODE_EDIT) {
-		if (obact->type == OB_MBALL) {
+	if (obedit != NULL) {
+		if (obedit->type == OB_MBALL) {
 			use_obedit = true;
 			obedit_mode = CTX_MODE_EDIT_METABALL;
 		}
-		else if (obact->type == OB_ARMATURE) {
+		else if (obedit->type == OB_ARMATURE) {
 			use_obedit = true;
 			obedit_mode = CTX_MODE_EDIT_ARMATURE;
 		}
 	}
 
-	gpuPushAttrib(GPU_ENABLE_BIT | GPU_VIEWPORT_BIT);
-	glDisable(GL_SCISSOR_TEST);
-
 	struct GPUViewport *viewport = GPU_viewport_create();
 	GPU_viewport_size_set(viewport, (const int[2]){BLI_rcti_size_x(rect), BLI_rcti_size_y(rect)});
 
 	DST.viewport = viewport;
 	v3d->zbuf = true;
 
-	/* Setup framebuffer */
-	draw_select_framebuffer_setup(rect);
-	GPU_framebuffer_bind(g_select_buffer.framebuffer);
-	DRW_framebuffer_clear(false, true, false, NULL, 1.0f);
-
 	DST.options.is_select = true;
 
 	/* Get list of enabled engines */
@@ -1496,8 +1537,10 @@ void DRW_draw_select_loop(
 
 	/* Instead of 'DRW_context_state_init(C, &DST.draw_ctx)', assign from args */
 	DST.draw_ctx = (DRWContextState){
-		ar, rv3d, v3d, scene, view_layer, obact, engine_type, depsgraph, object_mode,
-		(bContext *)NULL,
+		.ar = ar, .rv3d = rv3d, .v3d = v3d,
+		.scene = scene, .view_layer = view_layer, .obact = obact,
+		.engine_type = engine_type,
+		.depsgraph = depsgraph,
 	};
 	drw_context_state_init();
 	drw_viewport_var_init();
@@ -1534,11 +1577,15 @@ void DRW_draw_select_loop(
 		DRW_render_instance_buffer_finish();
 	}
 
+	/* Setup framebuffer */
+	draw_select_framebuffer_setup(rect);
+	GPU_framebuffer_bind(g_select_buffer.framebuffer);
+	GPU_framebuffer_clear_depth(g_select_buffer.framebuffer, 1.0f);
+
 	/* Start Drawing */
 	DRW_state_reset();
 	DRW_draw_callbacks_pre_scene();
 
-
 	DRW_state_lock(
 	        DRW_STATE_WRITE_DEPTH |
 	        DRW_STATE_DEPTH_ALWAYS |
@@ -1569,17 +1616,13 @@ void DRW_draw_select_loop(
 
 #ifdef DEBUG
 	/* Avoid accidental reuse. */
-	memset(&DST, 0xFF, offsetof(DRWManager, ogl_context));
+	drw_state_ensure_not_reused(&DST);
 #endif
 	GPU_framebuffer_restore();
 
 	/* Cleanup for selection state */
 	GPU_viewport_free(viewport);
 
-	/* Restore Drawing area. */
-	gpuPopAttrib();
-	glEnable(GL_SCISSOR_TEST);
-
 	/* restore */
 	rv3d->viewport = backup_viewport;
 #endif  /* USE_GPU_SELECT */
@@ -1626,7 +1669,7 @@ static void draw_depth_texture_to_screen(GPUTexture *texture)
  */
 void DRW_draw_depth_loop(
         Depsgraph *depsgraph,
-        ARegion *ar, View3D *v3d, const eObjectMode object_mode)
+        ARegion *ar, View3D *v3d)
 {
 	Scene *scene = DEG_get_evaluated_scene(depsgraph);
 	RenderEngineType *engine_type = RE_engines_find(scene->view_render.engine_id);
@@ -1640,10 +1683,7 @@ void DRW_draw_depth_loop(
 	rv3d->viewport = NULL;
 
 	/* Reset before using it. */
-	memset(&DST, 0x0, offsetof(DRWManager, ogl_context));
-
-	gpuPushAttrib(GPU_ENABLE_BIT | GPU_VIEWPORT_BIT);
-	glDisable(GL_SCISSOR_TEST);
+	drw_state_prepare_clean_for_draw(&DST);
 
 	struct GPUViewport *viewport = GPU_viewport_create();
 	GPU_viewport_size_set(viewport, (const int[2]){ar->winx, ar->winy});
@@ -1651,7 +1691,7 @@ void DRW_draw_depth_loop(
 	/* Setup framebuffer */
 	draw_select_framebuffer_setup(&ar->winrct);
 	GPU_framebuffer_bind(g_select_buffer.framebuffer);
-	DRW_framebuffer_clear(false, true, false, NULL, 1.0f);
+	GPU_framebuffer_clear_depth(g_select_buffer.framebuffer, 1.0f);
 
 	bool cache_is_dirty;
 	DST.viewport = viewport;
@@ -1670,8 +1710,10 @@ void DRW_draw_depth_loop(
 
 	/* Instead of 'DRW_context_state_init(C, &DST.draw_ctx)', assign from args */
 	DST.draw_ctx = (DRWContextState){
-		ar, rv3d, v3d, scene, view_layer, OBACT(view_layer), engine_type, depsgraph, object_mode,
-		(bContext *)NULL,
+		.ar = ar, .rv3d = rv3d, .v3d = v3d,
+		.scene = scene, .view_layer = view_layer, .obact = OBACT(view_layer),
+		.engine_type = engine_type,
+		.depsgraph = depsgraph,
 	};
 	drw_context_state_init();
 	drw_viewport_var_init();
@@ -1710,7 +1752,7 @@ void DRW_draw_depth_loop(
 
 #ifdef DEBUG
 	/* Avoid accidental reuse. */
-	memset(&DST, 0xFF, offsetof(DRWManager, ogl_context));
+	drw_state_ensure_not_reused(&DST);
 #endif
 
 	/* TODO: Reading depth for operators should be done here. */
@@ -1720,10 +1762,6 @@ void DRW_draw_depth_loop(
 	/* Cleanup for selection state */
 	GPU_viewport_free(viewport);
 
-	/* Restore Drawing area. */
-	gpuPopAttrib();
-	glEnable(GL_SCISSOR_TEST);
-
 	/* Changin context */
 	DRW_opengl_context_disable();
 
@@ -1945,7 +1983,7 @@ void DRW_engines_free(void)
 	DRW_opengl_context_enable();
 
 	DRW_TEXTURE_FREE_SAFE(g_select_buffer.texture_depth);
-	DRW_FRAMEBUFFER_FREE_SAFE(g_select_buffer.framebuffer);
+	GPU_FRAMEBUFFER_FREE_SAFE(g_select_buffer.framebuffer);
 
 	DRW_shape_cache_free();
 	DRW_stats_free();
@@ -2027,6 +2065,7 @@ void DRW_opengl_context_enable(void)
 		GWN_context_active_set(DST.gwn_context);
 		if (BLI_thread_is_main()) {
 			immActivate();
+			BLF_batch_reset();
 		}
 	}
 }
diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h
index c7e9b3dc08de3bd1f69197fee1ae21960d338538..dd7e84f67d40d032fc5503429cc78f6c3a1bdd8b 100644
--- a/source/blender/draw/intern/draw_manager.h
+++ b/source/blender/draw/intern/draw_manager.h
@@ -160,7 +160,7 @@ typedef enum {
 	DRW_UNIFORM_FLOAT,
 	DRW_UNIFORM_TEXTURE,
 	DRW_UNIFORM_TEXTURE_PERSIST,
-	DRW_UNIFORM_BUFFER,
+	DRW_UNIFORM_TEXTURE_REF,
 	DRW_UNIFORM_BLOCK,
 	DRW_UNIFORM_BLOCK_PERSIST
 } DRWUniformType;
diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c
index b1db3b63777a0ca00a1a0ef202c5536f71816d9b..ae7854b436cbb39e527d8b663435e54c216684b1 100644
--- a/source/blender/draw/intern/draw_manager_data.c
+++ b/source/blender/draw/intern/draw_manager_data.c
@@ -72,7 +72,7 @@ void DRW_uniformbuffer_free(GPUUniformBuffer *ubo)
 /** \name Uniforms (DRW_shgroup_uniform)
  * \{ */
 
-static void drw_interface_uniform_create_ex(DRWShadingGroup *shgroup, int loc,
+static void drw_shgroup_uniform_create_ex(DRWShadingGroup *shgroup, int loc,
                                             DRWUniformType type, const void *value, int length, int arraysize)
 {
 	DRWUniform *uni = BLI_mempool_alloc(DST.vmempool->uniforms);
@@ -85,17 +85,17 @@ static void drw_interface_uniform_create_ex(DRWShadingGroup *shgroup, int loc,
 	BLI_LINKS_PREPEND(shgroup->uniforms, uni);
 }
 
-static void drw_interface_builtin_uniform(
+static void drw_shgroup_builtin_uniform(
         DRWShadingGroup *shgroup, int builtin, const void *value, int length, int arraysize)
 {
 	int loc = GPU_shader_get_builtin_uniform(shgroup->shader, builtin);
 
 	if (loc != -1) {
-		drw_interface_uniform_create_ex(shgroup, loc, DRW_UNIFORM_FLOAT, value, length, arraysize);
+		drw_shgroup_uniform_create_ex(shgroup, loc, DRW_UNIFORM_FLOAT, value, length, arraysize);
 	}
 }
 
-static void drw_interface_uniform(DRWShadingGroup *shgroup, const char *name,
+static void drw_shgroup_uniform(DRWShadingGroup *shgroup, const char *name,
                                   DRWUniformType type, const void *value, int length, int arraysize)
 {
 	int location;
@@ -117,98 +117,98 @@ static void drw_interface_uniform(DRWShadingGroup *shgroup, const char *name,
 	BLI_assert(arraysize > 0 && arraysize <= 16);
 	BLI_assert(length >= 0 && length <= 16);
 
-	drw_interface_uniform_create_ex(shgroup, location, type, value, length, arraysize);
+	drw_shgroup_uniform_create_ex(shgroup, location, type, value, length, arraysize);
 }
 
 void DRW_shgroup_uniform_texture(DRWShadingGroup *shgroup, const char *name, const GPUTexture *tex)
 {
 	BLI_assert(tex != NULL);
-	drw_interface_uniform(shgroup, name, DRW_UNIFORM_TEXTURE, tex, 0, 1);
+	drw_shgroup_uniform(shgroup, name, DRW_UNIFORM_TEXTURE, tex, 0, 1);
 }
 
 /* Same as DRW_shgroup_uniform_texture but is garanteed to be bound if shader does not change between shgrp. */
 void DRW_shgroup_uniform_texture_persistent(DRWShadingGroup *shgroup, const char *name, const GPUTexture *tex)
 {
 	BLI_assert(tex != NULL);
-	drw_interface_uniform(shgroup, name, DRW_UNIFORM_TEXTURE_PERSIST, tex, 0, 1);
+	drw_shgroup_uniform(shgroup, name, DRW_UNIFORM_TEXTURE_PERSIST, tex, 0, 1);
 }
 
 void DRW_shgroup_uniform_block(DRWShadingGroup *shgroup, const char *name, const GPUUniformBuffer *ubo)
 {
 	BLI_assert(ubo != NULL);
-	drw_interface_uniform(shgroup, name, DRW_UNIFORM_BLOCK, ubo, 0, 1);
+	drw_shgroup_uniform(shgroup, name, DRW_UNIFORM_BLOCK, ubo, 0, 1);
 }
 
 /* Same as DRW_shgroup_uniform_block but is garanteed to be bound if shader does not change between shgrp. */
 void DRW_shgroup_uniform_block_persistent(DRWShadingGroup *shgroup, const char *name, const GPUUniformBuffer *ubo)
 {
 	BLI_assert(ubo != NULL);
-	drw_interface_uniform(shgroup, name, DRW_UNIFORM_BLOCK_PERSIST, ubo, 0, 1);
+	drw_shgroup_uniform(shgroup, name, DRW_UNIFORM_BLOCK_PERSIST, ubo, 0, 1);
 }
 
-void DRW_shgroup_uniform_buffer(DRWShadingGroup *shgroup, const char *name, GPUTexture **tex)
+void DRW_shgroup_uniform_texture_ref(DRWShadingGroup *shgroup, const char *name, GPUTexture **tex)
 {
-	drw_interface_uniform(shgroup, name, DRW_UNIFORM_BUFFER, tex, 0, 1);
+	drw_shgroup_uniform(shgroup, name, DRW_UNIFORM_TEXTURE_REF, tex, 0, 1);
 }
 
 void DRW_shgroup_uniform_bool(DRWShadingGroup *shgroup, const char *name, const int *value, int arraysize)
 {
-	drw_interface_uniform(shgroup, name, DRW_UNIFORM_BOOL, value, 1, arraysize);
+	drw_shgroup_uniform(shgroup, name, DRW_UNIFORM_BOOL, value, 1, arraysize);
 }
 
 void DRW_shgroup_uniform_float(DRWShadingGroup *shgroup, const char *name, const float *value, int arraysize)
 {
-	drw_interface_uniform(shgroup, name, DRW_UNIFORM_FLOAT, value, 1, arraysize);
+	drw_shgroup_uniform(shgroup, name, DRW_UNIFORM_FLOAT, value, 1, arraysize);
 }
 
 void DRW_shgroup_uniform_vec2(DRWShadingGroup *shgroup, const char *name, const float *value, int arraysize)
 {
-	drw_interface_uniform(shgroup, name, DRW_UNIFORM_FLOAT, value, 2, arraysize);
+	drw_shgroup_uniform(shgroup, name, DRW_UNIFORM_FLOAT, value, 2, arraysize);
 }
 
 void DRW_shgroup_uniform_vec3(DRWShadingGroup *shgroup, const char *name, const float *value, int arraysize)
 {
-	drw_interface_uniform(shgroup, name, DRW_UNIFORM_FLOAT, value, 3, arraysize);
+	drw_shgroup_uniform(shgroup, name, DRW_UNIFORM_FLOAT, value, 3, arraysize);
 }
 
 void DRW_shgroup_uniform_vec4(DRWShadingGroup *shgroup, const char *name, const float *value, int arraysize)
 {
-	drw_interface_uniform(shgroup, name, DRW_UNIFORM_FLOAT, value, 4, arraysize);
+	drw_shgroup_uniform(shgroup, name, DRW_UNIFORM_FLOAT, value, 4, arraysize);
 }
 
 void DRW_shgroup_uniform_short_to_int(DRWShadingGroup *shgroup, const char *name, const short *value, int arraysize)
 {
-	drw_interface_uniform(shgroup, name, DRW_UNIFORM_SHORT_TO_INT, value, 1, arraysize);
+	drw_shgroup_uniform(shgroup, name, DRW_UNIFORM_SHORT_TO_INT, value, 1, arraysize);
 }
 
 void DRW_shgroup_uniform_short_to_float(DRWShadingGroup *shgroup, const char *name, const short *value, int arraysize)
 {
-	drw_interface_uniform(shgroup, name, DRW_UNIFORM_SHORT_TO_FLOAT, value, 1, arraysize);
+	drw_shgroup_uniform(shgroup, name, DRW_UNIFORM_SHORT_TO_FLOAT, value, 1, arraysize);
 }
 
 void DRW_shgroup_uniform_int(DRWShadingGroup *shgroup, const char *name, const int *value, int arraysize)
 {
-	drw_interface_uniform(shgroup, name, DRW_UNIFORM_INT, value, 1, arraysize);
+	drw_shgroup_uniform(shgroup, name, DRW_UNIFORM_INT, value, 1, arraysize);
 }
 
 void DRW_shgroup_uniform_ivec2(DRWShadingGroup *shgroup, const char *name, const int *value, int arraysize)
 {
-	drw_interface_uniform(shgroup, name, DRW_UNIFORM_INT, value, 2, arraysize);
+	drw_shgroup_uniform(shgroup, name, DRW_UNIFORM_INT, value, 2, arraysize);
 }
 
 void DRW_shgroup_uniform_ivec3(DRWShadingGroup *shgroup, const char *name, const int *value, int arraysize)
 {
-	drw_interface_uniform(shgroup, name, DRW_UNIFORM_INT, value, 3, arraysize);
+	drw_shgroup_uniform(shgroup, name, DRW_UNIFORM_INT, value, 3, arraysize);
 }
 
 void DRW_shgroup_uniform_mat3(DRWShadingGroup *shgroup, const char *name, const float *value)
 {
-	drw_interface_uniform(shgroup, name, DRW_UNIFORM_FLOAT, value, 9, 1);
+	drw_shgroup_uniform(shgroup, name, DRW_UNIFORM_FLOAT, value, 9, 1);
 }
 
 void DRW_shgroup_uniform_mat4(DRWShadingGroup *shgroup, const char *name, const float *value)
 {
-	drw_interface_uniform(shgroup, name, DRW_UNIFORM_FLOAT, value, 16, 1);
+	drw_shgroup_uniform(shgroup, name, DRW_UNIFORM_FLOAT, value, 16, 1);
 }
 
 /** \} */
@@ -218,8 +218,9 @@ void DRW_shgroup_uniform_mat4(DRWShadingGroup *shgroup, const char *name, const
 /** \name Draw Call (DRW_calls)
  * \{ */
 
-static void drw_call_calc_orco(ID *ob_data, float (*r_orcofacs)[3])
+static void drw_call_calc_orco(Object *ob, float (*r_orcofacs)[3])
 {
+	ID *ob_data = (ob) ? ob->data : NULL;
 	float *texcoloc = NULL;
 	float *texcosize = NULL;
 	if (ob_data != NULL) {
@@ -297,7 +298,7 @@ static DRWCallState *drw_call_state_create(DRWShadingGroup *shgroup, float (*obm
 
 	/* Orco factors: We compute this at creation to not have to save the *ob_data */
 	if ((state->matflag & DRW_CALL_ORCOTEXFAC) != 0) {
-		drw_call_calc_orco(ob->data, state->orcotexfac);
+		drw_call_calc_orco(ob, state->orcotexfac);
 		state->matflag &= ~DRW_CALL_ORCOTEXFAC;
 	}
 
@@ -458,7 +459,7 @@ void DRW_shgroup_call_dynamic_add_array(DRWShadingGroup *shgroup, const void *at
 /** \name Shading Groups (DRW_shgroup)
  * \{ */
 
-static void drw_interface_init(DRWShadingGroup *shgroup, GPUShader *shader)
+static void drw_shgroup_init(DRWShadingGroup *shgroup, GPUShader *shader)
 {
 	shgroup->instance_geom = NULL;
 	shgroup->instance_vbo = NULL;
@@ -475,17 +476,17 @@ static void drw_interface_init(DRWShadingGroup *shgroup, GPUShader *shader)
 	int view_ubo_location = GPU_shader_get_uniform_block(shader, "viewBlock");
 
 	if (view_ubo_location != -1) {
-		drw_interface_uniform_create_ex(shgroup, view_ubo_location, DRW_UNIFORM_BLOCK_PERSIST, view_ubo, 0, 1);
+		drw_shgroup_uniform_create_ex(shgroup, view_ubo_location, DRW_UNIFORM_BLOCK_PERSIST, view_ubo, 0, 1);
 	}
 	else {
 		/* Only here to support builtin shaders. This should not be used by engines. */
-		drw_interface_builtin_uniform(shgroup, GWN_UNIFORM_VIEW, DST.view_data.matstate.mat[DRW_MAT_VIEW], 16, 1);
-		drw_interface_builtin_uniform(shgroup, GWN_UNIFORM_VIEW_INV, DST.view_data.matstate.mat[DRW_MAT_VIEWINV], 16, 1);
-		drw_interface_builtin_uniform(shgroup, GWN_UNIFORM_VIEWPROJECTION, DST.view_data.matstate.mat[DRW_MAT_PERS], 16, 1);
-		drw_interface_builtin_uniform(shgroup, GWN_UNIFORM_VIEWPROJECTION_INV, DST.view_data.matstate.mat[DRW_MAT_PERSINV], 16, 1);
-		drw_interface_builtin_uniform(shgroup, GWN_UNIFORM_PROJECTION, DST.view_data.matstate.mat[DRW_MAT_WIN], 16, 1);
-		drw_interface_builtin_uniform(shgroup, GWN_UNIFORM_PROJECTION_INV, DST.view_data.matstate.mat[DRW_MAT_WININV], 16, 1);
-		drw_interface_builtin_uniform(shgroup, GWN_UNIFORM_CAMERATEXCO, DST.view_data.viewcamtexcofac, 3, 2);
+		drw_shgroup_builtin_uniform(shgroup, GWN_UNIFORM_VIEW, DST.view_data.matstate.mat[DRW_MAT_VIEW], 16, 1);
+		drw_shgroup_builtin_uniform(shgroup, GWN_UNIFORM_VIEW_INV, DST.view_data.matstate.mat[DRW_MAT_VIEWINV], 16, 1);
+		drw_shgroup_builtin_uniform(shgroup, GWN_UNIFORM_VIEWPROJECTION, DST.view_data.matstate.mat[DRW_MAT_PERS], 16, 1);
+		drw_shgroup_builtin_uniform(shgroup, GWN_UNIFORM_VIEWPROJECTION_INV, DST.view_data.matstate.mat[DRW_MAT_PERSINV], 16, 1);
+		drw_shgroup_builtin_uniform(shgroup, GWN_UNIFORM_PROJECTION, DST.view_data.matstate.mat[DRW_MAT_WIN], 16, 1);
+		drw_shgroup_builtin_uniform(shgroup, GWN_UNIFORM_PROJECTION_INV, DST.view_data.matstate.mat[DRW_MAT_WININV], 16, 1);
+		drw_shgroup_builtin_uniform(shgroup, GWN_UNIFORM_CAMERATEXCO, DST.view_data.viewcamtexcofac, 3, 2);
 	}
 
 	shgroup->model = GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_MODEL);
@@ -517,14 +518,14 @@ static void drw_interface_init(DRWShadingGroup *shgroup, GPUShader *shader)
 		shgroup->matflag |= DRW_CALL_EYEVEC;
 }
 
-static void drw_interface_instance_init(
+static void drw_shgroup_instance_init(
         DRWShadingGroup *shgroup, GPUShader *shader, Gwn_Batch *batch, Gwn_VertFormat *format)
 {
 	BLI_assert(shgroup->type == DRW_SHG_INSTANCE);
 	BLI_assert(batch != NULL);
 	BLI_assert(format != NULL);
 
-	drw_interface_init(shgroup, shader);
+	drw_shgroup_init(shgroup, shader);
 
 	shgroup->instance_geom = batch;
 #ifndef NDEBUG
@@ -535,10 +536,10 @@ static void drw_interface_instance_init(
 	                              &shgroup->instance_geom, &shgroup->instance_vbo);
 }
 
-static void drw_interface_batching_init(
+static void drw_shgroup_batching_init(
         DRWShadingGroup *shgroup, GPUShader *shader, Gwn_VertFormat *format)
 {
-	drw_interface_init(shgroup, shader);
+	drw_shgroup_init(shgroup, shader);
 
 #ifndef NDEBUG
 	shgroup->attribs_count = (format != NULL) ? format->attrib_ct : 0;
@@ -668,7 +669,7 @@ DRWShadingGroup *DRW_shgroup_material_create(
 	DRWShadingGroup *shgroup = drw_shgroup_material_create_ex(gpupass, pass);
 
 	if (shgroup) {
-		drw_interface_init(shgroup, GPU_pass_shader(gpupass));
+		drw_shgroup_init(shgroup, GPU_pass_shader(gpupass));
 		drw_shgroup_material_inputs(shgroup, material);
 	}
 
@@ -684,8 +685,8 @@ DRWShadingGroup *DRW_shgroup_material_instance_create(
 	if (shgroup) {
 		shgroup->type = DRW_SHG_INSTANCE;
 		shgroup->instance_geom = geom;
-		drw_call_calc_orco(ob->data, shgroup->instance_orcofac);
-		drw_interface_instance_init(shgroup, GPU_pass_shader(gpupass), geom, format);
+		drw_call_calc_orco(ob, shgroup->instance_orcofac);
+		drw_shgroup_instance_init(shgroup, GPU_pass_shader(gpupass), geom, format);
 		drw_shgroup_material_inputs(shgroup, material);
 	}
 
@@ -702,8 +703,8 @@ DRWShadingGroup *DRW_shgroup_material_empty_tri_batch_create(
 	DRWShadingGroup *shgroup = drw_shgroup_material_create_ex(gpupass, pass);
 
 	if (shgroup) {
-		/* Calling drw_interface_init will cause it to call GWN_draw_primitive(). */
-		drw_interface_init(shgroup, GPU_pass_shader(gpupass));
+		/* Calling drw_shgroup_init will cause it to call GWN_draw_primitive(). */
+		drw_shgroup_init(shgroup, GPU_pass_shader(gpupass));
 		shgroup->type = DRW_SHG_TRIANGLE_BATCH;
 		shgroup->instance_count = tri_count * 3;
 		drw_shgroup_material_inputs(shgroup, material);
@@ -715,7 +716,7 @@ DRWShadingGroup *DRW_shgroup_material_empty_tri_batch_create(
 DRWShadingGroup *DRW_shgroup_create(struct GPUShader *shader, DRWPass *pass)
 {
 	DRWShadingGroup *shgroup = drw_shgroup_create_ex(shader, pass);
-	drw_interface_init(shgroup, shader);
+	drw_shgroup_init(shgroup, shader);
 	return shgroup;
 }
 
@@ -726,7 +727,7 @@ DRWShadingGroup *DRW_shgroup_instance_create(
 	shgroup->type = DRW_SHG_INSTANCE;
 	shgroup->instance_geom = geom;
 	drw_call_calc_orco(NULL, shgroup->instance_orcofac);
-	drw_interface_instance_init(shgroup, shader, geom, format);
+	drw_shgroup_instance_init(shgroup, shader, geom, format);
 
 	return shgroup;
 }
@@ -738,7 +739,7 @@ DRWShadingGroup *DRW_shgroup_point_batch_create(struct GPUShader *shader, DRWPas
 	DRWShadingGroup *shgroup = drw_shgroup_create_ex(shader, pass);
 	shgroup->type = DRW_SHG_POINT_BATCH;
 
-	drw_interface_batching_init(shgroup, shader, g_pos_format);
+	drw_shgroup_batching_init(shgroup, shader, g_pos_format);
 
 	return shgroup;
 }
@@ -750,7 +751,7 @@ DRWShadingGroup *DRW_shgroup_line_batch_create(struct GPUShader *shader, DRWPass
 	DRWShadingGroup *shgroup = drw_shgroup_create_ex(shader, pass);
 	shgroup->type = DRW_SHG_LINE_BATCH;
 
-	drw_interface_batching_init(shgroup, shader, g_pos_format);
+	drw_shgroup_batching_init(shgroup, shader, g_pos_format);
 
 	return shgroup;
 }
@@ -765,8 +766,8 @@ DRWShadingGroup *DRW_shgroup_empty_tri_batch_create(struct GPUShader *shader, DR
 #endif
 	DRWShadingGroup *shgroup = drw_shgroup_create_ex(shader, pass);
 
-	/* Calling drw_interface_init will cause it to call GWN_draw_primitive(). */
-	drw_interface_init(shgroup, shader);
+	/* Calling drw_shgroup_init will cause it to call GWN_draw_primitive(). */
+	drw_shgroup_init(shgroup, shader);
 
 	shgroup->type = DRW_SHG_TRIANGLE_BATCH;
 	shgroup->instance_count = tri_count * 3;
diff --git a/source/blender/draw/intern/draw_manager_exec.c b/source/blender/draw/intern/draw_manager_exec.c
index e90c1f63ecd280575b84eaa70eea1a7343300400..e69a102681537c988f2e32b7210a4155774ac79b 100644
--- a/source/blender/draw/intern/draw_manager_exec.c
+++ b/source/blender/draw/intern/draw_manager_exec.c
@@ -384,6 +384,102 @@ void DRW_state_clip_planes_reset(void)
 /** \name Clipping (DRW_clipping)
  * \{ */
 
+/* Extract the 8 corners (world space).
+ * Although less accurate, this solution can be simplified as follows:
+ *
+ * BKE_boundbox_init_from_minmax(&bbox, (const float[3]){-1.0f, -1.0f, -1.0f}, (const float[3]){1.0f, 1.0f, 1.0f});
+ * for (int i = 0; i < 8; i++) {mul_project_m4_v3(viewprojinv, bbox.vec[i]);}
+ */
+static void draw_frustum_boundbox_calc(const float (*projmat)[4], const float (*viewinv)[4], BoundBox *r_bbox)
+{
+	float screenvecs[3][3], loc[3], near, far, w_half, h_half;
+	bool is_persp = projmat[3][3] == 0.0f;
+	copy_m3_m4(screenvecs, viewinv);
+	copy_v3_v3(loc, viewinv[3]);
+
+	/* get the values of the minimum and maximum clipping planes distances
+	 * and half the width and height of the nearplane rectangle. */
+	if (is_persp) {
+		near = projmat[3][2] / (projmat[2][2] - 1.0f);
+		far = projmat[3][2] / (projmat[2][2] + 1.0f);
+		w_half = near / projmat[0][0];
+		h_half = near / projmat[1][1];
+	}
+	else {
+		near = (projmat[3][2] + 1.0f) / projmat[2][2];
+		far = (projmat[3][2] - 1.0f) / projmat[2][2];
+		w_half = 1.0f / projmat[0][0];
+		h_half = 1.0f / projmat[1][1];
+	}
+
+	/* With vectors aligned to the screen, reconstruct
+	 * the near plane from the dimensions obtained earlier. */
+	float mid[3], hor[3], ver[3];
+	mul_v3_v3fl(hor, screenvecs[0], w_half);
+	mul_v3_v3fl(ver, screenvecs[1], h_half);
+	madd_v3_v3v3fl(mid, loc, screenvecs[2], -near);
+
+	/* The case below is for non-symmetric frustum. */
+	if (is_persp) {
+		madd_v3_v3fl(mid, hor, projmat[2][0]);
+		madd_v3_v3fl(mid, ver, projmat[2][1]);
+	}
+	else {
+		madd_v3_v3fl(mid, hor, projmat[3][0]);
+		madd_v3_v3fl(mid, ver, projmat[3][1]);
+	}
+
+	r_bbox->vec[0][0] = mid[0] - ver[0] - hor[0];
+	r_bbox->vec[0][1] = mid[1] - ver[1] - hor[1];
+	r_bbox->vec[0][2] = mid[2] - ver[2] - hor[2];
+
+	r_bbox->vec[3][0] = mid[0] + ver[0] - hor[0];
+	r_bbox->vec[3][1] = mid[1] + ver[1] - hor[1];
+	r_bbox->vec[3][2] = mid[2] + ver[2] - hor[2];
+
+	r_bbox->vec[7][0] = mid[0] + ver[0] + hor[0];
+	r_bbox->vec[7][1] = mid[1] + ver[1] + hor[1];
+	r_bbox->vec[7][2] = mid[2] + ver[2] + hor[2];
+
+	r_bbox->vec[4][0] = mid[0] - ver[0] + hor[0];
+	r_bbox->vec[4][1] = mid[1] - ver[1] + hor[1];
+	r_bbox->vec[4][2] = mid[2] - ver[2] + hor[2];
+
+	/* Get the coordinates of the far plane. */
+	if (is_persp) {
+		float sca_far = far / near;
+		mid[0] = mid[0] + (mid[0] - loc[0]) * sca_far;
+		mid[1] = mid[1] + (mid[1] - loc[1]) * sca_far;
+		mid[2] = mid[2] + (mid[2] - loc[2]) * sca_far;
+
+		mul_v3_fl(hor, sca_far);
+		mul_v3_fl(ver, sca_far);
+	}
+	else {
+		madd_v3_v3v3fl(mid, loc, screenvecs[2], -far);
+
+		/* Non-symmetric frustum. */
+		madd_v3_v3fl(mid, hor, projmat[3][0]);
+		madd_v3_v3fl(mid, ver, projmat[3][1]);
+	}
+
+	r_bbox->vec[1][0] = mid[0] - ver[0] - hor[0];
+	r_bbox->vec[1][1] = mid[1] - ver[1] - hor[1];
+	r_bbox->vec[1][2] = mid[2] - ver[2] - hor[2];
+
+	r_bbox->vec[2][0] = mid[0] + ver[0] - hor[0];
+	r_bbox->vec[2][1] = mid[1] + ver[1] - hor[1];
+	r_bbox->vec[2][2] = mid[2] + ver[2] - hor[2];
+
+	r_bbox->vec[6][0] = mid[0] + ver[0] + hor[0];
+	r_bbox->vec[6][1] = mid[1] + ver[1] + hor[1];
+	r_bbox->vec[6][2] = mid[2] + ver[2] + hor[2];
+
+	r_bbox->vec[5][0] = mid[0] - ver[0] + hor[0];
+	r_bbox->vec[5][1] = mid[1] - ver[1] + hor[1];
+	r_bbox->vec[5][2] = mid[2] - ver[2] + hor[2];
+}
+
 static void draw_clipping_setup_from_view(void)
 {
 	if (DST.clipping.updated)
@@ -396,14 +492,7 @@ static void draw_clipping_setup_from_view(void)
 
 	/* Extract Clipping Planes */
 	BoundBox bbox;
-	BKE_boundbox_init_from_minmax(&bbox, (const float[3]){-1.0f, -1.0f, -1.0f}, (const float[3]){1.0f, 1.0f, 1.0f});
-
-	/* Extract the 8 corners (world space). */
-	for (int i = 0; i < 8; i++) {
-		/* Use separate matrix mul for more precision. */
-		mul_project_m4_v3(projinv, bbox.vec[i]);
-		mul_m4_v3(viewinv, bbox.vec[i]);
-	}
+	draw_frustum_boundbox_calc(projmat, viewinv, &bbox);
 
 	/* Compute clip planes using the world space frustum corners. */
 	for (int p = 0; p < 6; p++) {
@@ -748,7 +837,7 @@ static void bind_ubo(GPUUniformBuffer *ubo, char bind_type)
 				}
 				GPU_uniformbuffer_bind(ubo, index);
 				DST.RST.bound_ubos[index] = ubo;
-				slot_flags[bind_num] = bind_type;
+				slot_flags[index] = bind_type;
 				return;
 			}
 		}
@@ -772,6 +861,10 @@ static void release_texture_slots(bool with_persist)
 				DST.RST.bound_tex_slots[i] = BIND_NONE;
 		}
 	}
+
+	/* Reset so that slots are consistenly assigned for different shader
+	 * draw calls, to avoid shader specialization/patching by the driver. */
+	DST.RST.bind_tex_inc = 0;
 }
 
 static void release_ubo_slots(bool with_persist)
@@ -785,6 +878,10 @@ static void release_ubo_slots(bool with_persist)
 				DST.RST.bound_ubo_slots[i] = BIND_NONE;
 		}
 	}
+
+	/* Reset so that slots are consistenly assigned for different shader
+	 * draw calls, to avoid shader specialization/patching by the driver. */
+	DST.RST.bind_ubo_inc = 0;
 }
 
 static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state)
@@ -844,10 +941,7 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state)
 				bind_texture(tex, BIND_PERSIST);
 				GPU_shader_uniform_texture(shgroup->shader, uni->location, tex);
 				break;
-			case DRW_UNIFORM_BUFFER:
-				if (!DRW_state_is_fbo()) {
-					break;
-				}
+			case DRW_UNIFORM_TEXTURE_REF:
 				tex = *((GPUTexture **)uni->value);
 				BLI_assert(tex);
 				bind_texture(tex, BIND_TEMP);
diff --git a/source/blender/draw/intern/draw_manager_framebuffer.c b/source/blender/draw/intern/draw_manager_framebuffer.c
deleted file mode 100644
index a76b1c42a538b94b18e4c602014f729b46847a70..0000000000000000000000000000000000000000
--- a/source/blender/draw/intern/draw_manager_framebuffer.c
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright 2016, Blender Foundation.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * Contributor(s): Blender Institute
- *
- */
-
-/** \file blender/draw/intern/draw_manager_framebuffer.c
- *  \ingroup draw
- */
-
-#include "draw_manager.h"
-
-GPUFrameBuffer *DRW_framebuffer_create(void)
-{
-	return GPU_framebuffer_create();
-}
-
-void DRW_framebuffer_init(
-        GPUFrameBuffer **fb, void *engine_type, int width, int height,
-        DRWFboTexture textures[MAX_FBO_TEX], int textures_len)
-{
-	BLI_assert(textures_len <= MAX_FBO_TEX);
-	BLI_assert(width > 0 && height > 0);
-
-	bool create_fb = false;
-	int color_attachment = -1;
-
-	if (!*fb) {
-		*fb = GPU_framebuffer_create();
-		create_fb = true;
-	}
-
-	for (int i = 0; i < textures_len; ++i) {
-		int channels;
-		bool is_depth;
-		bool create_tex = false;
-		GPUTextureFormat gpu_format;
-
-		DRWFboTexture fbotex = textures[i];
-		bool is_temp = (fbotex.flag & DRW_TEX_TEMP) != 0;
-
-		drw_texture_get_format(fbotex.format, true, &gpu_format, &channels, &is_depth);
-
-		if (!*fbotex.tex || is_temp) {
-			/* Temp textures need to be queried each frame, others not. */
-			if (is_temp) {
-				*fbotex.tex = GPU_viewport_texture_pool_query(
-				        DST.viewport, engine_type, width, height, channels, gpu_format);
-			}
-			else {
-				*fbotex.tex = GPU_texture_create_2D_custom(
-				        width, height, channels, gpu_format, NULL, NULL);
-				create_tex = true;
-			}
-		}
-
-		if (!is_depth) {
-			++color_attachment;
-		}
-
-		if (create_fb || create_tex) {
-			drw_texture_set_parameters(*fbotex.tex, fbotex.flag);
-			GPU_framebuffer_texture_attach(*fb, *fbotex.tex, color_attachment, 0);
-		}
-	}
-
-	if (create_fb && (textures_len > 0)) {
-		if (!GPU_framebuffer_check_valid(*fb, NULL)) {
-			printf("Error invalid framebuffer\n");
-		}
-
-		/* Detach temp textures */
-		for (int i = 0; i < textures_len; ++i) {
-			DRWFboTexture fbotex = textures[i];
-
-			if ((fbotex.flag & DRW_TEX_TEMP) != 0) {
-				GPU_framebuffer_texture_detach(*fbotex.tex);
-			}
-		}
-
-		if (DST.default_framebuffer != NULL) {
-			GPU_framebuffer_bind(DST.default_framebuffer);
-		}
-	}
-}
-
-void DRW_framebuffer_free(GPUFrameBuffer *fb)
-{
-	GPU_framebuffer_free(fb);
-}
-
-void DRW_framebuffer_bind(GPUFrameBuffer *fb)
-{
-	GPU_framebuffer_bind(fb);
-}
-
-void DRW_framebuffer_clear(bool color, bool depth, bool stencil, float clear_col[4], float clear_depth)
-{
-	if (color) {
-		glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
-		glClearColor(clear_col[0], clear_col[1], clear_col[2], clear_col[3]);
-	}
-	if (depth) {
-		glDepthMask(GL_TRUE);
-		glClearDepth(clear_depth);
-	}
-	if (stencil) {
-		glStencilMask(0xFF);
-	}
-	glClear(((color) ? GL_COLOR_BUFFER_BIT : 0) |
-	        ((depth) ? GL_DEPTH_BUFFER_BIT : 0) |
-	        ((stencil) ? GL_STENCIL_BUFFER_BIT : 0));
-}
-
-void DRW_framebuffer_read_data(int x, int y, int w, int h, int channels, int slot, float *data)
-{
-	GLenum type;
-	switch (channels) {
-		case 1: type = GL_RED; break;
-		case 2: type = GL_RG; break;
-		case 3: type = GL_RGB; break;
-		case 4: type = GL_RGBA;	break;
-		default:
-			BLI_assert(false && "wrong number of read channels");
-			return;
-	}
-	glReadBuffer(GL_COLOR_ATTACHMENT0 + slot);
-	glReadPixels(x, y, w, h, type, GL_FLOAT, data);
-}
-
-void DRW_framebuffer_read_depth(int x, int y, int w, int h, float *data)
-{
-	GLenum type = GL_DEPTH_COMPONENT;
-
-	glReadBuffer(GL_COLOR_ATTACHMENT0); /* This is OK! */
-	glReadPixels(x, y, w, h, type, GL_FLOAT, data);
-}
-
-void DRW_framebuffer_texture_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int mip)
-{
-	GPU_framebuffer_texture_attach(fb, tex, slot, mip);
-}
-
-void DRW_framebuffer_texture_layer_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int layer, int mip)
-{
-	GPU_framebuffer_texture_layer_attach(fb, tex, slot, layer, mip);
-}
-
-void DRW_framebuffer_cubeface_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int face, int mip)
-{
-	GPU_framebuffer_texture_cubeface_attach(fb, tex, slot, face, mip);
-}
-
-void DRW_framebuffer_texture_detach(GPUTexture *tex)
-{
-	GPU_framebuffer_texture_detach(tex);
-}
-
-void DRW_framebuffer_blit(GPUFrameBuffer *fb_read, GPUFrameBuffer *fb_write, bool depth, bool stencil)
-{
-	GPU_framebuffer_blit(fb_read, 0, fb_write, 0, depth, stencil);
-}
-
-void DRW_framebuffer_recursive_downsample(
-        GPUFrameBuffer *fb, GPUTexture *tex, int num_iter,
-        void (*callback)(void *userData, int level), void *userData)
-{
-	GPU_framebuffer_recursive_downsample(fb, tex, num_iter, callback, userData);
-}
-
-void DRW_framebuffer_viewport_size(GPUFrameBuffer *UNUSED(fb_read), int x, int y, int w, int h)
-{
-	glViewport(x, y, w, h);
-}
diff --git a/source/blender/draw/intern/draw_manager_profiling.c b/source/blender/draw/intern/draw_manager_profiling.c
index 47769b1fb18382ccfaa4bb654e5cbbd76b12349e..d2ed22c57b78a410990b837b137e971b51d3d65b 100644
--- a/source/blender/draw/intern/draw_manager_profiling.c
+++ b/source/blender/draw/intern/draw_manager_profiling.c
@@ -225,7 +225,13 @@ void DRW_stats_draw(rcti *rect)
 
 	double init_tot_time = 0.0, background_tot_time = 0.0, render_tot_time = 0.0, tot_time = 0.0;
 
-	UI_FontThemeColor(BLF_default(), TH_TEXT_HI);
+	int fontid = BLF_default();
+	UI_FontThemeColor(fontid, TH_TEXT_HI);
+	BLF_enable(fontid, BLF_SHADOW);
+	BLF_shadow(fontid, 5, (const float[4]){0.0f, 0.0f, 0.0f, 0.75f});
+	BLF_shadow_offset(fontid, 0, -1);
+
+	BLF_batch_draw_begin();
 
 	/* ------------------------------------------ */
 	/* ---------------- CPU stats --------------- */
@@ -351,4 +357,7 @@ void DRW_stats_draw(rcti *rect)
 		draw_stat(rect, 16 + timer->lvl, v, stat_string, sizeof(stat_string));
 		v++;
 	}
+
+	BLF_batch_draw_end();
+	BLF_disable(fontid, BLF_SHADOW);
 }
diff --git a/source/blender/draw/intern/draw_manager_shader.c b/source/blender/draw/intern/draw_manager_shader.c
index d150bcc8d568ee22478a4967aa92c58d29bf6468..5b4971f073057a452ecd97582a638bb71a44ebbd 100644
--- a/source/blender/draw/intern/draw_manager_shader.c
+++ b/source/blender/draw/intern/draw_manager_shader.c
@@ -45,7 +45,7 @@
 
 extern char datatoc_gpu_shader_2D_vert_glsl[];
 extern char datatoc_gpu_shader_3D_vert_glsl[];
-extern char datatoc_gpu_shader_fullscreen_vert_glsl[];
+extern char datatoc_common_fullscreen_vert_glsl[];
 
 
 /* -------------------------------------------------------------------- */
@@ -297,7 +297,7 @@ GPUShader *DRW_shader_create_3D(const char *frag, const char *defines)
 
 GPUShader *DRW_shader_create_fullscreen(const char *frag, const char *defines)
 {
-	return GPU_shader_create(datatoc_gpu_shader_fullscreen_vert_glsl, frag, NULL, NULL, defines);
+	return GPU_shader_create(datatoc_common_fullscreen_vert_glsl, frag, NULL, NULL, defines);
 }
 
 GPUShader *DRW_shader_create_3D_depth_only(void)
diff --git a/source/blender/draw/intern/draw_manager_texture.c b/source/blender/draw/intern/draw_manager_texture.c
index e033a0c506eda6932f8c06f56a7bb16c86d93292..65856a6bf5c7dab745af26090aa8fe2cecc722be 100644
--- a/source/blender/draw/intern/draw_manager_texture.c
+++ b/source/blender/draw/intern/draw_manager_texture.c
@@ -120,7 +120,7 @@ void drw_texture_set_parameters(GPUTexture *tex, DRWTextureFlag flags)
 	GPU_texture_bind(tex, 0);
 	if (flags & DRW_TEX_MIPMAP) {
 		GPU_texture_mipmap_mode(tex, true, flags & DRW_TEX_FILTER);
-		DRW_texture_generate_mipmaps(tex);
+		GPU_texture_generate_mipmap(tex);
 	}
 	else {
 		GPU_texture_filter_mode(tex, flags & DRW_TEX_FILTER);
@@ -197,6 +197,33 @@ GPUTexture *DRW_texture_create_cube(int w, DRWTextureFormat format, DRWTextureFl
 	return tex;
 }
 
+GPUTexture *DRW_texture_pool_query_2D(int w, int h, DRWTextureFormat format, DrawEngineType *engine_type)
+{
+	GPUTexture *tex;
+	GPUTextureFormat data_type;
+	int channels;
+
+	drw_texture_get_format(format, true, &data_type, &channels, NULL);
+	tex = GPU_viewport_texture_pool_query(DST.viewport, engine_type, w, h, channels, data_type);
+
+	return tex;
+}
+
+void DRW_texture_ensure_fullscreen_2D(GPUTexture **tex, DRWTextureFormat format, DRWTextureFlag flags)
+{
+	if (*(tex) == NULL) {
+		const float *size = DRW_viewport_size_get();
+		*(tex) = DRW_texture_create_2D((int)size[0], (int)size[1], format, flags, NULL);
+	}
+}
+
+void DRW_texture_ensure_2D(GPUTexture **tex, int w, int h, DRWTextureFormat format, DRWTextureFlag flags)
+{
+	if (*(tex) == NULL) {
+		*(tex) = DRW_texture_create_2D(w, h, format, flags, NULL);
+	}
+}
+
 void DRW_texture_generate_mipmaps(GPUTexture *tex)
 {
 	GPU_texture_bind(tex, 0);
@@ -204,11 +231,6 @@ void DRW_texture_generate_mipmaps(GPUTexture *tex)
 	GPU_texture_unbind(tex);
 }
 
-void DRW_texture_update(GPUTexture *tex, const float *pixels)
-{
-	GPU_texture_update(tex, pixels);
-}
-
 void DRW_texture_free(GPUTexture *tex)
 {
 	GPU_texture_free(tex);
diff --git a/source/blender/draw/intern/draw_view.c b/source/blender/draw/intern/draw_view.c
index d9ffbde38e7d59271453779c76b451dc99fdae8c..c65ed55561e1716923eaa485e663f0d388d19087 100644
--- a/source/blender/draw/intern/draw_view.c
+++ b/source/blender/draw/intern/draw_view.c
@@ -625,7 +625,7 @@ static bool is_cursor_visible(const DRWContextState *draw_ctx, Scene *scene, Vie
 		}
 		/* exception: object in texture paint mode, clone brush, use_clone_layer disabled */
 		else if (draw_ctx->object_mode & OB_MODE_TEXTURE_PAINT) {
-			const Paint *p = BKE_paint_get_active(scene, view_layer, draw_ctx->object_mode);
+			const Paint *p = BKE_paint_get_active(scene, view_layer);
 
 			if (p && p->brush && p->brush->imagepaint_tool == PAINT_TOOL_CLONE) {
 				if ((scene->toolsettings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_CLONE) == 0) {
@@ -645,7 +645,7 @@ void DRW_draw_cursor(void)
 {
 	const DRWContextState *draw_ctx = DRW_context_state_get();
 	View3D *v3d = draw_ctx->v3d;
-	RegionView3D *rv3d = draw_ctx->rv3d;
+	ARegion *ar = draw_ctx->ar;
 	Scene *scene = draw_ctx->scene;
 	ViewLayer *view_layer = draw_ctx->view_layer;
 
@@ -655,61 +655,18 @@ void DRW_draw_cursor(void)
 	glLineWidth(1.0f);
 
 	if (is_cursor_visible(draw_ctx, scene, view_layer)) {
-		float *co = ED_view3d_cursor3d_get(scene, v3d);
-		unsigned char crosshair_color[3];
-
-		const float f5 = 0.25f;
-		const float f10 = 0.5f;
-		const float f20 = 1.0f;
-
-		Gwn_VertFormat *format = immVertexFormat();
-		unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
-		unsigned int color = GWN_vertformat_attr_add(format, "color", GWN_COMP_U8, 3, GWN_FETCH_INT_TO_FLOAT_UNIT);
-		unsigned int wpos = GWN_vertformat_attr_add(format, "world_pos", GWN_COMP_F32, 3, GWN_FETCH_FLOAT);
+		int co[2];
+		if (ED_view3d_project_int_global(ar, ED_view3d_cursor3d_get(scene, v3d), co, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) {
 
-		/* XXX Using instance shader without instance */
-		immBindBuiltinProgram(GPU_SHADER_3D_SCREENSPACE_VARIYING_COLOR);
-		immUniform1f("size", U.widget_unit);
-		immUniform1f("pixel_size", *DRW_viewport_pixelsize_get());
-		immUniformArray3fv("screen_vecs", DRW_viewport_screenvecs_get(), 2);
-		immUniformMatrix4fv("ViewProjectionMatrix", rv3d->persmat);
+			ED_region_pixelspace(ar);
+			gpuTranslate2f(co[0], co[1]);
+			gpuScale2f(U.widget_unit, U.widget_unit);
 
-		const int segments = 16;
-
-		immBegin(GWN_PRIM_LINE_LOOP, segments);
-		immAttrib3fv(wpos, co);
-
-		for (int i = 0; i < segments; ++i) {
-			float angle = (float)(2 * M_PI) * ((float)i / (float)segments);
-			float x = f10 * cosf(angle);
-			float y = f10 * sinf(angle);
-
-			if (i % 2 == 0)
-				immAttrib3ub(color, 255, 0, 0);
-			else
-				immAttrib3ub(color, 255, 255, 255);
-
-			immVertex2f(pos, x, y);
+			Gwn_Batch *cursor_batch = DRW_cache_cursor_get();
+			GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_FLAT_COLOR);
+			GWN_batch_program_set(cursor_batch, GPU_shader_get_program(shader), GPU_shader_get_interface(shader));
+			GWN_batch_draw(cursor_batch);
 		}
-		immEnd();
-
-		UI_GetThemeColor3ubv(TH_VIEW_OVERLAY, crosshair_color);
-
-		immBegin(GWN_PRIM_LINES, 8);
-		immAttrib3ubv(color, crosshair_color);
-		immAttrib3fv(wpos, co);
-
-		immVertex2f(pos, -f20, 0);
-		immVertex2f(pos, -f5, 0);
-		immVertex2f(pos, +f5, 0);
-		immVertex2f(pos, +f20, 0);
-		immVertex2f(pos, 0, -f20);
-		immVertex2f(pos, 0, -f5);
-		immVertex2f(pos, 0, +f5);
-		immVertex2f(pos, 0, +f20);
-		immEnd();
-
-		immUnbindProgram();
 	}
 }
 
diff --git a/source/blender/draw/modes/edit_mesh_mode.c b/source/blender/draw/modes/edit_mesh_mode.c
index cc1373dc29fcf925b9865dbf5f47403002d19775..4bd69941809b774aa9883e05932c62ca3b6f58f3 100644
--- a/source/blender/draw/modes/edit_mesh_mode.c
+++ b/source/blender/draw/modes/edit_mesh_mode.c
@@ -137,15 +137,17 @@ static void EDIT_MESH_engine_init(void *vedata)
 	EDIT_MESH_FramebufferList *fbl = ((EDIT_MESH_Data *)vedata)->fbl;
 
 	const float *viewport_size = DRW_viewport_size_get();
+	const int size[2] = {(int)viewport_size[0], (int)viewport_size[1]};
 
-	DRWFboTexture tex[2] = {{
-	    &e_data.occlude_wire_depth_tx, DRW_TEX_DEPTH_24, DRW_TEX_TEMP},
-	    {&e_data.occlude_wire_color_tx, DRW_TEX_RGBA_8, DRW_TEX_FILTER | DRW_TEX_TEMP}
-	};
-	DRW_framebuffer_init(
-	        &fbl->occlude_wire_fb, &draw_engine_edit_mesh_type,
-	        (int)viewport_size[0], (int)viewport_size[1],
-	        tex, ARRAY_SIZE(tex));
+	e_data.occlude_wire_depth_tx = DRW_texture_pool_query_2D(size[0], size[1], DRW_TEX_DEPTH_24,
+	                                                         &draw_engine_edit_mesh_type);
+	e_data.occlude_wire_color_tx = DRW_texture_pool_query_2D(size[0], size[1], DRW_TEX_RGBA_8,
+	                                                         &draw_engine_edit_mesh_type);
+
+	GPU_framebuffer_ensure_config(&fbl->occlude_wire_fb, {
+		GPU_ATTACHMENT_TEXTURE(e_data.occlude_wire_depth_tx),
+		GPU_ATTACHMENT_TEXTURE(e_data.occlude_wire_color_tx)
+	});
 
 	if (!e_data.vcolor_face_shader) {
 		e_data.vcolor_face_shader = GPU_shader_get_builtin_shader(GPU_SHADER_SIMPLE_LIGHTING_SMOOTH_COLOR_ALPHA);
@@ -406,9 +408,9 @@ static void EDIT_MESH_cache_init(void *vedata)
 		DRWShadingGroup *mix_shgrp = DRW_shgroup_create(e_data.overlay_mix_sh, psl->mix_occlude);
 		DRW_shgroup_call_add(mix_shgrp, quad, NULL);
 		DRW_shgroup_uniform_float(mix_shgrp, "alpha", &backwire_opacity, 1);
-		DRW_shgroup_uniform_buffer(mix_shgrp, "wireColor", &e_data.occlude_wire_color_tx);
-		DRW_shgroup_uniform_buffer(mix_shgrp, "wireDepth", &e_data.occlude_wire_depth_tx);
-		DRW_shgroup_uniform_buffer(mix_shgrp, "sceneDepth", &dtxl->depth);
+		DRW_shgroup_uniform_texture_ref(mix_shgrp, "wireColor", &e_data.occlude_wire_color_tx);
+		DRW_shgroup_uniform_texture_ref(mix_shgrp, "wireDepth", &e_data.occlude_wire_depth_tx);
+		DRW_shgroup_uniform_texture_ref(mix_shgrp, "sceneDepth", &dtxl->depth);
 	}
 }
 
@@ -523,7 +525,6 @@ static void EDIT_MESH_draw_scene(void *vedata)
 	EDIT_MESH_PassList *psl = ((EDIT_MESH_Data *)vedata)->psl;
 	EDIT_MESH_FramebufferList *fbl = ((EDIT_MESH_Data *)vedata)->fbl;
 	DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
-	DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
 
 	DRW_draw_pass(psl->vcolor_faces);
 
@@ -534,29 +535,15 @@ static void EDIT_MESH_draw_scene(void *vedata)
 		/* render facefill */
 		DRW_draw_pass(psl->facefill_occlude);
 
-		/* attach temp textures */
-		DRW_framebuffer_texture_attach(fbl->occlude_wire_fb, e_data.occlude_wire_depth_tx, 0, 0);
-		DRW_framebuffer_texture_attach(fbl->occlude_wire_fb, e_data.occlude_wire_color_tx, 0, 0);
-		
 		/* Render wires on a separate framebuffer */
-		DRW_framebuffer_bind(fbl->occlude_wire_fb);
-		DRW_framebuffer_clear(true, true, false, clearcol, 1.0f);
+		GPU_framebuffer_bind(fbl->occlude_wire_fb);
+		GPU_framebuffer_clear_color_depth(fbl->occlude_wire_fb, clearcol, 1.0f);
 		DRW_draw_pass(psl->normals);
 		DRW_draw_pass(psl->edit_face_occluded);
 
-		/* detach textures */
-		DRW_framebuffer_texture_detach(dtxl->depth);
-
 		/* Combine with scene buffer */
-		DRW_framebuffer_bind(dfbl->default_fb);
+		GPU_framebuffer_bind(dfbl->color_only_fb);
 		DRW_draw_pass(psl->mix_occlude);
-
-		/* detach temp textures */
-		DRW_framebuffer_texture_detach(e_data.occlude_wire_depth_tx);
-		DRW_framebuffer_texture_detach(e_data.occlude_wire_color_tx);
-
-		/* reattach */
-		DRW_framebuffer_texture_attach(dfbl->default_fb, dtxl->depth, 0, 0);
 	}
 	else {
 		DRW_draw_pass(psl->normals);
diff --git a/source/blender/draw/modes/object_mode.c b/source/blender/draw/modes/object_mode.c
index ded2bc680626b6156c14d932c1f0a8b8337005f8..939507a4f21873510ae815d7050135516dc47515 100644
--- a/source/blender/draw/modes/object_mode.c
+++ b/source/blender/draw/modes/object_mode.c
@@ -83,7 +83,7 @@ extern char datatoc_object_particle_dot_frag_glsl[];
 extern char datatoc_common_globals_lib_glsl[];
 extern char datatoc_common_fxaa_lib_glsl[];
 extern char datatoc_gpu_shader_flat_color_frag_glsl[];
-extern char datatoc_gpu_shader_fullscreen_vert_glsl[];
+extern char datatoc_common_fullscreen_vert_glsl[];
 extern char datatoc_gpu_shader_uniform_color_frag_glsl[];
 
 /* *********** LISTS *********** */
@@ -106,8 +106,9 @@ typedef struct OBJECT_PassList {
 } OBJECT_PassList;
 
 typedef struct OBJECT_FramebufferList {
-	struct GPUFrameBuffer *outlines;
-	struct GPUFrameBuffer *blur;
+	struct GPUFrameBuffer *outlines_fb;
+	struct GPUFrameBuffer *blur_fb;
+	struct GPUFrameBuffer *expand_fb;
 } OBJECT_FramebufferList;
 
 typedef struct OBJECT_StorageList {
@@ -282,23 +283,31 @@ static void OBJECT_engine_init(void *vedata)
 	OBJECT_FramebufferList *fbl = ((OBJECT_Data *)vedata)->fbl;
 
 	const float *viewport_size = DRW_viewport_size_get();
+	const int size[2] = {(int)viewport_size[0], (int)viewport_size[1]};
 
 	if (DRW_state_is_fbo()) {
-		DRWFboTexture tex[2] = {
-			{&e_data.outlines_depth_tx, DRW_TEX_DEPTH_24, DRW_TEX_TEMP},
-			{&e_data.outlines_color_tx, DRW_TEX_RGBA_8, DRW_TEX_FILTER | DRW_TEX_TEMP},
-		};
+		e_data.outlines_depth_tx = DRW_texture_pool_query_2D(size[0], size[1], DRW_TEX_DEPTH_24,
+		                                                     &draw_engine_object_type);
+		e_data.outlines_color_tx = DRW_texture_pool_query_2D(size[0], size[1], DRW_TEX_RGBA_8,
+		                                                     &draw_engine_object_type);
 
-		DRW_framebuffer_init(
-		        &fbl->outlines, &draw_engine_object_type,
-		        (int)viewport_size[0], (int)viewport_size[1],
-		        tex, 2);
+		GPU_framebuffer_ensure_config(&fbl->outlines_fb, {
+			GPU_ATTACHMENT_TEXTURE(e_data.outlines_depth_tx),
+			GPU_ATTACHMENT_TEXTURE(e_data.outlines_color_tx)
+		});
 
-		DRWFboTexture blur_tex = {&e_data.outlines_blur_tx, DRW_TEX_RGBA_8, DRW_TEX_FILTER | DRW_TEX_TEMP};
-		DRW_framebuffer_init(
-		        &fbl->blur, &draw_engine_object_type,
-		        (int)viewport_size[0], (int)viewport_size[1],
-		        &blur_tex, 1);
+		GPU_framebuffer_ensure_config(&fbl->expand_fb, {
+			GPU_ATTACHMENT_NONE,
+			GPU_ATTACHMENT_TEXTURE(e_data.outlines_color_tx)
+		});
+
+		e_data.outlines_blur_tx = DRW_texture_pool_query_2D(size[0], size[1], DRW_TEX_RGBA_8,
+		                                                    &draw_engine_object_type);
+
+		GPU_framebuffer_ensure_config(&fbl->blur_fb, {
+			GPU_ATTACHMENT_NONE,
+			GPU_ATTACHMENT_TEXTURE(e_data.outlines_blur_tx)
+		});
 	}
 
 	if (!e_data.outline_resolve_sh) {
@@ -307,7 +316,7 @@ static void OBJECT_engine_init(void *vedata)
 
 	if (!e_data.outline_resolve_aa_sh) {
 		e_data.outline_resolve_aa_sh = DRW_shader_create_with_lib(
-		            datatoc_gpu_shader_fullscreen_vert_glsl, NULL,
+		            datatoc_common_fullscreen_vert_glsl, NULL,
 		            datatoc_object_outline_resolve_frag_glsl,
 		            datatoc_common_fxaa_lib_glsl,
 		            "#define FXAA_ALPHA\n"
@@ -813,23 +822,23 @@ static void OBJECT_cache_init(void *vedata)
 		psl->outlines_search = DRW_pass_create("Outlines Detect Pass", state);
 
 		DRWShadingGroup *grp = DRW_shgroup_create(e_data.outline_detect_sh, psl->outlines_search);
-		DRW_shgroup_uniform_buffer(grp, "outlineColor", &e_data.outlines_color_tx);
-		DRW_shgroup_uniform_buffer(grp, "outlineDepth", &e_data.outlines_depth_tx);
-		DRW_shgroup_uniform_buffer(grp, "sceneDepth", &dtxl->depth);
+		DRW_shgroup_uniform_texture_ref(grp, "outlineColor", &e_data.outlines_color_tx);
+		DRW_shgroup_uniform_texture_ref(grp, "outlineDepth", &e_data.outlines_depth_tx);
+		DRW_shgroup_uniform_texture_ref(grp, "sceneDepth", &dtxl->depth);
 		DRW_shgroup_uniform_float(grp, "alphaOcclu", &alphaOcclu, 1);
 		DRW_shgroup_call_add(grp, quad, NULL);
 
 		psl->outlines_expand = DRW_pass_create("Outlines Expand Pass", state);
 
 		grp = DRW_shgroup_create(e_data.outline_fade_sh, psl->outlines_expand);
-		DRW_shgroup_uniform_buffer(grp, "outlineColor", &e_data.outlines_blur_tx);
+		DRW_shgroup_uniform_texture_ref(grp, "outlineColor", &e_data.outlines_blur_tx);
 		DRW_shgroup_uniform_bool(grp, "doExpand", &bTrue, 1);
 		DRW_shgroup_call_add(grp, quad, NULL);
 
 		psl->outlines_bleed = DRW_pass_create("Outlines Bleed Pass", state);
 
 		grp = DRW_shgroup_create(e_data.outline_fade_sh, psl->outlines_bleed);
-		DRW_shgroup_uniform_buffer(grp, "outlineColor", &e_data.outlines_color_tx);
+		DRW_shgroup_uniform_texture_ref(grp, "outlineColor", &e_data.outlines_color_tx);
 		DRW_shgroup_uniform_bool(grp, "doExpand", &bFalse, 1);
 		DRW_shgroup_call_add(grp, quad, NULL);
 	}
@@ -841,7 +850,7 @@ static void OBJECT_cache_init(void *vedata)
 		struct Gwn_Batch *quad = DRW_cache_fullscreen_quad_get();
 
 		DRWShadingGroup *grp = DRW_shgroup_create(e_data.outline_resolve_aa_sh, psl->outlines_resolve);
-		DRW_shgroup_uniform_buffer(grp, "outlineBluredColor", &e_data.outlines_blur_tx);
+		DRW_shgroup_uniform_texture_ref(grp, "outlineBluredColor", &e_data.outlines_blur_tx);
 		DRW_shgroup_uniform_vec2(grp, "rcpDimensions", e_data.inv_viewport_size, 1);
 		DRW_shgroup_call_add(grp, quad, NULL);
 	}
@@ -866,7 +875,7 @@ static void OBJECT_cache_init(void *vedata)
 		DRW_shgroup_uniform_float(grp, "gridOneOverLogSubdiv", &e_data.grid_settings[4], 1);
 		DRW_shgroup_uniform_block(grp, "globalsBlock", globals_ubo);
 		DRW_shgroup_uniform_vec2(grp, "viewportSize", DRW_viewport_size_get(), 1);
-		DRW_shgroup_uniform_buffer(grp, "depthBuffer", &dtxl->depth);
+		DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
 		DRW_shgroup_call_add(grp, quad, mat);
 
 		grp = DRW_shgroup_create(e_data.grid_sh, psl->grid);
@@ -874,7 +883,7 @@ static void OBJECT_cache_init(void *vedata)
 		DRW_shgroup_uniform_vec3(grp, "planeNormal", e_data.grid_normal, 1);
 		DRW_shgroup_uniform_vec3(grp, "planeAxes", e_data.grid_axes, 1);
 		DRW_shgroup_uniform_block(grp, "globalsBlock", globals_ubo);
-		DRW_shgroup_uniform_buffer(grp, "depthBuffer", &dtxl->depth);
+		DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
 		DRW_shgroup_call_add(grp, quad, mat);
 
 		grp = DRW_shgroup_create(e_data.grid_sh, psl->grid);
@@ -882,7 +891,7 @@ static void OBJECT_cache_init(void *vedata)
 		DRW_shgroup_uniform_vec3(grp, "planeNormal", e_data.zplane_normal, 1);
 		DRW_shgroup_uniform_vec3(grp, "planeAxes", e_data.zplane_axes, 1);
 		DRW_shgroup_uniform_block(grp, "globalsBlock", globals_ubo);
-		DRW_shgroup_uniform_buffer(grp, "depthBuffer", &dtxl->depth);
+		DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
 		DRW_shgroup_call_add(grp, quad, mat);
 	}
 
@@ -1304,7 +1313,8 @@ static void DRW_shgroup_camera(OBJECT_StorageList *stl, Object *ob, ViewLayer *v
 	RegionView3D *rv3d = draw_ctx->rv3d;
 
 	Camera *cam = ob->data;
-	const bool is_active = (ob == v3d->camera);
+	const  Object *camera_object = DEG_get_evaluated_object(draw_ctx->depsgraph, v3d->camera);
+	const bool is_active = (ob == camera_object);
 	const bool look_through = (is_active && (rv3d->persp == RV3D_CAMOB));
 	float *color;
 	DRW_object_wire_theme_get(ob, view_layer, &color);
@@ -1802,7 +1812,7 @@ static void OBJECT_cache_populate_particles(Object *ob,
 			unit_m4(mat);
 
 			if (draw_as != PART_DRAW_PATH) {
-				struct Gwn_Batch *geom = DRW_cache_particles_get_dots(psys);
+				struct Gwn_Batch *geom = DRW_cache_particles_get_dots(ob, psys);
 				DRWShadingGroup *shgrp = NULL;
 				static int screen_space[2] = {0, 1};
 				static float def_prim_col[3] = {0.5f, 0.5f, 0.5f};
@@ -2035,38 +2045,27 @@ static void OBJECT_draw_scene(void *vedata)
 
 	if (DRW_state_is_fbo()) {
 		DRW_stats_group_start("Outlines");
-		/* attach temp textures */
-		DRW_framebuffer_texture_attach(fbl->outlines, e_data.outlines_depth_tx, 0, 0);
-		DRW_framebuffer_texture_attach(fbl->outlines, e_data.outlines_color_tx, 0, 0);
-		DRW_framebuffer_texture_attach(fbl->blur, e_data.outlines_blur_tx, 0, 0);
 		
 		/* Render filled polygon on a separate framebuffer */
-		DRW_framebuffer_bind(fbl->outlines);
-		DRW_framebuffer_clear(true, true, false, clearcol, 1.0f);
+		GPU_framebuffer_bind(fbl->outlines_fb);
+		GPU_framebuffer_clear_color_depth(fbl->outlines_fb, clearcol, 1.0f);
 		DRW_draw_pass(psl->outlines);
 		DRW_draw_pass(psl->lightprobes);
 
-		/* detach textures */
-		DRW_framebuffer_texture_detach(e_data.outlines_depth_tx);
-
 		/* Search outline pixels */
-		DRW_framebuffer_bind(fbl->blur);
+		GPU_framebuffer_bind(fbl->blur_fb);
 		DRW_draw_pass(psl->outlines_search);
 
 		/* Expand outline to form a 3px wide line */
-		DRW_framebuffer_bind(fbl->outlines);
+		GPU_framebuffer_bind(fbl->expand_fb);
 		DRW_draw_pass(psl->outlines_expand);
 
 		/* Bleed color so the AA can do it's stuff */
-		DRW_framebuffer_bind(fbl->blur);
+		GPU_framebuffer_bind(fbl->blur_fb);
 		DRW_draw_pass(psl->outlines_bleed);
 
-		/* detach temp textures */
-		DRW_framebuffer_texture_detach(e_data.outlines_color_tx);
-		DRW_framebuffer_texture_detach(e_data.outlines_blur_tx);
-
 		/* restore main framebuffer */
-		DRW_framebuffer_bind(dfbl->default_fb);
+		GPU_framebuffer_bind(dfbl->default_fb);
 		DRW_stats_group_end();
 	}
 	else if (DRW_state_is_select()) {
@@ -2090,9 +2089,9 @@ static void OBJECT_draw_scene(void *vedata)
 
 	if (DRW_state_is_fbo()) {
 		if (e_data.draw_grid) {
-			DRW_framebuffer_texture_detach(dtxl->depth);
+			GPU_framebuffer_bind(dfbl->color_only_fb);
 			DRW_draw_pass(psl->grid);
-			DRW_framebuffer_texture_attach(dfbl->default_fb, dtxl->depth, 0, 0);
+			GPU_framebuffer_texture_attach(dfbl->default_fb, dtxl->depth, 0, 0);
 		}
 
 		/* Combine with scene buffer last */
diff --git a/source/blender/draw/modes/sculpt_mode.c b/source/blender/draw/modes/sculpt_mode.c
index d8c5bae522bcb6be4bed5d4739bf352fc7d3e63b..65f4653591f0c67894740e168d02df7b2e1fbb96 100644
--- a/source/blender/draw/modes/sculpt_mode.c
+++ b/source/blender/draw/modes/sculpt_mode.c
@@ -193,12 +193,8 @@ static void SCULPT_cache_populate(void *vedata, Object *ob)
 
 	if (ob->type == OB_MESH) {
 		const DRWContextState *draw_ctx = DRW_context_state_get();
-		EvaluationContext eval_ctx;
-
-		CTX_data_eval_ctx(draw_ctx->evil_C, &eval_ctx);
 
 		if (ob->sculpt && (ob == draw_ctx->obact)) {
-
 			/* XXX, needed for dyntopo-undo (which clears).
 			 * probably depsgraph should handlle? in 2.7x getting derived-mesh does this (mesh_build_data) */
 			if (ob->sculpt->pbvh == NULL) {
@@ -206,7 +202,7 @@ static void SCULPT_cache_populate(void *vedata, Object *ob)
 				 * but this avoids waiting on first stroke) */
 				Scene *scene = draw_ctx->scene;
 
-				BKE_sculpt_update_mesh_elements(&eval_ctx, scene, scene->toolsettings->sculpt, ob, false, false);
+				BKE_sculpt_update_mesh_elements(&draw_ctx->eval_ctx, scene, scene->toolsettings->sculpt, ob, false, false);
 			}
 
 			PBVH *pbvh = ob->sculpt->pbvh;
diff --git a/source/blender/gpu/shaders/gpu_shader_fullscreen_vert.glsl b/source/blender/draw/modes/shaders/common_fullscreen_vert.glsl
similarity index 100%
rename from source/blender/gpu/shaders/gpu_shader_fullscreen_vert.glsl
rename to source/blender/draw/modes/shaders/common_fullscreen_vert.glsl
diff --git a/source/blender/editors/CMakeLists.txt b/source/blender/editors/CMakeLists.txt
index c7ce4b44803254786292b13ac4b627cfa364c1b9..06f412b7019e0e9f868b4a1ad4b780966ccdc056 100644
--- a/source/blender/editors/CMakeLists.txt
+++ b/source/blender/editors/CMakeLists.txt
@@ -60,6 +60,7 @@ if(WITH_BLENDER)
 	add_subdirectory(space_userpref)
 	add_subdirectory(space_view3d)
 	add_subdirectory(transform)
+	add_subdirectory(undo)
 	add_subdirectory(util)
 	add_subdirectory(uvedit)
 	add_subdirectory(screen)
diff --git a/source/blender/editors/animation/fmodifier_ui.c b/source/blender/editors/animation/fmodifier_ui.c
index 8d77460e19701f693f528ae70542d739b28a0221..8106be79521dddcd074b7333f855849ae7d9bba9 100644
--- a/source/blender/editors/animation/fmodifier_ui.c
+++ b/source/blender/editors/animation/fmodifier_ui.c
@@ -62,7 +62,7 @@
 #include "UI_resources.h"
 
 #include "ED_anim_api.h"
-#include "ED_util.h"
+#include "ED_undo.h"
 
 /* ********************************************** */
 /* UI STUFF */
diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c
index 4ed5c49c1e32bd6300820380c14a32463704c8f5..126e4b5f7363a5258df9f5681f4f158ca5fa4cda 100644
--- a/source/blender/editors/animation/keyframing.c
+++ b/source/blender/editors/animation/keyframing.c
@@ -1579,9 +1579,6 @@ void ANIM_OT_keyframe_delete(wmOperatorType *ot)
  
 static int clear_anim_v3d_exec(bContext *C, wmOperator *UNUSED(op))
 {
-	EvaluationContext eval_ctx;
-	CTX_data_eval_ctx(C, &eval_ctx);
-
 	bool changed = false;
 
 	CTX_DATA_BEGIN (C, Object *, ob, selected_objects)
@@ -1598,7 +1595,7 @@ static int clear_anim_v3d_exec(bContext *C, wmOperator *UNUSED(op))
 				fcn = fcu->next;
 				
 				/* in pose mode, only delete the F-Curve if it belongs to a selected bone */
-				if (eval_ctx.object_mode & OB_MODE_POSE) {
+				if (ob->mode & OB_MODE_POSE) {
 					if ((fcu->rna_path) && strstr(fcu->rna_path, "pose.bones[")) {
 						bPoseChannel *pchan;
 						char *bone_name;
@@ -1661,10 +1658,8 @@ void ANIM_OT_keyframe_clear_v3d(wmOperatorType *ot)
 
 static int delete_key_v3d_exec(bContext *C, wmOperator *op)
 {
-	EvaluationContext eval_ctx;
-	CTX_data_eval_ctx(C, &eval_ctx);
-
-	const float cfra = eval_ctx.ctime;
+	Scene *scene = CTX_data_scene(C);
+	float cfra = (float)CFRA;
 	
 	CTX_DATA_BEGIN (C, Object *, ob, selected_objects)
 	{
@@ -1692,7 +1687,7 @@ static int delete_key_v3d_exec(bContext *C, wmOperator *op)
 				/* special exception for bones, as this makes this operator more convenient to use
 				 * NOTE: This is only done in pose mode. In object mode, we're dealign with the entire object.
 				 */
-				if ((eval_ctx.object_mode & OB_MODE_POSE) && strstr(fcu->rna_path, "pose.bones[\"")) {
+				if ((ob->mode & OB_MODE_POSE) && strstr(fcu->rna_path, "pose.bones[\"")) {
 					bPoseChannel *pchan;
 					char *bone_name;
 					
diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c
index 48436a979a2c93d6971200e91a176ef5ace99661..bd3ddfe93c613eb8b25fd56f351b6394ca659278 100644
--- a/source/blender/editors/armature/armature_add.c
+++ b/source/blender/editors/armature/armature_add.c
@@ -1029,7 +1029,7 @@ static int armature_bone_primitive_add_exec(bContext *C, wmOperator *op)
 	
 	ED_armature_deselect_all(obedit);
 	
-	/*	Create a bone	*/
+	/*	Create a bone */
 	bone = ED_armature_edit_bone_add(obedit->data, name);
 
 	copy_v3_v3(bone->head, curs);
diff --git a/source/blender/editors/armature/armature_edit.c b/source/blender/editors/armature/armature_edit.c
index 6c8779202e9713785ef8dfee6201cd8e381ac0db..bb3c4164fc16fd216e0374f69e91f21face4dec7 100644
--- a/source/blender/editors/armature/armature_edit.c
+++ b/source/blender/editors/armature/armature_edit.c
@@ -1458,8 +1458,9 @@ static int armature_dissolve_selected_exec(bContext *C, wmOperator *UNUSED(op))
 		if (ebone->flag & BONE_DONE) {
 			copy_v3_v3(ebone->parent->tail, ebone->tail);
 			ebone->parent->rad_tail = ebone->rad_tail;
+			SET_FLAG_FROM_TEST(ebone->parent->flag, ebone->flag & BONE_TIPSEL, BONE_TIPSEL);
 
-			ED_armature_edit_bone_remove(arm, ebone);
+			ED_armature_edit_bone_remove_ex(arm, ebone, false);
 			changed = true;
 		}
 	}
@@ -1468,10 +1469,9 @@ static int armature_dissolve_selected_exec(bContext *C, wmOperator *UNUSED(op))
 		for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
 			if (ebone->parent &&
 			    ebone->parent->temp.ebone &&
-			    (ebone->flag & BONE_CONNECTED) == 0)
+			    (ebone->flag & BONE_CONNECTED))
 			{
-				ebone->flag |= BONE_CONNECTED;
-				ebone->rad_head = ebone->parent->rad_head;
+				ebone->rad_head = ebone->parent->rad_tail;
 			}
 		}
 
diff --git a/source/blender/editors/armature/armature_relations.c b/source/blender/editors/armature/armature_relations.c
index 8fc7aeaf029e4ed6c3c0dbbbfd5aa1d0168c261e..de2611f7092ef223425aa249a0da86d4008f7e79 100644
--- a/source/blender/editors/armature/armature_relations.c
+++ b/source/blender/editors/armature/armature_relations.c
@@ -281,6 +281,7 @@ int join_armature_exec(bContext *C, wmOperator *op)
 	
 	/* get pose of active object and move it out of posemode */
 	pose = ob->pose;
+	ob->mode &= ~OB_MODE_POSE;
 
 	CTX_DATA_BEGIN(C, Base *, base, selected_editable_bases)
 	{
@@ -301,6 +302,8 @@ int join_armature_exec(bContext *C, wmOperator *op)
 			
 			/* Get Pose of current armature */
 			opose = base->object->pose;
+			base->object->mode &= ~OB_MODE_POSE;
+			//BASACT->flag &= ~OB_MODE_POSE;
 			
 			/* Find the difference matrix */
 			invert_m4_m4(oimat, ob->obmat);
@@ -605,6 +608,8 @@ static int separate_armature_exec(bContext *C, wmOperator *op)
 	/* 1) store starting settings and exit editmode */
 	oldob = obedit;
 	oldbase = view_layer->basact;
+	oldob->mode &= ~OB_MODE_POSE;
+	//oldbase->flag &= ~OB_POSEMODE;
 	
 	ED_armature_from_edit(obedit->data);
 	ED_armature_edit_free(obedit->data);
diff --git a/source/blender/editors/armature/armature_skinning.c b/source/blender/editors/armature/armature_skinning.c
index 8900da900c07017cac3669ef834dae4a98dc0a02..5c8e08a0d8975467a88855a1152ccaa6b549355c 100644
--- a/source/blender/editors/armature/armature_skinning.c
+++ b/source/blender/editors/armature/armature_skinning.c
@@ -192,7 +192,6 @@ static int dgroup_skinnable_cb(Object *ob, Bone *bone, void *datap)
 }
 
 static void envelope_bone_weighting(
-        const EvaluationContext *eval_ctx,
         Object *ob, Mesh *mesh, float (*verts)[3], int numbones, Bone **bonelist,
         bDeformGroup **dgrouplist, bDeformGroup **dgroupflip,
         float (*root)[3], float (*tip)[3], const int *selected, float scale)
@@ -206,7 +205,7 @@ static void envelope_bone_weighting(
 	bool use_topology = (mesh->editflag & ME_EDIT_MIRROR_TOPO) != 0;
 	bool use_mask = false;
 
-	if ((eval_ctx->object_mode & OB_MODE_WEIGHT_PAINT) &&
+	if ((ob->mode & OB_MODE_WEIGHT_PAINT) &&
 	    (mesh->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)))
 	{
 		use_mask = true;
@@ -277,12 +276,13 @@ static void add_verts_to_dgroups(
 	float (*root)[3], (*tip)[3], (*verts)[3];
 	int *selected;
 	int numbones, vertsfilled = 0, i, j, segments = 0;
+	const bool wpmode = (ob->mode & OB_MODE_WEIGHT_PAINT);
 	struct { Object *armob; void *list; int heat; bool is_weight_paint; } looper_data;
 
 	looper_data.armob = par;
 	looper_data.heat = heat;
 	looper_data.list = NULL;
-	looper_data.is_weight_paint = (eval_ctx->object_mode & OB_MODE_WEIGHT_PAINT);
+	looper_data.is_weight_paint = wpmode;
 
 	/* count the number of skinnable bones */
 	numbones = bone_looper(ob, arm->bonebase.first, &looper_data, bone_skinnable_cb);
@@ -355,7 +355,7 @@ static void add_verts_to_dgroups(
 		mul_m4_v3(par->obmat, tip[j]);
 		
 		/* set selected */
-		if (eval_ctx->object_mode & OB_MODE_WEIGHT_PAINT) {
+		if (wpmode) {
 			if ((arm->layer & bone->layer) && (bone->flag & BONE_SELECTED))
 				selected[j] = 1;
 		}
@@ -375,7 +375,7 @@ static void add_verts_to_dgroups(
 	mesh = (Mesh *)ob->data;
 	verts = MEM_callocN(mesh->totvert * sizeof(*verts), "closestboneverts");
 
-	if (eval_ctx->object_mode & OB_MODE_WEIGHT_PAINT) {
+	if (wpmode) {
 		/* if in weight paint mode, use final verts from derivedmesh */
 		DerivedMesh *dm = mesh_get_derived_final(eval_ctx, scene, ob, CD_MASK_BAREMESH);
 		
@@ -406,7 +406,7 @@ static void add_verts_to_dgroups(
 		const char *error = NULL;
 
 		heat_bone_weighting(
-		        eval_ctx, ob, mesh, verts, numbones, dgrouplist, dgroupflip,
+		        ob, mesh, verts, numbones, dgrouplist, dgroupflip,
 		        root, tip, selected, &error);
 		if (error) {
 			BKE_report(reports, RPT_WARNING, error);
@@ -414,7 +414,7 @@ static void add_verts_to_dgroups(
 	}
 	else {
 		envelope_bone_weighting(
-		        eval_ctx, ob, mesh, verts, numbones, bonelist, dgrouplist,
+		        ob, mesh, verts, numbones, bonelist, dgrouplist,
 		        dgroupflip, root, tip, selected, mat4_to_scale(par->obmat));
 	}
 
diff --git a/source/blender/editors/armature/armature_utils.c b/source/blender/editors/armature/armature_utils.c
index 23a49d282e1ba2e5ebe6cf1217fae744b7d6c10e..aa0aa0e03ffaa8faeab43f7f8ea9509565dc4117 100644
--- a/source/blender/editors/armature/armature_utils.c
+++ b/source/blender/editors/armature/armature_utils.c
@@ -134,7 +134,10 @@ void bone_free(bArmature *arm, EditBone *bone)
 	BLI_freelinkN(arm->edbo, bone);
 }
 
-void ED_armature_edit_bone_remove(bArmature *arm, EditBone *exBone)
+/**
+ * \param clear_connected: When false caller is responsible for keeping the flag in a valid state.
+ */
+void ED_armature_edit_bone_remove_ex(bArmature *arm, EditBone *exBone, bool clear_connected)
 {
 	EditBone *curBone;
 
@@ -142,13 +145,20 @@ void ED_armature_edit_bone_remove(bArmature *arm, EditBone *exBone)
 	for (curBone = arm->edbo->first; curBone; curBone = curBone->next) {
 		if (curBone->parent == exBone) {
 			curBone->parent = exBone->parent;
-			curBone->flag &= ~BONE_CONNECTED;
+			if (clear_connected) {
+				curBone->flag &= ~BONE_CONNECTED;
+			}
 		}
 	}
 
 	bone_free(arm, exBone);
 }
 
+void ED_armature_edit_bone_remove(bArmature *arm, EditBone *exBone)
+{
+	ED_armature_edit_bone_remove_ex(arm, exBone, true);
+}
+
 bool ED_armature_ebone_is_child_recursive(EditBone *ebone_parent, EditBone *ebone_child)
 {
 	for (ebone_child = ebone_child->parent; ebone_child; ebone_child = ebone_child->parent) {
@@ -592,7 +602,7 @@ void ED_armature_from_edit(bArmature *arm)
 		if (len_sq <= SQUARE(0.000001f)) {  /* FLT_EPSILON is too large? */
 			EditBone *fBone;
 			
-			/*	Find any bones that refer to this bone	*/
+			/* Find any bones that refer to this bone */
 			for (fBone = arm->edbo->first; fBone; fBone = fBone->next) {
 				if (fBone->parent == eBone)
 					fBone->parent = eBone->parent;
diff --git a/source/blender/editors/armature/editarmature_retarget.c b/source/blender/editors/armature/editarmature_retarget.c
index 2fb216c2ef8b71da0478005e66a9e2a8a6ea28c4..3dd41f25e0924bc970985f282295984ab21fbaf8 100644
--- a/source/blender/editors/armature/editarmature_retarget.c
+++ b/source/blender/editors/armature/editarmature_retarget.c
@@ -42,7 +42,7 @@
 #include "BKE_context.h"
 
 #include "ED_armature.h"
-#include "ED_util.h"
+#include "ED_undo.h"
 
 #include "BIF_retarget.h"
 
diff --git a/source/blender/editors/armature/editarmature_undo.c b/source/blender/editors/armature/editarmature_undo.c
index 36e6ec4ba7f087bd763bf129992696bc87b91621..217de06d99b8fd9f7f704b458cebab61cbb23e0b 100644
--- a/source/blender/editors/armature/editarmature_undo.c
+++ b/source/blender/editors/armature/editarmature_undo.c
@@ -33,21 +33,32 @@
 #include "MEM_guardedalloc.h"
 
 #include "BLI_math.h"
+#include "BLI_array_utils.h"
 
 #include "BKE_context.h"
+#include "BKE_undo_system.h"
+
+#include "DEG_depsgraph.h"
 
 #include "ED_armature.h"
+#include "ED_object.h"
 #include "ED_util.h"
 
+#include "WM_types.h"
+#include "WM_api.h"
+
+/* -------------------------------------------------------------------- */
+/** \name Undo Conversion
+ * \{ */
+
 typedef struct UndoArmature {
 	EditBone *act_edbone;
 	ListBase lb;
+	size_t undo_size;
 } UndoArmature;
 
-static void undoBones_to_editBones(void *uarmv, void *armv, void *UNUSED(data))
+static void undoarm_to_editarm(UndoArmature *uarm, bArmature *arm)
 {
-	UndoArmature *uarm = uarmv;
-	bArmature *arm = armv;
 	EditBone *ebone;
 
 	ED_armature_ebone_listbase_free(arm->edbo);
@@ -65,48 +76,117 @@ static void undoBones_to_editBones(void *uarmv, void *armv, void *UNUSED(data))
 	ED_armature_ebone_listbase_temp_clear(arm->edbo);
 }
 
-static void *editBones_to_undoBones(void *armv, void *UNUSED(obdata))
+static void *undoarm_from_editarm(UndoArmature *uarm, bArmature *arm)
 {
-	bArmature *arm = armv;
-	UndoArmature *uarm;
-	EditBone *ebone;
+	BLI_assert(BLI_array_is_zeroed(uarm, 1));
 
-	uarm = MEM_callocN(sizeof(UndoArmature), "listbase undo");
+	/* TODO: include size of ID-properties. */
+	uarm->undo_size = 0;
 
 	ED_armature_ebone_listbase_copy(&uarm->lb, arm->edbo);
 
 	/* active bone */
 	if (arm->act_edbone) {
-		ebone = arm->act_edbone;
+		EditBone *ebone = arm->act_edbone;
 		uarm->act_edbone = ebone->temp.ebone;
 	}
 
 	ED_armature_ebone_listbase_temp_clear(&uarm->lb);
 
+	for (EditBone *ebone = uarm->lb.first; ebone; ebone = ebone->next) {
+		uarm->undo_size += sizeof(EditBone);
+	}
+
 	return uarm;
 }
 
-static void free_undoBones(void *uarmv)
+static void undoarm_free_data(UndoArmature *uarm)
 {
-	UndoArmature *uarm = uarmv;
-
 	ED_armature_ebone_listbase_free(&uarm->lb);
-
-	MEM_freeN(uarm);
 }
 
-static void *get_armature_edit(bContext *C)
+static Object *editarm_object_from_context(bContext *C)
 {
 	Object *obedit = CTX_data_edit_object(C);
 	if (obedit && obedit->type == OB_ARMATURE) {
-		return obedit->data;
+		bArmature *arm = obedit->data;
+		if (arm->edbo != NULL) {
+			return obedit;
+		}
 	}
 	return NULL;
 }
 
-/* and this is all the undo system needs to know */
-void undo_push_armature(bContext *C, const char *name)
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Implements ED Undo System
+ * \{ */
+
+typedef struct ArmatureUndoStep {
+	UndoStep step;
+	/* note: will split out into list for multi-object-editmode. */
+	UndoRefID_Object obedit_ref;
+	UndoArmature data;
+} ArmatureUndoStep;
+
+static bool armature_undosys_poll(bContext *C)
+{
+	return editarm_object_from_context(C) != NULL;
+}
+
+static bool armature_undosys_step_encode(struct bContext *C, UndoStep *us_p)
+{
+	ArmatureUndoStep *us = (ArmatureUndoStep *)us_p;
+	us->obedit_ref.ptr = editarm_object_from_context(C);
+	bArmature *arm = us->obedit_ref.ptr->data;
+	undoarm_from_editarm(&us->data, arm);
+	us->step.data_size = us->data.undo_size;
+	return true;
+}
+
+static void armature_undosys_step_decode(struct bContext *C, UndoStep *us_p, int UNUSED(dir))
+{
+	/* TODO(campbell): undo_system: use low-level API to set mode. */
+	ED_object_mode_set(C, OB_MODE_EDIT);
+	BLI_assert(armature_undosys_poll(C));
+
+	ArmatureUndoStep *us = (ArmatureUndoStep *)us_p;
+	Object *obedit = us->obedit_ref.ptr;
+	bArmature *arm = obedit->data;
+	undoarm_to_editarm(&us->data, arm);
+	DEG_id_tag_update(&obedit->id, OB_RECALC_DATA);
+	WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
+}
+
+static void armature_undosys_step_free(UndoStep *us_p)
+{
+	ArmatureUndoStep *us = (ArmatureUndoStep *)us_p;
+	undoarm_free_data(&us->data);
+}
+
+static void armature_undosys_foreach_ID_ref(
+        UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
 {
-	// XXX solve getdata()
-	undo_editmode_push(C, name, get_armature_edit, free_undoBones, undoBones_to_editBones, editBones_to_undoBones, NULL);
+	ArmatureUndoStep *us = (ArmatureUndoStep *)us_p;
+	foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->obedit_ref));
 }
+
+/* Export for ED_undo_sys. */
+void ED_armature_undosys_type(UndoType *ut)
+{
+	ut->name = "Edit Armature";
+	ut->poll = armature_undosys_poll;
+	ut->step_encode = armature_undosys_step_encode;
+	ut->step_decode = armature_undosys_step_decode;
+	ut->step_free = armature_undosys_step_free;
+
+	ut->step_foreach_ID_ref = armature_undosys_foreach_ID_ref;
+
+	ut->mode = BKE_UNDOTYPE_MODE_STORE;
+	ut->use_context = true;
+
+	ut->step_size = sizeof(ArmatureUndoStep);
+}
+
+/** \} */
diff --git a/source/blender/editors/armature/meshlaplacian.c b/source/blender/editors/armature/meshlaplacian.c
index d53350ab3cbac89439548859477eb0218c6d1777..489940007e4f45a9c38c63982181b96f7f266f48 100644
--- a/source/blender/editors/armature/meshlaplacian.c
+++ b/source/blender/editors/armature/meshlaplacian.c
@@ -603,7 +603,6 @@ static float heat_limit_weight(float weight)
 }
 
 void heat_bone_weighting(
-        const EvaluationContext *eval_ctx,
         Object *ob, Mesh *me, float (*verts)[3], int numsource,
         bDeformGroup **dgrouplist, bDeformGroup **dgroupflip,
         float (*root)[3], float (*tip)[3], int *selected, const char **err_str)
@@ -627,7 +626,7 @@ void heat_bone_weighting(
 	tottri = poly_to_tri_count(me->totpoly, me->totloop);
 
 	/* count triangles and create mask */
-	if (eval_ctx->object_mode & OB_MODE_WEIGHT_PAINT &&
+	if (ob->mode & OB_MODE_WEIGHT_PAINT &&
 	    (use_face_sel || use_vert_sel))
 	{
 		mask = MEM_callocN(sizeof(int) * me->totvert, "heat_bone_weighting mask");
diff --git a/source/blender/editors/armature/meshlaplacian.h b/source/blender/editors/armature/meshlaplacian.h
index c790c2fbee75464f3c99ec2f361f39060b31e6e4..05ade4fc43e2fa1d0f32ca01c928fa1a990f748a 100644
--- a/source/blender/editors/armature/meshlaplacian.h
+++ b/source/blender/editors/armature/meshlaplacian.h
@@ -53,7 +53,6 @@ float laplacian_system_get_solution(LaplacianSystem *sys, int v);
 /* Heat Weighting */
 
 void heat_bone_weighting(
-        const struct EvaluationContext *eval_ctx,
         struct Object *ob, struct Mesh *me, float (*verts)[3],
         int numbones, struct bDeformGroup **dgrouplist,
         struct bDeformGroup **dgroupflip, float (*root)[3], float (*tip)[3],
diff --git a/source/blender/editors/armature/pose_edit.c b/source/blender/editors/armature/pose_edit.c
index 3f2cf6f585316f0d64fe80e3a871d174fe32be73..15ffd09ebd460182155dd18b7269e978dddaf167 100644
--- a/source/blender/editors/armature/pose_edit.c
+++ b/source/blender/editors/armature/pose_edit.c
@@ -94,16 +94,13 @@ void ED_armature_enter_posemode(bContext *C, Base *base)
 	
 	switch (ob->type) {
 		case OB_ARMATURE:
-		{
-			WorkSpace *workspace = CTX_wm_workspace(C);
-			workspace->object_mode_restore = workspace->object_mode;
-			workspace->object_mode |= OB_MODE_POSE;
+			ob->restore_mode = ob->mode;
+			ob->mode |= OB_MODE_POSE;
 			/* Inform all CoW versions that we changed the mode. */
 			DEG_id_tag_update_ex(CTX_data_main(C), &ob->id, DEG_TAG_COPY_ON_WRITE);
 			WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_POSE, NULL);
 			
 			break;
-		}
 		default:
 			return;
 	}
@@ -115,11 +112,10 @@ void ED_armature_enter_posemode(bContext *C, Base *base)
 void ED_armature_exit_posemode(bContext *C, Base *base)
 {
 	if (base) {
-		WorkSpace *workspace = CTX_wm_workspace(C);
 		Object *ob = base->object;
 		
-		workspace->object_mode_restore = workspace->object_mode;
-		workspace->object_mode &= ~OB_MODE_POSE;
+		ob->restore_mode = ob->mode;
+		ob->mode &= ~OB_MODE_POSE;
 
 		/* Inform all CoW versions that we changed the mode. */
 		DEG_id_tag_update_ex(CTX_data_main(C), &ob->id, DEG_TAG_COPY_ON_WRITE);
diff --git a/source/blender/editors/armature/pose_select.c b/source/blender/editors/armature/pose_select.c
index 16f5bb59641be9e590731d074f098b40f5a6e099..a66cedd8d4f124c7b561bf08de431b000c13f5d8 100644
--- a/source/blender/editors/armature/pose_select.c
+++ b/source/blender/editors/armature/pose_select.c
@@ -134,7 +134,6 @@ void ED_pose_bone_select(Object *ob, bPoseChannel *pchan, bool select)
 /* called from editview.c, for mode-less pose selection */
 /* assumes scene obact and basact is still on old situation */
 bool ED_do_pose_selectbuffer(
-        const EvaluationContext *eval_ctx,
         ViewLayer *view_layer, Base *base, const unsigned int *buffer, short hits,
         bool extend, bool deselect, bool toggle, bool do_nearest)
 {
@@ -144,7 +143,7 @@ bool ED_do_pose_selectbuffer(
 	if (!ob || !ob->pose) return 0;
 
 	Object *ob_act = OBACT(view_layer);
-	Object *obedit = OBEDIT_FROM_EVAL_CTX(eval_ctx);
+	Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
 
 	nearBone = get_bone_from_selectbuffer(base, obedit, buffer, hits, 1, do_nearest);
 	
@@ -157,7 +156,7 @@ bool ED_do_pose_selectbuffer(
 		 * note, special exception for armature mode so we can do multi-select
 		 * we could check for multi-select explicitly but think its fine to
 		 * always give predictable behavior in weight paint mode - campbell */
-		if ((ob_act == NULL) || ((ob_act != ob) && (eval_ctx->object_mode & OB_MODE_WEIGHT_PAINT) == 0)) {
+		if ((ob_act == NULL) || ((ob_act != ob) && (ob_act->mode & OB_MODE_WEIGHT_PAINT) == 0)) {
 			/* when we are entering into posemode via toggle-select,
 			 * from another active object - always select the bone. */
 			if (!extend && !deselect && toggle) {
@@ -198,7 +197,7 @@ bool ED_do_pose_selectbuffer(
 		
 		if (ob_act) {
 			/* in weightpaint we select the associated vertex group too */
-			if (eval_ctx->object_mode & OB_MODE_WEIGHT_PAINT) {
+			if (ob_act->mode & OB_MODE_WEIGHT_PAINT) {
 				if (nearBone == arm->act_bone) {
 					ED_vgroup_select_by_name(ob_act, nearBone->name);
 					DEG_id_tag_update(&ob_act->id, OB_RECALC_DATA);
@@ -231,7 +230,7 @@ void ED_pose_de_selectall(Object *ob, int select_mode, const bool ignore_visibil
 		return;
 	}
 	
-	/*	Determine if we're selecting or deselecting	*/
+	/* Determine if we're selecting or deselecting */
 	if (select_mode == SEL_TOGGLE) {
 		select_mode = SEL_SELECT;
 		for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
@@ -876,7 +875,6 @@ void POSE_OT_select_grouped(wmOperatorType *ot)
  */
 static int pose_select_mirror_exec(bContext *C, wmOperator *op)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Object *ob_act = CTX_data_active_object(C);
 	Object *ob = BKE_object_pose_armature_get(ob_act);
 	bArmature *arm;
@@ -884,6 +882,10 @@ static int pose_select_mirror_exec(bContext *C, wmOperator *op)
 	const bool active_only = RNA_boolean_get(op->ptr, "only_active");
 	const bool extend = RNA_boolean_get(op->ptr, "extend");
 
+	if ((ob && (ob->mode & OB_MODE_POSE)) == 0) {
+		return OPERATOR_CANCELLED;
+	}
+
 	arm = ob->data;
 
 	for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
@@ -920,7 +922,7 @@ static int pose_select_mirror_exec(bContext *C, wmOperator *op)
 		arm->act_bone = pchan_mirror_act->bone;
 
 		/* in weightpaint we select the associated vertex group too */
-		if (workspace->object_mode & OB_MODE_WEIGHT_PAINT) {
+		if (ob_act->mode & OB_MODE_WEIGHT_PAINT) {
 			ED_vgroup_select_by_name(ob_act, pchan_mirror_act->name);
 			DEG_id_tag_update(&ob_act->id, OB_RECALC_DATA);
 		}
diff --git a/source/blender/editors/armature/pose_transform.c b/source/blender/editors/armature/pose_transform.c
index bfe365d04fdf5ecbe78aec48abc24e1571ea643e..c49591be5d1b23d2724dcc620ee0aaf62fa671da 100644
--- a/source/blender/editors/armature/pose_transform.c
+++ b/source/blender/editors/armature/pose_transform.c
@@ -532,7 +532,7 @@ static int pose_paste_exec(bContext *C, wmOperator *op)
 		return OPERATOR_CANCELLED;
 	}
 	/* Make sure data from this file is usable for pose paste. */
-	if (BLI_listbase_count_ex(&tmp_bmain->object, 2) != 1) {
+	if (BLI_listbase_count_at_most(&tmp_bmain->object, 2) != 1) {
 		BKE_report(op->reports, RPT_ERROR, "Copy buffer is not from pose mode");
 		BKE_main_free(tmp_bmain);
 		return OPERATOR_CANCELLED;
diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c
index 1787e5599139d5c6163d95f95084fe9800f54602..f0b3233e35b702a9532cbcdf2023e1a85209d220 100644
--- a/source/blender/editors/curve/editcurve.c
+++ b/source/blender/editors/curve/editcurve.c
@@ -1232,7 +1232,10 @@ void ED_curve_editnurb_make(Object *obedit)
 
 		if (actkey) {
 			// XXX strcpy(G.editModeTitleExtra, "(Key) ");
+			/* TODO(campbell): undo_system: investigate why this was needed. */
+#if 0
 			undo_editmode_clear();
+#endif
 		}
 
 		if (editnurb) {
diff --git a/source/blender/editors/curve/editcurve_add.c b/source/blender/editors/curve/editcurve_add.c
index 281f6c3c22e7650b3b1df643c93a81b922d56621..f269799973f7fb41c263d63d9db825df54904934 100644
--- a/source/blender/editors/curve/editcurve_add.c
+++ b/source/blender/editors/curve/editcurve_add.c
@@ -53,7 +53,7 @@
 
 #include "ED_object.h"
 #include "ED_screen.h"
-#include "ED_util.h"
+#include "ED_undo.h"
 #include "ED_view3d.h"
 #include "ED_curve.h"
 
diff --git a/source/blender/editors/curve/editcurve_undo.c b/source/blender/editors/curve/editcurve_undo.c
index f8f96eb3bc9c9977046d60d4fe3d51347eea7586..4eb2abaefad8e8639d1ae0056c60ea90b111fcb5 100644
--- a/source/blender/editors/curve/editcurve_undo.c
+++ b/source/blender/editors/curve/editcurve_undo.c
@@ -30,18 +30,30 @@
 
 #include "BLI_blenlib.h"
 #include "BLI_ghash.h"
+#include "BLI_array_utils.h"
 
 #include "BKE_context.h"
 #include "BKE_curve.h"
 #include "BKE_fcurve.h"
 #include "BKE_library.h"
 #include "BKE_animsys.h"
+#include "BKE_undo_system.h"
 
+#include "DEG_depsgraph.h"
+
+#include "ED_object.h"
 #include "ED_util.h"
 #include "ED_curve.h"
 
+#include "WM_types.h"
+#include "WM_api.h"
+
 #include "curve_intern.h"
 
+/* -------------------------------------------------------------------- */
+/** \name Undo Conversion
+ * \{ */
+
 typedef struct {
 	ListBase nubase;
 	int actvert;
@@ -49,13 +61,18 @@ typedef struct {
 	ListBase fcurves, drivers;
 	int actnu;
 	int flag;
+
+	/* Stored in the object, needed since users may change the active key while in edit-mode. */
+	struct {
+		short shapenr;
+	} obedit;
+
+	size_t undo_size;
 } UndoCurve;
 
-static void undoCurve_to_editCurve(void *ucu, void *UNUSED(edata), void *cu_v)
+static void undocurve_to_editcurve(UndoCurve *ucu, Curve *cu, short *r_shapenr)
 {
-	Curve *cu = cu_v;
-	UndoCurve *undoCurve = ucu;
-	ListBase *undobase = &undoCurve->nubase;
+	ListBase *undobase = &ucu->nubase;
 	ListBase *editbase = BKE_curve_editNurbs_get(cu);
 	Nurb *nu, *newnu;
 	EditNurb *editnurb = cu->editnurb;
@@ -63,19 +80,19 @@ static void undoCurve_to_editCurve(void *ucu, void *UNUSED(edata), void *cu_v)
 
 	BKE_nurbList_free(editbase);
 
-	if (undoCurve->undoIndex) {
+	if (ucu->undoIndex) {
 		BKE_curve_editNurb_keyIndex_free(&editnurb->keyindex);
-		editnurb->keyindex = ED_curve_keyindex_hash_duplicate(undoCurve->undoIndex);
+		editnurb->keyindex = ED_curve_keyindex_hash_duplicate(ucu->undoIndex);
 	}
 
 	if (ad) {
 		if (ad->action) {
 			free_fcurves(&ad->action->curves);
-			copy_fcurves(&ad->action->curves, &undoCurve->fcurves);
+			copy_fcurves(&ad->action->curves, &ucu->fcurves);
 		}
 
 		free_fcurves(&ad->drivers);
-		copy_fcurves(&ad->drivers, &undoCurve->drivers);
+		copy_fcurves(&ad->drivers, &ucu->drivers);
 	}
 
 	/* copy  */
@@ -89,75 +106,152 @@ static void undoCurve_to_editCurve(void *ucu, void *UNUSED(edata), void *cu_v)
 		BLI_addtail(editbase, newnu);
 	}
 
-	cu->actvert = undoCurve->actvert;
-	cu->actnu = undoCurve->actnu;
-	cu->flag = undoCurve->flag;
+	cu->actvert = ucu->actvert;
+	cu->actnu = ucu->actnu;
+	cu->flag = ucu->flag;
+	*r_shapenr = ucu->obedit.shapenr;
 	ED_curve_updateAnimPaths(cu);
 }
 
-static void *editCurve_to_undoCurve(void *UNUSED(edata), void *cu_v)
+static void undocurve_from_editcurve(UndoCurve *ucu, Curve *cu, const short shapenr)
 {
-	Curve *cu = cu_v;
+	BLI_assert(BLI_array_is_zeroed(ucu, 1));
 	ListBase *nubase = BKE_curve_editNurbs_get(cu);
-	UndoCurve *undoCurve;
 	EditNurb *editnurb = cu->editnurb, tmpEditnurb;
 	Nurb *nu, *newnu;
 	AnimData *ad = BKE_animdata_from_id(&cu->id);
 
-	undoCurve = MEM_callocN(sizeof(UndoCurve), "undoCurve");
+	/* TODO: include size of fcurve & undoIndex */
+	// ucu->undo_size = 0;
 
 	if (editnurb->keyindex) {
-		undoCurve->undoIndex = ED_curve_keyindex_hash_duplicate(editnurb->keyindex);
-		tmpEditnurb.keyindex = undoCurve->undoIndex;
+		ucu->undoIndex = ED_curve_keyindex_hash_duplicate(editnurb->keyindex);
+		tmpEditnurb.keyindex = ucu->undoIndex;
 	}
 
 	if (ad) {
 		if (ad->action)
-			copy_fcurves(&undoCurve->fcurves, &ad->action->curves);
+			copy_fcurves(&ucu->fcurves, &ad->action->curves);
 
-		copy_fcurves(&undoCurve->drivers, &ad->drivers);
+		copy_fcurves(&ucu->drivers, &ad->drivers);
 	}
 
 	/* copy  */
 	for (nu = nubase->first; nu; nu = nu->next) {
 		newnu = BKE_nurb_duplicate(nu);
 
-		if (undoCurve->undoIndex) {
+		if (ucu->undoIndex) {
 			ED_curve_keyindex_update_nurb(&tmpEditnurb, nu, newnu);
 		}
 
-		BLI_addtail(&undoCurve->nubase, newnu);
+		BLI_addtail(&ucu->nubase, newnu);
+
+		ucu->undo_size += (
+		        (nu->bezt ? (sizeof(BezTriple) * nu->pntsu) : 0) +
+		        (nu->bp ? (sizeof(BPoint) * (nu->pntsu * nu->pntsv)) : 0) +
+		        (nu->knotsu ? (sizeof(float) * KNOTSU(nu)) : 0) +
+		        (nu->knotsv ? (sizeof(float) * KNOTSV(nu)) : 0) +
+		        sizeof(Nurb));
 	}
 
-	undoCurve->actvert = cu->actvert;
-	undoCurve->actnu = cu->actnu;
-	undoCurve->flag = cu->flag;
+	ucu->actvert = cu->actvert;
+	ucu->actnu = cu->actnu;
+	ucu->flag = cu->flag;
 
-	return undoCurve;
+	ucu->obedit.shapenr = shapenr;
 }
 
-static void free_undoCurve(void *ucv)
+static void undocurve_free_data(UndoCurve *uc)
 {
-	UndoCurve *undoCurve = ucv;
+	BKE_nurbList_free(&uc->nubase);
+
+	BKE_curve_editNurb_keyIndex_free(&uc->undoIndex);
+
+	free_fcurves(&uc->fcurves);
+	free_fcurves(&uc->drivers);
+}
+
+static Object *editcurve_object_from_context(bContext *C)
+{
+	Object *obedit = CTX_data_edit_object(C);
+	if (obedit && ELEM(obedit->type, OB_CURVE, OB_SURF)) {
+		Curve *cu = obedit->data;
+		if (BKE_curve_editNurbs_get(cu) != NULL) {
+			return obedit;
+		}
+	}
+	return NULL;
+}
 
-	BKE_nurbList_free(&undoCurve->nubase);
+/** \} */
 
-	BKE_curve_editNurb_keyIndex_free(&undoCurve->undoIndex);
+/* -------------------------------------------------------------------- */
+/** \name Implements ED Undo System
+ * \{ */
 
-	free_fcurves(&undoCurve->fcurves);
-	free_fcurves(&undoCurve->drivers);
+typedef struct CurveUndoStep {
+	UndoStep step;
+	/* note: will split out into list for multi-object-editmode. */
+	UndoRefID_Object obedit_ref;
+	UndoCurve data;
+} CurveUndoStep;
 
-	MEM_freeN(undoCurve);
+static bool curve_undosys_poll(bContext *C)
+{
+	Object *obedit = editcurve_object_from_context(C);
+	return (obedit != NULL);
 }
 
-static void *get_data(bContext *C)
+static bool curve_undosys_step_encode(struct bContext *C, UndoStep *us_p)
 {
-	Object *obedit = CTX_data_edit_object(C);
-	return obedit;
+	CurveUndoStep *us = (CurveUndoStep *)us_p;
+	us->obedit_ref.ptr = editcurve_object_from_context(C);
+	undocurve_from_editcurve(&us->data, us->obedit_ref.ptr->data, us->obedit_ref.ptr->shapenr);
+	us->step.data_size = us->data.undo_size;
+	return true;
+}
+
+static void curve_undosys_step_decode(struct bContext *C, UndoStep *us_p, int UNUSED(dir))
+{
+	/* TODO(campbell): undo_system: use low-level API to set mode. */
+	ED_object_mode_set(C, OB_MODE_EDIT);
+	BLI_assert(curve_undosys_poll(C));
+
+	CurveUndoStep *us = (CurveUndoStep *)us_p;
+	Object *obedit = us->obedit_ref.ptr;
+	undocurve_to_editcurve(&us->data, obedit->data, &obedit->shapenr);
+	DEG_id_tag_update(&obedit->id, OB_RECALC_DATA);
+	WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
+}
+
+static void curve_undosys_step_free(UndoStep *us_p)
+{
+	CurveUndoStep *us = (CurveUndoStep *)us_p;
+	undocurve_free_data(&us->data);
 }
 
-/* and this is all the undo system needs to know */
-void undo_push_curve(bContext *C, const char *name)
+static void curve_undosys_foreach_ID_ref(
+        UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
 {
-	undo_editmode_push(C, name, get_data, free_undoCurve, undoCurve_to_editCurve, editCurve_to_undoCurve, NULL);
+	CurveUndoStep *us = (CurveUndoStep *)us_p;
+	foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->obedit_ref));
 }
+
+/* Export for ED_undo_sys. */
+void ED_curve_undosys_type(UndoType *ut)
+{
+	ut->name = "Edit Curve";
+	ut->poll = curve_undosys_poll;
+	ut->step_encode = curve_undosys_step_encode;
+	ut->step_decode = curve_undosys_step_decode;
+	ut->step_free = curve_undosys_step_free;
+
+	ut->step_foreach_ID_ref = curve_undosys_foreach_ID_ref;
+
+	ut->mode = BKE_UNDOTYPE_MODE_STORE;
+	ut->use_context = true;
+
+	ut->step_size = sizeof(CurveUndoStep);
+}
+
+/** \} */
diff --git a/source/blender/editors/curve/editfont_undo.c b/source/blender/editors/curve/editfont_undo.c
index a61f863b61e9db17ca953677f241d119c74bc93c..d4d48e93f43c5fe8df6231b4095fd1e105180bac 100644
--- a/source/blender/editors/curve/editfont_undo.c
+++ b/source/blender/editors/curve/editfont_undo.c
@@ -29,6 +29,8 @@
 #include "MEM_guardedalloc.h"
 
 #include "BLI_utildefines.h"
+#include "BLI_array_utils.h"
+
 
 #include "DNA_curve_types.h"
 #include "DNA_object_types.h"
@@ -36,10 +38,17 @@
 
 #include "BKE_context.h"
 #include "BKE_font.h"
+#include "BKE_undo_system.h"
+
+#include "DEG_depsgraph.h"
 
+#include "ED_object.h"
 #include "ED_curve.h"
 #include "ED_util.h"
 
+#include "WM_types.h"
+#include "WM_api.h"
+
 #define USE_ARRAY_STORE
 
 #ifdef USE_ARRAY_STORE
@@ -50,6 +59,10 @@
 #  define ARRAY_CHUNK_SIZE 32
 #endif
 
+/* -------------------------------------------------------------------- */
+/** \name Undo Conversion
+ * \{ */
+
 typedef struct UndoFont {
 	wchar_t *textbuf;
 	struct CharInfo *textbufinfo;
@@ -62,6 +75,8 @@ typedef struct UndoFont {
 		BArrayState *textbufinfo;
 	} store;
 #endif
+
+	size_t undo_size;
 } UndoFont;
 
 
@@ -202,23 +217,20 @@ static void uf_arraystore_free(UndoFont *uf)
 
 		BLI_array_store_at_size_clear(&uf_arraystore.bs_stride);
 	}
-
 }
 
 /** \} */
 
 #endif  /* USE_ARRAY_STORE */
 
-static void undoFont_to_editFont(void *uf_v, void *ecu, void *UNUSED(obdata))
+static void undofont_to_editfont(UndoFont *uf, Curve *cu)
 {
-	Curve *cu = (Curve *)ecu;
 	EditFont *ef = cu->editfont;
-	const UndoFont *uf = uf_v;
 
 	size_t final_size;
 
 #ifdef USE_ARRAY_STORE
-	uf_arraystore_expand(uf_v);
+	uf_arraystore_expand(uf);
 #endif
 
 	final_size = sizeof(wchar_t) * (uf->len + 1);
@@ -233,16 +245,17 @@ static void undoFont_to_editFont(void *uf_v, void *ecu, void *UNUSED(obdata))
 	ef->selstart = ef->selend = 0;
 
 #ifdef USE_ARRAY_STORE
-	uf_arraystore_expand_clear(uf_v);
+	uf_arraystore_expand_clear(uf);
 #endif
 }
 
-static void *editFont_to_undoFont(void *ecu, void *UNUSED(obdata))
+static void *undofont_from_editfont(UndoFont *uf, Curve *cu)
 {
-	Curve *cu = (Curve *)ecu;
+	BLI_assert(BLI_array_is_zeroed(uf, 1));
+
 	EditFont *ef = cu->editfont;
 
-	UndoFont *uf = MEM_callocN(sizeof(*uf), __func__);
+	size_t mem_used_prev = MEM_get_memory_in_use();
 
 	size_t final_size;
 
@@ -269,13 +282,15 @@ static void *editFont_to_undoFont(void *ecu, void *UNUSED(obdata))
 	}
 #endif
 
+	size_t mem_used_curr = MEM_get_memory_in_use();
+
+	uf->undo_size = mem_used_prev < mem_used_curr ? mem_used_curr - mem_used_prev : sizeof(UndoFont);
+
 	return uf;
 }
 
-static void free_undoFont(void *uf_v)
+static void undofont_free_data(UndoFont *uf)
 {
-	UndoFont *uf = uf_v;
-
 #ifdef USE_ARRAY_STORE
 	{
 		LinkData *link = BLI_findptr(&uf_arraystore.local_links, uf, offsetof(LinkData, data));
@@ -291,21 +306,91 @@ static void free_undoFont(void *uf_v)
 	if (uf->textbufinfo) {
 		MEM_freeN(uf->textbufinfo);
 	}
-
-	MEM_freeN(uf);
 }
 
-static void *get_undoFont(bContext *C)
+static Object *editfont_object_from_context(bContext *C)
 {
 	Object *obedit = CTX_data_edit_object(C);
 	if (obedit && obedit->type == OB_FONT) {
-		return obedit->data;
+		Curve *cu = obedit->data;
+		EditFont *ef = cu->editfont;
+		if (ef != NULL) {
+			return obedit;
+		}
 	}
 	return NULL;
 }
 
-/* and this is all the undo system needs to know */
-void undo_push_font(bContext *C, const char *name)
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Implements ED Undo System
+ * \{ */
+
+typedef struct FontUndoStep {
+	UndoStep step;
+	/* note: will split out into list for multi-object-editmode. */
+	UndoRefID_Object obedit_ref;
+	UndoFont data;
+} FontUndoStep;
+
+static bool font_undosys_poll(bContext *C)
 {
-	undo_editmode_push(C, name, get_undoFont, free_undoFont, undoFont_to_editFont, editFont_to_undoFont, NULL);
+	return editfont_object_from_context(C) != NULL;
 }
+
+static bool font_undosys_step_encode(struct bContext *C, UndoStep *us_p)
+{
+	FontUndoStep *us = (FontUndoStep *)us_p;
+	us->obedit_ref.ptr = editfont_object_from_context(C);
+	Curve *cu = us->obedit_ref.ptr->data;
+	undofont_from_editfont(&us->data, cu);
+	us->step.data_size = us->data.undo_size;
+	return true;
+}
+
+static void font_undosys_step_decode(struct bContext *C, UndoStep *us_p, int UNUSED(dir))
+{
+	/* TODO(campbell): undo_system: use low-level API to set mode. */
+	ED_object_mode_set(C, OB_MODE_EDIT);
+	BLI_assert(font_undosys_poll(C));
+
+	FontUndoStep *us = (FontUndoStep *)us_p;
+	Object *obedit = us->obedit_ref.ptr;
+	Curve *cu = obedit->data;
+	undofont_to_editfont(&us->data, cu);
+	DEG_id_tag_update(&obedit->id, OB_RECALC_DATA);
+	WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
+}
+
+static void font_undosys_step_free(UndoStep *us_p)
+{
+	FontUndoStep *us = (FontUndoStep *)us_p;
+	undofont_free_data(&us->data);
+}
+
+static void font_undosys_foreach_ID_ref(
+        UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
+{
+	FontUndoStep *us = (FontUndoStep *)us_p;
+	foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->obedit_ref));
+}
+
+/* Export for ED_undo_sys. */
+void ED_font_undosys_type(UndoType *ut)
+{
+	ut->name = "Edit Font";
+	ut->poll = font_undosys_poll;
+	ut->step_encode = font_undosys_step_encode;
+	ut->step_decode = font_undosys_step_decode;
+	ut->step_free = font_undosys_step_free;
+
+	ut->step_foreach_ID_ref = font_undosys_foreach_ID_ref;
+
+	ut->mode = BKE_UNDOTYPE_MODE_STORE;
+	ut->use_context = true;
+
+	ut->step_size = sizeof(FontUndoStep);
+}
+
+/** \} */
diff --git a/source/blender/editors/gpencil/gpencil_brush.c b/source/blender/editors/gpencil/gpencil_brush.c
index 38927cf91e1dedc6c34163d8b46c10a3171ef876..533ab21dbb601088ad47288dbee5937d1cd36061 100644
--- a/source/blender/editors/gpencil/gpencil_brush.c
+++ b/source/blender/editors/gpencil/gpencil_brush.c
@@ -54,7 +54,6 @@
 #include "DNA_object_types.h"
 
 #include "BKE_context.h"
-#include "BKE_global.h"
 #include "BKE_gpencil.h"
 #include "BKE_library.h"
 #include "BKE_report.h"
diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c
index 8d45c6af686425e6ab51f417a784ee373ebd3c64..2c3c9f4f9b902fc2d5a395bd8c76bd4e6d87aaa8 100644
--- a/source/blender/editors/gpencil/gpencil_convert.c
+++ b/source/blender/editors/gpencil/gpencil_convert.c
@@ -1294,7 +1294,7 @@ static int gp_convert_poll(bContext *C)
 	bGPDframe *gpf = NULL;
 	ScrArea *sa = CTX_wm_area(C);
 	Scene *scene = CTX_data_scene(C);
-	const WorkSpace *workspace = CTX_wm_workspace(C);
+	ViewLayer *view_layer = CTX_data_view_layer(C);
 	
 	/* only if the current view is 3D View, if there's valid data (i.e. at least one stroke!),
 	 * and if we are not in edit mode!
@@ -1303,7 +1303,7 @@ static int gp_convert_poll(bContext *C)
 	        (gpl = BKE_gpencil_layer_getactive(gpd)) &&
 	        (gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0)) &&
 	        (gpf->strokes.first) &&
-	        ((workspace->object_mode & OB_MODE_EDIT) == 0));
+	        (OBEDIT_FROM_VIEW_LAYER(view_layer) == NULL));
 }
 
 static int gp_convert_layer_exec(bContext *C, wmOperator *op)
diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c
index 5bd5c9c74b96b98727c5ef1f53a0482f1798a6c0..9d183222c2d1fc2ff981cf2442ef451720b75a1a 100644
--- a/source/blender/editors/gpencil/gpencil_data.c
+++ b/source/blender/editors/gpencil/gpencil_data.c
@@ -53,7 +53,6 @@
 #include "DNA_gpencil_types.h"
 
 #include "BKE_context.h"
-#include "BKE_global.h"
 #include "BKE_gpencil.h"
 #include "BKE_library.h"
 #include "BKE_object.h"
@@ -1063,7 +1062,7 @@ static int gp_brush_remove_exec(bContext *C, wmOperator *op)
 	if (ELEM(NULL, ts, brush))
 		return OPERATOR_CANCELLED;
 
-	if (BLI_listbase_count_ex(&ts->gp_brushes, 2) < 2) {
+	if (BLI_listbase_count_at_most(&ts->gp_brushes, 2) < 2) {
 		BKE_report(op->reports, RPT_ERROR, "Grease Pencil needs a brush, unable to delete the last one");
 		return OPERATOR_CANCELLED;
 	}
@@ -1421,7 +1420,7 @@ static int gp_palette_remove_exec(bContext *C, wmOperator *op)
 	if (ELEM(NULL, gpd, palette))
 		return OPERATOR_CANCELLED;
 
-	if (BLI_listbase_count_ex(&gpd->palettes, 2) < 2) {
+	if (BLI_listbase_count_at_most(&gpd->palettes, 2) < 2) {
 		BKE_report(op->reports, RPT_ERROR, "Grease Pencil needs a palette, unable to delete the last one");
 		return OPERATOR_CANCELLED;
 	}
diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c
index 22a3224e5639561737c8e5c431cb9723f3f76a11..1eee774fd3e8eeddc5783a2dbba6d4b640503c35 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.c
@@ -55,7 +55,6 @@
 #include "DNA_gpencil_types.h"
 
 #include "BKE_context.h"
-#include "BKE_global.h"
 #include "BKE_gpencil.h"
 #include "BKE_library.h"
 #include "BKE_report.h"
diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c
index c25e4d3ea7346327fe6564c02e9e45eda6d9b037..77e5dc8fe5d6f2f481e286e4a2b3234e4f83acc7 100644
--- a/source/blender/editors/gpencil/gpencil_paint.c
+++ b/source/blender/editors/gpencil/gpencil_paint.c
@@ -46,7 +46,6 @@
 
 #include "PIL_time.h"
 
-#include "BKE_main.h"
 #include "BKE_paint.h"
 #include "BKE_gpencil.h"
 #include "BKE_context.h"
diff --git a/source/blender/editors/gpencil/gpencil_undo.c b/source/blender/editors/gpencil/gpencil_undo.c
index 7a9ad2b32c054a2c5c9b7b54e7a3666133e49618..202d7630ae0632d65e0bdf30e31c386fcebba840 100644
--- a/source/blender/editors/gpencil/gpencil_undo.c
+++ b/source/blender/editors/gpencil/gpencil_undo.c
@@ -45,7 +45,6 @@
 #include "BKE_context.h"
 #include "BKE_global.h"
 #include "BKE_gpencil.h"
-#include "BKE_main.h"
 
 #include "ED_gpencil.h"
 
diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h
index 51d8f8edb448411938c21ba7ee5210bd220b5999..0181cc20cdc0da81773df2e6a2accfe923e2d12b 100644
--- a/source/blender/editors/include/ED_armature.h
+++ b/source/blender/editors/include/ED_armature.h
@@ -52,6 +52,7 @@ struct ViewContext;
 struct wmKeyConfig;
 struct wmOperator;
 struct Main;
+struct UndoType;
 
 typedef struct EditBone {
 	struct EditBone *next, *prev;
@@ -142,20 +143,21 @@ void ED_armature_deselect_all(struct Object *obedit);
 void ED_armature_deselect_all_visible(struct Object *obedit);
 
 bool ED_do_pose_selectbuffer(
-        const struct EvaluationContext *eval_ctx,
         struct ViewLayer *view_layer, struct Base *base, const unsigned int *buffer, short hits,
         bool extend, bool deselect, bool toggle, bool do_nearest);
 bool ED_armature_select_pick(struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle);
 int join_armature_exec(struct bContext *C, struct wmOperator *op);
 struct Bone *get_indexed_bone(struct Object *ob, int index);
 float ED_rollBoneToVector(EditBone *bone, const float new_up_axis[3], const bool axis_only);
-EditBone *ED_armature_bone_find_name(const ListBase *edbo, const char *name);
+EditBone *ED_armature_bone_find_name(const struct ListBase *edbo, const char *name);
 EditBone *ED_armature_bone_get_mirrored(const struct ListBase *edbo, EditBone *ebo);
 void ED_armature_sync_selection(struct ListBase *edbo);
 void ED_armature_validate_active(struct bArmature *arm);
 
 EditBone *ED_armature_edit_bone_add_primitive(struct Object *obedit_arm, float length, bool view_aligned);
 EditBone *ED_armature_edit_bone_add(struct bArmature *arm, const char *name);
+
+void ED_armature_edit_bone_remove_ex(struct bArmature *arm, EditBone *exBone, bool clear_connected);
 void ED_armature_edit_bone_remove(struct bArmature *arm, EditBone *exBone);
 
 bool ED_armature_ebone_is_child_recursive(EditBone *ebone_parent, EditBone *ebone_child);
@@ -187,8 +189,6 @@ void unique_editbone_name(struct ListBase *ebones, char *name, EditBone *bone);
 void ED_armature_bone_rename(struct bArmature *arm, const char *oldnamep, const char *newnamep);
 void ED_armature_bones_flip_names(struct bArmature *arm, struct ListBase *bones_names, const bool do_strip_numbers);
 
-void undo_push_armature(struct bContext *C, const char *name);
-
 /* low level selection functions which handle */
 int  ED_armature_ebone_selectflag_get(const EditBone *ebone);
 void ED_armature_ebone_selectflag_set(EditBone *ebone, int flag);
@@ -196,6 +196,9 @@ void ED_armature_ebone_select_set(EditBone *ebone, bool select);
 void ED_armature_ebone_selectflag_enable(EditBone *ebone, int flag);
 void ED_armature_ebone_selectflag_disable(EditBone *ebone, int flag);
 
+/* editarmature_undo.c */
+void ED_armature_undosys_type(struct UndoType *ut);
+
 /* armature_utils.c */
 void ED_armature_ebone_listbase_temp_clear(struct ListBase *lb);
 void ED_armature_ebone_listbase_free(struct ListBase *lb);
diff --git a/source/blender/editors/include/ED_curve.h b/source/blender/editors/include/ED_curve.h
index d45e52d4c5a485893e891e1454b12e3cbc9edd1f..da726cb8000e2327e9edb141c45ae125227717ae 100644
--- a/source/blender/editors/include/ED_curve.h
+++ b/source/blender/editors/include/ED_curve.h
@@ -41,6 +41,7 @@ struct Curve;
 struct EditNurb;
 struct BezTriple;
 struct BPoint;
+struct UndoType;
 
 /* curve_ops.c */
 void    ED_operatortypes_curve(void);
@@ -48,7 +49,7 @@ void    ED_operatormacros_curve(void);
 void    ED_keymap_curve(struct wmKeyConfig *keyconf);
 
 /* editcurve.c */
-ListBase *object_editcurve_get(struct Object *ob);
+struct ListBase *object_editcurve_get(struct Object *ob);
 
 void    ED_curve_editnurb_load(struct Object *obedit);
 void    ED_curve_editnurb_make(struct Object *obedit);
@@ -72,7 +73,7 @@ void ED_curve_select_all(struct EditNurb *editnurb);
 void ED_curve_select_swap(struct EditNurb *editnurb, bool hide_handles);
 
 /* editcurve_undo.c */
-void undo_push_curve(struct bContext *C, const char *name);
+void ED_curve_undosys_type(struct UndoType *ut);
 
 /* editfont.c */
 void    ED_curve_editfont_load(struct Object *obedit);
@@ -91,7 +92,8 @@ bool ED_curve_active_center(struct Curve *cu, float center[3]);
 bool ED_curve_editfont_select_pick(struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle);
 
 /* editfont_undo.c */
-void    undo_push_font(struct bContext *C, const char *name);
+void ED_font_undosys_type(struct UndoType *ut);
+
 
 #if 0
 /* debug only */
diff --git a/source/blender/editors/include/ED_image.h b/source/blender/editors/include/ED_image.h
index 9802f8c3c03e274f857b70f04fbf8b191e7925c2..cb824b3c9b7c7820957c8b589c2681e39afa7c44 100644
--- a/source/blender/editors/include/ED_image.h
+++ b/source/blender/editors/include/ED_image.h
@@ -40,7 +40,6 @@ struct wmWindowManager;
 struct ARegion;
 struct Scene;
 struct ViewLayer;
-struct WorkSpace;
 
 /* image_edit.c, exported for transform */
 struct Image *ED_space_image(struct SpaceImage *sima);
@@ -76,10 +75,7 @@ bool ED_space_image_show_uvedit(struct SpaceImage *sima, struct Object *obedit);
 
 bool ED_space_image_paint_curve(const struct bContext *C);
 
-bool ED_space_image_check_show_maskedit(
-        struct SpaceImage *sima,
-        const struct WorkSpace *workspace,
-        struct ViewLayer *view_layer);
+bool ED_space_image_check_show_maskedit(struct SpaceImage *sima, struct ViewLayer *view_layer);
 int ED_space_image_maskedit_poll(struct bContext *C);
 int ED_space_image_maskedit_mask_poll(struct bContext *C);
 
diff --git a/source/blender/editors/include/ED_info.h b/source/blender/editors/include/ED_info.h
index 3d873906ecfe39a4bb3b9cce31a68e7b7f1ab628..072b1a135a38f5af85f9ed196caceff1e40f9582 100644
--- a/source/blender/editors/include/ED_info.h
+++ b/source/blender/editors/include/ED_info.h
@@ -27,11 +27,8 @@
 #ifndef __ED_INFO_H__
 #define __ED_INFO_H__
 
-struct EvaluationContext;
-
 /* info_stats.c */
 void ED_info_stats_clear(struct ViewLayer *view_layer);
-const char *ED_info_stats_string(
-        struct Scene *scene, struct WorkSpace *workspace, struct ViewLayer *view_layer);
+const char *ED_info_stats_string(struct Scene *scene, struct ViewLayer *view_layer);
 
 #endif /*  __ED_INFO_H__ */
diff --git a/source/blender/editors/include/ED_lattice.h b/source/blender/editors/include/ED_lattice.h
index b652fb4c00b6ea24f6917d65a208e7124d6b0953..b30929f53079d5b495141e5a101b28e6137cb835 100644
--- a/source/blender/editors/include/ED_lattice.h
+++ b/source/blender/editors/include/ED_lattice.h
@@ -31,6 +31,8 @@
 #define __ED_LATTICE_H__
 
 struct wmKeyConfig;
+struct UndoType;
+struct Object;
 
 /* lattice_ops.c */
 void ED_operatortypes_lattice(void);
@@ -41,6 +43,6 @@ void ED_lattice_flags_set(struct Object *obedit, int flag);
 bool ED_lattice_select_pick(struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle);
 
 /* editlattice_undo.c */
-void undo_push_lattice(struct bContext *C, const char *name);
+void ED_lattice_undosys_type(struct UndoType *ut);
 
 #endif  /* __ED_LATTICE_H__ */
diff --git a/source/blender/editors/include/ED_mball.h b/source/blender/editors/include/ED_mball.h
index 232d7d1d23425316d32ea259cd99a8ff60909707..9982c87a76449a30506011706eb6a728afc66adf 100644
--- a/source/blender/editors/include/ED_mball.h
+++ b/source/blender/editors/include/ED_mball.h
@@ -34,6 +34,7 @@
 struct bContext;
 struct Object;
 struct wmKeyConfig;
+struct UndoType;
 
 void ED_operatortypes_metaball(void);
 void ED_operatormacros_metaball(void);
@@ -47,6 +48,7 @@ void ED_mball_editmball_free(struct Object *obedit);
 void ED_mball_editmball_make(struct Object *obedit);
 void ED_mball_editmball_load(struct Object *obedit);
 
-void undo_push_mball(struct bContext *C, const char *name);
+/* editmball_undo.c */
+void ED_mball_undosys_type(struct UndoType *ut);
 
 #endif  /* __ED_MBALL_H__ */
diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h
index 4e25284dfa3a24d3621837814f83c9cc3e829f22..3217433204ea46e7187bf28e1e56ae31cbf4e82a 100644
--- a/source/blender/editors/include/ED_mesh.h
+++ b/source/blender/editors/include/ED_mesh.h
@@ -36,7 +36,6 @@ extern "C" {
 #endif
 
 struct ID;
-struct EvaluationContext;
 struct View3D;
 struct ARegion;
 struct bContext;
@@ -63,6 +62,7 @@ struct UvMapVert;
 struct ToolSettings;
 struct Object;
 struct rcti;
+struct UndoType;
 
 /* editmesh_utils.c */
 void           EDBM_verts_mirror_cache_begin_ex(struct BMEditMesh *em, const int axis,
@@ -99,8 +99,6 @@ void EDBM_selectmode_flush(struct BMEditMesh *em);
 void EDBM_deselect_flush(struct BMEditMesh *em);
 void EDBM_select_flush(struct BMEditMesh *em);
 
-void undo_push_mesh(struct bContext *C, const char *name);
-
 bool EDBM_vert_color_check(struct BMEditMesh *em);
 
 void EDBM_mesh_hide(struct BMEditMesh *em, bool swap);
@@ -131,6 +129,9 @@ bool BMBVH_EdgeVisible(struct BMBVHTree *tree, struct BMEdge *e,
                        const struct Depsgraph *depsgraph,
                        struct ARegion *ar, struct View3D *v3d, struct Object *obedit);
 
+/* editmesh_undo.c */
+void ED_mesh_undosys_type(struct UndoType *ut);
+
 /* editmesh_select.c */
 void EDBM_select_mirrored(
         struct BMEditMesh *em, const int axis, const bool extend,
@@ -265,7 +266,6 @@ void                 ED_vgroup_vert_remove(struct Object *ob, struct bDeformGrou
 float                ED_vgroup_vert_weight(struct Object *ob, struct bDeformGroup *dg, int vertnum);
 void                 ED_vgroup_vert_active_mirror(struct Object *ob, int def_nr);
 
-
 /* mesh_data.c */
 // void ED_mesh_geometry_add(struct Mesh *mesh, struct ReportList *reports, int verts, int edges, int faces);
 void ED_mesh_polys_add(struct Mesh *mesh, struct ReportList *reports, int count);
diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h
index a116cf5e5d0aa93c99d35478abcd32699b6a509f..ebd9313f7c5ee2325c38bb4972c085db4b3744aa 100644
--- a/source/blender/editors/include/ED_object.h
+++ b/source/blender/editors/include/ED_object.h
@@ -52,13 +52,12 @@ struct wmKeyConfig;
 struct wmKeyMap;
 struct wmOperator;
 struct wmOperatorType;
+struct wmWindow;
+struct wmWindowManager;
 struct PointerRNA;
 struct PropertyRNA;
 struct EnumPropertyItem;
 struct EvaluationContext;
-struct WorkSpace;
-struct wmWindow;
-struct wmWindowManager;
 
 #include "DNA_object_enums.h"
 
@@ -126,7 +125,7 @@ void ED_object_parent(struct Object *ob, struct Object *parent, const int type,
 #define EM_DO_UNDO      8
 #define EM_IGNORE_LAYER 16
 void ED_object_editmode_exit_ex(
-        struct bContext *C, struct WorkSpace *workspace, struct Scene *scene, struct Object *obedit, int flag);
+        struct bContext *C, struct Scene *scene, struct Object *obedit, int flag);
 void ED_object_editmode_exit(struct bContext *C, int flag);
 void ED_object_editmode_enter(struct bContext *C, int flag);
 bool ED_object_editmode_load(struct Object *obedit);
@@ -136,26 +135,26 @@ bool ED_object_editmode_calc_active_center(struct Object *obedit, const bool sel
 
 void ED_object_vpaintmode_enter_ex(
         const struct EvaluationContext *eval_ctx, struct wmWindowManager *wm,
-        struct WorkSpace *workspace, struct Scene *scene, struct Object *ob);
+        struct Scene *scene, struct Object *ob);
 void ED_object_vpaintmode_enter(struct bContext *C);
 void ED_object_wpaintmode_enter_ex(
         const struct EvaluationContext *eval_ctx, struct wmWindowManager *wm,
-        struct WorkSpace *workspace, struct Scene *scene, struct Object *ob);
+        struct Scene *scene, struct Object *ob);
 void ED_object_wpaintmode_enter(struct bContext *C);
 
-void ED_object_vpaintmode_exit_ex(struct WorkSpace *workspace, struct Object *ob);
+void ED_object_vpaintmode_exit_ex(struct Object *ob);
 void ED_object_vpaintmode_exit(struct bContext *C);
-void ED_object_wpaintmode_exit_ex(struct WorkSpace *workspace, struct Object *ob);
+void ED_object_wpaintmode_exit_ex(struct Object *ob);
 void ED_object_wpaintmode_exit(struct bContext *C);
 
 void ED_object_sculptmode_enter_ex(
         const struct EvaluationContext *eval_ctx,
-        struct WorkSpace *workspace, struct Scene *scene, struct Object *ob,
+        struct Scene *scene, struct Object *ob,
         struct ReportList *reports);
 void ED_object_sculptmode_enter(struct bContext *C, struct ReportList *reports);
 void ED_object_sculptmode_exit_ex(
         const struct EvaluationContext *eval_ctx,
-        struct WorkSpace *workspace, struct Scene *scene, struct Object *ob);
+        struct Scene *scene, struct Object *ob);
 void ED_object_sculptmode_exit(struct bContext *C);
 
 void ED_object_location_from_view(struct bContext *C, float loc[3]);
@@ -190,9 +189,9 @@ void ED_objects_clear_paths(struct bContext *C, bool only_selected);
 void ED_objects_recalculate_paths(struct bContext *C, struct Scene *scene);
 
 /* constraints */
-struct ListBase *get_active_constraints(const struct EvaluationContext *eval_ctx, struct Object *ob);
+struct ListBase *get_active_constraints(struct Object *ob);
 struct ListBase *get_constraint_lb(struct Object *ob, struct bConstraint *con, struct bPoseChannel **r_pchan);
-struct bConstraint *get_active_constraint(const struct EvaluationContext *eval_ctx, struct Object *ob);
+struct bConstraint *get_active_constraint(struct Object *ob);
 
 void object_test_constraints(struct Object *ob);
 
@@ -205,26 +204,20 @@ void ED_object_constraint_dependency_tag_update(struct Main *bmain, struct Objec
 
 /* object_modes.c */
 bool ED_object_mode_compat_test(const struct Object *ob, eObjectMode mode);
-bool ED_object_mode_compat_set(struct bContext *C, struct WorkSpace *workspace, eObjectMode mode, struct ReportList *reports);
+bool ED_object_mode_compat_set(struct bContext *C, struct Object *ob, eObjectMode mode, struct ReportList *reports);
 void ED_object_mode_toggle(struct bContext *C, eObjectMode mode);
+void ED_object_mode_set(struct bContext *C, eObjectMode mode);
 
 bool ED_object_mode_generic_enter(
         struct bContext *C,
         eObjectMode object_mode);
 void ED_object_mode_generic_exit(
         const struct EvaluationContext *eval_ctx,
-        struct WorkSpace *workspace, struct Scene *scene, struct Object *ob);
+        struct Scene *scene, struct Object *ob);
 bool ED_object_mode_generic_has_data(
         const struct EvaluationContext *eval_ctx,
         struct Object *ob);
 
-bool ED_object_mode_generic_enter_or_other_window(
-        struct bContext *C, const struct wmWindow *win_compare,
-        eObjectMode object_mode);
-void ED_object_mode_generic_exit_or_other_window(
-        const struct EvaluationContext *eval_ctx, struct wmWindowManager *wm,
-        struct WorkSpace *workspace, struct Scene *scene, struct Object *ob);
-
 bool ED_object_mode_generic_exists(
         struct wmWindowManager *wm, struct Object *ob,
         eObjectMode object_mode);
@@ -235,30 +228,25 @@ enum {
 	MODIFIER_APPLY_SHAPE
 };
 
-struct ModifierData *ED_object_modifier_add(
-        struct ReportList *reports, struct Main *bmain, struct Scene *scene,
-        struct Object *ob, eObjectMode object_mode, const char *name, int type);
+struct ModifierData *ED_object_modifier_add(struct ReportList *reports, struct Main *bmain, struct Scene *scene,
+                                            struct Object *ob, const char *name, int type);
 bool ED_object_modifier_remove(struct ReportList *reports, struct Main *bmain,
                                struct Object *ob, struct ModifierData *md);
 void ED_object_modifier_clear(struct Main *bmain, struct Object *ob);
 int ED_object_modifier_move_down(struct ReportList *reports, struct Object *ob, struct ModifierData *md);
 int ED_object_modifier_move_up(struct ReportList *reports, struct Object *ob, struct ModifierData *md);
-int ED_object_modifier_convert(
-        struct ReportList *reports, struct Main *bmain, struct Scene *scene,
-        struct ViewLayer *view_layer, struct Object *ob, eObjectMode object_mode, struct ModifierData *md);
-int ED_object_modifier_apply(struct ReportList *reports, const struct bContext *C, struct Scene *scene,
+int ED_object_modifier_convert(struct ReportList *reports, struct Main *bmain, struct Scene *scene,
+                               struct ViewLayer *view_layer, struct Object *ob, struct ModifierData *md);
+int ED_object_modifier_apply(struct ReportList *reports, const struct EvaluationContext *eval_ctx, struct Scene *scene,
                              struct Object *ob, struct ModifierData *md, int mode);
 int ED_object_modifier_copy(struct ReportList *reports, struct Object *ob, struct ModifierData *md);
 
 bool ED_object_iter_other(
-        const struct EvaluationContext *eval_ctx, struct Main *bmain,
-        struct Object *orig_ob, const bool include_orig,
-        bool (*callback)(const struct EvaluationContext *eval_ctx, struct Object *ob, void *callback_data),
+        struct Main *bmain, struct Object *orig_ob, const bool include_orig,
+        bool (*callback)(struct Object *ob, void *callback_data),
         void *callback_data);
 
-bool ED_object_multires_update_totlevels_cb(
-        const struct EvaluationContext *eval_ctx,
-        struct Object *ob, void *totlevel_v);
+bool ED_object_multires_update_totlevels_cb(struct Object *ob, void *totlevel_v);
 
 /* object_select.c */
 void ED_object_select_linked_by_id(struct bContext *C, struct ID *id);
@@ -270,9 +258,7 @@ const struct EnumPropertyItem *ED_object_vgroup_selection_itemf_helper(
         bool *r_free,
         const unsigned int selection_mask);
 
-void ED_object_check_force_modifiers(
-        struct Main *bmain, struct Scene *scene,
-        struct Object *object, eObjectMode object_mode);
+void ED_object_check_force_modifiers(struct Main *bmain, struct Scene *scene, struct Object *object);
 
 /* object_facemap_ops.c */
 void ED_object_facemap_face_add(struct Object *ob, struct bFaceMap *fmap, int facenum);
diff --git a/source/blender/editors/include/ED_paint.h b/source/blender/editors/include/ED_paint.h
index e46f4b966c05cd97b8ea3792442cec729c389f84..246419d64aa5d78a7d2e153d843833d33e0f10da 100644
--- a/source/blender/editors/include/ED_paint.h
+++ b/source/blender/editors/include/ED_paint.h
@@ -28,37 +28,32 @@
 struct bContext;
 struct wmKeyConfig;
 struct wmOperator;
+struct ImBuf;
+struct Image;
+struct UndoStep;
+struct UndoType;
 
 /* paint_ops.c */
 void ED_operatortypes_paint(void);
 void ED_operatormacros_paint(void);
 void ED_keymap_paint(struct wmKeyConfig *keyconf);
 
-/* paint_undo.c */
-enum {
-	UNDO_PAINT_IMAGE    = 0,
-	UNDO_PAINT_MESH     = 1,
-};
-
-typedef void (*UndoRestoreCb)(struct bContext *C, struct ListBase *lb);
-typedef void (*UndoFreeCb)(struct ListBase *lb);
-typedef bool (*UndoCleanupCb)(struct bContext *C, struct ListBase *lb);
-
-int ED_undo_paint_step(struct bContext *C, int type, int step, const char *name);
-void ED_undo_paint_step_num(struct bContext *C, int type, int num);
-const char *ED_undo_paint_get_name(struct bContext *C, int type, int nr, bool *r_active);
-void ED_undo_paint_free(void);
-bool ED_undo_paint_is_valid(int type, const char *name);
-bool ED_undo_paint_empty(int type);
-void ED_undo_paint_push_begin(int type, const char *name, UndoRestoreCb restore, UndoFreeCb free, UndoCleanupCb cleanup);
-void ED_undo_paint_push_end(int type);
-
 /* paint_image.c */
-/* image painting specific undo */
-void ED_image_undo_restore(struct bContext *C, struct ListBase *lb);
-void ED_image_undo_free(struct ListBase *lb);
 void ED_imapaint_clear_partial_redraw(void);
 void ED_imapaint_dirty_region(struct Image *ima, struct ImBuf *ibuf, int x, int y, int w, int h, bool find_old);
 void ED_imapaint_bucket_fill(struct bContext *C, float color[3], struct wmOperator *op);
 
+/* paint_image_undo.c */
+void ED_image_undo_push_begin(const char *name);
+void ED_image_undo_push_end(void);
+void ED_image_undo_restore(struct UndoStep *us);
+
+void ED_image_undosys_type(struct UndoType *ut);
+
+/* paint_curve_undo.c */
+void ED_paintcurve_undo_push_begin(const char *name);
+void ED_paintcurve_undo_push_end(void);
+
+void ED_paintcurve_undosys_type(struct UndoType *ut);
+
 #endif /* __ED_PAINT_H__ */
diff --git a/source/blender/editors/include/ED_particle.h b/source/blender/editors/include/ED_particle.h
index ee60d1c8eef0f61ebf76aaf0b227b7577603dd70..b3e274a235a641eb7038e8d78a02f95db6c00a73 100644
--- a/source/blender/editors/include/ED_particle.h
+++ b/source/blender/editors/include/ED_particle.h
@@ -39,13 +39,14 @@ struct rcti;
 struct PTCacheEdit;
 struct Scene;
 struct ViewLayer;
+struct UndoType;
 
 /* particle edit mode */
 void PE_free_ptcache_edit(struct PTCacheEdit *edit);
 int PE_start_edit(struct PTCacheEdit *edit);
 
 /* access */
-struct PTCacheEdit *PE_get_current(struct Scene *scene, struct ViewLayer *view_layer, struct Object *ob);
+struct PTCacheEdit *PE_get_current(struct Scene *scene, struct Object *ob);
 struct PTCacheEdit *PE_create_current(const struct EvaluationContext *eval_ctx, struct Scene *scene, struct Object *ob);
 void PE_current_changed(const struct EvaluationContext *eval_ctx, struct Scene *scene, struct Object *ob);
 int PE_minmax(struct Scene *scene, struct ViewLayer *view_layer, float min[3], float max[3]);
@@ -64,14 +65,8 @@ int PE_circle_select(struct bContext *C, int selecting, const int mval[2], float
 int PE_lasso_select(struct bContext *C, const int mcords[][2], const short moves, bool extend, bool select);
 void PE_deselect_all_visible(struct PTCacheEdit *edit);
 
-/* undo */
-void PE_undo_push(struct Scene *scene, struct ViewLayer *view_layer, const char *str);
-void PE_undo_step(struct Scene *scene, struct ViewLayer *view_layer, int step);
-void PE_undo(struct Scene *scene, struct ViewLayer *view_layer);
-void PE_redo(struct Scene *scene, struct ViewLayer *view_layer);
-bool PE_undo_is_valid(struct Scene *scene, struct ViewLayer *view_layer);
-void PE_undo_number(struct Scene *scene, struct ViewLayer *view_layer, int nr);
-const char *PE_undo_get_name(struct Scene *scene, struct ViewLayer *view_layer, int nr, bool *r_active);
+/* particle_edit_undo.c */
+void ED_particle_undosys_type(struct UndoType *ut);
 
 #endif /* __ED_PARTICLE_H__ */
 
diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h
index 1f78a5292a7bb9bee4811313e5600a95f0e2fec3..4720eb0536721c2022d6420653908f4c5dcc23a3 100644
--- a/source/blender/editors/include/ED_screen.h
+++ b/source/blender/editors/include/ED_screen.h
@@ -162,7 +162,7 @@ struct WorkSpace *ED_workspace_add(
 bool ED_workspace_change(
         struct WorkSpace *workspace_new,
         struct bContext *C,
-        struct wmWindow *win) ATTR_NONNULL();
+        struct wmWindowManager *wm, struct wmWindow *win) ATTR_NONNULL();
 struct WorkSpace *ED_workspace_duplicate(
         struct WorkSpace *workspace_old,
         struct Main *bmain, struct wmWindow *win);
@@ -194,10 +194,6 @@ void ED_workspace_object_mode_sync_from_object(
 void ED_workspace_object_mode_sync_from_scene(
         struct wmWindowManager *wm, WorkSpace *workspace, struct Scene *scene);
 
-bool ED_workspace_object_mode_in_other_window(
-        struct wmWindowManager *wm, const struct wmWindow *win_compare, struct Object *obact,
-        eObjectMode *r_object_mode);
-
 /* anim */
 void    ED_update_for_newframe(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer, struct Depsgraph *depsgraph);
 
diff --git a/source/blender/editors/include/ED_sculpt.h b/source/blender/editors/include/ED_sculpt.h
index a81d63d9f2593029ebb23c0f94ce53ff81c519c4..574523696f542ce48578cb30551c250b5a30eded 100644
--- a/source/blender/editors/include/ED_sculpt.h
+++ b/source/blender/editors/include/ED_sculpt.h
@@ -36,10 +36,16 @@ struct Object;
 struct RegionView3D;
 struct ViewContext;
 struct rcti;
+struct UndoStep;
+struct UndoType;
+struct ListBase;
 
 /* sculpt.c */
 void ED_operatortypes_sculpt(void);
 void ED_sculpt_redraw_planes_get(float planes[4][4], struct ARegion *ar, struct Object *ob);
 int  ED_sculpt_mask_box_select(struct bContext *C, struct ViewContext *vc, const struct rcti *rect, bool select, bool extend);
 
+/* sculpt_undo.c */
+void ED_sculpt_undosys_type(struct UndoType *ut);
+
 #endif /* __ED_SCULPT_H__ */
diff --git a/source/blender/editors/include/ED_text.h b/source/blender/editors/include/ED_text.h
index 5df7d9cfaef88c285ae25bca6c3f43d0d2fa95ac..763fbe3bac5317bbd982b0a3036278a32979063f 100644
--- a/source/blender/editors/include/ED_text.h
+++ b/source/blender/editors/include/ED_text.h
@@ -30,12 +30,16 @@
 #ifndef __ED_TEXT_H__
 #define __ED_TEXT_H__
 
-struct bContext;
 struct SpaceText;
 struct ARegion;
+struct UndoType;
+struct TextUndoBuf;
 
-void ED_text_undo_step(struct bContext *C, int step);
 bool ED_text_region_location_from_cursor(struct SpaceText *st, struct ARegion *ar, const int cursor_co[2], int r_pixel_co[2]);
 
-#endif /* __ED_TEXT_H__ */
+/* text_undo.c */
+void ED_text_undosys_type(struct UndoType *ut);
+
+struct TextUndoBuf *ED_text_undo_push_init(struct bContext *C);
 
+#endif /* __ED_TEXT_H__ */
diff --git a/source/blender/editors/include/ED_undo.h b/source/blender/editors/include/ED_undo.h
new file mode 100644
index 0000000000000000000000000000000000000000..b3814ab58998334b536c6ca305d8c6f2853e0f42
--- /dev/null
+++ b/source/blender/editors/include/ED_undo.h
@@ -0,0 +1,64 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version. 
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file ED_undo.h
+ *  \ingroup editors
+ */
+
+#ifndef __ED_UNDO_H__
+#define __ED_UNDO_H__
+
+struct bContext;
+struct wmOperator;
+struct wmOperatorType;
+struct UndoStack;
+
+/* undo.c */
+void    ED_undo_push(struct bContext *C, const char *str);
+void    ED_undo_push_op(struct bContext *C, struct wmOperator *op);
+void    ED_undo_grouped_push(struct bContext *C, const char *str);
+void    ED_undo_grouped_push_op(struct bContext *C, struct wmOperator *op);
+void    ED_undo_pop_op(struct bContext *C, struct wmOperator *op);
+void    ED_undo_pop(struct bContext *C);
+void    ED_undo_redo(struct bContext *C);
+void    ED_OT_undo(struct wmOperatorType *ot);
+void    ED_OT_undo_push(struct wmOperatorType *ot);
+void    ED_OT_redo(struct wmOperatorType *ot);
+void    ED_OT_undo_redo(struct wmOperatorType *ot);
+void    ED_OT_undo_history(struct wmOperatorType *ot);
+
+int     ED_undo_operator_repeat(struct bContext *C, struct wmOperator *op);
+/* convenience since UI callbacks use this mostly*/
+void    ED_undo_operator_repeat_cb(struct bContext *C, void *arg_op, void *arg_unused);
+void    ED_undo_operator_repeat_cb_evt(struct bContext *C, void *arg_op, int arg_unused);
+
+bool    ED_undo_is_valid(const struct bContext *C, const char *undoname);
+
+struct UndoStack *ED_undo_stack_get(void);
+
+/* undo_system_types.c */
+void ED_undosys_type_init(void);
+void ED_undosys_type_free(void);
+
+/* memfile_undo.c */
+struct MemFile *ED_undosys_stack_memfile_get_active(struct UndoStack *ustack);
+
+#endif /* __ED_UNDO_H__ */
+
diff --git a/source/blender/editors/include/ED_util.h b/source/blender/editors/include/ED_util.h
index 60c4b3593aa5e07393ca87fd13a336e79c243a90..2653585dacca7d9be9158d6e93774cbe29b4d97f 100644
--- a/source/blender/editors/include/ED_util.h
+++ b/source/blender/editors/include/ED_util.h
@@ -32,9 +32,10 @@
 #define __ED_UTIL_H__
 
 struct bContext;
-struct SpaceLink;
-struct wmOperator;
 struct wmOperatorType;
+struct ScrArea;
+struct SpaceLink;
+struct PackedFile;
 
 /* ed_util.c */
 
@@ -47,40 +48,6 @@ void    ED_spacedata_id_remap(struct ScrArea *sa, struct SpaceLink *sl, struct I
 
 void    ED_OT_flush_edits(struct wmOperatorType *ot);
 
-/* ************** Undo ************************ */
-
-/* undo.c */
-void    ED_undo_push(struct bContext *C, const char *str);
-void    ED_undo_push_op(struct bContext *C, struct wmOperator *op);
-void    ED_undo_grouped_push(struct bContext *C, const char *str);
-void    ED_undo_grouped_push_op(struct bContext *C, struct wmOperator *op);
-void    ED_undo_pop_op(struct bContext *C, struct wmOperator *op);
-void    ED_undo_pop(struct bContext *C);
-void    ED_undo_redo(struct bContext *C);
-void    ED_OT_undo(struct wmOperatorType *ot);
-void    ED_OT_undo_push(struct wmOperatorType *ot);
-void    ED_OT_redo(struct wmOperatorType *ot);
-void    ED_OT_undo_redo(struct wmOperatorType *ot);
-void    ED_OT_undo_history(struct wmOperatorType *ot);
-
-int     ED_undo_operator_repeat(struct bContext *C, struct wmOperator *op);
-/* convenience since UI callbacks use this mostly*/
-void    ED_undo_operator_repeat_cb(struct bContext *C, void *arg_op, void *arg_unused);
-void    ED_undo_operator_repeat_cb_evt(struct bContext *C, void *arg_op, int arg_unused);
-
-bool    ED_undo_is_valid(const struct bContext *C, const char *undoname);
-
-/* undo_editmode.c */
-void undo_editmode_push(struct bContext *C, const char *name, 
-                        void * (*getdata)(struct bContext *C),
-                        void (*freedata)(void *),
-                        void (*to_editmode)(void *, void *, void *),
-                        void *(*from_editmode)(void *, void *),
-                        int (*validate_undo)(void *, void *));
-
-
-void    undo_editmode_clear(void);
-
 /* ************** XXX OLD CRUFT WARNING ************* */
 
 void apply_keyb_grid(int shift, int ctrl, float *val, float fac1, float fac2, float fac3, int invert);
diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h
index 15de57da09cc7b866f6c3d860ba00250558df1ed..2a5ad4946438401caea18c915d3bfb652e7ad0dd 100644
--- a/source/blender/editors/include/ED_uvedit.h
+++ b/source/blender/editors/include/ED_uvedit.h
@@ -131,7 +131,7 @@ void ED_unwrap_lscm(struct Scene *scene, struct Object *obedit, const short sel)
 void ED_image_draw_cursor(
 struct ARegion *ar, const float cursor[2]);
 void ED_uvedit_draw_main(
-        struct SpaceImage *sima, const struct EvaluationContext *eval_ctx,
+        struct SpaceImage *sima,
         struct ARegion *ar, struct Scene *scene, struct ViewLayer *view_layer,
         struct Object *obedit, struct Object *obact, struct Depsgraph *depsgraph);
 
diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h
index 64d749dc7a7490b2449f8944f7aebedba3334259..1619c59ddaca1b1d5a7bb8dd899d01295364d19d 100644
--- a/source/blender/editors/include/ED_view3d.h
+++ b/source/blender/editors/include/ED_view3d.h
@@ -388,7 +388,7 @@ void ED_view3d_draw_offscreen(
         const struct EvaluationContext *eval_ctx, struct Scene *scene,
         struct ViewLayer *view_layer, struct View3D *v3d, struct ARegion *ar, int winx, int winy, float viewmat[4][4],
         float winmat[4][4], bool do_bgpic, bool do_sky, bool is_persp, const char *viewname,
-        struct GPUFX *fx, struct GPUFXSettings *fx_settings,
+        struct GPUFXSettings *fx_settings,
         struct GPUOffScreen *ofs, struct GPUViewport *viewport);
 void ED_view3d_draw_setup_view(
         struct wmWindow *win, const struct EvaluationContext *eval_ctx, struct Scene *scene, struct ARegion *ar, struct View3D *v3d,
@@ -411,13 +411,13 @@ struct ImBuf *ED_view3d_draw_offscreen_imbuf(
         struct ViewLayer *view_layer, struct View3D *v3d, struct ARegion *ar,
         int sizex, int sizey, unsigned int flag, unsigned int draw_flags,
         int alpha_mode, int samples, const char *viewname,
-        struct GPUFX *fx, struct GPUOffScreen *ofs, char err_out[256]);
+        struct GPUOffScreen *ofs, char err_out[256]);
 struct ImBuf *ED_view3d_draw_offscreen_imbuf_simple(
         const struct EvaluationContext *eval_ctx, struct Scene *scene,
         struct ViewLayer *view_layer, struct Object *camera, int width, int height,
         unsigned int flag, unsigned int draw_flags, int drawtype, int alpha_mode,
         int samples, const char *viewname,
-        struct GPUFX *fx, struct GPUOffScreen *ofs, char err_out[256]);
+        struct GPUOffScreen *ofs, char err_out[256]);
 
 struct Base *ED_view3d_give_base_under_cursor(struct bContext *C, const int mval[2]);
 void ED_view3d_quadview_update(struct ScrArea *sa, struct ARegion *ar, bool do_clip);
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index 7bc12ed8a80c64585518fd659dbf8dab479166df..4e4d62d850b81a13ea62dbafd6259dff55b63d77 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -64,7 +64,6 @@ struct Image;
 struct ImageUser;
 struct wmKeyConfig;
 struct wmOperatorType;
-struct uiWidgetColors;
 struct MTex;
 struct ImBuf;
 struct bNodeTree;
@@ -1169,4 +1168,9 @@ void UI_tooltip_free(struct bContext *C, struct bScreen *sc, struct ARegion *ar)
 
 int UI_calc_float_precision(int prec, double value);
 
+/* widget batched drawing */
+void UI_widgetbase_draw_cache_begin(void);
+void UI_widgetbase_draw_cache_flush(void);
+void UI_widgetbase_draw_cache_end(void);
+
 #endif  /* __UI_INTERFACE_H__ */
diff --git a/source/blender/editors/include/UI_interface_icons.h b/source/blender/editors/include/UI_interface_icons.h
index cee68ed361cfe7b39f32f66f087d496a8eac88d5..8b436942fdd2e36d27eefec0a73d0ea5d55ce3de 100644
--- a/source/blender/editors/include/UI_interface_icons.h
+++ b/source/blender/editors/include/UI_interface_icons.h
@@ -77,6 +77,9 @@ void UI_icon_draw_size(float x, float y, int size, int icon_id, float alpha);
 void UI_icons_free(void);
 void UI_icons_free_drawinfo(void *drawinfo);
 
+void UI_icon_draw_cache_begin(void);
+void UI_icon_draw_cache_end(void);
+
 struct ListBase *UI_iconfile_list(void);
 int UI_iconfile_get_index(const char *filename);
 
diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c
index d6385276b1a75ad26133947ad1d20e3ca0549e64..12ee1ad35a175a46cc30e0d6973f0bff0686fa64 100644
--- a/source/blender/editors/interface/interface.c
+++ b/source/blender/editors/interface/interface.c
@@ -64,6 +64,7 @@
 #include "BLT_translation.h"
 
 #include "UI_interface.h"
+#include "UI_interface_icons.h"
 
 #include "IMB_imbuf.h"
 
@@ -1426,6 +1427,10 @@ void UI_block_draw(const bContext *C, uiBlock *block)
 	else if (block->panel)
 		ui_draw_aligned_panel(&style, block, &rect, UI_panel_category_is_visible(ar));
 
+	BLF_batch_draw_begin();
+	UI_icon_draw_cache_begin();
+	UI_widgetbase_draw_cache_begin();
+
 	/* widgets */
 	for (but = block->buttons.first; but; but = but->next) {
 		if (!(but->flag & (UI_HIDDEN | UI_SCROLLED))) {
@@ -1437,6 +1442,10 @@ void UI_block_draw(const bContext *C, uiBlock *block)
 				ui_draw_but(C, ar, &style, but, &rect);
 		}
 	}
+
+	UI_widgetbase_draw_cache_end();
+	UI_icon_draw_cache_end();
+	BLF_batch_draw_end();
 	
 	/* restore matrix */
 	gpuPopProjectionMatrix();
diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c
index 5e5da60593c813fea11c37f9999c88b668701b86..f84347b97a4fbec5836478846d00b14d69c0b50a 100644
--- a/source/blender/editors/interface/interface_draw.c
+++ b/source/blender/editors/interface/interface_draw.c
@@ -64,6 +64,7 @@
 /* own include */
 #include "interface_intern.h"
 
+
 static int roundboxtype = UI_CNR_ALL;
 
 void UI_draw_roundbox_corner_set(int type)
@@ -101,12 +102,58 @@ void UI_draw_roundbox_3fvAlpha(bool filled, float minx, float miny, float maxx,
 	UI_draw_roundbox_4fv(filled, minx, miny, maxx, maxy, rad, colv);
 }
 
+void UI_draw_roundbox_aa(bool filled, float minx, float miny, float maxx, float maxy, float rad, const float color[4])
+{
+	uiWidgetBaseParameters widget_params = {
+		.recti.xmin = minx, .recti.ymin = miny,
+		.recti.xmax = maxx, .recti.ymax = maxy,
+		.radi = rad,
+		.round_corners[0] = (roundboxtype & UI_CNR_BOTTOM_LEFT) ? 1.0f : 0.0f,
+		.round_corners[1] = (roundboxtype & UI_CNR_BOTTOM_RIGHT) ? 1.0f : 0.0f,
+		.round_corners[2] = (roundboxtype & UI_CNR_TOP_RIGHT) ? 1.0f : 0.0f,
+		.round_corners[3] = (roundboxtype & UI_CNR_TOP_LEFT) ? 1.0f : 0.0f,
+		.color_inner1[0] = color[0], .color_inner2[0] = color[0],
+		.color_inner1[1] = color[1], .color_inner2[1] = color[1],
+		.color_inner1[2] = color[2], .color_inner2[2] = color[2],
+		.color_inner1[3] = color[3], .color_inner2[3] = color[3],
+	};
+
+	glEnable(GL_BLEND);
+
+	if (filled) {
+		/* plain antialiased filled box */
+		widget_params.color_inner1[3] *= 0.125f;
+		widget_params.color_inner2[3] *= 0.125f;
+
+		/* WATCH: This is assuming the ModelViewProjectionMatrix is area pixel space.
+		 * If it has been scaled, then it's no longer valid. */
+		Gwn_Batch *batch = ui_batch_roundbox_get(filled, true);
+		GWN_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE);
+		GWN_batch_uniform_4fv_array(batch, "parameters", 11, (float *)&widget_params);
+		GWN_batch_draw(batch);
+	}
+	else {
+		/* plain antialiased unfilled box */
+		glEnable(GL_LINE_SMOOTH);
+
+		Gwn_Batch *batch = ui_batch_roundbox_get(filled, false);
+		GWN_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE);
+		GWN_batch_uniform_4fv_array(batch, "parameters", 11, (float *)&widget_params);
+		GWN_batch_draw(batch);
+
+		glDisable(GL_LINE_SMOOTH);
+	}
+
+	glDisable(GL_BLEND);
+}
+
 void UI_draw_roundbox_4fv(bool filled, float minx, float miny, float maxx, float maxy, float rad, const float col[4])
 {
+#if 0
 	float vec[7][2] = {{0.195, 0.02}, {0.383, 0.067}, {0.55, 0.169}, {0.707, 0.293},
 	                   {0.831, 0.45}, {0.924, 0.617}, {0.98, 0.805}};
 	int a;
-	
+
 	Gwn_VertFormat *format = immVertexFormat();
 	unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
 
@@ -175,8 +222,29 @@ void UI_draw_roundbox_4fv(bool filled, float minx, float miny, float maxx, float
 	
 	immEnd();
 	immUnbindProgram();
+#endif
+
+	uiWidgetBaseParameters widget_params = {
+		.recti.xmin = minx, .recti.ymin = miny,
+		.recti.xmax = maxx, .recti.ymax = maxy,
+		.radi = rad,
+		.round_corners[0] = (roundboxtype & UI_CNR_BOTTOM_LEFT) ? 1.0f : 0.0f,
+		.round_corners[1] = (roundboxtype & UI_CNR_BOTTOM_RIGHT) ? 1.0f : 0.0f,
+		.round_corners[2] = (roundboxtype & UI_CNR_TOP_RIGHT) ? 1.0f : 0.0f,
+		.round_corners[3] = (roundboxtype & UI_CNR_TOP_LEFT) ? 1.0f : 0.0f,
+		.color_inner1[0] = col[0], .color_inner2[0] = col[0],
+		.color_inner1[1] = col[1], .color_inner2[1] = col[1],
+		.color_inner1[2] = col[2], .color_inner2[2] = col[2],
+		.color_inner1[3] = col[3], .color_inner2[3] = col[3],
+	};
+
+	Gwn_Batch *batch = ui_batch_roundbox_get(filled, false);
+	GWN_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE);
+	GWN_batch_uniform_4fv_array(batch, "parameters", 11, (float *)&widget_params);
+	GWN_batch_draw(batch);
 }
 
+#if 0
 static void round_box_shade_col(unsigned attrib, const float col1[3], float const col2[3], const float fac)
 {
 	float col[4] = {
@@ -187,6 +255,7 @@ static void round_box_shade_col(unsigned attrib, const float col1[3], float cons
 	};
 	immAttrib4fv(attrib, col);
 }
+#endif
 
 /* linear horizontal shade within button or in outline */
 /* view2d scrollers use it */
@@ -194,6 +263,7 @@ void UI_draw_roundbox_shade_x(
         bool filled, float minx, float miny, float maxx, float maxy,
         float rad, float shadetop, float shadedown, const float col[4])
 {
+#if 0
 	float vec[7][2] = {{0.195, 0.02}, {0.383, 0.067}, {0.55, 0.169}, {0.707, 0.293},
 	                   {0.831, 0.45}, {0.924, 0.617}, {0.98, 0.805}};
 	const float div = maxy - miny;
@@ -305,6 +375,30 @@ void UI_draw_roundbox_shade_x(
 
 	immEnd();
 	immUnbindProgram();
+#endif
+
+	uiWidgetBaseParameters widget_params = {
+		.recti.xmin = minx, .recti.ymin = miny,
+		.recti.xmax = maxx, .recti.ymax = maxy,
+		.radi = rad,
+		.round_corners[0] = (roundboxtype & UI_CNR_BOTTOM_LEFT) ? 1.0f : 0.0f,
+		.round_corners[1] = (roundboxtype & UI_CNR_BOTTOM_RIGHT) ? 1.0f : 0.0f,
+		.round_corners[2] = (roundboxtype & UI_CNR_TOP_RIGHT) ? 1.0f : 0.0f,
+		.round_corners[3] = (roundboxtype & UI_CNR_TOP_LEFT) ? 1.0f : 0.0f,
+		.color_inner1[0] = min_ff(1.0f, col[0] + shadetop),
+		.color_inner2[0] = max_ff(0.0f, col[0] + shadedown),
+		.color_inner1[1] = min_ff(1.0f, col[1] + shadetop),
+		.color_inner2[1] = max_ff(0.0f, col[1] + shadedown),
+		.color_inner1[2] = min_ff(1.0f, col[2] + shadetop),
+		.color_inner2[2] = max_ff(0.0f, col[2] + shadedown),
+		.color_inner1[3] = 1.0f,
+		.color_inner2[3] = 1.0f,
+	};
+
+	Gwn_Batch *batch = ui_batch_roundbox_get(filled, false);
+	GWN_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE);
+	GWN_batch_uniform_4fv_array(batch, "parameters", 11, (float *)&widget_params);
+	GWN_batch_draw(batch);
 }
 
 #if 0 /* unused */
@@ -816,6 +910,9 @@ void ui_draw_but_WAVEFORM(ARegion *ar, uiBut *but, uiWidgetColors *UNUSED(wcol),
 		}
 	}
 
+	/* Flush text cache before changing scissors. */
+	BLF_batch_draw_flush();
+
 	glEnable(GL_BLEND);
 	glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
 
@@ -840,6 +937,9 @@ void ui_draw_but_WAVEFORM(ARegion *ar, uiBut *but, uiWidgetColors *UNUSED(wcol),
 		BLF_draw_default(rect.xmin + 1, yofs - 5 + (i * 0.2f) * h, 0, str, sizeof(str) - 1);
 	}
 
+	/* Flush text cache before drawing things on top. */
+	BLF_batch_draw_flush();
+
 	glEnable(GL_BLEND);
 	glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
 
@@ -2031,16 +2131,40 @@ void ui_draw_dropshadow(const rctf *rct, float radius, float aspect, float alpha
 	}
 	
 	glEnable(GL_BLEND);
-
 	const float dalpha = alpha * 2.0f / 255.0f;
 	float calpha = dalpha;
-	for (; i--; a -= aspect) {
+	float visibility = 1.0f;
+	for (; i--;) {
 		/* alpha ranges from 2 to 20 or so */
+#if 0 /* Old Method (pre 2.8) */
 		float color[4] = {0.0f, 0.0f, 0.0f, calpha};
 		UI_draw_roundbox_4fv(true, rct->xmin - a, rct->ymin - a, rct->xmax + a, rct->ymax - 10.0f + a, rad + a, color);
+#endif
+		/* Compute final visibility to match old method result. */
+		/* TODO we could just find a better fit function inside the shader instead of this. */
+		visibility = visibility * (1.0f - calpha);
 		calpha += dalpha;
 	}
 	
+	uiWidgetBaseParameters widget_params = {
+		.recti.xmin = rct->xmin, .recti.ymin = rct->ymin,
+		.recti.xmax = rct->xmax, .recti.ymax = rct->ymax - 10.0f,
+		.rect.xmin = rct->xmin - a, .rect.ymin = rct->ymin - a,
+		.rect.xmax = rct->xmax + a, .rect.ymax = rct->ymax - 10.0f + a,
+		.radi = rad,
+		.rad = rad + a,
+		.round_corners[0] = (roundboxtype & UI_CNR_BOTTOM_LEFT) ? 1.0f : 0.0f,
+		.round_corners[1] = (roundboxtype & UI_CNR_BOTTOM_RIGHT) ? 1.0f : 0.0f,
+		.round_corners[2] = (roundboxtype & UI_CNR_TOP_RIGHT) ? 1.0f : 0.0f,
+		.round_corners[3] = (roundboxtype & UI_CNR_TOP_LEFT) ? 1.0f : 0.0f,
+	};
+
+	Gwn_Batch *batch = ui_batch_roundbox_shadow_get();
+	GWN_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_SHADOW);
+	GWN_batch_uniform_4fv_array(batch, "parameters", 4, (float *)&widget_params);
+	GWN_batch_uniform_1f(batch, "alpha", 1.0f - visibility);
+	GWN_batch_draw(batch);
+
 	/* outline emphasis */
 	glEnable(GL_LINE_SMOOTH);
 	float color[4] = {0.0f, 0.0f, 0.0f, 0.4f};
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index b1dc30945c4c1a3d9589e9e4c25ab0ee7b5d9c99..b95bf466186961d8a286b2f352f3ecfbe9e29581 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -46,7 +46,6 @@
 #include "DNA_object_types.h"
 #include "DNA_scene_types.h"
 #include "DNA_screen_types.h"
-#include "DNA_workspace_types.h"
 
 #include "BLI_math.h"
 #include "BLI_listbase.h"
@@ -74,10 +73,8 @@
 #include "BKE_unit.h"
 #include "BKE_paint.h"
 
-#include "DEG_depsgraph.h"
-
 #include "ED_screen.h"
-#include "ED_util.h"
+#include "ED_undo.h"
 #include "ED_keyframing.h"
 
 #include "UI_interface.h"
@@ -5234,10 +5231,9 @@ static int ui_do_but_COLOR(
 			if ((int)(but->a1) == UI_PALETTE_COLOR) {
 				if (!event->ctrl) {
 					float color[3];
-					const WorkSpace *workspace = CTX_wm_workspace(C);
 					Scene *scene = CTX_data_scene(C);
 					ViewLayer *view_layer = CTX_data_view_layer(C);
-					Paint *paint = BKE_paint_get_active(scene, view_layer, workspace->object_mode);
+					Paint *paint = BKE_paint_get_active(scene, view_layer);
 					Brush *brush = BKE_paint_brush(paint);
 
 					if (brush->flag & BRUSH_USE_GRADIENT) {
@@ -6153,7 +6149,6 @@ static int ui_do_but_CURVE(
 {
 	int mx, my, a;
 	bool changed = false;
-
 	Scene *scene = CTX_data_scene(C);
 	ViewLayer *view_layer = CTX_data_view_layer(C);
 
@@ -6270,7 +6265,6 @@ static int ui_do_but_CURVE(
 		}
 		else if (event->type == LEFTMOUSE && event->val != KM_PRESS) {
 			if (data->dragsel != -1) {
-				const WorkSpace *workspace = CTX_wm_workspace(C);
 				CurveMapping *cumap = (CurveMapping *)but->poin;
 				CurveMap *cuma = cumap->cm + cumap->cur;
 				CurveMapPoint *cmp = cuma->curve;
@@ -6285,7 +6279,7 @@ static int ui_do_but_CURVE(
 				}
 				else {
 					curvemapping_changed(cumap, true);  /* remove doubles */
-					BKE_paint_invalidate_cursor_overlay(scene, view_layer, cumap, workspace->object_mode);
+					BKE_paint_invalidate_cursor_overlay(scene, view_layer, cumap);
 				}
 			}
 
@@ -7788,7 +7782,10 @@ static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState s
 
 	/* highlight has timers for tooltips and auto open */
 	if (state == BUTTON_STATE_HIGHLIGHT) {
-		but->flag &= ~UI_SELECT;
+		/* for list-items (that are not drawn with regular emboss), don't change selection based on hovering */
+		if (((but->flag & UI_BUT_LIST_ITEM) == 0) && (but->dragflag & UI_EMBOSS_NONE)) {
+			but->flag &= ~UI_SELECT;
+		}
 
 		button_tooltip_timer_reset(C, but);
 
diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c
index 6f56576a2089eded55fd8cda9b405a7ed01af0b5..682eac6a352cb150dced1344e5d3acb943595125 100644
--- a/source/blender/editors/interface/interface_icons.c
+++ b/source/blender/editors/interface/interface_icons.c
@@ -34,11 +34,13 @@
 #include "MEM_guardedalloc.h"
 
 #include "GPU_draw.h"
+#include "GPU_matrix.h"
 #include "GPU_immediate.h"
 
 #include "BLI_blenlib.h"
 #include "BLI_utildefines.h"
 #include "BLI_fileops_types.h"
+#include "BLI_math_vector.h"
 
 #include "DNA_brush_types.h"
 #include "DNA_curve_types.h"
@@ -1007,6 +1009,9 @@ static void icon_draw_rect(float x, float y, int w, int h, float UNUSED(aspect),
 		rect = ima->rect;
 	}
 
+	/* We need to flush widget base first to ensure correct ordering. */
+	UI_widgetbase_draw_cache_flush();
+
 	/* draw */
 	IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR);
 	immDrawPixelsTex(&state, draw_x, draw_y, draw_w, draw_h, GL_RGBA, GL_UNSIGNED_BYTE, GL_NEAREST, rect,
@@ -1016,10 +1021,122 @@ static void icon_draw_rect(float x, float y, int w, int h, float UNUSED(aspect),
 		IMB_freeImBuf(ima);
 }
 
-static void icon_draw_texture(
+/* High enough to make a difference, low enough so that
+ * small draws are still efficient with the use of glUniform.
+ * NOTE TODO: We could use UBO but we would need some triple
+ * buffer system + persistent mapping for this to be more
+ * efficient than simple glUniform calls. */
+#define ICON_DRAW_CACHE_SIZE 16
+
+typedef struct IconDrawCall{
+	rctf pos;
+	rctf tex;
+	float color[4];
+} IconDrawCall;
+
+static struct {
+	IconDrawCall drawcall_cache[ICON_DRAW_CACHE_SIZE];
+	int calls; /* Number of calls batched together */
+	bool enabled;
+	float mat[4][4];
+} g_icon_draw_cache = {0};
+
+void UI_icon_draw_cache_begin(void)
+{
+	BLI_assert(g_icon_draw_cache.enabled == false);
+	g_icon_draw_cache.enabled = true;
+}
+
+static void icon_draw_cache_flush_ex(void)
+{
+	if (g_icon_draw_cache.calls == 0)
+		return;
+
+	/* We need to flush widget base first to ensure correct ordering. */
+	glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+	UI_widgetbase_draw_cache_flush();
+
+	glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+
+	glActiveTexture(GL_TEXTURE0);
+	glBindTexture(GL_TEXTURE_2D, icongltex.id);
+
+	GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_IMAGE_MULTI_RECT_COLOR);
+	GPU_shader_bind(shader);
+
+	int img_loc = GPU_shader_get_uniform(shader, "image");
+	int data_loc = GPU_shader_get_uniform(shader, "calls_data[0]");
+
+	glUniform1i(img_loc, 0);
+	glUniform4fv(data_loc, ICON_DRAW_CACHE_SIZE * 3, (float *)g_icon_draw_cache.drawcall_cache);
+
+	GWN_draw_primitive(GWN_PRIM_TRIS, 6 * g_icon_draw_cache.calls);
+
+	glBindTexture(GL_TEXTURE_2D, 0);
+
+	g_icon_draw_cache.calls = 0;
+}
+
+void UI_icon_draw_cache_end(void)
+{
+	BLI_assert(g_icon_draw_cache.enabled == true);
+	g_icon_draw_cache.enabled = false;
+
+	/* Don't change blend state if it's not needed. */
+	if (g_icon_draw_cache.calls == 0)
+		return;
+
+	glEnable(GL_BLEND);
+
+	icon_draw_cache_flush_ex();
+
+	glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+	glDisable(GL_BLEND);
+}
+
+static void icon_draw_texture_cached(
         float x, float y, float w, float h, int ix, int iy,
         int UNUSED(iw), int ih, float alpha, const float rgb[3])
 {
+
+	float mvp[4][4];
+	gpuGetModelViewProjectionMatrix(mvp);
+
+	IconDrawCall *call = &g_icon_draw_cache.drawcall_cache[g_icon_draw_cache.calls];
+	g_icon_draw_cache.calls++;
+
+	/* Manual mat4*vec2 */
+	call->pos.xmin = x * mvp[0][0] + y * mvp[1][0] + mvp[3][0];
+	call->pos.ymin = x * mvp[0][1] + y * mvp[1][1] + mvp[3][1];
+	call->pos.xmax = call->pos.xmin + w * mvp[0][0] + h * mvp[1][0];
+	call->pos.ymax = call->pos.ymin + w * mvp[0][1] + h * mvp[1][1];
+
+	call->tex.xmin = ix * icongltex.invw;
+	call->tex.xmax = (ix + ih) * icongltex.invw;
+	call->tex.ymin = iy * icongltex.invh;
+	call->tex.ymax = (iy + ih) * icongltex.invh;
+
+	if (rgb) copy_v4_fl4(call->color, rgb[0], rgb[1], rgb[2], alpha);
+	else     copy_v4_fl(call->color, alpha);
+
+	if (g_icon_draw_cache.calls == ICON_DRAW_CACHE_SIZE) {
+		icon_draw_cache_flush_ex();
+	}
+}
+
+static void icon_draw_texture(
+        float x, float y, float w, float h, int ix, int iy,
+        int iw, int ih, float alpha, const float rgb[3])
+{
+	if (g_icon_draw_cache.enabled) {
+		icon_draw_texture_cached(x, y, w, h, ix, iy, iw, ih, alpha, rgb);
+		return;
+	}
+
+	/* We need to flush widget base first to ensure correct ordering. */
+	glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+	UI_widgetbase_draw_cache_flush();
+
 	float x1, x2, y1, y2;
 
 	x1 = ix * icongltex.invw;
@@ -1027,32 +1144,20 @@ static void icon_draw_texture(
 	y1 = iy * icongltex.invh;
 	y2 = (iy + ih) * icongltex.invh;
 
+	glActiveTexture(GL_TEXTURE0);
 	glBindTexture(GL_TEXTURE_2D, icongltex.id);
-	Gwn_VertFormat *format = immVertexFormat();
-	unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
-	unsigned int texCoord = GWN_vertformat_attr_add(format, "texCoord", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
-
-	immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_COLOR);
-	if (rgb) immUniformColor3fvAlpha(rgb, alpha);
-	else     immUniformColor4f(alpha, alpha, alpha, alpha);
-
-	immUniform1i("image", 0);
 
-	immBegin(GWN_PRIM_TRI_STRIP, 4);
-	immAttrib2f(texCoord, x1, y2);
-	immVertex2f(pos, x, y + h);
+	GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_IMAGE_RECT_COLOR);
+	GPU_shader_bind(shader);
 
-	immAttrib2f(texCoord, x1, y1);
-	immVertex2f(pos, x, y);
+	if (rgb) glUniform4f(GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_COLOR), rgb[0], rgb[1], rgb[2], alpha);
+	else     glUniform4f(GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_COLOR), alpha, alpha, alpha, alpha);
 
-	immAttrib2f(texCoord, x2, y2);
-	immVertex2f(pos, x + w, y + h);
+	glUniform1i(GPU_shader_get_uniform(shader, "image"), 0);
+	glUniform4f(GPU_shader_get_uniform(shader, "rect_icon"), x1, y1, x2, y2);
+	glUniform4f(GPU_shader_get_uniform(shader, "rect_geom"), x, y, x + w, y + h);
 
-	immAttrib2f(texCoord, x2, y1);
-	immVertex2f(pos, x + w, y);
-	immEnd();
-
-	immUnbindProgram();
+	GWN_draw_primitive(GWN_PRIM_TRI_STRIP, 4);
 
 	glBindTexture(GL_TEXTURE_2D, 0);
 }
@@ -1196,7 +1301,6 @@ static int ui_id_brush_get_icon(const bContext *C, ID *id)
 		ui_id_icon_render(C, id, true);
 	}
 	else {
-		const WorkSpace *workspace = CTX_wm_workspace(C);
 		Object *ob = CTX_data_active_object(C);
 		SpaceImage *sima;
 		const EnumPropertyItem *items = NULL;
@@ -1207,11 +1311,11 @@ static int ui_id_brush_get_icon(const bContext *C, ID *id)
 		 * checking various context stuff here */
 
 		if (CTX_wm_view3d(C) && ob) {
-			if (workspace->object_mode & OB_MODE_SCULPT)
+			if (ob->mode & OB_MODE_SCULPT)
 				mode = OB_MODE_SCULPT;
-			else if (workspace->object_mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT))
+			else if (ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT))
 				mode = OB_MODE_VERTEX_PAINT;
-			else if (workspace->object_mode & OB_MODE_TEXTURE_PAINT)
+			else if (ob->mode & OB_MODE_TEXTURE_PAINT)
 				mode = OB_MODE_TEXTURE_PAINT;
 		}
 		else if ((sima = CTX_wm_space_image(C)) &&
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
index 8d7f9d47ab581d05e2018588715dbb1dd86a6233..043dfc9fd99da79d10731dae076f5864c957f0b9 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -700,6 +700,35 @@ struct wmIMEData *ui_but_ime_data_get(uiBut *but);
 #endif
 
 /* interface_widgets.c */
+
+/* Widget shader parameters, must match the shader layout. */
+typedef struct uiWidgetBaseParameters {
+	rctf recti, rect;
+	float radi, rad;
+	float facxi, facyi;
+	float round_corners[4];
+	float color_inner1[4], color_inner2[4];
+	float color_outline[4], color_emboss[4];
+	float color_tria[4];
+	float tria1_center[2], tria2_center[2];
+	float tria1_size, tria2_size;
+	float shade_dir, do_alpha_check;
+} uiWidgetBaseParameters;
+
+enum {
+	ROUNDBOX_TRIA_NONE = 0,
+	ROUNDBOX_TRIA_ARROWS,
+	ROUNDBOX_TRIA_SCROLL,
+	ROUNDBOX_TRIA_MENU,
+	ROUNDBOX_TRIA_CHECK,
+
+	ROUNDBOX_TRIA_MAX, /* don't use */
+};
+
+struct Gwn_Batch *ui_batch_roundbox_get(bool filled, bool antialiased);
+struct Gwn_Batch *ui_batch_roundbox_widget_get(int tria);
+struct Gwn_Batch *ui_batch_roundbox_shadow_get(void);
+
 void ui_draw_anti_tria(float x1, float y1, float x2, float y2, float x3, float y3, const float color[4]);
 void ui_draw_anti_roundbox(int mode, float minx, float miny, float maxx, float maxy,
                            float rad, bool use_alpha, const float color[4]);
diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c
index a7ea42e0fab38db001416889b6ad966ac71b9c93..59c639776ef79954673b15e1f13b1c25e17803f6 100644
--- a/source/blender/editors/interface/interface_layout.c
+++ b/source/blender/editors/interface/interface_layout.c
@@ -1500,7 +1500,7 @@ void uiItemFullR(uiLayout *layout, PointerRNA *ptr, PropertyRNA *prop, int index
 	}
 
 	/* Mark non-embossed textfields inside a listbox. */
-	if (but && (block->flag & UI_BLOCK_LIST_ITEM) && (but->type == UI_BTYPE_TEXT) && (but->dt & UI_EMBOSS_NONE)) {
+	if (but && (block->flag & UI_BLOCK_LIST_ITEM) && (but->dt & UI_EMBOSS_NONE)) {
 		UI_but_flag_enable(but, UI_BUT_LIST_ITEM);
 	}
 
diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c
index ad4aaf599989a0e6bff6ad5fd2978d9f34019752..9e31e8729d523f0b276ea6b3305347ba4b9475c6 100644
--- a/source/blender/editors/interface/interface_ops.c
+++ b/source/blender/editors/interface/interface_ops.c
@@ -943,7 +943,8 @@ static int reports_to_text_exec(bContext *C, wmOperator *UNUSED(op))
 	str = BKE_reports_string(reports, (G.debug & G_DEBUG) ? RPT_DEBUG : RPT_INFO);
 
 	if (str) {
-		BKE_text_write(txt, str);
+		TextUndoBuf *utxt = NULL; // FIXME
+		BKE_text_write(txt, utxt, str);
 		MEM_freeN(str);
 
 		return OPERATOR_FINISHED;
diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c
index c3759e232b021cf8b0134987216d9a983d47ba24..bb086d639173f2a36d52d238f3042c186c0459ed 100644
--- a/source/blender/editors/interface/interface_panel.c
+++ b/source/blender/editors/interface/interface_panel.c
@@ -501,9 +501,28 @@ static void ui_draw_panel_scalewidget(unsigned int pos, const rcti *rect)
 
 	glDisable(GL_BLEND);
 }
-static void ui_draw_panel_dragwidget(unsigned int pos, const rctf *rect)
+
+static void immRectf_tris_color_ex(unsigned int pos, float x1, float y1, float x2, float y2,
+                              unsigned int col, const float color[3])
+{
+	immAttrib4fv(col, color);
+	immVertex2f(pos, x1, y1);
+	immAttrib4fv(col, color);
+	immVertex2f(pos, x2, y1);
+	immAttrib4fv(col, color);
+	immVertex2f(pos, x2, y2);
+
+	immAttrib4fv(col, color);
+	immVertex2f(pos, x1, y1);
+	immAttrib4fv(col, color);
+	immVertex2f(pos, x2, y2);
+	immAttrib4fv(col, color);
+	immVertex2f(pos, x1, y2);
+}
+
+static void ui_draw_panel_dragwidget(unsigned int pos, unsigned int col, const rctf *rect)
 {
-	unsigned char col_back[3], col_high[3], col_dark[3];
+	float col_high[4], col_dark[4];
 	const int col_tint = 84;
 
 	const int px = (int)U.pixelsize;
@@ -518,24 +537,24 @@ static void ui_draw_panel_dragwidget(unsigned int pos, const rctf *rect)
 	const int x_ofs = y_ofs;
 	int i_x, i_y;
 
-
-	UI_GetThemeColor3ubv(UI_GetThemeValue(TH_PANEL_SHOW_HEADER) ? TH_PANEL_HEADER : TH_PANEL_BACK, col_back);
-	UI_GetColorPtrShade3ubv(col_back, col_high,  col_tint);
-	UI_GetColorPtrShade3ubv(col_back, col_dark, -col_tint);
-
+	int col_id = UI_GetThemeValue(TH_PANEL_SHOW_HEADER) ? TH_PANEL_HEADER : TH_PANEL_BACK;
+	UI_GetThemeColorShade4fv(col_id,  col_tint, col_high);
+	UI_GetThemeColorShade4fv(col_id, -col_tint, col_dark);
 
 	/* draw multiple boxes */
+	immBegin(GWN_PRIM_TRIS, 4 * 2 * (6 * 2));
 	for (i_x = 0; i_x < 4; i_x++) {
 		for (i_y = 0; i_y < 2; i_y++) {
 			const int x_co = (x_min + x_ofs) + (i_x * (box_size + box_margin));
 			const int y_co = (y_min + y_ofs) + (i_y * (box_size + box_margin));
 
-			immUniformColor3ubv(col_dark);
-			immRectf(pos, x_co - box_size, y_co - px_zoom, x_co, (y_co + box_size) - px_zoom);
-			immUniformColor3ubv(col_high);
-			immRectf(pos, x_co - box_size, y_co, x_co, y_co + box_size);
+			immRectf_tris_color_ex(pos, x_co - box_size, y_co - px_zoom, x_co, (y_co + box_size) - px_zoom,
+			                       col, col_dark);
+			immRectf_tris_color_ex(pos, x_co - box_size, y_co, x_co, y_co + box_size,
+			                       col, col_high);
 		}
 	}
+	immEnd();
 }
 
 
@@ -662,8 +681,11 @@ void ui_draw_aligned_panel(uiStyle *style, uiBlock *block, const rcti *rect, con
 
 	/* horizontal title */
 	if (is_closed_x == false) {
+		unsigned int col;
 		ui_draw_aligned_panel_header(style, block, &headrect, 'h');
-		pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
+		Gwn_VertFormat *format = immVertexFormat();
+		pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
+		col = GWN_vertformat_attr_add(format, "color", GWN_COMP_F32, 4, GWN_FETCH_FLOAT);
 
 		/* itemrect smaller */
 		itemrect.xmax = headrect.xmax - 5.0f / block->aspect;
@@ -672,9 +694,12 @@ void ui_draw_aligned_panel(uiStyle *style, uiBlock *block, const rcti *rect, con
 		itemrect.ymax = headrect.ymax;
 
 		BLI_rctf_scale(&itemrect, 0.7f);
-		immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
-		ui_draw_panel_dragwidget(pos, &itemrect);
+		immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
+		ui_draw_panel_dragwidget(pos, col, &itemrect);
 		immUnbindProgram();
+
+		/* Restore format for the following draws. */
+		pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
 	}
 
 	/* if the panel is minimized vertically:
@@ -1594,30 +1619,36 @@ static void ui_panel_category_draw_tab(
 
 	immBegin(filled ? GWN_PRIM_TRI_FAN : GWN_PRIM_LINE_STRIP, vert_ct);
 
-	immAttrib3ubv(color, col);
-
 	/* start with corner right-top */
 	if (use_highlight) {
 		if (roundboxtype & UI_CNR_TOP_RIGHT) {
+			immAttrib3ubv(color, col);
 			immVertex2f(pos, maxx, maxy - rad);
 			for (a = 0; a < 4; a++) {
+				immAttrib3ubv(color, col);
 				immVertex2f(pos, maxx - vec[a][1], maxy - rad + vec[a][0]);
 			}
+			immAttrib3ubv(color, col);
 			immVertex2f(pos, maxx - rad, maxy);
 		}
 		else {
+			immAttrib3ubv(color, col);
 			immVertex2f(pos, maxx, maxy);
 		}
 
 		/* corner left-top */
 		if (roundboxtype & UI_CNR_TOP_LEFT) {
+			immAttrib3ubv(color, col);
 			immVertex2f(pos, minx + rad, maxy);
 			for (a = 0; a < 4; a++) {
+				immAttrib3ubv(color, col);
 				immVertex2f(pos, minx + rad - vec[a][0], maxy - vec[a][1]);
 			}
+			immAttrib3ubv(color, col);
 			immVertex2f(pos, minx, maxy - rad);
 		}
 		else {
+			immAttrib3ubv(color, col);
 			immVertex2f(pos, minx, maxy);
 		}
 	}
@@ -1626,6 +1657,9 @@ static void ui_panel_category_draw_tab(
 		if (highlight_fade) {
 			immAttrib3ubv(color, highlight_fade);
 		}
+		else {
+			immAttrib3ubv(color, col);
+		}
 		immVertex2f(pos, minx, miny + rad);
 		immEnd();
 		immUnbindProgram();
@@ -1634,25 +1668,33 @@ static void ui_panel_category_draw_tab(
 
 	/* corner left-bottom */
 	if (roundboxtype & UI_CNR_BOTTOM_LEFT) {
+		immAttrib3ubv(color, col);
 		immVertex2f(pos, minx, miny + rad);
 		for (a = 0; a < 4; a++) {
+			immAttrib3ubv(color, col);
 			immVertex2f(pos, minx + vec[a][1], miny + rad - vec[a][0]);
 		}
+		immAttrib3ubv(color, col);
 		immVertex2f(pos, minx + rad, miny);
 	}
 	else {
+		immAttrib3ubv(color, col);
 		immVertex2f(pos, minx, miny);
 	}
 
 	/* corner right-bottom */
 	if (roundboxtype & UI_CNR_BOTTOM_RIGHT) {
+		immAttrib3ubv(color, col);
 		immVertex2f(pos, maxx - rad, miny);
 		for (a = 0; a < 4; a++) {
+			immAttrib3ubv(color, col);
 			immVertex2f(pos, maxx - rad + vec[a][0], miny + vec[a][1]);
 		}
+		immAttrib3ubv(color, col);
 		immVertex2f(pos, maxx, miny + rad);
 	}
 	else {
+		immAttrib3ubv(color, col);
 		immVertex2f(pos, maxx, miny);
 	}
 
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 75b657e1e2960630e230a44e9a4c0ed0fa0ae188..375027a84d03e3ed21a4ba2b0f16a93410878c35 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -79,7 +79,7 @@
 #include "ED_screen.h"
 #include "ED_object.h"
 #include "ED_render.h"
-#include "ED_util.h"
+#include "ED_undo.h"
 
 #include "RNA_access.h"
 
@@ -575,9 +575,6 @@ static void template_ID(
         bContext *C, uiLayout *layout, TemplateID *template_ui, StructRNA *type, int flag,
         const char *newop, const char *openop, const char *unlinkop)
 {
-	EvaluationContext eval_ctx;
-	CTX_data_eval_ctx(C, &eval_ctx);
-
 	uiBut *but;
 	uiBlock *block;
 	PointerRNA idptr;
@@ -662,7 +659,7 @@ static void template_ID(
 			    (idfrom && idfrom->lib) ||
 			    (!editable) ||
 			    /* object in editmode - don't change data */
-			    (idfrom && GS(idfrom->name) == ID_OB && (eval_ctx.object_mode & OB_MODE_EDIT)))
+			    (idfrom && GS(idfrom->name) == ID_OB && (((Object *)idfrom)->mode & OB_MODE_EDIT)))
 			{
 				UI_but_flag_enable(but, UI_BUT_DISABLED);
 			}
@@ -1210,8 +1207,7 @@ static int modifier_is_simulation(ModifierData *md)
 }
 
 static uiLayout *draw_modifier(
-        uiLayout *layout,
-        const EvaluationContext *eval_ctx, Scene *scene, Object *ob,
+        uiLayout *layout, Scene *scene, Object *ob,
         ModifierData *md, int index, int cageIndex, int lastCageIndex)
 {
 	const ModifierTypeInfo *mti = modifierType_getInfo(md->type);
@@ -1348,7 +1344,7 @@ static uiLayout *draw_modifier(
 			if (md->type == eModifierType_ParticleSystem) {
 				ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys;
 				
-				if (!(eval_ctx->object_mode & OB_MODE_PARTICLE_EDIT)) {
+				if (!(ob->mode & OB_MODE_PARTICLE_EDIT)) {
 					if (ELEM(psys->part->ren_as, PART_DRAW_GR, PART_DRAW_OB))
 						uiItemO(row, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Convert"), ICON_NONE,
 						        "OBJECT_OT_duplicates_make_real");
@@ -1397,8 +1393,6 @@ static uiLayout *draw_modifier(
 
 uiLayout *uiTemplateModifier(uiLayout *layout, bContext *C, PointerRNA *ptr)
 {
-	EvaluationContext eval_ctx;
-	CTX_data_eval_ctx(C, &eval_ctx);
 	Scene *scene = CTX_data_scene(C);
 	Object *ob;
 	ModifierData *md, *vmd;
@@ -1429,7 +1423,7 @@ uiLayout *uiTemplateModifier(uiLayout *layout, bContext *C, PointerRNA *ptr)
 
 	for (i = 0; vmd; i++, vmd = vmd->next) {
 		if (md == vmd)
-			return draw_modifier(layout, &eval_ctx, scene, ob, md, i, cageIndex, lastCageIndex);
+			return draw_modifier(layout, scene, ob, md, i, cageIndex, lastCageIndex);
 		else if (vmd->mode & eModifierMode_Virtual)
 			i--;
 	}
diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c
index 246f7d7bd61d9049f6389f801bbc1a537bd2a152..f15118d5f077f90dc0d9705fcffa7833623f89cf 100644
--- a/source/blender/editors/interface/interface_widgets.c
+++ b/source/blender/editors/interface/interface_widgets.c
@@ -55,6 +55,7 @@
 #include "interface_intern.h"
 
 #include "GPU_basic_shader.h"
+#include "GPU_batch.h"
 #include "GPU_immediate.h"
 #include "GPU_immediate_util.h"
 #include "GPU_matrix.h"
@@ -79,6 +80,7 @@ enum {
 #define UI_BUT_UPDATE_DELAY ((void)0)
 #define UI_BUT_UNDO ((void)0)
 
+
 /* ************** widget base functions ************** */
 /**
  * - in: roundbox codes for corner types and radius
@@ -98,6 +100,8 @@ enum {
 
 typedef struct uiWidgetTrias {
 	unsigned int tot;
+	int type;
+	float size, center[2];
 	
 	float vec[16][2];
 	const unsigned int (*index)[3];
@@ -105,21 +109,24 @@ typedef struct uiWidgetTrias {
 } uiWidgetTrias;
 
 /* max as used by round_box__edges */
+/* Make sure to change widget_base_vert.glsl accordingly. */
 #define WIDGET_CURVE_RESOLU 9
 #define WIDGET_SIZE_MAX (WIDGET_CURVE_RESOLU * 4)
 
 typedef struct uiWidgetBase {
-	
+	/* TODO remove these completely */
 	int totvert, halfwayvert;
 	float outer_v[WIDGET_SIZE_MAX][2];
 	float inner_v[WIDGET_SIZE_MAX][2];
 	float inner_uv[WIDGET_SIZE_MAX][2];
 	
-	bool draw_inner, draw_outline, draw_emboss, draw_shadedir;
+	bool draw_inner, draw_outline, draw_emboss;
 	
 	uiWidgetTrias tria1;
 	uiWidgetTrias tria2;
-	
+
+	/* Widget shader parameters, must match the shader layout. */
+	uiWidgetBaseParameters uniform_params;
 } uiWidgetBase;
 
 /** uiWidgetType: for time being only for visual appearance,
@@ -206,6 +213,270 @@ static const uint g_shape_preset_hold_action_face[2][3] = {{2, 0, 1}, {3, 5, 4}}
 
 /** \} */
 
+/* **************** Batch creations ****************** */
+/**
+ * In order to speed up UI drawing we create some batches that are then
+ * modified by specialized shaders to draw certain elements really fast.
+ * TODO: find a better place. Maybe it's own file?
+ **/
+
+/* offset in triavec[] in shader per type */
+static const int tria_ofs[ROUNDBOX_TRIA_MAX] = {0, 0, 6, 22, 28};
+static const int tria_vcount[ROUNDBOX_TRIA_MAX] = {0, 3, 16, 3, 6};
+
+static struct {
+	Gwn_Batch *roundbox_widget[ROUNDBOX_TRIA_MAX];
+
+	Gwn_Batch *roundbox_simple;
+	Gwn_Batch *roundbox_simple_aa;
+	Gwn_Batch *roundbox_simple_outline;
+	Gwn_Batch *roundbox_shadow;
+
+	Gwn_VertFormat format;
+	uint vflag_id;
+} g_ui_batch_cache = {{0}};
+
+static Gwn_VertFormat *vflag_format(void)
+{
+	if (g_ui_batch_cache.format.attrib_ct == 0) {
+		Gwn_VertFormat *format = &g_ui_batch_cache.format;
+		g_ui_batch_cache.vflag_id = GWN_vertformat_attr_add(format, "vflag", GWN_COMP_U32, 1, GWN_FETCH_INT);
+	}
+	return &g_ui_batch_cache.format;
+}
+
+#define INNER 0
+#define OUTLINE 1
+#define EMBOSS 2
+#define NO_AA WIDGET_AA_JITTER
+
+static void set_roundbox_vertex_data(
+        Gwn_VertBufRaw *vflag_step, uint32_t d)
+{
+	uint32_t *data = GWN_vertbuf_raw_step(vflag_step);
+	*data = d;
+}
+
+static uint32_t set_roundbox_vertex(
+        Gwn_VertBufRaw *vflag_step,
+        int corner_id, int corner_v, int jit_v, bool inner, bool emboss, int color)
+{
+	uint32_t *data = GWN_vertbuf_raw_step(vflag_step);
+	*data  = corner_id;
+	*data |= corner_v << 2;
+	*data |= jit_v << 6;
+	*data |= color << 12;
+	*data |= (inner) ? (1 << 10) : 0; /* is inner vert */
+	*data |= (emboss) ? (1 << 11) : 0; /* is emboss vert */
+	return *data;
+}
+
+static uint32_t set_tria_vertex(
+        Gwn_VertBufRaw *vflag_step,
+        int tria_type, int tria_v, int tria_id, int jit_v)
+{
+	uint32_t *data = GWN_vertbuf_raw_step(vflag_step);
+	if (ELEM(tria_type, ROUNDBOX_TRIA_ARROWS, ROUNDBOX_TRIA_MENU)) {
+		tria_v += tria_id * 3;
+	}
+	*data  = tria_ofs[tria_type] + tria_v;
+	*data |= jit_v << 6;
+	*data |= (tria_id == 0) ? (1 << 10) : 0; /* is first tria */
+	*data |= 1 << 14; /* is tria vert */
+	return *data;
+}
+
+static void roundbox_batch_add_tria(Gwn_VertBufRaw *vflag_step, int tria, uint32_t last_data)
+{
+	const int tria_num = (tria == ROUNDBOX_TRIA_CHECK) ? 1 : 2;
+	/* for each tria */
+	for (int t = 0; t < tria_num; ++t) {
+		for (int j = 0; j < WIDGET_AA_JITTER; j++) {
+			/* restart */
+			set_roundbox_vertex_data(vflag_step, last_data);
+			set_tria_vertex(vflag_step, tria, 0, t, j);
+			for (int v = 0; v < tria_vcount[tria]; v++) {
+				last_data = set_tria_vertex(vflag_step, tria, v, t, j);
+			}
+		}
+	}
+}
+
+Gwn_Batch *ui_batch_roundbox_widget_get(int tria)
+{
+	if (g_ui_batch_cache.roundbox_widget[tria] == NULL) {
+		uint32_t last_data;
+		Gwn_VertBufRaw vflag_step;
+		Gwn_VertBuf *vbo = GWN_vertbuf_create_with_format(vflag_format());
+		int vcount = WIDGET_SIZE_MAX; /* inner */
+		vcount += 2; /* restart */
+		vcount += ((WIDGET_SIZE_MAX + 1) * 2) * WIDGET_AA_JITTER; /* outline (edges) */
+		vcount += 2; /* restart */
+		vcount += ((WIDGET_CURVE_RESOLU * 2) * 2) * WIDGET_AA_JITTER; /* emboss */
+		if (tria) {
+			vcount += (tria_vcount[tria] + 2) * WIDGET_AA_JITTER; /* tria1 */
+			if (tria != ROUNDBOX_TRIA_CHECK) {
+				vcount += (tria_vcount[tria] + 2) * WIDGET_AA_JITTER; /* tria2 */
+			}
+		}
+		GWN_vertbuf_data_alloc(vbo, vcount);
+		GWN_vertbuf_attr_get_raw_data(vbo, g_ui_batch_cache.vflag_id, &vflag_step);
+		/* Inner */
+		for (int c1 = 0, c2 = 3; c1 < 2; c1++, c2--) {
+			for (int a1 = 0, a2 = WIDGET_CURVE_RESOLU -1; a2 >= 0; a1++, a2--) {
+				last_data = set_roundbox_vertex(&vflag_step, c1, a1, NO_AA, true, false, INNER);
+				last_data = set_roundbox_vertex(&vflag_step, c2, a2, NO_AA, true, false, INNER);
+			}
+		}
+		/* restart */
+		set_roundbox_vertex_data(&vflag_step, last_data);
+		set_roundbox_vertex(&vflag_step, 0, 0, 0, true, false, OUTLINE);
+		/* Outlines */
+		for (int j = 0; j < WIDGET_AA_JITTER; j++) {
+			for (int c = 0; c < 4; c++) {
+				for (int a = 0; a < WIDGET_CURVE_RESOLU; a++) {
+					set_roundbox_vertex(&vflag_step, c, a, j, true, false, OUTLINE);
+					set_roundbox_vertex(&vflag_step, c, a, j, false, false, OUTLINE);
+				}
+			}
+			/* Close the loop. */
+			set_roundbox_vertex(&vflag_step, 0, 0, j, true, false, OUTLINE);
+			last_data = set_roundbox_vertex(&vflag_step, 0, 0, j, false, false, OUTLINE);
+		}
+		/* restart */
+		set_roundbox_vertex_data(&vflag_step, last_data);
+		set_roundbox_vertex(&vflag_step, 0, 0, 0, false, false, EMBOSS);
+		/* Emboss */
+		bool rev = false; /* go back and forth : avoid degenerate triangle (but beware of backface cull) */
+		for (int j = 0; j < WIDGET_AA_JITTER; j++, rev = !rev) {
+			for (int c = (rev) ? 1 : 0; (rev) ? c >= 0 : c < 2; (rev) ? c-- : c++) {
+				int sta = (rev) ? WIDGET_CURVE_RESOLU - 1 : 0;
+				int end = WIDGET_CURVE_RESOLU;
+				for (int a = sta; (rev) ? a >= 0 : a < end; (rev) ? a-- : a++) {
+					set_roundbox_vertex(&vflag_step, c, a, j, false, false, EMBOSS);
+					last_data = set_roundbox_vertex(&vflag_step, c, a, j, false, true, EMBOSS);
+				}
+			}
+		}
+		if (tria) {
+			roundbox_batch_add_tria(&vflag_step, tria, last_data);
+		}
+		g_ui_batch_cache.roundbox_widget[tria] = GWN_batch_create_ex(GWN_PRIM_TRI_STRIP, vbo, NULL, GWN_BATCH_OWNS_VBO);
+		gpu_batch_presets_register(g_ui_batch_cache.roundbox_widget[tria]);
+	}
+	return g_ui_batch_cache.roundbox_widget[tria];
+}
+
+Gwn_Batch *ui_batch_roundbox_get(bool filled, bool antialiased)
+{
+	Gwn_Batch **batch = NULL;
+
+	if (filled) {
+		if (antialiased)
+			batch = &g_ui_batch_cache.roundbox_simple_aa;
+		else
+			batch = &g_ui_batch_cache.roundbox_simple;
+	}
+	else {
+		if (antialiased)
+			BLI_assert(0); /* Use GL_LINE_SMOOTH instead!!: */
+		else
+			batch = &g_ui_batch_cache.roundbox_simple_outline;
+	}
+
+	if (*batch == NULL) {
+		uint32_t last_data;
+		Gwn_VertBufRaw vflag_step;
+		Gwn_VertBuf *vbo = GWN_vertbuf_create_with_format(vflag_format());
+		int vcount = WIDGET_SIZE_MAX;
+		vcount += (filled) ? 2 : 0;
+		vcount *= (antialiased) ? WIDGET_AA_JITTER : 1;
+		GWN_vertbuf_data_alloc(vbo, vcount);
+		GWN_vertbuf_attr_get_raw_data(vbo, g_ui_batch_cache.vflag_id, &vflag_step);
+
+		if (filled) {
+			for (int j = 0; j < WIDGET_AA_JITTER; j++) {
+				if (!antialiased) {
+					j = NO_AA;
+				}
+				/* restart */
+				set_roundbox_vertex(&vflag_step, 0, 0, j, true, false, INNER);
+				for (int c1 = 0, c2 = 3; c1 < 2; c1++, c2--) {
+					for (int a1 = 0, a2 = WIDGET_CURVE_RESOLU -1; a2 >= 0; a1++, a2--) {
+						last_data = set_roundbox_vertex(&vflag_step, c1, a1, j, true, false, INNER);
+						last_data = set_roundbox_vertex(&vflag_step, c2, a2, j, true, false, INNER);
+					}
+				}
+				/* restart */
+				set_roundbox_vertex_data(&vflag_step, last_data);
+				if (!antialiased) {
+					break;
+				}
+			}
+			*batch = GWN_batch_create_ex(GWN_PRIM_TRI_STRIP, vbo, NULL, GWN_BATCH_OWNS_VBO);
+		}
+		else {
+			for (int j = 0; j < WIDGET_AA_JITTER; j++) {
+				if (!antialiased) {
+					j = NO_AA;
+				}
+				for (int c = 0; c < 4; c++) {
+					for (int a = 0; a < WIDGET_CURVE_RESOLU; a++) {
+						set_roundbox_vertex(&vflag_step, c, a, j, true, false, INNER);
+					}
+				}
+				if (!antialiased) {
+					break;
+				}
+			}
+			*batch = GWN_batch_create_ex(GWN_PRIM_LINE_LOOP, vbo, NULL, GWN_BATCH_OWNS_VBO);
+		}
+
+		gpu_batch_presets_register(*batch);
+	}
+	return *batch;
+}
+
+Gwn_Batch *ui_batch_roundbox_shadow_get(void)
+{
+	if (g_ui_batch_cache.roundbox_shadow == NULL) {
+		uint32_t last_data;
+		Gwn_VertBufRaw vflag_step;
+		Gwn_VertBuf *vbo = GWN_vertbuf_create_with_format(vflag_format());
+		int vcount = (WIDGET_SIZE_MAX + 1) * 2 + 2 + WIDGET_SIZE_MAX;
+		GWN_vertbuf_data_alloc(vbo, vcount);
+		GWN_vertbuf_attr_get_raw_data(vbo, g_ui_batch_cache.vflag_id, &vflag_step);
+
+		for (int c = 0; c < 4; c++) {
+			for (int a = 0; a < WIDGET_CURVE_RESOLU; a++) {
+				set_roundbox_vertex(&vflag_step, c, a, NO_AA, true, false, INNER);
+				set_roundbox_vertex(&vflag_step, c, a, NO_AA, false, false, INNER);
+			}
+		}
+		/* close loop */
+		last_data = set_roundbox_vertex(&vflag_step, 0, 0, NO_AA, true, false, INNER);
+		last_data = set_roundbox_vertex(&vflag_step, 0, 0, NO_AA, false, false, INNER);
+		/* restart */
+		set_roundbox_vertex_data(&vflag_step, last_data);
+		set_roundbox_vertex(&vflag_step, 0, 0, NO_AA, true, false, INNER);
+		/* filled */
+		for (int c1 = 0, c2 = 3; c1 < 2; c1++, c2--) {
+			for (int a1 = 0, a2 = WIDGET_CURVE_RESOLU -1; a2 >= 0; a1++, a2--) {
+				set_roundbox_vertex(&vflag_step, c1, a1, NO_AA, true, false, INNER);
+				set_roundbox_vertex(&vflag_step, c2, a2, NO_AA, true, false, INNER);
+			}
+		}
+		g_ui_batch_cache.roundbox_shadow = GWN_batch_create_ex(GWN_PRIM_TRI_STRIP, vbo, NULL, GWN_BATCH_OWNS_VBO);
+		gpu_batch_presets_register(g_ui_batch_cache.roundbox_shadow);
+	}
+	return g_ui_batch_cache.roundbox_shadow;
+}
+
+#undef INNER
+#undef OUTLINE
+#undef EMBOSS
+#undef NO_AA
+
 /* ************************************************* */
 
 void ui_draw_anti_tria(float x1, float y1, float x2, float y2, float x3, float y3,
@@ -239,42 +510,20 @@ void ui_draw_anti_tria(float x1, float y1, float x2, float y2, float x3, float y
 	glDisable(GL_BLEND);
 }
 
-/* belongs in interface_draw.c, but needs WIDGET_AA_JITTER from this file */
-void UI_draw_roundbox_aa(bool filled, float minx, float miny, float maxx, float maxy, float rad, const float color[4])
-{
-	glEnable(GL_BLEND);
-
-	if (filled) {
-		/* plain antialiased filled box */
-		const float alpha = color[3] * 0.125f;
-		
-		for (int j = 0; j < WIDGET_AA_JITTER; j++) {
-			gpuPushMatrix();
-			gpuTranslate2fv(jit[j]);
-			UI_draw_roundbox_3fvAlpha(true, minx, miny, maxx, maxy, rad, color, alpha);
-			gpuPopMatrix();
-		}
-	}
-	else {
-		/* plain antialiased unfilled box */
-		glEnable(GL_LINE_SMOOTH);
-		UI_draw_roundbox_4fv(false, minx, miny, maxx, maxy, rad, color);
-		glDisable(GL_LINE_SMOOTH);
-	}
-
-	glDisable(GL_BLEND);
-}
-
 static void widget_init(uiWidgetBase *wtb)
 {
 	wtb->totvert = wtb->halfwayvert = 0;
 	wtb->tria1.tot = 0;
 	wtb->tria2.tot = 0;
+	wtb->tria1.type = ROUNDBOX_TRIA_NONE;
+	wtb->tria1.size = 0;
+	wtb->tria2.size = 0;
 
 	wtb->draw_inner = true;
 	wtb->draw_outline = true;
 	wtb->draw_emboss = true;
-	wtb->draw_shadedir = true;
+
+	wtb->uniform_params.shade_dir = 1.0f;
 }
 
 /* helper call, makes shadow rect, with 'sun' above menu, so only shadow to left/right/bottom */
@@ -381,7 +630,18 @@ static void round_box__edges(uiWidgetBase *wt, int roundboxalign, const rcti *re
 
 	if (2.0f * (radi + 1.0f) > minsize)
 		radi = 0.5f * minsize - U.pixelsize;
-	
+
+	wt->uniform_params.rad = rad;
+	wt->uniform_params.radi = radi;
+	wt->uniform_params.facxi = facxi;
+	wt->uniform_params.facyi = facyi;
+	wt->uniform_params.round_corners[0] = (roundboxalign & UI_CNR_BOTTOM_LEFT) ? 1.0f : 0.0f;
+	wt->uniform_params.round_corners[1] = (roundboxalign & UI_CNR_BOTTOM_RIGHT) ? 1.0f : 0.0f;
+	wt->uniform_params.round_corners[2] = (roundboxalign & UI_CNR_TOP_RIGHT) ? 1.0f : 0.0f;
+	wt->uniform_params.round_corners[3] = (roundboxalign & UI_CNR_TOP_LEFT) ? 1.0f : 0.0f;
+	BLI_rctf_rcti_copy(&wt->uniform_params.rect, rect);
+	BLI_rctf_init(&wt->uniform_params.recti, minxi, maxxi, minyi, maxyi);
+
 	/* mult */
 	for (a = 0; a < WIDGET_CURVE_RESOLU; a++) {
 		veci[a][0] = radi * cornervec[a][0];
@@ -531,18 +791,20 @@ static void shape_preset_init_trias_ex(
 	/* center position and size */
 	centx = (float)rect->xmin + 0.4f * minsize;
 	centy = (float)rect->ymin + 0.5f * minsize;
-	sizex = sizey = -0.5f * triasize * minsize;
+	tria->size = sizex = sizey = -0.5f * triasize * minsize;
 
 	if (where == 'r') {
 		centx = (float)rect->xmax - 0.4f * minsize;
 		sizex = -sizex;
 	}
 	else if (where == 't') {
+		centx = (float)rect->xmin + 0.5f * minsize;
 		centy = (float)rect->ymax - 0.5f * minsize;
 		sizey = -sizey;
 		i2 = 0; i1 = 1;
 	}
 	else if (where == 'b') {
+		centx = (float)rect->xmin + 0.5f * minsize;
 		sizex = -sizex;
 		i2 = 0; i1 = 1;
 	}
@@ -552,12 +814,16 @@ static void shape_preset_init_trias_ex(
 		tria->vec[a][1] = sizey * verts[a][i2] + centy;
 	}
 
+	tria->center[0] = centx;
+	tria->center[1] = centy;
+
 	tria->tot = tris_tot;
 	tria->index = tris;
 }
 
 static void shape_preset_init_number_arrows(uiWidgetTrias *tria, const rcti *rect, float triasize, char where)
 {
+	tria->type = ROUNDBOX_TRIA_ARROWS;
 	shape_preset_init_trias_ex(
 	        tria, rect, triasize, where,
 	        g_shape_preset_number_arrow_vert, ARRAY_SIZE(g_shape_preset_number_arrow_vert),
@@ -574,19 +840,22 @@ static void shape_preset_init_hold_action(uiWidgetTrias *tria, const rcti *rect,
 
 static void shape_preset_init_scroll_circle(uiWidgetTrias *tria, const rcti *rect, float triasize, char where)
 {
+	tria->type = ROUNDBOX_TRIA_SCROLL;
 	shape_preset_init_trias_ex(
 	        tria, rect, triasize, where,
 	        g_shape_preset_scroll_circle_vert, ARRAY_SIZE(g_shape_preset_scroll_circle_vert),
 	        g_shape_preset_scroll_circle_face, ARRAY_SIZE(g_shape_preset_scroll_circle_face));
 }
 
-static void shape_preset_draw_trias(uiWidgetTrias *tria, uint pos)
+static void shape_preset_draw_trias_aa(uiWidgetTrias *tria, uint pos)
 {
-	immBegin(GWN_PRIM_TRIS, tria->tot * 3);
-	for (int i = 0; i < tria->tot; ++i)
-		for (int j = 0; j < 3; ++j)
-			immVertex2fv(pos, tria->vec[tria->index[i][j]]);
-	immEnd();
+	for (int k = 0; k < WIDGET_AA_JITTER; k++) {
+		for (int i = 0; i < tria->tot; ++i)
+			for (int j = 0; j < 3; ++j)
+				immVertex2f(pos,
+				            tria->vec[tria->index[i][j]][0] + jit[k][0],
+				            tria->vec[tria->index[i][j]][1] + jit[k][1]);
+	}
 }
 
 static void widget_draw_vertex_buffer(unsigned int pos, unsigned int col, int mode,
@@ -607,11 +876,12 @@ static void shape_preset_trias_from_rect_menu(uiWidgetTrias *tria, const rcti *r
 {
 	float centx, centy, size;
 	int a;
+	tria->type = ROUNDBOX_TRIA_MENU;
 
 	/* center position and size */
-	centx = rect->xmax - 0.32f * BLI_rcti_size_y(rect);
-	centy = rect->ymin + 0.50f * BLI_rcti_size_y(rect);
-	size = 0.4f * BLI_rcti_size_y(rect);
+	tria->center[0] = centx = rect->xmax - 0.32f * BLI_rcti_size_y(rect);
+	tria->center[1] = centy = rect->ymin + 0.50f * BLI_rcti_size_y(rect);
+	tria->size = size = 0.4f * BLI_rcti_size_y(rect);
 
 	for (a = 0; a < 6; a++) {
 		tria->vec[a][0] = size * g_shape_preset_menu_arrow_vert[a][0] + centx;
@@ -626,11 +896,12 @@ static void shape_preset_trias_from_rect_checkmark(uiWidgetTrias *tria, const rc
 {
 	float centx, centy, size;
 	int a;
+	tria->type = ROUNDBOX_TRIA_CHECK;
 	
 	/* center position and size */
-	centx = rect->xmin + 0.5f * BLI_rcti_size_y(rect);
-	centy = rect->ymin + 0.5f * BLI_rcti_size_y(rect);
-	size = 0.5f * BLI_rcti_size_y(rect);
+	tria->center[0] = centx = rect->xmin + 0.5f * BLI_rcti_size_y(rect);
+	tria->center[1] = centy = rect->ymin + 0.5f * BLI_rcti_size_y(rect);
+	tria->size = size = 0.5f * BLI_rcti_size_y(rect);
 	
 	for (a = 0; a < 6; a++) {
 		tria->vec[a][0] = size * g_shape_preset_checkmark_vert[a][0] + centx;
@@ -680,17 +951,6 @@ static void widget_verts_to_triangle_strip(uiWidgetBase *wtb, const int totvert,
 	copy_v2_v2(triangle_strip[a * 2 + 1], wtb->inner_v[0]);
 }
 
-static void widget_verts_to_triangle_strip_open(uiWidgetBase *wtb, const int totvert, float triangle_strip[WIDGET_SIZE_MAX * 2][2])
-{
-	int a;
-	for (a = 0; a < totvert; a++) {
-		triangle_strip[a * 2][0] = wtb->outer_v[a][0];
-		triangle_strip[a * 2][1] = wtb->outer_v[a][1];
-		triangle_strip[a * 2 + 1][0] = wtb->outer_v[a][0];
-		triangle_strip[a * 2 + 1][1] = wtb->outer_v[a][1] - 1.0f;
-	}
-}
-
 static void widgetbase_outline(uiWidgetBase *wtb, unsigned int pos)
 {
 	float triangle_strip[WIDGET_SIZE_MAX * 2 + 2][2]; /* + 2 because the last pair is wrapped */
@@ -699,135 +959,178 @@ static void widgetbase_outline(uiWidgetBase *wtb, unsigned int pos)
 	widget_draw_vertex_buffer(pos, 0, GL_TRIANGLE_STRIP, triangle_strip, NULL, wtb->totvert * 2 + 2);
 }
 
-static void widgetbase_draw(uiWidgetBase *wtb, uiWidgetColors *wcol)
+static void widgetbase_set_uniform_colors_ubv(
+        uiWidgetBase *wtb,
+        const unsigned char *col1, const unsigned char *col2,
+        const unsigned char *outline,
+        const unsigned char *emboss,
+        const unsigned char *tria,
+        const bool alpha_check)
 {
-	int j, a;
-
-	glEnable(GL_BLEND);
-
-	/* backdrop non AA */
-	if (wtb->draw_inner) {
-		BLI_assert(wtb->totvert != 0);
-		if (wcol->shaded == 0) {
-			if (wcol->alpha_check) {
-				float inner_v_half[WIDGET_SIZE_MAX][2];
-				float x_mid = 0.0f; /* used for dumb clamping of values */
-
-				unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
-				immBindBuiltinProgram(GPU_SHADER_2D_CHECKER);
+	wtb->uniform_params.do_alpha_check = (float)alpha_check;
+	rgba_float_args_set_ch(wtb->uniform_params.color_inner1, col1[0], col1[1], col1[2], col1[3]);
+	rgba_float_args_set_ch(wtb->uniform_params.color_inner2, col2[0], col2[1], col2[2], col2[3]);
+	rgba_float_args_set_ch(wtb->uniform_params.color_outline, outline[0], outline[1], outline[2], outline[3]);
+	rgba_float_args_set_ch(wtb->uniform_params.color_emboss, emboss[0], emboss[1], emboss[2], emboss[3]);
+	rgba_float_args_set_ch(wtb->uniform_params.color_tria, tria[0], tria[1], tria[2], tria[3]);
+}
 
-				/* checkers */
-				immUniform4f("color1", UI_ALPHA_CHECKER_DARK / 255.0f, UI_ALPHA_CHECKER_DARK / 255.0f, UI_ALPHA_CHECKER_DARK / 255.0f, 1.0f);
-				immUniform4f("color2", UI_ALPHA_CHECKER_LIGHT / 255.0f, UI_ALPHA_CHECKER_LIGHT / 255.0f, UI_ALPHA_CHECKER_LIGHT / 255.0f, 1.0f);
-				immUniform1i("size", 8);
+/* keep in sync with shader */
+#define MAX_WIDGET_BASE_BATCH 6
 
-				widget_draw_vertex_buffer(pos, 0, GL_TRIANGLE_FAN, wtb->inner_v, NULL, wtb->totvert);
+struct {
+	Gwn_Batch *batch; /* Batch type */
+	uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH];
+	int count;
+	bool enabled;
+} g_widget_base_batch = {0};
 
-				immUnbindProgram();
+void UI_widgetbase_draw_cache_flush(void)
+{
+	float checker_params[3] = {UI_ALPHA_CHECKER_DARK / 255.0f, UI_ALPHA_CHECKER_LIGHT / 255.0f, 8.0f};
 
-				/* alpha fill */
-				immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
-				immUniformColor4ubv((unsigned char *)wcol->inner);
+	if (g_widget_base_batch.count == 0)
+		return;
 
-				glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+	Gwn_Batch *batch = g_widget_base_batch.batch;
+	if (g_widget_base_batch.count == 1) {
+		/* draw single */
+		GWN_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE);
+		GWN_batch_uniform_4fv_array(batch, "parameters", 11, (float *)g_widget_base_batch.params);
+		GWN_batch_uniform_3fv(batch, "checkerColorAndSize", checker_params);
+		GWN_batch_draw(batch);
+	}
+	else {
+		GWN_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE_INST);
+		GWN_batch_uniform_4fv_array(batch, "parameters", 11 * MAX_WIDGET_BASE_BATCH, (float *)g_widget_base_batch.params);
+		GWN_batch_uniform_3fv(batch, "checkerColorAndSize", checker_params);
+		gpuBindMatrices(batch->interface);
+		GWN_batch_draw_range_ex(batch, 0, g_widget_base_batch.count, true);
+		GWN_batch_program_use_end(batch);
+	}
+	g_widget_base_batch.count = 0;
+}
 
-				for (a = 0; a < wtb->totvert; a++) {
-					x_mid += wtb->inner_v[a][0];
-				}
-				x_mid /= wtb->totvert;
+void UI_widgetbase_draw_cache_begin(void)
+{
+	BLI_assert(g_widget_base_batch.enabled == false);
+	g_widget_base_batch.enabled = true;
+}
 
-				widget_draw_vertex_buffer(pos, 0, GL_TRIANGLE_FAN, wtb->inner_v, NULL, wtb->totvert);
+void UI_widgetbase_draw_cache_end(void)
+{
+	BLI_assert(g_widget_base_batch.enabled == true);
+	g_widget_base_batch.enabled = false;
 
-				/* 1/2 solid color */
-				immUniformColor3ubv((unsigned char *)wcol->inner);
+	glEnable(GL_BLEND);
 
-				for (a = 0; a < wtb->totvert; a++) {
-					inner_v_half[a][0] = MIN2(wtb->inner_v[a][0], x_mid);
-					inner_v_half[a][1] = wtb->inner_v[a][1];
-				}
+	UI_widgetbase_draw_cache_flush();
 
-				widget_draw_vertex_buffer(pos, 0, GL_TRIANGLE_FAN, inner_v_half, NULL, wtb->totvert);
+	glDisable(GL_BLEND);
+}
 
-				immUnbindProgram();
-			}
-			else {
-				/* simple fill */
-				unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
-				immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
-				immUniformColor4ubv((unsigned char *)wcol->inner);
+static void draw_widgetbase_batch(Gwn_Batch *batch, uiWidgetBase *wtb)
+{
+	wtb->uniform_params.tria1_size = wtb->tria1.size;
+	wtb->uniform_params.tria2_size = wtb->tria2.size;
+	copy_v2_v2(wtb->uniform_params.tria1_center, wtb->tria1.center);
+	copy_v2_v2(wtb->uniform_params.tria2_center, wtb->tria2.center);
+
+	if (g_widget_base_batch.enabled) {
+		if (g_widget_base_batch.batch == NULL) {
+			g_widget_base_batch.batch = ui_batch_roundbox_widget_get(ROUNDBOX_TRIA_ARROWS);
+		}
 
-				widget_draw_vertex_buffer(pos, 0, GL_TRIANGLE_FAN, wtb->inner_v, NULL, wtb->totvert);
+		/* draw multi */
+		if (batch != g_ui_batch_cache.roundbox_widget[ROUNDBOX_TRIA_NONE] &&
+		    batch != g_widget_base_batch.batch)
+		{
+			/* issue previous calls before changing batch type. */
+			UI_widgetbase_draw_cache_flush();
+			g_widget_base_batch.batch = batch;
+		}
 
-				immUnbindProgram();
-			}
+		/* No need to change batch if tria is not visible. Just scale it to 0. */
+		if (batch == g_ui_batch_cache.roundbox_widget[ROUNDBOX_TRIA_NONE]) {
+			wtb->uniform_params.tria1_size = wtb->uniform_params.tria2_size = 0;
 		}
-		else {
-			char col1[4], col2[4];
-			unsigned char col_array[WIDGET_SIZE_MAX][4];
-			unsigned char *col_pt = &col_array[0][0];
 
-			Gwn_VertFormat *format = immVertexFormat();
-			unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
-			unsigned int col = GWN_vertformat_attr_add(format, "color", GWN_COMP_U8, 4, GWN_FETCH_INT_TO_FLOAT_UNIT);
+		g_widget_base_batch.params[g_widget_base_batch.count] = wtb->uniform_params;
+		g_widget_base_batch.count++;
 
-			immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR);
+		if (g_widget_base_batch.count == MAX_WIDGET_BASE_BATCH) {
+			UI_widgetbase_draw_cache_flush();
+		}
+	}
+	else {
+		float checker_params[3] = {UI_ALPHA_CHECKER_DARK / 255.0f, UI_ALPHA_CHECKER_LIGHT / 255.0f, 8.0f};
+		/* draw single */
+		GWN_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE);
+		GWN_batch_uniform_4fv_array(batch, "parameters", 11, (float *)&wtb->uniform_params);
+		GWN_batch_uniform_3fv(batch, "checkerColorAndSize", checker_params);
+		GWN_batch_draw(batch);
+	}
+}
 
-			shadecolors4(col1, col2, wcol->inner, wcol->shadetop, wcol->shadedown);
+static void widgetbase_draw(uiWidgetBase *wtb, uiWidgetColors *wcol)
+{
+	unsigned char inner_col1[4] = {0};
+	unsigned char inner_col2[4] = {0};
+	unsigned char emboss_col[4] = {0};
+	unsigned char outline_col[4] = {0};
+	unsigned char tria_col[4] = {0};
+	/* For color widget. */
+	bool alpha_check = (wcol->alpha_check && (wcol->shaded == 0));
 
-			for (a = 0; a < wtb->totvert; a++, col_pt += 4) {
-				round_box_shade_col4_r(col_pt, col1, col2, wtb->inner_uv[a][wtb->draw_shadedir ? 1 : 0]);
-			}
+	glEnable(GL_BLEND);
 
-			widget_draw_vertex_buffer(pos, col, GL_TRIANGLE_FAN, wtb->inner_v, col_array, wtb->totvert);
-			immUnbindProgram();
+	/* backdrop non AA */
+	if (wtb->draw_inner) {
+		if (wcol->shaded == 0) {
+			/* simple fill */
+			inner_col1[0] = inner_col2[0] = (unsigned char)wcol->inner[0];
+			inner_col1[1] = inner_col2[1] = (unsigned char)wcol->inner[1];
+			inner_col1[2] = inner_col2[2] = (unsigned char)wcol->inner[2];
+			inner_col1[3] = inner_col2[3] = (unsigned char)wcol->inner[3];
+		}
+		else {
+			/* gradient fill */
+			shadecolors4((char *)inner_col1, (char *)inner_col2, wcol->inner, wcol->shadetop, wcol->shadedown);
 		}
 	}
 
-	/* for each AA step */
 	if (wtb->draw_outline) {
-		BLI_assert(wtb->totvert != 0);
-		float triangle_strip[WIDGET_SIZE_MAX * 2 + 2][2]; /* + 2 because the last pair is wrapped */
-		float triangle_strip_emboss[WIDGET_SIZE_MAX * 2][2]; /* only for emboss */
-
-		const unsigned char tcol[4] = {wcol->outline[0],
-		                               wcol->outline[1],
-		                               wcol->outline[2],
-		                               wcol->outline[3] / WIDGET_AA_JITTER};
-		unsigned char emboss[4];
-
-		widget_verts_to_triangle_strip(wtb, wtb->totvert, triangle_strip);
+		outline_col[0] = wcol->outline[0];
+		outline_col[1] = wcol->outline[1];
+		outline_col[2] = wcol->outline[2];
+		outline_col[3] = wcol->outline[3] / WIDGET_AA_JITTER;
 
+		/* emboss bottom shadow */
 		if (wtb->draw_emboss) {
-			widget_verts_to_triangle_strip_open(wtb, wtb->halfwayvert, triangle_strip_emboss);
-			UI_GetThemeColor4ubv(TH_WIDGET_EMBOSS, emboss);
+			UI_GetThemeColor4ubv(TH_WIDGET_EMBOSS, emboss_col);
 		}
+	}
 
-		unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
-
-		immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
-		for (j = 0; j < WIDGET_AA_JITTER; j++) {
-			gpuTranslate2fv(jit[j]);
-			
-			/* outline */
-			immUniformColor4ubv(tcol);
+	if (wtb->tria1.type != ROUNDBOX_TRIA_NONE)
+	{
+		tria_col[0] = wcol->item[0];
+		tria_col[1] = wcol->item[1];
+		tria_col[2] = wcol->item[2];
+		tria_col[3] = (unsigned char)((float)wcol->item[3] / WIDGET_AA_JITTER);
+	}
 
-			widget_draw_vertex_buffer(pos, 0, GL_TRIANGLE_STRIP, triangle_strip, NULL, wtb->totvert * 2 + 2);
+	/* Draw everything in one drawcall */
+	if (inner_col1[3] || inner_col2[3] || outline_col[3] || emboss_col[3] || tria_col[3] || alpha_check) {
+		widgetbase_set_uniform_colors_ubv(wtb, inner_col1, inner_col2, outline_col, emboss_col, tria_col, alpha_check);
 
-			/* emboss bottom shadow */
-			if (wtb->draw_emboss) {
-				if (emboss[3]) {
-					immUniformColor4ubv(emboss);
-					widget_draw_vertex_buffer(pos, 0, GL_TRIANGLE_STRIP, triangle_strip_emboss, NULL, wtb->halfwayvert * 2);
-				}
-			}
-			
-			gpuTranslate2f(-jit[j][0], -jit[j][1]);
-		}
-		immUnbindProgram();
+		Gwn_Batch *roundbox_batch = ui_batch_roundbox_widget_get(wtb->tria1.type);
+		draw_widgetbase_batch(roundbox_batch, wtb);
 	}
 
-	/* decoration */
-	if (wtb->tria1.tot || wtb->tria2.tot) {
+	/* DEPRECATED: should be removed at some point. */
+	if ((wtb->tria1.type == ROUNDBOX_TRIA_NONE) &&
+	    (wtb->tria1.tot || wtb->tria2.tot))
+	{
 		const unsigned char tcol[4] = {wcol->item[0],
 		                               wcol->item[1],
 		                               wcol->item[2],
@@ -838,17 +1141,14 @@ static void widgetbase_draw(uiWidgetBase *wtb, uiWidgetColors *wcol)
 		immUniformColor4ubv(tcol);
 
 		/* for each AA step */
-		for (j = 0; j < WIDGET_AA_JITTER; j++) {
-			gpuTranslate2fv(jit[j]);
-
-			if (wtb->tria1.tot)
-				shape_preset_draw_trias(&wtb->tria1, pos);
-
-			if (wtb->tria2.tot)
-				shape_preset_draw_trias(&wtb->tria2, pos);
-
-			gpuTranslate2f(-jit[j][0], -jit[j][1]);
+		immBegin(GWN_PRIM_TRIS, (wtb->tria1.tot + wtb->tria2.tot) * 3 * WIDGET_AA_JITTER);
+		if (wtb->tria1.tot){
+			shape_preset_draw_trias_aa(&wtb->tria1, pos);
+		}
+		if (wtb->tria2.tot) {
+			shape_preset_draw_trias_aa(&wtb->tria2, pos);
 		}
+		immEnd();
 
 		immUnbindProgram();
 	}
@@ -1411,6 +1711,10 @@ static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *b
 			int selsta_draw, selwidth_draw;
 			
 			if (drawstr[0] != 0) {
+				/* We are drawing on top of widget bases. Flush cache. */
+				glEnable(GL_BLEND);
+				UI_widgetbase_draw_cache_flush();
+				glDisable(GL_BLEND);
 
 				if (but->selsta >= but->ofs) {
 					selsta_draw = BLF_width(fstyle->uifont_id, drawstr + but->ofs, but->selsta - but->ofs);
@@ -1452,6 +1756,10 @@ static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *b
 			else {
 				t = 0;
 			}
+			/* We are drawing on top of widget bases. Flush cache. */
+			glEnable(GL_BLEND);
+			UI_widgetbase_draw_cache_flush();
+			glDisable(GL_BLEND);
 
 			unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_I32, 2, GWN_FETCH_INT_TO_FLOAT);
 			immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
@@ -2892,7 +3200,7 @@ void UI_draw_widget_scroll(uiWidgetColors *wcol, const rcti *rect, const rcti *s
 	else
 		rad = 0.5f * BLI_rcti_size_x(rect);
 	
-	wtb.draw_shadedir = (horizontal) ? true : false;
+	wtb.uniform_params.shade_dir = (horizontal) ? 1.0f : 0.0;
 	
 	/* draw back part, colors swapped and shading inverted */
 	if (horizontal)
@@ -3286,6 +3594,8 @@ static void widget_menubut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state),
 	
 	/* decoration */
 	shape_preset_trias_from_rect_menu(&wtb.tria1, rect);
+	/* copy size and center to 2nd tria */
+	wtb.tria2 = wtb.tria1;
 	
 	widgetbase_draw(&wtb, wcol);
 	
diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c
index d31f2dec14565712af04265eae9b5a4798099456..375711194a305dbefb337b0823b303c9b3cae1ec 100644
--- a/source/blender/editors/interface/view2d.c
+++ b/source/blender/editors/interface/view2d.c
@@ -1211,10 +1211,10 @@ static void step_to_grid(float *step, int *power, int unit)
  * - Units + clamping args will be checked, to make sure they are valid values that can be used
  *   so it is very possible that we won't return grid at all!
  *
- * - xunits,yunits	= V2D_UNIT_*  grid steps in seconds or frames
- * - xclamp,yclamp	= V2D_CLAMP_* only show whole-number intervals
- * - winx			= width of region we're drawing to, note: not used but keeping for completeness.
- * - winy			= height of region we're drawing into
+ * - xunits,yunits = V2D_UNIT_*  grid steps in seconds or frames
+ * - xclamp,yclamp = V2D_CLAMP_* only show whole-number intervals
+ * - winx          = width of region we're drawing to, note: not used but keeping for completeness.
+ * - winy          = height of region we're drawing into
  */
 View2DGrid *UI_view2d_grid_calc(
         Scene *scene, View2D *v2d,
@@ -1892,7 +1892,9 @@ void UI_view2d_scrollers_draw(const bContext *C, View2D *v2d, View2DScrollers *v
 			/* draw numbers in the appropriate range */
 			if (dfac > 0.0f) {
 				float h = 0.1f * UI_UNIT_Y + (float)(hor.ymin);
-				
+
+				BLF_batch_draw_begin();
+
 				for (; fac < hor.xmax - 0.5f * U.widget_unit; fac += dfac, val += grid->dx) {
 					
 					/* make prints look nicer for scrollers */
@@ -1919,6 +1921,8 @@ void UI_view2d_scrollers_draw(const bContext *C, View2D *v2d, View2DScrollers *v
 							break;
 					}
 				}
+
+				BLF_batch_draw_end();
 			}
 		}
 	}
diff --git a/source/blender/editors/io/io_cache.c b/source/blender/editors/io/io_cache.c
index 975bbddd8931b180440c2036215073940fd40e0c..eb79d0bec13c1cb06dffdb58c4c7d7ef8b73ae9a 100644
--- a/source/blender/editors/io/io_cache.c
+++ b/source/blender/editors/io/io_cache.c
@@ -33,7 +33,6 @@
 
 #include "BKE_cachefile.h"
 #include "BKE_context.h"
-#include "BKE_global.h"
 #include "BKE_library.h"
 #include "BKE_main.h"
 #include "BKE_report.h"
diff --git a/source/blender/editors/io/io_collada.c b/source/blender/editors/io/io_collada.c
index a42aeee912ba1b40f6a97c4f422f9ce06b00d7ba..4d0d59ae42cdabc2d4eda58dc0367fcebfc06dfa 100644
--- a/source/blender/editors/io/io_collada.c
+++ b/source/blender/editors/io/io_collada.c
@@ -151,7 +151,7 @@ static int wm_collada_export_exec(bContext *C, wmOperator *op)
 
 	include_animations       = RNA_boolean_get(op->ptr, "include_animations");
 	sample_animations        = RNA_boolean_get(op->ptr, "sample_animations");
-	sampling_rate            = (sample_animations)? RNA_int_get(op->ptr, "sampling_rate") : 0;
+	sampling_rate            = (sample_animations) ? RNA_int_get(op->ptr, "sampling_rate") : 0;
 
 	deform_bones_only        = RNA_boolean_get(op->ptr, "deform_bones_only");
 
@@ -490,8 +490,7 @@ static int wm_collada_import_exec(bContext *C, wmOperator *op)
 	import_settings.min_chain_length = min_chain_length;
 	import_settings.keep_bind_info = keep_bind_info != 0;
 
-	if (collada_import(C, &import_settings) )
-	{
+	if (collada_import(C, &import_settings)) {
 		DEG_id_tag_update(&CTX_data_scene(C)->id, DEG_TAG_BASE_FLAGS_UPDATE);
 		return OPERATOR_FINISHED;
 	}
diff --git a/source/blender/editors/lattice/editlattice_undo.c b/source/blender/editors/lattice/editlattice_undo.c
index aa817928f92553fe145279775b1882a19340a452..58fa08e5aa9b4d180b931d734aa3db78b594d251 100644
--- a/source/blender/editors/lattice/editlattice_undo.c
+++ b/source/blender/editors/lattice/editlattice_undo.c
@@ -34,6 +34,7 @@
 #include "MEM_guardedalloc.h"
 
 #include "BLI_utildefines.h"
+#include "BLI_array_utils.h"
 
 #include "DNA_curve_types.h"
 #include "DNA_lattice_types.h"
@@ -41,31 +42,40 @@
 #include "DNA_scene_types.h"
 
 #include "BKE_context.h"
+#include "BKE_undo_system.h"
 
+#include "DEG_depsgraph.h"
+
+#include "ED_object.h"
 #include "ED_lattice.h"
 #include "ED_util.h"
 
+#include "WM_types.h"
+#include "WM_api.h"
+
 #include "lattice_intern.h"
 
+/* -------------------------------------------------------------------- */
+/** \name Undo Conversion
+ * \{ */
+
 typedef struct UndoLattice {
 	BPoint *def;
 	int pntsu, pntsv, pntsw, actbp;
+	size_t undo_size;
 } UndoLattice;
 
-static void undoLatt_to_editLatt(void *data, void *edata, void *UNUSED(obdata))
+static void undolatt_to_editlatt(UndoLattice *ult, EditLatt *editlatt)
 {
-	UndoLattice *ult = (UndoLattice *)data;
-	EditLatt *editlatt = (EditLatt *)edata;
-	int a = editlatt->latt->pntsu * editlatt->latt->pntsv * editlatt->latt->pntsw;
+	int len = editlatt->latt->pntsu * editlatt->latt->pntsv * editlatt->latt->pntsw;
 
-	memcpy(editlatt->latt->def, ult->def, a * sizeof(BPoint));
+	memcpy(editlatt->latt->def, ult->def, sizeof(BPoint) * len);
 	editlatt->latt->actbp = ult->actbp;
 }
 
-static void *editLatt_to_undoLatt(void *edata, void *UNUSED(obdata))
+static void *undolatt_from_editlatt(UndoLattice *ult, EditLatt *editlatt)
 {
-	UndoLattice *ult = MEM_callocN(sizeof(UndoLattice), "UndoLattice");
-	EditLatt *editlatt = (EditLatt *)edata;
+	BLI_assert(BLI_array_is_zeroed(ult, 1));
 
 	ult->def = MEM_dupallocN(editlatt->latt->def);
 	ult->pntsu = editlatt->latt->pntsu;
@@ -73,17 +83,19 @@ static void *editLatt_to_undoLatt(void *edata, void *UNUSED(obdata))
 	ult->pntsw = editlatt->latt->pntsw;
 	ult->actbp = editlatt->latt->actbp;
 
+	ult->undo_size += sizeof(*ult->def) * ult->pntsu * ult->pntsv * ult->pntsw;
+
 	return ult;
 }
 
-static void free_undoLatt(void *data)
+static void undolatt_free_data(UndoLattice *ult)
 {
-	UndoLattice *ult = (UndoLattice *)data;
-
-	if (ult->def) MEM_freeN(ult->def);
-	MEM_freeN(ult);
+	if (ult->def) {
+		MEM_freeN(ult->def);
+	}
 }
 
+#if 0
 static int validate_undoLatt(void *data, void *edata)
 {
 	UndoLattice *ult = (UndoLattice *)data;
@@ -93,21 +105,92 @@ static int validate_undoLatt(void *data, void *edata)
 	        ult->pntsv == editlatt->latt->pntsv &&
 	        ult->pntsw == editlatt->latt->pntsw);
 }
+#endif
 
-static void *get_editlatt(bContext *C)
+static Object *editlatt_object_from_context(bContext *C)
 {
 	Object *obedit = CTX_data_edit_object(C);
-
 	if (obedit && obedit->type == OB_LATTICE) {
 		Lattice *lt = obedit->data;
-		return lt->editlatt;
+		if (lt->editlatt != NULL) {
+			return obedit;
+		}
 	}
 
 	return NULL;
 }
 
-/* and this is all the undo system needs to know */
-void undo_push_lattice(bContext *C, const char *name)
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Implements ED Undo System
+ * \{ */
+
+typedef struct LatticeUndoStep {
+	UndoStep step;
+	/* note: will split out into list for multi-object-editmode. */
+	UndoRefID_Object obedit_ref;
+	UndoLattice data;
+} LatticeUndoStep;
+
+static bool lattice_undosys_poll(bContext *C)
+{
+	return editlatt_object_from_context(C) != NULL;
+}
+
+static bool lattice_undosys_step_encode(struct bContext *C, UndoStep *us_p)
 {
-	undo_editmode_push(C, name, get_editlatt, free_undoLatt, undoLatt_to_editLatt, editLatt_to_undoLatt, validate_undoLatt);
+	LatticeUndoStep *us = (LatticeUndoStep *)us_p;
+	us->obedit_ref.ptr = editlatt_object_from_context(C);
+	Lattice *lt = us->obedit_ref.ptr->data;
+	undolatt_from_editlatt(&us->data, lt->editlatt);
+	us->step.data_size = us->data.undo_size;
+	return true;
 }
+
+static void lattice_undosys_step_decode(struct bContext *C, UndoStep *us_p, int UNUSED(dir))
+{
+	/* TODO(campbell): undo_system: use low-level API to set mode. */
+	ED_object_mode_set(C, OB_MODE_EDIT);
+	BLI_assert(lattice_undosys_poll(C));
+
+	LatticeUndoStep *us = (LatticeUndoStep *)us_p;
+	Object *obedit = us->obedit_ref.ptr;
+	Lattice *lt = obedit->data;
+	EditLatt *editlatt = lt->editlatt;
+	undolatt_to_editlatt(&us->data, editlatt);
+	DEG_id_tag_update(&obedit->id, OB_RECALC_DATA);
+	WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
+}
+
+static void lattice_undosys_step_free(UndoStep *us_p)
+{
+	LatticeUndoStep *us = (LatticeUndoStep *)us_p;
+	undolatt_free_data(&us->data);
+}
+
+static void lattice_undosys_foreach_ID_ref(
+        UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
+{
+	LatticeUndoStep *us = (LatticeUndoStep *)us_p;
+	foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->obedit_ref));
+}
+
+/* Export for ED_undo_sys. */
+void ED_lattice_undosys_type(UndoType *ut)
+{
+	ut->name = "Edit Lattice";
+	ut->poll = lattice_undosys_poll;
+	ut->step_encode = lattice_undosys_step_encode;
+	ut->step_decode = lattice_undosys_step_decode;
+	ut->step_free = lattice_undosys_step_free;
+
+	ut->step_foreach_ID_ref = lattice_undosys_foreach_ID_ref;
+
+	ut->mode = BKE_UNDOTYPE_MODE_STORE;
+	ut->use_context = true;
+
+	ut->step_size = sizeof(LatticeUndoStep);
+}
+
+/** \} */
diff --git a/source/blender/editors/lattice/lattice_intern.h b/source/blender/editors/lattice/lattice_intern.h
index 94f528a04574d9b66d9870129010ca53e2e8d8b9..7902b992270ca14c0f7aa0918418fcdb735efea8 100644
--- a/source/blender/editors/lattice/lattice_intern.h
+++ b/source/blender/editors/lattice/lattice_intern.h
@@ -21,7 +21,7 @@
  * ***** END GPL LICENSE BLOCK *****
  */
 
-/** \file blender/editors/metaball/lattice_intern.h
+/** \file blender/editors/lattice/lattice_intern.h
  *  \ingroup edlattice
  */
 
diff --git a/source/blender/editors/lattice/lattice_ops.c b/source/blender/editors/lattice/lattice_ops.c
index 37a10cbe12a666a07846f8d1ea23e29ff80888e4..d3d57a0b51074a97f3aa2b320d5fbea3ef251dc7 100644
--- a/source/blender/editors/lattice/lattice_ops.c
+++ b/source/blender/editors/lattice/lattice_ops.c
@@ -24,7 +24,7 @@
  * ***** END GPL LICENSE BLOCK *****
  */
 
-/** \file blender/editors/metaball/lattice_ops.c
+/** \file blender/editors/lattice/lattice_ops.c
  *  \ingroup edlattice
  */
 
diff --git a/source/blender/editors/mesh/editmesh_bisect.c b/source/blender/editors/mesh/editmesh_bisect.c
index 1937a9f689113c13f6d32edec5c8c457bb389545..741d16206cd57c9313e3cfc61e4bd4b4bc7b6f18 100644
--- a/source/blender/editors/mesh/editmesh_bisect.c
+++ b/source/blender/editors/mesh/editmesh_bisect.c
@@ -58,7 +58,7 @@
 
 #ifdef USE_MANIPULATOR
 #include "ED_manipulator_library.h"
-#include "ED_util.h"
+#include "ED_undo.h"
 #endif
 
 static int mesh_bisect_exec(bContext *C, wmOperator *op);
diff --git a/source/blender/editors/mesh/editmesh_extrude.c b/source/blender/editors/mesh/editmesh_extrude.c
index aee9785ea83bdfcd76d47623803d03fd6691bd72..41b3ab0079b54b84c1581541d17d5f664a3fa149 100644
--- a/source/blender/editors/mesh/editmesh_extrude.c
+++ b/source/blender/editors/mesh/editmesh_extrude.c
@@ -36,8 +36,6 @@
 #include "BLI_listbase.h"
 
 #include "BKE_context.h"
-#include "BKE_global.h"
-#include "BKE_main.h"
 #include "BKE_object.h"
 #include "BKE_report.h"
 #include "BKE_editmesh.h"
@@ -63,7 +61,7 @@
 
 #ifdef USE_MANIPULATOR
 #include "ED_manipulator_library.h"
-#include "ED_util.h"
+#include "ED_undo.h"
 #endif
 
 /* -------------------------------------------------------------------- */
diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c
index cd894502bc4fc9a031b1fa549d367ac301c8f10c..0a12291c12803a353f3e453157bac5788c78e008 100644
--- a/source/blender/editors/mesh/editmesh_knife.c
+++ b/source/blender/editors/mesh/editmesh_knife.c
@@ -860,7 +860,7 @@ static void knife_cut_face(KnifeTool_OpData *kcd, BMFace *f, ListBase *hits)
 {
 	Ref *r;
 
-	if (BLI_listbase_count_ex(hits, 2) != 2)
+	if (BLI_listbase_count_at_most(hits, 2) != 2)
 		return;
 
 	for (r = hits->first; r->next; r = r->next) {
diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c
index 513e939c5290268b7551c4bdf58267944845e3e0..87937fd414682996239730af5be70522584b1042 100644
--- a/source/blender/editors/mesh/editmesh_select.c
+++ b/source/blender/editors/mesh/editmesh_select.c
@@ -295,7 +295,7 @@ bool EDBM_backbuf_border_mask_init(const struct EvaluationContext *eval_ctx, Vie
 
 	/* method in use for face selecting too */
 	if (vc->obedit == NULL) {
-		if (!BKE_paint_select_elem_test(vc->obact, eval_ctx->object_mode)) {
+		if (!BKE_paint_select_elem_test(vc->obact)) {
 			return false;
 		}
 	}
@@ -347,7 +347,7 @@ bool EDBM_backbuf_circle_init(
 
 	/* method in use for face selecting too */
 	if (vc->obedit == NULL) {
-		if (!BKE_paint_select_elem_test(vc->obact, eval_ctx->object_mode)) {
+		if (!BKE_paint_select_elem_test(vc->obact)) {
 			return false;
 		}
 	}
@@ -2006,7 +2006,7 @@ bool EDBM_select_pick(bContext *C, const int mval[2], bool extend, bool deselect
 				const int cd_fmap_offset = CustomData_get_offset(&vc.em->bm->pdata, CD_FACEMAP);
 				if (cd_fmap_offset != -1) {
 					int map = *((int *)BM_ELEM_CD_GET_VOID_P(efa, cd_fmap_offset));
-					if ((map < -1) || (map > BLI_listbase_count_ex(&vc.obedit->fmaps, map))) {
+					if ((map < -1) || (map > BLI_listbase_count_at_most(&vc.obedit->fmaps, map))) {
 						map = -1;
 					}
 					map += 1;
diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c
index ce7610b5d699bd4554d0fc90a672c1b697ebb343..dd64c957c4a6531e937abe9a08226de402a63305 100644
--- a/source/blender/editors/mesh/editmesh_tools.c
+++ b/source/blender/editors/mesh/editmesh_tools.c
@@ -1126,7 +1126,7 @@ static bool bm_vert_connect_select_history(BMesh *bm)
 	 * - Otherwise connect faces.
 	 * - If all edges have been created already, closed the loop.
 	 */
-	if (BLI_listbase_count_ex(&bm->selected, 2) == 2 && (bm->totvertsel > 2)) {
+	if (BLI_listbase_count_at_most(&bm->selected, 2) == 2 && (bm->totvertsel > 2)) {
 		BMEditSelection *ese;
 		int tot = 0;
 		bool changed = false;
@@ -1947,7 +1947,7 @@ static int edbm_do_smooth_laplacian_vertex_exec(bContext *C, wmOperator *op)
 	BMIter fiter;
 	BMFace *f;
 
-	/* Check if select faces are triangles	*/
+	/* Check if select faces are triangles */
 	BM_ITER_MESH (f, &fiter, em->bm, BM_FACES_OF_MESH) {
 		if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
 			if (f->len > 4) {
@@ -3762,7 +3762,7 @@ static void edbm_fill_grid_prepare(BMesh *bm, int offset, int *r_span, bool span
 			 *
 			 * note: we may have already checked 'edbm_fill_grid_vert_tag_angle()' on each
 			 * vert, but advantage of de-duplicating is minimal. */
-			struct SortPointerByFloat *ele_sort = MEM_mallocN(sizeof(*ele_sort) * verts_len, __func__);
+			struct SortPtrByFloat *ele_sort = MEM_mallocN(sizeof(*ele_sort) * verts_len, __func__);
 			LinkData *v_link;
 			for (v_link = verts->first, i = 0; v_link; v_link = v_link->next, i++) {
 				BMVert *v = v_link->data;
diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c
index 11667ed571051068361ed7c3ed4e1bf1b0340a51..ab7e13117a04802a38456ccd9d93ba46bfc95548 100644
--- a/source/blender/editors/mesh/editmesh_undo.c
+++ b/source/blender/editors/mesh/editmesh_undo.c
@@ -29,16 +29,25 @@
 #include "DNA_key_types.h"
 
 #include "BLI_listbase.h"
+#include "BLI_array_utils.h"
+#include "BLI_alloca.h"
 
 #include "BKE_DerivedMesh.h"
 #include "BKE_context.h"
 #include "BKE_key.h"
 #include "BKE_mesh.h"
 #include "BKE_editmesh.h"
+#include "BKE_undo_system.h"
 
+#include "DEG_depsgraph.h"
+
+#include "ED_object.h"
 #include "ED_mesh.h"
 #include "ED_util.h"
 
+#include "WM_types.h"
+#include "WM_api.h"
+
 #define USE_ARRAY_STORE
 
 #ifdef USE_ARRAY_STORE
@@ -60,6 +69,9 @@
 #  include "BLI_task.h"
 #endif
 
+/* -------------------------------------------------------------------- */
+/** \name Undo Conversion
+ * \{ */
 
 #ifdef USE_ARRAY_STORE
 
@@ -95,6 +107,8 @@ typedef struct UndoMesh {
 		BArrayState *mselect;
 	} store;
 #endif  /* USE_ARRAY_STORE */
+
+	size_t undo_size;
 } UndoMesh;
 
 
@@ -247,7 +261,7 @@ static void um_arraystore_cd_free(BArrayCustomData *bcd)
 		BArrayCustomData *bcd_next = bcd->next;
 		const int stride = CustomData_sizeof(bcd->type);
 		BArrayStore *bs = BLI_array_store_at_size_get(&um_arraystore.bs_stride, stride);
-		for (int i = 0; i <		bcd->states_len; i++) {
+		for (int i = 0; i < bcd->states_len; i++) {
 			if (bcd->states[i]) {
 				BLI_array_store_state_remove(bs, bcd->states[i]);
 			}
@@ -474,23 +488,17 @@ static void um_arraystore_free(UndoMesh *um)
 
 /* for callbacks */
 /* undo simply makes copies of a bmesh */
-static void *editbtMesh_to_undoMesh(void *emv, void *obdata)
+static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key)
 {
-
+	BLI_assert(BLI_array_is_zeroed(um, 1));
 #ifdef USE_ARRAY_STORE_THREAD
 	/* changes this waits is low, but must have finished */
 	if (um_arraystore.task_pool) {
 		BLI_task_pool_work_and_wait(um_arraystore.task_pool);
 	}
 #endif
-
-	BMEditMesh *em = emv;
-	Mesh *obme = obdata;
-
-	UndoMesh *um = MEM_callocN(sizeof(UndoMesh), "undo Mesh");
-
 	/* make sure shape keys work */
-	um->me.key = obme->key ? BKE_key_copy_nolib(obme->key) : NULL;
+	um->me.key = key ? BKE_key_copy_nolib(key) : NULL;
 
 	/* BM_mesh_validate(em->bm); */ /* for troubleshooting */
 
@@ -536,13 +544,12 @@ static void *editbtMesh_to_undoMesh(void *emv, void *obdata)
 	return um;
 }
 
-static void undoMesh_to_editbtMesh(void *um_v, void *em_v, void *obdata)
+static void undomesh_to_editmesh(UndoMesh *um, BMEditMesh *em, Mesh *obmesh)
 {
-	BMEditMesh *em = em_v, *em_tmp;
+	BMEditMesh *em_tmp;
 	Object *ob = em->ob;
-	UndoMesh *um = um_v;
 	BMesh *bm;
-	Key *key = ((Mesh *) obdata)->key;
+	Key *key = obmesh->key;
 
 #ifdef USE_ARRAY_STORE
 #ifdef USE_ARRAY_STORE_THREAD
@@ -615,9 +622,8 @@ static void undoMesh_to_editbtMesh(void *um_v, void *em_v, void *obdata)
 #endif
 }
 
-static void free_undo(void *um_v)
+static void undomesh_free_data(UndoMesh *um)
 {
-	UndoMesh *um = um_v;
 	Mesh *me = &um->me;
 
 #ifdef USE_ARRAY_STORE
@@ -644,28 +650,101 @@ static void free_undo(void *um_v)
 	}
 
 	BKE_mesh_free(me);
-	MEM_freeN(me);
 }
 
-static void *getEditMesh(bContext *C)
+static Object *editmesh_object_from_context(bContext *C)
 {
 	Object *obedit = CTX_data_edit_object(C);
 	if (obedit && obedit->type == OB_MESH) {
 		Mesh *me = obedit->data;
-		return me->edit_btmesh;
+		if (me->edit_btmesh != NULL) {
+			return obedit;
+		}
 	}
 	return NULL;
 }
 
-/* and this is all the undo system needs to know */
-void undo_push_mesh(bContext *C, const char *name)
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Implements ED Undo System
+ * \{ */
+
+typedef struct MeshUndoStep {
+	UndoStep step;
+	/* Use for all ID lookups (can be NULL). */
+	struct UndoIDPtrMap *id_map;
+
+	/* note: will split out into list for multi-object-editmode. */
+	UndoRefID_Object obedit_ref;
+	UndoMesh data;
+} MeshUndoStep;
+
+static bool mesh_undosys_poll(bContext *C)
 {
-	/* em->ob gets out of date and crashes on mesh undo,
-	 * this is an easy way to ensure its OK
-	 * though we could investigate the matter further. */
-	Object *obedit = CTX_data_edit_object(C);
-	BMEditMesh *em = BKE_editmesh_from_object(obedit);
-	em->ob = obedit;
+	return editmesh_object_from_context(C) != NULL;
+}
 
-	undo_editmode_push(C, name, getEditMesh, free_undo, undoMesh_to_editbtMesh, editbtMesh_to_undoMesh, NULL);
+static bool mesh_undosys_step_encode(struct bContext *C, UndoStep *us_p)
+{
+	MeshUndoStep *us = (MeshUndoStep *)us_p;
+	us->obedit_ref.ptr = editmesh_object_from_context(C);
+	Mesh *me = us->obedit_ref.ptr->data;
+	undomesh_from_editmesh(&us->data, me->edit_btmesh, me->key);
+	us->step.data_size = us->data.undo_size;
+	return true;
 }
+
+static void mesh_undosys_step_decode(struct bContext *C, UndoStep *us_p, int UNUSED(dir))
+{
+	/* TODO(campbell): undo_system: use low-level API to set mode. */
+	ED_object_mode_set(C, OB_MODE_EDIT);
+	BLI_assert(mesh_undosys_poll(C));
+
+	MeshUndoStep *us = (MeshUndoStep *)us_p;
+	Object *obedit = us->obedit_ref.ptr;
+	Mesh *me = obedit->data;
+	BMEditMesh *em = me->edit_btmesh;
+	undomesh_to_editmesh(&us->data, em, obedit->data);
+	DEG_id_tag_update(&obedit->id, OB_RECALC_DATA);
+	WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
+}
+
+static void mesh_undosys_step_free(UndoStep *us_p)
+{
+	MeshUndoStep *us = (MeshUndoStep *)us_p;
+	undomesh_free_data(&us->data);
+
+	if (us->id_map != NULL) {
+		BKE_undosys_ID_map_destroy(us->id_map);
+	}
+}
+
+static void mesh_undosys_foreach_ID_ref(
+        UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
+{
+	MeshUndoStep *us = (MeshUndoStep *)us_p;
+	foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->obedit_ref));
+	if (us->id_map != NULL) {
+		BKE_undosys_ID_map_foreach_ID_ref(us->id_map, foreach_ID_ref_fn, user_data);
+	}
+}
+
+/* Export for ED_undo_sys. */
+void ED_mesh_undosys_type(UndoType *ut)
+{
+	ut->name = "Edit Mesh";
+	ut->poll = mesh_undosys_poll;
+	ut->step_encode = mesh_undosys_step_encode;
+	ut->step_decode = mesh_undosys_step_decode;
+	ut->step_free = mesh_undosys_step_free;
+
+	ut->step_foreach_ID_ref = mesh_undosys_foreach_ID_ref;
+
+	ut->mode = BKE_UNDOTYPE_MODE_STORE;
+	ut->use_context = true;
+
+	ut->step_size = sizeof(MeshUndoStep);
+}
+
+/** \} */
diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c
index 312dc000a2b58adcaa2c5c380beeabd90e55ffd1..3382847c8a4eff7931cfc935460f05d117dd9517 100644
--- a/source/blender/editors/mesh/editmesh_utils.c
+++ b/source/blender/editors/mesh/editmesh_utils.c
@@ -43,8 +43,6 @@
 
 #include "BKE_DerivedMesh.h"
 #include "BKE_context.h"
-#include "BKE_global.h"
-#include "BKE_main.h"
 #include "BKE_mesh.h"
 #include "BKE_mesh_mapping.h"
 #include "BKE_report.h"
diff --git a/source/blender/editors/mesh/mesh_data.c b/source/blender/editors/mesh/mesh_data.c
index aca67dc84e928f9237c24915363b601d7f263218..69e265f7315d4e226c24d9d4a35f14d4b53d98e4 100644
--- a/source/blender/editors/mesh/mesh_data.c
+++ b/source/blender/editors/mesh/mesh_data.c
@@ -506,14 +506,13 @@ static int layers_poll(bContext *C)
 
 static int mesh_uv_texture_add_exec(bContext *C, wmOperator *UNUSED(op))
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Object *ob = ED_object_context(C);
 	Mesh *me = ob->data;
 
 	if (ED_mesh_uv_texture_add(me, NULL, true) == -1)
 		return OPERATOR_CANCELLED;
 
-	if (workspace->object_mode & OB_MODE_TEXTURE_PAINT) {
+	if (ob->mode & OB_MODE_TEXTURE_PAINT) {
 		Scene *scene = CTX_data_scene(C);
 		BKE_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL);
 		WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL);
@@ -623,14 +622,13 @@ void MESH_OT_drop_named_image(wmOperatorType *ot)
 
 static int mesh_uv_texture_remove_exec(bContext *C, wmOperator *UNUSED(op))
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Object *ob = ED_object_context(C);
 	Mesh *me = ob->data;
 
 	if (!ED_mesh_uv_texture_remove_active(me))
 		return OPERATOR_CANCELLED;
 
-	if (workspace->object_mode & OB_MODE_TEXTURE_PAINT) {
+	if (ob->mode & OB_MODE_TEXTURE_PAINT) {
 		Scene *scene = CTX_data_scene(C);
 		BKE_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL);
 		WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL);
@@ -744,14 +742,13 @@ static int mesh_customdata_mask_clear_poll(bContext *C)
 {
 	Object *ob = ED_object_context(C);
 	if (ob && ob->type == OB_MESH) {
-		const WorkSpace *workspace = CTX_wm_workspace(C);
+		Mesh *me = ob->data;
 
 		/* special case - can't run this if we're in sculpt mode */
-		if (workspace->object_mode & OB_MODE_SCULPT) {
+		if (ob->mode & OB_MODE_SCULPT) {
 			return false;
 		}
 
-		Mesh *me = ob->data;
 		if (!ID_IS_LINKED(me)) {
 			CustomData *data = GET_CD_DATA(me, vdata);
 			if (CustomData_has_layer(data, CD_PAINT_MASK)) {
diff --git a/source/blender/editors/mesh/mesh_navmesh.c b/source/blender/editors/mesh/mesh_navmesh.c
index aaa06951ec6c0dc3fe51b7ec23ce5e4bcad32f3e..bd2ad21d51ca24b24233039abfea3a7173e3323f 100644
--- a/source/blender/editors/mesh/mesh_navmesh.c
+++ b/source/blender/editors/mesh/mesh_navmesh.c
@@ -662,10 +662,8 @@ void MESH_OT_navmesh_face_add(struct wmOperatorType *ot)
 
 static int navmesh_obmode_data_poll(bContext *C)
 {
-	EvaluationContext eval_ctx;
-	CTX_data_eval_ctx(C, &eval_ctx);
 	Object *ob = ED_object_active_context(C);
-	if (ob && (eval_ctx.object_mode == OB_MODE_OBJECT) && (ob->type == OB_MESH)) {
+	if (ob && (ob->mode == OB_MODE_OBJECT) && (ob->type == OB_MESH)) {
 		Mesh *me = ob->data;
 		return CustomData_has_layer(&me->pdata, CD_RECAST);
 	}
@@ -674,10 +672,8 @@ static int navmesh_obmode_data_poll(bContext *C)
 
 static int navmesh_obmode_poll(bContext *C)
 {
-	EvaluationContext eval_ctx;
-	CTX_data_eval_ctx(C, &eval_ctx);
 	Object *ob = ED_object_active_context(C);
-	if (ob && (eval_ctx.object_mode == OB_MODE_OBJECT) && (ob->type == OB_MESH)) {
+	if (ob && (ob->mode == OB_MODE_OBJECT) && (ob->type == OB_MESH)) {
 		return true;
 	}
 	return false;
diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.c
index fd5beac9cc6adeab3e97507f16f788647d1acba9..531a26a66a8a294487e8d5f3e68c5588259fdc7d 100644
--- a/source/blender/editors/mesh/meshtools.c
+++ b/source/blender/editors/mesh/meshtools.c
@@ -79,7 +79,7 @@
  * return 0 if no join is made (error) and 1 if the join is done */
 
 static void join_mesh_single(
-        bContext *C, Main *bmain, Scene *scene,
+        const EvaluationContext *eval_ctx, Main *bmain, Scene *scene,
         Object *ob_dst, Object *ob_src, float imat[4][4],
         MVert **mvert_pp, MEdge **medge_pp, MLoop **mloop_pp, MPoly **mpoly_pp,
         CustomData *vdata, CustomData *edata, CustomData *ldata, CustomData *pdata,
@@ -88,7 +88,6 @@ static void join_mesh_single(
         Material **matar, int *matmap, int totcol,
         int *vertofs, int *edgeofs, int *loopofs, int *polyofs)
 {
-	EvaluationContext eval_ctx;
 	int a, b;
 
 	Mesh *me = ob_src->data;
@@ -97,8 +96,6 @@ static void join_mesh_single(
 	MLoop *mloop = *mloop_pp;
 	MPoly *mpoly = *mpoly_pp;
 
-	CTX_data_eval_ctx(C, &eval_ctx);
-
 	if (me->totvert) {
 		/* merge customdata flag */
 		((Mesh *)ob_dst->data)->cd_flag |= me->cd_flag;
@@ -210,13 +207,12 @@ static void join_mesh_single(
 		if (ob_src != ob_dst) {
 			MultiresModifierData *mmd;
 
-			multiresModifier_prepare_join(&eval_ctx, scene, ob_src, ob_dst);
+			multiresModifier_prepare_join(eval_ctx, scene, ob_src, ob_dst);
 
 			if ((mmd = get_multires_modifier(scene, ob_src, true))) {
-				ED_object_iter_other(
-				        &eval_ctx, bmain, ob_src, true,
-				        ED_object_multires_update_totlevels_cb,
-				        &mmd->totlvl);
+				ED_object_iter_other(bmain, ob_src, true,
+				                     ED_object_multires_update_totlevels_cb,
+				                     &mmd->totlvl);
 			}
 		}
 
@@ -266,7 +262,6 @@ static void join_mesh_single(
 
 int join_mesh_exec(bContext *C, wmOperator *op)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Main *bmain = CTX_data_main(C);
 	Scene *scene = CTX_data_scene(C);
 	Object *ob = CTX_data_active_object(C);
@@ -286,7 +281,7 @@ int join_mesh_exec(bContext *C, wmOperator *op)
 	bDeformGroup *dg, *odg;
 	CustomData vdata, edata, fdata, ldata, pdata;
 
-	if (workspace->object_mode & OB_MODE_EDIT) {
+	if (ob->mode & OB_MODE_EDIT) {
 		BKE_report(op->reports, RPT_WARNING, "Cannot join while in edit mode");
 		return OPERATOR_CANCELLED;
 	}
@@ -296,7 +291,10 @@ int join_mesh_exec(bContext *C, wmOperator *op)
 		BKE_report(op->reports, RPT_WARNING, "Active object is not a mesh");
 		return OPERATOR_CANCELLED;
 	}
-	
+
+	EvaluationContext eval_ctx;
+	CTX_data_eval_ctx(C, &eval_ctx);
+
 	/* count & check */
 	CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases)
 	{
@@ -491,7 +489,7 @@ int join_mesh_exec(bContext *C, wmOperator *op)
 	 * active mesh will remain first ones in new result of the merge, in same order for CD layers, etc. See also T50084.
 	 */
 	join_mesh_single(
-	            C, bmain, scene,
+	            &eval_ctx, bmain, scene,
 	            ob, ob, imat,
 	            &mvert, &medge, &mloop, &mpoly,
 	            &vdata, &edata, &ldata, &pdata,
@@ -508,7 +506,7 @@ int join_mesh_exec(bContext *C, wmOperator *op)
 		/* only join if this is a mesh */
 		if (base->object->type == OB_MESH) {
 			join_mesh_single(
-			            C, bmain, scene,
+			            &eval_ctx, bmain, scene,
 			            ob, base->object, imat,
 			            &mvert, &medge, &mloop, &mpoly,
 			            &vdata, &edata, &ldata, &pdata,
@@ -1253,17 +1251,17 @@ bool ED_mesh_pick_vert(bContext *C, Object *ob, const int mval[2], unsigned int
 
 MDeformVert *ED_mesh_active_dvert_get_em(Object *ob, BMVert **r_eve)
 {
-	if (ob->type == OB_MESH && ob->defbase.first) {
+	if (ob->mode & OB_MODE_EDIT && ob->type == OB_MESH && ob->defbase.first) {
 		Mesh *me = ob->data;
-		if (me->edit_btmesh != NULL) {
-			BMesh *bm = me->edit_btmesh->bm;
-			const int cd_dvert_offset = CustomData_get_offset(&bm->vdata, CD_MDEFORMVERT);
-			if (cd_dvert_offset != -1) {
-				BMVert *eve = BM_mesh_active_vert_get(bm);
-				if (eve) {
-					if (r_eve) *r_eve = eve;
-					return BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset);
-				}
+		BMesh *bm = me->edit_btmesh->bm;
+		const int cd_dvert_offset = CustomData_get_offset(&bm->vdata, CD_MDEFORMVERT);
+
+		if (cd_dvert_offset != -1) {
+			BMVert *eve = BM_mesh_active_vert_get(bm);
+
+			if (eve) {
+				if (r_eve) *r_eve = eve;
+				return BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset);
 			}
 		}
 	}
@@ -1288,8 +1286,7 @@ MDeformVert *ED_mesh_active_dvert_get_ob(Object *ob, int *r_index)
 MDeformVert *ED_mesh_active_dvert_get_only(Object *ob)
 {
 	if (ob->type == OB_MESH) {
-		Mesh *me = ob->data;
-		if (me->edit_btmesh != NULL) {
+		if (ob->mode & OB_MODE_EDIT) {
 			return ED_mesh_active_dvert_get_em(ob, NULL);
 		}
 		else {
diff --git a/source/blender/editors/metaball/editmball_undo.c b/source/blender/editors/metaball/editmball_undo.c
index 974bfb237d38bb26baf77392ee5ba3d2cb55eba9..cc461c0c365947b4f95fab9861f8e40e9915dcdd 100644
--- a/source/blender/editors/metaball/editmball_undo.c
+++ b/source/blender/editors/metaball/editmball_undo.c
@@ -29,19 +29,32 @@
 
 #include "BLI_utildefines.h"
 #include "BLI_listbase.h"
+#include "BLI_array_utils.h"
 
 #include "DNA_defs.h"
 #include "DNA_meta_types.h"
 #include "DNA_object_types.h"
 
 #include "BKE_context.h"
+#include "BKE_undo_system.h"
 
+#include "DEG_depsgraph.h"
+
+#include "ED_object.h"
 #include "ED_mball.h"
 #include "ED_util.h"
 
+#include "WM_types.h"
+#include "WM_api.h"
+
+/* -------------------------------------------------------------------- */
+/** \name Undo Conversion
+ * \{ */
+
 typedef struct UndoMBall {
 	ListBase editelems;
 	int lastelem_index;
+	size_t undo_size;
 } UndoMBall;
 
 /* free all MetaElems from ListBase */
@@ -58,11 +71,8 @@ static void freeMetaElemlist(ListBase *lb)
 	}
 }
 
-static void undoMball_to_editMball(void *umb_v, void *mb_v, void *UNUSED(obdata))
+static void undomball_to_editmball(UndoMBall *umb, MetaBall *mb)
 {
-	MetaBall *mb = mb_v;
-	UndoMBall *umb = umb_v;
-
 	freeMetaElemlist(mb->editelems);
 	mb->lastelem = NULL;
 
@@ -75,18 +85,15 @@ static void undoMball_to_editMball(void *umb_v, void *mb_v, void *UNUSED(obdata)
 			mb->lastelem = ml_edit;
 		}
 	}
-	
 }
 
-static void *editMball_to_undoMball(void *mb_v, void *UNUSED(obdata))
+static void *editmball_from_undomball(UndoMBall *umb, MetaBall *mb)
 {
-	MetaBall *mb = mb_v;
-	UndoMBall *umb;
+	BLI_assert(BLI_array_is_zeroed(umb, 1));
 
 	/* allocate memory for undo ListBase */
-	umb = MEM_callocN(sizeof(UndoMBall), __func__);
 	umb->lastelem_index = -1;
-	
+
 	/* copy contents of current ListBase to the undo ListBase */
 	int index = 0;
 	for (MetaElem *ml_edit = mb->editelems->first; ml_edit; ml_edit = ml_edit->next, index += 1) {
@@ -95,37 +102,99 @@ static void *editMball_to_undoMball(void *mb_v, void *UNUSED(obdata))
 		if (ml_edit == mb->lastelem) {
 			umb->lastelem_index = index;
 		}
+		umb->undo_size += sizeof(MetaElem);
 	}
-	
+
 	return umb;
 }
 
 /* free undo ListBase of MetaElems */
-static void free_undoMball(void *umb_v)
+static void undomball_free_data(UndoMBall *umb)
 {
-	UndoMBall *umb = umb_v;
-	
 	freeMetaElemlist(&umb->editelems);
-	MEM_freeN(umb);
 }
 
-static MetaBall *metaball_get_obdata(Object *ob)
+static Object *editmball_object_from_context(bContext *C)
 {
-	if (ob && ob->type == OB_MBALL) {
-		return ob->data;
+	Object *obedit = CTX_data_edit_object(C);
+	if (obedit && obedit->type == OB_MBALL) {
+		MetaBall *mb = obedit->data;
+		if (mb->editelems != NULL) {
+			return obedit;
+		}
 	}
 	return NULL;
 }
 
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Implements ED Undo System
+ * \{ */
 
-static void *get_data(bContext *C)
+typedef struct MBallUndoStep {
+	UndoStep step;
+	/* note: will split out into list for multi-object-editmode. */
+	UndoRefID_Object obedit_ref;
+	UndoMBall data;
+} MBallUndoStep;
+
+static bool mball_undosys_poll(bContext *C)
 {
-	Object *obedit = CTX_data_edit_object(C);
-	return metaball_get_obdata(obedit);
+	return editmball_object_from_context(C) != NULL;
+}
+
+static bool mball_undosys_step_encode(struct bContext *C, UndoStep *us_p)
+{
+	MBallUndoStep *us = (MBallUndoStep *)us_p;
+	us->obedit_ref.ptr = editmball_object_from_context(C);
+	MetaBall *mb = us->obedit_ref.ptr->data;
+	editmball_from_undomball(&us->data, mb);
+	us->step.data_size = us->data.undo_size;
+	return true;
 }
 
-/* this is undo system for MetaBalls */
-void undo_push_mball(bContext *C, const char *name)
+static void mball_undosys_step_decode(struct bContext *C, UndoStep *us_p, int UNUSED(dir))
 {
-	undo_editmode_push(C, name, get_data, free_undoMball, undoMball_to_editMball, editMball_to_undoMball, NULL);
+	ED_object_mode_set(C, OB_MODE_EDIT);
+
+	MBallUndoStep *us = (MBallUndoStep *)us_p;
+	Object *obedit = us->obedit_ref.ptr;
+	MetaBall *mb = obedit->data;
+	undomball_to_editmball(&us->data, mb);
+	DEG_id_tag_update(&obedit->id, OB_RECALC_DATA);
+	WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
 }
+
+static void mball_undosys_step_free(UndoStep *us_p)
+{
+	MBallUndoStep *us = (MBallUndoStep *)us_p;
+	undomball_free_data(&us->data);
+}
+
+static void mball_undosys_foreach_ID_ref(
+        UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
+{
+	MBallUndoStep *us = (MBallUndoStep *)us_p;
+	foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->obedit_ref));
+}
+
+/* Export for ED_undo_sys. */
+void ED_mball_undosys_type(UndoType *ut)
+{
+	ut->name = "Edit MBall";
+	ut->poll = mball_undosys_poll;
+	ut->step_encode = mball_undosys_step_encode;
+	ut->step_decode = mball_undosys_step_decode;
+	ut->step_free = mball_undosys_step_free;
+
+	ut->step_foreach_ID_ref = mball_undosys_foreach_ID_ref;
+
+	ut->mode = BKE_UNDOTYPE_MODE_STORE;
+	ut->use_context = true;
+
+	ut->step_size = sizeof(MBallUndoStep);
+
+}
+
+/** \} */
diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c
index 3590b38310f27ae067c9bf78a63fb9aa014d9598..bfeb48f9308c41281eb5f8fa6eea7e570028ba5d 100644
--- a/source/blender/editors/object/object_add.c
+++ b/source/blender/editors/object/object_add.c
@@ -1659,7 +1659,7 @@ static void curvetomesh(EvaluationContext *eval_ctx, Main *bmain, Scene *scene,
 	BKE_mesh_from_nurbs(ob); /* also does users */
 
 	if (ob->type == OB_MESH) {
-		BKE_object_free_modifiers(ob);
+		BKE_object_free_modifiers(ob, 0);
 
 		/* Game engine defaults for mesh objects */
 		ob->body_type = OB_BODY_TYPE_STATIC;
@@ -1788,7 +1788,7 @@ static int convert_exec(bContext *C, wmOperator *op)
 				/* When 2 objects with linked data are selected, converting both
 				 * would keep modifiers on all but the converted object [#26003] */
 				if (ob->type == OB_MESH) {
-					BKE_object_free_modifiers(ob);  /* after derivedmesh calls! */
+					BKE_object_free_modifiers(ob, 0);  /* after derivedmesh calls! */
 				}
 			}
 		}
@@ -1813,7 +1813,7 @@ static int convert_exec(bContext *C, wmOperator *op)
 			BKE_mesh_to_curve(&eval_ctx, scene, newob);
 
 			if (newob->type == OB_CURVE) {
-				BKE_object_free_modifiers(newob);   /* after derivedmesh calls! */
+				BKE_object_free_modifiers(newob, 0);   /* after derivedmesh calls! */
 				ED_rigidbody_object_remove(bmain, scene, newob);
 			}
 		}
@@ -1846,7 +1846,7 @@ static int convert_exec(bContext *C, wmOperator *op)
 
 			/* re-tessellation is called by DM_to_mesh */
 
-			BKE_object_free_modifiers(newob);   /* after derivedmesh calls! */
+			BKE_object_free_modifiers(newob, 0);   /* after derivedmesh calls! */
 		}
 		else if (ob->type == OB_FONT) {
 			ob->flag |= OB_DONE;
@@ -2081,9 +2081,7 @@ void OBJECT_OT_convert(wmOperatorType *ot)
 /* used below, assumes id.new is correct */
 /* leaves selection of base/object unaltered */
 /* Does set ID->newid pointers. */
-static Base *object_add_duplicate_internal(
-        Main *bmain, Scene *scene,
-        ViewLayer *view_layer, Object *ob, int dupflag)
+static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, ViewLayer *view_layer, Object *ob, int dupflag)
 {
 #define ID_NEW_REMAP_US(a)	if (      (a)->id.newid) { (a) = (void *)(a)->id.newid;       (a)->id.us++; }
 #define ID_NEW_REMAP_US2(a)	if (((ID *)a)->newid)    { (a) = ((ID  *)a)->newid;     ((ID *)a)->us++;    }
@@ -2094,14 +2092,10 @@ static Base *object_add_duplicate_internal(
 	ID *id;
 	int a, didit;
 
-	/* ignore pose mode now, Caller can inspect mode. */
-#if 0
-	if (eval_ctx->object_mode & OB_MODE_POSE) {
+	if (ob->mode & OB_MODE_POSE) {
 		; /* nothing? */
 	}
-	else
-#endif
-	{
+	else {
 		obn = ID_NEW_SET(ob, BKE_object_copy(bmain, ob));
 		DEG_id_tag_update(&obn->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME);
 
@@ -2529,10 +2523,9 @@ static int join_poll(bContext *C)
 
 static int join_exec(bContext *C, wmOperator *op)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Object *ob = CTX_data_active_object(C);
 
-	if (workspace->object_mode & OB_MODE_EDIT) {
+	if (ob->mode & OB_MODE_EDIT) {
 		BKE_report(op->reports, RPT_ERROR, "This data does not support joining in edit mode");
 		return OPERATOR_CANCELLED;
 	}
@@ -2583,10 +2576,9 @@ static int join_shapes_poll(bContext *C)
 
 static int join_shapes_exec(bContext *C, wmOperator *op)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Object *ob = CTX_data_active_object(C);
 
-	if (workspace->object_mode & OB_MODE_EDIT) {
+	if (ob->mode & OB_MODE_EDIT) {
 		BKE_report(op->reports, RPT_ERROR, "This data does not support joining in edit mode");
 		return OPERATOR_CANCELLED;
 	}
diff --git a/source/blender/editors/object/object_bake.c b/source/blender/editors/object/object_bake.c
index e23329a7a68489c60f8750a8898d6afcb2ba49c6..a38b9959daba768c426d9c314d34106bcb57bfb3 100644
--- a/source/blender/editors/object/object_bake.c
+++ b/source/blender/editors/object/object_bake.c
@@ -206,8 +206,7 @@ static bool multiresbake_check(bContext *C, wmOperator *op)
 	return ok;
 }
 
-static DerivedMesh *multiresbake_create_loresdm(
-        Scene *scene, Object *ob, eObjectMode object_mode, int *lvl)
+static DerivedMesh *multiresbake_create_loresdm(Scene *scene, Object *ob, int *lvl)
 {
 	DerivedMesh *dm;
 	MultiresModifierData *mmd = get_multires_modifier(scene, ob, 0);
@@ -227,15 +226,13 @@ static DerivedMesh *multiresbake_create_loresdm(
 
 	tmp_mmd.lvl = *lvl;
 	tmp_mmd.sculptlvl = *lvl;
-	dm = multires_make_derived_from_derived(cddm, &tmp_mmd, ob, 0, object_mode);
+	dm = multires_make_derived_from_derived(cddm, &tmp_mmd, ob, 0);
 	cddm->release(cddm);
 
 	return dm;
 }
 
-static DerivedMesh *multiresbake_create_hiresdm(
-        Scene *scene, Object *ob, eObjectMode object_mode,
-        int *lvl, bool *simple)
+static DerivedMesh *multiresbake_create_hiresdm(Scene *scene, Object *ob, int *lvl, bool *simple)
 {
 	Mesh *me = (Mesh *)ob->data;
 	MultiresModifierData *mmd = get_multires_modifier(scene, ob, 0);
@@ -256,7 +253,7 @@ static DerivedMesh *multiresbake_create_hiresdm(
 
 	tmp_mmd.lvl = mmd->totlvl;
 	tmp_mmd.sculptlvl = mmd->totlvl;
-	dm = multires_make_derived_from_derived(cddm, &tmp_mmd, ob, 0, object_mode);
+	dm = multires_make_derived_from_derived(cddm, &tmp_mmd, ob, 0);
 	cddm->release(cddm);
 
 	return dm;
@@ -320,7 +317,6 @@ static void clear_images_poly(Image **ob_image_array, int ob_image_array_len, Cl
 static int multiresbake_image_exec_locked(bContext *C, wmOperator *op)
 {
 	Object *ob;
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Scene *scene = CTX_data_scene(C);
 	int objects_baked = 0;
 
@@ -375,8 +371,8 @@ static int multiresbake_image_exec_locked(bContext *C, wmOperator *op)
 		bkr.ob_image.array = BKE_object_material_edit_image_get_array(ob);
 		bkr.ob_image.len = ob->totcol;
 
-		bkr.hires_dm = multiresbake_create_hiresdm(scene, ob, workspace->object_mode, &bkr.tot_lvl, &bkr.simple);
-		bkr.lores_dm = multiresbake_create_loresdm(scene, ob, workspace->object_mode, &bkr.lvl);
+		bkr.hires_dm = multiresbake_create_hiresdm(scene, ob, &bkr.tot_lvl, &bkr.simple);
+		bkr.lores_dm = multiresbake_create_loresdm(scene, ob, &bkr.lvl);
 
 		RE_multires_bake_images(&bkr);
 
@@ -400,7 +396,6 @@ static int multiresbake_image_exec_locked(bContext *C, wmOperator *op)
 /* Multiresbake adopted for job-system executing */
 static void init_multiresbake_job(bContext *C, MultiresBakeJob *bkj)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Scene *scene = CTX_data_scene(C);
 	Object *ob;
 
@@ -432,8 +427,8 @@ static void init_multiresbake_job(bContext *C, MultiresBakeJob *bkj)
 		data->ob_image.len = ob->totcol;
 
 		/* create low-resolution DM (to bake to) and hi-resolution DM (to bake from) */
-		data->hires_dm = multiresbake_create_hiresdm(scene, ob, workspace->object_mode, &data->tot_lvl, &data->simple);
-		data->lores_dm = multiresbake_create_loresdm(scene, ob, workspace->object_mode, &lvl);
+		data->hires_dm = multiresbake_create_hiresdm(scene, ob, &data->tot_lvl, &data->simple);
+		data->lores_dm = multiresbake_create_loresdm(scene, ob, &lvl);
 		data->lvl = lvl;
 
 		BLI_addtail(&bkj->data, data);
diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c
index acf3f73c9c6866f8ee1195514f007f251ac9a65c..0fde6f643a8b14068a831178191e401e9d2d0fbe 100644
--- a/source/blender/editors/object/object_bake_api.c
+++ b/source/blender/editors/object/object_bake_api.c
@@ -866,7 +866,7 @@ static int bake(
 
 			/* triangulating so BVH returns the primitive_id that will be used for rendering */
 			highpoly[i].tri_mod = ED_object_modifier_add(
-			        reports, bmain, scene, highpoly[i].ob, OB_MODE_OBJECT,
+			        reports, bmain, scene, highpoly[i].ob,
 			        "TmpTriangulate", eModifierType_Triangulate);
 			tmd = (TriangulateModifierData *)highpoly[i].tri_mod;
 			tmd->quad_method = MOD_TRIANGULATE_QUAD_FIXED;
diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c
index 1b462e92ca46ff2f90fbdb6bb34e49ab306bb1ab..1a20a8db5b31ab2f51eb153d5f4d39255eaf17ba 100644
--- a/source/blender/editors/object/object_constraint.c
+++ b/source/blender/editors/object/object_constraint.c
@@ -87,12 +87,12 @@
 /* -------------- Get Active Constraint Data ---------------------- */
 
 /* if object in posemode, active bone constraints, else object constraints */
-ListBase *get_active_constraints(const EvaluationContext *eval_ctx, Object *ob)
+ListBase *get_active_constraints(Object *ob)
 {
 	if (ob == NULL)
 		return NULL;
 	
-	if (eval_ctx->object_mode & OB_MODE_POSE) {
+	if (ob->mode & OB_MODE_POSE) {
 		bPoseChannel *pchan;
 		
 		pchan = BKE_pose_channel_active(ob);
@@ -142,9 +142,9 @@ ListBase *get_constraint_lb(Object *ob, bConstraint *con, bPoseChannel **r_pchan
 }
 
 /* single constraint */
-bConstraint *get_active_constraint(const EvaluationContext *eval_ctx, Object *ob)
+bConstraint *get_active_constraint(Object *ob)
 {
-	return BKE_constraints_active_get(get_active_constraints(eval_ctx, ob));
+	return BKE_constraints_active_get(get_active_constraints(ob));
 }
 
 /* -------------- Constraint Management (Add New, Remove, Rename) -------------------- */
@@ -639,8 +639,7 @@ static int edit_constraint_invoke_properties(bContext *C, wmOperator *op)
 	return 0;
 }
 
-static bConstraint *edit_constraint_property_get(
-        const EvaluationContext *eval_ctx, wmOperator *op, Object *ob, int type)
+static bConstraint *edit_constraint_property_get(wmOperator *op, Object *ob, int type)
 {
 	char constraint_name[MAX_NAME];
 	int owner = RNA_enum_get(op->ptr, "owner");
@@ -665,7 +664,7 @@ static bConstraint *edit_constraint_property_get(
 	else {
 		//if (G.debug & G_DEBUG)
 		//printf("edit_constraint_property_get: defaulting to getting list in the standard way\n");
-		list = get_active_constraints(eval_ctx, ob);
+		list = get_active_constraints(ob);
 	}
 	
 	con = BKE_constraints_find_name(list, constraint_name);
@@ -688,7 +687,7 @@ static int stretchto_reset_exec(bContext *C, wmOperator *op)
 	EvaluationContext eval_ctx;
 	CTX_data_eval_ctx(C, &eval_ctx);
 	Object *ob = ED_object_active_context(C);
-	bConstraint *con = edit_constraint_property_get(&eval_ctx, op, ob, CONSTRAINT_TYPE_STRETCHTO);
+	bConstraint *con = edit_constraint_property_get(op, ob, CONSTRAINT_TYPE_STRETCHTO);
 	bStretchToConstraint *data = (con) ? (bStretchToConstraint *)con->data : NULL;
 	
 	/* despite 3 layers of checks, we may still not be able to find a constraint */
@@ -736,7 +735,7 @@ static int limitdistance_reset_exec(bContext *C, wmOperator *op)
 	EvaluationContext eval_ctx;
 	CTX_data_eval_ctx(C, &eval_ctx);
 	Object *ob = ED_object_active_context(C);
-	bConstraint *con = edit_constraint_property_get(&eval_ctx, op, ob, CONSTRAINT_TYPE_DISTLIMIT);
+	bConstraint *con = edit_constraint_property_get(op, ob, CONSTRAINT_TYPE_DISTLIMIT);
 	bDistLimitConstraint *data = (con) ? (bDistLimitConstraint *)con->data : NULL;
 	
 	/* despite 3 layers of checks, we may still not be able to find a constraint */
@@ -875,7 +874,7 @@ static int childof_set_inverse_exec(bContext *C, wmOperator *op)
 	CTX_data_eval_ctx(C, &eval_ctx);
 	Scene *scene = CTX_data_scene(C);
 	Object *ob = ED_object_active_context(C);
-	bConstraint *con = edit_constraint_property_get(&eval_ctx, op, ob, CONSTRAINT_TYPE_CHILDOF);
+	bConstraint *con = edit_constraint_property_get(op, ob, CONSTRAINT_TYPE_CHILDOF);
 	bChildOfConstraint *data = (con) ? (bChildOfConstraint *)con->data : NULL;
 	const int owner = RNA_enum_get(op->ptr, "owner");
 
@@ -926,7 +925,7 @@ static int childof_clear_inverse_exec(bContext *C, wmOperator *op)
 	Object *ob = ED_object_active_context(C);
 	EvaluationContext eval_ctx;
 	CTX_data_eval_ctx(C, &eval_ctx);
-	bConstraint *con = edit_constraint_property_get(&eval_ctx, op, ob, CONSTRAINT_TYPE_CHILDOF);
+	bConstraint *con = edit_constraint_property_get(op, ob, CONSTRAINT_TYPE_CHILDOF);
 	bChildOfConstraint *data = (con) ? (bChildOfConstraint *)con->data : NULL;
 	
 	if (data == NULL) {
@@ -976,7 +975,7 @@ static int followpath_path_animate_exec(bContext *C, wmOperator *op)
 	EvaluationContext eval_ctx;
 	CTX_data_eval_ctx(C, &eval_ctx);
 	Object *ob = ED_object_active_context(C);
-	bConstraint *con = edit_constraint_property_get(&eval_ctx, op, ob, CONSTRAINT_TYPE_FOLLOWPATH);
+	bConstraint *con = edit_constraint_property_get(op, ob, CONSTRAINT_TYPE_FOLLOWPATH);
 	bFollowPathConstraint *data = (con) ? (bFollowPathConstraint *)con->data : NULL;
 	
 	bAction *act = NULL;
@@ -1103,7 +1102,7 @@ static int objectsolver_set_inverse_exec(bContext *C, wmOperator *op)
 	CTX_data_eval_ctx(C, &eval_ctx);
 	Scene *scene = CTX_data_scene(C);
 	Object *ob = ED_object_active_context(C);
-	bConstraint *con = edit_constraint_property_get(&eval_ctx, op, ob, CONSTRAINT_TYPE_OBJECTSOLVER);
+	bConstraint *con = edit_constraint_property_get(op, ob, CONSTRAINT_TYPE_OBJECTSOLVER);
 	bObjectSolverConstraint *data = (con) ? (bObjectSolverConstraint *)con->data : NULL;
 	const int owner = RNA_enum_get(op->ptr, "owner");
 
@@ -1153,7 +1152,7 @@ static int objectsolver_clear_inverse_exec(bContext *C, wmOperator *op)
 	EvaluationContext eval_ctx;
 	CTX_data_eval_ctx(C, &eval_ctx);
 	Object *ob = ED_object_active_context(C);
-	bConstraint *con = edit_constraint_property_get(&eval_ctx, op, ob, CONSTRAINT_TYPE_OBJECTSOLVER);
+	bConstraint *con = edit_constraint_property_get(op, ob, CONSTRAINT_TYPE_OBJECTSOLVER);
 	bObjectSolverConstraint *data = (con) ? (bObjectSolverConstraint *)con->data : NULL;
 
 	if (data == NULL) {
@@ -1324,7 +1323,7 @@ static int constraint_move_down_exec(bContext *C, wmOperator *op)
 	EvaluationContext eval_ctx;
 	CTX_data_eval_ctx(C, &eval_ctx);
 	Object *ob = ED_object_active_context(C);
-	bConstraint *con = edit_constraint_property_get(&eval_ctx, op, ob, 0);
+	bConstraint *con = edit_constraint_property_get(op, ob, 0);
 	
 	if (con && con->next) {
 		ListBase *conlist = get_constraint_lb(ob, con, NULL);
@@ -1375,7 +1374,7 @@ static int constraint_move_up_exec(bContext *C, wmOperator *op)
 	EvaluationContext eval_ctx;
 	CTX_data_eval_ctx(C, &eval_ctx);
 	Object *ob = ED_object_active_context(C);
-	bConstraint *con = edit_constraint_property_get(&eval_ctx, op, ob, 0);
+	bConstraint *con = edit_constraint_property_get(op, ob, 0);
 	
 	if (con && con->prev) {
 		ListBase *conlist = get_constraint_lb(ob, con, NULL);
@@ -1892,8 +1891,6 @@ static int object_constraint_add_exec(bContext *C, wmOperator *op)
 /* dummy operator callback */
 static int pose_constraint_add_exec(bContext *C, wmOperator *op)
 {
-	EvaluationContext eval_ctx;
-	CTX_data_eval_ctx(C, &eval_ctx);
 	Object *ob = BKE_object_pose_armature_get(ED_object_active_context(C));
 	int type = RNA_enum_get(op->ptr, "type");
 	short with_targets = 0;
@@ -1909,7 +1906,7 @@ static int pose_constraint_add_exec(bContext *C, wmOperator *op)
 	if (strstr(op->idname, "with_targets"))
 		with_targets = 1;
 	
-	return constraint_add_exec(C, op, ob, get_active_constraints(&eval_ctx, ob), type, with_targets);
+	return constraint_add_exec(C, op, ob, get_active_constraints(ob), type, with_targets);
 }
 
 /* ------------------ */
@@ -2050,13 +2047,11 @@ static int pose_ik_add_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED
 /* call constraint_add_exec() to add the IK constraint */
 static int pose_ik_add_exec(bContext *C, wmOperator *op)
 {
-	EvaluationContext eval_ctx;
-	CTX_data_eval_ctx(C, &eval_ctx);
 	Object *ob = CTX_data_active_object(C);
 	const bool with_targets = RNA_boolean_get(op->ptr, "with_targets");
 	
 	/* add the constraint - all necessary checks should have been done by the invoke() callback already... */
-	return constraint_add_exec(C, op, ob, get_active_constraints(&eval_ctx, ob), CONSTRAINT_TYPE_KINEMATIC, with_targets);
+	return constraint_add_exec(C, op, ob, get_active_constraints(ob), CONSTRAINT_TYPE_KINEMATIC, with_targets);
 }
 
 void POSE_OT_ik_add(wmOperatorType *ot)
diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c
index 3e629c8d7f6dd0fa0402f561232ee9a842de13fe..9306213581e36add426fed84c3bfa9f10d7f2a52 100644
--- a/source/blender/editors/object/object_edit.c
+++ b/source/blender/editors/object/object_edit.c
@@ -64,6 +64,7 @@
 #include "IMB_imbuf_types.h"
 
 #include "BKE_anim.h"
+#include "BKE_collection.h"
 #include "BKE_constraint.h"
 #include "BKE_context.h"
 #include "BKE_curve.h"
@@ -99,7 +100,7 @@
 #include "ED_lattice.h"
 #include "ED_object.h"
 #include "ED_screen.h"
-#include "ED_util.h"
+#include "ED_undo.h"
 #include "ED_image.h"
 
 #include "RNA_access.h"
@@ -109,11 +110,16 @@
 /* for menu/popup icons etc etc*/
 
 #include "UI_interface.h"
+#include "UI_resources.h"
 #include "WM_api.h"
 #include "WM_types.h"
 
 #include "object_intern.h"  // own include
 
+/* prototypes */
+typedef struct MoveToCollectionData MoveToCollectionData;
+static void move_to_collection_menus_items(struct uiLayout *layout, struct MoveToCollectionData *menu);
+
 /* ************* XXX **************** */
 static void error(const char *UNUSED(arg)) {}
 static void waitcursor(int UNUSED(val)) {}
@@ -268,9 +274,12 @@ bool ED_object_editmode_load(Object *obedit)
  * - Only in exceptional cases should #EM_DO_UNDO NOT be in the flag.
  * - If #EM_FREEDATA isn't in the flag, use ED_object_editmode_load directly.
  */
-void ED_object_editmode_exit_ex(bContext *C, WorkSpace *workspace, Scene *scene, Object *obedit, int flag)
+void ED_object_editmode_exit_ex(bContext *C, Scene *scene, Object *obedit, int flag)
 {
 	BLI_assert(C || !(flag & EM_DO_UNDO));
+	/* Note! only in exceptional cases should 'EM_DO_UNDO' NOT be in the flag */
+	/* Note! if 'EM_FREEDATA' isn't in the flag, use ED_object_editmode_load directly */
+	ViewLayer *view_layer = CTX_data_view_layer(C);
 	const bool freedata = (flag & EM_FREEDATA) != 0;
 
 	if (flag & EM_WAITCURSOR) waitcursor(1);
@@ -278,7 +287,9 @@ void ED_object_editmode_exit_ex(bContext *C, WorkSpace *workspace, Scene *scene,
 	if (ED_object_editmode_load_ex(G.main, obedit, freedata) == false) {
 		/* in rare cases (background mode) its possible active object
 		 * is flagged for editmode, without 'obedit' being set [#35489] */
-		workspace->object_mode &= ~OB_MODE_EDIT;
+		if (UNLIKELY(view_layer->basact && (view_layer->basact->object->mode & OB_MODE_EDIT))) {
+			view_layer->basact->object->mode &= ~OB_MODE_EDIT;
+		}
 		if (flag & EM_WAITCURSOR) waitcursor(0);
 		return;
 	}
@@ -300,21 +311,14 @@ void ED_object_editmode_exit_ex(bContext *C, WorkSpace *workspace, Scene *scene,
 
 		/* also flush ob recalc, doesn't take much overhead, but used for particles */
 		DEG_id_tag_update(&obedit->id, OB_RECALC_OB | OB_RECALC_DATA);
-
-		workspace->object_mode &= ~OB_MODE_EDIT;
 	
 		if (flag & EM_DO_UNDO)
 			ED_undo_push(C, "Editmode");
 
-		if (C != NULL) {
-			WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_OBJECT, scene);
-		}
-		else {
-			WM_main_add_notifier(NC_SCENE | ND_MODE | NS_MODE_OBJECT, scene);
-		}
-	}
+		WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_OBJECT, scene);
 
-	ED_workspace_object_mode_sync_from_object(G.main->wm.first, workspace, obedit);
+		obedit->mode &= ~OB_MODE_EDIT;
+	}
 
 	if (flag & EM_WAITCURSOR) waitcursor(0);
 
@@ -324,15 +328,13 @@ void ED_object_editmode_exit_ex(bContext *C, WorkSpace *workspace, Scene *scene,
 
 void ED_object_editmode_exit(bContext *C, int flag)
 {
-	WorkSpace *workspace = CTX_wm_workspace(C);
 	Scene *scene = CTX_data_scene(C);
 	Object *obedit = CTX_data_edit_object(C);
-	ED_object_editmode_exit_ex(C, workspace, scene, obedit, flag);
+	ED_object_editmode_exit_ex(C, scene, obedit, flag);
 }
 
 void ED_object_editmode_enter(bContext *C, int flag)
 {
-	WorkSpace *workspace = CTX_wm_workspace(C);
 	Scene *scene = CTX_data_scene(C);
 	ViewLayer *view_layer = CTX_data_view_layer(C);
 	Object *ob;
@@ -362,14 +364,14 @@ void ED_object_editmode_enter(bContext *C, int flag)
 
 	if (flag & EM_WAITCURSOR) waitcursor(1);
 
-	workspace->object_mode_restore = workspace->object_mode;
+	ob->restore_mode = ob->mode;
 
 	/* note, when switching scenes the object can have editmode data but
 	 * not be scene->obedit: bug 22954, this avoids calling self eternally */
-	if ((workspace->object_mode_restore & OB_MODE_EDIT) == 0)
-		ED_object_mode_toggle(C, workspace->object_mode);
+	if ((ob->restore_mode & OB_MODE_EDIT) == 0)
+		ED_object_mode_toggle(C, ob->mode);
 
-	workspace->object_mode = OB_MODE_EDIT;
+	ob->mode = OB_MODE_EDIT;
 
 	if (ob->type == OB_MESH) {
 		BMEditMesh *em;
@@ -440,25 +442,23 @@ void ED_object_editmode_enter(bContext *C, int flag)
 		DEG_id_tag_update(&scene->id, 0);
 	}
 	else {
-		workspace->object_mode &= ~OB_MODE_EDIT;
+		ob->mode &= ~OB_MODE_EDIT;
 		WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_OBJECT, scene);
 	}
 
-	ED_workspace_object_mode_sync_from_object(G.main->wm.first, workspace, ob);
-
 	if (flag & EM_DO_UNDO) ED_undo_push(C, "Enter Editmode");
 	if (flag & EM_WAITCURSOR) waitcursor(0);
 }
 
 static int editmode_toggle_exec(bContext *C, wmOperator *op)
 {
-	WorkSpace *workspace = CTX_wm_workspace(C);
 	const int mode_flag = OB_MODE_EDIT;
 	const bool is_mode_set = (CTX_data_edit_object(C) != NULL);
 	Scene *scene =  CTX_data_scene(C);
 
 	if (!is_mode_set) {
-		if (!ED_object_mode_compat_set(C, workspace, mode_flag, op->reports)) {
+		Object *ob = CTX_data_active_object(C);
+		if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) {
 			return OPERATOR_CANCELLED;
 		}
 	}
@@ -481,10 +481,8 @@ static int editmode_toggle_poll(bContext *C)
 	if (ELEM(NULL, ob, ob->data) || ID_IS_LINKED(ob->data))
 		return 0;
 
-	const WorkSpace *workspace = CTX_wm_workspace(C);
-
 	/* if hidden but in edit mode, we still display */
-	if ((ob->restrictflag & OB_RESTRICT_VIEW) && !(workspace->object_mode & OB_MODE_EDIT)) {
+	if ((ob->restrictflag & OB_RESTRICT_VIEW) && !(ob->mode & OB_MODE_EDIT)) {
 		return 0;
 	}
 
@@ -511,15 +509,13 @@ void OBJECT_OT_editmode_toggle(wmOperatorType *ot)
 
 static int posemode_exec(bContext *C, wmOperator *op)
 {
-	wmWindowManager *wm = CTX_wm_manager(C);
-	WorkSpace *workspace = CTX_wm_workspace(C);
 	Base *base = CTX_data_active_base(C);
 	Object *ob = base->object;
 	const int mode_flag = OB_MODE_POSE;
-	const bool is_mode_set = (workspace->object_mode & mode_flag) != 0;
-
+	const bool is_mode_set = (ob->mode & mode_flag) != 0;
+	
 	if (!is_mode_set) {
-		if (!ED_object_mode_compat_set(C, workspace, mode_flag, op->reports)) {
+		if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) {
 			return OPERATOR_CANCELLED;
 		}
 	}
@@ -534,8 +530,6 @@ static int posemode_exec(bContext *C, wmOperator *op)
 		else
 			ED_armature_enter_posemode(C, base);
 
-		ED_workspace_object_mode_sync_from_object(wm, workspace, ob);
-
 		return OPERATOR_FINISHED;
 	}
 	
@@ -1014,7 +1008,7 @@ static void UNUSED_FUNCTION(copy_attr_menu) (Main *bmain, Scene *scene, ViewLaye
 
 /* ******************* force field toggle operator ***************** */
 
-void ED_object_check_force_modifiers(Main *bmain, Scene *scene, Object *object, eObjectMode object_mode)
+void ED_object_check_force_modifiers(Main *bmain, Scene *scene, Object *object)
 {
 	PartDeflect *pd = object->pd;
 	ModifierData *md = modifiers_findByType(object, eModifierType_Surface);
@@ -1023,7 +1017,7 @@ void ED_object_check_force_modifiers(Main *bmain, Scene *scene, Object *object,
 	if (!md) {
 		if (pd && (pd->shape == PFIELD_SHAPE_SURFACE) && !ELEM(pd->forcefield, 0, PFIELD_GUIDE, PFIELD_TEXTURE)) {
 			if (ELEM(object->type, OB_MESH, OB_SURF, OB_FONT, OB_CURVE)) {
-				ED_object_modifier_add(NULL, bmain, scene, object, object_mode, NULL, eModifierType_Surface);
+				ED_object_modifier_add(NULL, bmain, scene, object, NULL, eModifierType_Surface);
 			}
 		}
 	}
@@ -1045,8 +1039,7 @@ static int forcefield_toggle_exec(bContext *C, wmOperator *UNUSED(op))
 	else
 		ob->pd->forcefield = 0;
 
-	const WorkSpace *workspace = CTX_wm_workspace(C);
-	ED_object_check_force_modifiers(CTX_data_main(C), CTX_data_scene(C), ob, workspace->object_mode);
+	ED_object_check_force_modifiers(CTX_data_main(C), CTX_data_scene(C), ob);
 	WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
 	WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
 
@@ -1507,11 +1500,10 @@ static int object_mode_set_poll(bContext *C)
 
 static int object_mode_set_exec(bContext *C, wmOperator *op)
 {
-	WorkSpace *workspace = CTX_wm_workspace(C);
 	Object *ob = CTX_data_active_object(C);
 	bGPdata *gpd = CTX_data_gpencil_data(C);
 	eObjectMode mode = RNA_enum_get(op->ptr, "mode");
-	eObjectMode restore_mode = workspace->object_mode;
+	eObjectMode restore_mode = (ob) ? ob->mode : OB_MODE_OBJECT;
 	const bool toggle = RNA_boolean_get(op->ptr, "toggle");
 	
 	if (gpd) {
@@ -1535,31 +1527,28 @@ static int object_mode_set_exec(bContext *C, wmOperator *op)
 	if (!ob || !ED_object_mode_compat_test(ob, mode))
 		return OPERATOR_PASS_THROUGH;
 
-	if (workspace->object_mode != mode) {
+	if (ob->mode != mode) {
 		/* we should be able to remove this call, each operator calls  */
-		ED_object_mode_compat_set(C, workspace, mode, op->reports);
+		ED_object_mode_compat_set(C, ob, mode, op->reports);
 	}
 
 	/* Exit current mode if it's not the mode we're setting */
-	if (mode != OB_MODE_OBJECT && (workspace->object_mode != mode || toggle)) {
+	if (mode != OB_MODE_OBJECT && (ob->mode != mode || toggle)) {
 		/* Enter new mode */
 		ED_object_mode_toggle(C, mode);
 	}
 
 	if (toggle) {
 		/* Special case for Object mode! */
-		if ((mode == OB_MODE_OBJECT) &&
-		    (restore_mode == OB_MODE_OBJECT) &&
-		    (workspace->object_mode_restore != OB_MODE_OBJECT))
-		{
-			ED_object_mode_toggle(C, workspace->object_mode_restore);
+		if (mode == OB_MODE_OBJECT && restore_mode == OB_MODE_OBJECT && ob->restore_mode != OB_MODE_OBJECT) {
+			ED_object_mode_toggle(C, ob->restore_mode);
 		}
-		else if (workspace->object_mode == mode) {
+		else if (ob->mode == mode) {
 			/* For toggling, store old mode so we know what to go back to */
-			workspace->object_mode_restore = restore_mode;
+			ob->restore_mode = restore_mode;
 		}
-		else if (!ELEM(workspace->object_mode_restore, mode, OB_MODE_OBJECT)) {
-			ED_object_mode_toggle(C, workspace->object_mode_restore);
+		else if (ob->restore_mode != OB_MODE_OBJECT && ob->restore_mode != mode) {
+			ED_object_mode_toggle(C, ob->restore_mode);
 		}
 	}
 
@@ -2039,3 +2028,278 @@ bool ED_object_editmode_calc_active_center(Object *obedit, const bool select_onl
 
 	return false;
 }
+
+#define COLLECTION_INVALID_INDEX -1
+
+static int move_to_collection_exec(bContext *C, wmOperator *op)
+{
+	Scene *scene = CTX_data_scene(C);
+	PropertyRNA *prop = RNA_struct_find_property(op->ptr, "collection_index");
+	const bool is_add = RNA_boolean_get(op->ptr, "is_add");
+	const bool is_new = RNA_boolean_get(op->ptr, "is_new");
+	SceneCollection *scene_collection;
+
+	if (!RNA_property_is_set(op->ptr, prop)) {
+		BKE_report(op->reports, RPT_ERROR, "No collection selected");
+		return OPERATOR_CANCELLED;
+	}
+
+	int collection_index = RNA_property_int_get(op->ptr, prop);
+	scene_collection = BKE_collection_from_index(CTX_data_scene(C), collection_index);
+	if (scene_collection == NULL) {
+		BKE_report(op->reports, RPT_ERROR, "Unexpected error, collection not found");
+		return OPERATOR_CANCELLED;
+	}
+
+	Object *single_object = NULL;
+	CTX_DATA_BEGIN (C, Object *, ob, selected_objects)
+	{
+		if (single_object != NULL) {
+			single_object = NULL;
+			break;
+		}
+		else {
+			single_object = ob;
+		}
+	}
+	CTX_DATA_END;
+
+	if (is_new) {
+		char new_collection_name[MAX_NAME];
+		RNA_string_get(op->ptr, "new_collection_name", new_collection_name);
+		scene_collection = BKE_collection_add(&scene->id, scene_collection, COLLECTION_TYPE_NONE, new_collection_name);
+	}
+
+	if ((single_object != NULL) &&
+	    is_add &&
+	    BLI_findptr(&scene_collection->objects, single_object, offsetof(LinkData, data)))
+	{
+		BKE_reportf(op->reports, RPT_ERROR, "%s already in %s", single_object->id.name + 2, scene_collection->name);
+		return OPERATOR_CANCELLED;
+	}
+
+	CTX_DATA_BEGIN (C, Object *, ob, selected_objects)
+	{
+		if (!is_add) {
+			BKE_collection_object_move(&scene->id, scene_collection, NULL, ob);
+		}
+		else {
+			BKE_collection_object_add(&scene->id, scene_collection, ob);
+		}
+	}
+	CTX_DATA_END;
+
+	BKE_reportf(op->reports,
+	            RPT_INFO,
+	            "%s %s to %s",
+	            (single_object != NULL) ? single_object->id.name + 2 : "Objects",
+	            is_add ? "added" : "moved",
+	            scene_collection->name);
+
+	DEG_relations_tag_update(CTX_data_main(C));
+	DEG_id_tag_update(&scene->id, 0);
+
+	WM_event_add_notifier(C, NC_SCENE | ND_LAYER, scene);
+	WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
+	WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene);
+
+	return OPERATOR_FINISHED;
+}
+
+typedef struct MoveToCollectionData {
+	struct MoveToCollectionData *next, *prev;
+	int index;
+	struct SceneCollection *collection;
+	struct ListBase submenus;
+	PointerRNA ptr;
+	struct wmOperatorType *ot;
+} MoveToCollectionData;
+
+static int move_to_collection_menus_create(wmOperator *op, MoveToCollectionData *menu)
+{
+	int index = menu->index;
+	for (SceneCollection *scene_collection = menu->collection->scene_collections.first;
+	     scene_collection != NULL;
+	     scene_collection = scene_collection->next)
+	{
+		MoveToCollectionData *submenu = MEM_callocN(sizeof(MoveToCollectionData),
+		                                            "MoveToCollectionData submenu - expected memleak");
+		BLI_addtail(&menu->submenus, submenu);
+		submenu->collection = scene_collection;
+		submenu->index = ++index;
+		index = move_to_collection_menus_create(op, submenu);
+		submenu->ot = op->type;
+	}
+	return index;
+}
+
+static void move_to_collection_menus_free_recursive(MoveToCollectionData *menu)
+{
+	for (MoveToCollectionData *submenu = menu->submenus.first;
+	     submenu != NULL;
+	     submenu = submenu->next)
+	{
+		move_to_collection_menus_free_recursive(submenu);
+	}
+	BLI_freelistN(&menu->submenus);
+}
+
+static void move_to_collection_menus_free(MoveToCollectionData **menu)
+{
+	if (*menu == NULL) {
+		return;
+	}
+
+	move_to_collection_menus_free_recursive(*menu);
+	MEM_freeN(*menu);
+	*menu = NULL;
+}
+
+static void move_to_collection_menu_create(bContext *UNUSED(C), uiLayout *layout, void *menu_v)
+{
+	MoveToCollectionData *menu = menu_v;
+
+	uiItemIntO(layout,
+			   menu->collection->name,
+			   ICON_NONE,
+			   "OBJECT_OT_move_to_collection",
+			   "collection_index",
+			   menu->index);
+	uiItemS(layout);
+
+	for (MoveToCollectionData *submenu = menu->submenus.first;
+		 submenu != NULL;
+		 submenu = submenu->next)
+	{
+		move_to_collection_menus_items(layout, submenu);
+	}
+
+	uiItemS(layout);
+
+	WM_operator_properties_create_ptr(&menu->ptr, menu->ot);
+	RNA_int_set(&menu->ptr, "collection_index", menu->index);
+	RNA_boolean_set(&menu->ptr, "is_new", true);
+
+	uiItemFullO_ptr(layout,
+	                menu->ot,
+	                "New Collection",
+	                ICON_ZOOMIN,
+	                menu->ptr.data,
+	                /* We use invoke here so we can read ctrl from event. */
+	                WM_OP_INVOKE_DEFAULT,
+	                0,
+	                NULL);
+}
+
+static void move_to_collection_menus_items(uiLayout *layout, MoveToCollectionData *menu)
+{
+	if (BLI_listbase_is_empty(&menu->submenus)) {
+		uiItemIntO(layout,
+		           menu->collection->name,
+		           ICON_NONE,
+		           "OBJECT_OT_move_to_collection",
+		           "collection_index",
+		           menu->index);
+	}
+	else {
+		uiItemMenuF(layout,
+		            menu->collection->name,
+		            ICON_NONE,
+		            move_to_collection_menu_create,
+		            menu);
+	}
+}
+
+/* This is allocated statically because we need this available for the menus creation callback. */
+static MoveToCollectionData *master_collection_menu = NULL;
+
+static int move_to_collection_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+	/* Reset the menus data for the current master collection, and free previously allocated data. */
+	move_to_collection_menus_free(&master_collection_menu);
+
+	PropertyRNA *prop;
+	prop = RNA_struct_find_property(op->ptr, "collection_index");
+	if (RNA_property_is_set(op->ptr, prop)) {
+		int collection_index = RNA_property_int_get(op->ptr, prop);
+		RNA_boolean_set(op->ptr, "is_add", event->ctrl);
+
+		if (RNA_boolean_get(op->ptr, "is_new")) {
+			prop = RNA_struct_find_property(op->ptr, "new_collection_name");
+			if (!RNA_property_is_set(op->ptr, prop)) {
+				char name[MAX_NAME];
+				SceneCollection *scene_collection;
+
+				scene_collection = BKE_collection_from_index(CTX_data_scene(C), collection_index);
+				BKE_collection_new_name_get(&CTX_data_scene(C)->id, scene_collection, name);
+
+				RNA_property_string_set(op->ptr, prop, name);
+				return WM_operator_props_dialog_popup(C, op, 10 * UI_UNIT_X, 5 * UI_UNIT_Y);
+			}
+		}
+		return move_to_collection_exec(C, op);
+	}
+
+	SceneCollection *master_collection = BKE_collection_master(&CTX_data_scene(C)->id);
+
+	/* We need the data to be allocated so it's available during menu drawing.
+	 * Technically we could use wmOperator->customdata. However there is no free callback
+	 * called to an operator that exit with OPERATOR_INTERFACE to launch a menu.
+	 *
+	 * So we are left with a memory that will necessarily leak. It's a small leak though.*/
+	if (master_collection_menu == NULL) {
+		master_collection_menu = MEM_callocN(sizeof(MoveToCollectionData),
+		                                     "MoveToCollectionData menu - expected eventual memleak");
+	}
+
+	master_collection_menu->collection = master_collection;
+	master_collection_menu->ot = op->type;
+	move_to_collection_menus_create(op, master_collection_menu);
+
+	uiPopupMenu *pup;
+	uiLayout *layout;
+
+	/* Build the menus. */
+	pup = UI_popup_menu_begin(C, IFACE_("Move to Collection"), ICON_NONE);
+	layout = UI_popup_menu_layout(pup);
+
+	/* We use invoke here so we can read ctrl from event. */
+	uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT);
+
+	move_to_collection_menu_create(C, layout, master_collection_menu);
+
+	UI_popup_menu_end(C, pup);
+
+	return OPERATOR_INTERFACE;
+}
+
+void OBJECT_OT_move_to_collection(wmOperatorType *ot)
+{
+	PropertyRNA *prop;
+
+	/* identifiers */
+	ot->name = "Move to Collection";
+	ot->description = "Move to a collection only (Ctrl to add)";
+	ot->idname = "OBJECT_OT_move_to_collection";
+
+	/* api callbacks */
+	ot->exec = move_to_collection_exec;
+	ot->invoke = move_to_collection_invoke;
+	ot->poll = ED_operator_object_active_editable;
+
+	/* flags */
+	ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+	prop = RNA_def_int(ot->srna, "collection_index", COLLECTION_INVALID_INDEX, COLLECTION_INVALID_INDEX, INT_MAX,
+	                   "Collection Index", "Index of the collection to move to", 0, INT_MAX);
+	RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
+	prop = RNA_def_boolean(ot->srna, "is_add", false, "Add", "Keep object in original collections as well");
+	RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
+	prop = RNA_def_boolean(ot->srna, "is_new", false, "New", "Move objects to a new collection");
+	RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
+	prop = RNA_def_string(ot->srna, "new_collection_name", NULL, MAX_NAME, "Name",
+	                      "Name of the newly added collection");
+	RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+}
+
+#undef COLLECTION_INVALID_INDEX
diff --git a/source/blender/editors/object/object_facemap_ops.c b/source/blender/editors/object/object_facemap_ops.c
index c865b45889a0738e211f16049061ad0cc0e8c8f0..a076521b41d3296df3fc306d8fe080c94d74f048 100644
--- a/source/blender/editors/object/object_facemap_ops.c
+++ b/source/blender/editors/object/object_facemap_ops.c
@@ -175,8 +175,7 @@ static int face_map_supported_edit_mode_poll(bContext *C)
 	Object *ob = ED_object_context(C);
 	ID *data = (ob) ? ob->data : NULL;
 	if (ob && !ob->id.lib && ob->type == OB_MESH && data && !data->lib) {
-		const WorkSpace *workspace = CTX_wm_workspace(C);
-		if (workspace->object_mode == OB_MODE_EDIT) {
+		if (ob->mode == OB_MODE_EDIT) {
 			return true;
 		}
 	}
diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h
index a3bb01b0f24185a5ea9891eaca48500674b64575..75117ccdce3e9606015ebfd7e40c7a31470ddc85 100644
--- a/source/blender/editors/object/object_intern.h
+++ b/source/blender/editors/object/object_intern.h
@@ -95,6 +95,8 @@ void OBJECT_OT_game_property_move(struct wmOperatorType *ot);
 void OBJECT_OT_logic_bricks_copy(struct wmOperatorType *ot);
 void OBJECT_OT_game_physics_copy(struct wmOperatorType *ot);
 
+void OBJECT_OT_move_to_collection(struct wmOperatorType *ot);
+
 /* object_select.c */
 void OBJECT_OT_select_all(struct wmOperatorType *ot);
 void OBJECT_OT_select_random(struct wmOperatorType *ot);
@@ -106,6 +108,7 @@ void OBJECT_OT_select_mirror(struct wmOperatorType *ot);
 void OBJECT_OT_select_more(struct wmOperatorType *ot);
 void OBJECT_OT_select_less(struct wmOperatorType *ot);
 void OBJECT_OT_select_same_group(struct wmOperatorType *ot);
+void OBJECT_OT_select_same_collection(struct wmOperatorType *ot);
 
 /* object_add.c */
 void OBJECT_OT_add(struct wmOperatorType *ot);
diff --git a/source/blender/editors/object/object_modes.c b/source/blender/editors/object/object_modes.c
index 33b9ea49ec0852e15d6d50032542ebb8c11251f1..f074a56fb8687b1d8d5ed14b05581a330529d2c3 100644
--- a/source/blender/editors/object/object_modes.c
+++ b/source/blender/editors/object/object_modes.c
@@ -121,14 +121,14 @@ bool ED_object_mode_compat_test(const Object *ob, eObjectMode mode)
  *
  * This is so each mode's exec function can call
  */
-bool ED_object_mode_compat_set(bContext *C, WorkSpace *workspace, eObjectMode mode, ReportList *reports)
+bool ED_object_mode_compat_set(bContext *C, Object *ob, eObjectMode mode, ReportList *reports)
 {
 	bool ok;
-	if (!ELEM(workspace->object_mode, mode, OB_MODE_OBJECT)) {
-		const char *opstring = object_mode_op_string(workspace->object_mode);
+	if (!ELEM(ob->mode, mode, OB_MODE_OBJECT)) {
+		const char *opstring = object_mode_op_string(ob->mode);
 
 		WM_operator_name_call(C, opstring, WM_OP_EXEC_REGION_WIN, NULL);
-		ok = ELEM(workspace->object_mode, mode, OB_MODE_OBJECT);
+		ok = ELEM(ob->mode, mode, OB_MODE_OBJECT);
 		if (!ok) {
 			wmOperatorType *ot = WM_operatortype_find(opstring, false);
 			BKE_reportf(reports, RPT_ERROR, "Unable to execute '%s', error changing modes", ot->name);
@@ -152,6 +152,17 @@ void ED_object_mode_toggle(bContext *C, eObjectMode mode)
 	}
 }
 
+
+/* Wrapper for operator  */
+void ED_object_mode_set(bContext *C, eObjectMode mode)
+{
+	wmWindowManager *wm = CTX_wm_manager(C);
+	wm->op_undo_depth++;
+	/* needed so we don't do undo pushes. */
+	ED_object_mode_generic_enter(C, mode);
+	wm->op_undo_depth--;
+}
+
 /** \} */
 
 /* -------------------------------------------------------------------- */
@@ -165,8 +176,8 @@ void ED_object_mode_toggle(bContext *C, eObjectMode mode)
 bool ED_object_mode_generic_enter(
         struct bContext *C, eObjectMode object_mode)
 {
-	WorkSpace *workspace = CTX_wm_workspace(C);
-	if (workspace->object_mode == object_mode) {
+	Object *ob = CTX_data_active_object(C);
+	if (ob->mode == object_mode) {
 		return true;
 	}
 	wmOperatorType *ot = WM_operatortype_find("OBJECT_OT_mode_set", false);
@@ -175,7 +186,7 @@ bool ED_object_mode_generic_enter(
 	RNA_enum_set(&ptr, "mode", object_mode);
 	WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr);
 	WM_operator_properties_free(&ptr);
-	return (workspace->object_mode == object_mode);
+	return (ob->mode == object_mode);
 }
 
 /**
@@ -184,46 +195,46 @@ bool ED_object_mode_generic_enter(
  */
 static bool ed_object_mode_generic_exit_ex(
         const struct EvaluationContext *eval_ctx,
-        struct WorkSpace *workspace, struct Scene *scene, struct Object *ob,
+        struct Scene *scene, struct Object *ob,
         bool only_test)
 {
-	if (eval_ctx->object_mode & OB_MODE_EDIT) {
+	if (ob->mode & OB_MODE_EDIT) {
 		if (BKE_object_is_in_editmode(ob)) {
 			if (only_test) {
 				return true;
 			}
-			ED_object_editmode_exit_ex(NULL, workspace, scene, ob, EM_FREEDATA);
+			ED_object_editmode_exit_ex(NULL, scene, ob, EM_FREEDATA);
 		}
 	}
-	else if (eval_ctx->object_mode & OB_MODE_VERTEX_PAINT) {
+	else if (ob->mode & OB_MODE_VERTEX_PAINT) {
 		if (ob->sculpt && (ob->sculpt->mode_type == OB_MODE_VERTEX_PAINT)) {
 			if (only_test) {
 				return true;
 			}
-			ED_object_vpaintmode_exit_ex(workspace, ob);
+			ED_object_vpaintmode_exit_ex(ob);
 		}
 	}
-	else if (eval_ctx->object_mode & OB_MODE_WEIGHT_PAINT) {
+	else if (ob->mode & OB_MODE_WEIGHT_PAINT) {
 		if (ob->sculpt && (ob->sculpt->mode_type == OB_MODE_WEIGHT_PAINT)) {
 			if (only_test) {
 				return true;
 			}
-			ED_object_wpaintmode_exit_ex(workspace, ob);
+			ED_object_wpaintmode_exit_ex(ob);
 		}
 	}
-	else if (eval_ctx->object_mode & OB_MODE_SCULPT) {
+	else if (ob->mode & OB_MODE_SCULPT) {
 		if (ob->sculpt && (ob->sculpt->mode_type == OB_MODE_SCULPT)) {
 			if (only_test) {
 				return true;
 			}
-			ED_object_sculptmode_exit_ex(eval_ctx, workspace, scene, ob);
+			ED_object_sculptmode_exit_ex(eval_ctx, scene, ob);
 		}
 	}
 	else {
 		if (only_test) {
 			return false;
 		}
-		BLI_assert((eval_ctx->object_mode & OB_MODE_ALL_MODE_DATA) == 0);
+		BLI_assert((ob->mode & OB_MODE_ALL_MODE_DATA) == 0);
 	}
 
 	return false;
@@ -231,88 +242,16 @@ static bool ed_object_mode_generic_exit_ex(
 
 void ED_object_mode_generic_exit(
         const struct EvaluationContext *eval_ctx,
-        struct WorkSpace *workspace, struct Scene *scene, struct Object *ob)
+        struct Scene *scene, struct Object *ob)
 {
-	ed_object_mode_generic_exit_ex(eval_ctx, workspace, scene, ob, false);
+	ed_object_mode_generic_exit_ex(eval_ctx, scene, ob, false);
 }
 
 bool ED_object_mode_generic_has_data(
         const struct EvaluationContext *eval_ctx,
         struct Object *ob)
 {
-	return ed_object_mode_generic_exit_ex(eval_ctx, NULL, NULL, ob, true);
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Mode Syncing Utils
- *
- * \{ */
-
-/**
- * A version of #ED_object_mode_generic_enter that checks if the object
- * has an active mode mode in another window we need to use another window first.
- */
-bool ED_object_mode_generic_enter_or_other_window(
-        struct bContext *C, const wmWindow *win_compare, eObjectMode object_mode)
-{
-	WorkSpace *workspace = CTX_wm_workspace(C);
-	ViewLayer *view_layer = CTX_data_view_layer(C);
-	Base *basact = view_layer->basact;
-	if (basact == NULL) {
-		workspace->object_mode = OB_MODE_OBJECT;
-		return (workspace->object_mode == object_mode);
-	}
-
-	wmWindowManager *wm = CTX_wm_manager(C);
-	eObjectMode object_mode_set = OB_MODE_OBJECT;
-	bool use_object_mode = ED_workspace_object_mode_in_other_window(wm, win_compare, basact->object, &object_mode_set);
-
-	if (use_object_mode) {
-		workspace->object_mode = object_mode_set;
-		return (workspace->object_mode == object_mode);
-	}
-	else {
-		workspace->object_mode = OB_MODE_OBJECT;
-		return ED_object_mode_generic_enter(C, object_mode);
-	}
-}
-
-void ED_object_mode_generic_exit_or_other_window(
-        const struct EvaluationContext *eval_ctx, wmWindowManager *wm,
-        struct WorkSpace *workspace, struct Scene *scene, struct Object *ob)
-{
-	if (ob == NULL) {
-		return;
-	}
-	bool is_active = ED_workspace_object_mode_in_other_window(wm, NULL, ob, NULL);
-	if (is_active == false) {
-		ED_object_mode_generic_exit(eval_ctx, workspace, scene, ob);
-	}
-}
-
-/**
- * Use to find if we need to create the mode-data.
- *
- * When the mode 'exists' it means we have a windowing showing an object with this mode.
- * So it's data is already created.
- * Used to check if we need to perform mode switching.
- */
-bool ED_object_mode_generic_exists(
-        wmWindowManager *wm, struct Object *ob,
-        eObjectMode object_mode)
-{
-	if (ob == NULL) {
-		return false;
-	}
-	eObjectMode object_mode_test;
-	if (ED_workspace_object_mode_in_other_window(wm, NULL, ob, &object_mode_test)) {
-		if (object_mode == object_mode_test) {
-			return true;
-		}
-	}
-	return false;
+	return ed_object_mode_generic_exit_ex(eval_ctx, NULL, ob, true);
 }
 
 /** \} */
diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c
index 9d1792e9f16f07a529565258e331b1331b098265..04243660440a32a44f36b6c4f0ddc4cc1288aed2 100644
--- a/source/blender/editors/object/object_modifier.c
+++ b/source/blender/editors/object/object_modifier.c
@@ -95,10 +95,7 @@ static void modifier_skin_customdata_delete(struct Object *ob);
 
 /******************************** API ****************************/
 
-ModifierData *ED_object_modifier_add(
-        ReportList *reports,
-        Main *bmain, Scene *scene,
-        Object *ob, eObjectMode object_mode, const char *name, int type)
+ModifierData *ED_object_modifier_add(ReportList *reports, Main *bmain, Scene *scene, Object *ob, const char *name, int type)
 {
 	ModifierData *md = NULL, *new_md = NULL;
 	const ModifierTypeInfo *mti = modifierType_getInfo(type);
@@ -165,7 +162,7 @@ ModifierData *ED_object_modifier_add(
 			/* set totlvl from existing MDISPS layer if object already had it */
 			multiresModifier_set_levels_from_disps((MultiresModifierData *)new_md, ob);
 
-			if (object_mode & OB_MODE_SCULPT) {
+			if (ob->mode & OB_MODE_SCULPT) {
 				/* ensure that grid paint mask layer is created */
 				BKE_sculpt_mask_layers_ensure(ob, (MultiresModifierData *)new_md);
 			}
@@ -205,11 +202,9 @@ static bool object_has_modifier(const Object *ob, const ModifierData *exclude,
  * If the callback ever returns true, iteration will stop and the
  * function value will be true. Otherwise the function returns false.
  */
-bool ED_object_iter_other(
-        const EvaluationContext *eval_ctx,
-        Main *bmain, Object *orig_ob, const bool include_orig,
-        bool (*callback)(const EvaluationContext *eval_ctx, Object *ob, void *callback_data),
-        void *callback_data)
+bool ED_object_iter_other(Main *bmain, Object *orig_ob, const bool include_orig,
+                          bool (*callback)(Object *ob, void *callback_data),
+                          void *callback_data)
 {
 	ID *ob_data_id = orig_ob->data;
 	int users = ob_data_id->us;
@@ -228,7 +223,7 @@ bool ED_object_iter_other(
 			if (((ob != orig_ob) || include_orig) &&
 			    (ob->data == orig_ob->data))
 			{
-				if (callback(eval_ctx, ob, callback_data))
+				if (callback(ob, callback_data))
 					return true;
 
 				totfound++;
@@ -236,15 +231,13 @@ bool ED_object_iter_other(
 		}
 	}
 	else if (include_orig) {
-		return callback(eval_ctx, orig_ob, callback_data);
+		return callback(orig_ob, callback_data);
 	}
 
 	return false;
 }
 
-static bool object_has_modifier_cb(
-        const EvaluationContext *UNUSED(eval_ctx),
-        Object *ob, void *data)
+static bool object_has_modifier_cb(Object *ob, void *data)
 {
 	ModifierType type = *((ModifierType *)data);
 
@@ -254,16 +247,14 @@ static bool object_has_modifier_cb(
 /* Use with ED_object_iter_other(). Sets the total number of levels
  * for any multires modifiers on the object to the int pointed to by
  * callback_data. */
-bool ED_object_multires_update_totlevels_cb(
-        const struct EvaluationContext *eval_ctx,
-        Object *ob, void *totlevel_v)
+bool ED_object_multires_update_totlevels_cb(Object *ob, void *totlevel_v)
 {
 	ModifierData *md;
 	int totlevel = *((char *)totlevel_v);
 
 	for (md = ob->modifiers.first; md; md = md->next) {
 		if (md->type == eModifierType_Multires) {
-			multires_set_tot_level((MultiresModifierData *)md, totlevel, eval_ctx->object_mode);
+			multires_set_tot_level(ob, (MultiresModifierData *)md, totlevel);
 			DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
 		}
 	}
@@ -276,7 +267,7 @@ static bool object_modifier_safe_to_delete(Main *bmain, Object *ob,
                                            ModifierType type)
 {
 	return (!object_has_modifier(ob, exclude, type) &&
-	        !ED_object_iter_other(NULL, bmain, ob, false,
+	        !ED_object_iter_other(bmain, ob, false,
 	                              object_has_modifier_cb, &type));
 }
 
@@ -325,13 +316,11 @@ static bool object_modifier_remove(Main *bmain, Object *ob, ModifierData *md,
 			modifier_skin_customdata_delete(ob);
 	}
 
-#if 0 /* not needed now modes are in workspace */
 	if (ELEM(md->type, eModifierType_Softbody, eModifierType_Cloth) &&
 	    BLI_listbase_is_empty(&ob->particlesystem))
 	{
 		ob->mode &= ~OB_MODE_PARTICLE_EDIT;
 	}
-#endif
 
 	DEG_relations_tag_update(bmain);
 
@@ -424,9 +413,7 @@ int ED_object_modifier_move_down(ReportList *reports, Object *ob, ModifierData *
 	return 1;
 }
 
-int ED_object_modifier_convert(
-        ReportList *UNUSED(reports), Main *bmain, Scene *scene,
-        ViewLayer *view_layer, Object *UNUSED(ob), eObjectMode object_mode, ModifierData *md)
+int ED_object_modifier_convert(ReportList *UNUSED(reports), Main *bmain, Scene *scene, ViewLayer *view_layer, Object *ob, ModifierData *md)
 {
 	Object *obn;
 	ParticleSystem *psys;
@@ -440,7 +427,7 @@ int ED_object_modifier_convert(
 	int totpart = 0, totchild = 0;
 
 	if (md->type != eModifierType_ParticleSystem) return 0;
-	if (object_mode & OB_MODE_PARTICLE_EDIT) return 0;
+	if (ob && ob->mode & OB_MODE_PARTICLE_EDIT) return 0;
 
 	psys = ((ParticleSystemModifierData *)md)->psys;
 	part = psys->part;
@@ -535,12 +522,9 @@ int ED_object_modifier_convert(
 	return 1;
 }
 
-static int modifier_apply_shape(ReportList *reports, const bContext *C, Scene *scene, Object *ob, ModifierData *md)
+static int modifier_apply_shape(ReportList *reports, const EvaluationContext *eval_ctx, Scene *scene, Object *ob, ModifierData *md)
 {
 	const ModifierTypeInfo *mti = modifierType_getInfo(md->type);
-	EvaluationContext eval_ctx;
-
-	CTX_data_eval_ctx(C, &eval_ctx);
 
 	md->scene = scene;
 
@@ -571,7 +555,7 @@ static int modifier_apply_shape(ReportList *reports, const bContext *C, Scene *s
 			return 0;
 		}
 		
-		dm = mesh_create_derived_for_modifier(&eval_ctx, scene, ob, md, 0);
+		dm = mesh_create_derived_for_modifier(eval_ctx, scene, ob, md, 0);
 		if (!dm) {
 			BKE_report(reports, RPT_ERROR, "Modifier is disabled or returned error, skipping apply");
 			return 0;
@@ -598,12 +582,9 @@ static int modifier_apply_shape(ReportList *reports, const bContext *C, Scene *s
 	return 1;
 }
 
-static int modifier_apply_obdata(ReportList *reports, const bContext *C, Scene *scene, Object *ob, ModifierData *md)
+static int modifier_apply_obdata(ReportList *reports, const EvaluationContext *eval_ctx, Scene *scene, Object *ob, ModifierData *md)
 {
 	const ModifierTypeInfo *mti = modifierType_getInfo(md->type);
-	EvaluationContext eval_ctx;
-
-	CTX_data_eval_ctx(C, &eval_ctx);
 
 	md->scene = scene;
 
@@ -627,13 +608,13 @@ static int modifier_apply_obdata(ReportList *reports, const bContext *C, Scene *
 			multires_force_update(ob);
 
 		if (mmd && mmd->totlvl && mti->type == eModifierTypeType_OnlyDeform) {
-			if (!multiresModifier_reshapeFromDeformMod(&eval_ctx, scene, mmd, ob, md)) {
+			if (!multiresModifier_reshapeFromDeformMod(eval_ctx, scene, mmd, ob, md)) {
 				BKE_report(reports, RPT_ERROR, "Multires modifier returned error, skipping apply");
 				return 0;
 			}
 		}
 		else {
-			dm = mesh_create_derived_for_modifier(&eval_ctx, scene, ob, md, 1);
+			dm = mesh_create_derived_for_modifier(eval_ctx, scene, ob, md, 1);
 			if (!dm) {
 				BKE_report(reports, RPT_ERROR, "Modifier returned error, skipping apply");
 				return 0;
@@ -659,7 +640,7 @@ static int modifier_apply_obdata(ReportList *reports, const bContext *C, Scene *
 		BKE_report(reports, RPT_INFO, "Applied modifier only changed CV points, not tessellated/bevel vertices");
 
 		vertexCos = BKE_curve_nurbs_vertexCos_get(&cu->nurb, &numVerts);
-		mti->deformVerts(md, &eval_ctx, ob, NULL, vertexCos, numVerts, 0);
+		mti->deformVerts(md, eval_ctx, ob, NULL, vertexCos, numVerts, 0);
 		BK_curve_nurbs_vertexCos_apply(&cu->nurb, vertexCos);
 
 		MEM_freeN(vertexCos);
@@ -681,16 +662,17 @@ static int modifier_apply_obdata(ReportList *reports, const bContext *C, Scene *
 			if (psys->part->type != PART_HAIR)
 				continue;
 
-			psys_apply_hair_lattice(&eval_ctx, scene, ob, psys);
+			psys_apply_hair_lattice(eval_ctx, scene, ob, psys);
 		}
 	}
 
 	return 1;
 }
 
-int ED_object_modifier_apply(ReportList *reports, const bContext *C, Scene *scene, Object *ob, ModifierData *md, int mode)
+int ED_object_modifier_apply(
+        ReportList *reports, const EvaluationContext *eval_ctx,
+        Scene *scene, Object *ob, ModifierData *md, int mode)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	int prev_mode;
 
 	if (BKE_object_is_in_editmode(ob)) {
@@ -701,7 +683,7 @@ int ED_object_modifier_apply(ReportList *reports, const bContext *C, Scene *scen
 		BKE_report(reports, RPT_ERROR, "Modifiers cannot be applied to multi-user data");
 		return 0;
 	}
-	else if ((workspace->object_mode & OB_MODE_SCULPT) &&
+	else if ((ob->mode & OB_MODE_SCULPT) &&
 	         (find_multires_modifier_before(scene, md)) &&
 	         (modifier_isSameTopology(md) == false))
 	{
@@ -717,13 +699,13 @@ int ED_object_modifier_apply(ReportList *reports, const bContext *C, Scene *scen
 	md->mode |= eModifierMode_Realtime;
 
 	if (mode == MODIFIER_APPLY_SHAPE) {
-		if (!modifier_apply_shape(reports, C, scene, ob, md)) {
+		if (!modifier_apply_shape(reports, eval_ctx, scene, ob, md)) {
 			md->mode = prev_mode;
 			return 0;
 		}
 	}
 	else {
-		if (!modifier_apply_obdata(reports, C, scene, ob, md)) {
+		if (!modifier_apply_obdata(reports, eval_ctx, scene, ob, md)) {
 			md->mode = prev_mode;
 			return 0;
 		}
@@ -753,13 +735,12 @@ int ED_object_modifier_copy(ReportList *UNUSED(reports), Object *ob, ModifierDat
 
 static int modifier_add_exec(bContext *C, wmOperator *op)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Main *bmain = CTX_data_main(C);
 	Scene *scene = CTX_data_scene(C);
 	Object *ob = ED_object_active_context(C);
 	int type = RNA_enum_get(op->ptr, "type");
 
-	if (!ED_object_modifier_add(op->reports, bmain, scene, ob, workspace->object_mode, NULL, type))
+	if (!ED_object_modifier_add(op->reports, bmain, scene, ob, NULL, type))
 		return OPERATOR_CANCELLED;
 
 	WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
@@ -896,12 +877,11 @@ ModifierData *edit_modifier_property_get(wmOperator *op, Object *ob, int type)
 
 static int modifier_remove_exec(bContext *C, wmOperator *op)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Main *bmain = CTX_data_main(C);
 	ViewLayer *view_layer = CTX_data_view_layer(C);
 	Object *ob = ED_object_active_context(C);
 	ModifierData *md = edit_modifier_property_get(op, ob, 0);
-	int mode_orig = workspace->object_mode;
+	int mode_orig = ob->mode;
 	
 	if (!md || !ED_object_modifier_remove(op->reports, bmain, ob, md))
 		return OPERATOR_CANCELLED;
@@ -910,8 +890,8 @@ static int modifier_remove_exec(bContext *C, wmOperator *op)
 
 	/* if cloth/softbody was removed, particle mode could be cleared */
 	if (mode_orig & OB_MODE_PARTICLE_EDIT) {
-		if ((workspace->object_mode & OB_MODE_PARTICLE_EDIT) == 0) {
-			if (view_layer->basact && view_layer->basact->object == ob) {
+		if ((ob->mode & OB_MODE_PARTICLE_EDIT) == 0) {
+			if (ob == OBACT(view_layer)) {
 				WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_OBJECT, NULL);
 			}
 		}
@@ -1029,7 +1009,10 @@ static int modifier_apply_exec(bContext *C, wmOperator *op)
 	ModifierData *md = edit_modifier_property_get(op, ob, 0);
 	int apply_as = RNA_enum_get(op->ptr, "apply_as");
 
-	if (!md || !ED_object_modifier_apply(op->reports, C, scene, ob, md, apply_as)) {
+	EvaluationContext eval_ctx;
+	CTX_data_eval_ctx(C, &eval_ctx);
+
+	if (!md || !ED_object_modifier_apply(op->reports, &eval_ctx, scene, ob, md, apply_as)) {
 		return OPERATOR_CANCELLED;
 	}
 
@@ -1074,16 +1057,14 @@ void OBJECT_OT_modifier_apply(wmOperatorType *ot)
 
 static int modifier_convert_exec(bContext *C, wmOperator *op)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Main *bmain = CTX_data_main(C);
 	Scene *scene = CTX_data_scene(C);
 	ViewLayer *view_layer = CTX_data_view_layer(C);
 	Object *ob = ED_object_active_context(C);
 	ModifierData *md = edit_modifier_property_get(op, ob, 0);
-
-	if (!md || !ED_object_modifier_convert(op->reports, bmain, scene, view_layer, ob, workspace->object_mode, md)) {
+	
+	if (!md || !ED_object_modifier_convert(op->reports, bmain, scene, view_layer, ob, md))
 		return OPERATOR_CANCELLED;
-	}
 
 	DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
 	WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
@@ -1167,13 +1148,10 @@ static int multires_higher_levels_delete_exec(bContext *C, wmOperator *op)
 	
 	if (!mmd)
 		return OPERATOR_CANCELLED;
+	
+	multiresModifier_del_levels(mmd, ob, 1);
 
-	EvaluationContext eval_ctx;
-	CTX_data_eval_ctx(C, &eval_ctx);
-
-	multiresModifier_del_levels(mmd, ob, 1, eval_ctx.object_mode);
-
-	ED_object_iter_other(&eval_ctx, CTX_data_main(C), ob, true,
+	ED_object_iter_other(CTX_data_main(C), ob, true,
 	                     ED_object_multires_update_totlevels_cb,
 	                     &mmd->totlvl);
 	
@@ -1214,20 +1192,17 @@ static int multires_subdivide_exec(bContext *C, wmOperator *op)
 	
 	if (!mmd)
 		return OPERATOR_CANCELLED;
+	
+	multiresModifier_subdivide(mmd, ob, 0, mmd->simple);
 
-	EvaluationContext eval_ctx;
-	CTX_data_eval_ctx(C, &eval_ctx);
-	multiresModifier_subdivide(mmd, ob, 0, mmd->simple, eval_ctx.object_mode);
-
-	ED_object_iter_other(
-	        &eval_ctx, CTX_data_main(C), ob, true,
-	        ED_object_multires_update_totlevels_cb,
-	        &mmd->totlvl);
+	ED_object_iter_other(CTX_data_main(C), ob, true,
+	                     ED_object_multires_update_totlevels_cb,
+	                     &mmd->totlvl);
 
 	DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
 	WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
 
-	if (eval_ctx.mode & OB_MODE_SCULPT) {
+	if (ob->mode & OB_MODE_SCULPT) {
 		/* ensure that grid paint mask layer is created */
 		BKE_sculpt_mask_layers_ensure(ob, mmd);
 	}
@@ -1264,11 +1239,8 @@ static int multires_reshape_exec(bContext *C, wmOperator *op)
 {
 	Object *ob = ED_object_active_context(C), *secondob = NULL;
 	Scene *scene = CTX_data_scene(C);
-	EvaluationContext eval_ctx;
 	MultiresModifierData *mmd = (MultiresModifierData *)edit_modifier_property_get(op, ob, eModifierType_Multires);
 
-	CTX_data_eval_ctx(C, &eval_ctx);
-
 	if (!mmd)
 		return OPERATOR_CANCELLED;
 
@@ -1291,6 +1263,9 @@ static int multires_reshape_exec(bContext *C, wmOperator *op)
 		return OPERATOR_CANCELLED;
 	}
 
+	EvaluationContext eval_ctx;
+	CTX_data_eval_ctx(C, &eval_ctx);
+
 	if (!multiresModifier_reshape(&eval_ctx, scene, mmd, ob, secondob)) {
 		BKE_report(op->reports, RPT_ERROR, "Objects do not have the same number of vertices");
 		return OPERATOR_CANCELLED;
@@ -1441,9 +1416,8 @@ static int multires_base_apply_exec(bContext *C, wmOperator *op)
 	
 	if (!mmd)
 		return OPERATOR_CANCELLED;
-
-	const WorkSpace *workspace = CTX_wm_workspace(C);
-	multiresModifier_base_apply(mmd, ob, workspace->object_mode);
+	
+	multiresModifier_base_apply(mmd, ob);
 
 	DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
 	WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
@@ -1725,10 +1699,8 @@ static void skin_armature_bone_create(Object *skin_ob,
 	}
 }
 
-static Object *modifier_skin_armature_create(const bContext *C, Scene *scene, ViewLayer *view_layer, Object *skin_ob)
+static Object *modifier_skin_armature_create(const EvaluationContext *eval_ctx, Main *bmain, Scene *scene, Object *skin_ob)
 {
-	Main *bmain = CTX_data_main(C);
-	EvaluationContext eval_ctx;
 	BLI_bitmap *edges_visited;
 	DerivedMesh *deform_dm;
 	MVert *mvert;
@@ -1740,9 +1712,7 @@ static Object *modifier_skin_armature_create(const bContext *C, Scene *scene, Vi
 	int *emap_mem;
 	int v;
 
-	CTX_data_eval_ctx(C, &eval_ctx);
-
-	deform_dm = mesh_get_derived_deform(&eval_ctx, scene, skin_ob, CD_MASK_BAREMESH);
+	deform_dm = mesh_get_derived_deform(eval_ctx, scene, skin_ob, CD_MASK_BAREMESH);
 	mvert = deform_dm->getVertArray(deform_dm);
 
 	/* add vertex weights to original mesh */
@@ -1752,7 +1722,7 @@ static Object *modifier_skin_armature_create(const bContext *C, Scene *scene, Vi
 	                     NULL,
 	                     me->totvert);
 	
-	arm_ob = BKE_object_add(bmain, scene, view_layer, OB_ARMATURE, NULL);
+	arm_ob = BKE_object_add(bmain, scene, eval_ctx->view_layer, OB_ARMATURE, NULL);
 	BKE_object_transform_copy(arm_ob, skin_ob);
 	arm = arm_ob->data;
 	arm->layer = 1;
@@ -1811,7 +1781,6 @@ static int skin_armature_create_exec(bContext *C, wmOperator *op)
 {
 	Main *bmain = CTX_data_main(C);
 	Scene *scene = CTX_data_scene(C);
-	ViewLayer *view_layer = CTX_data_view_layer(C);
 	Object *ob = CTX_data_active_object(C), *arm_ob;
 	Mesh *me = ob->data;
 	ModifierData *skin_md;
@@ -1822,8 +1791,11 @@ static int skin_armature_create_exec(bContext *C, wmOperator *op)
 		return OPERATOR_CANCELLED;
 	}
 
+	EvaluationContext eval_ctx;
+	CTX_data_eval_ctx(C, &eval_ctx);
+
 	/* create new armature */
-	arm_ob = modifier_skin_armature_create(C, scene, view_layer, ob);
+	arm_ob = modifier_skin_armature_create(&eval_ctx, bmain, scene, ob);
 
 	/* add a modifier to connect the new armature to the mesh */
 	arm_md = (ArmatureModifierData *)modifier_new(eModifierType_Armature);
diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c
index 59ba92eb1bffdf58601ee7b7404de6270b94aa99..66dc17fe77dd3e5887210f8b272eda1773c957dd 100644
--- a/source/blender/editors/object/object_ops.c
+++ b/source/blender/editors/object/object_ops.c
@@ -94,6 +94,7 @@ void ED_operatortypes_object(void)
 	WM_operatortype_append(OBJECT_OT_select_random);
 	WM_operatortype_append(OBJECT_OT_select_all);
 	WM_operatortype_append(OBJECT_OT_select_same_group);
+	WM_operatortype_append(OBJECT_OT_select_same_collection);
 	WM_operatortype_append(OBJECT_OT_select_by_type);
 	WM_operatortype_append(OBJECT_OT_select_linked);
 	WM_operatortype_append(OBJECT_OT_select_grouped);
@@ -219,6 +220,8 @@ void ED_operatortypes_object(void)
 	WM_operatortype_append(OBJECT_OT_logic_bricks_copy);
 	WM_operatortype_append(OBJECT_OT_game_physics_copy);
 
+	WM_operatortype_append(OBJECT_OT_move_to_collection);
+
 	WM_operatortype_append(OBJECT_OT_shape_key_add);
 	WM_operatortype_append(OBJECT_OT_shape_key_remove);
 	WM_operatortype_append(OBJECT_OT_shape_key_clear);
@@ -283,9 +286,8 @@ void ED_operatormacros_object(void)
 
 static int object_mode_poll(bContext *C)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Object *ob = CTX_data_active_object(C);
-	return (!ob || workspace->object_mode == OB_MODE_OBJECT);
+	return (!ob || ob->mode == OB_MODE_OBJECT);
 }
 
 void ED_keymap_object(wmKeyConfig *keyconf)
@@ -423,6 +425,8 @@ void ED_keymap_object(wmKeyConfig *keyconf)
 		kmi = WM_keymap_add_item(keymap, "OBJECT_OT_subdivision_set", ZEROKEY + i, KM_PRESS, KM_CTRL, 0);
 		RNA_int_set(kmi->ptr, "level", i);
 	}
+
+	WM_keymap_add_item(keymap, "OBJECT_OT_move_to_collection", MKEY, KM_PRESS, 0, 0);
 }
 
 void ED_keymap_proportional_cycle(struct wmKeyConfig *UNUSED(keyconf), struct wmKeyMap *keymap)
diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c
index 00c5fdf3cc728ea302b0979bc0199995a0e1bad4..3443a268ef2baab3bbc30b3f47a3e0f05525edc7 100644
--- a/source/blender/editors/object/object_relations.c
+++ b/source/blender/editors/object/object_relations.c
@@ -717,8 +717,7 @@ bool ED_object_parent_set(ReportList *reports, const bContext *C, Scene *scene,
 					switch (partype) {
 						case PAR_CURVE: /* curve deform */
 							if (modifiers_isDeformedByCurve(ob) != par) {
-								md = ED_object_modifier_add(
-								        reports, bmain, scene, ob, eval_ctx.object_mode, NULL, eModifierType_Curve);
+								md = ED_object_modifier_add(reports, bmain, scene, ob, NULL, eModifierType_Curve);
 								if (md) {
 									((CurveModifierData *)md)->object = par;
 								}
@@ -729,8 +728,7 @@ bool ED_object_parent_set(ReportList *reports, const bContext *C, Scene *scene,
 							break;
 						case PAR_LATTICE: /* lattice deform */
 							if (modifiers_isDeformedByLattice(ob) != par) {
-								md = ED_object_modifier_add(
-								        reports, bmain, scene, ob, eval_ctx.object_mode, NULL, eModifierType_Lattice);
+								md = ED_object_modifier_add(reports, bmain, scene, ob, NULL, eModifierType_Lattice);
 								if (md) {
 									((LatticeModifierData *)md)->object = par;
 								}
@@ -738,8 +736,7 @@ bool ED_object_parent_set(ReportList *reports, const bContext *C, Scene *scene,
 							break;
 						default: /* armature deform */
 							if (modifiers_isDeformedByArmature(ob) != par) {
-								md = ED_object_modifier_add(
-								        reports, bmain, scene, ob, eval_ctx.object_mode, NULL, eModifierType_Armature);
+								md = ED_object_modifier_add(reports, bmain, scene, ob, NULL, eModifierType_Armature);
 								if (md) {
 									((ArmatureModifierData *)md)->object = par;
 								}
@@ -1428,7 +1425,6 @@ static bool allow_make_links_data(const int type, Object *ob_src, Object *ob_dst
 
 static int make_links_data_exec(bContext *C, wmOperator *op)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Main *bmain = CTX_data_main(C);
 	const int type = RNA_enum_get(op->ptr, "type");
 	Object *ob_src;
@@ -1513,7 +1509,7 @@ static int make_links_data_exec(bContext *C, wmOperator *op)
 						}
 						break;
 					case MAKE_LINKS_MODIFIERS:
-						BKE_object_link_modifiers(ob_dst, ob_src, workspace->object_mode);
+						BKE_object_link_modifiers(ob_dst, ob_src);
 						DEG_id_tag_update(&ob_dst->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME);
 						break;
 					case MAKE_LINKS_FONTS:
diff --git a/source/blender/editors/object/object_select.c b/source/blender/editors/object/object_select.c
index f416f499b6a81e63b2ad185975fb7c769112bf88..cd1300dd52eaf235f50ce1fdb2f49828a0ad85e1 100644
--- a/source/blender/editors/object/object_select.c
+++ b/source/blender/editors/object/object_select.c
@@ -123,51 +123,7 @@ void ED_object_base_select(Base *base, eObjectSelect_Mode mode)
 void ED_object_base_activate(bContext *C, Base *base)
 {
 	ViewLayer *view_layer = CTX_data_view_layer(C);
-
-	wmWindowManager *wm = CTX_wm_manager(C);
-	wmWindow *win = CTX_wm_window(C);
-	WorkSpace *workspace = CTX_wm_workspace(C);
-
-	eObjectMode object_mode = workspace->object_mode;
-	eObjectMode object_mode_set = OB_MODE_OBJECT;
-
-	if (base && ED_workspace_object_mode_in_other_window(
-	            wm, win, base->object,
-	            &object_mode_set))
-	{
-		/* Sync existing object mode with workspace. */
-		workspace->object_mode = object_mode_set;
-		view_layer->basact = base;
-	}
-	else {
-		/* Apply the workspaces mode to the object (when possible). */
-		Scene *scene = CTX_data_scene(C);
-		Object *obact = base ? base->object : NULL;
-		/* We don't know the previous active object in update.
-		 *
-		 * Not correct because it's possible other work-spaces use these.
-		 * although that's a corner case. */
-		if (workspace->object_mode & OB_MODE_ALL_MODE_DATA) {
-			EvaluationContext eval_ctx;
-			CTX_data_eval_ctx(C, &eval_ctx);
-			FOREACH_OBJECT_BEGIN(view_layer, ob) {
-				if (ob != obact) {
-					if (ED_object_mode_generic_has_data(&eval_ctx, ob) &&
-					    ED_workspace_object_mode_in_other_window(wm, win, ob, NULL) == false)
-					{
-						ED_object_mode_generic_exit(&eval_ctx, workspace, scene, ob);
-					}
-				}
-			}
-			FOREACH_OBJECT_END;
-		}
-
-		workspace->object_mode = OB_MODE_OBJECT;
-
-		view_layer->basact = base;
-
-		ED_object_mode_generic_enter(C, object_mode);
-	}
+	view_layer->basact = base;
 
 	if (base) {
 		WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, view_layer);
@@ -183,14 +139,13 @@ static int objects_selectable_poll(bContext *C)
 {
 	/* we don't check for linked scenes here, selection is
 	 * still allowed then for inspection of scene */
-	if (CTX_data_edit_object(C)) {
-		return 0;
-	}
+	Object *obact = CTX_data_active_object(C);
 
-	const WorkSpace *workspace = CTX_wm_workspace(C);
-	if (workspace->object_mode != OB_MODE_OBJECT) {
+	if (CTX_data_edit_object(C))
 		return 0;
-	}
+	if (obact && obact->mode)
+		return 0;
+	
 	return 1;
 }
 
@@ -544,7 +499,7 @@ enum {
 	OBJECT_GRPSEL_PARENT             =  2,
 	OBJECT_GRPSEL_SIBLINGS           =  3,
 	OBJECT_GRPSEL_TYPE               =  4,
-	/*OBJECT_GRPSEL_LAYER              =  5,*/
+	OBJECT_GRPSEL_COLLECTION         =  5,
 	OBJECT_GRPSEL_GROUP              =  6,
 	OBJECT_GRPSEL_HOOK               =  7,
 	OBJECT_GRPSEL_PASS               =  8,
@@ -560,6 +515,7 @@ static const EnumPropertyItem prop_select_grouped_types[] = {
 	{OBJECT_GRPSEL_PARENT, "PARENT", 0, "Parent", ""},
 	{OBJECT_GRPSEL_SIBLINGS, "SIBLINGS", 0, "Siblings", "Shared Parent"},
 	{OBJECT_GRPSEL_TYPE, "TYPE", 0, "Type", "Shared object type"},
+	{OBJECT_GRPSEL_COLLECTION, "COLLECTION", 0, "Collection", "Shared collection"},
 	{OBJECT_GRPSEL_GROUP, "GROUP", 0, "Group", "Shared group"},
 	{OBJECT_GRPSEL_HOOK, "HOOK", 0, "Hook", ""},
 	{OBJECT_GRPSEL_PASS, "PASS", 0, "Pass", "Render pass Index"},
@@ -733,6 +689,60 @@ static bool select_grouped_type(bContext *C, Object *ob)
 	return changed;
 }
 
+#define COLLECTION_MENU_MAX  24
+static bool select_grouped_collection(bContext *C, Object *ob)  /* Select objects in the same collection as the active */
+{
+	typedef struct EnumeratedCollection {
+		struct SceneCollection *collection;
+		int index;
+	} EnumeratedCollection;
+
+	bool changed = false;
+	SceneCollection *collection;
+	EnumeratedCollection ob_collections[COLLECTION_MENU_MAX];
+	int collection_count = 0, i;
+	uiPopupMenu *pup;
+	uiLayout *layout;
+
+	i = 0;
+	FOREACH_SCENE_COLLECTION_BEGIN(CTX_data_scene(C), scene_collection)
+	{
+		if (BKE_collection_object_exists(scene_collection, ob)) {
+			ob_collections[collection_count].index = i;
+			ob_collections[collection_count].collection = scene_collection;
+			if (++collection_count >= COLLECTION_MENU_MAX) {
+				break;
+			}
+		}
+		i++;
+	}
+	FOREACH_SCENE_COLLECTION_END;
+
+	if (!collection_count) {
+		return 0;
+	}
+	else if (collection_count == 1) {
+		collection = ob_collections[0].collection;
+		return BKE_collection_objects_select(CTX_data_view_layer(C), collection);
+	}
+
+	/* build the menu. */
+	pup = UI_popup_menu_begin(C, IFACE_("Select Collection"), ICON_NONE);
+	layout = UI_popup_menu_layout(pup);
+
+	for (i = 0; i < collection_count; i++) {
+		uiItemIntO(layout,
+		           ob_collections[i].collection->name,
+		           ICON_NONE,
+		           "OBJECT_OT_select_same_collection",
+		           "collection_index",
+		           ob_collections[i].index);
+	}
+
+	UI_popup_menu_end(C, pup);
+	return changed;  /* The operator already handle this! */
+}
+
 static bool select_grouped_index_object(bContext *C, Object *ob)
 {
 	bool changed = false;
@@ -882,6 +892,9 @@ static int object_select_grouped_exec(bContext *C, wmOperator *op)
 		case OBJECT_GRPSEL_TYPE:
 			changed |= select_grouped_type(C, ob);
 			break;
+		case OBJECT_GRPSEL_COLLECTION:
+			changed |= select_grouped_collection(C, ob);
+			break;
 		case OBJECT_GRPSEL_GROUP:
 			changed |= select_grouped_group(C, ob);
 			break;
@@ -1055,6 +1068,49 @@ void OBJECT_OT_select_same_group(wmOperatorType *ot)
 	RNA_def_string(ot->srna, "group", NULL, MAX_ID_NAME, "Group", "Name of the group to select");
 }
 
+/**************************** Select In The Same Collection ****************************/
+
+static int object_select_same_collection_exec(bContext *C, wmOperator *op)
+{
+	SceneCollection *collection;
+
+	/* passthrough if no objects are visible */
+	if (CTX_DATA_COUNT(C, visible_bases) == 0) return OPERATOR_PASS_THROUGH;
+
+	int collection_index = RNA_int_get(op->ptr, "collection_index");
+	collection = BKE_collection_from_index(CTX_data_scene(C), collection_index);
+
+	if (!collection) {
+		return OPERATOR_PASS_THROUGH;
+	}
+
+	if (BKE_collection_objects_select(CTX_data_view_layer(C), collection)) {
+		WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, CTX_data_scene(C));
+	}
+
+	return OPERATOR_FINISHED;
+}
+
+void OBJECT_OT_select_same_collection(wmOperatorType *ot)
+{
+	PropertyRNA *prop;
+
+	/* identifiers */
+	ot->name = "Select Same Collection";
+	ot->description = "Select object in the same collection";
+	ot->idname = "OBJECT_OT_select_same_collection";
+
+	/* api callbacks */
+	ot->exec = object_select_same_collection_exec;
+	ot->poll = objects_selectable_poll;
+
+	/* flags */
+	ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+	prop = RNA_def_int(ot->srna, "collection_index", -1, -1, INT_MAX,
+	                   "Collection Index", "Index of the collection to select", 0, INT_MAX);
+	RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+}
 /**************************** Select Mirror ****************************/
 static int object_select_mirror_exec(bContext *C, wmOperator *op)
 {
diff --git a/source/blender/editors/object/object_shapekey.c b/source/blender/editors/object/object_shapekey.c
index bd3bd8fd0a5fe3bf78c746ed1a42dbdfd69f3c9c..1f80cb5f0bcdde1c3a13e60cbd0d0fb9360fc71a 100644
--- a/source/blender/editors/object/object_shapekey.c
+++ b/source/blender/editors/object/object_shapekey.c
@@ -49,7 +49,6 @@
 #include "DNA_mesh_types.h"
 #include "DNA_meshdata_types.h"
 #include "DNA_object_types.h"
-#include "DNA_workspace_types.h"
 
 #include "BKE_context.h"
 #include "BKE_key.h"
@@ -226,20 +225,18 @@ static bool object_shape_key_mirror(bContext *C, Object *ob,
 
 static int shape_key_mode_poll(bContext *C)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Object *ob = ED_object_context(C);
 	ID *data = (ob) ? ob->data : NULL;
-	return (ob && !ID_IS_LINKED(ob) && data && !ID_IS_LINKED(data) && (workspace->object_mode != OB_MODE_EDIT));
+	return (ob && !ID_IS_LINKED(ob) && data && !ID_IS_LINKED(data) && ob->mode != OB_MODE_EDIT);
 }
 
 static int shape_key_mode_exists_poll(bContext *C)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Object *ob = ED_object_context(C);
 	ID *data = (ob) ? ob->data : NULL;
 
 	/* same as shape_key_mode_poll */
-	return (ob && !ID_IS_LINKED(ob) && data && !ID_IS_LINKED(data) && (workspace->object_mode != OB_MODE_EDIT)) &&
+	return (ob && !ID_IS_LINKED(ob) && data && !ID_IS_LINKED(data) && ob->mode != OB_MODE_EDIT) &&
 	       /* check a keyblock exists */
 	       (BKE_keyblock_from_object(ob) != NULL);
 }
@@ -247,13 +244,12 @@ static int shape_key_mode_exists_poll(bContext *C)
 static int shape_key_move_poll(bContext *C)
 {
 	/* Same as shape_key_mode_exists_poll above, but ensure we have at least two shapes! */
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Object *ob = ED_object_context(C);
 	ID *data = (ob) ? ob->data : NULL;
 	Key *key = BKE_key_from_object(ob);
 
 	return (ob && !ID_IS_LINKED(ob) && data && !ID_IS_LINKED(data) &&
-	        (workspace->object_mode != OB_MODE_EDIT) && key && key->totkey > 1);
+	        ob->mode != OB_MODE_EDIT && key && key->totkey > 1);
 }
 
 static int shape_key_poll(bContext *C)
diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.c
index 7ea1a04f31f271e33668ad40c6de5379194872ce..bb23f871a256d9ccd5a9818e22084ca666d32504 100644
--- a/source/blender/editors/object/object_transform.c
+++ b/source/blender/editors/object/object_transform.c
@@ -245,7 +245,6 @@ static int object_clear_transform_generic_exec(bContext *C, wmOperator *op,
                                                void (*clear_func)(Object *, const bool),
                                                const char default_ksName[])
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Scene *scene = CTX_data_scene(C);
 	KeyingSet *ks;
 	const bool clear_delta = RNA_boolean_get(op->ptr, "clear_delta");
@@ -264,7 +263,7 @@ static int object_clear_transform_generic_exec(bContext *C, wmOperator *op,
 	 */
 	CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects)
 	{
-		if (!(workspace->object_mode & OB_MODE_WEIGHT_PAINT)) {
+		if (!(ob->mode & OB_MODE_WEIGHT_PAINT)) {
 			/* run provided clearing function */
 			clear_func(ob, clear_delta);
 			
diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c
index eb04de5feb271695f495fb6065528ea897bf86ce..7026e8671ad6fc112535281428077a555f2f555b 100644
--- a/source/blender/editors/object/object_vgroup.c
+++ b/source/blender/editors/object/object_vgroup.c
@@ -85,9 +85,9 @@
 #include "object_intern.h"
 
 /************************ Exported Functions **********************/
-static bool vertex_group_use_vert_sel(const Object *ob)
+static bool vertex_group_use_vert_sel(Object *ob)
 {
-	if (BKE_object_is_in_editmode(ob)) {
+	if (ob->mode == OB_MODE_EDIT) {
 		return true;
 	}
 	else if (ob->type == OB_MESH && ((Mesh *)ob->data)->editflag & ME_EDIT_PAINT_VERT_SEL) {
@@ -108,15 +108,13 @@ static Lattice *vgroup_edit_lattice(Object *ob)
 bool ED_vgroup_sync_from_pose(Object *ob)
 {
 	Object *armobj = BKE_object_pose_armature_get(ob);
-	if (armobj) {
+	if (armobj && (armobj->mode & OB_MODE_POSE)) {
 		struct bArmature *arm = armobj->data;
-		if (arm->flag & ARM_POSEMODE) {
-			if (arm->act_bone) {
-				int def_num = defgroup_name_index(ob, arm->act_bone->name);
-				if (def_num != -1) {
-					ob->actdef = def_num + 1;
-					return true;
-				}
+		if (arm->act_bone) {
+			int def_num = defgroup_name_index(ob, arm->act_bone->name);
+			if (def_num != -1) {
+				ob->actdef = def_num + 1;
+				return true;
 			}
 		}
 	}
@@ -2550,13 +2548,12 @@ static int vertex_group_vert_poll_ex(bContext *C, const bool needs_select, const
 		return false;
 	}
 
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	if (BKE_object_is_in_editmode_vgroup(ob)) {
 		return true;
 	}
-	else if (workspace->object_mode & OB_MODE_WEIGHT_PAINT) {
+	else if (ob->mode & OB_MODE_WEIGHT_PAINT) {
 		if (needs_select) {
-			if (BKE_object_is_in_wpaint_select_vert(ob, workspace->object_mode)) {
+			if (BKE_object_is_in_wpaint_select_vert(ob)) {
 				return true;
 			}
 			else {
@@ -2608,9 +2605,8 @@ static int vertex_group_vert_select_unlocked_poll(bContext *C)
 	if (!(ob && !ID_IS_LINKED(ob) && data && !ID_IS_LINKED(data)))
 		return 0;
 
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	if (!(BKE_object_is_in_editmode_vgroup(ob) ||
-	    BKE_object_is_in_wpaint_select_vert(ob, workspace->object_mode)))
+	    BKE_object_is_in_wpaint_select_vert(ob)))
 	{
 		return 0;
 	}
@@ -2636,9 +2632,8 @@ static int vertex_group_vert_select_mesh_poll(bContext *C)
 	if (ob->type != OB_MESH)
 		return 0;
 
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	return (BKE_object_is_in_editmode_vgroup(ob) ||
-	        BKE_object_is_in_wpaint_select_vert(ob, workspace->object_mode));
+	        BKE_object_is_in_wpaint_select_vert(ob));
 }
 
 static int vertex_group_add_exec(bContext *C, wmOperator *UNUSED(op))
@@ -3522,8 +3517,7 @@ static char *vgroup_init_remap(Object *ob)
 	return name_array;
 }
 
-static int vgroup_do_remap(
-        const EvaluationContext *eval_ctx, Object *ob, const char *name_array, wmOperator *op)
+static int vgroup_do_remap(Object *ob, const char *name_array, wmOperator *op)
 {
 	MDeformVert *dvert = NULL;
 	bDeformGroup *def;
@@ -3544,7 +3538,7 @@ static int vgroup_do_remap(
 		BLI_assert(sort_map[i] != -1);
 	}
 
-	if (eval_ctx->object_mode == OB_MODE_EDIT) {
+	if (ob->mode == OB_MODE_EDIT) {
 		if (ob->type == OB_MESH) {
 			BMEditMesh *em = BKE_editmesh_from_object(ob);
 			const int cd_dvert_offset = CustomData_get_offset(&em->bm->vdata, CD_MDEFORMVERT);
@@ -3642,8 +3636,6 @@ enum {
 
 static int vertex_group_sort_exec(bContext *C, wmOperator *op)
 {
-	EvaluationContext eval_ctx;
-	CTX_data_eval_ctx(C, &eval_ctx);
 	Object *ob = ED_object_context(C);
 	char *name_array;
 	int ret;
@@ -3663,7 +3655,7 @@ static int vertex_group_sort_exec(bContext *C, wmOperator *op)
 	}
 	
 	/*remap vgroup data to map to correct names*/
-	ret = vgroup_do_remap(&eval_ctx, ob, name_array, op);
+	ret = vgroup_do_remap(ob, name_array, op);
 
 	if (ret != OPERATOR_CANCELLED) {
 		DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
@@ -3699,8 +3691,6 @@ void OBJECT_OT_vertex_group_sort(wmOperatorType *ot)
 
 static int vgroup_move_exec(bContext *C, wmOperator *op)
 {
-	EvaluationContext eval_ctx;
-	CTX_data_eval_ctx(C, &eval_ctx);
 	Object *ob = ED_object_context(C);
 	bDeformGroup *def;
 	char *name_array;
@@ -3715,7 +3705,7 @@ static int vgroup_move_exec(bContext *C, wmOperator *op)
 	name_array = vgroup_init_remap(ob);
 
 	if (BLI_listbase_link_move(&ob->defbase, def, dir)) {
-		ret = vgroup_do_remap(&eval_ctx, ob, name_array, op);
+		ret = vgroup_do_remap(ob, name_array, op);
 
 		if (ret != OPERATOR_CANCELLED) {
 			DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
diff --git a/source/blender/editors/physics/dynamicpaint_ops.c b/source/blender/editors/physics/dynamicpaint_ops.c
index e068b898345760c1a6e5a4c4b26869c9bd3447fa..6360e6281c87661805106eb963ee6a8faeb02995 100644
--- a/source/blender/editors/physics/dynamicpaint_ops.c
+++ b/source/blender/editors/physics/dynamicpaint_ops.c
@@ -367,7 +367,7 @@ static void dynamicPaint_bakeImageSequence(DynamicPaintBakeJob *job)
 	scene->r.cfra = (int)frame;
 	ED_update_for_newframe(job->bmain, scene, job->view_layer, job->depsgraph);
 
-	/* Init surface	*/
+	/* Init surface */
 	if (!dynamicPaint_createUVSurface(scene, surface, job->progress, job->do_update)) {
 		job->success = 0;
 		return;
@@ -508,7 +508,7 @@ static int dynamicpaint_bake_exec(struct bContext *C, struct wmOperator *op)
 
 	WM_set_locked_interface(CTX_wm_manager(C), true);
 
-	/*  Bake Dynamic Paint	*/
+	/* Bake Dynamic Paint */
 	WM_jobs_start(CTX_wm_manager(C), wm_job);
 
 	return OPERATOR_FINISHED;
diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c
index 224790269e2b50276b8e323120977496f9334cc8..51abb5b2eaaeca17d4d5111e8394f507a4bdac0a 100644
--- a/source/blender/editors/physics/particle_edit.c
+++ b/source/blender/editors/physics/particle_edit.c
@@ -42,7 +42,6 @@
 #include "DNA_view3d_types.h"
 #include "DNA_screen_types.h"
 #include "DNA_space_types.h"
-#include "DNA_workspace_types.h"
 
 #include "BLI_math.h"
 #include "BLI_lasso_2d.h"
@@ -93,29 +92,25 @@
 
 int PE_poll(bContext *C)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Scene *scene= CTX_data_scene(C);
-	ViewLayer *view_layer = CTX_data_view_layer(C);
 	Object *ob= CTX_data_active_object(C);
 
-	if (!scene || !view_layer || !ob || !(workspace->object_mode & OB_MODE_PARTICLE_EDIT)) {
+	if (!scene || !ob || !(ob->mode & OB_MODE_PARTICLE_EDIT)) {
 		return 0;
 	}
-	return (PE_get_current(scene, view_layer, ob) != NULL);
+	return (PE_get_current(scene, ob) != NULL);
 }
 
 int PE_hair_poll(bContext *C)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Scene *scene= CTX_data_scene(C);
-	ViewLayer *view_layer = CTX_data_view_layer(C);
 	Object *ob= CTX_data_active_object(C);
 	PTCacheEdit *edit;
 
-	if (!scene || !ob || !(workspace->object_mode & OB_MODE_PARTICLE_EDIT)) {
+	if (!scene || !ob || !(ob->mode & OB_MODE_PARTICLE_EDIT)) {
 		return 0;
 	}
-	edit= PE_get_current(scene, view_layer, ob);
+	edit= PE_get_current(scene, ob);
 
 	return (edit && edit->psys);
 }
@@ -136,8 +131,6 @@ void PE_free_ptcache_edit(PTCacheEdit *edit)
 
 	if (edit==0) return;
 
-	PTCacheUndo_clear(edit);
-
 	if (edit->points) {
 		LOOP_POINTS {
 			if (point->keys)
@@ -201,7 +194,7 @@ static float pe_brush_size_get(const Scene *UNUSED(scene), ParticleBrushData *br
  * note: this function runs on poll, therefor it can runs many times a second
  * keep it fast! */
 static PTCacheEdit *pe_get_current(
-        const EvaluationContext *eval_ctx, Scene *scene, ViewLayer *view_layer, Object *ob, int create)
+        const EvaluationContext *eval_ctx, Scene *scene, Object *ob, int create)
 {
 	ParticleEditSettings *pset= PE_settings(scene);
 	PTCacheEdit *edit = NULL;
@@ -212,7 +205,6 @@ static PTCacheEdit *pe_get_current(
 		return NULL;
 
 	pset->scene = scene;
-	pset->view_layer = view_layer;
 	pset->object = ob;
 
 	BKE_ptcache_ids_from_object(&pidlist, ob, NULL, 0);
@@ -241,18 +233,18 @@ static PTCacheEdit *pe_get_current(
 				if (psys->part && psys->part->type == PART_HAIR) {
 					if (psys->flag & PSYS_HAIR_DYNAMICS && psys->pointcache->flag & PTCACHE_BAKED) {
 						if (create && !psys->pointcache->edit)
-							PE_create_particle_edit(eval_ctx, scene, view_layer, ob, pid->cache, NULL);
+							PE_create_particle_edit(eval_ctx, scene, ob, pid->cache, NULL);
 						edit = pid->cache->edit;
 					}
 					else {
 						if (create && !psys->edit && psys->flag & PSYS_HAIR_DONE)
-							PE_create_particle_edit(eval_ctx, scene, view_layer, ob, NULL, psys);
+							PE_create_particle_edit(eval_ctx, scene, ob, NULL, psys);
 						edit = psys->edit;
 					}
 				}
 				else {
 					if (create && pid->cache->flag & PTCACHE_BAKED && !pid->cache->edit)
-						PE_create_particle_edit(eval_ctx, scene, view_layer, ob, pid->cache, psys);
+						PE_create_particle_edit(eval_ctx, scene, ob, pid->cache, psys);
 					edit = pid->cache->edit;
 				}
 
@@ -263,7 +255,7 @@ static PTCacheEdit *pe_get_current(
 			if (create && pid->cache->flag & PTCACHE_BAKED && !pid->cache->edit) {
 				pset->flag |= PE_FADE_TIME;
 				// NICE TO HAVE but doesn't work: pset->brushtype = PE_BRUSH_COMB;
-				PE_create_particle_edit(eval_ctx, scene, view_layer, ob, pid->cache, NULL);
+				PE_create_particle_edit(eval_ctx, scene, ob, pid->cache, NULL);
 			}
 			edit = pid->cache->edit;
 			break;
@@ -272,7 +264,7 @@ static PTCacheEdit *pe_get_current(
 			if (create && pid->cache->flag & PTCACHE_BAKED && !pid->cache->edit) {
 				pset->flag |= PE_FADE_TIME;
 				// NICE TO HAVE but doesn't work: pset->brushtype = PE_BRUSH_COMB;
-				PE_create_particle_edit(eval_ctx, scene, view_layer, ob, pid->cache, NULL);
+				PE_create_particle_edit(eval_ctx, scene, ob, pid->cache, NULL);
 			}
 			edit = pid->cache->edit;
 			break;
@@ -287,19 +279,19 @@ static PTCacheEdit *pe_get_current(
 	return edit;
 }
 
-PTCacheEdit *PE_get_current(Scene *scene, ViewLayer *view_layer, Object *ob)
+PTCacheEdit *PE_get_current(Scene *scene, Object *ob)
 {
-	return pe_get_current(NULL, scene, view_layer, ob, 0);
+	return pe_get_current(NULL, scene, ob, 0);
 }
 
 PTCacheEdit *PE_create_current(const EvaluationContext *eval_ctx, Scene *scene, Object *ob)
 {
-	return pe_get_current(eval_ctx, scene, eval_ctx->view_layer, ob, 1);
+	return pe_get_current(eval_ctx, scene, ob, 1);
 }
 
 void PE_current_changed(const EvaluationContext *eval_ctx, Scene *scene, Object *ob)
 {
-	if (eval_ctx->object_mode == OB_MODE_PARTICLE_EDIT) {
+	if (ob->mode == OB_MODE_PARTICLE_EDIT) {
 		PE_create_current(eval_ctx, scene, ob);
 	}
 }
@@ -385,7 +377,7 @@ static void PE_set_data(bContext *C, PEData *data)
 	data->view_layer = CTX_data_view_layer(C);
 	data->ob = CTX_data_active_object(C);
 	CTX_data_eval_ctx(C, &data->eval_ctx);
-	data->edit = PE_get_current(data->scene, data->view_layer, data->ob);
+	data->edit = PE_get_current(data->scene, data->ob);
 }
 
 static void PE_set_view3d_data(bContext *C, PEData *data)
@@ -1146,7 +1138,7 @@ void recalc_emitter_field(Object *ob, ParticleSystem *psys)
 
 static void PE_update_selection(const EvaluationContext *eval_ctx, Scene *scene, Object *ob, int useflag)
 {
-	PTCacheEdit *edit = PE_get_current(scene, eval_ctx->view_layer, ob);
+	PTCacheEdit *edit = PE_get_current(scene, ob);
 	HairKey *hkey;
 	POINT_P; KEY_K;
 
@@ -1256,7 +1248,7 @@ void PE_update_object(const EvaluationContext *eval_ctx, Scene *scene, Object *o
 	/* use this to do partial particle updates, not usable when adding or
 	 * removing, then a full redo is necessary and calling this may crash */
 	ParticleEditSettings *pset= PE_settings(scene);
-	PTCacheEdit *edit = PE_get_current(scene, eval_ctx->view_layer, ob);
+	PTCacheEdit *edit = PE_get_current(scene, ob);
 	POINT_P;
 
 	if (!edit)
@@ -1396,7 +1388,7 @@ static int pe_select_all_exec(bContext *C, wmOperator *op)
 	EvaluationContext eval_ctx;
 	CTX_data_eval_ctx(C, &eval_ctx);
 	Object *ob= CTX_data_active_object(C);
-	PTCacheEdit *edit= PE_get_current(scene, eval_ctx.view_layer, ob);
+	PTCacheEdit *edit= PE_get_current(scene, ob);
 	POINT_P; KEY_K;
 	int action = RNA_enum_get(op->ptr, "action");
 
@@ -1448,9 +1440,8 @@ int PE_mouse_particles(bContext *C, const int mval[2], bool extend, bool deselec
 {
 	PEData data;
 	Scene *scene= CTX_data_scene(C);
-	ViewLayer *view_layer = CTX_data_view_layer(C);
 	Object *ob= CTX_data_active_object(C);
-	PTCacheEdit *edit= PE_get_current(scene, view_layer, ob);
+	PTCacheEdit *edit= PE_get_current(scene, ob);
 	POINT_P; KEY_K;
 
 	if (!PE_start_edit(edit))
@@ -1642,7 +1633,7 @@ static int select_random_exec(bContext *C, wmOperator *op)
 	data.select_action = SEL_SELECT;
 	scene = CTX_data_scene(C);
 	ob = CTX_data_active_object(C);
-	edit = PE_get_current(scene, data.eval_ctx.view_layer, ob);
+	edit = PE_get_current(scene, ob);
 
 	rng = BLI_rng_new_srandom(seed);
 
@@ -1759,9 +1750,8 @@ void PE_deselect_all_visible(PTCacheEdit *edit)
 int PE_border_select(bContext *C, rcti *rect, bool select, bool extend)
 {
 	Scene *scene= CTX_data_scene(C);
-	ViewLayer *view_layer = CTX_data_view_layer(C);
 	Object *ob= CTX_data_active_object(C);
-	PTCacheEdit *edit= PE_get_current(scene, view_layer, ob);
+	PTCacheEdit *edit= PE_get_current(scene, ob);
 	PEData data;
 
 	if (!PE_start_edit(edit))
@@ -1787,9 +1777,8 @@ int PE_border_select(bContext *C, rcti *rect, bool select, bool extend)
 int PE_circle_select(bContext *C, int selecting, const int mval[2], float rad)
 {
 	Scene *scene= CTX_data_scene(C);
-	ViewLayer *view_layer = CTX_data_view_layer(C);
 	Object *ob= CTX_data_active_object(C);
-	PTCacheEdit *edit= PE_get_current(scene, view_layer, ob);
+	PTCacheEdit *edit= PE_get_current(scene, ob);
 	PEData data;
 
 	if (!PE_start_edit(edit))
@@ -1813,11 +1802,10 @@ int PE_circle_select(bContext *C, int selecting, const int mval[2], float rad)
 int PE_lasso_select(bContext *C, const int mcords[][2], const short moves, bool extend, bool select)
 {
 	Scene *scene= CTX_data_scene(C);
-	ViewLayer *view_layer = CTX_data_view_layer(C);
 	Object *ob= CTX_data_active_object(C);
 	ARegion *ar= CTX_wm_region(C);
 	ParticleEditSettings *pset= PE_settings(scene);
-	PTCacheEdit *edit = PE_get_current(scene, view_layer, ob);
+	PTCacheEdit *edit = PE_get_current(scene, ob);
 	ParticleSystem *psys = edit->psys;
 	ParticleSystemModifierData *psmd = psys_get_modifier(ob, psys);
 	POINT_P; KEY_K;
@@ -1906,7 +1894,7 @@ static int hide_exec(bContext *C, wmOperator *op)
 	EvaluationContext eval_ctx;
 	CTX_data_eval_ctx(C, &eval_ctx);
 
-	PTCacheEdit *edit= PE_get_current(scene, eval_ctx.view_layer, ob);
+	PTCacheEdit *edit= PE_get_current(scene, ob);
 	POINT_P; KEY_K;
 
 
@@ -1961,7 +1949,7 @@ static int reveal_exec(bContext *C, wmOperator *op)
 	Scene *scene= CTX_data_scene(C);
 	EvaluationContext eval_ctx;
 	CTX_data_eval_ctx(C, &eval_ctx);
-	PTCacheEdit *edit= PE_get_current(scene, eval_ctx.view_layer, ob);
+	PTCacheEdit *edit= PE_get_current(scene, ob);
 	const bool select = RNA_boolean_get(op->ptr, "select");
 	POINT_P; KEY_K;
 
@@ -2228,9 +2216,9 @@ void PARTICLE_OT_rekey(wmOperatorType *ot)
 	RNA_def_int(ot->srna, "keys_number", 2, 2, INT_MAX, "Number of Keys", "", 2, 100);
 }
 
-static void rekey_particle_to_time(const bContext *C, Scene *scene, ViewLayer *view_layer, Object *ob, int pa_index, float path_time)
+static void rekey_particle_to_time(const bContext *C, Scene *scene, Object *ob, int pa_index, float path_time)
 {
-	PTCacheEdit *edit= PE_get_current(scene, view_layer, ob);
+	PTCacheEdit *edit= PE_get_current(scene, ob);
 	ParticleSystem *psys;
 	ParticleSimulationData sim = {0};
 	ParticleData *pa;
@@ -2570,9 +2558,8 @@ void PARTICLE_OT_subdivide(wmOperatorType *ot)
 static int remove_doubles_exec(bContext *C, wmOperator *op)
 {
 	Scene *scene= CTX_data_scene(C);
-	ViewLayer *view_layer = CTX_data_view_layer(C);
 	Object *ob= CTX_data_active_object(C);
-	PTCacheEdit *edit= PE_get_current(scene, view_layer, ob);
+	PTCacheEdit *edit= PE_get_current(scene, ob);
 	ParticleSystem *psys = edit->psys;
 	ParticleSystemModifierData *psmd;
 	KDTree *tree;
@@ -2663,10 +2650,9 @@ void PARTICLE_OT_remove_doubles(wmOperatorType *ot)
 static int weight_set_exec(bContext *C, wmOperator *op)
 {
 	Scene *scene= CTX_data_scene(C);
-	ViewLayer *view_layer = CTX_data_view_layer(C);
 	ParticleEditSettings *pset= PE_settings(scene);
 	Object *ob= CTX_data_active_object(C);
-	PTCacheEdit *edit= PE_get_current(scene, view_layer, ob);
+	PTCacheEdit *edit= PE_get_current(scene, ob);
 	ParticleSystem *psys = edit->psys;
 	POINT_P;
 	KEY_K;
@@ -2824,11 +2810,11 @@ void PARTICLE_OT_delete(wmOperatorType *ot)
 /*************************** mirror operator **************************/
 
 static void PE_mirror_x(
-        Scene *scene, ViewLayer *view_layer, Object *ob, int tagged)
+        Scene *scene, Object *ob, int tagged)
 {
 	Mesh *me= (Mesh *)(ob->data);
 	ParticleSystemModifierData *psmd;
-	PTCacheEdit *edit = PE_get_current(scene, view_layer, ob);
+	PTCacheEdit *edit = PE_get_current(scene, ob);
 	ParticleSystem *psys = edit->psys;
 	ParticleData *pa, *newpa, *new_pars;
 	PTCacheEditPoint *newpoint, *new_points;
@@ -2975,11 +2961,10 @@ static void PE_mirror_x(
 static int mirror_exec(bContext *C, wmOperator *UNUSED(op))
 {
 	Scene *scene= CTX_data_scene(C);
-	ViewLayer *view_layer = CTX_data_view_layer(C);
 	Object *ob= CTX_data_active_object(C);
-	PTCacheEdit *edit= PE_get_current(scene, view_layer, ob);
+	PTCacheEdit *edit= PE_get_current(scene, ob);
 	
-	PE_mirror_x(scene, view_layer, ob, 0);
+	PE_mirror_x(scene, ob, 0);
 
 	update_world_cos(ob, edit);
 	WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob);
@@ -3116,7 +3101,7 @@ static void brush_cut(PEData *data, int pa_index)
 			edit->points[pa_index].flag |= PEP_TAG;
 		}
 		else {
-			rekey_particle_to_time(data->context, data->scene, data->view_layer, ob, pa_index, cut_time);
+			rekey_particle_to_time(data->context, data->scene, ob, pa_index, cut_time);
 			edit->points[pa_index].flag |= PEP_EDIT_RECALC;
 		}
 	}
@@ -3771,7 +3756,7 @@ static int brush_edit_init(bContext *C, wmOperator *op)
 	ViewLayer *view_layer = CTX_data_view_layer(C);
 	Object *ob= CTX_data_active_object(C);
 	ParticleEditSettings *pset= PE_settings(scene);
-	PTCacheEdit *edit= PE_get_current(scene, view_layer, ob);
+	PTCacheEdit *edit= PE_get_current(scene, ob);
 	ARegion *ar= CTX_wm_region(C);
 	BrushEdit *bedit;
 	float min[3], max[3];
@@ -3805,7 +3790,6 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
 {
 	BrushEdit *bedit= op->customdata;
 	Scene *scene= bedit->scene;
-	ViewLayer *view_layer = bedit->view_layer;
 	Object *ob= bedit->ob;
 	PTCacheEdit *edit= bedit->edit;
 	ParticleEditSettings *pset= PE_settings(scene);
@@ -4000,7 +3984,7 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
 
 			if (ELEM(pset->brushtype, PE_BRUSH_ADD, PE_BRUSH_CUT) && (added || removed)) {
 				if (pset->brushtype == PE_BRUSH_ADD && pe_x_mirror(ob))
-					PE_mirror_x(scene, view_layer, ob, 1);
+					PE_mirror_x(scene, ob, 1);
 
 				update_world_cos(ob, edit);
 				psys_free_path_cache(NULL, edit);
@@ -4223,7 +4207,7 @@ static void shape_cut(PEData *data, int pa_index)
 			edit->points[pa_index].flag |= PEP_TAG;
 		}
 		else {
-			rekey_particle_to_time(data->context, data->scene, data->view_layer, ob, pa_index, cut_time);
+			rekey_particle_to_time(data->context, data->scene, ob, pa_index, cut_time);
 			edit->points[pa_index].flag |= PEP_EDIT_RECALC;
 		}
 	}
@@ -4232,10 +4216,9 @@ static void shape_cut(PEData *data, int pa_index)
 static int shape_cut_exec(bContext *C, wmOperator *UNUSED(op))
 {
 	Scene *scene = CTX_data_scene(C);
-	ViewLayer *view_layer = CTX_data_view_layer(C);
 	Object *ob = CTX_data_active_object(C);
 	ParticleEditSettings *pset = PE_settings(scene);
-	PTCacheEdit *edit = PE_get_current(scene, view_layer, ob);
+	PTCacheEdit *edit = PE_get_current(scene, ob);
 	Object *shapeob = pset->shape_object;
 	int selected = count_selected_keys(scene, edit);
 	int lock_root = pset->flag & PE_LOCK_FIRST;
@@ -4310,7 +4293,7 @@ void PARTICLE_OT_shape_cut(wmOperatorType *ot)
 int PE_minmax(Scene *scene, ViewLayer *view_layer, float min[3], float max[3])
 {
 	Object *ob= OBACT(view_layer);
-	PTCacheEdit *edit= PE_get_current(scene, view_layer, ob);
+	PTCacheEdit *edit= PE_get_current(scene, ob);
 	ParticleSystem *psys;
 	ParticleSystemModifierData *psmd = NULL;
 	POINT_P; KEY_K;
@@ -4348,7 +4331,7 @@ int PE_minmax(Scene *scene, ViewLayer *view_layer, float min[3], float max[3])
 
 /* initialize needed data for bake edit */
 void PE_create_particle_edit(
-        const EvaluationContext *eval_ctx, Scene *scene, ViewLayer *view_layer, Object *ob, PointCache *cache, ParticleSystem *psys)
+        const EvaluationContext *eval_ctx, Scene *scene, Object *ob, PointCache *cache, ParticleSystem *psys)
 {
 	PTCacheEdit *edit;
 	ParticleSystemModifierData *psmd = (psys) ? psys_get_modifier(ob, psys) : NULL;
@@ -4449,10 +4432,8 @@ void PE_create_particle_edit(
 		recalc_lengths(edit);
 		if (psys && !cache)
 			recalc_emitter_field(ob, psys);
-		PE_update_object(eval_ctx, scene, ob, 1);
 
-		PTCacheUndo_clear(edit);
-		PE_undo_push(scene, view_layer, "Original");
+		PE_update_object(eval_ctx, scene, ob, 1);
 	}
 }
 
@@ -4474,25 +4455,23 @@ static int particle_edit_toggle_poll(bContext *C)
 
 static int particle_edit_toggle_exec(bContext *C, wmOperator *op)
 {
-	wmWindowManager *wm = CTX_wm_manager(C);
-	struct WorkSpace *workspace = CTX_wm_workspace(C);
-	EvaluationContext eval_ctx;
-	CTX_data_eval_ctx(C, &eval_ctx);
 	Scene *scene = CTX_data_scene(C);
 	Object *ob = CTX_data_active_object(C);
 	const int mode_flag = OB_MODE_PARTICLE_EDIT;
-	const bool is_mode_set = (eval_ctx.object_mode & mode_flag) != 0;
+	const bool is_mode_set = (ob->mode & mode_flag) != 0;
 
 	if (!is_mode_set) {
-		if (!ED_object_mode_compat_set(C, workspace, mode_flag, op->reports)) {
+		if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) {
 			return OPERATOR_CANCELLED;
 		}
 	}
 
 	if (!is_mode_set) {
 		PTCacheEdit *edit;
+		EvaluationContext eval_ctx;
+		CTX_data_eval_ctx(C, &eval_ctx);
 
-		workspace->object_mode |= mode_flag;
+		ob->mode |= mode_flag;
 		edit= PE_create_current(&eval_ctx, scene, ob);
 	
 		/* mesh may have changed since last entering editmode.
@@ -4504,12 +4483,12 @@ static int particle_edit_toggle_exec(bContext *C, wmOperator *op)
 		WM_event_add_notifier(C, NC_SCENE|ND_MODE|NS_MODE_PARTICLE, NULL);
 	}
 	else {
-		workspace->object_mode &= ~mode_flag;
+		ob->mode &= ~mode_flag;
 		toggle_particle_cursor(C, 0);
 		WM_event_add_notifier(C, NC_SCENE|ND_MODE|NS_MODE_OBJECT, NULL);
 	}
 
-	ED_workspace_object_mode_sync_from_object(wm, workspace, ob);
+	// ED_workspace_object_mode_sync_from_object(wm, workspace, ob);
 
 	DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
 
@@ -4666,7 +4645,7 @@ static int unify_length_exec(bContext *C, wmOperator *UNUSED(op))
 	EvaluationContext eval_ctx;
 	CTX_data_eval_ctx(C, &eval_ctx);
 
-	PTCacheEdit *edit = PE_get_current(scene, eval_ctx.view_layer, ob);
+	PTCacheEdit *edit = PE_get_current(scene, ob);
 	float average_length = calculate_average_length(edit);
 
 	if (average_length == 0.0f) {
diff --git a/source/blender/editors/physics/particle_edit_undo.c b/source/blender/editors/physics/particle_edit_undo.c
index 45eb5923e577c207c0c5fd4cfad3388983f4f5fa..5199b1c54fac1359c6a07d861a36b133093a1d00 100644
--- a/source/blender/editors/physics/particle_edit_undo.c
+++ b/source/blender/editors/physics/particle_edit_undo.c
@@ -38,48 +38,38 @@
 
 #include "DNA_scene_types.h"
 #include "DNA_meshdata_types.h"
+#include "DNA_windowmanager_types.h"
 
 #include "BLI_listbase.h"
 #include "BLI_string.h"
 #include "BLI_utildefines.h"
 
-#include "BKE_global.h"
 #include "BKE_particle.h"
 #include "BKE_pointcache.h"
+#include "BKE_context.h"
+#include "BKE_undo_system.h"
 
 #include "DEG_depsgraph.h"
 
+#include "ED_object.h"
 #include "ED_particle.h"
+#include "ED_physics.h"
 
 #include "particle_edit_utildefines.h"
 
 #include "physics_intern.h"
 
-static void free_PTCacheUndo(PTCacheUndo *undo)
-{
-	PTCacheEditPoint *point;
-	int i;
+/* -------------------------------------------------------------------- */
+/** \name Undo Conversion
+ * \{ */
 
-	for (i=0, point=undo->points; i<undo->totpoint; i++, point++) {
-		if (undo->particles && (undo->particles + i)->hair)
-			MEM_freeN((undo->particles + i)->hair);
-		if (point->keys)
-			MEM_freeN(point->keys);
-	}
-	if (undo->points)
-		MEM_freeN(undo->points);
-
-	if (undo->particles)
-		MEM_freeN(undo->particles);
-
-	BKE_ptcache_free_mem(&undo->mem_cache);
-}
-
-static void make_PTCacheUndo(PTCacheEdit *edit, PTCacheUndo *undo)
+static void undoptcache_from_editcache(PTCacheUndo *undo, PTCacheEdit *edit)
 {
 	PTCacheEditPoint *point;
 	int i;
 
+	size_t mem_used_prev = MEM_get_memory_in_use();
+
 	undo->totpoint= edit->totpoint;
 
 	if (edit->psys) {
@@ -87,8 +77,9 @@ static void make_PTCacheUndo(PTCacheEdit *edit, PTCacheUndo *undo)
 
 		pa= undo->particles= MEM_dupallocN(edit->psys->particles);
 
-		for (i=0; i<edit->totpoint; i++, pa++)
+		for (i=0; i<edit->totpoint; i++, pa++) {
 			pa->hair= MEM_dupallocN(pa->hair);
+		}
 
 		undo->psys_flag = edit->psys->flag;
 	}
@@ -99,8 +90,9 @@ static void make_PTCacheUndo(PTCacheEdit *edit, PTCacheUndo *undo)
 		pm = undo->mem_cache.first;
 
 		for (; pm; pm=pm->next) {
-			for (i=0; i<BPHYS_TOT_DATA; i++)
+			for (i=0; i<BPHYS_TOT_DATA; i++) {
 				pm->data[i] = MEM_dupallocN(pm->data[i]);
+			}
 		}
 	}
 
@@ -111,9 +103,13 @@ static void make_PTCacheUndo(PTCacheEdit *edit, PTCacheUndo *undo)
 		point->keys= MEM_dupallocN(point->keys);
 		/* no need to update edit key->co & key->time pointers here */
 	}
+
+	size_t mem_used_curr = MEM_get_memory_in_use();
+
+	undo->undo_size = mem_used_prev < mem_used_curr ? mem_used_curr - mem_used_prev : sizeof(PTCacheUndo);
 }
 
-static void get_PTCacheUndo(PTCacheEdit *edit, PTCacheUndo *undo)
+static void undoptcache_to_editcache(PTCacheUndo *undo, PTCacheEdit *edit)
 {
 	ParticleSystem *psys = edit->psys;
 	ParticleData *pa;
@@ -121,16 +117,20 @@ static void get_PTCacheUndo(PTCacheEdit *edit, PTCacheUndo *undo)
 	POINT_P; KEY_K;
 
 	LOOP_POINTS {
-		if (psys && psys->particles[p].hair)
+		if (psys && psys->particles[p].hair) {
 			MEM_freeN(psys->particles[p].hair);
+		}
 
-		if (point->keys)
+		if (point->keys) {
 			MEM_freeN(point->keys);
+		}
 	}
-	if (psys && psys->particles)
+	if (psys && psys->particles) {
 		MEM_freeN(psys->particles);
-	if (edit->points)
+	}
+	if (edit->points) {
 		MEM_freeN(edit->points);
+	}
 	if (edit->mirror_cache) {
 		MEM_freeN(edit->mirror_cache);
 		edit->mirror_cache= NULL;
@@ -172,9 +172,9 @@ static void get_PTCacheUndo(PTCacheEdit *edit, PTCacheUndo *undo)
 		pm = edit->pid.cache->mem_cache.first;
 
 		for (; pm; pm=pm->next) {
-			for (i=0; i<BPHYS_TOT_DATA; i++)
+			for (i = 0; i < BPHYS_TOT_DATA; i++) {
 				pm->data[i] = MEM_dupallocN(pm->data[i]);
-
+			}
 			BKE_ptcache_mem_pointers_init(pm);
 
 			LOOP_POINTS {
@@ -192,150 +192,110 @@ static void get_PTCacheUndo(PTCacheEdit *edit, PTCacheUndo *undo)
 	}
 }
 
-void PE_undo_push(Scene *scene, ViewLayer *view_layer, const char *str)
+static void undoptcache_free_data(PTCacheUndo *undo)
 {
-	PTCacheEdit *edit= PE_get_current(scene, view_layer, OBACT(view_layer));
-	PTCacheUndo *undo;
-	int nr;
-
-	if (!edit) return;
-
-	/* remove all undos after (also when curundo==NULL) */
-	while (edit->undo.last != edit->curundo) {
-		undo= edit->undo.last;
-		BLI_remlink(&edit->undo, undo);
-		free_PTCacheUndo(undo);
-		MEM_freeN(undo);
-	}
+	PTCacheEditPoint *point;
+	int i;
 
-	/* make new */
-	edit->curundo= undo= MEM_callocN(sizeof(PTCacheUndo), "particle undo file");
-	BLI_strncpy(undo->name, str, sizeof(undo->name));
-	BLI_addtail(&edit->undo, undo);
-
-	/* and limit amount to the maximum */
-	nr= 0;
-	undo= edit->undo.last;
-	while (undo) {
-		nr++;
-		if (nr==U.undosteps) break;
-		undo= undo->prev;
-	}
-	if (undo) {
-		while (edit->undo.first != undo) {
-			PTCacheUndo *first= edit->undo.first;
-			BLI_remlink(&edit->undo, first);
-			free_PTCacheUndo(first);
-			MEM_freeN(first);
+	for (i = 0, point=undo->points; i < undo->totpoint; i++, point++) {
+		if (undo->particles && (undo->particles + i)->hair) {
+			MEM_freeN((undo->particles + i)->hair);
+		}
+		if (point->keys) {
+			MEM_freeN(point->keys);
 		}
 	}
-
-	/* copy  */
-	make_PTCacheUndo(edit, edit->curundo);
+	if (undo->points) {
+		MEM_freeN(undo->points);
+	}
+	if (undo->particles) {
+		MEM_freeN(undo->particles);
+	}
+	BKE_ptcache_free_mem(&undo->mem_cache);
 }
 
-void PE_undo_step(Scene *scene, ViewLayer *view_layer, int step)
-{
-	PTCacheEdit *edit= PE_get_current(scene, view_layer, OBACT(view_layer));
+/** \} */
 
-	if (!edit) return;
+/* -------------------------------------------------------------------- */
+/** \name Implements ED Undo System
+ * \{ */
 
-	if (step==0) {
-		get_PTCacheUndo(edit, edit->curundo);
-	}
-	else if (step==1) {
+typedef struct ParticleUndoStep {
+	UndoStep step;
+	UndoRefID_Scene scene_ref;
+	UndoRefID_Object object_ref;
+	PTCacheUndo data;
+} ParticleUndoStep;
 
-		if (edit->curundo==NULL || edit->curundo->prev==NULL) {
-			/* pass */
-		}
-		else {
-			if (G.debug & G_DEBUG) printf("undo %s\n", edit->curundo->name);
-			edit->curundo= edit->curundo->prev;
-			get_PTCacheUndo(edit, edit->curundo);
-		}
-	}
-	else {
-		/* curundo has to remain current situation! */
-
-		if (edit->curundo==NULL || edit->curundo->next==NULL) {
-			/* pass */
-		}
-		else {
-			get_PTCacheUndo(edit, edit->curundo->next);
-			edit->curundo= edit->curundo->next;
-			if (G.debug & G_DEBUG) printf("redo %s\n", edit->curundo->name);
-		}
-	}
-
-	DEG_id_tag_update(&OBACT(view_layer)->id, OB_RECALC_DATA);
+static bool particle_undosys_poll(struct bContext *C)
+{
+	Scene *scene = CTX_data_scene(C);
+	ViewLayer *view_layer = CTX_data_view_layer(C);
+	Object *ob = OBACT(view_layer);
+	PTCacheEdit *edit = PE_get_current(scene, ob);
+	
+	return (edit != NULL);
 }
 
-bool PE_undo_is_valid(Scene *scene, ViewLayer *view_layer)
+static bool particle_undosys_step_encode(struct bContext *C, UndoStep *us_p)
 {
-	PTCacheEdit *edit= PE_get_current(scene, view_layer, OBACT(view_layer));
-
-	if (edit) {
-		return (edit->undo.last != edit->undo.first);
-	}
-	return 0;
+	ParticleUndoStep *us = (ParticleUndoStep *)us_p;
+	ViewLayer *view_layer = CTX_data_view_layer(C);
+	us->scene_ref.ptr = CTX_data_scene(C);
+	us->object_ref.ptr = OBACT(view_layer);
+	PTCacheEdit *edit = PE_get_current(us->scene_ref.ptr, us->object_ref.ptr);
+	undoptcache_from_editcache(&us->data, edit);
+	return true;
 }
 
-void PTCacheUndo_clear(PTCacheEdit *edit)
+static void particle_undosys_step_decode(struct bContext *C, UndoStep *us_p, int UNUSED(dir))
 {
-	PTCacheUndo *undo;
-
-	if (edit==NULL) return;
-
-	undo= edit->undo.first;
-	while (undo) {
-		free_PTCacheUndo(undo);
-		undo= undo->next;
+	/* TODO(campbell): undo_system: use low-level API to set mode. */
+	ED_object_mode_set(C, OB_MODE_PARTICLE_EDIT);
+	BLI_assert(particle_undosys_poll(C));
+
+	ParticleUndoStep *us = (ParticleUndoStep *)us_p;
+	Scene *scene = us->scene_ref.ptr;
+	Object *ob = us->object_ref.ptr;
+	PTCacheEdit *edit = PE_get_current(scene, ob);
+	if (edit) {
+		undoptcache_to_editcache(&us->data, edit);
+		DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
+	}
+	else {
+		BLI_assert(0);
 	}
-	BLI_freelistN(&edit->undo);
-	edit->curundo= NULL;
 }
 
-void PE_undo(Scene *scene, ViewLayer *view_layer)
+static void particle_undosys_step_free(UndoStep *us_p)
 {
-	PE_undo_step(scene, view_layer, 1);
+	ParticleUndoStep *us = (ParticleUndoStep *)us_p;
+	undoptcache_free_data(&us->data);
 }
 
-void PE_redo(Scene *scene, ViewLayer *view_layer)
+static void particle_undosys_foreach_ID_ref(
+        UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
 {
-	PE_undo_step(scene, view_layer, -1);
+	ParticleUndoStep *us = (ParticleUndoStep *)us_p;
+	foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->scene_ref));
+	foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->object_ref));
 }
 
-void PE_undo_number(Scene *scene, ViewLayer *view_layer, int nr)
+/* Export for ED_undo_sys. */
+void ED_particle_undosys_type(UndoType *ut)
 {
-	PTCacheEdit *edit= PE_get_current(scene, view_layer, OBACT(view_layer));
-	PTCacheUndo *undo;
-	int a=0;
-
-	for (undo= edit->undo.first; undo; undo= undo->next, a++) {
-		if (a==nr) break;
-	}
-	edit->curundo= undo;
-	PE_undo_step(scene, view_layer, 0);
-}
-
+	ut->name = "Edit Particle";
+	ut->poll = particle_undosys_poll;
+	ut->step_encode = particle_undosys_step_encode;
+	ut->step_decode = particle_undosys_step_decode;
+	ut->step_free = particle_undosys_step_free;
 
-/* get name of undo item, return null if no item with this index */
-/* if active pointer, set it to 1 if true */
-const char *PE_undo_get_name(Scene *scene, ViewLayer *view_layer, int nr, bool *r_active)
-{
-	PTCacheEdit *edit= PE_get_current(scene, view_layer, OBACT(view_layer));
-	PTCacheUndo *undo;
+	ut->step_foreach_ID_ref = particle_undosys_foreach_ID_ref;
 
-	if (r_active) *r_active = false;
+	ut->mode = BKE_UNDOTYPE_MODE_STORE;
+	ut->use_context = true;
 
-	if (edit) {
-		undo= BLI_findlink(&edit->undo, nr);
-		if (undo) {
-			if (r_active && (undo == edit->curundo)) {
-				*r_active = true;
-			}
-			return undo->name;
-		}
-	}
-	return NULL;
+	ut->step_size = sizeof(ParticleUndoStep);
 }
+
+/** \} */
diff --git a/source/blender/editors/physics/particle_object.c b/source/blender/editors/physics/particle_object.c
index cbdb6e4592c0c76a64fbb92d38b519600778c4f2..1fae5b70aff4920056fffbe978657fc03ef7becf 100644
--- a/source/blender/editors/physics/particle_object.c
+++ b/source/blender/editors/physics/particle_object.c
@@ -110,25 +110,23 @@ void OBJECT_OT_particle_system_add(wmOperatorType *ot)
 
 static int particle_system_remove_exec(bContext *C, wmOperator *UNUSED(op))
 {
-	WorkSpace *workspace = CTX_wm_workspace(C);
 	Object *ob = ED_object_context(C);
 	Scene *scene = CTX_data_scene(C);
 	ViewLayer *view_layer = CTX_data_view_layer(C);
-	eObjectMode mode_orig;
+	int mode_orig;
 
 	if (!scene || !ob)
 		return OPERATOR_CANCELLED;
 
-	mode_orig = workspace->object_mode;
+	mode_orig = ob->mode;
 	object_remove_particle_system(scene, ob);
 
 	/* possible this isn't the active object
 	 * object_remove_particle_system() clears the mode on the last psys
 	 */
 	if (mode_orig & OB_MODE_PARTICLE_EDIT) {
-		if ((workspace->object_mode & OB_MODE_PARTICLE_EDIT) == 0) {
+		if ((ob->mode & OB_MODE_PARTICLE_EDIT) == 0) {
 			if (view_layer->basact && view_layer->basact->object == ob) {
-				workspace->object_mode &= ~OB_MODE_PARTICLE_EDIT;
 				WM_event_add_notifier(C, NC_SCENE|ND_MODE|NS_MODE_OBJECT, NULL);
 			}
 		}
@@ -548,7 +546,7 @@ void PARTICLE_OT_dupliob_move_down(wmOperatorType *ot)
 /************************ connect/disconnect hair operators *********************/
 
 static void disconnect_hair(
-        const EvaluationContext *eval_ctx, Scene *scene, ViewLayer *view_layer,
+        const EvaluationContext *eval_ctx, Scene *scene,
         Object *ob, ParticleSystem *psys)
 {
 	ParticleSystemModifierData *psmd = psys_get_modifier(ob, psys);
@@ -601,7 +599,6 @@ static void disconnect_hair(
 static int disconnect_hair_exec(bContext *C, wmOperator *op)
 {
 	Scene *scene= CTX_data_scene(C);
-	ViewLayer *view_layer = CTX_data_view_layer(C);
 	Object *ob= ED_object_context(C);
 	ParticleSystem *psys= NULL;
 	const bool all = RNA_boolean_get(op->ptr, "all");
@@ -614,12 +611,12 @@ static int disconnect_hair_exec(bContext *C, wmOperator *op)
 
 	if (all) {
 		for (psys=ob->particlesystem.first; psys; psys=psys->next) {
-			disconnect_hair(&eval_ctx, scene, view_layer, ob, psys);
+			disconnect_hair(&eval_ctx, scene, ob, psys);
 		}
 	}
 	else {
 		psys = psys_get_current(ob);
-		disconnect_hair(&eval_ctx, scene, view_layer, ob, psys);
+		disconnect_hair(&eval_ctx, scene, ob, psys);
 	}
 
 	DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
@@ -646,7 +643,7 @@ void PARTICLE_OT_disconnect_hair(wmOperatorType *ot)
  * from/to_mat : additional transform for from/to particles (e.g. for using object space copying)
  */
 static bool remap_hair_emitter(
-        const EvaluationContext *eval_ctx, Scene *scene, ViewLayer *view_layer, Object *ob, ParticleSystem *psys,
+        const EvaluationContext *eval_ctx, Scene *scene, Object *ob, ParticleSystem *psys,
         Object *target_ob, ParticleSystem *target_psys, PTCacheEdit *target_edit,
         float from_mat[4][4], float to_mat[4][4], bool from_global, bool to_global)
 {
@@ -838,7 +835,7 @@ static bool remap_hair_emitter(
 }
 
 static bool connect_hair(
-        const EvaluationContext *eval_ctx, Scene *scene, ViewLayer *view_layer,
+        const EvaluationContext *eval_ctx, Scene *scene,
         Object *ob, ParticleSystem *psys)
 {
 	bool ok;
@@ -847,7 +844,7 @@ static bool connect_hair(
 		return false;
 	
 	ok = remap_hair_emitter(
-	         eval_ctx, scene, view_layer, ob, psys, ob, psys, psys->edit,
+	         eval_ctx, scene, ob, psys, ob, psys, psys->edit,
 	         ob->obmat, ob->obmat, psys->flag & PSYS_GLOBAL_HAIR, false);
 	psys->flag &= ~PSYS_GLOBAL_HAIR;
 	
@@ -858,7 +855,6 @@ static int connect_hair_exec(bContext *C, wmOperator *op)
 {
 	EvaluationContext eval_ctx;
 	Scene *scene= CTX_data_scene(C);
-	ViewLayer *view_layer = CTX_data_view_layer(C);
 	Object *ob= ED_object_context(C);
 	ParticleSystem *psys= NULL;
 	const bool all = RNA_boolean_get(op->ptr, "all");
@@ -871,12 +867,12 @@ static int connect_hair_exec(bContext *C, wmOperator *op)
 
 	if (all) {
 		for (psys=ob->particlesystem.first; psys; psys=psys->next) {
-			any_connected |= connect_hair(&eval_ctx, scene, view_layer, ob, psys);
+			any_connected |= connect_hair(&eval_ctx, scene, ob, psys);
 		}
 	}
 	else {
 		psys = psys_get_current(ob);
-		any_connected |= connect_hair(&eval_ctx, scene, view_layer, ob, psys);
+		any_connected |= connect_hair(&eval_ctx, scene, ob, psys);
 	}
 
 	if (!any_connected) {
@@ -913,7 +909,7 @@ typedef enum eCopyParticlesSpace {
 } eCopyParticlesSpace;
 
 static void copy_particle_edit(
-        const EvaluationContext *eval_ctx, Scene *scene, ViewLayer *view_layer,
+        const EvaluationContext *eval_ctx, Scene *scene,
         Object *ob, ParticleSystem *psys, ParticleSystem *psys_from)
 {
 	PTCacheEdit *edit_from = psys_from->edit, *edit;
@@ -933,10 +929,7 @@ static void copy_particle_edit(
 	
 	edit->emitter_field = NULL;
 	edit->emitter_cosnos = NULL;
-	
-	BLI_listbase_clear(&edit->undo);
-	edit->curundo = NULL;
-	
+
 	edit->points = MEM_dupallocN(edit_from->points);
 	pa = psys->particles;
 	LOOP_POINTS {
@@ -965,9 +958,6 @@ static void copy_particle_edit(
 	recalc_lengths(edit);
 	recalc_emitter_field(ob, psys);
 	PE_update_object(eval_ctx, scene, ob, true);
-	
-	PTCacheUndo_clear(edit);
-	PE_undo_push(scene, view_layer, "Original");
 }
 
 static void remove_particle_systems_from_object(Object *ob_to)
@@ -997,7 +987,6 @@ static void remove_particle_systems_from_object(Object *ob_to)
 /* single_psys_from is optional, if NULL all psys of ob_from are copied */
 static bool copy_particle_systems_to_object(const bContext *C,
                                             Scene *scene,
-                                            ViewLayer *view_layer,
                                             Object *ob_from,
                                             ParticleSystem *single_psys_from,
                                             Object *ob_to,
@@ -1082,7 +1071,7 @@ static bool copy_particle_systems_to_object(const bContext *C,
 		DM_ensure_tessface(psmd->dm_final);
 		
 		if (psys_from->edit) {
-			copy_particle_edit(&eval_ctx, scene, view_layer, ob_to, psys, psys_from);
+			copy_particle_edit(&eval_ctx, scene, ob_to, psys, psys_from);
 		}
 
 		if (duplicate_settings) {
@@ -1118,7 +1107,7 @@ static bool copy_particle_systems_to_object(const bContext *C,
 		}
 		if (ob_from != ob_to) {
 			remap_hair_emitter(
-			        &eval_ctx, scene, view_layer, ob_from, psys_from, ob_to, psys, psys->edit,
+			        &eval_ctx, scene, ob_from, psys_from, ob_to, psys, psys->edit,
 			        from_mat, to_mat, psys_from->flag & PSYS_GLOBAL_HAIR, psys->flag & PSYS_GLOBAL_HAIR);
 		}
 		
@@ -1153,7 +1142,6 @@ static int copy_particle_systems_exec(bContext *C, wmOperator *op)
 	const bool remove_target_particles = RNA_boolean_get(op->ptr, "remove_target_particles");
 	const bool use_active = RNA_boolean_get(op->ptr, "use_active");
 	Scene *scene = CTX_data_scene(C);
-	ViewLayer *view_layer = CTX_data_view_layer(C);
 	Object *ob_from = ED_object_active_context(C);
 	ParticleSystem *psys_from = use_active ? CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem).data : NULL;
 	
@@ -1168,7 +1156,7 @@ static int copy_particle_systems_exec(bContext *C, wmOperator *op)
 				remove_particle_systems_from_object(ob_to);
 				changed = true;
 			}
-			if (copy_particle_systems_to_object(C, scene, view_layer, ob_from, psys_from, ob_to, space, false))
+			if (copy_particle_systems_to_object(C, scene, ob_from, psys_from, ob_to, space, false))
 				changed = true;
 			else
 				fail++;
@@ -1229,7 +1217,7 @@ static int duplicate_particle_systems_exec(bContext *C, wmOperator *op)
 	Scene *scene = CTX_data_scene(C);
 	Object *ob = ED_object_active_context(C);
 	ParticleSystem *psys = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem).data;
-	copy_particle_systems_to_object(C, scene, CTX_data_view_layer(C), ob, psys, ob,
+	copy_particle_systems_to_object(C, scene, ob, psys, ob,
 	                                PAR_COPY_SPACE_OBJECT, duplicate_settings);
 	return OPERATOR_FINISHED;
 }
diff --git a/source/blender/editors/physics/physics_fluid.c b/source/blender/editors/physics/physics_fluid.c
index d9a7d288f9ce1a3a155c7aed73159e7a43de7614..65ef3ad44d09fc266df1acc8cd339b3fe3efc537 100644
--- a/source/blender/editors/physics/physics_fluid.c
+++ b/source/blender/editors/physics/physics_fluid.c
@@ -48,8 +48,6 @@
 #include "BKE_context.h"
 #include "BKE_customdata.h"
 #include "BKE_fluidsim.h"
-#include "BKE_global.h"
-#include "BKE_main.h"
 #include "BKE_modifier.h"
 #include "BKE_object.h"
 #include "BKE_report.h"
@@ -69,6 +67,8 @@
 /* enable/disable overall compilation */
 #ifdef WITH_MOD_FLUID
 
+#include "BKE_global.h"
+
 #include "WM_api.h"
 
 #include "DNA_scene_types.h"
diff --git a/source/blender/editors/physics/physics_intern.h b/source/blender/editors/physics/physics_intern.h
index 8888589b5d797f00897b9cea801e655e7f0a77ed..246bf79360f1c522ebb3a15be2ef869038056b89 100644
--- a/source/blender/editors/physics/physics_intern.h
+++ b/source/blender/editors/physics/physics_intern.h
@@ -70,9 +70,8 @@ void PARTICLE_OT_edited_clear(struct wmOperatorType *ot);
 
 void PARTICLE_OT_unify_length(struct wmOperatorType *ot);
 
-void PTCacheUndo_clear(struct PTCacheEdit *edit);
 void PE_create_particle_edit(
-        const struct EvaluationContext *eval_ctx, struct Scene *scene, struct ViewLayer *view_layer,
+        const struct EvaluationContext *eval_ctx, struct Scene *scene,
         struct Object *ob, struct PointCache *cache, struct ParticleSystem *psys);
 void recalc_lengths(struct PTCacheEdit *edit);
 void recalc_emitter_field(struct Object *ob, struct ParticleSystem *psys);
diff --git a/source/blender/editors/physics/rigidbody_constraint.c b/source/blender/editors/physics/rigidbody_constraint.c
index f77e164ba160e98b9fcb81dde60dd202aad452e3..3bcc047bf5ba0e3c0e44f2d4577a7486bd93b160 100644
--- a/source/blender/editors/physics/rigidbody_constraint.c
+++ b/source/blender/editors/physics/rigidbody_constraint.c
@@ -38,7 +38,6 @@
 #include "DNA_scene_types.h"
 
 #include "BKE_context.h"
-#include "BKE_global.h"
 #include "BKE_group.h"
 #include "BKE_main.h"
 #include "BKE_report.h"
diff --git a/source/blender/editors/physics/rigidbody_object.c b/source/blender/editors/physics/rigidbody_object.c
index 3b667520550003930d45586026a3be2246a78cc0..3553ffa5033a895f2fc11d467341247a295df15c 100644
--- a/source/blender/editors/physics/rigidbody_object.c
+++ b/source/blender/editors/physics/rigidbody_object.c
@@ -43,7 +43,6 @@
 #include "BLT_translation.h"
 
 #include "BKE_context.h"
-#include "BKE_global.h"
 #include "BKE_group.h"
 #include "BKE_main.h"
 #include "BKE_report.h"
diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c
index 33ca6ea74954500f886f78a4a5b968cf6c24b12d..f8b1861b204bb9f8908d92997573b8ef21e28db0 100644
--- a/source/blender/editors/render/render_internal.c
+++ b/source/blender/editors/render/render_internal.c
@@ -65,6 +65,7 @@
 #include "BKE_sequencer.h"
 #include "BKE_screen.h"
 #include "BKE_scene.h"
+#include "BKE_undo_system.h"
 #include "BKE_workspace.h"
 
 #include "DEG_depsgraph.h"
@@ -76,6 +77,7 @@
 #include "ED_render.h"
 #include "ED_screen.h"
 #include "ED_util.h"
+#include "ED_undo.h"
 #include "ED_view3d.h"
 
 #include "RE_pipeline.h"
@@ -92,6 +94,7 @@
 #include "RNA_access.h"
 #include "RNA_define.h"
 
+#include "BLO_undofile.h"
 
 #include "render_intern.h"
 
@@ -640,7 +643,7 @@ static void render_image_restore_layer(RenderJob *rj)
 							/* For single layer renders keep the active layer
 							 * visible, or show the compositing result. */
 							RenderResult *rr = RE_AcquireResultRead(rj->re);
-							if(RE_HasCombinedLayer(rr)) {
+							if (RE_HasCombinedLayer(rr)) {
 								sima->iuser.layer = 0;
 							}
 							RE_ReleaseResult(rj->re);
@@ -901,7 +904,8 @@ static int screen_render_invoke(bContext *C, wmOperator *op, const wmEvent *even
 	/* get main */
 	if (G.debug_value == 101) {
 		/* thread-safety experiment, copy main from the undo buffer */
-		mainp = BKE_undo_get_main(&scene);
+		struct MemFile *memfile = ED_undosys_stack_memfile_get_active(CTX_wm_manager(C)->undo_stack);
+		mainp = BLO_memfile_main_get(memfile, CTX_data_main(C), &scene);
 	}
 	else
 		mainp = CTX_data_main(C);
diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c
index 1f9894b3b9f109647f62268ef6557c4a04c3e923..47e4fc5135139879690a6d4844ee6a3c624abaec 100644
--- a/source/blender/editors/render/render_opengl.c
+++ b/source/blender/editors/render/render_opengl.c
@@ -74,7 +74,6 @@
 #include "RNA_access.h"
 #include "RNA_define.h"
 
-#include "GPU_compositing.h"
 #include "GPU_framebuffer.h"
 #include "GPU_glew.h"
 #include "GPU_matrix.h"
@@ -122,7 +121,6 @@ typedef struct OGLRender {
 	GPUOffScreen *ofs;
 	int ofs_samples;
 	bool ofs_full_samples;
-	GPUFX *fx;
 	int sizex, sizey;
 	int write_still;
 
@@ -365,7 +363,7 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R
 			ibuf_view = ED_view3d_draw_offscreen_imbuf(
 			       &eval_ctx, scene, view_layer, v3d, ar, sizex, sizey,
 			       IB_rectfloat, draw_flags, alpha_mode, oglrender->ofs_samples, viewname,
-			       oglrender->fx, oglrender->ofs, err_out);
+			       oglrender->ofs, err_out);
 
 			/* for stamp only */
 			if (rv3d->persp == RV3D_CAMOB && v3d->camera) {
@@ -378,7 +376,7 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R
 			        &eval_ctx, scene, view_layer, scene->camera, oglrender->sizex, oglrender->sizey,
 			        IB_rectfloat, draw_flags, OB_SOLID,
 			        alpha_mode, oglrender->ofs_samples, viewname,
-			        oglrender->fx, oglrender->ofs, err_out);
+			        oglrender->ofs, err_out);
 			camera = scene->camera;
 		}
 
@@ -545,7 +543,6 @@ static void screen_opengl_render_apply(const bContext *C, OGLRender *oglrender)
 		for (view_id = 0; view_id < oglrender->views_len; view_id++) {
 			context.view_id = view_id;
 			context.gpu_offscreen = oglrender->ofs;
-			context.gpu_fx = oglrender->fx;
 			context.gpu_full_samples = oglrender->ofs_full_samples;
 
 			oglrender->seq_data.ibufs_arr[view_id] = BKE_sequencer_give_ibuf(&context, CFRA, chanshown);
@@ -706,19 +703,6 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op)
 		/* apply immediately in case we're rendering from a script,
 		 * running notifiers again will overwrite */
 		oglrender->scene->customdata_mask |= oglrender->scene->customdata_mask_modal;
-
-		if (oglrender->v3d->fx_settings.fx_flag & (GPU_FX_FLAG_DOF | GPU_FX_FLAG_SSAO)) {
-			oglrender->fx = GPU_fx_compositor_create();
-		}
-	}
-	else if (is_sequencer) {
-		/* NOTE: We allow animation of DoF setting for flexibility in edits, so
-		 * we can't check in advance whether we need FX compositor or not.
-		 * We just always allocated it and make sure it doesn't add extra
-		 * overhead rather than memory allocation here if it's not really
-		 * needed.
-		 */
-		oglrender->fx = GPU_fx_compositor_create();
 	}
 
 	/* create render */
@@ -844,9 +828,6 @@ static void screen_opengl_render_end(bContext *C, OGLRender *oglrender)
 
 	WM_event_add_notifier(C, NC_SCENE | ND_RENDER_RESULT, oglrender->scene);
 
-	if (oglrender->fx)
-		GPU_fx_compositor_destroy(oglrender->fx);
-
 	DRW_opengl_context_enable();
 	GPU_offscreen_free(oglrender->ofs);
 	DRW_opengl_context_disable();
diff --git a/source/blender/editors/render/render_shading.c b/source/blender/editors/render/render_shading.c
index 222e0e20dff10d4d22db56811fa660ec4bae4e3d..270ba2a7947abedf8fb3c808990945909fbb6d84 100644
--- a/source/blender/editors/render/render_shading.c
+++ b/source/blender/editors/render/render_shading.c
@@ -99,7 +99,6 @@
 
 static int material_slot_add_exec(bContext *C, wmOperator *UNUSED(op))
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Object *ob = ED_object_context(C);
 
 	if (!ob)
@@ -107,7 +106,7 @@ static int material_slot_add_exec(bContext *C, wmOperator *UNUSED(op))
 	
 	BKE_object_material_slot_add(ob);
 
-	if (workspace->object_mode & OB_MODE_TEXTURE_PAINT) {
+	if (ob->mode & OB_MODE_TEXTURE_PAINT) {
 		Scene *scene = CTX_data_scene(C);
 		BKE_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL);
 		WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL);
@@ -137,7 +136,6 @@ void OBJECT_OT_material_slot_add(wmOperatorType *ot)
 
 static int material_slot_remove_exec(bContext *C, wmOperator *op)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Object *ob = ED_object_context(C);
 
 	if (!ob)
@@ -151,7 +149,7 @@ static int material_slot_remove_exec(bContext *C, wmOperator *op)
 	
 	BKE_object_material_slot_remove(ob);
 
-	if (workspace->object_mode & OB_MODE_TEXTURE_PAINT) {
+	if (ob->mode & OB_MODE_TEXTURE_PAINT) {
 		Scene *scene = CTX_data_scene(C);
 		BKE_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL);
 		WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL);
diff --git a/source/blender/editors/render/render_update.c b/source/blender/editors/render/render_update.c
index 7b2fcc156493b063bfdcffc79476ad064d39c245..d29985041c61101e263bf5c5fe5a976f761702a7 100644
--- a/source/blender/editors/render/render_update.c
+++ b/source/blender/editors/render/render_update.c
@@ -401,12 +401,10 @@ static void texture_changed(Main *bmain, Tex *tex)
 	/* icons */
 	BKE_icon_changed(BKE_icon_id_ensure(&tex->id));
 
-	const eObjectMode object_mode = WM_windows_object_mode_get(bmain->wm.first);
-
 	/* paint overlays */
 	for (scene = bmain->scene.first; scene; scene = scene->id.next) {
 		for (view_layer = scene->view_layers.first; view_layer; view_layer = view_layer->next) {
-			BKE_paint_invalidate_overlay_tex(scene, view_layer, tex, object_mode);
+			BKE_paint_invalidate_overlay_tex(scene, view_layer, tex);
 		}
 	}
 
@@ -526,18 +524,8 @@ static void scene_changed(Main *bmain, Scene *scene)
 	Object *ob;
 
 	/* glsl */
-	bool has_texture_mode = false;
-	wmWindowManager *wm = bmain->wm.first;
-	for (wmWindow *win = wm->windows.first; win; win = win->next) {
-		WorkSpace *workspace = WM_window_get_active_workspace(win);
-		if (workspace->object_mode & OB_MODE_TEXTURE_PAINT) {
-			has_texture_mode = true;
-			break;
-		}
-	}
-
-	if (has_texture_mode) {
-		for (ob = bmain->object.first; ob; ob = ob->id.next) {
+	for (ob = bmain->object.first; ob; ob = ob->id.next) {
+		if (ob->mode & OB_MODE_TEXTURE_PAINT) {
 			BKE_texpaint_slots_refresh_object(scene, ob);
 			BKE_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL);
 			GPU_drawobject_free(ob->derivedFinal);
@@ -570,10 +558,8 @@ void ED_render_id_flush_update(const DEGEditorUpdateContext *update_ctx, ID *id)
 			lamp_changed(bmain, (Lamp *)id);
 			break;
 		case ID_IM:
-		{
 			image_changed(bmain, (Image *)id);
 			break;
-		}
 		case ID_SCE:
 			scene_changed(bmain, (Scene *)id);
 			render_engine_flag_changed(bmain, RE_ENGINE_UPDATE_OTHER);
diff --git a/source/blender/editors/scene/scene_edit.c b/source/blender/editors/scene/scene_edit.c
index da720272f674e9fd26511fda78fa082668d4d999..76abc1489fd1be09458e68b5da2a738e53820498 100644
--- a/source/blender/editors/scene/scene_edit.c
+++ b/source/blender/editors/scene/scene_edit.c
@@ -122,21 +122,23 @@ static ViewLayer *scene_change_get_new_view_layer(const WorkSpace *workspace, co
 
 void ED_scene_change_update(
         Main *bmain, bContext *C,
-        wmWindow *win, const bScreen *screen, Scene *scene_old, Scene *scene_new)
+        wmWindow *win, const bScreen *screen, Scene *UNUSED(scene_old), Scene *scene_new)
 {
 	WorkSpace *workspace = CTX_wm_workspace(C);
 	ViewLayer *layer_new = scene_change_get_new_view_layer(workspace, scene_new);
 	Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene_new, layer_new, true);
 	Object *obact_new = OBACT(layer_new);
+	UNUSED_VARS(obact_new);
 
+#if 0
 	/* mode syncing */
 	EvaluationContext eval_ctx_old;
 	CTX_data_eval_ctx(C, &eval_ctx_old);
 	eObjectMode object_mode_old = workspace->object_mode;
 	ViewLayer *layer_old = BKE_view_layer_from_workspace_get(scene_old, workspace);
 	Object *obact_old = OBACT(layer_old);
-	bool obact_new_mode_exists = ED_object_mode_generic_exists(bmain->wm.first, obact_new, workspace->object_mode);
-
+	UNUSED_VARS(obact_old, object_mode_old);
+#endif
 
 	win->scene = scene_new;
 	CTX_data_scene_set(C, scene_new);
@@ -145,19 +147,6 @@ void ED_scene_change_update(
 	DEG_graph_relations_update(depsgraph, bmain, scene_new, layer_new);
 	DEG_on_visible_update(bmain, false);
 
-	if (obact_new == obact_old) {
-		/* pass */
-	}
-	else {
-		ED_object_mode_generic_exit_or_other_window(&eval_ctx_old, bmain->wm.first, workspace, scene_old, obact_old);
-		if (obact_new_mode_exists) {
-			workspace->object_mode = object_mode_old;
-		}
-		else {
-			ED_object_mode_generic_enter_or_other_window(C, win, object_mode_old);
-		}
-	}
-
 	ED_screen_update_after_scene_change(screen, scene_new, layer_new);
 	ED_render_engine_changed(bmain);
 	ED_update_for_newframe(bmain, scene_new, layer_new, depsgraph);
diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c
index ac577375a31ba6b4318101cf55b4f9a29838bdf0..390098b3ea38017bb1f0395d89b4953253ae424b 100644
--- a/source/blender/editors/screen/area.c
+++ b/source/blender/editors/screen/area.c
@@ -66,6 +66,7 @@
 
 #include "IMB_imbuf.h"
 #include "IMB_imbuf_types.h"
+#include "IMB_metadata.h"
 
 #include "UI_interface.h"
 #include "UI_interface_icons.h"
@@ -102,9 +103,11 @@ static void region_draw_emboss(const ARegion *ar, const rcti *scirct)
 	/* right  */
 	immAttrib4ub(color, 0, 0, 0, 30);
 	immVertex2f(pos, rect.xmax, rect.ymax);
+	immAttrib4ub(color, 0, 0, 0, 30);
 	immVertex2f(pos, rect.xmax, rect.ymin);
 	
 	/* bottom  */
+	immAttrib4ub(color, 0, 0, 0, 30);
 	immVertex2f(pos, rect.xmin, rect.ymin);
 	
 	/* left  */
@@ -112,6 +115,7 @@ static void region_draw_emboss(const ARegion *ar, const rcti *scirct)
 	immVertex2f(pos, rect.xmin, rect.ymax);
 
 	/* top  */
+	immAttrib4ub(color, 255, 255, 255, 30);
 	immVertex2f(pos, rect.xmax, rect.ymax);
 	
 	immEnd();
@@ -264,26 +268,32 @@ static void area_draw_azone(short x1, short y1, short x2, short y2)
 
 	immAttrib4ub(col, 255, 255, 255, 180);
 	immVertex2f(pos, x1, y2);
+	immAttrib4ub(col, 255, 255, 255, 180);
 	immVertex2f(pos, x2, y1);
 
 	immAttrib4ub(col, 255, 255, 255, 130);
 	immVertex2f(pos, x1, y2 - dy);
+	immAttrib4ub(col, 255, 255, 255, 130);
 	immVertex2f(pos, x2 - dx, y1);
 
 	immAttrib4ub(col, 255, 255, 255, 80);
 	immVertex2f(pos, x1, y2 - 2 * dy);
+	immAttrib4ub(col, 255, 255, 255, 80);
 	immVertex2f(pos, x2 - 2 * dx, y1);
 	
 	immAttrib4ub(col, 0, 0, 0, 210);
 	immVertex2f(pos, x1, y2 + 1);
+	immAttrib4ub(col, 0, 0, 0, 210);
 	immVertex2f(pos, x2 + 1, y1);
 
 	immAttrib4ub(col, 0, 0, 0, 180);
 	immVertex2f(pos, x1, y2 - dy + 1);
+	immAttrib4ub(col, 0, 0, 0, 180);
 	immVertex2f(pos, x2 - dx + 1, y1);
 
 	immAttrib4ub(col, 0, 0, 0, 150);
 	immVertex2f(pos, x1, y2 - 2 * dy + 1);
+	immAttrib4ub(col, 0, 0, 0, 150);
 	immVertex2f(pos, x2 - 2 * dx + 1, y1);
 
 	immEnd();
@@ -2240,7 +2250,7 @@ static const char *meta_data_list[] =
 
 BLI_INLINE bool metadata_is_valid(ImBuf *ibuf, char *r_str, short index, int offset)
 {
-	return (IMB_metadata_get_field(ibuf, meta_data_list[index], r_str + offset, MAX_METADATA_STR - offset) && r_str[0]);
+	return (IMB_metadata_get_field(ibuf->metadata, meta_data_list[index], r_str + offset, MAX_METADATA_STR - offset) && r_str[0]);
 }
 
 static void metadata_draw_imbuf(ImBuf *ibuf, const rctf *rect, int fontid, const bool is_top)
@@ -2503,28 +2513,34 @@ void ED_region_grid_draw(ARegion *ar, float zoomx, float zoomy)
 		
 		float theme_color[3];
 		UI_GetThemeColorShade3fv(TH_BACK, (int)(20.0f * (1.0f - blendfac)), theme_color);
-		immAttrib3fv(color, theme_color);
 		fac = 0.0f;
 		
 		/* the fine resolution level */
 		for (int i = 0; i < count_fine; i++) {
+			immAttrib3fv(color, theme_color);
 			immVertex2f(pos, x1, y1 * (1.0f - fac) + y2 * fac);
+			immAttrib3fv(color, theme_color);
 			immVertex2f(pos, x2, y1 * (1.0f - fac) + y2 * fac);
+			immAttrib3fv(color, theme_color);
 			immVertex2f(pos, x1 * (1.0f - fac) + x2 * fac, y1);
+			immAttrib3fv(color, theme_color);
 			immVertex2f(pos, x1 * (1.0f - fac) + x2 * fac, y2);
 			fac += gridstep;
 		}
 
 		if (count_large > 0) {
 			UI_GetThemeColor3fv(TH_BACK, theme_color);
-			immAttrib3fv(color, theme_color);
 			fac = 0.0f;
 			
 			/* the large resolution level */
 			for (int i = 0; i < count_large; i++) {
+				immAttrib3fv(color, theme_color);
 				immVertex2f(pos, x1, y1 * (1.0f - fac) + y2 * fac);
+				immAttrib3fv(color, theme_color);
 				immVertex2f(pos, x2, y1 * (1.0f - fac) + y2 * fac);
+				immAttrib3fv(color, theme_color);
 				immVertex2f(pos, x1 * (1.0f - fac) + x2 * fac, y1);
+				immAttrib3fv(color, theme_color);
 				immVertex2f(pos, x1 * (1.0f - fac) + x2 * fac, y2);
 				fac += 4.0f * gridstep;
 			}
diff --git a/source/blender/editors/screen/glutil.c b/source/blender/editors/screen/glutil.c
index cd53655a0bbb85babd1531e0fb565a1fe5ce29b0..cd875c73eda0d151254aa1bc73803f3154527cde 100644
--- a/source/blender/editors/screen/glutil.c
+++ b/source/blender/editors/screen/glutil.c
@@ -198,6 +198,7 @@ void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state,
 	glGetIntegerv(GL_UNPACK_ROW_LENGTH, &unpack_row_length);
 
 	glPixelStorei(GL_UNPACK_ROW_LENGTH, img_w);
+	glActiveTexture(GL_TEXTURE0);
 	glBindTexture(GL_TEXTURE_2D, texid);
 
 	/* don't want nasty border artifacts */
diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c
index 4c217a5e829110621611835ebf5c2d07f6260a37..e0c73b1782e0306ea7337024b03edae10c5b494c 100644
--- a/source/blender/editors/screen/screen_context.c
+++ b/source/blender/editors/screen/screen_context.c
@@ -93,6 +93,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
 	WorkSpace *workspace = BKE_workspace_active_get(win->workspace_hook);
 	ViewLayer *view_layer = BKE_view_layer_from_workspace_get(scene, workspace);
 	Object *obact = (view_layer && view_layer->basact) ? view_layer->basact->object : NULL;
+	Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
 
 	if (CTX_data_dir(member)) {
 		CTX_data_dir_set(result, screen_context_dir);
@@ -203,7 +204,6 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
 		return 1;
 	}
 	else if (CTX_data_equals(member, "visible_bones") || CTX_data_equals(member, "editable_bones")) {
-		Object *obedit = BKE_workspace_edit_object(workspace, scene);
 		bArmature *arm = (obedit && obedit->type == OB_ARMATURE) ? obedit->data : NULL;
 		EditBone *ebone, *flipbone = NULL;
 		const bool editable_bones = CTX_data_equals(member, "editable_bones");
@@ -246,7 +246,6 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
 		}
 	}
 	else if (CTX_data_equals(member, "selected_bones") || CTX_data_equals(member, "selected_editable_bones")) {
-		Object *obedit = BKE_workspace_edit_object(workspace, scene);
 		bArmature *arm = (obedit && obedit->type == OB_ARMATURE) ? obedit->data : NULL;
 		EditBone *ebone, *flipbone = NULL;
 		const bool selected_editable_bones = CTX_data_equals(member, "selected_editable_bones");
@@ -368,38 +367,37 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
 	}
 	else if (CTX_data_equals(member, "edit_object")) {
 		/* convenience for now, 1 object per scene in editmode */
-		Object *obedit = BKE_workspace_edit_object(workspace, scene);
 		if (obedit)
 			CTX_data_id_pointer_set(result, &obedit->id);
 		
 		return 1;
 	}
 	else if (CTX_data_equals(member, "sculpt_object")) {
-		if (obact && (workspace->object_mode & OB_MODE_SCULPT)) {
+		if (obact && (obact->mode & OB_MODE_SCULPT))
 			CTX_data_id_pointer_set(result, &obact->id);
-		}
+
 		return 1;
 	}
 	else if (CTX_data_equals(member, "vertex_paint_object")) {
-		if (obact && (workspace->object_mode & OB_MODE_VERTEX_PAINT))
+		if (obact && (obact->mode & OB_MODE_VERTEX_PAINT))
 			CTX_data_id_pointer_set(result, &obact->id);
 
 		return 1;
 	}
 	else if (CTX_data_equals(member, "weight_paint_object")) {
-		if (obact && (workspace->object_mode & OB_MODE_WEIGHT_PAINT))
+		if (obact && (obact->mode & OB_MODE_WEIGHT_PAINT))
 			CTX_data_id_pointer_set(result, &obact->id);
 
 		return 1;
 	}
 	else if (CTX_data_equals(member, "image_paint_object")) {
-		if (obact && (workspace->object_mode & OB_MODE_TEXTURE_PAINT))
+		if (obact && (obact->mode & OB_MODE_TEXTURE_PAINT))
 			CTX_data_id_pointer_set(result, &obact->id);
 
 		return 1;
 	}
 	else if (CTX_data_equals(member, "particle_edit_object")) {
-		if (obact && (workspace->object_mode & OB_MODE_PARTICLE_EDIT))
+		if (obact && (obact->mode & OB_MODE_PARTICLE_EDIT))
 			CTX_data_id_pointer_set(result, &obact->id);
 
 		return 1;
diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c
index a745b19233fb698839bffa43d50ae2b95460caa9..210fce5fba5c81902f0f38f11ef3aa96a3d48d4c 100644
--- a/source/blender/editors/screen/screen_ops.c
+++ b/source/blender/editors/screen/screen_ops.c
@@ -79,6 +79,7 @@
 #include "ED_screen_types.h"
 #include "ED_sequencer.h"
 #include "ED_util.h"
+#include "ED_undo.h"
 #include "ED_view3d.h"
 
 #include "RNA_access.h"
@@ -152,7 +153,6 @@ int ED_operator_scene_editable(bContext *C)
 
 int ED_operator_objectmode(bContext *C)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Scene *scene = CTX_data_scene(C);
 	Object *obact = CTX_data_active_object(C);
 
@@ -162,7 +162,7 @@ int ED_operator_objectmode(bContext *C)
 		return 0;
 	
 	/* add a check for ob->mode too? */
-	if (obact && (workspace->object_mode != OB_MODE_OBJECT))
+	if (obact && (obact->mode != OB_MODE_OBJECT))
 		return 0;
 	
 	return 1;
@@ -304,39 +304,35 @@ int ED_operator_console_active(bContext *C)
 	return ed_spacetype_test(C, SPACE_CONSOLE);
 }
 
-static int ed_object_hidden(Object *ob, eObjectMode object_mode)
+static int ed_object_hidden(Object *ob)
 {
 	/* if hidden but in edit mode, we still display, can happen with animation */
-	return ((ob->restrictflag & OB_RESTRICT_VIEW) && !(object_mode & OB_MODE_EDIT));
+	return ((ob->restrictflag & OB_RESTRICT_VIEW) && !(ob->mode & OB_MODE_EDIT));
 }
 
 int ED_operator_object_active(bContext *C)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Object *ob = ED_object_active_context(C);
-	return ((ob != NULL) && !ed_object_hidden(ob, workspace->object_mode));
+	return ((ob != NULL) && !ed_object_hidden(ob));
 }
 
 int ED_operator_object_active_editable(bContext *C)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Object *ob = ED_object_active_context(C);
-	return ((ob != NULL) && !ID_IS_LINKED(ob) && !ed_object_hidden(ob, workspace->object_mode));
+	return ((ob != NULL) && !ID_IS_LINKED(ob) && !ed_object_hidden(ob));
 }
 
 int ED_operator_object_active_editable_mesh(bContext *C)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Object *ob = ED_object_active_context(C);
-	return ((ob != NULL) && !ID_IS_LINKED(ob) && !ed_object_hidden(ob, workspace->object_mode) &&
+	return ((ob != NULL) && !ID_IS_LINKED(ob) && !ed_object_hidden(ob) &&
 	        (ob->type == OB_MESH) && !ID_IS_LINKED(ob->data));
 }
 
 int ED_operator_object_active_editable_font(bContext *C)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Object *ob = ED_object_active_context(C);
-	return ((ob != NULL) && !ID_IS_LINKED(ob) && !ed_object_hidden(ob, workspace->object_mode) &&
+	return ((ob != NULL) && !ID_IS_LINKED(ob) && !ed_object_hidden(ob) &&
 	        (ob->type == OB_FONT));
 }
 
@@ -381,14 +377,11 @@ int ED_operator_posemode_exclusive(bContext *C)
 {
 	Object *obact = CTX_data_active_object(C);
 
-	if (obact) {
-		const WorkSpace *workspace = CTX_wm_workspace(C);
-		if ((workspace->object_mode & OB_MODE_EDIT) == 0) {
-			Object *obpose;
-			if ((obpose = BKE_object_pose_armature_get(obact))) {
-				if (obact == obpose) {
-					return 1;
-				}
+	if (obact && !(obact->mode & OB_MODE_EDIT)) {
+		Object *obpose;
+		if ((obpose = BKE_object_pose_armature_get(obact))) {
+			if (obact == obpose) {
+				return 1;
 			}
 		}
 	}
@@ -402,15 +395,9 @@ int ED_operator_posemode_context(bContext *C)
 {
 	Object *obpose = ED_pose_object_from_context(C);
 
-	if (obpose) {
-		const WorkSpace *workspace = CTX_wm_workspace(C);
-		/* TODO, should we allow this out of pose mode? */
-		if (workspace->object_mode & OB_MODE_POSE) {
-			// if ((workspace->object_mode & OB_MODE_EDIT) == 0) {
-			if (BKE_object_pose_context_check(obpose)) {
-				return 1;
-			}
-			// }
+	if (obpose && !(obpose->mode & OB_MODE_EDIT)) {
+		if (BKE_object_pose_context_check(obpose)) {
+			return 1;
 		}
 	}
 
@@ -421,17 +408,11 @@ int ED_operator_posemode(bContext *C)
 {
 	Object *obact = CTX_data_active_object(C);
 
-
-	if (obact) {
-		const WorkSpace *workspace = CTX_wm_workspace(C);
-		if ((workspace->object_mode & OB_MODE_EDIT) == 0) {
-			Object *obpose;
-			if ((obpose = BKE_object_pose_armature_get(obact))) {
-				if (((workspace->object_mode & OB_MODE_POSE) && (obact == obpose)) ||
-				    (workspace->object_mode & OB_MODE_WEIGHT_PAINT))
-				{
-					return 1;
-				}
+	if (obact && !(obact->mode & OB_MODE_EDIT)) {
+		Object *obpose;
+		if ((obpose = BKE_object_pose_armature_get(obact))) {
+			if ((obact == obpose) || (obact->mode & OB_MODE_WEIGHT_PAINT)) {
+				return 1;
 			}
 		}
 	}
@@ -567,10 +548,9 @@ int ED_operator_mask(bContext *C)
 			}
 			case SPACE_IMAGE:
 			{
-				WorkSpace *workspace = CTX_wm_workspace(C);
 				SpaceImage *sima = sa->spacedata.first;
 				ViewLayer *view_layer = CTX_data_view_layer(C);
-				return ED_space_image_check_show_maskedit(sima, workspace, view_layer);
+				return ED_space_image_check_show_maskedit(sima, view_layer);
 			}
 		}
 	}
@@ -901,23 +881,23 @@ static void SCREEN_OT_actionzone(wmOperatorType *ot)
 /** \name Swap Area Operator
  * \{ */
 
-/* operator state vars used:  
- * sa1		start area
- * sa2		area to swap with
- * 
+/* operator state vars used:
+ * sa1      start area
+ * sa2      area to swap with
+ *
  * functions:
- * 
+ *
  * init()   set custom data for operator, based on actionzone event custom data
- * 
- * cancel()	cancel the operator
- * 
- * exit()	cleanup, send notifier
- * 
+ *
+ * cancel() cancel the operator
+ *
+ * exit()   cleanup, send notifier
+ *
  * callbacks:
- * 
+ *
  * invoke() gets called on shift+lmb drag in actionzone
  * call init(), add handler
- * 
+ *
  * modal()  accept modal events while doing it
  */
 
@@ -1458,35 +1438,35 @@ static void SCREEN_OT_area_move(wmOperatorType *ot)
 /** \name Split Area Operator
  * \{ */
 
-/* 
- * operator state vars:  
+/*
+ * operator state vars:
  * fac              spit point
  * dir              direction 'v' or 'h'
- * 
+ *
  * operator customdata:
  * area             pointer to (active) area
- * x, y			last used mouse pos
+ * x, y             last used mouse pos
  * (more, see below)
- * 
+ *
  * functions:
- * 
+ *
  * init()   set default property values, find area based on context
- * 
- * apply()	split area based on state vars
- * 
- * exit()	cleanup, send notifier
- * 
+ *
+ * apply()  split area based on state vars
+ *
+ * exit()   cleanup, send notifier
+ *
  * cancel() remove duplicated area
- * 
+ *
  * callbacks:
- * 
+ *
  * exec()   execute without any user interaction, based on state vars
  * call init(), apply(), exit()
- * 
+ *
  * invoke() gets called on mouse click in action-widget
  * call init(), add modal handler
  * call apply() with initial motion
- * 
+ *
  * modal()  accept modal events while doing it
  * call move-areas code with delta motion
  * call exit() or cancel() and remove handler
diff --git a/source/blender/editors/screen/workspace_edit.c b/source/blender/editors/screen/workspace_edit.c
index 2b28f61f901e6f472d777a1aadc51c323de30343..dee62f9c5dd29d5a6e0eda2171339d08d5a57610 100644
--- a/source/blender/editors/screen/workspace_edit.c
+++ b/source/blender/editors/screen/workspace_edit.c
@@ -85,6 +85,26 @@ WorkSpace *ED_workspace_add(
 	return workspace;
 }
 
+/**
+ * Changes the object mode (if needed) to the one set in \a workspace_new.
+ * Object mode is still stored on object level. In future it should all be workspace level instead.
+ */
+static void workspace_change_update_mode(
+        const WorkSpace *workspace_old, const WorkSpace *workspace_new,
+        bContext *C, Object *ob_act, ReportList *reports)
+{
+	UNUSED_VARS(workspace_old, workspace_new, C, ob_act, reports);
+#if 0
+	eObjectMode mode_old = workspace_old->object_mode;
+	eObjectMode mode_new = workspace_new->object_mode;
+
+	if (mode_old != mode_new) {
+		ED_object_mode_compat_set(C, ob_act, mode_new, reports);
+		ED_object_mode_toggle(C, mode_new);
+	}
+#endif
+}
+
 static void workspace_change_update_view_layer(
         WorkSpace *workspace_new, const WorkSpace *workspace_old,
         Scene *scene)
@@ -96,10 +116,11 @@ static void workspace_change_update_view_layer(
 
 static void workspace_change_update(
         WorkSpace *workspace_new, const WorkSpace *workspace_old,
-        bContext *C)
+        bContext *C, wmWindowManager *wm)
 {
 	/* needs to be done before changing mode! (to ensure right context) */
 	workspace_change_update_view_layer(workspace_new, workspace_old, CTX_data_scene(C));
+	workspace_change_update_mode(workspace_old, workspace_new, C, CTX_data_active_object(C), &wm->reports);
 }
 
 static bool workspace_change_find_new_layout_cb(const WorkSpaceLayout *layout, void *UNUSED(arg))
@@ -151,7 +172,7 @@ static WorkSpaceLayout *workspace_change_get_new_layout(
  * \returns if workspace changing was successful.
  */
 bool ED_workspace_change(
-        WorkSpace *workspace_new, bContext *C, wmWindow *win)
+        WorkSpace *workspace_new, bContext *C, wmWindowManager *wm, wmWindow *win)
 {
 	Main *bmain = CTX_data_main(C);
 	WorkSpace *workspace_old = WM_window_get_active_workspace(win);
@@ -169,39 +190,11 @@ bool ED_workspace_change(
 	BLI_assert(BKE_workspace_layout_screen_get(layout_new) == screen_new);
 
 	if (screen_new) {
-		bool use_object_mode = false;
-
-		/* Store old context for exiting edit-mode. */
-		EvaluationContext eval_ctx_old;
-		CTX_data_eval_ctx(C, &eval_ctx_old);
-		Scene *scene = WM_window_get_active_scene(win);
-		ViewLayer *view_layer_old = BKE_workspace_view_layer_get(workspace_old, scene);
-		Object *obact_old = OBACT(view_layer_old);
-
-		ViewLayer *view_layer_new = BKE_workspace_view_layer_get(workspace_new, scene);
-		Object *obact_new = OBACT(view_layer_new);
-
-		/* Handle object mode switching */
-		if ((workspace_old->object_mode != OB_MODE_OBJECT) ||
-		    (workspace_new->object_mode != OB_MODE_OBJECT))
-		{
-			if ((workspace_old->object_mode == workspace_new->object_mode) &&
-			    (obact_old == obact_new))
-			{
-				/* pass */
-			}
-			else {
-				use_object_mode = true;
-			}
-		}
-
-
 		WM_window_set_active_layout(win, workspace_new, layout_new);
 		WM_window_set_active_workspace(win, workspace_new);
 
 		/* update screen *after* changing workspace - which also causes the actual screen change */
-		screen_change_update(C, win, screen_new);
-		workspace_change_update(workspace_new, workspace_old, C);
+		workspace_change_update(workspace_new, workspace_old, C, wm);
 
 		BLI_assert(BKE_workspace_view_layer_get(workspace_new, CTX_data_scene(C)) != NULL);
 		BLI_assert(CTX_wm_workspace(C) == workspace_new);
@@ -209,20 +202,6 @@ bool ED_workspace_change(
 		WM_toolsystem_unlink(C, workspace_old);
 		WM_toolsystem_link(C, workspace_new);
 
-		if (use_object_mode) {
-			/* weak, set it back so it's used when activating again. */
-			eObjectMode object_mode = workspace_old->object_mode;
-			ED_object_mode_generic_exit_or_other_window(&eval_ctx_old, bmain->wm.first, workspace_old, scene, obact_old);
-			workspace_old->object_mode = object_mode;
-			ED_workspace_object_mode_sync_from_object(bmain->wm.first, workspace_old, obact_old);
-			ED_object_mode_generic_enter_or_other_window(C, NULL, workspace_new->object_mode);
-		}
-		else {
-			if (obact_new == NULL) {
-				workspace_new->object_mode = OB_MODE_OBJECT;
-			}
-		}
-
 		return true;
 	}
 
@@ -249,7 +228,6 @@ WorkSpace *ED_workspace_duplicate(
 	BLI_duplicatelist(transform_orientations_new, transform_orientations_old);
 
 	workspace_new->tool = workspace_old->tool;
-	workspace_new->object_mode = workspace_old->object_mode;
 
 	for (WorkSpaceLayout *layout_old = layouts_old->first; layout_old; layout_old = layout_old->next) {
 		WorkSpaceLayout *layout_new = ED_workspace_layout_duplicate(workspace_new, layout_old, win);
@@ -277,7 +255,7 @@ bool ED_workspace_delete(
 		WorkSpace *prev = workspace_id->prev;
 		WorkSpace *next = workspace_id->next;
 
-		ED_workspace_change((prev != NULL) ? prev : next, C, win);
+		ED_workspace_change((prev != NULL) ? prev : next, C, wm, win);
 	}
 	BKE_libblock_free(bmain, workspace_id);
 
@@ -306,63 +284,6 @@ void ED_workspace_view_layer_unset(
 	}
 }
 
-/**
- * When a work-space mode has changed,
- * flush it to all other visible work-spaces using the same object
- * since we don't support one object being in two different modes at once.
- * \note We could support this but it's more trouble than it's worth.
- */
-
-void ED_workspace_object_mode_sync_from_object(wmWindowManager *wm, WorkSpace *workspace, Object *obact)
-{
-	if (obact == NULL) {
-		return;
-	}
-	for (wmWindow *win = wm->windows.first; win; win = win->next) {
-		WorkSpace *workspace_iter = BKE_workspace_active_get(win->workspace_hook);
-		if ((workspace != workspace_iter) && (workspace->object_mode != workspace_iter->object_mode)) {
-			Scene *scene_iter = WM_window_get_active_scene(win);
-			ViewLayer *view_layer = BKE_view_layer_from_workspace_get(scene_iter, workspace_iter);
-			if (obact == OBACT(view_layer)) {
-				workspace_iter->object_mode = workspace->object_mode;
-				/* TODO(campbell), use msgbus */
-				WM_main_add_notifier(NC_SCENE | ND_MODE | NS_MODE_OBJECT, scene_iter);
-			}
-		}
-	}
-}
-
-void ED_workspace_object_mode_sync_from_scene(wmWindowManager *wm, WorkSpace *workspace, Scene *scene)
-{
-	ViewLayer *view_layer = BKE_workspace_view_layer_get(workspace, scene);
-	if (view_layer) {
-		Object *obact = OBACT(view_layer);
-		ED_workspace_object_mode_sync_from_object(wm, workspace, obact);
-	}
-}
-
-bool ED_workspace_object_mode_in_other_window(
-        struct wmWindowManager *wm, const wmWindow *win_compare, Object *obact,
-        eObjectMode *r_object_mode)
-{
-	for (wmWindow *win_iter = wm->windows.first; win_iter; win_iter = win_iter->next) {
-		if (win_compare != win_iter) {
-			WorkSpace *workspace_iter = BKE_workspace_active_get(win_iter->workspace_hook);
-			Scene *scene_iter = WM_window_get_active_scene(win_iter);
-			ViewLayer *view_layer_iter = BKE_view_layer_from_workspace_get(scene_iter, workspace_iter);
-			Object *obact_iter = OBACT(view_layer_iter);
-			if (obact == obact_iter) {
-				if (r_object_mode) {
-					*r_object_mode = workspace_iter->object_mode;
-				}
-				return true;
-			}
-		}
-	}
-
-	return false;
-}
-
 /** \} Workspace API */
 
 
diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt
index b3e125baae312ff0aac1b54ec013168ecea42a0e..80c58e5b91ddbdb0f22ec205381dc1edc761a175 100644
--- a/source/blender/editors/sculpt_paint/CMakeLists.txt
+++ b/source/blender/editors/sculpt_paint/CMakeLists.txt
@@ -43,14 +43,15 @@ set(INC_SYS
 set(SRC
 	paint_cursor.c
 	paint_curve.c
+	paint_curve_undo.c
 	paint_hide.c
 	paint_image.c
 	paint_image_2d.c
 	paint_image_proj.c
+	paint_image_undo.c
 	paint_mask.c
 	paint_ops.c
 	paint_stroke.c
-	paint_undo.c
 	paint_utils.c
 	paint_vertex.c
 	paint_vertex_color_ops.c
diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c
index d2063d15ddfce5ef44d1c0e398dfb31cfcc0fc31..6229b91a518c5d1ce8e173720eaa5173e707efe4 100644
--- a/source/blender/editors/sculpt_paint/paint_cursor.c
+++ b/source/blender/editors/sculpt_paint/paint_cursor.c
@@ -338,6 +338,7 @@ static int load_tex(Brush *br, ViewContext *vc, float zoom, bool col, bool prima
 		size = target->old_size;
 	}
 
+	glActiveTexture(GL_TEXTURE0);
 	glBindTexture(GL_TEXTURE_2D, target->overlay_texture);
 
 	if (refresh) {
@@ -464,6 +465,7 @@ static int load_tex_cursor(Brush *br, ViewContext *vc, float zoom)
 		size = cursor_snap.size;
 	}
 
+	glActiveTexture(GL_TEXTURE0);
 	glBindTexture(GL_TEXTURE_2D, cursor_snap.overlay_texture);
 
 	if (refresh) {
@@ -767,7 +769,7 @@ static void paint_draw_cursor_overlay(UnifiedPaintSettings *ups, Brush *brush,
 		/* draw textured quad */
 
 		/* draw textured quad */
-		immUniform1i("image", GL_TEXTURE0);
+		immUniform1i("image", 0);
 
 		immBegin(GWN_PRIM_TRI_FAN, 4);
 		immAttrib2f(texCoord, 0.0f, 0.0f);
diff --git a/source/blender/editors/sculpt_paint/paint_curve.c b/source/blender/editors/sculpt_paint/paint_curve.c
index c9ad4a46a137e9c73a2a39314184c8ab6ec89447..049d8ff8c0b7d1d3ce67765f5982f63ab8e4bc4f 100644
--- a/source/blender/editors/sculpt_paint/paint_curve.c
+++ b/source/blender/editors/sculpt_paint/paint_curve.c
@@ -32,10 +32,8 @@
 #include "DNA_screen_types.h"
 #include "DNA_space_types.h"
 #include "DNA_view3d_types.h"
-#include "DNA_workspace_types.h"
 
 #include "BLI_math_vector.h"
-#include "BLI_string.h"
 
 #include "BKE_context.h"
 #include "BKE_main.h"
@@ -43,8 +41,8 @@
 
 #include "DEG_depsgraph.h"
 
-#include "ED_paint.h"
 #include "ED_view3d.h"
+#include "ED_paint.h"
 
 #include "WM_api.h"
 #include "WM_types.h"
@@ -59,16 +57,14 @@
 #define PAINT_CURVE_SELECT_THRESHOLD 40.0f
 #define PAINT_CURVE_POINT_SELECT(pcp, i) (*(&pcp->bez.f1 + i) = SELECT)
 
-
 int paint_curve_poll(bContext *C)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Object *ob = CTX_data_active_object(C);
 	Paint *p;
 	RegionView3D *rv3d = CTX_wm_region_view3d(C);
 	SpaceImage *sima;
 
-	if (rv3d && !(ob && ((workspace->object_mode & OB_MODE_ALL_PAINT) != 0)))
+	if (rv3d && !(ob && ((ob->mode & OB_MODE_ALL_PAINT) != 0)))
 		return false;
 
 	sima = CTX_wm_space_image(C);
@@ -85,91 +81,6 @@ int paint_curve_poll(bContext *C)
 	return false;
 }
 
-/* Paint Curve Undo*/
-
-typedef struct UndoCurve {
-	struct UndoImageTile *next, *prev;
-
-	PaintCurvePoint *points; /* points of curve */
-	int tot_points;
-	int active_point;
-
-	char idname[MAX_ID_NAME];  /* name instead of pointer*/
-} UndoCurve;
-
-static void paintcurve_undo_restore(bContext *C, ListBase *lb)
-{
-	Paint *p = BKE_paint_get_active_from_context(C);
-	UndoCurve *uc;
-	PaintCurve *pc = NULL;
-
-	if (p->brush) {
-		pc = p->brush->paint_curve;
-	}
-
-	if (!pc)
-		return;
-
-	uc = (UndoCurve *)lb->first;
-
-	if (STREQLEN(uc->idname, pc->id.name, BLI_strnlen(uc->idname, sizeof(uc->idname)))) {
-		SWAP(PaintCurvePoint *, pc->points, uc->points);
-		SWAP(int, pc->tot_points, uc->tot_points);
-		SWAP(int, pc->add_index, uc->active_point);
-	}
-}
-
-static void paintcurve_undo_delete(ListBase *lb)
-{
-	UndoCurve *uc;
-	uc = (UndoCurve *)lb->first;
-
-	if (uc->points)
-		MEM_freeN(uc->points);
-	uc->points = NULL;
-}
-
-
-static void paintcurve_undo_begin(bContext *C, wmOperator *op, PaintCurve *pc)
-{
-	ePaintMode mode = BKE_paintmode_get_active_from_context(C);
-	ListBase *lb = NULL;
-	int undo_stack_id;
-	UndoCurve *uc;
-
-	switch (mode) {
-		case ePaintTexture2D:
-		case ePaintTextureProjective:
-			undo_stack_id = UNDO_PAINT_IMAGE;
-			break;
-
-		case ePaintSculpt:
-			undo_stack_id = UNDO_PAINT_MESH;
-			break;
-
-		default:
-			/* do nothing, undo is handled by global */
-			return;
-	}
-
-
-	ED_undo_paint_push_begin(undo_stack_id, op->type->name,
-	                         paintcurve_undo_restore, paintcurve_undo_delete, NULL);
-	lb = undo_paint_push_get_list(undo_stack_id);
-
-	uc = MEM_callocN(sizeof(*uc), "Undo_curve");
-
-	lb->first = uc;
-
-	BLI_strncpy(uc->idname, pc->id.name, sizeof(uc->idname));
-	uc->tot_points = pc->tot_points;
-	uc->active_point = pc->add_index;
-	uc->points = MEM_dupallocN(pc->points);
-
-	undo_paint_push_count_alloc(undo_stack_id, sizeof(*uc) + sizeof(*pc->points) * pc->tot_points);
-
-	ED_undo_paint_push_end(undo_stack_id);
-}
 #define SEL_F1 (1 << 0)
 #define SEL_F2 (1 << 1)
 #define SEL_F3 (1 << 2)
@@ -295,7 +206,7 @@ static void paintcurve_point_add(bContext *C,  wmOperator *op, const int loc[2])
 		br->paint_curve = pc = BKE_paint_curve_add(bmain, "PaintCurve");
 	}
 
-	paintcurve_undo_begin(C, op, pc);
+	ED_paintcurve_undo_push_begin(op->type->name);
 
 	pcp = MEM_mallocN((pc->tot_points + 1) * sizeof(PaintCurvePoint), "PaintCurvePoint");
 	add_index = pc->add_index;
@@ -333,6 +244,8 @@ static void paintcurve_point_add(bContext *C,  wmOperator *op, const int loc[2])
 		pcp[add_index].bez.h1 = HD_ALIGN;
 	}
 
+	ED_paintcurve_undo_push_end();
+
 	WM_paint_cursor_tag_redraw(window, ar);
 }
 
@@ -394,7 +307,7 @@ static int paintcurve_delete_point_exec(bContext *C, wmOperator *op)
 		return OPERATOR_CANCELLED;
 	}
 
-	paintcurve_undo_begin(C, op, pc);
+	ED_paintcurve_undo_push_begin(op->type->name);
 
 #define DELETE_TAG 2
 
@@ -434,6 +347,8 @@ static int paintcurve_delete_point_exec(bContext *C, wmOperator *op)
 
 #undef DELETE_TAG
 
+	ED_paintcurve_undo_push_end();
+
 	WM_paint_cursor_tag_redraw(window, ar);
 
 	return OPERATOR_FINISHED;
@@ -471,7 +386,7 @@ static bool paintcurve_point_select(bContext *C, wmOperator *op, const int loc[2
 	if (!pc)
 		return false;
 
-	paintcurve_undo_begin(C, op, pc);
+	ED_paintcurve_undo_push_begin(op->type->name);
 
 	if (toggle) {
 		PaintCurvePoint *pcp;
@@ -536,10 +451,14 @@ static bool paintcurve_point_select(bContext *C, wmOperator *op, const int loc[2
 			}
 		}
 
-		if (!pcp)
+		if (!pcp) {
+			ED_paintcurve_undo_push_end();
 			return false;
+		}
 	}
 
+	ED_paintcurve_undo_push_end();
+
 	WM_paint_cursor_tag_redraw(window, ar);
 
 	return true;
@@ -654,9 +573,6 @@ static int paintcurve_slide_invoke(bContext *C, wmOperator *op, const wmEvent *e
 		psd->align = align;
 		op->customdata = psd;
 
-		if (do_select)
-			paintcurve_undo_begin(C, op, pc);
-
 		/* first, clear all selection from points */
 		for (i = 0; i < pc->tot_points; i++)
 			pc->points[i].bez.f1 = pc->points[i].bez.f3 = pc->points[i].bez.f2 = 0;
@@ -679,6 +595,8 @@ static int paintcurve_slide_modal(bContext *C, wmOperator *op, const wmEvent *ev
 
 	if (event->type == psd->event && event->val == KM_RELEASE) {
 		MEM_freeN(psd);
+		ED_paintcurve_undo_push_begin(op->type->name);
+		ED_paintcurve_undo_push_end();
 		return OPERATOR_FINISHED;
 	}
 
diff --git a/source/blender/editors/sculpt_paint/paint_curve_undo.c b/source/blender/editors/sculpt_paint/paint_curve_undo.c
new file mode 100644
index 0000000000000000000000000000000000000000..77f06180df6173c635f0ed9c4d9c50ce9f158858
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/paint_curve_undo.c
@@ -0,0 +1,168 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/sculpt_paint/paint_curve_undo.c
+ *  \ingroup edsculpt
+ */
+
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_brush_types.h"
+#include "DNA_space_types.h"
+
+#include "BLI_string.h"
+#include "BLI_array_utils.h"
+
+#include "BKE_context.h"
+#include "BKE_paint.h"
+#include "BKE_undo_system.h"
+
+#include "ED_paint.h"
+#include "ED_undo.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "paint_intern.h"
+
+/* -------------------------------------------------------------------- */
+/** \name Undo Conversion
+ * \{ */
+
+typedef struct UndoCurve {
+	PaintCurvePoint *points; /* points of curve */
+	int tot_points;
+	int add_index;
+} UndoCurve;
+
+static void undocurve_from_paintcurve(UndoCurve *uc, const PaintCurve *pc)
+{
+	BLI_assert(BLI_array_is_zeroed(uc, 1));
+	uc->points = MEM_dupallocN(pc->points);
+	uc->tot_points = pc->tot_points;
+	uc->add_index = pc->add_index;
+}
+
+static void undocurve_to_paintcurve(const UndoCurve *uc, PaintCurve *pc)
+{
+	MEM_SAFE_FREE(pc->points);
+	pc->points = MEM_dupallocN(uc->points);
+	pc->tot_points = uc->tot_points;
+	pc->add_index = uc->add_index;
+}
+
+static void undocurve_free_data(UndoCurve *uc)
+{
+	MEM_SAFE_FREE(uc->points);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Implements ED Undo System
+ * \{ */
+
+typedef struct PaintCurveUndoStep {
+	UndoStep step;
+	PaintCurve *pc;
+	UndoCurve data;
+} PaintCurveUndoStep;
+
+static bool paintcurve_undosys_poll(bContext *C)
+{
+	Paint *p = BKE_paint_get_active_from_context(C);
+	return (p->brush && p->brush->paint_curve);
+}
+
+static void paintcurve_undosys_step_encode_init(struct bContext *C, UndoStep *us_p)
+{
+	/* XXX, use to set the undo type only. */
+	UNUSED_VARS(C, us_p);
+}
+
+static bool paintcurve_undosys_step_encode(struct bContext *C, UndoStep *us_p)
+{
+	Paint *p = BKE_paint_get_active_from_context(C);
+	PaintCurve *pc = p ? (p->brush ? p->brush->paint_curve : NULL) : NULL;
+	if (pc == NULL) {
+		return false;
+	}
+
+	PaintCurveUndoStep *us = (PaintCurveUndoStep *)us_p;
+	BLI_assert(us->step.data_size == 0);
+
+	us->pc = pc;
+	undocurve_from_paintcurve(&us->data, pc);
+
+	return true;
+}
+
+static void paintcurve_undosys_step_decode(struct bContext *UNUSED(C), UndoStep *us_p, int UNUSED(dir))
+{
+	PaintCurveUndoStep *us = (PaintCurveUndoStep *)us_p;
+	undocurve_to_paintcurve(&us->data, us->pc);
+}
+
+static void paintcurve_undosys_step_free(UndoStep *us_p)
+{
+	PaintCurveUndoStep *us = (PaintCurveUndoStep *)us_p;
+	undocurve_free_data(&us->data);
+}
+
+/* Export for ED_undo_sys. */
+void ED_paintcurve_undosys_type(UndoType *ut)
+{
+	ut->name = "Paint Curve";
+	/* don't poll for now */
+	ut->poll = paintcurve_undosys_poll;
+	ut->step_encode_init = paintcurve_undosys_step_encode_init;
+	ut->step_encode = paintcurve_undosys_step_encode;
+	ut->step_decode = paintcurve_undosys_step_decode;
+	ut->step_free = paintcurve_undosys_step_free;
+
+	ut->mode = BKE_UNDOTYPE_MODE_STORE;
+	ut->use_context = false;
+
+	ut->step_size = sizeof(PaintCurveUndoStep);
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+/** \name Utilities
+ * \{ */
+
+void ED_paintcurve_undo_push_begin(const char *name)
+{
+	UndoStack *ustack = ED_undo_stack_get();
+	bContext *C = NULL; /* special case, we never read from this. */
+	BKE_undosys_step_push_init_with_type(ustack, C, name, BKE_UNDOSYS_TYPE_PAINTCURVE);
+}
+
+void ED_paintcurve_undo_push_end(void)
+{
+	UndoStack *ustack = ED_undo_stack_get();
+	BKE_undosys_step_push(ustack, NULL, NULL);
+}
+
+/** \} */
diff --git a/source/blender/editors/sculpt_paint/paint_hide.c b/source/blender/editors/sculpt_paint/paint_hide.c
index 5d4451dd3d895151e21658f6e386cf6316561f2b..4d3c8fe4d5c90d2038a97449a6df905177cad92c 100644
--- a/source/blender/editors/sculpt_paint/paint_hide.c
+++ b/source/blender/editors/sculpt_paint/paint_hide.c
@@ -384,7 +384,7 @@ static int hide_show_exec(bContext *C, wmOperator *op)
 	clip_planes_from_rect(C, clip_planes, &rect);
 
 	dm = mesh_get_derived_final(&eval_ctx, CTX_data_scene(C), ob, CD_MASK_BAREMESH);
-	pbvh = dm->getPBVH(ob, dm, eval_ctx.object_mode);
+	pbvh = dm->getPBVH(ob, dm);
 	ob->sculpt->pbvh = pbvh;
 
 	get_pbvh_nodes(pbvh, &nodes, &totnode, clip_planes, area);
diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c
index 969bf8f37b1d9be83277710635aa5dcb6bb4cc5d..ae26de8b269ad4e5b1721f5a8afffb0f69386056 100644
--- a/source/blender/editors/sculpt_paint/paint_image.c
+++ b/source/blender/editors/sculpt_paint/paint_image.c
@@ -39,7 +39,6 @@
 #include "BLI_math.h"
 #include "BLI_blenlib.h"
 #include "BLI_utildefines.h"
-#include "BLI_threads.h"
 
 #include "BLT_translation.h"
 
@@ -54,11 +53,12 @@
 #include "BKE_context.h"
 #include "BKE_DerivedMesh.h"
 #include "BKE_brush.h"
-#include "BKE_image.h"
 #include "BKE_main.h"
 #include "BKE_material.h"
 #include "BKE_node.h"
 #include "BKE_paint.h"
+#include "BKE_undo_system.h"
+
 
 #include "DEG_depsgraph.h"
 
@@ -87,43 +87,10 @@
 
 #include "paint_intern.h"
 
-typedef struct UndoImageTile {
-	struct UndoImageTile *next, *prev;
-
-	char idname[MAX_ID_NAME];  /* name instead of pointer*/
-	char ibufname[IMB_FILENAME_SIZE];
-
-	union {
-		float        *fp;
-		unsigned int *uint;
-		void         *pt;
-	} rect;
-
-	unsigned short *mask;
-
-	int x, y;
-
-	Image *ima;
-	short source, use_float;
-	char gen_type;
-	bool valid;
-} UndoImageTile;
-
 /* this is a static resource for non-globality,
  * Maybe it should be exposed as part of the
  * paint operation, but for now just give a public interface */
 static ImagePaintPartialRedraw imapaintpartial = {0, 0, 0, 0, 0};
-static SpinLock undolock;
-
-void image_undo_init_locks(void)
-{
-	BLI_spin_init(&undolock);
-}
-
-void image_undo_end_locks(void)
-{
-	BLI_spin_end(&undolock);
-}
 
 ImagePaintPartialRedraw *get_imapaintpartial(void)
 {
@@ -135,296 +102,6 @@ void set_imapaintpartial(struct ImagePaintPartialRedraw *ippr)
 	imapaintpartial = *ippr;
 }
 
-/* UNDO */
-typedef enum {
-	COPY = 0,
-	RESTORE = 1,
-	RESTORE_COPY = 2
-} CopyMode;
-
-static void undo_copy_tile(UndoImageTile *tile, ImBuf *tmpibuf, ImBuf *ibuf, CopyMode mode)
-{
-	if (mode == COPY) {
-		/* copy or swap contents of tile->rect and region in ibuf->rect */
-		IMB_rectcpy(tmpibuf, ibuf, 0, 0, tile->x * IMAPAINT_TILE_SIZE,
-		            tile->y * IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE);
-
-		if (ibuf->rect_float) {
-			SWAP(float *, tmpibuf->rect_float, tile->rect.fp);
-		}
-		else {
-			SWAP(unsigned int *, tmpibuf->rect, tile->rect.uint);
-		}
-	}
-	else {
-		if (mode == RESTORE_COPY) {
-			IMB_rectcpy(tmpibuf, ibuf, 0, 0, tile->x * IMAPAINT_TILE_SIZE,
-			            tile->y * IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE);
-		}
-		/* swap to the tmpbuf for easy copying */
-		if (ibuf->rect_float) {
-			SWAP(float *, tmpibuf->rect_float, tile->rect.fp);
-		}
-		else {
-			SWAP(unsigned int *, tmpibuf->rect, tile->rect.uint);
-		}
-
-		IMB_rectcpy(ibuf, tmpibuf, tile->x * IMAPAINT_TILE_SIZE,
-		            tile->y * IMAPAINT_TILE_SIZE, 0, 0, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE);
-
-		if (mode == RESTORE) {
-			if (ibuf->rect_float) {
-				SWAP(float *, tmpibuf->rect_float, tile->rect.fp);
-			}
-			else {
-				SWAP(unsigned int *, tmpibuf->rect, tile->rect.uint);
-			}
-		}
-	}
-}
-
-void *image_undo_find_tile(Image *ima, ImBuf *ibuf, int x_tile, int y_tile, unsigned short **mask, bool validate)
-{
-	ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE);
-	UndoImageTile *tile;
-	short use_float = ibuf->rect_float ? 1 : 0;
-
-	for (tile = lb->first; tile; tile = tile->next) {
-		if (tile->x == x_tile && tile->y == y_tile && ima->gen_type == tile->gen_type && ima->source == tile->source) {
-			if (tile->use_float == use_float) {
-				if (STREQ(tile->idname, ima->id.name) && STREQ(tile->ibufname, ibuf->name)) {
-					if (mask) {
-						/* allocate mask if requested */
-						if (!tile->mask) {
-							tile->mask = MEM_callocN(sizeof(unsigned short) * IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE,
-							                         "UndoImageTile.mask");
-						}
-
-						*mask = tile->mask;
-					}
-					if (validate)
-						tile->valid = true;
-
-					return tile->rect.pt;
-				}
-			}
-		}
-	}
-	
-	return NULL;
-}
-
-void *image_undo_push_tile(Image *ima, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile, int y_tile, unsigned short **mask, bool **valid, bool proj, bool find_prev)
-{
-	ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE);
-	UndoImageTile *tile;
-	int allocsize;
-	short use_float = ibuf->rect_float ? 1 : 0;
-	void *data;
-
-	/* check if tile is already pushed */
-
-	/* in projective painting we keep accounting of tiles, so if we need one pushed, just push! */
-	if (find_prev) {
-		data = image_undo_find_tile(ima, ibuf, x_tile, y_tile, mask, true);
-		if (data)
-			return data;
-	}
-
-	if (*tmpibuf == NULL)
-		*tmpibuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, IB_rectfloat | IB_rect);
-	
-	tile = MEM_callocN(sizeof(UndoImageTile), "UndoImageTile");
-	BLI_strncpy(tile->idname, ima->id.name, sizeof(tile->idname));
-	tile->x = x_tile;
-	tile->y = y_tile;
-
-	/* add mask explicitly here */
-	if (mask)
-		*mask = tile->mask = MEM_callocN(sizeof(unsigned short) * IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE,
-		                         "UndoImageTile.mask");
-
-	allocsize = IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE * 4;
-	allocsize *= (ibuf->rect_float) ? sizeof(float) : sizeof(char);
-	tile->rect.pt = MEM_mapallocN(allocsize, "UndeImageTile.rect");
-
-	BLI_strncpy(tile->ibufname, ibuf->name, sizeof(tile->ibufname));
-
-	tile->gen_type = ima->gen_type;
-	tile->source = ima->source;
-	tile->use_float = use_float;
-	tile->valid = true;
-	tile->ima = ima;
-
-	if (valid)
-		*valid = &tile->valid;
-
-	undo_copy_tile(tile, *tmpibuf, ibuf, COPY);
-
-	if (proj)
-		BLI_spin_lock(&undolock);
-
-	undo_paint_push_count_alloc(UNDO_PAINT_IMAGE, allocsize);
-	BLI_addtail(lb, tile);
-
-	if (proj)
-		BLI_spin_unlock(&undolock);
-
-	return tile->rect.pt;
-}
-
-void image_undo_remove_masks(void)
-{
-	ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE);
-	UndoImageTile *tile;
-
-	for (tile = lb->first; tile; tile = tile->next) {
-		if (tile->mask) {
-			MEM_freeN(tile->mask);
-			tile->mask = NULL;
-		}
-	}
-}
-
-static void image_undo_restore_runtime(ListBase *lb)
-{
-	ImBuf *ibuf, *tmpibuf;
-	UndoImageTile *tile;
-
-	tmpibuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32,
-	                         IB_rectfloat | IB_rect);
-
-	for (tile = lb->first; tile; tile = tile->next) {
-		Image *ima = tile->ima;
-		ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
-
-		undo_copy_tile(tile, tmpibuf, ibuf, RESTORE);
-
-		GPU_free_image(ima); /* force OpenGL reload (maybe partial update will operate better?) */
-		if (ibuf->rect_float)
-			ibuf->userflags |= IB_RECT_INVALID; /* force recreate of char rect */
-		if (ibuf->mipmap[0])
-			ibuf->userflags |= IB_MIPMAP_INVALID;  /* force mipmap recreatiom */
-		ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
-
-		BKE_image_release_ibuf(ima, ibuf, NULL);
-	}
-
-	IMB_freeImBuf(tmpibuf);
-}
-
-void ED_image_undo_restore(bContext *C, ListBase *lb)
-{
-	Main *bmain = CTX_data_main(C);
-	Image *ima = NULL;
-	ImBuf *ibuf, *tmpibuf;
-	UndoImageTile *tile;
-
-	tmpibuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32,
-	                         IB_rectfloat | IB_rect);
-
-	for (tile = lb->first; tile; tile = tile->next) {
-		short use_float;
-
-		/* find image based on name, pointer becomes invalid with global undo */
-		if (ima && STREQ(tile->idname, ima->id.name)) {
-			/* ima is valid */
-		}
-		else {
-			ima = BLI_findstring(&bmain->image, tile->idname, offsetof(ID, name));
-		}
-
-		ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
-
-		if (ima && ibuf && !STREQ(tile->ibufname, ibuf->name)) {
-			/* current ImBuf filename was changed, probably current frame
-			 * was changed when painting on image sequence, rather than storing
-			 * full image user (which isn't so obvious, btw) try to find ImBuf with
-			 * matched file name in list of already loaded images */
-
-			BKE_image_release_ibuf(ima, ibuf, NULL);
-
-			ibuf = BKE_image_get_ibuf_with_name(ima, tile->ibufname);
-		}
-
-		if (!ima || !ibuf || !(ibuf->rect || ibuf->rect_float)) {
-			BKE_image_release_ibuf(ima, ibuf, NULL);
-			continue;
-		}
-
-		if (ima->gen_type != tile->gen_type || ima->source != tile->source) {
-			BKE_image_release_ibuf(ima, ibuf, NULL);
-			continue;
-		}
-
-		use_float = ibuf->rect_float ? 1 : 0;
-
-		if (use_float != tile->use_float) {
-			BKE_image_release_ibuf(ima, ibuf, NULL);
-			continue;
-		}
-
-		undo_copy_tile(tile, tmpibuf, ibuf, RESTORE_COPY);
-
-		GPU_free_image(ima); /* force OpenGL reload */
-		if (ibuf->rect_float)
-			ibuf->userflags |= IB_RECT_INVALID; /* force recreate of char rect */
-		if (ibuf->mipmap[0])
-			ibuf->userflags |= IB_MIPMAP_INVALID;  /* force mipmap recreatiom */
-		ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
-
-		DEG_id_tag_update(&ima->id, 0);
-
-		BKE_image_release_ibuf(ima, ibuf, NULL);
-	}
-
-	IMB_freeImBuf(tmpibuf);
-}
-
-void ED_image_undo_free(ListBase *lb)
-{
-	UndoImageTile *tile;
-
-	for (tile = lb->first; tile; tile = tile->next)
-		MEM_freeN(tile->rect.pt);
-}
-
-static void image_undo_end(void)
-{
-	ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE);
-	UndoImageTile *tile;
-	int deallocsize = 0;
-	int allocsize = IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE * 4;
-
-	/* first dispose of invalid tiles (may happen due to drag dot for instance) */
-	for (tile = lb->first; tile;) {
-		if (!tile->valid) {
-			UndoImageTile *tmp_tile = tile->next;
-			deallocsize += allocsize * ((tile->use_float) ? sizeof(float) : sizeof(char));
-			MEM_freeN(tile->rect.pt);
-			BLI_freelinkN(lb, tile);
-			tile = tmp_tile;
-		}
-		else {
-			tile = tile->next;
-		}
-	}
-
-	/* don't forget to remove the size of deallocated tiles */
-	undo_paint_push_count_alloc(UNDO_PAINT_IMAGE, -deallocsize);
-
-	ED_undo_paint_push_end(UNDO_PAINT_IMAGE);
-}
-
-static void image_undo_invalidate(void)
-{
-	UndoImageTile *tile;
-	ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE);
-
-	for (tile = lb->first; tile; tile = tile->next)
-		tile->valid = false;
-}
-
 /* Imagepaint Partial Redraw & Dirty Region */
 
 void ED_imapaint_clear_partial_redraw(void)
@@ -454,7 +131,7 @@ void ED_imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, int
 
 	if (w == 0 || h == 0)
 		return;
-	
+
 	if (!imapaintpartial.enabled) {
 		imapaintpartial.x1 = x;
 		imapaintpartial.y1 = y;
@@ -471,12 +148,14 @@ void ED_imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, int
 
 	imapaint_region_tiles(ibuf, x, y, w, h, &tilex, &tiley, &tilew, &tileh);
 
+	ListBase *undo_tiles = ED_image_undo_get_tiles();
+
 	for (ty = tiley; ty <= tileh; ty++)
 		for (tx = tilex; tx <= tilew; tx++)
-			image_undo_push_tile(ima, ibuf, &tmpibuf, tx, ty, NULL, NULL, false, find_old);
+			image_undo_push_tile(undo_tiles, ima, ibuf, &tmpibuf, tx, ty, NULL, NULL, false, find_old);
 
 	ibuf->userflags |= IB_BITMAPDIRTY;
-	
+
 	if (tmpibuf)
 		IMB_freeImBuf(tmpibuf);
 }
@@ -590,13 +269,11 @@ static int image_paint_poll(bContext *C)
 {
 	Object *obact;
 
-	if (!image_paint_brush(C)) {
+	if (!image_paint_brush(C))
 		return 0;
-	}
 
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	obact = CTX_data_active_object(C);
-	if ((obact && workspace->object_mode & OB_MODE_TEXTURE_PAINT) && CTX_wm_region_view3d(C)) {
+	if ((obact && obact->mode & OB_MODE_TEXTURE_PAINT) && CTX_wm_region_view3d(C)) {
 		return 1;
 	}
 	else {
@@ -658,8 +335,9 @@ bool paint_use_opacity_masking(Brush *brush)
 	            false : true;
 }
 
-void paint_brush_color_get(struct Scene *scene, struct Brush *br, bool color_correction, bool invert, float distance,
-                           float pressure, float color[3], struct ColorManagedDisplay *display)
+void paint_brush_color_get(
+        struct Scene *scene, struct Brush *br, bool color_correction, bool invert, float distance,
+        float pressure, float color[3], struct ColorManagedDisplay *display)
 {
 	if (invert)
 		copy_v3_v3(color, BKE_brush_secondary_color_get(scene, br));
@@ -794,20 +472,11 @@ static PaintOperation *texture_paint_init(bContext *C, wmOperator *op, const flo
 	}
 	
 	settings->imapaint.flag |= IMAGEPAINT_DRAWING;
-	ED_undo_paint_push_begin(UNDO_PAINT_IMAGE, op->type->name,
-	                         ED_image_undo_restore, ED_image_undo_free, NULL);
+	ED_image_undo_push_begin(op->type->name);
 
 	return pop;
 }
 
-/* restore painting image to previous state. Used for anchored and drag-dot style brushes*/
-static void paint_stroke_restore(void)
-{
-	ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE);
-	image_undo_restore_runtime(lb);
-	image_undo_invalidate();
-}
-
 static void paint_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr)
 {
 	PaintOperation *pop = paint_stroke_mode_data(stroke);
@@ -844,7 +513,8 @@ static void paint_stroke_update_step(bContext *C, struct PaintStroke *stroke, Po
 		BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * alphafac));
 
 	if ((brush->flag & BRUSH_DRAG_DOT) || (brush->flag & BRUSH_ANCHORED)) {
-		paint_stroke_restore();
+		UndoStack *ustack = CTX_wm_manager(C)->undo_stack;
+		ED_image_undo_restore(ustack->step_init);
 	}
 
 	if (pop->mode == PAINT_MODE_3D_PROJECT) {
@@ -921,7 +591,7 @@ static void paint_stroke_done(const bContext *C, struct PaintStroke *stroke)
 		WM_paint_cursor_end(CTX_wm_manager(C), pop->cursor);
 	}
 
-	image_undo_end();
+	ED_image_undo_push_end();
 
 	/* duplicate warning, see texpaint_init */
 #if 0
@@ -1376,21 +1046,19 @@ static int texture_paint_toggle_poll(bContext *C)
 
 static int texture_paint_toggle_exec(bContext *C, wmOperator *op)
 {
-	wmWindowManager *wm = CTX_wm_manager(C);
-	WorkSpace *workspace = CTX_wm_workspace(C);
 	Scene *scene = CTX_data_scene(C);
 	Object *ob = CTX_data_active_object(C);
 	const int mode_flag = OB_MODE_TEXTURE_PAINT;
-	const bool is_mode_set = (workspace->object_mode & mode_flag) != 0;
+	const bool is_mode_set = (ob->mode & mode_flag) != 0;
 
 	if (!is_mode_set) {
-		if (!ED_object_mode_compat_set(C, workspace, mode_flag, op->reports)) {
+		if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) {
 			return OPERATOR_CANCELLED;
 		}
 	}
 
-	if (workspace->object_mode & mode_flag) {
-		workspace->object_mode &= ~mode_flag;
+	if (ob->mode & mode_flag) {
+		ob->mode &= ~mode_flag;
 
 		if (U.glreslimit != 0)
 			GPU_free_images();
@@ -1440,7 +1108,7 @@ static int texture_paint_toggle_exec(bContext *C, wmOperator *op)
 			}
 		}
 		
-		workspace->object_mode |= mode_flag;
+		ob->mode |= mode_flag;
 
 		BKE_paint_init(scene, ePaintTextureProjective, PAINT_CURSOR_TEXTURE_PAINT);
 
@@ -1451,7 +1119,7 @@ static int texture_paint_toggle_exec(bContext *C, wmOperator *op)
 		toggle_paint_cursor(C, 1);
 	}
 
-	ED_workspace_object_mode_sync_from_object(wm, workspace, ob);
+	// ED_workspace_object_mode_sync_from_object(wm, workspace, ob);
 
 	GPU_drawobject_free(ob->derivedFinal);
 	WM_event_add_notifier(C, NC_SCENE | ND_MODE, scene);
@@ -1479,10 +1147,9 @@ static int brush_colors_flip_exec(bContext *C, wmOperator *UNUSED(op))
 {
 	UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings;
 
-	WorkSpace *workspace = CTX_wm_workspace(C);
 	Object *ob = CTX_data_active_object(C);
 	Brush *br;
-	if (!(ob && (workspace->object_mode & OB_MODE_VERTEX_PAINT))) {
+	if (!(ob && (ob->mode & OB_MODE_VERTEX_PAINT))) {
 		br = image_paint_brush(C);
 	}
 	else {
@@ -1513,11 +1180,8 @@ static int brush_colors_flip_poll(bContext *C)
 	}
 	else {
 		Object *ob = CTX_data_active_object(C);
-		if (ob) {
-			WorkSpace *workspace = CTX_wm_workspace(C);
-			if (workspace->object_mode & OB_MODE_VERTEX_PAINT) {
-				return 1;
-			}
+		if (ob && (ob->mode & OB_MODE_VERTEX_PAINT)) {
+			return 1;
 		}
 	}
 	return 0;
@@ -1541,15 +1205,17 @@ void PAINT_OT_brush_colors_flip(wmOperatorType *ot)
 
 void ED_imapaint_bucket_fill(struct bContext *C, float color[3], wmOperator *op)
 {
+	wmWindowManager *wm = CTX_wm_manager(C);
 	SpaceImage *sima = CTX_wm_space_image(C);
 	Image *ima = sima->image;
 
-	ED_undo_paint_push_begin(UNDO_PAINT_IMAGE, op->type->name,
-	                      ED_image_undo_restore, ED_image_undo_free, NULL);
+	BKE_undosys_step_push_init_with_type(wm->undo_stack, C, op->type->name, BKE_UNDOSYS_TYPE_IMAGE);
+
+	ED_image_undo_push_begin(op->type->name);
 
 	paint_2d_bucket_fill(C, color, NULL, NULL, NULL);
 
-	ED_undo_paint_push_end(UNDO_PAINT_IMAGE);
+	BKE_undosys_step_push(wm->undo_stack, C, op->type->name);
 
 	DEG_id_tag_update(&ima->id, 0);
 }
@@ -1557,12 +1223,10 @@ void ED_imapaint_bucket_fill(struct bContext *C, float color[3], wmOperator *op)
 
 static int texture_paint_poll(bContext *C)
 {
-	if (texture_paint_toggle_poll(C)) {
-		WorkSpace *workspace = CTX_wm_workspace(C);
-		if (workspace->object_mode & OB_MODE_TEXTURE_PAINT) {
+	if (texture_paint_toggle_poll(C))
+		if (CTX_data_active_object(C)->mode & OB_MODE_TEXTURE_PAINT)
 			return 1;
-		}
-	}
+	
 	return 0;
 }
 
@@ -1573,19 +1237,16 @@ int image_texture_paint_poll(bContext *C)
 
 int facemask_paint_poll(bContext *C)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
-	return BKE_paint_select_face_test(CTX_data_active_object(C), workspace->object_mode);
+	return BKE_paint_select_face_test(CTX_data_active_object(C));
 }
 
 int vert_paint_poll(bContext *C)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
-	return BKE_paint_select_vert_test(CTX_data_active_object(C), workspace->object_mode);
+	return BKE_paint_select_vert_test(CTX_data_active_object(C));
 }
 
 int mask_paint_poll(bContext *C)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
-	return BKE_paint_select_elem_test(CTX_data_active_object(C), workspace->object_mode);
+	return BKE_paint_select_elem_test(CTX_data_active_object(C));
 }
 
diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c
index 2ce7c51b6b4c61628e0147841c3185e4b2b23ab8..83a5a0d0b1ba6dd2fd3d28c86811e621a6faedfa 100644
--- a/source/blender/editors/sculpt_paint/paint_image_2d.c
+++ b/source/blender/editors/sculpt_paint/paint_image_2d.c
@@ -1036,6 +1036,8 @@ static void paint_2d_do_making_brush(ImagePaintState *s,
 	ImBuf tmpbuf;
 	IMB_initImBuf(&tmpbuf, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, 0);
 
+	ListBase *undo_tiles = ED_image_undo_get_tiles();
+
 	for (int ty = tiley; ty <= tileh; ty++) {
 		for (int tx = tilex; tx <= tilew; tx++) {
 			/* retrieve original pixels + mask from undo buffer */
@@ -1044,9 +1046,9 @@ static void paint_2d_do_making_brush(ImagePaintState *s,
 			int origy = region->desty - ty * IMAPAINT_TILE_SIZE;
 
 			if (s->canvas->rect_float)
-				tmpbuf.rect_float = image_undo_find_tile(s->image, s->canvas, tx, ty, &mask, false);
+				tmpbuf.rect_float = image_undo_find_tile(undo_tiles, s->image, s->canvas, tx, ty, &mask, false);
 			else
-				tmpbuf.rect = image_undo_find_tile(s->image, s->canvas, tx, ty, &mask, false);
+				tmpbuf.rect = image_undo_find_tile(undo_tiles, s->image, s->canvas, tx, ty, &mask, false);
 
 			IMB_rectblend(s->canvas, &tmpbuf, frombuf, mask,
 			              curveb, texmaskb, mask_max,
diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c
index ae5b825e4ae2491674db778378a80c0069b34123..18019597865c55c7ce0730b37dfedb63968fe252 100644
--- a/source/blender/editors/sculpt_paint/paint_image_proj.c
+++ b/source/blender/editors/sculpt_paint/paint_image_proj.c
@@ -1501,15 +1501,16 @@ static int project_paint_undo_subtiles(const TileInfo *tinf, int tx, int ty)
 
 
 	if (generate_tile) {
+		ListBase *undo_tiles = ED_image_undo_get_tiles();
 		volatile void *undorect;
 		if (tinf->masked) {
 			undorect = image_undo_push_tile(
-			        pjIma->ima, pjIma->ibuf, tinf->tmpibuf,
+			        undo_tiles, pjIma->ima, pjIma->ibuf, tinf->tmpibuf,
 			        tx, ty, &pjIma->maskRect[tile_index], &pjIma->valid[tile_index], true, false);
 		}
 		else {
 			undorect = image_undo_push_tile(
-			        pjIma->ima, pjIma->ibuf, tinf->tmpibuf,
+			        undo_tiles, pjIma->ima, pjIma->ibuf, tinf->tmpibuf,
 			        tx, ty, NULL, &pjIma->valid[tile_index], true, false);
 		}
 
@@ -5396,8 +5397,7 @@ static int texture_paint_camera_project_exec(bContext *C, wmOperator *op)
 
 	scene->toolsettings->imapaint.flag |= IMAGEPAINT_DRAWING;
 
-	ED_undo_paint_push_begin(UNDO_PAINT_IMAGE, op->type->name,
-	                         ED_image_undo_restore, ED_image_undo_free, NULL);
+	ED_image_undo_push_begin(op->type->name);
 
 	/* allocate and initialize spatial data structures */
 	project_paint_begin(C, &ps, false, 0);
@@ -5478,7 +5478,7 @@ static int texture_paint_image_from_view_exec(bContext *C, wmOperator *op)
 	ibuf = ED_view3d_draw_offscreen_imbuf(
 	        &eval_ctx, scene, view_layer, CTX_wm_view3d(C), CTX_wm_region(C),
 	        w, h, IB_rect, V3D_OFSDRAW_NONE, R_ALPHAPREMUL, 0, NULL,
-	        NULL, NULL, err_out);
+	        NULL, err_out);
 	if (!ibuf) {
 		/* Mostly happens when OpenGL offscreen buffer was failed to create, */
 		/* but could be other reasons. Should be handled in the future. nazgul */
@@ -5941,9 +5941,9 @@ static int add_simple_uvs_exec(bContext *C, wmOperator *UNUSED(op))
 
 static int add_simple_uvs_poll(bContext *C)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Object *ob = CTX_data_active_object(C);
-	if (!ob || (ob->type != OB_MESH) || (workspace->object_mode != OB_MODE_TEXTURE_PAINT)) {
+
+	if (!ob || ob->type != OB_MESH || ob->mode != OB_MODE_TEXTURE_PAINT) {
 		return false;
 	}
 	return true;
diff --git a/source/blender/editors/sculpt_paint/paint_image_undo.c b/source/blender/editors/sculpt_paint/paint_image_undo.c
new file mode 100644
index 0000000000000000000000000000000000000000..ade775d14e65ea87169d3a0fa071cc0f53081f2a
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/paint_image_undo.c
@@ -0,0 +1,535 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/sculpt_paint/paint_image_undo.c
+ *  \ingroup edsculpt
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+#include "BLI_blenlib.h"
+#include "BLI_utildefines.h"
+#include "BLI_threads.h"
+
+#include "DNA_image_types.h"
+#include "DNA_windowmanager_types.h"
+#include "DNA_object_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+#include "DNA_workspace_types.h"
+
+#include "IMB_imbuf.h"
+#include "IMB_imbuf_types.h"
+
+#include "BKE_context.h"
+#include "BKE_image.h"
+#include "BKE_main.h"
+#include "BKE_undo_system.h"
+
+#include "DEG_depsgraph.h"
+
+#include "ED_paint.h"
+#include "ED_undo.h"
+
+#include "GPU_draw.h"
+
+#include "paint_intern.h"
+
+/* -------------------------------------------------------------------- */
+/** \name Undo Conversion
+ * \{ */
+
+typedef struct UndoImageTile {
+	struct UndoImageTile *next, *prev;
+
+	char ibufname[IMB_FILENAME_SIZE];
+
+	union {
+		float        *fp;
+		unsigned int *uint;
+		void         *pt;
+	} rect;
+
+	unsigned short *mask;
+
+	int x, y;
+
+	Image *ima;
+	short source, use_float;
+	char gen_type;
+	bool valid;
+
+	size_t undo_size;
+} UndoImageTile;
+
+/* this is a static resource for non-globality,
+ * Maybe it should be exposed as part of the
+ * paint operation, but for now just give a public interface */
+static SpinLock undolock;
+
+void image_undo_init_locks(void)
+{
+	BLI_spin_init(&undolock);
+}
+
+void image_undo_end_locks(void)
+{
+	BLI_spin_end(&undolock);
+}
+
+/* UNDO */
+typedef enum {
+	COPY = 0,
+	RESTORE = 1,
+	RESTORE_COPY = 2
+} CopyMode;
+
+static void undo_copy_tile(UndoImageTile *tile, ImBuf *tmpibuf, ImBuf *ibuf, CopyMode mode)
+{
+	if (mode == COPY) {
+		/* copy or swap contents of tile->rect and region in ibuf->rect */
+		IMB_rectcpy(tmpibuf, ibuf, 0, 0, tile->x * IMAPAINT_TILE_SIZE,
+		            tile->y * IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE);
+
+		if (ibuf->rect_float) {
+			SWAP(float *, tmpibuf->rect_float, tile->rect.fp);
+		}
+		else {
+			SWAP(unsigned int *, tmpibuf->rect, tile->rect.uint);
+		}
+	}
+	else {
+		if (mode == RESTORE_COPY) {
+			IMB_rectcpy(tmpibuf, ibuf, 0, 0, tile->x * IMAPAINT_TILE_SIZE,
+			            tile->y * IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE);
+		}
+		/* swap to the tmpbuf for easy copying */
+		if (ibuf->rect_float) {
+			SWAP(float *, tmpibuf->rect_float, tile->rect.fp);
+		}
+		else {
+			SWAP(unsigned int *, tmpibuf->rect, tile->rect.uint);
+		}
+
+		IMB_rectcpy(ibuf, tmpibuf, tile->x * IMAPAINT_TILE_SIZE,
+		            tile->y * IMAPAINT_TILE_SIZE, 0, 0, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE);
+
+		if (mode == RESTORE) {
+			if (ibuf->rect_float) {
+				SWAP(float *, tmpibuf->rect_float, tile->rect.fp);
+			}
+			else {
+				SWAP(unsigned int *, tmpibuf->rect, tile->rect.uint);
+			}
+		}
+	}
+}
+
+void *image_undo_find_tile(
+        ListBase *undo_tiles,
+        Image *ima, ImBuf *ibuf, int x_tile, int y_tile, unsigned short **mask, bool validate)
+{
+	UndoImageTile *tile;
+	short use_float = ibuf->rect_float ? 1 : 0;
+
+	for (tile = undo_tiles->first; tile; tile = tile->next) {
+		if (tile->x == x_tile && tile->y == y_tile && ima->gen_type == tile->gen_type && ima->source == tile->source) {
+			if (tile->use_float == use_float) {
+				if (STREQ(tile->ibufname, ibuf->name)) {
+					if (mask) {
+						/* allocate mask if requested */
+						if (!tile->mask) {
+							tile->mask = MEM_callocN(sizeof(unsigned short) * IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE,
+							                         "UndoImageTile.mask");
+						}
+
+						*mask = tile->mask;
+					}
+					if (validate) {
+						tile->valid = true;
+					}
+					return tile->rect.pt;
+				}
+			}
+		}
+	}
+
+	return NULL;
+}
+
+void *image_undo_push_tile(
+        ListBase *undo_tiles,
+        Image *ima, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile, int y_tile,
+        unsigned short **mask, bool **valid, bool proj, bool find_prev)
+{
+	UndoImageTile *tile;
+	int allocsize;
+	short use_float = ibuf->rect_float ? 1 : 0;
+	void *data;
+
+	/* check if tile is already pushed */
+
+	/* in projective painting we keep accounting of tiles, so if we need one pushed, just push! */
+	if (find_prev) {
+		data = image_undo_find_tile(undo_tiles, ima, ibuf, x_tile, y_tile, mask, true);
+		if (data) {
+			return data;
+		}
+	}
+
+	if (*tmpibuf == NULL) {
+		*tmpibuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, IB_rectfloat | IB_rect);
+	}
+
+	tile = MEM_callocN(sizeof(UndoImageTile), "UndoImageTile");
+	tile->x = x_tile;
+	tile->y = y_tile;
+
+	/* add mask explicitly here */
+	if (mask) {
+		*mask = tile->mask = MEM_callocN(sizeof(unsigned short) * IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE,
+		                         "UndoImageTile.mask");
+	}
+	allocsize = IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE * 4;
+	allocsize *= (ibuf->rect_float) ? sizeof(float) : sizeof(char);
+	tile->rect.pt = MEM_mapallocN(allocsize, "UndeImageTile.rect");
+
+	BLI_strncpy(tile->ibufname, ibuf->name, sizeof(tile->ibufname));
+
+	tile->gen_type = ima->gen_type;
+	tile->source = ima->source;
+	tile->use_float = use_float;
+	tile->valid = true;
+	tile->ima = ima;
+
+	if (valid) {
+		*valid = &tile->valid;
+	}
+	undo_copy_tile(tile, *tmpibuf, ibuf, COPY);
+
+	if (proj) {
+		BLI_spin_lock(&undolock);
+	}
+	BLI_addtail(undo_tiles, tile);
+
+	if (proj) {
+		BLI_spin_unlock(&undolock);
+	}
+	return tile->rect.pt;
+}
+
+void image_undo_remove_masks(void)
+{
+	ListBase *undo_tiles = ED_image_undo_get_tiles();
+	UndoImageTile *tile;
+
+	for (tile = undo_tiles->first; tile; tile = tile->next) {
+		if (tile->mask) {
+			MEM_freeN(tile->mask);
+			tile->mask = NULL;
+		}
+	}
+}
+
+static void image_undo_restore_runtime(ListBase *lb)
+{
+	ImBuf *ibuf, *tmpibuf;
+	UndoImageTile *tile;
+
+	tmpibuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32,
+	                         IB_rectfloat | IB_rect);
+
+	for (tile = lb->first; tile; tile = tile->next) {
+		Image *ima = tile->ima;
+		ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
+
+		undo_copy_tile(tile, tmpibuf, ibuf, RESTORE);
+
+		GPU_free_image(ima); /* force OpenGL reload (maybe partial update will operate better?) */
+		if (ibuf->rect_float) {
+			ibuf->userflags |= IB_RECT_INVALID; /* force recreate of char rect */
+		}
+		if (ibuf->mipmap[0]) {
+			ibuf->userflags |= IB_MIPMAP_INVALID;  /* force mipmap recreatiom */
+		}
+		ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
+
+		BKE_image_release_ibuf(ima, ibuf, NULL);
+	}
+
+	IMB_freeImBuf(tmpibuf);
+}
+
+static void image_undo_restore_list(ListBase *lb, struct UndoIDPtrMap *id_map)
+{
+	ImBuf *tmpibuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, IB_rectfloat | IB_rect);
+
+	/* Store last found image. */
+	ID *image_prev[2] = {NULL};
+
+	for (UndoImageTile *tile = lb->first; tile; tile = tile->next) {
+		short use_float;
+
+		Image *ima = (Image *)BKE_undosys_ID_map_lookup_with_prev(id_map, &tile->ima->id, image_prev);
+
+		ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
+
+		if (ima && ibuf && !STREQ(tile->ibufname, ibuf->name)) {
+			/* current ImBuf filename was changed, probably current frame
+			 * was changed when painting on image sequence, rather than storing
+			 * full image user (which isn't so obvious, btw) try to find ImBuf with
+			 * matched file name in list of already loaded images */
+
+			BKE_image_release_ibuf(ima, ibuf, NULL);
+
+			ibuf = BKE_image_get_ibuf_with_name(ima, tile->ibufname);
+		}
+
+		if (!ima || !ibuf || !(ibuf->rect || ibuf->rect_float)) {
+			BKE_image_release_ibuf(ima, ibuf, NULL);
+			continue;
+		}
+
+		if (ima->gen_type != tile->gen_type || ima->source != tile->source) {
+			BKE_image_release_ibuf(ima, ibuf, NULL);
+			continue;
+		}
+
+		use_float = ibuf->rect_float ? 1 : 0;
+
+		if (use_float != tile->use_float) {
+			BKE_image_release_ibuf(ima, ibuf, NULL);
+			continue;
+		}
+
+		undo_copy_tile(tile, tmpibuf, ibuf, RESTORE_COPY);
+
+		GPU_free_image(ima); /* force OpenGL reload */
+		if (ibuf->rect_float) {
+			ibuf->userflags |= IB_RECT_INVALID; /* force recreate of char rect */
+		}
+		if (ibuf->mipmap[0]) {
+			ibuf->userflags |= IB_MIPMAP_INVALID;  /* force mipmap recreatiom */
+		}
+		ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
+
+		DEG_id_tag_update(&ima->id, 0);
+
+		BKE_image_release_ibuf(ima, ibuf, NULL);
+	}
+
+	IMB_freeImBuf(tmpibuf);
+}
+
+static void image_undo_free_list(ListBase *lb)
+{
+	UndoImageTile *tile;
+
+	for (tile = lb->first; tile; tile = tile->next) {
+		MEM_freeN(tile->rect.pt);
+	}
+}
+
+void ED_image_undo_push_begin(const char *name)
+{
+	UndoStack *ustack = ED_undo_stack_get();
+	bContext *C = NULL; /* special case, we never read from this. */
+	BKE_undosys_step_push_init_with_type(ustack, C, name, BKE_UNDOSYS_TYPE_IMAGE);
+}
+
+void ED_image_undo_push_end(void)
+{
+	UndoStack *ustack = ED_undo_stack_get();
+	BKE_undosys_step_push(ustack, NULL, NULL);
+}
+
+static void image_undo_invalidate(void)
+{
+	UndoImageTile *tile;
+	ListBase *lb = ED_image_undo_get_tiles();
+
+	for (tile = lb->first; tile; tile = tile->next) {
+		tile->valid = false;
+	}
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Implements ED Undo System
+ * \{ */
+
+typedef struct ImageUndoStep {
+	UndoStep step;
+	ListBase tiles;
+
+	/* Use for all ID lookups (can be NULL). */
+	struct UndoIDPtrMap *id_map;
+} ImageUndoStep;
+
+static void image_undosys_step_encode_store_ids(ImageUndoStep *us)
+{
+	us->id_map = BKE_undosys_ID_map_create();
+
+	ID *image_prev = NULL;
+	for (UndoImageTile *tile = us->tiles.first; tile; tile = tile->next) {
+		BKE_undosys_ID_map_add_with_prev(us->id_map, &tile->ima->id, &image_prev);
+	}
+}
+
+/* Restore at runtime. */
+#if 0
+static void paint_undosys_step_decode_restore_ids(ImageUndoStep *us)
+{
+	ID *image_prev[2] = {NULL};
+	for (UndoImageTile *tile = us->tiles.first; tile; tile = tile->next) {
+		tile->ima = (Image *)BKE_undosys_ID_map_lookup_with_prev(us->id_map, &tile->ima->id, image_prev);
+	}
+}
+#endif
+
+static bool image_undosys_poll(bContext *C)
+{
+	Object *obact = CTX_data_active_object(C);
+
+	ScrArea *sa = CTX_wm_area(C);
+	if (sa && (sa->spacetype == SPACE_IMAGE)) {
+		SpaceImage *sima = (SpaceImage *)sa->spacedata.first;
+		if ((obact && (obact->mode & OB_MODE_TEXTURE_PAINT)) || (sima->mode == SI_MODE_PAINT)) {
+			return true;
+		}
+	}
+	else if (sa && (sa->spacetype == SPACE_VIEW3D)) {
+		if (obact && (obact->mode & OB_MODE_TEXTURE_PAINT)) {
+			return true;
+		}
+	}
+	return false;
+}
+
+static void image_undosys_step_encode_init(struct bContext *UNUSED(C), UndoStep *us_p)
+{
+	ImageUndoStep *us = (ImageUndoStep *)us_p;
+	/* dummy, memory is cleared anyway. */
+	BLI_listbase_clear(&us->tiles);
+}
+
+static bool image_undosys_step_encode(struct bContext *UNUSED(C), UndoStep *us_p)
+{
+	/* dummy, encoding is done along the way by adding tiles
+	 * to the current 'ImageUndoStep' added by encode_init. */
+	ImageUndoStep *us = (ImageUndoStep *)us_p;
+
+	BLI_assert(us->step.data_size == 0);
+
+	int allocsize = IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE * 4;
+
+
+	/* first dispose of invalid tiles (may happen due to drag dot for instance) */
+	for (UndoImageTile *tile = us->tiles.first; tile;) {
+		if (!tile->valid) {
+			UndoImageTile *tmp_tile = tile->next;
+			MEM_freeN(tile->rect.pt);
+			BLI_freelinkN(&us->tiles, tile);
+			tile = tmp_tile;
+		}
+		else {
+			us->step.data_size += allocsize * ((tile->use_float) ? sizeof(float) : sizeof(char));
+			tile = tile->next;
+		}
+	}
+
+	image_undosys_step_encode_store_ids(us);
+
+	return true;
+}
+
+static void image_undosys_step_decode(struct bContext *UNUSED(C), UndoStep *us_p, int UNUSED(dir))
+{
+	ImageUndoStep *us = (ImageUndoStep *)us_p;
+#if 0
+	paint_undosys_step_decode_restore_ids(us);
+#endif
+	image_undo_restore_list(&us->tiles, us->id_map);
+}
+
+static void image_undosys_step_free(UndoStep *us_p)
+{
+	ImageUndoStep *us = (ImageUndoStep *)us_p;
+	image_undo_free_list(&us->tiles);
+	BKE_undosys_ID_map_destroy(us->id_map);
+}
+
+static void image_undosys_foreach_ID_ref(
+        UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
+{
+	ImageUndoStep *us = (ImageUndoStep *)us_p;
+	if (us->id_map != NULL) {
+		BKE_undosys_ID_map_foreach_ID_ref(us->id_map, foreach_ID_ref_fn, user_data);
+	}
+}
+
+/* Export for ED_undo_sys. */
+void ED_image_undosys_type(UndoType *ut)
+{
+	ut->name = "Image";
+	ut->poll = image_undosys_poll;
+	ut->step_encode_init = image_undosys_step_encode_init;
+	ut->step_encode = image_undosys_step_encode;
+	ut->step_decode = image_undosys_step_decode;
+	ut->step_free = image_undosys_step_free;
+
+	ut->step_foreach_ID_ref = image_undosys_foreach_ID_ref;
+
+	ut->mode = BKE_UNDOTYPE_MODE_ACCUMULATE;
+	ut->use_context = true;
+
+	ut->step_size = sizeof(ImageUndoStep);
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+/** \name Utilities
+ * \{ */
+
+ListBase *ED_image_undosys_step_get_tiles(UndoStep *us_p)
+{
+	ImageUndoStep *us = (ImageUndoStep *)us_p;
+	return &us->tiles;
+}
+
+ListBase *ED_image_undo_get_tiles(void)
+{
+	UndoStack *ustack = ED_undo_stack_get();
+	UndoStep *us = BKE_undosys_stack_init_or_active_with_type(ustack, BKE_UNDOSYS_TYPE_IMAGE);
+	return ED_image_undosys_step_get_tiles(us);
+}
+
+/* restore painting image to previous state. Used for anchored and drag-dot style brushes*/
+void ED_image_undo_restore(UndoStep *us)
+{
+	ListBase *lb = ED_image_undosys_step_get_tiles(us);
+	image_undo_restore_runtime(lb);
+	image_undo_invalidate();
+}
+
+/** \} */
diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h
index 28d9dfe13b07e1520f7fdf136adcd5b5f808a90a..e22b996c6e5e72970c3f55f1aa880fb01f184b11 100644
--- a/source/blender/editors/sculpt_paint/paint_intern.h
+++ b/source/blender/editors/sculpt_paint/paint_intern.h
@@ -43,6 +43,7 @@ struct MTex;
 struct Object;
 struct PaintStroke;
 struct Paint;
+struct PaintCurve;
 struct PointerRNA;
 struct rcti;
 struct Scene;
@@ -54,6 +55,7 @@ struct wmOperator;
 struct wmOperatorType;
 struct wmWindowManager;
 struct DMCoNo;
+struct UndoStep;
 enum ePaintMode;
 
 /* paint_stroke.c */
@@ -176,12 +178,6 @@ typedef struct ImagePaintPartialRedraw {
 #define IMAPAINT_TILE_NUMBER(size)  (((size) + IMAPAINT_TILE_SIZE - 1) >> IMAPAINT_TILE_BITS)
 
 int image_texture_paint_poll(struct bContext *C);
-void *image_undo_find_tile(struct Image *ima, struct ImBuf *ibuf, int x_tile, int y_tile, unsigned short **mask, bool validate);
-void *image_undo_push_tile(struct Image *ima, struct ImBuf *ibuf, struct ImBuf **tmpibuf, int x_tile, int y_tile,  unsigned short **, bool **valid, bool proj, bool find_prev);
-void image_undo_remove_masks(void);
-void image_undo_init_locks(void);
-void image_undo_end_locks(void);
-
 void imapaint_image_update(struct SpaceImage *sima, struct Image *image, struct ImBuf *ibuf, short texpaint);
 struct ImagePaintPartialRedraw *get_imapaintpartial(void);
 void set_imapaintpartial(struct ImagePaintPartialRedraw *ippr);
@@ -190,15 +186,25 @@ int get_imapaint_zoom(struct bContext *C, float *zoomx, float *zoomy);
 void *paint_2d_new_stroke(struct bContext *, struct wmOperator *, int mode);
 void paint_2d_redraw(const struct bContext *C, void *ps, bool final);
 void paint_2d_stroke_done(void *ps);
-void paint_2d_stroke(void *ps, const float prev_mval[2], const float mval[2], const bool eraser, float pressure, float distance, float size);
-void paint_2d_bucket_fill(const struct bContext *C, const float color[3], struct Brush *br, const float mouse_init[2], void *ps);
-void paint_2d_gradient_fill(const struct bContext *C, struct Brush *br, const float mouse_init[2], const float mouse_final[2], void *ps);
-void *paint_proj_new_stroke(struct bContext *C, struct Object *ob, const float mouse[2], int mode);
-void paint_proj_stroke(const struct bContext *C, void *ps, const float prevmval_i[2], const float mval_i[2], const bool eraser, float pressure, float distance, float size);
+void paint_2d_stroke(
+        void *ps, const float prev_mval[2], const float mval[2],
+        const bool eraser, float pressure, float distance, float size);
+void paint_2d_bucket_fill(
+        const struct bContext *C, const float color[3], struct Brush *br, const float mouse_init[2], void *ps);
+void paint_2d_gradient_fill(
+        const struct bContext *C, struct Brush *br, const float mouse_init[2], const float mouse_final[2], void *ps);
+void *paint_proj_new_stroke(
+        struct bContext *C, struct Object *ob, const float mouse[2], int mode);
+void paint_proj_stroke(
+        const struct bContext *C, void *ps, const float prevmval_i[2], const float mval_i[2],
+        const bool eraser, float pressure, float distance, float size);
 void paint_proj_redraw(const struct bContext *C, void *pps, bool final);
 void paint_proj_stroke_done(void *ps);
 
-void paint_brush_color_get(struct Scene *scene, struct Brush *br, bool color_correction, bool invert, float distance, float pressure, float color[3], struct ColorManagedDisplay *display);
+void paint_brush_color_get(
+        struct Scene *scene, struct Brush *br,
+        bool color_correction, bool invert, float distance, float pressure, float color[3],
+        struct ColorManagedDisplay *display);
 bool paint_use_opacity_masking(struct Brush *brush);
 void paint_brush_init_tex(struct Brush *brush);
 void paint_brush_exit_tex(struct Brush *brush);
@@ -214,7 +220,23 @@ void PAINT_OT_delete_texture_paint_slot(struct wmOperatorType *ot);
 void PAINT_OT_image_paint(struct wmOperatorType *ot);
 void PAINT_OT_add_simple_uvs(struct wmOperatorType *ot);
 
-/* uv sculpting */
+/* paint_image_undo.c */
+void *image_undo_find_tile(
+        ListBase *undo_tiles,
+        struct Image *ima, struct ImBuf *ibuf, int x_tile, int y_tile,
+        unsigned short **mask, bool validate);
+void *image_undo_push_tile(
+        ListBase *undo_tiles,
+        struct Image *ima, struct ImBuf *ibuf, struct ImBuf **tmpibuf, int x_tile, int y_tile,
+        unsigned short **, bool **valid, bool proj, bool find_prev);
+void image_undo_remove_masks(void);
+void image_undo_init_locks(void);
+void image_undo_end_locks(void);
+
+struct ListBase *ED_image_undosys_step_get_tiles(struct UndoStep *us_p);
+struct ListBase *ED_image_undo_get_tiles(void);
+
+/* sculpt_uv.c */
 int uv_sculpt_poll(struct bContext *C);
 int uv_sculpt_keymap_poll(struct bContext *C);
 
@@ -287,10 +309,6 @@ typedef enum {
 void set_brush_rc_props(struct PointerRNA *ptr, const char *paint, const char *prop, const char *secondary_prop,
                         RCFlags flags);
 
-/* paint_undo.c */
-struct ListBase *undo_paint_push_get_list(int type);
-void undo_paint_push_count_alloc(int type, int size);
-
 /* paint_hide.c */
 
 typedef enum {
@@ -327,6 +345,9 @@ void PAINTCURVE_OT_slide(struct wmOperatorType *ot);
 void PAINTCURVE_OT_draw(struct wmOperatorType *ot);
 void PAINTCURVE_OT_cursor(struct wmOperatorType *ot);
 
+/* paint_curve_undo.c */
+void ED_paintcurve_undo_push(struct bContext *C, struct wmOperator *op, struct PaintCurve *pc);
+
 /* image painting blur kernel */
 typedef struct {
 	float *wdata; /* actual kernel */
diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c
index c031a7336304bec02b7fa3c562ab69f2244cfe92..004d2757a71c4e0e2ef32e7d97980f639947c46b 100644
--- a/source/blender/editors/sculpt_paint/paint_ops.c
+++ b/source/blender/editors/sculpt_paint/paint_ops.c
@@ -40,8 +40,6 @@
 #include "BKE_paint.h"
 #include "BKE_main.h"
 
-#include "DEG_depsgraph.h"
-
 #include "ED_paint.h"
 #include "ED_screen.h"
 #include "ED_image.h"
@@ -262,9 +260,7 @@ static void PALETTE_OT_color_delete(wmOperatorType *ot)
 }
 
 static int brush_reset_exec(bContext *C, wmOperator *UNUSED(op))
-
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Paint *paint = BKE_paint_get_active_from_context(C);
 	Brush *brush = BKE_paint_brush(paint);
 	Object *ob = CTX_data_active_object(C);
@@ -272,7 +268,7 @@ static int brush_reset_exec(bContext *C, wmOperator *UNUSED(op))
 	if (!ob || !brush) return OPERATOR_CANCELLED;
 
 	/* TODO: other modes */
-	if (workspace->object_mode & OB_MODE_SCULPT) {
+	if (ob->mode & OB_MODE_SCULPT) {
 		BKE_brush_sculpt_reset(brush);
 	}
 	else {
@@ -405,7 +401,6 @@ static int brush_generic_tool_set(Main *bmain, Paint *paint, const int tool,
 
 static int brush_select_exec(bContext *C, wmOperator *op)
 {
-	WorkSpace *workspace = CTX_wm_workspace(C);
 	Main *bmain = CTX_data_main(C);
 	ToolSettings *toolsettings = CTX_data_tool_settings(C);
 	Paint *paint = NULL;
@@ -419,7 +414,7 @@ static int brush_select_exec(bContext *C, wmOperator *op)
 		Object *ob = CTX_data_active_object(C);
 		if (ob) {
 			/* select current paint mode */
-			paint_mode = workspace->object_mode & OB_MODE_ALL_PAINT;
+			paint_mode = ob->mode & OB_MODE_ALL_PAINT;
 		}
 		else {
 			return OPERATOR_CANCELLED;
diff --git a/source/blender/editors/sculpt_paint/paint_undo.c b/source/blender/editors/sculpt_paint/paint_undo.c
deleted file mode 100644
index 27d3f6648a219bc90b919ded3c3f3005480034f5..0000000000000000000000000000000000000000
--- a/source/blender/editors/sculpt_paint/paint_undo.c
+++ /dev/null
@@ -1,410 +0,0 @@
-/*
- * ***** BEGIN GPL LICENSE BLOCK *****
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * ***** END GPL LICENSE BLOCK *****
- */
-
-/** \file blender/editors/sculpt_paint/paint_undo.c
- *  \ingroup edsculpt
- *  \brief Undo system for painting and sculpting.
- */
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "MEM_guardedalloc.h"
-
-#include "BLI_listbase.h"
-#include "BLI_utildefines.h"
-#include "BLI_string.h"
-
-#include "DNA_userdef_types.h"
-
-#include "BKE_blender_undo.h"
-#include "BKE_context.h"
-#include "BKE_global.h"
-
-#include "ED_paint.h"
-
-#include "paint_intern.h"
-
-typedef struct UndoElem {
-	struct UndoElem *next, *prev;
-	char name[BKE_UNDO_STR_MAX];
-	uintptr_t undosize;
-
-	ListBase elems;
-
-	UndoRestoreCb restore;
-	UndoFreeCb free;
-	UndoCleanupCb cleanup;
-} UndoElem;
-
-typedef struct UndoStack {
-	int type;
-	ListBase elems;
-	UndoElem *current;
-} UndoStack;
-
-static UndoStack ImageUndoStack = {UNDO_PAINT_IMAGE, {NULL, NULL}, NULL};
-static UndoStack MeshUndoStack = {UNDO_PAINT_MESH, {NULL, NULL}, NULL};
-
-/* Generic */
-
-static void undo_restore(bContext *C, UndoStack *UNUSED(stack), UndoElem *uel)
-{
-	if (uel && uel->restore)
-		uel->restore(C, &uel->elems);
-}
-
-static void undo_elem_free(UndoStack *UNUSED(stack), UndoElem *uel)
-{
-	if (uel && uel->free) {
-		uel->free(&uel->elems);
-		BLI_freelistN(&uel->elems);
-	}
-}
-
-static void undo_stack_push_begin(UndoStack *stack, const char *name, UndoRestoreCb restore, UndoFreeCb free, UndoCleanupCb cleanup)
-{
-	UndoElem *uel;
-	int nr;
-	
-	/* Undo push is split up in begin and end, the reason is that as painting
-	 * happens more tiles/nodes are added to the list, and at the very end we
-	 * know how much memory the undo used to remove old undo elements */
-
-	/* remove all undos after (also when stack->current==NULL) */
-	while (stack->elems.last != stack->current) {
-		uel = stack->elems.last;
-		undo_elem_free(stack, uel);
-		BLI_freelinkN(&stack->elems, uel);
-	}
-	
-	/* make new */
-	stack->current = uel = MEM_callocN(sizeof(UndoElem), "undo file");
-	uel->restore = restore;
-	uel->free = free;
-	uel->cleanup = cleanup;
-	BLI_addtail(&stack->elems, uel);
-
-	/* name can be a dynamic string */
-	BLI_strncpy(uel->name, name, sizeof(uel->name));
-	
-	/* limit amount to the maximum amount*/
-	nr = 0;
-	uel = stack->elems.last;
-	while (uel) {
-		nr++;
-		if (nr == U.undosteps) break;
-		uel = uel->prev;
-	}
-	if (uel) {
-		while (stack->elems.first != uel) {
-			UndoElem *first = stack->elems.first;
-			undo_elem_free(stack, first);
-			BLI_freelinkN(&stack->elems, first);
-		}
-	}
-}
-
-static void undo_stack_push_end(UndoStack *stack)
-{
-	UndoElem *uel;
-	uintptr_t totmem, maxmem;
-	int totundo = 0;
-
-	/* first limit to undo steps */
-	uel = stack->elems.last;
-
-	while (uel) {
-		totundo++;
-		if (totundo > U.undosteps) break;
-		uel = uel->prev;
-	}
-
-	if (uel) {
-		UndoElem *first;
-
-		/* in case the undo steps are zero, the current pointer will be invalid */
-		if (uel == stack->current)
-			stack->current = NULL;
-
-		do {
-			first = stack->elems.first;
-			undo_elem_free(stack, first);
-			BLI_freelinkN(&stack->elems, first);
-		} while (first != uel);
-	}
-
-	if (U.undomemory != 0) {
-		/* limit to maximum memory (afterwards, we can't know in advance) */
-		totmem = 0;
-		maxmem = ((uintptr_t)U.undomemory) * 1024 * 1024;
-
-		uel = stack->elems.last;
-		while (uel) {
-			totmem += uel->undosize;
-			if (totmem > maxmem) break;
-			uel = uel->prev;
-		}
-
-		if (uel) {
-			while (stack->elems.first != uel) {
-				UndoElem *first = stack->elems.first;
-				undo_elem_free(stack, first);
-				BLI_freelinkN(&stack->elems, first);
-			}
-		}
-	}
-}
-
-static void undo_stack_cleanup(UndoStack *stack, bContext *C)
-{
-	UndoElem *uel = stack->elems.first;
-	bool stack_reset = false;
-
-	while (uel) {
-		if (uel->cleanup && uel->cleanup(C, &uel->elems)) {
-			UndoElem *uel_tmp = uel->next;
-			if (stack->current == uel) {
-				stack->current = NULL;
-				stack_reset = true;
-			}
-			undo_elem_free(stack, uel);
-			BLI_freelinkN(&stack->elems, uel);
-			uel = uel_tmp;
-		}
-		else
-			uel = uel->next;
-	}
-	if (stack_reset) {
-		stack->current = stack->elems.last;
-	}
-
-}
-
-static int undo_stack_step(bContext *C, UndoStack *stack, int step, const char *name)
-{
-	UndoElem *undo;
-
-	/* first cleanup any old undo steps that may belong to invalid data */
-	undo_stack_cleanup(stack, C);
-
-	if (step == 1) {
-		if (stack->current == NULL) {
-			/* pass */
-		}
-		else {
-			if (!name || STREQ(stack->current->name, name)) {
-				if (G.debug & G_DEBUG_WM) {
-					printf("%s: undo '%s'\n", __func__, stack->current->name);
-				}
-				undo_restore(C, stack, stack->current);
-				stack->current = stack->current->prev;
-				return 1;
-			}
-		}
-	}
-	else if (step == -1) {
-		if ((stack->current != NULL && stack->current->next == NULL) || BLI_listbase_is_empty(&stack->elems)) {
-			/* pass */
-		}
-		else {
-			if (!name || STREQ(stack->current->name, name)) {
-				undo = (stack->current && stack->current->next) ? stack->current->next : stack->elems.first;
-				undo_restore(C, stack, undo);
-				stack->current = undo;
-				if (G.debug & G_DEBUG_WM) {
-					printf("%s: redo %s\n", __func__, undo->name);
-				}
-				return 1;
-			}
-		}
-	}
-
-	return 0;
-}
-
-static void undo_stack_free(UndoStack *stack)
-{
-	UndoElem *uel;
-	
-	for (uel = stack->elems.first; uel; uel = uel->next)
-		undo_elem_free(stack, uel);
-
-	BLI_freelistN(&stack->elems);
-	stack->current = NULL;
-}
-
-/* Exported Functions */
-
-void ED_undo_paint_push_begin(int type, const char *name, UndoRestoreCb restore, UndoFreeCb free, UndoCleanupCb cleanup)
-{
-	if (type == UNDO_PAINT_IMAGE)
-		undo_stack_push_begin(&ImageUndoStack, name, restore, free, cleanup);
-	else if (type == UNDO_PAINT_MESH)
-		undo_stack_push_begin(&MeshUndoStack, name, restore, free, cleanup);
-}
-
-ListBase *undo_paint_push_get_list(int type)
-{
-	if (type == UNDO_PAINT_IMAGE) {
-		if (ImageUndoStack.current) {
-			return &ImageUndoStack.current->elems;
-		}
-	}
-	else if (type == UNDO_PAINT_MESH) {
-		if (MeshUndoStack.current) {
-			return &MeshUndoStack.current->elems;
-		}
-	}
-	
-	return NULL;
-}
-
-void undo_paint_push_count_alloc(int type, int size)
-{
-	if (type == UNDO_PAINT_IMAGE)
-		ImageUndoStack.current->undosize += size;
-	else if (type == UNDO_PAINT_MESH)
-		MeshUndoStack.current->undosize += size;
-}
-
-void ED_undo_paint_push_end(int type)
-{
-	if (type == UNDO_PAINT_IMAGE)
-		undo_stack_push_end(&ImageUndoStack);
-	else if (type == UNDO_PAINT_MESH)
-		undo_stack_push_end(&MeshUndoStack);
-}
-
-int ED_undo_paint_step(bContext *C, int type, int step, const char *name)
-{
-	if (type == UNDO_PAINT_IMAGE)
-		return undo_stack_step(C, &ImageUndoStack, step, name);
-	else if (type == UNDO_PAINT_MESH)
-		return undo_stack_step(C, &MeshUndoStack, step, name);
-	
-	return 0;
-}
-
-static void undo_step_num(bContext *C, UndoStack *stack, int step)
-{
-	UndoElem *uel;
-	int a = 0;
-	int curnum = BLI_findindex(&stack->elems, stack->current);
-
-	for (uel = stack->elems.first; uel; uel = uel->next, a++) {
-		if (a == step) break;
-	}
-
-	if (curnum > a) {
-		while (a++ != curnum)
-			undo_stack_step(C, stack, 1, NULL);
-	}
-	else if (curnum < a) {
-		while (a-- != curnum)
-			undo_stack_step(C, stack, -1, NULL);
-	}
-}
-
-void ED_undo_paint_step_num(bContext *C, int type, int step)
-{
-	if (type == UNDO_PAINT_IMAGE)
-		undo_step_num(C, &ImageUndoStack, step);
-	else if (type == UNDO_PAINT_MESH)
-		undo_step_num(C, &MeshUndoStack, step);
-}
-
-static char *undo_stack_get_name(UndoStack *stack, int nr, bool *r_active)
-{
-	UndoElem *uel;
-
-	if (r_active) *r_active = false;
-
-	uel = BLI_findlink(&stack->elems, nr);
-	if (uel) {
-		if (r_active && (uel == stack->current)) {
-			*r_active = true;
-		}
-		return uel->name;
-	}
-
-	return NULL;
-}
-
-const char *ED_undo_paint_get_name(bContext *C, int type, int nr, bool *r_active)
-{
-
-	if (type == UNDO_PAINT_IMAGE) {
-		undo_stack_cleanup(&ImageUndoStack, C);
-		return undo_stack_get_name(&ImageUndoStack, nr, r_active);
-	}
-	else if (type == UNDO_PAINT_MESH) {
-		undo_stack_cleanup(&MeshUndoStack, C);
-		return undo_stack_get_name(&MeshUndoStack, nr, r_active);
-	}
-	return NULL;
-}
-
-bool ED_undo_paint_empty(int type)
-{
-	UndoStack *stack;
-
-	if (type == UNDO_PAINT_IMAGE)
-		stack = &ImageUndoStack;
-	else if (type == UNDO_PAINT_MESH)
-		stack = &MeshUndoStack;
-	else
-		return true;
-
-	if (stack->current == NULL) {
-		return true;
-	}
-
-	return false;
-}
-
-bool ED_undo_paint_is_valid(int type, const char *name)
-{
-	UndoStack *stack;
-	
-	if (type == UNDO_PAINT_IMAGE)
-		stack = &ImageUndoStack;
-	else if (type == UNDO_PAINT_MESH)
-		stack = &MeshUndoStack;
-	else 
-		return 0;
-	
-	if (stack->current == NULL) {
-		/* pass */
-	}
-	else {
-		if (name && STREQ(stack->current->name, name))
-			return 1;
-		else
-			return stack->elems.first != stack->elems.last;
-	}
-	return 0;
-}
-
-void ED_undo_paint_free(void)
-{
-	undo_stack_free(&ImageUndoStack);
-	undo_stack_free(&MeshUndoStack);
-}
diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c
index a45e33e46540179c2a6b079dffd26280aef1b84e..0f22973c45d9741fdf6eb76b6643705855d1b835 100644
--- a/source/blender/editors/sculpt_paint/paint_utils.c
+++ b/source/blender/editors/sculpt_paint/paint_utils.c
@@ -566,11 +566,10 @@ static int brush_curve_preset_exec(bContext *C, wmOperator *op)
 	Brush *br = BKE_paint_brush(BKE_paint_get_active_from_context(C));
 
 	if (br) {
-		const WorkSpace *workspace = CTX_wm_workspace(C);
 		Scene *scene = CTX_data_scene(C);
 		ViewLayer *view_layer = CTX_data_view_layer(C);
 		BKE_brush_curve_preset(br, RNA_enum_get(op->ptr, "shape"));
-		BKE_paint_invalidate_cursor_overlay(scene, view_layer, br->curve, workspace->object_mode);
+		BKE_paint_invalidate_cursor_overlay(scene, view_layer, br->curve);
 	}
 
 	return OPERATOR_FINISHED;
diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c
index f8ad943ffefb42165024033fa2fd4d42b051b059..12fe55fb9569a9c5f1d98f37a25a32fe047faf51 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex.c
@@ -201,12 +201,9 @@ static void paint_last_stroke_update(Scene *scene, ARegion *ar, const float mval
 /* Returns true if vertex paint mode is active */
 int vertex_paint_mode_poll(bContext *C)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Object *ob = CTX_data_active_object(C);
-	return (ob &&
-	        (ob->type == OB_MESH) &&
-	        ((Mesh *)ob->data)->totpoly &&
-	        (workspace->object_mode & OB_MODE_VERTEX_PAINT));
+
+	return ob && ob->mode == OB_MODE_VERTEX_PAINT && ((Mesh *)ob->data)->totpoly;
 }
 
 int vertex_paint_poll(bContext *C)
@@ -226,22 +223,18 @@ int vertex_paint_poll(bContext *C)
 
 int weight_paint_mode_poll(bContext *C)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Object *ob = CTX_data_active_object(C);
-	return (ob &&
-	        (ob->type == OB_MESH) &&
-	        ((Mesh *)ob->data)->totpoly &&
-	        (workspace->object_mode == OB_MODE_WEIGHT_PAINT));
+
+	return ob && ob->mode == OB_MODE_WEIGHT_PAINT && ((Mesh *)ob->data)->totpoly;
 }
 
 int weight_paint_poll(bContext *C)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Object *ob = CTX_data_active_object(C);
 	ScrArea *sa;
 
 	if ((ob != NULL) &&
-	    (workspace->object_mode & OB_MODE_WEIGHT_PAINT) &&
+	    (ob->mode & OB_MODE_WEIGHT_PAINT) &&
 	    (BKE_paint_brush(&CTX_data_tool_settings(C)->wpaint->paint) != NULL) &&
 	    (sa = CTX_wm_area(C)) &&
 	    (sa->spacetype == SPACE_VIEW3D))
@@ -958,18 +951,17 @@ static void vertex_paint_init_session(const EvaluationContext *eval_ctx, Scene *
 	}
 }
 
-static void vertex_paint_init_session_data(
-        const EvaluationContext *eval_ctx, const ToolSettings *ts, Object *ob)
+static void vertex_paint_init_session_data(const ToolSettings *ts, Object *ob)
 {
 	/* Create maps */
 	struct SculptVertexPaintGeomMap *gmap = NULL;
 	const Brush *brush = NULL;
-	if (eval_ctx->object_mode == OB_MODE_VERTEX_PAINT) {
+	if (ob->mode == OB_MODE_VERTEX_PAINT) {
 		gmap = &ob->sculpt->mode.vpaint.gmap;
 		brush = BKE_paint_brush(&ts->vpaint->paint);
 		ob->sculpt->mode_type = OB_MODE_VERTEX_PAINT;
 	}
-	else if (eval_ctx->object_mode == OB_MODE_WEIGHT_PAINT) {
+	else if (ob->mode == OB_MODE_WEIGHT_PAINT) {
 		gmap = &ob->sculpt->mode.wpaint.gmap;
 		brush = BKE_paint_brush(&ts->wpaint->paint);
 		ob->sculpt->mode_type = OB_MODE_WEIGHT_PAINT;
@@ -998,7 +990,7 @@ static void vertex_paint_init_session_data(
 	}
 
 	/* Create average brush arrays */
-	if (eval_ctx->object_mode == OB_MODE_VERTEX_PAINT) {
+	if (ob->mode == OB_MODE_VERTEX_PAINT) {
 		if (!brush_use_accumulate(brush)) {
 			if (ob->sculpt->mode.vpaint.previous_color == NULL) {
 				ob->sculpt->mode.vpaint.previous_color =
@@ -1009,7 +1001,7 @@ static void vertex_paint_init_session_data(
 			MEM_SAFE_FREE(ob->sculpt->mode.vpaint.previous_color);
 		}
 	}
-	else if (eval_ctx->object_mode == OB_MODE_WEIGHT_PAINT) {
+	else if (ob->mode == OB_MODE_WEIGHT_PAINT) {
 		if (!brush_use_accumulate(brush)) {
 			if (ob->sculpt->mode.wpaint.alpha_weight == NULL) {
 				ob->sculpt->mode.wpaint.alpha_weight =
@@ -1042,12 +1034,10 @@ static void vertex_paint_init_session_data(
  * \{ */
 
 static void ed_vwpaintmode_enter_generic(
-        const EvaluationContext *eval_ctx,
-        wmWindowManager *wm,
-        WorkSpace *workspace, Scene *scene,
+        const EvaluationContext *eval_ctx, wmWindowManager *wm, Scene *scene,
         Object *ob, const eObjectMode mode_flag)
 {
-	workspace->object_mode |= mode_flag;
+	ob->mode |= mode_flag;
 	Mesh *me = BKE_mesh_from_object(ob);
 
 	if (mode_flag == OB_MODE_VERTEX_PAINT) {
@@ -1091,44 +1081,40 @@ static void ed_vwpaintmode_enter_generic(
 	}
 
 	vertex_paint_init_session(eval_ctx, scene, ob);
-
-	ED_workspace_object_mode_sync_from_object(wm, workspace, ob);
 }
 
 void ED_object_vpaintmode_enter_ex(
         const EvaluationContext *eval_ctx, wmWindowManager *wm,
-        WorkSpace *workspace, Scene *scene, Object *ob)
+        Scene *scene, Object *ob)
 {
 	ed_vwpaintmode_enter_generic(
-	        eval_ctx, wm, workspace, scene, ob, OB_MODE_VERTEX_PAINT);
+	        eval_ctx, wm, scene, ob, OB_MODE_VERTEX_PAINT);
 }
 void ED_object_vpaintmode_enter(struct bContext *C)
 {
 	EvaluationContext eval_ctx;
 	CTX_data_eval_ctx(C, &eval_ctx);
 	wmWindowManager *wm = CTX_wm_manager(C);
-	WorkSpace *workspace = CTX_wm_workspace(C);
 	Scene *scene = CTX_data_scene(C);
 	Object *ob = CTX_data_active_object(C);
-	ED_object_vpaintmode_enter_ex(&eval_ctx, wm, workspace, scene, ob);
+	ED_object_vpaintmode_enter_ex(&eval_ctx, wm, scene, ob);
 }
 
 void ED_object_wpaintmode_enter_ex(
         const EvaluationContext *eval_ctx, wmWindowManager *wm,
-        WorkSpace *workspace, Scene *scene, Object *ob)
+        Scene *scene, Object *ob)
 {
 	ed_vwpaintmode_enter_generic(
-	        eval_ctx, wm, workspace, scene, ob, OB_MODE_WEIGHT_PAINT);
+	        eval_ctx, wm, scene, ob, OB_MODE_WEIGHT_PAINT);
 }
 void ED_object_wpaintmode_enter(struct bContext *C)
 {
 	EvaluationContext eval_ctx;
 	CTX_data_eval_ctx(C, &eval_ctx);
 	wmWindowManager *wm = CTX_wm_manager(C);
-	WorkSpace *workspace = CTX_wm_workspace(C);
 	Scene *scene = CTX_data_scene(C);
 	Object *ob = CTX_data_active_object(C);
-	ED_object_wpaintmode_enter_ex(&eval_ctx, wm, workspace, scene, ob);
+	ED_object_wpaintmode_enter_ex(&eval_ctx, wm, scene, ob);
 }
 
 /** \} */
@@ -1138,11 +1124,10 @@ void ED_object_wpaintmode_enter(struct bContext *C)
  * \{ */
 
 static void ed_vwpaintmode_exit_generic(
-        WorkSpace *workspace,
         Object *ob, const eObjectMode mode_flag)
 {
 	Mesh *me = BKE_mesh_from_object(ob);
-	workspace->object_mode &= ~mode_flag;
+	ob->mode &= ~mode_flag;
 
 	if (mode_flag == OB_MODE_VERTEX_PAINT) {
 		if (me->editflag & ME_EDIT_PAINT_FACE_SEL) {
@@ -1178,30 +1163,26 @@ static void ed_vwpaintmode_exit_generic(
 		ED_mesh_mirror_spatial_table(NULL, NULL, NULL, NULL, 'e');
 		ED_mesh_mirror_topo_table(NULL, NULL, 'e');
 	}
-
-	ED_workspace_object_mode_sync_from_object(G.main->wm.first, workspace, ob);
 }
 
-void ED_object_vpaintmode_exit_ex(WorkSpace *workspace, Object *ob)
+void ED_object_vpaintmode_exit_ex(Object *ob)
 {
-	ed_vwpaintmode_exit_generic(workspace, ob, OB_MODE_VERTEX_PAINT);
+	ed_vwpaintmode_exit_generic(ob, OB_MODE_VERTEX_PAINT);
 }
 void ED_object_vpaintmode_exit(struct bContext *C)
 {
-	WorkSpace *workspace = CTX_wm_workspace(C);
 	Object *ob = CTX_data_active_object(C);
-	ED_object_vpaintmode_exit_ex(workspace, ob);
+	ED_object_vpaintmode_exit_ex(ob);
 }
 
-void ED_object_wpaintmode_exit_ex(WorkSpace *workspace, Object *ob)
+void ED_object_wpaintmode_exit_ex(Object *ob)
 {
-	ed_vwpaintmode_exit_generic(workspace, ob, OB_MODE_WEIGHT_PAINT);
+	ed_vwpaintmode_exit_generic(ob, OB_MODE_WEIGHT_PAINT);
 }
 void ED_object_wpaintmode_exit(struct bContext *C)
 {
-	WorkSpace *workspace = CTX_wm_workspace(C);
 	Object *ob = CTX_data_active_object(C);
-	ED_object_wpaintmode_exit_ex(workspace, ob);
+	ED_object_wpaintmode_exit_ex(ob);
 }
 
 /** \} */
@@ -1213,14 +1194,13 @@ void ED_object_wpaintmode_exit(struct bContext *C)
  */
 static int wpaint_mode_toggle_exec(bContext *C, wmOperator *op)
 {
-	WorkSpace *workspace = CTX_wm_workspace(C);
 	Object *ob = CTX_data_active_object(C);
 	const int mode_flag = OB_MODE_WEIGHT_PAINT;
-	const bool is_mode_set = (workspace->object_mode & mode_flag) != 0;
+	const bool is_mode_set = (ob->mode & mode_flag) != 0;
 	Scene *scene = CTX_data_scene(C);
 
 	if (!is_mode_set) {
-		if (!ED_object_mode_compat_set(C, workspace, mode_flag, op->reports)) {
+		if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) {
 			return OPERATOR_CANCELLED;
 		}
 	}
@@ -1228,17 +1208,15 @@ static int wpaint_mode_toggle_exec(bContext *C, wmOperator *op)
 	Mesh *me = BKE_mesh_from_object(ob);
 
 	if (is_mode_set) {
-		ED_object_wpaintmode_exit_ex(workspace, ob);
+		ED_object_wpaintmode_exit_ex(ob);
 	}
 	else {
 		EvaluationContext eval_ctx;
 		CTX_data_eval_ctx(C, &eval_ctx);
 		wmWindowManager *wm = CTX_wm_manager(C);
-		ED_object_wpaintmode_enter_ex(&eval_ctx, wm, workspace, scene, ob);
+		ED_object_wpaintmode_enter_ex(&eval_ctx, wm, scene, ob);
 	}
 
-	BKE_mesh_batch_cache_dirty(ob->data, BKE_MESH_BATCH_DIRTY_ALL);
-
 	/* Weightpaint works by overriding colors in mesh,
 	 * so need to make sure we recalc on enter and
 	 * exit (exit needs doing regardless because we
@@ -1427,14 +1405,11 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo
 	bool *defbase_sel;
 	SculptSession *ss = ob->sculpt;
 	VPaint *vp = CTX_data_tool_settings(C)->wpaint;
-	EvaluationContext eval_ctx;
 
 	if (ED_wpaint_ensure_data(C, op->reports, WPAINT_ENSURE_MIRROR, &vgroup_index) == false) {
 		return false;
 	}
 
-	CTX_data_eval_ctx(C, &eval_ctx);
-
 	{
 		/* check if we are attempting to paint onto a locked vertex group,
 		 * and other options disallow it from doing anything useful */
@@ -1532,10 +1507,13 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo
 		wpd->precomputed_weight = MEM_mallocN(sizeof(float) * me->totvert, __func__);
 	}
 
+	EvaluationContext eval_ctx;
+	CTX_data_eval_ctx(C, &eval_ctx);
+
 	/* If not previously created, create vertex/weight paint mode session data */
 	vertex_paint_init_session(&eval_ctx, scene, ob);
 	vwpaint_update_cache_invariants(C, vp, ss, op, mouse);
-	vertex_paint_init_session_data(&eval_ctx, ts, ob);
+	vertex_paint_init_session_data(ts, ob);
 
 	if (ob->sculpt->mode.wpaint.dvert_prev != NULL) {
 		MDeformVert *dv = ob->sculpt->mode.wpaint.dvert_prev;
@@ -2351,14 +2329,13 @@ void PAINT_OT_weight_paint(wmOperatorType *ot)
  */
 static int vpaint_mode_toggle_exec(bContext *C, wmOperator *op)
 {
-	WorkSpace *workspace = CTX_wm_workspace(C);
 	Object *ob = CTX_data_active_object(C);
 	const int mode_flag = OB_MODE_VERTEX_PAINT;
-	const bool is_mode_set = (workspace->object_mode & mode_flag) != 0;
+	const bool is_mode_set = (ob->mode & mode_flag) != 0;
 	Scene *scene = CTX_data_scene(C);
 
 	if (!is_mode_set) {
-		if (!ED_object_mode_compat_set(C, workspace, mode_flag, op->reports)) {
+		if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) {
 			return OPERATOR_CANCELLED;
 		}
 	}
@@ -2367,13 +2344,13 @@ static int vpaint_mode_toggle_exec(bContext *C, wmOperator *op)
 
 	/* toggle: end vpaint */
 	if (is_mode_set) {
-		ED_object_vpaintmode_exit_ex(workspace, ob);
+		ED_object_vpaintmode_exit_ex(ob);
 	}
 	else {
 		EvaluationContext eval_ctx;
 		CTX_data_eval_ctx(C, &eval_ctx);
 		wmWindowManager *wm = CTX_wm_manager(C);
-		ED_object_vpaintmode_enter_ex(&eval_ctx, wm, workspace, scene, ob);
+		ED_object_vpaintmode_enter_ex(&eval_ctx, wm, scene, ob);
 	}
 
 	BKE_mesh_batch_cache_dirty(ob->data, BKE_MESH_BATCH_DIRTY_ALL);
@@ -2522,7 +2499,7 @@ static bool vpaint_stroke_test_start(bContext *C, struct wmOperator *op, const f
 	/* If not previously created, create vertex/weight paint mode session data */
 	vertex_paint_init_session(&eval_ctx, scene, ob);
 	vwpaint_update_cache_invariants(C, vp, ss, op, mouse);
-	vertex_paint_init_session_data(&eval_ctx, ts, ob);
+	vertex_paint_init_session_data(ts, ob);
 
 	if (ob->sculpt->mode.vpaint.previous_color != NULL) {
 		memset(ob->sculpt->mode.vpaint.previous_color, 0, sizeof(uint) * me->totloop);
diff --git a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c
index f442c12cbe92e53812407a6dafb6b86cf24678ad..d7668a481397be59d375b0dc5a15e40c03ee061f 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c
@@ -28,7 +28,6 @@
 #include "DNA_meshdata_types.h"
 #include "DNA_object_types.h"
 #include "DNA_scene_types.h"
-#include "DNA_workspace_types.h"
 
 #include "BLI_math_base.h"
 #include "BLI_math_color.h"
@@ -52,10 +51,9 @@
 
 static int vertex_weight_paint_mode_poll(bContext *C)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Object *ob = CTX_data_active_object(C);
 	Mesh *me = BKE_mesh_from_object(ob);
-	return (ob && ELEM(workspace->object_mode, OB_MODE_VERTEX_PAINT, OB_MODE_WEIGHT_PAINT)) &&
+	return (ob && (ob->mode == OB_MODE_VERTEX_PAINT || ob->mode == OB_MODE_WEIGHT_PAINT)) &&
 	       (me && me->totpoly && me->dvert);
 }
 
diff --git a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c
index 7ff9c3851ac43e38a74f68c12d36e88ad8c30e74..3892b7764404c5c247599dc38f28102c94c48f1b 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c
@@ -118,10 +118,9 @@ static void wpaint_prev_destroy(struct WPaintPrev *wpp)
 
 static int weight_from_bones_poll(bContext *C)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Object *ob = CTX_data_active_object(C);
 
-	return (ob && (workspace->object_mode & OB_MODE_WEIGHT_PAINT) && modifiers_isDeformedByArmature(ob));
+	return (ob && (ob->mode & OB_MODE_WEIGHT_PAINT) && modifiers_isDeformedByArmature(ob));
 }
 
 static int weight_from_bones_exec(bContext *C, wmOperator *op)
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index 2d847d53877ad0dcb659a76c59de379998e276cd..4a9d2597415a88e8e2b564865a2a1f5b0d00ec91 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -1563,7 +1563,7 @@ static float neighbor_average_mask(SculptSession *ss, unsigned vert)
 static void bmesh_neighbor_average(float avg[3], BMVert *v)
 {
 	/* logic for 3 or more is identical */
-	const int vfcount = BM_vert_face_count_ex(v, 3);
+	const int vfcount = BM_vert_face_count_at_most(v, 3);
 
 	/* Don't modify corner vertices */
 	if (vfcount > 1) {
@@ -1578,7 +1578,7 @@ static void bmesh_neighbor_average(float avg[3], BMVert *v)
 
 			for (i = 0; i < ARRAY_SIZE(adj_v); i++) {
 				const BMVert *v_other = adj_v[i];
-				if (vfcount != 2 || BM_vert_face_count_ex(v_other, 2) <= 2) {
+				if (vfcount != 2 || BM_vert_face_count_at_most(v_other, 2) <= 2) {
 					add_v3_v3(avg, v_other->co);
 					total++;
 				}
@@ -4083,9 +4083,8 @@ static void sculpt_update_tex(const Scene *scene, Sculpt *sd, SculptSession *ss)
 
 int sculpt_mode_poll(bContext *C)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Object *ob = CTX_data_active_object(C);
-	return ob && workspace->object_mode & OB_MODE_SCULPT;
+	return ob && ob->mode & OB_MODE_SCULPT;
 }
 
 int sculpt_mode_poll_view3d(bContext *C)
@@ -5220,7 +5219,7 @@ void sculpt_pbvh_clear(Object *ob)
 		BKE_pbvh_free(ss->pbvh);
 	ss->pbvh = NULL;
 	if (dm)
-		dm->getPBVH(NULL, dm, OB_MODE_OBJECT);
+		dm->getPBVH(NULL, dm);
 	BKE_object_free_derived_caches(ob);
 }
 
@@ -5649,14 +5648,14 @@ static int ed_object_sculptmode_flush_recalc_flag(Scene *scene, Object *ob, Mult
 
 void ED_object_sculptmode_enter_ex(
         const EvaluationContext *eval_ctx,
-        WorkSpace *workspace, Scene *scene, Object *ob,
+        Scene *scene, Object *ob,
         ReportList *reports)
 {
 	const int mode_flag = OB_MODE_SCULPT;
 	Mesh *me = BKE_mesh_from_object(ob);
 
 	/* Enter sculptmode */
-	workspace->object_mode |= mode_flag;
+	ob->mode |= mode_flag;
 
 	MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob);
 
@@ -5739,7 +5738,7 @@ void ED_object_sculptmode_enter_ex(
 		}
 	}
 
-	ED_workspace_object_mode_sync_from_object(G.main->wm.first, workspace, ob);
+	// ED_workspace_object_mode_sync_from_object(G.main->wm.first, workspace, ob);
 
 	/* VBO no longer valid */
 	if (ob->derivedFinal) {
@@ -5749,17 +5748,16 @@ void ED_object_sculptmode_enter_ex(
 
 void ED_object_sculptmode_enter(struct bContext *C, ReportList *reports)
 {
-	WorkSpace *workspace = CTX_wm_workspace(C);
 	Scene *scene = CTX_data_scene(C);
 	Object *ob = CTX_data_active_object(C);
 	EvaluationContext eval_ctx;
 	CTX_data_eval_ctx(C, &eval_ctx);
-	ED_object_sculptmode_enter_ex(&eval_ctx, workspace, scene, ob, reports);
+	ED_object_sculptmode_enter_ex(&eval_ctx, scene, ob, reports);
 }
 
 void ED_object_sculptmode_exit_ex(
         const EvaluationContext *eval_ctx,
-        WorkSpace *workspace, Scene *scene, Object *ob)
+        Scene *scene, Object *ob)
 {
 	const int mode_flag = OB_MODE_SCULPT;
 	Mesh *me = BKE_mesh_from_object(ob);
@@ -5792,9 +5790,9 @@ void ED_object_sculptmode_exit_ex(
 	}
 
 	/* Leave sculptmode */
-	workspace->object_mode &= ~mode_flag;
+	ob->mode &= ~mode_flag;
 
-	ED_workspace_object_mode_sync_from_object(G.main->wm.first, workspace, ob);
+	// ED_workspace_object_mode_sync_from_object(G.main->wm.first, workspace, ob);
 
 	BKE_sculptsession_free(ob);
 
@@ -5808,24 +5806,22 @@ void ED_object_sculptmode_exit_ex(
 
 void ED_object_sculptmode_exit(bContext *C)
 {
-	WorkSpace *workspace = CTX_wm_workspace(C);
 	Scene *scene = CTX_data_scene(C);
 	Object *ob = CTX_data_active_object(C);
 	EvaluationContext eval_ctx;
 	CTX_data_eval_ctx(C, &eval_ctx);
-	ED_object_sculptmode_exit_ex(&eval_ctx, workspace, scene, ob);
+	ED_object_sculptmode_exit_ex(&eval_ctx, scene, ob);
 }
 
 static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op)
 {
-	WorkSpace *workspace = CTX_wm_workspace(C);
 	Scene *scene = CTX_data_scene(C);
 	Object *ob = CTX_data_active_object(C);
 	const int mode_flag = OB_MODE_SCULPT;
-	const bool is_mode_set = (workspace->object_mode & mode_flag) != 0;
+	const bool is_mode_set = (ob->mode & mode_flag) != 0;
 
 	if (!is_mode_set) {
-		if (!ED_object_mode_compat_set(C, workspace, mode_flag, op->reports)) {
+		if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) {
 			return OPERATOR_CANCELLED;
 		}
 	}
@@ -5834,10 +5830,10 @@ static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op)
 	CTX_data_eval_ctx(C, &eval_ctx);
 
 	if (is_mode_set) {
-		ED_object_sculptmode_exit_ex(&eval_ctx, workspace, scene, ob);
+		ED_object_sculptmode_exit_ex(&eval_ctx, scene, ob);
 	}
 	else {
-		ED_object_sculptmode_enter_ex(&eval_ctx, workspace, scene, ob, op->reports);
+		ED_object_sculptmode_enter_ex(&eval_ctx, scene, ob, op->reports);
 	}
 
 	WM_event_add_notifier(C, NC_SCENE | ND_MODE, scene);
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index 54cdbb18693a255e4f23c11fcdb18c023acaa94b..ba39d51700d239f1f620c5ccc2fb7780db954c5d 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -125,6 +125,8 @@ typedef struct SculptUndoNode {
 
 	/* shape keys */
 	char shapeName[sizeof(((KeyBlock *)0))->name];
+
+	size_t undo_size;
 } SculptUndoNode;
 
 /* Factor of brush to have rake point following behind
diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c
index 55950a51fba8df84a671300e466a928eb3c500da..e12ef2df4ab2a0f5950d3c70661950318320ae14 100644
--- a/source/blender/editors/sculpt_paint/sculpt_undo.c
+++ b/source/blender/editors/sculpt_paint/sculpt_undo.c
@@ -48,6 +48,9 @@
 #include "DNA_object_types.h"
 #include "DNA_scene_types.h"
 #include "DNA_mesh_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+#include "DNA_workspace_types.h"
 
 #include "BKE_ccg.h"
 #include "BKE_context.h"
@@ -56,6 +59,8 @@
 #include "BKE_key.h"
 #include "BKE_mesh.h"
 #include "BKE_subsurf.h"
+#include "BKE_undo_system.h"
+
 #include "DEG_depsgraph.h"
 
 #include "WM_api.h"
@@ -64,12 +69,22 @@
 #include "GPU_buffers.h"
 
 #include "ED_paint.h"
+#include "ED_object.h"
+#include "ED_sculpt.h"
+#include "ED_undo.h"
 
 #include "bmesh.h"
 #include "paint_intern.h"
 #include "sculpt_intern.h"
 
-/************************** Undo *************************/
+
+typedef struct UndoSculpt {
+	ListBase nodes;
+
+	size_t undo_size;
+} UndoSculpt;
+
+static UndoSculpt *sculpt_undo_get_nodes(void);
 
 static void update_cb(PBVHNode *node, void *rebuild)
 {
@@ -457,7 +472,7 @@ static int sculpt_undo_bmesh_restore(bContext *C,
 	return false;
 }
 
-static void sculpt_undo_restore(bContext *C, ListBase *lb)
+static void sculpt_undo_restore_list(bContext *C, ListBase *lb)
 {
 	Scene *scene = CTX_data_scene(C);
 	Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
@@ -580,7 +595,7 @@ static void sculpt_undo_restore(bContext *C, ListBase *lb)
 	}
 }
 
-static void sculpt_undo_free(ListBase *lb)
+static void sculpt_undo_free_list(ListBase *lb)
 {
 	SculptUndoNode *unode;
 	int i;
@@ -623,6 +638,8 @@ static void sculpt_undo_free(ListBase *lb)
 	}
 }
 
+/* Most likely we don't need this. */
+#if 0
 static bool sculpt_undo_cleanup(bContext *C, ListBase *lb)
 {
 	Object *ob = CTX_data_active_object(C);
@@ -639,16 +656,17 @@ static bool sculpt_undo_cleanup(bContext *C, ListBase *lb)
 
 	return false;
 }
+#endif
 
 SculptUndoNode *sculpt_undo_get_node(PBVHNode *node)
 {
-	ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_MESH);
+	UndoSculpt *usculpt = sculpt_undo_get_nodes();
 
-	if (!lb) {
+	if (usculpt == NULL) {
 		return NULL;
 	}
 
-	return BLI_findptr(lb, node, offsetof(SculptUndoNode, node));
+	return BLI_findptr(&usculpt->nodes, node, offsetof(SculptUndoNode, node));
 }
 
 static void sculpt_undo_alloc_and_store_hidden(PBVH *pbvh,
@@ -674,10 +692,11 @@ static void sculpt_undo_alloc_and_store_hidden(PBVH *pbvh,
 	}
 }
 
-static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node,
-                                              SculptUndoType type)
+static SculptUndoNode *sculpt_undo_alloc_node(
+        Object *ob, PBVHNode *node,
+        SculptUndoType type)
 {
-	ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_MESH);
+	UndoSculpt *usculpt = sculpt_undo_get_nodes();
 	SculptUndoNode *unode;
 	SculptSession *ss = ob->sculpt;
 	int totvert, allvert, totgrid, maxgrid, gridsize, *grids;
@@ -702,23 +721,23 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node,
 	/* general TODO, fix count_alloc */
 	switch (type) {
 		case SCULPT_UNDO_COORDS:
-			unode->co = MEM_mapallocN(sizeof(float) * 3 * allvert, "SculptUndoNode.co");
-			unode->no = MEM_mapallocN(sizeof(short) * 3 * allvert, "SculptUndoNode.no");
-			undo_paint_push_count_alloc(UNDO_PAINT_MESH,
-			                            (sizeof(float) * 3 +
-			                             sizeof(short) * 3 +
-			                             sizeof(int)) * allvert);
+			unode->co = MEM_mapallocN(sizeof(float[3]) * allvert, "SculptUndoNode.co");
+			unode->no = MEM_mapallocN(sizeof(short[3]) * allvert, "SculptUndoNode.no");
+
+			usculpt->undo_size = (sizeof(float[3]) + sizeof(short[3]) + sizeof(int)) * allvert;
 			break;
 		case SCULPT_UNDO_HIDDEN:
 			if (maxgrid)
 				sculpt_undo_alloc_and_store_hidden(ss->pbvh, unode);
 			else
 				unode->vert_hidden = BLI_BITMAP_NEW(allvert, "SculptUndoNode.vert_hidden");
-		
+	
 			break;
 		case SCULPT_UNDO_MASK:
 			unode->mask = MEM_mapallocN(sizeof(float) * allvert, "SculptUndoNode.mask");
-			undo_paint_push_count_alloc(UNDO_PAINT_MESH, (sizeof(float) * sizeof(int)) * allvert);
+
+			usculpt->undo_size += (sizeof(float) * sizeof(int)) * allvert;
+
 			break;
 		case SCULPT_UNDO_DYNTOPO_BEGIN:
 		case SCULPT_UNDO_DYNTOPO_END:
@@ -727,7 +746,7 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node,
 			break;
 	}
 	
-	BLI_addtail(lb, unode);
+	BLI_addtail(&usculpt->nodes, unode);
 
 	if (maxgrid) {
 		/* multires */
@@ -804,12 +823,13 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob,
                                               PBVHNode *node,
                                               SculptUndoType type)
 {
-	ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_MESH);
-	SculptUndoNode *unode = lb->first;
+	UndoSculpt *usculpt = sculpt_undo_get_nodes();
 	SculptSession *ss = ob->sculpt;
 	PBVHVertexIter vd;
 
-	if (!lb->first) {
+	SculptUndoNode *unode = usculpt->nodes.first;
+
+	if (unode == NULL) {
 		unode = MEM_callocN(sizeof(*unode), __func__);
 
 		BLI_strncpy(unode->idname, ob->id.name, sizeof(unode->idname));
@@ -848,7 +868,7 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob,
 			unode->bm_entry = BM_log_entry_add(ss->bm_log);
 		}
 
-		BLI_addtail(lb, unode);
+		BLI_addtail(&usculpt->nodes, unode);
 	}
 
 	if (node) {
@@ -889,8 +909,9 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob,
 	return unode;
 }
 
-SculptUndoNode *sculpt_undo_push_node(Object *ob, PBVHNode *node,
-                                      SculptUndoType type)
+SculptUndoNode *sculpt_undo_push_node(
+        Object *ob, PBVHNode *node,
+        SculptUndoType type)
 {
 	SculptSession *ss = ob->sculpt;
 	SculptUndoNode *unode;
@@ -960,17 +981,18 @@ SculptUndoNode *sculpt_undo_push_node(Object *ob, PBVHNode *node,
 
 void sculpt_undo_push_begin(const char *name)
 {
-	ED_undo_paint_push_begin(UNDO_PAINT_MESH, name,
-	                         sculpt_undo_restore, sculpt_undo_free, sculpt_undo_cleanup);
+	UndoStack *ustack = ED_undo_stack_get();
+	bContext *C = NULL; /* special case, we never read from this. */
+	BKE_undosys_step_push_init_with_type(ustack, C, name, BKE_UNDOSYS_TYPE_SCULPT);
 }
 
 void sculpt_undo_push_end(void)
 {
-	ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_MESH);
+	UndoSculpt *usculpt = sculpt_undo_get_nodes();
 	SculptUndoNode *unode;
 
 	/* we don't need normals in the undo stack */
-	for (unode = lb->first; unode; unode = unode->next) {
+	for (unode = usculpt->nodes.first; unode; unode = unode->next) {
 		if (unode->no) {
 			MEM_freeN(unode->no);
 			unode->no = NULL;
@@ -980,7 +1002,97 @@ void sculpt_undo_push_end(void)
 			BKE_pbvh_node_layer_disp_free(unode->node);
 	}
 
-	ED_undo_paint_push_end(UNDO_PAINT_MESH);
+	UndoStack *ustack = ED_undo_stack_get();
+	BKE_undosys_step_push(ustack, NULL, NULL);
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Implements ED Undo System
+ * \{ */
+
+typedef struct SculptUndoStep {
+	UndoStep step;
+	/* note: will split out into list for multi-object-sculpt-mode. */
+	UndoSculpt data;
+} SculptUndoStep;
+
+static bool sculpt_undosys_poll(bContext *C)
+{
+	ScrArea *sa = CTX_wm_area(C);
+	if (sa && (sa->spacetype == SPACE_VIEW3D)) {
+		Object *obact = CTX_data_active_object(C);
+		if (obact && (obact->mode & OB_MODE_SCULPT)) {
+			return true;
+		}
+	}
+	return false;
+}
 
-	WM_file_tag_modified();
+static void sculpt_undosys_step_encode_init(struct bContext *UNUSED(C), UndoStep *us_p)
+{
+	SculptUndoStep *us = (SculptUndoStep *)us_p;
+	/* dummy, memory is cleared anyway. */
+	BLI_listbase_clear(&us->data.nodes);
 }
+
+static bool sculpt_undosys_step_encode(struct bContext *UNUSED(C), UndoStep *us_p)
+{
+	/* dummy, encoding is done along the way by adding tiles
+	 * to the current 'SculptUndoStep' added by encode_init. */
+	SculptUndoStep *us = (SculptUndoStep *)us_p;
+	us->step.data_size = us->data.undo_size;
+	return true;
+}
+
+static void sculpt_undosys_step_decode(struct bContext *C, UndoStep *us_p, int UNUSED(dir))
+{
+	/* TODO(campbell): undo_system: use low-level API to set mode. */
+	ED_object_mode_set(C, OB_MODE_SCULPT);
+	BLI_assert(sculpt_undosys_poll(C));
+
+	SculptUndoStep *us = (SculptUndoStep *)us_p;
+	sculpt_undo_restore_list(C, &us->data.nodes);
+}
+
+static void sculpt_undosys_step_free(UndoStep *us_p)
+{
+	SculptUndoStep *us = (SculptUndoStep *)us_p;
+	sculpt_undo_free_list(&us->data.nodes);
+}
+
+/* Export for ED_undo_sys. */
+void ED_sculpt_undosys_type(UndoType *ut)
+{
+	ut->name = "Sculpt";
+	ut->poll = sculpt_undosys_poll;
+	ut->step_encode_init = sculpt_undosys_step_encode_init;
+	ut->step_encode = sculpt_undosys_step_encode;
+	ut->step_decode = sculpt_undosys_step_decode;
+	ut->step_free = sculpt_undosys_step_free;
+
+	ut->mode = BKE_UNDOTYPE_MODE_ACCUMULATE;
+	ut->use_context = true;
+
+	ut->step_size = sizeof(SculptUndoStep);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Utilities
+ * \{ */
+
+static UndoSculpt *sculpt_undosys_step_get_nodes(UndoStep *us_p)
+{
+	SculptUndoStep *us = (SculptUndoStep *)us_p;
+	return &us->data;
+}
+
+static UndoSculpt *sculpt_undo_get_nodes(void)
+{
+	UndoStack *ustack = ED_undo_stack_get();
+	UndoStep *us = BKE_undosys_stack_init_or_active_with_type(ustack, BKE_UNDOSYS_TYPE_SCULPT);
+	return sculpt_undosys_step_get_nodes(us);
+}
+
+/** \} */
diff --git a/source/blender/editors/space_action/action_buttons.c b/source/blender/editors/space_action/action_buttons.c
index 80ec91079848abd1e81b35a9480a34d90ff51118..3ba59b3be7566b340311d702fcc4849cee948888 100644
--- a/source/blender/editors/space_action/action_buttons.c
+++ b/source/blender/editors/space_action/action_buttons.c
@@ -49,8 +49,6 @@
 #include "BKE_context.h"
 #include "BKE_curve.h"
 #include "BKE_fcurve.h"
-#include "BKE_main.h"
-#include "BKE_global.h"
 #include "BKE_screen.h"
 #include "BKE_unit.h"
 
diff --git a/source/blender/editors/space_action/action_data.c b/source/blender/editors/space_action/action_data.c
index fbef14c07c48ee389c65ff852d863be6f27d4eb1..f1153b5bed0a1adc47eef55188e079698cb69465 100644
--- a/source/blender/editors/space_action/action_data.c
+++ b/source/blender/editors/space_action/action_data.c
@@ -54,10 +54,8 @@
 #include "BKE_animsys.h"
 #include "BKE_action.h"
 #include "BKE_fcurve.h"
-#include "BKE_global.h"
 #include "BKE_library.h"
 #include "BKE_key.h"
-#include "BKE_main.h"
 #include "BKE_nla.h"
 #include "BKE_scene.h"
 #include "BKE_context.h"
diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c
index 72b1245ca8a5f173935603e5b54426ee4bf86f1a..1c15a7c595098a683e37e354da0e4f5ec0792e84 100644
--- a/source/blender/editors/space_action/action_edit.c
+++ b/source/blender/editors/space_action/action_edit.c
@@ -59,7 +59,6 @@
 #include "BKE_global.h"
 #include "BKE_library.h"
 #include "BKE_key.h"
-#include "BKE_main.h"
 #include "BKE_nla.h"
 #include "BKE_context.h"
 #include "BKE_report.h"
diff --git a/source/blender/editors/space_buttons/CMakeLists.txt b/source/blender/editors/space_buttons/CMakeLists.txt
index ec866780122366e43f7c4b9b08b44cb7790e7161..397d79e1dbe61d82bc006afc1b077ed882267b4b 100644
--- a/source/blender/editors/space_buttons/CMakeLists.txt
+++ b/source/blender/editors/space_buttons/CMakeLists.txt
@@ -23,7 +23,6 @@ set(INC
 	../../blenkernel
 	../../blenlib
 	../../blentranslation
-	../../depsgraph
 	../../gpu
 	../../makesdna
 	../../makesrna
diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c
index ebb14d3caebd59bd2b3c6e97c13e552127f61d96..8866c6b6c40e4f8f553d09924f7066a69cd193ec 100644
--- a/source/blender/editors/space_buttons/buttons_context.c
+++ b/source/blender/editors/space_buttons/buttons_context.c
@@ -62,8 +62,6 @@
 
 #include "RNA_access.h"
 
-#include "DEG_depsgraph.h"
-
 #include "ED_buttons.h"
 #include "ED_armature.h"
 #include "ED_screen.h"
@@ -425,9 +423,8 @@ static int buttons_context_path_brush(const bContext *C, ButsContextPath *path)
 		scene = path->ptr[path->len - 1].data;
 
 		if (scene) {
-			const WorkSpace *workspace = CTX_wm_workspace(C);
 			ViewLayer *view_layer = CTX_data_view_layer(C);
-			br = BKE_paint_brush(BKE_paint_get_active(scene, view_layer, workspace->object_mode));
+			br = BKE_paint_brush(BKE_paint_get_active(scene, view_layer));
 		}
 
 		if (br) {
diff --git a/source/blender/editors/space_buttons/buttons_ops.c b/source/blender/editors/space_buttons/buttons_ops.c
index fc1b6877f5eb3cacf63a05e3e7ca2221073ffdc2..2a703ebb46c777708da54e40a8865d88664d9494 100644
--- a/source/blender/editors/space_buttons/buttons_ops.c
+++ b/source/blender/editors/space_buttons/buttons_ops.c
@@ -51,7 +51,7 @@
 #include "WM_types.h"
 
 #include "ED_screen.h"
-#include "ED_util.h"
+#include "ED_undo.h"
 
 #include "RNA_access.h"
 
diff --git a/source/blender/editors/space_buttons/buttons_texture.c b/source/blender/editors/space_buttons/buttons_texture.c
index c6dda7f9f0dc706fcd1e29c864943a005118e628..809480baeced539cc8f0ed6c43794c980e3a0a2e 100644
--- a/source/blender/editors/space_buttons/buttons_texture.c
+++ b/source/blender/editors/space_buttons/buttons_texture.c
@@ -482,7 +482,7 @@ void buttons_texture_context_compute(const bContext *C, SpaceButs *sbuts)
 	}
 	else {
 		/* set one user as active based on active index */
-		if (ct->index >= BLI_listbase_count_ex(&ct->users, ct->index + 1))
+		if (ct->index >= BLI_listbase_count_at_most(&ct->users, ct->index + 1))
 			ct->index = 0;
 
 		ct->user = BLI_findlink(&ct->users, ct->index);
diff --git a/source/blender/editors/space_clip/clip_draw.c b/source/blender/editors/space_clip/clip_draw.c
index 96968a148839a5d1cb7ddf988a66ef60e59bd807..5962bfe33f3da8233d0a25f59566f532088ecd95 100644
--- a/source/blender/editors/space_clip/clip_draw.c
+++ b/source/blender/editors/space_clip/clip_draw.c
@@ -1131,6 +1131,7 @@ static void draw_plane_marker_image(Scene *scene,
 
 			glGenTextures(1, (GLuint *)&texid);
 
+			glActiveTexture(GL_TEXTURE0);
 			glBindTexture(GL_TEXTURE_2D, texid);
 
 			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
@@ -1148,7 +1149,7 @@ static void draw_plane_marker_image(Scene *scene,
 
 			immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_COLOR);
 			immUniformColor4f(1.0f, 1.0f, 1.0f, plane_track->image_opacity);
-			immUniform1i("image", GL_TEXTURE0);
+			immUniform1i("image", 0);
 
 			immBegin(GWN_PRIM_TRI_FAN, 4);
 
diff --git a/source/blender/editors/space_clip/clip_toolbar.c b/source/blender/editors/space_clip/clip_toolbar.c
index 1504ce1a7bafb0edc3d4d3a6088d4942b6f98635..d2a7244ededff071c56b82974c44e80c3e49078f 100644
--- a/source/blender/editors/space_clip/clip_toolbar.c
+++ b/source/blender/editors/space_clip/clip_toolbar.c
@@ -49,7 +49,7 @@
 #include "WM_api.h"
 
 #include "ED_screen.h"
-#include "ED_util.h"
+#include "ED_undo.h"
 
 #include "UI_interface.h"
 #include "UI_resources.h"
diff --git a/source/blender/editors/space_clip/tracking_ops_orient.c b/source/blender/editors/space_clip/tracking_ops_orient.c
index 49cfc4b71b074900aef5008123b9e30064b49643..583beef5001dd6d9ae7a8764cc81c2ea68db17ee 100644
--- a/source/blender/editors/space_clip/tracking_ops_orient.c
+++ b/source/blender/editors/space_clip/tracking_ops_orient.c
@@ -42,7 +42,6 @@
 #include "BKE_context.h"
 #include "BKE_constraint.h"
 #include "BKE_tracking.h"
-#include "BKE_global.h"
 #include "BKE_layer.h"
 #include "BKE_object.h"
 #include "BKE_report.h"
diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c
index 4bf377ddc1a03b6959519f318d4ee165f007a5a8..fb0ff23a57fcb0558346be0cba63b88e5ed33c61 100644
--- a/source/blender/editors/space_file/file_draw.c
+++ b/source/blender/editors/space_file/file_draw.c
@@ -52,6 +52,8 @@
 
 #include "BLT_translation.h"
 
+#include "BLF_api.h"
+
 #include "IMB_imbuf_types.h"
 
 #include "DNA_userdef_types.h"
@@ -607,6 +609,8 @@ void file_draw_list(const bContext *C, ARegion *ar)
 		}
 	}
 
+	BLF_batch_draw_begin();
+
 	for (i = offset; (i < numfiles) && (i < offset + numfiles_layout); i++) {
 		unsigned int file_selflag;
 		char path[FILE_MAX_LIBEXTRA];
@@ -736,6 +740,8 @@ void file_draw_list(const bContext *C, ARegion *ar)
 		}
 	}
 
+	BLF_batch_draw_end();
+
 	UI_block_end(C, block);
 	UI_block_draw(C, block);
 
diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c
index 49f498b3419816061da0f9d44aba3dc5f1e00f01..c79652795ac0320821aa0d3e6a5fcaf301095a32 100644
--- a/source/blender/editors/space_graph/graph_buttons.c
+++ b/source/blender/editors/space_graph/graph_buttons.c
@@ -64,7 +64,7 @@
 #include "ED_anim_api.h"
 #include "ED_keyframing.h"
 #include "ED_screen.h"
-#include "ED_util.h"
+#include "ED_undo.h"
 
 #include "UI_interface.h"
 #include "UI_resources.h"
diff --git a/source/blender/editors/space_graph/graph_draw.c b/source/blender/editors/space_graph/graph_draw.c
index 74349d5216919a23c03871b0700dcdb39435d6ac..26a3f110d36c533cd14de37a502ac47d8f9466f6 100644
--- a/source/blender/editors/space_graph/graph_draw.c
+++ b/source/blender/editors/space_graph/graph_draw.c
@@ -323,8 +323,12 @@ static void draw_fcurve_handles(SpaceIpo *sipo, FCurve *fcu)
 {
 	int sel, b;
 
-	unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
-	immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+	Gwn_VertFormat *format = immVertexFormat();
+	unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
+	unsigned int color = GWN_vertformat_attr_add(format, "color", GWN_COMP_U8, 4, GWN_FETCH_INT_TO_FLOAT_UNIT);
+	immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
+
+	immBeginAtMost(GWN_PRIM_LINES, 4 * 2 * fcu->totvert);
 
 	/* slightly hacky, but we want to draw unselected points before selected ones 
 	 * so that selected points are clearly visible
@@ -334,7 +338,7 @@ static void draw_fcurve_handles(SpaceIpo *sipo, FCurve *fcu)
 		int basecol = (sel) ? TH_HANDLE_SEL_FREE : TH_HANDLE_FREE;
 		const float *fp;
 		unsigned char col[4];
-		
+
 		for (b = 0; b < fcu->totvert; b++, prevbezt = bezt, bezt++) {
 			/* if only selected keyframes can get their handles shown, 
 			 * check that keyframe is selected
@@ -352,24 +356,20 @@ static void draw_fcurve_handles(SpaceIpo *sipo, FCurve *fcu)
 				if ((!prevbezt && (bezt->ipo == BEZT_IPO_BEZ)) || (prevbezt && (prevbezt->ipo == BEZT_IPO_BEZ))) {
 					UI_GetThemeColor3ubv(basecol + bezt->h1, col);
 					col[3] = fcurve_display_alpha(fcu) * 255;
-					immUniformColor4ubv(col);
-
-					immBegin(GWN_PRIM_LINES, 2);
+					immAttrib4ubv(color, col);
 					immVertex2fv(pos, fp);
+					immAttrib4ubv(color, col);
 					immVertex2fv(pos, fp + 3);
-					immEnd();
 				}
 				
 				/* only draw second handle if this segment is bezier */
 				if (bezt->ipo == BEZT_IPO_BEZ) {
 					UI_GetThemeColor3ubv(basecol + bezt->h2, col);
 					col[3] = fcurve_display_alpha(fcu) * 255;
-					immUniformColor4ubv(col);
-
-					immBegin(GWN_PRIM_LINES, 2);
+					immAttrib4ubv(color, col);
 					immVertex2fv(pos, fp + 3);
+					immAttrib4ubv(color, col);
 					immVertex2fv(pos, fp + 6);
-					immEnd();
 				}
 			}
 			else {
@@ -380,12 +380,10 @@ static void draw_fcurve_handles(SpaceIpo *sipo, FCurve *fcu)
 					fp = bezt->vec[0];
 					UI_GetThemeColor3ubv(basecol + bezt->h1, col);
 					col[3] = fcurve_display_alpha(fcu) * 255;
-					immUniformColor4ubv(col);
-
-					immBegin(GWN_PRIM_LINES, 2);
+					immAttrib4ubv(color, col);
 					immVertex2fv(pos, fp);
+					immAttrib4ubv(color, col);
 					immVertex2fv(pos, fp + 3);
-					immEnd();
 				}
 				
 				/* only draw second handle if this segment is bezier, and selection is ok */
@@ -395,17 +393,16 @@ static void draw_fcurve_handles(SpaceIpo *sipo, FCurve *fcu)
 					fp = bezt->vec[1];
 					UI_GetThemeColor3ubv(basecol + bezt->h2, col);
 					col[3] = fcurve_display_alpha(fcu) * 255;
-					immUniformColor4ubv(col);
-
-					immBegin(GWN_PRIM_LINES, 2);
+					immAttrib4ubv(color, col);
 					immVertex2fv(pos, fp);
+					immAttrib4ubv(color, col);
 					immVertex2fv(pos, fp + 3);
-					immEnd();
 				}
 			}
 		}
 	}
 
+	immEnd();
 	immUnbindProgram();
 }
 
diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c
index 20f9658020d106dc58f8991443ddafbe926d9b9e..c105f40f1d6a2a2eed31a35eb28b58058051dd24 100644
--- a/source/blender/editors/space_image/image_buttons.c
+++ b/source/blender/editors/space_image/image_buttons.c
@@ -762,7 +762,7 @@ static void uiblock_layer_pass_buttons(
 		}
 
 		/* view */
-		if (BLI_listbase_count_ex(&rr->views, 2) > 1 &&
+		if (BLI_listbase_count_at_most(&rr->views, 2) > 1 &&
 		    ((!show_stereo) || (!RE_RenderResult_is_stereo(rr))))
 		{
 			rview = BLI_findlink(&rr->views, iuser->view);
diff --git a/source/blender/editors/space_image/image_edit.c b/source/blender/editors/space_image/image_edit.c
index 95ed8967380c76966a7268052e23e5cdc6b70835..c719af9e0e26fbe6c8fbf4f68dade2b18f1aa598 100644
--- a/source/blender/editors/space_image/image_edit.c
+++ b/source/blender/editors/space_image/image_edit.c
@@ -323,16 +323,15 @@ bool ED_image_slot_cycle(struct Image *image, int direction)
 
 void ED_space_image_scopes_update(const struct bContext *C, struct SpaceImage *sima, struct ImBuf *ibuf, bool use_view_settings)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Scene *scene = CTX_data_scene(C);
 	Object *ob = CTX_data_active_object(C);
-
+	
 	/* scope update can be expensive, don't update during paint modes */
 	if (sima->mode == SI_MODE_PAINT)
 		return;
-	if (ob && ((workspace->object_mode & (OB_MODE_TEXTURE_PAINT | OB_MODE_EDIT)) != 0)) {
+	if (ob && ((ob->mode & (OB_MODE_TEXTURE_PAINT | OB_MODE_EDIT)) != 0))
 		return;
-	}
+
 	/* We also don't update scopes of render result during render. */
 	if (G.is_rendering) {
 		const Image *image = sima->image;
@@ -377,12 +376,11 @@ bool ED_space_image_show_uvedit(SpaceImage *sima, Object *obedit)
 }
 
 /* matches clip function */
-bool ED_space_image_check_show_maskedit(
-        SpaceImage *sima, const WorkSpace *workspace, ViewLayer *view_layer)
+bool ED_space_image_check_show_maskedit(SpaceImage *sima, ViewLayer *view_layer)
 {
 	/* check editmode - this is reserved for UV editing */
 	Object *ob = OBACT(view_layer);
-	if (ob && (workspace->object_mode & OB_MODE_EDIT) && ED_space_image_show_uvedit(sima, ob)) {
+	if (ob && ob->mode & OB_MODE_EDIT && ED_space_image_show_uvedit(sima, ob)) {
 		return false;
 	}
 
@@ -392,10 +390,10 @@ bool ED_space_image_check_show_maskedit(
 int ED_space_image_maskedit_poll(bContext *C)
 {
 	SpaceImage *sima = CTX_wm_space_image(C);
+
 	if (sima) {
-		WorkSpace *workspace = CTX_wm_workspace(C);
 		ViewLayer *view_layer = CTX_data_view_layer(C);
-		return ED_space_image_check_show_maskedit(sima, workspace, view_layer);
+		return ED_space_image_check_show_maskedit(sima, view_layer);
 	}
 
 	return false;
diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c
index 6ee9d547ac3633ef99e704d0017f3bf3200f8cc1..9162b8b76a9c4d7601e67df11b10c44114c3e52a 100644
--- a/source/blender/editors/space_image/image_ops.c
+++ b/source/blender/editors/space_image/image_ops.c
@@ -275,12 +275,11 @@ int space_image_main_region_poll(bContext *C)
 /* For IMAGE_OT_curves_point_set to avoid sampling when in uv smooth mode or editmode */
 static int space_image_main_area_not_uv_brush_poll(bContext *C)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	SpaceImage *sima = CTX_wm_space_image(C);
 	Scene *scene = CTX_data_scene(C);
 	ToolSettings *toolsettings = scene->toolsettings;
 
-	if (sima && !toolsettings->uvsculpt && ((workspace->object_mode & OB_MODE_EDIT) == 0)) {
+	if (sima && !toolsettings->uvsculpt && (CTX_data_edit_object(C) == NULL)) {
 		return 1;
 	}
 
@@ -793,7 +792,6 @@ void IMAGE_OT_view_all(wmOperatorType *ot)
 
 static int image_view_selected_exec(bContext *C, wmOperator *UNUSED(op))
 {
-	WorkSpace *workspace = CTX_wm_workspace(C);
 	SpaceImage *sima;
 	ARegion *ar;
 	Scene *scene;
@@ -817,7 +815,7 @@ static int image_view_selected_exec(bContext *C, wmOperator *UNUSED(op))
 			return OPERATOR_CANCELLED;
 		}
 	}
-	else if (ED_space_image_check_show_maskedit(sima, workspace, view_layer)) {
+	else if (ED_space_image_check_show_maskedit(sima, view_layer)) {
 		if (!ED_mask_selected_minmax(C, min, max)) {
 			return OPERATOR_CANCELLED;
 		}
@@ -1860,7 +1858,7 @@ static bool save_image_doit(bContext *C, SpaceImage *sima, wmOperator *op, SaveI
 		/* we need renderresult for exr and rendered multiview */
 		scene = CTX_data_scene(C);
 		rr = BKE_image_acquire_renderresult(scene, ima);
-		bool is_mono = rr ? BLI_listbase_count_ex(&rr->views, 2) < 2 : BLI_listbase_count_ex(&ima->views, 2) < 2;
+		bool is_mono = rr ? BLI_listbase_count_at_most(&rr->views, 2) < 2 : BLI_listbase_count_at_most(&ima->views, 2) < 2;
 		bool is_exr_rr = rr && ELEM(imf->imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER) && RE_HasFloatPixels(rr);
 
 		/* error handling */
@@ -2626,8 +2624,7 @@ static int image_invert_exec(bContext *C, wmOperator *op)
 		return OPERATOR_CANCELLED;
 
 	if (support_undo) {
-		ED_undo_paint_push_begin(UNDO_PAINT_IMAGE, op->type->name,
-		                         ED_image_undo_restore, ED_image_undo_free, NULL);
+		ED_image_undo_push_begin(op->type->name);
 		/* not strictly needed, because we only imapaint_dirty_region to invalidate all tiles
 		 * but better do this right in case someone copies this for a tool that uses partial redraw better */
 		ED_imapaint_clear_partial_redraw();
@@ -2668,8 +2665,9 @@ static int image_invert_exec(bContext *C, wmOperator *op)
 	if (ibuf->mipmap[0])
 		ibuf->userflags |= IB_MIPMAP_INVALID;
 
-	if (support_undo)
-		ED_undo_paint_push_end(UNDO_PAINT_IMAGE);
+	if (support_undo) {
+		ED_image_undo_push_end();
+	}
 
 	/* force GPU reupload, all image is invalid */
 	GPU_free_image(ima);
diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c
index c66d588c50d5725a2d7a956e5d77045b501e0652..54037059cc38d1c5c2fbc57fa30d442e35082d35 100644
--- a/source/blender/editors/space_image/space_image.c
+++ b/source/blender/editors/space_image/space_image.c
@@ -551,7 +551,7 @@ static void image_listener(bScreen *UNUSED(sc), ScrArea *sa, wmNotifier *wmn, Sc
 				{
 					ViewLayer *view_layer = BKE_view_layer_from_workspace_get(scene, workspace);
 					Object *ob = OBACT(view_layer);
-					if (ob && (ob == wmn->reference) && (workspace->object_mode & OB_MODE_EDIT)) {
+					if (ob && (ob == wmn->reference) && (ob->mode & OB_MODE_EDIT)) {
 						if (sima->lock && (sima->flag & SI_DRAWSHADOW)) {
 							ED_area_tag_refresh(sa);
 							ED_area_tag_redraw(sa);
@@ -725,9 +725,6 @@ static void image_main_region_init(wmWindowManager *wm, ARegion *ar)
 
 static void image_main_region_draw(const bContext *C, ARegion *ar)
 {
-	EvaluationContext eval_ctx;
-	CTX_data_eval_ctx(C, &eval_ctx);
-
 	/* draw entirely, view changes should be handled here */
 	SpaceImage *sima = CTX_wm_space_image(C);
 	Object *obact = CTX_data_active_object(C);
@@ -762,7 +759,7 @@ static void image_main_region_draw(const bContext *C, ARegion *ar)
 
 	ED_region_draw_cb_draw(C, ar, REGION_DRAW_PRE_VIEW);
 
-	ED_uvedit_draw_main(sima, &eval_ctx, ar, scene, view_layer, obedit, obact, depsgraph);
+	ED_uvedit_draw_main(sima, ar, scene, view_layer, obedit, obact, depsgraph);
 
 	/* check for mask (delay draw) */
 	if (ED_space_image_show_uvedit(sima, obedit)) {
diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c
index 800560c380c67b07dff99bc3745d91b8a2746bb0..157171481bc75f3e1080fc8721360c4e09fe477d 100644
--- a/source/blender/editors/space_info/info_stats.c
+++ b/source/blender/editors/space_info/info_stats.c
@@ -36,7 +36,6 @@
 #include "DNA_lattice_types.h"
 #include "DNA_meta_types.h"
 #include "DNA_scene_types.h"
-#include "DNA_workspace_types.h"
 
 #include "BLI_math.h"
 #include "BLI_string.h"
@@ -375,21 +374,22 @@ static bool stats_is_object_dynamic_topology_sculpt(Object *ob, const eObjectMod
 }
 
 /* Statistics displayed in info header. Called regularly on scene changes. */
-static void stats_update(ViewLayer *view_layer, Object *obedit, const eObjectMode object_mode)
+static void stats_update(ViewLayer *view_layer)
 {
 	SceneStats stats = {0};
-	Object *ob = (view_layer->basact) ? view_layer->basact->object : NULL;
+	Object *ob = OBACT(view_layer);
+	Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
 	Base *base;
 
 	if (obedit) {
 		/* Edit Mode */
 		stats_object_edit(ob, &stats);
 	}
-	else if (ob && (object_mode & OB_MODE_POSE)) {
+	else if (ob && (ob->mode & OB_MODE_POSE)) {
 		/* Pose Mode */
 		stats_object_pose(ob, &stats);
 	}
-	else if (stats_is_object_dynamic_topology_sculpt(ob, object_mode)) {
+	else if (stats_is_object_dynamic_topology_sculpt(ob, ob->mode)) {
 		/* Dynamic-topology sculpt mode */
 		stats_object_sculpt_dynamic_topology(ob, &stats);
 	}
@@ -408,12 +408,14 @@ static void stats_update(ViewLayer *view_layer, Object *obedit, const eObjectMod
 	*(view_layer->stats) = stats;
 }
 
-static void stats_string(ViewLayer *view_layer, Object *obedit, const eObjectMode object_mode)
+static void stats_string(ViewLayer *view_layer)
 {
 #define MAX_INFO_MEM_LEN  64
 	SceneStats *stats = view_layer->stats;
 	SceneStatsFmt stats_fmt;
-	Object *ob = (view_layer->basact) ? view_layer->basact->object : NULL;
+	Object *ob = OBACT(view_layer);
+	Object *obedit = OBEDIT_FROM_OBACT(ob);
+	eObjectMode object_mode = ob ? ob->mode : OB_MODE_OBJECT;
 	uintptr_t mem_in_use, mmap_in_use;
 	char memstr[MAX_INFO_MEM_LEN];
 	char gpumemstr[MAX_INFO_MEM_LEN] = "";
@@ -528,16 +530,11 @@ void ED_info_stats_clear(ViewLayer *view_layer)
 	}
 }
 
-const char *ED_info_stats_string(Scene *UNUSED(scene), WorkSpace *workspace, ViewLayer *view_layer)
+const char *ED_info_stats_string(Scene *UNUSED(scene), ViewLayer *view_layer)
 {
-	Object *obact = (view_layer->basact) ? view_layer->basact->object : NULL;
-	Object *obedit = (obact && (workspace->object_mode & OB_MODE_EDIT) &&
-	                  BKE_object_is_in_editmode(obact)) ? obact : NULL;
-
 	if (!view_layer->stats) {
-		stats_update(view_layer, obedit, workspace->object_mode);
+		stats_update(view_layer);
 	}
-	stats_string(view_layer, obedit, workspace->object_mode);
-
+	stats_string(view_layer);
 	return view_layer->stats->infostr;
 }
diff --git a/source/blender/editors/space_logic/logic_window.c b/source/blender/editors/space_logic/logic_window.c
index c6fd70a60dd84b9686f2e92506f74fe1b6af6c00..9bdc92f98ab146740ae457944ca4039796d9201e 100644
--- a/source/blender/editors/space_logic/logic_window.c
+++ b/source/blender/editors/space_logic/logic_window.c
@@ -56,7 +56,7 @@
 #include "BKE_main.h"
 #include "BKE_sca.h"
 
-#include "ED_util.h"
+#include "ED_undo.h"
 
 #include "BLT_translation.h"
 
diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c
index 87b7599ec6634edce19aca7155e27981ff8700ea..c86e9872c0a4e5a93c1f7f30d6d8787749c7eb19 100644
--- a/source/blender/editors/space_nla/nla_edit.c
+++ b/source/blender/editors/space_nla/nla_edit.c
@@ -1501,7 +1501,7 @@ static int nlaedit_swap_exec(bContext *C, wmOperator *op)
 			NlaStrip *mstrip = (NlaStrip *)nlt->strips.first;
 			
 			if ((mstrip->flag & NLASTRIP_FLAG_TEMP_META) &&
-			    (BLI_listbase_count_ex(&mstrip->strips, 3) == 2))
+			    (BLI_listbase_count_at_most(&mstrip->strips, 3) == 2))
 			{
 				/* remove this temp meta, so that we can see the strips inside */
 				BKE_nlastrips_clear_metas(&nlt->strips, 0, 1);
diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c
index 3c00114579ef81a21940e10b084f24f45d14fd93..1cb9c57404ada4c6cf213213194b56146d306f49 100644
--- a/source/blender/editors/space_node/drawnode.c
+++ b/source/blender/editors/space_node/drawnode.c
@@ -53,6 +53,7 @@
 #include "BIF_glutil.h"
 
 #include "GPU_draw.h"
+#include "GPU_batch.h"
 #include "GPU_immediate.h"
 #include "GPU_matrix.h"
 
@@ -3292,11 +3293,10 @@ void draw_nodespace_back_pix(const bContext *C, ARegion *ar, SpaceNode *snode, b
 	BKE_image_release_ibuf(ima, ibuf, lock);
 }
 
-
-/* if v2d not NULL, it clips and returns 0 if not visible */
-bool node_link_bezier_points(View2D *v2d, SpaceNode *snode, bNodeLink *link, float coord_array[][2], int resol)
+/* return quadratic beziers points for a given nodelink and clip if v2d is not NULL. */
+static bool node_link_bezier_handles(View2D *v2d, SpaceNode *snode, bNodeLink *link, float vec[4][2])
 {
-	float dist, vec[4][2];
+	float dist;
 	float deltax, deltay;
 	float cursor[2] = {0.0f, 0.0f};
 	int toreroute, fromreroute;
@@ -3364,13 +3364,23 @@ bool node_link_bezier_points(View2D *v2d, SpaceNode *snode, bNodeLink *link, flo
 		vec[2][0] = vec[3][0] - dist;
 		vec[2][1] = vec[3][1];
 	}
+
 	if (v2d && min_ffff(vec[0][0], vec[1][0], vec[2][0], vec[3][0]) > v2d->cur.xmax) {
-		/* clipped */
+		return 0; /* clipped */
 	}
 	else if (v2d && max_ffff(vec[0][0], vec[1][0], vec[2][0], vec[3][0]) < v2d->cur.xmin) {
-		/* clipped */
+		return 0; /* clipped */
 	}
-	else {
+
+	return 1;
+}
+
+/* if v2d not NULL, it clips and returns 0 if not visible */
+bool node_link_bezier_points(View2D *v2d, SpaceNode *snode, bNodeLink *link, float coord_array[][2], int resol)
+{
+	float vec[4][2];
+
+	if (node_link_bezier_handles(v2d, snode, link, vec)) {
 		/* always do all three, to prevent data hanging around */
 		BKE_curve_forward_diff_bezier(vec[0][0], vec[1][0], vec[2][0], vec[3][0],
 		                              coord_array[0] + 0, resol, sizeof(float) * 2);
@@ -3382,135 +3392,242 @@ bool node_link_bezier_points(View2D *v2d, SpaceNode *snode, bNodeLink *link, flo
 	return 0;
 }
 
+#define NODELINK_GROUP_SIZE 256
 #define LINK_RESOL  24
-#define LINK_ARROW  12  /* position of arrow on the link, LINK_RESOL/2 */
-#define ARROW_SIZE (7 * UI_DPI_FAC)
-void node_draw_link_bezier(View2D *v2d, SpaceNode *snode, bNodeLink *link,
-                           int th_col1, bool do_shaded, int th_col2, bool do_triple, int th_col3)
-{
-	float coord_array[LINK_RESOL + 1][2];
-	
-	if (node_link_bezier_points(v2d, snode, link, coord_array, LINK_RESOL)) {
-		float dist, spline_step = 0.0f;
-		int i;
-		int drawarrow;
-		/* store current linewidth */
-		float linew;
-		float arrow[2], arrow1[2], arrow2[2];
-		glGetFloatv(GL_LINE_WIDTH, &linew);
-		unsigned int pos;
-		
-		/* we can reuse the dist variable here to increment the GL curve eval amount*/
-		dist = 1.0f / (float)LINK_RESOL;
-		
-		glEnable(GL_LINE_SMOOTH);
-		
-		drawarrow = ((link->tonode && (link->tonode->type == NODE_REROUTE)) &&
-		             (link->fromnode && (link->fromnode->type == NODE_REROUTE)));
-
-		if (drawarrow) {
-			/* draw arrow in line segment LINK_ARROW */
-			float d_xy[2], len;
-
-			sub_v2_v2v2(d_xy, coord_array[LINK_ARROW], coord_array[LINK_ARROW - 1]);
-			len = len_v2(d_xy);
-			mul_v2_fl(d_xy, ARROW_SIZE / len);
-			arrow1[0] = coord_array[LINK_ARROW][0] - d_xy[0] + d_xy[1];
-			arrow1[1] = coord_array[LINK_ARROW][1] - d_xy[1] - d_xy[0];
-			arrow2[0] = coord_array[LINK_ARROW][0] - d_xy[0] - d_xy[1];
-			arrow2[1] = coord_array[LINK_ARROW][1] - d_xy[1] + d_xy[0];
-			arrow[0] = coord_array[LINK_ARROW][0];
-			arrow[1] = coord_array[LINK_ARROW][1];
+#define LINK_WIDTH  (2.5f * UI_DPI_FAC)
+#define ARROW_SIZE  (7 * UI_DPI_FAC)
+
+static float arrow_verts[3][2] = {{-1.0f, 1.0f}, {0.0f, 0.0f}, {-1.0f, -1.0f}};
+static float arrow_expand_axis[3][2] = {{0.7071f, 0.7071f}, {M_SQRT2, 0.0f}, {0.7071f, -0.7071f}};
+
+struct {
+	Gwn_Batch *batch; /* for batching line together */
+	Gwn_Batch *batch_single; /* for single line */
+	Gwn_VertBuf *inst_vbo;
+	unsigned int p0_id, p1_id, p2_id, p3_id;
+	unsigned int colid_id;
+	Gwn_VertBufRaw p0_step, p1_step, p2_step, p3_step;
+	Gwn_VertBufRaw colid_step;
+	unsigned int count;
+	bool enabled;
+} g_batch_link = {0};
+
+static void nodelink_batch_reset(void)
+{
+	GWN_vertbuf_attr_get_raw_data(g_batch_link.inst_vbo, g_batch_link.p0_id, &g_batch_link.p0_step);
+	GWN_vertbuf_attr_get_raw_data(g_batch_link.inst_vbo, g_batch_link.p1_id, &g_batch_link.p1_step);
+	GWN_vertbuf_attr_get_raw_data(g_batch_link.inst_vbo, g_batch_link.p2_id, &g_batch_link.p2_step);
+	GWN_vertbuf_attr_get_raw_data(g_batch_link.inst_vbo, g_batch_link.p3_id, &g_batch_link.p3_step);
+	GWN_vertbuf_attr_get_raw_data(g_batch_link.inst_vbo, g_batch_link.colid_id, &g_batch_link.colid_step);
+	g_batch_link.count = 0;
+}
+
+static void set_nodelink_vertex(
+        Gwn_VertBuf *vbo,
+        unsigned int uv_id, unsigned int pos_id, unsigned int exp_id, unsigned int v,
+        const unsigned char uv[2], const float pos[2], const float exp[2])
+{
+	GWN_vertbuf_attr_set(vbo, uv_id, v, uv);
+	GWN_vertbuf_attr_set(vbo, pos_id, v, pos);
+	GWN_vertbuf_attr_set(vbo, exp_id, v, exp);
+}
+
+static void nodelink_batch_init(void)
+{
+	Gwn_VertFormat format = {0};
+	unsigned int uv_id = GWN_vertformat_attr_add(&format, "uv", GWN_COMP_U8, 2, GWN_FETCH_INT_TO_FLOAT_UNIT);
+	unsigned int pos_id = GWN_vertformat_attr_add(&format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
+	unsigned int expand_id = GWN_vertformat_attr_add(&format, "expand", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
+	Gwn_VertBuf *vbo = GWN_vertbuf_create_with_format_ex(&format, GWN_USAGE_STATIC);
+	int vcount = LINK_RESOL * 2; /* curve */
+	vcount += 2; /* restart strip */
+	vcount += 3*2; /* arrow */
+	vcount *= 2; /* shadow */
+	vcount += 2; /* restart strip */
+	GWN_vertbuf_data_alloc(vbo, vcount);
+	int v = 0;
+
+	for (int k = 0; k < 2; ++k) {
+		unsigned char uv[2] = {0, 0};
+		float pos[2] = {0.0f, 0.0f};
+		float exp[2] = {0.0f, 1.0f};
+
+		/* restart */
+		if (k == 1)
+			set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp);
+
+		/* curve strip */
+		for (int i = 0; i < LINK_RESOL; ++i) {
+			uv[0] = 255 * (i / (float)(LINK_RESOL-1));
+			uv[1] = 0;
+			set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp);
+			uv[1] = 255;
+			set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp);
 		}
-
-		if (do_triple || drawarrow || (!do_shaded)) {
-			pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
-			immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+		/* restart */
+		set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp);
+
+		uv[0] = 127;
+		uv[1] = 0;
+		copy_v2_v2(pos, arrow_verts[0]);
+		copy_v2_v2(exp, arrow_expand_axis[0]);
+		set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp);
+		/* arrow */
+		for (int i = 0; i < 3; ++i) {
+			uv[1] = 0;
+			copy_v2_v2(pos, arrow_verts[i]);
+			copy_v2_v2(exp, arrow_expand_axis[i]);
+			set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp);
+
+			uv[1] = 255;
+			set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp);
 		}
 
-		if (do_triple) {
-			immUniformThemeColorShadeAlpha(th_col3, -80, -120);
-			glLineWidth(4.0f);
+		/* restart */
+		if (k == 0)
+			set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp);
+	}
 
-			immBegin(GWN_PRIM_LINE_STRIP, (LINK_RESOL + 1));
+	g_batch_link.batch = GWN_batch_create_ex(GWN_PRIM_TRI_STRIP, vbo, NULL, GWN_BATCH_OWNS_VBO);
+	gpu_batch_presets_register(g_batch_link.batch);
 
-			for (i = 0; i <= LINK_RESOL; i++) {
-				immVertex2fv(pos, coord_array[i]);
-			}
+	g_batch_link.batch_single = GWN_batch_create_ex(GWN_PRIM_TRI_STRIP, vbo, NULL, 0);
+	gpu_batch_presets_register(g_batch_link.batch_single);
 
-			immEnd();
+	/* Instances data */
+	Gwn_VertFormat format_inst = {0};
+	g_batch_link.p0_id = GWN_vertformat_attr_add(&format_inst, "P0", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
+	g_batch_link.p1_id = GWN_vertformat_attr_add(&format_inst, "P1", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
+	g_batch_link.p2_id = GWN_vertformat_attr_add(&format_inst, "P2", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
+	g_batch_link.p3_id = GWN_vertformat_attr_add(&format_inst, "P3", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
+	g_batch_link.colid_id = GWN_vertformat_attr_add(&format_inst, "colid_doarrow", GWN_COMP_U8, 4, GWN_FETCH_INT);
+	g_batch_link.inst_vbo = GWN_vertbuf_create_with_format_ex(&format_inst, GWN_USAGE_STREAM);
+	GWN_vertbuf_data_alloc(g_batch_link.inst_vbo, NODELINK_GROUP_SIZE); /* Alloc max count but only draw the range we need. */
 
-			if (drawarrow) {
-				immBegin(GWN_PRIM_LINE_STRIP, 3);
-				immVertex2fv(pos, arrow1);
-				immVertex2fv(pos, arrow);
-				immVertex2fv(pos, arrow2);
-				immEnd();
-			}
-		}
+	GWN_batch_instbuf_set(g_batch_link.batch, g_batch_link.inst_vbo, true);
 
-		glLineWidth(1.5f);
+	nodelink_batch_reset();
+}
 
-		if (drawarrow) {
-			immUniformThemeColorBlend(th_col1, th_col2, 0.5f);
+static char nodelink_get_color_id(int th_col)
+{
+	switch (th_col) {
+		case TH_WIRE:        return 1;
+		case TH_WIRE_INNER:  return 2;
+		case TH_ACTIVE:      return 3;
+		case TH_EDGE_SELECT: return 4;
+		case TH_REDALERT:    return 5;
+	}
+	return 0;
+}
 
-			immBegin(GWN_PRIM_LINE_STRIP, 3);
-			immVertex2fv(pos, arrow1);
-			immVertex2fv(pos, arrow);
-			immVertex2fv(pos, arrow2);
-			immEnd();
-		}
+static void nodelink_batch_draw(SpaceNode *snode)
+{
+	if (g_batch_link.count == 0)
+		return;
 
-		if (!do_shaded) {
-			immUniformThemeColor(th_col1);
+	glEnable(GL_BLEND);
 
-			immBegin(GWN_PRIM_LINE_STRIP, (LINK_RESOL + 1));
+	float colors[6][4] = {{0.0f}};
+	UI_GetThemeColor4fv(TH_WIRE_INNER,  colors[nodelink_get_color_id(TH_WIRE_INNER)]);
+	UI_GetThemeColor4fv(TH_WIRE,        colors[nodelink_get_color_id(TH_WIRE)]);
+	UI_GetThemeColor4fv(TH_ACTIVE,      colors[nodelink_get_color_id(TH_ACTIVE)]);
+	UI_GetThemeColor4fv(TH_EDGE_SELECT, colors[nodelink_get_color_id(TH_EDGE_SELECT)]);
+	UI_GetThemeColor4fv(TH_REDALERT,    colors[nodelink_get_color_id(TH_REDALERT)]);
 
-			for (i = 0; i <= LINK_RESOL; i++) {
-				immVertex2fv(pos, coord_array[i]);
-			}
+	GWN_vertbuf_vertex_count_set(g_batch_link.inst_vbo, g_batch_link.count);
+	GWN_vertbuf_use(g_batch_link.inst_vbo); /* force update. */
 
-			immEnd();
-		}
+	GWN_batch_program_set_builtin(g_batch_link.batch, GPU_SHADER_2D_NODELINK_INST);
+	GWN_batch_uniform_4fv_array(g_batch_link.batch, "colors", 6, (float *)colors);
+	GWN_batch_uniform_1f(g_batch_link.batch, "expandSize", snode->aspect * LINK_WIDTH);
+	GWN_batch_uniform_1f(g_batch_link.batch, "arrowSize", ARROW_SIZE);
+	GWN_batch_draw(g_batch_link.batch);
 
-		if (do_triple || drawarrow || (!do_shaded)) {
-			immUnbindProgram();
-		}
+	nodelink_batch_reset();
 
-		if (do_shaded) {
-			unsigned char col[3];
+	glDisable(GL_BLEND);
+}
 
-			Gwn_VertFormat *format = immVertexFormat();
-			pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
-			unsigned int color = GWN_vertformat_attr_add(format, "color", GWN_COMP_U8, 3, GWN_FETCH_INT_TO_FLOAT_UNIT);
+void nodelink_batch_start(SpaceNode *UNUSED(snode))
+{
+	g_batch_link.enabled = true;
+}
 
-			immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR);
+void nodelink_batch_end(SpaceNode *snode)
+{
+	nodelink_batch_draw(snode);
+	g_batch_link.enabled = false;
+}
 
-			immBegin(GWN_PRIM_LINE_STRIP, (LINK_RESOL + 1));
+static void nodelink_batch_add_link(
+        SpaceNode *snode,
+        const float p0[2], const float p1[2], const float p2[2], const float p3[2],
+        int th_col1, int th_col2, int th_col3, bool drawarrow)
+{
+	/* Only allow these colors. If more is needed, you need to modify the shader accordingly. */
+	BLI_assert(ELEM(th_col1, TH_WIRE_INNER, TH_WIRE, TH_ACTIVE, TH_EDGE_SELECT, TH_REDALERT));
+	BLI_assert(ELEM(th_col2, TH_WIRE_INNER, TH_WIRE, TH_ACTIVE, TH_EDGE_SELECT, TH_REDALERT));
+	BLI_assert(ELEM(th_col3, TH_WIRE, -1));
 
-			for (i = 0; i <= LINK_RESOL; i++) {
-				UI_GetThemeColorBlend3ubv(th_col1, th_col2, spline_step, col);
-				immAttrib3ubv(color, col);
+	g_batch_link.count++;
+	copy_v2_v2(GWN_vertbuf_raw_step(&g_batch_link.p0_step), p0);
+	copy_v2_v2(GWN_vertbuf_raw_step(&g_batch_link.p1_step), p1);
+	copy_v2_v2(GWN_vertbuf_raw_step(&g_batch_link.p2_step), p2);
+	copy_v2_v2(GWN_vertbuf_raw_step(&g_batch_link.p3_step), p3);
+	char *colid = GWN_vertbuf_raw_step(&g_batch_link.colid_step);
+	colid[0] = nodelink_get_color_id(th_col1);
+	colid[1] = nodelink_get_color_id(th_col2);
+	colid[2] = nodelink_get_color_id(th_col3);
+	colid[3] = drawarrow;
 
-				immVertex2fv(pos, coord_array[i]);
+	if (g_batch_link.count == NODELINK_GROUP_SIZE) {
+		nodelink_batch_draw(snode);
+	}
+}
 
-				spline_step += dist;
-			}
+/* don't do shadows if th_col3 is -1. */
+void node_draw_link_bezier(View2D *v2d, SpaceNode *snode, bNodeLink *link,
+                           int th_col1, int th_col2, int th_col3)
+{
+	float vec[4][2];
 
-			immEnd();
+	if (node_link_bezier_handles(v2d, snode, link, vec)) {
+		int drawarrow = ((link->tonode && (link->tonode->type == NODE_REROUTE)) &&
+		                 (link->fromnode && (link->fromnode->type == NODE_REROUTE)));
 
-			immUnbindProgram();
+		if (g_batch_link.batch == NULL) {
+			nodelink_batch_init();
+		}
+
+		if (g_batch_link.enabled) {
+			/* Add link to batch. */
+			nodelink_batch_add_link(snode, vec[0], vec[1], vec[2], vec[3], th_col1, th_col2, th_col3, drawarrow);
+		}
+		else {
+			/* Draw single link. */
+			float colors[3][4] = {{0.0f}};
+			if (th_col3 != -1) {
+				UI_GetThemeColor4fv(th_col3, colors[0]);
+			}
+			UI_GetThemeColor4fv(th_col1, colors[1]);
+			UI_GetThemeColor4fv(th_col2, colors[2]);
+
+			Gwn_Batch *batch = g_batch_link.batch_single;
+			GWN_batch_program_set_builtin(batch, GPU_SHADER_2D_NODELINK);
+			GWN_batch_uniform_2fv_array(batch, "bezierPts", 4, (float *)vec);
+			GWN_batch_uniform_4fv_array(batch, "colors", 3, (float *)colors);
+			GWN_batch_uniform_1f(batch, "expandSize", snode->aspect * LINK_WIDTH);
+			GWN_batch_uniform_1f(batch, "arrowSize", ARROW_SIZE);
+			GWN_batch_uniform_1i(batch, "doArrow", drawarrow);
+			GWN_batch_draw(batch);
 		}
-		
-		glDisable(GL_LINE_SMOOTH);
 	}
 }
 
 /* note; this is used for fake links in groups too */
 void node_draw_link(View2D *v2d, SpaceNode *snode, bNodeLink *link)
 {
-	bool do_shaded = false;
-	bool do_triple = false;
 	int th_col1 = TH_WIRE_INNER, th_col2 = TH_WIRE_INNER, th_col3 = TH_WIRE;
 	
 	if (link->fromsock == NULL && link->tosock == NULL)
@@ -3518,8 +3635,7 @@ void node_draw_link(View2D *v2d, SpaceNode *snode, bNodeLink *link)
 	
 	/* new connection */
 	if (!link->fromsock || !link->tosock) {
-		th_col1 = TH_ACTIVE;
-		do_triple = true;
+		th_col1 = th_col2 = TH_ACTIVE;
 	}
 	else {
 		/* going to give issues once... */
@@ -3540,15 +3656,14 @@ void node_draw_link(View2D *v2d, SpaceNode *snode, bNodeLink *link)
 				if (link->tonode && link->tonode->flag & SELECT)
 					th_col2 = TH_EDGE_SELECT;
 			}
-			do_shaded = true;
-			do_triple = true;
 		}
 		else {
-			th_col1 = TH_REDALERT;
+			th_col1 = th_col2 = TH_REDALERT;
+			// th_col3 = -1; /* no shadow */
 		}
 	}
 
-	node_draw_link_bezier(v2d, snode, link, th_col1, do_shaded, th_col2, do_triple, th_col3);
+	node_draw_link_bezier(v2d, snode, link, th_col1, th_col2, th_col3);
 //	node_draw_link_straight(v2d, snode, link, th_col1, do_shaded, th_col2, do_triple, th_col3);
 }
 
diff --git a/source/blender/editors/space_node/node_draw.c b/source/blender/editors/space_node/node_draw.c
index 83ea519a015afadb4d98a22f4468fcbf7ed9f276..c82c56ea6487e1a08d0c73b0b449f64ec9697eb2 100644
--- a/source/blender/editors/space_node/node_draw.c
+++ b/source/blender/editors/space_node/node_draw.c
@@ -621,13 +621,11 @@ static void node_draw_mute_line(View2D *v2d, SpaceNode *snode, bNode *node)
 	bNodeLink *link;
 
 	glEnable(GL_BLEND);
-	glEnable(GL_LINE_SMOOTH);
 
 	for (link = node->internal_links.first; link; link = link->next)
-		node_draw_link_bezier(v2d, snode, link, TH_REDALERT, 0, TH_WIRE, 0, TH_WIRE);
+		node_draw_link_bezier(v2d, snode, link, TH_REDALERT, TH_REDALERT, -1);
 
 	glDisable(GL_BLEND);
-	glDisable(GL_LINE_SMOOTH);
 }
 
 static void node_socket_circle_draw(const bContext *C, bNodeTree *ntree, PointerRNA node_ptr, bNodeSocket *sock, unsigned pos, unsigned col)
@@ -1246,12 +1244,12 @@ void node_draw_nodetree(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeT
 	
 	/* node lines */
 	glEnable(GL_BLEND);
-	glEnable(GL_LINE_SMOOTH);
+	nodelink_batch_start(snode);
 	for (link = ntree->links.first; link; link = link->next) {
 		if (!nodeLinkIsHidden(link))
 			node_draw_link(&ar->v2d, snode, link);
 	}
-	glDisable(GL_LINE_SMOOTH);
+	nodelink_batch_end(snode);
 	glDisable(GL_BLEND);
 	
 	/* draw foreground nodes, last nodes in front */
diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h
index 7138b63364a1d192bc26a95935e39f4b9597f669..1235133f8aca13cb5c61dfeeb438d8c99a3e6846 100644
--- a/source/blender/editors/space_node/node_intern.h
+++ b/source/blender/editors/space_node/node_intern.h
@@ -130,8 +130,11 @@ void NODE_OT_backimage_fit(struct wmOperatorType *ot);
 void NODE_OT_backimage_sample(struct wmOperatorType *ot);
 
 /* drawnode.c */
+void nodelink_batch_start(struct SpaceNode *snode);
+void nodelink_batch_end(struct SpaceNode *snode);
+
 void node_draw_link(struct View2D *v2d, struct SpaceNode *snode, struct bNodeLink *link);
-void node_draw_link_bezier(struct View2D *v2d, struct SpaceNode *snode, struct bNodeLink *link, int th_col1, bool do_shaded, int th_col2, bool do_triple, int th_col3);
+void node_draw_link_bezier(struct View2D *v2d, struct SpaceNode *snode, struct bNodeLink *link, int th_col1, int th_col2, int th_col3);
 bool node_link_bezier_points(struct View2D *v2d, struct SpaceNode *snode, struct bNodeLink *link, float coord_array[][2], int resol);
 // void node_draw_link_straight(View2D *v2d, SpaceNode *snode, bNodeLink *link, int th_col1, int do_shaded, int th_col2, int do_triple, int th_col3 );
 void draw_nodespace_back_pix(const struct bContext *C, struct ARegion *ar, struct SpaceNode *snode, bNodeInstanceKey parent_key);
diff --git a/source/blender/editors/space_node/node_templates.c b/source/blender/editors/space_node/node_templates.c
index 1047c498e4d975c8afc0d8118cae360759870690..241ab60fe43af92873846516a2ecc2aed66bc2df 100644
--- a/source/blender/editors/space_node/node_templates.c
+++ b/source/blender/editors/space_node/node_templates.c
@@ -53,7 +53,7 @@
 
 #include "ED_node.h"  /* own include */
 
-#include "ED_util.h"
+#include "ED_undo.h"
 
 
 /************************* Node Socket Manipulation **************************/
diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c
index 90e59a677ac4e7f9345d454d1e6f774335bc6c43..15b411280a5a206c72b062d9077056a72fb7b579 100644
--- a/source/blender/editors/space_outliner/outliner_draw.c
+++ b/source/blender/editors/space_outliner/outliner_draw.c
@@ -1902,10 +1902,10 @@ void draw_outliner(const bContext *C)
 {
 	EvaluationContext eval_ctx;
 	CTX_data_eval_ctx(C, &eval_ctx);
-	Object *obedit = OBEDIT_FROM_EVAL_CTX(&eval_ctx);
 	Main *mainvar = CTX_data_main(C); 
 	Scene *scene = CTX_data_scene(C);
 	ViewLayer *view_layer = CTX_data_view_layer(C);
+	Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
 	ARegion *ar = CTX_wm_region(C);
 	View2D *v2d = &ar->v2d;
 	SpaceOops *soops = CTX_wm_space_outliner(C);
@@ -1914,7 +1914,7 @@ void draw_outliner(const bContext *C)
 	TreeElement *te_edit = NULL;
 	bool has_restrict_icons;
 
-	outliner_build_tree(mainvar, &eval_ctx, scene, view_layer, soops, ar); // always
+	outliner_build_tree(mainvar, scene, view_layer, soops, ar); // always
 	
 	/* get extents of data */
 	outliner_height(soops, &soops->tree, &sizey);
diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c
index 6bbec6a1a48697114c6f35f39f2dc79dbf86ead5..f5d117922c018effe0faa58f909f934128befb08 100644
--- a/source/blender/editors/space_outliner/outliner_edit.c
+++ b/source/blender/editors/space_outliner/outliner_edit.c
@@ -51,7 +51,6 @@
 
 #include "BKE_animsys.h"
 #include "BKE_context.h"
-#include "BKE_global.h"
 #include "BKE_idcode.h"
 #include "BKE_layer.h"
 #include "BKE_library.h"
@@ -64,7 +63,6 @@
 #include "BKE_material.h"
 #include "BKE_group.h"
 
-#include "DEG_depsgraph.h"
 #include "DEG_depsgraph_build.h"
 
 #include "../blenloader/BLO_readfile.h"
@@ -978,7 +976,6 @@ static int outliner_open_back(TreeElement *te)
 
 static int outliner_show_active_exec(bContext *C, wmOperator *UNUSED(op))
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	SpaceOops *so = CTX_wm_space_outliner(C);
 	ViewLayer *view_layer = CTX_data_view_layer(C);
 	ARegion *ar = CTX_wm_region(C);
@@ -999,13 +996,13 @@ static int outliner_show_active_exec(bContext *C, wmOperator *UNUSED(op))
 		/* traverse down the bone hierarchy in case of armature */
 		TreeElement *te_obact = te;
 
-		if (workspace->object_mode & OB_MODE_POSE) {
+		if (obact->mode & OB_MODE_POSE) {
 			bPoseChannel *pchan = CTX_data_active_pose_bone(C);
 			if (pchan) {
 				te = outliner_find_posechannel(&te_obact->subtree, pchan);
 			}
 		}
-		else if (workspace->object_mode & OB_MODE_EDIT) {
+		else if (obact->mode & OB_MODE_EDIT) {
 			EditBone *ebone = CTX_data_active_bone(C);
 			if (ebone) {
 				te = outliner_find_editbone(&te_obact->subtree, ebone);
diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h
index 148af52050d6deda606540a70146741cd5242798..bedd2f09f3b066909fd448d9e98095a25693d1dc 100644
--- a/source/blender/editors/space_outliner/outliner_intern.h
+++ b/source/blender/editors/space_outliner/outliner_intern.h
@@ -193,7 +193,7 @@ void outliner_free_tree_element(TreeElement *element, ListBase *parent_subtree);
 void outliner_remove_treestore_element(struct SpaceOops *soops, TreeStoreElem *tselem);
 
 void outliner_build_tree(
-        struct Main *mainvar, const struct EvaluationContext *eval_ctx,
+        struct Main *mainvar,
         struct Scene *scene, struct ViewLayer *view_layer,
         struct SpaceOops *soops, struct ARegion *ar);
 
diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c
index e720eedbb2219931cd1b527c0217c98935c50630..bfa8633ab7d1986371d14ea0462a6e14485f687a 100644
--- a/source/blender/editors/space_outliner/outliner_select.c
+++ b/source/blender/editors/space_outliner/outliner_select.c
@@ -57,7 +57,7 @@
 #include "ED_object.h"
 #include "ED_screen.h"
 #include "ED_sequencer.h"
-#include "ED_util.h"
+#include "ED_undo.h"
 
 #include "WM_api.h"
 #include "WM_types.h"
@@ -193,10 +193,10 @@ static eOLDrawState tree_element_set_active_object(
 			WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
 		}
 	}
-
-	if (CTX_data_edit_object(C)) {
+	
+	if (ob != OBEDIT_FROM_VIEW_LAYER(view_layer))
 		ED_object_editmode_exit(C, EM_FREEDATA | EM_FREEUNDO | EM_WAITCURSOR | EM_DO_UNDO);
-	}
+		
 	return OL_DRAWSEL_NORMAL;
 }
 
@@ -658,7 +658,6 @@ static eOLDrawState tree_element_active_text(
 static eOLDrawState tree_element_active_pose(
         bContext *C, ViewLayer *view_layer, TreeElement *UNUSED(te), TreeStoreElem *tselem, const eOLSetState set)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Object *ob = (Object *)tselem->id;
 	Base *base = BKE_view_layer_base_find(view_layer, ob);
 
@@ -668,18 +667,16 @@ static eOLDrawState tree_element_active_pose(
 	}
 
 	if (set != OL_SETSEL_NONE) {
-		if (workspace->object_mode & OB_MODE_EDIT) {
+		if (OBEDIT_FROM_VIEW_LAYER(view_layer))
 			ED_object_editmode_exit(C, EM_FREEDATA | EM_FREEUNDO | EM_WAITCURSOR | EM_DO_UNDO);
-		}
-		if (workspace->object_mode & OB_MODE_POSE) {
+		
+		if (ob->mode & OB_MODE_POSE)
 			ED_armature_exit_posemode(C, base);
-		}
-		else {
+		else 
 			ED_armature_enter_posemode(C, base);
-		}
 	}
 	else {
-		if (workspace->object_mode & OB_MODE_POSE) {
+		if (ob->mode & OB_MODE_POSE) {
 			return OL_DRAWSEL_NORMAL;
 		}
 	}
diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c
index c30b14d43fac2179136341af079dde8e66165780..c19dcc0e1cbfd1eac37657b06afd601b33fd36eb 100644
--- a/source/blender/editors/space_outliner/outliner_tools.c
+++ b/source/blender/editors/space_outliner/outliner_tools.c
@@ -73,7 +73,7 @@
 #include "ED_scene.h"
 #include "ED_screen.h"
 #include "ED_sequencer.h"
-#include "ED_util.h"
+#include "ED_undo.h"
 
 #include "WM_api.h"
 #include "WM_types.h"
@@ -1855,7 +1855,7 @@ void OUTLINER_OT_modifier_operation(wmOperatorType *ot)
 static EnumPropertyItem prop_collection_op_types[] = {
 	{OL_COLLECTION_OP_OBJECTS_ADD, "OBJECTS_ADD", ICON_ZOOMIN, "Add Selected", "Add selected objects to collection"},
 	{OL_COLLECTION_OP_OBJECTS_REMOVE, "OBJECTS_REMOVE", ICON_X, "Remove Selected", "Remove selected objects from collection"},
-	{OL_COLLECTION_OP_OBJECTS_SELECT, "OBJECTS_SELECT", ICON_RESTRICT_SELECT_OFF, "Select Objects", "Selected collection objects"},
+	{OL_COLLECTION_OP_OBJECTS_SELECT, "OBJECTS_SELECT", ICON_RESTRICT_SELECT_OFF, "Select Objects", "Select collection objects"},
 	{OL_COLLECTION_OP_COLLECTION_NEW, "COLLECTION_NEW", ICON_NEW, "New Collection", "Add a new nested collection"},
 	{OL_COLLECTION_OP_COLLECTION_COPY, "COLLECTION_DUPLI", ICON_NONE, "Duplicate Collection", "Duplicate the collection"},
 	{OL_COLLECTION_OP_COLLECTION_UNLINK, "COLLECTION_UNLINK", ICON_UNLINKED, "Unlink", "Unlink collection"},
diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c
index 8c6d7783f010c26ea674004a922f15285bee227e..eff726868c7be2db8714502c3e392db4544021df 100644
--- a/source/blender/editors/space_outliner/outliner_tree.c
+++ b/source/blender/editors/space_outliner/outliner_tree.c
@@ -92,12 +92,10 @@
 
 /* prototypes */
 static void outliner_add_layer_collections_recursive(
-        SpaceOops *soops, const EvaluationContext *eval_ctx,
-        ListBase *tree, ID *id, ListBase *layer_collections, TreeElement *parent_ten,
+        SpaceOops *soops, ListBase *tree, ID *id, ListBase *layer_collections, TreeElement *parent_ten,
         const bool show_objects);
 static void outliner_add_view_layer(
-        SpaceOops *soops, const EvaluationContext *eval_ctx,
-        ListBase *tree, TreeElement *parent,
+        SpaceOops *soops, ListBase *tree, TreeElement *parent,
         Scene *scene, ViewLayer *layer, const bool show_objects);
 static void outliner_make_hierarchy(ListBase *lb);
 
@@ -235,25 +233,23 @@ void outliner_free_tree_element(TreeElement *element, ListBase *parent_subtree)
 /* ********************************************************* */
 
 /* Prototype, see functions below */
-static TreeElement *outliner_add_element(
-        SpaceOops *soops, const EvaluationContext *eval_ctx,
-        ListBase *lb, void *idv, TreeElement *parent, short type, short index);
+static TreeElement *outliner_add_element(SpaceOops *soops, ListBase *lb, void *idv, 
+                                         TreeElement *parent, short type, short index);
 
 /* -------------------------------------------------------- */
 
 /* special handling of hierarchical non-lib data */
-static void outliner_add_bone(
-        SpaceOops *soops, const EvaluationContext *eval_ctx,
-        ListBase *lb, ID *id, Bone *curBone, TreeElement *parent, int *a)
+static void outliner_add_bone(SpaceOops *soops, ListBase *lb, ID *id, Bone *curBone,
+                              TreeElement *parent, int *a)
 {
-	TreeElement *te = outliner_add_element(soops, eval_ctx, lb, id, parent, TSE_BONE, *a);
+	TreeElement *te = outliner_add_element(soops, lb, id, parent, TSE_BONE, *a);
 	
 	(*a)++;
 	te->name = curBone->name;
 	te->directdata = curBone;
 	
 	for (curBone = curBone->childbase.first; curBone; curBone = curBone->next) {
-		outliner_add_bone(soops, eval_ctx, &te->subtree, id, curBone, te, a);
+		outliner_add_bone(soops, &te->subtree, id, curBone, te, a);
 	}
 }
 
@@ -261,9 +257,7 @@ static void outliner_add_bone(
 
 #define LOG2I(x) (int)(log(x) / M_LN2)
 
-static void outliner_add_passes(
-        SpaceOops *soops, const EvaluationContext *eval_ctx,
-        TreeElement *tenla, ID *id, ViewLayer *view_layer)
+static void outliner_add_passes(SpaceOops *soops, TreeElement *tenla, ID *id, ViewLayer *view_layer)
 {
 	TreeStoreElem *tselem = NULL;
 	TreeElement *te = NULL;
@@ -271,7 +265,7 @@ static void outliner_add_passes(
 	/* log stuff is to convert bitflags (powers of 2) to small integers,
 	 * in order to not overflow short tselem->nr */
 	
-	te = outliner_add_element(soops, eval_ctx, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_COMBINED));
+	te = outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_COMBINED));
 	te->name = IFACE_("Combined");
 	te->directdata = &view_layer->passflag;
 	
@@ -280,71 +274,71 @@ static void outliner_add_passes(
 	if (tselem->flag & TSE_CLOSED)
 		return;
 	
-	te = outliner_add_element(soops, eval_ctx, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_Z));
+	te = outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_Z));
 	te->name = IFACE_("Z");
 	te->directdata = &view_layer->passflag;
 
-	te = outliner_add_element(soops, eval_ctx, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_VECTOR));
+	te = outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_VECTOR));
 	te->name = IFACE_("Vector");
 	te->directdata = &view_layer->passflag;
 
-	te = outliner_add_element(soops, eval_ctx, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_NORMAL));
+	te = outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_NORMAL));
 	te->name = IFACE_("Normal");
 	te->directdata = &view_layer->passflag;
 
-	te = outliner_add_element(soops, eval_ctx, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_UV));
+	te = outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_UV));
 	te->name = IFACE_("UV");
 	te->directdata = &view_layer->passflag;
 
-	te = outliner_add_element(soops, eval_ctx, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_MIST));
+	te = outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_MIST));
 	te->name = IFACE_("Mist");
 	te->directdata = &view_layer->passflag;
 
-	te = outliner_add_element(soops, eval_ctx, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_INDEXOB));
+	te = outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_INDEXOB));
 	te->name = IFACE_("Index Object");
 	te->directdata = &view_layer->passflag;
 
-	te = outliner_add_element(soops, eval_ctx, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_INDEXMA));
+	te = outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_INDEXMA));
 	te->name = IFACE_("Index Material");
 	te->directdata = &view_layer->passflag;
 
-	te = outliner_add_element(soops, eval_ctx, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_RGBA));
+	te = outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_RGBA));
 	te->name = IFACE_("Color");
 	te->directdata = &view_layer->passflag;
 
-	te = outliner_add_element(soops, eval_ctx, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_DIFFUSE));
+	te = outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_DIFFUSE));
 	te->name = IFACE_("Diffuse");
 	te->directdata = &view_layer->passflag;
 
-	te = outliner_add_element(soops, eval_ctx, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_SPEC));
+	te = outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_SPEC));
 	te->name = IFACE_("Specular");
 	te->directdata = &view_layer->passflag;
 
-	te = outliner_add_element(soops, eval_ctx, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_SHADOW));
+	te = outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_SHADOW));
 	te->name = IFACE_("Shadow");
 	te->directdata = &view_layer->passflag;
 
-	te = outliner_add_element(soops, eval_ctx, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_AO));
+	te = outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_AO));
 	te->name = IFACE_("AO");
 	te->directdata = &view_layer->passflag;
 
-	te = outliner_add_element(soops, eval_ctx, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_REFLECT));
+	te = outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_REFLECT));
 	te->name = IFACE_("Reflection");
 	te->directdata = &view_layer->passflag;
 
-	te = outliner_add_element(soops, eval_ctx, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_REFRACT));
+	te = outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_REFRACT));
 	te->name = IFACE_("Refraction");
 	te->directdata = &view_layer->passflag;
 
-	te = outliner_add_element(soops, eval_ctx, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_INDIRECT));
+	te = outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_INDIRECT));
 	te->name = IFACE_("Indirect");
 	te->directdata = &view_layer->passflag;
 
-	te = outliner_add_element(soops, eval_ctx, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_ENVIRONMENT));
+	te = outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_ENVIRONMENT));
 	te->name = IFACE_("Environment");
 	te->directdata = &view_layer->passflag;
 
-	te = outliner_add_element(soops, eval_ctx, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_EMIT));
+	te = outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_EMIT));
 	te->name = IFACE_("Emit");
 	te->directdata = &view_layer->passflag;
 }
@@ -379,43 +373,41 @@ static void outliner_add_line_styles(SpaceOops *soops, ListBase *lb, Scene *sce,
 				if (!(linestyle->id.tag & LIB_TAG_DOIT))
 					continue;
 				linestyle->id.tag &= ~LIB_TAG_DOIT;
-				outliner_add_element(soops, eval_ctx, lb, linestyle, te, 0, 0);
+				outliner_add_element(soops, lb, linestyle, te, 0, 0);
 			}
 		}
 	}
 }
 #endif
 
-static void outliner_add_scene_contents(
-        SpaceOops *soops, const EvaluationContext *eval_ctx,
-        ListBase *lb, Scene *sce, TreeElement *te)
+static void outliner_add_scene_contents(SpaceOops *soops, ListBase *lb, Scene *sce, TreeElement *te)
 {
 	ViewLayer *view_layer;
-	TreeElement *tenla = outliner_add_element(soops, eval_ctx, lb, sce, te, TSE_R_LAYER_BASE, 0);
+	TreeElement *tenla = outliner_add_element(soops, lb, sce, te, TSE_R_LAYER_BASE, 0);
 	int a;
 	
 	tenla->name = IFACE_("View Layers");
 	for (a = 0, view_layer = sce->view_layers.first; view_layer; view_layer = view_layer->next, a++) {
-		TreeElement *tenlay = outliner_add_element(soops, eval_ctx, &tenla->subtree, sce, te, TSE_R_LAYER, a);
+		TreeElement *tenlay = outliner_add_element(soops, &tenla->subtree, sce, te, TSE_R_LAYER, a);
 		tenlay->name = view_layer->name;
 		tenlay->directdata = &view_layer->flag;
 
 		TreeElement *te_view_layers;
-		te_view_layers = outliner_add_element(soops, eval_ctx, &tenlay->subtree, sce, tenlay, TSE_LAYER_COLLECTION_BASE, 0);
+		te_view_layers = outliner_add_element(soops, &tenlay->subtree, sce, tenlay, TSE_LAYER_COLLECTION_BASE, 0);
 		te_view_layers->name = IFACE_("Collections");
-		outliner_add_view_layer(soops, eval_ctx, &te_view_layers->subtree, te_view_layers, sce, view_layer, false);
+		outliner_add_view_layer(soops, &te_view_layers->subtree, te_view_layers, sce, view_layer, false);
 
 		TreeElement *te_passes;
-		te_passes = outliner_add_element(soops, eval_ctx, &tenlay->subtree, sce, tenlay, TSE_LAYER_COLLECTION_BASE, 0);
+		te_passes = outliner_add_element(soops, &tenlay->subtree, sce, tenlay, TSE_LAYER_COLLECTION_BASE, 0);
 		te_passes->name = IFACE_("Passes");
-		outliner_add_passes(soops, eval_ctx, te_passes, &sce->id, view_layer);
+		outliner_add_passes(soops, te_passes, &sce->id, view_layer);
 	}
 	
 	// TODO: move this to the front?
 	if (outliner_animdata_test(sce->adt))
-		outliner_add_element(soops, eval_ctx, lb, sce, te, TSE_ANIM_DATA, 0);
+		outliner_add_element(soops, lb, sce, te, TSE_ANIM_DATA, 0);
 		
-	outliner_add_element(soops, eval_ctx, lb, sce->gpd, te, 0, 0);
+	outliner_add_element(soops, lb, sce->gpd, te, 0, 0);
 	
 #ifdef WITH_FREESTYLE
 	if (STREQ(sce->view_render->engine_id, RE_engine_id_BLENDER_RENDER) && (sce->r.mode & R_EDGE_FRS))
@@ -520,41 +512,37 @@ static bool outliner_object_reorder_poll(
 }
 
 // can be inlined if necessary
-static void outliner_add_object_contents(
-        SpaceOops *soops, const EvaluationContext *eval_ctx,
-        TreeElement *te, TreeStoreElem *tselem, Object *ob)
+static void outliner_add_object_contents(SpaceOops *soops, TreeElement *te, TreeStoreElem *tselem, Object *ob)
 {
 	te->reinsert = outliner_object_reorder;
 	te->reinsert_poll = outliner_object_reorder_poll;
 
 	if (outliner_animdata_test(ob->adt))
-		outliner_add_element(soops, eval_ctx, &te->subtree, ob, te, TSE_ANIM_DATA, 0);
+		outliner_add_element(soops, &te->subtree, ob, te, TSE_ANIM_DATA, 0);
 
-	outliner_add_element(soops, eval_ctx, &te->subtree, ob->poselib, te, 0, 0); // XXX FIXME.. add a special type for this
+	outliner_add_element(soops, &te->subtree, ob->poselib, te, 0, 0); // XXX FIXME.. add a special type for this
 	
 	if (ob->proxy && !ID_IS_LINKED(ob))
-		outliner_add_element(soops, eval_ctx, &te->subtree, ob->proxy, te, TSE_PROXY, 0);
+		outliner_add_element(soops, &te->subtree, ob->proxy, te, TSE_PROXY, 0);
 		
-	outliner_add_element(soops, eval_ctx, &te->subtree, ob->gpd, te, 0, 0);
+	outliner_add_element(soops, &te->subtree, ob->gpd, te, 0, 0);
 	
-	outliner_add_element(soops, eval_ctx, &te->subtree, ob->data, te, 0, 0);
+	outliner_add_element(soops, &te->subtree, ob->data, te, 0, 0);
 	
 	if (ob->pose) {
 		bArmature *arm = ob->data;
 		bPoseChannel *pchan;
-		TreeElement *tenla = outliner_add_element(
-		        soops, eval_ctx, &te->subtree, ob, te, TSE_POSE_BASE, 0);
+		TreeElement *tenla = outliner_add_element(soops, &te->subtree, ob, te, TSE_POSE_BASE, 0);
 		
 		tenla->name = IFACE_("Pose");
 		
 		/* channels undefined in editmode, but we want the 'tenla' pose icon itself */
-		if ((arm->edbo == NULL) && (eval_ctx->object_mode & OB_MODE_POSE)) {
+		if ((arm->edbo == NULL) && (ob->mode & OB_MODE_POSE)) {
 			TreeElement *ten;
 			int a = 0, const_index = 1000;    /* ensure unique id for bone constraints */
 			
 			for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next, a++) {
-				ten = outliner_add_element(
-				        soops, eval_ctx, &tenla->subtree, ob, tenla, TSE_POSE_CHANNEL, a);
+				ten = outliner_add_element(soops, &tenla->subtree, ob, tenla, TSE_POSE_CHANNEL, a);
 				ten->name = pchan->name;
 				ten->directdata = pchan;
 				pchan->temp = (void *)ten;
@@ -563,14 +551,12 @@ static void outliner_add_object_contents(
 					//Object *target;
 					bConstraint *con;
 					TreeElement *ten1;
-					TreeElement *tenla1 = outliner_add_element(
-					        soops, eval_ctx, &ten->subtree, ob, ten, TSE_CONSTRAINT_BASE, 0);
+					TreeElement *tenla1 = outliner_add_element(soops, &ten->subtree, ob, ten, TSE_CONSTRAINT_BASE, 0);
 					//char *str;
 					
 					tenla1->name = IFACE_("Constraints");
 					for (con = pchan->constraints.first; con; con = con->next, const_index++) {
-						ten1 = outliner_add_element(
-						        soops, eval_ctx, &tenla1->subtree, ob, tenla1, TSE_CONSTRAINT, const_index);
+						ten1 = outliner_add_element(soops, &tenla1->subtree, ob, tenla1, TSE_CONSTRAINT, const_index);
 #if 0 /* disabled as it needs to be reworked for recoded constraints system */
 						target = get_constraint_target(con, &str);
 						if (str && str[0]) ten1->name = str;
@@ -604,15 +590,13 @@ static void outliner_add_object_contents(
 		/* Pose Groups */
 		if (ob->pose->agroups.first) {
 			bActionGroup *agrp;
-			TreeElement *ten_bonegrp = outliner_add_element(
-			        soops, eval_ctx, &te->subtree, ob, te, TSE_POSEGRP_BASE, 0);
+			TreeElement *ten_bonegrp = outliner_add_element(soops, &te->subtree, ob, te, TSE_POSEGRP_BASE, 0);
 			int a = 0;
 
 			ten_bonegrp->name = IFACE_("Bone Groups");
 			for (agrp = ob->pose->agroups.first; agrp; agrp = agrp->next, a++) {
 				TreeElement *ten;
-				ten = outliner_add_element(
-				        soops, eval_ctx, &ten_bonegrp->subtree, ob, ten_bonegrp, TSE_POSEGRP, a);
+				ten = outliner_add_element(soops, &ten_bonegrp->subtree, ob, ten_bonegrp, TSE_POSEGRP, a);
 				ten->name = agrp->name;
 				ten->directdata = agrp;
 			}
@@ -620,21 +604,20 @@ static void outliner_add_object_contents(
 	}
 	
 	for (int a = 0; a < ob->totcol; a++) {
-		outliner_add_element(
-		        soops, eval_ctx, &te->subtree, ob->mat[a], te, 0, a);
+		outliner_add_element(soops, &te->subtree, ob->mat[a], te, 0, a);
 	}
 	
 	if (ob->constraints.first) {
 		//Object *target;
 		bConstraint *con;
 		TreeElement *ten;
-		TreeElement *tenla = outliner_add_element(soops, eval_ctx, &te->subtree, ob, te, TSE_CONSTRAINT_BASE, 0);
+		TreeElement *tenla = outliner_add_element(soops, &te->subtree, ob, te, TSE_CONSTRAINT_BASE, 0);
 		//char *str;
 		int a;
 		
 		tenla->name = IFACE_("Constraints");
 		for (con = ob->constraints.first, a = 0; con; con = con->next, a++) {
-			ten = outliner_add_element(soops, eval_ctx, &tenla->subtree, ob, tenla, TSE_CONSTRAINT, a);
+			ten = outliner_add_element(soops, &tenla->subtree, ob, tenla, TSE_CONSTRAINT, a);
 #if 0 /* disabled due to constraints system targets recode... code here needs review */
 			target = get_constraint_target(con, &str);
 			if (str && str[0]) ten->name = str;
@@ -649,38 +632,32 @@ static void outliner_add_object_contents(
 	
 	if (ob->modifiers.first) {
 		ModifierData *md;
-		TreeElement *ten_mod = outliner_add_element(soops, eval_ctx, &te->subtree, ob, te, TSE_MODIFIER_BASE, 0);
+		TreeElement *ten_mod = outliner_add_element(soops, &te->subtree, ob, te, TSE_MODIFIER_BASE, 0);
 		int index;
 		
 		ten_mod->name = IFACE_("Modifiers");
 		for (index = 0, md = ob->modifiers.first; md; index++, md = md->next) {
-			TreeElement *ten = outliner_add_element(
-			        soops, eval_ctx, &ten_mod->subtree, ob, ten_mod, TSE_MODIFIER, index);
+			TreeElement *ten = outliner_add_element(soops, &ten_mod->subtree, ob, ten_mod, TSE_MODIFIER, index);
 			ten->name = md->name;
 			ten->directdata = md;
 			
 			if (md->type == eModifierType_Lattice) {
-				outliner_add_element(
-				        soops, eval_ctx, &ten->subtree, ((LatticeModifierData *) md)->object, ten, TSE_LINKED_OB, 0);
+				outliner_add_element(soops, &ten->subtree, ((LatticeModifierData *) md)->object, ten, TSE_LINKED_OB, 0);
 			}
 			else if (md->type == eModifierType_Curve) {
-				outliner_add_element(
-				        soops, eval_ctx, &ten->subtree, ((CurveModifierData *) md)->object, ten, TSE_LINKED_OB, 0);
+				outliner_add_element(soops, &ten->subtree, ((CurveModifierData *) md)->object, ten, TSE_LINKED_OB, 0);
 			}
 			else if (md->type == eModifierType_Armature) {
-				outliner_add_element(
-				        soops, eval_ctx, &ten->subtree, ((ArmatureModifierData *) md)->object, ten, TSE_LINKED_OB, 0);
+				outliner_add_element(soops, &ten->subtree, ((ArmatureModifierData *) md)->object, ten, TSE_LINKED_OB, 0);
 			}
 			else if (md->type == eModifierType_Hook) {
-				outliner_add_element(
-				        soops, eval_ctx, &ten->subtree, ((HookModifierData *) md)->object, ten, TSE_LINKED_OB, 0);
+				outliner_add_element(soops, &ten->subtree, ((HookModifierData *) md)->object, ten, TSE_LINKED_OB, 0);
 			}
 			else if (md->type == eModifierType_ParticleSystem) {
 				ParticleSystem *psys = ((ParticleSystemModifierData *) md)->psys;
 				TreeElement *ten_psys;
 				
-				ten_psys = outliner_add_element(
-				        soops, eval_ctx, &ten->subtree, ob, te, TSE_LINKED_PSYS, 0);
+				ten_psys = outliner_add_element(soops, &ten->subtree, ob, te, TSE_LINKED_PSYS, 0);
 				ten_psys->directdata = psys;
 				ten_psys->name = psys->part->id.name + 2;
 			}
@@ -691,29 +668,25 @@ static void outliner_add_object_contents(
 	if (ob->defbase.first) {
 		bDeformGroup *defgroup;
 		TreeElement *ten;
-		TreeElement *tenla = outliner_add_element(
-		        soops, eval_ctx, &te->subtree, ob, te, TSE_DEFGROUP_BASE, 0);
+		TreeElement *tenla = outliner_add_element(soops, &te->subtree, ob, te, TSE_DEFGROUP_BASE, 0);
 		int a;
 		
 		tenla->name = IFACE_("Vertex Groups");
 		for (defgroup = ob->defbase.first, a = 0; defgroup; defgroup = defgroup->next, a++) {
-			ten = outliner_add_element(soops, eval_ctx, &tenla->subtree, ob, tenla, TSE_DEFGROUP, a);
+			ten = outliner_add_element(soops, &tenla->subtree, ob, tenla, TSE_DEFGROUP, a);
 			ten->name = defgroup->name;
 			ten->directdata = defgroup;
 		}
 	}
 	
 	/* duplicated group */
-	if (ob->dup_group) {
-		outliner_add_element(soops, eval_ctx, &te->subtree, ob->dup_group, te, 0, 0);
-	}
+	if (ob->dup_group)
+		outliner_add_element(soops, &te->subtree, ob->dup_group, te, 0, 0);
 }
 
 
 // can be inlined if necessary
-static void outliner_add_id_contents(
-        SpaceOops *soops, const EvaluationContext *eval_ctx,
-        TreeElement *te, TreeStoreElem *tselem, ID *id)
+static void outliner_add_id_contents(SpaceOops *soops, TreeElement *te, TreeStoreElem *tselem, ID *id)
 {
 	/* tuck pointer back in object, to construct hierarchy */
 	if (GS(id->name) == ID_OB) id->newid = (ID *)te;
@@ -727,26 +700,25 @@ static void outliner_add_id_contents(
 		}
 		case ID_SCE:
 		{
-			outliner_add_scene_contents(soops, eval_ctx, &te->subtree, (Scene *)id, te);
+			outliner_add_scene_contents(soops, &te->subtree, (Scene *)id, te);
 			break;
 		}
 		case ID_OB:
 		{
-			outliner_add_object_contents(soops, eval_ctx, te, tselem, (Object *)id);
+			outliner_add_object_contents(soops, te, tselem, (Object *)id);
 			break;
 		}
 		case ID_ME:
 		{
 			Mesh *me = (Mesh *)id;
 			int a;
-
-			if (outliner_animdata_test(me->adt)) {
-				outliner_add_element(soops, eval_ctx, &te->subtree, me, te, TSE_ANIM_DATA, 0);
-			}
-
-			outliner_add_element(soops, eval_ctx, &te->subtree, me->key, te, 0, 0);
+			
+			if (outliner_animdata_test(me->adt))
+				outliner_add_element(soops, &te->subtree, me, te, TSE_ANIM_DATA, 0);
+			
+			outliner_add_element(soops, &te->subtree, me->key, te, 0, 0);
 			for (a = 0; a < me->totcol; a++)
-				outliner_add_element(soops, eval_ctx, &te->subtree, me->mat[a], te, 0, a);
+				outliner_add_element(soops, &te->subtree, me->mat[a], te, 0, a);
 			/* could do tfaces with image links, but the images are not grouped nicely.
 			 * would require going over all tfaces, sort images in use. etc... */
 			break;
@@ -757,10 +729,10 @@ static void outliner_add_id_contents(
 			int a;
 			
 			if (outliner_animdata_test(cu->adt))
-				outliner_add_element(soops, eval_ctx, &te->subtree, cu, te, TSE_ANIM_DATA, 0);
+				outliner_add_element(soops, &te->subtree, cu, te, TSE_ANIM_DATA, 0);
 			
 			for (a = 0; a < cu->totcol; a++)
-				outliner_add_element(soops, eval_ctx, &te->subtree, cu->mat[a], te, 0, a);
+				outliner_add_element(soops, &te->subtree, cu->mat[a], te, 0, a);
 			break;
 		}
 		case ID_MB:
@@ -769,10 +741,10 @@ static void outliner_add_id_contents(
 			int a;
 			
 			if (outliner_animdata_test(mb->adt))
-				outliner_add_element(soops, eval_ctx, &te->subtree, mb, te, TSE_ANIM_DATA, 0);
+				outliner_add_element(soops, &te->subtree, mb, te, TSE_ANIM_DATA, 0);
 			
 			for (a = 0; a < mb->totcol; a++)
-				outliner_add_element(soops, eval_ctx, &te->subtree, mb->mat[a], te, 0, a);
+				outliner_add_element(soops, &te->subtree, mb->mat[a], te, 0, a);
 			break;
 		}
 		case ID_MA:
@@ -780,14 +752,11 @@ static void outliner_add_id_contents(
 			Material *ma = (Material *)id;
 			int a;
 			
-			if (outliner_animdata_test(ma->adt)) {
-				outliner_add_element(soops, eval_ctx, &te->subtree, ma, te, TSE_ANIM_DATA, 0);
-			}
+			if (outliner_animdata_test(ma->adt))
+				outliner_add_element(soops, &te->subtree, ma, te, TSE_ANIM_DATA, 0);
 			
 			for (a = 0; a < MAX_MTEX; a++) {
-				if (ma->mtex[a]) {
-					outliner_add_element(soops, eval_ctx, &te->subtree, ma->mtex[a]->tex, te, 0, a);
-				}
+				if (ma->mtex[a]) outliner_add_element(soops, &te->subtree, ma->mtex[a]->tex, te, 0, a);
 			}
 			break;
 		}
@@ -795,10 +764,10 @@ static void outliner_add_id_contents(
 		{
 			Tex *tex = (Tex *)id;
 			
-			if (outliner_animdata_test(tex->adt)) {
-				outliner_add_element(soops, eval_ctx, &te->subtree, tex, te, TSE_ANIM_DATA, 0);
-			}
-			outliner_add_element(soops, eval_ctx, &te->subtree, tex->ima, te, 0, 0);
+			if (outliner_animdata_test(tex->adt))
+				outliner_add_element(soops, &te->subtree, tex, te, TSE_ANIM_DATA, 0);
+			
+			outliner_add_element(soops, &te->subtree, tex->ima, te, 0, 0);
 			break;
 		}
 		case ID_CA:
@@ -806,7 +775,7 @@ static void outliner_add_id_contents(
 			Camera *ca = (Camera *)id;
 			
 			if (outliner_animdata_test(ca->adt))
-				outliner_add_element(soops, eval_ctx, &te->subtree, ca, te, TSE_ANIM_DATA, 0);
+				outliner_add_element(soops, &te->subtree, ca, te, TSE_ANIM_DATA, 0);
 			break;
 		}
 		case ID_CF:
@@ -814,7 +783,7 @@ static void outliner_add_id_contents(
 			CacheFile *cache_file = (CacheFile *)id;
 
 			if (outliner_animdata_test(cache_file->adt)) {
-				outliner_add_element(soops, eval_ctx, &te->subtree, cache_file, te, TSE_ANIM_DATA, 0);
+				outliner_add_element(soops, &te->subtree, cache_file, te, TSE_ANIM_DATA, 0);
 			}
 
 			break;
@@ -825,12 +794,10 @@ static void outliner_add_id_contents(
 			int a;
 			
 			if (outliner_animdata_test(la->adt))
-				outliner_add_element(soops, eval_ctx, &te->subtree, la, te, TSE_ANIM_DATA, 0);
+				outliner_add_element(soops, &te->subtree, la, te, TSE_ANIM_DATA, 0);
 			
 			for (a = 0; a < MAX_MTEX; a++) {
-				if (la->mtex[a]) {
-					outliner_add_element(soops, eval_ctx, &te->subtree, la->mtex[a]->tex, te, 0, a);
-				}
+				if (la->mtex[a]) outliner_add_element(soops, &te->subtree, la->mtex[a]->tex, te, 0, a);
 			}
 			break;
 		}
@@ -838,18 +805,16 @@ static void outliner_add_id_contents(
 		{
 			Speaker *spk = (Speaker *)id;
 
-			if (outliner_animdata_test(spk->adt)) {
-				outliner_add_element(soops, eval_ctx, &te->subtree, spk, te, TSE_ANIM_DATA, 0);
-			}
+			if (outliner_animdata_test(spk->adt))
+				outliner_add_element(soops, &te->subtree, spk, te, TSE_ANIM_DATA, 0);
 			break;
 		}
 		case ID_LP:
 		{
 			LightProbe *prb = (LightProbe *)id;
 
-			if (outliner_animdata_test(prb->adt)) {
-				outliner_add_element(soops, eval_ctx, &te->subtree, prb, te, TSE_ANIM_DATA, 0);
-			}
+			if (outliner_animdata_test(prb->adt))
+				outliner_add_element(soops, &te->subtree, prb, te, TSE_ANIM_DATA, 0);
 			break;
 		}
 		case ID_WO:
@@ -857,13 +822,11 @@ static void outliner_add_id_contents(
 			World *wrld = (World *)id;
 			int a;
 			
-			if (outliner_animdata_test(wrld->adt)) {
-				outliner_add_element(soops, eval_ctx, &te->subtree, wrld, te, TSE_ANIM_DATA, 0);
-			}
+			if (outliner_animdata_test(wrld->adt))
+				outliner_add_element(soops, &te->subtree, wrld, te, TSE_ANIM_DATA, 0);
+			
 			for (a = 0; a < MAX_MTEX; a++) {
-				if (wrld->mtex[a]) {
-					outliner_add_element(soops, eval_ctx, &te->subtree, wrld->mtex[a]->tex, te, 0, a);
-				}
+				if (wrld->mtex[a]) outliner_add_element(soops, &te->subtree, wrld->mtex[a]->tex, te, 0, a);
 			}
 			break;
 		}
@@ -871,9 +834,8 @@ static void outliner_add_id_contents(
 		{
 			Key *key = (Key *)id;
 			
-			if (outliner_animdata_test(key->adt)) {
-				outliner_add_element(soops, eval_ctx, &te->subtree, key, te, TSE_ANIM_DATA, 0);
-			}
+			if (outliner_animdata_test(key->adt))
+				outliner_add_element(soops, &te->subtree, key, te, TSE_ANIM_DATA, 0);
 			break;
 		}
 		case ID_AC:
@@ -887,15 +849,15 @@ static void outliner_add_id_contents(
 			bArmature *arm = (bArmature *)id;
 			int a = 0;
 			
-			if (outliner_animdata_test(arm->adt)) {
-				outliner_add_element(soops, eval_ctx, &te->subtree, arm, te, TSE_ANIM_DATA, 0);
-			}
+			if (outliner_animdata_test(arm->adt))
+				outliner_add_element(soops, &te->subtree, arm, te, TSE_ANIM_DATA, 0);
+			
 			if (arm->edbo) {
 				EditBone *ebone;
 				TreeElement *ten;
 				
 				for (ebone = arm->edbo->first; ebone; ebone = ebone->next, a++) {
-					ten = outliner_add_element(soops, eval_ctx, &te->subtree, id, te, TSE_EBONE, a);
+					ten = outliner_add_element(soops, &te->subtree, id, te, TSE_EBONE, a);
 					ten->directdata = ebone;
 					ten->name = ebone->name;
 					ebone->temp.p = ten;
@@ -917,17 +879,13 @@ static void outliner_add_id_contents(
 			else {
 				/* do not extend Armature when we have posemode */
 				tselem = TREESTORE(te->parent);
-				if (GS(tselem->id->name) == ID_OB &&
-				    (eval_ctx->object_mode & OB_MODE_POSE)
-				    /* (((Object *)tselem->id)->mode & OB_MODE_POSE) */
-				    )
-				{
+				if (GS(tselem->id->name) == ID_OB && ((Object *)tselem->id)->mode & OB_MODE_POSE) {
 					/* pass */
 				}
 				else {
 					Bone *curBone;
 					for (curBone = arm->bonebase.first; curBone; curBone = curBone->next) {
-						outliner_add_bone(soops, eval_ctx, &te->subtree, id, curBone, te, &a);
+						outliner_add_bone(soops, &te->subtree, id, curBone, te, &a);
 					}
 				}
 			}
@@ -937,14 +895,13 @@ static void outliner_add_id_contents(
 		{
 			FreestyleLineStyle *linestyle = (FreestyleLineStyle *)id;
 			int a;
-
-			if (outliner_animdata_test(linestyle->adt)) {
-				outliner_add_element(soops, eval_ctx, &te->subtree, linestyle, te, TSE_ANIM_DATA, 0);
-			}
+			
+			if (outliner_animdata_test(linestyle->adt))
+				outliner_add_element(soops, &te->subtree, linestyle, te, TSE_ANIM_DATA, 0);
 
 			for (a = 0; a < MAX_MTEX; a++) {
 				if (linestyle->mtex[a])
-					outliner_add_element(soops, eval_ctx, &te->subtree, linestyle->mtex[a]->tex, te, 0, a);
+					outliner_add_element(soops, &te->subtree, linestyle->mtex[a]->tex, te, 0, a);
 			}
 			break;
 		}
@@ -955,11 +912,11 @@ static void outliner_add_id_contents(
 			int a = 0;
 			
 			if (outliner_animdata_test(gpd->adt))
-				outliner_add_element(soops, eval_ctx, &te->subtree, gpd, te, TSE_ANIM_DATA, 0);
+				outliner_add_element(soops, &te->subtree, gpd, te, TSE_ANIM_DATA, 0);
 			
 			// TODO: base element for layers?
 			for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
-				outliner_add_element(soops, eval_ctx, &te->subtree, gpl, te, TSE_GP_LAYER, a);
+				outliner_add_element(soops, &te->subtree, gpl, te, TSE_GP_LAYER, a);
 				a++;
 			}
 			break;
@@ -971,9 +928,8 @@ static void outliner_add_id_contents(
 
 // TODO: this function needs to be split up! It's getting a bit too large...
 // Note: "ID" is not always a real ID
-static TreeElement *outliner_add_element(
-        SpaceOops *soops, const EvaluationContext *eval_ctx,
-        ListBase *lb, void *idv, TreeElement *parent, short type, short index)
+static TreeElement *outliner_add_element(SpaceOops *soops, ListBase *lb, void *idv,
+                                         TreeElement *parent, short type, short index)
 {
 	TreeElement *te;
 	TreeStoreElem *tselem;
@@ -1046,7 +1002,7 @@ static TreeElement *outliner_add_element(
 		
 		/* ID datablock */
 		if (tsepar == NULL || tsepar->type != TSE_ID_BASE)
-			outliner_add_id_contents(soops, eval_ctx, te, tselem, id);
+			outliner_add_id_contents(soops, te, tselem, id);
 	}
 	else if (type == TSE_ANIM_DATA) {
 		IdAdtTemplate *iat = (IdAdtTemplate *)idv;
@@ -1057,11 +1013,11 @@ static TreeElement *outliner_add_element(
 		te->directdata = adt;
 		
 		/* Action */
-		outliner_add_element(soops, eval_ctx, &te->subtree, adt->action, te, 0, 0);
+		outliner_add_element(soops, &te->subtree, adt->action, te, 0, 0);
 		
 		/* Drivers */
 		if (adt->drivers.first) {
-			TreeElement *ted = outliner_add_element(soops, eval_ctx, &te->subtree, adt, te, TSE_DRIVER_BASE, 0);
+			TreeElement *ted = outliner_add_element(soops, &te->subtree, adt, te, TSE_DRIVER_BASE, 0);
 			ID *lastadded = NULL;
 			FCurve *fcu;
 			
@@ -1078,7 +1034,7 @@ static TreeElement *outliner_add_element(
 						{
 							if (lastadded != dtar->id) {
 								// XXX this lastadded check is rather lame, and also fails quite badly...
-								outliner_add_element(soops, eval_ctx, &ted->subtree, dtar->id, ted, TSE_LINKED_OB, 0);
+								outliner_add_element(soops, &ted->subtree, dtar->id, ted, TSE_LINKED_OB, 0);
 								lastadded = dtar->id;
 							}
 						}
@@ -1090,14 +1046,14 @@ static TreeElement *outliner_add_element(
 		
 		/* NLA Data */
 		if (adt->nla_tracks.first) {
-			TreeElement *tenla = outliner_add_element(soops, eval_ctx, &te->subtree, adt, te, TSE_NLA, 0);
+			TreeElement *tenla = outliner_add_element(soops, &te->subtree, adt, te, TSE_NLA, 0);
 			NlaTrack *nlt;
 			int a = 0;
 			
 			tenla->name = IFACE_("NLA Tracks");
 			
 			for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next) {
-				TreeElement *tenlt = outliner_add_element(soops, eval_ctx, &tenla->subtree, nlt, tenla, TSE_NLA_TRACK, a);
+				TreeElement *tenlt = outliner_add_element(soops, &tenla->subtree, nlt, tenla, TSE_NLA_TRACK, a);
 				NlaStrip *strip;
 				TreeElement *ten;
 				int b = 0;
@@ -1105,7 +1061,7 @@ static TreeElement *outliner_add_element(
 				tenlt->name = nlt->name;
 				
 				for (strip = nlt->strips.first; strip; strip = strip->next, b++) {
-					ten = outliner_add_element(soops, eval_ctx, &tenlt->subtree, strip->act, tenlt, TSE_NLA_ACTION, b);
+					ten = outliner_add_element(soops, &tenlt->subtree, strip->act, tenlt, TSE_NLA_ACTION, b);
 					if (ten) ten->directdata = strip;
 				}
 			}
@@ -1140,12 +1096,12 @@ static TreeElement *outliner_add_element(
 			if (seq->type == SEQ_TYPE_META) {
 				p = seq->seqbase.first;
 				while (p) {
-					outliner_add_element(soops, eval_ctx, &te->subtree, (void *)p, te, TSE_SEQUENCE, index);
+					outliner_add_element(soops, &te->subtree, (void *)p, te, TSE_SEQUENCE, index);
 					p = p->next;
 				}
 			}
 			else
-				outliner_add_element(soops, eval_ctx, &te->subtree, (void *)seq->strip, te, TSE_SEQ_STRIP, index);
+				outliner_add_element(soops, &te->subtree, (void *)seq->strip, te, TSE_SEQ_STRIP, index);
 		}
 	}
 	else if (type == TSE_SEQ_STRIP) {
@@ -1206,7 +1162,7 @@ static TreeElement *outliner_add_element(
 				for (a = 0; a < tot; a++) {
 					RNA_property_collection_lookup_int(ptr, iterprop, a, &propptr);
 					if (!(RNA_property_flag(propptr.data) & PROP_HIDDEN)) {
-						outliner_add_element(soops, eval_ctx, &te->subtree, (void *)ptr, te, TSE_RNA_PROPERTY, a);
+						outliner_add_element(soops, &te->subtree, (void *)ptr, te, TSE_RNA_PROPERTY, a);
 					}
 				}
 			}
@@ -1235,7 +1191,7 @@ static TreeElement *outliner_add_element(
 
 				if (pptr.data) {
 					if (TSELEM_OPEN(tselem, soops))
-						outliner_add_element(soops, eval_ctx, &te->subtree, (void *)&pptr, te, TSE_RNA_STRUCT, -1);
+						outliner_add_element(soops, &te->subtree, (void *)&pptr, te, TSE_RNA_STRUCT, -1);
 					else
 						te->flag |= TE_LAZY_CLOSED;
 				}
@@ -1247,7 +1203,7 @@ static TreeElement *outliner_add_element(
 				if (TSELEM_OPEN(tselem, soops)) {
 					for (a = 0; a < tot; a++) {
 						RNA_property_collection_lookup_int(ptr, prop, a, &pptr);
-						outliner_add_element(soops, eval_ctx, &te->subtree, (void *)&pptr, te, TSE_RNA_STRUCT, a);
+						outliner_add_element(soops, &te->subtree, (void *)&pptr, te, TSE_RNA_STRUCT, a);
 					}
 				}
 				else if (tot)
@@ -1259,7 +1215,7 @@ static TreeElement *outliner_add_element(
 
 				if (TSELEM_OPEN(tselem, soops)) {
 					for (a = 0; a < tot; a++)
-						outliner_add_element(soops, eval_ctx, &te->subtree, (void *)ptr, te, TSE_RNA_ARRAY_ELEM, a);
+						outliner_add_element(soops, &te->subtree, (void *)ptr, te, TSE_RNA_ARRAY_ELEM, a);
 				}
 				else if (tot)
 					te->flag |= TE_LAZY_CLOSED;
@@ -1307,8 +1263,7 @@ static TreeElement *outliner_add_element(
 					}
 					
 					if (ot || kmi->propvalue) {
-						TreeElement *ten = outliner_add_element(
-						        soops, eval_ctx, &te->subtree, kmi, te, TSE_KEYMAP_ITEM, a);
+						TreeElement *ten = outliner_add_element(soops, &te->subtree, kmi, te, TSE_KEYMAP_ITEM, a);
 						
 						ten->directdata = kmi;
 						
@@ -1330,8 +1285,7 @@ static TreeElement *outliner_add_element(
 
 	if ((type != TSE_LAYER_COLLECTION) && (te->idcode == ID_GR)) {
 		Group *group = (Group *)id;
-		outliner_add_layer_collections_recursive(
-		        soops, eval_ctx, &te->subtree, id, &group->view_layer->layer_collections, NULL, true);
+		outliner_add_layer_collections_recursive(soops, &te->subtree, id, &group->view_layer->layer_collections, NULL, true);
 	}
 
 	return te;
@@ -1387,9 +1341,7 @@ static int need_add_seq_dup(Sequence *seq)
 	return(1);
 }
 
-static void outliner_add_seq_dup(
-        SpaceOops *soops, const EvaluationContext *eval_ctx,
-        Sequence *seq, TreeElement *te, short index)
+static void outliner_add_seq_dup(SpaceOops *soops, Sequence *seq, TreeElement *te, short index)
 {
 	/* TreeElement *ch; */ /* UNUSED */
 	Sequence *p;
@@ -1402,8 +1354,7 @@ static void outliner_add_seq_dup(
 		}
 
 		if (STREQ(p->strip->stripdata->name, seq->strip->stripdata->name))
-			/* ch = */ /* UNUSED */ outliner_add_element(
-			        soops, eval_ctx, &te->subtree, (void *)p, te, TSE_SEQUENCE, index);
+			/* ch = */ /* UNUSED */ outliner_add_element(soops, &te->subtree, (void *)p, te, TSE_SEQUENCE, index);
 		p = p->next;
 	}
 }
@@ -1412,9 +1363,7 @@ static void outliner_add_seq_dup(
 /* ----------------------------------------------- */
 
 
-static void outliner_add_library_contents(
-        Main *mainvar, SpaceOops *soops, const EvaluationContext *eval_ctx,
-        TreeElement *te, Library *lib)
+static void outliner_add_library_contents(Main *mainvar, SpaceOops *soops, TreeElement *te, Library *lib)
 {
 	TreeElement *ten;
 	ListBase *lbarray[MAX_LIBARRAY];
@@ -1431,7 +1380,7 @@ static void outliner_add_library_contents(
 					break;
 			
 			if (id) {
-				ten = outliner_add_element(soops, eval_ctx, &te->subtree, lbarray[a], NULL, TSE_ID_BASE, 0);
+				ten = outliner_add_element(soops, &te->subtree, lbarray[a], NULL, TSE_ID_BASE, 0);
 				ten->directdata = lbarray[a];
 				
 				ten->name = BKE_idcode_to_name_plural(GS(id->name));
@@ -1440,7 +1389,7 @@ static void outliner_add_library_contents(
 				
 				for (id = lbarray[a]->first; id; id = id->next) {
 					if (id->lib == lib)
-						outliner_add_element(soops, eval_ctx, &ten->subtree, id, ten, 0, 0);
+						outliner_add_element(soops, &ten->subtree, id, ten, 0, 0);
 				}
 			}
 		}
@@ -1448,7 +1397,7 @@ static void outliner_add_library_contents(
 	
 }
 
-static void outliner_add_orphaned_datablocks(Main *mainvar, SpaceOops *soops, const EvaluationContext *eval_ctx)
+static void outliner_add_orphaned_datablocks(Main *mainvar, SpaceOops *soops)
 {
 	TreeElement *ten;
 	ListBase *lbarray[MAX_LIBARRAY];
@@ -1471,7 +1420,7 @@ static void outliner_add_orphaned_datablocks(Main *mainvar, SpaceOops *soops, co
 				 *   - Add a parameter to BKE_idcode_to_name_plural to get a sane "user-visible" name instead?
 				 *   - Ensure that this uses nice icons for the datablock type involved instead of the dot?
 				 */
-				ten = outliner_add_element(soops, eval_ctx, &soops->tree, lbarray[a], NULL, TSE_ID_BASE, 0);
+				ten = outliner_add_element(soops, &soops->tree, lbarray[a], NULL, TSE_ID_BASE, 0);
 				ten->directdata = lbarray[a];
 				
 				ten->name = BKE_idcode_to_name_plural(GS(id->name));
@@ -1481,7 +1430,7 @@ static void outliner_add_orphaned_datablocks(Main *mainvar, SpaceOops *soops, co
 				/* add the orphaned datablocks - these will not be added with any subtrees attached */
 				for (id = lbarray[a]->first; id; id = id->next) {
 					if (ID_REAL_USERS(id) <= 0)
-						outliner_add_element(soops, eval_ctx, &ten->subtree, id, ten, 0, 0);
+						outliner_add_element(soops, &ten->subtree, id, ten, 0, 0);
 				}
 			}
 		}
@@ -1527,24 +1476,22 @@ static bool outliner_layer_collections_reorder_poll(
 }
 
 static void outliner_add_layer_collections_recursive(
-        SpaceOops *soops, const EvaluationContext *eval_ctx,
-        ListBase *tree, ID *id, ListBase *layer_collections, TreeElement *parent_ten,
+        SpaceOops *soops, ListBase *tree, ID *id, ListBase *layer_collections, TreeElement *parent_ten,
         const bool show_objects)
 {
 	for (LayerCollection *collection = layer_collections->first; collection; collection = collection->next) {
-		TreeElement *ten = outliner_add_element(soops, eval_ctx, tree, id, parent_ten, TSE_LAYER_COLLECTION, 0);
+		TreeElement *ten = outliner_add_element(soops, tree, id, parent_ten, TSE_LAYER_COLLECTION, 0);
 
 		ten->name = collection->scene_collection->name;
 		ten->directdata = collection;
 		ten->reinsert = outliner_layer_collections_reorder;
 		ten->reinsert_poll = outliner_layer_collections_reorder_poll;
 
-		outliner_add_layer_collections_recursive(
-		        soops, eval_ctx, &ten->subtree, id, &collection->layer_collections, ten, show_objects);
+		outliner_add_layer_collections_recursive(soops, &ten->subtree, id, &collection->layer_collections, ten, show_objects);
 		if (show_objects) {
 			for (LinkData *link = collection->object_bases.first; link; link = link->next) {
 				Base *base = (Base *)link->data;
-				TreeElement *te_object = outliner_add_element(soops, eval_ctx, &ten->subtree, base->object, ten, 0, 0);
+				TreeElement *te_object = outliner_add_element(soops, &ten->subtree, base->object, ten, 0, 0);
 				te_object->directdata = base;
 			}
 		}
@@ -1552,13 +1499,10 @@ static void outliner_add_layer_collections_recursive(
 	}
 }
 
-static void outliner_add_view_layer(
-        SpaceOops *soops, const EvaluationContext *eval_ctx,
-        ListBase *tree, TreeElement *parent,
-        Scene *scene, ViewLayer *layer, const bool show_objects)
+static void outliner_add_view_layer(SpaceOops *soops, ListBase *tree, TreeElement *parent,
+                                    Scene *scene, ViewLayer *layer, const bool show_objects)
 {
-	outliner_add_layer_collections_recursive(
-	        soops, eval_ctx, tree, &scene->id, &layer->layer_collections, parent, show_objects);
+	outliner_add_layer_collections_recursive(soops, tree, &scene->id, &layer->layer_collections, parent, show_objects);
 }
 
 static void outliner_scene_collections_reorder(
@@ -1629,40 +1573,35 @@ BLI_INLINE void outliner_add_scene_collection_init(TreeElement *te, SceneCollect
 }
 
 BLI_INLINE void outliner_add_scene_collection_objects(
-        SpaceOops *soops, const EvaluationContext *eval_ctx,
-        ListBase *tree, SceneCollection *collection, TreeElement *parent)
+        SpaceOops *soops, ListBase *tree, SceneCollection *collection, TreeElement *parent)
 {
 	for (LinkData *link = collection->objects.first; link; link = link->next) {
-		outliner_add_element(soops, eval_ctx, tree, link->data, parent, 0, 0);
+		outliner_add_element(soops, tree, link->data, parent, 0, 0);
 	}
 }
 
 static TreeElement *outliner_add_scene_collection_recursive(
-        SpaceOops *soops, const EvaluationContext *eval_ctx,
-        ListBase *tree, ID *id, SceneCollection *scene_collection, TreeElement *parent_ten)
+        SpaceOops *soops, ListBase *tree, ID *id, SceneCollection *scene_collection, TreeElement *parent_ten)
 {
-	TreeElement *ten = outliner_add_element(soops, eval_ctx, tree, id, parent_ten, TSE_SCENE_COLLECTION, 0);
+	TreeElement *ten = outliner_add_element(soops, tree, id, parent_ten, TSE_SCENE_COLLECTION, 0);
 	outliner_add_scene_collection_init(ten, scene_collection);
-	outliner_add_scene_collection_objects(soops, eval_ctx, &ten->subtree, scene_collection, ten);
+	outliner_add_scene_collection_objects(soops, &ten->subtree, scene_collection, ten);
 
 	for (SceneCollection *scene_collection_nested = scene_collection->scene_collections.first;
 	     scene_collection_nested != NULL;
 	     scene_collection_nested = scene_collection_nested->next)
 	{
-		outliner_add_scene_collection_recursive(
-		        soops, eval_ctx, &ten->subtree, id, scene_collection_nested, ten);
+		outliner_add_scene_collection_recursive(soops, &ten->subtree, id, scene_collection_nested, ten);
 	}
 
 	outliner_make_hierarchy(&ten->subtree);
 	return ten;
 }
 
-static void outliner_add_collections(
-        SpaceOops *soops, const EvaluationContext *eval_ctx, Scene *scene)
+static void outliner_add_collections(SpaceOops *soops, Scene *scene)
 {
 	SceneCollection *master_collection = BKE_collection_master(&scene->id);
-	TreeElement *ten = outliner_add_scene_collection_recursive(
-	        soops, eval_ctx, &soops->tree, &scene->id, master_collection, NULL);
+	TreeElement *ten = outliner_add_scene_collection_recursive(soops, &soops->tree, &scene->id, master_collection, NULL);
 	/* Master Collection should always be expanded. */
 	TREESTORE(ten)->flag &= ~TSE_CLOSED;
 }
@@ -2227,9 +2166,7 @@ static void outliner_filter_tree(SpaceOops *soops, ViewLayer *view_layer)
 
 /* Main entry point for building the tree data-structure that the outliner represents */
 // TODO: split each mode into its own function?
-void outliner_build_tree(
-        Main *mainvar, const EvaluationContext *eval_ctx, Scene *scene,
-        ViewLayer *view_layer, SpaceOops *soops, ARegion *ar)
+void outliner_build_tree(Main *mainvar, Scene *scene, ViewLayer *view_layer, SpaceOops *soops, ARegion *ar)
 {
 	TreeElement *te = NULL, *ten;
 	TreeStoreElem *tselem;
@@ -2262,20 +2199,20 @@ void outliner_build_tree(
 		Library *lib;
 		
 		/* current file first - mainvar provides tselem with unique pointer - not used */
-		ten = outliner_add_element(soops, eval_ctx, &soops->tree, mainvar, NULL, TSE_ID_BASE, 0);
+		ten = outliner_add_element(soops, &soops->tree, mainvar, NULL, TSE_ID_BASE, 0);
 		ten->name = IFACE_("Current File");
 
 		tselem = TREESTORE(ten);
 		if (!tselem->used)
 			tselem->flag &= ~TSE_CLOSED;
 		
-		outliner_add_library_contents(mainvar, soops, eval_ctx, ten, NULL);
+		outliner_add_library_contents(mainvar, soops, ten, NULL);
 		
 		for (lib = mainvar->library.first; lib; lib = lib->id.next) {
-			ten = outliner_add_element(soops, eval_ctx, &soops->tree, lib, NULL, 0, 0);
+			ten = outliner_add_element(soops, &soops->tree, lib, NULL, 0, 0);
 			lib->id.newid = (ID *)ten;
 			
-			outliner_add_library_contents(mainvar, soops, eval_ctx, ten, lib);
+			outliner_add_library_contents(mainvar, soops, ten, lib);
 
 		}
 		/* make hierarchy */
@@ -2295,8 +2232,8 @@ void outliner_build_tree(
 				}
 				else {
 					/* Else, make a new copy of the libtree for our parent. */
-					TreeElement *dupten = outliner_add_element(soops, eval_ctx, &par->subtree, lib, NULL, 0, 0);
-					outliner_add_library_contents(mainvar, soops, eval_ctx, dupten, lib);
+					TreeElement *dupten = outliner_add_element(soops, &par->subtree, lib, NULL, 0, 0);
+					outliner_add_library_contents(mainvar, soops, dupten, lib);
 					dupten->parent = par;
 				}
 			}
@@ -2310,7 +2247,7 @@ void outliner_build_tree(
 	else if (soops->outlinevis == SO_SCENES) {
 		Scene *sce;
 		for (sce = mainvar->scene.first; sce; sce = sce->id.next) {
-			te = outliner_add_element(soops, eval_ctx, &soops->tree, sce, NULL, 0, 0);
+			te = outliner_add_element(soops, &soops->tree, sce, NULL, 0, 0);
 			tselem = TREESTORE(te);
 
 			if (sce == scene && show_opened) {
@@ -2323,7 +2260,7 @@ void outliner_build_tree(
 	else if (soops->outlinevis == SO_GROUPS) {
 		Group *group;
 		for (group = mainvar->group.first; group; group = group->id.next) {
-			te = outliner_add_element(soops, eval_ctx, &soops->tree, group, NULL, 0, 0);
+			te = outliner_add_element(soops, &soops->tree, group, NULL, 0, 0);
 			outliner_make_hierarchy(&te->subtree);
 		}
 	}
@@ -2342,11 +2279,11 @@ void outliner_build_tree(
 		while (seq) {
 			op = need_add_seq_dup(seq);
 			if (op == 1) {
-				/* ten = */ outliner_add_element(soops, eval_ctx, &soops->tree, (void *)seq, NULL, TSE_SEQUENCE, 0);
+				/* ten = */ outliner_add_element(soops, &soops->tree, (void *)seq, NULL, TSE_SEQUENCE, 0);
 			}
 			else if (op == 0) {
-				ten = outliner_add_element(soops, eval_ctx, &soops->tree, (void *)seq, NULL, TSE_SEQUENCE_DUP, 0);
-				outliner_add_seq_dup(soops, eval_ctx, seq, ten, 0);
+				ten = outliner_add_element(soops, &soops->tree, (void *)seq, NULL, TSE_SEQUENCE_DUP, 0);
+				outliner_add_seq_dup(soops, seq, ten, 0);
 			}
 			seq = seq->next;
 		}
@@ -2356,7 +2293,7 @@ void outliner_build_tree(
 
 		RNA_main_pointer_create(mainvar, &mainptr);
 
-		ten = outliner_add_element(soops, eval_ctx, &soops->tree, (void *)&mainptr, NULL, TSE_RNA_STRUCT, -1);
+		ten = outliner_add_element(soops, &soops->tree, (void *)&mainptr, NULL, TSE_RNA_STRUCT, -1);
 
 		if (show_opened) {
 			tselem = TREESTORE(ten);
@@ -2364,36 +2301,36 @@ void outliner_build_tree(
 		}
 	}
 	else if (soops->outlinevis == SO_ID_ORPHANS) {
-		outliner_add_orphaned_datablocks(mainvar, soops, eval_ctx);
+		outliner_add_orphaned_datablocks(mainvar, soops);
 	}
 	else if (soops->outlinevis == SO_VIEW_LAYER) {
 		if ((soops->filter & SO_FILTER_ENABLE) && (soops->filter & SO_FILTER_NO_COLLECTION)) {
 			for (Base *base = view_layer->object_bases.first; base; base = base->next) {
-				TreeElement *te_object = outliner_add_element(soops, eval_ctx, &soops->tree, base->object, NULL, 0, 0);
+				TreeElement *te_object = outliner_add_element(soops, &soops->tree, base->object, NULL, 0, 0);
 				te_object->directdata = base;
 			}
 			outliner_make_hierarchy(&soops->tree);
 		}
 		else {
-			outliner_add_view_layer(soops, eval_ctx, &soops->tree, NULL, scene, view_layer, true);
+			outliner_add_view_layer(soops, &soops->tree, NULL, scene, view_layer, true);
 		}
 	}
 	else if (soops->outlinevis == SO_COLLECTIONS) {
 		if ((soops->filter & SO_FILTER_ENABLE) && (soops->filter & SO_FILTER_NO_COLLECTION)) {
 			FOREACH_SCENE_OBJECT_BEGIN(scene, ob)
 			{
-				outliner_add_element(soops, eval_ctx, &soops->tree, ob, NULL, 0, 0);
+				outliner_add_element(soops, &soops->tree, ob, NULL, 0, 0);
 			}
 			FOREACH_SCENE_OBJECT_END;
 			outliner_make_hierarchy(&soops->tree);
 		}
 		else {
-			outliner_add_collections(soops, eval_ctx, scene);
+			outliner_add_collections(soops, scene);
 		}
 	}
 	else {
 		if (BASACT(view_layer)) {
-			ten = outliner_add_element(soops, eval_ctx, &soops->tree, OBACT(view_layer), NULL, 0, 0);
+			ten = outliner_add_element(soops, &soops->tree, OBACT(view_layer), NULL, 0, 0);
 			ten->directdata = BASACT(view_layer);
 		}
 	}
diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c
index db0593ef2d16bb6d607df1d12993ff98ceb969de..8e5863fd514fde6a88698fb4220e55515abfd59b 100644
--- a/source/blender/editors/space_sequencer/sequencer_draw.c
+++ b/source/blender/editors/space_sequencer/sequencer_draw.c
@@ -58,7 +58,6 @@
 
 #include "BIF_glutil.h"
 
-#include "GPU_compositing.h"
 #include "GPU_immediate.h"
 #include "GPU_immediate_util.h"
 #include "GPU_matrix.h"
@@ -924,12 +923,6 @@ ImBuf *sequencer_ibuf_get(struct Main *bmain, Scene *scene, SpaceSeq *sseq, int
 	        rectx, recty, proxy_size,
 	        &context);
 	context.view_id = BKE_scene_multiview_view_id_get(&scene->r, viewname);
-	if (scene->r.seq_flag & R_SEQ_CAMERA_DOF) {
-		if (sseq->compositor == NULL) {
-			sseq->compositor = GPU_fx_compositor_create();
-		}
-		context.gpu_fx = sseq->compositor;
-	}
 
 	/* sequencer could start rendering, in this case we need to be sure it wouldn't be canceled
 	 * by Esc pressed somewhere in the past
@@ -1316,6 +1309,7 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq
 
 	glGenTextures(1, (GLuint *)&texid);
 
+	glActiveTexture(GL_TEXTURE0);
 	glBindTexture(GL_TEXTURE_2D, texid);
 
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
@@ -1329,7 +1323,7 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq
 	if (!glsl_used) {
 		immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_COLOR);
 		immUniformColor3f(1.0f, 1.0f, 1.0f);
-		immUniform1i("image", GL_TEXTURE0);
+		immUniform1i("image", 0);
 	}
 
 	immBegin(GWN_PRIM_TRI_FAN, 4);
diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c
index 23387f291e6dba900ccc0869c3660be69262ab1e..da16ac5acaa93037b91be6bab11c3caecf29de31 100644
--- a/source/blender/editors/space_sequencer/space_sequencer.c
+++ b/source/blender/editors/space_sequencer/space_sequencer.c
@@ -60,8 +60,6 @@
 
 #include "IMB_imbuf.h"
 
-#include "GPU_compositing.h"
-
 #include "sequencer_intern.h"   // own include
 
 /**************************** common state *****************************/
@@ -220,11 +218,6 @@ static void sequencer_free(SpaceLink *sl)
 
 	if (scopes->histogram_ibuf)
 		IMB_freeImBuf(scopes->histogram_ibuf);
-
-	if (sseq->compositor != NULL) {
-		GPU_fx_compositor_destroy(sseq->compositor);
-		sseq->compositor = NULL;
-	}
 }
 
 
diff --git a/source/blender/editors/space_text/CMakeLists.txt b/source/blender/editors/space_text/CMakeLists.txt
index 39b48f5b52c11a971c24ea59929862821f22cc53..91420a5d63aa5f5fe22388655fbf90572e21e903 100644
--- a/source/blender/editors/space_text/CMakeLists.txt
+++ b/source/blender/editors/space_text/CMakeLists.txt
@@ -48,6 +48,7 @@ set(SRC
 	text_format_py.c
 	text_header.c
 	text_ops.c
+	text_undo.c
 
 	text_format.h
 	text_intern.h
diff --git a/source/blender/editors/space_text/text_autocomplete.c b/source/blender/editors/space_text/text_autocomplete.c
index da5fa9da046fb45dc39c22283ddd31375e2ec0e8..9163831c333b19b38e3be12624d0d95903b86397 100644
--- a/source/blender/editors/space_text/text_autocomplete.c
+++ b/source/blender/editors/space_text/text_autocomplete.c
@@ -241,7 +241,7 @@ static void get_suggest_prefix(Text *text, int offset)
 	texttool_suggest_prefix(line + i, len);
 }
 
-static void confirm_suggestion(Text *text)
+static void confirm_suggestion(Text *text, TextUndoBuf *utxt)
 {
 	SuggItem *sel;
 	int i, over = 0;
@@ -260,7 +260,7 @@ static void confirm_suggestion(Text *text)
 //	for (i = 0; i < skipleft; i++)
 //		txt_move_left(text, 0);
 	BLI_assert(memcmp(sel->name, &line[i], over) == 0);
-	txt_insert_buf(text, sel->name + over);
+	txt_insert_buf(text, utxt, sel->name + over);
 
 //	for (i = 0; i < skipleft; i++)
 //		txt_move_right(text, 0);
@@ -284,7 +284,8 @@ static int text_autocomplete_invoke(bContext *C, wmOperator *op, const wmEvent *
 		ED_area_tag_redraw(CTX_wm_area(C));
 
 		if (texttool_suggest_first() == texttool_suggest_last()) {
-			confirm_suggestion(st->text);
+			TextUndoBuf *utxt = NULL; // FIXME
+			confirm_suggestion(st->text, utxt);
 			text_update_line_edited(st->text->curl);
 			text_autocomplete_free(C, op);
 			return OPERATOR_FINISHED;
@@ -314,6 +315,8 @@ static int text_autocomplete_modal(bContext *C, wmOperator *op, const wmEvent *e
 
 	(void)text;
 
+	TextUndoBuf *utxt = NULL; // FIXME
+
 	if (st->doplugins && texttool_text_is_active(st->text)) {
 		if (texttool_suggest_first()) tools |= TOOL_SUGG_LIST;
 		if (texttool_docs_get()) tools |= TOOL_DOCUMENT;
@@ -340,7 +343,7 @@ static int text_autocomplete_modal(bContext *C, wmOperator *op, const wmEvent *e
 		case MIDDLEMOUSE:
 			if (event->val == KM_PRESS) {
 				if (text_do_suggest_select(st, ar)) {
-					confirm_suggestion(st->text);
+					confirm_suggestion(st->text, utxt);
 					text_update_line_edited(st->text->curl);
 					swallow = 1;
 				}
@@ -375,7 +378,7 @@ static int text_autocomplete_modal(bContext *C, wmOperator *op, const wmEvent *e
 		case PADENTER:
 			if (event->val == KM_PRESS) {
 				if (tools & TOOL_SUGG_LIST) {
-					confirm_suggestion(st->text);
+					confirm_suggestion(st->text, utxt);
 					text_update_line_edited(st->text->curl);
 					swallow = 1;
 					draw = 1;
diff --git a/source/blender/editors/space_text/text_format_pov.c b/source/blender/editors/space_text/text_format_pov.c
index 1ef3322711c96511e2dee6ca972924ff17ca9b51..4c9abecedd69c0fbddf4e3fd5cffce2d276acdad 100644
--- a/source/blender/editors/space_text/text_format_pov.c
+++ b/source/blender/editors/space_text/text_format_pov.c
@@ -486,7 +486,7 @@ static int txtfmt_pov_find_specialvar(const char *string)
 {
 	int i, len;
 	/* Modifiers */
-	if 		(STR_LITERAL_STARTSWITH(string, "dispersion_samples", len)) i = len;
+	if      (STR_LITERAL_STARTSWITH(string, "dispersion_samples", len)) i = len;
 	else if (STR_LITERAL_STARTSWITH(string, "projected_through",  len)) i = len;
 	else if (STR_LITERAL_STARTSWITH(string, "double_illuminate",  len)) i = len;
 	else if (STR_LITERAL_STARTSWITH(string, "expand_thresholds",  len)) i = len;
@@ -498,7 +498,7 @@ static int txtfmt_pov_find_specialvar(const char *string)
 	else if (STR_LITERAL_STARTSWITH(string, "max_trace_level",    len)) i = len;
 	else if (STR_LITERAL_STARTSWITH(string, "gray_threshold",     len)) i = len;
 	else if (STR_LITERAL_STARTSWITH(string, "pretrace_start",     len)) i = len;
-	else if	(STR_LITERAL_STARTSWITH(string, "normal_indices",     len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "normal_indices",     len)) i = len;
 	else if (STR_LITERAL_STARTSWITH(string, "normal_vectors",     len)) i = len;
 	else if (STR_LITERAL_STARTSWITH(string, "vertex_vectors",     len)) i = len;
 	else if (STR_LITERAL_STARTSWITH(string, "noise_generator",    len)) i = len;
@@ -577,7 +577,7 @@ static int txtfmt_pov_find_specialvar(const char *string)
 	else if (STR_LITERAL_STARTSWITH(string, "autostop",           len)) i = len;
 	else if (STR_LITERAL_STARTSWITH(string, "caustics",           len)) i = len;
 	else if (STR_LITERAL_STARTSWITH(string, "octaves",            len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "aa_level", 		  len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "aa_level",           len)) i = len;
 	else if (STR_LITERAL_STARTSWITH(string, "frequency",          len)) i = len;
 	else if (STR_LITERAL_STARTSWITH(string, "fog_offset",         len)) i = len;
 	else if (STR_LITERAL_STARTSWITH(string, "modulation",         len)) i = len;
@@ -642,45 +642,45 @@ static int txtfmt_pov_find_specialvar(const char *string)
 	else if (STR_LITERAL_STARTSWITH(string, "radius",             len)) i = len;
 	/* Camera Types and options*/
 	else if (STR_LITERAL_STARTSWITH(string, "omni_directional_stereo",  len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "lambert_cylindrical",		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "miller_cylindrical", 		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "lambert_azimuthal",  		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "ultra_wide_angle",   		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "camera_direction",   		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "camera_location ",   		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "van_der_grinten",    		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "aitoff_hammer",   	  		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "smyth_craster",   	  		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "orthographic",       		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "camera_right",       		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "blur_samples",       		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "plate_carree",       		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "camera_type",        		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "perspective",        		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "mesh_camera",        		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "focal_point",        		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "balthasart",         		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "confidence",         		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "parallaxe",          		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "hobo_dyer",          		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "camera_up",          		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "panoramic",          		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "eckert_vi",   	  	  		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "eckert_iv",   	  	  		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "mollweide",   	  	  		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "aperture",           		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "behrmann",           		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "variance",           		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "stereo",             		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "icosa",           	  		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "tetra",           	  		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "octa",           	  		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "mercator",   		  		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "omnimax",            		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "fisheye",            		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "edwards",            		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "peters",             		len)) i = len;
-	else if (STR_LITERAL_STARTSWITH(string, "gall",            	  		len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "lambert_cylindrical",      len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "miller_cylindrical",       len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "lambert_azimuthal",        len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "ultra_wide_angle",         len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "camera_direction",         len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "camera_location ",         len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "van_der_grinten",          len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "aitoff_hammer",            len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "smyth_craster",            len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "orthographic",             len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "camera_right",             len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "blur_samples",             len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "plate_carree",             len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "camera_type",              len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "perspective",              len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "mesh_camera",              len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "focal_point",              len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "balthasart",               len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "confidence",               len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "parallaxe",                len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "hobo_dyer",                len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "camera_up",                len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "panoramic",                len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "eckert_vi",                len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "eckert_iv",                len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "mollweide",                len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "aperture",                 len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "behrmann",                 len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "variance",                 len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "stereo",                   len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "icosa",                    len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "tetra",                    len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "octa",                     len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "mercator",                 len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "omnimax",                  len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "fisheye",                  len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "edwards",                  len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "peters",                   len)) i = len;
+	else if (STR_LITERAL_STARTSWITH(string, "gall",                     len)) i = len;
 	else                                                                i = 0;
 
 	/* If next source char is an identifier (eg. 'i' in "definate") no match */
diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c
index 772cd6bd41986bf6c1d0276ce4e7998c0903cab7..4f332c09ce29074813e454ad6feff3ad35b9668e 100644
--- a/source/blender/editors/space_text/text_ops.c
+++ b/source/blender/editors/space_text/text_ops.c
@@ -731,7 +731,8 @@ static int text_paste_exec(bContext *C, wmOperator *op)
 
 	text_drawcache_tag_update(CTX_wm_space_text(C), 0);
 
-	txt_insert_buf(text, buf);
+	TextUndoBuf *utxt = ED_text_undo_push_init(C);
+	txt_insert_buf(text, utxt, buf);
 	text_update_edited(text);
 
 	MEM_freeN(buf);
@@ -756,7 +757,10 @@ void TEXT_OT_paste(wmOperatorType *ot)
 	/* api callbacks */
 	ot->exec = text_paste_exec;
 	ot->poll = text_edit_poll;
-	
+
+	/* flags */
+	ot->flag = OPTYPE_UNDO;
+
 	/* properties */
 	RNA_def_boolean(ot->srna, "selection", 0, "Selection", "Paste text selected elsewhere rather than copied (X11 only)");
 }
@@ -766,9 +770,11 @@ void TEXT_OT_paste(wmOperatorType *ot)
 static int text_duplicate_line_exec(bContext *C, wmOperator *UNUSED(op))
 {
 	Text *text = CTX_data_edit_text(C);
-	
-	txt_duplicate_line(text);
-	
+
+	TextUndoBuf *utxt = ED_text_undo_push_init(C);
+
+	txt_duplicate_line(text, utxt);
+
 	WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
 
 	/* run the script while editing, evil but useful */
@@ -785,10 +791,13 @@ void TEXT_OT_duplicate_line(wmOperatorType *ot)
 	ot->name = "Duplicate Line";
 	ot->idname = "TEXT_OT_duplicate_line";
 	ot->description = "Duplicate the current line";
-	
+
 	/* api callbacks */
 	ot->exec = text_duplicate_line_exec;
 	ot->poll = text_edit_poll;
+
+	/* flags */
+	ot->flag = OPTYPE_UNDO;
 }
 
 /******************* copy operator *********************/
@@ -838,7 +847,9 @@ static int text_cut_exec(bContext *C, wmOperator *UNUSED(op))
 	text_drawcache_tag_update(CTX_wm_space_text(C), 0);
 
 	txt_copy_clipboard(text);
-	txt_delete_selected(text);
+
+	TextUndoBuf *utxt = ED_text_undo_push_init(C);
+	txt_delete_selected(text, utxt);
 
 	text_update_cursor_moved(C);
 	WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
@@ -860,6 +871,9 @@ void TEXT_OT_cut(wmOperatorType *ot)
 	/* api callbacks */
 	ot->exec = text_cut_exec;
 	ot->poll = text_edit_poll;
+
+	/* flags */
+	ot->flag = OPTYPE_UNDO;
 }
 
 /******************* indent operator *********************/
@@ -870,12 +884,15 @@ static int text_indent_exec(bContext *C, wmOperator *UNUSED(op))
 
 	text_drawcache_tag_update(CTX_wm_space_text(C), 0);
 
+	TextUndoBuf *utxt = ED_text_undo_push_init(C);
+
 	if (txt_has_sel(text)) {
 		txt_order_cursors(text, false);
-		txt_indent(text);
+		txt_indent(text, utxt);
+	}
+	else {
+		txt_add_char(text, utxt, '\t');
 	}
-	else
-		txt_add_char(text, '\t');
 
 	text_update_edited(text);
 
@@ -895,6 +912,9 @@ void TEXT_OT_indent(wmOperatorType *ot)
 	/* api callbacks */
 	ot->exec = text_indent_exec;
 	ot->poll = text_edit_poll;
+
+	/* flags */
+	ot->flag = OPTYPE_UNDO;
 }
 
 /******************* unindent operator *********************/
@@ -905,8 +925,10 @@ static int text_unindent_exec(bContext *C, wmOperator *UNUSED(op))
 
 	text_drawcache_tag_update(CTX_wm_space_text(C), 0);
 
+	TextUndoBuf *utxt = ED_text_undo_push_init(C);
+
 	txt_order_cursors(text, false);
-	txt_unindent(text);
+	txt_unindent(text, utxt);
 
 	text_update_edited(text);
 
@@ -926,6 +948,9 @@ void TEXT_OT_unindent(wmOperatorType *ot)
 	/* api callbacks */
 	ot->exec = text_unindent_exec;
 	ot->poll = text_edit_poll;
+
+	/* flags */
+	ot->flag = OPTYPE_UNDO;
 }
 
 /******************* line break operator *********************/
@@ -941,14 +966,15 @@ static int text_line_break_exec(bContext *C, wmOperator *UNUSED(op))
 
 	// double check tabs/spaces before splitting the line
 	curts = txt_setcurr_tab_spaces(text, space);
-	txt_split_curline(text);
+	TextUndoBuf *utxt = ED_text_undo_push_init(C);
+	txt_split_curline(text, utxt);
 
 	for (a = 0; a < curts; a++) {
 		if (text->flags & TXT_TABSTOSPACES) {
-			txt_add_char(text, ' ');
+			txt_add_char(text, utxt, ' ');
 		}
 		else {
-			txt_add_char(text, '\t');
+			txt_add_char(text, utxt, '\t');
 		}
 	}
 
@@ -961,7 +987,7 @@ static int text_line_break_exec(bContext *C, wmOperator *UNUSED(op))
 	text_update_cursor_moved(C);
 	WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
 
-	return OPERATOR_CANCELLED;
+	return OPERATOR_FINISHED;
 }
 
 void TEXT_OT_line_break(wmOperatorType *ot)
@@ -970,10 +996,13 @@ void TEXT_OT_line_break(wmOperatorType *ot)
 	ot->name = "Line Break";
 	ot->idname = "TEXT_OT_line_break";
 	ot->description = "Insert line break at cursor position";
-	
+
 	/* api callbacks */
 	ot->exec = text_line_break_exec;
 	ot->poll = text_edit_poll;
+
+	/* flags */
+	ot->flag = OPTYPE_UNDO;
 }
 
 /******************* comment operator *********************/
@@ -985,8 +1014,10 @@ static int text_comment_exec(bContext *C, wmOperator *UNUSED(op))
 	if (txt_has_sel(text)) {
 		text_drawcache_tag_update(CTX_wm_space_text(C), 0);
 
+		TextUndoBuf *utxt = ED_text_undo_push_init(C);
+
 		txt_order_cursors(text, false);
-		txt_comment(text);
+		txt_comment(text, utxt);
 		text_update_edited(text);
 
 		text_update_cursor_moved(C);
@@ -1003,10 +1034,13 @@ void TEXT_OT_comment(wmOperatorType *ot)
 	ot->name = "Comment";
 	ot->idname = "TEXT_OT_comment";
 	ot->description = "Convert selected text to comment";
-	
+
 	/* api callbacks */
 	ot->exec = text_comment_exec;
 	ot->poll = text_edit_poll;
+
+	/* flags */
+	ot->flag = OPTYPE_UNDO;
 }
 
 /******************* uncomment operator *********************/
@@ -1018,8 +1052,10 @@ static int text_uncomment_exec(bContext *C, wmOperator *UNUSED(op))
 	if (txt_has_sel(text)) {
 		text_drawcache_tag_update(CTX_wm_space_text(C), 0);
 
+		TextUndoBuf *utxt = ED_text_undo_push_init(C);
+
 		txt_order_cursors(text, false);
-		txt_uncomment(text);
+		txt_uncomment(text, utxt);
 		text_update_edited(text);
 
 		text_update_cursor_moved(C);
@@ -1041,6 +1077,9 @@ void TEXT_OT_uncomment(wmOperatorType *ot)
 	/* api callbacks */
 	ot->exec = text_uncomment_exec;
 	ot->poll = text_edit_poll;
+
+	/* flags */
+	ot->flag = OPTYPE_UNDO;
 }
 
 /******************* convert whitespace operator *********************/
@@ -1174,6 +1213,9 @@ void TEXT_OT_convert_whitespace(wmOperatorType *ot)
 	ot->exec = text_convert_whitespace_exec;
 	ot->poll = text_edit_poll;
 
+	/* flags */
+	ot->flag = OPTYPE_UNDO;
+
 	/* properties */
 	RNA_def_enum(ot->srna, "type", whitespace_type_items, TO_SPACES, "Type", "Type of whitespace to convert to");
 }
@@ -1265,8 +1307,10 @@ static int move_lines_exec(bContext *C, wmOperator *op)
 {
 	Text *text = CTX_data_edit_text(C);
 	const int direction = RNA_enum_get(op->ptr, "direction");
-	
-	txt_move_lines(text, direction);
+
+	TextUndoBuf *utxt = ED_text_undo_push_init(C);
+
+	txt_move_lines(text, utxt, direction);
 	
 	text_update_cursor_moved(C);
 	WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
@@ -1295,6 +1339,9 @@ void TEXT_OT_move_lines(wmOperatorType *ot)
 	ot->exec = move_lines_exec;
 	ot->poll = text_edit_poll;
 
+	/* flags */
+	ot->flag = OPTYPE_UNDO;
+
 	/* properties */
 	RNA_def_enum(ot->srna, "direction", direction_items, 1, "Direction", "");
 }
@@ -1936,6 +1983,7 @@ static int text_delete_exec(bContext *C, wmOperator *op)
 	Text *text = CTX_data_edit_text(C);
 	int type = RNA_enum_get(op->ptr, "type");
 
+
 	text_drawcache_tag_update(st, 0);
 
 	/* behavior could be changed here,
@@ -1945,11 +1993,13 @@ static int text_delete_exec(bContext *C, wmOperator *op)
 		else if (type == DEL_NEXT_WORD) type = DEL_NEXT_CHAR;
 	}
 
+	TextUndoBuf *utxt = ED_text_undo_push_init(C);
+
 	if (type == DEL_PREV_WORD) {
 		if (txt_cursor_is_line_start(text)) {
-			txt_backspace_char(text);
+			txt_backspace_char(text, utxt);
 		}
-		txt_backspace_word(text);
+		txt_backspace_word(text, utxt);
 	}
 	else if (type == DEL_PREV_CHAR) {
 
@@ -1965,13 +2015,13 @@ static int text_delete_exec(bContext *C, wmOperator *op)
 			}
 		}
 
-		txt_backspace_char(text);
+		txt_backspace_char(text, utxt);
 	}
 	else if (type == DEL_NEXT_WORD) {
 		if (txt_cursor_is_line_end(text)) {
-			txt_delete_char(text);
+			txt_delete_char(text, utxt);
 		}
-		txt_delete_word(text);
+		txt_delete_word(text, utxt);
 	}
 	else if (type == DEL_NEXT_CHAR) {
 
@@ -1987,7 +2037,7 @@ static int text_delete_exec(bContext *C, wmOperator *op)
 			}
 		}
 
-		txt_delete_char(text);
+		txt_delete_char(text, utxt);
 	}
 
 	text_update_line_edited(text->curl);
@@ -2840,16 +2890,18 @@ static int text_insert_exec(bContext *C, wmOperator *op)
 
 	str = RNA_string_get_alloc(op->ptr, "text", NULL, 0);
 
+	TextUndoBuf *utxt = ED_text_undo_push_init(C);
+
 	if (st && st->overwrite) {
 		while (str[i]) {
 			code = BLI_str_utf8_as_unicode_step(str, &i);
-			done |= txt_replace_char(text, code);
+			done |= txt_replace_char(text, utxt, code);
 		}
 	}
 	else {
 		while (str[i]) {
 			code = BLI_str_utf8_as_unicode_step(str, &i);
-			done |= txt_add_char(text, code);
+			done |= txt_add_char(text, utxt, code);
 		}
 	}
 
@@ -2919,6 +2971,9 @@ void TEXT_OT_insert(wmOperatorType *ot)
 	ot->invoke = text_insert_invoke;
 	ot->poll = text_edit_poll;
 
+	/* flags */
+	ot->flag = OPTYPE_UNDO;
+
 	/* properties */
 	prop = RNA_def_string(ot->srna, "text", NULL, 0, "Text", "Text to insert at the cursor position");
 	RNA_def_property_flag(prop, PROP_SKIP_SAVE);
@@ -2955,7 +3010,8 @@ static int text_find_and_replace(bContext *C, wmOperator *op, short mode)
 
 		if (found) {
 			if (mode == TEXT_REPLACE) {
-				txt_insert_buf(text, st->replacestr);
+				TextUndoBuf *utxt = ED_text_undo_push_init(C);
+				txt_insert_buf(text, utxt, st->replacestr);
 				if (text->curl && text->curl->format) {
 					MEM_freeN(text->curl->format);
 					text->curl->format = NULL;
@@ -3024,6 +3080,9 @@ void TEXT_OT_replace(wmOperatorType *ot)
 	/* api callbacks */
 	ot->exec = text_replace_exec;
 	ot->poll = text_space_edit_poll;
+
+	/* flags */
+	ot->flag = OPTYPE_UNDO;
 }
 
 /******************* find set selected *********************/
@@ -3081,6 +3140,9 @@ void TEXT_OT_replace_set_selected(wmOperatorType *ot)
 	/* api callbacks */
 	ot->exec = text_replace_set_selected_exec;
 	ot->poll = text_space_edit_poll;
+
+	/* flags */
+	ot->flag = OPTYPE_UNDO;
 }
 
 /****************** resolve conflict operator ******************/
@@ -3201,26 +3263,3 @@ void TEXT_OT_to_3d_object(wmOperatorType *ot)
 	/* properties */
 	RNA_def_boolean(ot->srna, "split_lines", 0, "Split Lines", "Create one object per line in the text");
 }
-
-
-/************************ undo ******************************/
-
-void ED_text_undo_step(bContext *C, int step)
-{
-	Text *text = CTX_data_edit_text(C);
-
-	if (!text)
-		return;
-
-	if (step == 1)
-		txt_do_undo(text);
-	else if (step == -1)
-		txt_do_redo(text);
-
-	text_update_edited(text);
-
-	text_update_cursor_moved(C);
-	text_drawcache_tag_update(CTX_wm_space_text(C), 1);
-	WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
-}
-
diff --git a/source/blender/editors/space_text/text_undo.c b/source/blender/editors/space_text/text_undo.c
new file mode 100644
index 0000000000000000000000000000000000000000..729522cc8f43cc33fe2be35a65d6efee167e1e76
--- /dev/null
+++ b/source/blender/editors/space_text/text_undo.c
@@ -0,0 +1,185 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/space_text/text_undo.c
+ *  \ingroup sptext
+ */
+
+#include <string.h>
+#include <errno.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_text_types.h"
+
+#include "BLI_listbase.h"
+#include "BLI_array_utils.h"
+
+#include "BLT_translation.h"
+
+#include "PIL_time.h"
+
+#include "BKE_context.h"
+#include "BKE_library.h"
+#include "BKE_report.h"
+#include "BKE_text.h"
+#include "BKE_undo_system.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_text.h"
+#include "ED_curve.h"
+#include "ED_screen.h"
+#include "ED_undo.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "text_intern.h"
+#include "text_format.h"
+
+/* TODO(campbell): undo_system: move text undo out of text block. */
+
+/* -------------------------------------------------------------------- */
+/** \name Implements ED Undo System
+ * \{ */
+
+typedef struct TextUndoStep {
+	UndoStep step;
+	UndoRefID_Text text_ref;
+	TextUndoBuf data;
+} TextUndoStep;
+
+static bool text_undosys_poll(bContext *C)
+{
+	Text *text = CTX_data_edit_text(C);
+	if (text == NULL) {
+		return false;
+	}
+	if (ID_IS_LINKED(text)) {
+		return false;
+	}
+	return true;
+}
+
+static void text_undosys_step_encode_init(struct bContext *C, UndoStep *us_p)
+{
+	TextUndoStep *us = (TextUndoStep *)us_p;
+	BLI_assert(BLI_array_is_zeroed(&us->data, 1));
+
+	UNUSED_VARS(C);
+	/* XXX, use to set the undo type only. */
+
+	us->data.buf = NULL;
+	us->data.len = 0;
+	us->data.pos = -1;
+}
+
+static bool text_undosys_step_encode(struct bContext *C, UndoStep *us_p)
+{
+	TextUndoStep *us = (TextUndoStep *)us_p;
+
+	Text *text = CTX_data_edit_text(C);
+
+	/* No undo data was generated. Hint, use global undo here. */
+	if ((us->data.pos == -1) || (us->data.buf == NULL)) {
+		return false;
+	}
+
+	us->text_ref.ptr = text;
+
+	us->step.data_size = us->data.len;
+
+	return true;
+}
+
+static void text_undosys_step_decode(struct bContext *C, UndoStep *us_p, int dir)
+{
+	TextUndoStep *us = (TextUndoStep *)us_p;
+	Text *text = us->text_ref.ptr;
+
+	if (dir < 0) {
+		TextUndoBuf data = us->data;
+		txt_do_undo(text, &data);
+	}
+	else {
+		TextUndoBuf data = us->data;
+		data.pos = -1;
+		txt_do_redo(text, &data);
+	}
+
+	text_update_edited(text);
+	text_update_cursor_moved(C);
+	text_drawcache_tag_update(CTX_wm_space_text(C), 1);
+	WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
+}
+
+static void text_undosys_step_free(UndoStep *us_p)
+{
+	TextUndoStep *us = (TextUndoStep *)us_p;
+	MEM_SAFE_FREE(us->data.buf);
+}
+
+static void text_undosys_foreach_ID_ref(
+        UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
+{
+	TextUndoStep *us = (TextUndoStep *)us_p;
+	foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->text_ref));
+}
+
+/* Export for ED_undo_sys. */
+
+void ED_text_undosys_type(UndoType *ut)
+{
+	ut->name = "Text";
+	ut->poll = text_undosys_poll;
+	ut->step_encode_init = text_undosys_step_encode_init;
+	ut->step_encode = text_undosys_step_encode;
+	ut->step_decode = text_undosys_step_decode;
+	ut->step_free = text_undosys_step_free;
+
+	ut->step_foreach_ID_ref = text_undosys_foreach_ID_ref;
+
+	ut->mode = BKE_UNDOTYPE_MODE_ACCUMULATE;
+	ut->use_context = false;
+
+	ut->step_size = sizeof(TextUndoStep);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Utilities
+ * \{ */
+
+/* Use operator system to finish the undo step. */
+TextUndoBuf *ED_text_undo_push_init(bContext *C)
+{
+	UndoStack *ustack = ED_undo_stack_get();
+	UndoStep *us_p = BKE_undosys_step_push_init_with_type(ustack, C, NULL, BKE_UNDOSYS_TYPE_TEXT);
+	TextUndoStep *us = (TextUndoStep *)us_p;
+	return &us->data;
+}
+
+/** \} */
diff --git a/source/blender/editors/space_time/space_time.c b/source/blender/editors/space_time/space_time.c
index 9a736ae977f2c491b41d1d805cd930f26586664a..42027f10fe60bacc1d6d193f0a9ec5aa6459bffc 100644
--- a/source/blender/editors/space_time/space_time.c
+++ b/source/blender/editors/space_time/space_time.c
@@ -402,7 +402,6 @@ static void time_draw_caches_keyframes(Main *bmain, ViewLayer *view_layer, View2
 /* draw keyframe lines for timeline */
 static void time_draw_keyframes(const bContext *C, ARegion *ar)
 {
-	WorkSpace *workspace = CTX_wm_workspace(C);
 	Scene *scene = CTX_data_scene(C);
 	ViewLayer *view_layer = CTX_data_view_layer(C);
 	Object *ob = CTX_data_active_object(C);
@@ -444,7 +443,7 @@ static void time_draw_keyframes(const bContext *C, ARegion *ar)
 	 */
 	UI_GetThemeColor3ubv(TH_TIME_KEYFRAME, color);
 
-	if (ob && ((workspace->object_mode == OB_MODE_POSE) || onlysel)) {
+	if (ob && ((ob->mode == OB_MODE_POSE) || onlysel)) {
 		/* draw keyframes for active object only */
 		time_draw_idblock_keyframes(v2d, (ID *)ob, onlysel, color);
 	}
diff --git a/source/blender/editors/space_view3d/drawarmature.c b/source/blender/editors/space_view3d/drawarmature.c
index f0adf307bdaf4f17668e33582092bdc75ab0bcf3..77cd64be7c18c25c268a8e2bfdf9dceca819b26a 100644
--- a/source/blender/editors/space_view3d/drawarmature.c
+++ b/source/blender/editors/space_view3d/drawarmature.c
@@ -2667,10 +2667,8 @@ static void ghost_poses_tag_unselected(Object *ob, short unset)
  *  note: object should be in posemode
  */
 static void draw_ghost_poses_range(
-        const EvaluationContext *eval_ctx_init,
-        Scene *scene, ViewLayer *view_layer, View3D *v3d, ARegion *ar, Base *base)
+        const EvaluationContext *eval_ctx, Scene *scene, ViewLayer *view_layer, View3D *v3d, ARegion *ar, Base *base)
 {
-	EvaluationContext eval_ctx = *eval_ctx_init;
 	Object *ob = base->object;
 	AnimData *adt = BKE_animdata_from_id(&ob->id);
 	bArmature *arm = ob->data;
@@ -2692,7 +2690,7 @@ static void draw_ghost_poses_range(
 	range = (float)(end - start);
 	
 	/* store values */
-	eval_ctx.object_mode &= ~OB_MODE_POSE;
+	ob->mode &= ~OB_MODE_POSE;
 	cfrao = CFRA;
 	flago = arm->flag;
 	arm->flag &= ~(ARM_DRAWNAMES | ARM_DRAWAXES);
@@ -2713,8 +2711,8 @@ static void draw_ghost_poses_range(
 		UI_GetThemeColorShadeAlpha4ubv(TH_WIRE, 0, -128 - (int)(120.0f * sqrtf(colfac)), col);
 		
 		BKE_animsys_evaluate_animdata(scene, &ob->id, adt, (float)CFRA, ADT_RECALC_ALL);
-		BKE_pose_where_is(&eval_ctx, scene, ob);
-		draw_pose_bones(&eval_ctx, scene, view_layer, v3d, ar, base, OB_WIRE, col, true, false);
+		BKE_pose_where_is(eval_ctx, scene, ob);
+		draw_pose_bones(eval_ctx, scene, view_layer, v3d, ar, base, OB_WIRE, col, true, false);
 	}
 	glDisable(GL_BLEND);
 	if (v3d->zbuf) glEnable(GL_DEPTH_TEST);
@@ -2730,17 +2728,16 @@ static void draw_ghost_poses_range(
 	CFRA = cfrao;
 	ob->pose = poseo;
 	arm->flag = flago;
-	eval_ctx.object_mode |= OB_MODE_POSE;
+	ob->mode |= OB_MODE_POSE;
 }
 
 /* draw ghosts on keyframes in action within range 
  *	- object should be in posemode 
  */
 static void draw_ghost_poses_keys(
-        const struct EvaluationContext *eval_ctx_init, Scene *scene, ViewLayer *view_layer,
+        const struct EvaluationContext *eval_ctx, Scene *scene, ViewLayer *view_layer,
         View3D *v3d, ARegion *ar, Base *base)
 {
-	EvaluationContext eval_ctx = *eval_ctx_init;
 	Object *ob = base->object;
 	AnimData *adt = BKE_animdata_from_id(&ob->id);
 	bAction *act = (adt) ? adt->action : NULL;
@@ -2774,7 +2771,7 @@ static void draw_ghost_poses_keys(
 	if (range == 0) return;
 	
 	/* store values */
-	eval_ctx.object_mode &= ~OB_MODE_POSE;
+	ob->mode &= ~OB_MODE_POSE;
 	cfrao = CFRA;
 	flago = arm->flag;
 	arm->flag &= ~(ARM_DRAWNAMES | ARM_DRAWAXES);
@@ -2797,8 +2794,8 @@ static void draw_ghost_poses_keys(
 		CFRA = (int)ak->cfra;
 		
 		BKE_animsys_evaluate_animdata(scene, &ob->id, adt, (float)CFRA, ADT_RECALC_ALL);
-		BKE_pose_where_is(&eval_ctx, scene, ob);
-		draw_pose_bones(&eval_ctx, scene, view_layer, v3d, ar, base, OB_WIRE, col, true, false);
+		BKE_pose_where_is(eval_ctx, scene, ob);
+		draw_pose_bones(eval_ctx, scene, view_layer, v3d, ar, base, OB_WIRE, col, true, false);
 	}
 	glDisable(GL_BLEND);
 	if (v3d->zbuf) glEnable(GL_DEPTH_TEST);
@@ -2815,17 +2812,16 @@ static void draw_ghost_poses_keys(
 	CFRA = cfrao;
 	ob->pose = poseo;
 	arm->flag = flago;
-	eval_ctx.object_mode |= OB_MODE_POSE;
+	ob->mode |= OB_MODE_POSE;
 }
 
 /* draw ghosts around current frame
  *  - object is supposed to be armature in posemode
  */
 static void draw_ghost_poses(
-        const struct EvaluationContext *eval_ctx_init, Scene *scene, ViewLayer *view_layer,
+        const struct EvaluationContext *eval_ctx, Scene *scene, ViewLayer *view_layer,
         View3D *v3d, ARegion *ar, Base *base)
 {
-	EvaluationContext eval_ctx = *eval_ctx_init;
 	Object *ob = base->object;
 	AnimData *adt = BKE_animdata_from_id(&ob->id);
 	bArmature *arm = ob->data;
@@ -2850,7 +2846,7 @@ static void draw_ghost_poses(
 	range = (float)(arm->ghostep) * stepsize + 0.5f;   /* plus half to make the for loop end correct */
 	
 	/* store values */
-	eval_ctx.object_mode &= ~OB_MODE_POSE;
+	ob->mode &= ~OB_MODE_POSE;
 	cfrao = CFRA;
 	actframe = BKE_nla_tweakedit_remap(adt, (float)CFRA, 0);
 	flago = arm->flag;
@@ -2878,8 +2874,8 @@ static void draw_ghost_poses(
 			
 			if (CFRA != cfrao) {
 				BKE_animsys_evaluate_animdata(scene, &ob->id, adt, (float)CFRA, ADT_RECALC_ALL);
-				BKE_pose_where_is(&eval_ctx, scene, ob);
-				draw_pose_bones(&eval_ctx, scene, view_layer, v3d, ar, base, OB_WIRE, col, true, false);
+				BKE_pose_where_is(eval_ctx, scene, ob);
+				draw_pose_bones(eval_ctx, scene, view_layer, v3d, ar, base, OB_WIRE, col, true, false);
 			}
 		}
 		
@@ -2893,8 +2889,8 @@ static void draw_ghost_poses(
 			
 			if (CFRA != cfrao) {
 				BKE_animsys_evaluate_animdata(scene, &ob->id, adt, (float)CFRA, ADT_RECALC_ALL);
-				BKE_pose_where_is(&eval_ctx, scene, ob);
-				draw_pose_bones(&eval_ctx, scene, view_layer, v3d, ar, base, OB_WIRE, col, true, false);
+				BKE_pose_where_is(eval_ctx, scene, ob);
+				draw_pose_bones(eval_ctx, scene, view_layer, v3d, ar, base, OB_WIRE, col, true, false);
 			}
 		}
 	}
@@ -2912,7 +2908,7 @@ static void draw_ghost_poses(
 	CFRA = cfrao;
 	ob->pose = poseo;
 	arm->flag = flago;
-	eval_ctx.object_mode |= OB_MODE_POSE;
+	ob->mode |= OB_MODE_POSE;
 }
 
 /* ********************************** Armature Drawing - Main ************************* */
@@ -2974,11 +2970,11 @@ bool draw_armature(
 					}
 					else
 #endif
-					if (eval_ctx->object_mode & OB_MODE_POSE) {
+					if (ob->mode & OB_MODE_POSE) {
 						arm->flag |= ARM_POSEMODE;
 					}
 				}
-				else if (eval_ctx->object_mode & OB_MODE_POSE) {
+				else if (ob->mode & OB_MODE_POSE) {
 					if (arm->ghosttype == ARM_GHOST_RANGE) {
 						draw_ghost_poses_range(eval_ctx, scene, view_layer, v3d, ar, base);
 					}
@@ -2992,7 +2988,7 @@ bool draw_armature(
 					if ((dflag & DRAW_SCENESET) == 0) {
 						if (ob == OBACT(view_layer))
 							arm->flag |= ARM_POSEMODE;
-						else if (OBACT(view_layer) && (eval_ctx->object_mode & OB_MODE_WEIGHT_PAINT)) {
+						else if (OBACT(view_layer) && (OBACT(view_layer)->mode & OB_MODE_WEIGHT_PAINT)) {
 							if (ob == modifiers_isDeformedByArmature(OBACT(view_layer)))
 								arm->flag |= ARM_POSEMODE;
 						}
diff --git a/source/blender/editors/space_view3d/drawmesh.c b/source/blender/editors/space_view3d/drawmesh.c
index c014c2b12a0baf9c3b1391bdf3dd09a9de661723..0d4ec4d42d14763fbc0013a186f4695c54265798 100644
--- a/source/blender/editors/space_view3d/drawmesh.c
+++ b/source/blender/editors/space_view3d/drawmesh.c
@@ -59,8 +59,6 @@
 
 #include "UI_resources.h"
 
-#include "DEG_depsgraph.h"
-
 #include "GPU_draw.h"
 #include "GPU_material.h"
 #include "GPU_basic_shader.h"
@@ -327,10 +325,8 @@ void draw_mesh_paint_weight_edges(RegionView3D *rv3d, DerivedMesh *dm,
 	}
 }
 
-void draw_mesh_paint(
-        const EvaluationContext *eval_ctx,
-        View3D *v3d, RegionView3D *rv3d,
-        Object *ob, DerivedMesh *dm, const int draw_flags)
+void draw_mesh_paint(View3D *v3d, RegionView3D *rv3d,
+                     Object *ob, DerivedMesh *dm, const int draw_flags)
 {
 	DMSetDrawOptions facemask = NULL;
 	Mesh *me = ob->data;
@@ -340,21 +336,21 @@ void draw_mesh_paint(
 	if (me->editflag & (ME_EDIT_PAINT_VERT_SEL | ME_EDIT_PAINT_FACE_SEL))
 		facemask = wpaint__setSolidDrawOptions_facemask;
 
-	if (eval_ctx->object_mode & OB_MODE_WEIGHT_PAINT) {
+	if (ob->mode & OB_MODE_WEIGHT_PAINT) {
 		draw_mesh_paint_weight_faces(dm, use_light, facemask, me);
 	}
-	else if (eval_ctx->object_mode & OB_MODE_VERTEX_PAINT) {
+	else if (ob->mode & OB_MODE_VERTEX_PAINT) {
 		draw_mesh_paint_vcolor_faces(dm, use_light, facemask, me, me);
 	}
 
 	/* draw face selection on top */
 	if (draw_flags & DRAW_FACE_SELECT) {
-		bool draw_select_edges = (eval_ctx->object_mode & OB_MODE_TEXTURE_PAINT) == 0;
+		bool draw_select_edges = (ob->mode & OB_MODE_TEXTURE_PAINT) == 0;
 		draw_mesh_face_select(rv3d, me, dm, draw_select_edges);
 	}
 	else if ((use_light == false) || (ob->dtx & OB_DRAWWIRE)) {
-		const bool use_depth = (v3d->flag & V3D_ZBUF_SELECT) || !(eval_ctx->object_mode & OB_MODE_WEIGHT_PAINT);
-		const bool use_alpha = (eval_ctx->object_mode & OB_MODE_VERTEX_PAINT) == 0;
+		const bool use_depth = (v3d->flag & V3D_ZBUF_SELECT) || !(ob->mode & OB_MODE_WEIGHT_PAINT);
+		const bool use_alpha = (ob->mode & OB_MODE_VERTEX_PAINT) == 0;
 
 		if (use_alpha == false) {
 			set_inverted_drawing(1);
diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c
index adb2d47546440d28c7bfa9978e0e30854c2d7e3f..2c6d977b552544a60e6f0aac41389ac9e228d7d2 100644
--- a/source/blender/editors/space_view3d/drawobject.c
+++ b/source/blender/editors/space_view3d/drawobject.c
@@ -314,15 +314,13 @@ static bool check_ob_drawface_dot(Scene *sce, View3D *vd, char dt)
 
 /* check for glsl drawing */
 
-bool draw_glsl_material(
-        const EvaluationContext *eval_ctx, Scene *scene, ViewLayer *view_layer,
-        Object *ob, View3D *v3d, const char dt)
+bool draw_glsl_material(Scene *scene, ViewLayer *view_layer, Object *ob, View3D *v3d, const char dt)
 {
 	if (G.f & G_PICKSEL)
 		return false;
 	if (!check_object_draw_texture(scene, v3d, dt))
 		return false;
-	if (ob == OBACT(view_layer) && (ob && eval_ctx->object_mode & OB_MODE_WEIGHT_PAINT))
+	if (ob == OBACT(view_layer) && (ob && ob->mode & OB_MODE_WEIGHT_PAINT))
 		return false;
 	
 	if (v3d->flag2 & V3D_SHOW_SOLID_MATCAP)
@@ -336,7 +334,7 @@ bool draw_glsl_material(
 		return false;
 }
 
-static bool check_alpha_pass(const EvaluationContext *eval_ctx, Base *base)
+static bool check_alpha_pass(Base *base)
 {
 	if (base->flag_legacy & OB_FROMDUPLI)
 		return false;
@@ -344,9 +342,8 @@ static bool check_alpha_pass(const EvaluationContext *eval_ctx, Base *base)
 	if (G.f & G_PICKSEL)
 		return false;
 
-	if (eval_ctx->object_mode & OB_MODE_ALL_PAINT) {
+	if (base->object->mode & OB_MODE_ALL_PAINT)
 		return false;
-	}
 
 	return (base->object->dtx & OB_DRAWTRANSP);
 }
@@ -755,8 +752,6 @@ static void draw_empty_image(Object *ob, const short dflag, const unsigned char
 		immEnd();
 
 		immUnbindProgram();
-
-		glBindTexture(GL_TEXTURE_2D, 0); /* necessary? */
 	}
 
 	/* Draw the image outline */
@@ -3895,9 +3890,8 @@ static DMDrawOption draw_em_fancy__setGLSLFaceOpts(void *userData, int index)
 	}
 }
 
-static void draw_em_fancy(
-        const EvaluationContext *eval_ctx, Scene *scene, ViewLayer *view_layer, ARegion *ar, View3D *v3d,
-        Object *ob, BMEditMesh *em, DerivedMesh *cageDM, DerivedMesh *finalDM, const char dt)
+static void draw_em_fancy(Scene *scene, ViewLayer *view_layer, ARegion *ar, View3D *v3d,
+                          Object *ob, BMEditMesh *em, DerivedMesh *cageDM, DerivedMesh *finalDM, const char dt)
 
 {
 	RegionView3D *rv3d = ar->regiondata;
@@ -3934,7 +3928,7 @@ static void draw_em_fancy(
 			glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
 		}
 		else if (check_object_draw_texture(scene, v3d, dt)) {
-			if (draw_glsl_material(eval_ctx, scene, view_layer, ob, v3d, dt)) {
+			if (draw_glsl_material(scene, view_layer, ob, v3d, dt)) {
 				glFrontFace((ob->transflag & OB_NEG_SCALE) ? GL_CW : GL_CCW);
 
 				finalDM->drawMappedFacesGLSL(finalDM, GPU_object_material_bind,
@@ -4227,12 +4221,10 @@ static void draw_em_fancy_new(Scene *UNUSED(scene), ARegion *UNUSED(ar), View3D
 
 /* Mesh drawing routines */
 
-void draw_mesh_object_outline(
-        const EvaluationContext *eval_ctx, View3D *v3d,
-        Object *ob, DerivedMesh *dm, const unsigned char ob_wire_col[4]) /* LEGACY */
+void draw_mesh_object_outline(View3D *v3d, Object *ob, DerivedMesh *dm, const unsigned char ob_wire_col[4]) /* LEGACY */
 {
 	if ((v3d->transp == false) &&  /* not when we draw the transparent pass */
-	    (eval_ctx->object_mode & OB_MODE_ALL_PAINT) == false) /* not when painting (its distracting) - campbell */
+	    (ob->mode & OB_MODE_ALL_PAINT) == false) /* not when painting (its distracting) - campbell */
 	{
 		glLineWidth(UI_GetThemeValuef(TH_OUTLINE_WIDTH) * 2.0f);
 		glDepthMask(GL_FALSE);
@@ -4255,12 +4247,10 @@ void draw_mesh_object_outline(
 	}
 }
 
-static void draw_mesh_object_outline_new(
-        const EvaluationContext *eval_ctx, View3D *v3d, RegionView3D *rv3d,
-        Mesh *me, const bool is_active)
+static void draw_mesh_object_outline_new(View3D *v3d, RegionView3D *rv3d, Object *ob, Mesh *me, const bool is_active)
 {
 	if ((v3d->transp == false) &&  /* not when we draw the transparent pass */
-	    (eval_ctx->object_mode & OB_MODE_ALL_PAINT) == false) /* not when painting (its distracting) - campbell */
+	    (ob->mode & OB_MODE_ALL_PAINT) == false) /* not when painting (its distracting) - campbell */
 	{
 		glLineWidth(UI_GetThemeValuef(TH_OUTLINE_WIDTH) * 2.0f);
 		glDepthMask(GL_FALSE);
@@ -4310,9 +4300,7 @@ static void draw_mesh_fancy(
         const char dt, const unsigned char ob_wire_col[4], const short dflag)
 {
 #ifdef WITH_GAMEENGINE
-	Object *ob = (
-	        (rv3d->rflag & RV3D_IS_GAME_ENGINE) ?
-	        BKE_object_lod_meshob_get(base->object, view_layer, eval_ctx->object_mode) : base->object);
+	Object *ob = (rv3d->rflag & RV3D_IS_GAME_ENGINE) ? BKE_object_lod_meshob_get(base->object, view_layer) : base->object;
 #else
 	Object *ob = base->object;
 #endif
@@ -4321,7 +4309,7 @@ static void draw_mesh_fancy(
 	bool /* no_verts,*/ no_edges, no_faces;
 	DerivedMesh *dm = mesh_get_derived_final(eval_ctx, scene, ob, scene->customdata_mask);
 	const bool is_obact = (ob == OBACT(view_layer));
-	int draw_flags = (is_obact && BKE_paint_select_face_test(ob, eval_ctx->object_mode)) ? DRAW_FACE_SELECT : 0;
+	int draw_flags = (is_obact && BKE_paint_select_face_test(ob)) ? DRAW_FACE_SELECT : 0;
 
 	if (!dm)
 		return;
@@ -4359,7 +4347,7 @@ static void draw_mesh_fancy(
 			draw_bounding_volume(ob, ob->boundtype, ob_wire_col);
 	}
 	else if ((no_faces && no_edges) ||
-	         ((!is_obact || (eval_ctx->object_mode == OB_MODE_OBJECT)) && object_is_halo(scene, ob)))
+	         ((!is_obact || (ob->mode == OB_MODE_OBJECT)) && object_is_halo(scene, ob)))
 	{
 		glPointSize(1.5f);
 		dm->drawVerts(dm);
@@ -4367,7 +4355,7 @@ static void draw_mesh_fancy(
 	else if ((dt == OB_WIRE) || no_faces) {
 		draw_wire = OBDRAW_WIRE_ON; /* draw wire only, no depth buffer stuff */
 	}
-	else if (((is_obact && eval_ctx->object_mode & OB_MODE_TEXTURE_PAINT)) ||
+	else if (((is_obact && ob->mode & OB_MODE_TEXTURE_PAINT)) ||
 	         check_object_draw_texture(scene, v3d, dt))
 	{
 		bool draw_loose = true;
@@ -4378,17 +4366,15 @@ static void draw_mesh_fancy(
 		    !(G.f & G_PICKSEL || (draw_flags & DRAW_FACE_SELECT)) &&
 		    (draw_wire == OBDRAW_WIRE_OFF))
 		{
-			draw_mesh_object_outline(eval_ctx, v3d, ob, dm, ob_wire_col);
+			draw_mesh_object_outline(v3d, ob, dm, ob_wire_col);
 		}
 
-		if (draw_glsl_material(eval_ctx, scene, view_layer, ob, v3d, dt) && !(draw_flags & DRAW_MODIFIERS_PREVIEW)) {
+		if (draw_glsl_material(scene, view_layer, ob, v3d, dt) && !(draw_flags & DRAW_MODIFIERS_PREVIEW)) {
 			Paint *p;
 
 			glFrontFace((ob->transflag & OB_NEG_SCALE) ? GL_CW : GL_CCW);
 
-			if ((v3d->flag2 & V3D_SHOW_SOLID_MATCAP) && ob->sculpt &&
-			    (p = BKE_paint_get_active(scene, view_layer, eval_ctx->object_mode)))
-			{
+			if ((v3d->flag2 & V3D_SHOW_SOLID_MATCAP) && ob->sculpt && (p = BKE_paint_get_active(scene, view_layer))) {
 				GPUVertexAttribs gattribs;
 				float planes[4][4];
 				float (*fpl)[4] = NULL;
@@ -4451,7 +4437,7 @@ static void draw_mesh_fancy(
 				    (draw_wire == OBDRAW_WIRE_OFF) &&
 				    (ob->sculpt == NULL))
 				{
-					draw_mesh_object_outline(eval_ctx, v3d, ob, dm, ob_wire_col);
+					draw_mesh_object_outline(v3d, ob, dm, ob_wire_col);
 				}
 
 				/* materials arent compatible with vertex colors */
@@ -4475,12 +4461,12 @@ static void draw_mesh_fancy(
 			    (draw_wire == OBDRAW_WIRE_OFF) &&
 			    (ob->sculpt == NULL))
 			{
-				draw_mesh_object_outline(eval_ctx, v3d, ob, dm, ob_wire_col);
+				draw_mesh_object_outline(v3d, ob, dm, ob_wire_col);
 			}
 
 			glFrontFace((ob->transflag & OB_NEG_SCALE) ? GL_CW : GL_CCW);
 
-			if (ob->sculpt && (p = BKE_paint_get_active(scene, view_layer, eval_ctx->object_mode))) {
+			if (ob->sculpt && (p = BKE_paint_get_active(scene, view_layer))) {
 				float planes[4][4];
 				float (*fpl)[4] = NULL;
 				const bool fast = (p->flags & PAINT_FAST_NAVIGATE) && (rv3d->rflag & RV3D_NAVIGATING);
@@ -4512,7 +4498,7 @@ static void draw_mesh_fancy(
 		}
 	}
 	else if (dt == OB_PAINT) {
-		draw_mesh_paint(eval_ctx, v3d, rv3d, ob, dm, draw_flags);
+		draw_mesh_paint(v3d, rv3d, ob, dm, draw_flags);
 
 		/* since we already draw wire as wp guide, don't draw over the top */
 		draw_wire = OBDRAW_WIRE_OFF;
@@ -4527,7 +4513,7 @@ static void draw_mesh_fancy(
 		 * with the background. */
 
 		if ((dflag & DRAW_CONSTCOLOR) == 0) {
-			if (is_obact && (eval_ctx->object_mode & OB_MODE_PARTICLE_EDIT)) {
+			if (is_obact && (ob->mode & OB_MODE_PARTICLE_EDIT)) {
 				float color[3];
 				ob_wire_color_blend_theme_id(ob_wire_col, TH_BACK, 0.15f, color);
 				glColor3fv(color);
@@ -4560,7 +4546,7 @@ static void draw_mesh_fancy(
 		}
 	}
 	
-	if (is_obact && BKE_paint_select_vert_test(ob, eval_ctx->object_mode)) {
+	if (is_obact && BKE_paint_select_vert_test(ob)) {
 		const bool use_depth = (v3d->flag & V3D_ZBUF_SELECT) != 0;
 		glPointSize(UI_GetThemeValuef(TH_VERTEX_SIZE));
 
@@ -4579,7 +4565,7 @@ static bool draw_mesh_object(
         const char dt, const unsigned char ob_wire_col[4], const short dflag)
 {
 	Object *ob = base->object;
-	Object *obedit = OBEDIT_FROM_EVAL_CTX(eval_ctx);
+	Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
 	Mesh *me = ob->data;
 	BMEditMesh *em = me->edit_btmesh;
 	bool do_alpha_after = false, drawlinked = false, retval = false;
@@ -4629,13 +4615,13 @@ static bool draw_mesh_object(
 
 		if (use_material) {
 			if (dt > OB_WIRE) {
-				const bool glsl = draw_glsl_material(eval_ctx, scene, view_layer, ob, v3d, dt);
+				const bool glsl = draw_glsl_material(scene, view_layer, ob, v3d, dt);
 
-				GPU_begin_object_materials(v3d, rv3d, scene, view_layer, ob, glsl, eval_ctx->object_mode, NULL);
+				GPU_begin_object_materials(v3d, rv3d, scene, view_layer, ob, glsl, NULL);
 			}
 		}
 
-		draw_em_fancy(eval_ctx, scene, view_layer, ar, v3d, ob, em, cageDM, finalDM, dt);
+		draw_em_fancy(scene, view_layer, ar, v3d, ob, em, cageDM, finalDM, dt);
 
 		if (use_material) {
 			GPU_end_object_materials();
@@ -4648,13 +4634,12 @@ static bool draw_mesh_object(
 		/* ob->bb was set by derived mesh system, do NULL check just to be sure */
 		if (me->totpoly <= 4 || (!ob->bb || ED_view3d_boundbox_clip(rv3d, ob->bb))) {
 			if (dt > OB_WIRE) {
-				const bool glsl = draw_glsl_material(eval_ctx, scene, view_layer, ob, v3d, dt);
+				const bool glsl = draw_glsl_material(scene, view_layer, ob, v3d, dt);
 
 				if (dt == OB_SOLID || glsl) {
-					const bool check_alpha = check_alpha_pass(eval_ctx, base);
-					GPU_begin_object_materials(
-					        v3d, rv3d, scene, view_layer, ob,
-					        glsl, eval_ctx->object_mode, (check_alpha) ? &do_alpha_after : NULL);
+					const bool check_alpha = check_alpha_pass(base);
+					GPU_begin_object_materials(v3d, rv3d, scene, view_layer, ob, glsl,
+					                           (check_alpha) ? &do_alpha_after : NULL);
 				}
 			}
 
@@ -4733,9 +4718,7 @@ static void draw_mesh_fancy_new(
 	}
 
 #ifdef WITH_GAMEENGINE
-	Object *ob = (
-	        (rv3d->rflag & RV3D_IS_GAME_ENGINE) ?
-	        BKE_object_lod_meshob_get(base->object, view_layer, eval_ctx->object_mode) : base->object);
+	Object *ob = (rv3d->rflag & RV3D_IS_GAME_ENGINE) ? BKE_object_lod_meshob_get(base->object, view_layer) : base->object;
 #else
 	Object *ob = base->object;
 #endif
@@ -4744,7 +4727,7 @@ static void draw_mesh_fancy_new(
 	bool no_edges, no_faces;
 	DerivedMesh *dm = mesh_get_derived_final(eval_ctx, scene, ob, scene->customdata_mask);
 	const bool is_obact = (ob == OBACT(view_layer));
-	int draw_flags = (is_obact && BKE_paint_select_face_test(ob, eval_ctx->object_mode)) ? DRAW_FACE_SELECT : 0;
+	int draw_flags = (is_obact && BKE_paint_select_face_test(ob)) ? DRAW_FACE_SELECT : 0;
 
 	if (!dm)
 		return;
@@ -4787,7 +4770,7 @@ static void draw_mesh_fancy_new(
 			draw_bounding_volume(ob, ob->boundtype, ob_wire_col);
 	}
 	else if ((no_faces && no_edges) ||
-	         ((!is_obact || (eval_ctx->object_mode == OB_MODE_OBJECT)) && object_is_halo(scene, ob)))
+	         ((!is_obact || (ob->mode == OB_MODE_OBJECT)) && object_is_halo(scene, ob)))
 	{
 		glPointSize(1.5f);
 		// dm->drawVerts(dm);
@@ -4853,7 +4836,7 @@ static void draw_mesh_fancy_new(
 		GWN_batch_draw(batch);
 #endif
 	}
-	else if (((is_obact && eval_ctx->object_mode & OB_MODE_TEXTURE_PAINT)) ||
+	else if (((is_obact && ob->mode & OB_MODE_TEXTURE_PAINT)) ||
 	         check_object_draw_texture(scene, v3d, dt))
 	{
 		bool draw_loose = true;
@@ -4864,17 +4847,15 @@ static void draw_mesh_fancy_new(
 		    !(G.f & G_PICKSEL || (draw_flags & DRAW_FACE_SELECT)) &&
 		    (draw_wire == OBDRAW_WIRE_OFF))
 		{
-			draw_mesh_object_outline_new(eval_ctx, v3d, rv3d, me, (ob == OBACT(view_layer)));
+			draw_mesh_object_outline_new(v3d, rv3d, ob, me, (ob == OBACT(view_layer)));
 		}
 
-		if (draw_glsl_material(eval_ctx, scene, view_layer, ob, v3d, dt) && !(draw_flags & DRAW_MODIFIERS_PREVIEW)) {
+		if (draw_glsl_material(scene, view_layer, ob, v3d, dt) && !(draw_flags & DRAW_MODIFIERS_PREVIEW)) {
 			Paint *p;
 
 			glFrontFace((ob->transflag & OB_NEG_SCALE) ? GL_CW : GL_CCW);
 
-			if ((v3d->flag2 & V3D_SHOW_SOLID_MATCAP) && ob->sculpt &&
-			    (p = BKE_paint_get_active(scene, view_layer, eval_ctx->object_mode)))
-			{
+			if ((v3d->flag2 & V3D_SHOW_SOLID_MATCAP) && ob->sculpt && (p = BKE_paint_get_active(scene, view_layer))) {
 				GPUVertexAttribs gattribs;
 				float planes[4][4];
 				float (*fpl)[4] = NULL;
@@ -4933,7 +4914,7 @@ static void draw_mesh_fancy_new(
 				    (draw_wire == OBDRAW_WIRE_OFF) &&
 				    (ob->sculpt == NULL))
 				{
-					draw_mesh_object_outline_new(eval_ctx, v3d, rv3d, me, (ob == OBACT(view_layer)));
+					draw_mesh_object_outline_new(v3d, rv3d, ob, me, (ob == OBACT(view_layer)));
 				}
 
 				/* materials arent compatible with vertex colors */
@@ -4958,12 +4939,12 @@ static void draw_mesh_fancy_new(
 			    (ob->sculpt == NULL))
 			{
 				/* TODO: move this into a separate pass */
-				draw_mesh_object_outline_new(eval_ctx, v3d, rv3d, me, (ob == OBACT(view_layer)));
+				draw_mesh_object_outline_new(v3d, rv3d, ob, me, (ob == OBACT(view_layer)));
 			}
 
 			glFrontFace((ob->transflag & OB_NEG_SCALE) ? GL_CW : GL_CCW);
 
-			if (ob->sculpt && (p = BKE_paint_get_active(scene, view_layer, eval_ctx->object_mode))) {
+			if (ob->sculpt && (p = BKE_paint_get_active(scene, view_layer))) {
 				float planes[4][4];
 				float (*fpl)[4] = NULL;
 				const bool fast = (p->flags & PAINT_FAST_NAVIGATE) && (rv3d->rflag & RV3D_NAVIGATING);
@@ -4995,7 +4976,7 @@ static void draw_mesh_fancy_new(
 		}
 	}
 	else if (dt == OB_PAINT) {
-		draw_mesh_paint(eval_ctx, v3d, rv3d, ob, dm, draw_flags);
+		draw_mesh_paint(v3d, rv3d, ob, dm, draw_flags);
 
 		/* since we already draw wire as wp guide, don't draw over the top */
 		draw_wire = OBDRAW_WIRE_OFF;
@@ -5057,7 +5038,7 @@ static bool UNUSED_FUNCTION(draw_mesh_object_new)(
 		/* TODO: handle shadow pass separately */
 		return true;
 	}
-	Object *obedit = OBEDIT_FROM_EVAL_CTX(eval_ctx);
+	Object *obedit = OBEDIT_FROM_VIEW_LAYER(eval_ctx->view_layer);
 	
 	if (obedit && ob != obedit && ob->data == obedit->data) {
 		if (BKE_key_from_object(ob) || BKE_key_from_object(obedit)) {}
@@ -5102,9 +5083,9 @@ static bool UNUSED_FUNCTION(draw_mesh_object_new)(
 				DM_update_materials(cageDM, ob);
 			}
 
-			const bool glsl = draw_glsl_material(eval_ctx, scene, eval_ctx->view_layer, ob, v3d, dt);
+			const bool glsl = draw_glsl_material(scene, eval_ctx->view_layer, ob, v3d, dt);
 
-			GPU_begin_object_materials(v3d, rv3d, scene, eval_ctx->view_layer, ob, glsl, eval_ctx->object_mode, NULL);
+			GPU_begin_object_materials(v3d, rv3d, scene, eval_ctx->view_layer, ob, glsl, NULL);
 		}
 
 		draw_em_fancy_new(scene, ar, v3d, ob, me, em, cageDM, finalDM, dt);
@@ -5120,13 +5101,12 @@ static bool UNUSED_FUNCTION(draw_mesh_object_new)(
 		/* ob->bb was set by derived mesh system, do NULL check just to be sure */
 		if (me->totpoly <= 4 || (!ob->bb || ED_view3d_boundbox_clip(rv3d, ob->bb))) {
 			if (solid) {
-				const bool glsl = draw_glsl_material(eval_ctx, scene, eval_ctx->view_layer, ob, v3d, dt);
+				const bool glsl = draw_glsl_material(scene, eval_ctx->view_layer, ob, v3d, dt);
 
 				if (dt == OB_SOLID || glsl) {
-					const bool check_alpha = check_alpha_pass(eval_ctx, base);
-					GPU_begin_object_materials(
-					        v3d, rv3d, scene, eval_ctx->view_layer, ob,
-					        eval_ctx->object_mode, glsl, (check_alpha) ? &do_alpha_after : NULL);
+					const bool check_alpha = check_alpha_pass(base);
+					GPU_begin_object_materials(v3d, rv3d, scene, eval_ctx->view_layer, ob, glsl,
+					                           (check_alpha) ? &do_alpha_after : NULL);
 				}
 			}
 
@@ -5443,9 +5423,7 @@ static void drawCurveDMWired(Object *ob)
 }
 
 /* return true when nothing was drawn */
-static bool drawCurveDerivedMesh(
-        const EvaluationContext *eval_ctx, Scene *scene, ViewLayer *view_layer, View3D *v3d, RegionView3D *rv3d,
-        Base *base, const char dt)
+static bool drawCurveDerivedMesh(Scene *scene, ViewLayer *view_layer, View3D *v3d, RegionView3D *rv3d, Base *base, const char dt)
 {
 	Object *ob = base->object;
 	DerivedMesh *dm = ob->derivedFinal;
@@ -5459,10 +5437,8 @@ static bool drawCurveDerivedMesh(
 	glFrontFace((ob->transflag & OB_NEG_SCALE) ? GL_CW : GL_CCW);
 
 	if (dt > OB_WIRE && dm->getNumPolys(dm)) {
-		bool glsl = draw_glsl_material(eval_ctx, scene, view_layer, ob, v3d, dt);
-		GPU_begin_object_materials(
-		        v3d, rv3d, scene, view_layer, ob,
-		        eval_ctx->object_mode, glsl, NULL);
+		bool glsl = draw_glsl_material(scene, view_layer, ob, v3d, dt);
+		GPU_begin_object_materials(v3d, rv3d, scene, view_layer, ob, glsl, NULL);
 
 		if (!glsl)
 			dm->drawFacesSolid(dm, NULL, 0, GPU_object_material_bind);
@@ -5483,9 +5459,8 @@ static bool drawCurveDerivedMesh(
  * Only called by #drawDispList
  * \return true when nothing was drawn
  */
-static bool drawDispList_nobackface(
-        const EvaluationContext *eval_ctx, Scene *scene, ViewLayer *view_layer, View3D *v3d, RegionView3D *rv3d,
-        Base *base, const char dt, const short dflag, const unsigned char ob_wire_col[4])
+static bool drawDispList_nobackface(Scene *scene, ViewLayer *view_layer, View3D *v3d, RegionView3D *rv3d, Base *base,
+                                    const char dt, const short dflag, const unsigned char ob_wire_col[4])
 {
 	Object *ob = base->object;
 	ListBase *lb = NULL;
@@ -5525,13 +5500,13 @@ static bool drawDispList_nobackface(
 					/* pass */
 				}
 				else {
-					if (draw_glsl_material(eval_ctx, scene, view_layer, ob, v3d, dt)) {
-						GPU_begin_object_materials(v3d, rv3d, scene, view_layer, ob, 1, eval_ctx->object_mode, NULL);
+					if (draw_glsl_material(scene, view_layer, ob, v3d, dt)) {
+						GPU_begin_object_materials(v3d, rv3d, scene, view_layer, ob, 1, NULL);
 						drawDispListsolid(lb, ob, dflag, ob_wire_col, true);
 						GPU_end_object_materials();
 					}
 					else {
-						GPU_begin_object_materials(v3d, rv3d, scene, view_layer, ob, 0, eval_ctx->object_mode, NULL);
+						GPU_begin_object_materials(v3d, rv3d, scene, view_layer, ob, 0, NULL);
 						drawDispListsolid(lb, ob, dflag, ob_wire_col, false);
 						GPU_end_object_materials();
 					}
@@ -5560,13 +5535,13 @@ static bool drawDispList_nobackface(
 
 				if (dl->nors == NULL) BKE_displist_normals_add(lb);
 
-				if (draw_glsl_material(eval_ctx, scene, view_layer, ob, v3d, dt)) {
-					GPU_begin_object_materials(v3d, rv3d, scene, view_layer, ob, 1, eval_ctx->object_mode, NULL);
+				if (draw_glsl_material(scene, view_layer, ob, v3d, dt)) {
+					GPU_begin_object_materials(v3d, rv3d, scene, view_layer, ob, 1, NULL);
 					drawDispListsolid(lb, ob, dflag, ob_wire_col, true);
 					GPU_end_object_materials();
 				}
 				else {
-					GPU_begin_object_materials(v3d, rv3d, scene, view_layer, ob, 0, eval_ctx->object_mode, NULL);
+					GPU_begin_object_materials(v3d, rv3d, scene, view_layer, ob, 0, NULL);
 					drawDispListsolid(lb, ob, dflag, ob_wire_col, false);
 					GPU_end_object_materials();
 				}
@@ -5585,13 +5560,13 @@ static bool drawDispList_nobackface(
 
 				if (solid) {
 
-					if (draw_glsl_material(eval_ctx, scene, view_layer, ob, v3d, dt)) {
-						GPU_begin_object_materials(v3d, rv3d, scene, view_layer, ob, 1, eval_ctx->object_mode, NULL);
+					if (draw_glsl_material(scene, view_layer, ob, v3d, dt)) {
+						GPU_begin_object_materials(v3d, rv3d, scene, view_layer, ob, 1, NULL);
 						drawDispListsolid(lb, ob, dflag, ob_wire_col, true);
 						GPU_end_object_materials();
 					}
 					else {
-						GPU_begin_object_materials(v3d, rv3d, scene, view_layer, ob, 0, eval_ctx->object_mode, NULL);
+						GPU_begin_object_materials(v3d, rv3d, scene, view_layer, ob, 0, NULL);
 						drawDispListsolid(lb, ob, dflag, ob_wire_col, false);
 						GPU_end_object_materials();
 					}
@@ -5622,7 +5597,7 @@ static bool drawDispList(
 	ensure_curve_cache(eval_ctx, scene, base->object);
 #endif
 
-	if (drawCurveDerivedMesh(eval_ctx, scene, view_layer, v3d, rv3d, base, dt) == false) {
+	if (drawCurveDerivedMesh(scene, view_layer, v3d, rv3d, base, dt) == false) {
 		retval = false;
 	}
 	else {
@@ -5638,7 +5613,7 @@ static bool drawDispList(
 
 		glFrontFace(mode);
 
-		retval = drawDispList_nobackface(eval_ctx, scene, view_layer, v3d, rv3d, base, dt, dflag, ob_wire_col);
+		retval = drawDispList_nobackface(scene, view_layer, v3d, rv3d, base, dt, dflag, ob_wire_col);
 
 		if (mode != GL_CCW) {
 			glFrontFace(GL_CCW);
@@ -5962,7 +5937,7 @@ static void draw_new_particle_system(
 	if (pars == NULL) return;
 
 	/* don't draw normal paths in edit mode */
-	if (psys_in_edit_mode(eval_ctx, eval_ctx->view_layer, psys) && (pset->flag & PE_DRAW_PART) == 0)
+	if (psys_in_edit_mode(eval_ctx->view_layer, psys) && (pset->flag & PE_DRAW_PART) == 0)
 		return;
 
 	if (part->draw_as == PART_DRAW_REND)
@@ -6619,7 +6594,7 @@ static void draw_new_particle_system(
 }
 
 static void draw_update_ptcache_edit(
-        const EvaluationContext *eval_ctx, Scene *scene, ViewLayer *view_layer, Object *ob, PTCacheEdit *edit)
+        const EvaluationContext *eval_ctx, Scene *scene, Object *ob, PTCacheEdit *edit)
 {
 	if (edit->psys && edit->psys->flag & PSYS_HAIR_UPDATED)
 		PE_update_object(eval_ctx, scene, ob, 0);
@@ -8321,7 +8296,7 @@ static void draw_object_selected_outline(
 		if (has_faces && ED_view3d_boundbox_clip(rv3d, ob->bb)) {
 			glLineWidth(UI_GetThemeValuef(TH_OUTLINE_WIDTH) * 2.0f);
 			if (dm) {
-				draw_mesh_object_outline(eval_ctx, v3d, ob, dm, ob_wire_col);
+				draw_mesh_object_outline(v3d, ob, dm, ob_wire_col);
 			}
 			else {
 				/* only draw 'solid' parts of the display list as wire. */
@@ -8338,7 +8313,7 @@ static void draw_object_selected_outline(
 		}
 	}
 	else if (ob->type == OB_ARMATURE) {
-		if (!(eval_ctx->object_mode & OB_MODE_POSE && base == view_layer->basact)) {
+		if (!(ob->mode & OB_MODE_POSE && base == view_layer->basact)) {
 			glLineWidth(UI_GetThemeValuef(TH_OUTLINE_WIDTH) * 2.0f);
 			draw_armature(eval_ctx, scene, view_layer, v3d, ar, base, OB_WIRE, 0, ob_wire_col, true);
 		}
@@ -8449,13 +8424,11 @@ static void draw_rigid_body_pivot(bRigidBodyJointConstraint *data,
 	immUnbindProgram();
 }
 
-void draw_object_wire_color(
-        const EvaluationContext *eval_ctx, ViewLayer *view_layer,
-        Base *base, unsigned char r_ob_wire_col[4])
+void draw_object_wire_color(ViewLayer *view_layer, Base *base, unsigned char r_ob_wire_col[4])
 {
 	Object *ob = base->object;
 	int colindex = 0;
-	const bool is_edit = (eval_ctx->object_mode & OB_MODE_EDIT) != 0;
+	const bool is_edit = (ob->mode & OB_MODE_EDIT) != 0;
 	/* confusing logic here, there are 2 methods of setting the color
 	 * 'colortab[colindex]' and 'theme_id', colindex overrides theme_id.
 	 *
@@ -8463,7 +8436,7 @@ void draw_object_wire_color(
 	int theme_id = is_edit ? TH_WIRE_EDIT : TH_WIRE;
 	int theme_shade = 0;
 
-	if (((eval_ctx->object_mode & OB_MODE_EDIT) == 0) &&
+	if (((ob->mode & OB_MODE_EDIT) == 0) &&
 	    (G.moving & G_TRANSFORM_OBJ) &&
 	    ((base->flag & BASE_SELECTED) || (base->flag_legacy & BA_WAS_SEL)))
 	{
@@ -8517,12 +8490,10 @@ void draw_object_wire_color(
 	r_ob_wire_col[3] = 255;
 }
 
-static void draw_object_matcap_check(
-        const EvaluationContext *eval_ctx, View3D *v3d, Object *ob)
+static void draw_object_matcap_check(View3D *v3d, Object *ob)
 {
 	/* fixed rule, active object draws as matcap */
-	BLI_assert((eval_ctx->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)) == 0);
-	UNUSED_VARS_NDEBUG(eval_ctx);
+	BLI_assert((ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)) == 0);
 	(void)ob;
 
 	if (v3d->defmaterial == NULL) {
@@ -8608,9 +8579,10 @@ void draw_object(
 	unsigned char _ob_wire_col[4];            /* dont initialize this */
 	const unsigned char *ob_wire_col = NULL;  /* dont initialize this, use NULL crashes as a way to find invalid use */
 	bool zbufoff = false, is_paint = false, empty_object = false;
-	const bool is_obact = (ob == OBACT(view_layer));
+	Object *ob_active = OBACT(view_layer);
+	const bool is_obact = (ob == ob_active);
 	/* this could be moved to a 'dflag'. */
-	const bool is_obedit = (is_obact && (ob == OBEDIT_FROM_EVAL_CTX(eval_ctx)));
+	const bool is_obedit = (is_obact && (ob == OBEDIT_FROM_OBACT(ob_active)));
 	const bool render_override = (v3d->flag2 & V3D_RENDER_OVERRIDE) != 0;
 	const bool is_picking = (G.f & G_PICKSEL) != 0;
 	const bool has_particles = (ob->particlesystem.first != NULL);
@@ -8636,7 +8608,7 @@ void draw_object(
 			return;
 		}
 
-		if (eval_ctx->object_mode == OB_MODE_OBJECT) {
+		if (ob->mode == OB_MODE_OBJECT) {
 			ParticleSystem *psys;
 
 			skip_object = render_override;
@@ -8676,7 +8648,7 @@ void draw_object(
 	/* xray delay? */
 	if ((dflag & DRAW_PICKING) == 0 && (base->flag_legacy & OB_FROMDUPLI) == 0 && (v3d->flag2 & V3D_RENDER_SHADOW) == 0) {
 		/* don't do xray in particle mode, need the z-buffer */
-		if (!(eval_ctx->object_mode & OB_MODE_PARTICLE_EDIT)) {
+		if (!(ob->mode & OB_MODE_PARTICLE_EDIT)) {
 			/* xray and transp are set when it is drawing the 2nd/3rd pass */
 			if (!v3d->xray && !v3d->transp && (ob->dtx & OB_DRAWXRAY) && !(ob->dtx & OB_DRAWTRANSP)) {
 				ED_view3d_after_add(&v3d->afterdraw_xray, base, dflag);
@@ -8727,7 +8699,7 @@ void draw_object(
 
 		ED_view3d_project_base(ar, base);
 
-		draw_object_wire_color(eval_ctx, view_layer, base, _ob_wire_col);
+		draw_object_wire_color(view_layer, base, _ob_wire_col);
 		ob_wire_col = _ob_wire_col;
 
 		//glColor3ubv(ob_wire_col);
@@ -8743,14 +8715,14 @@ void draw_object(
 
 	/* faceselect exception: also draw solid when (dt == wire), except in editmode */
 	if (is_obact) {
-		if (eval_ctx->object_mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)) {
+		if (ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)) {
 			if (ob->type == OB_MESH) {
 				if (dt < OB_SOLID) {
 					zbufoff = true;
 					dt = OB_SOLID;
 				}
 
-				if (eval_ctx->object_mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT)) {
+				if (ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT)) {
 					dt = OB_PAINT;
 				}
 
@@ -8766,13 +8738,13 @@ void draw_object(
 	    (is_paint == false && is_picking == false) &&
 	    ((v3d->flag2 & V3D_RENDER_SHADOW) == 0))
 	{
-		draw_object_matcap_check(eval_ctx, v3d, ob);
+		draw_object_matcap_check(v3d, ob);
 	}
 
 	/* draw-extra supported for boundbox drawmode too */
 	if (dt >= OB_BOUNDBOX) {
 		dtx = ob->dtx;
-		if (eval_ctx->object_mode & OB_MODE_EDIT) {
+		if (ob->mode & OB_MODE_EDIT) {
 			/* the only 2 extra drawtypes alowed in editmode */
 			dtx = dtx & (OB_DRAWWIRE | OB_TEXSPACE);
 		}
@@ -8781,7 +8753,7 @@ void draw_object(
 	if (!skip_object) {
 		/* draw outline for selected objects, mesh does itself */
 		if ((v3d->flag & V3D_SELECT_OUTLINE) && !render_override && ob->type != OB_MESH) {
-			if (dt > OB_WIRE && (eval_ctx->object_mode & OB_MODE_EDIT) == 0 && (dflag & DRAW_SCENESET) == 0) {
+			if (dt > OB_WIRE && (ob->mode & OB_MODE_EDIT) == 0 && (dflag & DRAW_SCENESET) == 0) {
 				if (!(ob->dtx & OB_DRAWWIRE) && (base->flag & BASE_SELECTED) && !(dflag & (DRAW_PICKING | DRAW_CONSTCOLOR))) {
 					draw_object_selected_outline(eval_ctx, scene, view_layer, v3d, ar, base, ob_wire_col);
 				}
@@ -8889,7 +8861,7 @@ void draw_object(
 			case OB_LATTICE:
 				if (!render_override) {
 					/* Do not allow boundbox in edit nor pose mode! */
-					if ((dt == OB_BOUNDBOX) && (eval_ctx->object_mode & OB_MODE_EDIT))
+					if ((dt == OB_BOUNDBOX) && (ob->mode & OB_MODE_EDIT))
 						dt = OB_WIRE;
 					if (dt == OB_BOUNDBOX) {
 						draw_bounding_volume(ob, ob->boundtype, ob_wire_col);
@@ -8905,7 +8877,7 @@ void draw_object(
 			case OB_ARMATURE:
 				if (!render_override) {
 					/* Do not allow boundbox in edit nor pose mode! */
-					if ((dt == OB_BOUNDBOX) && (eval_ctx->object_mode & (OB_MODE_EDIT | OB_MODE_POSE)))
+					if ((dt == OB_BOUNDBOX) && (ob->mode & (OB_MODE_EDIT | OB_MODE_POSE)))
 						dt = OB_WIRE;
 					if (dt == OB_BOUNDBOX) {
 						draw_bounding_volume(ob, ob->boundtype, ob_wire_col);
@@ -8973,10 +8945,10 @@ afterdraw:
 
 		for (psys = ob->particlesystem.first; psys; psys = psys->next) {
 			/* run this so that possible child particles get cached */
-			if (eval_ctx->object_mode & OB_MODE_PARTICLE_EDIT && is_obact) {
+			if (ob->mode & OB_MODE_PARTICLE_EDIT && is_obact) {
 				PTCacheEdit *edit = PE_create_current(eval_ctx, scene, ob);
 				if (edit && edit->psys == psys)
-					draw_update_ptcache_edit(eval_ctx, scene, view_layer, ob, edit);
+					draw_update_ptcache_edit(eval_ctx, scene, ob, edit);
 			}
 
 			draw_new_particle_system(eval_ctx, scene, v3d, rv3d, base, psys, dt, dflag);
@@ -8994,11 +8966,11 @@ afterdraw:
 	    (is_obedit == false))
 	{
 
-		if (eval_ctx->object_mode & OB_MODE_PARTICLE_EDIT && is_obact) {
+		if (ob->mode & OB_MODE_PARTICLE_EDIT && is_obact) {
 			PTCacheEdit *edit = PE_create_current(eval_ctx, scene, ob);
 			if (edit) {
 				gpuLoadMatrix(rv3d->viewmat);
-				draw_update_ptcache_edit(eval_ctx, scene, view_layer, ob, edit);
+				draw_update_ptcache_edit(eval_ctx, scene, ob, edit);
 				draw_ptcache_edit(scene, v3d, edit);
 				gpuMultMatrix(ob->obmat);
 			}
@@ -9095,7 +9067,7 @@ afterdraw:
 			}
 		}
 
-		if ((ob->gameflag & OB_BOUNDS) && (eval_ctx->object_mode == OB_MODE_OBJECT)) {
+		if ((ob->gameflag & OB_BOUNDS) && (ob->mode == OB_MODE_OBJECT)) {
 			if (ob->boundtype != ob->collision_boundtype || (dtx & OB_DRAWBOUNDOX) == 0) {
 				setlinestyle(2);
 				draw_bounding_volume(ob, ob->collision_boundtype, ob_wire_col);
@@ -9191,7 +9163,7 @@ afterdraw:
 	}
 
 	/* object centers, need to be drawn in viewmat space for speed, but OK for picking select */
-	if (!is_obact || !(eval_ctx->object_mode & OB_MODE_ALL_PAINT)) {
+	if (!is_obact || !(ob->mode & OB_MODE_ALL_PAINT)) {
 		int do_draw_center = -1; /* defines below are zero or positive... */
 
 		if (render_override) {
@@ -9791,7 +9763,7 @@ void draw_object_backbufsel(
 
 	switch (ob->type) {
 		case OB_MESH:
-			if (eval_ctx->object_mode & OB_MODE_EDIT) {
+			if (ob->mode & OB_MODE_EDIT) {
 				Mesh *me = ob->data;
 				BMEditMesh *em = me->edit_btmesh;
 
@@ -9837,7 +9809,7 @@ void draw_object_backbufsel(
 				Mesh *me = ob->data;
 				if ((me->editflag & ME_EDIT_PAINT_VERT_SEL) &&
 				    /* currently vertex select supports weight paint and vertex paint*/
-				    ((eval_ctx->object_mode & OB_MODE_WEIGHT_PAINT) || (eval_ctx->object_mode & OB_MODE_VERTEX_PAINT)))
+				    ((ob->mode & OB_MODE_WEIGHT_PAINT) || (ob->mode & OB_MODE_VERTEX_PAINT)))
 				{
 					bbs_mesh_solid_verts(eval_ctx, scene, ob);
 				}
@@ -9866,7 +9838,7 @@ static void draw_object_mesh_instance(
 	Mesh *me = ob->data;
 	DerivedMesh *dm = NULL, *edm = NULL;
 	
-	if (eval_ctx->object_mode & OB_MODE_EDIT) {
+	if (ob->mode & OB_MODE_EDIT) {
 		edm = editbmesh_get_derived_base(ob, me->edit_btmesh, CD_MASK_BAREMESH);
 		DM_update_materials(edm, ob);
 	}
@@ -9884,11 +9856,11 @@ static void draw_object_mesh_instance(
 	}
 	else {
 		if (outline)
-			draw_mesh_object_outline(eval_ctx, v3d, ob, dm ? dm : edm, ob_wire_col);
+			draw_mesh_object_outline(v3d, ob, dm ? dm : edm, ob_wire_col);
 
 		if (dm) {
-			bool glsl = draw_glsl_material(eval_ctx, scene, view_layer, ob, v3d, dt);
-			GPU_begin_object_materials(v3d, rv3d, scene, view_layer, ob, glsl, eval_ctx->object_mode, NULL);
+			bool glsl = draw_glsl_material(scene, view_layer, ob, v3d, dt);
+			GPU_begin_object_materials(v3d, rv3d, scene, view_layer, ob, glsl, NULL);
 		}
 		
 		glFrontFace((ob->transflag & OB_NEG_SCALE) ? GL_CW : GL_CCW);
diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c
index 89e84052c9bd8e9a050c7c2b5ee97ee3ab08e5a2..abfbccdcc2712aded46b4d9ad96e8d6b4d243799 100644
--- a/source/blender/editors/space_view3d/space_view3d.c
+++ b/source/blender/editors/space_view3d/space_view3d.c
@@ -59,7 +59,6 @@
 #include "ED_screen.h"
 #include "ED_transform.h"
 
-#include "GPU_compositing.h"
 #include "GPU_framebuffer.h"
 #include "GPU_material.h"
 #include "GPU_viewport.h"
@@ -568,11 +567,6 @@ static void view3d_main_region_exit(wmWindowManager *wm, ARegion *ar)
 		rv3d->gpuoffscreen = NULL;
 	}
 	
-	if (rv3d->compositor) {
-		GPU_fx_compositor_destroy(rv3d->compositor);
-		rv3d->compositor = NULL;
-	}
-
 	if (rv3d->viewport) {
 		DRW_opengl_context_enable();
 		GPU_viewport_free(rv3d->viewport);
@@ -756,9 +750,6 @@ static void view3d_main_region_free(ARegion *ar)
 		if (rv3d->gpuoffscreen) {
 			GPU_offscreen_free(rv3d->gpuoffscreen);
 		}
-		if (rv3d->compositor) {
-			GPU_fx_compositor_destroy(rv3d->compositor);
-		}
 		if (rv3d->viewport) {
 			DRW_opengl_context_enable();
 			GPU_viewport_free(rv3d->viewport);
@@ -919,7 +910,9 @@ static void view3d_main_region_listener(
 				case ND_SELECT:
 				{
 					WM_manipulatormap_tag_refresh(mmap);
-					Object *obedit = OBEDIT_FROM_WINDOW(wmn->window);
+
+					ViewLayer *view_layer = WM_window_get_active_view_layer(wmn->window);
+					Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
 					if (obedit) {
 						/* TODO(sergey): Notifiers shouldn't really be doing DEG tags. */
 						DEG_id_tag_update((ID *)obedit->data, DEG_TAG_SELECT_UPDATE);
@@ -1136,8 +1129,9 @@ static void view3d_main_region_message_subscribe(
 /* concept is to retrieve cursor type context-less */
 static void view3d_main_region_cursor(wmWindow *win, ScrArea *UNUSED(sa), ARegion *UNUSED(ar))
 {
-	WorkSpace *workspace = WM_window_get_active_workspace(win);
-	if (workspace->object_mode & OB_MODE_EDIT) {
+	ViewLayer *view_layer = WM_window_get_active_view_layer(win);
+	Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
+	if (obedit) {
 		WM_cursor_set(win, CURSOR_EDIT);
 	}
 	else {
@@ -1389,9 +1383,9 @@ static int view3d_context(const bContext *C, const char *member, bContextDataRes
 		Scene *scene = CTX_data_scene(C);
 		ViewLayer *view_layer = CTX_data_view_layer(C);
 		if (view_layer->basact) {
-			const WorkSpace *workspace = CTX_wm_workspace(C);
+			Object *ob = view_layer->basact->object;
 			/* if hidden but in edit mode, we still display, can happen with animation */
-			if ((view_layer->basact->flag & BASE_VISIBLED) != 0 || (workspace->object_mode & OB_MODE_EDIT)) {
+			if ((view_layer->basact->flag & BASE_VISIBLED) != 0 || (ob->mode & OB_MODE_EDIT)) {
 				CTX_data_pointer_set(result, &scene->id, &RNA_ObjectBase, view_layer->basact);
 			}
 		}
@@ -1401,10 +1395,9 @@ static int view3d_context(const bContext *C, const char *member, bContextDataRes
 	else if (CTX_data_equals(member, "active_object")) {
 		ViewLayer *view_layer = CTX_data_view_layer(C);
 		if (view_layer->basact) {
-			const WorkSpace *workspace = CTX_wm_workspace(C);
 			Object *ob = view_layer->basact->object;
 			/* if hidden but in edit mode, we still display, can happen with animation */
-			if ((view_layer->basact->flag & BASE_VISIBLED) != 0 || (workspace->object_mode & OB_MODE_EDIT) != 0) {
+			if ((view_layer->basact->flag & BASE_VISIBLED) != 0 || (ob->mode & OB_MODE_EDIT) != 0) {
 				CTX_data_id_pointer_set(result, &ob->id);
 			}
 		}
diff --git a/source/blender/editors/space_view3d/view3d_buttons.c b/source/blender/editors/space_view3d/view3d_buttons.c
index bd62a4fc3bf67d358d2da023afdad531c267e412..644a6956e54433b228c697977d1df0f782e053b7 100644
--- a/source/blender/editors/space_view3d/view3d_buttons.c
+++ b/source/blender/editors/space_view3d/view3d_buttons.c
@@ -787,12 +787,10 @@ static void do_view3d_vgroup_buttons(bContext *C, void *UNUSED(arg), int event)
 
 static int view3d_panel_vgroup_poll(const bContext *C, PanelType *UNUSED(pt))
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	ViewLayer *view_layer = CTX_data_view_layer(C);
 	Object *ob = OBACT(view_layer);
-
 	if (ob && (BKE_object_is_in_editmode_vgroup(ob) ||
-	           BKE_object_is_in_wpaint_select_vert(ob, workspace->object_mode)))
+	           BKE_object_is_in_wpaint_select_vert(ob)))
 	{
 		MDeformVert *dvert_act = ED_mesh_active_dvert_get_only(ob);
 		if (dvert_act) {
@@ -1130,7 +1128,6 @@ static int view3d_panel_transform_poll(const bContext *C, PanelType *UNUSED(pt))
 static void view3d_panel_transform(const bContext *C, Panel *pa)
 {
 	uiBlock *block;
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Scene *scene = CTX_data_scene(C);
 	ViewLayer *view_layer = CTX_data_view_layer(C);
 	Object *obedit = CTX_data_edit_object(C);
@@ -1155,7 +1152,7 @@ static void view3d_panel_transform(const bContext *C, Panel *pa)
 			v3d_editvertex_buts(col, v3d, ob, lim);
 		}
 	}
-	else if (workspace->object_mode & OB_MODE_POSE) {
+	else if (ob->mode & OB_MODE_POSE) {
 		v3d_posearmature_buts(col, ob);
 	}
 	else {
diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c
index b14128ab400728b2ade70c28db3208e1913ed968..f63bcb1571d01c7388311dc5009935936d56dd77 100644
--- a/source/blender/editors/space_view3d/view3d_draw.c
+++ b/source/blender/editors/space_view3d/view3d_draw.c
@@ -78,7 +78,6 @@
 #include "GPU_immediate_util.h"
 #include "GPU_material.h"
 #include "GPU_viewport.h"
-#include "GPU_compositing.h"
 
 #include "MEM_guardedalloc.h"
 
@@ -752,7 +751,7 @@ void ED_view3d_draw_depth(
 	else
 #endif /* WITH_OPENGL_LEGACY */
 	{
-		DRW_draw_depth_loop(graph, ar, v3d, eval_ctx->object_mode);
+		DRW_draw_depth_loop(graph, ar, v3d);
 	}
 
 	if (rv3d->rflag & RV3D_CLIPPING) {
@@ -1326,7 +1325,7 @@ float ED_view3d_grid_scale(Scene *scene, View3D *v3d, const char **grid_unit)
 	return v3d->grid * ED_scene_grid_scale(scene, grid_unit);
 }
 
-static bool is_cursor_visible(const EvaluationContext *eval_ctx, Scene *scene, ViewLayer *view_layer)
+static bool is_cursor_visible(Scene *scene, ViewLayer *view_layer)
 {
 	if (U.app_flag & USER_APP_VIEW3D_HIDE_CURSOR) {
 		return false;
@@ -1335,16 +1334,16 @@ static bool is_cursor_visible(const EvaluationContext *eval_ctx, Scene *scene, V
 	Object *ob = OBACT(view_layer);
 
 	/* don't draw cursor in paint modes, but with a few exceptions */
-	if (ob && eval_ctx->object_mode & OB_MODE_ALL_PAINT) {
+	if (ob && ob->mode & OB_MODE_ALL_PAINT) {
 		/* exception: object is in weight paint and has deforming armature in pose mode */
-		if (eval_ctx->object_mode & OB_MODE_WEIGHT_PAINT) {
+		if (ob->mode & OB_MODE_WEIGHT_PAINT) {
 			if (BKE_object_pose_armature_get(ob) != NULL) {
 				return true;
 			}
 		}
 		/* exception: object in texture paint mode, clone brush, use_clone_layer disabled */
-		else if (eval_ctx->object_mode & OB_MODE_TEXTURE_PAINT) {
-			const Paint *p = BKE_paint_get_active(scene, view_layer, eval_ctx->object_mode);
+		else if (ob->mode & OB_MODE_TEXTURE_PAINT) {
+			const Paint *p = BKE_paint_get_active(scene, view_layer);
 
 			if (p && p->brush && p->brush->imagepaint_tool == PAINT_TOOL_CLONE) {
 				if ((scene->toolsettings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_CLONE) == 0) {
@@ -1469,6 +1468,7 @@ static void draw_view_axis(RegionView3D *rv3d, const rcti *rect)
 
 		immAttrib4ubv(col, axis_col[i]);
 		immVertex2f(pos, startx, starty);
+		immAttrib4ubv(col, axis_col[i]);
 		immVertex2fv(pos, axis_pos[i]);
 	}
 
@@ -1718,8 +1718,7 @@ static void draw_viewport_name(ARegion *ar, View3D *v3d, const rcti *rect)
  * framenum, object name, bone name (if available), marker name (if available)
  */
 
-static void draw_selected_name(
-        Scene *scene, Object *ob, const eObjectMode object_mode, const rcti *rect)
+static void draw_selected_name(Scene *scene, Object *ob, rcti *rect)
 {
 	const int cfra = CFRA;
 	const char *msg_pin = " (Pinned)";
@@ -1761,7 +1760,7 @@ static void draw_selected_name(
 					s += BLI_strcpy_rlen(s, arm->act_edbone->name);
 				}
 			}
-			else if (object_mode & OB_MODE_POSE) {
+			else if (ob->mode & OB_MODE_POSE) {
 				if (arm->act_bone) {
 
 					if (arm->act_bone->layer & arm->layer) {
@@ -1774,9 +1773,9 @@ static void draw_selected_name(
 		else if (ELEM(ob->type, OB_MESH, OB_LATTICE, OB_CURVE)) {
 			/* try to display active bone and active shapekey too (if they exist) */
 
-			if (ob->type == OB_MESH && object_mode & OB_MODE_WEIGHT_PAINT) {
+			if (ob->type == OB_MESH && ob->mode & OB_MODE_WEIGHT_PAINT) {
 				Object *armobj = BKE_object_pose_armature_get(ob);
-				if (armobj) {
+				if (armobj  && armobj->mode & OB_MODE_POSE) {
 					bArmature *arm = armobj->data;
 					if (arm->act_bone) {
 						if (arm->act_bone->layer & arm->layer) {
@@ -1851,6 +1850,8 @@ void view3d_draw_region_info(const bContext *C, ARegion *ar, const int offset)
 	view3d_draw_border(C, ar);
 	view3d_draw_grease_pencil(C);
 
+	BLF_batch_draw_begin();
+
 	if (U.uiflag & USER_SHOW_ROTVIEWICON) {
 		draw_view_axis(rv3d, &rect);
 	}
@@ -1863,11 +1864,11 @@ void view3d_draw_region_info(const bContext *C, ARegion *ar, const int offset)
 	}
 
 	if (U.uiflag & USER_DRAWVIEWINFO) {
-		const WorkSpace *workspace = CTX_wm_workspace(C);
 		ViewLayer *view_layer = CTX_data_view_layer(C);
 		Object *ob = OBACT(view_layer);
-		draw_selected_name(scene, ob, workspace->object_mode, &rect);
+		draw_selected_name(scene, ob, &rect);
 	}
+
 #if 0 /* TODO */
 	if (grid_unit) { /* draw below the viewport name */
 		char numstr[32] = "";
@@ -1882,6 +1883,7 @@ void view3d_draw_region_info(const bContext *C, ARegion *ar, const int offset)
 		                       numstr[0] ? numstr : grid_unit, sizeof(numstr));
 	}
 #endif
+	BLF_batch_draw_end();
 }
 
 static void view3d_draw_view(const bContext *C, ARegion *ar)
@@ -1960,7 +1962,7 @@ void ED_view3d_draw_offscreen_init(const EvaluationContext *eval_ctx, Scene *sce
 	RenderEngineType *engine_type = eval_ctx->engine_type;
 	if (engine_type->flag & RE_USE_LEGACY_PIPELINE) {
 		/* shadow buffers, before we setup matrices */
-		if (draw_glsl_material(eval_ctx, scene, view_layer, NULL, v3d, v3d->drawtype)) {
+		if (draw_glsl_material(scene, view_layer, NULL, v3d, v3d->drawtype)) {
 			VP_deprecated_gpu_update_lamps_shadows_world(eval_ctx, scene, v3d);
 		}
 	}
@@ -1988,11 +1990,10 @@ void ED_view3d_draw_offscreen(
         const EvaluationContext *eval_ctx, Scene *scene, ViewLayer *view_layer,
         View3D *v3d, ARegion *ar, int winx, int winy,
         float viewmat[4][4], float winmat[4][4],
-        bool do_bgpic, bool do_sky, bool is_persp, const char *viewname,
-        GPUFX *fx, GPUFXSettings *fx_settings,
+        bool do_bgpic, bool do_sky, bool UNUSED(is_persp), const char *viewname,
+        GPUFXSettings *UNUSED(fx_settings),
         GPUOffScreen *ofs, GPUViewport *viewport)
 {
-	bool do_compositing = false;
 	RegionView3D *rv3d = ar->regiondata;
 
 	/* set temporary new size */
@@ -2035,30 +2036,7 @@ void ED_view3d_draw_offscreen(
 	/* main drawing call */
 	RenderEngineType *engine_type = eval_ctx->engine_type;
 	if (engine_type->flag & RE_USE_LEGACY_PIPELINE) {
-
-		/* framebuffer fx needed, we need to draw offscreen first */
-		if (v3d->fx_settings.fx_flag && fx) {
-			GPUSSAOSettings *ssao = NULL;
-
-			if (v3d->drawtype < OB_SOLID) {
-				ssao = v3d->fx_settings.ssao;
-				v3d->fx_settings.ssao = NULL;
-			}
-
-			do_compositing = GPU_fx_compositor_initialize_passes(fx, &ar->winrct, NULL, fx_settings);
-
-			if (ssao)
-				v3d->fx_settings.ssao = ssao;
-		}
-
-		VP_deprecated_view3d_draw_objects(NULL, eval_ctx, scene, v3d, ar, NULL, do_bgpic, true, do_compositing ? fx : NULL);
-
-		/* post process */
-		if (do_compositing) {
-			if (!winmat)
-				is_persp = rv3d->is_persp;
-			GPU_fx_do_composite_pass(fx, winmat, is_persp, scene, ofs);
-		}
+		VP_deprecated_view3d_draw_objects(NULL, eval_ctx, scene, v3d, ar, NULL, do_bgpic, true);
 
 		if ((v3d->flag2 & V3D_RENDER_SHADOW) == 0) {
 			/* draw grease-pencil stuff */
@@ -2075,7 +2053,7 @@ void ED_view3d_draw_offscreen(
 	}
 	else {
 		DRW_draw_render_loop_offscreen(
-		        eval_ctx->depsgraph, eval_ctx->engine_type, ar, v3d, eval_ctx->object_mode,
+		        depsgraph, eval_ctx->engine_type, ar, v3d,
 		        do_sky, ofs, viewport);
 	}
 
@@ -2104,7 +2082,7 @@ ImBuf *ED_view3d_draw_offscreen_imbuf(
         unsigned int flag, unsigned int draw_flags,
         int alpha_mode, int samples, const char *viewname,
         /* output vars */
-        GPUFX *fx, GPUOffScreen *ofs, char err_out[256])
+        GPUOffScreen *ofs, char err_out[256])
 {
 	const Depsgraph *depsgraph = eval_ctx->depsgraph;
 	RegionView3D *rv3d = ar->regiondata;
@@ -2178,7 +2156,7 @@ ImBuf *ED_view3d_draw_offscreen_imbuf(
 		ED_view3d_draw_offscreen(
 		        eval_ctx, scene, view_layer, v3d, ar, sizex, sizey, NULL, winmat,
 		        draw_background, draw_sky, !is_ortho, viewname,
-		        fx, &fx_settings, ofs, NULL);
+		        &fx_settings, ofs, NULL);
 
 		if (ibuf->rect_float) {
 			GPU_offscreen_read_pixels(ofs, GL_FLOAT, ibuf->rect_float);
@@ -2202,7 +2180,7 @@ ImBuf *ED_view3d_draw_offscreen_imbuf(
 		ED_view3d_draw_offscreen(
 		        eval_ctx, scene, view_layer, v3d, ar, sizex, sizey, NULL, winmat,
 		        draw_background, draw_sky, !is_ortho, viewname,
-		        fx, &fx_settings, ofs, viewport);
+		        &fx_settings, ofs, viewport);
 		GPU_offscreen_read_pixels(ofs, GL_FLOAT, accum_buffer);
 
 		/* skip the first sample */
@@ -2216,7 +2194,7 @@ ImBuf *ED_view3d_draw_offscreen_imbuf(
 			ED_view3d_draw_offscreen(
 			        eval_ctx, scene, view_layer, v3d, ar, sizex, sizey, NULL, winmat_jitter,
 			        draw_background, draw_sky, !is_ortho, viewname,
-			        fx, &fx_settings, ofs, viewport);
+			        &fx_settings, ofs, viewport);
 			GPU_offscreen_read_pixels(ofs, GL_FLOAT, rect_temp);
 
 			unsigned int i = sizex * sizey * 4;
@@ -2281,7 +2259,7 @@ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(
         Object *camera, int width, int height,
         unsigned int flag, unsigned int draw_flags, int drawtype,
         int alpha_mode, int samples, const char *viewname,
-        GPUFX *fx, GPUOffScreen *ofs, char err_out[256])
+        GPUOffScreen *ofs, char err_out[256])
 {
 	View3D v3d = {NULL};
 	ARegion ar = {NULL};
@@ -2340,7 +2318,7 @@ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(
 
 	return ED_view3d_draw_offscreen_imbuf(
 	        eval_ctx, scene, view_layer, &v3d, &ar, width, height, flag,
-	        draw_flags, alpha_mode, samples, viewname, fx, ofs, err_out);
+	        draw_flags, alpha_mode, samples, viewname, ofs, err_out);
 }
 
 /** \} */
@@ -2355,10 +2333,9 @@ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(
  *
  * \{ */
 
-void VP_legacy_drawcursor(
-        const EvaluationContext *eval_ctx, Scene *scene, ViewLayer *view_layer, ARegion *ar, View3D *v3d)
+void VP_legacy_drawcursor(Scene *scene, ViewLayer *view_layer, ARegion *ar, View3D *v3d)
 {
-	if (is_cursor_visible(eval_ctx, scene, view_layer)) {
+	if (is_cursor_visible(scene, view_layer)) {
 		drawcursor(scene, ar, v3d);
 	}
 }
@@ -2373,9 +2350,9 @@ void VP_legacy_draw_viewport_name(ARegion *ar, View3D *v3d, const rcti *rect)
 	draw_viewport_name(ar, v3d, rect);
 }
 
-void VP_legacy_draw_selected_name(Scene *scene, Object *ob, eObjectMode object_mode, const rcti *rect)
+void VP_legacy_draw_selected_name(Scene *scene, Object *ob, rcti *rect)
 {
-	draw_selected_name(scene, ob, object_mode, rect);
+	draw_selected_name(scene, ob, rect);
 }
 
 void VP_legacy_drawgrid(UnitSettings *unit, ARegion *ar, View3D *v3d, const char **grid_unit)
diff --git a/source/blender/editors/space_view3d/view3d_draw_legacy.c b/source/blender/editors/space_view3d/view3d_draw_legacy.c
index 775169e0b454f94dc6e72d8a3a3f62204ab3d726..f1c251891b0074f622d6924d145bc5ee5c68a650 100644
--- a/source/blender/editors/space_view3d/view3d_draw_legacy.c
+++ b/source/blender/editors/space_view3d/view3d_draw_legacy.c
@@ -100,7 +100,6 @@
 #include "GPU_framebuffer.h"
 #include "GPU_lamp.h"
 #include "GPU_material.h"
-#include "GPU_compositing.h"
 #include "GPU_extensions.h"
 #include "GPU_immediate.h"
 #include "GPU_immediate_util.h"
@@ -220,23 +219,23 @@ static void backdrawview3d(
 
 	BLI_assert(ar->regiontype == RGN_TYPE_WINDOW);
 
-	if (obact && (eval_ctx->object_mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT) ||
-	             BKE_paint_select_face_test(obact, eval_ctx->object_mode)))
+	if (obact && (obact->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT) ||
+	             BKE_paint_select_face_test(obact)))
 	{
 		/* do nothing */
 	}
 	/* texture paint mode sampling */
-	else if (obact && (eval_ctx->object_mode & OB_MODE_TEXTURE_PAINT) &&
+	else if (obact && (obact->mode & OB_MODE_TEXTURE_PAINT) &&
 	         (v3d->drawtype > OB_WIRE))
 	{
 		/* do nothing */
 	}
-	else if ((obact && (eval_ctx->object_mode & OB_MODE_PARTICLE_EDIT)) &&
+	else if ((obact && (obact->mode & OB_MODE_PARTICLE_EDIT)) &&
 	         V3D_IS_ZBUF(v3d))
 	{
 		/* do nothing */
 	}
-	else if ((eval_ctx->object_mode & OB_MODE_EDIT) && (obedit != NULL) &&
+	else if ((obedit && (obedit->mode & OB_MODE_EDIT)) &&
 	         V3D_IS_ZBUF(v3d))
 	{
 		/* do nothing */
@@ -1425,7 +1424,7 @@ static void gpu_update_lamps_shadows_world(const EvaluationContext *eval_ctx, Sc
 		ED_view3d_draw_offscreen(
 		            eval_ctx, scene, eval_ctx->view_layer, v3d, &ar, winsize, winsize, viewmat, winmat,
 		            false, false, true,
-		            NULL, NULL, NULL, NULL, NULL);
+		            NULL, NULL, NULL, NULL);
 		GPU_lamp_shadow_buffer_unbind(shadow->lamp);
 		
 		v3d->drawtype = drawtype;
@@ -1499,19 +1498,18 @@ static void view3d_draw_objects(
         const EvaluationContext *eval_ctx,
         Scene *scene, View3D *v3d, ARegion *ar,
         const char **grid_unit,
-        const bool do_bgpic, const bool draw_offscreen, GPUFX *fx)
+        const bool do_bgpic, const bool draw_offscreen)
 {
 	ViewLayer *view_layer = C ? CTX_data_view_layer(C) : BKE_view_layer_from_scene_get(scene);
 	Depsgraph *depsgraph = CTX_data_depsgraph(C);
 	RegionView3D *rv3d = ar->regiondata;
 	Base *base;
-	Object *obedit = OBEDIT_FROM_EVAL_CTX(eval_ctx);
+	Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
 	const bool do_camera_frame = !draw_offscreen;
 	const bool draw_grids = !draw_offscreen && (v3d->flag2 & V3D_RENDER_OVERRIDE) == 0;
 	const bool draw_floor = (rv3d->view == RV3D_VIEW_USER) || (rv3d->persp != RV3D_ORTHO);
 	/* only draw grids after in solid modes, else it hovers over mesh wires */
-	const bool draw_grids_after = draw_grids && draw_floor && (v3d->drawtype > OB_WIRE) && fx;
-	bool do_composite_xray = false;
+	const bool draw_grids_after = draw_grids && draw_floor && (v3d->drawtype > OB_WIRE);
 	bool xrayclear = true;
 
 	if (!draw_offscreen) {
@@ -1636,19 +1634,9 @@ static void view3d_draw_objects(
 	/* transp and X-ray afterdraw stuff */
 	if (v3d->afterdraw_transp.first)     view3d_draw_transp(eval_ctx, scene, view_layer, ar, v3d);
 
-	/* always do that here to cleanup depth buffers if none needed */
-	if (fx) {
-		do_composite_xray = v3d->zbuf && (v3d->afterdraw_xray.first || v3d->afterdraw_xraytransp.first);
-		GPU_fx_compositor_setup_XRay_pass(fx, do_composite_xray);
-	}
-
 	if (v3d->afterdraw_xray.first)       view3d_draw_xray(eval_ctx, scene, view_layer, ar, v3d, &xrayclear);
 	if (v3d->afterdraw_xraytransp.first) view3d_draw_xraytransp(eval_ctx, scene, view_layer, ar, v3d, xrayclear);
 
-	if (fx && do_composite_xray) {
-		GPU_fx_compositor_XRay_resolve(fx);
-	}
-
 	if (!draw_offscreen) {
 		ED_region_draw_cb_draw(C, ar, REGION_DRAW_POST_VIEW);
 	}
@@ -1946,9 +1934,8 @@ static void update_lods(Scene *scene, float camera_pos[3])
 }
 #endif
 
-static void view3d_main_region_draw_objects(
-        const bContext *C, Scene *scene, ViewLayer *view_layer, View3D *v3d,
-        ARegion *ar, const char **grid_unit)
+static void view3d_main_region_draw_objects(const bContext *C, Scene *scene, ViewLayer *view_layer, View3D *v3d,
+                                          ARegion *ar, const char **grid_unit)
 {
 	wmWindow *win = CTX_wm_window(C);
 	EvaluationContext eval_ctx;
@@ -1957,11 +1944,8 @@ static void view3d_main_region_draw_objects(
 	
 	CTX_data_eval_ctx(C, &eval_ctx);
 
-	/* post processing */
-	bool do_compositing = false;
-	
 	/* shadow buffers, before we setup matrices */
-	if (draw_glsl_material(&eval_ctx, scene, view_layer, NULL, v3d, v3d->drawtype))
+	if (draw_glsl_material(scene, view_layer, NULL, v3d, v3d->drawtype))
 		gpu_update_lamps_shadows_world(&eval_ctx, scene, v3d);
 
 	/* reset default OpenGL lights if needed (i.e. after preferences have been altered) */
@@ -1987,30 +1971,9 @@ static void view3d_main_region_draw_objects(
 		update_lods(scene, rv3d->viewinv[3]);
 	}
 #endif
-
-	/* framebuffer fx needed, we need to draw offscreen first */
-	if (v3d->fx_settings.fx_flag && v3d->drawtype >= OB_SOLID) {
-		BKE_screen_gpu_fx_validate(&v3d->fx_settings);
-		GPUFXSettings fx_settings = v3d->fx_settings;
-		if (!rv3d->compositor)
-			rv3d->compositor = GPU_fx_compositor_create();
-		
-		if (rv3d->persp == RV3D_CAMOB && v3d->camera)
-			BKE_camera_to_gpu_dof(v3d->camera, &fx_settings);
-		else {
-			fx_settings.dof = NULL;
-		}
-
-		do_compositing = GPU_fx_compositor_initialize_passes(rv3d->compositor, &ar->winrct, &ar->drawrct, &fx_settings);
-	}
 	
 	/* main drawing call */
-	view3d_draw_objects(C, &eval_ctx, scene, v3d, ar, grid_unit, true, false, do_compositing ? rv3d->compositor : NULL);
-
-	/* post process */
-	if (do_compositing) {
-		GPU_fx_do_composite_pass(rv3d->compositor, rv3d->winmat, rv3d->is_persp, scene, NULL);
-	}
+	view3d_draw_objects(C, &eval_ctx, scene, v3d, ar, grid_unit, true, false);
 
 	if (v3d->lay_used != lay_used) { /* happens when loading old files or loading with UI load */
 		/* find header and force tag redraw */
@@ -2024,13 +1987,10 @@ static void view3d_main_region_draw_objects(
 	}
 }
 
-static void view3d_main_region_draw_info(
-        const bContext *C, Scene *scene,
-        ARegion *ar, View3D *v3d,
-        const char *grid_unit, bool render_border)
+static void view3d_main_region_draw_info(const bContext *C, Scene *scene,
+                                       ARegion *ar, View3D *v3d,
+                                       const char *grid_unit, bool render_border)
 {
-	EvaluationContext eval_ctx;
-	CTX_data_eval_ctx(C, &eval_ctx);
 	const Depsgraph *depsgraph = CTX_data_depsgraph(C);
 	ViewLayer *view_layer = CTX_data_view_layer(C);
 	wmWindowManager *wm = CTX_wm_manager(C);
@@ -2053,7 +2013,7 @@ static void view3d_main_region_draw_info(
 	}
 
 	if ((v3d->flag2 & V3D_RENDER_OVERRIDE) == 0) {
-		VP_legacy_drawcursor(&eval_ctx, scene, view_layer, ar, v3d); /* 3D cursor */
+		VP_legacy_drawcursor(scene, view_layer, ar, v3d); /* 3D cursor */
 
 		if (U.uiflag & USER_SHOW_ROTVIEWICON)
 			VP_legacy_draw_view_axis(rv3d, &rect);
@@ -2062,7 +2022,7 @@ static void view3d_main_region_draw_info(
 
 		if (U.uiflag & USER_DRAWVIEWINFO) {
 			Object *ob = OBACT(view_layer);
-			VP_legacy_draw_selected_name(scene, ob, eval_ctx.object_mode, &rect);
+			VP_legacy_draw_selected_name(scene, ob, &rect);
 		}
 	}
 
@@ -2170,9 +2130,9 @@ void VP_deprecated_view3d_draw_objects(
         const EvaluationContext *eval_ctx,
         Scene *scene, View3D *v3d, ARegion *ar,
         const char **grid_unit,
-        const bool do_bgpic, const bool draw_offscreen, GPUFX *fx)
+        const bool do_bgpic, const bool draw_offscreen)
 {
-	view3d_draw_objects(C, eval_ctx, scene, v3d, ar, grid_unit, do_bgpic, draw_offscreen, fx);
+	view3d_draw_objects(C, eval_ctx, scene, v3d, ar, grid_unit, do_bgpic, draw_offscreen);
 }
 
 void VP_deprecated_gpu_update_lamps_shadows_world(const EvaluationContext *eval_ctx, Scene *scene, View3D *v3d)
diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c
index 45284c1805a4e3ebaef423214cee412e929e7c8b..0ae69debf184a2163e61f45b7120b0a6c8b53e58 100644
--- a/source/blender/editors/space_view3d/view3d_edit.c
+++ b/source/blender/editors/space_view3d/view3d_edit.c
@@ -244,22 +244,21 @@ void view3d_orbit_apply_dyn_ofs(
 static bool view3d_orbit_calc_center(bContext *C, float r_dyn_ofs[3])
 {
 	static float lastofs[3] = {0, 0, 0};
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	bool is_set = false;
 
 	Scene *scene = CTX_data_scene(C);
 	ViewLayer *view_layer = CTX_data_view_layer(C);
 	Object *ob_act = OBACT(view_layer);
 
-	if (ob_act && (workspace->object_mode & OB_MODE_ALL_PAINT) &&
+	if (ob_act && (ob_act->mode & OB_MODE_ALL_PAINT) &&
 	    /* with weight-paint + pose-mode, fall through to using calculateTransformCenter */
-	    ((workspace->object_mode & OB_MODE_WEIGHT_PAINT) && BKE_object_pose_armature_get(ob_act)) == 0)
+	    ((ob_act->mode & OB_MODE_WEIGHT_PAINT) && BKE_object_pose_armature_get(ob_act)) == 0)
 	{
 		/* in case of sculpting use last average stroke position as a rotation
 		 * center, in other cases it's not clear what rotation center shall be
 		 * so just rotate around object origin
 		 */
-		if (workspace->object_mode & (OB_MODE_SCULPT | OB_MODE_TEXTURE_PAINT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT)) {
+		if (ob_act->mode & (OB_MODE_SCULPT | OB_MODE_TEXTURE_PAINT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT)) {
 			float stroke[3];
 			BKE_paint_stroke_get_average(scene, ob_act, stroke);
 			copy_v3_v3(lastofs, stroke);
@@ -269,7 +268,7 @@ static bool view3d_orbit_calc_center(bContext *C, float r_dyn_ofs[3])
 		}
 		is_set = true;
 	}
-	else if (ob_act && (workspace->object_mode & OB_MODE_EDIT) && (ob_act->type == OB_FONT)) {
+	else if (ob_act && (ob_act->mode & OB_MODE_EDIT) && (ob_act->type == OB_FONT)) {
 		Curve *cu = ob_act->data;
 		EditFont *ef = cu->editfont;
 		int i;
@@ -284,7 +283,7 @@ static bool view3d_orbit_calc_center(bContext *C, float r_dyn_ofs[3])
 
 		is_set = true;
 	}
-	else if (ob_act == NULL || workspace->object_mode == OB_MODE_OBJECT) {
+	else if (ob_act == NULL || ob_act->mode == OB_MODE_OBJECT) {
 		/* object mode use boundbox centers */
 		Base *base;
 		unsigned int tot = 0;
@@ -2793,7 +2792,6 @@ void VIEW3D_OT_view_all(wmOperatorType *ot)
 /* like a localview without local!, was centerview() in 2.4x */
 static int viewselected_exec(bContext *C, wmOperator *op)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	ARegion *ar = CTX_wm_region(C);
 	View3D *v3d = CTX_wm_view3d(C);
 	Scene *scene = CTX_data_scene(C);
@@ -2817,10 +2815,16 @@ static int viewselected_exec(bContext *C, wmOperator *op)
 		ob = NULL;
 	}
 
-	if (ob && (workspace->object_mode & OB_MODE_WEIGHT_PAINT)) {
-		Object *ob_armature = BKE_object_pose_armature_get_visible(ob, view_layer);
-		if (ob_armature) {
-			ob = ob_armature;
+	if (ob && (ob->mode & OB_MODE_WEIGHT_PAINT)) {
+		/* hard-coded exception, we look for the one selected armature */
+		/* this is weak code this way, we should make a generic active/selection callback interface once... */
+		Base *base;
+		for (base = view_layer->object_bases.first; base; base = base->next) {
+			if (TESTBASELIB(base)) {
+				if (base->object->type == OB_ARMATURE)
+					if (base->object->mode & OB_MODE_POSE)
+						break;
+			}
 		}
 	}
 
@@ -2842,17 +2846,17 @@ static int viewselected_exec(bContext *C, wmOperator *op)
 	else if (obedit) {
 		ok = ED_view3d_minmax_verts(obedit, min, max);    /* only selected */
 	}
-	else if (ob && (workspace->object_mode & OB_MODE_POSE)) {
+	else if (ob && (ob->mode & OB_MODE_POSE)) {
 		ok = BKE_pose_minmax(ob, min, max, true, true);
 	}
-	else if (BKE_paint_select_face_test(ob, workspace->object_mode)) {
+	else if (BKE_paint_select_face_test(ob)) {
 		ok = paintface_minmax(ob, min, max);
 	}
-	else if (ob && (workspace->object_mode & OB_MODE_PARTICLE_EDIT)) {
+	else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT)) {
 		ok = PE_minmax(scene, view_layer, min, max);
 	}
 	else if (ob &&
-	         (workspace->object_mode & (OB_MODE_SCULPT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)))
+	         (ob->mode & (OB_MODE_SCULPT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)))
 	{
 		BKE_paint_stroke_get_average(scene, ob, min);
 		copy_v3_v3(max, min);
@@ -2964,8 +2968,7 @@ static int view_lock_to_active_exec(bContext *C, wmOperator *UNUSED(op))
 		v3d->ob_centre = obact; /* can be NULL */
 
 		if (obact && obact->type == OB_ARMATURE) {
-			const WorkSpace *workspace = CTX_wm_workspace(C);
-			if (workspace->object_mode & OB_MODE_POSE) {
+			if (obact->mode & OB_MODE_POSE) {
 				bPoseChannel *pcham_act = BKE_pose_channel_active(obact);
 				if (pcham_act) {
 					BLI_strncpy(v3d->ob_centre_bone, pcham_act->name, sizeof(v3d->ob_centre_bone));
diff --git a/source/blender/editors/space_view3d/view3d_header.c b/source/blender/editors/space_view3d/view3d_header.c
index bf4abf1852ddd9a7eb340fa469488ce195e57e19..3ed6c87b750a43a5de5e9597571bed8afe9d8809 100644
--- a/source/blender/editors/space_view3d/view3d_header.c
+++ b/source/blender/editors/space_view3d/view3d_header.c
@@ -55,7 +55,7 @@
 #include "WM_types.h"
 
 #include "ED_mesh.h"
-#include "ED_util.h"
+#include "ED_undo.h"
 #include "ED_screen.h"
 
 #include "UI_interface.h"
@@ -276,7 +276,6 @@ void uiTemplateEditModeSelection(uiLayout *layout, struct bContext *C)
 
 void uiTemplateHeader3D(uiLayout *layout, struct bContext *C)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	bScreen *screen = CTX_wm_screen(C);
 	ScrArea *sa = CTX_wm_area(C);
 	View3D *v3d = sa->spacedata.first;
@@ -291,7 +290,7 @@ void uiTemplateHeader3D(uiLayout *layout, struct bContext *C)
 	uiLayout *row;
 	bool is_paint = (
 	        ob && !(gpd && (gpd->flag & GP_DATA_STROKE_EDITMODE)) &&
-	        ELEM(workspace->object_mode,
+	        ELEM(ob->mode,
 	             OB_MODE_SCULPT, OB_MODE_VERTEX_PAINT, OB_MODE_WEIGHT_PAINT, OB_MODE_TEXTURE_PAINT));
 	
 	RNA_pointer_create(&screen->id, &RNA_SpaceView3D, v3d, &v3dptr);
@@ -306,18 +305,18 @@ void uiTemplateHeader3D(uiLayout *layout, struct bContext *C)
 
 	row = uiLayoutRow(layout, true);
 	uiItemR(row, &v3dptr, "pivot_point", UI_ITEM_R_ICON_ONLY, "", ICON_NONE);
-	if (!ob || ELEM(workspace->object_mode, OB_MODE_OBJECT, OB_MODE_POSE, OB_MODE_WEIGHT_PAINT)) {
+	if (!ob || ELEM(ob->mode, OB_MODE_OBJECT, OB_MODE_POSE, OB_MODE_WEIGHT_PAINT)) {
 		uiItemR(row, &v3dptr, "use_pivot_point_align", UI_ITEM_R_ICON_ONLY, "", ICON_NONE);
 	}
 
 	if (obedit == NULL && is_paint) {
 		/* Manipulators aren't used in paint modes */
-		if (!ELEM(workspace->object_mode, OB_MODE_SCULPT, OB_MODE_PARTICLE_EDIT)) {
+		if (!ELEM(ob->mode, OB_MODE_SCULPT, OB_MODE_PARTICLE_EDIT)) {
 			/* masks aren't used for sculpt and particle painting */
 			PointerRNA meshptr;
 
 			RNA_pointer_create(ob->data, &RNA_Mesh, ob->data, &meshptr);
-			if (workspace->object_mode & (OB_MODE_TEXTURE_PAINT)) {
+			if (ob->mode & (OB_MODE_TEXTURE_PAINT)) {
 				uiItemR(layout, &meshptr, "use_paint_mask", UI_ITEM_R_ICON_ONLY, "", ICON_NONE);
 			}
 			else {
diff --git a/source/blender/editors/space_view3d/view3d_intern.h b/source/blender/editors/space_view3d/view3d_intern.h
index 0eb7a964fdf44589d8fbbb18947fff9eb8247000..0f56f1265bccf5b68c95d2778ea945a0a884c8dd 100644
--- a/source/blender/editors/space_view3d/view3d_intern.h
+++ b/source/blender/editors/space_view3d/view3d_intern.h
@@ -54,7 +54,6 @@ struct wmKeyConfig;
 struct wmManipulatorGroupType;
 struct wmManipulatorType;
 struct wmWindowManager;
-struct EvaluationContext;
 
 /* drawing flags: */
 enum {
@@ -155,19 +154,13 @@ void draw_object_select(
         const struct EvaluationContext *eval_ctx, Scene *scene, struct ViewLayer *view_layer, struct ARegion *ar, View3D *v3d,
         Base *base, const short dflag);
 
-void draw_mesh_object_outline(
-        const struct EvaluationContext *eval_ctx, View3D *v3d,
-        struct Object *ob, struct DerivedMesh *dm, const unsigned char ob_wire_col[4]);
+void draw_mesh_object_outline(View3D *v3d, struct Object *ob, struct DerivedMesh *dm, const unsigned char ob_wire_col[4]);
 
-bool draw_glsl_material(
-        const struct EvaluationContext *eval_ctx, Scene *scene, struct ViewLayer *view_layer,
-        struct Object *ob, View3D *v3d, const char dt);
+bool draw_glsl_material(Scene *scene, struct ViewLayer *view_layer, struct Object *ob, View3D *v3d, const char dt);
 void draw_object_instance(const struct EvaluationContext *eval_ctx, Scene *scene, struct ViewLayer *view_layer, View3D *v3d, RegionView3D *rv3d, struct Object *ob, const char dt, int outline, const float wire_col[4]);
 void draw_object_backbufsel(const struct EvaluationContext *eval_ctx, Scene *scene, View3D *v3d, RegionView3D *rv3d, struct Object *ob);
 
-void draw_object_wire_color(
-        const struct EvaluationContext *eval_ctx, struct ViewLayer *,
-        Base *base, unsigned char r_ob_wire_col[4]);
+void draw_object_wire_color(struct ViewLayer *, Base *base, unsigned char r_ob_wire_col[4]);
 void drawaxes(const float viewmat_local[4][4], float size, char drawtype, const unsigned char color[4]);
 void drawlamp(View3D *v3d, RegionView3D *rv3d, Base *base,
               const char dt, const short dflag, const unsigned char ob_wire_col[4],
@@ -216,10 +209,8 @@ void draw_mesh_paint_vcolor_faces(struct DerivedMesh *dm, const bool use_light,
 void draw_mesh_paint_weight_edges(RegionView3D *rv3d, struct DerivedMesh *dm,
                                   const bool use_depth, const bool use_alpha,
                                   void *edgemask_cb, void *user_data);
-void draw_mesh_paint(
-        const struct EvaluationContext *eval_ctx,
-        View3D *v3d, RegionView3D *rv3d,
-        struct Object *ob, struct DerivedMesh *dm, const int draw_flags);
+void draw_mesh_paint(View3D *v3d, RegionView3D *rv3d,
+                     struct Object *ob, struct DerivedMesh *dm, const int draw_flags);
 
 /* drawsimdebug.c */
 void draw_sim_debug_data(Scene *scene, View3D *v3d, ARegion *ar);
@@ -376,12 +367,10 @@ extern bool view3d_camera_border_hack_test;
 #endif
 
 /* temporary for legacy viewport to work */
-void VP_legacy_drawcursor(
-        const struct EvaluationContext *eval_ctx, Scene *scene,
-        struct ViewLayer *view_layer, ARegion *ar, View3D *v3d);
+void VP_legacy_drawcursor(Scene *scene, struct ViewLayer *view_layer, ARegion *ar, View3D *v3d);
 void VP_legacy_draw_view_axis(RegionView3D *rv3d, const rcti *rect);
 void VP_legacy_draw_viewport_name(ARegion *ar, View3D *v3d, const rcti *rect);
-void VP_legacy_draw_selected_name(Scene *scene, struct Object *ob, eObjectMode object_mode, const rcti *rect);
+void VP_legacy_draw_selected_name(Scene *scene, struct Object *ob, rcti *rect);
 void VP_legacy_drawgrid(UnitSettings *unit, ARegion *ar, View3D *v3d, const char **grid_unit);
 void VP_legacy_drawfloor(Scene *scene, View3D *v3d, const char **grid_unit, bool write_depth);
 void VP_legacy_view3d_main_region_setup_view(const struct EvaluationContext *eval_ctx, Scene *scene, View3D *v3d, ARegion *ar, float viewmat[4][4], float winmat[4][4]);
@@ -402,6 +391,6 @@ void VP_deprecated_view3d_draw_objects(
         const struct EvaluationContext *eval_ctx,
         Scene *scene, View3D *v3d, ARegion *ar,
         const char **grid_unit,
-        const bool do_bgpic, const bool draw_offscreen, struct GPUFX *fx);
+        const bool do_bgpic, const bool draw_offscreen);
 
 #endif /* __VIEW3D_INTERN_H__ */
diff --git a/source/blender/editors/space_view3d/view3d_manipulator_camera.c b/source/blender/editors/space_view3d/view3d_manipulator_camera.c
index 6a45ec5095fc416c8422bec9bcf16d860a98ca03..3c826c69f3cf6d3dae8e06b93654d716fcb0d448 100644
--- a/source/blender/editors/space_view3d/view3d_manipulator_camera.c
+++ b/source/blender/editors/space_view3d/view3d_manipulator_camera.c
@@ -64,8 +64,14 @@ struct CameraWidgetGroup {
 static bool WIDGETGROUP_camera_poll(const bContext *C, wmManipulatorGroupType *UNUSED(wgt))
 {
 	Object *ob = CTX_data_active_object(C);
-
-	return (ob && ob->type == OB_CAMERA);
+	if (ob && ob->type == OB_CAMERA) {
+		Camera *camera = ob->data;
+		/* TODO: support overrides. */
+		if (camera->id.lib == NULL) {
+			return true;
+		}
+	}
+	return false;
 }
 
 static void WIDGETGROUP_camera_setup(const bContext *C, wmManipulatorGroup *mgroup)
@@ -332,10 +338,7 @@ static void manipulator_render_border_prop_matrix_set(
 
 static bool WIDGETGROUP_camera_view_poll(const bContext *C, wmManipulatorGroupType *UNUSED(wgt))
 {
-	ARegion *ar = CTX_wm_region(C);
-	RegionView3D *rv3d = ar->regiondata;
 	Scene *scene = CTX_data_scene(C);
-	View3D *v3d = CTX_wm_view3d(C);
 
 	/* This is just so the border isn't always in the way,
 	 * stealing mouse clicks from regular usage.
@@ -347,9 +350,15 @@ static bool WIDGETGROUP_camera_view_poll(const bContext *C, wmManipulatorGroupTy
 		}
 	}
 
+	ARegion *ar = CTX_wm_region(C);
+	RegionView3D *rv3d = ar->regiondata;
+	View3D *v3d = CTX_wm_view3d(C);
 	if (rv3d->persp == RV3D_CAMOB) {
 		if (scene->r.mode & R_BORDER) {
-			return true;
+			/* TODO: support overrides. */
+			if (scene->id.lib == NULL) {
+				return true;
+			}
 		}
 	}
 	else if (v3d->flag2 & V3D_RENDER_BORDER) {
diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c
index 6364df2af2b79b99ff797aed979d6c2dc2419da4..be8e63dffe6e77f2e412ee015b4fa26c2f435135 100644
--- a/source/blender/editors/space_view3d/view3d_select.c
+++ b/source/blender/editors/space_view3d/view3d_select.c
@@ -210,7 +210,6 @@ static void edbm_backbuf_check_and_select_tfaces(Mesh *me, const bool select)
 /* *********************** GESTURE AND LASSO ******************* */
 
 typedef struct LassoSelectUserData {
-	const EvaluationContext *eval_ctx;
 	ViewContext *vc;
 	const rcti *rect;
 	const rctf *rect_fl;
@@ -254,15 +253,14 @@ static int view3d_selectable_data(bContext *C)
 		return 0;
 
 	if (ob) {
-		const WorkSpace *workspace = CTX_wm_workspace(C);
-		if (workspace->object_mode & OB_MODE_EDIT) {
+		if (ob->mode & OB_MODE_EDIT) {
 			if (ob->type == OB_FONT) {
 				return 0;
 			}
 		}
 		else {
-			if ((workspace->object_mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)) &&
-			    !BKE_paint_select_elem_test(ob, workspace->object_mode))
+			if ((ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)) &&
+			    !BKE_paint_select_elem_test(ob))
 			{
 				return 0;
 			}
@@ -393,7 +391,6 @@ static void object_deselect_all_visible(ViewLayer *view_layer)
 }
 
 static void do_lasso_select_objects(
-        const EvaluationContext *eval_ctx,
         ViewContext *vc, const int mcords[][2], const short moves,
         const bool extend, const bool select)
 {
@@ -410,7 +407,7 @@ static void do_lasso_select_objects(
 					ED_object_base_select(base, select ? BA_SELECT : BA_DESELECT);
 				}
 			}
-			if (vc->obact == base->object && (eval_ctx->object_mode & OB_MODE_POSE)) {
+			if (vc->obact == base->object && (base->object->mode & OB_MODE_POSE)) {
 				do_lasso_select_pose(vc, base->object, mcords, moves, select);
 			}
 		}
@@ -466,7 +463,7 @@ static void do_lasso_select_mesh__doSelectFace(void *userData, BMFace *efa, cons
 }
 
 static void do_lasso_select_mesh(
-        const EvaluationContext *eval_ctx, ViewContext *vc,
+        const struct EvaluationContext *eval_ctx, ViewContext *vc,
         const int mcords[][2], short moves, bool extend, bool select)
 {
 	LassoSelectUserData data;
@@ -522,8 +519,7 @@ static void do_lasso_select_mesh(
 	EDBM_selectmode_flush(vc->em);
 }
 
-static void do_lasso_select_curve__doSelect(
-        void *userData, Nurb *UNUSED(nu), BPoint *bp, BezTriple *bezt, int beztindex, const float screen_co[2])
+static void do_lasso_select_curve__doSelect(void *userData, Nurb *UNUSED(nu), BPoint *bp, BezTriple *bezt, int beztindex, const float screen_co[2])
 {
 	LassoSelectUserData *data = userData;
 	Object *obedit = data->vc->obedit;
@@ -598,8 +594,7 @@ static void do_lasso_select_lattice(ViewContext *vc, const int mcords[][2], shor
 	lattice_foreachScreenVert(vc, do_lasso_select_lattice__doSelect, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
 }
 
-static void do_lasso_select_armature__doSelectBone(
-        void *userData, struct EditBone *ebone, const float screen_co_a[2], const float screen_co_b[2])
+static void do_lasso_select_armature__doSelectBone(void *userData, struct EditBone *ebone, const float screen_co_a[2], const float screen_co_b[2])
 {
 	LassoSelectUserData *data = userData;
 	bArmature *arm = data->vc->obedit->data;
@@ -705,8 +700,7 @@ static void do_lasso_select_meta(ViewContext *vc, const int mcords[][2], short m
 	mball_foreachScreenElem(vc, do_lasso_select_mball__doSelectElem, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
 }
 
-static void do_lasso_select_meshobject__doSelectVert(
-        void *userData, MVert *mv, const float screen_co[2], int UNUSED(index))
+static void do_lasso_select_meshobject__doSelectVert(void *userData, MVert *mv, const float screen_co[2], int UNUSED(index))
 {
 	LassoSelectUserData *data = userData;
 
@@ -716,9 +710,7 @@ static void do_lasso_select_meshobject__doSelectVert(
 		SET_FLAG_FROM_TEST(mv->flag, data->select, SELECT);
 	}
 }
-static void do_lasso_select_paintvert(
-        const EvaluationContext *eval_ctx,
-        ViewContext *vc, const int mcords[][2], short moves, bool extend, bool select)
+static void do_lasso_select_paintvert(const struct EvaluationContext *eval_ctx, ViewContext *vc, const int mcords[][2], short moves, bool extend, bool select)
 {
 	const bool use_zbuf = (vc->v3d->flag & V3D_ZBUF_SELECT) != 0;
 	Object *ob = vc->obact;
@@ -749,8 +741,8 @@ static void do_lasso_select_paintvert(
 
 		ED_view3d_init_mats_rv3d(vc->obact, vc->rv3d);
 
-		meshobject_foreachScreenVert(
-		        eval_ctx, vc, do_lasso_select_meshobject__doSelectVert, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
+		meshobject_foreachScreenVert(eval_ctx, vc, do_lasso_select_meshobject__doSelectVert, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
+
 	}
 
 	if (select == false) {
@@ -758,9 +750,7 @@ static void do_lasso_select_paintvert(
 	}
 	paintvert_flush_flags(ob);
 }
-static void do_lasso_select_paintface(
-        const EvaluationContext *eval_ctx,
-        ViewContext *vc, const int mcords[][2], short moves, bool extend, bool select)
+static void do_lasso_select_paintface(const struct EvaluationContext *eval_ctx, ViewContext *vc, const int mcords[][2], short moves, bool extend, bool select)
 {
 	Object *ob = vc->obact;
 	Mesh *me = ob->data;
@@ -826,20 +816,20 @@ static void view3d_lasso_select(
 	CTX_data_eval_ctx(C, &eval_ctx);
 
 	if (vc->obedit == NULL) { /* Object Mode */
-		if (BKE_paint_select_face_test(ob, eval_ctx.object_mode)) {
+		if (BKE_paint_select_face_test(ob)) {
 			do_lasso_select_paintface(&eval_ctx, vc, mcords, moves, extend, select);
 		}
-		else if (BKE_paint_select_vert_test(ob, eval_ctx.object_mode)) {
+		else if (BKE_paint_select_vert_test(ob)) {
 			do_lasso_select_paintvert(&eval_ctx, vc, mcords, moves, extend, select);
 		}
-		else if (ob && (eval_ctx.object_mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT))) {
+		else if (ob && (ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT))) {
 			/* pass */
 		}
-		else if (ob && (eval_ctx.object_mode & OB_MODE_PARTICLE_EDIT)) {
+		else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT)) {
 			PE_lasso_select(C, mcords, moves, extend, select);
 		}
 		else {
-			do_lasso_select_objects(&eval_ctx, vc, mcords, moves, extend, select);
+			do_lasso_select_objects(vc, mcords, moves, extend, select);
 			WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, vc->scene);
 		}
 	}
@@ -1510,10 +1500,7 @@ static bool ed_object_select_pick(
 						}
 					}
 				}
-				else if (ED_do_pose_selectbuffer(
-				                 &eval_ctx, view_layer,
-				                 basact, buffer, hits, extend, deselect, toggle, do_nearest))
-				{
+				else if (ED_do_pose_selectbuffer(view_layer, basact, buffer, hits, extend, deselect, toggle, do_nearest)) {
 					/* then bone is found */
 				
 					/* we make the armature selected: 
@@ -1526,7 +1513,7 @@ static bool ed_object_select_pick(
 					WM_event_add_notifier(C, NC_OBJECT | ND_BONE_ACTIVE, basact->object);
 					
 					/* in weightpaint, we use selected bone to select vertexgroup, so no switch to new active object */
-					if (BASACT(view_layer) && (eval_ctx.object_mode & OB_MODE_WEIGHT_PAINT)) {
+					if (BASACT(view_layer) && (BASACT(view_layer)->object->mode & OB_MODE_WEIGHT_PAINT)) {
 						/* prevent activating */
 						basact = NULL;
 					}
@@ -1624,8 +1611,7 @@ bool edge_inside_circle(const float cent[2], float radius, const float screen_co
 	return (dist_squared_to_line_segment_v2(cent, screen_co_a, screen_co_b) < radius_squared);
 }
 
-static void do_paintvert_box_select__doSelectVert(
-        void *userData, MVert *mv, const float screen_co[2], int UNUSED(index))
+static void do_paintvert_box_select__doSelectVert(void *userData, MVert *mv, const float screen_co[2], int UNUSED(index))
 {
 	BoxSelectUserData *data = userData;
 
@@ -1824,7 +1810,7 @@ static void do_mesh_box_select__doSelectFace(void *userData, BMFace *efa, const
 	}
 }
 static int do_mesh_box_select(
-        const EvaluationContext *eval_ctx, ViewContext *vc, rcti *rect, bool select, bool extend)
+        const struct EvaluationContext *eval_ctx, ViewContext *vc, rcti *rect, bool select, bool extend)
 {
 	BoxSelectUserData data;
 	ToolSettings *ts = vc->scene->toolsettings;
@@ -1878,7 +1864,7 @@ static int do_mesh_box_select(
 }
 
 static int do_meta_box_select(
-        const EvaluationContext *eval_ctx, ViewContext *vc,
+        const struct EvaluationContext *eval_ctx, ViewContext *vc,
         const rcti *rect, bool select, bool extend)
 {
 	MetaBall *mb = (MetaBall *)vc->obedit->data;
@@ -1914,7 +1900,7 @@ static int do_meta_box_select(
 }
 
 static int do_armature_box_select(
-        const EvaluationContext *eval_ctx, ViewContext *vc,
+        const struct EvaluationContext *eval_ctx, ViewContext *vc,
         const rcti *rect, bool select, bool extend)
 {
 	bArmature *arm = vc->obedit->data;
@@ -2014,8 +2000,8 @@ static int do_object_pose_box_select(bContext *C, ViewContext *vc, rcti *rect, b
 	EvaluationContext eval_ctx;
 	Bone *bone;
 	Object *ob = vc->obact;
-	unsigned int *vbuffer = NULL; /* selection buffer	*/
-	unsigned int *col;          /* color in buffer	*/
+	unsigned int *vbuffer = NULL; /* selection buffer */
+	unsigned int *col;            /* color in buffer */
 	int bone_only;
 	int bone_selected = 0;
 	int totobj = MAXPICKBUF; /* XXX solve later */
@@ -2023,7 +2009,7 @@ static int do_object_pose_box_select(bContext *C, ViewContext *vc, rcti *rect, b
 	
 	CTX_data_eval_ctx(C, &eval_ctx);
 
-	if ((ob) && (eval_ctx.object_mode & OB_MODE_POSE))
+	if ((ob) && (ob->mode & OB_MODE_POSE))
 		bone_only = 1;
 	else
 		bone_only = 0;
@@ -2178,16 +2164,16 @@ static int view3d_borderselect_exec(bContext *C, wmOperator *op)
 		}
 	}
 	else {  /* no editmode, unified for bones and objects */
-		if (vc.obact && eval_ctx.object_mode & OB_MODE_SCULPT) {
+		if (vc.obact && vc.obact->mode & OB_MODE_SCULPT) {
 			ret = ED_sculpt_mask_box_select(C, &vc, &rect, select, extend);
 		}
-		else if (vc.obact && BKE_paint_select_face_test(vc.obact, eval_ctx.object_mode)) {
+		else if (vc.obact && BKE_paint_select_face_test(vc.obact)) {
 			ret = do_paintface_box_select(&eval_ctx, &vc, &rect, select, extend);
 		}
-		else if (vc.obact && BKE_paint_select_vert_test(vc.obact, eval_ctx.object_mode)) {
+		else if (vc.obact && BKE_paint_select_vert_test(vc.obact)) {
 			ret = do_paintvert_box_select(&eval_ctx, &vc, &rect, select, extend);
 		}
-		else if (vc.obact && eval_ctx.object_mode & OB_MODE_PARTICLE_EDIT) {
+		else if (vc.obact && vc.obact->mode & OB_MODE_PARTICLE_EDIT) {
 			ret = PE_border_select(C, &rect, select, extend);
 		}
 		else { /* object mode with none active */
@@ -2273,7 +2259,6 @@ static bool ed_wpaint_vertex_select_pick(
 
 static int view3d_select_exec(bContext *C, wmOperator *op)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Object *obedit = CTX_data_edit_object(C);
 	Object *obact = CTX_data_active_object(C);
 	bool extend = RNA_boolean_get(op->ptr, "extend");
@@ -2285,9 +2270,9 @@ static int view3d_select_exec(bContext *C, wmOperator *op)
 	 * or paint-select to allow pose bone select with vert/face select */
 	bool object = (RNA_boolean_get(op->ptr, "object") &&
 	               (obedit ||
-	                BKE_paint_select_elem_test(obact, workspace->object_mode) ||
+	                BKE_paint_select_elem_test(obact) ||
 	                /* so its possible to select bones in weightpaint mode (LMB select) */
-	                (obact && (workspace->object_mode & OB_MODE_WEIGHT_PAINT) && BKE_object_pose_armature_get(obact))));
+	                (obact && (obact->mode & OB_MODE_WEIGHT_PAINT) && BKE_object_pose_armature_get(obact))));
 
 	bool retval = false;
 	int location[2];
@@ -2321,11 +2306,11 @@ static int view3d_select_exec(bContext *C, wmOperator *op)
 			retval = ED_curve_editfont_select_pick(C, location, extend, deselect, toggle);
 			
 	}
-	else if (obact && workspace->object_mode & OB_MODE_PARTICLE_EDIT)
+	else if (obact && obact->mode & OB_MODE_PARTICLE_EDIT)
 		return PE_mouse_particles(C, location, extend, deselect, toggle);
-	else if (obact && BKE_paint_select_face_test(obact, workspace->object_mode))
+	else if (obact && BKE_paint_select_face_test(obact))
 		retval = paintface_mouse_select(C, obact, location, extend, deselect, toggle);
-	else if (BKE_paint_select_vert_test(obact, workspace->object_mode))
+	else if (BKE_paint_select_vert_test(obact))
 		retval = ed_wpaint_vertex_select_pick(C, location, extend, deselect, toggle, obact);
 	else
 		retval = ed_object_select_pick(C, location, extend, deselect, toggle, center, enumerate, object);
@@ -2432,9 +2417,7 @@ static void mesh_circle_doSelectFace(void *userData, BMFace *efa, const float sc
 	}
 }
 
-static void mesh_circle_select(
-        const EvaluationContext *eval_ctx,
-        ViewContext *vc, const bool select, const int mval[2], float rad)
+static void mesh_circle_select(const struct EvaluationContext *eval_ctx, ViewContext *vc, const bool select, const int mval[2], float rad)
 {
 	ToolSettings *ts = vc->scene->toolsettings;
 	int bbsel;
@@ -2478,9 +2461,7 @@ static void mesh_circle_select(
 	EDBM_selectmode_flush(vc->em);
 }
 
-static void paint_facesel_circle_select(
-        const EvaluationContext *eval_ctx,
-        ViewContext *vc, const bool select, const int mval[2], float rad)
+static void paint_facesel_circle_select(const struct EvaluationContext *eval_ctx, ViewContext *vc, const bool select, const int mval[2], float rad)
 {
 	Object *ob = vc->obact;
 	Mesh *me = ob->data;
@@ -2496,8 +2477,7 @@ static void paint_facesel_circle_select(
 	}
 }
 
-static void paint_vertsel_circle_select_doSelectVert(
-        void *userData, MVert *mv, const float screen_co[2], int UNUSED(index))
+static void paint_vertsel_circle_select_doSelectVert(void *userData, MVert *mv, const float screen_co[2], int UNUSED(index))
 {
 	CircleSelectUserData *data = userData;
 
@@ -2505,8 +2485,7 @@ static void paint_vertsel_circle_select_doSelectVert(
 		SET_FLAG_FROM_TEST(mv->flag, data->select, SELECT);
 	}
 }
-static void paint_vertsel_circle_select(
-        const EvaluationContext *eval_ctx, ViewContext *vc, const bool select, const int mval[2], float rad)
+static void paint_vertsel_circle_select(const struct EvaluationContext *eval_ctx, ViewContext *vc, const bool select, const int mval[2], float rad)
 {
 	const bool use_zbuf = (vc->v3d->flag & V3D_ZBUF_SELECT) != 0;
 	Object *ob = vc->obact;
@@ -2529,8 +2508,7 @@ static void paint_vertsel_circle_select(
 		ED_view3d_init_mats_rv3d(vc->obact, vc->rv3d); /* for foreach's screen/vert projection */
 
 		view3d_userdata_circleselect_init(&data, vc, select, mval, rad);
-		meshobject_foreachScreenVert(
-		        eval_ctx, vc, paint_vertsel_circle_select_doSelectVert, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
+		meshobject_foreachScreenVert(eval_ctx, vc, paint_vertsel_circle_select_doSelectVert, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
 	}
 
 	if (select != LEFTMOUSE) {
@@ -2788,7 +2766,7 @@ static void mball_circle_select(ViewContext *vc, const bool select, const int mv
 /** Callbacks for circle selection in Editmode */
 
 static void obedit_circle_select(
-        const EvaluationContext *eval_ctx, ViewContext *vc, const bool select, const int mval[2], float rad)
+        const struct EvaluationContext *eval_ctx, ViewContext *vc, const bool select, const int mval[2], float rad)
 {
 	switch (vc->obedit->type) {
 		case OB_MESH:
@@ -2843,48 +2821,53 @@ static bool object_circle_select(ViewContext *vc, const bool select, const int m
 /* not a real operator, only for circle test */
 static int view3d_circle_select_exec(bContext *C, wmOperator *op)
 {
-	ViewContext vc;
-	ED_view3d_viewcontext_init(C, &vc);
-	Object *obact = vc.obact;
-	Object *obedit = vc.obedit;
-	EvaluationContext eval_ctx;
-	CTX_data_eval_ctx(C, &eval_ctx);
+	Scene *scene = CTX_data_scene(C);
+	Object *obact = CTX_data_active_object(C);
 	const int radius = RNA_int_get(op->ptr, "radius");
 	const bool select = !RNA_boolean_get(op->ptr, "deselect");
 	const int mval[2] = {RNA_int_get(op->ptr, "x"),
 	                     RNA_int_get(op->ptr, "y")};
 
-	if (obedit || BKE_paint_select_elem_test(obact, eval_ctx.object_mode) ||
-	    (obact && (eval_ctx.object_mode & (OB_MODE_PARTICLE_EDIT | OB_MODE_POSE))) )
+	if (CTX_data_edit_object(C) || BKE_paint_select_elem_test(obact) ||
+	    (obact && (obact->mode & (OB_MODE_PARTICLE_EDIT | OB_MODE_POSE))) )
 	{
+		EvaluationContext eval_ctx;
+		ViewContext vc;
+		
 		view3d_operator_needs_opengl(C);
+		
+		CTX_data_eval_ctx(C, &eval_ctx);
+		ED_view3d_viewcontext_init(C, &vc);
 
 		if (CTX_data_edit_object(C)) {
 			obedit_circle_select(&eval_ctx, &vc, select, mval, (float)radius);
 			WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obact->data);
 		}
-		else if (BKE_paint_select_face_test(obact, eval_ctx.object_mode)) {
+		else if (BKE_paint_select_face_test(obact)) {
 			paint_facesel_circle_select(&eval_ctx, &vc, select, mval, (float)radius);
 			WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obact->data);
 		}
-		else if (BKE_paint_select_vert_test(obact, eval_ctx.object_mode)) {
+		else if (BKE_paint_select_vert_test(obact)) {
 			paint_vertsel_circle_select(&eval_ctx, &vc, select, mval, (float)radius);
 			WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obact->data);
 		}
-		else if (eval_ctx.object_mode & OB_MODE_POSE)
+		else if (obact->mode & OB_MODE_POSE)
 			pose_circle_select(&vc, select, mval, (float)radius);
 		else
 			return PE_circle_select(C, select, mval, (float)radius);
 	}
-	else if (obact && eval_ctx.object_mode & OB_MODE_SCULPT) {
+	else if (obact && obact->mode & OB_MODE_SCULPT) {
 		return OPERATOR_CANCELLED;
 	}
 	else {
+		ViewContext vc;
+		ED_view3d_viewcontext_init(C, &vc);
+
 		if (object_circle_select(&vc, select, mval, (float)radius)) {
-			WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, vc.scene);
+			WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
 		}
 	}
-
+	
 	return OPERATOR_FINISHED;
 }
 
diff --git a/source/blender/editors/space_view3d/view3d_snap.c b/source/blender/editors/space_view3d/view3d_snap.c
index 40dd2f0f42885ef181d72b70e416648d69187455..1df29201bf63673b43c0f3827560bd85a62763d4 100644
--- a/source/blender/editors/space_view3d/view3d_snap.c
+++ b/source/blender/editors/space_view3d/view3d_snap.c
@@ -113,7 +113,7 @@ static int snap_sel_to_grid_exec(bContext *C, wmOperator *UNUSED(op))
 
 		CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects)
 		{
-			if (eval_ctx.object_mode & OB_MODE_POSE) {
+			if (ob->mode & OB_MODE_POSE) {
 				bPoseChannel *pchan;
 				bArmature *arm = ob->data;
 				
@@ -272,7 +272,7 @@ static int snap_selected_to_location(bContext *C, const float snap_target_global
 		ED_transverts_update_obedit(&tvs, obedit);
 		ED_transverts_free(&tvs);
 	}
-	else if (obact && (eval_ctx.object_mode & OB_MODE_POSE)) {
+	else if (obact && (obact->mode & OB_MODE_POSE)) {
 		struct KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, ANIM_KS_LOCATION_ID);
 
 		bPoseChannel *pchan;
@@ -554,8 +554,6 @@ static void bundle_midpoint(Scene *scene, Object *ob, float vec[3])
 
 static bool snap_curs_to_sel_ex(bContext *C, float cursor[3])
 {
-	EvaluationContext eval_ctx;
-	CTX_data_eval_ctx(C, &eval_ctx);
 	Object *obedit = CTX_data_edit_object(C);
 	Scene *scene = CTX_data_scene(C);
 	View3D *v3d = CTX_wm_view3d(C);
@@ -601,7 +599,7 @@ static bool snap_curs_to_sel_ex(bContext *C, float cursor[3])
 	else {
 		Object *obact = CTX_data_active_object(C);
 		
-		if (obact && (eval_ctx.object_mode & OB_MODE_POSE)) {
+		if (obact && (obact->mode & OB_MODE_POSE)) {
 			bArmature *arm = obact->data;
 			bPoseChannel *pchan;
 			for (pchan = obact->pose->chanbase.first; pchan; pchan = pchan->next) {
@@ -703,8 +701,7 @@ static bool snap_calc_active_center(bContext *C, const bool select_only, float r
 		Object *ob = CTX_data_active_object(C);
 
 		if (ob) {
-			const WorkSpace *workspace = CTX_wm_workspace(C);
-			if (workspace->object_mode & OB_MODE_POSE) {
+			if (ob->mode & OB_MODE_POSE) {
 				bPoseChannel *pchan = BKE_pose_channel_active(ob);
 				if (pchan) {
 					if (!select_only || (pchan->bone->flag & BONE_SELECTED)) {
diff --git a/source/blender/editors/space_view3d/view3d_toolbar.c b/source/blender/editors/space_view3d/view3d_toolbar.c
index 5e3c783c1b6c286af00da12aafb303dd297afa81..dfa64bd2015166685a953d2ea350c05f1b8761f7 100644
--- a/source/blender/editors/space_view3d/view3d_toolbar.c
+++ b/source/blender/editors/space_view3d/view3d_toolbar.c
@@ -55,7 +55,7 @@
 #include "RNA_access.h"
 
 #include "ED_screen.h"
-#include "ED_util.h"
+#include "ED_undo.h"
 
 #include "UI_interface.h"
 #include "UI_resources.h"
diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c
index 4225ee8b425a9f192f78abfed9e193250f374631..1a547d07b8044c71e446838c8bdeee1c7bd80672 100644
--- a/source/blender/editors/space_view3d/view3d_view.c
+++ b/source/blender/editors/space_view3d/view3d_view.c
@@ -918,7 +918,7 @@ int view3d_opengl_select(
 	ARegion *ar = vc->ar;
 	rcti rect;
 	int hits;
-	const bool use_obedit_skip = (OBEDIT_FROM_EVAL_CTX(eval_ctx) != NULL) && (vc->obedit == NULL);
+	const bool use_obedit_skip = (OBEDIT_FROM_VIEW_LAYER(vc->view_layer) != NULL) && (vc->obedit == NULL);
 	const bool is_pick_select = (U.gpu_select_pick_deph != 0);
 	const bool do_passes = (
 	        (is_pick_select == false) &&
@@ -1016,7 +1016,7 @@ int view3d_opengl_select(
 			.gpu_select_mode = gpu_select_mode,
 		};
 		DRW_draw_select_loop(
-		        graph, ar, v3d, eval_ctx->object_mode,
+		        graph, ar, v3d,
 		        use_obedit_skip, use_nearest, &rect,
 		        drw_select_loop_pass, &drw_select_loop_user_data);
 		hits = drw_select_loop_user_data.hits;
@@ -1104,13 +1104,11 @@ int ED_view3d_view_layer_set(int lay, const int *values, int *active)
 static ListBase queue_back;
 static void game_engine_save_state(bContext *C, wmWindow *win)
 {
-	EvaluationContext eval_ctx;
-	CTX_data_eval_ctx(C, &eval_ctx);
 	Object *obact = CTX_data_active_object(C);
 
 	glPushAttrib(GL_ALL_ATTRIB_BITS);
 
-	if (obact && eval_ctx.object_mode & OB_MODE_TEXTURE_PAINT) {
+	if (obact && obact->mode & OB_MODE_TEXTURE_PAINT) {
 		GPU_paint_set_mipmap(1);
 	}
 
@@ -1121,11 +1119,9 @@ static void game_engine_save_state(bContext *C, wmWindow *win)
 
 static void game_engine_restore_state(bContext *C, wmWindow *win)
 {
-	EvaluationContext eval_ctx;
-	CTX_data_eval_ctx(C, &eval_ctx);
 	Object *obact = CTX_data_active_object(C);
 
-	if (obact && eval_ctx.object_mode & OB_MODE_TEXTURE_PAINT) {
+	if (obact && obact->mode & OB_MODE_TEXTURE_PAINT) {
 		GPU_paint_set_mipmap(0);
 	}
 	/* check because closing win can set to NULL */
diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h
index b198918b698f6aa9f41877dd904c85167d6ea24e..fe05207e6450759afaa301e042cf6cdd57b7b297 100644
--- a/source/blender/editors/transform/transform.h
+++ b/source/blender/editors/transform/transform.h
@@ -655,7 +655,7 @@ void restoreBones(TransInfo *t);
 #define MANIPULATOR_AXIS_LINE_WIDTH 2.0f
 
 /* return 0 when no gimbal for selection */
-bool gimbal_axis(struct Object *ob, float gmat[3][3], const eObjectMode object_mode);
+bool gimbal_axis(struct Object *ob, float gmat[3][3]);
 
 /*********************** TransData Creation and General Handling *********** */
 void createTransData(struct bContext *C, TransInfo *t);
diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c
index 5d267767cf93f82ddacf7a293a328392b502d16c..2d7ff1eb5230faa738cdd0d4bbf10f4d8c884a0d 100644
--- a/source/blender/editors/transform/transform_conversions.c
+++ b/source/blender/editors/transform/transform_conversions.c
@@ -998,7 +998,7 @@ static short pose_grab_with_ik(Object *ob)
 	Bone *bonec;
 	short tot_ik = 0;
 
-	if ((ob == NULL) || (ob->pose == NULL))
+	if ((ob == NULL) || (ob->pose == NULL) || (ob->mode & OB_MODE_POSE) == 0)
 		return 0;
 
 	arm = ob->data;
@@ -1893,7 +1893,7 @@ static void createTransParticleVerts(bContext *C, TransInfo *t)
 	TransDataExtension *tx;
 	Object *ob = CTX_data_active_object(C);
 	ParticleEditSettings *pset = PE_settings(t->scene);
-	PTCacheEdit *edit = PE_get_current(t->scene, t->view_layer, ob);
+	PTCacheEdit *edit = PE_get_current(t->scene, ob);
 	ParticleSystem *psys = NULL;
 	ParticleSystemModifierData *psmd = NULL;
 	PTCacheEditPoint *point;
@@ -2010,7 +2010,7 @@ void flushTransParticles(TransInfo *t)
 	Scene *scene = t->scene;
 	ViewLayer *view_layer = t->view_layer;
 	Object *ob = OBACT(view_layer);
-	PTCacheEdit *edit = PE_get_current(scene, view_layer, ob);
+	PTCacheEdit *edit = PE_get_current(scene, ob);
 	ParticleSystem *psys = edit->psys;
 	ParticleSystemModifierData *psmd = NULL;
 	PTCacheEditPoint *point;
@@ -2559,7 +2559,7 @@ static void createTransEditVerts(TransInfo *t)
 	/* detect CrazySpace [tm] */
 	if (modifiers_getCageIndex(t->scene, t->obedit, NULL, 1) != -1) {
 		int totleft = -1;
-		if (modifiers_isCorrectableDeformed(&t->eval_ctx, t->scene, t->obedit)) {
+		if (modifiers_isCorrectableDeformed(t->scene, t->obedit)) {
 			/* check if we can use deform matrices for modifier from the
 			 * start up to stack, they are more accurate than quats */
 			totleft = BKE_crazyspace_get_first_deform_matrices_editbmesh(&t->eval_ctx, t->scene, t->obedit, em, &defmats, &defcos);
@@ -6549,8 +6549,8 @@ void special_aftertrans_update(bContext *C, TransInfo *t)
 	}
 	else if ((t->view_layer->basact) &&
 	         (ob = t->view_layer->basact->object) &&
-	         (t->eval_ctx.object_mode & OB_MODE_PARTICLE_EDIT) &&
-	         PE_get_current(t->scene, t->view_layer, ob))
+	         (ob->mode & OB_MODE_PARTICLE_EDIT) &&
+	         PE_get_current(t->scene, ob))
 	{
 		/* do nothing */
 	}
@@ -8287,28 +8287,26 @@ void createTransData(bContext *C, TransInfo *t)
 			t->poseobj = ob;    /* <- tsk tsk, this is going to give issues one day */
 		}
 	}
-	else if (ob && (t->eval_ctx.object_mode & OB_MODE_POSE)) {
+	else if (ob && (ob->mode & OB_MODE_POSE)) {
 		// XXX this is currently limited to active armature only...
 		// XXX active-layer checking isn't done as that should probably be checked through context instead
 		createTransPose(t, ob);
 	}
-	else if (ob && (t->eval_ctx.object_mode & OB_MODE_WEIGHT_PAINT) && !(t->options & CTX_PAINT_CURVE)) {
+	else if (ob && (ob->mode & OB_MODE_WEIGHT_PAINT) && !(t->options & CTX_PAINT_CURVE)) {
 		/* important that ob_armature can be set even when its not selected [#23412]
 		 * lines below just check is also visible */
 		Object *ob_armature = modifiers_isDeformedByArmature(ob);
-		if (ob_armature) {
-//			const bArmature *arm = ob_armature->data;
+		if (ob_armature && ob_armature->mode & OB_MODE_POSE) {
 			Base *base_arm = BKE_view_layer_base_find(t->view_layer, ob_armature);
 			if (base_arm) {
 				if (BASE_VISIBLE(base_arm)) {
 					createTransPose(t, ob_armature);
 				}
 			}
+			
 		}
 	}
-	else if (ob && (t->eval_ctx.object_mode & OB_MODE_PARTICLE_EDIT) &&
-	         PE_start_edit(PE_get_current(scene, view_layer, ob)))
-	{
+	else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT) && PE_start_edit(PE_get_current(scene, ob))) {
 		createTransParticleVerts(C, t);
 		t->flag |= T_POINTS;
 
@@ -8318,7 +8316,7 @@ void createTransData(bContext *C, TransInfo *t)
 			sort_trans_data_dist(t);
 		}
 	}
-	else if (ob && (t->eval_ctx.object_mode & OB_MODE_ALL_PAINT)) {
+	else if (ob && (ob->mode & OB_MODE_ALL_PAINT)) {
 		if ((t->options & CTX_PAINT_CURVE) && !ELEM(t->mode, TFM_SHEAR, TFM_SHRINKFATTEN)) {
 			t->flag |= T_POINTS | T_2D_EDIT;
 			createTransPaintCurveVerts(C, t);
diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c
index fbe9c2ef2405521aae92b450f8c4e1c651f36ae8..b42e00a2bb4836a0f0efda64418f8c6bedaa2fcc 100644
--- a/source/blender/editors/transform/transform_generics.c
+++ b/source/blender/editors/transform/transform_generics.c
@@ -901,8 +901,8 @@ static void recalcData_objects(TransInfo *t)
 		else
 			BKE_pose_where_is(&t->eval_ctx, t->scene, ob);
 	}
-	else if (base && (t->eval_ctx.object_mode & OB_MODE_PARTICLE_EDIT) &&
-	         PE_get_current(t->scene, t->view_layer, base->object))
+	else if (base && (base->object->mode & OB_MODE_PARTICLE_EDIT) &&
+	         PE_get_current(t->scene, base->object))
 	{
 		if (t->state != TRANS_CANCEL) {
 			applyProject(t);
@@ -1266,7 +1266,7 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
 			}
 		}
 
-		if (ob && t->eval_ctx.object_mode & OB_MODE_ALL_PAINT) {
+		if (ob && ob->mode & OB_MODE_ALL_PAINT) {
 			Paint *p = BKE_paint_get_active_from_context(C);
 			if (p && p->brush && (p->brush->flag & BRUSH_CURVE)) {
 				t->options |= CTX_PAINT_CURVE;
@@ -1815,7 +1815,7 @@ bool calculateCenterActive(TransInfo *t, bool select_only, float r_center[3])
 		}
 	}
 	else if (t->options & CTX_PAINT_CURVE) {
-		Paint *p = BKE_paint_get_active(t->scene, t->view_layer, t->eval_ctx.object_mode);
+		Paint *p = BKE_paint_get_active(t->scene, t->view_layer);
 		Brush *br = p->brush;
 		PaintCurve *pc = br->paint_curve;
 		copy_v3_v3(r_center, pc->points[pc->add_index - 1].bez.vec[1]);
diff --git a/source/blender/editors/transform/transform_manipulator.c b/source/blender/editors/transform/transform_manipulator.c
index 1839583015c841539b1413b47cb0beaf0deca857..8944817bacaa2db53b8e5ae744225a3e69cd3642 100644
--- a/source/blender/editors/transform/transform_manipulator.c
+++ b/source/blender/editors/transform/transform_manipulator.c
@@ -520,9 +520,9 @@ static bool test_rotmode_euler(short rotmode)
 	return (ELEM(rotmode, ROT_MODE_AXISANGLE, ROT_MODE_QUAT)) ? 0 : 1;
 }
 
-bool gimbal_axis(Object *ob, float gmat[3][3], const eObjectMode object_mode)
+bool gimbal_axis(Object *ob, float gmat[3][3])
 {
-	if (object_mode & OB_MODE_POSE) {
+	if (ob->mode & OB_MODE_POSE) {
 		bPoseChannel *pchan = BKE_pose_channel_active(ob);
 
 		if (pchan) {
@@ -591,7 +591,6 @@ static int calc_manipulator_stats(
         const bContext *C, bool use_only_center,
         struct TransformBounds *tbounds)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	ScrArea *sa = CTX_wm_area(C);
 	ARegion *ar = CTX_wm_region(C);
 	Scene *scene = CTX_data_scene(C);
@@ -629,7 +628,7 @@ static int calc_manipulator_stats(
 			case V3D_MANIP_GIMBAL:
 			{
 				float mat[3][3];
-				if (gimbal_axis(ob, mat, workspace->object_mode)) {
+				if (gimbal_axis(ob, mat)) {
 					copy_m4_m3(rv3d->twmat, mat);
 					break;
 				}
@@ -638,7 +637,7 @@ static int calc_manipulator_stats(
 			}
 			case V3D_MANIP_NORMAL:
 			{
-				if (obedit || workspace->object_mode & OB_MODE_POSE) {
+				if (obedit || ob->mode & OB_MODE_POSE) {
 					float mat[3][3];
 					ED_getTransformOrientationMatrix(C, mat, v3d->around);
 					copy_m4_m3(rv3d->twmat, mat);
@@ -649,7 +648,7 @@ static int calc_manipulator_stats(
 			}
 			case V3D_MANIP_LOCAL:
 			{
-				if (workspace->object_mode & OB_MODE_POSE) {
+				if (ob->mode & OB_MODE_POSE) {
 					/* each bone moves on its own local axis, but  to avoid confusion,
 					 * use the active pones axis for display [#33575], this works as expected on a single bone
 					 * and users who select many bones will understand whats going on and what local means
@@ -691,7 +690,7 @@ static int calc_manipulator_stats(
 
 #ifdef USE_AXIS_BOUNDS
 	copy_m3_m4(tbounds->axis, rv3d->twmat);
-	if (ob && workspace->object_mode & OB_MODE_EDIT) {
+	if (ob && ob->mode & OB_MODE_EDIT) {
 		float diff_mat[3][3];
 		copy_m3_m4(diff_mat, ob->obmat);
 		normalize_m3(diff_mat);
@@ -934,7 +933,7 @@ static int calc_manipulator_stats(
 			mul_m4_v3(obedit->obmat, tbounds->max);
 		}
 	}
-	else if (ob && (workspace->object_mode & OB_MODE_POSE)) {
+	else if (ob && (ob->mode & OB_MODE_POSE)) {
 		bPoseChannel *pchan;
 		int mode = TFM_ROTATION; // mislead counting bones... bah. We don't know the manipulator mode, could be mixed
 		bool ok = false;
@@ -972,11 +971,11 @@ static int calc_manipulator_stats(
 			mul_m4_v3(ob->obmat, tbounds->max);
 		}
 	}
-	else if (ob && (workspace->object_mode & OB_MODE_ALL_PAINT)) {
+	else if (ob && (ob->mode & OB_MODE_ALL_PAINT)) {
 		/* pass */
 	}
-	else if (ob && workspace->object_mode & OB_MODE_PARTICLE_EDIT) {
-		PTCacheEdit *edit = PE_get_current(scene, view_layer, ob);
+	else if (ob && ob->mode & OB_MODE_PARTICLE_EDIT) {
+		PTCacheEdit *edit = PE_get_current(scene, ob);
 		PTCacheEditPoint *point;
 		PTCacheEditKey *ek;
 		int k;
@@ -1065,13 +1064,12 @@ static void manipulator_prepare_mat(
 		case V3D_AROUND_CENTER_BOUNDS:
 		case V3D_AROUND_ACTIVE:
 		{
-			const WorkSpace *workspace = CTX_wm_workspace(C);
 			bGPdata *gpd = CTX_data_gpencil_data(C);
 			Object *ob = OBACT(view_layer);
 
-			if (((v3d->around == V3D_AROUND_ACTIVE) && ((workspace->object_mode & OB_MODE_EDIT) == 0)) &&
+			if (((v3d->around == V3D_AROUND_ACTIVE) && (OBEDIT_FROM_OBACT(ob) == NULL)) &&
 			    ((gpd == NULL) || !(gpd->flag & GP_DATA_STROKE_EDITMODE)) &&
-			    (!(workspace->object_mode & OB_MODE_POSE)))
+			    (!(ob->mode & OB_MODE_POSE)))
 			{
 				copy_v3_v3(rv3d->twmat[3], ob->obmat[3]);
 			}
@@ -1660,10 +1658,10 @@ static void WIDGETGROUP_xform_cage_draw_prepare(const bContext *C, wmManipulator
 {
 	struct XFormCageWidgetGroup *xmgroup = mgroup->customdata;
 	wmManipulator *mpr = xmgroup->manipulator;
-	const WorkSpace *workspace = CTX_wm_workspace(C);
+
 	ViewLayer *view_layer = CTX_data_view_layer(C);
 	Object *ob = OBACT(view_layer);
-	if (ob && workspace->object_mode & OB_MODE_EDIT) {
+	if (ob && ob->mode & OB_MODE_EDIT) {
 		copy_m4_m4(mpr->matrix_space, ob->obmat);
 	}
 	else {
diff --git a/source/blender/editors/transform/transform_orientations.c b/source/blender/editors/transform/transform_orientations.c
index 63eea11212c84554163dffde8300ba8875f5f7db..7b3f91b81dac994b9d17c8aae98312931f41c514 100644
--- a/source/blender/editors/transform/transform_orientations.c
+++ b/source/blender/editors/transform/transform_orientations.c
@@ -55,8 +55,6 @@
 #include "BKE_screen.h"
 #include "BKE_workspace.h"
 
-#include "DEG_depsgraph.h"
-
 #include "BLT_translation.h"
 
 #include "ED_armature.h"
@@ -298,7 +296,6 @@ void BIF_createTransformOrientation(bContext *C, ReportList *reports,
 		ts = createViewSpace(C, reports, name, overwrite);
 	}
 	else {
-		const WorkSpace *workspace = CTX_wm_workspace(C);
 		Object *obedit = CTX_data_edit_object(C);
 		Object *ob = CTX_data_active_object(C);
 		if (obedit) {
@@ -309,7 +306,7 @@ void BIF_createTransformOrientation(bContext *C, ReportList *reports,
 			else if (obedit->type == OB_CURVE)
 				ts = createCurveSpace(C, reports, name, overwrite);
 		}
-		else if (ob && (workspace->object_mode & OB_MODE_POSE)) {
+		else if (ob && (ob->mode & OB_MODE_POSE)) {
 			ts = createBoneSpace(C, reports, name, overwrite);
 		}
 		else {
@@ -444,13 +441,13 @@ void initTransformOrientation(bContext *C, TransInfo *t)
 
 		case V3D_MANIP_GIMBAL:
 			unit_m3(t->spacemtx);
-			if (ob && gimbal_axis(ob, t->spacemtx, t->eval_ctx.object_mode)) {
+			if (ob && gimbal_axis(ob, t->spacemtx)) {
 				BLI_strncpy(t->spacename, IFACE_("gimbal"), sizeof(t->spacename));
 				break;
 			}
 			ATTR_FALLTHROUGH;  /* no gimbal fallthrough to normal */
 		case V3D_MANIP_NORMAL:
-			if (obedit || (ob && t->eval_ctx.object_mode & OB_MODE_POSE)) {
+			if (obedit || (ob && ob->mode & OB_MODE_POSE)) {
 				BLI_strncpy(t->spacename, IFACE_("normal"), sizeof(t->spacename));
 				ED_getTransformOrientationMatrix(C, t->spacemtx, t->around);
 				break;
@@ -581,7 +578,6 @@ static unsigned int bm_mesh_faces_select_get_n(BMesh *bm, BMVert **elems, const
 
 int getTransformOrientation_ex(const bContext *C, float normal[3], float plane[3], const short around)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	ViewLayer *view_layer = CTX_data_view_layer(C);
 	Object *obedit = CTX_data_edit_object(C);
 	Base *base;
@@ -915,7 +911,7 @@ int getTransformOrientation_ex(const bContext *C, float normal[3], float plane[3
 								}
 								else if (is_next_sel) {
 									/* A segment, add the edge normal */
-									sub_v3_v3v3(tvec, bp->vec, bp_next->vec	);
+									sub_v3_v3v3(tvec, bp->vec, bp_next->vec);
 									normalize_v3(tvec);
 									add_v3_v3(normal, tvec);
 								}
@@ -1012,7 +1008,7 @@ int getTransformOrientation_ex(const bContext *C, float normal[3], float plane[3
 			mul_m3_v3(mat, plane);
 		}
 	}
-	else if (ob && (workspace->object_mode & OB_MODE_POSE)) {
+	else if (ob && (ob->mode & OB_MODE_POSE)) {
 		bArmature *arm = ob->data;
 		bPoseChannel *pchan;
 		float imat[3][3], mat[3][3];
@@ -1052,7 +1048,7 @@ int getTransformOrientation_ex(const bContext *C, float normal[3], float plane[3
 			result = ORIENTATION_EDGE;
 		}
 	}
-	else if (ob && (workspace->object_mode & (OB_MODE_ALL_PAINT | OB_MODE_PARTICLE_EDIT))) {
+	else if (ob && (ob->mode & (OB_MODE_ALL_PAINT | OB_MODE_PARTICLE_EDIT))) {
 		/* pass */
 	}
 	else {
diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c
index 9946f1ad546086fb702363b932ccc76d9998f8bb..ad913cd23368b9e0a3d67756c33eeb065798d284 100644
--- a/source/blender/editors/transform/transform_snap.c
+++ b/source/blender/editors/transform/transform_snap.c
@@ -544,7 +544,7 @@ static void initSnappingMode(TransInfo *t)
 		}
 		/* Particles edit mode*/
 		else if (t->tsnap.applySnap != NULL && // A snapping function actually exist
-		         (obedit == NULL && base_act && base_act->object && t->eval_ctx.object_mode & OB_MODE_PARTICLE_EDIT))
+		         (obedit == NULL && base_act && base_act->object && base_act->object->mode & OB_MODE_PARTICLE_EDIT))
 		{
 			t->tsnap.modeSelect = SNAP_ALL;
 		}
diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c
index 0020b0924bbd6d6b627ca538865596af037b3df5..c336cd1d31edff5c87fc4af8d66408c244a3dd00 100644
--- a/source/blender/editors/transform/transform_snap_object.c
+++ b/source/blender/editors/transform/transform_snap_object.c
@@ -821,7 +821,7 @@ static bool raycastObjects(
         Object **r_ob, float r_obmat[4][4],
         ListBase *r_hit_list)
 {
-	Object *obedit = use_object_edit_cage ? OBEDIT_FROM_EVAL_CTX(&sctx->eval_ctx) : NULL;
+	Object *obedit = use_object_edit_cage ? OBEDIT_FROM_VIEW_LAYER(sctx->eval_ctx.view_layer) : NULL;
 
 	struct RaycastObjUserData data = {
 		.ray_start = ray_start,
@@ -2054,7 +2054,7 @@ static bool snapObjectsRay(
         float r_loc[3], float r_no[3],
         Object **r_ob, float r_obmat[4][4])
 {
-	Object *obedit = use_object_edit_cage ? OBEDIT_FROM_EVAL_CTX(&sctx->eval_ctx) : NULL;
+	Object *obedit = use_object_edit_cage ? OBEDIT_FROM_VIEW_LAYER(sctx->eval_ctx.view_layer) : NULL;
 
 	struct SnapObjUserData data = {
 		.snapdata = snapdata,
@@ -2089,7 +2089,7 @@ SnapObjectContext *ED_transform_snap_object_context_create(
 	sctx->scene = scene;
 
 	DEG_evaluation_context_init_from_scene(
-	        &sctx->eval_ctx, scene, view_layer, engine_type, OB_MODE_OBJECT, DAG_EVAL_VIEWPORT);
+	        &sctx->eval_ctx, scene, view_layer, engine_type, DAG_EVAL_VIEWPORT);
 
 	sctx->cache.object_map = BLI_ghash_ptr_new(__func__);
 	sctx->cache.mem_arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
diff --git a/source/blender/editors/undo/CMakeLists.txt b/source/blender/editors/undo/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..89832604ed8c77b41b9a3679b68c6caa22237f8b
--- /dev/null
+++ b/source/blender/editors/undo/CMakeLists.txt
@@ -0,0 +1,45 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# Contributor(s): Jacques Beaurain.
+#
+# ***** END GPL LICENSE BLOCK *****
+
+set(INC
+	../include
+	../../blenkernel
+	../../blenlib
+	../../blentranslation
+	../../makesdna
+	../../makesrna
+	../../windowmanager
+	../../../../intern/guardedalloc
+	../../../../intern/clog
+)
+
+set(SRC
+	ed_undo.c
+	memfile_undo.c
+	undo_system_types.c
+
+	undo_intern.h
+)
+
+if(WITH_INTERNATIONAL)
+	add_definitions(-DWITH_INTERNATIONAL)
+endif()
+
+blender_add_lib(bf_editor_undo "${SRC}" "${INC}" "${INC_SYS}")
diff --git a/source/blender/editors/util/undo.c b/source/blender/editors/undo/ed_undo.c
similarity index 52%
rename from source/blender/editors/util/undo.c
rename to source/blender/editors/undo/ed_undo.c
index b7101e7ae99424d9af73f382244200a9f1daff27..d8b194e33363010956c3037a4e498b3a080e87c3 100644
--- a/source/blender/editors/util/undo.c
+++ b/source/blender/editors/undo/ed_undo.c
@@ -25,17 +25,16 @@
  * ***** END GPL LICENSE BLOCK *****
  */
 
-/** \file blender/editors/util/undo.c
- *  \ingroup edutil
+/** \file blender/editors/undo/ed_undo.c
+ *  \ingroup edundo
  */
 
-#include <stdlib.h>
 #include <string.h>
-#include <math.h>
 
 #include "MEM_guardedalloc.h"
 
-#include "DNA_object_types.h"
+#include "CLG_log.h"
+
 #include "DNA_scene_types.h"
 
 #include "BLI_utildefines.h"
@@ -45,25 +44,14 @@
 #include "BKE_blender_undo.h"
 #include "BKE_context.h"
 #include "BKE_global.h"
-#include "BKE_library_override.h"
 #include "BKE_main.h"
 #include "BKE_screen.h"
+#include "BKE_undo_system.h"
 
-#include "DEG_depsgraph.h"
-
-#include "ED_armature.h"
-#include "ED_particle.h"
-#include "ED_curve.h"
 #include "ED_gpencil.h"
-#include "ED_lattice.h"
-#include "ED_mball.h"
-#include "ED_mesh.h"
-#include "ED_object.h"
 #include "ED_render.h"
 #include "ED_screen.h"
-#include "ED_paint.h"
-#include "ED_util.h"
-#include "ED_text.h"
+#include "ED_undo.h"
 
 #include "WM_api.h"
 #include "WM_types.h"
@@ -74,48 +62,37 @@
 #include "UI_interface.h"
 #include "UI_resources.h"
 
-#include "util_intern.h"
+/** We only need this locally. */
+static CLG_LogRef LOG = {"ed.undo"};
 
-/* ***************** generic undo system ********************* */
+/* -------------------------------------------------------------------- */
+/** \name Generic Undo System Access
+ *
+ * Non-operator undo editor functions.
+ * \{ */
 
 void ED_undo_push(bContext *C, const char *str)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
-	Object *obedit = CTX_data_edit_object(C);
-	Object *obact = CTX_data_active_object(C);
-
-	if (G.debug & G_DEBUG)
-		printf("%s: %s\n", __func__, str);
-
-	/* Always do it for now, this might need to be refined... */
-	BKE_main_override_static_operations_create(CTX_data_main(C));
-
-	if (obedit) {
-		if (U.undosteps == 0) return;
-		
-		if (obedit->type == OB_MESH)
-			undo_push_mesh(C, str);
-		else if (ELEM(obedit->type, OB_CURVE, OB_SURF))
-			undo_push_curve(C, str);
-		else if (obedit->type == OB_FONT)
-			undo_push_font(C, str);
-		else if (obedit->type == OB_MBALL)
-			undo_push_mball(C, str);
-		else if (obedit->type == OB_LATTICE)
-			undo_push_lattice(C, str);
-		else if (obedit->type == OB_ARMATURE)
-			undo_push_armature(C, str);
-	}
-	else if (obact && workspace->object_mode & OB_MODE_PARTICLE_EDIT) {
-		if (U.undosteps == 0) return;
+	CLOG_INFO(&LOG, 1, "name='%s'", str);
 
-		PE_undo_push(CTX_data_scene(C), CTX_data_view_layer(C), str);
+	const int steps = U.undosteps;
+
+	if (steps <= 0) {
+		return;
 	}
-	else if (obact && workspace->object_mode & OB_MODE_SCULPT) {
-		/* do nothing for now */
+
+	wmWindowManager *wm = CTX_wm_manager(C);
+
+	/* Only apply limit if this is the last undo step. */
+	if (wm->undo_stack->step_active && (wm->undo_stack->step_active->next == NULL)) {
+		BKE_undosys_stack_limit_steps_and_memory(wm->undo_stack, steps - 1, 0);
 	}
-	else {
-		BKE_undo_write(C, str);
+
+	BKE_undosys_step_push(wm->undo_stack, C, str);
+
+	if (U.undomemory != 0) {
+		const size_t memory_limit = (size_t)U.undomemory * 1024 * 1024;
+		BKE_undosys_stack_limit_steps_and_memory(wm->undo_stack, 0, memory_limit);
 	}
 
 	WM_file_tag_modified();
@@ -124,15 +101,11 @@ void ED_undo_push(bContext *C, const char *str)
 /* note: also check undo_history_exec() in bottom if you change notifiers */
 static int ed_undo_step(bContext *C, int step, const char *undoname)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
+	CLOG_INFO(&LOG, 1, "name='%s', step=%d", undoname, step);
 	wmWindowManager *wm = CTX_wm_manager(C);
 	wmWindow *win = CTX_wm_window(C);
-	Main *bmain = CTX_data_main(C);
+	// Main *bmain = CTX_data_main(C);
 	Scene *scene = CTX_data_scene(C);
-	ViewLayer *view_layer = CTX_data_view_layer(C);
-	Object *obedit = CTX_data_edit_object(C);
-	Object *obact = CTX_data_active_object(C);
-	ScrArea *sa = CTX_wm_area(C);
 
 	/* undo during jobs are running can easily lead to freeing data using by jobs,
 	 * or they can just lead to freezing job in some other cases */
@@ -140,100 +113,45 @@ static int ed_undo_step(bContext *C, int step, const char *undoname)
 		return OPERATOR_CANCELLED;
 	}
 
+	/* TODO(campbell): undo_system: use undo system */
 	/* grease pencil can be can be used in plenty of spaces, so check it first */
 	if (ED_gpencil_session_active()) {
 		return ED_undo_gpencil_step(C, step, undoname);
 	}
 
-	if (sa && (sa->spacetype == SPACE_IMAGE)) {
-		SpaceImage *sima = (SpaceImage *)sa->spacedata.first;
-		
-		if ((obact && (workspace->object_mode & OB_MODE_TEXTURE_PAINT)) || (sima->mode == SI_MODE_PAINT)) {
-			if (!ED_undo_paint_step(C, UNDO_PAINT_IMAGE, step, undoname) && undoname) {
-				if (U.uiflag & USER_GLOBALUNDO) {
-					ED_viewport_render_kill_jobs(wm, bmain, true);
-					BKE_undo_name(C, undoname);
-				}
-			}
-			
-			WM_event_add_notifier(C, NC_WINDOW, NULL);
-			return OPERATOR_FINISHED;
-		}
-	}
-
-	if (sa && (sa->spacetype == SPACE_TEXT)) {
-		ED_text_undo_step(C, step);
-	}
-	else if (obedit) {
-		if (OB_TYPE_SUPPORT_EDITMODE(obedit->type)) {
-			if (undoname)
-				undo_editmode_name(C, undoname);
-			else
-				undo_editmode_step(C, step);
-			
-			WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
-		}
-	}
-	else {
-		/* Note: we used to do a fall-through here where if the
-		 * mode-specific undo system had no more steps to undo (or
-		 * redo), the global undo would run.
-		 *
-		 * That was inconsistent with editmode, and also makes for
-		 * unecessarily tricky interaction with the other undo
-		 * systems. */
-		if (obact && workspace->object_mode & OB_MODE_TEXTURE_PAINT) {
-			ED_undo_paint_step(C, UNDO_PAINT_IMAGE, step, undoname);
-		}
-		else if (obact && workspace->object_mode & OB_MODE_SCULPT) {
-			ED_undo_paint_step(C, UNDO_PAINT_MESH, step, undoname);
+	/* Undo System */
+	{
+		if (undoname) {
+			UndoStep *step_data = BKE_undosys_step_find_by_name(wm->undo_stack, undoname);
+			BKE_undosys_step_undo_with_data(wm->undo_stack, C, step_data);
 		}
-		else if (obact && workspace->object_mode & OB_MODE_PARTICLE_EDIT) {
-			if (step == 1)
-				PE_undo(scene, view_layer);
-			else
-				PE_redo(scene, view_layer);
-		}
-		else if (U.uiflag & USER_GLOBALUNDO) {
-			// note python defines not valid here anymore.
-			//#ifdef WITH_PYTHON
-			// XXX		BPY_scripts_clear_pyobjects();
-			//#endif
-			
-			/* for global undo/redo we should just clear the editmode stack */
-			/* for example, texface stores image pointers */
-			undo_editmode_clear();
-			
-			ED_viewport_render_kill_jobs(wm, bmain, true);
-
-			if (undoname)
-				BKE_undo_name(C, undoname);
-			else
-				BKE_undo_step(C, step);
-
-			scene = CTX_data_scene(C);
-				
-			WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene);
+		else {
+			BKE_undosys_step_undo_compat_only(wm->undo_stack, C, step);
 		}
 	}
-	
+
 	WM_event_add_notifier(C, NC_WINDOW, NULL);
 	WM_event_add_notifier(C, NC_WM | ND_UNDO, NULL);
 
 	if (win) {
 		win->addmousemove = true;
 	}
-	
+
 	return OPERATOR_FINISHED;
 }
 
 void ED_undo_grouped_push(bContext *C, const char *str)
 {
 	/* do nothing if previous undo task is the same as this one (or from the same undo group) */
-	const char *last_undo = BKE_undo_get_name_last();
+	{
+		wmWindowManager *wm = CTX_wm_manager(C);
+		if (wm->undo_stack->steps.last) {
+			const UndoStep *us = wm->undo_stack->steps.last;
+			if (STREQ(str, us->name)) {
+				return;
+			}
+		}
 
-	if (last_undo && STREQ(str, last_undo)) {
-		return;
 	}
 
 	/* push as usual */
@@ -274,50 +192,29 @@ void ED_undo_pop_op(bContext *C, wmOperator *op)
 /* name optionally, function used to check for operator redo panel */
 bool ED_undo_is_valid(const bContext *C, const char *undoname)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
-	Object *obedit = CTX_data_edit_object(C);
-	Object *obact = CTX_data_active_object(C);
-	ScrArea *sa = CTX_wm_area(C);
-	
-	if (sa && sa->spacetype == SPACE_IMAGE) {
-		SpaceImage *sima = (SpaceImage *)sa->spacedata.first;
-		
-		if ((obact && (workspace->object_mode & OB_MODE_TEXTURE_PAINT)) || (sima->mode == SI_MODE_PAINT)) {
-			return 1;
-		}
-	}
-	
-	if (sa && (sa->spacetype == SPACE_TEXT)) {
-		return 1;
-	}
-	else if (obedit) {
-		if (OB_TYPE_SUPPORT_EDITMODE(obedit->type)) {
-			return undo_editmode_is_valid(undoname);
-		}
-	}
-	else {
-		
-		/* if below tests fail, global undo gets executed */
-		
-		if (obact && workspace->object_mode & OB_MODE_TEXTURE_PAINT) {
-			if (ED_undo_paint_is_valid(UNDO_PAINT_IMAGE, undoname))
-				return 1;
-		}
-		else if (obact && workspace->object_mode & OB_MODE_SCULPT) {
-			if (ED_undo_paint_is_valid(UNDO_PAINT_MESH, undoname))
-				return 1;
-		}
-		else if (obact && workspace->object_mode & OB_MODE_PARTICLE_EDIT) {
-			return PE_undo_is_valid(CTX_data_scene(C), CTX_data_view_layer(C));
-		}
-		
-		if (U.uiflag & USER_GLOBALUNDO) {
-			return BKE_undo_is_valid(undoname);
-		}
-	}
-	return 0;
+	wmWindowManager *wm = CTX_wm_manager(C);
+	return BKE_undosys_stack_has_undo(wm->undo_stack, undoname);
+}
+
+/**
+ * Ideally we wont access the stack directly,
+ * this is needed for modes which handle undo themselves (bypassing #ED_undo_push).
+ *
+ * Using global isn't great, this just avoids doing inline,
+ * causing 'BKE_global.h' & 'BKE_main.h' includes.
+ */
+UndoStack *ED_undo_stack_get(void)
+{
+	wmWindowManager *wm = G.main->wm.first;
+	return wm->undo_stack;
 }
 
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Undo, Undo Push & Redo Operators
+ * \{ */
+
 static int ed_undo_exec(bContext *C, wmOperator *UNUSED(op))
 {
 	/* "last operator" should disappear, later we can tie this with undo stack nicer */
@@ -348,19 +245,17 @@ static int ed_undo_redo_exec(bContext *C, wmOperator *UNUSED(op))
 static int ed_undo_redo_poll(bContext *C)
 {
 	wmOperator *last_op = WM_operator_last_redo(C);
-	return last_op && ED_operator_screenactive(C) && 
+	return last_op && ED_operator_screenactive(C) &&
 		WM_operator_check_ui_enabled(C, last_op->type->name);
 }
 
-/* ********************** */
-
 void ED_OT_undo(wmOperatorType *ot)
 {
 	/* identifiers */
 	ot->name = "Undo";
 	ot->description = "Undo previous action";
 	ot->idname = "ED_OT_undo";
-	
+
 	/* api callbacks */
 	ot->exec = ed_undo_exec;
 	ot->poll = ED_operator_screenactive;
@@ -372,7 +267,7 @@ void ED_OT_undo_push(wmOperatorType *ot)
 	ot->name = "Undo Push";
 	ot->description = "Add an undo state (internal use only)";
 	ot->idname = "ED_OT_undo_push";
-	
+
 	/* api callbacks */
 	ot->exec = ed_undo_push_exec;
 
@@ -387,7 +282,7 @@ void ED_OT_redo(wmOperatorType *ot)
 	ot->name = "Redo";
 	ot->description = "Redo previous action";
 	ot->idname = "ED_OT_redo";
-	
+
 	/* api callbacks */
 	ot->exec = ed_redo_exec;
 	ot->poll = ED_operator_screenactive;
@@ -399,18 +294,25 @@ void ED_OT_undo_redo(wmOperatorType *ot)
 	ot->name = "Undo and Redo";
 	ot->description = "Undo and redo previous action";
 	ot->idname = "ED_OT_undo_redo";
-	
+
 	/* api callbacks */
 	ot->exec = ed_undo_redo_exec;
 	ot->poll = ed_undo_redo_poll;
 }
 
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Operator Repeat
+ * \{ */
+
 /* ui callbacks should call this rather than calling WM_operator_repeat() themselves */
 int ED_undo_operator_repeat(bContext *C, struct wmOperator *op)
 {
 	int ret = 0;
 
 	if (op) {
+		CLOG_INFO(&LOG, 1, "idname='%s'", op->type->idname);
 		wmWindowManager *wm = CTX_wm_manager(C);
 		struct Scene *scene = CTX_data_scene(C);
 
@@ -471,9 +373,7 @@ int ED_undo_operator_repeat(bContext *C, struct wmOperator *op)
 		CTX_wm_region_set(C, ar);
 	}
 	else {
-		if (G.debug & G_DEBUG) {
-			printf("redo_cb: ED_undo_operator_repeat called with NULL 'op'\n");
-		}
+		CLOG_WARN(&LOG, "called with NULL 'op'");
 	}
 
 	return ret;
@@ -490,115 +390,50 @@ void ED_undo_operator_repeat_cb_evt(bContext *C, void *arg_op, int UNUSED(arg_ev
 	ED_undo_operator_repeat(C, (wmOperator *)arg_op);
 }
 
+/** \} */
 
-/* ************************** */
-
-enum {
-	UNDOSYSTEM_GLOBAL   = 1,
-	UNDOSYSTEM_EDITMODE = 2,
-	UNDOSYSTEM_PARTICLE = 3,
-	UNDOSYSTEM_IMAPAINT = 4,
-	UNDOSYSTEM_SCULPT   = 5,
-};
-
-static int get_undo_system(bContext *C)
-{
-	const WorkSpace *workspace = CTX_wm_workspace(C);
-	Object *obact = CTX_data_active_object(C);
-	Object *obedit = CTX_data_edit_object(C);
-	ScrArea *sa = CTX_wm_area(C);
-
-	/* first check for editor undo */
-	if (sa && (sa->spacetype == SPACE_IMAGE)) {
-		SpaceImage *sima = (SpaceImage *)sa->spacedata.first;
-
-		if ((obact && (workspace->object_mode & OB_MODE_TEXTURE_PAINT)) || (sima->mode == SI_MODE_PAINT)) {
-			if (!ED_undo_paint_empty(UNDO_PAINT_IMAGE))
-				return UNDOSYSTEM_IMAPAINT;
-		}
-	}
-	/* find out which undo system */
-	if (obedit) {
-		if (OB_TYPE_SUPPORT_EDITMODE(obedit->type)) {
-			return UNDOSYSTEM_EDITMODE;
-		}
-	}
-	else {
-		if (obact) {
-			if (workspace->object_mode & OB_MODE_PARTICLE_EDIT)
-				return UNDOSYSTEM_PARTICLE;
-			else if (workspace->object_mode & OB_MODE_TEXTURE_PAINT) {
-				if (!ED_undo_paint_empty(UNDO_PAINT_IMAGE))
-					return UNDOSYSTEM_IMAPAINT;
-			}
-			else if (workspace->object_mode & OB_MODE_SCULPT) {
-				if (!ED_undo_paint_empty(UNDO_PAINT_MESH))
-					return UNDOSYSTEM_SCULPT;
-			}
-		}
-		if (U.uiflag & USER_GLOBALUNDO)
-			return UNDOSYSTEM_GLOBAL;
-	}
-	
-	return 0;
-}
+/* -------------------------------------------------------------------- */
+/** \name Undo History Operator
+ * \{ */
 
 /* create enum based on undo items */
-static const EnumPropertyItem *rna_undo_itemf(bContext *C, int undosys, int *totitem)
+static const EnumPropertyItem *rna_undo_itemf(bContext *C, int *totitem)
 {
 	EnumPropertyItem item_tmp = {0}, *item = NULL;
 	int i = 0;
-	bool active;
-	
-	while (true) {
-		const char *name = NULL;
-		
-		if (undosys == UNDOSYSTEM_PARTICLE) {
-			name = PE_undo_get_name(CTX_data_scene(C), CTX_data_view_layer(C), i, &active);
-		}
-		else if (undosys == UNDOSYSTEM_EDITMODE) {
-			name = undo_editmode_get_name(C, i, &active);
-		}
-		else if (undosys == UNDOSYSTEM_IMAPAINT) {
-			name = ED_undo_paint_get_name(C, UNDO_PAINT_IMAGE, i, &active);
-		}
-		else if (undosys == UNDOSYSTEM_SCULPT) {
-			name = ED_undo_paint_get_name(C, UNDO_PAINT_MESH, i, &active);
-		}
-		else {
-			name = BKE_undo_get_name(i, &active);
-		}
-		
-		if (name) {
-			item_tmp.identifier = name;
-			/* XXX This won't work with non-default contexts (e.g. operators) :/ */
-			item_tmp.name = IFACE_(name);
-			if (active)
+
+	wmWindowManager *wm = CTX_wm_manager(C);
+	if (wm->undo_stack == NULL) {
+		return NULL;
+	}
+
+	for (UndoStep *us = wm->undo_stack->steps.first; us; us = us->next, i++) {
+		if (us->skip == false) {
+			item_tmp.identifier = us->name;
+			item_tmp.name = IFACE_(us->name);
+			if (us == wm->undo_stack->step_active) {
 				item_tmp.icon = ICON_RESTRICT_VIEW_OFF;
-			else 
+			}
+			else {
 				item_tmp.icon = ICON_NONE;
-			item_tmp.value = i++;
+			}
+			item_tmp.value = i;
 			RNA_enum_item_add(&item, totitem, &item_tmp);
 		}
-		else
-			break;
 	}
-	
 	RNA_enum_item_end(&item, totitem);
-	
+
 	return item;
 }
 
 
 static int undo_history_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
 {
-	int undosys, totitem = 0;
-	
-	undosys = get_undo_system(C);
-	
-	if (undosys) {
-		const EnumPropertyItem *item = rna_undo_itemf(C, undosys, &totitem);
-		
+	int totitem = 0;
+
+	{
+		const EnumPropertyItem *item = rna_undo_itemf(C, &totitem);
+
 		if (totitem > 0) {
 			uiPopupMenu *pup = UI_popup_menu_begin(C, RNA_struct_ui_name(op->type->srna), ICON_NONE);
 			uiLayout *layout = UI_popup_menu_layout(pup);
@@ -607,7 +442,7 @@ static int undo_history_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE
 			const int col_size = 20 + totitem / 12;
 			int i, c;
 			bool add_col = true;
-			
+
 			for (c = 0, i = totitem; i--;) {
 				if (add_col && !(c % col_size)) {
 					column = uiLayoutColumn(split, false);
@@ -619,12 +454,12 @@ static int undo_history_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE
 					add_col = true;
 				}
 			}
-			
+
 			MEM_freeN((void *)item);
-			
+
 			UI_popup_menu_end(C, pup);
 		}
-		
+
 	}
 	return OPERATOR_CANCELLED;
 }
@@ -632,30 +467,12 @@ static int undo_history_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE
 /* note: also check ed_undo_step() in top if you change notifiers */
 static int undo_history_exec(bContext *C, wmOperator *op)
 {
-	if (RNA_struct_property_is_set(op->ptr, "item")) {
-		int undosys = get_undo_system(C);
-		int item = RNA_int_get(op->ptr, "item");
-		
-		if (undosys == UNDOSYSTEM_PARTICLE) {
-			PE_undo_number(CTX_data_scene(C), CTX_data_view_layer(C), item);
-		}
-		else if (undosys == UNDOSYSTEM_EDITMODE) {
-			undo_editmode_number(C, item + 1);
-			WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
-		}
-		else if (undosys == UNDOSYSTEM_IMAPAINT) {
-			ED_undo_paint_step_num(C, UNDO_PAINT_IMAGE, item);
-		}
-		else if (undosys == UNDOSYSTEM_SCULPT) {
-			ED_undo_paint_step_num(C, UNDO_PAINT_MESH, item);
-		}
-		else {
-			ED_viewport_render_kill_jobs(CTX_wm_manager(C), CTX_data_main(C), true);
-			BKE_undo_number(C, item);
-			WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, CTX_data_scene(C));
-		}
+	PropertyRNA *prop = RNA_struct_find_property(op->ptr, "item");
+	if (RNA_property_is_set(op->ptr, prop)) {
+		int item = RNA_property_int_get(op->ptr, prop);
+		wmWindowManager *wm = CTX_wm_manager(C);
+		BKE_undosys_step_undo_from_index(wm->undo_stack, C, item);
 		WM_event_add_notifier(C, NC_WINDOW, NULL);
-		
 		return OPERATOR_FINISHED;
 	}
 	return OPERATOR_CANCELLED;
@@ -667,14 +484,14 @@ void ED_OT_undo_history(wmOperatorType *ot)
 	ot->name = "Undo History";
 	ot->description = "Redo specific action in history";
 	ot->idname = "ED_OT_undo_history";
-	
+
 	/* api callbacks */
 	ot->invoke = undo_history_invoke;
 	ot->exec = undo_history_exec;
 	ot->poll = ED_operator_screenactive;
-	
+
 	RNA_def_int(ot->srna, "item", 0, 0, INT_MAX, "Item", "", 0, INT_MAX);
 
 }
 
-
+/** \} */
diff --git a/source/blender/editors/undo/memfile_undo.c b/source/blender/editors/undo/memfile_undo.c
new file mode 100644
index 0000000000000000000000000000000000000000..f4ed96f98f3d17eddca8af6c1c3c8fcddc0aa791
--- /dev/null
+++ b/source/blender/editors/undo/memfile_undo.c
@@ -0,0 +1,150 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/undo/memfile_undo.c
+ *  \ingroup edundo
+ *
+ * Wrapper between 'ED_undo.h' and 'BKE_undo_system.h' API's.
+ */
+
+#include "BLI_utildefines.h"
+#include "BLI_sys_types.h"
+
+#include "DNA_object_enums.h"
+
+#include "BKE_blender_undo.h"
+#include "BKE_context.h"
+#include "BKE_undo_system.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_object.h"
+#include "ED_undo.h"
+#include "ED_render.h"
+
+
+#include "../blenloader/BLO_undofile.h"
+
+#include "undo_intern.h"
+
+/* -------------------------------------------------------------------- */
+/** \name Implements ED Undo System
+ * \{ */
+
+typedef struct MemFileUndoStep {
+	UndoStep step;
+	MemFileUndoData *data;
+} MemFileUndoStep;
+
+static bool memfile_undosys_poll(bContext *UNUSED(C))
+{
+	/* other poll functions must run first, this is a catch-all. */
+
+	if ((U.uiflag & USER_GLOBALUNDO) == 0) {
+		return false;
+	}
+	return true;
+}
+
+static bool memfile_undosys_step_encode(struct bContext *C, UndoStep *us_p)
+{
+	MemFileUndoStep *us = (MemFileUndoStep *)us_p;
+
+	/* Important we only use 'main' from the context (see: BKE_undosys_stack_init_from_main). */
+	struct Main *bmain = CTX_data_main(C);
+
+	/* can be NULL, use when set. */
+	MemFileUndoStep *us_prev = (MemFileUndoStep *)BKE_undosys_step_same_type_prev(us_p);
+	us->data = BKE_memfile_undo_encode(bmain, us_prev ? us_prev->data : NULL);
+	us->step.data_size = us->data->undo_size;
+
+	return true;
+}
+
+static void memfile_undosys_step_decode(struct bContext *C, UndoStep *us_p, int UNUSED(dir))
+{
+	/* Loading the content will correctly switch into compatible non-object modes. */
+	ED_object_mode_set(C, OB_MODE_OBJECT);
+
+	/* This is needed so undoing/redoing doesn't crash with threaded previews going */
+	ED_viewport_render_kill_jobs(CTX_wm_manager(C), CTX_data_main(C), true);
+	MemFileUndoStep *us = (MemFileUndoStep *)us_p;
+	BKE_memfile_undo_decode(us->data, C);
+
+	WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, CTX_data_scene(C));
+}
+
+static void memfile_undosys_step_free(UndoStep *us_p)
+{
+	/* To avoid unnecessary slow down, free backwards (so we don't need to merge when clearing all). */
+	MemFileUndoStep *us = (MemFileUndoStep *)us_p;
+	if (us_p->next != NULL) {
+		UndoStep *us_next_p = BKE_undosys_step_same_type_next(us_p);
+		if (us_next_p != NULL) {
+			MemFileUndoStep *us_next = (MemFileUndoStep *)us_next_p;
+			BLO_memfile_merge(&us->data->memfile, &us_next->data->memfile);
+		}
+	}
+
+	BKE_memfile_undo_free(us->data);
+}
+
+/* Export for ED_undo_sys. */
+void ED_memfile_undosys_type(UndoType *ut)
+{
+	ut->name = "Global Undo";
+	ut->poll = memfile_undosys_poll;
+	ut->step_encode = memfile_undosys_step_encode;
+	ut->step_decode = memfile_undosys_step_decode;
+	ut->step_free = memfile_undosys_step_free;
+
+	ut->mode = BKE_UNDOTYPE_MODE_STORE;
+	ut->use_context = true;
+
+	ut->step_size = sizeof(MemFileUndoStep);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Utilities
+ * \{ */
+
+/**
+ * Ideally we wouldn't need to export global undo internals, there are some cases where it's needed though.
+ */
+static struct MemFile *ed_undosys_step_get_memfile(UndoStep *us_p)
+{
+	MemFileUndoStep *us = (MemFileUndoStep *)us_p;
+	return &us->data->memfile;
+}
+
+struct MemFile *ED_undosys_stack_memfile_get_active(UndoStack *ustack)
+{
+	UndoStep *us = BKE_undosys_stack_active_with_type(ustack, BKE_UNDOSYS_TYPE_MEMFILE);
+	if (us) {
+		return ed_undosys_step_get_memfile(us);
+	}
+	return NULL;
+}
+
+
+/** \} */
diff --git a/source/blender/editors/util/util_intern.h b/source/blender/editors/undo/undo_intern.h
similarity index 54%
rename from source/blender/editors/util/util_intern.h
rename to source/blender/editors/undo/undo_intern.h
index 0f65033095155ae1e93dae242838ab37fdb1adf4..671f9637d65f40583032f78e212e8e2cd9cdb28d 100644
--- a/source/blender/editors/util/util_intern.h
+++ b/source/blender/editors/undo/undo_intern.h
@@ -15,32 +15,21 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  *
- * The Original Code is Copyright (C) 2008 Blender Foundation.
- * All rights reserved.
- *
- * 
- * Contributor(s): Blender Foundation
- *
  * ***** END GPL LICENSE BLOCK *****
  */
 
-/** \file blender/editors/util/util_intern.h
- *  \ingroup edutil
+/** \file blender/editors/undo/undo_intern.h
+ *  \ingroup edundo
  */
 
-
-#ifndef __UTIL_INTERN_H__
-#define __UTIL_INTERN_H__
+#ifndef __UNDO_INTERN_H__
+#define __UNDO_INTERN_H__
 
 /* internal exports only */
 
-/* editmode_undo.c */
-void        undo_editmode_name(struct bContext *C, const char *undoname);
-bool        undo_editmode_is_valid(const char *undoname);
-const char *undo_editmode_get_name(struct bContext *C, int nr, bool *r_active);
-void       *undo_editmode_get_prev(struct Object *ob);
-void        undo_editmode_step(struct bContext *C, int step);
-void        undo_editmode_number(struct bContext *C, int nr);
+struct UndoType;
 
-#endif /* __UTIL_INTERN_H__ */
+/* memfile_undo.c */
+void ED_memfile_undosys_type(struct UndoType *ut);
 
+#endif /* __UNDO_INTERN_H__ */
diff --git a/source/blender/editors/undo/undo_system_types.c b/source/blender/editors/undo/undo_system_types.c
new file mode 100644
index 0000000000000000000000000000000000000000..75c3d2cc1b79870cda881c5ecba3491218e81ea1
--- /dev/null
+++ b/source/blender/editors/undo/undo_system_types.c
@@ -0,0 +1,74 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/undo/undo_system_types.c
+ *  \ingroup edundo
+ */
+
+#include <string.h>
+
+#include "BLI_utildefines.h"
+
+
+#include "ED_armature.h"
+#include "ED_curve.h"
+#include "ED_lattice.h"
+#include "ED_mball.h"
+#include "ED_mesh.h"
+#include "ED_paint.h"
+#include "ED_particle.h"
+#include "ED_sculpt.h"
+#include "ED_text.h"
+#include "ED_undo.h"
+#include "undo_intern.h"
+
+/* Keep last */
+#include "BKE_undo_system.h"
+
+void ED_undosys_type_init(void)
+{
+	/* Edit Modes */
+	BKE_undosys_type_append(ED_armature_undosys_type);
+	BKE_undosys_type_append(ED_curve_undosys_type);
+	BKE_undosys_type_append(ED_font_undosys_type);
+	BKE_undosys_type_append(ED_lattice_undosys_type);
+	BKE_undosys_type_append(ED_mball_undosys_type);
+	BKE_undosys_type_append(ED_mesh_undosys_type);
+
+	/* Paint Modes */
+	BKE_UNDOSYS_TYPE_IMAGE = BKE_undosys_type_append(ED_image_undosys_type);
+
+	BKE_UNDOSYS_TYPE_SCULPT = BKE_undosys_type_append(ED_sculpt_undosys_type);
+
+	BKE_UNDOSYS_TYPE_PARTICLE = BKE_undosys_type_append(ED_particle_undosys_type);
+
+	BKE_UNDOSYS_TYPE_PAINTCURVE = BKE_undosys_type_append(ED_paintcurve_undosys_type);
+
+	/* Text editor */
+	BKE_UNDOSYS_TYPE_TEXT = BKE_undosys_type_append(ED_text_undosys_type);
+
+	/* Keep global undo last (as a fallback). */
+	BKE_UNDOSYS_TYPE_MEMFILE = BKE_undosys_type_append(ED_memfile_undosys_type);
+}
+
+void ED_undosys_type_free(void)
+{
+	BKE_undosys_type_free_all();
+}
diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt
index 71a3322cb508db8caf3e121c4bbeb1bceb69e336..24cfde90804f3798ec6297a5a1cabb23053a0f8e 100644
--- a/source/blender/editors/util/CMakeLists.txt
+++ b/source/blender/editors/util/CMakeLists.txt
@@ -31,6 +31,7 @@ set(INC
 	../../makesrna
 	../../windowmanager
 	../../../../intern/guardedalloc
+	../../../../intern/clog
 	../../../../intern/glew-mx
 )
 
@@ -41,11 +42,8 @@ set(INC_SYS
 set(SRC
 	ed_transverts.c
 	ed_util.c
-	editmode_undo.c
 	numinput.c
-	undo.c
 
-	util_intern.h
 	# general includes
 	../include/BIF_gl.h
 	../include/BIF_glutil.h
diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c
index 7a5b8bfbda1bdc227727f2234385953fbaa6ae7f..d3a9c22bc73cc50acf27a0a90750b7f3b9f964b1 100644
--- a/source/blender/editors/util/ed_util.c
+++ b/source/blender/editors/util/ed_util.c
@@ -42,7 +42,6 @@
 #include "DNA_space_types.h"
 #include "DNA_scene_types.h"
 #include "DNA_packedFile_types.h"
-#include "DNA_workspace_types.h"
 
 #include "BLI_utildefines.h"
 #include "BLI_string.h"
@@ -62,6 +61,7 @@
 #include "BKE_screen.h"
 #include "BKE_workspace.h"
 #include "BKE_layer.h"
+#include "BKE_undo_system.h"
 
 #include "ED_armature.h"
 #include "ED_buttons.h"
@@ -74,8 +74,6 @@
 #include "ED_space_api.h"
 #include "ED_util.h"
 
-#include "DEG_depsgraph.h"
-
 #include "GPU_immediate.h"
 
 #include "UI_interface.h"
@@ -91,45 +89,38 @@
 
 void ED_editors_init(bContext *C)
 {
+	Main *bmain = CTX_data_main(C);
 	wmWindowManager *wm = CTX_wm_manager(C);
 
+	if (wm->undo_stack == NULL) {
+		wm->undo_stack = BKE_undosys_stack_create();
+	}
+
 	/* This is called during initialization, so we don't want to store any reports */
 	ReportList *reports = CTX_wm_reports(C);
 	int reports_flag_prev = reports->flag & ~RPT_STORE;
 
 	SWAP(int, reports->flag, reports_flag_prev);
 
-
 	/* toggle on modes for objects that were saved with these enabled. for
 	 * e.g. linked objects we have to ensure that they are actually the
 	 * active object in this scene. */
-	{
-		wmWindow *win_orig = CTX_wm_window(C);
-		CTX_wm_window_set(C, NULL);
-		for (wmWindow *win = wm->windows.first; win; win = win->next) {
-			WorkSpace *workspace = WM_window_get_active_workspace(win);
-			Scene *scene = WM_window_get_active_scene(win);
-			ViewLayer *view_layer = BKE_view_layer_from_workspace_get(scene, workspace);
-			Object *obact = view_layer ? OBACT(view_layer) : NULL;
-			eObjectMode object_mode = workspace->object_mode;
-			workspace->object_mode = OB_MODE_OBJECT;
-			if (view_layer && obact) {
-				const ID *data = obact->data;
-				if (!ELEM(object_mode, OB_MODE_OBJECT, OB_MODE_POSE)) {
-					if (!ID_IS_LINKED(obact) && !(data && ID_IS_LINKED(data))) {
-						CTX_wm_window_set(C, win);
-						ED_object_mode_toggle(C, object_mode);
-						CTX_wm_window_set(C, NULL);
-					}
-				}
-				else if (object_mode == OB_MODE_POSE) {
-					if (!ID_IS_LINKED(obact) && (obact->type == OB_ARMATURE)) {
-						workspace->object_mode = object_mode;
-					}
+	Object *obact = CTX_data_active_object(C);
+	if (obact != NULL) {
+		for (Object *ob = bmain->object.first; ob; ob = ob->id.next) {
+			int mode = ob->mode;
+
+			if (mode == OB_MODE_OBJECT) {
+				/* pass */
+			}
+			else {
+				ID *data = ob->data;
+				ob->mode = OB_MODE_OBJECT;
+				if ((ob == obact) && !ID_IS_LINKED(ob) && !(data && ID_IS_LINKED(data))) {
+					ED_object_mode_toggle(C, mode);
 				}
 			}
 		}
-		CTX_wm_window_set(C, win_orig);
 	}
 
 	/* image editor paint mode */
@@ -150,10 +141,16 @@ void ED_editors_exit(bContext *C)
 
 	if (!bmain)
 		return;
-	
+
 	/* frees all editmode undos */
-	undo_editmode_clear();
-	ED_undo_paint_free();
+	if (G.main->wm.first) {
+		wmWindowManager *wm = G.main->wm.first;
+		/* normally we don't check for NULL undo stack, do here since it may run in different context. */
+		if (wm->undo_stack) {
+			BKE_undosys_stack_destroy(wm->undo_stack);
+			wm->undo_stack = NULL;
+		}
+	}
 
 	for (Object *ob = bmain->object.first; ob; ob = ob->id.next) {
 		if (ob->type == OB_MESH) {
@@ -185,16 +182,11 @@ bool ED_editors_flush_edits(const bContext *C, bool for_render)
 	Object *ob;
 	Main *bmain = CTX_data_main(C);
 
-	eObjectMode object_mode = WM_windows_object_mode_get(bmain->wm.first);
-	if ((object_mode & (OB_MODE_SCULPT | OB_MODE_EDIT)) == 0) {
-		return has_edited;
-	}
-
 	/* loop through all data to find edit mode or object mode, because during
 	 * exiting we might not have a context for edit object and multiple sculpt
 	 * objects can exist at the same time */
 	for (ob = bmain->object.first; ob; ob = ob->id.next) {
-		if (object_mode & OB_MODE_SCULPT) {
+		if (ob->mode & OB_MODE_SCULPT) {
 			/* Don't allow flushing while in the middle of a stroke (frees data in use).
 			 * Auto-save prevents this from happening but scripts may cause a flush on saving: T53986. */
 			if ((ob->sculpt && ob->sculpt->cache) == 0) {
@@ -213,7 +205,7 @@ bool ED_editors_flush_edits(const bContext *C, bool for_render)
 				}
 			}
 		}
-		else if (object_mode & OB_MODE_EDIT) {
+		else if (ob->mode & OB_MODE_EDIT) {
 			/* get editmode results */
 			has_edited = true;
 			ED_object_editmode_load(ob);
diff --git a/source/blender/editors/util/editmode_undo.c b/source/blender/editors/util/editmode_undo.c
deleted file mode 100644
index 43d1bfe1e6c1644e81498a18356679770c41170a..0000000000000000000000000000000000000000
--- a/source/blender/editors/util/editmode_undo.c
+++ /dev/null
@@ -1,373 +0,0 @@
-/*
- * ***** BEGIN GPL LICENSE BLOCK *****
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * The Original Code is Copyright (C) 2004 Blender Foundation
- * All rights reserved.
- *
- * The Original Code is: all of this file.
- *
- * Contributor(s): none yet.
- *
- * ***** END GPL LICENSE BLOCK *****
- */
-
-/** \file blender/editors/util/editmode_undo.c
- *  \ingroup edutil
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-
-#include "MEM_guardedalloc.h"
-
-#include "DNA_object_types.h"
-
-#include "BLI_blenlib.h"
-#include "BLI_utildefines.h"
-
-#include "BKE_blender_undo.h"
-#include "BKE_context.h"
-#include "BKE_global.h"
-
-#include "DEG_depsgraph.h"
-
-#include "ED_util.h"
-#include "ED_mesh.h"
-
-#include "util_intern.h"
-
-/* ****** XXX ***** */
-static void error(const char *UNUSED(arg)) {}
-/* ****** XXX ***** */
-
-typedef struct UndoElem {
-	struct UndoElem *next, *prev;
-	/** copy of edit-mode object ID */
-	ID id;
-	/** pointer to edited object */
-	Object *ob;
-	/** type of edited object */
-	int type;
-	void *undodata;
-	uintptr_t undosize;
-	char name[BKE_UNDO_STR_MAX];
-
-	/** Use context to retrieve current edit-data. */
-	void * (*getdata)(bContext * C);
-	/** Pointer to function freeing data. */
-	void (*freedata)(void *);
-	 /** Data to edit-mode conversion. */
-	void (*to_editmode)(void *, void *, void *);
-	/** Edit-mode to data conversion. */
-	void * (*from_editmode)(void *, void *);
-	/** Check if undo data is still valid. */
-	int (*validate_undo)(void *, void *);
-} UndoElem;
-
-static ListBase g_undobase = {NULL, NULL};
-static UndoElem *g_curundo = NULL;
-
-static void undo_restore(UndoElem *undo, void *editdata, void *obdata)
-{
-	if (undo) {
-		undo->to_editmode(undo->undodata, editdata, obdata);
-	}
-}
-
-/**
- * name can be a dynamic string
- * See #UndoElem for callbacks docs.
- * */
-void undo_editmode_push(
-        bContext *C, const char *name,
-        void * (*getdata)(bContext * C),
-        void (*freedata)(void *),
-        void (*to_editmode)(void *, void *, void *),
-        void *(*from_editmode)(void *, void *),
-        int (*validate_undo)(void *, void *))
-{
-	UndoElem *uel;
-	Object *obedit = CTX_data_edit_object(C);
-	void *editdata;
-	int nr;
-	uintptr_t mem_used, mem_total, mem_max;
-
-	/* at first here was code to prevent an "original" key to be inserted twice
-	 * this was giving conflicts for example when mesh changed due to keys or apply */
-	
-	/* remove all undos after (also when g_curundo == NULL) */
-	while (g_undobase.last != g_curundo) {
-		uel = g_undobase.last;
-		uel->freedata(uel->undodata);
-		BLI_freelinkN(&g_undobase, uel);
-	}
-	
-	/* make new */
-	g_curundo = uel = MEM_callocN(sizeof(UndoElem), "undo editmode");
-	BLI_strncpy(uel->name, name, sizeof(uel->name));
-	BLI_addtail(&g_undobase, uel);
-	
-	uel->getdata = getdata;
-	uel->freedata = freedata;
-	uel->to_editmode = to_editmode;
-	uel->from_editmode = from_editmode;
-	uel->validate_undo = validate_undo;
-	
-	/* limit amount to the maximum amount*/
-	nr = 0;
-	uel = g_undobase.last;
-	while (uel) {
-		nr++;
-		if (nr == U.undosteps) {
-			break;
-		}
-		uel = uel->prev;
-	}
-	if (uel) {
-		while (g_undobase.first != uel) {
-			UndoElem *first = g_undobase.first;
-			first->freedata(first->undodata);
-			BLI_freelinkN(&g_undobase, first);
-		}
-	}
-
-	/* copy  */
-	mem_used = MEM_get_memory_in_use();
-	editdata = getdata(C);
-	g_curundo->undodata = g_curundo->from_editmode(editdata, obedit->data);
-	g_curundo->undosize = MEM_get_memory_in_use() - mem_used;
-	g_curundo->ob = obedit;
-	g_curundo->id = obedit->id;
-	g_curundo->type = obedit->type;
-
-	if (U.undomemory != 0) {
-		/* limit to maximum memory (afterwards, we can't know in advance) */
-		mem_total = 0;
-		mem_max = ((uintptr_t)U.undomemory) * 1024 * 1024;
-
-		uel = g_undobase.last;
-		while (uel && uel->prev) {
-			mem_total += uel->undosize;
-			if (mem_total > mem_max) {
-				break;
-			}
-			uel = uel->prev;
-		}
-
-		if (uel) {
-			if (uel->prev && uel->prev->prev) {
-				uel = uel->prev;
-			}
-			while (g_undobase.first != uel) {
-				UndoElem *first = g_undobase.first;
-				first->freedata(first->undodata);
-				BLI_freelinkN(&g_undobase, first);
-			}
-		}
-	}
-}
-
-/* helper to remove clean other objects from undo stack */
-static void undo_clean_stack(bContext *C)
-{
-	UndoElem *uel;
-	Object *obedit = CTX_data_edit_object(C);
-
-	/* global undo changes pointers, so we also allow identical names */
-	/* side effect: when deleting/renaming object and start editing new one with same name */
-
-	uel = g_undobase.first;
-	while (uel) {
-		void *editdata = uel->getdata(C);
-		bool is_valid = false;
-		UndoElem *uel_next = uel->next;
-
-		/* for when objects are converted, renamed, or global undo changes pointers... */
-		if (uel->type == obedit->type) {
-			if (STREQ(uel->id.name, obedit->id.name)) {
-				if (uel->validate_undo == NULL) {
-					is_valid = true;
-				}
-				else if (uel->validate_undo(uel->undodata, editdata)) {
-					is_valid = true;
-				}
-			}
-		}
-		if (is_valid) {
-			uel->ob = obedit;
-		}
-		else {
-			if (uel == g_curundo) {
-				g_curundo = NULL;
-			}
-
-			uel->freedata(uel->undodata);
-			BLI_freelinkN(&g_undobase, uel);
-		}
-
-		uel = uel_next;
-	}
-
-	if (g_curundo == NULL) {
-		g_curundo = g_undobase.last;
-	}
-}
-
-/**
- * 1 = an undo, -1 is a redo.
- * we have to make sure 'g_curundo' remains at current situation
- */
-void undo_editmode_step(bContext *C, int step)
-{
-	Object *obedit = CTX_data_edit_object(C);
-
-	/* prevent undo to happen on wrong object, stack can be a mix */
-	undo_clean_stack(C);
-
-	if (step == 0) {
-		undo_restore(g_curundo, g_curundo->getdata(C), obedit->data);
-	}
-	else if (step == 1) {
-		if (g_curundo == NULL || g_curundo->prev == NULL) {
-			error("No more steps to undo");
-		}
-		else {
-			if (G.debug & G_DEBUG) printf("undo %s\n", g_curundo->name);
-			g_curundo = g_curundo->prev;
-			undo_restore(g_curundo, g_curundo->getdata(C), obedit->data);
-		}
-	}
-	else {
-		/* g_curundo has to remain current situation! */
-		if (g_curundo == NULL || g_curundo->next == NULL) {
-			error("No more steps to redo");
-		}
-		else {
-			undo_restore(g_curundo->next, g_curundo->getdata(C), obedit->data);
-			g_curundo = g_curundo->next;
-			if (G.debug & G_DEBUG) printf("redo %s\n", g_curundo->name);
-		}
-	}
-
-	/* special case for editmesh, mode must be copied back to the scene */
-	if (obedit->type == OB_MESH) {
-		EDBM_selectmode_to_scene(C);
-	}
-
-	DEG_id_tag_update(&obedit->id, OB_RECALC_DATA);
-
-	/* XXX notifiers */
-}
-
-void undo_editmode_clear(void)
-{
-	UndoElem *uel;
-
-	uel = g_undobase.first;
-	while (uel) {
-		uel->freedata(uel->undodata);
-		uel = uel->next;
-	}
-	BLI_freelistN(&g_undobase);
-	g_curundo = NULL;
-}
-
-/* based on index nr it does a restore */
-void undo_editmode_number(bContext *C, int nr)
-{
-	UndoElem *uel;
-	int a = 1;
-
-	for (uel = g_undobase.first; uel; uel = uel->next, a++) {
-		if (a == nr) {
-			break;
-		}
-	}
-	g_curundo = uel;
-	undo_editmode_step(C, 0);
-}
-
-void undo_editmode_name(bContext *C, const char *undoname)
-{
-	UndoElem *uel;
-
-	for (uel = g_undobase.last; uel; uel = uel->prev) {
-		if (STREQ(undoname, uel->name)) {
-			break;
-		}
-	}
-	if (uel && uel->prev) {
-		g_curundo = uel->prev;
-		undo_editmode_step(C, 0);
-	}
-}
-
-/**
- * \a undoname is optional, when NULL it just checks for existing undo steps
- */
-bool undo_editmode_is_valid(const char *undoname)
-{
-	if (undoname) {
-		UndoElem *uel;
-
-		for (uel = g_undobase.last; uel; uel = uel->prev) {
-			if (STREQ(undoname, uel->name)) {
-				break;
-			}
-		}
-		return uel != NULL;
-	}
-	return g_undobase.last != g_undobase.first;
-}
-
-
-/**
- * Get name of undo item, return null if no item with this index.
- *
- * if active pointer, set it to 1 if true
- */
-const char *undo_editmode_get_name(bContext *C, int nr, bool *r_active)
-{
-	UndoElem *uel;
-
-	/* prevent wrong numbers to be returned */
-	undo_clean_stack(C);
-
-	if (r_active) {
-		*r_active = false;
-	}
-
-	uel = BLI_findlink(&g_undobase, nr);
-	if (uel) {
-		if (r_active && (uel == g_curundo)) {
-			*r_active = true;
-		}
-		return uel->name;
-	}
-	return NULL;
-}
-
-
-void *undo_editmode_get_prev(Object *ob)
-{
-	UndoElem *ue = g_undobase.last;
-	if (ue && ue->prev && ue->prev->ob == ob) {
-		return ue->prev->undodata;
-	}
-	return NULL;
-}
diff --git a/source/blender/editors/uvedit/uvedit_draw.c b/source/blender/editors/uvedit/uvedit_draw.c
index cb8aa9660cf7ac0908d4c2a2b0dcc79a93ac6f9e..3fcc89d097390bedc2c60dd5f94e72dea357b424 100644
--- a/source/blender/editors/uvedit/uvedit_draw.c
+++ b/source/blender/editors/uvedit/uvedit_draw.c
@@ -1072,7 +1072,7 @@ static void draw_uvs(SpaceImage *sima, Scene *scene, ViewLayer *view_layer, Obje
 
 
 static void draw_uv_shadows_get(
-        SpaceImage *sima, const EvaluationContext *eval_ctx, Object *ob, Object *obedit,
+        SpaceImage *sima, Object *ob, Object *obedit,
         bool *show_shadow, bool *show_texpaint)
 {
 	*show_shadow = *show_texpaint = false;
@@ -1086,18 +1086,18 @@ static void draw_uv_shadows_get(
 		*show_shadow = EDBM_uv_check(em);
 	}
 	
-	*show_texpaint = (ob && ob->type == OB_MESH && eval_ctx->object_mode == OB_MODE_TEXTURE_PAINT);
+	*show_texpaint = (ob && ob->type == OB_MESH && ob->mode == OB_MODE_TEXTURE_PAINT);
 }
 
 void ED_uvedit_draw_main(
-        SpaceImage *sima, const EvaluationContext *eval_ctx,
+        SpaceImage *sima,
         ARegion *ar, Scene *scene, ViewLayer *view_layer, Object *obedit, Object *obact, Depsgraph *depsgraph)
 {
 	ToolSettings *toolsettings = scene->toolsettings;
 	bool show_uvedit, show_uvshadow, show_texpaint_uvshadow;
 
 	show_uvedit = ED_space_image_show_uvedit(sima, obedit);
-	draw_uv_shadows_get(sima, eval_ctx, obact, obedit, &show_uvshadow, &show_texpaint_uvshadow);
+	draw_uv_shadows_get(sima, obact, obedit, &show_uvshadow, &show_texpaint_uvshadow);
 
 	if (show_uvedit || show_uvshadow || show_texpaint_uvshadow) {
 		if (show_uvshadow)
diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt
index 5c0115d967ae0fccf82023b8e3f1315d9ba72f2a..bf0e6ea33687a9b4e7f181db67dfeb8af593679f 100644
--- a/source/blender/gpu/CMakeLists.txt
+++ b/source/blender/gpu/CMakeLists.txt
@@ -55,7 +55,6 @@ set(SRC
 	intern/gpu_batch_presets.c
 	intern/gpu_buffers.c
 	intern/gpu_codegen.c
-	intern/gpu_compositing.c
 	intern/gpu_debug.c
 	intern/gpu_draw.c
 	intern/gpu_extensions.c
@@ -81,7 +80,6 @@ set(SRC
 	shaders/gpu_shader_fx_dof_hq_frag.glsl
 	shaders/gpu_shader_fx_dof_hq_vert.glsl
 	shaders/gpu_shader_fx_dof_hq_geo.glsl
-	shaders/gpu_shader_fullscreen_vert.glsl
 	shaders/gpu_shader_material.glsl
 	shaders/gpu_shader_sep_gaussian_blur_frag.glsl
 	shaders/gpu_shader_sep_gaussian_blur_vert.glsl
@@ -99,7 +97,6 @@ set(SRC
 	GPU_basic_shader.h
 	GPU_batch.h
 	GPU_buffers.h
-	GPU_compositing.h
 	GPU_debug.h
 	GPU_draw.h
 	GPU_extensions.h
@@ -135,6 +132,12 @@ data_to_c_simple(shaders/gpu_shader_simple_lighting_smooth_color_alpha_frag.glsl
 data_to_c_simple(shaders/gpu_shader_flat_color_frag.glsl SRC)
 data_to_c_simple(shaders/gpu_shader_flat_color_alpha_test_0_frag.glsl SRC)
 data_to_c_simple(shaders/gpu_shader_2D_vert.glsl SRC)
+data_to_c_simple(shaders/gpu_shader_2D_widget_base_vert.glsl SRC)
+data_to_c_simple(shaders/gpu_shader_2D_widget_base_frag.glsl SRC)
+data_to_c_simple(shaders/gpu_shader_2D_widget_shadow_vert.glsl SRC)
+data_to_c_simple(shaders/gpu_shader_2D_widget_shadow_frag.glsl SRC)
+data_to_c_simple(shaders/gpu_shader_2D_nodelink_frag.glsl SRC)
+data_to_c_simple(shaders/gpu_shader_2D_nodelink_vert.glsl SRC)
 data_to_c_simple(shaders/gpu_shader_2D_flat_color_vert.glsl SRC)
 data_to_c_simple(shaders/gpu_shader_2D_line_dashed_uniform_color_vert.glsl SRC)
 data_to_c_simple(shaders/gpu_shader_2D_line_dashed_frag.glsl SRC)
@@ -142,6 +145,8 @@ data_to_c_simple(shaders/gpu_shader_2D_line_dashed_geom.glsl SRC)
 data_to_c_simple(shaders/gpu_shader_2D_smooth_color_vert.glsl SRC)
 data_to_c_simple(shaders/gpu_shader_2D_smooth_color_frag.glsl SRC)
 data_to_c_simple(shaders/gpu_shader_2D_image_vert.glsl SRC)
+data_to_c_simple(shaders/gpu_shader_2D_image_rect_vert.glsl SRC)
+data_to_c_simple(shaders/gpu_shader_2D_image_multi_rect_vert.glsl SRC)
 data_to_c_simple(shaders/gpu_shader_image_frag.glsl SRC)
 data_to_c_simple(shaders/gpu_shader_image_linear_frag.glsl SRC)
 data_to_c_simple(shaders/gpu_shader_image_shuffle_color_frag.glsl SRC)
@@ -149,6 +154,7 @@ data_to_c_simple(shaders/gpu_shader_image_mask_uniform_color_frag.glsl SRC)
 data_to_c_simple(shaders/gpu_shader_image_modulate_alpha_frag.glsl SRC)
 data_to_c_simple(shaders/gpu_shader_image_alpha_color_frag.glsl SRC)
 data_to_c_simple(shaders/gpu_shader_image_color_frag.glsl SRC)
+data_to_c_simple(shaders/gpu_shader_image_varying_color_frag.glsl SRC)
 data_to_c_simple(shaders/gpu_shader_image_depth_linear_frag.glsl SRC)
 data_to_c_simple(shaders/gpu_shader_image_depth_copy_frag.glsl SRC)
 data_to_c_simple(shaders/gpu_shader_image_interlace_frag.glsl SRC)
@@ -203,7 +209,10 @@ data_to_c_simple(shaders/gpu_shader_edges_overlay_vert.glsl SRC)
 data_to_c_simple(shaders/gpu_shader_edges_overlay_geom.glsl SRC)
 data_to_c_simple(shaders/gpu_shader_edges_overlay_simple_geom.glsl SRC)
 data_to_c_simple(shaders/gpu_shader_edges_overlay_frag.glsl SRC)
+data_to_c_simple(shaders/gpu_shader_text_simple_vert.glsl SRC)
+data_to_c_simple(shaders/gpu_shader_text_simple_geom.glsl SRC)
 data_to_c_simple(shaders/gpu_shader_text_vert.glsl SRC)
+data_to_c_simple(shaders/gpu_shader_text_geom.glsl SRC)
 data_to_c_simple(shaders/gpu_shader_text_frag.glsl SRC)
 data_to_c_simple(shaders/gpu_shader_keyframe_diamond_vert.glsl SRC)
 data_to_c_simple(shaders/gpu_shader_keyframe_diamond_frag.glsl SRC)
@@ -222,15 +231,6 @@ data_to_c_simple(shaders/gpu_shader_vertex.glsl SRC)
 data_to_c_simple(shaders/gpu_shader_vertex_world.glsl SRC)
 data_to_c_simple(shaders/gpu_shader_vsm_store_frag.glsl SRC)
 data_to_c_simple(shaders/gpu_shader_vsm_store_vert.glsl SRC)
-data_to_c_simple(shaders/gpu_shader_fullscreen_vert.glsl SRC)
-data_to_c_simple(shaders/gpu_shader_fx_ssao_frag.glsl SRC)
-data_to_c_simple(shaders/gpu_shader_fx_dof_frag.glsl SRC)
-data_to_c_simple(shaders/gpu_shader_fx_dof_vert.glsl SRC)
-data_to_c_simple(shaders/gpu_shader_fx_dof_hq_frag.glsl SRC)
-data_to_c_simple(shaders/gpu_shader_fx_dof_hq_vert.glsl SRC)
-data_to_c_simple(shaders/gpu_shader_fx_dof_hq_geo.glsl SRC)
-data_to_c_simple(shaders/gpu_shader_fx_depth_resolve.glsl SRC)
-data_to_c_simple(shaders/gpu_shader_fx_lib.glsl SRC)
 
 if(WITH_GAMEENGINE)
 	add_definitions(-DWITH_GAMEENGINE)
diff --git a/source/blender/gpu/GPU_batch.h b/source/blender/gpu/GPU_batch.h
index fda24018150ca00e5e290a5569b68ae9ad4ef1d0..760faeff7b1bc7a08c3e56ea54c09c5865b6027d 100644
--- a/source/blender/gpu/GPU_batch.h
+++ b/source/blender/gpu/GPU_batch.h
@@ -40,6 +40,7 @@ struct rctf;
 // #include "gawain/batch.h"
 
 #include "BLI_compiler_attrs.h"
+#include "BLI_sys_types.h"
 
 #include "GPU_shader.h"
 
@@ -66,6 +67,7 @@ Gwn_Batch *GPU_batch_preset_sphere(int lod) ATTR_WARN_UNUSED_RESULT;
 Gwn_Batch *GPU_batch_preset_sphere_wire(int lod) ATTR_WARN_UNUSED_RESULT;
 
 void gpu_batch_presets_init(void);
+void gpu_batch_presets_register(Gwn_Batch *preset_batch);
 void gpu_batch_presets_reset(void);
 void gpu_batch_presets_exit(void);
 
diff --git a/source/blender/gpu/GPU_compositing.h b/source/blender/gpu/GPU_compositing.h
deleted file mode 100644
index d506d91a9aac0e4f83090fee8f96619c3351ee14..0000000000000000000000000000000000000000
--- a/source/blender/gpu/GPU_compositing.h
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * ***** BEGIN GPL LICENSE BLOCK *****
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * The Original Code is Copyright (C) 2005 Blender Foundation.
- * All rights reserved.
- *
- * The Original Code is: all of this file.
- *
- * Contributor(s): Antony Riakiotakis.
- *
- * ***** END GPL LICENSE BLOCK *****
- */
-
-/** \file GPU_compositing.h
- *  \ingroup gpu
- */
-
-#ifndef __GPU_COMPOSITING_H__
-#define __GPU_COMPOSITING_H__
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* opaque handle for framebuffer compositing effects (defined in gpu_compositing.c )*/
-typedef struct GPUFX GPUFX;
-struct GPUDOFSettings;
-struct GPUSSAOSettings;
-struct GPUOffScreen;
-struct GPUFXSettings;
-struct rcti;
-struct Scene;
-struct GPUShader;
-enum eGPUFXFlags;
-
-/**** Public API *****/
-
-typedef enum GPUFXShaderEffect {
-	/* Screen space ambient occlusion shader */
-	GPU_SHADER_FX_SSAO = 1,
-
-	/* depth of field passes. Yep, quite a complex effect */
-	GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_ONE = 2,
-	GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_TWO = 3,
-	GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_THREE = 4,
-	GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_FOUR = 5,
-	GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_FIVE = 6,
-
-	/* high quality */
-	GPU_SHADER_FX_DEPTH_OF_FIELD_HQ_PASS_ONE = 7,
-	GPU_SHADER_FX_DEPTH_OF_FIELD_HQ_PASS_TWO = 8,
-	GPU_SHADER_FX_DEPTH_OF_FIELD_HQ_PASS_THREE = 9,
-
-	GPU_SHADER_FX_DEPTH_RESOLVE = 10,
-} GPUFXShaderEffect;
-
-/* keep in synch with enum above! */
-#define MAX_FX_SHADERS 11
-
-/* generate a new FX compositor */
-GPUFX *GPU_fx_compositor_create(void);
-
-/* destroy a text compositor */
-void GPU_fx_compositor_destroy(GPUFX *fx);
-
-/* initialize a framebuffer with size taken from the viewport */
-bool GPU_fx_compositor_initialize_passes(
-        GPUFX *fx, const struct rcti *rect, const struct rcti *scissor_rect,
-        const struct GPUFXSettings *fx_settings);
-
-/* do compositing on the fx passes that have been initialized */
-bool GPU_fx_do_composite_pass(
-        GPUFX *fx, float projmat[4][4], bool is_persp,
-        struct Scene *scene, struct GPUOffScreen *ofs);
-
-/* bind new depth buffer for XRay pass */
-void GPU_fx_compositor_setup_XRay_pass(GPUFX *fx, bool do_xray);
-
-/* resolve a final depth buffer by compositing the XRay and normal depth buffers */
-void GPU_fx_compositor_XRay_resolve(GPUFX *fx);
-
-void GPU_fx_compositor_init_dof_settings(struct GPUDOFSettings *dof);
-void GPU_fx_compositor_init_ssao_settings(struct GPUSSAOSettings *ssao);
-
-
-/* initialize and cache the shader unform interface for effects */
-void GPU_fx_shader_init_interface(struct GPUShader *shader, GPUFXShaderEffect effect);
-#ifdef __cplusplus
-}
-#endif
-
-#endif // __GPU_COMPOSITING_H__
diff --git a/source/blender/gpu/GPU_draw.h b/source/blender/gpu/GPU_draw.h
index e437b4e4f580c753ddc745a947dfaabb96626e0b..9bbf46b2a1fb8d61922e85453789511bd2303b22 100644
--- a/source/blender/gpu/GPU_draw.h
+++ b/source/blender/gpu/GPU_draw.h
@@ -46,7 +46,6 @@ struct View3D;
 struct RegionView3D;
 struct SmokeModifierData;
 struct DupliObject;
-struct EvaluationContext;
 
 #include "DNA_object_enums.h"
 
@@ -79,7 +78,7 @@ void GPU_disable_program_point_size(void);
 void GPU_begin_object_materials(
         struct View3D *v3d, struct RegionView3D *rv3d,
         struct Scene *scene, struct ViewLayer *view_layer,
-        struct Object *ob, bool glsl, const eObjectMode object_mode, bool *do_alpha_after);
+        struct Object *ob, bool glsl, bool *do_alpha_after);
 void GPU_end_object_materials(void);
 bool GPU_object_materials_check(void);
 
diff --git a/source/blender/gpu/GPU_framebuffer.h b/source/blender/gpu/GPU_framebuffer.h
index 31f24aa7c2e0761b2a9dc106cc19d5db4a7c83bf..0ab15a4ea4711e768bfbcae56e19b2b3c618a701 100644
--- a/source/blender/gpu/GPU_framebuffer.h
+++ b/source/blender/gpu/GPU_framebuffer.h
@@ -36,52 +36,148 @@
 extern "C" {
 #endif
 
+struct GPUTexture;
+
+typedef struct GPUAttachment {
+	struct GPUTexture *tex;
+	int mip, layer;
+} GPUAttachment;
+
+typedef enum GPUFrameBufferBits{
+	GPU_COLOR_BIT    = (1 << 0),
+	GPU_DEPTH_BIT    = (1 << 1),
+	GPU_STENCIL_BIT  = (1 << 2),
+} GPUFrameBufferBits;
+
 typedef struct GPUFrameBuffer GPUFrameBuffer;
 typedef struct GPUOffScreen GPUOffScreen;
-struct GPUTexture;
 
 /* GPU Framebuffer
  * - this is a wrapper for an OpenGL framebuffer object (FBO). in practice
  *   multiple FBO's may be created, to get around limitations on the number
  *   of attached textures and the dimension requirements.
- * - after any of the GPU_framebuffer_* functions, GPU_framebuffer_restore must
- *   be called before rendering to the window framebuffer again */
-
-void GPU_texture_bind_as_framebuffer(struct GPUTexture *tex);
+ * - actual FBO creation & config is deferred until GPU_framebuffer_bind or
+ *   GPU_framebuffer_check_valid to allow creation & config while another
+ *   opengl context is bound (since FBOs are not shared between ogl contexts).
+ */
 
 GPUFrameBuffer *GPU_framebuffer_create(void);
-bool GPU_framebuffer_texture_attach(GPUFrameBuffer *fb, struct GPUTexture *tex, int slot, int mip);
-bool GPU_framebuffer_texture_layer_attach(
-        GPUFrameBuffer *fb, struct GPUTexture *tex, int slot, int layer, int mip);
-bool GPU_framebuffer_texture_cubeface_attach(
-        GPUFrameBuffer *fb, struct GPUTexture *tex, int slot, int face, int mip);
-void GPU_framebuffer_texture_detach(struct GPUTexture *tex);
-void GPU_framebuffer_bind(GPUFrameBuffer *fb);
-void GPU_framebuffer_slots_bind(GPUFrameBuffer *fb, int slot);
-void GPU_framebuffer_texture_unbind(GPUFrameBuffer *fb, struct GPUTexture *tex);
 void GPU_framebuffer_free(GPUFrameBuffer *fb);
+void GPU_framebuffer_bind(GPUFrameBuffer *fb);
+void GPU_framebuffer_restore(void);
+
+bool GPU_framebuffer_bound(GPUFrameBuffer *fb);
 bool GPU_framebuffer_check_valid(GPUFrameBuffer *fb, char err_out[256]);
 
-void GPU_framebuffer_bind_no_save(GPUFrameBuffer *fb, int slot);
+/* internal use only */
+unsigned int GPU_framebuffer_current_get(void);
 
-bool GPU_framebuffer_bound(GPUFrameBuffer *fb);
+#define GPU_FRAMEBUFFER_FREE_SAFE(fb) do { \
+	if (fb != NULL) { \
+		GPU_framebuffer_free(fb); \
+		fb = NULL; \
+	} \
+} while (0)
 
-void GPU_framebuffer_restore(void);
-void GPU_framebuffer_blur(
-        GPUFrameBuffer *fb, struct GPUTexture *tex,
-        GPUFrameBuffer *blurfb, struct GPUTexture *blurtex);
+/* Framebuffer setup : You need to call GPU_framebuffer_bind for theses
+ * to be effective. */
+
+void GPU_framebuffer_texture_attach(
+        GPUFrameBuffer *fb, struct GPUTexture *tex, int slot, int mip);
+void GPU_framebuffer_texture_layer_attach(
+        GPUFrameBuffer *fb, struct GPUTexture *tex, int slot, int layer, int mip);
+void GPU_framebuffer_texture_cubeface_attach(
+        GPUFrameBuffer *fb, struct GPUTexture *tex, int slot, int face, int mip);
+void GPU_framebuffer_texture_detach(GPUFrameBuffer *fb, struct GPUTexture *tex);
+void GPU_framebuffer_texture_detach_slot(
+        GPUFrameBuffer *fb, struct GPUTexture *tex, int type);
+
+/**
+ * How to use GPU_framebuffer_ensure_config().
+ *
+ * Example :
+ * GPU_framebuffer_ensure_config(&fb, {
+ *         GPU_ATTACHMENT_TEXTURE(depth), // must be depth buffer
+ *         GPU_ATTACHMENT_TEXTURE(tex1),
+ *         GPU_ATTACHMENT_TEXTURE_CUBEFACE(tex2, 0),
+ *         GPU_ATTACHMENT_TEXTURE_LAYER_MIP(tex2, 0, 0)
+ * })
+ *
+ * Note : Unspecified attachements (i.e: those beyond the last
+ *        GPU_ATTACHMENT_* in GPU_framebuffer_ensure_config list)
+ *        are left unchanged.
+ * Note : Make sure that the dimensions of your textures matches
+ *        otherwise you will have an invalid framebuffer error.
+ **/
+#define GPU_framebuffer_ensure_config(_fb, ...) do { \
+	if (*(_fb) == NULL) { \
+		*(_fb) = GPU_framebuffer_create(); \
+	} \
+	GPUAttachment config[] = __VA_ARGS__; \
+	GPU_framebuffer_config_array(*(_fb), config, (sizeof(config) / sizeof(GPUAttachment))); \
+} while (0)
+
+void GPU_framebuffer_config_array(GPUFrameBuffer *fb, const GPUAttachment *config, int config_ct);
+
+#define GPU_ATTACHMENT_NONE \
+        {.tex = NULL, .layer = -1, .mip = 0}
+#define GPU_ATTACHMENT_LEAVE \
+        {.tex = NULL, .layer = -1, .mip = -1}
+#define GPU_ATTACHMENT_TEXTURE(_tex) \
+        {.tex = _tex, .layer = -1, .mip = 0}
+#define GPU_ATTACHMENT_TEXTURE_MIP(_tex, _mip) \
+        {.tex = _tex, .layer = -1, .mip = _mip}
+#define GPU_ATTACHMENT_TEXTURE_LAYER(_tex, _layer) \
+        {.tex = _tex, .layer = _layer, .mip = 0}
+#define GPU_ATTACHMENT_TEXTURE_LAYER_MIP(_tex, _layer, _mip) \
+        {.tex = _tex, .layer = _layer, .mip = _mip}
+#define GPU_ATTACHMENT_TEXTURE_CUBEFACE(_tex, _face) \
+        {.tex = _tex, .layer = _face, .mip = 0}
+#define GPU_ATTACHMENT_TEXTURE_CUBEFACE_MIP(_tex, _face, _mip) \
+        {.tex = _tex, .layer = _face, .mip = _mip}
+
+/* Framebuffer operations */
+
+void GPU_framebuffer_viewport_set(GPUFrameBuffer *fb, int x, int y, int w, int h);
+
+void GPU_framebuffer_clear(
+        GPUFrameBuffer *fb, GPUFrameBufferBits buffers,
+        const float clear_col[4], float clear_depth, unsigned int clear_stencil);
+
+#define GPU_framebuffer_clear_color(fb, col) \
+        GPU_framebuffer_clear(fb, GPU_COLOR_BIT, col, 0.0f, 0x00)
+
+#define GPU_framebuffer_clear_depth(fb, depth) \
+        GPU_framebuffer_clear(fb, GPU_DEPTH_BIT, NULL, depth, 0x00)
+
+#define GPU_framebuffer_clear_color_depth(fb, col, depth) \
+        GPU_framebuffer_clear(fb, GPU_COLOR_BIT | GPU_DEPTH_BIT, col, depth, 0x00)
+
+#define GPU_framebuffer_clear_stencil(fb, stencil) \
+        GPU_framebuffer_clear(fb, GPU_STENCIL_BIT, NULL, 0.0f, stencil)
+
+#define GPU_framebuffer_clear_depth_stencil(fb, depth, stencil) \
+        GPU_framebuffer_clear(fb, GPU_DEPTH_BIT | GPU_STENCIL_BIT, NULL, depth, stencil)
+
+#define GPU_framebuffer_clear_color_depth_stencil(fb, col, depth, stencil) \
+        GPU_framebuffer_clear(fb, GPU_COLOR_BIT | GPU_DEPTH_BIT | GPU_STENCIL_BIT, col, depth, stencil)
+
+void GPU_framebuffer_read_depth(GPUFrameBuffer *fb, int x, int y, int w, int h, float *data);
+void GPU_framebuffer_read_color(
+        GPUFrameBuffer *fb, int x, int y, int w, int h, int channels, int slot, float *data);
 
 void GPU_framebuffer_blit(
         GPUFrameBuffer *fb_read, int read_slot,
-        GPUFrameBuffer *fb_write, int write_slot, bool use_depth, bool use_stencil);
+        GPUFrameBuffer *fb_write, int write_slot,
+        GPUFrameBufferBits blit_buffers);
 
 void GPU_framebuffer_recursive_downsample(
-        GPUFrameBuffer *fb, struct GPUTexture *tex, int num_iter,
+        GPUFrameBuffer *fb, int max_lvl,
         void (*callback)(void *userData, int level), void *userData);
 
 /* GPU OffScreen
  * - wrapper around framebuffer and texture for simple offscreen drawing
- * - changes size if graphics card can't support it */
+ */
 
 GPUOffScreen *GPU_offscreen_create(int width, int height, int samples,
         bool depth, bool high_bitdepth, char err_out[256]);
@@ -89,7 +185,6 @@ void GPU_offscreen_free(GPUOffScreen *ofs);
 void GPU_offscreen_bind(GPUOffScreen *ofs, bool save);
 void GPU_offscreen_unbind(GPUOffScreen *ofs, bool restore);
 void GPU_offscreen_read_pixels(GPUOffScreen *ofs, int type, void *pixels);
-void GPU_offscreen_blit(GPUOffScreen *ofs, int x, int y);
 int GPU_offscreen_width(const GPUOffScreen *ofs);
 int GPU_offscreen_height(const GPUOffScreen *ofs);
 struct GPUTexture *GPU_offscreen_color_texture(const GPUOffScreen *ofs);
diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h
index def0b0bfbc7fb55a1e541ebed00ffc17b71fc791..b33df9fdb2083c317ad697248084a80576f1306e 100644
--- a/source/blender/gpu/GPU_shader.h
+++ b/source/blender/gpu/GPU_shader.h
@@ -72,9 +72,6 @@ int GPU_shader_get_program(GPUShader *shader);
 
 void *GPU_shader_get_interface(GPUShader *shader);
 
-void *GPU_fx_shader_get_interface(GPUShader *shader);
-void GPU_fx_shader_set_interface(GPUShader *shader, void *interface);
-
 int GPU_shader_get_uniform(GPUShader *shader, const char *name);
 int GPU_shader_get_builtin_uniform(GPUShader *shader, int builtin);
 int GPU_shader_get_uniform_block(GPUShader *shader, const char *name);
@@ -100,6 +97,7 @@ typedef enum GPUBuiltinShader {
 
 	/* specialized drawing */
 	GPU_SHADER_TEXT,
+	GPU_SHADER_TEXT_SIMPLE,
 	GPU_SHADER_EDGES_FRONT_BACK_PERSP,
 	GPU_SHADER_EDGES_FRONT_BACK_ORTHO,
 	GPU_SHADER_EDGES_OVERLAY_SIMPLE,
@@ -117,6 +115,8 @@ typedef enum GPUBuiltinShader {
 	GPU_SHADER_2D_IMAGE_COLOR,
 	GPU_SHADER_2D_IMAGE_ALPHA_COLOR,
 	GPU_SHADER_2D_IMAGE_ALPHA,
+	GPU_SHADER_2D_IMAGE_RECT_COLOR,
+	GPU_SHADER_2D_IMAGE_MULTI_RECT_COLOR,
 	GPU_SHADER_2D_CHECKER,
 	GPU_SHADER_2D_DIAG_STRIPES,
 	/* for simple 3D drawing */
@@ -171,6 +171,12 @@ typedef enum GPUBuiltinShader {
 	GPU_SHADER_INSTANCE_VARIYING_COLOR_VARIYING_SIZE, /* Uniformly scaled */
 	GPU_SHADER_INSTANCE_VARIYING_COLOR_VARIYING_SCALE,
 	GPU_SHADER_INSTANCE_EDGES_VARIYING_COLOR,
+	/* specialized for UI drawing */
+	GPU_SHADER_2D_WIDGET_BASE,
+	GPU_SHADER_2D_WIDGET_BASE_INST,
+	GPU_SHADER_2D_WIDGET_SHADOW,
+	GPU_SHADER_2D_NODELINK,
+	GPU_SHADER_2D_NODELINK_INST,
 
 	GPU_SHADER_3D_INSTANCE_BONE_ENVELOPE_SOLID,
 	GPU_SHADER_3D_INSTANCE_BONE_ENVELOPE_WIRE,
@@ -191,7 +197,6 @@ typedef enum GPUInterlaceShader {
 } GPUInterlaceShader;
 
 GPUShader *GPU_shader_get_builtin_shader(GPUBuiltinShader shader);
-GPUShader *GPU_shader_get_builtin_fx_shader(int effects, bool persp);
 
 void GPU_shader_free_builtin_shaders(void);
 
diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h
index 0fde0edcf2bce6fe49a9b22dda2db3d51ea881ec..57185d2f39e2f22ccea173f12b77ac6506e451cd 100644
--- a/source/blender/gpu/GPU_texture.h
+++ b/source/blender/gpu/GPU_texture.h
@@ -189,17 +189,18 @@ void GPU_texture_filter_mode(GPUTexture *tex, bool use_filter);
 void GPU_texture_mipmap_mode(GPUTexture *tex, bool use_mipmap, bool use_filter);
 void GPU_texture_wrap_mode(GPUTexture *tex, bool use_repeat);
 
-struct GPUFrameBuffer *GPU_texture_framebuffer(GPUTexture *tex);
-int GPU_texture_framebuffer_attachment(GPUTexture *tex);
-void GPU_texture_framebuffer_set(GPUTexture *tex, struct GPUFrameBuffer *fb, int attachment);
+void GPU_texture_attach_framebuffer(GPUTexture *tex, struct GPUFrameBuffer *fb, int attachment);
+int GPU_texture_detach_framebuffer(GPUTexture *tex, struct GPUFrameBuffer *fb);
 
 int GPU_texture_target(const GPUTexture *tex);
 int GPU_texture_width(const GPUTexture *tex);
 int GPU_texture_height(const GPUTexture *tex);
-int GPU_texture_format(const GPUTexture *tex);
+GPUTextureFormat GPU_texture_format(const GPUTexture *tex);
 int GPU_texture_samples(const GPUTexture *tex);
+bool GPU_texture_cube(const GPUTexture *tex);
 bool GPU_texture_depth(const GPUTexture *tex);
 bool GPU_texture_stencil(const GPUTexture *tex);
+bool GPU_texture_integer(const GPUTexture *tex);
 int GPU_texture_opengl_bindcode(const GPUTexture *tex);
 
 #ifdef __cplusplus
diff --git a/source/blender/gpu/GPU_viewport.h b/source/blender/gpu/GPU_viewport.h
index 20d468459e6e4d277229eb058076a88f192ae587..ca7d5ae7ceb34c93ec63989d0c3764f6802c4ac6 100644
--- a/source/blender/gpu/GPU_viewport.h
+++ b/source/blender/gpu/GPU_viewport.h
@@ -125,13 +125,4 @@ GPUTexture *GPU_viewport_texture_pool_query(GPUViewport *viewport, void *engine,
 bool GPU_viewport_engines_data_validate(GPUViewport *viewport, unsigned int hash);
 void GPU_viewport_cache_release(GPUViewport *viewport);
 
-/* debug */
-bool GPU_viewport_debug_depth_create(GPUViewport *viewport, int width, int height, char err_out[256]);
-void GPU_viewport_debug_depth_free(GPUViewport *viewport);
-void GPU_viewport_debug_depth_store(GPUViewport *viewport, const int x, const int y);
-void GPU_viewport_debug_depth_draw(GPUViewport *viewport, const float znear, const float zfar);
-bool GPU_viewport_debug_depth_is_valid(GPUViewport *viewport);
-int GPU_viewport_debug_depth_width(const GPUViewport *viewport);
-int GPU_viewport_debug_depth_height(const GPUViewport *viewport);
-
 #endif // __GPU_VIEWPORT_H__
diff --git a/source/blender/gpu/intern/gpu_batch_presets.c b/source/blender/gpu/intern/gpu_batch_presets.c
index 696143a385787c79e93b077f626fecc529435220..10cbd16490bce7a35755179c767035df8e3c1cc4 100644
--- a/source/blender/gpu/intern/gpu_batch_presets.c
+++ b/source/blender/gpu/intern/gpu_batch_presets.c
@@ -32,6 +32,10 @@
 #include "BLI_utildefines.h"
 #include "BLI_math.h"
 #include "BLI_threads.h"
+#include "BLI_listbase.h"
+#include "MEM_guardedalloc.h"
+
+#include "UI_interface.h"
 
 #include "GPU_batch.h"
 #include "gpu_shader_private.h"
@@ -53,12 +57,22 @@ static struct {
 	} attr_id;
 } g_presets_3d = {{0}};
 
-/* We may want 2D presets later. */
+static ListBase presets_list = {NULL, NULL};
 
 /* -------------------------------------------------------------------- */
 /** \name 3D Primitives
  * \{ */
 
+static Gwn_VertFormat *preset_3D_format(void)
+{
+	if (g_presets_3d.format.attrib_ct == 0) {
+		Gwn_VertFormat *format = &g_presets_3d.format;
+		g_presets_3d.attr_id.pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 3, GWN_FETCH_FLOAT);
+		g_presets_3d.attr_id.nor = GWN_vertformat_attr_add(format, "nor", GWN_COMP_F32, 3, GWN_FETCH_FLOAT);
+	}
+	return &g_presets_3d.format;
+}
+
 static void batch_sphere_lat_lon_vert(
         Gwn_VertBufRaw *pos_step, Gwn_VertBufRaw *nor_step,
         float lat, float lon)
@@ -78,7 +92,7 @@ Gwn_Batch *gpu_batch_sphere(int lat_res, int lon_res)
 	const float lat_inc = M_PI / lat_res;
 	float lon, lat;
 
-	Gwn_VertBuf *vbo = GWN_vertbuf_create_with_format(&g_presets_3d.format);
+	Gwn_VertBuf *vbo = GWN_vertbuf_create_with_format(preset_3D_format());
 	const uint vbo_len = (lat_res - 1) * lon_res * 6;
 	GWN_vertbuf_data_alloc(vbo, vbo_len);
 
@@ -116,7 +130,7 @@ static Gwn_Batch *batch_sphere_wire(int lat_res, int lon_res)
 	const float lat_inc = M_PI / lat_res;
 	float lon, lat;
 
-	Gwn_VertBuf *vbo = GWN_vertbuf_create_with_format(&g_presets_3d.format);
+	Gwn_VertBuf *vbo = GWN_vertbuf_create_with_format(preset_3D_format());
 	const uint vbo_len = (lat_res * lon_res * 2) + ((lat_res - 1) * lon_res * 2);
 	GWN_vertbuf_data_alloc(vbo, vbo_len);
 
@@ -178,37 +192,45 @@ Gwn_Batch *GPU_batch_preset_sphere_wire(int lod)
 
 void gpu_batch_presets_init(void)
 {
-	if (g_presets_3d.format.attrib_ct == 0) {
-		Gwn_VertFormat *format = &g_presets_3d.format;
-		g_presets_3d.attr_id.pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 3, GWN_FETCH_FLOAT);
-		g_presets_3d.attr_id.nor = GWN_vertformat_attr_add(format, "nor", GWN_COMP_F32, 3, GWN_FETCH_FLOAT);
-	}
-
 	/* Hard coded resolution */
 	g_presets_3d.batch.sphere_low = gpu_batch_sphere(8, 16);
+	gpu_batch_presets_register(g_presets_3d.batch.sphere_low);
+
 	g_presets_3d.batch.sphere_med = gpu_batch_sphere(16, 10);
+	gpu_batch_presets_register(g_presets_3d.batch.sphere_med);
+
 	g_presets_3d.batch.sphere_high = gpu_batch_sphere(32, 24);
+	gpu_batch_presets_register(g_presets_3d.batch.sphere_high);
 
 	g_presets_3d.batch.sphere_wire_low = batch_sphere_wire(6, 8);
+	gpu_batch_presets_register(g_presets_3d.batch.sphere_wire_low);
+
 	g_presets_3d.batch.sphere_wire_med = batch_sphere_wire(8, 16);
+	gpu_batch_presets_register(g_presets_3d.batch.sphere_wire_med);
+}
+
+void gpu_batch_presets_register(Gwn_Batch *preset_batch)
+{
+	BLI_addtail(&presets_list, BLI_genericNodeN(preset_batch));
 }
 
 void gpu_batch_presets_reset(void)
 {
 	/* Reset vao caches for these every time we switch opengl context.
 	 * This way they will draw correctly for each window. */
-	gwn_batch_vao_cache_clear(g_presets_3d.batch.sphere_low);
-	gwn_batch_vao_cache_clear(g_presets_3d.batch.sphere_med);
-	gwn_batch_vao_cache_clear(g_presets_3d.batch.sphere_high);
-	gwn_batch_vao_cache_clear(g_presets_3d.batch.sphere_wire_low);
-	gwn_batch_vao_cache_clear(g_presets_3d.batch.sphere_wire_med);
+	LinkData *link = presets_list.first;
+	for (link = presets_list.first; link; link = link->next) {
+		Gwn_Batch *preset = link->data;
+		gwn_batch_vao_cache_clear(preset);
+	}
 }
 
 void gpu_batch_presets_exit(void)
 {
-	GWN_batch_discard(g_presets_3d.batch.sphere_low);
-	GWN_batch_discard(g_presets_3d.batch.sphere_med);
-	GWN_batch_discard(g_presets_3d.batch.sphere_high);
-	GWN_batch_discard(g_presets_3d.batch.sphere_wire_low);
-	GWN_batch_discard(g_presets_3d.batch.sphere_wire_med);
+	LinkData *link;
+	while ((link = BLI_pophead(&presets_list))) {
+		Gwn_Batch *preset = link->data;
+		GWN_batch_discard(preset);
+		MEM_freeN(link);
+	}
 }
diff --git a/source/blender/gpu/intern/gpu_buffers.c b/source/blender/gpu/intern/gpu_buffers.c
index 3043b3ab686c606789c599e2856221c4fdf5911b..3367a568bc553253cfd65f0c7f4ca5db6bbad6cb 100644
--- a/source/blender/gpu/intern/gpu_buffers.c
+++ b/source/blender/gpu/intern/gpu_buffers.c
@@ -986,15 +986,37 @@ struct GPU_PBVH_Buffers {
 	float diffuse_color[4];
 };
 
-typedef struct {
+static struct {
 	uint pos, nor, col;
-} VertexBufferAttrID;
+} g_vbo_id = {0};
 
-static void gpu_pbvh_vert_format_init__gwn(Gwn_VertFormat *format, VertexBufferAttrID *vbo_id)
+/* Allocates a non-initialized buffer to be sent to GPU.
+ * Return is false it indicates that the memory map failed. */
+static bool gpu_pbvh_vert_buf_data_set(GPU_PBVH_Buffers *buffers, unsigned int vert_ct)
 {
-	vbo_id->pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 3, GWN_FETCH_FLOAT);
-	vbo_id->nor = GWN_vertformat_attr_add(format, "nor", GWN_COMP_I16, 3, GWN_FETCH_INT_TO_FLOAT_UNIT);
-	vbo_id->col = GWN_vertformat_attr_add(format, "color", GWN_COMP_U8, 3, GWN_FETCH_INT_TO_FLOAT_UNIT);
+	if (buffers->vert_buf == NULL) {
+		/* Initialize vertex buffer */
+		/* match 'VertexBufferFormat' */
+
+		static Gwn_VertFormat format = {0};
+		if (format.attrib_ct == 0) {
+			g_vbo_id.pos = GWN_vertformat_attr_add(&format, "pos", GWN_COMP_F32, 3, GWN_FETCH_FLOAT);
+			g_vbo_id.nor = GWN_vertformat_attr_add(&format, "nor", GWN_COMP_I16, 3, GWN_FETCH_INT_TO_FLOAT_UNIT);
+			g_vbo_id.col = GWN_vertformat_attr_add(&format, "color", GWN_COMP_U8, 3, GWN_FETCH_INT_TO_FLOAT_UNIT);
+		}
+#if 0
+		buffers->vert_buf = GWN_vertbuf_create_with_format_ex(&format, GWN_USAGE_DYNAMIC);
+		GWN_vertbuf_data_alloc(buffers->vert_buf, vert_ct);
+	}
+	else if (vert_ct != buffers->vert_buf->vertex_ct) {
+		GWN_vertbuf_data_resize(buffers->vert_buf, vert_ct);
+	}
+#else
+		buffers->vert_buf = GWN_vertbuf_create_with_format_ex(&format, GWN_USAGE_STATIC);
+	}
+	GWN_vertbuf_data_alloc(buffers->vert_buf, vert_ct);
+#endif
+	return buffers->vert_buf->data != NULL;
 }
 
 static void gpu_pbvh_batch_init(GPU_PBVH_Buffers *buffers)
@@ -1004,14 +1026,14 @@ static void gpu_pbvh_batch_init(GPU_PBVH_Buffers *buffers)
 		GWN_vertbuf_use(buffers->vert_buf);
 	}
 
-	GWN_BATCH_DISCARD_SAFE(buffers->triangles);
-	buffers->triangles = GWN_batch_create(
-	        GWN_PRIM_TRIS, buffers->vert_buf,
-	        /* can be NULL */
-	        buffers->index_buf);
+	if (buffers->triangles == NULL) {
+		buffers->triangles = GWN_batch_create(
+		        GWN_PRIM_TRIS, buffers->vert_buf,
+		        /* can be NULL */
+		        buffers->index_buf);
+	}
 
-	GWN_BATCH_DISCARD_SAFE(buffers->triangles_fast);
-	if (buffers->index_buf_fast) {
+	if ((buffers->triangles_fast == NULL) && buffers->index_buf_fast) {
 		buffers->triangles_fast = GWN_batch_create(
 		        GWN_PRIM_TRIS, buffers->vert_buf,
 		        /* can be NULL */
@@ -1085,25 +1107,15 @@ void GPU_pbvh_mesh_buffers_update(
 		rgba_float_to_uchar(diffuse_color_ub, diffuse_color);
 
 		/* Build VBO */
-		GWN_VERTBUF_DISCARD_SAFE(buffers->vert_buf);
-
-		/* match 'VertexBufferFormat' */
-		Gwn_VertFormat format = {0};
-		VertexBufferAttrID vbo_id;
-		gpu_pbvh_vert_format_init__gwn(&format, &vbo_id);
-
-		buffers->vert_buf = GWN_vertbuf_create_with_format(&format);
-		GWN_vertbuf_data_alloc(buffers->vert_buf, totelem);
-
-		if (buffers->vert_buf->data) {
+		if (gpu_pbvh_vert_buf_data_set(buffers, totelem)) {
 			/* Vertex data is shared if smooth-shaded, but separate
 			 * copies are made for flat shading because normals
 			 * shouldn't be shared. */
 			if (buffers->smooth) {
 				for (uint i = 0; i < totvert; ++i) {
 					const MVert *v = &mvert[vert_indices[i]];
-					GWN_vertbuf_attr_set(buffers->vert_buf, vbo_id.pos, i, v->co);
-					GWN_vertbuf_attr_set(buffers->vert_buf, vbo_id.nor, i, v->no);
+					GWN_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.pos, i, v->co);
+					GWN_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.nor, i, v->no);
 				}
 
 				for (uint i = 0; i < buffers->face_indices_len; i++) {
@@ -1114,10 +1126,10 @@ void GPU_pbvh_mesh_buffers_update(
 							int v_index = buffers->mloop[lt->tri[j]].v;
 							uchar color_ub[3];
 							gpu_color_from_mask_copy(vmask[v_index], diffuse_color, color_ub);
-							GWN_vertbuf_attr_set(buffers->vert_buf, vbo_id.col, vidx, color_ub);
+							GWN_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vidx, color_ub);
 						}
 						else {
-							GWN_vertbuf_attr_set(buffers->vert_buf, vbo_id.col, vidx, diffuse_color_ub);
+							GWN_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vidx, diffuse_color_ub);
 						}
 					}
 				}
@@ -1160,9 +1172,9 @@ void GPU_pbvh_mesh_buffers_update(
 					for (uint j = 0; j < 3; j++) {
 						const MVert *v = &mvert[vtri[j]];
 
-						GWN_vertbuf_attr_set(buffers->vert_buf, vbo_id.pos, vbo_index, v->co);
-						GWN_vertbuf_attr_set(buffers->vert_buf, vbo_id.nor, vbo_index, no);
-						GWN_vertbuf_attr_set(buffers->vert_buf, vbo_id.col, vbo_index, color_ub);
+						GWN_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.pos, vbo_index, v->co);
+						GWN_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.nor, vbo_index, no);
+						GWN_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vbo_index, color_ub);
 
 						vbo_index++;
 					}
@@ -1171,9 +1183,6 @@ void GPU_pbvh_mesh_buffers_update(
 
 			gpu_pbvh_batch_init(buffers);
 		}
-		else {
-			GWN_VERTBUF_DISCARD_SAFE(buffers->vert_buf);
-		}
 	}
 
 	buffers->mvert = mvert;
@@ -1293,17 +1302,9 @@ void GPU_pbvh_grid_buffers_update(
 
 		copy_v4_v4(buffers->diffuse_color, diffuse_color);
 
-		Gwn_VertFormat format = {0};
-		VertexBufferAttrID vbo_id;
-		gpu_pbvh_vert_format_init__gwn(&format, &vbo_id);
-
-		/* Build coord/normal VBO */
-		GWN_VERTBUF_DISCARD_SAFE(buffers->vert_buf);
-		buffers->vert_buf = GWN_vertbuf_create_with_format(&format);
-		GWN_vertbuf_data_alloc(buffers->vert_buf, totgrid * key->grid_area);
-
 		uint vbo_index_offset = 0;
-		if (buffers->vert_buf->data) {
+		/* Build VBO */
+		if (gpu_pbvh_vert_buf_data_set(buffers, totgrid * key->grid_area)) {
 			for (i = 0; i < totgrid; ++i) {
 				CCGElem *grid = grids[grid_indices[i]];
 				int vbo_index = vbo_index_offset;
@@ -1311,12 +1312,12 @@ void GPU_pbvh_grid_buffers_update(
 				for (y = 0; y < key->grid_size; y++) {
 					for (x = 0; x < key->grid_size; x++) {
 						CCGElem *elem = CCG_grid_elem(key, grid, x, y);
-						GWN_vertbuf_attr_set(buffers->vert_buf, vbo_id.pos, vbo_index, CCG_elem_co(key, elem));
+						GWN_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.pos, vbo_index, CCG_elem_co(key, elem));
 
 						if (buffers->smooth) {
 							short no_short[3];
 							normal_float_to_short_v3(no_short, CCG_elem_no(key, elem));
-							GWN_vertbuf_attr_set(buffers->vert_buf, vbo_id.nor, vbo_index, no_short);
+							GWN_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.nor, vbo_index, no_short);
 
 							if (has_mask) {
 								uchar color_ub[3];
@@ -1327,7 +1328,7 @@ void GPU_pbvh_grid_buffers_update(
 								else {
 									F3TOCHAR3(diffuse_color, color_ub);
 								}
-								GWN_vertbuf_attr_set(buffers->vert_buf, vbo_id.col, vbo_index, color_ub);
+								GWN_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vbo_index, color_ub);
 							}
 						}
 						vbo_index += 1;
@@ -1354,7 +1355,7 @@ void GPU_pbvh_grid_buffers_update(
 							vbo_index = vbo_index_offset + ((j + 1) * key->grid_size + k);
 							short no_short[3];
 							normal_float_to_short_v3(no_short, fno);
-							GWN_vertbuf_attr_set(buffers->vert_buf, vbo_id.nor, vbo_index, no_short);
+							GWN_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.nor, vbo_index, no_short);
 
 							if (has_mask) {
 								uchar color_ub[3];
@@ -1370,7 +1371,7 @@ void GPU_pbvh_grid_buffers_update(
 								else {
 									F3TOCHAR3(diffuse_color, color_ub);
 								}
-								GWN_vertbuf_attr_set(buffers->vert_buf, vbo_id.col, vbo_index, color_ub);
+								GWN_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vbo_index, color_ub);
 							}
 						}
 					}
@@ -1381,9 +1382,6 @@ void GPU_pbvh_grid_buffers_update(
 
 			gpu_pbvh_batch_init(buffers);
 		}
-		else {
-			GWN_VERTBUF_DISCARD_SAFE(buffers->vert_buf);
-		}
 	}
 
 	buffers->grids = grids;
@@ -1562,7 +1560,6 @@ GPU_PBVH_Buffers *GPU_pbvh_grid_buffers_build(
 static void gpu_bmesh_vert_to_buffer_copy__gwn(
         BMVert *v,
         Gwn_VertBuf *vert_buf,
-        const VertexBufferAttrID *vbo_id,
         int *v_index,
         const float fno[3],
         const float *fmask,
@@ -1573,12 +1570,12 @@ static void gpu_bmesh_vert_to_buffer_copy__gwn(
 	if (!BM_elem_flag_test(v, BM_ELEM_HIDDEN)) {
 
 		/* Set coord, normal, and mask */
-		GWN_vertbuf_attr_set(vert_buf, vbo_id->pos, *v_index, v->co);
+		GWN_vertbuf_attr_set(vert_buf, g_vbo_id.pos, *v_index, v->co);
 
 		{
 			short no_short[3];
 			normal_float_to_short_v3(no_short, fno ? fno : v->no);
-			GWN_vertbuf_attr_set(vert_buf, vbo_id->nor, *v_index, no_short);
+			GWN_vertbuf_attr_set(vert_buf, g_vbo_id.nor, *v_index, no_short);
 		}
 
 		{
@@ -1596,7 +1593,7 @@ static void gpu_bmesh_vert_to_buffer_copy__gwn(
 			        effective_mask,
 			        diffuse_color,
 			        color_ub);
-			GWN_vertbuf_attr_set(vert_buf, vbo_id->col, *v_index, color_ub);
+			GWN_vertbuf_attr_set(vert_buf, g_vbo_id.col, *v_index, color_ub);
 		}
 
 		/* Assign index for use in the triangle index buffer */
@@ -1694,18 +1691,8 @@ void GPU_pbvh_bmesh_buffers_update(
 
 	copy_v4_v4(buffers->diffuse_color, diffuse_color);
 
-	/* Initialize vertex buffer */
-	GWN_VERTBUF_DISCARD_SAFE(buffers->vert_buf);
-	/* match 'VertexBufferFormat' */
-	Gwn_VertFormat format = {0};
-	VertexBufferAttrID vbo_id;
-	gpu_pbvh_vert_format_init__gwn(&format, &vbo_id);
-
-	buffers->vert_buf = GWN_vertbuf_create_with_format(&format);
-	GWN_vertbuf_data_alloc(buffers->vert_buf, totvert);
-
 	/* Fill vertex buffer */
-	if (buffers->vert_buf->data) {
+	if (gpu_pbvh_vert_buf_data_set(buffers, totvert)) {
 		int v_index = 0;
 
 		if (buffers->smooth) {
@@ -1718,7 +1705,7 @@ void GPU_pbvh_bmesh_buffers_update(
 			GSET_ITER (gs_iter, bm_unique_verts) {
 				gpu_bmesh_vert_to_buffer_copy__gwn(
 				        BLI_gsetIterator_getKey(&gs_iter),
-				        buffers->vert_buf, &vbo_id, &v_index, NULL, NULL,
+				        buffers->vert_buf, &v_index, NULL, NULL,
 				        cd_vert_mask_offset, diffuse_color,
 				        show_mask);
 			}
@@ -1726,7 +1713,7 @@ void GPU_pbvh_bmesh_buffers_update(
 			GSET_ITER (gs_iter, bm_other_verts) {
 				gpu_bmesh_vert_to_buffer_copy__gwn(
 				        BLI_gsetIterator_getKey(&gs_iter),
-				        buffers->vert_buf, &vbo_id, &v_index, NULL, NULL,
+				        buffers->vert_buf, &v_index, NULL, NULL,
 				        cd_vert_mask_offset, diffuse_color,
 				        show_mask);
 			}
@@ -1759,7 +1746,7 @@ void GPU_pbvh_bmesh_buffers_update(
 					
 					for (i = 0; i < 3; i++) {
 						gpu_bmesh_vert_to_buffer_copy__gwn(
-						        v[i], buffers->vert_buf, &vbo_id,
+						        v[i], buffers->vert_buf,
 						        &v_index, f->no, &fmask,
 						        cd_vert_mask_offset, diffuse_color,
 						        show_mask);
@@ -1774,7 +1761,6 @@ void GPU_pbvh_bmesh_buffers_update(
 		bm->elem_index_dirty |= BM_VERT;
 	}
 	else {
-		GWN_VERTBUF_DISCARD_SAFE(buffers->vert_buf);
 		/* Memory map failed */
 		return;
 	}
@@ -1786,9 +1772,6 @@ void GPU_pbvh_bmesh_buffers_update(
 		GWN_indexbuf_init(&elb, GWN_PRIM_TRIS, tottri, maxvert);
 
 		/* Initialize triangle index buffer */
-		if (buffers->triangles && !buffers->is_index_buf_global) {
-			GWN_BATCH_DISCARD_SAFE(buffers->triangles);
-		}
 		buffers->is_index_buf_global = false;
 
 		/* Fill triangle index buffer */
@@ -1812,7 +1795,12 @@ void GPU_pbvh_bmesh_buffers_update(
 
 			buffers->tot_tri = tottri;
 
-			buffers->index_buf = GWN_indexbuf_build(&elb);
+			if (buffers->index_buf == NULL) {
+				buffers->index_buf = GWN_indexbuf_build(&elb);
+			}
+			else {
+				GWN_indexbuf_build_in_place(&elb, buffers->index_buf);
+			}
 		}
 	}
 	else if (buffers->index_buf) {
diff --git a/source/blender/gpu/intern/gpu_compositing.c b/source/blender/gpu/intern/gpu_compositing.c
deleted file mode 100644
index ca81ca72a3295cfd3f45a0b6d3b69107e97732ee..0000000000000000000000000000000000000000
--- a/source/blender/gpu/intern/gpu_compositing.c
+++ /dev/null
@@ -1,1494 +0,0 @@
-/*
- * ***** BEGIN GPL LICENSE BLOCK *****
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * The Original Code is Copyright (C) 2006 Blender Foundation.
- * All rights reserved.
- *
- * The Original Code is: all of this file.
- *
- * Contributor(s): Antony Riakiotakis.
- *
- * ***** END GPL LICENSE BLOCK *****
- */
-
-/** \file blender/gpu/intern/gpu_compositing.c
- *  \ingroup gpu
- *
- * System that manages framebuffer compositing.
- */
-
-#include "BLI_sys_types.h"
-#include "BLI_rect.h"
-#include "BLI_math.h"
-
-#include "BLI_rand.h"
-
-#include "DNA_vec_types.h"
-#include "DNA_scene_types.h"
-#include "DNA_gpu_types.h"
-
-#include "GPU_compositing.h"
-#include "GPU_draw.h"
-#include "GPU_extensions.h"
-#include "GPU_framebuffer.h"
-#include "GPU_glew.h"
-#include "GPU_shader.h"
-#include "GPU_texture.h"
-#include "GPU_batch.h"
-
-#include "MEM_guardedalloc.h"
-
-static const float fullscreencos[4][2] = {{-1.0f, -1.0f}, {1.0f, -1.0f}, {-1.0f, 1.0f}, {1.0f, 1.0f}};
-static const float fullscreenuvs[4][2] = {{0.0f, 0.0f}, {1.0f, 0.0f}, {0.0f, 1.0f}, {1.0f, 1.0f}};
-
-
-/* shader interfaces (legacy GL 2 style, without uniform buffer objects) */
-
-typedef struct {
-	int ssao_uniform;
-	int ssao_color_uniform;
-	int color_uniform;
-	int depth_uniform;
-	int viewvecs_uniform;
-	int ssao_sample_params_uniform;
-	int ssao_concentric_tex;
-	int ssao_jitter_uniform;
-} GPUSSAOShaderInterface;
-
-typedef struct {
-	int invrendertargetdim_uniform;
-	int color_uniform;
-	int dof_uniform;
-	int depth_uniform;
-	int viewvecs_uniform;
-} GPUDOFHQPassOneInterface;
-
-typedef struct {
-	int rendertargetdim_uniform;
-	int color_uniform;
-	int coc_uniform;
-	int select_uniform;
-	int dof_uniform;
-} GPUDOFHQPassTwoInterface;
-
-typedef struct {
-	int dof_uniform;
-	int invrendertargetdim_uniform;
-	int color_uniform;
-	int far_uniform;
-	int near_uniform;
-	int viewvecs_uniform;
-	int depth_uniform;
-} GPUDOFHQPassThreeInterface;
-
-typedef struct {
-	int dof_uniform;
-	int invrendertargetdim_uniform;
-	int color_uniform;
-	int depth_uniform;
-	int viewvecs_uniform;
-} GPUDOFPassOneInterface;
-
-typedef struct {
-	int dof_uniform;
-	int invrendertargetdim_uniform;
-	int color_uniform;
-	int depth_uniform;
-	int viewvecs_uniform;
-} GPUDOFPassTwoInterface;
-
-typedef struct {
-	int near_coc_downsampled;
-	int near_coc_blurred;
-} GPUDOFPassThreeInterface;
-
-typedef struct {
-	int near_coc_downsampled;
-	int invrendertargetdim_uniform;
-} GPUDOFPassFourInterface;
-
-typedef struct {
-	int medium_blurred_uniform;
-	int high_blurred_uniform;
-	int dof_uniform;
-	int invrendertargetdim_uniform;
-	int original_uniform;
-	int depth_uniform;
-	int viewvecs_uniform;
-} GPUDOFPassFiveInterface;
-
-typedef struct {
-	int depth_uniform;
-} GPUDepthResolveInterface;
-
-
-struct GPUFX {
-	/* we borrow the term gbuffer from deferred rendering however this is just a regular
-	 * depth/color framebuffer. Could be extended later though */
-	GPUFrameBuffer *gbuffer;
-
-	/* dimensions of the gbuffer */
-	int gbuffer_dim[2];
-
-	/* texture bound to the first color attachment of the gbuffer */
-	GPUTexture *color_buffer;
-
-	/* second texture used for ping-pong compositing */
-	GPUTexture *color_buffer_sec;
-	/* texture bound to the depth attachment of the gbuffer */
-	GPUTexture *depth_buffer;
-	GPUTexture *depth_buffer_xray;
-
-	/* texture used for jittering for various effects */
-	GPUTexture *jitter_buffer;
-
-	/* all those buffers below have to coexist.
-	 * Fortunately they are all quarter sized (1/16th of memory) of original framebuffer */
-	int dof_downsampled_w;
-	int dof_downsampled_h;
-
-	/* texture used for near coc and color blurring calculation */
-	GPUTexture *dof_near_coc_buffer;
-	/* blurred near coc buffer. */
-	GPUTexture *dof_near_coc_blurred_buffer;
-	/* final near coc buffer. */
-	GPUTexture *dof_near_coc_final_buffer;
-
-	/* half size blur buffer */
-	GPUTexture *dof_half_downsampled_near;
-	GPUTexture *dof_half_downsampled_far;
-	/* high quality dof texture downsamplers. 6 levels means 64 pixels wide - should be enough */
-	GPUTexture *dof_nearfar_coc;
-	GPUTexture *dof_near_blur;
-	GPUTexture *dof_far_blur;
-
-	/* for high quality we use again a spiral texture with radius adapted */
-	bool dof_high_quality;
-
-	/* texture used for ssao */
-	int ssao_sample_count_cache;
-	GPUTexture *ssao_spiral_samples_tex;
-
-
-	GPUFXSettings settings;
-
-	/* or-ed flags of enabled effects */
-	int effects;
-
-	/* number of passes, needed to detect if ping pong buffer allocation is needed */
-	int num_passes;
-
-	/* we have a stencil, restore the previous state */
-	bool restore_stencil;
-
-	Gwn_Batch *quad_batch;
-	Gwn_Batch *point_batch;
-};
-
-#if 0
-/* concentric mapping, see "A Low Distortion Map Between Disk and Square" and
- * http://psgraphics.blogspot.nl/2011/01/improved-code-for-concentric-map.html */
-static GPUTexture * create_concentric_sample_texture(int side)
-{
-	GPUTexture *tex;
-	float midpoint = 0.5f * (side - 1);
-	float *texels = (float *)MEM_mallocN(sizeof(float) * 2 * side * side, "concentric_tex");
-	int i, j;
-
-	for (i = 0; i < side; i++) {
-		for (j = 0; j < side; j++) {
-			int index = (i * side + j) * 2;
-			float a = 1.0f - i / midpoint;
-			float b = 1.0f - j / midpoint;
-			float phi, r;
-			if (a * a > b * b) {
-				r = a;
-				phi = (M_PI_4) * (b / a);
-			}
-			else {
-				r = b;
-				phi = M_PI_2 - (M_PI_4) * (a / b);
-			}
-			texels[index] = r * cos(phi);
-			texels[index + 1] = r * sin(phi);
-		}
-	}
-
-	tex = GPU_texture_create_1D_custom(side * side, 2, GPU_RG16F, (float *)texels, NULL);
-
-	/* Set parameters */
-	GPU_texture_bind(tex, 0);
-	GPU_texture_filter_mode(tex, false);
-	GPU_texture_unbind(tex);
-
-	MEM_freeN(texels);
-	return tex;
-}
-#endif
-
-static GPUTexture *create_spiral_sample_texture(int numsaples)
-{
-	GPUTexture *tex;
-	float (*texels)[2] = MEM_mallocN(sizeof(float[2]) * numsaples, "concentric_tex");
-	const float numsaples_inv = 1.0f / numsaples;
-	int i;
-	/* arbitrary number to ensure we don't get conciding samples every circle */
-	const float spirals = 7.357;
-
-	for (i = 0; i < numsaples; i++) {
-		float r = (i + 0.5f) * numsaples_inv;
-		float phi = r * spirals * (float)(2.0 * M_PI);
-		texels[i][0] = r * cosf(phi);
-		texels[i][1] = r * sinf(phi);
-	}
-
-	tex = GPU_texture_create_1D_custom(numsaples, 2, GPU_RG16F, (float *)texels, NULL);
-
-	/* Set parameters */
-	GPU_texture_bind(tex, 0);
-	GPU_texture_filter_mode(tex, false);
-	GPU_texture_unbind(tex);
-
-	MEM_freeN(texels);
-	return tex;
-}
-
-/* generate a new FX compositor */
-GPUFX *GPU_fx_compositor_create(void)
-{
-	GPUFX *fx = MEM_callocN(sizeof(GPUFX), "GPUFX compositor");
-
-	/* Quad buffer */
-	static Gwn_VertFormat format = {0};
-	static unsigned int pos, uvs;
-	if (format.attrib_ct == 0) {
-		pos = GWN_vertformat_attr_add(&format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
-		uvs = GWN_vertformat_attr_add(&format, "uvs", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
-	}
-	Gwn_VertBuf *vbo = GWN_vertbuf_create_with_format(&format);
-	GWN_vertbuf_data_alloc(vbo, 4);
-	for (int i = 0; i < 4; ++i) {
-		GWN_vertbuf_attr_set(vbo, pos, i, fullscreencos[i]);
-		GWN_vertbuf_attr_set(vbo, uvs, i, fullscreenuvs[i]);
-	}
-	fx->quad_batch = GWN_batch_create_ex(GWN_PRIM_TRI_STRIP, vbo, NULL, GWN_BATCH_OWNS_VBO);
-
-	/* Point Buffer */
-	static Gwn_VertFormat format_point = {0};
-	static unsigned int dummy_attrib;
-	if (format_point.attrib_ct == 0) {
-		dummy_attrib = GWN_vertformat_attr_add(&format_point, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
-	}
-	float dummy[2] = {0.0f, 0.0f};
-	Gwn_VertBuf *vbo_point = GWN_vertbuf_create_with_format(&format_point);
-	GWN_vertbuf_data_alloc(vbo_point, 1);
-	GWN_vertbuf_attr_set(vbo_point, dummy_attrib, 0, &dummy);
-	fx->point_batch = GWN_batch_create_ex(GWN_PRIM_POINTS, vbo_point, NULL, GWN_BATCH_OWNS_VBO);
-
-	return fx;
-}
-
-static void cleanup_fx_dof_buffers(GPUFX *fx)
-{
-	if (fx->dof_near_coc_blurred_buffer) {
-		GPU_texture_free(fx->dof_near_coc_blurred_buffer);
-		fx->dof_near_coc_blurred_buffer = NULL;
-	}
-	if (fx->dof_near_coc_buffer) {
-		GPU_texture_free(fx->dof_near_coc_buffer);
-		fx->dof_near_coc_buffer = NULL;
-	}
-	if (fx->dof_near_coc_final_buffer) {
-		GPU_texture_free(fx->dof_near_coc_final_buffer);
-		fx->dof_near_coc_final_buffer = NULL;
-	}
-
-	if (fx->dof_half_downsampled_near) {
-		GPU_texture_free(fx->dof_half_downsampled_near);
-		fx->dof_half_downsampled_near = NULL;
-	}
-	if (fx->dof_half_downsampled_far) {
-		GPU_texture_free(fx->dof_half_downsampled_far);
-		fx->dof_half_downsampled_far = NULL;
-	}
-	if (fx->dof_nearfar_coc) {
-		GPU_texture_free(fx->dof_nearfar_coc);
-		fx->dof_nearfar_coc = NULL;
-	}
-	if (fx->dof_near_blur) {
-		GPU_texture_free(fx->dof_near_blur);
-		fx->dof_near_blur = NULL;
-	}
-	if (fx->dof_far_blur) {
-		GPU_texture_free(fx->dof_far_blur);
-		fx->dof_far_blur = NULL;
-	}
-}
-
-static void cleanup_fx_gl_data(GPUFX *fx, bool do_fbo)
-{
-	if (fx->color_buffer) {
-		GPU_framebuffer_texture_detach(fx->color_buffer);
-		GPU_texture_free(fx->color_buffer);
-		fx->color_buffer = NULL;
-	}
-
-	if (fx->color_buffer_sec) {
-		GPU_framebuffer_texture_detach(fx->color_buffer_sec);
-		GPU_texture_free(fx->color_buffer_sec);
-		fx->color_buffer_sec = NULL;
-	}
-
-	if (fx->depth_buffer) {
-		GPU_framebuffer_texture_detach(fx->depth_buffer);
-		GPU_texture_free(fx->depth_buffer);
-		fx->depth_buffer = NULL;
-	}
-
-	if (fx->depth_buffer_xray) {
-		GPU_framebuffer_texture_detach(fx->depth_buffer_xray);
-		GPU_texture_free(fx->depth_buffer_xray);
-		fx->depth_buffer_xray = NULL;
-	}
-
-	cleanup_fx_dof_buffers(fx);
-
-	if (fx->ssao_spiral_samples_tex) {
-		GPU_texture_free(fx->ssao_spiral_samples_tex);
-		fx->ssao_spiral_samples_tex = NULL;
-	}
-
-	if (fx->jitter_buffer && do_fbo) {
-		GPU_texture_free(fx->jitter_buffer);
-		fx->jitter_buffer = NULL;
-	}
-
-	if (fx->gbuffer && do_fbo) {
-		GPU_framebuffer_free(fx->gbuffer);
-		fx->gbuffer = NULL;
-	}
-}
-
-/* destroy a text compositor */
-void GPU_fx_compositor_destroy(GPUFX *fx)
-{
-	cleanup_fx_gl_data(fx, true);
-	GWN_batch_discard(fx->quad_batch);
-	GWN_batch_discard(fx->point_batch);
-	MEM_freeN(fx);
-}
-
-static GPUTexture * create_jitter_texture(void)
-{
-	GPUTexture *tex;
-	float jitter[64 * 64][2];
-	int i;
-
-	for (i = 0; i < 64 * 64; i++) {
-		jitter[i][0] = 2.0f * BLI_frand() - 1.0f;
-		jitter[i][1] = 2.0f * BLI_frand() - 1.0f;
-		normalize_v2(jitter[i]);
-	}
-
-	tex = GPU_texture_create_2D_custom(64, 64, 2, GPU_RG16F, &jitter[0][0], NULL);
-
-	/* Set parameters */
-	GPU_texture_bind(tex, 0);
-	GPU_texture_filter_mode(tex, false);
-	GPU_texture_wrap_mode(tex, true);
-	GPU_texture_unbind(tex);
-
-	return tex;
-}
-
-
-bool GPU_fx_compositor_initialize_passes(
-        GPUFX *fx, const rcti *rect, const rcti *scissor_rect,
-        const GPUFXSettings *fx_settings)
-{
-	int w = BLI_rcti_size_x(rect), h = BLI_rcti_size_y(rect);
-	char err_out[256];
-	int num_passes = 0;
-	char fx_flag;
-
-	fx->effects = 0;
-
-	if (!fx_settings) {
-		cleanup_fx_gl_data(fx, true);
-		return false;
-	}
-
-	fx_flag = fx_settings->fx_flag;
-
-	/* disable effects if no options passed for them */
-	if (!fx_settings->dof) {
-		fx_flag &= ~GPU_FX_FLAG_DOF;
-	}
-	if (!fx_settings->ssao || fx_settings->ssao->samples < 1) {
-		fx_flag &= ~GPU_FX_FLAG_SSAO;
-	}
-
-	if (!fx_flag) {
-		cleanup_fx_gl_data(fx, true);
-		return false;
-	}
-
-	/* scissor is missing when drawing offscreen, in that case, dimensions match exactly. In opposite case
-	 * add one to match viewport dimensions */
-	if (scissor_rect) {
-		w++;
-		h++;
-	}
-
-	fx->num_passes = 0;
-	/* dof really needs a ping-pong buffer to work */
-	if (fx_flag & GPU_FX_FLAG_DOF)
-		num_passes++;
-
-	if (fx_flag & GPU_FX_FLAG_SSAO)
-		num_passes++;
-
-	if (!fx->gbuffer) {
-		fx->gbuffer = GPU_framebuffer_create();
-
-		if (!fx->gbuffer) {
-			return false;
-		}
-	}
-
-	/* try creating the jitter texture */
-	if (!fx->jitter_buffer)
-		fx->jitter_buffer = create_jitter_texture();
-
-	/* check if color buffers need recreation */
-	if (!fx->color_buffer || !fx->depth_buffer || w != fx->gbuffer_dim[0] || h != fx->gbuffer_dim[1]) {
-		cleanup_fx_gl_data(fx, false);
-
-		if (!(fx->color_buffer = GPU_texture_create_2D(w, h, NULL, err_out))) {
-			printf(".256%s\n", err_out);
-			cleanup_fx_gl_data(fx, true);
-			return false;
-		}
-
-		if (!(fx->depth_buffer = GPU_texture_create_depth(w, h, err_out))) {
-			printf("%.256s\n", err_out);
-			cleanup_fx_gl_data(fx, true);
-			return false;
-		}
-	}
-
-	if (fx_flag & GPU_FX_FLAG_SSAO) {
-		if (fx_settings->ssao->samples != fx->ssao_sample_count_cache || !fx->ssao_spiral_samples_tex) {
-			if (fx_settings->ssao->samples < 1)
-				fx_settings->ssao->samples = 1;
-
-			fx->ssao_sample_count_cache = fx_settings->ssao->samples;
-
-			if (fx->ssao_spiral_samples_tex) {
-				GPU_texture_free(fx->ssao_spiral_samples_tex);
-			}
-
-			fx->ssao_spiral_samples_tex = create_spiral_sample_texture(fx_settings->ssao->samples);
-		}
-	}
-	else {
-		if (fx->ssao_spiral_samples_tex) {
-			GPU_texture_free(fx->ssao_spiral_samples_tex);
-			fx->ssao_spiral_samples_tex = NULL;
-		}
-	}
-
-	/* create textures for dof effect */
-	if (fx_flag & GPU_FX_FLAG_DOF) {
-		bool dof_high_quality = (fx_settings->dof->high_quality != 0);
-
-		/* cleanup buffers if quality setting has changed (no need to keep more buffers around than necessary ) */
-		if (dof_high_quality != fx->dof_high_quality)
-			cleanup_fx_dof_buffers(fx);
-
-		if (dof_high_quality) {
-			fx->dof_downsampled_w = w / 2;
-			fx->dof_downsampled_h = h / 2;
-
-			if (!fx->dof_half_downsampled_near || !fx->dof_nearfar_coc || !fx->dof_near_blur ||
-			    !fx->dof_far_blur || !fx->dof_half_downsampled_far)
-			{
-
-				if (!(fx->dof_half_downsampled_near = GPU_texture_create_2D(
-				      fx->dof_downsampled_w, fx->dof_downsampled_h, NULL, err_out)))
-				{
-					printf("%.256s\n", err_out);
-					cleanup_fx_gl_data(fx, true);
-					return false;
-				}
-				if (!(fx->dof_half_downsampled_far = GPU_texture_create_2D(
-				      fx->dof_downsampled_w, fx->dof_downsampled_h, NULL, err_out)))
-				{
-					printf("%.256s\n", err_out);
-					cleanup_fx_gl_data(fx, true);
-					return false;
-				}
-
-				if (!(fx->dof_nearfar_coc = GPU_texture_create_2D_custom(
-				    fx->dof_downsampled_w, fx->dof_downsampled_h, 2, GPU_RG16F, NULL, err_out)))
-				{
-					printf("%.256s\n", err_out);
-					cleanup_fx_gl_data(fx, true);
-					return false;
-				}
-				GPU_texture_bind(fx->dof_nearfar_coc, 0);
-				GPU_texture_filter_mode(fx->dof_nearfar_coc, false);
-				GPU_texture_wrap_mode(fx->dof_nearfar_coc, false);
-				GPU_texture_unbind(fx->dof_nearfar_coc);
-
-				if (!(fx->dof_near_blur = GPU_texture_create_2D_custom(
-				    fx->dof_downsampled_w, fx->dof_downsampled_h, 4, GPU_RGBA16F, NULL, err_out)))
-				{
-					printf("%.256s\n", err_out);
-					cleanup_fx_gl_data(fx, true);
-					return false;
-				}
-
-				if (!(fx->dof_far_blur = GPU_texture_create_2D_custom(
-				    fx->dof_downsampled_w, fx->dof_downsampled_h, 4, GPU_RGBA16F, NULL, err_out)))
-				{
-					printf("%.256s\n", err_out);
-					cleanup_fx_gl_data(fx, true);
-					return false;
-				}
-			}
-		}
-		else {
-			fx->dof_downsampled_w = w / 4;
-			fx->dof_downsampled_h = h / 4;
-
-			if (!fx->dof_near_coc_buffer || !fx->dof_near_coc_blurred_buffer || !fx->dof_near_coc_final_buffer) {
-
-				if (!(fx->dof_near_coc_buffer = GPU_texture_create_2D(
-				          fx->dof_downsampled_w, fx->dof_downsampled_h, NULL, err_out)))
-				{
-					printf("%.256s\n", err_out);
-					cleanup_fx_gl_data(fx, true);
-					return false;
-				}
-				if (!(fx->dof_near_coc_blurred_buffer = GPU_texture_create_2D(
-				          fx->dof_downsampled_w, fx->dof_downsampled_h, NULL, err_out)))
-				{
-					printf("%.256s\n", err_out);
-					cleanup_fx_gl_data(fx, true);
-					return false;
-				}
-				if (!(fx->dof_near_coc_final_buffer = GPU_texture_create_2D(
-				          fx->dof_downsampled_w, fx->dof_downsampled_h, NULL, err_out)))
-				{
-					printf("%.256s\n", err_out);
-					cleanup_fx_gl_data(fx, true);
-					return false;
-				}
-			}
-		}
-
-		fx->dof_high_quality = dof_high_quality;
-	}
-	else {
-		/* cleanup unnecessary buffers */
-		cleanup_fx_dof_buffers(fx);
-	}
-
-	/* we need to pass data between shader stages, allocate an extra color buffer */
-	if (num_passes > 1) {
-		if (!fx->color_buffer_sec) {
-			if (!(fx->color_buffer_sec = GPU_texture_create_2D(w, h, NULL, err_out))) {
-				printf(".256%s\n", err_out);
-				cleanup_fx_gl_data(fx, true);
-				return false;
-			}
-		}
-	}
-	else {
-		if (fx->color_buffer_sec) {
-			GPU_framebuffer_texture_detach(fx->color_buffer_sec);
-			GPU_texture_free(fx->color_buffer_sec);
-			fx->color_buffer_sec = NULL;
-		}
-	}
-
-	/* bind the buffers */
-
-	/* first depth buffer, because system assumes read/write buffers */
-	GPU_framebuffer_texture_attach(fx->gbuffer, fx->depth_buffer, 0, 0);
-	GPU_framebuffer_texture_attach(fx->gbuffer, fx->color_buffer, 0, 0);
-
-	if (!GPU_framebuffer_check_valid(fx->gbuffer, err_out))
-		printf("%.256s\n", err_out);
-
-	GPU_texture_bind_as_framebuffer(fx->color_buffer);
-
-	/* enable scissor test. It's needed to ensure sculpting works correctly */
-	if (scissor_rect) {
-		int w_sc = BLI_rcti_size_x(scissor_rect) + 1;
-		int h_sc = BLI_rcti_size_y(scissor_rect) + 1;
-		gpuPushAttrib(GPU_SCISSOR_BIT);
-		glEnable(GL_SCISSOR_TEST);
-		glScissor(scissor_rect->xmin - rect->xmin, scissor_rect->ymin - rect->ymin,
-		          w_sc, h_sc);
-		fx->restore_stencil = true;
-	}
-	else {
-		fx->restore_stencil = false;
-	}
-
-	fx->effects = fx_flag;
-
-	if (fx_settings)
-		fx->settings = *fx_settings;
-	fx->gbuffer_dim[0] = w;
-	fx->gbuffer_dim[1] = h;
-
-	fx->num_passes = num_passes;
-
-	return true;
-}
-
-static void gpu_fx_bind_render_target(int *passes_left, GPUFX *fx, struct GPUOffScreen *ofs, GPUTexture *target)
-{
-	if ((*passes_left)-- == 1) {
-		GPU_framebuffer_texture_unbind(fx->gbuffer, NULL);
-		if (ofs) {
-			GPU_offscreen_bind(ofs, false);
-		}
-		else
-			GPU_framebuffer_restore();
-	}
-	else {
-		/* bind the ping buffer to the color buffer */
-		GPU_framebuffer_texture_attach(fx->gbuffer, target, 0, 0);
-	}
-}
-
-void GPU_fx_compositor_setup_XRay_pass(GPUFX *fx, bool do_xray)
-{
-	char err_out[256];
-
-	if (do_xray) {
-		if (!fx->depth_buffer_xray &&
-		    !(fx->depth_buffer_xray = GPU_texture_create_depth(fx->gbuffer_dim[0], fx->gbuffer_dim[1], err_out)))
-		{
-			printf("%.256s\n", err_out);
-			cleanup_fx_gl_data(fx, true);
-			return;
-		}
-	}
-	else {
-		if (fx->depth_buffer_xray) {
-			GPU_framebuffer_texture_detach(fx->depth_buffer_xray);
-			GPU_texture_free(fx->depth_buffer_xray);
-			fx->depth_buffer_xray = NULL;
-		}
-		return;
-	}
-
-	GPU_framebuffer_texture_detach(fx->depth_buffer);
-
-	/* first depth buffer, because system assumes read/write buffers */
-	GPU_framebuffer_texture_attach(fx->gbuffer, fx->depth_buffer_xray, 0, 0);
-}
-
-
-void GPU_fx_compositor_XRay_resolve(GPUFX *fx)
-{
-	GPUShader *depth_resolve_shader;
-	GPU_framebuffer_texture_detach(fx->depth_buffer_xray);
-
-	/* attach regular framebuffer */
-	GPU_framebuffer_texture_attach(fx->gbuffer, fx->depth_buffer, 0, 0);
-
-	/* full screen quad where we will always write to depth buffer */
-	gpuPushAttrib(GPU_DEPTH_BUFFER_BIT | GPU_SCISSOR_BIT);
-	glDepthFunc(GL_ALWAYS);
-	/* disable scissor from sculpt if any */
-	glDisable(GL_SCISSOR_TEST);
-	/* disable writing to color buffer, it's depth only pass */
-	glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
-
-	depth_resolve_shader = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_RESOLVE, false);
-
-	if (depth_resolve_shader) {
-		GPUDepthResolveInterface *interface = GPU_fx_shader_get_interface(depth_resolve_shader);
-
-		/* set up quad buffer */
-		GWN_batch_program_set(fx->quad_batch, GPU_shader_get_program(depth_resolve_shader), GPU_shader_get_interface(depth_resolve_shader));
-
-		GPU_texture_bind(fx->depth_buffer_xray, 0);
-		GPU_texture_compare_mode(fx->depth_buffer_xray, false);
-		GPU_texture_filter_mode(fx->depth_buffer_xray, true);
-		GPU_shader_uniform_texture(depth_resolve_shader, interface->depth_uniform, fx->depth_buffer_xray);
-
-		/* draw */
-		GWN_batch_draw(fx->quad_batch);
-
-		/* disable bindings */
-		GPU_texture_compare_mode(fx->depth_buffer_xray, true);
-		GPU_texture_filter_mode(fx->depth_buffer_xray, false);
-		GPU_texture_unbind(fx->depth_buffer_xray);
-
-		GPU_shader_unbind();
-	}
-
-	glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
-
-	gpuPopAttrib();
-}
-
-
-bool GPU_fx_do_composite_pass(
-        GPUFX *fx, float projmat[4][4], bool is_persp,
-        struct Scene *scene, struct GPUOffScreen *ofs)
-{
-	GPUTexture *src, *target;
-	int numslots = 0;
-	float invproj[4][4];
-	int i;
-	float dfdyfac[2];
-	/* number of passes left. when there are no more passes, the result is passed to the frambuffer */
-	int passes_left = fx->num_passes;
-	/* view vectors for the corners of the view frustum. Can be used to recreate the world space position easily */
-	float viewvecs[3][4] = {
-	    {-1.0f, -1.0f, -1.0f, 1.0f},
-	    {1.0f, -1.0f, -1.0f, 1.0f},
-	    {-1.0f, 1.0f, -1.0f, 1.0f}
-	};
-
-	if (fx->effects == 0)
-		return false;
-
-	GPU_get_dfdy_factors(dfdyfac);
-	/* first, unbind the render-to-texture framebuffer */
-	GPU_framebuffer_texture_detach(fx->color_buffer);
-	GPU_framebuffer_texture_detach(fx->depth_buffer);
-
-	if (fx->restore_stencil) {
-		gpuPopAttrib();
-	}
-
-	src = fx->color_buffer;
-	target = fx->color_buffer_sec;
-
-	/* full screen FX pass */
-
-	/* invert the view matrix */
-	invert_m4_m4(invproj, projmat);
-
-	/* convert the view vectors to view space */
-	for (i = 0; i < 3; i++) {
-		mul_m4_v4(invproj, viewvecs[i]);
-		/* normalized trick see http://www.derschmale.com/2014/01/26/reconstructing-positions-from-the-depth-buffer */
-		mul_v3_fl(viewvecs[i], 1.0f / viewvecs[i][3]);
-		if (is_persp)
-			mul_v3_fl(viewvecs[i], 1.0f / viewvecs[i][2]);
-		viewvecs[i][3] = 1.0;
-	}
-
-	/* we need to store the differences */
-	viewvecs[1][0] -= viewvecs[0][0];
-	viewvecs[1][1] = viewvecs[2][1] - viewvecs[0][1];
-
-	/* calculate a depth offset as well */
-	if (!is_persp) {
-		float vec_far[] = {-1.0f, -1.0f, 1.0f, 1.0f};
-		mul_m4_v4(invproj, vec_far);
-		mul_v3_fl(vec_far, 1.0f / vec_far[3]);
-		viewvecs[1][2] = vec_far[2] - viewvecs[0][2];
-	}
-
-	glDisable(GL_DEPTH_TEST);
-
-	/* ssao pass */
-	if (fx->effects & GPU_FX_FLAG_SSAO) {
-		GPUShader *ssao_shader;
-		ssao_shader = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_SSAO, is_persp);
-		if (ssao_shader) {
-			const GPUSSAOSettings *fx_ssao = fx->settings.ssao;
-			/* adjust attenuation to be scale invariant */
-			float attenuation = fx_ssao->attenuation / (fx_ssao->distance_max * fx_ssao->distance_max);
-			float ssao_params[4] = {fx_ssao->distance_max, fx_ssao->factor, attenuation, 0.0f};
-			float sample_params[3];
-
-			sample_params[0] = fx->ssao_sample_count_cache;
-			/* multiplier so we tile the random texture on screen */
-			sample_params[1] = fx->gbuffer_dim[0] / 64.0;
-			sample_params[2] = fx->gbuffer_dim[1] / 64.0;
-
-			ssao_params[3] = (passes_left == 1 && !ofs) ? dfdyfac[0] : dfdyfac[1];
-
-			GPUSSAOShaderInterface *interface = GPU_fx_shader_get_interface(ssao_shader);
-
-			GWN_batch_program_set(fx->quad_batch, GPU_shader_get_program(ssao_shader), GPU_shader_get_interface(ssao_shader));
-
-			GPU_shader_uniform_vector(ssao_shader, interface->ssao_uniform, 4, 1, ssao_params);
-			GPU_shader_uniform_vector(ssao_shader, interface->ssao_color_uniform, 4, 1, fx_ssao->color);
-			GPU_shader_uniform_vector(ssao_shader, interface->viewvecs_uniform, 4, 3, viewvecs[0]);
-			GPU_shader_uniform_vector(ssao_shader, interface->ssao_sample_params_uniform, 3, 1, sample_params);
-
-			GPU_texture_bind(src, numslots++);
-			GPU_shader_uniform_texture(ssao_shader, interface->color_uniform, src);
-
-			GPU_texture_bind(fx->depth_buffer, numslots++);
-			GPU_texture_compare_mode(fx->depth_buffer, false);
-			GPU_texture_filter_mode(fx->depth_buffer, true);
-			GPU_shader_uniform_texture(ssao_shader, interface->depth_uniform, fx->depth_buffer);
-
-			GPU_texture_bind(fx->jitter_buffer, numslots++);
-			GPU_shader_uniform_texture(ssao_shader, interface->ssao_jitter_uniform, fx->jitter_buffer);
-
-			GPU_texture_bind(fx->ssao_spiral_samples_tex, numslots++);
-			GPU_shader_uniform_texture(ssao_shader, interface->ssao_concentric_tex, fx->ssao_spiral_samples_tex);
-
-			/* draw */
-			gpu_fx_bind_render_target(&passes_left, fx, ofs, target);
-
-			GWN_batch_draw(fx->quad_batch);
-
-			/* disable bindings */
-			GPU_texture_unbind(src);
-			GPU_texture_compare_mode(fx->depth_buffer, true);
-			GPU_texture_filter_mode(fx->depth_buffer, false);
-			GPU_texture_unbind(fx->depth_buffer);
-			GPU_texture_unbind(fx->jitter_buffer);
-			GPU_texture_unbind(fx->ssao_spiral_samples_tex);
-
-			/* may not be attached, in that case this just returns */
-			if (target) {
-				GPU_framebuffer_texture_detach(target);
-				if (ofs) {
-					GPU_offscreen_bind(ofs, false);
-				}
-				else {
-					GPU_framebuffer_restore();
-				}
-			}
-
-			/* swap here, after src/target have been unbound */
-			SWAP(GPUTexture *, target, src);
-			numslots = 0;
-		}
-	}
-
-	/* second pass, dof */
-	if (fx->effects & GPU_FX_FLAG_DOF) {
-		const GPUDOFSettings *fx_dof = fx->settings.dof;
-		float dof_params[4];
-		float scale = scene->unit.system ? scene->unit.scale_length : 1.0f;
-		/* this is factor that converts to the scene scale. focal length and sensor are expressed in mm
-		 * unit.scale_length is how many meters per blender unit we have. We want to convert to blender units though
-		 * because the shader reads coordinates in world space, which is in blender units.
-		 * Note however that focus_distance is already in blender units and shall not be scaled here (see T48157). */
-		float scale_camera = 0.001f / scale;
-		/* we want radius here for the aperture number  */
-		float aperture = 0.5f * scale_camera * fx_dof->focal_length / fx_dof->fstop;
-
-		dof_params[0] = aperture * fabsf(scale_camera * fx_dof->focal_length /
-		                                 (fx_dof->focus_distance - scale_camera * fx_dof->focal_length));
-		dof_params[1] = fx_dof->focus_distance;
-		dof_params[2] = fx->gbuffer_dim[0] / (scale_camera * fx_dof->sensor);
-		dof_params[3] = fx_dof->num_blades;
-
-		if (fx->dof_high_quality) {
-			GPUShader *dof_shader_pass1, *dof_shader_pass2, *dof_shader_pass3;
-
-			/* custom shaders close to the effect described in CryEngine 3 Graphics Gems */
-			dof_shader_pass1 = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_OF_FIELD_HQ_PASS_ONE, is_persp);
-			dof_shader_pass2 = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_OF_FIELD_HQ_PASS_TWO, is_persp);
-			dof_shader_pass3 = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_OF_FIELD_HQ_PASS_THREE, is_persp);
-
-			/* error occured, restore framebuffers and return */
-			if (!(dof_shader_pass1 && dof_shader_pass2 && dof_shader_pass3)) {
-				GPU_framebuffer_texture_unbind(fx->gbuffer, NULL);
-				GPU_framebuffer_restore();
-
-				GPU_shader_unbind();
-				return false;
-			}
-
-			/* pass first, downsample the color buffer to near/far targets and calculate coc texture */
-			{
-				float invrendertargetdim[2] = {1.0f / fx->dof_downsampled_w, 1.0f / fx->dof_downsampled_h};
-
-				GPUDOFHQPassOneInterface *interface = GPU_fx_shader_get_interface(dof_shader_pass1);
-
-				GWN_batch_program_set(fx->quad_batch, GPU_shader_get_program(dof_shader_pass1), GPU_shader_get_interface(dof_shader_pass1));
-
-				GPU_shader_uniform_vector(dof_shader_pass1, interface->dof_uniform, 4, 1, dof_params);
-				GPU_shader_uniform_vector(dof_shader_pass1, interface->invrendertargetdim_uniform, 2, 1, invrendertargetdim);
-				GPU_shader_uniform_vector(dof_shader_pass1, interface->viewvecs_uniform, 4, 3, viewvecs[0]);
-
-				GPU_shader_uniform_vector(dof_shader_pass1, interface->invrendertargetdim_uniform, 2, 1, invrendertargetdim);
-
-				GPU_texture_bind(fx->depth_buffer, numslots++);
-				GPU_texture_compare_mode(fx->depth_buffer, false);
-				GPU_texture_filter_mode(fx->depth_buffer, false);
-				GPU_shader_uniform_texture(dof_shader_pass1, interface->depth_uniform, fx->depth_buffer);
-
-				GPU_texture_bind(src, numslots++);
-				/* disable filtering for the texture so custom downsample can do the right thing */
-				GPU_texture_filter_mode(src, false);
-				GPU_shader_uniform_texture(dof_shader_pass2, interface->color_uniform, src);
-
-				/* target is the downsampled coc buffer */
-				GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_half_downsampled_near, 0, 0);
-				GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_half_downsampled_far, 1, 0);
-				GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_nearfar_coc, 2, 0);
-				/* binding takes care of setting the viewport to the downsampled size */
-				GPU_framebuffer_slots_bind(fx->gbuffer, 0);
-
-				GPU_framebuffer_check_valid(fx->gbuffer, NULL);
-
-				GWN_batch_draw(fx->quad_batch);
-
-				/* disable bindings */
-				GPU_texture_filter_mode(src, true);
-				GPU_texture_unbind(src);
-				GPU_texture_compare_mode(fx->depth_buffer, true);
-				GPU_texture_filter_mode(fx->depth_buffer, false);
-				GPU_texture_unbind(fx->depth_buffer);
-
-				GPU_framebuffer_texture_detach(fx->dof_half_downsampled_near);
-				GPU_framebuffer_texture_detach(fx->dof_half_downsampled_far);
-				GPU_framebuffer_texture_detach(fx->dof_nearfar_coc);
-				GPU_framebuffer_texture_unbind(fx->gbuffer, fx->dof_half_downsampled_near);
-
-				numslots = 0;
-			}
-
-			/* second pass, shoot quads for every pixel in the downsampled buffers, scaling according
-			 * to circle of confusion */
-			{
-				int rendertargetdim[2] = {fx->dof_downsampled_w, fx->dof_downsampled_h};
-				float selection[2] = {0.0f, 1.0f};
-
-				GPUDOFHQPassTwoInterface *interface = GPU_fx_shader_get_interface(dof_shader_pass2);
-
-				GWN_batch_program_set(fx->point_batch, GPU_shader_get_program(dof_shader_pass2), GPU_shader_get_interface(dof_shader_pass2));
-
-				GPU_texture_bind(fx->dof_nearfar_coc, numslots++);
-				GPU_texture_bind(fx->dof_half_downsampled_far, numslots++);
-				GPU_texture_bind(fx->dof_half_downsampled_near, numslots++);
-
-				GPU_shader_uniform_vector(dof_shader_pass2, interface->dof_uniform, 4, 1, dof_params);
-				GPU_shader_uniform_vector_int(dof_shader_pass2, interface->rendertargetdim_uniform, 2, 1, rendertargetdim);
-				GPU_shader_uniform_vector(dof_shader_pass2, interface->select_uniform, 2, 1, selection);
-				GPU_shader_uniform_texture(dof_shader_pass2, interface->coc_uniform, fx->dof_nearfar_coc);
-				GPU_shader_uniform_texture(dof_shader_pass2, interface->color_uniform, fx->dof_half_downsampled_far);
-				GPU_texture_filter_mode(fx->dof_half_downsampled_far, false);
-
-				/* target is the downsampled coc buffer */
-				GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_far_blur, 0, 0);
-				GPU_texture_bind_as_framebuffer(fx->dof_far_blur);
-
-				glDisable(GL_DEPTH_TEST);
-				glEnable(GL_BLEND);
-				glBlendFunc(GL_ONE, GL_ONE);
-				glPointSize(1.0f);
-				/* have to clear the buffer unfortunately */
-				glClearColor(0.0, 0.0, 0.0, 0.0);
-				glClear(GL_COLOR_BUFFER_BIT);
-				/* the draw call we all waited for, draw a point per pixel, scaled per circle of confusion */
-				// GWN_batch_draw_stupid_instanced(fx->point_batch, 0, fx->dof_downsampled_w * fx->dof_downsampled_h, 0, 0, 0, NULL, NULL);
-
-				GPU_texture_unbind(fx->dof_half_downsampled_far);
-				GPU_framebuffer_texture_detach(fx->dof_far_blur);
-
-				selection[0] = 1.0f;
-				selection[1] = 0.0f;
-
-				GPU_shader_uniform_vector(dof_shader_pass2, interface->select_uniform, 2, 1, selection);
-				GPU_shader_uniform_texture(dof_shader_pass2, interface->color_uniform, fx->dof_half_downsampled_near);
-				GPU_texture_filter_mode(fx->dof_half_downsampled_near, false);
-
-				GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_near_blur, 0, 0);
-				/* have to clear the buffer unfortunately */
-				glClear(GL_COLOR_BUFFER_BIT);
-				/* the draw call we all waited for, draw a point per pixel, scaled per circle of confusion */
-				// GWN_batch_draw_stupid_instanced(fx->point_batch, 0, fx->dof_downsampled_w * fx->dof_downsampled_h, 0, 0, 0, NULL, NULL);
-				GWN_batch_program_use_end(fx->point_batch);
-
-				/* disable bindings */
-				glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
-				glDisable(GL_BLEND);
-
-				GPU_framebuffer_texture_detach(fx->dof_near_blur);
-
-				GPU_texture_unbind(fx->dof_half_downsampled_near);
-				GPU_texture_unbind(fx->dof_nearfar_coc);
-
-				GPU_framebuffer_texture_unbind(fx->gbuffer, fx->dof_far_blur);
-				numslots = 0;
-			}
-
-			/* third pass, accumulate the near/far blur fields */
-			{
-				float invrendertargetdim[2] = {1.0f / fx->dof_downsampled_w, 1.0f / fx->dof_downsampled_h};
-
-				GPUDOFHQPassThreeInterface *interface = GPU_fx_shader_get_interface(dof_shader_pass3);
-
-				GWN_batch_program_set(fx->quad_batch, GPU_shader_get_program(dof_shader_pass3), GPU_shader_get_interface(dof_shader_pass3));
-
-				GPU_shader_uniform_vector(dof_shader_pass3, interface->dof_uniform, 4, 1, dof_params);
-
-				GPU_shader_uniform_vector(dof_shader_pass3, interface->invrendertargetdim_uniform, 2, 1, invrendertargetdim);
-				GPU_shader_uniform_vector(dof_shader_pass3, interface->viewvecs_uniform, 4, 3, viewvecs[0]);
-
-				GPU_texture_bind(fx->dof_near_blur, numslots++);
-				GPU_shader_uniform_texture(dof_shader_pass3, interface->near_uniform, fx->dof_near_blur);
-				GPU_texture_filter_mode(fx->dof_near_blur, true);
-
-				GPU_texture_bind(fx->dof_far_blur, numslots++);
-				GPU_shader_uniform_texture(dof_shader_pass3, interface->far_uniform, fx->dof_far_blur);
-				GPU_texture_filter_mode(fx->dof_far_blur, true);
-
-				GPU_texture_bind(fx->depth_buffer, numslots++);
-				GPU_texture_compare_mode(fx->depth_buffer, false);
-				GPU_texture_filter_mode(fx->depth_buffer, false);
-				GPU_shader_uniform_texture(dof_shader_pass3, interface->depth_uniform, fx->depth_buffer);
-
-				GPU_texture_bind(src, numslots++);
-				GPU_shader_uniform_texture(dof_shader_pass3, interface->color_uniform, src);
-
-				/* if this is the last pass, prepare for rendering on the frambuffer */
-				gpu_fx_bind_render_target(&passes_left, fx, ofs, target);
-
-				GWN_batch_draw(fx->quad_batch);
-
-				/* disable bindings */
-				GPU_texture_unbind(fx->dof_near_blur);
-				GPU_texture_unbind(fx->dof_far_blur);
-				GPU_texture_unbind(src);
-				GPU_texture_compare_mode(fx->depth_buffer, true);
-				GPU_texture_unbind(fx->depth_buffer);
-
-				/* may not be attached, in that case this just returns */
-				if (target) {
-					GPU_framebuffer_texture_detach(target);
-					if (ofs) {
-						GPU_offscreen_bind(ofs, false);
-					}
-					else {
-						GPU_framebuffer_restore();
-					}
-				}
-
-				numslots = 0;
-			}
-		}
-		else {
-			GPUShader *dof_shader_pass1, *dof_shader_pass2, *dof_shader_pass3, *dof_shader_pass4, *dof_shader_pass5;
-
-			/* DOF effect has many passes but most of them are performed
-			 * on a texture whose dimensions are 4 times less than the original
-			 * (16 times lower than original screen resolution).
-			 * Technique used is not very exact but should be fast enough and is based
-			 * on "Practical Post-Process Depth of Field"
-			 * see http://http.developer.nvidia.com/GPUGems3/gpugems3_ch28.html */
-			dof_shader_pass1 = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_ONE, is_persp);
-			dof_shader_pass2 = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_TWO, is_persp);
-			dof_shader_pass3 = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_THREE, is_persp);
-			dof_shader_pass4 = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_FOUR, is_persp);
-			dof_shader_pass5 = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_FIVE, is_persp);
-
-			/* error occured, restore framebuffers and return */
-			if (!(dof_shader_pass1 && dof_shader_pass2 && dof_shader_pass3 && dof_shader_pass4 && dof_shader_pass5)) {
-				GPU_framebuffer_texture_unbind(fx->gbuffer, NULL);
-				GPU_framebuffer_restore();
-
-				GPU_shader_unbind();
-				return false;
-			}
-
-			/* pass first, first level of blur in low res buffer */
-			{
-				float invrendertargetdim[2] = {1.0f / fx->gbuffer_dim[0], 1.0f / fx->gbuffer_dim[1]};
-
-				GPUDOFPassOneInterface *interface = GPU_fx_shader_get_interface(dof_shader_pass1);
-
-				GWN_batch_program_set(fx->quad_batch, GPU_shader_get_program(dof_shader_pass1), GPU_shader_get_interface(dof_shader_pass1));
-
-				GPU_shader_uniform_vector(dof_shader_pass1, interface->dof_uniform, 4, 1, dof_params);
-				GPU_shader_uniform_vector(dof_shader_pass1, interface->invrendertargetdim_uniform, 2, 1, invrendertargetdim);
-				GPU_shader_uniform_vector(dof_shader_pass1, interface->viewvecs_uniform, 4, 3, viewvecs[0]);
-
-				GPU_texture_bind(src, numslots++);
-				GPU_shader_uniform_texture(dof_shader_pass1, interface->color_uniform, src);
-
-				GPU_texture_bind(fx->depth_buffer, numslots++);
-				GPU_texture_compare_mode(fx->depth_buffer, false);
-				GPU_texture_filter_mode(fx->depth_buffer, true);
-				GPU_shader_uniform_texture(dof_shader_pass1, interface->depth_uniform, fx->depth_buffer);
-
-				/* target is the downsampled coc buffer */
-				GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_near_coc_buffer, 0, 0);
-				/* binding takes care of setting the viewport to the downsampled size */
-				GPU_texture_bind_as_framebuffer(fx->dof_near_coc_buffer);
-
-				GWN_batch_draw(fx->quad_batch);
-				/* disable bindings */
-				GPU_texture_unbind(src);
-				GPU_texture_compare_mode(fx->depth_buffer, true);
-				GPU_texture_filter_mode(fx->depth_buffer, false);
-				GPU_texture_unbind(fx->depth_buffer);
-
-				GPU_framebuffer_texture_detach(fx->dof_near_coc_buffer);
-				numslots = 0;
-			}
-
-			/* second pass, gaussian blur the downsampled image */
-			{
-				float invrendertargetdim[2] = {1.0f / GPU_texture_width(fx->dof_near_coc_blurred_buffer),
-				                               1.0f / GPU_texture_height(fx->dof_near_coc_blurred_buffer)};
-				float tmp = invrendertargetdim[0];
-				invrendertargetdim[0] = 0.0f;
-
-				GPUDOFPassTwoInterface *interface = GPU_fx_shader_get_interface(dof_shader_pass2);
-
-				dof_params[2] = GPU_texture_width(fx->dof_near_coc_blurred_buffer) / (scale_camera * fx_dof->sensor);
-
-				/* Blurring vertically */
-				GWN_batch_program_set(fx->quad_batch, GPU_shader_get_program(dof_shader_pass2), GPU_shader_get_interface(dof_shader_pass2));
-
-				GPU_shader_uniform_vector(dof_shader_pass2, interface->dof_uniform, 4, 1, dof_params);
-				GPU_shader_uniform_vector(dof_shader_pass2, interface->invrendertargetdim_uniform, 2, 1, invrendertargetdim);
-				GPU_shader_uniform_vector(dof_shader_pass2, interface->viewvecs_uniform, 4, 3, viewvecs[0]);
-
-				GPU_texture_bind(fx->depth_buffer, numslots++);
-				GPU_texture_compare_mode(fx->depth_buffer, false);
-				GPU_texture_filter_mode(fx->depth_buffer, true);
-				GPU_shader_uniform_texture(dof_shader_pass2, interface->depth_uniform, fx->depth_buffer);
-
-				GPU_texture_bind(fx->dof_near_coc_buffer, numslots++);
-				GPU_shader_uniform_texture(dof_shader_pass2, interface->color_uniform, fx->dof_near_coc_buffer);
-
-				/* use final buffer as a temp here */
-				GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_near_coc_final_buffer, 0, 0);
-
-				/* Drawing quad */
-				GWN_batch_draw(fx->quad_batch);
-
-				/* Rebind Shader */
-				GWN_batch_program_set(fx->quad_batch, GPU_shader_get_program(dof_shader_pass2), GPU_shader_get_interface(dof_shader_pass2));
-
-				/* *unbind/detach */
-				GPU_texture_unbind(fx->dof_near_coc_buffer);
-
-				GPU_framebuffer_texture_detach(fx->dof_near_coc_final_buffer);
-
-				/* Blurring horizontally */
-				invrendertargetdim[0] = tmp;
-				invrendertargetdim[1] = 0.0f;
-				GPU_shader_uniform_vector(dof_shader_pass2, interface->invrendertargetdim_uniform, 2, 1, invrendertargetdim);
-
-				GPU_texture_bind(fx->dof_near_coc_final_buffer, numslots++);
-				GPU_shader_uniform_texture(dof_shader_pass2, interface->color_uniform, fx->dof_near_coc_final_buffer);
-
-				GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_near_coc_blurred_buffer, 0, 0);
-
-				GWN_batch_draw(fx->quad_batch);
-
-				/* *unbind/detach */
-				GPU_texture_compare_mode(fx->depth_buffer, true);
-				GPU_texture_filter_mode(fx->depth_buffer, false);
-				GPU_texture_unbind(fx->depth_buffer);
-
-				GPU_texture_unbind(fx->dof_near_coc_final_buffer);
-				GPU_framebuffer_texture_detach(fx->dof_near_coc_blurred_buffer);
-
-				dof_params[2] = fx->gbuffer_dim[0] / (scale_camera * fx_dof->sensor);
-
-				numslots = 0;
-			}
-
-			/* third pass, calculate near coc */
-			{
-				GPUDOFPassThreeInterface *interface = GPU_fx_shader_get_interface(dof_shader_pass3);
-
-				GWN_batch_program_set(fx->quad_batch, GPU_shader_get_program(dof_shader_pass3), GPU_shader_get_interface(dof_shader_pass3));
-
-				GPU_texture_bind(fx->dof_near_coc_buffer, numslots++);
-				GPU_shader_uniform_texture(dof_shader_pass3, interface->near_coc_downsampled, fx->dof_near_coc_buffer);
-
-				GPU_texture_bind(fx->dof_near_coc_blurred_buffer, numslots++);
-				GPU_shader_uniform_texture(dof_shader_pass3, interface->near_coc_blurred, fx->dof_near_coc_blurred_buffer);
-
-				GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_near_coc_final_buffer, 0, 0);
-
-				GWN_batch_draw(fx->quad_batch);
-				/* disable bindings */
-				GPU_texture_unbind(fx->dof_near_coc_buffer);
-				GPU_texture_unbind(fx->dof_near_coc_blurred_buffer);
-
-				/* unbinding here restores the size to the original */
-				GPU_framebuffer_texture_detach(fx->dof_near_coc_final_buffer);
-
-				numslots = 0;
-			}
-
-			/* fourth pass blur final coc once to eliminate discontinuities */
-			{
-				float invrendertargetdim[2] = {1.0f / GPU_texture_width(fx->dof_near_coc_blurred_buffer),
-				                               1.0f / GPU_texture_height(fx->dof_near_coc_blurred_buffer)};
-
-				GPUDOFPassFourInterface *interface = GPU_fx_shader_get_interface(dof_shader_pass4);
-
-				GWN_batch_program_set(fx->quad_batch, GPU_shader_get_program(dof_shader_pass4), GPU_shader_get_interface(dof_shader_pass4));
-
-				GPU_texture_bind(fx->dof_near_coc_final_buffer, numslots++);
-				GPU_shader_uniform_texture(dof_shader_pass4, interface->near_coc_downsampled, fx->dof_near_coc_final_buffer);
-				GPU_shader_uniform_vector(dof_shader_pass4, interface->invrendertargetdim_uniform, 2, 1, invrendertargetdim);
-
-				GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_near_coc_buffer, 0, 0);
-
-				GWN_batch_draw(fx->quad_batch);
-				/* disable bindings */
-				GPU_texture_unbind(fx->dof_near_coc_final_buffer);
-
-				/* unbinding here restores the size to the original */
-				GPU_framebuffer_texture_unbind(fx->gbuffer, fx->dof_near_coc_buffer);
-				GPU_framebuffer_texture_detach(fx->dof_near_coc_buffer);
-
-				numslots = 0;
-			}
-
-			/* final pass, merge blurred layers according to final calculated coc */
-			{
-				float invrendertargetdim[2] = {1.0f / fx->gbuffer_dim[0], 1.0f / fx->gbuffer_dim[1]};
-
-				GPUDOFPassFiveInterface *interface = GPU_fx_shader_get_interface(dof_shader_pass5);
-
-				GWN_batch_program_set(fx->quad_batch, GPU_shader_get_program(dof_shader_pass5), GPU_shader_get_interface(dof_shader_pass5));
-
-				GPU_shader_uniform_vector(dof_shader_pass5, interface->dof_uniform, 4, 1, dof_params);
-				GPU_shader_uniform_vector(dof_shader_pass5, interface->invrendertargetdim_uniform, 2, 1, invrendertargetdim);
-				GPU_shader_uniform_vector(dof_shader_pass5, interface->viewvecs_uniform, 4, 3, viewvecs[0]);
-
-				GPU_texture_bind(src, numslots++);
-				GPU_shader_uniform_texture(dof_shader_pass5, interface->original_uniform, src);
-
-				GPU_texture_bind(fx->dof_near_coc_blurred_buffer, numslots++);
-				GPU_shader_uniform_texture(dof_shader_pass5, interface->high_blurred_uniform, fx->dof_near_coc_blurred_buffer);
-
-				GPU_texture_bind(fx->dof_near_coc_buffer, numslots++);
-				GPU_shader_uniform_texture(dof_shader_pass5, interface->medium_blurred_uniform, fx->dof_near_coc_buffer);
-
-				GPU_texture_bind(fx->depth_buffer, numslots++);
-				GPU_texture_compare_mode(fx->depth_buffer, false);
-				GPU_texture_filter_mode(fx->depth_buffer, true);
-				GPU_shader_uniform_texture(dof_shader_pass5, interface->depth_uniform, fx->depth_buffer);
-
-				/* if this is the last pass, prepare for rendering on the frambuffer */
-				gpu_fx_bind_render_target(&passes_left, fx, ofs, target);
-
-				GWN_batch_draw(fx->quad_batch);
-				/* disable bindings */
-				GPU_texture_unbind(fx->dof_near_coc_buffer);
-				GPU_texture_unbind(fx->dof_near_coc_blurred_buffer);
-				GPU_texture_unbind(src);
-				GPU_texture_compare_mode(fx->depth_buffer, true);
-				GPU_texture_filter_mode(fx->depth_buffer, false);
-				GPU_texture_unbind(fx->depth_buffer);
-
-				/* may not be attached, in that case this just returns */
-				if (target) {
-					GPU_framebuffer_texture_detach(target);
-					if (ofs) {
-						GPU_offscreen_bind(ofs, false);
-					}
-					else {
-						GPU_framebuffer_restore();
-					}
-				}
-
-				SWAP(GPUTexture *, target, src);
-				numslots = 0;
-			}
-		}
-	}
-
-	GPU_shader_unbind();
-
-	return true;
-}
-
-void GPU_fx_compositor_init_dof_settings(GPUDOFSettings *fx_dof)
-{
-	fx_dof->fstop = 128.0f;
-	fx_dof->focal_length = 1.0f;
-	fx_dof->focus_distance = 1.0f;
-	fx_dof->sensor = 1.0f;
-	fx_dof->num_blades = 6;
-	fx_dof->ratio = 1.0f;
-}
-
-void GPU_fx_compositor_init_ssao_settings(GPUSSAOSettings *fx_ssao)
-{
-	fx_ssao->factor = 1.0f;
-	fx_ssao->distance_max = 0.2f;
-	fx_ssao->attenuation = 1.0f;
-	fx_ssao->samples = 20;
-}
-
-void GPU_fx_shader_init_interface(GPUShader *shader, GPUFXShaderEffect effect)
-{
-	if (!shader)
-		return;
-
-	switch (effect) {
-		case GPU_SHADER_FX_SSAO:
-		{
-			GPUSSAOShaderInterface *interface = MEM_mallocN(sizeof(GPUSSAOShaderInterface), "GPUSSAOShaderInterface");
-
-			interface->ssao_uniform = GPU_shader_get_uniform(shader, "ssao_params");
-			interface->ssao_color_uniform = GPU_shader_get_uniform(shader, "ssao_color");
-			interface->color_uniform = GPU_shader_get_uniform(shader, "colorbuffer");
-			interface->depth_uniform = GPU_shader_get_uniform(shader, "depthbuffer");
-			interface->viewvecs_uniform = GPU_shader_get_uniform(shader, "viewvecs");
-			interface->ssao_sample_params_uniform = GPU_shader_get_uniform(shader, "ssao_sample_params");
-			interface->ssao_concentric_tex = GPU_shader_get_uniform(shader, "ssao_concentric_tex");
-			interface->ssao_jitter_uniform = GPU_shader_get_uniform(shader, "jitter_tex");
-
-			GPU_fx_shader_set_interface(shader, interface);
-			break;
-		}
-
-		case GPU_SHADER_FX_DEPTH_OF_FIELD_HQ_PASS_ONE:
-		{
-			GPUDOFHQPassOneInterface *interface = MEM_mallocN(sizeof(GPUDOFHQPassOneInterface), "GPUDOFHQPassOneInterface");
-
-			interface->invrendertargetdim_uniform = GPU_shader_get_uniform(shader, "invrendertargetdim");
-			interface->color_uniform = GPU_shader_get_uniform(shader, "colorbuffer");
-			interface->dof_uniform = GPU_shader_get_uniform(shader, "dof_params");
-			interface->depth_uniform = GPU_shader_get_uniform(shader, "depthbuffer");
-			interface->viewvecs_uniform = GPU_shader_get_uniform(shader, "viewvecs");
-
-			GPU_fx_shader_set_interface(shader, interface);
-			break;
-		}
-
-		case GPU_SHADER_FX_DEPTH_OF_FIELD_HQ_PASS_TWO:
-		{
-			GPUDOFHQPassTwoInterface *interface = MEM_mallocN(sizeof(GPUDOFHQPassTwoInterface), "GPUDOFHQPassTwoInterface");
-
-			interface->rendertargetdim_uniform = GPU_shader_get_uniform(shader, "rendertargetdim");
-			interface->color_uniform = GPU_shader_get_uniform(shader, "colorbuffer");
-			interface->coc_uniform = GPU_shader_get_uniform(shader, "cocbuffer");
-			interface->select_uniform = GPU_shader_get_uniform(shader, "layerselection");
-			interface->dof_uniform = GPU_shader_get_uniform(shader, "dof_params");
-
-			GPU_fx_shader_set_interface(shader, interface);
-			break;
-		}
-
-		case GPU_SHADER_FX_DEPTH_OF_FIELD_HQ_PASS_THREE:
-		{
-			GPUDOFHQPassThreeInterface *interface = MEM_mallocN(sizeof(GPUDOFHQPassThreeInterface), "GPUDOFHQPassThreeInterface");
-
-			interface->dof_uniform = GPU_shader_get_uniform(shader, "dof_params");
-			interface->invrendertargetdim_uniform = GPU_shader_get_uniform(shader, "invrendertargetdim");
-			interface->color_uniform = GPU_shader_get_uniform(shader, "colorbuffer");
-			interface->far_uniform = GPU_shader_get_uniform(shader, "farbuffer");
-			interface->near_uniform = GPU_shader_get_uniform(shader, "nearbuffer");
-			interface->viewvecs_uniform = GPU_shader_get_uniform(shader, "viewvecs");
-			interface->depth_uniform = GPU_shader_get_uniform(shader, "depthbuffer");
-
-			GPU_fx_shader_set_interface(shader, interface);
-			break;
-		}
-
-		case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_ONE:
-		{
-			GPUDOFPassOneInterface *interface = MEM_mallocN(sizeof(GPUDOFPassOneInterface), "GPUDOFPassOneInterface");
-
-			interface->dof_uniform = GPU_shader_get_uniform(shader, "dof_params");
-			interface->invrendertargetdim_uniform = GPU_shader_get_uniform(shader, "invrendertargetdim");
-			interface->color_uniform = GPU_shader_get_uniform(shader, "colorbuffer");
-			interface->depth_uniform = GPU_shader_get_uniform(shader, "depthbuffer");
-			interface->viewvecs_uniform = GPU_shader_get_uniform(shader, "viewvecs");
-
-			GPU_fx_shader_set_interface(shader, interface);
-			break;
-		}
-		case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_TWO:
-		{
-			GPUDOFPassTwoInterface *interface = MEM_mallocN(sizeof(GPUDOFPassTwoInterface), "GPUDOFPassTwoInterface");
-
-			interface->dof_uniform = GPU_shader_get_uniform(shader, "dof_params");
-			interface->invrendertargetdim_uniform = GPU_shader_get_uniform(shader, "invrendertargetdim");
-			interface->color_uniform = GPU_shader_get_uniform(shader, "colorbuffer");
-			interface->depth_uniform = GPU_shader_get_uniform(shader, "depthbuffer");
-			interface->viewvecs_uniform = GPU_shader_get_uniform(shader, "viewvecs");
-
-			GPU_fx_shader_set_interface(shader, interface);
-			break;
-		}
-		case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_THREE:
-		{
-			GPUDOFPassThreeInterface *interface = MEM_mallocN(sizeof(GPUDOFPassThreeInterface), "GPUDOFPassThreeInterface");
-
-			interface->near_coc_downsampled = GPU_shader_get_uniform(shader, "colorbuffer");
-			interface->near_coc_blurred = GPU_shader_get_uniform(shader, "blurredcolorbuffer");
-
-			GPU_fx_shader_set_interface(shader, interface);
-			break;
-		}
-		case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_FOUR:
-		{
-			GPUDOFPassFourInterface *interface = MEM_mallocN(sizeof(GPUDOFPassFourInterface), "GPUDOFPassFourInterface");
-
-			interface->near_coc_downsampled = GPU_shader_get_uniform(shader, "colorbuffer");
-			interface->invrendertargetdim_uniform = GPU_shader_get_uniform(shader, "invrendertargetdim");
-
-			GPU_fx_shader_set_interface(shader, interface);
-			break;
-		}
-		case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_FIVE:
-		{
-			GPUDOFPassFiveInterface *interface = MEM_mallocN(sizeof(GPUDOFPassFiveInterface), "GPUDOFPassFiveInterface");
-
-			interface->medium_blurred_uniform = GPU_shader_get_uniform(shader, "mblurredcolorbuffer");
-			interface->high_blurred_uniform = GPU_shader_get_uniform(shader, "blurredcolorbuffer");
-			interface->dof_uniform = GPU_shader_get_uniform(shader, "dof_params");
-			interface->invrendertargetdim_uniform = GPU_shader_get_uniform(shader, "invrendertargetdim");
-			interface->original_uniform = GPU_shader_get_uniform(shader, "colorbuffer");
-			interface->depth_uniform = GPU_shader_get_uniform(shader, "depthbuffer");
-			interface->viewvecs_uniform = GPU_shader_get_uniform(shader, "viewvecs");
-
-			GPU_fx_shader_set_interface(shader, interface);
-			break;
-		}
-
-		case GPU_SHADER_FX_DEPTH_RESOLVE:
-		{
-			GPUDepthResolveInterface *interface = MEM_mallocN(sizeof(GPUDepthResolveInterface), "GPUDepthResolveInterface");
-
-			interface->depth_uniform = GPU_shader_get_uniform(shader, "depthbuffer");
-
-			GPU_fx_shader_set_interface(shader, interface);
-			break;
-		}
-
-		default:
-			break;
-	}
-}
-
diff --git a/source/blender/gpu/intern/gpu_draw.c b/source/blender/gpu/intern/gpu_draw.c
index fdb5894e9edec90a5034ca59bacd1b97e0fcd091..8b344716a9cd25720657c91116c185c2958c6179 100644
--- a/source/blender/gpu/intern/gpu_draw.c
+++ b/source/blender/gpu/intern/gpu_draw.c
@@ -1460,7 +1460,6 @@ static struct GPUMaterialState {
 	Material *gmatbuf_fixed[FIXEDMAT];
 	Material *gboundmat;
 	Object *gob;
-	eObjectMode gob_object_mode;
 	DupliObject *dob;
 	Scene *gscene;
 	int glay;
@@ -1555,7 +1554,7 @@ void GPU_end_dupli_object(void)
 
 void GPU_begin_object_materials(
         View3D *v3d, RegionView3D *rv3d, Scene *scene, ViewLayer *view_layer, Object *ob,
-        bool glsl, const eObjectMode object_mode, bool *do_alpha_after)
+        bool glsl, bool *do_alpha_after)
 {
 	Material *ma;
 	GPUMaterial *gpumat;
@@ -1593,7 +1592,7 @@ void GPU_begin_object_materials(
 
 #ifdef WITH_GAMEENGINE
 	if (rv3d->rflag & RV3D_IS_GAME_ENGINE) {
-		ob = BKE_object_lod_matob_get(ob, view_layer, object_mode);
+		ob = BKE_object_lod_matob_get(ob, view_layer);
 	}
 #else
 	UNUSED_VARS(view_layer);
@@ -1617,7 +1616,6 @@ void GPU_begin_object_materials(
 		GMS.two_sided_lighting = (((Mesh *)ob->data)->flag & ME_TWOSIDED) != 0;
 
 	GMS.gob = ob;
-	GMS.gob_object_mode = object_mode;
 	GMS.gscene = scene;
 	GMS.is_opensubdiv = use_opensubdiv;
 	GMS.totmat = use_matcap ? 1 : ob->totcol + 1;  /* materials start from 1, default material is 0 */
@@ -1838,7 +1836,7 @@ int GPU_object_material_bind(int nr, void *attribs)
 			}
 
 			GPU_material_bind(
-			        gpumat, GMS.gob->lay, GMS.glay, 1.0, !(GMS.gob_object_mode & OB_MODE_TEXTURE_PAINT),
+			        gpumat, GMS.gob->lay, GMS.glay, 1.0, !(GMS.gob->mode & OB_MODE_TEXTURE_PAINT),
 			        GMS.gviewmat, GMS.gviewinv, GMS.gviewcamtexcofac);
 
 			auto_bump_scale = GMS.gob->derivedFinal != NULL ? GMS.gob->derivedFinal->auto_bump_scale : 1.0f;
@@ -2383,7 +2381,7 @@ static GPUAttribStack state = {
 };
 
 #define AttribStack state
-#define Gwn_VertAttr state.attrib_stack[state.top]
+#define Attrib state.attrib_stack[state.top]
 
 /**
  * Replacement for glPush/PopAttributes
@@ -2393,48 +2391,48 @@ static GPUAttribStack state = {
  */
 void gpuPushAttrib(eGPUAttribMask mask)
 {
-	Gwn_VertAttr.mask = mask;
+	Attrib.mask = mask;
 
 	if ((mask & GPU_DEPTH_BUFFER_BIT) != 0) {
-		Gwn_VertAttr.is_depth_test = glIsEnabled(GL_DEPTH_TEST);
-		glGetIntegerv(GL_DEPTH_FUNC, &Gwn_VertAttr.depth_func);
-		glGetDoublev(GL_DEPTH_CLEAR_VALUE, &Gwn_VertAttr.depth_clear_value);
-		glGetBooleanv(GL_DEPTH_WRITEMASK, (GLboolean *)&Gwn_VertAttr.depth_write_mask);
+		Attrib.is_depth_test = glIsEnabled(GL_DEPTH_TEST);
+		glGetIntegerv(GL_DEPTH_FUNC, &Attrib.depth_func);
+		glGetDoublev(GL_DEPTH_CLEAR_VALUE, &Attrib.depth_clear_value);
+		glGetBooleanv(GL_DEPTH_WRITEMASK, (GLboolean *)&Attrib.depth_write_mask);
 	}
 
 	if ((mask & GPU_ENABLE_BIT) != 0) {
-		Gwn_VertAttr.is_blend = glIsEnabled(GL_BLEND);
+		Attrib.is_blend = glIsEnabled(GL_BLEND);
 
 		for (int i = 0; i < 6; i++) {
-			Gwn_VertAttr.is_clip_plane[i] = glIsEnabled(GL_CLIP_PLANE0 + i);
+			Attrib.is_clip_plane[i] = glIsEnabled(GL_CLIP_PLANE0 + i);
 		}
 
-		Gwn_VertAttr.is_cull_face = glIsEnabled(GL_CULL_FACE);
-		Gwn_VertAttr.is_depth_test = glIsEnabled(GL_DEPTH_TEST);
-		Gwn_VertAttr.is_dither = glIsEnabled(GL_DITHER);
-		Gwn_VertAttr.is_line_smooth = glIsEnabled(GL_LINE_SMOOTH);
-		Gwn_VertAttr.is_color_logic_op = glIsEnabled(GL_COLOR_LOGIC_OP);
-		Gwn_VertAttr.is_multisample = glIsEnabled(GL_MULTISAMPLE);
-		Gwn_VertAttr.is_polygon_offset_line = glIsEnabled(GL_POLYGON_OFFSET_LINE);
-		Gwn_VertAttr.is_polygon_offset_fill = glIsEnabled(GL_POLYGON_OFFSET_FILL);
-		Gwn_VertAttr.is_polygon_smooth = glIsEnabled(GL_POLYGON_SMOOTH);
-		Gwn_VertAttr.is_sample_alpha_to_coverage = glIsEnabled(GL_SAMPLE_ALPHA_TO_COVERAGE);
-		Gwn_VertAttr.is_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
-		Gwn_VertAttr.is_stencil_test = glIsEnabled(GL_STENCIL_TEST);
+		Attrib.is_cull_face = glIsEnabled(GL_CULL_FACE);
+		Attrib.is_depth_test = glIsEnabled(GL_DEPTH_TEST);
+		Attrib.is_dither = glIsEnabled(GL_DITHER);
+		Attrib.is_line_smooth = glIsEnabled(GL_LINE_SMOOTH);
+		Attrib.is_color_logic_op = glIsEnabled(GL_COLOR_LOGIC_OP);
+		Attrib.is_multisample = glIsEnabled(GL_MULTISAMPLE);
+		Attrib.is_polygon_offset_line = glIsEnabled(GL_POLYGON_OFFSET_LINE);
+		Attrib.is_polygon_offset_fill = glIsEnabled(GL_POLYGON_OFFSET_FILL);
+		Attrib.is_polygon_smooth = glIsEnabled(GL_POLYGON_SMOOTH);
+		Attrib.is_sample_alpha_to_coverage = glIsEnabled(GL_SAMPLE_ALPHA_TO_COVERAGE);
+		Attrib.is_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
+		Attrib.is_stencil_test = glIsEnabled(GL_STENCIL_TEST);
 	}
 
 	if ((mask & GPU_SCISSOR_BIT) != 0) {
-		Gwn_VertAttr.is_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
-		glGetIntegerv(GL_SCISSOR_BOX, (GLint *)&Gwn_VertAttr.scissor_box);
+		Attrib.is_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
+		glGetIntegerv(GL_SCISSOR_BOX, (GLint *)&Attrib.scissor_box);
 	}
 
 	if ((mask & GPU_VIEWPORT_BIT) != 0) {
-		glGetDoublev(GL_DEPTH_RANGE, (GLdouble *)&Gwn_VertAttr.near_far);
-		glGetIntegerv(GL_VIEWPORT, (GLint *)&Gwn_VertAttr.viewport);
+		glGetDoublev(GL_DEPTH_RANGE, (GLdouble *)&Attrib.near_far);
+		glGetIntegerv(GL_VIEWPORT, (GLint *)&Attrib.viewport);
 	}
 
 	if ((mask & GPU_BLEND_BIT) != 0) {
-		Gwn_VertAttr.is_blend = glIsEnabled(GL_BLEND);
+		Attrib.is_blend = glIsEnabled(GL_BLEND);
 	}
 
 	BLI_assert(AttribStack.top < STATE_STACK_DEPTH);
@@ -2456,52 +2454,52 @@ void gpuPopAttrib(void)
 	BLI_assert(AttribStack.top > 0);
 	AttribStack.top--;
 
-	GLint mask = Gwn_VertAttr.mask;
+	GLint mask = Attrib.mask;
 
 	if ((mask & GPU_DEPTH_BUFFER_BIT) != 0) {
-		restore_mask(GL_DEPTH_TEST, Gwn_VertAttr.is_depth_test);
-		glDepthFunc(Gwn_VertAttr.depth_func);
-		glClearDepth(Gwn_VertAttr.depth_clear_value);
-		glDepthMask(Gwn_VertAttr.depth_write_mask);
+		restore_mask(GL_DEPTH_TEST, Attrib.is_depth_test);
+		glDepthFunc(Attrib.depth_func);
+		glClearDepth(Attrib.depth_clear_value);
+		glDepthMask(Attrib.depth_write_mask);
 	}
 
 	if ((mask & GPU_ENABLE_BIT) != 0) {
-		restore_mask(GL_BLEND, Gwn_VertAttr.is_blend);
+		restore_mask(GL_BLEND, Attrib.is_blend);
 
 		for (int i = 0; i < 6; i++) {
-			restore_mask(GL_CLIP_PLANE0 + i, Gwn_VertAttr.is_clip_plane[i]);
+			restore_mask(GL_CLIP_PLANE0 + i, Attrib.is_clip_plane[i]);
 		}
 
-		restore_mask(GL_CULL_FACE, Gwn_VertAttr.is_cull_face);
-		restore_mask(GL_DEPTH_TEST, Gwn_VertAttr.is_depth_test);
-		restore_mask(GL_DITHER, Gwn_VertAttr.is_dither);
-		restore_mask(GL_LINE_SMOOTH, Gwn_VertAttr.is_line_smooth);
-		restore_mask(GL_COLOR_LOGIC_OP, Gwn_VertAttr.is_color_logic_op);
-		restore_mask(GL_MULTISAMPLE, Gwn_VertAttr.is_multisample);
-		restore_mask(GL_POLYGON_OFFSET_LINE, Gwn_VertAttr.is_polygon_offset_line);
-		restore_mask(GL_POLYGON_OFFSET_FILL, Gwn_VertAttr.is_polygon_offset_fill);
-		restore_mask(GL_POLYGON_SMOOTH, Gwn_VertAttr.is_polygon_smooth);
-		restore_mask(GL_SAMPLE_ALPHA_TO_COVERAGE, Gwn_VertAttr.is_sample_alpha_to_coverage);
-		restore_mask(GL_SCISSOR_TEST, Gwn_VertAttr.is_scissor_test);
-		restore_mask(GL_STENCIL_TEST, Gwn_VertAttr.is_stencil_test);
+		restore_mask(GL_CULL_FACE, Attrib.is_cull_face);
+		restore_mask(GL_DEPTH_TEST, Attrib.is_depth_test);
+		restore_mask(GL_DITHER, Attrib.is_dither);
+		restore_mask(GL_LINE_SMOOTH, Attrib.is_line_smooth);
+		restore_mask(GL_COLOR_LOGIC_OP, Attrib.is_color_logic_op);
+		restore_mask(GL_MULTISAMPLE, Attrib.is_multisample);
+		restore_mask(GL_POLYGON_OFFSET_LINE, Attrib.is_polygon_offset_line);
+		restore_mask(GL_POLYGON_OFFSET_FILL, Attrib.is_polygon_offset_fill);
+		restore_mask(GL_POLYGON_SMOOTH, Attrib.is_polygon_smooth);
+		restore_mask(GL_SAMPLE_ALPHA_TO_COVERAGE, Attrib.is_sample_alpha_to_coverage);
+		restore_mask(GL_SCISSOR_TEST, Attrib.is_scissor_test);
+		restore_mask(GL_STENCIL_TEST, Attrib.is_stencil_test);
 	}
 
 	if ((mask & GPU_VIEWPORT_BIT) != 0) {
-		glViewport(Gwn_VertAttr.viewport[0], Gwn_VertAttr.viewport[1], Gwn_VertAttr.viewport[2], Gwn_VertAttr.viewport[3]);
-		glDepthRange(Gwn_VertAttr.near_far[0], Gwn_VertAttr.near_far[1]);
+		glViewport(Attrib.viewport[0], Attrib.viewport[1], Attrib.viewport[2], Attrib.viewport[3]);
+		glDepthRange(Attrib.near_far[0], Attrib.near_far[1]);
 	}
 
 	if ((mask & GPU_SCISSOR_BIT) != 0) {
-		restore_mask(GL_SCISSOR_TEST, Gwn_VertAttr.is_scissor_test);
-		glScissor(Gwn_VertAttr.scissor_box[0], Gwn_VertAttr.scissor_box[1], Gwn_VertAttr.scissor_box[2], Gwn_VertAttr.scissor_box[3]);
+		restore_mask(GL_SCISSOR_TEST, Attrib.is_scissor_test);
+		glScissor(Attrib.scissor_box[0], Attrib.scissor_box[1], Attrib.scissor_box[2], Attrib.scissor_box[3]);
 	}
 
 	if ((mask & GPU_BLEND_BIT) != 0) {
-		restore_mask(GL_BLEND, Gwn_VertAttr.is_blend);
+		restore_mask(GL_BLEND, Attrib.is_blend);
 	}
 }
 
-#undef Gwn_VertAttr
+#undef Attrib
 #undef AttribStack
 
 /** \} */
diff --git a/source/blender/gpu/intern/gpu_framebuffer.c b/source/blender/gpu/intern/gpu_framebuffer.c
index e83eeefe2c5589fe66c2e147a8ce88b4016e2d22..8d8fb50d9b9c1acfce4da82069daa4309249338d 100644
--- a/source/blender/gpu/intern/gpu_framebuffer.c
+++ b/source/blender/gpu/intern/gpu_framebuffer.c
@@ -28,7 +28,9 @@
 #include "MEM_guardedalloc.h"
 
 #include "BLI_blenlib.h"
+#include "BLI_threads.h"
 #include "BLI_utildefines.h"
+#include "BLI_math_base.h"
 
 #include "BKE_global.h"
 
@@ -40,20 +42,91 @@
 #include "GPU_shader.h"
 #include "GPU_texture.h"
 
-static struct GPUFrameBufferGlobal {
-	GLuint currentfb;
-} GG = {0};
+static ThreadLocal(GLuint) g_currentfb;
 
-/* Number of maximum output slots.
- * We support 5 outputs for now (usually we wouldn't need more to preserve fill rate) */
-#define GPU_FB_MAX_SLOTS 5
+typedef enum {
+	GPU_FB_DEPTH_ATTACHMENT = 0,
+	GPU_FB_DEPTH_STENCIL_ATTACHMENT,
+	GPU_FB_COLOR_ATTACHMENT0,
+	GPU_FB_COLOR_ATTACHMENT1,
+	GPU_FB_COLOR_ATTACHMENT2,
+	GPU_FB_COLOR_ATTACHMENT3,
+	GPU_FB_COLOR_ATTACHMENT4,
+	/* Number of maximum output slots.
+	 * We support 5 outputs for now (usually we wouldn't need more to preserve fill rate). */
+	/* Keep in mind that GL max is GL_MAX_DRAW_BUFFERS and is at least 8, corresponding to
+	 * the maximum number of COLOR attachments specified by glDrawBuffers. */
+	GPU_FB_MAX_ATTACHEMENT
+} GPUAttachmentType;
+
+#define GPU_FB_MAX_COLOR_ATTACHMENT (GPU_FB_MAX_ATTACHEMENT - GPU_FB_COLOR_ATTACHMENT0)
+
+#define GPU_FB_DIRTY_DRAWBUFFER (1 << 15)
+
+#define GPU_FB_ATTACHEMENT_IS_DIRTY(flag, type) ((flag & (1 << type)) != 0)
+#define GPU_FB_ATTACHEMENT_SET_DIRTY(flag, type) (flag |= (1 << type))
 
 struct GPUFrameBuffer {
 	GLuint object;
-	GPUTexture *colortex[GPU_FB_MAX_SLOTS];
-	GPUTexture *depthtex;
+	GPUAttachment attachments[GPU_FB_MAX_ATTACHEMENT];
+	uint16_t dirty_flag;
+	int width, height;
+	bool multisample;
+	/* TODO Check that we always use the right context when binding
+	 * (FBOs are not shared accross ogl contexts). */
+	// void *ctx;
 };
 
+static GLenum convert_attachment_type_to_gl(GPUAttachmentType type)
+{
+	static const GLenum table[] = {
+		[GPU_FB_DEPTH_ATTACHMENT] = GL_DEPTH_ATTACHMENT,
+		[GPU_FB_DEPTH_STENCIL_ATTACHMENT] = GL_DEPTH_STENCIL_ATTACHMENT,
+		[GPU_FB_COLOR_ATTACHMENT0] = GL_COLOR_ATTACHMENT0,
+		[GPU_FB_COLOR_ATTACHMENT1] = GL_COLOR_ATTACHMENT1,
+		[GPU_FB_COLOR_ATTACHMENT2] = GL_COLOR_ATTACHMENT2,
+		[GPU_FB_COLOR_ATTACHMENT3] = GL_COLOR_ATTACHMENT3,
+		[GPU_FB_COLOR_ATTACHMENT4] = GL_COLOR_ATTACHMENT4
+		};
+	return table[type];
+}
+
+static GPUAttachmentType attachment_type_from_tex(GPUTexture *tex, int slot)
+{
+	switch (GPU_texture_format(tex)) {
+		case GPU_DEPTH_COMPONENT32F:
+		case GPU_DEPTH_COMPONENT24:
+		case GPU_DEPTH_COMPONENT16:
+			return GPU_FB_DEPTH_ATTACHMENT;
+		case GPU_DEPTH24_STENCIL8:
+			return GPU_FB_DEPTH_STENCIL_ATTACHMENT;
+		default:
+			return GPU_FB_COLOR_ATTACHMENT0 + slot;
+	}
+}
+
+static GLenum convert_buffer_bits_to_gl(GPUFrameBufferBits bits)
+{
+	GLbitfield mask = 0;
+	mask |= (bits & GPU_DEPTH_BIT) ? GL_DEPTH_BUFFER_BIT : 0;
+	mask |= (bits & GPU_STENCIL_BIT) ? GL_STENCIL_BUFFER_BIT : 0;
+	mask |= (bits & GPU_COLOR_BIT) ? GL_COLOR_BUFFER_BIT : 0;
+	return mask;
+}
+
+static GPUTexture *framebuffer_get_depth_tex(GPUFrameBuffer *fb)
+{
+	if (fb->attachments[GPU_FB_DEPTH_ATTACHMENT].tex)
+		return fb->attachments[GPU_FB_DEPTH_ATTACHMENT].tex;
+	else
+		return fb->attachments[GPU_FB_DEPTH_STENCIL_ATTACHMENT].tex;;
+}
+
+static GPUTexture *framebuffer_get_color_tex(GPUFrameBuffer *fb, int slot)
+{
+	return fb->attachments[GPU_FB_COLOR_ATTACHMENT0 + slot].tex;
+}
+
 static void gpu_print_framebuffer_error(GLenum status, char err_out[256])
 {
 	const char *format = "GPUFrameBuffer: framebuffer status %s\n";
@@ -94,303 +167,268 @@ static void gpu_print_framebuffer_error(GLenum status, char err_out[256])
 
 GPUFrameBuffer *GPU_framebuffer_create(void)
 {
-	GPUFrameBuffer *fb;
+	/* We generate the FB object later at first use in order to
+	 * create the framebuffer in the right opengl context. */
+	return MEM_callocN(sizeof(GPUFrameBuffer), "GPUFrameBuffer");;
+}
 
-	fb = MEM_callocN(sizeof(GPUFrameBuffer), "GPUFrameBuffer");
+static void gpu_framebuffer_init(GPUFrameBuffer *fb)
+{
 	glGenFramebuffers(1, &fb->object);
-
-	if (!fb->object) {
-		fprintf(stderr, "GPUFFrameBuffer: framebuffer gen failed.\n");
-		GPU_framebuffer_free(fb);
-		return NULL;
-	}
-
-	/* make sure no read buffer is enabled, so completeness check will not fail. We set those at binding time */
-	glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
-	glReadBuffer(GL_NONE);
-	glDrawBuffer(GL_NONE);
-	glBindFramebuffer(GL_FRAMEBUFFER, 0);
-	
-	return fb;
 }
 
-bool GPU_framebuffer_texture_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int mip)
+void GPU_framebuffer_free(GPUFrameBuffer *fb)
 {
-	GLenum attachment;
-
-	if (slot >= GPU_FB_MAX_SLOTS) {
-		fprintf(stderr,
-		        "Attaching to index %d framebuffer slot unsupported. "
-		        "Use at most %d\n", slot, GPU_FB_MAX_SLOTS);
-		return false;
-	}
-
-	if ((G.debug & G_DEBUG)) {
-		if (GPU_texture_bound_number(tex) != -1) {
-			fprintf(stderr,
-			        "Feedback loop warning!: "
-			        "Attempting to attach texture to framebuffer while still bound to texture unit for drawing!\n");
+	for (GPUAttachmentType type = 0; type < GPU_FB_MAX_ATTACHEMENT; type++) {
+		if (fb->attachments[type].tex != NULL) {
+			GPU_framebuffer_texture_detach(fb, fb->attachments[type].tex);
 		}
 	}
 
-	glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
-	GG.currentfb = fb->object;
-
-	if (GPU_texture_stencil(tex) && GPU_texture_depth(tex))
-		attachment = GL_DEPTH_STENCIL_ATTACHMENT;
-	else if (GPU_texture_depth(tex))
-		attachment = GL_DEPTH_ATTACHMENT;
-	else
-		attachment = GL_COLOR_ATTACHMENT0 + slot;
-
-	glFramebufferTexture(GL_FRAMEBUFFER, attachment, GPU_texture_opengl_bindcode(tex), mip);
+	/* This restores the framebuffer if it was bound */
+	glDeleteFramebuffers(1, &fb->object);
 
-	if (GPU_texture_depth(tex))
-		fb->depthtex = tex;
-	else
-		fb->colortex[slot] = tex;
-
-	GPU_texture_framebuffer_set(tex, fb, slot);
+	if (g_currentfb == fb->object) {
+		g_currentfb = 0;
+	}
 
-	return true;
+	MEM_freeN(fb);
 }
 
-static bool gpu_framebuffer_texture_layer_attach_ex(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int layer, int mip, bool cubemap)
-{
-	GLenum attachment;
-	GLenum facetarget;
+/* ---------- Attach ----------- */
 
-	if (slot >= GPU_FB_MAX_SLOTS) {
+static void gpu_framebuffer_texture_attach_ex(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int layer, int mip)
+{
+	if (slot >= GPU_FB_MAX_COLOR_ATTACHMENT) {
 		fprintf(stderr,
 		        "Attaching to index %d framebuffer slot unsupported. "
-		        "Use at most %d\n", slot, GPU_FB_MAX_SLOTS);
-		return false;
-	}
-
-	if ((G.debug & G_DEBUG)) {
-		if (GPU_texture_bound_number(tex) != -1) {
-			fprintf(stderr,
-			        "Feedback loop warning!: "
-			        "Attempting to attach texture to framebuffer while still bound to texture unit for drawing!\n");
-		}
+		        "Use at most %d\n", slot, GPU_FB_MAX_COLOR_ATTACHMENT);
+		return;
 	}
 
-	glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
-	GG.currentfb = fb->object;
-
-	if (GPU_texture_stencil(tex) && GPU_texture_depth(tex))
-		attachment = GL_DEPTH_STENCIL_ATTACHMENT;
-	else if (GPU_texture_depth(tex))
-		attachment = GL_DEPTH_ATTACHMENT;
-	else
-		attachment = GL_COLOR_ATTACHMENT0 + slot;
+	GPUAttachmentType type = attachment_type_from_tex(tex, slot);
+	GPUAttachment *attachment = &fb->attachments[type];
 
-	if (cubemap) {
-		facetarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + layer;
-		glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, facetarget, GPU_texture_opengl_bindcode(tex), mip);
+	if ((attachment->tex == tex) &&
+		(attachment->mip == mip) &&
+		(attachment->layer == layer))
+	{
+		return; /* Exact same texture already bound here. */
 	}
-	else {
-		glFramebufferTextureLayer(GL_FRAMEBUFFER, attachment, GPU_texture_opengl_bindcode(tex), mip, layer);
+	else if (attachment->tex != NULL) {
+		GPU_framebuffer_texture_detach(fb, attachment->tex);
 	}
 
-	if (GPU_texture_depth(tex))
-		fb->depthtex = tex;
-	else
-		fb->colortex[slot] = tex;
-
-	GPU_texture_framebuffer_set(tex, fb, slot);
+	if (attachment->tex == NULL) {
+		GPU_texture_attach_framebuffer(tex, fb, type);
+	}
 
-	return true;
+	attachment->tex = tex;
+	attachment->mip = mip;
+	attachment->layer = layer;
+	GPU_FB_ATTACHEMENT_SET_DIRTY(fb->dirty_flag, type);
 }
 
-bool GPU_framebuffer_texture_layer_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int layer, int mip)
+void GPU_framebuffer_texture_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int mip)
 {
-	return gpu_framebuffer_texture_layer_attach_ex(fb, tex, slot, layer, mip, false);
+	gpu_framebuffer_texture_attach_ex(fb, tex, slot, -1, mip);
 }
 
-bool GPU_framebuffer_texture_cubeface_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int face, int mip)
+void GPU_framebuffer_texture_layer_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int layer, int mip)
 {
-	BLI_assert(GPU_texture_target(tex) == GL_TEXTURE_CUBE_MAP);
-	return gpu_framebuffer_texture_layer_attach_ex(fb, tex, slot, face, mip, true);
+	/* NOTE: We could support 1D ARRAY texture. */
+	BLI_assert(GPU_texture_target(tex) == GL_TEXTURE_2D_ARRAY);
+	gpu_framebuffer_texture_attach_ex(fb, tex, slot, layer, mip);
 }
 
-void GPU_framebuffer_texture_detach(GPUTexture *tex)
+void GPU_framebuffer_texture_cubeface_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int face, int mip)
 {
-	GLenum attachment;
-	GPUFrameBuffer *fb = GPU_texture_framebuffer(tex);
-	int fb_attachment = GPU_texture_framebuffer_attachment(tex);
+	BLI_assert(GPU_texture_cube(tex));
+	gpu_framebuffer_texture_attach_ex(fb, tex, slot, face, mip);
+}
 
-	if (!fb)
-		return;
+/* ---------- Detach ----------- */
 
-	if (GG.currentfb != fb->object) {
-		glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
-		GG.currentfb = fb->object;
-	}
+void GPU_framebuffer_texture_detach_slot(GPUFrameBuffer *fb, GPUTexture *tex, int type)
+{
+	GPUAttachment *attachment = &fb->attachments[type];
 
-	if (GPU_texture_stencil(tex) && GPU_texture_depth(tex)) {
-		fb->depthtex = NULL;
-		attachment = GL_DEPTH_STENCIL_ATTACHMENT;
-	}
-	else if (GPU_texture_depth(tex)) {
-		fb->depthtex = NULL;
-		attachment = GL_DEPTH_ATTACHMENT;
-	}
-	else {
-		BLI_assert(fb->colortex[fb_attachment] == tex);
-		fb->colortex[fb_attachment] = NULL;
-		attachment = GL_COLOR_ATTACHMENT0 + fb_attachment;
+	if (attachment->tex != tex) {
+		fprintf(stderr,
+		        "Warning, attempting to detach Texture %p from framebuffer %p "
+		        "but texture is not attached.\n", tex, fb);
+		return;
 	}
 
-	glFramebufferTexture(GL_FRAMEBUFFER, attachment, 0, 0);
+	attachment->tex = NULL;
+	GPU_FB_ATTACHEMENT_SET_DIRTY(fb->dirty_flag, type);
+}
 
-	GPU_texture_framebuffer_set(tex, NULL, -1);
+void GPU_framebuffer_texture_detach(GPUFrameBuffer *fb, GPUTexture *tex)
+{
+	GPUAttachmentType type = GPU_texture_detach_framebuffer(tex, fb);
+	GPU_framebuffer_texture_detach_slot(fb, tex, type);
 }
 
-void GPU_texture_bind_as_framebuffer(GPUTexture *tex)
+/* ---------- Config (Attach & Detach) ----------- */
+
+/**
+ * First GPUAttachment in *config is always the depth/depth_stencil buffer.
+ * Following GPUAttachments are color buffers.
+ * Setting GPUAttachment.mip to -1 will leave the texture in this slot.
+ * Setting GPUAttachment.tex to NULL will detach the texture in this slot.
+ **/
+void GPU_framebuffer_config_array(GPUFrameBuffer *fb, const GPUAttachment *config, int config_ct)
 {
-	GPUFrameBuffer *fb = GPU_texture_framebuffer(tex);
-	int fb_attachment = GPU_texture_framebuffer_attachment(tex);
+	if (config[0].tex) {
+		BLI_assert(GPU_texture_depth(config[0].tex));
+		gpu_framebuffer_texture_attach_ex(fb, config[0].tex, 0, config[0].layer, config[0].mip);
+	}
+	else if (config[0].mip == -1) {
+		/* Leave texture attached */
+	}
+	else if (fb->attachments[GPU_FB_DEPTH_ATTACHMENT].tex != NULL) {
+		GPU_framebuffer_texture_detach(fb, fb->attachments[GPU_FB_DEPTH_ATTACHMENT].tex);
+	}
+	else if (fb->attachments[GPU_FB_DEPTH_STENCIL_ATTACHMENT].tex != NULL) {
+		GPU_framebuffer_texture_detach(fb, fb->attachments[GPU_FB_DEPTH_STENCIL_ATTACHMENT].tex);
+	}
 
-	if (!fb) {
-		fprintf(stderr, "Error, texture not bound to framebuffer!\n");
-		return;
+	int slot = 0;
+	for (int i = 1; i < config_ct; ++i, ++slot) {
+		if (config[i].tex != NULL) {
+			BLI_assert(GPU_texture_depth(config[i].tex) == false);
+			gpu_framebuffer_texture_attach_ex(fb, config[i].tex, slot, config[i].layer, config[i].mip);
+		}
+		else if (config[i].mip != -1) {
+			GPUTexture *tex = framebuffer_get_color_tex(fb, slot);
+			if (tex != NULL) {
+				GPU_framebuffer_texture_detach(fb, tex);
+			}
+		}
 	}
+}
 
-	/* push attributes */
-	gpuPushAttrib(GPU_ENABLE_BIT | GPU_VIEWPORT_BIT);
-	glDisable(GL_SCISSOR_TEST);
+/* ---------- Bind / Restore ----------- */
 
-	/* bind framebuffer */
-	glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
+static void gpu_framebuffer_attachment_attach(GPUAttachment *attach, GPUAttachmentType attach_type)
+{
+	int tex_bind = GPU_texture_opengl_bindcode(attach->tex);
+	GLenum gl_attachment = convert_attachment_type_to_gl(attach_type);
 
-	if (GPU_texture_depth(tex)) {
-		glDrawBuffer(GL_NONE);
-		glReadBuffer(GL_NONE);
+	if (attach->layer > -1) {
+		if (GPU_texture_cube(attach->tex)) {
+			glFramebufferTexture2D(GL_FRAMEBUFFER, gl_attachment, GL_TEXTURE_CUBE_MAP_POSITIVE_X + attach->layer,
+			                       tex_bind, attach->mip);
+		}
+		else {
+			glFramebufferTextureLayer(GL_FRAMEBUFFER, gl_attachment, tex_bind, attach->mip, attach->layer);
+		}
 	}
 	else {
-		/* last bound prevails here, better allow explicit control here too */
-		glDrawBuffer(GL_COLOR_ATTACHMENT0 + fb_attachment);
-		glReadBuffer(GL_COLOR_ATTACHMENT0 + fb_attachment);
-	}
-	
-	if (GPU_texture_target(tex) == GL_TEXTURE_2D_MULTISAMPLE) {
-		glEnable(GL_MULTISAMPLE);
+		glFramebufferTexture(GL_FRAMEBUFFER, gl_attachment, tex_bind, attach->mip);
 	}
+}
 
-	/* set default viewport */
-	glViewport(0, 0, GPU_texture_width(tex), GPU_texture_height(tex));
-	GG.currentfb = fb->object;
+static void gpu_framebuffer_attachment_detach(GPUAttachment *UNUSED(attachment), GPUAttachmentType attach_type)
+{
+	GLenum gl_attachment = convert_attachment_type_to_gl(attach_type);
+	glFramebufferTexture(GL_FRAMEBUFFER, gl_attachment, 0, 0);
 }
 
-void GPU_framebuffer_slots_bind(GPUFrameBuffer *fb, int slot)
+static void gpu_framebuffer_update_attachments(GPUFrameBuffer *fb)
 {
-	int numslots = 0, i;
-	GLenum attachments[GPU_FB_MAX_SLOTS];
-	
-	if (!fb->colortex[slot]) {
-		fprintf(stderr, "Error, framebuffer slot empty!\n");
-		return;
-	}
-	
-	for (i = 0; i < GPU_FB_MAX_SLOTS; i++) {
-		if (fb->colortex[i]) {
-			attachments[numslots] = GL_COLOR_ATTACHMENT0 + i;
+	GLenum gl_attachments[GPU_FB_MAX_COLOR_ATTACHMENT];
+	int numslots = 0;
+
+	BLI_assert(g_currentfb == fb->object);
+
+	/* Update attachments */
+	for (GPUAttachmentType type = 0; type < GPU_FB_MAX_ATTACHEMENT; ++type) {
+
+		if (type >= GPU_FB_COLOR_ATTACHMENT0) {
+			if (fb->attachments[type].tex) {
+				gl_attachments[numslots] = convert_attachment_type_to_gl(type);
+			}
+			else {
+				gl_attachments[numslots] = GL_NONE;
+			}
 			numslots++;
 		}
-	}
-	
-	/* push attributes */
-	gpuPushAttrib(GPU_ENABLE_BIT | GPU_VIEWPORT_BIT);
-	glDisable(GL_SCISSOR_TEST);
 
-	/* bind framebuffer */
-	glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
+		if (GPU_FB_ATTACHEMENT_IS_DIRTY(fb->dirty_flag, type) == false) {
+			continue;
+		}
+		else if (fb->attachments[type].tex != NULL) {
+			gpu_framebuffer_attachment_attach(&fb->attachments[type], type);
 
-	/* last bound prevails here, better allow explicit control here too */
-	glDrawBuffers(numslots, attachments);
-	glReadBuffer(GL_COLOR_ATTACHMENT0 + slot);
+			fb->multisample = (GPU_texture_samples(fb->attachments[type].tex) > 0);
+			fb->width = GPU_texture_width(fb->attachments[type].tex);
+			fb->height = GPU_texture_height(fb->attachments[type].tex);
+		}
+		else {
+			gpu_framebuffer_attachment_detach(&fb->attachments[type], type);
+		}
+	}
+	fb->dirty_flag = 0;
 
-	/* set default viewport */
-	glViewport(0, 0, GPU_texture_width(fb->colortex[slot]), GPU_texture_height(fb->colortex[slot]));
-	GG.currentfb = fb->object;
+	/* Update draw buffers (color targets)
+	 * This state is saved in the FBO */
+	if (numslots)
+		glDrawBuffers(numslots, gl_attachments);
+	else
+		glDrawBuffer(GL_NONE);
 }
 
 void GPU_framebuffer_bind(GPUFrameBuffer *fb)
 {
-	int numslots = 0, i;
-	GLenum attachments[GPU_FB_MAX_SLOTS];
-	GLenum readattachement = 0;
-	GPUTexture *tex;
+	if (fb->object == 0)
+		gpu_framebuffer_init(fb);
 
-	for (i = 0; i < GPU_FB_MAX_SLOTS; i++) {
-		if (fb->colortex[i]) {
-			attachments[numslots] = GL_COLOR_ATTACHMENT0 + i;
-			tex = fb->colortex[i];
-
-			if (!readattachement)
-				readattachement = GL_COLOR_ATTACHMENT0 + i;
+	if (g_currentfb != fb->object)
+		glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
 
-			numslots++;
-		}
-	}
+	g_currentfb = fb->object;
 
-	/* bind framebuffer */
-	glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
+	if (fb->dirty_flag != 0)
+		gpu_framebuffer_update_attachments(fb);
 
-	if (numslots == 0) {
-		glDrawBuffer(GL_NONE);
-		glReadBuffer(GL_NONE);
-		tex = fb->depthtex;
-	}
-	else {
-		/* last bound prevails here, better allow explicit control here too */
-		glDrawBuffers(numslots, attachments);
-		glReadBuffer(readattachement);
+	/* TODO manually check for errors? */
+#if 0
+	char err_out[256];
+	if (!GPU_framebuffer_check_valid(fb, err_out)) {
+		printf("Invalid %s\n", err_out);
 	}
+#endif
 
-	if (GPU_texture_target(tex) == GL_TEXTURE_2D_MULTISAMPLE) {
+	if (fb->multisample)
 		glEnable(GL_MULTISAMPLE);
-	}
 
-	glViewport(0, 0, GPU_texture_width(tex), GPU_texture_height(tex));
-	GG.currentfb = fb->object;
+	glViewport(0, 0, fb->width, fb->height);
 }
 
-void GPU_framebuffer_texture_unbind(GPUFrameBuffer *UNUSED(fb), GPUTexture *UNUSED(tex))
+void GPU_framebuffer_restore(void)
 {
-	/* Restore attributes. */
-	gpuPopAttrib();
+	if (g_currentfb != 0) {
+		glBindFramebuffer(GL_FRAMEBUFFER, 0);
+		g_currentfb = 0;
+	}
 }
 
-void GPU_framebuffer_bind_no_save(GPUFrameBuffer *fb, int slot)
+bool GPU_framebuffer_bound(GPUFrameBuffer *fb)
 {
-	glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
-	/* last bound prevails here, better allow explicit control here too */
-	glDrawBuffer(GL_COLOR_ATTACHMENT0 + slot);
-	glReadBuffer(GL_COLOR_ATTACHMENT0 + slot);
-
-	/* push matrices and set default viewport and matrix */
-	glViewport(0, 0, GPU_texture_width(fb->colortex[slot]), GPU_texture_height(fb->colortex[slot]));
-	GG.currentfb = fb->object;
+	return (fb->object == g_currentfb) && (fb->object != 0);
 }
 
-bool GPU_framebuffer_bound(GPUFrameBuffer *fb)
+unsigned int GPU_framebuffer_current_get(void)
 {
-	return fb->object == GG.currentfb;
+	return g_currentfb;
 }
 
 bool GPU_framebuffer_check_valid(GPUFrameBuffer *fb, char err_out[256])
 {
-	glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
-	GG.currentfb = fb->object;
-
-	/* On macOS glDrawBuffer must be set when checking completeness,
-	 * otherwise it will return GL_FRAMEBUFFER_UNSUPPORTED when only a
-	 * color buffer without depth is used. */
-	if (fb->colortex[0]) {
-		glDrawBuffer(GL_COLOR_ATTACHMENT0);
-	}
+	if (!GPU_framebuffer_bound(fb))
+		GPU_framebuffer_bind(fb);
 
 	GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
 
@@ -403,172 +441,140 @@ bool GPU_framebuffer_check_valid(GPUFrameBuffer *fb, char err_out[256])
 	return true;
 }
 
-void GPU_framebuffer_free(GPUFrameBuffer *fb)
-{
-	int i;
-	if (fb->depthtex)
-		GPU_framebuffer_texture_detach(fb->depthtex);
+/* ---------- Framebuffer Operations ----------- */
 
-	for (i = 0; i < GPU_FB_MAX_SLOTS; i++) {
-		if (fb->colortex[i]) {
-			GPU_framebuffer_texture_detach(fb->colortex[i]);
-		}
-	}
+#define CHECK_FRAMEBUFFER_IS_BOUND(_fb) \
+	BLI_assert(GPU_framebuffer_bound(_fb)); \
+	UNUSED_VARS_NDEBUG(_fb);
 
-	if (fb->object) {
-		glDeleteFramebuffers(1, &fb->object);
-
-		if (GG.currentfb == fb->object) {
-			glBindFramebuffer(GL_FRAMEBUFFER, 0);
-			GG.currentfb = 0;
-		}
-	}
+/* Needs to be done after binding. */
+void GPU_framebuffer_viewport_set(GPUFrameBuffer *fb, int x, int y, int w, int h)
+{
+	CHECK_FRAMEBUFFER_IS_BOUND(fb);
 
-	MEM_freeN(fb);
+	glViewport(x, y, w, h);
 }
 
-void GPU_framebuffer_restore(void)
+void GPU_framebuffer_clear(
+        GPUFrameBuffer *fb, GPUFrameBufferBits buffers,
+        const float clear_col[4], float clear_depth, unsigned int clear_stencil)
 {
-	if (GG.currentfb != 0) {
-		glBindFramebuffer(GL_FRAMEBUFFER, 0);
-		GG.currentfb = 0;
+	CHECK_FRAMEBUFFER_IS_BOUND(fb);
+
+	if (buffers & GPU_COLOR_BIT) {
+		glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+		glClearColor(clear_col[0], clear_col[1], clear_col[2], clear_col[3]);
+	}
+	if (buffers & GPU_DEPTH_BIT) {
+		glDepthMask(GL_TRUE);
+		glClearDepth(clear_depth);
 	}
+	if (buffers & GPU_STENCIL_BIT) {
+		glStencilMask(clear_stencil);
+	}
+
+	GLbitfield mask = convert_buffer_bits_to_gl(buffers);
+	glClear(mask);
 }
 
-void GPU_framebuffer_blur(
-        GPUFrameBuffer *fb, GPUTexture *tex,
-        GPUFrameBuffer *blurfb, GPUTexture *blurtex)
+void GPU_framebuffer_read_depth(GPUFrameBuffer *fb, int x, int y, int w, int h, float *data)
 {
-	const float fullscreencos[4][2] = {{-1.0f, -1.0f}, {1.0f, -1.0f}, {-1.0f, 1.0f}, {1.0f, 1.0f}};
-	const float fullscreenuvs[4][2] = {{0.0f, 0.0f}, {1.0f, 0.0f}, {0.0f, 1.0f}, {1.0f, 1.0f}};
-
-	static Gwn_VertFormat format = {0};
-	static Gwn_VertBuf vbo = {{0}};
-	static Gwn_Batch batch = {{0}};
-
-	const float scaleh[2] = {1.0f / GPU_texture_width(blurtex), 0.0f};
-	const float scalev[2] = {0.0f, 1.0f / GPU_texture_height(tex)};
-
-	GPUShader *blur_shader = GPU_shader_get_builtin_shader(GPU_SHADER_SEP_GAUSSIAN_BLUR);
-
-	if (!blur_shader)
-		return;
-
-	/* Preparing to draw quad */
-	if (format.attrib_ct == 0) {
-		unsigned int i = 0;
-		/* Vertex format */
-		unsigned int pos = GWN_vertformat_attr_add(&format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
-		unsigned int uvs = GWN_vertformat_attr_add(&format, "uvs", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
-
-		/* Vertices */
-		GWN_vertbuf_init_with_format(&vbo, &format);
-		GWN_vertbuf_data_alloc(&vbo, 36);
+	CHECK_FRAMEBUFFER_IS_BOUND(fb);
 
-		for (int j = 0; j < 3; ++j) {
-			GWN_vertbuf_attr_set(&vbo, uvs, i, fullscreenuvs[j]);
-			GWN_vertbuf_attr_set(&vbo, pos, i++, fullscreencos[j]);
-		}
-		for (int j = 1; j < 4; ++j) {
-			GWN_vertbuf_attr_set(&vbo, uvs, i, fullscreenuvs[j]);
-			GWN_vertbuf_attr_set(&vbo, pos, i++, fullscreencos[j]);
-		}
+	GLenum type = GL_DEPTH_COMPONENT;
+	glReadBuffer(GL_COLOR_ATTACHMENT0); /* This is OK! */
+	glReadPixels(x, y, w, h, type, GL_FLOAT, data);
+}
 
-		GWN_batch_init(&batch, GL_TRIANGLES, &vbo, NULL);
+void GPU_framebuffer_read_color(
+        GPUFrameBuffer *fb, int x, int y, int w, int h, int channels, int slot, float *data)
+{
+	CHECK_FRAMEBUFFER_IS_BOUND(fb);
+
+	GLenum type;
+	switch (channels) {
+		case 1: type = GL_RED; break;
+		case 2: type = GL_RG; break;
+		case 3: type = GL_RGB; break;
+		case 4: type = GL_RGBA;	break;
+		default:
+			BLI_assert(false && "wrong number of read channels");
+			return;
 	}
-		
-	glDisable(GL_DEPTH_TEST);
-	
-	/* Blurring horizontally */
-	/* We do the bind ourselves rather than using GPU_framebuffer_texture_bind() to avoid
-	 * pushing unnecessary matrices onto the OpenGL stack. */
-	glBindFramebuffer(GL_FRAMEBUFFER, blurfb->object);
-	glDrawBuffer(GL_COLOR_ATTACHMENT0);
-	
-	/* avoid warnings from texture binding */
-	GG.currentfb = blurfb->object;
-
-	glViewport(0, 0, GPU_texture_width(blurtex), GPU_texture_height(blurtex));
+	glReadBuffer(GL_COLOR_ATTACHMENT0 + slot);
+	glReadPixels(x, y, w, h, type, GL_FLOAT, data);
+}
 
-	GPU_texture_bind(tex, 0);
+/* read_slot and write_slot are only used for color buffers. */
+void GPU_framebuffer_blit(
+        GPUFrameBuffer *fb_read, int read_slot,
+        GPUFrameBuffer *fb_write, int write_slot,
+        GPUFrameBufferBits blit_buffers)
+{
+	BLI_assert(blit_buffers != 0);
 
-	GWN_batch_program_set_builtin(&batch, GPU_SHADER_SEP_GAUSSIAN_BLUR);
-	GWN_batch_uniform_2f(&batch, "ScaleU", scaleh[0], scaleh[1]);
-	GWN_batch_uniform_1i(&batch, "textureSource", GL_TEXTURE0);
-	GWN_batch_draw(&batch);
+	GLuint prev_fb = g_currentfb;
 
-	/* Blurring vertically */
-	glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
-	glDrawBuffer(GL_COLOR_ATTACHMENT0);
-	
-	GG.currentfb = fb->object;
-	
-	glViewport(0, 0, GPU_texture_width(tex), GPU_texture_height(tex));
+	/* Framebuffers must be up to date. This simplify this function. */
+	if (fb_read->dirty_flag != 0 || fb_read->object == 0) {
+		GPU_framebuffer_bind(fb_read);
+	}
+	if (fb_write->dirty_flag != 0 || fb_write->object == 0) {
+		GPU_framebuffer_bind(fb_write);
+	}
 
-	GPU_texture_bind(blurtex, 0);
+	const bool do_color = (blit_buffers & GPU_COLOR_BIT);
+	const bool do_depth = (blit_buffers & GPU_DEPTH_BIT);
+	const bool do_stencil = (blit_buffers & GPU_STENCIL_BIT);
 
-	/* Hack to make the following uniform stick */
-	GWN_batch_program_set_builtin(&batch, GPU_SHADER_SEP_GAUSSIAN_BLUR);
-	GWN_batch_uniform_2f(&batch, "ScaleU", scalev[0], scalev[1]);
-	GWN_batch_uniform_1i(&batch, "textureSource", GL_TEXTURE0);
-	GWN_batch_draw(&batch);
-}
+	GPUTexture *read_tex = (do_depth || do_stencil)
+	                       ? framebuffer_get_depth_tex(fb_read)
+	                       : framebuffer_get_color_tex(fb_read, read_slot);
+	GPUTexture *write_tex = (do_depth || do_stencil)
+	                        ? framebuffer_get_depth_tex(fb_write)
+	                        : framebuffer_get_color_tex(fb_write, read_slot);
 
-void GPU_framebuffer_blit(
-        GPUFrameBuffer *fb_read, int read_slot, GPUFrameBuffer *fb_write,
-        int write_slot, bool use_depth, bool use_stencil)
-{
-	GPUTexture *read_tex = (use_depth || use_stencil) ? fb_read->depthtex : fb_read->colortex[read_slot];
-	GPUTexture *write_tex = (use_depth || use_stencil) ? fb_write->depthtex : fb_write->colortex[write_slot];
-	int read_attach = (use_depth) ? GL_DEPTH_ATTACHMENT :
-	                  (use_stencil) ? GL_DEPTH_STENCIL_ATTACHMENT :
-	                  GL_COLOR_ATTACHMENT0 + GPU_texture_framebuffer_attachment(read_tex);
-	int write_attach = (use_depth) ? GL_DEPTH_ATTACHMENT :
-	                   (use_stencil) ? GL_DEPTH_STENCIL_ATTACHMENT :
-	                   GL_COLOR_ATTACHMENT0 + GPU_texture_framebuffer_attachment(write_tex);
-	int read_bind = GPU_texture_opengl_bindcode(read_tex);
-	int write_bind = GPU_texture_opengl_bindcode(write_tex);
-	const int read_w = GPU_texture_width(read_tex);
-	const int read_h = GPU_texture_height(read_tex);
-	const int write_w = GPU_texture_width(write_tex);
-	const int write_h = GPU_texture_height(write_tex);
-
-
-	/* Never both! */
-	BLI_assert(!(use_depth && use_stencil));
-
-	if (use_depth) {
+	if (do_depth) {
 		BLI_assert(GPU_texture_depth(read_tex) && GPU_texture_depth(write_tex));
 		BLI_assert(GPU_texture_format(read_tex) == GPU_texture_format(write_tex));
 	}
-	else if (use_stencil) {
+	if (do_stencil) {
 		BLI_assert(GPU_texture_stencil(read_tex) && GPU_texture_stencil(write_tex));
 		BLI_assert(GPU_texture_format(read_tex) == GPU_texture_format(write_tex));
 	}
+	if (GPU_texture_samples(write_tex) != 0 ||
+		GPU_texture_samples(read_tex) != 0)
+	{
+		/* Can only blit multisample textures to another texture of the same size. */
+		BLI_assert((fb_read->width == fb_write->width) &&
+		           (fb_read->height == fb_write->height));
+	}
 
-	/* read from multi-sample buffer */
 	glBindFramebuffer(GL_READ_FRAMEBUFFER, fb_read->object);
-	glFramebufferTexture2D(
-	        GL_READ_FRAMEBUFFER, read_attach,
-	        GPU_texture_target(read_tex), read_bind, 0);
-	BLI_assert(glCheckFramebufferStatus(GL_READ_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
-
-	/* write into new single-sample buffer */
 	glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb_write->object);
-	glFramebufferTexture2D(
-	        GL_DRAW_FRAMEBUFFER, write_attach,
-	        GPU_texture_target(write_tex), write_bind, 0);
-	BLI_assert(glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
 
-	glDrawBuffer((use_depth || use_stencil) ? GL_COLOR_ATTACHMENT0 : read_attach);
-	glBlitFramebuffer(0, 0, read_w, read_h, 0, 0, write_w, write_h,
-	                  (use_depth) ? GL_DEPTH_BUFFER_BIT :
-	                  (use_stencil) ? GL_STENCIL_BUFFER_BIT :
-	                  GL_COLOR_BUFFER_BIT, GL_NEAREST);
+	if (do_color) {
+		glReadBuffer(GL_COLOR_ATTACHMENT0 + read_slot);
+		glDrawBuffer(GL_COLOR_ATTACHMENT0 + write_slot);
+		/* XXX we messed with the glDrawBuffer, this will reset the
+		 * glDrawBuffers the next time we bind fb_write. */
+		fb_write->dirty_flag = GPU_FB_DIRTY_DRAWBUFFER;
+	}
+
+	GLbitfield mask = convert_buffer_bits_to_gl(blit_buffers);
+
+	glBlitFramebuffer(0, 0, fb_read->width, fb_read->height,
+	                  0, 0, fb_write->width, fb_write->height,
+	                  mask, GL_NEAREST);
 
 	/* Restore previous framebuffer */
-	glBindFramebuffer(GL_FRAMEBUFFER, GG.currentfb);
-	glDrawBuffer(GL_COLOR_ATTACHMENT0);
+	if (fb_write->object == prev_fb) {
+		GPU_framebuffer_bind(fb_write); /* To update drawbuffers */
+	}
+	else {
+		glBindFramebuffer(GL_FRAMEBUFFER, prev_fb);
+		g_currentfb = prev_fb;
+	}
 }
 
 /**
@@ -576,68 +582,65 @@ void GPU_framebuffer_blit(
  * This function only takes care of the correct texture handling. It execute the callback for each texture level.
  **/
 void GPU_framebuffer_recursive_downsample(
-        GPUFrameBuffer *fb, GPUTexture *tex, int num_iter, void (*callback)(void *userData, int level), void *userData)
+        GPUFrameBuffer *fb, int max_lvl,
+        void (*callback)(void *userData, int level), void *userData)
 {
-	int i;
-	int current_dim[2] = {GPU_texture_width(tex), GPU_texture_height(tex)};
-	GLenum attachment;
-
-	/* Manually setup framebuffer to not use GPU_texture_framebuffer_set() */
-	glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
-	GG.currentfb = fb->object;
-
-	if (GPU_texture_stencil(tex) && GPU_texture_depth(tex))
-		attachment = GL_DEPTH_STENCIL_ATTACHMENT;
-	else if (GPU_texture_depth(tex))
-		attachment = GL_DEPTH_ATTACHMENT;
-	else
-		attachment = GL_COLOR_ATTACHMENT0;
-
-	/* last bound prevails here, better allow explicit control here too */
-	if (GPU_texture_depth(tex)) {
-		glDrawBuffer(GL_NONE);
-		glReadBuffer(GL_NONE);
-	}
-	else {
-		glDrawBuffer(GL_COLOR_ATTACHMENT0);
-		glReadBuffer(GL_COLOR_ATTACHMENT0);
+	/* Framebuffer must be up to date and bound. This simplify this function. */
+	if (g_currentfb != fb->object || fb->dirty_flag != 0 || fb->object == 0) {
+		GPU_framebuffer_bind(fb);
 	}
+	/* HACK: We make the framebuffer appear not bound in order to
+	 * not trigger any error in GPU_texture_bind().  */
+	GLuint prev_fb = g_currentfb;
+	g_currentfb = 0;
 
-	for (i = 1; i < num_iter + 1; i++) {
-
+	int i;
+	int current_dim[2] = {fb->width, fb->height};
+	for (i = 1; i < max_lvl + 1; i++) {
 		/* calculate next viewport size */
-		current_dim[0] /= 2;
-		current_dim[1] /= 2;
-
-		if (current_dim[0] <= 2 && current_dim[1] <= 2) {
-			/* Cannot reduce further. */
-			break;
+		current_dim[0] = max_ii(current_dim[0] / 2, 1);
+		current_dim[1] = max_ii(current_dim[1] / 2, 1);
+
+		for (GPUAttachmentType type = 0; type < GPU_FB_MAX_ATTACHEMENT; ++type) {
+			if (fb->attachments[type].tex != NULL) {
+				/* bind next level for rendering but first restrict fetches only to previous level */
+				GPUTexture *tex = fb->attachments[type].tex;
+				GPU_texture_bind(tex, 0);
+				glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_BASE_LEVEL, i - 1);
+				glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_MAX_LEVEL, i - 1);
+				GPU_texture_unbind(tex);
+				/* copy attachment and replace miplevel. */
+				GPUAttachment attachment = fb->attachments[type];
+				attachment.mip = i;
+				gpu_framebuffer_attachment_attach(&attachment, type);
+			}
 		}
 
-		/* ensure that the viewport size is always at least 1x1 */
-		CLAMP_MIN(current_dim[0], 1);
-		CLAMP_MIN(current_dim[1], 1);
+		BLI_assert(GL_FRAMEBUFFER_COMPLETE == glCheckFramebufferStatus(GL_FRAMEBUFFER));
 
 		glViewport(0, 0, current_dim[0], current_dim[1]);
-
-		/* bind next level for rendering but first restrict fetches only to previous level */
-		GPU_texture_bind(tex, 0);
-		glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_BASE_LEVEL, i - 1);
-		glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_MAX_LEVEL, i - 1);
-		GPU_texture_unbind(tex);
-
-		glFramebufferTexture(GL_FRAMEBUFFER, attachment, GPU_texture_opengl_bindcode(tex), i);
-
 		callback(userData, i);
+
+		if (current_dim[0] == 1 && current_dim[1] == 1)
+			break;
 	}
 
-	glFramebufferTexture(GL_FRAMEBUFFER, attachment, 0, 0);
+	for (GPUAttachmentType type = 0; type < GPU_FB_MAX_ATTACHEMENT; ++type) {
+		if (fb->attachments[type].tex != NULL) {
+			/* reset mipmap level range */
+			GPUTexture *tex = fb->attachments[type].tex;
+			GPU_texture_bind(tex, 0);
+			glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_BASE_LEVEL, 0);
+			glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_MAX_LEVEL, i - 1);
+			GPU_texture_unbind(tex);
+			/* Reattach original level */
+			/* NOTE: This is not necessary but this makes the FBO config
+			 *       remain in sync with the GPUFrameBuffer config. */
+			gpu_framebuffer_attachment_attach(&fb->attachments[type], type);
+		}
+	}
 
-	/* reset mipmap level range for the depth image */
-	GPU_texture_bind(tex, 0);
-	glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_BASE_LEVEL, 0);
-	glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_MAX_LEVEL, i - 1);
-	GPU_texture_unbind(tex);
+	g_currentfb = prev_fb;
 }
 
 /* GPUOffScreen */
@@ -654,51 +657,23 @@ GPUOffScreen *GPU_offscreen_create(int width, int height, int samples, bool dept
 
 	ofs = MEM_callocN(sizeof(GPUOffScreen), "GPUOffScreen");
 
-	ofs->fb = GPU_framebuffer_create();
-	if (!ofs->fb) {
-		GPU_offscreen_free(ofs);
-		return NULL;
-	}
-
-	if (samples) {
-		if (!GLEW_ARB_texture_multisample ||
-		    /* This is required when blitting from a multi-sampled buffers,
-		     * even though we're not scaling. */
-		    !GLEW_EXT_framebuffer_multisample_blit_scaled)
-		{
-			samples = 0;
-		}
-	}
+	ofs->color = GPU_texture_create_2D_custom_multisample(width, height, 4,
+	        (high_bitdepth) ? GPU_RGBA16F : GPU_RGBA8, NULL, samples, err_out);
 
 	if (depth) {
 		ofs->depth = GPU_texture_create_depth_with_stencil_multisample(width, height, samples, err_out);
-		if (!ofs->depth) {
-			GPU_offscreen_free(ofs);
-			return NULL;
-		}
-
-		if (!GPU_framebuffer_texture_attach(ofs->fb, ofs->depth, 0, 0)) {
-			GPU_offscreen_free(ofs);
-			return NULL;
-		}
-	}
-
-	if (high_bitdepth) {
-		ofs->color = GPU_texture_create_2D_custom_multisample(width, height, 4, GPU_RGBA16F, NULL, samples, err_out);
-	}
-	else {
-		ofs->color = GPU_texture_create_2D_multisample(width, height, NULL, samples, err_out);
-	}
-	if (!ofs->color) {
-		GPU_offscreen_free(ofs);
-		return NULL;
 	}
 
-	if (!GPU_framebuffer_texture_attach(ofs->fb, ofs->color, 0, 0)) {
+	if (!ofs->depth || !ofs->color) {
 		GPU_offscreen_free(ofs);
 		return NULL;
 	}
 	
+	GPU_framebuffer_ensure_config(&ofs->fb, {
+		GPU_ATTACHMENT_TEXTURE(ofs->depth),
+		GPU_ATTACHMENT_TEXTURE(ofs->color)
+	});
+
 	/* check validity at the very end! */
 	if (!GPU_framebuffer_check_valid(ofs->fb, err_out)) {
 		GPU_offscreen_free(ofs);
@@ -724,38 +699,19 @@ void GPU_offscreen_free(GPUOffScreen *ofs)
 
 void GPU_offscreen_bind(GPUOffScreen *ofs, bool save)
 {
-	glDisable(GL_SCISSOR_TEST);
-	if (save)
-		GPU_texture_bind_as_framebuffer(ofs->color);
-	else {
-		GPU_framebuffer_bind_no_save(ofs->fb, 0);
+	if (save) {
+		gpuPushAttrib(GPU_SCISSOR_BIT | GPU_VIEWPORT_BIT);
 	}
+	glDisable(GL_SCISSOR_TEST);
+	GPU_framebuffer_bind(ofs->fb);
 }
 
-void GPU_offscreen_unbind(GPUOffScreen *ofs, bool restore)
+void GPU_offscreen_unbind(GPUOffScreen *UNUSED(ofs), bool restore)
 {
-	if (restore)
-		GPU_framebuffer_texture_unbind(ofs->fb, ofs->color);
 	GPU_framebuffer_restore();
-	glEnable(GL_SCISSOR_TEST);
-}
-
-void GPU_offscreen_blit(GPUOffScreen *ofs, int x, int y)
-{
-	const int w = GPU_texture_width(ofs->color);
-	const int h = GPU_texture_height(ofs->color);
-
-	glBindFramebuffer(GL_READ_FRAMEBUFFER, ofs->fb->object);
-	GLenum status = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER);
-
-	if (status == GL_FRAMEBUFFER_COMPLETE) {
-		glBlitFramebuffer(0, 0, w, h, x, y, x + w, y + h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+	if (restore) {
+		gpuPopAttrib();
 	}
-	else {
-		gpu_print_framebuffer_error(status, NULL);
-	}
-
-	glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
 }
 
 void GPU_offscreen_read_pixels(GPUOffScreen *ofs, int type, void *pixels)
@@ -763,46 +719,28 @@ void GPU_offscreen_read_pixels(GPUOffScreen *ofs, int type, void *pixels)
 	const int w = GPU_texture_width(ofs->color);
 	const int h = GPU_texture_height(ofs->color);
 
+	BLI_assert(type == GL_UNSIGNED_BYTE || type == GL_FLOAT);
+
 	if (GPU_texture_target(ofs->color) == GL_TEXTURE_2D_MULTISAMPLE) {
 		/* For a multi-sample texture,
 		 * we need to create an intermediate buffer to blit to,
 		 * before its copied using 'glReadPixels' */
-
-		/* not needed since 'ofs' needs to be bound to the framebuffer already */
-// #define USE_FBO_CTX_SWITCH
-
 		GLuint fbo_blit = 0;
 		GLuint tex_blit = 0;
-		GLenum status;
 
 		/* create texture for new 'fbo_blit' */
 		glGenTextures(1, &tex_blit);
-		if (!tex_blit) {
-			goto finally;
-		}
-
 		glBindTexture(GL_TEXTURE_2D, tex_blit);
-		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, type, 0);
-
-#ifdef USE_FBO_CTX_SWITCH
-		/* read from multi-sample buffer */
-		glBindFramebuffer(GL_READ_FRAMEBUFFER, ofs->color->fb->object);
-		glFramebufferTexture2D(
-		        GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + ofs->color->fb_attachment,
-		        GL_TEXTURE_2D_MULTISAMPLE, ofs->color->bindcode, 0);
-		status = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER);
-		if (status != GL_FRAMEBUFFER_COMPLETE) {
-			goto finally;
-		}
-#endif
+		glTexImage2D(GL_TEXTURE_2D, 0, (type == GL_FLOAT) ? GL_RGBA16F : GL_RGBA8,
+		             w, h, 0, GL_RGBA, type, 0);
 
 		/* write into new single-sample buffer */
 		glGenFramebuffers(1, &fbo_blit);
 		glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_blit);
-		glFramebufferTexture2D(
-		        GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
-		        GL_TEXTURE_2D, tex_blit, 0);
-		status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
+		glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+		                       GL_TEXTURE_2D, tex_blit, 0);
+
+		GLenum status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
 		if (status != GL_FRAMEBUFFER_COMPLETE) {
 			goto finally;
 		}
@@ -814,21 +752,13 @@ void GPU_offscreen_read_pixels(GPUOffScreen *ofs, int type, void *pixels)
 		glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_blit);
 		glReadPixels(0, 0, w, h, GL_RGBA, type, pixels);
 
-#ifdef USE_FBO_CTX_SWITCH
 		/* restore the original frame-bufer */
-		glBindFramebuffer(GL_FRAMEBUFFER, ofs->color->fb->object);
-#undef USE_FBO_CTX_SWITCH
-#endif
-
+		glBindFramebuffer(GL_FRAMEBUFFER, ofs->fb->object);
 
 finally:
 		/* cleanup */
-		if (tex_blit) {
-			glDeleteTextures(1, &tex_blit);
-		}
-		if (fbo_blit) {
-			glDeleteFramebuffers(1, &fbo_blit);
-		}
+		glDeleteTextures(1, &tex_blit);
+		glDeleteFramebuffers(1, &fbo_blit);
 	}
 	else {
 		glReadPixels(0, 0, w, h, GL_RGBA, type, pixels);
diff --git a/source/blender/gpu/intern/gpu_lamp.c b/source/blender/gpu/intern/gpu_lamp.c
index 3c49c057b49738e05b85b0b20725a69f5cc726b5..8968521060da17f470dbbc71c286d1a589632196 100644
--- a/source/blender/gpu/intern/gpu_lamp.c
+++ b/source/blender/gpu/intern/gpu_lamp.c
@@ -49,6 +49,7 @@
 #include "GPU_material.h"
 #include "GPU_shader.h"
 #include "GPU_texture.h"
+#include "GPU_batch.h"
 
 #include "gpu_lamp_private.h"
 
@@ -266,90 +267,35 @@ GPULamp *GPU_lamp_from_blender(Scene *scene, Object *ob, Object *par)
 	if ((la->type == LA_SPOT && (la->mode & (LA_SHAD_BUF | LA_SHAD_RAY))) ||
 	    (la->type == LA_SUN && (la->mode & LA_SHAD_RAY)))
 	{
-		/* opengl */
-		lamp->fb = GPU_framebuffer_create();
-		if (!lamp->fb) {
-			gpu_lamp_shadow_free(lamp);
-			return lamp;
-		}
-
 		if (lamp->la->shadowmap_type == LA_SHADMAP_VARIANCE) {
-			/* Shadow depth map */
 			lamp->depthtex = GPU_texture_create_depth(lamp->size, lamp->size, NULL);
-			if (!lamp->depthtex) {
-				gpu_lamp_shadow_free(lamp);
-				return lamp;
-			}
-
-			GPU_texture_bind(lamp->depthtex, 0);
-			GPU_texture_compare_mode(lamp->depthtex, true);
-			GPU_texture_unbind(lamp->depthtex);
-
-			if (!GPU_framebuffer_texture_attach(lamp->fb, lamp->depthtex, 0, 0)) {
-				gpu_lamp_shadow_free(lamp);
-				return lamp;
-			}
-
-			/* Shadow color map */
 			lamp->tex = gpu_lamp_create_vsm_shadow_map(lamp->size);
-			if (!lamp->tex) {
-				gpu_lamp_shadow_free(lamp);
-				return lamp;
-			}
-
-			if (!GPU_framebuffer_texture_attach(lamp->fb, lamp->tex, 0, 0)) {
-				gpu_lamp_shadow_free(lamp);
-				return lamp;
-			}
-
-			if (!GPU_framebuffer_check_valid(lamp->fb, NULL)) {
-				gpu_lamp_shadow_free(lamp);
-				return lamp;
-			}
-
-			/* FBO and texture for blurring */
-			lamp->blurfb = GPU_framebuffer_create();
-			if (!lamp->blurfb) {
-				gpu_lamp_shadow_free(lamp);
-				return lamp;
-			}
-
 			lamp->blurtex = gpu_lamp_create_vsm_shadow_map(lamp->size * 0.5);
-			if (!lamp->blurtex) {
-				gpu_lamp_shadow_free(lamp);
-				return lamp;
-			}
 
-			if (!GPU_framebuffer_texture_attach(lamp->blurfb, lamp->blurtex, 0, 0)) {
-				gpu_lamp_shadow_free(lamp);
-				return lamp;
-			}
+			lamp->fb = GPU_framebuffer_create();
+			GPU_framebuffer_texture_attach(lamp->fb, lamp->depthtex, 0, 0);
+			GPU_framebuffer_texture_attach(lamp->fb, lamp->tex, 0, 0);
 
-			/* we need to properly bind to test for completeness */
-			GPU_texture_bind_as_framebuffer(lamp->blurtex);
+			lamp->blurfb = GPU_framebuffer_create();
+			GPU_framebuffer_texture_attach(lamp->blurfb, lamp->blurtex, 0, 0);
 
-			if (!GPU_framebuffer_check_valid(lamp->blurfb, NULL)) {
+			if (!GPU_framebuffer_check_valid(lamp->fb, NULL) ||
+			    !GPU_framebuffer_check_valid(lamp->blurfb, NULL))
+			{
 				gpu_lamp_shadow_free(lamp);
 				return lamp;
 			}
-
-			GPU_framebuffer_texture_unbind(lamp->blurfb, lamp->blurtex);
 		}
 		else {
 			lamp->tex = GPU_texture_create_depth(lamp->size, lamp->size, NULL);
-			if (!lamp->tex) {
-				gpu_lamp_shadow_free(lamp);
-				return lamp;
-			}
 
 			GPU_texture_bind(lamp->tex, 0);
 			GPU_texture_compare_mode(lamp->tex, true);
+			GPU_texture_filter_mode(lamp->tex, true);
 			GPU_texture_unbind(lamp->tex);
 
-			if (!GPU_framebuffer_texture_attach(lamp->fb, lamp->tex, 0, 0)) {
-				gpu_lamp_shadow_free(lamp);
-				return lamp;
-			}
+			lamp->fb = GPU_framebuffer_create();
+			GPU_framebuffer_texture_attach(lamp->fb, lamp->tex, 0, 0);
 
 			if (!GPU_framebuffer_check_valid(lamp->fb, NULL)) {
 				gpu_lamp_shadow_free(lamp);
@@ -437,7 +383,7 @@ void GPU_lamp_shadow_buffer_bind(GPULamp *lamp, float viewmat[4][4], int *winsiz
 
 	/* opengl */
 	glDisable(GL_SCISSOR_TEST);
-	GPU_texture_bind_as_framebuffer(lamp->tex);
+	GPU_framebuffer_bind(lamp->fb);
 	if (lamp->la->shadowmap_type == LA_SHADMAP_VARIANCE)
 		GPU_shader_bind(GPU_shader_get_builtin_shader(GPU_SHADER_VSM_STORE));
 
@@ -447,14 +393,45 @@ void GPU_lamp_shadow_buffer_bind(GPULamp *lamp, float viewmat[4][4], int *winsiz
 	*winsize = lamp->size;
 }
 
+static void gpu_lamp_shadow_blur(GPULamp *lamp)
+{
+	const float scaleh[2] = {1.0f / GPU_texture_width(lamp->blurtex), 0.0f};
+	const float scalev[2] = {0.0f, 1.0f / GPU_texture_height(lamp->tex)};
+
+	GPUShader *blur_shader = GPU_shader_get_builtin_shader(GPU_SHADER_SEP_GAUSSIAN_BLUR);
+
+	if (!blur_shader)
+		return;
+
+	int tex_loc = GPU_shader_get_uniform(blur_shader, "textureSource");
+	int scale_loc = GPU_shader_get_uniform(blur_shader, "ScaleU");
+
+	glDisable(GL_DEPTH_TEST);
+
+	GPU_shader_bind(blur_shader);
+
+	/* Blurring horizontally */
+	GPU_framebuffer_bind(lamp->blurfb);
+	GPU_texture_bind(lamp->tex, 0);
+	GPU_shader_uniform_vector(blur_shader, scale_loc, 2, 1, scaleh);
+	GPU_shader_uniform_texture(blur_shader, tex_loc, lamp->tex);
+	GWN_draw_primitive(GL_TRIANGLES, 3);
+
+	/* Blurring vertically */
+	GPU_framebuffer_bind(lamp->fb);
+	GPU_texture_bind(lamp->blurtex, 0);
+	GPU_shader_uniform_vector(blur_shader, scale_loc, 2, 1, scalev);
+	GPU_shader_uniform_texture(blur_shader, tex_loc, lamp->blurtex);
+	GWN_draw_primitive(GL_TRIANGLES, 3);
+}
+
 void GPU_lamp_shadow_buffer_unbind(GPULamp *lamp)
 {
 	if (lamp->la->shadowmap_type == LA_SHADMAP_VARIANCE) {
 		GPU_shader_unbind();
-		GPU_framebuffer_blur(lamp->fb, lamp->tex, lamp->blurfb, lamp->blurtex);
+		gpu_lamp_shadow_blur(lamp);
 	}
 
-	GPU_framebuffer_texture_unbind(lamp->fb, lamp->tex);
 	GPU_framebuffer_restore();
 	glEnable(GL_SCISSOR_TEST);
 }
diff --git a/source/blender/gpu/intern/gpu_select_sample_query.c b/source/blender/gpu/intern/gpu_select_sample_query.c
index b8c3e16405544b09ff710f61db533b0ef43997ad..d9e8351c09406d415075d92e18406532dcd08806 100644
--- a/source/blender/gpu/intern/gpu_select_sample_query.c
+++ b/source/blender/gpu/intern/gpu_select_sample_query.c
@@ -44,6 +44,8 @@
 
 #include "BLI_utildefines.h"
 
+#include "PIL_time.h"
+
 #include "gpu_select_private.h"
 
 
@@ -96,7 +98,7 @@ void gpu_select_query_begin(
 	g_query_state.id = MEM_mallocN(g_query_state.num_of_queries * sizeof(*g_query_state.id), "gpu selection ids");
 	glGenQueries(g_query_state.num_of_queries, g_query_state.queries);
 
-	gpuPushAttrib(GPU_DEPTH_BUFFER_BIT | GPU_VIEWPORT_BIT);
+	gpuPushAttrib(GPU_DEPTH_BUFFER_BIT | GPU_VIEWPORT_BIT | GPU_SCISSOR_BIT);
 	/* disable writing to the framebuffer */
 	glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
 
@@ -114,6 +116,7 @@ void gpu_select_query_begin(
 		glDepthMask(GL_FALSE);
 	}
 	else if (mode == GPU_SELECT_NEAREST_FIRST_PASS) {
+		glDisable(GL_SCISSOR_TEST); /* allows fast clear */
 		glClear(GL_DEPTH_BUFFER_BIT);
 		glEnable(GL_DEPTH_TEST);
 		glDepthMask(GL_TRUE);
@@ -172,8 +175,21 @@ uint gpu_select_query_end(void)
 		glEndQuery(GL_SAMPLES_PASSED);
 	}
 
+	/* We need to sync to get the results anyway.
+	 * If we don't do that the driver will do. */
+	glFinish();
+
 	for (i = 0; i < g_query_state.active_query; i++) {
-		uint result;
+		uint result = 0;
+		/* Wait until the result is available. This can happen even if glFinish() was called. */
+		while (result == 0) {
+			glGetQueryObjectuiv(g_query_state.queries[i], GL_QUERY_RESULT_AVAILABLE, &result);
+			if (result == 0) {
+				/* (fclem) Not sure if this is better than calling
+				 * glGetQueryObjectuiv() indefinitely. */
+				PIL_sleep_ms(1);
+			}
+		}
 		glGetQueryObjectuiv(g_query_state.queries[i], GL_QUERY_RESULT, &result);
 		if (result > 0) {
 			if (g_query_state.mode != GPU_SELECT_NEAREST_SECOND_PASS) {
diff --git a/source/blender/gpu/intern/gpu_shader.c b/source/blender/gpu/intern/gpu_shader.c
index b06ee56c21f1cc5699309f792a9317fea91479a6..db5c2d1bcb578389d4c44e599d5a77fb13c6364c 100644
--- a/source/blender/gpu/intern/gpu_shader.c
+++ b/source/blender/gpu/intern/gpu_shader.c
@@ -37,7 +37,6 @@
 
 #include "DNA_space_types.h"
 
-#include "GPU_compositing.h"
 #include "GPU_extensions.h"
 #include "GPU_matrix.h"
 #include "GPU_shader.h"
@@ -66,11 +65,20 @@ extern char datatoc_gpu_shader_2D_flat_color_vert_glsl[];
 extern char datatoc_gpu_shader_2D_smooth_color_vert_glsl[];
 extern char datatoc_gpu_shader_2D_smooth_color_frag_glsl[];
 extern char datatoc_gpu_shader_2D_image_vert_glsl[];
+extern char datatoc_gpu_shader_2D_image_rect_vert_glsl[];
+extern char datatoc_gpu_shader_2D_image_multi_rect_vert_glsl[];
+extern char datatoc_gpu_shader_2D_widget_base_vert_glsl[];
+extern char datatoc_gpu_shader_2D_widget_base_frag_glsl[];
+extern char datatoc_gpu_shader_2D_widget_shadow_vert_glsl[];
+extern char datatoc_gpu_shader_2D_widget_shadow_frag_glsl[];
+extern char datatoc_gpu_shader_2D_nodelink_frag_glsl[];
+extern char datatoc_gpu_shader_2D_nodelink_vert_glsl[];
 
 extern char datatoc_gpu_shader_3D_image_vert_glsl[];
 extern char datatoc_gpu_shader_image_frag_glsl[];
 extern char datatoc_gpu_shader_image_linear_frag_glsl[];
 extern char datatoc_gpu_shader_image_color_frag_glsl[];
+extern char datatoc_gpu_shader_image_varying_color_frag_glsl[];
 extern char datatoc_gpu_shader_image_alpha_color_frag_glsl[];
 extern char datatoc_gpu_shader_image_shuffle_color_frag_glsl[];
 extern char datatoc_gpu_shader_image_interlace_frag_glsl[];
@@ -134,7 +142,10 @@ extern char datatoc_gpu_shader_edges_overlay_geom_glsl[];
 extern char datatoc_gpu_shader_edges_overlay_simple_geom_glsl[];
 extern char datatoc_gpu_shader_edges_overlay_frag_glsl[];
 extern char datatoc_gpu_shader_text_vert_glsl[];
+extern char datatoc_gpu_shader_text_geom_glsl[];
 extern char datatoc_gpu_shader_text_frag_glsl[];
+extern char datatoc_gpu_shader_text_simple_vert_glsl[];
+extern char datatoc_gpu_shader_text_simple_geom_glsl[];
 extern char datatoc_gpu_shader_keyframe_diamond_vert_glsl[];
 extern char datatoc_gpu_shader_keyframe_diamond_frag_glsl[];
 
@@ -145,22 +156,10 @@ extern char datatoc_gpu_shader_vsm_store_vert_glsl[];
 extern char datatoc_gpu_shader_vsm_store_frag_glsl[];
 extern char datatoc_gpu_shader_sep_gaussian_blur_vert_glsl[];
 extern char datatoc_gpu_shader_sep_gaussian_blur_frag_glsl[];
-extern char datatoc_gpu_shader_fullscreen_vert_glsl[];
-extern char datatoc_gpu_shader_fx_ssao_frag_glsl[];
-extern char datatoc_gpu_shader_fx_dof_frag_glsl[];
-extern char datatoc_gpu_shader_fx_dof_vert_glsl[];
-extern char datatoc_gpu_shader_fx_dof_hq_frag_glsl[];
-extern char datatoc_gpu_shader_fx_dof_hq_vert_glsl[];
-extern char datatoc_gpu_shader_fx_dof_hq_geo_glsl[];
-extern char datatoc_gpu_shader_fx_depth_resolve_glsl[];
-extern char datatoc_gpu_shader_fx_lib_glsl[];
 
 /* cache of built-in shaders (each is created on first use) */
 static GPUShader *builtin_shaders[GPU_NUM_BUILTIN_SHADERS] = { NULL };
 
-/* cache for shader fx. Those can exist in combinations so store them here */
-static GPUShader *fx_shaders[MAX_FX_SHADERS * 2] = { NULL };
-
 typedef struct {
 	const char *vert;
 	const char *frag;
@@ -543,9 +542,6 @@ void GPU_shader_free(GPUShader *shader)
 	if (shader->program)
 		glDeleteProgram(shader->program);
 
-	if (shader->uniform_interface)
-		MEM_freeN(shader->uniform_interface);
-
 	if (shader->interface)
 		GWN_shaderinterface_discard(shader->interface);
 
@@ -574,11 +570,6 @@ int GPU_shader_get_uniform_block(GPUShader *shader, const char *name)
 	return ubo ? ubo->location : -1;
 }
 
-void *GPU_fx_shader_get_interface(GPUShader *shader)
-{
-	return shader->uniform_interface;
-}
-
 void *GPU_shader_get_interface(GPUShader *shader)
 {
 	return shader->interface;
@@ -590,11 +581,6 @@ int GPU_shader_get_program(GPUShader *shader)
 	return (int)shader->program;
 }
 
-void GPU_fx_shader_set_interface(GPUShader *shader, void *interface)
-{
-	shader->uniform_interface = interface;
-}
-
 void GPU_shader_uniform_vector(GPUShader *UNUSED(shader), int location, int length, int arraysize, const float *value)
 {
 	if (location == -1 || value == NULL)
@@ -641,32 +627,17 @@ void GPU_shader_uniform_buffer(GPUShader *shader, int location, GPUUniformBuffer
 void GPU_shader_uniform_texture(GPUShader *UNUSED(shader), int location, GPUTexture *tex)
 {
 	int number = GPU_texture_bound_number(tex);
-	int bindcode = GPU_texture_opengl_bindcode(tex);
-	int target = GPU_texture_target(tex);
 
-	if (number >= GPU_max_textures()) {
-		fprintf(stderr, "Not enough texture slots.\n");
+	if (number == -1) {
+		fprintf(stderr, "Texture is not bound.\n");
+		BLI_assert(0);
 		return;
 	}
-		
-	if (number == -1)
-		return;
 
 	if (location == -1)
 		return;
 
-	if (number != 0)
-		glActiveTexture(GL_TEXTURE0 + number);
-
-	if (bindcode != 0)
-		glBindTexture(target, bindcode);
-	else
-		GPU_invalid_tex_bind(target);
-
 	glUniform1i(location, number);
-
-	if (number != 0)
-		glActiveTexture(GL_TEXTURE0);
 }
 
 int GPU_shader_get_attribute(GPUShader *shader, const char *name)
@@ -688,7 +659,12 @@ GPUShader *GPU_shader_get_builtin_shader(GPUBuiltinShader shader)
 		[GPU_SHADER_SMOKE_FIRE] = { datatoc_gpu_shader_smoke_vert_glsl, datatoc_gpu_shader_smoke_frag_glsl },
 		[GPU_SHADER_SMOKE_COBA] = { datatoc_gpu_shader_smoke_vert_glsl, datatoc_gpu_shader_smoke_frag_glsl },
 
-		[GPU_SHADER_TEXT] = { datatoc_gpu_shader_text_vert_glsl, datatoc_gpu_shader_text_frag_glsl },
+		[GPU_SHADER_TEXT] = { datatoc_gpu_shader_text_vert_glsl,
+		                      datatoc_gpu_shader_text_frag_glsl,
+		                      datatoc_gpu_shader_text_geom_glsl },
+		[GPU_SHADER_TEXT_SIMPLE] = { datatoc_gpu_shader_text_simple_vert_glsl,
+		                             datatoc_gpu_shader_text_frag_glsl,
+		                             datatoc_gpu_shader_text_simple_geom_glsl },
 		[GPU_SHADER_KEYFRAME_DIAMOND] = { datatoc_gpu_shader_keyframe_diamond_vert_glsl,
 		                                  datatoc_gpu_shader_keyframe_diamond_frag_glsl },
 		[GPU_SHADER_EDGES_FRONT_BACK_PERSP] = { datatoc_gpu_shader_edges_front_back_persp_vert_glsl,
@@ -739,6 +715,11 @@ GPUShader *GPU_shader_get_builtin_shader(GPUBuiltinShader shader)
 		                                datatoc_gpu_shader_image_modulate_alpha_frag_glsl },
 		[GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR] = { datatoc_gpu_shader_2D_image_vert_glsl,
 		                                        datatoc_gpu_shader_image_shuffle_color_frag_glsl },
+		[GPU_SHADER_2D_IMAGE_RECT_COLOR] = { datatoc_gpu_shader_2D_image_rect_vert_glsl,
+		                                     datatoc_gpu_shader_image_color_frag_glsl },
+		[GPU_SHADER_2D_IMAGE_MULTI_RECT_COLOR] = { datatoc_gpu_shader_2D_image_multi_rect_vert_glsl,
+		                                           datatoc_gpu_shader_image_varying_color_frag_glsl },
+
 		[GPU_SHADER_3D_UNIFORM_COLOR] = { datatoc_gpu_shader_3D_vert_glsl, datatoc_gpu_shader_uniform_color_frag_glsl },
 		[GPU_SHADER_3D_UNIFORM_COLOR_U32] = { datatoc_gpu_shader_3D_vert_glsl, datatoc_gpu_shader_uniform_color_frag_glsl },
 		[GPU_SHADER_3D_FLAT_COLOR] = { datatoc_gpu_shader_3D_flat_color_vert_glsl,
@@ -821,6 +802,17 @@ GPUShader *GPU_shader_get_builtin_shader(GPUBuiltinShader shader)
 		                                               datatoc_gpu_shader_flat_color_frag_glsl,
 		                                               datatoc_gpu_shader_instance_edges_variying_color_geom_glsl},
 
+		[GPU_SHADER_2D_WIDGET_BASE] = { datatoc_gpu_shader_2D_widget_base_vert_glsl,
+		                                datatoc_gpu_shader_2D_widget_base_frag_glsl},
+		[GPU_SHADER_2D_WIDGET_BASE_INST] = { datatoc_gpu_shader_2D_widget_base_vert_glsl,
+		                                     datatoc_gpu_shader_2D_widget_base_frag_glsl},
+		[GPU_SHADER_2D_WIDGET_SHADOW] = { datatoc_gpu_shader_2D_widget_shadow_vert_glsl,
+		                                  datatoc_gpu_shader_2D_widget_shadow_frag_glsl },
+		[GPU_SHADER_2D_NODELINK] = { datatoc_gpu_shader_2D_nodelink_vert_glsl,
+		                             datatoc_gpu_shader_2D_nodelink_frag_glsl },
+		[GPU_SHADER_2D_NODELINK_INST] = { datatoc_gpu_shader_2D_nodelink_vert_glsl,
+		                                  datatoc_gpu_shader_2D_nodelink_frag_glsl },
+
 		[GPU_SHADER_3D_INSTANCE_BONE_ENVELOPE_SOLID] = { datatoc_gpu_shader_instance_bone_envelope_solid_vert_glsl,
 		                                                 datatoc_gpu_shader_simple_lighting_frag_glsl },
 		[GPU_SHADER_3D_INSTANCE_BONE_ENVELOPE_WIRE] = { datatoc_gpu_shader_instance_bone_envelope_wire_vert_glsl,
@@ -834,6 +826,10 @@ GPUShader *GPU_shader_get_builtin_shader(GPUBuiltinShader shader)
 		/* just a few special cases */
 		const char *defines = NULL;
 		switch (shader) {
+			case GPU_SHADER_2D_WIDGET_BASE_INST:
+			case GPU_SHADER_2D_NODELINK_INST:
+				defines = "#define USE_INSTANCE;\n";
+				break;
 			case GPU_SHADER_SMOKE_COBA:
 				defines = "#define USE_COBA;\n";
 				break;
@@ -885,82 +881,6 @@ GPUShader *GPU_shader_get_builtin_shader(GPUBuiltinShader shader)
 
 #define MAX_DEFINES 100
 
-GPUShader *GPU_shader_get_builtin_fx_shader(int effect, bool persp)
-{
-	int offset;
-	char defines[MAX_DEFINES] = "";
-	/* avoid shaders out of range */
-	if (effect >= MAX_FX_SHADERS)
-		return NULL;
-
-	offset = 2 * effect;
-
-	if (persp) {
-		offset += 1;
-		strcat(defines, "#define PERSP_MATRIX\n");
-	}
-
-	if (!fx_shaders[offset]) {
-		GPUShader *shader = NULL;
-
-		switch (effect) {
-			case GPU_SHADER_FX_SSAO:
-				shader = GPU_shader_create(datatoc_gpu_shader_fullscreen_vert_glsl, datatoc_gpu_shader_fx_ssao_frag_glsl, NULL, datatoc_gpu_shader_fx_lib_glsl, defines);
-				break;
-
-			case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_ONE:
-				strcat(defines, "#define FIRST_PASS\n");
-				shader = GPU_shader_create(datatoc_gpu_shader_fx_dof_vert_glsl, datatoc_gpu_shader_fx_dof_frag_glsl, NULL, datatoc_gpu_shader_fx_lib_glsl, defines);
-				break;
-
-			case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_TWO:
-				strcat(defines, "#define SECOND_PASS\n");
-				shader = GPU_shader_create(datatoc_gpu_shader_fx_dof_vert_glsl, datatoc_gpu_shader_fx_dof_frag_glsl, NULL, datatoc_gpu_shader_fx_lib_glsl, defines);
-				break;
-
-			case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_THREE:
-				strcat(defines, "#define THIRD_PASS\n");
-				shader = GPU_shader_create(datatoc_gpu_shader_fx_dof_vert_glsl, datatoc_gpu_shader_fx_dof_frag_glsl, NULL, datatoc_gpu_shader_fx_lib_glsl, defines);
-				break;
-
-			case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_FOUR:
-				strcat(defines, "#define FOURTH_PASS\n");
-				shader = GPU_shader_create(datatoc_gpu_shader_fx_dof_vert_glsl, datatoc_gpu_shader_fx_dof_frag_glsl, NULL, datatoc_gpu_shader_fx_lib_glsl, defines);
-				break;
-
-			case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_FIVE:
-				strcat(defines, "#define FIFTH_PASS\n");
-				shader = GPU_shader_create(datatoc_gpu_shader_fx_dof_vert_glsl, datatoc_gpu_shader_fx_dof_frag_glsl, NULL, datatoc_gpu_shader_fx_lib_glsl, defines);
-				break;
-
-			case GPU_SHADER_FX_DEPTH_OF_FIELD_HQ_PASS_ONE:
-				strcat(defines, "#define FIRST_PASS\n");
-				shader = GPU_shader_create(datatoc_gpu_shader_fx_dof_hq_vert_glsl, datatoc_gpu_shader_fx_dof_hq_frag_glsl, NULL, datatoc_gpu_shader_fx_lib_glsl, defines);
-				break;
-
-			case GPU_SHADER_FX_DEPTH_OF_FIELD_HQ_PASS_TWO:
-				strcat(defines, "#define SECOND_PASS\n");
-				shader = GPU_shader_create(datatoc_gpu_shader_fx_dof_hq_vert_glsl, datatoc_gpu_shader_fx_dof_hq_frag_glsl, datatoc_gpu_shader_fx_dof_hq_geo_glsl, datatoc_gpu_shader_fx_lib_glsl, defines);
-				break;
-
-			case GPU_SHADER_FX_DEPTH_OF_FIELD_HQ_PASS_THREE:
-				strcat(defines, "#define THIRD_PASS\n");
-				shader = GPU_shader_create(datatoc_gpu_shader_fx_dof_hq_vert_glsl, datatoc_gpu_shader_fx_dof_hq_frag_glsl, NULL, datatoc_gpu_shader_fx_lib_glsl, defines);
-				break;
-
-			case GPU_SHADER_FX_DEPTH_RESOLVE:
-				shader = GPU_shader_create(datatoc_gpu_shader_fullscreen_vert_glsl, datatoc_gpu_shader_fx_depth_resolve_glsl, NULL, NULL, defines);
-				break;
-		}
-
-		fx_shaders[offset] = shader;
-		GPU_fx_shader_init_interface(shader, effect);
-	}
-
-	return fx_shaders[offset];
-}
-
-
 void GPU_shader_free_builtin_shaders(void)
 {
 	for (int i = 0; i < GPU_NUM_BUILTIN_SHADERS; ++i) {
@@ -969,11 +889,4 @@ void GPU_shader_free_builtin_shaders(void)
 			builtin_shaders[i] = NULL;
 		}
 	}
-
-	for (int i = 0; i < 2 * MAX_FX_SHADERS; ++i) {
-		if (fx_shaders[i]) {
-			GPU_shader_free(fx_shaders[i]);
-			fx_shaders[i] = NULL;
-		}
-	}
 }
diff --git a/source/blender/gpu/intern/gpu_shader_private.h b/source/blender/gpu/intern/gpu_shader_private.h
index f883773df17baf68d623e7dff05d834e851bbfd3..67d8c6e62139b8ca800bb9829b3f0617096e5d02 100644
--- a/source/blender/gpu/intern/gpu_shader_private.h
+++ b/source/blender/gpu/intern/gpu_shader_private.h
@@ -35,9 +35,6 @@ struct GPUShader {
 	GLuint geometry; /* handle for geometry shader */
 	GLuint fragment; /* handle for fragment shader */
 
-	void *uniform_interface; /* cached uniform interface for shader. Data depends on shader */
-	/* NOTE: ^-- only FX compositing shaders use this */
-
 	Gwn_ShaderInterface *interface; /* cached uniform & attrib interface for shader */
 };
 
diff --git a/source/blender/gpu/intern/gpu_texture.c b/source/blender/gpu/intern/gpu_texture.c
index bd25dd03f133b888349a7e33ba3cc5470a52700b..75830f60f038c4c6eed221590e262c336ce22349 100644
--- a/source/blender/gpu/intern/gpu_texture.c
+++ b/source/blender/gpu/intern/gpu_texture.c
@@ -48,6 +48,22 @@ static struct GPUTextureGlobal {
 	GPUTexture *invalid_tex_3D;
 } GG = {NULL, NULL, NULL};
 
+/* Maximum number of FBOs a texture can be attached to. */
+#define GPU_TEX_MAX_FBO_ATTACHED 8
+
+typedef enum GPUTextureFormatFlag{
+	GPU_FORMAT_DEPTH     = (1 << 0),
+	GPU_FORMAT_STENCIL   = (1 << 1),
+	GPU_FORMAT_INTEGER   = (1 << 2),
+	GPU_FORMAT_FLOAT     = (1 << 3),
+
+	GPU_FORMAT_1D        = (1 << 10),
+	GPU_FORMAT_2D        = (1 << 11),
+	GPU_FORMAT_3D        = (1 << 12),
+	GPU_FORMAT_CUBE      = (1 << 13),
+	GPU_FORMAT_ARRAY     = (1 << 14),
+} GPUTextureFormatFlag;
+
 /* GPUTexture */
 struct GPUTexture {
 	int w, h, d;        /* width/height/depth */
@@ -59,15 +75,15 @@ struct GPUTexture {
 	GLuint bindcode;    /* opengl identifier for texture */
 	int fromblender;    /* we got the texture from Blender */
 
-	GPUFrameBuffer *fb; /* GPUFramebuffer this texture is attached to */
-	int fb_attachment;  /* slot the texture is attached to */
-	bool depth;         /* is a depth texture? */
-	bool stencil;       /* is a stencil texture? */
+	GPUTextureFormat format;
+	GPUTextureFormatFlag format_flag;
 
 	unsigned int bytesize; /* number of byte for one pixel */
-	int format;         /* GPUTextureFormat */
 	int components;     /* number of color/alpha channels */
 	int samples;        /* number of samples for multisamples textures. 0 if not multisample target */
+
+	int fb_attachment[GPU_TEX_MAX_FBO_ATTACHED];
+	GPUFrameBuffer *fb[GPU_TEX_MAX_FBO_ATTACHED];
 };
 
 /* ------ Memory Management ------- */
@@ -114,30 +130,26 @@ unsigned int GPU_texture_memory_usage_get(void)
 
 static GLenum gpu_texture_get_format(
         int components, GPUTextureFormat data_type,
-        GLenum *format, GLenum *data_format, bool *is_depth, bool *is_stencil, unsigned int *bytesize)
+        GLenum *format, GLenum *data_format, GPUTextureFormatFlag *format_flag, unsigned int *bytesize)
 {
 	if (ELEM(data_type, GPU_DEPTH_COMPONENT24,
 	                    GPU_DEPTH_COMPONENT16,
 	                    GPU_DEPTH_COMPONENT32F))
 	{
-		*is_depth = true;
-		*is_stencil = false;
+		*format_flag |= GPU_FORMAT_DEPTH;
 		*data_format = GL_FLOAT;
 		*format = GL_DEPTH_COMPONENT;
 	}
 	else if (data_type == GPU_DEPTH24_STENCIL8) {
-		*is_depth = true;
-		*is_stencil = true;
+		*format_flag |= GPU_FORMAT_DEPTH | GPU_FORMAT_STENCIL;
 		*data_format = GL_UNSIGNED_INT_24_8;
 		*format = GL_DEPTH_STENCIL;
 	}
 	else {
-		*is_depth = false;
-		*is_stencil = false;
-
 		/* Integer formats */
 		if (ELEM(data_type, GPU_RG16I, GPU_R16I)) {
 			*data_format = GL_INT;
+			*format_flag |= GPU_FORMAT_INTEGER;
 
 			switch (components) {
 				case 1: *format = GL_RED_INTEGER; break;
@@ -149,6 +161,7 @@ static GLenum gpu_texture_get_format(
 		}
 		else {
 			*data_format = GL_FLOAT;
+			*format_flag |= GPU_FORMAT_FLOAT;
 
 			switch (components) {
 				case 1: *format = GL_RED; break;
@@ -346,9 +359,9 @@ static GPUTexture *GPU_texture_create_nD(
 	tex->samples = samples;
 	tex->number = -1;
 	tex->refcount = 1;
-	tex->fb_attachment = -1;
 	tex->format = data_type;
 	tex->components = components;
+	tex->format_flag = 0;
 
 	if (n == 2) {
 		if (d == 0)
@@ -375,7 +388,8 @@ static GPUTexture *GPU_texture_create_nD(
 		tex->target = GL_TEXTURE_2D_MULTISAMPLE;
 
 	GLenum format, internalformat, data_format;
-	internalformat = gpu_texture_get_format(components, data_type, &format, &data_format, &tex->depth, &tex->stencil, &tex->bytesize);
+	internalformat = gpu_texture_get_format(components, data_type, &format, &data_format,
+	                                        &tex->format_flag, &tex->bytesize);
 
 	gpu_texture_memory_footprint_add(tex);
 
@@ -391,7 +405,6 @@ static GPUTexture *GPU_texture_create_nD(
 		return NULL;
 	}
 
-	tex->number = 0;
 	glBindTexture(tex->target, tex->bindcode);
 
 	/* Check if texture fit in VRAM */
@@ -450,17 +463,23 @@ static GPUTexture *GPU_texture_create_nD(
 		MEM_freeN(rescaled_fpixels);
 
 	/* Texture Parameters */
-	if (tex->depth) {
+	if (GPU_texture_stencil(tex) || /* Does not support filtering */
+		GPU_texture_integer(tex) || /* Does not support filtering */
+		GPU_texture_depth(tex))
+	{
 		glTexParameteri(tex->target_base, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-		glTexParameteri(tex->target_base, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-		glTexParameteri(tex->target_base, GL_TEXTURE_COMPARE_MODE, GL_NONE);
-		glTexParameteri(tex->target_base, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
+		glTexParameteri(tex->target_base, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 	}
 	else {
 		glTexParameteri(tex->target_base, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 		glTexParameteri(tex->target_base, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 	}
 
+	if (GPU_texture_depth(tex)) {
+		glTexParameteri(tex->target_base, GL_TEXTURE_COMPARE_MODE, GL_NONE);
+		glTexParameteri(tex->target_base, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
+	}
+
 	glTexParameteri(tex->target_base, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 	if (n > 1) {
 		glTexParameteri(tex->target_base, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
@@ -469,7 +488,7 @@ static GPUTexture *GPU_texture_create_nD(
 		glTexParameteri(tex->target_base, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
 	}
 
-	GPU_texture_unbind(tex);
+	glBindTexture(tex->target, 0);
 
 	return tex;
 }
@@ -490,9 +509,9 @@ static GPUTexture *GPU_texture_cube_create(
 	tex->samples = 0;
 	tex->number = -1;
 	tex->refcount = 1;
-	tex->fb_attachment = -1;
 	tex->format = data_type;
 	tex->components = components;
+	tex->format_flag = GPU_FORMAT_CUBE;
 
 	if (d == 0) {
 		tex->target_base = tex->target = GL_TEXTURE_CUBE_MAP;
@@ -502,7 +521,8 @@ static GPUTexture *GPU_texture_cube_create(
 		// tex->target_base = tex->target = GL_TEXTURE_CUBE_MAP_ARRAY;
 	}
 
-	internalformat = gpu_texture_get_format(components, data_type, &format, &data_format, &tex->depth, &tex->stencil, &tex->bytesize);
+	internalformat = gpu_texture_get_format(components, data_type, &format, &data_format,
+	                                        &tex->format_flag, &tex->bytesize);
 
 	gpu_texture_memory_footprint_add(tex);
 
@@ -518,7 +538,6 @@ static GPUTexture *GPU_texture_cube_create(
 		return NULL;
 	}
 
-	tex->number = 0;
 	glBindTexture(tex->target, tex->bindcode);
 
 	/* Upload Texture */
@@ -530,22 +549,28 @@ static GPUTexture *GPU_texture_cube_create(
 	glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, internalformat, tex->w, tex->h, 0, format, data_format, fpixels_nz);
 
 	/* Texture Parameters */
-	if (tex->depth) {
+	if (GPU_texture_stencil(tex) || /* Does not support filtering */
+	    GPU_texture_integer(tex) || /* Does not support filtering */
+	    GPU_texture_depth(tex))
+	{
 		glTexParameteri(tex->target_base, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-		glTexParameteri(tex->target_base, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-		glTexParameteri(tex->target_base, GL_TEXTURE_COMPARE_MODE, GL_NONE);
-		glTexParameteri(tex->target_base, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
+		glTexParameteri(tex->target_base, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 	}
 	else {
 		glTexParameteri(tex->target_base, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 		glTexParameteri(tex->target_base, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 	}
 
+	if (GPU_texture_depth(tex)) {
+		glTexParameteri(tex->target_base, GL_TEXTURE_COMPARE_MODE, GL_NONE);
+		glTexParameteri(tex->target_base, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
+	}
+
 	glTexParameteri(tex->target_base, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 	glTexParameteri(tex->target_base, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 	glTexParameteri(tex->target_base, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
 
-	GPU_texture_unbind(tex);
+	glBindTexture(tex->target, 0);
 
 	return tex;
 }
@@ -692,7 +717,8 @@ GPUTexture *GPU_texture_create_2D_custom_multisample(
 	return GPU_texture_create_nD(w, h, 0, 2, pixels, data_type, channels, samples, false, err_out);
 }
 
-GPUTexture *GPU_texture_create_2D_array_custom(int w, int h, int d, int channels, GPUTextureFormat data_type, const float *pixels, char err_out[256])
+GPUTexture *GPU_texture_create_2D_array_custom(
+        int w, int h, int d, int channels, GPUTextureFormat data_type, const float *pixels, char err_out[256])
 {
 	return GPU_texture_create_nD(w, h, d, 2, pixels, data_type, channels, 0, false, err_out);
 }
@@ -702,11 +728,13 @@ GPUTexture *GPU_texture_create_3D(int w, int h, int d, const float *pixels, char
 	return GPU_texture_create_nD(w, h, d, 3, pixels, GPU_RGBA8, 4, 0, true, err_out);
 }
 
-GPUTexture *GPU_texture_create_3D_custom(int w, int h, int d, int channels, GPUTextureFormat data_type, const float *pixels, char err_out[256])
+GPUTexture *GPU_texture_create_3D_custom(
+        int w, int h, int d, int channels, GPUTextureFormat data_type, const float *pixels, char err_out[256])
 {
 	return GPU_texture_create_nD(w, h, d, 3, pixels, data_type, channels, 0, true, err_out);
 }
-GPUTexture *GPU_texture_create_cube_custom(int w, int channels, GPUTextureFormat data_type, const float *fpixels, char err_out[256])
+GPUTexture *GPU_texture_create_cube_custom(
+        int w, int channels, GPUTextureFormat data_type, const float *fpixels, char err_out[256])
 {
 	const float *fpixels_px, *fpixels_py, *fpixels_pz, *fpixels_nx, *fpixels_ny, *fpixels_nz;
 
@@ -722,7 +750,8 @@ GPUTexture *GPU_texture_create_cube_custom(int w, int channels, GPUTextureFormat
 		fpixels_px = fpixels_py = fpixels_pz = fpixels_nx = fpixels_ny = fpixels_nz = NULL;
 	}
 
-	return GPU_texture_cube_create(w, 0, fpixels_px, fpixels_py, fpixels_pz, fpixels_nx, fpixels_ny, fpixels_nz, data_type, channels, err_out);
+	return GPU_texture_cube_create(w, 0, fpixels_px, fpixels_py, fpixels_pz, fpixels_nx, fpixels_ny, fpixels_nz,
+	                               data_type, channels, err_out);
 }
 
 GPUTexture *GPU_texture_create_depth(int w, int h, char err_out[256])
@@ -751,7 +780,8 @@ void GPU_texture_update(GPUTexture *tex, const float *pixels)
 	BLI_assert(tex->components > -1);
 
 	GLenum format, data_format;
-	gpu_texture_get_format(tex->components, tex->format, &format, &data_format, &tex->depth, &tex->stencil, &tex->bytesize);
+	gpu_texture_get_format(tex->components, tex->format, &format, &data_format,
+	                       &tex->format_flag, &tex->bytesize);
 
 	glBindTexture(tex->target, tex->bindcode);
 
@@ -809,55 +839,44 @@ void GPU_invalid_tex_free(void)
 		GPU_texture_free(GG.invalid_tex_3D);
 }
 
-
 void GPU_texture_bind(GPUTexture *tex, int number)
 {
+	BLI_assert(number >= 0);
+
 	if (number >= GPU_max_textures()) {
 		fprintf(stderr, "Not enough texture slots.\n");
 		return;
 	}
 
 	if ((G.debug & G_DEBUG)) {
-		if (tex->fb && GPU_framebuffer_bound(tex->fb)) {
-			fprintf(stderr, "Feedback loop warning!: Attempting to bind texture attached to current framebuffer!\n");
+		for (int i = 0; i < GPU_TEX_MAX_FBO_ATTACHED; ++i) {
+			if (tex->fb[i] && GPU_framebuffer_bound(tex->fb[i])) {
+				fprintf(stderr, "Feedback loop warning!: Attempting to bind "
+				                "texture attached to current framebuffer!\n");
+				BLI_assert(0); /* Should never happen! */
+				break;
+			}
 		}
 	}
 
-	if (number < 0)
-		return;
-
-	if (number != 0)
-		glActiveTexture(GL_TEXTURE0 + number);
+	glActiveTexture(GL_TEXTURE0 + number);
 
 	if (tex->bindcode != 0)
 		glBindTexture(tex->target, tex->bindcode);
 	else
 		GPU_invalid_tex_bind(tex->target_base);
 
-	if (number != 0)
-		glActiveTexture(GL_TEXTURE0);
-
 	tex->number = number;
 }
 
 void GPU_texture_unbind(GPUTexture *tex)
 {
-	if (tex->number >= GPU_max_textures()) {
-		fprintf(stderr, "Not enough texture slots.\n");
-		return;
-	}
-
 	if (tex->number == -1)
 		return;
 
-	if (tex->number != 0)
-		glActiveTexture(GL_TEXTURE0 + tex->number);
-
+	glActiveTexture(GL_TEXTURE0 + tex->number);
 	glBindTexture(tex->target, 0);
 
-	if (tex->number != 0)
-		glActiveTexture(GL_TEXTURE0);
-
 	tex->number = -1;
 }
 
@@ -866,114 +885,79 @@ int GPU_texture_bound_number(GPUTexture *tex)
 	return tex->number;
 }
 
+#define WARN_NOT_BOUND(_tex) do { \
+	if (_tex->number == -1) { \
+		fprintf(stderr, "Warning : Trying to set parameter on a texture not bound.\n"); \
+		BLI_assert(0); \
+		return; \
+	} \
+} while (0);
+
 void GPU_texture_generate_mipmap(GPUTexture *tex)
 {
-	if (tex->number >= GPU_max_textures()) {
-		fprintf(stderr, "Not enough texture slots.\n");
-		return;
-	}
-
-	if (tex->number == -1)
-		return;
-
-	if (tex->number != 0)
-		glActiveTexture(GL_TEXTURE0 + tex->number);
+	WARN_NOT_BOUND(tex);
 
+	glActiveTexture(GL_TEXTURE0 + tex->number);
 	glGenerateMipmap(tex->target_base);
-
-	if (tex->number != 0)
-		glActiveTexture(GL_TEXTURE0);
 }
 
 void GPU_texture_compare_mode(GPUTexture *tex, bool use_compare)
 {
-	if (tex->number >= GPU_max_textures()) {
-		fprintf(stderr, "Not enough texture slots.\n");
-		return;
-	}
+	WARN_NOT_BOUND(tex);
 
-	if (tex->number == -1)
+	/* Could become an assertion ? (fclem) */
+	if (!GPU_texture_depth(tex))
 		return;
 
-	if (tex->number != 0)
-		glActiveTexture(GL_TEXTURE0 + tex->number);
-
-	/* TODO viewport: use GL_COMPARE_REF_TO_TEXTURE after we switch to core profile */
-	if (tex->depth)
-		glTexParameteri(tex->target_base, GL_TEXTURE_COMPARE_MODE, use_compare ? GL_COMPARE_R_TO_TEXTURE : GL_NONE);
+	GLenum mode = (use_compare) ? GL_COMPARE_REF_TO_TEXTURE : GL_NONE;
 
-	if (tex->number != 0)
-		glActiveTexture(GL_TEXTURE0);
+	glActiveTexture(GL_TEXTURE0 + tex->number);
+	glTexParameteri(tex->target_base, GL_TEXTURE_COMPARE_MODE, mode);
 }
 
 void GPU_texture_filter_mode(GPUTexture *tex, bool use_filter)
 {
-	if (tex->number >= GPU_max_textures()) {
-		fprintf(stderr, "Not enough texture slots.\n");
-		return;
-	}
+	WARN_NOT_BOUND(tex);
 
-	if (tex->number == -1)
-		return;
+	/* Stencil and integer format does not support filtering. */
+	BLI_assert(!use_filter || !(GPU_texture_stencil(tex) || GPU_texture_integer(tex)));
 
-	if (tex->number != 0)
-		glActiveTexture(GL_TEXTURE0 + tex->number);
+	GLenum filter = (use_filter) ? GL_LINEAR : GL_NEAREST;
 
-	GLenum filter = use_filter ? GL_LINEAR : GL_NEAREST;
+	glActiveTexture(GL_TEXTURE0 + tex->number);
 	glTexParameteri(tex->target_base, GL_TEXTURE_MAG_FILTER, filter);
 	glTexParameteri(tex->target_base, GL_TEXTURE_MIN_FILTER, filter);
-
-	if (tex->number != 0)
-		glActiveTexture(GL_TEXTURE0);
 }
 
 void GPU_texture_mipmap_mode(GPUTexture *tex, bool use_mipmap, bool use_filter)
 {
-	if (tex->number >= GPU_max_textures()) {
-		fprintf(stderr, "Not enough texture slots.\n");
-		return;
-	}
-
-	if (tex->number == -1)
-		return;
+	WARN_NOT_BOUND(tex);
 
-	if (tex->number != 0)
-		glActiveTexture(GL_TEXTURE0 + tex->number);
+	/* Stencil and integer format does not support filtering. */
+	BLI_assert((!use_filter && !use_mipmap) || !(GPU_texture_stencil(tex) || GPU_texture_integer(tex)));
 
+	GLenum filter = (use_filter) ? GL_LINEAR : GL_NEAREST;
 	GLenum mipmap = (use_filter)
-	       ? use_mipmap ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR
-	       : use_mipmap ? GL_NEAREST_MIPMAP_LINEAR : GL_NEAREST;
-	glTexParameteri(tex->target_base, GL_TEXTURE_MIN_FILTER, mipmap);
+	       ? (use_mipmap) ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR
+	       : (use_mipmap) ? GL_NEAREST_MIPMAP_LINEAR : GL_NEAREST;
 
-	GLenum filter = use_filter ? GL_LINEAR : GL_NEAREST;
+	glActiveTexture(GL_TEXTURE0 + tex->number);
+	glTexParameteri(tex->target_base, GL_TEXTURE_MIN_FILTER, mipmap);
 	glTexParameteri(tex->target_base, GL_TEXTURE_MAG_FILTER, filter);
-
-	if (tex->number != 0)
-		glActiveTexture(GL_TEXTURE0);
 }
 
 void GPU_texture_wrap_mode(GPUTexture *tex, bool use_repeat)
 {
-	if (tex->number >= GPU_max_textures()) {
-		fprintf(stderr, "Not enough texture slots.\n");
-		return;
-	}
-
-	if (tex->number == -1)
-		return;
+	WARN_NOT_BOUND(tex);
 
-	if (tex->number != 0)
-		glActiveTexture(GL_TEXTURE0 + tex->number);
+	GLenum repeat = (use_repeat) ? GL_REPEAT : GL_CLAMP_TO_EDGE;
 
-	GLenum repeat = use_repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE;
+	glActiveTexture(GL_TEXTURE0 + tex->number);
 	glTexParameteri(tex->target_base, GL_TEXTURE_WRAP_S, repeat);
 	if (tex->target_base != GL_TEXTURE_1D)
 		glTexParameteri(tex->target_base, GL_TEXTURE_WRAP_T, repeat);
 	if (tex->target_base == GL_TEXTURE_3D)
 		glTexParameteri(tex->target_base, GL_TEXTURE_WRAP_R, repeat);
-
-	if (tex->number != 0)
-		glActiveTexture(GL_TEXTURE0);
 }
 
 void GPU_texture_free(GPUTexture *tex)
@@ -984,8 +968,12 @@ void GPU_texture_free(GPUTexture *tex)
 		fprintf(stderr, "GPUTexture: negative refcount\n");
 	
 	if (tex->refcount == 0) {
-		if (tex->fb)
-			GPU_framebuffer_texture_detach(tex);
+		for (int i = 0; i < GPU_TEX_MAX_FBO_ATTACHED; ++i) {
+			if (tex->fb[i] != NULL) {
+				GPU_framebuffer_texture_detach_slot(tex->fb[i], tex, tex->fb_attachment[i]);
+			}
+		}
+
 		if (tex->bindcode && !tex->fromblender)
 			glDeleteTextures(1, &tex->bindcode);
 
@@ -1015,7 +1003,7 @@ int GPU_texture_height(const GPUTexture *tex)
 	return tex->h;
 }
 
-int GPU_texture_format(const GPUTexture *tex)
+GPUTextureFormat GPU_texture_format(const GPUTexture *tex)
 {
 	return tex->format;
 }
@@ -1027,32 +1015,52 @@ int GPU_texture_samples(const GPUTexture *tex)
 
 bool GPU_texture_depth(const GPUTexture *tex)
 {
-	return tex->depth;
+	return (tex->format_flag & GPU_FORMAT_DEPTH) != 0;
 }
 
 bool GPU_texture_stencil(const GPUTexture *tex)
 {
-	return tex->stencil;
+	return (tex->format_flag & GPU_FORMAT_STENCIL) != 0;
 }
 
-int GPU_texture_opengl_bindcode(const GPUTexture *tex)
+bool GPU_texture_integer(const GPUTexture *tex)
 {
-	return tex->bindcode;
+	return (tex->format_flag & GPU_FORMAT_INTEGER) != 0;
 }
 
-GPUFrameBuffer *GPU_texture_framebuffer(GPUTexture *tex)
+bool GPU_texture_cube(const GPUTexture *tex)
 {
-	return tex->fb;
+	return (tex->format_flag & GPU_FORMAT_CUBE) != 0;
 }
 
-int GPU_texture_framebuffer_attachment(GPUTexture *tex)
+int GPU_texture_opengl_bindcode(const GPUTexture *tex)
 {
-	return tex->fb_attachment;
+	return tex->bindcode;
 }
 
-void GPU_texture_framebuffer_set(GPUTexture *tex, GPUFrameBuffer *fb, int attachment)
+void GPU_texture_attach_framebuffer(GPUTexture *tex, GPUFrameBuffer *fb, int attachment)
 {
-	tex->fb = fb;
-	tex->fb_attachment = attachment;
+	for (int i = 0; i < GPU_TEX_MAX_FBO_ATTACHED; ++i) {
+		if (tex->fb[i] == NULL) {
+			tex->fb[i] = fb;
+			tex->fb_attachment[i] = attachment;
+			return;
+		}
+	}
+
+	BLI_assert(!"Error: Texture: Not enough Framebuffer slots");
 }
 
+/* Return previous attachment point */
+int GPU_texture_detach_framebuffer(GPUTexture *tex, GPUFrameBuffer *fb)
+{
+	for (int i = 0; i < GPU_TEX_MAX_FBO_ATTACHED; ++i) {
+		if (tex->fb[i] == fb) {
+			tex->fb[i] = NULL;
+			return tex->fb_attachment[i];
+		}
+	}
+
+	BLI_assert(!"Error: Texture: Framebuffer is not attached");
+	return 0;
+}
diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c
index 2ad89bd1345d5527c7caed916c9c1d7d194be987..5d89bd59277a765c159843c6ca74adf0c06849a0 100644
--- a/source/blender/gpu/intern/gpu_viewport.c
+++ b/source/blender/gpu/intern/gpu_viewport.c
@@ -48,6 +48,7 @@
 #include "GPU_immediate.h"
 #include "GPU_texture.h"
 #include "GPU_viewport.h"
+#include "GPU_draw.h"
 
 #include "DRW_engine.h"
 
@@ -68,12 +69,7 @@ typedef struct ViewportTempTexture {
 } ViewportTempTexture;
 
 struct GPUViewport {
-	float pad[4];
-
-	/* debug */
-	GPUTexture *debug_depth;
 	int size[2];
-
 	int samples;
 	int flag;
 
@@ -141,12 +137,19 @@ GPUViewport *GPU_viewport_create_from_offscreen(struct GPUOffScreen *ofs)
 		viewport->txl->multisample_depth = depth;
 		viewport->fbl->multisample_fb = fb;
 		gpu_viewport_default_fb_create(viewport);
-		GPU_framebuffer_slots_bind(viewport->fbl->default_fb, 0);
 	}
 	else {
 		viewport->fbl->default_fb = fb;
 		viewport->txl->color = color;
 		viewport->txl->depth = depth;
+		GPU_framebuffer_ensure_config(&viewport->fbl->color_only_fb, {
+			GPU_ATTACHMENT_NONE,
+			GPU_ATTACHMENT_TEXTURE(viewport->txl->color)
+		});
+		GPU_framebuffer_ensure_config(&viewport->fbl->depth_only_fb, {
+			GPU_ATTACHMENT_TEXTURE(viewport->txl->depth),
+			GPU_ATTACHMENT_NONE
+		});
 	}
 
 	return viewport;
@@ -162,8 +165,7 @@ void GPU_viewport_clear_from_offscreen(GPUViewport *viewport)
 	if (dfbl->multisample_fb) {
 		/* GPUViewport expect the final result to be in default_fb but
 		 * GPUOffscreen wants it in its multisample_fb, so we sync it back. */
-		GPU_framebuffer_blit(dfbl->default_fb, 0, dfbl->multisample_fb, 0, false, false);
-		GPU_framebuffer_blit(dfbl->default_fb, 0, dfbl->multisample_fb, 0, true, false);
+		GPU_framebuffer_blit(dfbl->default_fb, 0, dfbl->multisample_fb, 0, GPU_COLOR_BIT | GPU_DEPTH_BIT);
 		dfbl->multisample_fb = NULL;
 		dtxl->multisample_color = NULL;
 		dtxl->multisample_depth = NULL;
@@ -292,9 +294,9 @@ GPUTexture *GPU_viewport_texture_pool_query(GPUViewport *viewport, void *engine,
 	GPUTexture *tex;
 
 	for (ViewportTempTexture *tmp_tex = viewport->tex_pool.first; tmp_tex; tmp_tex = tmp_tex->next) {
-		if ((GPU_texture_width(tmp_tex->texture) == width) &&
-		    (GPU_texture_height(tmp_tex->texture) == height) &&
-		    (GPU_texture_format(tmp_tex->texture) == format))
+		if ((GPU_texture_format(tmp_tex->texture) == format) &&
+		    (GPU_texture_width(tmp_tex->texture) == width) &&
+		    (GPU_texture_height(tmp_tex->texture) == height))
 		{
 			/* Search if the engine is not already using this texture */
 			for (int i = 0; i < MAX_ENGINE_BUFFER_SHARING; ++i) {
@@ -311,11 +313,16 @@ GPUTexture *GPU_viewport_texture_pool_query(GPUViewport *viewport, void *engine,
 	}
 
 	tex = GPU_texture_create_2D_custom(width, height, channels, format, NULL, NULL);
+	GPU_texture_bind(tex, 0);
+	/* Doing filtering for depth does not make sense when not doing shadow mapping,
+	 * and enabling texture filtering on integer texture make them unreadable. */
+	bool do_filter = !GPU_texture_depth(tex) && !GPU_texture_integer(tex);
+	GPU_texture_filter_mode(tex, do_filter);
+	GPU_texture_unbind(tex);
 
 	ViewportTempTexture *tmp_tex = MEM_callocN(sizeof(ViewportTempTexture), "ViewportTempTexture");
 	tmp_tex->texture = tex;
 	tmp_tex->user[0] = engine;
-
 	BLI_addtail(&viewport->tex_pool, tmp_tex);
 
 	return tex;
@@ -382,48 +389,66 @@ static void gpu_viewport_default_fb_create(GPUViewport *viewport)
 	int *size = viewport->size;
 	bool ok = true;
 
-	dfbl->default_fb = GPU_framebuffer_create();
-	if (!dfbl->default_fb) {
-		ok = false;
-		goto cleanup;
-	}
-
-	/* Color */
 	dtxl->color = GPU_texture_create_2D(size[0], size[1], NULL, NULL);
-	if (!dtxl->color) {
-		ok = false;
-		goto cleanup;
-	}
+	dtxl->depth = GPU_texture_create_depth_with_stencil(size[0], size[1], NULL);
 
-	if (!GPU_framebuffer_texture_attach(dfbl->default_fb, dtxl->color, 0, 0)) {
+	if (!(dtxl->depth && dtxl->color)) {
 		ok = false;
 		goto cleanup;
 	}
 
-	/* Depth */
-	dtxl->depth = GPU_texture_create_depth_with_stencil(size[0], size[1], NULL);
+	GPU_framebuffer_ensure_config(&dfbl->default_fb, {
+		GPU_ATTACHMENT_TEXTURE(dtxl->depth),
+		GPU_ATTACHMENT_TEXTURE(dtxl->color)
+	});
 
-	if (dtxl->depth) {
-		/* Define texture parameters */
-		GPU_texture_bind(dtxl->depth, 0);
-		GPU_texture_compare_mode(dtxl->depth, false);
-		GPU_texture_filter_mode(dtxl->depth, true);
-		GPU_texture_unbind(dtxl->depth);
-	}
-	else {
-		ok = false;
-		goto cleanup;
-	}
+	GPU_framebuffer_ensure_config(&dfbl->depth_only_fb, {
+		GPU_ATTACHMENT_TEXTURE(dtxl->depth),
+		GPU_ATTACHMENT_NONE
+	});
 
-	if (!GPU_framebuffer_texture_attach(dfbl->default_fb, dtxl->depth, 0, 0)) {
-		ok = false;
-		goto cleanup;
+	GPU_framebuffer_ensure_config(&dfbl->color_only_fb, {
+		GPU_ATTACHMENT_NONE,
+		GPU_ATTACHMENT_TEXTURE(dtxl->color)
+	});
+
+	ok = ok && GPU_framebuffer_check_valid(dfbl->default_fb, NULL);
+	ok = ok && GPU_framebuffer_check_valid(dfbl->color_only_fb, NULL);
+	ok = ok && GPU_framebuffer_check_valid(dfbl->depth_only_fb, NULL);
+
+cleanup:
+	if (!ok) {
+		GPU_viewport_free(viewport);
+		DRW_opengl_context_disable();
+		return;
 	}
-	else if (!GPU_framebuffer_check_valid(dfbl->default_fb, NULL)) {
+
+	GPU_framebuffer_restore();
+}
+
+static void gpu_viewport_default_multisample_fb_create(GPUViewport *viewport)
+{
+	DefaultFramebufferList *dfbl = viewport->fbl;
+	DefaultTextureList *dtxl = viewport->txl;
+	int *size = viewport->size;
+	int samples = viewport->samples;
+	bool ok = true;
+
+	dtxl->multisample_color = GPU_texture_create_2D_multisample(size[0], size[1], NULL, samples, NULL);
+	dtxl->multisample_depth = GPU_texture_create_depth_with_stencil_multisample(size[0], size[1], samples, NULL);
+
+	if (!(dtxl->multisample_depth && dtxl->multisample_color)) {
 		ok = false;
 		goto cleanup;
 	}
 
+	GPU_framebuffer_ensure_config(&dfbl->multisample_fb, {
+		GPU_ATTACHMENT_TEXTURE(dtxl->multisample_depth),
+		GPU_ATTACHMENT_TEXTURE(dtxl->multisample_color)
+	});
+
+	ok = ok && GPU_framebuffer_check_valid(dfbl->multisample_fb, NULL);
+
 cleanup:
 	if (!ok) {
 		GPU_viewport_free(viewport);
@@ -437,7 +462,6 @@ cleanup:
 void GPU_viewport_bind(GPUViewport *viewport, const rcti *rect)
 {
 	DefaultFramebufferList *dfbl = viewport->fbl;
-	DefaultTextureList *dtxl = viewport->txl;
 	int fbl_len, txl_len;
 
 	/* add one pixel because of scissor test */
@@ -464,68 +488,29 @@ void GPU_viewport_bind(GPUViewport *viewport, const rcti *rect)
 
 	viewport->size[0] = rect_w;
 	viewport->size[1] = rect_h;
+	viewport->samples = U.ogl_multisamples;
 
 	gpu_viewport_texture_pool_clear_users(viewport);
 
 	/* Multisample Buffer */
-	if (U.ogl_multisamples > 0) {
+	if (viewport->samples > 0) {
 		if (!dfbl->default_fb) {
-			bool ok = true;
-			viewport->samples = U.ogl_multisamples;
-
-			dfbl->multisample_fb = GPU_framebuffer_create();
-			if (!dfbl->multisample_fb) {
-				ok = false;
-				goto cleanup_multisample;
-			}
-
-			/* Color */
-			dtxl->multisample_color = GPU_texture_create_2D_multisample(rect_w, rect_h, NULL, U.ogl_multisamples, NULL);
-			if (!dtxl->multisample_color) {
-				ok = false;
-				goto cleanup_multisample;
-			}
-
-			if (!GPU_framebuffer_texture_attach(dfbl->multisample_fb, dtxl->multisample_color, 0, 0)) {
-				ok = false;
-				goto cleanup_multisample;
-			}
-
-			/* Depth */
-			dtxl->multisample_depth = GPU_texture_create_depth_with_stencil_multisample(rect_w, rect_h,
-				                                                                        U.ogl_multisamples, NULL);
-
-			if (!dtxl->multisample_depth) {
-				ok = false;
-				goto cleanup_multisample;
-			}
-
-			if (!GPU_framebuffer_texture_attach(dfbl->multisample_fb, dtxl->multisample_depth, 0, 0)) {
-				ok = false;
-				goto cleanup_multisample;
-			}
-			else if (!GPU_framebuffer_check_valid(dfbl->multisample_fb, NULL)) {
-				ok = false;
-				goto cleanup_multisample;
-			}
-
-cleanup_multisample:
-			if (!ok) {
-				GPU_viewport_free(viewport);
-				return;
-			}
+			gpu_viewport_default_multisample_fb_create(viewport);
 		}
 	}
 
 	if (!dfbl->default_fb) {
 		gpu_viewport_default_fb_create(viewport);
 	}
-
-	GPU_framebuffer_slots_bind(dfbl->default_fb, 0);
 }
 
-static void draw_ofs_to_screen(GPUViewport *viewport, const rcti *rect)
+void GPU_viewport_draw_to_screen(GPUViewport *viewport, const rcti *rect)
 {
+	DefaultFramebufferList *dfbl = viewport->fbl;
+
+	if (dfbl->default_fb == NULL)
+		return;
+
 	DefaultTextureList *dtxl = viewport->txl;
 
 	GPUTexture *color = dtxl->color;
@@ -536,61 +521,28 @@ static void draw_ofs_to_screen(GPUViewport *viewport, const rcti *rect)
 	BLI_assert(w == BLI_rcti_size_x(rect) + 1);
 	BLI_assert(h == BLI_rcti_size_y(rect) + 1);
 
-	Gwn_VertFormat *format = immVertexFormat();
-	unsigned int texcoord = GWN_vertformat_attr_add(format, "texCoord", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
-	unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
+	float x1 = rect->xmin;
+	float x2 = rect->xmin + w;
+	float y1 = rect->ymin;
+	float y2 = rect->ymin + h;
 
-	immBindBuiltinProgram(GPU_SHADER_3D_IMAGE_MODULATE_ALPHA);
-	GPU_texture_bind(color, 0);
-
-	immUniform1i("image", 0); /* default GL_TEXTURE0 unit */
-
-	immBegin(GWN_PRIM_TRI_STRIP, 4);
-
-	immAttrib2f(texcoord, 0.0f, 0.0f);
-	immVertex2f(pos, rect->xmin, rect->ymin);
+	GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_IMAGE_RECT_COLOR);
+	GPU_shader_bind(shader);
 
-	immAttrib2f(texcoord, 1.0f, 0.0f);
-	immVertex2f(pos, rect->xmin + w, rect->ymin);
-
-	immAttrib2f(texcoord, 0.0f, 1.0f);
-	immVertex2f(pos, rect->xmin, rect->ymin + h);
-
-	immAttrib2f(texcoord, 1.0f, 1.0f);
-	immVertex2f(pos, rect->xmin + w, rect->ymin + h);
+	GPU_texture_bind(color, 0);
+	glUniform1i(GPU_shader_get_uniform(shader, "image"), 0);
+	glUniform4f(GPU_shader_get_uniform(shader, "rect_icon"), 0.0f, 0.0f, 1.0f, 1.0f);
+	glUniform4f(GPU_shader_get_uniform(shader, "rect_geom"), x1, y1, x2, y2);
+	glUniform4f(GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_COLOR), 1.0f, 1.0f, 1.0f, 1.0f);
 
-	immEnd();
+	GWN_draw_primitive(GWN_PRIM_TRI_STRIP, 4);
 
 	GPU_texture_unbind(color);
-
-	immUnbindProgram();
 }
 
-void GPU_viewport_unbind(GPUViewport *viewport)
+void GPU_viewport_unbind(GPUViewport *UNUSED(viewport))
 {
-	DefaultFramebufferList *dfbl = viewport->fbl;
-
-	if (dfbl->default_fb) {
-		GPU_framebuffer_texture_unbind(NULL, NULL);
-		GPU_framebuffer_restore();
-	}
-
 	DRW_opengl_context_disable();
-
-	if (dfbl->default_fb) {
-		glEnable(GL_SCISSOR_TEST);
-		glDisable(GL_DEPTH_TEST);
-	}
-}
-
-void GPU_viewport_draw_to_screen(GPUViewport *viewport, const rcti *rect)
-{
-	DefaultFramebufferList *dfbl = viewport->fbl;
-
-	if (dfbl->default_fb) {
-		/* This might be bandwidth limiting */
-		draw_ofs_to_screen(viewport, rect);
-	}
 }
 
 static void gpu_viewport_buffers_free(
@@ -668,86 +620,5 @@ void GPU_viewport_free(GPUViewport *viewport)
 	DRW_instance_data_list_free(viewport->idatalist);
 	MEM_freeN(viewport->idatalist);
 
-	GPU_viewport_debug_depth_free(viewport);
-
 	MEM_freeN(viewport);
 }
-
-/****************** debug ********************/
-
-bool GPU_viewport_debug_depth_create(GPUViewport *viewport, int width, int height, char err_out[256])
-{
-	viewport->debug_depth = GPU_texture_create_2D_custom(width, height, 4, GPU_RGBA16F, NULL, err_out);
-	return (viewport->debug_depth != NULL);
-}
-
-void GPU_viewport_debug_depth_free(GPUViewport *viewport)
-{
-	if (viewport->debug_depth != NULL) {
-		MEM_freeN(viewport->debug_depth);
-		viewport->debug_depth = NULL;
-	}
-}
-
-void GPU_viewport_debug_depth_store(GPUViewport *viewport, const int x, const int y)
-{
-	const int w = GPU_texture_width(viewport->debug_depth);
-	const int h = GPU_texture_height(viewport->debug_depth);
-
-	GPU_texture_bind(viewport->debug_depth, 0);
-	glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, x, y, w, h, 0);
-	GPU_texture_unbind(viewport->debug_depth);
-}
-
-void GPU_viewport_debug_depth_draw(GPUViewport *viewport, const float znear, const float zfar)
-{
-	const float w = (float)GPU_texture_width(viewport->debug_depth);
-	const float h = (float)GPU_texture_height(viewport->debug_depth);
-
-	Gwn_VertFormat *format = immVertexFormat();
-	unsigned int texcoord = GWN_vertformat_attr_add(format, "texCoord", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
-	unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
-
-	immBindBuiltinProgram(GPU_SHADER_3D_IMAGE_DEPTH);
-
-	GPU_texture_bind(viewport->debug_depth, 0);
-
-	immUniform1f("znear", znear);
-	immUniform1f("zfar", zfar);
-	immUniform1i("image", 0); /* default GL_TEXTURE0 unit */
-
-	immBegin(GWN_PRIM_TRI_STRIP, 4);
-
-	immAttrib2f(texcoord, 0.0f, 0.0f);
-	immVertex2f(pos, 0.0f, 0.0f);
-
-	immAttrib2f(texcoord, 1.0f, 0.0f);
-	immVertex2f(pos, w, 0.0f);
-
-	immAttrib2f(texcoord, 0.0f, 1.0f);
-	immVertex2f(pos, 0.0f, h);
-
-	immAttrib2f(texcoord, 1.0f, 1.0f);
-	immVertex2f(pos, w, h);
-
-	immEnd();
-
-	GPU_texture_unbind(viewport->debug_depth);
-
-	immUnbindProgram();
-}
-
-int GPU_viewport_debug_depth_width(const GPUViewport *viewport)
-{
-	return GPU_texture_width(viewport->debug_depth);
-}
-
-int GPU_viewport_debug_depth_height(const GPUViewport *viewport)
-{
-	return GPU_texture_height(viewport->debug_depth);
-}
-
-bool GPU_viewport_debug_depth_is_valid(GPUViewport *viewport)
-{
-	return viewport->debug_depth != NULL;
-}
diff --git a/source/blender/gpu/shaders/gpu_shader_2D_image_multi_rect_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_image_multi_rect_vert.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..9fdf8ececc530340dd1ce2c73514e38f2695ab74
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_2D_image_multi_rect_vert.glsl
@@ -0,0 +1,48 @@
+/**
+ * Simple shader that just draw multiple icons at the specified locations
+ * does not need any vertex input (producing less call to immBegin/End)
+ **/
+
+/* Same as ICON_DRAW_CACHE_SIZE */
+#define MAX_CALLS 16
+
+uniform vec4 calls_data[MAX_CALLS * 3];
+
+out vec2 texCoord_interp;
+flat out vec4 finalColor;
+
+void main()
+{
+	/* Rendering 2 triangle per icon. */
+	int i = gl_VertexID / 6;
+	int v = gl_VertexID % 6;
+
+	vec4 pos = calls_data[i*3];
+	vec4 tex = calls_data[i*3+1];
+	finalColor = calls_data[i*3+2];
+
+	/* TODO Remove this */
+	if (v == 2) v = 4;
+	else if (v == 3) v = 0;
+	else if (v == 5) v = 2;
+
+	if (v == 0) {
+		pos.xy = pos.xw;
+		tex.xy = tex.xw;
+	}
+	else if (v == 1) {
+		pos.xy = pos.xz;
+		tex.xy = tex.xz;
+	}
+	else if (v == 2) {
+		pos.xy = pos.yw;
+		tex.xy = tex.yw;
+	}
+	else {
+		pos.xy = pos.yz;
+		tex.xy = tex.yz;
+	}
+
+	gl_Position = vec4(pos.xy, 0.0f, 1.0f);
+	texCoord_interp = tex.xy;
+}
diff --git a/source/blender/gpu/shaders/gpu_shader_2D_image_rect_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_image_rect_vert.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..118f4e3b187c77b4ea0632cbddf54bf1d3c77f9a
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_2D_image_rect_vert.glsl
@@ -0,0 +1,35 @@
+/**
+ * Simple shader that just draw one icon at the specified location
+ * does not need any vertex input (producing less call to immBegin/End)
+ **/
+
+uniform mat4 ModelViewProjectionMatrix;
+uniform vec4 rect_icon;
+uniform vec4 rect_geom;
+
+out vec2 texCoord_interp;
+
+void main()
+{
+	vec2 uv;
+	vec2 co;
+	if (gl_VertexID == 0) {
+		co = rect_geom.xw;
+		uv = rect_icon.xw;
+	}
+	else if (gl_VertexID == 1) {
+		co = rect_geom.xy;
+		uv = rect_icon.xy;
+	}
+	else if (gl_VertexID == 2) {
+		co = rect_geom.zw;
+		uv = rect_icon.zw;
+	}
+	else {
+		co = rect_geom.zy;
+		uv = rect_icon.zy;
+	}
+
+	gl_Position = ModelViewProjectionMatrix * vec4(co, 0.0f, 1.0f);
+	texCoord_interp = uv;
+}
diff --git a/source/blender/gpu/shaders/gpu_shader_2D_nodelink_frag.glsl b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_frag.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..8dda575107a05b2aebe148b2ef59da47ca20dfb3
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_frag.glsl
@@ -0,0 +1,10 @@
+
+in float colorGradient;
+in vec4 finalColor;
+
+out vec4 fragColor;
+
+void main() {
+	fragColor = finalColor;
+	fragColor.a *= smoothstep(1.0, 0.1, abs(colorGradient));
+}
\ No newline at end of file
diff --git a/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..4c295fcd72a6665593300880496608b71f06c86c
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl
@@ -0,0 +1,107 @@
+/**
+ * 2D Quadratic Bezier thick line drawing
+ **/
+
+#define MID_VERTEX 57
+
+/* u is position along the curve, defining the tangent space.
+ * v is "signed" distance (compressed to [0..1] range) from the pos in expand direction */
+in vec2 uv;
+in vec2 pos; /* verts position in the curve tangent space */
+in vec2 expand;
+
+#ifdef USE_INSTANCE
+/* Instance attrib */
+in vec2 P0;
+in vec2 P1;
+in vec2 P2;
+in vec2 P3;
+in ivec4 colid_doarrow;
+
+uniform vec4 colors[6];
+
+#define colStart   colors[colid_doarrow[0]]
+#define colEnd     colors[colid_doarrow[1]]
+#define colShadow  colors[colid_doarrow[2]]
+#define doArrow    (colid_doarrow[3] != 0)
+
+#else
+/* Single curve drawcall, use uniform. */
+uniform vec2 bezierPts[4];
+
+#define P0 bezierPts[0]
+#define P1 bezierPts[1]
+#define P2 bezierPts[2]
+#define P3 bezierPts[3]
+
+uniform vec4 colors[3];
+uniform bool doArrow;
+
+#define colShadow  colors[0]
+#define colStart   colors[1]
+#define colEnd     colors[2]
+
+#endif
+
+uniform float expandSize;
+uniform float arrowSize;
+uniform mat4 ModelViewProjectionMatrix;
+
+out float colorGradient;
+out vec4 finalColor;
+
+void main(void)
+{
+	float t = uv.x;
+	float t2 = t * t;
+	float t2_3 = 3.0 * t2;
+	float one_minus_t = 1.0 - t;
+	float one_minus_t2 = one_minus_t * one_minus_t;
+	float one_minus_t2_3 = 3.0 * one_minus_t2;
+
+	vec2 point = (P0 * one_minus_t2 * one_minus_t +
+	              P1 * one_minus_t2_3 * t +
+	              P2 * t2_3 * one_minus_t +
+	              P3 * t2 * t);
+
+	vec2 tangent = ((P1 - P0) * one_minus_t2_3 +
+	                (P2 - P1) * 6.0 * (t - t2) +
+	                (P3 - P2) * t2_3);
+
+	/* tangent space at t */
+	tangent = normalize(tangent);
+	vec2 normal = tangent.yx * vec2(-1.0, 1.0);
+
+	/* Position vertex on the curve tangent space */
+	point += (pos.x * tangent + pos.y * normal) * arrowSize;
+
+	gl_Position = ModelViewProjectionMatrix * vec4(point, 0.0, 1.0);
+
+	vec2 exp_axis = expand.x * tangent + expand.y * normal;
+
+	/* rotate & scale the expand axis */
+	exp_axis = ModelViewProjectionMatrix[0].xy * exp_axis.xx +
+	           ModelViewProjectionMatrix[1].xy * exp_axis.yy;
+
+
+	float expand_dist = (uv.y * 2.0 - 1.0);
+	colorGradient = expand_dist;
+
+	if (gl_VertexID < MID_VERTEX) {
+		/* Shadow pass */
+		finalColor = colShadow;
+	}
+	else {
+		/* Second pass */
+		finalColor = mix(colStart, colEnd, uv.x);
+		expand_dist *= 0.5;
+	}
+
+	/* Expand into a line */
+	gl_Position.xy += exp_axis * expandSize * expand_dist;
+
+	/* if arrow */
+	if (expand.y != 1.0 && !doArrow) {
+		gl_Position.xy *= 0.0;
+	}
+}
\ No newline at end of file
diff --git a/source/blender/gpu/shaders/gpu_shader_2D_widget_base_frag.glsl b/source/blender/gpu/shaders/gpu_shader_2D_widget_base_frag.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..929674ab446fe733c5d4d1c87dcebcb66989124c
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_2D_widget_base_frag.glsl
@@ -0,0 +1,34 @@
+uniform vec3 checkerColorAndSize;
+
+noperspective in vec4 finalColor;
+noperspective in float butCo;
+
+out vec4 fragColor;
+
+vec4 do_checkerboard()
+{
+	float size = checkerColorAndSize.z;
+	vec2 phase = mod(gl_FragCoord.xy, size * 2.0);
+
+	if ((phase.x > size && phase.y < size) ||
+		(phase.x < size && phase.y > size))
+	{
+		return vec4(checkerColorAndSize.xxx, 1.0);
+	}
+	else {
+		return vec4(checkerColorAndSize.yyy, 1.0);
+	}
+}
+
+void main()
+{
+	fragColor = finalColor;
+
+	if (butCo > 0.5) {
+		fragColor = mix(do_checkerboard(), fragColor, fragColor.a);
+	}
+
+	if (butCo > 0.0) {
+		fragColor.a = 1.0;
+	}
+}
\ No newline at end of file
diff --git a/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..9f28d9bb3a381602895bd3aa487dd6ba80cf5d54
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl
@@ -0,0 +1,186 @@
+#define BIT_RANGE(x) ((1u << x) - 1u)
+
+/* 2 bits for corner */
+/* Attention! Not the same order as in UI_interface.h!
+ * Ordered by drawing order. */
+#define BOTTOM_LEFT 0u
+#define BOTTOM_RIGHT 1u
+#define TOP_RIGHT 2u
+#define TOP_LEFT 3u
+#define CNR_FLAG_RANGE BIT_RANGE(2u)
+
+/* 4bits for corner id */
+#define CORNER_VEC_OFS 2u
+#define CORNER_VEC_RANGE BIT_RANGE(4u)
+const vec2 cornervec[36] = vec2[36](
+	vec2(0.0, 1.0), vec2(0.02, 0.805), vec2(0.067, 0.617), vec2(0.169, 0.45), vec2(0.293, 0.293), vec2(0.45, 0.169), vec2(0.617, 0.076), vec2(0.805, 0.02), vec2(1.0, 0.0),
+	vec2(-1.0, 0.0), vec2(-0.805, 0.02), vec2(-0.617, 0.067), vec2(-0.45, 0.169), vec2(-0.293, 0.293), vec2(-0.169, 0.45), vec2(-0.076, 0.617), vec2(-0.02, 0.805), vec2(0.0, 1.0),
+	vec2(0.0, -1.0), vec2(-0.02, -0.805), vec2(-0.067, -0.617), vec2(-0.169, -0.45), vec2(-0.293, -0.293), vec2(-0.45, -0.169), vec2(-0.617, -0.076), vec2(-0.805, -0.02), vec2(-1.0, 0.0),
+	vec2(1.0, 0.0), vec2(0.805, -0.02), vec2(0.617, -0.067), vec2(0.45, -0.169), vec2(0.293, -0.293), vec2(0.169, -0.45), vec2(0.076, -0.617), vec2(0.02, -0.805), vec2(0.0, -1.0)
+);
+
+/* 4bits for jitter id */
+#define JIT_OFS 6u
+#define JIT_RANGE BIT_RANGE(4u)
+const vec2 jit[9] = vec2[9](
+	vec2( 0.468813, -0.481430), vec2(-0.155755, -0.352820),
+	vec2( 0.219306, -0.238501), vec2(-0.393286, -0.110949),
+	vec2(-0.024699,  0.013908), vec2( 0.343805,  0.147431),
+	vec2(-0.272855,  0.269918), vec2( 0.095909,  0.388710),
+	vec2( 0.0,  0.0)
+);
+
+/* 2bits for other flags */
+#define INNER_FLAG     (1u << 10u) /* is inner vert */
+#define EMBOSS_FLAG    (1u << 11u) /* is emboss vert */
+
+/* 2bits for color */
+#define COLOR_OFS 12u
+#define COLOR_RANGE BIT_RANGE(2u)
+#define COLOR_INNER    0u
+#define COLOR_EDGE     1u
+#define COLOR_EMBOSS   2u
+
+/* 2bits for trias type */
+#define TRIA_FLAG      (1u << 14u) /* is tria vert */
+#define TRIA_FIRST     INNER_FLAG  /* is first tria (reuse INNER_FLAG) */
+
+/* We can reuse the CORNER_* bits for tria */
+#define TRIA_VEC_RANGE BIT_RANGE(6u)
+const vec2 triavec[34] = vec2[34](
+	/* horizontal tria */
+	vec2(-0.352077, 0.532607), vec2(-0.352077, -0.549313), vec2( 0.330000, -0.008353),
+	vec2( 0.352077, 0.532607), vec2( 0.352077, -0.549313), vec2(-0.330000, -0.008353),
+	/* circle tria (triangle strip) */
+	vec2(0.000000, 1.000000),
+	vec2(0.382684, 0.923879), vec2(-0.382683, 0.923880),
+	vec2(0.707107, 0.707107), vec2(-0.707107, 0.707107),
+	vec2(0.923879, 0.382684), vec2(-0.923879, 0.382684),
+	vec2(1.000000, 0.000000), vec2(-1.000000, 0.000000),
+	vec2(0.923879, -0.382684), vec2(-0.923879, -0.382684),
+	vec2(0.707107, -0.707107), vec2(-0.707107, -0.707107),
+	vec2(0.382684, -0.923879), vec2(-0.382683, -0.923880),
+	vec2(0.000000, -1.000000),
+	/* menu arrow */
+	vec2(-0.33, 0.16), vec2(0.33, 0.16), vec2(0.0, 0.82),
+	vec2(0.0, -0.82), vec2(-0.33, -0.16), vec2(0.33, -0.16),
+	/* check mark */
+	vec2(-0.578579, 0.253369),  vec2(-0.392773, 0.412794),  vec2(-0.004241, -0.328551),
+	vec2(-0.003001, 0.034320),  vec2(1.055313, 0.864744),   vec2(0.866408, 1.026895)
+);
+
+uniform mat4 ModelViewProjectionMatrix;
+
+#ifdef USE_INSTANCE
+#define MAX_INSTANCE 6
+uniform vec4 parameters[11 * MAX_INSTANCE];
+#else
+uniform vec4 parameters[11];
+#endif
+
+/* gl_InstanceID is 0 if not drawing instances. */
+#define recti        parameters[gl_InstanceID * 11 + 0]
+#define rect         parameters[gl_InstanceID * 11 + 1]
+#define radsi        parameters[gl_InstanceID * 11 + 2].x
+#define rads         parameters[gl_InstanceID * 11 + 2].y
+#define faci         parameters[gl_InstanceID * 11 + 2].zw
+#define roundCorners parameters[gl_InstanceID * 11 + 3]
+#define colorInner1  parameters[gl_InstanceID * 11 + 4]
+#define colorInner2  parameters[gl_InstanceID * 11 + 5]
+#define colorEdge    parameters[gl_InstanceID * 11 + 6]
+#define colorEmboss  parameters[gl_InstanceID * 11 + 7]
+#define colorTria    parameters[gl_InstanceID * 11 + 8]
+#define tria1Center  parameters[gl_InstanceID * 11 + 9].xy
+#define tria2Center  parameters[gl_InstanceID * 11 + 9].zw
+#define tria1Size    parameters[gl_InstanceID * 11 + 10].x
+#define tria2Size    parameters[gl_InstanceID * 11 + 10].y
+#define shadeDir     parameters[gl_InstanceID * 11 + 10].z
+#define doAlphaCheck parameters[gl_InstanceID * 11 + 10].w
+
+in uint vflag;
+
+noperspective out vec4 finalColor;
+noperspective out float butCo;
+
+vec2 do_widget(void)
+{
+	uint cflag = vflag & CNR_FLAG_RANGE;
+	uint vofs = (vflag >> CORNER_VEC_OFS) & CORNER_VEC_RANGE;
+
+	vec2 v = cornervec[cflag * 9u + vofs];
+
+	bool is_inner = (vflag & INNER_FLAG) != 0u;
+
+	/* Scale by corner radius */
+	v *= roundCorners[cflag] * ((is_inner) ? radsi : rads);
+
+	/* Position to corner */
+	vec4 rct = (is_inner) ? recti : rect;
+	if (cflag == BOTTOM_LEFT)
+		v += rct.xz;
+	else if (cflag == BOTTOM_RIGHT)
+		v += rct.yz;
+	else if (cflag == TOP_RIGHT)
+		v += rct.yw;
+	else /* (cflag == TOP_LEFT) */
+		v += rct.xw;
+
+	/* compute uv and color gradient */
+	uint color_id = (vflag >> COLOR_OFS) & COLOR_RANGE;
+	if (color_id == COLOR_INNER) {
+		vec2 uv = faci * (v - recti.xz);
+		float fac = clamp((shadeDir > 0.0) ? uv.y : uv.x, 0.0, 1.0);
+		if (doAlphaCheck != 0.0) {
+			finalColor = colorInner1;
+			butCo = uv.x;
+		}
+		else {
+			finalColor = mix(colorInner2, colorInner1, fac);
+			butCo = -1.0;
+		}
+	}
+	else if (color_id == COLOR_EDGE) {
+		finalColor = colorEdge;
+		butCo = -1.0;
+	}
+	else /* (color_id == COLOR_EMBOSS) */ {
+		finalColor = colorEmboss;
+		butCo = -1.0;
+	}
+
+	bool is_emboss = (vflag & EMBOSS_FLAG) != 0u;
+	v.y -= (is_emboss) ? 1.0f : 0.0;
+
+	return v;
+}
+
+vec2 do_tria()
+{
+	uint vofs = vflag & TRIA_VEC_RANGE;
+
+	vec2 v = triavec[vofs];
+
+	finalColor = colorTria;
+	butCo = -1.0;
+
+	bool is_tria_first = (vflag & TRIA_FIRST) != 0u;
+
+	if (is_tria_first)
+		v = v * tria1Size + tria1Center;
+	else
+		v = v * tria2Size + tria2Center;
+
+	return v;
+}
+
+void main()
+{
+	bool is_tria = (vflag & TRIA_FLAG) != 0u;
+
+	vec2 v = (is_tria) ? do_tria() : do_widget();
+
+	/* Antialiasing offset */
+	v += jit[(vflag >> JIT_OFS) & JIT_RANGE];
+
+	gl_Position = ModelViewProjectionMatrix * vec4(v, 0.0, 1.0);
+}
\ No newline at end of file
diff --git a/source/blender/gpu/shaders/gpu_shader_2D_widget_shadow_frag.glsl b/source/blender/gpu/shaders/gpu_shader_2D_widget_shadow_frag.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..7587b2fc18ac29374cc1458632f11a4c78d9214a
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_2D_widget_shadow_frag.glsl
@@ -0,0 +1,13 @@
+
+in float shadowFalloff;
+
+out vec4 fragColor;
+
+uniform float alpha;
+
+void main()
+{
+	fragColor = vec4(0.0);
+	/* Manual curve fit of the falloff curve of previous drawing method. */
+	fragColor.a = alpha * (shadowFalloff * shadowFalloff * 0.722 + shadowFalloff * 0.277);
+}
\ No newline at end of file
diff --git a/source/blender/gpu/shaders/gpu_shader_2D_widget_shadow_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_widget_shadow_vert.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..441f97e95f09b90d293bf554b10cbe170070c047
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_2D_widget_shadow_vert.glsl
@@ -0,0 +1,64 @@
+#define BIT_RANGE(x) ((1u << x) - 1u)
+
+/* 2 bits for corner */
+/* Attention! Not the same order as in UI_interface.h!
+ * Ordered by drawing order. */
+#define BOTTOM_LEFT 0u
+#define BOTTOM_RIGHT 1u
+#define TOP_RIGHT 2u
+#define TOP_LEFT 3u
+#define CNR_FLAG_RANGE BIT_RANGE(2u)
+
+/* 4bits for corner id */
+#define CORNER_VEC_OFS 2u
+#define CORNER_VEC_RANGE BIT_RANGE(4u)
+const vec2 cornervec[36] = vec2[36](
+	vec2(0.0, 1.0), vec2(0.02, 0.805), vec2(0.067, 0.617), vec2(0.169, 0.45), vec2(0.293, 0.293), vec2(0.45, 0.169), vec2(0.617, 0.076), vec2(0.805, 0.02), vec2(1.0, 0.0),
+	vec2(-1.0, 0.0), vec2(-0.805, 0.02), vec2(-0.617, 0.067), vec2(-0.45, 0.169), vec2(-0.293, 0.293), vec2(-0.169, 0.45), vec2(-0.076, 0.617), vec2(-0.02, 0.805), vec2(0.0, 1.0),
+	vec2(0.0, -1.0), vec2(-0.02, -0.805), vec2(-0.067, -0.617), vec2(-0.169, -0.45), vec2(-0.293, -0.293), vec2(-0.45, -0.169), vec2(-0.617, -0.076), vec2(-0.805, -0.02), vec2(-1.0, 0.0),
+	vec2(1.0, 0.0), vec2(0.805, -0.02), vec2(0.617, -0.067), vec2(0.45, -0.169), vec2(0.293, -0.293), vec2(0.169, -0.45), vec2(0.076, -0.617), vec2(0.02, -0.805), vec2(0.0, -1.0)
+);
+
+#define INNER_FLAG     (1u << 10u) /* is inner vert */
+
+uniform mat4 ModelViewProjectionMatrix;
+
+uniform vec4 parameters[4];
+/* radi and rad per corner */
+#define recti        parameters[0]
+#define rect         parameters[1]
+#define radsi        parameters[2].x
+#define rads         parameters[2].y
+#define roundCorners parameters[3]
+
+in uint vflag;
+
+out float shadowFalloff;
+
+void main()
+{
+	uint cflag = vflag & CNR_FLAG_RANGE;
+	uint vofs = (vflag >> CORNER_VEC_OFS) & CORNER_VEC_RANGE;
+
+	vec2 v = cornervec[cflag * 9u + vofs];
+
+	bool is_inner = (vflag & INNER_FLAG) != 0u;
+
+	shadowFalloff = (is_inner) ? 1.0 : 0.0;
+
+	/* Scale by corner radius */
+	v *= roundCorners[cflag] * ((is_inner) ? radsi : rads);
+
+	/* Position to corner */
+	vec4 rct = (is_inner) ? recti : rect;
+	if (cflag == BOTTOM_LEFT)
+		v += rct.xz;
+	else if (cflag == BOTTOM_RIGHT)
+		v += rct.yz;
+	else if (cflag == TOP_RIGHT)
+		v += rct.yw;
+	else /* (cflag == TOP_LEFT) */
+		v += rct.xw;
+
+	gl_Position = ModelViewProjectionMatrix * vec4(v, 0.0, 1.0);
+}
\ No newline at end of file
diff --git a/source/blender/gpu/shaders/gpu_shader_image_varying_color_frag.glsl b/source/blender/gpu/shaders/gpu_shader_image_varying_color_frag.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..37686092700f976c4ad6977081b64ec270af6584
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_image_varying_color_frag.glsl
@@ -0,0 +1,11 @@
+
+in vec2 texCoord_interp;
+flat in vec4 finalColor;
+out vec4 fragColor;
+
+uniform sampler2D image;
+
+void main()
+{
+	fragColor = texture(image, texCoord_interp) * finalColor;
+}
diff --git a/source/blender/gpu/shaders/gpu_shader_sep_gaussian_blur_vert.glsl b/source/blender/gpu/shaders/gpu_shader_sep_gaussian_blur_vert.glsl
index 1319e386c65b7518ee64406f5a932c7043957769..fca39852c2a433ed5f55a0c01f1dc544c6cca390 100644
--- a/source/blender/gpu/shaders/gpu_shader_sep_gaussian_blur_vert.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_sep_gaussian_blur_vert.glsl
@@ -1,12 +1,14 @@
 
-uniform mat4 ModelViewProjectionMatrix;
-
-in vec2 pos;
-in vec2 uvs;
 out vec2 texCoord_interp;
 
 void main()
 {
-	gl_Position = ModelViewProjectionMatrix * vec4(pos, 0.0, 1.0);
-	texCoord_interp = uvs;
+	const vec4 vert[3] = vec4[3](
+		vec3(-1.0, -1.0,  0.0,  0.0),
+		vec3( 3.0, -1.0,  2.0,  0.0),
+		vec3(-1.0,  3.0,  0.0,  2.0)
+	);
+
+	gl_Position = vec4(vert[gl_VertexID].xy, 0.0, 1.0);
+	texCoord_interp = vert[gl_VertexID].zw;
 }
diff --git a/source/blender/gpu/shaders/gpu_shader_text_frag.glsl b/source/blender/gpu/shaders/gpu_shader_text_frag.glsl
index 7ff90ad4f21f6424a4363293b2f9703aa5d5513f..fbfa4cfcc9d6fa5c9b2e4324670a973d9c4c9586 100644
--- a/source/blender/gpu/shaders/gpu_shader_text_frag.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_text_frag.glsl
@@ -1,15 +1,74 @@
 
 flat in vec4 color_flat;
+flat in vec4 texCoord_rect;
 noperspective in vec2 texCoord_interp;
 out vec4 fragColor;
 
 uniform sampler2D glyph;
 
+const vec2 offsets4[4] = vec2[4](
+	vec2(-0.5,  0.5), vec2( 0.5,  0.5),
+	vec2(-0.5, -0.5), vec2(-0.5, -0.5)
+);
+
+const vec2 offsets16[16] = vec2[16](
+	vec2(-1.5,  1.5), vec2(-0.5,  1.5), vec2( 0.5,  1.5), vec2( 1.5,  1.5),
+	vec2(-1.5,  0.5), vec2(-0.5,  0.5), vec2( 0.5,  0.5), vec2( 1.5,  0.5),
+	vec2(-1.5, -0.5), vec2(-0.5, -0.5), vec2( 0.5, -0.5), vec2( 1.5, -0.5),
+	vec2(-1.5, -1.5), vec2(-0.5, -1.5), vec2( 0.5, -1.5), vec2( 1.5, -1.5)
+);
+
+#define sample_glyph_offset(texco, texel, ofs) texture(glyph, texco + ofs * texel).r
+
 void main()
 {
 	// input color replaces texture color
 	fragColor.rgb = color_flat.rgb;
 
+	vec2 texel = 1.0 / vec2(textureSize(glyph, 0));
+	vec2 texco = mix(abs(texCoord_rect.xy), abs(texCoord_rect.zw), texCoord_interp);
+
 	// modulate input alpha & texture alpha
-	fragColor.a = color_flat.a * texture(glyph, texCoord_interp).r;
+	if (texCoord_rect.x > 0) {
+		fragColor.a = texture(glyph, texco).r;
+	}
+	else {
+		fragColor.a = 0.0;
+
+		if (texCoord_rect.w > 0) {
+			/* 3x3 blur */
+			/* Manual unroll for perf. (stupid glsl compiler) */
+			fragColor.a += sample_glyph_offset(texco, texel, offsets4[0]);
+			fragColor.a += sample_glyph_offset(texco, texel, offsets4[1]);
+			fragColor.a += sample_glyph_offset(texco, texel, offsets4[2]);
+			fragColor.a += sample_glyph_offset(texco, texel, offsets4[3]);
+			fragColor.a *= (1.0 / 4.0);
+		}
+		else {
+			/* 5x5 blur */
+			/* Manual unroll for perf. (stupid glsl compiler) */
+			fragColor.a += sample_glyph_offset(texco, texel, offsets16[ 0]);
+			fragColor.a += sample_glyph_offset(texco, texel, offsets16[ 1]);
+			fragColor.a += sample_glyph_offset(texco, texel, offsets16[ 2]);
+			fragColor.a += sample_glyph_offset(texco, texel, offsets16[ 3]);
+
+			fragColor.a += sample_glyph_offset(texco, texel, offsets16[ 4]);
+			fragColor.a += sample_glyph_offset(texco, texel, offsets16[ 5]) * 2.0;
+			fragColor.a += sample_glyph_offset(texco, texel, offsets16[ 6]) * 2.0;
+			fragColor.a += sample_glyph_offset(texco, texel, offsets16[ 7]);
+
+			fragColor.a += sample_glyph_offset(texco, texel, offsets16[ 8]);
+			fragColor.a += sample_glyph_offset(texco, texel, offsets16[ 9]) * 2.0;
+			fragColor.a += sample_glyph_offset(texco, texel, offsets16[10]) * 2.0;
+			fragColor.a += sample_glyph_offset(texco, texel, offsets16[11]);
+
+			fragColor.a += sample_glyph_offset(texco, texel, offsets16[12]);
+			fragColor.a += sample_glyph_offset(texco, texel, offsets16[13]);
+			fragColor.a += sample_glyph_offset(texco, texel, offsets16[14]);
+			fragColor.a += sample_glyph_offset(texco, texel, offsets16[15]);
+			fragColor.a *= (1.0 / 20.0);
+		}
+	}
+
+	fragColor.a *= color_flat.a;
 }
diff --git a/source/blender/gpu/shaders/gpu_shader_text_geom.glsl b/source/blender/gpu/shaders/gpu_shader_text_geom.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..0acd2106f7acb2ea908f0b1a2b2325a949c06500
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_text_geom.glsl
@@ -0,0 +1,37 @@
+
+uniform mat4 ModelViewProjectionMatrix;
+
+layout(points) in;
+layout(triangle_strip, max_vertices = 4) out;
+
+in vec4 pos_rect[];
+in vec4 tex_rect[];
+in vec4 color[];
+
+flat out vec4 color_flat;
+flat out vec4 texCoord_rect;
+noperspective out vec2 texCoord_interp;
+
+void main()
+{
+	color_flat = color[0];
+	texCoord_rect = tex_rect[0];
+
+	gl_Position = (ModelViewProjectionMatrix * vec4(pos_rect[0].xy, 0.0, 1.0));
+	texCoord_interp = vec2(0.0, 0.0);
+	EmitVertex();
+
+	gl_Position = (ModelViewProjectionMatrix * vec4(pos_rect[0].zy, 0.0, 1.0));
+	texCoord_interp = vec2(1.0, 0.0);
+	EmitVertex();
+
+	gl_Position = (ModelViewProjectionMatrix * vec4(pos_rect[0].xw, 0.0, 1.0));
+	texCoord_interp = vec2(0.0, 1.0);
+	EmitVertex();
+
+	gl_Position = (ModelViewProjectionMatrix * vec4(pos_rect[0].zw, 0.0, 1.0));
+	texCoord_interp = vec2(1.0, 1.0);
+	EmitVertex();
+
+	EndPrimitive();
+}
diff --git a/source/blender/gpu/shaders/gpu_shader_text_simple_geom.glsl b/source/blender/gpu/shaders/gpu_shader_text_simple_geom.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..8903fd1df5751e337042fc703398563eeaf7e2c9
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_text_simple_geom.glsl
@@ -0,0 +1,36 @@
+
+layout(points) in;
+layout(triangle_strip, max_vertices = 4) out;
+
+in vec4 pos_rect[];
+in vec4 tex_rect[];
+in vec4 color[];
+
+flat out vec4 color_flat;
+flat out vec4 texCoord_rect;
+noperspective out vec2 texCoord_interp;
+
+void main()
+{
+	color_flat = color[0];
+	texCoord_rect = tex_rect[0];
+	gl_Position.zw = vec2(0.0, 1.0);
+
+	gl_Position.xy  = pos_rect[0].xy;
+	texCoord_interp = vec2(0.0, 0.0);
+	EmitVertex();
+
+	gl_Position.xy  = pos_rect[0].zy;
+	texCoord_interp = vec2(1.0, 0.0);
+	EmitVertex();
+
+	gl_Position.xy  = pos_rect[0].xw;
+	texCoord_interp = vec2(0.0, 1.0);
+	EmitVertex();
+
+	gl_Position.xy  = pos_rect[0].zw;
+	texCoord_interp = vec2(1.0, 1.0);
+	EmitVertex();
+
+	EndPrimitive();
+}
diff --git a/source/blender/gpu/shaders/gpu_shader_text_simple_vert.glsl b/source/blender/gpu/shaders/gpu_shader_text_simple_vert.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..4a2cde71e072ffa1c545a79a1fd85ecdcfc3842e
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_text_simple_vert.glsl
@@ -0,0 +1,22 @@
+
+/* Simpler version of gpu_shader_text_vert that supports only 2D translation. */
+
+uniform mat4 ModelViewProjectionMatrix;
+
+in vec4 pos; /* rect */
+in vec4 tex; /* rect */
+in vec4 col;
+
+out vec4 pos_rect;
+out vec4 tex_rect;
+out vec4 color;
+
+void main()
+{
+	/* Manual mat4*vec2 */
+	pos_rect  = ModelViewProjectionMatrix[0].xyxy * pos.xxzz;
+	pos_rect += ModelViewProjectionMatrix[1].xyxy * pos.yyww;
+	pos_rect += ModelViewProjectionMatrix[3].xyxy;
+	tex_rect = tex;
+	color = col;
+}
diff --git a/source/blender/gpu/shaders/gpu_shader_text_vert.glsl b/source/blender/gpu/shaders/gpu_shader_text_vert.glsl
index 6129f49ce22ac15a2859faffd08900141dfedfd7..338156f5b68f44d65ba7d0ac87334456846c5ebe 100644
--- a/source/blender/gpu/shaders/gpu_shader_text_vert.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_text_vert.glsl
@@ -1,16 +1,17 @@
 
 uniform mat4 ModelViewProjectionMatrix;
 
-in vec2 pos;
-in vec2 texCoord;
-in vec4 color;
-flat out vec4 color_flat;
-noperspective out vec2 texCoord_interp;
+in vec4 pos; /* rect */
+in vec4 tex; /* rect */
+in vec4 col;
+
+out vec4 pos_rect;
+out vec4 tex_rect;
+out vec4 color;
 
 void main()
 {
-	gl_Position = ModelViewProjectionMatrix * vec4(pos, 0.0, 1.0);
-
-	color_flat = color;
-	texCoord_interp = texCoord;
+	pos_rect = pos;
+	tex_rect = tex;
+	color = col;
 }
diff --git a/source/blender/imbuf/CMakeLists.txt b/source/blender/imbuf/CMakeLists.txt
index c3950d8eb83445a125ade059d90861d198a1adc9..e73f227dec8a2eb49f637d518666dc7f846155e2 100644
--- a/source/blender/imbuf/CMakeLists.txt
+++ b/source/blender/imbuf/CMakeLists.txt
@@ -73,6 +73,7 @@ set(SRC
 	IMB_colormanagement.h
 	IMB_imbuf.h
 	IMB_imbuf_types.h
+	IMB_metadata.h
 	IMB_moviecache.h
 	IMB_thumbs.h
 	intern/IMB_allocimbuf.h
@@ -81,7 +82,6 @@ set(SRC
 	intern/IMB_filetype.h
 	intern/IMB_filter.h
 	intern/IMB_indexer.h
-	intern/IMB_metadata.h
 	intern/imbuf.h
 	
 	# orphan include
diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h
index a7f793b5b1180a9f4edbc6b809ca4c34526a2db1..e5cd21a3248f224db1672d86f531039f48d3ec87 100644
--- a/source/blender/imbuf/IMB_imbuf.h
+++ b/source/blender/imbuf/IMB_imbuf.h
@@ -380,13 +380,13 @@ struct ImBuf *IMB_onehalf(struct ImBuf *ibuf1);
  *
  * \attention Defined in scaling.c
  */
-struct ImBuf *IMB_scaleImBuf(struct ImBuf *ibuf, unsigned int newx, unsigned int newy);
+bool IMB_scaleImBuf(struct ImBuf *ibuf, unsigned int newx, unsigned int newy);
 
 /**
  *
  * \attention Defined in scaling.c
  */
-struct ImBuf *IMB_scalefastImBuf(struct ImBuf *ibuf, unsigned int newx, unsigned int newy);
+bool IMB_scalefastImBuf(struct ImBuf *ibuf, unsigned int newx, unsigned int newy);
 
 /**
  *
@@ -399,7 +399,7 @@ void IMB_scaleImBuf_threaded(struct ImBuf *ibuf, unsigned int newx, unsigned int
  * \attention Defined in writeimage.c
  */
 short IMB_saveiff(struct ImBuf *ibuf, const char *filepath, int flags);
-struct ImBuf *IMB_prepare_write_ImBuf(const bool isfloat, struct ImBuf *ibuf);
+bool IMB_prepare_write_ImBuf(const bool isfloat, struct ImBuf *ibuf);
 
 /**
  *
@@ -566,22 +566,6 @@ void buf_rectfill_area(unsigned char *rect, float *rectf, int width, int height,
                        const float col[4], struct ColorManagedDisplay *display,
                        int x1, int y1, int x2, int y2);
 
-/**
- *
- * \attention Defined in metadata.c
- */
-/** read the field from the image info into the field 
- *  \param img - the ImBuf that contains the image data
- *  \param key - the key of the field
- *  \param value - the data in the field, first one found with key is returned, 
- *                 memory has to be allocated by user.
- *  \param len - length of value buffer allocated by user.
- *  \return    - 1 (true) if ImageInfo present and value for the key found, 0 (false) otherwise
- */
-bool IMB_metadata_get_field(struct ImBuf *img, const char *key, char *value, const size_t len);
-bool IMB_metadata_change_field(struct ImBuf *img, const char *key, const char *field);
-void IMB_metadata_copy(struct ImBuf *dimb, struct ImBuf *simb);
-
 /* exported for image tools in blender, to quickly allocate 32 bits rect */
 void *imb_alloc_pixels(unsigned int x,
                        unsigned int y,
diff --git a/source/blender/imbuf/intern/IMB_metadata.h b/source/blender/imbuf/IMB_metadata.h
similarity index 51%
rename from source/blender/imbuf/intern/IMB_metadata.h
rename to source/blender/imbuf/IMB_metadata.h
index bc0b2c70ecbf5aa6a5e25b0dab7f9df8f6de7a21..6a29fa01594a5c8966250e5a8e547fb53eece2fb 100644
--- a/source/blender/imbuf/intern/IMB_metadata.h
+++ b/source/blender/imbuf/IMB_metadata.h
@@ -4,7 +4,7 @@
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version. 
+ * of the License, or (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -25,7 +25,7 @@
  * ***** END GPL LICENSE BLOCK *****
  */
 
-/** \file blender/imbuf/intern/IMB_metadata.h
+/** \file blender/imbuf/IMB_metadata.h
  *  \ingroup imbuf
  */
 
@@ -33,7 +33,9 @@
 #ifndef __IMB_METADATA_H__
 #define __IMB_METADATA_H__
 
+struct anim;
 struct ImBuf;
+struct IDProperty;
 
 /** The metadata is a list of key/value pairs (both char *) that can me
  * saved in the header of several image formats.
@@ -41,26 +43,36 @@ struct ImBuf;
  * 'Software' and 'Description' (png standard) we'll use keys within the
  * Blender namespace, so should be called 'Blender::StampInfo' or 'Blender::FrameNum'
  * etc...
+ *
+ * The keys & values are stored in ID properties, in the group "metadata".
  */
 
+/** Ensure that the metadata property is a valid IDProperty object.
+ * This is a no-op when *metadata != NULL.
+ */
+void IMB_metadata_ensure(struct IDProperty **metadata);
+void IMB_metadata_free(struct IDProperty *metadata);
 
-/* free blender ImMetaData struct */
-void IMB_metadata_free(struct ImBuf *img);
+/** Read the field from the image info into the field.
+ *  \param metadata - the IDProperty that contains the metadata
+ *  \param key - the key of the field
+ *  \param value - the data in the field, first one found with key is returned,
+ *                 memory has to be allocated by user.
+ *  \param len - length of value buffer allocated by user.
+ *  \return    - 1 (true) if metadata is present and value for the key found, 0 (false) otherwise
+ */
+bool IMB_metadata_get_field(struct IDProperty *metadata, const char *key, char *value, const size_t len);
 
-/** set user data in the ImMetaData struct, which has to be allocated with IMB_metadata_create
- *  before calling this function.
- *  \param img - the ImBuf that contains the image data
+/** Set user data in the metadata.
+ * If the field already exists its value is overwritten, otherwise the field
+ * will be added with the given value.
+ *  \param metadata - the IDProperty that contains the metadata
  *  \param key - the key of the field
  *  \param value - the data to be written to the field. zero terminated string
- *  \return    - 1 (true) if ImageInfo present, 0 (false) otherwise
  */
-bool IMB_metadata_add_field(struct ImBuf *img, const char *key, const char *value);
+void IMB_metadata_set_field(struct IDProperty *metadata, const char *key, const char *value);
 
-/** delete the key/field par in the ImMetaData struct.
- * \param img - the ImBuf that contains the image data
- * \param key - the key of the field
- * \return - 1 (true) if delete the key/field, 0 (false) otherwise
- */
-bool IMB_metadata_del_field(struct ImBuf *img, const char *key);
+void IMB_metadata_copy(struct ImBuf *dimb, struct ImBuf *simb);
+struct IDProperty *IMB_anim_load_metadata(struct anim *anim);
 
 #endif /* __IMB_METADATA_H__ */
diff --git a/source/blender/imbuf/intern/IMB_anim.h b/source/blender/imbuf/intern/IMB_anim.h
index c4c4f4405a50e43ffd210461af8f9530b6415472..6fa31e122cc03d75ce1a0caf90f3ffa72644aee3 100644
--- a/source/blender/imbuf/intern/IMB_anim.h
+++ b/source/blender/imbuf/intern/IMB_anim.h
@@ -93,6 +93,7 @@
 
 struct _AviMovie;
 struct anim_index;
+struct IDProperty;
 
 struct anim {
 	int ib_flags;
@@ -158,6 +159,8 @@ struct anim {
 
 	char colorspace[64];
 	char suffix[64]; /* MAX_NAME - multiview */
+
+	struct IDProperty *metadata;
 };
 
 #endif
diff --git a/source/blender/imbuf/intern/allocimbuf.c b/source/blender/imbuf/intern/allocimbuf.c
index 7fc4a65d8d79315e7eb3c6e30eb2f647a20436dc..faa0b5f7b6e6421dac4fa9fffbc86892ad84ffd8 100644
--- a/source/blender/imbuf/intern/allocimbuf.c
+++ b/source/blender/imbuf/intern/allocimbuf.c
@@ -219,7 +219,7 @@ void IMB_freeImBuf(ImBuf *ibuf)
 			IMB_freezbufImBuf(ibuf);
 			IMB_freezbuffloatImBuf(ibuf);
 			freeencodedbufferImBuf(ibuf);
-			IMB_metadata_free(ibuf);
+			IMB_metadata_free(ibuf->metadata);
 			colormanage_cache_free(ibuf);
 
 			if (ibuf->dds_data.data != NULL) {
diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c
index a770b34ecc6ae4d0cb37acd10d54919cd8db3663..f842b69418e2e52922a6fc056a31008206a8ed1e 100644
--- a/source/blender/imbuf/intern/anim_movie.c
+++ b/source/blender/imbuf/intern/anim_movie.c
@@ -68,8 +68,6 @@
 
 #include "MEM_guardedalloc.h"
 
-#include "BKE_global.h"
-
 #ifdef WITH_AVI
 #  include "AVI_avi.h"
 #endif
@@ -82,8 +80,11 @@
 
 #include "IMB_anim.h"
 #include "IMB_indexer.h"
+#include "IMB_metadata.h"
 
 #ifdef WITH_FFMPEG
+#  include "BKE_global.h"  /* ENDIAN_ORDER */
+
 #  include <libavformat/avformat.h>
 #  include <libavcodec/avcodec.h>
 #  include <libavutil/rational.h>
@@ -220,6 +221,7 @@ void IMB_free_anim(struct anim *anim)
 	free_anim_ffmpeg(anim);
 #endif
 	IMB_free_indices(anim);
+	IMB_metadata_free(anim->metadata);
 
 	MEM_freeN(anim);
 }
@@ -239,6 +241,26 @@ void IMB_close_anim_proxies(struct anim *anim)
 	IMB_free_indices(anim);
 }
 
+struct IDProperty *IMB_anim_load_metadata(struct anim *anim)
+{
+#ifdef WITH_FFMPEG
+	AVDictionaryEntry *entry = NULL;
+
+	BLI_assert(anim->pFormatCtx != NULL);
+	av_log(anim->pFormatCtx, AV_LOG_DEBUG, "METADATA FETCH\n");
+
+	while (true) {
+		entry = av_dict_get(anim->pFormatCtx->metadata, "", entry, AV_DICT_IGNORE_SUFFIX);
+		if (entry == NULL) break;
+
+		/* Delay creation of the property group until there is actual metadata to put in there. */
+		IMB_metadata_ensure(&anim->metadata);
+		IMB_metadata_set_field(anim->metadata, entry->key, entry->value);
+	}
+#endif
+	return anim->metadata;
+}
+
 struct anim *IMB_open_anim(const char *name, int ib_flags, int streamindex, char colorspace[IM_MAX_SPACE])
 {
 	struct anim *anim;
diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c
index e28c812200603afc0290346d652c995375f56f37..b2197ecb3b5a8dea7fa7fa0081034aed950d2b35 100644
--- a/source/blender/imbuf/intern/colormanagement.c
+++ b/source/blender/imbuf/intern/colormanagement.c
@@ -49,6 +49,7 @@
 #include "IMB_filetype.h"
 #include "IMB_filter.h"
 #include "IMB_moviecache.h"
+#include "IMB_metadata.h"
 
 #include "MEM_guardedalloc.h"
 
diff --git a/source/blender/imbuf/intern/jpeg.c b/source/blender/imbuf/intern/jpeg.c
index 35c7b6363a1e8512630c942cd1d3509b4b1c2b75..ef9217fbc8ca0988ec186446e9f5b70df1973b01 100644
--- a/source/blender/imbuf/intern/jpeg.c
+++ b/source/blender/imbuf/intern/jpeg.c
@@ -382,7 +382,8 @@ static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo, int fla
 					 * the information when we write
 					 * it back to disk.
 					 */
-					IMB_metadata_add_field(ibuf, "None", str);
+					IMB_metadata_ensure(&ibuf->metadata);
+					IMB_metadata_set_field(ibuf->metadata, "None", str);
 					ibuf->flags |= IB_metadata;
 					MEM_freeN(str);
 					goto next_stamp_marker;
@@ -408,7 +409,8 @@ static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo, int fla
 
 				*value = '\0'; /* need finish the key string */
 				value++;
-				IMB_metadata_add_field(ibuf, key, value);
+				IMB_metadata_ensure(&ibuf->metadata);
+				IMB_metadata_set_field(ibuf->metadata, key, value);
 				ibuf->flags |= IB_metadata;
 				MEM_freeN(str);
 next_stamp_marker:
diff --git a/source/blender/imbuf/intern/metadata.c b/source/blender/imbuf/intern/metadata.c
index da39967a4fe218ebf13c571a2d4d96edaec486e5..ef103f7afcff2f3cd7a96afc82f404ce3a17ea22 100644
--- a/source/blender/imbuf/intern/metadata.c
+++ b/source/blender/imbuf/intern/metadata.c
@@ -45,97 +45,69 @@
 
 #include "IMB_metadata.h"
 
+#define METADATA_MAX_VALUE_LENGTH 1024
 
 
-void IMB_metadata_free(struct ImBuf *img)
+void IMB_metadata_ensure(struct IDProperty **metadata)
 {
-	if (!img)
+	if (*metadata != NULL) {
 		return;
-	if (!img->metadata) {
+	}
+
+	IDPropertyTemplate val;
+	*metadata = IDP_New(IDP_GROUP, &val, "metadata");
+}
+
+void IMB_metadata_free(struct IDProperty *metadata)
+{
+	if (metadata == NULL) {
 		return;
 	}
 
-	IDP_FreeProperty(img->metadata);
-	MEM_freeN(img->metadata);
+	IDP_FreeProperty(metadata);
+	MEM_freeN(metadata);
 }
 
-bool IMB_metadata_get_field(struct ImBuf *img, const char *key, char *field, const size_t len)
+bool IMB_metadata_get_field(struct IDProperty *metadata, const char *key, char *field, const size_t len)
 {
 	IDProperty *prop;
 
-	bool retval = false;
-
-	if (!img)
-		return false;
-	if (!img->metadata)
+	if (metadata == NULL) {
 		return false;
+	}
 
-	prop = IDP_GetPropertyFromGroup(img->metadata, key);
+	prop = IDP_GetPropertyFromGroup(metadata, key);
 
 	if (prop && prop->type == IDP_STRING) {
 		BLI_strncpy(field, IDP_String(prop), len);
-		retval = true;
+		return true;
 	}
-	return retval;
+	return false;
 }
 
 void IMB_metadata_copy(struct ImBuf *dimb, struct ImBuf *simb)
 {
 	BLI_assert(dimb != simb);
 	if (simb->metadata) {
-		IMB_metadata_free(dimb);
+		IMB_metadata_free(dimb->metadata);
 		dimb->metadata = IDP_CopyProperty(simb->metadata);
 	}
 }
 
-bool IMB_metadata_add_field(struct ImBuf *img, const char *key, const char *value)
+void IMB_metadata_set_field(struct IDProperty *metadata, const char *key, const char *value)
 {
-	IDProperty *prop;
-
-	if (!img)
-		return false;
+	BLI_assert(metadata);
+	IDProperty *prop = IDP_GetPropertyFromGroup(metadata, key);
 
-	if (!img->metadata) {
-		IDPropertyTemplate val;
-		img->metadata = IDP_New(IDP_GROUP, &val, "metadata");
+	if (prop != NULL && prop->type != IDP_STRING) {
+		IDP_FreeFromGroup(metadata, prop);
+		prop = NULL;
 	}
 
-	prop = IDP_NewString(value, key, 512);
-	return IDP_AddToGroup(img->metadata, prop);
-}
-
-bool IMB_metadata_del_field(struct ImBuf *img, const char *key)
-{
-	IDProperty *prop;
-
-	if ((!img) || (!img->metadata))
-		return false;
-
-	prop = IDP_GetPropertyFromGroup(img->metadata, key);
-
-	if (prop) {
-		IDP_FreeFromGroup(img->metadata, prop);
+	if (prop == NULL) {
+		prop = IDP_NewString(value, key, METADATA_MAX_VALUE_LENGTH);
+		IDP_AddToGroup(metadata, prop);
 	}
-	return false;
-}
-
-bool IMB_metadata_change_field(struct ImBuf *img, const char *key, const char *field)
-{
-	IDProperty *prop;
 
-	if (!img)
-		return false;
-
-	prop = (img->metadata) ? IDP_GetPropertyFromGroup(img->metadata, key) : NULL;
-
-	if (!prop) {
-		return (IMB_metadata_add_field(img, key, field));
-	}
-	else if (prop->type == IDP_STRING) {
-		IDP_AssignString(prop, field, 1024);
-		return true;
-	}
-	else {
-		return false;
-	}
+	IDP_AssignString(prop, value, METADATA_MAX_VALUE_LENGTH);
 }
diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp
index 4e85d70d3829a56dac489ccd6d3a73725c4bd49d..ad04f1fb78d68c6d17d1cd9822eeeccae63f2050 100644
--- a/source/blender/imbuf/intern/openexr/openexr_api.cpp
+++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp
@@ -1790,12 +1790,13 @@ struct ImBuf *imb_load_openexr(const unsigned char *mem, size_t size, int flags,
 					const Header & header = file->header(0);
 					Header::ConstIterator iter;
 
+					IMB_metadata_ensure(&ibuf->metadata);
 					for (iter = header.begin(); iter != header.end(); iter++) {
 						const StringAttribute *attrib = file->header(0).findTypedAttribute <StringAttribute> (iter.name());
 
 						/* not all attributes are string attributes so we might get some NULLs here */
 						if (attrib) {
-							IMB_metadata_add_field(ibuf, iter.name(), attrib->value().c_str());
+							IMB_metadata_set_field(ibuf->metadata, iter.name(), attrib->value().c_str());
 							ibuf->flags |= IB_metadata;
 						}
 					}
diff --git a/source/blender/imbuf/intern/png.c b/source/blender/imbuf/intern/png.c
index 857f72e10eb4a66f2bc5818adca28ff87dd7633a..29fbe79d8dbe708463d089a5f80ae46f63113f99 100644
--- a/source/blender/imbuf/intern/png.c
+++ b/source/blender/imbuf/intern/png.c
@@ -759,8 +759,9 @@ ImBuf *imb_loadpng(const unsigned char *mem, size_t size, int flags, char colors
 		if (flags & IB_metadata) {
 			png_text *text_chunks;
 			int count = png_get_text(png_ptr, info_ptr, &text_chunks, NULL);
+			IMB_metadata_ensure(&ibuf->metadata);
 			for (int i = 0; i < count; i++) {
-				IMB_metadata_add_field(ibuf, text_chunks[i].key, text_chunks[i].text);
+				IMB_metadata_set_field(ibuf->metadata, text_chunks[i].key, text_chunks[i].text);
 				ibuf->flags |= IB_metadata;
 			}
 		}
diff --git a/source/blender/imbuf/intern/scaling.c b/source/blender/imbuf/intern/scaling.c
index 504b59b2b1d39d09dd1cbb6225c23df3296e406b..ff92ce158118f2cd1c6da87720812f8df3d1e657 100644
--- a/source/blender/imbuf/intern/scaling.c
+++ b/source/blender/imbuf/intern/scaling.c
@@ -1550,12 +1550,17 @@ static void scalefast_Z_ImBuf(ImBuf *ibuf, int newx, int newy)
 	}
 }
 
-struct ImBuf *IMB_scaleImBuf(struct ImBuf *ibuf, unsigned int newx, unsigned int newy)
+/**
+ * Return true if \a ibuf is modified.
+ */
+bool IMB_scaleImBuf(struct ImBuf *ibuf, unsigned int newx, unsigned int newy)
 {
-	if (ibuf == NULL) return (NULL);
-	if (ibuf->rect == NULL && ibuf->rect_float == NULL) return (ibuf);
+	if (ibuf == NULL) return false;
+	if (ibuf->rect == NULL && ibuf->rect_float == NULL) return false;
 	
-	if (newx == ibuf->x && newy == ibuf->y) { return ibuf; }
+	if (newx == ibuf->x && newy == ibuf->y) {
+		return false;
+	}
 
 	/* scaleup / scaledown functions below change ibuf->x and ibuf->y
 	 * so we first scale the Z-buffer (if any) */
@@ -1564,7 +1569,7 @@ struct ImBuf *IMB_scaleImBuf(struct ImBuf *ibuf, unsigned int newx, unsigned int
 	/* try to scale common cases in a fast way */
 	/* disabled, quality loss is unacceptable, see report #18609  (ton) */
 	if (0 && q_scale_linear_interpolation(ibuf, newx, newy)) {
-		return ibuf;
+		return true;
 	}
 
 	if (newx && (newx < ibuf->x)) scaledownx(ibuf, newx);
@@ -1572,14 +1577,17 @@ struct ImBuf *IMB_scaleImBuf(struct ImBuf *ibuf, unsigned int newx, unsigned int
 	if (newx && (newx > ibuf->x)) scaleupx(ibuf, newx);
 	if (newy && (newy > ibuf->y)) scaleupy(ibuf, newy);
 	
-	return(ibuf);
+	return true;
 }
 
 struct imbufRGBA {
 	float r, g, b, a;
 };
 
-struct ImBuf *IMB_scalefastImBuf(struct ImBuf *ibuf, unsigned int newx, unsigned int newy)
+/**
+ * Return true if \a ibuf is modified.
+ */
+bool IMB_scalefastImBuf(struct ImBuf *ibuf, unsigned int newx, unsigned int newy)
 {
 	unsigned int *rect, *_newrect, *newrect;
 	struct imbufRGBA *rectf, *_newrectf, *newrectf;
@@ -1590,16 +1598,16 @@ struct ImBuf *IMB_scalefastImBuf(struct ImBuf *ibuf, unsigned int newx, unsigned
 	rect = NULL; _newrect = NULL; newrect = NULL;
 	rectf = NULL; _newrectf = NULL; newrectf = NULL;
 
-	if (ibuf == NULL) return(NULL);
+	if (ibuf == NULL) return false;
 	if (ibuf->rect) do_rect = true;
 	if (ibuf->rect_float) do_float = true;
-	if (do_rect == false && do_float == false) return(ibuf);
+	if (do_rect == false && do_float == false) return false;
 	
-	if (newx == ibuf->x && newy == ibuf->y) return(ibuf);
+	if (newx == ibuf->x && newy == ibuf->y) return false;
 	
 	if (do_rect) {
 		_newrect = MEM_mallocN(newx * newy * sizeof(int), "scalefastimbuf");
-		if (_newrect == NULL) return(ibuf);
+		if (_newrect == NULL) return false;
 		newrect = _newrect;
 	}
 	
@@ -1607,7 +1615,7 @@ struct ImBuf *IMB_scalefastImBuf(struct ImBuf *ibuf, unsigned int newx, unsigned
 		_newrectf = MEM_mallocN(newx * newy * sizeof(float) * 4, "scalefastimbuf f");
 		if (_newrectf == NULL) {
 			if (_newrect) MEM_freeN(_newrect);
-			return(ibuf);
+			return false;
 		}
 		newrectf = _newrectf;
 	}
@@ -1643,18 +1651,18 @@ struct ImBuf *IMB_scalefastImBuf(struct ImBuf *ibuf, unsigned int newx, unsigned
 		ibuf->mall |= IB_rect;
 		ibuf->rect = _newrect;
 	}
-	
+
 	if (do_float) {
 		imb_freerectfloatImBuf(ibuf);
 		ibuf->mall |= IB_rectfloat;
 		ibuf->rect_float = (float *)_newrectf;
 	}
-	
+
 	scalefast_Z_ImBuf(ibuf, newx, newy);
-	
+
 	ibuf->x = newx;
 	ibuf->y = newy;
-	return(ibuf);
+	return true;
 }
 
 /* ******** threaded scaling ******** */
diff --git a/source/blender/imbuf/intern/thumbs.c b/source/blender/imbuf/intern/thumbs.c
index ba72decf44d3326403a7c2b9a799ee3d72b39549..86e1fba9af79c44bb69d77753cff5c943a20a2da 100644
--- a/source/blender/imbuf/intern/thumbs.c
+++ b/source/blender/imbuf/intern/thumbs.c
@@ -430,16 +430,17 @@ static ImBuf *thumb_create_ex(
 			IMB_scaleImBuf(img, ex, ey);
 		}
 		BLI_snprintf(desc, sizeof(desc), "Thumbnail for %s", uri);
-		IMB_metadata_change_field(img, "Description", desc);
-		IMB_metadata_change_field(img, "Software", "Blender");
-		IMB_metadata_change_field(img, "Thumb::URI", uri);
-		IMB_metadata_change_field(img, "Thumb::MTime", mtime);
+		IMB_metadata_ensure(&img->metadata);
+		IMB_metadata_set_field(img->metadata, "Software", "Blender");
+		IMB_metadata_set_field(img->metadata, "Thumb::URI", uri);
+		IMB_metadata_set_field(img->metadata, "Description", desc);
+		IMB_metadata_set_field(img->metadata, "Thumb::MTime", mtime);
 		if (use_hash) {
-			IMB_metadata_change_field(img, "X-Blender::Hash", hash);
+			IMB_metadata_set_field(img->metadata, "X-Blender::Hash", hash);
 		}
 		if (ELEM(source, THB_SOURCE_IMAGE, THB_SOURCE_BLEND, THB_SOURCE_FONT)) {
-			IMB_metadata_change_field(img, "Thumb::Image::Width", cwidth);
-			IMB_metadata_change_field(img, "Thumb::Image::Height", cheight);
+			IMB_metadata_set_field(img->metadata, "Thumb::Image::Width", cwidth);
+			IMB_metadata_set_field(img->metadata, "Thumb::Image::Height", cheight);
 		}
 		img->ftype = IMB_FTYPE_PNG;
 		img->planes = 32;
@@ -589,7 +590,7 @@ ImBuf *IMB_thumb_manage(const char *org_path, ThumbSize size, ThumbSource source
 
 				const bool use_hash = thumbhash_from_path(file_path, source, thumb_hash);
 
-				if (IMB_metadata_get_field(img, "Thumb::MTime", mtime, sizeof(mtime))) {
+				if (IMB_metadata_get_field(img->metadata, "Thumb::MTime", mtime, sizeof(mtime))) {
 					regenerate = (st.st_mtime != atol(mtime));
 				}
 				else {
@@ -598,7 +599,7 @@ ImBuf *IMB_thumb_manage(const char *org_path, ThumbSize size, ThumbSource source
 				}
 
 				if (use_hash && !regenerate) {
-					if (IMB_metadata_get_field(img, "X-Blender::Hash", thumb_hash_curr, sizeof(thumb_hash_curr))) {
+					if (IMB_metadata_get_field(img->metadata, "X-Blender::Hash", thumb_hash_curr, sizeof(thumb_hash_curr))) {
 						regenerate = !STREQ(thumb_hash, thumb_hash_curr);
 					}
 					else {
diff --git a/source/blender/imbuf/intern/thumbs_blend.c b/source/blender/imbuf/intern/thumbs_blend.c
index 00fb1a44dff4007f0ea27f7cccf1339a2d193027..efcd7d1f35fa3a47677a072dfe9ef8e49d1d6213 100644
--- a/source/blender/imbuf/intern/thumbs_blend.c
+++ b/source/blender/imbuf/intern/thumbs_blend.c
@@ -37,7 +37,6 @@
 #include "BLO_blend_defs.h"
 #include "BLO_readfile.h"
 
-#include "BKE_global.h"
 #include "BKE_idcode.h"
 #include "BKE_icons.h"
 #include "BKE_library.h"
diff --git a/source/blender/imbuf/intern/util.c b/source/blender/imbuf/intern/util.c
index 76a44aa81f7e4f64ffb58e018f386eca855fff9c..9dfe926ddffa77ad1c89e4181e10c440966ee8f4 100644
--- a/source/blender/imbuf/intern/util.c
+++ b/source/blender/imbuf/intern/util.c
@@ -43,8 +43,6 @@
 #include "BLI_fileops.h"
 #include "BLI_string.h"
 
-#include "BKE_global.h"
-
 #include "imbuf.h"
 #include "IMB_imbuf_types.h"
 #include "IMB_imbuf.h"
@@ -53,6 +51,7 @@
 #include "IMB_anim.h"
 
 #ifdef WITH_FFMPEG
+#include "BKE_global.h"  /* G.debug */
 #include <libavcodec/avcodec.h>
 #include <libavformat/avformat.h>
 #include <libavdevice/avdevice.h>
diff --git a/source/blender/imbuf/intern/writeimage.c b/source/blender/imbuf/intern/writeimage.c
index 84ec2534e7f68bebb039580894d2dbfb8a242321..e340d082895693ab419be32ed50454589a9f258c 100644
--- a/source/blender/imbuf/intern/writeimage.c
+++ b/source/blender/imbuf/intern/writeimage.c
@@ -46,7 +46,7 @@
 #include "IMB_colormanagement.h"
 #include "IMB_colormanagement_intern.h"
 
-static ImBuf *prepare_write_imbuf(const ImFileType *type, ImBuf *ibuf)
+static bool prepare_write_imbuf(const ImFileType *type, ImBuf *ibuf)
 {
 	return IMB_prepare_write_ImBuf((type->flag & IM_FTYPE_FLOAT), ibuf);
 }
@@ -64,15 +64,11 @@ short IMB_saveiff(struct ImBuf *ibuf, const char *name, int flags)
 
 	for (type = IMB_FILE_TYPES; type < IMB_FILE_TYPES_LAST; type++) {
 		if (type->save && type->ftype(type, ibuf)) {
-			ImBuf *write_ibuf;
 			short result = false;
 
-			write_ibuf = prepare_write_imbuf(type, ibuf);
+			prepare_write_imbuf(type, ibuf);
 
-			result = type->save(write_ibuf, name, flags);
-
-			if (write_ibuf != ibuf)
-				IMB_freeImBuf(write_ibuf);
+			result = type->save(ibuf, name, flags);
 
 			return result;
 		}
@@ -83,9 +79,9 @@ short IMB_saveiff(struct ImBuf *ibuf, const char *name, int flags)
 	return false;
 }
 
-ImBuf *IMB_prepare_write_ImBuf(const bool isfloat, ImBuf *ibuf)
+bool IMB_prepare_write_ImBuf(const bool isfloat, ImBuf *ibuf)
 {
-	ImBuf *write_ibuf = ibuf;
+	bool changed = false;
 
 	if (isfloat) {
 		/* pass */
@@ -94,8 +90,11 @@ ImBuf *IMB_prepare_write_ImBuf(const bool isfloat, ImBuf *ibuf)
 		if (ibuf->rect == NULL && ibuf->rect_float) {
 			ibuf->rect_colorspace = colormanage_colorspace_get_roled(COLOR_ROLE_DEFAULT_BYTE);
 			IMB_rect_from_float(ibuf);
+			if (ibuf->rect != NULL) {
+				changed = true;
+			}
 		}
 	}
 
-	return write_ibuf;
+	return changed;
 }
diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h
index c8780c6b4b8d970e31345bb01809bc6802b77210..95cbdeadf875ca383d1fc2bc290d4b8ea7a240ed 100644
--- a/source/blender/makesdna/DNA_action_types.h
+++ b/source/blender/makesdna/DNA_action_types.h
@@ -393,7 +393,12 @@ typedef enum eRotationModes {
 typedef struct bPose {
 	ListBase chanbase;          /* list of pose channels, PoseBones in RNA */
 	struct GHash *chanhash;     /* ghash for quicker string lookups */
-	
+
+	/* Flat array of pose channels. It references pointers from
+	 * chanbase. Used for quick pose channel lookup from an index.
+	 */
+	bPoseChannel **chan_array;
+
 	short flag, pad;
 	unsigned int proxy_layer;   /* proxy layer: copy from armature, gets synced */
 	int pad1;
diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h
index 0b7dccee9ab4ed329a1a5c8cb29dc7988af54c8f..74469acd27596b841133bc36d8c274d8ec1af4bf 100644
--- a/source/blender/makesdna/DNA_object_types.h
+++ b/source/blender/makesdna/DNA_object_types.h
@@ -172,6 +172,9 @@ typedef struct Object {
 	ListBase modifiers; /* list of ModifierData structures */
 	ListBase fmaps;     /* list of facemaps */
 
+	int mode;           /* Local object mode */
+	int restore_mode;
+
 	/* materials */
 	struct Material **mat;	/* material slots */
 	char *matbits;			/* a boolean field, with each byte 1 if corresponding material is linked to object */
diff --git a/source/blender/makesdna/DNA_particle_types.h b/source/blender/makesdna/DNA_particle_types.h
index a7be2e37c4b5f0880de9597287f416e18055b6c4..5f86ec31d0bcb0f2cc86aca1004433dd65e75734 100644
--- a/source/blender/makesdna/DNA_particle_types.h
+++ b/source/blender/makesdna/DNA_particle_types.h
@@ -262,7 +262,7 @@ typedef struct ParticleSettings {
 	short use_modifier_stack;
 
 	short pad5;
-	int recalc;
+	int pad8;
 
 	float twist;
 	float pad6;
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index bc3fd46e1a9db6e8bfd8e0e64e9fd46da259c403..3575ad976a5db6a1d9fa67148e05b522073df474 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -1086,7 +1086,6 @@ typedef struct ParticleEditSettings {
 	int draw_step, fade_frames;
 
 	struct Scene *scene;
-	struct ViewLayer *view_layer;
 	struct Object *object;
 	struct Object *shape_object;
 } ParticleEditSettings;
@@ -1835,10 +1834,11 @@ enum {
 #define R_STAMP_STRIPMETA	0x1000
 #define R_STAMP_MEMORY		0x2000
 #define R_STAMP_HIDE_LABELS	0x4000
+#define R_STAMP_FRAME_RANGE	0x8000
 #define R_STAMP_ALL (R_STAMP_TIME|R_STAMP_FRAME|R_STAMP_DATE|R_STAMP_CAMERA|R_STAMP_SCENE| \
                      R_STAMP_NOTE|R_STAMP_MARKER|R_STAMP_FILENAME|R_STAMP_SEQSTRIP|        \
                      R_STAMP_RENDERTIME|R_STAMP_CAMERALENS|R_STAMP_MEMORY|                 \
-                     R_STAMP_HIDE_LABELS)
+                     R_STAMP_HIDE_LABELS|R_STAMP_FRAME_RANGE)
 
 /* RenderData.alphamode */
 #define R_ADDSKY		0
@@ -1947,11 +1947,10 @@ extern const char *RE_engine_id_CYCLES;
 
 #define OBEDIT_FROM_WORKSPACE(workspace, _view_layer) \
 	(((workspace)->object_mode & OD_MODE_EDIT) ? OBACT(_view_layer) : NULL)
-#define OBEDIT_FROM_EVAL_CTX(eval_ctx) \
-	(((eval_ctx)->object_mode & OB_MODE_EDIT) ? OBACT((eval_ctx)->view_layer) : NULL)
-
-#define OBEDIT_FROM_WINDOW(window) \
-	BKE_workspace_edit_object(WM_window_get_active_workspace(window), WM_window_get_active_scene(window))
+#define OBEDIT_FROM_OBACT(ob) \
+	((ob) ? (((ob)->mode & OB_MODE_EDIT) ? ob : NULL) : NULL)
+#define OBEDIT_FROM_VIEW_LAYER(view_layer) \
+	OBEDIT_FROM_OBACT(OBACT(view_layer))
 
 #define V3D_CAMERA_LOCAL(v3d) ((!(v3d)->scenelock && (v3d)->camera) ? (v3d)->camera : NULL)
 #define V3D_CAMERA_SCENE(scene, v3d) ((!(v3d)->scenelock && (v3d)->camera) ? (v3d)->camera : (scene)->camera)
diff --git a/source/blender/makesdna/DNA_text_types.h b/source/blender/makesdna/DNA_text_types.h
index 0a9b8c320b5a93cad26756ee4ff8aa970ea63212..163dda678d9f967554f07c9d65a9ff0926b6548f 100644
--- a/source/blender/makesdna/DNA_text_types.h
+++ b/source/blender/makesdna/DNA_text_types.h
@@ -59,10 +59,6 @@ typedef struct Text {
 	TextLine *curl, *sell;
 	int curc, selc;
 	
-	char *undo_buf;
-	void *pad;
-	int undo_pos, undo_len;
-
 	double mtime;
 } Text;
 
diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h
index cc1db43758b01f4d2d442a0a0c6eb48db54c52f9..f03ff4ba8b72f3d21bd8177c589f6add4364ac1e 100644
--- a/source/blender/makesdna/DNA_windowmanager_types.h
+++ b/source/blender/makesdna/DNA_windowmanager_types.h
@@ -59,6 +59,7 @@ struct ReportList;
 struct Report;
 struct uiLayout;
 struct Stereo3dFormat;
+struct UndoStep;
 
 #define OP_MAX_TYPENAME 64
 #define KMAP_MAX_NAME   64
@@ -155,6 +156,8 @@ typedef struct wmWindowManager {
 	ListBase timers;                  /* active timers */
 	struct wmTimer *autosavetimer;    /* timer for auto save */
 
+	struct UndoStack *undo_stack;     /* all undo history (runtime only). */
+
 	char is_interface_locked;		/* indicates whether interface is locked for user interaction */
 	char par[7];
 
diff --git a/source/blender/makesdna/DNA_workspace_types.h b/source/blender/makesdna/DNA_workspace_types.h
index 894119b1e72198aecc35c050655b7b56a38a1094..82aa7b8b81cc00117f2176fa5d489bb4946b6551 100644
--- a/source/blender/makesdna/DNA_workspace_types.h
+++ b/source/blender/makesdna/DNA_workspace_types.h
@@ -101,9 +101,6 @@ typedef struct WorkSpace {
 	int pad;
 	int flags DNA_PRIVATE_WORKSPACE; /* enum eWorkSpaceFlags */
 
-	short object_mode, object_mode_restore;
-	char _pad[4];
-
 	/* should be: '#ifdef USE_WORKSPACE_TOOL'. */
 	bToolDef tool;
 
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index 1058099e9ef7fb9c970b1ca4a4c14279279ebaa4..562855c01ccbfeecbf20db20a59a128e7ff6ebdb 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -294,6 +294,8 @@ extern StructRNA RNA_HemiLamp;
 extern StructRNA RNA_Histogram;
 extern StructRNA RNA_HookModifier;
 extern StructRNA RNA_ID;
+extern StructRNA RNA_IDOverrideStatic;
+extern StructRNA RNA_IDOverrideStaticProperty;
 extern StructRNA RNA_IKParam;
 extern StructRNA RNA_Image;
 extern StructRNA RNA_ImageFormatSettings;
@@ -898,6 +900,9 @@ bool RNA_property_editable_index(PointerRNA *ptr, PropertyRNA *prop, int index);
 bool RNA_property_editable_flag(PointerRNA *ptr, PropertyRNA *prop); /* without lib check, only checks the flag */
 bool RNA_property_animateable(PointerRNA *ptr, PropertyRNA *prop);
 bool RNA_property_animated(PointerRNA *ptr, PropertyRNA *prop);
+bool RNA_property_overridable(PointerRNA *ptr, PropertyRNA *prop);
+bool RNA_property_overridden(PointerRNA *ptr, PropertyRNA *prop);
+bool RNA_property_comparable(PointerRNA *ptr, PropertyRNA *prop);
 bool RNA_property_path_from_ID_check(PointerRNA *ptr, PropertyRNA *prop); /* slow, use with care */
 
 void RNA_property_update(struct bContext *C, PointerRNA *ptr, PropertyRNA *prop);
diff --git a/source/blender/makesrna/RNA_types.h b/source/blender/makesrna/RNA_types.h
index 57eeb9e11e0e3e4e936b38a00c81e65f07496430..77d9aabd661dce8ede540c8255902047343b421f 100644
--- a/source/blender/makesrna/RNA_types.h
+++ b/source/blender/makesrna/RNA_types.h
@@ -158,7 +158,7 @@ typedef enum PropertySubType {
 
 /* Make sure enums are updated with these */
 /* HIGHEST FLAG IN USE: 1 << 31
- * FREE FLAGS: 3, 9, 11, 13, 14, 15, 30 */
+ * FREE FLAGS: 9, 11, 13, 14, 15, 30 */
 typedef enum PropertyFlag {
 	/* editable means the property is editable in the user
 	 * interface, properties are editable by default except
@@ -179,6 +179,10 @@ typedef enum PropertyFlag {
 	/* Means the property can be overriden by a local 'proxy' of some linked datablock. */
 	PROP_OVERRIDABLE_STATIC      = (1 << 2),
 
+	/* Forbid usage of this property in comparison (& hence override) code.
+	 * Useful e.g. for collections of data like mesh's geometry, particles, etc. */
+	PROP_NO_COMPARISON           = (1 << 3),
+
 	/* This flag means when the property's widget is in 'textedit' mode, it will be updated
 	 * after every typed char, instead of waiting final validation. Used e.g. for text searchbox.
 	 * It will also cause UI_BUT_VALUE_CLEAR to be set for text buttons. We could add an own flag
diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c
index 549c1a8b30d34a70e73795300fe07849bdc4ccb1..088b2b67af5bacede78ea2d9e8c6439b810d0b07 100644
--- a/source/blender/makesrna/intern/rna_ID.c
+++ b/source/blender/makesrna/intern/rna_ID.c
@@ -793,12 +793,12 @@ static int rna_ID_is_updated_data_get(PointerRNA *ptr)
 	return ((data->recalc & ID_RECALC_ALL) != 0);
 }
 
-static PointerRNA rna_ID_override_reference_get(PointerRNA *ptr)
+static IDProperty *rna_IDPropertyWrapPtr_idprops(PointerRNA *ptr, bool UNUSED(create))
 {
-	ID *id = (ID *)ptr->data;
-	ID *reference = (id && id->override_static) ? id->override_static->reference : NULL;
-
-	return reference ? rna_pointer_inherit_refine(ptr, ID_code_to_RNA_type(GS(reference->name)), reference) : PointerRNA_NULL;
+	if (ptr == NULL) {
+		return NULL;
+	}
+	return ptr->data;
 }
 
 #else
@@ -997,6 +997,33 @@ static void rna_def_image_preview(BlenderRNA *brna)
 	RNA_def_function_ui_description(func, "Reload the preview from its source path");
 }
 
+static void rna_def_ID_override_static_property(BlenderRNA *brna)
+{
+	StructRNA *srna;
+	PropertyRNA *prop;
+
+	srna = RNA_def_struct(brna, "IDOverrideStaticProperty", NULL);
+	RNA_def_struct_ui_text(srna, "ID Static Override Property", "Description of an overridden property");
+
+	prop = RNA_def_string(srna, "rna_path", NULL, INT_MAX, "RNA Path", "RNA path leading to that property, from owning ID");
+	RNA_def_property_clear_flag(prop, PROP_EDITABLE);  /* For now. */
+}
+
+	static void rna_def_ID_override_static(BlenderRNA *brna)
+{
+	StructRNA *srna;
+
+	srna = RNA_def_struct(brna, "IDOverrideStatic", NULL);
+	RNA_def_struct_ui_text(srna, "ID Static Override", "Struct gathering all data needed by statically overridden IDs");
+
+	RNA_def_pointer(srna, "reference", "ID", "Reference ID", "Linked ID used as reference by this override");
+
+	RNA_def_collection(srna, "properties", "IDOverrideStaticProperty", "Properties",
+	                   "List of overridden properties");
+
+	rna_def_ID_override_static_property(brna);
+}
+
 static void rna_def_ID(BlenderRNA *brna)
 {
 	StructRNA *srna;
@@ -1063,11 +1090,8 @@ static void rna_def_ID(BlenderRNA *brna)
 	RNA_def_property_clear_flag(prop, PROP_EDITABLE);
 	RNA_def_property_ui_text(prop, "Library", "Library file the data-block is linked from");
 
-	prop = RNA_def_pointer(srna, "override_static_reference", "ID",
-	                       "Override Reference", "Reference linked data-block overridden by this one");
-	RNA_def_property_pointer_sdna(prop, NULL, "override_static->reference");
+	prop = RNA_def_pointer(srna, "override_static", "IDOverrideStatic", "Static Override", "Static override data");
 	RNA_def_property_clear_flag(prop, PROP_EDITABLE);
-	RNA_def_property_pointer_funcs(prop, "rna_ID_override_reference_get", NULL, NULL, NULL);
 
 	prop = RNA_def_pointer(srna, "preview", "ImagePreview", "Preview",
 	                       "Preview image and icon of this data-block (None if not supported for this type of data)");
@@ -1164,6 +1188,20 @@ static void rna_def_library(BlenderRNA *brna)
 	RNA_def_function_flag(func, FUNC_USE_REPORTS | FUNC_USE_CONTEXT);
 	RNA_def_function_ui_description(func, "Reload this library and all its linked data-blocks");
 }
+
+/**
+ * \attention This is separate from the above. It allows for RNA functions to
+ * return an IDProperty *. See MovieClip.metadata for a usage example.
+ **/
+static void rna_def_idproperty_wrap_ptr(BlenderRNA *brna)
+{
+	StructRNA *srna;
+
+	srna = RNA_def_struct(brna, "IDPropertyWrapPtr", NULL);
+	RNA_def_struct_idprops_func(srna, "rna_IDPropertyWrapPtr_idprops");
+	RNA_def_struct_flag(srna, STRUCT_NO_DATABLOCK_IDPROPERTIES);
+}
+
 void RNA_def_ID(BlenderRNA *brna)
 {
 	StructRNA *srna;
@@ -1177,10 +1215,12 @@ void RNA_def_ID(BlenderRNA *brna)
 	RNA_def_struct_ui_text(srna, "Any Type", "RNA type used for pointers to any possible data");
 
 	rna_def_ID(brna);
+	rna_def_ID_override_static(brna);
 	rna_def_image_preview(brna);
 	rna_def_ID_properties(brna);
 	rna_def_ID_materials(brna);
 	rna_def_library(brna);
+	rna_def_idproperty_wrap_ptr(brna);
 }
 
 #endif
diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c
index b63cebbdde247ae69a60557bf00422fed2b1894f..bb8ee89c5abbaf8266e658ec22646121c86843a5 100644
--- a/source/blender/makesrna/intern/rna_access.c
+++ b/source/blender/makesrna/intern/rna_access.c
@@ -1843,7 +1843,7 @@ bool RNA_property_editable(PointerRNA *ptr, PropertyRNA *prop)
 	return ((flag & PROP_EDITABLE) &&
 	        (flag & PROP_REGISTER) == 0 &&
 	        (!id || ((!ID_IS_LINKED(id) || (prop->flag & PROP_LIB_EXCEPTION)) &&
-	                 (!id->override_static || (prop->flag & PROP_OVERRIDABLE_STATIC)))));
+	                 (!id->override_static || RNA_property_overridable(ptr, prop)))));
 }
 
 /**
@@ -1876,7 +1876,7 @@ bool RNA_property_editable_info(PointerRNA *ptr, PropertyRNA *prop, const char *
 			}
 			return false;
 		}
-		if (id->override_static != NULL && (prop->flag & PROP_OVERRIDABLE_STATIC) == 0) {
+		if (id->override_static != NULL && !RNA_property_overridable(ptr, prop)) {
 			if (!(*r_info)[0]) {
 				*r_info = "Can't edit this property from an override data-block.";
 			}
@@ -1955,6 +1955,34 @@ bool RNA_property_animated(PointerRNA *ptr, PropertyRNA *prop)
 	return false;
 }
 
+/** \note Does not take into account editable status, this has to be checked separately
+ * (using RNA_property_edtiable_flag() usually). */
+bool RNA_property_overridable(PointerRNA *UNUSED(ptr), PropertyRNA *prop)
+{
+	prop = rna_ensure_property(prop);
+
+	return !(prop->flag & PROP_NO_COMPARISON) && (prop->flag & PROP_OVERRIDABLE_STATIC);
+}
+
+bool RNA_property_overridden(PointerRNA *ptr, PropertyRNA *prop)
+{
+	char *rna_path = RNA_path_from_ID_to_property(ptr, prop);
+	ID *id = ptr->id.data;
+
+	if (rna_path == NULL || id == NULL || id->override_static == NULL) {
+		return false;
+	}
+
+	return (BKE_override_static_property_find(id->override_static, rna_path) != NULL);
+}
+
+bool RNA_property_comparable(PointerRNA *UNUSED(ptr), PropertyRNA *prop)
+{
+	prop = rna_ensure_property(prop);
+
+	return !(prop->flag & PROP_NO_COMPARISON);
+}
+
 /* this function is to check if its possible to create a valid path from the ID
  * its slow so don't call in a loop */
 bool RNA_property_path_from_ID_check(PointerRNA *ptr, PropertyRNA *prop)
@@ -7168,6 +7196,10 @@ static int rna_property_override_diff(
 		return (prop_a == prop_b) ? 0 : 1;
 	}
 
+	if (!RNA_property_comparable(ptr_a, prop_a) || !RNA_property_comparable(ptr_b, prop_b)) {
+		return 0;
+	}
+
 	if (mode == RNA_EQ_UNSET_MATCH_ANY) {
 		/* uninitialized properties are assumed to match anything */
 		if (!RNA_property_is_set(ptr_a, prop_a) || !RNA_property_is_set(ptr_b, prop_b)) {
@@ -7245,7 +7277,7 @@ static int rna_property_override_diff(
 
 	bool override_changed = false;
 	int diff_flags = flags;
-	if ((RNA_property_flag(prop_a) & PROP_OVERRIDABLE_STATIC) == 0) {
+	if (!RNA_property_overridable(ptr_a, prop_a)) {
 		diff_flags &= ~RNA_OVERRIDE_COMPARE_CREATE;
 	}
 	const int diff = override_diff(
@@ -7431,7 +7463,7 @@ bool RNA_struct_override_matches(
 			continue;
 		}
 
-		if (ignore_non_overridable && !(prop_local->flag & PROP_OVERRIDABLE_STATIC)) {
+		if (ignore_non_overridable && !RNA_property_overridable(ptr_local, prop_local)) {
 			continue;
 		}
 
@@ -7690,7 +7722,7 @@ eRNAOverrideStatus RNA_property_override_status(PointerRNA *ptr, PropertyRNA *pr
 		return override_status;
 	}
 
-	if ((prop->flag & PROP_OVERRIDABLE_STATIC) && (prop->flag & PROP_EDITABLE)) {
+	if (RNA_property_overridable(ptr, prop) && RNA_property_editable_flag(ptr, prop)) {
 		override_status |= RNA_OVERRIDE_STATUS_OVERRIDABLE;
 	}
 
diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c
index ca8abdc8b48bbca80c3a49bcb87f230e13209cdd..1ad33b683b7d28b3aa1f1f77ca21ca0843b4a9d1 100644
--- a/source/blender/makesrna/intern/rna_brush.c
+++ b/source/blender/makesrna/intern/rna_brush.c
@@ -31,7 +31,6 @@
 #include "DNA_texture_types.h"
 #include "DNA_scene_types.h"
 #include "DNA_object_types.h"
-#include "DNA_workspace_types.h"
 
 #include "BLI_math.h"
 
@@ -44,8 +43,6 @@
 
 #include "WM_types.h"
 
-#include "DEG_depsgraph.h"
-
 static const EnumPropertyItem prop_direction_items[] = {
 	{0, "ADD", 0, "Add", "Add effect of brush"},
 	{BRUSH_DIR_IN, "SUBTRACT", 0, "Subtract", "Subtract effect of brush"},
@@ -376,23 +373,21 @@ static void rna_Brush_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerR
 
 static void rna_Brush_main_tex_update(bContext *C, PointerRNA *ptr)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Main *bmain = CTX_data_main(C);
 	Scene *scene = CTX_data_scene(C);
 	ViewLayer *view_layer = CTX_data_view_layer(C);
 	Brush *br = (Brush *)ptr->data;
-	BKE_paint_invalidate_overlay_tex(scene, view_layer, br->mtex.tex, workspace->object_mode);
+	BKE_paint_invalidate_overlay_tex(scene, view_layer, br->mtex.tex);
 	rna_Brush_update(bmain, scene, ptr);
 }
 
 static void rna_Brush_secondary_tex_update(bContext *C, PointerRNA *ptr)
 {
 	Main *bmain = CTX_data_main(C);
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Scene *scene = CTX_data_scene(C);
 	ViewLayer *view_layer = CTX_data_view_layer(C);
 	Brush *br = (Brush *)ptr->data;
-	BKE_paint_invalidate_overlay_tex(scene, view_layer, br->mask_mtex.tex, workspace->object_mode);
+	BKE_paint_invalidate_overlay_tex(scene, view_layer, br->mask_mtex.tex);
 	rna_Brush_update(bmain, scene, ptr);
 }
 
@@ -453,9 +448,8 @@ static void rna_TextureSlot_brush_angle_update(bContext *C, PointerRNA *ptr)
 	MTex *mtex = ptr->data;
 	/* skip invalidation of overlay for stencil mode */
 	if (mtex->mapping != MTEX_MAP_MODE_STENCIL) {
-		const WorkSpace *workspace = CTX_wm_workspace(C);
 		ViewLayer *view_layer = CTX_data_view_layer(C);
-		BKE_paint_invalidate_overlay_tex(scene, view_layer, mtex->tex, workspace->object_mode);
+		BKE_paint_invalidate_overlay_tex(scene, view_layer, mtex->tex);
 	}
 
 	rna_TextureSlot_update(C, ptr);
diff --git a/source/blender/makesrna/intern/rna_depsgraph.c b/source/blender/makesrna/intern/rna_depsgraph.c
index bb20fcb271f35a5d359712ed371566bac0329403..04e7f64712ed125d1f6f6cc78a2d3ef82c9e39eb 100644
--- a/source/blender/makesrna/intern/rna_depsgraph.c
+++ b/source/blender/makesrna/intern/rna_depsgraph.c
@@ -81,6 +81,14 @@ static PointerRNA rna_DepsgraphIter_parent_get(PointerRNA *ptr)
 	return rna_pointer_inherit_refine(ptr, &RNA_Object, dupli_parent);
 }
 
+static PointerRNA rna_DepsgraphIter_particle_system_get(PointerRNA *ptr)
+{
+	BLI_Iterator *iterator = ptr->data;
+	DEGObjectIterData *deg_iter = (DEGObjectIterData *)iterator->data;
+	return rna_pointer_inherit_refine(ptr, &RNA_ParticleSystem,
+		deg_iter->dupli_object_current->particle_system);
+}
+
 static void rna_DepsgraphIter_persistent_id_get(PointerRNA *ptr, int *persistent_id)
 {
 	BLI_Iterator *iterator = ptr->data;
@@ -277,6 +285,12 @@ static void rna_def_depsgraph_iter(BlenderRNA *brna)
 	RNA_def_property_clear_flag(prop, PROP_ANIMATABLE | PROP_EDITABLE);
 	RNA_def_property_pointer_funcs(prop, "rna_DepsgraphIter_parent_get", NULL, NULL, NULL);
 
+	prop = RNA_def_property(srna, "particle_system", PROP_POINTER, PROP_NONE);
+	RNA_def_property_struct_type(prop, "ParticleSystem");
+	RNA_def_property_clear_flag(prop, PROP_ANIMATABLE | PROP_EDITABLE);
+	RNA_def_property_ui_text(prop, "Particle System", "Particle system that this object was instanced from");
+	RNA_def_property_pointer_funcs(prop, "rna_DepsgraphIter_particle_system_get", NULL, NULL, NULL);
+
 	prop = RNA_def_property(srna, "persistent_id", PROP_INT, PROP_NONE);
 	RNA_def_property_ui_text(prop, "Persistent ID",
 	                         "Persistent identifier for inter-frame matching of objects with motion blur");
diff --git a/source/blender/makesrna/intern/rna_layer.c b/source/blender/makesrna/intern/rna_layer.c
index 9992ab062114facff16501bceff0caa0a950e8da..7d09f30ed6ab34794621a0355fb34745bd1d59e8 100644
--- a/source/blender/makesrna/intern/rna_layer.c
+++ b/source/blender/makesrna/intern/rna_layer.c
@@ -64,7 +64,6 @@ const EnumPropertyItem rna_enum_collection_type_items[] = {
 
 #include "DNA_group_types.h"
 #include "DNA_object_types.h"
-#include "DNA_workspace_types.h"
 
 #include "RNA_access.h"
 
@@ -73,8 +72,6 @@ const EnumPropertyItem rna_enum_collection_type_items[] = {
 #include "BKE_node.h"
 #include "BKE_scene.h"
 #include "BKE_mesh.h"
-#include "BKE_object.h"
-#include "BKE_workspace.h"
 
 #include "DEG_depsgraph_build.h"
 #include "DEG_depsgraph_query.h"
@@ -838,18 +835,6 @@ static void rna_LayerObjects_active_object_set(PointerRNA *ptr, PointerRNA value
 		view_layer->basact = NULL;
 }
 
-static void rna_LayerObjects_active_object_update(struct bContext *C, PointerRNA *ptr)
-{
-	wmWindow *win = CTX_wm_window(C);
-	Scene *scene = WM_window_get_active_scene(win);
-
-	if (scene != ptr->id.data) {
-		return;
-	}
-	ViewLayer *view_layer = (ViewLayer *)ptr->data;
-	ED_object_base_activate(C, view_layer->basact);
-}
-
 static IDProperty *rna_ViewLayer_idprops(PointerRNA *ptr, bool create)
 {
 	ViewLayer *view_layer = (ViewLayer *)ptr->data;
@@ -2145,11 +2130,11 @@ static void rna_def_layer_objects(BlenderRNA *brna, PropertyRNA *cprop)
 	prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE);
 	RNA_def_property_struct_type(prop, "Object");
 	RNA_def_property_pointer_funcs(prop, "rna_LayerObjects_active_object_get", "rna_LayerObjects_active_object_set", NULL, NULL);
-	RNA_def_property_flag(prop, PROP_EDITABLE | PROP_NEVER_UNLINK | PROP_CONTEXT_UPDATE);
+	RNA_def_property_flag(prop, PROP_EDITABLE | PROP_NEVER_UNLINK);
 	RNA_def_property_ui_text(prop, "Active Object", "Active object for this layer");
 	/* Could call: ED_object_base_activate(C, rl->basact);
 	 * but would be a bad level call and it seems the notifier is enough */
-	RNA_def_property_update(prop, NC_SCENE | ND_OB_ACTIVE, "rna_LayerObjects_active_object_update");
+	RNA_def_property_update(prop, NC_SCENE | ND_OB_ACTIVE, NULL);
 
 	prop = RNA_def_property(srna, "selected", PROP_COLLECTION, PROP_NONE);
 	RNA_def_property_collection_sdna(prop, NULL, "object_bases", NULL);
diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c
index 6f23b0fd281eb2e369b7edf48e9d2933a4ca07ae..bf6e13e587c7b554c5dc82704b39794ced774569 100644
--- a/source/blender/makesrna/intern/rna_material.c
+++ b/source/blender/makesrna/intern/rna_material.c
@@ -207,16 +207,23 @@ static void rna_Material_active_paint_texture_index_update(Main *bmain, Scene *s
 			if (win == NULL) {
 				continue;
 			}
-			Object *obedit = OBEDIT_FROM_WINDOW(win);
+
+			Object *obedit = NULL;
+			{
+				WorkSpace *workspace = WM_window_get_active_workspace(win);
+				ViewLayer *view_layer = BKE_workspace_view_layer_get(workspace, scene);
+				obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
+			}
+
 			ScrArea *sa;
 			for (sa = sc->areabase.first; sa; sa = sa->next) {
 				SpaceLink *sl;
 				for (sl = sa->spacedata.first; sl; sl = sl->next) {
 					if (sl->spacetype == SPACE_IMAGE) {
 						SpaceImage *sima = (SpaceImage *)sl;
-						
-						if (!sima->pin)
+						if (!sima->pin) {
 							ED_space_image_set(sima, scene, obedit, image);
+						}
 					}
 				}
 			}
diff --git a/source/blender/makesrna/intern/rna_movieclip.c b/source/blender/makesrna/intern/rna_movieclip.c
index 850b1cd706b9a2ff6a9bdfd6e1a67a95e2ac8f0e..c663119eb42a66fce9b59a5aafa7e7416501dea3 100644
--- a/source/blender/makesrna/intern/rna_movieclip.c
+++ b/source/blender/makesrna/intern/rna_movieclip.c
@@ -33,6 +33,7 @@
 #include "DNA_movieclip_types.h"
 #include "DNA_scene_types.h"
 
+#include "RNA_access.h"
 #include "RNA_define.h"
 
 #include "rna_internal.h"
@@ -44,6 +45,7 @@
 
 #include "IMB_imbuf_types.h"
 #include "IMB_imbuf.h"
+#include "IMB_metadata.h"
 
 #ifdef RNA_RUNTIME
 
@@ -103,6 +105,22 @@ static void rna_MovieClipUser_proxy_render_settings_update(Main *UNUSED(bmain),
 	}
 }
 
+static PointerRNA rna_MovieClip_metadata_get(MovieClip *clip)
+{
+	if (clip == NULL || clip->anim == NULL) {
+		return PointerRNA_NULL;
+	}
+
+	IDProperty *metadata = IMB_anim_load_metadata(clip->anim);
+	if (metadata == NULL) {
+		return PointerRNA_NULL;
+	}
+
+	PointerRNA ptr;
+	RNA_pointer_create(NULL, &RNA_IDPropertyWrapPtr, metadata, &ptr);
+	return ptr;
+}
+
 #else
 
 static void rna_def_movieclip_proxy(BlenderRNA *brna)
@@ -257,6 +275,8 @@ static void rna_def_movieclip(BlenderRNA *brna)
 {
 	StructRNA *srna;
 	PropertyRNA *prop;
+	FunctionRNA *func;
+	PropertyRNA *parm;
 
 	static const EnumPropertyItem clip_source_items[] = {
 		{MCLIP_SRC_SEQUENCE, "SEQUENCE", 0, "Image Sequence", "Multiple image files, as a sequence"},
@@ -348,6 +368,14 @@ static void rna_def_movieclip(BlenderRNA *brna)
 	RNA_def_property_struct_type(prop, "ColorManagedInputColorspaceSettings");
 	RNA_def_property_ui_text(prop, "Color Space Settings", "Input color space settings");
 
+	/* metadata */
+	func = RNA_def_function(srna, "metadata", "rna_MovieClip_metadata_get");
+	RNA_def_function_ui_description(func, "Retrieve metadata of the movie file");
+	/* return type */
+	parm = RNA_def_pointer(func, "metadata", "IDPropertyWrapPtr", "", "Dict-like object containing the metadata");
+	RNA_def_parameter_flags(parm, 0, PARM_RNAPTR);
+	RNA_def_function_return(func, parm);
+
 	rna_def_animdata_common(srna);
 }
 
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index 55ae7fdb6afde041c5a3f1ea77418f68c9b9b56d..3268f0a7cf729e13d4e05076313ad8780976eb0e 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -2723,7 +2723,7 @@ static int rna_Node_image_has_views_get(PointerRNA *ptr)
 
 	if (!ima || !(ima->rr)) return 0;
 
-	return BLI_listbase_count_ex(&ima->rr->views, 2) > 1;
+	return BLI_listbase_count_at_most(&ima->rr->views, 2) > 1;
 }
 
 static const EnumPropertyItem *renderresult_views_add_enum(RenderView *rv)
diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c
index aa78b9e614d8e2f99c930d2f2cd1633e6517377c..e5de42c43fab744f590bcf77239d1a3325e0d280 100644
--- a/source/blender/makesrna/intern/rna_object.c
+++ b/source/blender/makesrna/intern/rna_object.c
@@ -347,7 +347,7 @@ static void rna_Object_data_set(PointerRNA *ptr, PointerRNA value)
 	Object *ob = (Object *)ptr->data;
 	ID *id = value.data;
 
-	if (BKE_object_is_in_editmode(ob)) {
+	if (ob->mode & OB_MODE_EDIT) {
 		return;
 	}
 
@@ -1404,9 +1404,7 @@ static void rna_Object_constraints_clear(Object *object)
 static ModifierData *rna_Object_modifier_new(Object *object, bContext *C, ReportList *reports,
                                              const char *name, int type)
 {
-	Main *bmain = CTX_data_main(C);
-	const WorkSpace *workspace = CTX_wm_workspace(C);
-	return ED_object_modifier_add(reports, bmain, CTX_data_scene(C), object, workspace->object_mode, name, type);
+	return ED_object_modifier_add(reports, CTX_data_main(C), CTX_data_scene(C), object, name, type);
 }
 
 static void rna_Object_modifier_remove(Object *object, bContext *C, ReportList *reports, PointerRNA *md_ptr)
@@ -2431,6 +2429,12 @@ static void rna_def_object(BlenderRNA *brna)
 	RNA_def_property_clear_flag(prop, PROP_EDITABLE);
 	RNA_def_property_ui_text(prop, "Type", "Type of Object");
 
+	prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
+	RNA_def_property_enum_sdna(prop, NULL, "mode");
+	RNA_def_property_enum_items(prop, rna_enum_object_mode_items);
+	RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+	RNA_def_property_ui_text(prop, "Mode", "Object interaction mode");
+
 	prop = RNA_def_property(srna, "layers_local_view", PROP_BOOLEAN, PROP_LAYER_MEMBER);
 	RNA_def_property_boolean_sdna(prop, NULL, "lay", 0x01000000);
 	RNA_def_property_array(prop, 8);
@@ -2835,6 +2839,7 @@ static void rna_def_object(BlenderRNA *brna)
 	                                  NULL,
 	                                  NULL);
 	RNA_def_property_struct_type(prop, "LayerCollectionSettings");
+	RNA_def_property_flag(prop, PROP_NO_COMPARISON);  /* XXX see T53800. */
 	RNA_def_property_ui_text(prop, "Collection Settings",
 	                         "Engine specific render settings to be overridden by collections");
 
diff --git a/source/blender/makesrna/intern/rna_object_force.c b/source/blender/makesrna/intern/rna_object_force.c
index 649cb523e92bde94ae79c1ee4258f3f94d745b29..c2797abbc75c89f2154033c16b3bb1c65430ad4c 100644
--- a/source/blender/makesrna/intern/rna_object_force.c
+++ b/source/blender/makesrna/intern/rna_object_force.c
@@ -53,7 +53,6 @@ static const EnumPropertyItem effector_shape_items[] = {
 
 #include "BLI_math_base.h"
 
-#include "BKE_main.h"
 
 /* type specific return values only used from functions */
 static const EnumPropertyItem curve_shape_items[] = {
@@ -524,7 +523,7 @@ static void rna_FieldSettings_shape_update(Main *bmain, Scene *scene, PointerRNA
 {
 	if (!particle_id_check(ptr)) {
 		Object *ob = (Object *)ptr->id.data;
-		ED_object_check_force_modifiers(bmain, scene, ob, bmain->eval_ctx->object_mode);
+		ED_object_check_force_modifiers(bmain, scene, ob);
 		WM_main_add_notifier(NC_OBJECT | ND_DRAW, ob);
 		WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ob);
 	}
@@ -711,7 +710,7 @@ static void rna_CollisionSettings_dependency_update(Main *bmain, Scene *scene, P
 
 	/* add/remove modifier as needed */
 	if (ob->pd->deflect && !md)
-		ED_object_modifier_add(NULL, bmain, scene, ob, bmain->eval_ctx->object_mode, NULL, eModifierType_Collision);
+		ED_object_modifier_add(NULL, bmain, scene, ob, NULL, eModifierType_Collision);
 	else if (!ob->pd->deflect && md)
 		ED_object_modifier_remove(NULL, bmain, ob, md);
 
diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c
index 14bf612f46742bf73c369e7ca44721e9f597fe45..ecaf0d52ef2a4f4ea3119d93960529032ddb2188 100644
--- a/source/blender/makesrna/intern/rna_particle.c
+++ b/source/blender/makesrna/intern/rna_particle.c
@@ -3318,11 +3318,13 @@ static void rna_def_particle_system(BlenderRNA *brna)
 	prop = RNA_def_property(srna, "particles", PROP_COLLECTION, PROP_NONE);
 	RNA_def_property_collection_sdna(prop, NULL, "particles", "totpart");
 	RNA_def_property_struct_type(prop, "Particle");
+	RNA_def_property_flag(prop, PROP_NO_COMPARISON);
 	RNA_def_property_ui_text(prop, "Particles", "Particles generated by the particle system");
 
 	prop = RNA_def_property(srna, "child_particles", PROP_COLLECTION, PROP_NONE);
 	RNA_def_property_collection_sdna(prop, NULL, "child", "totchild");
 	RNA_def_property_struct_type(prop, "ChildParticle");
+	RNA_def_property_flag(prop, PROP_NO_COMPARISON);
 	RNA_def_property_ui_text(prop, "Child Particles", "Child particles generated by the particle system");
 
 	prop = RNA_def_property(srna, "seed", PROP_INT, PROP_UNSIGNED);
diff --git a/source/blender/makesrna/intern/rna_rna.c b/source/blender/makesrna/intern/rna_rna.c
index 9c043c3563a75574cee9be64f98be78ac680b64c..a46c8e2f18f8ba3d40cbcf79504b824ae3076984 100644
--- a/source/blender/makesrna/intern/rna_rna.c
+++ b/source/blender/makesrna/intern/rna_rna.c
@@ -638,7 +638,7 @@ static const EnumPropertyItem *rna_Property_tags_itemf(
 	int totitem = 0;
 
 	for (const EnumPropertyItem *struct_tags = RNA_struct_property_tag_defines(srna);
-	     struct_tags->identifier;
+	     struct_tags != NULL && struct_tags->identifier != NULL;
 	     struct_tags++)
 	{
 		memcpy(&tmp, struct_tags, sizeof(tmp));
@@ -1476,7 +1476,7 @@ int rna_property_override_diff_default(PointerRNA *ptr_a, PointerRNA *ptr_b,
 					}
 				}
 
-				if (equals) {
+				if (equals || do_create) {
 					const bool no_ownership = (RNA_property_flag(prop_a) & PROP_PTR_NO_OWNERSHIP) != 0;
 					const int eq = rna_property_override_diff_propptr(
 					              &iter_a.ptr, &iter_b.ptr, mode, no_ownership,
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index 955cc5cc884aef02a4f541d862d0eac1373e3446..f888d198e24c32e37f6567e1f27d4d8f6f8b8e0a 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -1703,10 +1703,9 @@ static KeyingSet *rna_Scene_keying_set_new(Scene *sce, ReportList *reports, cons
 
 static void rna_UnifiedPaintSettings_update(bContext *C, PointerRNA *UNUSED(ptr))
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Scene *scene = CTX_data_scene(C);
 	ViewLayer *view_layer = CTX_data_view_layer(C);
-	Brush *br = BKE_paint_brush(BKE_paint_get_active(scene, view_layer, workspace->object_mode));
+	Brush *br = BKE_paint_brush(BKE_paint_get_active(scene, view_layer));
 	WM_main_add_notifier(NC_BRUSH | NA_EDITED, br);
 }
 
@@ -1925,13 +1924,6 @@ char *rna_GPUDOF_path(PointerRNA *ptr)
 	return BLI_strdup("");
 }
 
-static void rna_GPUFXSettings_fx_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
-{
-	GPUFXSettings *fx_settings = ptr->data;
-
-	BKE_screen_gpu_fx_validate(fx_settings);
-}
-
 static void rna_GPUDOFSettings_blades_set(PointerRNA *ptr, const int value)
 {
 	GPUDOFSettings *dofsettings = (GPUDOFSettings *)ptr->data;
@@ -4839,7 +4831,7 @@ static void rna_def_gpu_fx(BlenderRNA *brna)
 	RNA_def_property_boolean_sdna(prop, NULL, "fx_flag", GPU_FX_FLAG_DOF);
 	RNA_def_property_ui_text(prop, "Depth Of Field",
 	                         "Use depth of field on viewport using the values from active camera");
-	RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_GPUFXSettings_fx_update");
+	RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
 
 
 	prop = RNA_def_property(srna, "ssao", PROP_POINTER, PROP_NONE);
@@ -4850,7 +4842,7 @@ static void rna_def_gpu_fx(BlenderRNA *brna)
 	prop = RNA_def_property(srna, "use_ssao", PROP_BOOLEAN, PROP_NONE);
 	RNA_def_property_boolean_sdna(prop, NULL, "fx_flag", GPU_FX_FLAG_SSAO);
 	RNA_def_property_ui_text(prop, "SSAO", "Use screen space ambient occlusion of field on viewport");
-	RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_GPUFXSettings_fx_update");
+	RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
 }
 
 static void rna_def_view_layers(BlenderRNA *brna, PropertyRNA *cprop)
@@ -5238,19 +5230,19 @@ static void rna_def_scene_ffmpeg_settings(BlenderRNA *brna)
 	};
 
 	static const EnumPropertyItem ffmpeg_codec_items[] = {
-		{AV_CODEC_ID_NONE, "NONE", 0, "None", ""},
-		{AV_CODEC_ID_MPEG1VIDEO, "MPEG1", 0, "MPEG-1", ""},
-		{AV_CODEC_ID_MPEG2VIDEO, "MPEG2", 0, "MPEG-2", ""},
-		{AV_CODEC_ID_MPEG4, "MPEG4", 0, "MPEG-4(divx)", ""},
-		{AV_CODEC_ID_HUFFYUV, "HUFFYUV", 0, "HuffYUV", ""},
+		{AV_CODEC_ID_NONE, "NONE", 0, "No Video", "Disables video output, for audio-only renders"},
+		{AV_CODEC_ID_DNXHD, "DNXHD", 0, "DNxHD", ""},
 		{AV_CODEC_ID_DVVIDEO, "DV", 0, "DV", ""},
-		{AV_CODEC_ID_H264, "H264", 0, "H.264", ""},
-		{AV_CODEC_ID_THEORA, "THEORA", 0, "Theora", ""},
-		{AV_CODEC_ID_FLV1, "FLASH", 0, "Flash Video", ""},
 		{AV_CODEC_ID_FFV1, "FFV1", 0, "FFmpeg video codec #1", ""},
-		{AV_CODEC_ID_QTRLE, "QTRLE", 0, "QT rle / QT Animation", ""},
-		{AV_CODEC_ID_DNXHD, "DNXHD", 0, "DNxHD", ""},
+		{AV_CODEC_ID_FLV1, "FLASH", 0, "Flash Video", ""},
+		{AV_CODEC_ID_H264, "H264", 0, "H.264", ""},
+		{AV_CODEC_ID_HUFFYUV, "HUFFYUV", 0, "HuffYUV", ""},
+		{AV_CODEC_ID_MPEG1VIDEO, "MPEG1", 0, "MPEG-1", ""},
+		{AV_CODEC_ID_MPEG2VIDEO, "MPEG2", 0, "MPEG-2", ""},
+		{AV_CODEC_ID_MPEG4, "MPEG4", 0, "MPEG-4 (divx)", ""},
 		{AV_CODEC_ID_PNG, "PNG", 0, "PNG", ""},
+		{AV_CODEC_ID_QTRLE, "QTRLE", 0, "QT rle / QT Animation", ""},
+		{AV_CODEC_ID_THEORA, "THEORA", 0, "Theora", ""},
 		{0, NULL, 0, NULL, NULL}
 	};
 
@@ -5268,8 +5260,8 @@ static void rna_def_scene_ffmpeg_settings(BlenderRNA *brna)
 	};
 
 	static const EnumPropertyItem ffmpeg_crf_items[] = {
-		{FFM_CRF_NONE, "NONE", 0, "None; use custom bitrate",
-		 "Use constant bit rate, rather than constant output quality"},
+		{FFM_CRF_NONE, "NONE", 0, "Constant Bitrate",
+		 "Configure constant bit rate, rather than constant output quality"},
 		{FFM_CRF_LOSSLESS, "LOSSLESS", 0, "Lossless", ""},
 		{FFM_CRF_PERC_LOSSLESS, "PERC_LOSSLESS", 0, "Perceptually lossless", ""},
 		{FFM_CRF_HIGH, "HIGH", 0, "High quality", ""},
@@ -5281,14 +5273,14 @@ static void rna_def_scene_ffmpeg_settings(BlenderRNA *brna)
 	};
 
 	static const EnumPropertyItem ffmpeg_audio_codec_items[] = {
-		{AV_CODEC_ID_NONE, "NONE", 0, "None", ""},
-		{AV_CODEC_ID_MP2, "MP2", 0, "MP2", ""},
-		{AV_CODEC_ID_MP3, "MP3", 0, "MP3", ""},
-		{AV_CODEC_ID_AC3, "AC3", 0, "AC3", ""},
+		{AV_CODEC_ID_NONE, "NONE", 0, "No Audio", "Disables audio output, for video-only renders"},
 		{AV_CODEC_ID_AAC, "AAC", 0, "AAC", ""},
-		{AV_CODEC_ID_VORBIS, "VORBIS", 0, "Vorbis", ""},
+		{AV_CODEC_ID_AC3, "AC3", 0, "AC3", ""},
 		{AV_CODEC_ID_FLAC, "FLAC", 0, "FLAC", ""},
+		{AV_CODEC_ID_MP2, "MP2", 0, "MP2", ""},
+		{AV_CODEC_ID_MP3, "MP3", 0, "MP3", ""},
 		{AV_CODEC_ID_PCM_S16LE, "PCM", 0, "PCM", ""},
+		{AV_CODEC_ID_VORBIS, "VORBIS", 0, "Vorbis", ""},
 		{0, NULL, 0, NULL, NULL}
 	};
 #endif
@@ -5320,7 +5312,7 @@ static void rna_def_scene_ffmpeg_settings(BlenderRNA *brna)
 	RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
 	RNA_def_property_enum_items(prop, ffmpeg_codec_items);
 	RNA_def_property_enum_default(prop, AV_CODEC_ID_H264);
-	RNA_def_property_ui_text(prop, "Codec", "FFmpeg codec to use");
+	RNA_def_property_ui_text(prop, "Video Codec", "FFmpeg codec to use for video output");
 	RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_FFmpegSettings_codec_settings_update");
 
 	prop = RNA_def_property(srna, "video_bitrate", PROP_INT, PROP_NONE);
@@ -6144,7 +6136,7 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
 	
 	prop = RNA_def_property(srna, "use_stamp_date", PROP_BOOLEAN, PROP_NONE);
 	RNA_def_property_boolean_sdna(prop, NULL, "stamp", R_STAMP_DATE);
-	RNA_def_property_ui_text(prop, "Stamp Date", "Include the current date in image metadata");
+	RNA_def_property_ui_text(prop, "Stamp Date", "Include the current date in image/video metadata");
 	RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
 	
 	prop = RNA_def_property(srna, "use_stamp_frame", PROP_BOOLEAN, PROP_NONE);
@@ -6152,6 +6144,11 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
 	RNA_def_property_ui_text(prop, "Stamp Frame", "Include the frame number in image metadata");
 	RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
 	
+	prop = RNA_def_property(srna, "use_stamp_frame_range", PROP_BOOLEAN, PROP_NONE);
+	RNA_def_property_boolean_sdna(prop, NULL, "stamp", R_STAMP_FRAME_RANGE);
+	RNA_def_property_ui_text(prop, "Stamp Frame", "Include the rendered frame range in image/video metadata");
+	RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
 	prop = RNA_def_property(srna, "use_stamp_camera", PROP_BOOLEAN, PROP_NONE);
 	RNA_def_property_boolean_sdna(prop, NULL, "stamp", R_STAMP_CAMERA);
 	RNA_def_property_ui_text(prop, "Stamp Camera", "Include the name of the active camera in image metadata");
@@ -6164,12 +6161,12 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
 	
 	prop = RNA_def_property(srna, "use_stamp_scene", PROP_BOOLEAN, PROP_NONE);
 	RNA_def_property_boolean_sdna(prop, NULL, "stamp", R_STAMP_SCENE);
-	RNA_def_property_ui_text(prop, "Stamp Scene", "Include the name of the active scene in image metadata");
+	RNA_def_property_ui_text(prop, "Stamp Scene", "Include the name of the active scene in image/video metadata");
 	RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
 	
 	prop = RNA_def_property(srna, "use_stamp_note", PROP_BOOLEAN, PROP_NONE);
 	RNA_def_property_boolean_sdna(prop, NULL, "stamp", R_STAMP_NOTE);
-	RNA_def_property_ui_text(prop, "Stamp Note", "Include a custom note in image metadata");
+	RNA_def_property_ui_text(prop, "Stamp Note", "Include a custom note in image/video metadata");
 	RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
 	
 	prop = RNA_def_property(srna, "use_stamp_marker", PROP_BOOLEAN, PROP_NONE);
@@ -6179,7 +6176,7 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
 	
 	prop = RNA_def_property(srna, "use_stamp_filename", PROP_BOOLEAN, PROP_NONE);
 	RNA_def_property_boolean_sdna(prop, NULL, "stamp", R_STAMP_FILENAME);
-	RNA_def_property_ui_text(prop, "Stamp Filename", "Include the .blend filename in image metadata");
+	RNA_def_property_ui_text(prop, "Stamp Filename", "Include the .blend filename in image/video metadata");
 	RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
 	
 	prop = RNA_def_property(srna, "use_stamp_sequencer_strip", PROP_BOOLEAN, PROP_NONE);
@@ -6963,7 +6960,6 @@ void RNA_def_scene(BlenderRNA *brna)
 
 	/* Statistics */
 	func = RNA_def_function(srna, "statistics", "ED_info_stats_string");
-	parm = RNA_def_pointer(func, "workspace", "WorkSpace", "", "Active workspace");
 	parm = RNA_def_pointer(func, "view_layer", "ViewLayer", "", "Active layer");
 	RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
 	parm = RNA_def_string(func, "statistics", NULL, 0, "Statistics", "");
diff --git a/source/blender/makesrna/intern/rna_scene_api.c b/source/blender/makesrna/intern/rna_scene_api.c
index 212578cf3f03b6ce0396e52d02173daee6edf2c6..7a7537bcf1fa531480220ac8a70d34275d8c3481 100644
--- a/source/blender/makesrna/intern/rna_scene_api.c
+++ b/source/blender/makesrna/intern/rna_scene_api.c
@@ -117,9 +117,9 @@ static void rna_Scene_frame_set(Scene *scene, Main *bmain, int frame, float subf
 
 static void rna_Scene_uvedit_aspect(Scene *scene, Object *ob, float *aspect)
 {
-	if (ob->type == OB_MESH) {
-		/* Will be NULL when not in editmode */
-		BMEditMesh *em = BKE_editmesh_from_object(ob);
+	if ((ob->type == OB_MESH) && (ob->mode == OB_MODE_EDIT)) {
+		BMEditMesh *em;
+		em = BKE_editmesh_from_object(ob);
 		if (EDBM_uv_check(em)) {
 			ED_uvedit_get_aspect(scene, ob, em->bm, aspect, aspect + 1);
 			return;
@@ -366,7 +366,7 @@ void RNA_api_scene(StructRNA *srna)
 	RNA_def_float(func, "shutter_close", 1.0f, -1.0f, 1.0f, "Shutter close", "", -1.0f, 1.0f);
 	RNA_def_boolean(func, "selected_only"	, 0, "Selected only", "Export only selected objects");
 	RNA_def_boolean(func, "uvs"			, 1, "UVs", "Export UVs");
-	RNA_def_boolean(func, "normals"		, 1, "Normals", "Export cormals");
+	RNA_def_boolean(func, "normals"		, 1, "Normals", "Export normals");
 	RNA_def_boolean(func, "vcolors"		, 0, "Vertex colors", "Export vertex colors");
 	RNA_def_boolean(func, "apply_subdiv"	, 1, "Subsurfs as meshes", "Export subdivision surfaces as meshes");
 	RNA_def_boolean(func, "flatten"		, 0, "Flatten hierarchy", "Flatten hierarchy");
diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c
index 06c0260d08f41c582a0aab241b77bf79b5d3a1e3..49edc742a4b49db0e559903305e70b3ae206fefa 100644
--- a/source/blender/makesrna/intern/rna_sculpt_paint.c
+++ b/source/blender/makesrna/intern/rna_sculpt_paint.c
@@ -39,7 +39,6 @@
 #include "DNA_brush_types.h"
 #include "DNA_screen_types.h"
 #include "DNA_space_types.h"
-#include "DNA_workspace_types.h"
 
 #include "BKE_paint.h"
 #include "BKE_material.h"
@@ -160,7 +159,7 @@ static void rna_ParticleEdit_redo(bContext *C, PointerRNA *UNUSED(ptr))
 	Scene *scene = CTX_data_scene(C);
 	ViewLayer *view_layer = CTX_data_view_layer(C);
 	Object *ob = OBACT(view_layer);
-	PTCacheEdit *edit = PE_get_current(scene, view_layer, ob);
+	PTCacheEdit *edit = PE_get_current(scene, ob);
 
 	if (!edit)
 		return;
@@ -181,8 +180,8 @@ static void rna_ParticleEdit_tool_set(PointerRNA *ptr, int value)
 	ParticleEditSettings *pset = (ParticleEditSettings *)ptr->data;
 	
 	/* redraw hair completely if weight brush is/was used */
-	if ((pset->brushtype == PE_BRUSH_WEIGHT || value == PE_BRUSH_WEIGHT) && pset->view_layer) {
-		Object *ob = (pset->view_layer->basact) ? pset->view_layer->basact->object : NULL;
+	if ((pset->brushtype == PE_BRUSH_WEIGHT || value == PE_BRUSH_WEIGHT) && pset->object) {
+		Object *ob = pset->object;
 		if (ob) {
 			DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
 			WM_main_add_notifier(NC_OBJECT | ND_PARTICLE | NA_EDITED, NULL);
@@ -223,14 +222,14 @@ static int rna_ParticleEdit_editable_get(PointerRNA *ptr)
 {
 	ParticleEditSettings *pset = (ParticleEditSettings *)ptr->data;
 
-	return (pset->object && pset->scene && PE_get_current(pset->scene, pset->view_layer, pset->object));
+	return (pset->object && pset->scene && PE_get_current(pset->scene, pset->object));
 }
 static int rna_ParticleEdit_hair_get(PointerRNA *ptr)
 {
 	ParticleEditSettings *pset = (ParticleEditSettings *)ptr->data;
 
 	if (pset->scene) {
-		PTCacheEdit *edit = PE_get_current(pset->scene, pset->view_layer, pset->object);
+		PTCacheEdit *edit = PE_get_current(pset->scene, pset->object);
 
 		return (edit && edit->psys);
 	}
@@ -393,12 +392,11 @@ static void rna_ImaPaint_stencil_update(bContext *C, PointerRNA *UNUSED(ptr))
 
 static void rna_ImaPaint_canvas_update(bContext *C, PointerRNA *UNUSED(ptr))
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	Main *bmain = CTX_data_main(C);
 	Scene *scene = CTX_data_scene(C);
 	ViewLayer *view_layer = CTX_data_view_layer(C);
 	Object *ob = OBACT(view_layer);
-	Object *obedit = ((workspace->object_mode & OB_MODE_EDIT) && BKE_object_is_in_editmode(ob)) ? ob : NULL;
+	Object *obedit = OBEDIT_FROM_OBACT(ob);
 	bScreen *sc;
 	Image *ima = scene->toolsettings->imapaint.canvas;
 	
diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c
index cfd6d0c6db3fadef94f9a46eed30ce9e40c5fb84..33621af69afa60b4ee207ae115f7400043518c7a 100644
--- a/source/blender/makesrna/intern/rna_sequencer.c
+++ b/source/blender/makesrna/intern/rna_sequencer.c
@@ -38,10 +38,11 @@
 #include "BLT_translation.h"
 
 #include "BKE_animsys.h"
-#include "BKE_global.h"
 #include "BKE_sequencer.h"
 #include "BKE_sound.h"
 
+#include "IMB_metadata.h"
+
 #include "MEM_guardedalloc.h"
 
 #include "RNA_access.h"
@@ -588,6 +589,27 @@ static IDProperty *rna_Sequence_idprops(PointerRNA *ptr, bool create)
 	return seq->prop;
 }
 
+static PointerRNA rna_MovieSequence_metadata_get(Sequence *seq)
+{
+	if (seq == NULL || seq->anims.first == NULL) {
+		return PointerRNA_NULL;
+	}
+
+	StripAnim *sanim = seq->anims.first;
+	if (sanim->anim == NULL) {
+		return PointerRNA_NULL;
+	}
+
+	IDProperty *metadata = IMB_anim_load_metadata(sanim->anim);
+	if (metadata == NULL) {
+		return PointerRNA_NULL;
+	}
+
+	PointerRNA ptr;
+	RNA_pointer_create(NULL, &RNA_IDPropertyWrapPtr, metadata, &ptr);
+	return ptr;
+}
+
 static PointerRNA rna_SequenceEditor_meta_stack_get(CollectionPropertyIterator *iter)
 {
 	ListBaseIterator *internal = &iter->internal.listbase;
@@ -1964,7 +1986,9 @@ static void rna_def_movie(BlenderRNA *brna)
 {
 	StructRNA *srna;
 	PropertyRNA *prop;
-	
+	FunctionRNA *func;
+	PropertyRNA *parm;
+
 	srna = RNA_def_struct(brna, "MovieSequence", "Sequence");
 	RNA_def_struct_ui_text(srna, "Movie Sequence", "Sequence strip to load a video");
 	RNA_def_struct_sdna(srna, "Sequence");
@@ -1996,6 +2020,14 @@ static void rna_def_movie(BlenderRNA *brna)
 	                              "rna_Sequence_filepath_set");
 	RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_filepath_update");
 
+	/* metadata */
+	func = RNA_def_function(srna, "metadata", "rna_MovieSequence_metadata_get");
+	RNA_def_function_ui_description(func, "Retrieve metadata of the movie file");
+	/* return type */
+	parm = RNA_def_pointer(func, "metadata", "IDPropertyWrapPtr", "", "Dict-like object containing the metadata");
+	RNA_def_parameter_flags(parm, 0, PARM_RNAPTR);
+	RNA_def_function_return(func, parm);
+
 	/* multiview */
 	prop = RNA_def_property(srna, "use_multiview", PROP_BOOLEAN, PROP_NONE);
 	RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_USE_VIEWS);
diff --git a/source/blender/makesrna/intern/rna_smoke.c b/source/blender/makesrna/intern/rna_smoke.c
index 8fe97885f497f17066dcfd1e778505030e137166..d5580ae35ad71266500533a3ff44b3d7e5418f0e 100644
--- a/source/blender/makesrna/intern/rna_smoke.c
+++ b/source/blender/makesrna/intern/rna_smoke.c
@@ -907,7 +907,7 @@ static void rna_def_smoke_domain_settings(BlenderRNA *brna)
 	RNA_def_property_range(prop, 0.0, 1.0);
 	RNA_def_property_ui_range(prop, 0.0, 1.0, 0.1, 3);
 	RNA_def_property_ui_text(prop, "Clipping",
-							 "Value under which voxels are considered empty space to optimize caching or rendering");
+	                         "Value under which voxels are considered empty space to optimize caching or rendering");
 	RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, NULL);
 }
 
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index c95b33d90ad8daae81c811c0b51b21c96302ec01..2d8385a5f838779e0443d6ee1a262cc55f0a2e42 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -64,28 +64,38 @@
 const EnumPropertyItem rna_enum_space_type_items[] = {
 	/* empty must be here for python, is skipped for UI */
 	{SPACE_EMPTY, "EMPTY", ICON_NONE, "Empty", ""},
+
+	/* General */
+	{0, "", ICON_NONE, "General", ""},
 	{SPACE_VIEW3D, "VIEW_3D", ICON_VIEW3D, "3D View", "3D viewport"},
-	{0, "", ICON_NONE, NULL, NULL},
+	{SPACE_IMAGE, "IMAGE_EDITOR", ICON_IMAGE_COL, "UV/Image Editor", "View and edit images and UV Maps"},
+	{SPACE_NODE, "NODE_EDITOR", ICON_NODETREE, "Node Editor", "Editor for node-based shading and compositing tools"},
+	{SPACE_SEQ, "SEQUENCE_EDITOR", ICON_SEQUENCE, "Video Sequencer", "Video editing tools"},
+	{SPACE_CLIP, "CLIP_EDITOR", ICON_CLIP, "Movie Clip Editor", "Motion tracking tools"},
+
+	/* Animation */
+	{0, "", ICON_NONE, "Animation", ""},
 	{SPACE_TIME, "TIMELINE", ICON_TIME, "Timeline", "Timeline and playback controls"},
 	{SPACE_IPO, "GRAPH_EDITOR", ICON_IPO, "Graph Editor", "Edit drivers and keyframe interpolation"},
 	{SPACE_ACTION, "DOPESHEET_EDITOR", ICON_ACTION, "Dope Sheet", "Adjust timing of keyframes"},
 	{SPACE_NLA, "NLA_EDITOR", ICON_NLA, "NLA Editor", "Combine and layer Actions"},
-	{0, "", ICON_NONE, NULL, NULL},
-	{SPACE_IMAGE, "IMAGE_EDITOR", ICON_IMAGE_COL, "UV/Image Editor", "View and edit images and UV Maps"},
-	{SPACE_CLIP, "CLIP_EDITOR", ICON_CLIP, "Movie Clip Editor", "Motion tracking tools"},
-	{SPACE_SEQ, "SEQUENCE_EDITOR", ICON_SEQUENCE, "Video Sequence Editor", "Video editing tools"},
-	{SPACE_NODE, "NODE_EDITOR", ICON_NODETREE, "Node Editor", "Editor for node-based shading and compositing tools"},
+
+	/* Scripting */
+	{0, "", ICON_NONE, "Scripting", ""},
 	{SPACE_TEXT, "TEXT_EDITOR", ICON_TEXT, "Text Editor", "Edit scripts and in-file documentation"},
 	{SPACE_LOGIC, "LOGIC_EDITOR", ICON_LOGIC, "Logic Editor", "Game logic editing"},
-	{0, "", ICON_NONE, NULL, NULL},
-	{SPACE_BUTS, "PROPERTIES", ICON_BUTS, "Properties", "Edit properties of active object and related data-blocks"},
+	{SPACE_CONSOLE, "CONSOLE", ICON_CONSOLE, "Python Console", "Interactive programmatic console for "
+	                "advanced editing and script development"},
+	{SPACE_INFO, "INFO", ICON_INFO, "Info", "Main menu bar and list of error messages "
+	             "(drag down to expand and display)"},
+
+	/* Data */
+	{0, "", ICON_NONE, "Data", ""},
 	{SPACE_OUTLINER, "OUTLINER", ICON_OOPS, "Outliner", "Overview of scene graph and all available data-blocks"},
-	{SPACE_USERPREF, "USER_PREFERENCES", ICON_PREFERENCES, "User Preferences", "Edit persistent configuration settings"},
-	{SPACE_INFO, "INFO", ICON_INFO, "Info", "Main menu bar and list of error messages (drag down to expand and display)"},
-	{0, "", ICON_NONE, NULL, NULL},
+	{SPACE_BUTS, "PROPERTIES", ICON_BUTS, "Properties", "Edit properties of active object and related data-blocks"},
 	{SPACE_FILE, "FILE_BROWSER", ICON_FILESEL, "File Browser", "Browse for files and assets"},
-	{0, "", ICON_NONE, NULL, NULL},
-	{SPACE_CONSOLE, "CONSOLE", ICON_CONSOLE, "Python Console", "Interactive programmatic console for advanced editing and script development"},
+	{SPACE_USERPREF, "USER_PREFERENCES", ICON_PREFERENCES, "User Preferences",
+	                 "Edit persistent configuration settings"},
 	{0, NULL, 0, NULL, NULL}
 };
 
@@ -858,8 +868,8 @@ static int rna_SpaceImageEditor_show_uvedit_get(PointerRNA *ptr)
 	SpaceImage *sima = (SpaceImage *)(ptr->data);
 	bScreen *sc = (bScreen *)ptr->id.data;
 	wmWindow *win = ED_screen_window_find(sc, G.main->wm.first);
-	Object *obedit = OBEDIT_FROM_WINDOW(win);
-
+	ViewLayer *view_layer = WM_window_get_active_view_layer(win);
+	Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
 	return ED_space_image_show_uvedit(sima, obedit);
 }
 
@@ -867,11 +877,9 @@ static int rna_SpaceImageEditor_show_maskedit_get(PointerRNA *ptr)
 {
 	SpaceImage *sima = (SpaceImage *)(ptr->data);
 	bScreen *sc = (bScreen *)ptr->id.data;
-	wmWindow *window = NULL;
-	Scene *scene = ED_screen_scene_find_with_window(sc, G.main->wm.first, &window);
-	ViewLayer *view_layer = BKE_view_layer_context_active_PLACEHOLDER(scene);
-	const WorkSpace *workspace = WM_window_get_active_workspace(window);
-	return ED_space_image_check_show_maskedit(sima, workspace, view_layer);
+	wmWindow *win = ED_screen_window_find(sc, G.main->wm.first);
+	ViewLayer *view_layer = WM_window_get_active_view_layer(win);
+	return ED_space_image_check_show_maskedit(sima, view_layer);
 }
 
 static void rna_SpaceImageEditor_image_set(PointerRNA *ptr, PointerRNA value)
@@ -880,7 +888,8 @@ static void rna_SpaceImageEditor_image_set(PointerRNA *ptr, PointerRNA value)
 	bScreen *sc = (bScreen *)ptr->id.data;
 	wmWindow *win;
 	Scene *scene = ED_screen_scene_find_with_window(sc, G.main->wm.first, &win);
-	Object *obedit = OBEDIT_FROM_WINDOW(win);
+	ViewLayer *view_layer = WM_window_get_active_view_layer(win);
+	Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
 	ED_space_image_set(sima, scene, obedit, (Image *)value.data);
 }
 
diff --git a/source/blender/makesrna/intern/rna_text_api.c b/source/blender/makesrna/intern/rna_text_api.c
index 0287f74587b351e975f225ebfa899e2475b25d80..d7a862fecbcb9f44198702b7b6e62afd677481f7 100644
--- a/source/blender/makesrna/intern/rna_text_api.c
+++ b/source/blender/makesrna/intern/rna_text_api.c
@@ -40,13 +40,13 @@
 
 static void rna_Text_clear(Text *text)
 {
-	BKE_text_clear(text);
+	BKE_text_clear(text, NULL);
 	WM_main_add_notifier(NC_TEXT | NA_EDITED, text);
 }
 
 static void rna_Text_write(Text *text, const char *str)
 {
-	BKE_text_write(text, str);
+	BKE_text_write(text, NULL, str);
 	WM_main_add_notifier(NC_TEXT | NA_EDITED, text);
 }
 
diff --git a/source/blender/makesrna/intern/rna_texture.c b/source/blender/makesrna/intern/rna_texture.c
index e2e44a8ac5c3958e2da5f3beae4ef53cc8d69bc2..42e3e2c83fbcdfdc5e063e099ff1217126d3ed06 100644
--- a/source/blender/makesrna/intern/rna_texture.c
+++ b/source/blender/makesrna/intern/rna_texture.c
@@ -37,7 +37,6 @@
 #include "DNA_node_types.h"
 #include "DNA_particle_types.h"
 #include "DNA_scene_types.h" /* MAXFRAME only */
-#include "DNA_workspace_types.h"
 
 #include "BLI_utildefines.h"
 
@@ -248,11 +247,10 @@ void rna_TextureSlot_update(bContext *C, PointerRNA *ptr)
 			break;
 		case ID_BR:
 		{
-			const WorkSpace *workspace = CTX_wm_workspace(C);
 			Scene *scene = CTX_data_scene(C);
 			MTex *mtex = ptr->data;
 			ViewLayer *view_layer = CTX_data_view_layer(C);
-			BKE_paint_invalidate_overlay_tex(scene, view_layer, mtex->tex, workspace->object_mode);
+			BKE_paint_invalidate_overlay_tex(scene, view_layer, mtex->tex);
 			WM_main_add_notifier(NC_BRUSH, id);
 			break;
 		}
diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c
index d4896d47b429efbdfa741d28f7aa103b92c979fe..7c68ca0562985a20d2c70e98d27adbad69c84cde 100644
--- a/source/blender/makesrna/intern/rna_userdef.c
+++ b/source/blender/makesrna/intern/rna_userdef.c
@@ -364,11 +364,8 @@ static void rna_UserDef_weight_color_update(Main *bmain, Scene *scene, PointerRN
 	vDM_ColorBand_store((U.flag & USER_CUSTOM_RANGE) ? (&U.coba_weight) : NULL, btheme->tv3d.vertex_unreferenced);
 
 	for (ob = bmain->object.first; ob; ob = ob->id.next) {
-		/* TODO/OBMODE (not urgent) */
-		// if (ob->mode & OB_MODE_WEIGHT_PAINT)
-		{
+		if (ob->mode & OB_MODE_WEIGHT_PAINT)
 			DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
-		}
 	}
 
 	rna_userdef_update(bmain, scene, ptr);
@@ -447,6 +444,12 @@ static void rna_userdef_text_update(Main *UNUSED(bmain), Scene *UNUSED(scene), P
 	WM_main_add_notifier(NC_WINDOW, NULL);
 }
 
+static void rna_userdef_text_antialiasing_update(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+	BLF_antialias_set((U.text_render & USER_TEXT_DISABLE_AA) == 0);
+	rna_userdef_text_update(bmain, scene, ptr);
+}
+
 static PointerRNA rna_Theme_space_generic_get(PointerRNA *ptr)
 {
 	return rna_pointer_inherit_refine(ptr, &RNA_ThemeSpaceGeneric, ptr->data);
@@ -4216,7 +4219,7 @@ static void rna_def_userdef_system(BlenderRNA *brna)
 	prop = RNA_def_property(srna, "use_text_antialiasing", PROP_BOOLEAN, PROP_NONE);
 	RNA_def_property_boolean_negative_sdna(prop, NULL, "text_render", USER_TEXT_DISABLE_AA);
 	RNA_def_property_ui_text(prop, "Text Anti-aliasing", "Draw user interface text anti-aliased");
-	RNA_def_property_update(prop, 0, "rna_userdef_text_update");
+	RNA_def_property_update(prop, 0, "rna_userdef_text_antialiasing_update");
 
 	prop = RNA_def_property(srna, "select_method", PROP_ENUM, PROP_NONE);
 	RNA_def_property_enum_sdna(prop, NULL, "gpu_select_method");
diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c
index 5110f4cc27a6c33e9f8cf05b8f489c3eb64035dd..1d4652b02121797ec3950c843fbd629f7e13d7f4 100644
--- a/source/blender/makesrna/intern/rna_wm.c
+++ b/source/blender/makesrna/intern/rna_wm.c
@@ -473,7 +473,6 @@ const EnumPropertyItem rna_enum_wm_report_items[] = {
 #include "DNA_workspace_types.h"
 
 #include "ED_screen.h"
-#include "ED_object.h"
 
 #include "UI_interface.h"
 
@@ -761,9 +760,8 @@ static void rna_workspace_screen_update(bContext *C, PointerRNA *ptr)
 static PointerRNA rna_Window_view_layer_get(PointerRNA *ptr)
 {
 	wmWindow *win = ptr->data;
-	Scene *scene = WM_window_get_active_scene(win);
-	WorkSpace *workspace = WM_window_get_active_workspace(win);
-	ViewLayer *view_layer = BKE_workspace_view_layer_get(workspace, scene);
+	Scene *scene;
+	ViewLayer *view_layer = WM_window_get_active_view_layer_ex(win, &scene);
 	PointerRNA scene_ptr;
 
 	RNA_id_pointer_create(&scene->id, &scene_ptr);
@@ -779,21 +777,6 @@ static void rna_Window_view_layer_set(PointerRNA *ptr, PointerRNA value)
 	BKE_workspace_view_layer_set(workspace, value.data, scene);
 }
 
-static void rna_Window_view_layer_update(struct bContext *C, PointerRNA *ptr)
-{
-	wmWindow *win = ptr->data;
-	Scene *scene = WM_window_get_active_scene(win);
-	WorkSpace *workspace = WM_window_get_active_workspace(win);
-	ViewLayer *view_layer = BKE_workspace_view_layer_get(workspace, scene);
-	Object *obact = OBACT(view_layer);
-	eObjectMode object_mode = workspace->object_mode;
-	if (obact && (object_mode & OB_MODE_EDIT)) {
-		ED_object_editmode_exit_ex(NULL, workspace, scene, obact, EM_FREEDATA);
-	}
-	workspace->object_mode = object_mode;
-	ED_object_base_activate(C, view_layer->basact);
-}
-
 static PointerRNA rna_KeyMapItem_properties_get(PointerRNA *ptr)
 {
 	wmKeyMapItem *kmi = ptr->data;
@@ -2077,8 +2060,8 @@ static void rna_def_window(BlenderRNA *brna)
 	RNA_def_property_struct_type(prop, "ViewLayer");
 	RNA_def_property_pointer_funcs(prop, "rna_Window_view_layer_get", "rna_Window_view_layer_set", NULL, NULL);
 	RNA_def_property_ui_text(prop, "Active View Layer", "The active workspace view layer showing in the window");
-	RNA_def_property_flag(prop, PROP_EDITABLE | PROP_NEVER_NULL | PROP_CONTEXT_UPDATE);
-	RNA_def_property_update(prop, NC_SCREEN | ND_LAYER, "rna_Window_view_layer_update");
+	RNA_def_property_flag(prop, PROP_EDITABLE | PROP_NEVER_NULL);
+	RNA_def_property_update(prop, NC_SCREEN | ND_LAYER, NULL);
 
 	prop = RNA_def_property(srna, "x", PROP_INT, PROP_NONE);
 	RNA_def_property_int_sdna(prop, NULL, "posx");
diff --git a/source/blender/makesrna/intern/rna_workspace.c b/source/blender/makesrna/intern/rna_workspace.c
index 7ac4134f02b2cd20b11b58320fe1a5da5b19a267..609493c54b8c584c3ee382c36d7ef56455b2234a 100644
--- a/source/blender/makesrna/intern/rna_workspace.c
+++ b/source/blender/makesrna/intern/rna_workspace.c
@@ -216,9 +216,11 @@ static void rna_def_workspace(BlenderRNA *brna)
 	RNA_def_property_ui_text(prop, "UI Tags", "");
 	rna_def_workspace_owner_ids(brna, prop);
 
+#if 0
 	prop = RNA_def_property(srna, "object_mode", PROP_ENUM, PROP_NONE);
 	RNA_def_property_enum_items(prop, rna_enum_object_mode_items);
 	RNA_def_property_ui_text(prop, "Mode", "Object interaction mode used in this window");
+#endif
 
 	/* View Render */
 	prop = RNA_def_property(srna, "view_render", PROP_POINTER, PROP_NONE);
diff --git a/source/blender/modifiers/intern/MOD_displace.c b/source/blender/modifiers/intern/MOD_displace.c
index f90e6cc1d366fa836436ca826591b6234b146747..7ec89b23f2501cbd2fca8b03f4d66b9c48507ac5 100644
--- a/source/blender/modifiers/intern/MOD_displace.c
+++ b/source/blender/modifiers/intern/MOD_displace.c
@@ -80,14 +80,6 @@ static void copyData(ModifierData *md, ModifierData *target)
 	modifier_copyData_generic(md, target);
 }
 
-static void freeData(ModifierData *md)
-{
-	DisplaceModifierData *dmd = (DisplaceModifierData *) md;
-	if (dmd->texture) {
-		id_us_min(&dmd->texture->id);
-	}
-}
-
 static CustomDataMask requiredDataMask(Object *UNUSED(ob), ModifierData *md)
 {
 	DisplaceModifierData *dmd = (DisplaceModifierData *)md;
@@ -422,7 +414,7 @@ ModifierTypeInfo modifierType_Displace = {
 	/* applyModifierEM */   NULL,
 	/* initData */          initData,
 	/* requiredDataMask */  requiredDataMask,
-	/* freeData */          freeData,
+	/* freeData */          NULL,
 	/* isDisabled */        isDisabled,
 	/* updateDepsgraph */   updateDepsgraph,
 	/* dependsOnTime */     dependsOnTime,
diff --git a/source/blender/modifiers/intern/MOD_fluidsim_util.c b/source/blender/modifiers/intern/MOD_fluidsim_util.c
index 65f45c9af64081bf838193d3aa9ce82322d50b48..72c44121e0b615eeb79df237ef8349472acbe473 100644
--- a/source/blender/modifiers/intern/MOD_fluidsim_util.c
+++ b/source/blender/modifiers/intern/MOD_fluidsim_util.c
@@ -48,7 +48,9 @@
 
 #include "BKE_fluidsim.h" /* ensure definitions here match */
 #include "BKE_cdderivedmesh.h"
-#include "BKE_global.h" /* G.main->name only */
+#ifdef WITH_MOD_FLUID
+#  include "BKE_global.h"
+#endif
 
 #include "MOD_fluidsim_util.h"
 #include "MOD_modifiertypes.h"
diff --git a/source/blender/modifiers/intern/MOD_meshdeform.c b/source/blender/modifiers/intern/MOD_meshdeform.c
index a2ca9bde124829f22ad95be523e49e0754603248..6bf735e1e5cde9849b048575ef915623e7bc490c 100644
--- a/source/blender/modifiers/intern/MOD_meshdeform.c
+++ b/source/blender/modifiers/intern/MOD_meshdeform.c
@@ -298,7 +298,7 @@ static void meshdeformModifier_do(
 	 *
 	 * We'll support this case once granular dependency graph is landed.
 	 */
-	if (mmd->object == OBEDIT_FROM_EVAL_CTX(eval_ctx)) {
+	if (mmd->object == OBEDIT_FROM_VIEW_LAYER(eval_ctx->view_layer)) {
 		BMEditMesh *em = BKE_editmesh_from_object(mmd->object);
 		tmpdm = editbmesh_get_derived_cage_and_final(eval_ctx, md->scene, mmd->object, em, 0, &cagedm);
 		if (tmpdm)
diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.c b/source/blender/modifiers/intern/MOD_meshsequencecache.c
index 02021c4474900bc49b030b7c486a0db9a6d529df..10e1cdea4ca7feaa80f94c8d186e560ca1c19a15 100644
--- a/source/blender/modifiers/intern/MOD_meshsequencecache.c
+++ b/source/blender/modifiers/intern/MOD_meshsequencecache.c
@@ -33,7 +33,6 @@
 #include "BKE_cachefile.h"
 #include "BKE_DerivedMesh.h"
 #include "BKE_cdderivedmesh.h"
-#include "BKE_global.h"
 #include "BKE_library.h"
 #include "BKE_library_query.h"
 #include "BKE_scene.h"
@@ -44,6 +43,7 @@
 
 #ifdef WITH_ALEMBIC
 #	include "ABC_alembic.h"
+#	include "BKE_global.h"
 #endif
 
 static void initData(ModifierData *md)
@@ -74,10 +74,6 @@ static void freeData(ModifierData *md)
 {
 	MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *) md;
 
-	if (mcmd->cache_file) {
-		id_us_min(&mcmd->cache_file->id);
-	}
-
 	if (mcmd->reader) {
 #ifdef WITH_ALEMBIC
 		CacheReader_free(mcmd->reader);
diff --git a/source/blender/modifiers/intern/MOD_multires.c b/source/blender/modifiers/intern/MOD_multires.c
index 6704526ea0343f62869e74aa429a7dd460334dbe..2b675d36140839ccd4cf7b66ac68688f12a1dc07 100644
--- a/source/blender/modifiers/intern/MOD_multires.c
+++ b/source/blender/modifiers/intern/MOD_multires.c
@@ -48,8 +48,6 @@
 
 #include "MOD_modifiertypes.h"
 
-#include "DEG_depsgraph.h"
-
 static void initData(ModifierData *md)
 {
 	MultiresModifierData *mmd = (MultiresModifierData *)md;
@@ -69,7 +67,7 @@ static void copyData(ModifierData *md, ModifierData *target)
 	modifier_copyData_generic(md, target);
 }
 
-static DerivedMesh *applyModifier(ModifierData *md, const struct EvaluationContext *eval_ctx, Object *ob,
+static DerivedMesh *applyModifier(ModifierData *md, const struct EvaluationContext *UNUSED(eval_ctx), Object *ob,
                                   DerivedMesh *dm, ModifierApplyFlag flag)
 {
 	MultiresModifierData *mmd = (MultiresModifierData *)md;
@@ -96,7 +94,7 @@ static DerivedMesh *applyModifier(ModifierData *md, const struct EvaluationConte
 	if (ignore_simplify)
 		flags |= MULTIRES_IGNORE_SIMPLIFY;
 
-	result = multires_make_derived_from_derived(dm, mmd, ob, flags, eval_ctx->object_mode);
+	result = multires_make_derived_from_derived(dm, mmd, ob, flags);
 
 	if (result == dm)
 		return dm;
diff --git a/source/blender/modifiers/intern/MOD_particleinstance.c b/source/blender/modifiers/intern/MOD_particleinstance.c
index 81f52304b8f5c5f773526e339c5d99c00ce83a95..7979751395ae107747cf85e8a97070212bf7bd0a 100644
--- a/source/blender/modifiers/intern/MOD_particleinstance.c
+++ b/source/blender/modifiers/intern/MOD_particleinstance.c
@@ -44,7 +44,6 @@
 
 #include "BKE_cdderivedmesh.h"
 #include "BKE_effect.h"
-#include "BKE_global.h"
 #include "BKE_lattice.h"
 #include "BKE_library_query.h"
 #include "BKE_modifier.h"
diff --git a/source/blender/modifiers/intern/MOD_shrinkwrap.c b/source/blender/modifiers/intern/MOD_shrinkwrap.c
index e1df65dd748e78d363d9326a949df55a018aa883..cbb96b31db1c0daa385c48f03c19a718c2bd347f 100644
--- a/source/blender/modifiers/intern/MOD_shrinkwrap.c
+++ b/source/blender/modifiers/intern/MOD_shrinkwrap.c
@@ -103,7 +103,7 @@ static void foreachObjectLink(ModifierData *md, Object *ob, ObjectWalkFunc walk,
 	walk(userData, ob, &smd->auxTarget, IDWALK_CB_NOP);
 }
 
-static void deformVerts(ModifierData *md, const struct EvaluationContext *eval_ctx,
+static void deformVerts(ModifierData *md, const struct EvaluationContext *UNUSED(eval_ctx),
                         Object *ob, DerivedMesh *derivedData,
                         float (*vertexCos)[3],
                         int numVerts,
@@ -118,13 +118,13 @@ static void deformVerts(ModifierData *md, const struct EvaluationContext *eval_c
 		dm = get_cddm(ob, NULL, dm, vertexCos, dependsOnNormals(md));
 	}
 
-	shrinkwrapModifier_deform(eval_ctx, (ShrinkwrapModifierData *)md, ob, dm, vertexCos, numVerts, forRender);
+	shrinkwrapModifier_deform((ShrinkwrapModifierData *)md, ob, dm, vertexCos, numVerts, forRender);
 
 	if (dm != derivedData)
 		dm->release(dm);
 }
 
-static void deformVertsEM(ModifierData *md, const struct EvaluationContext *eval_ctx, Object *ob,
+static void deformVertsEM(ModifierData *md, const struct EvaluationContext *UNUSED(eval_ctx), Object *ob,
                           struct BMEditMesh *editData, DerivedMesh *derivedData,
                           float (*vertexCos)[3], int numVerts)
 {
@@ -136,7 +136,7 @@ static void deformVertsEM(ModifierData *md, const struct EvaluationContext *eval
 		dm = get_cddm(ob, editData, dm, vertexCos, dependsOnNormals(md));
 	}
 
-	shrinkwrapModifier_deform(eval_ctx, (ShrinkwrapModifierData *)md, ob, dm, vertexCos, numVerts, false);
+	shrinkwrapModifier_deform((ShrinkwrapModifierData *)md, ob, dm, vertexCos, numVerts, false);
 
 	if (dm != derivedData)
 		dm->release(dm);
diff --git a/source/blender/modifiers/intern/MOD_subsurf.c b/source/blender/modifiers/intern/MOD_subsurf.c
index 8fd510a6662baa34d4036d65064e923d5bc9ca4a..a09923d6dfe4d1162668d9933ff09f3164cbd89c 100644
--- a/source/blender/modifiers/intern/MOD_subsurf.c
+++ b/source/blender/modifiers/intern/MOD_subsurf.c
@@ -117,7 +117,7 @@ static DerivedMesh *applyModifier(ModifierData *md, const EvaluationContext *eva
 		subsurf_flags |= SUBSURF_USE_RENDER_PARAMS;
 	if (isFinalCalc)
 		subsurf_flags |= SUBSURF_IS_FINAL_CALC;
-	if (eval_ctx->object_mode & OB_MODE_EDIT)
+	if (ob->mode & OB_MODE_EDIT)
 		subsurf_flags |= SUBSURF_IN_EDIT_MODE;
 
 #ifdef WITH_OPENSUBDIV
@@ -132,7 +132,7 @@ static DerivedMesh *applyModifier(ModifierData *md, const EvaluationContext *eva
 		if (U.opensubdiv_compute_type == USER_OPENSUBDIV_COMPUTE_NONE) {
 			modifier_setError(md, "OpenSubdiv is disabled in User Preferences");
 		}
-		else if ((eval_ctx->object_mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)) != 0) {
+		else if ((ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)) != 0) {
 			modifier_setError(md, "OpenSubdiv is not supported in paint modes");
 		}
 		else if ((DEG_get_eval_flags_for_id(eval_ctx->depsgraph, &ob->id) & DAG_EVAL_NEED_CPU) == 0) {
@@ -143,8 +143,6 @@ static DerivedMesh *applyModifier(ModifierData *md, const EvaluationContext *eva
 			modifier_setError(md, "OpenSubdiv is disabled due to dependencies");
 		}
 	}
-#else
-	UNUSED_VARS(ob);
 #endif
 
 	result = subsurf_make_derived_from_derived(derivedData, smd, NULL, subsurf_flags);
diff --git a/source/blender/modifiers/intern/MOD_surfacedeform.c b/source/blender/modifiers/intern/MOD_surfacedeform.c
index e7f283c4a31e01567ba47af86c3b840e9d16bd32..b5fd1e558397a96d9fee986a98711a623f454eea 100644
--- a/source/blender/modifiers/intern/MOD_surfacedeform.c
+++ b/source/blender/modifiers/intern/MOD_surfacedeform.c
@@ -1110,7 +1110,7 @@ static void surfacedeformModifier_do(
 	}
 
 	/* Handle target mesh both in and out of edit mode */
-	if (smd->target == OBEDIT_FROM_EVAL_CTX(eval_ctx)) {
+	if (smd->target == OBEDIT_FROM_VIEW_LAYER(eval_ctx->view_layer)) {
 		BMEditMesh *em = BKE_editmesh_from_object(smd->target);
 		tdm = em->derivedFinal;
 	}
diff --git a/source/blender/modifiers/intern/MOD_wave.c b/source/blender/modifiers/intern/MOD_wave.c
index 33b2d904c23ca3b0233dcb8dab28da3a8d4d560a..7921ea662a72dc9ab6592c627e339445dbeda944 100644
--- a/source/blender/modifiers/intern/MOD_wave.c
+++ b/source/blender/modifiers/intern/MOD_wave.c
@@ -78,14 +78,6 @@ static void initData(ModifierData *md)
 	wmd->defgrp_name[0] = 0;
 }
 
-static void freeData(ModifierData *md)
-{
-	WaveModifierData *wmd = (WaveModifierData *) md;
-	if (wmd->texture) {
-		id_us_min(&wmd->texture->id);
-	}
-}
-
 static void copyData(ModifierData *md, ModifierData *target)
 {
 #if 0
@@ -366,7 +358,7 @@ ModifierTypeInfo modifierType_Wave = {
 	/* applyModifierEM */   NULL,
 	/* initData */          initData,
 	/* requiredDataMask */  requiredDataMask,
-	/* freeData */          freeData,
+	/* freeData */          NULL,
 	/* isDisabled */        NULL,
 	/* updateDepsgraph */   updateDepsgraph,
 	/* dependsOnTime */     dependsOnTime,
diff --git a/source/blender/modifiers/intern/MOD_weightvgedit.c b/source/blender/modifiers/intern/MOD_weightvgedit.c
index 23f36d3bc4ccaa20c2681895277a60613baaf7ca..6fdfe215c37f94a833d503cb521c22a2d0fa6db1 100644
--- a/source/blender/modifiers/intern/MOD_weightvgedit.c
+++ b/source/blender/modifiers/intern/MOD_weightvgedit.c
@@ -78,10 +78,6 @@ static void freeData(ModifierData *md)
 {
 	WeightVGEditModifierData *wmd = (WeightVGEditModifierData *) md;
 	curvemapping_free(wmd->cmap_curve);
-
-	if (wmd->mask_texture) {
-		id_us_min(&wmd->mask_texture->id);
-	}
 }
 
 static void copyData(ModifierData *md, ModifierData *target)
diff --git a/source/blender/modifiers/intern/MOD_weightvgmix.c b/source/blender/modifiers/intern/MOD_weightvgmix.c
index 1947e7e1f0fc0cb95858f50c9e4dfb577eb7397f..45c41498792b61b6706d0563da55ec30bccafb3c 100644
--- a/source/blender/modifiers/intern/MOD_weightvgmix.c
+++ b/source/blender/modifiers/intern/MOD_weightvgmix.c
@@ -125,14 +125,6 @@ static void initData(ModifierData *md)
 	wmd->mask_tex_mapping       = MOD_DISP_MAP_LOCAL;
 }
 
-static void freeData(ModifierData *md)
-{
-	WeightVGMixModifierData *wmd = (WeightVGMixModifierData *) md;
-	if (wmd->mask_texture) {
-		id_us_min(&wmd->mask_texture->id);
-	}
-}
-
 static void copyData(ModifierData *md, ModifierData *target)
 {
 #if 0
@@ -409,7 +401,7 @@ ModifierTypeInfo modifierType_WeightVGMix = {
 	/* applyModifierEM */   NULL,
 	/* initData */          initData,
 	/* requiredDataMask */  requiredDataMask,
-	/* freeData */          freeData,
+	/* freeData */          NULL,
 	/* isDisabled */        isDisabled,
 	/* updateDepsgraph */   updateDepsgraph,
 	/* dependsOnTime */     dependsOnTime,
diff --git a/source/blender/modifiers/intern/MOD_weightvgproximity.c b/source/blender/modifiers/intern/MOD_weightvgproximity.c
index 87c6a03a53682508c8832e31bf096dbd7307cd8b..84c5207830ee9988d420afc8dca322789f8a8818 100644
--- a/source/blender/modifiers/intern/MOD_weightvgproximity.c
+++ b/source/blender/modifiers/intern/MOD_weightvgproximity.c
@@ -285,14 +285,6 @@ static void initData(ModifierData *md)
 	wmd->max_dist             = 1.0f; /* vert arbitrary distance, but don't use 0 */
 }
 
-static void freeData(ModifierData *md)
-{
-	WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData *) md;
-	if (wmd->mask_texture) {
-		id_us_min(&wmd->mask_texture->id);
-	}
-}
-
 static void copyData(ModifierData *md, ModifierData *target)
 {
 #if 0
@@ -592,7 +584,7 @@ ModifierTypeInfo modifierType_WeightVGProximity = {
 	/* applyModifierEM */   NULL,
 	/* initData */          initData,
 	/* requiredDataMask */  requiredDataMask,
-	/* freeData */          freeData,
+	/* freeData */          NULL,
 	/* isDisabled */        isDisabled,
 	/* updateDepsgraph */   updateDepsgraph,
 	/* dependsOnTime */     dependsOnTime,
diff --git a/source/blender/nodes/intern/node_util.c b/source/blender/nodes/intern/node_util.c
index 43d4136d5567bbca4cca872514abdcfa95cac529..9f04c12fb5ec858d19a2dc572600b4c018d32a8f 100644
--- a/source/blender/nodes/intern/node_util.c
+++ b/source/blender/nodes/intern/node_util.c
@@ -124,6 +124,11 @@ void node_filter_label(bNodeTree *UNUSED(ntree), bNode *node, char *label, int m
 /* test if two sockets are interchangeable */
 static bool node_link_socket_match(bNodeSocket *a, bNodeSocket *b)
 {
+	/* check if sockets are of the same type */
+	if (a->typeinfo != b->typeinfo) {
+		return false;
+	}
+
 	/* tests if alphabetic prefix matches
 	 * this allows for imperfect matches, such as numeric suffixes,
 	 * like Color1/Color2
diff --git a/source/blender/nodes/shader/nodes/node_shader_volume_principled.c b/source/blender/nodes/shader/nodes/node_shader_volume_principled.c
index e51833e447469e810d21eaf3de88e9db12c75a74..4fbd9af1b585068b63a10b22bd5ae0f1637febb1 100644
--- a/source/blender/nodes/shader/nodes/node_shader_volume_principled.c
+++ b/source/blender/nodes/shader/nodes/node_shader_volume_principled.c
@@ -41,7 +41,7 @@ static bNodeSocketTemplate sh_node_volume_principled_in[] = {
 	{	SOCK_FLOAT, 1, N_("Blackbody Intensity"),	0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR},
 	{	SOCK_RGBA, 1, N_("Blackbody Tint"),			1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f},
 	{	SOCK_FLOAT, 1, N_("Temperature"),			1000.0f, 0.0f, 0.0f, 0.0f, 0.0f, 6500.0f},
-	{	SOCK_STRING, 1, N_("Temperature Attribute"),0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
+	{	SOCK_STRING, 1, N_("Temperature Attribute"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
 	{	-1, 0, ""	}
 };
 
diff --git a/source/blender/nodes/texture/node_texture_tree.c b/source/blender/nodes/texture/node_texture_tree.c
index fab28bacff8bcdaa828beb79ac759c61e3980998..932b60665c4498d448e483087144bde682c3c727 100644
--- a/source/blender/nodes/texture/node_texture_tree.c
+++ b/source/blender/nodes/texture/node_texture_tree.c
@@ -35,7 +35,6 @@
 #include "DNA_texture_types.h"
 #include "DNA_node_types.h"
 #include "DNA_space_types.h"
-#include "DNA_workspace_types.h"
 
 #include "BLI_listbase.h"
 #include "BLI_threads.h"
@@ -63,7 +62,6 @@
 static void texture_get_from_context(
         const bContext *C, bNodeTreeType *UNUSED(treetype), bNodeTree **r_ntree, ID **r_id, ID **r_from)
 {
-	const WorkSpace *workspace = CTX_wm_workspace(C);
 	SpaceNode *snode = CTX_wm_space_node(C);
 	Scene *scene = CTX_data_scene(C);
 	ViewLayer *view_layer = CTX_data_view_layer(C);
@@ -98,7 +96,7 @@ static void texture_get_from_context(
 	else if (snode->texfrom == SNODE_TEX_BRUSH) {
 		struct Brush *brush = NULL;
 		
-		if (ob && (workspace->object_mode & OB_MODE_SCULPT))
+		if (ob && (ob->mode & OB_MODE_SCULPT))
 			brush = BKE_paint_brush(&scene->toolsettings->sculpt->paint);
 		else
 			brush = BKE_paint_brush(&scene->toolsettings->imapaint.paint);
diff --git a/source/blender/physics/intern/implicit_blender.c b/source/blender/physics/intern/implicit_blender.c
index d676b1a152193acff3cc567e4a564308f3f0b307..0fdc1d86d5ce8bb467e0465a1c257f028cb0f1b1 100644
--- a/source/blender/physics/intern/implicit_blender.c
+++ b/source/blender/physics/intern/implicit_blender.c
@@ -48,7 +48,6 @@
 #include "BKE_cloth.h"
 #include "BKE_collision.h"
 #include "BKE_effect.h"
-#include "BKE_global.h"
 
 #include "BPH_mass_spring.h"
 
diff --git a/source/blender/python/BPY_extern_clog.h b/source/blender/python/BPY_extern_clog.h
new file mode 100644
index 0000000000000000000000000000000000000000..fbe7139ba1bd18c66fa29a01ddd3570158da8040
--- /dev/null
+++ b/source/blender/python/BPY_extern_clog.h
@@ -0,0 +1,35 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file BPY_extern_clog.h
+ *  \ingroup python
+ *
+ * Logging defines.
+ */
+
+#ifndef __BPY_EXTERN_CLOG_H__
+#define __BPY_EXTERN_CLOG_H__
+
+
+/* bpy_interface.c */
+extern struct CLG_LogRef *BPY_LOG_RNA;
+extern struct CLG_LogRef *BPY_LOG_CONTEXT;
+
+#endif  /* __BPY_EXTERN_CLOG_H__ */
diff --git a/source/blender/python/generic/py_capi_utils.c b/source/blender/python/generic/py_capi_utils.c
index 36609c6f29b0da4f2fd11276d04b0d94c841b4d1..6f265b2ae8747dba8f3c48848c9b9eafacc256a0 100644
--- a/source/blender/python/generic/py_capi_utils.c
+++ b/source/blender/python/generic/py_capi_utils.c
@@ -38,6 +38,8 @@
 
 #include "python_utildefines.h"
 
+#include "BLI_string.h"
+
 #ifndef MATH_STANDALONE
 /* only for BLI_strncpy_wchar_from_utf8, should replace with py funcs but too late in release now */
 #include "BLI_string_utf8.h"
@@ -144,6 +146,15 @@ PyObject *PyC_Tuple_PackArray_F32(const float *array, uint len)
 	return tuple;
 }
 
+PyObject *PyC_Tuple_PackArray_F64(const double *array, uint len)
+{
+	PyObject *tuple = PyTuple_New(len);
+	for (uint i = 0; i < len; i++) {
+		PyTuple_SET_ITEM(tuple, i, PyFloat_FromDouble(array[i]));
+	}
+	return tuple;
+}
+
 PyObject *PyC_Tuple_PackArray_I32(const int *array, uint len)
 {
 	PyObject *tuple = PyTuple_New(len);
@@ -225,22 +236,49 @@ int PyC_ParseBool(PyObject *o, void *p)
 /* for debugging */
 void PyC_ObSpit(const char *name, PyObject *var)
 {
+	const char *null_str = "<null>";
 	fprintf(stderr, "<%s> : ", name);
 	if (var == NULL) {
-		fprintf(stderr, "<NIL>");
+		fprintf(stderr, "%s\n", null_str);
 	}
 	else {
 		PyObject_Print(var, stderr, 0);
-		fprintf(stderr, " ref:%d ", (int)var->ob_refcnt);
-		fprintf(stderr, " ptr:%p", (void *)var);
-		
-		fprintf(stderr, " type:");
-		if (Py_TYPE(var))
-			fprintf(stderr, "%s", Py_TYPE(var)->tp_name);
-		else
-			fprintf(stderr, "<NIL>");
+		const PyTypeObject *type = Py_TYPE(var);
+		fprintf(stderr,
+		        " ref:%d, ptr:%p, type: %s\n",
+		        (int)var->ob_refcnt, (void *)var, type ? type->tp_name : null_str);
+	}
+}
+
+/**
+ * A version of #PyC_ObSpit that writes into a string (and doesn't take a name argument).
+ * Use for logging.
+ */
+void PyC_ObSpitStr(char *result, size_t result_len, PyObject *var)
+{
+	/* No name, creator of string can manage that. */
+	const char *null_str = "<null>";
+	if (var == NULL) {
+		BLI_snprintf(result, result_len, "%s", null_str);
+	}
+	else {
+		const PyTypeObject *type = Py_TYPE(var);
+		PyObject *var_str = PyObject_Repr(var);
+		if (var_str == NULL) {
+			/* We could print error here, but this may be used for generating errors - so don't for now. */
+			PyErr_Clear();
+		}
+		BLI_snprintf(
+		        result, result_len,
+		        " ref=%d, ptr=%p, type=%s, value=%.200s",
+		        (int)var->ob_refcnt,
+		        (void *)var,
+		        type ? type->tp_name : null_str,
+		        var_str ? _PyUnicode_AsString(var_str) : "<error>");
+		if (var_str != NULL) {
+			Py_DECREF(var_str);
+		}
 	}
-	fprintf(stderr, "\n");
 }
 
 void PyC_LineSpit(void)
diff --git a/source/blender/python/generic/py_capi_utils.h b/source/blender/python/generic/py_capi_utils.h
index 25c88799027e6b76f84abb53e9669d92db860c0f..fe7a046d99c6fda24ed40c66d1c25604fa1940f7 100644
--- a/source/blender/python/generic/py_capi_utils.h
+++ b/source/blender/python/generic/py_capi_utils.h
@@ -31,6 +31,7 @@
 #include "BLI_utildefines_variadic.h"
 
 void			PyC_ObSpit(const char *name, PyObject *var);
+void			PyC_ObSpitStr(char *result, size_t result_len, PyObject *var);
 void			PyC_LineSpit(void);
 void			PyC_StackSpit(void);
 PyObject *		PyC_ExceptionBuffer(void);
@@ -50,12 +51,15 @@ int             PyC_AsArray(
         const PyTypeObject *type, const bool is_double, const char *error_prefix);
 
 PyObject       *PyC_Tuple_PackArray_F32(const float *array, uint len);
+PyObject       *PyC_Tuple_PackArray_F64(const double *array, uint len);
 PyObject       *PyC_Tuple_PackArray_I32(const int *array, uint len);
 PyObject       *PyC_Tuple_PackArray_I32FromBool(const int *array, uint len);
 PyObject       *PyC_Tuple_PackArray_Bool(const bool *array, uint len);
 
 #define PyC_Tuple_Pack_F32(...) \
 	PyC_Tuple_PackArray_F32(((const float []){__VA_ARGS__}), VA_NARGS_COUNT(__VA_ARGS__))
+#define PyC_Tuple_Pack_F64(...) \
+	PyC_Tuple_PackArray_F64(((const double []){__VA_ARGS__}), VA_NARGS_COUNT(__VA_ARGS__))
 #define PyC_Tuple_Pack_I32(...) \
 	PyC_Tuple_PackArray_I32(((const int []){__VA_ARGS__}), VA_NARGS_COUNT(__VA_ARGS__))
 #define PyC_Tuple_Pack_I32FromBool(...) \
diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt
index a71478cdb1563f108c64a263420edfbf8741904d..f90985297726af1d3bd0d009fa60bee757d78f63 100644
--- a/source/blender/python/intern/CMakeLists.txt
+++ b/source/blender/python/intern/CMakeLists.txt
@@ -23,7 +23,7 @@
 #
 # ***** END GPL LICENSE BLOCK *****
 
-set(INC 
+set(INC
 	..
 	../../blenkernel
 	../../blenlib
@@ -35,9 +35,10 @@ set(INC
 	../../makesdna
 	../../makesrna
 	../../windowmanager
-	../../../../intern/cycles/blender
-	../../../../intern/opencolorio
+	../../../../intern/clog
 	../../../../intern/guardedalloc
+	../../../../intern/opencolorio
+	../../../../intern/cycles/blender
 )
 
 set(INC_SYS
@@ -119,6 +120,7 @@ set(SRC
 	bpy_utils_units.h
 	gpu.h
 	../BPY_extern.h
+	../BPY_extern_clog.h
 )
 
 # only to check if buildinfo is available
diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c
index 11a233461d86c60d3ab4533d8f2180966ca3817e..7156bed0c2b348b28064e295d54cecd7957d897a 100644
--- a/source/blender/python/intern/bpy_interface.c
+++ b/source/blender/python/intern/bpy_interface.c
@@ -33,6 +33,8 @@
 
 #include "MEM_guardedalloc.h"
 
+#include "CLG_log.h"
+
 #include "BLI_utildefines.h"
 #include "BLI_path_util.h"
 #include "BLI_fileops.h"
@@ -76,6 +78,9 @@
 #include "../bmesh/bmesh_py_api.h"
 #include "../mathutils/mathutils.h"
 
+/* Logging types to use anywhere in the Python modules. */
+CLG_LOGREF_DECLARE_GLOBAL(BPY_LOG_CONTEXT, "bpy.context");
+CLG_LOGREF_DECLARE_GLOBAL(BPY_LOG_RNA, "bpy.rna");
 
 /* for internal use, when starting and ending python scripts */
 
@@ -803,8 +808,9 @@ int BPY_context_member_get(bContext *C, const char *member, bContextDataResult *
 					CTX_data_list_add(result, ptr->id.data, ptr->type, ptr->data);
 				}
 				else {
-					printf("PyContext: '%s' list item not a valid type in sequece type '%s'\n",
-					       member, Py_TYPE(item)->tp_name);
+					CLOG_INFO(BPY_LOG_CONTEXT, 1,
+					          "'%s' list item not a valid type in sequence type '%s'",
+					          member, Py_TYPE(item)->tp_name);
 				}
 
 			}
@@ -816,16 +822,14 @@ int BPY_context_member_get(bContext *C, const char *member, bContextDataResult *
 
 	if (done == false) {
 		if (item) {
-			printf("PyContext '%s' not a valid type\n", member);
+			CLOG_INFO(BPY_LOG_CONTEXT, 1, "'%s' not a valid type", member);
 		}
 		else {
-			printf("PyContext '%s' not found\n", member);
+			CLOG_INFO(BPY_LOG_CONTEXT, 1, "'%s' not found\n", member);
 		}
 	}
 	else {
-		if (G.debug & G_DEBUG_PYTHON) {
-			printf("PyContext '%s' found\n", member);
-		}
+		CLOG_INFO(BPY_LOG_CONTEXT, 2, "'%s' found", member);
 	}
 
 	if (use_gil)
diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c
index 2aff61dc2b8a90a01a51ba139e615c474e090f05..12cce6855335a4f880cba035454cc3de6d1b71fa 100644
--- a/source/blender/python/intern/bpy_rna.c
+++ b/source/blender/python/intern/bpy_rna.c
@@ -45,6 +45,7 @@
 #include "BLI_utildefines.h"
 
 #include "BPY_extern.h"
+#include "BPY_extern_clog.h"
 
 #include "bpy_rna.h"
 #include "bpy_rna_anim.h"
@@ -61,6 +62,8 @@
 #include "RNA_define.h" /* RNA_def_property_free_identifier */
 #include "RNA_access.h"
 
+#include "CLG_log.h"
+
 #include "MEM_guardedalloc.h"
 
 #include "BKE_main.h"
@@ -1418,10 +1421,11 @@ static PyObject *pyrna_enum_to_py(PointerRNA *ptr, PropertyRNA *prop, int val)
 					const char *ptr_name = RNA_struct_name_get_alloc(ptr, NULL, 0, NULL);
 
 					/* prefer not fail silently in case of api errors, maybe disable it later */
-					printf("RNA Warning: Current value \"%d\" "
-					       "matches no enum in '%s', '%s', '%s'\n",
-					       val, RNA_struct_identifier(ptr->type),
-					       ptr_name, RNA_property_identifier(prop));
+					CLOG_WARN(BPY_LOG_RNA,
+					          "current value '%d' "
+					          "matches no enum in '%s', '%s', '%s'",
+					          val, RNA_struct_identifier(ptr->type),
+					          ptr_name, RNA_property_identifier(prop));
 
 #if 0				/* gives python decoding errors while generating docs :( */
 					char error_str[256];
@@ -6605,7 +6609,7 @@ static PyObject *pyrna_srna_ExternalType(StructRNA *srna)
 		if (bpy_types == NULL) {
 			PyErr_Print();
 			PyErr_Clear();
-			fprintf(stderr, "%s: failed to find 'bpy_types' module\n", __func__);
+			CLOG_ERROR(BPY_LOG_RNA, "failed to find 'bpy_types' module");
 			return NULL;
 		}
 		bpy_types_dict = PyModule_GetDict(bpy_types);  /* borrow */
@@ -6623,20 +6627,22 @@ static PyObject *pyrna_srna_ExternalType(StructRNA *srna)
 		PyObject *tp_slots = PyDict_GetItem(((PyTypeObject *)newclass)->tp_dict, bpy_intern_str___slots__);
 
 		if (tp_slots == NULL) {
-			fprintf(stderr, "%s: expected class '%s' to have __slots__ defined\n\nSee bpy_types.py\n", __func__, idname);
+			CLOG_ERROR(BPY_LOG_RNA, "expected class '%s' to have __slots__ defined, see bpy_types.py", idname);
 			newclass = NULL;
 		}
 		else if (PyTuple_GET_SIZE(tp_bases)) {
 			PyObject *base = PyTuple_GET_ITEM(tp_bases, 0);
 
 			if (base_compare != base) {
-				fprintf(stderr, "%s: incorrect subclassing of SRNA '%s'\nSee bpy_types.py\n", __func__, idname);
-				PyC_ObSpit("Expected! ", base_compare);
+				char pyob_info[256];
+				PyC_ObSpitStr(pyob_info, sizeof(pyob_info), base_compare);
+				CLOG_ERROR(BPY_LOG_RNA,
+				           "incorrect subclassing of SRNA '%s', expected '%s', see bpy_types.py",
+				           idname, pyob_info);
 				newclass = NULL;
 			}
 			else {
-				if (G.debug & G_DEBUG_PYTHON)
-					fprintf(stderr, "SRNA Subclassed: '%s'\n", idname);
+				CLOG_INFO(BPY_LOG_RNA, 2, "SRNA sub-classed: '%s'", idname);
 			}
 		}
 	}
@@ -6734,7 +6740,7 @@ static PyObject *pyrna_srna_Subtype(StructRNA *srna)
 		}
 		else {
 			/* this should not happen */
-			printf("%s: error registering '%s'\n", __func__, idname);
+			CLOG_ERROR(BPY_LOG_RNA, "failed to register '%s'", idname);
 			PyErr_Print();
 			PyErr_Clear();
 		}
@@ -6800,7 +6806,7 @@ PyObject *pyrna_struct_CreatePyObject(PointerRNA *ptr)
 			Py_DECREF(tp); /* srna owns, cant hold a ref */
 		}
 		else {
-			fprintf(stderr, "%s: could not make type\n", __func__);
+			CLOG_WARN(BPY_LOG_RNA, "could not make type '%s'", RNA_struct_identifier(ptr->type));
 			pyrna = (BPy_StructRNA *) PyObject_GC_New(BPy_StructRNA, &pyrna_struct_Type);
 #ifdef USE_WEAKREFS
 			pyrna->in_weakreflist = NULL;
@@ -7607,8 +7613,7 @@ static int bpy_class_call(bContext *C, PointerRNA *ptr, FunctionRNA *func, Param
 	py_class = RNA_struct_py_type_get(ptr->type);
 	/* rare case. can happen when registering subclasses */
 	if (py_class == NULL) {
-		fprintf(stderr, "%s: unable to get python class for rna struct '%.200s'\n",
-		        __func__, RNA_struct_identifier(ptr->type));
+		CLOG_WARN(BPY_LOG_RNA, "unable to get Python class for rna struct '%.200s'", RNA_struct_identifier(ptr->type));
 		return -1;
 	}
 
diff --git a/source/blender/python/intern/bpy_rna_array.c b/source/blender/python/intern/bpy_rna_array.c
index e0ca363426175cb17f134e789a3caf6466bb0290..ed9d1e9c0e5de5e0e44e024229d48745a3080f8c 100644
--- a/source/blender/python/intern/bpy_rna_array.c
+++ b/source/blender/python/intern/bpy_rna_array.c
@@ -28,6 +28,8 @@
 
 #include <Python.h>
 
+#include "CLG_log.h"
+
 #include "BLI_utildefines.h"
 
 #include "RNA_types.h"
@@ -39,6 +41,8 @@
 
 #include "RNA_access.h"
 
+#include "BPY_extern_clog.h"
+
 #include "../generic/py_capi_utils.h"
 
 #define USE_MATHUTILS
@@ -785,8 +789,7 @@ PyObject *pyrna_py_from_array_index(BPy_PropertyArrayRNA *self, PointerRNA *ptr,
 	len = RNA_property_multi_array_length(ptr, prop, arraydim);
 	if (index >= len || index < 0) {
 		/* this shouldn't happen because higher level funcs must check for invalid index */
-		if (G.debug & G_DEBUG_PYTHON)
-			printf("%s: invalid index %d for array with length=%d\n", __func__, index, len);
+		CLOG_WARN(BPY_LOG_RNA, "invalid index %d for array with length=%d", index, len);
 
 		PyErr_SetString(PyExc_IndexError, "out of range");
 		return NULL;
diff --git a/source/blender/python/intern/gpu_offscreen.c b/source/blender/python/intern/gpu_offscreen.c
index 2da0227cdcac6d4d220723ae6203561bbfdcc4b7..72d338bd5db058b9cb4c769feb1fcd20bbaaf84d 100644
--- a/source/blender/python/intern/gpu_offscreen.c
+++ b/source/blender/python/intern/gpu_offscreen.c
@@ -37,7 +37,6 @@
 
 #include "ED_screen.h"
 
-#include "GPU_compositing.h"
 #include "GPU_framebuffer.h"
 #include "GPU_texture.h"
 
diff --git a/source/blender/python/mathutils/mathutils_Euler.c b/source/blender/python/mathutils/mathutils_Euler.c
index 9492b6d67f3dcd4bb15bffa1a929d7c89088bba9..026384743bd7fe3650121da483e355db41f51f5e 100644
--- a/source/blender/python/mathutils/mathutils_Euler.c
+++ b/source/blender/python/mathutils/mathutils_Euler.c
@@ -689,6 +689,8 @@ PyDoc_STRVAR(euler_doc,
 "\n"
 "   This object gives access to Eulers in Blender.\n"
 "\n"
+"   .. seealso:: `Euler angles <https://en.wikipedia.org/wiki/Euler_angles>`__ on Wikipedia.\n"
+"\n"
 "   :param angles: Three angles, in radians.\n"
 "   :type angles: 3d vector\n"
 "   :param order: Optional order of the angles, a permutation of ``XYZ``.\n"
diff --git a/source/blender/render/extern/include/RE_engine.h b/source/blender/render/extern/include/RE_engine.h
index ad0cb34d3821e86a1fec04ddd1fba1dea3fb32a2..2a1d51158b3f71d7ac746030ae078c3b4d8876b7 100644
--- a/source/blender/render/extern/include/RE_engine.h
+++ b/source/blender/render/extern/include/RE_engine.h
@@ -39,7 +39,10 @@
 
 struct bNode;
 struct bNodeTree;
+struct BakePixel;
+struct Depsgraph;
 struct Depsgraph;
+struct EvaluationContext;
 struct IDProperty;
 struct Main;
 struct Object;
@@ -51,7 +54,7 @@ struct RenderLayer;
 struct RenderResult;
 struct ReportList;
 struct Scene;
-struct BakePixel;
+struct ViewLayer;
 
 /* External Engine */
 
@@ -133,6 +136,11 @@ typedef struct RenderEngine {
 
 	struct ReportList *reports;
 
+	/* Depsgraph */
+	struct EvaluationContext *eval_ctx;
+	struct Depsgraph *depsgraph;
+	struct ViewLayer *view_layer;
+
 	/* for blender internal only */
 	int update_flag;
 	int job_update_flag;
diff --git a/source/blender/render/intern/source/convertblender.c b/source/blender/render/intern/source/convertblender.c
index 0480ffe42d9a7f6b61a3862616f79d44aaa10f8d..419316d6217b33471268fbe7b2629a0b4ba25faf 100644
--- a/source/blender/render/intern/source/convertblender.c
+++ b/source/blender/render/intern/source/convertblender.c
@@ -1341,7 +1341,7 @@ static int render_new_particle_system(const EvaluationContext *eval_ctx, Render
 	if (part->ren_as==PART_DRAW_OB || part->ren_as==PART_DRAW_GR || part->ren_as==PART_DRAW_NOT)
 		return 1;
 
-	if ((re->r.scemode & R_VIEWPORT_PREVIEW) && (eval_ctx->object_mode & OB_MODE_PARTICLE_EDIT))
+	if ((re->r.scemode & R_VIEWPORT_PREVIEW) && (ob->mode & OB_MODE_PARTICLE_EDIT))
 		return 0;
 
 	if (part->ren_as == PART_DRAW_BB && part->bb_ob == NULL && RE_GetCamera(re) == NULL)
diff --git a/source/blender/render/intern/source/external_engine.c b/source/blender/render/intern/source/external_engine.c
index 285cf8d660bd83e129ce461332bf9fa31b5b276e..617e49c5d0f2247d14d33e64dfd30d1ff6eeeb17 100644
--- a/source/blender/render/intern/source/external_engine.c
+++ b/source/blender/render/intern/source/external_engine.c
@@ -527,6 +527,65 @@ RenderData *RE_engine_get_render_data(Render *re)
 	return &re->r;
 }
 
+/* Depsgraph */
+static void engine_depsgraph_init(RenderEngine *engine, ViewLayer *view_layer)
+{
+	engine->eval_ctx = DEG_evaluation_context_new(DAG_EVAL_RENDER);
+	engine->depsgraph = DEG_graph_new();
+	engine->view_layer = view_layer;
+
+	DEG_evaluation_context_init_from_view_layer_for_render(
+		engine->eval_ctx,
+		engine->depsgraph,
+		engine->re->scene,
+		view_layer);
+
+	BKE_scene_graph_update_tagged(
+		engine->eval_ctx,
+		engine->depsgraph,
+		engine->re->main,
+		engine->re->scene,
+		view_layer);
+}
+
+static void engine_depsgraph_free(RenderEngine *engine)
+{
+	DEG_graph_free(engine->depsgraph);
+	DEG_evaluation_context_free(engine->eval_ctx);
+
+	engine->eval_ctx = NULL;
+	engine->depsgraph = NULL;
+	engine->view_layer = NULL;
+}
+
+void RE_engine_frame_set(RenderEngine *engine, int frame, float subframe)
+{
+	if(!engine->depsgraph) {
+		return;
+	}
+
+	Render *re = engine->re;
+	double cfra = (double)frame + (double)subframe;
+
+	CLAMP(cfra, MINAFRAME, MAXFRAME);
+	BKE_scene_frame_set(re->scene, cfra);
+	BKE_scene_graph_update_for_newframe(engine->eval_ctx,
+	                                    engine->depsgraph,
+	                                    re->main,
+	                                    re->scene,
+	                                    engine->view_layer);
+
+#ifdef WITH_PYTHON
+	BPy_BEGIN_ALLOW_THREADS;
+#endif
+
+#ifdef WITH_PYTHON
+	BPy_END_ALLOW_THREADS;
+#endif
+
+	BKE_scene_camera_switch_update(re->scene);
+}
+
 /* Bake */
 void RE_bake_engine_set_engine_parameters(Render *re, Main *bmain, Scene *scene)
 {
@@ -583,20 +642,11 @@ bool RE_bake_engine(
 		type->update(engine, re->main, re->scene);
 
 	if (type->bake) {
-		EvaluationContext *eval_ctx = DEG_evaluation_context_new(DAG_EVAL_RENDER);
-		Depsgraph *depsgraph = DEG_graph_new();
 		ViewLayer *view_layer = BLI_findlink(&re->scene->view_layers, re->scene->active_view_layer);
-
-		DEG_evaluation_context_init_from_view_layer_for_render(
-					eval_ctx,
-					depsgraph,
-					re->scene,
-					view_layer);
-
-		BKE_scene_graph_update_tagged(eval_ctx, depsgraph, re->main, re->scene, view_layer);
+		engine_depsgraph_init(engine, view_layer);
 
 		type->bake(engine,
-		           depsgraph,
+		           engine->depsgraph,
 		           re->scene,
 		           object,
 		           pass_type,
@@ -607,8 +657,7 @@ bool RE_bake_engine(
 		           depth,
 		           result);
 
-		DEG_graph_free(depsgraph);
-		DEG_evaluation_context_free(eval_ctx);
+		engine_depsgraph_free(engine);
 	}
 
 	engine->tile_x = 0;
@@ -632,26 +681,6 @@ bool RE_bake_engine(
 	return true;
 }
 
-void RE_engine_frame_set(RenderEngine *engine, int frame, float subframe)
-{
-	Render *re = engine->re;
-	Scene *scene = re->scene;
-	double cfra = (double)frame + (double)subframe;
-
-	CLAMP(cfra, MINAFRAME, MAXFRAME);
-	BKE_scene_frame_set(scene, cfra);
-
-#ifdef WITH_PYTHON
-	BPy_BEGIN_ALLOW_THREADS;
-#endif
-
-#ifdef WITH_PYTHON
-	BPy_END_ALLOW_THREADS;
-#endif
-
-	BKE_scene_camera_switch_update(scene);
-}
-
 /* Render */
 
 int RE_engine_render(Render *re, int do_all)
@@ -755,21 +784,12 @@ int RE_engine_render(Render *re, int do_all)
 	if (type->render_to_image) {
 		FOREACH_VIEW_LAYER_TO_RENDER_BEGIN(re, view_layer_iter)
 		{
-			EvaluationContext *eval_ctx = DEG_evaluation_context_new(DAG_EVAL_RENDER);
-			Depsgraph *depsgraph = DEG_graph_new();
 			ViewLayer *view_layer = BLI_findstring(&re->scene->view_layers, view_layer_iter->name, offsetof(ViewLayer, name));
+			engine_depsgraph_init(engine, view_layer);
 
-			DEG_evaluation_context_init_from_view_layer_for_render(
-						eval_ctx,
-						depsgraph,
-						re->scene,
-						view_layer);
-
-			BKE_scene_graph_update_tagged(eval_ctx, depsgraph, re->main, re->scene, view_layer);
-			type->render_to_image(engine, depsgraph);
+			type->render_to_image(engine, engine->depsgraph);
 
-			DEG_graph_free(depsgraph);
-			DEG_evaluation_context_free(eval_ctx);
+			engine_depsgraph_free(engine);
 		}
 		FOREACH_VIEW_LAYER_TO_RENDER_END;
 	}
diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c
index 170cd0ad41997984561bb9d947752e10135d83e9..d6e45788942c49811d55f9f8a36a0ef77ac8a6a5 100644
--- a/source/blender/render/intern/source/pipeline.c
+++ b/source/blender/render/intern/source/pipeline.c
@@ -88,6 +88,7 @@
 #include "IMB_colormanagement.h"
 #include "IMB_imbuf.h"
 #include "IMB_imbuf_types.h"
+#include "IMB_metadata.h"
 
 #include "RE_engine.h"
 #include "RE_pipeline.h"
@@ -3343,7 +3344,7 @@ bool RE_WriteRenderViewsImage(ReportList *reports, RenderResult *rr, Scene *scen
 	if (!rr)
 		return false;
 
-	bool is_mono = BLI_listbase_count_ex(&rr->views, 2) < 2;
+	bool is_mono = BLI_listbase_count_at_most(&rr->views, 2) < 2;
 	bool is_exr_rr = ELEM(rd->im_format.imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER) &&
 	                 RE_HasFloatPixels(rr);
 
@@ -3460,7 +3461,7 @@ bool RE_WriteRenderViewsMovie(
 	if (!rr)
 		return false;
 
-	is_mono = BLI_listbase_count_ex(&rr->views, 2) < 2;
+	is_mono = BLI_listbase_count_at_most(&rr->views, 2) < 2;
 
 	if (is_mono || (scene->r.im_format.views_format == R_IMF_VIEWS_INDIVIDUAL)) {
 		int view_id;
@@ -4050,7 +4051,7 @@ bool RE_WriteEnvmapResult(struct ReportList *reports, Scene *scene, EnvMap *env,
 /* Used in the interface to decide whether to show layers or passes. */
 bool RE_layers_have_name(struct RenderResult *rr)
 {
-	switch (BLI_listbase_count_ex(&rr->layers, 2)) {
+	switch (BLI_listbase_count_at_most(&rr->layers, 2)) {
 		case 0:
 			return false;
 		case 1:
diff --git a/source/blender/render/intern/source/render_result.c b/source/blender/render/intern/source/render_result.c
index c5d1dfc50f4ad24f181b7d5694649e893accd023..5abef431f3dba9f8e4e445c1625b3fa17acb5fcd 100644
--- a/source/blender/render/intern/source/render_result.c
+++ b/source/blender/render/intern/source/render_result.c
@@ -740,7 +740,7 @@ void render_result_views_new(RenderResult *rr, RenderData *rd)
 	}
 
 	/* we always need at least one view */
-	if (BLI_listbase_count_ex(&rr->views, 1) == 0) {
+	if (BLI_listbase_count_at_most(&rr->views, 1) == 0) {
 		render_result_view_new(rr, "");
 	}
 }
diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt
index 234491a21861b39a1027c69602127a5b13c4406e..da61a201ef6d3905c32e073ff141ab31437d2dfa 100644
--- a/source/blender/windowmanager/CMakeLists.txt
+++ b/source/blender/windowmanager/CMakeLists.txt
@@ -43,6 +43,7 @@ set(INC
 	../nodes
 	../render/extern/include
 	../../gameengine/BlenderRoutines
+	../../../intern/clog
 	../../../intern/ghost
 	../../../intern/guardedalloc
 	../../../intern/glew-mx
diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h
index a6b27de895b900b509df01718dd902c2adddfb67..c9ccdb80cc4eb543dc277b6f0b3f6a5c52e80d20 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -67,8 +67,7 @@ struct ARegion;
 struct ScrArea;
 struct Main;
 struct bToolDef;
-
-#include "DNA_object_enums.h"
+struct ViewLayer;
 
 #ifdef WITH_INPUT_NDOF
 struct wmNDOFMotionData;
@@ -105,7 +104,6 @@ bool		WM_window_is_fullscreen	(struct wmWindow *win);
 void WM_windows_scene_data_sync(const ListBase *win_lb, struct Scene *scene) ATTR_NONNULL();
 struct Scene *WM_windows_scene_get_from_screen(const struct wmWindowManager *wm, const struct bScreen *screen) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT;
 struct WorkSpace *WM_windows_workspace_get_from_screen(const wmWindowManager *wm, const struct bScreen *screen) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT;
-eObjectMode WM_windows_object_mode_get(const struct wmWindowManager *wm) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT;
 
 struct Scene *WM_window_get_active_scene(const struct wmWindow *win) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT;
 void          WM_window_change_active_scene(struct Main *bmain, struct bContext *C, struct wmWindow *win,
@@ -117,6 +115,10 @@ void                    WM_window_set_active_layout(
         struct wmWindow *win, struct WorkSpace *workspace, struct WorkSpaceLayout *layout) ATTR_NONNULL(1);
 struct bScreen *WM_window_get_active_screen(const struct wmWindow *win) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT;
 void            WM_window_set_active_screen(struct wmWindow *win, struct WorkSpace *workspace, struct bScreen *screen) ATTR_NONNULL(1);
+
+struct ViewLayer *WM_window_get_active_view_layer_ex(const struct wmWindow *win, struct Scene **r_scene) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
+struct ViewLayer *WM_window_get_active_view_layer(const struct wmWindow *win) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
+
 bool WM_window_is_temp_screen(const struct wmWindow *win) ATTR_WARN_UNUSED_RESULT;
 
 void *WM_opengl_context_create(void);
@@ -220,7 +222,7 @@ enum {
 struct wmEventHandler *WM_event_add_dropbox_handler(ListBase *handlers, ListBase *dropboxes);
 
 			/* mouse */
-void		WM_event_add_mousemove(struct bContext *C);
+void		WM_event_add_mousemove(const struct bContext *C);
 bool		WM_event_is_modal_tweak_exit(const struct wmEvent *event, int tweak_event);
 bool		WM_event_is_last_mousemove(const struct wmEvent *event);
 
diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h
index a09814cf5dd54b3c21316762ed803e641a0565b5..4bd5bcfc056b1a65e65552d706e4e4576275d007 100644
--- a/source/blender/windowmanager/WM_types.h
+++ b/source/blender/windowmanager/WM_types.h
@@ -731,6 +731,14 @@ typedef struct RecentFile {
 	char *filepath;
 } RecentFile;
 
+/* Logging */
+struct CLG_LogRef;
+/* wm_init_exit.c */
+extern struct CLG_LogRef *WM_LOG_OPERATORS;
+extern struct CLG_LogRef *WM_LOG_HANDLERS;
+extern struct CLG_LogRef *WM_LOG_EVENTS;
+extern struct CLG_LogRef *WM_LOG_KEYMAPS;
+
 
 #ifdef __cplusplus
 }
diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c
index 54c2d7a3aef69fd3c17264e931e15100b01b3354..271072998636eacacf5e0b204353da7518651b4f 100644
--- a/source/blender/windowmanager/intern/wm.c
+++ b/source/blender/windowmanager/intern/wm.c
@@ -63,6 +63,7 @@
 #include "wm.h"
 
 #include "ED_screen.h"
+#include "BKE_undo_system.h"
 
 #ifdef WITH_PYTHON
 #include "BPY_extern.h"
@@ -506,7 +507,12 @@ void wm_close_and_free(bContext *C, wmWindowManager *wm)
 	WM_drag_free_list(&wm->drags);
 	
 	wm_reports_free(wm);
-	
+
+	if (wm->undo_stack) {
+		BKE_undosys_stack_destroy(wm->undo_stack);
+		wm->undo_stack = NULL;
+	}
+
 	if (C && CTX_wm_manager(C) == wm) CTX_wm_manager_set(C, NULL);
 }
 
diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c
index 4a6bc2ded28ad2645dc1d79c13bc7a475a6d1095..ed276aa690d1b0a96f27932fcaaf6b18d307f7b4 100644
--- a/source/blender/windowmanager/intern/wm_draw.c
+++ b/source/blender/windowmanager/intern/wm_draw.c
@@ -474,39 +474,26 @@ void wm_triple_draw_textures(wmWindow *win, wmDrawTriple *triple, float alpha)
 	const float halfx = GLA_PIXEL_OFS / sizex;
 	const float halfy = GLA_PIXEL_OFS / sizey;
 
-	Gwn_VertFormat *format = immVertexFormat();
-	unsigned int texcoord = GWN_vertformat_attr_add(format, "texCoord", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
-	unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
-
 	const int activeTex = 7; /* arbitrary */
 	glActiveTexture(GL_TEXTURE0 + activeTex);
 	glBindTexture(GL_TEXTURE_2D, triple->bind);
 
-	immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_ALPHA);
-
-	immUniform1f("alpha", alpha);
-	immUniform1i("image", activeTex);
-
-	immBegin(GWN_PRIM_TRI_FAN, 4);
-
-	immAttrib2f(texcoord, halfx, halfy);
-	immVertex2f(pos, 0.0f, 0.0f);
-
-	immAttrib2f(texcoord, ratiox + halfx, halfy);
-	immVertex2f(pos, sizex, 0.0f);
+	float x1 = halfx;
+	float x2 = ratiox + halfx;
+	float y1 = halfy;
+	float y2 = ratioy + halfy;
 
-	immAttrib2f(texcoord, ratiox + halfx, ratioy + halfy);
-	immVertex2f(pos, sizex, sizey);
+	GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_IMAGE_RECT_COLOR);
+	GPU_shader_bind(shader);
 
-	immAttrib2f(texcoord, halfx, ratioy + halfy);
-	immVertex2f(pos, 0.0f, sizey);
+	glUniform1i(GPU_shader_get_uniform(shader, "image"), activeTex);
+	glUniform4f(GPU_shader_get_uniform(shader, "rect_icon"), x1, y1, x2, y2);
+	glUniform4f(GPU_shader_get_uniform(shader, "rect_geom"), 0.0f, 0.0f, sizex, sizey);
+	glUniform4f(GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_COLOR), alpha, alpha, alpha, alpha);
 
-	immEnd();
-	immUnbindProgram();
+	GWN_draw_primitive(GWN_PRIM_TRI_STRIP, 4);
 
 	glBindTexture(GL_TEXTURE_2D, 0);
-	if (activeTex != 0)
-		glActiveTexture(GL_TEXTURE0);
 }
 
 static void wm_triple_copy_textures(wmWindow *win, wmDrawTriple *triple)
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index b6a9115cca45797a5ce8faaa6bfe6a9ff429c5b6..fcf06804e94baff72de9ec37cca5559d4224af7e 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -43,6 +43,8 @@
 
 #include "MEM_guardedalloc.h"
 
+#include "CLG_log.h"
+
 #include "GHOST_C-api.h"
 
 #include "BLI_blenlib.h"
@@ -67,6 +69,7 @@
 #include "ED_screen.h"
 #include "ED_view3d.h"
 #include "ED_util.h"
+#include "ED_undo.h"
 
 #include "RNA_access.h"
 
@@ -335,8 +338,6 @@ void wm_event_do_refresh_wm_and_depsgraph(bContext *C)
 
 			WorkSpace *workspace = WM_window_get_active_workspace(win);
 
-			BKE_workspace_update_object_mode(bmain->eval_ctx, workspace);
-
 			BKE_workspace_update_tagged(bmain->eval_ctx, bmain, workspace, scene);
 		}
 	}
@@ -381,7 +382,7 @@ void wm_event_do_notifiers(bContext *C)
 
 						UI_popup_handlers_remove_all(C, &win->modalhandlers);
 
-						ED_workspace_change(ref_ws, C, win);
+						ED_workspace_change(ref_ws, C, wm, win);
 						if (G.debug & G_DEBUG_EVENTS)
 							printf("%s: Workspace set %p\n", __func__, note->reference);
 					}
@@ -608,14 +609,6 @@ int WM_operator_poll_context(bContext *C, wmOperatorType *ot, short context)
 	return wm_operator_call_internal(C, ot, NULL, NULL, context, true);
 }
 
-static void wm_operator_print(bContext *C, wmOperator *op)
-{
-	/* context is needed for enum function */
-	char *buf = WM_operator_pystring(C, op, false, true);
-	puts(buf);
-	MEM_freeN(buf);
-}
-
 /**
  * Sets the active region for this space from the context.
  *
@@ -776,10 +769,7 @@ static void wm_operator_reports(bContext *C, wmOperator *op, int retval, bool ca
 	}
 	
 	if (retval & OPERATOR_FINISHED) {
-		if (G.debug & G_DEBUG_WM) {
-			/* todo - this print may double up, might want to check more flags then the FINISHED */
-			wm_operator_print(C, op);
-		}
+		CLOG_STR_INFO_N(WM_LOG_OPERATORS, 1, WM_operator_pystring(C, op, false, true));
 
 		if (caller_owns_reports == false) {
 			BKE_reports_print(op->reports, RPT_DEBUG); /* print out reports to console. */
@@ -1104,9 +1094,7 @@ bool WM_operator_last_properties_init(wmOperator *op)
 		IDProperty *replaceprops = IDP_New(IDP_GROUP, &val, "wmOperatorProperties");
 		PropertyRNA *iterprop;
 
-		if (G.debug & G_DEBUG_WM) {
-			printf("%s: loading previous properties for '%s'\n", __func__, op->type->idname);
-		}
+		CLOG_INFO(WM_LOG_OPERATORS, 1, "loading previous properties for '%s'", op->type->idname);
 
 		iterprop = RNA_struct_iterator_property(op->type->srna);
 
@@ -1151,9 +1139,7 @@ bool WM_operator_last_properties_store(wmOperator *op)
 	}
 
 	if (op->properties) {
-		if (G.debug & G_DEBUG_WM) {
-			printf("%s: storing properties for '%s'\n", __func__, op->type->idname);
-		}
+		CLOG_INFO(WM_LOG_OPERATORS, 1, "storing properties for '%s'", op->type->idname);
 		op->type->last_properties = IDP_CopyProperty(op->properties);
 		return true;
 	}
@@ -1203,11 +1189,12 @@ static int wm_operator_invoke(
 			WM_operator_last_properties_init(op);
 		}
 
-		if ((G.debug & G_DEBUG_HANDLERS) && ((event == NULL) || (event->type != MOUSEMOVE))) {
-			printf("%s: handle evt %d region %p op %s\n",
-			       __func__, event ? event->type : 0, CTX_wm_screen(C)->active_region, ot->idname);
+		if ((event == NULL) || (event->type != MOUSEMOVE)) {
+			CLOG_INFO(WM_LOG_HANDLERS, 2,
+			          "handle evt %d win %p op %s",
+			          event ? event->type : 0, CTX_wm_screen(C)->active_region, ot->idname);
 		}
-		
+
 		if (op->type->invoke && event) {
 			wm_region_mouse_co(C, event);
 
@@ -1232,9 +1219,9 @@ static int wm_operator_invoke(
 		}
 		else {
 			/* debug, important to leave a while, should never happen */
-			printf("%s: invalid operator call '%s'\n", __func__, ot->idname);
+			CLOG_ERROR(WM_LOG_OPERATORS, "invalid operator call '%s'", op->idname);
 		}
-		
+
 		/* Note, if the report is given as an argument then assume the caller will deal with displaying them
 		 * currently python only uses this */
 		if (!(retval & OPERATOR_HANDLED) && (retval & (OPERATOR_FINISHED | OPERATOR_CANCELLED))) {
@@ -1509,8 +1496,10 @@ int WM_operator_call_py(
 		if (is_undo && op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
 			wm->op_undo_depth--;
 	}
-	else
-		printf("error \"%s\" operator has no exec function, python cannot call it\n", op->type->name);
+	else {
+		CLOG_WARN(WM_LOG_OPERATORS, "\"%s\" operator has no exec function, Python cannot call it", op->type->name);
+	}
+
 #endif
 
 	/* not especially nice using undo depth here, its used so py never
@@ -1554,8 +1543,9 @@ static void wm_handler_op_context(bContext *C, wmEventHandler *handler, const wm
 			if (sa == NULL) {
 				/* when changing screen layouts with running modal handlers (like render display), this
 				 * is not an error to print */
-				if (handler->op == NULL)
-					printf("internal error: handler (%s) has invalid area\n", handler->op->type->idname);
+				if (handler->op == NULL) {
+					CLOG_ERROR(WM_LOG_HANDLERS, "internal error: handler (%s) has invalid area", handler->op->type->idname);
+				}
 			}
 			else {
 				ARegion *ar;
@@ -1901,10 +1891,9 @@ static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHand
 					//retval &= ~OPERATOR_PASS_THROUGH;
 				}
 			}
-			
 		}
 		else {
-			printf("%s: error '%s' missing modal\n", __func__, op->idname);
+			CLOG_ERROR(WM_LOG_HANDLERS, "missing modal '%s'", op->idname);
 		}
 	}
 	else {
@@ -2195,19 +2184,15 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers
 							action |= wm_handler_operator_call(C, handlers, handler, event, kmi->ptr);
 							if (action & WM_HANDLER_BREAK) {
 								/* not always_pass here, it denotes removed handler */
-								
-								if (G.debug & (G_DEBUG_EVENTS | G_DEBUG_HANDLERS))
-									printf("%s:       handled! '%s'\n", __func__, kmi->idname);
-
+								CLOG_INFO(WM_LOG_HANDLERS, 2, "handled! '%s'", kmi->idname);
 								break;
 							}
 							else {
 								if (action & WM_HANDLER_HANDLED) {
-									if (G.debug & (G_DEBUG_EVENTS | G_DEBUG_HANDLERS))
-										printf("%s:       handled - and pass on! '%s'\n", __func__, kmi->idname);
+									CLOG_INFO(WM_LOG_HANDLERS, 2, "handled - and pass on! '%s'", kmi->idname);
 								}
 								else {
-									PRINT("%s:       un-handled '%s'\n", __func__, kmi->idname);
+									CLOG_INFO(WM_LOG_HANDLERS, 2, "un-handled '%s'", kmi->idname);
 								}
 							}
 						}
@@ -2466,10 +2451,8 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
 					{
 						event->val = KM_CLICK;
 
-						if (G.debug & (G_DEBUG_HANDLERS)) {
-							printf("%s: handling CLICK\n", __func__);
-						}
-
+						CLOG_INFO(WM_LOG_HANDLERS, 1, "handling CLICK");
+	
 						action |= wm_handlers_do_intern(C, event, handlers);
 
 						event->val = KM_RELEASE;
@@ -2708,8 +2691,8 @@ void wm_event_do_handlers(bContext *C)
 
 			/* take care of pie event filter */
 			if (wm_event_pie_filter(win, event)) {
-				if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS) && !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
-					printf("\n%s: event filtered due to pie button pressed\n", __func__);
+				if (!ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+					CLOG_INFO(WM_LOG_HANDLERS, 1, "event filtered due to pie button pressed");
 				}
 				BLI_remlink(&win->queue, event);
 				wm_event_free(event);
@@ -3058,7 +3041,7 @@ wmEventHandler *WM_event_add_keymap_handler(ListBase *handlers, wmKeyMap *keymap
 	wmEventHandler *handler;
 
 	if (!keymap) {
-		printf("%s: called with NULL keymap\n", __func__);
+		CLOG_WARN(WM_LOG_HANDLERS, "called with NULL keymap");
 		return NULL;
 	}
 
@@ -3226,7 +3209,7 @@ static void WM_event_remove_handler(ListBase *handlers, wmEventHandler *handler)
 }
 #endif
 
-void WM_event_add_mousemove(bContext *C)
+void WM_event_add_mousemove(const bContext *C)
 {
 	wmWindow *window = CTX_wm_window(C);
 	
@@ -3679,8 +3662,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
 			
 			/* double click test */
 			if (wm_event_is_double_click(&event, evt)) {
-				if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS))
-					printf("%s Send double click\n", __func__);
+				CLOG_INFO(WM_LOG_HANDLERS, 1, "Send double click");
 				event.val = KM_DBL_CLICK;
 			}
 			if (event.val == KM_PRESS) {
@@ -3734,7 +3716,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
 
 				/* ghost should do this already for key up */
 				if (event.utf8_buf[0]) {
-					printf("%s: ghost on your platform is misbehaving, utf8 events on key up!\n", __func__);
+					CLOG_ERROR(WM_LOG_EVENTS, "ghost on your platform is misbehaving, utf8 events on key up!");
 				}
 				event.utf8_buf[0] = '\0';
 			}
@@ -3747,8 +3729,9 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
 
 			if (event.utf8_buf[0]) {
 				if (BLI_str_utf8_size(event.utf8_buf) == -1) {
-					printf("%s: ghost detected an invalid unicode character '%d'!\n",
-					       __func__, (int)(unsigned char)event.utf8_buf[0]);
+					CLOG_ERROR(WM_LOG_EVENTS,
+					           "ghost detected an invalid unicode character '%d'",
+					           (int)(unsigned char)event.utf8_buf[0]);
 					event.utf8_buf[0] = '\0';
 				}
 			}
@@ -3797,8 +3780,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
 			/* double click test */
 			/* if previous event was same type, and previous was release, and now it presses... */
 			if (wm_event_is_double_click(&event, evt)) {
-				if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS))
-					printf("%s Send double click\n", __func__);
+				CLOG_INFO(WM_LOG_HANDLERS, 1, "Send double click");
 				evt->val = event.val = KM_DBL_CLICK;
 			}
 			
@@ -3868,9 +3850,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
 			attach_ndof_data(&event, customdata);
 			wm_event_add(win, &event);
 
-			if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS))
-				printf("%s sending NDOF_MOTION, prev = %d %d\n", __func__, event.x, event.y);
-
+			CLOG_INFO(WM_LOG_HANDLERS, 1, "sending NDOF_MOTION, prev = %d %d", event.x, event.y);
 			break;
 		}
 
diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c
index 8c0f78a982436c1160340b2444e41819a7f32623..897a6308dd3e2c3f374d2fc38a8da8dceab5fb29 100644
--- a/source/blender/windowmanager/intern/wm_files.c
+++ b/source/blender/windowmanager/intern/wm_files.c
@@ -62,6 +62,8 @@
 
 #include "BLT_translation.h"
 
+#include "BLF_api.h"
+
 #include "DNA_mesh_types.h" /* only for USE_BMESH_SAVE_AS_COMPAT */
 #include "DNA_object_types.h"
 #include "DNA_space_types.h"
@@ -85,10 +87,12 @@
 #include "BKE_sound.h"
 #include "BKE_scene.h"
 #include "BKE_screen.h"
+#include "BKE_undo_system.h"
 #include "BKE_workspace.h"
 
 #include "BLO_readfile.h"
 #include "BLO_writefile.h"
+#include "BLO_undofile.h"  /* to save from an undo memfile */
 
 #include "RNA_access.h"
 #include "RNA_define.h"
@@ -102,6 +106,7 @@
 #include "ED_screen.h"
 #include "ED_view3d.h"
 #include "ED_util.h"
+#include "ED_undo.h"
 
 #include "GHOST_C-api.h"
 #include "GHOST_Path-api.h"
@@ -353,6 +358,8 @@ static void wm_init_userdef(Main *bmain, const bool read_userdef_from_memory)
 
 	/* update tempdir from user preferences */
 	BKE_tempdir_init(U.tempdir);
+
+	BLF_antialias_set((U.text_render & USER_TEXT_DISABLE_AA) == 0);
 }
 
 
@@ -530,9 +537,13 @@ static void wm_file_read_post(bContext *C, const bool is_startup_file, const boo
 	}
 
 	if (!G.background) {
-//		undo_editmode_clear();
-		BKE_undo_reset();
-		BKE_undo_write(C, "original");  /* save current state */
+		if (wm->undo_stack == NULL) {
+			wm->undo_stack = BKE_undosys_stack_create();
+		}
+		else {
+			BKE_undosys_stack_clear(wm->undo_stack);
+		}
+		BKE_undosys_stack_init_from_main(wm->undo_stack, CTX_data_main(C));
 	}
 }
 
@@ -1046,14 +1057,14 @@ static ImBuf *blend_file_thumb(const bContext *C, Scene *scene, ViewLayer *view_
 		        &eval_ctx, scene, view_layer, scene->camera,
 		        BLEN_THUMB_SIZE * 2, BLEN_THUMB_SIZE * 2,
 		        IB_rect, V3D_OFSDRAW_NONE, OB_SOLID, R_ALPHAPREMUL, 0, NULL,
-		        NULL, NULL, err_out);
+		        NULL, err_out);
 	}
 	else {
 		ibuf = ED_view3d_draw_offscreen_imbuf(
 		        &eval_ctx, scene, view_layer, v3d, ar,
 		        BLEN_THUMB_SIZE * 2, BLEN_THUMB_SIZE * 2,
 		        IB_rect, V3D_OFSDRAW_NONE, R_ALPHAPREMUL, 0, NULL,
-		        NULL, NULL, err_out);
+		        NULL, err_out);
 	}
 
 	if (ibuf) {
@@ -1283,7 +1294,10 @@ void wm_autosave_timer(const bContext *C, wmWindowManager *wm, wmTimer *UNUSED(w
 
 	if (U.uiflag & USER_GLOBALUNDO) {
 		/* fast save of last undobuffer, now with UI */
-		BKE_undo_save_file(filepath);
+		struct MemFile *memfile = ED_undosys_stack_memfile_get_active(wm->undo_stack);
+		if (memfile) {
+			BLO_memfile_write_file(memfile, filepath);
+		}
 	}
 	else {
 		/*  save as regular blend file */
@@ -2127,6 +2141,10 @@ static int wm_save_as_mainfile_exec(bContext *C, wmOperator *op)
 
 	WM_event_add_notifier(C, NC_WM | ND_FILESAVE, NULL);
 
+	if (RNA_boolean_get(op->ptr, "exit")) {
+		wm_exit_schedule_delayed(C);
+	}
+
 	return OPERATOR_FINISHED;
 }
 
@@ -2226,12 +2244,16 @@ void WM_OT_save_mainfile(wmOperatorType *ot)
 	ot->check = blend_save_check;
 	/* omit window poll so this can work in background mode */
 
+	PropertyRNA *prop;
 	WM_operator_properties_filesel(
 	        ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_BLENDER, FILE_SAVE,
 	        WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA);
 	RNA_def_boolean(ot->srna, "compress", false, "Compress", "Write compressed .blend file");
 	RNA_def_boolean(ot->srna, "relative_remap", false, "Remap Relative",
 	                "Remap relative paths when saving in a different directory");
+
+	prop = RNA_def_boolean(ot->srna, "exit", false, "Exit", "Exit Blender after saving");
+	RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
 }
 
 /** \} */
diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c
index 98ac3eebb57ca20f4a44ef92b8c28103eed814b8..4c7c2cc96e41c858623b728cd90330dc7610b4cb 100644
--- a/source/blender/windowmanager/intern/wm_init_exit.c
+++ b/source/blender/windowmanager/intern/wm_init_exit.c
@@ -40,6 +40,8 @@
 
 #include "MEM_guardedalloc.h"
 
+#include "CLG_log.h"
+
 #include "DNA_genfile.h"
 #include "DNA_scene_types.h"
 #include "DNA_userdef_types.h"
@@ -53,6 +55,7 @@
 #include "BLI_utildefines.h"
 
 #include "BLO_writefile.h"
+#include "BLO_undofile.h"
 
 #include "BKE_blender.h"
 #include "BKE_blender_undo.h"
@@ -111,6 +114,7 @@
 #include "ED_space_api.h"
 #include "ED_screen.h"
 #include "ED_util.h"
+#include "ED_undo.h"
 
 #include "UI_interface.h"
 #include "BLF_api.h"
@@ -132,6 +136,11 @@
 #  include "BKE_subsurf.h"
 #endif
 
+CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_OPERATORS, "wm.operator");
+CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_HANDLERS, "wm.handler");
+CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_EVENTS, "wm.event");
+CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_KEYMAPS, "wm.keymap");
+
 static void wm_init_reports(bContext *C)
 {
 	ReportList *reports = CTX_wm_reports(C);
@@ -147,11 +156,6 @@ static void wm_free_reports(bContext *C)
 	BKE_reports_clear(reports);
 }
 
-static void wm_undo_kill_callback(bContext *C)
-{
-	WM_jobs_kill_all_except(CTX_wm_manager(C), CTX_wm_screen(C));
-}
-
 bool wm_start_with_console = false; /* used in creator.c */
 
 /* only called once, for startup */
@@ -172,7 +176,7 @@ void WM_init(bContext *C, int argc, const char **argv)
 	wm_manipulatortype_init();
 	wm_manipulatorgrouptype_init();
 
-	BKE_undo_callback_wm_kill_jobs_set(wm_undo_kill_callback);
+	ED_undosys_type_init();
 
 	BKE_library_callback_free_window_manager_set(wm_close_and_free);   /* library.c */
 	BKE_library_callback_free_notifier_reference_set(WM_main_remove_notifier_reference);   /* library.c */
@@ -445,6 +449,29 @@ static void wait_for_console_key(void)
 }
 #endif
 
+static int wm_exit_handler(bContext *C, const wmEvent *event, void *userdata)
+{
+	WM_exit(C);
+
+	UNUSED_VARS(event, userdata);
+	return WM_UI_HANDLER_BREAK;
+}
+
+/**
+ * Cause a delayed WM_exit() call to avoid leaking memory when trying to exit from within operators.
+ */
+void wm_exit_schedule_delayed(const bContext *C)
+{
+	/* What we do here is a little bit hacky, but quite simple and doesn't require bigger
+	 * changes: Add a handler wrapping WM_exit() to cause a delayed call of it. */
+
+	wmWindow *win = CTX_wm_window(C);
+
+	/* Use modal UI handler for now. Could add separate WM handlers or so, but probably not worth it. */
+	WM_event_add_ui_handler(C, &win->modalhandlers, wm_exit_handler, NULL, NULL, 0);
+	WM_event_add_mousemove(C); /* ensure handler actually gets called */
+}
+
 /**
  * \note doesn't run exit() call #WM_exit() for that.
  */
@@ -459,7 +486,8 @@ void WM_exit_ext(bContext *C, const bool do_python)
 		wmWindow *win;
 
 		if (!G.background) {
-			if ((U.uiflag2 & USER_KEEP_SESSION) || BKE_undo_is_valid(NULL)) {
+			struct MemFile *undo_memfile = wm->undo_stack ? ED_undosys_stack_memfile_get_active(wm->undo_stack) : NULL;
+			if ((U.uiflag2 & USER_KEEP_SESSION) || (undo_memfile != NULL)) {
 				/* save the undo state as quit.blend */
 				char filename[FILE_MAX];
 				bool has_edited;
@@ -470,7 +498,7 @@ void WM_exit_ext(bContext *C, const bool do_python)
 				has_edited = ED_editors_flush_edits(C, false);
 
 				if ((has_edited && BLO_write_file(CTX_data_main(C), filename, fileflags, NULL, NULL)) ||
-				    BKE_undo_save_file(filename))
+				    (undo_memfile && BLO_memfile_write_file(undo_memfile, filename)))
 				{
 					printf("Saved session recovery to '%s'\n", filename);
 				}
@@ -493,11 +521,13 @@ void WM_exit_ext(bContext *C, const bool do_python)
 	wm_dropbox_free();
 	WM_menutype_free();
 	WM_uilisttype_free();
-	
+
 	/* all non-screen and non-space stuff editors did, like editmode */
 	if (C)
 		ED_editors_exit(C);
 
+	ED_undosys_type_free();
+
 //	XXX	
 //	BIF_GlobalReebFree();
 //	BIF_freeRetarget();
@@ -585,8 +615,6 @@ void WM_exit_ext(bContext *C, const bool do_python)
 	(void)do_python;
 #endif
 
-	BKE_undo_reset();
-	
 	ED_file_exit(); /* for fsmenu */
 
 	UI_exit();
@@ -611,6 +639,8 @@ void WM_exit_ext(bContext *C, const bool do_python)
 	 * see also T50676. */
 	BKE_sound_exit();
 
+	CLG_exit();
+
 	BKE_blender_atexit();
 
 	if (MEM_get_memory_blocks_in_use() != 0) {
@@ -625,6 +655,10 @@ void WM_exit_ext(bContext *C, const bool do_python)
 	BKE_tempdir_session_purge();
 }
 
+/**
+ * \brief Main exit function to close Blender ordinarily.
+ * \note Use #wm_exit_schedule_delayed() to close Blender from an operator. Might leak memory otherwise.
+ */
 void WM_exit(bContext *C)
 {
 	WM_exit_ext(C, 1);
diff --git a/source/blender/windowmanager/intern/wm_keymap.c b/source/blender/windowmanager/intern/wm_keymap.c
index e86e80dddf6285eca5ffc0fdc0a4f77d010279b2..0db67e0b199b01b6e79b6f5069694e01be4638d0 100644
--- a/source/blender/windowmanager/intern/wm_keymap.c
+++ b/source/blender/windowmanager/intern/wm_keymap.c
@@ -1,4 +1,5 @@
 /*
+ * 
  * ***** BEGIN GPL LICENSE BLOCK *****
  *
  * This program is free software; you can redistribute it and/or
@@ -40,6 +41,7 @@
 #include "DNA_workspace_types.h"
 
 #include "MEM_guardedalloc.h"
+#include "CLG_log.h"
 
 #include "BLI_blenlib.h"
 #include "BLI_utildefines.h"
@@ -902,11 +904,13 @@ wmKeyMapItem *WM_modalkeymap_find_propvalue(wmKeyMap *km, const int propvalue)
 void WM_modalkeymap_assign(wmKeyMap *km, const char *opname)
 {
 	wmOperatorType *ot = WM_operatortype_find(opname, 0);
-	
-	if (ot)
+
+	if (ot) {
 		ot->modalkeymap = km;
-	else
-		printf("error: modalkeymap_assign, unknown operator %s\n", opname);
+	}
+	else {
+		CLOG_ERROR(WM_LOG_KEYMAPS, "unknown operator '%s'", opname);
+	}
 }
 
 static void wm_user_modal_keymap_set_items(wmWindowManager *wm, wmKeyMap *km)
diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c
index 2ecbad81a94305fee3d652367190c4dc04feb871..e19ec6ca265b277e5403630d0df7e09ee8344f97 100644
--- a/source/blender/windowmanager/intern/wm_operators.c
+++ b/source/blender/windowmanager/intern/wm_operators.c
@@ -46,6 +46,8 @@
 
 #include "MEM_guardedalloc.h"
 
+#include "CLG_log.h"
+
 #include "DNA_ID.h"
 #include "DNA_object_types.h"
 #include "DNA_screen_types.h"
@@ -97,7 +99,7 @@
 
 #include "ED_numinput.h"
 #include "ED_screen.h"
-#include "ED_util.h"
+#include "ED_undo.h"
 #include "ED_view3d.h"
 
 #include "RNA_access.h"
@@ -142,12 +144,12 @@ wmOperatorType *WM_operatortype_find(const char *idname, bool quiet)
 		}
 
 		if (!quiet) {
-			printf("search for unknown operator '%s', '%s'\n", idname_bl, idname);
+			CLOG_INFO(WM_LOG_OPERATORS, 0, "search for unknown operator '%s', '%s'\n", idname_bl, idname);
 		}
 	}
 	else {
 		if (!quiet) {
-			printf("search for empty operator\n");
+			CLOG_INFO(WM_LOG_OPERATORS, 0, "search for empty operator");
 		}
 	}
 
@@ -180,8 +182,7 @@ static wmOperatorType *wm_operatortype_append__begin(void)
 static void wm_operatortype_append__end(wmOperatorType *ot)
 {
 	if (ot->name == NULL) {
-		fprintf(stderr, "ERROR: Operator %s has no name property!\n", ot->idname);
-		ot->name = N_("Dummy Name");
+		CLOG_ERROR(WM_LOG_OPERATORS, "Operator '%s' has no name property", ot->idname);
 	}
 
 	/* Allow calling _begin without _end in operatortype creation. */
@@ -269,7 +270,7 @@ static int wm_macro_exec(bContext *C, wmOperator *op)
 			}
 		}
 		else {
-			printf("%s: '%s' cant exec macro\n", __func__, opm->type->idname);
+			CLOG_WARN(WM_LOG_OPERATORS, "'%s' cant exec macro", opm->type->idname);
 		}
 	}
 	
@@ -314,8 +315,9 @@ static int wm_macro_modal(bContext *C, wmOperator *op, const wmEvent *event)
 	wmOperator *opm = op->opm;
 	int retval = OPERATOR_FINISHED;
 	
-	if (opm == NULL)
-		printf("%s: macro error, calling NULL modal()\n", __func__);
+	if (opm == NULL) {
+		CLOG_ERROR(WM_LOG_OPERATORS, "macro error, calling NULL modal()");
+	}
 	else {
 		retval = opm->type->modal(C, opm, event);
 		OPERATOR_RETVAL_CHECK(retval);
@@ -389,7 +391,7 @@ wmOperatorType *WM_operatortype_append_macro(const char *idname, const char *nam
 	const char *i18n_context;
 	
 	if (WM_operatortype_find(idname, true)) {
-		printf("%s: macro error: operator %s exists\n", __func__, idname);
+		CLOG_ERROR(WM_LOG_OPERATORS, "operator %s exists, cannot create macro", idname);
 		return NULL;
 	}
 	
@@ -1198,11 +1200,14 @@ int WM_menu_invoke_ex(bContext *C, wmOperator *op, int opcontext)
 	uiLayout *layout;
 
 	if (prop == NULL) {
-		printf("%s: %s has no enum property set\n", __func__, op->type->idname);
+		CLOG_ERROR(WM_LOG_OPERATORS,
+		           "'%s' has no enum property set",
+		           op->type->idname);
 	}
 	else if (RNA_property_type(prop) != PROP_ENUM) {
-		printf("%s: %s \"%s\" is not an enum property\n",
-		       __func__, op->type->idname, RNA_property_identifier(prop));
+		CLOG_ERROR(WM_LOG_OPERATORS,
+		           "'%s', '%s' is not an enum property",
+		           op->type->idname, RNA_property_identifier(prop));
 	}
 	else if (RNA_property_is_set(op->ptr, prop)) {
 		const int retval = op->type->exec(C, op);
@@ -1956,8 +1961,9 @@ static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(ar
 					memcpy(ibuf->rect, ibuf_template->rect, ibuf_template->x * ibuf_template->y * sizeof(char[4]));
 				}
 				else {
-					printf("Splash expected %dx%d found %dx%d, ignoring: %s\n",
-					       x_expect, y_expect, ibuf_template->x, ibuf_template->y, splash_filepath);
+					CLOG_ERROR(WM_LOG_OPERATORS,
+					           "Splash expected %dx%d found %dx%d, ignoring: %s\n",
+					           x_expect, y_expect, ibuf_template->x, ibuf_template->y, splash_filepath);
 				}
 				IMB_freeImBuf(ibuf_template);
 			}
@@ -2315,22 +2321,29 @@ static void WM_OT_window_fullscreen_toggle(wmOperatorType *ot)
 	ot->poll = WM_operator_winactive;
 }
 
-static int wm_exit_blender_exec(bContext *C, wmOperator *op)
+static int wm_exit_blender_exec(bContext *C, wmOperator *UNUSED(op))
 {
-	WM_operator_free(op);
-	
-	WM_exit(C);
-	
+	wm_quit_with_optional_confirmation_prompt(C, CTX_wm_window(C));
 	return OPERATOR_FINISHED;
 }
 
+static int wm_exit_blender_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+	if (U.uiflag & USER_QUIT_PROMPT) {
+		return wm_exit_blender_exec(C, op);
+	}
+	else {
+		return WM_operator_confirm(C, op, event);
+	}
+}
+
 static void WM_OT_quit_blender(wmOperatorType *ot)
 {
 	ot->name = "Quit Blender";
 	ot->idname = "WM_OT_quit_blender";
 	ot->description = "Quit Blender";
 
-	ot->invoke = WM_operator_confirm;
+	ot->invoke = wm_exit_blender_invoke;
 	ot->exec = wm_exit_blender_exec;
 }
 
@@ -2508,6 +2521,7 @@ static void radial_control_set_tex(RadialControl *rc)
 				glBindTexture(GL_TEXTURE_2D, rc->gltex);
 				glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, ibuf->x, ibuf->y, 0,
 				             GL_RED, GL_FLOAT, ibuf->rect_float);
+				glBindTexture(GL_TEXTURE_2D, 0);
 				MEM_freeN(ibuf->rect_float);
 				MEM_freeN(ibuf);
 			}
@@ -2548,6 +2562,7 @@ static void radial_control_paint_tex(RadialControl *rc, float radius, float alph
 
 		unsigned int texCoord = GWN_vertformat_attr_add(format, "texCoord", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
 
+		glActiveTexture(GL_TEXTURE0);
 		glBindTexture(GL_TEXTURE_2D, rc->gltex);
 
 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
@@ -2559,7 +2574,7 @@ static void radial_control_paint_tex(RadialControl *rc, float radius, float alph
 		immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_MASK_UNIFORM_COLOR);
 
 		immUniformColor3fvAlpha(col, alpha);
-		immUniform1i("image", GL_TEXTURE0);
+		immUniform1i("image", 0);
 
 		/* set up rotation if available */
 		if (rc->rot_prop) {
diff --git a/source/blender/windowmanager/intern/wm_stereo.c b/source/blender/windowmanager/intern/wm_stereo.c
index b70769b2d514b0fe6fdf7375cfc1e5c700b93cab..b52fc4ae213c9ec5d23c537dc82ecbf81bf166e4 100644
--- a/source/blender/windowmanager/intern/wm_stereo.c
+++ b/source/blender/windowmanager/intern/wm_stereo.c
@@ -204,6 +204,7 @@ static void wm_method_draw_stereo3d_sidebyside(wmWindow *win)
 	unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
 
 	immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_ALPHA);
+	glActiveTexture(GL_TEXTURE0);
 
 	for (view = 0; view < 2; view ++) {
 		drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1);
@@ -266,6 +267,7 @@ static void wm_method_draw_stereo3d_topbottom(wmWindow *win)
 	unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
 
 	immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_ALPHA);
+	glActiveTexture(GL_TEXTURE0);
 
 	for (view = 0; view < 2; view ++) {
 		drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1);
diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c
index 0412bb60d0183b07306016f637953710f59c11e2..f52880342846bc369bde87d9828e5d2b52e51330 100644
--- a/source/blender/windowmanager/intern/wm_window.c
+++ b/source/blender/windowmanager/intern/wm_window.c
@@ -82,6 +82,7 @@
 #include "GPU_batch.h"
 #include "GPU_draw.h"
 #include "GPU_extensions.h"
+#include "GPU_framebuffer.h"
 #include "GPU_init_exit.h"
 #include "GPU_immediate.h"
 #include "BLF_api.h"
@@ -334,11 +335,158 @@ wmWindow *wm_window_copy_test(bContext *C, wmWindow *win_src, const bool duplica
 	}
 }
 
+
+/* -------------------------------------------------------------------- */
+/** \name Quit Confirmation Dialog
+ * \{ */
+
+/** Cancel quitting and close the dialog */
+static void wm_block_confirm_quit_cancel(bContext *C, void *arg_block, void *UNUSED(arg))
+{
+	wmWindow *win = CTX_wm_window(C);
+	UI_popup_block_close(C, win, arg_block);
+}
+
+/** Discard the file changes and quit */
+static void wm_block_confirm_quit_discard(bContext *C, void *arg_block, void *UNUSED(arg))
+{
+	wmWindow *win = CTX_wm_window(C);
+	UI_popup_block_close(C, win, arg_block);
+	WM_exit(C);
+}
+
+/* Save changes and quit */
+static void wm_block_confirm_quit_save(bContext *C, void *arg_block, void *UNUSED(arg))
+{
+	PointerRNA props_ptr;
+	wmWindow *win = CTX_wm_window(C);
+
+	UI_popup_block_close(C, win, arg_block);
+
+	wmOperatorType *ot = WM_operatortype_find("WM_OT_save_mainfile", false);
+
+	WM_operator_properties_create_ptr(&props_ptr, ot);
+	RNA_boolean_set(&props_ptr, "exit", true);
+	/* No need for second confirmation popup. */
+	RNA_boolean_set(&props_ptr, "check_existing", false);
+	WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr);
+	WM_operator_properties_free(&props_ptr);
+}
+
+
+/* Build the confirm dialog UI */
+static uiBlock *block_create_confirm_quit(struct bContext *C, struct ARegion *ar, void *UNUSED(arg1))
+{
+
+	uiStyle *style = UI_style_get();
+	uiBlock *block = UI_block_begin(C, ar, "confirm_quit_popup", UI_EMBOSS);
+
+	UI_block_flag_enable(block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_LOOP | UI_BLOCK_NO_WIN_CLIP | UI_BLOCK_NUMSELECT);
+	UI_block_emboss_set(block, UI_EMBOSS);
+
+	uiLayout *layout = UI_block_layout(
+	        block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 10, 2, U.widget_unit * 24, U.widget_unit * 6, 0, style);
+
+	/* Text and some vertical space */
+	{
+		char *message;
+		if (G.main->name[0] == '\0') {
+			message = BLI_strdup(IFACE_("This file has not been saved yet. Save before closing?"));
+		}
+		else {
+			const char *basename = BLI_path_basename(G.main->name);
+			message = BLI_sprintfN(IFACE_("Save changes to \"%s\" before closing?"), basename);
+		}
+		uiItemL(layout, message, ICON_ERROR);
+		MEM_freeN(message);
+	}
+
+	uiItemS(layout);
+	uiItemS(layout);
+
+
+	/* Buttons */
+	uiBut *but;
+
+	uiLayout *split = uiLayoutSplit(layout, 0.0f, true);
+
+	uiLayout *col = uiLayoutColumn(split, false);
+
+	but = uiDefIconTextBut(
+	        block, UI_BTYPE_BUT, 0, ICON_SCREEN_BACK, IFACE_("Cancel"), 0, 0, 0, UI_UNIT_Y,
+	        NULL, 0, 0, 0, 0, TIP_("Do not quit"));
+	UI_but_func_set(but, wm_block_confirm_quit_cancel, block, NULL);
+
+	/* empty space between buttons */
+	col = uiLayoutColumn(split, false);
+	uiItemS(col);
+
+	col = uiLayoutColumn(split, 1);
+	but = uiDefIconTextBut(
+	        block, UI_BTYPE_BUT, 0, ICON_CANCEL, IFACE_("Discard Changes"), 0, 0, 50, UI_UNIT_Y,
+	        NULL, 0, 0, 0, 0, TIP_("Discard changes and quit"));
+	UI_but_func_set(but, wm_block_confirm_quit_discard, block, NULL);
+
+	col = uiLayoutColumn(split, 1);
+	but = uiDefIconTextBut(
+	        block, UI_BTYPE_BUT, 0, ICON_FILE_TICK, IFACE_("Save & Quit"), 0, 0, 50, UI_UNIT_Y,
+	        NULL, 0, 0, 0, 0, TIP_("Save and quit"));
+	UI_but_func_set(but, wm_block_confirm_quit_save, block, NULL);
+
+	UI_block_bounds_set_centered(block, 10);
+
+	return block;
+}
+
+
+/**
+ * Call the confirm dialog on quitting. It's displayed in the context window so
+ * caller should set it as desired.
+ */
+static void wm_confirm_quit(bContext *C)
+{
+	wmWindow *win = CTX_wm_window(C);
+
+	if (GHOST_SupportsNativeDialogs() == 0) {
+		UI_popup_block_invoke(C, block_create_confirm_quit, NULL);
+	}
+	else if (GHOST_confirmQuit(win->ghostwin)) {
+		wm_exit_schedule_delayed(C);
+	}
+}
+
+/**
+ * Call the quit confirmation prompt or exit directly if needed. The use can
+ * still cancel via the confirmation popup. Also, this may not quit Blender
+ * immediately, but rather schedule the closing.
+ *
+ * \param win The window to show the confirmation popup/window in.
+ */
+void wm_quit_with_optional_confirmation_prompt(bContext *C, wmWindow *win)
+{
+	wmWindowManager *wm = CTX_wm_manager(C);
+	wmWindow *win_ctx = CTX_wm_window(C);
+
+	/* The popup will be displayed in the context window which may not be set
+	 * here (this function gets called outside of normal event handling loop). */
+	CTX_wm_window_set(C, win);
+
+	if ((U.uiflag & USER_QUIT_PROMPT) && !wm->file_saved && !G.background) {
+		wm_confirm_quit(C);
+	}
+	else {
+		wm_exit_schedule_delayed(C);
+	}
+
+	CTX_wm_window_set(C, win_ctx);
+}
+
+/** \} */
+
 /* this is event from ghost, or exit-blender op */
 void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win)
 {
 	wmWindow *tmpwin;
-	bool do_exit = false;
 	
 	/* first check if we have to quit (there are non-temp remaining windows) */
 	for (tmpwin = wm->windows.first; tmpwin; tmpwin = tmpwin->next) {
@@ -348,19 +496,8 @@ void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win)
 			break;
 	}
 
-	if (tmpwin == NULL)
-		do_exit = 1;
-	
-	if ((U.uiflag & USER_QUIT_PROMPT) && !wm->file_saved && !G.background) {
-		if (do_exit) {
-			if (!GHOST_confirmQuit(win->ghostwin))
-				return;
-		}
-	}
-
-	/* let WM_exit do all freeing, for correct quit.blend save */
-	if (do_exit) {
-		WM_exit(C);
+	if (tmpwin == NULL) {
+		wm_quit_with_optional_confirmation_prompt(C, win);
 	}
 	else {
 		bScreen *screen = WM_window_get_active_screen(win);
@@ -382,6 +519,7 @@ void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win)
 		}
 
 		if (tmpwin) {
+			BLF_batch_reset();
 			gpu_batch_presets_reset();
 			immDeactivate();
 		}
@@ -877,7 +1015,7 @@ int wm_window_new_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(even
 	WorkSpace *workspace = WM_window_get_active_workspace(win);
 	ListBase *listbase = BKE_workspace_layouts_get(workspace);
 
-	if (BLI_listbase_count_ex(listbase, 2) == 1) {
+	if (BLI_listbase_count_at_most(listbase, 2) == 1) {
 		RNA_enum_set(op->ptr, "screen", 0);
 		return wm_window_new_exec(C, op);
 	}
@@ -1021,6 +1159,8 @@ static int query_qual(modifierKeyType qual)
 
 void wm_window_make_drawable(wmWindowManager *wm, wmWindow *win) 
 {
+	BLI_assert(GPU_framebuffer_current_get() == 0);
+
 	if (win != wm->windrawable && win->ghostwin) {
 //		win->lmbut = 0;	/* keeps hanging when mousepressed while other window opened */
 
@@ -1029,6 +1169,7 @@ void wm_window_make_drawable(wmWindowManager *wm, wmWindow *win)
 			printf("%s: set drawable %d\n", __func__, win->winid);
 		}
 
+		BLF_batch_reset();
 		gpu_batch_presets_reset();
 		immDeactivate();
 		GHOST_ActivateWindowDrawingContext(win->ghostwin);
@@ -1044,6 +1185,7 @@ void wm_window_make_drawable(wmWindowManager *wm, wmWindow *win)
 void wm_window_reset_drawable(void)
 {
 	BLI_assert(BLI_thread_is_main());
+	BLI_assert(GPU_framebuffer_current_get() == 0);
 	wmWindowManager *wm = G.main->wm.first;
 
 	if (wm == NULL)
@@ -1052,6 +1194,7 @@ void wm_window_reset_drawable(void)
 	wmWindow *win = wm->windrawable;
 
 	if (win && win->ghostwin) {
+		BLF_batch_reset();
 		gpu_batch_presets_reset();
 		immDeactivate();
 		GHOST_ActivateWindowDrawingContext(win->ghostwin);
@@ -1980,18 +2123,6 @@ WorkSpace *WM_windows_workspace_get_from_screen(const wmWindowManager *wm, const
 	return NULL;
 }
 
-eObjectMode WM_windows_object_mode_get(const struct wmWindowManager *wm)
-{
-	eObjectMode object_mode = 0;
-	for (wmWindow *win = wm->windows.first; win; win = win->next) {
-		const WorkSpace *workspace = BKE_workspace_active_get(win->workspace_hook);
-		if (workspace != NULL) {
-			object_mode |= workspace->object_mode;
-		}
-	}
-	return object_mode;
-}
-
 Scene *WM_window_get_active_scene(const wmWindow *win)
 {
 	return win->scene;
@@ -2041,6 +2172,26 @@ void WM_window_set_active_screen(wmWindow *win, WorkSpace *workspace, bScreen *s
 	BKE_workspace_active_screen_set(win->workspace_hook, workspace, screen);
 }
 
+struct ViewLayer *WM_window_get_active_view_layer_ex(const wmWindow *win, Scene **r_scene)
+{
+	const WorkSpace *workspace = WM_window_get_active_workspace(win);
+	Scene *scene = WM_window_get_active_scene(win);
+	/* May be NULL in rare cases like closing Blender */
+	bScreen *screen = (LIKELY(workspace != NULL) ? BKE_workspace_active_screen_get(win->workspace_hook) : NULL);
+	if (screen != NULL) {
+		if (r_scene) {
+			*r_scene = scene;
+		}
+		return BKE_workspace_view_layer_get(workspace, scene);
+	}
+	return NULL;
+}
+
+struct ViewLayer *WM_window_get_active_view_layer(const wmWindow *win)
+{
+	return WM_window_get_active_view_layer_ex(win, NULL);
+}
+
 bool WM_window_is_temp_screen(const wmWindow *win)
 {
 	const bScreen *screen = WM_window_get_active_screen(win);
@@ -2075,20 +2226,24 @@ void *WM_opengl_context_create(void)
 	 * So we should call this function only on the main thread.
 	 */
 	BLI_assert(BLI_thread_is_main());
+	BLI_assert(GPU_framebuffer_current_get() == 0);
 	return GHOST_CreateOpenGLContext(g_system);
 }
 
 void WM_opengl_context_dispose(void *context)
 {
+	BLI_assert(GPU_framebuffer_current_get() == 0);
 	GHOST_DisposeOpenGLContext(g_system, (GHOST_ContextHandle)context);
 }
 
 void WM_opengl_context_activate(void *context)
 {
+	BLI_assert(GPU_framebuffer_current_get() == 0);
 	GHOST_ActivateOpenGLContext((GHOST_ContextHandle)context);
 }
 
 void WM_opengl_context_release(void *context)
 {
+	BLI_assert(GPU_framebuffer_current_get() == 0);
 	GHOST_ReleaseOpenGLContext((GHOST_ContextHandle)context);
 }
diff --git a/source/blender/windowmanager/wm.h b/source/blender/windowmanager/wm.h
index 7fe6db4e4706a30378444456786542f83f575f40..b2e8c914fa8f276d75575378c5da3a2ddd94fc9d 100644
--- a/source/blender/windowmanager/wm.h
+++ b/source/blender/windowmanager/wm.h
@@ -45,6 +45,9 @@ typedef struct wmPaintCursor {
 	void (*draw)(bContext *C, int, int, void *customdata);
 } wmPaintCursor;
 
+
+void wm_exit_schedule_delayed(const bContext *C);
+
 extern void wm_close_and_free(bContext *C, wmWindowManager *);
 extern void wm_close_and_free_all(bContext *C, ListBase *);
 
diff --git a/source/blender/windowmanager/wm_window.h b/source/blender/windowmanager/wm_window.h
index 652cefb1a54ab7db96837d550e1a0403130413bb..b0c7021c4960459af40ef5c9c20d4be6c2155414 100644
--- a/source/blender/windowmanager/wm_window.h
+++ b/source/blender/windowmanager/wm_window.h
@@ -82,6 +82,7 @@ void		wm_window_IME_end	(wmWindow *win);
 /* *************** window operators ************** */
 int			wm_window_close_exec(bContext *C, struct wmOperator *op);
 int			wm_window_fullscreen_toggle_exec(bContext *C, struct wmOperator *op);
+void		wm_quit_with_optional_confirmation_prompt(bContext *C, wmWindow *win) ATTR_NONNULL();
 
 const struct EnumPropertyItem *wm_window_new_screen_itemf(
         bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, bool *r_free);
diff --git a/source/blenderplayer/bad_level_call_stubs/stubs.c b/source/blenderplayer/bad_level_call_stubs/stubs.c
index 03fd58bfbcd55e17ff228b68e8802a66ace88f88..865a133ed5df0881790c1595787ebaa0063c560b 100644
--- a/source/blenderplayer/bad_level_call_stubs/stubs.c
+++ b/source/blenderplayer/bad_level_call_stubs/stubs.c
@@ -337,6 +337,10 @@ struct WorkSpace *WM_windows_workspace_get_from_screen(const struct wmWindowMana
 struct bScreen *WM_window_get_active_screen(const struct wmWindow *win) RET_NULL
 struct Scene *WM_window_get_active_scene(const struct wmWindow *win) RET_NULL
 struct WorkSpace *WM_window_get_active_workspace(const wmWindow *win) RET_NULL
+
+struct ViewLayer *WM_window_get_active_view_layer_ex(const struct wmWindow *win, struct Scene **r_scene) RET_NULL
+struct ViewLayer *WM_window_get_active_view_layer(const struct wmWindow *win) RET_NULL
+	
 void WM_window_change_active_scene(struct Main *bmain, struct bContext *C, struct wmWindow *win, struct Scene *scene_new) RET_NONE
 bool WM_window_is_temp_screen(const struct wmWindow *win) RET_ZERO
 
@@ -449,7 +453,7 @@ void UI_view2d_sync(struct bScreen *screen, struct ScrArea *sa, struct View2D *v
 
 struct EditBone *ED_armature_bone_get_mirrored(const struct ListBase *edbo, EditBone *ebo) RET_NULL
 struct EditBone *ED_armature_edit_bone_add(struct bArmature *arm, const char *name) RET_NULL
-struct ListBase *get_active_constraints (const struct EvaluationContext *eval_ctx, struct Object *ob) RET_NULL
+struct ListBase *get_active_constraints (struct Object *ob) RET_NULL
 struct ListBase *get_constraint_lb(struct Object *ob, struct bConstraint *con, struct bPoseChannel **r_pchan) RET_NULL
 
 bool ED_space_image_show_uvedit(struct SpaceImage *sima, struct Object *obedit) RET_ZERO
@@ -482,7 +486,7 @@ void ED_fsmenu_entry_set_path(struct FSMenuEntry *fsentry, const char *name) RET
 char *ED_fsmenu_entry_get_name(struct FSMenuEntry *fsentry) RET_NULL
 void ED_fsmenu_entry_set_name(struct FSMenuEntry *fsentry, const char *name) RET_NONE
 
-struct PTCacheEdit *PE_get_current(struct Scene *scene, struct ViewLayer *view_layer, struct Object *ob) RET_NULL
+struct PTCacheEdit *PE_get_current(struct Scene *scene, struct Object *ob) RET_NULL
 void PE_current_changed(const struct EvaluationContext *eval_ctx, struct Scene *scene, struct Object *ob) RET_NONE
 
 /* rna keymap */
@@ -522,7 +526,7 @@ bool ANIM_remove_driver(struct ReportList *reports, struct ID *id, const char rn
 void ED_space_image_release_buffer(struct SpaceImage *sima, struct ImBuf *ibuf, void *lock) RET_NONE
 struct ImBuf *ED_space_image_acquire_buffer(struct SpaceImage *sima, void **r_lock) RET_NULL
 void ED_space_image_get_zoom(struct SpaceImage *sima, struct ARegion *ar, float *zoomx, float *zoomy) RET_NONE
-const char *ED_info_stats_string(struct Scene *scene, struct WorkSpace *workspace, struct ViewLayer *view_layer) RET_NULL
+const char *ED_info_stats_string(struct Scene *scene, struct ViewLayer *view_layer) RET_NULL
 void ED_area_tag_redraw(struct ScrArea *sa) RET_NONE
 void ED_area_tag_refresh(struct ScrArea *sa) RET_NONE
 void ED_area_newspace(struct bContext *C, struct ScrArea *sa, int type, const bool skip_ar_exit) RET_NONE
@@ -560,13 +564,13 @@ bool ED_scene_view_layer_delete(struct Main *bmain, Scene *scene, ViewLayer *lay
 void ED_object_base_select(struct Base *base, eObjectSelect_Mode mode) RET_NONE
 void ED_object_base_activate(struct bContext *C, struct Base *base) RET_NONE
 bool ED_object_modifier_remove(struct ReportList *reports, struct Main *bmain, struct Object *ob, struct ModifierData *md) RET_ZERO
-struct ModifierData *ED_object_modifier_add(struct ReportList *reports, struct Main *bmain, struct Scene *scene, struct Object *ob, eObjectMode object_mode, const char *name, int type) RET_ZERO
+struct ModifierData *ED_object_modifier_add(struct ReportList *reports, struct Main *bmain, struct Scene *scene, struct Object *ob, const char *name, int type) RET_ZERO
 void ED_object_modifier_clear(struct Main *bmain, struct Object *ob) RET_NONE
 void ED_object_editmode_enter(struct bContext *C, int flag) RET_NONE
 void ED_object_editmode_exit(struct bContext *C, int flag) RET_NONE
-void ED_object_editmode_exit_ex(struct bContext *C, struct WorkSpace *workspace, struct Scene *scene, struct Object *obedit, int flag) RET_NONE
+void ED_object_editmode_exit_ex(struct bContext *C, struct Scene *scene, struct Object *obedit, int flag) RET_NONE
 bool ED_object_editmode_load(struct Object *obedit) RET_ZERO
-void ED_object_check_force_modifiers(struct Main *bmain, struct Scene *scene, struct Object *object, eObjectMode object_mode) RET_NONE
+void ED_object_check_force_modifiers(struct Main *bmain, struct Scene *scene, struct Object *object) RET_NONE
 bool uiLayoutGetActive(struct uiLayout *layout) RET_ZERO
 int uiLayoutGetOperatorContext(struct uiLayout *layout) RET_ZERO
 int uiLayoutGetAlignment(struct uiLayout *layout) RET_ZERO
@@ -606,7 +610,7 @@ int ED_mesh_mirror_spatial_table(struct Object *ob, struct BMEditMesh *em, struc
 
 float ED_rollBoneToVector(EditBone *bone, const float new_up_axis[3], const bool axis_only) RET_ZERO
 void ED_space_image_get_size(struct SpaceImage *sima, int *width, int *height) RET_NONE
-bool ED_space_image_check_show_maskedit(struct SpaceImage *sima, const struct WorkSpace *workspace, struct ViewLayer *view_layer) RET_ZERO
+bool ED_space_image_check_show_maskedit(struct SpaceImage *sima, struct ViewLayer *view_layer) RET_ZERO
 
 bool ED_texture_context_check_world(const struct bContext *C) RET_ZERO
 bool ED_texture_context_check_material(const struct bContext *C) RET_ZERO
@@ -676,6 +680,7 @@ void UI_GetThemeColorBlendShade3fv(int colorid1, int colorid2, float fac, int of
 void UI_GetThemeColorBlendShade4fv(int colorid1, int colorid2, float fac, int offset, float col[4]) RET_NONE
 void UI_GetThemeColorBlend3ubv(int colorid1, int colorid2, float fac, unsigned char col[3]) RET_NONE
 void UI_GetThemeColorShadeAlpha4ubv(int colorid, int coloffset, int alphaoffset, unsigned char col[4]) RET_NONE
+void UI_widgetbase_draw_cache_flush(void) RET_NONE
 
 /* rna template */
 void uiTemplateAnyID(uiLayout *layout, struct PointerRNA *ptr, const char *propname, const char *proptypename, const char *text) RET_NONE
diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt
index 177e97445105c5b80194a909cf2f36905e8bff1d..1adef28f2c8774a88d52ef6b9b298ad351ffdca6 100644
--- a/source/creator/CMakeLists.txt
+++ b/source/creator/CMakeLists.txt
@@ -26,6 +26,7 @@
 setup_libdirs()
 
 blender_include_dirs(
+	../../intern/clog
 	../../intern/guardedalloc
 	../../intern/glew-mx
 	../blender/blenlib
diff --git a/source/creator/creator.c b/source/creator/creator.c
index 78bab14cd9696fbd0865495bf32215aa14b7e40b..2aa60c3e2a7f3724a105476a27a2e50b7e8f67e5 100644
--- a/source/creator/creator.c
+++ b/source/creator/creator.c
@@ -42,6 +42,8 @@
 
 #include "MEM_guardedalloc.h"
 
+#include "CLG_log.h"
+
 #include "DNA_genfile.h"
 
 #include "BLI_args.h"
@@ -49,6 +51,7 @@
 #include "BLI_utildefines.h"
 #include "BLI_callbacks.h"
 #include "BLI_string.h"
+#include "BLI_system.h"
 
 /* mostly init functions */
 #include "BKE_appdir.h"
@@ -180,6 +183,11 @@ static void callback_main_atexit(void *user_data)
 #endif
 }
 
+static void callback_clg_fatal(void *fp)
+{
+	BLI_system_backtrace(fp);
+}
+
 /** \} */
 
 
@@ -304,6 +312,10 @@ int main(
 	sdlewInit();
 #endif
 
+	/* Initialize logging */
+	CLG_init();
+	CLG_fatal_fn_set(callback_clg_fatal);
+
 	C = CTX_create();
 
 #ifdef WITH_PYTHON_MODULE
diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c
index df4946a817576dc5164ffcdeaa1e13d74a24ffe0..dac223223614eebc0825faa85de494c39dba2615 100644
--- a/source/creator/creator_args.c
+++ b/source/creator/creator_args.c
@@ -30,6 +30,8 @@
 
 #include "MEM_guardedalloc.h"
 
+#include "CLG_log.h"
+
 #ifdef WIN32
 #  include "BLI_winstuff.h"
 #endif
@@ -528,6 +530,12 @@ static int arg_handle_print_help(int UNUSED(argc), const char **UNUSED(argv), vo
 	BLI_argsPrintArgDoc(ba, "--python-exit-code");
 	BLI_argsPrintArgDoc(ba, "--addons");
 
+	printf("\n");
+	printf("Logging Options:\n");
+	BLI_argsPrintArgDoc(ba, "--log");
+	BLI_argsPrintArgDoc(ba, "--log-level");
+	BLI_argsPrintArgDoc(ba, "--log-show-basename");
+	BLI_argsPrintArgDoc(ba, "--log-file");
 
 	printf("\n");
 	printf("Debug Options:\n");
@@ -702,6 +710,112 @@ static int arg_handle_background_mode_set(int UNUSED(argc), const char **UNUSED(
 	return 0;
 }
 
+static const char arg_handle_log_level_set_doc[] =
+"<level>\n"
+"\n"
+"\tSet the logging verbosity level (higher for more details) defaults to 1, use -1 to log all levels."
+;
+static int arg_handle_log_level_set(int argc, const char **argv, void *UNUSED(data))
+{
+	const char *arg_id = "--log-level";
+	if (argc > 1) {
+		const char *err_msg = NULL;
+		if (!parse_int_clamp(argv[1], NULL, -1, INT_MAX, &G.log.level, &err_msg)) {
+			printf("\nError: %s '%s %s'.\n", err_msg, arg_id, argv[1]);
+		}
+		if (G.log.level == -1) {
+			G.log.level = INT_MAX;
+		}
+		return 1;
+	}
+	else {
+		printf("\nError: '%s' no args given.\n", arg_id);
+		return 0;
+	}
+}
+
+static const char arg_handle_log_show_basename_set_doc[] =
+"\n\tOnly show file name in output (not the leading path)."
+;
+static int arg_handle_log_show_basename_set(int UNUSED(argc), const char **UNUSED(argv), void *UNUSED(data))
+{
+	CLG_output_use_basename_set(true);
+	return 0;
+}
+
+static const char arg_handle_log_file_set_doc[] =
+"<filename>\n"
+"\n"
+"\tSet a file to output the log to."
+;
+static int arg_handle_log_file_set(int argc, const char **argv, void *UNUSED(data))
+{
+	const char *arg_id = "--log-file";
+	if (argc > 1) {
+		errno = 0;
+		FILE *fp = BLI_fopen(argv[1], "w");
+		if (fp == NULL) {
+			const char *err_msg = errno ? strerror(errno) : "unknown";
+			printf("\nError: %s '%s %s'.\n", err_msg, arg_id, argv[1]);
+		}
+		else {
+			if (UNLIKELY(G.log.file != NULL)) {
+				fclose(G.log.file);
+			}
+			G.log.file = fp;
+			CLG_output_set(G.log.file);
+		}
+		return 1;
+	}
+	else {
+		printf("\nError: '%s' no args given.\n", arg_id);
+		return 0;
+	}
+}
+
+static const char arg_handle_log_set_doc[] =
+"<match>\n"
+"\tEnable logging categories, taking a single comma separated argument.\n"
+"\tMultiple categories can be matched using a '.*' suffix,\n"
+"\tso '--log \"wm.*\"' logs every kind of window-manager message.\n"
+"\tUse \"^\" prefix to ignore, so '--log \"*,^wm.operator.*\"' logs all except for 'wm.operators.*'\n"
+"\tUse \"*\" to log everything."
+;
+static int arg_handle_log_set(int argc, const char **argv, void *UNUSED(data))
+{
+	const char *arg_id = "--log";
+	if (argc > 1) {
+		const char *str_step = argv[1];
+		while (*str_step) {
+			const char *str_step_end = strchr(str_step, ',');
+			int str_step_len = str_step_end ? (str_step_end - str_step) : strlen(str_step);
+
+			if (str_step[0] == '^') {
+				CLG_type_filter_exclude(str_step + 1, str_step_len - 1);
+			}
+			else {
+				CLG_type_filter_include(str_step, str_step_len);
+			}
+
+			if (str_step_end) {
+				/* typically only be one, but don't fail on multiple.*/
+				while (*str_step_end == ',') {
+					str_step_end++;
+				}
+				str_step = str_step_end;
+			}
+			else {
+				break;
+			}
+		}
+		return 1;
+	}
+	else {
+		printf("\nError: '%s' no args given.\n", arg_id);
+		return 0;
+	}
+}
+
 static const char arg_handle_debug_mode_set_doc[] =
 "\n"
 "\tTurn debugging on.\n"
@@ -1657,7 +1771,7 @@ static int arg_handle_python_console_run(int UNUSED(argc), const char **argv, vo
 }
 
 static const char arg_handle_python_exit_code_set_doc[] =
-"\n"
+"<code>\n"
 "\tSet the exit-code in [0..255] to exit if a Python exception is raised\n"
 "\t(only for scripts executed from the command line), zero disables."
 ;
@@ -1683,7 +1797,8 @@ static int arg_handle_python_exit_code_set(int argc, const char **argv, void *UN
 }
 
 static const char arg_handle_addons_set_doc[] =
-"\n\tComma separated list of add-ons (no spaces)."
+"<addon(s)>\n"
+"\tComma separated list of add-ons (no spaces)."
 ;
 static int arg_handle_addons_set(int argc, const char **argv, void *data)
 {
@@ -1803,6 +1918,11 @@ void main_args_setup(bContext *C, bArgs *ba, SYS_SystemHandle *syshandle)
 
 	BLI_argsAdd(ba, 1, "-a", NULL, CB(arg_handle_playback_mode), NULL);
 
+	BLI_argsAdd(ba, 1, NULL, "--log", CB(arg_handle_log_set), ba);
+	BLI_argsAdd(ba, 1, NULL, "--log-level", CB(arg_handle_log_level_set), ba);
+	BLI_argsAdd(ba, 1, NULL, "--log-show-basename", CB(arg_handle_log_show_basename_set), ba);
+	BLI_argsAdd(ba, 1, NULL, "--log-file", CB(arg_handle_log_file_set), ba);
+
 	BLI_argsAdd(ba, 1, "-d", "--debug", CB(arg_handle_debug_mode_set), ba);
 
 #ifdef WITH_FFMPEG
diff --git a/source/creator/creator_signals.c b/source/creator/creator_signals.c
index 81e6178c5025fff0daac0afd93503a8c2393b1ca..feb108da289c43c586562acf8ff56c07cddd3042 100644
--- a/source/creator/creator_signals.c
+++ b/source/creator/creator_signals.c
@@ -64,6 +64,7 @@
 #include "BKE_main.h"
 #include "BKE_report.h"
 
+
 /* for passing information between creator and gameengine */
 #ifdef WITH_GAMEENGINE
 #  include "BL_System.h"
@@ -75,6 +76,12 @@
 
 #include "creator_intern.h"  /* own include */
 
+// #define USE_WRITE_CRASH_BLEND
+#ifdef USE_WRITE_CRASH_BLEND
+#  include "BKE_undo_system.h"
+#  include "BLO_undofile.h"
+#endif
+
 /* set breakpoints here when running in debug mode, useful to catch floating point errors */
 #if defined(__linux__) || defined(_WIN32) || defined(OSX_SSE_FPE)
 static void sig_handle_fpe(int UNUSED(sig))
@@ -110,29 +117,32 @@ static void sig_handle_crash_backtrace(FILE *fp)
 
 static void sig_handle_crash(int signum)
 {
+	wmWindowManager *wm = G.main->wm.first;
 
-#if 0
-	{
-		char fname[FILE_MAX];
+#ifdef USE_WRITE_CRASH_BLEND
+	if (wm->undo_stack) {
+		struct MemFile *memfile = BKE_undosys_stack_memfile_get_active(wm->undo_stack);
+		if (memfile) {
+			char fname[FILE_MAX];
 
-		if (!G.main->name[0]) {
-			BLI_make_file_string("/", fname, BKE_tempdir_base(), "crash.blend");
-		}
-		else {
-			BLI_strncpy(fname, G.main->name, sizeof(fname));
-			BLI_replace_extension(fname, sizeof(fname), ".crash.blend");
-		}
+			if (!G.main->name[0]) {
+				BLI_make_file_string("/", fname, BKE_tempdir_base(), "crash.blend");
+			}
+			else {
+				BLI_strncpy(fname, G.main->name, sizeof(fname));
+				BLI_replace_extension(fname, sizeof(fname), ".crash.blend");
+			}
 
-		printf("Writing: %s\n", fname);
-		fflush(stdout);
+			printf("Writing: %s\n", fname);
+			fflush(stdout);
 
-		BKE_undo_save_file(fname);
+			BLO_memfile_write_file(memfile, fname);
+		}
 	}
 #endif
 
 	FILE *fp;
 	char header[512];
-	wmWindowManager *wm = G.main->wm.first;
 
 	char fname[FILE_MAX];
 
@@ -338,4 +348,4 @@ void main_signal_setup_fpe(void)
 #endif
 }
 
-#endif  /* WITH_PYTHON_MODULE */
\ No newline at end of file
+#endif  /* WITH_PYTHON_MODULE */
diff --git a/source/gameengine/Converter/BL_BlenderDataConversion.cpp b/source/gameengine/Converter/BL_BlenderDataConversion.cpp
index c390bff09a47b07b96a4db706580e6bb8057755d..39f517e03e899948ff8eaebb642fa59119a63007 100644
--- a/source/gameengine/Converter/BL_BlenderDataConversion.cpp
+++ b/source/gameengine/Converter/BL_BlenderDataConversion.cpp
@@ -1541,7 +1541,7 @@ static KX_GameObject *gameobject_from_blenderobject(
 		gameobj->AddMesh(meshobj);
 
 		// gather levels of detail
-		if (BLI_listbase_count_ex(&ob->lodlevels, 2) > 1) {
+		if (BLI_listbase_count_at_most(&ob->lodlevels, 2) > 1) {
 			LodLevel *lod = ((LodLevel*)ob->lodlevels.first)->next;
 			Mesh* lodmesh = mesh;
 			Object* lodmatob = ob;
diff --git a/source/gameengine/Ketsji/KX_FontObject.cpp b/source/gameengine/Ketsji/KX_FontObject.cpp
index 91e8e4fd42bff5380533902f5c720f89fd12c93b..ae79284288d7f4aa09dd6c2d29f74c6cd1265b17 100644
--- a/source/gameengine/Ketsji/KX_FontObject.cpp
+++ b/source/gameengine/Ketsji/KX_FontObject.cpp
@@ -142,7 +142,7 @@ int GetFontId(VFont *vfont)
 		fontid = BLF_load("default");
 		
 		/* XXX the following code is supposed to work (after you add get_builtin_packedfile to BKE_font.h )
-		 * unfortunately it's crashing on blf_glyph.c:173 because gc->max_glyph_width is 0
+		 * unfortunately it's crashing on blf_glyph.c:173 because gc->glyph_width_max is 0
 		 */
 		// packedfile=get_builtin_packedfile();
 		// fontid= BLF_load_mem(font->name, (unsigned char*)packedfile->data, packedfile->size);