mirror of
https://gitlab.freedesktop.org/wlroots/wlroots.git
synced 2026-04-26 06:46:56 -04:00
color management
This commit is contained in:
parent
842df2bd6c
commit
10bf68b928
13 changed files with 401 additions and 59 deletions
|
|
@ -33,6 +33,7 @@ struct wlr_gles2_pixel_format {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wlr_gles2_tex_shader {
|
struct wlr_gles2_tex_shader {
|
||||||
|
// attribute locations
|
||||||
GLuint program;
|
GLuint program;
|
||||||
GLint proj;
|
GLint proj;
|
||||||
GLint invert_y;
|
GLint invert_y;
|
||||||
|
|
@ -40,6 +41,11 @@ struct wlr_gles2_tex_shader {
|
||||||
GLint alpha;
|
GLint alpha;
|
||||||
GLint pos_attrib;
|
GLint pos_attrib;
|
||||||
GLint tex_attrib;
|
GLint tex_attrib;
|
||||||
|
GLuint color_table;
|
||||||
|
GLint color_enable;
|
||||||
|
|
||||||
|
// source components
|
||||||
|
const GLchar *head, *util, *main;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wlr_gles2_renderer {
|
struct wlr_gles2_renderer {
|
||||||
|
|
@ -110,4 +116,20 @@ void pop_gles2_marker(void);
|
||||||
#define PUSH_GLES2_DEBUG push_gles2_marker(_WLR_FILENAME, __func__)
|
#define PUSH_GLES2_DEBUG push_gles2_marker(_WLR_FILENAME, __func__)
|
||||||
#define POP_GLES2_DEBUG pop_gles2_marker()
|
#define POP_GLES2_DEBUG pop_gles2_marker()
|
||||||
|
|
||||||
|
// color engine
|
||||||
|
|
||||||
|
void color_engine_setup(void);
|
||||||
|
GLuint color_build_lut(struct wlr_color_config *input, struct wlr_color_config *output);
|
||||||
|
void color_convert(struct wlr_color_config *ic, struct wlr_color_config *oc, const float input[static 4], float output[static 4]);
|
||||||
|
|
||||||
|
#define COLOR_LUT_SIZE 64
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ignore half a texel near every edge because those
|
||||||
|
* areas are not interpolated. See GPU Gems 2, Ch. 24 for details.
|
||||||
|
*
|
||||||
|
* offset = (0.5 / COLOR_LUT_SIZE)
|
||||||
|
*/
|
||||||
|
#define COLOR_OFFSET "0.007813"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
19
include/wlr/render/color.h
Normal file
19
include/wlr/render/color.h
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
#ifndef _WLR_COLOR_H
|
||||||
|
#define _WLR_COLOR_H
|
||||||
|
|
||||||
|
struct wlr_color_config {
|
||||||
|
char *icc_profile_path; // not null
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a color config.
|
||||||
|
*
|
||||||
|
* icc_profile_path should not be NULL.
|
||||||
|
* It will be copied into the config.
|
||||||
|
*/
|
||||||
|
struct wlr_color_config *wlr_color_config_load(const char *icc_profile_path);
|
||||||
|
|
||||||
|
struct wlr_color_config *wlr_color_config_copy(struct wlr_color_config *value);
|
||||||
|
void wlr_color_config_free(struct wlr_color_config *config);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -24,6 +24,7 @@ struct wlr_drm_format_set;
|
||||||
|
|
||||||
struct wlr_renderer {
|
struct wlr_renderer {
|
||||||
const struct wlr_renderer_impl *impl;
|
const struct wlr_renderer_impl *impl;
|
||||||
|
struct wlr_color_config *color;
|
||||||
|
|
||||||
bool rendering;
|
bool rendering;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,12 +12,16 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <wayland-server-protocol.h>
|
#include <wayland-server-protocol.h>
|
||||||
#include <wlr/render/dmabuf.h>
|
#include <wlr/render/dmabuf.h>
|
||||||
|
#include <wlr/render/color.h>
|
||||||
|
|
||||||
struct wlr_renderer;
|
struct wlr_renderer;
|
||||||
struct wlr_texture_impl;
|
struct wlr_texture_impl;
|
||||||
|
|
||||||
struct wlr_texture {
|
struct wlr_texture {
|
||||||
const struct wlr_texture_impl *impl;
|
const struct wlr_texture_impl *impl;
|
||||||
|
struct wlr_color_config *color;
|
||||||
|
struct wlr_texture *colored;
|
||||||
|
|
||||||
uint32_t width, height;
|
uint32_t width, height;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -128,6 +128,8 @@ struct wlr_surface {
|
||||||
|
|
||||||
struct wl_listener renderer_destroy;
|
struct wl_listener renderer_destroy;
|
||||||
|
|
||||||
|
struct wlr_color_config *color;
|
||||||
|
|
||||||
void *data;
|
void *data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,7 @@ libinput = dependency('libinput', version: '>=1.9.0')
|
||||||
xkbcommon = dependency('xkbcommon')
|
xkbcommon = dependency('xkbcommon')
|
||||||
udev = dependency('libudev')
|
udev = dependency('libudev')
|
||||||
pixman = dependency('pixman-1')
|
pixman = dependency('pixman-1')
|
||||||
|
lcms2 = dependency('lcms2')
|
||||||
math = cc.find_library('m')
|
math = cc.find_library('m')
|
||||||
rt = cc.find_library('rt')
|
rt = cc.find_library('rt')
|
||||||
|
|
||||||
|
|
@ -128,6 +129,7 @@ wlr_deps = [
|
||||||
xkbcommon,
|
xkbcommon,
|
||||||
udev,
|
udev,
|
||||||
pixman,
|
pixman,
|
||||||
|
lcms2,
|
||||||
math,
|
math,
|
||||||
rt,
|
rt,
|
||||||
]
|
]
|
||||||
|
|
|
||||||
241
render/gles2/color.c
Normal file
241
render/gles2/color.c
Normal file
|
|
@ -0,0 +1,241 @@
|
||||||
|
// OpenGL Color conversion
|
||||||
|
|
||||||
|
#define _XOPEN_SOURCE 500 // for strdup
|
||||||
|
|
||||||
|
#include <GLES3/gl32.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include <wlr/types/wlr_list.h>
|
||||||
|
#include <wlr/render/color.h>
|
||||||
|
|
||||||
|
#include <lcms2.h>
|
||||||
|
|
||||||
|
#include "render/gles2.h"
|
||||||
|
|
||||||
|
// GL3+ or GL_EXT_texture_norm16 on ES 3.2
|
||||||
|
#define GL_RGBA16 0x805B
|
||||||
|
|
||||||
|
struct lut {
|
||||||
|
GLuint tex_id;
|
||||||
|
struct wlr_color_config *input, *output;
|
||||||
|
cmsHTRANSFORM transform;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define LUTS_MAX 100
|
||||||
|
|
||||||
|
static struct wlr_list luts;
|
||||||
|
|
||||||
|
static int color_compare(void *av, void *bv) {
|
||||||
|
struct wlr_color_config *a = av, *b = bv;
|
||||||
|
if(a == b)
|
||||||
|
return 0;
|
||||||
|
if(!a)
|
||||||
|
return 1;
|
||||||
|
if(!b)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return strcmp(a->icc_profile_path, b->icc_profile_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_color_config *wlr_color_config_copy(struct wlr_color_config *value) {
|
||||||
|
if(! value)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
struct wlr_color_config *copy = calloc(1, sizeof(*copy));
|
||||||
|
copy->icc_profile_path = strdup(value->icc_profile_path);
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
static cmsHPROFILE color_load(struct wlr_color_config *color) {
|
||||||
|
if(color) {
|
||||||
|
cmsHPROFILE pro = cmsOpenProfileFromFile (color->icc_profile_path, "r");
|
||||||
|
if (pro == NULL) {
|
||||||
|
wlr_log(WLR_ERROR, "error loading icc profile %s", color->icc_profile_path);
|
||||||
|
} else {
|
||||||
|
return pro;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cmsCreate_sRGBProfile();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *color_describe(struct wlr_color_config *color) {
|
||||||
|
if(! color)
|
||||||
|
return "sRGB";
|
||||||
|
|
||||||
|
char *base = strrchr(color->icc_profile_path, '/');
|
||||||
|
return base ? base + 1 : color->icc_profile_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_color_config *wlr_color_config_load(const char *icc_profile_path) {
|
||||||
|
assert(icc_profile_path);
|
||||||
|
|
||||||
|
if(0 == strlen(icc_profile_path)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool can_access = access(icc_profile_path, F_OK) != -1;
|
||||||
|
if (!can_access) {
|
||||||
|
wlr_log(WLR_ERROR, "Unable to access color profile '%s'", icc_profile_path);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_color_config *color = calloc(1, sizeof(*color));
|
||||||
|
color->icc_profile_path = strdup(icc_profile_path);
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wlr_color_config_free(struct wlr_color_config *color) {
|
||||||
|
if(! color)
|
||||||
|
return;
|
||||||
|
free(color->icc_profile_path);
|
||||||
|
free(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct lut *load_lut(struct wlr_color_config *input, struct wlr_color_config *output) {
|
||||||
|
if(0 == color_compare(input, output))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
struct lut *lut;
|
||||||
|
|
||||||
|
// find an existing cached lut
|
||||||
|
for (size_t i = 0; i < luts.length; i++) {
|
||||||
|
lut = luts.items[i];
|
||||||
|
if(color_compare(lut->input, input) || color_compare(lut->output, output))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// move to head to list
|
||||||
|
if(i > 5) {
|
||||||
|
wlr_list_del(&luts, i);
|
||||||
|
wlr_list_insert(&luts, 0, lut);
|
||||||
|
}
|
||||||
|
return lut;
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean an old LUT to avoid memory leak
|
||||||
|
if(luts.length > LUTS_MAX) {
|
||||||
|
struct lut *lut = wlr_list_pop(&luts);
|
||||||
|
wlr_log(WLR_DEBUG, "destroy CLUT %s -> %s", color_describe(lut->input), color_describe(lut->output));
|
||||||
|
wlr_color_config_free(lut->input);
|
||||||
|
wlr_color_config_free(lut->output);
|
||||||
|
cmsDeleteTransform(lut->transform);
|
||||||
|
glDeleteTextures(1, &lut->tex_id);
|
||||||
|
free(lut);
|
||||||
|
}
|
||||||
|
|
||||||
|
wlr_log(WLR_INFO, "create CLUT %s -> %s", color_describe(input), color_describe(output));
|
||||||
|
cmsHPROFILE inp = color_load(input);
|
||||||
|
cmsHPROFILE outp = color_load(output);
|
||||||
|
|
||||||
|
// According to lcms docs, black point compensation does not apply to perceptual intent, so we omit the flag.
|
||||||
|
cmsHTRANSFORM xform = cmsCreateTransform(
|
||||||
|
inp, TYPE_RGB_16,
|
||||||
|
outp, TYPE_RGB_16,
|
||||||
|
INTENT_PERCEPTUAL,
|
||||||
|
cmsFLAGS_HIGHRESPRECALC | cmsFLAGS_NOCACHE);
|
||||||
|
|
||||||
|
cmsCloseProfile(inp);
|
||||||
|
cmsCloseProfile(outp);
|
||||||
|
|
||||||
|
if (xform == NULL) {
|
||||||
|
wlr_log(WLR_ERROR, "failed to create color transform");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// save the new LUT
|
||||||
|
lut = malloc(sizeof(*lut));
|
||||||
|
*lut = (struct lut){
|
||||||
|
.input = wlr_color_config_copy(input),
|
||||||
|
.output = wlr_color_config_copy(output),
|
||||||
|
.transform = xform,
|
||||||
|
};
|
||||||
|
wlr_list_insert(&luts, 0, lut);
|
||||||
|
return lut;
|
||||||
|
}
|
||||||
|
|
||||||
|
void color_convert(struct wlr_color_config *ic, struct wlr_color_config *oc, const float input[static 4], float output[static 4]) {
|
||||||
|
struct lut *lut = load_lut(ic, oc);
|
||||||
|
if(! lut) {
|
||||||
|
memcpy(output, input, 4 * sizeof(float));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t out[3], in[3] = {
|
||||||
|
input[0] * UINT16_MAX,
|
||||||
|
input[1] * UINT16_MAX,
|
||||||
|
input[2] * UINT16_MAX,
|
||||||
|
};
|
||||||
|
cmsDoTransform(lut->transform, in, out, 1);
|
||||||
|
output[0] = (float)out[0] / UINT16_MAX;
|
||||||
|
output[1] = (float)out[1] / UINT16_MAX;
|
||||||
|
output[2] = (float)out[2] / UINT16_MAX;
|
||||||
|
output[3] = input[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
GLuint color_build_lut(struct wlr_color_config *input, struct wlr_color_config *output) {
|
||||||
|
struct lut *lut = load_lut(input, output);
|
||||||
|
if(! lut)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if(lut->tex_id)
|
||||||
|
return lut->tex_id;
|
||||||
|
|
||||||
|
// 3D array: [B][G][R] = [R,G,B,A]
|
||||||
|
// Notes:
|
||||||
|
// - The first dimension is blue
|
||||||
|
// - The alpha channel is unused but needed since GL_RGB16 does not work.
|
||||||
|
uint16_t table[COLOR_LUT_SIZE][COLOR_LUT_SIZE][COLOR_LUT_SIZE][4];
|
||||||
|
|
||||||
|
int n = COLOR_LUT_SIZE;
|
||||||
|
int r, g, b;
|
||||||
|
for (r = 0; r < n; r++) {
|
||||||
|
for (g = 0; g < n; g++) {
|
||||||
|
for (b = 0; b < n; b++) {
|
||||||
|
uint16_t in[3];
|
||||||
|
in[0] = floor((double) r / (n - 1) * UINT16_MAX + 0.5);
|
||||||
|
in[1] = floor((double) g / (n - 1) * UINT16_MAX + 0.5);
|
||||||
|
in[2] = floor((double) b / (n - 1) * UINT16_MAX + 0.5);
|
||||||
|
|
||||||
|
cmsDoTransform(lut->transform, in, table[b][g][r], 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
glGenTextures (1, &lut->tex_id);
|
||||||
|
glActiveTexture(GL_TEXTURE1);
|
||||||
|
glBindTexture(GL_TEXTURE_3D, lut->tex_id);
|
||||||
|
|
||||||
|
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
||||||
|
|
||||||
|
// we must use linear interpolation for small LUTs
|
||||||
|
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
|
||||||
|
glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA16, n, n, n,
|
||||||
|
0, GL_RGBA, GL_UNSIGNED_SHORT, table);
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
glBindTexture(GL_TEXTURE_3D, 0);
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
|
||||||
|
return lut->tex_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lcms_error_handler(cmsContext ctx, cmsUInt32Number code, const char *msg) {
|
||||||
|
wlr_log(WLR_ERROR, "color management: [%i] %s", code, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void color_engine_setup(void) {
|
||||||
|
wlr_list_init(&luts);
|
||||||
|
cmsSetLogErrorHandler(lcms_error_handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -161,6 +161,15 @@ static bool gles2_render_subtexture_with_matrix(
|
||||||
glEnableVertexAttribArray(shader->pos_attrib);
|
glEnableVertexAttribArray(shader->pos_attrib);
|
||||||
glEnableVertexAttribArray(shader->tex_attrib);
|
glEnableVertexAttribArray(shader->tex_attrib);
|
||||||
|
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
|
||||||
|
GLuint color_table = color_build_lut(wlr_texture->color, wlr_renderer->color);
|
||||||
|
glActiveTexture(GL_TEXTURE1);
|
||||||
|
glBindTexture(GL_TEXTURE_3D, color_table);
|
||||||
|
glUniform1i(shader->color_table, 1);
|
||||||
|
glUniform1i(shader->color_enable, color_table != 0);
|
||||||
|
|
||||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||||
|
|
||||||
glDisableVertexAttribArray(shader->pos_attrib);
|
glDisableVertexAttribArray(shader->pos_attrib);
|
||||||
|
|
@ -182,11 +191,14 @@ static void gles2_render_quad_with_matrix(struct wlr_renderer *wlr_renderer,
|
||||||
float transposition[9];
|
float transposition[9];
|
||||||
wlr_matrix_transpose(transposition, matrix);
|
wlr_matrix_transpose(transposition, matrix);
|
||||||
|
|
||||||
|
float converted[4];
|
||||||
|
color_convert(NULL, wlr_renderer->color, color, converted);
|
||||||
|
|
||||||
PUSH_GLES2_DEBUG;
|
PUSH_GLES2_DEBUG;
|
||||||
glUseProgram(renderer->shaders.quad.program);
|
glUseProgram(renderer->shaders.quad.program);
|
||||||
|
|
||||||
glUniformMatrix3fv(renderer->shaders.quad.proj, 1, GL_FALSE, transposition);
|
glUniformMatrix3fv(renderer->shaders.quad.proj, 1, GL_FALSE, transposition);
|
||||||
glUniform4f(renderer->shaders.quad.color, color[0], color[1], color[2], color[3]);
|
glUniform4f(renderer->shaders.quad.color, converted[0], converted[1], converted[2], converted[3]);
|
||||||
|
|
||||||
glVertexAttribPointer(renderer->shaders.quad.pos_attrib, 2, GL_FLOAT, GL_FALSE,
|
glVertexAttribPointer(renderer->shaders.quad.pos_attrib, 2, GL_FLOAT, GL_FALSE,
|
||||||
0, verts);
|
0, verts);
|
||||||
|
|
@ -217,11 +229,14 @@ static void gles2_render_ellipse_with_matrix(struct wlr_renderer *wlr_renderer,
|
||||||
0, 1, // bottom left
|
0, 1, // bottom left
|
||||||
};
|
};
|
||||||
|
|
||||||
|
float converted[4];
|
||||||
|
color_convert(NULL, wlr_renderer->color, color, converted);
|
||||||
|
|
||||||
PUSH_GLES2_DEBUG;
|
PUSH_GLES2_DEBUG;
|
||||||
glUseProgram(renderer->shaders.ellipse.program);
|
glUseProgram(renderer->shaders.ellipse.program);
|
||||||
|
|
||||||
glUniformMatrix3fv(renderer->shaders.ellipse.proj, 1, GL_FALSE, transposition);
|
glUniformMatrix3fv(renderer->shaders.ellipse.proj, 1, GL_FALSE, transposition);
|
||||||
glUniform4f(renderer->shaders.ellipse.color, color[0], color[1], color[2], color[3]);
|
glUniform4f(renderer->shaders.ellipse.color, converted[0], converted[1], converted[2], converted[3]);
|
||||||
|
|
||||||
glVertexAttribPointer(renderer->shaders.ellipse.pos_attrib, 2, GL_FLOAT,
|
glVertexAttribPointer(renderer->shaders.ellipse.pos_attrib, 2, GL_FLOAT,
|
||||||
GL_FALSE, 0, verts);
|
GL_FALSE, 0, verts);
|
||||||
|
|
@ -258,7 +273,7 @@ static bool gles2_resource_is_wl_drm_buffer(struct wlr_renderer *wlr_renderer,
|
||||||
|
|
||||||
EGLint fmt;
|
EGLint fmt;
|
||||||
return renderer->egl->procs.eglQueryWaylandBufferWL(renderer->egl->display,
|
return renderer->egl->procs.eglQueryWaylandBufferWL(renderer->egl->display,
|
||||||
resource, EGL_TEXTURE_FORMAT, &fmt);
|
resource, EGL_TEXTURE_FORMAT, &fmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gles2_wl_drm_buffer_get_size(struct wlr_renderer *wlr_renderer,
|
static void gles2_wl_drm_buffer_get_size(struct wlr_renderer *wlr_renderer,
|
||||||
|
|
@ -665,9 +680,41 @@ extern const GLchar quad_vertex_src[];
|
||||||
extern const GLchar quad_fragment_src[];
|
extern const GLchar quad_fragment_src[];
|
||||||
extern const GLchar ellipse_fragment_src[];
|
extern const GLchar ellipse_fragment_src[];
|
||||||
extern const GLchar tex_vertex_src[];
|
extern const GLchar tex_vertex_src[];
|
||||||
extern const GLchar tex_fragment_src_rgba[];
|
extern struct wlr_gles2_tex_shader
|
||||||
extern const GLchar tex_fragment_src_rgbx[];
|
tex_fragment_src_rgba,
|
||||||
extern const GLchar tex_fragment_src_external[];
|
tex_fragment_src_rgbx,
|
||||||
|
tex_fragment_src_external;
|
||||||
|
|
||||||
|
// return true on success
|
||||||
|
static bool build_tex_shader(struct wlr_gles2_tex_shader *shader)
|
||||||
|
{
|
||||||
|
GLuint prog;
|
||||||
|
|
||||||
|
char fragsrc[100000] = "";
|
||||||
|
int n = snprintf(fragsrc, sizeof(fragsrc), "%s\n%s\nvoid main() {\n%s\n}\n",
|
||||||
|
shader->head,
|
||||||
|
shader->util,
|
||||||
|
shader->main);
|
||||||
|
|
||||||
|
assert(n < (int)sizeof(fragsrc));
|
||||||
|
|
||||||
|
shader->program = prog = link_program(tex_vertex_src, fragsrc);
|
||||||
|
if (!prog) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
shader->proj = glGetUniformLocation(prog, "proj");
|
||||||
|
shader->invert_y = glGetUniformLocation(prog, "invert_y");
|
||||||
|
shader->tex = glGetUniformLocation(prog, "tex");
|
||||||
|
shader->alpha = glGetUniformLocation(prog, "alpha");
|
||||||
|
shader->pos_attrib = glGetAttribLocation(prog, "pos");
|
||||||
|
shader->tex_attrib = glGetAttribLocation(prog, "texcoord");
|
||||||
|
|
||||||
|
shader->color_table = glGetUniformLocation(prog, "color_table");
|
||||||
|
shader->color_enable = glGetUniformLocation(prog, "color_enable");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) {
|
struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) {
|
||||||
if (!wlr_egl_make_current(egl, EGL_NO_SURFACE, NULL)) {
|
if (!wlr_egl_make_current(egl, EGL_NO_SURFACE, NULL)) {
|
||||||
|
|
@ -695,6 +742,8 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) {
|
||||||
wlr_log(WLR_INFO, "GL renderer: %s", glGetString(GL_RENDERER));
|
wlr_log(WLR_INFO, "GL renderer: %s", glGetString(GL_RENDERER));
|
||||||
wlr_log(WLR_INFO, "Supported GLES2 extensions: %s", exts_str);
|
wlr_log(WLR_INFO, "Supported GLES2 extensions: %s", exts_str);
|
||||||
|
|
||||||
|
color_engine_setup();
|
||||||
|
|
||||||
if (!check_gl_ext(exts_str, "GL_EXT_texture_format_BGRA8888")) {
|
if (!check_gl_ext(exts_str, "GL_EXT_texture_format_BGRA8888")) {
|
||||||
wlr_log(WLR_ERROR, "BGRA8888 format not supported by GLES2");
|
wlr_log(WLR_ERROR, "BGRA8888 format not supported by GLES2");
|
||||||
free(renderer);
|
free(renderer);
|
||||||
|
|
@ -731,9 +780,9 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) {
|
||||||
|
|
||||||
// Silence unwanted message types
|
// Silence unwanted message types
|
||||||
gles2_procs.glDebugMessageControlKHR(GL_DONT_CARE,
|
gles2_procs.glDebugMessageControlKHR(GL_DONT_CARE,
|
||||||
GL_DEBUG_TYPE_POP_GROUP_KHR, GL_DONT_CARE, 0, NULL, GL_FALSE);
|
GL_DEBUG_TYPE_POP_GROUP_KHR, GL_DONT_CARE, 0, NULL, GL_FALSE);
|
||||||
gles2_procs.glDebugMessageControlKHR(GL_DONT_CARE,
|
gles2_procs.glDebugMessageControlKHR(GL_DONT_CARE,
|
||||||
GL_DEBUG_TYPE_PUSH_GROUP_KHR, GL_DONT_CARE, 0, NULL, GL_FALSE);
|
GL_DEBUG_TYPE_PUSH_GROUP_KHR, GL_DONT_CARE, 0, NULL, GL_FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
PUSH_GLES2_DEBUG;
|
PUSH_GLES2_DEBUG;
|
||||||
|
|
@ -758,42 +807,21 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) {
|
||||||
renderer->shaders.ellipse.pos_attrib = glGetAttribLocation(prog, "pos");
|
renderer->shaders.ellipse.pos_attrib = glGetAttribLocation(prog, "pos");
|
||||||
renderer->shaders.ellipse.tex_attrib = glGetAttribLocation(prog, "texcoord");
|
renderer->shaders.ellipse.tex_attrib = glGetAttribLocation(prog, "texcoord");
|
||||||
|
|
||||||
renderer->shaders.tex_rgba.program = prog =
|
renderer->shaders.tex_rgba = tex_fragment_src_rgba;
|
||||||
link_program(tex_vertex_src, tex_fragment_src_rgba);
|
if(! build_tex_shader(&renderer->shaders.tex_rgba)) {
|
||||||
if (!renderer->shaders.tex_rgba.program) {
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
renderer->shaders.tex_rgba.proj = glGetUniformLocation(prog, "proj");
|
|
||||||
renderer->shaders.tex_rgba.invert_y = glGetUniformLocation(prog, "invert_y");
|
|
||||||
renderer->shaders.tex_rgba.tex = glGetUniformLocation(prog, "tex");
|
|
||||||
renderer->shaders.tex_rgba.alpha = glGetUniformLocation(prog, "alpha");
|
|
||||||
renderer->shaders.tex_rgba.pos_attrib = glGetAttribLocation(prog, "pos");
|
|
||||||
renderer->shaders.tex_rgba.tex_attrib = glGetAttribLocation(prog, "texcoord");
|
|
||||||
|
|
||||||
renderer->shaders.tex_rgbx.program = prog =
|
renderer->shaders.tex_rgbx = tex_fragment_src_rgbx;
|
||||||
link_program(tex_vertex_src, tex_fragment_src_rgbx);
|
if(! build_tex_shader(&renderer->shaders.tex_rgbx)) {
|
||||||
if (!renderer->shaders.tex_rgbx.program) {
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
renderer->shaders.tex_rgbx.proj = glGetUniformLocation(prog, "proj");
|
|
||||||
renderer->shaders.tex_rgbx.invert_y = glGetUniformLocation(prog, "invert_y");
|
|
||||||
renderer->shaders.tex_rgbx.tex = glGetUniformLocation(prog, "tex");
|
|
||||||
renderer->shaders.tex_rgbx.alpha = glGetUniformLocation(prog, "alpha");
|
|
||||||
renderer->shaders.tex_rgbx.pos_attrib = glGetAttribLocation(prog, "pos");
|
|
||||||
renderer->shaders.tex_rgbx.tex_attrib = glGetAttribLocation(prog, "texcoord");
|
|
||||||
|
|
||||||
if (renderer->exts.egl_image_external_oes) {
|
if (renderer->exts.egl_image_external_oes) {
|
||||||
renderer->shaders.tex_ext.program = prog =
|
renderer->shaders.tex_ext = tex_fragment_src_external;
|
||||||
link_program(tex_vertex_src, tex_fragment_src_external);
|
if(! build_tex_shader(&renderer->shaders.tex_ext)) {
|
||||||
if (!renderer->shaders.tex_ext.program) {
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
renderer->shaders.tex_ext.proj = glGetUniformLocation(prog, "proj");
|
|
||||||
renderer->shaders.tex_ext.invert_y = glGetUniformLocation(prog, "invert_y");
|
|
||||||
renderer->shaders.tex_ext.tex = glGetUniformLocation(prog, "tex");
|
|
||||||
renderer->shaders.tex_ext.alpha = glGetUniformLocation(prog, "alpha");
|
|
||||||
renderer->shaders.tex_ext.pos_attrib = glGetAttribLocation(prog, "pos");
|
|
||||||
renderer->shaders.tex_ext.tex_attrib = glGetAttribLocation(prog, "texcoord");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
POP_GLES2_DEBUG;
|
POP_GLES2_DEBUG;
|
||||||
|
|
|
||||||
|
|
@ -56,33 +56,51 @@ const GLchar tex_vertex_src[] =
|
||||||
" }\n"
|
" }\n"
|
||||||
"}\n";
|
"}\n";
|
||||||
|
|
||||||
const GLchar tex_fragment_src_rgba[] =
|
static const GLchar tex_fragment_head[] =
|
||||||
|
"#extension GL_OES_texture_3D : require\n"
|
||||||
|
"\n"
|
||||||
"precision mediump float;\n"
|
"precision mediump float;\n"
|
||||||
"varying vec2 v_texcoord;\n"
|
"varying vec2 v_texcoord;\n"
|
||||||
"uniform sampler2D tex;\n"
|
"uniform sampler2D tex;\n"
|
||||||
"uniform float alpha;\n"
|
"uniform float alpha;\n";
|
||||||
"\n"
|
|
||||||
"void main() {\n"
|
|
||||||
" gl_FragColor = texture2D(tex, v_texcoord) * alpha;\n"
|
|
||||||
"}\n";
|
|
||||||
|
|
||||||
const GLchar tex_fragment_src_rgbx[] =
|
static const GLchar tex_fragment_util[] =
|
||||||
"precision mediump float;\n"
|
"uniform mediump sampler3D color_table;\n"
|
||||||
"varying vec2 v_texcoord;\n"
|
"uniform bool color_enable;\n"
|
||||||
"uniform sampler2D tex;\n"
|
"#define offset " COLOR_OFFSET "\n"
|
||||||
"uniform float alpha;\n"
|
|
||||||
"\n"
|
"\n"
|
||||||
"void main() {\n"
|
"#ifdef GL_OES_texture_3D\n"
|
||||||
" gl_FragColor = vec4(texture2D(tex, v_texcoord).rgb, 1.0) * alpha;\n"
|
"vec4 lookup(vec4 c) {\n"
|
||||||
"}\n";
|
" if(! color_enable)\n"
|
||||||
|
" return c;\n"
|
||||||
|
" vec4 m = texture3D(color_table, offset + c.rgb * (1.0 - offset*2.0));\n"
|
||||||
|
" return vec4(m.rgb, c.a);\n"
|
||||||
|
"}\n"
|
||||||
|
"#else\n"
|
||||||
|
"#define lookup(c) c\n"
|
||||||
|
"#endif\n"
|
||||||
|
;
|
||||||
|
|
||||||
const GLchar tex_fragment_src_external[] =
|
struct wlr_gles2_tex_shader tex_fragment_src_rgba = {
|
||||||
"#extension GL_OES_EGL_image_external : require\n\n"
|
.head = tex_fragment_head,
|
||||||
"precision mediump float;\n"
|
.util = tex_fragment_util,
|
||||||
"varying vec2 v_texcoord;\n"
|
.main = "gl_FragColor = lookup(texture2D(tex, v_texcoord)) * alpha;"
|
||||||
"uniform samplerExternalOES texture0;\n"
|
};
|
||||||
"uniform float alpha;\n"
|
|
||||||
"\n"
|
struct wlr_gles2_tex_shader tex_fragment_src_rgbx = {
|
||||||
"void main() {\n"
|
.head = tex_fragment_head,
|
||||||
" gl_FragColor = texture2D(texture0, v_texcoord) * alpha;\n"
|
.util = tex_fragment_util,
|
||||||
"}\n";
|
.main = "gl_FragColor = lookup(vec4(texture2D(tex, v_texcoord).rgb, 1.0)) * alpha;"
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wlr_gles2_tex_shader tex_fragment_src_external = {
|
||||||
|
.head = "#extension GL_OES_EGL_image_external : require\n"
|
||||||
|
"#extension GL_OES_texture_3D : enable\n"
|
||||||
|
"\n"
|
||||||
|
"precision mediump float;\n"
|
||||||
|
"varying vec2 v_texcoord;\n"
|
||||||
|
"uniform samplerExternalOES texture0;\n"
|
||||||
|
"uniform float alpha;\n",
|
||||||
|
.util = tex_fragment_util,
|
||||||
|
.main = "gl_FragColor = lookup(texture2D(texture0, v_texcoord)) * alpha;\n"
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -137,6 +137,7 @@ static const struct wlr_texture_impl texture_impl = {
|
||||||
struct wlr_texture *wlr_gles2_texture_from_pixels(struct wlr_egl *egl,
|
struct wlr_texture *wlr_gles2_texture_from_pixels(struct wlr_egl *egl,
|
||||||
enum wl_shm_format wl_fmt, uint32_t stride, uint32_t width,
|
enum wl_shm_format wl_fmt, uint32_t stride, uint32_t width,
|
||||||
uint32_t height, const void *data) {
|
uint32_t height, const void *data) {
|
||||||
|
|
||||||
wlr_egl_make_current(egl, EGL_NO_SURFACE, NULL);
|
wlr_egl_make_current(egl, EGL_NO_SURFACE, NULL);
|
||||||
|
|
||||||
const struct wlr_gles2_pixel_format *fmt = get_gles2_format_from_wl(wl_fmt);
|
const struct wlr_gles2_pixel_format *fmt = get_gles2_format_from_wl(wl_fmt);
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ wlr_files += files(
|
||||||
'gles2/renderer.c',
|
'gles2/renderer.c',
|
||||||
'gles2/shaders.c',
|
'gles2/shaders.c',
|
||||||
'gles2/texture.c',
|
'gles2/texture.c',
|
||||||
|
'gles2/color.c',
|
||||||
'wlr_renderer.c',
|
'wlr_renderer.c',
|
||||||
'wlr_texture.c',
|
'wlr_texture.c',
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,8 @@ void wlr_renderer_destroy(struct wlr_renderer *r) {
|
||||||
}
|
}
|
||||||
wlr_signal_emit_safe(&r->events.destroy, r);
|
wlr_signal_emit_safe(&r->events.destroy, r);
|
||||||
|
|
||||||
|
wlr_color_config_free(r->color);
|
||||||
|
|
||||||
if (r->impl && r->impl->destroy) {
|
if (r->impl && r->impl->destroy) {
|
||||||
r->impl->destroy(r);
|
r->impl->destroy(r);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -697,6 +697,7 @@ struct wlr_texture *wlr_surface_get_texture(struct wlr_surface *surface) {
|
||||||
if (surface->buffer == NULL) {
|
if (surface->buffer == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
surface->buffer->texture->color = surface->color;
|
||||||
return surface->buffer->texture;
|
return surface->buffer->texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue