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

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

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

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

साफ़ तौर पर सिंक करना

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

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

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

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

  • sync_timeline
  • sync_pt
  • sync_fence

sync_timeline

sync_timeline एक ऐसी टाइमलाइन है जो लगातार बढ़ती रहती है. इसे वेंडर को हर ड्राइवर इंस्टेंस के लिए लागू करना चाहिए. जैसे, GL कॉन्टेक्स्ट, डिसप्ले कंट्रोलर या 2D ब्लिटर. sync_timeline किसी खास हार्डवेयर के लिए, kernel में सबमिट की गई जॉब की गिनती करता है. 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_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 HAL इंटरफ़ेस के नियमों का पालन करें:

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

फ़ेंस ऑब्जेक्ट को हर बार BufferQueue से गुज़रने पर उसका नाम बदल दिया जाता है. Kernel fence support की मदद से, फ़ेंस के नाम के लिए स्ट्रिंग का इस्तेमाल किया जा सकता है. इसलिए, सिंक फ़्रेमवर्क, फ़ेंस के नाम के लिए, 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 का इंटिग्रेशन

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

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