stride3d/stride · error · InvalidOperationException
Cannot copy data between Buffers and Textures.
Error message
Cannot copy data between Buffers and Textures.
What it means
The Vulkan Copy implementation supports buffer-to-buffer and texture-to-texture copies only. If neither operand pair matches (one is a Buffer and the other a Texture), Stride throws InvalidOperationException because Vulkan requires vkCmdCopyBuffer vs vkCmdCopyImage and there is no implemented cross-type path.
Solutions
- Create a Texture and copy from the Buffer into it via staging texture upload instead of direct Buffer-to-Texture copy
- Use buffer-to-buffer copy into a buffer-backed resource, or texture-to-texture for image data
- Use MappedResource/MapSubResource to fill a Texture directly from CPU memory
Example fix
// before cmdList.Copy(myBuffer, myTexture); // mixed types // after var staging = Texture.New2D(device, w, h, fmt, data, GraphicsResourceUsage.Staging); cmdList.Copy(staging, myTexture);
Defensive patterns
Strategy: type-guard
Validate before calling
if ((source is Buffer) != (destination is Buffer)) throw new ArgumentException("Copy requires both Buffer or both Texture"); Type guard
bool IsSameKind(GraphicsResource a, GraphicsResource b) => (a is Buffer) == (b is Buffer);
Try / catch
try { cmdList.Copy(a, b); } catch (InvalidOperationException ex) when (ex.Message.Contains("Buffers and Textures")) { /* route to proper upload path */ } Prevention
- Never pass mixed Buffer/Texture pairs to Copy
- Upload buffer content into textures via staging textures
- Keep upload helpers strongly typed (Texture overload vs Buffer overload)
When it happens
Trigger: Calling CommandList.Copy passing one Buffer and one Texture (in either order), or any combination where source/destination are not both buffers or both textures.
Common situations: Confusing Buffer and Texture APIs when uploading data; trying to interpret a buffer as a texture without creating a Texture resource; accidental overload selection with mixed types.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
Related errors
- Cannot copy data between GraphicsResources of types
- Unexpected arguments
- This tool requires a build path.
- Type [ ] must be assignable to Asset or be a Package
- Type [ ] must be assignable to Asset
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/f6b1a3baad3155ab.
Report an issue: GitHub.
Appendix: source
Thrown at sources/engine/Stride.Graphics/Vulkan/CommandList.Vulkan.cs:1342
var copy = new VkBufferCopy { srcOffset = srcOffset, dstOffset = dstOffset, size = size };
GraphicsDevice.NativeDeviceApi.vkCmdCopyBuffer(currentCommandList.NativeCommandBuffer, sourceBuffer.NativeBuffer, destinationBuffer.NativeBuffer, regionCount: 1, ©);
if (destinationBuffer.Usage == GraphicsResourceUsage.Staging)
{
destinationBuffer.CommandListFenceValue = null;
destinationBuffer.UpdatingCommandList = this;
currentCommandList.StagingResources.Add(destinationBuffer);
}
bufferBarriers[0] = new VkBufferMemoryBarrier(sourceBuffer.NativeBuffer, VkAccessFlags.TransferRead, sourceBuffer.NativeAccessMask);
bufferBarriers[1] = new VkBufferMemoryBarrier(destinationBuffer.NativeBuffer, VkAccessFlags.TransferWrite, destinationBuffer.NativeAccessMask);
var dstStages = FixStagesForAccess(sourceBuffer.NativePipelineStageMask | destinationBuffer.NativePipelineStageMask, sourceBuffer.NativeAccessMask | destinationBuffer.NativeAccessMask);
GraphicsDevice.NativeDeviceApi.vkCmdPipelineBarrier(currentCommandList.NativeCommandBuffer, VkPipelineStageFlags.Transfer, dstStages, VkDependencyFlags.None, memoryBarrierCount: 0, memoryBarriers: null, bufferMemoryBarrierCount: 2, bufferBarriers, imageMemoryBarrierCount: 0, imageMemoryBarriers: null);
}
else
{
throw new InvalidOperationException("Cannot copy data between Buffers and Textures.");
}
}
/// <inheritdoc />
public void CopyCount(Buffer sourceBuffer, Buffer destBuffer, int offsetInBytes)
{
throw new NotImplementedException();
}
/// <summary>
/// Copies data from memory to a sub-resource created in non-mappable memory.
/// </summary>
/// <param name="resource">The destination Graphics Resource to copy data to.</param>
/// <param name="subResourceIndex">The sub-resource index of <paramref name="resource"/> to copy data to.</param>
/// <param name="sourceData">The source data in CPU memory to copy.</param>
/// <exception cref="ArgumentNullException"><paramref name="resource"/> is <see langword="null"/>.</exception>
/// <remarks>
/// <para>View on GitHub (pinned to 96fad776d2)