Vim 异步运行 Shell 指令的插件 – AsyncRun

自制另一个新的 Vim 8.0 专用异步插件:asyncrun.vim,它可以让你在 Vim 里面异步运行各种 Shell 指令并且把结果实时输出到 Quickfix,需要 Vim 7.4.1829 以上版本。

安装方法

到插件首页 https://github.com/skywind3000/asyncrun.vim 下载项目,并拷贝 asyncrun.vim 到你的 ~/.vim/plugin。或者使用 Vundle 指向 skywind3000/asyncrun.vim 来自动更新。

基本教程

使用 gcc 异步编译当前文件:

:AsyncRun gcc % -o %<
:AsyncRun g++ -O3 % -o %< -lpthread 

该命令会在后台运行 gcc 并且把输出实时显示在 Quickfix 窗口,宏 % 代表当前文件名,%< 代表没有扩展名的文件名。

异步运行 make:

:AsyncRun make
:AsyncRun make -f makefile

异步调用 grep:

:AsyncRun! grep -R word . 
:AsyncRun! grep -R <cword> .

默认 :AsyncRun 运行命令后,输出添加到 Quickfix时 Quickfix 会自动滚动到最下面那一行,使用感叹号修饰符以后,可以避免 Quickfix 自动滚动。同时 <cword> 代表当前光标下面的单词。

编译 go项目:

:AsyncRun go build %:p:h

%:p:h 代表当前文件的目录

查询 man page,异步 git push ,以及把设置 F7异步编译当前文件:

:AsyncRun! man -S 3:2:1 <cword> 
:AsyncRun git push origin master
:noremap <F7> :AsyncRun gcc % -o %< <cr> 

使用手册

AsyncRun – Run shell command:

:AsyncRun{!} [cmd] ...

后台运行命令并且实时输出到 quickfix 窗口,如果有感叹号修饰符,quickfix 窗口的自动滚动将会禁止。

命令参数以空格分割,接受下面这些以 ‘%‘, ‘#‘ or ‘<‘ 开头的替换宏:

%:p     - File name of current buffer with full path
%:t     - File name of current buffer without path
%:p:h   - File path of current buffer without file name
%:e     - File extension of current buffer
%:t:r   - File name of current buffer without path and extension
%       - File name relativize to current directory
%:h:.   - File path relativize to current directory
<cwd>   - Current working directory
<cword> - Current word under cursor
<cfile> - Current file name under cursor

运行一个命令前,环境变量也会做如下设置:

$VIM_FILEPATH  - File name of current buffer with full path
$VIM_FILENAME  - File name of current buffer without path
$VIM_FILEDIR   - Full path of current buffer without the file name
$VIM_FILEEXT   - File extension of current buffer
$VIM_FILENOEXT - File name of current buffer without path and extension
$VIM_CWD       - Current directory
$VIM_RELDIR    - File path relativize to current directory
$VIM_RELNAME   - File name relativize to current directory 
$VIM_CWORD     - Current word under cursor
$VIM_CFILE     - Current filename under cursor
$VIM_GUI       - Is running under gui ?
$VIM_VERSION   - Value of v:version
$VIM_MODE      - Execute via 0:!, 1:makeprg, 2:system()
$VIM_COLUMNS   - How many columns in vim's screen
$VIM_LINES     - How many lines in vim's screen

AsyncStop – Stop the running job:

:AsyncStop{!}

停止后台任务(使用 TERM信号),如果有感叹号修饰,则使用 KILL 信号结束

基本设置:

g:asyncrun_exit - 字符串,如果不为空那么任务结束时会被执行(VimScript)
g:asyncrun_bell - 如果非零的话,任务结束后会响铃(终端输出控制符 \a)
g:asyncrun_mode - 0:异步(需要 vim 7.4.1829) 1:同步 2:直接运行

全局变量:

g:asyncrun_code   - 退出码
g:asyncrun_status - 状态 'running', 'success' or 'failure'

如果你喜欢的话请为我投一票:
http://www.vim.org/scripts/script.php?script_id=5431

Loading

Posted in 随笔 | Tagged | 2 Comments

3D 图形光栅化的透视校正问题

写了文章《如何写一个软件渲染器》以后,不少网友希望进一步解释背后的数学公式,询问以及自己加一个 phong 光照该如何加,本文将对透视纹理映射的插值原理做一个简单的解释,希望能帮助到大家:

透视纹理绘制发生在最后阶段,坐标已经完成 projection,剔除,裁剪了,然后顶点/w,开始批量绘制扫描线之前,这时候开始计算纹理的位置。

使用 w 还是用 z,关系不大,早年的 3D 引擎,直接 /z 的,只是后面标准化了以后,发现 w 更好用,可以同时表示透视投影和正交投影。同时顶点经过标准的 mvp 矩阵运算后,w 和 z 是承线性关系的,方便对 z/w 做 [0,1] 的 cvv 裁剪。你可以理解成 w 就是另外一个 z。以前屏幕坐标:

x' = x / z * d + A
y' = y / z * d + A

现在是

x' = x / w * d + A
y' = y / w * d + A

然后绘制纹理前,你需要先证明屏幕上两个点之间,1/w 承线性关系,即屏幕上两个点X1′, X2′ 之间任意取一点 X3’,他们的 (1/w) 值的变化比例相同,即在 t 取任意值有:

x3' = x1' + (x2' - x1') * t
(1 / w3) = (1 / w1) + ((1 / w2)  - (1 / w1)) * t

再根据他们在同一个平面上,证明屏幕上两个点之间,u/w, v/w 承线性关系,即 t 取任意值有:

x3' = x1' + (x2' - x1') * t
(u3 / w3) = (u1 / w1) + ((u2 / w2) - (u1 / w1)) * t
(v3 / w3) = (v1 / w1) + ((v2 / w2) - (v1 / w1)) * t

具体到代码里面的做法就是三角形的三个顶点 /w 以后,u 和 v 也同时/w,然后把w换成自己的倒数:w = 1 / w,及把顶点数据:

(x, y, z, w) + (u, v)

变换成:

(x / w, y / w, z / w, 1 / w) + (u / w, v / w)

然后用 1/w, u/w, v/w 进行屏幕空间插值,具体绘制某个点的时候,先从 1/w 求倒得到 w,然后乘以 u/w, v/w 得到 u, v,就可以了。

更进一步,可以证明,所有在三维空间里同 x,y,z 成线性关系的变量,不管是纹理坐标,顶点色或者法向还是其他,他们在屏幕空间里的插值规则都可以通过:插值前先 /w ,插值后要用时再 * w 得到具体值,然后我们把这类三维空间里同 x,y,z 成线性关系的变量统进行统一的批量处理,和 OpenGL 的 attribute,varying 处理方法相同。

相关阅读:
如何写一个软件渲染器
OpenGL / DirectX 如何在知道顶点的情况下得到像素位置?
还原被摄像机透视的纹理

Loading

Posted in 图形编程 | Tagged | 1 Comment

如何用 OpenGL 封装一个 2D 引擎?

如何正确的使用 OpenGL “封装一个2D引擎” ?以下几个步骤:

1. 别用什么 glBegin/glEnd,至少写兼容GLES2代码,不然手机上跑不起来。
2. 用两个三角形的纹理拼凑出一个2D的图块出来,不是搞啥每个点自己画。
3. 2D图像库基本就是要把显示对象树给做出来就得了。
4. 每个显示对象除了自己外还有很多儿子节点。
5. 每个显示对象有一个变换矩阵,用来设置位置和角度还有缩放,最后是节点的显示效果。
6. 渲染的时候需要从远到近排序,并尽量归并相同效果(fs)及纹理。
7. 把常用纹理管理起来,提供资源加载,可以换进换出,提供类 LRU的机制。
8. 在此基础上提供一些动画(精灵)和场景控制的api,提供显示字体,即可。

最后推荐两个现成的轻量级2D引擎供阅读:

StarEngine:GitHub – StarEngine/engine: Crossplatform C++11 2D Game Engine for Desktop and Mobile games

EJoy2D:GitHub – ejoy/ejoy2d: A 2D Graphics Engine for Mobile Game

就是这样。

Loading

Posted in 图形编程 | Tagged | Leave a comment

Vim 异步编译插件 vimmake

推荐下自己写的用了好几年的编程插件:vimmake ?完美支持 vim的异步模式:
GitHub – skywind3000/vimmake: Customize shell commands in vim

让用户自定义各种不同的编译或运行任务,并且在 Vim 中执行他们。类似 NotePad++的自定义 Commands 和 EditPlus/UltraEdit 的 ‘User Tool’ 或者 GEdit中的 External Tool 以及 TextMate 中的 Shell Command。完美支持 vim 7.4.1829 后已经稳定成熟的异步任务机制,不需要写任何 Vim Script 也可以很容易的体验到 vim 的异步任务机制,并且使用它来执行各种艰巨的编译任务,让你一边编辑代码,一边跑编译任务。

安装:拷贝 vimmake.vim 到你的 ~/.vim/plugin 或用 vundle 指向 skywind3000/vimmake .

简单使用:异步编译 & 运行 C/C++ 代码

首先每个 “用户自定义工具” 使用一个独立的 shell脚本来描述(Windows下是.cmd的批处理文件),我们将首先编写 vimmake可以使用的 gcc编译工具脚本, “~/.vim/vimmake.gcc”:

#! /bin/sh
gcc "$VIM_FILEPATH" -o "$VIM_FILEDIR/$VIM_FILENOEXT"

就这么短短的两行,当你把它设置成 0755的权限时,就可以在 Vim中通过下面语句运行了:

:VimTool gcc

命令 :VimTool {name} 命令会在 Vim里面直接调用 ~/.vim/ 目录下,名为 “vimmake.{name}” 的脚本来完成各种类似编译或者执行的任务,所以用:VimTool gcc 就可以运行前面定义的名为 vimmake.gcc 的工具脚本来编译当前的源代码了。

现在编辑 “~/.vim/” 下面名为 “vimmake.run” 的脚本,以便用 :VimTool run 来运行当前代码:

#! /bin/sh
"$VIM_FILEDIR/$VIM_FILENOEXT"

记得将 vimmake.run 的模式设置成 0755,如今有了两个可以直接在 Vim里通过 VimTool命令启动的工具(gcc 和 run),接下来我们需要设置 run 这个工具的模式为默认运行模式,而 gcc 为 quickfix模式(输出会被捕获并重定向到 quickfix窗口),现在打开 .vimrc 添加一行:

let g:vimmake_mode = { 'gcc':'quickfix', 'run':'normal' }

而如果我们能够使用到较新版本的 vim(7.4.1829或者更高),我们就可以使用异步方式在后台启动 gcc,并且将后台进程的输出实时重定向到界面下端的 quickfix 窗口:

let g:vimmake_mode = { 'gcc':'async', 'run':'normal' }

在这之后,将 ‘gcc’的运行模式设置成 ‘async’ 后,:VimTool gcc 就可以以异步的方式运行名为 vimmake.gcc 的脚本然后在后台执行编译任务了,就像传统 IDE编译任务一样。

以往使用 Vim 的 :make 之类的命令编译项目时,往往无法异步,编译任务一运行,你就无法编辑了,只有等到编译结束,才能返回编辑状态,大项目时,不得不另外开一个终端来进行编译,这是很痛苦的事情,有了异步任务以后,你能在同一个屏幕下编辑并且实时在 quickfix窗口查看编译的进度。

vimmake 可以让你在不需要掌握晦涩的 VimScript 和繁琐的异步编程接口的情况下,直接方便的使用vim 异步功能来完成各种长时间编译任务。同时为了加快你的:编译-编辑-编译 工作流的流畅度,我们需要配置一两个热键来调用 :VimTool 命令:

noremap <F7> :VimTool gcc<cr>
noremap <F5> :VimTool run<cr>
inoremap <F7> <ESC>:VimTool gcc<cr>
inoremap <F5> <ESC>:VimTool run<cr>

在 .vimrc里面加入上面的几行代码,你就可以方便的按 F7编译当前文件,F5运行之了。和GEdit
类似,VimTool命令在运行具体工具脚本前会设置若干环境变量来记录当前编辑的文件名,路径,当前vim工作目录等一系列信息,然后在工具脚本里面可以直接取出这些值来调用需要的外部工具链:
如上图,后台运行工具脚本进行编译,并且编译器的输出会被实时显示到下面的 quickfix窗口,选中错误的那行输出,会直接跳转:

Continue reading

Loading

Posted in 随笔 | Tagged | 1 Comment

学习视频编解码知识需要哪些前置知识?

如果要随便学学,便于日后使用那花两个星期买本书,配合网上文章就行了。

如果你想自己动手改 x264,为其添加一些你想要的东西,那么下面步骤你得耐心走完:

1: JPEG编码不但要学,还要自己实现,这是图像编码的基础,理解下yuv, dct, 量化,熵编码(不用参考 libjpeg,太庞大,建议参考 tinyjpeg.c,单文件)

2: MPEG2编码要学,现代编码器都是 block based 的,而 block based编码器的祖先就在MPEG2,理解下帧内编码,帧间预测,运动矢量,残差图等基础概念。具体代码可以看早期版本的 ffmpeg 的 avcodec,比如 mpeg12enc.c 代码也就1000多行,容易看,不过其中牵扯很多ffmpeg的内部数据结构,比如 picture, DCTELEM,各种 table,bitstream,vlc, swscale 等公共模块,缺点是文档少,优点是读了这些对你读其他 ffmpeg代码有帮助。

3: 自己实现一个类 MPEG2 编码器,最好自己从头实现个编码器,具体实现方式可以参考我的上面提到的 “视频编码技术简介”。

4: 参照 MPEG2的原理阅读 h.264的相关文章和书籍,了解和MPEG2的异同,比如先从intra入手,并且阅读 x264的早期版本代码,比如 2005年的版本,重点阅读 common 目录,基本的数据结构都在那里了,基本的图像,宏块,预测等都在那里了,阅读完以后阅读 encoder目录,了解程序的结构,2005版本的 x264是今天 x264的基础。

5: 阅读最新的 x264代码,并整理代码脉络,了解近年来引入的各种优化方法,然后 google, google, google …….

6: 愉快的修改 x264吧,比如增加搜索强度,修改预测范围,增加抗丢包特性,或者增加带内编码冗余,修改内部缓存策略,寻找降低编码延迟的方法,根据你的需求,修改,测试,修改,测试。。。。。。

7: 至于 MPEG4文件格式,可看可不看,一下午的事情。

参考阅读:

视频编码器原理简介

Loading

Posted in 编程技术 | Tagged | Leave a comment

如何禁止 Win7 强制更新到 Windows 10 ?

岳父岳母,爹妈的 Windows 7 纷纷被自动升级成 Windows 10 各种问题(一台画面变成非全屏,解析度被莫名奇妙降低,占不满显示器、运行卡,另一台台式机无线网卡不能用,还有一台某炒股软件跑不起来,按win键弹出菜单时会顿一下)。

花费了我两个整天的时间帮他们降级,找了很多方法,网上大部分都不能对新版 Gwx起作用

终于,找到了并且永久性锁定自动升级的最直接方式了:

regedit:
HKEY_LOCAL_MACHINE 下面
SOFTWARE\Policies\Microsoft\Windows 下面新建项目(目录)Gwx
然后在
SOFTWARE\Policies\Microsoft\Windows\Gwx 下面新建 DWORD 值:
DisableGwx=1

好了,Windows 结扎手术完成,不用当心一不留神弄出个 Windows 10 来给大伙惹麻烦了。

补充:本来上次回家时间就短,好些事情没办,好些朋友没见,好了,微软一个强制升级,就把你锁家里了。

自己的事情也算小事了,爹妈平常和岳父母平日和我们聚少离多,也就爱炒个股看个基金寻找下乐趣,你把人家弄的用不了了,不好用了,老人家自己不会弄,又舍不得花钱请人修,只能眼巴巴花好几个月的时间等着你回去帮他们解决。这样的感受是很不好的,就像公园里两个大爷正高兴的下着棋,过来个顽皮小孩一把把老人的棋盘掀湖里去了,周围的人都会觉得小孩有些缺乏教养,没有公德心一般。

很多喜欢宠物的人为了不给大家添麻烦都倡导给自己的狗或猫做节育手术,真爱宠物就要对它负责,所以我们也本着对windows负责的态度对它做个结扎,防止它趁你不留神在外面闯祸,避免给大家添麻烦,对,公德心嘛。

Loading

Posted in 随笔 | Tagged | Leave a comment

计算机底层是如何访问显卡的?

以前 DOS下做游戏,操作系统除了磁盘和文件管理外基本不管事情,所有游戏都是直接操作显卡和声卡的,用不了什么驱动。

虽然没有驱动,但是硬件标准还是放在那里,VGA, SVGA, VESA, VESA2.0 之类的硬件标准,最起码,你只做320x200x256c的游戏,或者 ModeX 下 320x240x256c 的游戏的话,需要用到VGA和部分 SVGA标准,而要做真彩高彩,更高分辨率的游戏的话,就必须掌握 VESA的各项规范了。

翻几段以前写的代码演示下:

例子1: 初始化 VGA/VESA 显示模式

基本是参考 VGA的编程手册来做:

INT 10,0 - Set Video Mode
    AH = 00
    AL = 00  40x25 B/W text (CGA,EGA,MCGA,VGA)
       = 01  40x25 16 color text (CGA,EGA,MCGA,VGA)
       = 02  80x25 16 shades of gray text (CGA,EGA,MCGA,VGA)
       = 03  80x25 16 color text (CGA,EGA,MCGA,VGA)
       = 04  320x200 4 color graphics (CGA,EGA,MCGA,VGA)
       = 05  320x200 4 color graphics (CGA,EGA,MCGA,VGA)
       = 06  640x200 B/W graphics (CGA,EGA,MCGA,VGA)
       = 07  80x25 Monochrome text (MDA,HERC,EGA,VGA)
       = 08  160x200 16 color graphics (PCjr)
       = 09  320x200 16 color graphics (PCjr)
       = 0A  640x200 4 color graphics (PCjr)
       = 0B  Reserved (EGA BIOS function 11)
       = 0C  Reserved (EGA BIOS function 11)
       = 0D  320x200 16 color graphics (EGA,VGA)
       = 0E  640x200 16 color graphics (EGA,VGA)
       = 0F  640x350 Monochrome graphics (EGA,VGA)
       = 10  640x350 16 color graphics (EGA or VGA with 128K)
         640x350 4 color graphics (64K EGA)
       = 11  640x480 B/W graphics (MCGA,VGA)
       = 12  640x480 16 color graphics (VGA)
       = 13  320x200 256 color graphics (MCGA,VGA)
       = 8x  EGA, MCGA or VGA ignore bit 7, see below
       = 9x  EGA, MCGA or VGA ignore bit 7, see below

    - if AL bit 7=1, prevents EGA,MCGA & VGA from clearing display
    - function updates byte at 40:49;  bit 7 of byte 40:87
      (EGA/VGA Display Data Area) is set to the value of AL bit 7

转换成代码的话,类似这样:

// enter standard graphic mode
int display_enter_graph(int mode)
{ 
    short hr = 0;
    union REGS r;
    memset(&r, 0, sizeof(r));
    if (mode < 0x100) { 
        r.w.ax = (short)mode;
        int386(0x10, &r, &r);
        r.h.ah = 0xf;
        int386(0x10, &r, &r);
        if (r.h.al != mode) hr = -1;
    }   
    else { 
        r.w.ax = 0x4f02;
        r.w.bx = (short)mode;
        int386(0x10, &r, &r);
        if (r.w.ax != 0x004f) hr = -1;
    }
    return hr;
}

Continue reading

Loading

Posted in 图形编程 | Tagged , | 2 Comments

如何在高丢包率的链路上建立低延迟连接?

这是其实通信领域的话题了,低延迟传输有上百种优化方式,上面说的那些冗余码只是很小一部分,不考虑信道容量的冗余编码系统都是在耍流氓,不用等到同信道内跑两套这样的协议你才会发现问题,一套协议再接近信道带宽容量限制时,就会出现指数上升的丢包率,所以不考虑带宽检测的冗余法就是一个残次品。

要系统的解决低延迟传输问题,需要同时在传输层,协议层,路由层,应用层几个方面着手:

传输层带外冗余:弱智重复法

设你要发送的数据为 x1-xn,你实际发送出去的包为 p1-pn,那么比如 Pn = [Xn, Xn-1, Xn-2],重复前面出现过的 1-2个数据,丢包了你可以随时恢复出来。

传输层带外冗余:异或法

每发四个包 x1-x4,你多发一个冗余包,内容为前面四个包的异或: R = x1 ^ x2 ^ x3 ^ x4,那么本组数据五个数据包(x1-x4, R)中任意丢失一个,都可以从其他四个异或得到。当然,不一定要四个包冗余一个,你可以根据情况和丢包率,两个包或者三个包冗余一个。

传输层带外冗余:解方程法

把每个数据包看成一个整数,要发送 x1-x4 四个包,并不直接发送x,而是将他们进行线性运算得到 y1-y7,然后发送出去:

A1 * x1 + B1 * x2 + C1 * x3 + D1 * x4 = y1   收到
A2 * x1 + B2 * x2 + C2 * x3 + D2 * x4 = y2   收到
A3 * x1 + B3 * x2 + C3 * x3 + D3 * x4 = y3   丢失
A4 * x1 + B4 * x2 + C4 * x3 + D4 * x4 = y4   收到
A5 * x1 + B5 * x2 + C5 * x3 + D5 * x4 = y5   丢失
A6 * x1 + B6 * x2 + C6 * x3 + D6 * x4 = y6   收到
A7 * x1 + B7 * x2 + C7 * x3 + D7 * x4 = y7   丢失

接收方收到一组数据以后,发现y3, y5, y7丢失,得到:

A1 * x1 + B1 * x2 + C1 * x3 + D1 * x4 = y1   
A2 * x1 + B2 * x2 + C2 * x3 + D2 * x4 = y2   
A4 * x1 + B4 * x2 + C4 * x3 + D4 * x4 = y4   
A6 * x1 + B6 * x2 + C6 * x3 + D6 * x4 = y6   

如果 y1-y7 在发送的过程中丢失了三个:y3, y5, y7 观察等式。变量任然有4个,参数也任然有四个A1, A2, A4, A6, B?, C?, D? 四个变量,四个等式,那么我们可以通过解方程求出x1-x4,这就是一个矩阵运算和求逆的过程,也就是俗称的 Read-Solomon 冗余码的基本原理(PS: shorthair/long hair 两个库写的很烂呀),实际传输时需要根据网络质量来选择每组数据和冗余包的比例。

传输层网络评估

需要一套比较强大的系统,实时评估当前网络质量(RTT, 丢包率,抖动值,可用带宽),为协议决策提供参考,比如这时一个带宽很大,延迟却很高的信道呢?还是带宽很小,延迟也很小的信道呢?不同的情况对应不同的策略,当前丢包是常规丢包?震荡性丢包?还是接近信道限制出现无可挽回的丢包?再根据当前协议出于什么情况?交互模式还是单向传输模式,来给出最佳的传输策略。不考虑这些情况的协议,都是比较弱智的(比如楼上提到的几种)。

协议层决策模型

根据不同的情况,来推导不同的数据包丢失后,对整体协议的影响,从而通过马科夫决策过程,来找到哪个包丢失的代价最大,以此来指导冗余包的生成过程:

Continue reading

Loading

Posted in 网络编程 | Tagged , | Leave a comment