mirror of
https://github.com/brl/mutter.git
synced 2024-11-22 16:10:41 -05:00
cogl-gles2-context: Flip the rendering when framebuffer is offscreen
Cogl has a different origin for texture coordinates than OpenGL so that the results of rendering to a texture should leave the top of the image at the texture coordinate 0,0 rather than the bottom. When a GLES2 context is used to render to a Cogl texture via a CoglOffscreen we don't really want the application to have to be aware of the mismatch and flip the texture coordinates. To get that to work, this patch now tracks all of the programs that the application generates using the context and sneaks in an extra vertex shader with an alternative main function. This main function multiplies the final calculated gl_Position by a vector uniform which we can use to flip the image. When the application uploads the source code for a vertex shader we now replace any occurrences of the token 'main' with '_c31' and this renamed function gets called from the replacement main function. The token has a weird name so that it will be unlikely to conflict with a variable name in the application's source but it also needs to have the same number of characters as the original token so that it won't affect column numbers in the error reporting. We are also wrapping glGetShaderSource so that we can try to revert the token name. The same goes for the error logs just in case the error report mentions function names. Both places that cause drawing to occur (glDrawElements and glDrawArrays) are now also wrapped so that we can update the uniform value whenever the program is used with a different type of framebuffer from last time. We additionally need to manually track the state for the viewport, the stencil box and the front face because all of these will be affected by whether we are flipping the image or not. Any attempts to change these states will be queued and instead flushed at the last minute before drawing. There are still some known issues with this patch: • glCopyTexImage2D and glCopyTexSubImage2D will do the wrong thing when copying data from a CoglOffscreen. This could be quite fiddly to solve. • Point sprites won't flip correctly. To make this work we would need to flip the gl_PointSprite builtin variable somehow. This is done in the fragment shader not the vertex shader so flipping the calculated gl_Position doesn't help here. • The patch doesn't attempt to flip rendering to framebuffers for textures created within the GLES2 context. This probably makes sense because those textures are likely to be used within the GLES2 context in which case we want to leave the texture coordinates as they are. However, if the texture is shared back out to Cogl with cogl_gles2_texture_2d_new_from_handle then the texture will be upside-down. • The application can discover our secret uniform that we added via glGetActiveUniform. It might be worth trying to disguise this by wrapping that function although that could be quite fiddly. Reviewed-by: Robert Bragg <robert@linux.intel.com> (cherry picked from commit d589bf19e51f22c3241b2a18db10f22131ac126a)
This commit is contained in:
parent
0c66431df3
commit
8e12e40da9
@ -68,6 +68,13 @@ typedef struct
|
|||||||
CoglBool deleted;
|
CoglBool deleted;
|
||||||
} CoglGLES2ShaderData;
|
} CoglGLES2ShaderData;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
COGL_GLES2_FLIP_STATE_UNKNOWN,
|
||||||
|
COGL_GLES2_FLIP_STATE_NORMAL,
|
||||||
|
COGL_GLES2_FLIP_STATE_FLIPPED
|
||||||
|
} CoglGLES2FlipState;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
/* GL's ID for the program */
|
/* GL's ID for the program */
|
||||||
@ -89,6 +96,12 @@ typedef struct
|
|||||||
* application calls glDeleteProgram multiple times */
|
* application calls glDeleteProgram multiple times */
|
||||||
CoglBool deleted;
|
CoglBool deleted;
|
||||||
|
|
||||||
|
GLuint flip_vector_location;
|
||||||
|
|
||||||
|
/* A cache of what value we've put in the flip vector uniform so
|
||||||
|
* that we don't flush unless it's changed */
|
||||||
|
CoglGLES2FlipState flip_vector_state;
|
||||||
|
|
||||||
CoglGLES2Context *context;
|
CoglGLES2Context *context;
|
||||||
} CoglGLES2ProgramData;
|
} CoglGLES2ProgramData;
|
||||||
|
|
||||||
@ -129,6 +142,29 @@ struct _CoglGLES2Context
|
|||||||
* current */
|
* current */
|
||||||
CoglGLES2ProgramData *current_program;
|
CoglGLES2ProgramData *current_program;
|
||||||
|
|
||||||
|
/* A shader to provide a wrapper 'main' function. A single shader is
|
||||||
|
* used for all programs */
|
||||||
|
GLuint wrapper_shader;
|
||||||
|
|
||||||
|
/* Whether the currently bound framebuffer needs flipping. This is
|
||||||
|
* used to check for changes so that we can dirty the following
|
||||||
|
* state flags */
|
||||||
|
CoglGLES2FlipState current_flip_state;
|
||||||
|
|
||||||
|
/* The following state is tracked separately from the GL context
|
||||||
|
* because we need to modify it depending on whether we are flipping
|
||||||
|
* the geometry. */
|
||||||
|
CoglBool viewport_dirty;
|
||||||
|
int viewport[4];
|
||||||
|
CoglBool scissor_dirty;
|
||||||
|
int scissor[4];
|
||||||
|
CoglBool front_face_dirty;
|
||||||
|
GLenum front_face;
|
||||||
|
|
||||||
|
/* We need to keep track of the pack alignment so we can flip the
|
||||||
|
* results of glReadPixels read from a CoglOffscreen */
|
||||||
|
int pack_alignment;
|
||||||
|
|
||||||
void *winsys;
|
void *winsys;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -30,6 +30,8 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "cogl-gles2.h"
|
#include "cogl-gles2.h"
|
||||||
#include "cogl-gles2-context-private.h"
|
#include "cogl-gles2-context-private.h"
|
||||||
|
|
||||||
@ -49,6 +51,30 @@ static CoglGLES2Context *current_gles2_context;
|
|||||||
|
|
||||||
static CoglUserDataKey offscreen_wrapper_key;
|
static CoglUserDataKey offscreen_wrapper_key;
|
||||||
|
|
||||||
|
/* The application's main function is renamed to this so that we can
|
||||||
|
* provide an alternative main function */
|
||||||
|
#define MAIN_WRAPPER_REPLACEMENT_NAME "_c31"
|
||||||
|
/* This uniform is used to flip the rendering or not depending on
|
||||||
|
* whether we are rendering to an offscreen buffer or not */
|
||||||
|
#define MAIN_WRAPPER_FLIP_UNIFORM "_cogl_flip_vector"
|
||||||
|
|
||||||
|
/* This wrapper function around 'main' is appended to every program in
|
||||||
|
* a separate shader so that we can add some extra code to flip the
|
||||||
|
* rendering when rendering to an offscreen buffer */
|
||||||
|
static const char
|
||||||
|
main_wrapper_function[] =
|
||||||
|
"uniform vec4 " MAIN_WRAPPER_FLIP_UNIFORM ";\n"
|
||||||
|
"\n"
|
||||||
|
"void\n"
|
||||||
|
MAIN_WRAPPER_REPLACEMENT_NAME " ();\n"
|
||||||
|
"\n"
|
||||||
|
"void\n"
|
||||||
|
"main ()\n"
|
||||||
|
"{\n"
|
||||||
|
" " MAIN_WRAPPER_REPLACEMENT_NAME " ();\n"
|
||||||
|
" gl_Position *= " MAIN_WRAPPER_FLIP_UNIFORM ";\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
RESTORE_FB_NONE,
|
RESTORE_FB_NONE,
|
||||||
RESTORE_FB_FROM_OFFSCREEN,
|
RESTORE_FB_FROM_OFFSCREEN,
|
||||||
@ -98,6 +124,63 @@ detach_shader (CoglGLES2ProgramData *program_data,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static CoglBool
|
||||||
|
is_symbol_character (char ch)
|
||||||
|
{
|
||||||
|
return g_ascii_isalnum (ch) || ch == '_';
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
replace_token (char *string,
|
||||||
|
const char *token,
|
||||||
|
const char *replacement,
|
||||||
|
int length)
|
||||||
|
{
|
||||||
|
char *token_pos;
|
||||||
|
char *last_pos = string;
|
||||||
|
char *end = string + length;
|
||||||
|
int token_length = strlen (token);
|
||||||
|
|
||||||
|
/* NOTE: this assumes token and replacement are the same length */
|
||||||
|
|
||||||
|
while ((token_pos = _cogl_util_memmem (last_pos,
|
||||||
|
end - last_pos,
|
||||||
|
token,
|
||||||
|
token_length)))
|
||||||
|
{
|
||||||
|
/* Make sure this isn't in the middle of some longer token */
|
||||||
|
if ((token_pos <= string ||
|
||||||
|
!is_symbol_character (token_pos[-1])) &&
|
||||||
|
(token_pos + token_length == end ||
|
||||||
|
!is_symbol_character (token_pos[token_length])))
|
||||||
|
memcpy (token_pos, replacement, token_length);
|
||||||
|
|
||||||
|
last_pos = token_pos + token_length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
update_current_flip_state (CoglGLES2Context *gles2_ctx)
|
||||||
|
{
|
||||||
|
CoglGLES2FlipState new_flip_state;
|
||||||
|
|
||||||
|
if (gles2_ctx->current_fbo_handle == 0 &&
|
||||||
|
cogl_is_offscreen (gles2_ctx->write_buffer))
|
||||||
|
new_flip_state = COGL_GLES2_FLIP_STATE_FLIPPED;
|
||||||
|
else
|
||||||
|
new_flip_state = COGL_GLES2_FLIP_STATE_NORMAL;
|
||||||
|
|
||||||
|
/* If the flip state has changed then we need to reflush all of the
|
||||||
|
* dependent state */
|
||||||
|
if (new_flip_state != gles2_ctx->current_flip_state)
|
||||||
|
{
|
||||||
|
gles2_ctx->viewport_dirty = TRUE;
|
||||||
|
gles2_ctx->scissor_dirty = TRUE;
|
||||||
|
gles2_ctx->front_face_dirty = TRUE;
|
||||||
|
gles2_ctx->current_flip_state = new_flip_state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* We wrap glBindFramebuffer so that when framebuffer 0 is bound
|
/* We wrap glBindFramebuffer so that when framebuffer 0 is bound
|
||||||
* we can instead bind the write_framebuffer passed to
|
* we can instead bind the write_framebuffer passed to
|
||||||
* cogl_push_gles2_context().
|
* cogl_push_gles2_context().
|
||||||
@ -116,6 +199,8 @@ gl_bind_framebuffer_wrapper (GLenum target, GLuint framebuffer)
|
|||||||
}
|
}
|
||||||
|
|
||||||
gles2_ctx->context->glBindFramebuffer (target, framebuffer);
|
gles2_ctx->context->glBindFramebuffer (target, framebuffer);
|
||||||
|
|
||||||
|
update_current_flip_state (gles2_ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@ -192,6 +277,93 @@ gl_read_pixels_wrapper (GLint x,
|
|||||||
gles2_ctx->context->glReadPixels (x, y, width, height, format, type, pixels);
|
gles2_ctx->context->glReadPixels (x, y, width, height, format, type, pixels);
|
||||||
|
|
||||||
restore_write_buffer (gles2_ctx, restore_mode);
|
restore_write_buffer (gles2_ctx, restore_mode);
|
||||||
|
|
||||||
|
/* If the read buffer is a CoglOffscreen then the data will be
|
||||||
|
* upside down compared to what GL expects so we need to flip it */
|
||||||
|
if (gles2_ctx->current_fbo_handle == 0 &&
|
||||||
|
cogl_is_offscreen (gles2_ctx->read_buffer))
|
||||||
|
{
|
||||||
|
int bpp, bytes_per_row, stride, y;
|
||||||
|
uint8_t *bytes = pixels;
|
||||||
|
uint8_t *temprow;
|
||||||
|
|
||||||
|
/* Try to determine the bytes per pixel for the given
|
||||||
|
* format/type combination. If there's a format which doesn't
|
||||||
|
* make sense then we'll just give up because GL will probably
|
||||||
|
* have just thrown an error */
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case GL_RGB:
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case GL_UNSIGNED_BYTE:
|
||||||
|
bpp = 3;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GL_UNSIGNED_SHORT_5_6_5:
|
||||||
|
bpp = 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GL_RGBA:
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case GL_UNSIGNED_BYTE:
|
||||||
|
bpp = 4;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GL_UNSIGNED_SHORT_4_4_4_4:
|
||||||
|
case GL_UNSIGNED_SHORT_5_5_5_1:
|
||||||
|
bpp = 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GL_ALPHA:
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case GL_UNSIGNED_BYTE:
|
||||||
|
bpp = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes_per_row = bpp * width;
|
||||||
|
stride = ((bytes_per_row + gles2_ctx->pack_alignment - 1) &
|
||||||
|
~(gles2_ctx->pack_alignment - 1));
|
||||||
|
temprow = g_alloca (bytes_per_row);
|
||||||
|
|
||||||
|
/* vertically flip the buffer in-place */
|
||||||
|
for (y = 0; y < height / 2; y++)
|
||||||
|
{
|
||||||
|
if (y != height - y - 1) /* skip center row */
|
||||||
|
{
|
||||||
|
memcpy (temprow,
|
||||||
|
bytes + y * stride,
|
||||||
|
bytes_per_row);
|
||||||
|
memcpy (bytes + y * stride,
|
||||||
|
bytes + (height - y - 1) * stride,
|
||||||
|
bytes_per_row);
|
||||||
|
memcpy (bytes + (height - y - 1) * stride,
|
||||||
|
temprow,
|
||||||
|
bytes_per_row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -292,10 +464,14 @@ gl_create_program_wrapper (void)
|
|||||||
data->ref_count = 1;
|
data->ref_count = 1;
|
||||||
data->deleted = FALSE;
|
data->deleted = FALSE;
|
||||||
data->context = gles2_ctx;
|
data->context = gles2_ctx;
|
||||||
|
data->flip_vector_location = 0;
|
||||||
|
data->flip_vector_state = COGL_GLES2_FLIP_STATE_UNKNOWN;
|
||||||
|
|
||||||
g_hash_table_insert (gles2_ctx->program_map,
|
g_hash_table_insert (gles2_ctx->program_map,
|
||||||
GINT_TO_POINTER (id),
|
GINT_TO_POINTER (id),
|
||||||
data);
|
data);
|
||||||
|
|
||||||
|
gles2_ctx->context->glAttachShader (id, gles2_ctx->wrapper_shader);
|
||||||
}
|
}
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
@ -377,6 +553,521 @@ gl_detach_shader_wrapper (GLuint program,
|
|||||||
gles2_ctx->context->glDetachShader (program, shader);
|
gles2_ctx->context->glDetachShader (program, shader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gl_shader_source_wrapper (GLuint shader,
|
||||||
|
GLsizei count,
|
||||||
|
const char *const *string,
|
||||||
|
const GLint *length)
|
||||||
|
{
|
||||||
|
CoglGLES2Context *gles2_ctx = current_gles2_context;
|
||||||
|
CoglGLES2ShaderData *shader_data;
|
||||||
|
|
||||||
|
if ((shader_data = g_hash_table_lookup (gles2_ctx->shader_map,
|
||||||
|
GINT_TO_POINTER (shader))) &&
|
||||||
|
shader_data->type == GL_VERTEX_SHADER)
|
||||||
|
{
|
||||||
|
char **string_copy = g_alloca (count * sizeof (char *));
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Replace any occurences of the symbol 'main' with a different
|
||||||
|
* symbol so that we can provide our own wrapper main
|
||||||
|
* function */
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
int string_length = length ? length[i] : strlen (string[i]);
|
||||||
|
|
||||||
|
string_copy[i] = g_memdup (string[i], string_length);
|
||||||
|
|
||||||
|
replace_token (string_copy[i],
|
||||||
|
"main",
|
||||||
|
MAIN_WRAPPER_REPLACEMENT_NAME,
|
||||||
|
string_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
gles2_ctx->context->glShaderSource (shader,
|
||||||
|
count,
|
||||||
|
(const char *const *) string_copy,
|
||||||
|
length);
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++)
|
||||||
|
g_free (string_copy[i]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
gles2_ctx->context->glShaderSource (shader, count, string, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gl_get_shader_source_wrapper (GLuint shader,
|
||||||
|
GLsizei buf_size,
|
||||||
|
GLsizei *length_out,
|
||||||
|
GLchar *source)
|
||||||
|
{
|
||||||
|
CoglGLES2Context *gles2_ctx = current_gles2_context;
|
||||||
|
CoglGLES2ShaderData *shader_data;
|
||||||
|
GLsizei length;
|
||||||
|
|
||||||
|
gles2_ctx->context->glGetShaderSource (shader,
|
||||||
|
buf_size,
|
||||||
|
&length,
|
||||||
|
source);
|
||||||
|
|
||||||
|
if ((shader_data = g_hash_table_lookup (gles2_ctx->shader_map,
|
||||||
|
GINT_TO_POINTER (shader))) &&
|
||||||
|
shader_data->type == GL_VERTEX_SHADER)
|
||||||
|
{
|
||||||
|
GLsizei copy_length = MIN (length, buf_size - 1);
|
||||||
|
replace_token (source,
|
||||||
|
MAIN_WRAPPER_REPLACEMENT_NAME,
|
||||||
|
"main",
|
||||||
|
copy_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length_out)
|
||||||
|
*length_out = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gl_link_program_wrapper (GLuint program)
|
||||||
|
{
|
||||||
|
CoglGLES2Context *gles2_ctx = current_gles2_context;
|
||||||
|
CoglGLES2ProgramData *program_data;
|
||||||
|
|
||||||
|
gles2_ctx->context->glLinkProgram (program);
|
||||||
|
|
||||||
|
program_data = g_hash_table_lookup (gles2_ctx->program_map,
|
||||||
|
GINT_TO_POINTER (program));
|
||||||
|
|
||||||
|
if (program_data)
|
||||||
|
{
|
||||||
|
GLint status;
|
||||||
|
|
||||||
|
gles2_ctx->context->glGetProgramiv (program, GL_LINK_STATUS, &status);
|
||||||
|
|
||||||
|
if (status)
|
||||||
|
program_data->flip_vector_location =
|
||||||
|
gles2_ctx->context->glGetUniformLocation (program,
|
||||||
|
MAIN_WRAPPER_FLIP_UNIFORM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gl_get_program_iv_wrapper (GLuint program,
|
||||||
|
GLenum pname,
|
||||||
|
GLint *params)
|
||||||
|
{
|
||||||
|
CoglGLES2Context *gles2_ctx = current_gles2_context;
|
||||||
|
|
||||||
|
gles2_ctx->context->glGetProgramiv (program, pname, params);
|
||||||
|
|
||||||
|
switch (pname)
|
||||||
|
{
|
||||||
|
case GL_ATTACHED_SHADERS:
|
||||||
|
/* Decrease the number of shaders to try and hide the shader
|
||||||
|
* wrapper we added */
|
||||||
|
if (*params > 1)
|
||||||
|
(*params)--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gl_get_attached_shaders_wrapper (GLuint program,
|
||||||
|
GLsizei max_count,
|
||||||
|
GLsizei *count_ret,
|
||||||
|
GLuint *obj)
|
||||||
|
{
|
||||||
|
CoglGLES2Context *gles2_ctx = current_gles2_context;
|
||||||
|
GLuint *tmp_buf;
|
||||||
|
GLsizei count, count_out;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* We need to remove the wrapper shader we added from this list */
|
||||||
|
|
||||||
|
/* Allocate a temporary buffer that is one larger than the buffer
|
||||||
|
* passed in in case the application allocated exactly the size
|
||||||
|
* returned by GL_ATTACHED_SHADERS. */
|
||||||
|
tmp_buf = g_alloca (sizeof (GLuint) * (max_count + 1));
|
||||||
|
|
||||||
|
gles2_ctx->context->glGetAttachedShaders (program,
|
||||||
|
max_count + 1,
|
||||||
|
&count,
|
||||||
|
tmp_buf);
|
||||||
|
|
||||||
|
for (i = 0, count_out = 0; i < count; i++)
|
||||||
|
if (tmp_buf[i] != gles2_ctx->wrapper_shader)
|
||||||
|
obj[count_out++] = tmp_buf[i];
|
||||||
|
|
||||||
|
if (count_ret)
|
||||||
|
*count_ret = count_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
flush_viewport_state (CoglGLES2Context *gles2_ctx)
|
||||||
|
{
|
||||||
|
if (gles2_ctx->viewport_dirty)
|
||||||
|
{
|
||||||
|
int y;
|
||||||
|
|
||||||
|
if (gles2_ctx->current_flip_state == COGL_GLES2_FLIP_STATE_FLIPPED)
|
||||||
|
{
|
||||||
|
/* We need to know the height of the current framebuffer in
|
||||||
|
* order to flip the viewport. Fortunately we don't need to
|
||||||
|
* track the height of the FBOs created within the GLES2
|
||||||
|
* context because we would never be flipping if they are
|
||||||
|
* bound so we can just assume Cogl's framebuffer is bound
|
||||||
|
* when we are flipping */
|
||||||
|
int fb_height = cogl_framebuffer_get_height (gles2_ctx->write_buffer);
|
||||||
|
y = fb_height - (gles2_ctx->viewport[1] + gles2_ctx->viewport[3]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
y = gles2_ctx->viewport[1];
|
||||||
|
|
||||||
|
gles2_ctx->context->glViewport (gles2_ctx->viewport[0],
|
||||||
|
y,
|
||||||
|
gles2_ctx->viewport[2],
|
||||||
|
gles2_ctx->viewport[3]);
|
||||||
|
|
||||||
|
gles2_ctx->viewport_dirty = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
flush_scissor_state (CoglGLES2Context *gles2_ctx)
|
||||||
|
{
|
||||||
|
if (gles2_ctx->scissor_dirty)
|
||||||
|
{
|
||||||
|
int y;
|
||||||
|
|
||||||
|
if (gles2_ctx->current_flip_state == COGL_GLES2_FLIP_STATE_FLIPPED)
|
||||||
|
{
|
||||||
|
/* See comment above about the viewport flipping */
|
||||||
|
int fb_height = cogl_framebuffer_get_height (gles2_ctx->write_buffer);
|
||||||
|
y = fb_height - (gles2_ctx->scissor[1] + gles2_ctx->scissor[3]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
y = gles2_ctx->scissor[1];
|
||||||
|
|
||||||
|
gles2_ctx->context->glScissor (gles2_ctx->scissor[0],
|
||||||
|
y,
|
||||||
|
gles2_ctx->scissor[2],
|
||||||
|
gles2_ctx->scissor[3]);
|
||||||
|
|
||||||
|
gles2_ctx->scissor_dirty = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
flush_front_face_state (CoglGLES2Context *gles2_ctx)
|
||||||
|
{
|
||||||
|
if (gles2_ctx->front_face_dirty)
|
||||||
|
{
|
||||||
|
GLenum front_face;
|
||||||
|
|
||||||
|
if (gles2_ctx->current_flip_state == COGL_GLES2_FLIP_STATE_FLIPPED)
|
||||||
|
{
|
||||||
|
if (gles2_ctx->front_face == GL_CW)
|
||||||
|
front_face = GL_CCW;
|
||||||
|
else
|
||||||
|
front_face = GL_CW;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
front_face = gles2_ctx->front_face;
|
||||||
|
|
||||||
|
gles2_ctx->context->glFrontFace (front_face);
|
||||||
|
|
||||||
|
gles2_ctx->front_face_dirty = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pre_draw_wrapper (CoglGLES2Context *gles2_ctx)
|
||||||
|
{
|
||||||
|
/* If there's no current program then we'll just let GL report an
|
||||||
|
* error */
|
||||||
|
if (gles2_ctx->current_program == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
flush_viewport_state (gles2_ctx);
|
||||||
|
flush_scissor_state (gles2_ctx);
|
||||||
|
flush_front_face_state (gles2_ctx);
|
||||||
|
|
||||||
|
/* We want to flip rendering when the application is rendering to a
|
||||||
|
* Cogl offscreen buffer in order to maintain the flipped texture
|
||||||
|
* coordinate origin */
|
||||||
|
if (gles2_ctx->current_flip_state !=
|
||||||
|
gles2_ctx->current_program->flip_vector_state)
|
||||||
|
{
|
||||||
|
GLuint location =
|
||||||
|
gles2_ctx->current_program->flip_vector_location;
|
||||||
|
float value[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
|
||||||
|
|
||||||
|
if (gles2_ctx->current_flip_state == COGL_GLES2_FLIP_STATE_FLIPPED)
|
||||||
|
value[1] = -1.0f;
|
||||||
|
|
||||||
|
gles2_ctx->context->glUniform4fv (location, 1, value);
|
||||||
|
|
||||||
|
gles2_ctx->current_program->flip_vector_state =
|
||||||
|
gles2_ctx->current_flip_state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gl_clear_wrapper (GLbitfield mask)
|
||||||
|
{
|
||||||
|
CoglGLES2Context *gles2_ctx = current_gles2_context;
|
||||||
|
|
||||||
|
/* Clearing is affected by the scissor state so we need to ensure
|
||||||
|
* that's flushed */
|
||||||
|
flush_scissor_state (gles2_ctx);
|
||||||
|
|
||||||
|
gles2_ctx->context->glClear (mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gl_draw_elements_wrapper (GLenum mode,
|
||||||
|
GLsizei count,
|
||||||
|
GLenum type,
|
||||||
|
const GLvoid *indices)
|
||||||
|
{
|
||||||
|
CoglGLES2Context *gles2_ctx = current_gles2_context;
|
||||||
|
|
||||||
|
pre_draw_wrapper (gles2_ctx);
|
||||||
|
|
||||||
|
gles2_ctx->context->glDrawElements (mode, count, type, indices);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gl_draw_arrays_wrapper (GLenum mode,
|
||||||
|
GLint first,
|
||||||
|
GLsizei count)
|
||||||
|
{
|
||||||
|
CoglGLES2Context *gles2_ctx = current_gles2_context;
|
||||||
|
|
||||||
|
pre_draw_wrapper (gles2_ctx);
|
||||||
|
|
||||||
|
gles2_ctx->context->glDrawArrays (mode, first, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gl_get_program_info_log_wrapper (GLuint program,
|
||||||
|
GLsizei buf_size,
|
||||||
|
GLsizei *length_out,
|
||||||
|
GLchar *info_log)
|
||||||
|
{
|
||||||
|
CoglGLES2Context *gles2_ctx = current_gles2_context;
|
||||||
|
GLsizei length;
|
||||||
|
|
||||||
|
gles2_ctx->context->glGetProgramInfoLog (program,
|
||||||
|
buf_size,
|
||||||
|
&length,
|
||||||
|
info_log);
|
||||||
|
|
||||||
|
replace_token (info_log,
|
||||||
|
MAIN_WRAPPER_REPLACEMENT_NAME,
|
||||||
|
"main",
|
||||||
|
MIN (length, buf_size));
|
||||||
|
|
||||||
|
if (length_out)
|
||||||
|
*length_out = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gl_get_shader_info_log_wrapper (GLuint shader,
|
||||||
|
GLsizei buf_size,
|
||||||
|
GLsizei *length_out,
|
||||||
|
GLchar *info_log)
|
||||||
|
{
|
||||||
|
CoglGLES2Context *gles2_ctx = current_gles2_context;
|
||||||
|
GLsizei length;
|
||||||
|
|
||||||
|
gles2_ctx->context->glGetShaderInfoLog (shader,
|
||||||
|
buf_size,
|
||||||
|
&length,
|
||||||
|
info_log);
|
||||||
|
|
||||||
|
replace_token (info_log,
|
||||||
|
MAIN_WRAPPER_REPLACEMENT_NAME,
|
||||||
|
"main",
|
||||||
|
MIN (length, buf_size));
|
||||||
|
|
||||||
|
if (length_out)
|
||||||
|
*length_out = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gl_front_face_wrapper (GLenum mode)
|
||||||
|
{
|
||||||
|
CoglGLES2Context *gles2_ctx = current_gles2_context;
|
||||||
|
|
||||||
|
/* If the mode doesn't make any sense then we'll just let the
|
||||||
|
* context deal with it directly so that it will throw an error */
|
||||||
|
if (mode != GL_CW && mode != GL_CCW)
|
||||||
|
gles2_ctx->context->glFrontFace (mode);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gles2_ctx->front_face = mode;
|
||||||
|
gles2_ctx->front_face_dirty = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gl_viewport_wrapper (GLint x,
|
||||||
|
GLint y,
|
||||||
|
GLsizei width,
|
||||||
|
GLsizei height)
|
||||||
|
{
|
||||||
|
CoglGLES2Context *gles2_ctx = current_gles2_context;
|
||||||
|
|
||||||
|
/* If the viewport is invalid then we'll just let the context deal
|
||||||
|
* with it directly so that it will throw an error */
|
||||||
|
if (width < 0 || height < 0)
|
||||||
|
gles2_ctx->context->glViewport (x, y, width, height);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gles2_ctx->viewport[0] = x;
|
||||||
|
gles2_ctx->viewport[1] = y;
|
||||||
|
gles2_ctx->viewport[2] = width;
|
||||||
|
gles2_ctx->viewport[3] = height;
|
||||||
|
gles2_ctx->viewport_dirty = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gl_scissor_wrapper (GLint x,
|
||||||
|
GLint y,
|
||||||
|
GLsizei width,
|
||||||
|
GLsizei height)
|
||||||
|
{
|
||||||
|
CoglGLES2Context *gles2_ctx = current_gles2_context;
|
||||||
|
|
||||||
|
/* If the scissor is invalid then we'll just let the context deal
|
||||||
|
* with it directly so that it will throw an error */
|
||||||
|
if (width < 0 || height < 0)
|
||||||
|
gles2_ctx->context->glScissor (x, y, width, height);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gles2_ctx->scissor[0] = x;
|
||||||
|
gles2_ctx->scissor[1] = y;
|
||||||
|
gles2_ctx->scissor[2] = width;
|
||||||
|
gles2_ctx->scissor[3] = height;
|
||||||
|
gles2_ctx->scissor_dirty = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gl_get_boolean_v_wrapper (GLenum pname,
|
||||||
|
GLboolean *params)
|
||||||
|
{
|
||||||
|
CoglGLES2Context *gles2_ctx = current_gles2_context;
|
||||||
|
|
||||||
|
switch (pname)
|
||||||
|
{
|
||||||
|
case GL_VIEWPORT:
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 4; i++)
|
||||||
|
params[i] = !!gles2_ctx->viewport[i];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GL_SCISSOR_BOX:
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 4; i++)
|
||||||
|
params[i] = !!gles2_ctx->scissor[i];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
gles2_ctx->context->glGetBooleanv (pname, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gl_get_integer_v_wrapper (GLenum pname,
|
||||||
|
GLint *params)
|
||||||
|
{
|
||||||
|
CoglGLES2Context *gles2_ctx = current_gles2_context;
|
||||||
|
|
||||||
|
switch (pname)
|
||||||
|
{
|
||||||
|
case GL_VIEWPORT:
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 4; i++)
|
||||||
|
params[i] = gles2_ctx->viewport[i];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GL_SCISSOR_BOX:
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 4; i++)
|
||||||
|
params[i] = gles2_ctx->scissor[i];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GL_FRONT_FACE:
|
||||||
|
params[0] = gles2_ctx->front_face;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
gles2_ctx->context->glGetIntegerv (pname, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gl_get_float_v_wrapper (GLenum pname,
|
||||||
|
GLfloat *params)
|
||||||
|
{
|
||||||
|
CoglGLES2Context *gles2_ctx = current_gles2_context;
|
||||||
|
|
||||||
|
switch (pname)
|
||||||
|
{
|
||||||
|
case GL_VIEWPORT:
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 4; i++)
|
||||||
|
params[i] = gles2_ctx->viewport[i];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GL_SCISSOR_BOX:
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 4; i++)
|
||||||
|
params[i] = gles2_ctx->scissor[i];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GL_FRONT_FACE:
|
||||||
|
params[0] = gles2_ctx->front_face;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
gles2_ctx->context->glGetFloatv (pname, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gl_pixel_store_i_wrapper (GLenum pname, GLint param)
|
||||||
|
{
|
||||||
|
CoglGLES2Context *gles2_ctx = current_gles2_context;
|
||||||
|
|
||||||
|
gles2_ctx->context->glPixelStorei (pname, param);
|
||||||
|
|
||||||
|
if (pname == GL_PACK_ALIGNMENT &&
|
||||||
|
(param == 1 || param == 2 || param == 4 || param == 8))
|
||||||
|
gles2_ctx->pack_alignment = param;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_cogl_gles2_offscreen_free (CoglGLES2Offscreen *gles2_offscreen)
|
_cogl_gles2_offscreen_free (CoglGLES2Offscreen *gles2_offscreen)
|
||||||
{
|
{
|
||||||
@ -415,6 +1106,8 @@ _cogl_gles2_context_free (CoglGLES2Context *gles2_context)
|
|||||||
const CoglWinsysVtable *winsys;
|
const CoglWinsysVtable *winsys;
|
||||||
GList *objects, *l;
|
GList *objects, *l;
|
||||||
|
|
||||||
|
ctx->glDeleteShader (gles2_context->wrapper_shader);
|
||||||
|
|
||||||
if (gles2_context->current_program)
|
if (gles2_context->current_program)
|
||||||
program_data_unref (gles2_context->current_program);
|
program_data_unref (gles2_context->current_program);
|
||||||
|
|
||||||
@ -480,6 +1173,33 @@ free_program_data (CoglGLES2ProgramData *data)
|
|||||||
g_slice_free (CoglGLES2ProgramData, data);
|
g_slice_free (CoglGLES2ProgramData, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static GLuint
|
||||||
|
create_wrapper_shader (CoglContext *ctx)
|
||||||
|
{
|
||||||
|
const char *strings = main_wrapper_function;
|
||||||
|
GLint length = sizeof (main_wrapper_function) - 1;
|
||||||
|
GLint status;
|
||||||
|
GLuint shader;
|
||||||
|
|
||||||
|
shader = ctx->glCreateShader (GL_VERTEX_SHADER);
|
||||||
|
ctx->glShaderSource (shader, 1, &strings, &length);
|
||||||
|
ctx->glCompileShader (shader);
|
||||||
|
ctx->glGetShaderiv (shader, GL_COMPILE_STATUS, &status);
|
||||||
|
|
||||||
|
if (!status)
|
||||||
|
{
|
||||||
|
char buf[512];
|
||||||
|
|
||||||
|
ctx->glGetShaderInfoLog (shader,
|
||||||
|
sizeof (buf),
|
||||||
|
NULL, /* length */
|
||||||
|
buf);
|
||||||
|
g_warning ("Compiling wrapper shader failed:\n%s", buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
CoglGLES2Context *
|
CoglGLES2Context *
|
||||||
cogl_gles2_context_new (CoglContext *ctx, GError **error)
|
cogl_gles2_context_new (CoglContext *ctx, GError **error)
|
||||||
{
|
{
|
||||||
@ -511,6 +1231,13 @@ cogl_gles2_context_new (CoglContext *ctx, GError **error)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gles2_ctx->current_flip_state = COGL_GLES2_FLIP_STATE_UNKNOWN;
|
||||||
|
gles2_ctx->viewport_dirty = TRUE;
|
||||||
|
gles2_ctx->scissor_dirty = TRUE;
|
||||||
|
gles2_ctx->front_face_dirty = TRUE;
|
||||||
|
gles2_ctx->front_face = GL_CCW;
|
||||||
|
gles2_ctx->pack_alignment = 4;
|
||||||
|
|
||||||
gles2_ctx->vtable = g_malloc0 (sizeof (CoglGLES2Vtable));
|
gles2_ctx->vtable = g_malloc0 (sizeof (CoglGLES2Vtable));
|
||||||
#define COGL_EXT_BEGIN(name, \
|
#define COGL_EXT_BEGIN(name, \
|
||||||
min_gl_major, min_gl_minor, \
|
min_gl_major, min_gl_minor, \
|
||||||
@ -528,6 +1255,8 @@ cogl_gles2_context_new (CoglContext *ctx, GError **error)
|
|||||||
#undef COGL_EXT_FUNCTION
|
#undef COGL_EXT_FUNCTION
|
||||||
#undef COGL_EXT_END
|
#undef COGL_EXT_END
|
||||||
|
|
||||||
|
gles2_ctx->wrapper_shader = create_wrapper_shader (ctx);
|
||||||
|
|
||||||
gles2_ctx->vtable->glBindFramebuffer = gl_bind_framebuffer_wrapper;
|
gles2_ctx->vtable->glBindFramebuffer = gl_bind_framebuffer_wrapper;
|
||||||
gles2_ctx->vtable->glReadPixels = gl_read_pixels_wrapper;
|
gles2_ctx->vtable->glReadPixels = gl_read_pixels_wrapper;
|
||||||
gles2_ctx->vtable->glCopyTexImage2D = gl_copy_tex_image_2d_wrapper;
|
gles2_ctx->vtable->glCopyTexImage2D = gl_copy_tex_image_2d_wrapper;
|
||||||
@ -539,6 +1268,27 @@ cogl_gles2_context_new (CoglContext *ctx, GError **error)
|
|||||||
gles2_ctx->vtable->glUseProgram = gl_use_program_wrapper;
|
gles2_ctx->vtable->glUseProgram = gl_use_program_wrapper;
|
||||||
gles2_ctx->vtable->glAttachShader = gl_attach_shader_wrapper;
|
gles2_ctx->vtable->glAttachShader = gl_attach_shader_wrapper;
|
||||||
gles2_ctx->vtable->glDetachShader = gl_detach_shader_wrapper;
|
gles2_ctx->vtable->glDetachShader = gl_detach_shader_wrapper;
|
||||||
|
gles2_ctx->vtable->glShaderSource = gl_shader_source_wrapper;
|
||||||
|
gles2_ctx->vtable->glGetShaderSource = gl_get_shader_source_wrapper;
|
||||||
|
gles2_ctx->vtable->glLinkProgram = gl_link_program_wrapper;
|
||||||
|
gles2_ctx->vtable->glGetProgramiv = gl_get_program_iv_wrapper;
|
||||||
|
gles2_ctx->vtable->glGetAttachedShaders = gl_get_attached_shaders_wrapper;
|
||||||
|
gles2_ctx->vtable->glGetProgramInfoLog = gl_get_program_info_log_wrapper;
|
||||||
|
gles2_ctx->vtable->glGetShaderInfoLog = gl_get_shader_info_log_wrapper;
|
||||||
|
gles2_ctx->vtable->glClear = gl_clear_wrapper;
|
||||||
|
gles2_ctx->vtable->glDrawElements = gl_draw_elements_wrapper;
|
||||||
|
gles2_ctx->vtable->glDrawArrays = gl_draw_arrays_wrapper;
|
||||||
|
gles2_ctx->vtable->glFrontFace = gl_front_face_wrapper;
|
||||||
|
gles2_ctx->vtable->glViewport = gl_viewport_wrapper;
|
||||||
|
gles2_ctx->vtable->glScissor = gl_scissor_wrapper;
|
||||||
|
gles2_ctx->vtable->glGetBooleanv = gl_get_boolean_v_wrapper;
|
||||||
|
gles2_ctx->vtable->glGetIntegerv = gl_get_integer_v_wrapper;
|
||||||
|
gles2_ctx->vtable->glGetFloatv = gl_get_float_v_wrapper;
|
||||||
|
gles2_ctx->vtable->glPixelStorei = gl_pixel_store_i_wrapper;
|
||||||
|
|
||||||
|
/* FIXME: we need to do something with glCopyTexImage2D and
|
||||||
|
* glCopySubTexImage2D so that it will flip the data if it is read
|
||||||
|
* from a CoglOffscreen */
|
||||||
|
|
||||||
gles2_ctx->shader_map =
|
gles2_ctx->shader_map =
|
||||||
g_hash_table_new_full (g_direct_hash,
|
g_hash_table_new_full (g_direct_hash,
|
||||||
@ -719,6 +1469,8 @@ cogl_push_gles2_context (CoglContext *ctx,
|
|||||||
if (gles2_ctx->write_buffer)
|
if (gles2_ctx->write_buffer)
|
||||||
cogl_object_unref (gles2_ctx->write_buffer);
|
cogl_object_unref (gles2_ctx->write_buffer);
|
||||||
gles2_ctx->write_buffer = cogl_object_ref (write_buffer);
|
gles2_ctx->write_buffer = cogl_object_ref (write_buffer);
|
||||||
|
|
||||||
|
update_current_flip_state (gles2_ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!winsys->set_gles2_context (gles2_ctx, &internal_error))
|
if (!winsys->set_gles2_context (gles2_ctx, &internal_error))
|
||||||
|
Loading…
Reference in New Issue
Block a user