summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Baryshkov <dmitry.baryshkov@linaro.org>2023-06-28 19:39:57 +0300
committerEric Engestrom <eric@engestrom.ch>2023-07-12 17:43:33 +0000
commit53ea71efe63470cf698726d983e9da5748a754a1 (patch)
tree5119df6d7319411ec56983702c83a94f408175f7
parentf296635648ba31454bfe6a9ff7495f6b28af26ec (diff)
kmscube: add offscreen rendering support
It is useful to be able to render the kube without touching the KMS. For example, this allows us to utilise DRM render devices or to measure the performance without waiting for vblank. Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
-rw-r--r--drm-common.c64
-rw-r--r--drm-common.h2
-rw-r--r--drm-offscreen.c115
-rw-r--r--kmscube.c21
-rw-r--r--meson.build1
5 files changed, 200 insertions, 3 deletions
diff --git a/drm-common.c b/drm-common.c
index 95a5b92..6685b9f 100644
--- a/drm-common.c
+++ b/drm-common.c
@@ -175,6 +175,32 @@ static int get_resources(int fd, drmModeRes **resources)
#define MAX_DRM_DEVICES 64
+static int find_drm_render_device(void)
+{
+ drmDevicePtr devices[MAX_DRM_DEVICES] = { NULL };
+ int num_devices, fd = -1;
+
+ num_devices = drmGetDevices2(0, devices, MAX_DRM_DEVICES);
+ if (num_devices < 0) {
+ printf("drmGetDevices2 failed: %s\n", strerror(-num_devices));
+ return -1;
+ }
+
+ for (int i = 0; i < num_devices && fd < 0; i++) {
+ drmDevicePtr device = devices[i];
+
+ if (!(device->available_nodes & (1 << DRM_NODE_RENDER)))
+ continue;
+ fd = open(device->nodes[DRM_NODE_RENDER], O_RDWR);
+ }
+ drmFreeDevices(devices, num_devices);
+
+ if (fd < 0)
+ printf("no drm device found!\n");
+
+ return fd;
+}
+
static int find_drm_device(drmModeRes **resources)
{
drmDevicePtr devices[MAX_DRM_DEVICES] = { NULL };
@@ -332,3 +358,41 @@ int init_drm(struct drm *drm, const char *device, const char *mode_str,
return 0;
}
+
+int init_drm_render(struct drm *drm, const char *device, const char *mode_str, unsigned int count)
+{
+ int width, height;
+ drmModeModeInfo *mode;
+
+ if (!mode_str)
+ return -1;
+
+ if (sscanf(mode_str, "%dx%d", &width, &height) != 2)
+ return -1;
+
+ if (device) {
+ drm->fd = open(device, O_RDWR);
+ } else {
+ drm->fd = find_drm_render_device();
+ }
+
+ if (drm->fd < 0) {
+ printf("could not open drm device\n");
+ return -1;
+ }
+
+ mode = malloc(sizeof(*mode));
+ if (!mode) {
+ close(drm->fd);
+ drm->fd = -1;
+ return -1;
+ }
+
+ mode->hdisplay = width;
+ mode->vdisplay = height;
+ drm->mode = mode;
+
+ drm->count = count;
+
+ return 0;
+}
diff --git a/drm-common.h b/drm-common.h
index 32648b0..516896a 100644
--- a/drm-common.h
+++ b/drm-common.h
@@ -77,7 +77,9 @@ struct drm_fb {
struct drm_fb * drm_fb_get_from_bo(struct gbm_bo *bo);
int init_drm(struct drm *drm, const char *device, const char *mode_str, unsigned int vrefresh, unsigned int count);
+int init_drm_render(struct drm *drm, const char *device, const char *mode_str, unsigned int count);
const struct drm * init_drm_legacy(const char *device, const char *mode_str, unsigned int vrefresh, unsigned int count);
const struct drm * init_drm_atomic(const char *device, const char *mode_str, unsigned int vrefresh, unsigned int count);
+const struct drm * init_drm_offscreen(const char *device, const char *mode_str, unsigned int count);
#endif /* _DRM_COMMON_H */
diff --git a/drm-offscreen.c b/drm-offscreen.c
new file mode 100644
index 0000000..2b63fae
--- /dev/null
+++ b/drm-offscreen.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2017 Rob Clark <rclark@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sub license,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "drm-common.h"
+
+static struct drm drm;
+
+static int offscreen_run(const struct gbm *gbm, const struct egl *egl)
+{
+ struct gbm_bo *bo = NULL;
+ uint32_t i = 0;
+ int64_t start_time, report_time, cur_time;
+
+ start_time = report_time = get_time_ns();
+
+ while (i < drm.count) {
+ unsigned frame = i;
+ struct gbm_bo *next_bo;
+
+ /* Start fps measuring on second frame, to remove the time spent
+ * compiling shader, etc, from the fps:
+ */
+ if (i == 1) {
+ start_time = report_time = get_time_ns();
+ }
+
+ if (!gbm->surface) {
+ glBindFramebuffer(GL_FRAMEBUFFER, egl->fbs[frame % NUM_BUFFERS].fb);
+ }
+
+ egl->draw(i++);
+
+ if (gbm->surface) {
+ eglSwapBuffers(egl->display, egl->surface);
+ next_bo = gbm_surface_lock_front_buffer(gbm->surface);
+ } else {
+ glFinish();
+ next_bo = gbm->bos[frame % NUM_BUFFERS];
+ }
+ if (!next_bo) {
+ printf("Failed to lock frontbuffer\n");
+ return -1;
+ }
+
+ cur_time = get_time_ns();
+ if (cur_time > (report_time + 2 * NSEC_PER_SEC)) {
+ double elapsed_time = cur_time - start_time;
+ double secs = elapsed_time / (double)NSEC_PER_SEC;
+ unsigned frames = i - 1; /* first frame ignored */
+ printf("Rendered %u frames in %f sec (%f fps)\n",
+ frames, secs, (double)frames/secs);
+ report_time = cur_time;
+ }
+
+ /* release last buffer to render on again: */
+ if (bo && gbm->surface)
+ gbm_surface_release_buffer(gbm->surface, bo);
+ bo = next_bo;
+ }
+
+ finish_perfcntrs();
+
+ cur_time = get_time_ns();
+ double elapsed_time = cur_time - start_time;
+ double secs = elapsed_time / (double)NSEC_PER_SEC;
+ unsigned frames = i - 1; /* first frame ignored */
+ printf("Rendered %u frames in %f sec (%f fps)\n",
+ frames, secs, (double)frames/secs);
+
+ dump_perfcntrs(frames, elapsed_time);
+
+ return 0;
+}
+
+const struct drm * init_drm_offscreen(const char *device, const char *mode_str, unsigned int count)
+{
+ int ret;
+
+ ret = init_drm_render(&drm, device, mode_str, count);
+ if (ret)
+ return NULL;
+
+ drm.run = offscreen_run;
+
+ return &drm;
+}
diff --git a/kmscube.c b/kmscube.c
index 7f2ac26..bcd0f6d 100644
--- a/kmscube.c
+++ b/kmscube.c
@@ -41,7 +41,7 @@ static const struct egl *egl;
static const struct gbm *gbm;
static const struct drm *drm;
-static const char *shortopts = "Ac:D:f:M:m:p:S:s:V:v:x";
+static const char *shortopts = "Ac:D:f:M:m:Op:S:s:V:v:x";
static const struct option longopts[] = {
{"atomic", no_argument, 0, 'A'},
@@ -50,6 +50,7 @@ static const struct option longopts[] = {
{"format", required_argument, 0, 'f'},
{"mode", required_argument, 0, 'M'},
{"modifier", required_argument, 0, 'm'},
+ {"offscreen", no_argument, 0, 'O'},
{"perfcntr", required_argument, 0, 'p'},
{"samples", required_argument, 0, 's'},
{"video", required_argument, 0, 'V'},
@@ -73,6 +74,7 @@ static void usage(const char *name)
" nv12-2img - yuv textured (color conversion in shader)\n"
" nv12-1img - yuv textured (single nv12 texture)\n"
" -m, --modifier=MODIFIER hardcode the selected modifier\n"
+ " -O, --offscreen use offscreen rendering (e.g. for render nodes)\n"
" -p, --perfcntr=LIST sample specified performance counters using\n"
" the AMD_performance_monitor extension (comma\n"
" separated list, shadertoy mode only)\n"
@@ -99,6 +101,7 @@ int main(int argc, char *argv[])
uint64_t modifier = DRM_FORMAT_MOD_LINEAR;
int samples = 0;
int atomic = 0;
+ int offscreen = 0;
int opt;
unsigned int len;
unsigned int vrefresh = 0;
@@ -154,6 +157,9 @@ int main(int argc, char *argv[])
case 'm':
modifier = strtoull(optarg, NULL, 0);
break;
+ case 'O':
+ offscreen = 1;
+ break;
case 'p':
perfcntr = optarg;
break;
@@ -190,12 +196,21 @@ int main(int argc, char *argv[])
}
}
- if (atomic)
+ if (offscreen && atomic) {
+ printf("Only one of `--atomic' and `--offscreen' should be specified.\n");
+ return -1;
+ }
+
+ if (offscreen)
+ drm = init_drm_offscreen(device, mode_str, count);
+ else if (atomic)
drm = init_drm_atomic(device, mode_str, vrefresh, count);
else
drm = init_drm_legacy(device, mode_str, vrefresh, count);
if (!drm) {
- printf("failed to initialize %s DRM\n", atomic ? "atomic" : "legacy");
+ printf("failed to initialize %s DRM\n",
+ offscreen ? "offscreen" :
+ atomic ? "atomic" : "legacy");
return -1;
}
diff --git a/meson.build b/meson.build
index 0ac5c7d..5ed2b57 100644
--- a/meson.build
+++ b/meson.build
@@ -40,6 +40,7 @@ sources = files(
'drm-atomic.c',
'drm-common.c',
'drm-legacy.c',
+ 'drm-offscreen.c',
'esTransform.c',
'frame-512x512-NV12.c',
'frame-512x512-RGBA.c',