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
- Ensure source and destination textures are created with identical Width, Height, Depth, ArraySize and MipLevelCount
- Use CopyScissor (region copy) for differently sized textures instead of the full-texture Copy
- 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
- Create destination textures via src.Description clones
- Prefer CopyScissor for resizing/scaling needs
- Assert texture equality in debug builds before copy
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
- AHardwareBuffer import requires…
- argumentsBuffer
- Bound render targets
- failed
- Cannot copy data between Buffers and Textures.
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);
}
elseView on GitHub (pinned to 96fad776d2)