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

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

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

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

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

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

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

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

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

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

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

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

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

अपने ऐप्लिकेशन में डिवाइस रोटेशन मैनेज करने के लिए, ऐप्लिकेशन की AndroidManifest.xml फ़ाइल बदलकर शुरुआत करें, ताकि Android को यह बताया जा सके कि आपका ऐप्लिकेशन स्क्रीन की दिशा और स्क्रीन के साइज़ में किए गए बदलावों को हैंडल करेगा. इससे 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 के आधार पर चौड़ाई और ऊंचाई बदलें. यह भी पक्का करें कि आपने पहचान की स्क्रीन का रिज़ॉल्यूशन सेव किया हो:

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 पर, vkGetPhysicalDeviceSurfaceCapabilitiesKHR() को पॉलिंग करने में .120 से .250 मिलीसेकंड लगे. वहीं, Android 8 पर काम करने वाले Pixel 1XL पर, पॉलिंग करने में .110 से .350 मिलीसेकंड लगे.

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

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 जैसे डेरिवेटिव कैलकुलेशन का इस्तेमाल कर रहा है, तो घुमाए गए निर्देशांक सिस्टम को ध्यान में रखने के लिए, अतिरिक्त ट्रांसफ़ॉर्मेशन की ज़रूरत पड़ सकती है. ऐसा इसलिए, क्योंकि ये कैलकुलेशन पिक्सल स्पेस में किए जाते हैं. इसके लिए, ऐप्लिकेशन को फ़्रेगमेंट शेडर में preTransform का कुछ संकेत देना होगा. जैसे, डिवाइस के मौजूदा ओरिएंटेशन को दिखाने वाला पूर्णांक. साथ ही, डेरिवेटिव कैलकुलेशन को सही तरीके से मैप करने के लिए, इसका इस्तेमाल करना होगा:

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

नतीजा

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

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

ऐप्लिकेशन का सैंपल: Android का, पहले से ही अलग-अलग तरह से नेविगेट करने का कम से कम समय