diff --git a/server.c b/server.c index 4bbe03a..a282c53 100644 --- a/server.c +++ b/server.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -55,28 +56,91 @@ static void convert_bgrx_to_rgb(const char* in, int width, int height, char* buf } } -#define XSTRIPE 16 -#define YSTRIPE 128 -char *nv_stripe_buf = NULL; -static void convert_nv_stripe(const char* in, int width, int height, char* buff) { +char *kms_convert_buf = NULL; +size_t kms_convert_buf_len = 0; +static inline void convert_kmsbuf(const int XSTRIPE, const int YSTRIPE, const char* in, int width, int height, char* buff) { if (width % XSTRIPE) { return; } - if (!nv_stripe_buf) nv_stripe_buf = malloc(width * height * 4); - convert_bgrx_to_rgb(in, width, height, nv_stripe_buf); - for (int i = 0; i < height*width; i++) - { - int sno = i / (XSTRIPE * YSTRIPE); - int ord = i % (XSTRIPE * YSTRIPE); - int base_x = sno % (width / XSTRIPE) * XSTRIPE; - int base_y = sno / (width / XSTRIPE) * YSTRIPE; - int sx = ord % XSTRIPE + base_x; - int sy = ord / XSTRIPE + base_y; - int offset = sy * width + sx; - if (offset < height*width) { - memcpy(buff + offset*4, nv_stripe_buf + i*4, 4); + if (kms_convert_buf_len < width * height * 4) { + if (kms_convert_buf) free(kms_convert_buf); + kms_convert_buf = malloc(width * height * 4); + kms_convert_buf_len = width * height * 4; + } + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + { + int sno = (x / XSTRIPE) + (y / YSTRIPE) * (width / XSTRIPE); + int ord = (x % XSTRIPE) + (y % YSTRIPE) * XSTRIPE; + int offset = sno * XSTRIPE * YSTRIPE + ord; + if (offset < height*width) { + memcpy(kms_convert_buf + (x+y*width)*4, in + offset*4, 4); + } } } + convert_bgrx_to_rgb(kms_convert_buf, width, height, buff); +} + +static size_t get_extra_mmap_bytes(const int XSTRIPE, const int YSTRIPE, int width, int height) { + if (height % YSTRIPE) { + int offset_max = 0; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + { + int sno = (x / XSTRIPE) + (y / YSTRIPE) * (width / XSTRIPE); + int ord = (x % XSTRIPE) + (y % YSTRIPE) * XSTRIPE; + int offset = sno * XSTRIPE * YSTRIPE + ord; + if (offset > offset_max) { + offset_max = offset; + } + } + } + return offset_max * 4 - height*width * 4; + } + else { + return 0; + } +} +#define XSTRIPE_INTEL 128 +#define YSTRIPE_INTEL 8 +#define XSTRIPE_NVIDIA 16 +#define YSTRIPE_NVIDIA 128 +#define XSTRIPE_INTEL 16 +#define YSTRIPE_INTEL 128 +static size_t get_nvidia_extra_mmap_bytes(int width, int height) { + return get_extra_mmap_bytes(XSTRIPE_NVIDIA, YSTRIPE_NVIDIA, width, height); +} +static size_t get_intel_extra_mmap_bytes(int width, int height) { + return get_extra_mmap_bytes(XSTRIPE_NVIDIA, YSTRIPE_NVIDIA, width, height); +} +static void convert_nvidia_kmsbuf(const char* in, int width, int height, char* buff) { + convert_kmsbuf(XSTRIPE_INTEL, YSTRIPE_INTEL, in, width, height, buff); +} +static void convert_intel_kmsbuf(const char* in, int width, int height, char* buff) { + convert_kmsbuf(XSTRIPE_INTEL, YSTRIPE_INTEL, in, width, height, buff); +} + +struct vnc_drm_attr { + void (*sync_start)(int); + void (*sync_end)(int); + void (*convert)(const char*, int, int, char*); +}; + +static inline void drm_sync(int drmfd, uint64_t flags) { + int ioctl_err; + struct dma_buf_sync sync = { + .flags = flags, + }; + if (ioctl_err = ioctl(drmfd, DMA_BUF_IOCTL_SYNC, &sync)) + fprintf(stderr, "DRM ioctl error %d on line %d\n", ioctl_err, __LINE__); +} +static void drm_sync_start(int drmfd) { + drm_sync(drmfd, DMA_BUF_SYNC_START | DMA_BUF_SYNC_READ); +} +static void drm_sync_end(int drmfd) { + drm_sync(drmfd, DMA_BUF_SYNC_END | DMA_BUF_SYNC_READ); +} +static void drm_sync_noop(int drmfd) { } struct vnc_xkb { @@ -282,18 +346,6 @@ static void rfb_ptr_hook(int mask, int screen_x, int screen_y, rfbClientPtr cl) } -static void drm_wait_vblank_noop(int x) { -} -static void drm_wait_vblank(int drmfd) { - int ioctl_err; - union drm_wait_vblank vbl; - memset(&vbl, 0, sizeof(vbl)); - vbl.request.type = DRM_VBLANK_RELATIVE; - vbl.request.sequence = 1; - if (ioctl_err = drmIoctl(drmfd, DRM_IOCTL_WAIT_VBLANK, &vbl)) - fprintf(stderr, "DRM ioctl error %d on line %d\n", ioctl_err, __LINE__); -} - int main(int argc, const char **argv) { if (argc < 2) { @@ -305,6 +357,7 @@ int main(int argc, const char **argv) const char *card = argv[1]; const int drmfd = open(card, O_RDONLY); + int primefd = -1; if (drmfd < 0) { fprintf(stderr, "card %s open failed: %s\n", card, strerror(errno)); return 1; @@ -383,35 +436,42 @@ int main(int argc, const char **argv) } int ioctl_err = 0; - struct drm_gem_flink flink; - flink.handle = fb->handle; - if (ioctl_err = drmIoctl(drmfd, DRM_IOCTL_GEM_FLINK, &flink)) { - fprintf(stderr, "DRM ioctl error %d on line %d\n", ioctl_err, __LINE__); - goto cleanup; - } - struct drm_gem_open open_arg; - open_arg.name = flink.name; - if (ioctl_err = drmIoctl(drmfd, DRM_IOCTL_GEM_OPEN, &open_arg)) { - fprintf(stderr, "DRM ioctl error %d on line %d\n", ioctl_err, __LINE__); - goto cleanup; - } - off_t mmap_offset = 0; - size_t mmap_size = 0; + printf("drm driver is %s\n", drm_ver->name); char *mapped = NULL; - void (*convert_func)(const char*, int, int, char*) = &convert_bgrx_to_rgb; - void (*vblank_func)(int) = &drm_wait_vblank; + struct vnc_drm_attr funcs = { + .sync_start = &drm_sync_start, + .sync_end = &drm_sync_end, + .convert = &convert_bgrx_to_rgb, + }; + err = drmPrimeHandleToFD(drmfd, fb->handle, O_RDWR, &primefd); + if (err < 0 || primefd < 0) { + perror("Failed to get PRIME fd from framebuffer handle"); + goto cleanup; + } + + int mmap_fd = primefd; + size_t mmap_size = fb->width * fb->height * 32 / 8; + off_t mmap_offset = 0; + if (strcmp(drm_ver->name, "i915") == 0) { - struct drm_i915_gem_mmap_gtt mmap_arg; - mmap_arg.handle = open_arg.handle; - if (ioctl_err = drmIoctl(drmfd, DRM_IOCTL_I915_GEM_MMAP_GTT, &mmap_arg)) { + funcs.convert = &convert_intel_kmsbuf; + mmap_size += get_intel_extra_mmap_bytes(fb->width, fb->height); + } + else if (strcmp(drm_ver->name, "amdgpu") == 0) { + struct drm_gem_flink flink; + flink.handle = fb->handle; + if (ioctl_err = drmIoctl(drmfd, DRM_IOCTL_GEM_FLINK, &flink)) { fprintf(stderr, "DRM ioctl error %d on line %d\n", ioctl_err, __LINE__); goto cleanup; } - mmap_size = open_arg.size; - mmap_offset = mmap_arg.offset; - } - else if (strcmp(drm_ver->name, "amdgpu") == 0) { + struct drm_gem_open open_arg; + open_arg.name = flink.name; + printf("global name = %d\n", flink.name); + if (ioctl_err = drmIoctl(drmfd, DRM_IOCTL_GEM_OPEN, &open_arg)) { + fprintf(stderr, "DRM ioctl error %d on line %d\n", ioctl_err, __LINE__); + goto cleanup; + } union drm_amdgpu_gem_mmap mmap_arg; memset(&mmap_arg, 0, sizeof(mmap_arg)); mmap_arg.in.handle = open_arg.handle; @@ -422,33 +482,23 @@ int main(int argc, const char **argv) mmap_size = open_arg.size; mmap_offset = mmap_arg.out.addr_ptr; } - else { - if (strcmp(drm_ver->name, "nvidia-drm") == 0) { - // quirky and slow - convert_func = &convert_nv_stripe; - vblank_func = &drm_wait_vblank_noop; - } - else if (strcmp(drm_ver->name, "vmwgfx") == 0 || strcmp(drm_ver->name, "vboxvideo") == 0 || strcmp(drm_ver->name, "virtio_gpu") == 0) { - // virgl does not work - vblank_func = &drm_wait_vblank_noop; - } - else { - fprintf(stderr, "Untested drm driver, use at your own risk!\n"); - } - - struct drm_mode_map_dumb mreq; - memset(&mreq, 0, sizeof(mreq)); - mreq.handle = open_arg.handle; - if (ioctl_err = drmIoctl(drmfd, DRM_IOCTL_MODE_MAP_DUMB, &mreq)) { - fprintf(stderr, "DRM ioctl error %d on line %d\n", ioctl_err, __LINE__); - goto cleanup; - } - mmap_size = open_arg.size; - mmap_offset = mreq.offset; + else if (strcmp(drm_ver->name, "nvidia-drm") == 0) { + // quirky and slow + funcs.convert = &convert_nvidia_kmsbuf; + mmap_size += get_nvidia_extra_mmap_bytes(fb->width, fb->height); } + else if (strcmp(drm_ver->name, "vmwgfx") == 0 || strcmp(drm_ver->name, "vboxvideo") == 0 || strcmp(drm_ver->name, "virtio_gpu") == 0) { + // virgl does not work + //funcs.sync_start = &drm_sync_noop; + //funcs.sync_end = &drm_sync_noop; + } + else { + fprintf(stderr, "Untested drm driver, use at your own risk!\n"); + } + if (!mapped) { - printf("mapping with size = %d, offset = %d, drmfd = %d\n", mmap_size, mmap_offset, drmfd); - mapped = mmap(NULL, mmap_size, PROT_READ, MAP_SHARED, drmfd, mmap_offset); + printf("mapping with size = %d, offset = %d, fd = %d\n", mmap_size, mmap_offset, mmap_fd); + mapped = mmap(NULL, mmap_size, PROT_READ, MAP_SHARED, mmap_fd, mmap_offset); if (mapped == MAP_FAILED) { perror("mmap"); goto cleanup; @@ -458,8 +508,6 @@ int main(int argc, const char **argv) size_t buflen = fb->width * fb->height * 32 / 8; char *buf = malloc(buflen); memset(buf, 0, buflen); - char *buf2 = malloc(buflen); - memset(buf2, 0, buflen); resolution.x = fb->width; resolution.y = fb->height; @@ -479,23 +527,13 @@ int main(int argc, const char **argv) rfbRunEventLoop(server,-1,TRUE); while (rfbIsActive(server)) { if (server->clientHead && TimeToTakePicture()) { - vblank_func(drmfd); - // still tearing - memcpy(buf2, mapped, buflen); - convert_func(buf2, fb->width, fb->height, buf); + funcs.sync_start(primefd); + funcs.convert(mapped, fb->width, fb->height, buf); + funcs.sync_end(primefd); rfbMarkRectAsModified(server, 0, 0, fb->width, fb->height); } } - if (drm_ver != NULL) drmFreeVersion(drm_ver); - if (fb != NULL) drmModeFreeFB(fb); - if (uinput_fd > 0) { - ioctl(uinput_fd, UI_DEV_DESTROY); - close(uinput_fd); - } - close(drmfd); - return 0; - cleanup: if (drm_ver != NULL) drmFreeVersion(drm_ver); if (fb != NULL) drmModeFreeFB(fb); @@ -503,6 +541,7 @@ int main(int argc, const char **argv) ioctl(uinput_fd, UI_DEV_DESTROY); close(uinput_fd); } + if (primefd > 0) close(primefd); close(drmfd); return 1; }