Commit 88b0ba9d authored by Sergey Ulanov's avatar Sergey Ulanov Committed by Commit Bot

[Fuchsia, ozone] Implement ScenicSurfaceFactory

ScenicSurfaceFactory is a simple SurfaceFactory implementation for
the Scenic ozone platform that allows displaying 2D images on the
screen on Fuchsia.

Bug: 829980
Change-Id: Ic8326b4fcc000a2e435e63a6e6778d861f5f17a7
Reviewed-on: https://chromium-review.googlesource.com/1080980
Commit-Queue: Sergey Ulanov <sergeyu@chromium.org>
Reviewed-by: default avatarRobert Kroeger <rjkroege@chromium.org>
Reviewed-by: default avatarWez <wez@chromium.org>
Cr-Commit-Position: refs/heads/master@{#570296}
parent c4066dff
......@@ -16,6 +16,8 @@ source_set("scenic") {
"scenic_surface_factory.h",
"scenic_window.cc",
"scenic_window.h",
"scenic_window_canvas.cc",
"scenic_window_canvas.h",
"scenic_window_manager.cc",
"scenic_window_manager.h",
]
......
......@@ -45,7 +45,7 @@ class ScenicPlatformEventSource : public ui::PlatformEventSource {
// OzonePlatform for Scenic.
class OzonePlatformScenic : public OzonePlatform {
public:
OzonePlatformScenic() = default;
OzonePlatformScenic() : surface_factory_(&window_manager_) {}
~OzonePlatformScenic() override = default;
ScenicWindowManager* window_manager() { return &window_manager_; }
......
......@@ -238,13 +238,20 @@ void ScenicSession::SetEventMask(ResourceId resource_id, uint32_t event_mask) {
EnqueueGfxCommand(std::move(command));
}
void ScenicSession::AddAcquireFence(zx::event fence) {
acquire_fences_.push_back(std::move(fence));
}
void ScenicSession::AddReleaseFence(zx::event fence) {
release_fences_.push_back(std::move(fence));
}
void ScenicSession::Present() {
Flush();
// Pass empty non-null vectors for acquire_fences and release_fences.
fidl::VectorPtr<zx::event> acquire_fences(static_cast<size_t>(0));
fidl::VectorPtr<zx::event> release_fences(static_cast<size_t>(0));
session_->Present(0, std::move(acquire_fences), std::move(release_fences),
session_->Present(/*flags=*/0,
fidl::VectorPtr<zx::event>(std::move(acquire_fences_)),
fidl::VectorPtr<zx::event>(std::move(release_fences_)),
[](fuchsia::images::PresentationInfo info) {});
}
......
......@@ -11,6 +11,8 @@
#include <fuchsia/ui/gfx/cpp/fidl.h>
#include <fuchsia/ui/scenic/cpp/fidl.h>
#include <lib/fidl/cpp/binding.h>
#include <lib/zx/event.h>
#include <lib/zx/eventpair.h>
#include "base/macros.h"
#include "base/memory/shared_memory_handle.h"
......@@ -70,6 +72,12 @@ class ScenicSession : public fuchsia::ui::scenic::SessionListener {
void SetMaterialTexture(ResourceId material_id, ResourceId texture_id);
void SetEventMask(ResourceId resource_id, uint32_t event_mask);
// Enqueue acquire or release fences for the next Present() call. See
// Scenic documentation for description on how these should be used:
// https://fuchsia.googlesource.com/garnet/+/master/public/lib/ui/scenic/fidl/session.fidl
void AddAcquireFence(zx::event fence);
void AddReleaseFence(zx::event fence);
// Flushes queued commands and presents the resulting frame.
void Present();
......@@ -104,6 +112,10 @@ class ScenicSession : public fuchsia::ui::scenic::SessionListener {
fidl::VectorPtr<fuchsia::ui::scenic::Command> queued_commands_;
// Pending acquire and release fences passed in the next Present() call.
std::vector<zx::event> acquire_fences_;
std::vector<zx::event> release_fences_;
DISALLOW_COPY_AND_ASSIGN(ScenicSession);
};
......
......@@ -4,13 +4,69 @@
#include "ui/ozone/platform/scenic/scenic_surface_factory.h"
#include <lib/zx/event.h>
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "ui/gfx/native_pixmap.h"
#include "ui/gfx/skia_util.h"
#include "ui/gfx/vsync_provider.h"
#include "ui/ozone/public/surface_ozone_canvas.h"
#include "ui/ozone/platform/scenic/scenic_window.h"
#include "ui/ozone/platform/scenic/scenic_window_canvas.h"
#include "ui/ozone/platform/scenic/scenic_window_manager.h"
namespace ui {
ScenicSurfaceFactory::ScenicSurfaceFactory() = default;
namespace {
// TODO(crbug.com/852011): Implement this class - currently it's just a stub.
class ScenicPixmap : public gfx::NativePixmap {
public:
explicit ScenicPixmap(gfx::AcceleratedWidget widget,
gfx::Size size,
gfx::BufferFormat format)
: size_(size), format_(format) {
NOTIMPLEMENTED();
}
bool AreDmaBufFdsValid() const override { return false; }
size_t GetDmaBufFdCount() const override { return 0; }
int GetDmaBufFd(size_t plane) const override { return -1; }
int GetDmaBufPitch(size_t plane) const override { return 0; }
int GetDmaBufOffset(size_t plane) const override { return 0; }
uint64_t GetDmaBufModifier(size_t plane) const override { return 0; }
gfx::BufferFormat GetBufferFormat() const override { return format_; }
gfx::Size GetBufferSize() const override { return size_; }
uint32_t GetUniqueId() const override { return 0; }
bool ScheduleOverlayPlane(gfx::AcceleratedWidget widget,
int plane_z_order,
gfx::OverlayTransform plane_transform,
const gfx::Rect& display_bounds,
const gfx::RectF& crop_rect,
bool enable_blend,
std::unique_ptr<gfx::GpuFence> gpu_fence) override {
NOTIMPLEMENTED();
return false;
}
gfx::NativePixmapHandle ExportHandle() override {
NOTIMPLEMENTED();
return gfx::NativePixmapHandle();
}
private:
~ScenicPixmap() override {}
gfx::Size size_;
gfx::BufferFormat format_;
DISALLOW_COPY_AND_ASSIGN(ScenicPixmap);
};
} // namespace
ScenicSurfaceFactory::ScenicSurfaceFactory(ScenicWindowManager* window_manager)
: window_manager_(window_manager) {}
ScenicSurfaceFactory::~ScenicSurfaceFactory() = default;
std::vector<gl::GLImplementation>
......@@ -25,8 +81,10 @@ GLOzone* ScenicSurfaceFactory::GetGLOzone(gl::GLImplementation implementation) {
std::unique_ptr<SurfaceOzoneCanvas> ScenicSurfaceFactory::CreateCanvasForWidget(
gfx::AcceleratedWidget widget) {
NOTIMPLEMENTED();
return nullptr;
ScenicWindow* window = window_manager_->GetWindow(widget);
if (!window)
return nullptr;
return std::make_unique<ScenicWindowCanvas>(window);
}
std::vector<gfx::BufferFormat> ScenicSurfaceFactory::GetScanoutFormats(
......@@ -39,8 +97,7 @@ scoped_refptr<gfx::NativePixmap> ScenicSurfaceFactory::CreateNativePixmap(
gfx::Size size,
gfx::BufferFormat format,
gfx::BufferUsage usage) {
NOTIMPLEMENTED();
return nullptr;
return new ScenicPixmap(widget, size, format);
}
} // namespace ui
......@@ -14,9 +14,11 @@
namespace ui {
class ScenicWindowManager;
class ScenicSurfaceFactory : public SurfaceFactoryOzone {
public:
ScenicSurfaceFactory();
explicit ScenicSurfaceFactory(ScenicWindowManager* window_manager);
~ScenicSurfaceFactory() override;
// SurfaceFactoryOzone implementation.
......@@ -33,6 +35,8 @@ class ScenicSurfaceFactory : public SurfaceFactoryOzone {
gfx::BufferUsage usage) override;
private:
ScenicWindowManager* const window_manager_;
DISALLOW_COPY_AND_ASSIGN(ScenicSurfaceFactory);
};
......
// Copyright 2018 The Chromium 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 "ui/ozone/platform/scenic/scenic_window_canvas.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "ui/gfx/geometry/size_f.h"
#include "ui/gfx/skia_util.h"
#include "ui/gfx/vsync_provider.h"
#include "ui/ozone/platform/scenic/scenic_window.h"
namespace ui {
// How long we want to wait for release-fence from scenic for previous frames.
constexpr base::TimeDelta kFrameReleaseTimeout =
base::TimeDelta::FromMilliseconds(500);
ScenicWindowCanvas::Frame::Frame() = default;
ScenicWindowCanvas::Frame::~Frame() = default;
void ScenicWindowCanvas::Frame::Initialize(gfx::Size size,
ScenicSession* scenic) {
memory.Unmap();
int bytes_per_row = size.width() * SkColorTypeBytesPerPixel(kN32_SkColorType);
int buffer_size = bytes_per_row * size.height();
if (!memory.CreateAndMapAnonymous(buffer_size)) {
LOG(WARNING) << "Failed to map memory for ScenicWindowCanvas.";
memory.Unmap();
surface.reset();
} else {
memory_id = scenic->CreateMemory(memory.GetReadOnlyHandle(),
fuchsia::images::MemoryType::HOST_MEMORY);
surface = SkSurface::MakeRasterDirect(
SkImageInfo::MakeN32Premul(size.width(), size.height()),
memory.memory(), bytes_per_row);
dirty_region.setRect(gfx::RectToSkIRect(gfx::Rect(size)));
}
}
void ScenicWindowCanvas::Frame::CopyDirtyRegionFrom(const Frame& frame) {
int stride = surface->width() * SkColorTypeBytesPerPixel(kN32_SkColorType);
for (SkRegion::Iterator i(dirty_region); !i.done(); i.next()) {
uint8_t* dst_ptr =
reinterpret_cast<uint8_t*>(memory.memory()) +
i.rect().x() * SkColorTypeBytesPerPixel(kN32_SkColorType) +
i.rect().y() * stride;
frame.surface->readPixels(
SkImageInfo::MakeN32Premul(i.rect().width(), i.rect().height()),
dst_ptr, stride, i.rect().x(), i.rect().y());
}
dirty_region.setEmpty();
}
ScenicWindowCanvas::ScenicWindowCanvas(ScenicWindow* window) : window_(window) {
ScenicSession* scenic = window_->scenic_session();
shape_id_ = scenic->CreateShapeNode();
scenic->AddNodeChild(window_->node_id(), shape_id_);
material_id_ = scenic->CreateMaterial();
scenic->SetNodeMaterial(shape_id_, material_id_);
}
ScenicWindowCanvas::~ScenicWindowCanvas() {
ScenicSession* scenic = window_->scenic_session();
for (int i = 0; i < kNumBuffers; ++i) {
if (!frames_[i].is_empty())
scenic->ReleaseResource(frames_[i].memory_id);
}
scenic->ReleaseResource(material_id_);
scenic->ReleaseResource(shape_id_);
}
void ScenicWindowCanvas::ResizeCanvas(const gfx::Size& viewport_size) {
viewport_size_ = viewport_size;
ScenicSession* scenic = window_->scenic_session();
float device_pixel_ratio = window_->device_pixel_ratio();
// Allocate new buffers with the new size.
for (int i = 0; i < kNumBuffers; ++i) {
if (!frames_[i].is_empty())
scenic->ReleaseResource(frames_[i].memory_id);
frames_[i].Initialize(viewport_size_, scenic);
}
gfx::SizeF viewport_size_dips =
gfx::ScaleSize(gfx::SizeF(viewport_size_), 1.0 / device_pixel_ratio);
// Translate the node by half of the view dimensions to put it in the center
// of the view.
float t[] = {viewport_size_dips.width() / 2.0,
viewport_size_dips.height() / 2.0, 0.f};
scenic->SetNodeTranslation(window_->node_id(), t);
// Set node shape to rectangle that matches size of the view.
ScenicSession::ResourceId rect_id = scenic->CreateRectangle(
viewport_size_dips.width(), viewport_size_dips.height());
scenic->SetNodeShape(shape_id_, rect_id);
scenic->ReleaseResource(rect_id);
}
sk_sp<SkSurface> ScenicWindowCanvas::GetSurface() {
if (viewport_size_.IsEmpty() || frames_[current_frame_].is_empty())
return nullptr;
// Wait for the buffer to become available. This call has to be blocking
// because GetSurface() and PresentCanvas() are synchronous.
//
// TODO(sergeyu): Consider updating SurfaceOzoneCanvas interface to allow
// asynchronous PresentCanvas() and then wait for release-fence before
// PresentCanvas() completes.
if (frames_[current_frame_].release_fence) {
auto status = frames_[current_frame_].release_fence.wait_one(
ZX_EVENT_SIGNALED,
zx::deadline_after(zx::duration(kFrameReleaseTimeout.InNanoseconds())),
nullptr);
if (status == ZX_ERR_TIMED_OUT) {
// Timeout here indicates that Scenic is most likely broken. If it still
// works, then in the worst case returning before |release_fence| is
// signaled will cause screen tearing.
LOG(WARNING) << "Release fence from previous frame timed out after 500ms";
} else {
ZX_CHECK(status == ZX_OK, status);
}
}
return frames_[current_frame_].surface;
}
void ScenicWindowCanvas::PresentCanvas(const gfx::Rect& damage) {
// Subtract |damage| from the dirty region in the current frame since it's
// been repainted.
SkIRect sk_damage = gfx::RectToSkIRect(damage);
frames_[current_frame_].dirty_region.op(sk_damage, SkRegion::kDifference_Op);
// Copy dirty region from the previous buffer to make sure the whole frame
// is up to date.
int prev_frame =
current_frame_ == 0 ? (kNumBuffers - 1) : (current_frame_ - 1);
frames_[current_frame_].CopyDirtyRegionFrom(frames_[prev_frame]);
// |damage| rect was updated in the current frame. It means that the rect is
// no longer valid in all other buffers. Add |damage| to |dirty_region| in all
// buffers except the current one.
for (int i = 0; i < kNumBuffers; ++i) {
if (i != current_frame_) {
frames_[i].dirty_region.op(sk_damage, SkRegion::kUnion_Op);
}
}
// Create image that wraps the buffer and attach it as texture for the node.
ScenicSession* scenic = window_->scenic_session();
fuchsia::images::ImageInfo info;
info.width = viewport_size_.width();
info.height = viewport_size_.height();
info.stride =
viewport_size_.width() * SkColorTypeBytesPerPixel(kN32_SkColorType);
ScenicSession::ResourceId image_id = scenic->CreateImage(
frames_[current_frame_].memory_id, 0, std::move(info));
scenic->SetMaterialTexture(material_id_, image_id);
scenic->ReleaseResource(image_id);
// Create release fence for the current buffer or reset it if it already
// exists.
if (!frames_[current_frame_].release_fence) {
auto status = zx::event::create(
/*options=*/0u, &(frames_[current_frame_].release_fence));
ZX_CHECK(status == ZX_OK, status);
} else {
auto status = frames_[current_frame_].release_fence.signal(
/*clear_mask=*/ZX_EVENT_SIGNALED, /*set_maks=*/0);
ZX_CHECK(status == ZX_OK, status);
}
// Add release-fence for the Present() call below. The fence is used in
// GetCanvas() to ensure that we reuse the buffer only after it's released
// from scenic.
zx::event release_fence_dup;
auto status = frames_[current_frame_].release_fence.duplicate(
ZX_RIGHT_SAME_RIGHTS, &release_fence_dup);
ZX_CHECK(status == ZX_OK, status);
scenic->AddReleaseFence(std::move(release_fence_dup));
// Present the frame.
scenic->Present();
// Move to the next buffer.
current_frame_ = (current_frame_ + 1) % kNumBuffers;
}
std::unique_ptr<gfx::VSyncProvider> ScenicWindowCanvas::CreateVSyncProvider() {
// TODO(crbug.com/829980): Implement VSyncProvider. It can be implemented by
// observing PresentationInfo returned from scenic::Session::Present().
return nullptr;
}
} // namespace ui
\ No newline at end of file
// Copyright 2018 The Chromium 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 UI_OZONE_PLATFORM_SCENIC_SCENIC_WINDOW_CANVAS_H_
#define UI_OZONE_PLATFORM_SCENIC_SCENIC_WINDOW_CANVAS_H_
#include "base/macros.h"
#include "base/memory/shared_memory.h"
#include "third_party/skia/include/core/SkRegion.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "ui/gfx/geometry/size.h"
#include "ui/ozone/platform/scenic/scenic_session.h"
#include "ui/ozone/public/surface_ozone_canvas.h"
namespace ui {
class ScenicWindow;
// SurfaceOzoneCanvas implementation for ScenicWindow. It allows to draw on a
// ScenicWindow.
class ScenicWindowCanvas : public SurfaceOzoneCanvas {
public:
// |window| must outlive the surface. ScenicWindow owns the ScenicSession used
// in this class for all drawing operations.
explicit ScenicWindowCanvas(ScenicWindow* window);
~ScenicWindowCanvas() override;
// SurfaceOzoneCanvas implementation.
void ResizeCanvas(const gfx::Size& viewport_size) override;
sk_sp<SkSurface> GetSurface() override;
void PresentCanvas(const gfx::Rect& damage) override;
std::unique_ptr<gfx::VSyncProvider> CreateVSyncProvider() override;
private:
// Use 2 buffers: one is shown on the screen while the other is used to render
// the next frame.
static const int kNumBuffers = 2;
struct Frame {
Frame();
~Frame();
// Allocates and maps memory for a frame of |size| (in physical in pixels)
// and then registers it with |scenic|.
void Initialize(gfx::Size size, ScenicSession* scenic);
// Copies pixels covered by |dirty_region| from another |frame|.
void CopyDirtyRegionFrom(const Frame& frame);
bool is_empty() { return memory.mapped_size() == 0; }
// Shared memory for the buffer.
base::SharedMemory memory;
// SkSurface that wraps |memory|.
sk_sp<SkSurface> surface;
// Valid only when |memory| is set.
ScenicSession::ResourceId memory_id;
// Fence that will be release by Scenic when it stops using this frame.
zx::event release_fence;
// The region of the frame that's not up-to-date.
SkRegion dirty_region;
};
ScenicWindow* const window_;
// Shape and material resource ids for the view in the context of the scenic
// session for the |window_| (window_->scenic()). They are used to set shape
// and texture for the view node.
ScenicSession::ResourceId shape_id_;
ScenicSession::ResourceId material_id_;
Frame frames_[kNumBuffers];
// Buffer index in |frames_| for the frame that's currently being rendered.
int current_frame_ = 0;
// View size in device pixels.
gfx::Size viewport_size_;
DISALLOW_COPY_AND_ASSIGN(ScenicWindowCanvas);
};
} // namespace ui
#endif // UI_OZONE_PLATFORM_SCENIC_SCENIC_WINDOW_CANVAS_H_
......@@ -13,6 +13,7 @@
class SkSurface;
namespace gfx {
class Rect;
class Size;
class VSyncProvider;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment