Vulkan প্রি-রোটেশনের মাধ্যমে ডিভাইসের ওরিয়েন্টেশন ম্যানেজ করা

এই নিবন্ধে প্রি-রোটেশন প্রয়োগ করে আপনার Vulkan অ্যাপ্লিকেশনে ডিভাইস রোটেশন কীভাবে দক্ষতার সাথে ম্যানেজ করবেন তা বর্ণনা করা হয়েছে।

Vulkan-এর মাধ্যমে, আপনি OpenGL-এর তুলনায় রেন্ডারিং স্টেট সম্পর্কে অনেক বেশি তথ্য নির্দিষ্ট করতে পারবেন। Vulkan-এর মাধ্যমে, আপনাকে অবশ্যই স্পষ্টভাবে এমন জিনিসগুলি প্রয়োগ করতে হবে যা OpenGL-এ ড্রাইভার দ্বারা পরিচালিত হয়, যেমন ডিভাইস ওরিয়েন্টেশন এবং রেন্ডার সারফেস ওরিয়েন্টেশন-এর সাথে এর সম্পর্ক। Android তিনটি উপায়ে ডিভাইসের ওরিয়েন্টেশনের সাথে ডিভাইসের রেন্ডার সারফেসের রিকনসিলিং ম্যানেজ করতে পারে:

  1. Android OS ডিভাইসের ডিসপ্লে প্রসেসিং ইউনিট (DPU) ব্যবহার করতে পারে, যা হার্ডওয়্যারে সারফেস রোটেশন দক্ষতার সাথে ম্যানেজ করতে পারে। শুধুমাত্র কাজ করে এমন ডিভাইসে উপলভ্য।
  2. Android OS কম্পোজিটর পাস যোগ করে সারফেস রোটেশন ম্যানেজ করতে পারে। আউটপুট ইমেজ রোটেট করার সময় কম্পোজিটরকে যেভাবে কাজ করতে হয় তার উপর নির্ভর করে এটির পারফর্ম্যান্সের খরচ নির্ধারিত হবে।
  3. অ্যাপ্লিকেশন নিজেই সারফেস রোটেশন ম্যানেজ করতে পারে। এটি করার জন্য, এটি একটি রেন্ডার সারফেসে একটি রোটেট করা ছবি রেন্ডার করে যা ডিসপ্লের বর্তমান ওরিয়েন্টেশনের সাথে মেলে।

এইসব পদ্ধতির মধ্যে কোনটি আপনার ব্যবহার করা উচিত?

বর্তমানে, অ্যাপ্লিকেশনের বাইরে সারফেস রোটেশন হ্যান্ডেল করা হলে তা ফ্রি হবে কিনা তা জানার কোনও উপায় নেই। এমনকি আপনার জন্য এটি দেখভাল করার জন্য একটি DPU থাকলেও, আপনাকে এখনও পরিমাপযোগ্য পারফর্ম্যান্স পেনাল্টি দিতে হবে। আপনার অ্যাপ্লিকেশন CPU-বাউন্ড হলে, Android কম্পোজিটর দ্বারা GPU-এর ব্যবহার বৃদ্ধি পাওয়ার কারণে পাওয়ার সংক্রান্ত সমস্যা দেখা দেয়, যা সাধারণত বুস্ট করা ফ্রিকোয়েন্সিতে রান করে। আপনার অ্যাপ্লিকেশন GPU বাউন্ড হলে, Android Compositor আপনার অ্যাপ্লিকেশনের GPU কাজও প্রি-এম্পট করতে পারে, যার ফলে অতিরিক্ত পারফর্ম্যান্স ক্ষতি হয়।

Pixel 4XL-এ শিপিং টাইটেল চালানোর সময় আমরা দেখেছি যে SurfaceFlinger (Android Compositor-কে ড্রাইভ করে এমন বেশি-অগ্রাধিকারের টাস্ক):

  • নিয়মিতভাবে অ্যাপ্লিকেশনের কাজকে প্রি-এমপ্ট করে, যার ফলে ফ্রেমটাইমে ১-৩ মিলি সেকেন্ডের হিট হয় এবং

  • GPU-এর ভার্টেক্স/টেক্সচার মেমরির উপর বেশি চাপ সৃষ্টি করে, কারণ কম্পোজিটরকে তার কম্পোজিশনের কাজ করার জন্য সম্পূর্ণ ফ্রেমবাফার পড়তে হয়।

ওরিয়েন্টেশন সঠিকভাবে ম্যানেজ করলে SurfaceFlinger-এর মাধ্যমে GPU প্রি-এমপশন প্রায় সম্পূর্ণভাবে বন্ধ হয়ে যায়, একইসাথে GPU ফ্রিকোয়েন্সি ৪০% কমে যায় কারণ Android কম্পোজিটর দ্বারা ব্যবহৃত বুস্ট করা ফ্রিকোয়েন্সির আর প্রয়োজন হয় না।

সারফেস রোটেশন যাতে যতটা সম্ভব কম ওভারহেডের সাথে সঠিকভাবে পরিচালনা করা হয় তা নিশ্চিত করতে, আগের ক্ষেত্রে যেমন দেখা গেছে, আপনাকে পদ্ধতি ৩ প্রয়োগ করতে হবে। এটি প্রি-রোটেশন নামে পরিচিত। এটি Android OS-কে জানায় যে আপনার অ্যাপ সারফেস রোটেশন ম্যানেজ করে। আপনি সারফেস ট্রান্সফর্ম ফ্ল্যাগ পাস করে এটি করতে পারেন যা সোয়াপচেন তৈরির সময় ওরিয়েন্টেশন নির্দিষ্ট করে। এটি Android Compositor-কে নিজেই রোটেশন করা থেকে আটকায়।

সারফেস ট্রান্সফর্ম ফ্ল্যাগ কীভাবে সেট করতে হয় তা জানা প্রতিটি Vulkan অ্যাপ্লিকেশনের জন্য গুরুত্বপূর্ণ। অ্যাপ্লিকেশনগুলি একাধিক ওরিয়েন্টেশন সমর্থন করে বা একটি একক ওরিয়েন্টেশন সমর্থন করে যেখানে রেন্ডার সারফেসটি ডিভাইসের পরিচয় ওরিয়েন্টেশন হিসাবে যা বিবেচনা করে তার থেকে আলাদা ওরিয়েন্টেশনে থাকে। যেমন, পোর্ট্রেট-আইডেন্টিটি ফোনে শুধুমাত্র ল্যান্ডস্কেপ অ্যাপ্লিকেশন, অথবা ল্যান্ডস্কেপ-আইডেন্টিটি ট্যাবলেটে শুধুমাত্র পোর্ট্রেট অ্যাপ্লিকেশন।

AndroidManifest.xml পরিবর্তন করুন

আপনার অ্যাপে ডিভাইস রোটেশন ম্যানেজ করতে, অ্যাপ্লিকেশনের AndroidManifest.xml ফাইল পরিবর্তন করে শুরু করুন যাতে Android-কে জানানো যায় যে আপনার অ্যাপ ওরিয়েন্টেশন ও স্ক্রিন সাইজের পরিবর্তন ম্যানেজ করবে। এটি Android-কে Android Activity ধ্বংস ও আবার তৈরি করা এবং ওরিয়েন্টেশন পরিবর্তন হলে, বর্তমান উইন্ডো সারফেসে onDestroy() ফাংশন কল করা থেকে আটকায়। এটি করার জন্য অ্যাক্টিভিটির configChanges বিভাগে orientation (API লেভেল <১৩-এর জন্য) এবং screenSize অ্যাট্রিবিউট যোগ করতে হবে:

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

আপনার অ্যাপ্লিকেশন screenOrientation অ্যাট্রিবিউট ব্যবহার করে স্ক্রিন ওরিয়েন্টেশন ঠিক করলে, আপনাকে এটি করতে হবে না। এছাড়াও, আপনার অ্যাপ্লিকেশন যদি একটি নির্দিষ্ট ওরিয়েন্টেশন ব্যবহার করে, তাহলে অ্যাপ্লিকেশন স্টার্টআপ/রিজিউম করার সময় শুধুমাত্র একবার সোয়াপচেন সেট-আপ করতে হবে।

পরিচয় স্ক্রিন রেজোলিউশন এবং ক্যামেরা প্যারামিটার পান

এরপর, VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR ভ্যালুর সাথে সম্পর্কিত ডিভাইসের স্ক্রিন রেজোলিউশন শনাক্ত করুন। এই রেজোলিউশন ডিভাইসের আইডেন্টিটি ওরিয়েন্টেশনের সাথে যুক্ত এবং তাই swapchain-কে সবসময় এই রেজোলিউশনে সেট করতে হবে। এটি পাওয়ার সবচেয়ে নির্ভরযোগ্য উপায় হল অ্যাপ্লিকেশন স্টার্টআপে 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 ১০+)

আপনার অ্যাপ্লিকেশনে ওরিয়েন্টেশন পরিবর্তন শনাক্ত করার সবচেয়ে নির্ভরযোগ্য উপায় হল 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() কল করতে হবে এবং তারপর বর্তমানে স্টোর করা সারফেস ট্রান্সফর্মেশনের (এই কোডের উদাহরণে pretransformFlag-এ স্টোর করা আছে) সাথে রিটার্ন করা currentTransform ফিল্ডের তুলনা করতে হবে।

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

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

Android 10 অপারেটিং সিস্টেম সহ Pixel 4 ডিভাইসে, পোলিং vkGetPhysicalDeviceSurfaceCapabilitiesKHR() করতে .১২০-.২৫০ মিলি সেকেন্ড সময় লেগেছে এবং Android 8 অপারেটিং সিস্টেম সহ Pixel 1XL ডিভাইসে, পোলিং করতে .১১০-.৩৫০ মিলি সেকেন্ড সময় লেগেছে।

কলব্যাক ব্যবহার করা

Android ১০-এর আগের ভার্সন চলছে এমন ডিভাইসের জন্য দ্বিতীয় বিকল্প হল onNativeWindowResized() কলব্যাক রেজিস্টার করা যাতে এমন একটি ফাংশন কল করা যায় যা orientationChanged ফ্ল্যাগ সেট করে, অ্যাপ্লিকেশনকে সিগন্যাল দেয় যে ওরিয়েন্টেশন পরিবর্তন হয়েছে:

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

যেখানে ResizeCallback এইভাবে সংজ্ঞায়িত করা হয়েছে:

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

এই সমাধানের সমস্যা হল যে onNativeWindowResized() শুধুমাত্র ৯০-ডিগ্রি ওরিয়েন্টেশন পরিবর্তনের জন্য কল করা হয়, যেমন ল্যান্ডস্কেপ থেকে পোর্ট্রেট বা এর উল্টোটা। অন্যান্য ওরিয়েন্টেশন পরিবর্তন সোয়াপচেন রিক্রিয়েশন ট্রিগার করবে না। যেমন, ল্যান্ডস্কেপ থেকে রিভার্স-ল্যান্ডস্কেপে পরিবর্তন করলে এটি ট্রিগার হবে না, আপনার অ্যাপ্লিকেশনের জন্য ফ্লিপ করতে Android কম্পোজিটরকে প্রয়োজন হবে।

ওরিয়েন্টেশন পরিবর্তন ম্যানেজ করা

ওরিয়েন্টেশন পরিবর্তন ম্যানেজ করতে, orientationChanged ভেরিয়েবল true-তে সেট করা থাকলে, মূল রেন্ডারিং লুপের উপরে ওরিয়েন্টেশন পরিবর্তন রুটিন কল করুন। যেমন:

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

OnOrientationChange() ফাংশনের মধ্যে সোয়াপচেন আবার তৈরি করার জন্য প্রয়োজনীয় সব কাজ আপনি করেন। এর অর্থ হল, আপনি:

  1. Framebuffer এবং ImageView-এর আগে থেকে থাকা যেকোনও ইনস্ট্যান্স ধ্বংস করুন,

  2. পুরনো swapchain (যা পরে আলোচনা করা হবে) নষ্ট করার সময় swapchain আবার তৈরি করুন এবং

  3. নতুন Swapchain-এর DisplayImages-এর সাহায্যে Framebuffers আবার তৈরি করুন। মনে রাখবেন: অ্যাটাচমেন্ট ইমেজ (যেমন, ডেপথ/স্টেনসিল ইমেজ) সাধারণত আবার তৈরি করতে হয় না কারণ সেগুলি প্রি-রোটেটেড সোয়াপচেন ইমেজের আইডেন্টিটি রেজোলিউশনের উপর ভিত্তি করে তৈরি হয়।

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-এ রিসেট করেন যাতে বোঝা যায় যে আপনি ওরিয়েন্টেশন পরিবর্তন ম্যানেজ করেছেন।

Swapchain রিক্রিয়েশন

আগের বিভাগে আমরা swapchain আবার তৈরি করার কথা উল্লেখ করেছি। এটি করার প্রথম ধাপের মধ্যে রয়েছে রেন্ডারিং সারফেসের নতুন বৈশিষ্ট্যগুলি পাওয়া:

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

নতুন তথ্য সহ VkSurfaceCapabilities স্ট্রাক্ট পপুলেট করার মাধ্যমে, আপনি এখন 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 ম্যাট্রিক্সে একটি রোটেশন ম্যাট্রিক্স প্রয়োগ করে প্রি-ট্রান্সফর্মেশন প্রয়োগ করা। এটি মূলত ক্লিপ স্পেসে রোটেশন প্রয়োগ করে যাতে ফলাফল হিসেবে পাওয়া ছবিটি বর্তমান ডিভাইসের ওরিয়েন্টেশনে রোটেট করা হয়। তারপর আপনি এই আপডেট করা 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-এর মতো ডেরিভেটিভ কম্পিউটেশন ব্যবহার করে, তাহলে রোটেট করা কোঅর্ডিনেট সিস্টেমের জন্য অতিরিক্ত ট্রান্সফর্মেশনের প্রয়োজন হতে পারে কারণ এই কম্পিউটেশনগুলি পিক্সেল স্পেসে কার্যকর করা হয়। এর জন্য অ্যাপকে ফ্র্যাগমেন্ট শেডারে preTransform-এর কিছু ইঙ্গিত পাস করতে হবে (যেমন, বর্তমান ডিভাইস ওরিয়েন্টেশন দেখানো একটি পূর্ণসংখ্যা) এবং ডেরিভেটিভ কম্পিউটেশন সঠিকভাবে ম্যাপ করতে সেটি ব্যবহার করতে হবে:

  • ৯০ ডিগ্রি প্রি-রোটেটেড ফ্রেমের জন্য
    • dFdx অবশ্যই dFdy-এ ম্যাপ করতে হবে
    • dFdy অবশ্যই -dFdx-এ ম্যাপ করতে হবে
  • ২৭০ ডিগ্রি আগে থেকে ঘোরানো ফ্রেমের জন্য
    • dFdx অবশ্যই -dFdy-তে ম্যাপ করতে হবে
    • dFdy অবশ্যই dFdx-এ ম্যাপ করতে হবে
  • ১৮০ ডিগ্রি আগে থেকে ঘোরানো ফ্রেমের জন্য,
    • dFdx অবশ্যই -dFdx-এ ম্যাপ করতে হবে
    • dFdy অবশ্যই -dFdy-এ ম্যাপ করতে হবে

সিদ্ধান্ত

Android-এ Vulkan থেকে আপনার অ্যাপ্লিকেশন যাতে সবচেয়ে বেশি সুবিধা পেতে পারে, তার জন্য প্রি-রোটেশন প্রয়োগ করা আবশ্যক। এই নিবন্ধ থেকে সবচেয়ে গুরুত্বপূর্ণ বিষয়গুলি হল:

  • নিশ্চিত করুন যে সোয়াপচেন তৈরি বা আবার তৈরি করার সময়, প্রিট্রান্সফর্ম ফ্ল্যাগটি Android অপারেটিং সিস্টেমের মাধ্যমে ফেরত দেওয়া ফ্ল্যাগের সাথে ম্যাচ করার জন্য সেট করা হয়েছে। এটি কম্পোজিটর ওভারহেড এড়িয়ে যাবে।
  • ডিসপ্লের স্বাভাবিক ওরিয়েন্টেশনে অ্যাপের উইন্ডো সারফেসের আইডেন্টিটি রেজোলিউশনে সোয়াপচেন সাইজ ফিক্সড রাখুন।
  • ডিভাইসের ওরিয়েন্টেশন বিবেচনা করার জন্য ক্লিপ স্পেসে MVP ম্যাট্রিক্স ঘোরান, কারণ ডিসপ্লের ওরিয়েন্টেশনের সাথে সোয়াপচেন রেজোলিউশন/এক্সটেন্ট আর আপডেট হয় না।
  • আপনার অ্যাপ্লিকেশনের প্রয়োজন অনুযায়ী ভিউপোর্ট ও সিজর রেকট্যাঙ্গেল আপডেট করুন।

স্যাম্পেল অ্যাপ: Android-এ ন্যূনতম প্রি-রোটেশন