blob: a03780a7c04a240c5892779584c88b8e1eda1c65 [file] [log] [blame]
// Copyright 2017 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.
#include "src/ui/scenic/lib/gfx/resources/image_pipe.h"
#include <lib/trace/event.h>
#include "src/ui/lib/escher/flib/fence.h"
#include "src/ui/scenic/lib/gfx/engine/session.h"
#include "src/ui/scenic/lib/gfx/resources/memory.h"
#include "src/ui/scenic/lib/gfx/util/time.h"
namespace scenic_impl {
namespace gfx {
const ResourceTypeInfo ImagePipe::kTypeInfo = {ResourceType::kImagePipe | ResourceType::kImageBase,
"ImagePipe"};
ImagePipe::ImagePipe(Session* session, ResourceId id,
std::shared_ptr<ImagePipeUpdater> image_pipe_updater,
std::shared_ptr<ErrorReporter> error_reporter)
: ImagePipeBase(session, id, ImagePipe::kTypeInfo),
image_pipe_updater_(std::move(image_pipe_updater)),
error_reporter_(std::move(error_reporter)),
weak_ptr_factory_(this) {
FX_CHECK(error_reporter_);
}
ImagePipe::ImagePipe(Session* session, ResourceId id,
fidl::InterfaceRequest<fuchsia::images::ImagePipe> request,
std::shared_ptr<ImagePipeUpdater> image_pipe_updater,
std::shared_ptr<ErrorReporter> error_reporter)
: ImagePipeBase(session, id, ImagePipe::kTypeInfo),
handler_(std::make_unique<ImagePipeHandler>(std::move(request), this)),
image_pipe_updater_(std::move(image_pipe_updater)),
error_reporter_(std::move(error_reporter)),
weak_ptr_factory_(this) {
FX_CHECK(error_reporter_);
}
void ImagePipe::AddImage(uint32_t image_id, fuchsia::images::ImageInfo image_info, zx::vmo vmo,
uint64_t offset_bytes, uint64_t size_bytes,
fuchsia::images::MemoryType memory_type) {
if (image_id == 0) {
error_reporter_->ERROR() << "ImagePipe::AddImage: Image can not be assigned an ID of 0.";
CloseConnectionAndCleanUp();
return;
}
uint64_t vmo_size;
auto status = vmo.get_size(&vmo_size);
if (status != ZX_OK) {
error_reporter_->ERROR() << "ImagePipe::AddImage(): zx_vmo_get_size failed (err=" << status
<< ").";
CloseConnectionAndCleanUp();
return;
}
::fuchsia::ui::gfx::MemoryArgs memory_args;
memory_args.memory_type = memory_type;
memory_args.vmo = std::move(vmo);
memory_args.allocation_size = vmo_size;
MemoryPtr memory =
Memory::New(session_DEPRECATED(), 0u, std::move(memory_args), error_reporter_.get());
if (!memory) {
error_reporter_->ERROR() << "ImagePipe::AddImage: Unable to create a memory object.";
CloseConnectionAndCleanUp();
return;
}
auto image = CreateImage(session_DEPRECATED(), image_id, memory, image_info, offset_bytes);
auto result = images_.insert({image_id, std::move(image)});
if (!result.second) {
error_reporter_->ERROR() << "ImagePipe::AddImage(): resource with ID " << image_id
<< " already exists.";
CloseConnectionAndCleanUp();
return;
}
};
void ImagePipe::CloseConnectionAndCleanUp() {
handler_.reset();
is_valid_ = false;
frames_ = {};
images_.clear();
// Schedule a new frame.
FX_DCHECK(image_pipe_updater_);
image_pipe_updater_->ScheduleImagePipeUpdate(zx::time(0), fxl::WeakPtr<ImagePipeBase>(),
/*acquire_fences*/ {}, /*release_fences*/ {},
/*callback*/ [](auto...) {});
}
void ImagePipe::OnConnectionError() { CloseConnectionAndCleanUp(); }
ImagePtr ImagePipe::CreateImage(Session* session, ResourceId id, MemoryPtr memory,
const fuchsia::images::ImageInfo& image_info,
uint64_t memory_offset) {
return Image::New(session, id, memory, image_info, memory_offset, error_reporter_.get());
}
void ImagePipe::RemoveImage(uint32_t image_id) {
TRACE_DURATION("gfx", "ImagePipe::RemoveImage", "image_id", image_id);
size_t erased_count = images_.erase(image_id);
if (erased_count == 0) {
error_reporter_->ERROR() << "ImagePipe::RemoveImage(): Could not find image with id="
<< image_id << ".";
CloseConnectionAndCleanUp();
}
};
scheduling::PresentId ImagePipe::PresentImage(
uint32_t image_id, zx::time presentation_time, std::vector<zx::event> acquire_fences,
std::vector<zx::event> release_fences,
fuchsia::images::ImagePipe::PresentImageCallback callback) {
TRACE_DURATION("gfx", "ImagePipe::PresentImage", "image_id", image_id);
TRACE_FLOW_END("gfx", "image_pipe_present_image", image_id);
if (!frames_.empty() && presentation_time < frames_.back().presentation_time) {
error_reporter_->ERROR()
<< "ImagePipe: Present called with out-of-order presentation time. presentation_time="
<< presentation_time
<< ", last scheduled presentation time=" << frames_.back().presentation_time;
CloseConnectionAndCleanUp();
return scheduling::kInvalidPresentId;
}
// Verify that image_id is valid.
auto image_it = images_.find(image_id);
if (image_it == images_.end()) {
error_reporter_->ERROR() << "ImagePipe::PresentImage could not find Image with ID: "
<< image_id;
CloseConnectionAndCleanUp();
return scheduling::kInvalidPresentId;
}
const auto present_id = image_pipe_updater_->ScheduleImagePipeUpdate(
presentation_time, weak_ptr_factory_.GetWeakPtr(), std::move(acquire_fences),
std::move(release_fences), std::move(callback));
frames_.push({.present_id = present_id,
.image = image_it->second,
.presentation_time = presentation_time});
return present_id;
};
ImagePipeUpdateResults ImagePipe::Update(scheduling::PresentId present_id) {
ImagePipeUpdateResults results{.image_updated = false};
bool present_next_image = false;
ResourceId next_image_id = current_image_id_;
ImagePtr next_image = nullptr;
while (!frames_.empty() && frames_.front().present_id <= present_id) {
if (next_image) {
// We're skipping a frame, so we should also mark the image as dirty, in
// case the producer updates the pixels in the buffer between now and a
// future present call.
next_image->MarkAsDirty();
}
next_image = frames_.front().image;
FX_DCHECK(next_image);
next_image_id = next_image->id();
frames_.pop();
present_next_image = true;
}
if (!present_next_image) {
results.image_updated = false;
return results;
}
// TODO(fxbug.dev/23406): This code, and the code below that marks an image as dirty,
// assumes that the same image cannot be presented twice in a row on the same
// image pipe, while also requiring a call to UpdatePixels(). If not, this
// needs a new test.
if (next_image_id == current_image_id_) {
// This ImagePipe did not change since the last frame was rendered.
results.image_updated = false;
return results;
}
current_image_id_ = next_image_id;
// TODO(fxbug.dev/24223): Determine proper signaling for marking images as dirty.
// For now, mark all released images as dirty, with the assumption that the
// client will likely write into the buffer before submitting it again.
if (current_image_) {
current_image_->MarkAsDirty();
}
current_image_ = std::move(next_image);
results.image_updated = true;
return results;
}
void ImagePipe::UpdateEscherImage(escher::BatchGpuUploader* gpu_uploader,
escher::ImageLayoutUpdater* layout_updater) {
// Update current escher Image.
if (current_image_) {
current_image_->UpdateEscherImage(gpu_uploader, layout_updater);
}
}
const escher::ImagePtr& ImagePipe::GetEscherImage() {
if (current_image_) {
return current_image_->GetEscherImage();
}
static const escher::ImagePtr kNullEscherImage;
return kNullEscherImage;
}
} // namespace gfx
} // namespace scenic_impl