You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何触发GPU崩溃?求UE4/UE5相关代码用于研究崩溃处理流程

当然有,GPU崩溃通常源于驱动无法处理非法渲染指令、资源访问越界或硬件层面的无效操作——和CPU写空指针触发崩溃的逻辑类似,我们可以构造对应的GPU操作来触发崩溃。下面分原生DirectX和UE4/5两种场景给出代码示例,重点针对UE的场景,方便你研究它的GPU崩溃处理流程:

原生DirectX简单触发示例

在DX11环境下,绑定无效资源视图到管线,或者让Compute Shader越界访问,都能快速触发GPU崩溃:

// DX11环境下触发GPU崩溃的极简代码
void TriggerGPUDXCrash(ID3D11Device* Device, ID3D11DeviceContext* Context)
{
    // 创建一个仅1字节的Buffer
    D3D11_BUFFER_DESC BufferDesc{};
    BufferDesc.ByteWidth = 1;
    BufferDesc.Usage = D3D11_USAGE_DEFAULT;
    BufferDesc.BindFlags = D3D11_BIND_UNORDERED_ACCESS;
    BufferDesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
    BufferDesc.StructureByteStride = 1;

    ID3D11Buffer* TestBuffer = nullptr;
    Device->CreateBuffer(&BufferDesc, nullptr, &TestBuffer);

    // 创建对应的UAV
    ID3D11UnorderedAccessView* TestUAV = nullptr;
    Device->CreateUnorderedAccessView(TestBuffer, nullptr, &TestUAV);

    // 绑定UAV到计算着色器槽位
    Context->CSSetUnorderedAccessViews(0, 1, &TestUAV, nullptr);

    // 或者更简单:绑定空的SRV后执行绘制,GPU会因访问无效资源崩溃
    ID3D11ShaderResourceView* InvalidSRV = nullptr;
    Context->PSSetShaderResources(0, 1, &InvalidSRV);
    Context->Draw(3, 0);
}
UE4/5 专属代码示例

UE中所有渲染操作必须在渲染线程执行,因此需要将崩溃触发逻辑提交到渲染线程处理。以下是两种可靠的触发方式:

方式1:Compute Shader越界写入(最直接)

首先创建自定义Compute Shader文件GPUDebugShader.usf:

RWStructuredBuffer<float> TestBuffer : register(u0);

[numthreads(64,1,1)]
void CS(uint3 DispatchThreadID : SV_DispatchThreadID)
{
    // 故意写入超出Buffer范围的索引,触发GPU内存访问错误
    TestBuffer[1000] = 1.0f;
}

然后在Actor中实现调用逻辑:

头文件(GPUDebugActor.h)

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "GPUDebugActor.generated.h"

UCLASS()
class YOURPROJECT_API AGPUDebugActor : public AActor
{
    GENERATED_BODY()
    
public:    
    AGPUDebugActor();

protected:
    virtual void BeginPlay() override;

private:
    void TriggerGPUCrushOnRenderThread();
};

实现文件(GPUDebugActor.cpp)

#include "GPUDebugActor.h"
#include "RenderCore.h"
#include "RHI.h"
#include "ShaderParameterUtils.h"
#include "GlobalShader.h"
#include "ShaderParameterStruct.h"

// 声明自定义Compute Shader
class FGPUDebugComputeShader : public FGlobalShader
{
    DECLARE_GLOBAL_SHADER(FGPUDebugComputeShader);
    SHADER_USE_PARAMETER_STRUCT(FGPUDebugComputeShader, FGlobalShader);

    BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
        SHADER_PARAMETER_UAV(RWStructuredBuffer<float>, TestBuffer)
    END_SHADER_PARAMETER_STRUCT()

public:
    static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
    {
        return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
    }

    static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
    {
        FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
    }
};

IMPLEMENT_GLOBAL_SHADER(FGPUDebugComputeShader, "/Shaders/GPUDebugShader.usf", "CS", SF_Compute);

AGPUDebugActor::AGPUDebugActor()
{
    PrimaryActorTick.bCanEverTick = false;
}

void AGPUDebugActor::BeginPlay()
{
    Super::BeginPlay();

    // 将崩溃逻辑提交到渲染线程执行
    ENQUEUE_RENDER_COMMAND(TriggerGPUCrushCmd)(
        [this](FRHICommandListImmediate& RHICmdList)
        {
            TriggerGPUCrushOnRenderThread();
        });
}

void AGPUDebugActor::TriggerGPUCrushOnRenderThread()
{
    // 创建仅能容纳1个float的Buffer(4字节)
    FRHIResourceCreateInfo CreateInfo;
    FStructuredBufferRHIRef TestBuffer = RHICreateStructuredBuffer(sizeof(float), 1, BUF_UnorderedAccess, CreateInfo);
    FUnorderedAccessViewRHIRef TestUAV = RHICreateUnorderedAccessView(TestBuffer, false, 0);

    // 获取Shader并设置参数
    const auto ShaderMap = GetGlobalShaderMap(GMaxRHIFeatureLevel);
    TShaderRef<FGPUDebugComputeShader> ComputeShader(ShaderMap);

    FGPUDebugComputeShader::FParameters* Parameters = RHICmdList.AllocParameters<FGPUDebugComputeShader::FParameters>();
    Parameters->TestBuffer = TestUAV;

    RHICmdList.SetComputeShader(ComputeShader);
    SetShaderParameters(RHICmdList, ComputeShader, ComputeShader.GetShader(), *Parameters);

    // 调度足够多的线程组,确保有线程执行到越界写入操作
    RHICmdList.DispatchComputeShader(20, 1, 1);
}

方式2:绑定无效资源视图(无需自定义Shader)

如果不想编写自定义Shader,可直接在渲染线程绑定无效资源视图,后续渲染时GPU会触发崩溃:

修改TriggerGPUCrushOnRenderThread函数:

void AGPUDebugActor::TriggerGPUCrushOnRenderThread()
{
    // 创建空的纹理视图(无效资源)
    FTextureRHIRef InvalidTexture = nullptr;
    FRHITextureViewRHIRef InvalidSRV = RHICreateShaderResourceView(InvalidTexture, 0);

    // 将无效SRV绑定到像素着色器的第一个槽位
    RHICmdList.SetShaderResourceView(0, InvalidSRV);

    // 下一帧渲染时,所有使用该槽位的材质都会尝试访问无效资源,触发GPU崩溃
}
注意事项
  • 不同GPU厂商对非法操作的处理有差异:NVIDIA通常触发硬件重置,AMD可能返回驱动错误,Intel的表现介于两者之间。
  • UE默认有GPU崩溃防护机制,测试时可在Project Settings > Engine > Rendering > GPU Crash Handling中关闭相关选项,确保触发真实崩溃。
  • 崩溃日志会输出到Saved/Logs目录,可通过日志分析UE的GPU崩溃处理流程。

内容的提问来源于stack exchange,提问作者yuki

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 18:48:24