2015-10-16 9 views
7

In Microsoft's example per come usare PixelShader usano un singleton. Ho visto lo stesso modello in other places, e qui dico ShaderÈ consigliabile utilizzare un PixelShader singleton come best practice?

Il pixel è memorizzato in un campo _pixelShader private static. Questo campo è statico, perché un'istanza del codice shader compilato è sufficiente per l'intera classe.

Abbiamo riscontrato diversi problemi di perdita di memoria quando si utilizza questo modello. PixelShader è coinvolto nella gestione degli eventi che non vengono sempre cancellati correttamente. Abbiamo dovuto freeze loro, e ho visto qualche miglioramento. Abbiamo dovuto fare manualmente alcuni distacchi

 // Attach/detach effect as UI element is loaded/unloaded. This avoids 
     // a memory leak in the shader code as described here: 
     element.Loaded += (obj, args) => 
     { 
      effect.PixelShader = ms_shader; 
      element.Effect = effect; 
     }; 
     element.Unloaded += (obj, args) => 
     { 
      effect.PixelShader = null; 
      element.Effect = null; 
     }; 

E anche ora sotto stress ci sono ancora perdite di memoria in quella zona. Qualcuno sa se PixelShader utilizza risorse pesanti che valgono la pena di utilizzare un singleton?

risposta

1

Definitivamente NO. Tuttavia, la ragione non è con PixelShader stesso, ma il suo utilizzo in ShaderEffect.
Quasi tutte le implementazioni di Shader Effetti personalizzati per WPF si basano su esempi .NET 3.5 di Microsoft, che non sono stati aggiornati per .NET 4.0. Era perfetto usare l'istanza di singleton PixelShader per tutti gli effetti, che supportava solo shader ps_2_0. Con .NET 4.0 Microsoft ha introdotto il supporto per ps_3_0 pixel shader (per i dispositivi con accelerazione GPU) e con ciò ha introdotto la perdita di memoria nella classe ShaderEffect.
ShaderEffect traccia la sua proprietà PixelShader e controlla che non utilizzi registri ps_3_0 per bytecode ps_2_0 mediante una forte sottoscrizione all'evento interno di PixelShader denominato _shaderBytecodeChanged. Pertanto, singleton PixelShader utilizzato da più istanze ShaderEffect funge da Domination Root per GC e ritarda tutti gli oggetti che sono stati utilizzati con qualsiasi istanza corrispondente ShaderEffect. Questo è known memory leak, che non sarà riparato.
Se si desidera utilizzare PixelShader come Singleton, senza perdite, allora si dovrà utilizzare runtime hacker: ogni volta PixelShader istanza viene assegnato a PixelShader proprietà di ShaderEffect classe, _shaderBytecodeChanged campo della PixelShader istanza deve essere cancellata manualmente, ad esempio:

var ei = typeof(PixelShader).GetEvent("_shaderBytecodeChanged", 
      BindingFlags.Instance | BindingFlags.NonPublic); 
var fi = typeof(PixelShader).GetField(ei.Name, 
       BindingFlags.Instance | BindingFlags.NonPublic); 
fi.SetValue(pixelShader,null); 

Ovviamente, queste operazioni possono essere ottimizzate generando metodi di supporto in runtime tramite l'infrastruttura DynamicMethod o meccanismi simili. Tuttavia, questo dovrebbe essere usato solo per gli shader che sono sicuramente ps_2_0 o sicuramente ps_3_0