Извините, ничего не найдено.

Не расстраивайся! Лучше выпей чайку!
Регистрация
Справка
Календарь

Вернуться   www.boolean.name > Программирование игр для компьютеров > C++

Ответ
 
Опции темы
Старый 08.06.2012, 00:54   #1
jimon
 
Сообщений: n/a
Эффективная и быстрая загрузка текстур в OpenGL

Жили себе поживали на пк и проблем с памятью не знали, но тут приходит тренд мобильных игр и всё говно всплыло

Наша история сегодня о функции glTexImage2D (и glCompressedTexImage2D тоже), алгоритм работы с ней прост и понятен, казалось бы :

1) открываем файл с помощью libpng, libjpeg, или еще чего-то, есть способы как с полной предварительной загрузкой файла в память, так и с переопределением функций чтения файла для библиотеки загрузки
2) получаем указатель на данные текстуры в памяти
3) кидаем указатель в glTexImage2D
4) освобождаем данные по указателю
5) PROFIT !

А теперь давайте подумаем об очевидных вещах, все мы отдаём себе отчёт что у нас тут есть промежуточный буфер для целого изображения, когда оно 2048*2048*rgba то это 16 мегабайт, и время жизни этого буфера весьма малое, от загрузки из файла до загрузки в opengl, буквально 100-200 мс

Ладно если это на пк то ничего сильно плохого такой подход не вносит кроме фрагментации и небольшой потери скорости, но на мобильниках (ios и android, особенно последнее) часто бывает ситуация, когда у нас на всю игру 50-70 мегабайт и места для буфера на 16 метров просто нет ! (в случае превышения на ios ваше приложение просто грохнут из-за отсутствия swap файла)

Давайте глянем нагляднее : (скриншот из профайлера под ios)
Нажмите на изображение для увеличения
Название: mem1.png
Просмотров: 1794
Размер:	232.5 Кб
ID:	17003

В пункте 1 происходит инициализация приложения, движка, игры и прочей мелочи. В пункте 2 начинается загрузка текстур (зачастую самые большие ресурсы в мобильных играх), как видим количество занимаемой памяти резко подскочило (создавались временные буфера для изображений), конкретно в этом тесте грузились 5 изображений 1024*1024 формата tga на ipad 3. В пункте 3 уже начала работать игра, как видим количество выделенной памяти резко поехало вниз.

А теперь как решать всё это, как мы знаем с появлением защищенного режима (i386 привет), мы перешли от сегментной адресации памяти к страничной памяти, страничная память хороша тем что есть page fault, а как мы знаем page fault происходит когда странички то в памяти нету, а лежит она где-то в недоступном месте. Хороший, приятный, быстро работающий в ring0 механизм. А теперь прикинемся шлангом, и скажем системе чтобы страницей был файл, да, физический файл может быть страницей виртуальной памяти в выгруженном состоянии, в чём разница то ? По-сути для приложения это выглядит как указатель, мы же не знаем что на деле за тем указателем лежит, а ring0 уже сам обрабатывает page fault и грузит при этом файл с диска тоже сам. Всё это называется memory mapped file.

Конечно мы не изобрели это, мы всего лишь будем использовать такой механизм чтобы надурить OpenGL, ведь glTexImage2D принимает указатель, значит всё что представляется как указатель должно подойти за хранилище данных. Только вот придется на диске хранить изображения без сжатия (или в аппаратном сжатии : dxt, pvrtc, и тд), но в последних тенденциях App Store подняли максимальный размер приложения доступного по 3G связи с 20 до 50 мбайт, так что овчина стоит выделки, особенно на андроидах где другого выхода нет.

Выглядит в коде это так : (даю сразу заготовку для win\posix решений)
#if defined(WIN)

#include <windows.h>

typedef struct
{
	HANDLE f;
	HANDLE m;
	void * p;
} SIMPLE_UNMMAP;

void * simple_mmap(const char * filename, int * length, SIMPLE_UNMMAP * un)
{
	HANDLE f = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ,  NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	HANDLE m;
	void *p;
	if(!f) return NULL;
	m = CreateFileMapping(f, NULL, PAGE_READONLY, 0,0, NULL);
	if(!m) { CloseHandle(f); return NULL; }
	p = MapViewOfFile(m, FILE_MAP_READ, 0,0,0);
	if(!p) { CloseHandle(m); CloseHandle(f); return NULL; }
	if(length) *length = GetFileSize(f, NULL);
	if(un)
	{
		un->f = f;
		un->m = m;
		un->p = p;
	}
	return p;
}

void simple_unmmap(SIMPLE_UNMMAP * un)
{
	UnmapViewOfFile(un->p);
	CloseHandle(un->m);
	CloseHandle(un->f);
}

#else

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>

typedef struct
{
	int fd;
	int size;
	void * p;
} SIMPLE_UNMMAP;

void * simple_mmap(const char * filename, int * length, SIMPLE_UNMMAP * un)
{
	int fd = open(filename, O_RDONLY);
	
	if(fd < 0)
		return NULL;
	
	fcntl(fd, F_NOCACHE, 1); // не кешируем мы наше чтение текстур, потому что читаем линейно один раз то
	fcntl(fd, F_RDAHEAD, 1); // заставляем читать на перёд

	struct stat statbuf;
	
	if(fstat(fd, &statbuf) < 0)
		return NULL;
	
	void * p = mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
	
	if(length)
		*length = statbuf.st_size;
	
	if(un)
	{
		un->fd = fd;
		un->size = statbuf.st_size;
		un->p = p;
	}

	return p;
}

void simple_unmmap(SIMPLE_UNMMAP * un)
{
	munmap(un->p, un->size);
	close(un->fd);
}

#endif

..........................

SIMPLE_UNMMAP mmap;
int mmapLength;

simple_mmap("image.raw", &mmapLength, &mmap);

// тут вам надо самим узнать размер и формат вашего изображения, для этого у меня есть небольшой заголовок в начале моего файла

glTexImage2D(.........., mmap.p);

simple_unmmap(&mmap);
Как видим реализация в общем проста и банальна, кроме разве что некоторых флагов для POSIX.

Еще Кармак писал http://www.bethblog.com/2010/10/29/j...padipod-touch/ что F_NOCACHE под ios работает как надо, в итоге должно обеспечить максимальную производительность линейного одноразового чтения.

В итоге результат можно видеть на картинке :
Нажмите на изображение для увеличения
Название: mem2.png
Просмотров: 1445
Размер:	227.0 Кб
ID:	17004

Как видим мы избавились от критичного места - пика использования памяти при загрузке, но как плюс получили увеличении скорости загрузки по сравнению с обычным способом ! Увеличение в среднем на 20-50%, точные тесты не проводил.

ps. Геймдев это сфера разработки realtime софта, так что давайте и подходить к нему как в realtime сфере - у нас не игра, а программно-апаратный комплекс, у узлов есть свои особенности и ими нужно пользоваться и задавать вопросы, вплоть до "Почему трансатлантический пинг быстрее, чем вывод пиксела на экран"
 
Ответить с цитированием
Эти 10 пользователя(ей) сказали Спасибо за это полезное сообщение:
ABTOMAT (08.06.2012), Dzirt (06.07.2012), HolyDel (08.06.2012), Помеха (09.07.2012), is.SarCasm (07.07.2012), Mr_F_ (08.06.2012), Nerd (06.07.2012), pax (08.06.2012), SBJoker (08.06.2012), St_AnGer (06.07.2012)
Старый 08.06.2012, 01:13   #2
ffinder
Дэвелопер
 
Аватар для ffinder
 
Регистрация: 10.09.2007
Сообщений: 1,442
Написано 793 полезных сообщений
(для 1,460 пользователей)
Ответ: Эффективная и быстрая загрузка текстур в OpenGL

Сообщение от jimon Посмотреть сообщение
ps. Геймдев это сфера разработки realtime софта, так что давайте и подходить к нему как в realtime сфере - у нас не игра, а программно-апаратный комплекс
не гони, братюнь, до рилтайма играм как до Луны пешком.
в рилтайме статическое выделение памяти, а если есть многозадачность, то проц не должен использоваться больше чем на 70% (иначе алгоритм не может гарантировать ответ вовремя).
как-то около того.

по теме: если на мобильниках - почему сразу не грузить .pvrtc файл, не раскукоживая его?
(Offline)
 
Ответить с цитированием
Старый 08.06.2012, 01:17   #3
jimon
 
Сообщений: n/a
Ответ: Эффективная и быстрая загрузка текстур в OpenGL

не гони, братюнь, до рилтайма играм как до Луны пешком.
читаем soft realtime : "The usefulness of a result degrades after its deadline, thereby degrading the system's quality of service.", играм надо обеспечивать какой-то фпс, так что самый что ни на есть реалтайм

по теме: если на мобильниках - почему сразу не грузить .pvrtc файл, не раскукоживая его?
а скармливать в OpenGL ты его как будешь ? тебе по любому нужно его прочитать в кусок памяти, статья о том загружать текстуру без чтения в память.
 
Ответить с цитированием
Старый 06.07.2012, 15:26   #4
HolyDel
 
Регистрация: 26.09.2006
Сообщений: 6,035
Написано 1,474 полезных сообщений
(для 2,706 пользователей)
Ответ: Эффективная и быстрая загрузка текстур в OpenGL

спасибо. позаимствовал себе реализацию ))
на ПК дало прирост скорости где то на 15%-20%
(Offline)
 
Ответить с цитированием
Эти 2 пользователя(ей) сказали Спасибо HolyDel за это полезное сообщение:
Dzirt (06.07.2012), is.SarCasm (07.07.2012)
Ответ


Опции темы

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы не можете редактировать сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.


Часовой пояс GMT +4, время: 07:11.


vBulletin® Version 3.6.5.
Copyright ©2000 - 2022, Jelsoft Enterprises Ltd.
Перевод: zCarot
Style crйe par Allan - vBulletin-Ressources.com