diff --git a/.gitlab-ci/debian-install.sh b/.gitlab-ci/debian-install.sh index 758c15dd2168f0ba865c3601fa474ec57cf8b3e1..2a9eda24c5ae9c55f7297971c03789819ce60f01 100644 --- a/.gitlab-ci/debian-install.sh +++ b/.gitlab-ci/debian-install.sh @@ -4,7 +4,7 @@ set -o xtrace echo 'deb http://deb.debian.org/debian stretch-backports main' >> /etc/apt/sources.list apt-get update -apt-get -y --no-install-recommends install build-essential automake autoconf libtool pkg-config libexpat1-dev libffi-dev libxml2-dev libpixman-1-dev libpng-dev libjpeg-dev libcolord-dev mesa-common-dev libglu1-mesa-dev libegl1-mesa-dev libgles2-mesa-dev libwayland-dev libxcb1-dev libxcb-composite0-dev libxcb-xfixes0-dev libxcb-xkb-dev libx11-xcb-dev libx11-dev libudev-dev libgbm-dev libxkbcommon-dev libcairo2-dev libpango1.0-dev libgdk-pixbuf2.0-dev libxcursor-dev libmtdev-dev libpam0g-dev libvpx-dev libsystemd-dev libevdev-dev libinput-dev libwebp-dev libjpeg-dev libva-dev liblcms2-dev git libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev freerdp2-dev curl python3-pip python3-setuptools ninja-build +apt-get -y --no-install-recommends install build-essential automake autoconf libtool pkg-config libexpat1-dev libffi-dev libxml2-dev libpixman-1-dev libpng-dev libjpeg-dev libcolord-dev mesa-common-dev libglu1-mesa-dev libegl1-mesa-dev libgles2-mesa-dev libwayland-dev libxcb1-dev libxcb-composite0-dev libxcb-xfixes0-dev libxcb-xkb-dev libx11-xcb-dev libx11-dev libudev-dev libgbm-dev libxkbcommon-dev libcairo2-dev libpango1.0-dev libgdk-pixbuf2.0-dev libxcursor-dev libmtdev-dev libpam0g-dev libvpx-dev libsystemd-dev libevdev-dev libinput-dev libwebp-dev libjpeg-dev libva-dev liblcms2-dev git libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev freerdp2-dev curl python3-pip python3-setuptools ninja-build libavformat-dev libavutil-dev libavcodec-dev pip3 install --user git+https://github.com/mesonbuild/meson.git@0.49 diff --git a/clients/meson.build b/clients/meson.build index 3d36efe489e03c82d6b075a4a7997f93421ef91e..7b0db6130036a448744b43b212a15045cc028c7a 100644 --- a/clients/meson.build +++ b/clients/meson.build @@ -338,6 +338,109 @@ if simple_dmabuf_drm_deps.length() > 0 ) endif +simple_hdr_video_opts = get_option('simple-hdr-video') +simple_hdr_video_deps = [] +foreach driver : [ 'intel'] + if simple_hdr_video_opts.contains(driver) + required = true + enabled = true + elif simple_hdr_video_opts.contains('auto') + required = get_option('auto_features').enabled() + enabled = not get_option('auto_features').disabled() + else + enabled = false + endif + + if enabled + dep = dependency('libdrm_' + driver, required: false) + if dep.found() + simple_hdr_video_deps += dep + config_h.set('HAVE_LIBDRM_' + driver.to_upper(), 1) + elif required + error('simple-hdr-video is configured to use @0@ but it was not found. Or, you can remove @1@ from \'-Dsimple-hdr-video\' list.'.format('libdrm_' + driver, driver)) + endif + + simple_hdr_video_deps += dependency('libavutil') + simple_hdr_video_deps += dependency('libavcodec') + simple_hdr_video_deps += dependency('libavformat') + endif +endforeach + +if simple_hdr_video_deps.length() > 0 + executable( + 'weston-simple-hdr-video', + 'simple-hdr-video.c', + xdg_shell_client_protocol_h, + xdg_shell_protocol_c, + linux_dmabuf_unstable_v1_client_protocol_h, + linux_dmabuf_unstable_v1_protocol_c, + hdr_metadata_unstable_v1_protocol_c, + hdr_metadata_unstable_v1_client_protocol_h, + colorspace_unstable_v1_protocol_c, + colorspace_unstable_v1_client_protocol_h, + include_directories: include_directories('..'), + dependencies: [ + dep_wayland_client, + dep_libdrm, + simple_hdr_video_deps, + dep_toytoolkit + ], + install: true + ) +endif + +simple_hdr_video_gbm_opts = get_option('simple-hdr-video') +simple_hdr_video_gbm_deps = [] +foreach driver : [ 'intel'] + if simple_hdr_video_gbm_opts.contains(driver) + required = true + enabled = true + elif simple_hdr_video_gbm_opts.contains('auto') + required = get_option('auto_features').enabled() + enabled = not get_option('auto_features').disabled() + else + enabled = false + endif + + if enabled + dep = dependency('libdrm_' + driver, required: false) + if dep.found() + simple_hdr_video_gbm_deps += dep + config_h.set('HAVE_LIBDRM_' + driver.to_upper(), 1) + elif required + error('simple-hdr-video is configured to use @0@ but it was not found. Or, you can remove @1@ from \'-Dsimple-hdr-video\' list.'.format('libdrm_' + driver, driver)) + endif + + simple_hdr_video_gbm_deps += dependency('libavutil') + simple_hdr_video_gbm_deps += dependency('libavcodec') + simple_hdr_video_gbm_deps += dependency('libavformat') + simple_hdr_video_gbm_deps += dependency('gbm') + endif +endforeach + +if simple_hdr_video_gbm_deps.length() > 0 + executable( + 'weston-simple-hdr-video-gbm', + 'simple-hdr-video-gbm.c', + xdg_shell_client_protocol_h, + xdg_shell_protocol_c, + linux_dmabuf_unstable_v1_client_protocol_h, + linux_dmabuf_unstable_v1_protocol_c, + hdr_metadata_unstable_v1_protocol_c, + hdr_metadata_unstable_v1_client_protocol_h, + colorspace_unstable_v1_protocol_c, + colorspace_unstable_v1_client_protocol_h, + include_directories: include_directories('..'), + dependencies: [ + dep_wayland_client, + dep_libdrm, + simple_hdr_video_gbm_deps, + dep_toytoolkit + ], + install: true + ) +endif + if get_option('shell-desktop') exe_keyboard = executable( 'weston-keyboard', diff --git a/clients/simple-hdr-video-gbm.c b/clients/simple-hdr-video-gbm.c new file mode 100644 index 0000000000000000000000000000000000000000..bf9030092cea385c5aee4810565f438f67a7097c --- /dev/null +++ b/clients/simple-hdr-video-gbm.c @@ -0,0 +1,1160 @@ +/* + * Copyright © 2019 Harish Krupo + * Copyright © 2019 Intel Corporation + * + * 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, sublicense, + * 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 NONINFRINGEMENT. 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 "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +#ifdef HAVE_PANGO +#include +#endif + +#include "colorspace-unstable-v1-client-protocol.h" +#include "hdr-metadata-unstable-v1-client-protocol.h" +#include "linux-dmabuf-unstable-v1-client-protocol.h" + +#include "shared/os-compatibility.h" +#include "shared/helpers.h" +#include "shared/platform.h" +#include "shared/xalloc.h" +#include +#include "window.h" + +#define DBG(fmt, ...) \ + fprintf(stderr, "%d:%s " fmt, __LINE__, __func__, ##__VA_ARGS__) + +#define NUM_BUFFERS 3 + +#ifndef DRM_FORMAT_MOD_LINEAR +#define DRM_FORMAT_MOD_LINEAR 0 +#endif + +#ifndef DRM_FORMAT_P010 +#define DRM_FORMAT_P010 fourcc_code('P', '0', '1', '0') /* 2x2 subsampled Cb:Cr plane 10 bits per channel */ +#endif + +#ifndef DRM_FORMAT_P012 +#define DRM_FORMAT_P012 fourcc_code('P', '0', '1', '2') /* 2x2 subsampled Cr:Cb plane, 12 bit per channel */ +#endif + +#ifndef DRM_FORMAT_P016 +#define DRM_FORMAT_P016 fourcc_code('P', '0', '1', '6') /* 2x2 subsampled Cr:Cb plane, 16 bit per channel */ +#endif + +static int32_t option_help; +static int32_t option_fullscreen; +static int32_t option_subtitle; + +static const struct weston_option options[] = { + { WESTON_OPTION_BOOLEAN, "fullscreen", 'f', &option_fullscreen }, + { WESTON_OPTION_BOOLEAN, "subtitle", 's', &option_subtitle }, + { WESTON_OPTION_BOOLEAN, "help", 'h', &option_help }, +}; + +static const char help_text[] = +"Usage: %s [options] FILENAME\n" +"\n" +" -f, --fullscreen\t\tRun in fullscreen mode\n" +" -s, --subtitle\t\tShow subtiles\n" +" -h, --help\t\tShow this help text\n" +"\n"; + +struct app; +struct buffer; + +struct buffer { + struct wl_buffer *buffer; + int busy; + + int drm_fd; + uint32_t gem_handle; + int dmabuf_fd; + uint8_t *mmap; + + int width; + int height; + int bpp; + unsigned long stride; + int format; + AVFrame *prev_frame; + + struct gbm_device *gbm; + int cpp; + struct gbm_bo *bo; +}; + +struct subtitle { + + struct wl_surface *wl_surface; + int width; + int height; + + struct widget *widget; + uint32_t time; + struct wl_callback *frame_cb; + struct app *app; + struct buffer buffers[NUM_BUFFERS]; + struct buffer *prev_buffer; +}; + +struct video { + AVFormatContext *fmt_ctx; + AVCodecParserContext *parser; + AVCodecContext *codec; + AVPacket *pkt; + int stream_index; + + struct buffer buffers[NUM_BUFFERS]; + struct buffer *prev_buffer; +}; + +struct app { + struct display *display; + struct window *window; + struct widget *widget; + struct video video; + + struct subtitle *subtitle; + + struct zwp_colorspace_v1 *colorspace; + struct zwp_hdr_metadata_v1 *hdr_metadata; + + struct zwp_hdr_surface_v1 *hdr_surface; + struct zwp_linux_dmabuf_v1 *dmabuf; +}; + +static int +create_dmabuf_buffer(struct app *app, struct buffer *buffer, + int width, int height, int format); + +static void +gbm_fini(struct buffer *my_buf); + +static void +destroy_dmabuf_buffer(struct buffer *buffer) +{ + if (buffer->buffer) { + wl_buffer_destroy(buffer->buffer); + close(buffer->dmabuf_fd); + gbm_fini(buffer); + } +} + +static void +subtitle_resize_handler(struct widget *widget, + int32_t width, int32_t height, void *data) +{ + struct subtitle *sub = data; + struct app *app = sub->app; + struct rectangle allocation; + struct wl_surface *surface; + uint32_t format; + int i; + + widget_get_allocation(sub->widget, &allocation); + + surface = widget_get_wl_surface(widget); + + // clear surface's buffer + wl_surface_attach(surface, NULL, 0, 0); + for (i = 0; i < NUM_BUFFERS; i++) { + destroy_dmabuf_buffer(&sub->buffers[i]); + } + + format = DRM_FORMAT_ARGB8888; + + for (i = 0; i < NUM_BUFFERS; i++) { + create_dmabuf_buffer(app, &sub->buffers[i], + allocation.width, + allocation.height, + format); + + } + +} + +static struct buffer * +subtitle_next_buffer(struct subtitle *sub) +{ + int i; + + for (i = 0; i < NUM_BUFFERS; i++) + if (!sub->buffers[i].busy) + return &sub->buffers[i]; + + return NULL; +} + +#ifdef HAVE_PANGO +static PangoLayout * +create_layout(cairo_t *cr, const char *title) +{ + PangoLayout *layout; + PangoFontDescription *desc; + + layout = pango_cairo_create_layout(cr); + pango_layout_set_text(layout, title, -1); + desc = pango_font_description_from_string("Sans Bold 19"); + pango_layout_set_font_description(layout, desc); + pango_font_description_free(desc); + pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); + pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT); + pango_layout_set_auto_dir (layout, FALSE); + pango_layout_set_single_paragraph_mode (layout, TRUE); + pango_layout_set_width (layout, -1); + + return layout; +} +#endif + +static void +fill_subtitle(struct buffer *buffer) +{ + cairo_surface_t *surface; + cairo_t* cr; + char *title = "Sample subtitle string"; + PangoLayout *title_layout; + + assert(buffer->mmap); + + surface = cairo_image_surface_create_for_data(buffer->mmap, + CAIRO_FORMAT_ARGB32, + buffer->width, + buffer->height, + buffer->stride); + cr = cairo_create(surface); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); +#ifdef HAVE_PANGO + cairo_set_source_rgba(cr, 0, 0, 0, 0); +#else + cairo_set_source_rgba(cr, 1, 1, 1, 1); +#endif + + cairo_paint(cr); + +#ifdef HAVE_PANGO + /* cairo_set_operator(cr, CAIRO_OPERATOR_OVER); */ + title_layout = create_layout(cr, title); + cairo_move_to(cr, 0, 0); + cairo_set_source_rgb(cr, 1, 1, 1); + pango_cairo_show_layout(cr, title_layout); +#endif + + cairo_destroy(cr); + cairo_surface_destroy(surface); +} + +static void +subtitle_redraw_handler(struct widget *widget, void *data) +{ + struct subtitle *sub = data; + struct rectangle allocation; + struct buffer *buffer; + struct wl_surface *surface; + void *map_data = NULL; + uint32_t dst_stride; + + widget_get_allocation(sub->widget, &allocation); + buffer = subtitle_next_buffer(sub); + + buffer->mmap = gbm_bo_map(buffer->bo, 0, 0, buffer->width, buffer->height, + GBM_BO_TRANSFER_WRITE, &dst_stride, &map_data); + if (!buffer->mmap) { + fprintf(stderr, "failed to map failed\n"); + return; + } + + fill_subtitle(buffer); + + gbm_bo_unmap(buffer->bo, map_data); + + surface = widget_get_wl_surface(widget); + wl_surface_attach(surface, buffer->buffer, 0, 0); + wl_surface_damage(surface, 0, 0, allocation.width, allocation.height); + wl_surface_commit(surface); + buffer->busy = 1; + + widget_schedule_redraw(sub->widget); +} + +static struct subtitle * +subtitle_create(struct app *app) +{ + struct subtitle *sub; + + sub = xzalloc(sizeof *sub); + sub->app = app; + + sub->widget = window_add_subsurface(app->window, sub, + SUBSURFACE_SYNCHRONIZED); + + widget_set_use_cairo(sub->widget, 0); + widget_set_resize_handler(sub->widget, subtitle_resize_handler); + widget_set_redraw_handler(sub->widget, subtitle_redraw_handler); + + return sub; +} + +static void +subtitle_destroy(struct subtitle *sub) +{ + int i; + + for (i = 0; i < NUM_BUFFERS; i++) { + destroy_dmabuf_buffer(&sub->buffers[i]); + } + + widget_destroy(sub->widget); + free(sub); +} + +static void +buffer_release(void *data, struct wl_buffer *buffer) +{ + struct buffer *mybuf = data; + mybuf->busy = 0; + if (mybuf->prev_frame) + av_frame_free(&mybuf->prev_frame); + + mybuf->prev_frame = NULL; +} + +static const struct wl_buffer_listener buffer_listener = { + buffer_release +}; + +static uint32_t av_format_to_drm_format(int format) +{ + switch (format) { + case AV_PIX_FMT_YUV420P: + return DRM_FORMAT_YUV420; + case AV_PIX_FMT_YUV420P10BE: + case AV_PIX_FMT_YUV420P10LE: + return DRM_FORMAT_P010; + case AV_PIX_FMT_YUV420P12BE: + case AV_PIX_FMT_YUV420P12LE: + return DRM_FORMAT_P012; + case AV_PIX_FMT_YUV420P16BE: + case AV_PIX_FMT_YUV420P16LE: + return DRM_FORMAT_P016; + default: + return -1; + } +} + +static inline void +ensure_hdr_surface(struct app *app) +{ + struct window *window = app->window; + struct wl_surface *surface; + + assert(app->hdr_metadata); + if (app->hdr_surface) + return; + + surface = window_get_wl_surface(window); + app->hdr_surface = zwp_hdr_metadata_v1_get_hdr_surface(app->hdr_metadata, + surface); +} + +static inline void +destroy_hdr_surface(struct app *app) +{ + if (app->hdr_surface) { + zwp_hdr_surface_v1_destroy(app->hdr_surface); + app->hdr_surface = NULL; + } +} + +static bool +decode(struct video *s, AVFrame *frame) +{ + int r; + + if (s->pkt->size == 0) + return false; + + if (s->pkt->stream_index != s->stream_index) + return false; + + r = avcodec_send_packet(s->codec, s->pkt); + if (r < 0) + return false; + + r = avcodec_receive_frame(s->codec, frame); + if (r < 0) + return false; + + return true; +} + +static AVFrame * +demux_and_decode(struct video *s) +{ + AVFrame *frame; + bool ret; + + frame = av_frame_alloc(); + if (!frame) + return NULL; + + for (;;) { + int r; + + r = av_read_frame(s->fmt_ctx, s->pkt); + if (r < 0) + break; + + ret = decode(s, frame); + + av_packet_unref(s->pkt); + + if (ret) + break; + } + + if (!ret) + av_frame_free(&frame); + + return frame; +} + +static struct buffer * +video_next_buffer(struct video *s) +{ + int i; + + for (i = 0; i < NUM_BUFFERS; i++) + if (!s->buffers[i].busy) + return &s->buffers[i]; + + return NULL; +} + +static void +copy_420p10_to_p010(struct buffer *buffer, AVFrame *frame) +{ + int height, linesize; + uint16_t *yplane, *dsty, *uplane, *vplane, *dstuv; + int size; + + // copy plane 0 + height = buffer->height; + yplane = (uint16_t *) frame->data[0]; + dsty = (uint16_t *) buffer->mmap; + linesize = frame->linesize[0]; + + size = (linesize * height) / 2; + // bitshift and copy y plane + for (int i = 0; i < size; i++) { + dsty[i] = yplane[i] << 6; + } + + // copy plane 1 and 2 alternatingly from source + height = buffer->height / 2; + uplane = (uint16_t *) frame->data[1]; + vplane = (uint16_t *) frame->data[2]; + dstuv = (uint16_t *) (buffer->mmap + buffer->stride * buffer->height); + + // both uplane and vplane would have same linesize + linesize = frame->linesize[1]; + + size = (linesize * height) / 2; + + // bit shift and copy u, v alternatingly + for (int i = 0; i < size; i++) { + dstuv[2 * i] = uplane[i] << 6; + dstuv[(2 * i) + 1] = vplane[i] << 6; + } +} + +static int +fill_buffer(struct buffer *buffer, AVFrame *frame) { + int linesize; + int height; + int plane_heights[4] = {0}; + int offsets[4] = {0}; + int n_planes = 0; + uint8_t *src, *dst; + int i; + void *map_data = NULL; + uint32_t dst_stride; + + buffer->mmap = gbm_bo_map(buffer->bo, 0, 0, buffer->width, buffer->height, + GBM_BO_TRANSFER_WRITE, &dst_stride, &map_data); + if (!buffer->mmap) { + fprintf(stderr, "Unable to mmap buffer\n"); + return 0; + } + + switch (buffer->format) { + case DRM_FORMAT_YUV420: + plane_heights[0] = buffer->height; + plane_heights[1] = buffer->height / 2; + plane_heights[2] = buffer->height / 2; + offsets[0] = 0; + offsets[1] = buffer->height; + offsets[2] = buffer->height * 3 / 2; + n_planes = 3; + break; + case DRM_FORMAT_P010: + copy_420p10_to_p010(buffer, frame); + gbm_bo_unmap(buffer->bo, map_data); + return 1; + } + + for (i = 0; i < n_planes; i++) { + height = plane_heights[i]; + linesize = frame->linesize[i]; + + src = frame->data[i]; + dst = buffer->mmap + frame->linesize[0] * offsets[i]; + + for (;height > 0; height--) { + memcpy(dst, src, linesize); + dst += linesize; + src += linesize; + } + } + + gbm_bo_unmap(buffer->bo, map_data); + return 1; +} + +static void +redraw_handler(struct widget *widget, void *data) +{ + struct app *app = data; + struct video *video = &app->video; + struct buffer *buffer; + struct wl_buffer *wlbuffer; + struct wl_surface *surface; + AVFrame *frame; + AVFrameSideData *sd; + AVMasteringDisplayMetadata *hdr_metadata; + AVContentLightMetadata *ll_metadata; + int max_cll = -1, max_fall = -1; + wl_fixed_t rx, ry, gx, gy, bx, by, wx, wy, max_luma, min_luma; + + frame = demux_and_decode(&app->video); + if (!frame) { + fprintf(stderr, "no more frames?\n"); + return; + } + + sd = av_frame_get_side_data(frame, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL); + if (sd) { + ll_metadata = (AVContentLightMetadata *)sd->data; + max_cll = ll_metadata->MaxCLL; + max_fall = ll_metadata->MaxFALL; + } + sd = av_frame_get_side_data(frame, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA); + if (sd) { + hdr_metadata = (AVMasteringDisplayMetadata *)sd->data; + if (hdr_metadata->has_luminance && hdr_metadata->has_primaries) { + ensure_hdr_surface(app); + rx = wl_fixed_from_double(av_q2d(hdr_metadata->display_primaries[0][0])); + ry = wl_fixed_from_double(av_q2d(hdr_metadata->display_primaries[0][1])); + gx = wl_fixed_from_double(av_q2d(hdr_metadata->display_primaries[1][0])); + gy = wl_fixed_from_double(av_q2d(hdr_metadata->display_primaries[1][1])); + bx = wl_fixed_from_double(av_q2d(hdr_metadata->display_primaries[2][0])); + by = wl_fixed_from_double(av_q2d(hdr_metadata->display_primaries[2][1])); + wx = wl_fixed_from_double(av_q2d(hdr_metadata->white_point[0])); + wy = wl_fixed_from_double(av_q2d(hdr_metadata->white_point[1])); + max_luma = wl_fixed_from_double(av_q2d(hdr_metadata->max_luminance)); + min_luma = wl_fixed_from_double(av_q2d(hdr_metadata->min_luminance)); + zwp_hdr_surface_v1_set( + app->hdr_surface, + rx, + ry, + gx, + gy, + bx, + by, + wx, + wy, + max_luma, + min_luma, + max_cll == -1 ? 0 : max_cll, + max_fall == -1 ? 0 : max_fall); + + zwp_hdr_surface_v1_set_eotf( + app->hdr_surface, + ZWP_HDR_SURFACE_V1_EOTF_ST_2084_PQ); + } + } else { + // No metadata for this frame. Destroy the created hdr surface. + destroy_hdr_surface(app); + } + + buffer = video_next_buffer(video); + + // If no free buffers available schedule redraw and return; + if(!buffer) { + widget_schedule_redraw(widget); + return; + } + + if (!fill_buffer(buffer, frame)) { + fprintf(stderr, "failed to fill buffer\n"); + return; + } + + wlbuffer = buffer->buffer; + + surface = widget_get_wl_surface(widget); + wl_surface_attach(surface, wlbuffer, 0, 0); + wl_surface_damage(surface, 0, 0, frame->width, frame->height); + wl_surface_commit(surface); + widget_schedule_redraw(widget); + buffer->busy = 1; + buffer->prev_frame = frame; +} + +/* + * +---------------------------+ + * | | | + * | | | + * | |vm Video | + * | | | + * | | | + * |___+-------------------+ | + * | hm| Subtitle | | + * | +-------------------+ | + * | | + * +---------------------------+ + * + * hm : horizontal margin + * vm : vertical margin + */ + +static void +resize_handler(struct widget *widget, + int32_t width, int32_t height, void *data) +{ + struct app *app = data; + struct rectangle area; + + // margin is in percentage + int vm = 85, hm = 40; + int x, y, w, h; + int mhorizontal, mvertical; + + if (app->subtitle) { + widget_get_allocation(widget, &area); + + mhorizontal = area.width * hm / 100; + mvertical = area.height * vm / 100; + + x = area.x + mhorizontal; + y = area.y + mvertical; + w = area.width * 2 / 10; // 20% of total width + h = area.height / 20; // 5% of total height + + widget_set_allocation(app->subtitle->widget, + x, y, w, h); + } + +} + +static void +keyboard_focus_handler(struct window *window, + struct input *device, void *data) +{ + struct app *app = data; + + window_schedule_redraw(app->window); +} + +static void +key_handler(struct window *window, struct input *input, uint32_t time, + uint32_t key, uint32_t sym, + enum wl_keyboard_key_state state, void *data) +{ + struct app *app = data; + struct rectangle winrect; + + if (state == WL_KEYBOARD_KEY_STATE_RELEASED) + return; + + switch (sym) { + case XKB_KEY_Up: + window_get_allocation(window, &winrect); + winrect.height -= 100; + if (winrect.height < 150) + winrect.height = 150; + window_schedule_resize(window, winrect.width, winrect.height); + break; + case XKB_KEY_Down: + window_get_allocation(window, &winrect); + winrect.height += 100; + if (winrect.height > 600) + winrect.height = 600; + window_schedule_resize(window, winrect.width, winrect.height); + break; + case XKB_KEY_Escape: + display_exit(app->display); + break; + } +} + +static void video_close(struct video *s) +{ + av_parser_close(s->parser); + avcodec_free_context(&s->codec); + av_packet_free(&s->pkt); +} + +static const enum zwp_colorspace_v1_chromacities chromacities[] = { + [AVCOL_PRI_BT709] = ZWP_COLORSPACE_V1_CHROMACITIES_BT709, + [AVCOL_PRI_BT470M] = ZWP_COLORSPACE_V1_CHROMACITIES_BT470M, + [AVCOL_PRI_BT470BG] = ZWP_COLORSPACE_V1_CHROMACITIES_BT470BG, + [AVCOL_PRI_SMPTE170M] = ZWP_COLORSPACE_V1_CHROMACITIES_SMPTE170M, + [AVCOL_PRI_SMPTE240M] = ZWP_COLORSPACE_V1_CHROMACITIES_SMPTE170M, + [AVCOL_PRI_SMPTE431] = ZWP_COLORSPACE_V1_CHROMACITIES_DCI_P3, + [AVCOL_PRI_SMPTE432] = ZWP_COLORSPACE_V1_CHROMACITIES_DCI_P3, + [AVCOL_PRI_SMPTE428] = ZWP_COLORSPACE_V1_CHROMACITIES_CIEXYZ, + [AVCOL_PRI_BT2020] = ZWP_COLORSPACE_V1_CHROMACITIES_BT2020, +}; + +static enum zwp_colorspace_v1_chromacities +video_chromacities(struct video *s) +{ + if (s->codec->color_primaries >= ARRAY_LENGTH(chromacities)) + return ZWP_COLORSPACE_V1_CHROMACITIES_UNDEFINED; + + return chromacities[s->codec->color_primaries]; +} + +static bool video_open(struct app *app, + struct video *s, + const char *filename) +{ + AVCodec *codec = NULL; + AVStream *stream; + int r; + char buf[4096] = {}; +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100) + av_register_all(); +#endif + + r = avformat_open_input(&s->fmt_ctx, filename, NULL, NULL); + if (r < 0) + return false; + + r = avformat_find_stream_info(s->fmt_ctx, NULL); + if (r < 0) + return false; + + r = av_find_best_stream(s->fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0); + if (r < 0) + return false; + + stream = s->fmt_ctx->streams[r]; + s->stream_index = r; + + s->codec = avcodec_alloc_context3(codec); + if (!s->codec) + return false; + + s->codec->opaque = s; + + r = avcodec_parameters_to_context(s->codec, stream->codecpar); + if (r < 0) + return false; + + r = avcodec_open2(s->codec, codec, NULL); + if (r < 0) + return false; + + s->parser = av_parser_init(codec->id); + if (!s->parser) + return false; + + avcodec_string(buf, sizeof(buf), s->codec, false); + buf[sizeof(buf)-1] = '\0'; + puts(buf); + + s->pkt = av_packet_alloc(); + + return true; +} + +static void +dmabuf_modifiers(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf, + uint32_t format, uint32_t modifier_hi, uint32_t modifier_lo) +{ +} + +static void +dmabuf_format(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf, uint32_t format) +{ + /* XXX: deprecated */ +} + + +static const struct zwp_linux_dmabuf_v1_listener dmabuf_listener = { + dmabuf_format, + dmabuf_modifiers +}; + +static void +global_handler(struct display *display, uint32_t id, + const char *interface, uint32_t version, void *data) +{ + + struct app *app = data; + + if (strcmp(interface, "zwp_colorspace_v1") == 0) { + app->colorspace = + display_bind(display, id, + &zwp_colorspace_v1_interface, 1); + } else if (strcmp(interface, "zwp_hdr_metadata_v1") == 0) { + app->hdr_metadata = + display_bind(display, id, + &zwp_hdr_metadata_v1_interface, 1); + } else if (strcmp(interface, "zwp_linux_dmabuf_v1") == 0) { + if (version < 3) + return; + app->dmabuf = + display_bind(display, id, + &zwp_linux_dmabuf_v1_interface, 3); + zwp_linux_dmabuf_v1_add_listener(app->dmabuf, + &dmabuf_listener, + app); + } + +} + +static void +global_handler_remove(struct display *display, uint32_t id, + const char *interface, uint32_t version, void *data) +{ +} + +static int +gbm_init(struct buffer *my_buf) +{ + my_buf->drm_fd = open("/dev/dri/renderD128", O_RDWR); + if (my_buf->drm_fd < 0) + return 0; + + if (!my_buf->gbm) + my_buf->gbm = gbm_create_device(my_buf->drm_fd); + + if (!my_buf->gbm) + return 0; + + return 1; +} + +static void +gbm_fini(struct buffer *my_buf) +{ + gbm_device_destroy(my_buf->gbm); + close(my_buf->drm_fd); +} + +static int +create_dmabuf_buffer(struct app *app, struct buffer *buffer, + int width, int height, int format) +{ + struct zwp_linux_buffer_params_v1 *params; + uint64_t modifier = DRM_FORMAT_MOD_LINEAR; + uint32_t flags = 0; + unsigned buf_w, buf_h; + int pixel_format; + + memset(buffer, 0, sizeof(*buffer)); + if (!gbm_init(buffer)) { + fprintf(stderr, "drm_connect failed\n"); + goto error; + } + + buffer->width = width; + buffer->height = height; + buffer->format = format; + + switch (format) { + case DRM_FORMAT_NV12: + pixel_format = GBM_FORMAT_GR88; + buf_w = width / 2; + buf_h = height * 3 / 2; + buffer->cpp = 1; + break; + case DRM_FORMAT_YUV420: + pixel_format = GBM_FORMAT_GR88; + buf_w = width / 2; + buf_h = height * 2; /* U/V not interleaved */ + buffer->cpp = 1; + break; + case DRM_FORMAT_P010: + pixel_format = GBM_FORMAT_GR88; + buf_w = width; + buf_h = height * 3 / 2; + buffer->cpp = 2; + break; + case DRM_FORMAT_ARGB8888: + pixel_format = GBM_FORMAT_ARGB8888; + buf_w = width; + buf_h = height; + buffer->cpp = 1; + break; + default: + buffer->height = height; + buffer->cpp = 1; + } + + buffer->bo = gbm_bo_create_with_modifiers(buffer->gbm, buf_w, buf_h, pixel_format, &modifier, 1); + if (!buffer->bo) { + fprintf(stderr, "error: unable to allocate bo\n"); + goto error1; + } + + buffer->dmabuf_fd = gbm_bo_get_fd(buffer->bo); + buffer->stride = gbm_bo_get_stride(buffer->bo); + + if (buffer->dmabuf_fd < 0) { + fprintf(stderr, "error: dmabuf_fd < 0\n"); + goto error2; + } + + params = zwp_linux_dmabuf_v1_create_params(app->dmabuf); + zwp_linux_buffer_params_v1_add(params, + buffer->dmabuf_fd, + 0, /* plane_idx */ + 0, /* offset */ + buffer->stride, + modifier >> 32, + modifier & 0xffffffff); + + switch (format) { + case DRM_FORMAT_NV12: + /* add the second plane params */ + zwp_linux_buffer_params_v1_add(params, + buffer->dmabuf_fd, + 1, + buffer->stride * buffer->height, + buffer->stride, + modifier >> 32, + modifier & 0xffffffff); + break; + case DRM_FORMAT_YUV420: + /* add the second plane params */ + zwp_linux_buffer_params_v1_add(params, + buffer->dmabuf_fd, + 1, + buffer->stride * buffer->height, + buffer->stride / 2, + modifier >> 32, + modifier & 0xffffffff); + + /* add the third plane params */ + zwp_linux_buffer_params_v1_add(params, + buffer->dmabuf_fd, + 2, + buffer->stride * buffer->height * 3 / 2, + buffer->stride / 2, + modifier >> 32, + modifier & 0xffffffff); + break; + case DRM_FORMAT_P010: + /* add the second plane params */ + zwp_linux_buffer_params_v1_add(params, + buffer->dmabuf_fd, + 1, + buffer->stride * buffer->height, + buffer->stride, + modifier >> 32, + modifier & 0xffffffff); + break; + default: + break; + } + + buffer->buffer = zwp_linux_buffer_params_v1_create_immed(params, + buffer->width, + buffer->height, + format, + flags); + wl_buffer_add_listener(buffer->buffer, &buffer_listener, buffer); + + return 0; + +error2: + gbm_bo_destroy(buffer->bo); +error1: + gbm_fini(buffer); +error: + return -1; +} + +static struct app * +video_create(struct display *display, const char *filename) +{ + struct app *app; + struct wl_surface *surface; + struct wl_display *wldisplay; + uint32_t i, width, height, format; + int ret; + struct buffer *buffer; + + app = xzalloc(sizeof *app); + + app->display = display; + display_set_user_data(app->display, app); + display_set_global_handler(display, global_handler); + display_set_global_handler_remove(display, global_handler_remove); + + // Ensure that we have received the DMABUF format and modifier support + wldisplay = display_get_display(display); + wl_display_roundtrip(wldisplay); + + app->window = window_create(app->display); + app->widget = window_add_widget(app->window, app); + window_set_title(app->window, "Wayland Simple HDR video"); + + window_set_key_handler(app->window, key_handler); + window_set_user_data(app->window, app); + window_set_keyboard_focus_handler(app->window, keyboard_focus_handler); + + widget_set_redraw_handler(app->widget, redraw_handler); + widget_set_resize_handler(app->widget, resize_handler); + + widget_set_use_cairo(app->widget, 0); + + if (!video_open(app, &app->video, filename)) + goto err; + + surface = window_get_wl_surface(app->window); + zwp_colorspace_v1_set(app->colorspace, + surface, + video_chromacities(&app->video)); + + if (option_subtitle) + app->subtitle = subtitle_create(app); + + width = app->video.codec->width; + height = app->video.codec->height; + format = av_format_to_drm_format(app->video.codec->pix_fmt); + + if (option_fullscreen) { + window_set_fullscreen(app->window, 1); + } else { + /* if not fullscreen, resize as per the video size */ + widget_schedule_resize(app->widget, width, height); + } + + for (i = 0; i < NUM_BUFFERS; i++) { + buffer = &app->video.buffers[i]; + ret = create_dmabuf_buffer(app, buffer, width, height, format); + + if (ret < 0) + goto err; + } + + return app; + +err: + free(app); + return NULL; +} + +static void +video_destroy(struct app *app) +{ + if (app->subtitle) + subtitle_destroy(app->subtitle); + + video_close(&app->video); + + widget_destroy(app->widget); + window_destroy(app->window); + free(app); +} + +int +main(int argc, char *argv[]) +{ + struct display *display; + struct app *app; + + parse_options(options, ARRAY_LENGTH(options), &argc, argv); + if (option_help) { + printf(help_text, argv[0]); + return 0; + } + + display = display_create(&argc, argv); + if (display == NULL) { + fprintf(stderr, "failed to create display: %m\n"); + return -1; + } + + if (!display_has_subcompositor(display)) { + fprintf(stderr, "compositor does not support " + "the subcompositor extension\n"); + return -1; + } + + app = video_create(display, argv[argc - 1]); + if (!app) { + fprintf(stderr, "Failed to initialize!"); + exit(EXIT_FAILURE); + } + + display_run(display); + + video_destroy(app); + display_destroy(display); + + return 0; +} diff --git a/clients/simple-hdr-video.c b/clients/simple-hdr-video.c new file mode 100644 index 0000000000000000000000000000000000000000..bf39cf725d4ce65ea6c9c4160895315d35534f2d --- /dev/null +++ b/clients/simple-hdr-video.c @@ -0,0 +1,1262 @@ +/* + * Copyright © 2019 Harish Krupo + * Copyright © 2019 Intel Corporation + * + * 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, sublicense, + * 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 NONINFRINGEMENT. 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 "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#ifdef HAVE_LIBDRM_INTEL +#include +#include +#endif + +#ifdef HAVE_PANGO +#include +#endif + +#include "colorspace-unstable-v1-client-protocol.h" +#include "hdr-metadata-unstable-v1-client-protocol.h" +#include "linux-dmabuf-unstable-v1-client-protocol.h" + +#include "shared/os-compatibility.h" +#include "shared/helpers.h" +#include "shared/platform.h" +#include "shared/xalloc.h" +#include +#include "window.h" + +#define DBG(fmt, ...) \ + fprintf(stderr, "%d:%s " fmt, __LINE__, __func__, ##__VA_ARGS__) + +#define NUM_BUFFERS 3 + +#ifndef DRM_FORMAT_MOD_LINEAR +#define DRM_FORMAT_MOD_LINEAR 0 +#endif + +#ifndef DRM_FORMAT_P010 +#define DRM_FORMAT_P010 fourcc_code('P', '0', '1', '0') /* 2x2 subsampled Cb:Cr plane 10 bits per channel */ +#endif + +#ifndef DRM_FORMAT_P012 +#define DRM_FORMAT_P012 fourcc_code('P', '0', '1', '2') /* 2x2 subsampled Cr:Cb plane, 12 bit per channel */ +#endif + +#ifndef DRM_FORMAT_P016 +#define DRM_FORMAT_P016 fourcc_code('P', '0', '1', '6') /* 2x2 subsampled Cr:Cb plane, 16 bit per channel */ +#endif + +static int32_t option_help; +static int32_t option_fullscreen; +static int32_t option_subtitle; + +static const struct weston_option options[] = { + { WESTON_OPTION_BOOLEAN, "fullscreen", 'f', &option_fullscreen }, + { WESTON_OPTION_BOOLEAN, "subtitle", 's', &option_subtitle }, + { WESTON_OPTION_BOOLEAN, "help", 'h', &option_help }, +}; + +static const char help_text[] = +"Usage: %s [options] FILENAME\n" +"\n" +" -f, --fullscreen\t\tRun in fullscreen mode\n" +" -s, --subtitle\t\tShow subtiles\n" +" -h, --help\t\tShow this help text\n" +"\n"; + +struct app; +struct buffer; + +struct drm_device { + int fd; + char *name; + + int (*alloc_bo)(struct buffer *buf); + void (*free_bo)(struct buffer *buf); + int (*export_bo_to_prime)(struct buffer *buf); + int (*map_bo)(struct buffer *buf); + void (*unmap_bo)(struct buffer *buf); + void (*device_destroy)(struct buffer *buf); +}; + +struct buffer { + struct wl_buffer *buffer; + int busy; + + struct drm_device *dev; + int drm_fd; + +#ifdef HAVE_LIBDRM_INTEL + drm_intel_bufmgr *bufmgr; + drm_intel_bo *intel_bo; +#endif /* HAVE_LIBDRM_INTEL */ + + uint32_t gem_handle; + int dmabuf_fd; + uint8_t *mmap; + + int width; + int height; + int bpp; + unsigned long stride; + int format; + AVFrame *prev_frame; +}; + +struct subtitle { + + struct wl_surface *wl_surface; + int width; + int height; + + struct widget *widget; + uint32_t time; + struct wl_callback *frame_cb; + struct app *app; + struct buffer buffers[NUM_BUFFERS]; + struct buffer *prev_buffer; +}; + +struct video { + AVFormatContext *fmt_ctx; + AVCodecParserContext *parser; + AVCodecContext *codec; + AVPacket *pkt; + int stream_index; + + struct buffer buffers[NUM_BUFFERS]; + struct buffer *prev_buffer; +}; + +struct app { + struct display *display; + struct window *window; + struct widget *widget; + struct video video; + + struct subtitle *subtitle; + + struct zwp_colorspace_v1 *colorspace; + struct zwp_hdr_metadata_v1 *hdr_metadata; + + struct zwp_hdr_surface_v1 *hdr_surface; + struct zwp_linux_dmabuf_v1 *dmabuf; +}; + +static int +create_dmabuf_buffer(struct app *app, struct buffer *buffer, + int width, int height, int format); + +static void drm_shutdown(struct buffer *my_buf); + +static void +destroy_dmabuf_buffer(struct buffer *buffer) +{ + if (buffer->buffer) { + wl_buffer_destroy(buffer->buffer); + close(buffer->dmabuf_fd); + buffer->dev->free_bo(buffer); + drm_shutdown(buffer); + } +} + +static void +subtitle_resize_handler(struct widget *widget, + int32_t width, int32_t height, void *data) +{ + struct subtitle *sub = data; + struct app *app = sub->app; + struct rectangle allocation; + struct wl_surface *surface; + uint32_t format; + int i; + + widget_get_allocation(sub->widget, &allocation); + + surface = widget_get_wl_surface(widget); + + //clear surface's buffer + wl_surface_attach(surface, NULL, 0, 0); + for (i = 0; i < NUM_BUFFERS; i++) { + destroy_dmabuf_buffer(&sub->buffers[i]); + } + + format = DRM_FORMAT_ARGB8888; + + for (i = 0; i < NUM_BUFFERS; i++) { + create_dmabuf_buffer(app, &sub->buffers[i], + allocation.width, + allocation.height, + format); + + } + +} + +static struct buffer * +subtitle_next_buffer(struct subtitle *sub) +{ + int i; + + for (i = 0; i < NUM_BUFFERS; i++) + if (!sub->buffers[i].busy) + return &sub->buffers[i]; + + return NULL; +} + +#ifdef HAVE_PANGO +static PangoLayout * +create_layout(cairo_t *cr, const char *title) +{ + PangoLayout *layout; + PangoFontDescription *desc; + + layout = pango_cairo_create_layout(cr); + pango_layout_set_text(layout, title, -1); + desc = pango_font_description_from_string("Sans Bold 19"); + pango_layout_set_font_description(layout, desc); + pango_font_description_free(desc); + pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); + pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT); + pango_layout_set_auto_dir (layout, FALSE); + pango_layout_set_single_paragraph_mode (layout, TRUE); + pango_layout_set_width (layout, -1); + + return layout; +} +#endif + +static void +fill_subtitle(struct buffer *buffer) +{ + cairo_surface_t *surface; + cairo_t* cr; + char *title = "Sample subtitle string"; + PangoLayout *title_layout; + + assert(buffer->mmap); + + surface = cairo_image_surface_create_for_data(buffer->mmap, + CAIRO_FORMAT_ARGB32, + buffer->width, + buffer->height, + buffer->stride); + cr = cairo_create(surface); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); +#ifdef HAVE_PANGO + cairo_set_source_rgba(cr, 0, 0, 0, 0); +#else + cairo_set_source_rgba(cr, 1, 1, 1, 1); +#endif + + cairo_paint(cr); + +#ifdef HAVE_PANGO + /* cairo_set_operator(cr, CAIRO_OPERATOR_OVER); */ + title_layout = create_layout(cr, title); + cairo_move_to(cr, 0, 0); + cairo_set_source_rgb(cr, 1, 1, 1); + pango_cairo_show_layout(cr, title_layout); +#endif + + cairo_destroy(cr); + cairo_surface_destroy(surface); +} + +static void +subtitle_redraw_handler(struct widget *widget, void *data) +{ + struct subtitle *sub = data; + struct rectangle allocation; + struct buffer *buffer; + struct wl_surface *surface; + + widget_get_allocation(sub->widget, &allocation); + buffer = subtitle_next_buffer(sub); + + if (!buffer->dev->map_bo(buffer)) { + fprintf(stderr, "map_bo failed\n"); + return; + } + + fill_subtitle(buffer); + + buffer->dev->unmap_bo(buffer); + + surface = widget_get_wl_surface(widget); + wl_surface_attach(surface, buffer->buffer, 0, 0); + wl_surface_damage(surface, 0, 0, allocation.width, allocation.height); + wl_surface_commit(surface); + buffer->busy = 1; + + widget_schedule_redraw(sub->widget); +} + +static struct subtitle * +subtitle_create(struct app *app) +{ + struct subtitle *sub; + + sub = xzalloc(sizeof *sub); + sub->app = app; + + sub->widget = window_add_subsurface(app->window, sub, + SUBSURFACE_SYNCHRONIZED); + + widget_set_use_cairo(sub->widget, 0); + widget_set_resize_handler(sub->widget, subtitle_resize_handler); + widget_set_redraw_handler(sub->widget, subtitle_redraw_handler); + + return sub; +} + +static void +subtitle_destroy(struct subtitle *sub) +{ + int i; + + for (i = 0; i < NUM_BUFFERS; i++) { + destroy_dmabuf_buffer(&sub->buffers[i]); + } + + widget_destroy(sub->widget); + free(sub); +} + +static void +buffer_release(void *data, struct wl_buffer *buffer) +{ + struct buffer *mybuf = data; + mybuf->busy = 0; + if (mybuf->prev_frame) + av_frame_free(&mybuf->prev_frame); + + mybuf->prev_frame = NULL; +} + +static const struct wl_buffer_listener buffer_listener = { + buffer_release +}; + +static uint32_t av_format_to_drm_format(int format) +{ + switch (format) { + case AV_PIX_FMT_YUV420P: + return DRM_FORMAT_YUV420; + case AV_PIX_FMT_YUV420P10BE: + case AV_PIX_FMT_YUV420P10LE: + return DRM_FORMAT_P010; + case AV_PIX_FMT_YUV420P12BE: + case AV_PIX_FMT_YUV420P12LE: + return DRM_FORMAT_P012; + case AV_PIX_FMT_YUV420P16BE: + case AV_PIX_FMT_YUV420P16LE: + return DRM_FORMAT_P016; + default: + return -1; + } +} + +static inline void +ensure_hdr_surface(struct app *app) +{ + struct window *window = app->window; + struct wl_surface *surface; + + assert(app->hdr_metadata); + if (app->hdr_surface) + return; + + surface = window_get_wl_surface(window); + app->hdr_surface = zwp_hdr_metadata_v1_get_hdr_surface(app->hdr_metadata, + surface); +} + +static inline void +destroy_hdr_surface(struct app *app) +{ + if (app->hdr_surface) { + zwp_hdr_surface_v1_destroy(app->hdr_surface); + app->hdr_surface = NULL; + } +} + +static bool +decode(struct video *s, AVFrame *frame) +{ + int r; + + if (s->pkt->size == 0) + return false; + + if (s->pkt->stream_index != s->stream_index) + return false; + + r = avcodec_send_packet(s->codec, s->pkt); + if (r < 0) + return false; + + r = avcodec_receive_frame(s->codec, frame); + if (r < 0) + return false; + + return true; +} + +static AVFrame * +demux_and_decode(struct video *s) +{ + AVFrame *frame; + bool ret; + + frame = av_frame_alloc(); + if (!frame) + return NULL; + + for (;;) { + int r; + + r = av_read_frame(s->fmt_ctx, s->pkt); + if (r < 0) + break; + + ret = decode(s, frame); + + av_packet_unref(s->pkt); + + if (ret) + break; + } + + if (!ret) + av_frame_free(&frame); + + return frame; +} + +static struct buffer * +video_next_buffer(struct video *s) +{ + int i; + + for (i = 0; i < NUM_BUFFERS; i++) + if (!s->buffers[i].busy) + return &s->buffers[i]; + + return NULL; +} + +static void +copy_420p10_to_p010(struct buffer *buffer, AVFrame *frame) +{ + int height, linesize; + uint16_t *yplane, *dsty, *uplane, *vplane, *dstuv; + int size; + + // copy plane 0 + height = buffer->height; + yplane = (uint16_t *) frame->data[0]; + dsty = (uint16_t *) buffer->mmap; + linesize = frame->linesize[0]; + + size = (linesize * height) / 2; + // bitshift and copy y plane + for (int i = 0; i < size; i++) { + dsty[i] = yplane[i] << 6; + } + + // copy plane 1 and 2 alternatingly from source + height = buffer->height / 2; + uplane = (uint16_t *) frame->data[1]; + vplane = (uint16_t *) frame->data[2]; + dstuv = (uint16_t *) (buffer->mmap + buffer->stride * buffer->height); + + // both uplane and vplane would have same linesize + linesize = frame->linesize[1]; + + size = (linesize * height) / 2; + + // bit shift and copy u, v alternatingly + for (int i = 0; i < size; i++) { + dstuv[2 * i] = uplane[i] << 6; + dstuv[(2 * i) + 1] = vplane[i] << 6; + } +} + +static void +fill_buffer(struct buffer *buffer, AVFrame *frame) { + int linesize; + int height; + int plane_heights[4] = {0}; + int offsets[4] = {0}; + int n_planes = 0; + uint8_t *src, *dst; + int i; + assert(buffer->mmap); + + switch (buffer->format) { + case DRM_FORMAT_YUV420: + plane_heights[0] = buffer->height; + plane_heights[1] = buffer->height / 2; + plane_heights[2] = buffer->height / 2; + offsets[0] = 0; + offsets[1] = buffer->height; + offsets[2] = buffer->height * 3 / 2; + n_planes = 3; + break; + case DRM_FORMAT_P010: + copy_420p10_to_p010(buffer, frame); + return; + } + + for (i = 0; i < n_planes; i++) { + height = plane_heights[i]; + linesize = frame->linesize[i]; + + src = frame->data[i]; + dst = buffer->mmap + frame->linesize[0] * offsets[i]; + + for (;height > 0; height--) { + memcpy(dst, src, linesize); + dst += linesize; + src += linesize; + } + } + +} + +static void +redraw_handler(struct widget *widget, void *data) +{ + struct app *app = data; + struct video *video = &app->video; + struct buffer *buffer; + struct wl_buffer *wlbuffer; + struct wl_surface *surface; + AVFrame *frame; + AVFrameSideData *sd; + AVMasteringDisplayMetadata *hdr_metadata; + AVContentLightMetadata *ll_metadata; + int max_cll = -1, max_fall = -1; + wl_fixed_t rx, ry, gx, gy, bx, by, wx, wy, max_luma, min_luma; + + frame = demux_and_decode(&app->video); + if (!frame) { + fprintf(stderr, "no more frames?\n"); + return; + } + + sd = av_frame_get_side_data(frame, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL); + if (sd) { + ll_metadata = (AVContentLightMetadata *)sd->data; + max_cll = ll_metadata->MaxCLL; + max_fall = ll_metadata->MaxFALL; + } + sd = av_frame_get_side_data(frame, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA); + if (sd) { + hdr_metadata = (AVMasteringDisplayMetadata *)sd->data; + if (hdr_metadata->has_luminance && hdr_metadata->has_primaries) { + ensure_hdr_surface(app); + rx = wl_fixed_from_double(av_q2d(hdr_metadata->display_primaries[0][0])); + ry = wl_fixed_from_double(av_q2d(hdr_metadata->display_primaries[0][1])); + gx = wl_fixed_from_double(av_q2d(hdr_metadata->display_primaries[1][0])); + gy = wl_fixed_from_double(av_q2d(hdr_metadata->display_primaries[1][1])); + bx = wl_fixed_from_double(av_q2d(hdr_metadata->display_primaries[2][0])); + by = wl_fixed_from_double(av_q2d(hdr_metadata->display_primaries[2][1])); + wx = wl_fixed_from_double(av_q2d(hdr_metadata->white_point[0])); + wy = wl_fixed_from_double(av_q2d(hdr_metadata->white_point[1])); + max_luma = wl_fixed_from_double(av_q2d(hdr_metadata->max_luminance)); + min_luma = wl_fixed_from_double(av_q2d(hdr_metadata->min_luminance)); + zwp_hdr_surface_v1_set( + app->hdr_surface, + rx, + ry, + gx, + gy, + bx, + by, + wx, + wy, + max_luma, + min_luma, + max_cll == -1 ? 0 : max_cll, + max_fall == -1 ? 0 : max_fall); + + zwp_hdr_surface_v1_set_eotf( + app->hdr_surface, + ZWP_HDR_SURFACE_V1_EOTF_ST_2084_PQ); + } + } else { + // No metadata for this frame. Destroy the created hdr surface. + destroy_hdr_surface(app); + } + + buffer = video_next_buffer(video); + + // If no free buffers available schedule redraw and return; + if(!buffer) { + widget_schedule_redraw(widget); + return; + } + + if (!buffer->dev->map_bo(buffer)) { + fprintf(stderr, "map_bo failed\n"); + return; + } + + fill_buffer(buffer, frame); + + buffer->dev->unmap_bo(buffer); + wlbuffer = buffer->buffer; + + surface = widget_get_wl_surface(widget); + wl_surface_attach(surface, wlbuffer, 0, 0); + wl_surface_damage(surface, 0, 0, frame->width, frame->height); + wl_surface_commit(surface); + widget_schedule_redraw(widget); + buffer->busy = 1; + buffer->prev_frame = frame; +} + +/* + * +---------------------------+ + * | | | + * | | | + * | |vm Video | + * | | | + * | | | + * |___+-------------------+ | + * | hm| Subtitle | | + * | +-------------------+ | + * | | + * +---------------------------+ + * + * hm : horizontal margin + * vm : vertical margin + */ + +static void +resize_handler(struct widget *widget, + int32_t width, int32_t height, void *data) +{ + struct app *app = data; + struct rectangle area; + + // margin is in percentage + int vm = 85, hm = 40; + int x, y, w, h; + int mhorizontal, mvertical; + + if (app->subtitle) { + widget_get_allocation(widget, &area); + + mhorizontal = area.width * hm / 100; + mvertical = area.height * vm / 100; + + x = area.x + mhorizontal; + y = area.y + mvertical; + w = area.width * 2 / 10; // 20% of total width + h = area.height / 20; // 5% of total height + + widget_set_allocation(app->subtitle->widget, + x, y, w, h); + } + +} + +static void +keyboard_focus_handler(struct window *window, + struct input *device, void *data) +{ + struct app *app = data; + + window_schedule_redraw(app->window); +} + +static void +key_handler(struct window *window, struct input *input, uint32_t time, + uint32_t key, uint32_t sym, + enum wl_keyboard_key_state state, void *data) +{ + struct app *app = data; + struct rectangle winrect; + + if (state == WL_KEYBOARD_KEY_STATE_RELEASED) + return; + + switch (sym) { + case XKB_KEY_Up: + window_get_allocation(window, &winrect); + winrect.height -= 100; + if (winrect.height < 150) + winrect.height = 150; + window_schedule_resize(window, winrect.width, winrect.height); + break; + case XKB_KEY_Down: + window_get_allocation(window, &winrect); + winrect.height += 100; + if (winrect.height > 600) + winrect.height = 600; + window_schedule_resize(window, winrect.width, winrect.height); + break; + case XKB_KEY_Escape: + display_exit(app->display); + break; + } +} + +static void video_close(struct video *s) +{ + av_parser_close(s->parser); + avcodec_free_context(&s->codec); + av_packet_free(&s->pkt); +} + +static const enum zwp_colorspace_v1_chromacities chromacities[] = { + [AVCOL_PRI_BT709] = ZWP_COLORSPACE_V1_CHROMACITIES_BT709, + [AVCOL_PRI_BT470M] = ZWP_COLORSPACE_V1_CHROMACITIES_BT470M, + [AVCOL_PRI_BT470BG] = ZWP_COLORSPACE_V1_CHROMACITIES_BT470BG, + [AVCOL_PRI_SMPTE170M] = ZWP_COLORSPACE_V1_CHROMACITIES_SMPTE170M, + [AVCOL_PRI_SMPTE240M] = ZWP_COLORSPACE_V1_CHROMACITIES_SMPTE170M, + [AVCOL_PRI_SMPTE431] = ZWP_COLORSPACE_V1_CHROMACITIES_DCI_P3, + [AVCOL_PRI_SMPTE432] = ZWP_COLORSPACE_V1_CHROMACITIES_DCI_P3, + [AVCOL_PRI_SMPTE428] = ZWP_COLORSPACE_V1_CHROMACITIES_CIEXYZ, + [AVCOL_PRI_BT2020] = ZWP_COLORSPACE_V1_CHROMACITIES_BT2020, +}; + +static enum zwp_colorspace_v1_chromacities +video_chromacities(struct video *s) +{ + if (s->codec->color_primaries >= ARRAY_LENGTH(chromacities)) + return ZWP_COLORSPACE_V1_CHROMACITIES_UNDEFINED; + + return chromacities[s->codec->color_primaries]; +} + +static bool video_open(struct app *app, + struct video *s, + const char *filename) +{ + AVCodec *codec = NULL; + AVStream *stream; + int r; + char buf[4096] = {}; +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100) + av_register_all(); +#endif + + r = avformat_open_input(&s->fmt_ctx, filename, NULL, NULL); + if (r < 0) + return false; + + r = avformat_find_stream_info(s->fmt_ctx, NULL); + if (r < 0) + return false; + + r = av_find_best_stream(s->fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0); + if (r < 0) + return false; + + stream = s->fmt_ctx->streams[r]; + s->stream_index = r; + + s->codec = avcodec_alloc_context3(codec); + if (!s->codec) + return false; + + s->codec->opaque = s; + + r = avcodec_parameters_to_context(s->codec, stream->codecpar); + if (r < 0) + return false; + + r = avcodec_open2(s->codec, codec, NULL); + if (r < 0) + return false; + + s->parser = av_parser_init(codec->id); + if (!s->parser) + return false; + + avcodec_string(buf, sizeof(buf), s->codec, false); + buf[sizeof(buf)-1] = '\0'; + puts(buf); + + s->pkt = av_packet_alloc(); + + return true; +} + +static void +dmabuf_modifiers(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf, + uint32_t format, uint32_t modifier_hi, uint32_t modifier_lo) +{ +} + +static void +dmabuf_format(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf, uint32_t format) +{ + /* XXX: deprecated */ +} + + +static const struct zwp_linux_dmabuf_v1_listener dmabuf_listener = { + dmabuf_format, + dmabuf_modifiers +}; + +static void +global_handler(struct display *display, uint32_t id, + const char *interface, uint32_t version, void *data) +{ + + struct app *app = data; + + if (strcmp(interface, "zwp_colorspace_v1") == 0) { + app->colorspace = + display_bind(display, id, + &zwp_colorspace_v1_interface, 1); + } else if (strcmp(interface, "zwp_hdr_metadata_v1") == 0) { + app->hdr_metadata = + display_bind(display, id, + &zwp_hdr_metadata_v1_interface, 1); + } else if (strcmp(interface, "zwp_linux_dmabuf_v1") == 0) { + if (version < 3) + return; + app->dmabuf = + display_bind(display, id, + &zwp_linux_dmabuf_v1_interface, 3); + zwp_linux_dmabuf_v1_add_listener(app->dmabuf, + &dmabuf_listener, + app); + } + +} + +static void +global_handler_remove(struct display *display, uint32_t id, + const char *interface, uint32_t version, void *data) +{ +} + +#ifdef HAVE_LIBDRM_INTEL +static int +intel_alloc_bo(struct buffer *my_buf) +{ + /* XXX: try different tiling modes for testing FB modifiers. */ + uint32_t tiling = I915_TILING_NONE; + + assert(my_buf->bufmgr); + + my_buf->intel_bo = drm_intel_bo_alloc_tiled(my_buf->bufmgr, "test", + my_buf->width, my_buf->height, + (my_buf->bpp / 8), &tiling, + &my_buf->stride, 0); + + if (!my_buf->intel_bo) + return 0; + + if (tiling != I915_TILING_NONE) + return 0; + + return 1; +} + +static void +intel_free_bo(struct buffer *my_buf) +{ + drm_intel_bo_unreference(my_buf->intel_bo); +} + +static int +intel_map_bo(struct buffer *my_buf) +{ + if (drm_intel_gem_bo_map_gtt(my_buf->intel_bo) != 0) + return 0; + + my_buf->mmap = my_buf->intel_bo->virtual; + + return 1; +} + +static int +intel_bo_export_to_prime(struct buffer *buffer) +{ + return drm_intel_bo_gem_export_to_prime(buffer->intel_bo, &buffer->dmabuf_fd); +} + +static void +intel_unmap_bo(struct buffer *my_buf) +{ + drm_intel_gem_bo_unmap_gtt(my_buf->intel_bo); +} + +static void +intel_device_destroy(struct buffer *my_buf) +{ + drm_intel_bufmgr_destroy(my_buf->bufmgr); +} + +#endif /* HAVE_LIBDRM_INTEL */ + +static void +drm_device_destroy(struct buffer *buf) +{ + buf->dev->device_destroy(buf); + close(buf->drm_fd); +} + +static int +drm_device_init(struct buffer *buf) +{ + struct drm_device *dev = calloc(1, sizeof(struct drm_device)); + + drmVersionPtr version = drmGetVersion(buf->drm_fd); + + dev->fd = buf->drm_fd; + dev->name = strdup(version->name); + if (0) { + /* nothing */ + } +#ifdef HAVE_LIBDRM_INTEL + else if (!strcmp(dev->name, "i915")) { + buf->bufmgr = drm_intel_bufmgr_gem_init(buf->drm_fd, 32); + if (!buf->bufmgr) { + free(dev->name); + free(dev); + return 0; + } + dev->alloc_bo = intel_alloc_bo; + dev->free_bo = intel_free_bo; + dev->export_bo_to_prime = intel_bo_export_to_prime; + dev->map_bo = intel_map_bo; + dev->unmap_bo = intel_unmap_bo; + dev->device_destroy = intel_device_destroy; + } +#endif + else { + fprintf(stderr, "Error: drm device %s unsupported.\n", + dev->name); + free(dev->name); + free(dev); + return 0; + } + buf->dev = dev; + return 1; +} + +static int +drm_connect(struct buffer *my_buf) +{ + /* This won't work with card0 as we need to be authenticated; instead, + * boot with drm.rnodes=1 and use that. */ + my_buf->drm_fd = open("/dev/dri/renderD128", O_RDWR); + if (my_buf->drm_fd < 0) + return 0; + + return drm_device_init(my_buf); +} + +static void +drm_shutdown(struct buffer *my_buf) +{ + drm_device_destroy(my_buf); +} + +static int +create_dmabuf_buffer(struct app *app, struct buffer *buffer, + int width, int height, int format) +{ + struct zwp_linux_buffer_params_v1 *params; + uint64_t modifier = 0; + uint32_t flags = 0; + struct drm_device *drm_dev; + + if (!drm_connect(buffer)) { + fprintf(stderr, "drm_connect failed\n"); + goto error; + } + + drm_dev = buffer->dev; + + buffer->width = width; + switch (format) { + case DRM_FORMAT_NV12: + /* adjust height for allocation of NV12 Y and UV planes */ + buffer->height = height * 3 / 2; + buffer->bpp = 8; + break; + case DRM_FORMAT_YUV420: + buffer->height = height * 2; + buffer->bpp = 8; + break; + case DRM_FORMAT_P010: + buffer->height = height * 3 / 2; + buffer->bpp = 16; + break; + default: + buffer->height = height; + buffer->bpp = 32; + } + buffer->format = format; + + if (!drm_dev->alloc_bo(buffer)) { + fprintf(stderr, "alloc_bo failed\n"); + goto error1; + } + + if (drm_dev->export_bo_to_prime(buffer) != 0) { + fprintf(stderr, "gem_export_to_prime failed\n"); + goto error2; + } + if (buffer->dmabuf_fd < 0) { + fprintf(stderr, "error: dmabuf_fd < 0\n"); + goto error2; + } + + /* We now have a dmabuf! For format XRGB8888, it should contain 2x2 + * tiles (i.e. each tile is 256x256) of misc colours, and be mappable, + * either as ARGB8888, or XRGB8888. For format NV12, it should contain + * the Y and UV components, and needs to be re-adjusted for passing the + * correct height to the compositor. + */ + buffer->height = height; + + params = zwp_linux_dmabuf_v1_create_params(app->dmabuf); + zwp_linux_buffer_params_v1_add(params, + buffer->dmabuf_fd, + 0, /* plane_idx */ + 0, /* offset */ + buffer->stride, + modifier >> 32, + modifier & 0xffffffff); + + switch (format) { + case DRM_FORMAT_NV12: + /* add the second plane params */ + zwp_linux_buffer_params_v1_add(params, + buffer->dmabuf_fd, + 1, + buffer->width * buffer->height, + buffer->stride, + modifier >> 32, + modifier & 0xffffffff); + break; + case DRM_FORMAT_YUV420: + /* add the second plane params */ + zwp_linux_buffer_params_v1_add(params, + buffer->dmabuf_fd, + 1, + buffer->stride * buffer->height, + buffer->stride / 2, + modifier >> 32, + modifier & 0xffffffff); + + /* add the third plane params */ + zwp_linux_buffer_params_v1_add(params, + buffer->dmabuf_fd, + 2, + buffer->stride * buffer->height * 3 / 2, + buffer->stride / 2, + modifier >> 32, + modifier & 0xffffffff); + break; + case DRM_FORMAT_P010: + /* add the second plane params */ + zwp_linux_buffer_params_v1_add(params, + buffer->dmabuf_fd, + 1, + buffer->stride * buffer->height, + buffer->stride, + modifier >> 32, + modifier & 0xffffffff); + break; + default: + break; + } + + buffer->buffer = zwp_linux_buffer_params_v1_create_immed(params, + buffer->width, + buffer->height, + format, + flags); + wl_buffer_add_listener(buffer->buffer, &buffer_listener, buffer); + + return 0; + +error2: + drm_dev->free_bo(buffer); +error1: + drm_shutdown(buffer); +error: + return -1; +} + +static struct app * +video_create(struct display *display, const char *filename) +{ + struct app *app; + struct wl_surface *surface; + struct wl_display *wldisplay; + uint32_t i, width, height, format; + int ret; + struct buffer *buffer; + + app = xzalloc(sizeof *app); + + app->display = display; + display_set_user_data(app->display, app); + display_set_global_handler(display, global_handler); + display_set_global_handler_remove(display, global_handler_remove); + + // Ensure that we have received the DMABUF format and modifier support + wldisplay = display_get_display(display); + wl_display_roundtrip(wldisplay); + + app->window = window_create(app->display); + app->widget = window_add_widget(app->window, app); + window_set_title(app->window, "Wayland Simple HDR video"); + + window_set_key_handler(app->window, key_handler); + window_set_user_data(app->window, app); + window_set_keyboard_focus_handler(app->window, keyboard_focus_handler); + + widget_set_redraw_handler(app->widget, redraw_handler); + widget_set_resize_handler(app->widget, resize_handler); + + widget_set_use_cairo(app->widget, 0); + + if (!video_open(app, &app->video, filename)) + goto err; + + surface = window_get_wl_surface(app->window); + zwp_colorspace_v1_set(app->colorspace, + surface, + video_chromacities(&app->video)); + + if (option_subtitle) + app->subtitle = subtitle_create(app); + + width = app->video.codec->width; + height = app->video.codec->height; + format = av_format_to_drm_format(app->video.codec->pix_fmt); + + if (option_fullscreen) { + window_set_fullscreen(app->window, 1); + } else { + /* if not fullscreen, resize as per the video size */ + widget_schedule_resize(app->widget, width, height); + } + + for (i = 0; i < NUM_BUFFERS; i++) { + buffer = &app->video.buffers[i]; + ret = create_dmabuf_buffer(app, buffer, width, height, format); + + if (ret < 0) + goto err; + } + + return app; + +err: + free(app); + return NULL; +} + +static void +video_destroy(struct app *app) +{ + if (app->subtitle) + subtitle_destroy(app->subtitle); + + video_close(&app->video); + + widget_destroy(app->widget); + window_destroy(app->window); + free(app); +} + +int +main(int argc, char *argv[]) +{ + struct display *display; + struct app *app; + + parse_options(options, ARRAY_LENGTH(options), &argc, argv); + if (option_help) { + printf(help_text, argv[0]); + return 0; + } + + display = display_create(&argc, argv); + if (display == NULL) { + fprintf(stderr, "failed to create display: %m\n"); + return -1; + } + + if (!display_has_subcompositor(display)) { + fprintf(stderr, "compositor does not support " + "the subcompositor extension\n"); + return -1; + } + + app = video_create(display, argv[argc - 1]); + if (!app) { + fprintf(stderr, "Failed to initialize!"); + exit(EXIT_FAILURE); + } + + display_run(display); + + video_destroy(app); + display_destroy(display); + + return 0; +} diff --git a/include/libweston/colorspace.h b/include/libweston/colorspace.h new file mode 100644 index 0000000000000000000000000000000000000000..f02d628b2093a8f85c700f584f23fe8f305603c3 --- /dev/null +++ b/include/libweston/colorspace.h @@ -0,0 +1,79 @@ +/* + * Copyright © 2017 Intel Corporation + * + * 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, sublicense, 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 + * NONINFRINGEMENT. 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. + */ + +#ifndef WESTON_COLORSPACE_H +#define WESTON_COLORSPACE_H + +#include "matrix.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** A CIE 1931 color space*/ +struct cie_xy { + double x; + double y; +}; + +struct color_primaries { + struct cie_xy r; + struct cie_xy g; + struct cie_xy b; + struct cie_xy white_point; +}; + +struct weston_colorspace { + struct color_primaries primaries; + const char *name; + const char *whitepoint_name; +}; + +enum weston_colorspace_enums { + WESTON_CS_BT470M, + WESTON_CS_BT470BG, + WESTON_CS_SMPTE170M, + WESTON_CS_SMPTE240M, + WESTON_CS_BT709, + WESTON_CS_BT2020, + WESTON_CS_SRGB, + WESTON_CS_ADOBERGB, + WESTON_CS_DCI_P3, + WESTON_CS_PROPHOTORGB, + WESTON_CS_CIERGB, + WESTON_CS_CIEXYZ, + WESTON_CS_AP0, + WESTON_CS_AP1, + WESTON_CS_UNDEFINED +}; + +const struct weston_colorspace * +weston_colorspace_lookup(enum weston_colorspace_enums colorspace); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libweston/hdr_metadata_defs.h b/include/libweston/hdr_metadata_defs.h new file mode 100644 index 0000000000000000000000000000000000000000..d9312866291f13f6a24eb3912f626f5e379fe684 --- /dev/null +++ b/include/libweston/hdr_metadata_defs.h @@ -0,0 +1,74 @@ +/* + * Copyright © 2018 Intel Corporation + * + * 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, sublicense, 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 + * NONINFRINGEMENT. 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. + */ + +#ifndef WESTON_HDR_METADATA_DEFS_H +#define WESTON_HDR_METADATA_DEFS_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum hdr_metadata_type { + HDR_METADATA_TYPE1, + HDR_METADATA_TYPE2, +}; + +enum hdr_metadata_eotf { + WESTON_EOTF_TRADITIONAL_GAMMA_SDR, + WESTON_EOTF_TRADITIONAL_GAMMA_HDR, + WESTON_EOTF_ST2084, + WESTON_EOTF_HLG, +}; + +struct weston_hdr_metadata_dynamic { + uint8_t size; + uint8_t *metadata; +}; + +struct weston_hdr_metadata_static { + struct color_primaries primaries; + double max_luminance; + double min_luminance; + uint32_t max_cll; + uint32_t max_fall; + uint8_t eotf; +}; + +struct weston_hdr_metadata { + enum hdr_metadata_type metadata_type; + union { + struct weston_hdr_metadata_static static_metadata; + struct weston_hdr_metadata_dynamic dynamic_metadata; + } metadata; +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 965831878c2435b6b62f197e13ab5620dc3eba18..e3167f267dc4532cb28d72b6f0e320f774a744c5 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -45,6 +45,8 @@ extern "C" { #include #include #include +#include +#include struct weston_geometry { int32_t x, y; @@ -925,6 +927,12 @@ struct weston_renderer { void (*query_dmabuf_modifiers)(struct weston_compositor *ec, int format, uint64_t **modifiers, int *num_modifiers); + + void (*set_output_colorspace)(struct weston_output *output, + uint32_t colorspace); + + void (*set_output_hdr_metadata)(struct weston_output *output, + struct weston_hdr_metadata *metadata); }; enum weston_capability { @@ -1389,6 +1397,9 @@ struct weston_surface_state { /* zwp_surface_synchronization_v1.get_release */ struct weston_buffer_release_reference buffer_release_ref; + struct weston_hdr_metadata *hdr_metadata; + + uint32_t colorspace; }; struct weston_surface_activation_data { @@ -1518,6 +1529,9 @@ struct weston_surface { struct wl_resource *synchronization_resource; int acquire_fence_fd; struct weston_buffer_release_reference buffer_release_ref; + struct wl_resource *hdr_surface_resource; + struct weston_hdr_metadata *hdr_metadata; + uint32_t colorspace; }; struct weston_subsurface { @@ -2388,6 +2402,9 @@ weston_debug_compositor_destroy(struct weston_compositor *compositor); void weston_buffer_send_server_error(struct weston_buffer *buffer, const char *msg); +int weston_hdr_metadata_setup(struct weston_compositor *compositor); +int weston_colorspace_setup(struct weston_compositor *compositor); + #ifdef __cplusplus } diff --git a/include/libweston/matrix.h b/include/libweston/matrix.h index be4d4eb0deef31788aed81a214cbbca00b98f8ae..d83065010db4a3ff75d58b8b8cf1a81a2bb347c4 100644 --- a/include/libweston/matrix.h +++ b/include/libweston/matrix.h @@ -60,6 +60,8 @@ void weston_matrix_rotate_xy(struct weston_matrix *matrix, float cos, float sin); void weston_matrix_transform(struct weston_matrix *matrix, struct weston_vector *v); +void +weston_matrix_diag(struct weston_matrix *matrix, const struct weston_vector *v); int weston_matrix_invert(struct weston_matrix *inverse, diff --git a/include/libweston/meson.build b/include/libweston/meson.build index 8e8f816047e6ae36746d2404ca13c2c21ba17cac..7cf278df02aa71978a0af9059559275c8b3cc0cc 100644 --- a/include/libweston/meson.build +++ b/include/libweston/meson.build @@ -6,6 +6,7 @@ install_headers( 'timeline-object.h', 'windowed-output-api.h', 'zalloc.h', + 'colorspace.h', subdir: dir_include_libweston_install ) diff --git a/libweston/colorspace.c b/libweston/colorspace.c new file mode 100644 index 0000000000000000000000000000000000000000..00d20058e119f3601ee4d46208195c8d72f36aa3 --- /dev/null +++ b/libweston/colorspace.c @@ -0,0 +1,101 @@ +/* + * Copyright © 2017 Intel Corporation + * + * 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, sublicense, 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 + * NONINFRINGEMENT. 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 "config.h" + +#include + +#include + +#include "colorspace-unstable-v1-server-protocol.h" + +static void +colorspace_destroy_request(struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static void +colorspace_set_request(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *surface_resource, + uint32_t chromacities) +{ + static uint32_t colorspace_names[] = { + [ZWP_COLORSPACE_V1_CHROMACITIES_UNDEFINED] = WESTON_CS_UNDEFINED, + [ZWP_COLORSPACE_V1_CHROMACITIES_BT470M] = WESTON_CS_BT470M, + [ZWP_COLORSPACE_V1_CHROMACITIES_BT470BG] = WESTON_CS_BT470BG, + [ZWP_COLORSPACE_V1_CHROMACITIES_SMPTE170M] = WESTON_CS_SMPTE170M, + [ZWP_COLORSPACE_V1_CHROMACITIES_BT709] = WESTON_CS_BT709, + [ZWP_COLORSPACE_V1_CHROMACITIES_BT2020] = WESTON_CS_BT2020, + [ZWP_COLORSPACE_V1_CHROMACITIES_ADOBERGB] = WESTON_CS_ADOBERGB, + [ZWP_COLORSPACE_V1_CHROMACITIES_DCI_P3] = WESTON_CS_DCI_P3, + [ZWP_COLORSPACE_V1_CHROMACITIES_PROPHOTORGB] = WESTON_CS_PROPHOTORGB, + [ZWP_COLORSPACE_V1_CHROMACITIES_CIERGB] = WESTON_CS_CIERGB, + [ZWP_COLORSPACE_V1_CHROMACITIES_AP0] = WESTON_CS_AP0, + [ZWP_COLORSPACE_V1_CHROMACITIES_AP1] = WESTON_CS_AP1, + }; + + struct weston_surface *surface = + wl_resource_get_user_data(surface_resource); + + surface->pending.colorspace = colorspace_names[chromacities]; +} + +static const struct zwp_colorspace_v1_interface +zwp_colorspace_implementation = { + .destroy = colorspace_destroy_request, + .set = colorspace_set_request, +}; + +static void +bind_colorspace(struct wl_client *client, + void *data, uint32_t version, uint32_t id) +{ + struct weston_compositor *compositor = data; + struct wl_resource *resource; + + resource = wl_resource_create(client, &zwp_colorspace_v1_interface, + version, id); + if (resource == NULL) { + wl_client_post_no_memory(client); + return; + } + + wl_resource_set_implementation(resource, &zwp_colorspace_implementation, + compositor, NULL); +} + +WL_EXPORT int +weston_colorspace_setup(struct weston_compositor *compositor) +{ + if (!wl_global_create(compositor->wl_display, + &zwp_colorspace_v1_interface, 1, + compositor, bind_colorspace)) + return -1; + + return 0; +} diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c index 5c1ccdd7c80e6e38c107eb141a385d082ae9b8fd..d7fdc56909936e8d2c6a7c28ac28a311c95e8726 100644 --- a/libweston/compositor-drm.c +++ b/libweston/compositor-drm.c @@ -67,6 +67,7 @@ #include "linux-dmabuf.h" #include "linux-dmabuf-unstable-v1-server-protocol.h" #include "linux-explicit-synchronization.h" +#include "drm-hdr-metadata.h" #ifndef DRM_CLIENT_CAP_ASPECT_RATIO #define DRM_CLIENT_CAP_ASPECT_RATIO 4 @@ -80,6 +81,40 @@ #define GBM_BO_USE_LINEAR (1 << 4) #endif +/* Colorspace values as per CEA spec */ +#define DRM_MODE_COLORIMETRY_DEFAULT 0 + +/* CEA 861 Normal Colorimetry options */ +#define DRM_MODE_COLORIMETRY_NO_DATA 0 +#define DRM_MODE_COLORIMETRY_SMPTE_170M_YCC 1 +#define DRM_MODE_COLORIMETRY_BT709_YCC 2 + +/* CEA 861 Extended Colorimetry Options */ +#define DRM_MODE_COLORIMETRY_XVYCC_601 3 +#define DRM_MODE_COLORIMETRY_XVYCC_709 4 +#define DRM_MODE_COLORIMETRY_SYCC_601 5 +#define DRM_MODE_COLORIMETRY_OPYCC_601 6 +#define DRM_MODE_COLORIMETRY_OPRGB 7 +#define DRM_MODE_COLORIMETRY_BT2020_CYCC 8 +#define DRM_MODE_COLORIMETRY_BT2020_RGB 9 +#define DRM_MODE_COLORIMETRY_BT2020_YCC 10 +/* Additional Colorimetry extension added as part of CTA 861.G */ +#define DRM_MODE_COLORIMETRY_DCI_P3_RGB_D65 11 +#define DRM_MODE_COLORIMETRY_DCI_P3_RGB_THEATER 12 + +/* Colorspace bits */ +#define EDID_CS_BT2020RGB (1 << 7) +#define EDID_CS_BT2020YCC (1 << 6) +#define EDID_CS_BT2020CYCC (1 << 5) +#define EDID_CS_DCIP3 (1 << 15) +#define EDID_CS_HDR_GAMUT_MASK (EDID_CS_BT2020RGB | \ + EDID_CS_BT2020YCC | \ + EDID_CS_BT2020CYCC | \ + EDID_CS_DCIP3) +#define EDID_CS_HDR_CS_BASIC (EDID_CS_BT2020RGB | \ + EDID_CS_DCIP3 | \ + EDID_CS_BT2020YCC) + /** * A small wrapper to print information into the 'drm-backend' debug scope. * @@ -228,6 +263,8 @@ enum wdrm_connector_property { WDRM_CONNECTOR_DPMS, WDRM_CONNECTOR_CRTC_ID, WDRM_CONNECTOR_NON_DESKTOP, + WDRM_CONNECTOR_HDR_METADATA, + WDRM_CONNECTOR_OUTPUT_COLORSPACE, WDRM_CONNECTOR__COUNT }; @@ -254,6 +291,33 @@ static struct drm_property_enum_info dpms_state_enums[] = { }, }; +static struct drm_property_enum_info hdmi_clrspace_enums[] = { + /* For Default case, driver will set the colorspace */ + [DRM_MODE_COLORIMETRY_DEFAULT] = { .name = "Default"}, + /* Standard Definition Colorimetry based on CEA 861 */ + [DRM_MODE_COLORIMETRY_SMPTE_170M_YCC] = { .name = "SMPTE_170M_YCC"}, + [DRM_MODE_COLORIMETRY_BT709_YCC] = { .name = "BT709_YCC"}, + /* Standard Definition Colorimetry based on IEC 61966-2-4 */ + [DRM_MODE_COLORIMETRY_XVYCC_601] = { .name = "XVYCC_601"}, + /* High Definition Colorimetry based on IEC 61966-2-4 */ + [DRM_MODE_COLORIMETRY_XVYCC_709] = { .name = "XVYCC_709"}, + /* Colorimetry based on IEC 61966-2-1/Amendment 1 */ + [DRM_MODE_COLORIMETRY_SYCC_601] = { .name = "SYCC_601"}, + /* Colorimetry based on IEC 61966-2-5 [33] */ + [DRM_MODE_COLORIMETRY_OPYCC_601] = { .name = "opYCC_601"}, + /* Colorimetry based on IEC 61966-2-5 */ + [DRM_MODE_COLORIMETRY_OPRGB] = { .name = "opRGB"}, + /* Colorimetry based on ITU-R BT.2020 */ + [DRM_MODE_COLORIMETRY_BT2020_CYCC] = { .name = "BT2020_CYCC"}, + /* Colorimetry based on ITU-R BT.2020 */ + [DRM_MODE_COLORIMETRY_BT2020_RGB] = { .name = "BT2020_RGB"}, + /* Colorimetry based on ITU-R BT.2020 */ + [DRM_MODE_COLORIMETRY_BT2020_YCC] = { .name = "BT2020_YCC"}, + /* Added as part of Additional Colorimetry Extension in 861.G */ + [DRM_MODE_COLORIMETRY_DCI_P3_RGB_D65] = { .name = "DCI-P3_RGB_D65" }, + [DRM_MODE_COLORIMETRY_DCI_P3_RGB_THEATER] = { .name = "DCI-P3_RGB_Theater"}, +}; + static const struct drm_property_info connector_props[] = { [WDRM_CONNECTOR_EDID] = { .name = "EDID" }, [WDRM_CONNECTOR_DPMS] = { @@ -263,6 +327,12 @@ static const struct drm_property_info connector_props[] = { }, [WDRM_CONNECTOR_CRTC_ID] = { .name = "CRTC_ID", }, [WDRM_CONNECTOR_NON_DESKTOP] = { .name = "non-desktop", }, + [WDRM_CONNECTOR_HDR_METADATA] = { .name = "HDR_OUTPUT_METADATA", }, + [WDRM_CONNECTOR_OUTPUT_COLORSPACE] = { + .name = "Colorspace", + .enum_values = hdmi_clrspace_enums, + .num_enum_values = 13, + }, }; /** @@ -510,6 +580,55 @@ struct drm_plane { } formats[]; }; +/* CTA-861-G: HDR Metadata names and types */ +enum drm_hdr_eotf_type { + DRM_EOTF_SDR_TRADITIONAL = 0, + DRM_EOTF_HDR_TRADITIONAL, + DRM_EOTF_HDR_ST2084, + DRM_EOTF_HLG_BT2100, + DRM_EOTF_MAX +}; + +enum drm_colorspace { + DRM_COLORSPACE_INVALID, + DRM_COLORSPACE_REC709, + DRM_COLORSPACE_DCIP3, + DRM_COLORSPACE_REC2020, + DRM_COLORSPACE_MAX, +}; + +/* Static HDR metadata to be sent to kernel, matches kernel structure */ +struct drm_hdr_metadata_static { + uint8_t eotf; + uint8_t metadata_type; + struct { + uint16_t x, y; + } display_primaries[3]; + struct { + uint16_t x, y; + } white_point; + uint16_t max_display_mastering_luminance; + uint16_t min_display_mastering_luminance; + uint16_t max_cll; + uint16_t max_fall; +}; + +struct hdr_output_metadata { + uint32_t metadata_type; + union { + struct drm_hdr_metadata_static static_md; + }; +}; + +/* Connector's color correction status */ +struct drm_conn_color_state { + bool changed; + uint8_t o_cs; + uint8_t o_eotf; + uint32_t hdr_md_blob_id; + struct drm_hdr_metadata_static o_md; +}; + struct drm_head { struct weston_head base; struct drm_backend *backend; @@ -518,6 +637,15 @@ struct drm_head { uint32_t connector_id; struct drm_edid edid; + /* Display's static HDR metadata */ + struct drm_edid_hdr_metadata_static *hdr_md; + + /* Display's supported color spaces */ + uint32_t clrspaces; + + /* Connector's color correction status */ + struct drm_conn_color_state color_state; + /* Holds the properties for the connector */ struct drm_property_info props_conn[WDRM_CONNECTOR__COUNT]; @@ -575,6 +703,9 @@ struct drm_output { bool virtual; submit_frame_cb virtual_submit_frame; + + /* HDR sesstion is active */ + bool output_is_hdr; }; static const char *const aspect_ratio_as_string[] = { @@ -2091,6 +2222,31 @@ err: return NULL; } +static uint32_t +drm_cs_to_weston_cs(uint32_t drm_cs) { + switch (drm_cs) { + case DRM_COLORSPACE_REC709: + return WESTON_CS_BT709; + case DRM_COLORSPACE_DCIP3: + return WESTON_CS_DCI_P3; + case DRM_COLORSPACE_REC2020: + return WESTON_CS_BT2020; + } + + return WESTON_CS_BT709; +} + +static uint32_t +drm_eotf_to_weston_eotf(uint32_t drm_eotf) { + switch (drm_eotf) { + case DRM_EOTF_HDR_ST2084: + return WESTON_EOTF_ST2084; + case DRM_EOTF_HLG_BT2100: + return WESTON_EOTF_HLG; + } + return WESTON_EOTF_TRADITIONAL_GAMMA_SDR; +} + static struct drm_fb * drm_output_render_gl(struct drm_output_state *state, pixman_region32_t *damage) { @@ -2098,6 +2254,28 @@ drm_output_render_gl(struct drm_output_state *state, pixman_region32_t *damage) struct drm_backend *b = to_drm_backend(output->base.compositor); struct gbm_bo *bo; struct drm_fb *ret; + struct weston_head *w_head = weston_output_get_first_head(&output->base); + struct drm_head *head = to_drm_head(w_head); + struct drm_hdr_metadata_static *dmd = &head->color_state.o_md; + uint32_t target_cs = drm_cs_to_weston_cs(head->color_state.o_cs); + struct weston_hdr_metadata whm = {0}; + struct weston_renderer *renderer = output->base.compositor->renderer; + + // If we have eotf other than SDR gamma, then set HDR properties for the + // renderer + if (dmd->eotf) { + whm.metadata.static_metadata.eotf = + drm_eotf_to_weston_eotf(dmd->eotf); + + whm.metadata.static_metadata.max_luminance = + dmd->max_display_mastering_luminance; + + renderer->set_output_colorspace(&output->base, target_cs); + renderer->set_output_hdr_metadata(&output->base, &whm); + } else { + renderer->set_output_colorspace(&output->base, WESTON_CS_BT709); + renderer->set_output_hdr_metadata(&output->base, NULL); + } output->base.compositor->renderer->repaint_output(&output->base, damage); @@ -2573,6 +2751,62 @@ plane_add_damage(drmModeAtomicReq *req, struct drm_backend *backend, return 0; } +/* Return the colorspace values to be going to AVI infoframe */ +static inline uint32_t +to_kernel_colorspace(uint8_t colorspace) +{ + switch(colorspace) { + case DRM_COLORSPACE_DCIP3: + return DRM_MODE_COLORIMETRY_DCI_P3_RGB_D65; + case DRM_COLORSPACE_REC2020: + return DRM_MODE_COLORIMETRY_BT2020_RGB; + case DRM_COLORSPACE_REC709: + default: + return DRM_MODE_COLORIMETRY_DEFAULT; + } +} + +static int +connector_add_color_correction(drmModeAtomicReq *req, + struct drm_head *head, uint32_t *flags) +{ + int ret; + uint32_t kernel_cs; + struct drm_conn_color_state *conn_state = &head->color_state; + + if (!conn_state->changed) + return 0; + + if ((int)conn_state->hdr_md_blob_id == -1) + return 0; + + ret = connector_add_prop(req, + head, + WDRM_CONNECTOR_HDR_METADATA, + conn_state->hdr_md_blob_id); + if (ret != 0) { + weston_log("Failed to apply output HDR metadata\n"); + return ret; + } + + *flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; + + kernel_cs = to_kernel_colorspace(conn_state->o_cs); + ret = connector_add_prop(req, + head, + WDRM_CONNECTOR_OUTPUT_COLORSPACE, + kernel_cs); + if (ret != 0) { + weston_log("Failed to apply output colorspace\n"); + return ret; + } + + if (!(*flags & DRM_MODE_ATOMIC_TEST_ONLY)) + conn_state->changed = false; + + return 0; +} + static int drm_output_apply_state_atomic(struct drm_output_state *state, drmModeAtomicReq *req, @@ -2608,6 +2842,7 @@ drm_output_apply_state_atomic(struct drm_output_state *state, wl_list_for_each(head, &output->base.head_list, base.output_link) { ret |= connector_add_prop(req, head, WDRM_CONNECTOR_CRTC_ID, output->crtc_id); + ret |= connector_add_color_correction(req, head, flags); } } else { ret |= crtc_add_prop(req, output, WDRM_CRTC_MODE_ID, 0); @@ -3892,6 +4127,186 @@ drm_propose_state_mode_to_string(enum drm_output_propose_state_mode mode) return drm_output_propose_state_mode_as_string[mode]; } +/* Color primaries in HDR metadata are expected to be in power of 0.00002, +* and the maximum value allowed is 1 */ +#define PRIMARY(p) (p > 1 ? 50000 : (p * 50000)) + +/* Whitepoints in HDR metadata are expected to be in power of 0.00010, +* and the maximum value allowed is 1 */ +#define WP_PRIMARY(p) (p > 1 ? 10000 : (p * 10000)) + +static void +drm_prepare_output_hdr_metadata(struct drm_backend *b, + struct drm_head *head, + struct weston_hdr_metadata *surface_md, + struct drm_hdr_metadata_static *out_md) +{ + struct weston_hdr_metadata_static *c_md; + struct drm_edid_hdr_metadata_static *d_md; + + memset(out_md, 0, sizeof(*out_md)); + + c_md = &surface_md->metadata.static_metadata; + d_md = head->hdr_md; + + /* This function gets called only when there is an input HDR surface, + * which means we have to handle only H2S or H2H cases */ + if (d_md) { + out_md->max_cll = c_md->max_cll; + out_md->max_fall = c_md->max_fall; + out_md->max_display_mastering_luminance = c_md->max_luminance; + out_md->min_display_mastering_luminance = c_md->min_luminance; + out_md->white_point.x = WP_PRIMARY(c_md->primaries.white_point.x); + out_md->white_point.y = WP_PRIMARY(c_md->primaries.white_point.y); + out_md->display_primaries[0].x = PRIMARY(c_md->primaries.r.x); + out_md->display_primaries[0].y = PRIMARY(c_md->primaries.r.y); + out_md->display_primaries[1].x = PRIMARY(c_md->primaries.g.x); + out_md->display_primaries[1].y = PRIMARY(c_md->primaries.g.y); + out_md->display_primaries[2].x = PRIMARY(c_md->primaries.b.x); + out_md->display_primaries[0].y = PRIMARY(c_md->primaries.b.y); + out_md->eotf = DRM_EOTF_HDR_ST2084; + out_md->metadata_type = 1; + } +} + +static int +drm_head_prepare_hdr_metadata_blob(struct drm_backend *b, + struct weston_surface *hdr_surf, + struct drm_head *drm_head, + struct drm_conn_color_state *target) +{ + int ret; + uint32_t blob_id = 0; + struct hdr_output_metadata output_metadata = {0,}; + + /* Prepare and setup tone mapping metadata as output metadata */ + drm_prepare_output_hdr_metadata(b, + drm_head, + hdr_surf->hdr_metadata, + &target->o_md); + + memcpy(&output_metadata.static_md, &target->o_md, sizeof (target->o_md)); + + /* create blob to be set during next commit */ + ret = drmModeCreatePropertyBlob(b->drm.fd, + (const void *)&output_metadata, + sizeof(output_metadata), + &blob_id); + if (ret || !blob_id) { + drm_debug(b, "\t\t\t[view] Set HDR blob failed\n"); + memset(&target->o_md, 0, sizeof(target->o_md)); + return -1; + } + + return blob_id; +} + +static int +drm_head_prepare_color_state(struct drm_backend *b, + struct weston_output *output_base, + struct weston_surface *hdr_surf) +{ + int i; + uint32_t blob_id = 0; + struct drm_output *output = to_drm_output(output_base); + struct drm_edid_hdr_metadata_static *display_md; + struct weston_head *w_head = weston_output_get_first_head(&output->base); + struct drm_head *drm_head = to_drm_head(w_head); + struct drm_conn_color_state *target; + enum drm_colorspace target_cs = DRM_COLORSPACE_REC709; + uint8_t target_eotf = DRM_EOTF_SDR_TRADITIONAL; + uint16_t display_cs = drm_head->clrspaces & EDID_CS_HDR_CS_BASIC; + + if (!drm_head) + return 0; + + /* This is an active HDR session, so the state is already set */ + if (output->output_is_hdr) + return 0; + + /* Check if setting output HDR metadata is supported */ + for (i = 0; i < WDRM_CONNECTOR__COUNT; i++) { + struct drm_property_info *info = &drm_head->props_conn[i]; + + if (info && !strcmp(info->name, "HDR_OUTPUT_METADATA")) + goto found_hdr; + } + + /* Can't find HDR metadata property */ + weston_log("Warning: No support for HDR metadata property\n"); + return -1; + +found_hdr: + /* Check if output colorspace setting is supported */ + for (i = 0; i < WDRM_CONNECTOR__COUNT; i++) { + struct drm_property_info *info = &drm_head->props_conn[i]; + + if (info && !strcmp(info->name, "Colorspace")) + goto found_cs; + } + + /* Can't find colorspace property */ + weston_log("Warning: No support for output colorspace property\n"); + return -1; + +found_cs: + display_md = drm_head->hdr_md; + target = &drm_head->color_state; + + if (display_md && display_cs) { + /* Display is HDR and supports basic HDR wide gamuts */ + target_eotf = DRM_EOTF_HDR_ST2084; + if (display_cs & EDID_CS_BT2020RGB) + target_cs = DRM_COLORSPACE_REC2020; + else + target_cs = DRM_COLORSPACE_DCIP3; + } + + blob_id = drm_head_prepare_hdr_metadata_blob(b, hdr_surf, drm_head, target); + if (blob_id <= 0) { + drm_debug(b, "\t\t\t[view] failed to setup output hdr metadata\n"); + return -1; + } + + /* TODO: Setup output gamma here */ + target->o_eotf = target_eotf; + target->o_cs = target_cs; + target->changed = true; + target->hdr_md_blob_id = blob_id; + return 0; +} + + +static inline struct weston_surface * +drm_get_first_hdr_surface(struct weston_output *output_base) +{ + struct weston_view *ev; + struct weston_surface *hdr_surface = NULL; + /* Check if we are dealing with HDR view */ + wl_list_for_each(ev, &output_base->compositor->view_list, link) { + hdr_surface = ev->surface; + if (hdr_surface && hdr_surface->hdr_metadata) + return hdr_surface; + } + return NULL; +} + +static void +drm_head_reset_color_state(struct drm_backend *b, + struct weston_output *output_base) +{ + struct weston_head *w_head = weston_output_get_first_head(output_base); + struct drm_head *head = to_drm_head(w_head); + struct drm_conn_color_state *cs; + + if (!head) + return; + + cs = &head->color_state; + memset(cs, 0, sizeof(*cs)); + cs->changed = 1; +} + static void drm_assign_planes(struct weston_output *output_base, void *repaint_data) { @@ -3903,11 +4318,32 @@ drm_assign_planes(struct weston_output *output_base, void *repaint_data) struct weston_view *ev; struct weston_plane *primary = &output_base->compositor->primary_plane; enum drm_output_propose_state_mode mode = DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY; + struct weston_surface *hdr_surface = NULL; + int ret; drm_debug(b, "\t[repaint] preparing state for output %s (%lu)\n", output_base->name, (unsigned long) output_base->id); - if (!b->sprites_are_broken && !output->virtual) { + hdr_surface = drm_get_first_hdr_surface(output_base); + if (hdr_surface) { + + /* This is a HDR view, handle output HDR metadata */ + ret = drm_head_prepare_color_state(b, output_base, hdr_surface); + if (ret) + weston_log("Failed to create HDR target\n"); + output->output_is_hdr = true; + } else { + /* If this is first SDR view after a HDR view, do following: + * Reset the output HDR metadata + * Reset the output colorspace + */ + if (output->output_is_hdr) { + drm_head_reset_color_state(b, output_base); + output->output_is_hdr = false; + } + } + + if (!b->sprites_are_broken && !output->virtual && !hdr_surface) { drm_debug(b, "\t[repaint] trying planes-only build state\n"); state = drm_output_propose_state(output_base, pending_state, mode); if (!state) { @@ -5421,6 +5857,13 @@ find_and_parse_output_edid(struct drm_head *head, if (head->edid.serial_number[0] != '\0') *serial_number = head->edid.serial_number; } + + if (head->hdr_md) + drm_release_hdr_metadata(head->hdr_md); + head->hdr_md = drm_get_display_hdr_metadata(edid_blob->data, + edid_blob->length); + head->clrspaces = drm_get_display_clrspace(edid_blob->data, + edid_blob->length); drmModeFreePropertyBlob(edid_blob); } @@ -5509,6 +5952,7 @@ drm_output_attach_head(struct weston_output *output_base, struct weston_head *head_base) { struct drm_backend *b = to_drm_backend(output_base->compositor); + struct drm_head *head = NULL; if (wl_list_length(&output_base->head_list) >= MAX_CLONED_CONNECTORS) return -1; @@ -5516,6 +5960,16 @@ drm_output_attach_head(struct weston_output *output_base, if (!output_base->enabled) return 0; + /* If we already have a head attached to this output and if the head has + * HDR capabilities, then don't attach any other head */ + + if (wl_list_length(&output_base->head_list) == 1) { + head = to_drm_head(container_of(output_base->head_list.next, + struct weston_head, output_link)); + if (head->hdr_md) + return -1; + } + /* XXX: ensure the configuration will work. * This is actually impossible without major infrastructure * work. */ @@ -6565,14 +7019,22 @@ err_alloc: static void drm_head_destroy(struct drm_head *head) { + struct drm_backend *b = head->backend; + weston_head_release(&head->base); drm_property_info_free(head->props_conn, WDRM_CONNECTOR__COUNT); drmModeFreeConnector(head->connector); + if (head->hdr_md) + drm_release_hdr_metadata(head->hdr_md); + if (head->backlight) backlight_destroy(head->backlight); + if (head->color_state.hdr_md_blob_id) + drmModeDestroyPropertyBlob(b->drm.fd, head->color_state.hdr_md_blob_id); + free(head); } @@ -7618,6 +8080,14 @@ drm_backend_create(struct weston_compositor *compositor, " synchronization support failed.\n"); } + if (weston_hdr_metadata_setup(compositor) < 0) + weston_log("Error: initializing hdr metadata " + "support failed.\n"); + + if (weston_colorspace_setup(compositor) < 0) + weston_log("Error: initializing colorspace " + "support failed.\n"); + ret = weston_plugin_api_register(compositor, WESTON_DRM_OUTPUT_API_NAME, &api, sizeof(api)); diff --git a/libweston/compositor.c b/libweston/compositor.c index 6a7c4cedce0cbf3cfc8589f78874e5434dcfcaf1..6c3acb584193c70faca50fec8d78eb4d455f707e 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -452,8 +452,8 @@ weston_surface_state_init(struct weston_surface_state *state) state->buffer_viewport.buffer.src_width = wl_fixed_from_int(-1); state->buffer_viewport.surface.width = -1; state->buffer_viewport.changed = 0; - state->acquire_fence_fd = -1; + state->colorspace = WESTON_CS_BT709; } static void @@ -531,6 +531,7 @@ weston_surface_create(struct weston_compositor *compositor) weston_matrix_init(&surface->buffer_to_surface_matrix); weston_matrix_init(&surface->surface_to_buffer_matrix); + surface->colorspace = WESTON_CS_BT709; wl_list_init(&surface->pointer_constraints); @@ -3372,6 +3373,21 @@ weston_surface_commit_state(struct weston_surface *surface, &state->feedback_list); wl_list_init(&state->feedback_list); + //Apply colorspace and HDR metadata state + if (surface->pending.hdr_metadata) { + if (!surface->hdr_metadata) + surface->hdr_metadata = + zalloc(sizeof(struct weston_hdr_metadata)); + memcpy(surface->hdr_metadata, + surface->pending.hdr_metadata, + sizeof(struct weston_hdr_metadata)); + } else if (surface->hdr_metadata) { + free(surface->hdr_metadata); + surface->hdr_metadata = NULL; + } + + surface->colorspace = state->colorspace; + wl_signal_emit(&surface->commit_signal, surface); } diff --git a/libweston/drm-hdr-metadata.c b/libweston/drm-hdr-metadata.c new file mode 100644 index 0000000000000000000000000000000000000000..0dea2a58e3f9bc295c075b07b94485453a65f9b0 --- /dev/null +++ b/libweston/drm-hdr-metadata.c @@ -0,0 +1,200 @@ +/* + * Copyright © 2019 Intel Corporation + * + * 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, sublicense, 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 + * NONINFRINGEMENT. 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 +#include +#include "drm-hdr-metadata.h" +#include "shared/helpers.h" + +#define EDID_BLOCK_LENGTH 128 +#define EDID_CEA_EXT_ID 0x02 +#define EDID_CEA_TAG_EXTENDED 0x7 + +/* CEA-861-G new EDID blocks for HDR */ +#define EDID_CEA_TAG_COLORIMETRY 0x5 +#define EDID_CEA_EXT_TAG_STATIC_METADATA 0x6 +#define EDID_CEA_EXT_TAG_DYNAMIC_METADATA 0x7 + +static const uint8_t * +edid_find_cea_extension_block(const uint8_t *edid) +{ + uint8_t ext_blks; + int blk; + const uint8_t *ext = NULL; + + if (!edid) { + weston_log("No EDID\n"); + return NULL; + } + + ext_blks = edid[126]; + if (!ext_blks) { + weston_log("EDID doesn't have any extension block\n"); + return NULL; + } + + for (blk = 0; blk < ext_blks; blk++) { + ext = edid + EDID_BLOCK_LENGTH * (blk + 1); + if (ext[0] == EDID_CEA_EXT_ID) + break; + } + + if (blk == ext_blks) + return NULL; + + return ext; +} + +static const uint8_t * +edid_find_extended_data_block(const uint8_t *edid, + uint8_t *data_len, + uint32_t block_tag) +{ + uint8_t d; + uint8_t tag; + uint8_t extended_tag; + uint8_t dblen; + + const uint8_t *dbptr; + const uint8_t *cea_db_start; + const uint8_t *cea_db_end; + const uint8_t *cea_ext_blk; + + if (!edid) { + weston_log("No EDID in blob\n"); + return NULL; + } + + cea_ext_blk = edid_find_cea_extension_block(edid); + if (!cea_ext_blk) { + weston_log("No CEA extension block available\n"); + return NULL; + } + + /* CEA DB starts at blk[4] and ends at blk[d] */ + d = cea_ext_blk[2]; + cea_db_start = cea_ext_blk + 4; + cea_db_end = cea_ext_blk + d - 1; + + for (dbptr = cea_db_start; dbptr < cea_db_end; dbptr += (dblen + 1)) { + + /* First data byte contains db length and tag */ + dblen = dbptr[0] & 0x1F; + tag = dbptr[0] >> 5; + + /* Metadata bock is extended tag block */ + if (tag != EDID_CEA_TAG_EXTENDED) + continue; + + /* Extended block uses one extra byte for extended tag */ + extended_tag = dbptr[1]; + if (extended_tag != block_tag) + continue; + + *data_len = dblen - 1; + return dbptr + 2; + } + + return NULL; +} + +static struct drm_edid_hdr_metadata_static * +drm_get_hdr_static_metadata(const uint8_t *hdr_db, uint32_t data_len) +{ + struct drm_edid_hdr_metadata_static *s; + + if (data_len < 2) { + weston_log("Invalid metadata input to static parser\n"); + return NULL; + } + + s = zalloc(sizeof (struct drm_edid_hdr_metadata_static)); + if (!s) { + weston_log("OOM while parsing static metadata\n"); + return NULL; + } + + memset(s, 0, sizeof(struct drm_edid_hdr_metadata_static)); + + s->eotf = hdr_db[0] & 0x3F; + s->metadata_type = hdr_db[1]; + + if (data_len > 2 && data_len < 6) { + s->desired_max_ll = hdr_db[2]; + s->desired_max_fall = hdr_db[3]; + s->desired_min_ll = hdr_db[4]; + + if (!s->desired_max_ll) + s->desired_max_ll = 0xFF; + } + return s; +} + +uint16_t +drm_get_display_clrspace(const uint8_t *edid, uint32_t edid_len) +{ + uint8_t data_len = 0; + const uint8_t *clr_db; + uint16_t clrspaces = 0; + + clr_db = edid_find_extended_data_block(edid, &data_len, + EDID_CEA_TAG_COLORIMETRY); + if (clr_db && data_len != 0) + /* db[4] bit 7 is DCI-P3 support information (added in CTA-861-G) */ + clrspaces = ((!!(clr_db[1] & 0x80)) << 8) | (clr_db[0]); + + return clrspaces; +} + +struct drm_edid_hdr_metadata_static * +drm_get_display_hdr_metadata(const uint8_t *edid, uint32_t edid_len) +{ + uint8_t data_len = 0; + const uint8_t *hdr_db; + struct drm_edid_hdr_metadata_static *md = NULL; + + if (!edid) { + weston_log("Invalid EDID\n"); + return NULL; + } + + hdr_db = edid_find_extended_data_block(edid, &data_len, + EDID_CEA_EXT_TAG_STATIC_METADATA); + if (hdr_db && data_len != 0) { + md = drm_get_hdr_static_metadata(hdr_db, data_len); + if (!md) { + weston_log("Can't find static HDR metadata in EDID\n"); + return NULL; + } + weston_log("Monitor supports HDR\n"); + } + + return md; +} + +void +drm_release_hdr_metadata(struct drm_edid_hdr_metadata_static *md) +{ + free(md); +} diff --git a/libweston/drm-hdr-metadata.h b/libweston/drm-hdr-metadata.h new file mode 100644 index 0000000000000000000000000000000000000000..f5d7ac440de2f1834821701c588b15ccf3af669b --- /dev/null +++ b/libweston/drm-hdr-metadata.h @@ -0,0 +1,49 @@ +/* + * Copyright © 2019 Intel Corporation + * + * 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, sublicense, 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 + * NONINFRINGEMENT. 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. + */ + +#ifndef WESTON_DRM_HDR_METADATA_H +#define WESTON_DRM_HDR_METADATA_H + +#include + +/* Monitor's HDR metadata */ +struct drm_edid_hdr_metadata_static { + uint8_t eotf; + uint8_t metadata_type; + uint8_t desired_max_ll; + uint8_t desired_max_fall; + uint8_t desired_min_ll; +}; + +void +drm_release_hdr_metadata(struct drm_edid_hdr_metadata_static *md); + +struct drm_edid_hdr_metadata_static * +drm_get_display_hdr_metadata(const uint8_t *edid, uint32_t edid_len); + +uint16_t +drm_get_display_clrspace(const uint8_t *edid, uint32_t edid_len); + +#endif diff --git a/libweston/hdr_metadata.c b/libweston/hdr_metadata.c new file mode 100644 index 0000000000000000000000000000000000000000..f3bd39afcf21be38deae94009407f02820e1e481 --- /dev/null +++ b/libweston/hdr_metadata.c @@ -0,0 +1,204 @@ +/* + * Copyright © 2018 Intel Corporation + * + * 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, sublicense, 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 + * NONINFRINGEMENT. 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 "config.h" + +#include + +#include +#include +#include "hdr-metadata-unstable-v1-server-protocol.h" + +#define STATIC_METADATA(x) data->metadata.static_metadata.x + +static void +hdr_surface_set_metadata(struct wl_client *client, + struct wl_resource *surface_resource, + wl_fixed_t display_primary_r_x, + wl_fixed_t display_primary_r_y, + wl_fixed_t display_primary_g_x, + wl_fixed_t display_primary_g_y, + wl_fixed_t display_primary_b_x, + wl_fixed_t display_primary_b_y, + wl_fixed_t white_point_x, + wl_fixed_t white_point_y, + wl_fixed_t max_luminance, + wl_fixed_t min_luminance, + uint32_t max_cll, + uint32_t max_fall) +{ + struct weston_surface *surface = + wl_resource_get_user_data(surface_resource); + + struct weston_hdr_metadata *data = surface->pending.hdr_metadata; + data->metadata_type = HDR_METADATA_TYPE1; + STATIC_METADATA(primaries.r.x) = wl_fixed_to_double(display_primary_r_x); + STATIC_METADATA(primaries.r.y) = wl_fixed_to_double(display_primary_r_y); + STATIC_METADATA(primaries.g.x) = wl_fixed_to_double(display_primary_g_x); + STATIC_METADATA(primaries.g.y) = wl_fixed_to_double(display_primary_g_y); + STATIC_METADATA(primaries.b.x) = wl_fixed_to_double(display_primary_b_x); + STATIC_METADATA(primaries.b.y) = wl_fixed_to_double(display_primary_b_y); + STATIC_METADATA(primaries.white_point.x) = wl_fixed_to_double(white_point_x); + STATIC_METADATA(primaries.white_point.y) = wl_fixed_to_double(white_point_y); + STATIC_METADATA(max_luminance) = wl_fixed_to_double(max_luminance); + STATIC_METADATA(min_luminance) = wl_fixed_to_double(min_luminance); + STATIC_METADATA(max_cll) = max_cll; + STATIC_METADATA(max_fall) = max_fall; +} + +static void +hdr_surface_set_eotf(struct wl_client *client, + struct wl_resource *surface_resource, + uint32_t eotf) +{ + enum hdr_metadata_eotf internal_eotf = WESTON_EOTF_TRADITIONAL_GAMMA_SDR; + struct weston_surface *surface = + wl_resource_get_user_data(surface_resource); + + struct weston_hdr_metadata *data = surface->pending.hdr_metadata; + + + switch (eotf) { + case ZWP_HDR_SURFACE_V1_EOTF_ST_2084_PQ: + internal_eotf = WESTON_EOTF_ST2084; + break; + case ZWP_HDR_SURFACE_V1_EOTF_HLG: + internal_eotf = WESTON_EOTF_HLG; + break; + } + + data->metadata_type = HDR_METADATA_TYPE1; + STATIC_METADATA(eotf) = internal_eotf; +} + +static void +hdr_surface_destroy(struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static const struct zwp_hdr_surface_v1_interface +zwp_hdr_surface_implementation = { + .destroy = hdr_surface_destroy, + .set = hdr_surface_set_metadata, + .set_eotf = hdr_surface_set_eotf, +}; + + +static void +hdr_metadata_destroy_request(struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static void +destroy_hdr_surface(struct wl_resource *resource) +{ + struct weston_surface *surface = + wl_resource_get_user_data(resource); + + if (!surface) + return; + + surface->hdr_surface_resource = NULL; + if (surface->pending.hdr_metadata) + free(surface->pending.hdr_metadata); + surface->pending.hdr_metadata = NULL; +} + +static void +hdr_metadata_get_hdr_surface(struct wl_client *client, + struct wl_resource *hdr_metadata, + uint32_t id, + struct wl_resource *surface_resource) +{ + int version = wl_resource_get_version(hdr_metadata); + struct weston_surface *surface = + wl_resource_get_user_data(surface_resource); + + struct wl_resource *resource; + + if (surface->hdr_surface_resource) { + wl_resource_post_error(hdr_metadata, + ZWP_HDR_METADATA_V1_ERROR_HDR_SURFACE_EXISTS, + "a hdr surface for that surface already exists"); + return; + } + + resource = wl_resource_create(client, &zwp_hdr_surface_v1_interface, + version, id); + if (resource == NULL) { + wl_client_post_no_memory(client); + return; + } + + wl_resource_set_implementation(resource, &zwp_hdr_surface_implementation, + surface, destroy_hdr_surface); + + surface->hdr_surface_resource = resource; + surface->pending.hdr_metadata = + zalloc(sizeof(struct weston_hdr_metadata)); + + if (!surface->pending.hdr_metadata) { + wl_client_post_no_memory(client); + return; + } +} + +static const struct zwp_hdr_metadata_v1_interface +zwp_hdr_metadata_implementation = { + .destroy = hdr_metadata_destroy_request, + .get_hdr_surface = hdr_metadata_get_hdr_surface, +}; + +static void +bind_hdr_metadata(struct wl_client *client, + void *data, uint32_t version, uint32_t id) +{ + struct wl_resource *resource; + resource = wl_resource_create(client, &zwp_hdr_metadata_v1_interface, + version, id); + if (resource == NULL) { + wl_client_post_no_memory(client); + return; + } + + wl_resource_set_implementation(resource, + &zwp_hdr_metadata_implementation, + NULL, NULL); +} + +WL_EXPORT int +weston_hdr_metadata_setup(struct weston_compositor *compositor) +{ + if (!wl_global_create(compositor->wl_display, + &zwp_hdr_metadata_v1_interface, 1, + compositor, bind_hdr_metadata)) + return -1; + + return 0; +} diff --git a/libweston/meson.build b/libweston/meson.build index 3c9640887854d2e0dc8e3b53f4542a056b26f173..2514a31f7b4def7fc2d39891edf5813702249341 100644 --- a/libweston/meson.build +++ b/libweston/meson.build @@ -27,7 +27,11 @@ srcs_libweston = [ 'touch-calibration.c', 'weston-debug.c', 'zoom.c', + 'hdr_metadata.c', + 'colorspace.c', '../shared/matrix.c', + '../shared/colorspace.c', + '../shared/csc.c', linux_dmabuf_unstable_v1_protocol_c, linux_dmabuf_unstable_v1_server_protocol_h, linux_explicit_synchronization_unstable_v1_protocol_c, @@ -54,6 +58,10 @@ srcs_libweston = [ viewporter_server_protocol_h, weston_debug_protocol_c, weston_debug_server_protocol_h, + hdr_metadata_unstable_v1_protocol_c, + hdr_metadata_unstable_v1_server_protocol_h, + colorspace_unstable_v1_protocol_c, + colorspace_unstable_v1_server_protocol_h, ] if get_option('renderer-gl') @@ -175,6 +183,7 @@ if get_option('backend-drm') 'libbacklight.c', 'libinput-device.c', 'libinput-seat.c', + 'drm-hdr-metadata.c', linux_dmabuf_unstable_v1_protocol_c, linux_dmabuf_unstable_v1_server_protocol_h, presentation_time_server_protocol_h, diff --git a/libweston/noop-renderer.c b/libweston/noop-renderer.c index aef93002c74212ac2e91d4bd9cbfdac3c85b614d..716cbda486fd8139c8e78f4bae545981bebfb095 100644 --- a/libweston/noop-renderer.c +++ b/libweston/noop-renderer.c @@ -116,6 +116,9 @@ noop_renderer_init(struct weston_compositor *ec) renderer->attach = noop_renderer_attach; renderer->surface_set_color = noop_renderer_surface_set_color; renderer->destroy = noop_renderer_destroy; + renderer->set_output_colorspace = NULL; + renderer->set_output_hdr_metadata = NULL; + ec->renderer = renderer; return 0; diff --git a/libweston/pixel-formats.c b/libweston/pixel-formats.c index cade24226eca321c890bca2fe776a0a6fb90db43..5b60ce08db2c5258b0d7508c79dbb28537913d6b 100644 --- a/libweston/pixel-formats.c +++ b/libweston/pixel-formats.c @@ -52,6 +52,10 @@ #define SAMPLER_TYPE(type) .sampler_type = 0 #endif +#ifndef DRM_FORMAT_P010 +#define DRM_FORMAT_P010 fourcc_code('P', '0', '1', '0') /* 2x2 subsampled Cb:Cr plane 10 bits per channel */ +#endif + #define DRM_FORMAT(f) .format = DRM_FORMAT_ ## f, .drm_format_name = #f #include "weston-egl-ext.h" @@ -348,6 +352,14 @@ static const struct pixel_format_info pixel_format_table[] = { .hsub = 2, .vsub = 2, }, + { + DRM_FORMAT(P010), + SAMPLER_TYPE(EGL_TEXTURE_Y_UV_WL), + .num_planes = 2, + .chroma_order = ORDER_UV, + .hsub = 2, + .vsub = 2, + }, { DRM_FORMAT(YUV422), SAMPLER_TYPE(EGL_TEXTURE_Y_U_V_WL), diff --git a/libweston/pixman-renderer.c b/libweston/pixman-renderer.c index 8677023cf1fb5c238e474766c84e4a505802be02..ecca4df8b3468a2dc3ebbe1e6f86acc2da4eff8c 100644 --- a/libweston/pixman-renderer.c +++ b/libweston/pixman-renderer.c @@ -881,6 +881,8 @@ pixman_renderer_init(struct weston_compositor *ec) pixman_renderer_surface_get_content_size; renderer->base.surface_copy_content = pixman_renderer_surface_copy_content; + renderer->base.set_output_colorspace = NULL; + renderer->base.set_output_hdr_metadata = NULL; ec->renderer = &renderer->base; ec->capabilities |= WESTON_CAP_ROTATION_ANY; ec->capabilities |= WESTON_CAP_CAPTURE_YFLIP; diff --git a/libweston/renderer-gl/gl-renderer-private.h b/libweston/renderer-gl/gl-renderer-private.h new file mode 100644 index 0000000000000000000000000000000000000000..a0864e5b0f9ea694b688f0aecfc47b5090549e04 --- /dev/null +++ b/libweston/renderer-gl/gl-renderer-private.h @@ -0,0 +1,114 @@ +/* + * Copyright © 2019 Harish Krupo + * Copyright © 2019 Intel Corporation + * + * 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, sublicense, 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 + * NONINFRINGEMENT. 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. + */ + +#ifndef GL_RENDERER_PRIVATE_H +#define GL_RENDERER_PRIVATE_H + +#include +#include +#include +#include +#include + +enum gl_shader_texture_variant { + SHADER_VARIANT_NONE = 0, + SHADER_VARIANT_RGBX, + SHADER_VARIANT_RGBA, + SHADER_VARIANT_Y_U_V, + SHADER_VARIANT_Y_UV, + SHADER_VARIANT_Y_XUXV, + SHADER_VARIANT_SOLID, + SHADER_VARIANT_EXTERNAL, +}; + +enum gl_shader_degamma_variant { + SHADER_DEGAMMA_NONE = 0, + SHADER_DEGAMMA_LINEAR, + SHADER_DEGAMMA_SRGB, + SHADER_DEGAMMA_PQ, + SHADER_DEGAMMA_HLG, +}; + +enum gl_shader_gamma_variant { + SHADER_GAMMA_NONE = 0, + SHADER_GAMMA_LINEAR, + SHADER_GAMMA_SRGB, + SHADER_GAMMA_PQ, + SHADER_GAMMA_HLG, +}; + +enum gl_shader_tone_map_variant { + SHADER_TONE_MAP_NONE = 0, + SHADER_TONE_MAP_HDR_TO_SDR, + SHADER_TONE_MAP_SDR_TO_HDR, + SHADER_TONE_MAP_HDR_TO_HDR, +}; + +struct gl_shader_requirements +{ + enum gl_shader_texture_variant variant; + bool debug; + bool csc_matrix; + enum gl_shader_degamma_variant degamma; + enum gl_shader_gamma_variant nl_variant; + enum gl_shader_gamma_variant gamma; + enum gl_shader_tone_map_variant tone_mapping; +}; + +struct gl_shader { + struct gl_shader_requirements key; + GLuint program; + GLuint vertex_shader, fragment_shader; + GLint proj_uniform; + GLint tex_uniforms[3]; + GLint alpha_uniform; + GLint color_uniform; + GLint csc_uniform; + GLint display_max_luminance; + GLint content_max_luminance; + GLint content_min_luminance; + struct wl_list link; /* gl_renderer::shader_list */ +}; + +struct gl_shader_generator; + +void +gl_shader_requirements_init(struct gl_shader_requirements *requirements); + +void +gl_shader_destroy(struct gl_shader *shader); + +struct gl_shader * +gl_shader_create(struct gl_shader_generator *sg, + struct gl_shader_requirements *requirements); + +struct gl_shader_generator * +gl_shader_generator_create(struct weston_compositor *compositor); + +void +gl_shader_generator_destroy(struct gl_shader_generator *sg); + +#endif diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index d160a5d8847583f49cde0220b07ef0ffdd1d14ed..951416d693ea50e157c851f1f10bcb73211a68d1 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -57,6 +57,8 @@ #include "shared/platform.h" #include "shared/timespec-util.h" #include "weston-egl-ext.h" +#include "gl-renderer-private.h" +#include "shared/csc.h" #define GR_GL_VERSION(major, minor) \ (((uint32_t)(major) << 16) | (uint32_t)(minor)) @@ -64,16 +66,6 @@ #define GR_GL_VERSION_INVALID \ GR_GL_VERSION(0, 0) -struct gl_shader { - GLuint program; - GLuint vertex_shader, fragment_shader; - GLint proj_uniform; - GLint tex_uniforms[3]; - GLint alpha_uniform; - GLint color_uniform; - const char *vertex_source, *fragment_source; -}; - #define BUFFER_DAMAGE_COUNT 2 enum gl_border_status { @@ -107,6 +99,11 @@ struct gl_output_state { /* struct timeline_render_point::link */ struct wl_list timeline_render_point_list; + GLuint shadow_fbo; + GLuint shadow_tex; + enum weston_colorspace_enums target_colorspace; + struct weston_hdr_metadata *target_hdr_metadata; + bool hdr_state_changed; }; enum buffer_type { @@ -138,7 +135,7 @@ struct dmabuf_image { enum import_type import_type; GLenum target; - struct gl_shader *shader; + enum gl_shader_texture_variant shader_variant; }; struct yuv_plane_descriptor { @@ -158,7 +155,6 @@ struct yuv_format_descriptor { struct gl_surface_state { GLfloat color[4]; - struct gl_shader *shader; GLuint textures[3]; int num_textures; @@ -195,6 +191,7 @@ struct gl_surface_state { struct wl_listener surface_destroy_listener; struct wl_listener renderer_destroy_listener; + struct gl_shader_requirements shader_requirements; }; struct gl_renderer { @@ -243,14 +240,6 @@ struct gl_renderer { int has_gl_texture_rg; - struct gl_shader texture_shader_rgba; - struct gl_shader texture_shader_rgbx; - struct gl_shader texture_shader_egl_external; - struct gl_shader texture_shader_y_uv; - struct gl_shader texture_shader_y_u_v; - struct gl_shader texture_shader_y_xuxv; - struct gl_shader invert_color_shader; - struct gl_shader solid_shader; struct gl_shader *current_shader; struct wl_signal destroy_signal; @@ -268,6 +257,15 @@ struct gl_renderer { int has_wait_sync; PFNEGLWAITSYNCKHRPROC wait_sync; + + /** struct gl_shader::link + * + * List constains cached shaders built from struct gl_shader_requirements + */ + struct wl_list shader_list; + + bool supports_half_float_texture; + struct gl_shader_generator *sg; }; enum timeline_render_point_type { @@ -286,6 +284,10 @@ struct timeline_render_point { static PFNEGLGETPLATFORMDISPLAYEXTPROC get_platform_display = NULL; +static void +use_gl_program(struct gl_renderer *gr, + const struct gl_shader_requirements *requirements); + static inline const char * dump_format(uint32_t format, char out[4]) { @@ -732,6 +734,11 @@ triangle_fan_debug(struct weston_view *view, int first, int count) GLushort *index; int nelems; static int color_idx = 0; + struct gl_shader *prev_shader = NULL; + struct gl_shader_requirements shader_requirements; + + prev_shader = gr->current_shader; + static const GLfloat color[][4] = { { 1.0, 0.0, 0.0, 1.0 }, { 0.0, 1.0, 0.0, 1.0 }, @@ -754,10 +761,14 @@ triangle_fan_debug(struct weston_view *view, int first, int count) *index++ = first + i; } - glUseProgram(gr->solid_shader.program); - glUniform4fv(gr->solid_shader.color_uniform, 1, + gl_shader_requirements_init(&shader_requirements); + shader_requirements.variant = SHADER_VARIANT_SOLID; + use_gl_program(gr, &shader_requirements); + glUniform4fv(gr->current_shader->color_uniform, 1, color[color_idx++ % ARRAY_LENGTH(color)]); glDrawElements(GL_LINES, nelems, GL_UNSIGNED_SHORT, buffer); + + gr->current_shader = prev_shader; glUseProgram(gr->current_shader->program); free(buffer); } @@ -830,26 +841,38 @@ use_output(struct weston_output *output) return 0; } -static int -shader_init(struct gl_shader *shader, struct gl_renderer *gr, - const char *vertex_source, const char *fragment_source); - static void -use_shader(struct gl_renderer *gr, struct gl_shader *shader) +use_gl_program(struct gl_renderer *gr, + const struct gl_shader_requirements *requirements) { - if (!shader->program) { - int ret; + struct gl_shader *iterator, *shader = NULL; + struct gl_shader_requirements reqs; - ret = shader_init(shader, gr, - shader->vertex_source, - shader->fragment_source); + memcpy(&reqs, requirements, sizeof(struct gl_shader_requirements)); + if (gr->fragment_shader_debug) + reqs.debug = true; - if (ret < 0) - weston_log("warning: failed to compile shader\n"); + wl_list_for_each(iterator, &gr->shader_list, link) { + if (!memcmp(&reqs, &iterator->key, + sizeof(struct gl_shader_requirements))) { + shader = iterator; + break; + } + } + + if (!shader) { + shader = gl_shader_create(gr->sg, &reqs); + if (!shader) { + weston_log("warning: failed to generate gl program\n"); + return; + } + + wl_list_insert(&gr->shader_list, &shader->link); } if (gr->current_shader == shader) return; + glUseProgram(shader->program); gr->current_shader = shader; } @@ -862,6 +885,20 @@ shader_uniforms(struct gl_shader *shader, int i; struct gl_surface_state *gs = get_surface_state(view->surface); struct gl_output_state *go = get_output_state(output); + struct weston_surface *surface = view->surface; + struct weston_hdr_metadata *src_md = surface->hdr_metadata; + struct weston_hdr_metadata *dst_md = go->target_hdr_metadata; + struct weston_hdr_metadata_static *static_metadata; + const struct weston_colorspace *src_cs, *dst_cs; + struct weston_matrix csc_matrix; + float csc[9] = {0}; + float *dst; + uint32_t display_max_luminance; + uint32_t content_max_luminance; + uint32_t content_min_luminance; + + // shader key contains the set of requirements used to build the shader + struct gl_shader_requirements *requirements = &shader->key; glUniformMatrix4fv(shader->proj_uniform, 1, GL_FALSE, go->output_matrix.d); @@ -870,6 +907,41 @@ shader_uniforms(struct gl_shader *shader, for (i = 0; i < gs->num_textures; i++) glUniform1i(shader->tex_uniforms[i], i); + + if (requirements->csc_matrix) { + weston_matrix_init(&csc_matrix); + src_cs = weston_colorspace_lookup(surface->colorspace); + dst_cs = weston_colorspace_lookup(go->target_colorspace); + + weston_csc_matrix(&csc_matrix, dst_cs, src_cs, 1.0); + dst = csc_matrix.d; + for (i = 0; i < 3; i++) { + memcpy(csc + 3 * i, dst, 3 * sizeof(float)); + dst += 4; + } + glUniformMatrix3fv(shader->csc_uniform, 1, GL_FALSE, csc); + } + + switch(requirements->tone_mapping) { + case SHADER_TONE_MAP_HDR_TO_HDR: + static_metadata = &src_md->metadata.static_metadata; + content_max_luminance = static_metadata->max_luminance; + content_min_luminance = static_metadata->min_luminance; + glUniform1f(shader->content_max_luminance, + content_max_luminance); + glUniform1f(shader->content_min_luminance, + content_min_luminance); + /* fallthrough */ + case SHADER_TONE_MAP_SDR_TO_HDR: + static_metadata = &dst_md->metadata.static_metadata; + display_max_luminance = static_metadata->max_luminance; + glUniform1f(shader->display_max_luminance, + display_max_luminance); + break; + default: + glUniform1f(shader->display_max_luminance, 1.0); + break; + } } static int @@ -938,6 +1010,85 @@ ensure_surface_buffer_is_ready(struct gl_renderer *gr, return (wait_ret == EGL_TRUE && destroy_ret == EGL_TRUE) ? 0 : -1; } +static void +compute_hdr_requirements_from_view(struct weston_view *ev, + struct weston_output *output) +{ + struct weston_surface *surface = ev->surface; + struct gl_renderer *gr = get_renderer(surface->compositor); + struct gl_surface_state *gs = get_surface_state(ev->surface); + struct gl_output_state *go = get_output_state(output); + struct weston_hdr_metadata *src_md = surface->hdr_metadata; + struct weston_hdr_metadata *dst_md = go->target_hdr_metadata; + uint32_t target_colorspace = go->target_colorspace; + bool needs_csc = false, needs_tm = false; + uint32_t degamma = 0, gamma = 0; + enum gl_shader_tone_map_variant tone_map_type = SHADER_TONE_MAP_NONE; + + /* Start by assuming that we don't need color space conversion */ + /* and tone mapping. This resets the csc and tone mapping requirements */ + /* when the hdr requirements for the surface are removed. */ + gs->shader_requirements.csc_matrix = false; + gs->shader_requirements.tone_mapping = SHADER_TONE_MAP_NONE; + + needs_csc = surface->colorspace != target_colorspace; + needs_tm = src_md || dst_md; + + if (needs_csc) + gs->shader_requirements.csc_matrix = true; + + /* identify degamma curve from input metadata */ + degamma = SHADER_DEGAMMA_SRGB; + + if (src_md) { + switch (src_md->metadata.static_metadata.eotf) { + case WESTON_EOTF_ST2084: + degamma = SHADER_DEGAMMA_PQ; + break; + case WESTON_EOTF_HLG: + degamma = SHADER_DEGAMMA_HLG; + break; + } + } + + gs->shader_requirements.degamma = degamma; + + if (needs_tm) { + if (dst_md) { + if (src_md) + tone_map_type = SHADER_TONE_MAP_HDR_TO_HDR; + else + tone_map_type = SHADER_TONE_MAP_SDR_TO_HDR; + } else { + if (src_md) + tone_map_type = SHADER_TONE_MAP_HDR_TO_SDR; + else + tone_map_type = SHADER_TONE_MAP_NONE; + } + } + + gs->shader_requirements.tone_mapping = tone_map_type; + + // If RGBA16F textures aren't supported, then we are forced to go for + // non linear blending + if (!gr->supports_half_float_texture) { + gamma = SHADER_GAMMA_SRGB; + if (dst_md) { + switch (dst_md->metadata.static_metadata.eotf) { + case WESTON_EOTF_ST2084: + gamma = SHADER_GAMMA_PQ; + break; + case WESTON_EOTF_HLG: + gamma = SHADER_GAMMA_HLG; + break; + } + } + + gs->shader_requirements.nl_variant = gamma; + gs->shader_requirements.gamma = gamma; + } +} + static void draw_view(struct weston_view *ev, struct weston_output *output, pixman_region32_t *damage) /* in global coordinates */ @@ -953,13 +1104,16 @@ draw_view(struct weston_view *ev, struct weston_output *output, pixman_region32_t surface_blend; GLint filter; int i; + struct gl_shader_requirements shader_requirements; /* In case of a runtime switch of renderers, we may not have received * an attach for this surface since the switch. In that case we don't * have a valid buffer or a proper shader set up so skip rendering. */ - if (!gs->shader) + if (!gs->shader_requirements.variant) return; + compute_hdr_requirements_from_view(ev, output); + pixman_region32_init(&repaint); pixman_region32_intersect(&repaint, &ev->transform.boundingbox, damage); @@ -973,13 +1127,19 @@ draw_view(struct weston_view *ev, struct weston_output *output, glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + /* The built shader objects are cached in struct + * gL_renderer::shader_list and retrieved when requested with the same + * struct gl_shader_requirements. The SOILD shader is generated here so + * that the shader uniforms are cached with used later */ if (gr->fan_debug) { - use_shader(gr, &gr->solid_shader); - shader_uniforms(&gr->solid_shader, ev, output); + gl_shader_requirements_init(&shader_requirements); + shader_requirements.variant = SHADER_VARIANT_SOLID; + use_gl_program(gr, &shader_requirements); + shader_uniforms(gr->current_shader, ev, output); } - use_shader(gr, gs->shader); - shader_uniforms(gs->shader, ev, output); + use_gl_program(gr, &gs->shader_requirements); + shader_uniforms(gr->current_shader, ev, output); if (ev->transform.enabled || output->zoom.active || output->current_scale != ev->surface->buffer_viewport.buffer.scale) @@ -1013,14 +1173,17 @@ draw_view(struct weston_view *ev, struct weston_output *output, pixman_region32_copy(&surface_opaque, &ev->surface->opaque); if (pixman_region32_not_empty(&surface_opaque)) { - if (gs->shader == &gr->texture_shader_rgba) { + if (gs->shader_requirements.variant == SHADER_VARIANT_RGBA) { /* Special case for RGBA textures with possibly * bad data in alpha channel: use the shader * that forces texture alpha = 1.0. * Xwayland surfaces need this. */ - use_shader(gr, &gr->texture_shader_rgbx); - shader_uniforms(&gr->texture_shader_rgbx, ev, output); + memcpy(&shader_requirements, &gs->shader_requirements, + sizeof(struct gl_shader_requirements)); + shader_requirements.variant = SHADER_VARIANT_RGBX; + use_gl_program(gr, &shader_requirements); + shader_uniforms(gr->current_shader, ev, output); } if (ev->alpha < 1.0) @@ -1033,7 +1196,7 @@ draw_view(struct weston_view *ev, struct weston_output *output, } if (pixman_region32_not_empty(&surface_blend)) { - use_shader(gr, gs->shader); + use_gl_program(gr, &gs->shader_requirements); glEnable(GL_BLEND); repaint_region(ev, &repaint, &surface_blend); gs->used_in_output_repaint = true; @@ -1209,10 +1372,13 @@ draw_output_borders(struct weston_output *output, { struct gl_output_state *go = get_output_state(output); struct gl_renderer *gr = get_renderer(output->compositor); - struct gl_shader *shader = &gr->texture_shader_rgba; + uint32_t target_colorspace = go->target_colorspace; + struct weston_hdr_metadata *dst_md = go->target_hdr_metadata; struct gl_border_image *top, *bottom, *left, *right; struct weston_matrix matrix; int full_width, full_height; + struct gl_shader_requirements shader_requirements; + int gamma = 0; if (border_status == BORDER_STATUS_CLEAN) return; /* Clean. Nothing to do. */ @@ -1226,17 +1392,47 @@ draw_output_borders(struct weston_output *output, full_height = output->current_mode->height + top->height + bottom->height; glDisable(GL_BLEND); - use_shader(gr, shader); + gl_shader_requirements_init(&shader_requirements); + shader_requirements.variant = SHADER_VARIANT_RGBA; + + // assuming that the borders are always BT709 + if (target_colorspace != WESTON_CS_BT709) + shader_requirements.csc_matrix = true; + else + shader_requirements.csc_matrix = false; + + shader_requirements.degamma = SHADER_DEGAMMA_SRGB; + + gamma = SHADER_GAMMA_SRGB; + if (dst_md) { + switch (dst_md->metadata.static_metadata.eotf) { + case WESTON_EOTF_ST2084: + gamma = SHADER_GAMMA_PQ; + break; + case WESTON_EOTF_HLG: + gamma = SHADER_GAMMA_HLG; + break; + } + } + shader_requirements.gamma = gamma; + + if (dst_md) + shader_requirements.tone_mapping = SHADER_TONE_MAP_SDR_TO_HDR; + + use_gl_program(gr, &shader_requirements); glViewport(0, 0, full_width, full_height); weston_matrix_init(&matrix); weston_matrix_translate(&matrix, -full_width/2.0, -full_height/2.0, 0); weston_matrix_scale(&matrix, 2.0/full_width, -2.0/full_height, 1); - glUniformMatrix4fv(shader->proj_uniform, 1, GL_FALSE, matrix.d); + glUniformMatrix4fv(gr->current_shader->proj_uniform, 1, + GL_FALSE, matrix.d); + + glUniform1i(gr->current_shader->tex_uniforms[0], 0); + glUniform1f(gr->current_shader->alpha_uniform, 1); + glUniform1f(gr->current_shader->display_max_luminance, 1.0); - glUniform1i(shader->tex_uniforms[0], 0); - glUniform1f(shader->alpha_uniform, 1); glActiveTexture(GL_TEXTURE0); if (border_status & BORDER_TOP_DIRTY) @@ -1352,6 +1548,89 @@ output_rotate_damage(struct weston_output *output, go->border_damage[go->buffer_damage_index] = border_status; } +static void +repaint_from_texture(struct weston_output *output, + pixman_region32_t *output_damage) +{ + struct gl_output_state *go = get_output_state(output); + struct gl_renderer *gr = get_renderer(output->compositor); + double width = output->current_mode->width; + double height = output->current_mode->height; + struct weston_hdr_metadata *dst_md = go->target_hdr_metadata; + pixman_box32_t *rects; + int n_rects; + int i, gamma = 0; + struct gl_shader_requirements shader_requirements; + + GLfloat verts[4 * 2] = { 0.0f }; + + static const GLfloat proj[16] = { /* transpose */ + 2.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 2.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + -1.0f, -1.0f, 0.0f, 1.0f + }; + + /* Bind default framebuffer */ + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glViewport(go->borders[GL_RENDERER_BORDER_LEFT].width, + go->borders[GL_RENDERER_BORDER_BOTTOM].height, + output->current_mode->width, + output->current_mode->height); + + gamma = SHADER_GAMMA_SRGB; + if (dst_md) { + switch (dst_md->metadata.static_metadata.eotf) { + case WESTON_EOTF_ST2084: + gamma = SHADER_GAMMA_PQ; + break; + case WESTON_EOTF_HLG: + gamma = SHADER_GAMMA_HLG; + break; + } + } + + gl_shader_requirements_init(&shader_requirements); + shader_requirements.variant = SHADER_VARIANT_RGBA; + shader_requirements.nl_variant = gamma; + shader_requirements.gamma = gamma; + use_gl_program(gr, &shader_requirements); + + glUniformMatrix4fv(gr->current_shader->proj_uniform, 1, GL_FALSE, proj); + glUniform1f(gr->current_shader->alpha_uniform, 1.0f); + + glUniform1i(gr->current_shader->tex_uniforms[0], 0); + glUniform1f(gr->current_shader->display_max_luminance, 1.0); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, go->shadow_tex); + + rects = pixman_region32_rectangles(output_damage, &n_rects); + for (i = 0; i < n_rects; i++) { + + verts[0] = rects[i].x1 / width; + verts[1] = (height - rects[i].y1) / height; + verts[2] = rects[i].x2 / width; + verts[3] = (height - rects[i].y1) / height; + + verts[4] = rects[i].x2 / width; + verts[5] = (height - rects[i].y2) / height; + verts[6] = rects[i].x1 / width; + verts[7] = (height - rects[i].y2) / height; + + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, verts); + glEnableVertexAttribArray(0); + + /* texcoord: */ + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, verts); + glEnableVertexAttribArray(1); + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + + glBindTexture(GL_TEXTURE_2D, 0); +} + /* NOTE: We now allow falling back to ARGB gl visuals when XRGB is * unavailable, so we're assuming the background has no transparency * and that everything with a blend, like drop shadows, will have something @@ -1375,10 +1654,16 @@ gl_renderer_repaint_output(struct weston_output *output, pixman_region32_t buffer_damage, total_damage; enum gl_border_status border_damage = BORDER_STATUS_CLEAN; struct weston_view *view; + pixman_region32_t full_damage; + pixman_region32_t *repaint_damage, *repaint_texture_damage; if (use_output(output) < 0) return; + pixman_region32_init_rect(&full_damage, 0, 0, + output->current_mode->width, + output->current_mode->height); + /* Clear the used_in_output_repaint flag, so that we can properly track * which surfaces were used in this output repaint. */ wl_list_for_each_reverse(view, &compositor->view_list, link) { @@ -1396,12 +1681,6 @@ gl_renderer_repaint_output(struct weston_output *output, go->begin_render_sync = create_render_sync(gr); - /* Calculate the viewport */ - glViewport(go->borders[GL_RENDERER_BORDER_LEFT].width, - go->borders[GL_RENDERER_BORDER_BOTTOM].height, - output->current_mode->width, - output->current_mode->height); - /* Calculate the global GL matrix */ go->output_matrix = output->matrix; weston_matrix_translate(&go->output_matrix, @@ -1411,6 +1690,12 @@ gl_renderer_repaint_output(struct weston_output *output, 2.0 / output->current_mode->width, -2.0 / output->current_mode->height, 1); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glViewport(go->borders[GL_RENDERER_BORDER_LEFT].width, + go->borders[GL_RENDERER_BORDER_BOTTOM].height, + output->current_mode->width, + output->current_mode->height); + /* if debugging, redraw everything outside the damage to clean up * debug lines from the previous draw on this buffer: */ @@ -1425,22 +1710,57 @@ gl_renderer_repaint_output(struct weston_output *output, pixman_region32_fini(&undamaged); } + if (gr->supports_half_float_texture) { + glBindFramebuffer(GL_FRAMEBUFFER, go->shadow_fbo); + glViewport(0, 0, + output->current_mode->width, + output->current_mode->height); + + } + pixman_region32_init(&total_damage); pixman_region32_init(&buffer_damage); output_get_damage(output, &buffer_damage, &border_damage); - output_rotate_damage(output, output_damage, go->border_status); + + if (go->hdr_state_changed) { + output_rotate_damage(output, &full_damage, go->border_status); + } else { + output_rotate_damage(output, output_damage, go->border_status); + } pixman_region32_union(&total_damage, &buffer_damage, output_damage); border_damage |= go->border_status; - repaint_views(output, &total_damage); + + if (gr->supports_half_float_texture) { + if (go->hdr_state_changed) { + repaint_damage = &full_damage; + repaint_texture_damage = &full_damage; + } else { + repaint_damage = output_damage; + repaint_texture_damage = &total_damage; + } + + glBindFramebuffer(GL_FRAMEBUFFER, go->shadow_fbo); + repaint_views(output, repaint_damage); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + repaint_from_texture(output, repaint_texture_damage); + } else { + if (go->hdr_state_changed) { + repaint_damage = &full_damage; + } else { + repaint_damage = &total_damage; + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + repaint_views(output, repaint_damage); + } pixman_region32_fini(&total_damage); pixman_region32_fini(&buffer_damage); draw_output_borders(output, border_damage); - pixman_region32_copy(&output->previous_damage, output_damage); wl_signal_emit(&output->frame_signal, output); @@ -1502,6 +1822,9 @@ gl_renderer_repaint_output(struct weston_output *output, TIMELINE_RENDER_POINT_TYPE_END); update_buffer_release_fences(compositor, output); + + go->hdr_state_changed = false; + pixman_region32_fini(&full_damage); } static int @@ -1706,28 +2029,28 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer, switch (wl_shm_buffer_get_format(shm_buffer)) { case WL_SHM_FORMAT_XRGB8888: - gs->shader = &gr->texture_shader_rgbx; + gs->shader_requirements.variant = SHADER_VARIANT_RGBX; pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; gl_format[0] = GL_BGRA_EXT; gl_pixel_type = GL_UNSIGNED_BYTE; es->is_opaque = true; break; case WL_SHM_FORMAT_ARGB8888: - gs->shader = &gr->texture_shader_rgba; + gs->shader_requirements.variant = SHADER_VARIANT_RGBA; pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; gl_format[0] = GL_BGRA_EXT; gl_pixel_type = GL_UNSIGNED_BYTE; es->is_opaque = false; break; case WL_SHM_FORMAT_RGB565: - gs->shader = &gr->texture_shader_rgbx; + gs->shader_requirements.variant = SHADER_VARIANT_RGBX; pitch = wl_shm_buffer_get_stride(shm_buffer) / 2; gl_format[0] = GL_RGB; gl_pixel_type = GL_UNSIGNED_SHORT_5_6_5; es->is_opaque = true; break; case WL_SHM_FORMAT_YUV420: - gs->shader = &gr->texture_shader_y_u_v; + gs->shader_requirements.variant = SHADER_VARIANT_Y_U_V; pitch = wl_shm_buffer_get_stride(shm_buffer); gl_pixel_type = GL_UNSIGNED_BYTE; num_planes = 3; @@ -1759,18 +2082,18 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer, gs->hsub[1] = 2; gs->vsub[1] = 2; if (gr->has_gl_texture_rg) { - gs->shader = &gr->texture_shader_y_uv; + gs->shader_requirements.variant = SHADER_VARIANT_Y_UV; gl_format[0] = GL_R8_EXT; gl_format[1] = GL_RG8_EXT; } else { - gs->shader = &gr->texture_shader_y_xuxv; + gs->shader_requirements.variant = SHADER_VARIANT_Y_XUXV; gl_format[0] = GL_LUMINANCE; gl_format[1] = GL_LUMINANCE_ALPHA; } es->is_opaque = true; break; case WL_SHM_FORMAT_YUYV: - gs->shader = &gr->texture_shader_y_xuxv; + gs->shader_requirements.variant = SHADER_VARIANT_Y_XUXV; pitch = wl_shm_buffer_get_stride(shm_buffer) / 2; gl_pixel_type = GL_UNSIGNED_BYTE; num_planes = 2; @@ -1849,26 +2172,26 @@ gl_renderer_attach_egl(struct weston_surface *es, struct weston_buffer *buffer, case EGL_TEXTURE_RGBA: default: num_planes = 1; - gs->shader = &gr->texture_shader_rgba; + gs->shader_requirements.variant = SHADER_VARIANT_RGBA; break; case EGL_TEXTURE_EXTERNAL_WL: num_planes = 1; gs->target = GL_TEXTURE_EXTERNAL_OES; - gs->shader = &gr->texture_shader_egl_external; + gs->shader_requirements.variant = SHADER_VARIANT_EXTERNAL; break; case EGL_TEXTURE_Y_UV_WL: num_planes = 2; - gs->shader = &gr->texture_shader_y_uv; + gs->shader_requirements.variant = SHADER_VARIANT_Y_UV; es->is_opaque = true; break; case EGL_TEXTURE_Y_U_V_WL: num_planes = 3; - gs->shader = &gr->texture_shader_y_u_v; + gs->shader_requirements.variant = SHADER_VARIANT_Y_U_V; es->is_opaque = true; break; case EGL_TEXTURE_Y_XUXV_WL: num_planes = 2; - gs->shader = &gr->texture_shader_y_xuxv; + gs->shader_requirements.variant = SHADER_VARIANT_Y_XUXV; es->is_opaque = true; break; } @@ -2177,13 +2500,13 @@ import_yuv_dmabuf(struct gl_renderer *gr, switch (format->texture_type) { case EGL_TEXTURE_Y_XUXV_WL: - image->shader = &gr->texture_shader_y_xuxv; + image->shader_variant = SHADER_VARIANT_Y_XUXV; break; case EGL_TEXTURE_Y_UV_WL: - image->shader = &gr->texture_shader_y_uv; + image->shader_variant = SHADER_VARIANT_Y_UV; break; case EGL_TEXTURE_Y_U_V_WL: - image->shader = &gr->texture_shader_y_u_v; + image->shader_variant = SHADER_VARIANT_Y_U_V; break; default: assert(false); @@ -2229,10 +2552,10 @@ import_dmabuf(struct gl_renderer *gr, switch (image->target) { case GL_TEXTURE_2D: - image->shader = &gr->texture_shader_rgba; + image->shader_variant = SHADER_VARIANT_RGBA; break; default: - image->shader = &gr->texture_shader_egl_external; + image->shader_variant = SHADER_VARIANT_EXTERNAL; } } else { if (!import_yuv_dmabuf(gr, image)) { @@ -2465,7 +2788,7 @@ gl_renderer_attach_dmabuf(struct weston_surface *surface, gr->image_target_texture_2d(gs->target, gs->images[i]->image); } - gs->shader = image->shader; + gs->shader_requirements.variant = image->shader_variant; gs->pitch = buffer->width; gs->height = buffer->height; gs->buffer_type = BUFFER_TYPE_EGL; @@ -2533,7 +2856,6 @@ gl_renderer_surface_set_color(struct weston_surface *surface, float red, float green, float blue, float alpha) { struct gl_surface_state *gs = get_surface_state(surface); - struct gl_renderer *gr = get_renderer(surface->compositor); gs->color[0] = red; gs->color[1] = green; @@ -2543,7 +2865,7 @@ gl_renderer_surface_set_color(struct weston_surface *surface, gs->pitch = 1; gs->height = 1; - gs->shader = &gr->solid_shader; + gs->shader_requirements.variant = SHADER_VARIANT_SOLID; } static void @@ -2650,17 +2972,17 @@ gl_renderer_surface_copy_content(struct weston_surface *surface, glViewport(0, 0, cw, ch); glDisable(GL_BLEND); - use_shader(gr, gs->shader); + use_gl_program(gr, &gs->shader_requirements); if (gs->y_inverted) proj = projmat_normal; else proj = projmat_yinvert; - glUniformMatrix4fv(gs->shader->proj_uniform, 1, GL_FALSE, proj); - glUniform1f(gs->shader->alpha_uniform, 1.0f); + glUniformMatrix4fv(gr->current_shader->proj_uniform, 1, GL_FALSE, proj); + glUniform1f(gr->current_shader->alpha_uniform, 1.0f); for (i = 0; i < gs->num_textures; i++) { - glUniform1i(gs->shader->tex_uniforms[i], i); + glUniform1i(gr->current_shader->tex_uniforms[i], i); glActiveTexture(GL_TEXTURE0 + i); glBindTexture(gs->target, gs->textures[i]); @@ -2780,196 +3102,6 @@ gl_renderer_create_surface(struct weston_surface *surface) return 0; } -static const char vertex_shader[] = - "uniform mat4 proj;\n" - "attribute vec2 position;\n" - "attribute vec2 texcoord;\n" - "varying vec2 v_texcoord;\n" - "void main()\n" - "{\n" - " gl_Position = proj * vec4(position, 0.0, 1.0);\n" - " v_texcoord = texcoord;\n" - "}\n"; - -/* Declare common fragment shader uniforms */ -#define FRAGMENT_CONVERT_YUV \ - " y *= alpha;\n" \ - " u *= alpha;\n" \ - " v *= alpha;\n" \ - " gl_FragColor.r = y + 1.59602678 * v;\n" \ - " gl_FragColor.g = y - 0.39176229 * u - 0.81296764 * v;\n" \ - " gl_FragColor.b = y + 2.01723214 * u;\n" \ - " gl_FragColor.a = alpha;\n" - -static const char fragment_debug[] = - " gl_FragColor = vec4(0.0, 0.3, 0.0, 0.2) + gl_FragColor * 0.8;\n"; - -static const char fragment_brace[] = - "}\n"; - -static const char texture_fragment_shader_rgba[] = - "precision mediump float;\n" - "varying vec2 v_texcoord;\n" - "uniform sampler2D tex;\n" - "uniform float alpha;\n" - "void main()\n" - "{\n" - " gl_FragColor = alpha * texture2D(tex, v_texcoord)\n;" - ; - -static const char texture_fragment_shader_rgbx[] = - "precision mediump float;\n" - "varying vec2 v_texcoord;\n" - "uniform sampler2D tex;\n" - "uniform float alpha;\n" - "void main()\n" - "{\n" - " gl_FragColor.rgb = alpha * texture2D(tex, v_texcoord).rgb\n;" - " gl_FragColor.a = alpha;\n" - ; - -static const char texture_fragment_shader_egl_external[] = - "#extension GL_OES_EGL_image_external : require\n" - "precision mediump float;\n" - "varying vec2 v_texcoord;\n" - "uniform samplerExternalOES tex;\n" - "uniform float alpha;\n" - "void main()\n" - "{\n" - " gl_FragColor = alpha * texture2D(tex, v_texcoord)\n;" - ; - -static const char texture_fragment_shader_y_uv[] = - "precision mediump float;\n" - "uniform sampler2D tex;\n" - "uniform sampler2D tex1;\n" - "varying vec2 v_texcoord;\n" - "uniform float alpha;\n" - "void main() {\n" - " float y = 1.16438356 * (texture2D(tex, v_texcoord).x - 0.0625);\n" - " float u = texture2D(tex1, v_texcoord).r - 0.5;\n" - " float v = texture2D(tex1, v_texcoord).g - 0.5;\n" - FRAGMENT_CONVERT_YUV - ; - -static const char texture_fragment_shader_y_u_v[] = - "precision mediump float;\n" - "uniform sampler2D tex;\n" - "uniform sampler2D tex1;\n" - "uniform sampler2D tex2;\n" - "varying vec2 v_texcoord;\n" - "uniform float alpha;\n" - "void main() {\n" - " float y = 1.16438356 * (texture2D(tex, v_texcoord).x - 0.0625);\n" - " float u = texture2D(tex1, v_texcoord).x - 0.5;\n" - " float v = texture2D(tex2, v_texcoord).x - 0.5;\n" - FRAGMENT_CONVERT_YUV - ; - -static const char texture_fragment_shader_y_xuxv[] = - "precision mediump float;\n" - "uniform sampler2D tex;\n" - "uniform sampler2D tex1;\n" - "varying vec2 v_texcoord;\n" - "uniform float alpha;\n" - "void main() {\n" - " float y = 1.16438356 * (texture2D(tex, v_texcoord).x - 0.0625);\n" - " float u = texture2D(tex1, v_texcoord).g - 0.5;\n" - " float v = texture2D(tex1, v_texcoord).a - 0.5;\n" - FRAGMENT_CONVERT_YUV - ; - -static const char solid_fragment_shader[] = - "precision mediump float;\n" - "uniform vec4 color;\n" - "uniform float alpha;\n" - "void main()\n" - "{\n" - " gl_FragColor = alpha * color\n;" - ; - -static int -compile_shader(GLenum type, int count, const char **sources) -{ - GLuint s; - char msg[512]; - GLint status; - - s = glCreateShader(type); - glShaderSource(s, count, sources, NULL); - glCompileShader(s); - glGetShaderiv(s, GL_COMPILE_STATUS, &status); - if (!status) { - glGetShaderInfoLog(s, sizeof msg, NULL, msg); - weston_log("shader info: %s\n", msg); - return GL_NONE; - } - - return s; -} - -static int -shader_init(struct gl_shader *shader, struct gl_renderer *renderer, - const char *vertex_source, const char *fragment_source) -{ - char msg[512]; - GLint status; - int count; - const char *sources[3]; - - shader->vertex_shader = - compile_shader(GL_VERTEX_SHADER, 1, &vertex_source); - - if (renderer->fragment_shader_debug) { - sources[0] = fragment_source; - sources[1] = fragment_debug; - sources[2] = fragment_brace; - count = 3; - } else { - sources[0] = fragment_source; - sources[1] = fragment_brace; - count = 2; - } - - shader->fragment_shader = - compile_shader(GL_FRAGMENT_SHADER, count, sources); - - shader->program = glCreateProgram(); - glAttachShader(shader->program, shader->vertex_shader); - glAttachShader(shader->program, shader->fragment_shader); - glBindAttribLocation(shader->program, 0, "position"); - glBindAttribLocation(shader->program, 1, "texcoord"); - - glLinkProgram(shader->program); - glGetProgramiv(shader->program, GL_LINK_STATUS, &status); - if (!status) { - glGetProgramInfoLog(shader->program, sizeof msg, NULL, msg); - weston_log("link info: %s\n", msg); - return -1; - } - - shader->proj_uniform = glGetUniformLocation(shader->program, "proj"); - shader->tex_uniforms[0] = glGetUniformLocation(shader->program, "tex"); - shader->tex_uniforms[1] = glGetUniformLocation(shader->program, "tex1"); - shader->tex_uniforms[2] = glGetUniformLocation(shader->program, "tex2"); - shader->alpha_uniform = glGetUniformLocation(shader->program, "alpha"); - shader->color_uniform = glGetUniformLocation(shader->program, "color"); - - return 0; -} - -static void -shader_release(struct gl_shader *shader) -{ - glDeleteShader(shader->vertex_shader); - glDeleteShader(shader->fragment_shader); - glDeleteProgram(shader->program); - - shader->vertex_shader = 0; - shader->fragment_shader = 0; - shader->program = 0; -} - static void log_extensions(const char *name, const char *extensions) { @@ -3201,7 +3333,11 @@ gl_renderer_output_create(struct weston_output *output, EGLSurface surface) { struct gl_output_state *go; + struct gl_renderer *gr = get_renderer(output->compositor); int i; + int width = output->current_mode->width; + int height = output->current_mode->height; + int fb_status; go = zalloc(sizeof *go); if (go == NULL) @@ -3219,6 +3355,37 @@ gl_renderer_output_create(struct weston_output *output, output->renderer_state = go; + if (gr->supports_half_float_texture) { + glGenTextures(1, &go->shadow_tex); + glBindTexture(GL_TEXTURE_2D, go->shadow_tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_EXT, width, height, 0, + GL_RGBA, GL_FLOAT, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glBindTexture(GL_TEXTURE_2D, 0); + + glGenFramebuffers(1, &go->shadow_fbo); + glBindFramebuffer(GL_FRAMEBUFFER, go->shadow_fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, go->shadow_tex, 0); + + fb_status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + /* no framebuffer, bail! */ + if (fb_status != GL_FRAMEBUFFER_COMPLETE) { + weston_log("Unable to create shadow FBO\n"); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + return -1; + } + } + + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + go->target_colorspace = WESTON_CS_BT709; + go->target_hdr_metadata = NULL; + go->hdr_state_changed = false; + return 0; } @@ -3281,6 +3448,11 @@ gl_renderer_output_destroy(struct weston_output *output) if (go->end_render_sync != EGL_NO_SYNC_KHR) gr->destroy_sync(gr->egl_display, go->end_render_sync); + if (gr->supports_half_float_texture) { + glDeleteTextures(1, &go->shadow_tex); + glDeleteFramebuffers(1, &go->shadow_fbo); + } + free(go); } @@ -3312,12 +3484,17 @@ gl_renderer_destroy(struct weston_compositor *ec) { struct gl_renderer *gr = get_renderer(ec); struct dmabuf_image *image, *next; + struct gl_shader *shader, *next_shader; wl_signal_emit(&gr->destroy_signal, gr); if (gr->has_bind_display) gr->unbind_display(gr->egl_display, ec->wl_display); + wl_list_for_each_safe(shader, next_shader, &gr->shader_list, link) { + gl_shader_destroy(shader); + } + /* Work around crash in egl_dri2.c's dri2_make_current() - when does this apply? */ eglMakeCurrent(gr->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, @@ -3344,6 +3521,8 @@ gl_renderer_destroy(struct weston_compositor *ec) if (gr->fan_binding) weston_binding_destroy(gr->fan_binding); + gl_shader_generator_destroy(gr->sg); + free(gr); } @@ -3622,6 +3801,35 @@ gl_renderer_create_pbuffer_surface(struct gl_renderer *gr) { return 0; } +static void +gl_renderer_set_output_colorspace(struct weston_output *output, + uint32_t colorspace) +{ + struct gl_output_state *go = get_output_state(output); + if (go->target_colorspace == colorspace) + return; + + go->target_colorspace = colorspace; + go->hdr_state_changed = true; +} + +static void +gl_renderer_set_output_hdr_metadata(struct weston_output *output, + struct weston_hdr_metadata *hdr_metadata) +{ + struct gl_output_state *go = get_output_state(output); + + if (go->target_hdr_metadata == hdr_metadata) + return; + + if (go->target_hdr_metadata && hdr_metadata && + !memcmp(go->target_hdr_metadata, hdr_metadata, sizeof(*hdr_metadata))) + return; + + go->target_hdr_metadata = hdr_metadata; + go->hdr_state_changed = true; +} + static int gl_renderer_display_create(struct weston_compositor *ec, EGLenum platform, void *native_window, const EGLint *platform_attribs, @@ -3651,8 +3859,12 @@ gl_renderer_display_create(struct weston_compositor *ec, EGLenum platform, gr->base.surface_get_content_size = gl_renderer_surface_get_content_size; gr->base.surface_copy_content = gl_renderer_surface_copy_content; + gr->base.set_output_colorspace = gl_renderer_set_output_colorspace; + gr->base.set_output_hdr_metadata = gl_renderer_set_output_hdr_metadata; gr->egl_display = NULL; + wl_list_init(&gr->shader_list); + /* extension_suffix is supported */ if (supports) { if (!get_platform_display) { @@ -3741,6 +3953,8 @@ gl_renderer_display_create(struct weston_compositor *ec, EGLenum platform, goto fail_with_error; } + gr->sg = gl_shader_generator_create(ec); + return 0; fail_with_error: @@ -3758,38 +3972,6 @@ gl_renderer_display(struct weston_compositor *ec) return get_renderer(ec)->egl_display; } -static int -compile_shaders(struct weston_compositor *ec) -{ - struct gl_renderer *gr = get_renderer(ec); - - gr->texture_shader_rgba.vertex_source = vertex_shader; - gr->texture_shader_rgba.fragment_source = texture_fragment_shader_rgba; - - gr->texture_shader_rgbx.vertex_source = vertex_shader; - gr->texture_shader_rgbx.fragment_source = texture_fragment_shader_rgbx; - - gr->texture_shader_egl_external.vertex_source = vertex_shader; - gr->texture_shader_egl_external.fragment_source = - texture_fragment_shader_egl_external; - - gr->texture_shader_y_uv.vertex_source = vertex_shader; - gr->texture_shader_y_uv.fragment_source = texture_fragment_shader_y_uv; - - gr->texture_shader_y_u_v.vertex_source = vertex_shader; - gr->texture_shader_y_u_v.fragment_source = - texture_fragment_shader_y_u_v; - - gr->texture_shader_y_xuxv.vertex_source = vertex_shader; - gr->texture_shader_y_xuxv.fragment_source = - texture_fragment_shader_y_xuxv; - - gr->solid_shader.vertex_source = vertex_shader; - gr->solid_shader.fragment_source = solid_fragment_shader; - - return 0; -} - static void fragment_debug_binding(struct weston_keyboard *keyboard, const struct timespec *time, @@ -3798,16 +3980,13 @@ fragment_debug_binding(struct weston_keyboard *keyboard, struct weston_compositor *ec = data; struct gl_renderer *gr = get_renderer(ec); struct weston_output *output; + struct gl_shader *shader, *next; gr->fragment_shader_debug ^= 1; - shader_release(&gr->texture_shader_rgba); - shader_release(&gr->texture_shader_rgbx); - shader_release(&gr->texture_shader_egl_external); - shader_release(&gr->texture_shader_y_uv); - shader_release(&gr->texture_shader_y_u_v); - shader_release(&gr->texture_shader_y_xuxv); - shader_release(&gr->solid_shader); + wl_list_for_each_safe(shader, next, &gr->shader_list, link) { + gl_shader_destroy(shader); + } /* Force use_shader() to call glUseProgram(), since we need to use * the recompiled version of the shader. */ @@ -3852,6 +4031,7 @@ gl_renderer_setup(struct weston_compositor *ec, EGLSurface egl_surface) const char *extensions; EGLConfig context_config; EGLBoolean ret; + EGLint major_version; EGLint context_attribs[16] = { EGL_CONTEXT_CLIENT_VERSION, 0, @@ -3961,10 +4141,23 @@ gl_renderer_setup(struct weston_compositor *ec, EGLSurface egl_surface) if (weston_check_egl_extension(extensions, "GL_OES_EGL_image_external")) gr->has_egl_image_external = 1; - glActiveTexture(GL_TEXTURE0); + if (weston_check_egl_extension(extensions, "GL_OES_EGL_image_external")) + gr->has_egl_image_external = 1; - if (compile_shaders(ec)) - return -1; + major_version = gr->gl_version >> 16; + switch (major_version) { + case 3: + gr->supports_half_float_texture = true; + break; + case 2: + if (weston_check_egl_extension(extensions, "GL_OES_texture_half_float")) + gr->supports_half_float_texture = true; + break; + default: + gr->supports_half_float_texture = false; + } + + glActiveTexture(GL_TEXTURE0); gr->fragment_binding = weston_compositor_add_debug_binding(ec, KEY_S, diff --git a/libweston/renderer-gl/gl-shaders.c b/libweston/renderer-gl/gl-shaders.c new file mode 100644 index 0000000000000000000000000000000000000000..a4aab67804b259e155d05c741e156898ca42d0f1 --- /dev/null +++ b/libweston/renderer-gl/gl-shaders.c @@ -0,0 +1,740 @@ +/* + * Copyright © 2019 Harish Krupo + * Copyright © 2019 Intel Corporation + * + * 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, sublicense, 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 + * NONINFRINGEMENT. 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 +#include +#include + + +#include +#include "gl-renderer-private.h" +#include "shared/helpers.h" +#include "weston-debug.h" + +struct gl_shader_generator { + struct weston_debug_scope *debug; +}; + +static const char vertex_shader[] = + "uniform mat4 proj;\n" + "attribute vec2 position;\n" + "attribute vec2 texcoord;\n" + "varying vec2 v_texcoord;\n" + "void main()\n" + "{\n" + " gl_Position = proj * vec4(position, 0.0, 1.0);\n" + " v_texcoord = texcoord;\n" + "}\n"; + +#define FRAGMENT_CONVERT_YUV \ + " y *= alpha;\n" \ + " u *= alpha;\n" \ + " v *= alpha;\n" \ + " gl_FragColor.r = y + 1.59602678 * v;\n" \ + " gl_FragColor.g = y - 0.39176229 * u - 0.81296764 * v;\n" \ + " gl_FragColor.b = y + 2.01723214 * u;\n" \ + " gl_FragColor.a = alpha;\n" + +static const char external_extension[] = + "#extension GL_OES_EGL_image_external : require\n"; + +static const char fragment_header[] = + "precision mediump float;\n" + "varying vec2 v_texcoord;\n" + "uniform float alpha;\n"; + +static const char uniform_tex_external[] = "uniform samplerExternalOES tex;\n"; + +static const char uniform_color[] = "uniform vec4 color;\n"; + +static const char uniform_tex2[] = "uniform sampler2D tex2;\n"; + +static const char uniform_tex1[] = "uniform sampler2D tex1;\n"; + +static const char uniform_tex[] = "uniform sampler2D tex;\n"; + +static const char fragment_main_open[] = + "void main()\n" + "{\n"; + +static const char fragment_debug[] = + " gl_FragColor = vec4(0.0, 0.3, 0.0, 0.2) + gl_FragColor * 0.8;\n"; + +static const char texture_fragment_shader_rgba[] = + " gl_FragColor = alpha * texture2D(tex, v_texcoord);\n" + ; + +static const char texture_fragment_shader_rgbx[] = + " gl_FragColor.rgb = alpha * texture2D(tex, v_texcoord).rgb;\n" + " gl_FragColor.a = alpha;\n" + ; + +static const char texture_fragment_shader_external[] = + " gl_FragColor = alpha * texture2D(tex, v_texcoord);\n" + ; + +static const char texture_fragment_shader_y_uv[] = + " float y = 1.16438356 * (texture2D(tex, v_texcoord).x - 0.0625);\n" + " vec2 uv = texture2D(tex1, v_texcoord).rg;\n" + " float u = uv.r - 0.5;\n" + " float v = uv.g - 0.5;\n" + FRAGMENT_CONVERT_YUV + ; + +static const char texture_fragment_shader_y_u_v[] = + " float y = 1.16438356 * (texture2D(tex, v_texcoord).x - 0.0625);\n" + " float u = texture2D(tex1, v_texcoord).x - 0.5;\n" + " float v = texture2D(tex2, v_texcoord).x - 0.5;\n" + FRAGMENT_CONVERT_YUV + ; + +static const char texture_fragment_shader_y_xuxv[] = + " float y = 1.16438356 * (texture2D(tex, v_texcoord).x - 0.0625);\n" + " vec2 uv = texture2D(tex1, v_texcoord).ga;\n" + " float u = uv.r - 0.5;\n" + " float v = uv.g - 0.5;\n" + FRAGMENT_CONVERT_YUV + ; + +static const char solid_fragment_shader[] = + " gl_FragColor = alpha * color;\n" + ; + +static const char fragment_brace[] = "}\n"; + +/* eotfs */ +static const char eotf_srgb[] = + "float eotf_srgb_single(float c) {\n" + " return c < 0.04045 ? c / 12.92 : pow(((c + 0.055) / 1.055), 2.4);\n" + "}\n" + "\n" + "vec3 eotf_srgb(vec3 color) {\n" + " float r = eotf_srgb_single(color.r);\n" + " float g = eotf_srgb_single(color.g);\n" + " float b = eotf_srgb_single(color.b);\n" + " return vec3(r, g, b);\n" + "}\n" + "\n" + "vec3 eotf(vec3 color) {\n" + " return sign(color) * eotf_srgb(abs(color.rgb));\n" + "}\n" + "\n" + ; + +static const char eotf_pq[] = + "vec3 eotf(vec3 v) {\n" + " float m1 = 0.25 * 2610.0 / 4096.0;\n" + " float m2 = 128.0 * 2523.0 / 4096.0;\n" + " float c3 = 32.0 * 2392.0 / 4096.0;\n" + " float c2 = 32.0 * 2413.0 / 4096.0;\n" + " float c1 = c3 - c2 + 1.0;\n" + " vec3 n = pow(v, vec3(1.0 / m2));\n" + " return pow(max(n - c1, 0.0) / (c2 - c3 * n), vec3(1.0 / m1));\n" + "}\n" + "\n" + ; + +static const char eotf_hlg[] = + "vec3 eotf(vec3 l) {\n" + " float a = 0.17883277;\n" + " float b = 1.0 - 4.0 * a;\n" + " float c = 0.5 - a * log(4.0 * a);\n" + " float x = step(1.0 / 2.0, l);\n" + " vec3 v0 = pow(l, 2.0) / 3.0;\n" + " vec3 v1 = (exp((l - c) / a) + b) / 12.0;\n" + " return mix(v0, v1, x);\n" + "}\n" + "\n" + ; + +static const char eotf_default[] = + "vec3 eotf(vec3 color) {\n" + " return color;\n" + "}\n" + "\n" + ; + +/* oetfs */ +static const char oetf_srgb[] = + "float oetf_srgb_single(float c) {\n" + " float ret = 0.0;\n" + " if (c < 0.0031308) {\n" + " ret = 12.92 * c;\n" + " } else {\n" + " ret = 1.055 * pow(c, 1.0 / 2.4) - 0.055;\n" + " }\n" + " return ret;\n" + "}\n" + "\n" + "vec3 oetf_srgb(vec3 color) {\n" + " float r = oetf_srgb_single(color.r);\n" + " float g = oetf_srgb_single(color.g);\n" + " float b = oetf_srgb_single(color.b);\n" + " return vec3(r, g, b);\n" + "}\n" + "\n" + "vec3 oetf(vec3 linear) {\n" + " return sign(linear) * oetf_srgb(abs(linear.rgb));\n" + "}\n" + "\n" + ; + +static const char oetf_pq[] = + "vec3 oetf(vec3 l) {\n" + " float m1 = 0.25 * 2610.0 / 4096.0;\n" + " float m2 = 128.0 * 2523.0 / 4096.0;\n" + " float c3 = 32.0 * 2392.0 / 4096.0;\n" + " float c2 = 32.0 * 2413.0 / 4096.0;\n" + " float c1 = c3 - c2 + 1.0;\n" + " vec3 n = pow(l, vec3(m1));\n" + " return pow((c1 + c2 * n) / (1.0 + c3 * n), vec3(m2));\n" + "}\n" + "\n" + ; + +static const char oetf_hlg[] = + "vec3 oetf(vec3 l) {\n" + " float a = 0.17883277;\n" + " float b = 1.0 - 4.0 * a;\n" + " float c = 0.5 - a * log(4.0 * a);\n" + " float x = step(1.0 / 12.0, l);\n" + " vec3 v0 = a * log(12.0 * l - b) + c;\n" + " vec3 v1 = sqrt(3.0 * l);\n" + " return mix(v0, v1, x);\n" + "}\n" + "\n" + ; + +static const char oetf_default[] = + "vec3 oetf(vec3 color) {\n" + " return color;\n" + "}\n" + "\n" + ; + +static const char eotf_shader[] = + " gl_FragColor.rgb = eotf(gl_FragColor.rgb);\n" + ; + +static const char oetf_shader[] = + " gl_FragColor.rgb = oetf(gl_FragColor.rgb);\n" + ; + +static const char csc_shader[] = + " gl_FragColor.rgb = clamp((csc * gl_FragColor.rgb), 0.0, 1.0);\n" + ; + +static const char hdr_uniforms[] = + "uniform float display_max_luminance;\n" + "uniform float content_max_luminance;\n" + "uniform float content_min_luminance;\n" + ; + +#define LUMINANCE_FROM_RGB \ + " // These are ITU 2100 recommendations\n" \ + " float kr = 0.2627;\n" \ + " float kb = 0.0593;\n" \ + " float kg = 1.0 - kr - kb;\n" \ + " float luma = dot(color, vec3(kr, kg, kb));\n" \ + +/* Luminance scaling */ + +static const char sl_srgb[] = + "vec3 ScaleLuminance(vec3 color) {\n" + " return color * display_max_luminance;\n" + "}\n" + "\n" + ; + +static const char sl_pq[] = + "vec3 ScaleLuminance(vec3 color) {\n" + " return color * 10000.0;\n" + "}\n" + "\n" + ; + +static const char sl_hlg[] = + "vec3 ScaleLuminance(vec3 color) {\n" + LUMINANCE_FROM_RGB + " return color * 1000.0 * pow(luma, 0.2);\n" + "}\n" + "\n" + ; + +/* Luminance Normalization */ + +static const char nl_srgb[] = + "vec3 NormalizeLuminance(vec3 color) {\n" + " return color / display_max_luminance;\n" + "}\n" + "\n" + ; + +static const char nl_pq[] = + "vec3 NormalizeLuminance(vec3 color) {\n" + " return color / 10000.0;\n" + "}\n" + "\n" + ; + +static const char nl_hlg[] = + "vec3 NormalizeLuminance(vec3 color) {\n" + LUMINANCE_FROM_RGB + " return (color / 1000.0) * pow(luma, -0.2);\n" + "}\n" + "\n" + ; + +static const char sl_shader[] = + " gl_FragColor.rgb = ScaleLuminance(gl_FragColor.rgb);\n" + ; + +static const char nl_shader[] = + " gl_FragColor.rgb = NormalizeLuminance(gl_FragColor.rgb);\n" + ; + +/* Tone mapping Shaders */ + +static const char hdr_shader[] = + " gl_FragColor.rgb = tone_mapping(gl_FragColor.rgb);\n" + ; + +/* No tone mapping */ +static const char noop_tm[] = + "vec3 tone_mapping(vec3 color) {\n" + " return color;\n" + "}\n" + "\n" + ; + +/* HDR->SDR */ +static const char hdr_to_sdr_tm[] = + "vec3 hable_curve(vec3 c) {\n" + " float A = 0.15;\n" + " float B = 0.50;\n" + " float C = 0.10;\n" + " float D = 0.20;\n" + " float E = 0.02;\n" + " float F = 0.30;\n" + " vec3 numerator = (c * (A * c + C * B) + D * E);\n" + " vec3 denominator = (c * (A * c + B) + D * F);\n" + " c = (numerator / denominator) - E / F;\n" + " return c;\n" + "}\n" + "\n" + "vec3 tone_mapping(vec3 color) {\n" + " float W = 11.2;\n" + " float exposure = 100.0;\n" + " color *= exposure;\n" + " color = hable_curve(color);\n" + " float white = hable_curve(vec3(W, 0, 0)).x;\n" + " color /= white;\n" + " return color;\n" + "}\n" + "\n" + ; + +static const char sdr_to_hdr_tm[] = + "vec3 tone_mapping(vec3 color) {\n" + LUMINANCE_FROM_RGB + " highp float tone_mapped_luma = 0.0;" + "\n" + " if (luma > 5.0) {\n" + " tone_mapped_luma = luma / display_max_luminance;\n" + " tone_mapped_luma = pow(tone_mapped_luma, 1.5);\n" + " tone_mapped_luma *= display_max_luminance;\n" + " color *= tone_mapped_luma / luma;\n" + " }\n" + " return color;\n" + "}\n" + "\n" + ; + +static const char hdr_to_hdr_tm[] = + "vec3 tone_mapping(vec3 color) {\n" + " float range = content_max_luminance - content_min_luminance;\n" + LUMINANCE_FROM_RGB + " float tone_mapped_luma = luma - content_min_luminance;\n" + " tone_mapped_luma /= range;\n" + " tone_mapped_luma *= display_max_luminance;\n" + " color *= tone_mapped_luma / luma;\n" + " return color;\n" + "}\n" + "\n" + ; + +struct gl_shader_source { + const char *parts[64]; + uint32_t len; +}; + +static inline void +gl_shader_source_add(struct gl_shader_source *shader_source, const char *str) +{ + shader_source->parts[shader_source->len++] = str; + assert(shader_source->len < ARRAY_LENGTH(shader_source->parts)); +} + +static void +generate_fs_hdr_shader(struct gl_shader_source *shader_source, + struct gl_shader_requirements *requirements) +{ + // Write the hdr uniforms + if (requirements->csc_matrix) + gl_shader_source_add(shader_source, "uniform mat3 csc;\n"); + + gl_shader_source_add(shader_source, hdr_uniforms); + + // Choose the EOTF + switch (requirements->degamma) { + case SHADER_DEGAMMA_SRGB: + gl_shader_source_add(shader_source, eotf_srgb); + gl_shader_source_add(shader_source, sl_srgb); + break; + case SHADER_DEGAMMA_PQ: + gl_shader_source_add(shader_source, eotf_pq); + gl_shader_source_add(shader_source, sl_pq); + break; + case SHADER_DEGAMMA_HLG: + gl_shader_source_add(shader_source, eotf_hlg); + gl_shader_source_add(shader_source, sl_hlg); + break; + default: + gl_shader_source_add(shader_source, eotf_default); + break; + } + + // Choose the OETF + switch (requirements->gamma | requirements->nl_variant) { + case SHADER_GAMMA_SRGB: + gl_shader_source_add(shader_source, oetf_srgb); + gl_shader_source_add(shader_source, nl_srgb); + break; + case SHADER_GAMMA_PQ: + gl_shader_source_add(shader_source, oetf_pq); + gl_shader_source_add(shader_source, nl_pq); + break; + case SHADER_GAMMA_HLG: + gl_shader_source_add(shader_source, oetf_hlg); + gl_shader_source_add(shader_source, nl_hlg); + break; + default: + gl_shader_source_add(shader_source, oetf_default); + break; + } + + // Pick the tone mapping shader variant + switch (requirements->tone_mapping) { + case SHADER_TONE_MAP_NONE: + gl_shader_source_add(shader_source, noop_tm); + break; + case SHADER_TONE_MAP_HDR_TO_SDR: + gl_shader_source_add(shader_source, hdr_to_sdr_tm); + break; + case SHADER_TONE_MAP_SDR_TO_HDR: + gl_shader_source_add(shader_source, sdr_to_hdr_tm); + break; + case SHADER_TONE_MAP_HDR_TO_HDR: + gl_shader_source_add(shader_source, hdr_to_hdr_tm); + break; + } +} + +static void +generate_hdr_process_shader(struct gl_shader_source *shader_source, + struct gl_shader_requirements *requirements) +{ + uint32_t need_range_increment = + (requirements->tone_mapping == SHADER_TONE_MAP_HDR_TO_HDR) || + (requirements->tone_mapping == SHADER_TONE_MAP_SDR_TO_HDR); + + if (requirements->degamma) + gl_shader_source_add(shader_source, eotf_shader); + + if (requirements->csc_matrix) + gl_shader_source_add(shader_source, csc_shader); + + if (requirements->degamma && need_range_increment) + gl_shader_source_add(shader_source, sl_shader); + + if (requirements->tone_mapping) + gl_shader_source_add(shader_source, hdr_shader); + + if (requirements->nl_variant) + gl_shader_source_add(shader_source, nl_shader); + + if (requirements->gamma) + gl_shader_source_add(shader_source, oetf_shader); + +} + +static void +generate_fs_uniforms(struct gl_shader_source *shader_source, + struct gl_shader_requirements *requirements) +{ + /* write the header */ + /* require extension for EXTERNAL SHADERS: */ + if (requirements->variant == SHADER_VARIANT_EXTERNAL) { + gl_shader_source_add(shader_source, external_extension); + } + + gl_shader_source_add(shader_source, fragment_header); + + /* Generate uniforms based on variant */ + switch (requirements->variant) { + case SHADER_VARIANT_EXTERNAL: + gl_shader_source_add(shader_source, uniform_tex_external); + break; + case SHADER_VARIANT_SOLID: + gl_shader_source_add(shader_source, uniform_color); + break; + case SHADER_VARIANT_Y_U_V: + gl_shader_source_add(shader_source, uniform_tex2); + + /* fallthrough */ + case SHADER_VARIANT_Y_UV: + case SHADER_VARIANT_Y_XUXV: + gl_shader_source_add(shader_source, uniform_tex1); + + /* fallthrough */ + case SHADER_VARIANT_RGBX: + case SHADER_VARIANT_RGBA: + /* fallthrough */ + default: + gl_shader_source_add(shader_source, uniform_tex); + break; + } + +} + +static void +generate_fs_variants(struct gl_shader_source *shader_source, + struct gl_shader_requirements *requirements) +{ + /* Generate the shader based on variant */ + switch (requirements->variant) { + case SHADER_VARIANT_Y_U_V: + gl_shader_source_add(shader_source, + texture_fragment_shader_y_u_v); + break; + case SHADER_VARIANT_Y_UV: + gl_shader_source_add(shader_source, + texture_fragment_shader_y_uv); + break; + case SHADER_VARIANT_Y_XUXV: + gl_shader_source_add(shader_source, + texture_fragment_shader_y_xuxv); + break; + case SHADER_VARIANT_RGBX: + gl_shader_source_add(shader_source, + texture_fragment_shader_rgbx); + break; + case SHADER_VARIANT_RGBA: + gl_shader_source_add(shader_source, + texture_fragment_shader_rgba); + break; + case SHADER_VARIANT_EXTERNAL: + gl_shader_source_add(shader_source, + texture_fragment_shader_external); + break; + case SHADER_VARIANT_SOLID: + gl_shader_source_add(shader_source, solid_fragment_shader); + break; + case SHADER_VARIANT_NONE: + break; + } + +} + +static void +log_shader(struct gl_shader_generator *sg, + struct gl_shader_source *shader_source) +{ + char *str; + FILE *fp; + size_t len; + uint32_t i; + + fp = open_memstream(&str, &len); + assert(fp); + + fprintf(fp, "Generated shader length: %d, shader:\n", shader_source->len); + for(i = 0; i < shader_source->len; i++) { + fprintf(fp, "%s", shader_source->parts[i]); + } + fprintf(fp, "\n"); + fclose(fp); + + weston_debug_scope_printf(sg->debug, "%s", str); + free(str); +} + +static void +generate_fragment_shader(struct gl_shader_generator *sg, + struct gl_shader_source *shader_source, + struct gl_shader_requirements *requirements) +{ + /* Write the header and required uniforms */ + generate_fs_uniforms(shader_source, requirements); + + // Write shaders needed for HDR + generate_fs_hdr_shader(shader_source, requirements); + + /* begin main function */ + gl_shader_source_add(shader_source, fragment_main_open); + + /* Generate the shader based on variant */ + generate_fs_variants(shader_source, requirements); + + generate_hdr_process_shader(shader_source, requirements); + + if (requirements->debug) + gl_shader_source_add(shader_source, fragment_debug); + + gl_shader_source_add(shader_source, fragment_brace); + + log_shader(sg, shader_source); +} + +void +gl_shader_requirements_init(struct gl_shader_requirements *requirements) +{ + memset(requirements, 0, sizeof(struct gl_shader_requirements)); +} + +void +gl_shader_destroy(struct gl_shader *shader) +{ + glDeleteShader(shader->vertex_shader); + glDeleteShader(shader->fragment_shader); + glDeleteProgram(shader->program); + + shader->vertex_shader = 0; + shader->fragment_shader = 0; + shader->program = 0; + wl_list_remove(&shader->link); + free(shader); +} + +static int +compile_shader(GLenum type, int count, const char **sources) +{ + GLuint s; + char msg[512]; + GLint status; + + s = glCreateShader(type); + glShaderSource(s, count, sources, NULL); + glCompileShader(s); + glGetShaderiv(s, GL_COMPILE_STATUS, &status); + if (!status) { + glGetShaderInfoLog(s, sizeof msg, NULL, msg); + weston_log("shader info: %s\n", msg); + return GL_NONE; + } + + return s; +} + +struct gl_shader * +gl_shader_create(struct gl_shader_generator *sg, + struct gl_shader_requirements *requirements) +{ + struct gl_shader *shader = NULL; + char msg[512]; + GLint status; + const char *vertex_source[1]; + struct gl_shader_source fragment_source; + + shader = zalloc(sizeof *shader); + if (!shader) { + weston_log("could not create shader\n"); + return NULL; + } + + memcpy(&shader->key, requirements, + sizeof(struct gl_shader_requirements)); + + vertex_source[0] = vertex_shader; + + fragment_source.len = 0; + generate_fragment_shader(sg, &fragment_source, requirements); + + shader->vertex_shader = compile_shader(GL_VERTEX_SHADER, 1, + vertex_source); + + shader->fragment_shader = compile_shader(GL_FRAGMENT_SHADER, + fragment_source.len, + fragment_source.parts); + + shader->program = glCreateProgram(); + glAttachShader(shader->program, shader->vertex_shader); + glAttachShader(shader->program, shader->fragment_shader); + glBindAttribLocation(shader->program, 0, "position"); + glBindAttribLocation(shader->program, 1, "texcoord"); + + glLinkProgram(shader->program); + glGetProgramiv(shader->program, GL_LINK_STATUS, &status); + if (!status) { + glGetProgramInfoLog(shader->program, sizeof msg, NULL, msg); + weston_log("link info: %s\n", msg); + return NULL; + } + + shader->proj_uniform = glGetUniformLocation(shader->program, "proj"); + shader->tex_uniforms[0] = glGetUniformLocation(shader->program, "tex"); + shader->tex_uniforms[1] = glGetUniformLocation(shader->program, "tex1"); + shader->tex_uniforms[2] = glGetUniformLocation(shader->program, "tex2"); + shader->alpha_uniform = glGetUniformLocation(shader->program, "alpha"); + shader->color_uniform = glGetUniformLocation(shader->program, "color"); + shader->csc_uniform = glGetUniformLocation(shader->program, "csc"); + shader->display_max_luminance = + glGetUniformLocation(shader->program, "display_max_luminance"); + shader->content_max_luminance = + glGetUniformLocation(shader->program, "content_max_luminance"); + shader->content_min_luminance = + glGetUniformLocation(shader->program, "content_min_luminance"); + + return shader; +} + +struct gl_shader_generator * +gl_shader_generator_create(struct weston_compositor *compositor) +{ + struct gl_shader_generator *sg = zalloc(sizeof *sg); + sg->debug = weston_compositor_add_debug_scope(compositor, "gl-shader-generator", + "Debug messages from GL renderer", + NULL, NULL); + return sg; +} + +void +gl_shader_generator_destroy(struct gl_shader_generator *sg) +{ + weston_debug_scope_destroy(sg->debug); + sg->debug = NULL; + free(sg); +} diff --git a/libweston/renderer-gl/meson.build b/libweston/renderer-gl/meson.build index b5d77d5879cc5d037e018693ff9175ae0088ed89..47e99bef36fa27b50d32f56cbcb009701295ce99 100644 --- a/libweston/renderer-gl/meson.build +++ b/libweston/renderer-gl/meson.build @@ -6,6 +6,9 @@ config_h.set('ENABLE_EGL', '1') srcs_renderer_gl = [ 'gl-renderer.c', + 'gl-shaders.c', + '../../shared/colorspace.c', + '../../shared/csc.c', linux_dmabuf_unstable_v1_protocol_c, linux_dmabuf_unstable_v1_server_protocol_h, ] diff --git a/meson_options.txt b/meson_options.txt index 0e1d18338e3ba2db76b49cefc33f13a1bc465652..0a6557cb297ac6865befc92bca006150473a2047 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -171,6 +171,13 @@ option( value: [ 'intel', 'freedreno', 'etnaviv' ], description: 'List of DRM drivers to be supported by weston-simple-dmabuf-drm' ) +option( + 'simple-hdr-video', + type: 'array', + choices: [ 'auto', 'intel' ], + value: [ 'intel' ], + description: 'List of DRM drivers to be supported by weston-simple-hdr-video' +) option( 'demo-clients', type: 'boolean', diff --git a/protocol/meson.build b/protocol/meson.build index 34026ff92597a5f7907dd79af9b2bcf116fdd358..2654ce290d17b5688360fb2af9fc67a6261cbd0a 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -37,6 +37,8 @@ generated_protocols = [ [ 'xdg-output', 'v1' ], [ 'xdg-shell', 'v6' ], [ 'xdg-shell', 'stable' ], + [ 'hdr-metadata', 'v1' ], + [ 'colorspace', 'v1' ], ] foreach proto: generated_protocols diff --git a/shared/colorspace.c b/shared/colorspace.c new file mode 100644 index 0000000000000000000000000000000000000000..1d13dbc1b503241352fe37bf3d56844a1b266508 --- /dev/null +++ b/shared/colorspace.c @@ -0,0 +1,190 @@ +/* + * Copyright © 2017 Intel Corporation + * + * 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, sublicense, 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 + * NONINFRINGEMENT. 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 "config.h" + +#include +#include + +#ifdef IN_WESTON +#include +#else +#define WL_EXPORT +#endif + +#include "helpers.h" +#include + +static const struct weston_colorspace bt470m = { + .primaries.r = { 0.670f, 0.330f, }, + .primaries.g = { 0.210f, 0.710f, }, + .primaries.b = { 0.140f, 0.080f, }, + .primaries.white_point = { 0.3101f, 0.3162f, }, + .name = "BT.470 M", + .whitepoint_name = "C", +}; + +static const struct weston_colorspace bt470bg = { + .primaries.r = { 0.640f, 0.330f, }, + .primaries.g = { 0.290f, 0.600f, }, + .primaries.b = { 0.150f, 0.060f, }, + .primaries.white_point = { 0.3127f, 0.3290f, }, + .name = "BT.470 B/G", + .whitepoint_name = "D65", +}; + +static const struct weston_colorspace smpte170m = { + .primaries.r = { 0.630f, 0.340f, }, + .primaries.g = { 0.310f, 0.595f, }, + .primaries.b = { 0.155f, 0.070f, }, + .primaries.white_point = { 0.3127f, 0.3290f, }, + .name = "SMPTE 170M", + .whitepoint_name = "D65", +}; + +static const struct weston_colorspace smpte240m = { + .primaries.r = { 0.630f, 0.340f, }, + .primaries.g = { 0.310f, 0.595f, }, + .primaries.b = { 0.155f, 0.070f, }, + .primaries.white_point = { 0.3127f, 0.3290f, }, + .name = "SMPTE 240M", + .whitepoint_name = "D65", +}; + +static const struct weston_colorspace bt709 = { + .primaries.r = { 0.640f, 0.330f, }, + .primaries.g = { 0.300f, 0.600f, }, + .primaries.b = { 0.150f, 0.060f, }, + .primaries.white_point = { 0.3127f, 0.3290f, }, + .name = "BT.709", + .whitepoint_name = "D65", +}; + +static const struct weston_colorspace bt2020 = { + .primaries.r = { 0.708f, 0.292f, }, + .primaries.g = { 0.170f, 0.797f, }, + .primaries.b = { 0.131f, 0.046f, }, + .primaries.white_point = { 0.3127f, 0.3290f, }, + .name = "BT.2020", + .whitepoint_name = "D65", +}; + +static const struct weston_colorspace srgb = { + .primaries.r = { 0.640f, 0.330f, }, + .primaries.g = { 0.300f, 0.600f, }, + .primaries.b = { 0.150f, 0.060f, }, + .primaries.white_point = { 0.3127f, 0.3290f, }, + .name = "sRGB", + .whitepoint_name = "D65", +}; + +static const struct weston_colorspace adobergb = { + .primaries.r = { 0.640f, 0.330f, }, + .primaries.g = { 0.210f, 0.710f, }, + .primaries.b = { 0.150f, 0.060f, }, + .primaries.white_point = { 0.3127f, 0.3290f, }, + .name = "AdobeRGB", + .whitepoint_name = "D65", +}; + +static const struct weston_colorspace dci_p3 = { + .primaries.r = { 0.680f, 0.320f, }, + .primaries.g = { 0.265f, 0.690f, }, + .primaries.b = { 0.150f, 0.060f, }, + .primaries.white_point = { 0.3127f, 0.3290f, }, + .name = "DCI-P3 D65", + .whitepoint_name = "D65", +}; + +static const struct weston_colorspace prophotorgb = { + .primaries.r = { 0.7347f, 0.2653f, }, + .primaries.g = { 0.1596f, 0.8404f, }, + .primaries.b = { 0.0366f, 0.0001f, }, + .primaries.white_point = { .3457, .3585 }, + .name = "ProPhoto RGB", + .whitepoint_name = "D50", +}; + +static const struct weston_colorspace ciergb = { + .primaries.r = { 0.7347f, 0.2653f, }, + .primaries.g = { 0.2738f, 0.7174f, }, + .primaries.b = { 0.1666f, 0.0089f, }, + .primaries.white_point = { 1.0f / 3.0f, 1.0f / 3.0f, }, + .name = "CIE RGB", + .whitepoint_name = "E", +}; + +static const struct weston_colorspace ciexyz = { + .primaries.r = { 1.0f, 0.0f, }, + .primaries.g = { 0.0f, 1.0f, }, + .primaries.b = { 0.0f, 0.0f, }, + .primaries.white_point = { 1.0f / 3.0f, 1.0f / 3.0f, }, + .name = "CIE XYZ", + .whitepoint_name = "E", +}; + +const struct weston_colorspace ap0 = { + .primaries.r = { 0.7347f, 0.2653f, }, + .primaries.g = { 0.0000f, 1.0000f, }, + .primaries.b = { 0.0001f, -0.0770f, }, + .primaries.white_point = { .32168f, .33767f, }, + .name = "ACES .primaries #0", + .whitepoint_name = "D60", +}; + +const struct weston_colorspace ap1 = { + .primaries.r = { 0.713f, 0.393f, }, + .primaries.g = { 0.165f, 0.830f, }, + .primaries.b = { 0.128f, 0.044f, }, + .primaries.white_point = { 0.32168f, 0.33767f, }, + .name = "ACES .primaries #1", + .whitepoint_name = "D60", +}; + +static const struct weston_colorspace * const colorspaces[] = { + [WESTON_CS_BT470M] = &bt470m, + [WESTON_CS_BT470BG] = &bt470bg, + [WESTON_CS_SMPTE170M] = &smpte170m, + [WESTON_CS_SMPTE240M] = &smpte240m, + [WESTON_CS_BT709] = &bt709, + [WESTON_CS_BT2020] = &bt2020, + [WESTON_CS_SRGB] = &srgb, + [WESTON_CS_ADOBERGB] = &adobergb, + [WESTON_CS_DCI_P3] = &dci_p3, + [WESTON_CS_PROPHOTORGB] = &prophotorgb, + [WESTON_CS_CIERGB] = &ciergb, + [WESTON_CS_CIEXYZ] = &ciexyz, + [WESTON_CS_AP0] = &ap0, + [WESTON_CS_AP1] = &ap1, +}; + +WL_EXPORT const struct weston_colorspace * +weston_colorspace_lookup(enum weston_colorspace_enums colorspace) +{ + if (colorspace < 0 || colorspace >= WESTON_CS_UNDEFINED) + return NULL; + + return colorspaces[colorspace]; +} diff --git a/shared/csc.c b/shared/csc.c new file mode 100644 index 0000000000000000000000000000000000000000..f4d27aec58ad0c036e4dc36319ad4dd3410bdd45 --- /dev/null +++ b/shared/csc.c @@ -0,0 +1,217 @@ +/* + * Copyright © 2017 Intel Corporation + * + * 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, sublicense, 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 + * NONINFRINGEMENT. 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 "config.h" + +#include +#include +#include + +#ifdef IN_WESTON +#include +#else +#define WL_EXPORT +#endif + +#include "csc.h" + +static void xy_to_xyz(struct weston_vector *xyz, + const struct cie_xy *xy, + float luminance) +{ + float xy_z = 1.0f - xy->x - xy->y; + float xy_y_inv = 1.0f / xy->y; + + xyz->f[0] = luminance * xy->x * xy_y_inv; + xyz->f[1] = luminance; + xyz->f[2] = luminance * xy_z * xy_y_inv; + xyz->f[3] = 1.0f; +} + +static void +rgb_to_xyz_matrix(struct weston_matrix *matrix, + const struct weston_colorspace *cs) +{ + struct weston_vector r, g, b, w; + struct weston_matrix p, p_inv; + int err; + + xy_to_xyz(&w, &cs->primaries.white_point, 1.0f); + xy_to_xyz(&r, &cs->primaries.r, cs->primaries.r.y); + xy_to_xyz(&g, &cs->primaries.g, cs->primaries.g.y); + xy_to_xyz(&b, &cs->primaries.b, cs->primaries.b.y); + + if (cs->primaries.r.x == 1.0f && cs->primaries.r.y == 0.0f && + cs->primaries.g.x == 0.0f && cs->primaries.g.y == 1.0f && + cs->primaries.b.x == 0.0f && cs->primaries.b.y == 0.0f) { + r.f[0] = 1.0f; + r.f[1] = 0.0f; + r.f[2] = 0.0f; + g.f[0] = 0.0f; + g.f[1] = 1.0f; + g.f[2] = 0.0f; + b.f[0] = 0.0f; + b.f[1] = 0.0f; + b.f[2] = 1.0f; + } + + weston_matrix_init(&p); + + p.d[0 * 4 + 0] = r.f[0]; + p.d[1 * 4 + 0] = g.f[0]; + p.d[2 * 4 + 0] = b.f[0]; + p.d[0 * 4 + 1] = r.f[1]; + p.d[1 * 4 + 1] = g.f[1]; + p.d[2 * 4 + 1] = b.f[1]; + p.d[0 * 4 + 2] = r.f[2]; + p.d[1 * 4 + 2] = g.f[2]; + p.d[2 * 4 + 2] = b.f[2]; + + err = weston_matrix_invert(&p_inv, &p); + assert(err == 0); + + weston_matrix_transform(&p_inv, &w); + + weston_matrix_diag(matrix, &w); + + weston_matrix_multiply(matrix, &p); +} + +static void +xyz_to_lms_matrix(struct weston_matrix *matrix) +{ + weston_matrix_init(matrix); + +#if 0 + /* von Kries */ + matrix->d[0 * 4 + 0] = 0.4002f; + matrix->d[1 * 4 + 0] = 0.7076f; + matrix->d[2 * 4 + 0] = -0.0808f; + + matrix->d[0 * 4 + 1] = -0.2263f; + matrix->d[1 * 4 + 1] = 1.1653f; + matrix->d[2 * 4 + 1] = 0.0457f; + + matrix->d[0 * 4 + 2] = 0.0000f; + matrix->d[1 * 4 + 2] = 0.0000f; + matrix->d[2 * 4 + 2] = 0.9182f; +#endif +#if 1 + /* Bradford */ + matrix->d[0 * 4 + 0] = 0.8951f; + matrix->d[1 * 4 + 0] = 0.2664f; + matrix->d[2 * 4 + 0] = -0.1614f; + + matrix->d[0 * 4 + 1] = -0.7502f; + matrix->d[1 * 4 + 1] = 1.7135f; + matrix->d[2 * 4 + 1] = 0.0367f; + + matrix->d[0 * 4 + 2] = 0.0389f; + matrix->d[1 * 4 + 2] = -0.0685f; + matrix->d[2 * 4 + 2] = 1.0296f; +#endif +} + +static void +cat_matrix(struct weston_matrix *matrix, + const struct weston_colorspace *dst, + const struct weston_colorspace *src) +{ + struct weston_matrix xyz_to_lms; + struct weston_vector w_xyz_dst; + struct weston_vector w_xyz_src; + struct weston_vector w_lms_dst; + struct weston_vector w_lms_src; + + xy_to_xyz(&w_xyz_dst, &dst->primaries.white_point, 1.0f); + xy_to_xyz(&w_xyz_src, &src->primaries.white_point, 1.0f); + + xyz_to_lms_matrix(&xyz_to_lms); + + w_lms_dst = w_xyz_dst; + weston_matrix_transform(&xyz_to_lms, &w_xyz_dst); + + w_lms_src = w_xyz_src; + weston_matrix_transform(&xyz_to_lms, &w_xyz_src); + + weston_matrix_init(matrix); + + matrix->d[0 * 4 + 0] = w_lms_dst.f[0] / w_lms_src.f[0]; + matrix->d[1 * 4 + 1] = w_lms_dst.f[1] / w_lms_src.f[1]; + matrix->d[2 * 4 + 2] = w_lms_dst.f[2] / w_lms_src.f[2]; + matrix->d[3 * 4 + 3] = 1.0f; +} + +WL_EXPORT void +weston_csc_matrix(struct weston_matrix *matrix, + const struct weston_colorspace *dst, + const struct weston_colorspace *src, + float luminance_scale) +{ + struct weston_matrix rgb_to_xyz_src; + struct weston_matrix xyz_to_lms; + struct weston_matrix cat; + struct weston_matrix lms_to_xyz; + struct weston_matrix rgb_to_xyz_dst; + struct weston_matrix xyz_to_rgb_dst; + int err; + + rgb_to_xyz_matrix(&rgb_to_xyz_src, src); + rgb_to_xyz_matrix(&rgb_to_xyz_dst, dst); + err = weston_matrix_invert(&xyz_to_rgb_dst, &rgb_to_xyz_dst); + assert(err == 0); + + xyz_to_lms_matrix(&xyz_to_lms); + err = weston_matrix_invert(&lms_to_xyz, &xyz_to_lms); + assert(err == 0); + + cat_matrix(&cat, dst, src); + +#if 0 + printf("RGB to XYZ\n"); + weston_matrix_print(&rgb_to_xyz_src); + printf("XYZ to LMS\n"); + weston_matrix_print(&xyz_to_lms); + printf("CAT\n"); + weston_matrix_print(&cat); + printf("LMS to XYZ\n"); + weston_matrix_print(&lms_to_xyz); + printf("XYZ to RGB\n"); + weston_matrix_print(&xyz_to_rgb_dst); +#endif + + weston_matrix_init(matrix); + + weston_matrix_multiply(matrix, &rgb_to_xyz_src); + weston_matrix_multiply(matrix, &xyz_to_lms); + weston_matrix_multiply(matrix, &cat); + weston_matrix_multiply(matrix, &lms_to_xyz); + weston_matrix_multiply(matrix, &xyz_to_rgb_dst); + + weston_matrix_scale(matrix, + luminance_scale, + luminance_scale, + luminance_scale); +} diff --git a/shared/csc.h b/shared/csc.h new file mode 100644 index 0000000000000000000000000000000000000000..7b5a9bd22359899471d48949f483721e50938722 --- /dev/null +++ b/shared/csc.h @@ -0,0 +1,45 @@ +/* + * Copyright © 2017 Intel Corporation + * + * 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, sublicense, 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 + * NONINFRINGEMENT. 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. + */ + +#ifndef WESTON_CSC_H +#define WESTON_CSC_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void weston_csc_matrix(struct weston_matrix *matrix, + const struct weston_colorspace *dst, + const struct weston_colorspace *src, + float luminance_scale); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/shared/matrix.c b/shared/matrix.c index 4e8d6b40b63e4e9ae8efc86d00344db1e386e14f..532355a203644422729d413064b705c025976244 100644 --- a/shared/matrix.c +++ b/shared/matrix.c @@ -129,6 +129,19 @@ weston_matrix_transform(struct weston_matrix *matrix, struct weston_vector *v) *v = t; } +WL_EXPORT void +weston_matrix_diag(struct weston_matrix *matrix, const struct weston_vector *v) +{ + unsigned c; + + weston_matrix_init(matrix); + + for (c = 0; c < 4; c++) + matrix->d[c * 4 + c] = v->f[c]; + + matrix->type = WESTON_MATRIX_TRANSFORM_OTHER; +} + static inline void swap_rows(double *a, double *b) {