message: Vulkan: make ReadPixels synchronous.
  This makes it so that the PixelBufferDescriptor callback triggers at a
  time consistent with other backends.
  ReadPixels is still asynchronous in the sense that the callback is
  triggered on the main thread. However, it is now guaranteed to trigger
  during (or before) flushAndWait(), which is less surprising behavior.
diff: @@ -1420,17 +1420,10 @@ void VulkanDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y
  vkCmdPipelineBarrier(mContext.work.cmdbuffer, VK_PIPELINE_STAGE_TRANSFER_BIT,
  VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
  - flushWorkCommandBuffer(mContext);
  -
  - // Create a closure-friendly pointer that holds the rvalue reference.
  -
  - PixelBufferDescriptor* closure = new PixelBufferDescriptor();
  - *closure = std::move(pbd);
  + // Flush and wait.
  - // Create a disposable to defer execution of the following code until after
  - // the work command buffer has completed.
  -
  - mDisposer.createDisposable((VulkanDisposer::Key) stagingImage, [=] () {
  + flushWorkCommandBuffer(mContext);
  + acquireWorkCommandBuffer(mContext);
  VkImageSubresource subResource { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT };
  VkSubresourceLayout subResourceLayout;
  @@ -1442,9 +1435,12 @@ void VulkanDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y
  vkMapMemory(device, stagingMemory, 0, VK_WHOLE_SIZE, 0, (void**) &srcPixels);
  srcPixels += subResourceLayout.offset;
  - // TODO: investigate why this Y-flip exists.
  - constexpr bool flipY = true;
  - if (!DataReshaper::reshapeImage(closure, getComponentType(srcFormat), srcPixels,
  + // TODO: investigate why this Y-flip exists. This conditional seems to work with both
  + // test_ReadPixels.cpp (readpixels from a normal render target with texture attachment) and
  + // viewer_basic_test.cc (readpixels from an offscreen swap chain)
  + const bool flipY = srcTexture ? true : false;
  +
  + if (!DataReshaper::reshapeImage(&pbd, getComponentType(srcFormat), srcPixels,
  subResourceLayout.rowPitch, width, height, swizzle, flipY)) {
  utils::slog.e << "Unsupported PixelDataFormat or PixelDataType" << utils::io::endl;
  }
  @@ -1453,13 +1449,7 @@ void VulkanDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y
  vkFreeMemory(device, stagingMemory, nullptr);
  vkDestroyImage(device, stagingImage, nullptr);
  - scheduleDestroy(std::move(*closure));
  - delete closure;
  - });
  -
  - // Next we reduce the ref count of the image to zero, which schedules the above callback to be
  - // executed on the next beginFrame(), after the work command buffer is completed.
  - mDisposer.removeReference((VulkanDisposer::Key) stagingImage);
  + scheduleDestroy(std::move(pbd));
  }
  void VulkanDriver::readStreamPixels(Handle<HwStream> sh, uint32_t x, uint32_t y, uint32_t width,
  