Przeglądaj źródła

Evaluate shader combinations on demand. Closes #89.
Fixed D3D9 window being borderless when it shouldn't be.

Lasse Öörni 12 lat temu
rodzic
commit
ffcbe42413

+ 5 - 5
Source/Engine/Graphics/Direct3D9/D3D9Graphics.cpp

@@ -315,10 +315,10 @@ bool Graphics::SetMode(int width, int height, bool fullscreen, bool borderless,
     }
     }
     
     
     // Fullscreen or Borderless can not be resizable
     // Fullscreen or Borderless can not be resizable
-    if(fullscreen || borderless)
+    if (fullscreen || borderless)
         resizable = false;
         resizable = false;
 
 
-    if(borderless)
+    if (borderless)
         fullscreen = false;
         fullscreen = false;
     
     
     multiSample = Clamp(multiSample, 1, (int)D3DMULTISAMPLE_16_SAMPLES);
     multiSample = Clamp(multiSample, 1, (int)D3DMULTISAMPLE_16_SAMPLES);
@@ -2235,9 +2235,9 @@ bool Graphics::OpenWindow(int width, int height, bool resizable, bool borderless
     if(!externalWindow_)
     if(!externalWindow_)
     {
     {
         unsigned flags = 0;
         unsigned flags = 0;
-        if(resizable)
+        if (resizable)
             flags |= SDL_WINDOW_RESIZABLE;
             flags |= SDL_WINDOW_RESIZABLE;
-        if(borderless)
+        if (borderless)
             flags |= SDL_WINDOW_BORDERLESS;
             flags |= SDL_WINDOW_BORDERLESS;
 
 
         impl_->window_ = SDL_CreateWindow(windowTitle_.CString(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, flags);
         impl_->window_ = SDL_CreateWindow(windowTitle_.CString(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, flags);
@@ -2282,7 +2282,7 @@ void Graphics::AdjustWindow(int& newWidth, int& newHeight, bool& newFullscreen,
             SDL_SetWindowSize(impl_->window_, newWidth, newHeight);
             SDL_SetWindowSize(impl_->window_, newWidth, newHeight);
 
 
         SDL_SetWindowFullscreen(impl_->window_, newFullscreen ? SDL_TRUE : SDL_FALSE);
         SDL_SetWindowFullscreen(impl_->window_, newFullscreen ? SDL_TRUE : SDL_FALSE);
-        SDL_SetWindowBordered(impl_->window_, newBorderless ? SDL_TRUE : SDL_FALSE);
+        SDL_SetWindowBordered(impl_->window_, newBorderless ? SDL_FALSE : SDL_TRUE);
     }
     }
     else
     else
     {
     {

+ 2 - 2
Source/Engine/Graphics/Direct3D9/D3D9Shader.cpp

@@ -162,12 +162,12 @@ bool Shader::Load(Deserializer& source)
     {
     {
         PROFILE(ParseShaderDefinition);
         PROFILE(ParseShaderDefinition);
         
         
-        if (!vsParser_.Parse(VS, shaders, globalDefines, globalDefineValues))
+        if (!vsParser_.Parse(VS, shaders, false, globalDefines, globalDefineValues))
         {
         {
             LOGERROR("VS: " + vsParser_.GetErrorMessage());
             LOGERROR("VS: " + vsParser_.GetErrorMessage());
             return false;
             return false;
         }
         }
-        if (!psParser_.Parse(PS, shaders, globalDefines, globalDefineValues))
+        if (!psParser_.Parse(PS, shaders, false, globalDefines, globalDefineValues))
         {
         {
             LOGERROR("PS: " + psParser_.GetErrorMessage());
             LOGERROR("PS: " + psParser_.GetErrorMessage());
             return false;
             return false;

+ 170 - 87
Source/Engine/Graphics/ShaderParser.cpp

@@ -27,7 +27,14 @@
 namespace Urho3D
 namespace Urho3D
 {
 {
 
 
-bool ShaderParser::Parse(ShaderType type, const XMLElement& element, const Vector<String>& globalDefines, const Vector<String>& globalDefineValues)
+ShaderParser::ShaderParser() :
+    maxCombinations_(0),
+    numVariationGroups_(0),
+    builtAll_(false)
+{
+}
+
+bool ShaderParser::Parse(ShaderType type, const XMLElement& element, bool buildAll, const Vector<String>& globalDefines, const Vector<String>& globalDefineValues)
 {
 {
     if (globalDefines.Size() != globalDefineValues.Size())
     if (globalDefines.Size() != globalDefineValues.Size())
     {
     {
@@ -40,6 +47,9 @@ bool ShaderParser::Parse(ShaderType type, const XMLElement& element, const Vecto
     errorMessage_.Clear();
     errorMessage_.Clear();
     options_.Clear();
     options_.Clear();
     combinations_.Clear();
     combinations_.Clear();
+    usedCombinations_.Clear();
+    failedCombinations_.Clear();
+    nameToIndex_.Clear();
     
     
     XMLElement shader = element.GetChild("shader");
     XMLElement shader = element.GetChild("shader");
     while (shader)
     while (shader)
@@ -49,7 +59,8 @@ bool ShaderParser::Parse(ShaderType type, const XMLElement& element, const Vecto
         {
         {
             if (!ParseOptions(shader))
             if (!ParseOptions(shader))
                 return false;
                 return false;
-            BuildCombinations();
+            if (buildAll)
+                BuildCombinations();
             return true;
             return true;
         }
         }
         
         
@@ -59,15 +70,29 @@ bool ShaderParser::Parse(ShaderType type, const XMLElement& element, const Vecto
     return true;
     return true;
 }
 }
 
 
-bool ShaderParser::HasCombination(const String& name) const
+bool ShaderParser::HasCombination(const String& name)
 {
 {
-    return combinations_.Contains(name);
+    if (builtAll_)
+        return combinations_.Contains(name);
+    else
+    {
+        if (combinations_.Contains(name))
+            return true;
+        else
+            return BuildCombination(name);
+    }
 }
 }
 
 
-ShaderCombination ShaderParser::GetCombination(const String& name) const
+ShaderCombination ShaderParser::GetCombination(const String& name)
 {
 {
     ShaderCombination dest;
     ShaderCombination dest;
     
     
+    if (!builtAll_ && !combinations_.Contains(name))
+    {
+        if (!BuildCombination(name))
+            return dest;
+    }
+
     HashMap<String, unsigned>::ConstIterator i = combinations_.Find(name);
     HashMap<String, unsigned>::ConstIterator i = combinations_.Find(name);
     if (i != combinations_.End())
     if (i != combinations_.End())
     {
     {
@@ -82,13 +107,13 @@ ShaderCombination ShaderParser::GetCombination(const String& name) const
                     dest.defines_.Push(options_[j].defines_[k]);
                     dest.defines_.Push(options_[j].defines_[k]);
                     dest.defineValues_.Push(options_[j].defineValues_[k]);
                     dest.defineValues_.Push(options_[j].defineValues_[k]);
                 }
                 }
-                for (unsigned k = 0; k < globalDefines_.Size(); ++k)
-                {
-                    dest.defines_.Push(globalDefines_[k]);
-                    dest.defineValues_.Push(globalDefineValues_[k]);
-                }
             }
             }
         }
         }
+        for (unsigned j = 0; j < globalDefines_.Size(); ++j)
+        {
+            dest.defines_.Push(globalDefines_[j]);
+            dest.defineValues_.Push(globalDefineValues_[j]);
+        }
     }
     }
 
 
     return dest;
     return dest;
@@ -196,22 +221,14 @@ bool ShaderParser::ParseOptions(const XMLElement& element)
         option = option.GetNext();
         option = option.GetNext();
     }
     }
     
     
-    return true;
-}
-
-void ShaderParser::BuildCombinations()
-{
-    unsigned combinations = 1;
-    unsigned numVariationGroups = 0;
-    HashSet<unsigned> usedCombinations;
-    HashMap<String, unsigned> nameToIndex;
-    
+    maxCombinations_ = 1;
+    numVariationGroups_ = 0;
     for (unsigned i = 0; i < options_.Size(); ++i)
     for (unsigned i = 0; i < options_.Size(); ++i)
     {
     {
-        combinations *= 2;
-        nameToIndex[options_[i].name_] = i;
+        maxCombinations_ *= 2;
+        nameToIndex_[options_[i].name_] = i;
         if (options_[i].isVariation_ && (i == 0 || !options_[i - 1].isVariation_))
         if (options_[i].isVariation_ && (i == 0 || !options_[i - 1].isVariation_))
-            ++numVariationGroups;
+            ++numVariationGroups_;
     }
     }
     
     
     // Preprocess includes/excludes for faster combination handling
     // Preprocess includes/excludes for faster combination handling
@@ -221,9 +238,9 @@ void ShaderParser::BuildCombinations()
         i->includeIndices_.Resize(i->includes_.Size());
         i->includeIndices_.Resize(i->includes_.Size());
         
         
         for (unsigned j = 0; j < i->excludes_.Size(); ++j)
         for (unsigned j = 0; j < i->excludes_.Size(); ++j)
-            i->excludeIndices_[j] = nameToIndex[i->excludes_[j]];
+            i->excludeIndices_[j] = nameToIndex_[i->excludes_[j]];
         for (unsigned j = 0; j < i->includes_.Size(); ++j)
         for (unsigned j = 0; j < i->includes_.Size(); ++j)
-            i->includeIndices_[j] = nameToIndex[i->includes_[j]];
+            i->includeIndices_[j] = nameToIndex_[i->includes_[j]];
     }
     }
     
     
     // Preprocess requirements
     // Preprocess requirements
@@ -269,87 +286,153 @@ void ShaderParser::BuildCombinations()
         }
         }
     }
     }
     
     
-    for (unsigned i = 0; i < combinations; ++i)
+    return true;
+}
+
+void ShaderParser::BuildCombinations()
+{
+    for (unsigned i = 0; i < maxCombinations_; ++i)
+        BuildCombination(i);
+
+    builtAll_ = true;
+}
+
+bool ShaderParser::BuildCombination(unsigned active)
+{
+    unsigned variationsActive = 0;
+    bool skipThis = false;
+    
+    // Check for excludes & includes first
+    for (unsigned j = 0; j < options_.Size(); ++j)
     {
     {
-        // Variations/options active on this particular combination
-        unsigned active = i;
-        unsigned variationsActive = 0;
-        bool skipThis = false;
-        
-        // Check for excludes & includes first
-        for (unsigned j = 0; j < options_.Size(); ++j)
+        if ((active >> j) & 1)
         {
         {
-            if ((active >> j) & 1)
-            {
-                for (unsigned k = 0; k < options_[j].includeIndices_.Size(); ++k)
-                    active |= 1 << options_[j].includeIndices_[k];
+            for (unsigned k = 0; k < options_[j].includeIndices_.Size(); ++k)
+                active |= 1 << options_[j].includeIndices_[k];
                 
                 
-                for (unsigned k = 0; k < options_[j].excludeIndices_.Size(); ++k)
-                    active &= ~(1 << options_[j].excludeIndices_[k]);
+            for (unsigned k = 0; k < options_[j].excludeIndices_.Size(); ++k)
+                active &= ~(1 << options_[j].excludeIndices_[k]);
                 
                 
-                // Skip dummy separators (options without name and defines)
-                if (options_[j].name_.Empty() && !options_[j].isVariation_ && options_[j].defines_.Empty())
-                    active &= ~(1 << j);
+            // Skip dummy separators (options without name and defines)
+            if (options_[j].name_.Empty() && !options_[j].isVariation_ && options_[j].defines_.Empty())
+                active &= ~(1 << j);
                 
                 
-                // If it's a variation, exclude all other variations in the same group
-                if (options_[j].isVariation_)
+            // If it's a variation, exclude all other variations in the same group
+            if (options_[j].isVariation_)
+            {
+                for (unsigned k = j - 1; k < options_.Size(); --k)
                 {
                 {
-                    for (unsigned k = j - 1; k < options_.Size(); --k)
-                    {
-                        if (options_[k].isVariation_)
-                            active &= ~(1 << k);
-                        else
-                            break;
-                    }
-                    for (unsigned k = j + 1; k < options_.Size(); ++k)
-                    {
-                        if (options_[k].isVariation_)
-                            active &= ~(1 << k);
-                        else
-                            break;
-                    }
-                    
-                    ++variationsActive;
+                    if (options_[k].isVariation_)
+                        active &= ~(1 << k);
+                    else
+                        break;
+                }
+                for (unsigned k = j + 1; k < options_.Size(); ++k)
+                {
+                    if (options_[k].isVariation_)
+                        active &= ~(1 << k);
+                    else
+                        break;
                 }
                 }
+                    
+                ++variationsActive;
             }
             }
         }
         }
-        
-        // Check that combination is correct: a variation chosen from all groups, and is unique
-        if (variationsActive < numVariationGroups || usedCombinations.Contains(active))
-            continue;
-        
-        // Check for required defines, which may yet cause this combination to be skipped
-        unsigned compareBits = active | 0x80000000;
-        for (unsigned j = 0; j < options_.Size(); ++j)
+    }
+    
+    // Check that combination is correct: a variation chosen from all groups, and is unique
+    if (variationsActive < numVariationGroups_ || usedCombinations_.Contains(active))
+        return false;
+    
+    // Check for required defines, which may yet cause this combination to be skipped
+    unsigned compareBits = active | 0x80000000;
+    for (unsigned j = 0; j < options_.Size(); ++j)
+    {
+        if (active & (1 << j))
         {
         {
-            if (active & (1 << j))
+            for (unsigned l = 0; l < options_[j].requirementBits_.Size(); ++l)
             {
             {
-                for (unsigned l = 0; l < options_[j].requirementBits_.Size(); ++l)
+                if (!(compareBits & options_[j].requirementBits_[l]))
                 {
                 {
-                    if (!(compareBits & options_[j].requirementBits_[l]))
-                    {
-                        skipThis = true;
-                        break;
-                    }
+                    skipThis = true;
+                    break;
                 }
                 }
             }
             }
         }
         }
+    }
         
         
-        if (skipThis)
-            continue;
-        
-        String combinationName;
-        
-        // Build shader combination name from active options
-        for (unsigned j = 0; j < options_.Size(); ++j)
+    if (skipThis)
+        return false;
+    
+    String combinationName;
+    
+    // Build shader combination name from active options
+    for (unsigned j = 0; j < options_.Size(); ++j)
+    {
+        if (active & (1 << j) && options_[j].name_.Length())
+            combinationName += options_[j].name_;
+    }
+    
+    combinations_[combinationName] = active;
+    usedCombinations_.Insert(active);
+    return true;
+}
+
+bool ShaderParser::BuildCombination(const String& name)
+{
+    // Do not attempt again if already failed
+    if (failedCombinations_.Contains(name))
+        return false;
+
+    // Decode active bits from the name
+    unsigned active = 0;
+    String nameCopy = name;
+    for (unsigned i = 0; i < options_.Size(); ++i)
+    {
+        // Option
+        if (!options_[i].isVariation_)
+        {
+            if (!options_[i].name_.Empty() && nameCopy.StartsWith(options_[i].name_))
+            {
+                /// \todo Hack fix for options like Alpha & AlphaMask appearing in that order. Not a 100% general fix
+                if (i < options_.Size() - 1 && nameCopy.StartsWith(options_[i + 1].name_))
+                    continue;
+
+                active |= 1 << i;
+                nameCopy = nameCopy.Substring(options_[i].name_.Length());
+            }
+        }
+        // Variation, must choose only one from the group, furthermore there can be empty variations with defines
+        else
         {
         {
-            if (active & (1 << j) && options_[j].name_.Length())
-                combinationName += options_[j].name_;
+            bool emptyFirstVariation = options_[i].name_.Empty();
+            bool variationFound = false;
+            unsigned j = i;
+            for (; j < options_.Size(); ++j)
+            {
+                // Reach end of group?
+                if (!options_[j].isVariation_)
+                    break;
+                if (!variationFound && !options_[j].name_.Empty() && nameCopy.StartsWith(options_[j].name_))
+                {
+                    variationFound = true;
+                    active |= 1 << j;
+                    nameCopy = nameCopy.Substring(options_[j].name_.Length());
+                }
+            }
+            // If no other variation was found, must choose the empty first variation, as it may have defines
+            if (emptyFirstVariation && !variationFound)
+                active |= 1 << i;
+            // Skip past the group
+            i = j - 1;
         }
         }
-        
-        combinations_[combinationName] = active;
-        usedCombinations.Insert(active);
     }
     }
+
+    bool success = BuildCombination(active);
+    if (!success)
+        failedCombinations_.Insert(name);
+
+    return success;
 }
 }
 
 
 }
 }

+ 28 - 10
Source/Engine/Graphics/ShaderParser.h

@@ -24,6 +24,7 @@
 
 
 #include "GraphicsDefs.h"
 #include "GraphicsDefs.h"
 #include "HashMap.h"
 #include "HashMap.h"
+#include "HashSet.h"
 
 
 namespace Urho3D
 namespace Urho3D
 {
 {
@@ -70,25 +71,30 @@ struct ShaderCombination
 class URHO3D_API ShaderParser
 class URHO3D_API ShaderParser
 {
 {
 public:
 public:
+    /// Construct with defaults.
+    ShaderParser();
+
     /// Parse from an XML element. Return true if successful.
     /// Parse from an XML element. Return true if successful.
-    bool Parse(ShaderType type, const XMLElement& element, const Vector<String>& globalDefines = Vector<String>(), const Vector<String>& globalDefineValues = Vector<String>());
+    bool Parse(ShaderType type, const XMLElement& element, bool buildAll = false, const Vector<String>& globalDefines = Vector<String>(), const Vector<String>& globalDefineValues = Vector<String>());
     
     
     /// Return error message if parsing failed.
     /// Return error message if parsing failed.
     String GetErrorMessage() const { return errorMessage_; }
     String GetErrorMessage() const { return errorMessage_; }
-    /// Return number of combinations.
-    unsigned GetNumCombinations() const { return combinations_.Size(); }
-   /// Return all combinations.
+    /// Return all combinations. Should only be called after building all combinations, because otherwise it only returns combinations built so far.
     const HashMap<String, unsigned>& GetAllCombinations() const { return combinations_; }
     const HashMap<String, unsigned>& GetAllCombinations() const { return combinations_; }
-    /// Return whether a shader combination exists.
-    bool HasCombination(const String& name) const;
-    /// Return a combination by name.
-    ShaderCombination GetCombination(const String& name) const;
+    /// Return whether a shader combination exists. Will be built on demand if all combinations not yet built.
+    bool HasCombination(const String& name);
+    /// Return a combination by name. Will be built on demand if all combinations not yet built.
+    ShaderCombination GetCombination(const String& name);
     
     
 private:
 private:
-    /// Parse options for a shader.
+    /// Parse options for a shader, then preprocess them for building combinations.
     bool ParseOptions(const XMLElement& element);
     bool ParseOptions(const XMLElement& element);
-    /// Construct shader combinations from the options.
+    /// Construct all shader combinations from the options. This is potentially slow
     void BuildCombinations();
     void BuildCombinations();
+    /// Construct one shader combination on demand based on active option bitmask. Return true if was valid.
+    bool BuildCombination(unsigned active);
+    /// Construct one shader combination on demand based on the name. Return true if was valid.
+    bool BuildCombination(const String& name);
     
     
     /// Error message.
     /// Error message.
     String errorMessage_;
     String errorMessage_;
@@ -98,8 +104,20 @@ private:
     Vector<String> globalDefineValues_;
     Vector<String> globalDefineValues_;
     /// Shader options.
     /// Shader options.
     Vector<ShaderOption> options_;
     Vector<ShaderOption> options_;
+    /// Option name to index mapping.
+    HashMap<String, unsigned> nameToIndex_;
     /// Available combinations.
     /// Available combinations.
     HashMap<String, unsigned> combinations_;
     HashMap<String, unsigned> combinations_;
+    /// Combinations that were attempted to be built, but were not found.
+    HashSet<String> failedCombinations_;
+    /// Bitmasks of already built valid combinations.
+    HashSet<unsigned> usedCombinations_;
+    /// Total number of possible combinations, including invalid ones.
+    unsigned maxCombinations_;
+    /// Number of variation groups.
+    unsigned numVariationGroups_;
+    /// All combinations built flag.
+    bool builtAll_;
 };
 };
 
 
 }
 }

+ 10 - 2
Source/Tools/ShaderCompiler/ShaderCompiler.cpp

@@ -290,9 +290,13 @@ void CompileShader(const String& fileName)
     if (compileVS_)
     if (compileVS_)
     {
     {
         ShaderParser vsParser;
         ShaderParser vsParser;
-        if (!vsParser.Parse(VS, shaders, defines_, defineValues_))
+        if (!vsParser.Parse(VS, shaders, !compileVariation_, defines_, defineValues_))
             ErrorExit("VS: " + vsParser.GetErrorMessage());
             ErrorExit("VS: " + vsParser.GetErrorMessage());
         
         
+        // If compiling a specific variation only, request it beforehand
+        if (compileVariation_)
+            vsParser.GetCombination(variationName_);
+
         const HashMap<String, unsigned>& combinations = vsParser.GetAllCombinations();
         const HashMap<String, unsigned>& combinations = vsParser.GetAllCombinations();
         for (HashMap<String, unsigned>::ConstIterator i = combinations.Begin(); i != combinations.End(); ++i)
         for (HashMap<String, unsigned>::ConstIterator i = combinations.Begin(); i != combinations.End(); ++i)
         {
         {
@@ -322,9 +326,13 @@ void CompileShader(const String& fileName)
     if (compilePS_)
     if (compilePS_)
     {
     {
         ShaderParser psParser;
         ShaderParser psParser;
-        if (!psParser.Parse(PS, shaders, defines_, defineValues_))
+        if (!psParser.Parse(PS, shaders, !compileVariation_, defines_, defineValues_))
             ErrorExit("PS: " + psParser.GetErrorMessage());
             ErrorExit("PS: " + psParser.GetErrorMessage());
         
         
+        // If compiling a specific variation only, request it beforehand
+        if (compileVariation_)
+            psParser.GetCombination(variationName_);
+
         const HashMap<String, unsigned>& combinations = psParser.GetAllCombinations();
         const HashMap<String, unsigned>& combinations = psParser.GetAllCombinations();
         for (HashMap<String, unsigned>::ConstIterator i = combinations.Begin(); i != combinations.End(); ++i)
         for (HashMap<String, unsigned>::ConstIterator i = combinations.Begin(); i != combinations.End(); ++i)
         {
         {