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

如何在Unreal编辑器插件中访问材质指令计数?

Unreal编辑器插件获取材质指令计数的解决思路

问题背景

我正在开发Unreal编辑器插件,需要获取材质的指令计数。在引擎代码中找到官方编辑器的实现路径:\Engine\Source\Editor\MaterialEditor\Public\MaterialStatsCommon.h里的FMaterialStatsUtils类,提供了静态方法ExtractMatertialStatsInfo用来提取材质统计信息:

/** class used for various stats utilities */
class FMaterialStatsUtils
{
public:
    MATERIALEDITOR_API static void ExtractMatertialStatsInfo(struct FShaderStatsInfo& OutInfo, const FMaterialResource* Target);
};

但问题在于:FMaterialStatsUtils是公开API,而存储统计结果的FShaderStatsInfo结构体却定义在同模块的私有文件夹MaterialStats.h中。插件引入MaterialEditor模块后,只能通过前向声明FShaderStatsInfo调用方法,但无法访问其内部成员(编译器提示"类型不完整"),也不能直接包含私有头文件MaterialStats.h。

我的卡点代码如下:

#include "MaterialStatsCommon.h"

struct FShaderStatsInfo;

void MyEditorBPLibraryName::GetMaterialStatsInfo(UMaterial* Material, int32 BasePassShaderInstructionCount, int32 VertexShaderInstructionCount, int32 TextureSampleCount)
{
    if (Material == nullptr)
    {
        return;
    }
    FShaderStatsInfo* OutInfo = nullptr;
    FMaterialStatsUtils::ExtractMatertialStatsInfo(*OutInfo, Material->GetMaterialResource(ERHIFeatureLevel::SM5, EMaterialQualityLevel::Num));
    //OutInfo-> 需访问ShaderInstructionCount,但无法操作

    return;
}

需要用到的FShaderStatsInfo结构体定义(来自私有头文件MaterialStats.h):

#include "MaterialStatsCommon.h"
#include "UObject/GCObject.h"

/** structure used to store various statistics extracted from compiled shaders */
struct FShaderStatsInfo
{
    struct FContent
    {
        FString StrDescription;
        FString StrDescriptionLong;
    };

    TMap<ERepresentativeShader, FContent> ShaderInstructionCount;
    FContent SamplersCount;
    FContent InterpolatorsCount;
    FContent TextureSampleCount;
    FContent VirtualTextureLookupCount;
    FString StrShaderErrors;

    void Reset()
    {
        ShaderInstructionCount.Empty();

        SamplersCount.StrDescription = TEXT("Compiling...");
        SamplersCount.StrDescriptionLong = TEXT("Compiling...");

        InterpolatorsCount.StrDescription = TEXT("Compiling...");
        InterpolatorsCount.StrDescriptionLong = TEXT("Compiling...");

        TextureSampleCount.StrDescription = TEXT("Compiling...");
        TextureSampleCount.StrDescriptionLong = TEXT("Compiling...");

        VirtualTextureLookupCount.StrDescription = TEXT("Compiling...");
        VirtualTextureLookupCount.StrDescriptionLong = TEXT("Compiling...");
        
        StrShaderErrors.Empty();
    }

    void Empty()
    {
        ShaderInstructionCount.Empty();

        SamplersCount.StrDescription.Empty();
        SamplersCount.StrDescriptionLong.Empty();

        InterpolatorsCount.StrDescription.Empty();
        InterpolatorsCount.StrDescriptionLong.Empty();
        
        TextureSampleCount.StrDescription.Empty();
        TextureSampleCount.StrDescriptionLong.Empty();

        VirtualTextureLookupCount.StrDescription.Empty();
        VirtualTextureLookupCount.StrDescriptionLong.Empty();

        StrShaderErrors.Empty();
    }

    bool HasErrors()
    {
        return !StrShaderErrors.IsEmpty();
    }
};

解决办法

方法1:通过Build配置访问私有头文件

这种方法允许你直接使用官方的FMaterialStatsUtils逻辑,需要修改插件的.Build.cs文件添加私有头文件路径:

using System.IO;
using UnrealBuildTool;

public class YourEditorPlugin : ModuleRules
{
    public YourEditorPlugin(ReadOnlyTargetRules Target) : base(Target)
    {
        PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
        
        PublicDependencyModuleNames.AddRange(
            new string[]
            {
                "Core",
                "CoreUObject",
                "Engine",
                "UnrealEd",
                "MaterialEditor" // 必须依赖MaterialEditor模块
            }
        );

        // 添加MaterialEditor私有头文件的路径
        string EngineDir = Target.RelativeEnginePath;
        PrivateIncludePaths.Add(Path.Combine(EngineDir, "Source/Editor/MaterialEditor/Private"));
    }
}

修改后,就可以在代码中直接包含MaterialStats.h并访问结构体成员了:

#include "MaterialStatsCommon.h"
#include "MaterialStats.h" // 现在可以正常包含

void MyEditorBPLibraryName::GetMaterialStatsInfo(UMaterial* Material, int32& BasePassShaderInstructionCount, int32& VertexShaderInstructionCount, int32& TextureSampleCount)
{
    // 初始化输出参数
    BasePassShaderInstructionCount = 0;
    VertexShaderInstructionCount = 0;
    TextureSampleCount = 0;

    if (!Material)
    {
        return;
    }

    const FMaterialResource* MaterialResource = Material->GetMaterialResource(ERHIFeatureLevel::SM5, EMaterialQualityLevel::Num);
    if (!MaterialResource)
    {
        return;
    }

    FShaderStatsInfo OutInfo;
    OutInfo.Reset();
    FMaterialStatsUtils::ExtractMatertialStatsInfo(OutInfo, MaterialResource);

    // 提取BasePass像素Shader指令数
    if (const FContent* BasePassContent = OutInfo.ShaderInstructionCount.Find(ERepresentativeShader::BasePassPixel))
    {
        FString NumStr = BasePassContent->StrDescription.Split(" ").Get<0>();
        BasePassShaderInstructionCount = FCString::Atoi(*NumStr);
    }

    // 提取BasePass顶点Shader指令数
    if (const FContent* VSContent = OutInfo.ShaderInstructionCount.Find(ERepresentativeShader::BasePassVertex))
    {
        FString NumStr = VSContent->StrDescription.Split(" ").Get<0>();
        VertexShaderInstructionCount = FCString::Atoi(*NumStr);
    }

    // 提取纹理采样数
    FString SampleNumStr = OutInfo.TextureSampleCount.StrDescription.Split(" ").Get<0>();
    TextureSampleCount = FCString::Atoi(*SampleNumStr);
}

注意:这种方法依赖引擎私有API,不同Unreal版本可能会修改FShaderStatsInfo的结构或FMaterialStatsUtils的实现,需要做好版本兼容性测试。

方法2:自行实现材质统计逻辑(无私有API依赖)

如果不想依赖私有API,可以直接从FMaterialResource中获取Shader并统计指令数,逻辑参考官方实现但完全自主:

#include "Materials/Material.h"
#include "Materials/MaterialResource.h"
#include "ShaderCore.h"
#include "BasePassRendering.h" // 包含BasePassShader的定义

void MyEditorBPLibraryName::GetMaterialStatsInfo(UMaterial* Material, int32& BasePassShaderInstructionCount, int32& VertexShaderInstructionCount, int32& TextureSampleCount)
{
    // 初始化输出参数
    BasePassShaderInstructionCount = 0;
    VertexShaderInstructionCount = 0;
    TextureSampleCount = 0;

    if (!Material)
    {
        return;
    }

    const FMaterialResource* MaterialResource = Material->GetMaterialResource(ERHIFeatureLevel::SM5, EMaterialQualityLevel::Num);
    if (!MaterialResource)
    {
        return;
    }

    // 获取BasePass像素Shader并统计指令数
    const FShader* BasePassPS = MaterialResource->GetShader<FBasePassPixelShader>();
    if (BasePassPS)
    {
        BasePassShaderInstructionCount = BasePassPS->GetNumInstructions();
    }

    // 获取BasePass顶点Shader并统计指令数
    const FShader* BasePassVS = MaterialResource->GetShader<FBasePassVertexShader>();
    if (BasePassVS)
    {
        VertexShaderInstructionCount = BasePassVS->GetNumInstructions();
    }

    // 纹理采样数统计:参考官方FMaterialStatsUtils的实现,遍历Shader的资源绑定或使用Shader编译器输出
    // 这里可以通过解析Shader的反射信息或统计采样器数量来实现
    // 示例逻辑(需要根据引擎版本调整):
    // TextureSampleCount = MaterialResource->GetNumTextureSamples();
}

这种方法不依赖私有API,稳定性更高,但需要自行实现纹理采样数的统计逻辑,可参考引擎中FMaterialStatsUtils::ExtractMatertialStatsInfo的源码实现。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 19:27:34