Fix AMD RDNA startup crash and rendering corruption

# What changed
- Drop the unused VK_FORMAT_D24_UNORM_S8_UINT device check that
  rejected some AMD GPUs.
- Only treat VALIDATION-type Vulkan messages as fatal, not
  GENERAL-type ones.
- Restrict compute queues to the graphics queue's family.
- Force per-invocation lane mask emulation for 64-wide waves, since
  RDNA's lane layout differs from GCN despite matching wave size.

# Why
On AMD Radeon GPUs the emulator failed to find a suitable device,
crashed during queue setup, or rendered corrupted output, while
NVIDIA was unaffected.

Each change removes an assumption in device selection, debug
message handling, queue selection, or shader lane-mask emulation
that happened to hold for NVIDIA but not for AMD.

# Validation
- Built on Windows with clang-cl. Note: the latest Vulkan SDK
  (1.4.350) fails to build against the pinned Vulkan-Headers
  submodule; used 1.4.328.1 instead, which matches it.
- Tested on a Radeon RX 9070 XT + Ryzen 7 9800X3D.
- Alex Kidd in Miracle World DX and Dreaming Sarah now boot and
  render correctly (previously failed to start or rendered
  corrupted).
- Not yet verified for regressions on NVIDIA hardware; testing from
  someone with an NVIDIA GPU would help confirm no regression there.

# AI assistance disclosure
AI assistance was used to diagnose the root cause of each issue and
prepare the initial patch. The contributor reviewed the complete
diff and tested it on the affected AMD hardware before opening this
pull request.
This commit is contained in:
kirathenotebook
2026-07-18 10:46:16 +03:00
committed by nmzik
parent 601c82a031
commit e05cd30268
2 changed files with 27 additions and 11 deletions
@@ -230,7 +230,22 @@ static VulkanQueues VulkanFindQueues(VkPhysicalDevice device, VkSurfaceKHR surfa
};
select_queues(graphics_num, [](const auto& q) { return q.graphics; }, qs.graphics);
select_queues(compute_num, [](const auto& q) { return q.compute; }, qs.compute);
const uint32_t graphics_family =
qs.graphics.empty() ? static_cast<uint32_t>(-1) : qs.graphics.front().family;
select_queues(
compute_num,
[graphics_family](const auto& q) { return q.compute && q.family == graphics_family; },
qs.compute);
if (compute_num != 0 && qs.compute.empty()) {
auto graphics_compute = std::find_if(qs.graphics.begin(), qs.graphics.end(),
[](const auto& q) { return q.compute; });
if (graphics_compute != qs.graphics.end()) {
// Reuse the universal graphics queue when Intel GPUs expose no spare compute queue.
qs.compute.push_back(*graphics_compute);
}
}
select_queues(transfer_num, [](const auto& q) { return q.transfer; }, qs.transfer);
select_queues(present_num, [](const auto& q) { return q.present; }, qs.present);
@@ -433,12 +448,6 @@ static void VulkanFindPhysicalDevice(VkInstance instance, VkSurfaceKHR surface,
skip_device = true;
}
if (!skip_device && !CheckFormat(device, VK_FORMAT_D24_UNORM_S8_UINT, true,
VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)) {
LOGF("Format VK_FORMAT_D24_UNORM_S8_UINT cannot be used as depth buffer\n");
skip_device = true;
}
if (!skip_device && !CheckFormat(device, VK_FORMAT_BC3_SRGB_BLOCK, true,
VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT |
VK_FORMAT_FEATURE_TRANSFER_DST_BIT)) {
@@ -831,7 +840,9 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL VulkanDebugMessengerCallback(
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
severity_str = "E";
severity_style = Log::Color::BrightRed;
error = true;
// Only validation errors are fatal; GENERAL-type errors can come
// from unrelated loader/layer issues (e.g. a broken overlay).
error = (message_types & VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT) != 0;
break;
default: severity_str = "?";
}