Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 161 additions & 15 deletions app/src/main/cpp/lorie/InitOutput.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,13 @@ typedef struct {
} root;

Bool dri3;
Bool gpuPresentDisabled;

uint64_t vblank_interval;
struct xorg_list vblank_queue;
uint64_t current_msc;

uint64_t gpuCopySerialCounter;
} lorieScreenInfo;

ScreenPtr pScreenPtr;
Expand Down Expand Up @@ -110,12 +113,35 @@ typedef struct {
#define LORIE_PIXMAP_PRIV_FROM_PIXMAP(pixmap) (pixmap ? ((LoriePixmapPriv*) exaGetPixmapDriverPrivate(pixmap)) : NULL)
#define LORIE_BUFFER_FROM_PIXMAP(pixmap) (pixmap ? ((LoriePixmapPriv*) exaGetPixmapDriverPrivate(pixmap))->buffer : NULL)

static LorieBuffer *lorieEnsureGpuSampleable(PixmapPtr pixmap, int8_t type) {
LoriePixmapPriv *priv = LORIE_PIXMAP_PRIV_FROM_PIXMAP(pixmap);
const LorieBuffer_Desc *desc;
if (!priv || !priv->buffer || priv->mem)
return NULL;

desc = LorieBuffer_description(priv->buffer);
if (desc->type == LORIEBUFFER_REGULAR) {
LorieBuffer_convert(priv->buffer, type, AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM);
if (desc->type != LORIEBUFFER_REGULAR) {
// LorieBuffer_convert does not report status but it does not let the type change in the case of error.
pScreenPtr->ModifyPixmapHeader(pixmap, 0, 0, 0, 0, desc->stride * 4, NULL);
LorieBuffer_lock(priv->buffer, &priv->locked);
}
}

return desc->type == type ? priv->buffer : NULL;
}

static Bool lorieServerDebugEnabled = FALSE;

void OsVendorInit(void) {
pthread_mutexattr_t mutex_attr;

if (lorieScreen.stateFd != -1) // already initialized
return;

lorieServerDebugEnabled = getenv("TERMUX_X11_DEBUG") != NULL;

if (-1 == (lorieScreen.stateFd = LorieBuffer_createRegion("xserver", sizeof(*lorieScreen.state)))) {
dprintf(2, "FATAL: Failed to allocate server state.\n");
_exit(1);
Expand Down Expand Up @@ -284,6 +310,7 @@ void ddxUseMsg(void) {
ErrorF("-disable-dri3 disabling DRI3 support (to let lavapipe work)\n");
ErrorF("-force-sysvshm force using SysV shm syscalls\n");
ErrorF("-check-drawing run server only able to draw some test image (for testing if rendering root window works or not),\n");
ErrorF("-disable-gpu-present disable offloading Present copies to the GPU, always use the CPU path\n");
}

int ddxProcessArgument(unused int argc, unused char *argv[], unused int i) {
Expand Down Expand Up @@ -317,6 +344,11 @@ int ddxProcessArgument(unused int argc, unused char *argv[], unused int i) {
return 1;
}

if (strcmp(argv[i], "-disable-gpu-present") == 0) {
pvfb->gpuPresentDisabled = TRUE;
return 1;
}

return 0;
}

Expand Down Expand Up @@ -472,11 +504,16 @@ static Bool lorieRedraw(__unused ClientPtr pClient, __unused void *closure) {
return TRUE;
}

static uint64_t gpuCopyAttempts = 0, gpuCopyOffloads = 0;

static CARD32 lorieFramecounter(unused OsTimerPtr timer, unused CARD32 time, unused void *arg) {
if (pvfb->state->renderedFrames)
log(INFO, "%d frames in 5.0 seconds = %.1f FPS",
pvfb->state->renderedFrames, ((float) pvfb->state->renderedFrames) / 5);
if (pvfb->state->renderedFrames || gpuCopyAttempts)
log(INFO, gpuCopyAttempts ? "%d frames in 5.0 seconds = %.1f FPS, %llu/%llu present copies offloaded to GPU"
: "%d frames in 5.0 seconds = %.1f FPS",
pvfb->state->renderedFrames, ((float) pvfb->state->renderedFrames) / 5,
(unsigned long long) gpuCopyOffloads, (unsigned long long) gpuCopyAttempts);
pvfb->state->renderedFrames = 0;
gpuCopyAttempts = gpuCopyOffloads = 0;
return 5000;
}

Expand Down Expand Up @@ -803,6 +840,111 @@ static void loriePerformVblanks(void) {
}
}

// Tries to offload a Present "copy" operation (present_execute_copy) to the renderer's GPU
// context instead of doing a CPU CopyArea here. dst is whatever GetWindowPixmap(window) is - root
// for a plain window, or a Composite-redirected window's own backing pixmap. Returns FALSE
// (caller falls back to the regular CPU present_copy_region) whenever either buffer isn't
// GPU-sampleable, or the deferred copy queue is currently full.
Bool lorieTryScheduleGpuCopy(PixmapPtr pixmap, PixmapPtr dst, RegionPtr update, int16_t x_off, int16_t y_off,
uint64_t *out_serial, void **out_dst_buffer) {
LorieBuffer *srcBuffer, *dstBuffer;
LoriePixmapPriv *priv;
const LorieBuffer_Desc *desc, *dstDesc;
LorieGpuCopyEntry *entry;
BoxRec fullBox;
BoxPtr box;
int numRects, i;
uint32_t writeIndex, readIndex;

if (pvfb->gpuPresentDisabled || pvfb->root.legacyDrawing) {
gpuCopyAttempts++;
return FALSE;
}

if (!lorieConnectionAlive()) {
// No renderer to drain the queue, so fall back to CPU copy.
gpuCopyAttempts++;
return FALSE;
}

if (!(srcBuffer = lorieEnsureGpuSampleable(pixmap, LORIEBUFFER_AHARDWAREBUFFER)) ||
!(dstBuffer = lorieEnsureGpuSampleable(dst, LORIEBUFFER_AHARDWAREBUFFER))) {
gpuCopyAttempts++;
return FALSE;
}
priv = LORIE_PIXMAP_PRIV_FROM_PIXMAP(pixmap);
desc = LorieBuffer_description(srcBuffer);
dstDesc = LorieBuffer_description(dstBuffer);

if (update) {
numRects = RegionNumRects(update);
box = RegionRects(update);
} else {
fullBox = (BoxRec) { 0, 0, (short) pixmap->drawable.width, (short) pixmap->drawable.height };
numRects = 1;
box = &fullBox;
}

if (numRects <= 0 || numRects > LORIE_GPU_COPY_MAX_RECTS) {
gpuCopyAttempts++;
return FALSE;
}

writeIndex = pvfb->state->gpuCopyQueue.writeIndex;
readIndex = pvfb->state->gpuCopyQueue.readIndex;
if (writeIndex - readIndex >= LORIE_GPU_COPY_QUEUE_CAPACITY) {
gpuCopyAttempts++;
return FALSE;
}

// Make sure the renderer has (or will have) this texture. Idempotent if already registered.
lorieRegisterBuffer(srcBuffer);
// Extra reference: keeps the LorieBuffer struct alive on this side until lorieGpuCopyAck()
// releases it, independently from the X pixmap's own lifetime.
LorieBuffer_acquire(srcBuffer);
// Root already has its own lifecycle (recreated on resize, kept alive by pScreenPtr->devPrivate)
// - an extra reference here would outlive a resize and let the renderer keep finding a stale,
// already-destroyed root buffer. Redirected-window destinations have no such guarantee, so they
// still need registering and an extra reference.
Bool dstIsRoot = dst == pScreenPtr->devPrivate;
if (!dstIsRoot) {
lorieRegisterBuffer(dstBuffer);
LorieBuffer_acquire(dstBuffer);
}
*out_dst_buffer = dstIsRoot ? NULL : dstBuffer;

entry = &pvfb->state->gpuCopyQueue.entries[writeIndex % LORIE_GPU_COPY_QUEUE_CAPACITY];
entry->serial = ++pvfb->gpuCopySerialCounter;
entry->srcBufferId = desc->id;
entry->dstBufferId = dstDesc->id;
entry->xOff = x_off;
entry->yOff = y_off;
entry->numRects = (uint16_t) numRects;
for (i = 0; i < numRects; i++)
entry->rects[i] = (LorieGpuCopyRect) { box[i].x1, box[i].y1, box[i].x2, box[i].y2 };

__sync_synchronize(); // publish entry contents before the renderer can see the new writeIndex
pvfb->state->gpuCopyQueue.writeIndex = writeIndex + 1;
pthread_cond_signal(rendererCond);

*out_serial = entry->serial;
gpuCopyAttempts++;
gpuCopyOffloads++;
return TRUE;
}

Bool lorieGpuCopyIsDone(uint64_t serial) {
return pvfb->state->gpuCopyQueue.completedSerial >= serial;
}

void lorieGpuCopyAck(PixmapPtr pixmap, void *dst_buffer) {
LoriePixmapPriv *priv = LORIE_PIXMAP_PRIV_FROM_PIXMAP(pixmap);
if (priv && priv->buffer)
LorieBuffer_release(priv->buffer);
if (dst_buffer)
LorieBuffer_release((LorieBuffer *) dst_buffer);
}

Bool loriePresentFlip(__unused RRCrtcPtr crtc, __unused uint64_t event_id, __unused uint64_t target_msc, PixmapPtr pixmap, __unused Bool sync_flip) {
LoriePixmapPriv* priv = (LoriePixmapPriv*) exaGetPixmapDriverPrivate(pixmap);
if (!priv || !priv->buffer || priv->mem || pvfb->root.width != pixmap->drawable.width || pvfb->root.width != pixmap->drawable.height)
Expand All @@ -813,16 +955,8 @@ Bool loriePresentFlip(__unused RRCrtcPtr crtc, __unused uint64_t event_id, __unu
if (desc->type == LORIEBUFFER_FD && priv->imported && !(forceFlip && strcmp(forceFlip, "1") == 0))
return FALSE; // For some reason it does not work fine with turnip.

if (desc->type == LORIEBUFFER_REGULAR) {
// Regular buffers can not be shared to activity, we must explicitly convert LorieBuffer to FD or AHardwareBuffer
int8_t type = pvfb->root.legacyDrawing ? LORIEBUFFER_FD : LORIEBUFFER_AHARDWAREBUFFER;
LorieBuffer_convert(priv->buffer, type, AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM);
if (desc->type != LORIEBUFFER_REGULAR) {
// LorieBuffer_convert does not report status but it does not let the type change in the case of error.
pScreenPtr->ModifyPixmapHeader(pixmap, 0, 0, 0, 0, desc->stride * 4, NULL);
LorieBuffer_lock(priv->buffer, &priv->locked);
}
}
// Regular buffers can not be shared to activity, we must explicitly convert LorieBuffer to FD or AHardwareBuffer
lorieEnsureGpuSampleable(pixmap, pvfb->root.legacyDrawing ? LORIEBUFFER_FD : LORIEBUFFER_AHARDWAREBUFFER);

if (desc->type != LORIEBUFFER_FD && desc->type != LORIEBUFFER_AHARDWAREBUFFER)
return FALSE;
Expand Down Expand Up @@ -905,9 +1039,17 @@ Bool lorieModifyPixmapHeader(PixmapPtr pPix, __unused int w, __unused int h, __u
return FALSE;
}

// Whether a CPU access to pPix could race a GPU write from the renderer, and so needs state->lock.
static inline __always_inline Bool lorieNeedsGpuLock(PixmapPtr pPix, LoriePixmapPriv *priv, int index) {
if (pScreenPtr->GetScreenPixmap(pScreenPtr) == pPix)
return index == EXA_PREPARE_DEST || (!pvfb->gpuPresentDisabled && !pvfb->root.legacyDrawing);
return !pvfb->root.legacyDrawing && priv->buffer &&
LorieBuffer_description(priv->buffer)->type == LORIEBUFFER_AHARDWAREBUFFER;
}

Bool loriePrepareAccess(PixmapPtr pPix, int index) {
LoriePixmapPriv *priv = exaGetPixmapDriverPrivate(pPix);
if (index == EXA_PREPARE_DEST && pScreenPtr->GetScreenPixmap(pScreenPtr) == pPix)
if (lorieNeedsGpuLock(pPix, priv, index))
lorie_mutex_lock(&pvfb->state->lock, &pvfb->state->lockingPid);

if (!priv->locked && !priv->mem) {
Expand All @@ -926,7 +1068,7 @@ Bool loriePrepareAccess(PixmapPtr pPix, int index) {

void lorieFinishAccess(PixmapPtr pPix, int index) {
LoriePixmapPriv *priv = exaGetPixmapDriverPrivate(pPix);
if (index == EXA_PREPARE_DEST && pScreenPtr->GetScreenPixmap(pScreenPtr) == pPix)
if (lorieNeedsGpuLock(pPix, priv, index))
lorie_mutex_unlock(&pvfb->state->lock, &pvfb->state->lockingPid);

if (!priv->wasLocked) {
Expand Down Expand Up @@ -972,6 +1114,8 @@ static PixmapPtr loriePixmapFromFds(ScreenPtr screen, CARD8 num_fds, const int *
if (modifier == DRM_FORMAT_MOD_INVALID || modifier == DRM_FORMAT_MOD_LINEAR || modifier == RAW_MMAPPABLE_FD) {
check(!(priv->buffer = LorieBuffer_wrapFileDescriptor(width, strides[0]/4, height, AHARDWAREBUFFER_FORMAT_B8G8R8A8_UNORM, fds[0], offsets[0])), "DRI3: LorieBuffer_wrapAHardwareBuffer failed.");
screen->ModifyPixmapHeader(pixmap, width, height, 0, 0, strides[0], NULL);
if (lorieServerDebugEnabled)
log(INFO, "DRI3: imported raw fd, modifier %llu, %ux%u stride %u", (unsigned long long) modifier, width, height, strides[0]);
return pixmap;
}

Expand All @@ -997,6 +1141,8 @@ static PixmapPtr loriePixmapFromFds(ScreenPtr screen, CARD8 num_fds, const int *
check(!(priv->buffer = LorieBuffer_wrapAHardwareBuffer(buffer)), "DRI3: LorieBuffer_wrapAHardwareBuffer failed.");

screen->ModifyPixmapHeader(pixmap, desc.width, desc.height, 0, 0, desc.stride * 4, NULL);
if (lorieServerDebugEnabled)
log(INFO, "DRI3: imported AHardwareBuffer, modifier %llu, %ux%u stride %u", (unsigned long long) modifier, desc.width, desc.height, desc.stride);
}

return pixmap;
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/cpp/lorie/activity.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#define log(prio, ...) __android_log_print(ANDROID_LOG_ ## prio, "LorieNative", __VA_ARGS__)

extern volatile int conn_fd; // The only variable from shared with X server code.
bool lorieDebugEnabled = false;

static struct {
jclass self;
Expand Down Expand Up @@ -248,6 +249,7 @@ static jboolean connected(__unused JNIEnv* env,__unused jclass clazz) {

static void startLogcat(JNIEnv *env, __unused jobject cls, jint fd) {
log(DEBUG, "Starting logcat with output to given fd");
lorieDebugEnabled = true;

switch(fork()) {
case -1:
Expand Down
8 changes: 6 additions & 2 deletions app/src/main/cpp/lorie/buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ __LIBC_HIDDEN__ LorieBuffer* LorieBuffer_allocate(int32_t width, int32_t height,
return NULL;
} else if (type == LORIEBUFFER_AHARDWAREBUFFER) {
AHardwareBuffer_Desc desc = { .width = width, .height = height, .format = format, .layers = 1,
.usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE };
.usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER };
int err = AHardwareBuffer_allocate(&desc, &ahardwarebuffer);
if (err != 0)
dprintf(2, "FATAL: failed to allocate AHardwareBuffer (width %d height %d format %d): error %d\n", width, height, format, err);
Expand Down Expand Up @@ -228,7 +228,7 @@ __LIBC_HIDDEN__ void LorieBuffer_convert(LorieBuffer* buffer, int8_t type, int8_
} else {
AHardwareBuffer *b = NULL;
AHardwareBuffer_Desc desc = { .width = buffer->desc.width, .height = buffer->desc.height, .format = format, .layers = 1,
.usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE };
.usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER };
int err = AHardwareBuffer_allocate(&desc, &b);
if (err != 0)
return;
Expand Down Expand Up @@ -425,6 +425,10 @@ __LIBC_HIDDEN__ void LorieBuffer_bindTexture(LorieBuffer *buffer) {
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, buffer->desc.stride, buffer->desc.height, buffer->desc.format == AHARDWAREBUFFER_FORMAT_B8G8R8A8_UNORM ? GL_BGRA_EXT : GL_RGBA, GL_UNSIGNED_BYTE, buffer->desc.data);
}

__LIBC_HIDDEN__ unsigned int LorieBuffer_getGLTextureId(LorieBuffer *buffer) {
return buffer ? buffer->id : 0;
}

__LIBC_HIDDEN__ int LorieBuffer_getWidth(LorieBuffer *buffer) {
return LorieBuffer_description(buffer)->width;
}
Expand Down
9 changes: 9 additions & 0 deletions app/src/main/cpp/lorie/buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,15 @@ void LorieBuffer_attachToGL(LorieBuffer* _Nullable buffer);
*/
void LorieBuffer_bindTexture(LorieBuffer* _Nullable buffer);

/**
* Get the GL texture id the buffer is attached to (see LorieBuffer_attachToGL).
* Returns 0 if the buffer was not attached to GL yet.
*
* @param buffer
* @return
*/
unsigned int LorieBuffer_getGLTextureId(LorieBuffer* _Nullable buffer);

/**
* Get width of the buffer.
*
Expand Down
10 changes: 10 additions & 0 deletions app/src/main/cpp/lorie/cmdentrypoint.c
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,12 @@ static Bool handleClipboardAnnounce(__unused ClientPtr pClient, __unused void *c
return TRUE;
}

static Bool handleGpuCopyDoneEvent(__unused ClientPtr pClient, __unused void *closure) {
// This must be done only on X server thread (touches present's internal vblank queue).
lorieRecheckGpuCopies();
return TRUE;
}

static Bool handleClipboardData(__unused ClientPtr pClient, void *closure) {
// This must be done only on X server thread.
lorieHandleClipboardData(closure);
Expand Down Expand Up @@ -408,6 +414,10 @@ void handleLorieEvents(int fd, __unused int ready, __unused void *ignored) {
lorieSetRendererWakeupCond(wakeupFd);
break;
}
case EVENT_GPU_COPY_DONE:
QueueWorkProc(handleGpuCopyDoneEvent, NULL, NULL);
lorieWakeServer();
break;
}

int n;
Expand Down
Loading