Jetson TK1的交叉编译工具链

Jetson TK1是NV推出的非常强大的开发板。除了硬件以外,NV还提供L4T这个以Ubuntu 14.04为基础的Linux系统作为示例软件。但是,不知道何种原因,NV并没有在文档中明确指出如何对Jetons TK1的L4T发行版做交叉编译。于是,我花了一些时间研究一下这个问题并且将结果简单的记在这里。

简而言之,对于在ARM CPU上运行的代码,全部都可以采用Ubuntu Precise提供的工具链进行交叉编译。因为Debian系发行版——包括Ubuntu——很早就开始支持Multiarch,开发者需要做的仅仅是安装支持multiarch的gcc等编译工具而已。具体步骤如下:

## Create backup files
$ sudo mkdir /etc/apt/bak/
$ sudo cp /etc/apt/sources.list /etc/apt/bak/sources.list
$ sudo cp -R /etc/apt/sources.list.d/ /etc/apt/bak/

## Prevent host architecture from pollution
$ sudo sed -i "s/deb /deb [arch=amd64,i386] /g" /etc/apt/sources.list
$ sudo sed -i "s/deb /deb [arch=amd64,i386] /g" /etc/apt/sources.list.d/*

## Add debian percise repository
$ sudo sh -c 'echo "deb http://jp.archive.ubuntu.com/ubuntu/ precise main universe" >> /etc/apt/sources.list'
$ sudo sh -c 'echo "deb-src http://jp.archive.ubuntu.com/ubuntu/ precise main universe" >> /etc/apt/sources.list'

## Update apt
$ sudo apt-get update

## Install armhf toolchain
$ sudo apt-get install g++-4.6-arm-linux-gnueabihf g++-arm-linux-gnueabihf

安装结束后可有用yavta项目来做工具链的验证。

$ git clone git://git.ideasonboard.org/yavta.git
$ cd yavta
$ CROSS_COMPILE=arm-linux-gnueabihf- make

检查交叉编译的的结果。

$ file ./yavta 
./yavta: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 2.6.31, BuildID[sha1]=9d2e056f30c0e01bdab26b64c97a5647b304d131, not stripped

然后就可以将编译好的二进制文件拷贝到target系统上运行了。

因为这样的工具链仅仅可以编译在CPU上运行的代码,对于在GPU上运行的代码来说仍然是无效的。所以,要彻底得做到对CUDA代码进行编译的话,还需要做更多努力。持续探索中。

OpenGL ES模拟器

OpenGL ES 3.0 programming guide这本书提供了很多例子。为了在PC上运行这些例子,我决定使用OpenGL ES 3.0的模拟器来做这件事。而且,这次我决定使用Windows :)。由于长期以来,我都在使用类Unix系统工作,对于如何在Windows系统中编程这件事情非常不在行。在屡次尝试才得到结果之后,我决定把这个过程记录下来。

安装OpenGL ES模拟器之后,EGL和GSES两个基本库都被安装进入ARM\Mali Developer Tools\Mali OpenGL ES Emulator 2.2.1。用git克隆这些代码

https://github.com/danginsburg/opengles3-book/

下载并安装CMake的Windows版本并且使用CMake对这份代码进行配置

cmake -G "Visual Studio 14 2015 Win64" \
-DEGL_LIBRARY="C:\Program Files\ARM\Mali Developer Tools\Mali OpenGL ES Emulator 2.2.1\libEGL.lib" \
-DOPENGLES3_LIBRARY="C:\Program Files\ARM\MaliDeveloper Tools\Mali OpenGL ES Emulator 2.2.1\libGLESv2.lib"

其中,-G "Visual Studio 14 2015 Win64"是帮助CMake产生在64bit的Windows上运行Visual Studio的Project文件。用Visual Studio打开CMake产生的solution文件,编译并链接。打开

opengles3-book\Chapter_2\Hello_Triangle\Debug\Hello_Triangle.exe

就可以看到这个三角形了。

hello-triangle

OpenGL中的齐次坐标

偶然看到WikiBook上的OpenGL教程中的第一个例程给出使用openGL在显示器上画出三角形的例子:

https://gitlab.com/wikibooks-opengl/modern-tutorials/blob/master/tut01_intro-sdl2/triangle.cpp

这个例程几乎是所有OpenGL教程的Hellow World。然而,几乎所有的教程都会忽略一个非常重要的细节——即,在OpenGL中究竟是如何表示顶点坐标的。比如,上面的这段程序中,定义三角形顶点坐标的代码有两处:

第一处非常直观,在render函数中,该程序定义了在平面坐标系上的三个点:

GLfloat triangle_vertices[] = {
    0.0,  0.8,
   -0.8, -0.8,
    0.8, -0.8,
};

如果将这三个点在平面直角坐标系画出来的话,应该是这样的:

2d-triangle

另一处,这些顶点的坐标在Vertex Shader之中进行了变换:

const GLchar* vs_sources[] = {
	version,
	"attribute vec2 coord2d;                  "
	"void main(void) {                        "
	"  gl_Position = vec4(coord2d, 0.0, 1.0); "
	"}"
};

对于每一个代入到该Shader Kernel的顶点,这个计算会将二维坐标转变为一个三维空间中的齐次坐标。中文的解释非常难懂,所以,这里给出英文链接。简而言之,一个三维空间中的点,用直角坐标系表示时,这个点是一个有三个元素的向量:

\begin{bmatrix}  x \\ y \\ z  \end{bmatrix}

但是,使用齐次坐标之后,三维空间中的点需要用含有四个元素的向量表示:

\begin{bmatrix}  x\\   y\\   z\\   W  \end{bmatrix}

在齐次坐标中,如果两个坐标成比例,那么这两个坐标表示的就是同一个点;例如,(1, 2, 3, 0.5)(2, 4, 6, 1)表示的是同一个点。在这种情况下,坐标(0, 0, 0)是原点,且唯一。

当读到这段代码的时候,不禁要问,为什么在Vertext Shader中要采用如此多次一举的方式表达一个三维空间中的点?直接使用直角坐标系的(x, y, z)不是更直观吗?

之所以在图形计算中使用齐次坐标而不是直角坐标的原因在于——为了简化计算!在未加入这第四维时,三维的图形变化用矩阵表示如下:

{P}' = T + P
{P}' = S \cdot P
{P}' = R \cdot P

其中TSR分别代表平移、缩放和旋转操作。但是,这三个操作之中缩放和旋转都是矩阵乘法,仅有平移操作是加法。如果这些变换不能被统一成为同一种运算的话,用来专门做图形计算的硬件的设计复杂度就会大大增加。数学先贤们想出来的办法就是给坐标加一个维度,将以上三个操作全部统一成矩阵乘法。具体如何?Google一下就知道啦!这里是斯坦福给大答案