এই প্রবন্ধে বর্ণনা করা হয়েছে কিভাবে আপনার Vulkan অ্যাপ্লিকেশনে প্রি-রোটেশন বাস্তবায়নের মাধ্যমে ডিভাইস ঘূর্ণন দক্ষতার সাথে পরিচালনা করবেন।
Vulkan এর সাহায্যে, আপনি OpenGL এর তুলনায় রেন্ডারিং অবস্থা সম্পর্কে অনেক বেশি তথ্য নির্দিষ্ট করতে পারবেন। Vulkan এর সাহায্যে, আপনাকে OpenGL এর ড্রাইভার দ্বারা পরিচালিত জিনিসগুলি স্পষ্টভাবে বাস্তবায়ন করতে হবে, যেমন ডিভাইস ওরিয়েন্টেশন এবং রেন্ডার সারফেস ওরিয়েন্টেশনের সাথে এর সম্পর্ক। ডিভাইসের রেন্ডার সারফেসকে ডিভাইস ওরিয়েন্টেশনের সাথে সামঞ্জস্য করার জন্য Android তিনটি উপায়ে পরিচালনা করতে পারে:
- অ্যান্ড্রয়েড অপারেটিং সিস্টেম ডিভাইসের ডিসপ্লে প্রসেসিং ইউনিট (DPU) ব্যবহার করতে পারে, যা হার্ডওয়্যারে পৃষ্ঠ ঘূর্ণন দক্ষতার সাথে পরিচালনা করতে পারে। শুধুমাত্র সমর্থিত ডিভাইসগুলিতে উপলব্ধ।
- অ্যান্ড্রয়েড অপারেটিং সিস্টেম একটি কম্পোজিটর পাস যোগ করে পৃষ্ঠ ঘূর্ণন পরিচালনা করতে পারে। এর পারফরম্যান্স খরচ নির্ভর করবে কম্পোজিটর আউটপুট ইমেজ ঘূর্ণনের সাথে কীভাবে মোকাবিলা করবে তার উপর।
- অ্যাপ্লিকেশনটি নিজেই একটি ঘূর্ণিত চিত্রকে একটি রেন্ডার পৃষ্ঠের উপর রেন্ডার করে পৃষ্ঠের ঘূর্ণন পরিচালনা করতে পারে যা ডিসপ্লের বর্তমান ওরিয়েন্টেশনের সাথে মেলে।
এই পদ্ধতিগুলির মধ্যে কোনটি আপনার ব্যবহার করা উচিত?
বর্তমানে, কোনও অ্যাপ্লিকেশনের পক্ষে জানার কোনও উপায় নেই যে অ্যাপ্লিকেশনের বাইরে পৃষ্ঠের ঘূর্ণন বিনামূল্যে পরিচালিত হবে কিনা। এমনকি যদি আপনার জন্য এটির যত্ন নেওয়ার জন্য একটি DPU থাকে, তবুও সম্ভবত একটি পরিমাপযোগ্য কর্মক্ষমতা জরিমানা দিতে হবে। যদি আপনার অ্যাপ্লিকেশনটি CPU-আবদ্ধ হয়, তবে Android Compositor দ্বারা বর্ধিত GPU ব্যবহার, যা সাধারণত বর্ধিত ফ্রিকোয়েন্সিতে চলছে, এটি একটি পাওয়ার সমস্যা হয়ে দাঁড়ায়। যদি আপনার অ্যাপ্লিকেশন GPU-আবদ্ধ হয়, তাহলে Android Compositor আপনার অ্যাপ্লিকেশনের GPU কাজকে অগ্রাহ্য করতে পারে, যার ফলে অতিরিক্ত কর্মক্ষমতা ক্ষতি হতে পারে।
Pixel 4XL-এ শিপিং শিরোনাম চালানোর সময়, আমরা দেখেছি যে SurfaceFlinger (অ্যান্ড্রয়েড কম্পোজিটরকে চালিত করে এমন উচ্চ-অগ্রাধিকারের কাজ):
নিয়মিতভাবে অ্যাপ্লিকেশনের কাজকে প্রিম্পট করে, যার ফলে ফ্রেমটাইমে ১-৩ মিলিসেকেন্ড হিট হয়, এবং
GPU-এর ভার্টেক্স/টেক্সচার মেমোরির উপর চাপ বৃদ্ধি করে, কারণ কম্পোজিটরকে তার কম্পোজিশনের কাজ করার জন্য পুরো ফ্রেমবাফারটি পড়তে হয়।
ওরিয়েন্টেশন সঠিকভাবে পরিচালনা করলে SurfaceFlinger দ্বারা GPU প্রিম্পশন প্রায় সম্পূর্ণরূপে বন্ধ হয়ে যায়, অন্যদিকে Android Compositor দ্বারা ব্যবহৃত বুস্টেড ফ্রিকোয়েন্সি আর প্রয়োজন না হওয়ায় GPU ফ্রিকোয়েন্সি 40% কমে যায়।
পূর্ববর্তী ক্ষেত্রে যেমনটি দেখা গেছে, যতটা সম্ভব কম ওভারহেড ব্যবহার করে পৃষ্ঠের ঘূর্ণন সঠিকভাবে পরিচালনা করা নিশ্চিত করার জন্য, আপনার পদ্ধতি 3 প্রয়োগ করা উচিত। এটি প্রি-রোটেশন নামে পরিচিত। এটি অ্যান্ড্রয়েড ওএসকে বলে যে আপনার অ্যাপ পৃষ্ঠের ঘূর্ণন পরিচালনা করে। আপনি সোয়াপচেইন তৈরির সময় ওরিয়েন্টেশন নির্দিষ্ট করে এমন পৃষ্ঠের রূপান্তর ফ্ল্যাগগুলি পাস করে এটি করতে পারেন। এটি অ্যান্ড্রয়েড কম্পোজিটরকে নিজেই ঘূর্ণন করা থেকে বিরত রাখে ।
প্রতিটি Vulkan অ্যাপ্লিকেশনের জন্য সারফেস ট্রান্সফর্ম ফ্ল্যাগ কীভাবে সেট করতে হয় তা জানা গুরুত্বপূর্ণ। অ্যাপ্লিকেশনগুলি হয় একাধিক ওরিয়েন্টেশন সমর্থন করে অথবা একটি একক ওরিয়েন্টেশন সমর্থন করে যেখানে রেন্ডার সারফেসটি ডিভাইসটি যা বিবেচনা করে তার থেকে ভিন্ন ওরিয়েন্টেশনে থাকে। উদাহরণস্বরূপ, একটি পোর্ট্রেট-আইডেন্টিটি ফোনে একটি ল্যান্ডস্কেপ-অ্যাপ্লিকেশন, অথবা একটি ল্যান্ডস্কেপ-অ্যাপ্লিকেশন ট্যাবলেটে একটি পোর্ট্রেট-অ্যাপ্লিকেশন।
AndroidManifest.xml পরিবর্তন করুন
আপনার অ্যাপে ডিভাইস রোটেশন পরিচালনা করতে, অ্যাপ্লিকেশনের AndroidManifest.xml ফাইলটি পরিবর্তন করে অ্যান্ড্রয়েডকে বলুন যে আপনার অ্যাপটি ওরিয়েন্টেশন এবং স্ক্রিন সাইজের পরিবর্তনগুলি পরিচালনা করবে। এটি অ্যান্ড্রয়েডকে অ্যান্ড্রয়েড Activity ধ্বংস এবং পুনরায় তৈরি করতে এবং ওরিয়েন্টেশন পরিবর্তনের সময় বিদ্যমান উইন্ডো পৃষ্ঠে onDestroy() ফাংশনটি কল করতে বাধা দেয়। এটি অ্যাক্টিভিটির configChanges বিভাগে orientation (API লেভেল <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 যা অ্যাপ্লিকেশনের প্রধান রেন্ডারিং লুপ থেকে অ্যাক্সেসযোগ্য।
ডিভাইসের ওরিয়েন্টেশন পরিবর্তনগুলি সনাক্ত করুন (প্রি-অ্যান্ড্রয়েড 10)
Android 10 বা তার বেশি পুরনো ভার্সন চালিত ডিভাইসগুলির জন্য, একটি ভিন্ন বাস্তবায়ন প্রয়োজন, কারণ VK_SUBOPTIMAL_KHR সমর্থিত নয়।
পোলিং ব্যবহার করা
প্রাক-অ্যান্ড্রয়েড ১০ ডিভাইসে আপনি প্রতিটি 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-.250ms এবং Android 8 চালিত Pixel 1XL-এ, পোলিং .110-.350ms সময় নিয়েছে।
কলব্যাক ব্যবহার করা
অ্যান্ড্রয়েড ১০ এর নিচে চলমান ডিভাইসগুলির জন্য দ্বিতীয় বিকল্প হল একটি 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-ডিগ্রি ওরিয়েন্টেশন পরিবর্তনের জন্য ডাকা হয়, যেমন ল্যান্ডস্কেপ থেকে পোর্ট্রেটে যাওয়া বা তদ্বিপরীত। অন্যান্য ওরিয়েন্টেশন পরিবর্তনগুলি সোয়াপচেইন রিক্রিয়েশনকে ট্রিগার করবে না। উদাহরণস্বরূপ, ল্যান্ডস্কেপ থেকে রিভার্স-ল্যান্ডস্কেপে পরিবর্তন এটিকে ট্রিগার করবে না, যার ফলে অ্যান্ড্রয়েড কম্পোজিটরকে আপনার অ্যাপ্লিকেশনের জন্য ফ্লিপ করতে হবে।
ওরিয়েন্টেশন পরিবর্তন পরিচালনা করা
ওরিয়েন্টেশন পরিবর্তন পরিচালনা করার জন্য, orientationChanged ভেরিয়েবলটি সত্যে সেট করা থাকলে প্রধান রেন্ডারিং লুপের শীর্ষে ওরিয়েন্টেশন পরিবর্তন রুটিনটি কল করুন। উদাহরণস্বরূপ:
bool VulkanDrawFrame() {
if (orientationChanged) {
OnOrientationChange();
}
OnOrientationChange() ফাংশনের মধ্যে swapchain পুনরায় তৈরি করার জন্য প্রয়োজনীয় সমস্ত কাজ আপনি করেন। এর অর্থ হল আপনি:
FramebufferএবংImageViewএর বিদ্যমান যেকোনো উদাহরণ ধ্বংস করুন,পুরাতন সোয়াপচেইন (যা পরবর্তীতে আলোচনা করা হবে) ধ্বংস করার সময় সোয়াপচেইনটি পুনরায় তৈরি করুন, এবং
নতুন সোয়াপচেইনের ডিসপ্লে ইমেজ ব্যবহার করে ফ্রেমবাফারগুলি পুনরায় তৈরি করুন। দ্রষ্টব্য: সংযুক্তি চিত্রগুলি (উদাহরণস্বরূপ, গভীরতা/স্টেন্সিল চিত্রগুলি) সাধারণত পুনরায় তৈরি করার প্রয়োজন হয় না কারণ এগুলি আগে থেকে ঘোরানো সোয়াপচেইন চিত্রগুলির পরিচয় রেজোলিউশনের উপর ভিত্তি করে তৈরি করা হয়।
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 ফ্ল্যাগটিকে false এ রিসেট করবেন যাতে দেখা যাবে যে আপনি orientation পরিবর্তনটি পরিচালনা করেছেন।
সোয়াপচেইন রিক্রিয়েশন
আগের অংশে আমরা উল্লেখ করেছি যে সোয়াপচেইনটি পুনরায় তৈরি করতে হবে। এটি করার প্রথম ধাপ হল রেন্ডারিং পৃষ্ঠের নতুন বৈশিষ্ট্যগুলি অর্জন করা:
void createSwapChain(VkSwapchainKHR oldSwapchain) {
VkSurfaceCapabilitiesKHR capabilities;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physDevice, surface, &capabilities);
pretransformFlag = capabilities.currentTransform;
VkSurfaceCapabilities struct নতুন তথ্য দিয়ে পূর্ণ হওয়ার সাথে সাথে, আপনি এখন currentTransform ফিল্ডটি পরীক্ষা করে দেখতে পারেন যে কোনও ওরিয়েন্টেশন পরিবর্তন হয়েছে কিনা। আপনি এটিকে পরবর্তী সময়ের জন্য pretransformFlag ফিল্ডে সংরক্ষণ করবেন কারণ MVP ম্যাট্রিক্সে সামঞ্জস্য করার সময় আপনার এটির প্রয়োজন হবে।
এটি করার জন্য, 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 ফিল্ডটিকে swapchain এও সেট করবেন যা ধ্বংস হয়ে যাবে।
এমভিপি ম্যাট্রিক্স সমন্বয়
আপনার MVP ম্যাট্রিক্সে একটি ঘূর্ণন ম্যাট্রিক্স প্রয়োগ করে প্রাক-রূপান্তর প্রয়োগ করতে হবে। এটি মূলত যা করে তা হল ক্লিপ স্পেসে ঘূর্ণন প্রয়োগ করা যাতে ফলাফলটি বর্তমান ডিভাইস ওরিয়েন্টেশনে ঘোরানো হয়। এরপর আপনি কেবল এই আপডেট করা MVP ম্যাট্রিক্সটি আপনার ভার্টেক্স শেডারে পাস করতে পারেন এবং আপনার শেডারগুলি পরিবর্তন করার প্রয়োজন ছাড়াই এটিকে স্বাভাবিক হিসাবে ব্যবহার করতে পারেন।
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 এর মতো ডেরিভেটিভ কম্পিউটেশন ব্যবহার করে, তাহলে ঘূর্ণিত স্থানাঙ্ক সিস্টেমের জন্য অতিরিক্ত রূপান্তরের প্রয়োজন হতে পারে কারণ এই কম্পিউটেশনগুলি পিক্সেল স্পেসে কার্যকর করা হয়। এর জন্য অ্যাপটিকে প্রিট্রান্সফর্মের কিছু ইঙ্গিত ফ্র্যাগমেন্ট শেডারে (যেমন বর্তমান ডিভাইস ওরিয়েন্টেশন প্রতিনিধিত্বকারী একটি পূর্ণসংখ্যা) পাস করতে হবে এবং ডেরিভেটিভ কম্পিউটেশনগুলি সঠিকভাবে ম্যাপ করতে এটি ব্যবহার করতে হবে:
- ৯০ ডিগ্রি পূর্বে ঘোরানো ফ্রেমের জন্য
- dFdx কে dFdy তে ম্যাপ করতে হবে
- dFdy কে -dFdx- এ ম্যাপ করতে হবে
- ২৭০ ডিগ্রি পূর্বে ঘোরানো ফ্রেমের জন্য
- dFdx কে -dFdy তে ম্যাপ করতে হবে
- dFdy কে dFdx এর সাথে ম্যাপ করতে হবে
- ১৮০ ডিগ্রি পূর্বে ঘোরানো ফ্রেমের জন্য,
- dFdx কে -dFdx- এ ম্যাপ করতে হবে
- dFdy কে -dFdy তে ম্যাপ করতে হবে
উপসংহার
অ্যান্ড্রয়েডে Vulkan থেকে সর্বাধিক সুবিধা পেতে আপনার অ্যাপ্লিকেশনটির জন্য, প্রি-রোটেশন বাস্তবায়ন করা আবশ্যক। এই নিবন্ধ থেকে সবচেয়ে গুরুত্বপূর্ণ বিষয়গুলি হল:
- সোয়াপচেইন তৈরি বা পুনর্নির্মাণের সময়, প্রিট্রান্সফর্ম ফ্ল্যাগটি অ্যান্ড্রয়েড অপারেটিং সিস্টেম দ্বারা প্রদত্ত ফ্ল্যাগের সাথে মেলে কিনা তা নিশ্চিত করুন। এটি কম্পোজিটরের ওভারহেড এড়াবে।
- ডিসপ্লের স্বাভাবিক অভিযোজনে অ্যাপের উইন্ডো পৃষ্ঠের পরিচয় রেজোলিউশনের সাথে সোয়াপচেইনের আকার স্থির রাখুন।
- ডিভাইসের ওরিয়েন্টেশনের হিসাব করার জন্য ক্লিপ স্পেসে MVP ম্যাট্রিক্স ঘোরান, কারণ সোয়াপচেইন রেজোলিউশন/এক্সটেন্ট আর ডিসপ্লের ওরিয়েন্টেশনের সাথে আপডেট হয় না।
- আপনার অ্যাপ্লিকেশনের প্রয়োজন অনুযায়ী ভিউপোর্ট এবং কাঁচি আয়তক্ষেত্র আপডেট করুন।
নমুনা অ্যাপ: ন্যূনতম অ্যান্ড্রয়েড প্রি-রোটেশন