Saturday, November 12, 2011

Renderman RSL-4.2

GNU Make

            Агуулахын хэмжээ томрохын хэрээр доторхи файл бүрийг хөрвүүлэхэд тун их хугацаа зарцуулж байгааг анзаарна. Түүнчлэн файлын холбоос, удамшил зэргийг хайж олоход маш төвөгтэй болно. Нэг жишээн дээр тайлбарлахад та гэрэлтүүлэлтийн загвар хийгээд амжилттай ашиглаж байсан  гэж үзье. Гэнэт түүн дотор bug (шавьж) илэрч зурах үед асуудал тулгарлаа. Та гэрэлтүүлэлтийн загвар болох толгой файлыг шалгаж байгаад алдааг илрүүлж заслаа. Үүний дараагаар энэхүү загварыг ашигласан бүх шэйдр файлуудыг дахин хөрвүүлэх шаардлагатай болно. Гэвч аль файл энэ загварыг ашигласныг хэрхэн мэдэх вэ? Хэрэв файлууд нь тавь, жараас дээш бол бүгдийг нь нэг нэгээр нь дахин хөрвүүлэх үү? зэрэг хүндрэлүүд үүснэ. Өөрчлөгдсөн файлыг агуулсан шэйдр бүр автоматаар дахин хөрвүүлэгдэж байх арга хэрэгтэй болно. Энэ бүх төвөгтэй үйл ажиллагааг GNU Make зохицуулж хяналтанд байлгах болно.


 GNU Make нь файлыг үүсгэх, суулгах, хэрэглээнд нэвтрүүлэх, бүх төрлийн үйлдлүүд гүйцэтгэх дүрмүүдийг тодорхойлдог програм юм. Дүрэм нь бусад дүрэм болон тодорхойлогч файлаас хамааралтай. Дүрмийг биелүүлэхээр дуудах үед Make нь байг тодорхойлогч файлтай харьцуулж үздэг. Хэрэв эдгээр файлууд нь байнаас шинэ байх юм бол дүрэм биелэгдэж байг шинээр сольдог.
Variables (Хувьсагчид)
       Make файл нь өөрийгөө цэвэрхэн эмх цэгцтэй байлгахын тулд ашигтай хувьсагчуудыг дэмждэг. Make файл дотор утга оноох үед хувьсагчид нь зарлагддаг. Бүх хувьсагчид тэмдэгт мөр учраас хувьсагчдын төрлийг тодорхойлох шаардлага байхгүй. Хувьсагчид нь case sensitive учраас ихэвчлэн том үсгээр бичдэг:
SLOFILES = shader01.slo shader02.slo
 Make файл доторхи хувьсагчид руу хандахдаа:
$(SLOFILES)
 Make нь файлыг гүйцэтгэх үед хувьсагчийг утгаар нь сольдог. Системийн орчны (environment) хувьсагчид хандахдаа мөн энэ синтаксыг ашиглана.

Writing Rules and Commands (Дүрэм болон командаа бичих)
 Дүрэм нь Make файлын амин сүнс нь юм. Найдвартай бат бөх, ухаалаг дүрэм бичиж сурахад практик ажиллагаа чухал боловч ерөнхий зарчим, концепцийг мэдсэнээр хялбар аргаар бичиж болдог. Дүрэм нь эхний мөрөнд урдаа хоосон зай авахгүйгээр дараахи маягаар бичигдэнэ.
target: prerequisites
   commands
....
target буюу бай нь ихэнхдээ үүсгэх гэж байгаа файл эсвэл дүрмийнхээ нэртэй ижил байдаг. prerequisite буюу тодорхойлогч нь байг үүсгэхэд шаардлагатай эх файлуудын нэрсийн жагсаалт байдаг. Тодорхойлогчийн араас орох команд нь өмнөө нэг, хоёр хоосон зай тэмдэгт агуулсан байх ёстой. Make нь урдаа хоосон зайтай мөрийг олвол үүнийг команд гэж үзэн цааш дамжуулна. myshader.slo нэртэй шэйдрийг PRMan хөрвүүлэгч ашиглан үүсгэх гэж байгаа бол дараахи хэлбэртэй дүрмийг Make файл дотор оруулж өгнө:

myshader.slo: myshader.sl
       shader $<
     Локал хавтсанд байрлах make Myshader.slo-г ажиллуулахад myshader.slo нэртэй файлыг make нь хайж эхэлнэ. Хэрэв энэхүү файл олдохгүй бол дүрэмд харгалзах командыг биелүүлнэ. Харин файл олдсон тохиолдолд түүний огноог myshader.sl тодорхойлогчтой харьцуулна. myshader.sl нь шинэ байвал командыг биелүүлнэ, харин эсрэгээрээ байвал “There is nothing to be done for myshader.slo.” гэсэн мессэжийг харуулна. Маш энгийн энэ дүрэм бодит байдалд бараг л ашиглагдахгүй. myshader.slo-г файл бүрд харгалзуулан бичнэ гэвэл маш их хугацаа алдана. Иймээс make нь том хэмжээтэй дүрмүүдийг нэг л командаар биелүүлэх механизмийг хэрэгжүүлдэг.
 Томоохон build хийхийн тулд бүх дүрмийг ашиглана. Командын мөрөнд make гэж дангаар нь бичвэл програм нь бүх дүрмийг шалгаад хэрэв олдвол түүнийг биелүүлнэ.Тодорхойлогч файл хэлбэртэй объектуудаас бүх дүрэм бүрдэнэ. Доор бүх дүрэм болон түүний уялдаа хамаарлыг харуулсан жишээ байна.
all: myshader01.slo myshader02.slo myshader03.slo
myshader01.slo: myshader01.sl
   shader $<
myshader02.slo: myshader02.sl
   shader $<
myshader02.slo: myshader02.sl
   shader $<
     Локал сан дотор make гэж бичвэл програм нь бүх дүрмийг биелүүлэх бөгөөд myshader01.slo, myshader02.slo, ба myshader03.slo дүрмүүдэд хамааралтай. Эдгээр дүрмүүд нь өмнө дурьдсан аргаар хэрэгжинэ. Make файл нь одоохондоо бидэнд хэрэг болохгүй олон тооны нэмэлт үйлдлүүдийг агуулсан байдаг. Бид зөвхөн өөрсдийн хэрэгцээнд шаардлагатай шэйд агуулахын нэгэн хэсэг болсон make  файлыг тайлбарлана. Мөн түүнийг тухайн нөхцөлд хэрхэн тохируулан хэрэглэх талаар үзнэ. www.gnu.org/software/make/ хуудаснаас энэ програмын талаар илүү их зүйл мэдэж авах боломжтой.
Бидний агуулахад байрлах хоёр make файл нь бүх шэйдрийг байгуулж, нэвтрүүлэх үүрэгтэй. Үндсэн make буюу мастер файл нь  src хавтсан дотор байрлах ба бусад дэд хавтсан дотор байрлах make файлуудыг дуудаж ажиллуулах үүрэгтэй.
#
# Makefile to build all shaders in the recursive directories
#
# by Rudy Cortes
#
#--- VARIABLES ---
DIRS = displacement surface light imager volume
#--- RULES ---
all: $(DIRS)
$(DIRS): FORCE
   $(MAKE) -C $@
   @echo
release: $(DIRS)
   @echo
   @echo "##############################"
   @echo "##############################"
   @echo "#"
   @echo "# releasing shaders"
   @echo "#"
   @echo "##############################"
   @echo "##############################"
   @echo
   @for i in $(DIRS);do\
            $(MAKE) release -C $$i;\
    done
FORCE:docs:
    doxygen doxyfile

  make нь ямар нэгэн үүрэг гүйцэтгэхийн тулд  displacement surface light imager volume гэсэн хавтаснуудын нэрийг агуулсан DIRS хувьсагчийг тодорхойлсон байна. Дараа нь all: target гэсэн дүрмийг тодорхойлж өгсөн ба DIRS хувьсагчийн утгыг тодорхойлогчоор ашигласан. Тэгэхээр DIRS хувьсагчид оноосон директоруудаас бүх дүрэм хамааралтай болно. Дараагийн мөрөнд FORCE тодорхойлогчтой $(DIRS) гэсэн байг тодорхойлсон байна. Файлын төгсгөлд FORCE бай нь тодорхойлогч болон командгүйгээр бичигдсэн байна. Энэ нь $(DIRS) дотор байрлах байнууд бүрт  зайлшгүй үйлдэл хийх ёстой гэсэн үг. $(DIRS) хувьсагч хоосон зайгаар таслагдсан нэрсийн жагсаалт учраас мake нь нэр тус бүрийг нэг бай гэж авч үзнэ. $(MAKE) -C $@ команд нь make-ийг дэд хавтсан доторхи мake файл бүрийг  биелүүлэх ёстойг илтгэж байна. Энэхүү команд нь маш чухал үүрэгтэй учраас цаашид дэлгэрэнгүй тайлбарлана.
    $(MAKE) тусгай хувьсагч нь make файлыг биелүүлэх үүрэгтэй програмыг заадаг. Make файл дотроос make-ийг шууд дуудаж хэрэглэх нь найдваргүй, сөрөг үр дүн үзүүлдэг. -C флаг нь make-д дараагийн аргумент нь директор болохыг хэлж өгөн локал make командыг биелүүлэхээс өмнө тус директор луу cd хийж нэвтрэх хэрэгтэйг илэрхийлнэ. $@ нь make-ийн автоматаар үүсгэдэг хувьсагчдын нэг бөгөөд дүрмийг ерөнхий маягаар бичихэд ашигладаг. Энэ хувьсагч нь make-ийн үүсгэх гэж буй файлын нэрийг заадаг. (Хүснэгт 4.4-т өөр ашигтай автоматаар үүсдэг хувьсагчдыг харууллаа.) Make нь $(DIRS)-т заагдсан хавтас бүрт cd командыг ашиглан нэвтэрч файлыг боловсруулахын тулд адилхан мake командыг биелүүлнэ. Make командын араас  echo команд орсон байна. энэ команд нь хоосон мөр хэвлэх ба програмын гаралтын утгыг ойлгомжтой уншихад хялбар болгоно. Командыг хэвлэх биш гүйцэтгэхийн тулд @ тэмдэгт нь  echo командын өмнө зайлшгүй байрлах ёстой. Анхны утгаараа make нь бүх командыг хэвлэж харуулдаг ба энэ нь  шууд команд бичиж оруулсантай адилхан. 
Хүснэгт 4.4 Ашигтай автомат хувьсагчид
Тэмдэгт              Утга
$@                       The filename of the target of the rule.
$<                        The name of the first prerequisite.
$?                         The names of all the prerequisites that are newer than the target, with spaces
between them.
$^                         The names of all the prerequisites, with spaces between them.
$+                         This is like $^, but prerequisites listed more than once are duplicated in the
order they were listed in the Make file

The next target defined in this file is the release rule, which is used to copy all of
the compiled shader files into a release place where they will be found by the renderer.
The release target has $(DIRS) as a prerequisite. This is done so that Make
will recompile any file that is out of date before it is released. We first print several
lines with a nice big notice to let us know at which point the release actually
starts in the output of the program. We then write a simple for loop in standard
shell commands, which will call $(MAKE) release -C $$i. The i variable holds the
name of each of the values of DIRS, and we need to use a double $$ to restrict Make
from trying to expand the variable. We need to use this workaround of using a
shell for loop because the target to be built is release, which is not a file or a directory,
so we can’t use it as a parameter to our $(MAKE) command. The final rule in
the Make file is used to build the documentation with doxygen. To use it just type
make docs from the command prompt.
Дээрхи файлд тодорхойлогдсон дараагийн бай нь нэвтрүүлэх дүрэм юм. Энэ нь хөрвүүлэгдсэн шэйдр файлуудыг  render-ийн тодорхойлсон байрлал руу хуулах үүрэгтэй. Нэвтрүүлэх дүрэм нь $(DIRS) хувьсагчийг ашиглана. Нэвтрүүлэхийн өмнө make нь хугацаа нь хэтэрсэн файл бүрийг дахин хөрвүүлдэг. Юуны өмнө хэзээ, аль цэг дээрээс нэвтрүүлэлт эхэлж байгаа талаар програмын гаралт дээр ойлгомжтой хэвлэж харуулахын тулд хэд хэдэн мөртэй тайлбарыг нэмж оруулах хэрэгтэй.  Үүний дараа шэлийн стандарт командын нэг болох цикл дотор  $(MAKE) release -C $$i -г дуудсан байна. i- хувьсагч нь DIRS доторхи утгуудыг ээлжлэн авах ба Make-нь хувьсагчийг өргөтгөхөөс сэргийлэн $$ ашиглах ёстой.
The first two rules in this file won’t do anything else on their own. They are heavily
dependent on Make files that exist inside of each of the directories in DIRS. This
“master” Make file is simply a subcommand executer. The real magic goes on
inside the make file inside each of the subdirectories, which look like this:
#
# makefile for compiling all shaders in the current directory
#
# by Rudy Cortes - Created 04/24/2006
#
#--- VARIABLES ---
CURDIR = $(shell pwd)
# hack to eliminate spaces in the "Documents and settings" folder in
# windows
CURDIRN = $(word $(words $(CURDIR)),$(CURDIR))
SHADERTYPE = $(notdir $(CURDIRN))
SLFILES = $(wildcard *.sl)
RELEASEDIR = ../../rel/
ifeq ($(RENDERER), )
COMPILECMD = shader -DPRMAN
COMPILEEXT = slo
else ifeq ($(RENDERER),3delight)
COMPILECMD = shaderdl
COMPILEEXT = sdl
endif
SLOFILES = $(SLFILES:%.sl=%.$(COMPILEEXT))
RELEASEDFILES = $(SLFILES:%.sl=$(RELEASEDIR)%.$(COMPILEEXT))
INCLUDEDIRS= -I../include
#--- Targets ---
all: $(SLOFILES)
@echo
@echo --------COMPILE--------
@echo compile of $(SHADERTYPE) shaders done!!
@echo -----------------------
@echo
%.$(COMPILEEXT): %.sl
@echo
$(COMPILECMD) $(INCLUDEDIRS) $<
@echo
@echo - Moving shader to release dir -
mv $@ $(RELEASEDIR)
FORCE:
release: $(RELEASEDFILES)
@echo
@echo -------RELEASE--------
@echo release of $(SHADERTYPE) shaders done!!
@echo ----------------------
@echo
$(RELEASEDIR)%.$(COMPILEEXT): %.$(COMPILEEXT)
@echo
@echo .................
install $^ $(RELEASEDIR)
@echo .................
@echo
The first section defines some very important and useful variables. Let’s give them
a closer look.
CURDIR = $(shell pwd)
# hack to eliminate spaces in the "Documents and settings" folder in
# windows
CURDIRN = $(word $(words $(CURDIR)),$(CURDIR))
SHADERTYPE = $(notdir $(CURDIRN))
SLFILES = $(wildcard *.sl)
RELEASEDIR = ../../rel/
RELEASEDFILES = $(SLOFILES:%.slo=$(RELEASEDIR)%.slo)
INCLUDEDIRS= -I../include
The CURDIR variable reads the value returned by the shell command pwd. The $
(shell command) is used to read in values from shell commands, so when we type
$(shell pwd) we are telling make to execute pwd and give us the returned value. We
then declare CURDIRN, which is a hack to eliminate the spaces found in a path that
uses Documents and settings. The $(word) command has the following syntax:
$(word number, string)
and returns the word that is in the position number within string. So, for example
$(word 2, foo bar tops)
will return bar. The $(words) command will return the number of words in the
string, so using $(word $(words $(CURDIR)),$(CURDIR)) will return the last segment
of c:\Documents and Settings\rcortes\somedirectory, since it will treat the words
Documents and and as words because they are separated by white space.
The next variable declared is SHADERTYPE, which uses the $(notdir) command to
extract the last part of a file path, so this command:
$(notdir /home/rudy/surface)
will return the word surface. Since the directories in which our source code is
stored are named according to the type of shader they contain, we can use this
name to figure out what type of shaders we are compiling.
Next we use the $(wildcard) command to retrieve the value of all the sl files in
the current directory. If we don’t use the $(wildcard) command and we assign
SLFILES = *.sl, then the value of SLFILES will not be the names of all the sl files
in the current directory but the string *.sl. This causes problems down the line
because we will use the string substitution command $(SLFILES:%.sl=%.slo) later
to replace the .sl extension to slo to assign to the SLOFILES variable. The rest of
the variables use the same commands that we have already covered, so they should
be self-explanatory. Next, we have some interesting control flow statements:
ifeq ($(RENDERER), )
COMPILECMD = shader -DPRMAN
COMPILEEXT = slo
else ifeq ($(RENDERER),3delight)
COMPILECMD = shaderdl
COMPILEEXT = sdl
endif
This small segment allows our Make file to use either Pixar’s shader compiler or
3delights shaderdl compiler. The line ifeq($(RENDERER),) tells the program to use
the next segment of code if the environment variable (in your shell environment)
$RENDERER is not set. If the $RENDERER variable is set and it is equal to 3delight then
we use the next block of variables. This allows us to use the same make file to compile
shaders with different compilers based on environment variables. Next, we
have a set of useful variables:
SLOFILES = $(SLFILES:%.sl=%.$(COMPILEEXT))
RELEASEDFILES = $(SLFILES:%.sl=$(RELEASEDIR)%.$(COMPILEEXT))
INCLUDEDIRS= -I../include
The SLOFILES variable is set to all the files that where stored in the SLFILES variable,
but we replace the sl by the defined COMPILEEXT variable. This will give us all
of the target files that need to be built. The RELEASEDFILES creates a string with the
path to where the files are moved after compiled. This allows us to compare the
files in the local src folder with the released files to determine if the files need to
be rebuilt. The INCLUDEDIRS defines the location of our include directory. We then
have a small set of targets:
#--- Targets ---
all: $(SLOFILES)
@echo
@echo --------COMPILE--------
@echo compile of $(SHADERTYPE) shaders done!!
@echo -----------------------
@echo
%.slo: %.sl
@echo
$(COMPILECMD) $(INCLUDEDIRS) $<
@echo
FORCE:
release: $(RELEASEDFILES)
@echo
@echo -------RELEASE--------
@echo release of $(SHADERTYPE) shaders done!!
@echo ----------------------
@echo
$(RELEASEDIR)%.slo: %.slo
@echo
@echo .................
install $^ $(RELEASEDIR)
@echo .................
@echo
all is the default rule that has every slo file in the current directory as a prerequisite.
It will print out a Compile of $(SHADERTYPE) shader done!! message when all
the files in SLOFILES are processed.
The next target is interesting because it uses the pattern matching symbol %.slo.
This will create a target for each of the files in SLOFILES. The %.sl in the prerequisites
will look for a file that has the same name as the target but ends with sl. We
then declare this command:
$(COMPILECMD) $(INCLUDEDIRS) $<
This will call proper compile command and provide the paths in $(INCLUDEDIRS) as
options and the name of the last prerequisite $< as the source file. Next we have
the release target, which depends on the $(RELEASEDFILES) values. This variable
holds the names of the files in the release directory. This way the release target
will be built only if the timestamp in the local slo file is newer than the one in the
release directory.
With the setup of the folder structure, CYGWIN, RCS or SVN, XEmacs, and
the Make files, you are ready to start writing shaders in an efficient manner. If you
are an experienced programmer, you can use whatever tools or development environment
you are most comfortable with. Just remember to compensate accordingly
throughout the book.


No comments:

Post a Comment