应该使用单例PixelShader是最佳做法吗?

时间:2015-10-16 17:41:20

标签: c# wpf pixel-shader

在Microsoft的example中,如果使用PixelShader,他们会使用单例。我在other places中看到了相同的模式,他们在这里说

  

像素着色器存储在私有静态字段_pixelShader中。这个字段是静态的,因为编译后的着色器代码的一个实例对于整个类来说已经足够了。

使用此模式时,我们发现了几个内存泄漏问题。 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是否使用重型资源值得使用单例?

1 个答案:

答案 0 :(得分:4)

绝对没有。但是,原因不在于PixelShader本身,而是在ShaderEffect中的用法。
几乎所有针对WPF的自定义Shader Effects的实现都基于Microsoft的.NET 3.5示例,这些示例未针对.NET 4.0进行更新。对所有效果使用单例PixelShader实例是很好的,它只支持ps_2_0着色器。使用.NET 4.0,Microsoft引入了对ps_3_0像素着色器(对于GPU加速设备)的支持,并且由此,他们将内存泄漏引入ShaderEffect类。 ShaderEffect跟踪其PixelShader属性,并通过强烈订阅PixelShader的内部事件_shaderBytecodeChanged来检查它是否使用ps_3_0寄存器来获取ps_2_0字节码。因此,多个PixelShader实例使用的单个ShaderEffect充当GC的调控根,并重新发送与相应ShaderEffect的任何实例一起使用的所有对象。那是https://cloud.google.com/storage/docs/xml-api/put-bucket-cors,不会修复。
如果您希望使用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

的着色器