14cairo_user_data_key_t
constexpr key{};
16class PersistentPixelStreamer :
public PixelStreamer
31 glGenBuffers(1, &
pbo);
32 glBindBuffer(GL_PIXEL_UNPACK_BUFFER,
pbo);
33 glBufferStorage(GL_PIXEL_UNPACK_BUFFER,
bufsize,
nullptr, GL_MAP_READ_BIT | GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT);
34 data = (
unsigned char*)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0,
bufsize, GL_MAP_READ_BIT | GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_FLUSH_EXPLICIT_BIT);
41 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
42 glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
43 glDeleteBuffers(1, &pbo);
50 sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
52 auto ret = glClientWaitSync(sync, GL_SYNC_FLUSH_COMMANDS_BIT, 0);
53 if (ret == GL_CONDITION_SATISFIED || ret == GL_ALREADY_SIGNALED) {
87 PersistentPixelStreamer()
95 Method get_method()
const override {
return Method::Persistent; }
103 assert(sizeup < bufsize);
107 for (
int i = 0; i <
buffers.size(); i++) {
108 if (i != current_buffer && buffers[i].
refs == 0 && !buffers[i].
ready) {
114 if (buffers[current_buffer].
off + sizeup <= bufsize) {
122 if (buffers[current_buffer].
refs == 0) {
127 for (
int i = 0; i <
buffers.size(); i++) {
128 if (i != current_buffer && buffers[i].
refs == 0 && buffers[i].
ready) {
147 auto choose_mapping = [&,
this] {
148 for (
int i = 0; i <
mappings.size(); i++) {
149 if (!mappings[i].
used) {
159 auto mapping = choose_mapping();
171 cairo_surface_set_user_data(
surface->cobj(), &
key, (
void*)(uintptr_t)mapping,
nullptr);
176 void finish(Cairo::RefPtr<Cairo::ImageSurface>
surface,
bool junk)
override
179 auto mapping = (int)(uintptr_t)cairo_surface_get_user_data(
surface->cobj(), &
key);
188 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, b.pbo);
189 glFlushMappedBufferRange(GL_PIXEL_UNPACK_BUFFER, m.off, m.size);
197 glPixelStorei(GL_UNPACK_ROW_LENGTH, m.stride / 4);
198 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m.width, m.height, GL_BGRA, GL_UNSIGNED_BYTE, (
void*)(uintptr_t)m.off);
202 if (m.buf != current_buffer && b.refs == 0) {
204 b.sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
208 for (
int i = 0; i <
buffers.size(); i++) {
209 if (i != current_buffer && i != m.buf && buffers[i].refs == 0 && !buffers[i].ready) {
215 ~PersistentPixelStreamer()
override
218 for (
int i = 0; i <
buffers.size(); i++) {
219 if (i != current_buffer && buffers[i].
refs == 0 && !buffers[i].
ready && buffers[i].
sync) {
220 glDeleteSync(buffers[i].
sync);
234class AsynchronousPixelStreamer :
public PixelStreamer
240 static int constexpr bucket_maxsize(
int b) {
return minbufsize * (1 << b); }
249 glGenBuffers(1, &pbo);
250 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
251 glBufferData(GL_PIXEL_UNPACK_BUFFER,
size,
nullptr, GL_STREAM_DRAW);
252 data = (
unsigned char*)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0,
size, GL_MAP_READ_BIT | GL_MAP_WRITE_BIT);
257 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
258 glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
259 glDeleteBuffers(1, &pbo);
283 Method get_method()
const override {
return Method::Asynchronous; }
300 if (!b.spares.empty()) {
302 buf = std::move(b.spares.back());
314 if (b.used > b.high_use_count) {
316 b.high_use_count = b.used;
320 auto choose_mapping = [&,
this] {
321 for (
int i = 0; i <
mappings.size(); i++) {
322 if (!mappings[i].
used) {
330 auto mapping = choose_mapping();
334 m.buf = std::move(
buf);
340 auto surface = Cairo::ImageSurface::create(m.buf.data, Cairo::Surface::Format::ARGB32, m.width, m.height, m.stride);
341 cairo_surface_set_user_data(
surface->cobj(), &
key, (
void*)(uintptr_t)mapping,
nullptr);
345 void finish(Cairo::RefPtr<Cairo::ImageSurface>
surface,
bool junk)
override
347 auto mapping = (int)(uintptr_t)cairo_surface_get_user_data(
surface->cobj(), &
key);
354 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, m.buf.pbo);
355 glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
359 glPixelStorei(GL_UNPACK_ROW_LENGTH, m.stride / 4);
360 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m.width, m.height, GL_BGRA, GL_UNSIGNED_BYTE,
nullptr);
367 auto size = bucket_maxsize(m.bucket);
368 glBufferData(GL_PIXEL_UNPACK_BUFFER,
size,
nullptr, GL_STREAM_DRAW);
369 m.buf.data = (
unsigned char*)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0,
size, GL_MAP_READ_BIT | GL_MAP_WRITE_BIT);
372 b.spares.emplace_back(std::move(m.buf));
377 if (expire_timer >= expire_timeout) {
381 int max_spares = b.high_use_count - b.used;
382 assert(max_spares >= 0);
383 if (b.spares.size() > max_spares) {
384 for (
int i = max_spares; i < b.spares.size(); i++) {
385 b.spares[i].destroy();
387 b.spares.resize(max_spares);
389 b.high_use_count = b.used;
394 ~AsynchronousPixelStreamer()
override
405class SynchronousPixelStreamer :
public PixelStreamer
410 std::vector<unsigned char>
data;
416 Method get_method()
const override {
return Method::Synchronous; }
420 auto choose_mapping = [&,
this] {
421 for (
int i = 0; i <
mappings.size(); i++) {
422 if (!mappings[i].
used) {
430 auto mapping = choose_mapping();
436 m.stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, m.width);
437 m.size = m.stride * m.height;
438 m.data.resize(m.size);
440 auto surface = Cairo::ImageSurface::create(&m.data[0], Cairo::Surface::Format::ARGB32, m.width, m.height, m.stride);
441 cairo_surface_set_user_data(
surface->cobj(), &
key, (
void*)(uintptr_t)mapping,
nullptr);
445 void finish(Cairo::RefPtr<Cairo::ImageSurface>
surface,
bool junk)
override
447 auto mapping = (int)(uintptr_t)cairo_surface_get_user_data(
surface->cobj(), &
key);
453 glPixelStorei(GL_UNPACK_ROW_LENGTH, m.stride / 4);
454 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m.width, m.height, GL_BGRA, GL_UNSIGNED_BYTE, &m.data[0]);
466 int ver = epoxy_gl_version();
469 if (ver >= 30 || epoxy_has_gl_extension(
"GL_ARB_map_buffer_range")) {
471 if (ver >= 44 || (epoxy_has_gl_extension(
"GL_ARB_buffer_storage") &&
472 epoxy_has_gl_extension(
"GL_ARB_texture_storage") &&
473 epoxy_has_gl_extension(
"GL_ARB_SYNC")))
475 return std::make_unique<PersistentPixelStreamer>();
477 std::cerr <<
"Persistent PixelStreamer not available" << std::endl;
480 return std::make_unique<AsynchronousPixelStreamer>();
482 std::cerr <<
"Asynchronous PixelStreamer not available" << std::endl;
485 return std::make_unique<SynchronousPixelStreamer>();
Cairo::RefPtr< Cairo::ImageSurface > surface
Two-dimensional point with integer coordinates.
constexpr IntCoord x() const noexcept
constexpr IntCoord y() const noexcept
int constexpr floorlog2(T x)
Returns floor(log_2(x)), assuming x >= 1.
T constexpr round_up(T a, T b)
Returns a rounded up to the nearest multiple of b, assuming b >= 1.
Helper class to stream background task notifications as a series of messages.
static cairo_user_data_key_t key
std::vector< Bucket > buckets
std::vector< Buffer > spares
static int constexpr minbufsize
std::vector< Mapping > mappings
std::vector< Buffer > buffers
static int constexpr expire_timeout
static int constexpr bufsize
Geom::IntPoint dimensions(const Cairo::RefPtr< Cairo::ImageSurface > &surface)