Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question How can we do file IO operations inside burst?

Discussion in 'Burst' started by vildauget, Apr 2, 2023.

  1. vildauget

    vildauget

    Joined:
    Mar 10, 2014
    Posts:
    120
    Is it possible to do file IO operations inside burst?

    My use case is bursted Isystems, and loading the system's setting component in OnCreate, and saving it in OnDestroy, to keep these settings persistent through runs.

    I tried using [BustDiscard] on the methods doing IO, until I realized it meant the methods don't run at all inside Burst, not as I though running without Burst-optimizations.

    In earlier struggling with this, I used StreamBinaryReader and StreamBinaryWriter from Unity.Entities.Serialization, but they seem internal protected now.
     
  2. tim_jones

    tim_jones

    Unity Technologies

    Joined:
    May 2, 2019
    Posts:
    282
    I don't know of any Unity-supported APIs for this, although in theory you could write a C++ plugin for it, and call that from Burst via an extern [DllImport] method.
     
    Deleted User likes this.
  3. Luxxuor

    Luxxuor

    Joined:
    Jul 18, 2019
    Posts:
    89
  4. MikkelSA

    MikkelSA

    Joined:
    May 9, 2023
    Posts:
    1
    I managed to write to disk using the following unsafe code. I also tested the file by reading it back with another application. The code does also read and log from the pointer, but I think that might actually be from memory and not disk.

    Code (CSharp):
    1. [Test]
    2.         public unsafe void TestWriteToDisk()
    3.         {
    4.             string path = "pathToFile";
    5.             int length = 20;
    6.             int stride = UnsafeUtility.SizeOf<float4>();
    7.             using var mmf = MemoryMappedFile.CreateFromFile(path, FileMode.OpenOrCreate,
    8.                 "FileToWrite", length * stride);
    9.             using MemoryMappedViewAccessor access = mmf.CreateViewAccessor();
    10.             byte* ptr = default;
    11.             access.SafeMemoryMappedViewHandle.AcquirePointer(ref ptr);
    12.  
    13.             if (ptr != null)
    14.             {
    15.                 var floatPtr = (float4*)ptr;
    16.                 Debug.Log($"Got pointer to file at path {path}");
    17.  
    18.                 new DiskWriteTestJob() { ptr = floatPtr }.Schedule(length, length).Complete();
    19.  
    20.                 for (int i = 0; i < length; i++)
    21.                     Debug.Log(floatPtr[i]);
    22.             }
    23.  
    24.             access.SafeMemoryMappedViewHandle.ReleasePointer();
    25.         }
    26.  
    27.         [BurstCompile]
    28.         private unsafe struct DiskWriteTestJob : IJobParallelFor
    29.         {
    30.             [NativeDisableUnsafePtrRestriction] public float4* ptr;
    31.  
    32.             public void Execute(int index)
    33.             {
    34.                 ptr[index] = index;
    35.             }
    36.         }
     
    Deleted User likes this.
  5. Deleted User

    Deleted User

    Guest

    I want to chime in. `Unity.Logging` uses internal APIs to do this. Look at the `Unity.Logging.Sinks.FileOperationsBaselib` class for how it uses `Unity.Baselib.LowLevel.Binding` APIs:

    Code (CSharp):
    1. namespace Unity.Logging.Sinks
    2. {
    3.     internal struct FileOperationsBaselib : IFileOperationsImplementation
    4.     {
    5.         public unsafe IntPtr OpenFile(ref FixedString4096Bytes absFilePath)
    6.         {
    7.             var error = new Binding.Baselib_ErrorState();
    8.             var result = Binding.Baselib_FileIO_SyncOpen(absFilePath.GetUnsafePtr(), Binding.Baselib_FileIO_OpenFlags.CreateAlways | Binding.Baselib_FileIO_OpenFlags.Write, &error).handle;
    9.  
    10.             if (error.code != Binding.Baselib_ErrorCode.Success)
    11.             {
    12.                 FixedString4096Bytes errorMsg = "Failed to open log file '";
    13.                 errorMsg.Append(absFilePath);
    14.                 errorMsg.Append((FixedString32Bytes)"' with error: ");
    15.                 errorMsg.Append((int)error.code);
    16.                 errorMsg.Append((FixedString32Bytes)" - ");
    17.                 errorMsg.Append(error.nativeErrorCode);
    18. #if UNITY_DOTSRUNTIME
    19.                     Unity.Logging.DotsRuntimePrintWrapper.ConsoleWrite(errorMsg.GetUnsafePtr(), errorMsg.Length, (byte)1);
    20. #else
    21.                 UnityEngine.Debug.LogError(errorMsg);
    22. #endif
    23.                 result = IntPtr.Zero;
    24.             }
    25.             else
    26.             {
    27. #if LOGGING_FILE_OPS_DEBUG
    28.                 UnityEngine.Debug.Log(string.Format("Opened {0} {1}", absFilePath, result.ToInt64()));
    29. #endif
    30.             }
    31.  
    32.             return result;
    33.         }
    34.  
    35.         public unsafe bool Write(IntPtr fileHandle, byte* data, ulong length, ulong* offsetPtr)
    36.         {
    37.             var handle = new Binding.Baselib_FileIO_SyncFile { handle = fileHandle };
    38.  
    39.             var error = new Binding.Baselib_ErrorState();
    40.             *offsetPtr += Binding.Baselib_FileIO_SyncWrite(handle, *offsetPtr, (IntPtr)data, length, &error);
    41.             var success = error.code == Binding.Baselib_ErrorCode.Success;
    42.  
    43. #if LOGGING_FILE_OPS_DEBUG
    44.             if (success == false)
    45.                 UnityEngine.Debug.Log(string.Format("Write {0} failed. error = {1}", fileHandle.ToInt64(), error.code));
    46. #endif
    47.             return success;
    48.         }
    49.  
    50.         public void FlushFile(IntPtr fileHandle)
    51.         {
    52.             var handle = new Binding.Baselib_FileIO_SyncFile { handle = fileHandle };
    53.  
    54.             var error = new Binding.Baselib_ErrorState();
    55.             unsafe
    56.             {
    57.                 Binding.Baselib_FileIO_SyncFlush(handle, &error);
    58.             }
    59. #if LOGGING_FILE_OPS_DEBUG
    60.             var e1 = error.code;
    61.             UnityEngine.Debug.Log(string.Format("Flush. Error = {0}", e1));
    62. #endif
    63.         }
    64.  
    65.         public void CloseFile(IntPtr fileHandle)
    66.         {
    67.             var error = new Binding.Baselib_ErrorState();
    68.             var handle = new Binding.Baselib_FileIO_SyncFile { handle = fileHandle };
    69.             unsafe
    70.             {
    71.                 Binding.Baselib_FileIO_SyncFlush(handle, &error);
    72.                 var e2 = error.code;
    73.                 Binding.Baselib_FileIO_SyncClose(handle, &error);
    74.                 var e3 = error.code;
    75. #if LOGGING_FILE_OPS_DEBUG
    76.                 UnityEngine.Debug.Log(string.Format("Closing {0}. errors = {1},{2}", fileHandle.ToInt64(), e2, e3));
    77. #endif
    78.             }
    79.         }
    80.     }
    81. }