veldrid: [Android/Vulkan] CopyTexture crashes the app

This code crashes when running on Android/Vulkan:

var src = factory.CreateTexture(TextureDescription.Texture2D(
    texSize, texSize, 1, 1, PixelFormat.R8_G8_B8_A8_UNorm, TextureUsage.Sampled));
var dst = factory.CreateTexture(TextureDescription.Texture2D(
    texSize, texSize, 1, 1, PixelFormat.R8_G8_B8_A8_UNorm, TextureUsage.Sampled));

var cl = factory.CreateCommandList();
cl.Begin();
cl.CopyTexture(src, dst);
cl.End();

device.SubmitCommands(cl);
device.WaitForIdle();

Only tested it on a Xiaomi Redmi 4X (Android 7.1). You can find a small repro project here.

image

About this issue

  • Original URL
  • State: open
  • Created 6 years ago
  • Comments: 34 (16 by maintainers)

Most upvoted comments

This is the code generated by Mono’s AOT compiler in release mode:

LDR     R1, [R0] ; vkCmdCopyImage
LDR     R0, [R11,#commandBuffer]
LDR     R2, [R11,#srcImage_lo]
LDR     R3, [R11,#srcImage_hi]
LDR     R12, [R11,#srcImageLayout]
STR     R12, [SP]
LDR     R12, [R11,#dstImage_lo]
STR     R12, [SP,#4]
LDR     R12, [R11,#dstImage_hi]
STR     R12, [SP,#8]
LDR     R12, [R11,#dstImageLayout]
STR     R12, [SP,#0xC]
LDR     R12, [R11,#regionCount]
STR     R12, [SP,#0x10]
LDR     R12, [R11,#pRegions]
STR     R12, [SP,#0x14]
BLX     R1

Essentially, this is what the registers and the stack look like when vkCmdCopyImage is called:

R0: commandBuffer
R1: points to the function
R2: srcImage (low word)
R3: srcImage (high word)

[SP + 0] srcImageLayout
[SP + 4] dstImage (low word)
[SP + 8] dstImage (high word)
[SP + C] dstImageLayout
[SP + 10] regionCount
[SP + 14] pRegions

And here’s the equivalent (except working) code generated by clang:

LDR.W       LR, [LR] ; vkCmdCopyImage
LDR         R2, [SP,#0x700+commandBuffer]
LDR         R3, [SP,#0x700+srcImage+4]
LDR.W       R12, [SP,#0x700+srcImage]
LDR.W       R4, [SP,#0x700+dstImage]
LDR.W       R5, [SP,#0x700+dstImage+4]
MOV         R6, SP
LDR.W       R8, [SP,#0x700+pRegions]
STR.W       R8, [R6,#0x18] ; pRegions
STR         R0, [R6,#0x14] ; regionCount
MOVS        R0, #7
STR         R0, [R6,#0x10] ; dstImageLayout
STR         R5, [R6,#0xC] ; dstImage (low word)
STR         R4, [R6,#8] ; dstImage (high word)
MOVS        R0, #6
STR         R0, [R6] ; srcImageLayout
MOV         R0, R2
MOV         R2, R12
BLX         LR

So the arguments are passed like this:

R0: commandBuffer
R1: unused?
R2: srcImage (low word)
R3: srcImage (high word)

[SP + 0] srcImageLayout
[SP + 8] dstImage (low word)
[SP + C] dstImage (high word)
[SP + 10] dstImageLayout
[SP + 14] regionCount
[SP + 18] pRegions

Notice where pRegions is stored. The location is different, and that’s because here we have a 4 byte gap between srcImageLayout and dstImage. I don’t have an explanation for this. Could be some sort of convention that Mono doesn’t respect, I don’t know. But the fact of the matter is, the implementation expects to find pRegions at [SP + 18], not [SP + 14].

I’ve already tried changing the function signature so that the arguments would end up at the corrent locations, and it worked. You can do that by adding an extra 4 byte parameter after srcImageLayout. Removing the extra parameter makes it crash again. I think this will only work in release mode, though.