Compare commits

...

1 Commits

Author SHA1 Message Date
Powei Feng
5b9308817c vk: fix SSR crash on Windows AMD
Crash is due to the color output being used as the SSR history.
Here we blit the color output to another texture and use the copy
as the history.

Fixes #9680
2026-02-26 21:38:58 -08:00
6 changed files with 50 additions and 6 deletions

View File

@@ -1706,6 +1706,8 @@ enum class Workaround : uint16_t {
DISABLE_DEPTH_PRECACHE_FOR_DEFAULT_MATERIAL,
// Emulate an sRGB swapchain in shader code.
EMULATE_SRGB_SWAPCHAIN,
// Workaround for AMD Vulkan drivers on Windows crashing when SSR history is detached.
BLIT_SSR_HISTORY,
};
using StereoscopicType = Platform::StereoscopicType;

View File

@@ -121,6 +121,10 @@ public:
return mPhysicalDeviceProperties.properties.vendorID;
}
inline char const* getDriverName() const noexcept {
return mDriverProperties.driverName;
}
/**
* Fetches a list of pre-registered external formats for prewarming the Vulkan
* pipeline cache.
@@ -217,6 +221,9 @@ private:
VkPhysicalDeviceProperties2 mPhysicalDeviceProperties = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
};
VkPhysicalDeviceDriverProperties mDriverProperties = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES,
};
VkPhysicalDeviceVulkan11Features mPhysicalDeviceVk11Features = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES,
};

View File

@@ -49,6 +49,7 @@
#include <chrono>
#include <mutex>
#include <string_view>
using namespace bluevk;
@@ -1656,6 +1657,17 @@ bool VulkanDriver::isWorkaroundNeeded(Workaround workaround) {
return false;
case Workaround::DISABLE_BLIT_INTO_TEXTURE_ARRAY:
return false;
case Workaround::BLIT_SSR_HISTORY: {
#if defined(WIN32)
// AMD GPU on windows crashes when SSR is enabled. It's root-caused to be writing to the
// SSR history texture. We blit the history instead of using the linear output of the
// color pass. See Issue #9680.
std::string_view deviceName{ mContext.getDriverName() };
return deviceName.find("AMD proprietary driver") != std::string_view::npos;
#else
return false;
#endif
}
default:
return false;
}

View File

@@ -952,6 +952,9 @@ void VulkanPlatform::queryAndSetDeviceFeatures(Platform::DriverConfig const& dri
chainStruct(&context.mPhysicalDeviceFeatures, &globalPriorityFeatures);
}
// Obtain and store driver info
chainStruct(&context.mPhysicalDeviceProperties, &context.mDriverProperties);
// Initialize the following fields: physicalDeviceProperties, memoryProperties,
// physicalDeviceFeatures.
vkGetPhysicalDeviceProperties2(mImpl->mPhysicalDevice, &context.mPhysicalDeviceProperties);

View File

@@ -104,6 +104,8 @@ FRenderer::FRenderer(FEngine& engine) :
mIsFrameBufferFetchSupported(false),
mIsFrameBufferFetchMultiSampleSupported(false),
mIsAutoDepthResolveSupported(false),
mWorkaroundBlitSsrHistory(
engine.getDriverApi().isWorkaroundNeeded(Workaround::BLIT_SSR_HISTORY)),
mUserEpoch(engine.getEngineEpoch()),
mResourceAllocator(std::make_unique<TextureCache>(
engine.getSharedResourceAllocatorDisposer(),
@@ -1337,21 +1339,38 @@ void FRenderer::renderJob(DriverApi& driver, RootArenaScope& rootArenaScope, FVi
struct ExportSSRHistoryData {
FrameGraphId<FrameGraphTexture> history;
};
// FIXME: should we use the TAA-modified cameraInfo here or not? (we are).
mat4 const projection = cameraInfo.projection * cameraInfo.getUserViewMatrix();
fg.addPass<ExportSSRHistoryData>("Export SSR history",
// we can't use colorPassOutput here because it could be tonemapped
FrameGraphId<FrameGraphTexture> history = colorPassOutput.linearColor;
if (mWorkaroundBlitSsrHistory) {
history = ppm.blit(fg, false, history,
{ 0, 0, colorBufferDesc.width, colorBufferDesc.height },
{
.width = colorBufferDesc.width,
.height = colorBufferDesc.height,
.format = config.hdrFormat,
},
SamplerMagFilter::LINEAR, SamplerMinFilter::LINEAR);
}
fg.addPass<ExportSSRHistoryData>(
mWorkaroundBlitSsrHistory ? "Export SSR history (Blit Workaround)"
: "Export SSR history",
[&](FrameGraph::Builder& builder, auto& data) {
// We need to use sideEffect here to ensure this pass won't be culled.
// The "output" of this pass is going to be used during the next frame as
// an "import".
builder.sideEffect();
// we can't use colorPassOutput here because it could be tonemapped
data.history = builder.sample(colorPassOutput.linearColor); // FIXME: an access must be declared for detach(), why?
}, [&view, projection](FrameGraphResources const& resources, auto const& data) {
data.history = builder.sample(history);
},
[&view, projection](FrameGraphResources const& resources, auto const& data) {
auto& history = view.getFrameHistory();
auto& current = history.getCurrent();
current.ssr.projection = projection;
// FIXME: an access must be declared for detach(), why?
resources.detach(data.history, &current.ssr.color, &current.ssr.desc);
});
}

View File

@@ -219,6 +219,7 @@ private:
bool mIsFrameBufferFetchSupported : 1;
bool mIsFrameBufferFetchMultiSampleSupported : 1;
bool mIsAutoDepthResolveSupported : 1;
bool const mWorkaroundBlitSsrHistory : 1;
Epoch mUserEpoch;
math::float4 mShaderUserTime{};
DisplayInfo mDisplayInfo;