Vulkan प्री-रोटेशन की मदद से डिवाइस की स्क्रीन की दिशा मैनेज करें

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

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

  1. Android OS, डिवाइस के डिसप्ले प्रोसेसिंग यूनिट (डीपीयू) का इस्तेमाल कर सकता है. यह हार्डवेयर में, डिसप्ले को घुमाने की प्रोसेस को आसानी से मैनेज कर सकता है. यह सुविधा, सिर्फ़ ज़रूरी शर्तें पूरी करने वाले डिवाइसों पर उपलब्ध है.
  2. Android OS, कंपोज़िटर पास जोड़कर डिसप्ले रोटेशन को मैनेज कर सकता है. इससे परफ़ॉर्मेंस पर असर पड़ेगा. यह इस बात पर निर्भर करता है कि कंपोज़िटर को आउटपुट इमेज को रोटेट करने के लिए क्या करना होगा.
  3. ऐप्लिकेशन, डिसप्ले के मौजूदा ओरिएंटेशन से मेल खाने वाले रेंडरिंग सर्फ़ेस पर घुमाई गई इमेज को रेंडर करके, सर्फ़ेस रोटेशन को खुद मैनेज कर सकता है.

आपको इनमें से कौनसा तरीका इस्तेमाल करना चाहिए?

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

Pixel 4XL पर शिपिंग टाइटल चलाने के दौरान, हमने देखा कि SurfaceFlinger (Android Compositor को चलाने वाला ज़्यादा प्राथमिकता वाला टास्क):

  • ऐप्लिकेशन के काम में लगातार रुकावट डालता है. इससे फ़्रेमटाइम पर 1 से 3 मि॰से॰ का असर पड़ता है.

  • इससे जीपीयू की वर्टेक्स/टेक्सचर मेमोरी पर ज़्यादा दबाव पड़ता है, क्योंकि कंपोज़िटर को कंपोज़िशन का काम करने के लिए, पूरे फ़्रेमबफ़र को पढ़ना पड़ता है.

ओरिएंटेशन को सही तरीके से हैंडल करने पर, SurfaceFlinger की वजह से GPU के प्रीएम्पशन की समस्या लगभग पूरी तरह से खत्म हो जाती है. साथ ही, Android Compositor की ओर से इस्तेमाल की गई बूस्ट की गई फ़्रीक्वेंसी की अब ज़रूरत नहीं होती है. इसलिए, GPU की फ़्रीक्वेंसी 40% तक कम हो जाती है.

ऊपर दिए गए उदाहरण में, यह पक्का करने के लिए कि सर्फ़ेस रोटेशन को कम से कम ओवरहेड के साथ सही तरीके से हैंडल किया जाए, आपको तीसरा तरीका लागू करना चाहिए. इसे प्री-रोटेशन कहा जाता है. इससे Android OS को पता चलता है कि आपका ऐप्लिकेशन, स्क्रीन रोटेशन को मैनेज करता है. इसके लिए, आपको स्वैपचेन बनाते समय ओरिएंटेशन तय करने वाले सर्फ़ेस ट्रांसफ़ॉर्म फ़्लैग पास करने होंगे. इससे Android Compositor, रोटेशन अपने-आप नहीं करता, बल्कि बंद हो जाता है.

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

AndroidManifest.xml में बदलाव करना

अपने ऐप्लिकेशन में डिवाइस रोटेशन को मैनेज करने के लिए, ऐप्लिकेशन की AndroidManifest.xml फ़ाइल में बदलाव करें. इससे Android को यह पता चलेगा कि आपका ऐप्लिकेशन, ओरिएंटेशन और स्क्रीन साइज़ में होने वाले बदलावों को मैनेज करेगा. इससे ओरिएंटेशन में बदलाव होने पर, Android को Activity को डिस्ट्रॉय और फिर से बनाने से रोका जाता है. साथ ही, मौजूदा विंडो सरफेस पर onDestroy() फ़ंक्शन को कॉल करने से भी रोका जाता है. इसके लिए, गतिविधि के configChanges सेक्शन में orientation (एपीआई लेवल <13 के साथ काम करने के लिए) और screenSize एट्रिब्यूट जोड़ें:

<activity android:name="android.app.NativeActivity"
          android:configChanges="orientation|screenSize">

अगर आपका ऐप्लिकेशन, screenOrientation एट्रिब्यूट का इस्तेमाल करके स्क्रीन ओरिएंटेशन को ठीक करता है, तो आपको यह कार्रवाई करने की ज़रूरत नहीं है. इसके अलावा, अगर आपका ऐप्लिकेशन किसी तय ओरिएंटेशन का इस्तेमाल करता है, तो उसे ऐप्लिकेशन के शुरू होने/फिर से शुरू होने पर, स्वैपचेन को सिर्फ़ एक बार सेट अप करना होगा.

पहचान की पुष्टि करने के लिए, स्क्रीन रिज़ॉल्यूशन और कैमरे के पैरामीटर पाना

इसके बाद, VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR वैल्यू से जुड़े डिवाइस के स्क्रीन रिज़ॉल्यूशन का पता लगाएं. यह रिज़ॉल्यूशन, डिवाइस के आइडेंटिटी ओरिएंटेशन से जुड़ा होता है. इसलिए, स्वैपचेन को हमेशा इसी पर सेट करना होगा. इसे पाने का सबसे भरोसेमंद तरीका यह है कि ऐप्लिकेशन शुरू होने पर vkGetPhysicalDeviceSurfaceCapabilitiesKHR() को कॉल करें और मिले हुए एक्सटेंट को सेव करें. पहचान स्क्रीन का रिज़ॉल्यूशन सेव किया जा रहा है, यह पक्का करने के लिए, चौड़ाई और ऊंचाई को currentTransform के हिसाब से बदलें. यह currentTransform भी वापस भेजा जाता है:

VkSurfaceCapabilitiesKHR capabilities;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physDevice, surface, &capabilities);

uint32_t width = capabilities.currentExtent.width;
uint32_t height = capabilities.currentExtent.height;
if (capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR ||
    capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR) {
  // Swap to get identity width and height
  capabilities.currentExtent.height = width;
  capabilities.currentExtent.width = height;
}

displaySizeIdentity = capabilities.currentExtent;

displaySizeIdentity एक VkExtent2D स्ट्रक्चर है. इसका इस्तेमाल हम डिसप्ले के नैचुरल ओरिएंटेशन में, ऐप्लिकेशन की विंडो के साइज़ की जानकारी को सेव करने के लिए करते हैं.

डिवाइस के ओरिएंटेशन में हुए बदलावों का पता लगाना (Android 10 और इसके बाद के वर्शन)

आपके ऐप्लिकेशन में ओरिएंटेशन में हुए बदलाव का पता लगाने का सबसे भरोसेमंद तरीका यह है कि यह पुष्टि की जाए कि vkQueuePresentKHR() फ़ंक्शन, VK_SUBOPTIMAL_KHR दिखाता है या नहीं. उदाहरण के लिए:

auto res = vkQueuePresentKHR(queue_, &present_info);
if (res == VK_SUBOPTIMAL_KHR){
  orientationChanged = true;
}

ध्यान दें: यह तरीका सिर्फ़ Android 10 और इसके बाद के वर्शन वाले डिवाइसों पर काम करता है. Android के इन वर्शन में, vkQueuePresentKHR() से VK_SUBOPTIMAL_KHR मिलता है. हम इस जांच के नतीजे को orientationChanged में सेव करते हैं. यह एक boolean है, जिसे ऐप्लिकेशन के मुख्य रेंडरिंग लूप से ऐक्सेस किया जा सकता है.

डिवाइस के ओरिएंटेशन में हुए बदलावों का पता लगाना (Android 10 से पहले के वर्शन के लिए)

Android 10 या इससे पहले के वर्शन पर काम करने वाले डिवाइसों के लिए, अलग तरीके से लागू करने की ज़रूरत होती है, क्योंकि VK_SUBOPTIMAL_KHR काम नहीं करता.

पोलिंग का इस्तेमाल करना

Android 10 से पहले के वर्शन वाले डिवाइसों पर, हर pollingInterval फ़्रेम में डिवाइस ट्रांसफ़ॉर्म को पोल किया जा सकता है. यहाँ pollingInterval, प्रोग्रामर के हिसाब से तय की गई ग्रैन्युलैरिटी है. इसके लिए, आपको vkGetPhysicalDeviceSurfaceCapabilitiesKHR() को कॉल करना होगा. इसके बाद, currentTransform फ़ील्ड में मिले डेटा की तुलना, मौजूदा समय में सेव किए गए सर्फ़ेस ट्रांसफ़ॉर्मेशन (इस कोड के उदाहरण में, pretransformFlag में सेव किया गया) से करनी होगी.

currFrameCount++;
if (currFrameCount >= pollInterval){
  VkSurfaceCapabilitiesKHR capabilities;
  vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physDevice, surface, &capabilities);

  if (pretransformFlag != capabilities.currentTransform) {
    window_resized = true;
  }
  currFrameCount = 0;
}

Android 10 पर चलने वाले Pixel 4 पर, पोलिंग में 0.120 से 0.250 मि॰से॰ लगे. वहीं, Android 8 पर चलने वाले Pixel 1XL पर, पोलिंग में 0.110 से 0.350 मि॰से॰ लगे.vkGetPhysicalDeviceSurfaceCapabilitiesKHR()

कॉलबैक का इस्तेमाल करना

Android 10 से पहले के वर्शन पर काम करने वाले डिवाइसों के लिए, दूसरा विकल्प यह है कि onNativeWindowResized() कॉलबैक रजिस्टर करें. इससे एक ऐसे फ़ंक्शन को कॉल किया जा सकेगा जो orientationChanged फ़्लैग सेट करता है. इससे ऐप्लिकेशन को यह सूचना मिलती है कि ओरिएंटेशन में बदलाव हुआ है:

void android_main(struct android_app *app) {
  ...
  app->activity->callbacks->onNativeWindowResized = ResizeCallback;
}

यहां ResizeCallback को इस तरह से परिभाषित किया गया है:

void ResizeCallback(ANativeActivity *activity, ANativeWindow *window){
  orientationChanged = true;
}

इस समाधान में यह समस्या है कि onNativeWindowResized() को सिर्फ़ 90 डिग्री के ओरिएंटेशन में बदलाव के लिए कॉल किया जाता है. जैसे, लैंडस्केप से पोर्ट्रेट या इसके उलट. ओरिएंटेशन में किए गए अन्य बदलावों से, स्वैपचेन फिर से नहीं बनेगा. उदाहरण के लिए, लैंडस्केप से रिवर्स-लैंडस्केप में बदलने पर, यह ट्रिगर नहीं होगा. इसके लिए, Android कंपोज़िटर को आपके ऐप्लिकेशन के लिए फ़्लिप करना होगा.

ओरिएंटेशन बदलने की सुविधा को मैनेज करना

ओरिएंटेशन में बदलाव को मैनेज करने के लिए, orientationChanged वैरिएबल को सही पर सेट करने पर, मुख्य रेंडरिंग लूप के सबसे ऊपर ओरिएंटेशन में बदलाव करने वाले रूटीन को कॉल करें. उदाहरण के लिए:

bool VulkanDrawFrame() {
 if (orientationChanged) {
   OnOrientationChange();
}

OnOrientationChange() फ़ंक्शन में, स्वैपचेन को फिर से बनाने के लिए ज़रूरी सभी काम किए जाते हैं. इसका मतलब है कि:

  1. Framebuffer और ImageView के सभी मौजूदा इंस्टेंस मिटा दें,

  2. पुरानी स्वैपचेन को डिस्ट्रॉय करते समय स्वैपचेन को फिर से बनाएं. इसके बारे में अगले सेक्शन में बताया जाएगा.

  3. नई स्वैपचेन की DisplayImages के साथ, फ़्रेमबफ़र फिर से बनाएं. ध्यान दें: अटैचमेंट इमेज (उदाहरण के लिए, डेप्थ/स्टेंसिल इमेज) को आम तौर पर फिर से बनाने की ज़रूरत नहीं होती. ऐसा इसलिए, क्योंकि ये पहले से रोटेट की गई स्वैपचेन इमेज के आइडेंटिटी रिज़ॉल्यूशन पर आधारित होती हैं.

void OnOrientationChange() {
 vkDeviceWaitIdle(getDevice());

 for (int i = 0; i < getSwapchainLength(); ++i) {
   vkDestroyImageView(getDevice(), displayViews_[i], nullptr);
   vkDestroyFramebuffer(getDevice(), framebuffers_[i], nullptr);
 }

 createSwapChain(getSwapchain());
 createFrameBuffers(render_pass, depthBuffer.image_view);
 orientationChanged = false;
}

फ़ंक्शन के आखिर में, orientationChanged फ़्लैग को फ़ॉल्स पर रीसेट करें, ताकि यह पता चल सके कि आपने ओरिएंटेशन में हुए बदलाव को मैनेज कर लिया है.

स्वैपचेन को फिर से बनाना

पिछले सेक्शन में, हमने स्वैपचेन को फिर से बनाने के बारे में बताया था. इसके लिए, सबसे पहले रेंडरिंग सरफेस की नई विशेषताएं हासिल करनी होती हैं:

void createSwapChain(VkSwapchainKHR oldSwapchain) {
   VkSurfaceCapabilitiesKHR capabilities;
   vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physDevice, surface, &capabilities);
   pretransformFlag = capabilities.currentTransform;

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

ऐसा करने के लिए, VkSwapchainCreateInfo स्ट्रक्चर में यहां दिए गए एट्रिब्यूट की वैल्यू दें:

VkSwapchainCreateInfoKHR swapchainCreateInfo{
  ...
  .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
  .imageExtent = displaySizeIdentity,
  .preTransform = pretransformFlag,
  .oldSwapchain = oldSwapchain,
};

vkCreateSwapchainKHR(device_, &swapchainCreateInfo, nullptr, &swapchain_));

if (oldSwapchain != VK_NULL_HANDLE) {
  vkDestroySwapchainKHR(device_, oldSwapchain, nullptr);
}

imageExtent फ़ील्ड में, displaySizeIdentity की वह सीमा अपने-आप भर जाएगी जिसे आपने ऐप्लिकेशन शुरू होने पर सेव किया था. preTransform फ़ील्ड में, pretransformFlag वैरिएबल की वैल्यू भरी जाएगी. यह वैरिएबल, surfaceCapabilities के currentTransform फ़ील्ड पर सेट होता है. साथ ही, oldSwapchain फ़ील्ड को उस स्वैपचेन पर सेट किया जाता है जिसे डिस्ट्रॉय किया जाएगा.

एमवीपी मैट्रिक्स अडजस्टमेंट

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

glm::mat4 pre_rotate_mat = glm::mat4(1.0f);
glm::vec3 rotation_axis = glm::vec3(0.0f, 0.0f, 1.0f);

if (pretransformFlag & VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR) {
  pre_rotate_mat = glm::rotate(pre_rotate_mat, glm::radians(90.0f), rotation_axis);
}

else if (pretransformFlag & VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR) {
  pre_rotate_mat = glm::rotate(pre_rotate_mat, glm::radians(270.0f), rotation_axis);
}

else if (pretransformFlag & VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR) {
  pre_rotate_mat = glm::rotate(pre_rotate_mat, glm::radians(180.0f), rotation_axis);
}

MVP = pre_rotate_mat * MVP;

ध्यान देने वाली बातें - नॉन-फ़ुल स्क्रीन व्यूपोर्ट और सीज़र

अगर आपका ऐप्लिकेशन, फ़ुल स्क्रीन व्यूपोर्ट/सिज़र रीजन का इस्तेमाल नहीं कर रहा है, तो डिवाइस के ओरिएंटेशन के हिसाब से उन्हें अपडेट करना होगा. इसके लिए, Vulkan की पाइपलाइन बनाते समय, आपको डाइनैमिक व्यूपोर्ट और सीज़र के विकल्पों को चालू करना होगा:

VkDynamicState dynamicStates[2] = {
  VK_DYNAMIC_STATE_VIEWPORT,
  VK_DYNAMIC_STATE_SCISSOR,
};

VkPipelineDynamicStateCreateInfo dynamicInfo = {
  .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
  .pNext = nullptr,
  .flags = 0,
  .dynamicStateCount = 2,
  .pDynamicStates = dynamicStates,
};

VkGraphicsPipelineCreateInfo pipelineCreateInfo = {
  .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
  ...
  .pDynamicState = &dynamicInfo,
  ...
};

VkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineCreateInfo, nullptr, &mPipeline);

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

int x = 0, y = 0, w = 500, h = 400;

glm::vec4 viewportData;

switch (device->GetPretransformFlag()) {
  case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
    viewportData = {bufferWidth - h - y, x, h, w};
    break;
  case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR:
    viewportData = {bufferWidth - w - x, bufferHeight - h - y, w, h};
    break;
  case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR:
    viewportData = {y, bufferHeight - w - x, h, w};
    break;
  default:
    viewportData = {x, y, w, h};
    break;
}

const VkViewport viewport = {
    .x = viewportData.x,
    .y = viewportData.y,
    .width = viewportData.z,
    .height = viewportData.w,
    .minDepth = 0.0F,
    .maxDepth = 1.0F,
};

vkCmdSetViewport(renderer->GetCurrentCommandBuffer(), 0, 1, &viewport);

x और y वैरिएबल, व्यूपोर्ट के सबसे ऊपर बाएं कोने के कोऑर्डिनेट तय करते हैं. वहीं, w और h वैरिएबल, व्यूपोर्ट की चौड़ाई और ऊंचाई तय करते हैं. इसी कैलकुलेशन का इस्तेमाल, सिज़र टेस्ट सेट करने के लिए भी किया जा सकता है. इसे यहां पूरी जानकारी के लिए शामिल किया गया है:

int x = 0, y = 0, w = 500, h = 400;
glm::vec4 scissorData;

switch (device->GetPretransformFlag()) {
  case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
    scissorData = {bufferWidth - h - y, x, h, w};
    break;
  case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR:
    scissorData = {bufferWidth - w - x, bufferHeight - h - y, w, h};
    break;
  case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR:
    scissorData = {y, bufferHeight - w - x, h, w};
    break;
  default:
    scissorData = {x, y, w, h};
    break;
}

const VkRect2D scissor = {
    .offset =
        {
            .x = (int32_t)viewportData.x,
            .y = (int32_t)viewportData.y,
        },
    .extent =
        {
            .width = (uint32_t)viewportData.z,
            .height = (uint32_t)viewportData.w,
        },
};

vkCmdSetScissor(renderer->GetCurrentCommandBuffer(), 0, 1, &scissor);

ध्यान देने वाली बातें - फ़्रैगमेंट शेडर डेरिवेटिव

अगर आपका ऐप्लिकेशन, dFdx और dFdy जैसे डेरिवेटिव कंप्यूटेशन का इस्तेमाल कर रहा है, तो रोटेट किए गए कोऑर्डिनेट सिस्टम को ध्यान में रखने के लिए, अतिरिक्त ट्रांसफ़ॉर्मेशन की ज़रूरत पड़ सकती है. ऐसा इसलिए, क्योंकि ये कंप्यूटेशन पिक्सल स्पेस में किए जाते हैं. इसके लिए, ऐप्लिकेशन को फ़्रैगमेंट शेडर में प्रीट्रांसफ़ॉर्म का कुछ इंडिकेशन पास करना होगा. जैसे, मौजूदा डिवाइस ओरिएंटेशन को दिखाने वाला पूर्णांक. साथ ही, इसका इस्तेमाल करके डेरिवेटिव कंप्यूटेशन को सही तरीके से मैप करना होगा:

  • पहले से 90 डिग्री घुमाए गए फ़्रेम के लिए
    • dFdx को dFdy पर मैप किया जाना चाहिए
    • dFdy को -dFdx पर मैप किया जाना चाहिए
  • 270 डिग्री पहले से घुमाए गए फ़्रेम के लिए
    • dFdx को -dFdy पर मैप किया जाना चाहिए
    • dFdy को dFdx पर मैप किया जाना चाहिए
  • 180 डिग्री पर पहले से घुमाए गए फ़्रेम के लिए,
    • dFdx को -dFdx पर मैप किया जाना चाहिए
    • dFdy को -dFdy से मैप किया जाना चाहिए

नतीजा

Android पर Vulkan का ज़्यादा से ज़्यादा फ़ायदा पाने के लिए, अपने ऐप्लिकेशन में प्री-रोटेशन की सुविधा लागू करना ज़रूरी है. इस लेख से मिलने वाली सबसे अहम जानकारी यह है:

  • पक्का करें कि स्वैपचेन बनाने या फिर से बनाने के दौरान, प्रीट्रांसफ़ॉर्म फ़्लैग को Android ऑपरेटिंग सिस्टम से मिले फ़्लैग से मैच करने के लिए सेट किया गया हो. इससे कंपोज़िटर ओवरहेड से बचा जा सकेगा.
  • स्वैपचेन का साइज़, डिसप्ले के नैचुरल ओरिएंटेशन में ऐप्लिकेशन की विंडो के आइडेंटिटी रिज़ॉल्यूशन के हिसाब से तय करें.
  • डिवाइस के ओरिएंटेशन के हिसाब से, क्लिप स्पेस में MVP मैट्रिक्स को घुमाएं, क्योंकि स्वैपचेन रिज़ॉल्यूशन/एक्सटेंट अब डिसप्ले के ओरिएंटेशन के साथ अपडेट नहीं होता है.
  • अपने ऐप्लिकेशन की ज़रूरत के हिसाब से, व्यूपोर्ट और सिज़र रेक्टैंगल अपडेट करें.

सैंपल ऐप्लिकेशन: Android में प्री-रोटेशन की सुविधा