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

SharpDX自定义像素化Effect初始化触发E_INVALIDARG错误求助

SharpDX自定义像素化Effect参数错误解决及正确注册使用方式

问题背景

想要在SharpDX的DeviceContext中实现自定义像素化效果,参考内置ShadowEffect的使用流程,但在初始化自定义Effect时触发E_INVALIDARG参数错误,调用流程为Initialize() -> SetSingleTransformNode() -> InputCount getter后报错。

当前实现包含HLSL像素着色器、编译命令、C#自定义Effect类及调用代码,具体如下:

现有HLSL像素着色器

Texture2D InputTexture : register(t0);
SamplerState InputSampler : register(s0);

cbuffer PixelateParams : register(b0)
{
    float2 blockSize;
    float2 textureSize;
    float featherRadius;
};

float4 main(float2 uv : TEXCOORD) : SV_Target
{
    float2 pixelCoord = uv * textureSize;
    float2 blockOrigin = floor(pixelCoord / blockSize) * blockSize;
    float2 sampleUV = blockOrigin / textureSize;

    float4 color = InputTexture.Sample(InputSampler, sampleUV);

    if (featherRadius > 0)
    {
        float2 distToEdge = min(pixelCoord, textureSize - pixelCoord);
        float edgeDist = min(distToEdge.x, distToEdge.y);
        float alpha = saturate(edgeDist / featherRadius);
        color.a *= alpha;
    }

    return color;
}

着色器编译命令

fxc.exe /T ps_5_0 /E main /Fo $csoFile $hlslFile

错误根源分析

  1. 未注册自定义属性:调用SetValueByName设置TextureSize时,Effect类未通过RegisterProperty注册对应属性,导致参数识别失败。
  2. 常量缓冲区对齐问题:C#结构体的填充方式需严格匹配HLSL cbuffer的16字节对齐要求,原结构体的填充逻辑虽数值正确,但可优化为更清晰的16字节对齐布局。
  3. InputCount属性实现冲突:CustomEffectBase基类已包含InputCount相关逻辑,需明确实现DrawTransform接口的InputCount属性,避免歧义。
  4. 常量缓冲区初始化时机缺失:SetDrawInformation方法中未初始化常量缓冲区,导致初始参数未传递到GPU。

修复后的完整自定义Effect实现

using SharpDX;
using SharpDX.Direct2D1;
using System.Reflection;
using System.Runtime.InteropServices;

public class PixelateEffect : CustomEffectBase, DrawTransform
{
    private static readonly Guid PixelShaderId = new("AAAAF3D2-7322-4E9A-9F3F-12345678944B");
    private DrawInformation? _drawInformation;
    private PixelationParams _parameters;

    // 严格匹配HLSL cbuffer的16字节对齐布局
    [StructLayout(LayoutKind.Sequential, Pack = 4)]
    private struct PixelationParams
    {
        public Vector2 BlockSize;       // 8字节
        public Vector2 TextureSize;     // 8字节 → 累计16字节
        public float FeatherRadius;     // 4字节
        private float _padding1;        // 4字节
        private float _padding2;        // 4字节
        private float _padding3;        // 4字节 → 累计32字节(16×2),满足cbuffer对齐要求
    }

    // 注册自定义属性,供Effect包装类访问
    public static readonly PropertyDescription PixelSizeProperty = new(
        "PixelSize",
        PropertyType.Vector2,
        typeof(PixelateEffect),
        new Vector2(5f, 5f),
        new Vector2(1f, 1f),
        new Vector2(500f, 500f)
    );

    public static readonly PropertyDescription TextureSizeProperty = new(
        "TextureSize",
        PropertyType.Vector2,
        typeof(PixelateEffect),
        new Vector2(10f, 10f),
        new Vector2(1f, 1f),
        new Vector2(10000f, 10000f)
    );

    public static readonly PropertyDescription FeatherRadiusProperty = new(
        "FeatherRadius",
        PropertyType.Float,
        typeof(PixelateEffect),
        10f,
        0f,
        500f
    );

    public Vector2 PixelSize
    {
        get => _parameters.BlockSize;
        set
        {
            _parameters.BlockSize = new Vector2(
                MathUtil.Clamp(value.X, 1f, 500f),
                MathUtil.Clamp(value.Y, 1f, 500f)
            );
            UpdateConstants();
            NotifyPropertyChanged(PixelSizeProperty);
        }
    }

    public Vector2 TextureSize
    {
        get => _parameters.TextureSize;
        set
        {
            _parameters.TextureSize = new Vector2(
                MathUtil.Clamp(value.X, 1f, 10000f),
                MathUtil.Clamp(value.Y, 1f, 10000f)
            );
            UpdateConstants();
            NotifyPropertyChanged(TextureSizeProperty);
        }
    }

    public float FeatherRadius
    {
        get => _parameters.FeatherRadius;
        set
        {
            _parameters.FeatherRadius = MathUtil.Clamp(value, 0f, 500f);
            UpdateConstants();
            NotifyPropertyChanged(FeatherRadiusProperty);
        }
    }

    public PixelateEffect()
    {
        _parameters = new PixelationParams
        {
            BlockSize = new Vector2(5f, 5f),
            TextureSize = new Vector2(10f, 10f),
            FeatherRadius = 10f
        };

        // 注册属性到Effect系统
        RegisterProperty(PixelSizeProperty);
        RegisterProperty(TextureSizeProperty);
        RegisterProperty(FeatherRadiusProperty);
    }

    public override void Initialize(EffectContext effectContext, TransformGraph transformGraph)
    {
        var assembly = Assembly.GetAssembly(typeof(PixelateEffect));
        const string resourceName = "MyApp.Shaders.Compiled.Pixelate.cso";

        using var stream = assembly!.GetManifestResourceStream(resourceName) 
            ?? throw new FileNotFoundException("无法找到嵌入的着色器资源", resourceName);

        using var ms = new MemoryStream();
        stream.CopyTo(ms);
        effectContext.LoadPixelShader(PixelShaderId, ms.ToArray());

        transformGraph.SetSingleTransformNode(this);
    }

    public override void PrepareForRender(ChangeType changeType)
    {
        UpdateConstants();
    }

    public override void SetGraph(TransformGraph transformGraph)
    {
        // 单节点Transform无需额外设置,直接使用SetSingleTransformNode即可
    }

    public void SetDrawInformation(DrawInformation drawInfo)
    {
        _drawInformation = drawInfo;
        _drawInformation.SetPixelShader(PixelShaderId, PixelOptions.None);
        _drawInformation.SetInputDescription(0, new InputDescription(Filter.MinMagLinearMipPoint, 1));
        // 初始化时设置常量缓冲区
        UpdateConstants();
    }

    public RawRectangle MapInvalidRect(int inputIndex, RawRectangle invalidInputRect)
    {
        return invalidInputRect;
    }

    public RawRectangle MapInputRectanglesToOutputRectangle(RawRectangle[] inputRects, RawRectangle[] inputOpaqueSubRects, out RawRectangle outputOpaqueSubRect)
    {
        if (inputRects.Length != 1)
            throw new ArgumentException("输入矩形数组长度必须为1", nameof(inputRects));

        outputOpaqueSubRect = default;
        return inputRects[0];
    }

    public void MapOutputRectangleToInputRectangles(RawRectangle outputRect, RawRectangle[] inputRects)
    {
        if (inputRects.Length != 1)
            throw new ArgumentException("输入矩形数组长度必须为1", nameof(inputRects));

        // 像素化效果无需扩展输入区域,直接映射
        inputRects[0] = outputRect;
    }

    // 明确实现DrawTransform接口的InputCount属性
    int DrawTransform.InputCount => 1;

    private void UpdateConstants()
    {
        _drawInformation?.SetPixelConstantBuffer(ref _parameters);
    }
}

正确的调用方式

// 在Factory创建后、DeviceContext初始化前注册Effect
Factory.RegisterEffect<PixelateEffect>();

// 创建并使用自定义Effect
using var pixelateEffect = new Effect<PixelateEffect>(DeviceContext);
// 直接通过属性设置参数,无需SetValueByName
pixelateEffect.TextureSize = new Vector2(width, height);
pixelateEffect.PixelSize = new Vector2(8f, 8f);
pixelateEffect.FeatherRadius = 5f;
// 设置输入纹理
pixelateEffect.SetInput(0, cropped, true);

// 绘制效果
DeviceContext.DrawImage(pixelateEffect);

关键修复点说明

  • 属性注册:通过RegisterProperty方法将自定义属性注册到Effect系统,确保参数能被正确识别和访问。
  • 缓冲区对齐:调整C#结构体的填充字段,严格匹配HLSL cbuffer的16字节对齐要求,避免GPU读取常量时出错。
  • 接口实现:明确实现DrawTransform的InputCount属性,避免与基类逻辑冲突。
  • 初始化时机:在SetDrawInformation中调用UpdateConstants,确保初始参数被传递到GPU。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 03:45:54