ورود

View Full Version : آموزش: شیدرهای باینری



pswin.pooya
جمعه 15 مرداد 1389, 02:25 صبح
دوستانی که از قدیم با شیدر کار میکنن حتما شیدرهای اسمبلی رو به یاد دارن که بعد از اون شیدرهای High level Language اومدند مزیت شیدرهای اسمبلی توی سرعت کامپایلشون بود که عملا داخل OpenGL بعد از ویرایش 1.5 منسوخ شدن و تنها به صورت الحاقیهای سازنده ها به روز رسانی شدن. با معرفی OpenGL 4.1 (به تازگی توسط nvidia) یه الحاقی جدید معرفی شده که باعث میشه حتی لازم نباشه که شیدرها رو کامپایل کنید و کد باینری رو به کمک اون مستقیما بار کنید. این مساله میتونه مزایای زیادی مثل بالا رفتن سرعت لود بازی و یا حفظ کد اون داشته باشه و در کنار اون میتونه مسائلی مانند ناسازگاری رو هم پیش بیاره. خوبی این الحاقی اینه که از OpenGL 2.0 به بعد ساپورت میشه و این یعنی این الحاقی با تمام درایورهای سال 2004 به بعد کار میکنه و به نظر میاد برای بازیساز ها ایده آل باشه (مخصوصا اونهایی که شیدرهای زیادی داخل بازیشون دارن)

به هر حال اگر شیدرهای زیادی ندارین و یا نگران لو رفتن کد شیدرهاتون نیستید بهتره که برای حفظ سازگاری از همون GLSL استفاده بکنبد.

درایورهای جدید OpenGL (برای کارتهای nvidia):
http://developer.nvidia.com/object/opengl_driver.html

الحاقی GL_ARB_get_program_binary (http://www.opengl.org/registry/specs/ARB/get_program_binary.txt):
http://www.opengl.org/registry/specs/ARB/get_program_binary.txt

نحوه بارگذاری این شیدرها:

GLint formats = 0;
glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &formats)
GLenum *binaryFormats = new [formats];
glGetIntegerv(GL_PROGRAM_BINARY_FORMATS, binaryFormats);

GLuint progId = glCreateProgram();
glProgramBinary(progId, binaryFormats, binary, len);

delete [] binary;

GLint success;
glGetProgramiv(progId, GL_LINK_STATUS, &success);
if (!success)
{
// Loading failed..}
.

شیوه ساخت شیدرهای باینری:


PFNGLGETPROGRAMIVPROC glGetProgramiv = 0;
glGetProgramiv = (PFNGLGETPROGRAMIVPROC)wglGetProcAddress("glGetProgramiv");
typedef void * (WINAPI * PFNGLGETPROGRAMBINARYPROC) (unsigned int,
size_t, size_t *, GLenum *, void *);
PFNGLGETPROGRAMBINARYPROC glGetProgramBinary = 0;
glGetProgramBinary =
(PFNGLGETPROGRAMBINARYPROC)wglGetProcAddress("glGetProgramBinary");
if (glGetProgramBinary && glGetProgramiv)
{
static char *vertexShader=\
"void main()"
"{"
"gl_Position = gl_Vertex;"
"}";

static char *fragmentShader=\
"void main()"
"{"
"gl_FragColor=vec4(1.0, 0.4, 1.0, 1.0);"
"}";

int p = glCreateProgram();
int v = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(v, 1, &vertexShader, 0);
glCompileShader(v);
glAttachShader(p,v);
int f = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(f, 1, &fragmentShader, 0);
glCompileShader(f);
glAttachShader(p,f);
glLinkProgram(p);
glUseProgram(p);

#define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE
#define GL_PROGRAM_BINARY_FORMATS 0x87FF
GLint formats = 0;
glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &formats);
GLint *binaryFormats = new GLint[formats];
glGetIntegerv(GL_PROGRAM_BINARY_FORMATS, binaryFormats);

#define GL_PROGRAM_BINARY_LENGTH 0x8741
GLint len=0;
GLint progId = gpu->getGpuShaderData()->m_program_id;
glGetProgramiv(progId, GL_PROGRAM_BINARY_LENGTH, &len);
u8* binary = new u8[len];
glGetProgramBinary(progId, len, NULL, (GLenum*)binaryFormats, binary);
glUseProgram(0);

FILE* fp = fopen("shader.bin", "wb");
fwrite(binary, len, 1, fp);
fclose(fp);

delete [] binary;

// Don't forget the destruction of the GLSL shader...
// ...

REZAsys
جمعه 15 مرداد 1389, 13:23 عصر
سلام

آقاپویا میتونید در مورد شیدرنویسی انجین ها هم توضیح بدید؟

pswin.pooya
جمعه 15 مرداد 1389, 14:25 عصر
آقاپویا میتونید در مورد شیدرنویسی انجین ها هم توضیح بدید؟

دقیقا میتونید بگید منظورتون چیه؟

Nima_NF
جمعه 15 مرداد 1389, 16:31 عصر
امکان کامپایل باینری shader ها سال ها هست که در DirectX و همین طور بعد ها در OpenGL ES 2 وجود داشته است. اما موضوع عجیب این هست که چرا بعد از این همه مدت در OpenGL 4.1 این قابلیت عرضه شد (و الآن هم تحت یک extension برای قبلی ها، چون وابسته به سخت افزارهای 4 نیست)

برنامه نویسان باید دقت داشته باشند که کامپایل باینری همواره یک گزینه کاملا مناسب نبوده است، چون قرار هست shader های ما با همه سخت افزارها و سیستم های هدف سازگاری داشته باشد، که کامپایل باینری در برخی موارد می توانند دردسرساز شوند. (الآن تقریبا روی PC و OS های مشابه، افراد نظرات مختلف در مورد استفاده از آن دارند)

به عنوان مثال، این قابلیت گزینه ای مناسب در OpenGL ES 2 برای تلفن های همراه بوده است، چون در آنجا performance بسیار اهمیت دارد و پیشنهاد می شود که کامپایل شده عمل کنیم تا افزایش کارآیی در runt-time داشته باشیم و مهم تر اینکه در آنجا درایور و سایر موارد درکار نیست.
اما در سخت افزارهای قدرتمند کنونی باید کمی دقیق تر عمل کنیم و قبل از استفاده از آن ها حتما خوب همه زوایا را بررسی کنیم و بهینه برنامه را بنویسیم، چون خطا و کامپایل نشدن در سیستم کاربر (به دلایل درایور یا سخت افزار)، برابر خواهد شد با اجرا نشدن برنامه.

pswin.pooya
جمعه 15 مرداد 1389, 17:46 عصر
امکان کامپایل باینری shader ها سال ها هست که در DirectX و همین طور بعد ها در OpenGL ES 2 وجود داشته است. اما موضوع عجیب این هست که چرا بعد از این همه مدت در OpenGL 4.1 این قابلیت عرضه شد (و الآن هم تحت یک extension برای قبلی ها، چون وابسته به سخت افزارهای 4 نیست)

سلام
شما هم میتونسید قبل از این تحت عنوان یکسری از الحاقیهای nv اینکار رو انجام بدید اما مساله مهم اینجاست که حالا توی OpenGL 4.1 جزء هسته شده و توی ویرایشهای دیگه جزء الحاقیهای ARB شده که تمام سازندهها مجبور به رعایت و هماهنگ کردن هستند.

برای خود من هم این مساله سوال بود که چرا امکان لود کامپایل شده وجود نداره توی کتاب OpenGL Shading language (ویرایش دوم) ذکر شده که سیستم GLSL به شکلی طراحی شده که حداکثر سازگاری رو داشته باشه (مثل خود OpenGL) به همین منظور هم تنها امکان کامپایل رو فراهم کردند و حتی شیدرهای اسمبلی رو هم از رده خارج کردند تا سازگاری به حداکثر برسه. من فکر میکنم به همین دلیل هستش که تا خالا ARB این مورد رو تایید نکرده بود و حتی بعد از تاییدش هم وبلاگها و برنامه نویسها واکنش نشون داند و اون رو یه الحاقی غیر مفید معرفی کردند. بیشترین نگرانی اونها هم عدم ناسازگاری هستش و اشاره کردن که با توجه به سرعت بالای کامپایل شیدرهای GLSL نیازی به این الحاقی نیستش.

REZAsys
جمعه 15 مرداد 1389, 17:59 عصر
دقیقا میتونید بگید منظورتون چیه؟
منظورم شیدرهایی که تو انجین استفاده میشه.
چند تا شیدر تو وبلاگم گذاشتم:
http://3dgamestudio.blogfa.com/

kochol
جمعه 15 مرداد 1389, 18:22 عصر
سلام
برای برطرف کردن مشکل سازگاری می شه برای دفعه اول که برنامه اجرا می شه تمام شیدرها رو کامپایل کنه و دفعه بعدی دیگه از کامپایل شدش استفاده کنه

pswin.pooya
جمعه 15 مرداد 1389, 20:12 عصر
برای برطرف کردن مشکل سازگاری می شه برای دفعه اول که برنامه اجرا می شه تمام شیدرها رو کامپایل کنه و دفعه بعدی دیگه از کامپایل شدش استفاده کنه

ایده خوبیه


منظورم شیدرهایی که تو انجین استفاده میشه.

مثل شیدرهای معمولی هستند فقط بهتره که برای انجین بهینه بشن. مثلا تمام اونها از متغییر in_Vertex برای مکان استفاده بکنن و بعدش انجین اون رو به صورت یه متغییر eviroment تعریف کنه تا bind کمتر بشه.

معمولا انجینها قواعد خاصی رو برای نوشتن فراهم میکنن مثلا داخل Ogre متغییرها رو داخل فایلهای متریال تعریف میکنی (مقدارشون رو). داخل kge با متد set تابع انتصاب رو صدا میکنی و یا مثلا داخل dge حتما باید شیدر داخل چند سه تا تکسچر بنویسه (رنگ، نرمال و ... ).

این مساله کاملا وابسته به سازنده انجین هست.

Nima_NF
شنبه 16 مرداد 1389, 01:18 صبح
سلام
برای برطرف کردن مشکل سازگاری می شه برای دفعه اول که برنامه اجرا می شه تمام شیدرها رو کامپایل کنه و دفعه بعدی دیگه از کامپایل شدش استفاده کنه
اصلی ترین هدف binary compile که به شکل API ارائه می شود همین هست که شما در سیستم هدف عمل کامپایل را یک مرتبه انجام دهید و cache کنید، در غیر این صورت ابزار مثل fxc عرضه می شد نه API.

مشکل اینجاست که برنامه نویس باید این موضوع را در نظر بگیرد که تغییر درایور کارت گرافیک و یا تغییر خود کارت گرافیک (GPU) در سیستم فرد را باید همواره در نظر بگیرد.
یعنی باید با تغییر درایور (تغییر در هر ماه) و تغییر کارت گرافیک هنگام شروع برنامه از روی سورس کد مجددا کار کامپایل را انجام دهد تا در صورت تغییرات اساسی، برنامه/بازی درست عمل کند.

مثلا در بسیاری از Laptop ها 2 تا کارت گرافیک وجود دارد (یکی Intel و دیگری ATI یا Nvidia)، افراد هنگام شروع OS برای مصرف کمتر باطری Intel را انتخاب می کنند، حال اگر با Nvidia کامپایل شده باشد باید این کامپایل باینری هنگام شروع، مجددا انجام شود. حال تعویض خود کارت گرافیک روی PC Desktop که بحث دیگری هست.

این ها گوشه ای از مواردی هست که در مورد عدم سازگاری ها مطرح می شود و در صورتی که توسط برنامه نویس در نظر گرفته شود تا جای ممکن می توان ناسازگاری ها را از بین برد.