จัดการการวางแนวอุปกรณ์ด้วยการหมุนล่วงหน้าของ Vulkan

บทความนี้อธิบายวิธีจัดการการหมุนอุปกรณ์อย่างมีประสิทธิภาพ ในแอปพลิเคชัน Vulkan โดยใช้การหมุนเวียนล่วงหน้า

ด้วย Vulkan คุณจะทำสิ่งต่อไปนี้ได้ ระบุข้อมูลเกี่ยวกับสถานะการแสดงผลได้มากกว่าที่คุณสามารถทำได้ด้วย OpenGL เมื่อใช้ Vulkan คุณต้องใช้สิ่งต่างๆ ที่จัดการโดยผู้ขับขี่อย่างชัดเจน OpenGL เช่น การวางแนวของอุปกรณ์ และความสัมพันธ์กับ การวางแนวพื้นผิวของการแสดงผล คุณทำสิ่งต่อไปนี้ได้ 3 วิธีที่ Android แฮนเดิลการปรับยอดพื้นผิวการแสดงผลของอุปกรณ์ตามการวางแนวของอุปกรณ์

  1. ระบบปฏิบัติการ Android สามารถใช้หน่วยประมวลผลการแสดงผล (DPU) ของอุปกรณ์ ซึ่งสามารถจัดการการหมุนพื้นผิวในฮาร์ดแวร์ได้อย่างมีประสิทธิภาพ มีให้บริการใน อุปกรณ์ที่รองรับเท่านั้น
  2. ระบบปฏิบัติการ Android สามารถจัดการการหมุนพื้นผิวได้ด้วยการเพิ่มการส่งผ่านคอมโพสิเตอร์ ช่วงเวลานี้ จะมีต้นทุนในการปฏิบัติงาน ซึ่งขึ้นอยู่กับว่าเครื่องมือจัดเตรียมจะ การหมุนภาพผลลัพธ์
  3. ตัวแอปพลิเคชันเองก็สามารถจัดการกับการหมุนพื้นผิวโดยการแสดงผล หมุนภาพไปยังพื้นผิวการแสดงผลที่ตรงกับการวางแนวปัจจุบันของ จอแสดงผล

คุณควรใช้วิธีการใดต่อไปนี้

ปัจจุบันยังไม่มีวิธีที่แอปพลิเคชันจะทราบว่าการหมุนพื้นผิวหรือไม่ ดำเนินการภายนอกแอปพลิเคชันจะไม่เสียค่าใช้จ่าย แม้จะมี DPU ที่ใช้ ดูแลคุณ แต่ก็ยังมีบทลงโทษด้านประสิทธิภาพที่วัดผลได้ มากขึ้น หากแอปพลิเคชันของคุณเป็นแบบ CPU ปัญหานี้จะกลายเป็นปัญหาเกี่ยวกับพลังงานเนื่องจาก การใช้งาน GPU ที่เพิ่มขึ้นโดย Android Compositor ซึ่งปกติแล้วจะทำงานที่ คือความถี่เพิ่มขึ้น หากแอปพลิเคชันของคุณเชื่อมโยงกับ GPU แล้ว Android Compositor อาจขัดจังหวะงาน GPU ของแอปพลิเคชันของคุณ ซึ่งทำให้ประสิทธิภาพเพิ่มขึ้น การสูญเสีย

จากการเปิดตัวชื่อการจัดส่งใน Pixel 4XL เราได้เห็น SurfaceFlinger (งานที่มีลําดับความสําคัญสูงกว่าซึ่งขับเคลื่อนให้ Android องค์ประกอบ):

  • ขัดจังหวะการทำงานของแอปพลิเคชันเป็นประจำ ซึ่งทำให้เกิด 1-3 มิลลิวินาที ครั้งไปจนถึงช่วงเวลาเฟรม และ

  • ทำให้ GPU ได้รับแรงกดเพิ่มขึ้น เวอร์เท็กซ์/พื้นผิว หน่วยความจำ เนื่องจาก Compositor ต้องอ่านค่าทั้งหมด เฟรมบัฟเฟอร์มาใช้งานเพื่อองค์ประกอบ

การจัดการการวางแนวจะทำให้ SurfaceFlinger ยุติการขัดจังหวะชั่วคราวอย่างเหมาะสม ขณะที่ความถี่ของ GPU ลดลง 40% เนื่องจากความถี่ที่เพิ่มขึ้นเรื่อยๆ ไม่จำเป็นต้องใช้ Android Compositor อีกต่อไป

เพื่อให้แน่ใจว่ามีการจัดการการหมุนพื้นผิวอย่างเหมาะสมโดยมีค่าใช้จ่ายน้อยที่สุด คุณควรใช้วิธีที่ 3 ตามที่เห็นในกรณีก่อนหน้านี้ ซึ่งเรียกว่าการหมุนเวียนล่วงหน้า ข้อมูลนี้จะบอกระบบปฏิบัติการ Android ว่าแอปของคุณ จะจัดการการหมุนพื้นผิว คุณสามารถทำได้โดยการส่งแฟล็กการเปลี่ยนแปลงพื้นผิว ที่ระบุการวางแนวระหว่างการสร้างการสลับเชน ซึ่งจะหยุด Android Compositor ไม่หมุนตามตัวเอง

การรู้วิธีตั้งค่า Flag การเปลี่ยนรูปแบบพื้นผิวเป็นสิ่งสำคัญสำหรับ Vulkan ทั้งหมด แอปพลิเคชัน แอปพลิเคชันมีแนวโน้มที่จะรองรับการวางแนวหลายแนว หรือรองรับการวางแนวเดียวโดยมีพื้นผิวในการแสดงผล การวางแนวตามการวางแนวของอุปกรณ์ที่อุปกรณ์พิจารณาการวางแนวของพวกเขา ตัวอย่างเช่น แอปพลิเคชันแนวนอนเท่านั้นในโทรศัพท์แนวตั้งที่ไม่ซ้ำกันหรือแนวตั้ง บนแท็บเล็ตแนวนอน

แก้ไข AndroidManifest.xml

ในการจัดการกับการหมุนอุปกรณ์ในแอป ให้เริ่มด้วยการเปลี่ยนแอปพลิเคชัน AndroidManifest.xml เพื่อบอก Android ว่าแอปของคุณจะจัดการการวางแนว และขนาดหน้าจอ การดำเนินการนี้จะป้องกันไม่ให้ Android ทำลายและสร้างใหม่ Android Activity และการเรียกใช้ onDestroy() ในฟังก์ชัน พื้นผิวหน้าต่างที่มีอยู่เมื่อเกิดการเปลี่ยนการวางแนว ซึ่งทำโดย เพิ่มแอตทริบิวต์ orientation (เพื่อรองรับระดับ API <13) และ screenSize กับกิจกรรม ส่วน configChanges:

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

หากแอปพลิเคชันปรับการวางแนวหน้าจอโดยใช้screenOrientation คุณไม่จำเป็นต้องทำเช่นนั้น นอกจากนี้ หากแอปพลิเคชันของคุณใช้ การวางแนวก็เพียงแค่ตั้งค่า Swapเชน เพียงครั้งเดียวเท่านั้น การเริ่มต้นแอปพลิเคชัน/ทำงานต่อ

รับความละเอียดหน้าจอระบุตัวตนและพารามิเตอร์กล้อง

ถัดไป ให้ตรวจหาความละเอียดหน้าจอของอุปกรณ์ ที่เชื่อมโยงกับค่า 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 10 ขึ้นไป)

วิธีที่เชื่อถือได้มากที่สุดในการตรวจหาการเปลี่ยนการวางแนวในแอปพลิเคชันของคุณคือ เพื่อยืนยันว่าฟังก์ชัน vkQueuePresentKHR() แสดงผลหรือไม่ VK_SUBOPTIMAL_KHR เช่น

auto res = vkQueuePresentKHR(queue_, &present_info);
if (res == VK_SUBOPTIMAL_KHR){
  orientationChanged = true;
}

หมายเหตุ: โซลูชันนี้ใช้งานได้กับอุปกรณ์ที่ใช้เท่านั้น Android 10 ขึ้นไป Android เวอร์ชันเหล่านี้กลับมา VK_SUBOPTIMAL_KHR จาก vkQueuePresentKHR() เราจะจัดเก็บผลลัพธ์ของ เช็คอิน orientationChanged booleanที่เข้าถึงได้จาก แอปพลิเคชัน ลูปการแสดงผลหลัก

ตรวจหาการเปลี่ยนแปลงการวางแนวของอุปกรณ์ (ก่อน Android 10)

สำหรับอุปกรณ์ที่ใช้ Android 10 หรือเก่ากว่า จำเป็นต้องติดตั้งใช้งาน เนื่องจากไม่รองรับ VK_SUBOPTIMAL_KHR

การใช้แบบสำรวจ

ในอุปกรณ์ Android 10 ก่อน 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;
}

แบบสำรวจใน Pixel 4 ที่ใช้ Android 10 vkGetPhysicalDeviceSurfaceCapabilitiesKHR() ใช้เวลาระหว่าง 0 .120-.250 มิลลิวินาที และ Pixel 1XL ที่ใช้ Android 8 แบบสำรวจใช้เวลา 0.110-.350 มิลลิวินาที

การใช้ Callback

ตัวเลือกที่ 2 สำหรับอุปกรณ์ที่ใช้ Android 10 ต่ำกว่าคือการลงทะเบียน onNativeWindowResized() Callback เพื่อเรียกฟังก์ชันที่ตั้งค่าฟังก์ชัน Flag orientationChanged, ส่งสัญญาณให้แอปพลิเคชันเปลี่ยนการวางแนว เกิดขึ้น:

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

โดยวิธีปรับขนาด Callback คือ

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

ปัญหาของโซลูชันนี้คือ onNativeWindowResized() ได้รับ ต้องเปลี่ยนการวางแนว 90 องศา เช่น เปลี่ยนจากแนวนอนเป็นแนวตั้ง หรือ ในทางกลับกัน การเปลี่ยนการวางแนวอื่นๆ จะไม่ทริกเกอร์การสร้างการสลับเชนขึ้นมาใหม่ ตัวอย่างเช่น การเปลี่ยนจากแนวนอนเป็นกลับด้านแนวนอนจะ ทำให้คอมโพเนนต์ของ Android ต้องพลิกกลับด้าน แอปพลิเคชัน

การรับมือกับการเปลี่ยนแปลงการวางแนว

ในการจัดการกับการเปลี่ยนการวางแนว ให้เรียกกิจวัตรการเปลี่ยนการวางแนวที่ ด้านบนของลูปการแสดงผลหลักเมื่อ orientationChanged ตั้งค่าเป็น "จริง" เช่น

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

คุณทำทั้งหมดที่จำเป็นเพื่อสร้าง Swapchain ใหม่ภายใน ฟังก์ชัน OnOrientationChange() ซึ่งหมายความว่าคุณทำสิ่งต่อไปนี้ได้

  1. ทำลายอินสแตนซ์ที่มีอยู่ของ Framebuffer และ ImageView

  2. สร้าง Swapchain อีกครั้งขณะทำลาย Swapchain เก่า (ซึ่งเราจะอภิปรายต่อไป) และ

  3. สร้าง Framebuffers ใหม่ด้วย DisplayImages ของ Swapchain ใหม่ หมายเหตุ: รูปภาพไฟล์แนบ (เช่น รูปภาพความลึก/ลายฉลุ) มักจะไม่มี สร้างขึ้นใหม่ ขึ้นอยู่กับความละเอียดเฉพาะตัวของรูปภาพ Swapchain ที่หมุนไว้ล่วงหน้า

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 เป็น "เท็จ" เพื่อแสดงว่าคุณจัดการการเปลี่ยนการวางแนวแล้ว

สันทนาการ 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 (ซึ่งตั้งค่าเป็นฟิลด์ CurrentTransform ของsurfaceCapabilities) นอกจากนี้ คุณยังตั้งค่าช่อง oldSwapchain เป็น Swapchain ที่จะถูกทำลาย

การปรับเมทริกซ์ MVP

สิ่งสุดท้ายที่คุณต้องทำคือ การนำการเปลี่ยนแปลงก่อนการเปลี่ยนแปลงไปใช้ โดยการใช้เมทริกซ์การหมุนกับเมทริกซ์ MVP จุดประสงค์หลักก็คือ ใช้การหมุนในพื้นที่คลิปเพื่อให้ภาพที่ถูกหมุนไป การวางแนวของอุปกรณ์ในปัจจุบัน จากนั้น คุณสามารถส่งเมทริกซ์ MVP ที่อัปเดตแล้วนี้ ลงใน Vertex Shade ของคุณและใช้ตามปกติโดยไม่ต้องแก้ไข ตัวปรับแสงเงา

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;

การพิจารณา - วิวพอร์ตและกรรไกรแบบไม่เต็มหน้าจอ

หากแอปพลิเคชันของคุณใช้วิวพอร์ต/พื้นที่ที่ไม่ใช่โหมดเต็มหน้าจอ จะได้รับการอัปเดตตามการวางแนวของอุปกรณ์ ช่วงเวลานี้ คุณต้องเปิดใช้ตัวเลือก Viewport และ Scissor แบบไดนามิกระหว่าง การสร้างไปป์ไลน์:

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);

การพิจารณา - อนุพันธ์ของ Fragment Shader

หากแอปพลิเคชันของคุณใช้การคำนวณหาอนุพันธ์ เช่น dFdx และ dFdy อาจต้องมีการเปลี่ยนรูปแบบเพิ่มเติมเพื่อรองรับพิกัดที่หมุน เมื่อการคำนวณเหล่านี้ทำงานในพื้นที่พิกเซล การดำเนินการนี้ต้องใช้แอป เพื่อส่งผ่านตัวบ่งชี้บางอย่างเกี่ยวกับ preTransform ไปยังตัวใส่เฉดสีแฟรกเมนต์ (เช่น จำนวนเต็มที่แทนการวางแนวอุปกรณ์ในปัจจุบัน) และใช้ข้อมูลนี้เพื่อจับคู่ การคำนวณอนุพันธ์อย่างเหมาะสม:

  • สำหรับเฟรมที่หมุนล่วงหน้า 90 องศา
    • ต้องแมป dFdx กับ dFdy
    • ต้องแมป dFdy กับ -dFdx
  • สำหรับเฟรมที่หมุนล่วงหน้า 270 องศา
    • ต้องแมป dFdx กับ -dFdy
    • ต้องแมป dFdy กับ dFdx
  • สำหรับเฟรมที่หมุนล่วงหน้า 180 องศา
    • ต้องแมป dFdx กับ -dFdx
    • ต้องแมป dFdy กับ -dFdy

บทสรุป

เพื่อให้แอปพลิเคชันของคุณได้รับประโยชน์สูงสุดจาก Vulkan บน Android การหมุนเวียนใช้งานล่วงหน้าถือเป็นสิ่งจำเป็น สรุปประเด็นสำคัญที่สุดจาก ได้แก่

  • ตรวจสอบว่าในระหว่างการสร้าง Swapchain หรือสร้าง Flag ก่อนการเปลี่ยนรูปแบบ ตั้งค่าให้ตรงกับธงที่แสดงผลโดยระบบปฏิบัติการ Android วิธีนี้จะช่วยหลีกเลี่ยง โอเวอร์เฮดของคอมโพสิต
  • รักษาขนาดการสลับเชนให้คงที่ตามความละเอียดของหน้าต่างแอป ตามการวางแนวธรรมชาติของจอแสดงผล
  • หมุนเมทริกซ์ MVP ในพื้นที่คลิปเพื่อพิจารณาการวางแนวอุปกรณ์ เนื่องจากความละเอียด/ส่วนขยายการสลับเชนไม่อัปเดตตามการวางแนวอีกต่อไป ของจอแสดงผล
  • อัปเดตวิวพอร์ตและรูปสี่เหลี่ยมกรรไกรตามที่แอปพลิเคชันของคุณต้องการ

แอปตัวอย่าง: การหมุนหน้าจอ Android ล่วงหน้าขั้นต่ำ