Вулкану не удается очистить глубину

Я работаю с Vulkan последние пару недель, и у меня возникла проблема, которая произошла только на картах AMD. В частности, AMD 7970M. Я запускал свой проект на картах серии GTX 700 и 900 без проблем. Я даже бегал по ОС Linux (Steam OS) с картами Nvidia без заминки. Проблема возникает только на картах AMD и только с моим проектом; все образцы и проекты из Sascha Willems не создают проблем.

Сейчас я рисую текстурированную модель Raptor и вращаю ее. Я отрисовываю это до текстуры, а затем применяю эту текстуру к полноэкранному треугольнику; базовый экранный рендеринг. Однако на моем 7970M глубина кажется не совсем корректной. Вместо этого я получаю этот странный артефакт, так как глубина не очищается должным образом:

Bad Raptor

Конечно, я попытался вникать в это с помощью RenderDoc, и глубина совершенно неверна. И Raptor, и Fullscreen Triangle, нарисованные на нем, просто беспорядок:

Bad Depth

Bad Tri Depth

Я пробовал сравнить мой код с примером Offscreen от Sascha Willems, и я, кажется, делаю почти все одинаково. Я подумал, что может быть что-то не так с тем, как я создал свою глубину, но это кажется прекрасным по сравнению со всеми примерами, которые я видел.

Вот некоторые отладочные представления, где я создаю изображение глубины и представление:

image infoimage view info

Здесь весь метод:

        bool VKRenderTarget::setupFramebuffer(VKRenderer* renderer) 
            {
                VkDevice device = renderer->GetVKDevice();
                VkCommandBuffer setupCommand;

                m_colorFormat = renderer->GetPreferredImageFormat();
                m_depthFormat = renderer->GetPreferredDepthFormat();

                renderer->CreateSetupCommandBuffer();

                setupCommand = renderer->GetSetupCommandBuffer();

                VkResult err;

                //Color attachment
                VkImageCreateInfo imageInfo = {};
                imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
                imageInfo.pNext = nullptr;
                imageInfo.format = m_colorFormat;
                imageInfo.imageType = VK_IMAGE_TYPE_2D;
                imageInfo.extent.width = m_width;
                imageInfo.extent.height = m_height;
                imageInfo.mipLevels = 1;
                imageInfo.arrayLayers = 1;
                imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
                imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
                imageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
                imageInfo.flags = 0;

                VkMemoryAllocateInfo memAllocInfo = {};
                memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;

                VkMemoryRequirements memReqs;

                err = vkCreateImage(device, &imageInfo, nullptr, &m_color.image);
                assert(!err);
                if (err != VK_SUCCESS)
                {
#ifdef _DEBUG
                    Core::DebugPrintF("VKRenderTarget::VPrepare(): Error creating color image!\n");
#endif
                    return false;
                }

                vkGetImageMemoryRequirements(device, m_color.image, &memReqs);
                memAllocInfo.allocationSize = memReqs.size;
                renderer->MemoryTypeFromProperties(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &memAllocInfo.memoryTypeIndex);

                err = vkAllocateMemory(device, &memAllocInfo, nullptr, &m_color.memory);
                assert(!err);
                if (err != VK_SUCCESS)
                {
#ifdef _DEBUG
                    Core::DebugPrintF("VKRenderTarget::VPrepare(): Error allocating color image memory!\n");
#endif
                    return false;
                }

                err = vkBindImageMemory(device, m_color.image, m_color.memory, 0);
                if (err != VK_SUCCESS)
                {
#ifdef _DEBUG
                    Core::DebugPrintF("VKRenderTarget::VPrepare(): Error binding color image memory!\n");
#endif
                    return false;
                }

                renderer->SetImageLayout(setupCommand, m_color.image, VK_IMAGE_ASPECT_COLOR_BIT,
                    VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);

                VkImageViewCreateInfo viewInfo = {};
                viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
                viewInfo.pNext = nullptr;
                viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
                viewInfo.format = m_colorFormat;
                viewInfo.flags = 0;
                viewInfo.subresourceRange = {};
                viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
                viewInfo.subresourceRange.baseMipLevel = 0;
                viewInfo.subresourceRange.levelCount = 1;
                viewInfo.subresourceRange.baseArrayLayer = 0;
                viewInfo.subresourceRange.layerCount = 1;
                viewInfo.image = m_color.image;

                err = vkCreateImageView(device, &viewInfo, nullptr, &m_color.view);
                if (err != VK_SUCCESS)
                {
#ifdef _DEBUG
                    Core::DebugPrintF("VKRenderTarget::VPrepare(): Error creating color image view!\n");
#endif
                    return false;
                }

                //We can reuse the same info structs to build the depth image
                imageInfo.format = m_depthFormat;
                imageInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;

                err = vkCreateImage(device, &imageInfo, nullptr, &(m_depth.image));

                assert(!err);
                if (err != VK_SUCCESS)
                {
#ifdef _DEBUG
                    Core::DebugPrintF("VKRenderTarget::VPrepare(): Error creating depth image!\n");
#endif
                    return false;
                }

                viewInfo.format = m_depthFormat;
                viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;

                vkGetImageMemoryRequirements(device, m_depth.image, &memReqs);
                memAllocInfo.allocationSize = memReqs.size;
                renderer->MemoryTypeFromProperties(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &memAllocInfo.memoryTypeIndex);

                err = vkAllocateMemory(device, &memAllocInfo, nullptr, &m_depth.memory);
                assert(!err);
                if (err != VK_SUCCESS)
                {
#ifdef _DEBUG
                    Core::DebugPrintF("VKRenderTarget::VPrepare(): Error allocating depth image memory!\n");
#endif
                    return false;
                }

                err = vkBindImageMemory(device, m_depth.image, m_depth.memory, 0);
                if (err != VK_SUCCESS)
                {
#ifdef _DEBUG
                    Core::DebugPrintF("VKRenderTarget::VPrepare(): Error binding depth image memory!\n");
#endif
                    return false;
                }

                renderer->SetImageLayout(setupCommand, m_depth.image,
                    VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
                    VK_IMAGE_LAYOUT_UNDEFINED,
                    VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);

                viewInfo.image = m_depth.image;

                err = vkCreateImageView(device, &viewInfo, nullptr, &m_depth.view);
                if (err != VK_SUCCESS)
                {
#ifdef _DEBUG
                    Core::DebugPrintF("VKRenderTarget::VPrepare(): Error creating depth image view!\n");
#endif
                    return false;
                }

                renderer->FlushSetupCommandBuffer();

                //Finally create internal framebuffer
                VkImageView attachments[2];
                attachments[0] = m_color.view;
                attachments[1] = m_depth.view;

                VkFramebufferCreateInfo framebufferInfo = {};
                framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
                framebufferInfo.pNext = nullptr;
                framebufferInfo.flags = 0;
                framebufferInfo.renderPass = *((VKRenderPass*)m_renderPass)->GetVkRenderPass();
                framebufferInfo.attachmentCount = 2;
                framebufferInfo.pAttachments = attachments;
                framebufferInfo.width = m_width;
                framebufferInfo.height = m_height;
                framebufferInfo.layers = 1;

                err = vkCreateFramebuffer(device, &framebufferInfo, nullptr, &m_framebuffer);
                if (err != VK_SUCCESS)
                {
#ifdef _DEBUG
                    Core::DebugPrintF("VKRenderTarget::VPrepare(): Error creating framebuffer!\n");
#endif
                    return false;
                }

                return true;
            }

Если кто-то хочет получить дополнительную информацию о коде, не стесняйтесь спрашивать, и я его предоставил. Там много строк кода для этого проекта, поэтому я не хочу, чтобы все пробирались через все это. Если вы хотите, чтобы весь код можно было найти в http://github.com/thirddegree/HatchitGraphics/tree/dev

Изменить: после того, как я немного пошутил, я обнаружил, что даже цвет не совсем корректно. RenderDoc показывает, что каждый кадр отображает только вырез raptor и не очищает остальную часть кадра. Это проблема с драйвером?

Изменить: дополнительная информация. Я обнаружил, что если я рисую НИЧЕГО, просто начните и завершите проход рендеринга даже при рисовании полноэкранного треугольника, экран будет очищен. Однако, если я рисую только треугольник, глубина ошибочна (даже если я ничего не делаю из-за экрана или не применяю какую-либо текстуру).

Изменить: более конкретно цвет будет очищен, но глубина не будет. Если я ничего не рисую, глубина останется черной; все 0s. Почему полноэкранный треугольник вызывает странную статическую глубину, я не уверен.

Ответ 1

Это именно то, что случилось со мной, когда я начал использовать мои примеры Vulkan для аппаратного обеспечения AMD:

введите описание изображения здесь

Их графические процессоры в значительной степени зависят от правильных графических переходов (которые в основном игнорируются, например, NVIDIA), и я думаю, что коррупция, которую вы видите на ваших снимках экрана, является результатом отсутствия предварительного барьера.

Предшествующий барьер (см. здесь) преобразует макет изображения вашего вложения цветов в формат презентации для передачи его в своп цепь.

Это нужно сделать после того, как вы закончили рендеринг в своем цветовом приложении, чтобы убедиться, что вложение завершено до его представления.

Вы можете увидеть пример этого в схеме рисования моих примеров.

При рендеринге следующего кадра вам нужно будет преобразовать формат изображения вставки цвета, чтобы он мог снова отобразить его.

Подводя итог:

  • Перед тем, как выполнить переход к вашему цвету, переместите изображение с VK_IMAGE_LAYOUT_PRESENT_SRC_KHR на VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL (aka "post present" )

  • Сделайте рендеринг

  • Переместите образ прикрепления цвета с VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL до VK_IMAGE_LAYOUT_PRESENT_SRC_KHR и укажите его в цепочке подкачки

Ответ 2

Благодаря Sascha и некоторым дополнительным ошибкам, появившимся с новым 1.0.5 LunarG SDK, мне удалось исправить эту проблему. Конец с изменениями исправления (и несколько других мелочей) можно найти здесь: https://github.com/thirddegree/HatchitGraphics/commit/515d0303f45a8e9c00f67a74c824530ea37b687a

Это была комбинация нескольких вещей:

Мне нужно было установить изображение глубины в приложении framebuffer swapchain до VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, а не только VK_IMAGE_ASPECT_DEPTH_BIT

Для почти всех границ памяти изображений я забыл указать baseArrayLayer subresourceRange. Это не вызвало ошибку до версии 1.0.5.

Еще одна ошибка, которая не появлялась до 1.0.5, которая могла бы помочь вам отслеживать подобную ошибку и повлияла на генерацию текстуры, заключалась в том, что до того, как я сопоставил память устройства для хранения текстуры в памяти, мне нужно было перейти от VK_IMAGE_LAYOUT_UNDEFINED to VK_IMAGE_LAYOUT_GENERAL, отправьте эту команду, сопоставьте память и затем переведите ее из GENERAL в VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL (не забудьте также отправить эту команду). Опять же, это только для текстур, которые вы хотите сэмплировать, но я думаю, что мораль здесь "фактически представляет ваши переходы изображения".