บทความนี้อธิบายวิธีจัดการการหมุนอุปกรณ์อย่างมีประสิทธิภาพในแอปพลิเคชัน Vulkan โดยใช้การหมุนก่อน
Vulkan ช่วยให้คุณระบุข้อมูลเพิ่มเติมเกี่ยวกับสถานะการแสดงผลได้มากกว่าใน OpenGL เมื่อใช้ Vulkan คุณต้องติดตั้งใช้งานสิ่งที่ไดรเวอร์จัดการใน OpenGL อย่างชัดแจ้ง เช่น การวางแนวอุปกรณ์และความสัมพันธ์กับการวางแนวพื้นผิวการแสดงผล Android มี 3 วิธีในการปรับพื้นผิวการแสดงผลของอุปกรณ์ให้สอดคล้องกับการวางแนวของอุปกรณ์
- ระบบปฏิบัติการ Android สามารถใช้หน่วยประมวลผลการแสดงผล (DPU) ของอุปกรณ์ ซึ่งจัดการการหมุนพื้นผิวในฮาร์ดแวร์ได้อย่างมีประสิทธิภาพ ใช้ได้กับอุปกรณ์ที่รองรับเท่านั้น
- ระบบปฏิบัติการ Android สามารถจัดการการหมุนพื้นผิวได้ด้วยการเพิ่มบัตรผ่าน compositor การทำเช่นนี้จะมีต้นทุนด้านประสิทธิภาพ ซึ่งขึ้นอยู่กับว่าเครื่องมือทำองค์ประกอบต้องจัดการกับการหมุนภาพที่แสดงผลอย่างไร
- แอปพลิเคชันจะจัดการการหมุนพื้นผิวได้โดยการแสดงผลรูปภาพที่หมุนแล้วบนพื้นผิวการแสดงผลที่ตรงกับการวางแนวปัจจุบันของจอแสดงผล
คุณควรใช้วิธีการใดต่อไปนี้
ปัจจุบันแอปพลิเคชันจะไม่ทราบว่าการหมุนพื้นผิวที่จัดการนอกแอปพลิเคชันนั้นจะเป็นแบบไม่มีค่าใช้จ่ายหรือไม่ แม้ว่าจะมี DPU คอยจัดการเรื่องนี้ให้คุณ แต่คุณก็อาจยังต้องจ่ายค่าปรับประสิทธิภาพที่วัดผลได้ หากแอปพลิเคชันใช้ CPU มากเกินไป ปัญหานี้ก็จะกลายเป็นปัญหาด้านพลังงานเนื่องจาก Android Compositor ใช้ GPU มากขึ้น ซึ่งมักจะทำงานด้วยความถี่ที่เพิ่มขึ้น หากแอปพลิเคชันของคุณใช้ GPU อยู่ คอมโพสิตอร์ของ Android จะแย่งงานของ GPU ในแอปพลิเคชันของคุณได้ ซึ่งจะทำให้ประสิทธิภาพลดลงอีก
เมื่อเรียกใช้เนื้อหาที่พร้อมให้บริการใน Pixel 4XL เราพบว่า SurfaceFlinger (งานที่มีความสำคัญสูงกว่าซึ่งขับเคลื่อน Android Compositor)
ขัดจังหวะการทำงานของแอปพลิเคชันเป็นประจำ ซึ่งทำให้เฟรมเวลาเพิ่มขึ้น 1-3 มิลลิวินาที และ
เพิ่มแรงกดที่หน่วยความจำเวอร์เท็กซ์/พื้นผิวของ GPU เนื่องจาก Compositor ต้องอ่านเฟรมบัฟเฟอร์ทั้งหมดเพื่อทำงานองค์ประกอบ
การจัดการการวางแนวอย่างเหมาะสมจะหยุดการแย่งชิง GPU โดย SurfaceFlinger เกือบทั้งหมด ขณะที่ความถี่ของ GPU จะลดลง 40% เนื่องจากไม่จำเป็นต้องใช้ความถี่ที่เพิ่มซึ่ง Android Compositor ใช้
คุณควรใช้วิธีที่ 3 เพื่อให้แน่ใจว่าการหมุนพื้นผิวได้รับการจัดการอย่างเหมาะสมโดยมีค่าใช้จ่ายน้อยที่สุดเท่าที่จะเป็นไปได้ ดังที่เห็นจากกรณีก่อนหน้านี้ ซึ่งเรียกว่าการหมุนเวียนก่อนแสดง ข้อมูลนี้จะบอกระบบปฏิบัติการ Android ว่าแอปของคุณต้องจัดการกับการหมุนพื้นผิว ซึ่งทำได้โดยการส่งแฟล็กการเปลี่ยนรูปแบบพื้นผิวที่ระบุการวางแนวระหว่างการสร้างการสลับเชน ซึ่งจะหยุดไม่ให้โปรแกรมคอมโพสิตของ Android ทำการหมุนเอง
การทราบวิธีตั้งค่า Flag การเปลี่ยนรูปแบบพื้นผิวเป็นสิ่งสําคัญสําหรับแอปพลิเคชัน Vulkan ทั้งหมด แอปพลิเคชันมักจะรองรับการวางแนวหลายแบบหรือรองรับการวางแนวเดียวที่พื้นผิวการแสดงผลอยู่ในการวางแนวที่แตกต่างจากสิ่งที่อุปกรณ์พิจารณาว่าเป็นการวางแนวของข้อมูลประจำตัว ตัวอย่างเช่น แอปพลิเคชันแนวนอนเท่านั้นในโทรศัพท์แนวตั้งที่ใช้ข้อมูลประจำตัว หรือแอปพลิเคชันแนวตั้งเท่านั้นในแท็บเล็ตบัตรประจำตัวแนวนอน
แก้ไข AndroidManifest.xml
หากต้องการจัดการการหมุนอุปกรณ์ในแอป ให้เริ่มด้วยการเปลี่ยนไฟล์ AndroidManifest.xml
ของแอปพลิเคชันเพื่อบอก Android ว่าแอปจะจัดการกับการเปลี่ยนการวางแนวและขนาดหน้าจอ ซึ่งจะป้องกันไม่ให้ Android ทำลายและสร้าง Activity
ของ Android ขึ้นมาใหม่ และเรียกใช้ฟังก์ชัน 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 คุณสามารถโพลค่าการเปลี่ยนรูปแบบอุปกรณ์ปัจจุบันทุกๆ เฟรม 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-0.250 มิลลิวินาที และใน Pixel 1XL ที่ใช้ Android 8 การป้อนข้อมูลใช้เวลา 0.110-0.350 มิลลิวินาที
การใช้การเรียกกลับ
ตัวเลือกที่ 2 สำหรับอุปกรณ์ที่ใช้ Android ต่ำกว่า 10 คือลงทะเบียนการเรียกกลับ onNativeWindowResized()
เพื่อเรียกใช้ฟังก์ชันที่ตั้งค่า 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 องศา เช่น จากแนวนอนเป็นแนวตั้งหรือในทางกลับกัน การเปลี่ยนแปลงการวางแนวอื่นๆ จะไม่ทริกเกอร์การสร้าง Swapchain อีกครั้ง
ตัวอย่างเช่น การเปลี่ยนจากแนวนอนเป็นแนวนอนกลับด้านจะไม่ทริกเกอร์รูปภาพ ซึ่งต้องใช้ตัวประกอบ Android ทำการพลิกสำหรับแอปพลิเคชันของคุณ
การรับมือกับการเปลี่ยนแปลงการวางแนว
ในการจัดการกับการเปลี่ยนการวางแนว ให้เรียกใช้กิจวัตรการเปลี่ยนการวางแนวที่ด้านบนของลูปการแสดงผลหลักเมื่อตั้งค่าตัวแปร orientationChanged
เป็น "จริง" เช่น
bool VulkanDrawFrame() {
if (orientationChanged) {
OnOrientationChange();
}
คุณทำงานทั้งหมดที่จำเป็นเพื่อสร้าง Swapเชนใหม่ภายในฟังก์ชัน OnOrientationChange()
ได้ ซึ่งหมายความว่าคุณจะทำสิ่งต่อไปนี้ได้
ทำลายอินสแตนซ์ที่มีอยู่ของ
Framebuffer
และImageView
สร้างสวิตช์เชนใหม่ขณะทำลายสวิตช์เชนเก่า (ซึ่งจะกล่าวถึงในลำดับถัดไป) และ
สร้าง Framebuffer ใหม่ด้วย 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;
}
และในตอนท้ายของฟังก์ชัน คุณจะรีเซ็ต Flag 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
(ซึ่งตั้งค่าเป็นช่อง currentTransform ของ surfaceCapabilities
) นอกจากนี้ คุณยังตั้งค่าช่อง oldSwapchain
เป็น Swapchain ที่จะทำลายได้ด้วย
การปรับเมตริก MVP
ขั้นตอนสุดท้ายคือการใช้การเปลี่ยนรูปแบบก่อนการแปลงโดยการใช้เมทริกซ์การหมุนกับเมทริกซ์ MVP หลักๆ แล้วขั้นตอนนี้คือใช้การหมุนในพื้นที่คลิปเพื่อให้รูปภาพที่ได้หมุนตามการวางแนวอุปกรณ์ปัจจุบัน จากนั้น คุณสามารถส่งเมทริกซ์ MVP ที่อัปเดตแล้วนี้ไปยัง Vertex Shader และใช้งานได้ตามปกติโดยไม่ต้องปรับแต่งเฉดสี
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 แบบไดนามิกระหว่างการสร้างไปป์ไลน์ของ 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);
ข้อควรพิจารณา - อนุพันธ์ของ Shader ระดับเศษส่วน
หากแอปพลิเคชันของคุณใช้การคำนวณหาอนุพันธ์ เช่น dFdx
และ dFdy
อาจต้องมีการเปลี่ยนรูปแบบเพิ่มเติมเพื่อรองรับระบบพิกัดที่หมุนเวียน เนื่องจากการคำนวณเหล่านี้จะดำเนินการในพื้นที่พิกเซล ซึ่งแอปจะต้องส่งตัวบ่งชี้บางอย่างของ preTransform ไปยัง Shader ระดับเศษ (เช่น จำนวนเต็มที่ใช้แสดงการวางแนวอุปกรณ์ปัจจุบัน) และใช้ข้อมูลดังกล่าวเพื่อแมปการคำนวณอนุพันธ์อย่างถูกต้อง
- สำหรับเฟรมที่ผ่านการหมุนล่วงหน้า 90 องศา
- ต้องแมป dFdx กับ dFdy
- dFdy ต้องแมปกับ -dFdx
- สำหรับเฟรมที่ผ่านการหมุนล่วงหน้าเป็น270 องศา
- dFdx ต้องจับคู่กับ -dFdy
- ต้องแมป dFdy กับ dFdx
- สำหรับเฟรมที่ผ่านการหมุนล่วงหน้า 180 องศา ให้ทำดังนี้
- dFdx ต้องแมปกับ -dFdx
- ต้องแมป dFdy กับ -dFdy
บทสรุป
คุณต้องใช้การหมุนก่อนเพื่อให้แอปพลิเคชันได้รับประโยชน์สูงสุดจาก Vulkan ใน Android สรุปสำคัญที่สุดจากบทความนี้มีดังนี้
- ตรวจสอบว่าในระหว่างการสร้างหรือสร้าง Swapchain อีกครั้ง มีการตั้งค่า Flag การเปลี่ยนรูปแบบล่วงหน้าให้ตรงกับ Flag ที่ระบบปฏิบัติการ Android แสดง ซึ่งจะช่วยหลีกเลี่ยงไม่ให้ โอเวอร์เฮดของคอมโพสิต
- คงขนาดสวิตช์เชนไว้กับความละเอียดของข้อมูลประจำตัวของพื้นผิวหน้าต่างแอปในแนวการวางแนวตามปกติของจอแสดงผล
- หมุนเมทริกซ์ MVP ในคลิปสเปซเพื่อพิจารณาการวางแนวของอุปกรณ์ เนื่องจากความละเอียด/ขอบเขตของ Swapchain จะไม่อัปเดตตามการวางแนวของจอแสดงผลอีกต่อไป
- อัปเดตวิวพอร์ตและสี่เหลี่ยมผืนผ้ากรรไกรตามต้องการของแอปพลิเคชัน
ตัวอย่างแอป: การหมุนก่อนแสดงผลขั้นต่ำของ Android