Skip to content

Commit

Permalink
frontend/wayland: wp_viewporter support (#3445)
Browse files Browse the repository at this point in the history
Accidentally all squashed together into a single mega-commit, here's
`wp_viewporter`.

You can test with the `weston-scaler` client in... weston, and also with
canonical/wlcs#340
  • Loading branch information
AlanGriffiths authored Jul 11, 2024
2 parents 96738e0 + 45d7c8d commit ef638b6
Show file tree
Hide file tree
Showing 24 changed files with 694 additions and 27 deletions.
6 changes: 6 additions & 0 deletions include/core/mir/geometry/dimensions.h
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,12 @@ template<typename T, typename Scalar>
inline constexpr DeltaX<T> operator/(DeltaX<T> const& dx, Scalar scale) { return DeltaX<T>{dx.as_value() / scale}; }
template<typename T, typename Scalar>
inline constexpr DeltaY<T> operator/(DeltaY<T> const& dy, Scalar scale) { return DeltaY<T>{dy.as_value() / scale}; }

// Can divide a coördinate by a distance to get a scalar
template<typename T, typename U>
inline constexpr auto operator/(X<T> const& x, Width<U> const& width) { return x.as_value() / width.as_value(); }
template<typename T, typename U>
inline constexpr auto operator/(Y<T> const& y, Height<U> const& height) { return y.as_value() / height.as_value(); }
} // namespace

// Converting between types is fine, as long as they are along the same axis
Expand Down
18 changes: 18 additions & 0 deletions include/core/mir/geometry/size.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
#include "forward.h"
#include "dimensions.h"
#include <ostream>
#include <limits>
#include <concepts>
#include <type_traits>

namespace mir
{
Expand All @@ -32,6 +35,13 @@ struct Point;
template<typename T>
struct Displacement;

template<typename T, typename U>
concept is_exactly_representable = requires
{
typename std::enable_if<std::numeric_limits<T>::digits >= std::numeric_limits<U>::digits, U>::type;
requires std::floating_point<T>;
};

template<typename T>
struct Size
{
Expand All @@ -41,6 +51,14 @@ struct Size
constexpr Size(Size const&) noexcept = default;
Size& operator=(Size const&) noexcept = default;

template<typename U>
requires is_exactly_representable<T, U>
constexpr Size(Size<U> const& other) noexcept
: width{Width<T>{other.width}},
height{Height<T>{other.height}}
{
}

template<typename U>
explicit constexpr Size(Size<U> const& other) noexcept
: width{Width<T>{other.width}},
Expand Down
7 changes: 7 additions & 0 deletions include/platform/mir/graphics/renderable.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ class Renderable
virtual std::shared_ptr<Buffer> buffer() const = 0;

virtual geometry::Rectangle screen_position() const = 0;
/**
* The region of \ref buffer that should be sampled from.
*
* This is in buffer coordinates, so {{0, 0}, {buffer->size().width, buffer->size().height} means
* “use the whole buffer”.
*/
virtual geometry::RectangleD src_bounds() const = 0;
virtual std::optional<geometry::Rectangle> clip_area() const = 0;

// These are from the old CompositingCriteria. There is a little bit
Expand Down
43 changes: 34 additions & 9 deletions src/gl/tessellation_helpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,51 @@
namespace mg = mir::graphics;
namespace mgl = mir::gl;
namespace geom = mir::geometry;

namespace
{
struct SrcTexCoords
{
GLfloat top;
GLfloat bottom;
GLfloat left;
GLfloat right;
};

auto tex_coords_from_rect(geom::Size buffer_size, geom::RectangleD sample_rect) -> SrcTexCoords
{
/* GL Texture coordinates are normalised to the size of the buffer, so (0.0, 0.0) is the top-left
* and (1.0, 1.0) is the bottom-right
*/
SrcTexCoords coords;
coords.top = sample_rect.top() / buffer_size.height;
coords.bottom = sample_rect.bottom() / buffer_size.height;
coords.left = sample_rect.left() / buffer_size.width;
coords.right = sample_rect.right() / buffer_size.width;
return coords;
}
}

mgl::Primitive mgl::tessellate_renderable_into_rectangle(
mg::Renderable const& renderable, geom::Displacement const& offset)
{
auto rect = renderable.screen_position();
rect.top_left = rect.top_left - offset;
GLfloat left = rect.top_left.x.as_int();
GLfloat right = left + rect.size.width.as_int();
GLfloat top = rect.top_left.y.as_int();
GLfloat bottom = top + rect.size.height.as_int();
GLfloat const left = rect.top_left.x.as_int();
GLfloat const right = left + rect.size.width.as_int();
GLfloat const top = rect.top_left.y.as_int();
GLfloat const bottom = top + rect.size.height.as_int();

mgl::Primitive rectangle;
rectangle.type = GL_TRIANGLE_STRIP;

GLfloat const tex_right = 1.0f;
GLfloat const tex_bottom = 1.0f;
auto const [tex_top, tex_bottom, tex_left, tex_right] =
tex_coords_from_rect(renderable.buffer()->size(), renderable.src_bounds());

auto& vertices = rectangle.vertices;
vertices[0] = {{left, top, 0.0f}, {0.0f, 0.0f}};
vertices[1] = {{left, bottom, 0.0f}, {0.0f, tex_bottom}};
vertices[2] = {{right, top, 0.0f}, {tex_right, 0.0f}};
vertices[0] = {{left, top, 0.0f}, {tex_left, tex_top}};
vertices[1] = {{left, bottom, 0.0f}, {tex_left, tex_bottom}};
vertices[2] = {{right, top, 0.0f}, {tex_right, tex_top}};
vertices[3] = {{right, bottom, 0.0f}, {tex_right, tex_bottom}};
return rectangle;
}
1 change: 1 addition & 0 deletions src/server/frontend_wayland/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ set(
${PROJECT_SOURCE_DIR}/src/include/server/mir/frontend/pointer_input_dispatcher.h
session_credentials.cpp
${PROJECT_SOURCE_DIR}/src/include/server/mir/frontend/buffer_stream.h
wp_viewporter.cpp wp_viewporter.h
)

add_custom_command(
Expand Down
3 changes: 3 additions & 0 deletions src/server/frontend_wayland/wayland_connector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "wayland_executor.h"
#include "desktop_file_manager.h"
#include "foreign_toplevel_manager_v1.h"
#include "wp_viewporter.h"

#include "mir/main_loop.h"
#include "mir/thread_name.h"
Expand Down Expand Up @@ -331,6 +332,8 @@ mf::WaylandConnector::WaylandConnector(

shm_global = std::make_unique<WlShm>(display.get(), executor);

viewporter = std::make_unique<WpViewporter>(display.get());

char const* wayland_display = nullptr;

if (auto const display_name = getenv("WAYLAND_DISPLAY"))
Expand Down
2 changes: 2 additions & 0 deletions src/server/frontend_wayland/wayland_connector.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ class WlSeat;
class WlShm;
class WlSubcompositor;
class WlSurface;
class WpViewporter;
class DesktopFileManager;

class WaylandExtensions
Expand Down Expand Up @@ -210,6 +211,7 @@ class WaylandConnector : public Connector
std::shared_ptr<DesktopFileManager> desktop_file_manager;
std::unique_ptr<WlDataDeviceManager> data_device_manager_global;
std::unique_ptr<WlShm> shm_global;
std::unique_ptr<WpViewporter> viewporter;
std::shared_ptr<Executor> const executor;
std::shared_ptr<graphics::GraphicBufferAllocator> const allocator;
std::shared_ptr<shell::Shell> const shell;
Expand Down
75 changes: 59 additions & 16 deletions src/server/frontend_wayland/wl_surface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

#include "wl_surface.h"

#include "mir/geometry/forward.h"
#include "viewporter_wrapper.h"
#include "wayland_utils.h"
#include "wl_surface_role.h"
#include "wl_subcompositor.h"
Expand All @@ -39,9 +39,12 @@
#include "mir/scene/surface.h"
#include "mir/shell/surface_specification.h"
#include "mir/log.h"
#include "wp_viewporter.h"

#include <chrono>
#include <boost/throw_exception.hpp>
#include <cmath>
#include <limits>
#include <wayland-server-protocol.h>

namespace mf = mir::frontend;
Expand Down Expand Up @@ -72,6 +75,9 @@ void mf::WlSurfaceState::update_from(WlSurfaceState const& source)
begin(source.frame_callbacks),
end(source.frame_callbacks));

if (source.viewport)
viewport = source.viewport;

if (source.surface_data_invalidated)
surface_data_invalidated = true;
}
Expand Down Expand Up @@ -331,7 +337,19 @@ void mf::WlSurface::commit(WlSurfaceState const& state)
input_shape = state.input_shape.value();

if (state.scale)
inv_scale = 1.0f / state.scale.value();
scale = state.scale.value();

if (state.viewport)
{
viewport = std::move(state.viewport);
}

bool needs_buffer_submission =
state.scale || // If the scale has changed, or...
state.viewport || // ...we've added a viewport, or...
(viewport && viewport.value().changed_since_last_resolve()); // ...the viewport has changed...
// ...then we'll need to submit a new frame, even if the client hasn't
// attached a new buffer.

auto const executor_send_frame_callbacks = [executor = wayland_executor, weak_self = mw::make_weak(this)]()
{
Expand Down Expand Up @@ -366,49 +384,65 @@ void mf::WlSurface::commit(WlSurfaceState const& state)
}
});
};
std::shared_ptr<graphics::Buffer> mir_buffer;

if (auto const shm_buffer = ShmBuffer::from(weak_buffer.value()))
{
mir_buffer = allocator->buffer_from_shm(
current_buffer = allocator->buffer_from_shm(
shm_buffer->data(),
std::move(executor_send_frame_callbacks),
std::move(release_buffer));
tracepoint(
mir_server_wayland,
sw_buffer_committed,
wl_resource_get_client(resource),
mir_buffer->id().as_value());
current_buffer->id().as_value());
}
else
{
mir_buffer = allocator->buffer_from_resource(
current_buffer = allocator->buffer_from_resource(
weak_buffer.value(),
std::move(executor_send_frame_callbacks),
std::move(release_buffer));
tracepoint(
mir_server_wayland,
hw_buffer_committed,
wl_resource_get_client(resource),
mir_buffer->id().as_value());
}

stream->submit_buffer(mir_buffer, mir_buffer->size() * inv_scale, {{0, 0}, geom::SizeD{mir_buffer->size()}});
auto const new_buffer_size = mir_buffer->size() * inv_scale;

if (std::make_optional(new_buffer_size) != buffer_size_)
{
state.invalidate_surface_data(); // input shape needs to be recalculated for the new size
current_buffer->id().as_value());
}

buffer_size_ = new_buffer_size;
needs_buffer_submission = true;
}
}
else
{
frame_callback_executor->spawn(std::move(executor_send_frame_callbacks));
}

if (needs_buffer_submission)
{
geom::Size logical_size;
geom::RectangleD src_sample;

if (viewport)
{
std::tie(src_sample, logical_size) = viewport.value().resolve_viewport(scale, current_buffer->size());
}
else
{
src_sample = geom::RectangleD{{0, 0}, current_buffer->size()};
logical_size = current_buffer->size() / scale;
}

stream->submit_buffer(current_buffer, logical_size, src_sample);

if (std::make_optional(logical_size) != buffer_size_)
{
state.invalidate_surface_data(); // input shape needs to be recalculated for the new size
}

buffer_size_ = logical_size;
}

for (WlSubsurface* child: children)
{
child->parent_has_committed();
Expand Down Expand Up @@ -468,6 +502,15 @@ auto mf::WlSurface::confine_pointer_state() const -> MirPointerConfinementState
return mir_pointer_unconfined;
}

void mf::WlSurface::associate_viewport(wayland::Weak<Viewport> viewport)
{
if (this->viewport || pending.viewport)
{
BOOST_THROW_EXCEPTION((std::logic_error{"Cannot associate a viewport to a window with an existing viewport"}));
}
pending.viewport = viewport;
}

void mir::frontend::WlSurface::update_surface_spec(shell::SurfaceSpecification const& spec)
{
pending.surface_spec.update_from(spec);
Expand Down
22 changes: 21 additions & 1 deletion src/server/frontend_wayland/wl_surface.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class Executor;
namespace graphics
{
class GraphicBufferAllocator;
class Buffer;
}
namespace scene
{
Expand All @@ -57,6 +58,7 @@ namespace frontend
class WlSurface;
class WlSubsurface;
class ResourceLifetimeTracker;
class Viewport;

struct WlSurfaceState
{
Expand All @@ -83,6 +85,7 @@ struct WlSurfaceState
std::optional<geometry::Displacement> offset;
std::optional<std::optional<std::vector<geometry::Rectangle>>> input_shape;
std::vector<wayland::Weak<Callback>> frame_callbacks;
wayland::Weak<Viewport> viewport;

private:
// only set to true if invalidate_surface_data() is called
Expand Down Expand Up @@ -142,6 +145,13 @@ class WlSurface : public wayland::Surface
void commit(WlSurfaceState const& state);
auto confine_pointer_state() const -> MirPointerConfinementState;

/**
* Associate a viewport (buffer scale & crop metadata) with this surface
*
* \throws A std::logic_error if the surface already has a viewport associated
*/
void associate_viewport(wayland::Weak<Viewport> viewport);

std::shared_ptr<scene::Session> const session;
std::shared_ptr<compositor::BufferStream> const stream;

Expand All @@ -155,14 +165,24 @@ class WlSurface : public wayland::Surface
NullWlSurfaceRole null_role;
WlSurfaceRole* role;
std::vector<WlSubsurface*> children; // ordering is from bottom to top
/* We might need to resubmit the current buffer, but with different metadata
* For example: if a client commits a wl_surface.buffer_scale() without attaching
* a new buffer, we need to generate a new frame with the same content, but at the
* new scale.
*
* To simplify other interfaces, keep a reference to the current content so we can
* hide this, here, in the frontend.
*/
std::shared_ptr<graphics::Buffer> current_buffer;

WlSurfaceState pending;
geometry::Displacement offset_;
float inv_scale{1.0f};
int32_t scale{1};
std::optional<geometry::Size> buffer_size_;
std::vector<wayland::Weak<WlSurfaceState::Callback>> frame_callbacks;
std::optional<std::vector<mir::geometry::Rectangle>> input_shape;
std::vector<SceneSurfaceCreatedCallback> scene_surface_created_callbacks;
wayland::Weak<Viewport> viewport;

void send_frame_callbacks();

Expand Down
Loading

0 comments on commit ef638b6

Please sign in to comment.