Визуальная демонстрация производительности вычислительного кластера в реальном времени
(на примере Parsytec CC/nK)

[ Автор: Илья Евсеев ]
[ Организация: ИВВиБД ]
[ Подразделение: ЦСТ ]

 

Содержание

 


В чем должна заключаться демонстрация?

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

Было решено, что вычисления должны сводиться к построению мультипликации. Сравниваемые компьютеры генерируют эталонную серию картинок-кадров и в реальном времени отображают их на дисплее. Сравнительная скорость разных машин, таким образом, может быть немедленно оценена даже непрофессионалом "на глазок". Желательно (и в теории возможно), чтобы демонстрашка вызывала, помимо профессионального интереса, чисто праздное любопытство, то есть была бы сделана достаточно эффектно.

Наконец, визуализация - это ведь одна из основных областей применения скоростных машин; и поэтому демонстрация, основанная на ней, наиболее предпочтительна.

 

Что такое Parsytec CC/nK ?

Это 8-процессорная массивно-параллельная машина, через SCSI-мост подключенная к рабочей станции SunSPARC. На вычислительных узлах работают копии однозадачной операционной системы Parsytec nanoKernel (далее nK). Параллельная программа строится на рабочей станции (для этого есть кросс-компиляторы Си/Фортрана и библиотеки к ним), и с нее (со станции) запускается загрузчиком на вычислительных узлах. nK вкупе с библиотеками предоставляет программисту API (applications programmer's interface), который более-менее соответствует стандарту Unix SVR4 (System V Release 4 фирмы ATT). При этом все (на самом деле не все) операции с файлами, выполняемые приложениями на узлах, nK перенаправляет на диск рабочей станции; собственных дисков у узлов нет. Для написания параллельных приложений программисту предлагаются API Parix ("родной" для Парситеков) и PowerMPI (MPI на базе Парикса).

Сетевой адрес нашего CC/nK: pink.csa.ru. Документацию на Парситек и MPI на сайте ЦСТ ищите здесь, здесь и здесь.

Изложенный далее материал применим не только к CC/nK, но так или иначе, ко всем массивно-параллельным системам. Кроме того, программистам-системщикам он (материал) будет (втайне надеюсь) крайне полезен для расширения общего кругозора.

 

Почему MPE не годится для CC/nK

В состав пакета MPICH (базовая, свободно распространяемая реализация MPI, на Парситек не перенесена) входит нечто под названием MPE (возможно, это расшифровывается как "MPI Extensions"). Вот выдержка из его README-файла:

This directory contains useful routines that are not part of MPI
but that may be useful in writing and evaluating the performance
of parallel programs. They include (so far)

  timing routines
  logging routines
  real-time graphics routines
  parallel I/O routines
MPE включает 3 примера параллельных вычислений с рисованием вычисленного: кривая Мандельброта, "жизнь" (моделирование биологической колонии), и нечто непонятное под названием Mastermind. Примеры сами по себе достаточно невзрачные (особенно по сравнению с рекламными MPEG-ами от Silicon Graphics;), но они, во всяком случае, скажем так, выдержаны в политически перспективном ключе. Это и есть та причина, по которой в этом документе появился данный раздел.

MPE предлагает программисту формальные правила для работы параллельного приложения с X-Window. Например, вызов функции XCreateWindow (создание окна) должен выполняться так, чтобы на дисплее появлялось одно окно, а не несколько (по числу ветвей в параллельном приложении). В то же время, идентификатор созданного окна должен быть доступен не только в ветви-создателе, но и в остальных ветвях, чтобы предоставить им возможность самостоятельно работать с окном (рисовать свой фрагмент общей картинки, и т.д.). В то же время, за ветвями должна сохраняться возможность создавать окна для "личных" нужд.

Для построения MPE требуются:

  1. любая реализация MPI,
  2. Xlib - часть X-Window, предназначенная для построения графических изображений (X-клиентов) и их запуска.
В следующем разделе объясняется, почему визуальные примеры из MPE на сегодняшний день, к сожалению, построить на CC/nK невозможно. Однако MPE может быть с успехом использован на менее экзотических платформах.

 

Парситек и X-Window

В состав Парикса не входит специальной версии библиотеки Xlib, с помощью которой Parix-приложения могли бы работать с X-Window. На Парситеках CC/AIX, где на вычислительных узлах установлен AIX (Юникс от IBM), каждая часть (ветвь) параллельного приложения, являясь в то же время полноправным процессом AIX'a, может пользоваться библиотеками AIX'a, в том числе и Xlib'ом. Почему Xlib трудно скомпилировать для CC/nK ?

  1. XFree86 (свободно распространяемая реализация X-Window) категорически не приспособлен для кросс-компиляции, например, его авторы полагают, что компилятор Си называется только /bin/cc, и никак иначе (надо px ancc, и т.д.).
  2. В исходном тексте в большом количестве рассыпаны макродирективы проверки операционной системы (типа "#if defined(IRIX) || defined(AIX4)"). Подразумевается, что все такие места при переносе на новую платформу должны быть тщательно проверены на предмет ее (платформы) соответствия тому или иному варианту.
  3. По-видимому, Xlib в варианте для Парикса должен устанавливать не IP-соединение, и не с X-Server'ом (компьютером, на дисплее которого будет выводиться изображение), а с заглушкой на рабочей станции (скорее всего, через именованный канал), которая уже будет ретранслировать все запросы/ответы на X-сервер.
Не потребоваться заглушка может вот почему: на CC/AIX есть внутренняя Ethernet-сеть с IP-стеком, которую можно связать с сетью внешней, и через которую, таким образом, программа-X-клиент сможет рисовать на дисплее внешней машины. На CC/nK такой сети нет, но операции с сокетами перенаправляются на рабочую станцию наравне с файловыми операциями. Поэтому в данном случае, с точки зрения X-сервера, рисование на его дисплее с CC-узла эквивалентно рисованию с рабочей станции.

 

Какие требования архитектура CC/nK накладывает на демонстрашку?

Демонстрация должна состоять из двух частей: вычислительной и рисующей. Вычислительная часть, выполняющаяся на узлах, должна быть в явном виде распараллелена с использованием MPI. Рисующая часть, запускаемая на рабочей станции, должна получать подготовленные картинки от вычислительной одним из доступных способов, например, через файлы на диске, или через сокет, или через каналы именованные/неименованные.

В результате поисков в Интернете для дальнейшего рассмотрения были отобраны два пакета:

Известный пакет PovRay, вопреки слухам, не оказался параллельным (никаких следов MPI, PVM, многопоточности или чего-то в этом роде в его исходных текстах не обнаружено).


Parallel Graphics Library

PGL отложен в сторону, так как:

  1. картинки сохраняются на диске в уникальном формате YMF, и посмотреть их без PGL весьма затруднительно.
  2. по невыяснявшимся причинам рисующая часть отказалась рисовать то, что произвела вычислительная.
я не исключаю, что в-дальнейшем к нему имеет смысл вернуться для более детального разбирательства.

 


Ray

Архив ray120998.tar.gz поврежден - некоторые подкаталоги в нем отсутствуют. Из-за потерянных подкаталогов половина примеров не запускается. Пакет содержит только вычислительную часть, которая производит файлы TGA-формата (студии Targa TrueVision когда-то были очень популярны...); рисующую часть я написал сам.


Запись изображений в файл

Главное внесенное в Ray изменение: переделана процедура записи построенного изображения в файл.

Добавления в файле ~ray/raysrc/tgafile.c:

void maketgafile( scenedef *scene )  /* EBCEEB */
{
#   define tgaHeadSize 18

    static char tgaHeader[tgaHeadSize] =
    {
        0,    /* IdLength      */
        0,    /* ColorMapType  */
        2,    /* ImageTypeCode */
        0,    /* ColorMapOrigin, low byte */
        0,    /* ColorMapOrigin, high byte */
        0,    /* ColorMapLength, low byte */
        0,    /* ColorMapLength, high byte */
        0,    /* ColorMapEntrySize */
        0,    /* XOrigin, low byte */
        0,    /* XOrigin, high byte */
        0,    /* YOrigin, low byte */
        0,    /* YOrigin, high byte */
        0,    /* 12 <-- Width, low byte */
        0,    /* 13 <-- Width, high byte */
        0,    /* 14 <-- Height, low byte */
        0,    /* 15 <-- Height, high byte */
        24,   /* ImagePixelSize */
        0x20  /* ImageDescriptorByte: 0x20 == flip vertically */
    };

    int nwritten;
    FILE *f = NULL;

    unsigned char *filebuf;
    register unsigned char *src, *dest;
    register unsigned int pt;
    unsigned int total = scene->hres * scene->vres;

    /*\   Create file
    \*/
    if( !scene->outfilename || (f=fopen(scene->outfilename,"w+b"))==NULL)
    {
        char msgtxt[2048];
        sprintf( msgtxt, "Cannot create \"%s\" for output!",
            scene->outfilename );
        rt_ui_message(MSG_ERR, msgtxt);
        rt_ui_message(MSG_ABORT, "Rendering Aborted.");
        exit(1);
    }
    setvbuf( f, NULL, _IONBF, 0 );

    /*\   Allocate heap buffer
    \*/
    if(( filebuf = malloc( tgaHeadSize + total * 3 )) == NULL )
    {
        rt_ui_message(MSG_ERR,"writetgaregion: failed malloc()!\n");
        return;
    }

    /*\   Prepare header
    \*/
    memcpy( filebuf, tgaHeader, tgaHeadSize );
    filebuf[12] = scene->hres & 0xff;         /* Width, low byte */
    filebuf[13] = (scene->hres >> 8) & 0xff;  /* Width, high byte */
    filebuf[14] = scene->vres & 0xff;         /* Height, low byte */
    filebuf[15] = (scene->vres >> 8) & 0xff;  /* Height, high byte */

    /*\   Convert points
    \*/
    for( src = scene->img + total * 3,
         dest = filebuf + tgaHeadSize,
         pt = total; pt > 0; pt-- )
    {
        *dest++ = *--src;
        *dest++ = *--src;
        *dest++ = *--src;
    }

    /*\   Write to disk
    \*/
    if (( nwritten = fwrite( filebuf, tgaHeadSize + 3 * total,
        1, f ) ) != tgaHeadSize + 3 * total )
    {
        char msgtxt[256];
        sprintf(msgtxt,"File write problem, %d bytes written.\n",nwritten);
        rt_ui_message(MSG_ERR, msgtxt);
    }

    /*\   All done
    \*/
    free( filebuf );
    fclose( f );

} /* maketgafile */
Прототип maketgafile() помещен в tgafile.h

Изменения в файле ~ray/raysrc/render.c, функция renderio:

#if 1 /* EBCEEB */
  maketgafile( scene );
#else /* this is old text */
  createtgafile(scene->outfilename,  
                (unsigned short) scene->hres, 
                (unsigned short) scene->vres);
  outfile = opentgafile(scene->outfilename);
  writetgaregion(outfile, 1, 1, scene->hres, scene->vres, scene->img);
  closetgafile(outfile);
#endif
Теперь запись картинки производится не построчно, а за один вызов fwrite(). Как итог, скорость работы тестового примера на CC/nK увеличилась почти в 10 раз. Этому можно предложить 2 взаимодополняющих объяснения:
  1. Внутримашинная сеть имеет т.н. низкую латентность, т.е. время на организацию передачи слишком велико по отношению ко времени собственно передачи. (Примечание: латентность можно вычислять как разность между удвоенным временем передачи пакета минимальной длины и временем передачи пакета вдвое большей длины).
  2. nK не кэширует перед отправкой на рабочую станцию данные, предназначаемые для записи в файл.
Кстати, кэширование средствами стандартной библиотеки ввода/вывода работает на узлах CC/nK нормально, но здесь не использовано в силу недостаточной радикальности.


Внимание! Ошибка в nK

В стандартном Си существуют два способа работы с файлами: через файловые переменные (функции fopen, fwrite, fread, fputs, fprintf, fseek, fflush, fclose, еклмн) и через файловые номера (open, create, read, write, lseek, close и т.д.). Второй способ примитивнее и быстрее. Однако пользоваться на CC/nK (на CC/AIX не пробовал) им нельзя, так как write не перенаправляется на диск рабочей станции!!


Cнижение качества и увеличение скорости: MPI_DUMMY

Базовый Ray использует следующую схему распараллеливания: главная программа пишется как последовательная, без упоминаний об MPI. Таким образом, во всех ветвях сначала выполняется одно и то же. MPI используется только внутри функции построения кадра rt_renderscene(). Коллективом из N процессоров каждый кадр строится почти в N раз быстрее, чем на одиночном процессоре.

Однако с увеличением числа процессоров/ветвей увеличивается и количество приемопередач, а следовательно, и задержка, обусловленная низкой латентностью внутренней сети (см.выше). Из-за этого выигрыш от увеличения количества процессоров становится нелинейным, и, с какого-то момента - неоправданно малым.

Поэтому Ray строился в модифицированном виде (используемая для условной компиляции этого варианта переменная препроцессора названа мною MPI_DUMMY):

  1. библиотека построения изображений строилась как последовательная, без MPI,
  2. программа-драйвер описания движущегося изображения была переписана на MPI, и ее ветвь k строит независимо от остальных ветвей кадры  k, k+N, k+2N, ...
То есть: В итоге: скорость построения одного кадра фиксирована, и при увеличении количества процессоров более не увеличивается - это плохо. Однако скорость построения всей серии в-целом выросла, потому что количество межпроцессовых пересылок теперь так же при увеличении количества процессоров не увеличивается - это хорошо. Связанный с таким построителем просмотрщик должен самостоятельно заботиться о том, чтобы создаваемые не в последовательно-параллельном, а в параллельно-последовательном режиме кадры рисовались один за другим плавно, без рывков.

Здесь находится исправленный под MPI_DUMMY исходный текст примера ~ray/demosrc/mainanim.c (запускаемый файл называется ~ray/compile/???-dummy/animray). AnimRay изготавливает вот такие симпатичные картиночки:

AnimRay sample

Все-таки, последовательно-параллельная схема не может быть заменена параллельно-последовательной в двух наиболее частых ситуациях:

  1. в интерактивных приложениях, где значима именно скорость генерации одного кадра,
  2. если кадр строится на основании данных, вычисляемых при построении предыдущего кадра.
Однако изложенная идея может быть использована для быстрой "перешивки" изначально последовательных библиотек под параллельную архитектуру: пишется параллельная программа-драйвер, которая в разных ветвях вызывает последовательную библиотеку для раздельного построения кадров. Такой вырожденный параллелизм будет полезен, если требуется повысить скорость построения именно серий кадров.

Может показаться странным, что для демонстрации идеи был выбран изначально параллельный Ray, перестроенный в последовательном режиме. Тому причин две:

  1. его изначальная способность производить более актуальную последовательно-параллельную обработку изображений,
  2. мое нежелание дополнительно разбираться в какой-то другой библиотеке.


Дополнения в MAKE-сценарии

Файл ~ray/unix/makearch, начало:

MPIDIR=/export/home/parix/mpi    # EBCEEB
UTILBIN=/export/home/il/epxbin   # EBCEEB
Файл ~ray/unix/makearch, после раздела "default":
parix-mpi-dummy:
	$(MAKE) all \
	"ARCH = parix-mpi-dummy" \
	"CC = $(MPIDIR)/bin/mpicc" \
	"CFLAGS = -O2 -DParix -DMPI_DUMMY" \
	"AR = $(UTILBIN)/px_ar" \
	"ARFLAGS = r" \
	"STRIP = $(UTILBIN)/px_strip" \
	"RANLIB = $(UTILBIN)/px_ranlib" \
	"LIBS = -L. -lmgf -lray -lm"

parix-mpi:
	$(MAKE) all \
	"ARCH = parix-mpi" \
	"CC = $(MPIDIR)/bin/mpicc" \
	"CFLAGS = -O2 -DParix -DMPI" \
	"AR = $(UTILBIN)/px_ar" \
	"ARFLAGS = r" \
	"STRIP = $(UTILBIN)/px_strip" \
	"RANLIB = $(UTILBIN)/px_ranlib" \
	"LIBS = -L. -lmgf -lray -lm"

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

# This is file /export/home/il/epxbin/px_strip
px strip $*


Ключи командной строки AnimRay

-o PATH_PREFIX
Задает начало имени для создаваемых файлов-картинок. По умолчанию: "/tmp/outfile". Это значит, что файлы будут иметь имена /tmp/outfile.0000.tga, /tmp/outfile.0001.tga, и т.д.

-v
Включает вывод на консоль уведомляющего сообщения после генерации каждого кадра. По умолчанию: сообщение выводится только после генерации всех кадров.

-t
В каждом уведомляющем сообщении будет выводиться время, прошедшее с момента начала работы. Используйте этот ключ вместо команды /bin/date, так как в последнем случае ко времени счета будет добавлено время загрузки/выгрузки приложения, в данном случае интереса не представляющее.

-n NN
Задает количество картинок. По умолчанию: NN = 100.

 


Визуальная часть - TGA Player

TGA Player - это графическое приложение, которое запускается на рабочей станции, отслеживает появление файлов-кадров в указываемом каталоге и рисует их. Проигрыватель работает одновременно с вычислительной частью, которая написана на Ray'e. Запускаться обе части могут, например, так:

#!/bin/csh
setenv LD_LIBRARY_PATH /usr/openwin/lib   # Important for Solaris!!
if( ! $?DISPLAY )  setenv DISPLAY argon.csa.ru
rm -f /tmp/outfile.????.tga
tgaview -np 4 -n 100 -loop -prefix /tmp/outfile \
   -caption "Real-time, 4 CPUs" &
mpirun -np 4 ~il/ray/compile/mpi-dummy/animray

Проигрыватель, чтобы работать везде, не использует ни Gnome, ни KDE, ни что-либо еще. Даже Xtoolkit не используется, потому что у меня не было времени на его освоение (я до этого не писал под X-Window). Исходный текст находится здесь. К сожалению, он совершенно не откомментирован - ни времени, ни сил, ни желания для этого не было, нет и не предвидится.

Конструктивных особенностей у проигрывателя три:

  1. Для вывода изображений используется библиотека Imlib. Это единственное нестандартное средство, которое Вам придется установить, чтобы построить TgaPlay.
  2. Для слежения за появлением файлов-картинок используется дочерний процесс, который уведомляет о них рисующий, родительский процесс, посылая ему сообщения X-Window. Других способов заставить процесс, обрабатывающий сообщения X-Window, обратить внимание на что-то кроме этих сообщений, я не нашел.
  3. Дочерний процесс пытается сделать интервалы между сообщениями как можно более равными.
Какого-то более подробного разъяснения эта маленькая программа просто не заслуживает. Предположительно, в ней еще остались ошибки, поэтому стабильная работа не гарантируется - требуется отладка.


Построение TGA Player'a

Теоретически, должно без проблем осуществляться примерно следующим командным файлом:

#!/bin/sh

# X11_PATH=/usr/openwin    # for Solaris with OpenWindows
  X11_PATH=/usr/X11R6      # for Linux with XFree86
if [ ! -d ${X11_PATH} ]; then
   echo Edit $0 for right setting of X11_PATH !!!
   exit 1
fi

# Path where Imlib is installed, not path to Imlib sources!
IMLIB_PATH=/users/il/imlib-1.9.2
IMLIB_ADDONS=
#IMLIB_ADDONS=-ljpeg -lpng -ltiff -lz -lgif

cc tgaplay.c -o tgaplay \
   -I/usr/local/include
   -I${IMLIB_PATH}/include \
   -L${IMLIB_PATH}/lib \
   -L${X11_PATH}/lib \
   -lX11 -lXext -lImlib -lm ${IMLIB_ADDONS}
До какого-то момента под Линуксом (RedHat 5.1) компоновщик при автоматическом вызове из gcc по так и невыясненным причинам не видел некоторых библиотек, но при ручном вызове все работало (список автоматически передаваемых аргументов сообщает "gcc -v"):
gcc -c tgaplay.c -I/usr/local/include && \
ld -m elf_i386 -dynamic-linker -o tgaplay \
   -L/usr/local/lib \
   -L/usr/X11R6/lib \
   -L/usr/lib/gcc-lib/i386-redhat-linux/2.7.2.3 \
   -L/usr/i386-redhat-linux/lib \
   /lib/ld-linux.so.2 \
   /usr/lib/crt1.o \
   /usr/lib/crti.o \
   /usr/lib/gcc-lib/i386-redhat-linux/2.7.2.3/crtbegin.o \
   tgaplay.o -lX11 -lXext -ljpeg -lpng -ltiff \
   -lz -lm -lgif -lImlib -lgcc -lc -lgcc \
   /usr/lib/gcc-lib/i386-redhat-linux/2.7.2.3/crtend.o \
   /usr/lib/crtn.o
Этот глюк исчез так же непонятно, как и появился. Может быть, в конец строки затесался DOS-овский символ "возврат каретки" ?


Ключи командной строки TGA Player'а

-prefix PATH_PREFIX
Это единственный обязательный аргумент. Задает начальную часть имени всех файлов-картинок Например, -prefix /tmp/outfile означает, что картинки будут последоватьно грузиться из файлов /tmp/outfile.0000.tga, /tmp/outfile.0001.tga и т.д.

-np NNODES
Количество параллельных ветвей в генерирующем картинки MPI-приложении. Проигрывателю нужно знать его, чтобы не дергать изображение: пауза, потом быстрый показ нескольких кадров (когда несколько ветвей одновременно закончат запись своих файлов), потом новая пауза, новый рывок, ... Чтобы этого избежать, проигрыватель на шаге k ожидает появления не k+1-го файла, а файла с номером k*NNODES+NNODES. После этого файлы k*NNODES+1 .. k*NNODES+NNODES с задержкой, равной T/NNODES, где T - время генерации предыдущей порции файлов.
Примечание: даже если этот ключ не указан, проигрыватель самостоятельно пытается сделать скорость показа наиболее равномерной.

-n NIMAGES
Общее количество кадров. По его достижении проигрыватель либо останавливается, либо переходит к нулевому кадру. (см.параметр "-loop"). По умолчанию: 32767.

-loop
Задает поведение при достижении кадра с максимальным номером: остановка или циклический повтор. По умолчанию: остановка.

-period S
Задает интервал в миллисекундах между проверками появления новых файлов на диске. По умолчанию: 50 миллисекунд. Не задавайте ни слишком большое, ни слишком маленькое значение; оптимально - 10-20% от времени генерации всех картинок, деленного на их количество.

-caption TEXT
Заголовок окна. Не забудьте кавычки, если заголовок содержит пробелы. Пример: -caption "Real-time, 4 CPU\'s on CC/nK"


Изменения и дополнения в Imlib

Imlib нужен единственно для того, чтобы преобразовывать 24-битное изображение для вывода на дисплей с меньшей размерностью палитры. Удивительно, но ни MS Windows, ни X-Window не содержат встроенных средств для такого преобразования.

В Imlib нет встроенной поддержки TGA-формата, поэтому я добавил ее в самом необходимом виде: только чтение, и только тот простейший вариант, которым пользуется Ray.

Ниже идут мои дополнения к файлу ~imlib/Imlib/load.c.
До функции Imlib_load_image:

int
istga(FILE *f, char *fname)  /* EBCEEB */
{
  return !strcmp( fname+strlen(fname)-4, ".tga" );
}

unsigned char      *
_LoadTGA(ImlibData * id, FILE * f, int *w, int *h, int *transparent)
{
  unsigned char tga[18];
# define tga_identsize          tga[0]
# define tga_imagetype          tga[2]
# define tga_colormaplength   ( tga[5] + (((unsigned)tga[6]) << 8) )
# define tga_width            ( tga[12] + (((unsigned)tga[13]) << 8) )
# define tga_height           ( tga[14] + (((unsigned)tga[15]) << 8) )
# define tga_bits               tga[16]
# define tga_descriptor         tga[17]

  char *buffer, c;
  unsigned bufsize, i;
  
  if( fread( &tga, 1, sizeof(tga), f ) != sizeof(tga) )
    return NULL;
  
  if(  tga_bits != 24
    || tga_descriptor != 0x20
    || tga_colormaplength
    || tga_imagetype != 2
    || tga_identsize
    || tga_colormaplength
  )
    { fprintf(stderr,"IMLIB ERROR: Invalid header\n"); return NULL; }

  *w = tga_width;
  *h = tga_height;
  *transparent = 0;

  bufsize = 3 * tga_width * tga_height;
  if(( buffer = malloc( bufsize )) == NULL )
    { fprintf(stderr,"IMLIB ERROR: cannot malloc\n"); return NULL; }
  if( fread( buffer, 1, bufsize, f ) != bufsize )
  {
    fprintf(stderr,"IMLIB ERROR: image too short\n");
    free(buffer);
    return NULL;
  }
  for( i=0; i<bufsize; i+=3 )
  {
    c=buffer[i];
    buffer[i]=buffer[i+2];
    buffer[i+2]=c;
  }
  fprintf(stderr,"DEBUG: file \"%s\", line %d\n", __FILE__, __LINE__ );
  return buffer;
}
Функция Imlib_load_image, проверка типа файла:
   ...
  else if (istga(p,file)) /* EBCEEB */
    {
      needs_conv = 0;
      fmt = 0xEBCEEB;
    }
Функция Imlib_load_image, чтение файла:
      switch (fmt)
	{
	case 0xEBCEEB:
	  data = _LoadTGA(id, p, &w, &h, &trans);
	  break;
        ...

В-принципе, благодаря Imlib, TgaPlay в состоянии проигрывать не только TGA, но и JPEG, PPM, TIFF, XPM, EIM, PNG, GIF и BMP, а так же все, для чего существуют конвертеры в один из этих форматов. Или, наоборот, можно в промежутке между конфигурированием и построением Imlib (иначе говоря, в каталоге с дистрибутивом Imlib, между "sh ./configure" и "make check") вписать в начало Imlib.h:

    #undef HAVE_LIBJPEG
    #undef HAVE_LIBPNG
    #undef HAVE_LIBTIFF
    #undef HAVE_LIBGIF
Это сделает библиотеку менее универсальной, но более компактной. Построение упростится и ускорится, возможно, при этом исчезнут некоторые ошибки.


Перенос Imlib-приложений с машины на машину

Imlib использует два настроечных файла: imrc ищется сначала в домашнем каталоге, затем в каталоге директория_установки_Imlib/etc; путь к файлу im_palette.pal прописан в imrc. Эти файлы (кажется) не содержат системно-зависимых данных и могут быть скопированы с машины на машину в одноименный каталог без редактирования.

В процессе инсталляции Imlib директория_установки_Imlib ...

по умолчанию это /usr/local; настройки помещаются в /usr/local/etc.

Помните, что без настроечных файлов использующее библиотеку Imlib приложение не заработает! Диагноз: завершение с ошибкой "Imlib: required colormap not found" (цитируется по памяти) в момент вызова Imlib_init().


Рисование изображений в Windows

Первый вариант TGA Player'a был написан в среде Windows'95 с использованием двухтомника Петцольда, во-первых; и библиотеки Graphics Viewer версии 2.1 (см. описание), во-вторых. Автором библиотеки является некий Joe Oliphant. Написанная в эпоху засилья Windows 3.1, она и сейчас вполне работоспособна, если выскрести из нее всю 16-разрядность, а именно:

  1. у полей в структурах поменять тип int на short,
  2. и отключить выравнивание полей в структурах (#pragma pack(1) в начале исходного текста, или /Zp1 в командной строке Visual C++, или -a1 для Borland C++).
В этой библиотеке, в частности, приведен полный исходный текст для чтения всех вариантов TGA-формата. Если решите искать ее в Интернете, можете попробовать FTP-поиск: имя файла - gv21.zip, длина - 95421 байт.

Факт, заслуживающий напоследок повторного упоминания: само по себе чтение TGA-файла не представляет проблемы. Единственная причина, по которой GV оказался необходим под MS Windows, а Imlib под X-Window: являющееся нетривиальным приведение TrueColor-цветов к текущей палитре дисплея не поддерживается ни той, ни другой графической системой, и в обоих случаях должно выполняться программой пользователя.



Что и как можно продолжать развивать дальше?


... системному интегратору ...

Кроме собственно вычислительных узлов, в демонстрации задействованы: внутренняя сеть, рабочая станция (ее диск, процессор, видеокарта), и, возможно, внешняя сеть (если TGA Player рисует на дисплее другой машины). Каждый из этих компонентов способен стать узким местом и сделать скорость работы фиксированной независимо от числа и скорости процессоров на CC-узлах. От системного интегратора требуется: а) выявление критически медленных аппаратных компонентов, и б) их замена.

Что известно сейчас о Sun-совместимой рабочей станции Tatung, подключенной к нашему CC/nK?

  1. На ней установлена старая и чрезвычайно медленная видеокарта, из-за этого рисование приходится производить на дисплее соседнего SGI Octane.
  2. Максимальная скорость записи с CC-узлов на диск станции не превышает 1MB/s, и этого, по-видимому, недостаточно.

Если Tatung остается рабочей станцией для CC/nK, то его системному администратору настоятельно рекомендуется установить туда в конце концов компилятор Си для SPARC'a!


... программисту ...

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

Согласитесь, что подобный список можно при желании продолжать до бесконечности. Его реализация - это работа на много месяцев, а то и лет ... ;-\



Версии документа

31 мая 1999 .... первая версия документа
7 июня 1999 .... поправки

.


Хостинг от uCoz