Mir
render_surfaces.cpp

A simple program using the mir library.

/*
* Copyright © 2012-2014 Canonical Ltd.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
*/
#include "mir/server.h"
#include "mir_image.h"
#include "image_renderer.h"
#define GLM_FORCE_RADIANS
#include <glm/gtc/matrix_transform.hpp>
#include <thread>
#include <atomic>
#include <chrono>
#include <csignal>
#include <iostream>
#include <sstream>
#include <vector>
namespace mg = mir::graphics;
namespace mc = mir::compositor;
namespace ms = mir::scene;
namespace mf = mir::frontend;
namespace mo = mir::options;
namespace msh = mir::shell;
namespace mi = mir::input;
namespace geom = mir::geometry;
namespace mt = mir::tools;
namespace me = mir::examples;
namespace
{
std::atomic<bool> created{false};
static const float min_alpha = 0.3f;
char const* const surfaces_to_render = "surfaces-to-render";
auto as_context_source(mg::Display* display)
{
auto ctx = dynamic_cast<mir::renderer::gl::ContextSource*>(display->native_display());
if (!ctx)
BOOST_THROW_EXCEPTION(std::logic_error("Display does not support GL rendering"));
return ctx;
}
// tracks elapsed time - for animation.
class StopWatch
{
public:
StopWatch() : start(std::chrono::high_resolution_clock::now()),
last(start),
now(last)
{
}
void stop()
{
now = std::chrono::high_resolution_clock::now();
}
double elapsed_seconds_since_start()
{
auto elapsed = now - start;
float elapsed_sec = std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count() / 1000000.0f;
return elapsed_sec;
}
double elapsed_seconds_since_last_restart()
{
auto elapsed = now - last;
float elapsed_sec = std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count() / 1000000.0f;
return elapsed_sec;
}
void restart()
{
std::swap(last, now);
}
private:
std::chrono::high_resolution_clock::time_point start;
std::chrono::high_resolution_clock::time_point last;
std::chrono::high_resolution_clock::time_point now;
};
// Adapter to support movement of surfaces.
class Moveable
{
public:
Moveable() {}
Moveable(std::shared_ptr<ms::Surface> const& s, const geom::Size& display_size,
float dx, float dy, const glm::vec3& rotation_axis, float alpha_offset)
: surface(s), display_size(display_size),
x(s->top_left().x.as_int()),
y(s->top_left().y.as_int()),
w(s->size().width.as_int()),
h(s->size().height.as_int()),
dx{dx},
dy{dy},
rotation_axis(rotation_axis),
alpha_offset{alpha_offset}
{
}
void step()
{
stop_watch.stop();
float elapsed_sec = stop_watch.elapsed_seconds_since_last_restart();
float total_elapsed_sec = stop_watch.elapsed_seconds_since_start();
stop_watch.restart();
bool should_update = true;
float new_x = x + elapsed_sec * dx;
float new_y = y + elapsed_sec * dy;
if (new_x < 0.0 || new_x + w > display_size.width.as_uint32_t())
{
dx = -dx;
should_update = false;
}
if (new_y < 0.0 || new_y + h > display_size.height.as_uint32_t())
{
dy = -dy;
should_update = false;
}
if (should_update)
{
surface->move_to({new_x, new_y});
x = new_x;
y = new_y;
}
glm::mat4 trans = glm::rotate(glm::mat4(1.0f),
glm::radians(total_elapsed_sec * 120.0f),
rotation_axis);
surface->set_transformation(trans);
float const alpha_amplitude = (1.0f - min_alpha) / 2.0f;
surface->set_alpha(min_alpha + alpha_amplitude +
alpha_amplitude *
sin(alpha_offset + 2 * M_PI * total_elapsed_sec /
3.0));
}
private:
std::shared_ptr<ms::Surface> surface;
geom::Size display_size;
float x;
float y;
float w;
float h;
float dx;
float dy;
StopWatch stop_watch;
glm::vec3 rotation_axis;
float alpha_offset;
};
void callback_when_started(mir::Server& server, std::function<void()> callback)
{
struct ServerStatusListener : mir::ServerStatusListener
{
ServerStatusListener(std::function<void()> callback) :
callback(callback) {}
virtual void paused() override {}
virtual void resumed() override {}
virtual void started() override {callback(); callback = []{}; }
virtual void ready_for_user_input() override {}
virtual void stop_receiving_input() override {}
std::function<void()> callback;
};
{
return std::make_shared<ServerStatusListener>(callback);
});
}
class RenderSurfacesDisplayBufferCompositor : public mc::DisplayBufferCompositor
{
public:
RenderSurfacesDisplayBufferCompositor(
std::unique_ptr<DisplayBufferCompositor> db_compositor,
std::vector<Moveable>& moveables)
: db_compositor{std::move(db_compositor)},
moveables(moveables),
frames{0}
{
}
void composite(mc::SceneElementSequence&& scene_sequence) override
{
while (!created) std::this_thread::yield();
stop_watch.stop();
if (stop_watch.elapsed_seconds_since_last_restart() >= 1)
{
std::cout << "FPS: " << frames << " Frame Time: " << 1.0 / frames << std::endl;
frames = 0;
stop_watch.restart();
}
db_compositor->composite(std::move(scene_sequence));
for (auto& m : moveables)
m.step();
frames++;
}
private:
std::unique_ptr<DisplayBufferCompositor> const db_compositor;
StopWatch stop_watch;
std::vector<Moveable>& moveables;
uint32_t frames;
};
class RenderSurfacesDisplayBufferCompositorFactory : public mc::DisplayBufferCompositorFactory
{
public:
RenderSurfacesDisplayBufferCompositorFactory(
std::shared_ptr<mc::DisplayBufferCompositorFactory> const& factory,
std::vector<Moveable>& moveables)
: factory{factory},
moveables(moveables)
{
}
std::unique_ptr<mc::DisplayBufferCompositor> create_compositor_for(mg::DisplayBuffer& display_buffer)
{
auto compositor = factory->create_compositor_for(display_buffer);
auto raw = new RenderSurfacesDisplayBufferCompositor(
std::move(compositor), moveables);
return std::unique_ptr<RenderSurfacesDisplayBufferCompositor>(raw);
}
private:
std::shared_ptr<mc::DisplayBufferCompositorFactory> const factory;
std::vector<Moveable>& moveables;
};
// Extend the default configuration to manage moveables.
class RenderSurfacesServer : private mir::Server
{
std::shared_ptr<mir::input::EventFilter> const quit_filter{me::make_quit_filter_for(*this)};
public:
RenderSurfacesServer(int argc, char const** argv)
{
// Decorate the DefaultDisplayBufferCompositor in order to move surfaces.
wrap_display_buffer_compositor_factory([this](std::shared_ptr<mc::DisplayBufferCompositorFactory> const& wrapped)
{
return std::make_shared<RenderSurfacesDisplayBufferCompositorFactory>(
wrapped,
moveables);
});
add_configuration_option(surfaces_to_render, "Number of surfaces to render", 5);
// Provide the command line and run the *this
set_command_line(argc, argv);
setenv("MIR_SERVER_NO_FILE", "", 1);
// If there's a server available, try connecting to it
if (auto const socket = getenv("MIR_SOCKET"))
setenv("MIR_SERVER_HOST_SOCKET", socket, 0);
// Unless the compositor starts before we create the surfaces it won't respond to
// the change notification that causes.
callback_when_started(*this, [this] { create_surfaces(); });
apply_settings();
}
// New function to initialize moveables with surfaces
void create_surfaces()
try
{
moveables.resize(get_options()->get<int>(surfaces_to_render));
std::cout << "Rendering " << moveables.size() << " surfaces" << std::endl;
auto const display = the_display();
auto const buffer_stream_factory = the_buffer_stream_factory();
auto const surface_factory = the_surface_factory();
auto const surface_stack = the_surface_stack();
auto const gl_context = as_context_source(the_display().get())->create_gl_context();
/* TODO: Get proper configuration */
geom::Rectangles view_area;
{
{
view_area.add(db.view_area());
});
});
geom::Size const display_size{view_area.bounding_rectangle().size};
uint32_t const surface_side{300};
geom::Size const surface_size{surface_side, surface_side};
float const angular_step = 2.0 * M_PI / moveables.size();
float const w = display_size.width.as_uint32_t();
float const h = display_size.height.as_uint32_t();
auto const surface_pf = supported_pixel_formats()[0];
int i = 0;
for (auto& m : moveables)
{
auto params = ms::a_surface()
.of_size(surface_size)
.of_pixel_format(surface_pf)
.of_buffer_usage(mg::BufferUsage::hardware);
mg::BufferProperties properties{params.size, params.pixel_format, params.buffer_usage};
struct NullBufferSink : mf::BufferSink
{
void send_buffer(mf::BufferStreamId, mg::Buffer&, mg::BufferIpcMsgType) override {}
void add_buffer(mg::Buffer&) override {}
void remove_buffer(mg::Buffer&) override {}
void update_buffer(mg::Buffer&) override {}
void error_buffer(mg::BufferProperties const&, std::string const&) override {}
};
auto buffers = buffer_stream_factory->create_buffer_map(std::make_shared<NullBufferSink>());
auto const stream = buffer_stream_factory->create_buffer_stream({}, buffers, properties);
auto const surface = surface_factory->create_surface(
{ms::StreamInfo{stream, {}, {}}}, params);
surface_stack->add_surface(surface, params.input_mode);
{
mg::Buffer* buffer{nullptr};
auto const complete = [&](mg::Buffer* new_buf){ buffer = new_buf; };
surface->primary_buffer_stream()->swap_buffers(buffer, complete); // Fetch buffer for rendering
if (!buffer)
{
auto buffer_id = buffers->add_buffer(properties);
buffer = (*buffers)[buffer_id].get();
}
{
gl_context->make_current();
mt::ImageRenderer img_renderer{mir_image.pixel_data,
geom::Size{mir_image.width, mir_image.height},
mir_image.bytes_per_pixel};
mt::BufferRenderTarget brt{*buffer};
brt.make_current();
img_renderer.render();
gl_context->release_current();
}
surface->primary_buffer_stream()->swap_buffers(buffer, complete); // Post rendered buffer
}
/*
* Place each surface at a different starting location and give it a
* different speed, rotation and alpha offset.
*/
uint32_t const x = w * (0.5 + 0.25 * cos(i * angular_step)) - surface_side / 2.0;
uint32_t const y = h * (0.5 + 0.25 * sin(i * angular_step)) - surface_side / 2.0;
surface->move_to({x, y});
m = Moveable(surface, display_size,
cos(0.1f + i * M_PI / 6.0f) * w / 3.0f,
sin(0.1f + i * M_PI / 6.0f) * h / 3.0f,
glm::vec3{(i % 3 == 0) * 1.0f, (i % 3 == 1) * 1.0f, (i % 3 == 2) * 1.0f},
2.0f * M_PI * cos(i));
++i;
}
created = true;
}
catch (...)
{
exit(EXIT_FAILURE);
}
private:
std::vector<Moveable> moveables;
};
}
int main(int argc, char const** argv)
try
{
RenderSurfacesServer render_surfaces{argc, argv};
render_surfaces.run();
return render_surfaces.exited_normally() ? EXIT_SUCCESS : EXIT_FAILURE;
}
catch (...)
{
return EXIT_FAILURE;
}

Copyright © 2012-2016 Canonical Ltd.
Generated on Sat Dec 3 12:48:59 UTC 2016