diff --git a/libweston/renderer-gl/fragment.glsl b/libweston/renderer-gl/fragment.glsl index 2036490a..d747d4e7 100644 --- a/libweston/renderer-gl/fragment.glsl +++ b/libweston/renderer-gl/fragment.glsl @@ -28,7 +28,7 @@ /* GLSL version 1.00 ES, defined in gl-shaders.c */ /* - * Enumeration of shader variants. + * Enumeration of shader variants, must match enum gl_shader_texture_variant. */ #define SHADER_VARIANT_RGBX 1 #define SHADER_VARIANT_RGBA 2 diff --git a/libweston/renderer-gl/gl-renderer-internal.h b/libweston/renderer-gl/gl-renderer-internal.h index b208a770..2848c80b 100644 --- a/libweston/renderer-gl/gl-renderer-internal.h +++ b/libweston/renderer-gl/gl-renderer-internal.h @@ -1,5 +1,7 @@ /* * Copyright © 2019 Collabora, Ltd. + * 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 @@ -26,18 +28,60 @@ #ifndef GL_RENDERER_INTERNAL_H #define GL_RENDERER_INTERNAL_H +#include + +#include #include #include #include "shared/weston-egl-ext.h" /* for PFN* stuff */ +#include "shared/helpers.h" + +enum gl_shader_texture_variant { + SHADER_VARIANT_NONE = 0, +/* Keep the following in sync with fragment.glsl. */ + SHADER_VARIANT_RGBX, + SHADER_VARIANT_RGBA, + SHADER_VARIANT_Y_U_V, + SHADER_VARIANT_Y_UV, + SHADER_VARIANT_Y_XUXV, + SHADER_VARIANT_XYUV, + SHADER_VARIANT_SOLID, + SHADER_VARIANT_EXTERNAL, +}; + +/** GL shader requirements key + * + * This structure is used as a binary blob key for building and searching + * shaders. Therefore it must not contain any bytes or bits the C compiler + * would be free to leave undefined e.g. after struct initialization, + * struct assignment, or member operations. + * + * Use 'pahole' from package 'dwarves' to inspect this structure. + */ +struct gl_shader_requirements +{ + unsigned variant:4; /* enum gl_shader_texture_variant */ + bool green_tint:1; + + /* + * The total size of all bitfields plus pad_bits_ must fill up exactly + * how many bytes the compiler allocates for them together. + */ + unsigned pad_bits_:27; +}; +static_assert(sizeof(struct gl_shader_requirements) == + 4 /* total bitfield size in bytes */, + "struct gl_shader_requirements must not contain implicit padding"); 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; - const char *vertex_source, *fragment_source; + struct wl_list link; /* gl_renderer::shader_list */ }; struct gl_renderer { @@ -91,15 +135,6 @@ struct gl_renderer { bool 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 texture_shader_xyuv; - struct gl_shader invert_color_shader; - struct gl_shader solid_shader; struct gl_shader *current_shader; struct wl_signal destroy_signal; @@ -117,6 +152,12 @@ struct gl_renderer { bool 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; }; static inline struct gl_renderer * @@ -149,14 +190,15 @@ gl_renderer_setup_egl_client_extensions(struct gl_renderer *gr); int gl_renderer_setup_egl_extensions(struct weston_compositor *ec); -int -shader_init(struct gl_shader *shader, struct gl_renderer *renderer, - const char *vertex_source, const char *fragment_source); - void -shader_release(struct gl_shader *shader); +gl_shader_destroy(struct gl_shader *shader); + +struct gl_shader * +gl_shader_create(struct gl_renderer *gr, + const struct gl_shader_requirements *requirements); int -compile_shaders(struct weston_compositor *ec); +gl_shader_requirements_cmp(const struct gl_shader_requirements *a, + const struct gl_shader_requirements *b); #endif /* GL_RENDERER_INTERNAL_H */ diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 1e5527da..dba3a5b3 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -123,7 +123,7 @@ struct dmabuf_image { enum import_type import_type; GLenum target; - struct gl_shader *shader; + enum gl_shader_texture_variant shader_variant; }; struct dmabuf_format { @@ -159,7 +159,6 @@ struct yuv_format_descriptor { struct gl_surface_state { GLfloat color[4]; - struct gl_shader *shader; GLuint textures[3]; int num_textures; @@ -197,6 +196,7 @@ struct gl_surface_state { struct wl_listener surface_destroy_listener; struct wl_listener renderer_destroy_listener; + struct gl_shader_requirements shader_requirements; }; enum timeline_render_point_type { @@ -625,6 +625,55 @@ texture_region(struct weston_view *ev, pixman_region32_t *region, return nvtx; } +static struct gl_shader * +gl_renderer_get_program(struct gl_renderer *gr, + const struct gl_shader_requirements *requirements) +{ + struct gl_shader_requirements reqs = *requirements; + struct gl_shader *shader; + + assert(reqs.pad_bits_ == 0); + + if (gr->fragment_shader_debug) + reqs.green_tint = true; + + if (gr->current_shader && + gl_shader_requirements_cmp(&reqs, &gr->current_shader->key) == 0) + return gr->current_shader; + + wl_list_for_each(shader, &gr->shader_list, link) { + if (gl_shader_requirements_cmp(&reqs, &shader->key) == 0) + return shader; + } + + shader = gl_shader_create(gr, &reqs); + if (shader) + return shader; + + weston_log("warning: failed to generate gl program\n"); + return NULL; +} + +static bool +gl_renderer_use_program(struct gl_renderer *gr, struct gl_shader *shader) +{ + if (!shader) { + weston_log("Error: trying to use NULL GL shader.\n"); + return false; + } + + if (gr->current_shader == shader) + return true; + + glUseProgram(shader->program); + gr->current_shader = shader; + return true; +} + +static const struct gl_shader_requirements requirements_triangle_fan = { + .variant = SHADER_VARIANT_SOLID, +}; + static void triangle_fan_debug(struct weston_view *view, int first, int count) { @@ -635,6 +684,8 @@ triangle_fan_debug(struct weston_view *view, int first, int count) GLushort *index; int nelems; static int color_idx = 0; + struct gl_shader *shader; + struct gl_shader *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 }, @@ -642,6 +693,10 @@ triangle_fan_debug(struct weston_view *view, int first, int count) { 1.0, 1.0, 1.0, 1.0 }, }; + shader = gl_renderer_get_program(gr, &requirements_triangle_fan); + if (!gl_renderer_use_program(gr, shader)) + return; + nelems = (count - 1 + count - 2) * 2; buffer = malloc(sizeof(GLushort) * nelems); @@ -657,11 +712,11 @@ 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, - color[color_idx++ % ARRAY_LENGTH(color)]); + glUniform4fv(shader->color_uniform, 1, + color[color_idx++ % ARRAY_LENGTH(color)]); glDrawElements(GL_LINES, nelems, GL_UNSIGNED_SHORT, buffer); - glUseProgram(gr->current_shader->program); + + gl_renderer_use_program(gr, prev_shader); free(buffer); } @@ -734,34 +789,18 @@ use_output(struct weston_output *output) } static void -use_shader(struct gl_renderer *gr, struct gl_shader *shader) -{ - if (!shader->program) { - int ret; - - ret = shader_init(shader, gr, - shader->vertex_source, - shader->fragment_source); - - if (ret < 0) - weston_log("warning: failed to compile shader\n"); - } - - if (gr->current_shader == shader) - return; - glUseProgram(shader->program); - gr->current_shader = shader; -} - -static void -shader_uniforms(struct gl_shader *shader, - struct weston_view *view, - struct weston_output *output) +gl_renderer_use_program_with_view_uniforms(struct gl_renderer *gr, + struct gl_shader *shader, + struct weston_view *view, + struct weston_output *output) { int i; struct gl_surface_state *gs = get_surface_state(view->surface); struct gl_output_state *go = get_output_state(output); + if (!gl_renderer_use_program(gr, shader)) + return; + glUniformMatrix4fv(shader->proj_uniform, 1, GL_FALSE, go->output_matrix.d); glUniform4fv(shader->color_uniform, 1, gs->color); @@ -851,6 +890,9 @@ static struct gl_shader * maybe_censor_override(struct weston_output *output, struct weston_view *ev) { + const struct gl_shader_requirements requirements_censor = { + .variant = SHADER_VARIANT_SOLID, + }; struct weston_compositor *ec = ev->surface->compositor; struct gl_renderer *gr = get_renderer(ec); struct gl_surface_state *gs = get_surface_state(ev->surface); @@ -866,24 +908,24 @@ maybe_censor_override(struct weston_output *output, gs->color[1] = 0.0; gs->color[2] = 0.0; gs->color[3] = 1.0; - return &gr->solid_shader; + return gl_renderer_get_program(gr, &requirements_censor); } /* When not in enforced mode, the client is notified of the protection */ /* change, so content censoring is not required */ if (ev->surface->protection_mode != WESTON_SURFACE_PROTECTION_MODE_ENFORCED) - return gs->shader; + return gl_renderer_get_program(gr, &gs->shader_requirements); if (recording_censor || unprotected_censor) { gs->color[0] = 0.40; gs->color[1] = 0.0; gs->color[2] = 0.0; gs->color[3] = 1.0; - return &gr->solid_shader; + return gl_renderer_get_program(gr, &requirements_censor); } - return gs->shader; + return gl_renderer_get_program(gr, &gs->shader_requirements); } static void @@ -906,7 +948,8 @@ draw_view(struct weston_view *ev, struct weston_output *output, /* 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 && !gs->direct_display) + if (gs->shader_requirements.variant == SHADER_VARIANT_NONE && + !gs->direct_display) return; pixman_region32_init(&repaint); @@ -920,18 +963,20 @@ draw_view(struct weston_view *ev, struct weston_output *output, if (ensure_surface_buffer_is_ready(gr, gs) < 0) goto out; - shader = maybe_censor_override(output, ev); - 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 triangle fan shader is generated + * here so that the shader uniforms are cached when used later + */ if (gr->fan_debug) { - use_shader(gr, &gr->solid_shader); - shader_uniforms(&gr->solid_shader, ev, output); + shader = gl_renderer_get_program(gr, + &requirements_triangle_fan); + gl_renderer_use_program_with_view_uniforms(gr, shader, + ev, output); } - use_shader(gr, shader); - shader_uniforms(shader, ev, output); - if (ev->transform.enabled || output->zoom.active || output->current_scale != ev->surface->buffer_viewport.buffer.scale) filter = GL_LINEAR; @@ -963,15 +1008,25 @@ draw_view(struct weston_view *ev, struct weston_output *output, else pixman_region32_copy(&surface_opaque, &ev->surface->opaque); + shader = maybe_censor_override(output, ev); + gl_renderer_use_program_with_view_uniforms(gr, shader, ev, output); + if (pixman_region32_not_empty(&surface_opaque)) { - if (shader == &gr->texture_shader_rgba) { + if (shader->key.variant == SHADER_VARIANT_RGBA) { + struct gl_shader_requirements tmp_requirements; + struct gl_shader *tmp_shader; + /* 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); + tmp_requirements = shader->key; + tmp_requirements.variant = SHADER_VARIANT_RGBX; + tmp_shader = gl_renderer_get_program(gr, &tmp_requirements); + gl_renderer_use_program_with_view_uniforms(gr, + tmp_shader, + ev, output); } if (ev->alpha < 1.0) @@ -984,7 +1039,7 @@ draw_view(struct weston_view *ev, struct weston_output *output, } if (pixman_region32_not_empty(&surface_blend)) { - use_shader(gr, shader); + gl_renderer_use_program(gr, shader); glEnable(GL_BLEND); repaint_region(ev, &repaint, &surface_blend); gs->used_in_output_repaint = true; @@ -1158,10 +1213,13 @@ static void draw_output_borders(struct weston_output *output, enum gl_border_status border_status) { + const struct gl_shader_requirements requirements_rgba = { + .variant = SHADER_VARIANT_RGBA, + }; 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; struct gl_border_image *top, *bottom, *left, *right; + struct gl_shader *shader; struct weston_matrix matrix; int full_width, full_height; @@ -1177,7 +1235,9 @@ draw_output_borders(struct weston_output *output, full_height = output->current_mode->height + top->height + bottom->height; glDisable(GL_BLEND); - use_shader(gr, shader); + shader = gl_renderer_get_program(gr, &requirements_rgba); + if (!gl_renderer_use_program(gr, shader)) + return; glViewport(0, 0, full_width, full_height); @@ -1711,28 +1771,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; @@ -1764,18 +1824,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; @@ -1855,26 +1915,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; } @@ -2198,16 +2258,16 @@ import_yuv_dmabuf(struct gl_renderer *gr, switch (format->texture_type) { case TEXTURE_Y_XUXV_WL: - image->shader = &gr->texture_shader_y_xuxv; + image->shader_variant = SHADER_VARIANT_Y_XUXV; break; case TEXTURE_Y_UV_WL: - image->shader = &gr->texture_shader_y_uv; + image->shader_variant = SHADER_VARIANT_Y_UV; break; case TEXTURE_Y_U_V_WL: - image->shader = &gr->texture_shader_y_u_v; + image->shader_variant = SHADER_VARIANT_Y_U_V; break; case TEXTURE_XYUV_WL: - image->shader = &gr->texture_shader_xyuv; + image->shader_variant = SHADER_VARIANT_XYUV; break; default: assert(false); @@ -2320,10 +2380,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)) { @@ -2587,7 +2647,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; } static void @@ -2651,7 +2711,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; @@ -2661,7 +2720,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 @@ -2725,6 +2784,7 @@ gl_renderer_surface_copy_content(struct weston_surface *surface, const GLenum gl_format = GL_RGBA; /* PIXMAN_a8b8g8r8 little-endian */ struct gl_renderer *gr = get_renderer(surface->compositor); struct gl_surface_state *gs = get_surface_state(surface); + struct gl_shader *shader; int cw, ch; GLuint fbo; GLuint tex; @@ -2747,6 +2807,10 @@ gl_renderer_surface_copy_content(struct weston_surface *surface, break; } + shader = gl_renderer_get_program(gr, &gs->shader_requirements); + if (!gl_renderer_use_program(gr, shader)) + return -1; + glGenTextures(1, &tex); glBindTexture(GL_TEXTURE_2D, tex); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, cw, ch, @@ -2768,17 +2832,16 @@ gl_renderer_surface_copy_content(struct weston_surface *surface, glViewport(0, 0, cw, ch); glDisable(GL_BLEND); - use_shader(gr, gs->shader); 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(shader->proj_uniform, 1, GL_FALSE, proj); + glUniform1f(shader->alpha_uniform, 1.0f); for (i = 0; i < gs->num_textures; i++) { - glUniform1i(gs->shader->tex_uniforms[i], i); + glUniform1i(shader->tex_uniforms[i], i); glActiveTexture(GL_TEXTURE0 + i); glBindTexture(gs->target, gs->textures[i]); @@ -3181,18 +3244,21 @@ gl_renderer_destroy(struct weston_compositor *ec) struct gl_renderer *gr = get_renderer(ec); struct dmabuf_image *image, *next; struct dmabuf_format *format, *next_format; + 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, EGL_NO_CONTEXT); - wl_list_for_each_safe(image, next, &gr->dmabuf_images, link) dmabuf_image_destroy(image); @@ -3275,6 +3341,7 @@ gl_renderer_display_create(struct weston_compositor *ec, if (gr == NULL) return -1; + wl_list_init(&gr->shader_list); gr->platform = options->egl_platform; if (gl_renderer_setup_egl_client_extensions(gr) < 0) @@ -3381,19 +3448,6 @@ fragment_debug_binding(struct weston_keyboard *keyboard, gr->fragment_shader_debug = !gr->fragment_shader_debug; - 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->texture_shader_xyuv); - shader_release(&gr->solid_shader); - - /* Force use_shader() to call glUseProgram(), since we need to use - * the recompiled version of the shader. */ - gr->current_shader = NULL; - wl_list_for_each(output, &ec->output_list, link) weston_output_damage(output); } @@ -3536,9 +3590,6 @@ gl_renderer_setup(struct weston_compositor *ec, EGLSurface egl_surface) glActiveTexture(GL_TEXTURE0); - if (compile_shaders(ec)) - return -1; - gr->fragment_binding = weston_compositor_add_debug_binding(ec, KEY_S, fragment_debug_binding, diff --git a/libweston/renderer-gl/gl-shaders.c b/libweston/renderer-gl/gl-shaders.c index a55005a3..86490fba 100644 --- a/libweston/renderer-gl/gl-shaders.c +++ b/libweston/renderer-gl/gl-shaders.c @@ -2,6 +2,8 @@ * Copyright 2012 Intel Corporation * Copyright 2015,2019 Collabora, Ltd. * Copyright 2016 NVIDIA Corporation + * 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 @@ -27,12 +29,18 @@ #include "config.h" +#include +#include +#include + +#include #include #include #include "gl-renderer.h" #include "gl-renderer-internal.h" +#include "shared/helpers.h" /* static const char vertex_shader[]; vertex.glsl */ #include "vertex-shader.h" @@ -40,6 +48,26 @@ /* static const char fragment_shader[]; fragment.glsl */ #include "fragment-shader.h" +static const char * +gl_shader_texture_variant_to_string(enum gl_shader_texture_variant v) +{ + switch (v) { +#define CASERET(x) case x: return #x; + CASERET(SHADER_VARIANT_NONE) + CASERET(SHADER_VARIANT_RGBX) + CASERET(SHADER_VARIANT_RGBA) + CASERET(SHADER_VARIANT_Y_U_V) + CASERET(SHADER_VARIANT_Y_UV) + CASERET(SHADER_VARIANT_Y_XUXV) + CASERET(SHADER_VARIANT_XYUV) + CASERET(SHADER_VARIANT_SOLID) + CASERET(SHADER_VARIANT_EXTERNAL) +#undef CASERET + } + + return "!?!?"; /* never reached */ +} + static void dump_program_with_line_numbers(int count, const char **sources) { @@ -77,6 +105,14 @@ dump_program_with_line_numbers(int count, const char **sources) free(dumpstr); } +void +gl_shader_destroy(struct gl_shader *shader) +{ + glDeleteProgram(shader->program); + wl_list_remove(&shader->link); + free(shader); +} + static int compile_shader(GLenum type, int count, const char **sources) { @@ -99,44 +135,57 @@ compile_shader(GLenum type, int count, const char **sources) return s; } -int -shader_init(struct gl_shader *shader, struct gl_renderer *renderer, - const char *vertex_source, const char *fragment_variant) +static char * +create_shader_config_string(const struct gl_shader_requirements *req) +{ + int size; + char *str; + + size = asprintf(&str, + "#define DEF_GREEN_TINT %s\n" + "#define DEF_VARIANT %s\n", + req->green_tint ? "true" : "false", + gl_shader_texture_variant_to_string(req->variant)); + if (size < 0) + return NULL; + return str; +} + +struct gl_shader * +gl_shader_create(struct gl_renderer *gr, + const struct gl_shader_requirements *requirements) { - static const char fragment_shader_attrs_fmt[] = - "#define DEF_GREEN_TINT %s\n" - "#define DEF_VARIANT %s\n" - ; + struct gl_shader *shader = NULL; char msg[512]; GLint status; - int ret; const char *sources[3]; - char *attrs; - const char *def_green_tint; + char *conf = NULL; - shader->vertex_shader = - compile_shader(GL_VERTEX_SHADER, 1, &vertex_source); - if (shader->vertex_shader == GL_NONE) - return -1; + shader = zalloc(sizeof *shader); + if (!shader) { + weston_log("could not create shader\n"); + goto error_vertex; + } - if (renderer->fragment_shader_debug) - def_green_tint = "true"; - else - def_green_tint = "false"; + wl_list_init(&shader->link); + shader->key = *requirements; - ret = asprintf(&attrs, fragment_shader_attrs_fmt, - def_green_tint, fragment_variant); - if (ret < 0) - return -1; + sources[0] = vertex_shader; + shader->vertex_shader = compile_shader(GL_VERTEX_SHADER, 1, sources); + if (shader->vertex_shader == GL_NONE) + goto error_vertex; + + conf = create_shader_config_string(&shader->key); + if (!conf) + goto error_fragment; sources[0] = "#version 100\n"; - sources[1] = attrs; + sources[1] = conf; sources[2] = fragment_shader; shader->fragment_shader = compile_shader(GL_FRAGMENT_SHADER, 3, sources); - free(attrs); if (shader->fragment_shader == GL_NONE) - return -1; + goto error_fragment; shader->program = glCreateProgram(); glAttachShader(shader->program, shader->vertex_shader); @@ -149,9 +198,12 @@ shader_init(struct gl_shader *shader, struct gl_renderer *renderer, if (!status) { glGetProgramInfoLog(shader->program, sizeof msg, NULL, msg); weston_log("link info: %s\n", msg); - return -1; + goto error_link; } + glDeleteShader(shader->vertex_shader); + glDeleteShader(shader->fragment_shader); + shader->proj_uniform = glGetUniformLocation(shader->program, "proj"); shader->tex_uniforms[0] = glGetUniformLocation(shader->program, "tex"); shader->tex_uniforms[1] = glGetUniformLocation(shader->program, "tex1"); @@ -159,49 +211,28 @@ shader_init(struct gl_shader *shader, struct gl_renderer *renderer, shader->alpha_uniform = glGetUniformLocation(shader->program, "alpha"); shader->color_uniform = glGetUniformLocation(shader->program, "color"); - return 0; -} + free(conf); -void -shader_release(struct gl_shader *shader) -{ - glDeleteShader(shader->vertex_shader); - glDeleteShader(shader->fragment_shader); + wl_list_insert(&gr->shader_list, &shader->link); + + return shader; + +error_link: glDeleteProgram(shader->program); + glDeleteShader(shader->fragment_shader); - shader->vertex_shader = 0; - shader->fragment_shader = 0; - shader->program = 0; +error_fragment: + glDeleteShader(shader->vertex_shader); + +error_vertex: + free(conf); + free(shader); + return NULL; } int -compile_shaders(struct weston_compositor *ec) +gl_shader_requirements_cmp(const struct gl_shader_requirements *a, + const struct gl_shader_requirements *b) { - struct gl_renderer *gr = get_renderer(ec); - - gr->texture_shader_rgba.vertex_source = vertex_shader; - gr->texture_shader_rgba.fragment_source = "SHADER_VARIANT_RGBA"; - - gr->texture_shader_rgbx.vertex_source = vertex_shader; - gr->texture_shader_rgbx.fragment_source = "SHADER_VARIANT_RGBX"; - - gr->texture_shader_egl_external.vertex_source = vertex_shader; - gr->texture_shader_egl_external.fragment_source = "SHADER_VARIANT_EXTERNAL"; - - gr->texture_shader_y_uv.vertex_source = vertex_shader; - gr->texture_shader_y_uv.fragment_source = "SHADER_VARIANT_Y_UV"; - - gr->texture_shader_y_u_v.vertex_source = vertex_shader; - gr->texture_shader_y_u_v.fragment_source = "SHADER_VARIANT_Y_U_V"; - - gr->texture_shader_y_xuxv.vertex_source = vertex_shader; - gr->texture_shader_y_xuxv.fragment_source = "SHADER_VARIANT_Y_XUXV"; - - gr->texture_shader_xyuv.vertex_source = vertex_shader; - gr->texture_shader_xyuv.fragment_source = "SHADER_VARIANT_XYUV"; - - gr->solid_shader.vertex_source = vertex_shader; - gr->solid_shader.fragment_source = "SHADER_VARIANT_SOLID"; - - return 0; + return memcmp(a, b, sizeof(*a)); }