4 using System.Collections.Generic;
8 using SiliconStudio.Core;
9 using SiliconStudio.Core.Diagnostics;
10 using SiliconStudio.Core.IO;
11 using SiliconStudio.Paradox.Games;
12 using SiliconStudio.Paradox.Graphics;
13 using SiliconStudio.Paradox.Shaders;
14 using SiliconStudio.Paradox.Shaders.Compiler;
16 namespace SiliconStudio.
Paradox.Effects
23 #region Private static members
25 public static readonly
string DefaultSourceShaderFolder =
"shaders";
29 #region Private members
31 private static Logger Log = GlobalLogger.GetLogger(
"EffectSystem");
35 private Dictionary<EffectBytecode, Effect> cachedEffects =
new Dictionary<EffectBytecode, Effect>();
38 private readonly List<EffectUpdateInfos> effectsToRecompile =
new List<EffectUpdateInfos>();
39 private readonly List<EffectUpdateInfos> effectsToUpdate =
new List<EffectUpdateInfos>();
40 private readonly List<Effect> updatedEffects =
new List<Effect>();
41 private readonly HashSet<string> modifiedShaders =
new HashSet<string>();
42 private readonly HashSet<string> recentlyModifiedShaders =
new HashSet<string>();
43 private bool clearNextFrame =
false;
47 #region Public members
70 #region Public methods
77 #if SILICONSTUDIO_PLATFORM_WINDOWS_DESKTOP
79 effectCompiler.SourceDirectories.Add(DefaultSourceShaderFolder);
83 directoryWatcher.Modified += FileModifiedEvent;
94 base.Update(gameTime);
99 foreach (var effect
in updatedEffects)
101 effect.Changed =
false;
103 updatedEffects.Clear();
105 clearNextFrame =
false;
108 lock (effectsToUpdate)
110 if (effectsToUpdate.Count > 0)
112 foreach (var effectToUpdate
in effectsToUpdate)
113 UpdateEffect(effectToUpdate);
114 clearNextFrame =
true;
116 effectsToUpdate.Clear();
129 if (effectName == null)
throw new ArgumentNullException(
"effectName");
130 if (compilerParameters == null)
throw new ArgumentNullException(
"compilerParameters");
134 var compilerResult = GetCompilerResults(effectName, compilerParameters, out subEffect);
135 CheckResult(compilerResult);
137 if (!compilerResult.Bytecodes.ContainsKey(subEffect))
139 throw new InvalidOperationException(
string.Format(
"Unable to find sub effect [{0}] from effect [{1}]", subEffect, effectName));
143 var bytecode = compilerResult.Bytecodes[subEffect];
147 bytecode.Name = effectName;
149 return CreateEffect(bytecode, compilerResult.UsedParameters[subEffect]);
161 if (effectName == null)
throw new ArgumentNullException(
"effectName");
162 if (compilerParameters == null)
throw new ArgumentNullException(
"compilerParameters");
165 var compilerResult = GetCompilerResults(effectName, compilerParameters, out subEffect);
166 CheckResult(compilerResult);
168 var result =
new Dictionary<string, Effect>();
170 foreach (var byteCodePair
in compilerResult.Bytecodes)
172 var bytecode = byteCodePair.Value;
173 bytecode.Name = effectName;
175 result.Add(byteCodePair.Key, CreateEffect(bytecode, compilerResult.UsedParameters[byteCodePair.Key]));
182 #region Private methods
189 throw new InvalidOperationException(
"Could not compile shader. See error messages." + compilerResult.
ToText());
198 if (!cachedEffects.TryGetValue(bytecode, out effect))
200 effect =
new Effect(graphicsDeviceService.GraphicsDevice, bytecode, usedParameters);
201 effect.Name = bytecode.Name;
202 cachedEffects.Add(bytecode, effect);
204 #if SILICONSTUDIO_PLATFORM_WINDOWS_DESKTOP
205 foreach (var sourcePath
in bytecode.HashSources.Keys)
207 using (var pathStream = Asset.OpenAsStream(sourcePath +
"/path"))
210 var path = reader.ReadToEnd();
211 directoryWatcher.Track(path);
222 compilerParameters.Profile = GraphicsDevice.ShaderProfile.HasValue ? GraphicsDevice.ShaderProfile.Value : graphicsDeviceService.GraphicsDevice.Features.Profile;
223 #if SILICONSTUDIO_PARADOX_GRAPHICS_API_OPENGLCORE
224 compilerParameters.Platform = GraphicsPlatform.OpenGL;
226 #if SILICONSTUDIO_PARADOX_GRAPHICS_API_OPENGLES
227 compilerParameters.Platform = GraphicsPlatform.OpenGLES;
231 var mainEffectNameEnd = effectName.IndexOf(
'.');
232 var mainEffectName = mainEffectNameEnd != -1 ? effectName.Substring(0, mainEffectNameEnd) : effectName;
234 subEffect = mainEffectNameEnd != -1 ? effectName.Substring(mainEffectNameEnd + 1) :
string.Empty;
237 var isPdxfx = ShaderMixinManager.Contains(mainEffectName);
240 var compilerResult = compiler.Compile(source, compilerParameters, modifiedShaders, recentlyModifiedShaders);
242 foreach (var message
in compilerResult.Messages)
247 return compilerResult;
250 private void FileModifiedEvent(
object sender,
FileEvent e)
254 var shaderSourceName = DefaultSourceShaderFolder +
"/" + e.Name;
255 modifiedShaders.Add(shaderSourceName);
256 recentlyModifiedShaders.Add(shaderSourceName);
260 foreach (var bytecode
in cachedEffects.Keys)
262 if (bytecode.HashSources.ContainsKey(shaderSourceName))
264 var effect = cachedEffects[bytecode];
265 lock (effectsToRecompile)
267 EffectUpdateInfos updateInfos;
268 updateInfos.Effect = effect;
269 updateInfos.CompilerResults = null;
270 updateInfos.EffectName = null;
271 updateInfos.SubEffectName = null;
272 updateInfos.OldBytecode = bytecode;
273 effectsToRecompile.Add(updateInfos);
279 lock (effectsToRecompile)
281 foreach (var effectToRecompile
in effectsToRecompile)
283 RecompileEffect(effectToRecompile);
285 effectsToRecompile.Clear();
290 private void RecompileEffect(EffectUpdateInfos updateInfos)
293 updateInfos.Effect.CompilationParameters.CopyTo(compilerParameters);
294 var effectName = updateInfos.Effect.Name;
297 var compilerResult = GetCompilerResults(effectName, compilerParameters, out subEffect);
300 if (compilerResult.HasErrors)
302 Log.Error(
"Effect {0} failed to reompile: {0}", compilerResult.ToText());
307 updateInfos.CompilerResults = compilerResult;
308 updateInfos.EffectName = effectName;
309 updateInfos.SubEffectName = subEffect;
310 lock (effectsToUpdate)
312 effectsToUpdate.Add(updateInfos);
316 private void UpdateEffect(EffectUpdateInfos updateInfos)
321 bytecode = updateInfos.CompilerResults.Bytecodes[updateInfos.SubEffectName];
323 catch (KeyNotFoundException)
325 Log.Error(
"The sub-effect {0} wasn't found in the compiler results.", updateInfos.SubEffectName);
332 parameters = updateInfos.CompilerResults.UsedParameters[updateInfos.SubEffectName];
334 catch (KeyNotFoundException)
336 Log.Error(
"The sub-effect {0} parameters weren't found in the compiler results.", updateInfos.SubEffectName);
340 bytecode.Name = updateInfos.EffectName;
341 updateInfos.Effect.Initialize(
GraphicsDevice, bytecode, parameters);
342 updatedEffects.Add(updateInfos.Effect);
346 cachedEffects.Remove(updateInfos.OldBytecode);
349 if (!cachedEffects.TryGetValue(bytecode, out newEffect))
351 cachedEffects.Add(bytecode, updateInfos.Effect);
360 private struct EffectUpdateInfos
364 public string EffectName;
365 public string SubEffectName;
A shader source that is linked to a pdxfx effect.
Service providing method to access GraphicsDevice life-cycle.
string ToText()
Returns a string representation of this
Parameters used for mixin.
Checks if an effect has already been compiled in its cache before deferring to a real IEffectCompiler...
FileEventChangeType ChangeType
Gets the type of the change.
Parameters used for compilation.
A service registry is a IServiceProvider that provides methods to register and unregister services...
override void Initialize()
This method is called when the component is added to the game.
Performs primitive-based rendering, creates resources, handles system-level variables, adjusts gamma ramp levels, and creates shaders. See The+GraphicsDevice+class to learn more about the class.
Base class for a GameSystemBase component.
Base implementation for ILogger.
An IEffectCompiler which will compile effect into multiple shader code, and compile them with a IShad...
Current timing used for variable-step (real time) or fixed-step (game time) games.
Dictionary< string, Effect > LoadEffects(string effectName, CompilerParameters compilerParameters)
Loads the effect and its children.
override void Update(GameTime gameTime)
This method is called when this game component is updated.
EffectSystem(IServiceRegistry services)
Initializes a new instance of the EffectSystem class.
Track file system events from several directories.
Effect LoadEffect(string effectName, CompilerParameters compilerParameters)
Loads the effect.
bool HasErrors
Gets or sets a value indicating whether this instance has errors.
Contains a compiled shader with bytecode for each stage.
Output message to log right away.
Main interface used to compile a shader.
FileEventChangeType
Change type of file used by FileEvent and DirectoryWatcher.
Ä file event used notified by DirectoryWatcher
A shader class used for mixin.