Commit cb8ff730 authored by Maksim Sisov's avatar Maksim Sisov Committed by Commit Bot

ozone/wayland: add GLSurfaceEglReadbackWayland

This adds missing readback bits so that Wayland can be used with
SwiftShader. I encountered this problem when started to work on
enabled tests with wayland backend enabled.

The implementation is quite naive - we create a
shared memory buffer, import a wl_buffer with the fd for that
shm buffer and write pixels that are read with glReadPixels into
the shm buffer.

Also, this patch restores async buffer swap support in
SkiaOutputDeviceGL that was removed in https://crrev.com/c/2025968.
Now, it's Wayland that has the support for async buffer swap, it's
wrong to assume there are no clients that do not support it.

and also running chrome --ozone-platform=wayland --use-gl=swiftshader.

Test: views_unittests to ensure that surface with read back is created
Bug: 1054305
Change-Id: Iff307e1155d0a14965a2bd193a7b9b8d9aceb952
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2064929
Commit-Queue: Maksim Sisov <msisov@igalia.com>
Reviewed-by: default avatarRobert Kroeger <rjkroege@chromium.org>
Reviewed-by: default avatarMichael Spang <spang@chromium.org>
Cr-Commit-Position: refs/heads/master@{#746758}
parent a9ada7b4
...@@ -41,9 +41,8 @@ SkiaOutputDeviceGL::SkiaOutputDeviceGL( ...@@ -41,9 +41,8 @@ SkiaOutputDeviceGL::SkiaOutputDeviceGL(
std::move(did_swap_buffer_complete_callback)), std::move(did_swap_buffer_complete_callback)),
mailbox_manager_(mailbox_manager), mailbox_manager_(mailbox_manager),
context_state_(context_state), context_state_(context_state),
gl_surface_(std::move(gl_surface)) { gl_surface_(std::move(gl_surface)),
// Only BufferQueue should support async swap. supports_async_swap_(gl_surface_->SupportsAsyncSwap()) {
DCHECK(!gl_surface_->SupportsAsyncSwap());
capabilities_.output_surface_origin = gl_surface_->GetOrigin(); capabilities_.output_surface_origin = gl_surface_->GetOrigin();
capabilities_.supports_post_sub_buffer = gl_surface_->SupportsPostSubBuffer(); capabilities_.supports_post_sub_buffer = gl_surface_->SupportsPostSubBuffer();
if (feature_info->workarounds() if (feature_info->workarounds()
...@@ -172,8 +171,15 @@ void SkiaOutputDeviceGL::SwapBuffers( ...@@ -172,8 +171,15 @@ void SkiaOutputDeviceGL::SwapBuffers(
gfx::Size surface_size = gfx::Size surface_size =
gfx::Size(sk_surface_->width(), sk_surface_->height()); gfx::Size(sk_surface_->width(), sk_surface_->height());
FinishSwapBuffers(gl_surface_->SwapBuffers(std::move(feedback)), surface_size, if (supports_async_swap_) {
auto callback = base::BindOnce(&SkiaOutputDeviceGL::DoFinishSwapBuffers,
weak_ptr_factory_.GetWeakPtr(), surface_size,
std::move(latency_info)); std::move(latency_info));
gl_surface_->SwapBuffersAsync(std::move(callback), std::move(feedback));
} else {
FinishSwapBuffers(gl_surface_->SwapBuffers(std::move(feedback)),
surface_size, std::move(latency_info));
}
} }
void SkiaOutputDeviceGL::PostSubBuffer( void SkiaOutputDeviceGL::PostSubBuffer(
...@@ -185,10 +191,20 @@ void SkiaOutputDeviceGL::PostSubBuffer( ...@@ -185,10 +191,20 @@ void SkiaOutputDeviceGL::PostSubBuffer(
gfx::Size surface_size = gfx::Size surface_size =
gfx::Size(sk_surface_->width(), sk_surface_->height()); gfx::Size(sk_surface_->width(), sk_surface_->height());
if (supports_async_swap_) {
auto callback = base::BindOnce(&SkiaOutputDeviceGL::DoFinishSwapBuffers,
weak_ptr_factory_.GetWeakPtr(), surface_size,
std::move(latency_info));
gl_surface_->PostSubBufferAsync(rect.x(), rect.y(), rect.width(),
rect.height(), std::move(callback),
std::move(feedback));
} else {
FinishSwapBuffers( FinishSwapBuffers(
gl_surface_->PostSubBuffer(rect.x(), rect.y(), rect.width(), gl_surface_->PostSubBuffer(rect.x(), rect.y(), rect.width(),
rect.height(), std::move(feedback)), rect.height(), std::move(feedback)),
surface_size, std::move(latency_info)); surface_size, std::move(latency_info));
}
} }
void SkiaOutputDeviceGL::CommitOverlayPlanes( void SkiaOutputDeviceGL::CommitOverlayPlanes(
...@@ -199,8 +215,25 @@ void SkiaOutputDeviceGL::CommitOverlayPlanes( ...@@ -199,8 +215,25 @@ void SkiaOutputDeviceGL::CommitOverlayPlanes(
gfx::Size surface_size = gfx::Size surface_size =
gfx::Size(sk_surface_->width(), sk_surface_->height()); gfx::Size(sk_surface_->width(), sk_surface_->height());
if (supports_async_swap_) {
auto callback = base::BindOnce(&SkiaOutputDeviceGL::DoFinishSwapBuffers,
weak_ptr_factory_.GetWeakPtr(), surface_size,
std::move(latency_info));
gl_surface_->CommitOverlayPlanesAsync(std::move(callback),
std::move(feedback));
} else {
FinishSwapBuffers(gl_surface_->CommitOverlayPlanes(std::move(feedback)), FinishSwapBuffers(gl_surface_->CommitOverlayPlanes(std::move(feedback)),
surface_size, std::move(latency_info)); surface_size, std::move(latency_info));
}
}
void SkiaOutputDeviceGL::DoFinishSwapBuffers(
const gfx::Size& size,
std::vector<ui::LatencyInfo> latency_info,
gfx::SwapResult result,
std::unique_ptr<gfx::GpuFence> gpu_fence) {
DCHECK(!gpu_fence);
FinishSwapBuffers(result, size, latency_info);
} }
void SkiaOutputDeviceGL::SetDrawRectangle(const gfx::Rect& draw_rectangle) { void SkiaOutputDeviceGL::SetDrawRectangle(const gfx::Rect& draw_rectangle) {
......
...@@ -20,6 +20,10 @@ class GLImage; ...@@ -20,6 +20,10 @@ class GLImage;
class GLSurface; class GLSurface;
} // namespace gl } // namespace gl
namespace gfx {
class GpuFence;
} // namespace gfx
namespace gpu { namespace gpu {
class MailboxManager; class MailboxManager;
class SharedContextState; class SharedContextState;
...@@ -71,12 +75,20 @@ class SkiaOutputDeviceGL final : public SkiaOutputDevice { ...@@ -71,12 +75,20 @@ class SkiaOutputDeviceGL final : public SkiaOutputDevice {
void EndPaint(const GrBackendSemaphore& semaphore) override; void EndPaint(const GrBackendSemaphore& semaphore) override;
private: private:
// Used as callback for SwapBuffersAsync and PostSubBufferAsync to finish
// operation
void DoFinishSwapBuffers(const gfx::Size& size,
std::vector<ui::LatencyInfo> latency_info,
gfx::SwapResult result,
std::unique_ptr<gfx::GpuFence>);
scoped_refptr<gl::GLImage> GetGLImageForMailbox(const gpu::Mailbox& mailbox); scoped_refptr<gl::GLImage> GetGLImageForMailbox(const gpu::Mailbox& mailbox);
gpu::MailboxManager* const mailbox_manager_; gpu::MailboxManager* const mailbox_manager_;
gpu::SharedContextState* const context_state_; gpu::SharedContextState* const context_state_;
scoped_refptr<gl::GLSurface> gl_surface_; scoped_refptr<gl::GLSurface> gl_surface_;
const bool supports_async_swap_;
sk_sp<SkSurface> sk_surface_; sk_sp<SkSurface> sk_surface_;
......
...@@ -22,6 +22,8 @@ source_set("wayland") { ...@@ -22,6 +22,8 @@ source_set("wayland") {
"common/wayland_util.h", "common/wayland_util.h",
"gpu/drm_render_node_path_finder.cc", "gpu/drm_render_node_path_finder.cc",
"gpu/drm_render_node_path_finder.h", "gpu/drm_render_node_path_finder.h",
"gpu/gl_surface_egl_readback_wayland.cc",
"gpu/gl_surface_egl_readback_wayland.h",
"gpu/gl_surface_wayland.cc", "gpu/gl_surface_wayland.cc",
"gpu/gl_surface_wayland.h", "gpu/gl_surface_wayland.h",
"gpu/wayland_buffer_manager_gpu.cc", "gpu/wayland_buffer_manager_gpu.cc",
......
// Copyright 2020 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/wayland/gpu/gl_surface_egl_readback_wayland.h"
#include "base/memory/shared_memory_mapping.h"
#include "base/memory/unsafe_shared_memory_region.h"
#include "base/numerics/checked_math.h"
#include "ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h"
namespace ui {
namespace {
constexpr size_t kMaxBuffers = 2;
constexpr size_t kBytesPerPixelBGRA = 4;
} // namespace
GLSurfaceEglReadbackWayland::PixelBuffer::PixelBuffer(
base::WritableSharedMemoryMapping shm_mapping,
uint32_t buffer_id)
: shm_mapping_(std::move(shm_mapping)), buffer_id_(buffer_id) {}
GLSurfaceEglReadbackWayland::PixelBuffer::~PixelBuffer() = default;
GLSurfaceEglReadbackWayland::GLSurfaceEglReadbackWayland(
gfx::AcceleratedWidget widget,
WaylandBufferManagerGpu* buffer_manager)
: PbufferGLSurfaceEGL(gfx::Size(1, 1)),
widget_(widget),
buffer_manager_(buffer_manager) {
buffer_manager_->RegisterSurface(widget_, this);
}
void GLSurfaceEglReadbackWayland::Destroy() {
DestroyBuffers();
buffer_manager_->UnregisterSurface(widget_);
PbufferGLSurfaceEGL::Destroy();
}
bool GLSurfaceEglReadbackWayland::Resize(const gfx::Size& size,
float scale_factor,
const gfx::ColorSpace& color_space,
bool has_alpha) {
DestroyBuffers();
pending_frames_ = 0;
if (!PbufferGLSurfaceEGL::Resize(size, scale_factor, color_space, has_alpha))
return false;
for (size_t i = 0; i < kMaxBuffers; ++i) {
base::CheckedNumeric<size_t> checked_length(size.width());
checked_length *= size.height();
checked_length *= kBytesPerPixelBGRA;
if (!checked_length.IsValid())
return false;
base::UnsafeSharedMemoryRegion shm_region =
base::UnsafeSharedMemoryRegion::Create(checked_length.ValueOrDie());
if (!shm_region.IsValid())
return false;
auto shm_mapping = shm_region.Map();
if (!shm_mapping.IsValid())
return false;
base::subtle::PlatformSharedMemoryRegion platform_shm =
base::UnsafeSharedMemoryRegion::TakeHandleForSerialization(
std::move(shm_region));
base::subtle::ScopedFDPair fd_pair = platform_shm.PassPlatformHandle();
auto buffer_id = buffer_manager_->AllocateBufferID();
available_buffers_.push_back(
std::make_unique<PixelBuffer>(std::move(shm_mapping), buffer_id));
buffer_manager_->CreateShmBasedBuffer(
std::move(fd_pair.fd), checked_length.ValueOrDie(), size, buffer_id);
}
return true;
}
bool GLSurfaceEglReadbackWayland::IsOffscreen() {
return false;
}
bool GLSurfaceEglReadbackWayland::SupportsAsyncSwap() {
return true;
}
gfx::SwapResult GLSurfaceEglReadbackWayland::SwapBuffers(
PresentationCallback callback) {
NOTREACHED();
return gfx::SwapResult::SWAP_FAILED;
}
void GLSurfaceEglReadbackWayland::SwapBuffersAsync(
SwapCompletionCallback completion_callback,
PresentationCallback presentation_callback) {
DCHECK(pending_frames_ < kMaxBuffers);
// Increase pending frames number.
++pending_frames_;
completion_callbacks_.push_back(std::move(completion_callback));
presentation_callbacks_.push_back(std::move(presentation_callback));
DCHECK(!available_buffers_.empty());
in_flight_pixel_buffers_.push_back(std::move(available_buffers_.front()));
auto* next_buffer = in_flight_pixel_buffers_.back().get();
available_buffers_.erase(available_buffers_.begin());
const gfx::Size size = GetSize();
CHECK(next_buffer->shm_mapping_.memory());
glReadPixels(0, 0, size.width(), size.height(), GL_BGRA, GL_UNSIGNED_BYTE,
next_buffer->shm_mapping_.memory());
buffer_manager_->CommitBuffer(widget_, next_buffer->buffer_id_,
{{0, 0}, size});
}
gfx::SurfaceOrigin GLSurfaceEglReadbackWayland::GetOrigin() const {
// GLSurfaceEglReadbackWayland's y-axis is flipped compare to GL - (0,0) is at
// top left corner.
return gfx::SurfaceOrigin::kTopLeft;
}
GLSurfaceEglReadbackWayland::~GLSurfaceEglReadbackWayland() {
Destroy();
}
void GLSurfaceEglReadbackWayland::OnSubmission(
uint32_t buffer_id,
const gfx::SwapResult& swap_result) {
--pending_frames_;
if (in_flight_pixel_buffers_.front()) {
if (displayed_buffer_)
available_buffers_.push_back(std::move(displayed_buffer_));
displayed_buffer_ = std::move(in_flight_pixel_buffers_.front());
DCHECK_EQ(displayed_buffer_->buffer_id_, buffer_id);
}
in_flight_pixel_buffers_.pop_front();
DCHECK(!completion_callbacks_.empty());
std::move(completion_callbacks_.front()).Run(swap_result, nullptr);
completion_callbacks_.erase(completion_callbacks_.begin());
}
void GLSurfaceEglReadbackWayland::OnPresentation(
uint32_t buffer_id,
const gfx::PresentationFeedback& feedback) {
DCHECK(!presentation_callbacks_.empty());
std::move(presentation_callbacks_.front()).Run(feedback);
presentation_callbacks_.erase(presentation_callbacks_.begin());
}
void GLSurfaceEglReadbackWayland::DestroyBuffers() {
for (const auto& pixel_buffer : available_buffers_)
buffer_manager_->DestroyBuffer(widget_, pixel_buffer->buffer_id_);
for (const auto& pixel_buffer : in_flight_pixel_buffers_)
buffer_manager_->DestroyBuffer(widget_, pixel_buffer->buffer_id_);
if (displayed_buffer_)
buffer_manager_->DestroyBuffer(widget_, displayed_buffer_->buffer_id_);
available_buffers_.clear();
in_flight_pixel_buffers_.clear();
displayed_buffer_.reset();
}
} // namespace ui
// Copyright 2020 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_WAYLAND_GPU_GL_SURFACE_EGL_READBACK_WAYLAND_H_
#define UI_OZONE_PLATFORM_WAYLAND_GPU_GL_SURFACE_EGL_READBACK_WAYLAND_H_
#include "base/containers/circular_deque.h"
#include "base/containers/flat_map.h"
#include "base/memory/shared_memory_mapping.h"
#include "ui/ozone/common/gl_surface_egl_readback.h"
#include "ui/ozone/platform/wayland/gpu/wayland_surface_gpu.h"
namespace ui {
class WaylandBufferManagerGpu;
// GLSurface implementation that copies pixels from readback to shared memory
// and let Wayland compositor to present them.
class GLSurfaceEglReadbackWayland : public gl::PbufferGLSurfaceEGL,
public WaylandSurfaceGpu {
public:
GLSurfaceEglReadbackWayland(gfx::AcceleratedWidget widget,
WaylandBufferManagerGpu* buffer_manager);
GLSurfaceEglReadbackWayland(const GLSurfaceEglReadbackWayland&) = delete;
GLSurfaceEglReadbackWayland& operator=(const GLSurfaceEglReadbackWayland&) =
delete;
// gl::GLSurface:
void Destroy() override;
bool Resize(const gfx::Size& size,
float scale_factor,
const gfx::ColorSpace& color_space,
bool has_alpha) override;
bool IsOffscreen() override;
gfx::SwapResult SwapBuffers(PresentationCallback callback) override;
bool SupportsAsyncSwap() override;
void SwapBuffersAsync(SwapCompletionCallback completion_callback,
PresentationCallback presentation_callback) override;
gfx::SurfaceOrigin GetOrigin() const override;
private:
struct PixelBuffer {
PixelBuffer(base::WritableSharedMemoryMapping shm_mapping,
uint32_t buffer_id);
~PixelBuffer();
PixelBuffer(const PixelBuffer&) = delete;
PixelBuffer& operator=(const PixelBuffer&) = delete;
// Shared memory mapping that readback pixels are written to so that Wayland
// is able to turn them in light.
base::WritableSharedMemoryMapping shm_mapping_;
// The buffer id that corresponds to the |wl_buffer| created on the browser
// process side.
uint32_t buffer_id_ = 0;
};
~GLSurfaceEglReadbackWayland() override;
// WaylandSurfaceGpu:
void OnSubmission(uint32_t buffer_id,
const gfx::SwapResult& swap_result) override;
void OnPresentation(uint32_t buffer_id,
const gfx::PresentationFeedback& feedback) override;
void DestroyBuffers();
// Widget of the window that this readback writes pixels to.
const gfx::AcceleratedWidget widget_;
WaylandBufferManagerGpu* const buffer_manager_;
// Size of the buffer.
gfx::Size size_;
// Available pixel buffers based on shared memory.
std::vector<std::unique_ptr<PixelBuffer>> available_buffers_;
// Displayed buffer that will become available after another buffer is
// submitted.
std::unique_ptr<PixelBuffer> displayed_buffer_;
// Submitted buffers waiting to be displayed.
base::circular_deque<std::unique_ptr<PixelBuffer>> in_flight_pixel_buffers_;
std::vector<SwapCompletionCallback> completion_callbacks_;
std::vector<PresentationCallback> presentation_callbacks_;
size_t pending_frames_ = 0;
};
} // namespace ui
#endif // UI_OZONE_PLATFORM_WAYLAND_GPU_GL_SURFACE_EGL_READBACK_WAYLAND_H_
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "ui/ozone/common/egl_util.h" #include "ui/ozone/common/egl_util.h"
#include "ui/ozone/common/gl_ozone_egl.h" #include "ui/ozone/common/gl_ozone_egl.h"
#include "ui/ozone/platform/wayland/common/wayland_object.h" #include "ui/ozone/platform/wayland/common/wayland_object.h"
#include "ui/ozone/platform/wayland/gpu/gl_surface_egl_readback_wayland.h"
#include "ui/ozone/platform/wayland/gpu/gl_surface_wayland.h" #include "ui/ozone/platform/wayland/gpu/gl_surface_wayland.h"
#include "ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h" #include "ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h"
#include "ui/ozone/platform/wayland/gpu/wayland_canvas_surface.h" #include "ui/ozone/platform/wayland/gpu/wayland_canvas_surface.h"
...@@ -77,10 +78,11 @@ scoped_refptr<gl::GLSurface> GLOzoneEGLWayland::CreateViewGLSurface( ...@@ -77,10 +78,11 @@ scoped_refptr<gl::GLSurface> GLOzoneEGLWayland::CreateViewGLSurface(
scoped_refptr<gl::GLSurface> GLOzoneEGLWayland::CreateSurfacelessViewGLSurface( scoped_refptr<gl::GLSurface> GLOzoneEGLWayland::CreateSurfacelessViewGLSurface(
gfx::AcceleratedWidget window) { gfx::AcceleratedWidget window) {
// Only EGLGLES2 is supported with surfaceless view gl. if (gl::GetGLImplementation() == gl::kGLImplementationSwiftShaderGL) {
if (gl::GetGLImplementation() != gl::kGLImplementationEGLGLES2) return gl::InitializeGLSurface(
return nullptr; base::MakeRefCounted<GLSurfaceEglReadbackWayland>(window,
buffer_manager_));
} else {
#if defined(WAYLAND_GBM) #if defined(WAYLAND_GBM)
// If there is a gbm device available, use surfaceless gl surface. // If there is a gbm device available, use surfaceless gl surface.
if (!buffer_manager_->gbm_device()) if (!buffer_manager_->gbm_device())
...@@ -90,6 +92,7 @@ scoped_refptr<gl::GLSurface> GLOzoneEGLWayland::CreateSurfacelessViewGLSurface( ...@@ -90,6 +92,7 @@ scoped_refptr<gl::GLSurface> GLOzoneEGLWayland::CreateSurfacelessViewGLSurface(
#else #else
return nullptr; return nullptr;
#endif #endif
}
} }
scoped_refptr<gl::GLSurface> GLOzoneEGLWayland::CreateOffscreenGLSurface( scoped_refptr<gl::GLSurface> GLOzoneEGLWayland::CreateOffscreenGLSurface(
......
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