// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "gpu/command_buffer/service/graphite_utils.h"

#include "base/check.h"
#include "gpu/command_buffer/service/graphite_shared_context.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/core/SkPixmap.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/gpu/graphite/Context.h"
#include "third_party/skia/include/gpu/graphite/GraphiteTypes.h"
#include "third_party/skia/include/gpu/graphite/Surface.h"

namespace gpu {
namespace {

struct ReadPixelsContext {
  std::unique_ptr<const SkImage::AsyncReadResult> async_result;
  bool finished = false;
};

void OnReadPixelsDone(
    void* raw_ctx,
    std::unique_ptr<const SkImage::AsyncReadResult> async_result) {
  ReadPixelsContext* context = reinterpret_cast<ReadPixelsContext*>(raw_ctx);
  context->async_result = std::move(async_result);
  context->finished = true;
}

// Synchronously read pixels from a graphite image.
template <typename T>
bool GraphiteReadPixelsSyncImpl(GraphiteSharedContext* context,
                                skgpu::graphite::Recorder* recorder,
                                T* imageOrSurface,
                                const SkImageInfo& dst_info,
                                void* dst_pointer,
                                size_t dst_bytes_per_row,
                                int src_x,
                                int src_y) {
  if (!GraphiteFlush(context, recorder)) {
    return false;
  }

  ReadPixelsContext read_context;
  const SkIRect src_rect =
      SkIRect::MakeXYWH(src_x, src_x, dst_info.width(), dst_info.height());

  context->asyncRescaleAndReadPixelsAndSubmit(
      imageOrSurface, dst_info, src_rect, SkImage::RescaleGamma::kSrc,
      SkImage::RescaleMode::kRepeatedLinear, base::BindOnce(&OnReadPixelsDone),
      &read_context);

  CHECK(read_context.finished);

  if (!read_context.async_result) {
    return false;
  }

  SkPixmap pixmap(dst_info, read_context.async_result->data(0),
                  read_context.async_result->rowBytes(0));
  return pixmap.readPixels(dst_info, dst_pointer, dst_bytes_per_row);
}

}  // namespace

bool GraphiteFlush(GraphiteSharedContext* context,
                   skgpu::graphite::Recorder* recorder) {
  auto recording = recorder->snap();
  if (recording) {
    return context->insertRecording({recording.get()});
  }
  return true;
}

bool GraphiteFlushAndSubmit(GraphiteSharedContext* context,
                            skgpu::graphite::Recorder* recorder) {
  bool success = GraphiteFlush(context, recorder);
  // We submit any pending GPU work despite insertRecording failing since the
  // caller can expect any resources used to be eventually released when the
  // submitted work is done on the GPU.
  context->submit();
  return success;
}

bool GraphiteReadPixelsSync(GraphiteSharedContext* context,
                            skgpu::graphite::Recorder* recorder,
                            SkImage* image,
                            const SkImageInfo& dst_info,
                            void* dst_pointer,
                            size_t dst_bytes_per_row,
                            int src_x,
                            int src_y) {
  return GraphiteReadPixelsSyncImpl(context, recorder, image, dst_info,
                                    dst_pointer, dst_bytes_per_row, src_x,
                                    src_y);
}

bool GraphiteReadPixelsSync(GraphiteSharedContext* context,
                            skgpu::graphite::Recorder* recorder,
                            SkSurface* surface,
                            const SkImageInfo& dst_info,
                            void* dst_pointer,
                            size_t dst_bytes_per_row,
                            int src_x,
                            int src_y) {
  return GraphiteReadPixelsSyncImpl(context, recorder, surface, dst_info,
                                    dst_pointer, dst_bytes_per_row, src_x,
                                    src_y);
}

}  // namespace gpu
