Browse Source

Shader precaching mechanism. Call Graphics::BeginDumpShaders() or use the engine command line parameter -ds <file> to create a shader dump file of the variations that are loaded during program run, and Graphics::PrecacheShaders() to load the dump file.

Moved GetShader() from Renderer subsystem to Graphics, which is more logical as loading shaders is a low-level operation, while Renderer should be concerned with high-level rendering of 3D scenes. Now eg. UI does not need to depend on Renderer at all.

Simplified internal state of ShaderVariation & ShaderProgram objects, the success of compile/link can be deduced from the presence of a non-null GPU object.

Fixed FILE_READWRITE mode to not clear the file contents, and to handle alternating reads and writes correctly.
Lasse Öörni 12 năm trước cách đây
mục cha
commit
abc6fcd8b8

+ 2 - 2
Bin/CoreData/Shaders/HLSL/Lighting.hlsl

@@ -151,7 +151,7 @@ float GetShadow(float4 shadowPos)
             tex2Dproj(sShadowMap, float4(shadowPos.x, shadowPos.y + offsets.y, shadowPos.zw)).r,
             tex2Dproj(sShadowMap, float4(shadowPos.x, shadowPos.y + offsets.y, shadowPos.zw)).r,
             tex2Dproj(sShadowMap, float4(shadowPos.xy + offsets.xy, shadowPos.zw)).r
             tex2Dproj(sShadowMap, float4(shadowPos.xy + offsets.xy, shadowPos.zw)).r
         );
         );
-        #ifdef HWSHADOW
+        #ifndef SHADOWCMP
             return cShadowIntensity.y + dot(inLight, cShadowIntensity.x);
             return cShadowIntensity.y + dot(inLight, cShadowIntensity.x);
         #else
         #else
             #ifndef POINTLIGHT
             #ifndef POINTLIGHT
@@ -163,7 +163,7 @@ float GetShadow(float4 shadowPos)
     #else
     #else
         // Take one sample
         // Take one sample
         float inLight = tex2Dproj(sShadowMap, shadowPos).r;
         float inLight = tex2Dproj(sShadowMap, shadowPos).r;
-        #ifdef HWSHADOW
+        #ifndef SHADOWCMP
             return cShadowIntensity.y + cShadowIntensity.x * inLight;
             return cShadowIntensity.y + cShadowIntensity.x * inLight;
         #else
         #else
             #ifndef POINTLIGHT
             #ifndef POINTLIGHT

+ 33 - 0
Bin/Data/NinjaSnowWarShaders.txt

@@ -0,0 +1,33 @@
+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_SHADOWBasic_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

+ 4 - 0
Bin/Data/Scripts/NinjaSnowWar.as

@@ -165,6 +165,10 @@ void InitScene()
             dirLight.shadowIntensity = 0.333f;
             dirLight.shadowIntensity = 0.333f;
         }
         }
     }
     }
+    
+    // Precache shaders if possible
+    if (cache.Exists("NinjaSnowWarShaders.txt"))
+        graphics.PrecacheShaders(cache.GetFile("NinjaSnowWarShaders.txt"));
 }
 }
 
 
 void InitNetworking()
 void InitNetworking()

+ 1 - 0
Docs/GettingStarted.dox

@@ -192,6 +192,7 @@ The engine can be configured using the following command line options.
 -r <freq>    Sound mixing frequency in Hz
 -r <freq>    Sound mixing frequency in Hz
 -p <paths>   Resource path(s) to use, separated by semicolons
 -p <paths>   Resource path(s) to use, separated by semicolons
 -log <level> Change the log level, valid 'level' values are 'debug', 'info', 'warning', 'error'
 -log <level> Change the log level, valid 'level' values are 'debug', 'info', 'warning', 'error'
+-ds <file>   Dump used shader variations to a file for precaching
 -borderless  Borderless window mode
 -borderless  Borderless window mode
 -headless    Headless mode. No application window will be created
 -headless    Headless mode. No application window will be created
 -prepass     Use light pre-pass rendering
 -prepass     Use light pre-pass rendering

+ 10 - 0
Docs/Reference.dox

@@ -143,6 +143,7 @@ The full list of supported parameters, their datatypes and default values:
 - TripleBuffer (bool) Whether to use triple-buffering. 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.
 - VSync (bool) Whether to wait for vertical sync when presenting rendering window contents. Default false.
 - Multisample (int) Hardware multisampling level. Default 1 (no multisampling.)
 - Multisample (int) Hardware multisampling level. Default 1 (no multisampling.)
+- DumpShaders (string) Filename to dump used shader variations to for precaching.
 - %RenderPath (string) Default renderpath resource name. Default empty, which causes forward rendering (Bin/CoreData/RenderPaths/Forward.xml) to be used.
 - %RenderPath (string) Default renderpath resource name. Default empty, which causes forward rendering (Bin/CoreData/RenderPaths/Forward.xml) to be used.
 - Shadows (bool) Shadow rendering enable. Default true.
 - Shadows (bool) Shadow rendering enable. Default true.
 - LowQualityShadows (bool) Low-quality (1 sample) shadow mode. Default false.
 - LowQualityShadows (bool) Low-quality (1 sample) shadow mode. Default false.
@@ -671,6 +672,7 @@ Graphics implements the low-level functionality:
 - Setting the screen mode
 - Setting the screen mode
 - Keeping track of GPU resources
 - Keeping track of GPU resources
 - Keeping track of rendering context state (current rendertarget, vertex and index buffers, textures, shaders and renderstates)
 - Keeping track of rendering context state (current rendertarget, vertex and index buffers, textures, shaders and renderstates)
+- Loading shaders
 - Performing primitive rendering operations
 - Performing primitive rendering operations
 - Handling lost device
 - Handling lost device
 
 
@@ -1040,6 +1042,14 @@ 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.
 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
+
+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.
+
+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.
+
 \page RenderPaths Render path
 \page RenderPaths Render path
 
 
 %Scene rendering and any post-processing on a Viewport is defined by its RenderPath object, which can either be read from an XML file or be created programmatically.
 %Scene rendering and any post-processing on a Viewport is defined by its RenderPath object, which can either be read from an XML file or be created programmatically.

+ 0 - 1
Source/Engine/Core/Object.h

@@ -23,7 +23,6 @@
 #pragma once
 #pragma once
 
 
 #include "LinkedList.h"
 #include "LinkedList.h"
-#include "Ptr.h"
 #include "Variant.h"
 #include "Variant.h"
 
 
 namespace Urho3D
 namespace Urho3D

+ 7 - 0
Source/Engine/Engine/Engine.cpp

@@ -275,6 +275,8 @@ bool Engine::Initialize(const VariantMap& parameters)
             GetParameter(parameters, "MultiSample", 1).GetInt()
             GetParameter(parameters, "MultiSample", 1).GetInt()
         ))
         ))
             return false;
             return false;
+        if (HasParameter(parameters, "DumpShaders"))
+            graphics->BeginDumpShaders(GetParameter(parameters, "DumpShaders", String::EMPTY).GetString());
 
 
         if (HasParameter(parameters, "RenderPath"))
         if (HasParameter(parameters, "RenderPath"))
             renderer->SetDefaultRenderPath(cache->GetResource<XMLFile>(GetParameter(parameters, "RenderPath").GetString()));
             renderer->SetDefaultRenderPath(cache->GetResource<XMLFile>(GetParameter(parameters, "RenderPath").GetString()));
@@ -702,6 +704,11 @@ VariantMap Engine::ParseParameters(const Vector<String>& arguments)
                 ret["ResourcePaths"] = value;
                 ret["ResourcePaths"] = value;
                 ++i;
                 ++i;
             }
             }
+            else if (argument == "ds" && !value.Empty())
+            {
+                ret["DumpShaders"] = value;
+                ++i;
+            }
             #ifdef ENABLE_TESTING
             #ifdef ENABLE_TESTING
             else if (argument == "timeout" && !value.Empty())
             else if (argument == "timeout" && !value.Empty())
             {
             {

+ 2 - 4
Source/Engine/Graphics/DebugRenderer.cpp

@@ -31,7 +31,6 @@
 #include "Light.h"
 #include "Light.h"
 #include "Polyhedron.h"
 #include "Polyhedron.h"
 #include "Profiler.h"
 #include "Profiler.h"
-#include "Renderer.h"
 #include "ResourceCache.h"
 #include "ResourceCache.h"
 #include "ShaderVariation.h"
 #include "ShaderVariation.h"
 #include "VertexBuffer.h"
 #include "VertexBuffer.h"
@@ -310,15 +309,14 @@ void DebugRenderer::Render()
         return;
         return;
 
 
     Graphics* graphics = GetSubsystem<Graphics>();
     Graphics* graphics = GetSubsystem<Graphics>();
-    Renderer* renderer = GetSubsystem<Renderer>();
 
 
     if (!graphics || graphics->IsDeviceLost())
     if (!graphics || graphics->IsDeviceLost())
         return;
         return;
 
 
     PROFILE(RenderDebugGeometry);
     PROFILE(RenderDebugGeometry);
 
 
-    ShaderVariation* vs = renderer->GetShader(VS, "Basic", "VERTEXCOLOR");
-    ShaderVariation* ps = renderer->GetShader(PS, "Basic", "VERTEXCOLOR");
+    ShaderVariation* vs = graphics->GetShader(VS, "Basic", "VERTEXCOLOR");
+    ShaderVariation* ps = graphics->GetShader(PS, "Basic", "VERTEXCOLOR");
     
     
     unsigned numVertices = (lines_.Size() + noDepthLines_.Size()) * 2;
     unsigned numVertices = (lines_.Size() + noDepthLines_.Size()) * 2;
     // Resize the vertex buffer if too small or much too large
     // Resize the vertex buffer if too small or much too large

+ 94 - 3
Source/Engine/Graphics/Direct3D9/D3D9Graphics.cpp

@@ -29,6 +29,7 @@
 #include "CustomGeometry.h"
 #include "CustomGeometry.h"
 #include "DebugRenderer.h"
 #include "DebugRenderer.h"
 #include "DecalSet.h"
 #include "DecalSet.h"
+#include "File.h"
 #include "Graphics.h"
 #include "Graphics.h"
 #include "GraphicsEvents.h"
 #include "GraphicsEvents.h"
 #include "GraphicsImpl.h"
 #include "GraphicsImpl.h"
@@ -39,6 +40,7 @@
 #include "ParticleEmitter.h"
 #include "ParticleEmitter.h"
 #include "ProcessUtils.h"
 #include "ProcessUtils.h"
 #include "Profiler.h"
 #include "Profiler.h"
+#include "ResourceCache.h"
 #include "Shader.h"
 #include "Shader.h"
 #include "ShaderVariation.h"
 #include "ShaderVariation.h"
 #include "Skybox.h"
 #include "Skybox.h"
@@ -205,7 +207,9 @@ Graphics::Graphics(Context* context) :
     numPrimitives_(0),
     numPrimitives_(0),
     numBatches_(0),
     numBatches_(0),
     maxScratchBufferRequest_(0),
     maxScratchBufferRequest_(0),
-    defaultTextureFilterMode_(FILTER_BILINEAR)
+    defaultTextureFilterMode_(FILTER_BILINEAR),
+    shaderPath_("Shaders/HLSL/"),
+    shaderExtension_(".hlsl")
 {
 {
     SetTextureUnitMappings();
     SetTextureUnitMappings();
     
     
@@ -1026,7 +1030,7 @@ void Graphics::SetShaders(ShaderVariation* vs, ShaderVariation* ps)
         }
         }
         
         
         // Create the shader now if not yet created. If already attempted, do not retry
         // Create the shader now if not yet created. If already attempted, do not retry
-        if (vs && !vs->IsCompiled())
+        if (vs && !vs->GetGPUObject())
         {
         {
             if (vs->GetCompilerOutput().Empty())
             if (vs->GetCompilerOutput().Empty())
             {
             {
@@ -1069,7 +1073,7 @@ void Graphics::SetShaders(ShaderVariation* vs, ShaderVariation* ps)
                 i->second_.register_ = M_MAX_UNSIGNED;
                 i->second_.register_ = M_MAX_UNSIGNED;
         }
         }
         
         
-        if (ps && !ps->IsCompiled())
+        if (ps && !ps->GetGPUObject())
         {
         {
             if (ps->GetCompilerOutput().Empty())
             if (ps->GetCompilerOutput().Empty())
             {
             {
@@ -1102,6 +1106,17 @@ void Graphics::SetShaders(ShaderVariation* vs, ShaderVariation* ps)
         
         
         pixelShader_ = 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);
+        }
+    }
 }
 }
 
 
 void Graphics::SetShaderParameter(StringHash param, const float* data, unsigned count)
 void Graphics::SetShaderParameter(StringHash param, const float* data, unsigned count)
@@ -1879,6 +1894,59 @@ void Graphics::SetForceSM2(bool enable)
         LOGERROR("Force Shader Model 2 can not be changed after setting the initial screen mode");
         LOGERROR("Force Shader Model 2 can not be changed after setting the initial screen mode");
 }
 }
 
 
+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());
+}
+
+void Graphics::EndDumpShaders()
+{
+    if (shaderDumpFile_)
+    {
+        LOGDEBUG("End dumping shaders");
+        shaderDumpFile_.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");
+}
+
 bool Graphics::IsInitialized() const
 bool Graphics::IsInitialized() const
 {
 {
     return impl_->window_ != 0 && impl_->GetDevice() != 0;
     return impl_->window_ != 0 && impl_->GetDevice() != 0;
@@ -1971,6 +2039,29 @@ unsigned Graphics::GetFormat(CompressedFormat format) const
     return 0;
     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
 VertexBuffer* Graphics::GetVertexBuffer(unsigned index) const
 {
 {
     return index < MAX_VERTEX_STREAMS ? vertexBuffers_[index] : 0;
     return index < MAX_VERTEX_STREAMS ? vertexBuffers_[index] : 0;

+ 26 - 1
Source/Engine/Graphics/Direct3D9/D3D9Graphics.h

@@ -24,6 +24,7 @@
 
 
 #include "ArrayPtr.h"
 #include "ArrayPtr.h"
 #include "Color.h"
 #include "Color.h"
+#include "HashSet.h"
 #include "Image.h"
 #include "Image.h"
 #include "Object.h"
 #include "Object.h"
 #include "Plane.h"
 #include "Plane.h"
@@ -33,11 +34,13 @@
 namespace Urho3D
 namespace Urho3D
 {
 {
 
 
+class File;
 class Image;
 class Image;
 class IndexBuffer;
 class IndexBuffer;
 class GPUObject;
 class GPUObject;
 class GraphicsImpl;
 class GraphicsImpl;
 class RenderSurface;
 class RenderSurface;
+class Shader;
 class ShaderVariation;
 class ShaderVariation;
 class Texture;
 class Texture;
 class Texture2D;
 class Texture2D;
@@ -211,6 +214,12 @@ public:
     void ResetStreamFrequencies();
     void ResetStreamFrequencies();
     /// Set force Shader Model 2 flag. Only effective before setting the initial screen mode.
     /// Set force Shader Model 2 flag. Only effective before setting the initial screen mode.
     void SetForceSM2(bool enable);
     void SetForceSM2(bool enable);
+    /// Begin dumping shader variation names to a file for precaching.
+    void BeginDumpShaders(const String& fileName);
+    /// End dumping shader variations names.
+    void EndDumpShaders();
+    /// Precache shader variations from a file containing rows of shader names, generated with BeginDumpShaders().
+    void PrecacheShaders(Deserializer& source);
     
     
     /// Return whether rendering initialized.
     /// Return whether rendering initialized.
     bool IsInitialized() const;
     bool IsInitialized() const;
@@ -278,7 +287,11 @@ public:
     IntVector2 GetDesktopResolution() const;
     IntVector2 GetDesktopResolution() const;
     /// Return hardware format for a compressed image format, or 0 if unsupported.
     /// Return hardware format for a compressed image format, or 0 if unsupported.
     unsigned GetFormat(CompressedFormat format) const;
     unsigned GetFormat(CompressedFormat format) const;
-    /// Return vertex buffer by index.
+    /// 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;
     VertexBuffer* GetVertexBuffer(unsigned index) const;
     /// Return current index buffer.
     /// Return current index buffer.
     IndexBuffer* GetIndexBuffer() const { return indexBuffer_; }
     IndexBuffer* GetIndexBuffer() const { return indexBuffer_; }
@@ -559,6 +572,18 @@ private:
     TextureFilterMode defaultTextureFilterMode_;
     TextureFilterMode defaultTextureFilterMode_;
     /// Remembered shader parameter sources.
     /// Remembered shader parameter sources.
     const void* shaderParameterSources_[MAX_SHADER_PARAMETER_GROUPS];
     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_;
+    /// File shader variations are dumped into. Null if dumping not active.
+    SharedPtr<File> shaderDumpFile_;
+    /// Hash set for detecting already dumped variations.
+    HashSet<String> usedShaders_;
 };
 };
 
 
 /// Register Graphics library objects.
 /// Register Graphics library objects.

+ 2 - 10
Source/Engine/Graphics/Direct3D9/D3D9ShaderVariation.cpp

@@ -42,8 +42,7 @@ namespace Urho3D
 ShaderVariation::ShaderVariation(Shader* owner, ShaderType type) :
 ShaderVariation::ShaderVariation(Shader* owner, ShaderType type) :
     GPUObject(owner->GetSubsystem<Graphics>()),
     GPUObject(owner->GetSubsystem<Graphics>()),
     owner_(owner),
     owner_(owner),
-    type_(type),
-    compiled_(false)
+    type_(type)
 {
 {
     for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
     for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
         useTextureUnit_[i] = false;
         useTextureUnit_[i] = false;
@@ -97,8 +96,6 @@ bool ShaderVariation::Create()
             (const DWORD*)&byteCode[0],
             (const DWORD*)&byteCode[0],
             (IDirect3DVertexShader9**)&object_)))
             (IDirect3DVertexShader9**)&object_)))
             compilerOutput_ = "Could not create vertex shader";
             compilerOutput_ = "Could not create vertex shader";
-        else
-            compiled_ = true;
     }
     }
     else
     else
     {
     {
@@ -106,11 +103,9 @@ bool ShaderVariation::Create()
             (const DWORD*)&byteCode[0],
             (const DWORD*)&byteCode[0],
             (IDirect3DPixelShader9**)&object_)))
             (IDirect3DPixelShader9**)&object_)))
             compilerOutput_ = "Could not create pixel shader";
             compilerOutput_ = "Could not create pixel shader";
-        else
-            compiled_ = true;
     }
     }
     
     
-    return compiled_;
+    return object_ != 0;
 }
 }
 
 
 void ShaderVariation::Release()
 void ShaderVariation::Release()
@@ -136,7 +131,6 @@ void ShaderVariation::Release()
         }
         }
         
         
         object_ = 0;
         object_ = 0;
-        compiled_ = false;
         compilerOutput_.Clear();
         compilerOutput_.Clear();
         
         
         for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
         for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
@@ -395,8 +389,6 @@ void ShaderVariation::SaveByteCode(const PODVector<unsigned>& byteCode, const St
 {
 {
     ResourceCache* cache = owner_->GetSubsystem<ResourceCache>();
     ResourceCache* cache = owner_->GetSubsystem<ResourceCache>();
     FileSystem* fileSystem = owner_->GetSubsystem<FileSystem>();
     FileSystem* fileSystem = owner_->GetSubsystem<FileSystem>();
-    if (!cache || !fileSystem)
-        return;
     
     
     String path = GetPath(cache->GetResourceFileName(owner_->GetName())) + "Cache/";
     String path = GetPath(cache->GetResourceFileName(owner_->GetName())) + "Cache/";
     String fullName = path + GetFileNameAndExtension(binaryShaderName);
     String fullName = path + GetFileNameAndExtension(binaryShaderName);

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

@@ -88,8 +88,6 @@ public:
     const String& GetName() const { return name_; }
     const String& GetName() const { return name_; }
     /// Return defines.
     /// Return defines.
     const String& GetDefines() const { return defines_; }
     const String& GetDefines() const { return defines_; }
-    /// Return whether successfully compiled.
-    bool IsCompiled() const { return compiled_; }
     /// Return compile error/warning string.
     /// Return compile error/warning string.
     const String& GetCompilerOutput() const { return compilerOutput_; }
     const String& GetCompilerOutput() const { return compilerOutput_; }
     /// Return whether uses a parameter.
     /// Return whether uses a parameter.
@@ -121,8 +119,6 @@ private:
     String defines_;
     String defines_;
     /// Shader compile error string.
     /// Shader compile error string.
     String compilerOutput_;
     String compilerOutput_;
-    /// Compiled flag.
-    bool compiled_;
     /// Shader parameters.
     /// Shader parameters.
     HashMap<StringHash, ShaderParameter> parameters_;
     HashMap<StringHash, ShaderParameter> parameters_;
     /// Texture unit use flags.
     /// Texture unit use flags.

+ 95 - 4
Source/Engine/Graphics/OpenGL/OGLGraphics.cpp

@@ -30,6 +30,7 @@
 #include "CustomGeometry.h"
 #include "CustomGeometry.h"
 #include "DebugRenderer.h"
 #include "DebugRenderer.h"
 #include "DecalSet.h"
 #include "DecalSet.h"
+#include "File.h"
 #include "Graphics.h"
 #include "Graphics.h"
 #include "GraphicsEvents.h"
 #include "GraphicsEvents.h"
 #include "GraphicsImpl.h"
 #include "GraphicsImpl.h"
@@ -42,6 +43,7 @@
 #include "ProcessUtils.h"
 #include "ProcessUtils.h"
 #include "Profiler.h"
 #include "Profiler.h"
 #include "RenderSurface.h"
 #include "RenderSurface.h"
+#include "ResourceCache.h"
 #include "Shader.h"
 #include "Shader.h"
 #include "ShaderProgram.h"
 #include "ShaderProgram.h"
 #include "ShaderVariation.h"
 #include "ShaderVariation.h"
@@ -188,7 +190,9 @@ Graphics::Graphics(Context* context_) :
     shadowMapFormat_(GL_DEPTH_COMPONENT16),
     shadowMapFormat_(GL_DEPTH_COMPONENT16),
     hiresShadowMapFormat_(GL_DEPTH_COMPONENT24),
     hiresShadowMapFormat_(GL_DEPTH_COMPONENT24),
     defaultTextureFilterMode_(FILTER_BILINEAR),
     defaultTextureFilterMode_(FILTER_BILINEAR),
-    releasingGPUObjects_(false)
+    releasingGPUObjects_(false),
+    shaderPath_("Shaders/GLSL/"),
+    shaderExtension_(".glsl")
 {
 {
     SetTextureUnitMappings();
     SetTextureUnitMappings();
     ResetCachedState();
     ResetCachedState();
@@ -1014,7 +1018,7 @@ void Graphics::SetShaders(ShaderVariation* vs, ShaderVariation* ps)
     ClearParameterSources();
     ClearParameterSources();
     
     
     // Compile the shaders now if not yet compiled. If already attempted, do not retry
     // Compile the shaders now if not yet compiled. If already attempted, do not retry
-    if (vs && !vs->IsCompiled())
+    if (vs && !vs->GetGPUObject())
     {
     {
         if (vs->GetCompilerOutput().Empty())
         if (vs->GetCompilerOutput().Empty())
         {
         {
@@ -1033,7 +1037,7 @@ void Graphics::SetShaders(ShaderVariation* vs, ShaderVariation* ps)
             vs = 0;
             vs = 0;
     }
     }
     
     
-    if (ps && !ps->IsCompiled())
+    if (ps && !ps->GetGPUObject())
     {
     {
         if (ps->GetCompilerOutput().Empty())
         if (ps->GetCompilerOutput().Empty())
         {
         {
@@ -1070,7 +1074,7 @@ void Graphics::SetShaders(ShaderVariation* vs, ShaderVariation* ps)
         if (i != shaderPrograms_.End())
         if (i != shaderPrograms_.End())
         {
         {
             // Use the existing linked program
             // Use the existing linked program
-            if (i->second_->IsLinked())
+            if (i->second_->GetGPUObject())
             {
             {
                 glUseProgram(i->second_->GetGPUObject());
                 glUseProgram(i->second_->GetGPUObject());
                 shaderProgram_ = i->second_;
                 shaderProgram_ = i->second_;
@@ -1105,6 +1109,18 @@ void Graphics::SetShaders(ShaderVariation* vs, ShaderVariation* ps)
             shaderPrograms_[combination] = newProgram;
             shaderPrograms_[combination] = newProgram;
         }
         }
     }
     }
+    
+
+    // 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);
+        }
+    }
 }
 }
 
 
 void Graphics::SetShaderParameter(StringHash param, const float* data, unsigned count)
 void Graphics::SetShaderParameter(StringHash param, const float* data, unsigned count)
@@ -1868,6 +1884,59 @@ 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());
+}
+
+void Graphics::EndDumpShaders()
+{
+    if (shaderDumpFile_)
+    {
+        LOGDEBUG("End dumping shaders");
+        shaderDumpFile_.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");
+}
+
 bool Graphics::IsInitialized() const
 bool Graphics::IsInitialized() const
 {
 {
     return impl_->window_ != 0;
     return impl_->window_ != 0;
@@ -1981,6 +2050,28 @@ unsigned Graphics::GetFormat(CompressedFormat format) const
     }
     }
 }
 }
 
 
+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
 VertexBuffer* Graphics::GetVertexBuffer(unsigned index) const
 {
 {

+ 26 - 2
Source/Engine/Graphics/OpenGL/OGLGraphics.h

@@ -25,7 +25,7 @@
 #include "ArrayPtr.h"
 #include "ArrayPtr.h"
 #include "Color.h"
 #include "Color.h"
 #include "GraphicsDefs.h"
 #include "GraphicsDefs.h"
-#include "HashMap.h"
+#include "HashSet.h"
 #include "Image.h"
 #include "Image.h"
 #include "Object.h"
 #include "Object.h"
 #include "Plane.h"
 #include "Plane.h"
@@ -34,11 +34,13 @@
 namespace Urho3D
 namespace Urho3D
 {
 {
 
 
+class File;
 class Image;
 class Image;
 class IndexBuffer;
 class IndexBuffer;
 class GPUObject;
 class GPUObject;
 class GraphicsImpl;
 class GraphicsImpl;
 class RenderSurface;
 class RenderSurface;
+class Shader;
 class ShaderProgram;
 class ShaderProgram;
 class ShaderVariation;
 class ShaderVariation;
 class Texture;
 class Texture;
@@ -217,6 +219,12 @@ public:
     void ResetStreamFrequencies();
     void ResetStreamFrequencies();
     /// Set force Shader Model 2 flag. No-op on OpenGL.
     /// Set force Shader Model 2 flag. No-op on OpenGL.
     void SetForceSM2(bool enable);
     void SetForceSM2(bool enable);
+    /// Begin dumping shader variation names to a file for precaching.
+    void BeginDumpShaders(const String& fileName);
+    /// End dumping shader variations names.
+    void EndDumpShaders();
+    /// Precache shader variations from a file containing rows of shader names, generated with BeginDumpShaders().
+    void PrecacheShaders(Deserializer& source);
 
 
     /// Return whether rendering initialized.
     /// Return whether rendering initialized.
     bool IsInitialized() const;
     bool IsInitialized() const;
@@ -286,7 +294,11 @@ public:
     IntVector2 GetDesktopResolution() const;
     IntVector2 GetDesktopResolution() const;
     /// Return hardware format for a compressed image format, or 0 if unsupported.
     /// Return hardware format for a compressed image format, or 0 if unsupported.
     unsigned GetFormat(CompressedFormat format) const;
     unsigned GetFormat(CompressedFormat format) const;
-    /// Return vertex buffer by index.
+    /// 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;
     VertexBuffer* GetVertexBuffer(unsigned index) const;
     /// Return index buffer.
     /// Return index buffer.
     IndexBuffer* GetIndexBuffer() const { return indexBuffer_; }
     IndexBuffer* GetIndexBuffer() const { return indexBuffer_; }
@@ -571,6 +583,18 @@ private:
     bool useClipPlane_;
     bool useClipPlane_;
     /// Releasing GPU objects flag.
     /// Releasing GPU objects flag.
     bool releasingGPUObjects_;
     bool releasingGPUObjects_;
+    /// 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_;
+    /// File shader variations are dumped into. Null if dumping not active.
+    SharedPtr<File> shaderDumpFile_;
+    /// Hash set for detecting already dumped variations.
+    HashSet<String> usedShaders_;
 };
 };
 
 
 /// Register Graphics library objects.
 /// Register Graphics library objects.

+ 6 - 7
Source/Engine/Graphics/OpenGL/OGLShaderProgram.cpp

@@ -34,8 +34,7 @@ namespace Urho3D
 ShaderProgram::ShaderProgram(Graphics* graphics, ShaderVariation* vertexShader, ShaderVariation* pixelShader) :
 ShaderProgram::ShaderProgram(Graphics* graphics, ShaderVariation* vertexShader, ShaderVariation* pixelShader) :
     GPUObject(graphics),
     GPUObject(graphics),
     vertexShader_(vertexShader),
     vertexShader_(vertexShader),
-    pixelShader_(pixelShader),
-    linked_(false)
+    pixelShader_(pixelShader)
 {
 {
     for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
     for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
         useTextureUnit_[i] = false;
         useTextureUnit_[i] = false;
@@ -53,7 +52,7 @@ void ShaderProgram::OnDeviceLost()
     if (graphics_ && graphics_->GetShaderProgram() == this)
     if (graphics_ && graphics_->GetShaderProgram() == this)
         graphics_->SetShaders(0, 0);
         graphics_->SetShaders(0, 0);
     
     
-    linked_ = false;
+
     linkerOutput_.Clear();
     linkerOutput_.Clear();
 }
 }
 
 
@@ -73,7 +72,6 @@ void ShaderProgram::Release()
         }
         }
         
         
         object_ = 0;
         object_ = 0;
-        linked_ = false;
         linkerOutput_.Clear();
         linkerOutput_.Clear();
         shaderParameters_.Clear();
         shaderParameters_.Clear();
         
         
@@ -121,18 +119,19 @@ bool ShaderProgram::Link()
     
     
     int linked, length;
     int linked, length;
     glGetProgramiv(object_, GL_LINK_STATUS, &linked);
     glGetProgramiv(object_, GL_LINK_STATUS, &linked);
-    linked_ = linked != 0;
-    if (!linked_)
+    if (!linked)
     {
     {
         glGetProgramiv(object_, GL_INFO_LOG_LENGTH, &length);
         glGetProgramiv(object_, GL_INFO_LOG_LENGTH, &length);
         linkerOutput_.Resize(length);
         linkerOutput_.Resize(length);
         int outLength;
         int outLength;
         glGetProgramInfoLog(object_, length, &outLength, &linkerOutput_[0]);
         glGetProgramInfoLog(object_, length, &outLength, &linkerOutput_[0]);
+        glDeleteProgram(object_);
+        object_ = 0;
     }
     }
     else
     else
         linkerOutput_.Clear();
         linkerOutput_.Clear();
     
     
-    if (!linked_)
+    if (!object_)
         return false;
         return false;
     
     
     const int MAX_PARAMETER_NAME_LENGTH = 256;
     const int MAX_PARAMETER_NAME_LENGTH = 256;

+ 0 - 4
Source/Engine/Graphics/OpenGL/OGLShaderProgram.h

@@ -69,8 +69,6 @@ public:
     bool HasTextureUnit(TextureUnit unit) const { return useTextureUnit_[unit]; }
     bool HasTextureUnit(TextureUnit unit) const { return useTextureUnit_[unit]; }
     /// Return the info for a shader parameter, or null if does not exist.
     /// Return the info for a shader parameter, or null if does not exist.
     const ShaderParameter* GetParameter(StringHash param) const;
     const ShaderParameter* GetParameter(StringHash param) const;
-    /// Return whether successfully linked.
-    bool IsLinked() const { return linked_; }
     /// Return linker output.
     /// Return linker output.
     const String& GetLinkerOutput() const { return linkerOutput_; }
     const String& GetLinkerOutput() const { return linkerOutput_; }
     
     
@@ -85,8 +83,6 @@ private:
     bool useTextureUnit_[MAX_TEXTURE_UNITS];
     bool useTextureUnit_[MAX_TEXTURE_UNITS];
     /// Shader link error string.
     /// Shader link error string.
     String linkerOutput_;
     String linkerOutput_;
-    /// Linked flag.
-    bool linked_;
 };
 };
 
 
 }
 }

+ 7 - 9
Source/Engine/Graphics/OpenGL/OGLShaderVariation.cpp

@@ -36,8 +36,7 @@ namespace Urho3D
 ShaderVariation::ShaderVariation(Shader* owner, ShaderType type) :
 ShaderVariation::ShaderVariation(Shader* owner, ShaderType type) :
     GPUObject(owner->GetSubsystem<Graphics>()),
     GPUObject(owner->GetSubsystem<Graphics>()),
     owner_(owner),
     owner_(owner),
-    type_(type),
-    compiled_(false)
+    type_(type)
 {
 {
 }
 }
 
 
@@ -49,8 +48,7 @@ ShaderVariation::~ShaderVariation()
 void ShaderVariation::OnDeviceLost()
 void ShaderVariation::OnDeviceLost()
 {
 {
     GPUObject::OnDeviceLost();
     GPUObject::OnDeviceLost();
-    
-    compiled_ = false;
+
     compilerOutput_.Clear();
     compilerOutput_.Clear();
     
     
     if (graphics_)
     if (graphics_)
@@ -81,7 +79,6 @@ void ShaderVariation::Release()
         }
         }
         
         
         object_ = 0;
         object_ = 0;
-        compiled_ = false;
         compilerOutput_.Clear();
         compilerOutput_.Clear();
         
         
         graphics_->CleanupShaderPrograms();
         graphics_->CleanupShaderPrograms();
@@ -91,7 +88,7 @@ void ShaderVariation::Release()
 bool ShaderVariation::Create()
 bool ShaderVariation::Create()
 {
 {
     Release();
     Release();
-    
+
     if (!owner_)
     if (!owner_)
     {
     {
         compilerOutput_ = "Owner shader has expired";
         compilerOutput_ = "Owner shader has expired";
@@ -139,18 +136,19 @@ bool ShaderVariation::Create()
     
     
     int compiled, length;
     int compiled, length;
     glGetShaderiv(object_, GL_COMPILE_STATUS, &compiled);
     glGetShaderiv(object_, GL_COMPILE_STATUS, &compiled);
-    compiled_ = compiled != 0;
-    if (!compiled_)
+    if (!compiled)
     {
     {
         glGetShaderiv(object_, GL_INFO_LOG_LENGTH, &length);
         glGetShaderiv(object_, GL_INFO_LOG_LENGTH, &length);
         compilerOutput_.Resize(length);
         compilerOutput_.Resize(length);
         int outLength;
         int outLength;
         glGetShaderInfoLog(object_, length, &outLength, &compilerOutput_[0]);
         glGetShaderInfoLog(object_, length, &outLength, &compilerOutput_[0]);
+        glDeleteShader(object_);
+        object_ = 0;
     }
     }
     else
     else
         compilerOutput_.Clear();
         compilerOutput_.Clear();
     
     
-    return compiled_;
+    return object_ != 0;
 }
 }
 
 
 void ShaderVariation::SetName(const String& name)
 void ShaderVariation::SetName(const String& name)

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

@@ -60,8 +60,6 @@ public:
     const String& GetName() const { return name_; }
     const String& GetName() const { return name_; }
     /// Return defines.
     /// Return defines.
     const String& GetDefines() const { return defines_; }
     const String& GetDefines() const { return defines_; }
-    /// Return whether successfully compiled.
-    bool IsCompiled() const { return compiled_; }
     /// Return compile error/warning string.
     /// Return compile error/warning string.
     const String& GetCompilerOutput() const { return compilerOutput_; }
     const String& GetCompilerOutput() const { return compilerOutput_; }
     
     
@@ -76,8 +74,6 @@ private:
     String defines_;
     String defines_;
     /// Shader compile error string.
     /// Shader compile error string.
     String compilerOutput_;
     String compilerOutput_;
-    /// Compiled flag.
-    bool compiled_;
 };
 };
 
 
 }
 }

+ 13 - 45
Source/Engine/Graphics/Renderer.cpp

@@ -38,7 +38,6 @@
 #include "RenderPath.h"
 #include "RenderPath.h"
 #include "ResourceCache.h"
 #include "ResourceCache.h"
 #include "Scene.h"
 #include "Scene.h"
-#include "Shader.h"
 #include "ShaderVariation.h"
 #include "ShaderVariation.h"
 #include "Technique.h"
 #include "Technique.h"
 #include "Texture2D.h"
 #include "Texture2D.h"
@@ -181,10 +180,10 @@ static const char* shadowVariations[] =
     ""
     ""
     #else
     #else
     // On Direct3D the quality is always "low" if not using hardware shadow compare
     // On Direct3D the quality is always "low" if not using hardware shadow compare
-    "",
-    "LQSHADOW HWSHADOW ",
-    "",
-    "HWSHADOW "
+    "SHADOWCMP",
+    "LQSHADOW",
+    "SHADOWCMP",
+    ""
     #endif
     #endif
 };
 };
 
 
@@ -288,14 +287,6 @@ Renderer::Renderer(Context* context) :
     shadersDirty_(true),
     shadersDirty_(true),
     initialized_(false)
     initialized_(false)
 {
 {
-    #ifndef USE_OPENGL
-    shaderPath_ = "Shaders/HLSL/";
-    shaderExtension_ = ".hlsl";
-    #else
-    shaderPath_ = "Shaders/GLSL/";
-    shaderExtension_ = ".glsl";
-    #endif
-
     SubscribeToEvent(E_SCREENMODE, HANDLER(Renderer, HandleScreenMode));
     SubscribeToEvent(E_SCREENMODE, HANDLER(Renderer, HandleScreenMode));
     SubscribeToEvent(E_GRAPHICSFEATURES, HANDLER(Renderer, HandleGraphicsFeatures));
     SubscribeToEvent(E_GRAPHICSFEATURES, HANDLER(Renderer, HandleGraphicsFeatures));
     
     
@@ -560,29 +551,6 @@ unsigned Renderer::GetNumOccluders(bool allViews) const
     return numOccluders;
     return numOccluders;
 }
 }
 
 
-
-ShaderVariation* Renderer::GetShader(ShaderType type, const String& name, const String& defines) const
-{
-    return GetShader(type, name.CString(), defines.CString());
-}
-
-ShaderVariation* Renderer::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;
-}
-
 void Renderer::Update(float timeStep)
 void Renderer::Update(float timeStep)
 {
 {
     PROFILE(UpdateViews);
     PROFILE(UpdateViews);
@@ -1237,8 +1205,8 @@ void Renderer::SetLightVolumeBatchShaders(Batch& batch, const String& vsName, co
         psi += DLPS_ORTHO;
         psi += DLPS_ORTHO;
     }
     }
     
     
-    batch.vertexShader_ = GetShader(VS, vsName, deferredLightVSVariations[vsi]);
-    batch.pixelShader_ = GetShader(PS, psName, deferredLightPSVariations_[psi]);
+    batch.vertexShader_ = graphics_->GetShader(VS, vsName, deferredLightVSVariations[vsi]);
+    batch.pixelShader_ = graphics_->GetShader(PS, psName, deferredLightPSVariations_[psi]);
 }
 }
 
 
 void Renderer::SetCullMode(CullMode mode, Camera* camera)
 void Renderer::SetCullMode(CullMode mode, Camera* camera)
@@ -1352,7 +1320,7 @@ void Renderer::OptimizeLightByStencil(Light* light, Camera* camera)
         graphics_->SetColorWrite(false);
         graphics_->SetColorWrite(false);
         graphics_->SetDepthWrite(false);
         graphics_->SetDepthWrite(false);
         graphics_->SetStencilTest(true, CMP_ALWAYS, OP_REF, OP_KEEP, OP_KEEP, lightStencilValue_);
         graphics_->SetStencilTest(true, CMP_ALWAYS, OP_REF, OP_KEEP, OP_KEEP, lightStencilValue_);
-        graphics_->SetShaders(GetShader(VS, "Stencil"), GetShader(PS, "Stencil"));
+        graphics_->SetShaders(graphics_->GetShader(VS, "Stencil"), graphics_->GetShader(PS, "Stencil"));
         graphics_->SetShaderParameter(VSP_VIEWPROJ, projection * view);
         graphics_->SetShaderParameter(VSP_VIEWPROJ, projection * view);
         graphics_->SetShaderParameter(VSP_MODEL, light->GetVolumeTransform(camera));
         graphics_->SetShaderParameter(VSP_MODEL, light->GetVolumeTransform(camera));
         
         
@@ -1546,7 +1514,7 @@ void Renderer::LoadPassShaders(Technique* tech, StringHash type)
             unsigned g = k / MAX_LIGHT_VS_VARIATIONS;
             unsigned g = k / MAX_LIGHT_VS_VARIATIONS;
             unsigned l = k % MAX_LIGHT_VS_VARIATIONS;
             unsigned l = k % MAX_LIGHT_VS_VARIATIONS;
             
             
-            vertexShaders[j] = GetShader(VS, pass->GetVertexShader(), pass->GetVertexShaderDefines() + " " +
+            vertexShaders[j] = graphics_->GetShader(VS, pass->GetVertexShader(), pass->GetVertexShaderDefines() + " " +
                 lightVSVariations[l] + geometryVSVariations[g] + heightFogVariations[h]);
                 lightVSVariations[l] + geometryVSVariations[g] + heightFogVariations[h]);
         }
         }
         for (unsigned j = 0; j < MAX_LIGHT_PS_VARIATIONS * 2; ++j)
         for (unsigned j = 0; j < MAX_LIGHT_PS_VARIATIONS * 2; ++j)
@@ -1555,11 +1523,11 @@ void Renderer::LoadPassShaders(Technique* tech, StringHash type)
             unsigned h = j / MAX_LIGHT_PS_VARIATIONS;
             unsigned h = j / MAX_LIGHT_PS_VARIATIONS;
             if (k & LPS_SHADOW)
             if (k & LPS_SHADOW)
             {
             {
-                pixelShaders[j] = GetShader(PS, pass->GetPixelShader(), pass->GetPixelShaderDefines() + " " +
+                pixelShaders[j] = graphics_->GetShader(PS, pass->GetPixelShader(), pass->GetPixelShaderDefines() + " " +
                     lightPSVariations[k] + shadowVariations[shadows] + heightFogVariations[h]);
                     lightPSVariations[k] + shadowVariations[shadows] + heightFogVariations[h]);
             }
             }
             else
             else
-                pixelShaders[j] = GetShader(PS, pass->GetPixelShader(), pass->GetPixelShaderDefines() + " " +
+                pixelShaders[j] = graphics_->GetShader(PS, pass->GetPixelShader(), pass->GetPixelShaderDefines() + " " +
                     lightPSVariations[k] + heightFogVariations[h]);
                     lightPSVariations[k] + heightFogVariations[h]);
         }
         }
     }
     }
@@ -1575,7 +1543,7 @@ void Renderer::LoadPassShaders(Technique* tech, StringHash type)
                 unsigned h = j / (MAX_GEOMETRYTYPES * MAX_VERTEXLIGHT_VS_VARIATIONS);
                 unsigned h = j / (MAX_GEOMETRYTYPES * MAX_VERTEXLIGHT_VS_VARIATIONS);
                 unsigned g = k / MAX_VERTEXLIGHT_VS_VARIATIONS;
                 unsigned g = k / MAX_VERTEXLIGHT_VS_VARIATIONS;
                 unsigned l = k % MAX_VERTEXLIGHT_VS_VARIATIONS;
                 unsigned l = k % MAX_VERTEXLIGHT_VS_VARIATIONS;
-                vertexShaders[j] = GetShader(VS, pass->GetVertexShader(), pass->GetVertexShaderDefines() + " " +
+                vertexShaders[j] = graphics_->GetShader(VS, pass->GetVertexShader(), pass->GetVertexShaderDefines() + " " +
                     vertexLightVSVariations[l] + geometryVSVariations[g] + heightFogVariations[h]);
                     vertexLightVSVariations[l] + geometryVSVariations[g] + heightFogVariations[h]);
             }
             }
         }
         }
@@ -1586,7 +1554,7 @@ void Renderer::LoadPassShaders(Technique* tech, StringHash type)
             {
             {
                 unsigned k = j % MAX_GEOMETRYTYPES;
                 unsigned k = j % MAX_GEOMETRYTYPES;
                 unsigned h = j / MAX_GEOMETRYTYPES;
                 unsigned h = j / MAX_GEOMETRYTYPES;
-                vertexShaders[j] = GetShader(VS, pass->GetVertexShader(), pass->GetVertexShaderDefines() + " " +
+                vertexShaders[j] = graphics_->GetShader(VS, pass->GetVertexShader(), pass->GetVertexShaderDefines() + " " +
                     geometryVSVariations[k] + heightFogVariations[h]);
                     geometryVSVariations[k] + heightFogVariations[h]);
             }
             }
         }
         }
@@ -1594,7 +1562,7 @@ void Renderer::LoadPassShaders(Technique* tech, StringHash type)
         pixelShaders.Resize(2);
         pixelShaders.Resize(2);
         for (unsigned j = 0; j < 2; ++j)
         for (unsigned j = 0; j < 2; ++j)
         {
         {
-            pixelShaders[j] = GetShader(PS, pass->GetPixelShader(), pass->GetPixelShaderDefines() + " " +
+            pixelShaders[j] = graphics_->GetShader(PS, pass->GetPixelShader(), pass->GetPixelShaderDefines() + " " +
                 heightFogVariations[j]);
                 heightFogVariations[j]);
         }
         }
     }
     }

+ 0 - 13
Source/Engine/Graphics/Renderer.h

@@ -43,7 +43,6 @@ class Graphics;
 class RenderPath;
 class RenderPath;
 class RenderSurface;
 class RenderSurface;
 class ResourceCache;
 class ResourceCache;
-class Shader;
 class Skeleton;
 class Skeleton;
 class OcclusionBuffer;
 class OcclusionBuffer;
 class Texture2D;
 class Texture2D;
@@ -287,10 +286,6 @@ public:
     TextureCube* GetIndirectionCubeMap() const { return indirectionCubeMap_; }
     TextureCube* GetIndirectionCubeMap() const { return indirectionCubeMap_; }
     /// Return the instancing vertex buffer
     /// Return the instancing vertex buffer
     VertexBuffer* GetInstancingBuffer() const { return dynamicInstancing_ ? instancingBuffer_ : (VertexBuffer*)0; }
     VertexBuffer* GetInstancingBuffer() const { return dynamicInstancing_ ? instancingBuffer_ : (VertexBuffer*)0; }
-    /// 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 the frame update parameters.
     /// Return the frame update parameters.
     const FrameInfo& GetFrameInfo() const { return frame_; }
     const FrameInfo& GetFrameInfo() const { return frame_; }
     
     
@@ -430,16 +425,8 @@ private:
     HashSet<Technique*> shaderErrorDisplayed_;
     HashSet<Technique*> shaderErrorDisplayed_;
     /// Mutex for shadow camera allocation.
     /// Mutex for shadow camera allocation.
     Mutex rendererMutex_;
     Mutex rendererMutex_;
-    /// Base directory for shaders.
-    String shaderPath_;
-    /// File extension for shaders.
-    String shaderExtension_;
     /// Current variation names for deferred light volume shaders.
     /// Current variation names for deferred light volume shaders.
     Vector<String> deferredLightPSVariations_;
     Vector<String> deferredLightPSVariations_;
-    /// Last used shader in shader variation query.
-    mutable WeakPtr<Shader> lastShader_;
-    /// Last used shader name in shader variation query.
-    mutable String lastShaderName_;
     /// Frame info for rendering.
     /// Frame info for rendering.
     FrameInfo frame_;
     FrameInfo frame_;
     /// Texture anisotropy level.
     /// Texture anisotropy level.

+ 3 - 3
Source/Engine/Graphics/View.cpp

@@ -1549,10 +1549,10 @@ void View::RenderQuad(RenderPathCommand& command)
         return;
         return;
     
     
     // If shader can not be found, clear it from the command to prevent redundant attempts
     // If shader can not be found, clear it from the command to prevent redundant attempts
-    ShaderVariation* vs = renderer_->GetShader(VS, command.vertexShaderName_, command.vertexShaderDefines_);
+    ShaderVariation* vs = graphics_->GetShader(VS, command.vertexShaderName_, command.vertexShaderDefines_);
     if (!vs)
     if (!vs)
         command.vertexShaderName_ = String::EMPTY;
         command.vertexShaderName_ = String::EMPTY;
-    ShaderVariation* ps = renderer_->GetShader(PS, command.pixelShaderName_, command.pixelShaderDefines_);
+    ShaderVariation* ps = graphics_->GetShader(PS, command.pixelShaderName_, command.pixelShaderDefines_);
     if (!ps)
     if (!ps)
         command.pixelShaderName_ = String::EMPTY;
         command.pixelShaderName_ = String::EMPTY;
     
     
@@ -1807,7 +1807,7 @@ void View::BlitFramebuffer(Texture2D* source, RenderSurface* destination, bool d
     graphics_->SetViewport(viewRect_);
     graphics_->SetViewport(viewRect_);
     
     
     String shaderName = "CopyFramebuffer";
     String shaderName = "CopyFramebuffer";
-    graphics_->SetShaders(renderer_->GetShader(VS, shaderName), renderer_->GetShader(PS, shaderName));
+    graphics_->SetShaders(graphics_->GetShader(VS, shaderName), graphics_->GetShader(PS, shaderName));
     
     
     float rtWidth = (float)rtSize_.x_;
     float rtWidth = (float)rtSize_.x_;
     float rtHeight = (float)rtSize_.y_;
     float rtHeight = (float)rtSize_.y_;

+ 44 - 4
Source/Engine/IO/File.cpp

@@ -41,6 +41,7 @@ static const wchar_t* openMode[] =
 {
 {
     L"rb",
     L"rb",
     L"wb",
     L"wb",
+    L"r+b",
     L"w+b"
     L"w+b"
 };
 };
 #else
 #else
@@ -48,6 +49,7 @@ static const char* openMode[] =
 {
 {
     "rb",
     "rb",
     "wb",
     "wb",
+    "r+b",
     "w+b"
     "w+b"
 };
 };
 #endif
 #endif
@@ -66,7 +68,9 @@ File::File(Context* context) :
     readBufferSize_(0),
     readBufferSize_(0),
     offset_(0),
     offset_(0),
     checksum_(0),
     checksum_(0),
-    compressed_(false)
+    compressed_(false),
+    readSyncNeeded_(false),
+    writeSyncNeeded_(false)
 {
 {
 }
 }
 
 
@@ -81,7 +85,9 @@ File::File(Context* context, const String& fileName, FileMode mode) :
     readBufferSize_(0),
     readBufferSize_(0),
     offset_(0),
     offset_(0),
     checksum_(0),
     checksum_(0),
-    compressed_(false)
+    compressed_(false),
+    readSyncNeeded_(false),
+    writeSyncNeeded_(false)
 {
 {
     Open(fileName, mode);
     Open(fileName, mode);
 }
 }
@@ -97,7 +103,9 @@ File::File(Context* context, PackageFile* package, const String& fileName) :
     readBufferSize_(0),
     readBufferSize_(0),
     offset_(0),
     offset_(0),
     checksum_(0),
     checksum_(0),
-    compressed_(false)
+    compressed_(false),
+    readSyncNeeded_(false),
+    writeSyncNeeded_(false)
 {
 {
     Open(package, fileName);
     Open(package, fileName);
 }
 }
@@ -161,6 +169,16 @@ bool File::Open(const String& fileName, FileMode mode)
     handle_ = fopen(GetNativePath(fileName).CString(), openMode[mode]);
     handle_ = fopen(GetNativePath(fileName).CString(), openMode[mode]);
     #endif
     #endif
 
 
+    // If file did not exist in readwrite mode, retry with write-update mode
+    if (mode == FILE_READWRITE && !handle_)
+    {
+        #ifdef WIN32
+        handle_ = _wfopen(GetWideNativePath(fileName).CString(), openMode[mode + 1]);
+        #else
+        handle_ = fopen(GetNativePath(fileName).CString(), openMode[mode + 1]);
+        #endif
+    }
+    
     if (!handle_)
     if (!handle_)
     {
     {
         LOGERROR("Could not open file " + fileName);
         LOGERROR("Could not open file " + fileName);
@@ -173,6 +191,8 @@ bool File::Open(const String& fileName, FileMode mode)
     offset_ = 0;
     offset_ = 0;
     checksum_ = 0;
     checksum_ = 0;
     compressed_ = false;
     compressed_ = false;
+    readSyncNeeded_ = false;
+    writeSyncNeeded_ = false;
 
 
     fseek((FILE*)handle_, 0, SEEK_END);
     fseek((FILE*)handle_, 0, SEEK_END);
     size_ = ftell((FILE*)handle_);
     size_ = ftell((FILE*)handle_);
@@ -209,7 +229,9 @@ bool File::Open(PackageFile* package, const String& fileName)
     position_ = 0;
     position_ = 0;
     size_ = entry->size_;
     size_ = entry->size_;
     compressed_ = package->IsCompressed();
     compressed_ = package->IsCompressed();
-
+    readSyncNeeded_ = false;
+    writeSyncNeeded_ = false;
+    
     fseek((FILE*)handle_, offset_, SEEK_SET);
     fseek((FILE*)handle_, offset_, SEEK_SET);
     return true;
     return true;
 }
 }
@@ -304,6 +326,13 @@ unsigned File::Read(void* dest, unsigned size)
         return size;
         return size;
     }
     }
 
 
+    // Need to reassign the position due to internal buffering when transitioning from writing to reading
+    if (readSyncNeeded_)
+    {
+        fseek((FILE*)handle_, position_ + offset_, SEEK_SET);
+        readSyncNeeded_ = false;
+    }
+    
     size_t ret = fread(dest, size, 1, (FILE*)handle_);
     size_t ret = fread(dest, size, 1, (FILE*)handle_);
     if (ret != 1)
     if (ret != 1)
     {
     {
@@ -313,6 +342,7 @@ unsigned File::Read(void* dest, unsigned size)
         return 0;
         return 0;
     }
     }
 
 
+    writeSyncNeeded_ = true;
     position_ += size;
     position_ += size;
     return size;
     return size;
 }
 }
@@ -368,6 +398,8 @@ unsigned File::Seek(unsigned position)
 
 
     fseek((FILE*)handle_, position + offset_, SEEK_SET);
     fseek((FILE*)handle_, position + offset_, SEEK_SET);
     position_ = position;
     position_ = position;
+    readSyncNeeded_ = false;
+    writeSyncNeeded_ = false;
     return position_;
     return position_;
 }
 }
 
 
@@ -388,6 +420,13 @@ unsigned File::Write(const void* data, unsigned size)
     if (!size)
     if (!size)
         return 0;
         return 0;
 
 
+    // Need to reassign the position due to internal buffering when transitioning from reading to writing
+    if (writeSyncNeeded_)
+    {
+        fseek((FILE*)handle_, position_ + offset_, SEEK_SET);
+        writeSyncNeeded_ = false;
+    }
+    
     if (fwrite(data, size, 1, (FILE*)handle_) != 1)
     if (fwrite(data, size, 1, (FILE*)handle_) != 1)
     {
     {
         // Return to the position where the write began
         // Return to the position where the write began
@@ -396,6 +435,7 @@ unsigned File::Write(const void* data, unsigned size)
         return 0;
         return 0;
     }
     }
 
 
+    readSyncNeeded_ = true;
     position_ += size;
     position_ += size;
     if (position_ > size_)
     if (position_ > size_)
         size_ = position_;
         size_ = position_;

+ 4 - 0
Source/Engine/IO/File.h

@@ -115,6 +115,10 @@ private:
     unsigned checksum_;
     unsigned checksum_;
     /// Compression flag.
     /// Compression flag.
     bool compressed_;
     bool compressed_;
+    /// Synchronization needed before read -flag.
+    bool readSyncNeeded_;
+    /// Synchronization needed before write -flag.
+    bool writeSyncNeeded_;
 };
 };
 
 
 }
 }

+ 16 - 1
Source/Engine/LuaScript/pkgs/Graphics/Graphics.pkg

@@ -1,3 +1,4 @@
+$#include "File.h"
 $#include "Graphics.h"
 $#include "Graphics.h"
 
 
 class Graphics : public Object
 class Graphics : public Object
@@ -17,7 +18,11 @@ class Graphics : public Object
     void Minimize();
     void Minimize();
     void Close();
     void Close();
     bool TakeScreenShot(Image& destImage);
     bool TakeScreenShot(Image& destImage);
-    
+    void BeginDumpShaders(const String fileName);
+    void EndDumpShaders();
+    void PrecacheShaders(Deserializer& source);
+    tolua_outside void GraphicsPrecacheShaders @ PrecacheShaders(const String fileName);
+
     bool IsInitialized() const;
     bool IsInitialized() const;
     void* GetExternalWindow() const;
     void* GetExternalWindow() const;
     const String GetWindowTitle() const;
     const String GetWindowTitle() const;
@@ -84,6 +89,16 @@ Graphics* GetGraphics();
 tolua_readonly tolua_property__get_set Graphics* graphics;
 tolua_readonly tolua_property__get_set Graphics* graphics;
 
 
 ${
 ${
+static void GraphicsPrecacheShaders(Graphics* graphics, const String& fileName)
+{
+    if (!graphics)
+        return;
+
+    File file(graphics->GetContext());
+    if (file.Open(fileName, FILE_READ))
+        graphics->PrecacheShaders(file);
+}
+
 #define TOLUA_DISABLE_tolua_GraphicsLuaAPI_GetGraphics00
 #define TOLUA_DISABLE_tolua_GraphicsLuaAPI_GetGraphics00
 static int tolua_GraphicsLuaAPI_GetGraphics00(lua_State* tolua_S)
 static int tolua_GraphicsLuaAPI_GetGraphics00(lua_State* tolua_S)
 {
 {

+ 9 - 0
Source/Engine/Script/GraphicsAPI.cpp

@@ -1210,6 +1210,12 @@ static CScriptArray* GraphicsGetMultiSampleLevels(Graphics* ptr)
     return VectorToArray<int>(ptr->GetMultiSampleLevels(), "Array<int>");
     return VectorToArray<int>(ptr->GetMultiSampleLevels(), "Array<int>");
 }
 }
 
 
+static void GraphicsPrecacheShaders(File* file, Graphics* ptr)
+{
+    if (file)
+        ptr->PrecacheShaders(*file);
+}
+
 static Graphics* GetGraphics()
 static Graphics* GetGraphics()
 {
 {
     return GetScriptContext()->GetSubsystem<Graphics>();
     return GetScriptContext()->GetSubsystem<Graphics>();
@@ -1226,6 +1232,9 @@ static void RegisterGraphics(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Graphics", "void Minimize()", asMETHOD(Graphics, Minimize), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "void Minimize()", asMETHOD(Graphics, Minimize), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "void Close()", asMETHOD(Graphics, Close), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "void Close()", asMETHOD(Graphics, Close), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "bool TakeScreenShot(Image@+)", asMETHOD(Graphics, TakeScreenShot), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "bool TakeScreenShot(Image@+)", asMETHOD(Graphics, TakeScreenShot), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Graphics", "void BeginDumpShaders(const String&in)", asMETHOD(Graphics, BeginDumpShaders), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Graphics", "void EndDumpShaders()", asMETHOD(Graphics, EndDumpShaders), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Graphics", "void PrecacheShaders(File@+)", asFUNCTION(GraphicsPrecacheShaders), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("Graphics", "void set_windowTitle(const String&in)", asMETHOD(Graphics, SetWindowTitle), asCALL_THISCALL);
     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_windowTitle() const", asMETHOD(Graphics, GetWindowTitle), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "void set_windowIcon(Image@+)", asMETHOD(Graphics, SetWindowIcon), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "void set_windowIcon(Image@+)", asMETHOD(Graphics, SetWindowIcon), asCALL_THISCALL);

+ 7 - 9
Source/Engine/UI/UI.cpp

@@ -38,7 +38,6 @@
 #include "Matrix3x4.h"
 #include "Matrix3x4.h"
 #include "MessageBox.h"
 #include "MessageBox.h"
 #include "Profiler.h"
 #include "Profiler.h"
-#include "Renderer.h"
 #include "ResourceCache.h"
 #include "ResourceCache.h"
 #include "ScrollBar.h"
 #include "ScrollBar.h"
 #include "Shader.h"
 #include "Shader.h"
@@ -654,8 +653,7 @@ void UI::Render(VertexBuffer* buffer, const PODVector<UIBatch>& batches, unsigne
     // Engine does not render when window is closed or device is lost
     // Engine does not render when window is closed or device is lost
     assert(graphics_ && graphics_->IsInitialized() && !graphics_->IsDeviceLost());
     assert(graphics_ && graphics_->IsInitialized() && !graphics_->IsDeviceLost());
 
 
-    Renderer* renderer = GetSubsystem<Renderer>();
-    if (!renderer || batches.Empty())
+    if (batches.Empty())
         return;
         return;
 
 
     Vector2 invScreenSize(1.0f / (float)graphics_->GetWidth(), 1.0f / (float)graphics_->GetHeight());
     Vector2 invScreenSize(1.0f / (float)graphics_->GetWidth(), 1.0f / (float)graphics_->GetHeight());
@@ -679,12 +677,12 @@ void UI::Render(VertexBuffer* buffer, const PODVector<UIBatch>& batches, unsigne
     graphics_->ResetRenderTargets();
     graphics_->ResetRenderTargets();
     graphics_->SetVertexBuffer(buffer);
     graphics_->SetVertexBuffer(buffer);
 
 
-    ShaderVariation* noTextureVS = renderer->GetShader(VS, "Basic", "VERTEXCOLOR");
-    ShaderVariation* diffTextureVS = renderer->GetShader(VS, "Basic", "DIFFMAP VERTEXCOLOR");
-    ShaderVariation* noTexturePS = renderer->GetShader(PS, "Basic", "VERTEXCOLOR");
-    ShaderVariation* diffTexturePS = renderer->GetShader(PS, "Basic", "DIFFMAP VERTEXCOLOR");
-    ShaderVariation* diffMaskTexturePS = renderer->GetShader(PS, "Basic", "DIFFMAP ALPHAMASK VERTEXCOLOR");
-    ShaderVariation* alphaTexturePS = renderer->GetShader(PS, "Basic", "ALPHAMAP VERTEXCOLOR");
+    ShaderVariation* noTextureVS = graphics_->GetShader(VS, "Basic", "VERTEXCOLOR");
+    ShaderVariation* diffTextureVS = graphics_->GetShader(VS, "Basic", "DIFFMAP VERTEXCOLOR");
+    ShaderVariation* noTexturePS = graphics_->GetShader(PS, "Basic", "VERTEXCOLOR");
+    ShaderVariation* diffTexturePS = graphics_->GetShader(PS, "Basic", "DIFFMAP VERTEXCOLOR");
+    ShaderVariation* diffMaskTexturePS = graphics_->GetShader(PS, "Basic", "DIFFMAP ALPHAMASK VERTEXCOLOR");
+    ShaderVariation* alphaTexturePS = graphics_->GetShader(PS, "Basic", "ALPHAMAP VERTEXCOLOR");
 
 
     unsigned alphaFormat = Graphics::GetAlphaFormat();
     unsigned alphaFormat = Graphics::GetAlphaFormat();
 
 

+ 1 - 0
Source/Tools/Urho3DPlayer/Urho3DPlayer.cpp

@@ -93,6 +93,7 @@ void Urho3DPlayer::Setup()
             "-r <freq>    Sound mixing frequency in Hz\n"
             "-r <freq>    Sound mixing frequency in Hz\n"
             "-p <paths>   Resource path(s) to use, separated by semicolons\n"
             "-p <paths>   Resource path(s) to use, separated by semicolons\n"
             "-log <level> Change the log level, valid 'level' values are 'debug', 'info', 'warning', 'error'\n"
             "-log <level> Change the log level, valid 'level' values are 'debug', 'info', 'warning', 'error'\n"
+            "-ds <file>   Dump used shader variations to a file for precaching\n"
             "-borderless  Borderless window mode\n"
             "-borderless  Borderless window mode\n"
             "-headless    Headless mode. No application window will be created\n"
             "-headless    Headless mode. No application window will be created\n"
             "-prepass     Use light pre-pass rendering\n"
             "-prepass     Use light pre-pass rendering\n"