diff --git a/Android.bp b/Android.bp index 69f5eb3..fb07953 100644 --- a/Android.bp +++ b/Android.bp @@ -4,9 +4,6 @@ package { cc_library_static { name: "libpixman", - - sdk_version: "current", - sdk_variant_only: true, srcs: [ "pixman/pixman/pixman-access-accessors.c", @@ -59,28 +56,17 @@ cc_library_shared { sdk_variant_only: true, srcs: [ - "shmem_ivshm_ivshmem.c", - "virtio.c", - "virtio_over_shmem.c", - "dm_helpers.c", - "dm_stubs.c", - "utils.c", - "main.cpp", - "devicemodel/lib/dm_string.c", - "devicemodel/core/mevent.c", - "devicemodel/core/timer.c", - "devicemodel/hw/block_if.c", - "devicemodel/hw/gc.c", - "devicemodel/hw/vga.c", - "devicemodel/hw/pci/virtio/vhost.c", - "devicemodel/hw/pci/virtio/virtio_gpu.c", - "devicemodel/hw/vdisplay_egl.c", + "client/main.cpp", + "client/renderer.cpp", + "client/vdisplay_client.cpp", ], local_include_dirs: [ "devicemodel/include/public", "misc/library/include", "devicemodel/include", + "pixman/pixman/", + "client", ], stl: "libc++_static", @@ -112,7 +98,6 @@ cc_library_shared { static_libs: [ "android_native_app_glue", - "libpixman", "libbase_ndk", ], ldflags: [ @@ -122,3 +107,65 @@ cc_library_shared { export_static_lib_headers: [ ], } + +cc_binary { + name: "acrn-virtio-gpu", + + srcs: ["acrn-virtio-gpu.c", + "shmem_ivshm_ivshmem.c", + "virtio.c", + "virtio_over_shmem.c", + "dm_helpers.c", + "dm_stubs.c", + "utils.c", + "devicemodel/lib/dm_string.c", + "devicemodel/core/mevent.c", + "devicemodel/core/timer.c", + "devicemodel/hw/block_if.c", + "devicemodel/hw/gc.c", + "devicemodel/hw/vga.c", + "devicemodel/hw/pci/virtio/vhost.c", + "devicemodel/hw/pci/virtio/virtio_gpu.c", + "devicemodel/hw/vdisplay_server.c", + ], + + local_include_dirs: [ + "devicemodel/include/public", + "misc/library/include", + "devicemodel/include", + ], + + stl: "libc++_static", + cflags: [ + "-pthread", + "-Wall", + "-D__USE_BSD", + ], + cppflags: ["-std=c++14"], + + shared_libs: [ + "libEGL", + "libGLESv3", + "liblog", + "libz", + "libsync", + "libc", + "libandroid", + ], + + header_libs: [ + "jni_headers", + ], + + export_header_lib_headers: [ + ], + + static_libs: [ + "libpixman", + ], + + export_static_lib_headers: [ + ], + +relative_install_path: "hw" +} diff --git a/acrn-virtio-gpu.c b/acrn-virtio-gpu.c new file mode 100644 index 0000000..cc04a07 --- /dev/null +++ b/acrn-virtio-gpu.c @@ -0,0 +1,19 @@ +#include +#include + +#include "utils.h" + +extern struct pci_vdev_ops pci_ops_virtio_gpu; + +static void init_vdpy(struct virtio_backend_info *info __attribute__((unused))) { +// vdpy_parse_cmd_option(info->opts); +} + +static struct virtio_backend_info virtio_gpu_info = { + .pci_vdev_ops = &pci_ops_virtio_gpu, + + .hook_before_init = init_vdpy, +}; + +ACRN_BACKEND_MAIN(virtio_gpu_info) + diff --git a/client/common.h b/client/common.h new file mode 100644 index 0000000..58c7dc0 --- /dev/null +++ b/client/common.h @@ -0,0 +1,6 @@ +#include + + +#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "main", __VA_ARGS__)) +#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "main", __VA_ARGS__)) +#define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "main", __VA_ARGS__)) diff --git a/main.cpp b/client/main.cpp similarity index 86% rename from main.cpp rename to client/main.cpp index 6e3cb24..e6ccdfb 100644 --- a/main.cpp +++ b/client/main.cpp @@ -18,15 +18,10 @@ // #include "agq.h" // #include "circle.h" // #include "common.h" -// #include "renderer.h" +#include "renderer.h" +#include "vdisplay_client.h" // #include "vecmath.h" -extern "C" { -#include -#include -#include -} - #include #include #include @@ -42,12 +37,13 @@ extern "C" { #include #include -#include #include +#include "common.h" -#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "main", __VA_ARGS__)) +#define USE_GAME_RENDER +#ifndef USE_GAME_RENDER static void init_vdpy(struct virtio_backend_info *info) { vdpy_gfx_ui_init(info->native_window); } @@ -57,6 +53,7 @@ static struct virtio_backend_info virtio_gpu_info = { .pci_vdev_ops = &pci_ops_virtio_gpu, .hook_before_init = init_vdpy, }; +#endif namespace { int animating = 0; @@ -75,7 +72,8 @@ int32_t engine_handle_input(struct android_app*, AInputEvent* event) { /** * Process the next main command. */ -void engine_handle_cmd(struct android_app* app, int32_t cmd) { +void engine_handle_cmd(struct android_app* app, int32_t cmd) { + Renderer *renderer = (Renderer *)(app->userData); switch (cmd) { case APP_CMD_SAVE_STATE: // We are not saving the state. @@ -86,8 +84,13 @@ void engine_handle_cmd(struct android_app* app, int32_t cmd) { LOGI("APP_CMD_INIT_WINDOW"); if (app->window != NULL) { LOGI("APP_CMD_INIT_WINDOW -1"); +#ifdef USE_GAME_RENDER + renderer->init(app->window); + renderer->draw(); +#else virtio_gpu_info.native_window = app->window; create_backend_thread(&virtio_gpu_info); +#endif animating = 1; } @@ -95,7 +98,11 @@ void engine_handle_cmd(struct android_app* app, int32_t cmd) { break; case APP_CMD_TERM_WINDOW: // The window is being hidden or closed, clean it up. +#ifdef USE_GAME_RENDER + renderer->terminate(); +#else close_backend_thread(); +#endif LOGI("APP_CMD_TERM_WINDOW"); animating = 0; break; @@ -125,6 +132,11 @@ void android_main(struct android_app* state) { LOGI("Running with SDK %d", state->activity->sdkVersion); + std::unique_ptr renderer(new Renderer()); + std::unique_ptr display_client(new DisplayClient(renderer.get())); + display_client->start(); + + state->userData = renderer.get(); state->onAppCmd = engine_handle_cmd; state->onInputEvent = engine_handle_input; @@ -147,7 +159,11 @@ void android_main(struct android_app* state) { LOGI("ALooper_pollAll -2"); // Check if we are exiting. if (state->destroyRequested != 0) { +#ifdef USE_GAME_RENDER + renderer->terminate(); +#else close_backend_thread(); +#endif LOGI("state->destroyRequested != 0, exit..."); return; } @@ -156,7 +172,7 @@ void android_main(struct android_app* state) { #ifdef USE_GAME_RENDER if (animating) { - renderer->update(); + // renderer->update(); // Drawing is throttled to the screen update rate, so there // is no need to do timing here. diff --git a/client/renderer.cpp b/client/renderer.cpp new file mode 100644 index 0000000..2c48433 --- /dev/null +++ b/client/renderer.cpp @@ -0,0 +1,558 @@ +extern "C" +{ +#include +#include +#include +#include +} + +#include "renderer.h" + +#include "common.h" + +#define checkGlError(op) { \ + LOGE("%s():%d CALL %s()\n", __func__, __LINE__, op); \ + for (GLint error = glGetError(); error; error = glGetError()) { \ + LOGE("%s():%d glError (0x%x) for %s()\n", __func__, __LINE__, error, op); \ + } \ +} +#define checkGlError2(op, arg) { \ + LOGE("%s():%d CALL %s() 0x%x\n", __func__, __LINE__, op, arg); \ + for (GLint error = glGetError(); error; error = glGetError()) { \ + LOGE("%s():%d glError (0x%x) for %s()\n", __func__, __LINE__, error, op); \ + } \ +} +#define checkEglError(op) { \ + LOGE("%s():%d CALL %s()\n", __func__, __LINE__, op); \ + for (GLint error = eglGetError(); error!=EGL_SUCCESS; error = eglGetError()) { \ + LOGE("%s():%d eglError (0x%x) for %s()\n", __func__, __LINE__, error, op); \ + } \ +} + +#define VDPY_MIN_WIDTH 640 +#define VDPY_MIN_HEIGHT 480 + +Renderer::Renderer() : gl_ops(), gl_ctx(), initialized(false) +{ + gl_ops.eglCreateImageKHR = (PFNEGLCREATEIMAGEKHRPROC) + eglGetProcAddress("eglCreateImageKHR"); + gl_ops.eglDestroyImageKHR = (PFNEGLDESTROYIMAGEKHRPROC) + eglGetProcAddress("eglDestroyImageKHR"); + gl_ops.glEGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) + eglGetProcAddress("glEGLImageTargetTexture2DOES"); + + if ((gl_ops.eglCreateImageKHR == NULL) || + (gl_ops.eglDestroyImageKHR == NULL) || + (gl_ops.glEGLImageTargetTexture2DOES == NULL)) { + LOGI("DMABuf is not supported.\n"); + gl_ctx.egl_dmabuf_supported = false; + } else + gl_ctx.egl_dmabuf_supported = true; +} + +int Renderer::init(NativeWindowType window) +{ + EGLint majorVersion; + EGLint minorVersion; + EGLint numConfigs = 0, n = -1; + EGLConfig myConfig; + int w, h; + + LOGD("%s", __func__); + // only support 1 physical screen now + + // create egl surface from native window + gl_ctx.eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (eglInitialize(gl_ctx.eglDisplay, &majorVersion, &minorVersion) != EGL_TRUE) { + checkEglError("eglInitialize"); + LOGE("%s, eglInitialize failed.", __func__); + return -1; + } + + EGLint s_configAttribs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_NONE }; + eglChooseConfig(gl_ctx.eglDisplay, s_configAttribs, 0, 0, &numConfigs); + checkEglError("eglChooseConfig0"); + if (numConfigs <= 0) { + LOGE("%s, eglChooseConfig failed.", __func__); + return -1; + } + + EGLConfig* const configs = (EGLConfig*) malloc(sizeof(EGLConfig) * numConfigs); + eglChooseConfig(gl_ctx.eglDisplay, s_configAttribs, configs, numConfigs, &n); + checkEglError("eglChooseConfig1"); + myConfig = configs[0]; + free(configs); + + // gl_ctx.eglContext = SDL_GL_GetCurrentContext(); + EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; + gl_ctx.eglContext = eglCreateContext(gl_ctx.eglDisplay, myConfig, EGL_NO_CONTEXT, context_attribs); + checkEglError("eglCreateContext"); + + gl_ctx.eglSurface = eglCreateWindowSurface(gl_ctx.eglDisplay, myConfig, window, NULL); + checkEglError("eglCreateWindowSurface"); + if (gl_ctx.eglSurface == EGL_NO_SURFACE) { + LOGE("gelCreateWindowSurface failed.\n"); + return -1; + } + + EGLBoolean returnValue; + returnValue = eglMakeCurrent(gl_ctx.eglDisplay, gl_ctx.eglSurface, gl_ctx.eglSurface, gl_ctx.eglContext); + checkEglError("eglMakeCurrent"); + // checkEglError("eglMakeCurrent", returnValue); + if (returnValue != EGL_TRUE) { + LOGE("eglMakeCurrent failed.\n"); + return -1; + } + eglQuerySurface(gl_ctx.eglDisplay, gl_ctx.eglSurface, EGL_WIDTH, &w); + checkEglError("eglQuerySurface0"); + eglQuerySurface(gl_ctx.eglDisplay, gl_ctx.eglSurface, EGL_HEIGHT, &h); + checkEglError("eglQuerySurface1"); + w = 0x400; + h = 0x300; + gl_ctx.width = w; + gl_ctx.height = h; + LOGI("%s (gl_ctx.eglDisplay/gl_ctx.eglSurface)=0x%lx/0x%lx w/h=%d/%d\n", + __func__, (unsigned long)gl_ctx.eglDisplay, (unsigned long)gl_ctx.eglSurface, w, h); + // static_cast(reinterpret_cast(ptr)) + + if (gl_ctx.width < VDPY_MIN_WIDTH || + gl_ctx.width < VDPY_MIN_HEIGHT) { + LOGE("Too small resolutions. Please check the " + " graphics system\n"); + // SDL_Quit(); + return -1; + } + + char vShaderStr[] = + "#version 300 es \n" + "layout(location = 0) in vec4 a_position; \n" + "layout(location = 1) in vec2 a_texCoord; \n" + "out vec2 v_texCoord; \n" + "void main() \n" + "{ \n" + " gl_Position = a_position; \n" + " v_texCoord = a_texCoord; \n" + "} \n"; + + char fShaderStr[] = + "#version 300 es \n" + "#extension GL_OES_EGL_image_external : require \n" + "precision mediump float; \n" + "layout(location = 0) out vec4 outColor; \n" + "in vec2 v_texCoord; \n" + "uniform samplerExternalOES uTexture; \n" + "void main() \n" + "{ \n" + " outColor = texture2D(uTexture, v_texCoord); \n" + "} \n"; + gl_ctx.programObjectExternal = esLoadProgram(vShaderStr, fShaderStr); + if (!gl_ctx.programObjectExternal) + LOGE("%s failed to load programObjectExternal\n", __func__); + + char fShaderStr2[] = + "#version 300 es \n" + "precision mediump float; \n" + "layout(location = 0) out vec4 outColor; \n" + "in vec2 v_texCoord; \n" + "uniform sampler2D uTexture; \n" + "void main() \n" + "{ \n" + " outColor = texture2D(uTexture, v_texCoord); \n" + "} \n"; + gl_ctx.programObject = esLoadProgram(vShaderStr, fShaderStr2); + if (!gl_ctx.programObject) + LOGE("%s failed to load programObject\n", __func__); + + returnValue = eglMakeCurrent(gl_ctx.eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + checkEglError("eglMakeCurrent"); + + initialized = true; + return 0; +} + +void Renderer::terminate() +{ + // Delete program object + if (gl_ctx.programObjectExternal) { + glDeleteProgram(gl_ctx.programObjectExternal); + checkGlError("glDeleteProgram1"); + } + if (gl_ctx.programObject) { + glDeleteProgram(gl_ctx.programObject); + checkGlError("glDeleteProgram2"); + } + + if (gl_ctx.eglDisplay != EGL_NO_DISPLAY) { + eglMakeCurrent(gl_ctx.eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + checkEglError("eglMakeCurrent"); + if (gl_ctx.eglContext != EGL_NO_CONTEXT) { + eglDestroyContext(gl_ctx.eglDisplay, gl_ctx.eglContext); + checkEglError("eglDestroyContext"); + } + if (gl_ctx.eglSurface != EGL_NO_SURFACE) { + eglDestroySurface(gl_ctx.eglDisplay, gl_ctx.eglSurface); + checkEglError("eglDestroySurface"); + } + eglTerminate(gl_ctx.eglDisplay); + checkEglError("eglTerminate"); + } + gl_ctx.eglDisplay = EGL_NO_DISPLAY; + gl_ctx.eglContext = EGL_NO_CONTEXT; + gl_ctx.eglSurface = EGL_NO_SURFACE; +} + +void Renderer::draw() +{ + +} + +void Renderer::vdpy_surface_set(struct surface *surf) +{ + // int format; + int i; + + // if (vdpy.tid != pthread_self()) { + // LOGE("%s: unexpected code path as unsafe 3D ops in multi-threads env.\n", + // __func__); + // return; + // } + + LOGI("%s -1\n", __func__); + + if (!initialized) + return; + + if (surf->surf_type == SURFACE_DMABUF) { + gl_ctx.cur_surf = *surf; + } else { + /* Unsupported type */ + return; + } + + if (gl_ctx.surf_tex) { + // SDL_DestroyTexture(gl_ctx.surf_tex); + glDeleteTextures(1, &gl_ctx.surf_tex); + checkGlError2("glDeleteTextures", gl_ctx.surf_tex); + } + if (surf && (surf->surf_type == SURFACE_DMABUF)) { + egl_create_dma_tex(&gl_ctx.surf_tex); + } + + /* For the surf_switch, it will be updated in surface_update */ + if (surf->surf_type == SURFACE_DMABUF) { + EGLImageKHR egl_img = EGL_NO_IMAGE_KHR; + EGLint attrs[64]; + + i = 0; + attrs[i++] = EGL_WIDTH; + attrs[i++] = surf->width; + attrs[i++] = EGL_HEIGHT; + attrs[i++] = surf->height; + attrs[i++] = EGL_LINUX_DRM_FOURCC_EXT; + attrs[i++] = surf->dma_info.surf_fourcc; + attrs[i++] = EGL_DMA_BUF_PLANE0_FD_EXT; + attrs[i++] = surf->dma_info.dmabuf_fd; + attrs[i++] = EGL_DMA_BUF_PLANE0_PITCH_EXT; + attrs[i++] = surf->stride; + attrs[i++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT; + attrs[i++] = surf->dma_info.dmabuf_offset; + if (gl_ctx.modifier) { + attrs[i++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT; + attrs[i++] = gl_ctx.modifier & 0xffffffff; + attrs[i++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT; + attrs[i++] = (gl_ctx.modifier & 0xffffffff00000000) >> 32; + } + attrs[i++] = EGL_NONE; + + egl_img = gl_ops.eglCreateImageKHR(gl_ctx.eglDisplay, + EGL_NO_CONTEXT, + EGL_LINUX_DMA_BUF_EXT, + NULL, attrs); + if (egl_img == EGL_NO_IMAGE_KHR) { + LOGE("Failed in eglCreateImageKHR.\n"); + return; + } + checkEglError("eglCreateImageKHR"); + + // SDL_GL_BindTexture(gl_ctx.surf_tex, NULL, NULL); + glBindTexture(GL_TEXTURE_EXTERNAL_OES, gl_ctx.surf_tex); + checkGlError2("glBindTexture", gl_ctx.surf_tex); + gl_ops.glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, egl_img); + checkGlError("glEGLImageTargetTexture2DOES"); + if (gl_ctx.egl_img != EGL_NO_IMAGE_KHR) + gl_ops.eglDestroyImageKHR(gl_ctx.eglDisplay, + gl_ctx.egl_img); + + /* In theory the created egl_img can be released after it is bound + * to texture. + * Now it is released next time so that it is controlled correctly + */ + gl_ctx.egl_img = egl_img; + + egl_render_copy(gl_ctx.surf_tex, NULL, true); + } + + eglSwapBuffers(gl_ctx.eglDisplay, gl_ctx.eglSurface); + + LOGI("%s -2\n", __func__); +} + +void Renderer::vdpy_set_modifier(uint64_t modifier) +{ + gl_ctx.modifier = modifier; +} + +#define checkGlError(op) { \ + LOGE("%s():%d CALL %s()\n", __func__, __LINE__, op); \ + for (GLint error = glGetError(); error; error = glGetError()) { \ + LOGE("%s():%d glError (0x%x) for %s()\n", __func__, __LINE__, error, op); \ + } \ +} +#define checkGlError2(op, arg) { \ + LOGE("%s():%d CALL %s() 0x%x\n", __func__, __LINE__, op, arg); \ + for (GLint error = glGetError(); error; error = glGetError()) { \ + LOGE("%s():%d glError (0x%x) for %s()\n", __func__, __LINE__, error, op); \ + } \ +} +#define checkEglError(op) { \ + LOGE("%s():%d CALL %s()\n", __func__, __LINE__, op); \ + for (GLint error = eglGetError(); error!=EGL_SUCCESS; error = eglGetError()) { \ + LOGE("%s():%d eglError (0x%x) for %s()\n", __func__, __LINE__, error, op); \ + } \ +} + +int Renderer::egl_render_copy(GLuint src_tex, + const SDL_Rect * dstrect __attribute__((unused)), bool is_dmabuf) +{ + GLfloat vVertices[] = {-1.0f, 1.0f, 0.0f, // Position 0 + 0.0f, 0.0f, // TexCoord 0 + -1.0f, -1.0f, 0.0f, // Position 1 + 0.0f, 1.0f, // TexCoord 1 + 1.0f, -1.0f, 0.0f, // Position 2 + 1.0f, 1.0f, // TexCoord 2 + 1.0f, 1.0f, 0.0f, // Position 3 + 1.0f, 0.0f // TexCoord 3 + }; + GLushort indices[] = {0, 1, 2, 0, 2, 3}; + + if (dstrect) { + vVertices[0] = dstrect->x; + vVertices[1] = dstrect->y; + vVertices[5] = dstrect->x + dstrect->w; + vVertices[6] = dstrect->y; + vVertices[10] = dstrect->x + dstrect->w; + vVertices[11] = dstrect->y + dstrect->h; + vVertices[15] = dstrect->x; + vVertices[16] = dstrect->y + dstrect->h; + LOGI("%s dstrect={%d, %d, %d, %d}\n", + __func__, dstrect->x, dstrect->y, dstrect->w, dstrect->h); + } else { + LOGI("%s dstrect=NULL\n", __func__); + } + + // Set the viewport + glViewport(0, 0, gl_ctx.width, gl_ctx.height); + checkGlError("glViewport"); + + // Clear the color buffer + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + checkGlError("glClear"); + + // Use the program object + GLuint program; + if (is_dmabuf) + program = gl_ctx.programObjectExternal; + else + program = gl_ctx.programObject; + glLinkProgram(program); + glUseProgram(program); + checkGlError("glUseProgram"); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + // Load the vertex position + glVertexAttribPointer(0, 3, GL_FLOAT, + GL_FALSE, 5 * sizeof(GLfloat), vVertices); + checkGlError("glVertexAttribPointer0"); + // Load the texture coordinate + glVertexAttribPointer(1, 2, GL_FLOAT, + GL_FALSE, 5 * sizeof(GLfloat), &vVertices[3]); + checkGlError("glVertexAttribPointer1"); + + glEnableVertexAttribArray(0); + checkGlError("glEnableVertexAttribArray0"); + glEnableVertexAttribArray(1); + checkGlError("glEnableVertexAttribArray1"); + + // Bind the base map + glActiveTexture(GL_TEXTURE0); + checkGlError("glActiveTexture"); + if (is_dmabuf) + glBindTexture(GL_TEXTURE_EXTERNAL_OES, src_tex); + else + glBindTexture(GL_TEXTURE_2D, src_tex); + checkGlError2("glBindTexture", src_tex); + + // Set the base map sampler to texture unit to 0 + // glUniform1i(userData->baseMapLoc, 0); + GLuint uniformlocation = glGetUniformLocation(program, "uTexture"); + checkGlError("glGetUniformLocation"); + glUniform1i(uniformlocation, 0); + checkGlError("glUniform1i"); + + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices); + checkGlError("glDrawElements"); + return 0; +} + +int Renderer::egl_create_dma_tex(GLuint *texid) +{ + LOGI("%s -1\n", __func__); + glGenTextures(1, texid); + checkGlError2("glGenTextures", *texid); + glBindTexture(GL_TEXTURE_EXTERNAL_OES, *texid); + checkGlError2("glBindTexture", *texid); + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + checkGlError("glTexParameteri"); + LOGI("%s -2\n", __func__); + return 0; +} + +GLuint Renderer::esLoadShader ( GLenum type, const char *shaderSrc ) +{ + GLuint shader; + GLint compiled; + + // Create the shader object + shader = glCreateShader ( type ); + checkGlError("glCreateShader"); + if ( shader == 0 ) + { + LOGE("%s() failed to create shader!\n", __func__); + return 0; + } + + // Load the shader source + glShaderSource ( shader, 1, &shaderSrc, NULL ); + checkGlError("glShaderSource"); + + // Compile the shader + glCompileShader ( shader ); + checkGlError("glCompileShader"); + + // Check the compile status + glGetShaderiv ( shader, GL_COMPILE_STATUS, &compiled ); + checkGlError("glGetShaderiv"); + if ( !compiled ) + { + GLint infoLen = 0; + + glGetShaderiv ( shader, GL_INFO_LOG_LENGTH, &infoLen ); + checkGlError("glGetShaderiv2"); + if ( infoLen > 1 ) + { + char *infoLog = (char *) malloc ( sizeof ( char ) * infoLen ); + + glGetShaderInfoLog ( shader, infoLen, NULL, infoLog ); + LOGE ( "Error compiling shader:\n%s\n", infoLog ); + + free ( infoLog ); + } + + glDeleteShader ( shader ); + checkGlError("glDeleteShader"); + LOGE("%s() failed to compile shader!\n", __func__); + return 0; + } + + return shader; + +} + +GLuint Renderer::esLoadProgram ( const char *vertShaderSrc, const char *fragShaderSrc ) +{ + GLuint vertexShader; + GLuint fragmentShader; + GLuint programObject; + GLint linked; + + // Load the vertex/fragment shaders + vertexShader = esLoadShader ( GL_VERTEX_SHADER, vertShaderSrc ); + if ( vertexShader == 0 ) + { + LOGE("%s() failed to load vertex shader!\n", __func__); + return 0; + } + + fragmentShader = esLoadShader ( GL_FRAGMENT_SHADER, fragShaderSrc ); + if ( fragmentShader == 0 ) + { + glDeleteShader ( vertexShader ); + checkGlError("glDeleteShader"); + LOGE("%s() failed to load fragment shader!\n", __func__); + return 0; + } + + // Create the program object + programObject = glCreateProgram ( ); + checkGlError("glCreateProgram"); + if ( programObject == 0 ) + { + LOGE("%s() failed to create program!\n", __func__); + return 0; + } + + glAttachShader ( programObject, vertexShader ); + checkGlError("glAttachShader"); + glAttachShader ( programObject, fragmentShader ); + checkGlError("glAttachShader2"); + + // Link the program + glLinkProgram ( programObject ); + checkGlError("glLinkProgram"); + + // Check the link status + glGetProgramiv ( programObject, GL_LINK_STATUS, &linked ); + checkGlError("glGetProgramiv"); + + if ( !linked ) + { + GLint infoLen = 0; + + glGetProgramiv ( programObject, GL_INFO_LOG_LENGTH, &infoLen ); + checkGlError("glGetProgramiv2"); + + if ( infoLen > 1 ) + { + char *infoLog = (char *) malloc ( sizeof ( char ) * infoLen ); + + glGetProgramInfoLog ( programObject, infoLen, NULL, infoLog ); + checkGlError("glGetProgramInfoLog"); + LOGE ( "Error linking program:\n%s\n", infoLog ); + + free ( infoLog ); + } + + glDeleteProgram ( programObject ); + checkGlError("glDeleteProgram"); + LOGE("%s() failed to link program!\n", __func__); + return 0; + } + + // Free up no longer needed shader resources + glDeleteShader ( vertexShader ); + checkGlError("glDeleteShader"); + glDeleteShader ( fragmentShader ); + checkGlError("glDeleteShader2"); + + return programObject; +} diff --git a/client/renderer.h b/client/renderer.h new file mode 100644 index 0000000..a167572 --- /dev/null +++ b/client/renderer.h @@ -0,0 +1,66 @@ +#ifndef CLIENT_RENDERER_H +#define CLIENT_RENDERER_H + +#include +#include +#include +#include + +#include "vdisplay.h" + +class Renderer { +public: + Renderer(); + + struct egl_display_ops { + PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR; + PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR; + PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES; + }; + + struct egl_ctx { + //struct android_app* app; + NativeWindowType window; + int32_t width; + int32_t height; + + bool egl_dmabuf_supported; + + EGLContext eglContext; + EGLDisplay eglDisplay; + EGLSurface eglSurface; + + uint64_t modifier; + struct surface cur_surf; + EGLImage egl_img; + GLuint surf_tex; + + // Handle to a program object + GLuint programObject; + GLuint programObjectExternal; + }; + + struct egl_display_ops gl_ops; + struct egl_ctx gl_ctx; + + int init(NativeWindowType window); + void terminate(); + void draw(); + void vdpy_surface_set(struct surface *surf); + void vdpy_set_modifier(uint64_t modifier); +private: + typedef struct{ + short x, y; + short w, h; + } SDL_Rect; + + int egl_render_copy(GLuint src_tex, + const SDL_Rect * dstrect __attribute__((unused)), bool is_dmabuf); + int egl_create_dma_tex(GLuint *texid); + + GLuint esLoadShader ( GLenum type, const char *shaderSrc ); + GLuint esLoadProgram ( const char *vertShaderSrc, const char *fragShaderSrc ); + bool initialized; +}; + +#endif // CLIENT_RENDERER_H diff --git a/client/vdisplay_client.cpp b/client/vdisplay_client.cpp new file mode 100644 index 0000000..6cb13cd --- /dev/null +++ b/client/vdisplay_client.cpp @@ -0,0 +1,178 @@ +#include "vdisplay_client.h" + +extern "C" +{ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vdisplay_protocol.h" +} + +#include "common.h" + +#define SERVER_SOCK_PATH "/data/virt_disp_server" +#define CLIENT_SOCK_PATH "/data/virt_disp_client" + +DisplayClient::DisplayClient(Renderer * rd) : client_sock(-1), is_connected(false), renderer(rd) +{} + +int DisplayClient::start() +{ + int ret, len; + struct sockaddr_un client_sockaddr; + + client_sock = ::socket(AF_UNIX, SOCK_STREAM, 0); + if (client_sock == -1) { + LOGE("SOCKET ERROR = %d\n", errno); + return -1; + } + + memset(&client_sockaddr, 0, sizeof(struct sockaddr_un)); + client_sockaddr.sun_family = AF_UNIX; + strcpy(client_sockaddr.sun_path, CLIENT_SOCK_PATH); + len = sizeof(client_sockaddr); + + unlink(CLIENT_SOCK_PATH); + ret = ::bind(client_sock, (struct sockaddr *) &client_sockaddr, len); + if (ret == -1){ + LOGE("BIND ERROR: %d\n", errno); + close(client_sock); + client_sock = -1; + return -1; + } + + server_tid = make_shared(work_thread, this); + server_tid->join(); + return ret; +} + +int DisplayClient::term() +{ + if (client_sock != -1) { + close(client_sock); + client_sock = -1; + } + return 0; +} + + +int DisplayClient::connect() +{ + int ret, len; + struct sockaddr_un server_sockaddr; + + memset(&server_sockaddr, 0, sizeof(struct sockaddr_un)); + server_sockaddr.sun_family = AF_UNIX; + strcpy(server_sockaddr.sun_path, SERVER_SOCK_PATH); + + len = sizeof(server_sockaddr); + ret = ::connect(client_sock, (struct sockaddr *) &server_sockaddr, len); + if(ret == -1){ + LOGE("CONNECT ERROR = %d\n", errno); + } else { + is_connected = true; + LOGI("CONNECT OK! ret = %d\n", ret); + } + + return ret; +} + +void * DisplayClient::work_thread(DisplayClient *cur_ctx) +{ + int ret; + struct dpy_evt_header msg_header; + char buf[256]; + + int epollfd = epoll_create1 (0); + if (epollfd == -1) { + perror ("epoll_create1"); + return NULL; + } + + // we have to register the server socket for “epoll” events + struct epoll_event event; + event.events = EPOLLIN | EPOLLERR | EPOLLHUP | EPOLLRDHUP; + event.data.fd = cur_ctx->client_sock; + if (epoll_ctl (epollfd, EPOLL_CTL_ADD, cur_ctx->client_sock, &event) == -1) { + perror ("epoll_ctl"); + exit (EXIT_FAILURE); + } + + while (true) { + if (!cur_ctx->is_connected) { + cur_ctx->connect(); + continue; + } + + // Buffer to hold events + struct epoll_event events[5]; + int numEvents = epoll_wait (epollfd, events, 5, -1); + if (numEvents == -1) { + perror ("epoll_wait"); + continue; + } + + // Process events + for (int i = 0; i < numEvents; i++) { + // Check if the event is for the server socket + if (events[i].data.fd != cur_ctx->client_sock) { + continue; + } + + if (!(events[i].events & EPOLLIN)) { + LOGE("poll client error: 0x%x", events[i].events); + continue; + } + + while ((ret = ::recv(cur_ctx->client_sock, &msg_header, sizeof(msg_header), 0) ) > 0) { + if (ret != sizeof(msg_header)) { + LOGE("recv event header fail (%d vs. 0x%lx)!", ret, (unsigned long)sizeof(msg_header)); + continue; + } + + if (msg_header.e_magic != DISPLAY_MAGIC_CODE) { + // data error, clear receive buffer + LOGE("recv data err!"); + while (::recv(cur_ctx->client_sock, &buf, 256, 0) > 0); + break; + } + + ret = ::recv(cur_ctx->client_sock, &buf, msg_header.e_size, 0); + if (ret != msg_header.e_size) + LOGE("recv event body fail (%d vs. %d) !", ret, msg_header.e_size); + + switch (msg_header.e_type) { + case DPY_EVENT_SURFACE_SET: + { + if (cur_ctx->renderer) + cur_ctx->renderer->vdpy_surface_set((struct surface *)buf); + break; + } + case DPY_EVENT_SET_MODIFIER: + { + if (cur_ctx->renderer) + cur_ctx->renderer->vdpy_set_modifier(*(uint64_t *)buf); + break; + } + // case DPY_EVENT_DISPLAY_INFO: + // { + // struct display_info *info = (struct display_info *)buf; + // vscr->info.xoff = info->xoff; + // vscr->info.yoff = info->yoff; + // vscr->info.width = info->width; + // vscr->info.height = info->height; + // break; + // } + default: + break; + } + } + } + } +} diff --git a/client/vdisplay_client.h b/client/vdisplay_client.h new file mode 100644 index 0000000..b542260 --- /dev/null +++ b/client/vdisplay_client.h @@ -0,0 +1,28 @@ +#ifndef VDISPLAY_CLIENT_H +#define VDISPLAY_CLIENT_H + +#include "renderer.h" + +#include + +using namespace std; + +class DisplayClient { +public: + DisplayClient(Renderer * rd); + int start(); + int term(); + + int connect(); + +private: + + static void * work_thread(DisplayClient *cur_ctx); + int client_sock; + bool is_connected; + shared_ptr server_tid; + + Renderer *renderer; +}; + +#endif // VDISPLAY_CLIENT_H diff --git a/devicemodel/hw/vdisplay_sdl.c b/devicemodel/hw/vdisplay_sdl.c new file mode 100644 index 0000000..ed1edf8 --- /dev/null +++ b/devicemodel/hw/vdisplay_sdl.c @@ -0,0 +1,1415 @@ +/* + * Copyright (C) 2022 Intel Corporation. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * Virtual Display for VMs + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "log.h" +#include "vdisplay.h" +#include "atomic.h" +#include "timer.h" +#include +#include +#include +#include + +#define VDPY_MAX_WIDTH 1920 +#define VDPY_MAX_HEIGHT 1080 +#define VDPY_DEFAULT_WIDTH 1024 +#define VDPY_DEFAULT_HEIGHT 768 +#define VDPY_MIN_WIDTH 640 +#define VDPY_MIN_HEIGHT 480 +#define transto_10bits(color) (uint16_t)(color * 1024 + 0.5) +#define VSCREEN_MAX_NUM 2 + +static unsigned char default_raw_argb[VDPY_DEFAULT_WIDTH * VDPY_DEFAULT_HEIGHT * 4]; + +struct state { + bool is_ui_realized; + bool is_active; + bool is_wayland; + bool is_x11; + bool is_fullscreen; + uint64_t updates; + int n_connect; +}; + +struct egl_display_ops { + PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR; + PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR; + PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES; +}; + +struct vscreen { + struct display_info info; + int pscreen_id; + SDL_Rect pscreen_rect; + bool is_fullscreen; + int org_x; + int org_y; + int width; + int height; + int guest_width; + int guest_height; + struct surface surf; + struct cursor cur; + SDL_Texture *surf_tex; + SDL_Texture *cur_tex; + SDL_Texture *bogus_tex; + int surf_updates; + int cur_updates; + SDL_Window *win; + SDL_Renderer *renderer; + pixman_image_t *img; + EGLImage egl_img; + /* Record the update_time that is activated from guest_vm */ + struct timespec last_time; +}; + +static struct display { + struct state s; + struct vscreen *vscrs; + int vscrs_num; + pthread_t tid; + /* Add one UI_timer(33ms) to render the buffers from guest_vm */ + struct acrn_timer ui_timer; + struct vdpy_display_bh ui_timer_bh; + // protect the request_list + pthread_mutex_t vdisplay_mutex; + // receive the signal that request is submitted + pthread_cond_t vdisplay_signal; + TAILQ_HEAD(display_list, vdpy_display_bh) request_list; + /* add the below two fields for calling eglAPI directly */ + bool egl_dmabuf_supported; + SDL_GLContext eglContext; + EGLDisplay eglDisplay; + struct egl_display_ops gl_ops; +} vdpy = { + .s.is_ui_realized = false, + .s.is_active = false, + .s.is_wayland = false, + .s.is_x11 = false, + .s.n_connect = 0 +}; + +typedef enum { + ESTT = 1, // Established Timings I & II + STDT, // Standard Timings + ESTT3, // Established Timings III +} TIMING_MODE; + +static const struct timing_entry { + uint32_t hpixel;// Horizontal pixels + uint32_t vpixel;// Vertical pixels + uint32_t byte; // byte idx in the Established Timings I & II + uint32_t byte_t3;// byte idx in the Established Timings III Descriptor + uint32_t bit; // bit idx + uint8_t hz; // frequency + bool is_std; // the flag of standard mode +} timings[] = { + /* Established Timings I & II (all @ 60Hz) */ + { .hpixel = 1280, .vpixel = 1024, .byte = 36, .bit = 0, .hz = 75}, + { .hpixel = 1024, .vpixel = 768, .byte = 36, .bit = 1, .hz = 75}, + { .hpixel = 1024, .vpixel = 768, .byte = 36, .bit = 3, .hz = 60}, + { .hpixel = 800, .vpixel = 600, .byte = 35, .bit = 0, .hz = 60 }, + { .hpixel = 640, .vpixel = 480, .byte = 35, .bit = 5, .hz = 60 }, + + /* Standard Timings */ + { .hpixel = 1920, .vpixel = 1080, .hz = 60, .is_std = true }, + { .hpixel = 1680, .vpixel = 1050, .hz = 60, .is_std = true }, + { .hpixel = 1600, .vpixel = 1200, .hz = 60, .is_std = true }, + { .hpixel = 1600, .vpixel = 900, .hz = 60, .is_std = true }, + { .hpixel = 1440, .vpixel = 900, .hz = 60, .is_std = true }, +}; + +typedef struct frame_param{ + uint32_t hav_pixel; // Horizontal Addressable Video in pixels + uint32_t hb_pixel; // Horizontal Blanking in pixels + uint32_t hfp_pixel; // Horizontal Front Porch in pixels + uint32_t hsp_pixel; // Horizontal Sync Pulse Width in pixels + uint32_t lhb_pixel; // Left Horizontal Border or Right Horizontal + // Border in pixels + + uint32_t vav_line; // Vertical Addressable Video in lines + uint32_t vb_line; // Vertical Blanking in lines + uint32_t vfp_line; // Vertical Front Porch in Lines + uint32_t vsp_line; // Vertical Sync Pulse Width in Lines + uint32_t tvb_line; // Top Vertical Border or Bottom Vertical + // Border in Lines + + uint64_t pixel_clock; // Hz + uint32_t width; // mm + uint32_t height; // mm +}frame_param; + +typedef struct base_param{ + uint32_t h_pixel; // pixels + uint32_t v_pixel; // lines + uint32_t rate; // Hz + uint32_t width; // mm + uint32_t height; // mm + + const char *id_manuf; // ID Manufacturer Name, ISA 3-character ID Code + uint16_t id_product; // ID Product Code + uint32_t id_sn; // ID Serial Number and it is a number only. + + const char *sn; // Serial number. + const char *product_name;// Product name. +}base_param; + +static void +vdpy_edid_set_baseparam(base_param *b_param, uint32_t width, uint32_t height) +{ + b_param->h_pixel = width; + b_param->v_pixel = height; + b_param->rate = 60; + b_param->width = width; + b_param->height = height; + + b_param->id_manuf = "ACRN"; + b_param->id_product = 4321; + b_param->id_sn = 12345678; + + b_param->sn = "A0123456789"; + b_param->product_name = "ACRN_Monitor"; +} + +static void +vdpy_edid_set_frame(frame_param *frame, const base_param *b_param) +{ + frame->hav_pixel = b_param->h_pixel; + frame->hb_pixel = b_param->h_pixel * 35 / 100; + frame->hfp_pixel = b_param->h_pixel * 25 / 100; + frame->hsp_pixel = b_param->h_pixel * 3 / 100; + frame->lhb_pixel = 0; + frame->vav_line = b_param->v_pixel; + frame->vb_line = b_param->v_pixel * 35 / 1000; + frame->vfp_line = b_param->v_pixel * 5 / 1000; + frame->vsp_line = b_param->v_pixel * 5 / 1000; + frame->tvb_line = 0; + frame->pixel_clock = b_param->rate * + (frame->hav_pixel + frame->hb_pixel + frame->lhb_pixel * 2) * + (frame->vav_line + frame->vb_line + frame->tvb_line * 2); + frame->width = b_param->width; + frame->height = b_param->height; +} + +static void +vdpy_edid_set_color(uint8_t *edid, float red_x, float red_y, + float green_x, float green_y, + float blue_x, float blue_y, + float white_x, float white_y) +{ + uint8_t *color; + uint16_t rx, ry, gx, gy, bx, by, wx, wy; + + rx = transto_10bits(red_x); + ry = transto_10bits(red_y); + gx = transto_10bits(green_x); + gy = transto_10bits(green_y); + bx = transto_10bits(blue_x); + by = transto_10bits(blue_y); + wx = transto_10bits(white_x); + wy = transto_10bits(white_y); + + color = edid + 25; + color[0] = ((rx & 0x03) << 6) | + ((ry & 0x03) << 4) | + ((gx & 0x03) << 2) | + (gy & 0x03); + color[1] = ((bx & 0x03) << 6) | + ((by & 0x03) << 4) | + ((wx & 0x03) << 2) | + (wy & 0x03); + color[2] = rx >> 2; + color[3] = ry >> 2; + color[4] = gx >> 2; + color[5] = gy >> 2; + color[6] = bx >> 2; + color[7] = by >> 2; + color[8] = wx >> 2; + color[9] = wy >> 2; +} + +static void +vdpy_edid_set_timing(uint8_t *addr, TIMING_MODE mode) +{ + static uint16_t idx; + static uint16_t size; + const struct timing_entry *timing; + uint8_t stdcnt; + uint16_t hpixel; + int16_t AR; + + stdcnt = 0; + + if(mode == STDT) { + addr += 38; + } + + idx = 0; + size = sizeof(timings) / sizeof(timings[0]); + for(; idx < size; idx++){ + timing = timings + idx; + + switch(mode){ + case ESTT: // Established Timings I & II + if(timing->byte) { + addr[timing->byte] |= (1 << timing->bit); + break; + } else { + continue; + } + case ESTT3: // Established Timings III + if(timing->byte_t3){ + addr[timing->byte_t3] |= (1 << timing->bit); + break; + } else { + continue; + } + case STDT: // Standard Timings + if(stdcnt < 8 && timing->is_std) { + hpixel = (timing->hpixel >> 3) - 31; + if (timing->hpixel == 0 || + timing->vpixel == 0) { + AR = -1; + } else if (hpixel & 0xff00) { + AR = -2; + } else if (timing->hpixel * 10 == + timing->vpixel * 16) { + AR = 0; + } else if (timing->hpixel * 3 == + timing->vpixel * 4) { + AR = 1; + } else if (timing->hpixel * 4 == + timing->vpixel * 5) { + AR = 2; + } else if (timing->hpixel * 9 == + timing->vpixel * 16) { + AR = 3; + } else { + AR = -2; + } + if (AR >= 0) { + addr[0] = hpixel & 0xff; + addr[1] = (AR << 6) | ((timing->hz - 60) & 0x3f); + addr += 2; + stdcnt++; + } else if (AR == -1){ + addr[0] = 0x01; + addr[1] = 0x01; + addr += 2; + stdcnt++; + } + break; + } else { + continue; + } + break; + default: + continue; + } + } + while(mode == STDT && stdcnt < 8){ + addr[0] = 0x01; + addr[1] = 0x01; + addr += 2; + stdcnt++; + } +} + +static void +vdpy_edid_set_dtd(uint8_t *dtd, const frame_param *frame) +{ + uint16_t pixel_clk; + + // Range: 10 kHz to 655.35 MHz in 10 kHz steps + pixel_clk = frame->pixel_clock / 10000; + memcpy(dtd, &pixel_clk, sizeof(pixel_clk)); + dtd[2] = frame->hav_pixel & 0xff; + dtd[3] = frame->hb_pixel & 0xff; + dtd[4] = ((frame->hav_pixel & 0xf00) >> 4) | + ((frame->hb_pixel & 0xf00) >> 8); + dtd[5] = frame->vav_line & 0xff; + dtd[6] = frame->vb_line & 0xff; + dtd[7] = ((frame->vav_line & 0xf00) >> 4) | + ((frame->vb_line & 0xf00) >> 8); + dtd[8] = frame->hfp_pixel & 0xff; + dtd[9] = frame->hsp_pixel & 0xff; + dtd[10] = ((frame->vfp_line & 0xf) << 4) | + (frame->vsp_line & 0xf); + dtd[11] = ((frame->hfp_pixel & 0x300) >> 2) | + ((frame->hsp_pixel & 0x300) >> 4) | + ((frame->vfp_line & 0x030) >> 6) | + ((frame->vsp_line & 0x030) >> 8); + dtd[12] = frame->width & 0xff; + dtd[13] = frame->height & 0xff; + dtd[14] = ((frame->width & 0xf00) >> 4) | + ((frame->height & 0xf00) >> 8); + dtd[15] = frame->lhb_pixel & 0xff; + dtd[16] = frame->tvb_line & 0xff; + dtd[17] = 0x18; +} + +static void +vdpy_edid_set_descripter(uint8_t *desc, uint8_t is_dtd, + uint8_t tag, const base_param *b_param) +{ + frame_param frame; + const char* text; + uint16_t len; + + + if (is_dtd) { + vdpy_edid_set_frame(&frame, b_param); + vdpy_edid_set_dtd(desc, &frame); + return; + } + desc[3] = tag; + text = NULL; + switch(tag){ + // Established Timings III Descriptor (tag #F7h) + case 0xf7: + desc[5] = 0x0a; // Revision Number + vdpy_edid_set_timing(desc, ESTT3); + break; + // Display Range Limits & Additional Timing Descriptor (tag #FDh) + case 0xfd: + desc[5] = 50; // Minimum Vertical Rate. (50 -> 125 Hz) + desc[6] = 125; // Maximum Vertical Rate. + desc[7] = 30; // Minimum Horizontal Rate.(30 -> 160 kHz) + desc[8] = 160; // Maximum Horizontal Rate. + desc[9] = 2550 / 10; // Max Pixel Clock. (2550 MHz) + desc[10] = 0x01; // no extended timing information + desc[11] = '\n'; // padding + break; + // Display Product Name (ASCII) String Descriptor (tag #FCh) + case 0xfc: + // Display Product Serial Number Descriptor (tag #FFh) + case 0xff: + text = (tag == 0xff) ? b_param->sn : b_param->product_name; + memset(desc + 5, ' ', 13); + if (text == NULL) + break; + len = strlen(text); + if (len > 12) + len = 12; + memcpy(desc + 5, text, len); + desc[len + 5] = '\n'; + break; + // Dummy Descriptor (Tag #10h) + case 0x10: + default: + break; + } +} + +static uint8_t +vdpy_edid_get_checksum(uint8_t *edid) +{ + uint8_t sum; + int i; + + sum = 0; + for (i = 0; i < 127; i++) { + sum += edid[i]; + } + + return 0x100 - sum; +} + +static void +vdpy_edid_generate(uint8_t *edid, size_t size, struct edid_info *info) +{ + uint16_t id_manuf; + uint16_t id_product; + uint32_t serial; + uint8_t *desc; + base_param b_param; + + vdpy_edid_set_baseparam(&b_param, info->prefx, info->prefy); + + memset(edid, 0, size); + /* edid[7:0], fixed header information, (00 FF FF FF FF FF FF 00)h */ + memset(edid + 1, 0xff, 6); + + /* edid[17:8], Vendor & Product Identification */ + // Manufacturer ID is a big-endian 16-bit value. + id_manuf = ((((b_param.id_manuf[0] - '@') & 0x1f) << 10) | + (((b_param.id_manuf[1] - '@') & 0x1f) << 5) | + (((b_param.id_manuf[2] - '@') & 0x1f) << 0)); + edid[8] = id_manuf >> 8; + edid[9] = id_manuf & 0xff; + + // Manufacturer product code is a little-endian 16-bit number. + id_product = b_param.id_product; + memcpy(edid+10, &id_product, sizeof(id_product)); + + // Serial number is a little-endian 32-bit value. + serial = b_param.id_sn; + memcpy(edid+12, &serial, sizeof(serial)); + + edid[16] = 0; // Week of Manufacture + edid[17] = 2018 - 1990; // Year of Manufacture or Model Year. + // Acrn is released in 2018. + + edid[18] = 1; // Version Number + edid[19] = 4; // Revision Number + + /* edid[24:20], Basic Display Parameters & Features */ + // Video Input Definition: 1 Byte + edid[20] = 0xa5; // Digital input; + // 8 Bits per Primary Color; + // DisplayPort is supported + + // Horizontal and Vertical Screen Size or Aspect Ratio: 2 Bytes + // screen size, in centimetres + edid[21] = info->prefx / 10; + edid[22] = info->prefy / 10; + + // Display Transfer Characteristics (GAMMA): 1 Byte + // Stored Value = (GAMMA x 100) - 100 + edid[23] = 120; // display gamma: 2.2 + + // Feature Support: 1 Byte + edid[24] = 0x06; // sRGB Standard is the default color space; + // Preferred Timing Mode includes the native + // pixel format and preferred. + + /* edid[34:25], Display x, y Chromaticity Coordinates */ + vdpy_edid_set_color(edid, 0.6400, 0.3300, + 0.3000, 0.6000, + 0.1500, 0.0600, + 0.3127, 0.3290); + + /* edid[37:35], Established Timings */ + vdpy_edid_set_timing(edid, ESTT); + + /* edid[53:38], Standard Timings: Identification 1 -> 8 */ + vdpy_edid_set_timing(edid, STDT); + + /* edid[125:54], Detailed Timing Descriptor - 18 bytes x 4 */ + // Preferred Timing Mode + desc = edid + 54; + vdpy_edid_set_descripter(desc, 0x1, 0, &b_param); + // Display Range Limits & Additional Timing Descriptor (tag #FDh) + desc += 18; + vdpy_edid_set_descripter(desc, 0, 0xfd, &b_param); + // Display Product Name (ASCII) String Descriptor (tag #FCh) + desc += 18; + vdpy_edid_set_descripter(desc, 0, 0xfc, &b_param); + // Display Product Serial Number Descriptor (tag #FFh) + desc += 18; + vdpy_edid_set_descripter(desc, 0, 0xff, &b_param); + + /* EDID[126], Extension Block Count */ + edid[126] = 0; // no Extension Block + + /* Checksum */ + edid[127] = vdpy_edid_get_checksum(edid); +} + +void +vdpy_get_edid(int handle, int scanout_id, uint8_t *edid, size_t size) +{ + struct edid_info edid_info; + struct vscreen *vscr; + + if (scanout_id >= vdpy.vscrs_num) + return; + + vscr = vdpy.vscrs + scanout_id; + + if (handle == vdpy.s.n_connect) { + edid_info.prefx = vscr->info.width; + edid_info.prefy = vscr->info.height; + edid_info.maxx = VDPY_MAX_WIDTH; + edid_info.maxy = VDPY_MAX_HEIGHT; + } else { + edid_info.prefx = VDPY_DEFAULT_WIDTH; + edid_info.prefy = VDPY_DEFAULT_HEIGHT; + edid_info.maxx = VDPY_MAX_WIDTH; + edid_info.maxy = VDPY_MAX_HEIGHT; + } + edid_info.refresh_rate = 0; + edid_info.vendor = NULL; + edid_info.name = NULL; + edid_info.sn = NULL; + + vdpy_edid_generate(edid, size, &edid_info); +} + +void +vdpy_get_display_info(int handle, int scanout_id, struct display_info *info) +{ + struct vscreen *vscr; + + if (scanout_id >= vdpy.vscrs_num) + return; + + vscr = vdpy.vscrs + scanout_id; + + if (handle == vdpy.s.n_connect) { + info->xoff = vscr->info.xoff; + info->yoff = vscr->info.yoff; + info->width = vscr->info.width; + info->height = vscr->info.height; + } else { + info->xoff = 0; + info->yoff = 0; + info->width = 0; + info->height = 0; + } +} + +static void +sdl_gl_display_init(void) +{ + struct egl_display_ops *gl_ops = &vdpy.gl_ops; + struct vscreen *vscr; + int i; + + /* obtain the eglDisplay/eglContext */ + vdpy.eglDisplay = eglGetCurrentDisplay(); + vdpy.eglContext = SDL_GL_GetCurrentContext(); + + /* Try to use the eglGetProcaddress to obtain callback API for + * eglCreateImageKHR/eglDestroyImageKHR + * glEGLImageTargetTexture2DOES + */ + gl_ops->eglCreateImageKHR = (PFNEGLCREATEIMAGEKHRPROC) + eglGetProcAddress("eglCreateImageKHR"); + gl_ops->eglDestroyImageKHR = (PFNEGLDESTROYIMAGEKHRPROC) + eglGetProcAddress("eglDestroyImageKHR"); + gl_ops->glEGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) + eglGetProcAddress("glEGLImageTargetTexture2DOES"); + + for (i = 0; i < vdpy.vscrs_num; i++) { + vscr = vdpy.vscrs + i; + vscr->egl_img = EGL_NO_IMAGE_KHR; + } + + if ((gl_ops->eglCreateImageKHR == NULL) || + (gl_ops->eglDestroyImageKHR == NULL) || + (gl_ops->glEGLImageTargetTexture2DOES == NULL)) { + pr_info("DMABuf is not supported.\n"); + vdpy.egl_dmabuf_supported = false; + } else + vdpy.egl_dmabuf_supported = true; + + return; +} + +static void sdl_gl_prepare_draw(struct vscreen *vscr) +{ + SDL_Rect bogus_rect; + + if (vscr == NULL) + return; + + bogus_rect.x = 0; + bogus_rect.y = 0; + bogus_rect.w = 32; + bogus_rect.h = 32; + /* The limitation in libSDL causes that ACRN can't display framebuffer + * correctly on one window when using multi SDL_context to displaying + * the framebuffers under multi-display scenario. + * The small texture is added to workaround the display issue caused by + * libSDL limitation. + * Todo: Keep monitoring the libSDL to check whether the limitation is + * fixed. + */ + SDL_RenderClear(vscr->renderer); + SDL_RenderCopy(vscr->renderer, vscr->bogus_tex, NULL, &bogus_rect); + return; +} + +void +vdpy_surface_set(int handle, int scanout_id, struct surface *surf) +{ + pixman_image_t *src_img; + int format; + int access, i; + struct vscreen *vscr; + + if (handle != vdpy.s.n_connect) { + return; + } + + if (vdpy.tid != pthread_self()) { + pr_err("%s: unexpected code path as unsafe 3D ops in multi-threads env.\n", + __func__); + return; + } + + if (scanout_id >= vdpy.vscrs_num) { + return; + } + + vscr = vdpy.vscrs + scanout_id; + + if (surf == NULL ) { + vscr->surf.width = 0; + vscr->surf.height = 0; + /* Need to use the default 640x480 for the SDL_Texture */ + src_img = pixman_image_create_bits(PIXMAN_a8r8g8b8, + VDPY_MIN_WIDTH, VDPY_MIN_HEIGHT, + (uint32_t *)default_raw_argb, + VDPY_MIN_WIDTH * 4); + if (src_img == NULL) { + pr_err("failed to create pixman_image\n"); + return; + } + vscr->guest_width = VDPY_MIN_WIDTH; + vscr->guest_height = VDPY_MIN_HEIGHT; + } else if (surf->surf_type == SURFACE_PIXMAN) { + src_img = pixman_image_create_bits(surf->surf_format, + surf->width, surf->height, surf->pixel, + surf->stride); + if (src_img == NULL) { + pr_err("failed to create pixman_image\n"); + return; + } + vscr->surf = *surf; + vscr->guest_width = surf->width; + vscr->guest_height = surf->height; + } else if (surf->surf_type == SURFACE_DMABUF) { + src_img = NULL; + vscr->surf = *surf; + vscr->guest_width = surf->width; + vscr->guest_height = surf->height; + } else { + /* Unsupported type */ + return; + } + + if (vscr->surf_tex) { + SDL_DestroyTexture(vscr->surf_tex); + } + if (surf && (surf->surf_type == SURFACE_DMABUF)) { + access = SDL_TEXTUREACCESS_STATIC; + format = SDL_PIXELFORMAT_EXTERNAL_OES; + } else { + access = SDL_TEXTUREACCESS_STREAMING; + format = SDL_PIXELFORMAT_ARGB8888; + switch (pixman_image_get_format(src_img)) { + case PIXMAN_a8r8g8b8: + case PIXMAN_x8r8g8b8: + format = SDL_PIXELFORMAT_ARGB8888; + break; + case PIXMAN_a8b8g8r8: + case PIXMAN_x8b8g8r8: + format = SDL_PIXELFORMAT_ABGR8888; + break; + case PIXMAN_r8g8b8a8: + format = SDL_PIXELFORMAT_RGBA8888; + case PIXMAN_r8g8b8x8: + format = SDL_PIXELFORMAT_RGBX8888; + break; + case PIXMAN_b8g8r8a8: + case PIXMAN_b8g8r8x8: + format = SDL_PIXELFORMAT_BGRA8888; + break; + default: + pr_err("Unsupported format. %x\n", + pixman_image_get_format(src_img)); + } + } + vscr->surf_tex = SDL_CreateTexture(vscr->renderer, + format, access, + vscr->guest_width, vscr->guest_height); + + if (vscr->surf_tex == NULL) { + pr_err("Failed to create SDL_texture for surface.\n"); + } + + /* For the surf_switch, it will be updated in surface_update */ + if (!surf) { + SDL_UpdateTexture(vscr->surf_tex, NULL, + pixman_image_get_data(src_img), + pixman_image_get_stride(src_img)); + sdl_gl_prepare_draw(vscr); + SDL_RenderCopy(vscr->renderer, vscr->surf_tex, NULL, NULL); + SDL_RenderPresent(vscr->renderer); + } else if (surf->surf_type == SURFACE_DMABUF) { + EGLImageKHR egl_img = EGL_NO_IMAGE_KHR; + EGLint attrs[64]; + struct egl_display_ops *gl_ops; + + gl_ops = &vdpy.gl_ops; + i = 0; + attrs[i++] = EGL_WIDTH; + attrs[i++] = surf->width; + attrs[i++] = EGL_HEIGHT; + attrs[i++] = surf->height; + attrs[i++] = EGL_LINUX_DRM_FOURCC_EXT; + attrs[i++] = surf->dma_info.surf_fourcc; + attrs[i++] = EGL_DMA_BUF_PLANE0_FD_EXT; + attrs[i++] = surf->dma_info.dmabuf_fd; + attrs[i++] = EGL_DMA_BUF_PLANE0_PITCH_EXT; + attrs[i++] = surf->stride; + attrs[i++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT; + attrs[i++] = surf->dma_info.dmabuf_offset; + attrs[i++] = EGL_NONE; + + egl_img = gl_ops->eglCreateImageKHR(vdpy.eglDisplay, + EGL_NO_CONTEXT, + EGL_LINUX_DMA_BUF_EXT, + NULL, attrs); + if (egl_img == EGL_NO_IMAGE_KHR) { + pr_err("Failed in eglCreateImageKHR.\n"); + return; + } + + SDL_GL_BindTexture(vscr->surf_tex, NULL, NULL); + gl_ops->glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, egl_img); + if (vscr->egl_img != EGL_NO_IMAGE_KHR) + gl_ops->eglDestroyImageKHR(vdpy.eglDisplay, + vscr->egl_img); + + /* In theory the created egl_img can be released after it is bound + * to texture. + * Now it is released next time so that it is controlled correctly + */ + vscr->egl_img = egl_img; + } + + if (vscr->img) + pixman_image_unref(vscr->img); + + if (surf == NULL) { + SDL_SetWindowTitle(vscr->win, + "Not activate display yet!"); + } else { + SDL_SetWindowTitle(vscr->win, + "ACRN Virtual Monitor"); + } + /* Replace the cur_img with the created_img */ + vscr->img = src_img; +} + +void +vdpy_cursor_position_transformation(struct display *vdpy, int scanout_id, SDL_Rect *rect) +{ + struct vscreen *vscr; + + if (scanout_id >= vdpy->vscrs_num) { + return; + } + + vscr = vdpy->vscrs + scanout_id; + rect->x = (vscr->cur.x * vscr->width) / vscr->guest_width; + rect->y = (vscr->cur.y * vscr->height) / vscr->guest_height; + rect->w = (vscr->cur.width * vscr->width) / vscr->guest_width; + rect->h = (vscr->cur.height * vscr->height) / vscr->guest_height; +} + +void +vdpy_surface_update(int handle, int scanout_id, struct surface *surf) +{ + SDL_Rect cursor_rect; + struct vscreen *vscr; + + if (handle != vdpy.s.n_connect) { + return; + } + + if (vdpy.tid != pthread_self()) { + pr_err("%s: unexpected code path as unsafe 3D ops in multi-threads env.\n", + __func__); + return; + } + + if (!surf) { + pr_err("Incorrect order of submitting Virtio-GPU cmd.\n"); + return; + } + + if (scanout_id >= vdpy.vscrs_num) { + return; + } + + vscr = vdpy.vscrs + scanout_id; + if (surf->surf_type == SURFACE_PIXMAN) + SDL_UpdateTexture(vscr->surf_tex, NULL, + surf->pixel, + surf->stride); + + sdl_gl_prepare_draw(vscr); + SDL_RenderCopy(vscr->renderer, vscr->surf_tex, NULL, NULL); + + /* This should be handled after rendering the surface_texture. + * Otherwise it will be hidden + */ + if (vscr->cur_tex) { + vdpy_cursor_position_transformation(&vdpy, scanout_id, &cursor_rect); + SDL_RenderCopy(vscr->renderer, vscr->cur_tex, + NULL, &cursor_rect); + } + + SDL_RenderPresent(vscr->renderer); + + /* update the rendering time */ + clock_gettime(CLOCK_MONOTONIC, &vscr->last_time); +} + +void +vdpy_cursor_define(int handle, int scanout_id, struct cursor *cur) +{ + struct vscreen *vscr; + + if (handle != vdpy.s.n_connect) { + return; + } + + if (vdpy.tid != pthread_self()) { + pr_err("%s: unexpected code path as unsafe 3D ops in multi-threads env.\n", + __func__); + return; + } + + if (scanout_id >= vdpy.vscrs_num) { + return; + } + + if (cur->data == NULL) + return; + + vscr = vdpy.vscrs + scanout_id; + + if (vscr->cur_tex) + SDL_DestroyTexture(vscr->cur_tex); + + vscr->cur_tex = SDL_CreateTexture( + vscr->renderer, + SDL_PIXELFORMAT_ARGB8888, + SDL_TEXTUREACCESS_STREAMING, + cur->width, cur->height); + if (vscr->cur_tex == NULL) { + pr_err("Failed to create sdl_cursor surface for %p.\n", cur); + return; + } + + SDL_SetTextureBlendMode(vscr->cur_tex, SDL_BLENDMODE_BLEND); + vscr->cur = *cur; + SDL_UpdateTexture(vscr->cur_tex, NULL, cur->data, cur->width * 4); +} + +void +vdpy_cursor_move(int handle, int scanout_id, uint32_t x, uint32_t y) +{ + struct vscreen *vscr; + + if (handle != vdpy.s.n_connect) { + return; + } + + if (scanout_id >= vdpy.vscrs_num) { + return; + } + + vscr = vdpy.vscrs + scanout_id; + /* Only move the position of the cursor. The cursor_texture + * will be handled in surface_update + */ + vscr->cur.x = x; + vscr->cur.y = y; +} + +static void +vdpy_sdl_ui_refresh(void *data) +{ + struct display *ui_vdpy; + struct timespec cur_time; + uint64_t elapsed_time; + SDL_Rect cursor_rect; + struct vscreen *vscr; + int i; + + ui_vdpy = (struct display *)data; + + for (i = 0; i < vdpy.vscrs_num; i++) { + vscr = ui_vdpy->vscrs + i; + + /* Skip it if no surface needs to be rendered */ + if (vscr->surf_tex == NULL) + continue; + + clock_gettime(CLOCK_MONOTONIC, &cur_time); + + elapsed_time = (cur_time.tv_sec - vscr->last_time.tv_sec) * 1000000000 + + cur_time.tv_nsec - vscr->last_time.tv_nsec; + + /* the time interval is less than 10ms. Skip it */ + if (elapsed_time < 10000000) + return; + + sdl_gl_prepare_draw(vscr); + SDL_RenderCopy(vscr->renderer, vscr->surf_tex, NULL, NULL); + + /* This should be handled after rendering the surface_texture. + * Otherwise it will be hidden + */ + if (vscr->cur_tex) { + vdpy_cursor_position_transformation(ui_vdpy, i, &cursor_rect); + SDL_RenderCopy(vscr->renderer, vscr->cur_tex, + NULL, &cursor_rect); + } + + SDL_RenderPresent(vscr->renderer); + } +} + +static void +vdpy_sdl_ui_timer(void *data, uint64_t nexp) +{ + struct display *ui_vdpy; + struct vdpy_display_bh *bh_task; + + ui_vdpy = (struct display *)data; + + /* Don't submit the display_request if another func already + * acquires the mutex. + * This is to optimize the mevent thread otherwise it needs + * to wait for some time. + */ + if (pthread_mutex_trylock(&ui_vdpy->vdisplay_mutex)) + return; + + bh_task = &ui_vdpy->ui_timer_bh; + if ((bh_task->bh_flag & ACRN_BH_PENDING) == 0) { + bh_task->bh_flag |= ACRN_BH_PENDING; + TAILQ_INSERT_TAIL(&ui_vdpy->request_list, bh_task, link); + } + pthread_cond_signal(&ui_vdpy->vdisplay_signal); + pthread_mutex_unlock(&ui_vdpy->vdisplay_mutex); +} + +void +vdpy_calibrate_vscreen_geometry(struct vscreen *vscr) +{ + if (vscr->guest_width && vscr->guest_height) { + /* clip the region between (640x480) and (1920x1080) */ + if (vscr->guest_width < VDPY_MIN_WIDTH) + vscr->guest_width = VDPY_MIN_WIDTH; + if (vscr->guest_width > VDPY_MAX_WIDTH) + vscr->guest_width = VDPY_MAX_WIDTH; + if (vscr->guest_height < VDPY_MIN_HEIGHT) + vscr->guest_height = VDPY_MIN_HEIGHT; + if (vscr->guest_height > VDPY_MAX_HEIGHT) + vscr->guest_height = VDPY_MAX_HEIGHT; + } else { + /* the default window(1280x720) is created with undefined pos + * when no geometry info is passed + */ + vscr->org_x = 0xFFFF; + vscr->org_y = 0xFFFF; + vscr->guest_width = VDPY_DEFAULT_WIDTH; + vscr->guest_height = VDPY_DEFAULT_HEIGHT; + } +} + +int +vdpy_create_vscreen_window(struct vscreen *vscr) +{ + uint32_t win_flags; + + win_flags = SDL_WINDOW_OPENGL | + SDL_WINDOW_ALWAYS_ON_TOP | + SDL_WINDOW_SHOWN; + if (vscr->is_fullscreen) { + win_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; + vscr->org_x = vscr->pscreen_rect.x; + vscr->org_y = vscr->pscreen_rect.y; + vscr->width = vscr->pscreen_rect.w; + vscr->height = vscr->pscreen_rect.h; + } else { + vscr->width = vscr->guest_width; + vscr->height = vscr->guest_height; + } + vscr->win = NULL; + vscr->renderer = NULL; + vscr->img = NULL; + // Zoom to width and height of pscreen is fullscreen enabled + vscr->win = SDL_CreateWindow("ACRN_DM", + vscr->org_x, vscr->org_y, + vscr->width, vscr->height, + win_flags); + if (vscr->win == NULL) { + pr_err("Failed to Create SDL_Window\n"); + return -1; + } + pr_info("SDL display bind to screen %d: [%d,%d,%d,%d].\n", vscr->pscreen_id, + vscr->org_x, vscr->org_y, vscr->width, vscr->height); + + vscr->renderer = SDL_CreateRenderer(vscr->win, -1, 0); + if (vscr->renderer == NULL) { + pr_err("Failed to Create GL_Renderer \n"); + return -1; + } + vscr->bogus_tex = SDL_CreateTexture(vscr->renderer, + SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC, + 32, 32); + if (vscr->bogus_tex == NULL) { + pr_err("%s: Failed to create SDL_Texture\n", __func__); + return -1; + } + SDL_SetTextureColorMod(vscr->bogus_tex, 0x80, 0x80, 0x80); + + + return 0; +} + +static void * +vdpy_sdl_display_thread(void *data) +{ + struct vdpy_display_bh *bh; + struct itimerspec ui_timer_spec; + + struct vscreen *vscr; + int i; + + for (i = 0; i < vdpy.vscrs_num; i++) { + vscr = vdpy.vscrs + i; + + vdpy_calibrate_vscreen_geometry(vscr); + + vscr->info.xoff = vscr->org_x; + vscr->info.yoff = vscr->org_y; + vscr->info.width = vscr->guest_width; + vscr->info.height = vscr->guest_height; + + if (vdpy_create_vscreen_window(vscr)) { + goto sdl_fail; + } + clock_gettime(CLOCK_MONOTONIC, &vscr->last_time); + } + sdl_gl_display_init(); + pthread_mutex_init(&vdpy.vdisplay_mutex, NULL); + pthread_cond_init(&vdpy.vdisplay_signal, NULL); + TAILQ_INIT(&vdpy.request_list); + vdpy.s.is_active = 1; + + vdpy.ui_timer_bh.task_cb = vdpy_sdl_ui_refresh; + vdpy.ui_timer_bh.data = &vdpy; + vdpy.ui_timer.clockid = CLOCK_MONOTONIC; + acrn_timer_init(&vdpy.ui_timer, vdpy_sdl_ui_timer, &vdpy); + ui_timer_spec.it_interval.tv_sec = 0; + ui_timer_spec.it_interval.tv_nsec = 33000000; + /* Wait for 5s to start the timer */ + ui_timer_spec.it_value.tv_sec = 5; + ui_timer_spec.it_value.tv_nsec = 0; + /* Start one periodic timer to refresh UI based on 30fps */ + acrn_timer_settime(&vdpy.ui_timer, &ui_timer_spec); + + pr_info("SDL display thread is created\n"); + /* Begin to process the display_cmd after initialization */ + do { + if (!vdpy.s.is_active) { + pr_info("display is exiting\n"); + break; + } + pthread_mutex_lock(&vdpy.vdisplay_mutex); + + if (TAILQ_EMPTY(&vdpy.request_list)) + pthread_cond_wait(&vdpy.vdisplay_signal, + &vdpy.vdisplay_mutex); + + /* the bh_task is handled in vdisplay_mutex lock */ + while (!TAILQ_EMPTY(&vdpy.request_list)) { + bh = TAILQ_FIRST(&vdpy.request_list); + + TAILQ_REMOVE(&vdpy.request_list, bh, link); + + bh->task_cb(bh->data); + + if (atomic_load(&bh->bh_flag) & ACRN_BH_FREE) { + free(bh); + bh = NULL; + } else { + /* free is owned by the submitter */ + atomic_store(&bh->bh_flag, ACRN_BH_DONE); + } + } + + pthread_mutex_unlock(&vdpy.vdisplay_mutex); + } while (1); + + acrn_timer_deinit(&vdpy.ui_timer); + /* SDL display_thread will exit because of DM request */ + pthread_mutex_destroy(&vdpy.vdisplay_mutex); + pthread_cond_destroy(&vdpy.vdisplay_signal); + + for (i = 0; i < vdpy.vscrs_num; i++) { + vscr = vdpy.vscrs + i; + if (vscr->img) { + pixman_image_unref(vscr->img); + vscr->img = NULL; + } + /* Continue to thread cleanup */ + if (vscr->surf_tex) { + SDL_DestroyTexture(vscr->surf_tex); + vscr->surf_tex = NULL; + } + if (vscr->cur_tex) { + SDL_DestroyTexture(vscr->cur_tex); + vscr->cur_tex = NULL; + } + + if (vdpy.egl_dmabuf_supported && (vscr->egl_img != EGL_NO_IMAGE_KHR)) + vdpy.gl_ops.eglDestroyImageKHR(vdpy.eglDisplay, + vscr->egl_img); + } + +sdl_fail: + for (i = 0; i < vdpy.vscrs_num; i++) { + vscr = vdpy.vscrs + i; + if (vscr->bogus_tex) { + SDL_DestroyTexture(vscr->bogus_tex); + vscr->bogus_tex = NULL; + } + if (vscr->renderer) { + SDL_DestroyRenderer(vscr->renderer); + vscr->renderer = NULL; + } + if (vscr->win) { + SDL_DestroyWindow(vscr->win); + vscr->win = NULL; + } + } + + /* This is used to workaround the TLS issue of libEGL + libGLdispatch + * after unloading library. + */ + eglReleaseThread(); + return NULL; +} + +bool vdpy_submit_bh(int handle, struct vdpy_display_bh *bh_task) +{ + bool bh_ok = false; + + if (handle != vdpy.s.n_connect) { + return bh_ok; + } + + if (!vdpy.s.is_active) + return bh_ok; + + pthread_mutex_lock(&vdpy.vdisplay_mutex); + + if ((bh_task->bh_flag & ACRN_BH_PENDING) == 0) { + bh_task->bh_flag |= ACRN_BH_PENDING; + TAILQ_INSERT_TAIL(&vdpy.request_list, bh_task, link); + bh_ok = true; + } + pthread_cond_signal(&vdpy.vdisplay_signal); + pthread_mutex_unlock(&vdpy.vdisplay_mutex); + + return bh_ok; +} + +int +vdpy_init(int *num_vscreens) +{ + int err, count; + + if (vdpy.s.n_connect) { + return 0; + } + + /* start one vdpy_sdl_display_thread to handle the 3D request + * in this dedicated thread. Otherwise the libSDL + 3D doesn't + * work. + */ + err = pthread_create(&vdpy.tid, NULL, vdpy_sdl_display_thread, &vdpy); + if (err) { + pr_err("Failed to create the sdl_display_thread.\n"); + return 0; + } + pthread_setname_np(vdpy.tid, "acrn_vdisplay"); + count = 0; + /* Wait up to 200ms so that the vdpy_sdl_display_thread is ready to + * handle the 3D request + */ + while (!vdpy.s.is_active && count < 20) { + usleep(10000); + count++; + } + if (!vdpy.s.is_active) { + pr_err("display_thread is not ready.\n"); + } + + vdpy.s.n_connect++; + if (num_vscreens) + *num_vscreens = vdpy.vscrs_num; + return vdpy.s.n_connect; +} + +int +vdpy_deinit(int handle) +{ + if (handle != vdpy.s.n_connect) { + return -1; + } + + vdpy.s.n_connect--; + + if (!vdpy.s.is_active) { + return -1; + } + + pthread_mutex_lock(&vdpy.vdisplay_mutex); + vdpy.s.is_active = 0; + /* Wakeup the vdpy_sdl_display_thread if it is waiting for signal */ + pthread_cond_signal(&vdpy.vdisplay_signal); + pthread_mutex_unlock(&vdpy.vdisplay_mutex); + + pthread_join(vdpy.tid, NULL); + pr_info("Exit SDL display thread\n"); + + return 0; +} + +int +gfx_ui_init() +{ + SDL_SysWMinfo info; + int num_pscreen; + struct vscreen *vscr; + int i; + + setenv("SDL_VIDEO_X11_FORCE_EGL", "1", 1); + setenv("SDL_OPENGL_ES_DRIVER", "1", 1); + setenv("SDL_RENDER_DRIVER", "opengles2", 1); + setenv("SDL_RENDER_SCALE_QUALITY", "linear", 1); + + if (SDL_Init(SDL_INIT_VIDEO)) { + pr_err("Failed to init SDL2 system\n"); + return -1; + } + + if (vdpy.vscrs_num <= 0) { + pr_err("Incorrect geometry parameter for virtio-gpu\n"); + return -1; + } + num_pscreen = SDL_GetNumVideoDisplays(); + + for (i = 0; i < vdpy.vscrs_num; i++) { + vscr = vdpy.vscrs + i; + if (vscr->pscreen_id >= num_pscreen) { + pr_err("Monitor id %d is out of avalble range [0~%d].\n", + vscr->pscreen_id, num_pscreen); + SDL_Quit(); + return -1; + } + + SDL_GetDisplayBounds(vscr->pscreen_id, &vscr->pscreen_rect); + + if (vscr->pscreen_rect.w < VDPY_MIN_WIDTH || + vscr->pscreen_rect.h < VDPY_MIN_HEIGHT) { + pr_err("Too small resolutions. Please check the " + " graphics system\n"); + SDL_Quit(); + return -1; + } + } + + SDL_SetHint(SDL_HINT_GRAB_KEYBOARD, "1"); + memset(&info, 0, sizeof(info)); + SDL_VERSION(&info.version); + + /* Set the GL_parameter for Window/Renderer */ + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, + SDL_GL_CONTEXT_PROFILE_ES); + /* GLES2.0 is used */ + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + + /* GL target surface selects A8/R8/G8/B8 */ + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); + + vdpy.s.is_ui_realized = true; + + return 0; +} + +void +gfx_ui_deinit() +{ + if (!vdpy.s.is_ui_realized) { + return; + } + + free(vdpy.vscrs); + SDL_Quit(); + pr_info("SDL_Quit\r\n"); +} + +int vdpy_parse_cmd_option(const char *opts) +{ + char *str, *stropts, *tmp; + int snum, error; + struct vscreen *vscr; + + error = 0; + vdpy.vscrs = calloc(VSCREEN_MAX_NUM, sizeof(struct vscreen)); + vdpy.vscrs_num = 0; + + stropts = strdup(opts); + while ((str = strsep(&stropts, ",")) != NULL) { + vscr = vdpy.vscrs + vdpy.vscrs_num; + tmp = strcasestr(str, "geometry="); + if (str && strcasestr(str, "geometry=fullscreen")) { + snum = sscanf(tmp, "geometry=fullscreen:%d", &vscr->pscreen_id); + if (snum != 1) { + vscr->pscreen_id = 0; + } + vscr->org_x = 0; + vscr->org_y = 0; + vscr->guest_width = VDPY_MAX_WIDTH; + vscr->guest_height = VDPY_MAX_HEIGHT; + vscr->is_fullscreen = true; + pr_info("virtual display: fullscreen on monitor %d.\n", + vscr->pscreen_id); + vdpy.vscrs_num++; + } else if (str && strcasestr(str, "geometry=")) { + snum = sscanf(tmp, "geometry=%dx%d+%d+%d", + &vscr->guest_width, &vscr->guest_height, + &vscr->org_x, &vscr->org_y); + if (snum != 4) { + pr_err("incorrect geometry option. Should be" + " WxH+x+y\n"); + error = -1; + } + vscr->is_fullscreen = false; + vscr->pscreen_id = 0; + pr_info("virtual display: windowed on monitor %d.\n", + vscr->pscreen_id); + vdpy.vscrs_num++; + } + + if (vdpy.vscrs_num > VSCREEN_MAX_NUM) { + pr_err("%d virtual displays are too many that acrn-dm can't support!\n"); + break; + } + } + free(stropts); + + return error; +} diff --git a/devicemodel/hw/vdisplay_server.c b/devicemodel/hw/vdisplay_server.c new file mode 100644 index 0000000..abab877 --- /dev/null +++ b/devicemodel/hw/vdisplay_server.c @@ -0,0 +1,1232 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +// #include +// #include +// #include +#include +#include +#include +#include +#include "log.h" +#include "vdisplay.h" +#include "vdisplay_protocol.h" +#include "atomic.h" +#include "timer.h" + + +#define VDPY_MAX_WIDTH 3840 +#define VDPY_MAX_HEIGHT 2160 +#define VDPY_DEFAULT_WIDTH 1024 +#define VDPY_DEFAULT_HEIGHT 768 +#define VDPY_MIN_WIDTH 640 +#define VDPY_MIN_HEIGHT 480 +#define transto_10bits(color) (uint16_t)(color * 1024 + 0.5) +#define VSCREEN_MAX_NUM VDPY_MAX_NUM +#define EDID_BASIC_BLOCK_SIZE 128 +#define EDID_CEA861_EXT_BLOCK_SIZE 128 + +struct state { + bool is_ui_realized; + bool is_active; + bool is_wayland; + bool is_x11; + bool is_fullscreen; + bool is_termed; + uint64_t updates; + int n_connect; +}; + +typedef struct{ + short x, y; + short w, h; +} SDL_Rect; + +struct vscreen { + struct display_info info; + int pscreen_id; + SDL_Rect pscreen_rect; + bool is_fullscreen; + bool set_modifier; + int org_x; + int org_y; + int width; // sdl window width + int height; // sdl window height + int guest_width; // image/tex width + int guest_height; // image/tex width + struct surface surf; + struct cursor cur; + uint64_t modifier; + // GLuint surf_tex; + // GLuint cur_tex; + // GLuint bogus_tex; + // int surf_format; + // int surf_updates; + // int cur_updates; + // pixman_image_t *img; + // EGLImage egl_img; + /* Record the update_time that is activated from guest_vm */ + struct timespec last_time; +}; + +static struct display { + struct state s; + struct vscreen *vscrs; + int vscrs_num; + pthread_t tid; + pthread_t server_tid; + /* Add one UI_timer(33ms) to render the buffers from guest_vm */ + struct acrn_timer ui_timer; + struct vdpy_display_bh ui_timer_bh; + // protect the request_list + pthread_mutex_t vdisplay_mutex; + // receive the signal that request is submitted + pthread_cond_t vdisplay_signal; + pthread_mutex_t client_mutex; + TAILQ_HEAD(display_list, vdpy_display_bh) request_list; + /* add the below two fields for calling eglAPI directly */ + // bool egl_dmabuf_supported; + // SDL_GLContext eglContext; + // EGLContext eglContext; + // EGLDisplay eglDisplay; + // EGLSurface eglSurface; + // struct egl_display_ops gl_ops; + + // Handle to a program object + // GLuint programObject; + // GLuint programObjectExternal; +} vdpy = { + .s.is_ui_realized = false, + .s.is_active = false, + .s.is_wayland = false, + .s.is_x11 = false, + .s.n_connect = 0, + // .eglDisplay = EGL_NO_DISPLAY, + // .eglContext = EGL_NO_CONTEXT, + // .eglSurface = EGL_NO_SURFACE +}; + +typedef enum { + ESTT = 1, // Established Timings I & II + STDT, // Standard Timings + ESTT3, // Established Timings III + CEA861, // CEA-861 Timings +} TIMING_MODE; + +static const struct timing_entry { + uint32_t hpixel;// Horizontal pixels + uint32_t vpixel;// Vertical pixels + uint32_t byte; // byte idx in the Established Timings I & II + uint32_t byte_t3;// byte idx in the Established Timings III Descriptor + uint32_t bit; // bit idx + uint8_t hz; // frequency + bool is_std; // the flag of standard mode + bool is_cea861; // CEA-861 timings + uint8_t vic; // Video Indentification Code +} timings[] = { + /* Established Timings I & II (all @ 60Hz) */ + { .hpixel = 1280, .vpixel = 1024, .byte = 36, .bit = 0, .hz = 75}, + { .hpixel = 1024, .vpixel = 768, .byte = 36, .bit = 1, .hz = 75}, + { .hpixel = 1024, .vpixel = 768, .byte = 36, .bit = 3, .hz = 60}, + { .hpixel = 800, .vpixel = 600, .byte = 35, .bit = 0, .hz = 60 }, + { .hpixel = 640, .vpixel = 480, .byte = 35, .bit = 5, .hz = 60 }, + + /* Standard Timings */ + { .hpixel = 1920, .vpixel = 1080, .hz = 60, .is_std = true }, + { .hpixel = 1680, .vpixel = 1050, .hz = 60, .is_std = true }, + { .hpixel = 1600, .vpixel = 1200, .hz = 60, .is_std = true }, + { .hpixel = 1600, .vpixel = 900, .hz = 60, .is_std = true }, + { .hpixel = 1440, .vpixel = 900, .hz = 60, .is_std = true }, + + /* CEA-861 Timings */ + { .hpixel = 3840, .vpixel = 2160, .hz = 60, .is_cea861 = true, .vic = 97 }, +}; + +typedef struct frame_param{ + uint32_t hav_pixel; // Horizontal Addressable Video in pixels + uint32_t hb_pixel; // Horizontal Blanking in pixels + uint32_t hfp_pixel; // Horizontal Front Porch in pixels + uint32_t hsp_pixel; // Horizontal Sync Pulse Width in pixels + uint32_t lhb_pixel; // Left Horizontal Border or Right Horizontal + // Border in pixels + + uint32_t vav_line; // Vertical Addressable Video in lines + uint32_t vb_line; // Vertical Blanking in lines + uint32_t vfp_line; // Vertical Front Porch in Lines + uint32_t vsp_line; // Vertical Sync Pulse Width in Lines + uint32_t tvb_line; // Top Vertical Border or Bottom Vertical + // Border in Lines + + uint64_t pixel_clock; // Hz + uint32_t width; // mm + uint32_t height; // mm +}frame_param; + +typedef struct base_param{ + uint32_t h_pixel; // pixels + uint32_t v_pixel; // lines + uint32_t rate; // Hz + uint32_t width; // mm + uint32_t height; // mm + + const char *id_manuf; // ID Manufacturer Name, ISA 3-character ID Code + uint16_t id_product; // ID Product Code + uint32_t id_sn; // ID Serial Number and it is a number only. + + const char *sn; // Serial number. + const char *product_name;// Product name. +}base_param; + +static void +vdpy_edid_set_baseparam(base_param *b_param, uint32_t width, uint32_t height) +{ + b_param->h_pixel = width; + b_param->v_pixel = height; + b_param->rate = 60; + b_param->width = width; + b_param->height = height; + + b_param->id_manuf = "ACRN"; + b_param->id_product = 4321; + b_param->id_sn = 12345678; + + b_param->sn = "A0123456789"; + b_param->product_name = "ACRN_Monitor"; +} + +static void +vdpy_edid_set_frame(frame_param *frame, const base_param *b_param) +{ + frame->hav_pixel = b_param->h_pixel; + frame->hb_pixel = b_param->h_pixel * 35 / 100; + frame->hfp_pixel = b_param->h_pixel * 25 / 100; + frame->hsp_pixel = b_param->h_pixel * 3 / 100; + frame->lhb_pixel = 0; + frame->vav_line = b_param->v_pixel; + frame->vb_line = b_param->v_pixel * 35 / 1000; + frame->vfp_line = b_param->v_pixel * 5 / 1000; + frame->vsp_line = b_param->v_pixel * 5 / 1000; + frame->tvb_line = 0; + frame->pixel_clock = b_param->rate * + (frame->hav_pixel + frame->hb_pixel + frame->lhb_pixel * 2) * + (frame->vav_line + frame->vb_line + frame->tvb_line * 2); + frame->width = b_param->width; + frame->height = b_param->height; +} + +static void +vdpy_edid_set_color(uint8_t *edid, float red_x, float red_y, + float green_x, float green_y, + float blue_x, float blue_y, + float white_x, float white_y) +{ + uint8_t *color; + uint16_t rx, ry, gx, gy, bx, by, wx, wy; + + rx = transto_10bits(red_x); + ry = transto_10bits(red_y); + gx = transto_10bits(green_x); + gy = transto_10bits(green_y); + bx = transto_10bits(blue_x); + by = transto_10bits(blue_y); + wx = transto_10bits(white_x); + wy = transto_10bits(white_y); + + color = edid + 25; + color[0] = ((rx & 0x03) << 6) | + ((ry & 0x03) << 4) | + ((gx & 0x03) << 2) | + (gy & 0x03); + color[1] = ((bx & 0x03) << 6) | + ((by & 0x03) << 4) | + ((wx & 0x03) << 2) | + (wy & 0x03); + color[2] = rx >> 2; + color[3] = ry >> 2; + color[4] = gx >> 2; + color[5] = gy >> 2; + color[6] = bx >> 2; + color[7] = by >> 2; + color[8] = wx >> 2; + color[9] = wy >> 2; +} + +static uint8_t +vdpy_edid_set_timing(uint8_t *addr, TIMING_MODE mode) +{ + static uint16_t idx; + static uint16_t size; + const struct timing_entry *timing; + uint8_t stdcnt; + uint16_t hpixel; + int16_t AR; + uint8_t num_timings; + + stdcnt = 0; + num_timings = 0; + + if(mode == STDT) { + addr += 38; + } + + idx = 0; + size = sizeof(timings) / sizeof(timings[0]); + for(; idx < size; idx++){ + timing = timings + idx; + + switch(mode){ + case ESTT: // Established Timings I & II + if(timing->byte) { + addr[timing->byte] |= (1 << timing->bit); + break; + } else { + continue; + } + case ESTT3: // Established Timings III + if(timing->byte_t3){ + addr[timing->byte_t3] |= (1 << timing->bit); + break; + } else { + continue; + } + case STDT: // Standard Timings + if(stdcnt < 8 && timing->is_std) { + hpixel = (timing->hpixel >> 3) - 31; + if (timing->hpixel == 0 || + timing->vpixel == 0) { + AR = -1; + } else if (hpixel & 0xff00) { + AR = -2; + } else if (timing->hpixel * 10 == + timing->vpixel * 16) { + AR = 0; + } else if (timing->hpixel * 3 == + timing->vpixel * 4) { + AR = 1; + } else if (timing->hpixel * 4 == + timing->vpixel * 5) { + AR = 2; + } else if (timing->hpixel * 9 == + timing->vpixel * 16) { + AR = 3; + } else { + AR = -2; + } + if (AR >= 0) { + addr[0] = hpixel & 0xff; + addr[1] = (AR << 6) | ((timing->hz - 60) & 0x3f); + addr += 2; + stdcnt++; + } else if (AR == -1){ + addr[0] = 0x01; + addr[1] = 0x01; + addr += 2; + stdcnt++; + } + break; + } else { + continue; + } + break; + case CEA861: // CEA-861 Timings + if (timing->is_cea861) { + addr[0] = timing->vic; + addr += 1; + num_timings++; + } + break; + default: + continue; + } + } + while(mode == STDT && stdcnt < 8){ + addr[0] = 0x01; + addr[1] = 0x01; + addr += 2; + stdcnt++; + } + + return num_timings; +} + +static void +vdpy_edid_set_dtd(uint8_t *dtd, const frame_param *frame) +{ + uint16_t pixel_clk; + + if ((frame->pixel_clock / 10000) > 65535) { + /* + * Large screen. The pixel_clock won't fit in two bytes. + * We fill in a dummy DTD here and OS will pick up PTM + * from extension block. + */ + dtd[3] = 0x10; /* Tag 0x10: Dummy descriptor */ + return; + } + + // Range: 10 kHz to 655.35 MHz in 10 kHz steps + pixel_clk = frame->pixel_clock / 10000; + memcpy(dtd, &pixel_clk, sizeof(pixel_clk)); + dtd[2] = frame->hav_pixel & 0xff; + dtd[3] = frame->hb_pixel & 0xff; + dtd[4] = ((frame->hav_pixel & 0xf00) >> 4) | + ((frame->hb_pixel & 0xf00) >> 8); + dtd[5] = frame->vav_line & 0xff; + dtd[6] = frame->vb_line & 0xff; + dtd[7] = ((frame->vav_line & 0xf00) >> 4) | + ((frame->vb_line & 0xf00) >> 8); + dtd[8] = frame->hfp_pixel & 0xff; + dtd[9] = frame->hsp_pixel & 0xff; + dtd[10] = ((frame->vfp_line & 0xf) << 4) | + (frame->vsp_line & 0xf); + dtd[11] = ((frame->hfp_pixel & 0x300) >> 2) | + ((frame->hsp_pixel & 0x300) >> 4) | + ((frame->vfp_line & 0x030) >> 6) | + ((frame->vsp_line & 0x030) >> 8); + dtd[12] = frame->width & 0xff; + dtd[13] = frame->height & 0xff; + dtd[14] = ((frame->width & 0xf00) >> 4) | + ((frame->height & 0xf00) >> 8); + dtd[15] = frame->lhb_pixel & 0xff; + dtd[16] = frame->tvb_line & 0xff; + dtd[17] = 0x18; +} + +static void +vdpy_edid_set_descripter(uint8_t *desc, uint8_t is_dtd, + uint8_t tag, const base_param *b_param) +{ + frame_param frame; + const char* text; + uint16_t len; + + + if (is_dtd) { + vdpy_edid_set_frame(&frame, b_param); + vdpy_edid_set_dtd(desc, &frame); + return; + } + desc[3] = tag; + text = NULL; + switch(tag){ + // Established Timings III Descriptor (tag #F7h) + case 0xf7: + desc[5] = 0x0a; // Revision Number + vdpy_edid_set_timing(desc, ESTT3); + break; + // Display Range Limits & Additional Timing Descriptor (tag #FDh) + case 0xfd: + desc[5] = 50; // Minimum Vertical Rate. (50 -> 125 Hz) + desc[6] = 125; // Maximum Vertical Rate. + desc[7] = 30; // Minimum Horizontal Rate.(30 -> 160 kHz) + desc[8] = 160; // Maximum Horizontal Rate. + desc[9] = 2550 / 10; // Max Pixel Clock. (2550 MHz) + desc[10] = 0x01; // no extended timing information + desc[11] = '\n'; // padding + break; + // Display Product Name (ASCII) String Descriptor (tag #FCh) + case 0xfc: + // Display Product Serial Number Descriptor (tag #FFh) + case 0xff: + text = (tag == 0xff) ? b_param->sn : b_param->product_name; + memset(desc + 5, ' ', 13); + if (text == NULL) + break; + len = strlen(text); + if (len > 12) + len = 12; + memcpy(desc + 5, text, len); + desc[len + 5] = '\n'; + break; + // Dummy Descriptor (Tag #10h) + case 0x10: + default: + break; + } +} + +static uint8_t +vdpy_edid_get_checksum(uint8_t *edid) +{ + uint8_t sum; + int i; + + sum = 0; + for (i = 0; i < 127; i++) { + sum += edid[i]; + } + + return 0x100 - sum; +} + +static void +vdpy_edid_generate(uint8_t *edid, size_t size, struct edid_info *info) +{ + uint16_t id_manuf; + uint16_t id_product; + uint32_t serial; + uint8_t *desc; + base_param b_param; + uint8_t num_cea_timings; + + vdpy_edid_set_baseparam(&b_param, info->prefx, info->prefy); + + memset(edid, 0, size); + /* edid[7:0], fixed header information, (00 FF FF FF FF FF FF 00)h */ + memset(edid + 1, 0xff, 6); + + /* edid[17:8], Vendor & Product Identification */ + // Manufacturer ID is a big-endian 16-bit value. + id_manuf = ((((b_param.id_manuf[0] - '@') & 0x1f) << 10) | + (((b_param.id_manuf[1] - '@') & 0x1f) << 5) | + (((b_param.id_manuf[2] - '@') & 0x1f) << 0)); + edid[8] = id_manuf >> 8; + edid[9] = id_manuf & 0xff; + + // Manufacturer product code is a little-endian 16-bit number. + id_product = b_param.id_product; + memcpy(edid+10, &id_product, sizeof(id_product)); + + // Serial number is a little-endian 32-bit value. + serial = b_param.id_sn; + memcpy(edid+12, &serial, sizeof(serial)); + + edid[16] = 0; // Week of Manufacture + edid[17] = 2018 - 1990; // Year of Manufacture or Model Year. + // Acrn is released in 2018. + + edid[18] = 1; // Version Number + edid[19] = 4; // Revision Number + + /* edid[24:20], Basic Display Parameters & Features */ + // Video Input Definition: 1 Byte + edid[20] = 0xa5; // Digital input; + // 8 Bits per Primary Color; + // DisplayPort is supported + + // Horizontal and Vertical Screen Size or Aspect Ratio: 2 Bytes + // screen size, in centimetres + edid[21] = info->prefx / 10; + edid[22] = info->prefy / 10; + + // Display Transfer Characteristics (GAMMA): 1 Byte + // Stored Value = (GAMMA x 100) - 100 + edid[23] = 120; // display gamma: 2.2 + + // Feature Support: 1 Byte + edid[24] = 0x06; // sRGB Standard is the default color space; + // Preferred Timing Mode includes the native + // pixel format and preferred. + + /* edid[34:25], Display x, y Chromaticity Coordinates */ + vdpy_edid_set_color(edid, 0.6400, 0.3300, + 0.3000, 0.6000, + 0.1500, 0.0600, + 0.3127, 0.3290); + + /* edid[37:35], Established Timings */ + vdpy_edid_set_timing(edid, ESTT); + + /* edid[53:38], Standard Timings: Identification 1 -> 8 */ + vdpy_edid_set_timing(edid, STDT); + + /* edid[125:54], Detailed Timing Descriptor - 18 bytes x 4 */ + // Preferred Timing Mode + desc = edid + 54; + vdpy_edid_set_descripter(desc, 0x1, 0, &b_param); + // Display Range Limits & Additional Timing Descriptor (tag #FDh) + desc += 18; + vdpy_edid_set_descripter(desc, 0, 0xfd, &b_param); + // Display Product Name (ASCII) String Descriptor (tag #FCh) + desc += 18; + vdpy_edid_set_descripter(desc, 0, 0xfc, &b_param); + // Display Product Serial Number Descriptor (tag #FFh) + desc += 18; + vdpy_edid_set_descripter(desc, 0, 0xff, &b_param); + + /* EDID[126], Extension Block Count */ + edid[126] = 0; // no Extension Block + + /* Checksum */ + edid[127] = vdpy_edid_get_checksum(edid); + + if (size >= (EDID_BASIC_BLOCK_SIZE + EDID_CEA861_EXT_BLOCK_SIZE)) { + edid[126] = 1; + edid[127] = vdpy_edid_get_checksum(edid); + + // CEA EDID Extension + edid[EDID_BASIC_BLOCK_SIZE + 0] = 0x02; + // Revision Number + edid[EDID_BASIC_BLOCK_SIZE + 1] = 0x03; + // SVDs + edid[EDID_BASIC_BLOCK_SIZE + 4] |= 0x02 << 5; + desc = edid + EDID_BASIC_BLOCK_SIZE + 5; + num_cea_timings = vdpy_edid_set_timing(desc, CEA861); + edid[EDID_BASIC_BLOCK_SIZE + 4] |= num_cea_timings; + edid[EDID_BASIC_BLOCK_SIZE + 2] |= 5 + num_cea_timings; + + desc = edid + EDID_BASIC_BLOCK_SIZE; + edid[EDID_BASIC_BLOCK_SIZE + 127] = vdpy_edid_get_checksum(desc); + } +} + +void +vdpy_get_edid(int handle, int scanout_id, uint8_t *edid, size_t size) +{ + struct edid_info edid_info; + struct vscreen *vscr; + + if (scanout_id >= vdpy.vscrs_num) + return; + + vscr = vdpy.vscrs + scanout_id; + + if (handle == vdpy.s.n_connect) { + edid_info.prefx = vscr->info.width; + edid_info.prefy = vscr->info.height; + edid_info.maxx = VDPY_MAX_WIDTH; + edid_info.maxy = VDPY_MAX_HEIGHT; + } else { + edid_info.prefx = VDPY_DEFAULT_WIDTH; + edid_info.prefy = VDPY_DEFAULT_HEIGHT; + edid_info.maxx = VDPY_MAX_WIDTH; + edid_info.maxy = VDPY_MAX_HEIGHT; + } + edid_info.refresh_rate = 0; + edid_info.vendor = NULL; + edid_info.name = NULL; + edid_info.sn = NULL; + + vdpy_edid_generate(edid, size, &edid_info); +} + +void +vdpy_get_display_info(int handle, int scanout_id, struct display_info *info) +{ + struct vscreen *vscr; + + if (scanout_id >= vdpy.vscrs_num) + return; + + vscr = vdpy.vscrs + scanout_id; + + if (handle == vdpy.s.n_connect) { + info->xoff = vscr->info.xoff; + info->yoff = vscr->info.yoff; + info->width = vscr->info.width; + info->height = vscr->info.height; + } else { + info->xoff = 0; + info->yoff = 0; + info->width = 0; + info->height = 0; + } +} + +static void +vdpy_sdl_ui_refresh(void *data __attribute__((unused))) +{ + #if 0 + struct display *ui_vdpy; + struct timespec cur_time; + uint64_t elapsed_time; + SDL_Rect cursor_rect; + struct vscreen *vscr; + int i; + + ui_vdpy = (struct display *)data; + + for (i = 0; i < vdpy.vscrs_num; i++) { + // vscr = ui_vdpy->vscrs + i; + + // /* Skip it if no surface needs to be rendered */ + // if (vscr->surf_tex == NULL) + // continue; + + // clock_gettime(CLOCK_MONOTONIC, &cur_time); + + // elapsed_time = (cur_time.tv_sec - vscr->last_time.tv_sec) * 1000000000 + + // cur_time.tv_nsec - vscr->last_time.tv_nsec; + + // /* the time interval is less than 10ms. Skip it */ + // if (elapsed_time < 10000000) + // return; + + // sdl_gl_prepare_draw(vscr); + // SDL_RenderCopy(vscr->renderer, vscr->surf_tex, NULL, NULL); + + // /* This should be handled after rendering the surface_texture. + // * Otherwise it will be hidden + // */ + // if (vscr->cur_tex) { + // vdpy_cursor_position_transformation(ui_vdpy, i, &cursor_rect); + // SDL_RenderCopy(vscr->renderer, vscr->cur_tex, + // NULL, &cursor_rect); + // } + + // SDL_RenderPresent(vscr->renderer); + } + #endif +} + +static void +vdpy_sdl_ui_timer(void *data, uint64_t nexp __attribute__((unused))) +{ + struct display *ui_vdpy; + struct vdpy_display_bh *bh_task; + + ui_vdpy = (struct display *)data; + + /* Don't submit the display_request if another func already + * acquires the mutex. + * This is to optimize the mevent thread otherwise it needs + * to wait for some time. + */ + if (pthread_mutex_trylock(&ui_vdpy->vdisplay_mutex)) + return; + + bh_task = &ui_vdpy->ui_timer_bh; + if ((bh_task->bh_flag & ACRN_BH_PENDING) == 0) { + bh_task->bh_flag |= ACRN_BH_PENDING; + TAILQ_INSERT_TAIL(&ui_vdpy->request_list, bh_task, link); + } + pthread_cond_signal(&ui_vdpy->vdisplay_signal); + pthread_mutex_unlock(&ui_vdpy->vdisplay_mutex); +} + +void +vdpy_calibrate_vscreen_geometry(struct vscreen *vscr) +{ + if (vscr->guest_width && vscr->guest_height) { + /* clip the region between (640x480) and (1920x1080) */ + if (vscr->guest_width < VDPY_MIN_WIDTH) + vscr->guest_width = VDPY_MIN_WIDTH; + if (vscr->guest_width > VDPY_MAX_WIDTH) + vscr->guest_width = VDPY_MAX_WIDTH; + if (vscr->guest_height < VDPY_MIN_HEIGHT) + vscr->guest_height = VDPY_MIN_HEIGHT; + if (vscr->guest_height > VDPY_MAX_HEIGHT) + vscr->guest_height = VDPY_MAX_HEIGHT; + } else { + /* the default window(1280x720) is created with undefined pos + * when no geometry info is passed + */ + vscr->org_x = 0xFFFF; + vscr->org_y = 0xFFFF; + vscr->guest_width = VDPY_DEFAULT_WIDTH; + vscr->guest_height = VDPY_DEFAULT_HEIGHT; + } +} + +int +vdpy_create_vscreen_window(struct vscreen *vscr __attribute__((unused))) +{ + return 0; +} + +static void +sdl_gl_display_init(void) +{ + return; +} + +static void * +vdpy_sdl_display_thread(void *data __attribute__((unused))) +{ + // static bool is_egl_current = false; + struct vdpy_display_bh *bh; + struct itimerspec ui_timer_spec; + + struct vscreen *vscr; + int i; + + // if (!is_egl_current) { + // pr_info("vdpy_sdl_display_proc() eglMakeCurrent\n"); + // eglMakeCurrent(vdpy.eglDisplay, vdpy.eglSurface, vdpy.eglSurface, vdpy.eglContext); + // checkEglError("eglMakeCurrent"); + // is_egl_current = true; + // } + + for (i = 0; i < vdpy.vscrs_num; i++) { + vscr = vdpy.vscrs + i; + + vdpy_calibrate_vscreen_geometry(vscr); + + if (vdpy_create_vscreen_window(vscr)) { + goto sdl_fail; + } + + vscr->info.xoff = vscr->org_x; + vscr->info.yoff = vscr->org_y; + vscr->info.width = vscr->guest_width; + vscr->info.height = vscr->guest_height; + + clock_gettime(CLOCK_MONOTONIC, &vscr->last_time); + } + sdl_gl_display_init(); + pthread_mutex_init(&vdpy.vdisplay_mutex, NULL); + pthread_cond_init(&vdpy.vdisplay_signal, NULL); + TAILQ_INIT(&vdpy.request_list); + vdpy.s.is_active = 1; + + vdpy.ui_timer_bh.task_cb = vdpy_sdl_ui_refresh; + vdpy.ui_timer_bh.data = &vdpy; + vdpy.ui_timer.clockid = CLOCK_MONOTONIC; + acrn_timer_init(&vdpy.ui_timer, vdpy_sdl_ui_timer, &vdpy); + ui_timer_spec.it_interval.tv_sec = 0; + ui_timer_spec.it_interval.tv_nsec = 33000000; + /* Wait for 5s to start the timer */ + ui_timer_spec.it_value.tv_sec = 5; + ui_timer_spec.it_value.tv_nsec = 0; + /* Start one periodic timer to refresh UI based on 30fps */ + acrn_timer_settime(&vdpy.ui_timer, &ui_timer_spec); + + pr_info("SDL display thread is created\n"); + /* Begin to process the display_cmd after initialization */ + do { + if (!vdpy.s.is_active) { + pr_info("display is exiting\n"); + break; + } + pthread_mutex_lock(&vdpy.vdisplay_mutex); + + if (TAILQ_EMPTY(&vdpy.request_list)) + pthread_cond_wait(&vdpy.vdisplay_signal, + &vdpy.vdisplay_mutex); + + /* the bh_task is handled in vdisplay_mutex lock */ + while (!TAILQ_EMPTY(&vdpy.request_list)) { + bh = TAILQ_FIRST(&vdpy.request_list); + + TAILQ_REMOVE(&vdpy.request_list, bh, link); + + bh->task_cb(bh->data); + + if (atomic_load(&bh->bh_flag) & ACRN_BH_FREE) { + free(bh); + bh = NULL; + } else { + /* free is owned by the submitter */ + atomic_store(&bh->bh_flag, ACRN_BH_DONE); + } + } + + pthread_mutex_unlock(&vdpy.vdisplay_mutex); + } while (1); + + acrn_timer_deinit(&vdpy.ui_timer); + /* SDL display_thread will exit because of DM request */ + pthread_mutex_destroy(&vdpy.vdisplay_mutex); + pthread_cond_destroy(&vdpy.vdisplay_signal); + + // for (i = 0; i < vdpy.vscrs_num; i++) { + // vscr = vdpy.vscrs + i; + // if (vscr->img) { + // pixman_image_unref(vscr->img); + // vscr->img = NULL; + // } + /* Continue to thread cleanup */ + // if (vscr->surf_tex) { + // // SDL_DestroyTexture(vscr->surf_tex); + // glDeleteTextures(1, &vscr->surf_tex); + // checkGlError2("glDeleteTextures", vscr->surf_tex); + // vscr->surf_tex = 0; + // } + // if (vscr->cur_tex) { + // // SDL_DestroyTexture(vscr->cur_tex); + // glDeleteTextures(1, &vscr->cur_tex); + // checkGlError2("glDeleteTextures", vscr->cur_tex); + // vscr->cur_tex = 0; + // } + + // if (vdpy.egl_dmabuf_supported && (vscr->egl_img != EGL_NO_IMAGE_KHR)) + // vdpy.gl_ops.eglDestroyImageKHR(vdpy.eglDisplay, + // vscr->egl_img); + // } + +sdl_fail: + for (i = 0; i < vdpy.vscrs_num; i++) { + vscr = vdpy.vscrs + i; + // if (vscr->bogus_tex) { + // // SDL_DestroyTexture(vscr->bogus_tex); + // glDeleteTextures(1, &vscr->bogus_tex); + // checkGlError2("glDeleteTextures", vscr->bogus_tex); + // vscr->bogus_tex = 0; + // } + // if (vscr->renderer) { + // SDL_DestroyRenderer(vscr->renderer); + // vscr->renderer = NULL; + // } + // if (vscr->win) { + // SDL_DestroyWindow(vscr->win); + // vscr->win = NULL; + // } + } + + /* This is used to workaround the TLS issue of libEGL + libGLdispatch + * after unloading library. + */ + // eglReleaseThread(); + return NULL; +} + + +#define SERVER_SOCK_PATH "/data/virt_disp_server" +static int client_sock = -1; +static inline int client_send(int e_type, void *data, int len) +{ + int ret; + struct dpy_evt_header evt_hdr; + + if (client_sock != -1) { + evt_hdr.e_type = e_type; + evt_hdr.e_magic = DISPLAY_MAGIC_CODE; + evt_hdr.e_size = len; + ret = send(client_sock, &evt_hdr, sizeof(evt_hdr), 0); + if (ret != len) { + pr_err("%s() send header fail(%d vs. %d)", ret, sizeof(evt_hdr)); + return -1; + } + + ret = send(client_sock, data, len, 0); + if (ret != len) { + pr_err("%s() send body fail(%d vs. %d)", ret, len); + return -1; + } + } + return 0; +} + +static void * +vdpy_display_server_thread(void *data __attribute__((unused))) +{ + struct vscreen *vscr = vdpy.vscrs; + + int ret; + struct sockaddr_un server_sockaddr; + struct sockaddr_un client_sockaddr; + char buf[256]; + int server_sock = -1, new_client_sock; + socklen_t len; + client_sock = -1; + + int epollfd; + struct epoll_event event, events[10]; + + struct dpy_evt_header msg_header; + + memset(&server_sockaddr, 0, sizeof(struct sockaddr_un)); + server_sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (server_sock == -1){ + pr_err("SOCKET ERROR: %d\n", errno); + return NULL; + } + + server_sockaddr.sun_family = AF_UNIX; + strcpy(server_sockaddr.sun_path, SERVER_SOCK_PATH); + len = sizeof(server_sockaddr); + + unlink(SERVER_SOCK_PATH); + ret = bind(server_sock, (struct sockaddr *) &server_sockaddr, len); + if (ret == -1){ + pr_err("BIND ERROR: %d\n", errno); + goto close_sockets; + } + + ret = listen(server_sock, 10); + if (ret == -1){ + pr_err("LISTEN ERROR: %d\n", errno); + goto close_sockets; + } + + epollfd = epoll_create1(0); + if (epollfd == -1) { + pr_err ("epoll_create1"); + goto close_sockets; + } + + event.events = EPOLLIN; + event.data.fd = server_sock; + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, server_sock, &event) == -1) { + pr_err ("EPOLL_CTL_ADD server %d fail", server_sock); + goto close_epoll_fd; + } + + while (1) { + int numEvents = epoll_wait(epollfd, events, 5, -1); + if (numEvents == -1) { + perror ("epoll_wait"); + goto close_epoll_fd; + } + + for (int i = 0; i < numEvents; i++) { + pthread_mutex_lock(&vdpy.client_mutex); + + if (events[i].data.fd == server_sock) { + // Accept incoming connection + len = sizeof (client_sockaddr); + new_client_sock = accept (server_sock, (struct sockaddr*)&client_sockaddr, &len); + pr_err("Client connected!\n"); + + // Close previous client connect, and remove listener + if (client_sock != -1) { + event.events = EPOLLIN | EPOLLERR | EPOLLHUP | EPOLLRDHUP; + event.data.fd = client_sock; + if (epoll_ctl(epollfd, EPOLL_CTL_DEL, client_sock, &event) == -1) { + pr_err("EPOLL_CTL_DEL client %d fail!", client_sock); + } + close(client_sock); + } + + client_sock = new_client_sock; + if (vscr->set_modifier) + client_send(DPY_EVENT_SET_MODIFIER, &vscr->modifier, sizeof(vscr->modifier)); + + // Add new listener + event.events = EPOLLIN | EPOLLERR | EPOLLHUP | EPOLLRDHUP; + event.data.fd = client_sock; + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, client_sock, &event) == -1) { + pr_err("EPOLL_CTL_ADD client %d fail!", client_sock); + } + } else if (events[i].data.fd == client_sock) { + if (!(events[i].events & EPOLLIN)) { + pr_err("poll client error: 0x%x", events[i].events); + continue; + } + + while ((ret = recv(client_sock, &msg_header, sizeof(msg_header), 0) ) > 0) { + if (ret != sizeof(msg_header)) { + pr_err("recv event header fail (%d vs. %d)!", ret, sizeof(msg_header)); + continue; + } + + if (msg_header.e_magic != DISPLAY_MAGIC_CODE) { + // data error, clear receive buffer + pr_err("recv data err!"); + while (recv(client_sock, &buf, 256, 0) > 0); + break; + } + + ret = recv(client_sock, &buf, msg_header.e_size, 0); + if (ret != msg_header.e_size) + pr_err("recv event body fail (%d vs. %d) !", ret, msg_header.e_size); + + switch (msg_header.e_type) { + case DPY_EVENT_DISPLAY_INFO: + { + struct display_info *info = (struct display_info *)buf; + vscr->info.xoff = info->xoff; + vscr->info.yoff = info->yoff; + vscr->info.width = info->width; + vscr->info.height = info->height; + break; + } + default: + break; + } + } + } + pthread_mutex_unlock(&vdpy.client_mutex); + } + } + +close_epoll_fd: + if (epollfd != -1) { + close(epollfd); + } + +close_sockets: + if (server_sock != -1) + close(server_sock); + if (client_sock != -1) + close(client_sock); + return NULL; +} + +int +vdpy_init(int *num_vscreens) +{ + int err, count; + + if (vdpy.s.n_connect) { + return 0; + } + + /* start one vdpy_sdl_display_thread to handle the 3D request + * in this dedicated thread. Otherwise the libSDL + 3D doesn't + * work. + */ + err = pthread_create(&vdpy.tid, NULL, vdpy_sdl_display_thread, &vdpy); + if (err) { + pr_err("Failed to create the sdl_display_thread.\n"); + return 0; + } + pthread_setname_np(vdpy.tid, "acrn_vdisplay"); + + pthread_mutex_init(&vdpy.client_mutex, NULL); + err = pthread_create(&vdpy.server_tid, NULL, vdpy_display_server_thread, &vdpy); + if (err) { + pr_err("Failed to create the sdl_display_thread.\n"); + return 0; + } + pthread_setname_np(vdpy.server_tid, "acrn_dpy_server"); + + count = 0; + /* Wait up to 200ms so that the vdpy_sdl_display_thread is ready to + * handle the 3D request + */ + while (!vdpy.s.is_active && count < 20) { + usleep(10000); + count++; + } + if (!vdpy.s.is_active) { + pr_err("display_thread is not ready.\n"); + } + + vdpy.s.n_connect++; + if (num_vscreens) + *num_vscreens = vdpy.vscrs_num; + return vdpy.s.n_connect; +} + +void vdpy_surface_set(int handle __attribute__((unused)), int scanout_id __attribute__((unused)), struct surface *surf) +{ + if (!surf || (surf->surf_type != SURFACE_DMABUF)) { + pr_err("%s Only dma buf is supported!", __func__); + return; + } + + pthread_mutex_lock(&vdpy.client_mutex); + client_send(DPY_EVENT_SURFACE_SET, surf, sizeof(struct surface)); + pthread_mutex_unlock(&vdpy.client_mutex); +} + +void vdpy_surface_update(int handle __attribute__((unused)), int scanout_id __attribute__((unused)), struct surface *surf __attribute__((unused))) +{ + // all action done in vdpy_surface_set(), check whether need this func later + return; +} + +void +vdpy_set_modifier(int handle __attribute__((unused)), int scanout_id, uint64_t modifier) +{ + struct vscreen *vscr; + + if (scanout_id >= vdpy.vscrs_num) { + return; + } + + vscr = vdpy.vscrs + scanout_id; + vscr->modifier = modifier; + vscr->set_modifier = true; + + pthread_mutex_lock(&vdpy.client_mutex); + client_send(DPY_EVENT_SET_MODIFIER, &modifier, sizeof(modifier)); + pthread_mutex_unlock(&vdpy.client_mutex); +} + +bool vdpy_submit_bh(int handle, struct vdpy_display_bh *bh_task) +{ + bool bh_ok = false; + + if (handle != vdpy.s.n_connect) { + pr_info("%s handle != vdpy.s.n_connect\n", __func__); + return bh_ok; + } + + if (!vdpy.s.is_active) { + pr_info("%s !vdpy.s.is_active\n", __func__); + return bh_ok; + } + + pthread_mutex_lock(&vdpy.vdisplay_mutex); + + if ((bh_task->bh_flag & ACRN_BH_PENDING) == 0) { + bh_task->bh_flag |= ACRN_BH_PENDING; + TAILQ_INSERT_TAIL(&vdpy.request_list, bh_task, link); + bh_ok = true; + } + pthread_cond_signal(&vdpy.vdisplay_signal); + pthread_mutex_unlock(&vdpy.vdisplay_mutex); + + return bh_ok; +} + +void vdpy_cursor_define(int handle __attribute__((unused)), int scanout_id __attribute__((unused)), struct cursor *cur __attribute__((unused))) +{ + return; +} + +void vdpy_cursor_move(int handle __attribute__((unused)), int scanout_id __attribute__((unused)), uint32_t x __attribute__((unused)), uint32_t y __attribute__((unused))) +{ + return; +} + +int vdpy_deinit(int handle __attribute__((unused))) +{return 0;} + +int +gfx_ui_init() +{return 0;} +void +gfx_ui_deinit() +{} + +int vdpy_parse_cmd_option(const char *opts) +{ + char *str, *stropts, *tmp; + int snum, error; + struct vscreen *vscr; + + error = 0; + vdpy.vscrs = calloc(VSCREEN_MAX_NUM, sizeof(struct vscreen)); + vdpy.vscrs_num = 0; + + stropts = strdup(opts); + while ((str = strsep(&stropts, ",")) != NULL) { + vscr = vdpy.vscrs + vdpy.vscrs_num; + tmp = strcasestr(str, "geometry="); + if (str && strcasestr(str, "geometry=fullscreen")) { + snum = sscanf(tmp, "geometry=fullscreen:%d", &vscr->pscreen_id); + if (snum != 1) { + vscr->pscreen_id = 0; + } + vscr->org_x = 0; + vscr->org_y = 0; + vscr->guest_width = VDPY_MAX_WIDTH; + vscr->guest_height = VDPY_MAX_HEIGHT; + vscr->is_fullscreen = true; + pr_info("virtual display: fullscreen on monitor %d.\n", + vscr->pscreen_id); + vdpy.vscrs_num++; + } else if (str && strcasestr(str, "geometry=")) { + snum = sscanf(tmp, "geometry=%dx%d+%d+%d", + &vscr->guest_width, &vscr->guest_height, + &vscr->org_x, &vscr->org_y); + if (snum != 4) { + pr_err("incorrect geometry option. Should be" + " WxH+x+y\n"); + error = -1; + } + vscr->is_fullscreen = false; + vscr->pscreen_id = 0; + pr_info("virtual display: windowed on monitor %d.\n", + vscr->pscreen_id); + vdpy.vscrs_num++; + } + + if (vdpy.vscrs_num > VSCREEN_MAX_NUM) { + pr_err("%d virtual displays are too many that acrn-dm can't support!\n"); + break; + } + } + free(stropts); + + return error; +} diff --git a/devicemodel/include/vdisplay.h b/devicemodel/include/vdisplay.h index 8a83914..def8541 100644 --- a/devicemodel/include/vdisplay.h +++ b/devicemodel/include/vdisplay.h @@ -89,19 +89,17 @@ struct cursor { int vdpy_parse_cmd_option(const char *opts); int gfx_ui_init(); int vdpy_init(int *num_vscreens); +int vdpy_deinit(int handle); +void gfx_ui_deinit(); + void vdpy_get_display_info(int handle, int scanout_id, struct display_info *info); +void vdpy_set_modifier(int handle, int scanout_id, uint64_t modifier); void vdpy_surface_set(int handle, int scanout_id, struct surface *surf); void vdpy_surface_update(int handle, int scanout_id, struct surface *surf); -void vdpy_set_modifier(int handle, int scanout_id, uint64_t modifier); -bool vdpy_submit_bh(int handle, struct vdpy_display_bh *bh); -void vdpy_get_edid(int handle, int scanout_id, uint8_t *edid, size_t size); void vdpy_cursor_define(int handle, int scanout_id, struct cursor *cur); void vdpy_cursor_move(int handle, int scanout_id, uint32_t x, uint32_t y); -int vdpy_deinit(int handle); -void gfx_ui_deinit(); -int vdpy_gfx_ui_init(void *data); -void vdpy_gfx_ui_deinit(); -void *vdpy_sdl_display_proc(bool termed); +bool vdpy_submit_bh(int handle, struct vdpy_display_bh *bh); +void vdpy_get_edid(int handle, int scanout_id, uint8_t *edid, size_t size); #endif /* _VDISPLAY_H_ */ diff --git a/utils.c b/utils.c index 2692785..6899362 100644 --- a/utils.c +++ b/utils.c @@ -143,7 +143,7 @@ void set_shmem_args(struct virtio_backend_info *info) info->shmem_ops->name, info->shmem_devpath, info->opts); } -void *run_backend(void *data) +void *run_backend(void *data, int argc __attribute__((unused)), char *argv[] __attribute__((unused))) { int ret; struct virtio_backend_info *info = (struct virtio_backend_info *)data; @@ -171,24 +171,6 @@ void *run_backend(void *data) return NULL; } -int create_backend_thread(struct virtio_backend_info *info) -{ - pr_info ("start create_backend_thread.\n"); - if (pthread_create(&info->tid, NULL, run_backend, (void *)info)) { - pr_err("Failed to create the create_backend_thread.\n"); - return -1; - } - pr_info ("start create_backend_thread -1.\n"); - pthread_setname_np(info->tid, "acrn_gpu_backend"); - pr_info ("start create_backend_thread -2.\n"); - return 0; -} - -void close_backend_thread() -{ - //vm_set_suspend_mode(VM_SUSPEND_NONE); -} - void dump_hex(void *base, int size) { int i; diff --git a/utils.h b/utils.h index ec1c822..ffb0d52 100644 --- a/utils.h +++ b/utils.h @@ -8,15 +8,13 @@ #define min(a, b) (((a) < (b)) ? (a) : (b)) -// #define ACRN_BACKEND_MAIN(info) \ -// int main(int argc, char *argv[]) { \ -// run_backend(&info, argc, argv); \ -// return 0; \ -// } +#define ACRN_BACKEND_MAIN(info) \ +int main(int argc, char *argv[]) { \ + run_backend(&info, argc, argv); \ + return 0; \ +} -void *run_backend(void *info); -int create_backend_thread(struct virtio_backend_info *info); -void close_backend_thread(); +void *run_backend(void *info, int argc, char *argv[]); void dump_hex(void *base, int size); void dump_desc(volatile struct vring_desc *desc, int idx, bool cond); diff --git a/vdisplay_protocol.h b/vdisplay_protocol.h new file mode 100644 index 0000000..28e6bc2 --- /dev/null +++ b/vdisplay_protocol.h @@ -0,0 +1,22 @@ +#ifndef __VDISPLAY_PROTOCOL_H__ +#define __VDISPLAY_PROTOCOL_H__ + +enum dpy_evt_type { + DPY_EVENT_SURFACE_SET =0x100, + DPY_EVENT_SURFACE_UPDATE, + DPY_EVENT_SET_MODIFIER, + DPY_EVENT_CURSOR_DEFINE, + DPY_EVENT_CURSOR_MOVE, + DPY_EVENT_DISPLAY_INFO +}; + +#define DISPLAY_MAGIC_CODE 0x5566 + + +struct dpy_evt_header { + int e_type; + int e_magic; + int e_size; +}; + +#endif /* __VDISPLAY_PROTOCOL_H__ */