Должно ли использование одноэлементного 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

Другие вопросы по тегам