/// Side effects: /// - May cause HandleIntersectionWithXxx to be called /// /// Returns true if and only if any "intersection actions" were carried out (ie, /// if at least one HandleIntersectionWithXxx call returned true) /// private bool UpdateGpuIntersection(Vector3 vDetectionCenter_GS, float size_GS) { // Possible states of m_GpuFutureResult and m_GpuFutureResultlist: // // m_GpuFutureResult = null No outstanding GPU request. m_GpuFutureResultList // is unused, might contain garbage, and is ready to pass // to a new GpuFutureResult // m_GpuFutureResult != null // IsReady = false Request is running; will fill in m_GpuFutureResultList // IsReady = true Request is done; m_GpuFutureResultList is filled in // // Possible states of m_GpuOldResultList and m_GpuConsumedResults: // // m_GpuOldResultList Always not-null, but may have been fully-processed // m_GpuConsumedResults Indices >= this have yet to be consumed // // m_GpuFutureResult may be pending for multiple frames. // m_GpuOldResultList may be processed over multiple frames. if (m_GpuFutureResult == null) { // Note that m_GpuFutureResultList will be cleared and populated at some future point // after this call. int intersectionLayer = (1 << m_CurrentCanvas.gameObject.layer) | AdditionalGpuIntersectionLayerMasks(); // The new request will only be null when the intersector is disabled. // Given the logic in this function, this should be fine without any special handling. // TODO: use a pool of List<BatchResult> instead of being so stateful m_GpuFutureResult = App.Instance.GpuIntersector .RequestBatchIntersections(vDetectionCenter_GS, size_GS, m_GpuFutureResultList, 255, intersectionLayer); } else if (m_GpuFutureResult.IsReady) { // We could go use GpuResultList, but as we're swapping the buffers here, it feels better // to be explicit about which buffers we're swapping. // TODO: use m_GpuFutureResult.GetResults() instead List <GpuIntersector.BatchResult> results = m_GpuFutureResultList; m_GpuFutureResultList = m_GpuOldResultList; // Note that this throws away any results that have yet to be consumed. m_GpuOldResultList = results; m_GpuConsumedResults = 0; // We could immediately submit another request, however we have likely already hit our budget // when there are intersections, so it should generally feel better to allow this to be a // three frame cycle. m_GpuFutureResult = null; } if (m_GpuConsumedResults < m_GpuOldResultList.Count) { int hitCount = 0; for (int i = m_GpuConsumedResults; i < m_GpuOldResultList.Count && !m_TimesUp; i++) { // Prefer to find widgets, although the results struct should never have both. if (m_GpuOldResultList[i].widget) { if (HandleIntersectionWithWidget(m_GpuOldResultList[i].widget)) { hitCount++; } } else { BatchSubset subset = m_GpuOldResultList[i].subset; if (subset.m_ParentBatch == null) { // The stroke was deleted between creating the result and processing the result. This // could happen due to the inherent latency in GPU intersection, although in practice, // this should be very rare. But this will also happen if the selection tool intersects // more than a single stroke in the same group. In this case, the following happens: // // * HandleIntersectionWithBatchedStroke() is called once with one of the strokes in // the group. // * All the strokes in that group are moved to the selection canvas and thus, a // different subset. // * HandleIntersectionWithBatchedStroke() is called once with another stroke in the // same group. continue; } if (HandleIntersectionWithBatchedStroke(subset)) { hitCount++; } } // Always process at least 1 hit. This number can be tuned to taste, but in initial // tests, it kept the deletion time under the frame budget while still feeling responsive. if (hitCount > 0) { m_TimesUp = m_DetectionStopwatch.ElapsedTicks > m_TimeSliceInTicks; } } m_GpuConsumedResults += hitCount; if (hitCount > 0) { return(true); } } return(false); }
// TODO: We're disabling "duplicate on hover" until we've had a chance to UER test // the basic functionality. Once that's been tested, we can experiment with what we do // when the selection tool hovers over an existing selection. //override public bool HasHoverInteractions() { return true; } // //override public void AssignHoverControllerMaterials(InputManager.ControllerName controller) { // // If we're intersecting with the brush hand, allow additional actions. // if (controller == InputManager.ControllerName.Brush) { // InputManager.GetControllerGeometry(controller).ShowSelectionOptions(); // } //} override public float GetActivationScore( Vector3 vControllerPos_GS, InputManager.ControllerName name) { if (!PointInCollider(vControllerPos_GS)) { return(-1); } // If the data we've got is old, delete it all. if ((Time.frameCount - m_IntersectionFrame) > 3) { m_CurrentIntersectionController = null; m_NextIntersectionController = null; m_IntersectionFuture = null; } // If we have an intersection in the pipe, set up the next one if it is of a different type. // This is so that if we're looking for both Wand and Brush, it will toggle between them. // If the results are ready, then store them off and clear the current intersection. if (m_CurrentIntersectionController.HasValue) { if (m_CurrentIntersectionController.Value != name) { m_NextIntersectionController = name; } if (m_IntersectionFuture.IsReady) { m_LastIntersectionResult[m_CurrentIntersectionController.Value] = m_IntersectionFuture.HasAnyIntersections() ? 1 : -1; m_CurrentIntersectionController = null; m_IntersectionFuture = null; } } // If we don't have a current intersection in the pipe, grab the next one if there is one, // or just start off the intersection we have been asked for. if (!m_CurrentIntersectionController.HasValue) { if (!m_NextIntersectionController.HasValue) { m_NextIntersectionController = name; } m_CurrentIntersectionController = m_NextIntersectionController.Value; m_NextIntersectionController = null; Debug.Assert(m_CurrentIntersectionController.Value == InputManager.ControllerName.Wand || m_CurrentIntersectionController.Value == InputManager.ControllerName.Brush); // Because we may be requesting an intersection on another controller's behalf, don't use // the passed position, but instead the position respective of the enum. Vector3 pos = (m_CurrentIntersectionController.Value == InputManager.ControllerName.Brush) ? InputManager.Brush.Geometry.ToolAttachPoint.position : InputManager.Wand.Geometry.ToolAttachPoint.position; m_IntersectionFuture = App.Instance.GpuIntersector.RequestBatchIntersection( pos, m_CollisionRadius, (1 << m_SelectionCanvas.gameObject.layer)); m_IntersectionFrame = Time.frameCount; } float result = -1; m_LastIntersectionResult.TryGetValue(name, out result); return(result); }
protected void ClearGpuFutureLists() { m_GpuFutureResultList.Clear(); m_GpuOldResultList.Clear(); m_GpuFutureResult = null; }