使用单例 PixelShader 应该是最佳实践吗?

Should using a singleton PixelShader be a best practice?

在微软的 example for how to use the PixelShader they use a singleton. I've seen the same pattern in other places 中,他们说的是

The pixel shader is stored in a private static field _pixelShader. This field is static, because one instance of the compiled shader code is enough for the whole class.

我们在使用此模式时发现了几个内存泄漏问题。 PixelShader 涉及的事件处理并不总是正确清除。我们不得不 freeze 他们,并看到了一些改进。我们不得不手动执行一些分遣队

        // 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 使用繁重的资源是否值得使用单例?

绝对没有。然而,原因不在于 PixelShader 本身,而在于它在 ShaderEffect 中的用法。
几乎所有 WPF 自定义着色器效果的实现都基于 Microsoft 的 .NET 3.5 示例,这些示例未针对 .NET 4.0 进行更新。对所有效果使用单例 PixelShader 实例很好,它仅支持 ps_2_0 着色器。在 .NET 4.0 中,Microsoft 引入了对 ps_3_0 像素着色器(用于 GPU 加速设备)的支持,并且他们将内存泄漏引入 ShaderEffect class.
ShaderEffect 跟踪它的 PixelShader 属性 并检查它没有使用 ps_3_0 注册 ps_2_0 字节码通过强订阅 PixelShader 的内部事件调用 _shaderBytecodeChanged.因此,多个 ShaderEffect 实例使用的单例 PixelShader 作为 GC 的支配根,并重新保留与相应 ShaderEffect 的任何实例一起使用的所有对象。即known memory leak,不会固定。
如果您想将 PixelShader 用作无泄漏的单例,那么您将不得不使用运行时黑客:每次 PixelShader 实例分配给 PixelShader 属性 of [=12] =] class, _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

的着色器