aboutsummaryrefslogtreecommitdiff
path: root/render/gles2
diff options
context:
space:
mode:
Diffstat (limited to 'render/gles2')
-rw-r--r--render/gles2/meson.build1
-rw-r--r--render/gles2/pass.c257
-rw-r--r--render/gles2/renderer.c20
3 files changed, 278 insertions, 0 deletions
diff --git a/render/gles2/meson.build b/render/gles2/meson.build
index c9226983..2a6db964 100644
--- a/render/gles2/meson.build
+++ b/render/gles2/meson.build
@@ -8,6 +8,7 @@ features += { 'gles2-renderer': true }
wlr_deps += glesv2
wlr_files += files(
+ 'pass.c',
'pixel_format.c',
'renderer.c',
'texture.c',
diff --git a/render/gles2/pass.c b/render/gles2/pass.c
new file mode 100644
index 00000000..df61d8fd
--- /dev/null
+++ b/render/gles2/pass.c
@@ -0,0 +1,257 @@
+#include <stdlib.h>
+#include <assert.h>
+#include <pixman.h>
+#include <wlr/types/wlr_matrix.h>
+#include "render/gles2.h"
+#include "types/wlr_matrix.h"
+
+#define MAX_QUADS 86 // 4kb
+
+static const struct wlr_render_pass_impl render_pass_impl;
+
+static struct wlr_gles2_render_pass *get_render_pass(struct wlr_render_pass *wlr_pass) {
+ assert(wlr_pass->impl == &render_pass_impl);
+ struct wlr_gles2_render_pass *pass = wl_container_of(wlr_pass, pass, base);
+ return pass;
+}
+
+static bool render_pass_submit(struct wlr_render_pass *wlr_pass) {
+ struct wlr_gles2_render_pass *pass = get_render_pass(wlr_pass);
+ struct wlr_gles2_renderer *renderer = pass->buffer->renderer;
+ push_gles2_debug(renderer);
+ glFlush();
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ pop_gles2_debug(renderer);
+ wlr_buffer_unlock(pass->buffer->buffer);
+ free(pass);
+
+ return true;
+}
+
+static void render(const struct wlr_box *box, const pixman_region32_t *clip, GLint attrib) {
+ pixman_region32_t region;
+ pixman_region32_init_rect(&region, box->x, box->y, box->width, box->height);
+
+ if (clip) {
+ pixman_region32_intersect(&region, &region, clip);
+ }
+
+ int rects_len;
+ const pixman_box32_t *rects = pixman_region32_rectangles(&region, &rects_len);
+ if (rects_len == 0) {
+ pixman_region32_fini(&region);
+ return;
+ }
+
+ glEnableVertexAttribArray(attrib);
+
+ for (int i = 0; i < rects_len;) {
+ int batch = rects_len - i < MAX_QUADS ? rects_len - i : MAX_QUADS;
+ int batch_end = batch + i;
+
+ size_t vert_index = 0;
+ GLfloat verts[MAX_QUADS * 6 * 2];
+ for (; i < batch_end; i++) {
+ const pixman_box32_t *rect = &rects[i];
+
+ verts[vert_index++] = (GLfloat)(rect->x1 - box->x) / box->width;
+ verts[vert_index++] = (GLfloat)(rect->y1 - box->y) / box->height;
+ verts[vert_index++] = (GLfloat)(rect->x2 - box->x) / box->width;
+ verts[vert_index++] = (GLfloat)(rect->y1 - box->y) / box->height;
+ verts[vert_index++] = (GLfloat)(rect->x1 - box->x) / box->width;
+ verts[vert_index++] = (GLfloat)(rect->y2 - box->y) / box->height;
+ verts[vert_index++] = (GLfloat)(rect->x2 - box->x) / box->width;
+ verts[vert_index++] = (GLfloat)(rect->y1 - box->y) / box->height;
+ verts[vert_index++] = (GLfloat)(rect->x2 - box->x) / box->width;
+ verts[vert_index++] = (GLfloat)(rect->y2 - box->y) / box->height;
+ verts[vert_index++] = (GLfloat)(rect->x1 - box->x) / box->width;
+ verts[vert_index++] = (GLfloat)(rect->y2 - box->y) / box->height;
+ }
+
+ glVertexAttribPointer(attrib, 2, GL_FLOAT, GL_FALSE, 0, verts);
+ glDrawArrays(GL_TRIANGLES, 0, batch * 6);
+ }
+
+ glDisableVertexAttribArray(attrib);
+
+ pixman_region32_fini(&region);
+}
+
+static void set_proj_matrix(GLint loc, float proj[9], const struct wlr_box *box) {
+ float gl_matrix[9];
+ wlr_matrix_identity(gl_matrix);
+ wlr_matrix_translate(gl_matrix, box->x, box->y);
+ wlr_matrix_scale(gl_matrix, box->width, box->height);
+ wlr_matrix_multiply(gl_matrix, proj, gl_matrix);
+ glUniformMatrix3fv(loc, 1, GL_FALSE, gl_matrix);
+}
+
+static void set_tex_matrix(GLint loc, enum wl_output_transform trans,
+ const struct wlr_fbox *box) {
+ float tex_matrix[9];
+ wlr_matrix_identity(tex_matrix);
+ wlr_matrix_translate(tex_matrix, box->x, box->y);
+ wlr_matrix_scale(tex_matrix, box->width, box->height);
+ wlr_matrix_translate(tex_matrix, .5, .5);
+
+ // since textures have a different origin point we have to transform
+ // differently if we are rotating
+ if (trans & WL_OUTPUT_TRANSFORM_90) {
+ wlr_matrix_transform(tex_matrix, wlr_output_transform_invert(trans));
+ } else {
+ wlr_matrix_transform(tex_matrix, trans);
+ }
+ wlr_matrix_translate(tex_matrix, -.5, -.5);
+
+ glUniformMatrix3fv(loc, 1, GL_FALSE, tex_matrix);
+}
+
+static void render_pass_add_texture(struct wlr_render_pass *wlr_pass,
+ const struct wlr_render_texture_options *options) {
+ struct wlr_gles2_render_pass *pass = get_render_pass(wlr_pass);
+ struct wlr_gles2_renderer *renderer = pass->buffer->renderer;
+ struct wlr_gles2_texture *texture = gles2_get_texture(options->texture);
+
+ struct wlr_gles2_tex_shader *shader = NULL;
+
+ switch (texture->target) {
+ case GL_TEXTURE_2D:
+ if (texture->has_alpha) {
+ shader = &renderer->shaders.tex_rgba;
+ } else {
+ shader = &renderer->shaders.tex_rgbx;
+ }
+ break;
+ case GL_TEXTURE_EXTERNAL_OES:
+ // EGL_EXT_image_dma_buf_import_modifiers requires
+ // GL_OES_EGL_image_external
+ assert(renderer->exts.OES_egl_image_external);
+ shader = &renderer->shaders.tex_ext;
+ break;
+ default:
+ abort();
+ }
+
+ struct wlr_box dst_box;
+ struct wlr_fbox src_fbox;
+ wlr_render_texture_options_get_src_box(options, &src_fbox);
+ wlr_render_texture_options_get_dst_box(options, &dst_box);
+ float alpha = wlr_render_texture_options_get_alpha(options);
+
+ src_fbox.x /= options->texture->width;
+ src_fbox.y /= options->texture->height;
+ src_fbox.width /= options->texture->width;
+ src_fbox.height /= options->texture->height;
+
+ push_gles2_debug(renderer);
+
+ if (!texture->has_alpha && alpha == 1.0) {
+ glDisable(GL_BLEND);
+ } else {
+ glEnable(GL_BLEND);
+ }
+
+ glUseProgram(shader->program);
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(texture->target, texture->tex);
+ glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glUniform1i(shader->tex, 0);
+ glUniform1f(shader->alpha, alpha);
+ set_proj_matrix(shader->proj, pass->projection_matrix, &dst_box);
+ set_tex_matrix(shader->tex_proj, options->transform, &src_fbox);
+
+ render(&dst_box, options->clip, shader->pos_attrib);
+
+ glBindTexture(texture->target, 0);
+ pop_gles2_debug(renderer);
+}
+
+static void render_pass_add_rect(struct wlr_render_pass *wlr_pass,
+ const struct wlr_render_rect_options *options) {
+ struct wlr_gles2_render_pass *pass = get_render_pass(wlr_pass);
+ struct wlr_gles2_renderer *renderer = pass->buffer->renderer;
+
+ const struct wlr_render_color *color = &options->color;
+ const struct wlr_box *box = &options->box;
+
+ push_gles2_debug(renderer);
+
+ switch (options->blend_mode) {
+ case WLR_RENDER_BLEND_MODE_PREMULTIPLIED:
+ if (color->a == 1.0) {
+ glDisable(GL_BLEND);
+ } else {
+ glEnable(GL_BLEND);
+ }
+ break;
+ case WLR_RENDER_BLEND_MODE_NONE:
+ glDisable(GL_BLEND);
+ break;
+ }
+
+ glUseProgram(renderer->shaders.quad.program);
+
+ set_proj_matrix(renderer->shaders.quad.proj, pass->projection_matrix, &options->box);
+ glUniform4f(renderer->shaders.quad.color, color->r, color->g, color->b, color->a);
+
+ render(box, options->clip, renderer->shaders.quad.pos_attrib);
+
+ pop_gles2_debug(renderer);
+}
+
+static const struct wlr_render_pass_impl render_pass_impl = {
+ .submit = render_pass_submit,
+ .add_texture = render_pass_add_texture,
+ .add_rect = render_pass_add_rect,
+};
+
+static const char *reset_status_str(GLenum status) {
+ switch (status) {
+ case GL_GUILTY_CONTEXT_RESET_KHR:
+ return "guilty";
+ case GL_INNOCENT_CONTEXT_RESET_KHR:
+ return "innocent";
+ case GL_UNKNOWN_CONTEXT_RESET_KHR:
+ return "unknown";
+ default:
+ return "<invalid>";
+ }
+}
+
+struct wlr_gles2_render_pass *begin_gles2_buffer_pass(struct wlr_gles2_buffer *buffer) {
+ struct wlr_gles2_renderer *renderer = buffer->renderer;
+ struct wlr_buffer *wlr_buffer = buffer->buffer;
+
+ if (renderer->procs.glGetGraphicsResetStatusKHR) {
+ GLenum status = renderer->procs.glGetGraphicsResetStatusKHR();
+ if (status != GL_NO_ERROR) {
+ wlr_log(WLR_ERROR, "GPU reset (%s)", reset_status_str(status));
+ wl_signal_emit_mutable(&renderer->wlr_renderer.events.lost, NULL);
+ return NULL;
+ }
+ }
+
+ struct wlr_gles2_render_pass *pass = calloc(1, sizeof(*pass));
+ if (pass == NULL) {
+ return NULL;
+ }
+
+ wlr_render_pass_init(&pass->base, &render_pass_impl);
+ wlr_buffer_lock(wlr_buffer);
+ pass->buffer = buffer;
+
+ matrix_projection(pass->projection_matrix, wlr_buffer->width, wlr_buffer->height,
+ WL_OUTPUT_TRANSFORM_FLIPPED_180);
+
+ push_gles2_debug(renderer);
+ glBindFramebuffer(GL_FRAMEBUFFER, buffer->fbo);
+
+ glViewport(0, 0, wlr_buffer->width, wlr_buffer->height);
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ glDisable(GL_SCISSOR_TEST);
+ glDisable(GL_DEPTH_TEST);
+ pop_gles2_debug(renderer);
+
+ return pass;
+}
diff --git a/render/gles2/renderer.c b/render/gles2/renderer.c
index 48883a2f..414d5feb 100644
--- a/render/gles2/renderer.c
+++ b/render/gles2/renderer.c
@@ -536,6 +536,25 @@ static void gles2_destroy(struct wlr_renderer *wlr_renderer) {
free(renderer);
}
+static struct wlr_render_pass *gles2_begin_buffer_pass(struct wlr_renderer *wlr_renderer,
+ struct wlr_buffer *wlr_buffer) {
+ struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer);
+ if (!wlr_egl_make_current(renderer->egl)) {
+ return NULL;
+ }
+
+ struct wlr_gles2_buffer *buffer = get_or_create_buffer(renderer, wlr_buffer);
+ if (!buffer) {
+ return NULL;
+ }
+
+ struct wlr_gles2_render_pass *pass = begin_gles2_buffer_pass(buffer);
+ if (!pass) {
+ return NULL;
+ }
+ return &pass->base;
+}
+
static const struct wlr_renderer_impl renderer_impl = {
.destroy = gles2_destroy,
.bind_buffer = gles2_bind_buffer,
@@ -553,6 +572,7 @@ static const struct wlr_renderer_impl renderer_impl = {
.get_drm_fd = gles2_get_drm_fd,
.get_render_buffer_caps = gles2_get_render_buffer_caps,
.texture_from_buffer = gles2_texture_from_buffer,
+ .begin_buffer_pass = gles2_begin_buffer_pass,
};
void push_gles2_debug_(struct wlr_gles2_renderer *renderer,