ভুলকান ফ্রেম পেসিং এক্সটেনশন

একটি মসৃণ গেমিং অভিজ্ঞতা প্রদানের জন্য ফ্রেম পেসিং অত্যন্ত গুরুত্বপূর্ণ। ফ্রেম পেসিং নিশ্চিত করে যে ফ্রেমগুলো নিয়মিত বিরতিতে প্রদর্শিত হয়, যার ফলে স্টাটার এবং ইনপুট ল্যাটেন্সি কমে আসে। যদিও বেশিরভাগ গেমের জন্য অ্যান্ড্রয়েড ফ্রেম পেসিং লাইব্রেরি (Swappy) একটি প্রস্তাবিত উচ্চ-স্তরের সমাধান, তবে ভলকান এক্সটেনশনের মাধ্যমে নিম্ন-স্তরের নিয়ন্ত্রণও পাওয়া যায়।

ফ্রেম উপস্থাপনার উপর সুনির্দিষ্ট নিয়ন্ত্রণ পেতে নিম্নলিখিত ভলকান ফ্রেম পেসিং এক্সটেনশনগুলি ব্যবহার করুন:

  • VK_GOOGLE_display_timing : নির্দিষ্ট সময়ে ফ্রেম প্রদর্শনের জন্য সময়সূচী নির্ধারণ করতে এবং রেন্ডারিং লুপ সামঞ্জস্য করার জন্য অতীতের প্রদর্শনের সময় অনুসন্ধান করতে দেয়।
  • VK_EXT_present_timing : একটি নতুন, প্রমিত এক্সটেনশন যা প্রেজেন্টেশন অনুরোধের জন্য ব্যাপক টাইমিং ফিডব্যাক প্রদান করে, যা অ্যান্ড্রয়েড ১৭ (এপিআই লেভেল ৩৭) এবং তার পরবর্তী সংস্করণগুলোতে চালু করা হয়েছে।

VK_GOOGLE_display_timing এক্সটেনশনটি পুরোনো এবং এটি আরও বেশি সংখ্যক অ্যান্ড্রয়েড ডিভাইসে সমর্থিত। তবে, নতুন ডিভাইসগুলোর ক্ষেত্রে VK_EXT_present_timing বেশি পছন্দনীয়, কারণ এটি আরও বেশি ফিচার এবং আরও বিস্তারিত টাইমিং তথ্য প্রদান করে।

VK_GOOGLE_display_timing

VK_GOOGLE_display_timing এক্সটেনশনটি অ্যাপ্লিকেশনগুলিকে নিম্নলিখিত কাজগুলি করার একটি উপায় প্রদান করে:

  1. একটি ডিসপ্লের রিফ্রেশ চক্রের সময়কাল অনুসন্ধান করুন।
  2. প্রতিটি ফ্রেমের জন্য একটি নির্বাচিত উপস্থাপনার সময় নির্দিষ্ট করুন।
  3. একটি ফিডব্যাক লুপ বাস্তবায়নের জন্য পূর্ববর্তী ফ্রেমগুলোর প্রকৃত উপস্থাপনার সময় কোয়েরি করুন।

এই এক্সটেনশনটি সেইসব গেমের জন্য উপযোগী, যেগুলো Swappy ব্যবহার না করে নিজস্ব ফ্রেম পেসিং অ্যালগরিদম প্রয়োগ করে।

এক্সটেনশনটি সক্রিয় করুন

VK_GOOGLE_display_timing ব্যবহার করতে, ভলকান ডিভাইস তৈরি করার সময় এটি সক্রিয় করুন। এক্সটেনশনটি সক্রিয় করার আগে, যাচাই করে নিন যে এটি ফিজিক্যাল ডিভাইস দ্বারা সমর্থিত কিনা:

// Check for extension support
uint32_t extensionCount;
vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extensionCount, nullptr);
std::vector<VkExtensionProperties> availableExtensions(extensionCount);
vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extensionCount, availableExtensions.data());

bool supported = false;
for (const auto& ext : availableExtensions) {
    if (strcmp(ext.extensionName, VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME) == 0) {
        supported = true;
        break;
    }
}

if (supported) {
    // Add to your enabled extensions list when calling vkCreateDevice
    enabledDeviceExtensions.push_back(VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME);
}

কোয়েরি প্রদর্শনের রিফ্রেশ সময়কাল

আপনি vkGetRefreshCycleDurationGOOGLE ব্যবহার করে একটি সোয়াপচেইনের সাথে যুক্ত ডিসপ্লের রিফ্রেশ সময়কাল জানতে পারেন।

VkRefreshCycleDurationGOOGLE refreshCycle;
vkGetRefreshCycleDurationGOOGLE(device, swapchain, &refreshCycle);
// refreshCycle.refreshDuration is the duration in nanoseconds

সময়সূচী কাঠামো উপস্থাপনা

একটি ফ্রেম কখন প্রদর্শিত হবে তা নির্দিষ্ট করতে, vkQueuePresentKHR কল করার সময় VkPresentInfoKHR- এর pNext চেইনে একটি VkPresentTimesInfoGOOGLE স্ট্রাকচার সংযুক্ত করুন:

VkPresentTimeGOOGLE presentTime = {};
presentTime.presentID = frameIndex; // Unique ID for this frame
presentTime.desiredPresentTime = targetTimeNs; // Target time in nanoseconds (CLOCK_MONOTONIC)

VkPresentTimesInfoGOOGLE presentTimesInfo = {};
presentTimesInfo.sType = VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE;
presentTimesInfo.swapchainCount = 1;
presentTimesInfo.pTimes = &presentTime;

VkPresentInfoKHR presentInfo = {};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.pNext = &presentTimesInfo;
// ... populate other presentInfo fields ...

vkQueuePresentKHR(queue, &presentInfo);

desiredPresentTime টি সিস্টেমের মনোটোনিক ক্লক ( CLOCK_MONOTONIC ) থেকে নেওয়া একটি টাইমস্ট্যাম্প হওয়া উচিত। ডিসপ্লের রিফ্রেশ রেট এবং পূর্ববর্তী ফ্রেমগুলোর প্রকৃত উপস্থাপনার সময়ের উপর ভিত্তি করে এই টার্গেট টাইমটি গণনা করুন। অ্যান্ড্রয়েডে, desiredPresentTime এর মান ০ হলে অথবা ১ সেকেন্ডের বেশি ভবিষ্যতের কোনো টাইমস্ট্যাম্প উপেক্ষা করা হয়।

VK_GOOGLE_display_timing ধরে নেয় যে সমস্ত টাইমস্ট্যাম্প একই ঘড়ি থেকে এসেছে। অ্যান্ড্রয়েডে, VK_GOOGLE_display_timing এবং VK_EXT_present_timing উভয়ের জন্য প্রাসঙ্গিক সমস্ত টাইমস্ট্যাম্প CLOCK_MONOTONIC ব্যবহার করে। (যদিও VK_EXT_present_timing একাধিক টাইম ডোমেইন সমর্থন করে, অ্যান্ড্রয়েডে ভিন্ন ঘড়ি ব্যবহার করার প্রয়োজন নেই)।

পূর্ববর্তী উপস্থাপনার সময় সম্পর্কে অনুসন্ধান করুন

আপনার ফ্রেম পেসিং লুপ সামঞ্জস্য করতে, অতীতের ফ্রেমগুলি কখন প্রদর্শিত হয়েছিল তা জানতে vkGetPastPresentationTimingGOOGLE ব্যবহার করুন:

uint32_t timingCount = 0;
// Query the number of available timings
vkGetPastPresentationTimingGOOGLE(device, swapchain, &timingCount, nullptr);

if (timingCount > 0) {
    std::vector<VkPastPresentationTimingGOOGLE> presentationTimings(timingCount);
    vkGetPastPresentationTimingGOOGLE(device, swapchain, &timingCount, presentationTimings.data());

    for (const auto& timing : presentationTimings) {
        // Use timing information to adjust your pacing algorithm
        // timing.presentID identifies the frame
        // timing.actualPresentTime is when the frame was displayed (nanoseconds)
        // timing.earliestPresentTime is the earliest the frame could have been displayed
        // timing.presentMargin is the slack time between GPU completion and presentation
    }
}

আপনার actualPresentTime সাথে desiredPresentTime তুলনা করে, ফ্রেমগুলো খুব তাড়াতাড়ি বা খুব দেরিতে আসছে কিনা তা আপনি নির্ধারণ করতে পারেন এবং সেই অনুযায়ী আপনার রেন্ডারিং লুপটি সামঞ্জস্য করতে পারেন।

কোডের উদাহরণ

একটি ভলকান রেন্ডারারে কিভাবে VK_GOOGLE_display_timing সংহত করতে হয় তার একটি সম্পূর্ণ কার্যকরী উদাহরণের জন্য, অ্যান্ড্রয়েড গেম এসডিকে রিপোজিটরিতে থাকা কিউব ডেমোটি দেখুন:

ডিসপ্লে টাইমিং সহ ভলকান কিউব ডেমো

এই ডেমোতে দেখানো হয়েছে কীভাবে এক্সটেনশনটি সক্রিয় করতে হয়, লক্ষ্য উপস্থাপনার সময় গণনা করতে হয় এবং একটি স্থিতিশীল ফ্রেম রেট বজায় রাখার জন্য পূর্ববর্তী উপস্থাপনার সময় থেকে প্রাপ্ত ফিডব্যাক প্রক্রিয়া করতে হয়।


VK_EXT_present_timing

Vulkan-এ প্রবর্তিত এবং Android 17 ও তার পরবর্তী সংস্করণগুলিতে সমর্থিত, VK_EXT_present_timing এক্সটেনশনটি হলো ফ্রেম উপস্থাপনা সম্পর্কে বিস্তারিত প্রতিক্রিয়া পাওয়ার একটি প্রমিত ও অধিক শক্তিশালী উপায়। এটি VK_GOOGLE_display_timing এর ধারণাগুলিকে প্রতিস্থাপন করে এবং সেগুলির পরিধি আরও বিস্তৃত করে।

VK_EXT_present_timing এর প্রধান সুবিধাগুলোর মধ্যে রয়েছে:

  • প্রমিত এপিআই : অফিসিয়াল ক্রোনোস ভলকান এক্সটেনশন সেটের একটি অংশ
  • বিস্তারিত পর্যায়ভিত্তিক অনুসন্ধান : প্রেজেন্টেশন পাইপলাইনের নির্দিষ্ট পর্যায়গুলোর টাইমস্ট্যাম্প জানার সুযোগ দেয় (উদাহরণস্বরূপ, কখন ফ্রেমটি ডিকিউ করা হয়েছিল, কখন প্রথম পিক্সেলটি ডিসপ্লেতে পাঠানো হয়েছিল, এবং কখন প্রথম পিক্সেলটি দৃশ্যমান হয়েছিল)।
  • টাইম ডোমেইন সাপোর্ট : এটি বিভিন্ন টাইম ডোমেইন (যেমন, সিস্টেম টাইম, জিপিইউ টাইম) সাপোর্ট করে এবং সেগুলোর মধ্যে ক্যালিব্রেট করার সুযোগ দেয়। একটি টাইম ডোমেইন হলো একটি নির্দিষ্ট ক্লক সোর্স বা টাইম বেস, যা টাইমস্ট্যাম্প পরিমাপ করতে ব্যবহৃত হয়। অ্যান্ড্রয়েডে, সমস্ত প্রাসঙ্গিক টাইমস্ট্যাম্প CLOCK_MONOTONIC ব্যবহার করে, তাই একাধিক টাইম ডোমেইন ব্যবহার করার প্রয়োজন হয় না।
  • VK_KHR_present_id2 সাথে একীকরণ : উপস্থাপনার অনুরোধ শনাক্ত করতে প্রমিত VkPresentId2KHR ব্যবহার করে।

এক্সটেনশনটি সক্রিয় করুন

VK_EXT_present_timing ব্যবহার করতে হলে, ডিভাইস তৈরির সময় আপনাকে অবশ্যই এটি এবং এর পূর্বশর্ত, VK_KHR_present_id2 , সক্রিয় করতে হবে। এছাড়াও, ফিজিক্যাল ডিভাইসের ফিচারগুলোর সাপোর্ট আছে কিনা তাও আপনার যাচাই করে নেওয়া উচিত:

VkPhysicalDevicePresentId2FeaturesKHR presentId2Features = {};
presentId2Features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_2_FEATURES_KHR;

VkPhysicalDevicePresentTimingFeaturesEXT presentTimingFeatures = {};
presentTimingFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_TIMING_FEATURES_EXT;
presentTimingFeatures.pNext = &presentId2Features;

VkPhysicalDeviceFeatures2 features2 = {};
features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
features2.pNext = &presentTimingFeatures;

vkGetPhysicalDeviceFeatures2(physicalDevice, &features2);

if (presentTimingFeatures.presentTiming && presentId2Features.presentId2) {
    // Enable VK_EXT_present_timing and VK_KHR_present_id2
    enabledDeviceExtensions.push_back(VK_EXT_PRESENT_TIMING_EXTENSION_NAME);
    enabledDeviceExtensions.push_back(VK_KHR_PRESENT_ID_2_EXTENSION_NAME);
}

যদি এই ফিচার চেকগুলির কোনো একটি ব্যর্থ হয় ( presentTiming বা presentId2 মিথ্যা হলে), তাহলে ডিভাইস বা ড্রাইভারটি VK_EXT_present_timing বা এর পূর্বশর্ত সমর্থন করে না। এই ক্ষেত্রে, আপনার অ্যাপ্লিকেশনটি VK_EXT_present_timing ব্যবহার করতে পারবে না এবং এর পরিবর্তে VK_GOOGLE_display_timing (যদি সমর্থিত হয়) ব্যবহার করা উচিত অথবা Swappy-এর মতো ডিফল্ট ফ্রেম পেসিং পদ্ধতির উপর নির্ভর করা উচিত।

সোয়াপচেইনে বর্তমান টাইমিং সক্রিয় করুন

সোয়াপচেইন তৈরি করার সময়, VK_SWAPCHAIN_CREATE_PRESENT_TIMING_BIT_EXT ফ্ল্যাগটি সেট করে প্রেজেন্ট টাইমিং স্পষ্টভাবে সক্রিয় করুন:

VkSwapchainCreateInfoKHR swapchainCreateInfo = {};
swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
swapchainCreateInfo.flags = VK_SWAPCHAIN_CREATE_PRESENT_TIMING_BIT_EXT;
// ... populate other fields ...

vkCreateSwapchainKHR(device, &swapchainCreateInfo, nullptr, &swapchain);

সহযোগী বর্তমান আইডি

প্রেজেন্ট করার সময়, VkPresentId2KHR (যা VK_KHR_present_id2 এক্সটেনশনের একটি অংশ) ব্যবহার করে প্রতিটি ফ্রেমের সাথে একটি অনন্য আইডি যুক্ত করুন:

uint64_t presentId = frameIndex; // Unique, monotonically increasing ID

VkPresentId2KHR presentIdInfo = {};
presentIdInfo.sType = VK_STRUCTURE_TYPE_PRESENT_ID_2_KHR;
presentIdInfo.swapchainCount = 1;
presentIdInfo.pPresentIds = &presentId;

VkPresentInfoKHR presentInfo = {};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.pNext = &presentIdInfo;
// ... populate other presentInfo fields ...

vkQueuePresentKHR(queue, &presentInfo);

সোয়াপচেইন টাইমিং প্রোপার্টিজ কোয়েরি করুন

vkGetSwapchainTimingPropertiesEXT ব্যবহার করে সোয়াপচেইন টাইমিং প্রোপার্টি, যেমন রিফ্রেশ ডিউরেশন, কোয়েরি করুন:

VkSwapchainTimingPropertiesEXT timingProperties = {};
timingProperties.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_TIMING_PROPERTIES_EXT;

uint64_t propertiesCounter = 0;
vkGetSwapchainTimingPropertiesEXT(device, swapchain, &timingProperties, &propertiesCounter);
// timingProperties.refreshDuration is the duration in nanoseconds

কোয়েরি সমর্থিত সময় ডোমেন

যেমনটি আগে উল্লেখ করা হয়েছে, একটি টাইম ডোমেইন একটি নির্দিষ্ট ক্লক সোর্স বা টাইম বেসকে প্রতিনিধিত্ব করে। যদিও VK_EXT_present_timing একাধিক টাইম ডোমেইন সমর্থন করে এবং সেগুলোর মধ্যে ক্যালিব্রেট করার সুযোগ দেয়, অ্যান্ড্রয়েডে ভিন্ন ভিন্ন ক্লক ব্যবহার করার প্রয়োজন নেই, কারণ সমস্ত প্রাসঙ্গিক টাইমস্ট্যাম্প CLOCK_MONOTONIC ব্যবহার করে। আপনার সোয়াপচেইনের জন্য সমর্থিত টাইম ডোমেইনগুলো কোয়েরি করুন:

VkSwapchainTimeDomainPropertiesEXT timeDomainProps = {};
timeDomainProps.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_TIME_DOMAIN_PROPERTIES_EXT;

// Query the count first
vkGetSwapchainTimeDomainPropertiesEXT(device, swapchain, &timeDomainProps, nullptr);

std::vector<VkTimeDomainKHR> timeDomains(timeDomainProps.timeDomainCount);
std::vector<uint64_t> timeDomainIds(timeDomainProps.timeDomainCount);
timeDomainProps.pTimeDomains = timeDomains.data();
timeDomainProps.pTimeDomainIds = timeDomainIds.data();

// Populate the data
vkGetSwapchainTimeDomainPropertiesEXT(device, swapchain, &timeDomainProps, nullptr);

লক্ষ্য উপস্থাপনার সময় অনুরোধ করুন

একটি ফ্রেমকে নির্দিষ্ট সময়ে উপস্থাপন করার অনুরোধ জানাতে, আপনার VkPresentInfoKHR এর সাথে একটি VkPresentTimingInfoEXT স্ট্রাকচার চেইন করুন।

VkPresentTimingInfoEXT timingInfo = {};
timingInfo.sType = VK_STRUCTURE_TYPE_PRESENT_TIMING_INFO_EXT;
timingInfo.flags = VK_PRESENT_TIMING_INFO_PRESENT_AT_RELATIVE_TIME_BIT_EXT; // Or absolute if supported
timingInfo.targetTime = targetTime; // Time value
timingInfo.timeDomainId = timeDomainIds[0]; // Use a supported time domain ID
timingInfo.presentStageQueries = VK_PRESENT_STAGE_IMAGE_FIRST_PIXEL_VISIBLE_BIT_EXT;

VkPresentTimingsInfoEXT presentTimingsInfo = {};
presentTimingsInfo.sType = VK_STRUCTURE_TYPE_PRESENT_TIMINGS_INFO_EXT;
presentTimingsInfo.swapchainCount = 1;
presentTimingsInfo.pTimingInfos = &timingInfo;

// Chain to VkPresentId2KHR
presentIdInfo.pNext = &presentTimingsInfo;

পূর্বে যেমন উল্লেখ করা হয়েছে, অ্যান্ড্রয়েডে, targetTime এর মান ০ অথবা ১ সেকেন্ডের বেশি ভবিষ্যতের যেকোনো টার্গেট টাইমস্ট্যাম্প উপেক্ষা করা হয়।

পূর্ববর্তী উপস্থাপনার সময়সূচী সম্পর্কে জিজ্ঞাসা করুন

পূর্ববর্তী উপস্থাপনাগুলির বিস্তারিত টাইমিং তথ্য জানতে, প্রথমে vkSetSwapchainPresentTimingQueueSizeEXT ব্যবহার করে টাইমিং কিউ-এর আকার কনফিগার করুন, এবং তারপরে vkGetPastPresentationTimingEXT ব্যবহার করে টাইমিংগুলি পুনরুদ্ধার করুন:

// Set the size of the timing queue (do this during initialization)
vkSetSwapchainPresentTimingQueueSizeEXT(device, swapchain, 10); // Keep last 10 frames

// ... later in your frame loop ...

VkPastPresentationTimingInfoEXT pastTimingInfo = {};
pastTimingInfo.sType = VK_STRUCTURE_TYPE_PAST_PRESENTATION_TIMING_INFO_EXT;
pastTimingInfo.swapchain = swapchain;

VkPastPresentationTimingPropertiesEXT pastProperties = {};
pastProperties.sType = VK_STRUCTURE_TYPE_PAST_PRESENTATION_TIMING_PROPERTIES_EXT;

// First query to get the count of available timings
vkGetPastPresentationTimingEXT(device, &pastTimingInfo, &pastProperties);

if (pastProperties.presentationTimingCount > 0) {
    std::vector<VkPastPresentationTimingEXT> timings(pastProperties.presentationTimingCount);
    pastProperties.pPresentationTimings = timings.data();

    // Populate the timings
    vkGetPastPresentationTimingEXT(device, &pastTimingInfo, &pastProperties);

    for (const auto& timing : timings) {
        // timing.presentId identifies the frame (matches the presentId you set)
        // timing.targetTime is the requested target time
        // If you requested stage queries, you can inspect timing.pPresentStages
    }
}

কোডের উদাহরণ

VK_EXT_present_timing এর একটি সম্পূর্ণ ইন্টিগ্রেশন উদাহরণ এবং কনফরমেন্স টেস্টের জন্য, Vulkan CTS (Compatibility Test Suite) রিপোজিটরিতে থাকা deqp (Draw Elements Quality Program) টেস্টগুলো দেখুন:

ভুলকান সিটিএস বর্তমান সময় পরীক্ষা

এই পরীক্ষাগুলো দেখায় কীভাবে বর্তমান টাইমিংয়ের জন্য সোয়াপচেইন কনফিগার করতে হয়, টার্গেট টাইম সেট করতে হয় এবং বিভিন্ন টাইম ডোমেন জুড়ে রিপোর্ট করা প্রেজেন্টেশন টাইমস্ট্যাম্পের নির্ভুলতা যাচাই করতে হয়।