Модераторы: Rickert
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Загрузка объекта в отдельном потоке, Новый поток лезет в вид.память старого 
V
    Опции темы
EgoBrain
Дата 28.2.2009, 05:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 537
Регистрация: 23.3.2008
Где: Комната

Репутация: 1
Всего: 2



Проблема такая: хочу загружать каждый новый объект сцены (объекты динамически добавляются в сцену пользователем) в отдельном потоке (пока тяжелый объект загружается пользователь продолжает работу: вращает сцену и т.п., а на месте загружаемого объекта мигает надпись "загрузка"), и вот казалось бы идея реализована, но возникает исключение когда при загрузке объекта я пытаюсь тут же загружать еще один - 
Код

System.AccessViolationException was unhandled
  Message="Attempted to read or write protected memory. This is often an indication that other memory is corrupt."
  Source="Microsoft.DirectX.Direct3DX"
  StackTrace:
       at D3DXLoadMeshFromXA(SByte* , UInt32 , IDirect3DDevice9* , ID3DXBuffer** , ID3DXBuffer** , ID3DXBuffer** , UInt32* , ID3DXMesh** )
       at Microsoft.DirectX.Direct3D.Mesh.FromFile(String filename, MeshFlags options, Device device, GraphicsStream& adjacency, ExtendedMaterial[]& materials, EffectInstance[]& effects)

Как я понял новый объект загрузки (получается второй побочный поток) пытается записаться в в память выделенную для старого объекта загрузки (первый побочный поток) или просто вся видео память в данный момент заблочена. Какие будут коментарии, может есть легкий способ решения (например какой-то особый флаг загрузки объекта "MeshFlags" нужен)? И вообще какие флаги устройства и мех-объекта лучше использовать для многопоточного приложения?

З.Ы. Есть у когонить хелп по DX на русском, посмотрел так еще некоторые фалги и куча дополнительных вопросов появилась: например - класс Mesh сам создает для объекта вершинный буфер, а вот индексный создает, если нет то как сделать чтоб создавал?

Это сообщение отредактировал(а) EgoBrain - 28.2.2009, 06:09
PM MAIL ICQ Skype   Вверх
arilou
Дата 2.3.2009, 02:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Великий МунаБудвин
****


Профиль
Группа: Экс. модератор
Сообщений: 2646
Регистрация: 15.7.2004
Где: город-герой Минск

Репутация: 6
Всего: 61



EgoBrain, а ты не пробовал просто разделить понятия? Вот что-то в таком духе (полупсевдокод):

Код

public delegate void LoadCompleteCallback(Model loadedModel);

class AsyncModelLoader
{

    public Model BeginLoad(string pathToModel, LoadCompleteCallback completionCallback)
    {
        _Enqueue(pathToModel, completionCallback);
        return LoadingPleaseWaitModel.Clone(); // это св-во, возвращающее инстанс модели с текстом "Пжлста ждите"
    }

    void _Enqueue(string pathToModel, LoadCompleteCallback completiobCallback)
    {
        // вот тут ты асинхронно грузишь модель
        // и когда она загрузилась, вызываешь делегат completionCallback
        // передавая ему ссылку на вновь загруженную модель
    }    
}

class App
{
    ...
    void LoadNewModel()
    {
        ...
        LoadCompleteCallback loadComplete = new LoadCompleteCallback(delegate(Model loadedModel)
        {
            scene.Replace(pathToModel, loadedModel);
        });

        AsyncModelLoader loader = new AsyncModelLoader();
        scene.Add(pathToModel, loader.BeginLoad(pathToModel, loadComplete));

        // что тут получится: метод BeginLoad поставит в очередь на загрузку новую модель, и сразу вернет ссылку на 
        // модель "Loading please wait". Когда загрузка модели завершится, вызовется анонимный метод по ссылке loadComplete, 
        // и заменит в сцене временную модель на загруженную. Естественно, scene.Add и scene.Replace - это псевдокод, 
        // зависящий от твоей реализации


Есть понимание?  smile

Добавлено через 3 минуты и 23 секунды
Важное замечание: для загрузки объектов нужно не каждый раз запускать новый поток, а иметь спец. менеджер ресурсов, который внутри себя инкапсулирует 1 поток и очередь загрузки. 


--------------------
user posted imageuser posted image
PM WWW ICQ   Вверх
EgoBrain
Дата 2.3.2009, 03:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 537
Регистрация: 23.3.2008
Где: Комната

Репутация: 1
Всего: 2



Моя проблема заключается в том, что я еще не умею толком с многопоточностью работать и с делегатами (чтиво есть, а вот за конкретное изучение взяться руки не доходят). Пока остановился на таком варианте: перед загрузкой модели проверяется жив ли поток, который такое дело должен выполнять, если жив то весь процесс его ждет - это уже конечно криво, но пока моих знаний хватило только на это, если не жив то грузим...
PM MAIL ICQ Skype   Вверх
arilou
Дата 2.3.2009, 15:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Великий МунаБудвин
****


Профиль
Группа: Экс. модератор
Сообщений: 2646
Регистрация: 15.7.2004
Где: город-герой Минск

Репутация: 6
Всего: 61



EgoBrain, я набросал для тебя пример.

Сначала идет код для асинхронной загрузки

Код

    public class Model 
    {
        public string ModelName { get; set; }

        public Model Clone()
        {
            // корректная реализация клонирования
            return new Model();
        }
    }

    /// <summary>
    /// Делегат описывающий событие завершения загрузки
    /// </summary>
    /// <param name="model"></param>
    public delegate void LoadCompleteCallback(Model model);

    
    /// <summary>
    /// Класс, описывающий одну загрузку
    /// </summary>
    class LoadTask
    {
        /// <summary>
        /// Ссылка на загруженную модель
        /// </summary>
        public Model Model { get; set; }
        /// <summary>
        /// Путь к файлу модели
        /// </summary>
        public string FileName { get; set; }
        /// <summary>
        /// Ссылка на метод, который вызывается по окончании загрузки модели
        /// </summary>
        public LoadCompleteCallback CompleteCallback { get; set; }
    }

    /// <summary>
    /// Асинхронный загрузчик моделей
    /// </summary>
    public class AsyncLoader
    {
        Thread _thread;
        Queue<LoadTask> _queue = new Queue<LoadTask>();

        /// <summary>
        /// Protected конструктор, т.к. класс является Singleton
        /// </summary>
        protected AsyncLoader()
        {
            _thread = new Thread(_ThreadMethod);
            _thread.IsBackground = true;
            _thread.Start();
        }

        static AsyncLoader _inst = null;
        /// <summary>
        /// Ссылка на единственный инстанс
        /// </summary>
        public static AsyncLoader Instance
        {
            get
            {
                if (_inst == null)
                    _inst = new AsyncLoader();

                return _inst;
            }
        }

        Model _loadingPleaseWait;

        Model _GetLoadingPleaseWaitModel(string modelName)
        {
            if (_loadingPleaseWait == null)
                _loadingPleaseWait = new Model();

            Model newModel = _loadingPleaseWait.Clone();
            newModel.ModelName = "Loading Model " + modelName;

            return newModel;
        }

        /// <summary>
        /// Асинхронно загружает модель
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="callback"></param>
        /// <returns></returns>
        public Model BeginLoad(string fileName, LoadCompleteCallback callback)
        {
            lock (_queue) 
                // блокируем доступ к _queue, чтобы избежать одновременного доступа 
                // к нему из разных потоков
            {
                // добавляем в очередь новый таск на загрузку модели
                _queue.Enqueue(new LoadTask() { FileName = fileName, CompleteCallback = callback });
            }

            // возвращаем заглушку "please wait"
            return _GetLoadingPleaseWaitModel(fileName);
        }

        Model _DoLoad(string fileName)
        {
            // тут выполняется код по загрузке модели
            Model model = new Model() { ModelName = fileName };

            return model;
        }

        /// <summary>
        /// Метод, работающий в отдельном потоке и обрабатывающий очередь загрузки моделей
        /// </summary>
        void _ThreadMethod()
        {
            while (true)
            {
                if (_queue.Count > 0)
                {
                    LoadTask task = null;
                    
                    lock (_queue)
                        // блокируем доступ к _queue, чтобы избежать одновременного доступа 
                        // к нему из разных потоков
                        // т.к. этот код выполняется в параллельном потоке, может произойти одновременное
                        // добавление к очереди из метода BeginLoad
                    {
                        task = _queue.Dequeue();
                    }

                    // вызываем собственно код загрузки модели
                    task.Model = _DoLoad(task.FileName);

                    // вызываем коллбэк, чтобы уведобить основной поток о завершении 
                    // загрузки
                    if (task.CompleteCallback != null)
                        task.CompleteCallback(task.Model);
                }
                else
                    // это коряво очень, но для примера подойдет.
                    // если нет задач на загрузку, говорим потоку "Спать", чтобы не загружать
                    // процессор пустой работой.
                    // правильный вариант -- использовать ManualResetEvent для сигнализции потоку
                    // о появлении нового файла на загрузку
                    Thread.Sleep(300); 
            }
        }
    }


А это пример использования. Скопируй весь код в Console project и посмотри, что оно тебе будет выводить в консоль.

Код

    static class Program
    {
        static void Main()
        {
            Dictionary<string, Model> loadedModels = new Dictionary<string, Model>();

            // для простоты примера используем анонимный метод
            LoadCompleteCallback callback = new LoadCompleteCallback(delegate(Model model)
                {
                    Console.WriteLine("Модель " + model.ModelName + " загружена");
                    lock (loadedModels)
                    {
                        // заменяем заглушку модели на ее загруженную копию
                        loadedModels[model.ModelName] = model;
                    }
                });

            for (int i = 0; i < 100; i++)
            {
                string modelName = "model" + i.ToString() + ".mdl";
                
                lock (loadedModels)
                {
                    // кладем в словарь модель-заглушку
                    // она будет заменена, когда вызовется callback-метод
                    loadedModels.Add(modelName, AsyncLoader.Instance.BeginLoad(modelName, callback));
                }
            }

            Console.ReadLine();
        }
    }


В примере используются два неоптимальных подхода, но достаточных для демонстрации.

1) lock - это "тяжелая" операция, и не очень подходит для приложений, в которых важно соблюдать константный фреймрейт. большое кол-во локов на кадр заметно снизит FPS
2) в потоке, выполняеющем загрузку, если в очереди ничего нет, используется Thread.Sleep(300). это неоптимально, лучше использовать ManualResetEvent для сигнализации потоку о появлении новых задач на загрузку.

Задавай вопросы, помогу разобраться.


--------------------
user posted imageuser posted image
PM WWW ICQ   Вверх
EgoBrain
Дата 3.3.2009, 03:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 537
Регистрация: 23.3.2008
Где: Комната

Репутация: 1
Всего: 2



Сразу вывело 200 моделей загружено. Так как должно получится на практике: нажимаю сколько хочу и появляется множество надписей "загрузка", или надписи появляются только после того, как предыдущая модель уже загружена? За помощь спасибо, и пример такой подробный, но пока что труден для восприятия.

Это сообщение отредактировал(а) EgoBrain - 3.3.2009, 03:20
PM MAIL ICQ Skype   Вверх
arilou
Дата 3.3.2009, 19:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Великий МунаБудвин
****


Профиль
Группа: Экс. модератор
Сообщений: 2646
Регистрация: 15.7.2004
Где: город-герой Минск

Репутация: 6
Всего: 61



Цитата(EgoBrain @  3.3.2009,  03:19 Найти цитируемый пост)
Сразу вывело 200 моделей загружено. 


А ты попробуй в _DoLoad вставить Thread.Sleep(1000)  smile как имитацию загрузки.

Цитата(EgoBrain @  3.3.2009,  03:19 Найти цитируемый пост)
Так как должно получится на практике: нажимаю сколько хочу и появляется множество надписей "загрузка"

Ага, в теории так должно получиться. Потому что внутри AsyncLoader клон меша "Loading please wait" возвращается сразу, а загрузка ставится в очередь. 

Цитата(EgoBrain @  3.3.2009,  03:19 Найти цитируемый пост)
пример такой подробный, но пока что труден для восприятия.

Так задавай вопросы, где именно трудности?


--------------------
user posted imageuser posted image
PM WWW ICQ   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Программирование игр, графики и искуственного интеллекта"
Rickert

НА ЗЛОБУ ДНЯ: Дорогие посетители, прошу обратить внимание что новые темы касающиеся новых вопросов создаются кнопкой "Новая тема" а не "Ответить"! Любые оффтопиковые вопросы, заданные в текущих тематических темах будут удалены а их авторы, при рецедиве, забанены.

  • Литературу, связанную с программированием графики, обсуждаем здесь
  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы связанные с программированием графики и мультимедии на языках С++ и Delphi
  • Вопросы по реализации алгоритмов рассматриваются здесь

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Rickert.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Программирование игр, графики и искусственного интеллекта | Следующая тема »


 




[ Время генерации скрипта: 0.0940 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.