Скорость дессериализации

Общие вопросы о Unity3D

Скорость дессериализации

Сообщение Roman777 13 сен 2017, 18:34

Добрый всем день!
Я попробовал сериализовать GameObject. Сохранил все интересующие меня параметры (Mesh|Position|Rotation|Material|Color), всё это в сериализуемых типах сохранил и сериализовал. Но меня опечалил размер бинарника :5400кбайт. И ещё больше расстроила скорость десериализации. Сериализацию я использовал стандартную (с маркером [System.Serializable]). Секунд 30 загружается объект из такого бинарника, хотя создаётся он в считанные секунды. Почему такая большая разница в скоростях сериализации и десериализации? И это нормальная скорость десериализации для такого файла?
Roman777
UNIт
 
Сообщения: 95
Зарегистрирован: 06 мар 2016, 12:09

Re: Скорость дессериализации

Сообщение Tumbalalaika 13 сен 2017, 19:21

Цель именно сериализации в бинарник? Код где, или телепатически догадаться где вы там поля своего "GameObject" циклом по 10 раз пишете? Вы не можете построить вопрос, о какой программной логике блин речь? Вобщем вот я тут товарищу накатал пример с сериализацией юнитевской - viewtopic.php?f=5&t=44785 можете попробовать разобраться.
Tumbalalaika
UNец
 
Сообщения: 17
Зарегистрирован: 12 сен 2017, 19:01

Re: Скорость дессериализации

Сообщение Roman777 13 сен 2017, 19:45

Собственно да, именно запись в бинарник интересует. Меня в большей степени волнует не размер файла. Для обычного бокса он крайне мал, я же сохраняю достаточно деталлизированый объект. Меш более 100к треугольных полигонов имеет.
Меня интреесует именно скорость десериализации. Нормально ли для таких размеров порядка 30 секунд загрузки.

Синтаксис:
Используется csharp
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;

public static class Saves{
        public static List<Scena> savedScens = new List<Scena>();
        public static bool exist=false;

        //методы загрузки и сохранения статические, поэтому их можно вызвать откуда угодно
        public static void Save() {
                savedScens.Add(Scena.current);
                BinaryFormatter bf = new BinaryFormatter();
                //Application.persistentDataPath это строка; выведите ее в логах и вы увидите расположение файла сохранений
                FileStream file = File.Create (Application.persistentDataPath + "/savedGames.gd");
                bf.Serialize(file, savedScens);
                file.Close();
        }  
        public static void Load() {
                if(File.Exists(Application.persistentDataPath + "/savedGames.gd")) {
                        BinaryFormatter bf = new BinaryFormatter();
                        FileStream file = File.Open(Application.persistentDataPath + "/savedGames.gd", FileMode.Open);
                        savedScens = (List<Scena>)bf.Deserialize(file);
                        file.Close();
                        exist=true;
                }

        }
}

Всё время, в основном, занимает именно строчка "savedScens = (List<Scena>)bf.Deserialize(file);". Остальное - доли секунды.
За Ваш пример спасибо, гляну его =)
Roman777
UNIт
 
Сообщения: 95
Зарегистрирован: 06 мар 2016, 12:09

Re: Скорость дессериализации

Сообщение Tumbalalaika 13 сен 2017, 20:00

Ну я так понимаю во время десериализации может происходить что угодно.

А что именно вы делаете, вы генерируете меш в рантайме, и хотите его сериализовать в собственный бинарный формат? Все зависит от целей, может вообще проще сохранить процедурный меш как стандартный юнитевский ассет?
Tumbalalaika
UNец
 
Сообщения: 17
Зарегистрирован: 12 сен 2017, 19:01

Re: Скорость дессериализации

Сообщение Roman777 13 сен 2017, 20:22

"Ну я так понимаю во время десериализации может происходить что угодно.

А что именно вы делаете, вы генерируете меш в рантайме, и хотите его сериализовать в собственный бинарный формат? Все зависит от целей, может вообще проще сохранить процедурный меш как стандартный юнитевский ассет?"

Нет, несколько проще пока подход. Поскольку Геймобъект, Меш, материал и все эти великолепные объекты не сереализуются, я просто записываю все данные в массивы и переменные сериализуемых типов (float, в основном), а их уже сериализую. Так у меша я сохраняю вертексы, трианглы, uv, и нормали в свой сериализуемый класс (имеет метку [Serializable], в котором эти же поля заданы сериализуемыми типами. Грубо говоря, получается куча разных массивов и переменных.
Я бы попробовал метод с таким названием... Surrogate, который позволяет одним объектом сериализовать другой, в том числе без метки [Serializable]. Но пока не очень представляю как мне это провернуть с таким "сложным" объектом как GameObject, в котором так же много полей (свойств) с несериализуемыми типам...
Всё это нужно мне больше в целях изучения просто. Я сделал 3д визуализатор небольшой, позволяющий загружать объекты obj формата и редактировать их немного...
Хотел добавить возможность сохранять именно в бинарнике эти объеты с изменениями всеми.
Меня просто несколько встревожило, что я из obj (текстовика фактически) загружаюсь быстрее гораздо, чем из бинарника))).
Последний раз редактировалось Roman777 13 сен 2017, 20:55, всего редактировалось 1 раз.
Roman777
UNIт
 
Сообщения: 95
Зарегистрирован: 06 мар 2016, 12:09

Re: Скорость дессериализации

Сообщение Tumbalalaika 13 сен 2017, 20:51

Вот небольшой пример сериализации/десериализации данных меша в бинарник.

Синтаксис:
Используется csharp
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using UnityEngine;

[System.Serializable]
class MeshWrapper
{
    public Vector3Wrapper[] Vertices;
    public int[] Triangles;
}

[System.Serializable]
class Vector3Wrapper
{
    float x, y, z;

    public static Vector3Wrapper Create(Vector3 source)
    {
        return new Vector3Wrapper()
        {
            x = source.x,
            y = source.y,
            z = source.z
        };
    }

    public Vector3 ToV3()
    {
        return new Vector3(x, y, z);
    }
}

public class MeshBinSerializer : MonoBehaviour
{
    public MeshFilter SourceObj;
    public MeshFilter TargetToDeserizlize;

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.S))
            Serialize(SourceObj.mesh, Application.dataPath + "/mesh_s.bin");

        if (Input.GetKeyDown(KeyCode.L))
            TargetToDeserizlize.mesh = Deserialize(Application.dataPath + "/mesh_s.bin");
    }

    private void Serialize(Mesh mesh, string path)
    {
        var verts = new List<Vector3Wrapper>();
        foreach (var v in mesh.vertices)
            verts.Add(Vector3Wrapper.Create(v));

        var wrapper = new MeshWrapper()
        {
            Vertices = verts.ToArray(),
            Triangles = mesh.GetIndices(0)
        };

        using (FileStream fs = new FileStream(path, FileMode.Create))
        {
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Serialize(fs, wrapper);
        }
    }

    private Mesh Deserialize(string path)
    {
        MeshWrapper wrapper = null;

        using (FileStream fs = new FileStream(path, FileMode.Open))
        {
            BinaryFormatter formatter = new BinaryFormatter();
            wrapper = (MeshWrapper)formatter.Deserialize(fs);
        }

        if (wrapper != null)
        {
            var verts = new List<Vector3>();
            foreach (var vw in wrapper.Vertices)
                verts.Add(vw.ToV3());

            Mesh mesh = new Mesh();
            mesh.SetVertices(verts);
            mesh.SetIndices(wrapper.Triangles, MeshTopology.Triangles, 0);
            mesh.RecalculateNormals();
            mesh.RecalculateTangents();
            return mesh;
        }

        return null;
    }
}
 


Вроде все нормально. В SourceObj кидаете ссылку на объет для сериализации (сфера например), в Target - обхект с MeshFilter и MeshRenderer, туда будет загружен сохраненный ранее объект. Разберитесь, может поможет. :)
Tumbalalaika
UNец
 
Сообщения: 17
Зарегистрирован: 12 сен 2017, 19:01

Re: Скорость дессериализации

Сообщение Roman777 13 сен 2017, 22:43

Благодарю! Примерно аналогичным способом и записывал данные. Правда преобразования из Vector3 делал не посредством конструктора, а функцией). Ваш код потестил на своём объекте. Если записывать помимо всего и нормали с uv координатами, выходит то же самое время...). Без нормалей и UV - в 3 раза быстрей (ну собственно и размер файла меняется, что и так понятно).
Интересный способ получить и назначить треугольники:
Синтаксис:
Используется csharp
Triangles = mesh.GetIndices(0)
mesh.SetIndices(wrapper.Triangles, MeshTopology.Triangles, 0);
Roman777
UNIт
 
Сообщения: 95
Зарегистрирован: 06 мар 2016, 12:09

Re: Скорость дессериализации

Сообщение Tumbalalaika 14 сен 2017, 15:55

Интересно. Попробуйте этим скриптом обработать Ваш меш.

Синтаксис:
Используется csharp
using System;
using System.Linq;
using System.Runtime.InteropServices;
using UnityEngine;

//
// Unity mesh serializer test
// written by Tumbalalaika @ unity3d.ru forums
//

public unsafe class FastMeshSerializer
{
    const int INT_SIZE = sizeof(int);
    const int HEADER_SIZE = 4 * INT_SIZE;

    public static Mesh Deserialize(byte[] bytes)
    {
        byte* pBytes;
        fixed (byte* b = &bytes[0]) pBytes = b;

        int vertex_block_size = *((int*)pBytes);
        int vertex_count = vertex_block_size / sizeof(Vector3);
        byte[] vertex_buf = new byte[vertex_block_size];

        int index_block_size = *(((int*)pBytes) + 1);
        int index_count = index_block_size / sizeof(int);
        byte[] index_buf = new byte[index_block_size];

        int normals_block_size = *(((int*)pBytes) + 2);
        int normals_count = normals_block_size / sizeof(Vector3);
        byte[] normals_buf = new byte[normals_block_size];

        int uvs_block_size = *(((int*)pBytes) + 3);
        int uv_count = uvs_block_size / sizeof(Vector2);
        byte[] uvs_buf = new byte[uvs_block_size];

        int vertices_offset = HEADER_SIZE;
        int indices_offset = HEADER_SIZE + vertex_block_size;
        int normals_offset = HEADER_SIZE + vertex_block_size + index_block_size;
        int uvs_offset = HEADER_SIZE + vertex_block_size + index_block_size + normals_block_size;

        Array.Copy(bytes, vertices_offset, vertex_buf, 0, vertex_block_size);
        Array.Copy(bytes, indices_offset, index_buf, 0, index_block_size);
        Array.Copy(bytes, normals_offset, normals_buf, 0, normals_block_size);

        Array.Copy(bytes, uvs_offset, uvs_buf, 0, uvs_block_size);

        Vector3[] vertices = new Vector3[vertex_count];
        int[] indices = new int[index_count];
        Vector3[] normals = new Vector3[normals_count];
        Vector2[] uvs = new Vector2[uv_count];

        Vector3* pVertices;
        fixed (Vector3* p = &vertices[0]) pVertices = p;

        int* pIndices;
        fixed (int* p = &indices[0]) pIndices = p;

        Vector3* pNormals;
        fixed (Vector3* p = &normals[0]) pNormals = p;

        Vector2* pUvs;
        fixed (Vector2* p = &uvs[0]) pUvs = p;

        Marshal.Copy(vertex_buf, 0, (IntPtr)pVertices, vertex_block_size);
        Marshal.Copy(index_buf, 0, (IntPtr)pIndices, index_block_size);
        Marshal.Copy(normals_buf, 0, (IntPtr)pNormals, normals_block_size);
        Marshal.Copy(uvs_buf, 0, (IntPtr)pUvs, uvs_block_size);

        // Build mesh

        Mesh mesh = new Mesh();
        mesh.vertices = vertices;
        mesh.SetIndices(indices, MeshTopology.Triangles, 0);
        mesh.SetNormals(normals.ToList());
        mesh.SetUVs(0, uvs.ToList());
        return mesh;
    }

    public static byte[] Serialize(Mesh mesh)
    {
        int vertex_block_size = sizeof(Vector3) * mesh.vertices.Length;
        int index_block_size = INT_SIZE * mesh.GetIndices(0).Length;
        int normals_block_size = sizeof(Vector3) * mesh.normals.Length;
        int uv_block_size = sizeof(Vector2) * mesh.uv.Length;

        byte[] data = new byte[vertex_block_size + index_block_size + normals_block_size + uv_block_size + HEADER_SIZE];

        Vector3* pVertices;
        Vector3* pNormals;
        int* pIndices;
        Vector2* pUvs;

        int[] indices = mesh.GetIndices(0);
        fixed (int* p = &indices[0]) pIndices = p;
        fixed (Vector3* p = &mesh.vertices[0]) pVertices = p;
        fixed (Vector3* p = &mesh.normals[0]) pNormals = p;
        fixed (Vector2* p = &mesh.uv[0]) pUvs = p;

        Marshal.Copy((IntPtr)(&vertex_block_size), data, 0, INT_SIZE);
        Marshal.Copy((IntPtr)(&index_block_size), data, INT_SIZE, INT_SIZE);
        Marshal.Copy((IntPtr)(&normals_block_size), data, INT_SIZE * 2, INT_SIZE);
        Marshal.Copy((IntPtr)(&uv_block_size), data, INT_SIZE * 3, INT_SIZE);

        int vertices_offset = HEADER_SIZE;
        int indices_offset = HEADER_SIZE + vertex_block_size;
        int normals_offset = HEADER_SIZE + vertex_block_size + index_block_size;
        int uvs_offset = HEADER_SIZE + vertex_block_size + index_block_size + normals_block_size;

        Marshal.Copy((IntPtr)pVertices, data, vertices_offset, vertex_block_size);
        Marshal.Copy((IntPtr)pIndices, data, indices_offset, index_block_size);
        Marshal.Copy((IntPtr)pNormals, data, normals_offset, normals_block_size);
        Marshal.Copy((IntPtr)pUvs, data, uvs_offset, uv_block_size);

        return data;
    }
}
 



Требует начичия uv1

Собственно дергайте Serrialize и Deserialize. Можно как-то так:

var watch = System.Diagnostics.Stopwatch.StartNew();
Obj.GetComponent<MeshFilter>().mesh = Deserialize(Serialize(YOUR_MESH));
watch.Stop();
Debug.Log(watch.ElapsedMilliseconds);


Выведет в консоль общее время выполнения в мс.
Tumbalalaika
UNец
 
Сообщения: 17
Зарегистрирован: 12 сен 2017, 19:01

Re: Скорость дессериализации

Сообщение SuKioto 14 сен 2017, 19:23

зачем обертка для вектор3
SuKioto
UNIт
 
Сообщения: 86
Зарегистрирован: 21 июн 2016, 19:48

Re: Скорость дессериализации

Сообщение Roman777 14 сен 2017, 19:36

Ого!!! Огонь! Запись и чтение порядка 20 миллисекунд. Просто отлично! Спасибо большое! Если не секрет, откуда такой код? Я и не знал, что С# работает с указателями ))).
Интересно, что и бинарник из 5487 (при стандартной сериализации) превратился в 2809 (при этой писанной сериализации) кбайт))).
Последний раз редактировалось Roman777 14 сен 2017, 20:01, всего редактировалось 2 раз(а).
Roman777
UNIт
 
Сообщения: 95
Зарегистрирован: 06 мар 2016, 12:09

Re: Скорость дессериализации

Сообщение Roman777 14 сен 2017, 19:38

SuKioto писал(а):зачем обертка для вектор3

Разве стандартый Vector3 сериализуется?
Roman777
UNIт
 
Сообщения: 95
Зарегистрирован: 06 мар 2016, 12:09

Re: Скорость дессериализации

Сообщение SuKioto 14 сен 2017, 19:43

а нет забейте извиняюсь
SuKioto
UNIт
 
Сообщения: 86
Зарегистрирован: 21 июн 2016, 19:48

Re: Скорость дессериализации

Сообщение Tumbalalaika 14 сен 2017, 19:46

Код чисто для теста, написан "на коленке", я бы не стал использовать как есть. В качестве небольшой доки по ансейфу (поинтерам в C#) как раз пойдет, от этого уже сами оттолкнетесь.

Интересно что и бинарник из 5487 (при стандартной сериализации) превратился в 2809 (при этой писанной сериализации) кбайт))).

Ага, вы откройте HEX радактором каким-нибудь выхлоп дотнетовкого BinaryFormatter'a, он там кучу доп инфы пишет. Этот скрипт формирует такой бинарник:

int32 vertex block length
int32 index bloc klength
int32 normal block length
int32 uv block length
float array (vector3 - 12 bytes) [vertex block]
int32 array [indices block]
float array (vector3 - 12 bytes) [normal block]
float array (vector2 - 8 bytes) [uv block]

т.е. ничего лишнего, разве что еще можно выиграть на типах, если например те же индексы писать 16битным интом. Хотя думаю это особо никчему :)
Последний раз редактировалось Tumbalalaika 14 сен 2017, 20:06, всего редактировалось 3 раз(а).
Tumbalalaika
UNец
 
Сообщения: 17
Зарегистрирован: 12 сен 2017, 19:01

Re: Скорость дессериализации

Сообщение Roman777 14 сен 2017, 19:57

Благодарю) мнеб такое "на коленке" писать )))). Тоже думал уже своё что-то писать, но точно бы не додумался указатели использовать (ибо о них даже и не знал). Думаю, только, с моими познаниями растянулось бы на недельку-другую. А тут прям кладезь для дальнейшей работы, Спасибо!
Roman777
UNIт
 
Сообщения: 95
Зарегистрирован: 06 мар 2016, 12:09

Re: Скорость дессериализации

Сообщение Tumbalalaika 14 сен 2017, 19:58

Да не за что) я в общем то тоже впервые ансейф в шарпе потрогал, до этого только знал что он есть.
Tumbalalaika
UNец
 
Сообщения: 17
Зарегистрирован: 12 сен 2017, 19:01

След.

Вернуться в Общие вопросы

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 11