सिंक्रनाइज़ेशन फ़्रेमवर्क, Android ग्राफ़िक्स सिस्टम में अलग-अलग एसिंक्रोनस कार्रवाइयों के बीच की डिपेंडेंसी के बारे में साफ़ तौर पर बताता है. फ़्रेमवर्क, एक ऐसा एपीआई उपलब्ध कराता है जिसकी मदद से कॉम्पोनेंट यह बता सकते हैं कि बफ़र कब रिलीज़ किए जाते हैं. यह फ़्रेमवर्क, सिंक्रोनाइज़ेशन प्रिमिटिव को कर्नल से यूज़रस्पेस और यूज़रस्पेस प्रोसेस के बीच पास करने की अनुमति भी देता है.
उदाहरण के लिए, कोई ऐप्लिकेशन, जीपीयू में किए जाने वाले काम को कतार में लगा सकता है. इसके बाद, GPU उस इमेज को ड्रॉ करना शुरू कर देता है. इमेज को अब तक मेमोरी में नहीं डाला गया है. हालांकि, बफ़र पॉइंटर को विंडो कंपोज़िटर को पास कर दिया जाता है. साथ ही, एक फ़ेंस भी पास किया जाता है, जिससे यह पता चलता है कि GPU का काम कब पूरा होगा. विंडो कंपोज़िटर, समय से पहले प्रोसेसिंग शुरू कर देता है और डिसप्ले कंट्रोलर को काम सौंप देता है. इसी तरह, सीपीयू का काम समय से पहले पूरा हो जाता है. जीपीयू के काम पूरा करने के बाद, डिसप्ले कंट्रोलर तुरंत इमेज दिखाता है.
सिंक्रनाइज़ेशन फ़्रेमवर्क की मदद से, हार्डवेयर बनाने वाली कंपनियां अपने हार्डवेयर कॉम्पोनेंट में सिंक्रनाइज़ेशन संसाधनों का इस्तेमाल कर सकती हैं. आखिर में, फ़्रेमवर्क, ग्राफ़िक्स पाइपलाइन की जानकारी देता है, ताकि डीबग करने में मदद मिल सके.
एक्सप्लिसिट सिंक्रोनाइज़ेशन
एक्सप्लिसिट सिंक्रनाइज़ेशन की मदद से, ग्राफ़िक बफ़र के प्रोड्यूसर और कंज्यूमर, यह सिग्नल दे सकते हैं कि उन्होंने बफ़र का इस्तेमाल कब बंद कर दिया है. एक्सप्लिसिट सिंक्रनाइज़ेशन को कर्नल-स्पेस में लागू किया जाता है.
डेटा को साफ़ तौर पर सिंक करने के ये फ़ायदे हैं:
- डिवाइसों के बीच बिहेवियर में कम अंतर
- डीबग करने की बेहतर सुविधा
- टेस्टिंग की बेहतर मेट्रिक
सिंक फ़्रेमवर्क में तीन तरह के ऑब्जेक्ट होते हैं:
sync_timelinesync_ptsync_fence
sync_timeline
sync_timeline एक ऐसी टाइमलाइन है जो लगातार बढ़ती है. वेंडर को इसे हर ड्राइवर इंस्टेंस के लिए लागू करना चाहिए. जैसे, GL कॉन्टेक्स्ट, डिसप्ले कंट्रोलर या 2D ब्लिटर. sync_timeline counts
किसी खास हार्डवेयर के लिए कर्नल को सबमिट किए गए कामों की संख्या.
sync_timeline कार्रवाइयों के क्रम को तय करता है और हार्डवेयर के हिसाब से लागू करने की सुविधा देता है.
sync_timeline लागू करते समय इन दिशा-निर्देशों का पालन करें:
- डीबग करने की प्रोसेस को आसान बनाने के लिए, सभी ड्राइवर, टाइमलाइन, और फ़ेंस के लिए काम के नाम दें.
- टाइमलाइन में
timeline_value_strऔरpt_value_strऑपरेटर लागू करें, ताकि डीबग करने के आउटपुट को आसानी से पढ़ा जा सके. - अगर ज़रूरत हो, तो उपयोगकर्ताओं की स्पेस लाइब्रेरी, जैसे कि GL लाइब्रेरी को निजी टाइमलाइन डेटा का ऐक्सेस देने के लिए, फ़िल
driver_dataको लागू करें.data_driverकी मदद से, वेंडरsync_fenceऔरsync_ptsके बारे में जानकारी पास कर सकते हैं. इससे, इन जानकारी के आधार पर कमांड लाइनें बनाई जा सकती हैं. - यह उपयोगकर्ताओं को, फ़ेंस को साफ़ तौर पर बनाने या सिग्नल देने की अनुमति नहीं देता. सिग्नल/फ़ेंस को साफ़ तौर पर बनाने से, सेवा से इनकार करने वाला हमला होता है. इससे पाइपलाइन की सुविधा काम करना बंद कर देती है.
sync_timeline,sync_ptयाsync_fenceएलिमेंट को साफ़ तौर पर ऐक्सेस न करें. एपीआई में सभी ज़रूरी फ़ंक्शन उपलब्ध होते हैं.
sync_pt
sync_pt, sync_timeline पर मौजूद एक वैल्यू या पॉइंट है. किसी पॉइंट की तीन स्थितियां होती हैं: चालू, सिग्नल दिया गया, और गड़बड़ी. पॉइंट, चालू स्थिति में शुरू होते हैं और सिग्नल या गड़बड़ी की स्थितियों में बदल जाते हैं. उदाहरण के लिए, जब किसी इमेज कंज्यूमर को बफ़र की ज़रूरत नहीं होती है, तो sync_pt का सिग्नल दिया जाता है. इससे इमेज प्रोड्यूसर को पता चलता है कि बफ़र में फिर से लिखा जा सकता है.
sync_fence
sync_fence, sync_pt वैल्यू का कलेक्शन होता है. इसमें अक्सर sync_timeline पैरंट अलग-अलग होते हैं. जैसे, डिसप्ले कंट्रोलर और जीपीयू के लिए. sync_fence, sync_pt, और sync_timeline मुख्य प्रिमिटिव हैं. इनका इस्तेमाल ड्राइवर और यूज़रस्पेस, अपनी डिपेंडेंसी के बारे में बताने के लिए करते हैं. जब फ़ेंस का सिग्नल मिलता है, तो फ़ेंस से पहले दिए गए सभी निर्देश पूरे हो जाते हैं. ऐसा इसलिए होता है, क्योंकि कर्नल ड्राइवर या हार्डवेयर ब्लॉक, निर्देशों को क्रम से लागू करता है.
सिंक फ़्रेमवर्क की मदद से, कई उपभोक्ता या प्रोड्यूसर यह सिग्नल दे सकते हैं कि उन्होंने बफ़र का इस्तेमाल कर लिया है. साथ ही, वे एक फ़ंक्शन पैरामीटर के साथ निर्भरता की जानकारी दे सकते हैं. फ़ेंस, फ़ाइल डिस्क्रिप्टर पर आधारित होते हैं. इन्हें कर्नल स्पेस से यूज़रस्पेस में पास किया जाता है. उदाहरण के लिए, किसी फ़ेंस में दो sync_pt वैल्यू हो सकती हैं. इनसे यह पता चलता है कि दो अलग-अलग इमेज कंज्यूमर ने बफ़र को कब पढ़ना बंद किया. जब फ़ेंस का सिग्नल मिलता है, तो इमेज बनाने वाले लोगों को पता चल जाता है कि दोनों
उपयोगकर्ताओं ने इमेज देख ली है.
sync_pt वैल्यू जैसे फ़ेंस, डिफ़ॉल्ट रूप से चालू होते हैं. इनके पॉइंट की स्थिति के आधार पर, इनकी स्थिति बदलती है. अगर सभी sync_pt वैल्यू को सिग्नल मिल जाता है, तो sync_fence को सिग्नल मिल जाता है. अगर एक sync_pt में गड़बड़ी होती है, तो पूरे sync_fence में गड़बड़ी होती है.
sync_fence की सदस्यता में बदलाव नहीं किया जा सकता. किसी फ़ेंस में एक से ज़्यादा पॉइंट पाने के लिए, फ़ेंस को मर्ज किया जाता है. इसमें दो अलग-अलग फ़ेंस के पॉइंट को तीसरे फ़ेंस में जोड़ा जाता है.
अगर उनमें से एक पॉइंट को ओरिजनल फ़ेंस में सिग्नल किया गया था और दूसरे को नहीं, तो तीसरे फ़ेंस को भी सिग्नल नहीं किया जाएगा.
एक्सप्लिसिट सिंक्रनाइज़ेशन लागू करने के लिए, यह जानकारी दें:
- यह कर्नल-स्पेस सबसिस्टम है, जो किसी हार्डवेयर ड्राइवर के लिए सिंक फ़्रेमवर्क लागू करता है. फ़ेंस के बारे में जानकारी रखने वाले ड्राइवर, आम तौर पर ऐसे ड्राइवर होते हैं जो हार्डवेयर कंपोज़र (एचडब्ल्यूसी) को ऐक्सेस करते हैं या उससे कम्यूनिकेट करते हैं.
मुख्य फ़ाइलों में ये शामिल हैं:
- कोर इंप्लीमेंटेशन:
kernel/common/include/linux/sync.hkernel/common/drivers/base/sync.c
kernel/common/Documentation/sync.txtपर दस्तावेज़platform/system/core/libsyncमें कर्नल स्पेस से कम्यूनिकेट करने के लिए लाइब्रेरी
- कोर इंप्लीमेंटेशन:
- वेंडर को हार्डवेयर ऐब्स्ट्रैक्शन लेयर (एचएएल) में
validateDisplay()औरpresentDisplay()फ़ंक्शन के पैरामीटर के तौर पर, सही सिंक्रनाइज़ेशन फ़ेंस देने होंगे. - फ़ेंस से जुड़े दो GL एक्सटेंशन (
EGL_ANDROID_native_fence_syncऔरEGL_ANDROID_wait_sync) और ग्राफ़िक्स ड्राइवर में फ़ेंस की सुविधा.
केस स्टडी: डिसप्ले ड्राइवर लागू करना
सिंक्रनाइज़ेशन फ़ंक्शन के साथ काम करने वाले एपीआई का इस्तेमाल करने के लिए, डिसप्ले ड्राइवर डेवलप करें. इसमें डिसप्ले बफ़र फ़ंक्शन होना चाहिए. सिंक्रनाइज़ेशन फ़्रेमवर्क के मौजूद होने से पहले, यह फ़ंक्शन dma-buf ऑब्जेक्ट पाता था, उन बफ़र को डिसप्ले पर रखता था, और बफ़र के दिखने के दौरान ब्लॉक करता था. उदाहरण के लिए:
/* * assumes buffer is ready to be displayed. returns when buffer is no longer on * screen. */ void display_buffer(struct dma_buf *buffer);
सिंक करने के फ़्रेमवर्क के साथ, display_buffer फ़ंक्शन ज़्यादा जटिल होता है. डिसप्ले पर बफ़र डालते समय, बफ़र को एक फ़ेंस से जोड़ा जाता है. इससे पता चलता है कि बफ़र कब तैयार होगा. फ़ेंस हट जाने के बाद, काम को लाइन में लगाया जा सकता है और शुरू किया जा सकता है.
फ़ेंस के हट जाने के बाद, काम को लाइन में लगाने और शुरू करने से कुछ भी ब्लॉक नहीं होता. आपको तुरंत अपना फ़ेंस वापस मिल जाता है. इससे यह पता चलता है कि बफ़र कब डिसप्ले से हट जाएगा. बफ़र को कतार में लगाते समय, कर्नल सिंक्रनाइज़ेशन फ़्रेमवर्क के साथ डिपेंडेंसी की सूची दिखाता है:
/* * displays buffer when fence is signaled. returns immediately with a fence * that signals when buffer is no longer displayed. */ struct sync_fence* display_buffer(struct dma_buf *buffer, struct sync_fence *fence);
सिंक इंटिग्रेशन
इस सेक्शन में, कर्नल-स्पेस सिंक फ़्रेमवर्क को Android फ़्रेमवर्क के यूज़रस्पेस वाले हिस्सों और उन ड्राइवर के साथ इंटिग्रेट करने का तरीका बताया गया है जिन्हें एक-दूसरे के साथ कम्यूनिकेट करना होता है. कर्नेल-स्पेस ऑब्जेक्ट को उपयोगकर्ता स्पेस में फ़ाइल डिस्क्रिप्टर के तौर पर दिखाया जाता है.
इंटिग्रेशन के लिए तय किए गए नियम
Android एचएएल इंटरफ़ेस के नियमों का पालन करें:
- अगर एपीआई,
sync_ptको रेफ़र करने वाला फ़ाइल डिस्क्रिप्टर उपलब्ध कराता है, तो एपीआई का इस्तेमाल करने वाले वेंडर के ड्राइवर या HAL को फ़ाइल डिस्क्रिप्टर बंद करना होगा. - अगर वेंडर ड्राइवर या HAL, एपीआई फ़ंक्शन को
sync_ptवाला फ़ाइल डिस्क्रिप्टर पास करता है, तो वेंडर ड्राइवर या HAL को फ़ाइल डिस्क्रिप्टर बंद नहीं करना चाहिए. - फ़ेंस फ़ाइल डिस्क्रिप्टर का इस्तेमाल जारी रखने के लिए, वेंडर ड्राइवर या HAL को डिस्क्रिप्टर को डुप्लीकेट करना होगा.
बफ़रक्यू से पास होने पर, हर बार फ़ेंस ऑब्जेक्ट का नाम बदल जाता है.
कर्नल फ़ेंस की सुविधा की मदद से, फ़ेंस के नाम के लिए स्ट्रिंग का इस्तेमाल किया जा सकता है. इसलिए, सिंक फ़्रेमवर्क, फ़ेंस का नाम रखने के लिए विंडो के नाम और बफ़र इंडेक्स का इस्तेमाल करता है. जैसे, SurfaceView:0. इससे डीबग करने में मदद मिलती है, ताकि डेडलॉक के सोर्स की पहचान की जा सके. ऐसा इसलिए, क्योंकि नाम /d/sync और गड़बड़ी की रिपोर्ट के आउटपुट में दिखते हैं.
ANativeWindow इंटिग्रेशन
ANativeWindow को फ़ेंस के बारे में पता होता है. dequeueBuffer,
queueBuffer, और cancelBuffer में फ़ेंस पैरामीटर होते हैं.
OpenGL ES इंटिग्रेशन
OpenGL ES सिंक इंटिग्रेशन, दो EGL एक्सटेंशन पर निर्भर करता है:
EGL_ANDROID_native_fence_sync,EGLSyncKHRऑब्जेक्ट में नेटिव Android फ़ेंस फ़ाइल डिस्क्रिप्टर को रैप करने या बनाने का तरीका उपलब्ध कराता है.EGL_ANDROID_wait_sync, सीपीयू के बजाय जीपीयू-साइड स्टॉल की अनुमति देता है. इससे जीपीयू कोEGLSyncKHRके लिए इंतज़ार करना पड़ता है.EGL_ANDROID_wait_syncएक्सटेंशन औरEGL_KHR_wait_syncएक्सटेंशन एक ही हैं.
इन एक्सटेंशन का अलग-अलग इस्तेमाल करने के लिए, EGL_ANDROID_native_fence_sync एक्सटेंशन को उससे जुड़े कर्नल सपोर्ट के साथ लागू करें. इसके बाद, अपने ड्राइवर में EGL_ANDROID_wait_sync
एक्सटेंशन चालू करें. EGL_ANDROID_native_fence_sync एक्सटेंशन में, एक अलग नेटिव फ़ेंस EGLSyncKHR ऑब्जेक्ट टाइप होता है. इस वजह से, मौजूदा EGLSyncKHR ऑब्जेक्ट टाइप पर लागू होने वाले एक्सटेंशन, ज़रूरी नहीं कि EGL_ANDROID_native_fence ऑब्जेक्ट पर भी लागू हों. इससे, अनचाहे इंटरैक्शन से बचा जा सकता है.
EGL_ANDROID_native_fence_sync एक्सटेंशन, फ़ाइल के ब्यौरे के नेटिव फ़ेंस एट्रिब्यूट का इस्तेमाल करता है. इसे सिर्फ़ फ़ाइल बनाते समय सेट किया जा सकता है. साथ ही, इसे मौजूदा सिंक ऑब्जेक्ट से सीधे तौर पर क्वेरी नहीं किया जा सकता. इस एट्रिब्यूट को दो में से किसी एक मोड पर सेट किया जा सकता है:
- मान्य फ़ेंस फ़ाइल डिस्क्रिप्टर, मौजूदा नेटिव Android फ़ेंस फ़ाइल डिस्क्रिप्टर को
EGLSyncKHRऑब्जेक्ट में रैप करता है. - -1,
EGLSyncKHRऑब्जेक्ट से Android की नेटिव फ़ेंस फ़ाइल डिस्क्रिप्टर बनाता है.
नेटिव Android फ़ेंस फ़ाइल डिस्क्रिप्टर से EGLSyncKHR ऑब्जेक्ट निकालने के लिए, DupNativeFenceFD() फ़ंक्शन कॉल का इस्तेमाल करें.
इससे सेट किए गए एट्रिब्यूट के बारे में क्वेरी करने जैसा ही नतीजा मिलता है. हालांकि, यह इस नियम का पालन करता है कि डेटा पाने वाला व्यक्ति फ़ेंस को बंद करता है. इसलिए, यह डुप्लीकेट ऑपरेशन है. आखिर में, EGLSyncKHR ऑब्जेक्ट को डिस्ट्रॉय करने से, इंटरनल फ़ेंस एट्रिब्यूट बंद हो जाता है.
Hardware Composer इंटिग्रेशन
एचडब्ल्यूसी, तीन तरह के सिंक फ़ेंस को मैनेज करता है:
- Acquire fences को इनपुट बफ़र के साथ
setLayerBufferऔरsetClientTargetकॉल में पास किया जाता है. ये बफ़र में लिखे जाने वाले डेटा को दिखाते हैं. SurfaceFlinger या HWC को कंपोज़िशन करने के लिए, इससे जुड़े बफ़र से डेटा पढ़ने से पहले, इस डेटा को सिग्नल देना होगा. getReleaseFencesकॉल का इस्तेमाल करके,presentDisplayको कॉल करने के बाद रिलीज़ फ़ेंस वापस पाए जाते हैं. ये एक ही लेयर पर, पिछले बफ़र से पढ़े जाने वाले डेटा को दिखाते हैं. रिलीज़ फ़ेंस, तब सिग्नल देता है, जब एचडब्ल्यूसी पिछले बफ़र का इस्तेमाल नहीं कर रहा होता है. ऐसा इसलिए होता है, क्योंकि मौजूदा बफ़र ने डिसप्ले पर पिछले बफ़र की जगह ले ली होती है. रिलीज़ फ़ेंस को ऐप्लिकेशन पर वापस भेज दिया जाता है. साथ ही, पिछले बफ़र भी भेजे जाते हैं. इन बफ़र को मौजूदा कंपोज़िशन के दौरान बदल दिया जाएगा. ऐप्लिकेशन को तब तक इंतज़ार करना होगा, जब तक रिलीज़ फ़ेंस के सिग्नल न मिल जाएं. इसके बाद ही, वह उस बफ़र में नया कॉन्टेंट लिख सकता है जिसे उसे वापस भेजा गया था.- मौजूदा फ़ेंस,
presentDisplayको किए गए कॉल के हिस्से के तौर पर, हर फ़्रेम के लिए एक बार दिखाए जाते हैं. मौजूदा फ़ेंस यह बताते हैं कि इस फ़्रेम की कंपोज़िशन कब पूरी हुई या इसके अलावा, पिछले फ़्रेम की कंपोज़िशन का नतीजा कब तक ज़रूरी नहीं है. फ़िज़िकल डिसप्ले के लिए,presentDisplayफ़िलहाल मौजूद फ़ेंस दिखाता है. ऐसा तब होता है, जब मौजूदा फ़्रेम स्क्रीन पर दिखता है. मौजूदा फ़ेंस के वापस आने के बाद, SurfaceFlinger के टारगेट बफ़र में फिर से लिखना सुरक्षित है. हालांकि, ऐसा तब ही किया जा सकता है, जब यह लागू हो. वर्चुअल डिसप्ले के लिए, आउटपुट बफ़र से डेटा को सुरक्षित तरीके से पढ़ने पर, फ़ेंस दिखाए जाते हैं.