stride3d/stride · error · InvalidOperationException

and textures don't match

Error message

{nameof(source)} and {nameof(destination)} textures don't match

What it means

Copy of a full texture resource on Vulkan requires source and destination textures to have identical dimensions, array size, and mip level count. Stride validates this and throws InvalidOperationException when they diverge, since vkCmdCopyImage cannot reconcile differing extents.

Solutions

  1. Ensure source and destination textures are created with identical Width, Height, Depth, ArraySize and MipLevelCount
  2. Use CopyScissor (region copy) for differently sized textures instead of the full-texture Copy
  3. Recreate the destination texture to match the source description before copying

Example fix

// before
cmdList.Copy(oldTexture, resizedTexture); // sizes differ
// after
var newTex = Texture.New2D(device, oldTexture.Width, oldTexture.Height, oldTexture.Format);
cmdList.Copy(oldTexture, newTex);
Defensive patterns

Strategy: validation

Validate before calling

bool canCopy = src.Width == dst.Width && src.Height == dst.Height && src.Depth == dst.Depth && src.ArraySize == dst.ArraySize && src.MipLevelCount == dst.MipLevelCount;
if (!canCopy) useRegionCopyInstead();

Type guard

bool IsCopyCompatible(Texture s, Texture d) => s.Width==d.Width && s.Height==d.Height && s.Depth==d.Depth && s.ArraySize==d.ArraySize && s.MipLevelCount==d.MipLevelCount;

Try / catch

try { cmdList.Copy(src, dst); } catch (InvalidOperationException ex) { /* recreate dst to match src */ }

Prevention

When it happens

Trigger: Calling CommandList.Copy(Texture source, Texture destination) (full-resource copy) where Width, Height, Depth, ArraySize, or MipLevelCount differ between the two textures.

Common situations: Copying between a render target and a staging texture allocated with different mip counts or array layers; resizing a texture and keeping a stale destination; downscaling via Copy instead of a blit.

Related errors


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

Appendix: source

Thrown at sources/engine/Stride.Graphics/Vulkan/CommandList.Vulkan.cs:958

            GraphicsDevice.NativeDeviceApi.vkCmdClearColorImage(currentCommandList.NativeCommandBuffer, texture.NativeImage, VkImageLayout.TransferDstOptimal, &clearValue, 1, &clearRange);

            ResourceBarrierTransition(texture, BarrierLayout.UnorderedAccess);
        }

        public unsafe void Copy(GraphicsResource source, GraphicsResource destination)
        {
            RecordDebugCounter(DebugCounterKind.Copy);
            // TODO VULKAN: One copy per mip level

            if (source is Texture sourceTexture && destination is Texture destinationTexture)
            {
                if (sourceTexture.Width != destinationTexture.Width ||
                    sourceTexture.Height != destinationTexture.Height ||
                    sourceTexture.Depth != destinationTexture.Depth ||
                    sourceTexture.ArraySize != destinationTexture.ArraySize ||
                    sourceTexture.MipLevelCount != destinationTexture.MipLevelCount)
                    throw new InvalidOperationException($"{nameof(source)} and {nameof(destination)} textures don't match");

                CleanupRenderPass();

                var imageBarriers = stackalloc VkImageMemoryBarrier[2];
                var bufferBarriers = stackalloc VkBufferMemoryBarrier[2];

                var sourceParent = sourceTexture.ParentTexture ?? sourceTexture;
                var destinationParent = destinationTexture.ParentTexture ?? destinationTexture;

                uint bufferBarrierCount = 0;
                uint imageBarrierCount = 0;

                // Initial barriers
                if (sourceTexture.Usage == GraphicsResourceUsage.Staging)
                {
                    bufferBarriers[bufferBarrierCount++] = new VkBufferMemoryBarrier(sourceParent.NativeBuffer, sourceTexture.NativeAccessMask, VkAccessFlags.TransferRead);
                }
                else

View on GitHub (pinned to 96fad776d2)