使用单例 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
的着色器
在微软的 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