Skip to content

Commit

Permalink
enhancement: move more LorieBuffer-related code to buffer.c
Browse files Browse the repository at this point in the history
  • Loading branch information
twaik committed Jan 29, 2025
1 parent 46602a0 commit c9d29ac
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 67 deletions.
61 changes: 61 additions & 0 deletions app/src/main/cpp/lorie/buffer.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#define EGL_EGLEXT_PROTOTYPES
#define GL_GLEXT_PROTOTYPES
#include <string.h>
#include <stdlib.h>
#include <math.h>
Expand All @@ -7,6 +9,10 @@
#include <sys/mman.h>
#include <sys/socket.h>
#include <errno.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include "misc.h"
#include "buffer.h"

Expand All @@ -19,6 +25,9 @@ struct LorieBuffer {

// file descriptor of shared memory fragment for shared memory backed buffer
int fd;

GLuint id;
EGLImage image;
};

__attribute__((unused))
Expand Down Expand Up @@ -125,6 +134,12 @@ __LIBC_HIDDEN__ void __LorieBuffer_free(LorieBuffer* buffer) {
if (!buffer)
return;

if (eglGetCurrentContext())
glDeleteTextures(1, &buffer->id);

if (eglGetCurrentDisplay() && buffer->image)
eglDestroyImageKHR(eglGetCurrentDisplay(), buffer->image);

if (buffer->desc.type == LORIEBUFFER_REGULAR) {
if (buffer->desc.data)
munmap (buffer->desc.data, alignToPage(buffer->desc.width * buffer->desc.height * sizeof(uint32_t)));
Expand Down Expand Up @@ -223,6 +238,7 @@ __LIBC_HIDDEN__ void LorieBuffer_recvHandleFromUnixSocket(int socketFd, LorieBuf
__sync_fetch_and_add(&buffer.refcount, 1); // refcount is the first object in the struct

read(socketFd, &buffer, sizeof(buffer));
buffer.image = NULL; // Only for process-local use
if (buffer.desc.type == LORIEBUFFER_REGULAR) {
size_t size = alignToPage(buffer.desc.width * buffer.desc.height * sizeof(uint32_t));
buffer.fd = ancil_recv_fd(socketFd);
Expand Down Expand Up @@ -259,6 +275,51 @@ __LIBC_HIDDEN__ void LorieBuffer_recvHandleFromUnixSocket(int socketFd, LorieBuf
*outBuffer = ret;
}

void LorieBuffer_attachToGL(LorieBuffer* buffer) {
const EGLint imageAttributes[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE };
if (!eglGetCurrentDisplay() || !buffer)
return;

if (buffer->image == NULL && buffer->desc.buffer)
buffer->image = eglCreateImageKHR(eglGetCurrentDisplay(), EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, eglGetNativeClientBufferANDROID(buffer->desc.buffer), imageAttributes);

glGenTextures(1, &buffer->id);
glBindTexture(GL_TEXTURE_2D, buffer->id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

if (buffer->image)
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, buffer->image);
else if (buffer->desc.data && buffer->desc.width > 0 && buffer->desc.height > 0) {
int format = buffer->desc.format == AHARDWAREBUFFER_FORMAT_B8G8R8A8_UNORM ? GL_BGRA_EXT : GL_RGBA;
// The image will be updated in redraw call because of `drawRequested` flag, so we are not uploading pixels
glTexImage2D(GL_TEXTURE_2D, 0, format, buffer->desc.width, buffer->desc.height, 0, format, GL_UNSIGNED_BYTE, NULL);
}
}

void LorieBuffer_bindTexture(LorieBuffer *buffer) {
if (!buffer)
return;

glBindTexture(GL_TEXTURE_2D, buffer->id);
if (buffer->desc.type == LORIEBUFFER_REGULAR)
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, buffer->desc.width, buffer->desc.height, buffer->desc.format == AHARDWAREBUFFER_FORMAT_B8G8R8A8_UNORM ? GL_BGRA_EXT : GL_RGBA, GL_UNSIGNED_BYTE, buffer->desc.data);
}

int LorieBuffer_getWidth(LorieBuffer *buffer) {
return buffer ? buffer->desc.width : 0;
}

int LorieBuffer_getHeight(LorieBuffer *buffer) {
return buffer ? buffer->desc.height : 0;
}

bool LorieBuffer_isRgba(LorieBuffer *buffer) {
return buffer ? buffer->desc.format != AHARDWAREBUFFER_FORMAT_B8G8R8A8_UNORM : false;
}

__LIBC_HIDDEN__ int ancil_send_fd(int sock, int fd) {
char nothing = '!';
struct iovec nothing_ptr = { .iov_base = &nothing, .iov_len = 1 };
Expand Down
7 changes: 7 additions & 0 deletions app/src/main/cpp/lorie/buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,13 @@ void LorieBuffer_sendHandleToUnixSocket(LorieBuffer* buffer, int socketFd);
*/
void LorieBuffer_recvHandleFromUnixSocket(int socketFd, LorieBuffer** outBuffer);

void LorieBuffer_attachToGL(LorieBuffer* buffer);
void LorieBuffer_bindTexture(LorieBuffer *buffer);

int LorieBuffer_getWidth(LorieBuffer *buffer);
int LorieBuffer_getHeight(LorieBuffer *buffer);
bool LorieBuffer_isRgba(LorieBuffer *buffer);

#undef STATIC_INLINE

int ancil_send_fd(int sock, int fd);
Expand Down
89 changes: 22 additions & 67 deletions app/src/main/cpp/lorie/renderer.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,6 @@ static EGLSurface defaultSfc = EGL_NO_SURFACE, sfc = EGL_NO_SURFACE;
static EGLConfig cfg = 0;
static ANativeWindow *defaultWin = NULL, *win = NULL;
static LorieBuffer *buffer = NULL;
static EGLImageKHR image = NULL;

static JNIEnv* renderEnv = NULL;
static volatile bool stateChanged = false, bufferChanged = false, windowChanged = false;
Expand All @@ -126,10 +125,6 @@ static pthread_mutex_t stateLock;
static pthread_cond_t stateCond;
static pthread_cond_t stateChangeFinishCond;
static volatile struct lorie_shared_server_state* state = NULL;
static struct {
GLuint id;
LorieBuffer_Desc desc;
} display;
static struct {
GLuint id;
bool cursorChanged;
Expand Down Expand Up @@ -228,7 +223,6 @@ int rendererInitThread(JavaVM *vm) {
gv_coords_bgra = (GLuint) glGetAttribLocation(g_texture_program_bgra, "texCoords");

glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &display.id);
glGenTextures(1, &cursor.id);

rendererThread(env);
Expand Down Expand Up @@ -383,12 +377,6 @@ __unused void rendererSetSharedState(struct lorie_shared_server_state* newState)

void rendererSetBuffer(LorieBuffer* buf) {
pthread_mutex_lock(&stateLock);
if (buf == pendingBuffer)
goto end;

if (pendingBuffer)
LorieBuffer_release(pendingBuffer);

pendingBuffer = buf;
bufferChanged = true;

Expand All @@ -400,7 +388,10 @@ void rendererSetBuffer(LorieBuffer* buf) {
pthread_cond_signal(&state->cond);
pthread_cond_signal(&stateCond);

end: pthread_mutex_unlock(&stateLock);
while(bufferChanged)
pthread_cond_wait(&stateChangeFinishCond, &stateLock);

pthread_mutex_unlock(&stateLock);
}

void rendererSetWindow(ANativeWindow* newWin) {
Expand Down Expand Up @@ -435,7 +426,7 @@ void rendererSetWindow(ANativeWindow* newWin) {

static inline __always_inline void releaseWinAndSurface(JNIEnv *env, ANativeWindow** anw, EGLSurface *esfc) {
if (esfc && *esfc && *esfc != defaultSfc) {
// Requeue the dequeued buffer
// Requeue the dequeued buffer, causes flickering during window reconfiguring
eglSwapBuffers(egl_display, *esfc);
if (eglMakeCurrent(egl_display, defaultSfc, defaultSfc, ctx) != EGL_TRUE)
return vprintEglError("eglMakeCurrent failed (EGL_NO_SURFACE)", __LINE__);
Expand Down Expand Up @@ -495,64 +486,35 @@ void rendererRefreshContext(JNIEnv* env) {
}

static void draw(GLuint id, float x0, float y0, float x1, float y1, uint8_t flip);
static void drawCursor(void);
static void drawCursor(float displayWidth, float displayHeight);

static void rendererRenewImage(void) {
const EGLint imageAttributes[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
uint32_t emptyData = {0};

if (image)
eglDestroyImageKHR(egl_display, image);
if (buffer)
LorieBuffer_release(buffer);

buffer = pendingBuffer;
pendingBuffer = NULL;
bufferChanged = false;
image = NULL;
blockRedraw = false;

LorieBuffer_describe(buffer, &display.desc);

if (display.desc.buffer)
image = eglCreateImageKHR(egl_display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, eglGetNativeClientBufferANDROID(display.desc.buffer), imageAttributes);
LorieBuffer_attachToGL(buffer);
log("renderer: buffer changed %p %d %d", buffer, (int) LorieBuffer_getWidth(buffer), (int) LorieBuffer_getHeight(buffer));

// We should redraw image at least once right after buffer change
if (state)
state->surfaceAvailable = state->drawRequested = state->cursor.updated = win != defaultWin;

bindLinearTexture(display.id);
if (image)
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
else if (display.desc.data && display.desc.width > 0 && display.desc.height > 0) {
int format = display.desc.format == AHARDWAREBUFFER_FORMAT_B8G8R8A8_UNORM ? GL_BGRA_EXT : GL_RGBA;
// The image will be updated in redraw call because of `drawRequested` flag, so we are not uploading pixels
glTexImage2D(GL_TEXTURE_2D, 0, format, display.desc.width, display.desc.height, 0, format, GL_UNSIGNED_BYTE, NULL);
} else {
loge("There is no %s, nothing to be bound.", !buffer ? "AHardwareBuffer" : "EGLImage");
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &emptyData);
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
}

log("renderer: buffer changed %p %d %d", buffer, display.desc.width, display.desc.height);
}

void rendererRedrawLocked(JNIEnv* env) {
EGLSync fence;
int err = EGL_SUCCESS;

// We should signal X server to not use root window while we actively copy use it
// We should signal X server to not use root window while we actively copy it
lorie_mutex_lock(&state->lock, &state->lockingPid);
// Non-null display.desc.data means we have root window created in legacy drawing mode so we should update it on each frame.
if (display.desc.data && state->drawRequested) {
state->drawRequested = FALSE;
bindLinearTexture(display.id);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, display.desc.width, display.desc.height, display.desc.format == AHARDWAREBUFFER_FORMAT_B8G8R8A8_UNORM ? GL_BGRA_EXT : GL_RGBA, GL_UNSIGNED_BYTE, display.desc.data);
}

// Not a mistake, we reset drawRequested flag even in the case if there is no legacy drawing.
state->drawRequested = FALSE;
draw(display.id, -1.f, -1.f, 1.f, 1.f, display.desc.format != AHARDWAREBUFFER_FORMAT_B8G8R8A8_UNORM);

LorieBuffer_bindTexture(buffer);
draw(0, -1.f, -1.f, 1.f, 1.f, LorieBuffer_isRgba(buffer));
fence = eglCreateSyncKHR(egl_display, EGL_SYNC_FENCE_KHR, NULL);
glFlush();

Expand All @@ -566,7 +528,7 @@ void rendererRedrawLocked(JNIEnv* env) {
}

state->cursor.moved = FALSE;
drawCursor();
drawCursor((float) (LorieBuffer_getWidth(buffer)), (float) (LorieBuffer_getHeight(buffer)));
glFlush();

// Wait until root window drawing is finished before giving control back to X server
Expand All @@ -575,16 +537,8 @@ void rendererRedrawLocked(JNIEnv* env) {
state->waitForNextFrame = true;
lorie_mutex_unlock(&state->lock, &state->lockingPid);

if (eglSwapBuffers(egl_display, sfc) != EGL_TRUE) {
if (eglSwapBuffers(egl_display, sfc) != EGL_TRUE)
printEglError("Failed to swap buffers", __LINE__);
err = eglGetError();
if (err == EGL_BAD_NATIVE_WINDOW || err == EGL_BAD_SURFACE) {
log("The window is to be destroyed. Native window disconnected/abandoned, probably activity is destroyed or in background");
pendingWin = NULL;
windowChanged = TRUE;
lorie_mutex_unlock(&state->lock, &state->lockingPid);
}
}

// Perform a little drawing operation to make sure the next buffer is ready on the next invocation of drawing
glEnable(GL_SCISSOR_TEST);
Expand Down Expand Up @@ -725,7 +679,8 @@ static void draw(GLuint id, float x0, float y0, float x1, float y1, uint8_t flip

glActiveTexture(GL_TEXTURE0);
glUseProgram(flip ? g_texture_program_bgra : g_texture_program);
glBindTexture(GL_TEXTURE_2D, id);
if (id)
glBindTexture(GL_TEXTURE_2D, id);

glVertexAttribPointer(p, 2, GL_FLOAT, GL_FALSE, 16, coords);
glVertexAttribPointer(c, 2, GL_FLOAT, GL_FALSE, 16, &coords[2]);
Expand All @@ -734,16 +689,16 @@ static void draw(GLuint id, float x0, float y0, float x1, float y1, uint8_t flip
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); checkGlError();
}

__unused static void drawCursor(void) {
__unused static void drawCursor(float displayWidth, float displayHeight) {
float x, y, w, h;

if (!state->cursor.width || !state->cursor.height)
return;

x = 2.f * ((float) state->cursor.x - (float) state->cursor.xhot) / (float) display.desc.width - 1.f;
y = 2.f * ((float) state->cursor.y - (float) state->cursor.yhot) / (float) display.desc.height - 1.f;
w = 2.f * (float) state->cursor.width / (float) display.desc.width;
h = 2.f * (float) state->cursor.height / (float) display.desc.height;
x = 2.f * ((float) state->cursor.x - (float) state->cursor.xhot) / displayWidth - 1.f;
y = 2.f * ((float) state->cursor.y - (float) state->cursor.yhot) / displayHeight - 1.f;
w = 2.f * (float) state->cursor.width / displayWidth;
h = 2.f * (float) state->cursor.height / displayHeight;
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
draw(cursor.id, x, y, x + w, y + h, false);
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/java/com/termux/x11/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,8 @@ private void setTerminalToolbarView() {
(TermuxX11ExtraKeys.getExtraKeysInfo() == null ? 0 : TermuxX11ExtraKeys.getExtraKeysInfo().getMatrix().length));
pager.setLayoutParams(layoutParams);

// getLorieView().setBottomPadding(prefs.adjustHeightForEK.get() && showNow ? layoutParams.height : 0);
// getLorieView().triggerCallback();
frm.setPadding(0, 0, 0, prefs.adjustHeightForEK.get() && showNow ? layoutParams.height : 0);
getLorieView().requestFocus();
}
Expand Down

0 comments on commit c9d29ac

Please sign in to comment.