Browse Source

Moved to XML based shader precache files. Refactored shader precaching code to its own class. Closes #199.

Lasse Öörni 12 years ago
parent
commit
cc9e5e1994

+ 0 - 34
Bin/Data/NinjaSnowWarShaders.txt

@@ -1,34 +0,0 @@
-Shadow_SKINNED Shadow
-LitSolid_DIRLIGHT_PERPIXEL_SHADOW LitSolid_AMBIENT_DIFFMAP_DIRLIGHT_PERPIXEL_SHADOW
-LitSolid_DIRLIGHT_PERPIXEL_SHADOW_SKINNED LitSolid_AMBIENT_DIFFMAP_DIRLIGHT_PERPIXEL_SHADOW
-Unlit Unlit_DIFFMAP
-Unlit_BILLBOARD_VERTEXCOLOR Unlit_DIFFMAP_VERTEXCOLOR
-Shadow Shadow
-Shadow_INSTANCED Shadow
-LitSolid_DIRLIGHT_INSTANCED_PERPIXEL_SHADOW LitSolid_AMBIENT_DIFFMAP_DIRLIGHT_PERPIXEL_SHADOW
-LitSolid_PERPIXEL_POINTLIGHT_SHADOW LitSolid_DIFFMAP_PERPIXEL_POINTLIGHT_SHADOW
-LitSolid_PERPIXEL_POINTLIGHT_SHADOW_SKINNED LitSolid_DIFFMAP_PERPIXEL_POINTLIGHT_SHADOW
-Stencil Stencil
-LitSolid_DIRLIGHT_PERPIXEL_SHADOW LitSolid_AMBIENT_DIRLIGHT_PERPIXEL_SHADOW
-LitSolid_PERPIXEL_POINTLIGHT_SHADOW LitSolid_PERPIXEL_POINTLIGHT_SHADOW
-LitSolid_DIRLIGHT_PERPIXEL_SHADOW_SKINNED LitSolid_AMBIENT_DIFFMAP_DIRLIGHT_LQSHADOW_PERPIXEL_SHADOW
-LitSolid_DIRLIGHT_INSTANCED_PERPIXEL_SHADOW LitSolid_AMBIENT_DIFFMAP_DIRLIGHT_LQSHADOW_PERPIXEL_SHADOW
-LitSolid_PERPIXEL_POINTLIGHT_SHADOW LitSolid_DIFFMAP_LQSHADOW_PERPIXEL_POINTLIGHT_SHADOW
-LitSolid_PERPIXEL_POINTLIGHT_SHADOW_SKINNED LitSolid_DIFFMAP_LQSHADOW_PERPIXEL_POINTLIGHT_SHADOW
-LitSolid_INSTANCED_PERPIXEL_POINTLIGHT_SHADOW LitSolid_DIFFMAP_LQSHADOW_PERPIXEL_POINTLIGHT_SHADOW
-LitSolid_PERPIXEL_POINTLIGHT LitSolid_DIFFMAP_PERPIXEL_POINTLIGHT
-LitSolid_DIRLIGHT_PERPIXEL_SHADOW LitSolid_AMBIENT_DIRLIGHT_LQSHADOW_PERPIXEL_SHADOW
-LitSolid_PERPIXEL_POINTLIGHT_SHADOW LitSolid_LQSHADOW_PERPIXEL_POINTLIGHT_SHADOW
-Basic_DIFFMAP_VERTEXCOLOR Basic_ALPHAMASK_DIFFMAP_VERTEXCOLOR
-Basic_DIFFMAP_VERTEXCOLOR Basic_ALPHAMAP_VERTEXCOLOR
-Basic_VERTEXCOLOR Basic_VERTEXCOLOR
-LitSolid_INSTANCED_PERPIXEL_POINTLIGHT_SHADOW LitSolid_DIFFMAP_PERPIXEL_POINTLIGHT_SHADOW
-LitSolid_DIRLIGHT_PERPIXEL_SHADOW LitSolid_AMBIENT_DIFFMAP_DIRLIGHT_LQSHADOW_PERPIXEL_SHADOW
-Basic_DIFFMAP_VERTEXCOLOR Basic_ALPHAMASK_DIFFMAP_VERTEXCOLOR
-LitSolid_PERPIXEL_POINTLIGHT_SHADOW LitSolid_LQSHADOW_PERPIXEL_POINTLIGHT_SHADOW
-LitSolid_DIRLIGHT_PERPIXEL LitSolid_AMBIENT_DIFFMAP_DIRLIGHT_PERPIXEL
-LitSolid_DIRLIGHT_PERPIXEL_SKINNED LitSolid_AMBIENT_DIFFMAP_DIRLIGHT_PERPIXEL
-LitSolid_DIRLIGHT_INSTANCED_PERPIXEL LitSolid_AMBIENT_DIFFMAP_DIRLIGHT_PERPIXEL
-LitSolid_PERPIXEL_POINTLIGHT_SKINNED LitSolid_DIFFMAP_PERPIXEL_POINTLIGHT
-LitSolid_DIRLIGHT_PERPIXEL LitSolid_AMBIENT_DIRLIGHT_PERPIXEL
-LitSolid_PERPIXEL_POINTLIGHT LitSolid_PERPIXEL_POINTLIGHT

+ 28 - 0
Bin/Data/NinjaSnowWarShaders.xml

@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<shaders>
+	<shader vs="Shadow" vsdefines="SKINNED" ps="Shadow" psdefines="" />
+	<shader vs="LitSolid" vsdefines="DIRLIGHT PERPIXEL SHADOW" ps="LitSolid" psdefines="AMBIENT DIFFMAP DIRLIGHT PERPIXEL SHADOW" />
+	<shader vs="LitSolid" vsdefines="DIRLIGHT PERPIXEL SHADOW SKINNED" ps="LitSolid" psdefines="AMBIENT DIFFMAP DIRLIGHT PERPIXEL SHADOW" />
+	<shader vs="Unlit" vsdefines="" ps="Unlit" psdefines="DIFFMAP" />
+	<shader vs="Basic" vsdefines="DIFFMAP VERTEXCOLOR" ps="Basic" psdefines="ALPHAMASK DIFFMAP VERTEXCOLOR" />
+	<shader vs="Basic" vsdefines="DIFFMAP VERTEXCOLOR" ps="Basic" psdefines="ALPHAMAP VERTEXCOLOR" />
+	<shader vs="Unlit" vsdefines="BILLBOARD VERTEXCOLOR" ps="Unlit" psdefines="DIFFMAP VERTEXCOLOR" />
+	<shader vs="Shadow" vsdefines="" ps="Shadow" psdefines="" />
+	<shader vs="Shadow" vsdefines="INSTANCED" ps="Shadow" psdefines="" />
+	<shader vs="LitSolid" vsdefines="DIRLIGHT INSTANCED PERPIXEL SHADOW" ps="LitSolid" psdefines="AMBIENT DIFFMAP DIRLIGHT PERPIXEL SHADOW" />
+	<shader vs="LitSolid" vsdefines="PERPIXEL POINTLIGHT SHADOW" ps="LitSolid" psdefines="DIFFMAP PERPIXEL POINTLIGHT SHADOW" />
+	<shader vs="LitSolid" vsdefines="PERPIXEL POINTLIGHT SHADOW SKINNED" ps="LitSolid" psdefines="DIFFMAP PERPIXEL POINTLIGHT SHADOW" />
+	<shader vs="LitSolid" vsdefines="INSTANCED PERPIXEL POINTLIGHT SHADOW" ps="LitSolid" psdefines="DIFFMAP PERPIXEL POINTLIGHT SHADOW" />
+	<shader vs="Stencil" vsdefines="" ps="Stencil" psdefines="" />
+	<shader vs="LitSolid" vsdefines="PERPIXEL POINTLIGHT" ps="LitSolid" psdefines="DIFFMAP PERPIXEL POINTLIGHT" />
+	<shader vs="LitSolid" vsdefines="DIRLIGHT PERPIXEL SHADOW" ps="LitSolid" psdefines="AMBIENT DIRLIGHT PERPIXEL SHADOW" />
+	<shader vs="LitSolid" vsdefines="PERPIXEL POINTLIGHT SHADOW" ps="LitSolid" psdefines="PERPIXEL POINTLIGHT SHADOW" />
+	<shader vs="LitSolid" vsdefines="DIRLIGHT PERPIXEL SHADOW" ps="LitSolid" psdefines="AMBIENT DIFFMAP DIRLIGHT LQSHADOW PERPIXEL SHADOW" />
+	<shader vs="LitSolid" vsdefines="DIRLIGHT PERPIXEL SHADOW SKINNED" ps="LitSolid" psdefines="AMBIENT DIFFMAP DIRLIGHT LQSHADOW PERPIXEL SHADOW" />
+	<shader vs="Basic" vsdefines="VERTEXCOLOR" ps="Basic" psdefines="VERTEXCOLOR" />
+	<shader vs="LitSolid" vsdefines="DIRLIGHT INSTANCED PERPIXEL SHADOW" ps="LitSolid" psdefines="AMBIENT DIFFMAP DIRLIGHT LQSHADOW PERPIXEL SHADOW" />
+	<shader vs="LitSolid" vsdefines="PERPIXEL POINTLIGHT SHADOW" ps="LitSolid" psdefines="DIFFMAP LQSHADOW PERPIXEL POINTLIGHT SHADOW" />
+	<shader vs="LitSolid" vsdefines="PERPIXEL POINTLIGHT SHADOW SKINNED" ps="LitSolid" psdefines="DIFFMAP LQSHADOW PERPIXEL POINTLIGHT SHADOW" />
+	<shader vs="LitSolid" vsdefines="DIRLIGHT PERPIXEL SHADOW" ps="LitSolid" psdefines="AMBIENT DIRLIGHT LQSHADOW PERPIXEL SHADOW" />
+	<shader vs="LitSolid" vsdefines="PERPIXEL POINTLIGHT SHADOW" ps="LitSolid" psdefines="LQSHADOW PERPIXEL POINTLIGHT SHADOW" />
+</shaders>

+ 2 - 2
Bin/Data/Scripts/NinjaSnowWar.as

@@ -167,8 +167,8 @@ void InitScene()
     }
     
     // Precache shaders if possible
-    if (cache.Exists("NinjaSnowWarShaders.txt"))
-        graphics.PrecacheShaders(cache.GetFile("NinjaSnowWarShaders.txt"));
+    if (cache.Exists("NinjaSnowWarShaders.xml"))
+        graphics.PrecacheShaders(cache.GetFile("NinjaSnowWarShaders.xml"));
 }
 
 void InitNetworking()

+ 3 - 5
Docs/Reference.dox

@@ -1042,13 +1042,11 @@ current system, look up the defines from the description files and add them dire
 
 GLSL shaders in 1.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.
 
-\section Shaders_Precaching
+\section Shaders_Precaching Shader precaching
 
-The shader variations that are possibly used by a material technique in different lighting conditions and rendering passes are enumerated at material load time, but because of their large amount, are not actually compiled or loaded from bytecode before being used in rendering. Especially on OpenGL the compiling of shaders just before rendering can cause hitches in the framerate. To avoid this, used shader combinations can be dumped out to a file, then preloaded. See \ref Graphics::BeginDumpShaders "BeginDumpShaders()", \ref Graphics::EndDumpShaders "EndDumpShaders()" and \ref Graphics::PrecacheShaders "PrecacheShaders()" in the Graphics subsystem. The command line parameters -ds <file> can be used to instruct the Engine to begin dumping shaders automatically on startup.
+The shader variations that are potentially used by a material technique in different lighting conditions and rendering passes are enumerated at material load time, but because of their large amount, they are not actually compiled or loaded from bytecode before being used in rendering. Especially on OpenGL the compiling of shaders just before rendering can cause hitches in the framerate. To avoid this, used shader combinations can be dumped out to an XML file, then preloaded. See \ref Graphics::BeginDumpShaders "BeginDumpShaders()", \ref Graphics::EndDumpShaders "EndDumpShaders()" and \ref Graphics::PrecacheShaders "PrecacheShaders()" in the Graphics subsystem. The command line parameters -ds <file> can be used to instruct the Engine to begin dumping shaders automatically on startup.
 
-Note that the shader variations that actually end up being loaded will vary due to graphics settings, for example shadow quality or instancing on/off.
-
-When you create your own compilation defines, do not use the underscore _ in them, as that will interfere with the shader precaching: compilation defines will be separated by underscores in the shader variation names and it is impossible to distinguish between underscores separating the defines, and underscores inside a define.
+Note that the used shader variations will vary with graphics settings, for example shadow quality high/low or instancing on/off.
 
 \page RenderPaths Render path
 

+ 7 - 49
Source/Engine/Graphics/Direct3D9/D3D9Graphics.cpp

@@ -42,6 +42,7 @@
 #include "Profiler.h"
 #include "ResourceCache.h"
 #include "Shader.h"
+#include "ShaderPrecache.h"
 #include "ShaderVariation.h"
 #include "Skybox.h"
 #include "StaticModelGroup.h"
@@ -1107,16 +1108,9 @@ void Graphics::SetShaders(ShaderVariation* vs, ShaderVariation* ps)
         pixelShader_ = ps;
     }
     
-    // If used shaders are being dumped, check now for unique combination and write if necessary
-    if (shaderDumpFile_ && vertexShader_ && pixelShader_)
-    {
-        String shaderNames = vertexShader_->GetName() + " " + pixelShader_->GetName();
-        if (!usedShaders_.Contains(shaderNames))
-        {
-            shaderDumpFile_->WriteLine(shaderNames);
-            usedShaders_.Insert(shaderNames);
-        }
-    }
+    // Store shader combination if shader dumping in progress
+    if (shaderPrecache_)
+        shaderPrecache_->StoreShaders(vertexShader_, pixelShader_);
 }
 
 void Graphics::SetShaderParameter(StringHash param, const float* data, unsigned count)
@@ -1896,55 +1890,19 @@ void Graphics::SetForceSM2(bool enable)
 
 void Graphics::BeginDumpShaders(const String& fileName)
 {
-    shaderDumpFile_ = new File(context_, fileName, FILE_READWRITE);
-    if (!shaderDumpFile_->IsOpen())
-    {
-        shaderDumpFile_.Reset();
-        return;
-    }
-    
-    LOGDEBUG("Begin dumping shaders to file " + fileName);
-    
-    // Read existing combinations from the file (if any) to avoid duplicates
-    usedShaders_.Clear();
-    while (!shaderDumpFile_->IsEof())
-        usedShaders_.Insert(shaderDumpFile_->ReadLine());
+    shaderPrecache_ = new ShaderPrecache(context_, fileName);
 }
 
 void Graphics::EndDumpShaders()
 {
-    if (shaderDumpFile_)
-    {
-        LOGDEBUG("End dumping shaders");
-        shaderDumpFile_.Reset();
-    }
+    shaderPrecache_.Reset();
 }
 
 void Graphics::PrecacheShaders(Deserializer& source)
 {
     PROFILE(PrecacheShaders);
     
-    LOGDEBUG("Begin precaching shaders");
-    
-    while (!source.IsEof())
-    {
-        Vector<String> shaders = source.ReadLine().Split(' ');
-        if (shaders.Size() == 2)
-        {
-            // Shader names are stored as Name_DEFINE1_DEFINE2 etc.
-            unsigned vsNameEnd = shaders[0].Find('_');
-            unsigned psNameEnd = shaders[1].Find('_');
-            ShaderVariation* vs = GetShader(VS, shaders[0].Substring(0, vsNameEnd), vsNameEnd != String::NPOS ?
-                shaders[0].Substring(vsNameEnd + 1).Replaced('_', ' ') : String::EMPTY);
-            ShaderVariation* ps = GetShader(PS, shaders[1].Substring(0, psNameEnd), psNameEnd != String::NPOS ?
-                shaders[1].Substring(psNameEnd + 1).Replaced('_', ' ') : String::EMPTY);
-            // Set the shaders in use to fully compile them
-            if (vs && ps)
-                SetShaders(vs, ps);
-        }
-    }
-    
-    LOGDEBUG("End precaching shaders");
+    ShaderPrecache::LoadShaders(this, source);
 }
 
 bool Graphics::IsInitialized() const

+ 3 - 4
Source/Engine/Graphics/Direct3D9/D3D9Graphics.h

@@ -41,6 +41,7 @@ class GPUObject;
 class GraphicsImpl;
 class RenderSurface;
 class Shader;
+class ShaderPrecache;
 class ShaderVariation;
 class Texture;
 class Texture2D;
@@ -580,10 +581,8 @@ private:
     mutable WeakPtr<Shader> lastShader_;
     /// Last used shader name in shader variation query.
     mutable String lastShaderName_;
-    /// File shader variations are dumped into. Null if dumping not active.
-    SharedPtr<File> shaderDumpFile_;
-    /// Hash set for detecting already dumped variations.
-    HashSet<String> usedShaders_;
+    /// Shader precache utility.
+    SharedPtr<ShaderPrecache> shaderPrecache_;
 };
 
 /// Register Graphics library objects.

+ 5 - 0
Source/Engine/Graphics/Direct3D9/D3D9ShaderVariation.cpp

@@ -149,6 +149,11 @@ void ShaderVariation::SetDefines(const String& defines)
     defines_ = defines;
 }
 
+Shader* ShaderVariation::GetOwner() const
+{
+    return owner_;
+}
+
 bool ShaderVariation::LoadByteCode(PODVector<unsigned>& byteCode, const String& binaryShaderName)
 {
     ResourceCache* cache = owner_->GetSubsystem<ResourceCache>();

+ 2 - 0
Source/Engine/Graphics/Direct3D9/D3D9ShaderVariation.h

@@ -82,6 +82,8 @@ public:
     /// Set defines.
     void SetDefines(const String& defines);
     
+    /// Return the owner resource.
+    Shader* GetOwner() const;
     /// Return shader type.
     ShaderType GetShaderType() const { return type_; }
     /// Return full shader name.

+ 7 - 49
Source/Engine/Graphics/OpenGL/OGLGraphics.cpp

@@ -45,6 +45,7 @@
 #include "RenderSurface.h"
 #include "ResourceCache.h"
 #include "Shader.h"
+#include "ShaderPrecache.h"
 #include "ShaderProgram.h"
 #include "ShaderVariation.h"
 #include "Skybox.h"
@@ -1111,16 +1112,9 @@ void Graphics::SetShaders(ShaderVariation* vs, ShaderVariation* ps)
     }
     
 
-    // If used shaders are being dumped, check now for unique combination and write if necessary
-    if (shaderDumpFile_ && vertexShader_ && pixelShader_)
-    {
-        String shaderNames = vertexShader_->GetName() + " " + pixelShader_->GetName();
-        if (!usedShaders_.Contains(shaderNames))
-        {
-            shaderDumpFile_->WriteLine(shaderNames);
-            usedShaders_.Insert(shaderNames);
-        }
-    }
+    // Store shader combination if shader dumping in progress
+    if (shaderPrecache_)
+        shaderPrecache_->StoreShaders(vertexShader_, pixelShader_);
 }
 
 void Graphics::SetShaderParameter(StringHash param, const float* data, unsigned count)
@@ -1886,55 +1880,19 @@ void Graphics::SetForceSM2(bool enable)
 
 void Graphics::BeginDumpShaders(const String& fileName)
 {
-    shaderDumpFile_ = new File(context_, fileName, FILE_READWRITE);
-    if (!shaderDumpFile_->IsOpen())
-    {
-        shaderDumpFile_.Reset();
-        return;
-    }
-    
-    LOGDEBUG("Begin dumping shaders to file " + fileName);
-    
-    // Read existing combinations from the file (if any) to avoid duplicates
-    usedShaders_.Clear();
-    while (!shaderDumpFile_->IsEof())
-        usedShaders_.Insert(shaderDumpFile_->ReadLine());
+    shaderPrecache_ = new ShaderPrecache(context_, fileName);
 }
 
 void Graphics::EndDumpShaders()
 {
-    if (shaderDumpFile_)
-    {
-        LOGDEBUG("End dumping shaders");
-        shaderDumpFile_.Reset();
-    }
+    shaderPrecache_.Reset();
 }
 
 void Graphics::PrecacheShaders(Deserializer& source)
 {
     PROFILE(PrecacheShaders);
     
-    LOGDEBUG("Begin precaching shaders");
-    
-    while (!source.IsEof())
-    {
-        Vector<String> shaders = source.ReadLine().Split(' ');
-        if (shaders.Size() == 2)
-        {
-            // Shader names are stored as Name_DEFINE1_DEFINE2 etc.
-            unsigned vsNameEnd = shaders[0].Find('_');
-            unsigned psNameEnd = shaders[1].Find('_');
-            ShaderVariation* vs = GetShader(VS, shaders[0].Substring(0, vsNameEnd), vsNameEnd != String::NPOS ?
-                shaders[0].Substring(vsNameEnd + 1).Replaced('_', ' ') : String::EMPTY);
-            ShaderVariation* ps = GetShader(PS, shaders[1].Substring(0, psNameEnd), psNameEnd != String::NPOS ?
-                shaders[1].Substring(psNameEnd + 1).Replaced('_', ' ') : String::EMPTY);
-            // Set the shaders in use to fully compile them
-            if (vs && ps)
-                SetShaders(vs, ps);
-        }
-    }
-    
-    LOGDEBUG("End precaching shaders");
+    ShaderPrecache::LoadShaders(this, source);
 }
 
 bool Graphics::IsInitialized() const

+ 3 - 5
Source/Engine/Graphics/OpenGL/OGLGraphics.h

@@ -25,7 +25,6 @@
 #include "ArrayPtr.h"
 #include "Color.h"
 #include "GraphicsDefs.h"
-#include "HashSet.h"
 #include "Image.h"
 #include "Object.h"
 #include "Plane.h"
@@ -41,6 +40,7 @@ class GPUObject;
 class GraphicsImpl;
 class RenderSurface;
 class Shader;
+class ShaderPrecache;
 class ShaderProgram;
 class ShaderVariation;
 class Texture;
@@ -591,10 +591,8 @@ private:
     mutable WeakPtr<Shader> lastShader_;
     /// Last used shader name in shader variation query.
     mutable String lastShaderName_;
-    /// File shader variations are dumped into. Null if dumping not active.
-    SharedPtr<File> shaderDumpFile_;
-    /// Hash set for detecting already dumped variations.
-    HashSet<String> usedShaders_;
+    /// Shader precache utility.
+    SharedPtr<ShaderPrecache> shaderPrecache_;
 };
 
 /// Register Graphics library objects.

+ 5 - 0
Source/Engine/Graphics/OpenGL/OGLShaderVariation.cpp

@@ -161,4 +161,9 @@ void ShaderVariation::SetDefines(const String& defines)
     defines_ = defines;
 }
 
+Shader* ShaderVariation::GetOwner() const
+{
+    return owner_;
+}
+
 }

+ 2 - 0
Source/Engine/Graphics/OpenGL/OGLShaderVariation.h

@@ -54,6 +54,8 @@ public:
     /// Set defines.
     void SetDefines(const String& defines);
     
+    /// Return the owner resource.
+    Shader* GetOwner() const;
     /// Return shader type.
     ShaderType GetShaderType() const { return type_; }
     /// Return full shader name.

+ 118 - 0
Source/Engine/Graphics/ShaderPrecache.cpp

@@ -0,0 +1,118 @@
+//
+// Copyright (c) 2008-2014 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 "Precompiled.h"
+#include "File.h"
+#include "FileSystem.h"
+#include "Graphics.h"
+#include "Log.h"
+#include "ShaderPrecache.h"
+#include "ShaderVariation.h"
+
+#include "DebugNew.h"
+
+namespace Urho3D
+{
+
+ShaderPrecache::ShaderPrecache(Context* context, const String& fileName) :
+    Object(context),
+    fileName_(fileName),
+    xmlFile_(context)
+{
+    if (GetSubsystem<FileSystem>()->FileExists(fileName))
+    {
+        // If file exists, read the already listed combinations
+        File source(context_, fileName);
+        xmlFile_.Load(source);
+        
+        XMLElement shader = xmlFile_.GetRoot().GetChild("shader");
+        while (shader)
+        {
+            String oldCombination = shader.GetAttribute("vs") + " " + shader.GetAttribute("vsdefines") + " " +
+                shader.GetAttribute("ps") + " " + shader.GetAttribute("psdefines");
+            usedCombinations_.Insert(oldCombination);
+            
+            shader = shader.GetNext("shader");
+        }
+    }
+    else
+        xmlFile_.CreateRoot("shaders");
+    
+    LOGDEBUG("Begin dumping shaders to " + fileName_);
+}
+
+ShaderPrecache::~ShaderPrecache()
+{
+    LOGDEBUG("End dumping shaders");
+    
+    if (usedCombinations_.Empty())
+        return;
+    
+    File dest(context_, fileName_, FILE_WRITE);
+    xmlFile_.Save(dest);
+}
+
+void ShaderPrecache::StoreShaders(ShaderVariation* vs, ShaderVariation* ps)
+{
+    if (!vs || !ps)
+        return;
+    
+    String vsName = vs->GetName().Substring(0, vs->GetName().Find('_'));
+    String psName = ps->GetName().Substring(0, ps->GetName().Find('_'));
+    const String& vsDefines = vs->GetDefines();
+    const String& psDefines = ps->GetDefines();
+    
+    // Check for duplicate
+    String newCombination = vsName + " " + vsDefines + " " + psName + " " + psDefines;
+    if (usedCombinations_.Contains(newCombination))
+        return;
+    
+    XMLElement shaderElem = xmlFile_.GetRoot().CreateChild("shader");
+    shaderElem.SetAttribute("vs", vsName);
+    shaderElem.SetAttribute("vsdefines", vsDefines);
+    shaderElem.SetAttribute("ps", psName);
+    shaderElem.SetAttribute("psdefines", psDefines);
+    usedCombinations_.Insert(newCombination);
+}
+
+void ShaderPrecache::LoadShaders(Graphics* graphics, Deserializer& source)
+{
+    LOGDEBUG("Begin precaching shaders");
+    
+    XMLFile xmlFile(graphics->GetContext());
+    xmlFile.Load(source);
+    
+    XMLElement shader = xmlFile.GetRoot().GetChild("shader");
+    while (shader)
+    {
+        ShaderVariation* vs = graphics->GetShader(VS, shader.GetAttribute("vs"), shader.GetAttribute("vsdefines"));
+        ShaderVariation* ps = graphics->GetShader(PS, shader.GetAttribute("ps"), shader.GetAttribute("psdefines"));
+        // Set the shaders active to actually compile them
+        graphics->SetShaders(vs, ps);
+        
+        shader = shader.GetNext("shader");
+    }
+    
+    LOGDEBUG("End precaching shaders");
+}
+
+}

+ 61 - 0
Source/Engine/Graphics/ShaderPrecache.h

@@ -0,0 +1,61 @@
+//
+// Copyright (c) 2008-2014 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 "HashSet.h"
+#include "Object.h"
+#include "XMLFile.h"
+
+namespace Urho3D
+{
+
+class Graphics;
+class ShaderVariation;
+
+/// Utility class for collecting used shader combinations during runtime for precaching.
+class URHO3D_API ShaderPrecache : public Object
+{
+    OBJECT(ShaderPrecache);
+    
+public:
+    /// Construct and begin collecting shader combinations. Load existing combinations from XML if the file exists.
+    ShaderPrecache(Context* context, const String& fileName);
+    /// Destruct. Write the collected shaders to XML.
+    ~ShaderPrecache();
+    
+    /// Collect a shader combination. Called by Graphics when shaders have been set.
+    void StoreShaders(ShaderVariation* vs, ShaderVariation* ps);
+    
+    /// Load shaders from a XML file.
+    static void LoadShaders(Graphics* graphics, Deserializer& source);
+
+private:
+    /// XML file name.
+    String fileName_;
+    /// XML file.
+    XMLFile xmlFile_;
+    /// Already encountered shader combinations.
+    HashSet<String> usedCombinations_;
+};
+
+}

+ 2 - 4
Source/Engine/Resource/XMLFile.cpp

@@ -108,7 +108,7 @@ bool XMLFile::Load(Deserializer& source)
         XMLFile* inheritedXMLFile = cache->GetResource<XMLFile>(inherit);
         if (!inheritedXMLFile)
         {
-            LOGERRORF("Could not find inherit RenderPath XML file: %s", inherit.CString());
+            LOGERRORF("Could not find inherited XML file: %s", inherit.CString());
             return false;
         }
 
@@ -123,10 +123,8 @@ bool XMLFile::Load(Deserializer& source)
         cache->StoreResourceDependency(this, inherit);
 
         // Approximate patched data size
-        dataSize += inheritedXMLFile->GetRoot().GetUInt("dataSize");
+        dataSize += inheritedXMLFile->GetMemoryUse();
     }
-    else
-        rootElem.SetUInt("dataSize", dataSize);
 
     // Note: this probably does not reflect internal data structure size accurately
     SetMemoryUse(dataSize);