Russian Qt Forum
Ноябрь 23, 2024, 17:08 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2 3 ... 5   Вниз
  Печать  
Автор Тема: Пример проектирования  (Прочитано 25285 раз)
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« : Декабрь 16, 2010, 16:13 »

Добрый день

Заказчик попросил улучшить качество UI/preview (изначально писано не мной). Работая над задачей, подумалось: вот хороший пример "живого" проектирования классов и их взаимодействия. С одной стороны - не слишком сложно, классов немного и они понятны. Не требуется знания предметной области. С др. стороны - решить проблемы не так уж просто (по крайней мере для меня).

Краткое описание
Есть N окон (обычно немного, до 10) в которых рисуются 3D объекты. Любое окно может содержать любое кол-во объектов. Есть 2 способа отображения - с использованием OpenGL и без, пользователь может выбрать один из них для каждого окна индивидуально. Объекты могут отображаться текстурированными, для каждого объекта текстура назначается индивидуально. В любой момент пользователь может открыть/закрыть любое из окон, добавить/удалить объекты, изменить текстуру любого объекта. 

Принципиальные классы
Object3D - объект который рисуем

Texture - указатель на Image + файл имеджа + набор параметров наложения (фильтр, сдвиг, поворот, способ наложения и др)

Image - просто картинка загруженная в память

DrawEngine3D - базовый класс для рисования объектов, с текстурой или без. Каждое окно имеет свою DrawEngine

DrawEngine_Soft - порожден от DrawEngine3D, умеет рисовать без OpenGL
DrawEngine_Hard - то же но с OpenGL

GL_TextureID - ID имеджа загруженного в OpenGL. Этот ресурс должен быть создан для DrawEngine_Hard (используя пиксели класса Image) перед началом рисования и освобожден когда необходимость в отрисовке данного имеджа отпала

-------- Требуется ----------

Оптимизировать использование ресурсов (классы Image и GL_TextureID). Примеры: многие текстуры используют один и тот же файл картинки, значит она должна быть загружена 1 раз и выгружена когда никто больше ее не использует. Также и OpenGL текстура должна создаваться 1 раз (а не для каждого окна и не для каждой текстуры) и освобождаться автоматычно.

-----------------

Охотно послушаю тех кто любит (и умеет) проектировать - ну или по крайней мере прочитал много толстых книг  Улыбающийся

Спасибо
Записан
BRE
Гость
« Ответ #1 : Декабрь 16, 2010, 16:33 »

boost::shared_ptr
QSharedDataPointer+QSharedData
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #2 : Декабрь 16, 2010, 17:03 »

boost::shared_ptr
QSharedDataPointer+QSharedData
Ну что нужно "шерить" - это всем понятно. Но вот кто кого и как "шерит" ?
Записан
BRE
Гость
« Ответ #3 : Декабрь 16, 2010, 17:10 »

Ну что нужно "шерить" - это всем понятно. Но вот кто кого и как "шерит" ?
Код
C++ (Qt)
class ImageData : public QSharedData // хранит сами данные (приватный класс)
{
...
};
 
class Image // хранит указатель на данные (публичный класс)
{
public:
...
private:
QSharedDataPointer<ImageData> d;
};
 

Все же описано в документации. Или я не вижу в чем проблема?
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #4 : Декабрь 16, 2010, 17:39 »

Все же описано в документации. Или я не вижу в чем проблема?
Кстати, я дал такое же имя ImageData  Улыбающийся
А проблема в том кто (и как) должен "владеть". Напр. пусть это класс Texture - не годится, т.к. мы не знаем захочет ли хоть одна DrawEngine это рисовать. А сама DrawEngine еще меньше подходит - ведь 100 Texture(s) могут использовать 1 Image

Вопрос по взаимодействию классов, техника shared не проблема
Записан
BRE
Гость
« Ответ #5 : Декабрь 16, 2010, 17:53 »

Код
C++ (Qt)
{
Image i; // Создался объект Image, внутри создался объект Image Data
// Владеет данными объект i
}
// Вышли из области видимости, объект i разрушился, он использовал данными один - данные разрушились.
 

Код
C++ (Qt)
{
Image i;
{
Image i2;
i2.load( ... );
i = i2; // i и i2 указывают на одни и те-же данные, но объекта два
}
// Объект i2 разрушился, но данные остались, т.к. i на них ссылается.
}
 

Владельцем данными картинки (ImageData) является сам объект картинки (Image). Если этот объект разрушается и он монопольно этими данными пользуется, то разрушаются и данные.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #6 : Декабрь 16, 2010, 18:01 »

Код
C++ (Qt)
// Вышли из области видимости, объект i разрушился, он использовал данными один - данные разрушились.
 
Ресурсы должны быть загружены если они нужны для рисования хотя бы в 1 открытом окне. Напр. если пользователь вращает модель, то будет дороговато грузить картинку с диска и распаковывать ее на каждый move мыши.
Записан
BRE
Гость
« Ответ #7 : Декабрь 16, 2010, 18:20 »

Сообразил, тут вопрос в реализации менеджера ресурсов.
На примере картинок... Все картинки грузятся методом специального объекта - менеджера картинок.
Этот метод проверяет, если такая картинка не загружена, то создается новый объект Image, в него загружается картинка, помещается в кэш и возвращается объект Image. Если же такой объект уже есть в кэше, то возвращается его копия.
Код
C++ (Qt)
class ImageManager
{
public:
Image load( const QString &name );
 
private:
QMap<QString, Image> m_cache;
};
 

Владельцами данных по прежнему остаются сами объекты Image. Только один из таких объектов будет храниться в кэше, соответственно для окончательного разрушения данных его объект Image должен быть выброшен из кэша.
Но даже если очистить кэш картинок, то все картинки используемые в других местах останутся существовать.

Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #8 : Декабрь 16, 2010, 18:53 »

На примере картинок... Все картинки грузятся методом специального объекта - менеджера картинок.
Это хороший подход, но по существу это "кэш", и значит надо как-то контролировать число помещаемых картинок, да и общий размер данных. То есть мы избавляемся от одних забот но получаем другие.

Хотя кэш выглядит значительно проще для реализации, мне кажется здесь нет оснований его применять - ведь имеются/определены все данные о том что должно быть загружено и что нет.
Записан
BRE
Гость
« Ответ #9 : Декабрь 16, 2010, 19:40 »

Хотя кэш выглядит значительно проще для реализации, мне кажется здесь нет оснований его применять - ведь имеются/определены все данные о том что должно быть загружено и что нет.
Как-то опять непонятно.  Улыбающийся
Какие нужны основания? Для централизованного управления ресурсами нужны эти самые средства. В том или ином виде. Если ничего не контролировать, то и проконтролировать ничего не получиться.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #10 : Декабрь 16, 2010, 19:54 »

Как-то опять непонятно.  Улыбающийся
Какие нужны основания? Для централизованного управления ресурсами нужны эти самые средства. В том или ином виде. Если ничего не контролировать, то и проконтролировать ничего не получиться.
Я хочу иметь загруженными только те ресурсы что нужны для рисования. Число ссылок стало = 0, освобождаем ресурс. Кэш не совсем то. Напр. картинка может сидеть в кэше хотя ее уже никто не пользует. Или наоборот, емкость кэша недостаточна - и тогда при рисовании придется грузить (апельсины бочками). Кэш очень хорош если данные идут потоком и могут повторяться. Но это не мой случай.
Записан
BRE
Гость
« Ответ #11 : Декабрь 16, 2010, 20:10 »

Без централизованного получения/создания ресурсов все равно не обойтись.
А для кэше можно попробовать использовать "слабые указатели". Тогда в кэше не будет объекта удерживающего данные и они будут разрушаться после разрушения последнего владельца.
Кэш подчищать придется только от уже невалидных указателей.
Записан
ufna
Гость
« Ответ #12 : Декабрь 16, 2010, 20:29 »

+1 к BRE

Все задачи, описанные в сабже, решаются за счет ввода менеджера ресурсов. Это очень важная часть движка.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #13 : Декабрь 16, 2010, 20:47 »

А для кэше можно попробовать использовать "слабые указатели". Тогда в кэше не будет объекта удерживающего данные и они будут разрушаться после разрушения последнего владельца.
Это мне и нужно. Но кто и как должен удерживать (или не удерживать) данные? (см. пост #4)
Где взять "владельца"?

Все задачи, описанные в сабже, решаются за счет ввода менеджера ресурсов. Это очень важная часть движка.
Так давайте введем чудесный менеджер ресурсов  Улыбающийся  Расскажите мне что он должен делать, а я уж как-нибудь реализую.
Записан
BRE
Гость
« Ответ #14 : Декабрь 16, 2010, 21:06 »

Набросал немного кода, посмотри (компилябельно):
Код
C++ (Qt)
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <string>
#include <map>
#include <iostream>
 
 
class Resource
{
public:
Resource() {}
Resource( const std::string &name ) : m_data( name ) {}
std::string m_data;
};
 
typedef boost::shared_ptr<Resource> ResourcePtr;
 
class ResourceManager
{
public:
ResourcePtr load( const std::string &name );
 
void clearCache();
void dumpCache() const;
 
private:
typedef boost::weak_ptr<Resource> SResourcePtr;
typedef std::map<std::string, SResourcePtr> MapCache;
MapCache m_cache;
};
 
ResourcePtr ResourceManager::load( const std::string &name )
{
MapCache::iterator it = m_cache.find( name );
if( it != m_cache.end() )
{
ResourcePtr o = it->second.lock();
if( o )
return o;
else
m_cache.erase( it );
}
 
ResourcePtr r( new Resource( name ) );
m_cache[ name ] = SResourcePtr( r );
return r;
}
 
void ResourceManager::clearCache()
{
MapCache::iterator it;
for( it = m_cache.begin(); it != m_cache.end(); ++it )
{
if( !it->second.lock() )
m_cache.erase( it );
}
}
 
 
void ResourceManager::dumpCache() const
{
std::cout << "Cache num: " << m_cache.size() << std::endl;
MapCache::const_iterator it;
for( it = m_cache.begin(); it != m_cache.end(); ++it )
{
ResourcePtr o = it->second.lock();
std::cout << "key: " << it->first << " -> " << ( ( o )? o->m_data : "unload" ) << std::endl;
}
}
 
 
int main( int argc, char **argv )
{
ResourceManager man;
 
{
ResourcePtr o1 = man.load( "AAA" );
ResourcePtr o2 = man.load( "BBB" );
ResourcePtr o3 = man.load( "BBB" );
 
std::cout << o1->m_data << " " << o2->m_data << " " << o3->m_data << " " << std::endl;
}
 
ResourcePtr o4 = man.load( "DDD" );
man.dumpCache();
man.clearCache();
man.dumpCache();
 
   return 0;
}
 
Записан
Страниц: [1] 2 3 ... 5   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.224 секунд. Запросов: 23.