IOSurface-based MTLTexture gets corrupted

I have this code to create an IOSurface from a bitmap image:

    auto src = loadSource32f(); // rgba 32-bit float image
    const auto desc = src->getDescriptor(); // metadata for that image

    auto pixelFmt = CGMTLBufferManager::getCVPixelFormat( desc.channelBitDepth, desc.channelOrder ); // returns proper `RGfA`
    int width = static_cast<int>( desc.width );
    int height = static_cast<int>( desc.height );
    int trowbytes = static_cast<int>( desc.trueRowbytes() ); // returns proper rowbytes value

    CFMutableDictionaryRef properties = CFDictionaryCreateMutable(
        kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
    CFDictionarySetValue(
        properties, kIOSurfaceWidth, CFNumberCreate( kCFAllocatorDefault, kCFNumberIntType, &width ) );
    CFDictionarySetValue(
        properties, kIOSurfaceHeight, CFNumberCreate( kCFAllocatorDefault, kCFNumberIntType, &height ) );
    CFDictionarySetValue(
        properties, kIOSurfacePixelFormat, CFNumberCreate( kCFAllocatorDefault, kCFNumberIntType, &pixelFmt ) );
    CFDictionarySetValue(
        properties, kIOSurfaceBytesPerRow, CFNumberCreate( kCFAllocatorDefault, kCFNumberIntType, &trowbytes ) );

    NSDictionary *nsprops = ( __bridge NSDictionary * )properties;

    IOSurface *oSurface = [[IOSurface alloc] initWithProperties:nsprops];

    CFRelease( properties );

    ASSERT_TRUE( oSurface );

    auto ioSurface = (IOSurfaceRef) oSurface;

I tested that the pixels are properly written into the iosurface:

    // copy data to surface
    memcpy([oSurface baseAddress], src->getRawPtr(), src->getSizeInBytes());

    auto surfPtr = (uint8_t*)[oSurface baseAddress];

    // extract raw surface data and write it into a file
    saveOutputRaw(desc, surfPtr, getFileName("IOSurfaceTestSurfaceRaw"));

And I see this:

Now I want to create a MTLTexture based on the iosurface:

    // create texture
    auto fmt = IOSurfaceGetPixelFormat( ioSurface );
    auto w = IOSurfaceGetWidth( ioSurface );
    auto h = IOSurfaceGetHeight( ioSurface );
    auto rowbytes = IOSurfaceGetBytesPerRow( ioSurface );

    MTLTextureDescriptor *textureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:CGMTLBufferManager::getMTLPixelFormat( fmt )
                                                                                                 width:w
                                                                                                height:h
                                                                                             mipmapped:NO];
    textureDescriptor.usage = MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite;
    textureDescriptor.storageMode = MTLStorageModeShared;

    auto device = MTLCreateSystemDefaultDevice();

    id<MTLTexture> surfaceTex = [device newTextureWithDescriptor:textureDescriptor iosurface:ioSurface plane:0];

And now I want to test this:

    auto region = MTLRegionMake2D(0, 0, w, h);
    auto bufSize = [oSurface allocationSize];

    // get texture bytes
    auto outBuf2 = std::vector<uint8_t>(bufSize);
    [surfaceTex getBytes:outBuf2.data()
             bytesPerRow:rowbytes
              fromRegion:region
             mipmapLevel:0];
     // save to file
    saveOutputRaw(desc, outBuf2.data(), getFileName("IOSurfaceTestCreateTex"));

    // get bytes
    saveOutputRaw(desc, surfPtr, getFileName("IOSurfaceTestCreateRaw"));

And I get this result:

I also tried replaceRegion and blitEncoder copyFromTexture: toTexture: as well as managed texture with syncing, but the result is always the same - only the first 22 pixels get filled and the rest is transparent.

I have no idea what I'm missing. Please help.