clip-stack: Splits out opengl specific code
As part of an on-going effort to enable non-opengl drivers for Cogl this splits out the opengl specific code in cogl-clip-stack.c into cogl/driver/cogl-clip-stack-gl.c Reviewed-by: Neil Roberts <neil@linux.intel.com> (cherry picked from commit acf989f1bb628282c53d1249b2e3fc6f6579f1e9)
This commit is contained in:
parent
ae713a32d0
commit
ffd2cf8ef8
@ -165,6 +165,8 @@ cogl_driver_sources += \
|
||||
$(srcdir)/driver/gl/cogl-texture-2d-gl.c \
|
||||
$(srcdir)/driver/gl/cogl-attribute-gl-private.h \
|
||||
$(srcdir)/driver/gl/cogl-attribute-gl.c \
|
||||
$(srcdir)/driver/gl/cogl-clip-stack-gl-private.h \
|
||||
$(srcdir)/driver/gl/cogl-clip-stack-gl.c \
|
||||
$(srcdir)/driver/gl/cogl-pipeline-opengl.c \
|
||||
$(srcdir)/driver/gl/cogl-pipeline-opengl-private.h \
|
||||
$(srcdir)/driver/gl/cogl-pipeline-fragend-glsl.c \
|
||||
|
@ -16,7 +16,8 @@
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
* License along with this library. If not, see
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
*/
|
||||
@ -48,430 +49,7 @@
|
||||
#include "cogl-offscreen.h"
|
||||
#include "cogl-matrix-stack.h"
|
||||
|
||||
#ifndef GL_CLIP_PLANE0
|
||||
#define GL_CLIP_PLANE0 0x3000
|
||||
#define GL_CLIP_PLANE1 0x3001
|
||||
#define GL_CLIP_PLANE2 0x3002
|
||||
#define GL_CLIP_PLANE3 0x3003
|
||||
#define GL_CLIP_PLANE4 0x3004
|
||||
#define GL_CLIP_PLANE5 0x3005
|
||||
#endif
|
||||
|
||||
static void
|
||||
project_vertex (const CoglMatrix *modelview_projection,
|
||||
float *vertex)
|
||||
{
|
||||
int i;
|
||||
|
||||
cogl_matrix_transform_point (modelview_projection,
|
||||
&vertex[0], &vertex[1],
|
||||
&vertex[2], &vertex[3]);
|
||||
|
||||
/* Convert from homogenized coordinates */
|
||||
for (i = 0; i < 4; i++)
|
||||
vertex[i] /= vertex[3];
|
||||
}
|
||||
|
||||
static void
|
||||
set_clip_plane (CoglFramebuffer *framebuffer,
|
||||
GLint plane_num,
|
||||
const float *vertex_a,
|
||||
const float *vertex_b)
|
||||
{
|
||||
GLfloat planef[4];
|
||||
double planed[4];
|
||||
GLfloat angle;
|
||||
CoglMatrixStack *modelview_stack =
|
||||
_cogl_framebuffer_get_modelview_stack (framebuffer);
|
||||
CoglMatrixStack *projection_stack =
|
||||
_cogl_framebuffer_get_projection_stack (framebuffer);
|
||||
CoglMatrix inverse_projection;
|
||||
|
||||
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
||||
|
||||
_cogl_matrix_stack_get_inverse (projection_stack, &inverse_projection);
|
||||
|
||||
/* Calculate the angle between the axes and the line crossing the
|
||||
two points */
|
||||
angle = atan2f (vertex_b[1] - vertex_a[1],
|
||||
vertex_b[0] - vertex_a[0]) * (180.0/G_PI);
|
||||
|
||||
_cogl_matrix_stack_push (modelview_stack);
|
||||
|
||||
/* Load the inverse of the projection matrix so we can specify the plane
|
||||
* in screen coordinates */
|
||||
_cogl_matrix_stack_set (modelview_stack, &inverse_projection);
|
||||
|
||||
/* Rotate about point a */
|
||||
_cogl_matrix_stack_translate (modelview_stack,
|
||||
vertex_a[0], vertex_a[1], vertex_a[2]);
|
||||
/* Rotate the plane by the calculated angle so that it will connect
|
||||
the two points */
|
||||
_cogl_matrix_stack_rotate (modelview_stack, angle, 0.0f, 0.0f, 1.0f);
|
||||
_cogl_matrix_stack_translate (modelview_stack,
|
||||
-vertex_a[0], -vertex_a[1], -vertex_a[2]);
|
||||
|
||||
/* Clip planes can only be used when a fixed function backend is in
|
||||
use so we know we can directly push this matrix to the builtin
|
||||
state */
|
||||
_cogl_matrix_entry_flush_to_gl_builtins (ctx,
|
||||
modelview_stack->last_entry,
|
||||
COGL_MATRIX_MODELVIEW,
|
||||
framebuffer,
|
||||
FALSE /* don't disable flip */);
|
||||
|
||||
planef[0] = 0;
|
||||
planef[1] = -1.0;
|
||||
planef[2] = 0;
|
||||
planef[3] = vertex_a[1];
|
||||
|
||||
switch (ctx->driver)
|
||||
{
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
|
||||
case COGL_DRIVER_GLES1:
|
||||
GE( ctx, glClipPlanef (plane_num, planef) );
|
||||
break;
|
||||
|
||||
case COGL_DRIVER_GL:
|
||||
planed[0] = planef[0];
|
||||
planed[1] = planef[1];
|
||||
planed[2] = planef[2];
|
||||
planed[3] = planef[3];
|
||||
GE( ctx, glClipPlane (plane_num, planed) );
|
||||
break;
|
||||
}
|
||||
|
||||
_cogl_matrix_stack_pop (modelview_stack);
|
||||
}
|
||||
|
||||
static void
|
||||
set_clip_planes (CoglFramebuffer *framebuffer,
|
||||
CoglMatrixEntry *modelview_entry,
|
||||
float x_1,
|
||||
float y_1,
|
||||
float x_2,
|
||||
float y_2)
|
||||
{
|
||||
CoglMatrix modelview_matrix;
|
||||
CoglMatrixStack *projection_stack =
|
||||
_cogl_framebuffer_get_projection_stack (framebuffer);
|
||||
CoglMatrix projection_matrix;
|
||||
CoglMatrix modelview_projection;
|
||||
float signed_area;
|
||||
|
||||
float vertex_tl[4] = { x_1, y_1, 0, 1.0 };
|
||||
float vertex_tr[4] = { x_2, y_1, 0, 1.0 };
|
||||
float vertex_bl[4] = { x_1, y_2, 0, 1.0 };
|
||||
float vertex_br[4] = { x_2, y_2, 0, 1.0 };
|
||||
|
||||
_cogl_matrix_stack_get (projection_stack, &projection_matrix);
|
||||
_cogl_matrix_entry_get (modelview_entry, &modelview_matrix);
|
||||
|
||||
cogl_matrix_multiply (&modelview_projection,
|
||||
&projection_matrix,
|
||||
&modelview_matrix);
|
||||
|
||||
project_vertex (&modelview_projection, vertex_tl);
|
||||
project_vertex (&modelview_projection, vertex_tr);
|
||||
project_vertex (&modelview_projection, vertex_bl);
|
||||
project_vertex (&modelview_projection, vertex_br);
|
||||
|
||||
/* Calculate the signed area of the polygon formed by the four
|
||||
vertices so that we can know its orientation */
|
||||
signed_area = (vertex_tl[0] * (vertex_tr[1] - vertex_bl[1])
|
||||
+ vertex_tr[0] * (vertex_br[1] - vertex_tl[1])
|
||||
+ vertex_br[0] * (vertex_bl[1] - vertex_tr[1])
|
||||
+ vertex_bl[0] * (vertex_tl[1] - vertex_br[1]));
|
||||
|
||||
/* Set the clip planes to form lines between all of the vertices
|
||||
using the same orientation as we calculated */
|
||||
if (signed_area > 0.0f)
|
||||
{
|
||||
/* counter-clockwise */
|
||||
set_clip_plane (framebuffer, GL_CLIP_PLANE0, vertex_tl, vertex_bl);
|
||||
set_clip_plane (framebuffer, GL_CLIP_PLANE1, vertex_bl, vertex_br);
|
||||
set_clip_plane (framebuffer, GL_CLIP_PLANE2, vertex_br, vertex_tr);
|
||||
set_clip_plane (framebuffer, GL_CLIP_PLANE3, vertex_tr, vertex_tl);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* clockwise */
|
||||
set_clip_plane (framebuffer, GL_CLIP_PLANE0, vertex_tl, vertex_tr);
|
||||
set_clip_plane (framebuffer, GL_CLIP_PLANE1, vertex_tr, vertex_br);
|
||||
set_clip_plane (framebuffer, GL_CLIP_PLANE2, vertex_br, vertex_bl);
|
||||
set_clip_plane (framebuffer, GL_CLIP_PLANE3, vertex_bl, vertex_tl);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
add_stencil_clip_rectangle (CoglFramebuffer *framebuffer,
|
||||
CoglMatrixEntry *modelview_entry,
|
||||
float x_1,
|
||||
float y_1,
|
||||
float x_2,
|
||||
float y_2,
|
||||
CoglBool first)
|
||||
{
|
||||
CoglMatrixStack *projection_stack =
|
||||
_cogl_framebuffer_get_projection_stack (framebuffer);
|
||||
CoglContext *ctx = cogl_framebuffer_get_context (framebuffer);
|
||||
|
||||
/* NB: This can be called while flushing the journal so we need
|
||||
* to be very conservative with what state we change.
|
||||
*/
|
||||
|
||||
_cogl_context_set_current_projection_entry (ctx,
|
||||
projection_stack->last_entry);
|
||||
_cogl_context_set_current_modelview_entry (ctx, modelview_entry);
|
||||
|
||||
if (first)
|
||||
{
|
||||
GE( ctx, glEnable (GL_STENCIL_TEST) );
|
||||
|
||||
/* Initially disallow everything */
|
||||
GE( ctx, glClearStencil (0) );
|
||||
GE( ctx, glClear (GL_STENCIL_BUFFER_BIT) );
|
||||
|
||||
/* Punch out a hole to allow the rectangle */
|
||||
GE( ctx, glStencilFunc (GL_NEVER, 0x1, 0x1) );
|
||||
GE( ctx, glStencilOp (GL_REPLACE, GL_REPLACE, GL_REPLACE) );
|
||||
|
||||
_cogl_rectangle_immediate (framebuffer,
|
||||
ctx->stencil_pipeline,
|
||||
x_1, y_1, x_2, y_2);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Add one to every pixel of the stencil buffer in the
|
||||
rectangle */
|
||||
GE( ctx, glStencilFunc (GL_NEVER, 0x1, 0x3) );
|
||||
GE( ctx, glStencilOp (GL_INCR, GL_INCR, GL_INCR) );
|
||||
_cogl_rectangle_immediate (framebuffer,
|
||||
ctx->stencil_pipeline,
|
||||
x_1, y_1, x_2, y_2);
|
||||
|
||||
/* Subtract one from all pixels in the stencil buffer so that
|
||||
only pixels where both the original stencil buffer and the
|
||||
rectangle are set will be valid */
|
||||
GE( ctx, glStencilOp (GL_DECR, GL_DECR, GL_DECR) );
|
||||
|
||||
_cogl_context_set_current_projection_entry (ctx, &ctx->identity_entry);
|
||||
_cogl_context_set_current_modelview_entry (ctx, &ctx->identity_entry);
|
||||
|
||||
_cogl_rectangle_immediate (framebuffer,
|
||||
ctx->stencil_pipeline,
|
||||
-1.0, -1.0, 1.0, 1.0);
|
||||
}
|
||||
|
||||
/* Restore the stencil mode */
|
||||
GE( ctx, glStencilFunc (GL_EQUAL, 0x1, 0x1) );
|
||||
GE( ctx, glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) );
|
||||
}
|
||||
|
||||
typedef void (*SilhouettePaintCallback) (CoglFramebuffer *framebuffer,
|
||||
CoglPipeline *pipeline,
|
||||
void *user_data);
|
||||
|
||||
static void
|
||||
add_stencil_clip_silhouette (CoglFramebuffer *framebuffer,
|
||||
SilhouettePaintCallback silhouette_callback,
|
||||
CoglMatrixEntry *modelview_entry,
|
||||
float bounds_x1,
|
||||
float bounds_y1,
|
||||
float bounds_x2,
|
||||
float bounds_y2,
|
||||
CoglBool merge,
|
||||
CoglBool need_clear,
|
||||
void *user_data)
|
||||
{
|
||||
CoglMatrixStack *projection_stack =
|
||||
_cogl_framebuffer_get_projection_stack (framebuffer);
|
||||
CoglContext *ctx = cogl_framebuffer_get_context (framebuffer);
|
||||
|
||||
/* NB: This can be called while flushing the journal so we need
|
||||
* to be very conservative with what state we change.
|
||||
*/
|
||||
|
||||
_cogl_context_set_current_projection_entry (ctx,
|
||||
projection_stack->last_entry);
|
||||
_cogl_context_set_current_modelview_entry (ctx, modelview_entry);
|
||||
|
||||
_cogl_pipeline_flush_gl_state (ctx->stencil_pipeline, framebuffer, FALSE, 0);
|
||||
|
||||
GE( ctx, glEnable (GL_STENCIL_TEST) );
|
||||
|
||||
GE( ctx, glColorMask (FALSE, FALSE, FALSE, FALSE) );
|
||||
GE( ctx, glDepthMask (FALSE) );
|
||||
|
||||
if (merge)
|
||||
{
|
||||
GE (ctx, glStencilMask (2));
|
||||
GE (ctx, glStencilFunc (GL_LEQUAL, 0x2, 0x6));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If we're not using the stencil buffer for clipping then we
|
||||
don't need to clear the whole stencil buffer, just the area
|
||||
that will be drawn */
|
||||
if (need_clear)
|
||||
/* If this is being called from the clip stack code then it
|
||||
will have set up a scissor for the minimum bounding box of
|
||||
all of the clips. That box will likely mean that this
|
||||
_cogl_clear won't need to clear the entire
|
||||
buffer. _cogl_framebuffer_clear_without_flush4f is used instead
|
||||
of cogl_clear because it won't try to flush the journal */
|
||||
_cogl_framebuffer_clear_without_flush4f (framebuffer,
|
||||
COGL_BUFFER_BIT_STENCIL,
|
||||
0, 0, 0, 0);
|
||||
else
|
||||
{
|
||||
/* Just clear the bounding box */
|
||||
GE( ctx, glStencilMask (~(GLuint) 0) );
|
||||
GE( ctx, glStencilOp (GL_ZERO, GL_ZERO, GL_ZERO) );
|
||||
_cogl_rectangle_immediate (framebuffer,
|
||||
ctx->stencil_pipeline,
|
||||
bounds_x1, bounds_y1,
|
||||
bounds_x2, bounds_y2);
|
||||
}
|
||||
GE (ctx, glStencilMask (1));
|
||||
GE (ctx, glStencilFunc (GL_LEQUAL, 0x1, 0x3));
|
||||
}
|
||||
|
||||
GE (ctx, glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT));
|
||||
|
||||
silhouette_callback (framebuffer, ctx->stencil_pipeline, user_data);
|
||||
|
||||
if (merge)
|
||||
{
|
||||
/* Now we have the new stencil buffer in bit 1 and the old
|
||||
stencil buffer in bit 0 so we need to intersect them */
|
||||
GE (ctx, glStencilMask (3));
|
||||
GE (ctx, glStencilFunc (GL_NEVER, 0x2, 0x3));
|
||||
GE (ctx, glStencilOp (GL_DECR, GL_DECR, GL_DECR));
|
||||
/* Decrement all of the bits twice so that only pixels where the
|
||||
value is 3 will remain */
|
||||
|
||||
_cogl_context_set_current_projection_entry (ctx, &ctx->identity_entry);
|
||||
_cogl_context_set_current_modelview_entry (ctx, &ctx->identity_entry);
|
||||
|
||||
_cogl_rectangle_immediate (framebuffer, ctx->stencil_pipeline,
|
||||
-1.0, -1.0, 1.0, 1.0);
|
||||
_cogl_rectangle_immediate (framebuffer, ctx->stencil_pipeline,
|
||||
-1.0, -1.0, 1.0, 1.0);
|
||||
}
|
||||
|
||||
GE (ctx, glStencilMask (~(GLuint) 0));
|
||||
GE (ctx, glDepthMask (TRUE));
|
||||
GE (ctx, glColorMask (TRUE, TRUE, TRUE, TRUE));
|
||||
|
||||
GE (ctx, glStencilFunc (GL_EQUAL, 0x1, 0x1));
|
||||
GE (ctx, glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP));
|
||||
}
|
||||
|
||||
static void
|
||||
paint_path_silhouette (CoglFramebuffer *framebuffer,
|
||||
CoglPipeline *pipeline,
|
||||
void *user_data)
|
||||
{
|
||||
CoglPath *path = user_data;
|
||||
if (path->data->path_nodes->len >= 3)
|
||||
_cogl_path_fill_nodes (path,
|
||||
framebuffer,
|
||||
pipeline,
|
||||
COGL_DRAW_SKIP_JOURNAL_FLUSH |
|
||||
COGL_DRAW_SKIP_PIPELINE_VALIDATION |
|
||||
COGL_DRAW_SKIP_FRAMEBUFFER_FLUSH);
|
||||
}
|
||||
|
||||
static void
|
||||
add_stencil_clip_path (CoglFramebuffer *framebuffer,
|
||||
CoglMatrixEntry *modelview_entry,
|
||||
CoglPath *path,
|
||||
CoglBool merge,
|
||||
CoglBool need_clear)
|
||||
{
|
||||
CoglPathData *data = path->data;
|
||||
add_stencil_clip_silhouette (framebuffer,
|
||||
paint_path_silhouette,
|
||||
modelview_entry,
|
||||
data->path_nodes_min.x,
|
||||
data->path_nodes_min.y,
|
||||
data->path_nodes_max.x,
|
||||
data->path_nodes_max.y,
|
||||
merge,
|
||||
need_clear,
|
||||
path);
|
||||
}
|
||||
|
||||
static void
|
||||
paint_primitive_silhouette (CoglFramebuffer *framebuffer,
|
||||
CoglPipeline *pipeline,
|
||||
void *user_data)
|
||||
{
|
||||
_cogl_framebuffer_draw_primitive (framebuffer,
|
||||
pipeline,
|
||||
user_data,
|
||||
COGL_DRAW_SKIP_JOURNAL_FLUSH |
|
||||
COGL_DRAW_SKIP_PIPELINE_VALIDATION |
|
||||
COGL_DRAW_SKIP_FRAMEBUFFER_FLUSH |
|
||||
COGL_DRAW_SKIP_LEGACY_STATE);
|
||||
}
|
||||
|
||||
static void
|
||||
add_stencil_clip_primitive (CoglFramebuffer *framebuffer,
|
||||
CoglMatrixEntry *modelview_entry,
|
||||
CoglPrimitive *primitive,
|
||||
float bounds_x1,
|
||||
float bounds_y1,
|
||||
float bounds_x2,
|
||||
float bounds_y2,
|
||||
CoglBool merge,
|
||||
CoglBool need_clear)
|
||||
{
|
||||
add_stencil_clip_silhouette (framebuffer,
|
||||
paint_primitive_silhouette,
|
||||
modelview_entry,
|
||||
bounds_x1,
|
||||
bounds_y1,
|
||||
bounds_x2,
|
||||
bounds_y2,
|
||||
merge,
|
||||
need_clear,
|
||||
primitive);
|
||||
}
|
||||
|
||||
static void
|
||||
disable_stencil_buffer (void)
|
||||
{
|
||||
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
||||
|
||||
GE( ctx, glDisable (GL_STENCIL_TEST) );
|
||||
}
|
||||
|
||||
static void
|
||||
enable_clip_planes (void)
|
||||
{
|
||||
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
||||
|
||||
GE( ctx, glEnable (GL_CLIP_PLANE0) );
|
||||
GE( ctx, glEnable (GL_CLIP_PLANE1) );
|
||||
GE( ctx, glEnable (GL_CLIP_PLANE2) );
|
||||
GE( ctx, glEnable (GL_CLIP_PLANE3) );
|
||||
}
|
||||
|
||||
static void
|
||||
disable_clip_planes (void)
|
||||
{
|
||||
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
||||
|
||||
GE( ctx, glDisable (GL_CLIP_PLANE3) );
|
||||
GE( ctx, glDisable (GL_CLIP_PLANE2) );
|
||||
GE( ctx, glDisable (GL_CLIP_PLANE1) );
|
||||
GE( ctx, glDisable (GL_CLIP_PLANE0) );
|
||||
}
|
||||
|
||||
static void *
|
||||
_cogl_clip_stack_push_entry (CoglClipStack *clip_stack,
|
||||
@ -881,183 +459,6 @@ _cogl_clip_stack_flush (CoglClipStack *stack,
|
||||
CoglFramebuffer *framebuffer)
|
||||
{
|
||||
CoglContext *ctx = framebuffer->context;
|
||||
int has_clip_planes;
|
||||
CoglBool using_clip_planes = FALSE;
|
||||
CoglBool using_stencil_buffer = FALSE;
|
||||
int scissor_x0;
|
||||
int scissor_y0;
|
||||
int scissor_x1;
|
||||
int scissor_y1;
|
||||
CoglClipStack *entry;
|
||||
int scissor_y_start;
|
||||
|
||||
/* If we have already flushed this state then we don't need to do
|
||||
anything */
|
||||
if (ctx->current_clip_stack_valid)
|
||||
{
|
||||
if (ctx->current_clip_stack == stack)
|
||||
return;
|
||||
|
||||
_cogl_clip_stack_unref (ctx->current_clip_stack);
|
||||
}
|
||||
|
||||
ctx->current_clip_stack_valid = TRUE;
|
||||
ctx->current_clip_stack = _cogl_clip_stack_ref (stack);
|
||||
|
||||
has_clip_planes =
|
||||
ctx->private_feature_flags & COGL_PRIVATE_FEATURE_FOUR_CLIP_PLANES;
|
||||
|
||||
if (has_clip_planes)
|
||||
disable_clip_planes ();
|
||||
disable_stencil_buffer ();
|
||||
|
||||
/* If the stack is empty then there's nothing else to do */
|
||||
if (stack == NULL)
|
||||
{
|
||||
COGL_NOTE (CLIPPING, "Flushed empty clip stack");
|
||||
|
||||
ctx->current_clip_stack_uses_stencil = FALSE;
|
||||
GE (ctx, glDisable (GL_SCISSOR_TEST));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Calculate the scissor rect first so that if we eventually have to
|
||||
clear the stencil buffer then the clear will be clipped to the
|
||||
intersection of all of the bounding boxes. This saves having to
|
||||
clear the whole stencil buffer */
|
||||
_cogl_clip_stack_get_bounds (stack,
|
||||
&scissor_x0, &scissor_y0,
|
||||
&scissor_x1, &scissor_y1);
|
||||
|
||||
/* Enable scissoring as soon as possible */
|
||||
if (scissor_x0 >= scissor_x1 || scissor_y0 >= scissor_y1)
|
||||
scissor_x0 = scissor_y0 = scissor_x1 = scissor_y1 = scissor_y_start = 0;
|
||||
else
|
||||
{
|
||||
/* We store the entry coordinates in Cogl coordinate space
|
||||
* but OpenGL requires the window origin to be the bottom
|
||||
* left so we may need to convert the incoming coordinates.
|
||||
*
|
||||
* NB: Cogl forces all offscreen rendering to be done upside
|
||||
* down so in this case no conversion is needed.
|
||||
*/
|
||||
|
||||
if (cogl_is_offscreen (framebuffer))
|
||||
scissor_y_start = scissor_y0;
|
||||
else
|
||||
{
|
||||
int framebuffer_height =
|
||||
cogl_framebuffer_get_height (framebuffer);
|
||||
|
||||
scissor_y_start = framebuffer_height - scissor_y1;
|
||||
}
|
||||
}
|
||||
|
||||
COGL_NOTE (CLIPPING, "Flushing scissor to (%i, %i, %i, %i)",
|
||||
scissor_x0, scissor_y0,
|
||||
scissor_x1, scissor_y1);
|
||||
|
||||
GE (ctx, glEnable (GL_SCISSOR_TEST));
|
||||
GE (ctx, glScissor (scissor_x0, scissor_y_start,
|
||||
scissor_x1 - scissor_x0,
|
||||
scissor_y1 - scissor_y0));
|
||||
|
||||
/* Add all of the entries. This will end up adding them in the
|
||||
reverse order that they were specified but as all of the clips
|
||||
are intersecting it should work out the same regardless of the
|
||||
order */
|
||||
for (entry = stack; entry; entry = entry->parent)
|
||||
{
|
||||
switch (entry->type)
|
||||
{
|
||||
case COGL_CLIP_STACK_PATH:
|
||||
{
|
||||
CoglClipStackPath *path_entry = (CoglClipStackPath *) entry;
|
||||
|
||||
COGL_NOTE (CLIPPING, "Adding stencil clip for path");
|
||||
|
||||
add_stencil_clip_path (framebuffer,
|
||||
path_entry->matrix_entry,
|
||||
path_entry->path,
|
||||
using_stencil_buffer,
|
||||
TRUE);
|
||||
|
||||
using_stencil_buffer = TRUE;
|
||||
break;
|
||||
}
|
||||
case COGL_CLIP_STACK_PRIMITIVE:
|
||||
{
|
||||
CoglClipStackPrimitive *primitive_entry =
|
||||
(CoglClipStackPrimitive *) entry;
|
||||
|
||||
COGL_NOTE (CLIPPING, "Adding stencil clip for primitive");
|
||||
|
||||
add_stencil_clip_primitive (framebuffer,
|
||||
primitive_entry->matrix_entry,
|
||||
primitive_entry->primitive,
|
||||
primitive_entry->bounds_x1,
|
||||
primitive_entry->bounds_y1,
|
||||
primitive_entry->bounds_x2,
|
||||
primitive_entry->bounds_y2,
|
||||
using_stencil_buffer,
|
||||
TRUE);
|
||||
|
||||
using_stencil_buffer = TRUE;
|
||||
break;
|
||||
}
|
||||
case COGL_CLIP_STACK_RECT:
|
||||
{
|
||||
CoglClipStackRect *rect = (CoglClipStackRect *) entry;
|
||||
|
||||
/* We don't need to do anything extra if the clip for this
|
||||
rectangle was entirely described by its scissor bounds */
|
||||
if (!rect->can_be_scissor)
|
||||
{
|
||||
/* If we support clip planes and we haven't already used
|
||||
them then use that instead */
|
||||
if (has_clip_planes)
|
||||
{
|
||||
COGL_NOTE (CLIPPING,
|
||||
"Adding clip planes clip for rectangle");
|
||||
|
||||
set_clip_planes (framebuffer,
|
||||
rect->matrix_entry,
|
||||
rect->x0,
|
||||
rect->y0,
|
||||
rect->x1,
|
||||
rect->y1);
|
||||
using_clip_planes = TRUE;
|
||||
/* We can't use clip planes a second time */
|
||||
has_clip_planes = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
COGL_NOTE (CLIPPING, "Adding stencil clip for rectangle");
|
||||
|
||||
add_stencil_clip_rectangle (framebuffer,
|
||||
rect->matrix_entry,
|
||||
rect->x0,
|
||||
rect->y0,
|
||||
rect->x1,
|
||||
rect->y1,
|
||||
!using_stencil_buffer);
|
||||
using_stencil_buffer = TRUE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case COGL_CLIP_STACK_WINDOW_RECT:
|
||||
break;
|
||||
/* We don't need to do anything for window space rectangles because
|
||||
* their functionality is entirely implemented by the entry bounding
|
||||
* box */
|
||||
}
|
||||
}
|
||||
|
||||
/* Enabling clip planes is delayed to now so that they won't affect
|
||||
setting up the stencil buffer */
|
||||
if (using_clip_planes)
|
||||
enable_clip_planes ();
|
||||
|
||||
ctx->current_clip_stack_uses_stencil = using_stencil_buffer;
|
||||
ctx->driver_vtable->clip_stack_flush (stack, framebuffer);
|
||||
}
|
||||
|
@ -222,6 +222,13 @@ struct _CoglDriverVtable
|
||||
CoglDrawFlags flags,
|
||||
CoglAttribute **attributes,
|
||||
int n_attributes);
|
||||
|
||||
/* Flushes the clip stack to the GPU using a combination of the
|
||||
* stencil buffer, scissor and clip plane state.
|
||||
*/
|
||||
void
|
||||
(* clip_stack_flush) (CoglClipStack *stack, CoglFramebuffer *framebuffer);
|
||||
|
||||
};
|
||||
|
||||
#endif /* __COGL_DRIVER_H */
|
||||
|
39
cogl/driver/gl/cogl-clip-stack-gl-private.h
Normal file
39
cogl/driver/gl/cogl-clip-stack-gl-private.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Cogl
|
||||
*
|
||||
* An object oriented GL/GLES Abstraction/Utility Layer
|
||||
*
|
||||
* Copyright (C) 2012 Intel Corporation.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors:
|
||||
* Robert Bragg <robert@linux.intel.com>
|
||||
*/
|
||||
|
||||
#ifndef _COGL_CLIP_STACK_GL_PRIVATE_H_
|
||||
#define _COGL_CLIP_STACK_GL_PRIVATE_H_
|
||||
|
||||
#include "cogl-types.h"
|
||||
#include "cogl-framebuffer.h"
|
||||
#include "cogl-clip-stack.h"
|
||||
|
||||
void
|
||||
_cogl_clip_stack_gl_flush (CoglClipStack *stack,
|
||||
CoglFramebuffer *framebuffer);
|
||||
|
||||
#endif /* _COGL_CLIP_STACK_GL_PRIVATE_H_ */
|
634
cogl/driver/gl/cogl-clip-stack-gl.c
Normal file
634
cogl/driver/gl/cogl-clip-stack-gl.c
Normal file
@ -0,0 +1,634 @@
|
||||
/*
|
||||
* Cogl
|
||||
*
|
||||
* An object oriented GL/GLES Abstraction/Utility Layer
|
||||
*
|
||||
* Copyright (C) 2007,2008,2009,2010,2011,2012 Intel Corporation.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors:
|
||||
* Neil Roberts <neil@linux.intel.com>
|
||||
* Robert Bragg <robert@linux.intel.com>
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "cogl-context-private.h"
|
||||
#include "cogl-primitives-private.h"
|
||||
#include "cogl-pipeline-opengl-private.h"
|
||||
#include "cogl-path-private.h"
|
||||
#include "cogl-clip-stack-gl-private.h"
|
||||
|
||||
#ifndef GL_CLIP_PLANE0
|
||||
#define GL_CLIP_PLANE0 0x3000
|
||||
#define GL_CLIP_PLANE1 0x3001
|
||||
#define GL_CLIP_PLANE2 0x3002
|
||||
#define GL_CLIP_PLANE3 0x3003
|
||||
#define GL_CLIP_PLANE4 0x3004
|
||||
#define GL_CLIP_PLANE5 0x3005
|
||||
#endif
|
||||
|
||||
static void
|
||||
project_vertex (const CoglMatrix *modelview_projection,
|
||||
float *vertex)
|
||||
{
|
||||
int i;
|
||||
|
||||
cogl_matrix_transform_point (modelview_projection,
|
||||
&vertex[0], &vertex[1],
|
||||
&vertex[2], &vertex[3]);
|
||||
|
||||
/* Convert from homogenized coordinates */
|
||||
for (i = 0; i < 4; i++)
|
||||
vertex[i] /= vertex[3];
|
||||
}
|
||||
|
||||
static void
|
||||
set_clip_plane (CoglFramebuffer *framebuffer,
|
||||
int plane_num,
|
||||
const float *vertex_a,
|
||||
const float *vertex_b)
|
||||
{
|
||||
CoglContext *ctx = framebuffer->context;
|
||||
float planef[4];
|
||||
double planed[4];
|
||||
float angle;
|
||||
CoglMatrixStack *modelview_stack =
|
||||
_cogl_framebuffer_get_modelview_stack (framebuffer);
|
||||
CoglMatrixStack *projection_stack =
|
||||
_cogl_framebuffer_get_projection_stack (framebuffer);
|
||||
CoglMatrix inverse_projection;
|
||||
|
||||
_cogl_matrix_stack_get_inverse (projection_stack, &inverse_projection);
|
||||
|
||||
/* Calculate the angle between the axes and the line crossing the
|
||||
two points */
|
||||
angle = atan2f (vertex_b[1] - vertex_a[1],
|
||||
vertex_b[0] - vertex_a[0]) * (180.0/G_PI);
|
||||
|
||||
_cogl_matrix_stack_push (modelview_stack);
|
||||
|
||||
/* Load the inverse of the projection matrix so we can specify the plane
|
||||
* in screen coordinates */
|
||||
_cogl_matrix_stack_set (modelview_stack, &inverse_projection);
|
||||
|
||||
/* Rotate about point a */
|
||||
_cogl_matrix_stack_translate (modelview_stack,
|
||||
vertex_a[0], vertex_a[1], vertex_a[2]);
|
||||
/* Rotate the plane by the calculated angle so that it will connect
|
||||
the two points */
|
||||
_cogl_matrix_stack_rotate (modelview_stack, angle, 0.0f, 0.0f, 1.0f);
|
||||
_cogl_matrix_stack_translate (modelview_stack,
|
||||
-vertex_a[0], -vertex_a[1], -vertex_a[2]);
|
||||
|
||||
/* Clip planes can only be used when a fixed function backend is in
|
||||
use so we know we can directly push this matrix to the builtin
|
||||
state */
|
||||
_cogl_matrix_entry_flush_to_gl_builtins (ctx,
|
||||
modelview_stack->last_entry,
|
||||
COGL_MATRIX_MODELVIEW,
|
||||
framebuffer,
|
||||
FALSE /* don't disable flip */);
|
||||
|
||||
planef[0] = 0;
|
||||
planef[1] = -1.0;
|
||||
planef[2] = 0;
|
||||
planef[3] = vertex_a[1];
|
||||
|
||||
switch (ctx->driver)
|
||||
{
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
|
||||
case COGL_DRIVER_GLES1:
|
||||
GE( ctx, glClipPlanef (plane_num, planef) );
|
||||
break;
|
||||
|
||||
case COGL_DRIVER_GL:
|
||||
planed[0] = planef[0];
|
||||
planed[1] = planef[1];
|
||||
planed[2] = planef[2];
|
||||
planed[3] = planef[3];
|
||||
GE( ctx, glClipPlane (plane_num, planed) );
|
||||
break;
|
||||
}
|
||||
|
||||
_cogl_matrix_stack_pop (modelview_stack);
|
||||
}
|
||||
|
||||
static void
|
||||
set_clip_planes (CoglFramebuffer *framebuffer,
|
||||
CoglMatrixEntry *modelview_entry,
|
||||
float x_1,
|
||||
float y_1,
|
||||
float x_2,
|
||||
float y_2)
|
||||
{
|
||||
CoglMatrix modelview_matrix;
|
||||
CoglMatrixStack *projection_stack =
|
||||
_cogl_framebuffer_get_projection_stack (framebuffer);
|
||||
CoglMatrix projection_matrix;
|
||||
CoglMatrix modelview_projection;
|
||||
float signed_area;
|
||||
|
||||
float vertex_tl[4] = { x_1, y_1, 0, 1.0 };
|
||||
float vertex_tr[4] = { x_2, y_1, 0, 1.0 };
|
||||
float vertex_bl[4] = { x_1, y_2, 0, 1.0 };
|
||||
float vertex_br[4] = { x_2, y_2, 0, 1.0 };
|
||||
|
||||
_cogl_matrix_stack_get (projection_stack, &projection_matrix);
|
||||
_cogl_matrix_entry_get (modelview_entry, &modelview_matrix);
|
||||
|
||||
cogl_matrix_multiply (&modelview_projection,
|
||||
&projection_matrix,
|
||||
&modelview_matrix);
|
||||
|
||||
project_vertex (&modelview_projection, vertex_tl);
|
||||
project_vertex (&modelview_projection, vertex_tr);
|
||||
project_vertex (&modelview_projection, vertex_bl);
|
||||
project_vertex (&modelview_projection, vertex_br);
|
||||
|
||||
/* Calculate the signed area of the polygon formed by the four
|
||||
vertices so that we can know its orientation */
|
||||
signed_area = (vertex_tl[0] * (vertex_tr[1] - vertex_bl[1])
|
||||
+ vertex_tr[0] * (vertex_br[1] - vertex_tl[1])
|
||||
+ vertex_br[0] * (vertex_bl[1] - vertex_tr[1])
|
||||
+ vertex_bl[0] * (vertex_tl[1] - vertex_br[1]));
|
||||
|
||||
/* Set the clip planes to form lines between all of the vertices
|
||||
using the same orientation as we calculated */
|
||||
if (signed_area > 0.0f)
|
||||
{
|
||||
/* counter-clockwise */
|
||||
set_clip_plane (framebuffer, GL_CLIP_PLANE0, vertex_tl, vertex_bl);
|
||||
set_clip_plane (framebuffer, GL_CLIP_PLANE1, vertex_bl, vertex_br);
|
||||
set_clip_plane (framebuffer, GL_CLIP_PLANE2, vertex_br, vertex_tr);
|
||||
set_clip_plane (framebuffer, GL_CLIP_PLANE3, vertex_tr, vertex_tl);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* clockwise */
|
||||
set_clip_plane (framebuffer, GL_CLIP_PLANE0, vertex_tl, vertex_tr);
|
||||
set_clip_plane (framebuffer, GL_CLIP_PLANE1, vertex_tr, vertex_br);
|
||||
set_clip_plane (framebuffer, GL_CLIP_PLANE2, vertex_br, vertex_bl);
|
||||
set_clip_plane (framebuffer, GL_CLIP_PLANE3, vertex_bl, vertex_tl);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
add_stencil_clip_rectangle (CoglFramebuffer *framebuffer,
|
||||
CoglMatrixEntry *modelview_entry,
|
||||
float x_1,
|
||||
float y_1,
|
||||
float x_2,
|
||||
float y_2,
|
||||
CoglBool first)
|
||||
{
|
||||
CoglMatrixStack *projection_stack =
|
||||
_cogl_framebuffer_get_projection_stack (framebuffer);
|
||||
CoglContext *ctx = cogl_framebuffer_get_context (framebuffer);
|
||||
|
||||
/* NB: This can be called while flushing the journal so we need
|
||||
* to be very conservative with what state we change.
|
||||
*/
|
||||
|
||||
_cogl_context_set_current_projection_entry (ctx,
|
||||
projection_stack->last_entry);
|
||||
_cogl_context_set_current_modelview_entry (ctx, modelview_entry);
|
||||
|
||||
if (first)
|
||||
{
|
||||
GE( ctx, glEnable (GL_STENCIL_TEST) );
|
||||
|
||||
/* Initially disallow everything */
|
||||
GE( ctx, glClearStencil (0) );
|
||||
GE( ctx, glClear (GL_STENCIL_BUFFER_BIT) );
|
||||
|
||||
/* Punch out a hole to allow the rectangle */
|
||||
GE( ctx, glStencilFunc (GL_NEVER, 0x1, 0x1) );
|
||||
GE( ctx, glStencilOp (GL_REPLACE, GL_REPLACE, GL_REPLACE) );
|
||||
|
||||
_cogl_rectangle_immediate (framebuffer,
|
||||
ctx->stencil_pipeline,
|
||||
x_1, y_1, x_2, y_2);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Add one to every pixel of the stencil buffer in the
|
||||
rectangle */
|
||||
GE( ctx, glStencilFunc (GL_NEVER, 0x1, 0x3) );
|
||||
GE( ctx, glStencilOp (GL_INCR, GL_INCR, GL_INCR) );
|
||||
_cogl_rectangle_immediate (framebuffer,
|
||||
ctx->stencil_pipeline,
|
||||
x_1, y_1, x_2, y_2);
|
||||
|
||||
/* Subtract one from all pixels in the stencil buffer so that
|
||||
only pixels where both the original stencil buffer and the
|
||||
rectangle are set will be valid */
|
||||
GE( ctx, glStencilOp (GL_DECR, GL_DECR, GL_DECR) );
|
||||
|
||||
_cogl_context_set_current_projection_entry (ctx, &ctx->identity_entry);
|
||||
_cogl_context_set_current_modelview_entry (ctx, &ctx->identity_entry);
|
||||
|
||||
_cogl_rectangle_immediate (framebuffer,
|
||||
ctx->stencil_pipeline,
|
||||
-1.0, -1.0, 1.0, 1.0);
|
||||
}
|
||||
|
||||
/* Restore the stencil mode */
|
||||
GE( ctx, glStencilFunc (GL_EQUAL, 0x1, 0x1) );
|
||||
GE( ctx, glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) );
|
||||
}
|
||||
|
||||
typedef void (*SilhouettePaintCallback) (CoglFramebuffer *framebuffer,
|
||||
CoglPipeline *pipeline,
|
||||
void *user_data);
|
||||
|
||||
static void
|
||||
add_stencil_clip_silhouette (CoglFramebuffer *framebuffer,
|
||||
SilhouettePaintCallback silhouette_callback,
|
||||
CoglMatrixEntry *modelview_entry,
|
||||
float bounds_x1,
|
||||
float bounds_y1,
|
||||
float bounds_x2,
|
||||
float bounds_y2,
|
||||
CoglBool merge,
|
||||
CoglBool need_clear,
|
||||
void *user_data)
|
||||
{
|
||||
CoglMatrixStack *projection_stack =
|
||||
_cogl_framebuffer_get_projection_stack (framebuffer);
|
||||
CoglContext *ctx = cogl_framebuffer_get_context (framebuffer);
|
||||
|
||||
/* NB: This can be called while flushing the journal so we need
|
||||
* to be very conservative with what state we change.
|
||||
*/
|
||||
|
||||
_cogl_context_set_current_projection_entry (ctx,
|
||||
projection_stack->last_entry);
|
||||
_cogl_context_set_current_modelview_entry (ctx, modelview_entry);
|
||||
|
||||
_cogl_pipeline_flush_gl_state (ctx->stencil_pipeline, framebuffer, FALSE, 0);
|
||||
|
||||
GE( ctx, glEnable (GL_STENCIL_TEST) );
|
||||
|
||||
GE( ctx, glColorMask (FALSE, FALSE, FALSE, FALSE) );
|
||||
GE( ctx, glDepthMask (FALSE) );
|
||||
|
||||
if (merge)
|
||||
{
|
||||
GE (ctx, glStencilMask (2));
|
||||
GE (ctx, glStencilFunc (GL_LEQUAL, 0x2, 0x6));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If we're not using the stencil buffer for clipping then we
|
||||
don't need to clear the whole stencil buffer, just the area
|
||||
that will be drawn */
|
||||
if (need_clear)
|
||||
/* If this is being called from the clip stack code then it
|
||||
will have set up a scissor for the minimum bounding box of
|
||||
all of the clips. That box will likely mean that this
|
||||
_cogl_clear won't need to clear the entire
|
||||
buffer. _cogl_framebuffer_clear_without_flush4f is used instead
|
||||
of cogl_clear because it won't try to flush the journal */
|
||||
_cogl_framebuffer_clear_without_flush4f (framebuffer,
|
||||
COGL_BUFFER_BIT_STENCIL,
|
||||
0, 0, 0, 0);
|
||||
else
|
||||
{
|
||||
/* Just clear the bounding box */
|
||||
GE( ctx, glStencilMask (~(GLuint) 0) );
|
||||
GE( ctx, glStencilOp (GL_ZERO, GL_ZERO, GL_ZERO) );
|
||||
_cogl_rectangle_immediate (framebuffer,
|
||||
ctx->stencil_pipeline,
|
||||
bounds_x1, bounds_y1,
|
||||
bounds_x2, bounds_y2);
|
||||
}
|
||||
GE (ctx, glStencilMask (1));
|
||||
GE (ctx, glStencilFunc (GL_LEQUAL, 0x1, 0x3));
|
||||
}
|
||||
|
||||
GE (ctx, glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT));
|
||||
|
||||
silhouette_callback (framebuffer, ctx->stencil_pipeline, user_data);
|
||||
|
||||
if (merge)
|
||||
{
|
||||
/* Now we have the new stencil buffer in bit 1 and the old
|
||||
stencil buffer in bit 0 so we need to intersect them */
|
||||
GE (ctx, glStencilMask (3));
|
||||
GE (ctx, glStencilFunc (GL_NEVER, 0x2, 0x3));
|
||||
GE (ctx, glStencilOp (GL_DECR, GL_DECR, GL_DECR));
|
||||
/* Decrement all of the bits twice so that only pixels where the
|
||||
value is 3 will remain */
|
||||
|
||||
_cogl_context_set_current_projection_entry (ctx, &ctx->identity_entry);
|
||||
_cogl_context_set_current_modelview_entry (ctx, &ctx->identity_entry);
|
||||
|
||||
_cogl_rectangle_immediate (framebuffer, ctx->stencil_pipeline,
|
||||
-1.0, -1.0, 1.0, 1.0);
|
||||
_cogl_rectangle_immediate (framebuffer, ctx->stencil_pipeline,
|
||||
-1.0, -1.0, 1.0, 1.0);
|
||||
}
|
||||
|
||||
GE (ctx, glStencilMask (~(GLuint) 0));
|
||||
GE (ctx, glDepthMask (TRUE));
|
||||
GE (ctx, glColorMask (TRUE, TRUE, TRUE, TRUE));
|
||||
|
||||
GE (ctx, glStencilFunc (GL_EQUAL, 0x1, 0x1));
|
||||
GE (ctx, glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP));
|
||||
}
|
||||
|
||||
static void
|
||||
paint_path_silhouette (CoglFramebuffer *framebuffer,
|
||||
CoglPipeline *pipeline,
|
||||
void *user_data)
|
||||
{
|
||||
CoglPath *path = user_data;
|
||||
if (path->data->path_nodes->len >= 3)
|
||||
_cogl_path_fill_nodes (path,
|
||||
framebuffer,
|
||||
pipeline,
|
||||
COGL_DRAW_SKIP_JOURNAL_FLUSH |
|
||||
COGL_DRAW_SKIP_PIPELINE_VALIDATION |
|
||||
COGL_DRAW_SKIP_FRAMEBUFFER_FLUSH);
|
||||
}
|
||||
|
||||
static void
|
||||
add_stencil_clip_path (CoglFramebuffer *framebuffer,
|
||||
CoglMatrixEntry *modelview_entry,
|
||||
CoglPath *path,
|
||||
CoglBool merge,
|
||||
CoglBool need_clear)
|
||||
{
|
||||
CoglPathData *data = path->data;
|
||||
add_stencil_clip_silhouette (framebuffer,
|
||||
paint_path_silhouette,
|
||||
modelview_entry,
|
||||
data->path_nodes_min.x,
|
||||
data->path_nodes_min.y,
|
||||
data->path_nodes_max.x,
|
||||
data->path_nodes_max.y,
|
||||
merge,
|
||||
need_clear,
|
||||
path);
|
||||
}
|
||||
|
||||
static void
|
||||
paint_primitive_silhouette (CoglFramebuffer *framebuffer,
|
||||
CoglPipeline *pipeline,
|
||||
void *user_data)
|
||||
{
|
||||
_cogl_framebuffer_draw_primitive (framebuffer,
|
||||
pipeline,
|
||||
user_data,
|
||||
COGL_DRAW_SKIP_JOURNAL_FLUSH |
|
||||
COGL_DRAW_SKIP_PIPELINE_VALIDATION |
|
||||
COGL_DRAW_SKIP_FRAMEBUFFER_FLUSH |
|
||||
COGL_DRAW_SKIP_LEGACY_STATE);
|
||||
}
|
||||
|
||||
static void
|
||||
add_stencil_clip_primitive (CoglFramebuffer *framebuffer,
|
||||
CoglMatrixEntry *modelview_entry,
|
||||
CoglPrimitive *primitive,
|
||||
float bounds_x1,
|
||||
float bounds_y1,
|
||||
float bounds_x2,
|
||||
float bounds_y2,
|
||||
CoglBool merge,
|
||||
CoglBool need_clear)
|
||||
{
|
||||
add_stencil_clip_silhouette (framebuffer,
|
||||
paint_primitive_silhouette,
|
||||
modelview_entry,
|
||||
bounds_x1,
|
||||
bounds_y1,
|
||||
bounds_x2,
|
||||
bounds_y2,
|
||||
merge,
|
||||
need_clear,
|
||||
primitive);
|
||||
}
|
||||
|
||||
static void
|
||||
enable_clip_planes (CoglContext *ctx)
|
||||
{
|
||||
GE( ctx, glEnable (GL_CLIP_PLANE0) );
|
||||
GE( ctx, glEnable (GL_CLIP_PLANE1) );
|
||||
GE( ctx, glEnable (GL_CLIP_PLANE2) );
|
||||
GE( ctx, glEnable (GL_CLIP_PLANE3) );
|
||||
}
|
||||
|
||||
static void
|
||||
disable_clip_planes (CoglContext *ctx)
|
||||
{
|
||||
GE( ctx, glDisable (GL_CLIP_PLANE3) );
|
||||
GE( ctx, glDisable (GL_CLIP_PLANE2) );
|
||||
GE( ctx, glDisable (GL_CLIP_PLANE1) );
|
||||
GE( ctx, glDisable (GL_CLIP_PLANE0) );
|
||||
}
|
||||
|
||||
void
|
||||
_cogl_clip_stack_gl_flush (CoglClipStack *stack,
|
||||
CoglFramebuffer *framebuffer)
|
||||
{
|
||||
CoglContext *ctx = framebuffer->context;
|
||||
int has_clip_planes;
|
||||
CoglBool using_clip_planes = FALSE;
|
||||
CoglBool using_stencil_buffer = FALSE;
|
||||
int scissor_x0;
|
||||
int scissor_y0;
|
||||
int scissor_x1;
|
||||
int scissor_y1;
|
||||
CoglClipStack *entry;
|
||||
int scissor_y_start;
|
||||
|
||||
/* If we have already flushed this state then we don't need to do
|
||||
anything */
|
||||
if (ctx->current_clip_stack_valid)
|
||||
{
|
||||
if (ctx->current_clip_stack == stack)
|
||||
return;
|
||||
|
||||
_cogl_clip_stack_unref (ctx->current_clip_stack);
|
||||
}
|
||||
|
||||
ctx->current_clip_stack_valid = TRUE;
|
||||
ctx->current_clip_stack = _cogl_clip_stack_ref (stack);
|
||||
|
||||
has_clip_planes =
|
||||
ctx->private_feature_flags & COGL_PRIVATE_FEATURE_FOUR_CLIP_PLANES;
|
||||
|
||||
if (has_clip_planes)
|
||||
disable_clip_planes (ctx);
|
||||
GE( ctx, glDisable (GL_STENCIL_TEST) );
|
||||
|
||||
/* If the stack is empty then there's nothing else to do */
|
||||
if (stack == NULL)
|
||||
{
|
||||
COGL_NOTE (CLIPPING, "Flushed empty clip stack");
|
||||
|
||||
ctx->current_clip_stack_uses_stencil = FALSE;
|
||||
GE (ctx, glDisable (GL_SCISSOR_TEST));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Calculate the scissor rect first so that if we eventually have to
|
||||
clear the stencil buffer then the clear will be clipped to the
|
||||
intersection of all of the bounding boxes. This saves having to
|
||||
clear the whole stencil buffer */
|
||||
_cogl_clip_stack_get_bounds (stack,
|
||||
&scissor_x0, &scissor_y0,
|
||||
&scissor_x1, &scissor_y1);
|
||||
|
||||
/* Enable scissoring as soon as possible */
|
||||
if (scissor_x0 >= scissor_x1 || scissor_y0 >= scissor_y1)
|
||||
scissor_x0 = scissor_y0 = scissor_x1 = scissor_y1 = scissor_y_start = 0;
|
||||
else
|
||||
{
|
||||
/* We store the entry coordinates in Cogl coordinate space
|
||||
* but OpenGL requires the window origin to be the bottom
|
||||
* left so we may need to convert the incoming coordinates.
|
||||
*
|
||||
* NB: Cogl forces all offscreen rendering to be done upside
|
||||
* down so in this case no conversion is needed.
|
||||
*/
|
||||
|
||||
if (cogl_is_offscreen (framebuffer))
|
||||
scissor_y_start = scissor_y0;
|
||||
else
|
||||
{
|
||||
int framebuffer_height =
|
||||
cogl_framebuffer_get_height (framebuffer);
|
||||
|
||||
scissor_y_start = framebuffer_height - scissor_y1;
|
||||
}
|
||||
}
|
||||
|
||||
COGL_NOTE (CLIPPING, "Flushing scissor to (%i, %i, %i, %i)",
|
||||
scissor_x0, scissor_y0,
|
||||
scissor_x1, scissor_y1);
|
||||
|
||||
GE (ctx, glEnable (GL_SCISSOR_TEST));
|
||||
GE (ctx, glScissor (scissor_x0, scissor_y_start,
|
||||
scissor_x1 - scissor_x0,
|
||||
scissor_y1 - scissor_y0));
|
||||
|
||||
/* Add all of the entries. This will end up adding them in the
|
||||
reverse order that they were specified but as all of the clips
|
||||
are intersecting it should work out the same regardless of the
|
||||
order */
|
||||
for (entry = stack; entry; entry = entry->parent)
|
||||
{
|
||||
switch (entry->type)
|
||||
{
|
||||
case COGL_CLIP_STACK_PATH:
|
||||
{
|
||||
CoglClipStackPath *path_entry = (CoglClipStackPath *) entry;
|
||||
|
||||
COGL_NOTE (CLIPPING, "Adding stencil clip for path");
|
||||
|
||||
add_stencil_clip_path (framebuffer,
|
||||
path_entry->matrix_entry,
|
||||
path_entry->path,
|
||||
using_stencil_buffer,
|
||||
TRUE);
|
||||
|
||||
using_stencil_buffer = TRUE;
|
||||
break;
|
||||
}
|
||||
case COGL_CLIP_STACK_PRIMITIVE:
|
||||
{
|
||||
CoglClipStackPrimitive *primitive_entry =
|
||||
(CoglClipStackPrimitive *) entry;
|
||||
|
||||
COGL_NOTE (CLIPPING, "Adding stencil clip for primitive");
|
||||
|
||||
add_stencil_clip_primitive (framebuffer,
|
||||
primitive_entry->matrix_entry,
|
||||
primitive_entry->primitive,
|
||||
primitive_entry->bounds_x1,
|
||||
primitive_entry->bounds_y1,
|
||||
primitive_entry->bounds_x2,
|
||||
primitive_entry->bounds_y2,
|
||||
using_stencil_buffer,
|
||||
TRUE);
|
||||
|
||||
using_stencil_buffer = TRUE;
|
||||
break;
|
||||
}
|
||||
case COGL_CLIP_STACK_RECT:
|
||||
{
|
||||
CoglClipStackRect *rect = (CoglClipStackRect *) entry;
|
||||
|
||||
/* We don't need to do anything extra if the clip for this
|
||||
rectangle was entirely described by its scissor bounds */
|
||||
if (!rect->can_be_scissor)
|
||||
{
|
||||
/* If we support clip planes and we haven't already used
|
||||
them then use that instead */
|
||||
if (has_clip_planes)
|
||||
{
|
||||
COGL_NOTE (CLIPPING,
|
||||
"Adding clip planes clip for rectangle");
|
||||
|
||||
set_clip_planes (framebuffer,
|
||||
rect->matrix_entry,
|
||||
rect->x0,
|
||||
rect->y0,
|
||||
rect->x1,
|
||||
rect->y1);
|
||||
using_clip_planes = TRUE;
|
||||
/* We can't use clip planes a second time */
|
||||
has_clip_planes = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
COGL_NOTE (CLIPPING, "Adding stencil clip for rectangle");
|
||||
|
||||
add_stencil_clip_rectangle (framebuffer,
|
||||
rect->matrix_entry,
|
||||
rect->x0,
|
||||
rect->y0,
|
||||
rect->x1,
|
||||
rect->y1,
|
||||
!using_stencil_buffer);
|
||||
using_stencil_buffer = TRUE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case COGL_CLIP_STACK_WINDOW_RECT:
|
||||
break;
|
||||
/* We don't need to do anything for window space rectangles because
|
||||
* their functionality is entirely implemented by the entry bounding
|
||||
* box */
|
||||
}
|
||||
}
|
||||
|
||||
/* Enabling clip planes is delayed to now so that they won't affect
|
||||
setting up the stencil buffer */
|
||||
if (using_clip_planes)
|
||||
enable_clip_planes (ctx);
|
||||
|
||||
ctx->current_clip_stack_uses_stencil = using_stencil_buffer;
|
||||
}
|
@ -36,6 +36,7 @@
|
||||
#include "cogl-framebuffer-gl-private.h"
|
||||
#include "cogl-texture-2d-gl-private.h"
|
||||
#include "cogl-attribute-gl-private.h"
|
||||
#include "cogl-clip-stack-gl-private.h"
|
||||
|
||||
static CoglBool
|
||||
_cogl_driver_pixel_format_from_gl_internal (CoglContext *context,
|
||||
@ -550,4 +551,5 @@ _cogl_driver_gl =
|
||||
_cogl_texture_2d_gl_copy_from_bitmap,
|
||||
_cogl_texture_2d_gl_get_data,
|
||||
_cogl_gl_flush_attributes_state,
|
||||
_cogl_clip_stack_gl_flush,
|
||||
};
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include "cogl-framebuffer-gl-private.h"
|
||||
#include "cogl-texture-2d-gl-private.h"
|
||||
#include "cogl-attribute-gl-private.h"
|
||||
#include "cogl-clip-stack-gl-private.h"
|
||||
|
||||
#ifndef GL_UNSIGNED_INT_24_8
|
||||
#define GL_UNSIGNED_INT_24_8 0x84FA
|
||||
@ -368,4 +369,5 @@ _cogl_driver_gles =
|
||||
_cogl_texture_2d_gl_copy_from_bitmap,
|
||||
NULL, /* texture_2d_get_data */
|
||||
_cogl_gl_flush_attributes_state,
|
||||
_cogl_clip_stack_gl_flush,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user