PDA

View Full Version : ایجاد fps دلخواه



h00manb
چهارشنبه 02 فروردین 1391, 23:51 عصر
سلام
در محیط opengl چطور میشه fps را به اندازه دلخواه درآورد به طوری که حرکتها نرم باشد و از sleep هم استفاده نشود؟

h00manb
پنج شنبه 03 فروردین 1391, 09:05 صبح
در ضمیمه یک برنامه گذاشتم که از اینترنت پیدا کردم و وقتی اجرا میشه در taskmanager مقدار مصرف cpu را 0 نشان میده , کسی میدانه چطوری این کار را کرده؟

Ananas
جمعه 04 فروردین 1391, 05:00 صبح
در محیط opengl چطور میشه fps را به اندازه دلخواه درآورد به طوری که حرکتها نرم باشد و از sleep هم استفاده نشود؟ سلام.
خوب بالاخره بسته به مقدار محاسبات برای نمایش هر فریم زمان طی شده برای هر فریمی میتونه متفاوت باشه. مخصوصا تو انیمیشن که زوایای مختلف از اجسام مختلف ممکنه در کادر دوربین قرار بگیرن و سرعت های متفاوتی در محاسبه داشته باشن مثلا ممکنه تو یه بازی کاربر به آسمون نگاه کنه که فقط یه جسم داخل کادر دوربین باشه و سرعت رندر بره بالا و ممکنه به افق ای نکاه کنه که کلی جسم تو کادر دوربین باشن و سرعت رندر بیاد پایین شما تا حدی میتونی زمان رندر رو برای فریم های سنگین کم کنی ازون بیشتر دیگه نمیشه پس باید برای برقراری توازن بین فریم ها کاری کنی که فریم های سبک، به اندازه فریم های سنگین طولانی تر بشن. پس یه راه خوب اینه که فریم ها رو محدود کنی و هر فریمی که زمان کمتری نسبت به بقیه داشت بعد از اون فریم تابع Sleep اجرا بشه.
البته به نظر من لزومی نداره که حتما فریم ها سرعت اجرای یکسانی داشته باشن تا انیمیشن نرم و روانی داشته باشیم. اگه به جای تعداد فریم ها از توابع زمان برای حرکاتها استفاده کنید انیمیشن های روان و نرمی رو میتونید بسازید.


در ضمیمه یک برنامه گذاشتم که از اینترنت پیدا کردم و وقتی اجرا میشه در taskmanager مقدار مصرف cpu را 0 نشان میده , کسی میدانه چطوری این کار را کرده؟
به نظرم چیز ساده ای از نظر محاسبات نمایشی، داره نمایش میده. پس تعجب نداره که cpu کمی مصرف کنه مخصوصا که تعداد فریم ها در ثانیه خیلی نیستن. من امتحان کردم رو سیستم من به طور معمولی بدون کلیک و درگ موس حدود 60 تا 70 فریم نمایش میده ولی وقتی کلیک میکنم و موس رو تند تند حرکت میدم تعداد فریم ها تا 150 الی 180 میرسه و این به خاطر استفاده از تایمر و تابع SetTimer هست و حلقه نمایش بی پایان نداره و به شکل رویدادی تابع نمایش اجرا میشه و چون موقع کلیک و درگ پشت سر هم پیغام های بیشتری در ثانیه به پنجره برنامه ارسال میشه موقع انجام اینکار تعداد فریم ها زیاد میشه.

h00manb
جمعه 04 فروردین 1391, 08:04 صبح
سلام.
به نظرم چیز ساده ای از نظر محاسبات نمایشی، داره نمایش میده. پس تعجب نداره که cpu کمی مصرف کنه مخصوصا که تعداد فریم ها در ثانیه خیلی نیستن.
سلام
لزوما این طوری نیست, خیلی از برنامه ها هستند که کار زیادی انجام نمیدن ولی به دلیل حلقه بی پایانی که دارند مصرف cpu را 100% می کنند.
البته این برنامه هم حلقه بی پایان داره ولی توی حلقه فقط msg ها را برسی میکنه, اما قسمت جالب اینجاست که اگر تابع RenderScene را که رسم را برعهده دارد را در این حلقه بگذارید بازهم cpu را 0 نشان میدهد.
در برنامه تابعی هست به نام ValidateRect(Wnd, NIL); که اگر این تابع را حذف کنید مصرف cpu بالا میرود, این تابع چه کار می کندکه مصرف cpu پایین می ماند؟

Ananas
جمعه 04 فروردین 1391, 15:34 عصر
در برنامه تابعی هست به نام ValidateRect(Wnd, NIL); که اگر این تابع را حذف کنید مصرف cpu بالا میرود, این تابع چه کار می کندکه مصرف cpu پایین می ماند؟نمی دونم این تابع برای چی هست فکر میکنم برای معرفی مستطیلی از پنجره برای آپدیت شدن هست ولی اینو میدونم که اجرای این تابع باعث میشه یه وقفه زمانی بین فریم ها بوجود بیاد.

البته این برنامه هم حلقه بی پایان داره ولی توی حلقه فقط msg ها را برسی میکنه, اما قسمت جالب اینجاست که اگر تابع RenderScene را که رسم را برعهده دارد را در این حلقه بگذارید بازهم cpu را 0 نشان میدهد.درسته حلقه بی پایان داره ولی چیزی هست که همه ی برنامه های پنجره ای این حلقه رو دارن ولی منظورم این نبود منظورم حلقه ای بود که بدون توقف تابع نمایش رو اجرا کنه. مثلا اگه این خط رو حذف کنی :

Timer := SetTimer(Wnd, 0, 10, @TimerProc);

بعد تو حلقه بی پایان خودش تابع RenderScene رو اجرا کنی می بینی نمایش متوقف میشه مگه اینکه با موس کلیک کنی یا همچین کاری. ولی اگه به جای GetMessage از PeekMessage استفاده کنی این اتفاق نمی افته. ببین تابع نمایش داره موقع گرفتن پیغام Paint و MoseMove اجرا میشه چون GetMessage تا موقعی که پیغام برابر WM_QUIT نباشه خروجی true داره یعنی حلقه تموم نمیشه ولی اگه پیغامی در کار نباشه دستورات داخل حلقه هم اجرا نمیشن پس دستورات نمایش فقط با گرفتن پیغام اجرا میشن به اضافه ی تایمر. پس این حلقه با اون حلقه ای که ما برای نمایش میخوایم کمی فرق میکنه. شما اگه یه حلقه بی پایان بنویسی که هیچ کاری هم انجام نده مصرف cpu بالا میره ولی اگه این وسطا یه تابعی که محاسبات cpu زیادی نداشته باشه اجرا کنی cpu فرصت میکنه نفسی بکشه. تابع GetMessage هم وقتی پیغامی نداشته باشه همین کار رو میکنه. میشه با این روش کار کرد که تایمر حذف بشه و از تابع PeekMessage استفاده بشه هر چند فریم های بیشتری اجرا کنه. ولی میشه از Sleep طوری استفاده کنید که مصرف بیش از اندازه cpu هم کنترل بشه.

h00manb
جمعه 04 فروردین 1391, 20:17 عصر
راستش یک تابع با sleep نوشتم که میشه تعدادفریم برثانیه را کنترل کرد و مصرف cpu را هم کم میکنه ولی برنامه با حرکت یکنواخت نمایش داده نمیشه و پرش داره برای همین دنبال روشی هستم که هم بشه مصرف cpu را کم کرد و هم بشه فریم را کنترل کرد به طوری که پرش هم در کار نباشه
یک سوالی دیگه ای که برام پیش امده اینکه پیغام Paint چه موقعهایی صادر میشه؟

Ananas
شنبه 05 فروردین 1391, 01:05 صبح
راستش یک تابع با sleep نوشتم که میشه تعدادفریم برثانیه را کنترل کرد و مصرف cpu را هم کم میکنه ولی برنامه با حرکت یکنواخت نمایش داده نمیشه و پرش داره برای همین دنبال روشی هستم که هم بشه مصرف cpu را کم کرد و هم بشه فریم را کنترل کرد به طوری که پرش هم در کار نباشههمون طور که تو پستهای قبلی هم عرض کردم راهش اینه که از توابع زمان سنج برای حرکات و انیمیشن استفاده کنی نه از فریم ها و تعداد فریم ها. من این مشکل رو قبلا داشتم و خیلی روش کار کردم به نظرم بهترین روش استفاده از توابع زمان هست که شما تو هر لحظه بدون توجه به تعداد فریم ها و کم و زیاد شدن سرعت با استفاده از اختلاف زمانی بین دو لحظه، حرکت انیمیشنی خودتون رو پیاده کنید. مثلا اگه قرار باشه دوربین در مدت 2 ثانیه 180 درجه بچرخه نیایم تو هر فریم به مقدار مساوی زاویه رو تغییر بدیم کهر اشتباهیه بلکه باید بر حسب زمان تو هر فریم زاویه چرخش رو مشخص کنیم. تابع Now خیلی میتونه مفید باشه اگه شما اون رو در 24 * 60 * 60 ضرب کنید زمان رو بر حسب ثانیه دارید و خیلی راحت میتونید به عنوان یک متغیر Double ازش استفاده کنید. اگرم با C++‎ کار میکنید میتونید مستقیما از توابعی مثل GetLocalTime استفاده کنید که تابع Now هم خودش همین کار رو میکنه. میتونید یک متغیر به عنوان مبدا زمانی تو برنامه داشته باشید که موقع شروع برنامه با تابع Now مقدار بگیره بعد در طول برنامه با استفاده از تفریق اختلاف زمانی رو بدست بیارید و خیلی راه های دیگه.

یک سوالی دیگه ای که برام پیش امده اینکه پیغام Paint چه موقعهایی صادر میشه؟دقیقا مطمئن نیستم باید امتحان کنیم ولی تا اونجایی که میدونم هر وقت پنجره برنامه تغییر مکان و یا اندازه بده و یا مینیمایز و یا ماکسیمایز بشه و یا چیزی بیاد جلوش که تصویرشو بپوشونه و یا از کادر مانیتور بیرون بره یا داخل بیاد و مواردی از این قبیل که تصویر پنجره تو مانیتور تغییر کنه و لازم باشه که دوباره رنگ بشه باعث میشه پیغام Painte فرستاده بشه.

h00manb
شنبه 05 فروردین 1391, 08:13 صبح
سلام
برای درگیر نشدن cpu چه راهی پیشنهاد میکنید؟
راستش روی برنامه ای کار میکنم که کاربردیه و در یک قسمتش نمایش گرافیکی داره که البته بوسیله ورودی های کاربر تغییر میکنه و سبک هم هست برای همین نمیخوام cpu بیخود درگیر بشه نمایش هم روان باشه

kochol
شنبه 05 فروردین 1391, 12:04 عصر
خوب به نظر من بهترین کار روشن کردن VSYNC هست که fps رو روی 60 نگه می داره و صحنه هم خیلی روان می شه

Ananas
شنبه 05 فروردین 1391, 12:05 عصر
سلام.
پسشنهاد میکنم فقط جاهایی که حرکت وجود داره و یا دوربین زاویش تغییر میکنه و یا پنجره نمایش توسط پنجره های دیگه پوشیده میشه بعد دوباره دیده میشه و کلا فقط جاهایی که تصویر لازمه دوباره Update بشه ، تابع نمایش رو فراخونی کن و تا میتونی از حلقه بی پایان پرهیز کنی مثلا اگه قراره دو ثانیه چیزی جابجا بشه یه حلقه در حد دو سه ثانیه اجرا بشه بعد متوقف بشه کلا صحنه تکراری رو Update نکن یه جور کار اضافی انجام دادن هست.
و یک تایمر که Interval اون مثلا 1000 هست و هر یک ثانیه یکبار نمایش رئ آپدیت میکنه چون ممکنه کاربر پنجره تنایش رو جابجا کنه و یا تغییری ناخواسته روش اتفاق بیفته. به هر حال تایمر خیلی ضروری نیست میتونه کلا نباشه.

سپول
یک شنبه 06 فروردین 1391, 21:59 عصر
اولا در برنامه تون از تابع sleep یا تایمر های message ای مثل WM_TIMER برای کنترل زمان به هیچ وجه استفاده نکنید چون آنها تایمر های کیفیت بالا (High-Res) نیستند و نتیجه اشون دقیق نیست، مخصوصا توی گیم که دقت عملیات در حد کسری از میلی ثانیه هست.

مورد دوم اینکه در اصل نرم افزار شما باید تمام قسمت های کنترلی (دوربین) و انیمیشن و فیزیکش به زمان ربط داشته باشه، اینجور احتیاج به محدود کردن frame-rate نیست. در صورتی که frame-rate بیشتر باشه در واقع دقت انیمشن هم با درون یابی بالاتر می ره و بازی نرم تر به نظر می رسه.

قبل از همه چیز از توابع تایمر high-res استفاده کنید مثل QueryPerformanceCounter و QueryPerformanceFreq
احیانا اگه احتیاج به محدود کردن frame-rate در بازی داشتید از یک لوپ نامحدود استفاده کنید تا وقتی که زمان به حد مطلوب شما رسید :



const fl64 targetFt = 1.0 / fl64(lockedFps);
while( ft < targetFt ) ft = theTimer->CalcTickTime(startTick, theTimer->GetClockTicks());


این کد frame-rate رو روی عدد دلخواه شما در هر فریم نگه می داره و در صورتی که برنامه cycle اضافی برای مصرف داشت، مصرف می کنه! تقریبا همون کاری هست که درایور کارت گرافیک موقع vsync (در thread خودش) انجام می ده ولی با تنظیم Fps دلخواه شما. این کار رو هم برای تست انجام می دهند که در fps های پایینتر بازی رو تست کنند.

vsync هم راه ساده ای هست ولی خیلی مواقع بازیباز ها اون رو روشن نمی کنند و بیشتر برای جلوگیری از tearing توی مانیتور های ضعیف تر هست. مشکل دیگه اش در کاربرد شما اینه که vsync مثلا رو 60hz نگه می داره فریم رو ولی اگه کامپیوتری داشتید که توان اجرای 60hz رو نداشت، طبیعتا Frame-rate کمتر می شه ولی همچنان 100% از cpu مصرف می شه.

البته این هم بگم که برای ادیتور و یا لپ تاپ که می خواهید مصرف برق بیاد پایین یا الکی cpu کار نکنه، بهتره کلا فریم رو با WM_TIMER یا موارد مشابه آپدیت کنید که دقت کافی نداره ولی این متد در مواقعی هست که نرم افزار به صورت idle هست و دقت زمان در اون مهم نیست، هم موقع که زمان مهم شد (مثل اجرای simulation در حین اجرا) اون موقع برنامه رو وارد لوپ کنید و Timer ویندوز رو قطع کنید.
یک راه دیگه که به ذهنم رسید اینه که لوپ و چک کردن زمان رو در thread جدا با priority پایینتر انجام بدی، و از mutex استفاده کنید. که باید خودم امتحان کنم اول
کلا برای کاریرد های اینجوری راه حل بهتری به ذهنم نمی رسه، اگه خودت راه حل بهتری پیدا کردی یا کسی راه حلی می دونست خوشحال می شیم بگه.

SeganX
دوشنبه 07 فروردین 1391, 03:48 صبح
بیشتر راه ها رو که سپول گفت. اما با توجه به اینکه ظاهرا هدف این دوستمون کم کردن کارکرد CPU هستش می تونه از توابع سیستم عامل مربوط به متوقف کردن thread استفاده کنه. مثلا میتونه یه mutex یا event که همیشه signal هستن درست کنه و بده به WaitForSingleObject و یه maxTime توش ست کنه. این تابع thread رو متوقف میکنه تا وقتی که یا event تغییر signal بده یا maxTime زمانش به برسته.

سپول
دوشنبه 07 فروردین 1391, 05:32 صبح
بیشتر راه ها رو که سپول گفت. اما با توجه به اینکه ظاهرا هدف این دوستمون کم کردن کارکرد CPU هستش می تونه از توابع سیستم عامل مربوط به متوقف کردن thread استفاده کنه. مثلا میتونه یه mutex یا event که همیشه signal هستن درست کنه و بده به WaitForSingleObject و یه maxTime توش ست کنه. این تابع thread رو متوقف میکنه تا وقتی که یا event تغییر signal بده یا maxTime زمانش به برسته.

اینی که گفتی فکر کنم فرقی با sleep نداره. راهی هم که من به ذهنم رسید همین اشکال رو داره. هم دقتش پایینه هم روند اجرای برنامه و رابط کاربر رو متوقف می کنه. دنبال راه بهتر نسبت به WM_TIMER هستیم.

h00manb
دوشنبه 07 فروردین 1391, 23:48 عصر
سلام
تابعی نداریم که مثل sleep عمل کنه ولی دقتش از میلی ثانیه بیشتر باشه؟

سپول
سه شنبه 08 فروردین 1391, 12:02 عصر
فکر نکنم. در ضمن sleep و راه های مشابه برنامه اصلی رو بلوکه می کنند و در روند بقیه قسمت های برنامه مثل رابط کاربر و پنجره ها اخلال ایجاد می کنند.