Beispiel #1
0
        override protected bool HandleIntersectionWithBatchedStroke(BatchSubset rGroup)
        {
            if (!rGroup.m_Active)
            {
                // Subset has already been deleted.
                // Collision detection is async and has latency, so theoretically we should expect this case.
                // However, in practice it's currently not possible; so flag it as unexpected.
                Debug.LogWarningFormat(
                    "{0}: Unexpected: deleting already-deleted stroke @ {1}",
                    rGroup.m_ParentBatch.ParentPool.Name, Time.frameCount);
                return(false);
            }

            if (altSelect)
            {
                if (m_BatchFilter == null && rGroup.m_ParentBatch != null)
                {
                    m_BatchFilter = rGroup.m_ParentBatch;
                }

                if (!ReferenceEquals(m_BatchFilter, rGroup.m_ParentBatch))
                {
                    return(true);
                }
            }
            else
            {
                m_BatchFilter = null;
            }

            SketchMemoryScript.m_Instance.MemorizeDeleteSelection(rGroup.m_Stroke);
            PlayModifyStrokeSound();
            return(true);
        }
        public void MemorizeBatchedBrushStroke(
            BatchSubset subset, Color rColor, Guid brushGuid,
            float fBrushSize, float brushScale,
            List <PointerManager.ControlPoint> rControlPoints, StrokeFlags strokeFlags,
            StencilWidget stencil, float lineLength, int seed)
        {
            // NOTE: PointerScript calls ClearRedo() in batch case

            Stroke rNewStroke = new Stroke();

            rNewStroke.m_Type                = Stroke.Type.BatchedBrushStroke;
            rNewStroke.m_BatchSubset         = subset;
            rNewStroke.m_ControlPoints       = rControlPoints.ToArray();
            rNewStroke.m_ControlPointsToDrop = new bool[rNewStroke.m_ControlPoints.Length];
            rNewStroke.m_Color               = rColor;
            rNewStroke.m_BrushGuid           = brushGuid;
            rNewStroke.m_BrushSize           = fBrushSize;
            rNewStroke.m_BrushScale          = brushScale;
            rNewStroke.m_Flags               = strokeFlags;
            rNewStroke.m_Seed                = seed;
            subset.m_Stroke = rNewStroke;

            SketchMemoryScript.m_Instance.RecordCommand(
                new BrushStrokeCommand(rNewStroke, stencil, lineLength));

            if (m_SanityCheckStrokes)
            {
                SanityCheckGeometryGeneration(rNewStroke);
                //SanityCheckVersusReplacementBrush(rNewObject);
            }
            MemoryListAdd(rNewStroke);

            TiltMeterScript.m_Instance.AdjustMeter(rNewStroke, up: true);
        }
Beispiel #3
0
        public void DisableSubset(BatchSubset subset)
        {
            SelfCheck();
            if (!subset.m_Active)
            {
                return;
            }

            Debug.Assert(subset.m_Active);
            subset.m_Active = false;
            if (subset.m_TriangleBackup == null)
            {
                subset.m_TriangleBackup = new ushort[subset.m_nTriIndex];
            }

            m_Geometry.EnsureGeometryResident();
            var aTris = m_Geometry.m_Tris.GetBackingArray();
            int t0    = subset.m_iTriIndex;
            int t1    = subset.m_iTriIndex + subset.m_nTriIndex;

            // TODO: Possibly could optimize this in C++ for 4.4% of time in selection.
            for (int t = t0; t < t1; ++t)
            {
                subset.m_TriangleBackup[t - t0] = (ushort)aTris[t];
                aTris[t] = 0;
            }
            m_bTopologyDirty = true;
            SelfCheck();
        }
Beispiel #4
0
        override protected bool HandleIntersectionWithBatchedStroke(BatchSubset rGroup)
        {
            if (altSelect)
            {
                if (m_BatchFilter == null && rGroup.m_ParentBatch != null)
                {
                    m_BatchFilter = rGroup.m_ParentBatch;
                }

                if (!ReferenceEquals(m_BatchFilter, rGroup.m_ParentBatch))
                {
                    return(true);
                }
            }
            else
            {
                m_BatchFilter = null;
            }
            var didRepaint = SketchMemoryScript.m_Instance.MemorizeStrokeRepaint(
                rGroup.m_Stroke, RecolorOn, RebrushOn, ResizeOn, JitterOn);

            if (didRepaint)
            {
                PlayModifyStrokeSound();
            }
            return(didRepaint);
        }
Beispiel #5
0
        override protected bool HandleIntersectionWithBatchedStroke(BatchSubset rGroup)
        {
#if (UNITY_EDITOR || EXPERIMENTAL_ENABLED)
            if (altSelect && Config.IsExperimental)
            {
                if (m_BatchFilter == null && rGroup.m_ParentBatch != null)
                {
                    m_BatchFilter = rGroup.m_ParentBatch;
                }

                if (!ReferenceEquals(m_BatchFilter, rGroup.m_ParentBatch))
                {
                    return(true);
                }
            }
            else
            {
                m_BatchFilter = null;
            }
#endif

            var didRepaint = SketchMemoryScript.m_Instance.MemorizeStrokeRepaint(
                rGroup.m_Stroke, m_Recolor, m_Rebrush);
            if (didRepaint)
            {
                PlayModifyStrokeSound();
            }
            return(didRepaint);
        }
Beispiel #6
0
 /// Like BaseBrushScript.CloneAsUndoObject(), except:
 /// - to avoid waste, use a pre-instantiated object
 /// - assume object already has UndoMeshAnimScript, doesn't have BaseBrushScript
 public void CloneAsUndoObject(BatchSubset subset, GameObject clone)
 {
     // GameObject clone = Instantiate<GameObject>(...);  premade for us
     // clone.name = ...;
     subset.m_ParentBatch.CopyToMesh(subset, clone);
     clone.transform.parent          = m_ParentTransform;
     Coords.AsLocal[clone.transform] = TrTransform.identity;
     clone.SetActive(true);
     clone.GetComponent <UndoMeshAnimScript>().Init();
 }
Beispiel #7
0
        override protected bool HandleIntersectionWithBatchedStroke(BatchSubset rGroup)
        {
#if (UNITY_EDITOR || EXPERIMENTAL_ENABLED)
            if (altSelect && Config.IsExperimental)
            {
                if (m_BatchFilter == null && rGroup.m_ParentBatch != null)
                {
                    m_BatchFilter = rGroup.m_ParentBatch;
                }

                if (!ReferenceEquals(m_BatchFilter, rGroup.m_ParentBatch))
                {
                    return(true);
                }
            }
            else
            {
                m_BatchFilter = null;
            }
#endif

            var  stroke              = rGroup.m_Stroke;
            var  isSelected          = SelectionManager.m_Instance.IsStrokeSelected(stroke);
            bool removeFromSelection = SelectionManager.m_Instance.ShouldRemoveFromSelection();
            if ((removeFromSelection && !isSelected) || (!removeFromSelection && isSelected))
            {
                // I think it's actually expected that this happens every now and then.
                // The intersection results are from some time in the past.
                Debug.LogWarning(
                    "Attempted to " + (removeFromSelection ? "deselect" : "select") +
                    " a stroke that's already " + (isSelected ? "selected" : "deselected") + ".");
                return(true);
            }

            PlayModifyStrokeSound();

            SketchMemoryScript.m_Instance.PerformAndRecordCommand(
                new SelectCommand(new[] { stroke },
                                  null,
                                  SelectionManager.m_Instance.SelectionTransform,
                                  initial: !m_ActiveSelectionHasAtLeastOneObject,
                                  deselect: removeFromSelection));
            m_ActiveSelectionHasAtLeastOneObject = true;
            m_LastIntersectionTime = Time.realtimeSinceStartup;

            // If we're selecting strokes while an existing selection has been transformed,
            // create a new selection and consolidate the command for selecting the future strokes
            // with the command to deselect the prior selection.
            if (!removeFromSelection && SelectionManager.m_Instance.SelectionWasTransformed)
            {
                EndSelection();
            }

            return(true);
        }
Beispiel #8
0
        override protected bool HandleIntersectionWithBatchedStroke(BatchSubset rGroup)
        {
            var didRepaint = SketchMemoryScript.m_Instance.MemorizeStrokeRepaint(
                rGroup.m_Stroke, m_Recolor, m_Rebrush);

            if (didRepaint)
            {
                PlayModifyStrokeSound();
            }
            return(didRepaint);
        }
Beispiel #9
0
        /// Creates and returns a new subset containing the passed geometry.
        /// Pass:
        ///   otherSubset -
        ///     Specifies both the material/batch to copy into,
        ///     as well as the geometry to copy.
        ///     May be owned by a different BatchManager.
        ///   leftTransform - optional transform to transform the subset.
        public BatchSubset CreateSubset(BatchSubset otherSubset, TrTransform?leftTransform = null)
        {
            Batch     otherBatch = otherSubset.m_ParentBatch;
            BatchPool otherPool  = otherBatch.ParentPool;

            var pool  = GetPool(otherPool.m_BrushGuid);
            var batch = GetBatch(pool, otherSubset.m_VertLength);

            return(batch.AddSubset(
                       otherBatch.Geometry,
                       otherSubset.m_StartVertIndex, otherSubset.m_VertLength,
                       otherSubset.m_iTriIndex, otherSubset.m_nTriIndex,
                       leftTransform));
        }
Beispiel #10
0
        public void CopyToMesh(BatchSubset subset, GameObject obj)
        {
            SelfCheck();
            int iVert = subset.m_StartVertIndex;
            int nVert = subset.m_VertLength;

            m_Geometry.CopyToMesh(obj.GetComponent <MeshFilter>().mesh,
                                  iVert, nVert,
                                  subset.m_iTriIndex, subset.m_nTriIndex);

            MeshRenderer objRenderer = obj.GetComponent <MeshRenderer>();

            // Temporary workaround for b/31346571
            objRenderer.material = GetComponent <MeshRenderer>().material;
        }
        private void AdjustMeter(BatchSubset subset, float fBrushSize, bool bAddToMeter)
        {
            //make brush size not matter if we're ignorning it
            if (!m_BrushSizeAffectsCost)
            {
                fBrushSize = 1.0f;
            }

            //figure out how much this batch costs
            BrushDescriptor brush = BrushCatalog.m_Instance.GetBrush(
                subset.m_ParentBatch.ParentPool.m_BrushGuid);
            float fCost = BaseBrushScript.GetStrokeCost(brush, subset.m_VertLength, fBrushSize);

            AdjustMeter(bAddToMeter ? fCost : -fCost);
        }
Beispiel #12
0
        static void WriteStroke(JsonWriter json, Stroke stroke,
                                Dictionary <Guid, int> brushMap)
        {
            json.WriteStartObject();

            var             brushGuid = stroke.m_BrushGuid;
            BrushDescriptor desc      = BrushCatalog.m_Instance.GetBrush(brushGuid);
            int             brushIndex;

            if (!brushMap.TryGetValue(brushGuid, out brushIndex))
            {
                brushIndex          = brushMap.Count;
                brushMap[brushGuid] = brushIndex;
            }

            json.WritePropertyName("brush");
            json.WriteValue(brushIndex);

            if (stroke.m_Type == Stroke.Type.BrushStroke)
            {
                // Some strokes (eg particles) don't have meshes. For now, assume that
                // if the stroke has a mesh, it should be written.
                var meshFilter = stroke.m_Object.GetComponent <MeshFilter>();
                if (meshFilter != null)
                {
                    var mesh = meshFilter.sharedMesh;
                    if (mesh != null)
                    {
                        WriteMesh(json, mesh, desc.VertexLayout);
                    }
                }
            }
            else if (stroke.m_Type == Stroke.Type.BatchedBrushStroke)
            {
                BatchSubset  subset = stroke.m_BatchSubset;
                GeometryPool geom   = subset.m_ParentBatch.Geometry;

                Mesh tempMesh = new Mesh();
                geom.CopyToMesh(tempMesh,
                                subset.m_StartVertIndex, subset.m_VertLength,
                                subset.m_iTriIndex, subset.m_nTriIndex);
                WriteMesh(json, tempMesh, geom.Layout);
                tempMesh.Clear();
                UnityEngine.Object.Destroy(tempMesh);
            }

            json.WriteEndObject();
        }
Beispiel #13
0
        public void RemoveSubset(BatchSubset subset)
        {
            // Often O(1) because it's the last one
            SelfCheck();
            int iSubset = m_Groups.LastIndexOf(subset);

            if (iSubset < 0)
            {
                Debug.Assert(false, "Not found");
                return;
            }

            // Could do some compaction, but this case is not very common.
            // Just disable the triangles. If all subsets after this one are
            // freed, we'll reclaim the space then.
            DisableSubset(subset);

            // If this is the last subset, we can free up some space.
            if (iSubset == m_Groups.Count - 1)
            {
                // It would be incorrect to simply subtract from Num{Verts,TriIndices}, because
                // there may be dead space before this subset.
                Debug.Assert(subset.m_StartVertIndex + subset.m_VertLength == m_Geometry.NumVerts);
                Debug.Assert(subset.m_iTriIndex + subset.m_nTriIndex == m_Geometry.NumTriIndices);
                int newNumVert, newNumIndices;
                if (iSubset > 0)
                {
                    var prev = m_Groups[iSubset - 1];
                    newNumVert    = prev.m_StartVertIndex + prev.m_VertLength;
                    newNumIndices = prev.m_iTriIndex + prev.m_nTriIndex;
                }
                else
                {
                    newNumVert = newNumIndices = 0;
                }

                m_Geometry.NumVerts      = newNumVert;
                m_Geometry.NumTriIndices = newNumIndices;
                DelayedUpdateMesh();
            }

            m_Groups.RemoveAt(iSubset);
            subset.m_ParentBatch = null;
            SelfCheck();
        }
Beispiel #14
0
        /// Returns a new subset containing the passed geometry.
        /// raises ArgumentOutOfRangeException if not enough room.
        /// Pass:
        ///   rMasterBrush - Geometry, all of which will be copied into the new subset
        public BatchSubset AddSubset(int nVert, int nTris, MasterBrush rMasterBrush)
        {
            SelfCheck();
            // If we're not empty, the caller should never have tried to add the subset,
            // because it's caller's responsibility to check HasSpaceFor().
            // If we're empty, allow anything (up to the Unity limit).
            if (!HasSpaceFor(nVert) && (m_Geometry.NumVerts > 0))
            {
                throw new ArgumentOutOfRangeException("nVert");
            }
            if (m_Geometry.NumVerts == 0)
            {
                m_Geometry.Layout = rMasterBrush.VertexLayout.Value;
            }

            BatchSubset child = new BatchSubset();

            child.m_ParentBatch = this;
            m_Groups.Add(child);
            child.m_Active         = true;
            child.m_StartVertIndex = m_Geometry.NumVerts;
            child.m_VertLength     = nVert;
            child.m_iTriIndex      = m_Geometry.NumTriIndices;
            child.m_nTriIndex      = nTris;
            // This is normally true -- unless the geometry has been welded
            // Debug.Assert(nVert % 3 == 0);
            child.m_Bounds = GetBoundsFor(rMasterBrush.m_Vertices, 0, nVert);

            if (nVert > 0)
            {
                m_Geometry.EnsureGeometryResident();
                AppendVertexData(nVert, rMasterBrush.m_Vertices,
                                 rMasterBrush.m_Normals,
                                 rMasterBrush.m_UVs,
                                 rMasterBrush.m_UVWs,
                                 rMasterBrush.m_Colors,
                                 rMasterBrush.m_Tangents);
                AppendTriangleData(child.m_StartVertIndex, child.m_nTriIndex, rMasterBrush.m_Tris);
                DelayedUpdateMesh();
            }

            SelfCheck();
            return(child);
        }
Beispiel #15
0
        public void EnableSubset(BatchSubset subset)
        {
            SelfCheck();
            if (subset.m_Active)
            {
                return;
            }

            Debug.Assert(!subset.m_Active);
            subset.m_Active = true;
            m_Geometry.EnsureGeometryResident();
            var aTris = m_Geometry.m_Tris.GetBackingArray();
            int t0    = subset.m_iTriIndex;
            int t1    = subset.m_iTriIndex + subset.m_nTriIndex;

            for (int t = t0; t < t1; ++t)
            {
                aTris[t] = subset.m_TriangleBackup[t - t0];
            }
            m_bTopologyDirty = true;
            SelfCheck();
        }
Beispiel #16
0
        /// Returns a new subset containing the passed geometry.
        /// raises ArgumentOutOfRangeException if not enough room.
        ///
        /// Make sure the triangle indices refer only to verts inside the
        /// passed range of verts.
        ///
        /// Pass:
        ///   geom          - Geometry, a subset of which will be copied into the new subset
        ///   i/nVert       - start/count of verts to copy
        ///   i/nTriIndex   - start/count of triangle indices to copy.
        ///   leftTransform - optional transform to transform the subset.
        ///
        public BatchSubset AddSubset(
            GeometryPool geom, int iVert, int nVert, int iTriIndex, int nTriIndex, TrTransform?leftTransform = null)
        {
            // If we're not empty, the caller should never have tried to add the subset,
            // because it's caller's responsibility to check HasSpaceFor().
            // If we're empty, allow anything (up to the Unity limit).
            SelfCheck();
            if (!HasSpaceFor(nVert) && (m_Geometry.NumVerts > 0))
            {
                throw new ArgumentOutOfRangeException("nVert");
            }
            if (m_Geometry.NumVerts == 0)
            {
                m_Geometry.Layout = geom.Layout;
            }

            BatchSubset child = new BatchSubset();

            child.m_ParentBatch = this;
            m_Groups.Add(child);
            child.m_Active         = true;
            child.m_StartVertIndex = m_Geometry.NumVerts;
            child.m_VertLength     = nVert;
            child.m_iTriIndex      = m_Geometry.NumTriIndices;
            child.m_nTriIndex      = nTriIndex;
            geom.EnsureGeometryResident();
            child.m_Bounds = GetBoundsFor(geom.m_Vertices, iVert, nVert, leftTransform);

            if (nVert > 0)
            {
                m_Geometry.Append(geom, iVert, nVert, iTriIndex, nTriIndex, leftTransform);
                DelayedUpdateMesh();
            }

            SelfCheck();
            return(child);
        }
        // Generate geometry from stroke as if it were being played back.
        // If played-back geometry is different, complain and make the new stroke visible.
        // TODO: do more kinds of brushes
        private static void SanityCheckGeometryGeneration(Stroke oldStroke)
        {
            if (oldStroke.m_Type == Stroke.Type.BrushStroke)
            {
                if (oldStroke.m_Object.GetComponent <MeshFilter>() == null)
                {
                    // Stroke can't be checked
                    return;
                }
            }

            {
                BrushDescriptor desc = BrushCatalog.m_Instance.GetBrush(oldStroke.m_BrushGuid);
                if (desc == null || desc.m_Nondeterministic)
                {
                    // Stroke doesn't want to be checked
                    return;
                }
            }

            // Re-create geometry. PointerManager's pointer management is a complete mess.
            // "5" is the most-likely to be unused.
            var pointer = PointerManager.m_Instance.GetTransientPointer(5);

            // Make a copy, since Begin/EndLineFromMemory mutate little bits of MemoryBrushStroke
            Stroke newStroke = new Stroke();

            newStroke.m_BrushGuid     = oldStroke.m_BrushGuid;
            newStroke.m_ControlPoints = oldStroke.m_ControlPoints;
            newStroke.m_BrushScale    = oldStroke.m_BrushScale;
            newStroke.m_BrushSize     = oldStroke.m_BrushSize;
            newStroke.m_Color         = oldStroke.m_Color;
            newStroke.m_Seed          = oldStroke.m_Seed;

            // Now swap r and b
            newStroke.m_Color.r = oldStroke.m_Color.b;
            newStroke.m_Color.b = oldStroke.m_Color.r;

            Array.Copy(oldStroke.m_ControlPointsToDrop, newStroke.m_ControlPointsToDrop,
                       oldStroke.m_ControlPointsToDrop.Length);

            newStroke.m_Object = pointer.BeginLineFromMemory(newStroke, oldStroke.Canvas);
            pointer.UpdateLineFromStroke(newStroke);

            // Compare geometry
            string err;
            var    newBrush = pointer.CurrentBrushScript;

            if (oldStroke.m_Type == Stroke.Type.BrushStroke)
            {
                Mesh oldMesh = oldStroke.m_Object.GetComponent <MeshFilter>().sharedMesh;
                err = Compare(oldMesh, newBrush);
            }
            else
            {
                BatchSubset  oldMesh = oldStroke.m_BatchSubset;
                GeometryPool allGeom = oldMesh.m_ParentBatch.Geometry;

                Vector3[] verts = allGeom.m_Vertices.GetBackingArray();
                int[]     tris  = allGeom.m_Tris.GetBackingArray();

                // To avoid super complications, only check in the common case of
                // uv0 == Vector2[]
                Vector2[] uv0;
                if (allGeom.Layout.texcoord0.size == 2)
                {
                    uv0 = allGeom.m_Texcoord0.v2.GetBackingArray();
                }
                else
                {
                    uv0 = null;
                }

                err = Compare(
                    verts, oldMesh.m_StartVertIndex, oldMesh.m_StartVertIndex + oldMesh.m_VertLength,
                    tris, oldMesh.m_iTriIndex, oldMesh.m_iTriIndex + oldMesh.m_nTriIndex,
                    uv0, newBrush);
            }

            if (err != null)
            {
                // Use assert so it shows up in ExceptionRenderScript
                UnityEngine.Debug.Assert(false, string.Format("Sanity check stroke: {0}", err));
                newBrush.ApplyChangesToVisuals();

                // Turn off batching for this stroke because our batching system currently
                // can't handle strokes that aren't on the undo stack, in sketch memory, etc
                bool prevValue = App.Config.m_UseBatchedBrushes;
                App.Config.m_UseBatchedBrushes = false;
                pointer.EndLineFromMemory(newStroke, discard: false);
                App.Config.m_UseBatchedBrushes = prevValue;
            }
            else
            {
                pointer.EndLineFromMemory(newStroke, discard: true);
            }
        }
 public static void InitUndoObject(BatchSubset subset)
 {
     // TODO: Finish moving control of prefab into BatchManager
     subset.Canvas.BatchManager.CloneAsUndoObject(
         subset, m_Instance.m_UndoBatchMesh);
 }
Beispiel #19
0
 /// An alternative to overriding HandleIntersection(),
 /// if subclasses need finer-grained control
 /// Returns true if an action was carried out as a result of the intersection.
 ///
 /// The subset is guaranteed to be valid (ie, not part of a deleted batch)
 /// but you should still check if its state is correct (ie, if you're erasing,
 /// that it's not already erased)
 virtual protected bool HandleIntersectionWithBatchedStroke(BatchSubset rGroup)
 {
     HandleIntersection(rGroup.m_Stroke);
     return(true);
 }
Beispiel #20
0
        /// 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);
        }
Beispiel #21
0
            // ------------------------------------------------------------------------------------------ //
            // Private Internals
            // ------------------------------------------------------------------------------------------ //

            override protected void OnReadResults()
            {
                // Mark results as ready.
                m_ResultCount = 0;
                if (m_ResultList != null)
                {
                    m_ResultList.Clear();
                }

                uint[] resultColors = GetTextureColors();

                //
                // Process the color buffer into result objects, if requested.
                //
                UnityEngine.Profiling.Profiler.BeginSample("Intersection: Process Results");
                HashSet <object> seen = AllocateHashSet();

                uint c;

                for (int i = 0; i < resultColors.Length; i++)
                {
                    if (m_ResultCount == m_MaxResults)
                    {
                        break;
                    }

                    c = resultColors[i];

                    if (c > 0)
                    {
                        // Don't bother looking up the batch if the exact result set wasn't requested.
                        if (m_ResultList == null)
                        {
                            m_ResultCount++;
                            continue;
                        }

                        // TODO: the Color32 -> object lookup is deterministic and O(n), so 'seen'
                        // should store the Color32, not the object.

                        int         triIndex = (int)(c & 0xffff) * 3;
                        ushort      batchId  = (ushort)((c & 0xffff0000) >> 16);
                        GrabWidget  widget   = null;
                        Batch       batch    = null;
                        BatchSubset subset   = null;

                        // See if this batch refers to a brush stroke.
                        batch = App.ActiveCanvas.BatchManager.GetBatch(batchId);
                        if (batch != null)
                        {
                            // TODO: move this into Batch, so can do binary search if necessary
                            for (int j = 0; j < batch.m_Groups.Count; j++)
                            {
                                BatchSubset bs = batch.m_Groups[j];
                                if (triIndex >= bs.m_iTriIndex && triIndex < bs.m_iTriIndex + bs.m_nTriIndex)
                                {
                                    subset = bs;
                                    break;
                                }
                            }

                            // A stroke may be deleted by the time this executes. This is due to the delay between
                            // sending an intersection request and processing the results.
                            if (subset == null)
                            {
                                // This actually happens in practice!
                                // TODO: investigate if it's okay
                                // Debug.LogWarningFormat(
                                //     "Unexpected: Nonexistent subset for c = {0:x} {1:x}",
                                //     (ushort)(c >> 16),
                                //     (ushort)(c & 0xffff));
                                continue;
                            }

                            if (!seen.Add(subset))
                            {
                                continue;
                            }
                        }
                        else
                        {
                            // Not a brush stroke?  See if this is a widget.
                            widget = WidgetManager.m_Instance.GetBatch(batchId);

                            // A widget may be deleted by the time this executes. This is due to the delay between
                            // sending an intersection request and processing the results.
                            if (widget == null)
                            {
                                continue;
                            }

                            if (!seen.Add(widget))
                            {
                                continue;
                            }
                        }

                        // A batch should never be null, but in the future that may change. This is possible due
                        // to the delay between sending an intersection request and processing the results.
                        if (batch == null && widget == null)
                        {
                            Debug.LogWarningFormat(
                                "Unexpected: Null batch {0} and widget {1}",
                                ReferenceEquals(batch, null), ReferenceEquals(widget, null));
                            continue;
                        }

                        // These cannot both be valid.
                        Debug.Assert(subset == null || widget == null);
                        m_ResultList.Add(
                            new BatchResult {
                            widget = widget, subset = subset
                        });
                        m_ResultCount++;
                    }
                }

                DeallocateHashSet(seen);
                UnityEngine.Profiling.Profiler.EndSample();
            }