Я работал с Vulkan последние пару недель и столкнулся с проблемой, которая возникала только на картах AMD. В частности, AMD 7970M. Я выполнил свой проект на картах GTX 700 и 900 без проблем. Я даже запускал на Windows Linux (Steam OS) с картами Nvidia без проблем. Проблема появляется только на картах AMD и только с моим проектом; все образцы и проекты из Саша Виллемс запустить без проблем.
Прямо сейчас я рисую текстурированную модель Raptor и вращаю ее на месте. Я отрисовываю это для текстуры и затем применяю эту текстуру к полноэкранному треугольнику; базовый внеэкранный рендеринг. Однако глубина на моем 7970M кажется не совсем корректной. Вместо этого я получаю этот странный артефакт, как будто глубина не очищается должным образом:
Конечно, я попытался покопаться в этом с RenderDoc, и глубина совершенно неверна. И Raptor, и полноэкранный треугольник, на котором он нарисован, просто беспорядок:
Я попытался сравнить мой код с примером за кадром от Sascha Willems, и, похоже, я делаю почти все одинаково. Я подумал, что, возможно, что-то будет не так с тем, как я создал свою глубину, но это кажется хорошим по сравнению со всеми примерами, которые я видел.
Вот несколько отладочных представлений о том, где я создаю изображение и представление глубины:
Вот весь метод:
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 показывает, что каждый кадр отображает только вырез раптора и не очищает остальную часть кадра. Это проблема с драйверами?
Изменить: немного больше информации. Я обнаружил, что если я НИЧЕГО не рисую, просто начинаю и заканчиваю проход рендера, даже не рисуя мой полноэкранный треугольник, экран очистится. Однако, если я рисую только треугольник, глубина будет неправильной (даже если я ничего не сглажу с экрана или не применю какую-либо текстуру).
Изменить: более конкретно цвет будет очищаться, а глубина нет. Если я ничего не нарисую, глубина останется черной; все 0 Почему полноэкранный треугольник вызывает странную статичность глубины, я не уверен.
Это именно то, что случилось со мной, когда я начал работать над примерами Vulkan на оборудовании AMD:
Их графические процессоры в значительной степени полагаются на правильные переходы изображения (которые в основном игнорируются, например, NVIDIA), и я думаю, что искажение, которое вы видите на своих скриншотах, является результатом отсутствующего существующего барьера.
Прежний барьер (см. Вот) преобразует макет изображения вашего цветного вложения в формат представления для передачи представления в цепочку обмена.
Это должно быть сделано после того, как вы закончили рендеринг для вашего цветного вложения, чтобы убедиться, что вложение завершено перед его представлением.
Вы можете увидеть пример этого в рисовать рутины из моих примеров.
При рендеринге следующего кадра вам необходимо преобразовать формат изображения цветового вложения обратно, чтобы иметь возможность снова выполнить его рендеринг.
Подвести итог:
Перед рендерингом к вашему цветному приложению переведите ваше изображение из VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
в VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
(также известный как «пост подарок»)
Сделай свой рендеринг
Переходите ваше цветное вложение изображения из VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
в VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
и представить это в цепочку обмена
Благодаря Sascha и некоторым дополнительным ошибкам, которые появились в новом 1.0.5 LunarG SDK, мне удалось решить эту проблему. Коммит с исправленными изменениями (и парочкой других мелочей) можно найти здесь: https://github.com/thirddegree/HatchitGraphics/commit/515d0303f45a8e9c00f67a74c824530ea37b687a
Это была комбинация нескольких вещей:
Мне нужно было установить глубину изображения в приложении кадрового буфера в цепочке обмена, чтобы 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
в VK_IMAGE_LAYOUT_GENERAL
, отправьте эту команду, отобразите память и затем переведите ее из ОБЩЕГО в VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
(не забудьте отправить эту команду тоже). Опять же, это только для текстур, которые вы хотите попробовать, но я полагаю, что мораль здесь заключается в том, чтобы «на самом деле представить ваши переходы изображения»