blob: 995f7f0f2e764326469c82c396c450d7d64bb2c0 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_UI_LIB_ESCHER_RENDERER_BATCH_GPU_DOWNLOADER_H_
#define SRC_UI_LIB_ESCHER_RENDERER_BATCH_GPU_DOWNLOADER_H_
#include <lib/fit/function.h>
#include <variant>
#include "src/ui/lib/escher/escher.h"
#include "src/ui/lib/escher/renderer/buffer_cache.h"
#include "src/ui/lib/escher/renderer/frame.h"
#include "src/ui/lib/escher/third_party/granite/vk/command_buffer.h"
#include "src/ui/lib/escher/vk/buffer.h"
#include "src/ui/lib/escher/vk/command_buffer.h"
#include <vulkan/vulkan.hpp>
namespace escher {
// Provides host-accessible GPU memory for clients to download Images and
// Buffers from the GPU to host memory. Offers the ability to batch downloads
// into consolidated submissions to the GPU driver.
//
// About synchronization:
// We use semaphore (usually generated by ChainedSemaphoreGenerator) for
// synchronization between BatchGpuDownloader/Uploader and other gfx components,
// so we only need the barrier to synchronize all transfer-related commands.
//
// Currently users of BatchGpuDownloader should manually enforce that
// the BatchGpuDownloader waits on other BatchGpuUploader or gfx::Engine if they
// write to the images / buffers the BatchGpuDownloader reads from, by using
// AddWaitSemaphore() function. Also, Submit() function will return a semaphore
// being signaled when command buffer finishes execution, which can be used for
// synchronization.
//
// TODO(fxbug.dev/24401) Add memory barriers so the BatchGpuUploader and
// BatchGpuDownloader can handle synchronzation of reads and writes on the same
// Resource.
//
class BatchGpuDownloader {
public:
// Called after download is complete, with a pointer into the downloaded data.
using Callback = fit::function<void(const void* host_ptr, size_t size)>;
public:
static std::unique_ptr<BatchGpuDownloader> New(
EscherWeakPtr weak_escher,
CommandBuffer::Type command_buffer_type = CommandBuffer::Type::kTransfer,
uint64_t frame_trace_number = 0);
BatchGpuDownloader(EscherWeakPtr weak_escher,
CommandBuffer::Type command_buffer_type = CommandBuffer::Type::kTransfer,
uint64_t frame_trace_number = 0);
~BatchGpuDownloader();
// Returns true if the BatchGpuDownloader has work to do on the GPU.
bool HasContentToDownload() const { return !copy_info_records_.empty(); }
// Returns true if BatchGpuDownloader needs a command buffer, i.e. it needs to
// downloading images/buffers, or it needs to wait on/signal semaphores.
bool NeedsCommandBuffer() const {
return HasContentToDownload() || !wait_semaphores_.empty() || !signal_semaphores_.empty();
}
// Schedule a buffer-to-buffer copy that will be submitted when Submit()
// is called. Retains a reference to the source until the submission's
// CommandBuffer is retired.
//
// |source_offset| is the starting offset in bytes from the start of the
// source buffer.
// |copy_size| is the size to be copied to the buffer.
//
// These arguments are used to build VkBufferCopy struct. See the Vulkan
// specs of VkBufferCopy for more details. If copy_size is set to 0 by
// default, it copies all the contents from source.
void ScheduleReadBuffer(const BufferPtr& source, Callback callback,
vk::DeviceSize source_offset = 0U, vk::DeviceSize copy_size = 0U);
// Schedule a image-to-buffer copy that will be submitted when Submit()
// is called. Retains a reference to the source until the submission's
// CommandBuffer is retired.
//
// |region| specifies the buffer region which will be copied to the target
// image.
// |region.bufferOffset| should be set to zero since target buffer is
// managed internally by the uploader; currently |imageOffset| requires to
// be zero and |imageExtent| requires to be the whole image.
// The default value of |region| is vk::BufferImageCopy(), in which case we
// create a default copy region which reads the color data from the whole
// image with only one mipmap layer.
//
// |write_function| is a callback function which will be called at
// GenerateCommands(), where we copy our data to the host-visible buffer.
void ScheduleReadImage(const ImagePtr& source, Callback callback,
vk::BufferImageCopy region = vk::BufferImageCopy());
// Submits all pending work to the given CommandBuffer. Users need to call
// cmds->Submit() after calling this function. Returns a lambda function
// which calls all the callback functions we passed in to ScheduleReadBuffer
// and ScheduleReadImage functions; this function should be called after the
// command buffer has finished execution, typically by passing it to
// cmds->Submit().
//
// After this function is called, all the contents in |copy_info_records|,
// staged |resources_| and semaphores will be moved to the lambda function
// and the downloader will be cleaned and ready for reuse.
//
// The argument |cmds| cannot be nullptr if there is any pending work,
// including writing to buffer/images and waiting on/signaling semaphores.
CommandBufferFinishedCallback GenerateCommands(CommandBuffer* cmds);
// Submits all pending work to the GPU.
// The function |callback|, and all callback functions the user passed to
// ScheduleReadBuffer and ScheduleReadImage, will be called after all work
// is done.
//
// After this function is called, all the contents in |copy_info_records|,
// staged |resources_| and semaphores will be moved to the lambda function
// and the downloader will be cleaned and ready for reuse.
void Submit(CommandBufferFinishedCallback client_callback = nullptr);
// Submit() and GenerateCommands() will wait on all semaphores added by
// AddWaitSemaphore().
void AddWaitSemaphore(SemaphorePtr sema, vk::PipelineStageFlags flags) {
wait_semaphores_.push_back({std::move(sema), flags});
}
// Submit() and GenerateCommands() will signals all semaphores added by
// AddSignalSemaphore().
void AddSignalSemaphore(SemaphorePtr sema) { signal_semaphores_.push_back(std::move(sema)); }
private:
enum class CopyType { COPY_IMAGE = 0, COPY_BUFFER = 1 };
struct ImageCopyInfo {
ImagePtr source;
vk::BufferImageCopy region;
};
struct BufferCopyInfo {
BufferPtr source;
vk::BufferCopy region;
};
using CopyInfoVariant = std::variant<ImageCopyInfo, BufferCopyInfo>;
struct CopyInfo {
CopyType type;
vk::DeviceSize offset;
vk::DeviceSize size;
Callback callback;
// copy_info can either be a ImageCopyInfo or a BufferCopyInfo.
CopyInfoVariant copy_info;
};
EscherWeakPtr escher_;
CommandBuffer::Type command_buffer_type_ = CommandBuffer::Type::kTransfer;
// The trace number for the frame. Cached to support lazy frame creation.
const uint64_t frame_trace_number_;
BufferCacheWeakPtr buffer_cache_;
vk::DeviceSize current_offset_ = 0U;
std::vector<CopyInfo> copy_info_records_;
std::vector<ResourcePtr> resources_;
std::vector<std::pair<SemaphorePtr, vk::PipelineStageFlags>> wait_semaphores_;
std::vector<SemaphorePtr> signal_semaphores_;
FXL_DISALLOW_COPY_AND_ASSIGN(BatchGpuDownloader);
};
} // namespace escher
#endif // SRC_UI_LIB_ESCHER_RENDERER_BATCH_GPU_DOWNLOADER_H_