phantom images after going from split screen to fu

2019-08-09 21:09发布

问题:

I am writing a game for Android using the NDK. My game uses vulkan if it is available and otherwise uses OpenGL.

I have a problem where if you put the game in split screen mode with the device held in portrait orientation, then resize the game to full screen mode, an after image of the game on the old view is still visible. Note: doing this with the game triggers SurfaceHolder.Callback.surfaceDestroyed (in Java) to be called, which in turn shuts down my render thread in C++. My callback for surfaceDestroyed tells the C++ render thread to stop, then joins it.

I can fix this in OpenGL by calling glClearColor with any color, then calling eglSwapBuffers right before the render thread shuts down.

Is this a valid fix for OpenGL? Is there something else I should be doing to clean out the old surface? I verified that ANativeWindow_release is called on the window I get from ANativeWindow_fromSurface before I exit the render thread.

Then I tried to do the same thing in vulkan and ran into problems again... I used vkCmdClearColorImage by do ing the following:

(1) vkQueueWaitIdle(presentQueue)

(2) vkAquireNextImageKHR

(3) initialize the corresponding command buffer with:

(3a) ImageMemoryBarrier VK_IMAGE_LAYOUT_UNDEFINED -> VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 0 -> VK_ACCESS_TRANSFER_WRITE_BIT

(3b) vkCmdClearColor VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL

(3c) ImageMemoryBarrier VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL -> VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_ACCESS_TRANSFER_WRITE_BIT -> VK_ACCESS_MEMORY_READ_BIT

(4) vkQueueSubmit(graphicsQueue...)

(5) vkQueuePresentKHR(presentQueue...)

(6) vkQueueWaitIdle(presentQueue)

I got to 3a, and then I got an error in the validation layer saying that the image was not created with VK_IMAGE_USAGE_TRANSFER_DST_BIT usage flag. How do I cause the swapchain images to be created with this usage bit?

Please let me know if additional information is needed. Thanks!

回答1:

The Vulkan part is pretty self explanatory; there's a imageUsage member. Let me just give you code:

VkSurfaceCapabilitiesKHR caps;
errco = vkGetPhysicalDeviceSurfaceCapabilitiesKHR( pdev, mySurface, &caps ); if(errco) panic();
if( !(caps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT) ) panic();

VkSwapchainCreateInfoKHR sci = {VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR};
sci.surface = mySurface;
sci.imageUsage = VK_IMAGE_USAGE_TRANSFER_DST_BIT; // !
// etc

VkSwapchainKHR mySwapchain;
errco = vkCreateSwapchainKHR( dev, &sci, nullptr, &mySwapchain ); if(errco) panic();

Though you should probably not be doing it anyway. There is no reason to do vkCmdClearColorImage. Use render pass to clear color images before you intend to write them (VkAttachmentDescription::loadOp). It is more efficient and as a bonus it counts as a render and does not need TRANSFER usage.

It seems windowBackgroundFallback is supposed to be the general solution to this assuming your app cannot provide a new image in time.

The best solution to get rid of the phantom image is to tell android not to shutdown the app if a screen resize occurs. This way, the screen resize can be handled by recreating the swapchain and redrawing the game. This article talks setting android:configChanges in the manifests file. The following setting stops android from shutting down the app when a screen resize from split screen to full screen occurs:

android:configChanges="screenSize|orientation|screenLayout"