Фантомные изображения в SurfaceView, если приложение снова переходит на передний план до завершения onPause - PullRequest
1 голос
/ 23 марта 2019

Я пишу игру для Android с использованием Java для пользовательского интерфейса и C ++ с NDK для потока рендеринга.Он использует Vulkan, если он доступен, и в противном случае использует OpenGL ES.Эта ошибка возникает в OpenGL ES и Vulkan.

Если я приостанавливаю приложение и быстро возобновляю его до завершения onPause, onStop или SurfaceHolder.Callback.surfaceDestroyed, то будет иметь место остаточное изображение того, что было показано наповерхность непосредственно перед тем, как нить рендера останавливается.Остаточное изображение ведет себя как фон для любой новой вещи, которую я рисую.При выполнении прохода рендеринга я установил чистый цвет: черный с 0,0 для альфы.Я делаю это так, чтобы фон приложения был фоном для игры.Если я установлю альфа-значение на 1,0, то симптомы этой ошибки не будут проявляться.

Эта проблема может рассматриваться как обобщение проблемы, описанной в: фантомных изображениях после перехода с разделенного экранана весь экран Но решение этой проблемы (то есть избежание закрытия приложения) не будет работать для этого более общего случая.

Я остановил поток рендеринга во время onPause или SurfaceHolder.Callback.surfaceDestroyed,который когда-либо случается первым.Эти функции не будут возвращаться до тех пор, пока не будет присоединен поток рендеринга (если он есть).

Если я очищу экран с помощью пустого прохода рендеринга с чистым цветом, являющимся черным, и 0,0 для альфы (в Vulkan) иливызовом glClear (в OpenGL ES) перед тем, как завершится поток рендеринга, ошибка не проявляется.Но это воняет от того, что что-то делается не так с поверхностью (или, может быть, с какой-то расой).Я обеспокоен тем, что это решение просто скрывает ошибку, а не исправляет ее, освобождая ресурсы, связанные с остаточным изображением.Есть ли что-то еще, что мне нужно сделать, чтобы обеспечить правильную перезагрузку поверхности?

Начало прохода рендеринга (vulkan):

std::array<VkClearValue, 2> clearValues = {};
clearValues[0].color = {0.0f, 0.0f, 0.0f, 0.0f};
clearValues[1].depthStencil = {1.0, 0};
renderPassInfo.clearValueCount = static_cast<uint32_t>(clearValues.size());
renderPassInfo.pClearValues = clearValues.data();
vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);

Во время инициализации в OpenGL ES:

glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

очистка экрана в цикле рисования (OpenGL):

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

В потоке рендеринга это установка и разрушение ANativeWindow.Я проверил в отладчике, что средство удаления общего указателя фактически вызывалось:

ANativeWindow *window = ANativeWindow_fromSurface(env, jsurface);
if (window == nullptr) {
    notify->sendError("Unable to acquire window from surface.");
    return;
}

auto deleter = [](WindowType *windowRaw) {
    /* release the java window object */
    if (windowRaw != nullptr) {
        ANativeWindow_release(windowRaw);
    }
};
std::shared_ptr<WindowType> surface(window, deleter);

в onPause и SurfaceHolder.Callback.surfaceDestroyed:

if (drawer == null) {
    return;
}

try {
    Draw.tellDrawerStop();
    drawer.join();
} catch (InterruptedException e) {
}
drawer = null;

В onCreate делается следующее, чтобы сделатьSurfaceView видеть сквозь:

SurfaceView drawSurfaceView = findViewById(R.id.drawingSurface);
drawSurfaceView.setZOrderOnTop(true);
SurfaceHolder drawSurfaceHolder = drawSurfaceView.getHolder();
drawSurfaceHolder.setFormat(PixelFormat.TRANSPARENT);
drawSurfaceHolder.addCallback(new MySurfaceCallback(this));
...