WPF 使用 SharpDX 在 D3DImage 显示
原文:WPF 使用 SharpDX 在 D3DImage 显示
版权声明:博客已迁移到 http://lindexi.gitee.io 欢迎访问。如果当前博客图片看不到,请到 http://lindexi.gitee.io 访问博客。本文地址 https://blog.csdn.net/lindexi_gd/article/details/84253093
本文告诉大家如何使用 SharpDX 在 D3DImage 显示。在上一篇WPF 使用 SharpDX只是使用窗口,也就是无法使用其它的 WPF 控件。所以这一篇就来告诉大家如何使用 WPF 控件和使用 SharpDX
本文是一个系列,希望大家从第一篇开始看
如果只是使用 SharpDX 使用窗口渲染,就无法使用其它的 WPF 控件,实际使用经常只是使用 SharpDX 加快一些渲染,很多元素都是不需要。
如果拿来 HWND 做渲染,那么 WPF 只是提供一个窗口,这和 WPF 的设计,高效而且灵活不符合,所以本文就来告诉大家如何使用 SharpDx 高性能渲染同时使用 WPF 的元素。
微软为了大家方便使用 Direct2D 就添加了 D3DImage ,虽然这个元素不是很好用。
介绍
先告诉大家什么是 D3DImage ,这是一个可以和 Direct2D、3D 交互的元素,他是一个 ImageSource ,可以放在 Image 控件显示。
使用 D3DImage 会发送一次内存复制,如果在显卡渲染,那么就会先从显卡获得位图,复制到 D3DImage 作为图片显示到 WPF ,也就是同个位图需要现在显卡渲染,然后复制到内存,让 WPF 渲染图片。
一般使用 D3DImage 都不能拿到比原来好的性能。
那么 D3DImage 有什么用?一般渲染是比较慢的,如果需要使用 Dx12 进行加速,而 WPF 无法使用 dx12 那么就需要使用 dx12 渲染。虽然需要使用内存复制,但是经常使用 dx12 渲染的速度比内存复制然后 WPF 显示的速度快。
在 SharpDX 可以使用 D3DImage 进行离屏渲染,本来 WPF 只能有一个渲染线程,但是使用了 SharpDX 就可以有多个渲染线程,这时通过 dx12 加速,一般渲染速度会比不使用 SharpDX 快。
创建控件
首先创建一个 .net framework 4.5 以上的项目。还记得WPF 使用 Direct2D1 画图入门说需要使用 x64 才可以编译,实际上 SharpDX 可以使用 AnyCpu ,而且支持 .net framwork 4.5 和以上的项目。所以使用 SharpDx 就比较简单。
打开主页面,创建一个图片
<Grid>
<Image>
<Image.Source>
<interop:D3DImage x:Name="KsyosqStmckfy"></interop:D3DImage>
</Image.Source>
</Image>
</Grid>
从上面可以看到D3DImage的方法,他在 WPF 和其他元素没有不一样的。
因为没有直接从 Direct2D 到 D3D 显示的方法,下面需要告诉大家如何在 D3D11 显示 Direct2D 然后通过相同的格式转 D3D9 最后把缓冲区指针显示。
D3D 设备
如果需要使用 Direct2D 渲染,需要先创建 D3D11 的设备,因为实际的渲染是通过 3D 渲染。
先引用命名,这样大家直接复制代码就不会不知道使用的是哪个
using D2D = SharpDX.Direct2D1;
using SharpDX.Direct3D;
using SharpDX.Mathematics.Interop;
using DXGI = SharpDX.DXGI;
using D3D11 = SharpDX.Direct3D11;
using D3D9 = SharpDX.Direct3D9;
在使用之前,需要使用 Nuget 安装,安装方法请看WPF 使用 SharpDX
创建设备请看下面
var device = new D3D11.Device(DriverType.Hardware, D3D11.DeviceCreationFlags.BgraSupport);
因为 D3DImage 需要使用 SetBackBuffer 传入指针,所以通过 D3D11.Texture2D 可以作为指针。
下面来告诉大家如何创建 D3D11.Texture2D ,创建的方法因为需要很多参数,所以代码很多
从 D3D11.Texture2D 的构造函数可以知道,需要传入两个参数 D3D11.Device 和 D3D11.Texture2DDescription ,先创建 D3D11.Texture2DDescription
var width = Math.Max((int) ActualWidth, 100);
var height = Math.Max((int) ActualHeight, 100);
var renderDesc = new D3D11.Texture2DDescription
{
BindFlags = D3D11.BindFlags.RenderTarget | D3D11.BindFlags.ShaderResource,
Format = DXGI.Format.B8G8R8A8_UNorm,
Width = width,
Height = height,
MipLevels = 1,
SampleDescription = new DXGI.SampleDescription(1, 0),
Usage = D3D11.ResourceUsage.Default,
OptionFlags = D3D11.ResourceOptionFlags.Shared,
CpuAccessFlags = D3D11.CpuAccessFlags.None,
ArraySize = 1
};
参数大家先直接使用,我这里不告诉大家每个参数是怎么计算
现在创建两个参数就可以创建 D3D11.Texture2D ,创建只需要使用下面代码
var renderTarget = new D3D11.Texture2D(device, renderDesc);
设置指针
创建好了 D3D11.Texture2D 需要让 D3DImage 显示需要使用 SetBackBuffer 设置。
因为传入 D3D11.Texture2D ,但是 D3DImage 是 dx9 的,所以需要转换一下。
首先转换 Format ,因为 D3D11.Texture2D 使用的是 SharpDX.DXGI.Format 需要转换为 D3D9.Format ,请看下面代码
private static D3D9.Format TranslateFormat(D3D11.Texture2D texture)
{
switch (texture.Description.Format)
{
case SharpDX.DXGI.Format.R10G10B10A2_UNorm:
return D3D9.Format.A2B10G10R10;
case SharpDX.DXGI.Format.R16G16B16A16_Float:
return D3D9.Format.A16B16G16R16F;
case SharpDX.DXGI.Format.B8G8R8A8_UNorm:
return D3D9.Format.A8R8G8B8;
default:
return D3D9.Format.Unknown;
}
}
除了转换还需要拿到指针
private IntPtr GetSharedHandle(D3D11.Texture2D texture)
{
using (var resource = texture.QueryInterface<DXGI.Resource>())
{
return resource.SharedHandle;
}
}
窗口的指针
private static D3D9.PresentParameters GetPresentParameters()
{
var presentParams = new D3D9.PresentParameters();
presentParams.Windowed = true;
presentParams.SwapEffect = D3D9.SwapEffect.Discard;
presentParams.DeviceWindowHandle = NativeMethods.GetDesktopWindow();
presentParams.PresentationInterval = D3D9.PresentInterval.Default;
return presentParams;
}
实际设置的是 D3D9.Texture ,这个类需要传入 D3D9.Device 和D3D9.PresentParameters,所以才需要上面的代码。
传入 D3D9.Device 需要 D3D9.Direct3DEx ,所以请看代码
var format = TranslateFormat(target);
var handle = GetSharedHandle(target);
var presentParams = GetPresentParameters();
var createFlags = D3D9.CreateFlags.HardwareVertexProcessing | D3D9.CreateFlags.Multithreaded |
D3D9.CreateFlags.FpuPreserve;
var d3DContext = new D3D9.Direct3DEx();
var d3DDevice = new D3D9.DeviceEx(d3DContext, 0, D3D9.DeviceType.Hardware, IntPtr.Zero, createFlags,
presentParams);
现在可以创建 D3D9.Texture ,通过这个来给指针
var renderTarget = new D3D9.Texture(d3DDevice, target.Description.Width, target.Description.Height, 1,
D3D9.Usage.RenderTarget, format, D3D9.Pool.Default, ref handle);
using (var surface = renderTarget.GetSurfaceLevel(0))
{
_d3D.Lock();
_d3D.SetBackBuffer(D3DResourceType.IDirect3DSurface9, surface.NativePointer);
_d3D.Unlock();
}
这样就设置好了,通过 D3D11.Texture2D 就可以显示出来了。
但是直接使用 D3D11.Texture2D 是无法画出来的,如果需要 D2D.RenderTarget 还需要通过 D3D11.Texture2D 创建 Surface 为缓冲区。
var surface = renderTarget.QueryInterface<DXGI.Surface>();
var d2DFactory = new D2D.Factory();
var renderTargetProperties =
new D2D.RenderTargetProperties(new D2D.PixelFormat(DXGI.Format.Unknown, D2D.AlphaMode.Premultiplied));
_d2DRenderTarget = new D2D.RenderTarget(d2DFactory, surface, renderTargetProperties);
device.ImmediateContext.Rasterizer.SetViewport(0, 0,(int)ActualWidth , (int) ActualHeight);
画出来
下面就来尝试使用 D2D.RenderTarget 画出一个矩形,代码写在 CompositionTarget.Rendering ,画出来的代码和之前的一样
private void CompositionTarget_Rendering(object sender, EventArgs e)
{
_d2DRenderTarget.BeginDraw();
OnRender(_d2DRenderTarget);
_d2DRenderTarget.EndDraw();
_d3D.Lock();
_d3D.AddDirtyRect(new Int32Rect(0, 0, _d3D.PixelWidth, _d3D.PixelHeight));
_d3D.Unlock();
}
private void OnRender(D2D.RenderTarget renderTarget)
{
var brush = new D2D.SolidColorBrush(_d2DRenderTarget, new RawColor4(1, 0, 0, 1));
renderTarget.Clear(null);
renderTarget.DrawRectangle(new RawRectangleF(_x, _y, _x + 10, _y + 10), brush);
_x = _x + _dx;
_y = _y + _dy;
if (_x >= ActualWidth - 10 || _x <= 0)
{
_dx = -_dx;
}
if (_y >= ActualHeight - 10 || _y <= 0)
{
_dy = -_dy;
}
}
private float _x;
private float _y;
private float _dx = 1;
private float _dy = 1;
主要和原来不同的是需要 AddDirtyRect 告诉重新渲染,不然不会显示
现在修改一下前台界面,尝试添加一些代码
<Grid>
<Grid Background="Goldenrod">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="在图片下方"></TextBlock>
</Grid>
<Image>
<Image.Source>
<interop:D3DImage x:Name="KsyosqStmckfy"></interop:D3DImage>
</Image.Source>
</Image>
<Grid Background="Bisque" VerticalAlignment="Bottom">
<TextBlock Margin="0,10,0,0" HorizontalAlignment="Center" Text="在图片上方,所以看不见矩形"></TextBlock>
<TextBlock Margin="0,100,0,100" HorizontalAlignment="Center" VerticalAlignment="Center" Text="欢迎来我博客lindexi.gitee.io"></TextBlock>
</Grid>
</Grid>
建议复制一下我的代码,在自己的vs粘贴,尝试跑一下,然后继续看博客。
所有代码
前台界面只有一个控件
xmlns:interop="clr-namespace:System.Windows.Interop;assembly=PresentationCore"
<Image x:Name="DcwtTmmwvcr">
<Image.Source>
<interop:D3DImage x:Name="KsyosqStmckfy"></interop:D3DImage>
</Image.Source>
</Image>
先添加 引用
using D2D = SharpDX.Direct2D1;
using SharpDX.Direct3D;
using SharpDX.Mathematics.Interop;
using DXGI = SharpDX.DXGI;
using D3D11 = SharpDX.Direct3D11;
using D3D9 = SharpDX.Direct3D9;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
_d3D = KsyosqStmckfy;
Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
CreateAndBindTargets();
}
private void CompositionTarget_Rendering(object sender, EventArgs e)
{
_d2DRenderTarget.BeginDraw();
OnRender(_d2DRenderTarget);
_d2DRenderTarget.EndDraw();
_d3D.Lock();
_d3D.AddDirtyRect(new Int32Rect(0, 0, _d3D.PixelWidth, _d3D.PixelHeight));
_d3D.Unlock();
}
private void OnRender(D2D.RenderTarget renderTarget)
{
var brush = new D2D.SolidColorBrush(_d2DRenderTarget, new RawColor4(1, 0, 0, 1));
renderTarget.Clear(null);
renderTarget.DrawRectangle(new RawRectangleF(_x, _y, _x + 10, _y + 10), brush);
_x = _x + _dx;
_y = _y + _dy;
if (_x >= ActualWidth - 10 || _x <= 0)
{
_dx = -_dx;
}
if (_y >= ActualHeight - 10 || _y <= 0)
{
_dy = -_dy;
}
}
private float _x;
private float _y;
private float _dx = 1;
private float _dy = 1;
private D3D9.Texture _renderTarget;
private D3DImage _d3D;
private D2D.RenderTarget _d2DRenderTarget;
private void CreateAndBindTargets()
{
var width = Math.Max((int) ActualWidth, 100);
var height = Math.Max((int) ActualHeight, 100);
var renderDesc = new D3D11.Texture2DDescription
{
BindFlags = D3D11.BindFlags.RenderTarget | D3D11.BindFlags.ShaderResource,
Format = DXGI.Format.B8G8R8A8_UNorm,
Width = width,
Height = height,
MipLevels = 1,
SampleDescription = new DXGI.SampleDescription(1, 0),
Usage = D3D11.ResourceUsage.Default,
OptionFlags = D3D11.ResourceOptionFlags.Shared,
CpuAccessFlags = D3D11.CpuAccessFlags.None,
ArraySize = 1
};
var device = new D3D11.Device(DriverType.Hardware, D3D11.DeviceCreationFlags.BgraSupport);
var renderTarget = new D3D11.Texture2D(device, renderDesc);
var surface = renderTarget.QueryInterface<DXGI.Surface>();
var d2DFactory = new D2D.Factory();
var renderTargetProperties =
new D2D.RenderTargetProperties(new D2D.PixelFormat(DXGI.Format.Unknown, D2D.AlphaMode.Premultiplied));
_d2DRenderTarget = new D2D.RenderTarget(d2DFactory, surface, renderTargetProperties);
SetRenderTarget(renderTarget);
device.ImmediateContext.Rasterizer.SetViewport(0, 0, (int) ActualWidth, (int) ActualHeight);
CompositionTarget.Rendering += CompositionTarget_Rendering;
}
private void SetRenderTarget(D3D11.Texture2D target)
{
var format = TranslateFormat(target);
var handle = GetSharedHandle(target);
var presentParams = GetPresentParameters();
var createFlags = D3D9.CreateFlags.HardwareVertexProcessing | D3D9.CreateFlags.Multithreaded |
D3D9.CreateFlags.FpuPreserve;
var d3DContext = new D3D9.Direct3DEx();
var d3DDevice = new D3D9.DeviceEx(d3DContext, 0, D3D9.DeviceType.Hardware, IntPtr.Zero, createFlags,
presentParams);
_renderTarget = new D3D9.Texture(d3DDevice, target.Description.Width, target.Description.Height, 1,
D3D9.Usage.RenderTarget, format, D3D9.Pool.Default, ref handle);
using (var surface = _renderTarget.GetSurfaceLevel(0))
{
_d3D.Lock();
_d3D.SetBackBuffer(D3DResourceType.IDirect3DSurface9, surface.NativePointer);
_d3D.Unlock();
}
}
private static D3D9.PresentParameters GetPresentParameters()
{
var presentParams = new D3D9.PresentParameters();
presentParams.Windowed = true;
presentParams.SwapEffect = D3D9.SwapEffect.Discard;
presentParams.DeviceWindowHandle = NativeMethods.GetDesktopWindow();
presentParams.PresentationInterval = D3D9.PresentInterval.Default;
return presentParams;
}
private IntPtr GetSharedHandle(D3D11.Texture2D texture)
{
using (var resource = texture.QueryInterface<DXGI.Resource>())
{
return resource.SharedHandle;
}
}
private static D3D9.Format TranslateFormat(D3D11.Texture2D texture)
{
switch (texture.Description.Format)
{
case SharpDX.DXGI.Format.R10G10B10A2_UNorm:
return D3D9.Format.A2B10G10R10;
case SharpDX.DXGI.Format.R16G16B16A16_Float:
return D3D9.Format.A16B16G16R16F;
case SharpDX.DXGI.Format.B8G8R8A8_UNorm:
return D3D9.Format.A8R8G8B8;
default:
return D3D9.Format.Unknown;
}
}
}
public static class NativeMethods
{
[DllImport("user32.dll", SetLastError = false)]
public static extern IntPtr GetDesktopWindow();
}
参见:
在 WinForm 中使用 Direct2D - CYJB - 博客园
Multithreaded Direct2D Apps (Windows)
Improving the performance of Direct2D apps (Windows)
更多渲染博客请看 WPF 底层渲染
我搭建了自己的博客 https://lindexi.gitee.io/ 欢迎大家访问,里面有很多新的博客。只有在我看到博客写成熟之后才会放在csdn或博客园,但是一旦发布了就不再更新
如果在博客看到有任何不懂的,欢迎交流,我搭建了 dotnet 职业技术学院 欢迎大家加入
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名林德熙(包含链接:http://blog.csdn.net/lindexi_gd ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系。
WPF 使用 SharpDX 在 D3DImage 显示的更多相关文章
- WPF 使用 SharpDX
原文:WPF 使用 SharpDX 版权声明:博客已迁移到 http://lindexi.gitee.io 欢迎访问.如果当前博客图片看不到,请到 http://lindexi.gitee.io 访问 ...
- WPF 使用 SharpDx 异步渲染
本文告诉大家如何通过 SharpDx 进行异步渲染,但是因为在 WPF 是需要使用 D3DImage 画出来,所以渲染只是画出图片,最后的显示还是需要 WPF 在他自己的主线程渲染 本文是一个系列,希 ...
- WPF防止界面卡死并显示加载中效果
原文:WPF防止界面卡死并显示加载中效果 网上貌似没有完整的WPF正在加载的例子,所以自己写了一个,希望能帮到有需要的同学 前台: <Window x:Class="WpfApplic ...
- 一张图搞定OAuth2.0 在Office应用中打开WPF窗体并且让子窗体显示在Office应用上 彻底关闭Excle进程的几个方法 (七)Net Core项目使用Controller之二
一张图搞定OAuth2.0 目录 1.引言 2.OAuth2.0是什么 3.OAuth2.0怎么写 回到顶部 1.引言 本篇文章是介绍OAuth2.0中最经典最常用的一种授权模式:授权码模式 非常 ...
- WPF动态创建Image的显示问题
原文:WPF动态创建Image的显示问题 最近学习WPF,看到一篇教程讲解如何动态创建Image控件,自己练手时候无论如何也显示不出图片.刚开始以为是图片的路径有问题,可后来将图片的路径设为相对路径或 ...
- WPF中StringFormat的用法--显示特定位数的数字
原文:WPF中StringFormat的用法--显示特定位数的数字 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/huangli321456/art ...
- WPF 4 目录树型显示
原文:WPF 4 目录树型显示 本篇将通过WPF4 制作简单的目录树型结构显示实例,完成本篇内容我们将作出下图所示的应用程序. 从图中我们可以看到程序主要分为两部分:左边显示本地驱 ...
- WPF 子窗体关闭时显示父窗体
这个问题纠结了两天,今天在一个朋友的帮助下,解决了,其实很简单,但是可能作为新手,接触WPF时间还是短,因此作为一个问题困扰了我. 父窗体部分代码 private void EditInformati ...
- wpf image控件循环显示图片 以达到动画效果 问题及解决方案
1>最初方案: 用wpf的image控件循环显示图片,达到动画效果,其实就是在后台代码动态改变Image.Source的值,关键代码: ; i < ; i++)//六百张图片 { Bitm ...
随机推荐
- 【Codeforces Round #443 (Div. 2) B】Table Tennis
[链接] 我是链接,点我呀:) [题意] n个人站在一排. 每次第一个人和第二个人打架. 输的人跑到队列的尾巴去. 然后赢的人继续在队首.和第三个人打. 谁会先赢K次. [题解] 会发现,一轮之后就一 ...
- 洛谷——P3128 [USACO15DEC]最大流Max Flow
https://www.luogu.org/problem/show?pid=3128 题目描述 Farmer John has installed a new system of pipes to ...
- 飞镖忍者 quick-cocos2d-x3.2
经典的入门小游戏.这里用quick-cocos2d-x3.2又一次写一遍,以便熟悉下quick 首先,创建project,假设不会自行百度啊. 1.编译效果例如以下: watermark/2/text ...
- 读<阿里亿级日活网关通道架构演进>有感
读<阿里亿级日活网关通道架构演进>时对优化方法有些概念不理解,特意搜索了一下,拓展自己的思路. 其中的优化: 优化方法中1,2比较常见,3,4我知道的比较少,很感兴趣.就继续追踪下去: 于 ...
- (嵌入式开发)自己写bootloader之编写第二阶段
内核编译(make)之后会生成两个文件,一个Image,一个zImage,其中Image为内核映像文件,而zImage为内核的一种映像压缩文件,Image大约为4M,而zImage不到2M. ...
- 1、opencv3.3.0和cmake安装步骤(按照以下步骤安装后仅能在PC上运行,动态库也是PC端的属性)
1.下载安装CMake for Linux 下载地址:https://cmake.org/download/ 我下载的版本是,下载文件cmake-3.11.1.tar.gz ./bootstrap m ...
- NIO 入门(转)
NIO 入门 Greg Travis2003 年 11 月 17 日发布 分享此页面 WeiboGoogle+用电子邮件发送本页面 20 在开始之前 关于本教程 新的输入/输出 (NIO) 库是在 J ...
- DBeaver笔记-快捷键篇
公司使用的是PostgreSQL数据库,可以使用pgAdmin或者DBeaver进行连接该数据库.个人更喜欢用DBeaver,因为其界面更加美观,操作也相对简单.对于习惯了eclipse的开发者来说, ...
- [TypeScript] Define a function type
type DigitValidator = (char) => boolean; -]{}/.test(char); export const digitValidators: {[key: s ...
- PHP Filesystem 函数(文件系统函数)(每日一课的内容可以从php参考手册上面来)
PHP Filesystem 函数(文件系统函数)(每日一课的内容可以从php参考手册上面来) 一.总结 1.文件路径中的正反斜杠:当在 Unix 平台上规定路径时,正斜杠 (/) 用作目录分隔符.而 ...