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

  1. Create a Texture and copy from the Buffer into it via staging texture upload instead of direct Buffer-to-Texture copy
  2. Use buffer-to-buffer copy into a buffer-backed resource, or texture-to-texture for image data
  3. 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

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


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, &copy);

                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)