सिंक्रोनाइज़ेशन फ़्रेमवर्क

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

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

सिंक करने के फ़्रेमवर्क की मदद से, सिंक करने की सुविधा को लागू करने वाले लोग, अपने हार्डवेयर कॉम्पोनेंट में सिंक करने के संसाधनों का फ़ायदा भी ले सकते हैं. आखिर में, फ़्रेमवर्क, डीबग करने में मदद के लिए, ग्राफ़िक पाइपलाइन में जानकारी देता है.

एक्सप्लिसिट सिंकिंग

एक्सप्लिसिट सिंक, ग्राफ़िक्स बफ़र के प्रोड्यूसर और उपभोक्ताओं को तब सिग्नल देने की सुविधा देता है, जब वे बफ़र का इस्तेमाल पूरा कर लें. साफ़ तौर पर सिंक करने की सुविधा को kernel-space में लागू किया जाता है.

साफ़ तौर पर सिंक करने की सुविधा के ये फ़ायदे हैं:

  • डिवाइसों के बीच व्यवहार में कम अंतर
  • डीबग करने के लिए बेहतर सहायता
  • बेहतर टेस्टिंग मेट्रिक

सिंक फ़्रेमवर्क में तीन ऑब्जेक्ट टाइप होते हैं:

  • sync_timeline
  • sync_pt
  • sync_fence

Sync_timeline

sync_timeline एक ऐसी टाइमलाइन है जो लगातार बढ़ती रहती है. इसे वेंडर को हर ड्राइवर इंस्टेंस के लिए लागू करना चाहिए. जैसे, GL कॉन्टेक्स्ट, डिसप्ले कंट्रोलर या 2D ब्लिटर. sync_timeline किसी खास हार्डवेयर के लिए, कर्नेल में सबमिट की गई जॉब की गिनती करता है. sync_timeline, कार्रवाइयों के क्रम की गारंटी देता है और हार्डवेयर के हिसाब से लागू करने की सुविधा देता है.

sync_timeline लागू करते समय इन दिशा-निर्देशों का पालन करें:

  • डीबग करने की प्रोसेस को आसान बनाने के लिए, सभी ड्राइवर, टाइमलाइन, और फ़ेंस के लिए काम के नाम दें.
  • डीबगिंग आउटपुट को पढ़ने में आसान बनाने के लिए, टाइमलाइन में timeline_value_str और pt_value_str ऑपरेटर लागू करें.
  • अगर ज़रूरी हो, तो उपयोगकर्तास्पेस लाइब्रेरी को निजी टाइमलाइन डेटा का ऐक्सेस देने के लिए, driver_data को लागू करें. जैसे, GL लाइब्रेरी. data_driver की मदद से वेंडर, बदले न जा सकने वाले sync_fence और sync_pts के बारे में जानकारी दे सकते हैं, ताकि उन पर आधारित कमांड लाइन बनाई जा सकें.
  • उपयोगकर्ताओं को साफ़ तौर पर फ़ेंस बनाने या सिग्नल देने की अनुमति न दें. साफ़ तौर पर सिग्नल/फ़ेंस बनाने पर, सेवा के अस्वीकार होने का हमला होता है. इससे, पाइपलाइन की सुविधाएं काम करना बंद कर देती हैं.
  • sync_timeline, sync_pt या sync_fence एलिमेंट को साफ़ तौर पर ऐक्सेस न करें. यह एपीआई सभी ज़रूरी फ़ंक्शन उपलब्ध कराता है.

Sync_pt

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

सिंक_फ़ेंस

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.h
      • kernel/common/drivers/base/sync.c
    • kernel/common/Documentation/sync.txt पर दस्तावेज़
    • platform/system/core/libsync में मौजूद कर्नेल स्पेस के साथ कम्यूनिकेट करने के लिए लाइब्रेरी
  • वेंडर को HAL में 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 फ़्रेमवर्क के यूज़रस्पेस वाले हिस्सों और एक-दूसरे के साथ कम्यूनिकेट करने वाले ड्राइवर के साथ, कर्नेल-स्पेस सिंक फ़्रेमवर्क को इंटिग्रेट करने का तरीका बताया गया है. Kernel-space ऑब्जेक्ट को userspace में फ़ाइल डिस्क्रिप्टर के तौर पर दिखाया जाता है.

इंटिग्रेशन के लिए इस्तेमाल होने वाले नियम

Android HAL इंटरफ़ेस के तौर-तरीकों का पालन करें:

  • अगर एपीआई, sync_pt का रेफ़रंस देने वाला फ़ाइल डिस्क्रिप्टर उपलब्ध कराता है, तो एपीआई का इस्तेमाल करने वाले वेंडर के ड्राइवर या एचएएल को फ़ाइल डिस्क्रिप्टर बंद करना होगा.
  • अगर वेंडर ड्राइवर या एचएएल, किसी एपीआई फ़ंक्शन को sync_pt वाला फ़ाइल डिस्क्रिप्टर पास करता है, तो वेंडर ड्राइवर या एचएएल को फ़ाइल डिस्क्रिप्टर को बंद नहीं करना चाहिए.
  • फ़ेंस फ़ाइल डिस्क्रिप्टर का इस्तेमाल जारी रखने के लिए, वेंडर ड्राइवर या एचएएल को डिस्क्रिप्टर का डुप्लीकेट बनाना होगा.

फ़ेंस ऑब्जेक्ट को हर बार BufferQueue से गुज़रने पर उसका नाम बदल दिया जाता है. कर्नेल फ़ेंस के साथ काम करने की सुविधा की मदद से, फ़ेंस के नाम के लिए स्ट्रिंग का इस्तेमाल किया जा सकता है. इसलिए, सिंक फ़्रेमवर्क, फ़ेंस के नाम के लिए, 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() फ़ंक्शन कॉल का इस्तेमाल करें. इसका नतीजा, set एट्रिब्यूट के लिए क्वेरी करने जैसा ही होता है. हालांकि, यह उस तरीके का पालन करता है जिसमें पाने वाला, बाड़ को बंद कर देता है (इसलिए, डुप्लीकेट ऑपरेशन). आखिर में, EGLSyncKHR ऑब्जेक्ट को मिटाने पर, इंटरनल फ़ेंस एट्रिब्यूट बंद हो जाता है.

Hardware Composer का इंटिग्रेशन

हार्डवेयर कंपोजर, सिंक फ़ेंस के तीन टाइप को मैनेज करता है:

  • ऐक्वेयर फ़ेंस को इनपुट बफ़र के साथ setLayerBuffer और setClientTarget कॉल पर पास किया जाता है. ये बफ़र में लिखे जाने वाले डेटा को दिखाते हैं. साथ ही, इनका सिग्नल तब भेजा जाना चाहिए, जब SurfaceFlinger या HWC, कॉम्पोज़िशन करने के लिए उससे जुड़े बफ़र से डेटा पढ़ने की कोशिश करे.
  • getReleaseFences कॉल का इस्तेमाल करके presentDisplay को कॉल करने के बाद, रिलीज़ फ़ेंस वापस पाएं. ये उसी लेयर पर पिछले बफ़र से, पढ़े जाने के लिए बाकी डेटा दिखाते हैं. जब एचडब्ल्यूसी, पिछले बफ़र का इस्तेमाल नहीं कर रहा होता, तब रिलीज़ फ़ेंस सिग्नल भेजे जाते हैं. ऐसा इसलिए होता है, क्योंकि मौजूदा बफ़र ने डिसप्ले पर पिछले बफ़र की जगह ले ली होती है. रिलीज़ फ़ेंस को ऐप्लिकेशन में वापस भेज दिया जाता है. साथ ही, उन पिछले बफ़र को भी भेज दिया जाता है जिन्हें मौजूदा कॉम्पोज़िशन के दौरान बदल दिया जाएगा. ऐप्लिकेशन को रिलीज़ फ़ेंस सिग्नल मिलने तक इंतज़ार करना होगा. इसके बाद ही, उसे वापस दिए गए बफ़र में नया कॉन्टेंट लिखा जा सकता है.
  • presentDisplay को कॉल करने पर, फ़्रेम के फ़ेंस दिखाए जाते हैं. हर फ़्रेम के लिए एक फ़ेंस दिखाया जाता है. फ़ेंस से पता चलता है कि इस फ़्रेम का कंपोज़िशन कब पूरा हो गया है या फिर, पिछले फ़्रेम के कंपोज़िशन के नतीजे की अब ज़रूरत कब नहीं है. फ़िज़िकल डिसप्ले के लिए, presentDisplay फ़ंक्शन, स्क्रीन पर मौजूदा फ़्रेम दिखने पर मौजूदा फ़ेंस दिखाता है. फ़ेंस के मौजूद होने के बाद, अगर लागू हो, तो SurfaceFlinger के टारगेट बफ़र में फिर से लिखा जा सकता है. वर्चुअल डिसप्ले के लिए, फ़ेंस तब दिखाए जाते हैं, जब आउटपुट बफ़र से पढ़ना सुरक्षित हो.