Here is what is not efficient, ever: getting a GPU texture into a raster format CPU accessible buffer. At best this requires synchronization and a full memory copy into a new buffer, but it can easily be much much more expensive because GPUs, and mobile GPUs basically never, store textures in raster formats. So now you need a full copy and a bunch of extra SIMD to convert it from the GPU preferred format into raster format for your getImageData call.
And all this for what is essentially a trap API! Chances are if you are calling getImageData regularly, you are doing something wrong and inefficient.