/// <summary> /// Returns result of GPU readback of a LOD data. Do not hold onto the returned reference across frames. /// </summary> protected PerLodData GetData(Rect sampleAreaXZ, float minSpatialLength) { PerLodData lastCandidate = null; // This data will be returned if lastCandidate is null at the end PerLodData lastPossibleCandidate = null; for (int i = 0; i < _perLodData.KeyArray.Length; i++) { var lodData = _perLodData.ValueArray[i]; if (!lodData._activelyBeingRendered || lodData._resultData._time == -1f) { continue; } // Store the last lodData with valid rectangle coverage; lastPossibleCandidate = lodData; // Check that the region of interest is covered by this data var wdcRect = lodData._resultData._renderData.RectXZ; // Shrink rect by 1 texel border - this is to make finite differences fit as well float texelWidth = _perLodData.KeyArray[i]; wdcRect.x += texelWidth; wdcRect.y += texelWidth; wdcRect.width -= 2f * texelWidth; wdcRect.height -= 2f * texelWidth; if (!wdcRect.Contains(sampleAreaXZ.min) || !wdcRect.Contains(sampleAreaXZ.max)) { continue; } // This data covers our required area, so store it as a potential candidate lastCandidate = lodData; // The smallest wavelengths should repeat no more than twice across the smaller spatial length. Unless we're // in the last LOD - then this is the best we can do. float minWavelength = texelWidth * OceanRenderer.Instance.MinTexelsPerWave; if (minSpatialLength / minWavelength > 2f) { continue; } // A good match - return immediately return(lodData); } // We didnt get a perfect match, but pick the next best candidate if (lastCandidate != null) { return(lastCandidate); } else { return(lastPossibleCandidate); } }
/// <summary> /// Returns result of GPU readback of a LOD data. Do not hold onto the returned reference across frames. /// </summary> /// <param name="sampleAreaXZ">The area of interest, can be 0 area.</param> /// <param name="minSpatialLength">Min spatial length is the minimum side length that you care about. For e.g. /// if a boat has dimensions 3m x 2m, set this to 2, and then suitable wavelengths will be preferred.</param> protected PerLodData GetData(Rect sampleAreaXZ, float minSpatialLength) { PerLodData lastCandidate = null; foreach (var gridSize_lodData in _perLodData) { if (!gridSize_lodData.Value._activelyBeingRendered || gridSize_lodData.Value._resultData._time == -1f) { continue; } // Check that the region of interest is covered by this data var wdcRect = gridSize_lodData.Value._resultData._renderData.RectXZ; // Shrink rect by 1 texel border - this is to make finite differences fit as well float texelWidth = gridSize_lodData.Key; wdcRect.x += texelWidth; wdcRect.y += texelWidth; wdcRect.width -= 2f * texelWidth; wdcRect.height -= 2f * texelWidth; if (!wdcRect.Contains(sampleAreaXZ.min) || !wdcRect.Contains(sampleAreaXZ.max)) { continue; } // This data covers our required area, so store it as a potential candidate lastCandidate = gridSize_lodData.Value; // The smallest wavelengths should repeat no more than twice across the smaller spatial length. Unless we're // in the last LOD - then this is the best we can do. float minWavelength = texelWidth * OceanRenderer.Instance._minTexelsPerWave; if (minSpatialLength / minWavelength > 2f) { continue; } // A good match - return immediately return(gridSize_lodData.Value); } // We didnt get a perfect match, but pick the next best candidate return(lastCandidate); }
public bool PrewarmForSamplingArea(Rect areaXZ, float minSpatialLength) { return((_areaData = GetData(areaXZ, minSpatialLength)) != null); }
void ProcessArrivedRequests(PerLodData lodData) { var requests = lodData._requests; if (!lodData._activelyBeingRendered) { // Dump all requests :/. No point processing these, and we have marked the data as being invalid and don't // want any new data coming in and stomping the valid flag. requests.Clear(); return; } // Physics stuff may call update from FixedUpdate() - therefore check if this component was already // updated this frame. if (lodData._lastUpdateFrame == Time.frameCount) { return; } lodData._lastUpdateFrame = Time.frameCount; // remove any failed readback requests for (int i = 0; i < MAX_REQUESTS && requests.Count > 0; i++) { var request = requests.Peek(); if (request._request.hasError) { requests.Dequeue(); } else { break; } } // process current request queue if (requests.Count > 0) { var request = requests.Peek(); if (request._request.done) { requests.Dequeue(); // Eat up any more completed requests to squeeze out latency wherever possible ReadbackRequest nextRequest; while (requests.Count > 0 && (nextRequest = requests.Peek())._request.done) { // Has error will be true if data already destroyed and is therefore unusable if (!nextRequest._request.hasError) { request = nextRequest; } requests.Dequeue(); } UnityEngine.Profiling.Profiler.BeginSample("Copy out readback data"); var result = lodData._resultData; var resultLast = lodData._resultDataPrevFrame; // copy result into resultLast resultLast._renderData = result._renderData; resultLast._time = result._time; Swap(ref result._data, ref resultLast._data); // copy new data into result var data = request._request.GetData <ushort>(); data.CopyTo(result._data); result._renderData = request._renderData; result._time = request._time; UnityEngine.Profiling.Profiler.EndSample(); } } }
void ProcessRequestsInternal(bool runningFromUpdate) { var ocean = OceanRenderer.Instance; int lodCount = ocean.CurrentLodCount; // When viewer changes altitude, lods will start/stop updating. Mark ones that are/arent being rendered! for (int i = 0; i < _perLodData.KeyArray.Length; i++) { int lastUsableIndex = CanUseLastTwoLODs ? (lodCount - 1) : (lodCount - 3); _perLodData.ValueArray[i]._activelyBeingRendered = _perLodData.KeyArray[i] >= ocean._lodTransform._renderData[0]._texelWidth && _perLodData.KeyArray[i] <= ocean._lodTransform._renderData[lastUsableIndex]._texelWidth; if (!_perLodData.ValueArray[i]._activelyBeingRendered) { // It would be cleaner to destroy these. However they contain a NativeArray with a non-negligible amount of data // which we don't want to alloc and dealloc willy nilly, so just mark as invalid by setting time to -1. _perLodData.ValueArray[i]._resultData._time = -1f; _perLodData.ValueArray[i]._resultDataPrevFrame._time = -1f; } } var lt = ocean._lodTransform; for (int lodIndex = 0; lodIndex < ocean.CurrentLodCount; lodIndex++) { float lodTexelWidth = lt._renderData[lodIndex]._texelWidth; // Don't add uninitialised data if (lodTexelWidth == 0f) { continue; } if (lodTexelWidth >= _minGridSize && (lodTexelWidth <= _maxGridSize || _maxGridSize == 0f)) { var tex = _lodComponent.DataTexture; if (tex == null) { continue; } if (!_perLodData.ContainsKey(lodTexelWidth)) { var resultData = new PerLodData(); resultData._resultData = new ReadbackData(); resultData._resultDataPrevFrame = new ReadbackData(); // create native arrays Debug.Assert(_textureFormat != TexFormat.NotSet, "ReadbackLodData: Texture format must be set.", this); var num = ((int)_textureFormat) * tex.width * tex.height; if (!resultData._resultData._data.IsCreated || resultData._resultData._data.Length != num) { resultData._resultData._data = new NativeArray <ushort>(num, Allocator.Persistent); resultData._resultDataPrevFrame._data = new NativeArray <ushort>(num, Allocator.Persistent); } _perLodData.Add(lodTexelWidth, resultData); } var lodData = _perLodData[lodTexelWidth]; if (lodData._activelyBeingRendered) { // Only enqueue new requests at beginning of update turns out to be a good time to sample the textures to // ensure everything in the frame is done. if (runningFromUpdate) { EnqueueReadbackRequest(tex, lodIndex, lt._renderData[lodIndex], _prevFrameTime); } ProcessArrivedRequests(lodData); } } } }
void ProcessRequestsInternal(bool runningFromUpdate) { // When viewer changes altitude, lods will start/stop updating. Mark ones that are/arent being rendered! foreach (var gridSize_lodData in _perLodData) { bool CAN_USE_LAST_LOD = false; int lastUsableIndex = CAN_USE_LAST_LOD ? (_lodComponents.Length - 1) : (_lodComponents.Length - 2); gridSize_lodData.Value._activelyBeingRendered = gridSize_lodData.Key >= _lodComponents[0].LodTransform._renderData._texelWidth && gridSize_lodData.Key <= _lodComponents[lastUsableIndex].LodTransform._renderData._texelWidth; if (!gridSize_lodData.Value._activelyBeingRendered) { // It would be cleaner to destroy these. However they contain a NativeArray with a non-negligible amount of data // which we don't want to alloc and dealloc willy nilly, so just mark as invalid by setting time to -1. gridSize_lodData.Value._resultData._time = -1f; gridSize_lodData.Value._resultDataPrevFrame._time = -1f; } } foreach (var data in _lodComponents) { var lt = data.LodTransform; if (lt._renderData._texelWidth >= _minGridSize && (lt._renderData._texelWidth <= _maxGridSize || _maxGridSize == 0f)) { var cam = lt.GetComponent <Camera>(); if (!_perLodData.ContainsKey(lt._renderData._texelWidth)) { var resultData = new PerLodData(); resultData._resultData = new ReadbackData(); resultData._resultDataPrevFrame = new ReadbackData(); // create native arrays Debug.Assert(_textureFormat != TexFormat.NotSet, "ReadbackLodData: Texture format must be set.", this); var num = ((int)_textureFormat) * cam.targetTexture.width * cam.targetTexture.height; if (!resultData._resultData._data.IsCreated || resultData._resultData._data.Length != num) { resultData._resultData._data = new NativeArray <ushort>(num, Allocator.Persistent); resultData._resultDataPrevFrame._data = new NativeArray <ushort>(num, Allocator.Persistent); } _perLodData.Add(lt._renderData._texelWidth, resultData); } var lodData = _perLodData[lt._renderData._texelWidth]; if (lodData._activelyBeingRendered) { // Only enqueue new requests at beginning of update turns out to be a good time to sample the textures to // ensure everything in the frame is done. if (runningFromUpdate) { EnqueueReadbackRequest(cam.targetTexture, lt._renderData, _prevFrameTime); } ProcessArrivedRequests(lodData); } } } }