गेम लॉजिक थ्रेड और रेंडरिंग थ्रेड के बीच सिंक्रोनाइज़ेशन


16

कैसे एक अलग खेल तर्क और प्रतिपादन करता है? मुझे पता है कि यहां पहले से ही सवाल हैं जो वास्तव में पूछ रहे हैं लेकिन जवाब मेरे लिए संतोषजनक नहीं हैं।

अब तक जो मैं समझता हूं कि उन्हें अलग-अलग थ्रेड में अलग करने की बात इतनी है कि गेम लॉजिक अगले vsync के लिए प्रतीक्षा करने के बजाय तुरंत अगले टिक के लिए चलना शुरू कर सकता है, जहां अंत में स्वैबफ़र कॉल से रिटर्न इसकी ब्लॉकिंग को रोक रहा है।

लेकिन विशेष रूप से गेम लॉजिक थ्रेड और रेंडरिंग थ्रेड के बीच दौड़ की स्थिति को रोकने के लिए डेटा संरचनाओं का उपयोग किया जाता है। संभवत: प्रदान करने वाले धागे को विभिन्न चरों तक पहुंच की जरूरत है ताकि यह पता लगाया जा सके कि गेम लॉजिक इन समान चर को कैसे अपडेट कर सकता है।

क्या इस समस्या से निपटने के लिए एक वास्तविक मानक तकनीक है। शायद गेम लॉजिक के प्रत्येक निष्पादन के बाद रेंडरिंग थ्रेड द्वारा आवश्यक डेटा को कॉपी करें। जो भी समाधान सिंक्रनाइज़ेशन का ओवरहेड है या जो भी हो सब कुछ केवल थ्रेडेड चलाने से कम है?


1
मुझे बस एक लिंक से नफरत है, लेकिन मुझे लगता है कि यह बहुत अच्छा पढ़ा गया है और इसे आपके सभी सवालों का जवाब देना चाहिए: altdevblogaday.com/2011/07/03/threading-and-your-game-loop
Roy T.

एक अन्य लिंक: software.intel.com/en-us/articles/…
Chewy Gumball

1
वे लिंक विशिष्ट अंतिम परिणाम देते हैं जो एक को पसंद करेंगे, लेकिन इसे कैसे करना है, इसके बारे में विस्तार से नहीं बताएं। क्या आप प्रत्येक फ्रेम या कुछ और पूरे दृश्य ग्राफ की नकल करेंगे? चर्चाएँ बहुत उच्च स्तर की और अस्पष्ट हैं।
user782220

मुझे लगा कि लिंक प्रत्येक मामले में कितना राज्य की नकल है के बारे में स्पष्ट रूप से स्पष्ट थे। जैसे। (1 लिंक से) "एक बैच में एक फ्रेम बनाने के लिए आवश्यक सभी जानकारी होती है, लेकिन इसमें कोई अन्य गेम स्थिति नहीं होती है।" या (2 लिंक से) "डेटा को अभी भी साझा करने की आवश्यकता है, लेकिन अब प्रत्येक सिस्टम के बजाय एक सामान्य डेटा स्थान तक पहुँचने के लिए कहने के लिए, स्थिति या अभिविन्यास डेटा प्राप्त करें, प्रत्येक सिस्टम की अपनी प्रतिलिपि है" (विशेष रूप से 3.2.2 - राज्य देखें प्रबंधक)
DMGregory

जिसने भी लिखा है कि इंटेल लेख को यह पता नहीं लगता है कि शीर्ष स्तर का थ्रेडिंग एक बहुत बुरा विचार है। कोई भी ऐसा कुछ नहीं करता है जो मूर्खतापूर्ण हो। अचानक पूरे एप्लिकेशन को विशेष चैनलों के माध्यम से संवाद करना पड़ता है और हर जगह ताले और / या विशाल समन्वित राज्य एक्सचेंज होते हैं। यह उल्लेख नहीं करने के लिए कि कोई भी नहीं बता रहा है कि भेजे गए डेटा को कब संसाधित किया जाएगा इसलिए कोड क्या करता है इसके बारे में तर्क करना बहुत कठिन है। एक बिंदु पर संबंधित दृश्य डेटा (ref.counted संकेत के रूप में अपरिवर्तनीय, उत्परिवर्तनीय -) की प्रतिलिपि बनाना बहुत आसान है और सबसिस्टम को इसे छाँटना देना चाहिए, हालांकि यह चाहता है।
स्नेक 5

जवाबों:


1

मैं उसी चीज पर काम कर रहा हूं। अतिरिक्त चिंता यह है कि OpenGL (और मेरी जानकारी के लिए, OpenAL), और कई अन्य हार्डवेयर इंटरफेस, प्रभावी रूप से राज्य मशीनें हैं जो कई थ्रेड्स द्वारा कॉल किए जाने के साथ-साथ नहीं मिलती हैं। मुझे नहीं लगता कि उनका व्यवहार भी परिभाषित किया गया है, और LWJGL (संभवतः JOGL) के लिए भी यह अक्सर एक अपवाद फेंकता है।

मैंने जो किया, वह थ्रेड का एक क्रम बना रहा था जो एक विशिष्ट इंटरफ़ेस को लागू करता था, और उन्हें एक नियंत्रण ऑब्जेक्ट के स्टैक पर लोड करता था। जब उस ऑब्जेक्ट को गेम को बंद करने के लिए एक संकेत मिला, तो यह प्रत्येक थ्रेड के माध्यम से चलेगा, एक कार्यान्वित ceaseOperations () विधि को कॉल करेगा, और खुद को बंद करने से पहले उनके बंद होने की प्रतीक्षा करेगा। यूनिवर्सल डेटा, जो ध्वनि, ग्राफिक्स या किसी अन्य डेटा को रेंडर करने के लिए प्रासंगिक हो सकता है, उन वस्तुओं के अनुक्रम में रखा जाता है जो अस्थिर हैं, या सार्वभौमिक रूप से सभी थ्रेड्स के लिए उपलब्ध हैं, लेकिन थ्रेड मेमोरी में कभी भी नहीं रखे गए हैं। वहाँ एक मामूली प्रदर्शन जुर्माना है, लेकिन ठीक से इस्तेमाल किया गया है, इसने मुझे लचीले ढंग से एक धागा, ग्राफिक्स को दूसरे, भौतिकी को एक और, और अभी तक पारंपरिक (और खूंखार) में बांधने के बिना "गेम लूप" के लिए ऑडियो असाइन करने की अनुमति दी है।

तो एक नियम के रूप में, सभी ओपन स्क्रीन ग्राफिक्स थ्रेड के माध्यम से जाते हैं, सभी ओडल ऑडियो थ्रेड के माध्यम से, सभी इनपुट इनपुट थ्रू के माध्यम से, और जो सभी नियंत्रण थ्रेड को व्यवस्थित करने की आवश्यकता होती है, वह थ्रेड प्रबंधन है। गेम स्टेट गेमस्टोर क्लास में आयोजित किया जाता है, जिसे वे सभी देख सकते हैं जैसे उन्हें ज़रूरत है। अगर मैं कभी यह तय करता हूं कि, जॉयल ने डेट किया है और मैं इसके बजाय जावासाउंड के नए संस्करण का उपयोग करना चाहता हूं, तो मैं सिर्फ ऑडियो के लिए एक अलग धागा लागू करता हूं।

उम्मीद है कि आप देख रहे हैं कि मैं क्या कह रहा हूं, मेरे पास इस परियोजना पर पहले से ही कुछ हजार लाइनें हैं। यदि आप चाहते हैं कि मैं कोशिश करूं और एक साथ एक नमूना बनाऊं, तो मैं देखूंगा कि मैं क्या कर सकता हूं।


आखिरकार आपके सामने जो समस्या है वह यह है कि यह सेटअप विशेष रूप से मल्टी-कोर मशीन पर अच्छा नहीं है। हां, एक ऐसे खेल के पहलू हैं जो आम तौर पर अपने स्वयं के धागे में सबसे अच्छे रूप में परोसे जाते हैं जैसे कि ऑडियो लेकिन गेम लूप के शेष भाग का वास्तव में थ्रेड पूल कार्यों के साथ क्रमिक रूप से प्रबंधित किया जा सकता है। यदि आपका थ्रेड पूल आत्मीयता के मुखौटे का समर्थन करता है, तो आप आसानी से कह सकते हैं कि एक ही थ्रेड पर निष्पादित किए जाने वाले कार्यों को रेंडर करें और अपने थ्रेड शेड्यूलर को थ्रेड वर्क कतारों का प्रबंधन करें और आवश्यक रूप से मल्टी-थ्रेडिंग और मल्टी-कोर समर्थन देने के लिए चोरी का काम करें।
नरोस

1

आमतौर पर, ग्राफिक्स रेंडरिंग से संबंधित तर्क (और उनके शेड्यूल, और जब वे चलाने वाले होते हैं, आदि) एक अलग थ्रेड द्वारा नियंत्रित किया जाता है। हालाँकि वह धागा आपके गेम लूप (और गेम) को विकसित करने के लिए आपके द्वारा उपयोग किए जा रहे प्लेटफॉर्म द्वारा पहले से ही लागू (ऊपर और चल रहा है) है।

तो गेम लूप प्राप्त करने के लिए जहां ग्राफिक्स लॉजिक शेड्यूल से स्वतंत्र रूप से गेम लॉजिक अपडेट होता है, आपको अतिरिक्त थ्रेड बनाने की आवश्यकता नहीं है, आप बस उक्त ग्राफिक्स अपडेट के लिए पहले से मौजूद थ्रेड में टैप करें।

यह इस बात पर निर्भर करता है कि आप किस प्लेटफॉर्म का उपयोग कर रहे हैं। उदाहरण के लिए:

  • यदि आप इसे अधिकांश खुले जीएल संबंधित प्लेटफार्मों ( GLUT for C / C ++ , जावा के लिए JOLG , Android के OpenGL ES संबंधित कार्रवाई ) में कर रहे हैं, तो वे आमतौर पर आपको एक विधि / कार्य देंगे जो समय-समय पर रेंडरिंग थ्रेड द्वारा कहा जाता है, और जिसे आप अपने गेम लूप में एकीकृत कर सकते हैं (गैमोलेप के पुनरावृत्तियों को बनाए बिना उस पद्धति पर निर्भर होने पर)। C का उपयोग करते हुए GLUT के लिए, आप कुछ इस तरह करते हैं:

    glutDisplayFunc (myFunctionForGraphicsDrawing);

    glutIdleFunc (myFunctionForUpdatingState);

  • जावास्क्रिप्ट में, आप वेब वर्कर्स का उपयोग कर सकते हैं क्योंकि कोई मल्टी-थ्रेडिंग नहीं है (कि आप प्रोग्राममेट तक पहुंच सकते हैं) , आप एक नए ग्राफिक्स रेंडरिंग को शेड्यूल किए जाने पर अधिसूचित होने के लिए "requestAnimationFrame" तंत्र का भी उपयोग कर सकते हैं, और अपने खेल के अनुसार स्टेट्स अपडेट करें। ।

मूल रूप से आप जो चाहते हैं वह एक मिश्रित चरण गेम लूप है: आपके पास कुछ कोड है जो गेम स्थिति को अपडेट करता है, और जिसे आपके गेम के मुख्य थ्रेड के अंदर कहा जाता है, और आप समय-समय पर टैप करना चाहते हैं (या द्वारा वापस बुलाया जा सकता है) पहले से ही मौजूदा ग्राफिक्स सिर के लिए धागा प्रदान करते हैं जब यह ग्राफिक्स को ताज़ा करने का समय होता है।


0

जावा में "सिंक्रनाइज़" कीवर्ड है, जो आपके द्वारा इसे थ्रेडसेफ़ बनाने के लिए पास किए गए चर को लॉक करता है। C ++ में आप म्यूटेक्स का उपयोग करके एक ही चीज प्राप्त कर सकते हैं। उदाहरण के लिए:

जावा:

synchronized(a){
    //code using a
}

सी ++:

mutex a_mutex;

void f(){
    a_mutex.lock();
    //code using a
    a_mutex.unlock();
}

वैरिएबल लॉक करने से यह सुनिश्चित हो जाता है कि कोड का पालन करते समय वे बदल नहीं जाते हैं, इसलिए चर आपके अपडेट किए गए थ्रेड द्वारा परिवर्तित नहीं होते हैं जबकि आप उन्हें रेंडर कर रहे हैं (वास्तव में वे बदलते हैं, लेकिन आपके रेंडर थ्रेड के दृष्टिकोण से वे डॉन नहीं करते हैं ' टी)। आपको जावा में सिंक्रनाइज़ किए गए कीवर्ड के साथ देखना होगा, क्योंकि यह केवल यह सुनिश्चित करता है कि सूचक चर / ऑब्जेक्ट में परिवर्तित नहीं होता है। सूचक को बदले बिना विशेषताएँ अभी भी बदल सकते हैं। इसके लिए चिंतन करने के लिए, आप ऑब्जेक्ट को स्वयं कॉपी कर सकते हैं या उस ऑब्जेक्ट की सभी विशेषताओं पर सिंक्रनाइज़ कॉल कर सकते हैं जिसे आप बदलना नहीं चाहते हैं।


1
Mutexes आवश्यक रूप से यहां का जवाब नहीं है क्योंकि ओपी को न केवल गेम लॉजिक और रेंडरिंग को डिकूप करने की आवश्यकता होगी, बल्कि वे किसी एक थ्रेड की क्षमता को आगे बढ़ने से रोकने से भी बचना चाहते हैं, चाहे वह अन्य थ्रेड में हो, जहां प्रोसेसिंग हो सकती है। पाश।
Naros

0

मैंने आमतौर पर लॉजिक / थ्रेड कम्युनिकेशन को हैंडल करने के लिए जो देखा है, वह आपके डेटा को ट्रिपल बफर करना है। इस तरह से रेंडर थ्रेड ने कहा है कि बकेट 0 इसे पढ़ता है। तर्क धागा बाल्टी 1 का उपयोग करता है क्योंकि यह अगले फ्रेम के लिए इनपुट स्रोत है और फ्रेम डेटा को बाल्टी 2 में लिखता है।

सिंक पॉइंट्स पर, तीन बकेट्स में से प्रत्येक के सूचक को स्वैप किया जाता है ताकि अगले फ्रेम का डेटा रेंडर थ्रेड को दिया जाए और लॉजिक थ्रेड आगे जारी रह सके।

लेकिन रेंडरिंग और लॉजिक को उनके संबंधित थ्रेड में विभाजित करने का कोई कारण नहीं है। आप वास्तव में गेम लूप सीरियल रख सकते हैं और अपने रेंडर फ्रेम रेट को इंटरपोलेशन का उपयोग करके लॉजिक स्टेप से डिकम्पोज कर सकते हैं। इस तरह के सेटअप का उपयोग करके मल्टी-कोर प्रोसेसर का लाभ उठाने के लिए जहां आपके पास एक थ्रेड पूल होगा जो कार्यों के समूहों पर काम करता है। ये कार्य केवल 0 से 100 तक की वस्तुओं की सूची को पुन: व्यवस्थित करने के बजाय ऐसी चीजें हो सकते हैं, आप सूची को 20 के 5 बकेट में 5 थ्रेड में 5 बार थ्रेड को प्रभावी ढंग से बढ़ाते हैं, लेकिन मुख्य लूप को जटिल नहीं करते।


0

यह एक पुरानी पोस्ट है, लेकिन यह अभी भी पॉप अप करता है इसलिए यहां मेरे 2 सेंट जोड़ना चाहता था।

पहली सूची डेटा जिसे UI / संग्रह थ्रेड बनाम लॉजिक थ्रेड में संग्रहित किया जाना चाहिए। UI थ्रेड में आप 3D मेष, बनावट, प्रकाश जानकारी और स्थिति / रोटेशन / दिशा डेटा की एक प्रति शामिल कर सकते हैं।

गेम लॉजिक थ्रेड में आपको गेम ऑब्जेक्ट आकार की आवश्यकता हो सकती है 3 डी, प्रिमिटिंग (गोला, घन), सरलीकृत 3 डी मेष डेटा (उदाहरण के लिए विस्तृत टकराव के लिए), सभी विशेषताओं जो आंदोलन / व्यवहार को प्रभावित करते हैं, जैसे ऑब्जेक्ट वेग, टर्न अनुपात, आदि। और भी स्थिति / रोटेशन / दिशा डेटा।

यदि आप दो सूचियों की तुलना करते हैं, तो आप देख सकते हैं कि स्थिति / रोटेशन / दिशा डेटा की केवल प्रतिलिपि को तर्क से यूआई थ्रेड में पारित करने की आवश्यकता है। आपको यह निर्धारित करने के लिए किसी तरह के सहसंबंध आईडी की आवश्यकता हो सकती है कि यह डेटा किस गेम ऑब्जेक्ट का है।

आप यह कैसे करते हैं यह इस बात पर निर्भर करता है कि आप किस भाषा में काम कर रहे हैं। Scala में आप Java / C ++ में किसी तरह का लॉकिंग / सिंक्रोनाइजेशन में Software Transactional Memory का उपयोग कर सकते हैं। मुझे अपरिवर्तनीय डेटा पसंद है इसलिए मैं हर अपडेट के लिए नई अपरिवर्तनीय वस्तु वापस करता हूं। यह थोड़ा मेमोरी बेकार है लेकिन आधुनिक कंप्यूटरों के साथ यह इतनी बड़ी बात नहीं है। फिर भी यदि आप साझा डेटा संरचनाओं को लॉक करना चाहते हैं तो आप कर सकते हैं। दो या अधिक बफ़र्स का उपयोग करके जावा में एक्सचेंज क्लास की जाँच करें, इससे चीजों को गति मिल सकती है।

इससे पहले कि आप थ्रेड्स के बीच डेटा साझा करना शुरू कर दें, आपको वास्तव में कितना डेटा पास करना है। यदि आपके पास अपने 3 डी स्थान को विभाजित करने वाला एक ऑक्ट्री है, और आप कुल 10 वस्तुओं में से 5 गेम ऑब्जेक्ट देख सकते हैं, भले ही आपके तर्क को सभी 10 को अपडेट करने की आवश्यकता हो, आपको केवल 5 को फिर से देखने की आवश्यकता है जो आप देख रहे हैं। अधिक पढ़ने के लिए इस ब्लॉग को देखें: http://gameprogrammingpatterns.com/game-loop.html यह सिंक्रनाइज़ेशन के बारे में नहीं है, लेकिन यह दिखाता है कि गेम लॉजिक को प्रदर्शन से कैसे अलग किया जाता है और आपको (एफपीएस) को पार करने के लिए किन चुनौतियों की आवश्यकता है। उम्मीद है की यह मदद करेगा,

निशान

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.