在 WPF 中,常用的画刷里面有纯色画刷 SolidColorBrush 类。因为画刷会对应到 DirectX 的资源,因此之前我以为纯色画刷其实会比 Color 会占用更多的资源。在 WPF 中 Color 其实是结构体,创建速度快。而 SolidColorBrush 是画刷,会对应 DirectX 资源,相对来说性能会比较差。但在通过阅读 WPF 的源代码,发现其实 SolidColorBrush 的创建的性能其实是特别好的,因此请不要担心创建了太多的纯色画刷类
在 WPF 中,画刷 Brush 有很多实现,本文的内容是纯色画刷的实现。在 WPF 的纯色画刷是继承 Brush 的类,这个类自己定义的只有一个字段 _duceResource
和 Color 一个属性,而 Color 属性是一个依赖属性。从这里可以看到 SolidColorBrush 类占用的托管内存空间其实很小
那在日常调试内存的时候,遇到的 SolidColorBrush 类占用非托管内存,这里的非托管内存是在什么时候申请的?其实非托管内存的申请是在 SolidColorBrush 被使用的时候,准确来说是被调用到 AddRefOnChannelCore 方法的时候,才会申请非托管内存。而如果只是构建出来,那么纯色画刷不会申请任何的非托管内存。也就是说此时创建纯色画刷仅仅只会用到很少量的托管内存
在 WPF 设计里面,所有继承 System.Windows.Media.Composition.DUCE.IResource 接口的类型,都可以表示这是一个 DirectX 资源类,将会在渲染过程中,申请或使用 DirectX 资源。而 DirectX 资源就是非托管资源。在 WPF 的机制,将会在 WPF 资源被使用的时候,如画刷被附加到某个元素上,在此元素渲染的时候(准确来说是之前)将会通过 IResource 接口的 AddRefOnChannel 方法让资源通过 System.Windows.Media.Composition.DUCE.Channel 申请到 DirectX 资源。以下是 IResource 接口代码
在 Brush 类将会重写 AddRefOnChannel 方法,如下面代码
可以看到在 Brush 类中,其实是调用了 AddRefOnChannelCore 抽象方法,在 SolidColorBrush 里面实现了 AddRefOnChannelCore 申请非托管资源
上面代码核心就是 _duceResource.CreateOrAddRefOnChannel
创建 ResourceHandle 以及通过 UpdateResource 将颜色更新到 DirectX 资源
在 UpdateResource 里面,将会通过如下代码在非托管层注册纯色画刷
回到主题,在创建 SolidColorBrush 时,在 WPF 框架里面做了什么?通过上文可以看到申请非托管资源是在使用到画刷的时候,如果我创建的纯色画刷只是存放而已,而不会使用他去参加渲染,那么纯色画刷将不会占用任何非托管资源,也不需要有任何逻辑调用到非托管的 DirectX 层
在 SolidColorBrush 的构造函数里面,可以选择传入或不传入 Color 参数。如上文可以了解到在 SolidColorBrush 的颜色属性是依赖属性,假定没有传入构造参数,那么将会使用依赖属性默认值,也就是说此实例仅仅只使用到字段 _duceResource
的内存。从性能角度上,如果没有传入构造参数,那么如下面代码,这是一个空白的构造函数,啥都没有做
当然了 SolidColorBrush 继承了 Brush 类,咱也需要看一下 Brush 类的构造函数的定义
可以看到 Brush 也是空白。但 Brush 继承了 Animatable 类,咱继续看接下来的继承的类的构造
可以看到性能层面上,几乎构造函数是啥都没有做。通过上面的代码也可以看到,如果一个类的继承很长,那么构造函数的调用性能,也许需要关注。在另一个仓库,也算是跨平台版本的 WPF 仓库 https://github.com/AvaloniaUI/Avalonia 这里面的元素定义,元素的类型继承十分长,这是设计上的缺点
那如果在 SolidColorBrush 的构造加上参数,传入颜色,此时发生了什么?在 SolidColorBrush 的构造函数将会给依赖属性设置值,如下面代码
从上面代码可以看到给依赖属性设置值将会触发 ColorPropertyChanged 函数。在这个函数调用了 PropertyChanged 方法。这是定义在 Animatable 的方法,代码如下
刚创建的 SolidColorBrush 是不存在 AnimationStorage 的,因此 independentAnimationStorage 一定是空,将会调用 RegisterForAsyncUpdateResource 方法
在上面代码中,因为 Animatable_IsResourceInvalidationNecessary 默认值是 false 因此这个函数啥都没有做
可以看到无论是在 SolidColorBrush 的构造函数有没有设置参数,执行的代码逻辑都非常少,执行时间基本都可以忽略。从执行性能层面,可以认为创建 SolidColorBrush 的性能是特别好的,以上代码的执行时间预计不会比创建一个空对象慢多少。从内存层面,在 SolidColorBrush 类本身,不算继承类的情况下,只有一个字段和一个依赖属性,占用内存量不会比 Color 结构体多多少。所以可以放心创建 SolidColorBrush 对象。好吧,本文说的是创建的性能,如果要将 SolidColorBrush 用上,这就是另一个坑了,建议如果是要使用的 SolidColorBrush 对象,还是使用缓存比较好,非托管的占用还是比较多的
当前的 WPF 在 https://github.com/dotnet/wpf 完全开源,使用友好的 MIT 协议,意味着允许任何人任何组织和企业任意处置,包括使用,复制,修改,合并,发表,分发,再授权,或者销售。在仓库里面包含了完全的构建逻辑,只需要本地的网络足够好(因为需要下载一堆构建工具),即可进行本地构建
原文链接: http://blog.lindexi.com/post/dotnet-%E8%AF%BB-WPF-%E6%BA%90%E4%BB%A3%E7%A0%81%E7%AC%94%E8%AE%B0-%E5%88%9B%E5%BB%BA-SolidColorBrush-%E6%80%A7%E8%83%BD%E6%B2%A1%E6%9C%89%E6%83%B3%E8%B1%A1%E9%82%A3%E4%B9%88%E5%B7%AE
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。
欢迎转载、使用、重新发布,但务必保留文章署名 林德熙 (包含链接: https://blog.lindexi.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我 联系。