mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-10-19 02:13:03 +08:00
125 lines
6.6 KiB
C++
125 lines
6.6 KiB
C++
// GLES presentation only; all UI and callbacks belong to GuiApplication.
|
|
struct Surface {
|
|
DesktopWindow& owner;
|
|
explicit Surface(DesktopWindow& window) : owner(window) {}
|
|
~Surface() { DetachSurface(); }
|
|
EGLDisplay display = EGL_NO_DISPLAY;
|
|
EGLContext context = EGL_NO_CONTEXT;
|
|
EGLSurface surface = EGL_NO_SURFACE;
|
|
OHNativeWindow* native_window = nullptr;
|
|
GLuint program = 0, texture = 0, vao = 0;
|
|
float density = 1;
|
|
std::vector<slint::Rgb8Pixel> pixels;
|
|
uint64_t frames = 0;
|
|
uint32_t rendered_width = 0, rendered_height = 0;
|
|
void DetachSurface() {
|
|
if (display != EGL_NO_DISPLAY) {
|
|
if (surface != EGL_NO_SURFACE && context != EGL_NO_CONTEXT) {
|
|
eglMakeCurrent(display, surface, surface, context);
|
|
glDeleteTextures(1, &texture); glDeleteProgram(program); glDeleteVertexArrays(1, &vao);
|
|
}
|
|
eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
|
if (surface != EGL_NO_SURFACE) eglDestroySurface(display, surface);
|
|
if (context != EGL_NO_CONTEXT) eglDestroyContext(display, context);
|
|
// EGL display is shared by the application windows.
|
|
}
|
|
if (native_window) OH_NativeWindow_DestroyNativeWindow(native_window);
|
|
display = EGL_NO_DISPLAY; context = EGL_NO_CONTEXT; surface = EGL_NO_SURFACE;
|
|
native_window = nullptr; program = texture = vao = 0;
|
|
// ReusedBuffer's retained contents belong to the Slint window, not the EGL
|
|
// surface. Keep them when a hidden window gets a new XComponent; otherwise
|
|
// Slint repaints only dirty regions over a cleared (black) buffer.
|
|
frames = 0;
|
|
rendered_width = rendered_height = 0;
|
|
}
|
|
|
|
GLuint Shader(GLenum type, const char* source) {
|
|
GLuint value = glCreateShader(type);
|
|
glShaderSource(value, 1, &source, nullptr); glCompileShader(value);
|
|
GLint ok = 0; glGetShaderiv(value, GL_COMPILE_STATUS, &ok);
|
|
if (!ok) {
|
|
char log[1024]{}; glGetShaderInfoLog(value, sizeof(log), nullptr, log);
|
|
OH_LOG_Print(LOG_APP, LOG_ERROR, 0, "SlintDesktop", "Shader failed: %{public}s", log);
|
|
glDeleteShader(value); return 0;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
bool AttachSurface(uint64_t id, float scale) {
|
|
DetachSurface();
|
|
if (!id || !std::isfinite(scale) || scale <= 0) return false;
|
|
owner.SetDensity(scale);
|
|
density = scale;
|
|
if (OH_NativeWindow_CreateNativeWindowFromSurfaceId(id, &native_window) != 0) return false;
|
|
display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
|
if (!eglInitialize(display, nullptr, nullptr) || !eglBindAPI(EGL_OPENGL_ES_API)) return false;
|
|
const EGLint attributes[] = {EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RENDERABLE_TYPE,
|
|
EGL_OPENGL_ES3_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_NONE};
|
|
EGLConfig config; EGLint count = 0;
|
|
if (!eglChooseConfig(display, attributes, &config, 1, &count) || count == 0) return false;
|
|
const EGLint context_attributes[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
|
|
context = eglCreateContext(display, config, EGL_NO_CONTEXT, context_attributes);
|
|
surface = eglCreateWindowSurface(display, config, reinterpret_cast<EGLNativeWindowType>(native_window), nullptr);
|
|
if (context == EGL_NO_CONTEXT || surface == EGL_NO_SURFACE ||
|
|
!eglMakeCurrent(display, surface, surface, context)) return false;
|
|
const char* vertex = "#version 300 es\n"
|
|
"out vec2 uv; void main(){ vec2 p=vec2((gl_VertexID<<1)&2,gl_VertexID&2);"
|
|
"uv=vec2(p.x,1.-p.y); gl_Position=vec4(p*2.-1.,0.,1.);}";
|
|
const char* fragment = "#version 300 es\nprecision mediump float;"
|
|
"in vec2 uv; uniform sampler2D frame; out vec4 color; void main(){color=texture(frame,uv);}";
|
|
GLuint v = Shader(GL_VERTEX_SHADER, vertex), f = Shader(GL_FRAGMENT_SHADER, fragment);
|
|
if (!v || !f) { glDeleteShader(v); glDeleteShader(f); return false; }
|
|
program = glCreateProgram(); glAttachShader(program, v); glAttachShader(program, f);
|
|
glLinkProgram(program); glDeleteShader(v); glDeleteShader(f);
|
|
GLint linked = 0; glGetProgramiv(program, GL_LINK_STATUS, &linked);
|
|
if (!linked) return false;
|
|
glGenVertexArrays(1, &vao); glBindVertexArray(vao);
|
|
glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
|
|
|
OH_LOG_Print(LOG_APP, LOG_INFO, 0, "SlintDesktop", "Surface attached scale=%{public}.3f", density);
|
|
return true;
|
|
}
|
|
|
|
void Render() {
|
|
if (surface == EGL_NO_SURFACE) return;
|
|
EGLint width = 0, height = 0;
|
|
eglQuerySurface(display, surface, EGL_WIDTH, &width);
|
|
eglQuerySurface(display, surface, EGL_HEIGHT, &height);
|
|
if (width <= 0 || height <= 0 || width > 8192 || height > 8192) return;
|
|
if (rendered_width != uint32_t(width) || rendered_height != uint32_t(height)) {
|
|
rendered_width = width; rendered_height = height;
|
|
owner.extent = slint::PhysicalSize({uint32_t(width), uint32_t(height)});
|
|
owner.window().dispatch_resize_event(slint::LogicalSize({width / density, height / density}));
|
|
pixels.resize(size_t(width) * height); owner.dirty = true;
|
|
OH_LOG_Print(LOG_APP, LOG_INFO, 0, "SlintDesktop", "Window %{public}d resize %{public}dx%{public}d", owner.id, width, height);
|
|
}
|
|
if (!owner.dirty && !owner.window().has_active_animations()) return;
|
|
if (pixels.size() != size_t(width) * height) pixels.resize(size_t(width) * height);
|
|
const auto started = std::chrono::steady_clock::now();
|
|
owner.dirty = false;
|
|
owner.software.render(pixels, width);
|
|
if (!eglMakeCurrent(display, surface, surface, context)) return;
|
|
glViewport(0, 0, width, height); glUseProgram(program);
|
|
glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture);
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels.data());
|
|
glUniform1i(glGetUniformLocation(program, "frame"), 0);
|
|
glBindVertexArray(vao); glDrawArrays(GL_TRIANGLES, 0, 3);
|
|
const GLenum error = glGetError();
|
|
const bool swapped = eglSwapBuffers(display, surface);
|
|
eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
|
if (error || !swapped) {
|
|
OH_LOG_Print(LOG_APP, LOG_ERROR, 0, "SlintDesktop", "Present failed gl=%{public}x egl=%{public}x", error, eglGetError());
|
|
} else if (++frames == 1 || frames % 120 == 0) {
|
|
double ms = std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - started).count();
|
|
OH_LOG_Print(LOG_APP, LOG_INFO, 0, "SlintDesktop", "Presented frames=%{public}llu frame_ms=%{public}.2f", (unsigned long long)frames, ms);
|
|
}
|
|
}
|
|
|
|
};
|