Readiness of textures after upload?

Hi,


I've ran into a weird situation, on macOS, where a texture I uploaded seems to not be available for usage immediately after the calls are done to upload it are done.


At first I was using what I assume is the typical way (- [id<MTLTexture> replaceRegion:mipmapLevel:withBytes:bytesPerRow:), in a secondary thread, and whenever that call was returning, I was assuming the texture would then be available for the next frame. This seemed not to be the case, quite often.

After looking around a bit, I found another way to upload the texture data, which is by creating an MTLBuffer first, then blitting it onto the texture. This improves things quite a bit, as some textures are now available as I expect them to, but still others can't seem to make the cut. Being a blit operation, I would expect some dependency to be enforced, to make sure that the operation is finished before the next command encoder would try and use that texture.


So, either I'm missing some synchronization - but after searching for such information, I was left under the impression that it shouldn't be necessary. Or, I'm doing something really wrong that I can't pinpoint.

The trick here is that this is not a game, but an application displaying some UI overlay in Metal. Thus, I'm doing redraws only when needed, and not rendering continuously (which means the problems would be solved at some point, after rendering a few frames). I am already detecting when a texture is not yet fully created and uploaded (as far as I know, from either the replaceRegion: call returning, or the blit command having been committed), and redrawing if a frame was missing some, but even that seems to not be enough.


Any help appreciated here!

Replies

Hi Ittkettch,


what storage mode are you using for your MTLTexture?

I'm using MTLStorageModePrivate, as I don't need that data back ever on the CPU side.

Apple mentiones this special case of private textures in their API documentation (MTLTexture):


"This method immediately copies the pixel data into the texture. It does not synchronize against any GPU accesses to this texture. For example, if a command buffer includes read or write operations on a given texture, you must ensure that these operations complete before calling the

replace(region:mipmapLevel:withBytes:bytesPerRow:)
method on the given texture. You can use the
addCompletedHandler(_:)
method,
waitUntilCompleted()
method, or custom semaphores to signal that a command buffer has completed execution.

If the texture image has a compressed pixel format, then only block-aligned regions can be written. If the size of a dimension of

region
is not a multiple of the block size, then
bytesPerRow
in system memory must include the full edge block, and the size of that dimension less than or equal to the texture dimensions..

This method is supported if you're copying to an entire texture with a PowerVR Texture Compression (PVRTC) pixel format; in which case,

bytesPerRow
and
bytesPerImage
must both be set to 0. This method isn't supported for copying to a subregion of a texture that has a PVRTC pixel format.

For all other pixel formats, compressed or uncompressed, set the

bytesPerRow
and
bytesPerImage
to 0 if they are not applicable.

Don't use this method for textures with a private storage mode. To copy data to a private texture, copy that data to a texture that has a non-private storage mode and then use a

MTLBlitCommandEncoder
to copy the data to the private texture."

Thanks, I'll be trying that! I guess that's more or less the same as what I tried to do (using an intermediate buffer and doing a blit), but maybe my buffer was Private as well, thus only moving the problem...

Well that's quite weird. I thought they were in Private, but I'm actually using this storage mode only for render targets. All the textures that are uploaded are in fact in Managed mode, so they should not have this exception...