gtk/gsk/gskglrenderer.c
Emmanuele Bassi b98fb07419 gsk: Enable depth testing
Use the appropriate depth testing function for transparent items.
2016-10-18 11:49:08 +01:00

926 lines
26 KiB
C

#include "config.h"
#include "gskglrendererprivate.h"
#include "gskdebugprivate.h"
#include "gskenums.h"
#include "gskgldriverprivate.h"
#include "gskrendererprivate.h"
#include "gskrendernodeprivate.h"
#include "gskrendernodeiter.h"
#include "gskshaderbuilderprivate.h"
#include "gskprivate.h"
#include <epoxy/gl.h>
#define SHADER_VERSION_GLES 110
#define SHADER_VERSION_GL_LEGACY 120
#define SHADER_VERSION_GL3 150
typedef struct {
guint vao_id;
guint buffer_id;
guint texture_id;
guint program_id;
guint mvp_location;
guint map_location;
guint parentMap_location;
guint uv_location;
guint position_location;
guint alpha_location;
guint blendMode_location;
} RenderData;
typedef struct {
/* Back pointer to the node, only meant for comparison */
GskRenderNode *node;
graphene_point3d_t min;
graphene_point3d_t max;
graphene_size_t size;
graphene_matrix_t mvp;
gboolean opaque : 1;
float opacity;
float z;
const char *name;
GskBlendMode blend_mode;
RenderData render_data;
RenderData *parent_data;
} RenderItem;
enum {
MVP,
MAP,
PARENT_MAP,
ALPHA,
BLEND_MODE,
N_UNIFORMS
};
enum {
POSITION,
UV,
N_ATTRIBUTES
};
struct _GskGLRenderer
{
GskRenderer parent_instance;
GdkGLContext *context;
graphene_matrix_t mvp;
graphene_frustum_t frustum;
guint frame_buffer;
guint render_buffer;
guint depth_stencil_buffer;
guint texture_id;
GQuark uniforms[N_UNIFORMS];
GQuark attributes[N_ATTRIBUTES];
GskGLDriver *gl_driver;
GskShaderBuilder *shader_builder;
int gl_min_filter;
int gl_mag_filter;
int blend_program_id;
int blit_program_id;
guint vao_id;
GArray *opaque_render_items;
GArray *transparent_render_items;
gboolean has_buffers : 1;
gboolean has_alpha : 1;
gboolean has_stencil_buffer : 1;
gboolean has_depth_buffer : 1;
};
struct _GskGLRendererClass
{
GskRendererClass parent_class;
};
G_DEFINE_TYPE (GskGLRenderer, gsk_gl_renderer, GSK_TYPE_RENDERER)
static void
gsk_gl_renderer_dispose (GObject *gobject)
{
GskGLRenderer *self = GSK_GL_RENDERER (gobject);
g_clear_object (&self->context);
G_OBJECT_CLASS (gsk_gl_renderer_parent_class)->dispose (gobject);
}
static void
gsk_gl_renderer_create_buffers (GskGLRenderer *self)
{
if (self->has_buffers)
return;
GSK_NOTE (OPENGL, g_print ("Creating buffers\n"));
glGenFramebuffersEXT (1, &self->frame_buffer);
if (gsk_renderer_get_use_alpha (GSK_RENDERER (self)))
{
if (self->texture_id == 0)
glGenTextures (1, &self->texture_id);
if (self->render_buffer != 0)
{
glDeleteRenderbuffersEXT (1, &self->render_buffer);
self->render_buffer = 0;
}
}
else
{
if (self->render_buffer == 0)
glGenRenderbuffersEXT (1, &self->render_buffer);
if (self->texture_id != 0)
{
glDeleteTextures (1, &self->texture_id);
self->texture_id = 0;
}
}
if (self->has_depth_buffer || self->has_stencil_buffer)
{
if (self->depth_stencil_buffer == 0)
glGenRenderbuffersEXT (1, &self->depth_stencil_buffer);
}
else
{
if (self->depth_stencil_buffer != 0)
{
glDeleteRenderbuffersEXT (1, &self->depth_stencil_buffer);
self->depth_stencil_buffer = 0;
}
}
/* We only have one VAO at the moment */
glGenVertexArrays (1, &self->vao_id);
glBindVertexArray (self->vao_id);
self->has_buffers = TRUE;
}
static void
gsk_gl_renderer_allocate_buffers (GskGLRenderer *self,
int width,
int height)
{
if (self->context == NULL)
return;
if (self->texture_id != 0)
{
glBindTexture (GL_TEXTURE_2D, self->texture_id);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
if (gdk_gl_context_get_use_es (self->context))
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
else
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
}
if (self->render_buffer != 0)
{
glBindRenderbuffer (GL_RENDERBUFFER, self->render_buffer);
glRenderbufferStorage (GL_RENDERBUFFER, GL_RGB8, width, height);
}
if (self->has_depth_buffer || self->has_stencil_buffer)
{
glBindRenderbuffer (GL_RENDERBUFFER, self->depth_stencil_buffer);
if (self->has_stencil_buffer)
glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
else
glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height);
}
}
static void
gsk_gl_renderer_attach_buffers (GskGLRenderer *self)
{
gsk_gl_renderer_create_buffers (self);
GSK_NOTE (OPENGL, g_print ("Attaching buffers\n"));
glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, self->frame_buffer);
if (self->texture_id != 0)
{
glFramebufferTexture2D (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
GL_TEXTURE_2D, self->texture_id, 0);
}
else if (self->render_buffer != 0)
{
glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
GL_RENDERBUFFER_EXT, self->render_buffer);
}
if (self->depth_stencil_buffer != 0)
{
if (self->has_depth_buffer)
glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
GL_RENDERBUFFER_EXT, self->depth_stencil_buffer);
if (self->has_stencil_buffer)
glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
GL_RENDERBUFFER_EXT, self->depth_stencil_buffer);
}
}
static void
gsk_gl_renderer_destroy_buffers (GskGLRenderer *self)
{
if (self->context == NULL)
return;
if (!self->has_buffers)
return;
GSK_NOTE (OPENGL, g_print ("Destroying buffers\n"));
gdk_gl_context_make_current (self->context);
if (self->vao_id != 0)
{
glDeleteVertexArrays (1, &self->vao_id);
self->vao_id = 0;
}
if (self->depth_stencil_buffer != 0)
{
glDeleteRenderbuffersEXT (1, &self->depth_stencil_buffer);
self->depth_stencil_buffer = 0;
}
if (self->render_buffer != 0)
{
glDeleteRenderbuffersEXT (1, &self->render_buffer);
self->render_buffer = 0;
}
if (self->texture_id != 0)
{
glDeleteTextures (1, &self->texture_id);
self->texture_id = 0;
}
if (self->frame_buffer != 0)
{
glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);
glDeleteFramebuffersEXT (1, &self->frame_buffer);
self->frame_buffer = 0;
}
self->has_buffers = FALSE;
}
static gboolean
gsk_gl_renderer_create_programs (GskGLRenderer *self)
{
GskShaderBuilder *builder;
GError *error = NULL;
gboolean res = FALSE;
builder = gsk_shader_builder_new ();
gsk_shader_builder_set_resource_base_path (builder, "/org/gtk/libgsk/glsl");
self->uniforms[MVP] = gsk_shader_builder_add_uniform (builder, "mvp");
self->uniforms[MAP] = gsk_shader_builder_add_uniform (builder, "map");
self->uniforms[PARENT_MAP] = gsk_shader_builder_add_uniform (builder, "parentMap");
self->uniforms[ALPHA] = gsk_shader_builder_add_uniform (builder, "alpha");
self->uniforms[BLEND_MODE] = gsk_shader_builder_add_uniform (builder, "blendMode");
self->attributes[POSITION] = gsk_shader_builder_add_attribute (builder, "position");
self->attributes[UV] = gsk_shader_builder_add_attribute (builder, "uv");
if (gdk_gl_context_get_use_es (self->context))
{
gsk_shader_builder_set_version (builder, SHADER_VERSION_GLES);
gsk_shader_builder_set_vertex_preamble (builder, "es2_common.vs.glsl");
gsk_shader_builder_set_fragment_preamble (builder, "es2_common.fs.glsl");
gsk_shader_builder_add_define (builder, "GSK_GLES", "1");
}
else if (gdk_gl_context_is_legacy (self->context))
{
gsk_shader_builder_set_version (builder, SHADER_VERSION_GL_LEGACY);
gsk_shader_builder_set_vertex_preamble (builder, "gl_common.vs.glsl");
gsk_shader_builder_set_fragment_preamble (builder, "gl_common.fs.glsl");
gsk_shader_builder_add_define (builder, "GSK_LEGACY", "1");
}
else
{
gsk_shader_builder_set_version (builder, SHADER_VERSION_GL3);
gsk_shader_builder_set_vertex_preamble (builder, "gl3_common.vs.glsl");
gsk_shader_builder_set_fragment_preamble (builder, "gl3_common.fs.glsl");
gsk_shader_builder_add_define (builder, "GSK_GL3", "1");
}
#ifdef G_ENABLE_DEBUG
if (GSK_RENDER_MODE_CHECK (SHADERS))
gsk_shader_builder_add_define (builder, "GSK_DEBUG", "1");
#endif
self->blend_program_id =
gsk_shader_builder_create_program (builder, "blend.vs.glsl", "blend.fs.glsl", &error);
if (error != NULL)
{
g_critical ("Unable to create 'blend' program: %s", error->message);
g_error_free (error);
g_object_unref (builder);
goto out;
}
self->blit_program_id =
gsk_shader_builder_create_program (builder, "blit.vs.glsl", "blit.fs.glsl", &error);
if (error != NULL)
{
g_critical ("Unable to create 'blit' program: %s", error->message);
g_error_free (error);
g_object_unref (builder);
goto out;
}
/* Keep a pointer to query for the uniform and attribute locations
* when rendering the scene
*/
self->shader_builder = builder;
res = TRUE;
out:
return res;
}
static void
gsk_gl_renderer_destroy_programs (GskGLRenderer *self)
{
g_clear_object (&self->shader_builder);
}
static gboolean
gsk_gl_renderer_realize (GskRenderer *renderer)
{
GskGLRenderer *self = GSK_GL_RENDERER (renderer);
GError *error = NULL;
/* If we didn't get a GdkGLContext before realization, try creating
* one now, for our exclusive use.
*/
if (self->context == NULL)
{
GdkWindow *window = gsk_renderer_get_window (renderer);
if (window == NULL)
return FALSE;
self->context = gdk_window_create_gl_context (window, &error);
if (error != NULL)
{
g_critical ("Unable to create GL context for renderer: %s",
error->message);
g_error_free (error);
return FALSE;
}
}
gdk_gl_context_realize (self->context, &error);
if (error != NULL)
{
g_critical ("Unable to realize GL renderer: %s", error->message);
g_error_free (error);
return FALSE;
}
gdk_gl_context_make_current (self->context);
g_assert (self->gl_driver == NULL);
self->gl_driver = gsk_gl_driver_new (self->context);
GSK_NOTE (OPENGL, g_print ("Creating buffers and programs\n"));
if (!gsk_gl_renderer_create_programs (self))
return FALSE;
gsk_gl_renderer_create_buffers (self);
return TRUE;
}
static void
gsk_gl_renderer_unrealize (GskRenderer *renderer)
{
GskGLRenderer *self = GSK_GL_RENDERER (renderer);
if (self->context == NULL)
return;
gdk_gl_context_make_current (self->context);
g_clear_pointer (&self->opaque_render_items, g_array_unref);
g_clear_pointer (&self->transparent_render_items, g_array_unref);
gsk_gl_renderer_destroy_buffers (self);
gsk_gl_renderer_destroy_programs (self);
g_clear_object (&self->gl_driver);
if (self->context == gdk_gl_context_get_current ())
gdk_gl_context_clear_current ();
}
static void
gsk_gl_renderer_resize_viewport (GskGLRenderer *self,
const graphene_rect_t *viewport)
{
GSK_NOTE (OPENGL, g_print ("glViewport(0, 0, %g, %g)\n",
viewport->size.width,
viewport->size.height));
glViewport (viewport->origin.x, viewport->origin.y,
viewport->size.width, viewport->size.height);
}
static void
gsk_gl_renderer_update_frustum (GskGLRenderer *self,
const graphene_matrix_t *modelview,
const graphene_matrix_t *projection)
{
GSK_NOTE (OPENGL, g_print ("Updating the modelview/projection\n"));
graphene_matrix_multiply (projection, modelview, &self->mvp);
graphene_frustum_init_from_matrix (&self->frustum, &self->mvp);
GSK_NOTE (OPENGL, g_print ("Renderer MVP:\n"));
GSK_NOTE (OPENGL, graphene_matrix_print (&self->mvp));
}
#define N_VERTICES 6
static void
render_item (GskGLRenderer *self,
RenderItem *item)
{
float mvp[16];
gsk_gl_driver_bind_vao (self->gl_driver, item->render_data.vao_id);
glUseProgram (item->render_data.program_id);
/* Use texture unit 0 for the source */
glUniform1i (item->render_data.map_location, 0);
gsk_gl_driver_bind_source_texture (self->gl_driver, item->render_data.texture_id);
if (item->parent_data != NULL)
{
glUniform1i (item->render_data.blendMode_location, item->blend_mode);
/* Use texture unit 1 for the mask */
if (item->parent_data->texture_id != 0)
gsk_gl_driver_bind_mask_texture (self->gl_driver, item->parent_data->texture_id);
}
/* Pass the opacity component */
glUniform1f (item->render_data.alpha_location, item->opaque ? 1 : item->opacity);
/* Pass the mvp to the vertex shader */
GSK_NOTE (OPENGL, graphene_matrix_print (&item->mvp));
graphene_matrix_to_float (&item->mvp, mvp);
glUniformMatrix4fv (item->render_data.mvp_location, 1, GL_FALSE, mvp);
/* Draw the quad */
GSK_NOTE (OPENGL, g_print ("Drawing item <%s>[%p] with opacity: %g\n",
item->name,
item,
item->opaque ? 1 : item->opacity));
glDrawArrays (GL_TRIANGLES, 0, N_VERTICES);
}
static void
get_gl_scaling_filters (GskRenderer *renderer,
int *min_filter_r,
int *mag_filter_r)
{
GskScalingFilter min_filter, mag_filter;
gsk_renderer_get_scaling_filters (renderer, &min_filter, &mag_filter);
switch (min_filter)
{
case GSK_SCALING_FILTER_NEAREST:
*min_filter_r = GL_NEAREST;
break;
case GSK_SCALING_FILTER_LINEAR:
*min_filter_r = GL_LINEAR;
break;
case GSK_SCALING_FILTER_TRILINEAR:
*min_filter_r = GL_LINEAR_MIPMAP_LINEAR;
break;
}
switch (mag_filter)
{
case GSK_SCALING_FILTER_NEAREST:
*mag_filter_r = GL_NEAREST;
break;
/* There's no point in using anything above GL_LINEAR for
* magnification filters
*/
case GSK_SCALING_FILTER_LINEAR:
case GSK_SCALING_FILTER_TRILINEAR:
*mag_filter_r = GL_LINEAR;
break;
}
}
#if 0
static gboolean
check_in_frustum (const graphene_frustum_t *frustum,
RenderItem *item)
{
graphene_box_t aabb;
graphene_box_init (&aabb, &item->min, &item->max);
graphene_matrix_transform_box (&item->mvp, &aabb, &aabb);
return graphene_frustum_intersects_box (frustum, &aabb);
}
#endif
static float
project_item (const graphene_matrix_t *projection,
const graphene_matrix_t *modelview)
{
graphene_vec4_t vec;
graphene_matrix_get_row (modelview, 3, &vec);
graphene_matrix_transform_vec4 (projection, &vec, &vec);
return graphene_vec4_get_z (&vec) / graphene_vec4_get_w (&vec);
}
static void
gsk_gl_renderer_add_render_item (GskGLRenderer *self,
GskRenderNode *node,
RenderItem *parent)
{
graphene_rect_t viewport;
cairo_surface_t *surface;
GskRenderNodeIter iter;
graphene_matrix_t mv, projection;
graphene_rect_t bounds;
GskRenderNode *child;
RenderItem item;
RenderItem *ritem = NULL;
int program_id;
if (gsk_render_node_is_hidden (node))
{
GSK_NOTE (OPENGL, g_print ("Skipping hidden node <%s>[%p]\n",
node->name != NULL ? node->name : "unnamed",
node));
return;
}
memset (&item, 0, sizeof (RenderItem));
gsk_renderer_get_viewport (GSK_RENDERER (self), &viewport);
gsk_render_node_get_bounds (node, &bounds);
item.node = node;
item.name = node->name != NULL ? node->name : "unnamed";
/* The texture size */
item.size = bounds.size;
/* Each render item is an axis-aligned bounding box that we
* transform using the given transformation matrix
*/
item.min.x = bounds.origin.x;
item.min.y = bounds.origin.y;
item.min.z = 0.f;
item.max.x = bounds.origin.x + bounds.size.width;
item.max.y = bounds.origin.y + bounds.size.height;
item.max.z = 0.f;
/* The location of the item, in normalized world coordinates */
gsk_render_node_get_world_matrix (node, &mv);
graphene_matrix_multiply (&mv, &self->mvp, &item.mvp);
item.opaque = gsk_render_node_is_opaque (node);
item.opacity = gsk_render_node_get_opacity (node);
item.blend_mode = gsk_render_node_get_blend_mode (node);
/* Back-pointer to the parent node */
if (parent != NULL)
item.parent_data = &(parent->render_data);
else
item.parent_data = NULL;
/* Select the program to use */
if (parent != NULL)
program_id = self->blend_program_id;
else
program_id = self->blit_program_id;
item.render_data.program_id = program_id;
/* Retrieve all the uniforms and attributes */
item.render_data.map_location =
gsk_shader_builder_get_uniform_location (self->shader_builder, program_id, self->uniforms[MAP]);
item.render_data.parentMap_location =
gsk_shader_builder_get_uniform_location (self->shader_builder, program_id, self->uniforms[PARENT_MAP]);
item.render_data.mvp_location =
gsk_shader_builder_get_uniform_location (self->shader_builder, program_id, self->uniforms[MVP]);
item.render_data.alpha_location =
gsk_shader_builder_get_uniform_location (self->shader_builder, program_id, self->uniforms[ALPHA]);
item.render_data.blendMode_location =
gsk_shader_builder_get_uniform_location (self->shader_builder, program_id, self->uniforms[BLEND_MODE]);
item.render_data.position_location =
gsk_shader_builder_get_attribute_location (self->shader_builder, program_id, self->attributes[POSITION]);
item.render_data.uv_location =
gsk_shader_builder_get_attribute_location (self->shader_builder, program_id, self->attributes[UV]);
/* Create the vertex buffers holding the geometry of the quad */
{
GskQuadVertex vertex_data[N_VERTICES] = {
{ { item.min.x, item.min.y }, { 0, 0 }, },
{ { item.min.x, item.max.y }, { 0, 1 }, },
{ { item.max.x, item.min.y }, { 1, 0 }, },
{ { item.max.x, item.max.y }, { 1, 1 }, },
{ { item.min.x, item.max.y }, { 0, 1 }, },
{ { item.max.x, item.min.y }, { 1, 0 }, },
};
item.render_data.vao_id =
gsk_gl_driver_create_vao_for_quad (self->gl_driver,
item.render_data.position_location,
item.render_data.uv_location,
sizeof (GskQuadVertex) * N_VERTICES,
vertex_data);
}
gsk_renderer_get_projection (GSK_RENDERER (self), &projection);
item.z = project_item (&projection, &mv);
/* TODO: This should really be an asset atlas, to avoid uploading a ton
* of textures. Ideally we could use a single Cairo surface to get around
* the GL texture limits and reorder the texture data on the CPU side and
* do a single upload; alternatively, we could use a separate FBO and
* render each texture into it
*/
surface = gsk_render_node_get_surface (node);
/* If the node does not have any surface we skip drawing it, but we still
* recurse.
*
* XXX: This needs to be re-done if the opacity is != 0, in which case we
* need to composite the opacity level of the children
*/
if (surface == NULL)
goto recurse_children;
/* Upload the Cairo surface to a GL texture */
item.render_data.texture_id = gsk_gl_driver_create_texture (self->gl_driver,
bounds.size.width,
bounds.size.height,
self->gl_min_filter,
self->gl_mag_filter,
surface);
GSK_NOTE (OPENGL, g_print ("Adding node <%s>[%p] to render items\n",
node->name != NULL ? node->name : "unnamed",
node));
if (gsk_render_node_is_opaque (node) && gsk_render_node_get_opacity (node) == 1.f)
{
g_array_append_val (self->opaque_render_items, item);
ritem = &g_array_index (self->opaque_render_items,
RenderItem,
self->opaque_render_items->len - 1);
}
else
{
g_array_append_val (self->transparent_render_items, item);
ritem = &g_array_index (self->transparent_render_items,
RenderItem,
self->transparent_render_items->len - 1);
}
recurse_children:
gsk_render_node_iter_init (&iter, node);
while (gsk_render_node_iter_next (&iter, &child))
gsk_gl_renderer_add_render_item (self, child, ritem);
}
static gboolean
gsk_gl_renderer_validate_tree (GskGLRenderer *self,
GskRenderNode *root)
{
int n_nodes;
if (self->context == NULL)
{
GSK_NOTE (OPENGL, g_print ("No valid GL context associated to the renderer"));
return FALSE;
}
n_nodes = gsk_render_node_get_size (root);
gdk_gl_context_make_current (self->context);
self->opaque_render_items = g_array_sized_new (FALSE, FALSE, sizeof (RenderItem), n_nodes);
self->transparent_render_items = g_array_sized_new (FALSE, FALSE, sizeof (RenderItem), n_nodes);
GSK_NOTE (OPENGL, g_print ("RenderNode -> RenderItem\n"));
gsk_gl_renderer_add_render_item (self, root, NULL);
GSK_NOTE (OPENGL, g_print ("Total render items: %d of %d (opaque:%d, transparent:%d)\n",
self->opaque_render_items->len + self->transparent_render_items->len,
n_nodes,
self->opaque_render_items->len,
self->transparent_render_items->len));
return TRUE;
}
static void
gsk_gl_renderer_clear_tree (GskGLRenderer *self)
{
if (self->context == NULL)
return;
g_clear_pointer (&self->opaque_render_items, g_array_unref);
g_clear_pointer (&self->transparent_render_items, g_array_unref);
}
static void
gsk_gl_renderer_clear (GskGLRenderer *self)
{
int clear_bits = GL_COLOR_BUFFER_BIT;
if (self->has_depth_buffer)
clear_bits |= GL_DEPTH_BUFFER_BIT;
if (self->has_stencil_buffer)
clear_bits |= GL_STENCIL_BUFFER_BIT;
GSK_NOTE (OPENGL, g_print ("Clearing viewport\n"));
glClearColor (0, 0, 0, 0);
glClear (clear_bits);
}
static void
gsk_gl_renderer_render (GskRenderer *renderer,
GskRenderNode *root,
GdkDrawingContext *context)
{
GskGLRenderer *self = GSK_GL_RENDERER (renderer);
graphene_matrix_t modelview, projection;
graphene_rect_t viewport;
gboolean use_alpha;
int status;
guint i;
if (self->context == NULL)
return;
gdk_gl_context_make_current (self->context);
gsk_renderer_get_viewport (renderer, &viewport);
gsk_gl_renderer_create_buffers (self);
gsk_gl_renderer_allocate_buffers (self, viewport.size.width, viewport.size.height);
gsk_gl_renderer_attach_buffers (self);
gsk_renderer_get_modelview (renderer, &modelview);
gsk_renderer_get_projection (renderer, &projection);
gsk_gl_renderer_update_frustum (self, &modelview, &projection);
get_gl_scaling_filters (GSK_RENDERER (self),
&self->gl_min_filter,
&self->gl_mag_filter);
if (!gsk_gl_renderer_validate_tree (self, root))
goto out;
gsk_gl_driver_begin_frame (self->gl_driver);
/* Ensure that the viewport is up to date */
status = glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT);
if (status == GL_FRAMEBUFFER_COMPLETE_EXT)
gsk_gl_renderer_resize_viewport (self, &viewport);
gsk_gl_renderer_clear (self);
glDisable (GL_BLEND);
if (self->has_depth_buffer)
{
glEnable (GL_DEPTH_TEST);
glDepthFunc (GL_LESS);
}
else
glDisable (GL_DEPTH_TEST);
/* Opaque pass: front-to-back */
GSK_NOTE (OPENGL, g_print ("Rendering %u opaque items\n", self->opaque_render_items->len));
for (i = 0; i < self->opaque_render_items->len; i++)
{
RenderItem *item = &g_array_index (self->opaque_render_items, RenderItem, i);
render_item (self, item);
}
if (self->has_depth_buffer)
{
glEnable (GL_DEPTH_TEST);
glDepthFunc (GL_LEQUAL);
}
glEnable (GL_BLEND);
glBlendFuncSeparate (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);
glBlendEquation (GL_FUNC_ADD);
/* Transparent pass: back-to-front */
GSK_NOTE (OPENGL, g_print ("Rendering %u transparent items\n", self->transparent_render_items->len));
for (i = 0; i < self->transparent_render_items->len; i++)
{
RenderItem *item = &g_array_index (self->transparent_render_items, RenderItem, i);
render_item (self, item);
}
/* Draw the output of the GL rendering to the window */
GSK_NOTE (OPENGL, g_print ("Drawing GL content on Cairo surface using a %s\n",
self->texture_id != 0 ? "texture" : "renderbuffer"));
out:
use_alpha = gsk_renderer_get_use_alpha (renderer);
gdk_cairo_draw_from_gl (gdk_drawing_context_get_cairo_context (context),
gdk_drawing_context_get_window (context),
use_alpha ? self->texture_id : self->render_buffer,
use_alpha ? GL_TEXTURE : GL_RENDERBUFFER,
gsk_renderer_get_scale_factor (renderer),
0, 0, viewport.size.width, viewport.size.height);
gdk_gl_context_make_current (self->context);
gsk_gl_driver_end_frame (self->gl_driver);
gsk_gl_renderer_clear_tree (self);
}
static void
gsk_gl_renderer_class_init (GskGLRendererClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GskRendererClass *renderer_class = GSK_RENDERER_CLASS (klass);
gobject_class->dispose = gsk_gl_renderer_dispose;
renderer_class->realize = gsk_gl_renderer_realize;
renderer_class->unrealize = gsk_gl_renderer_unrealize;
renderer_class->render = gsk_gl_renderer_render;
}
static void
gsk_gl_renderer_init (GskGLRenderer *self)
{
gsk_ensure_resources ();
graphene_matrix_init_identity (&self->mvp);
self->has_depth_buffer = TRUE;
self->has_stencil_buffer = TRUE;
}