summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--glcapture.c36
-rw-r--r--hooks.h11
2 files changed, 46 insertions, 1 deletions
diff --git a/glcapture.c b/glcapture.c
index 2dd202d..a271f99 100644
--- a/glcapture.c
+++ b/glcapture.c
@@ -51,6 +51,9 @@ static bool DROP_FRAMES = true;
// Multiplier for system clock (MONOTONIC, RAW) can be used to make recordings of replays smoother (or speed hack)
static double SPEED_HACK = 1.0;
+// If your video is upside down set this to false
+static bool FLIP_VIDEO = true;
+
// Path for the fifo where glcapture will output the rawmux data
static const char *FIFO_PATH = "/tmp/glcapture.fifo";
@@ -70,12 +73,14 @@ static const bool ENABLED_STREAMS[STREAM_LAST] = {
#define WARN(x, ...) do { warn("glcapture: "x, ##__VA_ARGS__); } while (0)
#define WARNX(x, ...) do { warnx("glcapture: "x, ##__VA_ARGS__); } while (0)
#define ERRX(x, y, ...) do { errx(x, "glcapture: "y, ##__VA_ARGS__); } while (0)
+#define ERR(x, y, ...) do { err(x, "glcapture: "y, ##__VA_ARGS__); } while (0)
#define WARN_ONCE(x, ...) do { static bool o = false; if (!o) { WARNX(x, ##__VA_ARGS__); o = true; } } while (0)
// "entrypoints" exposed to hooks.h
static void swap_buffers(void);
static void alsa_writei(snd_pcm_t *pcm, const void *buffer, const snd_pcm_uframes_t size, const char *caller);
static uint64_t get_fake_time_ns(void);
+static __thread GLint LAST_FRAMEBUFFER_BLIT[8];
#include "hooks.h"
#include "glwrangle.h"
@@ -291,6 +296,34 @@ write_data(const struct frame_info *info, const void *buffer, const size_t size)
pthread_mutex_unlock(&mutex);
}
+void
+flip_pixels_if_needed(const GLint view[4], uint8_t *pixels, const uint32_t width, const uint32_t height, const uint8_t components)
+{
+ // Will detect at least wine which blits viewport sized framebuffer at the end already flipped
+ if (!FLIP_VIDEO ||
+ (LAST_FRAMEBUFFER_BLIT[0] == 0 && LAST_FRAMEBUFFER_BLIT[1] == 0 &&
+ LAST_FRAMEBUFFER_BLIT[2] == view[2] && LAST_FRAMEBUFFER_BLIT[3] == view[3] &&
+ LAST_FRAMEBUFFER_BLIT[4] == 0 && LAST_FRAMEBUFFER_BLIT[5] == view[3] &&
+ LAST_FRAMEBUFFER_BLIT[6] == view[2] && LAST_FRAMEBUFFER_BLIT[7] == 0))
+ return;
+
+ // Sadly I can't come up with any reliable way to do this on GPU on all possible OpenGL versions and variants.
+ const uint32_t stride = width * components;
+ static __thread struct { size_t size; uint8_t *data; } row;
+ if (row.size < stride) {
+ if (!(row.data = realloc(row.data, stride)))
+ ERR(EXIT_FAILURE, "realloc(%p, %u)", row.data, stride);
+
+ row.size = stride;
+ }
+
+ for (uint8_t *lo = pixels, *hi = pixels + (height - 1) * stride; lo < hi; lo += stride, hi -= stride) {
+ memcpy(row.data, lo, stride);
+ memcpy(lo, hi, stride);
+ memcpy(hi, row.data, stride);
+ }
+}
+
static void
capture_frame_pbo(struct gl *gl, const GLint view[4], const uint64_t ts)
{
@@ -348,10 +381,11 @@ capture_frame_pbo(struct gl *gl, const GLint view[4], const uint64_t ts)
.video.fps = FPS,
};
- const void *buf;
+ void *buf;
const size_t size = info.video.width * info.video.height * frame.components;
glBindBuffer(GL_PIXEL_PACK_BUFFER, gl->pbo[gl->active].obj);
if ((buf = glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, size, GL_MAP_READ_BIT))) {
+ flip_pixels_if_needed(view, buf, info.video.width, info.video.height, frame.components);
write_data(&info, buf, size);
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
gl->pbo[gl->active].written = false;
diff --git a/hooks.h b/hooks.h
index 6edc94b..c0da47e 100644
--- a/hooks.h
+++ b/hooks.h
@@ -1,6 +1,7 @@
#pragma once
static void* (*_dlsym)(void*, const char*);
+static void (*_glBlitFramebuffer)(GLint, GLint, GLint, GLint, GLint, GLint, GLint, GLint, GLbitfield, GLenum);
static EGLBoolean (*_eglSwapBuffers)(EGLDisplay, EGLSurface);
static __eglMustCastToProperFunctionPointerType (*_eglGetProcAddress)(const char*);
static void (*_glXSwapBuffers)(Display*, GLXDrawable);
@@ -16,6 +17,15 @@ static void hook_function(void**, const char*, const bool);
#define HOOK(x) hook_function((void**)&_##x, #x, false)
+void
+glBlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter)
+{
+ HOOK(glBlitFramebuffer);
+ const GLint a[] = { srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1 };
+ memcpy(LAST_FRAMEBUFFER_BLIT, a, sizeof(a));
+ _glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
+}
+
EGLBoolean
eglSwapBuffers(EGLDisplay dpy, EGLSurface surface)
{
@@ -109,6 +119,7 @@ store_real_symbol_and_return_fake_symbol(const char *symbol, void *ret)
if (0) {}
#define SET_IF_NOT_HOOKED(x, y) do { if (!_##x) { _##x = y; WARNX("SET %s to %p", #x, y); } } while (0)
#define FAKE_SYMBOL(x) else if (!strcmp(symbol, #x)) { SET_IF_NOT_HOOKED(x, ret); return x; }
+ FAKE_SYMBOL(glBlitFramebuffer)
FAKE_SYMBOL(eglSwapBuffers)
FAKE_SYMBOL(eglGetProcAddress)
FAKE_SYMBOL(glXSwapBuffers)