chromium/testing/libfuzzer/fuzzers/command_buffer_lpm_fuzzer/cmd_buf_lpm_fuzz.h

// 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.

#ifndef TESTING_LIBFUZZER_FUZZERS_COMMAND_BUFFER_LPM_FUZZER_CMD_BUF_LPM_FUZZ_H_
#define TESTING_LIBFUZZER_FUZZERS_COMMAND_BUFFER_LPM_FUZZER_CMD_BUF_LPM_FUZZ_H_

#include <dawn/native/DawnNative.h>
#include <dawn/webgpu_cpp.h>
#include <dawn/wire/WireServer.h>

#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/synchronization/lock.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/test_simple_task_runner.h"
#include "base/test/test_suite.h"
#include "build/build_config.h"
#include "components/viz/test/test_gpu_service_holder.h"
#include "gpu/command_buffer/client/cmd_buffer_helper.h"
#include "gpu/command_buffer/client/webgpu_cmd_helper.h"
#include "gpu/command_buffer/client/webgpu_implementation.h"
#include "gpu/command_buffer/service/webgpu_decoder.h"
#include "gpu/config/gpu_preferences.h"
#include "gpu/ipc/webgpu_in_process_context.h"
#include "ui/gl/buildflags.h"

#include "testing/libfuzzer/fuzzers/command_buffer_lpm_fuzzer/cmd_buf_lpm_fuzz.pb.h"
#include "testing/libfuzzer/fuzzers/command_buffer_lpm_fuzzer/webgpu_support.h"

namespace gpu::cmdbuf::fuzzing {

const uint32_t kCommandBufferSize = 16384;
const uint32_t kTransferBufferSize = 16384;
const uint32_t kSmallTransferBufferSize = 16;
const uint32_t kTinyTransferBufferSize = 3;

const base::TimeDelta kTinyTimeout = base::Milliseconds(100);

struct Config {};

class CmdBufFuzz : public base::TestSuite {
 public:
  CmdBufFuzz();
  ~CmdBufFuzz() override;
  void CommandLineInit();
  void GfxInit();

  void RuntimeInit();
  bool Reset();

  // CommandBuffer ops
  void RunCommandBuffer(cmdbuf::fuzzing::CmdBufSession);
  bool CmdBufReset();
  gpu::SyncToken SyncTokenFromProto(cmdbuf::fuzzing::SyncToken token_proto);
  void SignalSyncTokenCallback();

  // WebGPU ops
  void WebGPURequestAdapter();
  void WebGPURequestDevice();
  void WebGPUDestroyDevice();
  void WebGPUCreateBuffer();
  void WebGPUDestroyBuffer();
  void WebGPUReset();

  inline gpu::webgpu::WebGPUImplementation* webgpu() {
    return webgpu_context_->GetImplementation();
  }

  webgpu::WebGPUDecoder* GetDecoder() const {
    return webgpu_context_->GetCommandBufferForTest()
        ->GetWebGPUDecoderForTest();
  }

  void RunPendingTasks() {
    webgpu_context_->GetTaskRunner()->RunPendingTasks();
    gpu_service_holder_->ScheduleGpuMainTask(base::BindOnce(
        [](webgpu::WebGPUDecoder* decoder) {
          if (decoder->HasPollingWork()) {
            LOG(INFO) << "GPU has waiting work";
            decoder->PerformPollingWork();
          }
        },
        GetDecoder()));
  }

  void PollUntilIdle() {
    webgpu()->FlushCommands();
    base::WaitableEvent wait;
    gpu_service_holder_->ScheduleGpuMainTask(base::BindOnce(
        [](viz::TestGpuServiceHolder* service, base::WaitableEvent* wait,
           webgpu::WebGPUDecoder* decoder) {
          service->gpu_main_thread_task_runner()->RunsTasksInCurrentSequence();
          while (decoder->HasPollingWork()) {
            base::PlatformThread::Sleep(kTinyTimeout);
            decoder->PerformPollingWork();
          }
          wait->Signal();
        },
        gpu_service_holder_.get(), &wait, GetDecoder()));
    wait.Wait();
    webgpu_context_->GetTaskRunner()->RunPendingTasks();
  }

  void PostTaskToGpuThread(base::OnceClosure callback) {
    gpu_service_holder_->ScheduleGpuMainTask(std::move(callback));
  }

  void WaitForCompletion(wgpu::Instance instance, wgpu::Device device) {
    // Wait for any work submitted to the queue to be finished. The guarantees
    // of Dawn are that all previous operations will have been completed and
    // more importantly the callbacks will have been called.
    wgpu::Queue queue = device.GetQueue();
    wgpu::FutureWaitInfo wait_info = {queue.OnSubmittedWorkDone(
        wgpu::CallbackMode::WaitAnyOnly, [](wgpu::QueueWorkDoneStatus) {})};

    while (!wait_info.completed) {
      instance.WaitAny(1, &wait_info, 0u);
      webgpu()->FlushCommands();
      RunPendingTasks();
      base::PlatformThread::Sleep(kTinyTimeout);
    }
  }

 private:
  GpuPreferences gpu_preferences_;
  std::unique_ptr<viz::TestGpuServiceHolder> gpu_service_holder_;
  std::unique_ptr<WebGPUInProcessContext> webgpu_context_;
  // std::unique_ptr<CommandBufferDirect> command_buffer_;
  raw_ptr<InProcessCommandBuffer> command_buffer_ = nullptr;
  std::unique_ptr<CommandBufferService> command_buffer_service_;
  raw_ptr<CommandBufferHelper> cmd_helper_ = nullptr;
  std::unique_ptr<webgpu::WebGPUCmdHelper> webgpu_cmd_helper_ = nullptr;
  // std::unique_ptr<gpu::CommandBufferProxyImpl> command_buffer_proxy_impl_;
  //  scoped_refptr<viz::ContextProviderCommandBuffer> provider_;
  wgpu::Instance webgpu_instance_;
  raw_ptr<webgpu::WebGPUDecoder> decoder_;
  wgpu::Adapter webgpu_adapter_;
  std::unique_ptr<gpu::TransferBuffer> transfer_buffer_;
  raw_ptr<gpu::webgpu::WebGPUImplementation> webgpu_impl_;
  std::unique_ptr<dawn::wire::WireServer> wire_server_;
  scoped_refptr<webgpu::APIChannel> wire_channel_;
  std::unique_ptr<dawn::native::Instance> dawn_instance_;
  std::unique_ptr<dawn::wire::WireServerDescriptor> wire_descriptor_;
  scoped_refptr<Buffer> buffer_;
  int32_t command_buffer_id_ = -1;
  std::unique_ptr<SharedMemoryLimits> shared_memory_limits_;
  raw_ptr<CommandBufferTaskExecutor> task_executor_;
  // scoped_refptr<SharedContextState> shared_context_state_;
  raw_ptr<webgpu::DawnWireServices> dawn_wire_services_;
  raw_ptr<webgpu::DawnClientSerializer> dawn_wire_serializer_;
  wgpu::Device webgpu_device_ = {};
  std::vector<wgpu::Buffer> wgpu_buffers_ = {};

  // Actions
  int action_index_ = 0;
  base::Lock cmd_buf_lock_;

  // TODO(bookholt): enable to support GL fuzzing
  // gpu::gles2::GLES2Interface* gl_interface_;
  // TODO(bookholt): this looks useful
  // raw_ptr<gpu::ContextSupport, DanglingUntriaged> context_support_;
  // TODO(bookohlt)
  // gpu::GpuChannelEstablishFactory* factory_;
  // TODO(bookholt)
  // scoped_refptr<gpu::GpuChannelHost> gpu_channel_host_;
};
}  // namespace gpu::cmdbuf::fuzzing

#endif  // TESTING_LIBFUZZER_FUZZERS_COMMAND_BUFFER_LPM_FUZZER_CMD_BUF_LPM_FUZZ_H_