stride3d/stride · error · NotImplementedException

Vulkan: only simple BorderColor are supported

Error message

Vulkan: only simple BorderColor are supported

What it means

Vulkan only accepts a fixed VkBorderColor enum (transparent/opaque black/white, float or int) for sampler border colors, while Stride's SamplerState lets you specify any Color. CreateNativeSampler maps only the well-known values (white, black, transparent) to Vulkan border colors and throws NotImplementedException for anything else, because an arbitrary color would require an integer border color setup or clamping workaround the backend doesn't implement.

Solutions

  1. Set SamplerStateDescription.BorderColor to one of the supported values: Color4.White, Color4.Black, or Color.Transparent.
  2. If a custom border color is required, implement it in-shader (e.g. detect out-of-range UVs and output the color) instead of relying on the sampler.
  3. Consider extending CreateNativeSampler to map nearest matching VkBorderColor (Float variants) if you control the engine code.

Example fix

// before
var desc = new SamplerStateDescription(TextureAddressMode.ClampToBorder, Filter.Linear) { BorderColor = new Color4(1f, 0f, 0f, 1f) };
// after
desc.BorderColor = Color4.Black;
Defensive patterns

Strategy: validation

Validate before calling

static readonly Color4[] supported = { Color4.White, Color4.Black, Color.Transparent };
if (!supported.Contains(desc.BorderColor))
    throw new NotSupportedException("BorderColor must be White, Black, or Transparent on Vulkan");

Type guard

bool HasSimpleBorderColor(SamplerStateDescription d) =>
    d.BorderColor == Color4.White || d.BorderColor == Color4.Black || d.BorderColor == Color.Transparent;

Try / catch

try { sampler = new SamplerState(device, desc); }
catch (NotImplementedException ex) when (ex.Message.Contains("BorderColor")) { desc.BorderColor = Color4.Black; sampler = new SamplerState(device, desc); }

Prevention

When it happens

Trigger: Creating (or recreating after device loss) a SamplerState whose Description.BorderColor is not Color4.White, Color4.Black, or Color.Transparent.

Common situations: Setting a custom tinted border color (e.g. Color.Red) on a sampler for clamp-to-border addressing; porting D3D11 code where arbitrary float border colors were allowed.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/21fd7bfc18262448. Report an issue: GitHub.

Appendix: source

Thrown at sources/engine/Stride.Graphics/Vulkan/SamplerState.Vulkan.cs:76

                mipLodBias = Description.MipMapLevelOfDetailBias,
                maxAnisotropy = Description.MaxAnisotropy,
                compareOp = VulkanConvertExtensions.ConvertComparisonFunction(Description.CompareFunction),
                minLod = Description.MinMipLevel,
                maxLod = Description.MaxMipLevel,
            };

            if (Description.AddressU == TextureAddressMode.Border ||
                Description.AddressV == TextureAddressMode.Border ||
                Description.AddressW == TextureAddressMode.Border)
            {
                if (Description.BorderColor == Color4.White)
                    createInfo.borderColor = VkBorderColor.FloatOpaqueWhite;
                else if (Description.BorderColor == Color4.Black)
                    createInfo.borderColor = VkBorderColor.FloatOpaqueBlack;
                else if (Description.BorderColor == Color.Transparent)
                    createInfo.borderColor = VkBorderColor.FloatTransparentBlack;
                else
                    throw new NotImplementedException("Vulkan: only simple BorderColor are supported");
            }

            ConvertMinFilter(Description.Filter, out createInfo.minFilter, out createInfo.magFilter, out createInfo.mipmapMode, out createInfo.compareEnable, out createInfo.anisotropyEnable);

            GraphicsDevice.CheckResult(GraphicsDevice.NativeDeviceApi.vkCreateSampler(GraphicsDevice.NativeDevice, &createInfo, null, out NativeSampler));
        }

        private static VkSamplerAddressMode ConvertAddressMode(TextureAddressMode addressMode)
        {
            switch (addressMode)
            {
                case TextureAddressMode.Wrap:
                    return VkSamplerAddressMode.Repeat;
                case TextureAddressMode.Border:
                    return VkSamplerAddressMode.ClampToBorder;
                case TextureAddressMode.Clamp:
                    return VkSamplerAddressMode.ClampToEdge;
                case TextureAddressMode.Mirror:

View on GitHub (pinned to 96fad776d2)