Game.Simulation.SurfaceDataReader
Assembly: Assembly-CSharp (typical game assembly; adjust if different)
Namespace: Game.Simulation
Type: internal class
Base: System.Object
Summary:
SurfaceDataReader is a helper used to read water surface data back from a GPU RenderTexture into CPU memory for use by simulation systems. It uses Unity's AsyncGPUReadback into a NativeArray and supports splitting the readback into tiles (readback distribution) to spread GPU/CPU work across frames. The class is Burst-aware: the actual copy logic is Burst-compiled for performance and a generated DirectCall helper is present to obtain a function pointer when Burst is enabled. It stores results as NativeArray
Fields
-
private int m_ReadbackDistribution
Controls how the source texture is subdivided for tiled readbacks. Default in code is 8 (8x8 tiles). -
private int m_ReadbackIndex
Current tile index used to select which tile to read back this frame. Incremented modulo (distribution * distribution) by ExecuteReadBack. -
private NativeArray<float4> m_CPUTemp
Temporary NativeArray used as the target for AsyncGPUReadback.RequestIntoNativeArray. Holds raw RGBA float data read from the GPU. -
private NativeArray<SurfaceWater> m_CPU
Persistent NativeArray holding the decoded SurfaceWater values for the full texture. Accessible via WaterSurfaceCPUArray. -
private JobHandle m_Writers
JobHandle that represents work (writers) that must be completed before initiating a readback. The class waits on this handle before requesting the GPU readback. -
private JobHandle m_Readers
JobHandle for reader jobs that process CPU data after readback. The Burst-managed copy method completes this handle before copying data into m_CPU. -
private int2 m_TexSize
Dimensions of the source RenderTexture (width, height) captured at construction. -
private int m_mapSize
Map world size used when constructing WaterSurfaceData (scale calculations). -
private AsyncGPUReadbackRequest m_AsyncReadback
Active AsyncGPUReadbackRequest representing the in-flight GPU->CPU transfer. -
private bool m_PendingReadback
Internal flag indicating whether a readback is currently pending. Note: the class also defines a public auto-property named PendingReadback (see Properties) which is distinct from this private field — the internal logic relies on this private field. -
private RenderTexture m_sourceTexture
Source RenderTexture to read water surface information from.
Properties
-
public JobHandle JobWriters => m_Writers
Read-only accessor exposing the current writers JobHandle. Use this as a dependency when scheduling readers that depend on writers being finished. -
public JobHandle JobReaders { get; set; }
Public getter/setter for the reader JobHandle (wraps m_Readers internally). Allows external code to set/observe dependencies for reader jobs. -
public bool PendingReadback { get; set; }
Auto-implemented public property. IMPORTANT: this property is a separate auto property and is not used by the internal logic which uses the private m_PendingReadback field. Do not rely on this public property to reflect internal pending state unless you explicitly synchronize it. -
public NativeArray<SurfaceWater> WaterSurfaceCPUArray => m_CPU
Expose the underlying NativeArraythat contains the full-surface data after readback and copy.
Constructors
public SurfaceDataReader(RenderTexture sourceTexture, int mapSize)
Creates a SurfaceDataReader for a given source texture and map size. Captures texture dimensions into m_TexSize, allocates m_CPU with size width*height and allocates m_CPUTemp sized to the readback tile (computed via GetReadbackBounds). mapSize is stored and later used to construct WaterSurfaceData (scale/offset).
Methods
-
public void LoadData(NativeArray<float4> buffer)
Copy helper that converts an array of float4 (RGBA floats) to the internal SurfaceWater format. For each element, it sets m_Depth = max(float.x, 0f), m_Polluted = float.w, m_Velocity = float.yz. -
public void ExecuteReadBack()
If no readback is currently pending (internal m_PendingReadback), completes writer jobs, increments the readback tile index, computes the tile bounds and requests an AsyncGPUReadback into m_CPUTemp for that tile. Uses GraphicsFormat.R32G32B32A32_SFloat and supplies CopyWaterValues as the callback. Sets the internal pending flag to true. Must be called on the main thread (AsyncGPUReadback.RequestIntoNativeArray requirement). -
private void CopyWaterValues(AsyncGPUReadbackRequest request)
Callback invoked by AsyncGPUReadback once the transfer completes. Calls the static Burst-aware CopyWaterValues to perform the copy from cpuTemp into cpu. -
private static void CopyWaterValues(ref AsyncGPUReadbackRequest asyncReadback, ref NativeArray<SurfaceWater> cpu, ref NativeArray<float4> cpuTemp, ref JobHandle readers, ref int2 texSize, ref bool pendingReadback, int readbackDistribution, int readbackIndex)
BurstCompile wrapper that invokes the generated DirectCall helper (CopyWaterValues_...BurstDirectCall) to call either a function pointer (if Burst enabled) or the managed copy implementation. -
public WaterSurfaceData GetSurfaceData(out JobHandle deps)
Returns a WaterSurfaceData value that wraps the m_CPU array with resolution, scale and offset computed from m_TexSize and m_mapSize. Also returns the current writers JobHandle in deps; callers should ensure this dependency is respected before consuming the data. -
public SurfaceWater GetSurface(int2 cell)
Returns the SurfaceWater value at the provided cell coordinate using index calculation: m_CPU[cell.x + 1 + m_TexSize.x * cell.y]. Note the +1 offset in x — this class's indexing accounts for an extra column/offset in the stored array. -
private void GetReadbackBounds(out int2 pos, out int2 size)
Instance helper that forwards to the static GetReadbackBounds using m_TexSize, m_ReadbackDistribution and m_ReadbackIndex. -
private static void GetReadbackBounds(int2 texSize, int readbackDistribution, int readbackIndex, out int2 pos, out int2 size)
Compute the tile size and position for a given texSize, distribution (tiles per axis) and tile index. size = texSize / distribution; pos.x = (readbackIndex % distribution) * size.x; pos.y = (readbackIndex / distribution) * size.y. -
public void Dispose()
Waits for any in-flight AsyncGPUReadback (m_AsyncReadback) to complete, disposes m_CPU and m_CPUTemp if they are created, and completes m_Readers. Use this to clean up NativeArrays when the reader is no longer needed. -
public static void CopyWaterValues$BurstManaged(ref AsyncGPUReadbackRequest asyncReadback, ref NativeArray<SurfaceWater> cpu, ref NativeArray<float4> cpuTemp, ref JobHandle readers, ref int2 texSize, ref bool pendingReadback, int readbackDistribution, int readbackIndex)
Managed copy implementation used when Burst function pointer path is not used. It checks for asyncReadback.hasError and cpu.IsCreated, completes readers, computes the tile bounds, and iterates over the tile pixels copying float4 -> SurfaceWater (m_Depth = float.x, m_Polluted = float.w, m_Velocity = float.yz) writing into the proper indices inside cpu. On success it sets pendingReadback = false; on error it logs a warning to UnityEngine.Debug. -
(Nested generated helper)
internal static class CopyWaterValues_00005ABA$BurstDirectCall
Generated Burst direct-call helper used to obtain a function pointer for the Burst-compilation of the copy routine. It is infrastructure for high-performance Burst invocation and calls CopyWaterValues$BurstManaged when Burst isn't providing the pointer.
Usage Example
// Example usage on the main thread / game loop:
RenderTexture sourceTexture = /* your water surface RT */;
int mapSize = /* world map size */;
var reader = new SurfaceDataReader(sourceTexture, mapSize);
// When your simulation has produced writer jobs, ensure reader.m_Writers (internal) is set appropriately.
// Typically the simulation layer that owns writer jobs would set reader.JobWriters/JobReaders or
// otherwise synchronize via JobHandles prior to calling ExecuteReadBack.
// Request a readback for the next available tile (must be called on main thread)
reader.ExecuteReadBack();
// Later (when AsyncGPUReadback completes and the callback ran) you can retrieve the data:
JobHandle deps;
var waterSurfaceData = reader.GetSurfaceData(out deps);
// Ensure any returned deps are accounted for before reading data on other threads.
// Example: deps.Complete(); then read waterSurfaceData.surfaceArray (reader.WaterSurfaceCPUArray)
// When finished with the reader:
reader.Dispose();
Notes / Tips: - AsyncGPUReadback.Request* must be called from the main thread. The actual copy into m_CPU is done in the callback and completed via the Burst-managed implementation. - The internal m_PendingReadback flag is used by the class to avoid re-issuing readbacks; the public PendingReadback property is a separate auto-property and does not reflect the internal pending state — treat that as an implementation quirk. - The class expects the source texture format to match GraphicsFormat.R32G32B32A32_SFloat for correct decoding. - Dispose waits for the AsyncGPUReadback request to finish and cleans up NativeArrays — always call Dispose to avoid leaking NativeArray memory. - The class spreads readbacks across tiles using m_ReadbackDistribution (default 8) to reduce per-frame GPU bandwidth/latency spikes.