FFmpeg 入门

废话少说,要解码一个视频帧,你需要这样做:

获得帧数据后,用SDL还是直接用D3d渲染,那就看你自己了。

评论

D3DImage in WPF

在.Net Framework 3.5 SP1中,微软在WPF中提供了D3DImage对象,D3DImage是一个ImageSource,这可以让我们在WPF原生的D3D Surface上渲染Direct3D Surface,大大提高了WPF和DirectX内容的交互性。
在此之前,要想在WPF上渲染DirectX的内容,只能让DirectX直接渲染到窗口上,这样会造成不可避免的Airspace问题,因为DirectX内容要时刻刷新重绘,导致WPF窗口上的其他内容被覆盖,表现就是DirectX内容始终在窗口最顶层。
正如前面所说,D3DImage是一个ImageSource,在WPF中,这意味着,我们可以将一个3D场景变成一个Image对象的Source,或者构建一个ImageBrush,这意味这D3D Surface可以渲染到WPF中任意一个以Brush进行渲染的元素上,例如图片,文本前景色,元素背景色等等。
如下,就是一个典型的应用D3DImage的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if (d3dimg.IsFrontBufferAvailable)
{
IntPtr pSurface = IntPtr.Zero;
pSurface = _view.GetBackBuffer();

if (pSurface != IntPtr.Zero)
{
d3dimg.Lock();
d3dimg.SetBackBuffer(D3DResourceType.IDirect3DSurface9,pSurface);
_view.Draw();
d3dimg.AddDirtyRect(new Int32Rect(0, 0, d3dimg.PixelWidth, d3dimg.PixelHeight));
d3dimg.Unlock();
}
}

只需要Direct3D内容渲染的Surface接口指针即可,如上面的pSurface。

评论

【翻译】There Is No Thread

这里并没有线程

原文地址: http://blog.stephencleary.com/2013/11/there-is-no-thread.html


最纯粹的async形式中存在一个重要的真相:这里并没有线程(或者不存在新建的线程)
举不胜数的反对者哭喊道:“不!如果我正在等待一个操作,那一定存在一个线程在等待这个操作!它可能是一个线程池中的线程。或者是系统线程!或者是其他类似设备驱动的东西…”。

不要听从那些哭喊的人。如果那些async操作是纯粹的,那么这里将不会存在线程。

那些持怀疑态度的人并没有被说服。让我们来娱乐一下他们。

我们可以一路跟踪一条异步操作指令到硬件层面,特别留意其中的.Net部分和设备驱动部分。为了简化这部分描述,我们排除掉部分中间层的细节,但是这应该不会让我们偏离真相。

思考一个通用的“写”操作(写一个文件、网络流、USB等等)。我们的代码很简单:

1
2
3
4
5
private async void Button_Clicked(object sender, RoutedEventArgs e)
{
byte[] data = ...
await myDevice.WriteAsync(data, 0, data.Length);
}

我们早就知道,UI线程并不会被await操作阻塞。那么问题来了:这里是不是存在另外一个线程,它牺牲自己所以UI线程才能存活?

抓住我的手,我们要潜的更深一点。

第一站:类库(例如,查看BCL代码)。我们假设 WriteAsync 是用 .Net 标准的 P/Invoke 异步 I/O 操作实现的。所以,这个操作在设备的句柄上开始了一个Win32的重叠I/O操作。

系统紧接着转到设备驱动并让设备开始写操作。它首先构造一个表示写请求的对象,这被称为I/O Request Packet(IRP)。设备驱动获得这个IRP并向设备发起命令来写对应的数据。如果设备支持Direct Memory Access(DMA),这个操作就像向设备寄存器中写入缓存地址一样简单。这是设备驱动能做的所有事情;它使得IRP进入“等待”并转回到系统。

事实的核心:当处理IRP时,设备驱动不允许堵塞。这意味着,如果IRP不能立即完成,那么它一定要异步执行。这对于同步API也一样成立。在设备驱动这一层,所有(重要的)请求都是异步的。
随着IRP进入“等待”状态,系统通过返回一个未完成的Task给刚才堵塞的async按钮点击事件,然后UI线程继续执行。
我们深入追踪到系统底层的写操作,直至物理设备。
现在,写操作正在执行,那有多少线程在处理它呢?

一个都没有

这里并没有设备驱动线程,系统线程,BLC线程或者线程池线程在处理那个写操作。这里根本就没有线程
现在,我们看一下对应的回复(Response)。
写请求开始片刻,设备完成了写操作,他通过中断(Interrupt)通知CPU。
设备驱动的Interrupt Service Routine(ISR)对这个中断做出反应。中断是CPU层的时间,它会临时从当前CPU所运行的线程中获得CPU的控制权。你可以认为ISR是在“借用”当前正在运行的线程,但是我更倾向于认为ISR在更底层中执行,底层到根本不存在线程这个概念的水平。或者说它在所有线程之下。
无论如何,ISR已经被妥当的进行了写操作,它做的所有事情就是告诉设备“谢谢你的中断请求”并且将一个Deferred Procedure Call(DPC)入队(queue)。
当CPU被中断“骚扰”完之后,它会转向它的DPC。DPC也是在一个不能直接用线程描述的底层水平。和ISR一样,DPC直接在CPU上执行,在线程系统之下。
DPC获取代表写操作的ISR并将其标志成“完成”。然而,那么“完成”状态只存在系统层;必须要通知到进程自己拥有的内存空间。所以系统会入队一个special-kernel-mode Asynchronous Procedure Call(APC)给拥有HANDLE的线程。
因为上述提到的类库/BLC使用的是标准P/Inovke Overlapped I/O 系统,它早就注册了I/O Completion Port(IOCP)的句柄,这个句柄是线程池的一部分。所以,一个I/O线程池线程被短暂的“借用”来执行APC,通过它来通知task已经完成。
现在task捕获了UI线程的上下文,它不直接从线程池线程中返回async方法,相反,它将async方法后续的执行加入到UI线程的上下文,当UI线程执行到这里的时候就会继续执行这部分代码。
所以,当请求发生时,我们可以看到这里并没有线程。当请求完成时,大量的线程被“借用”或者短暂暂存到它们那里。这些工作大约在一毫秒(运行在线程池的APC)或者低到一微秒(例如ISR)。但是这里并没有线程因为等待请求完毕而被堵塞。

评论

Mandelbrot Set - 在复平面上绘制曼德博集合

曼德博集合(Mandelbrot set,或译为曼德布洛特复数集合)是一种在复平面上组成分形的点的集合,以数学家本华·曼德博的名字命名。曼德博集合与朱利亚集合有些相似的地方,例如使用相同的复二次多项式来进行迭代。(维基百科)

其中的迭代公式:

1
f(Zn+1)=Zn^2+c

其中,c是任意复数。我们知道c可以表示为:c=x+y*i。根据复数的定义,i^2=-1。因此,我们通过将二维平面当作复平面,x是其中复数的实部R,y是复数的虚部Im。根据上面的额迭代公式,使用OpenCL对每个点进行同步的迭代,快速得到曼德博集合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//openCL代码
kernel void Mandelbrot(
global write_only int* result,
int width,//图片宽度
int height,//图片高度
float minReal,//最小实部
float maxReal,//最大实部
float minIma,//最小虚部
float maxIma,//最大虚部
int max_iter)//最大迭代次数,当迭代这么多次后,该复数还没逃逸,则认为其在曼德博集合中
{
//获取GPU当前线程的编号,可以将这个物化为图片当前位置的一个像素点的计算方位
int tX = global_get_id(0);
int tY = global_get_id(1);

//计算坐标的刻度值
float real_inter = (maxReal - minReal) / width;
float ima_inter = (maxIma - minIma) / height;

//当前线程(像素点)在复平面的位置
int cX = minReal + tx * real_inter;
int cY = minIma + (height - tY) * ima_inter;

//迭代
float zX=0;
float zY=0;
int iter=0;
float length_sqr=0;

do
{
iter++;
float temp = zX * zX - zY * zY + cX;
zY = 2 * zX * zY + cY;
zX = temp;

length_sqr=zX * zX + zY * zY;//在GPU上,根号运算要慢得多
}//根据曼德博集合的性质,我们知道集合的中的任意一个复数|z|<2,因此对于复数z=tX+tY*i,|z|^2=tX*tX+tY*tY<4
while(length_sqr < 4 && iter < max_iter);

int loc = tY * width + tX;
result[loc] = iter;//通过迭代值来标识当前位置的像素点是否属于曼德博集合
}

这种每个点都可以独立迭代运算,互不干扰的特性,最适合用GPU来进行计算的了,因此,通过OpenCL,我们可以快速得到想要的数据。使用 Surface Book 的内置显卡nVidia 520计算3000*3000规模,迭代512次的数据,可以在30ms内完成。如下是通过得到的数据生成的一些配色图:

X光效果

复数与曼德罗集合的相关信息,可参考:https://msdn.microsoft.com/zh-cn/library/jj635753(v=vs.85).aspx

评论

OpenCL with CLOO

CLOO 是一个对 OpenCL 的 .Net 封装,可以让 .Net/Mono 程序充分使用 OpenCL 的优势,易用、开源。

今天用 OpenCL 中的 “Hello World” 程序 - 矩阵乘法,来简单介绍一下 OpenCL。

OpenCL 是一个开放的工业标准,既然是开放的,那么,所有厂商就可以提供自己的实现,例如英特尔,英伟达等等。也正因为如此,也导致在同一台机器上可能存在多个支持不同版本的硬件。例如,英伟达的GPU到现在也才支持 OpenCL 1.2 版本,但是OpenCL都已经出到2.x版本了。

我们可以查看当前设备中,有哪些厂商提供了 OpenCL 的支持,以及运算平台是啥。例如,在 Surface Book (with Nvidia GPU) 上,我们调用以下代码看看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//获取所有平台
var platforms = ComputePlatform.Platforms;

foreach(var platform in platforms)
{
Console.WriteLine($"{platform.Name},{platform.Version}");

//获取该平台下的计算设备
var devices = platform.QueryDevices();
foreach(var device in devices)
{
Console.WriteLine($" Device:{device.Name}");
}
}

查看更多

评论

Timelapse

4K:http://omg3ewm0l.bkt.clouddn.com/timelapse_home.mp4

评论

深挖 WPF 渲染系统

这是一篇2011年的文章,原地址在 A Critical Deep Dive into the WPF Rendering System,个人认为到现在都可以给WPF程序开发人员作为一个参考,里面详细讲述了 WPF 这个号称从底层支持硬件加速的 UI 框架为什么有时候看起来并不是那么回事的原因。以下是正文。


刚开始我并不认为我会发表这篇文章。在被一些我高度重视的人说服后,我决定要发表这篇文章。那些深入投入微软UX平台的程序员应该更加深入了解这个平台内部是怎么工作的,当他们撞到一睹石墙的时候,他们可以清晰了解到问题所在并且更加准确地沟通他们希望平台做出怎样的改变。

我相信 WPF 和 Silverlight 是精心打造的技术,但是…

如果这几个月你有关注我的Twitter,你也许会发现我一直在吐槽WPF(Silverlight也一样)的性能。为什么我会这样做?毕竟这些年我花费了大量的时间为这个平台布道、写库、社区帮助和指导等等。准确来讲,我个人全身投入到这个平台里面,我希望这个平台越来越好。

性能,性能,性能

当在开发沉浸式的、消费者导向的UX的时候,性能是你第一位的特性。性能是你添加其他特性的前提和基础。有多少次因为UI太卡你需要缩小UI的规模?有多少次因为这个技术做不到所以你需要丢弃“开创性的UX模型”?有多少次你告诉客户他们需要一个2.4GHz的四核CPU才能获得全部的体验?我一直以来都被客户问,为什么在PC上拥有4倍于iPad的性能的情况下,WPF程序却做不到像iPad应用那般流畅?

查看更多

评论