软件渲染模式 - WPF

21

我有一个WPF用户控件,需要强制在RenderMode.SoftwareOnly下进行渲染。由于我正在使用.NET 3.5,所以我必须做类似这样的事情:

var hwndSource = PresentationSource.FromVisual(this) as HwndSource;
if (hwndSource != null)
{
    hwndSource.CompositionTarget.RenderMode = RenderMode.SoftwareOnly;        
}

但是这在我的应用程序中不起作用,WPF程序在一些机器上崩溃,关闭注册表级别的硬件加速似乎可以解决问题。

上述代码是写在窗口的Loaded事件中的。如果我没错的话,Loaded事件发生在控件被渲染之后(MSDN)。所以把上面的代码放在这个事件里是否有意义?如果没有,那么哪个事件比较合适呢?

同时,设置视觉元素的RenderMode属性会影响其子元素吗?还是我需要为每个子元素单独设置呢?


嗨,我很好奇。调度程序解决方案对你有帮助吗? - HCL
3个回答

35

以下是我们所做的事情:

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    if (ForceSoftwareRendering)
    {
        HwndSource hwndSource = PresentationSource.FromVisual(this) as HwndSource;
        HwndTarget hwndTarget = hwndSource.CompositionTarget;
        hwndTarget.RenderMode = RenderMode.SoftwareOnly;
    }
}

除了一点,它对我们起作用...就是需要为每个窗口执行此操作。在 .NET 3.5 中,没有办法使设置在整个应用程序范围内生效。而且有些窗口你无法控制得那么多,例如右键单击“上下文”窗口。我们发现,在 .NET 3.5 中没有好的解决方案,只能使用注册表设置。

编辑

这是我们用来确定何时强制使用软件渲染的逻辑,这是由 Microsoft 支持工程师建议的。

public bool ForceSoftwareRendering 
{
    get 
    { 
        int renderingTier = (System.Windows.Media.RenderCapability.Tier >> 16);
        return renderingTier == 0;
    }
}

在 .NET 4 中,微软添加了一个应用程序范围的设置,非常适合我们使用。这是一种更好的选择,因为您不需要在每个窗口上设置它。您只需设置一次,就可以应用于所有窗口。

System.Windows.Media.RenderOptions.ProcessRenderMode

已编辑

新的 .NET 4.0 属性可以在应用程序启动时设置如下:

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        if (ForceSoftwareRendering)
            RenderOptions.ProcessRenderMode = RenderMode.SoftwareOnly;
    }
}

“ForceSoftwareRendering”是从哪里来的?你找到了一种检测哪些机器会因软件渲染而崩溃的方法吗? - Eduardo Wada
@EduardoWada 我会更新答案以包含这个。 - Matt Varblow
有人知道类似的东西,但是适用于WinForms吗? - user1722669
@user1722669,WinForms 无法使用硬件加速。 - T McKeown
我的WPF应用程序存在渲染问题,使用SoftwareOnly可以解决。然而,System.Windows.Media.RenderCapability.Tier返回0x20000,因此检查表明该机器应该支持它。 - Jan-Jaap van der Geer

24

您还可以通过在应用程序启动处理程序中添加下一行来禁用整个过程的硬件渲染:

RenderOptions.ProcessRenderMode = RenderMode.SoftwareOnly;

在运行时切换也是可能的。


5
可以在运行时切换的话,给个赞。这并不是 MSDN 上立即显而易见的信息,但却是重要的信息。 - Charlie

4

事件-问题
对于缺失的hwnd-source,请尝试以下操作:

    Dispatcher.BeginInvoke(new Action(delegate {               
       HwndSource hwndSource = PresentationSource.FromVisual(this) as System.Windows.Interop.HwndSource;
            if (null == hwndSource) {
                throw new InvalidOperationException("No HWND");
            }
            HwndTarget hwndTarget = hwndSource.CompositionTarget;
            hwndTarget.RenderMode = RenderMode.SoftwareOnly;

  }),System.Windows.Threading.DispatcherPriority.ContextIdle, null);

RenderMode的范围
据我所知,每个WPF窗口只有一个Win32窗口,其余内容都是在WPF中呈现的。因此,我认为设置RenderMode涉及视觉元素所在窗口中的所有内容。在这种情况下,范围是整个窗口。


谢谢。您能解释一下为什么在这种情况下需要使用Dispatcher吗? - Navaneeth K N
@appu:这不是必需的 - 但如果hwnd在加载事件执行时不存在(如果这是问题),那么显式等待系统完成所有重要任务(如渲染:)可能会有所帮助。我已经看到了许多不寻常的情况,这些问题可以通过这种方式解决。但这只是一个想法,我不能保证它有效。顺便说一句,我使用上面的代码而没有使用dispatcher,到目前为止还没有出现问题。但我只在一个应用程序中使用它,并且那里很少使用该选项。因此,这个声明肯定不具有代表性。 - HCL

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接