इस लेख में, Vulkan ऐप्लिकेशन में प्री-रोटेशन लागू करके डिवाइस ओरिएंटेशन को असरदार तरीके से मैनेज करने का तरीका बताया गया है.
Vulkan में, OpenGL की तुलना में रेंडरिंग की स्थिति के बारे में काफ़ी ज़्यादा जानकारी दी जा सकती है. Vulkan में, आपको वे चीज़ें खुद लागू करनी होंगी जिन्हें OpenGL में ड्राइवर मैनेज करता है. जैसे, आपको डिवाइस ओरिएंटेशन और रेंडर सरफ़ेस ओरिएंटेशन के साथ इसके संबंध की जानकारी देनी होगी. Android, डिवाइस के रेंडर सरफ़ेस को डिवाइस के ओरिएंटेशन के साथ तीन तरीकों से मैच कर सकता है:
- Android OS, डिवाइस की डिसप्ले प्रोसेसिंग यूनिट (डीपीयू) का इस्तेमाल कर सकता है. यह यूनिट, हार्डवेयर में डिसप्ले रोटेशन को आसानी से मैनेज कर सकती है. यह सुविधा, सिर्फ़ ज़रूरी शर्तें पूरी करने वाले डिवाइसों पर उपलब्ध है.
- Android OS, कंपोज़िटर पास जोड़कर डिसप्ले रोटेशन को मैनेज कर सकता है. इससे परफ़ॉर्मेंस पर असर पड़ेगा. यह इस बात पर निर्भर करेगा कि कंपोज़िटर को आउटपुट इमेज को रोटेट करने के लिए क्या करना होगा.
- ऐप्लिकेशन, घुमाई गई इमेज को खुद उस रेंडर सरफ़ेस पर रेंडर कर देगा जो डिसप्ले के मौजूदा ओरिएंटेशन से मेल खाता हो.
आपको इनमें से कौनसा तरीका इस्तेमाल करना चाहिए?
फ़िलहाल, कोई भी ऐप्लिकेशन यह पता नहीं लगा सकता कि ऐप्लिकेशन के बजाय किसी और ऐप्लिकेशन में किए जाने वाले डिसप्ले रोटेशन के लिए किसी और रिसॉर्स का इस्तेमाल किया गया या नहीं. अगर इस काम के लिए कोई डीपीयू उपलब्ध है, तब भी आपको परफ़ॉर्मेंस के लिहाज़ से अच्छी-खासी पेनल्टी चुकानी पड़ सकती है. अगर आपका ऐप्लिकेशन सीपीयू पर निर्भर है, तो यह बिजली की समस्या बन जाती है. ऐसा इसलिए होता है, क्योंकि Android कंपोज़िटर, जीपीयू का ज़्यादा इस्तेमाल करता है. यह आम तौर पर बढ़ी हुई फ़्रीक्वेंसी पर काम करता है. अगर आपका ऐप्लिकेशन जीपीयू पर निर्भर है, तो Android कंपोज़िटर आपके ऐप्लिकेशन के जीपीयू के काम को भी रोक सकता है. इससे परफ़ॉर्मेंस में और गिरावट आ सकती है.
Pixel 4XL पर शिपिंग टाइटल चलाने के दौरान, हमने देखा कि SurfaceFlinger (Android Compositor को चलाने वाला ज़्यादा प्राथमिकता वाला टास्क):
ऐप्लिकेशन के काम में लगातार रुकावट डालता है. इससे फ़्रेमटाइम पर 1 से 3 मि॰से॰ का असर पड़ता है.
जीपीयू की वर्टेक्स/टेक्सचर मेमोरी पर ज़्यादा दबाव डालता है, क्योंकि कंपोज़िटर को कंपोज़ करने के लिए पूरे फ़्रेमबफ़र को पढ़ना पड़ता है.
ओरिएंटेशन को सही तरीके से मैनेज करने पर, SurfaceFlinger के, GPU को प्रीम्प्ट करने की समस्या करीब-करीब पूरी तरह से खत्म हो जाती है. इससे, GPU की फ़्रीक्वेंसी में 40% की गिरावट आती है, क्योंकि इसके बाद, Android Compositor की ओर से इस्तेमाल की जाने वाली बूस्ट की गई फ़्रीक्वेंसी की ज़रूरत नहीं होती.
जैसा कि ऊपर बताया गया है, रोटेशन को कम से कम ओवरहेड के साथ सही तरीके से मैनेज किया जा सकता है. ऐसा करने के लिए तीसरे तरीके का इस्तेमाल करना चाहिए. इसे प्री-रोटेशन कहा जाता है. इससे Android OS को पता चलता है कि आपका ऐप्लिकेशन, डिसप्ले रोटेशन को मैनेज करता है. इसके लिए, आपको स्वैपचेन बनाते समय, ओरिएंटेशन के बारे में बताने वाले सरफ़ेस ट्रांसफ़ॉर्म फ़्लैग पास करने होंगे. इससे, Android Compositor अपने-आप रोटेशन नहीं करता.
हर Vulkan ऐप्लिकेशन के लिए, यह जानना ज़रूरी है कि Surface Transform फ़्लैग कैसे सेट किया जाता है. ऐप्लिकेशन, आम तौर पर एक से ज़्यादा ओरिएंटेशन के साथ काम करते हैं या ऐसे ओरिएंटेशन के साथ काम करते हैं जहां रेंडर सरफ़ेस, डिवाइस के आइडेंटिटी ओरिएंटेशन से अलग होता है. उदाहरण के लिए, पोर्ट्रेट मोड में इस्तेमाल होने वाले फ़ोन पर सिर्फ़ लैंडस्केप मोड में इस्तेमाल होने वाला ऐप्लिकेशन या लैंडस्केप मोड में इस्तेमाल होने वाले टैबलेट पर सिर्फ़ पोर्ट्रेट मोड में इस्तेमाल होने वाला ऐप्लिकेशन.
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() में 0.120 से 0.250 मि॰से॰ लगे. वहीं, Android 8 पर चलने वाले Pixel 1XL पर, पोलिंग में 0.110 से 0.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() फ़ंक्शन में, स्वैपचेन को फिर से बनाने के लिए सभी ज़रूरी चरण पूरे करने होंगे. इसमें ये चीज़ें शामिल हैं:
आपको
FramebufferऔरImageViewके सभी मौजूदा इंस्टेंस पूरी तरह मिटाने होंगे.आपको पुरानी स्वैपचेन मिटाने के साथ-साथ, नई स्वैपचेन बनानी होगा. इसके बारे में अगले सेक्शन में बताया जाएगा.
नई स्वैपचेन की 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 ऑपरेटिंग सिस्टम से मिले फ़्लैग से मैच करने के लिए सेट किया गया हो. इससे कंपोज़िटर के ओवरहेड से बचा जा सकेगा.
- स्वैपचेन का साइज़, डिसप्ले के नैचुरल ओरिएंटेशन में ऐप्लिकेशन की विंडो के आइडेंटिटी रिज़ॉल्यूशन के हिसाब से तय करें.
- डिवाइस के ओरिएंटेशन के हिसाब से, क्लिप स्पेस में एमवीपी मैट्रिक्स को घुमाएं. ऐसा इसलिए, क्योंकि स्वैपचेन रिज़ॉल्यूशन/एक्सटेंट अब डिसप्ले के ओरिएंटेशन के साथ अपडेट नहीं होता है.
- अपने ऐप्लिकेशन की ज़रूरत के हिसाब से, व्यूपोर्ट और सिज़र रेक्टैंगल अपडेट करें.
सैंपल ऐप्लिकेशन: Android में प्री-रोटेशन से जुड़ी बुनियादी सुविधाएं