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

标签 c# wpf pixel-shader

在微软的example对于如何使用 PixelShader,他们使用单例。我在 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 类。
ShaderEffect 跟踪它的 PixelShader 属性,并通过强订阅名为 _shaderBytecodeChanged 的 PixelShader 内部事件来检查它没有使用 ps_2_0 字节码的 ps_3_0 寄存器。因此,多个 ShaderEffect 实例使用的单例 PixelShader 充当 GC 的支配根,并重新保留与相应 ShaderEffect 的任何实例一起使用的所有对象。即known memory leak ,这不会被修复。
如果您想将 PixelShader 用作无泄漏的单例,那么您将不得不使用运行时黑客:每次将 PixelShader 实例分配给 PixelShader ShaderEffect 类的属性,PixelShader 实例的 _shaderBytecodeChanged 字段应手动清除,例如:

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 的着色器

关于c# - 使用单例 PixelShader 应该是最佳实践吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33176446/

相关文章:

c# - OrderByDescending with Skip and Take 在 LINQ 中显示错误

c# - 由于文本框失去焦点,需要单击两次按钮

java - 改变每个形状的颜色深浅

python - 像素着色器中的 Mandelbrot

c# - 如何在本地测试 Azure ActiveDirectory?

c# - .Net Framework 中的手动内存分页(可能/如何?)

c# - 调试时检查 IEnumerable "stack"?

c# - 为什么 WPF 中没有点击事件?

wpf BackgroundWorker - 关于更新 UI

c# - HLSL:有问题的像素着色器代码(采样时 alpha 为零)?