// Copyright (c) 2008-2023 the Urho3D project // License: MIT #include "../Precompiled.h" #include "../Graphics/Graphics.h" #include "../GraphicsAPI/GraphicsImpl.h" #include "../GraphicsAPI/ShaderPrecache.h" #include "../GraphicsAPI/ShaderVariation.h" #include "../IO/File.h" #include "../IO/FileSystem.h" #include "../IO/Log.h" #include "../DebugNew.h" namespace Urho3D { ShaderPrecache::ShaderPrecache(Context* context, const String& fileName) : Object(context), fileName_(fileName), xmlFile_(context) { if (GetSubsystem()->FileExists(fileName)) { // If file exists, read the already listed combinations File source(context_, fileName); xmlFile_.Load(source); XMLElement shader = xmlFile_.GetRoot().GetChild("shader"); while (shader) { String oldCombination = shader.GetAttribute("vs") + " " + shader.GetAttribute("vsdefines") + " " + shader.GetAttribute("ps") + " " + shader.GetAttribute("psdefines"); usedCombinations_.Insert(oldCombination); shader = shader.GetNext("shader"); } } // If no file yet or loading failed, create the root element now if (!xmlFile_.GetRoot()) xmlFile_.CreateRoot("shaders"); URHO3D_LOGINFO("Begin dumping shaders to " + fileName_); } ShaderPrecache::~ShaderPrecache() { URHO3D_LOGINFO("End dumping shaders"); if (usedCombinations_.Empty()) return; File dest(context_, fileName_, FILE_WRITE); xmlFile_.Save(dest); } void ShaderPrecache::StoreShaders(ShaderVariation* vs, ShaderVariation* ps) { if (!vs || !ps) return; // Check for duplicate using pointers first (fast) Pair shaderPair = MakePair(vs, ps); if (usedPtrCombinations_.Contains(shaderPair)) return; usedPtrCombinations_.Insert(shaderPair); String vsName = vs->GetName(); String psName = ps->GetName(); const String& vsDefines = vs->GetDefines(); const String& psDefines = ps->GetDefines(); // Check for duplicate using strings (needed for combinations loaded from existing file) String newCombination = vsName + " " + vsDefines + " " + psName + " " + psDefines; if (usedCombinations_.Contains(newCombination)) return; usedCombinations_.Insert(newCombination); XMLElement shaderElem = xmlFile_.GetRoot().CreateChild("shader"); shaderElem.SetAttribute("vs", vsName); shaderElem.SetAttribute("vsdefines", vsDefines); shaderElem.SetAttribute("ps", psName); shaderElem.SetAttribute("psdefines", psDefines); } void ShaderPrecache::LoadShaders(Graphics* graphics, Deserializer& source) { URHO3D_LOGDEBUG("Begin precaching shaders"); XMLFile xmlFile(graphics->GetContext()); xmlFile.Load(source); XMLElement shader = xmlFile.GetRoot().GetChild("shader"); while (shader) { String vsDefines = shader.GetAttribute("vsdefines"); String psDefines = shader.GetAttribute("psdefines"); // Check for illegal variations on OpenGL ES and skip them #if defined(GL_ES_VERSION_2_0) && !defined(GL_ES_VERSION_3_0) if ( #ifndef __EMSCRIPTEN__ vsDefines.Contains("INSTANCED") || #endif (psDefines.Contains("POINTLIGHT") && psDefines.Contains("SHADOW"))) { shader = shader.GetNext("shader"); continue; } #endif ShaderVariation* vs = graphics->GetShader(VS, shader.GetAttribute("vs"), vsDefines); ShaderVariation* ps = graphics->GetShader(PS, shader.GetAttribute("ps"), psDefines); // Set the shaders active to actually compile them graphics->SetShaders(vs, ps); shader = shader.GetNext("shader"); } URHO3D_LOGDEBUG("End precaching shaders"); } }