float4 的 NativeArray 如何将其中一个 xyzw 设置为循环中的值?

NativeArray of float4 how to set one of the xyzw to a value in loop?

当循环遍历 float4[] 的 NativeArray 并想要将每个 float4(比方说 y)的 'fields' 之一设置为一个值时,似乎没有立即可行,因为它是一个临时值。

这个:

  NativeArray<float4> naFloat4s ;
  void Start ( ) {
    naFloat4s = new NativeArray<float4> ( 96000, Allocator.Persistent ) ;
  }
  void MakeNoise ( ) {
    for ( int i = 0 ; i < naFloat4s.Length ; i++ ) {
      naFloat4s[i].y = Random.Range ( - 1f, 1f ) ;
    }
  }

生成关于它是结构的临时值的投诉,无法设置。

如何以最有效的方式克服这个问题,以便不产生垃圾,并且 NativeArray 和 Burst/Jobs 可以尽最大努力尽快完成数万个设置的这个过程?

注意:这里使用的random只是一个例子。假设那里还有其他东西会产生更有趣的东西。

另请注意,执行此操作时,其他值(在本例中为 x、z 和 w)必须保持不变。它们仍然有用,因为它们是。需要的是在整个数组中仅更改 float4 中的一个值。

编辑:根据 Hugo 爵士的评论,固定浮动范围。

回应 Hugo 爵士关于 float4 中的 float 指针的评论:

通过这样做,我得到了指向单个浮动的指针:

      void LoopDemoWithPointerToFloatOfFloat4_NativeArray() {
        int    samples = 2000;
        int    size_T = UnsafeUtility.SizeOf<float4> ( ) ;
        int    size_F = UnsafeUtility.SizeOf<float> ( ) ;
        int    mStepr = size_F * 1 ; // 1 is y's index value in the float4 struct
        IntPtr dstPtr = ( IntPtr )NativeArrayUnsafeUtility
                         .GetUnsafeBufferPointerWithoutChecks ( naFloat4s ) ;
        
        for ( int i = 0 ; i < samples ; i++ ) {
          unsafe {
            float* yOffset =  (float*) (dstPtr + i * size_T + mStepr);
            *yOffset = (float)i ;
          }
        }
      }

还没来得及看速度,好像很快。

需要创建一个装备来测试各种秒表....

更新的用法示例:

var p = (float4*)noizADSR.GetUnsafePtr (  );
float stepDekay = 1f / dekayLength ;
ptr = (float*)(p + attakFinish); ptr += nID;
j = overlapping;
for ( int i = attakFinish ; i < noizeLength ; i++, j++, ptr += 4 ) {
*ptr = dekayCrv.Evaluate( j * stepDekay) ;
}

像往常一样,对于任何 struct(= 值)类型,如果它在数组中,您只能执行

var value = naFloat4s[i]; 
value.y = Random.Range(-1, 1); 
naFloat4s[i] = value;

The indexer ([i]) 是一个 属性 并且 return 或采用完整的 float4 结构值。

所以如果你(能够)做到

naFloat4s[i].y = something;

基本上 return 一个 float4 并改变 returned float4y零件。但这不会以任何方式改变存储在数组中的值。


更新

指向指针的东西:

我刚做了个小测试,确实如果你真的愿意去unsafe你可以使用指针。以下测试

private unsafe void Test()
{
    var testIndexer = new NativeArray<float4>(amount, Allocator.Persistent);
    var testPointer = new NativeArray<float4>(amount, Allocator.Persistent);

    Profiler.BeginSample("TEST Indexed");

    for (var i = 0; i < testIndexer.Length; i++)
    {
        var value = testIndexer[i];
        value.y = i;
        testIndexer[i] = value;
    }

    Profiler.EndSample();

    Profiler.BeginSample("TEST Pointer");
    var pointer = (float4*)testPointer.GetUnsafePtr();

    for (var i = 0; i < testPointer.Length; i++)
    {
        (pointer + i)->y = i;
        // or also (seems equally fast - 10ms slower on 10.000 elements)
        // pointer[i].y = i;
        // or also (seems equally fast)
        // (*(pointer + i)).y = i;
    }

    Profiler.EndSample();

    for (var i = 0; i < amount; i++)
    {
        Debug.Log($"indexed: {testIndexer[i].y}, pointer: {testPointer[i].y}");
    }

    Debug.Assert(testIndexer.ToArray().SequenceEqual(testPointer.ToArray()));

    testIndexer.Dispose();
    testPointer.Dispose();
}

已经至少快了三倍,这很可能是因为使用索引器您有更多操作(读取值、存储值、写入值)。

一些基准值:

  • amount = 100

    • 索引:0.77 毫秒
    • 指针:0.25 毫秒
  • amount = 1000

    • 索引:3.40 毫秒
    • 指针:0.67 毫秒
  • amount = 10000

    • 索引:37.39 毫秒
    • 指针:7.70 毫秒

我不知道是否有可能直接写入单个浮点指针,但它甚至可能更快。