zoukankan      html  css  js  c++  java
  • A Simple Wrapper of DirectX Math

    A Simple Wrapper of DirectX Math

    关于DirectX Math
    DirectX Math最初叫做XNA Math,是一个跨平台的C++数学库,全SIMD指令优化,目前的版本是3.03,支持x86,x64和arm平台,用于代替DX 9/10中的数学库。

    为什么使用DirectX Math
    如果你有足够的时间,精力和知识,并且坚定的认为自己写的数学库才是最好,那么可以忽略此文:)。根据gamasutra技术控做的细介,基本上很难再比DXM的实现好了,并且它是完全跨平台的,只包含.h和.inl文件,就算是OpenGL程序也可以用,最后,DXM非常全面,实现了大部分常见的3D计算,甚至还包括了一个简单的碰撞检测库。

     正确使用DXM
            首先,用 #define _XM_NO_INTRINSICS_ 宏可以取消SIMD加速,生成普通指令,除非希望兼容非常,非常老的CPU,否则不应该使用该宏。虽然DXM对SIMD指令做了封装,你还是需要了解一点相关知识(请仔细阅读DXM文档)。DXM定义了大量常见的数据类型,但只为XMVECTOR 和XMMATRIX定义了各种数学操作。DXM所有的计算都基于XMVECTOR 和XMMATRIX。对x86平台,XMVECTOR的定义如下:
    typedef __m128 XMVECTOR;

             而XMMATRIX则是一组(4个) XMVECTOR。由于__m128在内存中需要严格的16 byte对齐,因此并不建议直接在程序中使用这两种类型保存数据,比如

    Class Camera
    {
          XMVECTOR position;
          XMMATRIX viewMatrix;
    }

             上面的代码基本上不会成功运行,32位下new并不保证16byte对齐,而且由于类成员布局的关系,其中的XMVECTOR也不一定是16byte对齐,访问以上数据,程序可能直接崩溃。因此,DXM定义了大量用来储存数据的类型,比如XMFLOAT4,XMFLOAT3,XMFLOAT4X4等:

    Class Camera
    {
        XMFLOAT3 position;
        XMFLOAT4X4 viewMatrix;
    }

             这些类型大部分只是float数组而已,因此不用考虑对齐问题,在需要计算时,再把他们转换为于XMVECTOR 或者XMMATRIX,比如:

    XMFLOAT3 position;
    XMVECTOR myVector = XMLoadFloat3(position)   //load to mmx register
    //perform calculate with SIMD …..
    //
    XMStoreFloat4(&position,myVector);           //save to memory

             显然,这种load/store模式确实很麻烦,简单计算两个向量的和也需要编写4,5行代码,于是DX开发组的程序写了一个名为SimpleMath的简单wrapper包含在DirectX Tool Kit中,为普通类型添加计算功能。然而,SimpleMath只是简单的包装了load,store而已:

    struct Vector2 : public XMFLOAT2
    {
        //......other member function
        float Dot( const Vector2& V ) const
        float LengthSquared() const;
        //…..other member function
    }
    
    
    inline float Vector2::Dot( const Vector2& V ) const
    {
        using namespace DirectX;
        XMVECTOR v1 = XMLoadFloat2( this );
        XMVECTOR v2 = XMLoadFloat2( &V );
        XMVECTOR X = XMVector2Dot( v1, v2 );
        return XMVectorGetX( X );
    }
     

             这样的实现对于Float4来说也许有意义,但对于Float2来说就太笨重了,把2个Float2复制到mmx寄存器相加以后再保存到普通内存位置也许比直接用非SIMD指令相加还慢!此外对于SIMD计算来说,应该尽量减少load和unload,尽量把数据保存在寄存器中,比如在计算前把数据load为XMVECTOR,执行完一系列计算,最后再保存到普通位置,而不是每执行一次计算,就load/store一次

    case 1:
    Load to XMVECTOR
    perform calc 1
    perform calc 1
    ..
    Perform calc n
    Store to Float4
    
     
    case 2
    Load to XMVECTOR
    Perform calc 1
    Store to Float4
    Load to XMVECTOR
    Perform calc 2
    Store to Float4
    …

    两种不同写法,如果编译器没有特别优化的情况下,效率相差会很明显。因此,好的数学库还要有正确的使用方法,才会得到最好的性能。

    FlameMath
             FlameMath保留了SimpleMath的接口,但改进了一部分性能问题,替换了部分函数的实现,对于简单类型和轻量级计算,直接用普通指令,对于matrix等复杂计算,再使用SIMD。

    inline float Float2::Dot( const Float2& V ) const
    {
       return (x * V.x + y * V.y);
    }

           但是,FlameMath仍然不能避免不同计算间反复load/store的问题。因此,最佳方案应该是使用Float3,Float4等类型保存数据,对于轻量级简单计算,直接用FloatX的成员函数进行,对于需要连续进行的复杂计算,则应该把数据load为XMVECTOR等类型,直接用DXM函数计算,最后再store到FloatX中。FloatMath中的FloatX对应DXM和SimpleMath中的标量数据类型,Vector4和Matrix4x4则相当于XMVECTOR 和XMMATRIX。所有DXM中的函数以SimdMath类静态函数的方式提供。

    Simpe Usage:
    Float2 v1;
    Float2 v2;
    Float2 result = v1 + v2;
    //done
    
     
    Advance usage
    Float2 f1;
    Float2 f2;
    Vector4 v1 = SimdMath.LoadFloat2(f1);
    Vector4 v2 = SimdMath.LoadFloat2(f2);
    Vector4 v3 = SimdMath. Vector2Normalize (v1);
    Vector4 v4 = SimdMath. Vector2Normalize (v2);
    Vector4 v5 = SimdMath. Vector2AngleBetweenNormals(v3,v4);
    Float result;
    SimdMath. StoreFloat(&result,v5);
    //done

    ps:FlameMath所有代码都未经测试,使用时只需要包含FlameMath.h即可:)
    https://files.cnblogs.com/clayman/FlameMath.rar

  • 相关阅读:
    python3 -- 堆(heapq)
    二叉堆的实现(最大堆)
    解决layui layer.load() Ajax提交时,只在结束闪一下
    两个for循环嵌套,写成一个列表生成式
    time模块,计算当前时间和时间差
    私有化&property
    面向对象魔术方法之__str__()方法
    面向对象魔术方法-__del__()方法
    面向对象之魔术方法__init__()和__new__()
    Django的自定义标签
  • 原文地址:https://www.cnblogs.com/clayman/p/3053196.html
Copyright © 2011-2022 走看看