Browse Source

Merge pull request #680 from urho3d/render-refactor

Render refactor
Lasse Öörni 10 years ago
parent
commit
d06ffe1445
100 changed files with 11006 additions and 1818 deletions
  1. 31 10
      CMake/Modules/FindDirect3D.cmake
  2. 14 0
      CMake/Modules/Urho3D-CMake-common.cmake
  3. 11 8
      Docs/GettingStarted.dox
  4. 67 35
      Docs/Reference.dox
  5. 1 1
      Source/CMakeLists.txt
  6. 8 0
      Source/ThirdParty/GLEW/glew.c
  7. 15 6
      Source/Tools/AssetImporter/AssetImporter.cpp
  8. 13 3
      Source/Tools/OgreImporter/OgreImporter.cpp
  9. 18 7
      Source/Tools/RampGenerator/RampGenerator.cpp
  10. 2 2
      Source/Tools/Urho3DPlayer/Urho3DPlayer.cpp
  11. 6 1
      Source/Urho3D/CMakeLists.txt
  12. 0 191
      Source/Urho3D/Container/HashTable.h
  13. 2 6
      Source/Urho3D/Engine/DebugHud.cpp
  14. 7 3
      Source/Urho3D/Engine/Engine.cpp
  15. 3 3
      Source/Urho3D/Graphics/AnimatedModel.cpp
  16. 206 244
      Source/Urho3D/Graphics/Batch.cpp
  17. 2 0
      Source/Urho3D/Graphics/Batch.h
  18. 29 0
      Source/Urho3D/Graphics/ConstantBuffer.h
  19. 0 1
      Source/Urho3D/Graphics/DebugRenderer.cpp
  20. 1 1
      Source/Urho3D/Graphics/DecalSet.cpp
  21. 138 0
      Source/Urho3D/Graphics/Direct3D11/D3D11ConstantBuffer.cpp
  22. 73 0
      Source/Urho3D/Graphics/Direct3D11/D3D11ConstantBuffer.h
  23. 51 0
      Source/Urho3D/Graphics/Direct3D11/D3D11GPUObject.cpp
  24. 63 0
      Source/Urho3D/Graphics/Direct3D11/D3D11GPUObject.h
  25. 2729 0
      Source/Urho3D/Graphics/Direct3D11/D3D11Graphics.cpp
  26. 638 0
      Source/Urho3D/Graphics/Direct3D11/D3D11Graphics.h
  27. 65 0
      Source/Urho3D/Graphics/Direct3D11/D3D11GraphicsImpl.cpp
  28. 92 0
      Source/Urho3D/Graphics/Direct3D11/D3D11GraphicsImpl.h
  29. 391 0
      Source/Urho3D/Graphics/Direct3D11/D3D11IndexBuffer.cpp
  30. 107 0
      Source/Urho3D/Graphics/Direct3D11/D3D11IndexBuffer.h
  31. 155 0
      Source/Urho3D/Graphics/Direct3D11/D3D11RenderSurface.cpp
  32. 101 0
      Source/Urho3D/Graphics/Direct3D11/D3D11RenderSurface.h
  33. 88 0
      Source/Urho3D/Graphics/Direct3D11/D3D11ShaderProgram.h
  34. 464 0
      Source/Urho3D/Graphics/Direct3D11/D3D11ShaderVariation.cpp
  35. 153 0
      Source/Urho3D/Graphics/Direct3D11/D3D11ShaderVariation.h
  36. 477 0
      Source/Urho3D/Graphics/Direct3D11/D3D11Texture.cpp
  37. 176 0
      Source/Urho3D/Graphics/Direct3D11/D3D11Texture.h
  38. 548 0
      Source/Urho3D/Graphics/Direct3D11/D3D11Texture2D.cpp
  39. 81 0
      Source/Urho3D/Graphics/Direct3D11/D3D11Texture2D.h
  40. 572 0
      Source/Urho3D/Graphics/Direct3D11/D3D11Texture3D.cpp
  41. 80 0
      Source/Urho3D/Graphics/Direct3D11/D3D11Texture3D.h
  42. 746 0
      Source/Urho3D/Graphics/Direct3D11/D3D11TextureCube.cpp
  43. 89 0
      Source/Urho3D/Graphics/Direct3D11/D3D11TextureCube.h
  44. 479 0
      Source/Urho3D/Graphics/Direct3D11/D3D11VertexBuffer.cpp
  45. 130 0
      Source/Urho3D/Graphics/Direct3D11/D3D11VertexBuffer.h
  46. 90 0
      Source/Urho3D/Graphics/Direct3D11/D3D11VertexDeclaration.cpp
  47. 53 0
      Source/Urho3D/Graphics/Direct3D11/D3D11VertexDeclaration.h
  48. 121 247
      Source/Urho3D/Graphics/Direct3D9/D3D9Graphics.cpp
  49. 30 38
      Source/Urho3D/Graphics/Direct3D9/D3D9Graphics.h
  50. 54 0
      Source/Urho3D/Graphics/Direct3D9/D3D9ShaderProgram.h
  51. 8 22
      Source/Urho3D/Graphics/Direct3D9/D3D9ShaderVariation.cpp
  52. 4 0
      Source/Urho3D/Graphics/Direct3D9/D3D9Texture.h
  53. 25 19
      Source/Urho3D/Graphics/Drawable.cpp
  54. 21 27
      Source/Urho3D/Graphics/Drawable.h
  55. 3 1
      Source/Urho3D/Graphics/GPUObject.h
  56. 3 1
      Source/Urho3D/Graphics/Graphics.h
  57. 1 13
      Source/Urho3D/Graphics/GraphicsDefs.cpp
  58. 22 28
      Source/Urho3D/Graphics/GraphicsDefs.h
  59. 3 1
      Source/Urho3D/Graphics/GraphicsImpl.h
  60. 3 1
      Source/Urho3D/Graphics/IndexBuffer.h
  61. 2 11
      Source/Urho3D/Graphics/Light.cpp
  62. 42 24
      Source/Urho3D/Graphics/Material.cpp
  63. 10 8
      Source/Urho3D/Graphics/Material.h
  64. 139 0
      Source/Urho3D/Graphics/OpenGL/OGLConstantBuffer.cpp
  65. 75 0
      Source/Urho3D/Graphics/OpenGL/OGLConstantBuffer.h
  66. 359 283
      Source/Urho3D/Graphics/OpenGL/OGLGraphics.cpp
  67. 60 41
      Source/Urho3D/Graphics/OpenGL/OGLGraphics.h
  68. 4 2
      Source/Urho3D/Graphics/OpenGL/OGLGraphicsImpl.cpp
  69. 6 4
      Source/Urho3D/Graphics/OpenGL/OGLGraphicsImpl.h
  70. 3 7
      Source/Urho3D/Graphics/OpenGL/OGLIndexBuffer.cpp
  71. 184 10
      Source/Urho3D/Graphics/OpenGL/OGLShaderProgram.cpp
  72. 33 1
      Source/Urho3D/Graphics/OpenGL/OGLShaderProgram.h
  73. 10 5
      Source/Urho3D/Graphics/OpenGL/OGLShaderVariation.cpp
  74. 11 2
      Source/Urho3D/Graphics/OpenGL/OGLTexture.cpp
  75. 3 3
      Source/Urho3D/Graphics/OpenGL/OGLTexture.h
  76. 4 3
      Source/Urho3D/Graphics/OpenGL/OGLVertexBuffer.cpp
  77. 2 0
      Source/Urho3D/Graphics/RenderPath.h
  78. 3 1
      Source/Urho3D/Graphics/RenderSurface.h
  79. 7 24
      Source/Urho3D/Graphics/Renderer.cpp
  80. 1 7
      Source/Urho3D/Graphics/Renderer.h
  81. 5 1
      Source/Urho3D/Graphics/ShaderProgram.h
  82. 4 2
      Source/Urho3D/Graphics/ShaderVariation.h
  83. 6 10
      Source/Urho3D/Graphics/StaticModel.cpp
  84. 120 57
      Source/Urho3D/Graphics/Technique.cpp
  85. 52 41
      Source/Urho3D/Graphics/Technique.h
  86. 3 1
      Source/Urho3D/Graphics/Texture.h
  87. 4 2
      Source/Urho3D/Graphics/Texture2D.h
  88. 3 1
      Source/Urho3D/Graphics/Texture3D.h
  89. 4 2
      Source/Urho3D/Graphics/TextureCube.h
  90. 3 1
      Source/Urho3D/Graphics/VertexBuffer.h
  91. 3 1
      Source/Urho3D/Graphics/VertexDeclaration.h
  92. 222 254
      Source/Urho3D/Graphics/View.cpp
  93. 24 17
      Source/Urho3D/Graphics/View.h
  94. 3 4
      Source/Urho3D/LuaScript/pkgs/Graphics/Graphics.pkg
  95. 1 25
      Source/Urho3D/LuaScript/pkgs/Graphics/GraphicsDefs.pkg
  96. 2 7
      Source/Urho3D/LuaScript/pkgs/Graphics/Material.pkg
  97. 0 3
      Source/Urho3D/LuaScript/pkgs/Graphics/Renderer.pkg
  98. 6 10
      Source/Urho3D/LuaScript/pkgs/Graphics/Technique.pkg
  99. 19 22
      Source/Urho3D/Script/GraphicsAPI.cpp
  100. 1 1
      Source/Urho3D/UI/Text3D.cpp

+ 31 - 10
CMake/Modules/FindDirect3D.cmake

@@ -20,7 +20,7 @@
 # THE SOFTWARE.
 #
 
-# For MSVC compiler, find Microsoft DirectX installation (need June 2010 SDK or later)
+# For MSVC compiler, find Microsoft DirectX installation (need June 2010 SDK or later, or a Windows SDK)
 # For MinGW compiler, assume MinGW not only comes with the necessary headers & libraries but also has the headers & libraries directories in its default search path
 # (use 'echo |gcc -v -E -' and 'gcc -print-search-dirs', respectively, to double check)
 #
@@ -33,7 +33,10 @@ if (NOT WIN32 OR DIRECT3D_FOUND)
     return ()
 endif ()
 
-if (MSVC)
+# When using Direct3D11, do not search for the SDK from Visual Studio 2012 onward
+# to avoid incompatibility between DirectX SDK and Windows SDK defines and the 
+# resulting warning spam
+if (MSVC AND (MSVC_VERSION LESS 1700 OR NOT URHO3D_D3D11))
     set (DIRECTX_INC_SEARCH_PATH
         "C:/Program Files (x86)/Microsoft DirectX SDK (June 2010)/Include"
         "C:/Program Files/Microsoft DirectX SDK (June 2010)/Include"
@@ -58,12 +61,22 @@ if (MSVC)
             "$ENV{DXSDK_DIR}/Lib"
             "$ENV{DXSDK_DIR}/Lib/x86")
     endif ()
-    find_library (DIRECT3D_LIB_D3D9 NAMES d3d9 PATHS ${DIRECTX_LIB_SEARCH_PATH})
-    find_library (DIRECT3D_LIB_D3DCOMPILER NAMES d3dcompiler PATHS ${DIRECTX_LIB_SEARCH_PATH})
-
-    if (DIRECT3D_INCLUDE_DIRS AND DIRECT3D_LIB_D3D9 AND DIRECT3D_LIB_D3DCOMPILER)
-        set (DIRECT3D_LIBRARIES ${DIRECT3D_LIB_D3D9} ${DIRECT3D_LIB_D3DCOMPILER})
-        set (DIRECT3D_FOUND 1)
+    if (NOT URHO3D_D3D11)
+        find_library (DIRECT3D_LIB_D3D9 NAMES d3d9 PATHS ${DIRECTX_LIB_SEARCH_PATH})
+        find_library (DIRECT3D_LIB_D3DCOMPILER NAMES d3dcompiler PATHS ${DIRECTX_LIB_SEARCH_PATH})
+        if (DIRECT3D_INCLUDE_DIRS AND DIRECT3D_LIB_D3D9 AND DIRECT3D_LIB_D3DCOMPILER)
+            set (DIRECT3D_LIBRARIES ${DIRECT3D_LIB_D3D9} ${DIRECT3D_LIB_D3DCOMPILER})
+            set (DIRECT3D_FOUND 1)
+        endif ()
+    else ()
+        find_library (DIRECT3D_LIB_D3D11 NAMES d3d11 PATHS ${DIRECTX_LIB_SEARCH_PATH})
+        find_library (DIRECT3D_LIB_D3DCOMPILER NAMES d3dcompiler PATHS ${DIRECTX_LIB_SEARCH_PATH})
+        find_library (DIRECT3D_LIB_DXGI NAMES dxgi PATHS ${DIRECTX_LIB_SEARCH_PATH})
+        find_library (DIRECT3D_LIB_DXGUID NAMES dxguid PATHS ${DIRECTX_LIB_SEARCH_PATH})
+        if (DIRECT3D_INCLUDE_DIRS AND DIRECT3D_LIB_D3D11 AND DIRECT3D_LIB_D3DCOMPILER AND DIRECT3D_LIB_DXGI AND DIRECT3D_LIB_DXGUID)
+            set (DIRECT3D_LIBRARIES ${DIRECT3D_LIB_D3D11} ${DIRECT3D_LIB_D3DCOMPILER} ${DIRECT3D_LIB_DXGI} ${DIRECT3D_LIB_DXGUID})
+            set (DIRECT3D_FOUND 1)
+        endif ()
     endif ()
 endif ()
 
@@ -71,9 +84,17 @@ if (DIRECT3D_FOUND)
     include (FindPackageMessage)
     FIND_PACKAGE_MESSAGE (Direct3D "Found DirectX SDK: ${DIRECT3D_LIBRARIES} ${DIRECT3D_INCLUDE_DIRS}" "[${DIRECT3D_LIBRARIES}][${DIRECT3D_INCLUDE_DIRS}]")
 else ()
-    set (DIRECT3D_LIBRARIES d3d9 d3dcompiler)
+    if (NOT URHO3D_D3D11)
+        set (DIRECT3D_LIBRARIES d3d9 d3dcompiler)
+    else ()
+        set (DIRECT3D_LIBRARIES d3d11 d3dcompiler dxgi dxguid)
+    endif ()
     if (MSVC)
-        message (STATUS "DirectX SDK not found. This is not fatal if a recent Windows SDK is installed")
+        if (MSVC_VERSION LESS 1700 OR NOT URHO3D_D3D11)
+            message (STATUS "DirectX SDK not found. This is not fatal if a recent Windows SDK is installed")
+        else ()
+            message (STATUS "DirectX SDK search skipped for MSVC 2012 and greater when using Direct3D 11")
+        endif ()
     else ()
         message (STATUS "DirectX SDK search skipped for MinGW. It is assumed that MinGW itself comes with the necessary headers & libraries")
     endif ()

+ 14 - 0
CMake/Modules/Urho3D-CMake-common.cmake

@@ -133,6 +133,11 @@ elseif (WIN32)
     # Direct3D can be manually enabled with -DURHO3D_OPENGL=0, but it is likely to fail unless the MinGW-w64 distribution is used due to dependency to Direct3D headers and libs
     option (URHO3D_OPENGL "Use OpenGL instead of Direct3D (Windows platform only)" TRUE)
 endif ()
+if (WIN32)
+    # On Windows platform Direct3D11 can be optionally chosen
+    # Using Direct3D11 on non-MSVC compiler may require copying and renaming Microsoft official libraries (.lib to .a), else link failures or non-functioning graphics may result
+    option (URHO3D_D3D11 "Use Direct3D11 instead of Direct3D9 (Windows platform only)")
+endif ()
 if (CMAKE_HOST_WIN32 AND NOT DEFINED URHO3D_MKLINK)
     # Test whether the host system is capable of setting up symbolic link
     execute_process (COMMAND cmd /C mklink test-link CMakeCache.txt RESULT_VARIABLE MKLINK_EXIT_CODE OUTPUT_QUIET ERROR_QUIET)
@@ -272,6 +277,15 @@ if (NOT WIN32)
     set (URHO3D_OPENGL 1)
 endif ()
 
+# Add definition for Direct3D11
+if (URHO3D_D3D11)
+    if (NOT WIN32)
+        message(FATAL_ERROR "Direct3D 11 can only be used on Windows platform")
+    endif ()
+    set (URHO3D_OPENGL 0)
+    add_definitions (-DURHO3D_D3D11)
+endif ()
+
 # Add definition for OpenGL
 if (URHO3D_OPENGL)
     add_definitions (-DURHO3D_OPENGL)

+ 11 - 8
Docs/GettingStarted.dox

@@ -8,7 +8,7 @@ namespace Urho3D
 
 Although all required third-party libraries are included as source code, there are system-level dependencies that must be satisfied before Urho3D can be built successfully:
 
-- For Windows, the June 2010 DirectX SDK needs to be installed.
+- For Windows, the June 2010 DirectX SDK needs to be installed. This is not necessary if building on Visual Studio 2012 or newer, which install the Windows SDK with the necessary DirectX files.
 
 - For Linux, the following development packages need to be installed: libx11-dev, libxrandr-dev, libasound2-dev on Debian-based distros; libX11-devel, libXrandr-devel, alsa-lib-devel on RedHat-based distros. Also install the package libgl1-mesa-dev (Debian) or mesa-libGL-devel (RH) if your GPU driver does not include OpenGL headers & libs, but in most of the case it usually does and has better performance than Mesa's OpenGL implementation. Building as 32-bit on a 64-bit system requires installing also the 32-bit versions of the development libraries.
 
@@ -22,7 +22,7 @@ Although all required third-party libraries are included as source code, there a
 
 To run Urho3D, the minimum system requirements are:
 
-- Windows: CPU with SSE instructions support, Windows XP or newer, DirectX 9.0c, GPU with %Shader %Model 2 support (%Shader %Model 3 recommended.)
+- Windows: CPU with SSE instructions support, Windows XP or newer, DirectX 9.0c, GPU with %Shader %Model 3 support.
 
 - Linux & Mac OS X: CPU with SSE instructions support, GPU with OpenGL 2.0 support, EXT_framebuffer_object and EXT_packed_depth_stencil extensions.
 
@@ -93,6 +93,7 @@ A number of build options can be defined when invoking the build scripts or when
 |URHO3D_TESTING       |0|Enable testing support|
 |URHO3D_TEST_TIMEOUT  |*|Number of seconds to test run the executables (when testing support is enabled only), default to 10 on Emscripten platform and 5 on other platforms|
 |URHO3D_OPENGL        |0|Use OpenGL instead of Direct3D (Windows platform only)|
+|URHO3D_D3D11         |0|Use Direct3D11 instead of Direct3D9 (Windows platform only). Overrides URHO3D_OPENGL option.|
 |URHO3D_STATIC_RUNTIME|0|Use static C/C++ runtime libraries and eliminate the need for runtime DLLs installation (VS only)|
 |URHO3D_WIN32_CONSOLE |0|Use console main() as entry point when setting up Windows executable targets (Windows platform only)|
 |URHO3D_MACOSX_BUNDLE |0|Use MACOSX_BUNDLE when setting up Mac OS X executable targets (Xcode native build only)|
@@ -139,7 +140,9 @@ Urho3D uses CMake (http://www.cmake.org) to build. The process has two steps:
 
 Note that Eclipse requires CDT plugin to build C/C++ project. When using generator backed by Unix Makefiles, you can also execute make command directly in the build tree to build the project.
 
-If using MinGW to compile, DirectX headers may need to be acquired separately. They can be copied to the MinGW installation eg. from the following package: http://www.libsdl.org/extras/win32/common/directx-devel.tar.gz These will be missing some of the headers related to shader compilation, so a MinGW build will use OpenGL by default. To build in Direct3D9 mode, the MinGW-w64 port is necessary: http://mingw-w64.sourceforge.net/. Using it, Direct3D9 can be enabled with the "-DURHO3D_OPENGL=0" build option.
+On Windows platform Urho3D can use either Direct3D 9 (default), Direct3D 11 or OpenGL rendering. Other platforms always use OpenGL. Use the CMake options "-DURHO3D_D3D11=1" or "-DURHO3D_OPENGL=1" to choose the non-default APIs.
+
+If using MinGW to compile, DirectX headers may need to be acquired separately. They can be copied to the MinGW installation eg. from the following package: http://www.libsdl.org/extras/win32/common/directx-devel.tar.gz These will be missing some of the headers related to shader compilation, so a MinGW build will use OpenGL by default. To build in Direct3D mode, the MinGW-w64 port is necessary: http://mingw-w64.sourceforge.net/. Using it, Direct3D can be enabled with the "-DURHO3D_OPENGL=0" build option.
 
 After the build is complete, the programs can be run from the bin subdirectory in the build tree. These include the Urho3D player application, which can run application scripts, the tools, and C++ sample applications if they have been enabled. Unless your build tree location is also the same as the Urho3D project source tree, in order to run the Urho3D executables successfully, you must do one of the following first:
 - Copy both the bin/Data and bin/CoreData directories in the source tree to the bin subdirectory in the build tree.
@@ -244,7 +247,7 @@ When using MSVC compiler, Urho3D common CMake module configures the build tree t
 
 \section Building_Library Library build
 
-As of V1.31, the build process first builds the Urho3D library target (either static or shared). The library is then linked against by other targets like tools and samples that reference Urho3D as one of the external libraries. The Urho3D library type is defaulted to static, so the build process would generate standalone executables as previous releases. The Urho3D library type can be changed using "URHO3D_LIB_TYPE" build option.
+The build process first builds the Urho3D library target (either static or shared). The library is then linked against by other targets like tools and samples that reference Urho3D as one of the external libraries. The Urho3D library type is defaulted to static, so the build process would generate standalone executables as previous releases. The Urho3D library type can be changed using "URHO3D_LIB_TYPE" build option.
 
 To install the Urho3D library as an SDK, use the usual 'make install' command. There is an equivalent built-in target called "install" in Visual Studio solution and Xcode project to perform the SDK installation. This could be useful when you want your application to always link against a 'stable' installed version of the Urho3D library, while keeping your Urho3D project root tree in sync with origin/master; or when you want other users in the same host system to use the installed Urho3D SDK instead of them building from source again.
 
@@ -271,9 +274,9 @@ The prerequisites are Doxygen and Graphviz. Tools to dump the \ref ScriptAPI "An
 
 \section Building_Shaders Compiling Direct3D shaders
 
-When building with the Windows 8 SDK, copy d3dcompiler_46.dll from "C:\Program Files (x86)\Windows Kits\8.0\bin\x86" to build tree's "bin" directory so that Urho3D executables will run correctly.
+When building for Direct3D9 with the Windows 8 SDK (Visual Studio 2012+), copy d3dcompiler_46.dll from "C:\Program Files (x86)\Windows Kits\8.0\bin\x86" to build tree's "bin" directory so that Urho3D executables will run correctly. When building for Direct3D11, copy d3dcompiler_47.dll from "C:\Program Files (x86)\Windows Kits\8.1\bin\x86" to the "bin" directory. In both cases, if compiling Urho3D as 64-bit, copy from the "bin\x64" directory instead.
 
-Note that you can also force an OpenGL mode build on Windows platform by using the "URHO3D_OPENGL" build option in the table below; OpenGL does not depend on a separate shader compiler DLL.
+OpenGL does not depend on a separate shader compiler DLL.
 
 \page Running Running Urho3D player application
 
@@ -314,7 +317,8 @@ The engine can be configured using the following command line options.
 -tq <level>  Texture quality level, default 2 (high)
 -tf <level>  Texture filter mode, default 2 (trilinear)
 -af <level>  Texture anisotropy level, default 4. Also sets anisotropic filter mode
--flushgpu    Flush GPU command queue each frame. Effective only on Direct3D9
+-gl2         Force OpenGL 2 use even if OpenGL 3 is available
+-flushgpu    Flush GPU command queue each frame. Effective only on Direct3D
 -borderless  Borderless window mode
 -headless    Headless mode. No application window will be created
 -landscape   Use landscape orientations (iOS only, default)
@@ -328,7 +332,6 @@ The engine can be configured using the following command line options.
 -nothreads   Disable worker threads
 -nosound     Disable sound output
 -noip        Disable sound mixing interpolation
--sm2         Force SM2.0 rendering
 -touch       Touch emulation on desktop platform
 \endverbatim
 

+ 67 - 35
Docs/Reference.dox

@@ -133,7 +133,6 @@ The full list of supported parameters, their datatypes and default values:
 - ResourcePaths (string) A semicolon-separated list of resource paths to use. If corresponding packages (ie. Data.pak for Data directory) exist they will be used instead. Default "Data;CoreData".
 - ResourcePackages (string) A semicolon-separated list of resource packages to use. Default empty.
 - AutoloadPaths (string) A semicolon-separated list of autoload paths to use. Any resource packages and subdirectories inside an autoload path will be added to the resource system. Default "Autoload".
-- ForceSM2 (bool) Whether to force %Shader %Model 2, effective in Direct3D9 mode only. Default false.
 - ExternalWindow (void ptr) External window handle to use instead of creating an application window. Default null.
 - WindowIcon (string) %Window icon image resource name. Default empty (use application default icon.)
 - WindowTitle (string) %Window title. Default "Urho3D".
@@ -146,7 +145,8 @@ The full list of supported parameters, their datatypes and default values:
 - Borderless (bool) Whether to create the window as borderless. Default false.
 - TripleBuffer (bool) Whether to use triple-buffering. Default false.
 - VSync (bool) Whether to wait for vertical sync when presenting rendering window contents. Default false.
-- FlushGPU (bool) Whether to flush GPU command buffer each frame for less input latency. Effective only on Direct3D9. Default false.
+- FlushGPU (bool) Whether to flush GPU command buffer each frame (Direct3D9) or limit the amount of buffered frames (Direct3D11) for less input latency. Ineffective on OpenGL. Default false.
+- ForceGL2 (bool) When true, forces OpenGL 2 use even if OpenGL 3 is available. No effect on Direct3D or mobile builds. Default false.
 - Multisample (int) Hardware multisampling level. Default 1 (no multisampling.)
 - Orientations (string) Space-separated list of allowed orientations. Effective only on iOS. All possible values are "LandscapeLeft", "LandscapeRight", "Portrait" and "PortraitUpsideDown". Default "LandscapeLeft LandscapeRight".
 - DumpShaders (string) Filename to dump used shader variations to for precaching.
@@ -713,19 +713,11 @@ Screen resolution, fullscreen/windowed, vertical sync and hardware multisampling
 
 When setting the initial screen mode, Graphics does a few checks:
 
-- For Direct3D9, the supported shader model is checked. 2 is minimum, but 3 will be used if available. For testing, SM2 can be forced by calling \ref Graphics::SetForceSM2 "SetForceSM2()" before setting the initial screen mode.
-- For OpenGL, version 2.0 with EXT_framebuffer_object, EXT_packed_depth_stencil and EXT_texture_filter_anisotropic extensions is checked for.
-- Is hardware instancing supported? This requires %Shader %Model 3 on Direct3D9 and the ARB_instanced_arrays extension on OpenGL.
+- For Direct3D9, shader model 3.0 support is checked.
+- For OpenGL, version 3.2 support is checked for first and used if available. As a fallback, version 2.0 with EXT_framebuffer_object, EXT_packed_depth_stencil and EXT_texture_filter_anisotropic extensions is checked for. The ARB_instanced_arrays extension is also checked for but not required; it will enable hardware instancing support when present.
 - Are hardware shadow maps supported? Both AMD & NVIDIA style shadow maps can be used. If neither are available, no shadows will be rendered.
 - Are light pre-pass and deferred rendering modes supported? These require sufficient multiple rendertarget support, and R32F texture format support.
 
-%Shader model 2 has the following limitations due to limited pixel shader instruction count:
-
-- Directional light shadows support a maximum of 3 cascade splits instead of 4.
-- Shadowed point lights do not support specular calculations in forward rendering.
-- Complex materials do not combine the ambient pass with the first forward light (lit base pass optimization.)
-- Complex forward rendered materials (eg. normal + specular map + alpha masking) combined with shadow mapping or height fog may not work.
-
 \section Rendering_Renderer Renderer
 
 Renderer implements the actual rendering of 3D views each frame, and controls global settings such as texture quality, material quality, specular lighting and shadow map base resolution.
@@ -805,7 +797,7 @@ See also \ref Materials "Materials", \ref Shaders "Shaders", \ref Lights "Lights
 
 See \ref RenderingModes "Rendering modes" for detailed discussion on the forward, light pre-pass and deferred rendering modes.
 
-See \ref APIDifferences "Differences between Direct3D9 and OpenGL" for what to watch out for when using the low-level rendering functionality directly.
+See \ref APIDifferences "Differences between Direct3D and OpenGL" for what to watch out for when using the low-level rendering functionality directly.
 
 
 \page RenderingModes Rendering modes
@@ -855,23 +847,23 @@ Forward rendering makes it possible to use hardware multisampling and different
 Finally note that due to OpenGL framebuffer object limitations an extra framebuffer blit has to happen at the end in both light pre-pass and deferred rendering, which costs some performance. Also, because multiple rendertargets on OpenGL must have the same format, an R32F texture can not be used for linear depth, but instead 24-bit depth is manually encoded and decoded into RGB channels.
 
 
-\page APIDifferences Differences between Direct3D9 and OpenGL
+\page APIDifferences Differences between Direct3D and OpenGL
 
 These differences need to be observed when using the low-level rendering functionality directly. The high-level rendering architecture, including the Renderer and UI subsystems and the Drawable subclasses already handle most of them transparently to the user.
 
-- The post-projection depth range is (0,1) for Direct3D9 and (-1,1) for OpenGL. The Camera can be queried either for an API-specific or API-independent (Direct3D9 convention) projection matrix.
+- The post-projection depth range is (0,1) for Direct3D and (-1,1) for OpenGL. The Camera can be queried either for an API-specific or API-independent (Direct3D convention) projection matrix.
 
-- To render with 1:1 texel-to-pixel mapping, on Direct3D9 UV coordinates have to be shifted a half-pixel to the right and down, or alternatively vertex positions can be shifted a half-pixel left and up.
+- To render with 1:1 texel-to-pixel mapping, on Direct3D9 UV coordinates have to be shifted a half-pixel to the right and down, or alternatively vertex positions can be shifted a half-pixel left and up. The required shift can be queried with the function \ref Graphics::GetPixelUVOffset "GetPixelUVOffset()".
 
-- On Direct3D9 the depth-stencil surface can be equal or larger in size than the color rendertarget. On OpenGL the sizes must always match. Furthermore, OpenGL can not use the backbuffer depth-stencil surface when rendering to a texture. To overcome these limitations, Graphics will create correctly sized depth-stencil surfaces on demand whenever a texture is set as a color rendertarget, and a null depth-stencil is specified.
+- On Direct3D the depth-stencil surface can be equal or larger in size than the color rendertarget. On OpenGL the sizes must always match. Furthermore, OpenGL can not use the backbuffer depth-stencil surface when rendering to a texture. To overcome these limitations, Graphics will create correctly sized depth-stencil surfaces on demand whenever a texture is set as a color rendertarget, and a null depth-stencil is specified.
 
-- On Direct3D9 the viewport will be reset to full size when the first color rendertarget is changed. On OpenGL this does not happen. To ensure correct operation on both APIs, always use this sequence: first set the rendertargets, then the depth-stencil surface and finally the viewport.
+- On Direct3D9 the viewport will be reset to full size when the first color rendertarget is changed. On OpenGL & Direct3D11 this does not happen. To ensure correct operation on both APIs, always use this sequence: first set the rendertargets, then the depth-stencil surface and finally the viewport.
 
 - On OpenGL modifying a texture will cause it to be momentarily set on the first texture unit. If another texture was set there, the assignment will be lost. Graphics performs a check to not assign textures redundantly, so it is safe and recommended to always set all needed textures before rendering.
 
 - Modifying an index buffer on OpenGL will similarly cause the existing index buffer assignment to be lost. Therefore, always set the vertex and index buffers before rendering.
 
-- %Shader resources are stored in different locations depending on the API: bin/CoreData/Shaders/HLSL for Direct3D9, and bin/CoreData/Shaders/GLSL for OpenGL.
+- %Shader resources are stored in different locations depending on the API: bin/CoreData/Shaders/HLSL for Direct3D, and bin/CoreData/Shaders/GLSL for OpenGL.
 
 - To ensure similar UV addressing for render-to-texture viewports on both APIs, on OpenGL texture viewports will be rendered upside down.
 
@@ -992,8 +984,8 @@ For the layout definitions, see http://www.cgtextures.com/content.php?action=tut
 A technique definition looks like this:
 
 \code
-<technique vs="VertexShaderName" ps="PixelShaderName" vsdefines="DEFINE1 DEFINE2" psdefines="DEFINE3 DEFINE4" sm3="false|true" desktop="false|true" >
-    <pass name="base|litbase|light|alpha|litalpha|postopaque|refract|postalpha|prepass|material|deferred|depth|shadow" sm3="false|true" desktop="false|true" >
+<technique vs="VertexShaderName" ps="PixelShaderName" vsdefines="DEFINE1 DEFINE2" psdefines="DEFINE3 DEFINE4" desktop="false|true" >
+    <pass name="base|litbase|light|alpha|litalpha|postopaque|refract|postalpha|prepass|material|deferred|depth|shadow" desktop="false|true" >
         vs="VertexShaderName" ps="PixelShaderName" vsdefines="DEFINE1 DEFINE2" psdefines="DEFINE3 DEFINE4"
         lighting="unlit|pervertex|perpixel"
         blend="replace|add|multiply|alpha|addalpha|premulalpha|invdestalpha|subtract|subtractalpha"
@@ -1005,11 +997,9 @@ A technique definition looks like this:
 </technique>
 \endcode
 
-The "sm3" attribute in the technique root element allows the technique to specify it requires %Shader %Model 3 hardware. Omitting it is same as specifying false (works on both SM2 & 3.) The sm3 attribute can also be specified per pass, to disable for example a too complex lit base optimization pass on SM2.
+The "desktop" attribute in either technique or pass allows to specify it requires desktop graphics hardware (exclude mobile devices.) Omitting it is the same as specifying false.
 
-The "desktop" attribute in either technique or pass works similarly to allow requiring desktop hardware and excluding mobile devices.
-
-Shaders are referred to by giving the name of a shader without path and file extension. For example "Basic" or "LitSolid". The engine will add the correct path and file extension (Shaders/HLSL/LitSolid.hlsl for Direct3D9, and Shaders/GLSL/LitSolid.glsl for OpenGL) automatically. The same shader source file contains both the vertex and pixel shader. In addition, compilation defines can be specified, which are passed to the shader compiler. For example the define "DIFFMAP" typically enables diffuse mapping in the pixel shader.
+Shaders are referred to by giving the name of a shader without path and file extension. For example "Basic" or "LitSolid". The engine will add the correct path and file extension (Shaders/HLSL/LitSolid.hlsl for Direct3D, and Shaders/GLSL/LitSolid.glsl for OpenGL) automatically. The same shader source file contains both the vertex and pixel shader. In addition, compilation defines can be specified, which are passed to the shader compiler. For example the define "DIFFMAP" typically enables diffuse mapping in the pixel shader.
 
 Shaders and their compilation defines can be specified on both the technique and pass level. If a pass does not override the default shaders specified on the technique level, it still can specify additional compilation defines to be used. However, if a pass overrides the shaders, then the technique-level defines are not used.
 
@@ -1045,7 +1035,7 @@ Urho3D uses an ubershader-like approach: permutations of each shader will be bui
 
 The building of these permutations happens on demand: technique and renderpath definition files both refer to shaders and the compilation defines to use with them. In addition the engine will add inbuilt defines related to geometry type and lighting. It is not generally possible to enumerate beforehand all the possible permutations that can be built out of a single shader.
 
-On Direct3D9 compiled shader bytecode is saved to disk in a "Cache" subdirectory next to the shader source code, so that the possibly time-consuming compile can be skipped on the next time the shader permutation is needed. On OpenGL such mechanism is not available.
+On Direct3D compiled shader bytecode is saved to disk in a "Cache" subdirectory next to the shader source code, so that the possibly time-consuming compile can be skipped on the next time the shader permutation is needed. On OpenGL such mechanism is not available.
 
 \section Shaders_InbuiltDefines Inbuilt compilation defines
 
@@ -1101,7 +1091,7 @@ Pixel shader uniforms:
 
 \section Shaders_Writing Writing shaders
 
-Shaders must be written separately for HLSL (Direct3D9) and GLSL (OpenGL). The built-in shaders try to implement the same functionality on both shader languages as closely as possible.
+Shaders must be written separately for HLSL (Direct3D) and GLSL (OpenGL). The built-in shaders try to implement the same functionality on both shader languages as closely as possible.
 
 To get started with writing your own shaders, start with studying the most basic examples possible: the Basic, Shadow & Unlit shaders. Note the shader include files which bring common functionality, for example Uniforms.hlsl, Samplers.hlsl & Transform.hlsl for HLSL shaders.
 
@@ -1121,7 +1111,7 @@ vec3 worldPos = GetWorldPos(modelMatrix);
 gl_Position = GetClipPos(worldPos);
 \endcode
 
-On both Direct3D9 and OpenGL the vertex and pixel shaders are written into the same file, and the entrypoint functions must be called VS() and PS(). In OpenGL mode one of these is transformed to the main() function required by GLSL behind the scenes. When compiling a vertex shader, the compilation define "COMPILEVS" is always present, and likewise "COMPILEPS" when compiling a pixel shader. These are heavily used in the shader include files to prevent constructs that are illegal for the "wrong" type of shader, and to reduce compilation time. When compiling for %Shader %Model 3 on Direct3D9, the compilation define "SM3" is present: this can be used to separate code which would not compile on SM2.
+On both Direct3D and OpenGL the vertex and pixel shaders are written into the same file, and the entrypoint functions must be called VS() and PS(). In OpenGL mode one of these is transformed to the main() function required by GLSL behind the scenes. When compiling a vertex shader, the compilation define "COMPILEVS" is always present, and likewise "COMPILEPS" when compiling a pixel shader. These are heavily used in the shader include files to prevent constructs that are illegal for the "wrong" type of shader, and to reduce compilation time.
 
 The uniforms must be prefixed in a certain way so that the engine understands them:
 
@@ -1137,13 +1127,26 @@ uniform sampler2D sDetailMap2;
 uniform sampler2D sDetailMap3;
 \endcode
 
-\section Shaders_Migration Migration of old shaders
+The maximum number of bones supported for hardware skinning depends on the graphics API and is relayed to the shader code in the MAXBONES compilation define. Typically the maximum is 64, but is reduced to 32 on the Raspberry PI, and increased to 128 on Direct3D 11 & OpenGL 3. See also \ref Graphics::GetMaxBones "GetMaxBones()".
+
+\section Shaders_API API differences
+
+Direct3D9 and Direct3D11 share the same HLSL shader code, and likewise OpenGL 2, OpenGL 3, OpenGL ES 2 and WebGL share the same GLSL code. Macros and some conditional code are used to hide the API differences where possible.
+
+When HLSL shaders are compiled for Direct3D11, the define D3D11 is present, and the following details need to be observed:
+
+- Uniforms are organized into constant buffers. See the file Uniforms.hlsl for the built-in uniforms. See TerrainBlend.hlsl for an example of defining your own uniforms into the "custom" constant buffer slot.
+- Both textures and samplers are defined for each texture unit. The macros in Samplers.hlsl (Sample2D, SampleCube etc.) can be used to write code that works on both APIs. These take the texture unit name without the 's' prefix.
+- Vertex shader output position and pixel shader output color need to use the SV_POSITION and SV_TARGET semantics. The macros OUTPOSITION and OUTCOLOR0-3 can be used to select the correct semantic on both APIs. In the vertex shader, the output position should be specified last, as otherwise other interpolator outputs may not function correctly.
+- On Direct3D11 the clip plane coordinate must be calculated manually. This is indicated by the CLIPPLANE compilation define, which is added automatically by the Graphics class. See for example the LitSolid.hlsl shader.
+- Direct3D11 does not support luminance and luminance-alpha texture formats, but rather uses the R and RG channels. Therefore be prepared to perform swizzling in the texture reads as appropriate.
+- Direct3D11 will fail to render if the vertex shader refers to vertex elements that don't exist in the vertex buffers.
 
-In Urho3D V1.3 and before an XML description file was used for shaders, which would enumerate the compilation defines used by shaders into named "options" or "variations".
-The shader would then be referred to with these option names, for example a pixel shader LitSolid_Diff would use the LitSolid source code and the define DIFFMAP. To convert to the
-current system, look up the defines from the description files and add them directly to the technique or renderpath XML descriptions ("vsdefines" or "psdefines" attributes). Also make sure the shader references ("vs" and "ps" attributes) are just the bare shader names with no underscore or option names attached. Finally, delete the shader XML description files.
+For OpenGL, the define GL3 is present when GLSL shaders are being compiled for OpenGL 3+, the define GL_ES is present for OpenGL ES 2, WEBGL define is present for WebGL and RPI define is present for the Raspberry Pi. Observe the following differences:
 
-GLSL shaders in V1.3 and before used separate .vert and .frag files for the vertex and pixel shader source code. Now these are combined into .glsl files that include both shaders. Include files are likewise merged into .glsl files instead of separate vertex and pixel shader includes. To merge your shader code, append the pixel shader source into the vertex shader source file without the varyings definition (which should already be in the vertex shader code). Merge and fix include statements: change the file extension to .glsl. Note that some include files depend on each other so it is safest to always include Uniforms.glsl and Samplers.glsl first. Finally change the file extension of the vertex shader source file from .vert to .glsl and delete the .frag file. If you have pieces of pixel shader code (for example additional functions or variables) that produce errors when included in the vertex shader compile, wrap these with #ifdef COMPILEPS, and vice versa for vertex shader code.
+- On OpenGL 3 GLSL version 150 will be used if the shader source code does not define the version. The texture sampling functions are different but are worked around with defines in the file Samplers.glsl. Likewise the file Transform.glsl contains macros to hide the differences in declaring vertex attributes, interpolators and fragment outputs.
+- On OpenGL 3 luminance, alpha and luminance-alpha texture formats are deprecated, and are replaced with R and RG formats. Therefore be prepared to perform swizzling in the texture reads as appropriate.
+- On OpenGL ES 2 precision qualifiers need to be used.
 
 \section Shaders_Precaching Shader precaching
 
@@ -2481,6 +2484,7 @@ Options:
 -ns         Do not create subdirectories for resources
 -nz         Do not create a zone and a directional light (scene mode only)
 -nf         Do not fix infacing normals
+-mb <x>     Maximum number of bones per submesh. Default 64
 -p <path>   Set path for scene resources. Default is output file path
 -r <name>   Use the named scene node as root node\n"
 -f <freq>   Animation tick frequency to use if unspecified. Default 4800
@@ -2518,6 +2522,7 @@ Options:
 -r      Output only rotations from animations
 -s      Split each submesh into own vertex buffer
 -t      Generate tangents
+-mb <x> Maximum number of bones per submesh, default 64
 \endverbatim
 
 Note: outputting only bone rotations may help when using an animation in a different model, but if bone position changes have been used for effect, the animation may become less lively. Unpredictable mutilations might result from using an animation in a model not originally intended for, as Urho3D does not specifically attempt to retarget animations.
@@ -2717,13 +2722,13 @@ uint       Number of tracks
 
 Note: animations are stored using absolute bone transformations. Therefore only lerp-blending between animations is supported; additive pose modification is not.
 
-\section FileFormats_Shader Direct3D9 binary shader format (.vs2, .ps2, .vs3, .ps3)
+\section FileFormats_Shader Direct3D9 binary shader format (.vs3, .ps3)
 
 \verbatim
 byte[4]    Identifier "USHD"
 
 short      Shader type (0 = vertex, 1 = pixel)
-short      Shader model (2 or 3)
+short      Shader model (3)
 
 uint       Number of constant parameters
 
@@ -2742,6 +2747,33 @@ uint       Bytecode size
 byte[]     Bytecode
 \endverbatim
 
+\section FileFormats_Shader Direct3D11 binary shader format (.vs4, .ps4)
+
+\verbatim
+byte[4]    Identifier "USHD"
+
+short      Shader type (0 = vertex, 1 = pixel)
+short      Shader model (4)
+uint       Vertex element mask (0 for pixel shaders)
+
+uint       Number of constant parameters
+
+    For each constant parameter:
+    cstring    Parameter name
+    byte       CBuffer index
+    uint       Start byte offset in CBuffer
+    uint       Byte size
+
+uint       Number of texture units
+
+    For each texture unit:
+    cstring    Texture unit name
+    byte       Sampler index
+
+uint       Bytecode size
+byte[]     Bytecode
+\endverbatim
+
 \section FileFormats_Package Package file (.pak)
 
 \verbatim

+ 1 - 1
Source/CMakeLists.txt

@@ -85,7 +85,7 @@ endif ()
 if (NOT IOS AND NOT ANDROID AND NOT RPI AND NOT EMSCRIPTEN)
     if (URHO3D_OPENGL)
         add_subdirectory (ThirdParty/GLEW)
-    else ()
+    elseif (NOT URHO3D_D3D11)
         add_subdirectory (ThirdParty/MojoShader)
     endif ()
     add_subdirectory (ThirdParty/LibCpuId)

+ 8 - 0
Source/ThirdParty/GLEW/glew.c

@@ -29,6 +29,8 @@
 ** THE POSSIBILITY OF SUCH DAMAGE.
 */
 
+// Modified by Lasse Oorni for Urho3D
+
 #include <glew.h>
 
 #if defined(_WIN32)
@@ -8927,6 +8929,12 @@ GLenum GLEWAPIENTRY glewContextInit (GLEW_CONTEXT_ARG_DEF_LIST)
     CONST_CAST(GLEW_VERSION_1_1)   = GLEW_VERSION_1_2   == GL_TRUE || ( major == 1 && minor >= 1 ) ? GL_TRUE : GL_FALSE;
   }
 
+  // Urho3D: GLEW does not query extensions properly on an OpenGL 3+ core context. Enable experimental mode in that case.
+  // However on OpenGL 2 we need to be strict about not using features that are not listed in extensions (for example
+  // instancing) even if the corresponding function pointers are non-null
+  if (GLEW_VERSION_3_2)
+    glewExperimental = GL_TRUE;
+
   /* query opengl extensions string */
   extStart = glGetString(GL_EXTENSIONS);
   if (extStart == 0)

+ 15 - 6
Source/Tools/AssetImporter/AssetImporter.cpp

@@ -122,6 +122,7 @@ bool noOverwriteMaterial_ = false;
 bool noOverwriteTexture_ = false;
 bool noOverwriteNewerTexture_ = false;
 bool checkUniqueModel_ = true;
+unsigned maxBones_ = 64;
 Vector<String> nonSkinningBoneIncludes_;
 Vector<String> nonSkinningBoneExcludes_;
 
@@ -231,6 +232,7 @@ void Run(const Vector<String>& arguments)
             "-nz         Do not create a zone and a directional light (scene mode only)\n"
             "-nf         Do not fix infacing normals\n"
             "-ne         Do not save empty nodes (scene mode only)\n"
+            "-mb <x>     Maximum number of bones per submesh. Default 64\n"
             "-p <path>   Set path for scene resources. Default is output file path\n"
             "-r <name>   Use the named scene node as root node\n"
             "-f <freq>   Animation tick frequency to use if unspecified. Default 4800\n"
@@ -338,6 +340,13 @@ void Run(const Vector<String>& arguments)
                     break;
                 }
             }
+            else if (argument == "mb" && !value.Empty())
+            {
+                maxBones_ = ToUInt(value);
+                if (maxBones_ < 1)
+                    maxBones_ = 1;
+                ++i;
+            }
             else if (argument == "p" && !value.Empty())
             {
                 resourcePath_ = AddTrailingSlash(value);
@@ -905,7 +914,7 @@ void BuildAndSaveModel(OutModel& model)
         outModel->SetNumGeometryLodLevels(destGeomIndex, 1);
         outModel->SetGeometry(destGeomIndex, 0, geom);
         outModel->SetGeometryCenter(destGeomIndex, center);
-        if (model.bones_.Size() > MAX_SKIN_MATRICES)
+        if (model.bones_.Size() > maxBones_)
             allBoneMappings.Push(boneMappings);
         
         startVertexOffset += mesh->mNumVertices;
@@ -966,7 +975,7 @@ void BuildAndSaveModel(OutModel& model)
         }
         
         outModel->SetSkeleton(skeleton);
-        if (model.bones_.Size() > MAX_SKIN_MATRICES)
+        if (model.bones_.Size() > maxBones_)
             outModel->SetGeometryBoneMappings(allBoneMappings);
     }
     
@@ -1892,13 +1901,13 @@ void GetBlendData(OutModel& model, aiMesh* mesh, PODVector<unsigned>& boneMappin
     boneMappings.Clear();
     
     // If model has more bones than can fit vertex shader parameters, write the per-geometry mappings
-    if (model.bones_.Size() > MAX_SKIN_MATRICES)
+    if (model.bones_.Size() > maxBones_)
     {
-        if (mesh->mNumBones > MAX_SKIN_MATRICES)
+        if (mesh->mNumBones > maxBones_)
         {
             ErrorExit(
-                "Geometry (submesh) has over 64 bone influences. Try splitting to more submeshes\n"
-                "that each stay at 64 bones or below."
+                "Geometry (submesh) has over " + String(maxBones_) + " bone influences. Try splitting to more submeshes\n"
+                "that each stay at " + String(maxBones_) + " bones or below."
             );
         }
         boneMappings.Resize(mesh->mNumBones);

+ 13 - 3
Source/Tools/OgreImporter/OgreImporter.cpp

@@ -28,6 +28,7 @@
 #include <Urho3D/Container/HashSet.h>
 #include <Urho3D/Core/ProcessUtils.h>
 #include <Urho3D/Container/Sort.h>
+#include <Urho3D/Core/StringUtils.h>
 #include <Urho3D/Graphics/Tangent.h>
 #include <Urho3D/Resource/XMLFile.h>
 
@@ -55,6 +56,7 @@ Vector<ModelBone> bones_;
 Vector<ModelMorph> morphs_;
 Vector<String> materialNames_;
 BoundingBox boundingBox_;
+unsigned maxBones_ = 64;
 unsigned numSubMeshes_ = 0;
 bool useOneBuffer_ = true;
 
@@ -94,6 +96,7 @@ void Run(const Vector<String>& arguments)
             "-r      Output only rotations from animations\n"
             "-s      Split each submesh into own vertex buffer\n"
             "-t      Generate tangents\n"
+            "-mb <x> Maximum number of bones per submesh, default 64\n"
         );
     }
     
@@ -133,6 +136,13 @@ void Run(const Vector<String>& arguments)
                     }
                     break;
                 }
+                else if (argument == "mb" && i < arguments.Size() - 1)
+                {
+                    maxBones_ = ToUInt(arguments[i + 1]);
+                    if (maxBones_ < 1)
+                        maxBones_ = 1;
+                    ++i;
+                }
             }
         }
     }
@@ -500,7 +510,7 @@ void LoadMesh(const String& inputFileName, bool generateTangents, bool splitSubM
                 bool sorted = false;
                 
                 // If amount of bones is larger than supported by HW skinning, must remap per submesh
-                if (bones_.Size() > MAX_SKIN_MATRICES)
+                if (bones_.Size() > maxBones_)
                 {
                     HashMap<unsigned, unsigned> usedBoneMap;
                     unsigned remapIndex = 0;
@@ -524,8 +534,8 @@ void LoadMesh(const String& inputFileName, bool generateTangents, bool splitSubM
                     }
                     
                     // If still too many bones in one subgeometry, error
-                    if (usedBoneMap.Size() > MAX_SKIN_MATRICES)
-                        ErrorExit("Too many bones in submesh " + String(subMeshIndex + 1));
+                    if (usedBoneMap.Size() > maxBones_)
+                        ErrorExit("Too many bones (limit " + String(maxBones_) + ") in submesh " + String(subMeshIndex + 1));
                     
                     // Write mapping of vertex buffer bone indices to original bone indices
                     subGeometryLodLevel.boneMapping_.Resize(usedBoneMap.Size());

+ 18 - 7
Source/Tools/RampGenerator/RampGenerator.cpp

@@ -93,13 +93,13 @@ void Run(const Vector<String>& arguments)
     
     if (dimensions == 2)
     {
-        SharedArrayPtr<unsigned char> data(new unsigned char[width * width]);
+        SharedArrayPtr<unsigned char> data(new unsigned char[width * width * 3]);
         
         for (int y = 0; y < width; ++y)
         {
             for (int x = 0; x < width; ++x)
             {
-                unsigned i = y * width + x;
+                unsigned i = (y * width + x) * 3;
                 
                 float halfWidth = width * 0.5f;
                 float xf = (x - halfWidth + 0.5f) / (halfWidth - 0.5f);
@@ -109,18 +109,29 @@ void Run(const Vector<String>& arguments)
                     dist = 1.0f;
                 
                 data[i] = (unsigned char)((1.0f - pow(dist, power)) * 255.0f);
+                data[i + 1] = data[i];
+                data[i + 2] = data[i];
             }
         }
         
         // Ensure the border is completely black
         for (int x = 0; x < width; ++x)
         {
-            data[x] = 0;
-            data[(width - 1) * width + x] = 0;
-            data[x * width] = 0;
-            data[x * width + (width - 1)] = 0;
+            data[x * 3] = 0;
+            data[x * 3 + 1] = 0;
+            data[x * 3 + 2] = 0;
+            data[((width - 1) * width + x) * 3] = 0;
+            data[((width - 1) * width + x) * 3 + 1] = 0;
+            data[((width - 1) * width + x) * 3 + 2] = 0;
+            data[x * width * 3] = 0;
+            data[x * width * 3 + 1] = 0;
+            data[x * width * 3 + 2] = 0;
+            data[(x * width + (width - 1)) * 3] = 0;
+            data[(x * width + (width - 1)) * 3 + 1] = 0;
+            data[(x * width + (width - 1)) * 3 + 2] = 0;
         }
         
-        stbi_write_png(arguments[0].CString(), width, width, 1, data.Get(), 0);
+        // Save as RGB to allow Direct3D11 shaders to sample monochrome and color spot textures similarly
+        stbi_write_png(arguments[0].CString(), width, width, 3, data.Get(), 0);
     }
 }

+ 2 - 2
Source/Tools/Urho3DPlayer/Urho3DPlayer.cpp

@@ -100,7 +100,8 @@ void Urho3DPlayer::Setup()
             "-tq <level>  Texture quality level, default 2 (high)\n"
             "-tf <level>  Texture filter mode, default 2 (trilinear)\n"
             "-af <level>  Texture anisotropy level, default 4. Also sets anisotropic filter mode\n"
-            "-flushgpu    Flush GPU command queue each frame. Effective only on Direct3D9\n"
+            "-gl2         Force OpenGL 2 use even if OpenGL 3 is available\n"
+            "-flushgpu    Flush GPU command queue each frame. Effective only on Direct3D\n"
             "-borderless  Borderless window mode\n"
             "-headless    Headless mode. No application window will be created\n"
             "-landscape   Use landscape orientations (iOS only, default)\n"
@@ -114,7 +115,6 @@ void Urho3DPlayer::Setup()
             "-nothreads   Disable worker threads\n"
             "-nosound     Disable sound output\n"
             "-noip        Disable sound mixing interpolation\n"
-            "-sm2         Force SM2.0 rendering\n"
             "-touch       Touch emulation on desktop platform\n"
             #endif
         );

+ 6 - 1
Source/Urho3D/CMakeLists.txt

@@ -127,9 +127,14 @@ foreach (DIR Navigation Network Physics Urho2D)
 endforeach ()
 if (URHO3D_OPENGL)
     # Exclude the opposite source directory
-    list (APPEND EXCLUDED_SOURCE_DIRS Graphics/Direct3D9)
+    list (APPEND EXCLUDED_SOURCE_DIRS Graphics/Direct3D9 Graphics/Direct3D11)
 else ()
     list (APPEND EXCLUDED_SOURCE_DIRS Graphics/OpenGL)
+    if (URHO3D_D3D11)
+        list (APPEND EXCLUDED_SOURCE_DIRS Graphics/Direct3D9)
+    else ()
+        list (APPEND EXCLUDED_SOURCE_DIRS Graphics/Direct3D11)
+    endif ()
 endif ()
 if (APPLE AND NOT IOS)
     set (GLOB_OBJC_PATTERN *.m)     # Should only pick up MacFileWatcher.m for MacOSX platform at the moment

+ 0 - 191
Source/Urho3D/Container/HashTable.h

@@ -1,191 +0,0 @@
-//
-// Copyright (c) 2008-2015 the Urho3D project.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-//
-
-#pragma once
-
-#include "../Container/Allocator.h"
-#include "../Container/Vector.h"
-
-namespace Urho3D
-{
-
-/// Hash table with fixed bucket count. Does not support iteration. Should only be used when performance is critical, as HashMap is much more user-friendly.
-template <class T, unsigned U> class HashTable
-{
-public:
-    /// Hash table node.
-    struct Node
-    {
-        /// Construct.
-        Node(unsigned hash, const T& value, Node* next) :
-            hash_(hash),
-            value_(value),
-            next_(next)
-        {
-        }
-        
-        /// Hash value.
-        unsigned hash_;
-        /// Node value.
-        T value_;
-        /// Next node in bucket.
-        Node* next_;
-    };
-    
-    /// Construct empty.
-    HashTable() :
-        allocator_(0)
-    {
-        for (unsigned i = 0; i < U; ++i)
-            ptrs_[i] = 0;
-    }
-    
-    /// Destruct.
-    ~HashTable()
-    {
-        Clear();
-        AllocatorUninitialize(allocator_);
-    }
-    
-    /// Insert by hash value. If value with same hash already exists, it is replaced.
-    void Insert(unsigned hash, const T& value)
-    {
-        unsigned bucket = hash & (U - 1);
-        
-        if (!allocator_)
-            allocator_ = AllocatorInitialize(sizeof(Node));
-        
-        Node* ptr = ptrs_[bucket];
-        while (ptr)
-        {
-            if (ptr->hash_ == hash)
-            {
-                ptr->value_ = value;
-                return;
-            }
-            ptr = ptr->next_;
-        }
-        
-        Node* newNode = static_cast<Node*>(AllocatorReserve(allocator_));
-        // Insert at the top of the bucket, connect to the previous top node if exists
-        new(newNode) Node(hash, value, ptrs_[bucket]);
-        ptrs_[bucket] = newNode;
-    }
-    
-    /// Remove by hash value. Return true if was found and removed.
-    bool Erase(unsigned hash)
-    {
-        unsigned bucket = hash & (U - 1);
-        
-        Node* ptr = ptrs_[bucket];
-        while (ptr)
-        {
-            if (ptr->hash_ == hash)
-            {
-                (ptr)->~Node();
-                AllocatorFree(allocator_, ptr);
-                return true;
-            }
-            else
-                ptr = ptr->next_;
-        }
-        
-        return false;
-    }
-    
-    /// Remove all values.
-    void Clear()
-    {
-        for (unsigned i = 0; i < U; ++i)
-        {
-            Node* ptr = ptrs_[i];
-            while (ptr)
-            {
-                Node* next = ptr->next_;
-                (ptr)->~Node();
-                AllocatorFree(allocator_, ptr);
-                ptr = next;
-            }
-        }
-    }
-    
-    /// Find by hash value. Return pointer if was found or null if not found.
-    T* Find(unsigned hash) const
-    {
-        unsigned bucket = hash & (U - 1);
-        
-        Node* ptr = ptrs_[bucket];
-        while (ptr)
-        {
-            if (ptr->hash_ == hash)
-                return &ptr->value_;
-            else
-                ptr = ptr->next_;
-        }
-        
-        return 0;
-    }
-    
-    /// Return all the keys.
-    PODVector<unsigned> Keys() const
-    {
-        PODVector<unsigned> ret;
-
-        for (unsigned i = 0; i < U; ++i)
-        {
-            Node* ptr = ptrs_[i];
-            while (ptr)
-            {
-                ret.Push(ptr->hash_);
-                ptr = ptr->next_;
-            }
-        }
-
-        return ret;
-    }
-
-    /// Return pointers to all values.
-    PODVector<T*> Values() const
-    {
-        PODVector<T*> ret;
-        
-        for (unsigned i = 0; i < U; ++i)
-        {
-            Node* ptr = ptrs_[i];
-            while (ptr)
-            {
-                ret.Push(&ptr->value_);
-                ptr = ptr->next_;
-            }
-        }
-        
-        return ret;
-    }
-    
-private:
-    /// Allocator.
-    AllocatorBlock* allocator_;
-    /// Bucket pointers, fixed size.
-    Node* ptrs_[U];
-};
-
-}

+ 2 - 6
Source/Urho3D/Engine/DebugHud.cpp

@@ -142,7 +142,7 @@ void DebugHud::Update()
     if (modeText_->IsVisible())
     {
         String mode;
-        mode.AppendWithFormat("Tex:%s Mat:%s Spec:%s Shadows:%s Size:%i Quality:%s Occlusion:%s Instancing:%s Mode:%s",
+        mode.AppendWithFormat("Tex:%s Mat:%s Spec:%s Shadows:%s Size:%i Quality:%s Occlusion:%s Instancing:%s API:%s",
             qualityTexts[renderer->GetTextureQuality()],
             qualityTexts[renderer->GetMaterialQuality()],
             renderer->GetSpecularLighting() ? "On" : "Off",
@@ -151,11 +151,7 @@ void DebugHud::Update()
             shadowQualityTexts[renderer->GetShadowQuality()],
             renderer->GetMaxOccluderTriangles() > 0 ? "On" : "Off",
             renderer->GetDynamicInstancing() ? "On" : "Off",
-            #ifdef URHO3D_OPENGL
-            "OGL");
-            #else
-            graphics->GetSM3Support() ? "SM3" : "SM2");
-            #endif
+            graphics->GetApiName().CString());
 
         modeText_->SetText(mode);
     }

+ 7 - 3
Source/Urho3D/Engine/Engine.cpp

@@ -314,7 +314,6 @@ bool Engine::Initialize(const VariantMap& parameters)
 
         if (HasParameter(parameters, "ExternalWindow"))
             graphics->SetExternalWindow(GetParameter(parameters, "ExternalWindow").GetVoidPtr());
-        graphics->SetForceSM2(GetParameter(parameters, "ForceSM2", false).GetBool());
         graphics->SetWindowTitle(GetParameter(parameters, "WindowTitle", "Urho3D").GetString());
         graphics->SetWindowIcon(cache->GetResource<Image>(GetParameter(parameters, "WindowIcon", String::EMPTY).GetString()));
         graphics->SetFlushGPU(GetParameter(parameters, "FlushGPU", false).GetBool());
@@ -323,6 +322,11 @@ bool Engine::Initialize(const VariantMap& parameters)
         if (HasParameter(parameters, "WindowPositionX") && HasParameter(parameters, "WindowPositionY"))
             graphics->SetWindowPosition(GetParameter(parameters, "WindowPositionX").GetInt(), GetParameter(parameters, "WindowPositionY").GetInt());
 
+        #ifdef URHO3D_OPENGL
+        if (HasParameter(parameters, "ForceGL2"))
+            graphics->SetForceGL2(GetParameter(parameters, "ForceGL2").GetBool());
+        #endif
+
         if (!graphics->SetMode(
             GetParameter(parameters, "WindowWidth", 0).GetInt(),
             GetParameter(parameters, "WindowHeight", 0).GetInt(),
@@ -728,6 +732,8 @@ VariantMap Engine::ParseParameters(const Vector<String>& arguments)
                 ret["FrameLimiter"] = false;
             else if (argument == "flushgpu")
                 ret["FlushGPU"] = true;
+            else if (argument == "gl2")
+                ret["ForceGL2"] = true;
             else if (argument == "landscape")
                 ret["Orientations"] = "LandscapeLeft LandscapeRight " + ret["Orientations"].GetString();
             else if (argument == "portrait")
@@ -753,8 +759,6 @@ VariantMap Engine::ParseParameters(const Vector<String>& arguments)
                 ret["LowQualityShadows"] = true;
             else if (argument == "nothreads")
                 ret["WorkerThreads"] = false;
-            else if (argument == "sm2")
-                ret["ForceSM2"] = true;
             else if (argument == "v")
                 ret["VSync"] = true;
             else if (argument == "t")

+ 3 - 3
Source/Urho3D/Graphics/AnimatedModel.cpp

@@ -229,13 +229,13 @@ void AnimatedModel::UpdateBatches(const FrameInfo& frame)
 
     // Note: per-geometry distances do not take skinning into account. Especially in case of a ragdoll they may be
     // much off base if the node's own transform is not updated
-    if (batches_.Size() > 1)
+    if (batches_.Size() == 1)
+        batches_[0].distance_ = distance_;
+    else
     {
         for (unsigned i = 0; i < batches_.Size(); ++i)
             batches_[i].distance_ = frame.camera_->GetDistance(worldTransform * geometryData_[i].center_);
     }
-    else if (batches_.Size() == 1)
-        batches_[0].distance_ = distance_;
 
     // Use a transformed version of the model's bounding box instead of world bounding box for LOD scale
     // determination so that animation does not change the scale

+ 206 - 244
Source/Urho3D/Graphics/Batch.cpp

@@ -88,40 +88,39 @@ void CalculateShadowMatrix(Matrix4& dest, LightBatchQueue* queue, unsigned split
     float width = (float)shadowMap->GetWidth();
     float height = (float)shadowMap->GetHeight();
     
-    Vector2 offset(
+    Vector3 offset(
         (float)viewport.left_ / width,
-        (float)viewport.top_ / height
+        (float)viewport.top_ / height,
+        0.0f
     );
     
-    Vector2 scale(
+    Vector3 scale(
         0.5f * (float)viewport.Width() / width,
-        0.5f * (float)viewport.Height() / height
+        0.5f * (float)viewport.Height() / height,
+        1.0f
     );
-    
+
+    // Add pixel-perfect offset if needed by the graphics API
+    const Vector2& pixelUVOffset = Graphics::GetPixelUVOffset();
+    offset.x_ += scale.x_ + pixelUVOffset.x_ / width;
+    offset.y_ += scale.y_ + pixelUVOffset.y_ / height;
+
     #ifdef URHO3D_OPENGL
-    offset.x_ += scale.x_;
-    offset.y_ += scale.y_;
+    offset.z_ = 0.5f;
+    scale.z_ = 0.5f;
     offset.y_ = 1.0f - offset.y_;
-    // If using 4 shadow samples, offset the position diagonally by half pixel
-    if (renderer->GetShadowQuality() & SHADOWQUALITY_HIGH_16BIT)
-    {
-        offset.x_ -= 0.5f / width;
-        offset.y_ -= 0.5f / height;
-    }
-    texAdjust.SetTranslation(Vector3(offset.x_, offset.y_, 0.5f));
-    texAdjust.SetScale(Vector3(scale.x_, scale.y_, 0.5f));
     #else
-    offset.x_ += scale.x_ + 0.5f / width;
-    offset.y_ += scale.y_ + 0.5f / height;
+    scale.y_ = -scale.y_;
+    #endif
+
+    // If using 4 shadow samples, offset the position diagonally by half pixel
     if (renderer->GetShadowQuality() & SHADOWQUALITY_HIGH_16BIT)
     {
         offset.x_ -= 0.5f / width;
         offset.y_ -= 0.5f / height;
     }
-    scale.y_ = -scale.y_;
-    texAdjust.SetTranslation(Vector3(offset.x_, offset.y_, 0.0f));
-    texAdjust.SetScale(Vector3(scale.x_, scale.y_, 1.0f));
-    #endif
+    texAdjust.SetTranslation(offset);
+    texAdjust.SetScale(scale);
     
     dest = texAdjust * shadowProj * shadowView * posAdjust;
 }
@@ -180,11 +179,12 @@ void Batch::Prepare(View* view, bool setModelTransform, bool allowDepthWrite) co
     Light* light = lightQueue_ ? lightQueue_->light_ : 0;
     Texture2D* shadowMap = lightQueue_ ? lightQueue_->shadowMap_ : 0;
 
+    // Set shaders first. The available shader parameters and their register/uniform positions depend on the currently set shaders
+    graphics->SetShaders(vertexShader_, pixelShader_);
+
     // Set pass / material-specific renderstates
     if (pass_ && material_)
     {
-        bool isShadowPass = pass_->GetType() == PASS_SHADOW;
-        
         BlendMode blend = pass_->GetBlendMode();
         // Turn additive blending into subtract if the light is negative
         if (light && light->IsNegative())
@@ -194,43 +194,38 @@ void Batch::Prepare(View* view, bool setModelTransform, bool allowDepthWrite) co
             else if (blend == BLEND_ADDALPHA)
                 blend = BLEND_SUBTRACTALPHA;
         }
-        
         graphics->SetBlendMode(blend);
+
+        bool isShadowPass = pass_->GetIndex() == Technique::shadowPassIndex;
         renderer->SetCullMode(isShadowPass ? material_->GetShadowCullMode() : material_->GetCullMode(), camera_);
         if (!isShadowPass)
         {
             const BiasParameters& depthBias = material_->GetDepthBias();
             graphics->SetDepthBias(depthBias.constantBias_, depthBias.slopeScaledBias_);
         }
+
         graphics->SetDepthTest(pass_->GetDepthTestMode());
         graphics->SetDepthWrite(pass_->GetDepthWrite() && allowDepthWrite);
     }
     
-    // Set shaders first. The available shader parameters and their register/uniform positions depend on the currently set shaders
-    graphics->SetShaders(vertexShader_, pixelShader_);
-    
     // Set global (per-frame) shader parameters
     if (graphics->NeedParameterUpdate(SP_FRAME, (void*)0))
         view->SetGlobalShaderParameters();
     
-    // Set camera shader parameters
+    // Set camera & viewport shader parameters
     unsigned cameraHash = (unsigned)(size_t)camera_;
-    if (graphics->NeedParameterUpdate(SP_CAMERA, reinterpret_cast<void*>(cameraHash)))
-        view->SetCameraShaderParameters(camera_, true);
-    
-    // Set viewport shader parameters
     IntRect viewport = graphics->GetViewport();
     IntVector2 viewSize = IntVector2(viewport.Width(), viewport.Height());
     unsigned viewportHash = viewSize.x_ | (viewSize.y_ << 16);
-    
-    if (graphics->NeedParameterUpdate(SP_VIEWPORT, reinterpret_cast<void*>(viewportHash)))
+    if (graphics->NeedParameterUpdate(SP_CAMERA, reinterpret_cast<const void*>(cameraHash + viewportHash)))
     {
+        view->SetCameraShaderParameters(camera_, true);
         // During renderpath commands the G-Buffer or viewport texture is assumed to always be viewport-sized
         view->SetGBufferShaderParameters(viewSize, IntRect(0, 0, viewSize.x_, viewSize.y_));
     }
     
     // Set model or skinning transforms
-    if (setModelTransform && graphics->NeedParameterUpdate(SP_OBJECTTRANSFORM, worldTransform_))
+    if (setModelTransform && graphics->NeedParameterUpdate(SP_OBJECT, worldTransform_))
     {
         if (geometryType_ == GEOM_SKINNED)
         {
@@ -257,7 +252,7 @@ void Batch::Prepare(View* view, bool setModelTransform, bool allowDepthWrite) co
     unsigned zoneHash = (unsigned)(size_t)zone_;
     if (overrideFogColorToBlack)
         zoneHash += 0x80000000;
-    if (zone_ && graphics->NeedParameterUpdate(SP_ZONE, reinterpret_cast<void*>(zoneHash)))
+    if (zone_ && graphics->NeedParameterUpdate(SP_ZONE, reinterpret_cast<const void*>(zoneHash)))
     {
         graphics->SetShaderParameter(VSP_AMBIENTSTARTCOLOR, zone_->GetAmbientStartColor());
         graphics->SetShaderParameter(VSP_AMBIENTENDCOLOR, zone_->GetAmbientEndColor().ToVector4() - zone_->GetAmbientStartColor().ToVector4());
@@ -295,104 +290,52 @@ void Batch::Prepare(View* view, bool setModelTransform, bool allowDepthWrite) co
     // Set light-related shader parameters
     if (lightQueue_)
     {
-        if (graphics->NeedParameterUpdate(SP_VERTEXLIGHTS, lightQueue_) && graphics->HasShaderParameter(VSP_VERTEXLIGHTS))
+        if (light && graphics->NeedParameterUpdate(SP_LIGHT, lightQueue_))
         {
-            Vector4 vertexLights[MAX_VERTEX_LIGHTS * 3];
-            const PODVector<Light*>& lights = lightQueue_->vertexLights_;
-            
-            for (unsigned i = 0; i < lights.Size(); ++i)
-            {
-                Light* vertexLight = lights[i];
-                Node* vertexLightNode = vertexLight->GetNode();
-                LightType type = vertexLight->GetLightType();
-                
-                // Attenuation
-                float invRange, cutoff, invCutoff;
-                if (type == LIGHT_DIRECTIONAL)
-                    invRange = 0.0f;
-                else
-                    invRange = 1.0f / Max(vertexLight->GetRange(), M_EPSILON);
-                if (type == LIGHT_SPOT)
-                {
-                    cutoff = Cos(vertexLight->GetFov() * 0.5f);
-                    invCutoff = 1.0f / (1.0f - cutoff);
-                }
-                else
-                {
-                    cutoff = -1.0f;
-                    invCutoff = 1.0f;
-                }
-                
-                // Color
-                float fade = 1.0f;
-                float fadeEnd = vertexLight->GetDrawDistance();
-                float fadeStart = vertexLight->GetFadeDistance();
-                
-                // Do fade calculation for light if both fade & draw distance defined
-                if (vertexLight->GetLightType() != LIGHT_DIRECTIONAL && fadeEnd > 0.0f && fadeStart > 0.0f && fadeStart < fadeEnd)
-                    fade = Min(1.0f - (vertexLight->GetDistance() - fadeStart) / (fadeEnd - fadeStart), 1.0f);
-                
-                Color color = vertexLight->GetEffectiveColor() * fade;
-                vertexLights[i * 3] = Vector4(color.r_, color.g_, color.b_, invRange);
-                
-                // Direction
-                vertexLights[i * 3 + 1] = Vector4(-(vertexLightNode->GetWorldDirection()), cutoff);
-                
-                // Position
-                vertexLights[i * 3 + 2] = Vector4(vertexLightNode->GetWorldPosition(), invCutoff);
-            }
-            
-            if (lights.Size())
-                graphics->SetShaderParameter(VSP_VERTEXLIGHTS, vertexLights[0].Data(), lights.Size() * 3 * 4);
-        }
-    }
-    
-    if (light && graphics->NeedParameterUpdate(SP_LIGHT, light))
-    {
-        // Deferred light volume batches operate in a camera-centered space. Detect from material, zone & pass all being null
-        bool isLightVolume = !material_ && !pass_ && !zone_;
-        
-        Matrix3x4 cameraEffectiveTransform = camera_->GetEffectiveWorldTransform();
-        Vector3 cameraEffectivePos = cameraEffectiveTransform.Translation();
+            // Deferred light volume batches operate in a camera-centered space. Detect from material, zone & pass all being null
+            bool isLightVolume = !material_ && !pass_ && !zone_;
 
-        Node* lightNode = light->GetNode();
-        Matrix3 lightWorldRotation = lightNode->GetWorldRotation().RotationMatrix();
-        
-        graphics->SetShaderParameter(VSP_LIGHTDIR, lightWorldRotation * Vector3::BACK);
-        
-        float atten = 1.0f / Max(light->GetRange(), M_EPSILON);
-        graphics->SetShaderParameter(VSP_LIGHTPOS, Vector4(lightNode->GetWorldPosition(), atten));
-        
-        if (graphics->HasShaderParameter(VSP_LIGHTMATRICES))
-        {
-            switch (light->GetLightType())
+            Matrix3x4 cameraEffectiveTransform = camera_->GetEffectiveWorldTransform();
+            Vector3 cameraEffectivePos = cameraEffectiveTransform.Translation();
+
+            Node* lightNode = light->GetNode();
+            Matrix3 lightWorldRotation = lightNode->GetWorldRotation().RotationMatrix();
+
+            graphics->SetShaderParameter(VSP_LIGHTDIR, lightWorldRotation * Vector3::BACK);
+
+            float atten = 1.0f / Max(light->GetRange(), M_EPSILON);
+            graphics->SetShaderParameter(VSP_LIGHTPOS, Vector4(lightNode->GetWorldPosition(), atten));
+
+            if (graphics->HasShaderParameter(VSP_LIGHTMATRICES))
             {
-            case LIGHT_DIRECTIONAL:
+                switch (light->GetLightType())
+                {
+                case LIGHT_DIRECTIONAL:
                 {
                     Matrix4 shadowMatrices[MAX_CASCADE_SPLITS];
                     unsigned numSplits = Min(MAX_CASCADE_SPLITS, (int)lightQueue_->shadowSplits_.Size());
 
                     for (unsigned i = 0; i < numSplits; ++i)
                         CalculateShadowMatrix(shadowMatrices[i], lightQueue_, i, renderer, Vector3::ZERO);
-                    
+
                     graphics->SetShaderParameter(VSP_LIGHTMATRICES, shadowMatrices[0].Data(), 16 * numSplits);
                 }
                 break;
-                
-            case LIGHT_SPOT:
+
+                case LIGHT_SPOT:
                 {
                     Matrix4 shadowMatrices[2];
-                    
+
                     CalculateSpotMatrix(shadowMatrices[0], light, Vector3::ZERO);
                     bool isShadowed = shadowMap && graphics->HasTextureUnit(TU_SHADOWMAP);
                     if (isShadowed)
                         CalculateShadowMatrix(shadowMatrices[1], lightQueue_, 0, renderer, Vector3::ZERO);
-                    
+
                     graphics->SetShaderParameter(VSP_LIGHTMATRICES, shadowMatrices[0].Data(), isShadowed ? 32 : 16);
                 }
                 break;
-                
-            case LIGHT_POINT:
+
+                case LIGHT_POINT:
                 {
                     Matrix4 lightVecRot(lightNode->GetWorldRotation().RotationMatrix());
                     // HLSL compiler will pack the parameters as if the matrix is only 3x4, so must be careful to not overwrite
@@ -404,29 +347,29 @@ void Batch::Prepare(View* view, bool setModelTransform, bool allowDepthWrite) co
                     #endif
                 }
                 break;
+                }
             }
-        }
-        
-        float fade = 1.0f;
-        float fadeEnd = light->GetDrawDistance();
-        float fadeStart = light->GetFadeDistance();
-        
-        // Do fade calculation for light if both fade & draw distance defined
-        if (light->GetLightType() != LIGHT_DIRECTIONAL && fadeEnd > 0.0f && fadeStart > 0.0f && fadeStart < fadeEnd)
-            fade = Min(1.0f - (light->GetDistance() - fadeStart) / (fadeEnd - fadeStart), 1.0f);
-        
-        // Negative lights will use subtract blending, so write absolute RGB values to the shader parameter
-        graphics->SetShaderParameter(PSP_LIGHTCOLOR, Color(light->GetEffectiveColor().Abs(),
-            light->GetEffectiveSpecularIntensity()) * fade);
-        graphics->SetShaderParameter(PSP_LIGHTDIR, lightWorldRotation * Vector3::BACK);
-        graphics->SetShaderParameter(PSP_LIGHTPOS, Vector4((isLightVolume ? (lightNode->GetWorldPosition() -
-            cameraEffectivePos) : lightNode->GetWorldPosition()), atten));
-        
-        if (graphics->HasShaderParameter(PSP_LIGHTMATRICES))
-        {
-            switch (light->GetLightType())
+
+            float fade = 1.0f;
+            float fadeEnd = light->GetDrawDistance();
+            float fadeStart = light->GetFadeDistance();
+
+            // Do fade calculation for light if both fade & draw distance defined
+            if (light->GetLightType() != LIGHT_DIRECTIONAL && fadeEnd > 0.0f && fadeStart > 0.0f && fadeStart < fadeEnd)
+                fade = Min(1.0f - (light->GetDistance() - fadeStart) / (fadeEnd - fadeStart), 1.0f);
+
+            // Negative lights will use subtract blending, so write absolute RGB values to the shader parameter
+            graphics->SetShaderParameter(PSP_LIGHTCOLOR, Color(light->GetEffectiveColor().Abs(),
+                light->GetEffectiveSpecularIntensity()) * fade);
+            graphics->SetShaderParameter(PSP_LIGHTDIR, lightWorldRotation * Vector3::BACK);
+            graphics->SetShaderParameter(PSP_LIGHTPOS, Vector4((isLightVolume ? (lightNode->GetWorldPosition() -
+                cameraEffectivePos) : lightNode->GetWorldPosition()), atten));
+
+            if (graphics->HasShaderParameter(PSP_LIGHTMATRICES))
             {
-            case LIGHT_DIRECTIONAL:
+                switch (light->GetLightType())
+                {
+                case LIGHT_DIRECTIONAL:
                 {
                     Matrix4 shadowMatrices[MAX_CASCADE_SPLITS];
                     unsigned numSplits = Min(MAX_CASCADE_SPLITS, (int)lightQueue_->shadowSplits_.Size());
@@ -439,11 +382,11 @@ void Batch::Prepare(View* view, bool setModelTransform, bool allowDepthWrite) co
                     graphics->SetShaderParameter(PSP_LIGHTMATRICES, shadowMatrices[0].Data(), 16 * numSplits);
                 }
                 break;
-                
-            case LIGHT_SPOT:
+
+                case LIGHT_SPOT:
                 {
                     Matrix4 shadowMatrices[2];
-                    
+
                     CalculateSpotMatrix(shadowMatrices[0], light, cameraEffectivePos);
                     bool isShadowed = lightQueue_->shadowMap_ != 0;
                     if (isShadowed)
@@ -451,12 +394,12 @@ void Batch::Prepare(View* view, bool setModelTransform, bool allowDepthWrite) co
                         CalculateShadowMatrix(shadowMatrices[1], lightQueue_, 0, renderer, isLightVolume ? cameraEffectivePos :
                             Vector3::ZERO);
                     }
-                    
+
                     graphics->SetShaderParameter(PSP_LIGHTMATRICES, shadowMatrices[0].Data(), isShadowed ? 32 : 16);
                 }
                 break;
-                
-            case LIGHT_POINT:
+
+                case LIGHT_POINT:
                 {
                     Matrix4 lightVecRot(lightNode->GetWorldRotation().RotationMatrix());
                     // HLSL compiler will pack the parameters as if the matrix is only 3x4, so must be careful to not overwrite
@@ -468,103 +411,151 @@ void Batch::Prepare(View* view, bool setModelTransform, bool allowDepthWrite) co
                     #endif
                 }
                 break;
+                }
             }
-        }
-        
-        // Set shadow mapping shader parameters
-        if (shadowMap)
-        {
+
+            // Set shadow mapping shader parameters
+            if (shadowMap)
             {
-                // Calculate point light shadow sampling offsets (unrolled cube map)
-                unsigned faceWidth = shadowMap->GetWidth() / 2;
-                unsigned faceHeight = shadowMap->GetHeight() / 3;
-                float width = (float)shadowMap->GetWidth();
-                float height = (float)shadowMap->GetHeight();
-                #ifdef URHO3D_OPENGL
+                {
+                    // Calculate point light shadow sampling offsets (unrolled cube map)
+                    unsigned faceWidth = shadowMap->GetWidth() / 2;
+                    unsigned faceHeight = shadowMap->GetHeight() / 3;
+                    float width = (float)shadowMap->GetWidth();
+                    float height = (float)shadowMap->GetHeight();
+                    #ifdef URHO3D_OPENGL
                     float mulX = (float)(faceWidth - 3) / width;
                     float mulY = (float)(faceHeight - 3) / height;
                     float addX = 1.5f / width;
                     float addY = 1.5f / height;
-                #else
+                    #else
                     float mulX = (float)(faceWidth - 4) / width;
                     float mulY = (float)(faceHeight - 4) / height;
                     float addX = 2.5f / width;
                     float addY = 2.5f / height;
-                #endif
-                // If using 4 shadow samples, offset the position diagonally by half pixel
-                if (renderer->GetShadowQuality() & SHADOWQUALITY_HIGH_16BIT)
+                    #endif
+                    // If using 4 shadow samples, offset the position diagonally by half pixel
+                    if (renderer->GetShadowQuality() & SHADOWQUALITY_HIGH_16BIT)
+                    {
+                        addX -= 0.5f / width;
+                        addY -= 0.5f / height;
+                    }
+                    graphics->SetShaderParameter(PSP_SHADOWCUBEADJUST, Vector4(mulX, mulY, addX, addY));
+                }
+
+                {
+                    // Calculate shadow camera depth parameters for point light shadows and shadow fade parameters for
+                    //  directional light shadows, stored in the same uniform
+                    Camera* shadowCamera = lightQueue_->shadowSplits_[0].shadowCamera_;
+                    float nearClip = shadowCamera->GetNearClip();
+                    float farClip = shadowCamera->GetFarClip();
+                    float q = farClip / (farClip - nearClip);
+                    float r = -q * nearClip;
+
+                    const CascadeParameters& parameters = light->GetShadowCascade();
+                    float viewFarClip = camera_->GetFarClip();
+                    float shadowRange = parameters.GetShadowRange();
+                    float fadeStart = parameters.fadeStart_ * shadowRange / viewFarClip;
+                    float fadeEnd = shadowRange / viewFarClip;
+                    float fadeRange = fadeEnd - fadeStart;
+
+                    graphics->SetShaderParameter(PSP_SHADOWDEPTHFADE, Vector4(q, r, fadeStart, 1.0f / fadeRange));
+                }
+
                 {
-                    addX -= 0.5f / width;
-                    addY -= 0.5f / height;
+                    float intensity = light->GetShadowIntensity();
+                    float fadeStart = light->GetShadowFadeDistance();
+                    float fadeEnd = light->GetShadowDistance();
+                    if (fadeStart > 0.0f && fadeEnd > 0.0f && fadeEnd > fadeStart)
+                        intensity = Lerp(intensity, 1.0f, Clamp((light->GetDistance() - fadeStart) / (fadeEnd - fadeStart), 0.0f, 1.0f));
+                    float pcfValues = (1.0f - intensity);
+                    float samples = renderer->GetShadowQuality() >= SHADOWQUALITY_HIGH_16BIT ? 4.0f : 1.0f;
+
+                    graphics->SetShaderParameter(PSP_SHADOWINTENSITY, Vector4(pcfValues / samples, intensity, 0.0f, 0.0f));
                 }
-                graphics->SetShaderParameter(PSP_SHADOWCUBEADJUST, Vector4(mulX, mulY, addX, addY));
+
+                float sizeX = 1.0f / (float)shadowMap->GetWidth();
+                float sizeY = 1.0f / (float)shadowMap->GetHeight();
+                graphics->SetShaderParameter(PSP_SHADOWMAPINVSIZE, Vector4(sizeX, sizeY, 0.0f, 0.0f));
+
+                Vector4 lightSplits(M_LARGE_VALUE, M_LARGE_VALUE, M_LARGE_VALUE, M_LARGE_VALUE);
+                if (lightQueue_->shadowSplits_.Size() > 1)
+                    lightSplits.x_ = lightQueue_->shadowSplits_[0].farSplit_ / camera_->GetFarClip();
+                if (lightQueue_->shadowSplits_.Size() > 2)
+                    lightSplits.y_ = lightQueue_->shadowSplits_[1].farSplit_ / camera_->GetFarClip();
+                if (lightQueue_->shadowSplits_.Size() > 3)
+                    lightSplits.z_ = lightQueue_->shadowSplits_[2].farSplit_ / camera_->GetFarClip();
+
+                graphics->SetShaderParameter(PSP_SHADOWSPLITS, lightSplits);
             }
+        }
+        else if (lightQueue_->vertexLights_.Size() && graphics->HasShaderParameter(VSP_VERTEXLIGHTS) &&
+            graphics->NeedParameterUpdate(SP_LIGHT, lightQueue_))
+        {
+            Vector4 vertexLights[MAX_VERTEX_LIGHTS * 3];
+            const PODVector<Light*>& lights = lightQueue_->vertexLights_;
             
+            for (unsigned i = 0; i < lights.Size(); ++i)
             {
-                // Calculate shadow camera depth parameters for point light shadows and shadow fade parameters for
-                //  directional light shadows, stored in the same uniform
-                Camera* shadowCamera = lightQueue_->shadowSplits_[0].shadowCamera_;
-                float nearClip = shadowCamera->GetNearClip();
-                float farClip = shadowCamera->GetFarClip();
-                float q = farClip / (farClip - nearClip);
-                float r = -q * nearClip;
+                Light* vertexLight = lights[i];
+                Node* vertexLightNode = vertexLight->GetNode();
+                LightType type = vertexLight->GetLightType();
                 
-                const CascadeParameters& parameters = light->GetShadowCascade();
-                float viewFarClip = camera_->GetFarClip();
-                float shadowRange = parameters.GetShadowRange();
-                float fadeStart = parameters.fadeStart_ * shadowRange / viewFarClip;
-                float fadeEnd = shadowRange / viewFarClip;
-                float fadeRange = fadeEnd - fadeStart;
+                // Attenuation
+                float invRange, cutoff, invCutoff;
+                if (type == LIGHT_DIRECTIONAL)
+                    invRange = 0.0f;
+                else
+                    invRange = 1.0f / Max(vertexLight->GetRange(), M_EPSILON);
+                if (type == LIGHT_SPOT)
+                {
+                    cutoff = Cos(vertexLight->GetFov() * 0.5f);
+                    invCutoff = 1.0f / (1.0f - cutoff);
+                }
+                else
+                {
+                    cutoff = -1.0f;
+                    invCutoff = 1.0f;
+                }
                 
-                graphics->SetShaderParameter(PSP_SHADOWDEPTHFADE, Vector4(q, r, fadeStart, 1.0f / fadeRange));
-            }
-            
-            {
-                float intensity = light->GetShadowIntensity();
-                float fadeStart = light->GetShadowFadeDistance();
-                float fadeEnd = light->GetShadowDistance();
-                if (fadeStart > 0.0f && fadeEnd > 0.0f && fadeEnd > fadeStart)
-                    intensity = Lerp(intensity, 1.0f, Clamp((light->GetDistance() - fadeStart) / (fadeEnd - fadeStart), 0.0f, 1.0f));
-                float pcfValues = (1.0f - intensity);
-                float samples = renderer->GetShadowQuality() >= SHADOWQUALITY_HIGH_16BIT ? 4.0f : 1.0f;
-
-                graphics->SetShaderParameter(PSP_SHADOWINTENSITY, Vector4(pcfValues / samples, intensity, 0.0f, 0.0f));
+                // Color
+                float fade = 1.0f;
+                float fadeEnd = vertexLight->GetDrawDistance();
+                float fadeStart = vertexLight->GetFadeDistance();
+                
+                // Do fade calculation for light if both fade & draw distance defined
+                if (vertexLight->GetLightType() != LIGHT_DIRECTIONAL && fadeEnd > 0.0f && fadeStart > 0.0f && fadeStart < fadeEnd)
+                    fade = Min(1.0f - (vertexLight->GetDistance() - fadeStart) / (fadeEnd - fadeStart), 1.0f);
+                
+                Color color = vertexLight->GetEffectiveColor() * fade;
+                vertexLights[i * 3] = Vector4(color.r_, color.g_, color.b_, invRange);
+                
+                // Direction
+                vertexLights[i * 3 + 1] = Vector4(-(vertexLightNode->GetWorldDirection()), cutoff);
+                
+                // Position
+                vertexLights[i * 3 + 2] = Vector4(vertexLightNode->GetWorldPosition(), invCutoff);
             }
             
-            float sizeX = 1.0f / (float)shadowMap->GetWidth();
-            float sizeY = 1.0f / (float)shadowMap->GetHeight();
-            graphics->SetShaderParameter(PSP_SHADOWMAPINVSIZE, Vector4(sizeX, sizeY, 0.0f, 0.0f));
-            
-            Vector4 lightSplits(M_LARGE_VALUE, M_LARGE_VALUE, M_LARGE_VALUE, M_LARGE_VALUE);
-            if (lightQueue_->shadowSplits_.Size() > 1)
-                lightSplits.x_ = lightQueue_->shadowSplits_[0].farSplit_ / camera_->GetFarClip();
-            if (lightQueue_->shadowSplits_.Size() > 2)
-                lightSplits.y_ = lightQueue_->shadowSplits_[1].farSplit_ / camera_->GetFarClip();
-            if (lightQueue_->shadowSplits_.Size() > 3)
-                lightSplits.z_ = lightQueue_->shadowSplits_[2].farSplit_ / camera_->GetFarClip();
-            
-            graphics->SetShaderParameter(PSP_SHADOWSPLITS, lightSplits);
+            graphics->SetShaderParameter(VSP_VERTEXLIGHTS, vertexLights[0].Data(), lights.Size() * 3 * 4);
         }
     }
-    
+
     // Set material-specific shader parameters and textures
     if (material_)
     {
-        if (graphics->NeedParameterUpdate(SP_MATERIAL, (const void*)material_->GetShaderParameterHash()))
+        if (graphics->NeedParameterUpdate(SP_MATERIAL, reinterpret_cast<const void*>(material_->GetShaderParameterHash())))
         {
             const HashMap<StringHash, MaterialShaderParameter>& parameters = material_->GetShaderParameters();
             for (HashMap<StringHash, MaterialShaderParameter>::ConstIterator i = parameters.Begin(); i != parameters.End(); ++i)
                 graphics->SetShaderParameter(i->first_, i->second_.value_);
         }
         
-        const SharedPtr<Texture>* textures = material_->GetTextures();
-        unsigned numTextures = material_->GetNumUsedTextureUnits();
-
-        for (unsigned i = 0; i < numTextures; ++i)
+        const HashMap<TextureUnit, SharedPtr<Texture> >& textures = material_->GetTextures();
+        for (HashMap<TextureUnit, SharedPtr<Texture> >::ConstIterator i = textures.Begin(); i != textures.End(); ++i)
         {
-            TextureUnit unit = (TextureUnit)i;
-            if (textures[i] && graphics->HasTextureUnit(unit))
-                graphics->SetTexture(i, textures[i]);
+            if (graphics->HasTextureUnit(i->first_))
+                graphics->SetTexture(i->first_, i->second_.Get());
         }
     }
     
@@ -590,8 +581,10 @@ void Batch::Prepare(View* view, bool setModelTransform, bool allowDepthWrite) co
     }
     
     // Set zone texture if necessary
+    #ifdef DESKTOP_GRAPHICS
     if (zone_ && graphics->HasTextureUnit(TU_ZONE))
         graphics->SetTexture(TU_ZONE, zone_->GetZoneTexture());
+    #endif
 }
 
 void Batch::Draw(View* view, bool allowDepthWrite) const
@@ -626,9 +619,9 @@ void BatchGroup::Draw(View* view, bool allowDepthWrite) const
     
     if (instances_.Size() && !geometry_->IsEmpty())
     {
-        // Draw as individual objects if instancing not supported
+        // Draw as individual objects if instancing not supported or could not fill the instancing buffer
         VertexBuffer* instanceBuffer = renderer->GetInstancingBuffer();
-        if (!instanceBuffer || geometryType_ != GEOM_INSTANCED)
+        if (!instanceBuffer || geometryType_ != GEOM_INSTANCED || startIndex_ == M_MAX_UNSIGNED)
         {
             Batch::Prepare(view, false, allowDepthWrite);
             
@@ -637,7 +630,7 @@ void BatchGroup::Draw(View* view, bool allowDepthWrite) const
             
             for (unsigned i = 0; i < instances_.Size(); ++i)
             {
-                if (graphics->NeedParameterUpdate(SP_OBJECTTRANSFORM, instances_[i].worldTransform_))
+                if (graphics->NeedParameterUpdate(SP_OBJECT, instances_[i].worldTransform_))
                     graphics->SetShaderParameter(VSP_MODEL, *instances_[i].worldTransform_);
                 
                 graphics->Draw(geometry_->GetPrimitiveType(), geometry_->GetIndexStart(), geometry_->GetIndexCount(),
@@ -656,41 +649,10 @@ void BatchGroup::Draw(View* view, bool allowDepthWrite) const
             vertexBuffers.Push(SharedPtr<VertexBuffer>(instanceBuffer));
             elementMasks.Push(instanceBuffer->GetElementMask());
             
-            // No stream offset support, instancing buffer not pre-filled with transforms: have to fill now
-            if (startIndex_ == M_MAX_UNSIGNED)
-            {
-                unsigned startIndex = 0;
-                while (startIndex < instances_.Size())
-                {
-                    unsigned instances = instances_.Size() - startIndex;
-                    if (instances > instanceBuffer->GetVertexCount())
-                        instances = instanceBuffer->GetVertexCount();
-                    
-                    // Copy the transforms
-                    Matrix3x4* dest = (Matrix3x4*)instanceBuffer->Lock(0, instances, true);
-                    if (dest)
-                    {
-                        for (unsigned i = 0; i < instances; ++i)
-                            dest[i] = *instances_[i + startIndex].worldTransform_;
-                        instanceBuffer->Unlock();
-                        
-                        graphics->SetIndexBuffer(geometry_->GetIndexBuffer());
-                        graphics->SetVertexBuffers(vertexBuffers, elementMasks);
-                        graphics->DrawInstanced(geometry_->GetPrimitiveType(), geometry_->GetIndexStart(),
-                            geometry_->GetIndexCount(), geometry_->GetVertexStart(), geometry_->GetVertexCount(), instances);
-                    }
-                    
-                    startIndex += instances;
-                }
-            }
-            // Stream offset supported and instancing buffer has been already filled, so just draw
-            else
-            {
-                graphics->SetIndexBuffer(geometry_->GetIndexBuffer());
-                graphics->SetVertexBuffers(vertexBuffers, elementMasks, startIndex_);
-                graphics->DrawInstanced(geometry_->GetPrimitiveType(), geometry_->GetIndexStart(), geometry_->GetIndexCount(),
-                    geometry_->GetVertexStart(), geometry_->GetVertexCount(), instances_.Size());
-            }
+            graphics->SetIndexBuffer(geometry_->GetIndexBuffer());
+            graphics->SetVertexBuffers(vertexBuffers, elementMasks, startIndex_);
+            graphics->DrawInstanced(geometry_->GetPrimitiveType(), geometry_->GetIndexStart(), geometry_->GetIndexCount(),
+                geometry_->GetVertexStart(), geometry_->GetVertexCount(), instances_.Size());
             
             // Remove the instancing buffer & element mask now
             vertexBuffers.Pop();

+ 2 - 0
Source/Urho3D/Graphics/Batch.h

@@ -271,6 +271,8 @@ struct LightBatchQueue
 {
     /// Per-pixel light.
     Light* light_;
+    /// Light negative flag.
+    bool negative_;
     /// Shadow map depth texture.
     Texture2D* shadowMap_;
     /// Lit geometry draw calls, base (replace blend mode)

+ 29 - 0
Source/Urho3D/Graphics/ConstantBuffer.h

@@ -0,0 +1,29 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#if defined(URHO3D_OPENGL)
+#include "OpenGL/OGLConstantBuffer.h"
+#elif defined(URHO3D_D3D11)
+#include "Direct3D11/D3D11ConstantBuffer.h"
+#endif

+ 0 - 1
Source/Urho3D/Graphics/DebugRenderer.cpp

@@ -405,7 +405,6 @@ void DebugRenderer::Render()
     graphics->SetColorWrite(true);
     graphics->SetCullMode(CULL_NONE);
     graphics->SetDepthWrite(true);
-    graphics->SetDrawAntialiased(true);
     graphics->SetScissorTest(false);
     graphics->SetStencilTest(false);
     graphics->SetShaders(vs, ps);

+ 1 - 1
Source/Urho3D/Graphics/DecalSet.cpp

@@ -913,7 +913,7 @@ bool DecalSet::GetBones(Drawable* target, unsigned batchIndex, const float* blen
 
             if (!found)
             {
-                if (bones_.Size() >= MAX_SKIN_MATRICES)
+                if (bones_.Size() >= Graphics::GetMaxBones())
                 {
                     LOGWARNING("Maximum skinned decal bone count reached");
                     return false;

+ 138 - 0
Source/Urho3D/Graphics/Direct3D11/D3D11ConstantBuffer.cpp

@@ -0,0 +1,138 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../../Graphics/Graphics.h"
+#include "../../Graphics/GraphicsImpl.h"
+#include "../../IO/Log.h"
+#include "../../Graphics/ConstantBuffer.h"
+
+#include "../../DebugNew.h"
+
+namespace Urho3D
+{
+
+
+ConstantBuffer::ConstantBuffer(Context* context) :
+    Object(context),
+    GPUObject(GetSubsystem<Graphics>())
+{
+}
+
+ConstantBuffer::~ConstantBuffer()
+{
+    Release();
+}
+
+void ConstantBuffer::Release()
+{
+    if (object_)
+    {
+        if (!graphics_)
+            return;
+
+        ((ID3D11Buffer*)object_)->Release();
+        object_ = 0;
+    }
+
+    shadowData_.Reset();
+    size_ = 0;
+}
+
+bool ConstantBuffer::SetSize(unsigned size)
+{
+    Release();
+
+    if (!size)
+    {
+        LOGERROR("Can not create zero-sized constant buffer");
+        return false;
+    }
+
+    // Round up to next 16 bytes
+    size += 15;
+    size &= 0xfffffff0;
+
+    size_ = size;
+    dirty_ = false;
+    shadowData_ = new unsigned char[size_];
+    memset(shadowData_.Get(), 0, size_);
+
+    if (graphics_)
+    {
+        D3D11_BUFFER_DESC bufferDesc;
+        memset(&bufferDesc, 0, sizeof bufferDesc);
+
+        bufferDesc.ByteWidth = size_;
+        bufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
+        bufferDesc.CPUAccessFlags = 0;
+        bufferDesc.Usage = D3D11_USAGE_DEFAULT;
+
+        graphics_->GetImpl()->GetDevice()->CreateBuffer(&bufferDesc, 0, (ID3D11Buffer**)&object_);
+
+        if (!object_)
+        {
+            LOGERROR("Failed to create constant buffer");
+            return false;
+        }
+    }
+
+    return true;
+}
+
+void ConstantBuffer::SetParameter(unsigned offset, unsigned size, const void* data)
+{
+    if (offset + size > size_)
+        return; // Would overflow the buffer
+
+    memcpy(&shadowData_[offset], data, size);
+    dirty_ = true;
+}
+
+void ConstantBuffer::SetVector3ArrayParameter(unsigned offset, unsigned rows, const void* data)
+{
+    if (offset + rows * 4 * sizeof(float) > size_)
+        return; // Would overflow the buffer
+
+    float* dest = (float*)&shadowData_[offset];
+    const float* src = (const float*)data;
+
+    while (rows--)
+    {
+        *dest++ = *src++;
+        *dest++ = *src++;
+        *dest++ = *src++;
+        ++dest; // Skip over the w coordinate
+    }
+
+    dirty_ = true;
+}
+
+void ConstantBuffer::Apply()
+{
+    if (dirty_ && object_)
+    {
+        graphics_->GetImpl()->GetDeviceContext()->UpdateSubresource((ID3D11Buffer*)object_, 0, 0, shadowData_.Get(), 0, 0);
+        dirty_ = false;
+    }
+}
+
+}

+ 73 - 0
Source/Urho3D/Graphics/Direct3D11/D3D11ConstantBuffer.h

@@ -0,0 +1,73 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../../Graphics/GPUObject.h"
+#include "../../Graphics/GraphicsDefs.h"
+#include "../../Container/ArrayPtr.h"
+#include "../../Core/Object.h"
+
+namespace Urho3D
+{
+
+/// Hardware constant buffer.
+class URHO3D_API ConstantBuffer : public Object, public GPUObject
+{
+    OBJECT(ConstantBuffer);
+    
+public:
+    /// Construct.
+    ConstantBuffer(Context* context);
+    /// Destruct.
+    virtual ~ConstantBuffer();
+    
+    /// Release buffer.
+    virtual void Release();
+    
+    /// Set size and create GPU-side buffer. Return true on success.
+    bool SetSize(unsigned size);
+    /// Set a generic parameter and mark buffer dirty.
+    void SetParameter(unsigned offset, unsigned size, const void* data);
+    /// Set a Vector3 array parameter and mark buffer dirty.
+    void SetVector3ArrayParameter(unsigned offset, unsigned rows, const void* data);
+    /// Apply to GPU.
+    void Apply();
+
+    /// Return size.
+    unsigned GetSize() const { return size_; }
+    /// Return whether has unapplied data.
+    bool IsDirty() const { return dirty_; }
+
+private:
+    /// Create buffer.
+    bool Create();
+
+    /// Shadow data.
+    SharedArrayPtr<unsigned char> shadowData_;
+    /// Buffer byte size.
+    unsigned size_;
+    /// Dirty flag.
+    bool dirty_;
+};
+
+}

+ 51 - 0
Source/Urho3D/Graphics/Direct3D11/D3D11GPUObject.cpp

@@ -0,0 +1,51 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../../Graphics/Graphics.h"
+#include "../../Graphics/GraphicsImpl.h"
+#include "../../Graphics/GPUObject.h"
+
+#include "../../DebugNew.h"
+
+namespace Urho3D
+{
+
+GPUObject::GPUObject(Graphics* graphics) :
+    graphics_(graphics),
+    object_(0)
+{
+    if (graphics_)
+        graphics->AddGPUObject(this);
+}
+
+GPUObject::~GPUObject()
+{
+    if (graphics_)
+        graphics_->RemoveGPUObject(this);
+}
+
+Graphics* GPUObject::GetGraphics() const
+{
+    return graphics_;
+}
+
+}

+ 63 - 0
Source/Urho3D/Graphics/Direct3D11/D3D11GPUObject.h

@@ -0,0 +1,63 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../../Container/Ptr.h"
+
+namespace Urho3D
+{
+
+class Graphics;
+
+/// Base class for GPU resources.
+class URHO3D_API GPUObject
+{
+public:
+    /// Construct with graphics subsystem pointer.
+    GPUObject(Graphics* graphics);
+    /// Destruct. Remove from the graphics subsystem.
+    virtual ~GPUObject();
+    
+    /// Unconditionally release the GPU resource.
+    virtual void Release() {}
+    
+    /// Clear the data lost flag. No-op on D3D11.
+    void ClearDataLost() {}
+    
+    /// Return the graphics subsystem.
+    Graphics* GetGraphics() const;
+    /// Return Direct3D object.
+    void* GetGPUObject() const { return object_; }
+    /// Return whether data is lost due to device loss. Always false on D3D11.
+    bool IsDataLost() const { return false; }
+    /// Return whether has pending data assigned while device was lost. Always false on D3D11.
+    bool HasPendingData() const { return false; }
+    
+protected:
+    /// Graphics subsystem.
+    WeakPtr<Graphics> graphics_;
+    /// Direct3D object.
+    void* object_;
+};
+
+}

+ 2729 - 0
Source/Urho3D/Graphics/Direct3D11/D3D11Graphics.cpp

@@ -0,0 +1,2729 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../../Graphics/AnimatedModel.h"
+#include "../../Graphics/Animation.h"
+#include "../../Graphics/AnimationController.h"
+#include "../../Graphics/Camera.h"
+#include "../../Graphics/ConstantBuffer.h"
+#include "../../Core/Context.h"
+#include "../../Graphics/CustomGeometry.h"
+#include "../../Graphics/DebugRenderer.h"
+#include "../../Graphics/DecalSet.h"
+#include "../../IO/File.h"
+#include "../../Graphics/Geometry.h"
+#include "../../Graphics/Graphics.h"
+#include "../../Graphics/GraphicsEvents.h"
+#include "../../Graphics/GraphicsImpl.h"
+#include "../../Graphics/IndexBuffer.h"
+#include "../../IO/Log.h"
+#include "../../Graphics/Material.h"
+#include "../../Graphics/Octree.h"
+#include "../../Graphics/ParticleEffect.h"
+#include "../../Graphics/ParticleEmitter.h"
+#include "../../Core/ProcessUtils.h"
+#include "../../Core/Profiler.h"
+#include "../../Graphics/Renderer.h"
+#include "../../Resource/ResourceCache.h"
+#include "../../Graphics/Shader.h"
+#include "../../Graphics/ShaderPrecache.h"
+#include "../../Graphics/ShaderProgram.h"
+#include "../../Graphics/ShaderVariation.h"
+#include "../../Graphics/Skybox.h"
+#include "../../Graphics/StaticModelGroup.h"
+#include "../../Graphics/Technique.h"
+#include "../../Graphics/Terrain.h"
+#include "../../Graphics/TerrainPatch.h"
+#include "../../Graphics/Texture2D.h"
+#include "../../Graphics/Texture3D.h"
+#include "../../Graphics/TextureCube.h"
+#include "../../Core/Timer.h"
+#include "../../Graphics/VertexBuffer.h"
+#include "../../Graphics/VertexDeclaration.h"
+#include "../../Graphics/Zone.h"
+
+#include <SDL/SDL_syswm.h>
+
+#include "../../DebugNew.h"
+
+#ifdef _MSC_VER
+#pragma warning(disable:4355)
+#endif
+
+// On Intel / NVIDIA setups prefer the NVIDIA GPU
+extern "C" {
+    __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
+}
+
+namespace Urho3D
+{
+
+static const D3D11_COMPARISON_FUNC d3dCmpFunc[] =
+{
+    D3D11_COMPARISON_ALWAYS,
+    D3D11_COMPARISON_EQUAL,
+    D3D11_COMPARISON_NOT_EQUAL,
+    D3D11_COMPARISON_LESS,
+    D3D11_COMPARISON_LESS_EQUAL,
+    D3D11_COMPARISON_GREATER,
+    D3D11_COMPARISON_GREATER_EQUAL
+};
+
+static const DWORD d3dBlendEnable[] =
+{
+    FALSE,
+    TRUE,
+    TRUE,
+    TRUE,
+    TRUE,
+    TRUE,
+    TRUE,
+    TRUE
+};
+
+static const D3D11_BLEND d3dSrcBlend[] =
+{
+    D3D11_BLEND_ONE,
+    D3D11_BLEND_ONE,
+    D3D11_BLEND_DEST_COLOR,
+    D3D11_BLEND_SRC_ALPHA,
+    D3D11_BLEND_SRC_ALPHA,
+    D3D11_BLEND_ONE,
+    D3D11_BLEND_INV_DEST_ALPHA,
+    D3D11_BLEND_ONE,
+    D3D11_BLEND_SRC_ALPHA,
+};
+
+static const D3D11_BLEND d3dDestBlend[] =
+{
+    D3D11_BLEND_ZERO,
+    D3D11_BLEND_ONE,
+    D3D11_BLEND_ZERO,
+    D3D11_BLEND_INV_SRC_ALPHA,
+    D3D11_BLEND_ONE,
+    D3D11_BLEND_INV_SRC_ALPHA,
+    D3D11_BLEND_DEST_ALPHA,
+    D3D11_BLEND_ONE,
+    D3D11_BLEND_ONE
+};
+
+static const D3D11_BLEND_OP d3dBlendOp[] =
+{
+    D3D11_BLEND_OP_ADD,
+    D3D11_BLEND_OP_ADD,
+    D3D11_BLEND_OP_ADD,
+    D3D11_BLEND_OP_ADD,
+    D3D11_BLEND_OP_ADD,
+    D3D11_BLEND_OP_ADD,
+    D3D11_BLEND_OP_ADD,
+    D3D11_BLEND_OP_REV_SUBTRACT,
+    D3D11_BLEND_OP_REV_SUBTRACT
+};
+
+static const D3D11_STENCIL_OP d3dStencilOp[] =
+{
+    D3D11_STENCIL_OP_KEEP,
+    D3D11_STENCIL_OP_ZERO,
+    D3D11_STENCIL_OP_REPLACE,
+    D3D11_STENCIL_OP_INCR,
+    D3D11_STENCIL_OP_DECR
+};
+
+static const D3D11_CULL_MODE d3dCullMode[] =
+{
+    D3D11_CULL_NONE,
+    D3D11_CULL_BACK,
+    D3D11_CULL_FRONT
+};
+
+static const D3D11_FILL_MODE d3dFillMode[] =
+{
+    D3D11_FILL_SOLID,
+    D3D11_FILL_WIREFRAME,
+    D3D11_FILL_WIREFRAME // Point mode not supported
+};
+
+static unsigned GetD3DColor(const Color& color)
+{
+    unsigned r = (unsigned)(Clamp(color.r_ * 255.0f, 0.0f, 255.0f));
+    unsigned g = (unsigned)(Clamp(color.g_ * 255.0f, 0.0f, 255.0f));
+    unsigned b = (unsigned)(Clamp(color.b_ * 255.0f, 0.0f, 255.0f));
+    unsigned a = (unsigned)(Clamp(color.a_ * 255.0f, 0.0f, 255.0f));
+    return (((a) & 0xff) << 24) | (((r) & 0xff) << 16) | (((g) & 0xff) << 8) | ((b) & 0xff);
+}
+
+static void GetD3DPrimitiveType(unsigned elementCount, PrimitiveType type, unsigned& primitiveCount, D3D_PRIMITIVE_TOPOLOGY& d3dPrimitiveType)
+{
+    switch (type)
+    {
+    case TRIANGLE_LIST:
+        primitiveCount = elementCount / 3;
+        d3dPrimitiveType = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
+        break;
+        
+    case LINE_LIST:
+        primitiveCount = elementCount / 2;
+        d3dPrimitiveType = D3D_PRIMITIVE_TOPOLOGY_LINELIST;
+        break;
+
+    case POINT_LIST:
+        primitiveCount = elementCount;
+        d3dPrimitiveType = D3D_PRIMITIVE_TOPOLOGY_POINTLIST;
+        break;
+        
+    case TRIANGLE_STRIP:
+        primitiveCount = elementCount - 2;
+        d3dPrimitiveType = D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
+        break;
+        
+    case LINE_STRIP:
+        primitiveCount = elementCount - 1;
+        d3dPrimitiveType = D3D_PRIMITIVE_TOPOLOGY_LINESTRIP;
+        break;
+        
+    case TRIANGLE_FAN:
+        // Triangle fan is not supported on D3D11
+        primitiveCount = 0;
+        d3dPrimitiveType = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED;
+        break;
+    }
+}
+
+static HWND GetWindowHandle(SDL_Window* window)
+{
+    SDL_SysWMinfo sysInfo;
+    
+    SDL_VERSION(&sysInfo.version);
+    SDL_GetWindowWMInfo(window, &sysInfo);
+    return sysInfo.info.win.window;
+}
+
+static unsigned readableDepthFormat = 0;
+
+const Vector2 Graphics::pixelUVOffset(0.0f, 0.0f);
+
+Graphics::Graphics(Context* context) :
+    Object(context),
+    impl_(new GraphicsImpl()),
+    windowIcon_(0),
+    externalWindow_(0),
+    width_(0),
+    height_(0),
+    position_(SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED),
+    multiSample_(1),
+    fullscreen_(false),
+    borderless_(false),
+    resizable_(false),
+    vsync_(false),
+    tripleBuffer_(false),
+    flushGPU_(false),
+    sRGB_(false),
+    lightPrepassSupport_(false),
+    deferredSupport_(false),
+    instancingSupport_(false),
+    sRGBSupport_(false),
+    sRGBWriteSupport_(false),
+    numPrimitives_(0),
+    numBatches_(0),
+    maxScratchBufferRequest_(0),
+    defaultTextureFilterMode_(FILTER_TRILINEAR),
+    shaderProgram_(0),
+    shaderPath_("Shaders/HLSL/"),
+    shaderExtension_(".hlsl"),
+    orientations_("LandscapeLeft LandscapeRight"),
+    apiName_("D3D11")
+{
+    SetTextureUnitMappings();
+    ResetCachedState();
+    
+    // Initialize SDL now. Graphics should be the first SDL-using subsystem to be created
+    SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER | SDL_INIT_NOPARACHUTE);
+    
+    // Register Graphics library object factories
+    RegisterGraphicsLibrary(context_);
+}
+
+Graphics::~Graphics()
+{
+    {
+        MutexLock lock(gpuObjectMutex_);
+
+        // Release all GPU objects that still exist
+        for (PODVector<GPUObject*>::Iterator i = gpuObjects_.Begin(); i != gpuObjects_.End(); ++i)
+            (*i)->Release();
+        gpuObjects_.Clear();
+    }
+
+    vertexDeclarations_.Clear();
+    constantBuffers_.Clear();
+    
+    for (HashMap<unsigned, ID3D11BlendState*>::Iterator i = impl_->blendStates_.Begin(); i != impl_->blendStates_.End(); ++i)
+    {
+        if (i->second_)
+            i->second_->Release();
+    }
+    impl_->blendStates_.Clear();
+    
+    for (HashMap<unsigned, ID3D11DepthStencilState*>::Iterator i = impl_->depthStates_.Begin(); i != impl_->depthStates_.End(); ++i)
+    {
+        if (i->second_)
+            i->second_->Release();
+    }
+    impl_->depthStates_.Clear();
+
+    for (HashMap<unsigned, ID3D11RasterizerState*>::Iterator i = impl_->rasterizerStates_.Begin(); i != impl_->rasterizerStates_.End(); ++i)
+    {
+        if (i->second_)
+            i->second_->Release();
+    }
+    impl_->rasterizerStates_.Clear();
+
+    if (impl_->defaultRenderTargetView_)
+    {
+        impl_->defaultRenderTargetView_->Release();
+        impl_->defaultRenderTargetView_ = 0;
+    }
+    if (impl_->defaultDepthStencilView_)
+    {
+        impl_->defaultDepthStencilView_->Release();
+        impl_->defaultDepthStencilView_ = 0;
+    }
+    if (impl_->defaultDepthTexture_)
+    {
+        impl_->defaultDepthTexture_->Release();
+        impl_->defaultDepthTexture_ = 0;
+    }
+    if (impl_->swapChain_)
+    {
+        impl_->swapChain_->Release();
+        impl_->swapChain_ = 0;
+    }
+    if (impl_->deviceContext_)
+    {
+        impl_->deviceContext_->Release();
+        impl_->deviceContext_ = 0;
+    }
+    if (impl_->device_)
+    {
+        impl_->device_->Release();
+        impl_->device_ = 0;
+    }
+    if (impl_->window_)
+    {
+        SDL_ShowCursor(SDL_TRUE);
+        SDL_DestroyWindow(impl_->window_);
+        impl_->window_ = 0;
+    }
+    
+    delete impl_;
+    impl_ = 0;
+    
+    // Shut down SDL now. Graphics should be the last SDL-using subsystem to be destroyed
+    SDL_Quit();
+}
+
+void Graphics::SetExternalWindow(void* window)
+{
+    if (!impl_->window_)
+        externalWindow_ = window;
+    else
+        LOGERROR("Window already opened, can not set external window");
+}
+
+void Graphics::SetWindowTitle(const String& windowTitle)
+{
+    windowTitle_ = windowTitle;
+    if (impl_->window_)
+        SDL_SetWindowTitle(impl_->window_, windowTitle_.CString());
+}
+
+void Graphics::SetWindowIcon(Image* windowIcon)
+{
+    windowIcon_ = windowIcon;
+    if (impl_->window_)
+        CreateWindowIcon();
+}
+
+void Graphics::SetWindowPosition(const IntVector2& position)
+{
+    if (impl_->window_)
+        SDL_SetWindowPosition(impl_->window_, position.x_, position.y_);
+    else
+        position_ = position; // Sets as initial position for OpenWindow()
+}
+
+void Graphics::SetWindowPosition(int x, int y)
+{
+    SetWindowPosition(IntVector2(x, y));
+}
+
+bool Graphics::SetMode(int width, int height, bool fullscreen, bool borderless, bool resizable, bool vsync, bool tripleBuffer, int multiSample)
+{
+    PROFILE(SetScreenMode);
+
+    bool maximize = false;
+    
+    // Find out the full screen mode display format (match desktop color depth)
+    SDL_DisplayMode mode;
+    SDL_GetDesktopDisplayMode(0, &mode);
+    DXGI_FORMAT fullscreenFormat = SDL_BITSPERPIXEL(mode.format) == 16 ? DXGI_FORMAT_B5G6R5_UNORM : DXGI_FORMAT_R8G8B8A8_UNORM;
+    
+    // If zero dimensions in windowed mode, set windowed mode to maximize and set a predefined default restored window size. If zero in fullscreen, use desktop mode
+    if (!width || !height)
+    {
+        if (fullscreen || borderless)
+        {
+            width = mode.w;
+            height = mode.h;
+        }
+        else
+        {
+            maximize = resizable;
+            width = 1024;
+            height = 768;
+        }
+    }
+    
+    // Fullscreen or Borderless can not be resizable
+    if (fullscreen || borderless)
+        resizable = false;
+
+    // Borderless cannot be fullscreen, they are mutually exclusive
+    if (borderless)
+        fullscreen = false;
+    
+    // If nothing changes, do not reset the device
+    if (width == width_ && height == height_ && fullscreen == fullscreen_ && borderless == borderless_ && resizable == resizable_ &&
+        vsync == vsync_ && tripleBuffer == tripleBuffer_ && multiSample == multiSample_)
+        return true;
+    
+    SDL_SetHint(SDL_HINT_ORIENTATIONS, orientations_.CString());
+
+    if (!impl_->window_)
+    {
+        if (!OpenWindow(width, height, resizable, borderless))
+            return false;
+    }
+    
+    // Check fullscreen mode validity. Use a closest match if not found
+    if (fullscreen)
+    {
+        PODVector<IntVector2> resolutions = GetResolutions();
+        if (resolutions.Empty())
+            fullscreen = false;
+        else
+        {
+            unsigned best = 0;
+            unsigned bestError = M_MAX_UNSIGNED;
+
+            for (unsigned i = 0; i < resolutions.Size(); ++i)
+            {
+                unsigned error = Abs(resolutions[i].x_ - width) + Abs(resolutions[i].y_ - height);
+                if (error < bestError)
+                {
+                    best = i;
+                    bestError = error;
+                }
+            }
+            
+            width = resolutions[best].x_;
+            height = resolutions[best].y_;
+        }
+    }
+    
+    AdjustWindow(width, height, fullscreen, borderless);
+
+    if (maximize)
+    {
+        Maximize();
+        SDL_GetWindowSize(impl_->window_, &width, &height);
+    }
+    
+    if (!impl_->device_ || multiSample_ != multiSample)
+        CreateDevice(width, height, multiSample);
+    UpdateSwapChain(width, height);
+
+    fullscreen_ = fullscreen;
+    borderless_ = borderless;
+    resizable_ = resizable;
+    vsync_ = vsync;
+    tripleBuffer_ = tripleBuffer;
+    
+    // Clear the initial window contents to black
+    Clear(CLEAR_COLOR);
+    impl_->swapChain_->Present(0, 0);
+    
+    #ifdef URHO3D_LOGGING
+    String msg;
+    msg.AppendWithFormat("Set screen mode %dx%d %s", width_, height_, (fullscreen_ ? "fullscreen" : "windowed"));
+    if (borderless_)
+        msg.Append(" borderless");
+    if (resizable_)
+        msg.Append(" resizable");
+    if (multiSample > 1)
+        msg.AppendWithFormat(" multisample %d", multiSample);
+    LOGINFO(msg);
+    #endif
+
+    using namespace ScreenMode;
+    
+    VariantMap& eventData = GetEventDataMap();
+    eventData[P_WIDTH] = width_;
+    eventData[P_HEIGHT] = height_;
+    eventData[P_FULLSCREEN] = fullscreen_;
+    eventData[P_RESIZABLE] = resizable_;
+    eventData[P_BORDERLESS] = borderless_;
+    SendEvent(E_SCREENMODE, eventData);
+    
+    return true;
+}
+
+bool Graphics::SetMode(int width, int height)
+{
+    return SetMode(width, height, fullscreen_, borderless_, resizable_, vsync_, tripleBuffer_, multiSample_);
+}
+
+void Graphics::SetSRGB(bool enable)
+{
+    bool newEnable = enable && sRGBWriteSupport_;
+    if (newEnable != sRGB_)
+    {
+        sRGB_ = newEnable;
+        if (impl_->swapChain_)
+        {
+            // Recreate swap chain for the new backbuffer format
+            CreateDevice(width_, height_, multiSample_);
+            UpdateSwapChain(width_, height_);
+        }
+    }
+}
+
+void Graphics::SetFlushGPU(bool enable)
+{
+    flushGPU_ = enable;
+    
+    if (impl_->device_)
+    {
+        IDXGIDevice1* dxgiDevice;
+        impl_->device_->QueryInterface(IID_IDXGIDevice1, (void **)&dxgiDevice);
+        if (dxgiDevice)
+        {
+            dxgiDevice->SetMaximumFrameLatency(enable ? 1 : 3);
+            dxgiDevice->Release();
+        }
+    }
+}
+
+void Graphics::SetOrientations(const String& orientations)
+{
+    orientations_ = orientations.Trimmed();
+    SDL_SetHint(SDL_HINT_ORIENTATIONS, orientations_.CString());
+}
+
+bool Graphics::ToggleFullscreen()
+{
+    return SetMode(width_, height_, !fullscreen_, borderless_, resizable_, vsync_, tripleBuffer_, multiSample_);
+}
+
+void Graphics::Close()
+{
+    if (impl_->window_)
+    {
+        SDL_ShowCursor(SDL_TRUE);
+        SDL_DestroyWindow(impl_->window_);
+        impl_->window_ = 0;
+    }
+}
+
+bool Graphics::TakeScreenShot(Image& destImage)
+{
+    PROFILE(TakeScreenShot);
+
+    if (!impl_->device_)
+        return false;
+
+    D3D11_TEXTURE2D_DESC textureDesc;
+    memset(&textureDesc, 0, sizeof textureDesc);
+    textureDesc.Width = width_;
+    textureDesc.Height = height_;
+    textureDesc.MipLevels = 1;
+    textureDesc.ArraySize = 1;
+    textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+    textureDesc.SampleDesc.Count = 1;
+    textureDesc.SampleDesc.Quality = 0;
+    textureDesc.Usage = D3D11_USAGE_STAGING;
+    textureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+
+    ID3D11Texture2D* stagingTexture = 0;
+    impl_->device_->CreateTexture2D(&textureDesc, 0, &stagingTexture);
+    if (!stagingTexture)
+    {
+        LOGERROR("Could not create staging texture for screenshot");
+        return false;
+    }
+
+    ID3D11Resource* source = 0;
+    impl_->defaultRenderTargetView_->GetResource(&source);
+
+    if (multiSample_ > 1)
+    {
+        // If backbuffer is multisampled, need another DEFAULT usage texture to resolve the data to first
+        textureDesc.Usage = D3D11_USAGE_DEFAULT;
+        textureDesc.CPUAccessFlags = 0;
+        ID3D11Texture2D* resolveTexture = 0;
+
+        impl_->device_->CreateTexture2D(&textureDesc, 0, &resolveTexture);
+        if (!resolveTexture)
+        {
+            LOGERROR("Could not create intermediate texture for multisampled screenshot");
+            stagingTexture->Release();
+            return false;
+        }
+
+        impl_->deviceContext_->ResolveSubresource(resolveTexture, 0, source, 0, DXGI_FORMAT_R8G8B8A8_UNORM);
+        impl_->deviceContext_->CopyResource(stagingTexture, resolveTexture);
+        resolveTexture->Release();
+    }
+    else
+        impl_->deviceContext_->CopyResource(stagingTexture, source);
+
+    source->Release();
+
+    D3D11_MAPPED_SUBRESOURCE mappedData;
+    mappedData.pData = 0;
+    impl_->deviceContext_->Map(stagingTexture, 0, D3D11_MAP_READ, 0, &mappedData);
+
+    destImage.SetSize(width_, height_, 3);
+    unsigned char* destData = destImage.GetData();
+    if (mappedData.pData)
+    {
+        for (int y = 0; y < height_; ++y)
+        {
+            unsigned char* src = (unsigned char*)mappedData.pData + y * mappedData.RowPitch;
+            for (int x = 0; x < width_; ++x)
+            {
+                *destData++ = *src++;
+                *destData++ = *src++;
+                *destData++ = *src++;
+                ++src;
+            }
+        }
+        impl_->deviceContext_->Unmap(stagingTexture, 0);
+        stagingTexture->Release();
+        return true;
+    }
+    else
+    {
+        LOGERROR("Could not map staging texture for screenshot");
+        stagingTexture->Release();
+        return false;
+    }
+}
+
+bool Graphics::BeginFrame()
+{
+    if (!IsInitialized())
+        return false;
+    
+    // If using an external window, check it for size changes, and reset screen mode if necessary
+    if (externalWindow_)
+    {
+        int width, height;
+        
+        SDL_GetWindowSize(impl_->window_, &width, &height);
+        if (width != width_ || height != height_)
+            SetMode(width, height);
+    }
+    else
+    {
+        // To prevent a loop of endless device loss and flicker, do not attempt to render when in fullscreen
+        // and the window is minimized
+        if (fullscreen_ && (SDL_GetWindowFlags(impl_->window_) & SDL_WINDOW_MINIMIZED))
+            return false;
+    }
+    
+    // Set default rendertarget and depth buffer
+    ResetRenderTargets();
+    
+    // Cleanup textures from previous frame
+    for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
+        SetTexture(i, 0);
+    
+    numPrimitives_ = 0;
+    numBatches_ = 0;
+    
+    SendEvent(E_BEGINRENDERING);
+    
+    return true;
+}
+
+void Graphics::EndFrame()
+{
+    if (!IsInitialized())
+        return;
+    
+    {
+        PROFILE(Present);
+        
+        SendEvent(E_ENDRENDERING);
+        
+        impl_->swapChain_->Present(vsync_ ? 1 : 0, 0);
+    }
+    
+    // Clean up too large scratch buffers
+    CleanupScratchBuffers();
+}
+
+void Graphics::Clear(unsigned flags, const Color& color, float depth, unsigned stencil)
+{
+    PrepareDraw();
+
+    IntVector2 rtSize = GetRenderTargetDimensions();
+    
+    // D3D11 clear always clears the whole target regardless of viewport or scissor test settings
+    // Emulate partial clear by rendering a quad
+    if (!viewport_.left_ && !viewport_.top_ && viewport_.right_ == rtSize.x_ && viewport_.bottom_ == rtSize.y_)
+    {
+        if ((flags & CLEAR_COLOR) && impl_->renderTargetViews_[0])
+            impl_->deviceContext_->ClearRenderTargetView(impl_->renderTargetViews_[0], color.Data());
+        
+        if ((flags & (CLEAR_DEPTH | CLEAR_STENCIL)) && impl_->depthStencilView_)
+        {
+            unsigned depthClearFlags = 0;
+            if (flags & CLEAR_DEPTH)
+                depthClearFlags |= D3D11_CLEAR_DEPTH;
+            if (flags & CLEAR_STENCIL)
+                depthClearFlags |= D3D11_CLEAR_STENCIL;
+            impl_->deviceContext_->ClearDepthStencilView(impl_->depthStencilView_, depthClearFlags, depth, stencil);
+        }
+    }
+    else
+    {
+        Renderer* renderer = GetSubsystem<Renderer>();
+        if (!renderer)
+            return;
+
+        Geometry* geometry = renderer->GetQuadGeometry();
+
+        Matrix3x4 model = Matrix3x4::IDENTITY;
+        Matrix4 projection = Matrix4::IDENTITY;
+        model.m23_ = Clamp(depth, 0.0f, 1.0f);
+
+        SetColorWrite((flags & CLEAR_COLOR) != 0);
+        SetCullMode(CULL_NONE);
+        SetDepthTest(CMP_ALWAYS);
+        SetDepthWrite((flags & CLEAR_DEPTH) != 0);
+        SetFillMode(FILL_SOLID);
+        SetScissorTest(false);
+        SetStencilTest((flags & CLEAR_STENCIL) != 0, CMP_ALWAYS, OP_REF, OP_KEEP, OP_KEEP, stencil);
+        SetShaders(GetShader(VS, "ClearFramebuffer"), GetShader(PS, "ClearFramebuffer"));
+        SetShaderParameter(VSP_MODEL, model);
+        SetShaderParameter(VSP_VIEWPROJ, projection);
+        SetShaderParameter(PSP_MATDIFFCOLOR, color);
+
+        geometry->Draw(this);
+        
+        SetColorWrite(true);
+        SetDepthWrite(true);
+        SetStencilTest(false);
+        ClearParameterSources();
+    }
+}
+
+bool Graphics::ResolveToTexture(Texture2D* destination, const IntRect& viewport)
+{
+    if (!destination || !destination->GetRenderSurface())
+        return false;
+    
+    PROFILE(ResolveToTexture);
+    
+    IntRect vpCopy = viewport;
+    if (vpCopy.right_ <= vpCopy.left_)
+        vpCopy.right_ = vpCopy.left_ + 1;
+    if (vpCopy.bottom_ <= vpCopy.top_)
+        vpCopy.bottom_ = vpCopy.top_ + 1;
+    
+    RECT rect;
+    rect.left = Clamp(vpCopy.left_, 0, width_);
+    rect.top = Clamp(vpCopy.top_, 0, height_);
+    rect.right = Clamp(vpCopy.right_, 0, width_);
+    rect.bottom = Clamp(vpCopy.bottom_, 0, height_);
+    
+    RECT destRect;
+    destRect.left = 0;
+    destRect.top = 0;
+    destRect.right = destination->GetWidth();
+    destRect.bottom = destination->GetHeight();
+    
+    ID3D11Resource* source = 0;
+    bool resolve = false;
+    bool needRelease = false;
+
+    if (renderTargets_[0])
+        source = (ID3D11Resource*)renderTargets_[0]->GetParentTexture()->GetGPUObject();
+    else
+    {
+        impl_->defaultRenderTargetView_->GetResource(&source);
+        resolve = multiSample_ > 1;
+        needRelease = true;
+    }
+
+    if (!resolve)
+        impl_->deviceContext_->CopyResource((ID3D11Resource*)destination->GetGPUObject(), source);
+    else
+    {
+        impl_->deviceContext_->ResolveSubresource((ID3D11Resource*)destination->GetGPUObject(), 0, source, 0, (DXGI_FORMAT)
+            destination->GetFormat());
+    }
+
+    if (needRelease)
+        source->Release();
+
+    return true;
+}
+
+void Graphics::Draw(PrimitiveType type, unsigned vertexStart, unsigned vertexCount)
+{
+    if (!vertexCount || !shaderProgram_)
+        return;
+    
+    PrepareDraw();
+
+    unsigned primitiveCount;
+    D3D_PRIMITIVE_TOPOLOGY d3dPrimitiveType;
+    
+    GetD3DPrimitiveType(vertexCount, type, primitiveCount, d3dPrimitiveType);
+    if (d3dPrimitiveType != primitiveType_)
+    {
+        impl_->deviceContext_->IASetPrimitiveTopology(d3dPrimitiveType);
+        primitiveType_ = d3dPrimitiveType;
+    }
+    impl_->deviceContext_->Draw(vertexCount, vertexStart);
+
+    numPrimitives_ += primitiveCount;
+    ++numBatches_;
+}
+
+void Graphics::Draw(PrimitiveType type, unsigned indexStart, unsigned indexCount, unsigned minVertex, unsigned vertexCount)
+{
+    if (!vertexCount || !shaderProgram_)
+        return;
+    
+    PrepareDraw();
+
+    unsigned primitiveCount;
+    D3D_PRIMITIVE_TOPOLOGY d3dPrimitiveType;
+    
+    GetD3DPrimitiveType(indexCount, type, primitiveCount, d3dPrimitiveType);
+    if (d3dPrimitiveType != primitiveType_)
+    {
+        impl_->deviceContext_->IASetPrimitiveTopology(d3dPrimitiveType);
+        primitiveType_ = d3dPrimitiveType;
+    }
+    impl_->deviceContext_->DrawIndexed(indexCount, indexStart, 0);
+
+    numPrimitives_ += primitiveCount;
+    ++numBatches_;
+}
+
+void Graphics::DrawInstanced(PrimitiveType type, unsigned indexStart, unsigned indexCount, unsigned minVertex, unsigned vertexCount,
+    unsigned instanceCount)
+{
+    if (!indexCount || !instanceCount || !shaderProgram_)
+        return;
+    
+    PrepareDraw();
+
+    unsigned primitiveCount;
+    D3D_PRIMITIVE_TOPOLOGY d3dPrimitiveType;
+    
+    GetD3DPrimitiveType(indexCount, type, primitiveCount, d3dPrimitiveType);
+    if (d3dPrimitiveType != primitiveType_)
+    {
+        impl_->deviceContext_->IASetPrimitiveTopology(d3dPrimitiveType);
+        primitiveType_ = d3dPrimitiveType;
+    }
+    impl_->deviceContext_->DrawIndexedInstanced(indexCount, instanceCount, indexStart, 0, 0);
+
+    numPrimitives_ += instanceCount * primitiveCount;
+    ++numBatches_;
+}
+
+void Graphics::SetVertexBuffer(VertexBuffer* buffer)
+{
+    // Note: this is not multi-instance safe
+    static PODVector<VertexBuffer*> vertexBuffers(1);
+    static PODVector<unsigned> elementMasks(1);
+    vertexBuffers[0] = buffer;
+    elementMasks[0] = MASK_DEFAULT;
+    SetVertexBuffers(vertexBuffers, elementMasks);
+}
+
+bool Graphics::SetVertexBuffers(const PODVector<VertexBuffer*>& buffers, const PODVector<unsigned>&
+    elementMasks, unsigned instanceOffset)
+{
+    if (buffers.Size() > MAX_VERTEX_STREAMS)
+    {
+        LOGERROR("Too many vertex buffers");
+        return false;
+    }
+    if (buffers.Size() != elementMasks.Size())
+    {
+        LOGERROR("Amount of element masks and vertex buffers does not match");
+        return false;
+    }
+    
+    for (unsigned i = 0; i < MAX_VERTEX_STREAMS; ++i)
+    {
+        VertexBuffer* buffer = 0;
+        bool changed = false;
+
+        buffer = i < buffers.Size() ? buffers[i] : 0;
+        if (buffer)
+        {
+            unsigned elementMask = buffer->GetElementMask() & elementMasks[i];
+            unsigned offset = (elementMask & MASK_INSTANCEMATRIX1) ? instanceOffset * buffer->GetVertexSize() : 0;
+
+            if (buffer != vertexBuffers_[i] || elementMask != elementMasks_[i] || offset != impl_->vertexOffsets_[i])
+            {
+                vertexBuffers_[i] = buffer;
+                elementMasks_[i] = elementMask;
+                impl_->vertexBuffers_[i] = (ID3D11Buffer*)buffer->GetGPUObject();
+                impl_->vertexSizes_[i] = buffer->GetVertexSize();
+                impl_->vertexOffsets_[i] = offset;
+                changed = true;
+            }
+        }
+        else if (vertexBuffers_[i])
+        {
+            vertexBuffers_[i] = 0;
+            elementMasks_[i] = 0;
+            impl_->vertexBuffers_[i] = 0;
+            impl_->vertexSizes_[i] = 0;
+            impl_->vertexOffsets_[i] = 0;
+            changed = true;
+        }
+
+        if (changed)
+        {
+            vertexDeclarationDirty_ = true;
+
+            if (firstDirtyVB_ == M_MAX_UNSIGNED)
+                firstDirtyVB_ = lastDirtyVB_ = i;
+            else
+            {
+                if (i < firstDirtyVB_)
+                    firstDirtyVB_ = i;
+                if (i > lastDirtyVB_)
+                    lastDirtyVB_ = i;
+            }
+        }
+    }
+    
+    return true;
+}
+
+bool Graphics::SetVertexBuffers(const Vector<SharedPtr<VertexBuffer> >& buffers, const PODVector<unsigned>&
+    elementMasks, unsigned instanceOffset)
+{
+    return SetVertexBuffers(reinterpret_cast<const PODVector<VertexBuffer*>&>(buffers), elementMasks, instanceOffset);
+}
+
+void Graphics::SetIndexBuffer(IndexBuffer* buffer)
+{
+    if (buffer != indexBuffer_)
+    {
+        if (buffer)
+        {
+            impl_->deviceContext_->IASetIndexBuffer((ID3D11Buffer*)buffer->GetGPUObject(), buffer->GetIndexSize() ==
+                sizeof(unsigned short) ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0);
+        }
+        else
+            impl_->deviceContext_->IASetIndexBuffer(0, DXGI_FORMAT_UNKNOWN, 0);
+
+        indexBuffer_ = buffer;
+    }
+}
+
+void Graphics::SetShaders(ShaderVariation* vs, ShaderVariation* ps)
+{
+    // Switch to the clip plane variations if necessary
+    /// \todo Causes overhead and string manipulation per drawcall
+    if (useClipPlane_)
+    {
+        if (vs)
+            vs = vs->GetOwner()->GetVariation(VS, vs->GetDefines() + " CLIPPLANE");
+        if (ps)
+            ps = ps->GetOwner()->GetVariation(PS, ps->GetDefines() + " CLIPPLANE");
+    }
+
+    if (vs == vertexShader_ && ps == pixelShader_)
+        return;
+    
+    if (vs != vertexShader_)
+    {
+        // Create the shader now if not yet created. If already attempted, do not retry
+        if (vs && !vs->GetGPUObject())
+        {
+            if (vs->GetCompilerOutput().Empty())
+            {
+                PROFILE(CompileVertexShader);
+
+                bool success = vs->Create();
+                if (!success)
+                {
+                    LOGERROR("Failed to compile vertex shader " + vs->GetFullName() + ":\n" + vs->GetCompilerOutput());
+                    vs = 0;
+                }
+            }
+            else
+                vs = 0;
+        }
+        
+        impl_->deviceContext_->VSSetShader((ID3D11VertexShader*)(vs ? vs->GetGPUObject() : 0), 0, 0);
+        vertexShader_ = vs;
+        vertexDeclarationDirty_ = true;
+    }
+    
+    if (ps != pixelShader_)
+    {
+        if (ps && !ps->GetGPUObject())
+        {
+            if (ps->GetCompilerOutput().Empty())
+            {
+                PROFILE(CompilePixelShader);
+
+                bool success = ps->Create();
+                if (!success)
+                {
+                    LOGERROR("Failed to compile pixel shader " + ps->GetFullName() + ":\n" + ps->GetCompilerOutput());
+                    ps = 0;
+                }
+            }
+            else
+                ps = 0;
+        }
+        
+        impl_->deviceContext_->PSSetShader((ID3D11PixelShader*)(ps ? ps->GetGPUObject() : 0), 0, 0);
+        pixelShader_ = ps;
+    }
+    
+    // Update current shader parameters & constant buffers
+    if (vertexShader_ && pixelShader_)
+    {
+        Pair<ShaderVariation*, ShaderVariation*> key = MakePair(vertexShader_, pixelShader_);
+        ShaderProgramMap::Iterator i = shaderPrograms_.Find(key);
+        if (i != shaderPrograms_.End())
+            shaderProgram_ = i->second_.Get();
+        else
+        {
+            ShaderProgram* newProgram = shaderPrograms_[key] = new ShaderProgram(this, vertexShader_, pixelShader_);
+            shaderProgram_ = newProgram;
+        }
+
+        bool vsBuffersChanged = false;
+        bool psBuffersChanged = false;
+
+        for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS; ++i)
+        {
+            ID3D11Buffer* vsBuffer = shaderProgram_->vsConstantBuffers_[i] ? (ID3D11Buffer*)shaderProgram_->vsConstantBuffers_[i]->
+                GetGPUObject() : 0;
+            if (vsBuffer != impl_->constantBuffers_[VS][i])
+            {
+                impl_->constantBuffers_[VS][i] = vsBuffer;
+                shaderParameterSources_[i] = (const void*)M_MAX_UNSIGNED;
+                vsBuffersChanged = true;
+            }
+
+            ID3D11Buffer* psBuffer = shaderProgram_->psConstantBuffers_[i] ? (ID3D11Buffer*)shaderProgram_->psConstantBuffers_[i]->
+                GetGPUObject() : 0;
+            if (psBuffer != impl_->constantBuffers_[PS][i])
+            {
+                impl_->constantBuffers_[PS][i] = psBuffer;
+                shaderParameterSources_[i] = (const void*)M_MAX_UNSIGNED;
+                psBuffersChanged = true;
+            }
+        }
+
+        if (vsBuffersChanged)
+            impl_->deviceContext_->VSSetConstantBuffers(0, MAX_SHADER_PARAMETER_GROUPS, &impl_->constantBuffers_[VS][0]);
+        if (psBuffersChanged)
+            impl_->deviceContext_->PSSetConstantBuffers(0, MAX_SHADER_PARAMETER_GROUPS, &impl_->constantBuffers_[PS][0]);
+    }
+    else
+        shaderProgram_ = 0;
+
+    // Store shader combination if shader dumping in progress
+    if (shaderPrecache_)
+        shaderPrecache_->StoreShaders(vertexShader_, pixelShader_);
+
+    // Update clip plane parameter if necessary
+    if (useClipPlane_)
+        SetShaderParameter(VSP_CLIPPLANE, clipPlane_);
+}
+
+void Graphics::SetShaderParameter(StringHash param, const float* data, unsigned count)
+{
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
+        return;
+
+    ConstantBuffer* buffer = i->second_.bufferPtr_;
+    if (!buffer->IsDirty())
+        dirtyConstantBuffers_.Push(buffer);
+    buffer->SetParameter(i->second_.offset_, count * sizeof(float), data);
+}
+
+void Graphics::SetShaderParameter(StringHash param, float value)
+{
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
+        return;
+
+    ConstantBuffer* buffer = i->second_.bufferPtr_;
+    if (!buffer->IsDirty())
+        dirtyConstantBuffers_.Push(buffer);
+    buffer->SetParameter(i->second_.offset_, sizeof(float), &value);
+}
+
+void Graphics::SetShaderParameter(StringHash param, bool value)
+{
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
+        return;
+
+    ConstantBuffer* buffer = i->second_.bufferPtr_;
+    if (!buffer->IsDirty())
+        dirtyConstantBuffers_.Push(buffer);
+    buffer->SetParameter(i->second_.offset_, sizeof(bool), &value);
+}
+
+void Graphics::SetShaderParameter(StringHash param, const Color& color)
+{
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
+        return;
+
+    ConstantBuffer* buffer = i->second_.bufferPtr_;
+    if (!buffer->IsDirty())
+        dirtyConstantBuffers_.Push(buffer);
+    buffer->SetParameter(i->second_.offset_, sizeof(Color), &color);
+}
+
+void Graphics::SetShaderParameter(StringHash param, const Vector2& vector)
+{
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
+        return;
+
+    ConstantBuffer* buffer = i->second_.bufferPtr_;
+    if (!buffer->IsDirty())
+        dirtyConstantBuffers_.Push(buffer);
+    buffer->SetParameter(i->second_.offset_, sizeof(Vector2), &vector);
+}
+
+void Graphics::SetShaderParameter(StringHash param, const Matrix3& matrix)
+{
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
+        return;
+
+    ConstantBuffer* buffer = i->second_.bufferPtr_;
+    if (!buffer->IsDirty())
+        dirtyConstantBuffers_.Push(buffer);
+    buffer->SetVector3ArrayParameter(i->second_.offset_, 3, &matrix);
+}
+
+void Graphics::SetShaderParameter(StringHash param, const Vector3& vector)
+{
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
+        return;
+
+    ConstantBuffer* buffer = i->second_.bufferPtr_;
+    if (!buffer->IsDirty())
+        dirtyConstantBuffers_.Push(buffer);
+    buffer->SetParameter(i->second_.offset_, sizeof(Vector3), &vector);
+}
+
+void Graphics::SetShaderParameter(StringHash param, const Matrix4& matrix)
+{
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
+        return;
+
+    ConstantBuffer* buffer = i->second_.bufferPtr_;
+    if (!buffer->IsDirty())
+        dirtyConstantBuffers_.Push(buffer);
+    buffer->SetParameter(i->second_.offset_, sizeof(Matrix4), &matrix);
+}
+
+void Graphics::SetShaderParameter(StringHash param, const Vector4& vector)
+{
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
+        return;
+
+    ConstantBuffer* buffer = i->second_.bufferPtr_;
+    if (!buffer->IsDirty())
+        dirtyConstantBuffers_.Push(buffer);
+    buffer->SetParameter(i->second_.offset_, sizeof(Vector4), &vector);
+}
+
+void Graphics::SetShaderParameter(StringHash param, const Matrix3x4& matrix)
+{
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
+        return;
+
+    ConstantBuffer* buffer = i->second_.bufferPtr_;
+    if (!buffer->IsDirty())
+        dirtyConstantBuffers_.Push(buffer);
+    buffer->SetParameter(i->second_.offset_, sizeof(Matrix3x4), &matrix);
+}
+
+void Graphics::SetShaderParameter(StringHash param, const Variant& value)
+{
+    switch (value.GetType())
+    {
+    case VAR_BOOL:
+        SetShaderParameter(param, value.GetBool());
+        break;
+
+    case VAR_FLOAT:
+        SetShaderParameter(param, value.GetFloat());
+        break;
+        
+    case VAR_VECTOR2:
+        SetShaderParameter(param, value.GetVector2());
+        break;
+        
+    case VAR_VECTOR3:
+        SetShaderParameter(param, value.GetVector3());
+        break;
+        
+    case VAR_VECTOR4:
+        SetShaderParameter(param, value.GetVector4());
+        break;
+        
+    case VAR_COLOR:
+        SetShaderParameter(param, value.GetColor());
+        break;
+
+    case VAR_MATRIX3:
+        SetShaderParameter(param, value.GetMatrix3());
+        break;
+        
+    case VAR_MATRIX3X4:
+        SetShaderParameter(param, value.GetMatrix3x4());
+        break;
+        
+    case VAR_MATRIX4:
+        SetShaderParameter(param, value.GetMatrix4());
+        break;
+        
+    default:
+        // Unsupported parameter type, do nothing
+        break;
+    }
+}
+
+bool Graphics::NeedParameterUpdate(ShaderParameterGroup group, const void* source)
+{
+    if ((unsigned)(size_t)shaderParameterSources_[group] == M_MAX_UNSIGNED || shaderParameterSources_[group] != source)
+    {
+        shaderParameterSources_[group] = source;
+        return true;
+    }
+    else
+        return false;
+}
+
+bool Graphics::HasShaderParameter(StringHash param)
+{
+    return shaderProgram_ && shaderProgram_->parameters_.Find(param) != shaderProgram_->parameters_.End();
+}
+
+bool Graphics::HasTextureUnit(TextureUnit unit)
+{
+    return pixelShader_ && pixelShader_->HasTextureUnit(unit);
+}
+
+void Graphics::ClearParameterSource(ShaderParameterGroup group)
+{
+    shaderParameterSources_[group] = (const void*)M_MAX_UNSIGNED;
+}
+
+void Graphics::ClearParameterSources()
+{
+    for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS; ++i)
+        shaderParameterSources_[i] = (const void*)M_MAX_UNSIGNED;
+}
+
+void Graphics::ClearTransformSources()
+{
+    shaderParameterSources_[SP_CAMERA] = (const void*)M_MAX_UNSIGNED;
+    shaderParameterSources_[SP_OBJECT] = (const void*)M_MAX_UNSIGNED;
+}
+
+void Graphics::SetTexture(unsigned index, Texture* texture)
+{
+    if (index >= MAX_TEXTURE_UNITS)
+        return;
+    
+    // Check if texture is currently bound as a rendertarget. In that case, use its backup texture, or blank if not defined
+    if (texture)
+    {
+        if (renderTargets_[0] && renderTargets_[0]->GetParentTexture() == texture)
+            texture = texture->GetBackupTexture();
+    }
+    
+    if (texture && texture->GetParametersDirty())
+    {
+        texture->UpdateParameters();
+        textures_[index] = 0; // Force reassign
+    }
+
+    if (texture != textures_[index])
+    {
+        if (firstDirtyTexture_ == M_MAX_UNSIGNED)
+            firstDirtyTexture_ = lastDirtyTexture_ = index;
+        else
+        {
+            if (index < firstDirtyTexture_)
+                firstDirtyTexture_ = index;
+            if (index > lastDirtyTexture_)
+                lastDirtyTexture_ = index;
+        }
+
+        textures_[index] = texture;
+        impl_->shaderResourceViews_[index] = texture ? (ID3D11ShaderResourceView*)texture->GetShaderResourceView() : 0;
+        impl_->samplers_[index] = texture ? (ID3D11SamplerState*)texture->GetSampler() : 0;
+        texturesDirty_ = true;
+    }
+}
+
+void Graphics::SetDefaultTextureFilterMode(TextureFilterMode mode)
+{
+    if (mode != defaultTextureFilterMode_)
+    {
+        defaultTextureFilterMode_ = mode;
+        SetTextureParametersDirty();
+    }
+}
+
+void Graphics::SetTextureAnisotropy(unsigned level)
+{
+    if (level != textureAnisotropy_)
+    {
+        textureAnisotropy_ = level;
+        SetTextureParametersDirty();
+    }
+}
+
+void Graphics::SetTextureParametersDirty()
+{
+    MutexLock lock(gpuObjectMutex_);
+
+    for (PODVector<GPUObject*>::Iterator i = gpuObjects_.Begin(); i != gpuObjects_.End(); ++i)
+    {
+        Texture* texture = dynamic_cast<Texture*>(*i);
+        if (texture)
+            texture->SetParametersDirty();
+    }
+}
+
+void Graphics::ResetRenderTargets()
+{
+    for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
+        SetRenderTarget(i, (RenderSurface*)0);
+    SetDepthStencil((RenderSurface*)0);
+    SetViewport(IntRect(0, 0, width_, height_));
+}
+
+void Graphics::ResetRenderTarget(unsigned index)
+{
+    SetRenderTarget(index, (RenderSurface*)0);
+}
+
+void Graphics::ResetDepthStencil()
+{
+    SetDepthStencil((RenderSurface*)0);
+}
+
+void Graphics::SetRenderTarget(unsigned index, RenderSurface* renderTarget)
+{
+    if (index >= MAX_RENDERTARGETS)
+        return;
+    
+    if (renderTarget != renderTargets_[index])
+    {
+        renderTargets_[index] = renderTarget;
+        renderTargetsDirty_ = true;
+
+        // If the rendertarget is also bound as a texture, replace with backup texture or null
+        if (renderTarget)
+        {
+            Texture* parentTexture = renderTarget->GetParentTexture();
+            
+            for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
+            {
+                if (textures_[i] == parentTexture)
+                    SetTexture(i, textures_[i]->GetBackupTexture());
+            }
+        }
+    }
+}
+
+void Graphics::SetRenderTarget(unsigned index, Texture2D* texture)
+{
+    RenderSurface* renderTarget = 0;
+    if (texture)
+        renderTarget = texture->GetRenderSurface();
+    
+    SetRenderTarget(index, renderTarget);
+}
+
+void Graphics::SetDepthStencil(RenderSurface* depthStencil)
+{
+    if (depthStencil != depthStencil_)
+    {
+        depthStencil_ = depthStencil;
+        renderTargetsDirty_ = true;
+    }
+}
+
+void Graphics::SetDepthStencil(Texture2D* texture)
+{
+    RenderSurface* depthStencil = 0;
+    if (texture)
+        depthStencil = texture->GetRenderSurface();
+    
+    SetDepthStencil(depthStencil);
+    // Constant depth bias depends on the bitdepth
+    rasterizerStateDirty_ = true;
+}
+
+void Graphics::SetViewport(const IntRect& rect)
+{
+    IntVector2 size = GetRenderTargetDimensions();
+    
+    IntRect rectCopy = rect;
+    
+    if (rectCopy.right_ <= rectCopy.left_)
+        rectCopy.right_ = rectCopy.left_ + 1;
+    if (rectCopy.bottom_ <= rectCopy.top_)
+        rectCopy.bottom_ = rectCopy.top_ + 1;
+    rectCopy.left_ = Clamp(rectCopy.left_, 0, size.x_);
+    rectCopy.top_ = Clamp(rectCopy.top_, 0, size.y_);
+    rectCopy.right_ = Clamp(rectCopy.right_, 0, size.x_);
+    rectCopy.bottom_ = Clamp(rectCopy.bottom_, 0, size.y_);
+    
+    static D3D11_VIEWPORT d3dViewport;
+    d3dViewport.TopLeftX = (float)rectCopy.left_;
+    d3dViewport.TopLeftY = (float)rectCopy.top_;
+    d3dViewport.Width = (float)(rectCopy.right_ - rectCopy.left_);
+    d3dViewport.Height = (float)(rectCopy.bottom_ - rectCopy.top_);
+    d3dViewport.MinDepth = 0.0f;
+    d3dViewport.MaxDepth = 1.0f;
+
+    impl_->deviceContext_->RSSetViewports(1, &d3dViewport);
+
+    viewport_ = rectCopy;
+    
+    // Disable scissor test, needs to be re-enabled by the user
+    SetScissorTest(false);
+}
+
+void Graphics::SetBlendMode(BlendMode mode)
+{
+    if (mode != blendMode_)
+    {
+        blendMode_ = mode;
+        blendStateDirty_ = true;
+    }
+}
+
+void Graphics::SetColorWrite(bool enable)
+{
+    if (enable != colorWrite_)
+    {
+        colorWrite_ = enable;
+        blendStateDirty_ = true;
+    }
+}
+
+void Graphics::SetCullMode(CullMode mode)
+{
+    if (mode != cullMode_)
+    {
+        cullMode_ = mode;
+        rasterizerStateDirty_ = true;
+    }
+}
+
+void Graphics::SetDepthBias(float constantBias, float slopeScaledBias)
+{
+    if (constantBias != constantDepthBias_ || slopeScaledBias != slopeScaledDepthBias_)
+    {
+        constantDepthBias_ = constantBias;
+        slopeScaledDepthBias_ = slopeScaledBias;
+        rasterizerStateDirty_ = true;
+    }
+}
+
+void Graphics::SetDepthTest(CompareMode mode)
+{
+    if (mode != depthTestMode_)
+    {
+        depthTestMode_ = mode;
+        depthStateDirty_ = true;
+    }
+}
+
+void Graphics::SetDepthWrite(bool enable)
+{
+    if (enable != depthWrite_)
+    {
+        depthWrite_ = enable;
+        depthStateDirty_ = true;
+    }
+}
+
+void Graphics::SetFillMode(FillMode mode)
+{
+    if (mode != fillMode_)
+    {
+        fillMode_ = mode;
+        rasterizerStateDirty_ = true;
+    }
+}
+
+void Graphics::SetScissorTest(bool enable, const Rect& rect, bool borderInclusive)
+{
+    // During some light rendering loops, a full rect is toggled on/off repeatedly.
+    // Disable scissor in that case to reduce state changes
+    if (rect.min_.x_ <= 0.0f && rect.min_.y_ <= 0.0f && rect.max_.x_ >= 1.0f && rect.max_.y_ >= 1.0f)
+        enable = false;
+    
+    if (enable)
+    {
+        IntVector2 rtSize(GetRenderTargetDimensions());
+        IntVector2 viewSize(viewport_.Size());
+        IntVector2 viewPos(viewport_.left_, viewport_.top_);
+        IntRect intRect;
+        int expand = borderInclusive ? 1 : 0;
+        
+        intRect.left_ = Clamp((int)((rect.min_.x_ + 1.0f) * 0.5f * viewSize.x_) + viewPos.x_, 0, rtSize.x_ - 1);
+        intRect.top_ = Clamp((int)((-rect.max_.y_ + 1.0f) * 0.5f * viewSize.y_) + viewPos.y_, 0, rtSize.y_ - 1);
+        intRect.right_ = Clamp((int)((rect.max_.x_ + 1.0f) * 0.5f * viewSize.x_) + viewPos.x_ + expand, 0, rtSize.x_);
+        intRect.bottom_ = Clamp((int)((-rect.min_.y_ + 1.0f) * 0.5f * viewSize.y_) + viewPos.y_ + expand, 0, rtSize.y_);
+        
+        if (intRect.right_ == intRect.left_)
+            intRect.right_++;
+        if (intRect.bottom_ == intRect.top_)
+            intRect.bottom_++;
+        
+        if (intRect.right_ < intRect.left_ || intRect.bottom_ < intRect.top_)
+            enable = false;
+        
+        if (enable && intRect != scissorRect_)
+        {
+            scissorRect_ = intRect;
+            scissorRectDirty_ = true;
+        }
+    }
+    
+    if (enable != scissorTest_)
+    {
+        scissorTest_ = enable;
+        rasterizerStateDirty_ = true;
+    }
+}
+
+void Graphics::SetScissorTest(bool enable, const IntRect& rect)
+{
+    IntVector2 rtSize(GetRenderTargetDimensions());
+    IntVector2 viewSize(viewport_.Size());
+    IntVector2 viewPos(viewport_.left_, viewport_.top_);
+    
+    if (enable)
+    {
+        IntRect intRect;
+        intRect.left_ = Clamp(rect.left_ + viewPos.x_, 0, rtSize.x_ - 1);
+        intRect.top_ = Clamp(rect.top_ + viewPos.y_, 0, rtSize.y_ - 1);
+        intRect.right_ = Clamp(rect.right_ + viewPos.x_, 0, rtSize.x_);
+        intRect.bottom_ = Clamp(rect.bottom_ + viewPos.y_, 0, rtSize.y_);
+        
+        if (intRect.right_ == intRect.left_)
+            intRect.right_++;
+        if (intRect.bottom_ == intRect.top_)
+            intRect.bottom_++;
+        
+        if (intRect.right_ < intRect.left_ || intRect.bottom_ < intRect.top_)
+            enable = false;
+        
+        if (enable && intRect != scissorRect_)
+        {
+            scissorRect_ = intRect;
+            scissorRectDirty_ = true;
+        }
+    }
+
+    if (enable != scissorTest_)
+    {
+        scissorTest_ = enable;
+        rasterizerStateDirty_ = true;
+    }
+}
+
+void Graphics::SetStencilTest(bool enable, CompareMode mode, StencilOp pass, StencilOp fail, StencilOp zFail, unsigned stencilRef, unsigned compareMask, unsigned writeMask)
+{
+    if (enable != stencilTest_)
+    {
+        stencilTest_ = enable;
+        depthStateDirty_ = true;
+    }
+    
+    if (enable)
+    {
+        if (mode != stencilTestMode_)
+        {
+            stencilTestMode_ = mode;
+            depthStateDirty_ = true;
+        }
+        if (pass != stencilPass_)
+        {
+            stencilPass_ = pass;
+            depthStateDirty_ = true;
+        }
+        if (fail != stencilFail_)
+        {
+            stencilFail_ = fail;
+            depthStateDirty_ = true;
+        }
+        if (zFail != stencilZFail_)
+        {
+            stencilZFail_ = zFail;
+            depthStateDirty_ = true;
+        }
+        if (compareMask != stencilCompareMask_)
+        {
+            stencilCompareMask_ = compareMask;
+            depthStateDirty_ = true;
+        }
+        if (writeMask != stencilWriteMask_)
+        {
+            stencilWriteMask_ = writeMask;
+            depthStateDirty_ = true;
+        }
+        if (stencilRef != stencilRef_)
+        {
+            stencilRef_ = stencilRef;
+            stencilRefDirty_ = true;
+            depthStateDirty_ = true;
+        }
+    }
+}
+
+void Graphics::SetClipPlane(bool enable, const Plane& clipPlane, const Matrix3x4& view, const Matrix4& projection)
+{
+    useClipPlane_ = enable;
+
+    if (enable)
+    {
+        Matrix4 viewProj = projection * view;
+        clipPlane_ = clipPlane.Transformed(viewProj).ToVector4();
+        SetShaderParameter(VSP_CLIPPLANE, clipPlane_);
+    }
+}
+
+void Graphics::BeginDumpShaders(const String& fileName)
+{
+    shaderPrecache_ = new ShaderPrecache(context_, fileName);
+}
+
+void Graphics::EndDumpShaders()
+{
+    shaderPrecache_.Reset();
+}
+
+void Graphics::PrecacheShaders(Deserializer& source)
+{
+    PROFILE(PrecacheShaders);
+    
+    ShaderPrecache::LoadShaders(this, source);
+}
+
+bool Graphics::IsInitialized() const
+{
+    return impl_->window_ != 0 && impl_->GetDevice() != 0;
+}
+
+IntVector2 Graphics::GetWindowPosition() const
+{
+    if (impl_->window_)
+        return position_;
+    return IntVector2::ZERO;
+}
+
+PODVector<IntVector2> Graphics::GetResolutions() const
+{
+    PODVector<IntVector2> ret;
+    unsigned numModes = SDL_GetNumDisplayModes(0);
+    
+    for (unsigned i = 0; i < numModes; ++i)
+    {
+        SDL_DisplayMode mode;
+        SDL_GetDisplayMode(0, i, &mode);
+        int width = mode.w;
+        int height  = mode.h;
+        
+        // Store mode if unique
+        bool unique = true;
+        for (unsigned j = 0; j < ret.Size(); ++j)
+        {
+            if (ret[j].x_ == width && ret[j].y_ == height)
+            {
+                unique = false;
+                break;
+            }
+        }
+        
+        if (unique)
+            ret.Push(IntVector2(width, height));
+    }
+    
+    return ret;
+}
+
+PODVector<int> Graphics::GetMultiSampleLevels() const
+{
+    PODVector<int> ret;
+    ret.Push(1);
+
+    if (impl_->device_)
+    {
+        for (unsigned i = 2; i <= 16; ++i)
+        {
+            unsigned levels = 0;
+            impl_->device_->CheckMultisampleQualityLevels(sRGB_ ? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB : DXGI_FORMAT_R8G8B8A8_UNORM,
+                i, &levels);
+            if (levels)
+                ret.Push(i);
+        }
+    }
+
+    return ret;
+}
+
+IntVector2 Graphics::GetDesktopResolution() const
+{
+    SDL_DisplayMode mode;
+    SDL_GetDesktopDisplayMode(0, &mode);
+    return IntVector2(mode.w, mode.h);
+}
+
+unsigned Graphics::GetFormat(CompressedFormat format) const
+{
+    switch (format)
+    {
+    case CF_RGBA:
+        return DXGI_FORMAT_R8G8B8A8_UNORM;
+
+    case CF_DXT1:
+        return DXGI_FORMAT_BC1_UNORM;
+        
+    case CF_DXT3:
+        return DXGI_FORMAT_BC2_UNORM;
+        
+    case CF_DXT5:
+        return DXGI_FORMAT_BC3_UNORM;
+    }
+    
+    return 0;
+}
+
+ShaderVariation* Graphics::GetShader(ShaderType type, const String& name, const String& defines) const
+{
+    return GetShader(type, name.CString(), defines.CString());
+}
+
+ShaderVariation* Graphics::GetShader(ShaderType type, const char* name, const char* defines) const
+{
+    if (lastShaderName_ != name || !lastShader_)
+    {
+        ResourceCache* cache = GetSubsystem<ResourceCache>();
+        
+        String fullShaderName = shaderPath_ + name + shaderExtension_;
+        // Try to reduce repeated error log prints because of missing shaders
+        if (lastShaderName_ == name && !cache->Exists(fullShaderName))
+            return 0;
+        
+        lastShader_ = cache->GetResource<Shader>(fullShaderName);
+        lastShaderName_ = name;
+    }
+    
+    return lastShader_ ? lastShader_->GetVariation(type, defines) : (ShaderVariation*)0;
+}
+
+VertexBuffer* Graphics::GetVertexBuffer(unsigned index) const
+{
+    return index < MAX_VERTEX_STREAMS ? vertexBuffers_[index] : 0;
+}
+
+TextureUnit Graphics::GetTextureUnit(const String& name)
+{
+    HashMap<String, TextureUnit>::Iterator i = textureUnits_.Find(name);
+    if (i != textureUnits_.End())
+        return i->second_;
+    else
+        return MAX_TEXTURE_UNITS;
+}
+
+const String& Graphics::GetTextureUnitName(TextureUnit unit)
+{
+    for (HashMap<String, TextureUnit>::Iterator i = textureUnits_.Begin(); i != textureUnits_.End(); ++i)
+    {
+        if (i->second_ == unit)
+            return i->first_;
+    }
+    return String::EMPTY;
+}
+
+Texture* Graphics::GetTexture(unsigned index) const
+{
+    return index < MAX_TEXTURE_UNITS ? textures_[index] : 0;
+}
+
+RenderSurface* Graphics::GetRenderTarget(unsigned index) const
+{
+    return index < MAX_RENDERTARGETS ? renderTargets_[index] : 0;
+}
+
+IntVector2 Graphics::GetRenderTargetDimensions() const
+{
+    int width, height;
+    
+    if (renderTargets_[0])
+    {
+        width = renderTargets_[0]->GetWidth();
+        height = renderTargets_[0]->GetHeight();
+    }
+    else if (depthStencil_) // Depth-only rendering
+    {
+        width = depthStencil_->GetWidth();
+        height = depthStencil_->GetHeight();
+    }
+    else
+    {
+        width = width_;
+        height = height_;
+    }
+    
+    return IntVector2(width, height);
+}
+
+void Graphics::WindowResized()
+{
+    if (!impl_->device_ || !impl_->window_)
+        return;
+    
+    int newWidth, newHeight;
+    
+    SDL_GetWindowSize(impl_->window_, &newWidth, &newHeight);
+    if (newWidth == width_ && newHeight == height_)
+        return;
+
+    UpdateSwapChain(newWidth, newHeight);
+    
+    // Reset rendertargets and viewport for the new screen size
+    ResetRenderTargets();
+    
+    LOGDEBUGF("Window was resized to %dx%d", width_, height_);
+    
+    using namespace ScreenMode;
+    
+    VariantMap& eventData = GetEventDataMap();
+    eventData[P_WIDTH] = width_;
+    eventData[P_HEIGHT] = height_;
+    eventData[P_FULLSCREEN] = fullscreen_;
+    eventData[P_RESIZABLE] = resizable_;
+    eventData[P_BORDERLESS] = borderless_;
+    SendEvent(E_SCREENMODE, eventData);
+}
+
+void Graphics::WindowMoved()
+{
+    if (!impl_->device_ || !impl_->window_ || fullscreen_)
+        return;
+
+    int newX, newY;
+
+    SDL_GetWindowPosition(impl_->window_, &newX, &newY);
+    if (newX == position_.x_ && newY == position_.y_)
+        return;
+
+    position_.x_ = newX;
+    position_.y_ = newY;
+
+    LOGDEBUGF("Window was moved to %d,%d", position_.x_, position_.y_);
+
+    using namespace WindowPos;
+
+    VariantMap& eventData = GetEventDataMap();
+    eventData[P_X] = position_.x_;
+    eventData[P_Y] = position_.y_;
+    SendEvent(E_WINDOWPOS, eventData);
+}
+
+void Graphics::Maximize()
+{
+    if (!impl_->window_)
+        return;
+
+    SDL_MaximizeWindow(impl_->window_);
+}
+
+void Graphics::Minimize()
+{
+    if (!impl_->window_)
+        return;
+
+    SDL_MinimizeWindow(impl_->window_);
+}
+
+void Graphics::AddGPUObject(GPUObject* object)
+{
+    MutexLock lock(gpuObjectMutex_);
+
+    gpuObjects_.Push(object);
+}
+
+void Graphics::RemoveGPUObject(GPUObject* object)
+{
+    MutexLock lock(gpuObjectMutex_);
+
+    gpuObjects_.Remove(object);
+}
+
+void* Graphics::ReserveScratchBuffer(unsigned size)
+{
+    if (!size)
+        return 0;
+    
+    if (size > maxScratchBufferRequest_)
+        maxScratchBufferRequest_ = size;
+    
+    // First check for a free buffer that is large enough
+    for (Vector<ScratchBuffer>::Iterator i = scratchBuffers_.Begin(); i != scratchBuffers_.End(); ++i)
+    {
+        if (!i->reserved_ && i->size_ >= size)
+        {
+            i->reserved_ = true;
+            return i->data_.Get();
+        }
+    }
+    
+    // Then check if a free buffer can be resized
+    for (Vector<ScratchBuffer>::Iterator i = scratchBuffers_.Begin(); i != scratchBuffers_.End(); ++i)
+    {
+        if (!i->reserved_)
+        {
+            i->data_ = new unsigned char[size];
+            i->size_ = size;
+            i->reserved_ = true;
+            
+            LOGDEBUG("Resized scratch buffer to size " + String(size));
+            
+            return i->data_.Get();
+        }
+    }
+    
+    // Finally allocate a new buffer
+    ScratchBuffer newBuffer;
+    newBuffer.data_ = new unsigned char[size];
+    newBuffer.size_ = size;
+    newBuffer.reserved_ = true;
+    scratchBuffers_.Push(newBuffer);
+    return newBuffer.data_.Get();
+    
+    LOGDEBUG("Allocated scratch buffer with size " + String(size));
+}
+
+void Graphics::FreeScratchBuffer(void* buffer)
+{
+    if (!buffer)
+        return;
+    
+    for (Vector<ScratchBuffer>::Iterator i = scratchBuffers_.Begin(); i != scratchBuffers_.End(); ++i)
+    {
+        if (i->reserved_ && i->data_.Get() == buffer)
+        {
+            i->reserved_ = false;
+            return;
+        }
+    }
+
+    LOGWARNING("Reserved scratch buffer " + ToStringHex((unsigned)(size_t)buffer) + " not found");
+}
+
+void Graphics::CleanupScratchBuffers()
+{
+    for (Vector<ScratchBuffer>::Iterator i = scratchBuffers_.Begin(); i != scratchBuffers_.End(); ++i)
+    {
+        if (!i->reserved_ && i->size_ > maxScratchBufferRequest_ * 2)
+        {
+            i->data_ = maxScratchBufferRequest_ > 0 ? new unsigned char[maxScratchBufferRequest_] : 0;
+            i->size_ = maxScratchBufferRequest_;
+            
+            LOGDEBUG("Resized scratch buffer to size " + String(maxScratchBufferRequest_));
+        }
+    }
+    
+    maxScratchBufferRequest_ = 0;
+}
+
+void Graphics::CleanUpShaderPrograms(ShaderVariation* variation)
+{
+    for (ShaderProgramMap::Iterator i = shaderPrograms_.Begin(); i != shaderPrograms_.End();)
+    {
+        if (i->first_.first_ == variation || i->first_.second_ == variation)
+            i = shaderPrograms_.Erase(i);
+        else
+            ++i;
+    }
+
+    if (vertexShader_ == variation || pixelShader_ == variation)
+        shaderProgram_ = 0;
+}
+
+ConstantBuffer* Graphics::GetOrCreateConstantBuffer(ShaderType type, unsigned index, unsigned size)
+{
+    // Ensure that different shader types and index slots get unique buffers, even if the size is same
+    unsigned key = type | (index << 1) | (size << 4);
+    HashMap<unsigned, SharedPtr<ConstantBuffer> >::Iterator i = constantBuffers_.Find(key);
+    if (i != constantBuffers_.End())
+        return i->second_.Get();
+    else
+    {
+        SharedPtr<ConstantBuffer> newConstantBuffer(new ConstantBuffer(context_));
+        newConstantBuffer->SetSize(size);
+        constantBuffers_[key] = newConstantBuffer;
+        return newConstantBuffer.Get();
+    }
+}
+
+unsigned Graphics::GetAlphaFormat()
+{
+    return DXGI_FORMAT_A8_UNORM;
+}
+
+unsigned Graphics::GetLuminanceFormat()
+{
+    // Note: not same sampling behavior as on D3D9; need to sample the R channel only
+    return DXGI_FORMAT_R8_UNORM; 
+}
+
+unsigned Graphics::GetLuminanceAlphaFormat()
+{
+    // Note: not same sampling behavior as on D3D9; need to sample the RG channels
+    return DXGI_FORMAT_R8G8_UNORM;
+}
+
+unsigned Graphics::GetRGBFormat()
+{
+    return DXGI_FORMAT_R8G8B8A8_UNORM;
+}
+
+unsigned Graphics::GetRGBAFormat()
+{
+    return DXGI_FORMAT_R8G8B8A8_UNORM;
+}
+
+unsigned Graphics::GetRGBA16Format()
+{
+    return DXGI_FORMAT_R16G16B16A16_UNORM;
+}
+
+unsigned Graphics::GetRGBAFloat16Format()
+{
+    return DXGI_FORMAT_R16G16B16A16_FLOAT;
+}
+
+unsigned Graphics::GetRGBAFloat32Format()
+{
+    return DXGI_FORMAT_R32G32B32A32_FLOAT;
+}
+
+unsigned Graphics::GetRG16Format()
+{
+    return DXGI_FORMAT_R16G16_UNORM;
+}
+
+unsigned Graphics::GetRGFloat16Format()
+{
+    return DXGI_FORMAT_R16G16_FLOAT;
+}
+
+unsigned Graphics::GetRGFloat32Format()
+{
+    return DXGI_FORMAT_R32G32_FLOAT;
+}
+
+unsigned Graphics::GetFloat16Format()
+{
+    return DXGI_FORMAT_R16_FLOAT;
+}
+
+unsigned Graphics::GetFloat32Format()
+{
+    return DXGI_FORMAT_R32_FLOAT;
+}
+
+unsigned Graphics::GetLinearDepthFormat()
+{
+    return DXGI_FORMAT_R32_FLOAT;
+}
+
+unsigned Graphics::GetDepthStencilFormat()
+{
+    return DXGI_FORMAT_R24G8_TYPELESS;
+}
+
+unsigned Graphics::GetReadableDepthFormat()
+{
+    return DXGI_FORMAT_R24G8_TYPELESS;
+}
+
+unsigned Graphics::GetFormat(const String& formatName)
+{
+    String nameLower = formatName.ToLower().Trimmed();
+    
+    if (nameLower == "a")
+        return GetAlphaFormat();
+    if (nameLower == "l")
+        return GetLuminanceFormat();
+    if (nameLower == "la")
+        return GetLuminanceAlphaFormat();
+    if (nameLower == "rgb")
+        return GetRGBFormat();
+    if (nameLower == "rgba")
+        return GetRGBAFormat();
+    if (nameLower == "rgba16")
+        return GetRGBA16Format();
+    if (nameLower == "rgba16f")
+        return GetRGBAFloat16Format();
+    if (nameLower == "rgba32f")
+        return GetRGBAFloat32Format();
+    if (nameLower == "rg16")
+        return GetRG16Format();
+    if (nameLower == "rg16f")
+        return GetRGFloat16Format();
+    if (nameLower == "rg32f")
+        return GetRGFloat32Format();
+    if (nameLower == "r16f")
+        return GetFloat16Format();
+    if (nameLower == "r32f" || nameLower == "float")
+        return GetFloat32Format();
+    if (nameLower == "lineardepth" || nameLower == "depth")
+        return GetLinearDepthFormat();
+    if (nameLower == "d24s8")
+        return GetDepthStencilFormat();
+    if (nameLower == "readabledepth" || nameLower == "hwdepth")
+        return GetReadableDepthFormat();
+    
+    return GetRGBFormat();
+}
+
+bool Graphics::OpenWindow(int width, int height, bool resizable, bool borderless)
+{
+    if (!externalWindow_)
+    {
+        unsigned flags = 0;
+        if (resizable)
+            flags |= SDL_WINDOW_RESIZABLE;
+        if (borderless)
+            flags |= SDL_WINDOW_BORDERLESS;
+
+        impl_->window_ = SDL_CreateWindow(windowTitle_.CString(), position_.x_, position_.y_, width, height, flags);
+    }
+    else
+        impl_->window_ = SDL_CreateWindowFrom(externalWindow_, 0);
+
+    if (!impl_->window_)
+    {
+        LOGERROR("Could not create window");
+        return false;
+    }
+
+    SDL_GetWindowPosition(impl_->window_, &position_.x_, &position_.y_);
+
+    CreateWindowIcon();
+
+    return true;
+}
+
+void Graphics::CreateWindowIcon()
+{
+    if (windowIcon_)
+    {
+        SDL_Surface* surface = windowIcon_->GetSDLSurface();
+        if (surface)
+        {
+            SDL_SetWindowIcon(impl_->window_, surface);
+            SDL_FreeSurface(surface);
+        }
+    }
+}
+
+void Graphics::AdjustWindow(int& newWidth, int& newHeight, bool& newFullscreen, bool& newBorderless)
+{
+    if (!externalWindow_)
+    {
+        if (!newWidth || !newHeight)
+        {
+            SDL_MaximizeWindow(impl_->window_);
+            SDL_GetWindowSize(impl_->window_, &newWidth, &newHeight);
+        }
+        else
+            SDL_SetWindowSize(impl_->window_, newWidth, newHeight);
+
+        SDL_SetWindowFullscreen(impl_->window_, newFullscreen ? SDL_TRUE : SDL_FALSE);
+        SDL_SetWindowBordered(impl_->window_, newBorderless ? SDL_FALSE : SDL_TRUE);
+    }
+    else
+    {
+        // If external window, must ask its dimensions instead of trying to set them
+        SDL_GetWindowSize(impl_->window_, &newWidth, &newHeight);
+        newFullscreen = false;
+    }
+}
+
+bool Graphics::CreateDevice(int width, int height, int multiSample)
+{
+    // Device needs only to be created once
+    if (!impl_->device_)
+    {
+        D3D11CreateDevice(
+            0,
+            D3D_DRIVER_TYPE_HARDWARE,
+            0,
+            0,
+            0,
+            0,
+            D3D11_SDK_VERSION,
+            &impl_->device_,
+            0,
+            &impl_->deviceContext_
+        );
+
+        if (!impl_->device_ || !impl_->deviceContext_)
+        {
+            LOGERROR("Failed to create D3D11 device");
+            return false;
+        }
+
+        CheckFeatureSupport();
+        // Set the flush mode now as the device has been created
+        SetFlushGPU(flushGPU_);
+    }
+
+    // Check that multisample level is supported
+    PODVector<int> multiSampleLevels = GetMultiSampleLevels();
+    if (!multiSampleLevels.Contains(multiSample))
+        multiSample = 1;
+
+    // Create swap chain. Release old if necessary
+    if (impl_->swapChain_)
+    {
+        impl_->swapChain_->Release();
+        impl_->swapChain_ = 0;
+    }
+
+    DXGI_SWAP_CHAIN_DESC swapChainDesc;
+    memset(&swapChainDesc, 0, sizeof swapChainDesc);
+    swapChainDesc.BufferCount = 1;
+    swapChainDesc.BufferDesc.Width = width;
+    swapChainDesc.BufferDesc.Height = height;
+    swapChainDesc.BufferDesc.Format = sRGB_ ? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB : DXGI_FORMAT_R8G8B8A8_UNORM;
+    swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+    swapChainDesc.OutputWindow = GetWindowHandle(impl_->window_);
+    swapChainDesc.SampleDesc.Count = multiSample;
+    swapChainDesc.SampleDesc.Quality = multiSample > 1 ? 0xffffffff : 0;
+    swapChainDesc.Windowed = TRUE;
+    swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
+
+    IDXGIDevice* dxgiDevice = 0;
+    impl_->device_->QueryInterface(IID_IDXGIDevice, (void **)&dxgiDevice);
+    IDXGIAdapter* dxgiAdapter = 0;
+    dxgiDevice->GetParent(IID_IDXGIAdapter, (void **)&dxgiAdapter);
+    IDXGIFactory* dxgiFactory = 0;
+    dxgiAdapter->GetParent(IID_IDXGIFactory, (void **)&dxgiFactory);
+    dxgiFactory->CreateSwapChain(impl_->device_, &swapChainDesc, &impl_->swapChain_);
+    // After creating the swap chain, disable automatic Alt-Enter fullscreen/windowed switching
+    // (the application will switch manually if it wants to)
+    dxgiFactory->MakeWindowAssociation(GetWindowHandle(impl_->window_), DXGI_MWA_NO_ALT_ENTER);
+
+    dxgiFactory->Release();
+    dxgiAdapter->Release();
+    dxgiDevice->Release();
+
+    if (impl_->swapChain_)
+    {
+        multiSample_ = multiSample;
+        return true;
+    }
+    else
+    {
+        LOGERROR("Failed to create D3D11 swap chain");
+        return false;
+    }
+}
+
+bool Graphics::UpdateSwapChain(int width, int height)
+{
+    bool success = true;
+
+    ID3D11RenderTargetView* nullView = 0;
+    impl_->deviceContext_->OMSetRenderTargets(1, &nullView, 0);
+    if (impl_->defaultRenderTargetView_)
+    {
+        impl_->defaultRenderTargetView_->Release();
+        impl_->defaultRenderTargetView_ = 0;
+    }
+    if (impl_->defaultDepthStencilView_)
+    {
+        impl_->defaultDepthStencilView_->Release();
+        impl_->defaultDepthStencilView_ = 0;
+    }
+    if (impl_->defaultDepthTexture_)
+    {
+        impl_->defaultDepthTexture_->Release();
+        impl_->defaultDepthTexture_ = 0;
+    }
+
+    impl_->depthStencilView_ = 0;
+    for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
+        impl_->renderTargetViews_[i] = 0;
+    renderTargetsDirty_ = true;
+
+    impl_->swapChain_->ResizeBuffers(1, width, height, DXGI_FORMAT_UNKNOWN, DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH);
+
+    // Create default rendertarget view representing the backbuffer
+    ID3D11Texture2D* backbufferTexture;
+    impl_->swapChain_->GetBuffer(0, IID_ID3D11Texture2D, (void**)&backbufferTexture);
+    if (backbufferTexture)
+    {
+        impl_->device_->CreateRenderTargetView(backbufferTexture, 0, &impl_->defaultRenderTargetView_);
+        backbufferTexture->Release();
+    }
+    else
+    {
+        LOGERROR("Failed to get backbuffer texture");
+        success = false;
+    }
+
+    // Create default depth-stencil texture and view
+    D3D11_TEXTURE2D_DESC depthDesc;
+    memset(&depthDesc, 0, sizeof depthDesc);
+    depthDesc.Width = width;
+    depthDesc.Height = height;
+    depthDesc.MipLevels = 1;
+    depthDesc.ArraySize = 1;
+    depthDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
+    depthDesc.SampleDesc.Count = multiSample_;
+    depthDesc.SampleDesc.Quality = multiSample_ > 1 ? 0xffffffff : 0;
+    depthDesc.Usage = D3D11_USAGE_DEFAULT;
+    depthDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
+    depthDesc.CPUAccessFlags = 0;
+    depthDesc.MiscFlags = 0;
+    impl_->device_->CreateTexture2D(&depthDesc, 0, &impl_->defaultDepthTexture_);
+    if (impl_->defaultDepthTexture_)
+        impl_->device_->CreateDepthStencilView(impl_->defaultDepthTexture_, 0, &impl_->defaultDepthStencilView_);
+    else
+    {
+        LOGERROR("Failed to create backbuffer depth-stencil texture");
+        success = false;
+    }
+
+    // Update internally held backbuffer size
+    width_ = width;
+    height_ = height;
+
+    ResetRenderTargets();
+    return success;
+}
+
+void Graphics::CheckFeatureSupport()
+{
+    lightPrepassSupport_ = true;
+    deferredSupport_ = true;
+    hardwareShadowSupport_ = true;
+    instancingSupport_ = true;
+    shadowMapFormat_ = DXGI_FORMAT_R16_TYPELESS;
+    hiresShadowMapFormat_ = DXGI_FORMAT_R32_TYPELESS;
+    dummyColorFormat_ = DXGI_FORMAT_UNKNOWN;
+    sRGBSupport_ = true;
+    sRGBWriteSupport_ = true;
+    
+    SendEvent(E_GRAPHICSFEATURES);
+}
+
+void Graphics::ResetCachedState()
+{
+    for (unsigned i = 0; i < MAX_VERTEX_STREAMS; ++i)
+    {
+        vertexBuffers_[i] = 0;
+        elementMasks_[i] = 0;
+        impl_->vertexBuffers_[i] = 0;
+        impl_->vertexSizes_[i] = 0;
+        impl_->vertexOffsets_[i] = 0;
+    }
+    
+    for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
+    {
+        textures_[i] = 0;
+        impl_->shaderResourceViews_[i] = 0;
+        impl_->samplers_[i] = 0;
+    }
+
+    for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
+    {
+        renderTargets_[i] = 0;
+        impl_->renderTargetViews_[i] = 0;
+    }
+
+    for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS; ++i)
+    {
+        impl_->constantBuffers_[VS][i] = 0;
+        impl_->constantBuffers_[PS][i] = 0;
+    }
+    
+    depthStencil_ = 0;
+    impl_->depthStencilView_ = 0;
+    viewport_ = IntRect(0, 0, width_, height_);
+    
+    indexBuffer_ = 0;
+    vertexDeclarationHash_ = 0;
+    primitiveType_ = 0;
+    vertexShader_ = 0;
+    pixelShader_ = 0;
+    shaderProgram_ = 0;
+    blendMode_ = BLEND_REPLACE;
+    textureAnisotropy_ = 1;
+    colorWrite_ = true;
+    cullMode_ = CULL_CCW;
+    constantDepthBias_ = 0.0f;
+    slopeScaledDepthBias_ = 0.0f;
+    depthTestMode_ = CMP_LESSEQUAL;
+    depthWrite_ = true;
+    fillMode_ = FILL_SOLID;
+    scissorTest_ = false;
+    scissorRect_ = IntRect::ZERO;
+    stencilTest_ = false;
+    stencilTestMode_ = CMP_ALWAYS;
+    stencilPass_ = OP_KEEP;
+    stencilFail_ = OP_KEEP;
+    stencilZFail_ = OP_KEEP;
+    stencilRef_ = 0;
+    stencilCompareMask_ = M_MAX_UNSIGNED;
+    stencilWriteMask_ = M_MAX_UNSIGNED;
+    useClipPlane_ = false;
+    renderTargetsDirty_ = true;
+    texturesDirty_ = true;
+    vertexDeclarationDirty_ = true;
+    blendStateDirty_ = true;
+    depthStateDirty_ = true;
+    rasterizerStateDirty_ = true;
+    scissorRectDirty_ = true;
+    stencilRefDirty_ = true;
+    blendStateHash_ = M_MAX_UNSIGNED;
+    depthStateHash_ = M_MAX_UNSIGNED;
+    rasterizerStateHash_ = M_MAX_UNSIGNED;
+    firstDirtyTexture_ = lastDirtyTexture_ = M_MAX_UNSIGNED;
+    firstDirtyVB_ = lastDirtyVB_ = M_MAX_UNSIGNED;
+    dirtyConstantBuffers_.Clear();
+}
+
+void Graphics::PrepareDraw()
+{
+    if (renderTargetsDirty_)
+    {
+        impl_->depthStencilView_ = depthStencil_ ? (ID3D11DepthStencilView*)depthStencil_->GetRenderTargetView() : impl_->defaultDepthStencilView_;
+
+        for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
+            impl_->renderTargetViews_[i] = renderTargets_[i] ? (ID3D11RenderTargetView*)renderTargets_[i]->GetRenderTargetView() : 0;
+        // If rendertarget 0 is null and not doing depth-only rendering, render to the backbuffer
+        if (!renderTargets_[0] && !depthStencil_)
+            impl_->renderTargetViews_[0] = impl_->defaultRenderTargetView_;
+        
+        impl_->deviceContext_->OMSetRenderTargets(MAX_RENDERTARGETS, &impl_->renderTargetViews_[0], impl_->depthStencilView_);
+        renderTargetsDirty_ = false;
+    }
+
+    if (texturesDirty_ && firstDirtyTexture_ < M_MAX_UNSIGNED)
+    {
+        // Set also VS textures to enable vertex texture fetch to work the same way as on OpenGL
+        impl_->deviceContext_->VSSetShaderResources(firstDirtyTexture_, lastDirtyTexture_ - firstDirtyTexture_ + 1,
+            &impl_->shaderResourceViews_[firstDirtyTexture_]);
+        impl_->deviceContext_->VSSetSamplers(firstDirtyTexture_, lastDirtyTexture_ - firstDirtyTexture_ + 1,
+            &impl_->samplers_[firstDirtyTexture_]);
+        impl_->deviceContext_->PSSetShaderResources(firstDirtyTexture_, lastDirtyTexture_ - firstDirtyTexture_ + 1,
+            &impl_->shaderResourceViews_[firstDirtyTexture_]);
+        impl_->deviceContext_->PSSetSamplers(firstDirtyTexture_, lastDirtyTexture_ - firstDirtyTexture_ + 1,
+            &impl_->samplers_[firstDirtyTexture_]);
+
+        firstDirtyTexture_ = lastDirtyTexture_ = M_MAX_UNSIGNED;
+        texturesDirty_ = false;
+    }
+
+    if (vertexDeclarationDirty_ && vertexShader_ && vertexShader_->GetByteCode().Size())
+    {
+        if (firstDirtyVB_ < M_MAX_UNSIGNED)
+        {
+            impl_->deviceContext_->IASetVertexBuffers(firstDirtyVB_, lastDirtyVB_ - firstDirtyVB_ + 1, &impl_->vertexBuffers_
+                [firstDirtyVB_], &impl_->vertexSizes_[firstDirtyVB_], &impl_->vertexOffsets_[firstDirtyVB_]);
+
+            firstDirtyVB_ = lastDirtyVB_ = M_MAX_UNSIGNED;
+        }
+
+        unsigned long long newVertexDeclarationHash = 0;
+        for (unsigned i = 0; i < MAX_VERTEX_STREAMS; ++i)
+            newVertexDeclarationHash |= (unsigned long long)elementMasks_[i] << (i * 13);
+
+        // Do not create input layout if no vertex buffers / elements
+        if (newVertexDeclarationHash)
+        {
+            newVertexDeclarationHash |= (unsigned long long)vertexShader_->GetElementMask() << 51;
+            if (newVertexDeclarationHash != vertexDeclarationHash_)
+            {
+                HashMap<unsigned long long, SharedPtr<VertexDeclaration> >::Iterator i = vertexDeclarations_.Find(newVertexDeclarationHash);
+                if (i == vertexDeclarations_.End())
+                {
+                    SharedPtr<VertexDeclaration> newVertexDeclaration(new VertexDeclaration(this, vertexShader_, vertexBuffers_,
+                        elementMasks_));
+                    i = vertexDeclarations_.Insert(MakePair(newVertexDeclarationHash, newVertexDeclaration));
+                }
+    
+                impl_->deviceContext_->IASetInputLayout((ID3D11InputLayout*)i->second_->GetInputLayout());
+                vertexDeclarationHash_ = newVertexDeclarationHash;
+            }
+        }
+
+        vertexDeclarationDirty_ = false;
+    }
+
+    if (blendStateDirty_)
+    {
+        unsigned newBlendStateHash = (colorWrite_ ? 1 : 0) | (blendMode_ << 1);
+        if (newBlendStateHash != blendStateHash_)
+        {
+            HashMap<unsigned, ID3D11BlendState*>::Iterator i = impl_->blendStates_.Find(newBlendStateHash);
+            if (i == impl_->blendStates_.End())
+            {
+                PROFILE(CreateBlendState);
+
+                D3D11_BLEND_DESC stateDesc;
+                memset(&stateDesc, 0, sizeof stateDesc);
+                stateDesc.AlphaToCoverageEnable = false;
+                stateDesc.IndependentBlendEnable = false;
+                stateDesc.RenderTarget[0].BlendEnable = d3dBlendEnable[blendMode_];
+                stateDesc.RenderTarget[0].SrcBlend = d3dSrcBlend[blendMode_];
+                stateDesc.RenderTarget[0].DestBlend = d3dDestBlend[blendMode_];
+                stateDesc.RenderTarget[0].BlendOp = d3dBlendOp[blendMode_];
+                stateDesc.RenderTarget[0].SrcBlendAlpha = d3dSrcBlend[blendMode_];
+                stateDesc.RenderTarget[0].DestBlendAlpha = d3dDestBlend[blendMode_];
+                stateDesc.RenderTarget[0].BlendOpAlpha = d3dBlendOp[blendMode_];
+                stateDesc.RenderTarget[0].RenderTargetWriteMask = colorWrite_ ? D3D11_COLOR_WRITE_ENABLE_ALL : 0x0;
+
+                ID3D11BlendState* newBlendState = 0;
+                impl_->device_->CreateBlendState(&stateDesc, &newBlendState);
+                if (!newBlendState)
+                    LOGERROR("Failed to create blend state");
+
+                i = impl_->blendStates_.Insert(MakePair(newBlendStateHash, newBlendState));
+            }
+
+            impl_->deviceContext_->OMSetBlendState(i->second_, 0, M_MAX_UNSIGNED);
+            blendStateHash_ = newBlendStateHash;
+        }
+
+        blendStateDirty_ = false;
+    }
+
+    if (depthStateDirty_)
+    {
+        unsigned newDepthStateHash = (depthWrite_ ? 1 : 0) | (stencilTest_ ? 2 : 0) | (depthTestMode_ << 2) |
+            ((stencilCompareMask_ & 0xff) << 5) | ((stencilWriteMask_ & 0xff) << 13) | (stencilTestMode_ << 21) |
+            ((stencilFail_ + stencilZFail_ * 5 + stencilPass_ * 25) << 24);
+        if (newDepthStateHash != depthStateHash_ || stencilRefDirty_)
+        {
+            HashMap<unsigned, ID3D11DepthStencilState*>::Iterator i = impl_->depthStates_.Find(newDepthStateHash);
+            if (i == impl_->depthStates_.End())
+            {
+                PROFILE(CreateDepthState);
+
+                D3D11_DEPTH_STENCIL_DESC stateDesc;
+                memset(&stateDesc, 0, sizeof stateDesc);
+                stateDesc.DepthEnable = TRUE;
+                stateDesc.DepthWriteMask = depthWrite_ ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO;
+                stateDesc.DepthFunc = d3dCmpFunc[depthTestMode_];
+                stateDesc.StencilEnable = stencilTest_ ? TRUE : FALSE;
+                stateDesc.StencilReadMask = (unsigned char)stencilCompareMask_;
+                stateDesc.StencilWriteMask = (unsigned char)stencilWriteMask_;
+                stateDesc.FrontFace.StencilFailOp = d3dStencilOp[stencilFail_];
+                stateDesc.FrontFace.StencilDepthFailOp = d3dStencilOp[stencilZFail_];
+                stateDesc.FrontFace.StencilPassOp = d3dStencilOp[stencilPass_];
+                stateDesc.FrontFace.StencilFunc = d3dCmpFunc[stencilTestMode_];
+                stateDesc.BackFace.StencilFailOp = d3dStencilOp[stencilFail_];
+                stateDesc.BackFace.StencilDepthFailOp = d3dStencilOp[stencilZFail_];
+                stateDesc.BackFace.StencilPassOp = d3dStencilOp[stencilPass_];
+                stateDesc.BackFace.StencilFunc = d3dCmpFunc[stencilTestMode_];
+
+                ID3D11DepthStencilState* newDepthState = 0;
+                impl_->device_->CreateDepthStencilState(&stateDesc, &newDepthState);
+                if (!newDepthState)
+                    LOGERROR("Failed to create depth state");
+
+                i = impl_->depthStates_.Insert(MakePair(newDepthStateHash, newDepthState));
+            }
+
+            impl_->deviceContext_->OMSetDepthStencilState(i->second_, stencilRef_);
+            depthStateHash_ = newDepthStateHash;
+        }
+        
+        depthStateDirty_ = false;
+        stencilRefDirty_ = false;
+    }
+
+    if (rasterizerStateDirty_)
+    {
+        unsigned depthBits = 24;
+        if (depthStencil_ && depthStencil_->GetParentTexture()->GetFormat() == DXGI_FORMAT_R16_TYPELESS)
+            depthBits = 16;
+        int scaledDepthBias = (int)(constantDepthBias_ * (1 << depthBits));
+
+        unsigned newRasterizerStateHash = (scissorTest_ ? 1 : 0) | (fillMode_ << 1) | (cullMode_ << 3) |
+            ((scaledDepthBias & 0x1fff) << 5) | ((*((unsigned*)&slopeScaledDepthBias_) & 0x1fff) << 18);
+        if (newRasterizerStateHash != rasterizerStateHash_)
+        {
+            HashMap<unsigned, ID3D11RasterizerState*>::Iterator i = impl_->rasterizerStates_.Find(newRasterizerStateHash);
+            if (i == impl_->rasterizerStates_.End())
+            {
+                PROFILE(CreateRasterizerState);
+
+                D3D11_RASTERIZER_DESC stateDesc;
+                memset(&stateDesc, 0, sizeof stateDesc);
+                stateDesc.FillMode = d3dFillMode[fillMode_];
+                stateDesc.CullMode = d3dCullMode[cullMode_];
+                stateDesc.FrontCounterClockwise = FALSE;
+                stateDesc.DepthBias = scaledDepthBias;
+                stateDesc.DepthBiasClamp = M_INFINITY;
+                stateDesc.SlopeScaledDepthBias = slopeScaledDepthBias_;
+                stateDesc.DepthClipEnable = TRUE;
+                stateDesc.ScissorEnable = scissorTest_ ? TRUE : FALSE;
+                stateDesc.MultisampleEnable = TRUE;
+                stateDesc.AntialiasedLineEnable = FALSE;
+
+                ID3D11RasterizerState* newRasterizerState = 0;
+                impl_->device_->CreateRasterizerState(&stateDesc, &newRasterizerState);
+                if (!newRasterizerState)
+                    LOGERROR("Failed to create rasterizer state");
+
+                i = impl_->rasterizerStates_.Insert(MakePair(newRasterizerStateHash, newRasterizerState));
+            }
+
+            impl_->deviceContext_->RSSetState(i->second_);
+            rasterizerStateHash_ = newRasterizerStateHash;
+        }
+
+        rasterizerStateDirty_ = false;
+    }
+
+    if (scissorRectDirty_)
+    {
+        D3D11_RECT d3dRect;
+        d3dRect.left = scissorRect_.left_;
+        d3dRect.top = scissorRect_.top_;
+        d3dRect.right = scissorRect_.right_;
+        d3dRect.bottom = scissorRect_.bottom_;
+        impl_->deviceContext_->RSSetScissorRects(1, &d3dRect);
+        scissorRectDirty_ = false;
+    }
+
+    for (unsigned i = 0; i < dirtyConstantBuffers_.Size(); ++i)
+        dirtyConstantBuffers_[i]->Apply();
+    dirtyConstantBuffers_.Clear();
+}
+
+void Graphics::SetTextureUnitMappings()
+{
+    textureUnits_["DiffMap"] = TU_DIFFUSE;
+    textureUnits_["DiffCubeMap"] = TU_DIFFUSE;
+    textureUnits_["NormalMap"] = TU_NORMAL;
+    textureUnits_["SpecMap"] = TU_SPECULAR;
+    textureUnits_["EmissiveMap"] = TU_EMISSIVE;
+    textureUnits_["EnvMap"] = TU_ENVIRONMENT;
+    textureUnits_["EnvCubeMap"] = TU_ENVIRONMENT;
+    textureUnits_["LightRampMap"] = TU_LIGHTRAMP;
+    textureUnits_["LightSpotMap"] = TU_LIGHTSHAPE;
+    textureUnits_["LightCubeMap"]  = TU_LIGHTSHAPE;
+    textureUnits_["ShadowMap"] = TU_SHADOWMAP;
+    textureUnits_["FaceSelectCubeMap"] = TU_FACESELECT;
+    textureUnits_["IndirectionCubeMap"] = TU_INDIRECTION;
+    textureUnits_["VolumeMap"] = TU_VOLUMEMAP;
+    textureUnits_["ZoneCubeMap"] = TU_ZONE;
+    textureUnits_["ZoneVolumeMap"] = TU_ZONE;
+}
+
+void RegisterGraphicsLibrary(Context* context)
+{
+    Animation::RegisterObject(context);
+    Material::RegisterObject(context);
+    Model::RegisterObject(context);
+    Shader::RegisterObject(context);
+    Technique::RegisterObject(context);
+    Texture2D::RegisterObject(context);
+    Texture3D::RegisterObject(context);
+    TextureCube::RegisterObject(context);
+    Camera::RegisterObject(context);
+    Drawable::RegisterObject(context);
+    Light::RegisterObject(context);
+    StaticModel::RegisterObject(context);
+    StaticModelGroup::RegisterObject(context);
+    Skybox::RegisterObject(context);
+    AnimatedModel::RegisterObject(context);
+    AnimationController::RegisterObject(context);
+    BillboardSet::RegisterObject(context);
+    ParticleEffect::RegisterObject(context);
+    ParticleEmitter::RegisterObject(context);
+    CustomGeometry::RegisterObject(context);
+    DecalSet::RegisterObject(context);
+    Terrain::RegisterObject(context);
+    TerrainPatch::RegisterObject(context);
+    DebugRenderer::RegisterObject(context);
+    Octree::RegisterObject(context);
+    Zone::RegisterObject(context);
+}
+
+}

+ 638 - 0
Source/Urho3D/Graphics/Direct3D11/D3D11Graphics.h

@@ -0,0 +1,638 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../../Container/ArrayPtr.h"
+#include "../../Math/Color.h"
+#include "../../Container/HashSet.h"
+#include "../../Resource/Image.h"
+#include "../../Core/Mutex.h"
+#include "../../Core/Object.h"
+#include "../../Math/Plane.h"
+#include "../../Math/Rect.h"
+#include "../../Graphics/GraphicsDefs.h"
+#include "../../Graphics/ShaderVariation.h"
+
+namespace Urho3D
+{
+
+class ConstantBuffer;
+class File;
+class Image;
+class IndexBuffer;
+class GPUObject;
+class GraphicsImpl;
+class RenderSurface;
+class Shader;
+class ShaderPrecache;
+class ShaderProgram;
+class ShaderVariation;
+class Texture;
+class Texture2D;
+class TextureCube;
+class Vector3;
+class Vector4;
+class VertexBuffer;
+class VertexDeclaration;
+
+struct ShaderParameter;
+
+/// CPU-side scratch buffer for vertex data updates.
+struct ScratchBuffer
+{
+    ScratchBuffer() :
+        size_(0),
+        reserved_(false)
+    {
+    }
+    
+    /// Buffer data.
+    SharedArrayPtr<unsigned char> data_;
+    /// Data size.
+    unsigned size_;
+    /// Reserved flag.
+    bool reserved_;
+};
+
+typedef HashMap<Pair<ShaderVariation*, ShaderVariation*>, SharedPtr<ShaderProgram> > ShaderProgramMap;
+
+/// %Graphics subsystem. Manages the application window, rendering state and GPU resources.
+class URHO3D_API Graphics : public Object
+{
+    OBJECT(Graphics);
+    
+public:
+    /// Construct.
+    Graphics(Context* context);
+    /// Destruct. Release the Direct3D11 device and close the window.
+    virtual ~Graphics();
+    
+    /// Set external window handle. Only effective before setting the initial screen mode.
+    void SetExternalWindow(void* window);
+    /// Set window title.
+    void SetWindowTitle(const String& windowTitle);
+    /// Set window icon.
+    void SetWindowIcon(Image* windowIcon);
+    /// Set window position. Sets initial position if window is not created yet.
+    void SetWindowPosition(const IntVector2& position);
+    /// Set window position. Sets initial position if window is not created yet.
+    void SetWindowPosition(int x, int y);
+    /// Set screen mode. Return true if successful.
+    bool SetMode(int width, int height, bool fullscreen, bool borderless, bool resizable, bool vsync, bool tripleBuffer, int multiSample);
+    /// Set screen resolution only. Return true if successful.
+    bool SetMode(int width, int height);
+    /// Set whether the main window uses sRGB conversion on write.
+    void SetSRGB(bool enable);
+    /// Set whether to flush the GPU command buffer to prevent multiple frames being queued and uneven frame timesteps. Default off, may decrease performance if enabled.
+    void SetFlushGPU(bool enable);
+    /// Set allowed screen orientations as a space-separated list of "LandscapeLeft", "LandscapeRight", "Portrait" and "PortraitUpsideDown". Affects currently only iOS platform.
+    void SetOrientations(const String& orientations);
+    /// Toggle between full screen and windowed mode. Return true if successful.
+    bool ToggleFullscreen();
+    /// Close the window.
+    void Close();
+    /// Take a screenshot. Return true if successful.
+    bool TakeScreenShot(Image& destImage);
+    /// Begin frame rendering. Return true if device available and can render.
+    bool BeginFrame();
+    /// End frame rendering and swap buffers.
+    void EndFrame();
+    /// Clear any or all of rendertarget, depth buffer and stencil buffer.
+    void Clear(unsigned flags, const Color& color = Color(0.0f, 0.0f, 0.0f, 0.0f), float depth = 1.0f, unsigned stencil = 0);
+    /// Resolve multisampled backbuffer to a texture rendertarget. The texture's size should match the viewport size.
+    bool ResolveToTexture(Texture2D* destination, const IntRect& viewport);
+    /// Draw non-indexed geometry.
+    void Draw(PrimitiveType type, unsigned vertexStart, unsigned vertexCount);
+    /// Draw indexed geometry.
+    void Draw(PrimitiveType type, unsigned indexStart, unsigned indexCount, unsigned minVertex, unsigned vertexCount);
+    /// Draw indexed, instanced geometry. An instancing vertex buffer must be set.
+    void DrawInstanced(PrimitiveType type, unsigned indexStart, unsigned indexCount, unsigned minVertex, unsigned vertexCount, unsigned instanceCount);
+    /// Set vertex buffer.
+    void SetVertexBuffer(VertexBuffer* buffer);
+    /// Set multiple vertex buffers.
+    bool SetVertexBuffers(const PODVector<VertexBuffer*>& buffers, const PODVector<unsigned>& elementMasks, unsigned instanceOffset = 0);
+    /// Set multiple vertex buffers.
+    bool SetVertexBuffers(const Vector<SharedPtr<VertexBuffer> >& buffers, const PODVector<unsigned>& elementMasks, unsigned instanceOffset = 0);
+    /// Set index buffer.
+    void SetIndexBuffer(IndexBuffer* buffer);
+    /// Set shaders.
+    void SetShaders(ShaderVariation* vs, ShaderVariation* ps);
+    /// Set shader float constants.
+    void SetShaderParameter(StringHash param, const float* data, unsigned count);
+    /// Set shader float constant.
+    void SetShaderParameter(StringHash param, float value);
+    /// Set shader boolean constant.
+    void SetShaderParameter(StringHash param, bool value);
+    /// Set shader color constant.
+    void SetShaderParameter(StringHash param, const Color& color);
+    /// Set shader 2D vector constant.
+    void SetShaderParameter(StringHash param, const Vector2& vector);
+    /// Set shader 3x3 matrix constant.
+    void SetShaderParameter(StringHash param, const Matrix3& matrix);
+    /// Set shader 3D vector constant.
+    void SetShaderParameter(StringHash param, const Vector3& vector);
+    /// Set shader 4x4 matrix constant.
+    void SetShaderParameter(StringHash param, const Matrix4& matrix);
+    /// Set shader 4D vector constant.
+    void SetShaderParameter(StringHash param, const Vector4& vector);
+    /// Set shader 3x4 matrix constant.
+    void SetShaderParameter(StringHash param, const Matrix3x4& matrix);
+    /// Set shader constant from a variant. Supported variant types: bool, float, vector2, vector3, vector4, color.
+    void SetShaderParameter(StringHash param, const Variant& value);
+    /// Check whether a shader parameter group needs update. Does not actually check whether parameters exist in the shaders.
+    bool NeedParameterUpdate(ShaderParameterGroup group, const void* source);
+    /// Check whether a shader parameter exists on the currently set shaders.
+    bool HasShaderParameter(StringHash param);
+    /// Check whether the current pixel shader uses a texture unit.
+    bool HasTextureUnit(TextureUnit unit);
+    /// Clear remembered shader parameter source group.
+    void ClearParameterSource(ShaderParameterGroup group);
+    /// Clear remembered shader parameter sources.
+    void ClearParameterSources();
+    /// Clear remembered transform shader parameter sources.
+    void ClearTransformSources();
+    /// Set texture.
+    void SetTexture(unsigned index, Texture* texture);
+    /// Set default texture filtering mode.
+    void SetDefaultTextureFilterMode(TextureFilterMode mode);
+    /// Set texture anisotropy.
+    void SetTextureAnisotropy(unsigned level);
+    /// Dirty texture parameters of all textures (when global settings change.)
+    void SetTextureParametersDirty();
+    /// Reset all rendertargets, depth-stencil surface and viewport.
+    void ResetRenderTargets();
+    /// Reset specific rendertarget.
+    void ResetRenderTarget(unsigned index);
+    /// Reset depth-stencil surface.
+    void ResetDepthStencil();
+    /// Set rendertarget.
+    void SetRenderTarget(unsigned index, RenderSurface* renderTarget);
+    /// Set rendertarget.
+    void SetRenderTarget(unsigned index, Texture2D* texture);
+    /// Set depth-stencil surface.
+    void SetDepthStencil(RenderSurface* depthStencil);
+    /// Set depth-stencil surface.
+    void SetDepthStencil(Texture2D* texture);
+    /// Set viewport.
+    void SetViewport(const IntRect& rect);
+    /// Set blending mode.
+    void SetBlendMode(BlendMode mode);
+    /// Set color write on/off.
+    void SetColorWrite(bool enable);
+    /// Set hardware culling mode.
+    void SetCullMode(CullMode mode);
+    /// Set depth bias.
+    void SetDepthBias(float constantBias, float slopeScaledBias);
+    /// Set depth compare.
+    void SetDepthTest(CompareMode mode);
+    /// Set depth write on/off.
+    void SetDepthWrite(bool enable);
+    /// Set polygon fill mode.
+    void SetFillMode(FillMode mode);
+    /// Set scissor test.
+    void SetScissorTest(bool enable, const Rect& rect = Rect::FULL, bool borderInclusive = true);
+    /// Set scissor test.
+    void SetScissorTest(bool enable, const IntRect& rect);
+    /// Set stencil test.
+    void SetStencilTest(bool enable, CompareMode mode = CMP_ALWAYS, StencilOp pass = OP_KEEP, StencilOp fail = OP_KEEP, StencilOp zFail = OP_KEEP, unsigned stencilRef = 0, unsigned compareMask = M_MAX_UNSIGNED, unsigned writeMask = M_MAX_UNSIGNED);
+    /// Set a custom clipping plane. The plane is specified in world space, but is dependent on the view and projection matrices.
+    void SetClipPlane(bool enable, const Plane& clipPlane = Plane::UP, const Matrix3x4& view = Matrix3x4::IDENTITY, const Matrix4& projection = Matrix4::IDENTITY);
+    /// Begin dumping shader variation names to an XML file for precaching.
+    void BeginDumpShaders(const String& fileName);
+    /// End dumping shader variations names.
+    void EndDumpShaders();
+    /// Precache shader variations from an XML file generated with BeginDumpShaders().
+    void PrecacheShaders(Deserializer& source);
+    
+    /// Return whether rendering initialized.
+    bool IsInitialized() const;
+    /// Return graphics implementation, which holds the actual API-specific resources.
+    GraphicsImpl* GetImpl() const { return impl_; }
+    /// Return OS-specific external window handle. Null if not in use.
+    void* GetExternalWindow() const { return externalWindow_; }
+    /// Return window title.
+    const String& GetWindowTitle() const { return windowTitle_; }
+    /// Return graphics API name.
+    const String& GetApiName() const { return apiName_; }
+    /// Return window position.
+    IntVector2 GetWindowPosition() const;
+    /// Return window width.
+    int GetWidth() const { return width_; }
+    /// Return window height.
+    int GetHeight() const { return height_; }
+    /// Return multisample mode (1 = no multisampling.)
+    int GetMultiSample() const { return multiSample_; }
+    /// Return whether window is fullscreen.
+    bool GetFullscreen() const { return fullscreen_; }
+    /// Return whether window is resizable.
+    bool GetResizable() const { return resizable_; }
+    /// Return whether window is borderless.
+    bool GetBorderless() const { return borderless_; }
+    /// Return whether vertical sync is on.
+    bool GetVSync() const { return vsync_; }
+    /// Return whether triple buffering is enabled.
+    bool GetTripleBuffer() const { return tripleBuffer_; }
+    /// Return whether the main window is using sRGB conversion on write.
+    bool GetSRGB() const { return sRGB_; }
+    /// Return whether the GPU command buffer is flushed each frame.
+    bool GetFlushGPU() const { return flushGPU_; }
+    /// Return allowed screen orientations.
+    const String& GetOrientations() const { return orientations_; }
+    /// Return whether Direct3D device is lost, and can not yet render. Always false on D3D11.
+    bool IsDeviceLost() const { return false; }
+    /// Return number of primitives drawn this frame.
+    unsigned GetNumPrimitives() const { return numPrimitives_; }
+    /// Return number of batches drawn this frame.
+    unsigned GetNumBatches() const { return numBatches_; }
+    /// Return dummy color texture format for shadow maps. Is "NULL" (consume no video memory) if supported.
+    unsigned GetDummyColorFormat() const { return dummyColorFormat_; }
+    /// Return shadow map depth texture format, or 0 if not supported.
+    unsigned GetShadowMapFormat() const { return shadowMapFormat_; }
+    /// Return 24-bit shadow map depth texture format, or 0 if not supported.
+    unsigned GetHiresShadowMapFormat() const { return hiresShadowMapFormat_; }
+    /// Return whether hardware instancing is supported..
+    bool GetInstancingSupport() const { return instancingSupport_; }
+    /// Return whether light pre-pass rendering is supported.
+    bool GetLightPrepassSupport() const { return lightPrepassSupport_; }
+    /// Return whether deferred rendering is supported.
+    bool GetDeferredSupport() const { return deferredSupport_; }
+    /// Return whether shadow map depth compare is done in hardware.
+    bool GetHardwareShadowSupport() const { return hardwareShadowSupport_; }
+    /// Return whether a readable hardware depth format is available.
+    bool GetReadableDepthSupport() const { return GetReadableDepthFormat() != 0; }
+    /// Return whether sRGB conversion on texture sampling is supported.
+    bool GetSRGBSupport() const { return sRGBSupport_; }
+    /// Return whether sRGB conversion on rendertarget writing is supported.
+    bool GetSRGBWriteSupport() const { return sRGBWriteSupport_; }
+    /// Return supported fullscreen resolutions.
+    PODVector<IntVector2> GetResolutions() const;
+    /// Return supported multisampling levels.
+    PODVector<int> GetMultiSampleLevels() const;
+    /// Return the desktop resolution.
+    IntVector2 GetDesktopResolution() const;
+    /// Return hardware format for a compressed image format, or 0 if unsupported.
+    unsigned GetFormat(CompressedFormat format) const;
+    /// Return a shader variation by name and defines.
+    ShaderVariation* GetShader(ShaderType type, const String& name, const String& defines = String::EMPTY) const;
+    /// Return a shader variation by name and defines.
+    ShaderVariation* GetShader(ShaderType type, const char* name, const char* defines) const;
+    /// Return current vertex buffer by index.
+    VertexBuffer* GetVertexBuffer(unsigned index) const;
+    /// Return current index buffer.
+    IndexBuffer* GetIndexBuffer() const { return indexBuffer_; }
+    /// Return current vertex shader.
+    ShaderVariation* GetVertexShader() const { return vertexShader_; }
+    /// Return current pixel shader.
+    ShaderVariation* GetPixelShader() const { return pixelShader_; }
+    /// Return texture unit index by name.
+    TextureUnit GetTextureUnit(const String& name);
+    /// Return texture unit name by index.
+    const String& GetTextureUnitName(TextureUnit unit);
+    /// Return current texture by texture unit index.
+    Texture* GetTexture(unsigned index) const;
+    /// Return default texture filtering mode.
+    TextureFilterMode GetDefaultTextureFilterMode() const { return defaultTextureFilterMode_; }
+    /// Return current rendertarget by index.
+    RenderSurface* GetRenderTarget(unsigned index) const;
+    /// Return current depth-stencil surface.
+    RenderSurface* GetDepthStencil() const { return depthStencil_; }
+    /// Return the viewport coordinates.
+    IntRect GetViewport() const { return viewport_; }
+    /// Return texture anisotropy.
+    unsigned GetTextureAnisotropy() const { return textureAnisotropy_; }
+    /// Return blending mode.
+    BlendMode GetBlendMode() const { return blendMode_; }
+    /// Return whether color write is enabled.
+    bool GetColorWrite() const { return colorWrite_; }
+    /// Return hardware culling mode.
+    CullMode GetCullMode() const { return cullMode_; }
+    /// Return depth constant bias.
+    float GetDepthConstantBias() const { return constantDepthBias_; }
+    /// Return depth slope scaled bias.
+    float GetDepthSlopeScaledBias() const { return slopeScaledDepthBias_; }
+    /// Return depth compare mode.
+    CompareMode GetDepthTest() const { return depthTestMode_; }
+    /// Return whether depth write is enabled.
+    bool GetDepthWrite() const { return depthWrite_; }
+    /// Return polygon fill mode.
+    FillMode GetFillMode() const { return fillMode_; }
+    /// Return whether stencil test is enabled.
+    bool GetStencilTest() const { return stencilTest_; }
+    /// Return whether scissor test is enabled.
+    bool GetScissorTest() const { return scissorTest_; }
+    /// Return scissor rectangle coordinates.
+    const IntRect& GetScissorRect() const { return scissorRect_; }
+    /// Return stencil compare mode.
+    CompareMode GetStencilTestMode() const { return stencilTestMode_; }
+    /// Return stencil operation to do if stencil test passes.
+    StencilOp GetStencilPass() const { return stencilPass_; }
+    /// Return stencil operation to do if stencil test fails.
+    StencilOp GetStencilFail() const { return stencilFail_; }
+    /// Return stencil operation to do if depth compare fails.
+    StencilOp GetStencilZFail() const { return stencilZFail_; }
+    /// Return stencil reference value.
+    unsigned GetStencilRef() const { return stencilRef_; }
+    /// Return stencil compare bitmask.
+    unsigned GetStencilCompareMask() const { return stencilCompareMask_; }
+    /// Return stencil write bitmask.
+    unsigned GetStencilWriteMask() const { return stencilWriteMask_; }
+    /// Return whether a custom clipping plane is in use.
+    bool GetUseClipPlane() const { return useClipPlane_; }
+    /// Return rendertarget width and height.
+    IntVector2 GetRenderTargetDimensions() const;
+    
+    /// Window was resized through user interaction. Called by Input subsystem.
+    void WindowResized();
+    /// Window was moved through user interaction. Called by Input subsystem.
+    void WindowMoved();
+    /// Maximize the Window.
+    void Maximize();
+    /// Minimize the Window.
+    void Minimize();
+    /// Add a GPU object to keep track of. Called by GPUObject.
+    void AddGPUObject(GPUObject* object);
+    /// Remove a GPU object. Called by GPUObject.
+    void RemoveGPUObject(GPUObject* object);
+    /// Reserve a CPU-side scratch buffer.
+    void* ReserveScratchBuffer(unsigned size);
+    /// Free a CPU-side scratch buffer.
+    void FreeScratchBuffer(void* buffer);
+    /// Clean up too large scratch buffers.
+    void CleanupScratchBuffers();
+    /// Clean up shader parameters when a shader variation is released or destroyed.
+    void CleanUpShaderPrograms(ShaderVariation* variation);
+    /// Get or create a constant buffer. Will be shared between shaders if possible.
+    ConstantBuffer* GetOrCreateConstantBuffer(ShaderType type, unsigned index, unsigned size);
+
+    /// Return the API-specific alpha texture format.
+    static unsigned GetAlphaFormat();
+    /// Return the API-specific luminance texture format.
+    static unsigned GetLuminanceFormat();
+    /// Return the API-specific luminance alpha texture format.
+    static unsigned GetLuminanceAlphaFormat();
+    /// Return the API-specific RGB texture format.
+    static unsigned GetRGBFormat();
+    /// Return the API-specific RGBA texture format.
+    static unsigned GetRGBAFormat();
+    /// Return the API-specific RGBA 16-bit texture format.
+    static unsigned GetRGBA16Format();
+    /// Return the API-specific RGBA 16-bit float texture format.
+    static unsigned GetRGBAFloat16Format();
+    /// Return the API-specific RGBA 32-bit float texture format.
+    static unsigned GetRGBAFloat32Format();
+    /// Return the API-specific RG 16-bit texture format.
+    static unsigned GetRG16Format();
+    /// Return the API-specific RG 16-bit float texture format.
+    static unsigned GetRGFloat16Format();
+    /// Return the API-specific RG 32-bit float texture format.
+    static unsigned GetRGFloat32Format();
+    /// Return the API-specific single channel 16-bit float texture format.
+    static unsigned GetFloat16Format();
+    /// Return the API-specific single channel 32-bit float texture format.
+    static unsigned GetFloat32Format();
+    /// Return the API-specific linear depth texture format.
+    static unsigned GetLinearDepthFormat();
+    /// Return the API-specific hardware depth-stencil texture format.
+    static unsigned GetDepthStencilFormat();
+    /// Return the API-specific readable hardware depth format, or 0 if not supported.
+    static unsigned GetReadableDepthFormat();
+    /// Return the API-specific texture format from a textual description, for example "rgb".
+    static unsigned GetFormat(const String& formatName);
+    /// Return UV offset required for pixel perfect rendering.
+    static const Vector2& GetPixelUVOffset() { return pixelUVOffset; }
+    /// Return maximum number of supported bones for skinning.
+    static unsigned GetMaxBones() { return 128; }
+
+private:
+    /// Create the application window.
+    bool OpenWindow(int width, int height, bool resizable, bool borderless);
+    /// Create the application window icon.
+    void CreateWindowIcon();
+    /// Adjust the window for new resolution and fullscreen mode.
+    void AdjustWindow(int& newWidth, int& newHeight, bool& newFullscreen, bool& newBorderless);
+    /// Create the D3D11 device and swap chain. Requires an open window. Can also be called again to recreate swap chain. Return true on success.
+    bool CreateDevice(int width, int height, int multiSample);
+    /// Update swap chain state for a new mode and create views for the backbuffer & default depth buffer. Return true on success.
+    bool UpdateSwapChain(int width, int height);
+    /// Check supported rendering features.
+    void CheckFeatureSupport();
+    /// Reset cached rendering state.
+    void ResetCachedState();
+    /// Initialize texture unit mappings.
+    void SetTextureUnitMappings();
+    /// Process dirtied state before draw.
+    void PrepareDraw();
+    
+    /// Mutex for accessing the GPU objects vector from several threads.
+    Mutex gpuObjectMutex_;
+    /// Implementation.
+    GraphicsImpl* impl_;
+    /// Window title.
+    String windowTitle_;
+    /// Window Icon File Name
+    Image* windowIcon_;
+    /// External window, null if not in use (default.)
+    void* externalWindow_;
+    /// Window width.
+    int width_;
+    /// Window height.
+    int height_;
+    /// Window position.
+    IntVector2 position_;
+    /// Multisampling mode.
+    int multiSample_;
+    /// Fullscreen flag.
+    bool fullscreen_;
+    /// Borderless flag.
+    bool borderless_;
+    /// Resizable flag.
+    bool resizable_;
+    /// Vertical sync flag.
+    bool vsync_;
+    /// Triple buffering flag.
+    bool tripleBuffer_;
+    /// Flush GPU command buffer flag.
+    bool flushGPU_;
+    /// sRGB conversion on write flag for the main window.
+    bool sRGB_;
+    /// Light pre-pass rendering support flag.
+    bool lightPrepassSupport_;
+    /// Deferred rendering support flag.
+    bool deferredSupport_;
+    /// Hardware shadow map depth compare support flag.
+    bool hardwareShadowSupport_;
+    /// Instancing support flag.
+    bool instancingSupport_;
+    /// sRGB conversion on read support flag.
+    bool sRGBSupport_;
+    /// sRGB conversion on write support flag.
+    bool sRGBWriteSupport_;
+    /// Number of primitives this frame.
+    unsigned numPrimitives_;
+    /// Number of batches this frame.
+    unsigned numBatches_;
+    /// Largest scratch buffer request this frame.
+    unsigned maxScratchBufferRequest_;
+    /// GPU objects.
+    PODVector<GPUObject*> gpuObjects_;
+    /// Scratch buffers.
+    Vector<ScratchBuffer> scratchBuffers_;
+    /// Shadow map dummy color texture format.
+    unsigned dummyColorFormat_;
+    /// Shadow map depth texture format.
+    unsigned shadowMapFormat_;
+    /// Shadow map 24-bit depth texture format.
+    unsigned hiresShadowMapFormat_;
+    /// Vertex buffers in use.
+    VertexBuffer* vertexBuffers_[MAX_VERTEX_STREAMS];
+    /// Element masks by vertex buffer.
+    unsigned elementMasks_[MAX_VERTEX_STREAMS];
+    /// Index buffer in use.
+    IndexBuffer* indexBuffer_;
+    /// Current vertex declaration hash.
+    unsigned long long vertexDeclarationHash_;
+    /// Current primitive type.
+    unsigned primitiveType_;
+    /// Vertex shader in use.
+    ShaderVariation* vertexShader_;
+    /// Pixel shader in use.
+    ShaderVariation* pixelShader_;
+    /// Textures in use.
+    Texture* textures_[MAX_TEXTURE_UNITS];
+    /// Texture unit mappings.
+    HashMap<String, TextureUnit> textureUnits_;
+    /// Rendertargets in use.
+    RenderSurface* renderTargets_[MAX_RENDERTARGETS];
+    /// Depth-stencil surface in use.
+    RenderSurface* depthStencil_;
+    /// Viewport coordinates.
+    IntRect viewport_;
+    /// Texture anisotropy level.
+    unsigned textureAnisotropy_;
+    /// Blending mode.
+    BlendMode blendMode_;
+    /// Color write enable.
+    bool colorWrite_;
+    /// Hardware culling mode.
+    CullMode cullMode_;
+    /// Depth constant bias.
+    float constantDepthBias_;
+    /// Depth slope scaled bias.
+    float slopeScaledDepthBias_;
+    /// Depth compare mode.
+    CompareMode depthTestMode_;
+    /// Depth write enable flag.
+    bool depthWrite_;
+    /// Polygon fill mode.
+    FillMode fillMode_;
+    /// Scissor test rectangle.
+    IntRect scissorRect_;
+    /// Scissor test enable flag.
+    bool scissorTest_;
+    /// Stencil test compare mode.
+    CompareMode stencilTestMode_;
+    /// Stencil operation on pass.
+    StencilOp stencilPass_;
+    /// Stencil operation on fail.
+    StencilOp stencilFail_;
+    /// Stencil operation on depth fail.
+    StencilOp stencilZFail_;
+    /// Stencil test reference value.
+    unsigned stencilRef_;
+    /// Stencil compare bitmask.
+    unsigned stencilCompareMask_;
+    /// Stencil write bitmask.
+    unsigned stencilWriteMask_;
+    /// Current custom clip plane in post-projection space.
+    Vector4 clipPlane_;
+    /// Stencil test enable flag.
+    bool stencilTest_;
+    /// Custom clip plane enable flag.
+    bool useClipPlane_;
+    /// Rendertargets dirty flag.
+    bool renderTargetsDirty_;
+    /// Textures dirty flag.
+    bool texturesDirty_;
+    /// Vertex declaration dirty flag.
+    bool vertexDeclarationDirty_;
+    /// Blend state dirty flag.
+    bool blendStateDirty_;
+    /// Depth state dirty flag.
+    bool depthStateDirty_;
+    /// Rasterizer state dirty flag.
+    bool rasterizerStateDirty_;
+    /// Scissor rect dirty flag.
+    bool scissorRectDirty_;
+    /// Stencil ref dirty flag.
+    bool stencilRefDirty_;
+    /// Hash of current blend state.
+    unsigned blendStateHash_;
+    /// Hash of current depth state.
+    unsigned depthStateHash_;
+    /// Hash of current rasterizer state.
+    unsigned rasterizerStateHash_;
+    /// First dirtied texture unit.
+    unsigned firstDirtyTexture_;
+    /// Last dirtied texture unit.
+    unsigned lastDirtyTexture_;
+    /// First dirtied vertex buffer.
+    unsigned firstDirtyVB_;
+    /// Last dirtied vertex buffer.
+    unsigned lastDirtyVB_;
+    /// Default texture filtering mode.
+    TextureFilterMode defaultTextureFilterMode_;
+    /// Vertex declarations.
+    HashMap<unsigned long long, SharedPtr<VertexDeclaration> > vertexDeclarations_;
+    /// Constant buffers.
+    HashMap<unsigned, SharedPtr<ConstantBuffer> > constantBuffers_;
+    /// Currently dirty constant buffers.
+    PODVector<ConstantBuffer*> dirtyConstantBuffers_;
+    /// Shader programs.
+    ShaderProgramMap shaderPrograms_;
+    /// Shader program in use.
+    ShaderProgram* shaderProgram_;
+    /// Remembered shader parameter sources.
+    const void* shaderParameterSources_[MAX_SHADER_PARAMETER_GROUPS];
+    /// Base directory for shaders.
+    String shaderPath_;
+    /// File extension for shaders.
+    String shaderExtension_;
+    /// Last used shader in shader variation query.
+    mutable WeakPtr<Shader> lastShader_;
+    /// Last used shader name in shader variation query.
+    mutable String lastShaderName_;
+    /// Shader precache utility.
+    SharedPtr<ShaderPrecache> shaderPrecache_;
+    /// Allowed screen orientations.
+    String orientations_;
+    /// Graphics API name.
+    String apiName_;
+
+    /// Pixel perfect UV offset.
+    static const Vector2 pixelUVOffset;
+};
+
+/// Register Graphics library objects.
+void URHO3D_API RegisterGraphicsLibrary(Context* context);
+
+}

+ 65 - 0
Source/Urho3D/Graphics/Direct3D11/D3D11GraphicsImpl.cpp

@@ -0,0 +1,65 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../../Graphics/Graphics.h"
+#include "../../Graphics/GraphicsEvents.h"
+#include "../../Graphics/GraphicsImpl.h"
+
+#include "../../DebugNew.h"
+
+namespace Urho3D
+{
+
+GraphicsImpl::GraphicsImpl() :
+    window_(0),
+    device_(0),
+    deviceContext_(0),
+    swapChain_(0),
+    defaultRenderTargetView_(0),
+    defaultDepthTexture_(0),
+    defaultDepthStencilView_(0),
+    depthStencilView_(0)
+{
+    for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
+        renderTargetViews_[i] = 0;
+
+    for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
+    {
+        shaderResourceViews_[i] = 0;
+        samplers_[i] = 0;
+    }
+
+    for (unsigned i = 0; i < MAX_VERTEX_STREAMS; ++i)
+    {
+        vertexBuffers_[i] = 0;
+        vertexSizes_[i] = 0;
+        vertexOffsets_[i] = 0;
+    }
+
+    for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS; ++i)
+    {
+        constantBuffers_[VS][i] = 0;
+        constantBuffers_[PS][i] = 0;
+    }
+}
+
+}

+ 92 - 0
Source/Urho3D/Graphics/Direct3D11/D3D11GraphicsImpl.h

@@ -0,0 +1,92 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../../Math/Color.h"
+#include "../../Graphics/GraphicsDefs.h"
+
+#include <d3d11.h>
+#include <dxgi.h>
+#include <SDL/SDL.h>
+
+namespace Urho3D
+{
+
+/// %Graphics implementation. Holds API-specific objects.
+class URHO3D_API GraphicsImpl
+{
+    friend class Graphics;
+    
+public:
+    /// Construct.
+    GraphicsImpl();
+    
+    /// Return Direct3D device.
+    ID3D11Device* GetDevice() const { return device_; }
+    /// Return Direct3D immediate device context.
+    ID3D11DeviceContext* GetDeviceContext() const { return deviceContext_; }
+    /// Return swapchain.
+    IDXGISwapChain* GetSwapChain() const { return swapChain_; }
+    /// Return window.
+    SDL_Window* GetWindow() const { return window_; }
+    
+private:
+    /// SDL window.
+    SDL_Window* window_;
+    /// Graphics device.
+    ID3D11Device* device_;
+    /// Immediate device context.
+    ID3D11DeviceContext* deviceContext_;
+    /// Swap chain.
+    IDXGISwapChain* swapChain_;
+    /// Default (backbuffer) rendertarget view.
+    ID3D11RenderTargetView* defaultRenderTargetView_;
+    /// Default depth-stencil texture.
+    ID3D11Texture2D* defaultDepthTexture_;
+    /// Default depth-stencil view.
+    ID3D11DepthStencilView* defaultDepthStencilView_;
+    /// Current color rendertarget views.
+    ID3D11RenderTargetView* renderTargetViews_[MAX_RENDERTARGETS];
+    /// Current depth-stencil view.
+    ID3D11DepthStencilView* depthStencilView_;
+    /// Created blend state objects.
+    HashMap<unsigned, ID3D11BlendState*> blendStates_;
+    /// Created depth state objects.
+    HashMap<unsigned, ID3D11DepthStencilState*> depthStates_;
+    /// Created rasterizer state objects.
+    HashMap<unsigned, ID3D11RasterizerState*> rasterizerStates_;
+    /// Bound shader resource views.
+    ID3D11ShaderResourceView* shaderResourceViews_[MAX_TEXTURE_UNITS];
+    /// Bound sampler state objects.
+    ID3D11SamplerState* samplers_[MAX_TEXTURE_UNITS];
+    /// Bound vertex buffers.
+    ID3D11Buffer* vertexBuffers_[MAX_VERTEX_STREAMS];
+    /// Bound constant buffers.
+    ID3D11Buffer* constantBuffers_[2][MAX_SHADER_PARAMETER_GROUPS];
+    /// Vertex sizes per buffer.
+    unsigned vertexSizes_[MAX_VERTEX_STREAMS];
+    /// Vertex stream offsets per buffer.
+    unsigned vertexOffsets_[MAX_VERTEX_STREAMS];
+};
+
+}

+ 391 - 0
Source/Urho3D/Graphics/Direct3D11/D3D11IndexBuffer.cpp

@@ -0,0 +1,391 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../../Core/Context.h"
+#include "../../Graphics/Graphics.h"
+#include "../../Graphics/GraphicsImpl.h"
+#include "../../Graphics/IndexBuffer.h"
+#include "../../IO/Log.h"
+
+#include "../../DebugNew.h"
+
+namespace Urho3D
+{
+
+IndexBuffer::IndexBuffer(Context* context) :
+    Object(context),
+    GPUObject(GetSubsystem<Graphics>()),
+    indexCount_(0),
+    indexSize_(0),
+    lockState_(LOCK_NONE),
+    lockStart_(0),
+    lockCount_(0),
+    lockScratchData_(0),
+    dynamic_(false),
+    shadowed_(false)
+{
+    // Force shadowing mode if graphics subsystem does not exist
+    if (!graphics_)
+        shadowed_ = true;
+}
+
+IndexBuffer::~IndexBuffer()
+{
+    Release();
+}
+
+void IndexBuffer::Release()
+{
+    Unlock();
+    
+    if (object_)
+    {
+        if (!graphics_)
+            return;
+        
+        if (graphics_->GetIndexBuffer() == this)
+            graphics_->SetIndexBuffer(0);
+        
+        ((ID3D11Buffer*)object_)->Release();
+        object_ = 0;
+    }
+}
+
+void IndexBuffer::SetShadowed(bool enable)
+{
+    // If no graphics subsystem, can not disable shadowing
+    if (!graphics_)
+        enable = true;
+    
+    if (enable != shadowed_)
+    {
+        if (enable && indexCount_ && indexSize_)
+            shadowData_ = new unsigned char[indexCount_ * indexSize_];
+        else
+            shadowData_.Reset();
+        
+        shadowed_ = enable;
+    }
+}
+
+bool IndexBuffer::SetSize(unsigned indexCount, bool largeIndices, bool dynamic)
+{
+    Unlock();
+    
+    dynamic_ = dynamic;
+    indexCount_ = indexCount;
+    indexSize_ = largeIndices ? sizeof(unsigned) : sizeof(unsigned short);
+    
+    if (shadowed_ && indexCount_ && indexSize_)
+        shadowData_ = new unsigned char[indexCount_ * indexSize_];
+    else
+        shadowData_.Reset();
+    
+    return Create();
+}
+
+bool IndexBuffer::SetData(const void* data)
+{
+    if (!data)
+    {
+        LOGERROR("Null pointer for index buffer data");
+        return false;
+    }
+    
+    if (!indexSize_)
+    {
+        LOGERROR("Index size not defined, can not set index buffer data");
+        return false;
+    }
+    
+    if (shadowData_ && data != shadowData_.Get())
+        memcpy(shadowData_.Get(), data, indexCount_ * indexSize_);
+    
+    if (object_)
+    {
+        if (dynamic_)
+        {
+            void* hwData = MapBuffer(0, indexCount_, true);
+            if (hwData)
+            {
+                memcpy(hwData, data, indexCount_ * indexSize_);
+                UnmapBuffer();
+            }
+            else
+                return false;
+        }
+        else
+        {
+            D3D11_BOX destBox;
+            destBox.left = 0;
+            destBox.right = indexCount_ * indexSize_;
+            destBox.top = 0;
+            destBox.bottom = 1;
+            destBox.front = 0;
+            destBox.back = 1;
+
+            graphics_->GetImpl()->GetDeviceContext()->UpdateSubresource((ID3D11Buffer*)object_, 0, &destBox, data, 0, 0);
+        }
+    }
+    
+    return true;
+}
+
+bool IndexBuffer::SetDataRange(const void* data, unsigned start, unsigned count, bool discard)
+{
+    if (start == 0 && count == indexCount_)
+        return SetData(data);
+    
+    if (!data)
+    {
+        LOGERROR("Null pointer for index buffer data");
+        return false;
+    }
+    
+    if (!indexSize_)
+    {
+        LOGERROR("Index size not defined, can not set index buffer data");
+        return false;
+    }
+    
+    if (start + count > indexCount_)
+    {
+        LOGERROR("Illegal range for setting new index buffer data");
+        return false;
+    }
+    
+    if (!count)
+        return true;
+    
+    if (shadowData_ && shadowData_.Get() + start * indexSize_ != data)
+        memcpy(shadowData_.Get() + start * indexSize_, data, count * indexSize_);
+    
+    if (object_)
+    {
+        if (dynamic_)
+        {
+            void* hwData = MapBuffer(start, count, discard);
+            if (hwData)
+            {
+                memcpy(hwData, data, count * indexSize_);
+                UnmapBuffer();
+            }
+            else
+                return false;
+        }
+        else
+        {
+            D3D11_BOX destBox;
+            destBox.left = start * indexSize_;
+            destBox.right = destBox.left + count * indexSize_;
+            destBox.top = 0;
+            destBox.bottom = 1;
+            destBox.front = 0;
+            destBox.back = 1;
+
+            graphics_->GetImpl()->GetDeviceContext()->UpdateSubresource((ID3D11Buffer*)object_, 0, &destBox, data, 0, 0);
+        }
+    }
+
+    return true;
+}
+
+void* IndexBuffer::Lock(unsigned start, unsigned count, bool discard)
+{
+    if (lockState_ != LOCK_NONE)
+    {
+        LOGERROR("Index buffer already locked");
+        return 0;
+    }
+    
+    if (!indexSize_)
+    {
+        LOGERROR("Index size not defined, can not lock index buffer");
+        return 0;
+    }
+    
+    if (start + count > indexCount_)
+    {
+        LOGERROR("Illegal range for locking index buffer");
+        return 0;
+    }
+    
+    if (!count)
+        return 0;
+    
+    lockStart_ = start;
+    lockCount_ = count;
+    
+    // Because shadow data must be kept in sync, can only lock hardware buffer if not shadowed
+    if (object_ && !shadowData_ && dynamic_)
+        return MapBuffer(start, count, discard);
+    else if (shadowData_)
+    {
+        lockState_ = LOCK_SHADOW;
+        return shadowData_.Get() + start * indexSize_;
+    }
+    else if (graphics_)
+    {
+        lockState_ = LOCK_SCRATCH;
+        lockScratchData_ = graphics_->ReserveScratchBuffer(count * indexSize_);
+        return lockScratchData_;
+    }
+    else
+        return 0;
+}
+
+void IndexBuffer::Unlock()
+{
+    switch (lockState_)
+    {
+    case LOCK_HARDWARE:
+        UnmapBuffer();
+        break;
+        
+    case LOCK_SHADOW:
+        SetDataRange(shadowData_.Get() + lockStart_ * indexSize_, lockStart_, lockCount_);
+        lockState_ = LOCK_NONE;
+        break;
+        
+    case LOCK_SCRATCH:
+        SetDataRange(lockScratchData_, lockStart_, lockCount_);
+        if (graphics_)
+            graphics_->FreeScratchBuffer(lockScratchData_);
+        lockScratchData_ = 0;
+        lockState_ = LOCK_NONE;
+        break;
+    }
+}
+
+bool IndexBuffer::GetUsedVertexRange(unsigned start, unsigned count, unsigned& minVertex, unsigned& vertexCount)
+{
+    if (!shadowData_)
+    {
+        LOGERROR("Used vertex range can only be queried from an index buffer with shadow data");
+        return false;
+    }
+    
+    if (start + count > indexCount_)
+    {
+        LOGERROR("Illegal index range for querying used vertices");
+        return false;
+    }
+    
+    minVertex = M_MAX_UNSIGNED;
+    unsigned maxVertex = 0;
+    
+    if (indexSize_ == sizeof(unsigned))
+    {
+        unsigned* indices = ((unsigned*)shadowData_.Get()) + start;
+        
+        for (unsigned i = 0; i < count; ++i)
+        {
+            if (indices[i] < minVertex)
+                minVertex = indices[i];
+            if (indices[i] > maxVertex)
+                maxVertex = indices[i];
+        }
+    }
+    else
+    {
+        unsigned short* indices = ((unsigned short*)shadowData_.Get()) + start;
+        
+        for (unsigned i = 0; i < count; ++i)
+        {
+            if (indices[i] < minVertex)
+                minVertex = indices[i];
+            if (indices[i] > maxVertex)
+                maxVertex = indices[i];
+        }
+    }
+    
+    vertexCount = maxVertex - minVertex + 1;
+    return true;
+}
+
+bool IndexBuffer::Create()
+{
+    Release();
+    
+    if (!indexCount_)
+        return true;
+    
+    if (graphics_)
+    {
+        D3D11_BUFFER_DESC bufferDesc;
+        memset(&bufferDesc, 0, sizeof bufferDesc);
+        bufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
+        bufferDesc.CPUAccessFlags = dynamic_ ? D3D11_CPU_ACCESS_WRITE : 0;
+        bufferDesc.Usage = dynamic_ ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT;
+        bufferDesc.ByteWidth = (unsigned)(indexCount_ * indexSize_);
+
+        graphics_->GetImpl()->GetDevice()->CreateBuffer(&bufferDesc, 0, (ID3D11Buffer**)&object_);
+
+        if (!object_)
+        {
+            LOGERROR("Failed to create index buffer");
+            return false;
+        }
+    }
+    
+    return true;
+}
+
+bool IndexBuffer::UpdateToGPU()
+{
+    if (object_ && shadowData_)
+        return SetData(shadowData_.Get());
+    else
+        return false;
+}
+
+void* IndexBuffer::MapBuffer(unsigned start, unsigned count, bool discard)
+{
+    void* hwData = 0;
+    
+    if (object_)
+    {
+        D3D11_MAPPED_SUBRESOURCE mappedData;
+        mappedData.pData = 0;
+
+        graphics_->GetImpl()->GetDeviceContext()->Map((ID3D11Buffer*)object_, 0, discard ? D3D11_MAP_WRITE_DISCARD :
+            D3D11_MAP_WRITE, 0, &mappedData);
+        hwData = mappedData.pData;
+        if (!hwData)
+            LOGERROR("Failed to map index buffer");
+        else
+            lockState_ = LOCK_HARDWARE;
+    }
+    
+    return hwData;
+}
+
+void IndexBuffer::UnmapBuffer()
+{
+    if (object_ && lockState_ == LOCK_HARDWARE)
+    {
+        graphics_->GetImpl()->GetDeviceContext()->Unmap((ID3D11Buffer*)object_, 0);
+        lockState_ = LOCK_NONE;
+    }
+}
+
+}

+ 107 - 0
Source/Urho3D/Graphics/Direct3D11/D3D11IndexBuffer.h

@@ -0,0 +1,107 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../../Graphics/GPUObject.h"
+#include "../../Core/Object.h"
+#include "../../Graphics/GraphicsDefs.h"
+#include "../../Container/ArrayPtr.h"
+
+namespace Urho3D
+{
+
+/// Hardware index buffer.
+class URHO3D_API IndexBuffer : public Object, public GPUObject
+{
+    OBJECT(IndexBuffer);
+    
+public:
+    /// Construct.
+    IndexBuffer(Context* context);
+    /// Destruct.
+    virtual ~IndexBuffer();
+    
+    /// Release buffer.
+    virtual void Release();
+    
+    /// Enable shadowing in CPU memory. Shadowing is forced on if the graphics subsystem does not exist.
+    void SetShadowed(bool enable);
+    /// Set size and vertex elements and dynamic mode. Previous data will be lost.
+    bool SetSize(unsigned indexCount, bool largeIndices, bool dynamic = false);
+    /// Set all data in the buffer.
+    bool SetData(const void* data);
+    /// Set a data range in the buffer. Optionally discard data outside the range.
+    bool SetDataRange(const void* data, unsigned start, unsigned count, bool discard = false);
+    /// Lock the buffer for write-only editing. Return data pointer if successful. Optionally discard data outside the range.
+    void* Lock(unsigned start, unsigned count, bool discard = false);
+    /// Unlock the buffer and apply changes to the GPU buffer.
+    void Unlock();
+    
+    /// Return whether CPU memory shadowing is enabled.
+    bool IsShadowed() const { return shadowed_; }
+    /// Return whether is dynamic.
+    bool IsDynamic() const { return dynamic_; }
+    /// Return whether is currently locked.
+    bool IsLocked() const { return lockState_ != LOCK_NONE; }
+    /// Return number of indices.
+    unsigned GetIndexCount() const {return indexCount_; }
+    /// Return index size.
+    unsigned GetIndexSize() const { return indexSize_; }
+    /// Return used vertex range from index range.
+    bool GetUsedVertexRange(unsigned start, unsigned count, unsigned& minVertex, unsigned& vertexCount);
+    /// Return CPU memory shadow data.
+    unsigned char* GetShadowData() const { return shadowData_.Get(); }
+    /// Return shared array pointer to the CPU memory shadow data.
+    SharedArrayPtr<unsigned char> GetShadowDataShared() const { return shadowData_; }
+
+private:
+    /// Create buffer.
+    bool Create();
+    /// Update the shadow data to the GPU buffer.
+    bool UpdateToGPU();
+    /// Map the GPU buffer into CPU memory.
+    void* MapBuffer(unsigned start, unsigned count, bool discard);
+    /// Unmap the GPU buffer.
+    void UnmapBuffer();
+    
+    /// Shadow data.
+    SharedArrayPtr<unsigned char> shadowData_;
+    /// Number of indices.
+    unsigned indexCount_;
+    /// Index size.
+    unsigned indexSize_;
+    /// Buffer locking state.
+    LockState lockState_;
+    /// Lock start vertex.
+    unsigned lockStart_;
+    /// Lock number of vertices.
+    unsigned lockCount_;
+    /// Scratch buffer for fallback locking.
+    void* lockScratchData_;
+    /// Dynamic flag.
+    bool dynamic_;
+    /// Shadowed flag.
+    bool shadowed_;
+};
+
+}

+ 155 - 0
Source/Urho3D/Graphics/Direct3D11/D3D11RenderSurface.cpp

@@ -0,0 +1,155 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../../Graphics/Camera.h"
+#include "../../Graphics/Graphics.h"
+#include "../../Graphics/GraphicsImpl.h"
+#include "../../IO/Log.h"
+#include "../../Graphics/Renderer.h"
+#include "../../Graphics/RenderSurface.h"
+#include "../../Scene/Scene.h"
+#include "../../Graphics/Texture.h"
+
+#include "../../DebugNew.h"
+
+namespace Urho3D
+{
+
+RenderSurface::RenderSurface(Texture* parentTexture) :
+    parentTexture_(parentTexture),
+    renderTargetView_(0),
+    updateMode_(SURFACE_UPDATEVISIBLE),
+    updateQueued_(false)
+{
+}
+
+RenderSurface::~RenderSurface()
+{
+    Release();
+}
+
+void RenderSurface::SetNumViewports(unsigned num)
+{
+    viewports_.Resize(num);
+}
+
+void RenderSurface::SetViewport(unsigned index, Viewport* viewport)
+{
+    if (index >= viewports_.Size())
+        viewports_.Resize(index + 1);
+    
+    viewports_[index] = viewport;
+}
+
+void RenderSurface::SetUpdateMode(RenderSurfaceUpdateMode mode)
+{
+    updateMode_ = mode;
+}
+
+void RenderSurface::SetLinkedRenderTarget(RenderSurface* renderTarget)
+{
+    if (renderTarget != this)
+        linkedRenderTarget_ = renderTarget;
+}
+
+void RenderSurface::SetLinkedDepthStencil(RenderSurface* depthStencil)
+{
+    if (depthStencil != this)
+        linkedDepthStencil_ = depthStencil;
+}
+
+void RenderSurface::QueueUpdate()
+{
+    if (!updateQueued_)
+    {
+        bool hasValidView = false;
+        
+        // Verify that there is at least 1 non-null viewport, as otherwise Renderer will not accept the surface and the update flag
+        // will be left on
+        for (unsigned i = 0; i < viewports_.Size(); ++i)
+        {
+            if (viewports_[i])
+            {
+                hasValidView = true;
+                break;
+            }
+        }
+        
+        if (hasValidView)
+        {
+            Renderer* renderer = parentTexture_->GetSubsystem<Renderer>();
+            if (renderer)
+                renderer->QueueRenderSurface(this);
+            
+            updateQueued_ = true;
+        }
+    }
+}
+
+void RenderSurface::Release()
+{
+    Graphics* graphics = parentTexture_->GetGraphics();
+    if (!graphics)
+        return;
+    
+    if (renderTargetView_)
+    {
+        for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
+        {
+            if (graphics->GetRenderTarget(i) == this)
+                graphics->ResetRenderTarget(i);
+        }
+        
+        if (graphics->GetDepthStencil() == this)
+            graphics->ResetDepthStencil();
+        
+        ((ID3D11View*)renderTargetView_)->Release();
+        renderTargetView_ = 0;
+    }
+}
+
+int RenderSurface::GetWidth() const
+{
+    return parentTexture_->GetWidth();
+}
+
+int RenderSurface::GetHeight() const
+{
+    return parentTexture_->GetHeight();
+}
+
+TextureUsage RenderSurface::GetUsage() const
+{
+    return parentTexture_->GetUsage();
+}
+
+Viewport* RenderSurface::GetViewport(unsigned index) const
+{
+    return index < viewports_.Size() ? viewports_[index] : (Viewport*)0;
+}
+
+void RenderSurface::WasUpdated()
+{
+    updateQueued_ = false;
+}
+
+}

+ 101 - 0
Source/Urho3D/Graphics/Direct3D11/D3D11RenderSurface.h

@@ -0,0 +1,101 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../../Graphics/GraphicsDefs.h"
+#include "../../Graphics/Viewport.h"
+
+namespace Urho3D
+{
+
+class Texture;
+
+/// %Color or depth-stencil surface that can be rendered into.
+class URHO3D_API RenderSurface : public RefCounted
+{
+    friend class Texture2D;
+    friend class TextureCube;
+    
+public:
+    /// Construct with parent texture.
+    RenderSurface(Texture* parentTexture);
+    /// Destruct.
+    ~RenderSurface();
+    
+    /// Set number of viewports.
+    void SetNumViewports(unsigned num);
+    /// Set viewport.
+    void SetViewport(unsigned index, Viewport* viewport);
+    /// Set viewport update mode. Default is to update when visible.
+    void SetUpdateMode(RenderSurfaceUpdateMode mode);
+    /// Set linked color rendertarget.
+    void SetLinkedRenderTarget(RenderSurface* renderTarget);
+    /// Set linked depth-stencil surface.
+    void SetLinkedDepthStencil(RenderSurface* depthStencil);
+    /// Queue manual update of the viewport(s).
+    void QueueUpdate();
+    /// Release surface.
+    void Release();
+    
+    /// Return parent texture.
+    Texture* GetParentTexture() const { return parentTexture_; }
+    /// Return Direct3D rendertarget or depth-stencil view.
+    void* GetRenderTargetView() const { return renderTargetView_; }
+    /// Return width.
+    int GetWidth() const;
+    /// Return height.
+    int GetHeight() const;
+    /// Return usage.
+    TextureUsage GetUsage() const;
+    /// Return number of viewports.
+    unsigned GetNumViewports() const { return viewports_.Size(); }
+    /// Return viewport by index.
+    Viewport* GetViewport(unsigned index) const;
+    /// Return viewport update mode.
+    RenderSurfaceUpdateMode GetUpdateMode() const { return updateMode_; }
+    /// Return linked color rendertarget.
+    RenderSurface* GetLinkedRenderTarget() const { return linkedRenderTarget_; }
+    /// Return linked depth-stencil surface.
+    RenderSurface* GetLinkedDepthStencil() const { return linkedDepthStencil_; }
+    
+    /// Clear update flag. Called by Renderer.
+    void WasUpdated();
+    
+private:
+    /// Parent texture.
+    Texture* parentTexture_;
+    /// Direct3D rendertarget or depth-stencil view.
+    void* renderTargetView_;
+    /// Viewports.
+    Vector<SharedPtr<Viewport> > viewports_;
+    /// Linked color buffer.
+    WeakPtr<RenderSurface> linkedRenderTarget_;
+    /// Linked depth buffer.
+    WeakPtr<RenderSurface> linkedDepthStencil_;
+    /// Update mode for viewports.
+    RenderSurfaceUpdateMode updateMode_;
+    /// Update queued flag.
+    bool updateQueued_;
+};
+
+}

+ 88 - 0
Source/Urho3D/Graphics/Direct3D11/D3D11ShaderProgram.h

@@ -0,0 +1,88 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../../Container/HashMap.h"
+#include "../../Graphics/ConstantBuffer.h"
+#include "../../Graphics/Graphics.h"
+#include "../../Graphics/ShaderVariation.h"
+
+namespace Urho3D
+{
+
+/// Combined information for specific vertex and pixel shaders.
+class URHO3D_API ShaderProgram : public RefCounted
+{
+public:
+    /// Construct.
+    ShaderProgram(Graphics* graphics, ShaderVariation* vertexShader, ShaderVariation* pixelShader)
+    {
+        // Create needed constant buffers
+        const unsigned* vsBufferSizes = vertexShader->GetConstantBufferSizes();
+        for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS; ++i)
+        {
+            if (vsBufferSizes[i])
+                vsConstantBuffers_[i] = graphics->GetOrCreateConstantBuffer(VS, i, vsBufferSizes[i]);
+        }
+
+        const unsigned* psBufferSizes = pixelShader->GetConstantBufferSizes();
+        for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS; ++i)
+        {
+            if (psBufferSizes[i])
+                psConstantBuffers_[i] = graphics->GetOrCreateConstantBuffer(PS, i, psBufferSizes[i]);
+        }
+
+        // Copy parameters. Add direct links to constant buffers.
+        const HashMap<StringHash, ShaderParameter>& vsParams = vertexShader->GetParameters();
+        for (HashMap<StringHash, ShaderParameter>::ConstIterator i = vsParams.Begin(); i != vsParams.End(); ++i)
+        {
+            parameters_[i->first_] = i->second_;
+            parameters_[i->first_].bufferPtr_ = vsConstantBuffers_[i->second_.buffer_].Get();
+        }
+
+        const HashMap<StringHash, ShaderParameter>& psParams = pixelShader->GetParameters();
+        for (HashMap<StringHash, ShaderParameter>::ConstIterator i = psParams.Begin(); i != psParams.End(); ++i)
+        {
+            parameters_[i->first_] = i->second_;
+            parameters_[i->first_].bufferPtr_ = psConstantBuffers_[i->second_.buffer_].Get();
+        }
+
+        // Optimize shader parameter lookup by rehashing to next power of two
+        parameters_.Rehash(NextPowerOfTwo(parameters_.Size()));
+
+    }
+
+    /// Destruct.
+    ~ShaderProgram()
+    {
+    }
+
+    /// Combined parameters from the vertex and pixel shader.
+    HashMap<StringHash, ShaderParameter> parameters_;
+    /// Vertex shader constant buffers.
+    SharedPtr<ConstantBuffer> vsConstantBuffers_[MAX_SHADER_PARAMETER_GROUPS];
+    /// Pixel shader constant buffers.
+    SharedPtr<ConstantBuffer> psConstantBuffers_[MAX_SHADER_PARAMETER_GROUPS];
+};
+
+}

+ 464 - 0
Source/Urho3D/Graphics/Direct3D11/D3D11ShaderVariation.cpp

@@ -0,0 +1,464 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../../IO/File.h"
+#include "../../IO/FileSystem.h"
+#include "../../Graphics/Graphics.h"
+#include "../../Graphics/GraphicsImpl.h"
+#include "../../IO/Log.h"
+#include "../../Resource/ResourceCache.h"
+#include "../../Graphics/Shader.h"
+#include "../../Graphics/ShaderVariation.h"
+#include "../../Graphics/VertexBuffer.h"
+
+#include <windows.h>
+#include <d3dcompiler.h>
+
+#include "../../DebugNew.h"
+
+namespace Urho3D
+{
+
+ShaderVariation::ShaderVariation(Shader* owner, ShaderType type) :
+    GPUObject(owner->GetSubsystem<Graphics>()),
+    owner_(owner),
+    type_(type),
+    elementMask_(0)
+{
+    for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
+        useTextureUnit_[i] = false;
+    for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS; ++i)
+        constantBufferSizes_[i] = 0;
+}
+
+ShaderVariation::~ShaderVariation()
+{
+    Release();
+}
+
+bool ShaderVariation::Create()
+{
+    Release();
+    
+    if (!graphics_)
+        return false;
+    
+    if (!owner_)
+    {
+        compilerOutput_ = "Owner shader has expired";
+        return false;
+    }
+
+    // Check for up-to-date bytecode on disk
+    String path, name, extension;
+    SplitPath(owner_->GetName(), path, name, extension);
+    extension = type_ == VS ? ".vs4" : ".ps4";
+    
+    String binaryShaderName = path + "Cache/" + name + "_" + StringHash(defines_).ToString() + extension;
+    
+    if (!LoadByteCode(binaryShaderName))
+    {
+        // Compile shader if don't have valid bytecode
+        if (!Compile())
+            return false;
+        // Save the bytecode after successful compile, but not if the source is from a package
+        if (owner_->GetTimeStamp())
+            SaveByteCode(binaryShaderName);
+    }
+    
+    // Then create shader from the bytecode
+    ID3D11Device* device = graphics_->GetImpl()->GetDevice();
+    if (type_ == VS)
+    {
+        if (device && byteCode_.Size())
+            device->CreateVertexShader(&byteCode_[0], byteCode_.Size(), 0, (ID3D11VertexShader**)&object_);
+        if (!object_)
+            compilerOutput_ = "Could not create vertex shader";
+    }
+    else
+    {
+        if (device && byteCode_.Size())
+            device->CreatePixelShader(&byteCode_[0], byteCode_.Size(), 0, (ID3D11PixelShader**)&object_);
+        if (!object_)
+            compilerOutput_ = "Could not create pixel shader";
+    }
+
+    return object_ != 0;
+}
+
+void ShaderVariation::Release()
+{
+    if (object_)
+    {
+        if (!graphics_)
+            return;
+        
+        graphics_->CleanUpShaderPrograms(this);
+
+        if (type_ == VS)
+        {
+            if (graphics_->GetVertexShader() == this)
+                graphics_->SetShaders(0, 0);
+            
+            ((ID3D11VertexShader*)object_)->Release();
+        }
+        else
+        {
+            if (graphics_->GetPixelShader() == this)
+                graphics_->SetShaders(0, 0);
+            
+            ((ID3D11PixelShader*)object_)->Release();
+        }
+
+        object_ = 0;
+    }
+    
+    compilerOutput_.Clear();
+    
+    for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
+        useTextureUnit_[i] = false;
+    for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS; ++i)
+        constantBufferSizes_[i] = 0;
+    parameters_.Clear();
+    byteCode_.Clear();
+    elementMask_ = 0;
+}
+
+void ShaderVariation::SetName(const String& name)
+{
+    name_ = name;
+}
+
+void ShaderVariation::SetDefines(const String& defines)
+{
+    defines_ = defines;
+}
+
+Shader* ShaderVariation::GetOwner() const
+{
+    return owner_;
+}
+
+bool ShaderVariation::LoadByteCode(const String& binaryShaderName)
+{
+    ResourceCache* cache = owner_->GetSubsystem<ResourceCache>();
+    if (!cache->Exists(binaryShaderName))
+        return false;
+    
+    FileSystem* fileSystem = owner_->GetSubsystem<FileSystem>();
+    unsigned sourceTimeStamp = owner_->GetTimeStamp();
+    // If source code is loaded from a package, its timestamp will be zero. Else check that binary is not older
+    // than source
+    if (sourceTimeStamp && fileSystem->GetLastModifiedTime(cache->GetResourceFileName(binaryShaderName)) <
+        sourceTimeStamp)
+        return false;
+    
+    SharedPtr<File> file = cache->GetFile(binaryShaderName);
+    if (!file || file->ReadFileID() != "USHD")
+    {
+        LOGERROR(binaryShaderName + " is not a valid shader bytecode file");
+        return false;
+    }
+    
+    /// \todo Check that shader type and model match
+    unsigned short shaderType = file->ReadUShort();
+    unsigned short shaderModel = file->ReadUShort();
+    elementMask_ = file->ReadUInt();
+    
+    unsigned numParameters = file->ReadUInt();
+    for (unsigned i = 0; i < numParameters; ++i)
+    {
+        String name = file->ReadString();
+        unsigned buffer = file->ReadUByte();
+        unsigned offset = file->ReadUInt();
+        unsigned size = file->ReadUInt();
+
+        ShaderParameter parameter(type_, name_, buffer, offset, size);
+        parameters_[StringHash(name)] = parameter;
+    }
+    
+    unsigned numTextureUnits = file->ReadUInt();
+    for (unsigned i = 0; i < numTextureUnits; ++i)
+    {
+        String unitName = file->ReadString();
+        unsigned reg = file->ReadUByte();
+        
+        if (reg < MAX_TEXTURE_UNITS)
+            useTextureUnit_[reg] = true;
+    }
+    
+    unsigned byteCodeSize = file->ReadUInt();
+    if (byteCodeSize)
+    {
+        byteCode_.Resize(byteCodeSize);
+        file->Read(&byteCode_[0], byteCodeSize);
+        
+        if (type_ == VS)
+            LOGDEBUG("Loaded cached vertex shader " + GetFullName());
+        else
+            LOGDEBUG("Loaded cached pixel shader " + GetFullName());
+        
+        CalculateConstantBufferSizes();
+        return true;
+    }
+    else
+    {
+        LOGERROR(binaryShaderName + " has zero length bytecode");
+        return false;
+    }
+}
+
+bool ShaderVariation::Compile()
+{
+    const String& sourceCode = owner_->GetSourceCode(type_);
+    Vector<String> defines = defines_.Split(' ');
+    
+    // Set the entrypoint, profile and flags according to the shader being compiled
+    const char* entryPoint = 0;
+    const char* profile = 0;
+    unsigned flags = D3DCOMPILE_OPTIMIZATION_LEVEL3;
+    
+    defines.Push("D3D11");
+
+    if (type_ == VS)
+    {
+        entryPoint = "VS";
+        defines.Push("COMPILEVS");
+        profile = "vs_4_0";
+    }
+    else
+    {
+        entryPoint = "PS";
+        defines.Push("COMPILEPS");
+        profile = "ps_4_0";
+        flags |= D3DCOMPILE_PREFER_FLOW_CONTROL;
+    }
+
+    defines.Push("MAXBONES=" + String(Graphics::GetMaxBones()));
+    
+    // Collect defines into macros
+    Vector<String> defineValues;
+    PODVector<D3D_SHADER_MACRO> macros;
+    
+    for (unsigned i = 0; i < defines.Size(); ++i)
+    {
+        unsigned equalsPos = defines[i].Find('=');
+        if (equalsPos != String::NPOS)
+        {
+            defineValues.Push(defines[i].Substring(equalsPos + 1));
+            defines[i].Resize(equalsPos);
+        }
+        else
+            defineValues.Push("1");
+    }
+    for (unsigned i = 0; i < defines.Size(); ++i)
+    {
+        D3D_SHADER_MACRO macro;
+        macro.Name = defines[i].CString();
+        macro.Definition = defineValues[i].CString();
+        macros.Push(macro);
+
+        // In debug mode, check that all defines are referenced by the shader code
+        #ifdef _DEBUG
+        if (sourceCode.Find(defines[i]) == String::NPOS)
+            LOGWARNING("Shader " + GetFullName() + " does not use the define " + defines[i]);
+        #endif
+    }
+    
+    D3D_SHADER_MACRO endMacro;
+    endMacro.Name = 0;
+    endMacro.Definition = 0;
+    macros.Push(endMacro);
+    
+    // Compile using D3DCompile
+    ID3DBlob* shaderCode = 0;
+    ID3DBlob* errorMsgs = 0;
+    
+    if (FAILED(D3DCompile(sourceCode.CString(), sourceCode.Length(), owner_->GetName().CString(), &macros.Front(), 0,
+        entryPoint, profile, flags, 0, &shaderCode, &errorMsgs)))
+        compilerOutput_ = String((const char*)errorMsgs->GetBufferPointer(), errorMsgs->GetBufferSize());
+    else
+    {
+        if (type_ == VS)
+            LOGDEBUG("Compiled vertex shader " + GetFullName());
+        else
+            LOGDEBUG("Compiled pixel shader " + GetFullName());
+        
+        unsigned char* bufData = (unsigned char*)shaderCode->GetBufferPointer();
+        unsigned bufSize = shaderCode->GetBufferSize();
+        // Use the original bytecode to reflect the parameters
+        ParseParameters(bufData, bufSize);
+        CalculateConstantBufferSizes();
+
+        // Then strip everything not necessary to use the shader
+        ID3DBlob* strippedCode = 0;
+        D3DStripShader(bufData, bufSize, D3DCOMPILER_STRIP_REFLECTION_DATA | D3DCOMPILER_STRIP_DEBUG_INFO |
+            D3DCOMPILER_STRIP_TEST_BLOBS, &strippedCode);
+        byteCode_.Resize(strippedCode->GetBufferSize());
+        memcpy(&byteCode_[0], strippedCode->GetBufferPointer(), byteCode_.Size());
+        strippedCode->Release();
+    }
+
+    if (shaderCode)
+        shaderCode->Release();
+    if (errorMsgs)
+        errorMsgs->Release();
+    
+    return !byteCode_.Empty();
+}
+
+void ShaderVariation::ParseParameters(unsigned char* bufData, unsigned bufSize)
+{
+    ID3D11ShaderReflection* reflection = 0;
+    D3D11_SHADER_DESC shaderDesc;
+
+    D3DReflect(bufData, bufSize, IID_ID3D11ShaderReflection, (void**)&reflection);
+    if (!reflection)
+    {
+        LOGERROR("Failed to reflect vertex shader's input signature");
+        return;
+    }
+
+    reflection->GetDesc(&shaderDesc);
+
+    if (type_ == VS)
+    {
+        for (unsigned i = 0; i < shaderDesc.InputParameters; ++i)
+        {
+            D3D11_SIGNATURE_PARAMETER_DESC paramDesc;
+            reflection->GetInputParameterDesc((unsigned)i, &paramDesc);
+            for (unsigned j = 0; j < MAX_VERTEX_ELEMENTS; ++j)
+            {
+                if (!String::Compare(paramDesc.SemanticName, VertexBuffer::elementSemantics[j], true) && paramDesc.SemanticIndex ==
+                    VertexBuffer::elementSemanticIndices[j])
+                {
+                    elementMask_ |= (1 << j);
+                    break;
+                }
+            }
+        }
+    }
+
+    HashMap<String, unsigned> cbRegisterMap;
+
+    for (unsigned i = 0; i < shaderDesc.BoundResources; ++i)
+    {
+        D3D11_SHADER_INPUT_BIND_DESC resourceDesc;
+        reflection->GetResourceBindingDesc(i, &resourceDesc);
+        String resourceName(resourceDesc.Name);
+        if (resourceDesc.Type == D3D_SIT_CBUFFER)
+            cbRegisterMap[resourceName] = resourceDesc.BindPoint;
+        else if (type_ == PS && resourceDesc.Type == D3D_SIT_SAMPLER && resourceDesc.BindPoint < MAX_TEXTURE_UNITS)
+            useTextureUnit_[resourceDesc.BindPoint] = true;
+    }
+
+    for (unsigned i = 0; i < shaderDesc.ConstantBuffers; ++i)
+    {
+        ID3D11ShaderReflectionConstantBuffer* cb = reflection->GetConstantBufferByIndex(i);
+        D3D11_SHADER_BUFFER_DESC cbDesc;
+        cb->GetDesc(&cbDesc);
+        unsigned cbRegister = cbRegisterMap[String(cbDesc.Name)];
+
+        for (unsigned j = 0; j < cbDesc.Variables; ++j)
+        {
+            ID3D11ShaderReflectionVariable* var = cb->GetVariableByIndex(j);
+            D3D11_SHADER_VARIABLE_DESC varDesc;
+            var->GetDesc(&varDesc);
+            String varName(varDesc.Name);
+            if (varName[0] == 'c')
+            {
+                varName = varName.Substring(1); // Strip the c to follow Urho3D constant naming convention
+                parameters_[varName] = ShaderParameter(type_, varName, cbRegister, varDesc.StartOffset, varDesc.Size);
+            }
+        }
+    }
+
+    reflection->Release();
+}
+
+void ShaderVariation::SaveByteCode(const String& binaryShaderName)
+{
+    ResourceCache* cache = owner_->GetSubsystem<ResourceCache>();
+    FileSystem* fileSystem = owner_->GetSubsystem<FileSystem>();
+    
+    String path = GetPath(cache->GetResourceFileName(owner_->GetName())) + "Cache/";
+    String fullName = path + GetFileNameAndExtension(binaryShaderName);
+    if (!fileSystem->DirExists(path))
+        fileSystem->CreateDir(path);
+    
+    SharedPtr<File> file(new File(owner_->GetContext(), fullName, FILE_WRITE));
+    if (!file->IsOpen())
+        return;
+    
+    file->WriteFileID("USHD");
+    file->WriteShort((unsigned short)type_);
+    file->WriteShort(4);
+    file->WriteUInt(elementMask_);
+
+    file->WriteUInt(parameters_.Size());
+    for (HashMap<StringHash, ShaderParameter>::ConstIterator i = parameters_.Begin(); i != parameters_.End(); ++i)
+    {
+        file->WriteString(i->second_.name_);
+        file->WriteUByte((unsigned char)i->second_.buffer_);
+        file->WriteUInt(i->second_.offset_);
+        file->WriteUInt(i->second_.size_);
+    }
+    
+    unsigned usedTextureUnits = 0;
+    for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
+    {
+        if (useTextureUnit_[i])
+            ++usedTextureUnits;
+    }
+    file->WriteUInt(usedTextureUnits);
+    for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
+    {
+        if (useTextureUnit_[i])
+        {
+            file->WriteString(graphics_->GetTextureUnitName((TextureUnit)i));
+            file->WriteUByte(i);
+        }
+    }
+    
+    file->WriteUInt(byteCode_.Size());
+    if (byteCode_.Size())
+        file->Write(&byteCode_[0], byteCode_.Size());
+}
+
+void ShaderVariation::CalculateConstantBufferSizes()
+{
+    for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS; ++i)
+        constantBufferSizes_[i] = 0;
+    
+    for (HashMap<StringHash, ShaderParameter>::ConstIterator i = parameters_.Begin(); i != parameters_.End(); ++i)
+    {
+        if (i->second_.buffer_ < MAX_SHADER_PARAMETER_GROUPS)
+        {
+            unsigned oldSize = constantBufferSizes_[i->second_.buffer_];
+            unsigned paramEnd = i->second_.offset_ + i->second_.size_;
+            if (paramEnd > oldSize)
+                constantBufferSizes_[i->second_.buffer_] = paramEnd;
+        }
+    }
+}
+
+}

+ 153 - 0
Source/Urho3D/Graphics/Direct3D11/D3D11ShaderVariation.h

@@ -0,0 +1,153 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../../Graphics/GPUObject.h"
+#include "../../Graphics/GraphicsDefs.h"
+#include "../../Container/HashSet.h"
+#include "../../Container/RefCounted.h"
+#include "../../Container/ArrayPtr.h"
+
+namespace Urho3D
+{
+
+class ConstantBuffer;
+class Shader;
+
+/// %Shader parameter definition.
+struct ShaderParameter
+{
+    /// Construct with defaults.
+    ShaderParameter() :
+        type_(VS),
+        buffer_(0),
+        offset_(0),
+        size_(0),
+        bufferPtr_(0)
+    {
+    }
+    
+    /// Construct with parameters.
+    ShaderParameter(ShaderType type, const String& name, unsigned buffer, unsigned offset, unsigned size, ConstantBuffer* ptr = 0) :
+        type_(type),
+        name_(name),
+        buffer_(buffer),
+        offset_(offset),
+        size_(size),
+        bufferPtr_(ptr)
+    {
+    }
+    
+    /// %Shader type.
+    ShaderType type_;
+    /// Name of the parameter.
+    String name_;
+    /// Constant buffer index.
+    unsigned buffer_;
+    /// Offset in constant buffer.
+    unsigned offset_;
+    /// Size of parameter in bytes.
+    unsigned size_;
+    /// Constant buffer pointer. Defined only in shader programs.
+    ConstantBuffer* bufferPtr_;
+};
+
+/// Vertex or pixel shader on the GPU.
+class URHO3D_API ShaderVariation : public RefCounted, public GPUObject
+{
+public:
+    /// Construct.
+    ShaderVariation(Shader* owner, ShaderType type);
+    /// Destruct.
+    virtual ~ShaderVariation();
+    
+    /// Release the shader.
+    virtual void Release();
+    
+    /// Compile the shader. Return true if successful.
+    bool Create();
+    /// Set name.
+    void SetName(const String& name);
+    /// Set defines.
+    void SetDefines(const String& defines);
+    
+    /// Return the owner resource.
+    Shader* GetOwner() const;
+    /// Return shader type.
+    ShaderType GetShaderType() const { return type_; }
+    /// Return shader name.
+    const String& GetName() const { return name_; }
+    /// Return full shader name.
+    String GetFullName() const { return name_ + "(" + defines_ + ")"; }
+    /// Return whether uses a parameter.
+    bool HasParameter(StringHash param) const { return parameters_.Contains(param); }
+    /// Return whether uses a texture unit (only for pixel shaders.)
+    bool HasTextureUnit(TextureUnit unit) const { return useTextureUnit_[unit]; }
+    /// Return all parameter definitions.
+    const HashMap<StringHash, ShaderParameter>& GetParameters() const { return parameters_; }
+    /// Return vertex element mask.
+    unsigned GetElementMask() const { return elementMask_; }
+    /// Return shader bytecode.
+    const PODVector<unsigned char>& GetByteCode() const { return byteCode_; }
+    /// Return defines.
+    const String& GetDefines() const { return defines_; }
+    /// Return compile error/warning string.
+    const String& GetCompilerOutput() const { return compilerOutput_; }
+    /// Return constant buffer data sizes.
+    const unsigned* GetConstantBufferSizes() const { return &constantBufferSizes_[0]; }
+
+private:
+    /// Load bytecode from a file. Return true if successful.
+    bool LoadByteCode(const String& binaryShaderName);
+    /// Compile from source. Return true if successful.
+    bool Compile();
+    /// Inspect the constant parameters and input layout (if applicable) from the shader bytecode.
+    void ParseParameters(unsigned char* bufData, unsigned bufSize);
+    /// Save bytecode to a file.
+    void SaveByteCode(const String& binaryShaderName);
+    /// Calculate constant buffer sizes from parameters.
+    void CalculateConstantBufferSizes();
+    
+    /// Shader this variation belongs to.
+    WeakPtr<Shader> owner_;
+    /// Shader type.
+    ShaderType type_;
+    /// Vertex element mask for vertex shaders. Zero for pixel shaders.
+    unsigned elementMask_;
+    /// Shader parameters.
+    HashMap<StringHash, ShaderParameter> parameters_;
+    /// Texture unit use flags.
+    bool useTextureUnit_[MAX_TEXTURE_UNITS];
+    /// Constant buffer sizes. 0 if a constant buffer slot is not in use.
+    unsigned constantBufferSizes_[MAX_SHADER_PARAMETER_GROUPS];
+    /// Bytecode. Needed for inspecting the input signature and parameters.
+    PODVector<unsigned char> byteCode_;
+    /// Shader name.
+    String name_;
+    /// Defines to use in compiling.
+    String defines_;
+    /// Shader compile error string.
+    String compilerOutput_;
+};
+
+}

+ 477 - 0
Source/Urho3D/Graphics/Direct3D11/D3D11Texture.cpp

@@ -0,0 +1,477 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../../IO/FileSystem.h"
+#include "../../IO/Log.h"
+#include "../../Graphics/Graphics.h"
+#include "../../Graphics/GraphicsImpl.h"
+#include "../../Graphics/Material.h"
+#include "../../Core/Profiler.h"
+#include "../../Resource/ResourceCache.h"
+#include "../../Core/StringUtils.h"
+#include "../../Graphics/Texture.h"
+#include "../../Resource/XMLFile.h"
+
+#include "../../DebugNew.h"
+
+namespace Urho3D
+{
+
+static const char* addressModeNames[] =
+{
+    "wrap",
+    "mirror",
+    "clamp",
+    "border",
+    0
+};
+
+static const char* filterModeNames[] =
+{
+    "nearest",
+    "bilinear",
+    "trilinear",
+    "anisotropic",
+    "default",
+    0
+};
+
+static const D3D11_FILTER d3dFilterMode[] =
+{
+    D3D11_FILTER_MIN_MAG_MIP_POINT,
+    D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT,
+    D3D11_FILTER_MIN_MAG_MIP_LINEAR,
+    D3D11_FILTER_ANISOTROPIC,
+    D3D11_FILTER_COMPARISON_MIN_MAG_MIP_POINT,
+    D3D11_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT,
+    D3D11_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR,
+    D3D11_FILTER_COMPARISON_ANISOTROPIC
+};
+
+static const D3D11_TEXTURE_ADDRESS_MODE d3dAddressMode[] = 
+{
+    D3D11_TEXTURE_ADDRESS_WRAP,
+    D3D11_TEXTURE_ADDRESS_MIRROR,
+    D3D11_TEXTURE_ADDRESS_CLAMP,
+    D3D11_TEXTURE_ADDRESS_BORDER
+};
+
+Texture::Texture(Context* context) :
+    Resource(context),
+    GPUObject(GetSubsystem<Graphics>()),
+    shaderResourceView_(0),
+    sampler_(0),
+    format_(DXGI_FORMAT_UNKNOWN),
+    usage_(TEXTURE_STATIC),
+    levels_(0),
+    requestedLevels_(0),
+    width_(0),
+    height_(0),
+    depth_(0),
+    shadowCompare_(false),
+    filterMode_(FILTER_DEFAULT),
+    sRGB_(false),
+    parametersDirty_(true)
+{
+    for (int i = 0; i < MAX_COORDS; ++i)
+        addressMode_[i] = ADDRESS_WRAP;
+    for (int i = 0; i < MAX_TEXTURE_QUALITY_LEVELS; ++i)
+        mipsToSkip_[i] = MAX_TEXTURE_QUALITY_LEVELS - 1 - i;
+}
+
+Texture::~Texture()
+{
+}
+
+void Texture::SetNumLevels(unsigned levels)
+{
+    if (usage_ > TEXTURE_RENDERTARGET)
+        requestedLevels_ = 1;
+    else
+        requestedLevels_ = levels;
+}
+
+void Texture::SetFilterMode(TextureFilterMode mode)
+{
+    filterMode_ = mode;
+    parametersDirty_ = true;
+}
+
+void Texture::SetAddressMode(TextureCoordinate coord, TextureAddressMode mode)
+{
+    addressMode_[coord] = mode;
+    parametersDirty_ = true;
+}
+
+void Texture::SetShadowCompare(bool enable)
+{
+    shadowCompare_ = enable;
+    parametersDirty_ = true;
+}
+
+void Texture::SetBorderColor(const Color& color)
+{
+    borderColor_ = color;
+    parametersDirty_ = true;
+}
+
+void Texture::SetSRGB(bool enable)
+{
+    if (graphics_)
+        enable &= graphics_->GetSRGBSupport();
+    
+    // Note: on D3D11 sRGB only affects the texture before creation
+    sRGB_ = enable;
+}
+
+void Texture::SetBackupTexture(Texture* texture)
+{
+    backupTexture_ = texture;
+}
+
+void Texture::SetMipsToSkip(int quality, int mips)
+{
+    if (quality >= QUALITY_LOW && quality < MAX_TEXTURE_QUALITY_LEVELS)
+    {
+        mipsToSkip_[quality] = mips;
+        
+        // Make sure a higher quality level does not actually skip more mips
+        for (int i = 1; i < MAX_TEXTURE_QUALITY_LEVELS; ++i)
+        {
+            if (mipsToSkip_[i] > mipsToSkip_[i - 1])
+                mipsToSkip_[i] = mipsToSkip_[i - 1];
+        }
+    }
+}
+
+bool Texture::IsCompressed() const
+{
+    return format_ == DXGI_FORMAT_BC1_UNORM || format_ == DXGI_FORMAT_BC2_UNORM || format_ == DXGI_FORMAT_BC3_UNORM;
+}
+
+int Texture::GetMipsToSkip(int quality) const
+{
+    return (quality >= QUALITY_LOW && quality < MAX_TEXTURE_QUALITY_LEVELS) ? mipsToSkip_[quality] : 0;
+}
+
+int Texture::GetLevelWidth(unsigned level) const
+{
+    if (level > levels_)
+        return 0;
+    return Max(width_ >> level, 1);
+}
+
+int Texture::GetLevelHeight(unsigned level) const
+{
+    if (level > levels_)
+        return 0;
+    return Max(height_ >> level, 1);
+}
+
+int Texture::GetLevelDepth(unsigned level) const
+{
+    if (level > levels_)
+        return 0;
+    return Max(depth_ >> level, 1);
+}
+
+unsigned Texture::GetDataSize(int width, int height) const
+{
+    if (IsCompressed())
+        return GetRowDataSize(width) * ((height + 3) >> 2);
+    else
+        return GetRowDataSize(width) * height;
+}
+
+unsigned Texture::GetDataSize(int width, int height, int depth) const
+{
+    return depth * GetDataSize(width, height);
+}
+
+unsigned Texture::GetRowDataSize(int width) const
+{
+    switch (format_)
+    {
+    case DXGI_FORMAT_R8_UNORM:
+    case DXGI_FORMAT_A8_UNORM:
+        return width;
+    
+    case DXGI_FORMAT_R8G8_UNORM:
+    case DXGI_FORMAT_R16_UNORM:
+    case DXGI_FORMAT_R16_FLOAT:
+    case DXGI_FORMAT_R16_TYPELESS:
+        return width * 2;
+
+    case DXGI_FORMAT_R8G8B8A8_UNORM:
+    case DXGI_FORMAT_R16G16_UNORM:
+    case DXGI_FORMAT_R16G16_FLOAT:
+    case DXGI_FORMAT_R32_FLOAT:
+    case DXGI_FORMAT_R24G8_TYPELESS:
+    case DXGI_FORMAT_R32_TYPELESS:
+        return width * 4;
+        
+    case DXGI_FORMAT_R16G16B16A16_UNORM:
+    case DXGI_FORMAT_R16G16B16A16_FLOAT:
+        return width * 8;
+        
+    case DXGI_FORMAT_R32G32B32A32_FLOAT:
+        return width * 16;
+        
+    case DXGI_FORMAT_BC1_UNORM:
+        return ((width + 3) >> 2) * 8;
+        
+    case DXGI_FORMAT_BC2_UNORM:
+    case DXGI_FORMAT_BC3_UNORM:
+        return ((width + 3) >> 2) * 16;
+        
+    default:
+        return 0;
+    }
+}
+
+void Texture::SetParameters(XMLFile* file)
+{
+    if (!file)
+        return;
+    
+    XMLElement rootElem = file->GetRoot();
+    SetParameters(rootElem);
+}
+
+void Texture::SetParameters(const XMLElement& element)
+{
+    XMLElement paramElem = element.GetChild();
+    while (paramElem)
+    {
+        String name = paramElem.GetName();
+        
+        if (name == "address")
+        {
+            String coord = paramElem.GetAttributeLower("coord");
+            if (coord.Length() >= 1)
+            {
+                TextureCoordinate coordIndex = (TextureCoordinate)(coord[0] - 'u');
+                String mode = paramElem.GetAttributeLower("mode");
+                SetAddressMode(coordIndex, (TextureAddressMode)GetStringListIndex(mode.CString(), addressModeNames, ADDRESS_WRAP));
+            }
+        }
+        
+        if (name == "border")
+            SetBorderColor(paramElem.GetColor("color"));
+        
+        if (name == "filter")
+        {
+            String mode = paramElem.GetAttributeLower("mode");
+            SetFilterMode((TextureFilterMode)GetStringListIndex(mode.CString(), filterModeNames, FILTER_DEFAULT));
+        }
+        
+        if (name == "mipmap")
+            SetNumLevels(paramElem.GetBool("enable") ? 0 : 1);
+        
+        if (name == "quality")
+        {
+            if (paramElem.HasAttribute("low"))
+                SetMipsToSkip(QUALITY_LOW, paramElem.GetInt("low"));
+            if (paramElem.HasAttribute("med"))
+                SetMipsToSkip(QUALITY_MEDIUM, paramElem.GetInt("med"));
+            if (paramElem.HasAttribute("medium"))
+                SetMipsToSkip(QUALITY_MEDIUM, paramElem.GetInt("medium"));
+            if (paramElem.HasAttribute("high"))
+                SetMipsToSkip(QUALITY_HIGH, paramElem.GetInt("high"));
+        }
+
+        if (name == "srgb")
+            SetSRGB(paramElem.GetBool("enable"));
+        
+        paramElem = paramElem.GetNext();
+    }
+}
+
+void Texture::SetParametersDirty()
+{
+    parametersDirty_ = true;
+}
+
+void Texture::UpdateParameters()
+{
+    if ((!parametersDirty_ && sampler_) || !object_)
+        return;
+
+    // Release old sampler
+    if (sampler_)
+    {
+        ((ID3D11SamplerState*)sampler_)->Release();
+        sampler_ = 0;
+    }
+
+    D3D11_SAMPLER_DESC samplerDesc;
+    memset(&samplerDesc, 0, sizeof samplerDesc);
+    unsigned filterModeIndex = filterMode_ != FILTER_DEFAULT ? filterMode_ : graphics_->GetDefaultTextureFilterMode();
+    if (shadowCompare_)
+        filterModeIndex += 4;
+    samplerDesc.Filter = d3dFilterMode[filterModeIndex];
+    samplerDesc.AddressU = d3dAddressMode[addressMode_[0]];
+    samplerDesc.AddressV = d3dAddressMode[addressMode_[1]];
+    samplerDesc.AddressW = d3dAddressMode[addressMode_[2]];
+    samplerDesc.MaxAnisotropy = graphics_->GetTextureAnisotropy();
+    samplerDesc.ComparisonFunc = D3D11_COMPARISON_LESS_EQUAL;
+    samplerDesc.MinLOD = -M_INFINITY;
+    samplerDesc.MaxLOD = M_INFINITY;
+    memcpy(&samplerDesc.BorderColor, borderColor_.Data(), 4 * sizeof(float));
+
+    graphics_->GetImpl()->GetDevice()->CreateSamplerState(&samplerDesc, (ID3D11SamplerState**)&sampler_);
+
+    if (!sampler_)
+        LOGERROR("Failed to create sampler state");
+
+    parametersDirty_ = false;
+}
+
+SharedArrayPtr<unsigned char> Texture::ConvertRGBToRGBA(int width, int height, const unsigned char* data)
+{
+    if (!width || !height)
+        return SharedArrayPtr<unsigned char>();
+
+    SharedArrayPtr<unsigned char> ret(new unsigned char[width * height * 4]);
+    unsigned char* dest = ret.Get();
+
+    for (int i = 0; i < width * height; ++i)
+    {
+        dest[0] = data[0];
+        dest[1] = data[1];
+        dest[2] = data[2];
+        dest[3] = 255;
+        dest += 4;
+        data += 3;
+    }
+
+    return ret;
+}
+
+SharedArrayPtr<unsigned char> Texture::ConvertRGBToRGBA(int width, int height, int depth, const unsigned char* data)
+{
+    if (!width || !height || !depth)
+        return SharedArrayPtr<unsigned char>();
+
+    SharedArrayPtr<unsigned char> ret(new unsigned char[width * height * depth * 4]);
+    unsigned char* dest = ret.Get();
+
+    for (int i = 0; i < width * height * depth; ++i)
+    {
+        dest[0] = data[0];
+        dest[1] = data[1];
+        dest[2] = data[2];
+        dest[3] = 255;
+        dest += 4;
+        data += 3;
+    }
+
+    return ret;
+}
+
+unsigned Texture::CheckMaxLevels(int width, int height, unsigned requestedLevels)
+{
+    unsigned maxLevels = 1;
+    while (width > 1 && height > 1)
+    {
+        ++maxLevels;
+        width >>= 1;
+        height >>= 1;
+    }
+
+    if (!requestedLevels || maxLevels < requestedLevels)
+        return maxLevels;
+    else
+        return requestedLevels;
+}
+
+unsigned Texture::CheckMaxLevels(int width, int height, int depth, unsigned requestedLevels)
+{
+    unsigned maxLevels = 1;
+    while (width > 1 && height > 1 && depth > 1)
+    {
+        ++maxLevels;
+        width >>= 1;
+        height >>= 1;
+        depth >>= 1;
+    }
+
+    if (!requestedLevels || maxLevels < requestedLevels)
+        return maxLevels;
+    else
+        return requestedLevels;
+}
+
+
+unsigned Texture::GetSRVFormat(unsigned format)
+{
+    if (format == DXGI_FORMAT_R24G8_TYPELESS)
+        return DXGI_FORMAT_R24_UNORM_X8_TYPELESS;
+    else if (format == DXGI_FORMAT_R16_TYPELESS)
+        return DXGI_FORMAT_R16_UNORM;
+    else if (format == DXGI_FORMAT_R32_TYPELESS)
+        return DXGI_FORMAT_R32_FLOAT;
+    else
+        return format;
+}
+
+unsigned Texture::GetDSVFormat(unsigned format)
+{
+    if (format == DXGI_FORMAT_R24G8_TYPELESS)
+        return DXGI_FORMAT_D24_UNORM_S8_UINT;
+    else if (format == DXGI_FORMAT_R16_TYPELESS)
+        return DXGI_FORMAT_D16_UNORM;
+    else if (format == DXGI_FORMAT_R32_TYPELESS)
+        return DXGI_FORMAT_D32_FLOAT;
+    else
+        return format;
+}
+
+unsigned Texture::GetSRGBFormat(unsigned format)
+{
+    if (format == DXGI_FORMAT_R8G8B8A8_UNORM)
+        return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
+    else if (format == DXGI_FORMAT_BC1_UNORM)
+        return DXGI_FORMAT_BC1_UNORM_SRGB;
+    else if (format == DXGI_FORMAT_BC2_UNORM)
+        return DXGI_FORMAT_BC2_UNORM_SRGB;
+    else if (format == DXGI_FORMAT_BC3_UNORM)
+        return DXGI_FORMAT_BC3_UNORM_SRGB;
+    else
+        return format;
+}
+
+void Texture::CheckTextureBudget(StringHash type)
+{
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    unsigned textureBudget = cache->GetMemoryBudget(type);
+    unsigned textureUse = cache->GetMemoryUse(type);
+    if (!textureBudget)
+        return;
+    
+    // If textures are over the budget, they likely can not be freed directly as materials still refer to them.
+    // Therefore free unused materials first
+    if (textureUse > textureBudget)
+        cache->ReleaseResources(Material::GetTypeStatic());
+}
+
+}

+ 176 - 0
Source/Urho3D/Graphics/Direct3D11/D3D11Texture.h

@@ -0,0 +1,176 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../../Container/ArrayPtr.h"
+#include "../../Math/Color.h"
+#include "../../Graphics/GPUObject.h"
+#include "../../Graphics/GraphicsDefs.h"
+#include "../../Resource/Resource.h"
+
+namespace Urho3D
+{
+
+static const int MAX_TEXTURE_QUALITY_LEVELS = 3;
+
+class XMLElement;
+class XMLFile;
+
+/// Base class for texture resources.
+class URHO3D_API Texture : public Resource, public GPUObject
+{
+public:
+    /// Construct.
+    Texture(Context* context);
+    /// Destruct.
+    virtual ~Texture();
+    
+    /// Set number of requested mip levels. Needs to be called before setting size.
+    void SetNumLevels(unsigned levels);
+    /// Set filtering mode.
+    void SetFilterMode(TextureFilterMode filter);
+    /// Set addressing mode by texture coordinate.
+    void SetAddressMode(TextureCoordinate coord, TextureAddressMode address);
+    /// Set shadow compare mode.
+    void SetShadowCompare(bool enable);
+    /// Set border color for border addressing mode.
+    void SetBorderColor(const Color& color);
+    /// Set sRGB sampling and writing mode.
+    void SetSRGB(bool enable);
+    /// Set backup texture to use when rendering to this texture.
+    void SetBackupTexture(Texture* texture);
+    /// Set mip levels to skip on a quality setting when loading. Ensures higher quality levels do not skip more.
+    void SetMipsToSkip(int quality, int mips);
+    
+    /// Return texture format.
+    unsigned GetFormat() const { return format_; }
+    /// Return whether the texture format is compressed.
+    bool IsCompressed() const;
+    /// Return number of mip levels.
+    unsigned GetLevels() const { return levels_; }
+    /// Return width.
+    int GetWidth() const { return width_; }
+    /// Return height.
+    int GetHeight() const { return height_; }
+    /// Return height.
+    int GetDepth() const { return depth_; }
+    /// Return filtering mode.
+    TextureFilterMode GetFilterMode() const { return filterMode_; }
+    /// Return addressing mode by texture coordinate.
+    TextureAddressMode GetAddressMode(TextureCoordinate coord) const { return addressMode_[coord]; }
+    /// Return whether shadow compare is enabled.
+    bool GetShadowCompare() const { return shadowCompare_; }
+    /// Return border color.
+    const Color& GetBorderColor() const { return borderColor_; }
+    /// Return whether is using sRGB sampling and writing.
+    bool GetSRGB() const { return sRGB_; }
+    /// Return backup texture.
+    Texture* GetBackupTexture() const { return backupTexture_; }
+    /// Return mip levels to skip on a quality setting when loading.
+    int GetMipsToSkip(int quality) const;
+    /// Return mip level width, or 0 if level does not exist.
+    int GetLevelWidth(unsigned level) const;
+    /// Return mip level width, or 0 if level does not exist.
+    int GetLevelHeight(unsigned level) const;
+    /// Return mip level depth, or 0 if level does not exist.
+    int GetLevelDepth(unsigned level) const;
+    /// Return texture usage type.
+    TextureUsage GetUsage() const { return usage_; }
+    /// Return data size in bytes for a rectangular region.
+    unsigned GetDataSize(int width, int height) const;
+    /// Return data size in bytes for a volume region.
+    unsigned GetDataSize(int width, int height, int depth) const;
+    /// Return data size in bytes for a pixel or block row.
+    unsigned GetRowDataSize(int width) const;
+    /// Return whether the parameters are dirty.
+    bool GetParametersDirty() const { return parametersDirty_ || !sampler_; }
+
+    /// Set additional parameters from an XML file.
+    void SetParameters(XMLFile* xml);
+    /// Set additional parameters from an XML element.
+    void SetParameters(const XMLElement& element);
+    /// Mark parameters dirty. Called by Graphics.
+    void SetParametersDirty();
+    /// Create sampler state object after parameters have been changed. Called by Graphics when assigning the texture.
+    void UpdateParameters();
+    /// Return shader resource view.
+    void* GetShaderResourceView() const { return shaderResourceView_; }
+    /// Return sampler state object.
+    void* GetSampler() const { return sampler_; }
+    
+    /// Convert RGB data to RGBA for loading into a texture.
+    static SharedArrayPtr<unsigned char> ConvertRGBToRGBA(int width, int height, const unsigned char* data);
+    /// Convert RGB data to RGBA for loading into a 3D texture.
+    static SharedArrayPtr<unsigned char> ConvertRGBToRGBA(int width, int height, int depth, const unsigned char* data);
+    /// Check maximum allowed mip levels for a specific texture size.
+    static unsigned CheckMaxLevels(int width, int height, unsigned requestedLevels);
+    /// Check maximum allowed mip levels for a specific 3D texture size.
+    static unsigned CheckMaxLevels(int width, int height, int depth, unsigned requestedLevels);
+    /// Return the shader resource view format corresponding to a texture format. Handles conversion of typeless depth texture formats.
+    static unsigned GetSRVFormat(unsigned format);
+    /// Return the depth-stencil view format corresponding to a texture format. Handles conversion of typeless depth texture formats.
+    static unsigned GetDSVFormat(unsigned format);
+    /// Convert format to sRGB.
+    static unsigned GetSRGBFormat(unsigned format);
+
+protected:
+    /// Check whether texture memory budget has been exceeded. Free unused materials in that case to release the texture references.
+    void CheckTextureBudget(StringHash type);
+    
+    /// Shader resource view.
+    void* shaderResourceView_;
+    /// Sampler state object.
+    void* sampler_;
+    /// Texture format.
+    unsigned format_;
+    /// Texture usage type.
+    TextureUsage usage_;
+    /// Current mip levels.
+    unsigned levels_;
+    /// Requested mip levels.
+    unsigned requestedLevels_;
+    /// Texture width.
+    int width_;
+    /// Texture height.
+    int height_;
+    /// Texture depth.
+    int depth_;
+    /// Shadow compare mode.
+    bool shadowCompare_;
+    /// Filtering mode.
+    TextureFilterMode filterMode_;
+    /// Addressing mode.
+    TextureAddressMode addressMode_[MAX_COORDS];
+    /// Mip levels to skip when loading per texture quality setting.
+    unsigned mipsToSkip_[MAX_TEXTURE_QUALITY_LEVELS];
+    /// Border color.
+    Color borderColor_;
+    /// sRGB sampling and writing mode flag.
+    bool sRGB_;
+    /// Parameters dirty flag.
+    bool parametersDirty_;
+    /// Backup texture.
+    SharedPtr<Texture> backupTexture_;
+};
+
+}

+ 548 - 0
Source/Urho3D/Graphics/Direct3D11/D3D11Texture2D.cpp

@@ -0,0 +1,548 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../../Core/Context.h"
+#include "../../IO/FileSystem.h"
+#include "../../Graphics/Graphics.h"
+#include "../../Graphics/GraphicsEvents.h"
+#include "../../Graphics/GraphicsImpl.h"
+#include "../../IO/Log.h"
+#include "../../Graphics/Renderer.h"
+#include "../../Core/Profiler.h"
+#include "../../Resource/ResourceCache.h"
+#include "../../Graphics/Texture2D.h"
+#include "../../Resource/XMLFile.h"
+
+#include "../../DebugNew.h"
+
+namespace Urho3D
+{
+
+Texture2D::Texture2D(Context* context) :
+    Texture(context)
+{
+}
+
+Texture2D::~Texture2D()
+{
+    Release();
+}
+
+void Texture2D::RegisterObject(Context* context)
+{
+    context->RegisterFactory<Texture2D>();
+}
+
+bool Texture2D::BeginLoad(Deserializer& source)
+{
+    // In headless mode, do not actually load the texture, just return success
+    if (!graphics_)
+        return true;
+    
+    // Load the image data for EndLoad()
+    loadImage_ = new Image(context_);
+    if (!loadImage_->Load(source))
+    {
+        loadImage_.Reset();
+        return false;
+    }
+
+    // Precalculate mip levels if async loading
+    if (GetAsyncLoadState() == ASYNC_LOADING)
+        loadImage_->PrecalculateLevels();
+    
+    // Load the optional parameters file
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    String xmlName = ReplaceExtension(GetName(), ".xml");
+    loadParameters_ = cache->GetTempResource<XMLFile>(xmlName, false);
+    
+    return true;
+}
+
+bool Texture2D::EndLoad()
+{
+    // In headless mode, do not actually load the texture, just return success
+    if (!graphics_)
+        return true;
+    
+    // If over the texture budget, see if materials can be freed to allow textures to be freed
+    CheckTextureBudget(GetTypeStatic());
+
+    SetParameters(loadParameters_);
+    bool success = SetData(loadImage_);
+    
+    loadImage_.Reset();
+    loadParameters_.Reset();
+    
+    return success;
+}
+
+void Texture2D::Release()
+{
+    if (object_)
+    {
+        if (!graphics_)
+            return;
+        
+        for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
+        {
+            if (graphics_->GetTexture(i) == this)
+                graphics_->SetTexture(i, 0);
+        }
+        
+        if (renderSurface_)
+            renderSurface_->Release();
+        
+        ((ID3D11Resource*)object_)->Release();
+        object_ = 0;
+
+        if (shaderResourceView_)
+        {
+            ((ID3D11ShaderResourceView*)shaderResourceView_)->Release();
+            shaderResourceView_ = 0;
+        }
+
+        if (sampler_)
+        {
+            ((ID3D11SamplerState*)sampler_)->Release();
+            sampler_ = 0;
+        }
+    }
+    else
+    {
+        if (renderSurface_)
+            renderSurface_->Release();
+    }
+}
+
+bool Texture2D::SetSize(int width, int height, unsigned format, TextureUsage usage)
+{
+    // Delete the old rendersurface if any
+    renderSurface_.Reset();
+    usage_ = usage;
+    
+    if (usage_ == TEXTURE_RENDERTARGET || usage_ == TEXTURE_DEPTHSTENCIL)
+    {
+        renderSurface_ = new RenderSurface(this);
+        // Clamp mode addressing by default, nearest filtering, and mipmaps disabled
+        addressMode_[COORD_U] = ADDRESS_CLAMP;
+        addressMode_[COORD_V] = ADDRESS_CLAMP;
+        filterMode_ = FILTER_NEAREST;
+        requestedLevels_ = 1;
+    }
+    
+    if (usage_ == TEXTURE_RENDERTARGET)
+        SubscribeToEvent(E_RENDERSURFACEUPDATE, HANDLER(Texture2D, HandleRenderSurfaceUpdate));
+    else
+        UnsubscribeFromEvent(E_RENDERSURFACEUPDATE);
+
+    width_ = width;
+    height_ = height;
+    format_ = format;
+    
+    return Create();
+}
+
+bool Texture2D::SetData(unsigned level, int x, int y, int width, int height, const void* data)
+{
+    PROFILE(SetTextureData);
+    
+    if (!object_)
+    {
+        LOGERROR("No texture created, can not set data");
+        return false;
+    }
+    
+    if (!data)
+    {
+        LOGERROR("Null source for setting data");
+        return false;
+    }
+    
+    if (level >= levels_)
+    {
+        LOGERROR("Illegal mip level for setting data");
+        return false;
+    }
+    
+    int levelWidth = GetLevelWidth(level);
+    int levelHeight = GetLevelHeight(level);
+    if (x < 0 || x + width > levelWidth || y < 0 || y + height > levelHeight || width <= 0 || height <= 0)
+    {
+        LOGERROR("Illegal dimensions for setting data");
+        return false;
+    }
+    
+    // If compressed, align the update region on a block
+    if (IsCompressed())
+    {
+        x &= ~3;
+        y &= ~3;
+        width += 3;
+        width &= 0xfffffffc;
+        height += 3;
+        height &= 0xfffffffc;
+    }
+
+    unsigned char* src = (unsigned char*)data;
+    unsigned rowSize = GetRowDataSize(width);
+    unsigned rowStart = GetRowDataSize(x);
+    unsigned subResource = D3D11CalcSubresource(level, 0, levels_);
+
+    if (usage_ == TEXTURE_DYNAMIC)
+    {
+        if (IsCompressed())
+        {
+            height = (height + 3) >> 2;
+            y >>= 2;
+        }
+
+        D3D11_MAPPED_SUBRESOURCE mappedData;
+        mappedData.pData = 0;
+
+        graphics_->GetImpl()->GetDeviceContext()->Map((ID3D11Resource*)object_, subResource, D3D11_MAP_WRITE_DISCARD, 0,
+            &mappedData);
+        if (mappedData.pData)
+        {
+            for (int row = 0; row < height; ++row)
+                memcpy((unsigned char*)mappedData.pData + (row + y) * mappedData.RowPitch + rowStart, src + row * rowSize, rowSize);
+            graphics_->GetImpl()->GetDeviceContext()->Unmap((ID3D11Resource*)object_, subResource);
+        }
+        else
+        {
+            LOGERROR("Failed to map texture for update");
+            return false;
+        }
+    }
+    else
+    {
+        D3D11_BOX destBox;
+        destBox.left = x;
+        destBox.right = x + width;
+        destBox.top = y;
+        destBox.bottom = y + height;
+        destBox.front = 0;
+        destBox.back = 1;
+
+        graphics_->GetImpl()->GetDeviceContext()->UpdateSubresource((ID3D11Resource*)object_, subResource, &destBox, data,
+            rowSize, 0);
+    }
+
+    return true;
+}
+
+bool Texture2D::SetData(SharedPtr<Image> image, bool useAlpha)
+{
+    if (!image)
+    {
+        LOGERROR("Null image, can not load texture");
+        return false;
+    }
+    
+    unsigned memoryUse = sizeof(Texture2D);
+    
+    int quality = QUALITY_HIGH;
+    Renderer* renderer = GetSubsystem<Renderer>();
+    if (renderer)
+        quality = renderer->GetTextureQuality();
+    
+    if (!image->IsCompressed())
+    {
+        unsigned char* levelData = image->GetData();
+        int levelWidth = image->GetWidth();
+        int levelHeight = image->GetHeight();
+        unsigned components = image->GetComponents();
+        unsigned format = 0;
+        
+        // Discard unnecessary mip levels
+        for (unsigned i = 0; i < mipsToSkip_[quality]; ++i)
+        {
+            image = image->GetNextLevel();
+            levelData = image->GetData();
+            levelWidth = image->GetWidth();
+            levelHeight = image->GetHeight();
+        }
+        
+        switch (components)
+        {
+        case 1:
+            format = useAlpha ? Graphics::GetAlphaFormat() : Graphics::GetLuminanceFormat();
+            break;
+            
+        case 2:
+            format = Graphics::GetLuminanceAlphaFormat();
+            break;
+            
+        case 3:
+            format = Graphics::GetRGBFormat();
+            break;
+            
+        case 4:
+            format = Graphics::GetRGBAFormat();
+            break;
+        }
+        
+        // If image was previously compressed, reset number of requested levels to avoid error if level count is too high for new size
+        if (IsCompressed() && requestedLevels_ > 1)
+            requestedLevels_ = 0;
+        SetSize(levelWidth, levelHeight, format);
+        
+        for (unsigned i = 0; i < levels_; ++i)
+        {
+            // D3D11 needs RGB data as 4-component
+            SharedArrayPtr<unsigned char> convertedData;
+            if (components == 3)
+            {
+                convertedData = ConvertRGBToRGBA(levelWidth, levelHeight, levelData);
+                levelData = convertedData;
+            }
+
+            SetData(i, 0, 0, levelWidth, levelHeight, levelData);
+            memoryUse += levelWidth * levelHeight * components;
+            
+            if (i < levels_ - 1)
+            {
+                image = image->GetNextLevel();
+                levelData = image->GetData();
+                levelWidth = image->GetWidth();
+                levelHeight = image->GetHeight();
+            }
+        }
+    }
+    else
+    {
+        int width = image->GetWidth();
+        int height = image->GetHeight();
+        unsigned levels = image->GetNumCompressedLevels();
+        unsigned format = graphics_->GetFormat(image->GetCompressedFormat());
+        bool needDecompress = false;
+        
+        if (!format)
+        {
+            format = Graphics::GetRGBAFormat();
+            needDecompress = true;
+        }
+        
+        unsigned mipsToSkip = mipsToSkip_[quality];
+        if (mipsToSkip >= levels)
+            mipsToSkip = levels - 1;
+        while (mipsToSkip && (width / (1 << mipsToSkip) < 4 || height / (1 << mipsToSkip) < 4))
+            --mipsToSkip;
+        width /= (1 << mipsToSkip);
+        height /= (1 << mipsToSkip);
+        
+        SetNumLevels(Max((int)(levels - mipsToSkip), 1));
+        SetSize(width, height, format);
+        
+        for (unsigned i = 0; i < levels_ && i < levels - mipsToSkip; ++i)
+        {
+            CompressedLevel level = image->GetCompressedLevel(i + mipsToSkip);
+            if (!needDecompress)
+            {
+                SetData(i, 0, 0, level.width_, level.height_, level.data_);
+                memoryUse += level.rows_ * level.rowSize_;
+            }
+            else
+            {
+                unsigned char* rgbaData = new unsigned char[level.width_ * level.height_ * 4];
+                level.Decompress(rgbaData);
+                SetData(i, 0, 0, level.width_, level.height_, rgbaData);
+                memoryUse += level.width_ * level.height_ * 4;
+                delete[] rgbaData;
+            }
+        }
+    }
+    
+    SetMemoryUse(memoryUse);
+    return true;
+}
+
+bool Texture2D::GetData(unsigned level, void* dest) const
+{
+    if (!object_)
+    {
+        LOGERROR("No texture created, can not get data");
+        return false;
+    }
+    
+    if (!dest)
+    {
+        LOGERROR("Null destination for getting data");
+        return false;
+    }
+    
+    if (level >= levels_)
+    {
+        LOGERROR("Illegal mip level for getting data");
+        return false;
+    }
+    
+    int levelWidth = GetLevelWidth(level);
+    int levelHeight = GetLevelHeight(level);
+    
+    D3D11_TEXTURE2D_DESC textureDesc;
+    memset(&textureDesc, 0, sizeof textureDesc);
+    textureDesc.Width = levelWidth;
+    textureDesc.Height = levelHeight;
+    textureDesc.MipLevels = 1;
+    textureDesc.ArraySize = 1;
+    textureDesc.Format = (DXGI_FORMAT)format_;
+    textureDesc.SampleDesc.Count = 1;
+    textureDesc.SampleDesc.Quality = 0;
+    textureDesc.Usage = D3D11_USAGE_STAGING;
+    textureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+
+    ID3D11Texture2D* stagingTexture = 0;
+    graphics_->GetImpl()->GetDevice()->CreateTexture2D(&textureDesc, 0, &stagingTexture);
+    if (!stagingTexture)
+    {
+        LOGERROR("Failed to create staging texture for GetData");
+        return false;
+    }
+
+    unsigned srcSubResource = D3D11CalcSubresource(level, 0, levels_);
+    D3D11_BOX srcBox;
+    srcBox.left = 0;
+    srcBox.right = levelWidth;
+    srcBox.top = 0;
+    srcBox.bottom = levelHeight;
+    srcBox.front = 0;
+    srcBox.back = 1;
+    graphics_->GetImpl()->GetDeviceContext()->CopySubresourceRegion(stagingTexture, 0, 0, 0, 0, (ID3D11Resource*)object_,
+        srcSubResource, &srcBox);
+    
+    D3D11_MAPPED_SUBRESOURCE mappedData;
+    mappedData.pData = 0;
+    unsigned rowSize = GetRowDataSize(levelWidth);
+    unsigned numRows = IsCompressed() ? (levelHeight + 3) >> 2 : levelHeight;
+
+    graphics_->GetImpl()->GetDeviceContext()->Map((ID3D11Resource*)stagingTexture, 0, D3D11_MAP_READ, 0, &mappedData);
+    if (mappedData.pData)
+    {
+        for (unsigned row = 0; row < numRows; ++row)
+            memcpy((unsigned char*)dest + row * rowSize, (unsigned char*)mappedData.pData + row * mappedData.RowPitch, rowSize);
+        graphics_->GetImpl()->GetDeviceContext()->Unmap((ID3D11Resource*)stagingTexture, 0);
+        stagingTexture->Release();
+        return true;
+    }
+    else
+    {
+        LOGERROR("Failed to map staging texture for GetData");
+        stagingTexture->Release();
+        return false;
+    }
+}
+
+bool Texture2D::Create()
+{
+    Release();
+    
+    if (!graphics_ || !width_ || !height_)
+        return false;
+    
+    levels_ = CheckMaxLevels(width_, height_, requestedLevels_);
+    
+    D3D11_TEXTURE2D_DESC textureDesc;
+    memset(&textureDesc, 0, sizeof textureDesc);
+    textureDesc.Width = width_;
+    textureDesc.Height = height_;
+    textureDesc.MipLevels = levels_;
+    textureDesc.ArraySize = 1;
+    textureDesc.Format = (DXGI_FORMAT)(sRGB_ ? GetSRGBFormat(format_) : format_);
+    textureDesc.SampleDesc.Count = 1;
+    textureDesc.SampleDesc.Quality = 0;
+    textureDesc.Usage = usage_ == TEXTURE_DYNAMIC ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT;
+    textureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
+    if (usage_ == TEXTURE_RENDERTARGET)
+        textureDesc.BindFlags |= D3D11_BIND_RENDER_TARGET;
+    else if (usage_ == TEXTURE_DEPTHSTENCIL)
+        textureDesc.BindFlags |= D3D11_BIND_DEPTH_STENCIL;
+    textureDesc.CPUAccessFlags = usage_ == TEXTURE_DYNAMIC ? D3D11_CPU_ACCESS_WRITE : 0;
+
+    graphics_->GetImpl()->GetDevice()->CreateTexture2D(&textureDesc, 0, (ID3D11Texture2D**)&object_);
+    if (!object_)
+    {
+        LOGERROR("Failed to create texture");
+        return false;
+    }
+
+    D3D11_SHADER_RESOURCE_VIEW_DESC resourceViewDesc;
+    memset(&resourceViewDesc, 0, sizeof resourceViewDesc);
+    resourceViewDesc.Format = (DXGI_FORMAT)GetSRVFormat(textureDesc.Format);
+    resourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
+    resourceViewDesc.Texture2D.MipLevels = (unsigned)levels_;
+
+    graphics_->GetImpl()->GetDevice()->CreateShaderResourceView((ID3D11Resource*)object_, &resourceViewDesc,
+        (ID3D11ShaderResourceView**)&shaderResourceView_);
+    if (!shaderResourceView_)
+    {
+        LOGERROR("Failed to create shader resource view for texture");
+        return false;
+    }
+
+    if (usage_ == TEXTURE_RENDERTARGET)
+    {
+        renderSurface_ = new RenderSurface(this);
+
+        D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc;
+        memset(&renderTargetViewDesc, 0, sizeof renderTargetViewDesc);
+        renderTargetViewDesc.Format = textureDesc.Format;
+        renderTargetViewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
+
+        graphics_->GetImpl()->GetDevice()->CreateRenderTargetView((ID3D11Resource*)object_, &renderTargetViewDesc,
+            (ID3D11RenderTargetView**)&renderSurface_->renderTargetView_);
+
+        if (!renderSurface_->renderTargetView_)
+        {
+            LOGERROR("Failed to create rendertarget view for texture");
+            return false;
+        }
+    }
+    else if (usage_ == TEXTURE_DEPTHSTENCIL)
+    {
+        renderSurface_ = new RenderSurface(this);
+
+        D3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc;
+        memset(&depthStencilViewDesc, 0, sizeof depthStencilViewDesc);
+        depthStencilViewDesc.Format = (DXGI_FORMAT)GetDSVFormat(textureDesc.Format);
+        depthStencilViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
+
+        graphics_->GetImpl()->GetDevice()->CreateDepthStencilView((ID3D11Resource*)object_, &depthStencilViewDesc,
+            (ID3D11DepthStencilView**)&renderSurface_->renderTargetView_);
+
+        if (!renderSurface_->renderTargetView_)
+        {
+            LOGERROR("Failed to create depth-stencil view for texture");
+            return false;
+        }
+    }
+
+    return true;
+}
+
+void Texture2D::HandleRenderSurfaceUpdate(StringHash eventType, VariantMap& eventData)
+{
+    if (renderSurface_ && renderSurface_->GetUpdateMode() == SURFACE_UPDATEALWAYS)
+        renderSurface_->QueueUpdate();
+}
+
+}

+ 81 - 0
Source/Urho3D/Graphics/Direct3D11/D3D11Texture2D.h

@@ -0,0 +1,81 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../../Graphics/RenderSurface.h"
+#include "../../Container/Ptr.h"
+#include "../../Graphics/Texture.h"
+
+namespace Urho3D
+{
+
+class Image;
+class XMLFile;
+
+/// 2D texture resource.
+class URHO3D_API Texture2D : public Texture
+{
+    OBJECT(Texture2D);
+    
+public:
+    /// Construct.
+    Texture2D(Context* context);
+    /// Destruct.
+    virtual ~Texture2D();
+    /// Register object factory.
+    static void RegisterObject(Context* context);
+    
+    /// Load resource from stream. May be called from a worker thread. Return true if successful.
+    virtual bool BeginLoad(Deserializer& source);
+    /// Finish resource loading. Always called from the main thread. Return true if successful.
+    virtual bool EndLoad();
+    /// Release texture.
+    virtual void Release();
+    
+    /// Set size, format and usage. Zero size will follow application window size. Return true if successful.
+    bool SetSize(int width, int height, unsigned format, TextureUsage usage = TEXTURE_STATIC);
+    /// Set data either partially or fully on a mip level. Return true if successful.
+    bool SetData(unsigned level, int x, int y, int width, int height, const void* data);
+    /// Set data from an image. Return true if successful. Optionally make a single channel image alpha-only.
+    bool SetData(SharedPtr<Image> image, bool useAlpha = false);
+    
+    /// Get data from a mip level. The destination buffer must be big enough. Return true if successful.
+    bool GetData(unsigned level, void* dest) const;
+    /// Return render surface.
+    RenderSurface* GetRenderSurface() const { return renderSurface_; }
+    
+private:
+    /// Create texture.
+    bool Create();
+    /// Handle render surface update event.
+    void HandleRenderSurfaceUpdate(StringHash eventType, VariantMap& eventData);
+    
+    /// Render surface.
+    SharedPtr<RenderSurface> renderSurface_;
+    /// Image file acquired during BeginLoad.
+    SharedPtr<Image> loadImage_;
+    /// Parameter file acquired during BeginLoad.
+    SharedPtr<XMLFile> loadParameters_;
+};
+
+}

+ 572 - 0
Source/Urho3D/Graphics/Direct3D11/D3D11Texture3D.cpp

@@ -0,0 +1,572 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../../Core/Context.h"
+#include "../../IO/FileSystem.h"
+#include "../../Graphics/Graphics.h"
+#include "../../Graphics/GraphicsEvents.h"
+#include "../../Graphics/GraphicsImpl.h"
+#include "../../IO/Log.h"
+#include "../../Graphics/Renderer.h"
+#include "../../Core/Profiler.h"
+#include "../../Resource/ResourceCache.h"
+#include "../../Graphics/Texture3D.h"
+#include "../../Resource/XMLFile.h"
+
+#include "../../DebugNew.h"
+
+namespace Urho3D
+{
+
+Texture3D::Texture3D(Context* context) :
+    Texture(context)
+{
+}
+
+Texture3D::~Texture3D()
+{
+    Release();
+}
+
+void Texture3D::RegisterObject(Context* context)
+{
+    context->RegisterFactory<Texture3D>();
+}
+
+bool Texture3D::BeginLoad(Deserializer& source)
+{
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+
+    // In headless mode, do not actually load the texture, just return success
+    if (!graphics_)
+        return true;
+    
+    String texPath, texName, texExt;
+    SplitPath(GetName(), texPath, texName, texExt);
+    
+    cache->ResetDependencies(this);
+
+    loadParameters_ = new XMLFile(context_);
+    if (!loadParameters_->Load(source))
+    {
+        loadParameters_.Reset();
+        return false;
+    }
+    
+    XMLElement textureElem = loadParameters_->GetRoot();
+    XMLElement volumeElem = textureElem.GetChild("volume");
+    XMLElement colorlutElem = textureElem.GetChild("colorlut");
+
+    if (volumeElem)
+    {
+        String name = volumeElem.GetAttribute("name");
+        
+        String volumeTexPath, volumeTexName, volumeTexExt;
+        SplitPath(name, volumeTexPath, volumeTexName, volumeTexExt);
+        // If path is empty, add the XML file path
+        if (volumeTexPath.Empty())
+            name = texPath + name;
+
+        loadImage_ = cache->GetTempResource<Image>(name);
+        // Precalculate mip levels if async loading
+        if (loadImage_ && GetAsyncLoadState() == ASYNC_LOADING)
+            loadImage_->PrecalculateLevels();
+        cache->StoreResourceDependency(this, name);
+        return true;
+    }
+    else if (colorlutElem)
+    {
+        String name = colorlutElem.GetAttribute("name");
+        
+        String colorlutTexPath, colorlutTexName, colorlutTexExt;
+        SplitPath(name, colorlutTexPath, colorlutTexName, colorlutTexExt);
+        // If path is empty, add the XML file path
+        if (colorlutTexPath.Empty())
+            name = texPath + name;
+
+        SharedPtr<File> file = GetSubsystem<ResourceCache>()->GetFile(name);
+        loadImage_ = new Image(context_);
+        if (!loadImage_->LoadColorLUT(*(file.Get())))
+        {
+            loadParameters_.Reset();
+            loadImage_.Reset();
+            return false;
+        }
+        // Precalculate mip levels if async loading
+        if (loadImage_ && GetAsyncLoadState() == ASYNC_LOADING)
+            loadImage_->PrecalculateLevels();
+        cache->StoreResourceDependency(this, name);
+        return true;
+    }
+
+    LOGERROR("Texture3D XML data for " + GetName() + " did not contain either volume or colorlut element");
+    return false;
+}
+
+bool Texture3D::EndLoad()
+{
+    // In headless mode, do not actually load the texture, just return success
+    if (!graphics_)
+        return true;
+    
+    // If over the texture budget, see if materials can be freed to allow textures to be freed
+    CheckTextureBudget(GetTypeStatic());
+
+    SetParameters(loadParameters_);
+    bool success = SetData(loadImage_);
+    
+    loadImage_.Reset();
+    loadParameters_.Reset();
+    
+    return success;
+}
+
+void Texture3D::Release()
+{
+    if (object_)
+    {
+        if (!graphics_)
+            return;
+        
+        for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
+        {
+            if (graphics_->GetTexture(i) == this)
+                graphics_->SetTexture(i, 0);
+        }
+        
+        if (renderSurface_)
+            renderSurface_->Release();
+        
+        ((ID3D11Resource*)object_)->Release();
+        object_ = 0;
+
+        if (shaderResourceView_)
+        {
+            ((ID3D11ShaderResourceView*)shaderResourceView_)->Release();
+            shaderResourceView_ = 0;
+        }
+
+        if (sampler_)
+        {
+            ((ID3D11SamplerState*)sampler_)->Release();
+            sampler_ = 0;
+        }
+    }
+    else
+    {
+        if (renderSurface_)
+            renderSurface_->Release();
+    }
+}
+
+bool Texture3D::SetSize(int width, int height, int depth, unsigned format, TextureUsage usage)
+{
+    // Delete the old rendersurface if any
+    renderSurface_.Reset();
+    usage_ = usage;
+
+    if (usage_ == TEXTURE_RENDERTARGET)
+    {
+        renderSurface_ = new RenderSurface(this);
+
+        // Clamp mode addressing by default, nearest filtering, and mipmaps disabled
+        addressMode_[COORD_U] = ADDRESS_CLAMP;
+        addressMode_[COORD_V] = ADDRESS_CLAMP;
+        filterMode_ = FILTER_NEAREST;
+        requestedLevels_ = 1;
+    }
+    
+    if (usage_ == TEXTURE_RENDERTARGET)
+        SubscribeToEvent(E_RENDERSURFACEUPDATE, HANDLER(Texture3D, HandleRenderSurfaceUpdate));
+    else
+        UnsubscribeFromEvent(E_RENDERSURFACEUPDATE);
+
+    width_ = width;
+    height_ = height;
+    depth_ = depth;
+    format_ = format;
+    
+    return Create();
+}
+
+bool Texture3D::SetData(unsigned level, int x, int y, int z, int width, int height, int depth, const void* data)
+{
+    PROFILE(SetTextureData);
+    
+    if (!object_)
+    {
+        LOGERROR("No texture created, can not set data");
+        return false;
+    }
+    
+    if (!data)
+    {
+        LOGERROR("Null source for setting data");
+        return false;
+    }
+    
+    if (level >= levels_)
+    {
+        LOGERROR("Illegal mip level for setting data");
+        return false;
+    }
+    
+    int levelWidth = GetLevelWidth(level);
+    int levelHeight = GetLevelHeight(level);
+    int levelDepth = GetLevelDepth(level);
+    if (x < 0 || x + width > levelWidth || y < 0 || y + height > levelHeight || z < 0 || z + depth > levelDepth || width <= 0 || height <= 0 || depth <= 0)
+    {
+        LOGERROR("Illegal dimensions for setting data");
+        return false;
+    }
+    
+    // If compressed, align the update region on a block
+    if (IsCompressed())
+    {
+        x &= ~3;
+        y &= ~3;
+        width += 3;
+        width &= 0xfffffffc;
+        height += 3;
+        height &= 0xfffffffc;
+    }
+
+    unsigned char* src = (unsigned char*)data;
+    unsigned rowSize = GetRowDataSize(width);
+    unsigned rowStart = GetRowDataSize(x);
+    unsigned subResource = D3D11CalcSubresource(level, 0, levels_);
+
+    if (usage_ == TEXTURE_DYNAMIC)
+    {
+        if (IsCompressed())
+        {
+            height = (height + 3) >> 2;
+            y >>= 2;
+        }
+
+        D3D11_MAPPED_SUBRESOURCE mappedData;
+        mappedData.pData = 0;
+
+        graphics_->GetImpl()->GetDeviceContext()->Map((ID3D11Resource*)object_, subResource, D3D11_MAP_WRITE_DISCARD, 0,
+            &mappedData);
+        if (mappedData.pData)
+        {
+            for (int page = 0; page < depth; ++page)
+            {
+                for (int row = 0; row < height; ++row)
+                {
+                    memcpy((unsigned char*)mappedData.pData + (page + z) * mappedData.DepthPitch + (row + y) * mappedData.RowPitch
+                        + rowStart, src + row * rowSize, rowSize);
+                }
+            }
+
+            graphics_->GetImpl()->GetDeviceContext()->Unmap((ID3D11Resource*)object_, subResource);
+        }
+        else
+        {
+            LOGERROR("Failed to map texture for update");
+            return false;
+        }
+    }
+    else
+    {
+        if (IsCompressed())
+            levelHeight = (levelHeight + 3) >> 2;
+
+        D3D11_BOX destBox;
+        destBox.left = x;
+        destBox.right = x + width;
+        destBox.top = y;
+        destBox.bottom = y + height;
+        destBox.front = z;
+        destBox.back = z + depth;
+
+        graphics_->GetImpl()->GetDeviceContext()->UpdateSubresource((ID3D11Resource*)object_, subResource, &destBox, data,
+            rowSize, levelHeight * rowSize);
+    }
+
+    return true;
+}
+
+bool Texture3D::SetData(SharedPtr<Image> image, bool useAlpha)
+{
+    if (!image)
+    {
+        LOGERROR("Null image, can not load texture");
+        return false;
+    }
+    
+    unsigned memoryUse = sizeof(Texture3D);
+    
+    int quality = QUALITY_HIGH;
+    Renderer* renderer = GetSubsystem<Renderer>();
+    if (renderer)
+        quality = renderer->GetTextureQuality();
+    
+    if (!image->IsCompressed())
+    {
+        unsigned char* levelData = image->GetData();
+        int levelWidth = image->GetWidth();
+        int levelHeight = image->GetHeight();
+        int levelDepth = image->GetDepth();
+        unsigned components = image->GetComponents();
+        unsigned format = 0;
+        
+        // Discard unnecessary mip levels
+        for (unsigned i = 0; i < mipsToSkip_[quality]; ++i)
+        {
+            image = image->GetNextLevel();
+            levelData = image->GetData();
+            levelWidth = image->GetWidth();
+            levelHeight = image->GetHeight();
+            levelDepth = image->GetDepth();
+        }
+        
+        switch (components)
+        {
+        case 1:
+            format = useAlpha ? Graphics::GetAlphaFormat() : Graphics::GetLuminanceFormat();
+            break;
+            
+        case 2:
+            format = Graphics::GetLuminanceAlphaFormat();
+            break;
+            
+        case 3:
+            format = Graphics::GetRGBFormat();
+            break;
+            
+        case 4:
+            format = Graphics::GetRGBAFormat();
+            break;
+        }
+        
+        // If image was previously compressed, reset number of requested levels to avoid error if level count is too high for new size
+        if (IsCompressed() && requestedLevels_ > 1)
+            requestedLevels_ = 0;
+        SetSize(levelWidth, levelHeight, levelDepth, format);
+        
+        for (unsigned i = 0; i < levels_; ++i)
+        {
+            // D3D11 needs RGB data as 4-component
+            SharedArrayPtr<unsigned char> convertedData;
+            if (components == 3)
+            {
+                convertedData = ConvertRGBToRGBA(levelWidth, levelHeight, levelDepth, levelData);
+                levelData = convertedData;
+            }
+
+            SetData(i, 0, 0, 0, levelWidth, levelHeight, levelDepth, levelData);
+            memoryUse += levelWidth * levelHeight * levelDepth * components;
+            
+            if (i < levels_ - 1)
+            {
+                image = image->GetNextLevel();
+                levelData = image->GetData();
+                levelWidth = image->GetWidth();
+                levelHeight = image->GetHeight();
+                levelDepth = image->GetDepth();
+            }
+        }
+    }
+    else
+    {
+        int width = image->GetWidth();
+        int height = image->GetHeight();
+        int depth = image->GetDepth();
+        unsigned levels = image->GetNumCompressedLevels();
+        unsigned format = graphics_->GetFormat(image->GetCompressedFormat());
+        bool needDecompress = false;
+        
+        if (!format)
+        {
+            format = Graphics::GetRGBAFormat();
+            needDecompress = true;
+        }
+        
+        unsigned mipsToSkip = mipsToSkip_[quality];
+        if (mipsToSkip >= levels)
+            mipsToSkip = levels - 1;
+        while (mipsToSkip && (width / (1 << mipsToSkip) < 4 || height / (1 << mipsToSkip) < 4 || depth / (1 << mipsToSkip) < 4))
+            --mipsToSkip;
+        width /= (1 << mipsToSkip);
+        height /= (1 << mipsToSkip);
+        depth /= (1 << mipsToSkip);
+        
+        SetNumLevels(Max((int)(levels - mipsToSkip), 1));
+        SetSize(width, height, depth, format);
+        
+        for (unsigned i = 0; i < levels_ && i < levels - mipsToSkip; ++i)
+        {
+            CompressedLevel level = image->GetCompressedLevel(i + mipsToSkip);
+            if (!needDecompress)
+            {
+                SetData(i, 0, 0, 0, level.width_, level.height_, level.depth_, level.data_);
+                memoryUse += level.depth_ * level.rows_ * level.rowSize_;
+            }
+            else
+            {
+                unsigned char* rgbaData = new unsigned char[level.width_ * level.height_ * level.depth_ * 4];
+                level.Decompress(rgbaData);
+                SetData(i, 0, 0, 0, level.width_, level.height_, level.depth_, rgbaData);
+                memoryUse += level.width_ * level.height_ * level.depth_ * 4;
+                delete[] rgbaData;
+            }
+        }
+    }
+    
+    SetMemoryUse(memoryUse);
+    return true;
+}
+
+bool Texture3D::GetData(unsigned level, void* dest) const
+{
+    if (!object_)
+    {
+        LOGERROR("No texture created, can not get data");
+        return false;
+    }
+    
+    if (!dest)
+    {
+        LOGERROR("Null destination for getting data");
+        return false;
+    }
+    
+    if (level >= levels_)
+    {
+        LOGERROR("Illegal mip level for getting data");
+        return false;
+    }
+    
+    int levelWidth = GetLevelWidth(level);
+    int levelHeight = GetLevelHeight(level);
+    int levelDepth = GetLevelDepth(level);
+    
+    D3D11_TEXTURE3D_DESC textureDesc;
+    memset(&textureDesc, 0, sizeof textureDesc);
+    textureDesc.Width = levelWidth;
+    textureDesc.Height = levelHeight;
+    textureDesc.Depth = levelDepth;
+    textureDesc.MipLevels = 1;
+    textureDesc.Format = (DXGI_FORMAT)format_;
+    textureDesc.Usage = D3D11_USAGE_STAGING;
+    textureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+
+    ID3D11Texture3D* stagingTexture = 0;
+    graphics_->GetImpl()->GetDevice()->CreateTexture3D(&textureDesc, 0, &stagingTexture);
+    if (!stagingTexture)
+    {
+        LOGERROR("Failed to create staging texture for GetData");
+        return false;
+    }
+
+    unsigned srcSubResource = D3D11CalcSubresource(level, 0, levels_);
+    D3D11_BOX srcBox;
+    srcBox.left = 0;
+    srcBox.right = levelWidth;
+    srcBox.top = 0;
+    srcBox.bottom = levelHeight;
+    srcBox.front = 0;
+    srcBox.back = levelDepth;
+    graphics_->GetImpl()->GetDeviceContext()->CopySubresourceRegion(stagingTexture, 0, 0, 0, 0, (ID3D11Resource*)object_,
+        srcSubResource, &srcBox);
+
+    D3D11_MAPPED_SUBRESOURCE mappedData;
+    mappedData.pData = 0;
+    unsigned rowSize = GetRowDataSize(levelWidth);
+    unsigned numRows = IsCompressed() ? (levelHeight + 3) >> 2 : levelHeight;
+
+    graphics_->GetImpl()->GetDeviceContext()->Map((ID3D11Resource*)stagingTexture, 0, D3D11_MAP_READ, 0, &mappedData);
+    if (mappedData.pData)
+    {
+        for (int page = 0; page < levelDepth; ++page)
+        {
+            for (unsigned row = 0; row < numRows; ++row)
+            {
+                memcpy((unsigned char*)dest + (page * numRows + row) * rowSize, (unsigned char*)mappedData.pData + page *
+                    mappedData.DepthPitch + row * mappedData.RowPitch, rowSize);
+            }
+        }
+        graphics_->GetImpl()->GetDeviceContext()->Unmap((ID3D11Resource*)stagingTexture, 0);
+        stagingTexture->Release();
+        return true;
+    }
+    else
+    {
+        LOGERROR("Failed to map staging texture for GetData");
+        stagingTexture->Release();
+        return false;
+    }
+}
+
+bool Texture3D::Create()
+{
+    Release();
+    
+    if (!graphics_ || !width_ || !height_ || !depth_)
+        return false;
+    
+    levels_ = CheckMaxLevels(width_, height_, depth_, requestedLevels_);
+
+    D3D11_TEXTURE3D_DESC textureDesc;
+    memset(&textureDesc, 0, sizeof textureDesc);
+    textureDesc.Width = width_;
+    textureDesc.Height = height_;
+    textureDesc.Depth = depth_;
+    textureDesc.MipLevels = levels_;
+    textureDesc.Format = (DXGI_FORMAT)(sRGB_ ? GetSRGBFormat(format_) : format_);
+    textureDesc.Usage = usage_ == TEXTURE_DYNAMIC ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT;
+    textureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
+    textureDesc.CPUAccessFlags = usage_ == TEXTURE_DYNAMIC ? D3D11_CPU_ACCESS_WRITE : 0;
+
+    graphics_->GetImpl()->GetDevice()->CreateTexture3D(&textureDesc, 0, (ID3D11Texture3D**)&object_);
+    if (!object_)
+    {
+        LOGERROR("Failed to create texture");
+        return false;
+    }
+
+    D3D11_SHADER_RESOURCE_VIEW_DESC resourceViewDesc;
+    memset(&resourceViewDesc, 0, sizeof resourceViewDesc);
+    resourceViewDesc.Format = (DXGI_FORMAT)GetSRVFormat(textureDesc.Format);
+    resourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D;
+    resourceViewDesc.Texture3D.MipLevels = (unsigned)levels_;
+
+    graphics_->GetImpl()->GetDevice()->CreateShaderResourceView((ID3D11Resource*)object_, &resourceViewDesc,
+        (ID3D11ShaderResourceView**)&shaderResourceView_);
+    if (!shaderResourceView_)
+    {
+        LOGERROR("Failed to create shader resource view for texture");
+        return false;
+    }
+
+    return true;
+}
+
+void Texture3D::HandleRenderSurfaceUpdate(StringHash eventType, VariantMap& eventData)
+{
+    if (renderSurface_ && renderSurface_->GetUpdateMode() == SURFACE_UPDATEALWAYS)
+        renderSurface_->QueueUpdate();
+}
+
+}

+ 80 - 0
Source/Urho3D/Graphics/Direct3D11/D3D11Texture3D.h

@@ -0,0 +1,80 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../../Graphics/RenderSurface.h"
+#include "../../Container/Ptr.h"
+#include "../../Graphics/Texture.h"
+
+namespace Urho3D
+{
+
+class Image;
+
+/// 3D texture resource.
+class URHO3D_API Texture3D : public Texture
+{
+    OBJECT(Texture3D);
+    
+public:
+    /// Construct.
+    Texture3D(Context* context);
+    /// Destruct.
+    virtual ~Texture3D();
+    /// Register object factory.
+    static void RegisterObject(Context* context);
+    
+    /// Load resource from stream. May be called from a worker thread. Return true if successful.
+    virtual bool BeginLoad(Deserializer& source);
+    /// Finish resource loading. Always called from the main thread. Return true if successful.
+    virtual bool EndLoad();
+    /// Release texture.
+    virtual void Release();
+    
+    /// Set size, format and usage. Zero size will follow application window size. Return true if successful.
+    bool SetSize(int width, int height, int depth, unsigned format, TextureUsage usage = TEXTURE_STATIC);
+    /// Set data either partially or fully on a mip level. Return true if successful.
+    bool SetData(unsigned level, int x, int y, int z, int width, int height, int depth, const void* data);
+    /// Set data from an image. Return true if successful. Optionally make a single channel image alpha-only.
+    bool SetData(SharedPtr<Image> image, bool useAlpha = false);
+    
+    /// Get data from a mip level. The destination buffer must be big enough. Return true if successful.
+    bool GetData(unsigned level, void* dest) const;
+    /// Return render surface.
+    RenderSurface* GetRenderSurface() const { return renderSurface_; }
+    
+private:
+    /// Create texture.
+    bool Create();
+    /// Handle render surface update event.
+    void HandleRenderSurfaceUpdate(StringHash eventType, VariantMap& eventData);
+    
+    /// Render surface.
+    SharedPtr<RenderSurface> renderSurface_;
+    /// Image file acquired during BeginLoad.
+    SharedPtr<Image> loadImage_;
+    /// Parameter file acquired during BeginLoad.
+    SharedPtr<XMLFile> loadParameters_;
+};
+
+}

+ 746 - 0
Source/Urho3D/Graphics/Direct3D11/D3D11TextureCube.cpp

@@ -0,0 +1,746 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../../Core/Context.h"
+#include "../../IO/FileSystem.h"
+#include "../../Graphics/Graphics.h"
+#include "../../Graphics/GraphicsEvents.h"
+#include "../../Graphics/GraphicsImpl.h"
+#include "../../IO/Log.h"
+#include "../../Core/Profiler.h"
+#include "../../Graphics/Renderer.h"
+#include "../../Resource/ResourceCache.h"
+#include "../../Graphics/TextureCube.h"
+#include "../../Resource/XMLFile.h"
+
+#include "../../DebugNew.h"
+
+#ifdef _MSC_VER
+#pragma warning(disable:4355)
+#endif
+
+namespace Urho3D
+{
+
+static const char* cubeMapLayoutNames[] = {
+    "horizontal",
+    "horizontalnvidia",
+    "horizontalcross",
+    "verticalcross",
+    "blender",
+    0
+};
+
+static SharedPtr<Image> GetTileImage(Image* src, int tileX, int tileY, int tileWidth, int tileHeight)
+{
+    return SharedPtr<Image>(src->GetSubimage(IntRect(tileX * tileWidth, tileY * tileHeight, (tileX + 1) * tileWidth, (tileY + 1) * tileHeight)));
+}
+
+TextureCube::TextureCube(Context* context) :
+    Texture(context),
+    lockedLevel_(-1)
+{
+    // Default to clamp mode addressing
+    addressMode_[COORD_U] = ADDRESS_CLAMP;
+    addressMode_[COORD_V] = ADDRESS_CLAMP;
+    addressMode_[COORD_W] = ADDRESS_CLAMP;
+    
+    for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
+        faceMemoryUse_[i] = 0;
+}
+
+TextureCube::~TextureCube()
+{
+    Release();
+}
+
+void TextureCube::RegisterObject(Context* context)
+{
+    context->RegisterFactory<TextureCube>();
+}
+
+bool TextureCube::BeginLoad(Deserializer& source)
+{
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    
+    // In headless mode, do not actually load the texture, just return success
+    if (!graphics_)
+        return true;
+    
+    cache->ResetDependencies(this);
+
+    String texPath, texName, texExt;
+    SplitPath(GetName(), texPath, texName, texExt);
+    
+    loadParameters_ = (new XMLFile(context_));
+    if (!loadParameters_->Load(source))
+    {
+        loadParameters_.Reset();
+        return false;
+    }
+    
+    loadImages_.Clear();
+
+    XMLElement textureElem = loadParameters_->GetRoot();
+    XMLElement imageElem = textureElem.GetChild("image");
+    // Single image and multiple faces with layout
+    if (imageElem)
+    {
+        String name = imageElem.GetAttribute("name");
+        // If path is empty, add the XML file path
+        if (GetPath(name).Empty())
+            name = texPath + name;
+        
+        CubeMapLayout layout = (CubeMapLayout)GetStringListIndex(imageElem.GetAttribute("layout").CString(), cubeMapLayoutNames, CML_HORIZONTAL);
+        SharedPtr<Image> image = cache->GetTempResource<Image>(name);
+        if (!image)
+            return false;
+        
+        int faceWidth, faceHeight;
+        loadImages_.Resize(MAX_CUBEMAP_FACES);
+        
+        switch (layout)
+        {
+        case CML_HORIZONTAL:
+            faceWidth = image->GetWidth() / MAX_CUBEMAP_FACES;
+            faceHeight = image->GetHeight();
+            loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 0, 0, faceWidth, faceHeight);
+            loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 1, 0, faceWidth, faceHeight);
+            loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 2, 0, faceWidth, faceHeight);
+            loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 3, 0, faceWidth, faceHeight);
+            loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 4, 0, faceWidth, faceHeight);
+            loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 5, 0, faceWidth, faceHeight);
+            break;
+            
+        case CML_HORIZONTALNVIDIA:
+            faceWidth = image->GetWidth() / MAX_CUBEMAP_FACES;
+            faceHeight = image->GetHeight();
+            for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
+                loadImages_[i] = GetTileImage(image, i, 0, faceWidth, faceHeight);
+            break;
+            
+        case CML_HORIZONTALCROSS:
+            faceWidth = image->GetWidth() / 4;
+            faceHeight = image->GetHeight() / 3;
+            loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 1, 0, faceWidth, faceHeight);
+            loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 0, 1, faceWidth, faceHeight);
+            loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 1, 1, faceWidth, faceHeight);
+            loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 2, 1, faceWidth, faceHeight);
+            loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 3, 1, faceWidth, faceHeight);
+            loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 1, 2, faceWidth, faceHeight);
+            break;
+            
+        case CML_VERTICALCROSS:
+            faceWidth = image->GetWidth() / 3;
+            faceHeight = image->GetHeight() / 4;
+            loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 1, 0, faceWidth, faceHeight);
+            loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 0, 1, faceWidth, faceHeight);
+            loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 1, 1, faceWidth, faceHeight);
+            loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 2, 1, faceWidth, faceHeight);
+            loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 1, 2, faceWidth, faceHeight);
+            loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 1, 3, faceWidth, faceHeight);
+            if (loadImages_[FACE_NEGATIVE_Z])
+            {
+                loadImages_[FACE_NEGATIVE_Z]->FlipVertical();
+                loadImages_[FACE_NEGATIVE_Z]->FlipHorizontal();
+            }
+            break;
+            
+        case CML_BLENDER:
+            faceWidth = image->GetWidth() / 3;
+            faceHeight = image->GetHeight() / 2;
+            loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 0, 0, faceWidth, faceHeight);
+            loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 1, 0, faceWidth, faceHeight);
+            loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 2, 0, faceWidth, faceHeight);
+            loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 0, 1, faceWidth, faceHeight);
+            loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 1, 1, faceWidth, faceHeight);
+            loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 2, 1, faceWidth, faceHeight);
+            break;
+        }
+    }
+    // Face per image
+    else
+    {
+        XMLElement faceElem = textureElem.GetChild("face");
+        while (faceElem)
+        {
+            String name = faceElem.GetAttribute("name");
+            
+            // If path is empty, add the XML file path
+            if (GetPath(name).Empty())
+                name = texPath + name;
+            
+            loadImages_.Push(cache->GetTempResource<Image>(name));
+            cache->StoreResourceDependency(this, name);
+            
+            faceElem = faceElem.GetNext("face");
+        }
+    }
+
+    // Precalculate mip levels if async loading
+    if (GetAsyncLoadState() == ASYNC_LOADING)
+    {
+        for (unsigned i = 0; i < loadImages_.Size(); ++i)
+        {
+            if (loadImages_[i])
+                loadImages_[i]->PrecalculateLevels();
+        }
+    }
+
+    return true;
+}
+
+bool TextureCube::EndLoad()
+{
+    // In headless mode, do not actually load the texture, just return success
+    if (!graphics_)
+        return true;
+    
+    // If over the texture budget, see if materials can be freed to allow textures to be freed
+    CheckTextureBudget(GetTypeStatic());
+
+    SetParameters(loadParameters_);
+    
+    for (unsigned i = 0; i < loadImages_.Size() && i < MAX_CUBEMAP_FACES; ++i)
+        SetData((CubeMapFace)i, loadImages_[i]);
+    
+    loadImages_.Clear();
+    loadParameters_.Reset();
+    
+    return true;
+}
+
+void TextureCube::Release()
+{
+    if (object_)
+    {
+        if (!graphics_)
+            return;
+        
+        for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
+        {
+            if (graphics_->GetTexture(i) == this)
+                graphics_->SetTexture(i, 0);
+        }
+        
+        for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
+        {
+            if (renderSurfaces_[i])
+                renderSurfaces_[i]->Release();
+        }
+        
+        ((ID3D11Resource*)object_)->Release();
+        object_ = 0;
+
+        if (shaderResourceView_)
+        {
+            ((ID3D11ShaderResourceView*)shaderResourceView_)->Release();
+            shaderResourceView_ = 0;
+        }
+
+        if (sampler_)
+        {
+            ((ID3D11SamplerState*)sampler_)->Release();
+            sampler_ = 0;
+        }
+    }
+}
+
+bool TextureCube::SetSize(int size, unsigned format, TextureUsage usage)
+{
+    if (size <= 0)
+    {
+        LOGERROR("Zero or negative cube texture size");
+        return false;
+    }
+    if (usage == TEXTURE_DEPTHSTENCIL)
+    {
+        LOGERROR("Depth-stencil usage not supported for cube maps");
+        return false;
+    }
+    
+    // Delete the old rendersurfaces if any
+    for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
+    {
+        renderSurfaces_[i].Reset();
+        faceMemoryUse_[i] = 0;
+    }
+    
+    usage_ = usage;
+    if (usage_ == TEXTURE_RENDERTARGET)
+    {
+        for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
+            renderSurfaces_[i] = new RenderSurface(this);
+        
+        // Nearest filtering and mipmaps disabled by default
+        filterMode_ = FILTER_NEAREST;
+        requestedLevels_ = 1;
+    }
+    
+    if (usage_ == TEXTURE_RENDERTARGET)
+        SubscribeToEvent(E_RENDERSURFACEUPDATE, HANDLER(TextureCube, HandleRenderSurfaceUpdate));
+    else
+        UnsubscribeFromEvent(E_RENDERSURFACEUPDATE);
+    
+    width_ = size;
+    height_ = size;
+    format_ = format;
+    
+    return Create();
+}
+
+bool TextureCube::SetData(CubeMapFace face, unsigned level, int x, int y, int width, int height, const void* data)
+{
+    PROFILE(SetTextureData);
+    
+    if (!object_)
+    {
+        LOGERROR("No texture created, can not set data");
+        return false;
+    }
+    
+    if (!data)
+    {
+        LOGERROR("Null source for setting data");
+        return false;
+    }
+    
+    if (level >= levels_)
+    {
+        LOGERROR("Illegal mip level for setting data");
+        return false;
+    }
+    
+    int levelWidth = GetLevelWidth(level);
+    int levelHeight = GetLevelHeight(level);
+    if (x < 0 || x + width > levelWidth || y < 0 || y + height > levelHeight || width <= 0 || height <= 0)
+    {
+        LOGERROR("Illegal dimensions for setting data");
+        return false;
+    }
+
+    // If compressed, align the update region on a block
+    if (IsCompressed())
+    {
+        x &= ~3;
+        y &= ~3;
+        width += 3;
+        width &= 0xfffffffc;
+        height += 3;
+        height &= 0xfffffffc;
+    }
+
+    unsigned char* src = (unsigned char*)data;
+    unsigned rowSize = GetRowDataSize(width);
+    unsigned rowStart = GetRowDataSize(x);
+    unsigned subResource = D3D11CalcSubresource(level, face, levels_);
+
+    if (usage_ == TEXTURE_DYNAMIC)
+    {
+        if (IsCompressed())
+        {
+            height = (height + 3) >> 2;
+            y >>= 2;
+        }
+
+        D3D11_MAPPED_SUBRESOURCE mappedData;
+        mappedData.pData = 0;
+
+        graphics_->GetImpl()->GetDeviceContext()->Map((ID3D11Resource*)object_, subResource, D3D11_MAP_WRITE_DISCARD, 0,
+            &mappedData);
+        if (mappedData.pData)
+        {
+            for (int row = 0; row < height; ++row)
+                memcpy((unsigned char*)mappedData.pData + (row + y) * mappedData.RowPitch + rowStart, src + row * rowSize, rowSize);
+            graphics_->GetImpl()->GetDeviceContext()->Unmap((ID3D11Resource*)object_, subResource);
+        }
+        else
+        {
+            LOGERROR("Failed to map texture for update");
+            return false;
+        }
+    }
+    else
+    {
+        D3D11_BOX destBox;
+        destBox.left = x;
+        destBox.right = x + width;
+        destBox.top = y;
+        destBox.bottom = y + height;
+        destBox.front = 0;
+        destBox.back = 1;
+
+        graphics_->GetImpl()->GetDeviceContext()->UpdateSubresource((ID3D11Resource*)object_, subResource, &destBox, data,
+            rowSize, 0);
+    }
+
+    return true;
+}
+
+bool TextureCube::SetData(CubeMapFace face, Deserializer& source)
+{
+    SharedPtr<Image> image(new Image(context_));
+    if (!image->Load(source))
+        return false;
+    
+    return SetData(face, image);
+}
+
+bool TextureCube::SetData(CubeMapFace face, SharedPtr<Image> image, bool useAlpha)
+{
+    if (!image)
+    {
+        LOGERROR("Null image, can not load texture");
+        return false;
+    }
+    
+    unsigned memoryUse = 0;
+    
+    int quality = QUALITY_HIGH;
+    Renderer* renderer = GetSubsystem<Renderer>();
+    if (renderer)
+        quality = renderer->GetTextureQuality();
+    
+    if (!image->IsCompressed())
+    {
+        unsigned char* levelData = image->GetData();
+        int levelWidth = image->GetWidth();
+        int levelHeight = image->GetHeight();
+        unsigned components = image->GetComponents();
+        unsigned format = 0;
+        
+        if (levelWidth != levelHeight)
+        {
+            LOGERROR("Cube texture width not equal to height");
+            return false;
+        }
+        
+        // Discard unnecessary mip levels
+        for (unsigned i = 0; i < mipsToSkip_[quality]; ++i)
+        {
+            image = image->GetNextLevel();
+            levelData = image->GetData();
+            levelWidth = image->GetWidth();
+            levelHeight = image->GetHeight();
+        }
+        
+        switch (components)
+        {
+        case 1:
+            format = useAlpha ? Graphics::GetAlphaFormat() : Graphics::GetLuminanceFormat();
+            break;
+            
+        case 2:
+            format = Graphics::GetLuminanceAlphaFormat();
+            break;
+            
+        case 3:
+            format = Graphics::GetRGBFormat();
+            break;
+            
+        case 4:
+            format = Graphics::GetRGBAFormat();
+            break;
+        }
+        
+        // Create the texture when face 0 is being loaded, check that rest of the faces are same size & format
+        if (!face)
+        {
+            // If image was previously compressed, reset number of requested levels to avoid error if level count is too high for new size
+            if (IsCompressed() && requestedLevels_ > 1)
+                requestedLevels_ = 0;
+            SetSize(levelWidth, format);
+        }
+        else
+        {
+            if (!object_)
+            {
+                LOGERROR("Cube texture face 0 must be loaded first");
+                return false;
+            }
+            if (levelWidth != width_ || format != format_)
+            {
+                LOGERROR("Cube texture face does not match size or format of face 0");
+                return false;
+            }
+        }
+        
+        for (unsigned i = 0; i < levels_; ++i)
+        {
+            // D3D11 needs RGB data as 4-component
+            SharedArrayPtr<unsigned char> convertedData;
+            if (components == 3)
+            {
+                convertedData = ConvertRGBToRGBA(levelWidth, levelHeight, levelData);
+                levelData = convertedData;
+            }
+
+            SetData(face, i, 0, 0, levelWidth, levelHeight, levelData);
+            memoryUse += levelWidth * levelHeight * components;
+            
+            if (i < levels_ - 1)
+            {
+                image = image->GetNextLevel();
+                levelData = image->GetData();
+                levelWidth = image->GetWidth();
+                levelHeight = image->GetHeight();
+            }
+        }
+    }
+    else
+    {
+        int width = image->GetWidth();
+        int height = image->GetHeight();
+        unsigned levels = image->GetNumCompressedLevels();
+        unsigned format = graphics_->GetFormat(image->GetCompressedFormat());
+        bool needDecompress = false;
+        
+        if (width != height)
+        {
+            LOGERROR("Cube texture width not equal to height");
+            return false;
+        }
+        
+        if (!format)
+        {
+            format = Graphics::GetRGBAFormat();
+            needDecompress = true;
+        }
+        
+        unsigned mipsToSkip = mipsToSkip_[quality];
+        if (mipsToSkip >= levels)
+            mipsToSkip = levels - 1;
+        while (mipsToSkip && (width / (1 << mipsToSkip) < 4 || height / (1 << mipsToSkip) < 4))
+            --mipsToSkip;
+        width /= (1 << mipsToSkip);
+        height /= (1 << mipsToSkip);
+        
+        // Create the texture when face 0 is being loaded, assume rest of the faces are same size & format
+        if (!face)
+        {
+            SetNumLevels(Max((int)(levels - mipsToSkip), 1));
+            SetSize(width, format);
+        }
+        else
+        {
+            if (!object_)
+            {
+                LOGERROR("Cube texture face 0 must be loaded first");
+                return false;
+            }
+            if (width != width_ || format != format_)
+            {
+                LOGERROR("Cube texture face does not match size or format of face 0");
+                return false;
+            }
+        }
+        
+        for (unsigned i = 0; i < levels_ && i < levels - mipsToSkip; ++i)
+        {
+            CompressedLevel level = image->GetCompressedLevel(i + mipsToSkip);
+            if (!needDecompress)
+            {
+                SetData(face, i, 0, 0, level.width_, level.height_, level.data_);
+                memoryUse += level.rows_ * level.rowSize_;
+            }
+            else
+            {
+                unsigned char* rgbaData = new unsigned char[level.width_ * level.height_ * 4];
+                level.Decompress(rgbaData);
+                SetData(face, i, 0, 0, level.width_, level.height_, rgbaData);
+                memoryUse += level.width_ * level.height_ * 4;
+                delete[] rgbaData;
+            }
+        }
+    }
+    
+    faceMemoryUse_[face] = memoryUse;
+    unsigned totalMemoryUse = sizeof(TextureCube);
+    for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
+        totalMemoryUse += faceMemoryUse_[i];
+    SetMemoryUse(totalMemoryUse);
+    
+    return true;
+}
+
+bool TextureCube::GetData(CubeMapFace face, unsigned level, void* dest) const
+{
+    if (!object_)
+    {
+        LOGERROR("No texture created, can not get data");
+        return false;
+    }
+    
+    if (!dest)
+    {
+        LOGERROR("Null destination for getting data");
+        return false;
+    }
+    
+    if (level >= levels_)
+    {
+        LOGERROR("Illegal mip level for getting data");
+        return false;
+    }
+    
+    int levelWidth = GetLevelWidth(level);
+    int levelHeight = GetLevelHeight(level);
+
+    D3D11_TEXTURE2D_DESC textureDesc;
+    memset(&textureDesc, 0, sizeof textureDesc);
+    textureDesc.Width = levelWidth;
+    textureDesc.Height = levelHeight;
+    textureDesc.MipLevels = 1;
+    textureDesc.ArraySize = 1;
+    textureDesc.Format = (DXGI_FORMAT)format_;
+    textureDesc.SampleDesc.Count = 1;
+    textureDesc.SampleDesc.Quality = 0;
+    textureDesc.Usage = D3D11_USAGE_STAGING;
+    textureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+
+    ID3D11Texture2D* stagingTexture = 0;
+    graphics_->GetImpl()->GetDevice()->CreateTexture2D(&textureDesc, 0, &stagingTexture);
+    if (!stagingTexture)
+    {
+        LOGERROR("Failed to create staging texture for GetData");
+        return false;
+    }
+
+    unsigned srcSubResource = D3D11CalcSubresource(level, face, levels_);
+    D3D11_BOX srcBox;
+    srcBox.left = 0;
+    srcBox.right = levelWidth;
+    srcBox.top = 0;
+    srcBox.bottom = levelHeight;
+    srcBox.front = 0;
+    srcBox.back = 1;
+    graphics_->GetImpl()->GetDeviceContext()->CopySubresourceRegion(stagingTexture, 0, 0, 0, 0, (ID3D11Resource*)object_,
+        srcSubResource, &srcBox);
+
+    D3D11_MAPPED_SUBRESOURCE mappedData;
+    mappedData.pData = 0;
+    unsigned rowSize = GetRowDataSize(levelWidth);
+    unsigned numRows = IsCompressed() ? (levelHeight + 3) >> 2 : levelHeight;
+
+    graphics_->GetImpl()->GetDeviceContext()->Map((ID3D11Resource*)stagingTexture, 0, D3D11_MAP_READ, 0, &mappedData);
+    if (mappedData.pData)
+    {
+        for (unsigned row = 0; row < numRows; ++row)
+            memcpy((unsigned char*)dest + row * rowSize, (unsigned char*)mappedData.pData + row * mappedData.RowPitch, rowSize);
+        graphics_->GetImpl()->GetDeviceContext()->Unmap((ID3D11Resource*)stagingTexture, 0);
+        stagingTexture->Release();
+        return true;
+    }
+    else
+    {
+        LOGERROR("Failed to map staging texture for GetData");
+        stagingTexture->Release();
+        return false;
+    }
+}
+
+bool TextureCube::Create()
+{
+    Release();
+    
+    if (!graphics_ || !width_ || !height_)
+        return false;
+    
+    levels_ = CheckMaxLevels(width_, height_, requestedLevels_);
+
+    D3D11_TEXTURE2D_DESC textureDesc;
+    memset(&textureDesc, 0, sizeof textureDesc);
+    textureDesc.Width = width_;
+    textureDesc.Height = height_;
+    textureDesc.MipLevels = levels_;
+    textureDesc.ArraySize = MAX_CUBEMAP_FACES;
+    textureDesc.Format = (DXGI_FORMAT)(sRGB_ ? GetSRGBFormat(format_) : format_);
+    textureDesc.SampleDesc.Count = 1;
+    textureDesc.SampleDesc.Quality = 0;
+    textureDesc.Usage = usage_ == TEXTURE_DYNAMIC ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT;
+    textureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
+    if (usage_ == TEXTURE_RENDERTARGET)
+        textureDesc.BindFlags |= D3D11_BIND_RENDER_TARGET;
+    else if (usage_ == TEXTURE_DEPTHSTENCIL)
+        textureDesc.BindFlags |= D3D11_BIND_DEPTH_STENCIL;
+    textureDesc.CPUAccessFlags = usage_ == TEXTURE_DYNAMIC ? D3D11_CPU_ACCESS_WRITE : 0;
+    textureDesc.MiscFlags = D3D11_RESOURCE_MISC_TEXTURECUBE;
+
+    graphics_->GetImpl()->GetDevice()->CreateTexture2D(&textureDesc, 0, (ID3D11Texture2D**)&object_);
+    if (!object_)
+    {
+        LOGERROR("Failed to create texture");
+        return false;
+    }
+
+    D3D11_SHADER_RESOURCE_VIEW_DESC resourceViewDesc;
+    memset(&resourceViewDesc, 0, sizeof resourceViewDesc);
+    resourceViewDesc.Format = (DXGI_FORMAT)GetSRVFormat(textureDesc.Format);
+    resourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
+    resourceViewDesc.Texture2D.MipLevels = (unsigned)levels_;
+
+    graphics_->GetImpl()->GetDevice()->CreateShaderResourceView((ID3D11Resource*)object_, &resourceViewDesc,
+        (ID3D11ShaderResourceView**)&shaderResourceView_);
+    if (!shaderResourceView_)
+    {
+        LOGERROR("Failed to create shader resource view for texture");
+        return false;
+    }
+
+    if (usage_ == TEXTURE_RENDERTARGET)
+    {
+        for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
+        {
+            renderSurfaces_[i] = new RenderSurface(this);
+
+            D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc;
+            memset(&renderTargetViewDesc, 0, sizeof renderTargetViewDesc);
+            renderTargetViewDesc.Format = textureDesc.Format;
+            renderTargetViewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
+            renderTargetViewDesc.Texture2DArray.ArraySize = 1;
+            renderTargetViewDesc.Texture2DArray.FirstArraySlice = i;
+            renderTargetViewDesc.Texture2DArray.MipSlice = 0;
+
+            graphics_->GetImpl()->GetDevice()->CreateRenderTargetView((ID3D11Resource*)object_, &renderTargetViewDesc,
+                (ID3D11RenderTargetView**)&renderSurfaces_[i]->renderTargetView_);
+
+            if (!renderSurfaces_[i]->renderTargetView_)
+            {
+                LOGERROR("Failed to create rendertarget view for texture");
+                return false;
+            }
+        }
+    }
+
+    return true;
+}
+
+void TextureCube::HandleRenderSurfaceUpdate(StringHash eventType, VariantMap& eventData)
+{
+    for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
+    {
+        if (renderSurfaces_[i] && renderSurfaces_[i]->GetUpdateMode() == SURFACE_UPDATEALWAYS)
+            renderSurfaces_[i]->QueueUpdate();
+    }
+}
+
+}

+ 89 - 0
Source/Urho3D/Graphics/Direct3D11/D3D11TextureCube.h

@@ -0,0 +1,89 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../../Graphics/RenderSurface.h"
+#include "../../Container/Ptr.h"
+#include "../../Graphics/Texture.h"
+
+namespace Urho3D
+{
+
+class Deserializer;
+class Image;
+
+/// Cube texture resource.
+class URHO3D_API TextureCube : public Texture
+{
+    OBJECT(TextureCube);
+    
+public:
+    /// Construct.
+    TextureCube(Context* context);
+    /// Destruct.
+    virtual ~TextureCube();
+    /// Register object factory.
+    static void RegisterObject(Context* context);
+    
+    /// Load resource from stream. May be called from a worker thread. Return true if successful.
+    virtual bool BeginLoad(Deserializer& source);
+    /// Finish resource loading. Always called from the main thread. Return true if successful.
+    virtual bool EndLoad();
+    /// Release texture.
+    virtual void Release();
+    
+    /// Set size, format and usage. Return true if successful.
+    bool SetSize(int size, unsigned format, TextureUsage usage = TEXTURE_STATIC);
+    /// Set data either partially or fully on a face's mip level. Return true if successful.
+    bool SetData(CubeMapFace face, unsigned level, int x, int y, int width, int height, const void* data);
+    /// Set data of one face from a stream. Return true if successful.
+    bool SetData(CubeMapFace face, Deserializer& source);
+    /// Set data of one face from an image. Return true if successful. Optionally make a single channel image alpha-only.
+    bool SetData(CubeMapFace face, SharedPtr<Image> image, bool useAlpha = false);
+    
+    /// Get data from a face's mip level. The destination buffer must be big enough. Return true if successful.
+    bool GetData(CubeMapFace face, unsigned level, void* dest) const;
+    /// Return render surface for one face.
+    RenderSurface* GetRenderSurface(CubeMapFace face) const { return renderSurfaces_[face]; }
+    
+private:
+    /// Create texture.
+    bool Create();
+    /// Handle render surface update event.
+    void HandleRenderSurfaceUpdate(StringHash eventType, VariantMap& eventData);
+    
+    /// Render surfaces.
+    SharedPtr<RenderSurface> renderSurfaces_[MAX_CUBEMAP_FACES];
+    /// Memory use per face.
+    unsigned faceMemoryUse_[MAX_CUBEMAP_FACES];
+    /// Currently locked mip level.
+    int lockedLevel_;
+    /// Currently locked face.
+    CubeMapFace lockedFace_;
+    /// Face image files acquired during BeginLoad.
+    Vector<SharedPtr<Image> > loadImages_;
+    /// Parameter file acquired during BeginLoad.
+    SharedPtr<XMLFile> loadParameters_;
+};
+
+}

+ 479 - 0
Source/Urho3D/Graphics/Direct3D11/D3D11VertexBuffer.cpp

@@ -0,0 +1,479 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../../Graphics/Graphics.h"
+#include "../../Graphics/GraphicsImpl.h"
+#include "../../IO/Log.h"
+#include "../../Graphics/VertexBuffer.h"
+
+#include "../../DebugNew.h"
+
+namespace Urho3D
+{
+
+const unsigned VertexBuffer::elementSize[] =
+{
+    3 * sizeof(float), // Position
+    3 * sizeof(float), // Normal
+    4 * sizeof(unsigned char), // Color
+    2 * sizeof(float), // Texcoord1
+    2 * sizeof(float), // Texcoord2
+    3 * sizeof(float), // Cubetexcoord1
+    3 * sizeof(float), // Cubetexcoord2
+    4 * sizeof(float), // Tangent
+    4 * sizeof(float), // Blendweights
+    4 * sizeof(unsigned char), // Blendindices
+    4 * sizeof(float), // Instancematrix1
+    4 * sizeof(float), // Instancematrix2
+    4 * sizeof(float) // Instancematrix3
+};
+
+const char* VertexBuffer::elementSemantics[] =
+{
+    "POSITION",
+    "NORMAL",
+    "COLOR",
+    "TEXCOORD",
+    "TEXCOORD",
+    "TEXCOORD",
+    "TEXCOORD",
+    "TANGENT",
+    "BLENDWEIGHT",
+    "BLENDINDICES",
+    "TEXCOORD",
+    "TEXCOORD",
+    "TEXCOORD"
+};
+
+const unsigned VertexBuffer::elementSemanticIndices[] =
+{
+    0,
+    0,
+    0,
+    0,
+    1,
+    0,
+    1,
+    0,
+    0,
+    0,
+    2,
+    3,
+    4
+};
+
+const unsigned VertexBuffer::elementFormats[] =
+{
+    DXGI_FORMAT_R32G32B32_FLOAT,
+    DXGI_FORMAT_R32G32B32_FLOAT,
+    DXGI_FORMAT_R8G8B8A8_UNORM,
+    DXGI_FORMAT_R32G32_FLOAT,
+    DXGI_FORMAT_R32G32_FLOAT,
+    DXGI_FORMAT_R32G32B32_FLOAT,
+    DXGI_FORMAT_R32G32B32_FLOAT,
+    DXGI_FORMAT_R32G32B32A32_FLOAT,
+    DXGI_FORMAT_R32G32B32A32_FLOAT,
+    DXGI_FORMAT_R8G8B8A8_UINT,
+    DXGI_FORMAT_R32G32B32A32_FLOAT,
+    DXGI_FORMAT_R32G32B32A32_FLOAT,
+    DXGI_FORMAT_R32G32B32A32_FLOAT
+};
+
+VertexBuffer::VertexBuffer(Context* context) :
+    Object(context),
+    GPUObject(GetSubsystem<Graphics>()),
+    vertexCount_(0),
+    elementMask_(0),
+    lockState_(LOCK_NONE),
+    lockStart_(0),
+    lockCount_(0),
+    lockScratchData_(0),
+    dynamic_(false),
+    shadowed_(false)
+{
+    UpdateOffsets();
+    
+    // Force shadowing mode if graphics subsystem does not exist
+    if (!graphics_)
+        shadowed_ = true;
+}
+
+VertexBuffer::~VertexBuffer()
+{
+    Release();
+}
+
+void VertexBuffer::Release()
+{
+    Unlock();
+    
+    if (object_)
+    {
+        if (!graphics_)
+            return;
+        
+        for (unsigned i = 0; i < MAX_VERTEX_STREAMS; ++i)
+        {
+            if (graphics_->GetVertexBuffer(i) == this)
+                graphics_->SetVertexBuffer(0);
+        }
+        
+        ((ID3D11Buffer*)object_)->Release();
+        object_ = 0;
+    }
+}
+
+void VertexBuffer::SetShadowed(bool enable)
+{
+    // If no graphics subsystem, can not disable shadowing
+    if (!graphics_)
+        enable = true;
+    
+    if (enable != shadowed_)
+    {
+        if (enable && vertexSize_ && vertexCount_)
+            shadowData_ = new unsigned char[vertexCount_ * vertexSize_];
+        else
+            shadowData_.Reset();
+        
+        shadowed_ = enable;
+    }
+}
+
+bool VertexBuffer::SetSize(unsigned vertexCount, unsigned elementMask, bool dynamic)
+{
+    Unlock();
+    
+    dynamic_ = dynamic;
+    vertexCount_ = vertexCount;
+    elementMask_ = elementMask;
+    
+    UpdateOffsets();
+    
+    if (shadowed_ && vertexCount_ && vertexSize_)
+        shadowData_ = new unsigned char[vertexCount_ * vertexSize_];
+    else
+        shadowData_.Reset();
+    
+    return Create();
+}
+
+bool VertexBuffer::SetData(const void* data)
+{
+    if (!data)
+    {
+        LOGERROR("Null pointer for vertex buffer data");
+        return false;
+    }
+    
+    if (!vertexSize_)
+    {
+        LOGERROR("Vertex elements not defined, can not set vertex buffer data");
+        return false;
+    }
+    
+    if (shadowData_ && data != shadowData_.Get())
+        memcpy(shadowData_.Get(), data, vertexCount_ * vertexSize_);
+    
+    if (object_)
+    {
+        if (dynamic_)
+        {
+            void* hwData = MapBuffer(0, vertexCount_, true);
+            if (hwData)
+            {
+                memcpy(hwData, data, vertexCount_ * vertexSize_);
+                UnmapBuffer();
+            }
+            else
+                return false;
+        }
+        else
+        {
+            D3D11_BOX destBox;
+            destBox.left = 0;
+            destBox.right = vertexCount_ * vertexSize_;
+            destBox.top = 0;
+            destBox.bottom = 1;
+            destBox.front = 0;
+            destBox.back = 1;
+
+            graphics_->GetImpl()->GetDeviceContext()->UpdateSubresource((ID3D11Buffer*)object_, 0, &destBox, data, 0, 0);
+        }
+    }
+    
+    return true;
+}
+
+bool VertexBuffer::SetDataRange(const void* data, unsigned start, unsigned count, bool discard)
+{
+    if (start == 0 && count == vertexCount_)
+        return SetData(data);
+    
+    if (!data)
+    {
+        LOGERROR("Null pointer for vertex buffer data");
+        return false;
+    }
+    
+    if (!vertexSize_)
+    {
+        LOGERROR("Vertex elements not defined, can not set vertex buffer data");
+        return false;
+    }
+    
+    if (start + count > vertexCount_)
+    {
+        LOGERROR("Illegal range for setting new vertex buffer data");
+        return false;
+    }
+    
+    if (!count)
+        return true;
+    
+    if (shadowData_ && shadowData_.Get() + start * vertexSize_ != data)
+        memcpy(shadowData_.Get() + start * vertexSize_, data, count * vertexSize_);
+    
+    if (object_)
+    {
+        if (dynamic_)
+        {
+            void* hwData = MapBuffer(start, count, discard);
+            if (hwData)
+            {
+                memcpy(hwData, data, count * vertexSize_);
+                UnmapBuffer();
+            }
+            else
+                return false;
+        }
+        else
+        {
+            D3D11_BOX destBox;
+            destBox.left = start * vertexSize_;
+            destBox.right = destBox.left + count * vertexSize_;
+            destBox.top = 0;
+            destBox.bottom = 1;
+            destBox.front = 0;
+            destBox.back = 1;
+
+            graphics_->GetImpl()->GetDeviceContext()->UpdateSubresource((ID3D11Buffer*)object_, 0, &destBox, data, 0, 0);
+        }
+    }
+
+    return true;
+}
+
+void* VertexBuffer::Lock(unsigned start, unsigned count, bool discard)
+{
+    if (lockState_ != LOCK_NONE)
+    {
+        LOGERROR("Vertex buffer already locked");
+        return 0;
+    }
+    
+    if (!vertexSize_)
+    {
+        LOGERROR("Vertex elements not defined, can not lock vertex buffer");
+        return 0;
+    }
+    
+    if (start + count > vertexCount_)
+    {
+        LOGERROR("Illegal range for locking vertex buffer");
+        return 0;
+    }
+    
+    if (!count)
+        return 0;
+    
+    lockStart_ = start;
+    lockCount_ = count;
+    
+    // Because shadow data must be kept in sync, can only lock hardware buffer if not shadowed
+    if (object_ && !shadowData_ && dynamic_)
+        return MapBuffer(start, count, discard);
+    else if (shadowData_)
+    {
+        lockState_ = LOCK_SHADOW;
+        return shadowData_.Get() + start * vertexSize_;
+    }
+    else if (graphics_)
+    {
+        lockState_ = LOCK_SCRATCH;
+        lockScratchData_ = graphics_->ReserveScratchBuffer(count * vertexSize_);
+        return lockScratchData_;
+    }
+    else
+        return 0;
+}
+
+void VertexBuffer::Unlock()
+{
+    switch (lockState_)
+    {
+    case LOCK_HARDWARE:
+        UnmapBuffer();
+        break;
+        
+    case LOCK_SHADOW:
+        SetDataRange(shadowData_.Get() + lockStart_ * vertexSize_, lockStart_, lockCount_);
+        lockState_ = LOCK_NONE;
+        break;
+        
+    case LOCK_SCRATCH:
+        SetDataRange(lockScratchData_, lockStart_, lockCount_);
+        if (graphics_)
+            graphics_->FreeScratchBuffer(lockScratchData_);
+        lockScratchData_ = 0;
+        lockState_ = LOCK_NONE;
+        break;
+    }
+}
+
+void VertexBuffer::UpdateOffsets()
+{
+    unsigned elementOffset = 0;
+    for (unsigned i = 0; i < MAX_VERTEX_ELEMENTS; ++i)
+    {
+        if (elementMask_ & (1 << i))
+        {
+            elementOffset_[i] = elementOffset;
+            elementOffset += elementSize[i];
+        }
+        else
+            elementOffset_[i] = NO_ELEMENT;
+    }
+    vertexSize_ = elementOffset;
+}
+
+unsigned long long VertexBuffer::GetBufferHash(unsigned streamIndex, unsigned useMask)
+{
+    unsigned long long bufferHash = elementMask_;
+    unsigned long long maskHash;
+    if (useMask == MASK_DEFAULT)
+        maskHash = ((unsigned long long)elementMask_) * 0x100000000ULL;
+    else
+        maskHash = ((unsigned long long)useMask) * 0x100000000ULL;
+    
+    bufferHash |= maskHash;
+    bufferHash <<= streamIndex * MAX_VERTEX_ELEMENTS;
+    
+    return bufferHash;
+}
+
+unsigned VertexBuffer::GetVertexSize(unsigned elementMask)
+{
+    unsigned vertexSize = 0;
+    
+    for (unsigned i = 0; i < MAX_VERTEX_ELEMENTS; ++i)
+    {
+        if (elementMask & (1 << i))
+            vertexSize += elementSize[i];
+    }
+    
+    return vertexSize;
+}
+
+unsigned VertexBuffer::GetElementOffset(unsigned elementMask, VertexElement element)
+{
+    unsigned offset = 0;
+    
+    for (unsigned i = 0; i < MAX_VERTEX_ELEMENTS; ++i)
+    {
+        if (i == element)
+            break;
+        
+        if (elementMask & (1 << i))
+            offset += elementSize[i];
+    }
+    
+    return offset;
+}
+
+bool VertexBuffer::Create()
+{
+    Release();
+    
+    if (!vertexCount_ || !elementMask_)
+        return true;
+    
+    if (graphics_)
+    {
+        D3D11_BUFFER_DESC bufferDesc;
+        memset(&bufferDesc, 0, sizeof bufferDesc);
+        bufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
+        bufferDesc.CPUAccessFlags = dynamic_ ? D3D11_CPU_ACCESS_WRITE : 0;
+        bufferDesc.Usage = dynamic_ ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT;
+        bufferDesc.ByteWidth = (unsigned)(vertexCount_ * vertexSize_);
+
+        graphics_->GetImpl()->GetDevice()->CreateBuffer(&bufferDesc, 0, (ID3D11Buffer**)&object_);
+
+        if (!object_)
+        {
+            LOGERROR("Failed to create vertex buffer");
+            return false;
+        }
+    }
+    
+    return true;
+}
+
+bool VertexBuffer::UpdateToGPU()
+{
+    if (object_ && shadowData_)
+        return SetData(shadowData_.Get());
+    else
+        return false;
+}
+
+void* VertexBuffer::MapBuffer(unsigned start, unsigned count, bool discard)
+{
+    void* hwData = 0;
+    
+    if (object_)
+    {
+        D3D11_MAPPED_SUBRESOURCE mappedData;
+        mappedData.pData = 0;
+
+        graphics_->GetImpl()->GetDeviceContext()->Map((ID3D11Buffer*)object_, 0, discard ? D3D11_MAP_WRITE_DISCARD :
+            D3D11_MAP_WRITE, 0, &mappedData);
+        hwData = mappedData.pData;
+        if (!hwData)
+            LOGERROR("Failed to map vertex buffer");
+        else
+            lockState_ = LOCK_HARDWARE;
+    }
+    
+    return hwData;
+}
+
+void VertexBuffer::UnmapBuffer()
+{
+    if (object_ && lockState_ == LOCK_HARDWARE)
+    {
+        graphics_->GetImpl()->GetDeviceContext()->Unmap((ID3D11Buffer*)object_, 0);
+        lockState_ = LOCK_NONE;
+    }
+}
+
+}

+ 130 - 0
Source/Urho3D/Graphics/Direct3D11/D3D11VertexBuffer.h

@@ -0,0 +1,130 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../../Graphics/GPUObject.h"
+#include "../../Graphics/GraphicsDefs.h"
+#include "../../Container/ArrayPtr.h"
+
+namespace Urho3D
+{
+
+/// Hardware vertex buffer.
+class URHO3D_API VertexBuffer : public Object, public GPUObject
+{
+    OBJECT(VertexBuffer);
+    
+public:
+    /// Construct.
+    VertexBuffer(Context* context);
+    /// Destruct.
+    virtual ~VertexBuffer();
+    
+    /// Release buffer.
+    virtual void Release();
+    
+    /// Enable shadowing in CPU memory. Shadowing is forced on if the graphics subsystem does not exist.
+    void SetShadowed(bool enable);
+    /// Set size and vertex elements and dynamic mode. Previous data will be lost.
+    bool SetSize(unsigned vertexCount, unsigned elementMask, bool dynamic = false);
+    /// Set all data in the buffer.
+    bool SetData(const void* data);
+    /// Set a data range in the buffer. Optionally discard data outside the range.
+    bool SetDataRange(const void* data, unsigned start, unsigned count, bool discard = false);
+    /// Lock the buffer for write-only editing. Return data pointer if successful. Optionally discard data outside the range.
+    void* Lock(unsigned start, unsigned count, bool discard = false);
+    /// Unlock the buffer and apply changes to the GPU buffer.
+    void Unlock();
+    
+    /// Return whether CPU memory shadowing is enabled.
+    bool IsShadowed() const { return shadowed_; }
+    /// Return whether is dynamic.
+    bool IsDynamic() const { return dynamic_; }
+    /// Return whether is currently locked.
+    bool IsLocked() const { return lockState_ != LOCK_NONE; }
+    /// Return number of vertices.
+    unsigned GetVertexCount() const {return vertexCount_; }
+    /// Return vertex size.
+    unsigned GetVertexSize() const { return vertexSize_; }
+    /// Return bitmask of vertex elements.
+    unsigned GetElementMask() const { return elementMask_; }
+    /// Return offset of a specified element within a vertex.
+    unsigned GetElementOffset(VertexElement element) const { return elementOffset_[element]; }
+    /// Return buffer hash for building vertex declarations.
+    unsigned long long GetBufferHash(unsigned streamIndex, unsigned useMask);
+    /// Return CPU memory shadow data.
+    unsigned char* GetShadowData() const { return shadowData_.Get(); }
+    /// Return shared array pointer to the CPU memory shadow data.
+    SharedArrayPtr<unsigned char> GetShadowDataShared() const { return shadowData_; }
+
+    /// Return vertex size corresponding to a vertex element mask.
+    static unsigned GetVertexSize(unsigned elementMask);
+    /// Return element offset from an element mask.
+    static unsigned GetElementOffset(unsigned elementMask, VertexElement element);
+    
+    /// Vertex element sizes.
+    static const unsigned elementSize[];
+    /// Vertex element semantic names.
+    static const char* elementSemantics[];
+    /// Vertex element semantic indices.
+    static const unsigned elementSemanticIndices[];
+    /// Vertex element formats.
+    static const unsigned elementFormats[];
+
+private:
+    /// Update offsets of vertex elements.
+    void UpdateOffsets();
+    /// Create buffer.
+    bool Create();
+    /// Update the shadow data to the GPU buffer.
+    bool UpdateToGPU();
+    /// Map the GPU buffer into CPU memory.
+    void* MapBuffer(unsigned start, unsigned count, bool discard);
+    /// Unmap the GPU buffer.
+    void UnmapBuffer();
+    
+    /// Shadow data.
+    SharedArrayPtr<unsigned char> shadowData_;
+    /// Number of vertices.
+    unsigned vertexCount_;
+    /// Vertex size.
+    unsigned vertexSize_;
+    /// Vertex element bitmask.
+    unsigned elementMask_;
+   /// Vertex element offsets.
+    unsigned elementOffset_[MAX_VERTEX_ELEMENTS];
+    /// Buffer locking state.
+    LockState lockState_;
+    /// Lock start vertex.
+    unsigned lockStart_;
+    /// Lock number of vertices.
+    unsigned lockCount_;
+    /// Scratch buffer for fallback locking.
+    void* lockScratchData_;
+    /// Dynamic flag.
+    bool dynamic_;
+    /// Shadowed flag.
+    bool shadowed_;
+};
+
+}

+ 90 - 0
Source/Urho3D/Graphics/Direct3D11/D3D11VertexDeclaration.cpp

@@ -0,0 +1,90 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../../Graphics/Graphics.h"
+#include "../../Graphics/GraphicsImpl.h"
+#include "../../Graphics/ShaderVariation.h"
+#include "../../Graphics/VertexBuffer.h"
+#include "../../Graphics/VertexDeclaration.h"
+#include "../../IO/Log.h"
+
+#include "../../DebugNew.h"
+
+namespace Urho3D
+{
+
+VertexDeclaration::VertexDeclaration(Graphics* graphics, ShaderVariation* vertexShader, VertexBuffer** vertexBuffers, unsigned* elementMasks) :
+    inputLayout_(0)
+{
+    PODVector<D3D11_INPUT_ELEMENT_DESC> elementDescs;
+
+    unsigned vbElementMask = 0;
+
+    for (unsigned i = 0; i < MAX_VERTEX_STREAMS; ++i)
+    {
+        if (vertexBuffers[i] && elementMasks[i])
+        {
+            for (unsigned j = 0; j < MAX_VERTEX_ELEMENTS; ++j)
+            {
+                if (elementMasks[i] & (1 << j))
+                {
+                    D3D11_INPUT_ELEMENT_DESC newDesc;
+                    newDesc.SemanticName = VertexBuffer::elementSemantics[j];
+                    newDesc.SemanticIndex = VertexBuffer::elementSemanticIndices[j];
+                    newDesc.Format = (DXGI_FORMAT)VertexBuffer::elementFormats[j];
+                    newDesc.InputSlot = (unsigned)i;
+                    newDesc.AlignedByteOffset = vertexBuffers[i]->GetElementOffset((VertexElement)j);
+                    newDesc.InputSlotClass = (j >= ELEMENT_INSTANCEMATRIX1 && j <= ELEMENT_INSTANCEMATRIX3) ?
+                        D3D11_INPUT_PER_INSTANCE_DATA : D3D11_INPUT_PER_VERTEX_DATA;
+                    newDesc.InstanceDataStepRate = (j >= ELEMENT_INSTANCEMATRIX1 && j <= ELEMENT_INSTANCEMATRIX3) ? 1 : 0;
+                    elementDescs.Push(newDesc);
+                    vbElementMask |= 1 << j;
+                }
+            }
+        }
+    }
+
+    if (elementDescs.Empty())
+        return;
+
+    ID3D11InputLayout* d3dInputLayout = 0;
+    const PODVector<unsigned char>& byteCode = vertexShader->GetByteCode();
+
+    graphics->GetImpl()->GetDevice()->CreateInputLayout(&elementDescs[0], (unsigned)elementDescs.Size(), &byteCode[0],
+        byteCode.Size(), &d3dInputLayout);
+    if (d3dInputLayout)
+        inputLayout_ = d3dInputLayout;
+    else
+        LOGERRORF("Failed to create input layout for shader %s, missing element mask %d",
+            vertexShader->GetFullName().CString(), vertexShader->GetElementMask() & ~vbElementMask);
+}
+
+VertexDeclaration::~VertexDeclaration()
+{
+    if (inputLayout_)
+    {
+        ((ID3D11InputLayout*)inputLayout_)->Release();
+        inputLayout_ = 0;
+    }
+}
+
+}

+ 53 - 0
Source/Urho3D/Graphics/Direct3D11/D3D11VertexDeclaration.h

@@ -0,0 +1,53 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../../Graphics/GraphicsDefs.h"
+#include "../../Container/RefCounted.h"
+#include "../../Container/Vector.h"
+
+namespace Urho3D
+{
+
+class Graphics;
+class ShaderVariation;
+class VertexBuffer;
+
+/// Vertex declaration.
+class URHO3D_API VertexDeclaration : public RefCounted
+{
+public:
+    /// Construct with vertex buffers and element masks to base declaration on.
+    VertexDeclaration(Graphics* graphics, ShaderVariation* vertexShader, VertexBuffer** buffers, unsigned* elementMasks);
+    /// Destruct.
+    ~VertexDeclaration();
+    
+    /// Return input layout object corresponding to the declaration.
+    void* GetInputLayout() const { return inputLayout_; }
+    
+private:
+    /// Input layout object.
+    void* inputLayout_;
+};
+
+}

+ 121 - 247
Source/Urho3D/Graphics/Direct3D9/D3D9Graphics.cpp

@@ -43,6 +43,7 @@
 #include "../../Resource/ResourceCache.h"
 #include "../../Graphics/Shader.h"
 #include "../../Graphics/ShaderPrecache.h"
+#include "../../Graphics/ShaderProgram.h"
 #include "../../Graphics/ShaderVariation.h"
 #include "../../Graphics/Skybox.h"
 #include "../../Graphics/StaticModelGroup.h"
@@ -243,6 +244,8 @@ static HWND GetWindowHandle(SDL_Window* window)
 
 static unsigned readableDepthFormat = 0;
 
+const Vector2 Graphics::pixelUVOffset(0.5f, 0.5f);
+
 Graphics::Graphics(Context* context) :
     Object(context),
     impl_(new GraphicsImpl()),
@@ -263,20 +266,18 @@ Graphics::Graphics(Context* context) :
     queryIssued_(false),
     lightPrepassSupport_(false),
     deferredSupport_(false),
-    hardwareShadowSupport_(false),
-    streamOffsetSupport_(false),
+    instancingSupport_(false),
     sRGBSupport_(false),
     sRGBWriteSupport_(false),
-    hasSM3_(false),
-    forceSM2_(false),
     numPrimitives_(0),
     numBatches_(0),
     maxScratchBufferRequest_(0),
     defaultTextureFilterMode_(FILTER_TRILINEAR),
-    currentShaderParameters_(0),
+    shaderProgram_(0),
     shaderPath_("Shaders/HLSL/"),
     shaderExtension_(".hlsl"),
-    orientations_("LandscapeLeft LandscapeRight")
+    orientations_("LandscapeLeft LandscapeRight"),
+    apiName_("D3D9")
 {
     SetTextureUnitMappings();
     
@@ -293,7 +294,7 @@ Graphics::~Graphics()
         MutexLock lock(gpuObjectMutex_);
 
         // Release all GPU objects that still exist
-        for (Vector<GPUObject*>::Iterator i = gpuObjects_.Begin(); i != gpuObjects_.End(); ++i)
+        for (PODVector<GPUObject*>::Iterator i = gpuObjects_.Begin(); i != gpuObjects_.End(); ++i)
             (*i)->Release();
         gpuObjects_.Clear();
     }
@@ -774,9 +775,6 @@ bool Graphics::BeginFrame()
     for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
         SetTexture(i, 0);
     
-    // Cleanup stream frequencies from previous frame
-    ResetStreamFrequencies();
-    
     numPrimitives_ = 0;
     numBatches_ = 0;
     
@@ -942,7 +940,7 @@ void Graphics::SetVertexBuffer(VertexBuffer* buffer)
 bool Graphics::SetVertexBuffers(const PODVector<VertexBuffer*>& buffers, const PODVector<unsigned>&
     elementMasks, unsigned instanceOffset)
 {
-   if (buffers.Size() > MAX_VERTEX_STREAMS)
+    if (buffers.Size() > MAX_VERTEX_STREAMS)
     {
         LOGERROR("Too many vertex buffers");
         return false;
@@ -1016,73 +1014,7 @@ bool Graphics::SetVertexBuffers(const PODVector<VertexBuffer*>& buffers, const P
 bool Graphics::SetVertexBuffers(const Vector<SharedPtr<VertexBuffer> >& buffers, const PODVector<unsigned>&
     elementMasks, unsigned instanceOffset)
 {
-   if (buffers.Size() > MAX_VERTEX_STREAMS)
-    {
-        LOGERROR("Too many vertex buffers");
-        return false;
-    }
-    if (buffers.Size() != elementMasks.Size())
-    {
-        LOGERROR("Amount of element masks and vertex buffers does not match");
-        return false;
-    }
-    
-    unsigned long long hash = 0;
-    for (unsigned i = 0; i < buffers.Size(); ++i)
-    {
-        if (!buffers[i])
-            continue;
-        
-        hash |= buffers[i]->GetBufferHash(i, elementMasks[i]);
-    }
-    
-    if (hash)
-    {
-        if (!vertexDeclarations_.Contains(hash))
-        {
-            SharedPtr<VertexDeclaration> newDeclaration(new VertexDeclaration(this, buffers, elementMasks));
-            if (!newDeclaration->GetDeclaration())
-            {
-                LOGERROR("Failed to create vertex declaration");
-                return false;
-            }
-            
-            vertexDeclarations_[hash] = newDeclaration;
-        }
-        
-        VertexDeclaration* declaration = vertexDeclarations_[hash];
-        if (declaration != vertexDeclaration_)
-        {
-            impl_->device_->SetVertexDeclaration(declaration->GetDeclaration());
-            vertexDeclaration_ = declaration;
-        }
-    }
-    
-    for (unsigned i = 0; i < MAX_VERTEX_STREAMS; ++i)
-    {
-        VertexBuffer* buffer = 0;
-        unsigned offset = 0;
-        
-        if (i < buffers.Size())
-        {
-            buffer = buffers[i];
-            if (buffer && buffer->GetElementMask() & MASK_INSTANCEMATRIX1)
-                offset = instanceOffset * buffer->GetVertexSize();
-        }
-        
-        if (buffer != vertexBuffers_[i] || offset != streamOffsets_[i])
-        {
-            if (buffer)
-                impl_->device_->SetStreamSource(i, (IDirect3DVertexBuffer9*)buffer->GetGPUObject(), offset, buffer->GetVertexSize());
-            else
-                impl_->device_->SetStreamSource(i, 0, 0, 0);
-            
-            vertexBuffers_[i] = buffer;
-            streamOffsets_[i] = offset;
-        }
-    }
-    
-    return true;
+    return SetVertexBuffers(reinterpret_cast<const PODVector<VertexBuffer*>&>(buffers), elementMasks, instanceOffset);
 }
 
 void Graphics::SetIndexBuffer(IndexBuffer* buffer)
@@ -1170,29 +1102,17 @@ void Graphics::SetShaders(ShaderVariation* vs, ShaderVariation* ps)
     if (vertexShader_ && pixelShader_)
     {
         Pair<ShaderVariation*, ShaderVariation*> key = MakePair(vertexShader_, pixelShader_);
-        HashMap<Pair<ShaderVariation*, ShaderVariation*>, HashMap<StringHash, Pair<ShaderType, unsigned> > >::Iterator i =
-            shaderParameters_.Find(key);
-        if (i != shaderParameters_.End())
-            currentShaderParameters_ = &i->second_;
+        ShaderProgramMap::Iterator i = shaderPrograms_.Find(key);
+        if (i != shaderPrograms_.End())
+            shaderProgram_ = i->second_.Get();
         else
         {
-            HashMap<StringHash, Pair<ShaderType, unsigned> >& parameters = shaderParameters_[key];
-
-            const HashMap<StringHash, ShaderParameter>& vsParams = vertexShader_->GetParameters();
-            for (HashMap<StringHash, ShaderParameter>::ConstIterator i = vsParams.Begin(); i != vsParams.End(); ++i)
-                parameters[i->first_] = MakePair(i->second_.type_, i->second_.register_);
-
-            const HashMap<StringHash, ShaderParameter>& psParams = pixelShader_->GetParameters();
-            for (HashMap<StringHash, ShaderParameter>::ConstIterator i = psParams.Begin(); i != psParams.End(); ++i)
-                parameters[i->first_] = MakePair(i->second_.type_, i->second_.register_);
-
-            // Optimize shader parameter lookup by rehashing to next power of two
-            parameters.Rehash(NextPowerOfTwo(parameters.Size()));
-            currentShaderParameters_ = &parameters;
+            ShaderProgram* newProgram = shaderPrograms_[key] = new ShaderProgram(vertexShader_, pixelShader_);
+            shaderProgram_ = newProgram;
         }
     }
     else
-        currentShaderParameters_ = 0;
+        shaderProgram_ = 0;
 
     // Store shader combination if shader dumping in progress
     if (shaderPrecache_)
@@ -1201,161 +1121,148 @@ void Graphics::SetShaders(ShaderVariation* vs, ShaderVariation* ps)
 
 void Graphics::SetShaderParameter(StringHash param, const float* data, unsigned count)
 {
-    HashMap<StringHash, Pair<ShaderType, unsigned> >::Iterator i;
-    if (!currentShaderParameters_ || (i = currentShaderParameters_->Find(param)) == currentShaderParameters_->End())
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
         return;
     
-    if (i->second_.first_ == VS)
-        impl_->device_->SetVertexShaderConstantF(i->second_.second_, data, count / 4);
+    if (i->second_.type_ == VS)
+        impl_->device_->SetVertexShaderConstantF(i->second_.register_, data, count / 4);
     else
-        impl_->device_->SetPixelShaderConstantF(i->second_.second_, data, count / 4);
+        impl_->device_->SetPixelShaderConstantF(i->second_.register_, data, count / 4);
 }
 
 void Graphics::SetShaderParameter(StringHash param, float value)
 {
-    HashMap<StringHash, Pair<ShaderType, unsigned> >::Iterator i;
-    if (!currentShaderParameters_ || (i = currentShaderParameters_->Find(param)) == currentShaderParameters_->End())
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
         return;
     
-    float data[4];
-    
-    data[0] = value;
-    data[1] = 0.0f;
-    data[2] = 0.0f;
-    data[3] = 0.0f;
+    static Vector4 data(Vector4::ZERO);
+    data.x_ = value;
     
-    if (i->second_.first_ == VS)
-        impl_->device_->SetVertexShaderConstantF(i->second_.second_, &data[0], 1);
+    if (i->second_.type_ == VS)
+        impl_->device_->SetVertexShaderConstantF(i->second_.register_, &data.x_, 1);
     else
-        impl_->device_->SetPixelShaderConstantF(i->second_.second_, &data[0], 1);
+        impl_->device_->SetPixelShaderConstantF(i->second_.register_, &data.x_, 1);
 }
 
 void Graphics::SetShaderParameter(StringHash param, bool value)
 {
     /// \todo Bool constants possibly have no effect on Direct3D9
-    HashMap<StringHash, Pair<ShaderType, unsigned> >::Iterator i;
-    if (!currentShaderParameters_ || (i = currentShaderParameters_->Find(param)) == currentShaderParameters_->End())
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
         return;
 
     BOOL data = value;
 
-    if (i->second_.first_ == VS)
-        impl_->device_->SetVertexShaderConstantB(i->second_.second_, &data, 1);
+    if (i->second_.type_ == VS)
+        impl_->device_->SetVertexShaderConstantB(i->second_.register_, &data, 1);
     else
-        impl_->device_->SetPixelShaderConstantB(i->second_.second_, &data, 1);
+        impl_->device_->SetPixelShaderConstantB(i->second_.register_, &data, 1);
 }
 
 void Graphics::SetShaderParameter(StringHash param, const Color& color)
 {
-    HashMap<StringHash, Pair<ShaderType, unsigned> >::Iterator i;
-    if (!currentShaderParameters_ || (i = currentShaderParameters_->Find(param)) == currentShaderParameters_->End())
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
         return;
     
-    if (i->second_.first_ == VS)
-        impl_->device_->SetVertexShaderConstantF(i->second_.second_, color.Data(), 1);
+    if (i->second_.type_ == VS)
+        impl_->device_->SetVertexShaderConstantF(i->second_.register_, color.Data(), 1);
     else
-        impl_->device_->SetPixelShaderConstantF(i->second_.second_, color.Data(), 1);
+        impl_->device_->SetPixelShaderConstantF(i->second_.register_, color.Data(), 1);
 }
 
 void Graphics::SetShaderParameter(StringHash param, const Vector2& vector)
 {
-    HashMap<StringHash, Pair<ShaderType, unsigned> >::Iterator i;
-    if (!currentShaderParameters_ || (i = currentShaderParameters_->Find(param)) == currentShaderParameters_->End())
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
         return;
     
-    float data[4];
+    static Vector4 data(Vector4::ZERO);
+    data.x_ = vector.x_;
+    data.y_ = vector.y_;
     
-    data[0] = vector.x_;
-    data[1] = vector.y_;
-    data[2] = 0.0f;
-    data[3] = 0.0f;
-    
-    if (i->second_.first_ == VS)
-        impl_->device_->SetVertexShaderConstantF(i->second_.second_, &data[0], 1);
+    if (i->second_.type_ == VS)
+        impl_->device_->SetVertexShaderConstantF(i->second_.register_, &data.x_, 1);
     else
-        impl_->device_->SetPixelShaderConstantF(i->second_.second_, &data[0], 1);
+        impl_->device_->SetPixelShaderConstantF(i->second_.register_, &data.x_, 1);
 }
 
 void Graphics::SetShaderParameter(StringHash param, const Matrix3& matrix)
 {
-    HashMap<StringHash, Pair<ShaderType, unsigned> >::Iterator i;
-    if (!currentShaderParameters_ || (i = currentShaderParameters_->Find(param)) == currentShaderParameters_->End())
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
         return;
     
-    float data[12];
-    
-    data[0] = matrix.m00_;
-    data[1] = matrix.m01_;
-    data[2] = matrix.m02_;
-    data[3] = 0.0f;
-    data[4] = matrix.m10_;
-    data[5] = matrix.m11_;
-    data[6] = matrix.m12_;
-    data[7] = 0.0f;
-    data[8] = matrix.m20_;
-    data[9] = matrix.m21_;
-    data[10] = matrix.m22_;
-    data[11] = 0.0f;
-    
-    if (i->second_.first_ == VS)
-        impl_->device_->SetVertexShaderConstantF(i->second_.second_, &data[0], 3);
+    static Matrix3x4 data(Matrix3x4::ZERO);
+    data.m00_ = matrix.m00_;
+    data.m01_ = matrix.m01_;
+    data.m02_ = matrix.m02_;
+    data.m10_ = matrix.m10_;
+    data.m11_ = matrix.m11_;
+    data.m12_ = matrix.m12_;
+    data.m20_ = matrix.m20_;
+    data.m21_ = matrix.m21_;
+    data.m22_ = matrix.m22_;
+    
+    if (i->second_.type_ == VS)
+        impl_->device_->SetVertexShaderConstantF(i->second_.register_, &data.m00_, 3);
     else
-        impl_->device_->SetPixelShaderConstantF(i->second_.second_, &data[0], 3);
+        impl_->device_->SetPixelShaderConstantF(i->second_.register_, &data.m00_, 3);
 }
 
 void Graphics::SetShaderParameter(StringHash param, const Vector3& vector)
 {
-    HashMap<StringHash, Pair<ShaderType, unsigned> >::Iterator i;
-    if (!currentShaderParameters_ || (i = currentShaderParameters_->Find(param)) == currentShaderParameters_->End())
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
         return;
     
-    float data[4];
-    
-    data[0] = vector.x_;
-    data[1] = vector.y_;
-    data[2] = vector.z_;
-    data[3] = 0.0f;
-    
-    if (i->second_.first_ == VS)
-        impl_->device_->SetVertexShaderConstantF(i->second_.second_, &data[0], 1);
+    static Vector4 data(Vector4::ZERO);
+    data.x_ = vector.x_;
+    data.y_ = vector.y_;
+    data.z_ = vector.z_;
+
+    if (i->second_.type_ == VS)
+        impl_->device_->SetVertexShaderConstantF(i->second_.register_, &data.x_, 1);
     else
-        impl_->device_->SetPixelShaderConstantF(i->second_.second_, &data[0], 1);
+        impl_->device_->SetPixelShaderConstantF(i->second_.register_, &data.x_, 1);
 }
 
 void Graphics::SetShaderParameter(StringHash param, const Matrix4& matrix)
 {
-    HashMap<StringHash, Pair<ShaderType, unsigned> >::Iterator i;
-    if (!currentShaderParameters_ || (i = currentShaderParameters_->Find(param)) == currentShaderParameters_->End())
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
         return;
     
-    if (i->second_.first_ == VS)
-        impl_->device_->SetVertexShaderConstantF(i->second_.second_, matrix.Data(), 4);
+    if (i->second_.type_ == VS)
+        impl_->device_->SetVertexShaderConstantF(i->second_.register_, matrix.Data(), 4);
     else
-        impl_->device_->SetPixelShaderConstantF(i->second_.second_, matrix.Data(), 4);
+        impl_->device_->SetPixelShaderConstantF(i->second_.register_, matrix.Data(), 4);
 }
 
 void Graphics::SetShaderParameter(StringHash param, const Vector4& vector)
 {
-    HashMap<StringHash, Pair<ShaderType, unsigned> >::Iterator i;
-    if (!currentShaderParameters_ || (i = currentShaderParameters_->Find(param)) == currentShaderParameters_->End())
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
         return;
     
-    if (i->second_.first_ == VS)
-        impl_->device_->SetVertexShaderConstantF(i->second_.second_, vector.Data(), 1);
+    if (i->second_.type_ == VS)
+        impl_->device_->SetVertexShaderConstantF(i->second_.register_, vector.Data(), 1);
     else
-        impl_->device_->SetPixelShaderConstantF(i->second_.second_, vector.Data(), 1);
+        impl_->device_->SetPixelShaderConstantF(i->second_.register_, vector.Data(), 1);
 }
 
 void Graphics::SetShaderParameter(StringHash param, const Matrix3x4& matrix)
 {
-    HashMap<StringHash, Pair<ShaderType, unsigned> >::Iterator i;
-    if (!currentShaderParameters_ || (i = currentShaderParameters_->Find(param)) == currentShaderParameters_->End())
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
         return;
     
-    if (i->second_.first_ == VS)
-        impl_->device_->SetVertexShaderConstantF(i->second_.second_, matrix.Data(), 3);
+    if (i->second_.type_ == VS)
+        impl_->device_->SetVertexShaderConstantF(i->second_.register_, matrix.Data(), 3);
     else
-        impl_->device_->SetPixelShaderConstantF(i->second_.second_, matrix.Data(), 3);
+        impl_->device_->SetPixelShaderConstantF(i->second_.register_, matrix.Data(), 3);
 }
 
 void Graphics::SetShaderParameter(StringHash param, const Variant& value)
@@ -1417,7 +1324,7 @@ bool Graphics::NeedParameterUpdate(ShaderParameterGroup group, const void* sourc
 
 bool Graphics::HasShaderParameter(StringHash param)
 {
-    return currentShaderParameters_ && currentShaderParameters_->Find(param) != currentShaderParameters_->End();
+    return shaderProgram_ && shaderProgram_->parameters_.Find(param) != shaderProgram_->parameters_.End();
 }
 
 bool Graphics::HasTextureUnit(TextureUnit unit)
@@ -1439,7 +1346,7 @@ void Graphics::ClearParameterSources()
 void Graphics::ClearTransformSources()
 {
     shaderParameterSources_[SP_CAMERA] = (const void*)M_MAX_UNSIGNED;
-    shaderParameterSources_[SP_OBJECTTRANSFORM] = (const void*)M_MAX_UNSIGNED;
+    shaderParameterSources_[SP_OBJECT] = (const void*)M_MAX_UNSIGNED;
 }
 
 void Graphics::SetTexture(unsigned index, Texture* texture)
@@ -1773,15 +1680,6 @@ void Graphics::SetDepthWrite(bool enable)
     }
 }
 
-void Graphics::SetDrawAntialiased(bool enable)
-{
-    if (enable != drawAntialiased_)
-    {
-        impl_->device_->SetRenderState(D3DRS_MULTISAMPLEANTIALIAS, enable ? TRUE : FALSE);
-        drawAntialiased_ = enable;
-    }
-}
-
 void Graphics::SetFillMode(FillMode mode)
 {
     if (mode != fillMode_)
@@ -1948,35 +1846,6 @@ void Graphics::SetClipPlane(bool enable, const Plane& clipPlane, const Matrix3x4
     }
 }
 
-void Graphics::SetStreamFrequency(unsigned index, unsigned frequency)
-{
-    if (index < MAX_VERTEX_STREAMS && streamFrequencies_[index] != frequency)
-    {
-        impl_->device_->SetStreamSourceFreq(index, frequency);
-        streamFrequencies_[index] = frequency;
-    }
-}
-
-void Graphics::ResetStreamFrequencies()
-{
-    for (unsigned i = 0; i < MAX_VERTEX_STREAMS; ++i)
-    {
-        if (streamFrequencies_[i] != 1)
-        {
-            impl_->device_->SetStreamSourceFreq(i, 1);
-            streamFrequencies_[i] = 1;
-        }
-    }
-}
-
-void Graphics::SetForceSM2(bool enable)
-{
-    if (!IsInitialized())
-        forceSM2_ = enable;
-    else
-        LOGERROR("Force Shader Model 2 can not be changed after setting the initial screen mode");
-}
-
 void Graphics::BeginDumpShaders(const String& fileName)
 {
     shaderPrecache_ = new ShaderPrecache(context_, fileName);
@@ -2143,11 +2012,6 @@ RenderSurface* Graphics::GetRenderTarget(unsigned index) const
     return index < MAX_RENDERTARGETS ? renderTargets_[index] : 0;
 }
 
-unsigned Graphics::GetStreamFrequency(unsigned index) const
-{
-    return index < MAX_VERTEX_STREAMS ? streamFrequencies_[index] : 0;
-}
-
 IntVector2 Graphics::GetRenderTargetDimensions() const
 {
     int width, height;
@@ -2202,7 +2066,7 @@ void Graphics::WindowResized()
 
 void Graphics::WindowMoved()
 {
-    if (!impl_->device_ || !impl_->window_)
+    if (!impl_->device_ || !impl_->window_ || fullscreen_)
         return;
 
     int newX, newY;
@@ -2331,19 +2195,18 @@ void Graphics::CleanupScratchBuffers()
     maxScratchBufferRequest_ = 0;
 }
 
-void Graphics::CleanupShaderParameters(ShaderVariation* variation)
+void Graphics::CleanupShaderPrograms(ShaderVariation* variation)
 {
-    for (HashMap<Pair<ShaderVariation*, ShaderVariation*>, HashMap<StringHash, Pair<ShaderType, unsigned> > >::Iterator i =
-        shaderParameters_.Begin(); i != shaderParameters_.End();)
+    for (ShaderProgramMap::Iterator i = shaderPrograms_.Begin(); i != shaderPrograms_.End();)
     {
         if (i->first_.first_ == variation || i->first_.second_ == variation)
-            i = shaderParameters_.Erase(i);
+            i = shaderPrograms_.Erase(i);
         else
             ++i;
     }
 
     if (vertexShader_ == variation || pixelShader_ == variation)
-        currentShaderParameters_ = 0;
+        shaderProgram_ = 0;
 }
 
 unsigned Graphics::GetAlphaFormat()
@@ -2466,6 +2329,27 @@ unsigned Graphics::GetFormat(const String& formatName)
     return GetRGBFormat();
 }
 
+void Graphics::SetStreamFrequency(unsigned index, unsigned frequency)
+{
+    if (index < MAX_VERTEX_STREAMS && streamFrequencies_[index] != frequency)
+    {
+        impl_->device_->SetStreamSourceFreq(index, frequency);
+        streamFrequencies_[index] = frequency;
+    }
+}
+
+void Graphics::ResetStreamFrequencies()
+{
+    for (unsigned i = 0; i < MAX_VERTEX_STREAMS; ++i)
+    {
+        if (streamFrequencies_[i] != 1)
+        {
+            impl_->device_->SetStreamSourceFreq(i, 1);
+            streamFrequencies_[i] = 1;
+        }
+    }
+}
+
 bool Graphics::OpenWindow(int width, int height, bool resizable, bool borderless)
 {
     if (!externalWindow_)
@@ -2551,9 +2435,9 @@ bool Graphics::CreateInterface()
         return false;
     }
     
-    if (impl_->deviceCaps_.PixelShaderVersion < D3DPS_VERSION(2, 0))
+    if (impl_->deviceCaps_.PixelShaderVersion < D3DPS_VERSION(3, 0))
     {
-        LOGERROR("Shader model 2.0 display adapter is required");
+        LOGERROR("Shader model 3.0 display adapter is required");
         return false;
     }
     
@@ -2603,8 +2487,7 @@ void Graphics::CheckFeatureSupport()
     lightPrepassSupport_ = false;
     deferredSupport_ = false;
     hardwareShadowSupport_ = false;
-    streamOffsetSupport_ = false;
-    hasSM3_ = false;
+    instancingSupport_ = false;
     readableDepthFormat = 0;
     
     // Check hardware shadow map support: prefer NVIDIA style hardware depth compared shadow maps if available
@@ -2661,14 +2544,6 @@ void Graphics::CheckFeatureSupport()
         dummyColorFormat_ = D3DFMT_R5G6B5;
     else if (impl_->CheckFormatSupport(D3DFMT_A4R4G4B4, D3DUSAGE_RENDERTARGET, D3DRTYPE_TEXTURE))
         dummyColorFormat_ = D3DFMT_A4R4G4B4;
-        
-    // Check for Shader Model 3
-    if (!forceSM2_)
-    {
-        if (impl_->deviceCaps_.VertexShaderVersion >= D3DVS_VERSION(3, 0) && impl_->deviceCaps_.PixelShaderVersion >=
-            D3DPS_VERSION(3, 0))
-            hasSM3_ = true;
-    }
     
     // Check for light prepass and deferred rendering support
     if (impl_->deviceCaps_.NumSimultaneousRTs >= 2 && impl_->CheckFormatSupport(D3DFMT_R32F, D3DUSAGE_RENDERTARGET,
@@ -2681,7 +2556,7 @@ void Graphics::CheckFeatureSupport()
     
     // Check for stream offset (needed for instancing)
     if (impl_->deviceCaps_.DevCaps2 & D3DDEVCAPS2_STREAMOFFSET)
-        streamOffsetSupport_ = true;
+        instancingSupport_ = true;
     
     // Check for sRGB read & write
     /// \todo Should be checked for each texture format separately
@@ -2725,7 +2600,7 @@ void Graphics::OnDeviceLost()
     {
         MutexLock lock(gpuObjectMutex_);
 
-        for (Vector<GPUObject*>::Iterator i = gpuObjects_.Begin(); i != gpuObjects_.End(); ++i)
+        for (PODVector<GPUObject*>::Iterator i = gpuObjects_.Begin(); i != gpuObjects_.End(); ++i)
             (*i)->OnDeviceLost();
     }
     
@@ -2737,7 +2612,7 @@ void Graphics::OnDeviceReset()
     {
         MutexLock lock(gpuObjectMutex_);
 
-        for (Vector<GPUObject*>::Iterator i = gpuObjects_.Begin(); i != gpuObjects_.End(); ++i)
+        for (PODVector<GPUObject*>::Iterator i = gpuObjects_.Begin(); i != gpuObjects_.End(); ++i)
             (*i)->OnDeviceReset();
     }
     
@@ -2811,7 +2686,6 @@ void Graphics::ResetCachedState()
     stencilCompareMask_ = M_MAX_UNSIGNED;
     stencilWriteMask_ = M_MAX_UNSIGNED;
     useClipPlane_ = false;
-    drawAntialiased_ = true;
     impl_->blendEnable_ = FALSE;
     impl_->srcBlend_ = D3DBLEND_ONE;
     impl_->destBlend_ = D3DBLEND_ZERO;

+ 30 - 38
Source/Urho3D/Graphics/Direct3D9/D3D9Graphics.h

@@ -43,6 +43,7 @@ class GraphicsImpl;
 class RenderSurface;
 class Shader;
 class ShaderPrecache;
+class ShaderProgram;
 class ShaderVariation;
 class Texture;
 class Texture2D;
@@ -54,6 +55,8 @@ class VertexDeclaration;
 
 struct ShaderParameter;
 
+typedef HashMap<Pair<ShaderVariation*, ShaderVariation*>, SharedPtr<ShaderProgram> > ShaderProgramMap;
+
 /// CPU-side scratch buffer for vertex data updates.
 struct ScratchBuffer
 {
@@ -200,8 +203,6 @@ public:
     void SetDepthTest(CompareMode mode);
     /// Set depth write on/off.
     void SetDepthWrite(bool enable);
-    /// Set antialiased drawing mode on/off. Default is on if the backbuffer is multisampled. Has no effect when backbuffer is not multisampled.
-    void SetDrawAntialiased(bool enable);
     /// Set polygon fill mode.
     void SetFillMode(FillMode mode);
     /// Set scissor test.
@@ -212,12 +213,6 @@ public:
     void SetStencilTest(bool enable, CompareMode mode = CMP_ALWAYS, StencilOp pass = OP_KEEP, StencilOp fail = OP_KEEP, StencilOp zFail = OP_KEEP, unsigned stencilRef = 0, unsigned compareMask = M_MAX_UNSIGNED, unsigned writeMask = M_MAX_UNSIGNED);
     /// Set a custom clipping plane. The plane is specified in world space, but is dependent on the view and projection matrices.
     void SetClipPlane(bool enable, const Plane& clipPlane = Plane::UP, const Matrix3x4& view = Matrix3x4::IDENTITY, const Matrix4& projection = Matrix4::IDENTITY);
-    /// Set vertex buffer stream frequency.
-    void SetStreamFrequency(unsigned index, unsigned frequency);
-    /// Reset stream frequencies.
-    void ResetStreamFrequencies();
-    /// Set force Shader Model 2 flag. Only effective before setting the initial screen mode.
-    void SetForceSM2(bool enable);
     /// Begin dumping shader variation names to an XML file for precaching.
     void BeginDumpShaders(const String& fileName);
     /// End dumping shader variations names.
@@ -233,6 +228,8 @@ public:
     void* GetExternalWindow() const { return externalWindow_; }
     /// Return window title.
     const String& GetWindowTitle() const { return windowTitle_; }
+    /// Return graphics API name.
+    const String& GetApiName() const { return apiName_; }
     /// Return window position.
     IntVector2 GetWindowPosition() const;
     /// Return window width.
@@ -269,10 +266,8 @@ public:
     unsigned GetShadowMapFormat() const { return shadowMapFormat_; }
     /// Return 24-bit shadow map depth texture format, or 0 if not supported.
     unsigned GetHiresShadowMapFormat() const { return hiresShadowMapFormat_; }
-    /// Return whether Shader Model 3 is supported.
-    bool GetSM3Support() const { return hasSM3_; }
-    /// Return whether hardware instancing is supported.
-    bool GetInstancingSupport() const { return hasSM3_; }
+    /// Return whether hardware instancing is supported..
+    bool GetInstancingSupport() const { return instancingSupport_; }
     /// Return whether light pre-pass rendering is supported.
     bool GetLightPrepassSupport() const { return lightPrepassSupport_; }
     /// Return whether deferred rendering is supported.
@@ -281,8 +276,6 @@ public:
     bool GetHardwareShadowSupport() const { return hardwareShadowSupport_; }
     /// Return whether a readable hardware depth format is available.
     bool GetReadableDepthSupport() const { return GetReadableDepthFormat() != 0; }
-    /// Return whether stream offset is supported.
-    bool GetStreamOffsetSupport() const { return streamOffsetSupport_; }
     /// Return whether sRGB conversion on texture sampling is supported.
     bool GetSRGBSupport() const { return sRGBSupport_; }
     /// Return whether sRGB conversion on rendertarget writing is supported.
@@ -303,8 +296,6 @@ public:
     VertexBuffer* GetVertexBuffer(unsigned index) const;
     /// Return current index buffer.
     IndexBuffer* GetIndexBuffer() const { return indexBuffer_; }
-    /// Return current vertex declaration.
-    VertexDeclaration* GetVertexDeclaration() const { return vertexDeclaration_; }
     /// Return current vertex shader.
     ShaderVariation* GetVertexShader() const { return vertexShader_; }
     /// Return current pixel shader.
@@ -339,8 +330,6 @@ public:
     CompareMode GetDepthTest() const { return depthTestMode_; }
     /// Return whether depth write is enabled.
     bool GetDepthWrite() const { return depthWrite_; }
-    /// Return whether antialiased drawing mode is enabled.
-    bool GetDrawAntialiased() const { return drawAntialiased_; }
     /// Return polygon fill mode.
     FillMode GetFillMode() const { return fillMode_; }
     /// Return whether stencil test is enabled.
@@ -365,12 +354,8 @@ public:
     unsigned GetStencilWriteMask() const { return stencilWriteMask_; }
     /// Return whether a custom clipping plane is in use.
     bool GetUseClipPlane() const { return useClipPlane_; }
-    /// Return stream frequency by vertex buffer index.
-    unsigned GetStreamFrequency(unsigned index) const;
     /// Return rendertarget width and height.
     IntVector2 GetRenderTargetDimensions() const;
-    /// Return force Shader Model 2 flag.
-    bool GetForceSM2() const { return forceSM2_; }
     
     /// Window was resized through user interaction. Called by Input subsystem.
     void WindowResized();
@@ -390,8 +375,8 @@ public:
     void FreeScratchBuffer(void* buffer);
     /// Clean up too large scratch buffers.
     void CleanupScratchBuffers();
-    /// Clean up shader parameters when a shader variation is released or destroyed.
-    void CleanupShaderParameters(ShaderVariation* variation);
+    /// Clean up shader programs when a shader variation is released or destroyed.
+    void CleanupShaderPrograms(ShaderVariation* variation);
 
     /// Return the API-specific alpha texture format.
     static unsigned GetAlphaFormat();
@@ -427,8 +412,16 @@ public:
     static unsigned GetReadableDepthFormat();
     /// Return the API-specific texture format from a textual description, for example "rgb".
     static unsigned GetFormat(const String& formatName);
-    
+    /// Return UV offset required for pixel perfect rendering.
+    static const Vector2& GetPixelUVOffset() { return pixelUVOffset; }
+    /// Return maximum number of supported bones for skinning.
+    static unsigned GetMaxBones() { return 64; }
+
 private:
+    /// Set vertex buffer stream frequency.
+    void SetStreamFrequency(unsigned index, unsigned frequency);
+    /// Reset stream frequencies.
+    void ResetStreamFrequencies();
     /// Create the application window.
     bool OpenWindow(int width, int height, bool resizable, bool borderless);
     /// Create the application window icon.
@@ -494,16 +487,12 @@ private:
     bool deferredSupport_;
     /// Hardware shadow map depth compare support flag.
     bool hardwareShadowSupport_;
-    /// Stream offset support flag.
-    bool streamOffsetSupport_;
+    /// Instancing support flag.
+    bool instancingSupport_;
     /// sRGB conversion on read support flag.
     bool sRGBSupport_;
     /// sRGB conversion on write support flag.
     bool sRGBWriteSupport_;
-    /// Shader Model 3 flag.
-    bool hasSM3_;
-    /// Force Shader Model 2 flag.
-    bool forceSM2_;
     /// Number of primitives this frame.
     unsigned numPrimitives_;
     /// Number of batches this frame.
@@ -511,7 +500,7 @@ private:
     /// Largest scratch buffer request this frame.
     unsigned maxScratchBufferRequest_;
     /// GPU objects.
-    Vector<GPUObject*> gpuObjects_;
+    PODVector<GPUObject*> gpuObjects_;
     /// Scratch buffers.
     Vector<ScratchBuffer> scratchBuffers_;
     /// Vertex declarations.
@@ -586,14 +575,12 @@ private:
     bool stencilTest_;
     /// Custom clip plane enable flag.
     bool useClipPlane_;
-    /// Draw antialiased mode flag.
-    bool drawAntialiased_;
     /// Default texture filtering mode.
     TextureFilterMode defaultTextureFilterMode_;
-    /// Shader parameters for all vertex/pixel shader combinations.
-    HashMap<Pair<ShaderVariation*, ShaderVariation*>, HashMap<StringHash, Pair<ShaderType, unsigned> > > shaderParameters_;
-    /// Current active shader parameters.
-    HashMap<StringHash, Pair<ShaderType, unsigned> >* currentShaderParameters_;
+    /// Shader programs.
+    ShaderProgramMap shaderPrograms_;
+    /// Shader program in use.
+    ShaderProgram* shaderProgram_;
     /// Remembered shader parameter sources.
     const void* shaderParameterSources_[MAX_SHADER_PARAMETER_GROUPS];
     /// Base directory for shaders.
@@ -608,6 +595,11 @@ private:
     SharedPtr<ShaderPrecache> shaderPrecache_;
     /// Allowed screen orientations.
     String orientations_;
+    /// Graphics API name.
+    String apiName_;
+
+    /// Pixel perfect UV offset.
+    static const Vector2 pixelUVOffset;
 };
 
 /// Register Graphics library objects.

+ 54 - 0
Source/Urho3D/Graphics/Direct3D9/D3D9ShaderProgram.h

@@ -0,0 +1,54 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../../Container/HashMap.h"
+#include "../../Graphics/ShaderVariation.h"
+
+namespace Urho3D
+{
+
+/// Combined information for specific vertex and pixel shaders.
+class ShaderProgram : public RefCounted
+{
+public:
+    /// Construct.
+    ShaderProgram(ShaderVariation* vertexShader, ShaderVariation* pixelShader)
+    {
+        const HashMap<StringHash, ShaderParameter>& vsParams = vertexShader->GetParameters();
+        for (HashMap<StringHash, ShaderParameter>::ConstIterator i = vsParams.Begin(); i != vsParams.End(); ++i)
+        parameters_[i->first_] = i->second_;
+
+        const HashMap<StringHash, ShaderParameter>& psParams = pixelShader->GetParameters();
+        for (HashMap<StringHash, ShaderParameter>::ConstIterator i = psParams.Begin(); i != psParams.End(); ++i)
+            parameters_[i->first_] = i->second_;
+    
+        // Optimize shader parameter lookup by rehashing to next power of two
+        parameters_.Rehash(NextPowerOfTwo(parameters_.Size()));
+    }
+
+    /// Combined parameters from the vertex and pixel shader.
+    HashMap<StringHash, ShaderParameter> parameters_;
+};
+
+}

+ 8 - 22
Source/Urho3D/Graphics/Direct3D9/D3D9ShaderVariation.cpp

@@ -66,13 +66,9 @@ bool ShaderVariation::Create()
     }
 
     // Check for up-to-date bytecode on disk
-    bool useSM3 = graphics_->GetSM3Support();
     String path, name, extension;
     SplitPath(owner_->GetName(), path, name, extension);
-    if (useSM3)
-        extension = type_ == VS ? ".vs3" : ".ps3";
-    else
-        extension = type_ == VS ? ".vs2" : ".ps2";
+    extension = type_ == VS ? ".vs3" : ".ps3";
     
     String binaryShaderName = path + "Cache/" + name + "_" + StringHash(defines_).ToString() + extension;
     PODVector<unsigned> byteCode;
@@ -114,7 +110,7 @@ void ShaderVariation::Release()
         if (!graphics_)
             return;
         
-        graphics_->CleanupShaderParameters(this);
+        graphics_->CleanupShaderPrograms(this);
 
         if (type_ == VS)
         {
@@ -231,32 +227,22 @@ bool ShaderVariation::Compile(PODVector<unsigned>& byteCode)
     const char* entryPoint = 0;
     const char* profile = 0;
     unsigned flags = D3DCOMPILE_OPTIMIZATION_LEVEL3;
-    bool useSM3 = graphics_->GetSM3Support();
     
     if (type_ == VS)
     {
         entryPoint = "VS";
         defines.Push("COMPILEVS");
-        if (!useSM3)
-            profile = "vs_2_0";
-        else
-            profile = "vs_3_0";
+        profile = "vs_3_0";
     }
     else
     {
         entryPoint = "PS";
         defines.Push("COMPILEPS");
-        if (!useSM3)
-            profile = "ps_2_0";
-        else
-        {
-            profile = "ps_3_0";
-            flags |= D3DCOMPILE_PREFER_FLOW_CONTROL;
-        }
+        profile = "ps_3_0";
+        flags |= D3DCOMPILE_PREFER_FLOW_CONTROL;
     }
-    
-    if (useSM3)
-        defines.Push("SM3");
+
+    defines.Push("MAXBONES=" + String(Graphics::GetMaxBones()));
     
     // Collect defines into macros
     Vector<String> defineValues;
@@ -399,7 +385,7 @@ void ShaderVariation::SaveByteCode(const PODVector<unsigned>& byteCode, const St
     
     file->WriteFileID("USHD");
     file->WriteShort((unsigned short)type_);
-    file->WriteShort(graphics_->GetSM3Support() ? 3 : 2);
+    file->WriteShort(3);
 
     file->WriteUInt(parameters_.Size());
     for (HashMap<StringHash, ShaderParameter>::ConstIterator i = parameters_.Begin(); i != parameters_.End(); ++i)

+ 4 - 0
Source/Urho3D/Graphics/Direct3D9/D3D9Texture.h

@@ -50,6 +50,8 @@ public:
     void SetFilterMode(TextureFilterMode filter);
     /// Set addressing mode by texture coordinate.
     void SetAddressMode(TextureCoordinate coord, TextureAddressMode address);
+    /// Set shadow compare mode. No-op on D3D9.
+    void SetShadowCompare(bool enable) {}
     /// Set border color for border addressing mode.
     void SetBorderColor(const Color& color);
     /// Set sRGB sampling and writing mode.
@@ -75,6 +77,8 @@ public:
     TextureFilterMode GetFilterMode() const { return filterMode_; }
     /// Return addressing mode by texture coordinate.
     TextureAddressMode GetAddressMode(TextureCoordinate coord) const { return addressMode_[coord]; }
+    /// Return whether shadow compare is enabled. Always false on D3D9.
+    bool GetShadowCompare() const { return false; }
     /// Return border color.
     const Color& GetBorderColor() const { return borderColor_; }
     /// Return whether is using sRGB sampling and writing.

+ 25 - 19
Source/Urho3D/Graphics/Drawable.cpp

@@ -60,6 +60,9 @@ Drawable::Drawable(Context* context, unsigned char drawableFlags) :
     occluder_(false),
     occludee_(true),
     updateQueued_(false),
+    zoneDirty_(false),
+    octant_(0),
+    zone_(0),
     viewMask_(DEFAULT_VIEWMASK),
     lightMask_(DEFAULT_LIGHTMASK),
     shadowMask_(DEFAULT_SHADOWMASK),
@@ -75,10 +78,7 @@ Drawable::Drawable(Context* context, unsigned char drawableFlags) :
     lodBias_(1.0f),
     basePassFlags_(0),
     maxLights_(0),
-    octant_(0),
-    firstLight_(0),
-    zone_(0),
-    zoneDirty_(false)
+    firstLight_(0)
 {
 }
 
@@ -292,33 +292,30 @@ void Drawable::SetSortValue(float value)
     sortValue_ = value;
 }
 
-void Drawable::SetMinMaxZ(float minZ, float maxZ)
-{
-    minZ_ = minZ;
-    maxZ_ = maxZ;
-}
-
 void Drawable::MarkInView(const FrameInfo& frame)
 {
     if (frame.frameNumber_ != viewFrameNumber_)
     {
         viewFrameNumber_ = frame.frameNumber_;
-        viewCameras_.Clear();
+        viewCameras_.Resize(1);
+        viewCameras_[0] = frame.camera_;
     }
-    
-    viewCameras_.Insert(frame.camera_);
+    else
+        viewCameras_.Push(frame.camera_);
+
+    basePassFlags_ = 0;
+    firstLight_ = 0;
+    lights_.Clear();
+    vertexLights_.Clear();
 }
 
-void Drawable::MarkInView(unsigned frameNumber, Camera* camera)
+void Drawable::MarkInView(unsigned frameNumber)
 {
     if (frameNumber != viewFrameNumber_)
     {
         viewFrameNumber_ = frameNumber;
         viewCameras_.Clear();
     }
-    
-    if (camera)
-        viewCameras_.Insert(camera);
 }
 
 void Drawable::LimitLights()
@@ -337,13 +334,22 @@ void Drawable::LimitLights()
     lights_.Resize(maxLights_);
 }
 
-void Drawable::LimitVertexLights()
+void Drawable::LimitVertexLights(bool removeConvertedLights)
 {
+    if (removeConvertedLights)
+    {
+        for (unsigned i = vertexLights_.Size() - 1; i < vertexLights_.Size(); --i)
+        {
+            if (!vertexLights_[i]->GetPerVertex())
+                vertexLights_.Erase(i);
+        }
+    }
+
     if (vertexLights_.Size() <= MAX_VERTEX_LIGHTS)
         return;
 
     const BoundingBox& box = GetWorldBoundingBox();
-    for (unsigned i = vertexLights_.Size() - 1; i < vertexLights_.Size(); --i)
+    for (unsigned i = 0; i < vertexLights_.Size(); ++i)
         vertexLights_[i]->SetIntensitySortValue(box);
 
     Sort(vertexLights_.Begin(), vertexLights_.End(), CompareDrawables);

+ 21 - 27
Source/Urho3D/Graphics/Drawable.h

@@ -25,7 +25,6 @@
 #include "../Math/BoundingBox.h"
 #include "../Scene/Component.h"
 #include "../Graphics/GraphicsDefs.h"
-#include "../Container/HashSet.h"
 
 namespace Urho3D
 {
@@ -199,15 +198,15 @@ public:
     /// Set sorting value.
     void SetSortValue(float value);
     /// Set view-space depth bounds.
-    void SetMinMaxZ(float minZ, float maxZ);
-    /// Mark in view.
+    void SetMinMaxZ(float minZ, float maxZ) { minZ_ = minZ; maxZ_ = maxZ; }
+    /// Mark in view. Also clear the light list.
     void MarkInView(const FrameInfo& frame);
-    /// Mark in view of a specific camera. Specify null camera to update just the frame number.
-    void MarkInView(unsigned frameNumber, Camera* camera);
+    /// Mark in view without specifying a camera. Used for shadow casters.
+    void MarkInView(unsigned frameNumber);
     /// Sort and limit per-pixel lights to maximum allowed. Convert extra lights into vertex lights.
     void LimitLights();
     /// Sort and limit per-vertex lights to maximum allowed.
-    void LimitVertexLights();
+    void LimitVertexLights(bool removeConvertedLights);
     /// Set base pass flag for a batch.
     void SetBasePass(unsigned batchIndex) { basePassFlags_ |= (1 << batchIndex); }
     /// Return octree octant.
@@ -237,21 +236,16 @@ public:
     /// Return the maximum view-space depth.
     float GetMaxZ() const { return maxZ_; }
     
-    // Clear the frame's light list.
-    void ClearLights()
-    {
-        basePassFlags_ = 0;
-        firstLight_ = 0;
-        lights_.Clear();
-        vertexLights_.Clear();
-    }
-
     // Add a per-pixel light affecting the object this frame.
     void AddLight(Light* light)
     {
-        if (lights_.Empty())
+        if (!firstLight_)
             firstLight_ = light;
-        lights_.Push(light);
+
+        // Need to store into the light list only if the per-pixel lights are being limited.
+        // Otherwise recording the first light is enough
+        if (maxLights_)
+            lights_.Push(light);
     }
 
     // Add a per-vertex light affecting the object this frame.
@@ -294,6 +288,12 @@ protected:
     bool occludee_;
     /// Octree update queued flag.
     bool updateQueued_;
+    /// Zone inconclusive or dirtied flag.
+    bool zoneDirty_;
+    /// Octree octant.
+    Octant* octant_;
+    /// Current zone.
+    Zone* zone_;
     /// View mask.
     unsigned viewMask_;
     /// Light mask.
@@ -320,24 +320,18 @@ protected:
     float maxZ_;
     /// LOD bias.
     float lodBias_;
-    /// Base pass flags.
+    /// Base pass flags, bit per batch.
     unsigned basePassFlags_;
-    /// Maximum lights.
+    /// Maximum per-pixel lights.
     unsigned maxLights_;
-    /// Octree octant.
-    Octant* octant_;
+    /// List of cameras from which is seen on the current frame.
+    PODVector<Camera*> viewCameras_;
     /// First per-pixel light added this frame.
     Light* firstLight_;
     /// Per-pixel lights affecting this drawable.
     PODVector<Light*> lights_;
     /// Per-vertex lights affecting this drawable.
     PODVector<Light*> vertexLights_;
-    /// Current zone.
-    Zone* zone_;
-    /// Zone inconclusive or dirtied flag.
-    bool zoneDirty_;
-    /// Set of cameras from which is seen on the current frame.
-    HashSet<Camera*> viewCameras_;
 };
 
 inline bool CompareDrawables(Drawable* lhs, Drawable* rhs)

+ 3 - 1
Source/Urho3D/Graphics/GPUObject.h

@@ -22,8 +22,10 @@
 
 #pragma once
 
-#ifdef URHO3D_OPENGL
+#if defined(URHO3D_OPENGL)
 #include "OpenGL/OGLGPUObject.h"
+#elif defined(URHO3D_D3D11)
+#include "Direct3D11/D3D11GPUObject.h"
 #else
 #include "Direct3D9/D3D9GPUObject.h"
 #endif

+ 3 - 1
Source/Urho3D/Graphics/Graphics.h

@@ -22,8 +22,10 @@
 
 #pragma once
 
-#ifdef URHO3D_OPENGL
+#if defined(URHO3D_OPENGL)
 #include "OpenGL/OGLGraphics.h"
+#elif defined(URHO3D_D3D11)
+#include "Direct3D11/D3D11Graphics.h"
 #else
 #include "Direct3D9/D3D9Graphics.h"
 #endif

+ 1 - 13
Source/Urho3D/Graphics/GraphicsDefs.cpp

@@ -34,6 +34,7 @@ extern URHO3D_API const StringHash VSP_AMBIENTENDCOLOR("AmbientEndColor");
 extern URHO3D_API const StringHash VSP_BILLBOARDROT("BillboardRot");
 extern URHO3D_API const StringHash VSP_CAMERAPOS("CameraPos");
 extern URHO3D_API const StringHash VSP_CAMERAROT("CameraRot");
+extern URHO3D_API const StringHash VSP_CLIPPLANE("ClipPlane");
 extern URHO3D_API const StringHash VSP_NEARCLIP("NearClip");
 extern URHO3D_API const StringHash VSP_FARCLIP("FarClip");
 extern URHO3D_API const StringHash VSP_DEPTHMODE("DepthMode");
@@ -75,19 +76,6 @@ extern URHO3D_API const StringHash PSP_SHADOWMAPINVSIZE("ShadowMapInvSize");
 extern URHO3D_API const StringHash PSP_SHADOWSPLITS("ShadowSplits");
 extern URHO3D_API const StringHash PSP_LIGHTMATRICES("LightMatricesPS");
 
-extern URHO3D_API const StringHash PASS_BASE("base");
-extern URHO3D_API const StringHash PASS_LITBASE("litbase");
-extern URHO3D_API const StringHash PASS_LIGHT("light");
-extern URHO3D_API const StringHash PASS_ALPHA("alpha");
-extern URHO3D_API const StringHash PASS_LITALPHA("litalpha");
-extern URHO3D_API const StringHash PASS_SHADOW("shadow");
-extern URHO3D_API const StringHash PASS_DEFERRED("deferred");
-extern URHO3D_API const StringHash PASS_PREPASS("prepass");
-extern URHO3D_API const StringHash PASS_MATERIAL("material");
-extern URHO3D_API const StringHash PASS_POSTOPAQUE("postopaque");
-extern URHO3D_API const StringHash PASS_REFRACT("refract");
-extern URHO3D_API const StringHash PASS_POSTALPHA("postalpha");
-
 extern URHO3D_API const Vector3 DOT_SCALE(1 / 3.0f, 1 / 3.0f, 1 / 3.0f);
 
 }

+ 22 - 28
Source/Urho3D/Graphics/GraphicsDefs.h

@@ -218,17 +218,16 @@ enum ShaderType
     PS,
 };
 
-/// Shader parameter groups for determining need to update.
+/// Shader parameter groups for determining need to update. On APIs that support constant buffers, these correspond to different constant buffers.
 enum ShaderParameterGroup
 {
     SP_FRAME = 0,
     SP_CAMERA,
-    SP_VIEWPORT,
     SP_ZONE,
     SP_LIGHT,
-    SP_VERTEXLIGHTS,
     SP_MATERIAL,
-    SP_OBJECTTRANSFORM,
+    SP_OBJECT,
+    SP_CUSTOM,
     MAX_SHADER_PARAMETER_GROUPS
 };
 
@@ -242,18 +241,27 @@ enum TextureUnit
     TU_SPECULAR = 2,
     TU_EMISSIVE = 3,
     TU_ENVIRONMENT = 4,
-    MAX_MATERIAL_TEXTURE_UNITS = 5,
+#ifdef DESKTOP_GRAPHICS
+    TU_VOLUMEMAP = 5,
+    TU_CUSTOM1 = 6,
+    TU_CUSTOM2 = 7,
+    TU_LIGHTRAMP = 8,
+    TU_LIGHTSHAPE = 9,
+    TU_SHADOWMAP = 10,
+    TU_FACESELECT = 11,
+    TU_INDIRECTION = 12,
+    TU_DEPTHBUFFER = 13,
+    TU_LIGHTBUFFER = 14,
+    TU_ZONE = 15,
+    MAX_MATERIAL_TEXTURE_UNITS = 8,
+    MAX_TEXTURE_UNITS = 16
+#else
     TU_LIGHTRAMP = 5,
     TU_LIGHTSHAPE = 6,
     TU_SHADOWMAP = 7,
-    TU_FACESELECT = 8,
-    TU_INDIRECTION = 9,
-    TU_DEPTHBUFFER = 10,
-    TU_LIGHTBUFFER = 11,
-    TU_VOLUMEMAP = 12,
-    TU_ZONE = 13,
-    MAX_NAMED_TEXTURE_UNITS = 14,
-    MAX_TEXTURE_UNITS = 16
+    MAX_MATERIAL_TEXTURE_UNITS = 5,
+    MAX_TEXTURE_UNITS = 8
+#endif
 };
 
 /// Billboard camera facing modes.
@@ -272,6 +280,7 @@ extern URHO3D_API const StringHash VSP_AMBIENTENDCOLOR;
 extern URHO3D_API const StringHash VSP_BILLBOARDROT;
 extern URHO3D_API const StringHash VSP_CAMERAPOS;
 extern URHO3D_API const StringHash VSP_CAMERAROT;
+extern URHO3D_API const StringHash VSP_CLIPPLANE;
 extern URHO3D_API const StringHash VSP_NEARCLIP;
 extern URHO3D_API const StringHash VSP_FARCLIP;
 extern URHO3D_API const StringHash VSP_DEPTHMODE;
@@ -313,20 +322,6 @@ extern URHO3D_API const StringHash PSP_SHADOWMAPINVSIZE;
 extern URHO3D_API const StringHash PSP_SHADOWSPLITS;
 extern URHO3D_API const StringHash PSP_LIGHTMATRICES;
 
-// Inbuilt pass types
-extern URHO3D_API const StringHash PASS_BASE;
-extern URHO3D_API const StringHash PASS_LITBASE;
-extern URHO3D_API const StringHash PASS_LIGHT;
-extern URHO3D_API const StringHash PASS_ALPHA;
-extern URHO3D_API const StringHash PASS_LITALPHA;
-extern URHO3D_API const StringHash PASS_SHADOW;
-extern URHO3D_API const StringHash PASS_DEFERRED;
-extern URHO3D_API const StringHash PASS_PREPASS;
-extern URHO3D_API const StringHash PASS_MATERIAL;
-extern URHO3D_API const StringHash PASS_POSTOPAQUE;
-extern URHO3D_API const StringHash PASS_REFRACT;
-extern URHO3D_API const StringHash PASS_POSTALPHA;
-
 // Scale calculation from bounding box diagonal.
 extern URHO3D_API const Vector3 DOT_SCALE;
 
@@ -363,7 +358,6 @@ static const unsigned NO_ELEMENT = 0xffffffff;
 
 static const int MAX_RENDERTARGETS = 4;
 static const int MAX_VERTEX_STREAMS = 4;
-static const int MAX_SKIN_MATRICES = 64;
 static const int MAX_CONSTANT_REGISTERS = 256;
 
 static const int BITS_PER_COMPONENT = 8;

+ 3 - 1
Source/Urho3D/Graphics/GraphicsImpl.h

@@ -22,8 +22,10 @@
 
 #pragma once
 
-#ifdef URHO3D_OPENGL
+#if defined(URHO3D_OPENGL)
 #include "OpenGL/OGLGraphicsImpl.h"
+#elif defined(URHO3D_D3D11)
+#include "Direct3D11/D3D11GraphicsImpl.h"
 #else
 #include "Direct3D9/D3D9GraphicsImpl.h"
 #endif

+ 3 - 1
Source/Urho3D/Graphics/IndexBuffer.h

@@ -22,8 +22,10 @@
 
 #pragma once
 
-#ifdef URHO3D_OPENGL
+#if defined(URHO3D_OPENGL)
 #include "OpenGL/OGLIndexBuffer.h"
+#elif defined(URHO3D_D3D11)
+#include "Direct3D11/D3D11IndexBuffer.h"
 #else
 #include "Direct3D9/D3D9IndexBuffer.h"
 #endif

+ 2 - 11
Source/Urho3D/Graphics/Light.cpp

@@ -396,16 +396,7 @@ int Light::GetNumShadowSplits() const
         }
     }
 
-    ret = Min(ret, MAX_CASCADE_SPLITS);
-    // Shader Model 2 can only support 3 splits max. due to pixel shader instruction count limits
-    if (ret == 4)
-    {
-        Graphics* graphics = GetSubsystem<Graphics>();
-        if (graphics && !graphics->GetSM3Support())
-            --ret;
-    }
-
-    return ret;
+    return Min(ret, MAX_CASCADE_SPLITS);
 }
 
 const Matrix3x4& Light::GetVolumeTransform(Camera* camera)
@@ -556,7 +547,7 @@ void Light::SetIntensitySortValue(const BoundingBox& box)
             float distance = lightRay.HitDistance(box);
             float normDistance = distance / range_;
             float att = Max(1.0f - normDistance * normDistance, M_EPSILON);
-            sortValue_ = 1.0f / (Max(color_.SumRGB(), 0.0f) * att + M_EPSILON);
+            sortValue_ = 1.0f / GetIntensityDivisor(att);
         }
         break;
     }

+ 42 - 24
Source/Urho3D/Graphics/Material.cpp

@@ -35,6 +35,7 @@
 #include "../Core/StringUtils.h"
 #include "../Graphics/Technique.h"
 #include "../Graphics/Texture2D.h"
+#include "../Graphics/Texture3D.h"
 #include "../Graphics/TextureCube.h"
 #include "../Scene/ValueAnimation.h"
 #include "../Resource/XMLFile.h"
@@ -53,6 +54,10 @@ static const char* textureUnitNames[] =
     "specular",
     "emissive",
     "environment",
+#ifdef DESKTOP_GRAPHICS
+    "volume",
+    "custom1",
+    "custom2",
     "lightramp",
     "lightshape",
     "shadowmap",
@@ -60,9 +65,14 @@ static const char* textureUnitNames[] =
     "indirection",
     "depth",
     "light",
-    "volume",
     "zone",
     0
+#else
+    "lightramp",
+    "lightshape",
+    "shadowmap",
+    0
+#endif
 };
 
 static const char* cullModeNames[] =
@@ -153,7 +163,6 @@ void ShaderParameterAnimationInfo::ApplyValue(const Variant& newValue)
 Material::Material(Context* context) :
     Resource(context),
     auxViewFrameNumber_(0),
-    numUsedTextureUnits_(0),
     shaderParameterHash_(0),
     occlusion_(true),
     specular_(false),
@@ -202,7 +211,17 @@ bool Material::BeginLoad(Deserializer& source)
                 // Detect cube maps by file extension: they are defined by an XML file
                 /// \todo Differentiate with 3D textures by actually reading the XML content
                 if (GetExtension(name) == ".xml")
-                    cache->BackgroundLoadResource<TextureCube>(name, true, this);
+                {
+                    #ifdef DESKTOP_GRAPHICS
+                    TextureUnit unit = TU_DIFFUSE;
+                    if (textureElem.HasAttribute("unit"))
+                        unit = ParseTextureUnitName(textureElem.GetAttribute("unit"));
+                    if (unit == TU_VOLUMEMAP)
+                        cache->BackgroundLoadResource<Texture3D>(name, true, this);
+                    else
+                    #endif
+                        cache->BackgroundLoadResource<TextureCube>(name, true, this);
+                }
                 else
                     cache->BackgroundLoadResource<Texture2D>(name, true, this);
                 textureElem = textureElem.GetNext("texture");
@@ -294,7 +313,14 @@ bool Material::Load(const XMLElement& source)
             // Detect cube maps by file extension: they are defined by an XML file
             /// \todo Differentiate with 3D textures by actually reading the XML content
             if (GetExtension(name) == ".xml")
-                SetTexture(unit, cache->GetResource<TextureCube>(name));
+            {
+                #ifdef DESKTOP_GRAPHICS
+                if (unit == TU_VOLUMEMAP)
+                    SetTexture(unit, cache->GetResource<Texture3D>(name));
+                else
+                #endif
+                    SetTexture(unit, cache->GetResource<TextureCube>(name));
+            }
             else
                 SetTexture(unit, cache->GetResource<Texture2D>(name));
         }
@@ -385,7 +411,7 @@ bool Material::Save(XMLElement& dest) const
         if (texture)
         {
             XMLElement textureElem = dest.CreateChild("texture");
-            textureElem.SetString("unit", j < MAX_NAMED_TEXTURE_UNITS ? textureUnitNames[j] : String(j).CString());
+            textureElem.SetString("unit", textureUnitNames[j]);
             textureElem.SetString("name", texture->GetName());
         }
     }
@@ -526,16 +552,10 @@ void Material::SetTexture(TextureUnit unit, Texture* texture)
 {
     if (unit < MAX_TEXTURE_UNITS)
     {
-        textures_[unit] = texture;
-
-        // Update the number of used texture units
-        if (texture && (unsigned)unit >= numUsedTextureUnits_)
-            numUsedTextureUnits_ = unit + 1;
-        else if (!texture && unit == numUsedTextureUnits_ - 1)
-        {
-            while (numUsedTextureUnits_ && !textures_[numUsedTextureUnits_ - 1])
-                --numUsedTextureUnits_;
-        }
+        if (texture)
+            textures_[unit] = texture;
+        else
+            textures_.Erase(unit);
     }
 }
 
@@ -626,13 +646,11 @@ SharedPtr<Material> Material::Clone(const String& cloneName) const
     ret->SetName(cloneName);
     ret->techniques_ = techniques_;
     ret->shaderParameters_ = shaderParameters_;
-    for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
-        ret->textures_[i] = textures_[i];
+    ret->textures_ = textures_;
     ret->occlusion_ = occlusion_;
     ret->specular_ = specular_;
     ret->cullMode_ = cullMode_;
     ret->shadowCullMode_ = shadowCullMode_;
-    ret->numUsedTextureUnits_ = numUsedTextureUnits_;
     ret->RefreshMemoryUse();
 
     return ret;
@@ -658,15 +676,16 @@ Technique* Material::GetTechnique(unsigned index) const
     return index < techniques_.Size() ? techniques_[index].technique_ : (Technique*)0;
 }
 
-Pass* Material::GetPass(unsigned index, StringHash passType) const
+Pass* Material::GetPass(unsigned index, const String& passName) const
 {
     Technique* tech = index < techniques_.Size() ? techniques_[index].technique_ : (Technique*)0;
-    return tech ? tech->GetPass(passType) : 0;
+    return tech ? tech->GetPass(passName) : 0;
 }
 
 Texture* Material::GetTexture(TextureUnit unit) const
 {
-    return unit < MAX_TEXTURE_UNITS ? textures_[unit] : (Texture*)0;
+    HashMap<TextureUnit, SharedPtr<Texture> >::ConstIterator i = textures_.Find(unit);
+    return i != textures_.End() ? i->second_.Get() : (Texture*)0;
 }
 
 const Variant& Material::GetShaderParameter(const String& name) const
@@ -721,7 +740,7 @@ void Material::CheckOcclusion()
         Technique* tech = techniques_[i].technique_;
         if (tech)
         {
-            Pass* pass = tech->GetPass(PASS_BASE);
+            Pass* pass = tech->GetPass("base");
             if (pass && pass->GetDepthWrite() && !pass->GetAlphaMask())
                 occlusion_ = true;
         }
@@ -737,8 +756,7 @@ void Material::ResetToDefaults()
     SetNumTechniques(1);
     SetTechnique(0, GetSubsystem<ResourceCache>()->GetResource<Technique>("Techniques/NoTexture.xml"));
 
-    for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
-        textures_[i] = 0;
+    textures_.Clear();
 
     batchedParameterUpdate_ = true;
     shaderParameters_.Clear();

+ 10 - 8
Source/Urho3D/Graphics/Material.h

@@ -90,6 +90,12 @@ private:
     String name_;
 };
 
+/// TextureUnit hash function.
+template<> inline unsigned MakeHash(const TextureUnit& value)
+{
+    return (unsigned)value;
+}
+
 /// Describes how to render 3D geometries.
 class URHO3D_API Material : public Resource
 {
@@ -159,12 +165,12 @@ public:
     const TechniqueEntry& GetTechniqueEntry(unsigned index) const;
     /// Return technique by index.
     Technique* GetTechnique(unsigned index) const;
-    /// Return pass by technique index and pass type.
-    Pass* GetPass(unsigned index, StringHash passType) const;
+    /// Return pass by technique index and pass name.
+    Pass* GetPass(unsigned index, const String& passName) const;
     /// Return texture by unit.
     Texture* GetTexture(TextureUnit unit) const;
    /// Return all textures.
-    const SharedPtr<Texture>* GetTextures() const { return &textures_[0]; }
+    const HashMap<TextureUnit, SharedPtr<Texture> >& GetTextures() const { return textures_; }
     /// Return shader parameter.
     const Variant& GetShaderParameter(const String& name) const;
     /// Return shader parameter animation.
@@ -189,8 +195,6 @@ public:
     bool GetSpecular() const { return specular_; }
     /// Return the scene associated with the material for shader parameter animation updates.
     Scene* GetScene() const;
-    /// Return the last non-null texture unit + 1. Used as an optimization when applying the material to render state.
-    unsigned GetNumUsedTextureUnits() const { return numUsedTextureUnits_; }
     /// Return shader parameter hash value. Used as an optimization to avoid setting shader parameters unnecessarily.
     unsigned GetShaderParameterHash() const { return shaderParameterHash_; }
 
@@ -218,7 +222,7 @@ private:
     /// Techniques.
     Vector<TechniqueEntry> techniques_;
     /// Textures.
-    SharedPtr<Texture> textures_[MAX_TEXTURE_UNITS];
+    HashMap<TextureUnit, SharedPtr<Texture> > textures_;
     /// %Shader parameters.
     HashMap<StringHash, MaterialShaderParameter> shaderParameters_;
     /// %Shader parameters animation infos.
@@ -231,8 +235,6 @@ private:
     BiasParameters depthBias_;
     /// Last auxiliary view rendered frame number.
     unsigned auxViewFrameNumber_;
-    /// Number of maximum non-null texture unit + 1.
-    unsigned numUsedTextureUnits_;
     /// Shader parameter hash value.
     unsigned shaderParameterHash_;
     /// Render occlusion flag.

+ 139 - 0
Source/Urho3D/Graphics/OpenGL/OGLConstantBuffer.cpp

@@ -0,0 +1,139 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../../Graphics/Graphics.h"
+#include "../../Graphics/GraphicsImpl.h"
+#include "../../IO/Log.h"
+#include "../../Graphics/ConstantBuffer.h"
+
+#include "../../DebugNew.h"
+
+namespace Urho3D
+{
+
+
+ConstantBuffer::ConstantBuffer(Context* context) :
+    Object(context),
+    GPUObject(GetSubsystem<Graphics>())
+{
+}
+
+ConstantBuffer::~ConstantBuffer()
+{
+    Release();
+}
+
+void ConstantBuffer::Release()
+{
+    if (object_)
+    {
+        if (!graphics_)
+            return;
+
+        #ifndef GL_ES_VERSION_2_0
+        graphics_->SetUBO(0);
+        glDeleteBuffers(1, &object_);
+        #endif
+        object_ = 0;
+    }
+
+    shadowData_.Reset();
+    size_ = 0;
+}
+
+void ConstantBuffer::OnDeviceReset()
+{
+    if (size_)
+        SetSize(size_); // Recreate
+}
+
+bool ConstantBuffer::SetSize(unsigned size)
+{
+    if (!size)
+    {
+        LOGERROR("Can not create zero-sized constant buffer");
+        return false;
+    }
+
+    // Round up to next 16 bytes
+    size += 15;
+    size &= 0xfffffff0;
+
+    size_ = size;
+    dirty_ = false;
+    shadowData_ = new unsigned char[size_];
+    memset(shadowData_.Get(), 0, size_);
+
+    if (graphics_)
+    {
+        #ifndef GL_ES_VERSION_2_0
+        if (!object_)
+            glGenBuffers(1, &object_);
+        graphics_->SetUBO(object_);
+        glBufferData(GL_UNIFORM_BUFFER, size_, shadowData_.Get(), GL_DYNAMIC_DRAW);
+        #endif
+    }
+
+    return true;
+}
+
+void ConstantBuffer::SetParameter(unsigned offset, unsigned size, const void* data)
+{
+    if (offset + size > size_)
+        return; // Would overflow the buffer
+
+    memcpy(&shadowData_[offset], data, size);
+    dirty_ = true;
+}
+
+void ConstantBuffer::SetVector3ArrayParameter(unsigned offset, unsigned rows, const void* data)
+{
+    if (offset + rows * 4 * sizeof(float) > size_)
+        return; // Would overflow the buffer
+
+    float* dest = (float*)&shadowData_[offset];
+    const float* src = (const float*)data;
+
+    while (rows--)
+    {
+        *dest++ = *src++;
+        *dest++ = *src++;
+        *dest++ = *src++;
+        ++dest; // Skip over the w coordinate
+    }
+
+    dirty_ = true;
+}
+
+void ConstantBuffer::Apply()
+{
+    if (dirty_ && object_)
+    {
+        #ifndef GL_ES_VERSION_2_0
+        graphics_->SetUBO(object_);
+        glBufferData(GL_UNIFORM_BUFFER, size_, shadowData_.Get(), GL_DYNAMIC_DRAW);
+        #endif
+        dirty_ = false;
+    }
+}
+
+}

+ 75 - 0
Source/Urho3D/Graphics/OpenGL/OGLConstantBuffer.h

@@ -0,0 +1,75 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../../Graphics/GPUObject.h"
+#include "../../Graphics/GraphicsDefs.h"
+#include "../../Container/ArrayPtr.h"
+#include "../../Core/Object.h"
+
+namespace Urho3D
+{
+
+/// Hardware constant buffer.
+class URHO3D_API ConstantBuffer : public Object, public GPUObject
+{
+    OBJECT(ConstantBuffer);
+    
+public:
+    /// Construct.
+    ConstantBuffer(Context* context);
+    /// Destruct.
+    virtual ~ConstantBuffer();
+    
+    /// Recreate the GPU resource and restore data if applicable.
+    virtual void OnDeviceReset();
+    /// Release the buffer.
+    virtual void Release();
+    
+    /// Set size and create GPU-side buffer. Return true on success.
+    bool SetSize(unsigned size);
+    /// Set a generic parameter and mark buffer dirty.
+    void SetParameter(unsigned offset, unsigned size, const void* data);
+    /// Set a Vector3 array parameter and mark buffer dirty.
+    void SetVector3ArrayParameter(unsigned offset, unsigned rows, const void* data);
+    /// Apply to GPU.
+    void Apply();
+
+    /// Return size.
+    unsigned GetSize() const { return size_; }
+    /// Return whether has unapplied data.
+    bool IsDirty() const { return dirty_; }
+
+private:
+    /// Create buffer.
+    bool Create();
+
+    /// Shadow data.
+    SharedArrayPtr<unsigned char> shadowData_;
+    /// Buffer byte size.
+    unsigned size_;
+    /// Dirty flag.
+    bool dirty_;
+};
+
+}

File diff suppressed because it is too large
+ 359 - 283
Source/Urho3D/Graphics/OpenGL/OGLGraphics.cpp


+ 60 - 41
Source/Urho3D/Graphics/OpenGL/OGLGraphics.h

@@ -34,6 +34,7 @@
 namespace Urho3D
 {
 
+class ConstantBuffer;
 class File;
 class Image;
 class IndexBuffer;
@@ -54,7 +55,6 @@ class VertexBuffer;
 typedef HashMap<Pair<ShaderVariation*, ShaderVariation*>, SharedPtr<ShaderProgram> > ShaderProgramMap;
 
 static const unsigned NUM_SCREEN_BUFFERS = 2;
-static const unsigned NUM_TEMP_MATRICES = 8;
 
 /// CPU-side scratch buffer for vertex data updates.
 struct ScratchBuffer
@@ -102,6 +102,8 @@ public:
     void SetSRGB(bool enable);
     /// Set whether to flush the GPU command buffer to prevent multiple frames being queued and uneven frame timesteps. Not yet implemented on OpenGL.
     void SetFlushGPU(bool enable);
+    /// Set forced use of OpenGL 2 even if OpenGL 3 is available. Must be called before setting the screen mode for the first time. Default false.
+    void SetForceGL2(bool enable);
     /// Set allowed screen orientations as a space-separated list of "LandscapeLeft", "LandscapeRight", "Portrait" and "PortraitUpsideDown". Affects currently only iOS platform.
     void SetOrientations(const String& orientations);
     /// Toggle between full screen and windowed mode. Return true if successful.
@@ -166,8 +168,6 @@ public:
     void ClearParameterSources();
     /// Clear remembered transform shader parameter sources.
     void ClearTransformSources();
-    /// Clean up unused shader programs.
-    void CleanupShaderPrograms();
     /// Set texture.
     void SetTexture(unsigned index, Texture* texture);
     /// Bind texture unit 0 for update. Called by Texture.
@@ -206,8 +206,6 @@ public:
     void SetDepthTest(CompareMode mode);
     /// Set depth write on/off.
     void SetDepthWrite(bool enable);
-    /// Set antialiased drawing mode on/off. Default is on if the backbuffer is multisampled. Has no effect when backbuffer is not multisampled.
-    void SetDrawAntialiased(bool enable);
     /// Set polygon fill mode.
     void SetFillMode(FillMode mode);
     /// Set scissor test.
@@ -218,12 +216,6 @@ public:
     void SetStencilTest(bool enable, CompareMode mode = CMP_ALWAYS, StencilOp pass = OP_KEEP, StencilOp fail = OP_KEEP, StencilOp zFail = OP_KEEP, unsigned stencilRef = 0, unsigned compareMask = M_MAX_UNSIGNED, unsigned writeMask = M_MAX_UNSIGNED);
     /// Set a custom clipping plane. The plane is specified in world space, but is dependent on the view and projection matrices.
     void SetClipPlane(bool enable, const Plane& clipPlane = Plane::UP, const Matrix3x4& view = Matrix3x4::IDENTITY, const Matrix4& projection = Matrix4::IDENTITY);
-    /// Set vertex buffer stream frequency. No-op on OpenGL.
-    void SetStreamFrequency(unsigned index, unsigned frequency);
-    /// Reset stream frequencies. No-op on OpenGL.
-    void ResetStreamFrequencies();
-    /// Set force Shader Model 2 flag. No-op on OpenGL.
-    void SetForceSM2(bool enable);
     /// Begin dumping shader variation names to an XML file for precaching.
     void BeginDumpShaders(const String& fileName);
     /// End dumping shader variations names.
@@ -239,6 +231,8 @@ public:
     void* GetExternalWindow() const { return externalWindow_; }
     /// Return window title.
     const String& GetWindowTitle() const { return windowTitle_; }
+    /// Return graphics API name.
+    const String& GetApiName() const { return apiName_; }
     /// Return window position.
     IntVector2 GetWindowPosition() const;
     /// Return window width.
@@ -261,6 +255,8 @@ public:
     bool GetSRGB() const { return sRGB_; }
     /// Return whether the GPU command buffer is flushed each frame. Not yet implemented on OpenGL.
     bool GetFlushGPU() const { return false; }
+    /// Return whether OpenGL 2 use is forced.
+    bool GetForceGL2() const { return forceGL2_; }
     /// Return allowed screen orientations.
     const String& GetOrientations() const { return orientations_; }
     /// Return whether device is lost, and can not yet render.
@@ -275,8 +271,6 @@ public:
     unsigned GetShadowMapFormat() const { return shadowMapFormat_; }
     /// Return 24-bit shadow map depth texture format, or 0 if not supported.
     unsigned GetHiresShadowMapFormat() const { return hiresShadowMapFormat_; }
-    /// Return whether Shader Model 3 is supported. Has no meaning on OpenGL, so is assumed to be true.
-    bool GetSM3Support() const { return true; }
     /// Return whether hardware instancing is supported.
     bool GetInstancingSupport() const { return instancingSupport_; }
     /// Return whether light pre-pass rendering is supported.
@@ -289,8 +283,6 @@ public:
     bool GetHardwareShadowSupport() const { return true; }
     /// Return whether a readable hardware depth format is available.
     bool GetReadableDepthSupport() const { return GetReadableDepthFormat() != 0; }
-    /// Return whether stream offset is supported. Always true on OpenGL.
-    bool GetStreamOffsetSupport() const { return true; }
     /// Return whether sRGB conversion on texture sampling is supported.
     bool GetSRGBSupport() const { return sRGBSupport_; }
     /// Return whether sRGB conversion on rendertarget writing is supported.
@@ -349,8 +341,6 @@ public:
     CompareMode GetDepthTest() const { return depthTestMode_; }
     /// Return whether depth write is enabled.
     bool GetDepthWrite() const { return depthWrite_; }
-    /// Return whether antialiased drawing mode is enabled.
-    bool GetDrawAntialiased() const { return drawAntialiased_; }
     /// Return polygon fill mode.
     FillMode GetFillMode() const { return fillMode_; }
     /// Return whether stencil test is enabled.
@@ -375,12 +365,8 @@ public:
     unsigned GetStencilWriteMask() const { return stencilWriteMask_; }
     /// Return whether a custom clipping plane is in use.
     bool GetUseClipPlane() const { return useClipPlane_; }
-    /// Return stream frequency by vertex buffer index. Always returns 0 on OpenGL.
-    unsigned GetStreamFrequency(unsigned index) const;
     /// Return rendertarget width and height.
     IntVector2 GetRenderTargetDimensions() const;
-    /// Return force Shader Model 2 flag. Always false on OpenGL.
-    bool GetForceSM2() const { return false; }
 
     /// Window was resized through user interaction. Called by Input subsystem.
     void WindowResized();
@@ -396,6 +382,12 @@ public:
     void FreeScratchBuffer(void* buffer);
     /// Clean up too large scratch buffers.
     void CleanupScratchBuffers();
+    /// Clean up a render surface from all FBOs.
+    void CleanupRenderSurface(RenderSurface* surface);
+    /// Clean up shader programs when a shader variation is released or destroyed.
+    void CleanupShaderPrograms(ShaderVariation* variation);
+    /// Reserve a constant buffer.
+    ConstantBuffer* GetOrCreateConstantBuffer(unsigned bindingIndex, unsigned size);
     /// Release/clear GPU objects and optionally close the window.
     void Release(bool clearGPUObjects, bool closeWindow);
     /// Restore GPU objects and reinitialize state. Requires an open window.
@@ -404,10 +396,12 @@ public:
     void Maximize();
     /// Minimize the Window.
     void Minimize();
-    /// Clean up a render surface from all FBOs.
-    void CleanupRenderSurface(RenderSurface* surface);
     /// Mark the FBO needing an update.
     void MarkFBODirty();
+    /// Bind a VBO, avoiding redundant operation.
+    void SetVBO(unsigned object);
+    /// Bind a UBO, avoiding redundant operation.
+    void SetUBO(unsigned object);
     
     /// Return the API-specific alpha texture format.
     static unsigned GetAlphaFormat();
@@ -443,22 +437,40 @@ public:
     static unsigned GetReadableDepthFormat();
     /// Return the API-specific texture format from a textual description, for example "rgb".
     static unsigned GetFormat(const String& formatName);
-    
+    /// Return UV offset required for pixel perfect rendering.
+    static const Vector2& GetPixelUVOffset() { return pixelUVOffset; }
+    /// Return maximum number of supported bones for skinning.
+    static unsigned GetMaxBones();
+    /// Return whether is using an OpenGL 3 context.
+    static bool GetGL3Support() { return gl3Support; }
+
 private:
     /// Create the application window icon.
     void CreateWindowIcon();
     /// Check supported rendering features.
     void CheckFeatureSupport(String& extensions);
-    /// Select FBO and commit changes.
-    void CommitFramebuffer();
-    /// Check FBO completeness.
-    bool CheckFramebuffer();
-    /// Cleanup unused and unbound FBO's.
-    void CleanupFramebuffers(bool force = false);
+    /// Prepare for draw call. Update constant buffers and setup the FBO.
+    void PrepareDraw();
+    /// Clean up all framebuffers. Called when destroying the context.
+    void CleanupFramebuffers();
     /// Reset cached rendering state.
     void ResetCachedState();
     /// Initialize texture unit mappings.
     void SetTextureUnitMappings();
+    /// Create a framebuffer using either extension or core functionality.
+    unsigned CreateFramebuffer();
+    /// Delete a framebuffer using either extension or core functionality.
+    void DeleteFramebuffer(unsigned fbo);
+    /// Bind a framebuffer using either extension or core functionality.
+    void BindFramebuffer(unsigned fbo);
+    /// Bind a framebuffer color attachment using either extension or core functionality.
+    void BindColorAttachment(unsigned index, unsigned target, unsigned object);
+    /// Bind a framebuffer depth attachment using either extension or core functionality.
+    void BindDepthAttachment(unsigned object, bool isRenderBuffer);
+    /// Bind a framebuffer stencil attachment using either extension or core functionality.
+    void BindStencilAttachment(unsigned object, bool isRenderBuffer);
+    /// Check FBO completeness using either extension or core functionality.
+    bool CheckFramebuffer();
     
     /// Mutex for accessing the GPU objects vector from several threads.
     Mutex gpuObjectMutex_;
@@ -490,6 +502,8 @@ private:
     bool tripleBuffer_;
     /// sRGB conversion on write flag for the main window.
     bool sRGB_;
+    /// Force OpenGL 2 use flag.
+    bool forceGL2_;
     /// Instancing support flag.
     bool instancingSupport_;
     /// Light prepass support flag.
@@ -515,7 +529,7 @@ private:
     /// Largest scratch buffer request this frame.
     unsigned maxScratchBufferRequest_;
     /// GPU objects.
-    Vector<GPUObject*> gpuObjects_;
+    PODVector<GPUObject*> gpuObjects_;
     /// Scratch buffers.
     Vector<ScratchBuffer> scratchBuffers_;
     /// Shadow map dummy color texture format.
@@ -544,6 +558,12 @@ private:
     unsigned textureTypes_[MAX_TEXTURE_UNITS];
     /// Texture unit mappings.
     HashMap<String, TextureUnit> textureUnits_;
+    /// All constant buffers.
+    HashMap<unsigned, SharedPtr<ConstantBuffer> > constantBuffers_;
+    /// Currently bound constant buffers.
+    ConstantBuffer* currentConstantBuffers_[MAX_SHADER_PARAMETER_GROUPS * 2];
+    /// Dirty constant buffers.
+    PODVector<ConstantBuffer*> dirtyConstantBuffers_;
     /// Rendertargets in use.
     RenderSurface* renderTargets_[MAX_RENDERTARGETS];
     /// Depth-stencil surface in use.
@@ -572,6 +592,8 @@ private:
     IntRect scissorRect_;
     /// Scissor test enable flag.
     bool scissorTest_;
+    /// Current custom clip plane in post-projection space.
+    Vector4 clipPlane_;
     /// Stencil test compare mode.
     CompareMode stencilTestMode_;
     /// Stencil operation on pass.
@@ -590,22 +612,12 @@ private:
     bool stencilTest_;
     /// Custom clip plane enable flag.
     bool useClipPlane_;
-    /// Draw antialiased mode flag.
-    bool drawAntialiased_;
-    /// Releasing GPU objects flag.
-    bool releasingGPUObjects_;
     /// Last used instance data offset.
     unsigned lastInstanceOffset_;
     /// Default texture filtering mode.
     TextureFilterMode defaultTextureFilterMode_;
     /// Map for additional depth textures, to emulate Direct3D9 ability to mix render texture and backbuffer rendering.
     HashMap<int, SharedPtr<Texture2D> > depthTextures_;
-    /// Remembered shader parameter sources.
-    const void* shaderParameterSources_[MAX_SHADER_PARAMETER_GROUPS];
-    /// Temp matrices for transposing shader parameters.
-    Matrix3 tempMatrices3_[NUM_TEMP_MATRICES];
-    /// Temp matrices for transposing shader parameters.
-    Matrix4 tempMatrices4_[NUM_TEMP_MATRICES];
     /// Base directory for shaders.
     String shaderPath_;
     /// File extension for shaders.
@@ -618,6 +630,13 @@ private:
     SharedPtr<ShaderPrecache> shaderPrecache_;
     /// Allowed screen orientations.
     String orientations_;
+    /// Graphics API name.
+    String apiName_;
+
+    /// Pixel perfect UV offset.
+    static const Vector2 pixelUVOffset;
+    /// Flag for OpenGL 3 support.
+    static bool gl3Support;
 };
 
 /// Register Graphics library objects.

+ 4 - 2
Source/Urho3D/Graphics/OpenGL/OGLGraphicsImpl.cpp

@@ -31,10 +31,12 @@ namespace Urho3D
 GraphicsImpl::GraphicsImpl() :
     window_(0),
     context_(0),
-    systemFbo_(0),
+    systemFBO_(0),
     activeTexture_(0),
     enabledAttributes_(0),
-    boundFbo_(0),
+    boundFBO_(0),
+    boundVBO_(0),
+    boundUBO_(0),
     pixelFormat_(0),
     fboDirty_(false)
 {

+ 6 - 4
Source/Urho3D/Graphics/OpenGL/OGLGraphicsImpl.h

@@ -85,8 +85,6 @@ struct FrameBufferObject
     unsigned readBuffers_;
     /// Draw buffer bits.
     unsigned drawBuffers_;
-    /// Use timer for cleaning up.
-    Timer useTimer_;
 };
 
 /// %Graphics subsystem implementation. Holds API-specific objects.
@@ -106,13 +104,17 @@ private:
     /// SDL OpenGL context.
     SDL_GLContext context_;
     /// IOS system framebuffer handle.
-    unsigned systemFbo_;
+    unsigned systemFBO_;
     /// Active texture unit.
     unsigned activeTexture_;
     /// Vertex attributes in use.
     unsigned enabledAttributes_;
     /// Currently bound frame buffer object.
-    unsigned boundFbo_;
+    unsigned boundFBO_;
+    /// Currently bound vertex buffer object.
+    unsigned boundVBO_;
+    /// Currently bound uniform buffer object.
+    unsigned boundUBO_;
     /// Current pixel format.
     int pixelFormat_;
     /// Map for FBO's per resolution and format.

+ 3 - 7
Source/Urho3D/Graphics/OpenGL/OGLIndexBuffer.cpp

@@ -143,8 +143,7 @@ bool IndexBuffer::SetData(const void* data)
     {
         if (!graphics_->IsDeviceLost())
         {
-            graphics_->SetIndexBuffer(0);
-            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, object_);
+            graphics_->SetIndexBuffer(this);
             glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexCount_ * indexSize_, data, dynamic_ ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
         }
         else
@@ -191,8 +190,7 @@ bool IndexBuffer::SetDataRange(const void* data, unsigned start, unsigned count,
     {
         if (!graphics_->IsDeviceLost())
         {
-            graphics_->SetIndexBuffer(0);
-            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, object_);
+            graphics_->SetIndexBuffer(this);
             if (!discard || start != 0)
                 glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, start * indexSize_, count * indexSize_, data);
             else
@@ -333,8 +331,6 @@ bool IndexBuffer::Create()
             return true;
         }
         
-        graphics_->SetIndexBuffer(0);
-        
         if (!object_)
             glGenBuffers(1, &object_);
         if (!object_)
@@ -343,7 +339,7 @@ bool IndexBuffer::Create()
             return false;
         }
         
-        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, object_);
+        graphics_->SetIndexBuffer(this);
         glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexCount_ * indexSize_, 0, dynamic_ ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
     }
     

+ 184 - 10
Source/Urho3D/Graphics/OpenGL/OGLShaderProgram.cpp

@@ -20,23 +20,41 @@
 // THE SOFTWARE.
 //
 
+#include "../../Graphics/ConstantBuffer.h"
 #include "../../Graphics/Graphics.h"
 #include "../../Graphics/GraphicsImpl.h"
 #include "../../Graphics/ShaderProgram.h"
 #include "../../Graphics/ShaderVariation.h"
+#include "../../IO/Log.h"
 
 #include "../../DebugNew.h"
 
 namespace Urho3D
 {
 
+const char* shaderParameterGroups[] = {
+    "frame",
+    "camera",
+    "zone",
+    "light",
+    "material",
+    "object",
+    "custom"
+};
+
+unsigned ShaderProgram::globalFrameNumber = 0;
+const void* ShaderProgram::globalParameterSources[MAX_SHADER_PARAMETER_GROUPS];
+
 ShaderProgram::ShaderProgram(Graphics* graphics, ShaderVariation* vertexShader, ShaderVariation* pixelShader) :
     GPUObject(graphics),
     vertexShader_(vertexShader),
-    pixelShader_(pixelShader)
+    pixelShader_(pixelShader),
+    frameNumber_(0)
 {
     for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
         useTextureUnit_[i] = false;
+    for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS; ++i)
+        parameterSources_[i] = (const void*)M_MAX_UNSIGNED;
 }
 
 ShaderProgram::~ShaderProgram()
@@ -50,7 +68,6 @@ void ShaderProgram::OnDeviceLost()
     
     if (graphics_ && graphics_->GetShaderProgram() == this)
         graphics_->SetShaders(0, 0);
-    
 
     linkerOutput_.Clear();
 }
@@ -76,6 +93,8 @@ void ShaderProgram::Release()
         
         for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
             useTextureUnit_[i] = false;
+        for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS; ++i)
+            constantBuffers_[i].Reset();
     }
 }
 
@@ -140,6 +159,75 @@ bool ShaderProgram::Link()
     glUseProgram(object_);
     glGetProgramiv(object_, GL_ACTIVE_UNIFORMS, &uniformCount);
     
+    // Check for constant buffers
+    #ifndef GL_ES_VERSION_2_0
+    HashMap<unsigned, unsigned> blockToBinding;
+
+    if (Graphics::GetGL3Support())
+    {
+        int numUniformBlocks = 0;
+
+        glGetProgramiv(object_, GL_ACTIVE_UNIFORM_BLOCKS, &numUniformBlocks);
+        for (int i = 0; i < numUniformBlocks; ++i)
+        {
+            int nameLength;
+            glGetActiveUniformBlockName(object_, i, MAX_PARAMETER_NAME_LENGTH, &nameLength, uniformName);
+
+            String name(uniformName, nameLength);
+
+            unsigned blockIndex = glGetUniformBlockIndex(object_, name.CString());
+            unsigned group = M_MAX_UNSIGNED;
+
+            // Try to recognize the use of the buffer from its name
+            for (unsigned j = 0; j < MAX_SHADER_PARAMETER_GROUPS; ++j)
+            {
+                if (name.Contains(shaderParameterGroups[j], false))
+                {
+                    group = j;
+                    break;
+                }
+            }
+
+            // If name is not recognized, search for a digit in the name and use that as the group index
+            if (group == M_MAX_UNSIGNED)
+            {
+                for (unsigned j = 1; j < name.Length(); ++j)
+                {
+                    if (name[j] >= '0' && name[j] <= '5')
+                    {
+                        group = name[j] - '0';
+                        break;
+                    }
+                }
+            }
+
+            if (group >= MAX_SHADER_PARAMETER_GROUPS)
+            {
+                LOGWARNING("Skipping unrecognized uniform block " + name + " in shader program " + vertexShader_->GetFullName() +
+                    " " + pixelShader_->GetFullName());
+                continue;
+            }
+
+            // Find total constant buffer data size
+            int dataSize;
+            glGetActiveUniformBlockiv(object_, blockIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &dataSize);
+            if (!dataSize)
+                continue;
+
+            unsigned bindingIndex = group;
+            // Vertex shader constant buffer bindings occupy slots starting from zero to maximum supported, pixel shader bindings
+            // from that point onward
+            if (name.Contains("PS", false))
+                bindingIndex += MAX_SHADER_PARAMETER_GROUPS;
+
+            glUniformBlockBinding(object_, blockIndex, bindingIndex);
+            blockToBinding[blockIndex] = bindingIndex;
+
+            constantBuffers_[bindingIndex] = graphics_->GetOrCreateConstantBuffer(bindingIndex, dataSize);
+        }
+    }
+    #endif
+
     // Check for shader parameters and texture units
     for (int i = 0; i < uniformCount; ++i)
     {
@@ -149,10 +237,6 @@ bool ShaderProgram::Link()
         glGetActiveUniform(object_, i, MAX_PARAMETER_NAME_LENGTH, 0, &count, &type, uniformName);
         int location = glGetUniformLocation(object_, uniformName);
         
-        // Skip inbuilt or disabled uniforms
-        if (location < 0)
-            continue;
-        
         // Check for array index included in the name and strip it
         String name(uniformName);
         unsigned index = name.Find('[');
@@ -167,14 +251,31 @@ bool ShaderProgram::Link()
         
         if (name[0] == 'c')
         {
-            // Store the constant uniform mapping
+            // Store constant uniform
             String paramName = name.Substring(1);
             ShaderParameter newParam;
-            newParam.location_ = location;
             newParam.type_ = type;
-            shaderParameters_[StringHash(paramName)] = newParam;
+            newParam.location_ = location;
+
+            #ifndef GL_ES_VERSION_2_0
+            // If running OpenGL 3, the uniform may be inside a constant buffer
+            if (newParam.location_ < 0 && Graphics::GetGL3Support())
+            {
+                int blockIndex, blockOffset;
+                glGetActiveUniformsiv(object_, 1, (const GLuint*)&i, GL_UNIFORM_BLOCK_INDEX, &blockIndex);
+                glGetActiveUniformsiv(object_, 1, (const GLuint*)&i, GL_UNIFORM_OFFSET, &blockOffset);
+                if (blockIndex >= 0)
+                {
+                    newParam.location_ = blockOffset;
+                    newParam.bufferPtr_ = constantBuffers_[blockToBinding[blockIndex]];
+                }
+            }
+            #endif
+
+            if (newParam.location_ >= 0)
+                shaderParameters_[StringHash(paramName)] = newParam;
         }
-        else if (name[0] == 's')
+        else if (location >= 0 && name[0] == 's')
         {
             // Set the samplers here so that they do not have to be set later
             int unit = graphics_->GetTextureUnit(name.Substring(1));
@@ -229,4 +330,77 @@ const ShaderParameter* ShaderProgram::GetParameter(StringHash param) const
         return 0;
 }
 
+bool ShaderProgram::NeedParameterUpdate(ShaderParameterGroup group, const void* source)
+{
+    // If global framenumber has changed, invalidate all per-program parameter sources now
+    if (globalFrameNumber != frameNumber_)
+    {
+        for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS; ++i)
+            parameterSources_[i] = (const void*)M_MAX_UNSIGNED;
+        frameNumber_ = globalFrameNumber;
+    }
+
+    // The shader program may use a mixture of constant buffers and individual uniforms even in the same group
+    #ifndef GL_ES_VERSION_2_0
+    bool useBuffer = constantBuffers_[group].Get() || constantBuffers_[group + MAX_SHADER_PARAMETER_GROUPS].Get();
+    bool useIndividual = !constantBuffers_[group].Get() || !constantBuffers_[group + MAX_SHADER_PARAMETER_GROUPS].Get();
+    bool needUpdate = false;
+
+    if (useBuffer && globalParameterSources[group] != source)
+    {
+        globalParameterSources[group] = source;
+        needUpdate = true;
+    }
+
+    if (useIndividual && parameterSources_[group] != source)
+    {
+        parameterSources_[group] = source;
+        needUpdate = true;
+    }
+
+    return needUpdate;
+    #else
+    if (parameterSources_[group] != source)
+    {
+        parameterSources_[group] = source;
+        return true;
+    }
+    else
+        return false;
+    #endif
+}
+
+void ShaderProgram::ClearParameterSource(ShaderParameterGroup group)
+{
+    // The shader program may use a mixture of constant buffers and individual uniforms even in the same group
+    #ifndef GL_ES_VERSION_2_0
+    bool useBuffer = constantBuffers_[group].Get() || constantBuffers_[group + MAX_SHADER_PARAMETER_GROUPS].Get();
+    bool useIndividual = !constantBuffers_[group].Get() || !constantBuffers_[group + MAX_SHADER_PARAMETER_GROUPS].Get();
+
+    if (useBuffer)
+        globalParameterSources[group] = (const void*)M_MAX_UNSIGNED;
+    if (useIndividual)
+        parameterSources_[group] = (const void*)M_MAX_UNSIGNED;
+    #else
+    parameterSources_[group] = (const void*)M_MAX_UNSIGNED;
+    #endif
+}
+
+void ShaderProgram::ClearParameterSources()
+{
+    ++globalFrameNumber;
+    if (!globalFrameNumber)
+        ++globalFrameNumber;
+
+    #ifndef GL_ES_VERSION_2_0
+    for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS; ++i)
+        globalParameterSources[i] = (const void*)M_MAX_UNSIGNED;
+    #endif
+}
+
+void ShaderProgram::ClearGlobalParameterSource(ShaderParameterGroup group)
+{
+    globalParameterSources[group] = (const void*)M_MAX_UNSIGNED;
+}
+
 }

+ 33 - 1
Source/Urho3D/Graphics/OpenGL/OGLShaderProgram.h

@@ -30,16 +30,25 @@
 namespace Urho3D
 {
 
+class ConstantBuffer;
 class Graphics;
 class ShaderVariation;
 
 /// %Shader parameter definition.
 struct ShaderParameter
 {
-    /// Uniform location.
+    /// Construct with defaults.
+    ShaderParameter() :
+        bufferPtr_(0)
+    {
+    }
+
+    /// Uniform location or byte offset in constant buffer.
     int location_;
     /// Element type.
     unsigned type_;
+    /// Constant buffer pointer.
+    ConstantBuffer* bufferPtr_;
 };
 
 /// Linked shader program on the GPU.
@@ -71,7 +80,19 @@ public:
     const ShaderParameter* GetParameter(StringHash param) const;
     /// Return linker output.
     const String& GetLinkerOutput() const { return linkerOutput_; }
+    /// Return all constant buffers.
+    const SharedPtr<ConstantBuffer>* GetConstantBuffers() const { return &constantBuffers_[0]; }
     
+    /// Check whether a shader parameter group needs update. Does not actually check whether parameters exist in the shaders.
+    bool NeedParameterUpdate(ShaderParameterGroup group, const void* source);
+    /// Clear a parameter source. Affects only the current shader program if appropriate.
+    void ClearParameterSource(ShaderParameterGroup group);
+
+    /// Clear all parameter sources from all shader programs by incrementing the global parameter source framenumber.
+    static void ClearParameterSources();
+    /// Clear a global parameter source when constant buffers change.
+    static void ClearGlobalParameterSource(ShaderParameterGroup group);
+
 private:
     /// Vertex shader.
     WeakPtr<ShaderVariation> vertexShader_;
@@ -81,8 +102,19 @@ private:
     HashMap<StringHash, ShaderParameter> shaderParameters_;
     /// Texture unit use.
     bool useTextureUnit_[MAX_TEXTURE_UNITS];
+    /// Constant buffers by binding index.
+    SharedPtr<ConstantBuffer> constantBuffers_[MAX_SHADER_PARAMETER_GROUPS * 2];
+    /// Remembered shader parameter sources for individual uniform mode.
+    const void* parameterSources_[MAX_SHADER_PARAMETER_GROUPS];
     /// Shader link error string.
     String linkerOutput_;
+    /// Shader parameter source framenumber.
+    unsigned frameNumber_;
+
+    /// Global shader parameter source framenumber.
+    static unsigned globalFrameNumber;
+    /// Remembered global shader parameter sources for constant buffer mode.
+    static const void* globalParameterSources[MAX_SHADER_PARAMETER_GROUPS];
 };
 
 }

+ 10 - 5
Source/Urho3D/Graphics/OpenGL/OGLShaderVariation.cpp

@@ -49,9 +49,6 @@ void ShaderVariation::OnDeviceLost()
     GPUObject::OnDeviceLost();
 
     compilerOutput_.Clear();
-    
-    if (graphics_)
-        graphics_->CleanupShaderPrograms();
 }
 
 void ShaderVariation::Release()
@@ -78,7 +75,7 @@ void ShaderVariation::Release()
         }
         
         object_ = 0;
-        graphics_->CleanupShaderPrograms();
+        graphics_->CleanupShaderPrograms(this);
     }
     
     compilerOutput_.Clear();
@@ -124,10 +121,16 @@ bool ShaderVariation::Create()
             shaderCode += versionDefine + "\n";
         }
     }
+    // Force GLSL version 150 if no version define and GL3 is being used
+    if (!verEnd && Graphics::GetGL3Support())
+        shaderCode += "#version 150\n";
 
     // Distinguish between VS and PS compile in case the shader code wants to include/omit different things
     shaderCode += type_ == VS ? "#define COMPILEVS\n" : "#define COMPILEPS\n";
-    
+
+    // Add define for the maximum number of supported bones
+    shaderCode += "#define MAXBONES " + String(Graphics::GetMaxBones()) + "\n";
+
     // Prepend the defines to the shader code
     Vector<String> defineVec = defines_.Split(' ');
     for (unsigned i = 0; i < defineVec.Size(); ++i)
@@ -151,6 +154,8 @@ bool ShaderVariation::Create()
     #ifdef EMSCRIPTEN
     shaderCode += "#define WEBGL\n";
     #endif
+    if (Graphics::GetGL3Support())
+        shaderCode += "#define GL3\n";
 
     // When version define found, do not insert it a second time
     if (verEnd > 0)

+ 11 - 2
Source/Urho3D/Graphics/OpenGL/OGLTexture.cpp

@@ -310,10 +310,17 @@ unsigned Texture::GetRowDataSize(int width) const
     case GL_LUMINANCE32F_ARB:
     case GL_DEPTH24_STENCIL8_EXT:
     case GL_RG16:
+    case GL_R32F:
     #endif
         return width * 4;
         
     #ifndef GL_ES_VERSION_2_0
+    case GL_R8:
+        return width;
+
+    case GL_RG8:
+        return width * 2;
+
     case GL_RGBA16:
         return width * 8;
         
@@ -358,7 +365,9 @@ unsigned Texture::GetExternalFormat(unsigned format)
         return GL_LUMINANCE;
     else if (format == GL_SLUMINANCE_ALPHA_EXT)
         return GL_LUMINANCE_ALPHA;
-    else if (format == GL_RG16 || format == GL_RG16F || format == GL_RG32F)
+    else if (format == GL_R8 || format == GL_R32F)
+        return GL_RED;
+    else if (format == GL_RG8 || format == GL_RG16 || format == GL_RG16F || format == GL_RG32F)
         return GL_RG;
     else if (format == GL_RGBA16 || format == GL_RGBA16F_ARB || format == GL_RGBA32F_ARB || format == GL_SRGB_ALPHA_EXT)
         return GL_RGBA;
@@ -379,7 +388,7 @@ unsigned Texture::GetDataType(unsigned format)
     else if (format == GL_RG16 || format == GL_RGBA16)
         return GL_UNSIGNED_SHORT;
     else if (format == GL_LUMINANCE16F_ARB || format == GL_LUMINANCE32F_ARB || format == GL_RGBA16F_ARB ||
-        format == GL_RGBA32F_ARB || format == GL_RG16F || format == GL_RG32F)
+        format == GL_RGBA32F_ARB || format == GL_RG16F || format == GL_RG32F || format == GL_R32F)
         return GL_FLOAT;
     else
         return GL_UNSIGNED_BYTE;

+ 3 - 3
Source/Urho3D/Graphics/OpenGL/OGLTexture.h

@@ -50,7 +50,7 @@ public:
     void SetFilterMode(TextureFilterMode filter);
     /// Set addressing mode by texture coordinate.
     void SetAddressMode(TextureCoordinate coord, TextureAddressMode address);
-    /// Set shadow compare mode, OpenGL only.
+    /// Set shadow compare mode.
     void SetShadowCompare(bool enable);
     /// Set border color for border addressing mode.
     void SetBorderColor(const Color& color);
@@ -85,7 +85,7 @@ public:
     TextureFilterMode GetFilterMode() const { return filterMode_; }
     /// Return addressing mode by texture coordinate.
     TextureAddressMode GetAddressMode(TextureCoordinate coord) const { return addressMode_[coord]; }
-    /// Return whether shadow compare is enabled, OpenGL only.
+    /// Return whether shadow compare is enabled.
     bool GetShadowCompare() const { return shadowCompare_; }
      /// Return border color.
     const Color& GetBorderColor() const { return borderColor_; }
@@ -143,7 +143,7 @@ protected:
     int height_;
     /// Texture depth.
     int depth_;
-    /// Shadow compare mode, OpenGL only.
+    /// Shadow compare mode.
     bool shadowCompare_;
     /// Parameters dirty flag.
     bool parametersDirty_;

+ 4 - 3
Source/Urho3D/Graphics/OpenGL/OGLVertexBuffer.cpp

@@ -154,6 +154,7 @@ void VertexBuffer::Release()
                     graphics_->SetVertexBuffer(0);
             }
             
+            graphics_->SetVBO(0);
             glDeleteBuffers(1, &object_);
         }
         
@@ -217,7 +218,7 @@ bool VertexBuffer::SetData(const void* data)
     {
         if (!graphics_->IsDeviceLost())
         {
-            glBindBuffer(GL_ARRAY_BUFFER, object_);
+            graphics_->SetVBO(object_);
             glBufferData(GL_ARRAY_BUFFER, vertexCount_ * vertexSize_, data, dynamic_ ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
         }
         else
@@ -264,7 +265,7 @@ bool VertexBuffer::SetDataRange(const void* data, unsigned start, unsigned count
     {
         if (!graphics_->IsDeviceLost())
         {
-            glBindBuffer(GL_ARRAY_BUFFER, object_);
+            graphics_->SetVBO(object_);
             if (!discard || start != 0)
                 glBufferSubData(GL_ARRAY_BUFFER, start * vertexSize_, count * vertexSize_, data);
             else
@@ -409,7 +410,7 @@ bool VertexBuffer::Create()
             return false;
         }
         
-        glBindBuffer(GL_ARRAY_BUFFER, object_);
+        graphics_->SetVBO(object_);
         glBufferData(GL_ARRAY_BUFFER, vertexCount_ * vertexSize_, 0, dynamic_ ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
     }
     

+ 2 - 0
Source/Urho3D/Graphics/RenderPath.h

@@ -146,6 +146,8 @@ struct RenderPathCommand
     RenderCommandSortMode sortMode_;
     /// Scene pass name.
     String pass_;
+    /// Scene pass index. Filled by View.
+    unsigned passIndex_;
     /// Command/pass metadata.
     String metadata_;
     /// Vertex shader name.

+ 3 - 1
Source/Urho3D/Graphics/RenderSurface.h

@@ -22,8 +22,10 @@
 
 #pragma once
 
-#ifdef URHO3D_OPENGL
+#if defined(URHO3D_OPENGL)
 #include "OpenGL/OGLRenderSurface.h"
+#elif defined(URHO3D_D3D11)
+#include "Direct3D11/D3D11RenderSurface.h"
 #else
 #include "Direct3D9/D3D9RenderSurface.h"
 #endif

+ 7 - 24
Source/Urho3D/Graphics/Renderer.cpp

@@ -260,7 +260,6 @@ Renderer::Renderer(Context* context) :
     shadowQuality_(SHADOWQUALITY_HIGH_16BIT),
     maxShadowMaps_(1),
     minInstances_(2),
-    maxInstanceTriangles_(500),
     maxSortedInstances_(1000),
     maxOccluderTriangles_(5000),
     occlusionBufferSize_(256),
@@ -438,11 +437,6 @@ void Renderer::SetMinInstances(int instances)
     minInstances_ = Max(instances, 2);
 }
 
-void Renderer::SetMaxInstanceTriangles(int triangles)
-{
-    maxInstanceTriangles_ = Max(triangles, 0);
-}
-
 void Renderer::SetMaxSortedInstances(int instances)
 {
     maxSortedInstances_ = Max(instances, 0);
@@ -661,7 +655,6 @@ void Renderer::Render()
     
     graphics_->SetDefaultTextureFilterMode(textureFilterMode_);
     graphics_->SetTextureAnisotropy(textureAnisotropy_);
-    graphics_->ClearParameterSources();
     
     // If no views, just clear the screen
     if (views_.Empty())
@@ -891,13 +884,12 @@ Texture2D* Renderer::GetShadowMap(Light* light, Camera* camera, unsigned viewWid
         }
         else
         {
-            #ifdef URHO3D_OPENGL
             #ifndef GL_ES_VERSION_2_0
-            // OpenGL (desktop): shadow compare mode needs to be specifically enabled for the shadow map
+            // OpenGL (desktop) and D3D11: shadow compare mode needs to be specifically enabled for the shadow map
             newShadowMap->SetFilterMode(FILTER_BILINEAR);
             newShadowMap->SetShadowCompare(true);
             #endif
-            #else
+            #ifndef URHO3D_OPENGL
             // Direct3D9: when shadow compare must be done manually, use nearest filtering so that the filtering of point lights
             // and other shadowed lights matches
             newShadowMap->SetFilterMode(graphics_->GetHardwareShadowSupport() ? FILTER_BILINEAR : FILTER_NEAREST);
@@ -1051,7 +1043,7 @@ void Renderer::SetBatchShaders(Batch& batch, Technique* tech, bool allowShadows)
     {
         // First release all previous shaders, then load
         pass->ReleaseShaders();
-        LoadPassShaders(tech, pass->GetType());
+        LoadPassShaders(pass);
     }
     
     // Make sure shaders are loaded now
@@ -1059,10 +1051,8 @@ void Renderer::SetBatchShaders(Batch& batch, Technique* tech, bool allowShadows)
     {
         bool heightFog = batch.zone_ && batch.zone_->GetHeightFog();
         
-        // If instancing is not supported, but was requested, or the object is too large to be instanced,
-        // choose static geometry vertex shader instead
-        if (batch.geometryType_ == GEOM_INSTANCED && (!GetDynamicInstancing() || batch.geometry_->GetIndexCount() >
-            (unsigned)maxInstanceTriangles_ * 3))
+        // If instancing is not supported, but was requested, choose static geometry vertex shader instead
+        if (batch.geometryType_ == GEOM_INSTANCED && !GetDynamicInstancing())
             batch.geometryType_ = GEOM_STATIC;
         
         if (batch.geometryType_ == GEOM_STATIC_NOINSTANCING)
@@ -1466,12 +1456,8 @@ void Renderer::LoadShaders()
     shadersDirty_ = false;
 }
 
-void Renderer::LoadPassShaders(Technique* tech, StringHash type)
+void Renderer::LoadPassShaders(Pass* pass)
 {
-    Pass* pass = tech->GetPass(type);
-    if (!pass)
-        return;
-    
     PROFILE(LoadPassShaders);
     
     unsigned shadows = (graphics_->GetHardwareShadowSupport() ? 1 : 0) | (shadowQuality_ & SHADOWQUALITY_HIGH_16BIT);
@@ -1695,11 +1681,8 @@ void Renderer::CreateInstancingBuffer()
         return;
     }
     
-    // If must lock the buffer for each batch group, set a smaller size
-    unsigned defaultSize = graphics_->GetStreamOffsetSupport() ? INSTANCING_BUFFER_DEFAULT_SIZE : INSTANCING_BUFFER_DEFAULT_SIZE / 4;
-    
     instancingBuffer_ = new VertexBuffer(context_);
-    if (!instancingBuffer_->SetSize(defaultSize, INSTANCING_BUFFER_MASK, true))
+    if (!instancingBuffer_->SetSize(INSTANCING_BUFFER_DEFAULT_SIZE, INSTANCING_BUFFER_MASK, true))
     {
         instancingBuffer_.Reset();
         dynamicInstancing_ = false;

+ 1 - 7
Source/Urho3D/Graphics/Renderer.h

@@ -191,8 +191,6 @@ public:
     void SetDynamicInstancing(bool enable);
     /// Set minimum number of instances required in a batch group to render as instanced.
     void SetMinInstances(int instances);
-    /// Set maximum number of triangles per object for instancing.
-    void SetMaxInstanceTriangles(int triangles);
     /// Set maximum number of sorted instances per batch group. If exceeded, instances are rendered unsorted.
     void SetMaxSortedInstances(int instances);
     /// Set maximum number of occluder trianges.
@@ -240,8 +238,6 @@ public:
     bool GetDynamicInstancing() const { return dynamicInstancing_; }
     /// Return minimum number of instances required in a batch group to render as instanced.
     int GetMinInstances() const { return minInstances_; }
-    /// Return maximum number of triangles per object for instancing.
-    int GetMaxInstanceTriangles() const { return maxInstanceTriangles_; }
     /// Return maximum number of sorted instances per batch group.
     int GetMaxSortedInstances() const { return maxSortedInstances_; }
     /// Return maximum number of occluder triangles.
@@ -335,7 +331,7 @@ private:
     /// Reload shaders.
     void LoadShaders();
     /// Reload shaders for a material pass.
-    void LoadPassShaders(Technique* tech, StringHash passType);
+    void LoadPassShaders(Pass* pass);
     /// Release shaders used in materials.
     void ReleaseMaterialShaders();
     /// Reload textures.
@@ -439,8 +435,6 @@ private:
     int maxShadowMaps_;
     /// Minimum number of instances required in a batch group to render as instanced.
     int minInstances_;
-    /// Maximum triangles per object for instancing.
-    int maxInstanceTriangles_;
     /// Maximum sorted instances per batch group.
     int maxSortedInstances_;
     /// Maximum occluder triangles.

+ 5 - 1
Source/Urho3D/Graphics/ShaderProgram.h

@@ -22,6 +22,10 @@
 
 #pragma once
 
-#ifdef URHO3D_OPENGL
+#if defined(URHO3D_OPENGL)
 #include "OpenGL/OGLShaderProgram.h"
+#elif defined(URHO3D_D3D11)
+#include "Direct3D11/D3D11ShaderProgram.h"
+#else
+#include "Direct3D9/D3D9ShaderProgram.h"
 #endif

+ 4 - 2
Source/Urho3D/Graphics/ShaderVariation.h

@@ -22,8 +22,10 @@
 
 #pragma once
 
-#ifdef URHO3D_OPENGL
+#if defined(URHO3D_OPENGL)
 #include "OpenGL/OGLShaderVariation.h"
+#elif defined(URHO3D_D3D11)
+#include "Direct3D11/D3D11ShaderVariation.h"
 #else
 #include "Direct3D9/D3D9ShaderVariation.h"
-#endif
+#endif

+ 6 - 10
Source/Urho3D/Graphics/StaticModel.cpp

@@ -125,21 +125,15 @@ void StaticModel::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQuer
 void StaticModel::UpdateBatches(const FrameInfo& frame)
 {
     const BoundingBox& worldBoundingBox = GetWorldBoundingBox();
-    const Matrix3x4& worldTransform = node_->GetWorldTransform();
     distance_ = frame.camera_->GetDistance(worldBoundingBox.Center());
 
-    if (batches_.Size() > 1)
+    if (batches_.Size() == 1)
+        batches_[0].distance_ = distance_;
+    else
     {
+        const Matrix3x4& worldTransform = node_->GetWorldTransform();
         for (unsigned i = 0; i < batches_.Size(); ++i)
-        {
             batches_[i].distance_ = frame.camera_->GetDistance(worldTransform * geometryData_[i].center_);
-            batches_[i].worldTransform_ = &worldTransform;
-        }
-    }
-    else if (batches_.Size() == 1)
-    {
-        batches_[0].distance_ = distance_;
-        batches_[0].worldTransform_ = &worldTransform;
     }
 
     float scale = worldBoundingBox.Size().DotProduct(DOT_SCALE);
@@ -254,8 +248,10 @@ void StaticModel::SetModel(Model* model)
         SetNumGeometries(model->GetNumGeometries());
         const Vector<Vector<SharedPtr<Geometry> > >& geometries = model->GetGeometries();
         const PODVector<Vector3>& geometryCenters = model->GetGeometryCenters();
+        const Matrix3x4* worldTransform = node_ ? &node_->GetWorldTransform() : (const Matrix3x4*)0;
         for (unsigned i = 0; i < geometries.Size(); ++i)
         {
+            batches_[i].worldTransform_ = worldTransform;
             geometries_[i] = geometries[i];
             geometryData_[i].center_ = geometryCenters[i];
         }

+ 120 - 57
Source/Urho3D/Graphics/Technique.cpp

@@ -69,21 +69,23 @@ static const char* lightingModeNames[] =
     0
 };
 
-Pass::Pass(StringHash type) :
-    type_(type),
+Pass::Pass(const String& name) :
     blendMode_(BLEND_REPLACE),
     depthTestMode_(CMP_LESSEQUAL),
     lightingMode_(LIGHTING_UNLIT),
     shadersLoadedFrameNumber_(0),
     depthWrite_(true),
     alphaMask_(false),
-    isSM3_(false),
     isDesktop_(false)
 {
+    name_ = name.ToLower();
+    index_ = Technique::GetPassIndex(name_);
+
     // Guess default lighting mode from pass name
-    if (type == PASS_BASE || type == PASS_ALPHA || type == PASS_MATERIAL || type == PASS_DEFERRED)
+    if (index_ == Technique::basePassIndex || index_ == Technique::alphaPassIndex || index_ == Technique::materialPassIndex ||
+        index_ == Technique::deferredPassIndex)
         lightingMode_ = LIGHTING_PERVERTEX;
-    else if (type == PASS_LIGHT || type == PASS_LITBASE || type == PASS_LITALPHA)
+    else if (index_ == Technique::lightPassIndex || index_ == Technique::litBasePassIndex || index_ == Technique::litAlphaPassIndex)
         lightingMode_ = LIGHTING_PERPIXEL;
 }
 
@@ -116,11 +118,6 @@ void Pass::SetAlphaMask(bool enable)
     alphaMask_ = enable;
 }
 
-void Pass::SetIsSM3(bool enable)
-{
-    isSM3_ = enable;
-}
-
 void Pass::SetIsDesktop(bool enable)
 {
     isDesktop_ = enable;
@@ -161,15 +158,20 @@ void Pass::MarkShadersLoaded(unsigned frameNumber)
     shadersLoadedFrameNumber_ = frameNumber;
 }
 
+unsigned Technique::basePassIndex = 0;
+unsigned Technique::alphaPassIndex = 0;
+unsigned Technique::materialPassIndex = 0;
+unsigned Technique::deferredPassIndex = 0;
+unsigned Technique::lightPassIndex = 0;
+unsigned Technique::litBasePassIndex = 0;
+unsigned Technique::litAlphaPassIndex = 0;
+unsigned Technique::shadowPassIndex = 0;
+HashMap<String, unsigned> Technique::passIndices;
+
 Technique::Technique(Context* context) :
     Resource(context),
-    isSM3_(false),
-    isDesktop_(false),
-    numPasses_(0)
+    isDesktop_(false)
 {
-    Graphics* graphics = GetSubsystem<Graphics>();
-    sm3Support_ = graphics ? graphics->GetSM3Support() : true;
-    
     #ifdef DESKTOP_GRAPHICS
     desktopSupport_ = true;
     #else
@@ -190,7 +192,6 @@ bool Technique::BeginLoad(Deserializer& source)
 {
     passes_.Clear();
 
-    numPasses_ = 0;
     SetMemoryUse(sizeof(Technique));
     
     SharedPtr<XMLFile> xml(new XMLFile(context_));
@@ -198,8 +199,6 @@ bool Technique::BeginLoad(Deserializer& source)
         return false;
     
     XMLElement rootElem = xml->GetRoot();
-    if (rootElem.HasAttribute("sm3"))
-        isSM3_ = rootElem.GetBool("sm3");
     if (rootElem.HasAttribute("desktop"))
         isDesktop_ = rootElem.GetBool("desktop");
     
@@ -221,12 +220,8 @@ bool Technique::BeginLoad(Deserializer& source)
     {
         if (passElem.HasAttribute("name"))
         {
-            StringHash nameHash(passElem.GetAttribute("name"));
-            
-            Pass* newPass = CreatePass(nameHash);
-            
-            if (passElem.HasAttribute("sm3"))
-                newPass->SetIsSM3(passElem.GetBool("sm3"));
+            Pass* newPass = CreatePass(passElem.GetAttribute("name"));
+
             if (passElem.HasAttribute("desktop"))
                 newPass->SetIsDesktop(passElem.GetBool("desktop"));
             
@@ -291,11 +286,6 @@ bool Technique::BeginLoad(Deserializer& source)
     return true;
 }
 
-void Technique::SetIsSM3(bool enable)
-{
-    isSM3_ = enable;
-}
-
 void Technique::SetIsDesktop(bool enable)
 {
     isDesktop_ = enable;
@@ -303,55 +293,128 @@ void Technique::SetIsDesktop(bool enable)
 
 void Technique::ReleaseShaders()
 {
-    PODVector<SharedPtr<Pass>*> allPasses = passes_.Values();
-    
-    for (unsigned i = 0; i < allPasses.Size(); ++i)
-        allPasses[i]->Get()->ReleaseShaders();
+    for (Vector<SharedPtr<Pass> >::ConstIterator i = passes_.Begin(); i != passes_.End(); ++i)
+    {
+        Pass* pass = i->Get();
+        if (pass)
+            pass->ReleaseShaders();
+    }
 }
 
-Pass* Technique::CreatePass(StringHash type)
+Pass* Technique::CreatePass(const String& name)
 {
-    Pass* oldPass = GetPass(type);
+    Pass* oldPass = GetPass(name);
     if (oldPass)
         return oldPass;
     
-    SharedPtr<Pass> newPass(new Pass(type));
-    passes_.Insert(type.Value(), newPass);
+    SharedPtr<Pass> newPass(new Pass(name));
+    unsigned passIndex = newPass->GetIndex();
+    if (passIndex >= passes_.Size())
+        passes_.Resize(passIndex + 1);
+    passes_[passIndex] = newPass;
     
     // Calculate memory use now
-    SetMemoryUse(sizeof(Technique) + ++numPasses_ * sizeof(Pass));
+    SetMemoryUse(sizeof(Technique) + GetNumPasses() * sizeof(Pass));
 
     return newPass;
 }
 
-void Technique::RemovePass(StringHash type)
+void Technique::RemovePass(const String& name)
+{
+    HashMap<String, unsigned>::ConstIterator i = passIndices.Find(name.ToLower());
+    if (i == passIndices.End())
+        return;
+    else if (i->second_ < passes_.Size() && passes_[i->second_].Get())
+    {
+        passes_[i->second_].Reset();
+        SetMemoryUse(sizeof(Technique) + GetNumPasses() * sizeof(Pass));
+    }
+}
+
+bool Technique::HasPass(const String& name) const
+{
+    HashMap<String, unsigned>::ConstIterator i = passIndices.Find(name.ToLower());
+    return i != passIndices.End() ? HasPass(i->second_) : false;
+}
+
+Pass* Technique::GetPass(const String& name) const
 {
-    if (passes_.Erase(type.Value()))
-        SetMemoryUse(sizeof(Technique) + --numPasses_ * sizeof(Pass));
+    HashMap<String, unsigned>::ConstIterator i = passIndices.Find(name.ToLower());
+    return i != passIndices.End() ? GetPass(i->second_) : 0;
 }
 
-Vector<StringHash> Technique::GetPassTypes() const
+Pass* Technique::GetSupportedPass(const String& name) const
 {
-    // Convert PODVector<unsigned> to Vector<StringHash>
-    PODVector<unsigned> vectorIn = passes_.Keys();
-    Vector<StringHash> vectorOut;
-    vectorOut.Reserve(vectorIn.Size());
-    for (unsigned i = 0; i < vectorIn.Size(); ++i)
-        vectorOut.Push(StringHash(vectorIn[i]));
+    HashMap<String, unsigned>::ConstIterator i = passIndices.Find(name.ToLower());
+    return i != passIndices.End() ? GetSupportedPass(i->second_) : 0;
+}
+
+unsigned Technique::GetNumPasses() const
+{
+    unsigned ret = 0;
+
+    for (Vector<SharedPtr<Pass> >::ConstIterator i = passes_.Begin(); i != passes_.End(); ++i)
+    {
+        if (i->Get())
+            ++ret;
+    }
+
+    return ret;
+}
+
+Vector<String> Technique::GetPassNames() const
+{
+    Vector<String> ret;
+
+    for (Vector<SharedPtr<Pass> >::ConstIterator i = passes_.Begin(); i != passes_.End(); ++i)
+    {
+        Pass* pass = i->Get();
+        if (pass)
+            ret.Push(pass->GetName());
+    }
 
-    return vectorOut;
+    return ret;
 }
 
 PODVector<Pass*> Technique::GetPasses() const
 {
-    // Convert PODVector<SharedPtr<Pass>*> to PODVector<Pass*>
-    PODVector<SharedPtr<Pass>*> vectorIn = passes_.Values();
-    PODVector<Pass*> vectorOut;
-    vectorOut.Reserve(vectorIn.Size());
-    for (unsigned i = 0; i < vectorIn.Size(); ++i)
-        vectorOut.Push(vectorIn[i]->Get());
+    PODVector<Pass*> ret;
+
+    for (Vector<SharedPtr<Pass> >::ConstIterator i = passes_.Begin(); i != passes_.End(); ++i)
+    {
+        Pass* pass = i->Get();
+        if (pass)
+            ret.Push(pass);
+    }
+
+    return ret;
+}
+
+unsigned Technique::GetPassIndex(const String& passName)
+{
+    // Initialize built-in pass indices on first call
+    if (passIndices.Empty())
+    {
+        basePassIndex = passIndices["base"] = 0;
+        alphaPassIndex = passIndices["alpha"] = 1;
+        materialPassIndex = passIndices["material"] = 2;
+        deferredPassIndex = passIndices["deferred"] = 3;
+        lightPassIndex = passIndices["light"] = 4;
+        litBasePassIndex = passIndices["litbase"] = 5;
+        litAlphaPassIndex = passIndices["litalpha"] = 6;
+        shadowPassIndex = passIndices["shadow"] = 7;
+    }
 
-    return vectorOut;
+    String nameLower = passName.ToLower();
+    HashMap<String, unsigned>::Iterator i = passIndices.Find(nameLower);
+    if (i != passIndices.End())
+        return i->second_;
+    else
+    {
+        unsigned newPassIndex = passIndices.Size();
+        passIndices[nameLower] = newPassIndex;
+        return newPassIndex;
+    }
 }
 
 }

+ 52 - 41
Source/Urho3D/Graphics/Technique.h

@@ -23,7 +23,6 @@
 #pragma once
 
 #include "../Graphics/GraphicsDefs.h"
-#include "../Container/HashTable.h"
 #include "../Resource/Resource.h"
 
 namespace Urho3D
@@ -44,7 +43,7 @@ class URHO3D_API Pass : public RefCounted
 {
 public:
     /// Construct.
-    Pass(StringHash type);
+    Pass(const String& passName);
     /// Destruct.
     ~Pass();
     
@@ -58,8 +57,6 @@ public:
     void SetDepthWrite(bool enable);
     /// Set alpha masking hint. Completely opaque draw calls will be performed before alpha masked.
     void SetAlphaMask(bool enable);
-    /// Set whether requires %Shader %Model 3.
-    void SetIsSM3(bool enable);
     /// Set whether requires desktop level hardware.
     void SetIsDesktop(bool enable);
     /// Set vertex shader name.
@@ -75,8 +72,10 @@ public:
     /// Mark shaders loaded this frame.
     void MarkShadersLoaded(unsigned frameNumber);
     
-    /// Return pass type.
-    const StringHash& GetType() const { return type_; }
+    /// Return pass name.
+    const String& GetName() const { return name_; }
+    /// Return pass index. This is used for optimal render-time pass queries that avoid map lookups.
+    unsigned GetIndex() const { return index_; }
     /// Return blend mode.
     BlendMode GetBlendMode() const { return blendMode_; }
     /// Return depth compare mode.
@@ -89,8 +88,6 @@ public:
     bool GetDepthWrite() const { return depthWrite_; }
     /// Return alpha masking hint.
     bool GetAlphaMask() const { return alphaMask_; }
-    /// Return whether requires %Shader %Model 3.
-    bool IsSM3() const { return isSM3_; }
     /// Return whether requires desktop level hardware.
     bool IsDesktop() const { return isDesktop_; }
     /// Return vertex shader name.
@@ -107,8 +104,8 @@ public:
     Vector<SharedPtr<ShaderVariation> >& GetPixelShaders() { return pixelShaders_; }
     
 private:
-    /// Pass type.
-    StringHash type_;
+    /// Pass index.
+    unsigned index_;
     /// Blend mode.
     BlendMode blendMode_;
     /// Depth compare mode.
@@ -121,8 +118,6 @@ private:
     bool depthWrite_;
     /// Alpha masking hint.
     bool alphaMask_;
-    /// Require %Shader %Model 3 flag.
-    bool isSM3_;
     /// Require desktop level hardware flag.
     bool isDesktop_;
     /// Vertex shader name.
@@ -137,6 +132,8 @@ private:
     Vector<SharedPtr<ShaderVariation> > vertexShaders_;
     /// Pixel shaders.
     Vector<SharedPtr<ShaderVariation> > pixelShaders_;
+    /// Pass name.
+    String name_;
 };
 
 /// %Material technique. Consists of several passes.
@@ -157,61 +154,75 @@ public:
     /// Load resource from stream. May be called from a worker thread. Return true if successful.
     virtual bool BeginLoad(Deserializer& source);
     
-    /// Set whether requires %Shader %Model 3.
-    void SetIsSM3(bool enable);
     /// Set whether requires desktop level hardware.
     void SetIsDesktop(bool enable);
     /// Create a new pass.
-    Pass* CreatePass(StringHash type);
+    Pass* CreatePass(const String& passName);
     /// Remove a pass.
-    void RemovePass(StringHash type);
+    void RemovePass(const String& passName);
     /// Reset shader pointers in all passes.
     void ReleaseShaders();
     
-    /// Return whether requires %Shader %Model 3.
-    bool IsSM3() const { return isSM3_; }
     /// Return whether requires desktop level hardware.
     bool IsDesktop() const { return isDesktop_; }
     /// Return whether technique is supported by the current hardware.
-    bool IsSupported() const { return (!isSM3_ || sm3Support_) && (!isDesktop_ || desktopSupport_); }
+    bool IsSupported() const { return !isDesktop_ || desktopSupport_; }
     /// Return whether has a pass.
-    bool HasPass(StringHash type) const { return  passes_.Find(type.Value()) != 0; }
-    
+    bool HasPass(unsigned passIndex) const { return passIndex < passes_.Size() && passes_[passIndex].Get() != 0; }
+    /// Return whether has a pass by name. This overload should not be called in time-critical rendering loops; use a pre-acquired pass index instead.
+    bool HasPass(const String& passName) const;
     /// Return a pass, or null if not found.
-    Pass* GetPass(StringHash type) const
-    {
-        SharedPtr<Pass>* passPtr = passes_.Find(type.Value());
-        return passPtr ? passPtr->Get() : 0;
-    }
+    Pass* GetPass(unsigned passIndex) const { return passIndex < passes_.Size() ? passes_[passIndex].Get() : 0; }
+    /// Return a pass by name, or null if not found. This overload should not be called in time-critical rendering loops; use a pre-acquired pass index instead.
+    Pass* GetPass(const String& passName) const;
     
     /// Return a pass that is supported for rendering, or null if not found.
-    Pass* GetSupportedPass(StringHash type) const
+    Pass* GetSupportedPass(unsigned passIndex) const
     {
-        SharedPtr<Pass>* passPtr = passes_.Find(type.Value());
-        Pass* pass = passPtr ? passPtr->Get() : 0;
-        return pass && (!pass->IsSM3() || sm3Support_) && (!pass->IsDesktop() || desktopSupport_) ? pass : 0;
+        Pass* pass = passIndex < passes_.Size() ? passes_[passIndex].Get() : 0;
+        return pass && (!pass->IsDesktop() || desktopSupport_) ? pass : 0;
     }
+
+    /// Return a supported pass by name. This overload should not be called in time-critical rendering loops; use a pre-acquired pass index instead.
+    Pass* GetSupportedPass(const String& passName) const;
     
     /// Return number of passes.
-    unsigned GetNumPasses() const { return numPasses_; }
-    /// Return all the pass types in the hash table. The returned collection is not guaranteed to be in the same order as the hash table insertion order.
-    Vector<StringHash> GetPassTypes() const;
-    /// Return all the passes in the hash table. The returned collection is not guaranteed to be in the same order as the hash table insertion order.
+    unsigned GetNumPasses() const;
+    /// Return all pass names.
+    Vector<String> GetPassNames() const;
+    /// Return all passes.
     PODVector<Pass*> GetPasses() const;
 
+    /// Return a pass type index by name. Allocate new if not used yet.
+    static unsigned GetPassIndex(const String& passName);
+
+    /// Index for base pass. Initialized once GetPassIndex() has been called for the first time.
+    static unsigned basePassIndex;
+    /// Index for alpha pass. Initialized once GetPassIndex() has been called for the first time.
+    static unsigned alphaPassIndex;
+    /// Index for prepass material pass. Initialized once GetPassIndex() has been called for the first time.
+    static unsigned materialPassIndex;
+    /// Index for deferred G-buffer pass. Initialized once GetPassIndex() has been called for the first time.
+    static unsigned deferredPassIndex;
+    /// Index for per-pixel light pass. Initialized once GetPassIndex() has been called for the first time.
+    static unsigned lightPassIndex;
+    /// Index for lit base pass. Initialized once GetPassIndex() has been called for the first time.
+    static unsigned litBasePassIndex;
+    /// Index for lit alpha pass. Initialized once GetPassIndex() has been called for the first time.
+    static unsigned litAlphaPassIndex;
+    /// Index for shadow pass. Initialized once GetPassIndex() has been called for the first time.
+    static unsigned shadowPassIndex;
+
 private:
-    /// Require %Shader %Model 3 flag.
-    bool isSM3_;
-    /// Cached %Shader %Model 3 support flag.
-    bool sm3Support_;
     /// Require desktop GPU flag.
     bool isDesktop_;
     /// Cached desktop GPU support flag.
     bool desktopSupport_;
     /// Passes.
-    HashTable<SharedPtr<Pass>, 16> passes_;
-    /// Number of passes.
-    unsigned numPasses_;
+    Vector<SharedPtr<Pass> > passes_;
+
+    /// Pass index assignments.
+    static HashMap<String, unsigned> passIndices;
 };
 
 }

+ 3 - 1
Source/Urho3D/Graphics/Texture.h

@@ -22,8 +22,10 @@
 
 #pragma once
 
-#ifdef URHO3D_OPENGL
+#if defined(URHO3D_OPENGL)
 #include "OpenGL/OGLTexture.h"
+#elif defined(URHO3D_D3D11)
+#include "Direct3D11/D3D11Texture.h"
 #else
 #include "Direct3D9/D3D9Texture.h"
 #endif

+ 4 - 2
Source/Urho3D/Graphics/Texture2D.h

@@ -22,8 +22,10 @@
 
 #pragma once
 
-#ifdef URHO3D_OPENGL
+#if defined(URHO3D_OPENGL)
 #include "OpenGL/OGLTexture2D.h"
+#elif defined(URHO3D_D3D11)
+#include "Direct3D11/D3D11Texture2D.h"
 #else
 #include "Direct3D9/D3D9Texture2D.h"
-#endif
+#endif

+ 3 - 1
Source/Urho3D/Graphics/Texture3D.h

@@ -22,8 +22,10 @@
 
 #pragma once
 
-#ifdef URHO3D_OPENGL
+#if defined(URHO3D_OPENGL)
 #include "OpenGL/OGLTexture3D.h"
+#elif defined(URHO3D_D3D11)
+#include "Direct3D11/D3D11Texture3D.h"
 #else
 #include "Direct3D9/D3D9Texture3D.h"
 #endif

+ 4 - 2
Source/Urho3D/Graphics/TextureCube.h

@@ -22,8 +22,10 @@
 
 #pragma once
 
-#ifdef URHO3D_OPENGL
+#if defined(URHO3D_OPENGL)
 #include "OpenGL/OGLTextureCube.h"
+#elif defined(URHO3D_D3D11)
+#include "Direct3D11/D3D11TextureCube.h"
 #else
 #include "Direct3D9/D3D9TextureCube.h"
-#endif
+#endif

+ 3 - 1
Source/Urho3D/Graphics/VertexBuffer.h

@@ -22,8 +22,10 @@
 
 #pragma once
 
-#ifdef URHO3D_OPENGL
+#if defined(URHO3D_OPENGL)
 #include "OpenGL/OGLVertexBuffer.h"
+#elif defined(URHO3D_D3D11)
+#include "Direct3D11/D3D11VertexBuffer.h"
 #else
 #include "Direct3D9/D3D9VertexBuffer.h"
 #endif

+ 3 - 1
Source/Urho3D/Graphics/VertexDeclaration.h

@@ -22,6 +22,8 @@
 
 #pragma once
 
-#ifndef URHO3D_OPENGL
+#if defined(URHO3D_D3D11)
+#include "Direct3D11/D3D11VertexDeclaration.h"
+#else
 #include "Direct3D9/D3D9VertexDeclaration.h"
 #endif

+ 222 - 254
Source/Urho3D/Graphics/View.cpp

@@ -180,22 +180,18 @@ void CheckVisibilityWork(const WorkItem* item, unsigned threadIndex)
     while (start != end)
     {
         Drawable* drawable = *start++;
-        bool batchesUpdated = false;
-        
-        // If draw distance non-zero, update and check it
-        float maxDistance = drawable->GetDrawDistance();
-        if (maxDistance > 0.0f)
-        {
-            drawable->UpdateBatches(view->frame_);
-            batchesUpdated = true;
-            if (drawable->GetDistance() > maxDistance)
-                continue;
-        }
-        
+
         if (!buffer || !drawable->IsOccludee() || buffer->IsVisible(drawable->GetWorldBoundingBox()))
         {
-            if (!batchesUpdated)
-                drawable->UpdateBatches(view->frame_);
+            drawable->UpdateBatches(view->frame_);
+            // If draw distance non-zero, update and check it
+            float maxDistance = drawable->GetDrawDistance();
+            if (maxDistance > 0.0f)
+            {
+                if (drawable->GetDistance() > maxDistance)
+                    continue;
+            }
+
             drawable->MarkInView(view->frame_);
             
             // For geometries, find zone, clear lights and calculate view space Z range
@@ -208,22 +204,22 @@ void CheckVisibilityWork(const WorkItem* item, unsigned threadIndex)
                 
                 const BoundingBox& geomBox = drawable->GetWorldBoundingBox();
                 Vector3 center = geomBox.Center();
-                float viewCenterZ = viewZ.DotProduct(center) + viewMatrix.m23_;
                 Vector3 edge = geomBox.Size() * 0.5f;
-                float viewEdgeZ = absViewZ.DotProduct(edge);
-                float minZ = viewCenterZ - viewEdgeZ;
-                float maxZ = viewCenterZ + viewEdgeZ;
-                
-                drawable->SetMinMaxZ(viewCenterZ - viewEdgeZ, viewCenterZ + viewEdgeZ);
-                drawable->ClearLights();
-                
-                // Expand the scene bounding box and Z range (skybox not included because of infinite size) and store the drawawble
-                if (drawable->GetType() != Skybox::GetTypeStatic())
+
+                // Do not add "infinite" objects like skybox to prevent shadow map focusing behaving erroneously
+                if (edge.LengthSquared() < M_LARGE_VALUE * M_LARGE_VALUE)
                 {
+                    float viewCenterZ = viewZ.DotProduct(center) + viewMatrix.m23_;
+                    float viewEdgeZ = absViewZ.DotProduct(edge);
+                    float minZ = viewCenterZ - viewEdgeZ;
+                    float maxZ = viewCenterZ + viewEdgeZ;
+                    drawable->SetMinMaxZ(viewCenterZ - viewEdgeZ, viewCenterZ + viewEdgeZ);
                     result.minZ_ = Min(result.minZ_, minZ);
                     result.maxZ_ = Max(result.maxZ_, maxZ);
                 }
-                
+                else
+                    drawable->SetMinMaxZ(M_LARGE_VALUE, M_LARGE_VALUE);
+
                 result.geometries_.Push(drawable);
             }
             else if (drawable->GetDrawableFlags() & DRAWABLE_LIGHT)
@@ -317,6 +313,14 @@ bool View::Define(RenderSurface* renderTarget, Viewport* viewport)
     if (!renderPath_)
         return false;
     
+    // Set default passes
+    gBufferPassIndex_ = M_MAX_UNSIGNED;
+    basePassIndex_ = Technique::GetPassIndex("base");
+    alphaPassIndex_ = Technique::GetPassIndex("alpha");
+    lightPassIndex_ = Technique::GetPassIndex("light");
+    litBasePassIndex_ = Technique::GetPassIndex("litbase");
+    litAlphaPassIndex_ = Technique::GetPassIndex("litalpha");
+
     drawDebug_ = viewport->GetDrawDebug();
     hasScenePasses_ = false;
     lightVolumeCommand_ = 0;
@@ -349,7 +353,7 @@ bool View::Define(RenderSurface* renderTarget, Viewport* viewport)
 
     for (unsigned i = 0; i < renderPath_->commands_.Size(); ++i)
     {
-        const RenderPathCommand& command = renderPath_->commands_[i];
+        RenderPathCommand& command = renderPath_->commands_[i];
         if (!command.enabled_)
             continue;
         
@@ -358,7 +362,7 @@ bool View::Define(RenderSurface* renderTarget, Viewport* viewport)
             hasScenePasses_ = true;
             
             ScenePassInfo info;
-            info.pass_ = command.pass_;
+            info.passIndex_ = command.passIndex_ = Technique::GetPassIndex(command.pass_);
             info.allowInstancing_ = command.sortMode_ != SORT_BACKTOFRONT;
             info.markToStencil_ = !noStencil_ && command.markToStencil_;
             info.vertexLights_ = command.vertexLights_;
@@ -367,32 +371,31 @@ bool View::Define(RenderSurface* renderTarget, Viewport* viewport)
             if (!command.metadata_.Empty())
             {
                 if (command.metadata_ == "gbuffer")
-                    gBufferPassName_ = command.pass_;
+                    gBufferPassIndex_ = command.passIndex_;
                 else if (command.metadata_ == "base" && command.pass_ != "base")
                 {
-                    basePassName_ = command.pass_;
-                    litBasePassName_ = "lit" + command.pass_;
+                    basePassIndex_ = command.passIndex_;
+                    litBasePassIndex_ = Technique::GetPassIndex("lit" + command.pass_);
                 }
                 else if (command.metadata_ == "alpha" && command.pass_ != "alpha")
                 {
-                    alphaPassName_ = command.pass_;
-                    litAlphaPassName_ = "lit" + command.pass_;
+                    alphaPassIndex_ = command.passIndex_;
+                    litAlphaPassIndex_ = Technique::GetPassIndex("lit" + command.pass_);
                 }
             }
             
-            HashMap<StringHash, BatchQueue>::Iterator j = batchQueues_.Find(command.pass_);
+            HashMap<unsigned, BatchQueue>::Iterator j = batchQueues_.Find(info.passIndex_);
             if (j == batchQueues_.End())
-                j = batchQueues_.Insert(Pair<StringHash, BatchQueue>(command.pass_, BatchQueue()));
+                j = batchQueues_.Insert(Pair<unsigned, BatchQueue>(info.passIndex_, BatchQueue()));
             info.batchQueue_ = &j->second_;
             
             scenePasses_.Push(info);
         }
         // Allow a custom forward light pass
         else if (command.type_ == CMD_FORWARDLIGHTS && !command.pass_.Empty())
-            lightPassName_ = command.pass_;
+            lightPassIndex_ = command.passIndex_ = Technique::GetPassIndex(command.pass_);
     }
     
-    
     scene_ = viewport->GetScene();
     camera_ = viewport->GetCamera();
     octree_ = 0;
@@ -421,13 +424,6 @@ bool View::Define(RenderSurface* renderTarget, Viewport* viewport)
     cameraNode_ = camera_ ? camera_->GetNode() : (Node*)0;
     renderTarget_ = renderTarget;
     
-    gBufferPassName_ = StringHash();
-    basePassName_  = PASS_BASE;
-    alphaPassName_ = PASS_ALPHA;
-    lightPassName_ = PASS_LIGHT;
-    litBasePassName_ = PASS_LITBASE;
-    litAlphaPassName_ = PASS_LITALPHA;
-    
     // Go through commands to check for deferred rendering and other flags
     deferred_ = false;
     deferredAmbient_ = false;
@@ -528,7 +524,7 @@ void View::Update(const FrameInfo& frame)
     zones_.Clear();
     occluders_.Clear();
     vertexLightQueues_.Clear();
-    for (HashMap<StringHash, BatchQueue>::Iterator i = batchQueues_.Begin(); i != batchQueues_.End(); ++i)
+    for (HashMap<unsigned, BatchQueue>::Iterator i = batchQueues_.Begin(); i != batchQueues_.End(); ++i)
         i->second_.Clear(maxSortedInstances);
     
     if (hasScenePasses_ && (!camera_ || !octree_))
@@ -561,9 +557,7 @@ void View::Render()
     // Forget parameter sources from the previous view
     graphics_->ClearParameterSources();
     
-    // If stream offset is supported, write all instance transforms to a single large buffer
-    // Else we must lock the instance buffer for each batch group
-    if (renderer_->GetDynamicInstancing() && graphics_->GetStreamOffsetSupport())
+    if (renderer_->GetDynamicInstancing() && graphics_->GetInstancingSupport())
         PrepareInstancingBuffer();
     
     // It is possible, though not recommended, that the same camera is used for multiple main views. Set automatic aspect ratio
@@ -613,7 +607,6 @@ void View::Render()
     graphics_->SetDepthBias(0.0f, 0.0f);
     graphics_->SetScissorTest(false);
     graphics_->SetStencilTest(false);
-    graphics_->ResetStreamFrequencies();
     
     // Draw the associated debug geometry now if enabled
     if (drawDebug_ && octree_ && camera_)
@@ -735,8 +728,9 @@ void View::SetGBufferShaderParameters(const IntVector2& texSize, const IntRect&
     Vector4 bufferUVOffset(((float)viewRect.left_) / texWidth + widthRange,
         1.0f - (((float)viewRect.top_) / texHeight + heightRange), widthRange, heightRange);
     #else
-    Vector4 bufferUVOffset((0.5f + (float)viewRect.left_) / texWidth + widthRange,
-        (0.5f + (float)viewRect.top_) / texHeight + heightRange, widthRange, heightRange);
+    const Vector2& pixelUVOffset = Graphics::GetPixelUVOffset();
+    Vector4 bufferUVOffset((pixelUVOffset.x_ + (float)viewRect.left_) / texWidth + widthRange,
+        (pixelUVOffset.y_ + (float)viewRect.top_) / texHeight + heightRange, widthRange, heightRange);
     #endif
     graphics_->SetShaderParameter(VSP_GBUFFEROFFSETS, bufferUVOffset);
     
@@ -922,33 +916,40 @@ void View::GetBatches()
     nonThreadedGeometries_.Clear();
     threadedGeometries_.Clear();
     
-    WorkQueue* queue = GetSubsystem<WorkQueue>();
-    PODVector<Light*> vertexLights;
-    BatchQueue* alphaQueue = batchQueues_.Contains(alphaPassName_) ? &batchQueues_[alphaPassName_] : (BatchQueue*)0;
-    
+    ProcessLights();
+    GetLightBatches();
+    GetBaseBatches();
+}
+
+void View::ProcessLights()
+{
     // Process lit geometries and shadow casters for each light
+    PROFILE(ProcessLights);
+
+    WorkQueue* queue = GetSubsystem<WorkQueue>();
+    lightQueryResults_.Resize(lights_.Size());
+
+    for (unsigned i = 0; i < lightQueryResults_.Size(); ++i)
     {
-        PROFILE(ProcessLights);
-        
-        lightQueryResults_.Resize(lights_.Size());
-        
-        for (unsigned i = 0; i < lightQueryResults_.Size(); ++i)
-        {
-            SharedPtr<WorkItem> item = queue->GetFreeItem();
-            item->priority_ = M_MAX_UNSIGNED;
-            item->workFunction_ = ProcessLightWork;
-            item->aux_ = this;
+        SharedPtr<WorkItem> item = queue->GetFreeItem();
+        item->priority_ = M_MAX_UNSIGNED;
+        item->workFunction_ = ProcessLightWork;
+        item->aux_ = this;
 
-            LightQueryResult& query = lightQueryResults_[i];
-            query.light_ = lights_[i];
-            
-            item->start_ = &query;
-            queue->AddWorkItem(item);
-        }
-        
-        // Ensure all lights have been processed before proceeding
-        queue->Complete(M_MAX_UNSIGNED);
+        LightQueryResult& query = lightQueryResults_[i];
+        query.light_ = lights_[i];
+
+        item->start_ = &query;
+        queue->AddWorkItem(item);
     }
+
+    // Ensure all lights have been processed before proceeding
+    queue->Complete(M_MAX_UNSIGNED);
+}
+
+void View::GetLightBatches()
+{
+    BatchQueue* alphaQueue = batchQueues_.Contains(alphaPassIndex_) ? &batchQueues_[alphaPassIndex_] : (BatchQueue*)0;
     
     // Build light queues and lit batches
     {
@@ -986,6 +987,7 @@ void View::GetBatches()
                 LightBatchQueue& lightQueue = lightQueues_[usedLightQueues++];
                 light->SetLightQueue(&lightQueue);
                 lightQueue.light_ = light;
+                lightQueue.negative_ = light->IsNegative();
                 lightQueue.shadowMap_ = 0;
                 lightQueue.litBaseBatches_.Clear(maxSortedInstances);
                 lightQueue.litBatches_.Clear(maxSortedInstances);
@@ -1023,7 +1025,7 @@ void View::GetBatches()
                         // If drawable is not in actual view frustum, mark it in view here and check its geometry update type
                         if (!drawable->IsInView(frame_, true))
                         {
-                            drawable->MarkInView(frame_.frameNumber_, 0);
+                            drawable->MarkInView(frame_.frameNumber_);
                             UpdateGeometryType type = drawable->GetUpdateGeometryType();
                             if (type == UPDATE_MAIN_THREAD)
                                 nonThreadedGeometries_.Push(drawable);
@@ -1042,7 +1044,7 @@ void View::GetBatches()
                             if (!srcBatch.geometry_ || !srcBatch.numWorldTransforms_ || !tech)
                                 continue;
                             
-                            Pass* pass = tech->GetSupportedPass(PASS_SHADOW);
+                            Pass* pass = tech->GetSupportedPass(Technique::shadowPassIndex);
                             // Skip if material has no shadow pass
                             if (!pass)
                                 continue;
@@ -1051,7 +1053,6 @@ void View::GetBatches()
                             destBatch.pass_ = pass;
                             destBatch.camera_ = shadowCamera;
                             destBatch.zone_ = zone;
-                            destBatch.lightQueue_ = &lightQueue;
                             
                             AddBatchToQueue(shadowQueue.shadowBatches_, destBatch, tech);
                         }
@@ -1125,100 +1126,92 @@ void View::GetBatches()
             }
         }
     }
+}
+
+void View::GetBaseBatches()
+{
+    PROFILE(GetBaseBatches);
     
-    // Build base pass batches and find out the geometry update queue (threaded or nonthreaded) drawables should end up to
+    for (PODVector<Drawable*>::ConstIterator i = geometries_.Begin(); i != geometries_.End(); ++i)
     {
-        PROFILE(GetBaseBatches);
+        Drawable* drawable = *i;
+        UpdateGeometryType type = drawable->GetUpdateGeometryType();
+        if (type == UPDATE_MAIN_THREAD)
+            nonThreadedGeometries_.Push(drawable);
+        else if (type == UPDATE_WORKER_THREAD)
+            threadedGeometries_.Push(drawable);
         
-        for (PODVector<Drawable*>::ConstIterator i = geometries_.Begin(); i != geometries_.End(); ++i)
+        const Vector<SourceBatch>& batches = drawable->GetBatches();
+        bool vertexLightsProcessed = false;
+
+        for (unsigned j = 0; j < batches.Size(); ++j)
         {
-            Drawable* drawable = *i;
-            UpdateGeometryType type = drawable->GetUpdateGeometryType();
-            if (type == UPDATE_MAIN_THREAD)
-                nonThreadedGeometries_.Push(drawable);
-            else if (type == UPDATE_WORKER_THREAD)
-                threadedGeometries_.Push(drawable);
+            const SourceBatch& srcBatch = batches[j];
             
-            Zone* zone = GetZone(drawable);
-            const Vector<SourceBatch>& batches = drawable->GetBatches();
+            // Check here if the material refers to a rendertarget texture with camera(s) attached
+            // Only check this for backbuffer views (null rendertarget)
+            if (srcBatch.material_ && srcBatch.material_->GetAuxViewFrameNumber() != frame_.frameNumber_ && !renderTarget_)
+                CheckMaterialForAuxView(srcBatch.material_);
             
-            const PODVector<Light*>& drawableVertexLights = drawable->GetVertexLights();
-            if (!drawableVertexLights.Empty())
-                drawable->LimitVertexLights();
+            Technique* tech = GetTechnique(drawable, srcBatch.material_);
+            if (!srcBatch.geometry_ || !srcBatch.numWorldTransforms_ || !tech)
+                continue;
             
-            for (unsigned j = 0; j < batches.Size(); ++j)
+            // Check each of the scene passes
+            for (unsigned k = 0; k < scenePasses_.Size(); ++k)
             {
-                const SourceBatch& srcBatch = batches[j];
-                
-                // Check here if the material refers to a rendertarget texture with camera(s) attached
-                // Only check this for backbuffer views (null rendertarget)
-                if (srcBatch.material_ && srcBatch.material_->GetAuxViewFrameNumber() != frame_.frameNumber_ && !renderTarget_)
-                    CheckMaterialForAuxView(srcBatch.material_);
-                
-                Technique* tech = GetTechnique(drawable, srcBatch.material_);
-                if (!srcBatch.geometry_ || !srcBatch.numWorldTransforms_ || !tech)
+                ScenePassInfo& info = scenePasses_[k];
+                // Skip forward base pass if the corresponding litbase pass already exists
+                if (info.passIndex_ == basePassIndex_ && j < 32 && drawable->HasBasePass(j))
+                    continue;
+
+                Pass* pass = tech->GetSupportedPass(info.passIndex_);
+                if (!pass)
                     continue;
                 
                 Batch destBatch(srcBatch);
+                destBatch.pass_ = pass;
                 destBatch.camera_ = camera_;
-                destBatch.zone_ = zone;
+                destBatch.zone_ = GetZone(drawable);
                 destBatch.isBase_ = true;
-                destBatch.pass_ = 0;
                 destBatch.lightMask_ = GetLightMask(drawable);
-                
-                // Check each of the scene passes
-                for (unsigned k = 0; k < scenePasses_.Size(); ++k)
+
+                if (info.vertexLights_)
                 {
-                    ScenePassInfo& info = scenePasses_[k];
-                    destBatch.pass_ = tech->GetSupportedPass(info.pass_);
-                    if (!destBatch.pass_)
-                        continue;
-                    
-                    // Skip forward base pass if the corresponding litbase pass already exists
-                    if (info.pass_ == basePassName_ && j < 32 && drawable->HasBasePass(j))
-                        continue;
-                    
-                    if (info.vertexLights_ && !drawableVertexLights.Empty())
+                    const PODVector<Light*>& drawableVertexLights = drawable->GetVertexLights();
+                    if (drawableVertexLights.Size() && !vertexLightsProcessed)
+                    {
+                        // Limit vertex lights. If this is a deferred opaque batch, remove converted per-pixel lights,
+                        // as they will be rendered as light volumes in any case, and drawing them also as vertex lights
+                        // would result in double lighting
+                        drawable->LimitVertexLights(deferred_ && destBatch.pass_->GetBlendMode() == BLEND_REPLACE);
+                        vertexLightsProcessed = true;
+                    }
+
+                    if (drawableVertexLights.Size())
                     {
-                        // For a deferred opaque batch, check if the vertex lights include converted per-pixel lights, and remove
-                        // them to prevent double-lighting
-                        if (deferred_ && destBatch.pass_->GetBlendMode() == BLEND_REPLACE)
+                        // Find a vertex light queue. If not found, create new
+                        unsigned long long hash = GetVertexLightQueueHash(drawableVertexLights);
+                        HashMap<unsigned long long, LightBatchQueue>::Iterator i = vertexLightQueues_.Find(hash);
+                        if (i == vertexLightQueues_.End())
                         {
-                            vertexLights.Clear();
-                            for (unsigned i = 0; i < drawableVertexLights.Size(); ++i)
-                            {
-                                if (drawableVertexLights[i]->GetPerVertex())
-                                    vertexLights.Push(drawableVertexLights[i]);
-                            }
+                            i = vertexLightQueues_.Insert(MakePair(hash, LightBatchQueue()));
+                            i->second_.light_ = 0;
+                            i->second_.shadowMap_ = 0;
+                            i->second_.vertexLights_ = drawableVertexLights;
                         }
-                        else
-                            vertexLights = drawableVertexLights;
                         
-                        if (!vertexLights.Empty())
-                        {
-                            // Find a vertex light queue. If not found, create new
-                            unsigned long long hash = GetVertexLightQueueHash(vertexLights);
-                            HashMap<unsigned long long, LightBatchQueue>::Iterator i = vertexLightQueues_.Find(hash);
-                            if (i == vertexLightQueues_.End())
-                            {
-                                i = vertexLightQueues_.Insert(MakePair(hash, LightBatchQueue()));
-                                i->second_.light_ = 0;
-                                i->second_.shadowMap_ = 0;
-                                i->second_.vertexLights_ = vertexLights;
-                            }
-                            
-                            destBatch.lightQueue_ = &(i->second_);
-                        }
+                        destBatch.lightQueue_ = &(i->second_);
                     }
-                    else
-                        destBatch.lightQueue_ = 0;
-                    
-                    bool allowInstancing = info.allowInstancing_;
-                    if (allowInstancing && info.markToStencil_ && destBatch.lightMask_ != (zone->GetLightMask() & 0xff))
-                        allowInstancing = false;
-                    
-                    AddBatchToQueue(*info.batchQueue_, destBatch, tech, allowInstancing);
                 }
+                else
+                    destBatch.lightQueue_ = 0;
+                
+                bool allowInstancing = info.allowInstancing_;
+                if (allowInstancing && info.markToStencil_ && destBatch.lightMask_ != (destBatch.zone_->GetLightMask() & 0xff))
+                    allowInstancing = false;
+                
+                AddBatchToQueue(*info.batchQueue_, destBatch, tech, allowInstancing);
             }
         }
     }
@@ -1243,7 +1236,7 @@ void View::UpdateGeometries()
                 SharedPtr<WorkItem> item = queue->GetFreeItem();
                 item->priority_ = M_MAX_UNSIGNED;
                 item->workFunction_ = command.sortMode_ == SORT_FRONTTOBACK ? SortBatchQueueFrontToBackWork : SortBatchQueueBackToFrontWork;
-                item->start_ = &batchQueues_[command.pass_];
+                item->start_ = &batchQueues_[command.passIndex_];
                 queue->AddWorkItem(item);
             }
         }
@@ -1320,11 +1313,8 @@ void View::GetLitBatches(Drawable* drawable, LightBatchQueue& lightQueue, BatchQ
     Zone* zone = GetZone(drawable);
     const Vector<SourceBatch>& batches = drawable->GetBatches();
     
-    bool hasAmbientGradient = zone->GetAmbientGradient() && zone->GetAmbientStartColor() != zone->GetAmbientEndColor();
-    // Shadows on transparencies can only be rendered if shadow maps are not reused
-    bool allowTransparentShadows = !renderer_->GetReuseShadowMaps();
-    bool allowLitBase = useLitBase_ && !light->IsNegative() && light == drawable->GetFirstLight() &&
-        drawable->GetVertexLights().Empty() && !hasAmbientGradient;
+    bool allowLitBase = useLitBase_ && !lightQueue.negative_ && light == drawable->GetFirstLight() &&
+        drawable->GetVertexLights().Empty() && !zone->GetAmbientGradient();
     
     for (unsigned i = 0; i < batches.Size(); ++i)
     {
@@ -1335,7 +1325,7 @@ void View::GetLitBatches(Drawable* drawable, LightBatchQueue& lightQueue, BatchQ
             continue;
         
         // Do not create pixel lit forward passes for materials that render into the G-buffer
-        if (gBufferPassName_.Value() && tech->HasPass(gBufferPassName_))
+        if (gBufferPassIndex_ != M_MAX_UNSIGNED && tech->HasPass(gBufferPassIndex_))
             continue;
         
         Batch destBatch(srcBatch);
@@ -1345,22 +1335,22 @@ void View::GetLitBatches(Drawable* drawable, LightBatchQueue& lightQueue, BatchQ
         // Also vertex lighting or ambient gradient require the non-lit base pass, so skip in those cases
         if (i < 32 && allowLitBase)
         {
-            destBatch.pass_ = tech->GetSupportedPass(litBasePassName_);
+            destBatch.pass_ = tech->GetSupportedPass(litBasePassIndex_);
             if (destBatch.pass_)
             {
                 destBatch.isBase_ = true;
                 drawable->SetBasePass(i);
             }
             else
-                destBatch.pass_ = tech->GetSupportedPass(lightPassName_);
+                destBatch.pass_ = tech->GetSupportedPass(lightPassIndex_);
         }
         else
-            destBatch.pass_ = tech->GetSupportedPass(lightPassName_);
+            destBatch.pass_ = tech->GetSupportedPass(lightPassIndex_);
         
         // If no lit pass, check for lit alpha
         if (!destBatch.pass_)
         {
-            destBatch.pass_ = tech->GetSupportedPass(litAlphaPassName_);
+            destBatch.pass_ = tech->GetSupportedPass(litAlphaPassIndex_);
             isLitAlpha = true;
         }
         
@@ -1381,8 +1371,9 @@ void View::GetLitBatches(Drawable* drawable, LightBatchQueue& lightQueue, BatchQ
         }
         else if (alphaQueue)
         {
-            // Transparent batches can not be instanced
-            AddBatchToQueue(*alphaQueue, destBatch, tech, false, allowTransparentShadows);
+            // Transparent batches can not be instanced, and shadows on transparencies can only be rendered if shadow maps are
+            // not reused
+            AddBatchToQueue(*alphaQueue, destBatch, tech, false, !renderer_->GetReuseShadowMaps());
         }
     }
 }
@@ -1512,16 +1503,18 @@ void View::ExecuteRenderPathCommands()
                 break;
                 
             case CMD_SCENEPASS:
-                if (!batchQueues_[command.pass_].IsEmpty())
                 {
-                    PROFILE(RenderScenePass);
-                    
-                    SetRenderTargets(command);
-                    bool allowDepthWrite = SetTextures(command);
-                    graphics_->SetDrawAntialiased(true);
-                    graphics_->SetFillMode(camera_->GetFillMode());
-                    graphics_->SetClipPlane(camera_->GetUseClipping(), camera_->GetClipPlane(), camera_->GetView(), camera_->GetProjection());
-                    batchQueues_[command.pass_].Draw(this, command.markToStencil_, false, allowDepthWrite);
+                    BatchQueue& queue = batchQueues_[command.passIndex_];
+                    if (!queue.IsEmpty())
+                    {
+                        PROFILE(RenderScenePass);
+                        
+                        SetRenderTargets(command);
+                        bool allowDepthWrite = SetTextures(command);
+                        graphics_->SetFillMode(camera_->GetFillMode());
+                        graphics_->SetClipPlane(camera_->GetUseClipping(), camera_->GetClipPlane(), camera_->GetView(), camera_->GetProjection());
+                        queue.Draw(this, command.markToStencil_, false, allowDepthWrite);
+                    }
                 }
                 break;
                 
@@ -1553,7 +1546,6 @@ void View::ExecuteRenderPathCommands()
                         }
 
                         bool allowDepthWrite = SetTextures(command);
-                        graphics_->SetDrawAntialiased(true);
                         graphics_->SetFillMode(camera_->GetFillMode());
                         graphics_->SetClipPlane(camera_->GetUseClipping(), camera_->GetClipPlane(), camera_->GetView(), camera_->GetProjection());
                         
@@ -1730,9 +1722,11 @@ bool View::SetTextures(RenderPathCommand& command)
         if (GetExtension(command.textureNames_[i]) == ".xml")
         {
             // Assume 3D textures are only bound to the volume map unit, otherwise it's a cube texture
+            #ifdef DESKTOP_GRAPHICS
             if (i == TU_VOLUMEMAP)
                 texture = cache->GetResource<Texture3D>(command.textureNames_[i]);
             else
+            #endif
                 texture = cache->GetResource<TextureCube>(command.textureNames_[i]);
         }
         else
@@ -1794,18 +1788,14 @@ void View::RenderQuad(RenderPathCommand& command)
         float width = (float)renderTargets_[nameHash]->GetWidth();
         float height = (float)renderTargets_[nameHash]->GetHeight();
         
+        const Vector2& pixelUVOffset = Graphics::GetPixelUVOffset();
         graphics_->SetShaderParameter(invSizeName, Vector2(1.0f / width, 1.0f / height));
-        #ifdef URHO3D_OPENGL
-        graphics_->SetShaderParameter(offsetsName, Vector2::ZERO);
-        #else
-        graphics_->SetShaderParameter(offsetsName, Vector2(0.5f / width, 0.5f / height));
-        #endif
+        graphics_->SetShaderParameter(offsetsName, Vector2(pixelUVOffset.x_ / width, pixelUVOffset.y_ / height));
     }
     
     graphics_->SetBlendMode(BLEND_REPLACE);
     graphics_->SetDepthTest(CMP_ALWAYS);
     graphics_->SetDepthWrite(false);
-    graphics_->SetDrawAntialiased(false);
     graphics_->SetFillMode(FILL_SOLID);
     graphics_->SetClipPlane(false);
     graphics_->SetScissorTest(false);
@@ -1817,7 +1807,7 @@ void View::RenderQuad(RenderPathCommand& command)
 bool View::IsNecessary(const RenderPathCommand& command)
 {
     return command.enabled_ && command.outputNames_.Size() && (command.type_ != CMD_SCENEPASS ||
-        !batchQueues_[command.pass_].IsEmpty());
+        !batchQueues_[command.passIndex_].IsEmpty());
 }
 
 bool View::CheckViewportRead(const RenderPathCommand& command)
@@ -1875,9 +1865,10 @@ void View::AllocateScreenBuffers()
 
     #ifdef URHO3D_OPENGL
     // Due to FBO limitations, in OpenGL deferred modes need to render to texture first and then blit to the backbuffer
-    // Also, if rendering to a texture with full deferred rendering, it must be RGBA to comply with the rest of the buffers.
-    if ((deferred_ && !renderTarget_) || (deferredAmbient_ && renderTarget_ && renderTarget_->GetParentTexture()->GetFormat() !=
-        Graphics::GetRGBAFormat()))
+    // Also, if rendering to a texture with full deferred rendering, it must be RGBA to comply with the rest of the buffers,
+    // unless using OpenGL 3
+    if ((deferred_ && !renderTarget_) || (!Graphics::GetGL3Support() && deferredAmbient_ && renderTarget_ && 
+        renderTarget_->GetParentTexture()->GetFormat() != Graphics::GetRGBAFormat()))
         needSubstitute = true;
     // Also need substitute if rendering to backbuffer using a custom (readable) depth buffer
     if (!renderTarget_ && !needSubstitute)
@@ -1944,7 +1935,8 @@ void View::AllocateScreenBuffers()
     }
     
     #ifdef URHO3D_OPENGL
-    if (deferred_ && !renderer_->GetHDRRendering())
+    // On OpenGL 2 ensure that all MRT buffers are RGBA in deferred rendering
+    if (deferred_ && !renderer_->GetHDRRendering() && !Graphics::GetGL3Support())
         format = Graphics::GetRGBAFormat();
     #endif
     
@@ -2315,36 +2307,24 @@ void View::ProcessShadowCasters(LightQueryResult& query, const PODVector<Drawabl
             continue;
         
         // Check shadow distance
+        // Note: as lights are processed threaded, it is possible a drawable's UpdateBatches() function is called several
+        // times. However, this should not cause problems as no scene modification happens at this point.
+        if (!drawable->IsInView(frame_, true))
+            drawable->UpdateBatches(frame_);
         float maxShadowDistance = drawable->GetShadowDistance();
         float drawDistance = drawable->GetDrawDistance();
-        bool batchesUpdated = drawable->IsInView(frame_, true);
         if (drawDistance > 0.0f && (maxShadowDistance <= 0.0f || drawDistance < maxShadowDistance))
             maxShadowDistance = drawDistance;
-        if (maxShadowDistance > 0.0f)
-        {
-            if (!batchesUpdated)
-            {
-                drawable->UpdateBatches(frame_);
-                batchesUpdated = true;
-            }
-            if (drawable->GetDistance() > maxShadowDistance)
-                continue;
-        }
-        
-        // Note: as lights are processed threaded, it is possible a drawable's UpdateBatches() function is called several
-        // times. However, this should not cause problems as no scene modification happens at this point.
-        if (!batchesUpdated)
-            drawable->UpdateBatches(frame_);
+        if (maxShadowDistance > 0.0f && drawable->GetDistance() > maxShadowDistance)
+            continue;
 
         // Project shadow caster bounding box to light view space for visibility check
         lightViewBox = drawable->GetWorldBoundingBox().Transformed(lightView);
         
         if (IsShadowCasterVisible(drawable, lightViewBox, shadowCamera, lightView, lightViewFrustum, lightViewFrustumBox))
         {
-            // Merge to shadow caster bounding box and add to the list
-            if (type == LIGHT_DIRECTIONAL)
-                query.shadowCasterBox_[splitIndex].Merge(lightViewBox);
-            else
+            // Merge to shadow caster bounding box (only needed for focused spot lights) and add to the list
+            if (type == LIGHT_SPOT && light->GetShadowFocus().focus_)
             {
                 lightProjBox = lightViewBox.Projected(lightProj);
                 query.shadowCasterBox_[splitIndex].Merge(lightProjBox);
@@ -2533,11 +2513,6 @@ void View::SetupDirLightShadowCamera(Camera* shadowCamera, Light* light, float n
         for (unsigned i = 0; i < geometries_.Size(); ++i)
         {
             Drawable* drawable = geometries_[i];
-            
-            // Skip skyboxes as they have undefinedly large bounding box size
-            if (drawable->GetType() == Skybox::GetTypeStatic())
-                continue;
-            
             if (drawable->GetMinZ() <= farSplit && drawable->GetMaxZ() >= nearSplit &&
                 (GetLightMask(drawable) & light->GetLightMask()))
                 litGeometriesBox.Merge(drawable->GetWorldBoundingBox());
@@ -2590,22 +2565,19 @@ void View::FinalizeShadowCamera(Camera* shadowCamera, Light* light, const IntRec
         QuantizeDirLightShadowCamera(shadowCamera, light, shadowViewport, shadowBox);
     }
     
-    if (type == LIGHT_SPOT)
+    if (type == LIGHT_SPOT && parameters.focus_)
     {
-        if (parameters.focus_)
-        {
-            float viewSizeX = Max(Abs(shadowCasterBox.min_.x_), Abs(shadowCasterBox.max_.x_));
-            float viewSizeY = Max(Abs(shadowCasterBox.min_.y_), Abs(shadowCasterBox.max_.y_));
-            float viewSize = Max(viewSizeX, viewSizeY);
-            // Scale the quantization parameters, because view size is in projection space (-1.0 - 1.0)
-            float invOrthoSize = 1.0f / shadowCamera->GetOrthoSize();
-            float quantize = parameters.quantize_ * invOrthoSize;
-            float minView = parameters.minView_ * invOrthoSize;
+        float viewSizeX = Max(Abs(shadowCasterBox.min_.x_), Abs(shadowCasterBox.max_.x_));
+        float viewSizeY = Max(Abs(shadowCasterBox.min_.y_), Abs(shadowCasterBox.max_.y_));
+        float viewSize = Max(viewSizeX, viewSizeY);
+        // Scale the quantization parameters, because view size is in projection space (-1.0 - 1.0)
+        float invOrthoSize = 1.0f / shadowCamera->GetOrthoSize();
+        float quantize = parameters.quantize_ * invOrthoSize;
+        float minView = parameters.minView_ * invOrthoSize;
             
-            viewSize = Max(ceilf(viewSize / quantize) * quantize, minView);
-            if (viewSize < 1.0f)
-                shadowCamera->SetZoom(1.0f / viewSize);
-        }
+        viewSize = Max(ceilf(viewSize / quantize) * quantize, minView);
+        if (viewSize < 1.0f)
+            shadowCamera->SetZoom(1.0f / viewSize);
     }
     
     // Perform a finalization step for all lights: ensure zoom out of 2 pixels to eliminate border filtering issues
@@ -2617,9 +2589,9 @@ void View::FinalizeShadowCamera(Camera* shadowCamera, Light* light, const IntRec
         else
         {
             #ifdef URHO3D_OPENGL
-                shadowCamera->SetZoom(shadowCamera->GetZoom() * ((shadowMapWidth - 3.0f) / shadowMapWidth));
+            shadowCamera->SetZoom(shadowCamera->GetZoom() * ((shadowMapWidth - 3.0f) / shadowMapWidth));
             #else
-                shadowCamera->SetZoom(shadowCamera->GetZoom() * ((shadowMapWidth - 4.0f) / shadowMapWidth));
+            shadowCamera->SetZoom(shadowCamera->GetZoom() * ((shadowMapWidth - 4.0f) / shadowMapWidth));
             #endif
         }
     }
@@ -2712,10 +2684,7 @@ void View::FindZone(Drawable* drawable)
 Technique* View::GetTechnique(Drawable* drawable, Material* material)
 {
     if (!material)
-    {
-        const Vector<TechniqueEntry>& techniques = renderer_->GetDefaultMaterial()->GetTechniques();
-        return techniques.Size() ? techniques[0].technique_ : (Technique*)0;
-    }
+        return renderer_->GetDefaultMaterial()->GetTechniques()[0].technique_;
     
     const Vector<TechniqueEntry>& techniques = material->GetTechniques();
     // If only one technique, no choice
@@ -2748,12 +2717,11 @@ Technique* View::GetTechnique(Drawable* drawable, Material* material)
 
 void View::CheckMaterialForAuxView(Material* material)
 {
-    const SharedPtr<Texture>* textures = material->GetTextures();
-    unsigned numTextures = material->GetNumUsedTextureUnits();
+    const HashMap<TextureUnit, SharedPtr<Texture> >& textures = material->GetTextures();
 
-    for (unsigned i = 0; i < numTextures; ++i)
+    for (HashMap<TextureUnit, SharedPtr<Texture> >::ConstIterator i = textures.Begin(); i != textures.End(); ++i)
     {
-        Texture* texture = textures[i];
+        Texture* texture = i->second_.Get();
         if (texture && texture->GetUsage() == TEXTURE_RENDERTARGET)
         {
             // Have to check cube & 2D textures separately
@@ -2844,7 +2812,7 @@ void View::PrepareInstancingBuffer()
     
     unsigned totalInstances = 0;
     
-    for (HashMap<StringHash, BatchQueue>::Iterator i = batchQueues_.Begin(); i != batchQueues_.End(); ++i)
+    for (HashMap<unsigned, BatchQueue>::Iterator i = batchQueues_.Begin(); i != batchQueues_.End(); ++i)
         totalInstances += i->second_.GetNumInstances();
     
     for (Vector<LightBatchQueue>::Iterator i = lightQueues_.Begin(); i != lightQueues_.End(); ++i)
@@ -2855,28 +2823,27 @@ void View::PrepareInstancingBuffer()
         totalInstances += i->litBatches_.GetNumInstances();
     }
     
-    // If fail to set buffer size, fall back to per-group locking
-    if (totalInstances && renderer_->ResizeInstancingBuffer(totalInstances))
-    {
-        VertexBuffer* instancingBuffer = renderer_->GetInstancingBuffer();
-        unsigned freeIndex = 0;
-        void* dest = instancingBuffer->Lock(0, totalInstances, true);
-        if (!dest)
-            return;
-        
-        for (HashMap<StringHash, BatchQueue>::Iterator i = batchQueues_.Begin(); i != batchQueues_.End(); ++i)
-            i->second_.SetTransforms(dest, freeIndex);
-        
-        for (Vector<LightBatchQueue>::Iterator i = lightQueues_.Begin(); i != lightQueues_.End(); ++i)
-        {
-            for (unsigned j = 0; j < i->shadowSplits_.Size(); ++j)
-                i->shadowSplits_[j].shadowBatches_.SetTransforms(dest, freeIndex);
-            i->litBaseBatches_.SetTransforms(dest, freeIndex);
-            i->litBatches_.SetTransforms(dest, freeIndex);
-        }
+    if (!totalInstances || !renderer_->ResizeInstancingBuffer(totalInstances))
+        return;
+
+    VertexBuffer* instancingBuffer = renderer_->GetInstancingBuffer();
+    unsigned freeIndex = 0;
+    void* dest = instancingBuffer->Lock(0, totalInstances, true);
+    if (!dest)
+        return;
+    
+    for (HashMap<unsigned, BatchQueue>::Iterator i = batchQueues_.Begin(); i != batchQueues_.End(); ++i)
+        i->second_.SetTransforms(dest, freeIndex);
         
-        instancingBuffer->Unlock();
+    for (Vector<LightBatchQueue>::Iterator i = lightQueues_.Begin(); i != lightQueues_.End(); ++i)
+    {
+        for (unsigned j = 0; j < i->shadowSplits_.Size(); ++j)
+            i->shadowSplits_[j].shadowBatches_.SetTransforms(dest, freeIndex);
+        i->litBaseBatches_.SetTransforms(dest, freeIndex);
+        i->litBatches_.SetTransforms(dest, freeIndex);
     }
+    
+    instancingBuffer->Unlock();
 }
 
 void View::SetupLightVolumeBatch(Batch& batch)
@@ -2935,7 +2902,6 @@ void View::RenderShadowMap(const LightBatchQueue& queue)
     graphics_->SetTexture(TU_SHADOWMAP, 0);
     
     graphics_->SetColorWrite(false);
-    graphics_->SetDrawAntialiased(true);
     graphics_->SetFillMode(FILL_SOLID);
     graphics_->SetClipPlane(false);
     graphics_->SetStencilTest(false);
@@ -2958,6 +2924,8 @@ void View::RenderShadowMap(const LightBatchQueue& queue)
         {
             multiplier = Max(queue.shadowSplits_[i].shadowCamera_->GetFarClip() / queue.shadowSplits_[0].shadowCamera_->GetFarClip(), 1.0f);
             multiplier = 1.0f + (multiplier - 1.0f) * queue.light_->GetShadowCascade().biasAutoAdjust_;
+            // Quantize multiplier to prevent creation of too many rasterizer states on D3D11
+            multiplier = (int)(multiplier * 10.0f) / 10.0f;
         }
         
         // Perform further modification of depth bias on OpenGL ES, as shadow calculations' precision is limited

+ 24 - 17
Source/Urho3D/Graphics/View.h

@@ -65,7 +65,7 @@ struct LightQueryResult
     unsigned shadowCasterBegin_[MAX_LIGHT_SPLITS];
     /// Shadow caster end indices.
     unsigned shadowCasterEnd_[MAX_LIGHT_SPLITS];
-    /// Combined bounding box of shadow casters in light view or projection space.
+    /// Combined bounding box of shadow casters in light projection space. Only used for focused spot lights.
     BoundingBox shadowCasterBox_[MAX_LIGHT_SPLITS];
     /// Shadow camera near splits (directional lights only.)
     float shadowNearSplits_[MAX_LIGHT_SPLITS];
@@ -78,8 +78,8 @@ struct LightQueryResult
 /// Scene render pass info.
 struct ScenePassInfo
 {
-    /// Pass name hash.
-    StringHash pass_;
+    /// Pass index.
+    unsigned passIndex_;
     /// Allow instancing flag.
     bool allowInstancing_;
     /// Mark to stencil flag.
@@ -164,6 +164,12 @@ private:
     void GetDrawables();
     /// Construct batches from the drawable objects.
     void GetBatches();
+    /// Get lit geometries and shadowcasters for visible lights.
+    void ProcessLights();
+    /// Get batches from lit geometries and shadowcasters.
+    void GetLightBatches();
+    /// Get unlit batches.
+    void GetBaseBatches();
     /// Update geometries and sort batches.
     void UpdateGeometries();
     /// Get pixel lit batches for a certain light and drawable.
@@ -341,6 +347,7 @@ private:
     PODVector<Drawable*> occluders_;
     /// Lights.
     PODVector<Light*> lights_;
+    
     /// Drawables that limit their maximum light count.
     HashSet<Drawable*> maxLightsDrawables_;
     /// Rendertargets defined by the renderpath.
@@ -353,20 +360,20 @@ private:
     Vector<LightBatchQueue> lightQueues_;
     /// Per-vertex light queues.
     HashMap<unsigned long long, LightBatchQueue> vertexLightQueues_;
-    /// Batch queues.
-    HashMap<StringHash, BatchQueue> batchQueues_;
-    /// Hash of the GBuffer pass, or null if none.
-    StringHash gBufferPassName_;
-    /// Hash of the opaque forward base pass.
-    StringHash basePassName_;
-    /// Hash of the alpha pass.
-    StringHash alphaPassName_;
-    /// Hash of the forward light pass.
-    StringHash lightPassName_;
-    /// Hash of the litbase pass.
-    StringHash litBasePassName_;
-    /// Hash of the litalpha pass.
-    StringHash litAlphaPassName_;
+    /// Batch queues by pass index.
+    HashMap<unsigned, BatchQueue> batchQueues_;
+    /// Index of the GBuffer pass.
+    unsigned gBufferPassIndex_;
+    /// Index of the opaque forward base pass.
+    unsigned basePassIndex_;
+    /// Index of the alpha pass.
+    unsigned alphaPassIndex_;
+    /// Index of the forward light pass.
+    unsigned lightPassIndex_;
+    /// Index of the litbase pass.
+    unsigned litBasePassIndex_;
+    /// Index of the litalpha pass.
+    unsigned litAlphaPassIndex_;
     /// Pointer to the light volume command if any.
     const RenderPathCommand* lightVolumeCommand_;
 };

+ 3 - 4
Source/Urho3D/LuaScript/pkgs/Graphics/Graphics.pkg

@@ -27,6 +27,7 @@ class Graphics : public Object
     bool IsInitialized() const;
     void* GetExternalWindow() const;
     const String GetWindowTitle() const;
+    const String GetApiName() const;
     IntVector2 GetWindowPosition() const;
     int GetWidth() const;
     int GetHeight() const;
@@ -45,13 +46,11 @@ class Graphics : public Object
     unsigned GetDummyColorFormat() const;
     unsigned GetShadowMapFormat() const;
     unsigned GetHiresShadowMapFormat() const;
-    bool GetSM3Support() const;
     bool GetInstancingSupport() const;
     bool GetLightPrepassSupport() const;
     bool GetDeferredSupport() const;
     bool GetHardwareShadowSupport() const;
     bool GetReadableDepthSupport() const;
-    bool GetStreamOffsetSupport() const;
     bool GetSRGBSupport() const;
     bool GetSRGBWriteSupport() const;
     IntVector2 GetDesktopResolution() const;
@@ -73,9 +72,11 @@ class Graphics : public Object
     static unsigned GetDepthStencilFormat();
     static unsigned GetReadableDepthFormat();
     static unsigned GetFormat(const String formatName);
+    static unsigned GetMaxBones();
 
     tolua_readonly tolua_property__is_set bool initialized;
     tolua_property__get_set String windowTitle;
+    tolua_readonly tolua_property__get_set String apiName;
     tolua_property__get_set IntVector2 windowPosition;
     tolua_readonly tolua_property__get_set int width;
     tolua_readonly tolua_property__get_set int height;
@@ -94,13 +95,11 @@ class Graphics : public Object
     tolua_readonly tolua_property__get_set unsigned dummyColorFormat;
     tolua_readonly tolua_property__get_set unsigned shadowMapFormat;
     tolua_readonly tolua_property__get_set unsigned hiresShadowMapFormat;
-    tolua_readonly tolua_property__get_set bool SM3Support;
     tolua_readonly tolua_property__get_set bool instancingSupport;
     tolua_readonly tolua_property__get_set bool lightPrepassSupport;
     tolua_readonly tolua_property__get_set bool deferredSupport;
     tolua_readonly tolua_property__get_set bool hardwareShadowSupport;
     tolua_readonly tolua_property__get_set bool readableDepthSupport;
-    tolua_readonly tolua_property__get_set bool streamOffsetSupport;
     tolua_readonly tolua_property__get_set bool sRGBSupport;
     tolua_readonly tolua_property__get_set bool sRGBWriteSupport;
     tolua_readonly tolua_property__get_set IntVector2 desktopResolution;

+ 1 - 25
Source/Urho3D/LuaScript/pkgs/Graphics/GraphicsDefs.pkg

@@ -155,19 +155,7 @@ enum ShaderType
     PS,
 };
 
-enum ShaderParameterGroup
-{
-    SP_FRAME = 0,
-    SP_CAMERA,
-    SP_VIEWPORT,
-    SP_ZONE,
-    SP_LIGHT,
-    SP_VERTEXLIGHTS,
-    SP_MATERIAL,
-    SP_OBJECTTRANSFORM,
-    MAX_SHADER_PARAMETER_GROUPS
-};
-
+// Texture unit numbering depends on graphics capability (mobile or desktop). Expose only the units we are sure of
 enum TextureUnit
 {
     TU_DIFFUSE = 0,
@@ -177,18 +165,6 @@ enum TextureUnit
     TU_SPECULAR = 2,
     TU_EMISSIVE = 3,
     TU_ENVIRONMENT = 4,
-    MAX_MATERIAL_TEXTURE_UNITS = 5,
-    TU_LIGHTRAMP = 5,
-    TU_LIGHTSHAPE = 6,
-    TU_SHADOWMAP = 7,
-    TU_FACESELECT = 8,
-    TU_INDIRECTION = 9,
-    TU_DEPTHBUFFER = 10,
-    TU_LIGHTBUFFER = 11,
-    TU_VOLUMEMAP = 12,
-    TU_ZONE = 13,
-    MAX_NAMED_TEXTURE_UNITS = 14,
-    MAX_TEXTURE_UNITS = 16
 };
 
 enum FaceCameraMode

+ 2 - 7
Source/Urho3D/LuaScript/pkgs/Graphics/Material.pkg

@@ -33,9 +33,8 @@ class Material : public Resource
     unsigned GetNumTechniques() const;
     
     Technique* GetTechnique(unsigned index) const;
-    
-    Pass* GetPass(unsigned index, StringHash passType) const;
-    Pass* GetPass(unsigned index, const String passType) const;
+
+    Pass* GetPass(unsigned index, const String passName) const;
     
     Texture* GetTexture(TextureUnit unit) const;
     ValueAnimation* GetShaderParameterAnimation(const String name) const;
@@ -45,16 +44,12 @@ class Material : public Resource
     CullMode GetCullMode() const;
     CullMode GetShadowCullMode() const;
     const BiasParameters& GetDepthBias() const;
-    unsigned GetAuxViewFrameNumber() const;
-    unsigned GetNumUsedTextureUnits() const;
     bool GetOcclusion() const;
     bool GetSpecular() const;
     Scene* GetScene() const;
 
     tolua_readonly tolua_property__get_set CullMode cullMode;
     tolua_readonly tolua_property__get_set CullMode shadowCullMode;
-    tolua_readonly tolua_property__get_set unsigned auxViewFrameNumber;
-    tolua_readonly tolua_property__get_set unsigned numUsedTextureUnits;
     tolua_readonly tolua_property__get_set bool occlusion;
     tolua_readonly tolua_property__get_set bool specular;
     tolua_property__get_set Scene* scene;

+ 0 - 3
Source/Urho3D/LuaScript/pkgs/Graphics/Renderer.pkg

@@ -19,7 +19,6 @@ class Renderer
     void SetMaxShadowMaps(int shadowMaps);
     void SetDynamicInstancing(bool enable);
     void SetMinInstances(int instances);
-    void SetMaxInstanceTriangles(int triangles);
     void SetMaxSortedInstances(int instances);
     void SetMaxOccluderTriangles(int triangles);
     void SetOcclusionBufferSize(int size);
@@ -44,7 +43,6 @@ class Renderer
     int GetMaxShadowMaps() const;
     bool GetDynamicInstancing() const;
     int GetMinInstances() const;
-    int GetMaxInstanceTriangles() const;
     int GetMaxSortedInstances() const;
     int GetMaxOccluderTriangles() const;
     int GetOcclusionBufferSize() const;
@@ -80,7 +78,6 @@ class Renderer
     tolua_property__get_set int maxShadowMaps;
     tolua_property__get_set bool dynamicInstancing;
     tolua_property__get_set int minInstances;
-    tolua_property__get_set int maxInstanceTriangles;
     tolua_property__get_set int maxSortedInstances;
     tolua_property__get_set int maxOccluderTriangles;
     tolua_property__get_set int occlusionBufferSize;

+ 6 - 10
Source/Urho3D/LuaScript/pkgs/Graphics/Technique.pkg

@@ -9,12 +9,10 @@ enum PassLightingMode
 
 class Pass : public RefCounted
 {
-    bool IsSM3() const;
     bool IsDesktop() const;
     const String GetVertexShader() const;
     const String GetPixelShader() const;
 
-    tolua_readonly tolua_property__is_set bool SM3;
     tolua_readonly tolua_property__is_set bool desktop;
     tolua_readonly tolua_property__get_set const String vertexShader;
     tolua_readonly tolua_property__get_set const String pixelShader;
@@ -22,26 +20,24 @@ class Pass : public RefCounted
 
 class Technique : public Resource
 {
-    bool HasPass(const StringHash type) const;
-    Pass* GetPass(const StringHash type) const;
-    Pass* GetSupportedPass(const StringHash type) const;
+    bool HasPass(const String type) const;
+    Pass* GetPass(const String type) const;
+    Pass* GetSupportedPass(const String type) const;
     bool IsSupported() const;
-    bool IsSM3() const;
     bool IsDesktop() const;
     unsigned GetNumPasses() const;
-    tolua_outside const Vector<StringHash>& TechniqueGetPassTypes @ GetPassTypes() const;
+    tolua_outside const Vector<String>& TechniqueGetPassNames @ GetPassTypes() const;
     tolua_outside const PODVector<Pass*>& TechniqueGetPasses @ GetPasses() const;
 
     tolua_readonly tolua_property__is_set bool supported;
-    tolua_readonly tolua_property__is_set bool SM3;
     tolua_readonly tolua_property__is_set bool desktop;
     tolua_readonly tolua_property__get_set unsigned numPasses;
 };
 
 ${
-static const Vector<StringHash>& TechniqueGetPassTypes(const Technique* technique)
+static const Vector<String>& TechniqueGetPassNames(const Technique* technique)
 {
-    static Vector<StringHash> vector = technique->GetPassTypes();
+    static Vector<String> vector = technique->GetPassNames();
     return vector;
 }
 

+ 19 - 22
Source/Urho3D/Script/GraphicsAPI.cpp

@@ -278,23 +278,26 @@ static void RegisterRenderPath(asIScriptEngine* engine)
     
     engine->RegisterEnum("TextureUnit");
     engine->RegisterEnumValue("TextureUnit", "TU_DIFFUSE", TU_DIFFUSE);
+    engine->RegisterEnumValue("TextureUnit", "TU_ALBEDOBUFFER", TU_ALBEDOBUFFER);
     engine->RegisterEnumValue("TextureUnit", "TU_NORMAL", TU_NORMAL);
+    engine->RegisterEnumValue("TextureUnit", "TU_NORMALBUFFER", TU_NORMALBUFFER);
     engine->RegisterEnumValue("TextureUnit", "TU_SPECULAR", TU_SPECULAR);
     engine->RegisterEnumValue("TextureUnit", "TU_EMISSIVE", TU_EMISSIVE);
     engine->RegisterEnumValue("TextureUnit", "TU_ENVIRONMENT", TU_ENVIRONMENT);
     engine->RegisterEnumValue("TextureUnit", "TU_LIGHTRAMP", TU_LIGHTRAMP);
     engine->RegisterEnumValue("TextureUnit", "TU_LIGHTSHAPE", TU_LIGHTSHAPE);
     engine->RegisterEnumValue("TextureUnit", "TU_SHADOWMAP", TU_SHADOWMAP);
+    #ifdef DESKTOP_GRAPHICS
+    engine->RegisterEnumValue("TextureUnit", "TU_CUSTOM1", TU_CUSTOM1);
+    engine->RegisterEnumValue("TextureUnit", "TU_CUSTOM2", TU_CUSTOM2);
+    engine->RegisterEnumValue("TextureUnit", "TU_VOLUMEMAP", TU_VOLUMEMAP);
     engine->RegisterEnumValue("TextureUnit", "TU_FACESELECT", TU_FACESELECT);
     engine->RegisterEnumValue("TextureUnit", "TU_INDIRECTION", TU_INDIRECTION);
-    engine->RegisterEnumValue("TextureUnit", "TU_ALBEDOBUFFER", TU_ALBEDOBUFFER);
-    engine->RegisterEnumValue("TextureUnit", "TU_NORMALBUFFER", TU_NORMALBUFFER);
     engine->RegisterEnumValue("TextureUnit", "TU_DEPTHBUFFER", TU_DEPTHBUFFER);
     engine->RegisterEnumValue("TextureUnit", "TU_LIGHTBUFFER", TU_LIGHTBUFFER);
-    engine->RegisterEnumValue("TextureUnit", "TU_VOLUMEMAP", TU_VOLUMEMAP);
     engine->RegisterEnumValue("TextureUnit", "TU_ZONE", TU_ZONE);
+    #endif
     engine->RegisterEnumValue("TextureUnit", "MAX_MATERIAL_TEXTURE_UNITS", MAX_MATERIAL_TEXTURE_UNITS);
-    engine->RegisterEnumValue("TextureUnit", "MAX_NAMED_TEXTURE_UNITS", MAX_NAMED_TEXTURE_UNITS);
     engine->RegisterEnumValue("TextureUnit", "MAX_TEXTURE_UNITS", MAX_TEXTURE_UNITS);
     
     engine->RegisterObjectType("RenderTargetInfo", sizeof(RenderTargetInfo), asOBJ_VALUE | asOBJ_APP_CLASS_C);
@@ -480,9 +483,11 @@ static void RegisterTextures(asIScriptEngine* engine)
     engine->RegisterGlobalFunction("uint GetRGFloat32Format()", asFUNCTION(Graphics::GetRGFloat32Format), asCALL_CDECL);
     engine->RegisterGlobalFunction("uint GetFloat16Format()", asFUNCTION(Graphics::GetFloat16Format), asCALL_CDECL);
     engine->RegisterGlobalFunction("uint GetFloat32Format()", asFUNCTION(Graphics::GetFloat32Format), asCALL_CDECL);
+    engine->RegisterGlobalFunction("uint GetLinearDepthFormat()", asFUNCTION(Graphics::GetLinearDepthFormat), asCALL_CDECL);
     engine->RegisterGlobalFunction("uint GetDepthStencilFormat()", asFUNCTION(Graphics::GetDepthStencilFormat), asCALL_CDECL);
     engine->RegisterGlobalFunction("uint GetReadableDepthFormat()", asFUNCTION(Graphics::GetReadableDepthFormat), asCALL_CDECL);
     engine->RegisterGlobalFunction("uint GetFormat(const String&in)", asFUNCTIONPR(Graphics::GetFormat, (const String&), unsigned), asCALL_CDECL);
+    engine->RegisterGlobalFunction("uint GetMaxBones()", asFUNCTION(Graphics::GetMaxBones), asCALL_CDECL);
 }
 
 static Material* MaterialClone(const String& cloneName, Material* ptr)
@@ -514,9 +519,9 @@ static void ConstructTechniqueEntry(TechniqueEntry* ptr)
     new(ptr) TechniqueEntry();
 }
 
-static CScriptArray* TechniqueGetPassTypes(const Technique& technique)
+static CScriptArray* TechniqueGetPassNames(const Technique& technique)
 {
-    return VectorToArray<StringHash>(technique.GetPassTypes(), "Array<StringHash>");
+    return VectorToArray<String>(technique.GetPassNames(), "Array<String>");
 }
 
 static CScriptArray* TechniqueGetPasses(const Technique& technique)
@@ -619,8 +624,6 @@ static void RegisterMaterial(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Pass", "bool get_depthWrite() const", asMETHOD(Pass, GetDepthWrite), asCALL_THISCALL);
     engine->RegisterObjectMethod("Pass", "void set_alphaMask(bool)", asMETHOD(Pass, SetAlphaMask), asCALL_THISCALL);
     engine->RegisterObjectMethod("Pass", "bool get_alphaMask() const", asMETHOD(Pass, GetAlphaMask), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Pass", "void set_sm3(bool)", asMETHOD(Technique, SetIsSM3), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Pass", "bool get_sm3() const", asMETHOD(Technique, IsSM3), asCALL_THISCALL);
     engine->RegisterObjectMethod("Pass", "void set_desktop(bool)", asMETHOD(Technique, SetIsDesktop), asCALL_THISCALL);
     engine->RegisterObjectMethod("Pass", "bool get_desktop() const", asMETHOD(Technique, IsDesktop), asCALL_THISCALL);
     engine->RegisterObjectMethod("Pass", "void set_vertexShader(const String&in)", asMETHOD(Pass, SetVertexShader), asCALL_THISCALL);
@@ -633,18 +636,16 @@ static void RegisterMaterial(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Pass", "const String& get_pixelShaderDefines() const", asMETHOD(Pass, GetPixelShaderDefines), asCALL_THISCALL);
     
     RegisterResource<Technique>(engine, "Technique");
-    engine->RegisterObjectMethod("Technique", "Pass@+ CreatePass(StringHash)", asMETHOD(Technique, CreatePass), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Technique", "void RemovePass(StringHash)", asMETHOD(Technique, RemovePass), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Technique", "bool HasPass(StringHash) const", asMETHOD(Technique, HasPass), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Technique", "Pass@+ GetPass(StringHash)", asMETHOD(Technique, GetPass), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Technique", "Pass@+ GetSupportedPass(StringHash)", asMETHOD(Technique, GetSupportedPass), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Technique", "Pass@+ CreatePass(const String&in)", asMETHOD(Technique, CreatePass), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Technique", "void RemovePass(const String&in)", asMETHOD(Technique, RemovePass), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Technique", "bool HasPass(const String&in) const", asMETHODPR(Technique, HasPass, (const String&) const, bool), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Technique", "Pass@+ GetPass(const String&in)", asMETHODPR(Technique, GetPass, (const String&) const, Pass*), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Technique", "Pass@+ GetSupportedPass(const String&in)", asMETHODPR(Technique, GetSupportedPass, (const String&) const, Pass*), asCALL_THISCALL);
     engine->RegisterObjectMethod("Technique", "bool get_supported() const", asMETHOD(Technique, IsSupported), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Technique", "void set_sm3(bool)", asMETHOD(Technique, SetIsSM3), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Technique", "bool get_sm3() const", asMETHOD(Technique, IsSM3), asCALL_THISCALL);
     engine->RegisterObjectMethod("Technique", "void set_desktop(bool)", asMETHOD(Technique, SetIsDesktop), asCALL_THISCALL);
     engine->RegisterObjectMethod("Technique", "bool get_desktop() const", asMETHOD(Technique, IsDesktop), asCALL_THISCALL);
     engine->RegisterObjectMethod("Technique", "uint get_numPasses() const", asMETHOD(Technique, GetNumPasses), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Technique", "Array<StringHash>@ get_passTypes() const", asFUNCTION(TechniqueGetPassTypes), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectMethod("Technique", "Array<String>@ get_passNames() const", asFUNCTION(TechniqueGetPassNames), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("Technique", "Array<Pass@>@ get_passes() const", asFUNCTION(TechniqueGetPasses), asCALL_CDECL_OBJLAST);
     
     engine->RegisterObjectType("TechniqueEntry", sizeof(TechniqueEntry), asOBJ_VALUE | asOBJ_APP_CLASS_CD);
@@ -666,6 +667,7 @@ static void RegisterMaterial(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Material", "void RemoveShaderParameter(const String&in)", asMETHOD(Material, RemoveShaderParameter), asCALL_THISCALL);
     engine->RegisterObjectMethod("Material", "void SortTechniques()", asMETHOD(Material, SortTechniques), asCALL_THISCALL);
     engine->RegisterObjectMethod("Material", "Material@ Clone(const String&in cloneName = String()) const", asFUNCTION(MaterialClone), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectMethod("Material", "Pass@+ GetPass(uint, const String&in)", asMETHOD(Material, GetPass), asCALL_THISCALL);
     engine->RegisterObjectMethod("Material", "void set_numTechniques(uint)", asMETHOD(Material, SetNumTechniques), asCALL_THISCALL);
     engine->RegisterObjectMethod("Material", "uint get_numTechniques() const", asMETHOD(Material, GetNumTechniques), asCALL_THISCALL);
     engine->RegisterObjectMethod("Material", "Technique@+ get_techniques(uint)", asMETHOD(Material, GetTechnique), asCALL_THISCALL);
@@ -688,7 +690,6 @@ static void RegisterMaterial(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Material", "CullMode get_shadowCullMode() const", asMETHOD(Material, GetShadowCullMode), asCALL_THISCALL);
     engine->RegisterObjectMethod("Material", "void set_depthBias(const BiasParameters&in)", asMETHOD(Material, SetDepthBias), asCALL_THISCALL);
     engine->RegisterObjectMethod("Material", "const BiasParameters& get_depthBias() const", asMETHOD(Material, GetDepthBias), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Material", "uint get_numUsedTextureUnits() const", asMETHOD(Material, GetNumUsedTextureUnits), asCALL_THISCALL);
     engine->RegisterObjectMethod("Material", "void set_scene(Scene@+)", asMETHOD(Material, SetScene), asCALL_THISCALL);
     engine->RegisterObjectMethod("Material", "Scene@+ get_scene() const", asMETHOD(Material, GetScene), asCALL_THISCALL);
     
@@ -1355,6 +1356,7 @@ static void RegisterGraphics(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Graphics", "void PrecacheShaders(VectorBuffer&)", asFUNCTION(GraphicsPrecacheShadersVectorBuffer), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("Graphics", "void set_windowTitle(const String&in)", asMETHOD(Graphics, SetWindowTitle), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "const String& get_windowTitle() const", asMETHOD(Graphics, GetWindowTitle), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Graphics", "const String& get_apiName() const", asMETHOD(Graphics, GetApiName), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "void set_windowIcon(Image@+)", asMETHOD(Graphics, SetWindowIcon), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "void set_windowPosition(const IntVector2&in)", asMETHODPR(Graphics, SetWindowPosition, (const IntVector2&), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "IntVector2 get_windowPosition() const", asMETHOD(Graphics, GetWindowPosition), asCALL_THISCALL);
@@ -1376,7 +1378,6 @@ static void RegisterGraphics(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Graphics", "bool get_deviceLost() const", asMETHOD(Graphics, IsDeviceLost), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "uint get_numPrimitives() const", asMETHOD(Graphics, GetNumPrimitives), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "uint get_numBatches() const", asMETHOD(Graphics, GetNumBatches), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Graphics", "bool get_sm3Support() const", asMETHOD(Graphics, GetSM3Support), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "bool get_instancingSupport() const", asMETHOD(Graphics, GetInstancingSupport), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "bool get_lightPrepassSupport() const", asMETHOD(Graphics, GetLightPrepassSupport), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "bool get_deferredSupport() const", asMETHOD(Graphics, GetDeferredSupport), asCALL_THISCALL);
@@ -1384,8 +1385,6 @@ static void RegisterGraphics(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Graphics", "bool get_readableDepthSupport() const", asMETHOD(Graphics, GetReadableDepthSupport), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "bool get_sRGBSupport() const", asMETHOD(Graphics, GetSRGBSupport), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "bool get_sRGBWriteSupport() const", asMETHOD(Graphics, GetSRGBWriteSupport), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Graphics", "void set_forceSM2(bool)", asMETHOD(Graphics, SetForceSM2), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Graphics", "bool get_forceSM2() const", asMETHOD(Graphics, GetForceSM2), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "Array<IntVector2>@ get_resolutions() const", asFUNCTION(GraphicsGetResolutions), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("Graphics", "Array<int>@ get_multiSampleLevels() const", asFUNCTION(GraphicsGetMultiSampleLevels), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("Graphics", "IntVector2 get_desktopResolution() const", asMETHOD(Graphics, GetDesktopResolution), asCALL_THISCALL);
@@ -1449,8 +1448,6 @@ static void RegisterRenderer(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Renderer", "bool get_dynamicInstancing() const", asMETHOD(Renderer, GetDynamicInstancing), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void set_minInstances(int)", asMETHOD(Renderer, SetMinInstances), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "int get_minInstances() const", asMETHOD(Renderer, GetMinInstances), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Renderer", "void set_maxInstanceTriangles(int)", asMETHOD(Renderer, SetMaxInstanceTriangles), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Renderer", "int get_maxInstanceTriangles() const", asMETHOD(Renderer, GetMaxInstanceTriangles), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void set_maxSortedInstances(int)", asMETHOD(Renderer, SetMaxSortedInstances), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "int get_maxSortedInstances() const", asMETHOD(Renderer, GetMaxSortedInstances), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void set_maxOccluderTriangles(int)", asMETHOD(Renderer, SetMaxOccluderTriangles), asCALL_THISCALL);

+ 1 - 1
Source/Urho3D/UI/Text3D.cpp

@@ -522,7 +522,7 @@ void Text3D::UpdateTextMaterials(bool forceUpdate)
             {
                 Material* material = new Material(context_);
                 Technique* tech = new Technique(context_);
-                Pass* pass = tech->CreatePass(PASS_ALPHA);
+                Pass* pass = tech->CreatePass("alpha");
                 pass->SetVertexShader("Text");
                 pass->SetPixelShader("Text");
 

Some files were not shown because too many files changed in this diff