用SSE加速CPU蒙皮计算
http://blog.csdn.net/garuda/article/details/6539271
我们知道现在绝大多数情况下角色动画的蒙皮计算是放在GPU中计算的。
但是仍然有一些特殊的场合我们需要在CPU端使用蒙皮计算的结果,比如涉及到
布料的物理模拟的时候。这时我们需要在CPU端计算蒙皮。
为了节约宝贵的CPU计算的时间,我们需要用SSE对CPU计算蒙皮进行加速。
顶点结构如下:
struct Vertex
{
float3 Pos;
float3 Normal;
int n; //该顶点蒙到了几个骨骼上
int BoneId[4];
float Weight[3];
.... //切线等等
};
对于每个顶点的计算过程是先由BoneId和Weight计算出变换矩阵,然后用矩阵变换
顶点的位置,法线等。其中求变换矩阵是对多个矩阵加权求和的过程,这一步很适合
使用SSE加速。
每个顶点可能蒙到1-4个骨骼上,也就是每个顶点可能需要对1-4个矩阵加权求和。
1个骨骼的时候不用计算。2-4个骨骼的时候,我们分别写3个函数对应这3种情况。
__forceinline void LoadFourFloats(float* a0, __m128& res)
{
res = _mm_load_ps(a0);
}
__forceinline void StoreFourFloats(float* a0, const __m128& src)
{
_mm_store_ps(a0, src);
}
__forceinline void MulMatrixFloat(__m128& mo0, __m128& mo1, __m128& mo2,
const __m128& mi0, const __m128& mi1, const __m128& mi2,
float w)
{
__m128 xmm;
xmm = _mm_load_ss(&w);
xmm = _mm_shuffle_ps(xmm,xmm,0);
// Multiply matrix 1 by weight 1.
mo0 = _mm_mul_ps(xmm, mi0);
mo1 = _mm_mul_ps(xmm, mi1);
mo2 = _mm_mul_ps(xmm, mi2);
}
__forceinline void Collapse2MatSSE(float* pM1, float* pM2,
float W1, float W2, float* pR)
{
__m128 xmm1, xmm2, xmm3;
__m128 xmm4, xmm5, xmm6;
// Load matrix 1.
LoadFourFloats(pM1 + 0, xmm1);
LoadFourFloats(pM1 + 4, xmm2);
LoadFourFloats(pM1 + 8, xmm3);
MulMatrixFloat(xmm1, xmm2, xmm3, xmm1, xmm2, xmm3, W1);
// Load matrix 2.
LoadFourFloats(pM2 + 0, xmm4);
LoadFourFloats(pM2 + 4, xmm5);
LoadFourFloats(pM2 + 8, xmm6);
MulMatrixFloat(xmm4, xmm5, xmm6, xmm4, xmm5, xmm6, W2);
// Add matrix 1 to matrix 2.
xmm1 = _mm_add_ps(xmm1, xmm4);
xmm2 = _mm_add_ps(xmm2, xmm5);
xmm3 = _mm_add_ps(xmm3, xmm6);
StoreFourFloats(pR + 0, xmm1);
StoreFourFloats(pR + 4, xmm2);
StoreFourFloats(pR + 8, xmm3);
}
__forceinline void Collapse3MatSSE(float* pM1, float* pM2, float* pM3,
float W1, float W2, float W3, float* pR)
{
__m128 xmm1, xmm2, xmm3;
__m128 xmm4, xmm5, xmm6;
// Load matrix 1.
LoadFourFloats(pM1 + 0, xmm1);
LoadFourFloats(pM1 + 4, xmm2);
LoadFourFloats(pM1 + 8, xmm3);
MulMatrixFloat(xmm1, xmm2, xmm3, xmm1, xmm2, xmm3, W1);
// Load matrix 2.
LoadFourFloats(pM2 + 0, xmm4);
LoadFourFloats(pM2 + 4, xmm5);
LoadFourFloats(pM2 + 8, xmm6);
MulMatrixFloat(xmm4, xmm5, xmm6, xmm4, xmm5, xmm6, W2);
// Add matrix 1 to matrix 2.
xmm1 = _mm_add_ps(xmm1, xmm4);
xmm2 = _mm_add_ps(xmm2, xmm5);
xmm3 = _mm_add_ps(xmm3, xmm6);
// Load matrix 2.
LoadFourFloats(pM3 + 0, xmm4);
LoadFourFloats(pM3 + 4, xmm5);
LoadFourFloats(pM3 + 8, xmm6);
MulMatrixFloat(xmm4, xmm5, xmm6, xmm4, xmm5, xmm6, W3);
// Add matrix 1 to matrix 2.
xmm1 = _mm_add_ps(xmm1, xmm4);
xmm2 = _mm_add_ps(xmm2, xmm5);
xmm3 = _mm_add_ps(xmm3, xmm6);
StoreFourFloats(pR + 0, xmm1);
StoreFourFloats(pR + 4, xmm2);
StoreFourFloats(pR + 8, xmm3);
}
__forceinline void Collapse4MatSSE(float* pM1, float* pM2, float* pM3, float* pM4,
float W1, float W2, float W3, float W4, float* pR)
{
__m128 xmm1, xmm2, xmm3;
__m128 xmm4, xmm5, xmm6;
// Load matrix 1.
LoadFourFloats(pM1 + 0, xmm1);
LoadFourFloats(pM1 + 4, xmm2);
LoadFourFloats(pM1 + 8, xmm3);
MulMatrixFloat(xmm1, xmm2, xmm3, xmm1, xmm2, xmm3, W1);
// Load matrix 2.
LoadFourFloats(pM2 + 0, xmm4);
LoadFourFloats(pM2 + 4, xmm5);
LoadFourFloats(pM2 + 8, xmm6);
MulMatrixFloat(xmm4, xmm5, xmm6, xmm4, xmm5, xmm6, W2);
// Add matrix 1 to matrix 2.
xmm1 = _mm_add_ps(xmm1, xmm4);
xmm2 = _mm_add_ps(xmm2, xmm5);
xmm3 = _mm_add_ps(xmm3, xmm6);
LoadFourFloats(pM3 + 0, xmm4);
LoadFourFloats(pM3 + 4, xmm5);
LoadFourFloats(pM3 + 8, xmm6);
MulMatrixFloat(xmm4, xmm5, xmm6, xmm4, xmm5, xmm6, W3);
xmm1 = _mm_add_ps(xmm1, xmm4);
xmm2 = _mm_add_ps(xmm2, xmm5);
xmm3 = _mm_add_ps(xmm3, xmm6);
LoadFourFloats(pM4 + 0, xmm4);
LoadFourFloats(pM4 + 4, xmm5);
LoadFourFloats(pM4 + 8, xmm6);
MulMatrixFloat(xmm4, xmm5, xmm6, xmm4, xmm5, xmm6, W4);
xmm1 = _mm_add_ps(xmm1, xmm4);
xmm2 = _mm_add_ps(xmm2, xmm5);
xmm3 = _mm_add_ps(xmm3, xmm6);
StoreFourFloats(pR + 0, xmm1);
StoreFourFloats(pR + 4, xmm2);
StoreFourFloats(pR + 8, xmm3);
}
计算顶点蒙皮矩阵的过程如下
// 为了使用sse优化,这个矩阵必须是16字节对齐的。
// release 编译时编译器会自动保证这一点,但
// 要debug正确运行必须加上内存对齐的声明.
__declspec(align(16)) Matrix4 matObj;
for (int i = 0; i < vertCount; i++, pVertex++)
{
if (pVertex->n == 1)
{
matObj = *BoneMatrixPalette[pWeight->nBones[0]];
}
else if (pVertex->n == 2)
{
float* pMat0 = BoneMatrixPalette[pVertex->nBones[0]]->ToFloatPtr();
float* pMat1 = BoneMatrixPalette[pVertex->nBones[1]]->ToFloatPtr();
Collapse2MatSSE(pMat0, pMat1,pVertex->Weight[0], pVertex->Weight[1], matObj.ToFloatPtr());
}
else if (pVertex->n == 3)
{
float* pMat0 = BoneMatrixPalette[pWeight->nBones[0]]->ToFloatPtr();
float* pMat1 = BoneMatrixPalette[pWeight->nBones[1]]->ToFloatPtr();
float* pMat2 = BoneMatrixPalette[pWeight->nBones[2]]->ToFloatPtr();
Collapse3MatSSE(pMat0, pMat1, pMat2,pVertex->Weight[0], pVertex->Weight[1],
pVertex->Weight[2], matObj.ToFloatPtr());
}
else if (pVertex->n == 4)
{
float* pMat0 = BoneMatrixPalette[pVertex->nBones[0]]->ToFloatPtr();
float* pMat1 = BoneMatrixPalette[pVertex->nBones[1]]->ToFloatPtr();
float* pMat2 = BoneMatrixPalette[pVertex->nBones[2]]->ToFloatPtr();
float* pMat3 = BoneMatrixPalette[pVertex->nBones[3]]->ToFloatPtr();
Collapse4MatSSE(pMat0, pMat1, pMat2, pMat3, pVertex->Weight[0], pVertex->Weight[1],
pVertex->Weight[2], pVertex->Weight[3], matObj.ToFloatPtr());
}
else
{
assert(0);
}
}
得到matObj后分别对等点位置、法线等做变换就可以了。这一过程仍然可以用SSE加速,但这需要顶点
的位置、法线等均是16字节对齐的。这需要较大改动,因此我们没有做。
经profile,加速后CPU蒙皮的速度提升了1倍。
主要参考
Optimized CPU-based Skinning for 3D Games
下面两篇来自id,更加变态的优化
Fast Skinning
The Skeleton Assembly Line
用SSE加速CPU蒙皮计算相关推荐
- 0.基于C++的图像处理算法实现、INTEL CPU上SSE加速、ARM CPU上NEON加速
基于C++的图像处理算法实现.INTEL CPU上SSE加速.ARM CPU上NEON加速 基于C++的图像处理算法在INTEL CPU上SSE加速实现 基于C++的图像处理算法在ARM CPU上NE ...
- 稀疏 Harris 响应的 C++ 实现及 SSE 加速
前言 Harris 响应值可以判断图像上的点是否"像一个角点",通常用于角点检测,有时候也会在特征提取/匹配/跟踪算法中用于评估特征点质量的好坏,因为角点响应值大的点往往更适合特征 ...
- 有bug!PyTorch在AMD CPU的计算机上卡死了
视学算法报道 转载自:机器之心 编辑:小舟.陈萍 AMD,No?PyTorch在AMD CPU的机器上出现死锁了. PyTorch 作为机器学习中广泛使用的开源框架,具有速度快.效率高等特点.而近年来 ...
- cpu密集型 计算密集型 io密集型 简介
CPU密集型(CPU-bound) CPU密集型也叫计算密集型,指的是系统的硬盘.内存性能相对CPU要好很多,此时,系统运作大部分的状况是CPU Loading 100%,CPU要读/写I/O(硬盘/ ...
- 使用opencv作物件识别(一) —— 积分直方图加速HOG特征计算
使用opencv作物件识别(一) -- 积分直方图加速HOG特征计算 博客分类: 图像识别.机器学习.数据挖掘 CC++C# 方向梯度直方图(Histograms of Oriented Gradi ...
- 浪潮发布全新AI品牌TensorServer ,加速推进智慧计算战略
面对AI,浪潮将可提供整体系统方案,整合提供高性能的计算平台.管理套件.框架优化和应用加速.适合线下训练的计算加速节点采用浪潮领先业界设计的浮点运算能力强.高扩展的GPU服务器,或KNM计算加速器,而 ...
- TaiChi Lang 让Python代码提速100倍!(高性能计算、图形学、仿真等领域;加速 Python 中计算密集任务程序;希望使用 Python 开发但部署到其它环境)
1.TaiChi简介 Taichi 起步于 MIT 的计算机科学与人工智能实验室(CSAIL),设计初衷是便利计算机图形学研究人员的日常工作,帮助他们快速实现适用于 GPU 的视觉计算和物理模拟算法. ...
- SSE 加速运算例子详解:乘法、加法、平方、最小值、最大值、与操作
SSE(Streaming SIMD Extensions)是英特尔在AMD的3D Now!发布一年之后,在其计算机芯片Pentium III中引入的指令集,是MMX的超集.AMD后来在Athlon ...
- 信息上传服务器加速cpu处理,英特尔发布全新第二代至强可扩展处理器携手浪潮加速新型应用发展...
原标题:英特尔发布全新第二代至强可扩展处理器携手浪潮加速新型应用发展 近日,英特尔发布了提供更高性能.更好的性价比选择的 全新第二代英特尔®至强可扩展处理器,通过增加核心数量.提高缓存或提升处理器频率 ...
最新文章
- 机器学习成为未来趋势 北美未来将保持最大市场规模
- 深度学习的40种应用
- 01.elasticsearch-security_es鉴权机制
- 四大技巧轻松搞定云容器
- [Noi2014]随机数生成器
- 前端基础-html-表格的基本标签和相关属性
- Citrix XenServer
- dba_tables 和 dba_segments 表中 blocks 的区别
- 辽宁计算机专业大学排名及分数线,辽宁一本大学排名及分数线2021
- JAVA 实现《飞机大战-III》游戏
- java pdf替换内容_java PDF批量替换关键词
- (完全理解)二重积分中的换元积分中的雅可比矩阵
- JS中的两个感叹号是什么意思
- 【MySQL】幻读是什么?如何避免幻读?
- 《Java核心卷 I》第10版阅读笔记第八章(书第九章)
- Mysql批量插入数据问题解决和优化
- Linux中常见的web中间件
- Rockchip开发系列 - 3.1.GPIO IO引脚复用问题
- 车载红外夜视「升温」
- java里怎么判断时间重合_java 时间段重合时间差
热门文章
- Java中使用JNA实现全局监听Windows键盘事件
- python画太极八卦图_太极八卦图的正确画法
- php mencache扩展,【memcache缓存专题(3)】PHP-memcache扩展的安装以及使用
- python中easygui和tkinter_python easygui Tkinter
- python读取配置文件并添加字典中_Python如何使用ConfigParser读取配置文件
- php server vscode,如何使用code-server打造自己的云端VSCode?
- c语言递归求n的阶乘之和,c语言用递归的方法实现1!+2!+3!+4!+.....+n!=?阶乘之和...
- Spark基础学习笔记19:RDD的依赖与Stage划分
- Spring Boot基础学习笔记08:Spring Boot整合Redis
- 大数据学习笔记43:Hive - JDBC编程