सिंक करने का फ़्रेमवर्क

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

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

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

एक्सप्लिसिट सिंक्रोनाइज़ेशन

एक्सप्लिसिट सिंक्रनाइज़ेशन की मदद से, ग्राफ़िक बफ़र के प्रोड्यूसर और कंज्यूमर, यह सिग्नल दे सकते हैं कि उन्होंने बफ़र का इस्तेमाल कब बंद कर दिया है. एक्सप्लिसिट सिंक्रनाइज़ेशन को कर्नल-स्पेस में लागू किया जाता है.

डेटा को साफ़ तौर पर सिंक करने के ये फ़ायदे हैं:

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

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

  • sync_timeline
  • sync_pt
  • sync_fence

sync_timeline

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

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

  • डीबग करने की प्रोसेस को आसान बनाने के लिए, सभी ड्राइवर, टाइमलाइन, और फ़ेंस के लिए काम के नाम दें.
  • टाइमलाइन में timeline_value_str और pt_value_str ऑपरेटर लागू करें, ताकि डीबग करने के आउटपुट को आसानी से पढ़ा जा सके.
  • अगर आपको उपयोगकर्ताओं को निजी टाइमलाइन के डेटा का ऐक्सेस देना है, तो fill 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_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 फ़्रेमवर्क के यूज़रस्पेस वाले हिस्सों और उन ड्राइवर के साथ कैसे इंटिग्रेट किया जाए जिन्हें एक-दूसरे के साथ कम्यूनिकेट करना है. कर्नेल-स्पेस ऑब्जेक्ट को उपयोगकर्ता स्पेस में फ़ाइल डिस्क्रिप्टर के तौर पर दिखाया जाता है.

इंटिग्रेशन के लिए तय किए गए नियम

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 के टारगेट बफ़र में फिर से लिखना सुरक्षित है. हालांकि, ऐसा तब ही किया जा सकता है, जब यह लागू हो. वर्चुअल डिसप्ले के लिए, आउटपुट बफ़र से डेटा को सुरक्षित तरीके से पढ़ने पर, मौजूदा फ़ेंस वापस कर दिए जाते हैं.