Должно ли использование одноэлементного PixelShader быть лучшей практикой?
В примере Microsoft о том, как использовать PixelShader, они используют синглтон. Я видел такую же картину в других местах, и здесь они говорят
Пиксельный шейдер хранится в закрытом статическом поле _pixelShader. Это поле является статическим, потому что одного экземпляра скомпилированного кода шейдера достаточно для всего класса.
Мы видели несколько проблем утечки памяти при использовании этого шаблона. PixelShader - это обработка событий, которая не всегда корректно очищается. Мы должны были заморозить их и увидели некоторое улучшение. Пришлось вручную сделать несколько отрядов
// 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;
};
И даже сейчас в состоянии стресса в этой области все еще есть утечки памяти. Кто-нибудь знает, использует ли PixelShader тяжелые ресурсы, которые стоят проблем с использованием синглтона?
1 ответ
Определенно нет. Однако причина не в PixelShader
само по себе, но его использование в ShaderEffect
,
Почти все реализации пользовательских Shader Effects для WPF основаны на примерах Microsoft.NET 3.5, которые не были обновлены для.NET 4.0. Было хорошо использовать синглтон PixelShader
экземпляр для всех эффектов, которые поддерживаются только шейдерами ps_2_0. С.NET 4.0 Microsoft представила поддержку пиксельных шейдеров ps_3_0 (для устройств с GPU-ускорением), а с этим они привели к утечке памяти в ShaderEffect
учебный класс.ShaderEffect
отслеживает его PixelShader
свойство и проверяет, что он не использует регистры ps_3_0 для байт-кода ps_2_0 путем строгой подписки на внутреннее событие PixelShader, называемое _shaderBytecodeChanged
, Следовательно, синглтон PixelShader
используется несколькими ShaderEffect
экземпляры служат Domination Root для GC и повторно отображают все объекты, которые использовались с любым экземпляром соответствующего ShaderEffect
, Это известная утечка памяти, которая не будет исправлена.
Если вы хотите использовать PixelShader
как синглтон без утечек, то вам придется использовать хакинг во время выполнения: каждый раз PixelShader
экземпляр назначен PixelShader
собственностью ShaderEffect
учебный класс, _shaderBytecodeChanged
поле PixelShader
экземпляр должен быть очищен вручную, например:
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);
Конечно, эти операции могут быть оптимизированы путем создания вспомогательных методов во время выполнения DynamicMethod
инфраструктура или аналогичные механизмы. Однако это следует использовать только для шейдеров, которые определенно являются ps_2_0 или определенно ps_3_0