0
\$\begingroup\$

*Edit: I finally figured out after some profiling that the problematic code was not actually the ones part of ECS, but this line over here:

objectManagerController.GenerateMetaballEdgePointVerteciesList(metaballEdgePointVertexCoordinates); //writes magnets, this is the culprit for the lagg!!!

in OnRenderImage() of MainCameraController:

    public void OnRenderImage(RenderTexture src, RenderTexture dest) {
        //Debug.Log("Metaball Shader, OnRenderImage for main camera");
        //called after operationCamera's OnRenderImage is called game object count times?
            //How the fuck was I able to make sure that src contains the full texture upon function call?
            //Since all rendered colored slimes are moved back to default layer after each of them are separately
            //rendered. Thus by the time MainCameraController.OnRenderImage is called all slimes are back in default layer

        //Not the case where main camera is not seeing anything

        //Resolution
        Vector2Int resolution = new Vector2Int( preprocessCameraRender.width, preprocessCameraRender.height );

        //Thread control (hoping this is accurate enough)
        threadGroupX = Mathf.CeilToInt(preprocessCameraRender.width / (float)threadsPerGroup);
        threadGroupY = Mathf.CeilToInt(preprocessCameraRender.height / (float)threadsPerGroup);

        //Make a writable copy of src and dest
        //RenderTexture srcWritable = new RenderTexture(src.width, src.height, 32, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Default){ useMipMap = false };
        RenderTexture srcWritable = new RenderTexture(preprocessCameraRender.width, preprocessCameraRender.height, 32, colorFormat, 1)
        {
            useMipMap = false,
            autoGenerateMips = false,
            enableRandomWrite = true // Set the UAV usage flag
        };
        srcWritable.Create();

        //At this point both srcWritable and destWritable empty
        RenderTexture destWritable = new RenderTexture(srcWritable);
        destWritable.Create();

        RenderTexture metaballEdgeTexture = new RenderTexture(srcWritable);
        metaballEdgeTexture.Create();

        //Copying src to srcWritable
        RenderTexture.active = src;
        Graphics.Blit(src, srcWritable);

        //metaball edge texture
        // RenderTexture metaballEdgeTexture = CreateTexture("MetaballEdgeTexture", resolution, colorFormat);

        //Fill compute shader variables
        metaballComputeShader.SetTexture(kernelHandleFirst, "Src", srcWritable);
        metaballComputeShader.SetTexture(kernelHandleFirst, "Dest", destWritable);
        //Inner Edge Texture(empty at this point)
        metaballComputeShader.SetTexture(kernelHandleFirst, "MetaballEdgeTex", metaballEdgeTexture);
        metaballComputeShader.SetFloat("MaxWidth", preprocessCameraRender.width);
        metaballComputeShader.SetFloat("MaxHeight", preprocessCameraRender.height);
        metaballComputeShader.SetFloat("GameObjectListCount", colorSlimes.Count);
        metaballComputeShader.SetVector( "_Resolution", (Vector2) resolution );
    
    //Texture Arrays
        //First dispatch prep
        //Read (Raw) Color Texture Array
        metaballComputeShader.SetTexture(kernelHandleFirst, "ColorTextureArray", colorTextureArray);
        //Read (Larger) Color Texture's Array
        metaballComputeShader.SetTexture(kernelHandleFirst, "ColorTextureArrayBeta", colorTextureArrayBeta);
        //SDF Texture Array
        metaballComputeShader.SetTexture(kernelHandleFirst, "SDFTextureArray", sdfTextureArray);
        //Delta X Texture Array
        metaballComputeShader.SetTexture(kernelHandleFirst, "DeltaXTextureArray", deltaXTextureArray);
        //Delta Y Texture Array
        metaballComputeShader.SetTexture(kernelHandleFirst, "DeltaYTextureArray", deltaYTextureArray);
        //Weight Texture Array(fill weight values in it)
        metaballComputeShader.SetTexture(kernelHandleFirst, "WeightTextureArray", weightTextureArray);
        //Blur Texture Array
        // metaballComputeShader.SetTexture(kernelHandleFirst, "BlurTextureArray", blurTextureArray);
        
        //First metaball compute shader dispatch
        metaballComputeShader.Dispatch(kernelHandleFirst, threadGroupX, threadGroupY, 1);
        //Debug.Log("First metaball effect complete!");
        // Debug.Log("destWritable size: " + destWritable.width + " " + destWritable.height);
        // Debug.Log("metaballEdgeTexture size: " + metaballEdgeTexture.width + " " + metaballEdgeTexture.height);

        //Copying destWritable(which accounts for overlap blending) to srcWritable
        // RenderTexture.active = dest;
        // Graphics.Blit(dest, src);
        // RenderTexture.active = null;

        //Apply sobel filter to metaballEdgeTexture
        RenderTexture sobelledMetaballEdgeTexture = Sobel(sobelComputeShader, metaballEdgeTexture, resolution, threadGroupX, threadGroupY);
        //Debug.Log("sobelledMetaballEdgeTexture size: " + sobelledMetaballEdgeTexture.width + " " + sobelledMetaballEdgeTexture.height);
        RenderTexture sobelledMetaballEdgeTextureFinal = new RenderTexture(metaballEdgeTexture);
        sobelledMetaballEdgeTextureFinal.Create(); 

    //Final dispatch prep
        //Receive previous srcWritable(which is now simply a copy of destWritable) as Src
        metaballComputeShader.SetTexture(kernelHandleFinal, "Src", srcWritable);
        //First write same value as Src to Dest and then rewrite during dispatch
        metaballComputeShader.SetTexture(kernelHandleFinal, "Dest", destWritable);
        //InnerEdgeTex 
        metaballComputeShader.SetTexture(kernelHandleFinal, "MetaballEdgeTex", sobelledMetaballEdgeTexture);
        //Final processed sobelled metaball edge tex
        metaballComputeShader.SetTexture(kernelHandleFinal, "MetaballEdgeTexFinal", sobelledMetaballEdgeTextureFinal);
        //Buffer.SetCounterValue(0); only used for Append/Consume buffers

        //Color Texture Array
        metaballComputeShader.SetTexture(kernelHandleFinal, "ColorTextureArray", colorTextureArray);
        //SDF Texture Array
        metaballComputeShader.SetTexture(kernelHandleFinal, "SDFTextureArray", sdfTextureArray);
        //Delta X Texture Array
        metaballComputeShader.SetTexture(kernelHandleFinal, "DeltaXTextureArray", deltaXTextureArray);
        //Delta Y Texture Array
        metaballComputeShader.SetTexture(kernelHandleFinal, "DeltaYTextureArray", deltaYTextureArray);
        //Weight Texture Array(now read from it)
        metaballComputeShader.SetTexture(kernelHandleFinal, "WeightTextureArray", weightTextureArray);

        ComputeBuffer magnetVertexCoordinateBuffer = new ComputeBuffer(src.width * src.height, sizeof(uint) * 2, ComputeBufferType.Append);

        // Create a buffer to hold the counter.
        ComputeBuffer countBuffer = new ComputeBuffer(1, sizeof(int), ComputeBufferType.Raw);
        int[] countArray = new int[1]; //to read back counter data later

        //Set buffer to hold values and buffer to hold counter
        metaballComputeShader.SetBuffer(kernelHandleFinal, "MagnetVertexCoordinates", magnetVertexCoordinateBuffer);
        magnetVertexCoordinateBuffer.SetCounterValue(0);
        
        //Final metaball compute shader dispatch
        metaballComputeShader.Dispatch(kernelHandleFinal, threadGroupX, threadGroupY, 1);
        //Debug.Log("sobelledEdgeTextureFinal size: " + sobelledMetaballEdgeTextureFinal.width + " " + sobelledMetaballEdgeTextureFinal.height);

        // Copy the append buffer count to the count buffer.
        ComputeBuffer.CopyCount(magnetVertexCoordinateBuffer, countBuffer, 0);

        // Read the count to a c# int[]
        countBuffer.GetData(countArray);
        int numEdgePoints = countArray[0];

        // Now you can read back just that many edge points.
        uint2[] edgePoints = new uint2[numEdgePoints];
        magnetVertexCoordinateBuffer.GetData(edgePoints, 0, 0, numEdgePoints);

        Debug.Log("edgePoints (post EdgePointsFilled) Count: " + edgePoints.Length);
        List<Vector3> metaballEdgePointVertexCoordinates = edgePoints.Select(ep => new Vector3(ep.x, ep.y, 0)).ToList();

        //Call object manager controller's GenerateMetaballEdgePointVerteciesList(magnetic)
        objectManagerController.GenerateMetaballEdgePointVerteciesList(metaballEdgePointVertexCoordinates); //writes magnets, this is the culprit for the lagg!!!

        //Write metaball effect to final dest (non-metaballed, but hoping the magnets will work)
        Graphics.SetRenderTarget(dest);
        Graphics.Blit(destWritable, dest);
        RenderTexture.active = null;

        srcWritable.Release();
        destWritable.Release();
        metaballEdgeTexture.Release();
        sobelledMetaballEdgeTexture.Release();
        sobelledMetaballEdgeTextureFinal.Release();
        countBuffer.Release();
        magnetVertexCoordinateBuffer.Release();
        //Debug.Log("Metaball effect complete!");
    }

This is the code for GenerateMetaballEdgePointVerticesList in objectManagerController:

    public void GenerateMetaballEdgePointVerteciesList(List<Vector3> metaballEdgePixels) // Generates and initialises metaball edge vertices passed from MainCameraController
    {
        //if passed list is empty, reset all vertices (it means game objects are not close enough to metaball)
        if (metaballEdgePixels.Count == 0) {
            //Reset vertices for each game object
            foreach (GameObject colorSlimesDisplay in colorSlimesDisplayGlobal) {
                colorSlimesDisplay.GetComponent<SpriteMeshModifier>().Reset();
            }
            
            //Reset metaballEdgePointVertices
            foreach (var item in GetComponentsInChildren<MetaballEdgePointVertex>())
            {
                DestroyImmediate(item.gameObject);
            }
            metaballEdgePointVertices.Clear();
            //Reset metaballEdgePointVertex GOs
            metaballEdgePointVertexGOs.Clear();
        }
        else {
            //Create magnetic metaball edge points
            foreach (var item in GetComponentsInChildren<MetaballEdgePointVertex>())
            {
                DestroyImmediate(item.gameObject);
            }
            metaballEdgePointVertices.Clear();
            //Reset metaballEdgePointVertex GOs
            metaballEdgePointVertexGOs.Clear();
            
            int cntr = 0;
            // float circleColliderRadius = 10.0f;
            Debug.Log("Logging metaball vertex locations");

            //typeof(metaballEdgePointVertex) == typeof(Vector3)
            foreach (Vector3 metaballEdgePointVertex in metaballEdgePixels)
            {
                //Convert passed compute shader RenderTexture.id.xy to world coord
                Vector2 normalizedCoords = new Vector2(
                    metaballEdgePointVertex.x / preprocessCameraRenderTexture.width,
                    metaballEdgePointVertex.y / preprocessCameraRenderTexture.height
                );

                Vector2 screenCoords = new Vector2(
                    normalizedCoords.x * Screen.width,
                    normalizedCoords.y * Screen.height
                );

                Vector3 metaballEdgePointVertexWorldCoords = _displayCameraGlobal.ScreenToWorldPoint(new Vector3(screenCoords.x, screenCoords.y, 0));
                metaballEdgePointVertexWorldCoords.z = 0;
                Debug.Log(metaballEdgePointVertexWorldCoords);

                var go = Instantiate(MetaballEdgePointVertex, transform); //transform of the game object that will become parent of GOs == ObjectManager.transform
                go.name = go.name + " " + cntr;

                // Add CircleCollider2D
                // CircleCollider2D circleCollider = go.AddComponent<CircleCollider2D>();
                // // Configure CircleCollider2D properties
                // circleCollider.radius = circleColliderRadius;
                // circleCollider.isTrigger = true;

                metaballEdgePointVertexGOs.Add(go);

                var ver = go.GetComponent<MetaballEdgePointVertex>();
                ver.SetInitialPos(metaballEdgePointVertexWorldCoords, cntr, go); //this is reference to SpriteMeshModifier object that owns vertices
                metaballEdgePointVertices.Add(ver);
                cntr++;
            }
        }
    }

Here is a visual of that shows the magnets that each vertex is trying to go to:

enter image description here

Here are 2 techniques I can think of:

  1. Create a public RenderTexture in the Unity editor using create -> RenderTexture simply Blit in MainCameraController's OnRenderImage when the magnet texture is created. The logic for extracting opaque pixels from the public RenderTexture to a List and then generation of metaballEdgePointVerticesList in objectManagerController.cs(since this is the de-facto MonoBehavior global game object manager script)

  2. In MainCameraController's OnRenderImage, update a newly created public List metaballEdgePointPositionsList(objectManagerController.cs) instead of instantiating a new List metaballEdgePointPositionsList in OnRenderImage every time.

metaballEdgePointPositionsList = edgePoints.Select(ep => new Vector3(ep.x, ep.y, 0)).ToList();

Then the ECS SystemBase class will fetch from its reference of objectManagerController its metaballEdgePointPositionsList and generate the actual global List metaballEdgePointVertexList in the first compute job using the elements of metaballEdgePointPositionsList.

My question is, which one is the better method of optimization? Also, in order to optimize the generation of vertices, what is the general method of updating MonoBehavior global list in a SystemBase object job? I am having trouble figuring out how exactly I should translate GenerateMetaballEdgePointVerteciesList to a ComputeJob, since the job GenerateMetaballEdgePointVerteciesList is to Add Vertex objects to a MonoBehavior global objectManager, and ComputeJobs require to operate on elements of a NativeArray and cannot modify MonoBehavior objects directly.

May I have some more sophisticated advice in this area in general?

*Here is a demonstration of the lag that arises from updating the mesh vertice: https://drive.google.com/file/d/1wUGs4MV1KoIgkgBqv9zD2g87zs9AlRzN/view?usp=sharing

\$\endgroup\$
1
  • \$\begingroup\$ What bottlenecks do you find when you profile in the code? You've identified that it's likely related to your ECS, but the more you can narrow down the situations that lead performance problems, the faster and better answers you are likely to get. We can try to make educated guesses about what might be impacting performance, but it's better to start by using the profiler to measure the performance issues on an actual run. \$\endgroup\$
    – Pikalek
    Sep 19, 2023 at 14:14

1 Answer 1

0
\$\begingroup\$

I was an idiot for trying to create game objects for metaball edge magnet points. After all, it was only their Vector3 position information I needed to move the vertices of the slimes accordingly. Here is how I speeded up the merging process a little

MainCameraController

    public Vector3 WorldCoordsTranslate(uint2 edgePoint) {
        Vector3 metaballEdgePointVertexWorldCoords = new Vector3(edgePoint.x, edgePoint.y, 0);

        //Convert passed compute shader RenderTexture.id.xy to world coord
        Vector2 normalizedCoords = new Vector2(
            metaballEdgePointVertexWorldCoords.x / preprocessCameraRender.width,
            metaballEdgePointVertexWorldCoords.y / preprocessCameraRender.height
        );

        Vector2 screenCoords = new Vector2(
            normalizedCoords.x * _displayCamera.scaledPixelWidth,
            normalizedCoords.y * _displayCamera.scaledPixelHeight
        );

        metaballEdgePointVertexWorldCoords = _displayCamera.ScreenToWorldPoint(new Vector3(screenCoords.x, screenCoords.y, 0));
        metaballEdgePointVertexWorldCoords.z = 0;

        return metaballEdgePointVertexWorldCoords;
    }

    public void OnRenderImage(RenderTexture src, RenderTexture dest) {
        //Debug.Log("Metaball Shader, OnRenderImage for main camera");
        //called after operationCamera's OnRenderImage is called game object count times?
            //How the fuck was I able to make sure that src contains the full texture upon function call?
            //Since all rendered colored slimes are moved back to default layer after each of them are separately
            //rendered. Thus by the time MainCameraController.OnRenderImage is called all slimes are back in default layer

        //Not the case where main camera is not seeing anything

        //Resolution
        Vector2Int resolution = new Vector2Int( preprocessCameraRender.width, preprocessCameraRender.height );

        //Thread control (hoping this is accurate enough)
        threadGroupX = Mathf.CeilToInt(preprocessCameraRender.width / (float)threadsPerGroup);
        threadGroupY = Mathf.CeilToInt(preprocessCameraRender.height / (float)threadsPerGroup);

        //Make a writable copy of src and dest
        //RenderTexture srcWritable = new RenderTexture(src.width, src.height, 32, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Default){ useMipMap = false };
        RenderTexture srcWritable = new RenderTexture(preprocessCameraRender.width, preprocessCameraRender.height, 32, colorFormat, 1)
        {
            useMipMap = false,
            autoGenerateMips = false,
            enableRandomWrite = true // Set the UAV usage flag
        };
        srcWritable.Create();

        //At this point both srcWritable and destWritable empty
        RenderTexture destWritable = new RenderTexture(srcWritable);
        destWritable.Create();

        RenderTexture metaballEdgeTexture = new RenderTexture(srcWritable);
        metaballEdgeTexture.Create();

        //Copying src to srcWritable
        RenderTexture.active = src;
        Graphics.Blit(src, srcWritable);

        //metaball edge texture
        // RenderTexture metaballEdgeTexture = CreateTexture("MetaballEdgeTexture", resolution, colorFormat);

        //Fill compute shader variables
        metaballComputeShader.SetTexture(kernelHandleFirst, "Src", srcWritable);
        metaballComputeShader.SetTexture(kernelHandleFirst, "Dest", destWritable);
        //Inner Edge Texture(empty at this point)
        metaballComputeShader.SetTexture(kernelHandleFirst, "MetaballEdgeTex", metaballEdgeTexture);
        metaballComputeShader.SetFloat("MaxWidth", preprocessCameraRender.width);
        metaballComputeShader.SetFloat("MaxHeight", preprocessCameraRender.height);
        metaballComputeShader.SetFloat("GameObjectListCount", colorSlimes.Count);
        metaballComputeShader.SetVector( "_Resolution", (Vector2) resolution );
    
    //Texture Arrays
        //First dispatch prep
        //Read (Raw) Color Texture Array
        metaballComputeShader.SetTexture(kernelHandleFirst, "ColorTextureArray", colorTextureArray);
        //Read (Larger) Color Texture's Array
        metaballComputeShader.SetTexture(kernelHandleFirst, "ColorTextureArrayBeta", colorTextureArrayBeta);
        //SDF Texture Array
        metaballComputeShader.SetTexture(kernelHandleFirst, "SDFTextureArray", sdfTextureArray);
        //Delta X Texture Array
        metaballComputeShader.SetTexture(kernelHandleFirst, "DeltaXTextureArray", deltaXTextureArray);
        //Delta Y Texture Array
        metaballComputeShader.SetTexture(kernelHandleFirst, "DeltaYTextureArray", deltaYTextureArray);
        //Weight Texture Array(fill weight values in it)
        metaballComputeShader.SetTexture(kernelHandleFirst, "WeightTextureArray", weightTextureArray);
        //Blur Texture Array
        // metaballComputeShader.SetTexture(kernelHandleFirst, "BlurTextureArray", blurTextureArray);
        
        //First metaball compute shader dispatch
        metaballComputeShader.Dispatch(kernelHandleFirst, threadGroupX, threadGroupY, 1);
        //Debug.Log("First metaball effect complete!");
        // Debug.Log("destWritable size: " + destWritable.width + " " + destWritable.height);
        // Debug.Log("metaballEdgeTexture size: " + metaballEdgeTexture.width + " " + metaballEdgeTexture.height);

        //Copying destWritable(which accounts for overlap blending) to srcWritable
        // RenderTexture.active = dest;
        // Graphics.Blit(dest, src);
        // RenderTexture.active = null;

        //Apply sobel filter to metaballEdgeTexture
        RenderTexture sobelledMetaballEdgeTexture = Sobel(sobelComputeShader, metaballEdgeTexture, resolution, threadGroupX, threadGroupY);
        //Debug.Log("sobelledMetaballEdgeTexture size: " + sobelledMetaballEdgeTexture.width + " " + sobelledMetaballEdgeTexture.height);
        RenderTexture sobelledMetaballEdgeTextureFinal = new RenderTexture(metaballEdgeTexture);
        sobelledMetaballEdgeTextureFinal.Create(); 

    //Final dispatch prep
        //Receive previous srcWritable(which is now simply a copy of destWritable) as Src
        metaballComputeShader.SetTexture(kernelHandleFinal, "Src", srcWritable);
        //First write same value as Src to Dest and then rewrite during dispatch
        metaballComputeShader.SetTexture(kernelHandleFinal, "Dest", destWritable);
        //InnerEdgeTex 
        metaballComputeShader.SetTexture(kernelHandleFinal, "MetaballEdgeTex", sobelledMetaballEdgeTexture);
        //Final processed sobelled metaball edge tex
        metaballComputeShader.SetTexture(kernelHandleFinal, "MetaballEdgeTexFinal", sobelledMetaballEdgeTextureFinal);
        //Buffer.SetCounterValue(0); only used for Append/Consume buffers

        //Color Texture Array
        metaballComputeShader.SetTexture(kernelHandleFinal, "ColorTextureArray", colorTextureArray);
        //SDF Texture Array
        metaballComputeShader.SetTexture(kernelHandleFinal, "SDFTextureArray", sdfTextureArray);
        //Delta X Texture Array
        metaballComputeShader.SetTexture(kernelHandleFinal, "DeltaXTextureArray", deltaXTextureArray);
        //Delta Y Texture Array
        metaballComputeShader.SetTexture(kernelHandleFinal, "DeltaYTextureArray", deltaYTextureArray);
        //Weight Texture Array(now read from it)
        metaballComputeShader.SetTexture(kernelHandleFinal, "WeightTextureArray", weightTextureArray);

        ComputeBuffer magnetVertexCoordinateBuffer = new ComputeBuffer(src.width * src.height, sizeof(uint) * 2, ComputeBufferType.Append);

        // Create a buffer to hold the counter.
        ComputeBuffer countBuffer = new ComputeBuffer(1, sizeof(int), ComputeBufferType.Raw);
        int[] countArray = new int[1]; //to read back counter data later

        //Set buffer to hold values and buffer to hold counter
        metaballComputeShader.SetBuffer(kernelHandleFinal, "MagnetVertexCoordinates", magnetVertexCoordinateBuffer);
        magnetVertexCoordinateBuffer.SetCounterValue(0);
        
        //Final metaball compute shader dispatch
        metaballComputeShader.Dispatch(kernelHandleFinal, threadGroupX, threadGroupY, 1);
        //Debug.Log("sobelledEdgeTextureFinal size: " + sobelledMetaballEdgeTextureFinal.width + " " + sobelledMetaballEdgeTextureFinal.height);

        // Copy the append buffer count to the count buffer.
        ComputeBuffer.CopyCount(magnetVertexCoordinateBuffer, countBuffer, 0);

        // Read the count to a c# int[]
        countBuffer.GetData(countArray);
        int numEdgePoints = countArray[0];

        // Now you can read back just that many edge points.
        uint2[] edgePoints = new uint2[numEdgePoints];
        magnetVertexCoordinateBuffer.GetData(edgePoints, 0, 0, numEdgePoints);

        //Debug.Log("edgePoints (post EdgePointsFilled) Count: " + edgePoints.Length);
        objectManagerController.metaballEdgePointVertices = edgePoints.Select(ep => WorldCoordsTranslate(ep)).ToList();
        //Debug.Log("edgePoints (post EdgePointsFilled) Count: " + objectManagerController.metaballEdgePointVertices.Count);

        //Write metaball effect to final dest (non-metaballed, but hoping the magnets will work)
        Graphics.SetRenderTarget(dest);
        Graphics.Blit(destWritable, dest);
        RenderTexture.active = null;

        srcWritable.Release();
        destWritable.Release();
        metaballEdgeTexture.Release();
        sobelledMetaballEdgeTexture.Release();
        sobelledMetaballEdgeTextureFinal.Release();
        countBuffer.Release();
        magnetVertexCoordinateBuffer.Release();
        //Debug.Log("Metaball effect complete!");
    }

VertexUpdater

    private void UpdatePositions(in NativeArray<VertexCopy> copiesArray, SpriteMeshModifier spriteMeshModifier) {
        for (int i = 0; i < spriteMeshModifier.vertices.Count; ++i) {
            if(copiesArray[i]._currentPos == copiesArray[i]._closestMagnetPoint) {
                Vector3 updatePosition = Vector3.Lerp(copiesArray[i]._closestMagnetPoint, spriteMeshModifier.vertices[i].initialPos, copiesArray[i]._minDistanceToInitialpos / 0.8f);
                spriteMeshModifier.vertices[i]._closestMagnetPoint = copiesArray[i]._closestMagnetPoint;
                spriteMeshModifier.UpdateVertecies(updatePosition, copiesArray[i]._index);
            }
            else {
                spriteMeshModifier.ResetPositionOfSelectedVertex(copiesArray[i]._index);
            }
            spriteMeshModifier.UpdateMesh();
        }
    }

    private void CallJobs() {
        foreach (SpriteMeshModifier spriteMeshModifier in spriteMeshModifiers) {
            if(spriteMeshModifier.vertices.Count > 0 && spriteMeshModifier._isRegenerated && spriteMeshModifier.objectManagerController.metaballEdgePointVertices.Count > 0) {
                NativeArray<VertexCopy> vertexCopies = new NativeArray<VertexCopy>(spriteMeshModifier.vertices.Count, Allocator.TempJob);
                NativeArray<Vector3> metaballVertexPositions = new NativeArray<Vector3>(spriteMeshModifier.objectManagerController.metaballEdgePointVertices.Count, Allocator.TempJob);
                CopyThingsTo(ref vertexCopies, ref metaballVertexPositions, spriteMeshModifier);
                // Do heavy computation in a job
                ComputeJob job = new ComputeJob() {
                    vertexCopyArray = vertexCopies,
                    metaballVertexPositionArray = metaballVertexPositions
                };
                job.ScheduleParallel(vertexCopies.Length, 64, default).Complete();
                UpdatePositions(vertexCopies, spriteMeshModifier);
                // Don't forget to dispose
                vertexCopies.Dispose();
                metaballVertexPositions.Dispose();
            }
            else if(spriteMeshModifier.vertices.Count > 0 && spriteMeshModifier._isRegenerated && spriteMeshModifier.objectManagerController.metaballEdgePointVertices.Count <= 0) {
                // Debug.Log("metaballEdgePointVertices.Count: " + spriteMeshModifier.objectManagerController.metaballEdgePointVertices.Count);
                //Reset vertices for each game object
                spriteMeshModifier.Reset();
            }
        }
    }

The final problem is that UpdatePositions is still a bit laggy due to the very fact that it's logic contains updating a mesh's vertices. As far as I know, ECS only allows copies of MonoBehavior Lists and does operations on them. Is there a way to speed this up even more? I want to harness the power of multithreading for UpdatePositions as well, but the entire mesh generation logic heavily relies heavily on MonoBehavior components. How to I translate all of the mesh vertex updating logic to DOTS such that even the mesh updating is optimized fully?

Here are additional method definitions for FYI SpriteMeshModifier

        public void UpdateVertecies(Vector3 moveTo, int index)    //  Update vertices
        {
            // moveTo = Quaternion.Inverse(transform.rotation) * moveTo;
            // Vector3 newMousePosition = new Vector3(moveTo.x / transform.localScale.x, moveTo.y / transform.localScale.y, moveTo.z / transform.localScale.z)
            //       - Quaternion.Inverse(transform.rotation) * new Vector3(transform.position.x / transform.localScale.x, transform.position.y / transform.localScale.y, transform.position.z / transform.localScale.z);

            //UpdatePositionOfVertices(moveTo, newMousePosition - vertices[index].currentPos);
            //UpdatePositionOfSelectedVertex(index, newMousePosition);
            UpdatePositionOfSelectedVertex(index, moveTo);
            //UpdateMesh();
        }

        private void UpdatePositionOfSelectedVertex(int index, Vector3 pos) // Update position of selected vertex
        {
            vertices[index].currentPos = pos;
        }

        public void UpdateMesh()                        // Updates Mesh
        {
            Mesh mesh = GetComponent<MeshFilter>().sharedMesh;
            mesh.vertices = vertices.Select(x => x.currentPos).ToArray();
            GetComponent<MeshFilter>().sharedMesh = mesh;
        }

As you can see, the sharedMesh of the MeshFilter component of SpriteMeshModifer(which is attached to each game object) is generated using MonoBehavior logic, and what I want is to be able to run UpdateMesh for all the updated vertices in the fastest way possible.

\$\endgroup\$
1
  • \$\begingroup\$ Please use Answer posts for answers and Question posts for questions. There's no way on this site for someone to post an answer to an answer. So if you want to ask something new, you should either edit your question to ask that, or post a new question instead. Remember to mark answers that worked for you as "Accepted" by clicking the checkmark icon in the top-left. \$\endgroup\$
    – DMGregory
    Sep 24, 2023 at 7:26

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .