SharpDX自定义像素化Effect初始化触发E_INVALIDARG错误求助
问题背景
想要在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
错误根源分析
- 未注册自定义属性:调用
SetValueByName设置TextureSize时,Effect类未通过RegisterProperty注册对应属性,导致参数识别失败。 - 常量缓冲区对齐问题:C#结构体的填充方式需严格匹配HLSL cbuffer的16字节对齐要求,原结构体的填充逻辑虽数值正确,但可优化为更清晰的16字节对齐布局。
- InputCount属性实现冲突:CustomEffectBase基类已包含InputCount相关逻辑,需明确实现DrawTransform接口的InputCount属性,避免歧义。
- 常量缓冲区初始化时机缺失: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
相关产品推荐
相关产品推荐

